Projects
Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 17
View file
pipewire-aptx.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Mon Nov 21 11:36:55 UTC 2022 - Bjørn Lie <zaitor@opensuse.org> + +- Update to version 0.3.60 + +------------------------------------------------------------------- Sat Oct 15 16:39:17 UTC 2022 - Bjørn Lie <zaitor@opensuse.org> - Update to version 0.3.59
View file
pipewire-aptx.spec
Changed
@@ -7,7 +7,7 @@ %define soversion 0_2 Name: pipewire-aptx -Version: 0.3.59 +Version: 0.3.60 Release: 0 Summary: PipeWire Bluetooth aptX codec plugin License: MIT
View file
pipewire-0.3.59.tar.gz/.gitlab-ci.yml -> pipewire-0.3.60.tar.gz/.gitlab-ci.yml
Changed
@@ -25,7 +25,7 @@ .fedora: variables: # Update this tag when you want to trigger a rebuild - FDO_DISTRIBUTION_TAG: '2022-03-05.0' + FDO_DISTRIBUTION_TAG: '2022-11-07.0' FDO_DISTRIBUTION_VERSION: '35' FDO_DISTRIBUTION_PACKAGES: >- alsa-lib-devel @@ -52,6 +52,7 @@ libv4l-devel libva-devel libX11-devel + ModemManager-devel openssl-devel pulseaudio-libs-devel python3-docutils @@ -68,6 +69,7 @@ python3-pip pulseaudio-utils openal-soft + readline-devel FDO_DISTRIBUTION_EXEC: >- pip3 install meson @@ -105,7 +107,7 @@ .alpine: variables: # Update this tag when you want to trigger a rebuild - FDO_DISTRIBUTION_TAG: '2022-01-28.2' + FDO_DISTRIBUTION_TAG: '2022-09-07.0' FDO_DISTRIBUTION_VERSION: '3.15' FDO_DISTRIBUTION_PACKAGES: >- alsa-lib-dev @@ -128,6 +130,7 @@ libusb-dev libx11-dev meson + modemmanager-dev ncurses-dev pulseaudio-dev readline-dev @@ -176,7 +179,6 @@ - ninja $NINJA_ARGS -C "$BUILD_DIR" - ninja $NINJA_ARGS -C "$BUILD_DIR" test - ninja $NINJA_ARGS -C "$BUILD_DIR" install - - ./check_missing_headers.sh artifacts: name: pipewire-$CI_COMMIT_SHA when: always @@ -223,6 +225,8 @@ - .fdo.distribution-image@ubuntu - .build stage: build + variables: + MESON_OPTIONS: "-Dsession-managers=" .build_on_fedora: extends: @@ -248,6 +252,7 @@ -Dvulkan=enabled -Dsdl2=enabled -Dsndfile=enabled + -Dsession-managers= artifacts: name: pipewire-$CI_COMMIT_SHA when: always @@ -262,6 +267,8 @@ - .fdo.distribution-image@alpine - .build stage: build + variables: + MESON_OPTIONS: "-Dsession-managers=" # build with all auto() options enabled build_all: @@ -270,7 +277,14 @@ variables: # 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 -Dbluez5-codec-lc3=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 + -Dsession-managers= parallel: matrix: - CC: gcc, clang @@ -280,7 +294,7 @@ extends: - .build_on_fedora variables: - MESON_OPTIONS: "" + MESON_OPTIONS: "-Dsession-managers=" parallel: matrix: - CC: gcc, clang @@ -296,7 +310,7 @@ MESON_OPTION_VALUE: enabled, disabled script: - echo "Building with -D$MESON_OPTION=$MESON_OPTION_VALUE" - - meson "$BUILD_DIR" . --prefix="$PREFIX" "-D$MESON_OPTION=$MESON_OPTION_VALUE" + - meson "$BUILD_DIR" . --prefix="$PREFIX" "-D$MESON_OPTION=$MESON_OPTION_VALUE" -Dsession-managers= - ninja $NINJA_ARGS -C "$BUILD_DIR" - ninja $NINJA_ARGS -C "$BUILD_DIR" test @@ -307,7 +321,7 @@ extends: - .build_on_fedora variables: - MESON_OPTIONS: "-Dtest=enabled -Dbuildtype=release -Db_ndebug=true" + MESON_OPTIONS: "-Dtest=enabled -Dbuildtype=release -Db_ndebug=true -Dsession-managers=" parallel: matrix: - CC: gcc, clang @@ -363,6 +377,8 @@ - echo "Building with meson options $MESON_OPTIONS" - meson "$BUILD_DIR" . --prefix="$PREFIX" $MESON_OPTIONS - meson test -C "$BUILD_DIR" --setup=valgrind + variables: + MESON_OPTIONS: "-Dsession-managers=" build_with_coverity: extends: @@ -383,6 +399,7 @@ -Dvulkan=enabled -Dsdl2=enabled -Dsndfile=enabled + -Dsession-managers= - cov-configure --config coverity_conf.xml --comptype gcc --compiler cc --template --xml-option=append_arg@C:--ppp_translator @@ -432,6 +449,18 @@ git grep -q -e "\\\subpage $page" || (echo "\\page $page is missing \\subpage entry in doc/pipewire-modules.dox" && false) done +check_missing_headers: + extends: + - .fedora + - .not_coverity + - .fdo.distribution-image@fedora + stage: analysis + dependencies: + - build_on_fedora + script: + - export PREFIX=`find -name prefix-*` + - ./.gitlab/ci/check_missing_headers.sh + pages: extends: - .not_coverity
View file
pipewire-0.3.60.tar.gz/.gitlab/ci
Added
+(directory)
View file
pipewire-0.3.60.tar.gz/.gitlab/ci/check_missing_headers.sh
Changed
(renamed from check_missing_headers.sh)
View file
pipewire-0.3.59.tar.gz/NEWS -> pipewire-0.3.60.tar.gz/NEWS
Changed
@@ -1,3 +1,151 @@ +# PipeWire 0.3.60 (2022-11-10) + +This is a bugfix release that is API and ABI compatible with previous +0.3.x releases. + +## Highlights + - The filter-chain now handles errors better and has fixes for many + crasher bugs. + - A new RTP module was added with a sender and receiver. It uses SAP + to announce and consume RTP streams and is compatible with the + PulseAudio RTP modules. + - Many small bluetooth improvements and fixes. + - The alsa plugin will now only start playback when there is data. This + results in better sync and lower latency between capture and playback. + - The v4l2 and libcamera plugins have seen a lot of improvements. They + support control properties now. Also pw-v4l2 has seen many improvements + and mostly passes the v4l2-compliance test now. + - Many more bugfixes and improvements. + + +## PipeWire + - Code cleanups, compiler warning fixes. + - Add some extra checks to avoid scheduling an inactive node. + - Rework the sequence of events to start and stop nodes. + - Improve param enumeration. + - An option was added to give priority to the Buffer params of the + consumer. This makes it possible to use the default values of the + consumer (instead of the producer) when capturing from a source. + - The graph rate selection was improved to pick a rate closest to the + requested one (instead of picking the default). + +## Modules + - Fix some crashes in filter-chain. (#2737) + - X11 Bell module will now be loaded by default when available. + - A new RTP module was added with a sender and receiver. It uses SAP + to announce and consume RTP streams and is compatible with the + PulseAudio RTP modules. + - Improve RAOP compatibility. + - The echo-cancel module now uses the resampler prefill option to align + input and output samples without buffering. Better latency control + when starting and stopping has been implemented. + - The pulse tunnel will now write aligned samples to pulseaudio even + when the ringbuffer wraps around. This fixes playback issues with + multichannel sinks. + - Add a delay option to module-loopback using a ringbuffer. + - Implement echo-cancel params. + - The filter-chain module has better error reporting. + - The LADSPA search path was extended with some more common paths. + - The echo-canceler input can now also be a monitor of a sink. This + improves compatibility with some proton games that expect a real + sink instead of a virtual one. + +## Tools + - Better error reporting in pw-link. + - pw-top now also shows IEC958 passthrough formats and JPEG/H264 video + formats. + - pw-top refreshes the screen faster. + - pw-top now prints the state of the node and shows less info for + inactive nodes. + - pw-dump now uses the new seq field in the spa_param_info to discard + old param updates and avoid duplicate params in the output. + +## Bluetooth + - Add ModemManager support in the native backend. + - Clean up GetManagedObjects handling. + - Handle QoS from the endpoints in the codec. + - Increase the socket buffer to have more control over the rate and QoS. + - Simplify the packet flushing code. + - Stop processing nodes before destroying them. + - Fix timers when a source switches drivers. + - Codecs can now share endpoints. This reduces the amount of endpoints and + avoids problems with devices that can't handle a large amount of + codec endpoints. + - Report batery status to UPower for HFP AG. + - Fix bitpool increase. + +## SPA + - The audioresampler now avoids clicks and pops between activating and + deactivating the adaptive resampler when used by the stream API. + - Use default locale to parse float parameters. + - The upmix functions now have SSE optimizations. + - Avoid recalculating the complete channelmix setup when only the + volume changes. + - The alsa plugin will now only start playback when there is data. This + results in better sync and lower latency between capture and playback. + - The ALSA MIDI sequencer will now pull data from the graph even when it + did not output anything. Fixes some graph stalls with the sequencer in + some cases. (#2775) + - v4l2 and libcamera sources now recycle buffers when nothing is consuming + them. This avoids stalling the graph. + - libcamera now suggests a more appropriate frame size than the smallest + poster frame. + - Improve state changes in audioconvert. (#2764) + - A new seq field was added to spa_param_info to keep track of pending + param updates. + - Support speaker output only on RealTek ALC4080. (#2744) + - The v4l2 source now supports setting controls. + - The libcamera plugin now supports enumerating and setting controls. + - A new unit test for 6.1 channel mapping was added. (#2809) More debug + info was added to audioconvert for the channel matrix. + - Audioconvert will now also upmix a rear-center channel when needed. + +## pulse-server + - Add support for the RTP send and recv modules with the new native + RTP module. + - Add option to set latency for pulse-tunnel streams and + module-zeroconf-discover. + - The socket will now be given the same permissions as what pulseaudio + did (0777). + - Implement module-loopback latency_msec correctly with the new delay + parameter. + - sysfs.path is now filled with the same data as pulseaudio. + - The manager now uses the new seq field in the spa_param_info. + - Fix a bug where in some cases the read pointer would get out of sync + and cause too large requests. (#2799) + +## ALSA + - The alsa plugin now reuses the stream in prepare which results in + better performance. + - Some deadlocks have been fixed in the ALSA plugin. + - The ALSA plugin reports more accurate timing information in some cases. + +## V4l2 + - The v4l2 compatibility layer has received a lot of updates. + - Improved node names and format enumeration. + - Support for multiple /dev/videoX devices, each mapped to a unique + PipeWire node. + - Passes the v4l2-compliance test now with both the v4l2 and libcamera + backend in PipeWire. + - Improved mmap support for inline buffer memory. This makes it possible to + consume PipeWire streams. + - Negotiation works more reliably now. + +## JACK + - Implement jack_acquire_real_time_scheduling() and + jack_drop_real_time_scheduling() by keeping the thread utils in a global + state. + - Fix jack_client_thread_id() to return NULL when the client is not active, + just like jack1 and jack2. + - An option was added to let the jack_set_buffer_size() function update the + global metadata. A quirk was added so that jack_bufsize uses this new feature + to make the buffer size settings persistent and global, just like jack. + - jack_port_register() and jack_port_unregister() can be called on an + active client so make this thread safe. (#2652) + + +Older versions: + # PipeWire 0.3.59 (2022-09-30) This is a bugfix release that is API and ABI compatible with previous @@ -72,9 +220,6 @@ - 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
View file
pipewire-0.3.59.tar.gz/doc/pipewire-modules.dox -> pipewire-0.3.60.tar.gz/doc/pipewire-modules.dox
Changed
@@ -73,6 +73,8 @@ - \subpage page_module_raop_discover - \subpage page_module_roc_sink - \subpage page_module_roc_source +- \subpage page_module_rtp_sink +- \subpage page_module_rtp_source - \subpage page_module_rt - \subpage page_module_session_manager - \subpage page_module_x11_bell
View file
pipewire-0.3.59.tar.gz/man/pw-top.1.rst.in -> pipewire-0.3.60.tar.gz/man/pw-top.1.rst.in
Changed
@@ -27,13 +27,15 @@ The columns presented are as follows: S - Measurement status. - ! representing inactive - no connections - - Blank representing active + Node status. + E = ERROR + C = CREATING + S = SUSPENDED + I = IDLE + R = RUNNING ID - The ID of the pipewire node/device, as found in *pw-dump* + The ID of the pipewire node/device, as found in *pw-dump* and *pw-cli* QUANT The current quantum (for drivers) and the suggested quantum for follower @@ -135,6 +137,8 @@ For raw audio formats, the layout is <sampleformat> <channels> <samplerate>. + For IEC958 passthrough audio formats, the layout is IEC958 <codec> <samplerate>. + For DSD formats, the layout is <dsd-rate> <channels>. For Video formats, the layout is <pixelformat> <width>x<height>.
View file
pipewire-0.3.59.tar.gz/meson.build -> pipewire-0.3.60.tar.gz/meson.build
Changed
@@ -1,5 +1,5 @@ project('pipewire', 'c' , - version : '0.3.59', + version : '0.3.60', license : 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' , meson_version : '>= 0.59.0', default_options : 'warning_level=3', @@ -67,6 +67,7 @@ common_flags = '-fvisibility=hidden', + '-fno-strict-aliasing', '-Werror=suggest-attribute=format', '-Wsign-compare', '-Wpointer-arith', @@ -97,7 +98,7 @@ if have_cpp cxx = meson.get_compiler('cpp') - cxx_flags = common_flags + cxx_flags = common_flags + '-Wno-c99-designator' add_project_arguments(cxx.get_supported_arguments(cxx_flags), language: 'cpp') endif @@ -259,10 +260,10 @@ sdl_dep = dependency('sdl2', required : get_option('sdl2')) summary({'SDL2 (video examples)': sdl_dep.found()}, bool_yn: true, section: 'Misc dependencies') drm_dep = dependency('libdrm', required : false) -readline_dep = dependency('readline', required : false) +readline_dep = dependency('readline', required : get_option('readline')) if not readline_dep.found() - readline_dep = cc.find_library('readline', required: false) + readline_dep = cc.find_library('readline', required : get_option('readline')) endif summary({'readline (for pw-cli)': readline_dep.found()}, bool_yn: true, section: 'Misc dependencies')
View file
pipewire-0.3.59.tar.gz/meson_options.txt -> pipewire-0.3.60.tar.gz/meson_options.txt
Changed
@@ -92,6 +92,10 @@ description: 'Enable HFP in native backend in bluez5 spa plugin', type: 'feature', value: 'enabled') +option('bluez5-backend-native-mm', + description: 'Enable ModemManager in native backend in bluez5 spa plugin', + type: 'feature', + value: 'disabled') option('bluez5-backend-ofono', description: 'Enable oFono HFP backend in bluez5 spa plugin (no dependency on oFono)', type: 'feature', @@ -228,7 +232,7 @@ option('session-managers', description : 'Session managers to build (can be for none or an absolute path)', type : 'array', - value : 'media-session') + value : 'wireplumber') option('raop', description: 'Enable module for Remote Audio Output Protocol', type: 'feature', @@ -261,3 +265,7 @@ description: 'Enable Flatpak support', type: 'feature', value: 'enabled') +option('readline', + description: 'Enable code that depends on libreadline', + type: 'feature', + value: 'auto')
View file
pipewire-0.3.59.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c -> pipewire-0.3.60.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c
Changed
@@ -123,7 +123,9 @@ struct spa_hook stream_listener; int64_t delay; - uint64_t now; + uint64_t transfered; + uint64_t buffered; + int64_t now; uintptr_t seq; struct spa_audio_info_raw format; @@ -163,21 +165,15 @@ static int update_active(snd_pcm_ioplug_t *io) { snd_pcm_pipewire_t *pw = io->private_data; - bool active; - - active = check_active(io); - - if (pw->active != active) { - uint64_t val; + pw->active = check_active(io); + uint64_t val; - pw->active = active; + if (pw->active || pw->error < 0) + spa_system_eventfd_write(pw->system, io->poll_fd, 1); + else + spa_system_eventfd_read(pw->system, io->poll_fd, &val); - if (active) - spa_system_eventfd_write(pw->system, io->poll_fd, 1); - else - spa_system_eventfd_read(pw->system, io->poll_fd, &val); - } - return active; + return pw->active; } static void snd_pcm_pipewire_free(snd_pcm_pipewire_t *pw) @@ -233,8 +229,10 @@ return pw->error; *revents = pfds0.revents & ~(POLLIN | POLLOUT); - if (pfds0.revents & POLLIN && check_active(io)) + if (pfds0.revents & POLLIN && check_active(io)) { *revents |= (io->stream == SND_PCM_STREAM_PLAYBACK) ? POLLOUT : POLLIN; + update_active(io); + } return 0; } @@ -266,7 +264,7 @@ do { seq1 = SEQ_READ(pw->seq); - delay = pw->delay; + delay = pw->delay + pw->transfered; now = pw->now; if (io->stream == SND_PCM_STREAM_PLAYBACK) avail = snd_pcm_ioplug_hw_avail(io, pw->hw_ptr, io->appl_ptr); @@ -450,9 +448,8 @@ pw_stream_get_time_n(pw->stream, &pwt, sizeof(pwt)); delay = pwt.delay; - if (pwt.rate.num != 0) { + if (pwt.rate.num != 0) delay = delay * io->rate * pwt.rate.num / pwt.rate.denom; - } before = hw_avail = snd_pcm_ioplug_hw_avail(io, pw->hw_ptr, io->appl_ptr); @@ -467,12 +464,20 @@ SEQ_WRITE(pw->seq); + if (pw->now != pwt.now) { + pw->transfered = pw->buffered; + pw->buffered = 0; + } + xfer = snd_pcm_pipewire_process(pw, b, &hw_avail, want); pw->delay = delay; /* the buffer is now queued in the stream and consumed */ if (io->stream == SND_PCM_STREAM_PLAYBACK) - pw->delay += xfer; + pw->transfered += xfer; + + /* more then requested data transfered, use them in next iteration */ + pw->buffered = (want == 0 || pw->transfered < want) ? 0 : (pw->transfered % want); pw->now = pwt.now; SEQ_WRITE(pw->seq); @@ -563,11 +568,6 @@ goto done; pw->hw_params_changed = false; - if (pw->stream != NULL) { - pw_stream_destroy(pw->stream); - pw->stream = NULL; - } - props = pw_properties_new(NULL, NULL); if (props == NULL) goto error; @@ -593,13 +593,20 @@ pw_properties_get(props, PW_KEY_MEDIA_ROLE) == NULL) pw_properties_setf(props, PW_KEY_MEDIA_ROLE, "%s", pw->role); + params0 = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &pw->format); + + if (pw->stream != NULL) { + pw_stream_update_properties(pw->stream, &props->dict); + pw_stream_update_params(pw->stream, params, 1); + goto done; + } + pw->stream = pw_stream_new(pw->core, pw->node_name, props); if (pw->stream == NULL) goto error; pw_stream_add_listener(pw->stream, &pw->stream_listener, &stream_events, pw); - params0 = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &pw->format); pw->error = 0; pw_stream_connect(pw->stream, @@ -867,10 +874,9 @@ static enum snd_pcm_chmap_position channel_to_chmap(enum spa_audio_channel channel) { - uint32_t i; - for (i = 0; i < SPA_N_ELEMENTS(chmap_info); i++) - if (chmap_infoi.channel == channel) - return chmap_infoi.pos; + SPA_FOR_EACH_ELEMENT_VAR(chmap_info, info) + if (info->channel == channel) + return info->pos; return SND_CHMAP_UNKNOWN; }
View file
pipewire-0.3.59.tar.gz/pipewire-jack/jack/uuid.h -> pipewire-0.3.60.tar.gz/pipewire-jack/jack/uuid.h
Changed
@@ -1,18 +1,18 @@ /* Copyright (C) 2013 Paul Davis - + This program 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. - + This program 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 Lesser General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software + along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -30,7 +30,7 @@ #define JACK_UUID_STRING_SIZE (JACK_UUID_SIZE+1) /* includes trailing null */ #define JACK_UUID_EMPTY_INITIALIZER 0 -extern jack_uuid_t jack_client_uuid_generate (); +extern jack_uuid_t jack_client_uuid_generate (void); extern jack_uuid_t jack_port_uuid_generate (uint32_t port_id); extern uint32_t jack_uuid_to_index (jack_uuid_t);
View file
pipewire-0.3.59.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.60.tar.gz/pipewire-jack/src/pipewire-jack.c
Changed
@@ -98,6 +98,7 @@ pthread_mutex_t lock; struct pw_array descriptions; struct spa_list free_objects; + struct spa_thread_utils *thread_utils; }; static struct globals globals; @@ -311,6 +312,7 @@ struct spa_hook proxy_listener; struct metadata *metadata; + struct metadata *settings; uint32_t node_id; uint32_t serial; @@ -401,6 +403,7 @@ int self_connect_mode; int rt_max; unsigned int fix_midi_events:1; + unsigned int global_buffer_size:1; jack_position_t jack_position; jack_transport_state_t jack_state; @@ -610,13 +613,9 @@ { struct mix *m; - if (!p->valid) - return; - spa_list_consume(m, &p->mix, port_link) free_mix(c, m); - p->valid = false; pw_map_remove(&c->portsp->direction, p->port_id); free_object(c, p->object); pw_properties_free(p->props); @@ -1056,7 +1055,7 @@ struct buffer *b; struct spa_data *d; - if (frames == 0) + if (frames == 0 || !p->valid) return NULL; if (SPA_UNLIKELY((mix = p->global_mix) == NULL)) @@ -2576,11 +2575,18 @@ return spa_thread_utils_acquire_rt(c->context.old_thread_utils, thread, priority); } +static int impl_drop_rt(void *object, struct spa_thread *thread) +{ + struct client *c = (struct client *) object; + return spa_thread_utils_drop_rt(c->context.old_thread_utils, thread); +} + static struct spa_thread_utils_methods thread_utils_impl = { SPA_VERSION_THREAD_UTILS_METHODS, .create = impl_create, .join = impl_join, .acquire_rt = impl_acquire_rt, + .drop_rt = impl_drop_rt, }; static jack_port_type_id_t string_to_type(const char *port_type) @@ -2721,6 +2727,24 @@ .destroy = metadata_proxy_destroy, }; +static void settings_proxy_removed(void *data) +{ + struct client *c = data; + pw_proxy_destroy((struct pw_proxy*)c->settings->proxy); +} + +static void settings_proxy_destroy(void *data) +{ + struct client *c = data; + spa_hook_remove(&c->settings->proxy_listener); + c->settings = NULL; +} + +static const struct pw_proxy_events settings_proxy_events = { + PW_VERSION_PROXY_EVENTS, + .removed = settings_proxy_removed, + .destroy = settings_proxy_destroy, +}; static void proxy_removed(void *data) { struct object *o = data; @@ -3033,24 +3057,34 @@ if (c->metadata != NULL) goto exit; - if ((str = spa_dict_lookup(props, PW_KEY_METADATA_NAME)) != NULL && - !spa_streq(str, "default")) + if ((str = spa_dict_lookup(props, PW_KEY_METADATA_NAME)) == NULL) goto exit; - proxy = pw_registry_bind(c->registry, - id, type, PW_VERSION_METADATA, sizeof(struct metadata)); - - c->metadata = pw_proxy_get_user_data(proxy); - c->metadata->proxy = (struct pw_metadata*)proxy; - c->metadata->default_audio_sink0 = '\0'; - c->metadata->default_audio_source0 = '\0'; - - pw_proxy_add_listener(proxy, - &c->metadata->proxy_listener, - &metadata_proxy_events, c); - pw_metadata_add_listener(proxy, - &c->metadata->listener, - &metadata_events, c); + if (spa_streq(str, "default")) { + proxy = pw_registry_bind(c->registry, + id, type, PW_VERSION_METADATA, sizeof(struct metadata)); + + c->metadata = pw_proxy_get_user_data(proxy); + c->metadata->proxy = (struct pw_metadata*)proxy; + c->metadata->default_audio_sink0 = '\0'; + c->metadata->default_audio_source0 = '\0'; + + pw_proxy_add_listener(proxy, + &c->metadata->proxy_listener, + &metadata_proxy_events, c); + pw_metadata_add_listener(proxy, + &c->metadata->listener, + &metadata_events, c); + } else if (spa_streq(str, "settings")) { + proxy = pw_registry_bind(c->registry, + id, type, PW_VERSION_METADATA, sizeof(struct metadata)); + + c->settings = pw_proxy_get_user_data(proxy); + c->settings->proxy = (struct pw_metadata*)proxy; + pw_proxy_add_listener(proxy, + &c->settings->proxy_listener, + &settings_proxy_events, c); + } goto exit; } else { @@ -3303,6 +3337,8 @@ if (client->context.old_thread_utils == NULL) client->context.old_thread_utils = pw_thread_utils_get(); + globals.thread_utils = client->context.old_thread_utils; + client->context.thread_utils.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_ThreadUtils, SPA_VERSION_THREAD_UTILS, @@ -3411,6 +3447,7 @@ client->locked_process = pw_properties_get_bool(client->props, "jack.locked-process", true); client->default_as_system = pw_properties_get_bool(client->props, "jack.default-as-system", false); client->fix_midi_events = pw_properties_get_bool(client->props, "jack.fix-midi-events", true); + client->global_buffer_size = pw_properties_get_bool(client->props, "jack.global-buffer-size", false); client->self_connect_mode = SELF_CONNECT_ALLOW; if ((str = pw_properties_get(client->props, "jack.self-connect-mode")) != NULL) { @@ -3509,10 +3546,11 @@ pw_proxy_destroy((struct pw_proxy*)c->registry); } if (c->metadata && c->metadata->proxy) { - spa_hook_remove(&c->metadata->listener); - spa_hook_remove(&c->metadata->proxy_listener); pw_proxy_destroy((struct pw_proxy*)c->metadata->proxy); } + if (c->settings && c->settings->proxy) { + pw_proxy_destroy((struct pw_proxy*)c->settings->proxy); + } if (c->core) { spa_hook_remove(&c->core_listener); @@ -3773,14 +3811,10 @@ jack_native_thread_t jack_client_thread_id (jack_client_t *client) { struct client *c = (struct client *) client; - void *thr; spa_return_val_if_fail(c != NULL, (pthread_t){0}); - thr = pw_data_loop_get_thread(c->loop); - if (thr == NULL) - return pthread_self(); - return (pthread_t) thr; + return (jack_native_thread_t)pw_data_loop_get_thread(c->loop); } SPA_EXPORT @@ -4137,15 +4171,22 @@ pw_log_info("%p: buffer-size %u", client, nframes); pw_thread_loop_lock(c->context.loop); - pw_properties_setf(c->props, PW_KEY_NODE_FORCE_QUANTUM, "%u", nframes); + if (c->global_buffer_size && c->settings && c->settings->proxy) { + char val256; + snprintf(val, sizeof(val), "%u", nframes == 1 ? 0: nframes); + pw_metadata_set_property(c->settings->proxy, 0, + "clock.force-quantum", "", val); + } else { + pw_properties_setf(c->props, PW_KEY_NODE_FORCE_QUANTUM, "%u", nframes); - c->info.change_mask |= SPA_NODE_CHANGE_MASK_PROPS; - c->info.props = &c->props->dict; + c->info.change_mask |= SPA_NODE_CHANGE_MASK_PROPS; + c->info.props = &c->props->dict; - pw_client_node_update(c->node, - PW_CLIENT_NODE_UPDATE_INFO, - 0, NULL, &c->info); - c->info.change_mask = 0; + pw_client_node_update(c->node, + PW_CLIENT_NODE_UPDATE_INFO, + 0, NULL, &c->info); + c->info.change_mask = 0; + } pw_thread_loop_unlock(c->context.loop); return 0; @@ -4369,6 +4410,15 @@ return (jack_port_t *) o; } +static int +do_invalidate_port(struct spa_loop *loop, + bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct port *p = user_data; + p->valid = false; + return 0; +} + SPA_EXPORT int jack_port_unregister (jack_client_t *client, jack_port_t *port) { @@ -4389,6 +4439,9 @@ res = -EINVAL; goto done; } + pw_data_loop_invoke(c->loop, + do_invalidate_port, 1, NULL, 0, !c->data_locked, p); + pw_log_info("%p: port %p unregister \"%s\"", client, port, o->port.name); pw_client_node_port_update(c->node, @@ -4566,6 +4619,8 @@ return SPA_PTROFF(d->data, offset, void); } + if (!p->valid) + return NULL; ptr = p->get_buffer(p, frames); pw_log_trace_fp("%p: port %p buffer %p empty:%u", p->client, p, ptr, p->empty_out); @@ -5715,7 +5770,7 @@ } SPA_EXPORT -jack_time_t jack_get_time() +jack_time_t jack_get_time(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -6115,15 +6170,21 @@ SPA_EXPORT int jack_acquire_real_time_scheduling (jack_native_thread_t thread, int priority) { - pw_log_info("acquire"); - return pw_thread_utils_acquire_rt((struct spa_thread*)thread, priority); + struct spa_thread *t = (struct spa_thread*)thread; + pw_log_info("acquire %p", t); + spa_return_val_if_fail(globals.thread_utils != NULL, -1); + spa_return_val_if_fail(t != NULL, -1); + return spa_thread_utils_acquire_rt(globals.thread_utils, t, priority); } SPA_EXPORT int jack_drop_real_time_scheduling (jack_native_thread_t thread) { - pw_log_info("drop"); - return pw_thread_utils_drop_rt((struct spa_thread*)thread); + struct spa_thread *t = (struct spa_thread*)thread; + pw_log_info("drop %p", t); + spa_return_val_if_fail(globals.thread_utils != NULL, -1); + spa_return_val_if_fail(t != NULL, -1); + return spa_thread_utils_drop_rt(globals.thread_utils, t); } /**
View file
pipewire-0.3.59.tar.gz/pipewire-jack/src/uuid.c -> pipewire-0.3.60.tar.gz/pipewire-jack/src/uuid.c
Changed
@@ -33,7 +33,7 @@ #include <pipewire/pipewire.h> SPA_EXPORT -jack_uuid_t jack_client_uuid_generate () +jack_uuid_t jack_client_uuid_generate (void) { static uint32_t uuid_cnt = 0; jack_uuid_t uuid = 0x2; /* JackUUIDClient */;
View file
pipewire-0.3.59.tar.gz/pipewire-v4l2/src/pipewire-v4l2.c -> pipewire-0.3.60.tar.gz/pipewire-v4l2/src/pipewire-v4l2.c
Changed
@@ -57,6 +57,7 @@ #define DEFAULT_CARD "PipeWire Camera" #define DEFAULT_BUS_INFO "PipeWire" +#define MAX_DEV 32 struct file_map { void *addr; struct file *file; @@ -64,6 +65,8 @@ struct fd_map { int fd; +#define FD_MAP_DUP (1<<0) + uint32_t flags; struct file *file; }; @@ -73,6 +76,7 @@ pthread_mutex_t lock; struct pw_array fd_maps; struct pw_array file_maps; + uint32_t dev_mapMAX_DEV; }; static struct globals globals; @@ -93,6 +97,9 @@ struct file { int ref; + uint32_t dev_id; + uint32_t serial; + struct pw_properties *props; struct pw_thread_loop *loop; struct pw_loop *l; @@ -114,23 +121,27 @@ struct pw_stream *stream; struct spa_hook stream_listener; + enum v4l2_priority priority; + struct v4l2_format v4l2_format; uint32_t reqbufs; + int reqbufs_fd; struct buffer buffersMAX_BUFFERS; uint32_t n_buffers; uint32_t size; + uint32_t sequence; + struct pw_array buffer_maps; uint32_t last_fourcc; unsigned int running:1; + unsigned int closed:1; int fd; }; -#define MAX_PARAMS 32 - struct global_info { const char *type; uint32_t version; @@ -156,8 +167,8 @@ int changed; void *info; + struct spa_list pending_list; struct spa_list param_list; - int param_seqMAX_PARAMS; union { struct { @@ -173,6 +184,7 @@ struct param { struct spa_list link; uint32_t id; + int32_t seq; struct spa_pod *param; }; @@ -192,7 +204,7 @@ } static struct param *add_param(struct spa_list *params, - int seq, int *param_seq, uint32_t id, const struct spa_pod *param) + int seq, uint32_t id, const struct spa_pod *param) { struct param *p; @@ -204,24 +216,12 @@ id = SPA_POD_OBJECT_ID(param); } - if (id >= MAX_PARAMS) { - pw_log_error("too big param id %d", id); - errno = EINVAL; - return NULL; - } - - if (seq != param_seqid) { - pw_log_debug("ignoring param %d, seq:%d != current_seq:%d", - id, seq, param_seqid); - errno = EBUSY; - return NULL; - } - p = malloc(sizeof(*p) + (param != NULL ? SPA_POD_SIZE(param) : 0)); if (p == NULL) return NULL; p->id = id; + p->seq = seq; if (param != NULL) { p->param = SPA_PTROFF(p, sizeof(*p), struct spa_pod); memcpy(p->param, param, SPA_POD_SIZE(param)); @@ -234,6 +234,39 @@ return p; } +static void update_params(struct file *file) +{ + struct param *p, *t; + struct global *node; + struct pw_node_info *info; + uint32_t i; + + if ((node = file->node) == NULL) + return; + if ((info = node->info) == NULL) + return; + + for (i = 0; i < info->n_params; i++) { + spa_list_for_each_safe(p, t, &node->pending_list, link) { + if (p->id == info->paramsi.id && + p->seq != info->paramsi.seq && + p->param != NULL) { + spa_list_remove(&p->link); + free(p); + } + } + } + + spa_list_consume(p, &node->pending_list, link) { + spa_list_remove(&p->link); + if (p->param == NULL) { + clear_params(&node->param_list, p->id); + free(p); + } else { + spa_list_append(&node->param_list, &p->link); + } + } +} #define ATOMIC_DEC(s) __atomic_sub_fetch(&(s), 1, __ATOMIC_SEQ_CST) #define ATOMIC_INC(s) __atomic_add_fetch(&(s), 1, __ATOMIC_SEQ_CST) @@ -247,6 +280,8 @@ file->ref = 1; file->fd = -1; + file->reqbufs_fd = -1; + file->priority = V4L2_PRIORITY_DEFAULT; spa_list_init(&file->globals); pw_array_init(&file->buffer_maps, sizeof(struct buffer_map) * MAX_BUFFERS); return file; @@ -254,6 +289,8 @@ static void free_file(struct file *file) { + pw_log_info("file:%d", file->fd); + if (file->loop) pw_thread_loop_stop(file->loop); @@ -282,49 +319,99 @@ static void unref_file(struct file *file) { + pw_log_debug("file:%d ref:%d", file->fd, file->ref); if (ATOMIC_DEC(file->ref) <= 0) free_file(file); } -static int add_fd_map(int fd, struct file *file) +static int add_fd_map(int fd, struct file *file, uint32_t flags) { struct fd_map *map; pthread_mutex_lock(&globals.lock); map = pw_array_add(&globals.fd_maps, sizeof(*map)); if (map != NULL) { map->fd = fd; + map->flags = flags; map->file = file; ATOMIC_INC(file->ref); + pw_log_debug("fd:%d -> file:%d ref:%d", fd, file->fd, file->ref); } pthread_mutex_unlock(&globals.lock); return 0; } +static uint32_t find_dev_for_serial(uint32_t serial) +{ + uint32_t i, res = SPA_ID_INVALID; + pthread_mutex_lock(&globals.lock); + for (i = 0; i < SPA_N_ELEMENTS(globals.dev_map); i++) { + if (globals.dev_mapi == serial) { + res = i; + break; + } + } + pthread_mutex_unlock(&globals.lock); + return res; +} + +static bool add_dev_for_serial(uint32_t dev, uint32_t serial) +{ + pthread_mutex_lock(&globals.lock); + globals.dev_mapdev = serial; + pthread_mutex_unlock(&globals.lock); + return true; +} + /* must be called with `globals.lock` held */ static struct fd_map *find_fd_map_unlocked(int fd) { struct fd_map *map; - pw_array_for_each(map, &globals.fd_maps) { if (map->fd == fd) { ATOMIC_INC(map->file->ref); + pw_log_debug("fd:%d find:%d ref:%d", map->fd, fd, map->file->ref); return map; } } - return NULL; } -static struct file *find_file(int fd) +static struct file *find_file(int fd, uint32_t *flags) { pthread_mutex_lock(&globals.lock); struct fd_map *map = find_fd_map_unlocked(fd); struct file *file = NULL; - if (map != NULL) + if (map != NULL) { file = map->file; + *flags = map->flags; + } + + pthread_mutex_unlock(&globals.lock); + + return file; +} + +static struct file *find_file_by_dev(uint32_t dev) +{ + struct fd_map *map = NULL, *tmp; + struct file *file = NULL; + pthread_mutex_lock(&globals.lock); + pw_array_for_each(tmp, &globals.fd_maps) { + if (tmp->file->dev_id == dev) { + if (tmp->file->closed) + tmp->file->fd = tmp->fd; + ATOMIC_INC(tmp->file->ref); + map = tmp; + pw_log_debug("dev:%d find:%d ref:%d", + tmp->file->dev_id, dev, tmp->file->ref); + break; + } + } + if (map != NULL) + file = map->file; pthread_mutex_unlock(&globals.lock); return file; @@ -339,6 +426,7 @@ if (map != NULL) { file = map->file; + pw_log_debug("fd:%d find:%d", map->fd, fd); pw_array_remove(&globals.fd_maps, map); } @@ -350,7 +438,7 @@ return file; } -static int add_file_map(void *addr, struct file *file) +static int add_file_map(struct file *file, void *addr) { struct file_map *map; pthread_mutex_lock(&globals.lock); @@ -448,15 +536,17 @@ return; file->last_seq = seq; - if (file->pending_seq == seq) + if (file->pending_seq == seq) { + update_params(file); pw_thread_loop_signal(file->loop, false); + } } static void on_error(void *data, uint32_t id, int seq, int res, const char *message) { struct file *file = data; - pw_log_warn("%p: error id:%u seq:%d res:%d (%s): %s", file, + pw_log_warn("file:%d: error id:%u seq:%d res:%d (%s): %s", file->fd, id, seq, res, spa_strerror(res), message); if (id == PW_ID_CORE) { @@ -485,6 +575,8 @@ uint32_t i; info = g->info = pw_node_info_merge(g->info, info, g->changed == 0); + if (info == NULL) + return; pw_log_debug("update %d %"PRIu64, g->id, info->change_mask); @@ -512,21 +604,14 @@ continue; info->paramsi.user = 0; - if (id >= MAX_PARAMS) { - pw_log_error("too big param id %d", id); - continue; - } - - if (id != SPA_PARAM_EnumFormat) - continue; - + add_param(&g->pending_list, info->paramsi.seq, id, NULL); if (!(info->paramsi.flags & SPA_PARAM_INFO_READ)) continue; res = pw_node_enum_params((struct pw_node*)g->proxy, - ++g->param_seqid, id, 0, -1, NULL); + ++info->paramsi.seq, id, 0, -1, NULL); if (SPA_RESULT_IS_ASYNC(res)) - g->param_seqid = res; + info->paramsi.seq = res; } } do_resync(file); @@ -538,8 +623,8 @@ { struct global *g = object; - pw_log_debug("update param %d %d %d %d", g->id, id, seq, g->param_seqid); - add_param(&g->param_list, seq, g->param_seq, id, param); + pw_log_debug("update param %d %d %d", g->id, id, seq); + add_param(&g->pending_list, seq, id, param); } static const struct pw_node_events node_events = { @@ -566,6 +651,10 @@ struct global *g = data; spa_list_remove(&g->link); g->proxy = NULL; + if (g->file) + g->file->node = NULL; + clear_params(&g->param_list, SPA_ID_INVALID); + clear_params(&g->pending_list, SPA_ID_INVALID); } static const struct pw_proxy_events proxy_events = { @@ -582,20 +671,31 @@ const struct global_info *info = NULL; struct pw_proxy *proxy; const char *str; - - pw_log_debug("got %d %s", id, type); + uint32_t serial = SPA_ID_INVALID, dev; if (spa_streq(type, PW_TYPE_INTERFACE_Node)) { + if (file->node != NULL) return; - if (props == NULL || - ((str = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)) == NULL) || + pw_log_info("got %d %s", id, type); + + if (props == NULL) + return; + if (((str = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)) == NULL) || ((!spa_streq(str, "Video/Sink")) && (!spa_streq(str, "Video/Source")))) return; - pw_log_debug("found node %d type:%s", id, str); + if (((str = spa_dict_lookup(props, PW_KEY_OBJECT_SERIAL)) == NULL) || + !spa_atou32(str, &serial, 10)) + return; + + dev = find_dev_for_serial(serial); + if (dev != SPA_ID_INVALID && dev != file->dev_id) + return; + + pw_log_info("found node:%d serial:%d type:%s", id, serial, str); info = &node_info; } if (info) { @@ -612,6 +712,7 @@ g->permissions = permissions; g->props = props ? pw_properties_new_dict(props) : NULL; g->proxy = proxy; + spa_list_init(&g->pending_list); spa_list_init(&g->param_list); spa_list_append(&file->globals, &g->link); @@ -627,6 +728,7 @@ if (info->init) info->init(g); + file->serial = serial; file->node = g; do_resync(file); @@ -660,17 +762,58 @@ .global_remove = registry_event_global_remove, }; -static int v4l2_openat(int dirfd, const char *path, int oflag, mode_t mode) +static int do_dup(int oldfd, uint32_t flags) { int res; struct file *file; + uint32_t fl; - if (!spa_strstartswith(path, "/dev/video0")) + res = globals.old_fops.dup(oldfd); + if (res < 0) + return res; + + if ((file = find_file(oldfd, &fl)) != NULL) { + add_fd_map(res, file, flags | fl); + unref_file(file); + pw_log_info("fd:%d %08x -> %d (%s)", oldfd, flags, + res, strerror(res < 0 ? errno : 0)); + } + return res; +} + +static int v4l2_dup(int oldfd) +{ + return do_dup(oldfd, FD_MAP_DUP); +} + +static int v4l2_openat(int dirfd, const char *path, int oflag, mode_t mode) +{ + int res, flags; + struct file *file; + bool passthrough = true; + uint32_t dev_id = SPA_ID_INVALID; + + if (spa_strstartswith(path, "/dev/video")) { + if (spa_atou32(path+10, &dev_id, 10) && dev_id < MAX_DEV) + passthrough = false; + } + if (passthrough) return globals.old_fops.openat(dirfd, path, oflag, mode); + pw_log_info("path:%s oflag:%d mode:%d", path, oflag, mode); + + if ((file = find_file_by_dev(dev_id)) != NULL) { + res = do_dup(file->fd, 0); + unref_file(file); + if (res >= 0) + fcntl(res, F_SETFL, oflag); + return res; + } + if ((file = make_file()) == NULL) goto error; + file->dev_id = dev_id; file->props = pw_properties_new( PW_KEY_CLIENT_API, "v4l2", NULL); @@ -711,47 +854,41 @@ goto error_unlock; } if (file->node == NULL) { - errno = -ENOENT; + errno = ENOENT; goto error_unlock; } pw_thread_loop_unlock(file->loop); - res = file->fd = spa_system_eventfd_create(file->l->system, - SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); + flags = SPA_FD_CLOEXEC; + if (oflag & O_NONBLOCK) + flags |= SPA_FD_NONBLOCK; + + res = spa_system_eventfd_create(file->l->system, flags); if (res < 0) goto error; + file->fd = res; + pw_log_info("path:%s oflag:%d mode:%d -> %d (%s)", path, oflag, mode, res, strerror(res < 0 ? errno : 0)); - add_fd_map(res, file); + add_fd_map(res, file, 0); + add_dev_for_serial(file->dev_id, file->serial); + unref_file(file); return res; error_unlock: pw_thread_loop_unlock(file->loop); error: + res = -errno; if (file) free_file(file); - return -1; -} - -static int v4l2_dup(int oldfd) -{ - int res; - struct file *file; - res = globals.old_fops.dup(oldfd); - if (res < 0) - return res; - - if ((file = find_file(oldfd)) != NULL) { - add_fd_map(res, file); - unref_file(file); - pw_log_info("fd:%d -> %d (%s)", oldfd, - res, strerror(res < 0 ? errno : 0)); - } - return res; + pw_log_info("path:%s oflag:%d mode:%d -> %d (%s)", path, oflag, mode, + -1, spa_strerror(res)); + errno = -res; + return -1; } static int v4l2_close(int fd) @@ -761,9 +898,12 @@ if ((file = remove_fd_map(fd)) == NULL) return globals.old_fops.close(fd); + pw_log_info("fd:%d file:%d", fd, file->fd); + if (fd != file->fd) spa_system_close(file->l->system, fd); + file->closed = true; unref_file(file); return 0; @@ -774,10 +914,24 @@ static int vidioc_querycap(struct file *file, struct v4l2_capability *arg) { int res = 0; + const char *str = NULL; + struct pw_node_info *info; + + if (file->node == NULL) + return -EIO; + + info = file->node->info; + + if (info != NULL && info->props != NULL) { + str = spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION); + } + if (str == NULL) + str = DEFAULT_CARD; spa_scnprintf((char*)arg->driver, sizeof(arg->driver), "%s", DEFAULT_DRIVER); - spa_scnprintf((char*)arg->card, sizeof(arg->card), "%s", DEFAULT_CARD); - spa_scnprintf((char*)arg->bus_info, sizeof(arg->bus_info), "%s:%d", DEFAULT_BUS_INFO, 1); + spa_scnprintf((char*)arg->card, sizeof(arg->card), "%s", str); + spa_scnprintf((char*)arg->bus_info, sizeof(arg->bus_info), "platform:%s-%d", + DEFAULT_BUS_INFO, file->node->id); arg->version = KERNEL_VERSION(5, 2, 0); arg->device_caps = V4L2_CAP_VIDEO_CAPTURE @@ -786,7 +940,7 @@ arg->capabilities = arg->device_caps | V4L2_CAP_DEVICE_CAPS; memset(arg->reserved, 0, sizeof(arg->reserved)); - pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res)); + pw_log_info("file:%d -> %d (%s)", file->fd, res, spa_strerror(res)); return res; } @@ -799,17 +953,17 @@ const char *desc; }; -#define MAKE_FORMAT(fcc,mt,mst,bpp,fmt) \ - { V4L2_PIX_FMT_ ## fcc, SPA_MEDIA_TYPE_ ## mt, SPA_MEDIA_SUBTYPE_ ## mst, SPA_VIDEO_FORMAT_ ## fmt, bpp, #fcc } +#define MAKE_FORMAT(fcc,mt,mst,bpp,fmt,...) \ + { V4L2_PIX_FMT_ ## fcc, SPA_MEDIA_TYPE_ ## mt, SPA_MEDIA_SUBTYPE_ ## mst, SPA_VIDEO_FORMAT_ ## fmt, bpp, __VA_ARGS__ } static const struct format_info format_info = { /* RGB formats */ - MAKE_FORMAT(RGB332, video, raw, 4, UNKNOWN), + MAKE_FORMAT(RGB332, video, raw, 4, UNKNOWN, "8-bit RGB 3-3-2"), MAKE_FORMAT(ARGB555, video, raw, 4, UNKNOWN), - MAKE_FORMAT(XRGB555, video, raw, 4, RGB15), + MAKE_FORMAT(XRGB555, video, raw, 4, RGB15, "16-bit XRGB 1-5-5-5"), MAKE_FORMAT(ARGB555X, video, raw, 4, UNKNOWN), - MAKE_FORMAT(XRGB555X, video, raw, 4, BGR15), - MAKE_FORMAT(RGB565, video, raw, 4, RGB16), + MAKE_FORMAT(XRGB555X, video, raw, 4, BGR15, "16-bit XRGB 1-5-5-5 BE"), + MAKE_FORMAT(RGB565, video, raw, 4, RGB16, "16-bit RGB 5-6-5"), MAKE_FORMAT(RGB565X, video, raw, 4, UNKNOWN), MAKE_FORMAT(BGR666, video, raw, 4, UNKNOWN), MAKE_FORMAT(BGR24, video, raw, 4, BGR), @@ -843,13 +997,13 @@ MAKE_FORMAT(UV8, video, raw, 2, UNKNOWN), /* Luminance+Chrominance formats */ - MAKE_FORMAT(YVU410, video, raw, 1, YVU9), - MAKE_FORMAT(YVU420, video, raw, 1, YV12), + MAKE_FORMAT(YVU410, video, raw, 1, YVU9, "Planar YVU 4:1:0"), + MAKE_FORMAT(YVU420, video, raw, 1, YV12, "Planar YVU 4:2:0"), MAKE_FORMAT(YVU420M, video, raw, 1, UNKNOWN), - MAKE_FORMAT(YUYV, video, raw, 2, YUY2), + MAKE_FORMAT(YUYV, video, raw, 2, YUY2, "YUYV 4:2:2"), MAKE_FORMAT(YYUV, video, raw, 2, UNKNOWN), - MAKE_FORMAT(YVYU, video, raw, 2, YVYU), - MAKE_FORMAT(UYVY, video, raw, 2, UYVY), + MAKE_FORMAT(YVYU, video, raw, 2, YVYU, "YVYU 4:2:2"), + MAKE_FORMAT(UYVY, video, raw, 2, UYVY, "UYVY 4:2:2"), MAKE_FORMAT(VYUY, video, raw, 2, UNKNOWN), MAKE_FORMAT(YUV422P, video, raw, 1, Y42B), MAKE_FORMAT(YUV411P, video, raw, 1, Y41B), @@ -859,24 +1013,24 @@ MAKE_FORMAT(YUV565, video, raw, 1, UNKNOWN), MAKE_FORMAT(YUV32, video, raw, 1, UNKNOWN), MAKE_FORMAT(YUV410, video, raw, 1, YUV9), - MAKE_FORMAT(YUV420, video, raw, 1, I420), - MAKE_FORMAT(YUV420M, video, raw, 1, I420), + MAKE_FORMAT(YUV420, video, raw, 1, I420, "Planar YUV 4:2:0"), + MAKE_FORMAT(YUV420M, video, raw, 1, I420, "Planar YUV 4:2:0 (N-C)"), MAKE_FORMAT(HI240, video, raw, 1, UNKNOWN), MAKE_FORMAT(HM12, video, raw, 1, UNKNOWN), MAKE_FORMAT(M420, video, raw, 1, UNKNOWN), /* two planes -- one Y, one Cr + Cb interleaved */ - MAKE_FORMAT(NV12, video, raw, 1, NV12), - MAKE_FORMAT(NV12M, video, raw, 1, NV12), - MAKE_FORMAT(NV12MT, video, raw, 1, NV12_64Z32), + MAKE_FORMAT(NV12, video, raw, 1, NV12, "Y/CbCr 4:2:0"), + MAKE_FORMAT(NV12M, video, raw, 1, NV12, "Y/CbCr 4:2:0 (N-C)"), + MAKE_FORMAT(NV12MT, video, raw, 1, NV12_64Z32, "Y/CbCr 4:2:0 (64x32 MB, N-C)"), MAKE_FORMAT(NV12MT_16X16, video, raw, 1, UNKNOWN), - MAKE_FORMAT(NV21, video, raw, 1, NV21), - MAKE_FORMAT(NV21M, video, raw, 1, NV21), - MAKE_FORMAT(NV16, video, raw, 1, NV16), - MAKE_FORMAT(NV16M, video, raw, 1, NV16), - MAKE_FORMAT(NV61, video, raw, 1, NV61), - MAKE_FORMAT(NV61M, video, raw, 1, NV61), - MAKE_FORMAT(NV24, video, raw, 1, NV24), + MAKE_FORMAT(NV21, video, raw, 1, NV21, "Y/CrCb 4:2:0"), + MAKE_FORMAT(NV21M, video, raw, 1, NV21, "Y/CrCb 4:2:0 (N-C)"), + MAKE_FORMAT(NV16, video, raw, 1, NV16, "Y/CbCr 4:2:2"), + MAKE_FORMAT(NV16M, video, raw, 1, NV16, "Y/CbCr 4:2:2 (N-C)"), + MAKE_FORMAT(NV61, video, raw, 1, NV61, "Y/CrCb 4:2:2"), + MAKE_FORMAT(NV61M, video, raw, 1, NV61, "Y/CrCb 4:2:2 (N-C)"), + MAKE_FORMAT(NV24, video, raw, 1, NV24, "Y/CbCr 4:4:4"), MAKE_FORMAT(NV42, video, raw, 1, UNKNOWN), /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */ @@ -886,14 +1040,14 @@ MAKE_FORMAT(SRGGB8, video, bayer, 1, UNKNOWN), /* compressed formats */ - MAKE_FORMAT(MJPEG, video, mjpg, 1, ENCODED), - MAKE_FORMAT(JPEG, video, mjpg, 1, ENCODED), - MAKE_FORMAT(PJPG, video, mjpg, 1, ENCODED), + MAKE_FORMAT(MJPEG, video, mjpg, 1, ENCODED, "Motion-JPEG"), + MAKE_FORMAT(JPEG, video, mjpg, 1, ENCODED, "JFIF JPEG"), + MAKE_FORMAT(PJPG, video, mjpg, 1, ENCODED, "GSPCA PJPG"), MAKE_FORMAT(DV, video, dv, 1, ENCODED), MAKE_FORMAT(MPEG, video, mpegts, 1, ENCODED), - MAKE_FORMAT(H264, video, h264, 1, ENCODED), - MAKE_FORMAT(H264_NO_SC, video, h264, 1, ENCODED), - MAKE_FORMAT(H264_MVC, video, h264, 1, ENCODED), + MAKE_FORMAT(H264, video, h264, 1, ENCODED, "H.264"), + MAKE_FORMAT(H264_NO_SC, video, h264, 1, ENCODED, "H.264 (No Start Codes)"), + MAKE_FORMAT(H264_MVC, video, h264, 1, ENCODED, "H.264 MVC"), MAKE_FORMAT(H263, video, h263, 1, ENCODED), MAKE_FORMAT(MPEG1, video, mpeg1, 1, ENCODED), MAKE_FORMAT(MPEG2, video, mpeg2, 1, ENCODED), @@ -913,23 +1067,19 @@ static const struct format_info *format_info_from_media_type(uint32_t type, uint32_t subtype, uint32_t format) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) { - if ((format_infoi.media_type == type) && - (format_infoi.media_subtype == subtype) && - (format == 0 || format_infoi.format == format)) - return &format_infoi; - } + SPA_FOR_EACH_ELEMENT_VAR(format_info, i) + if ((i->media_type == type) && + (i->media_subtype == subtype) && + (format == 0 || i->format == format)) + return i; return NULL; } static const struct format_info *format_info_from_fourcc(uint32_t fourcc) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) { - if (format_infoi.fourcc == fourcc) - return &format_infoi; - } + SPA_FOR_EACH_ELEMENT_VAR(format_info, i) + if (i->fourcc == fourcc) + return i; return NULL; } @@ -940,7 +1090,7 @@ pw_log_info("type: %u", arg->type); pw_log_info("width: %u", arg->fmt.pix.width); pw_log_info("height: %u", arg->fmt.pix.height); - pw_log_info("fmt: %u", arg->fmt.pix.pixelformat); + pw_log_info("fmt: %.4s", (char*)&arg->fmt.pix.pixelformat); if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1064,22 +1214,30 @@ case SPA_MEDIA_SUBTYPE_raw: fmt->fmt.pix.width = info->info.raw.size.width; fmt->fmt.pix.height = info->info.raw.size.height; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; break; case SPA_MEDIA_SUBTYPE_mjpg: case SPA_MEDIA_SUBTYPE_jpeg: fmt->fmt.pix.width = info->info.mjpg.size.width; fmt->fmt.pix.height = info->info.mjpg.size.height; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; break; case SPA_MEDIA_SUBTYPE_h264: fmt->fmt.pix.width = info->info.h264.size.width; fmt->fmt.pix.height = info->info.h264.size.height; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; break; default: return -EINVAL; } + if (fmt->fmt.pix.width == 0 || + fmt->fmt.pix.height == 0) + return -EINVAL; + fmt->fmt.pix.bytesperline = SPA_ROUND_UP_N(fmt->fmt.pix.width, 4) * fi->bpp; fmt->fmt.pix.sizeimage = fmt->fmt.pix.bytesperline * SPA_ROUND_UP_N(fmt->fmt.pix.height, 2); + return 0; } @@ -1108,7 +1266,7 @@ uint32_t n_params = 0; uint8_t buffer4096; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - uint32_t buffers, size; + uint32_t buffers, size, stride; struct v4l2_format fmt; if (param == NULL || id != SPA_PARAM_Format) @@ -1119,18 +1277,20 @@ file->v4l2_format = fmt; - buffers = SPA_CLAMP(file->reqbufs, 2u, MAX_BUFFERS); - size = 0; + buffers = SPA_CLAMP(file->reqbufs, 1u, MAX_BUFFERS); + size = fmt.fmt.pix.sizeimage; + stride = fmt.fmt.pix.bytesperline; paramsn_params++ = spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(buffers, - 2, MAX_BUFFERS), + 1, MAX_BUFFERS), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(size, 0, INT_MAX), - SPA_PARAM_BUFFERS_stride, SPA_POD_CHOICE_RANGE_Int(0, 0, INT_MAX), + SPA_PARAM_BUFFERS_stride, SPA_POD_CHOICE_RANGE_Int(stride, 0, INT_MAX), SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int((1<<SPA_DATA_MemFd))); + pw_stream_update_params(file->stream, params, n_params); } @@ -1140,7 +1300,7 @@ { struct file *file = data; - pw_log_info("%p: state %s", file, pw_stream_state_as_string(state)); + pw_log_info("file:%d: state %s", file->fd, pw_stream_state_as_string(state)); switch (state) { case PW_STREAM_STATE_ERROR: @@ -1165,7 +1325,8 @@ file->size = d->maxsize; - pw_log_info("%p: id:%d fd:%"PRIi64" size:%u", file, id, d->fd, file->size); + pw_log_info("file:%d: id:%d fd:%"PRIi64" size:%u offset:%u", file->fd, + id, d->fd, file->size, id * file->size); spa_zero(vb); vb.index = id; @@ -1192,7 +1353,8 @@ static void on_stream_process(void *data) { struct file *file = data; - spa_system_eventfd_write(file->l->system, file->fd, 1); + pw_log_debug("file:%d", file->fd); + spa_system_eventfd_write(file->l->system, file->fd, 1); } static const struct pw_stream_events stream_events = { @@ -1251,7 +1413,7 @@ arg->discrete.width = size.width; arg->discrete.height = size.height; - pw_log_debug("count:%d %d %dx%d", count, fi->fourcc, + pw_log_debug("count:%d %.4s %dx%d", count, (char*)&fi->fourcc, size.width, size.height); if (count == arg->index) { found = true; @@ -1327,10 +1489,13 @@ if (fi->fourcc == last_fourcc) continue; - pw_log_info("count:%d %d %d", count, fi->fourcc, last_fourcc); + pw_log_info("count:%d fourcc:%.4s last:%.4s", count, + (char*)&fi->fourcc, (char*)&last_fourcc); arg->flags = fi->format == SPA_VIDEO_FORMAT_ENCODED ? V4L2_FMT_FLAG_COMPRESSED : 0; arg->pixelformat = fi->fourcc; + snprintf((char*)arg->description, sizeof(arg->description), "%s", + fi->desc ? fi->desc : "Unknown"); last_fourcc = fi->fourcc; if (count == arg->index) { found = true; @@ -1343,7 +1508,7 @@ if (!found) return -EINVAL; - pw_log_info("format: %u", arg->pixelformat); + pw_log_info("format: %.4s", (char*)&arg->pixelformat); pw_log_info("flags: %u", arg->type); memset(arg->reserved, 0, sizeof(arg->reserved)); @@ -1352,21 +1517,50 @@ static int vidioc_g_fmt(struct file *file, struct v4l2_format *arg) { + struct param *p; + struct global *g = file->node; + int res; + if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - *arg = file->v4l2_format; - return 0; + pw_thread_loop_lock(file->loop); + if (file->v4l2_format.fmt.pix.pixelformat != 0) { + *arg = file->v4l2_format; + } else { + struct v4l2_format tmp; + bool found = false; + + spa_list_for_each(p, &g->param_list, link) { + if (p->id != SPA_PARAM_EnumFormat || p->param == NULL) + continue; + + if (param_to_fmt(p->param, &tmp) < 0) + continue; + + found = true; + break; + } + if (!found) { + res = -EINVAL; + goto exit_unlock; + } + *arg = file->v4l2_format = tmp; + } + res = 0; +exit_unlock: + pw_thread_loop_unlock(file->loop); + return res; } static int score_diff(struct v4l2_format *fmt, struct v4l2_format *tmp) { - int score = 0; + int score = 0, w, h; if (fmt->fmt.pix.pixelformat != tmp->fmt.pix.pixelformat) score += 20000; - score += SPA_ABS((int)fmt->fmt.pix.width - (int)tmp->fmt.pix.width); - score += SPA_ABS((int)fmt->fmt.pix.height - (int)tmp->fmt.pix.height); - return score; + w = SPA_ABS((int)fmt->fmt.pix.width - (int)tmp->fmt.pix.width); + h = SPA_ABS((int)fmt->fmt.pix.height - (int)tmp->fmt.pix.height); + return score + (w*w) + (h*h); } static int try_format(struct file *file, struct v4l2_format *fmt) @@ -1377,6 +1571,9 @@ int best = -1; pw_log_info("in: type: %u", fmt->type); + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + pw_log_info("in: format: %.4s", (char*)&fmt->fmt.pix.pixelformat); pw_log_info("in: width: %u", fmt->fmt.pix.width); pw_log_info("in: height: %u", fmt->fmt.pix.height); @@ -1385,7 +1582,10 @@ struct v4l2_format tmp; int score; - if (p->id != SPA_PARAM_EnumFormat || p->param == NULL) + if (p->param == NULL) + continue; + + if (p->id != SPA_PARAM_EnumFormat && p->id != SPA_PARAM_Format) continue; if (param_to_fmt(p->param, &tmp) < 0) @@ -1398,6 +1598,10 @@ pw_log_debug("check: height: %u", tmp.fmt.pix.height); pw_log_debug("check: score: %d best:%d", score, best); + if (p->id == SPA_PARAM_Format) { + best_fmt = tmp; + break; + } if (best == -1 || score < best) { best = score; best_fmt = tmp; @@ -1415,6 +1619,7 @@ static int disconnect_stream(struct file *file) { if (file->stream != NULL) { + pw_log_info("file:%d disconnect", file->fd); pw_stream_destroy(file->stream); file->stream = NULL; file->n_buffers = 0; @@ -1530,34 +1735,60 @@ static int vidioc_enuminput(struct file *file, struct v4l2_input *arg) { - if (arg->index != 0) - return -EINVAL; - + uint32_t index = arg->index; spa_zero(*arg); - spa_scnprintf((char*)arg->name, sizeof(arg->name), "%s", DEFAULT_CARD); - arg->type = V4L2_INPUT_TYPE_CAMERA; - - return 0; + arg->index = index; + switch (arg->index) { + case 0: + spa_scnprintf((char*)arg->name, sizeof(arg->name), "%s", DEFAULT_CARD); + arg->type = V4L2_INPUT_TYPE_CAMERA; + break; + default: + return -EINVAL; + } + return 0; } static int vidioc_g_input(struct file *file, int *arg) { *arg = 0; - return 0; + return 0; } static int vidioc_s_input(struct file *file, int *arg) { if (*arg != 0) return -EINVAL; - return 0; + return 0; +} + +static int vidioc_g_priority(struct file *file, enum v4l2_priority *arg) +{ + *arg = file->priority; + pw_log_info("file:%d prio:%d", file->fd, *arg); + return 0; +} +static int vidioc_s_priority(struct file *file, int fd, enum v4l2_priority *arg) +{ + if (*arg > V4L2_PRIORITY_RECORD) + return -EINVAL; + + if (file->fd != fd && file->priority > *arg) + return -EINVAL; + + pw_log_info("file:%d (%d) prio:%d", file->fd, fd, *arg); + file->priority = *arg; + return 0; } -static int vidioc_reqbufs(struct file *file, struct v4l2_requestbuffers *arg) +static int vidioc_reqbufs(struct file *file, int fd, struct v4l2_requestbuffers *arg) { int res; pw_log_info("count: %u", arg->count); pw_log_info("type: %u", arg->type); pw_log_info("memory: %u", arg->memory); +#ifdef V4L2_MEMORY_FLAG_NON_COHERENT + pw_log_info("flags: %08x", arg->flags); +#endif if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1566,17 +1797,25 @@ pw_thread_loop_lock(file->loop); + if (file->n_buffers > 0 && file->reqbufs_fd != fd) { + pw_log_info("%u fd:%d != %d", file->n_buffers, file->reqbufs_fd, fd); + res = -EBUSY; + goto exit_unlock; + } if (arg->count == 0) { if (pw_array_get_len(&file->buffer_maps, struct buffer_map) != 0) { + pw_log_info("fd:%d have maps", fd); res = -EBUSY; goto exit_unlock; } if (file->running) { + pw_log_info("fd:%d running", fd); res = -EBUSY; goto exit_unlock; } - file->reqbufs = 0; res = disconnect_stream(file); + file->reqbufs = 0; + file->reqbufs_fd = -1; } else { file->reqbufs = arg->count; @@ -1584,7 +1823,11 @@ goto exit_unlock; arg->count = file->n_buffers; + file->reqbufs_fd = fd; } +#ifdef V4L2_MEMORY_FLAG_NON_COHERENT + arg->flags = 0; +#endif #ifdef V4L2_BUF_CAP_SUPPORTS_MMAP arg->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP; #endif @@ -1593,6 +1836,8 @@ pw_log_info("result count: %u", arg->count); exit_unlock: + if (res < 0) + pw_log_info("error : %s", spa_strerror(res)); pw_thread_loop_unlock(file->loop); return res; } @@ -1645,42 +1890,52 @@ arg->flags = buf->v4l2.flags; pw_stream_queue_buffer(file->stream, buf->buf); - pw_log_debug("file:%p %d -> %d (%s)", file, arg->index, res, spa_strerror(res)); - exit: + pw_log_debug("file:%d %d -> %d (%s)", file->fd, arg->index, res, spa_strerror(res)); pw_thread_loop_unlock(file->loop); return res; } -static int vidioc_dqbuf(struct file *file, struct v4l2_buffer *arg) +static int vidioc_dqbuf(struct file *file, int fd, struct v4l2_buffer *arg) { int res = 0; struct pw_buffer *b; struct buffer *buf; uint64_t val; struct spa_data *d; + struct timespec ts; if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (arg->memory != V4L2_MEMORY_MMAP) return -EINVAL; + pw_log_debug("file:%d (%d) %d", file->fd, fd, + arg->index); + pw_thread_loop_lock(file->loop); if (arg->index >= file->n_buffers) { res = -EINVAL; goto exit_unlock; } - if (!file->running) { - res = -EINVAL; - goto exit_unlock; - } - b = pw_stream_dequeue_buffer(file->stream); - if (b == NULL) { - res = -EAGAIN; - goto exit_unlock; + while (true) { + if (!file->running) { + res = -EINVAL; + goto exit_unlock; + } + + b = pw_stream_dequeue_buffer(file->stream); + if (b != NULL) + break; + + pw_thread_loop_unlock(file->loop); + res = spa_system_eventfd_read(file->l->system, fd, &val); + pw_thread_loop_lock(file->loop); + if (res < 0) + goto exit_unlock; } - spa_system_eventfd_read(file->l->system, file->fd, &val); + buf = b->user_data; d = &buf->buf->buffer->datas0; @@ -1689,13 +1944,20 @@ SPA_FLAG_UPDATE(buf->v4l2.flags, V4L2_BUF_FLAG_ERROR, SPA_FLAG_IS_SET(d->chunk->flags, SPA_CHUNK_FLAG_CORRUPTED)); + SPA_FLAG_SET(buf->v4l2.flags, V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC); + clock_gettime(CLOCK_MONOTONIC, &ts); + buf->v4l2.timestamp.tv_sec = ts.tv_sec; + buf->v4l2.timestamp.tv_usec = ts.tv_nsec / 1000; + + buf->v4l2.field = V4L2_FIELD_NONE; buf->v4l2.bytesused = d->chunk->size; + buf->v4l2.sequence = file->sequence++; *arg = buf->v4l2; exit_unlock: + pw_log_debug("file:%d (%d) %d -> %d (%s)", file->fd, fd, + arg->index, res, spa_strerror(res)); pw_thread_loop_unlock(file->loop); - - pw_log_debug("file:%p %d -> %d (%s)", file, arg->index, res, spa_strerror(res)); return res; } @@ -1703,8 +1965,6 @@ { int res; - pw_log_info("file:%p -> %d", file, *arg); - if (*arg != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1723,28 +1983,34 @@ exit_unlock: pw_thread_loop_unlock(file->loop); - pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res)); + pw_log_info("file:%d -> %d (%s)", file->fd, res, spa_strerror(res)); return res; } static int vidioc_streamoff(struct file *file, int *arg) { int res; + uint32_t i; if (*arg != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; pw_thread_loop_lock(file->loop); + for (i = 0; i < file->n_buffers; i++) { + struct buffer *buf = &file->buffersi; + SPA_FLAG_CLEAR(buf->v4l2.flags, V4L2_BUF_FLAG_QUEUED); + } if (!file->running) { res = 0; goto exit_unlock; } res = pw_stream_set_active(file->stream, false); file->running = false; + file->sequence = 0; exit_unlock: pw_thread_loop_unlock(file->loop); - pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res)); + pw_log_info("file:%d -> %d (%s)", file->fd, res, spa_strerror(res)); return res; } @@ -1752,8 +2018,9 @@ { int res; struct file *file; + uint32_t flags; - if ((file = find_file(fd)) == NULL) + if ((file = find_file(fd, &flags)) == NULL) return globals.old_fops.ioctl(fd, request, arg); #if defined(__FreeBSD__) || defined(__MidnightBSD__) @@ -1765,6 +2032,9 @@ goto done; } + if (flags & FD_MAP_DUP) + fd = file->fd; + switch (request & 0xffffffff) { case VIDIOC_QUERYCAP: res = vidioc_querycap(file, (struct v4l2_capability *)arg); @@ -1793,8 +2063,14 @@ case VIDIOC_S_INPUT: res = vidioc_s_input(file, (int *)arg); break; + case VIDIOC_G_PRIORITY: + res = vidioc_g_priority(file, (enum v4l2_priority *)arg); + break; + case VIDIOC_S_PRIORITY: + res = vidioc_s_priority(file, fd, (enum v4l2_priority *)arg); + break; case VIDIOC_REQBUFS: - res = vidioc_reqbufs(file, (struct v4l2_requestbuffers *)arg); + res = vidioc_reqbufs(file, fd, (struct v4l2_requestbuffers *)arg); break; case VIDIOC_QUERYBUF: res = vidioc_querybuf(file, (struct v4l2_buffer *)arg); @@ -1803,7 +2079,7 @@ res = vidioc_qbuf(file, (struct v4l2_buffer *)arg); break; case VIDIOC_DQBUF: - res = vidioc_dqbuf(file, (struct v4l2_buffer *)arg); + res = vidioc_dqbuf(file, fd, (struct v4l2_buffer *)arg); break; case VIDIOC_STREAMON: res = vidioc_streamon(file, (int *)arg); @@ -1820,8 +2096,8 @@ errno = -res; res = -1; } - pw_log_debug("fd:%d request:%lx nr:%d arg:%p -> %d (%s)", - fd, request, (int)_IOC_NR(request), arg, + pw_log_debug("file:%d fd:%d request:%lx nr:%d arg:%p -> %d (%s)", + file->fd, fd, request, (int)_IOC_NR(request), arg, res, strerror(res < 0 ? errno : 0)); unref_file(file); @@ -1838,8 +2114,9 @@ struct pw_map_range range; struct buffer *buf; struct spa_data *data; + uint32_t fl; - if ((file = find_file(fd)) == NULL) + if ((file = find_file(fd, &fl)) == NULL) return globals.old_fops.mmap(addr, length, prot, flags, fd, offset); pw_thread_loop_lock(file->loop); @@ -1864,14 +2141,19 @@ if (!SPA_FLAG_IS_SET(data->flags, SPA_DATA_FLAG_WRITABLE)) prot &= ~PROT_WRITE; - res = globals.old_fops.mmap(addr, range.size, prot, flags, data->fd, range.offset); + if (data->data == NULL) + res = globals.old_fops.mmap(addr, range.size, prot, flags, data->fd, range.offset); + else + res = data->data; - add_file_map(file, addr); - add_buffer_map(file, addr, id); + add_file_map(file, res); + add_buffer_map(file, res, id); SPA_FLAG_SET(buf->v4l2.flags, V4L2_BUF_FLAG_MAPPED); - pw_log_info("addr:%p length:%u prot:%d flags:%d fd:%"PRIi64" offset:%u -> %p (%s)" , - addr, range.size, prot, flags, data->fd, range.offset, + pw_log_info("file:%d addr:%p length:%zu prot:%d flags:%d fd:%"PRIi64 + " offset:%"PRIi64" (%u - %u) -> %p (%s)" , + file->fd, addr, length, prot, flags, data->fd, offset, + range.offset, range.size, res, strerror(res == MAP_FAILED ? errno : 0)); error_unlock: @@ -1884,7 +2166,9 @@ { int res; struct buffer_map *bmap; + struct buffer *buf; struct file *file; + struct spa_data *data; if ((file = remove_file_map(addr)) == NULL) return globals.old_fops.munmap(addr, length); @@ -1896,12 +2180,18 @@ res = -EINVAL; goto exit_unlock; } - res = globals.old_fops.munmap(addr, length); + buf = &file->buffersbmap->id; + data = &buf->buf->buffer->datas0; + + if (data->data == NULL) + res = globals.old_fops.munmap(addr, length); + else + res = 0; pw_log_info("addr:%p length:%zu -> %d (%s)", addr, length, res, strerror(res < 0 ? errno : 0)); - file->buffersbmap->id.v4l2.flags &= ~V4L2_BUF_FLAG_MAPPED; + buf->v4l2.flags &= ~V4L2_BUF_FLAG_MAPPED; remove_buffer_map(file, bmap); exit_unlock:
View file
pipewire-0.3.59.tar.gz/pipewire-v4l2/src/pw-v4l2.in -> pipewire-0.3.60.tar.gz/pipewire-v4l2/src/pw-v4l2.in
Changed
@@ -54,10 +54,16 @@ shift $(( OPTIND - 1 )) +if "$PW_UNINSTALLED" = 1 ; then + PW_V4L2_LD_PRELOAD="$PW_BUILDDIR"'/pipewire-v4l2/src/libpw-v4l2.so' +else + PW_V4L2_LD_PRELOAD='@LIBV4L2_PATH@/libpw-v4l2.so' +fi + if "$LD_PRELOAD" = "" ; then - LD_PRELOAD='@LIBV4L2_PATH@/libpw-v4l2.so' + LD_PRELOAD="$PW_V4L2_LD_PRELOAD" else - LD_PRELOAD="$LD_PRELOAD "'@LIBV4L2_PATH@/libpw-v4l2.so' + LD_PRELOAD="$LD_PRELOAD $PW_V4L2_LD_PRELOAD" fi export LD_PRELOAD
View file
pipewire-0.3.59.tar.gz/po/cs.po -> pipewire-0.3.60.tar.gz/po/cs.po
Changed
@@ -8,10 +8,10 @@ msgid "" msgstr "" "Project-Id-Version: pipewire.master-tx\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: 2021-10-12 14:18+0200\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-10-21 16:44+0200\n" "Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n" "Language-Team: čeština <gnome-cs-list@gnome.org>\n" "Language: cs\n" @@ -19,9 +19,9 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -"X-Generator: Poedit 3.0\n" +"X-Generator: Poedit 3.1.1\n" -#: src/daemon/pipewire.c:43 +#: src/daemon/pipewire.c:46 #, c-format msgid "" "%s options\n" @@ -42,36 +42,52 @@ msgid "Start the PipeWire Media System" msgstr "Spustit multimediální systém PipeWire" -#: src/examples/media-session/alsa-monitor.c:526 -#: spa/plugins/alsa/acp/compat.c:187 -msgid "Built-in Audio" -msgstr "Vnitřní zvukový systém" +#: 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 "Tunel do %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 "Game Output" +msgid "Dummy Output" +msgstr "Předstíraný výstup" -#: src/examples/media-session/alsa-monitor.c:539 +#: src/modules/module-pulse-tunnel.c:662 +#, c-format +msgid "Tunnel for %s@%s" +msgstr "Tunel pro %s@%s" + +#: src/modules/module-zeroconf-discover.c:332 msgid "Unknown device" msgstr "Neznámé zařízení" -#: src/tools/pw-cat.c:991 +#: src/modules/module-zeroconf-discover.c:344 +#, c-format +msgid "%s on %s@%s" +msgstr "%s na %s@%s" + +#: src/modules/module-zeroconf-discover.c:348 +#, c-format +msgid "%s on %s" +msgstr "%s na %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 volby <soubor>\n" +"%s volby <soubor>|-\n" " -h, --help Zobrazit tuto nápovědu\n" " --version Zobrazit verzi\n" " -v, --verbose Povolit podrobné operace\n" "\n" -#: src/tools/pw-cat.c:998 +#: src/tools/pw-cat.c:791 #, c-format msgid "" " -R, --remote Remote daemon name\n" @@ -85,7 +101,7 @@ " 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 Název vzdáleného démonu\n" @@ -102,10 +118,10 @@ " nebo přímé vzorky (256)\n" " frekvence je stejná jako u " "zdrojového souboru\n" -" --list-targets Zobrazit dostupné cíle pro --target\n" +" -P --properties Nastavit vlastnosti uzlu\n" "\n" -#: src/tools/pw-cat.c:1016 +#: src/tools/pw-cat.c:809 #, c-format msgid "" " --rate Sample rate (req. for rec) (default " @@ -141,19 +157,21 @@ "je %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 Playback mód\n" " -r, --record Recording 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" @@ -171,199 +189,195 @@ " -r, --remote Název vzdáleného démonu\n" "\n" -#: spa/plugins/alsa/acp/acp.c:290 +#: spa/plugins/alsa/acp/acp.c:321 msgid "Pro Audio" 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 "Vypnuto" -#: spa/plugins/alsa/acp/channelmap.h:466 -msgid "(invalid)" -msgstr "(neplatné)" - -#: spa/plugins/alsa/acp/alsa-mixer.c:2709 +#: spa/plugins/alsa/acp/alsa-mixer.c:2652 msgid "Input" msgstr "Vstup" -#: spa/plugins/alsa/acp/alsa-mixer.c:2710 +#: spa/plugins/alsa/acp/alsa-mixer.c:2653 msgid "Docking Station Input" msgstr "Vstup dokovací stanice" -#: spa/plugins/alsa/acp/alsa-mixer.c:2711 +#: spa/plugins/alsa/acp/alsa-mixer.c:2654 msgid "Docking Station Microphone" msgstr "Mikrofon dokovací stanice" -#: spa/plugins/alsa/acp/alsa-mixer.c:2712 +#: spa/plugins/alsa/acp/alsa-mixer.c:2655 msgid "Docking Station Line In" msgstr "Linkový vstup dokovací stanice" -#: 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 "Linkový vstup" -#: 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 "Přední 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 "Zadní mikrofon" -#: spa/plugins/alsa/acp/alsa-mixer.c:2717 +#: spa/plugins/alsa/acp/alsa-mixer.c:2660 msgid "External Microphone" msgstr "Externí 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 "Interní 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ádio" -#: 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 "Obraz" -#: spa/plugins/alsa/acp/alsa-mixer.c:2721 +#: spa/plugins/alsa/acp/alsa-mixer.c:2664 msgid "Automatic Gain Control" msgstr "Automatické řízení zesílení" -#: spa/plugins/alsa/acp/alsa-mixer.c:2722 +#: spa/plugins/alsa/acp/alsa-mixer.c:2665 msgid "No Automatic Gain Control" msgstr "Bez automatického řízení zesílení" -#: spa/plugins/alsa/acp/alsa-mixer.c:2723 +#: spa/plugins/alsa/acp/alsa-mixer.c:2666 msgid "Boost" msgstr "Zdůraznění" -#: spa/plugins/alsa/acp/alsa-mixer.c:2724 +#: spa/plugins/alsa/acp/alsa-mixer.c:2667 msgid "No Boost" msgstr "Bez zdůraznění" -#: spa/plugins/alsa/acp/alsa-mixer.c:2725 +#: spa/plugins/alsa/acp/alsa-mixer.c:2668 msgid "Amplifier" msgstr "Zesilovač" -#: spa/plugins/alsa/acp/alsa-mixer.c:2726 +#: spa/plugins/alsa/acp/alsa-mixer.c:2669 msgid "No Amplifier" msgstr "Bez zesilovače" -#: spa/plugins/alsa/acp/alsa-mixer.c:2727 +#: spa/plugins/alsa/acp/alsa-mixer.c:2670 msgid "Bass Boost" msgstr "Zdůraznění basů" -#: spa/plugins/alsa/acp/alsa-mixer.c:2728 +#: spa/plugins/alsa/acp/alsa-mixer.c:2671 msgid "No Bass Boost" msgstr "Bez zdůraznění basů" -#: 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 "Reproduktor" -#: 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 "Sluchátka" -#: spa/plugins/alsa/acp/alsa-mixer.c:2797 +#: spa/plugins/alsa/acp/alsa-mixer.c:2740 msgid "Analog Input" msgstr "Analogový vstup" -#: spa/plugins/alsa/acp/alsa-mixer.c:2801 +#: spa/plugins/alsa/acp/alsa-mixer.c:2744 msgid "Dock Microphone" msgstr "Dokovací mikrofon" -#: spa/plugins/alsa/acp/alsa-mixer.c:2803 +#: spa/plugins/alsa/acp/alsa-mixer.c:2746 msgid "Headset Microphone" msgstr "Mikrofon náhlavní soupravy" -#: spa/plugins/alsa/acp/alsa-mixer.c:2807 +#: spa/plugins/alsa/acp/alsa-mixer.c:2750 msgid "Analog Output" msgstr "Analogový výstup" -#: spa/plugins/alsa/acp/alsa-mixer.c:2809 +#: spa/plugins/alsa/acp/alsa-mixer.c:2752 msgid "Headphones 2" msgstr "Sluchátka 2" -#: spa/plugins/alsa/acp/alsa-mixer.c:2810 +#: spa/plugins/alsa/acp/alsa-mixer.c:2753 msgid "Headphones Mono Output" msgstr "Sluchátkový výstup mono" -#: spa/plugins/alsa/acp/alsa-mixer.c:2811 +#: spa/plugins/alsa/acp/alsa-mixer.c:2754 msgid "Line Out" msgstr "Linkový výstup" -#: spa/plugins/alsa/acp/alsa-mixer.c:2812 +#: spa/plugins/alsa/acp/alsa-mixer.c:2755 msgid "Analog Mono Output" msgstr "Analogový výstup mono" -#: spa/plugins/alsa/acp/alsa-mixer.c:2813 +#: spa/plugins/alsa/acp/alsa-mixer.c:2756 msgid "Speakers" msgstr "Reproduktory" -#: 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ální výstup (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ální vstup (S/PDIF)" -#: spa/plugins/alsa/acp/alsa-mixer.c:2817 +#: spa/plugins/alsa/acp/alsa-mixer.c:2760 msgid "Multichannel Input" msgstr "Vícekanálový vstup" -#: spa/plugins/alsa/acp/alsa-mixer.c:2818 +#: spa/plugins/alsa/acp/alsa-mixer.c:2761 msgid "Multichannel Output" msgstr "Vícekanálový výstup" -#: spa/plugins/alsa/acp/alsa-mixer.c:2819 +#: spa/plugins/alsa/acp/alsa-mixer.c:2762 msgid "Game Output" msgstr "Herní výstup" -#: spa/plugins/alsa/acp/alsa-mixer.c:2820 -#: spa/plugins/alsa/acp/alsa-mixer.c:2821 +#: spa/plugins/alsa/acp/alsa-mixer.c:2763 +#: spa/plugins/alsa/acp/alsa-mixer.c:2764 msgid "Chat Output" msgstr "Komunikační výstup" -#: spa/plugins/alsa/acp/alsa-mixer.c:2822 +#: spa/plugins/alsa/acp/alsa-mixer.c:2765 msgid "Chat Input" msgstr "Komunikační vstup" -#: spa/plugins/alsa/acp/alsa-mixer.c:2823 +#: spa/plugins/alsa/acp/alsa-mixer.c:2766 msgid "Virtual Surround 7.1" msgstr "Virtuální surround 7.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4527 +#: spa/plugins/alsa/acp/alsa-mixer.c:4471 msgid "Analog Mono" msgstr "Analogové mono" -#: spa/plugins/alsa/acp/alsa-mixer.c:4528 +#: spa/plugins/alsa/acp/alsa-mixer.c:4472 msgid "Analog Mono (Left)" msgstr "Analogové mono (levé)" -#: spa/plugins/alsa/acp/alsa-mixer.c:4529 +#: spa/plugins/alsa/acp/alsa-mixer.c:4473 msgid "Analog Mono (Right)" msgstr "Analogové mono (pravé)" @@ -372,147 +386,147 @@ #. * 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 "Analogové stereo" -#: spa/plugins/alsa/acp/alsa-mixer.c:4531 +#: spa/plugins/alsa/acp/alsa-mixer.c:4475 msgid "Mono" msgstr "Mono" -#: spa/plugins/alsa/acp/alsa-mixer.c:4532 +#: spa/plugins/alsa/acp/alsa-mixer.c:4476 msgid "Stereo" msgstr "Stereo" -#: 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 "Náhlavní souprava" -#: spa/plugins/alsa/acp/alsa-mixer.c:4541 -#: spa/plugins/alsa/acp/alsa-mixer.c:4699 +#: spa/plugins/alsa/acp/alsa-mixer.c:4485 +#: spa/plugins/alsa/acp/alsa-mixer.c:4643 msgid "Speakerphone" msgstr "Hlasitý odposlech" -#: 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 "Více kanálů" -#: spa/plugins/alsa/acp/alsa-mixer.c:4544 +#: spa/plugins/alsa/acp/alsa-mixer.c:4488 msgid "Analog Surround 2.1" msgstr "Analogový Surround 2.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4545 +#: spa/plugins/alsa/acp/alsa-mixer.c:4489 msgid "Analog Surround 3.0" msgstr "Analogový Surround 3.0" -#: spa/plugins/alsa/acp/alsa-mixer.c:4546 +#: spa/plugins/alsa/acp/alsa-mixer.c:4490 msgid "Analog Surround 3.1" msgstr "Analogový Surround 3.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4547 +#: spa/plugins/alsa/acp/alsa-mixer.c:4491 msgid "Analog Surround 4.0" msgstr "Analogový Surround 4.0" -#: spa/plugins/alsa/acp/alsa-mixer.c:4548 +#: spa/plugins/alsa/acp/alsa-mixer.c:4492 msgid "Analog Surround 4.1" msgstr "Analogový Surround 4.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4549 +#: spa/plugins/alsa/acp/alsa-mixer.c:4493 msgid "Analog Surround 5.0" msgstr "Analogový Surround 5.0" -#: spa/plugins/alsa/acp/alsa-mixer.c:4550 +#: spa/plugins/alsa/acp/alsa-mixer.c:4494 msgid "Analog Surround 5.1" msgstr "Analogový Surround 5.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4551 +#: spa/plugins/alsa/acp/alsa-mixer.c:4495 msgid "Analog Surround 6.0" msgstr "Analogový Surround 6.0" -#: spa/plugins/alsa/acp/alsa-mixer.c:4552 +#: spa/plugins/alsa/acp/alsa-mixer.c:4496 msgid "Analog Surround 6.1" msgstr "Analogový Surround 6.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4553 +#: spa/plugins/alsa/acp/alsa-mixer.c:4497 msgid "Analog Surround 7.0" msgstr "Analogový Surround 7.0" -#: spa/plugins/alsa/acp/alsa-mixer.c:4554 +#: spa/plugins/alsa/acp/alsa-mixer.c:4498 msgid "Analog Surround 7.1" msgstr "Analogový Surround 7.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4555 +#: spa/plugins/alsa/acp/alsa-mixer.c:4499 msgid "Digital Stereo (IEC958)" msgstr "Digitální stereo (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ální Surround 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ální Surround 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ální Surround 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ální stereo (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ální Surround 5.1 (HDMI)" -#: spa/plugins/alsa/acp/alsa-mixer.c:4561 +#: spa/plugins/alsa/acp/alsa-mixer.c:4505 msgid "Chat" msgstr "Chat" -#: spa/plugins/alsa/acp/alsa-mixer.c:4562 +#: spa/plugins/alsa/acp/alsa-mixer.c:4506 msgid "Game" msgstr "Hra" -#: spa/plugins/alsa/acp/alsa-mixer.c:4696 +#: spa/plugins/alsa/acp/alsa-mixer.c:4640 msgid "Analog Mono Duplex" msgstr "Analogové duplexní mono" -#: spa/plugins/alsa/acp/alsa-mixer.c:4697 +#: spa/plugins/alsa/acp/alsa-mixer.c:4641 msgid "Analog Stereo Duplex" msgstr "Analogové duplexní stereo" -#: spa/plugins/alsa/acp/alsa-mixer.c:4700 +#: spa/plugins/alsa/acp/alsa-mixer.c:4644 msgid "Digital Stereo Duplex (IEC958)" msgstr "Digitální duplexní stereo (IEC958)" -#: spa/plugins/alsa/acp/alsa-mixer.c:4701 +#: spa/plugins/alsa/acp/alsa-mixer.c:4645 msgid "Multichannel Duplex" msgstr "Vícekanálový duplex" -#: spa/plugins/alsa/acp/alsa-mixer.c:4702 +#: spa/plugins/alsa/acp/alsa-mixer.c:4646 msgid "Stereo Duplex" msgstr "Duplexní stereo" -#: spa/plugins/alsa/acp/alsa-mixer.c:4703 +#: spa/plugins/alsa/acp/alsa-mixer.c:4647 msgid "Mono Chat + 7.1 Surround" msgstr "Mono Chat + 7.1 Surround" -#: spa/plugins/alsa/acp/alsa-mixer.c:4806 +#: spa/plugins/alsa/acp/alsa-mixer.c:4754 #, c-format msgid "%s Output" msgstr "Výstup %s" -#: spa/plugins/alsa/acp/alsa-mixer.c:4813 +#: spa/plugins/alsa/acp/alsa-mixer.c:4761 #, c-format msgid "%s Input" msgstr "Vstup %s" -#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269 +#: 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 " @@ -540,16 +554,16 @@ "S největší pravděpodobností se jedná o chybu v ovladači ALSA „%s“. Nahlaste " "prosím tento problém vývojářům ALSA." -#: spa/plugins/alsa/acp/alsa-util.c:1241 +#: 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" +"snd_pcm_delay() returned a value that is exceptionally large: %li byte " +"(%s%lu ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "to the ALSA developers." msgid_plural "" -"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s" -"%lu ms).\n" +"snd_pcm_delay() returned a value that is exceptionally large: %li bytes " +"(%s%lu ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "to the ALSA developers." msgstr0 "" @@ -568,7 +582,7 @@ "S největší pravděpodobností se jedná o chybu v ovladači ALSA „%s“. Nahlaste " "prosím tento problém vývojářům ALSA." -#: 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 " @@ -581,7 +595,7 @@ "S největší pravděpodobností se jedná o chybu v ovladači ALSA „%s“. Nahlaste " "prosím tento problém vývojářům ALSA." -#: spa/plugins/alsa/acp/alsa-util.c:1331 +#: spa/plugins/alsa/acp/alsa-util.c:1343 #, c-format msgid "" "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte " @@ -609,61 +623,109 @@ "S největší pravděpodobností se jedná o chybu v ovladači ALSA „%s“. Nahlaste " "prosím tento problém vývojářům ALSA." -#: spa/plugins/bluez5/bluez5-device.c:1010 +#: spa/plugins/alsa/acp/channelmap.h:457 +msgid "(invalid)" +msgstr "(neplatné)" + +#: spa/plugins/alsa/acp/compat.c:189 +msgid "Built-in Audio" +msgstr "Vnitřní zvukový systém" + +#: 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 "Zvuková brána (A2DP Source & 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 "High Fidelity Playback (A2DP Sink, kodek %s)" -#: 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 "High Fidelity Duplex (A2DP Source/Sink, kodek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1041 +#: spa/plugins/bluez5/bluez5-device.c:1283 msgid "High Fidelity Playback (A2DP Sink)" msgstr "High Fidelity Playback (A2DP Sink)" -#: spa/plugins/bluez5/bluez5-device.c:1043 +#: spa/plugins/bluez5/bluez5-device.c:1285 msgid "High Fidelity Duplex (A2DP Source/Sink)" msgstr "High Fidelity Duplex (A2DP Source/Sink)" -#: spa/plugins/bluez5/bluez5-device.c:1070 +#: spa/plugins/bluez5/bluez5-device.c:1322 +#, c-format +#| msgid "High Fidelity Playback (A2DP Sink, codec %s)" +msgid "High Fidelity Playback (BAP Sink, codec %s)" +msgstr "High Fidelity Playback (BAP Sink, kodek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1326 +#, c-format +#| msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" +msgid "High Fidelity Input (BAP Source, codec %s)" +msgstr "High Fidelity Input (BAP Source, kodek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1330 +#, c-format +#| msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" +msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)" +msgstr "High Fidelity Duplex (BAP Source/Sink, kodek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1359 #, c-format msgid "Headset Head Unit (HSP/HFP, codec %s)" msgstr "Jednotka náhlavní soupravy (HSP/HFP, kodek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1074 +#: spa/plugins/bluez5/bluez5-device.c:1364 msgid "Headset Head Unit (HSP/HFP)" msgstr "Jednotka náhlavní soupravy (HSP/HFP)" -#: spa/plugins/bluez5/bluez5-device.c:1140 +#: 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 "Handsfree" -#: spa/plugins/bluez5/bluez5-device.c:1155 +#: spa/plugins/bluez5/bluez5-device.c:1449 +#| msgid "Handsfree" +msgid "Handsfree (HFP)" +msgstr "Handsfree (HFP)" + +#: spa/plugins/bluez5/bluez5-device.c:1466 msgid "Headphone" msgstr "Sluchátko" -#: spa/plugins/bluez5/bluez5-device.c:1160 +#: spa/plugins/bluez5/bluez5-device.c:1472 msgid "Portable" msgstr "Přenosné zařízení" -#: spa/plugins/bluez5/bluez5-device.c:1165 +#: spa/plugins/bluez5/bluez5-device.c:1478 msgid "Car" msgstr "Auto" -#: spa/plugins/bluez5/bluez5-device.c:1170 +#: spa/plugins/bluez5/bluez5-device.c:1484 msgid "HiFi" msgstr "HiFi" -#: 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 +#: spa/plugins/bluez5/bluez5-device.c:1497 msgid "Bluetooth" msgstr "Bluetooth" + +#: spa/plugins/bluez5/bluez5-device.c:1498 +#| msgid "Bluetooth" +msgid "Bluetooth (HFP)" +msgstr "Bluetooth (HFP)"
View file
pipewire-0.3.59.tar.gz/po/hr.po -> pipewire-0.3.60.tar.gz/po/hr.po
Changed
@@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: pipewire\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-06-30 12:50+0200\n" -"PO-Revision-Date: 2022-06-30 13:14+0200\n" +"POT-Creation-Date: 2022-10-01 14:01+0200\n" +"PO-Revision-Date: 2022-10-01 14:12+0200\n" "Last-Translator: gogo <trebelnik2@gmail.com>\n" "Language-Team: Croatian <https://translate.fedoraproject.org/projects/" "pipewire/pipewire/hr/>\n" @@ -16,9 +16,9 @@ "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Poedit 2.3\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Poedit 3.0.1\n" "X-Launchpad-Export-Date: 2017-04-20 21:04+0000\n" #: src/daemon/pipewire.c:46 @@ -44,7 +44,7 @@ msgid "Dummy Output" msgstr "Lažni izlaz" -#: src/modules/module-pulse-tunnel.c:648 +#: src/modules/module-pulse-tunnel.c:662 #, c-format msgid "Tunnel for %s@%s" msgstr "Tunel za %s@%s" @@ -164,7 +164,7 @@ " -d, --dsd DSD način\n" "\n" -#: src/tools/pw-cli.c:3165 +#: src/tools/pw-cli.c:2250 #, c-format msgid "" "%s options command\n" @@ -187,8 +187,8 @@ msgid "Pro Audio" msgstr "Pro Audio" -#: spa/plugins/alsa/acp/acp.c:446 spa/plugins/alsa/acp/alsa-mixer.c:4648 -#: spa/plugins/bluez5/bluez5-device.c:1161 +#: 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 "Isključeno" @@ -215,7 +215,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:2657 #: spa/plugins/alsa/acp/alsa-mixer.c:2741 -#: spa/plugins/bluez5/bluez5-device.c:1330 +#: spa/plugins/bluez5/bluez5-device.c:1454 msgid "Microphone" msgstr "Mikrofon" @@ -281,7 +281,7 @@ msgstr "Bez pojačanja basa" #: spa/plugins/alsa/acp/alsa-mixer.c:2672 -#: spa/plugins/bluez5/bluez5-device.c:1335 +#: spa/plugins/bluez5/bluez5-device.c:1460 msgid "Speaker" msgstr "Zvučnik" @@ -396,7 +396,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:4484 #: spa/plugins/alsa/acp/alsa-mixer.c:4642 -#: spa/plugins/bluez5/bluez5-device.c:1320 +#: spa/plugins/bluez5/bluez5-device.c:1442 msgid "Headset" msgstr "Slušalice s mikrofonom" @@ -520,7 +520,8 @@ msgid "%s Input" msgstr "%s ulaz" -#: spa/plugins/alsa/acp/alsa-util.c:1173 spa/plugins/alsa/acp/alsa-util.c:1267 +#: 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 " @@ -548,16 +549,16 @@ "Najvjerojatnije je ovo greška ALSA upravljačkog programa '%s'. Prijavite " "problem ALSA razvijateljima." -#: spa/plugins/alsa/acp/alsa-util.c:1239 +#: 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" +"snd_pcm_delay() returned a value that is exceptionally large: %li byte " +"(%s%lu ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "to the ALSA developers." msgid_plural "" -"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s" -"%lu ms).\n" +"snd_pcm_delay() returned a value that is exceptionally large: %li bytes " +"(%s%lu ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "to the ALSA developers." msgstr0 "" @@ -566,17 +567,17 @@ "Najvjerojatnije je ovo greška ALSA upravljačkog programa '%s'. Prijavite " "problem ALSA razvijateljima." msgstr1 "" -"snd_pcm_delay() je vratio vrijednost koja je iznimno velika: %li bajta (%s" -"%lu ms).\n" +"snd_pcm_delay() je vratio vrijednost koja je iznimno velika: %li bajta " +"(%s%lu ms).\n" "Najvjerojatnije je ovo greška ALSA upravljačkog programa '%s'. Prijavite " "problem ALSA razvijateljima." msgstr2 "" -"snd_pcm_delay() je vratio vrijednost koja je iznimno velika: %li bajta (%s" -"%lu ms).\n" +"snd_pcm_delay() je vratio vrijednost koja je iznimno velika: %li bajta " +"(%s%lu ms).\n" "Najvjerojatnije je ovo greška ALSA upravljačkog programa '%s'. Prijavite " "problem ALSA razvijateljima." -#: spa/plugins/alsa/acp/alsa-util.c:1286 +#: spa/plugins/alsa/acp/alsa-util.c:1300 #, c-format msgid "" "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail " @@ -589,7 +590,7 @@ "Najvjerojatnije je ovo greška ALSA upravljačkog programa '%s'. Prijavite " "problem ALSA razvijateljima." -#: spa/plugins/alsa/acp/alsa-util.c:1329 +#: spa/plugins/alsa/acp/alsa-util.c:1343 #, c-format msgid "" "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte " @@ -629,65 +630,96 @@ msgid "Modem" msgstr "Modem" -#: spa/plugins/bluez5/bluez5-device.c:1172 +#: spa/plugins/bluez5/bluez5-device.c:1247 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" msgstr "Zvučni pristupnik (A2DP izvor i HSP/HFP AG)" -#: spa/plugins/bluez5/bluez5-device.c:1197 +#: spa/plugins/bluez5/bluez5-device.c:1272 #, c-format msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgstr "Reprodukcija visoke autentičnosti (A2DP slivnik, kôdek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1200 +#: spa/plugins/bluez5/bluez5-device.c:1275 #, c-format msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgstr "Telefonija visoke autentičnosti (A2DP slivnik, kôdek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1208 +#: spa/plugins/bluez5/bluez5-device.c:1283 msgid "High Fidelity Playback (A2DP Sink)" msgstr "Reprodukcija visoke autentičnosti (A2DP slivnik)" -#: spa/plugins/bluez5/bluez5-device.c:1210 +#: spa/plugins/bluez5/bluez5-device.c:1285 msgid "High Fidelity Duplex (A2DP Source/Sink)" msgstr "Telefonija visoke autentičnosti (A2DP izvor/slivnik)" -#: spa/plugins/bluez5/bluez5-device.c:1238 +#: spa/plugins/bluez5/bluez5-device.c:1322 +#, c-format +msgid "High Fidelity Playback (BAP Sink, codec %s)" +msgstr "Reprodukcija visoke autentičnosti (BAP slivnik, kôdek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1326 +#, c-format +msgid "High Fidelity Input (BAP Source, codec %s)" +msgstr "Ulaz visoke autentičnosti (BAP izvor, kôdek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1330 +#, c-format +msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)" +msgstr "Telefonija visoke autentičnosti (BAP izvor/slivnik, kôdek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1359 #, c-format msgid "Headset Head Unit (HSP/HFP, codec %s)" msgstr "Jedinica slušalice s mikrofonom (HSP/HFP, kôdek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1243 +#: spa/plugins/bluez5/bluez5-device.c:1364 msgid "Headset Head Unit (HSP/HFP)" msgstr "Jedinica slušalice s mikrofonom (HSP/HFP)" -#: spa/plugins/bluez5/bluez5-device.c:1325 +#: 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 "Bez-ruku" -#: spa/plugins/bluez5/bluez5-device.c:1340 +#: spa/plugins/bluez5/bluez5-device.c:1449 +msgid "Handsfree (HFP)" +msgstr "Bez-ruku (HFP)" + +#: spa/plugins/bluez5/bluez5-device.c:1466 msgid "Headphone" -msgstr "Slušalice" +msgstr "Slušalica" -#: spa/plugins/bluez5/bluez5-device.c:1345 +#: spa/plugins/bluez5/bluez5-device.c:1472 msgid "Portable" msgstr "Prijenosnik" -#: spa/plugins/bluez5/bluez5-device.c:1350 +#: spa/plugins/bluez5/bluez5-device.c:1478 msgid "Car" msgstr "Automobil" -#: spa/plugins/bluez5/bluez5-device.c:1355 +#: spa/plugins/bluez5/bluez5-device.c:1484 msgid "HiFi" msgstr "HiFi" -#: spa/plugins/bluez5/bluez5-device.c:1360 +#: spa/plugins/bluez5/bluez5-device.c:1490 msgid "Phone" msgstr "Telefon" -#: spa/plugins/bluez5/bluez5-device.c:1366 +#: spa/plugins/bluez5/bluez5-device.c:1497 msgid "Bluetooth" msgstr "Bluetooth" +#: spa/plugins/bluez5/bluez5-device.c:1498 +msgid "Bluetooth (HFP)" +msgstr "Bluetooth (HFP)" + #~ msgid "PipeWire Media System" #~ msgstr "PipeWire medijski sustav"
View file
pipewire-0.3.59.tar.gz/po/pipewire.pot -> pipewire-0.3.60.tar.gz/po/pipewire.pot
Changed
@@ -37,7 +37,7 @@ msgid "Dummy Output" msgstr "" -#: src/modules/module-pulse-tunnel.c:648 +#: src/modules/module-pulse-tunnel.c:662 #, c-format msgid "Tunnel for %s@%s" msgstr "" @@ -113,7 +113,7 @@ "\n" msgstr "" -#: src/tools/pw-cli.c:3165 +#: src/tools/pw-cli.c:2250 #, c-format msgid "" "%s options command\n" @@ -128,8 +128,8 @@ msgid "Pro Audio" msgstr "" -#: spa/plugins/alsa/acp/acp.c:446 spa/plugins/alsa/acp/alsa-mixer.c:4648 -#: spa/plugins/bluez5/bluez5-device.c:1161 +#: 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 "" @@ -156,7 +156,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:2657 #: spa/plugins/alsa/acp/alsa-mixer.c:2741 -#: spa/plugins/bluez5/bluez5-device.c:1330 +#: spa/plugins/bluez5/bluez5-device.c:1454 msgid "Microphone" msgstr "" @@ -222,7 +222,7 @@ msgstr "" #: spa/plugins/alsa/acp/alsa-mixer.c:2672 -#: spa/plugins/bluez5/bluez5-device.c:1335 +#: spa/plugins/bluez5/bluez5-device.c:1460 msgid "Speaker" msgstr "" @@ -337,7 +337,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:4484 #: spa/plugins/alsa/acp/alsa-mixer.c:4642 -#: spa/plugins/bluez5/bluez5-device.c:1320 +#: spa/plugins/bluez5/bluez5-device.c:1442 msgid "Headset" msgstr "" @@ -461,8 +461,8 @@ msgid "%s Input" msgstr "" -#: spa/plugins/alsa/acp/alsa-util.c:1173 -#: spa/plugins/alsa/acp/alsa-util.c:1267 +#: 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 " @@ -477,22 +477,22 @@ msgstr0 "" msgstr1 "" -#: spa/plugins/alsa/acp/alsa-util.c:1239 +#: 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" +"snd_pcm_delay() returned a value that is exceptionally large: %li byte " +"(%s%lu ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "to the ALSA developers." msgid_plural "" -"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s" -"%lu ms).\n" +"snd_pcm_delay() returned a value that is exceptionally large: %li bytes " +"(%s%lu ms).\n" "Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "to the ALSA developers." msgstr0 "" msgstr1 "" -#: spa/plugins/alsa/acp/alsa-util.c:1286 +#: spa/plugins/alsa/acp/alsa-util.c:1300 #, c-format msgid "" "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail " @@ -501,7 +501,7 @@ "to the ALSA developers." msgstr "" -#: spa/plugins/alsa/acp/alsa-util.c:1329 +#: spa/plugins/alsa/acp/alsa-util.c:1343 #, c-format msgid "" "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte " @@ -528,61 +528,92 @@ msgid "Modem" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1172 +#: spa/plugins/bluez5/bluez5-device.c:1247 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1197 +#: spa/plugins/bluez5/bluez5-device.c:1272 #, c-format msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1200 +#: spa/plugins/bluez5/bluez5-device.c:1275 #, c-format msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1208 +#: spa/plugins/bluez5/bluez5-device.c:1283 msgid "High Fidelity Playback (A2DP Sink)" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1210 +#: spa/plugins/bluez5/bluez5-device.c:1285 msgid "High Fidelity Duplex (A2DP Source/Sink)" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1238 +#: spa/plugins/bluez5/bluez5-device.c:1322 +#, c-format +msgid "High Fidelity Playback (BAP Sink, codec %s)" +msgstr "" + +#: spa/plugins/bluez5/bluez5-device.c:1326 +#, c-format +msgid "High Fidelity Input (BAP Source, codec %s)" +msgstr "" + +#: spa/plugins/bluez5/bluez5-device.c:1330 +#, c-format +msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)" +msgstr "" + +#: spa/plugins/bluez5/bluez5-device.c:1359 #, c-format msgid "Headset Head Unit (HSP/HFP, codec %s)" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1243 +#: spa/plugins/bluez5/bluez5-device.c:1364 msgid "Headset Head Unit (HSP/HFP)" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1325 +#: 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 "" -#: spa/plugins/bluez5/bluez5-device.c:1340 +#: spa/plugins/bluez5/bluez5-device.c:1449 +msgid "Handsfree (HFP)" +msgstr "" + +#: spa/plugins/bluez5/bluez5-device.c:1466 msgid "Headphone" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1345 +#: spa/plugins/bluez5/bluez5-device.c:1472 msgid "Portable" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1350 +#: spa/plugins/bluez5/bluez5-device.c:1478 msgid "Car" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1355 +#: spa/plugins/bluez5/bluez5-device.c:1484 msgid "HiFi" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1360 +#: spa/plugins/bluez5/bluez5-device.c:1490 msgid "Phone" msgstr "" -#: spa/plugins/bluez5/bluez5-device.c:1366 +#: spa/plugins/bluez5/bluez5-device.c:1497 msgid "Bluetooth" msgstr "" + +#: spa/plugins/bluez5/bluez5-device.c:1498 +msgid "Bluetooth (HFP)" +msgstr ""
View file
pipewire-0.3.59.tar.gz/po/pt_BR.po -> pipewire-0.3.60.tar.gz/po/pt_BR.po
Changed
@@ -1,18 +1,19 @@ # Brazilian Portuguese translation for pipewire -# Copyright (C) 2021 Rafael Fontenelle <rafaelff@gnome.org> +# Copyright (C) 2022 Rafael Fontenelle <rafaelff@gnome.org> # This file is distributed under the same license as the pipewire package. # Fabian Affolter <fab@fedoraproject.org>, 2008. # Igor Pires Soares <igor@projetofedora.org>, 2009, 2012. # Rafael Fontenelle <rafaelff@gnome.org>, 2013-2021. +# Matheus Barbosa <mdpb.matheus@gmail.com>, 2022. # msgid "" msgstr "" "Project-Id-Version: pipewire\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/" "issues\n" -"POT-Creation-Date: 2021-08-01 15:31+0000\n" -"PO-Revision-Date: 2021-08-01 17:02-0300\n" -"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n" +"POT-Creation-Date: 2022-09-30 03:27+0000\n" +"PO-Revision-Date: 2022-01-25 19:49-0300\n" +"Last-Translator: Matheus Barbosa <mdpb.matheus@gmail.com>\n" "Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" @@ -21,7 +22,7 @@ "Plural-Forms: nplurals=2; plural=(n > 1)\n" "X-Generator: Gtranslator 40.0\n" -#: src/daemon/pipewire.c:45 +#: src/daemon/pipewire.c:46 #, c-format msgid "" "%s options\n" @@ -43,58 +44,51 @@ msgid "Start the PipeWire Media System" msgstr "Inicia o Sistema de Mídia PipeWire" -#: src/examples/media-session/alsa-monitor.c:586 -#: spa/plugins/alsa/acp/compat.c:189 -msgid "Built-in Audio" -msgstr "Áudio interno" - -#: src/examples/media-session/alsa-monitor.c:590 -#: spa/plugins/alsa/acp/compat.c:194 -msgid "Modem" -msgstr "Modem" - -#: src/examples/media-session/alsa-monitor.c:599 -#: src/modules/module-zeroconf-discover.c:296 -msgid "Unknown device" -msgstr "Dispositivo desconhecido" - -#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:173 -#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:173 +#: 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 "Túnel para %s/%s" -#: src/modules/module-pulse-tunnel.c:534 +#: src/modules/module-fallback-sink.c:51 +msgid "Dummy Output" +msgstr "Saída de falsa" + +#: src/modules/module-pulse-tunnel.c:662 #, c-format msgid "Tunnel for %s@%s" msgstr "Túnel para %s@%s" -#: src/modules/module-zeroconf-discover.c:308 +#: src/modules/module-zeroconf-discover.c:332 +msgid "Unknown device" +msgstr "Dispositivo desconhecido" + +#: src/modules/module-zeroconf-discover.c:344 #, c-format msgid "%s on %s@%s" msgstr "%s em %s@%s" -#: src/modules/module-zeroconf-discover.c:312 +#: src/modules/module-zeroconf-discover.c:348 #, c-format msgid "%s on %s" msgstr "%s em %s" -#: src/tools/pw-cat.c:1000 +#: 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 opções <arquivo>\n" +"%s opções <arquivo>|-\n" " -h, --help Mostra esta ajuda\n" " --version Mostra a versão\n" " -v, --verbose Habilita operações verbosas\n" "\n" -#: src/tools/pw-cat.c:1007 +#: src/tools/pw-cat.c:791 #, c-format msgid "" " -R, --remote Remote daemon name\n" @@ -108,7 +102,7 @@ " 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 Nome do daemon remoto\n" @@ -124,11 +118,10 @@ " Xunit (unidade = s, ms, us, ns)\n" " ou amostras diretas (256)\n" " a taxa é um dos arquivos fontes\n" -" --list-targets Lista alvos disponíveis para --" -"target\n" +" --properties Define as propriedades do nó\n" "\n" -#: src/tools/pw-cat.c:1025 +#: src/tools/pw-cat.c:809 #, c-format msgid "" " --rate Sample rate (req. for rec) (default " @@ -165,19 +158,21 @@ "(padrão: %d)\n" "\n" -#: src/tools/pw-cat.c:1042 +#: 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 Modo de reprodução\n" " -r, --record Modo de gravação\n" -" -m, --midi Modo midi\n" +" -m, --midi Modo Midi\n" +" -d, --dsd Modo DSD\n" "\n" -#: src/tools/pw-cli.c:2954 +#: src/tools/pw-cli.c:2255 #, c-format msgid "" "%s options command\n" @@ -194,12 +189,12 @@ " -r, --remote Nome do daemon remoto\n" "\n" -#: spa/plugins/alsa/acp/acp.c:306 +#: spa/plugins/alsa/acp/acp.c:321 msgid "Pro Audio" msgstr "Pro Audio" -#: spa/plugins/alsa/acp/acp.c:429 spa/plugins/alsa/acp/alsa-mixer.c:4648 -#: spa/plugins/bluez5/bluez5-device.c:1043 +#: 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 "Desligado" @@ -226,7 +221,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:2657 #: spa/plugins/alsa/acp/alsa-mixer.c:2741 -#: spa/plugins/bluez5/bluez5-device.c:1198 +#: spa/plugins/bluez5/bluez5-device.c:1454 msgid "Microphone" msgstr "Microfone" @@ -298,7 +293,7 @@ msgstr "Sem reforço de graves" #: spa/plugins/alsa/acp/alsa-mixer.c:2672 -#: spa/plugins/bluez5/bluez5-device.c:1203 +#: spa/plugins/bluez5/bluez5-device.c:1460 msgid "Speaker" msgstr "Auto-falante" @@ -414,7 +409,7 @@ # Fone de ouvido não se encaixa como tradução aqui, pois há ou pode haver microfone junto. #: spa/plugins/alsa/acp/alsa-mixer.c:4484 #: spa/plugins/alsa/acp/alsa-mixer.c:4642 -#: spa/plugins/bluez5/bluez5-device.c:1188 +#: spa/plugins/bluez5/bluez5-device.c:1442 msgid "Headset" msgstr "Headset" @@ -528,17 +523,17 @@ msgid "Mono Chat + 7.1 Surround" msgstr "Bate-papo monofônico + surround 7.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4750 +#: spa/plugins/alsa/acp/alsa-mixer.c:4754 #, c-format msgid "%s Output" msgstr "Saída %s" -#: spa/plugins/alsa/acp/alsa-mixer.c:4757 +#: spa/plugins/alsa/acp/alsa-mixer.c:4761 #, c-format msgid "%s Input" msgstr "Entrada %s" -#: spa/plugins/alsa/acp/alsa-util.c:1173 spa/plugins/alsa/acp/alsa-util.c:1267 +#: 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 " @@ -561,7 +556,7 @@ "É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, " "relate esse problema aos desenvolvedores do ALSA." -#: spa/plugins/alsa/acp/alsa-util.c:1239 +#: spa/plugins/alsa/acp/alsa-util.c:1253 #, c-format msgid "" "snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s" @@ -584,7 +579,7 @@ "É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, " "relate esse problema aos desenvolvedores do ALSA." -#: spa/plugins/alsa/acp/alsa-util.c:1286 +#: spa/plugins/alsa/acp/alsa-util.c:1300 #, c-format msgid "" "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail " @@ -597,7 +592,7 @@ "É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, " "relate esse problema aos desenvolvedores do ALSA." -#: spa/plugins/alsa/acp/alsa-util.c:1329 +#: spa/plugins/alsa/acp/alsa-util.c:1343 #, c-format msgid "" "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte " @@ -620,66 +615,106 @@ "É mais provável que isso seja um erro no driver “%s” do ALSA. Por favor, " "relate esse problema aos desenvolvedores do ALSA." -#: spa/plugins/alsa/acp/channelmap.h:466 +#: spa/plugins/alsa/acp/channelmap.h:457 msgid "(invalid)" msgstr "(inválido)" -#: spa/plugins/bluez5/bluez5-device.c:1053 +#: spa/plugins/alsa/acp/compat.c:189 +msgid "Built-in Audio" +msgstr "Áudio interno" + +#: 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 "Gateway de áudio (fonte A2DP & HSP/HFP AG)" -#: spa/plugins/bluez5/bluez5-device.c:1076 +#: spa/plugins/bluez5/bluez5-device.c:1272 #, c-format msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgstr "Reprodução de alta-fidelidade (destino A2DP, codec %s)" -#: spa/plugins/bluez5/bluez5-device.c:1078 +#: spa/plugins/bluez5/bluez5-device.c:1275 #, c-format msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgstr "Duplex de alta-fidelidade (fonte/destino A2DP, codec %s)" -#: spa/plugins/bluez5/bluez5-device.c:1084 +#: spa/plugins/bluez5/bluez5-device.c:1283 msgid "High Fidelity Playback (A2DP Sink)" msgstr "Reprodução de alta-fidelidade (destino A2DP)" -#: spa/plugins/bluez5/bluez5-device.c:1086 +#: spa/plugins/bluez5/bluez5-device.c:1285 msgid "High Fidelity Duplex (A2DP Source/Sink)" msgstr "Duplex de alta-fidelidade (fonte/destino A2DP)" -#: spa/plugins/bluez5/bluez5-device.c:1113 +#: spa/plugins/bluez5/bluez5-device.c:1322 +#, c-format +msgid "High Fidelity Playback (BAP Sink, codec %s)" +msgstr "Reprodução de alta-fidelidade (destino BAP, codec %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1326 +#, c-format +msgid "High Fidelity Input (BAP Source, codec %s)" +msgstr "Entrada de alta-fidelidade (fonte BAP, codec %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1330 +#, c-format +msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)" +msgstr "Duplex de alta-fidelidade (fonte/destino BAP, codec %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1359 #, c-format msgid "Headset Head Unit (HSP/HFP, codec %s)" msgstr "Unidade de headset (HSP/HFP, codec %s)" -#: spa/plugins/bluez5/bluez5-device.c:1117 +#: spa/plugins/bluez5/bluez5-device.c:1364 msgid "Headset Head Unit (HSP/HFP)" msgstr "Unidade de headset (HSP/HFP)" -# Desconheço tradução comum para esta palavra. -#: spa/plugins/bluez5/bluez5-device.c:1193 +# Supostamente relacionado a HFP, hands-free profile, mas não encontrei tradução comum +#: 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 "Handsfree" -#: spa/plugins/bluez5/bluez5-device.c:1208 +# Supostamente relacionado a HFP, hands-free profile, mas não encontrei tradução comum +#: spa/plugins/bluez5/bluez5-device.c:1449 +msgid "Handsfree (HFP)" +msgstr "Handsfree (HFP)" + +#: spa/plugins/bluez5/bluez5-device.c:1466 msgid "Headphone" msgstr "Fones de ouvido" -#: spa/plugins/bluez5/bluez5-device.c:1213 +#: spa/plugins/bluez5/bluez5-device.c:1472 msgid "Portable" msgstr "Portátil" -#: spa/plugins/bluez5/bluez5-device.c:1218 +#: spa/plugins/bluez5/bluez5-device.c:1478 msgid "Car" msgstr "Carro" -#: spa/plugins/bluez5/bluez5-device.c:1223 +#: spa/plugins/bluez5/bluez5-device.c:1484 msgid "HiFi" msgstr "HiFi" -#: spa/plugins/bluez5/bluez5-device.c:1228 +#: spa/plugins/bluez5/bluez5-device.c:1490 msgid "Phone" msgstr "Telefone" -#: spa/plugins/bluez5/bluez5-device.c:1234 +#: spa/plugins/bluez5/bluez5-device.c:1497 msgid "Bluetooth" msgstr "Bluetooth" + +#: spa/plugins/bluez5/bluez5-device.c:1498 +msgid "Bluetooth (HFP)" +msgstr "Bluetooth (HFP)"
View file
pipewire-0.3.59.tar.gz/po/sv.po -> pipewire-0.3.60.tar.gz/po/sv.po
Changed
@@ -19,8 +19,8 @@ "Project-Id-Version: pipewire\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/" "issues\n" -"POT-Creation-Date: 2022-07-19 15:27+0000\n" -"PO-Revision-Date: 2022-07-10 10:22+0200\n" +"POT-Creation-Date: 2022-10-20 15:27+0000\n" +"PO-Revision-Date: 2022-09-16 12:58+0200\n" "Last-Translator: Anders Jonsson <anders.jonsson@norsjovallen.se>\n" "Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n" "Language: sv\n" @@ -28,7 +28,7 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Poedit 3.1\n" +"X-Generator: Poedit 3.1.1\n" #: src/daemon/pipewire.c:46 #, c-format @@ -61,26 +61,26 @@ msgid "Dummy Output" msgstr "Attrapputgång" -#: src/modules/module-pulse-tunnel.c:648 +#: src/modules/module-pulse-tunnel.c:681 #, c-format msgid "Tunnel for %s@%s" msgstr "Tunnel för %s@%s" -#: src/modules/module-zeroconf-discover.c:332 +#: src/modules/module-zeroconf-discover.c:335 msgid "Unknown device" msgstr "Okänd enhet" -#: src/modules/module-zeroconf-discover.c:344 +#: src/modules/module-zeroconf-discover.c:347 #, c-format msgid "%s on %s@%s" msgstr "%s på %s@%s" -#: src/modules/module-zeroconf-discover.c:348 +#: src/modules/module-zeroconf-discover.c:351 #, c-format msgid "%s on %s" msgstr "%s på %s" -#: src/tools/pw-cat.c:784 +#: src/tools/pw-cat.c:782 #, c-format msgid "" "%s options <file>|-\n" @@ -95,7 +95,7 @@ " -v, --verbose Aktivera utförliga operationer\n" "\n" -#: src/tools/pw-cat.c:791 +#: src/tools/pw-cat.c:789 #, c-format msgid "" " -R, --remote Remote daemon name\n" @@ -125,7 +125,7 @@ " -P --properties Sätt nodegenskaper\n" "\n" -#: src/tools/pw-cat.c:809 +#: src/tools/pw-cat.c:807 #, c-format msgid "" " --rate Sample rate (req. for rec) (default " @@ -160,7 +160,7 @@ "%d)\n" "\n" -#: src/tools/pw-cat.c:826 +#: src/tools/pw-cat.c:824 msgid "" " -p, --playback Playback mode\n" " -r, --record Recording mode\n" @@ -174,7 +174,7 @@ " -d, --dsd DSD-läge\n" "\n" -#: src/tools/pw-cli.c:3165 +#: src/tools/pw-cli.c:2250 #, c-format msgid "" "%s options command\n" @@ -195,8 +195,8 @@ msgid "Pro Audio" msgstr "Professionellt ljud" -#: spa/plugins/alsa/acp/acp.c:446 spa/plugins/alsa/acp/alsa-mixer.c:4648 -#: spa/plugins/bluez5/bluez5-device.c:1188 +#: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648 +#: spa/plugins/bluez5/bluez5-device.c:1237 msgid "Off" msgstr "Av" @@ -223,7 +223,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:1455 msgid "Microphone" msgstr "Mikrofon" @@ -289,7 +289,7 @@ msgstr "Ingen basökning" #: spa/plugins/alsa/acp/alsa-mixer.c:2672 -#: spa/plugins/bluez5/bluez5-device.c:1366 +#: spa/plugins/bluez5/bluez5-device.c:1461 msgid "Speaker" msgstr "Högtalare" @@ -404,7 +404,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:1443 msgid "Headset" msgstr "Headset" @@ -528,7 +528,7 @@ msgid "%s Input" msgstr "%s-ingång" -#: spa/plugins/alsa/acp/alsa-util.c:1173 spa/plugins/alsa/acp/alsa-util.c:1267 +#: 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 " @@ -551,7 +551,7 @@ "Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera " "problemet till ALSA-utvecklarna." -#: spa/plugins/alsa/acp/alsa-util.c:1239 +#: spa/plugins/alsa/acp/alsa-util.c:1253 #, c-format msgid "" "snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s" @@ -574,7 +574,7 @@ "Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera " "problemet till ALSA-utvecklarna." -#: spa/plugins/alsa/acp/alsa-util.c:1286 +#: spa/plugins/alsa/acp/alsa-util.c:1300 #, c-format msgid "" "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail " @@ -587,7 +587,7 @@ "Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera " "problemet till ALSA-utvecklarna." -#: spa/plugins/alsa/acp/alsa-util.c:1329 +#: spa/plugins/alsa/acp/alsa-util.c:1343 #, c-format msgid "" "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte " @@ -622,77 +622,92 @@ msgid "Modem" msgstr "Modem" -#: spa/plugins/bluez5/bluez5-device.c:1199 +#: spa/plugins/bluez5/bluez5-device.c:1248 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" msgstr "Audio gateway (A2DP-källa & HSP/HFP AG)" -#: spa/plugins/bluez5/bluez5-device.c:1224 +#: spa/plugins/bluez5/bluez5-device.c:1273 #, c-format msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgstr "High fidelity-uppspelning (A2DP-utgång, kodek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1227 +#: spa/plugins/bluez5/bluez5-device.c:1276 #, c-format msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgstr "High fidelity duplex (A2DP-källa/utgång, kodek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1235 +#: spa/plugins/bluez5/bluez5-device.c:1284 msgid "High Fidelity Playback (A2DP Sink)" msgstr "High fidelity-uppspelning (A2DP-utgång)" -#: spa/plugins/bluez5/bluez5-device.c:1237 +#: spa/plugins/bluez5/bluez5-device.c:1286 msgid "High Fidelity Duplex (A2DP Source/Sink)" msgstr "High fidelity duplex (A2DP-källa/utgång)" -#: spa/plugins/bluez5/bluez5-device.c:1265 +#: spa/plugins/bluez5/bluez5-device.c:1323 +#, c-format +msgid "High Fidelity Playback (BAP Sink, codec %s)" +msgstr "High fidelity-uppspelning (BAP-utgång, kodek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1327 +#, c-format +msgid "High Fidelity Input (BAP Source, codec %s)" +msgstr "High fidelity-ingång (BAP-källa, kodek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1331 +#, c-format +msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)" +msgstr "High fidelity duplex (BAP-källa/utgång, kodek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1360 #, c-format msgid "Headset Head Unit (HSP/HFP, codec %s)" msgstr "Headset-huvudenhet (HSP/HFP, kodek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1270 +#: spa/plugins/bluez5/bluez5-device.c:1365 msgid "Headset Head Unit (HSP/HFP)" msgstr "Headset-huvudenhet (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:1444 +#: spa/plugins/bluez5/bluez5-device.c:1449 +#: spa/plugins/bluez5/bluez5-device.c:1456 +#: spa/plugins/bluez5/bluez5-device.c:1462 +#: spa/plugins/bluez5/bluez5-device.c:1468 +#: spa/plugins/bluez5/bluez5-device.c:1474 +#: spa/plugins/bluez5/bluez5-device.c:1480 +#: spa/plugins/bluez5/bluez5-device.c:1486 +#: spa/plugins/bluez5/bluez5-device.c:1492 msgid "Handsfree" msgstr "Handsfree" -#: spa/plugins/bluez5/bluez5-device.c:1355 +#: spa/plugins/bluez5/bluez5-device.c:1450 msgid "Handsfree (HFP)" msgstr "Handsfree (HFP)" -#: spa/plugins/bluez5/bluez5-device.c:1372 +#: spa/plugins/bluez5/bluez5-device.c:1467 msgid "Headphone" msgstr "Hörlurar" -#: spa/plugins/bluez5/bluez5-device.c:1378 +#: spa/plugins/bluez5/bluez5-device.c:1473 msgid "Portable" msgstr "Bärbar" -#: spa/plugins/bluez5/bluez5-device.c:1384 +#: spa/plugins/bluez5/bluez5-device.c:1479 msgid "Car" msgstr "Bil" -#: spa/plugins/bluez5/bluez5-device.c:1390 +#: spa/plugins/bluez5/bluez5-device.c:1485 msgid "HiFi" msgstr "HiFi" -#: spa/plugins/bluez5/bluez5-device.c:1396 +#: spa/plugins/bluez5/bluez5-device.c:1491 msgid "Phone" msgstr "Telefon" -#: spa/plugins/bluez5/bluez5-device.c:1403 +#: spa/plugins/bluez5/bluez5-device.c:1498 msgid "Bluetooth" msgstr "Bluetooth" -#: spa/plugins/bluez5/bluez5-device.c:1404 +#: spa/plugins/bluez5/bluez5-device.c:1499 msgid "Bluetooth (HFP)" msgstr "Bluetooth (HFP)"
View file
pipewire-0.3.59.tar.gz/po/tr.po -> pipewire-0.3.60.tar.gz/po/tr.po
Changed
@@ -11,8 +11,8 @@ "Project-Id-Version: PipeWire master\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/" "issues/new\n" -"POT-Creation-Date: 2022-04-03 12:56+0200\n" -"PO-Revision-Date: 2022-05-14 18:35+0300\n" +"POT-Creation-Date: 2022-06-30 12:50+0200\n" +"PO-Revision-Date: 2022-10-23 10:40+0300\n" "Last-Translator: Oğuz Ersen <oguz@ersen.moe>\n" "Language-Team: Turkish <tr>\n" "Language: tr\n" @@ -35,8 +35,8 @@ " --version Sürümü göster\n" " -c, --config Yapılandırmayı yükle (Öntanımlı %s)\n" -#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:190 -#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:190 +#: 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 "%s/%s tüneli" @@ -45,41 +45,41 @@ msgid "Dummy Output" msgstr "Temsili Çıkış" -#: src/modules/module-pulse-tunnel.c:545 +#: src/modules/module-pulse-tunnel.c:662 #, c-format msgid "Tunnel for %s@%s" msgstr "%s@%s için tünel" -#: src/modules/module-zeroconf-discover.c:313 +#: src/modules/module-zeroconf-discover.c:332 msgid "Unknown device" msgstr "Bilinmeyen aygıt" -#: src/modules/module-zeroconf-discover.c:325 +#: src/modules/module-zeroconf-discover.c:344 #, c-format msgid "%s on %s@%s" msgstr "%s, %s@%s" -#: src/modules/module-zeroconf-discover.c:329 +#: src/modules/module-zeroconf-discover.c:348 #, c-format msgid "%s on %s" msgstr "%s, %s" -#: src/tools/pw-cat.c:1087 +#: 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 seçenekler <dosya>\n" +"%s seçenekler <dosya>|-\n" " -h, --help Bu yardımı göster\n" " --version Sürümü göster\n" " -v, --verbose Ayrıntılı işlemleri etkinleştir\n" "\n" -#: src/tools/pw-cat.c:1094 +#: src/tools/pw-cat.c:791 #, c-format msgid "" " -R, --remote Remote daemon name\n" @@ -93,7 +93,7 @@ " 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 Uzak arka plan programı adı\n" @@ -109,11 +109,10 @@ " Xbirim (birim = s, ms, us, ns)\n" " veya doğrudan örneklemeler (256)\n" " oran kaynak dosyadan biridir\n" -" --list-targets --target için kullanılabilir " -"hedefleri listele\n" +" -P --properties Düğüm özelliklerini ayarla\n" "\n" -#: src/tools/pw-cat.c:1112 +#: src/tools/pw-cat.c:809 #, c-format msgid "" " --rate Sample rate (req. for rec) (default " @@ -149,7 +148,7 @@ "15) (öntanımlı %d)\n" "\n" -#: src/tools/pw-cat.c:1129 +#: src/tools/pw-cat.c:826 msgid "" " -p, --playback Playback mode\n" " -r, --record Recording mode\n" @@ -163,7 +162,7 @@ " -d, --dsd DSD modu\n" "\n" -#: src/tools/pw-cli.c:3051 +#: src/tools/pw-cli.c:2250 #, c-format msgid "" "%s options command\n" @@ -186,7 +185,7 @@ msgstr "Profesyonel Ses" #: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648 -#: spa/plugins/bluez5/bluez5-device.c:1159 +#: spa/plugins/bluez5/bluez5-device.c:1236 msgid "Off" msgstr "Kapalı" @@ -213,7 +212,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:2657 #: spa/plugins/alsa/acp/alsa-mixer.c:2741 -#: spa/plugins/bluez5/bluez5-device.c:1328 +#: spa/plugins/bluez5/bluez5-device.c:1454 msgid "Microphone" msgstr "Mikrofon" @@ -279,7 +278,7 @@ msgstr "Bas Artırma Yok" #: spa/plugins/alsa/acp/alsa-mixer.c:2672 -#: spa/plugins/bluez5/bluez5-device.c:1333 +#: spa/plugins/bluez5/bluez5-device.c:1460 msgid "Speaker" msgstr "Hoparlör" @@ -394,7 +393,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:4484 #: spa/plugins/alsa/acp/alsa-mixer.c:4642 -#: spa/plugins/bluez5/bluez5-device.c:1318 +#: spa/plugins/bluez5/bluez5-device.c:1442 msgid "Headset" msgstr "Kulaklık" @@ -518,7 +517,7 @@ msgid "%s Input" msgstr "%s Girişi" -#: spa/plugins/alsa/acp/alsa-util.c:1173 spa/plugins/alsa/acp/alsa-util.c:1267 +#: 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 " @@ -535,7 +534,7 @@ "Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA " "geliştiricilerine bildirin." -#: spa/plugins/alsa/acp/alsa-util.c:1239 +#: spa/plugins/alsa/acp/alsa-util.c:1253 #, c-format msgid "" "snd_pcm_delay() returned a value that is exceptionally large: %li byte " @@ -552,7 +551,7 @@ "Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA " "geliştiricilerine bildirin." -#: spa/plugins/alsa/acp/alsa-util.c:1286 +#: spa/plugins/alsa/acp/alsa-util.c:1300 #, c-format msgid "" "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail " @@ -565,7 +564,7 @@ "Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA " "geliştiricilerine bildirin." -#: spa/plugins/alsa/acp/alsa-util.c:1329 +#: spa/plugins/alsa/acp/alsa-util.c:1343 #, c-format msgid "" "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte " @@ -583,7 +582,7 @@ "Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA " "geliştiricilerine bildirin." -#: spa/plugins/alsa/acp/channelmap.h:464 +#: spa/plugins/alsa/acp/channelmap.h:457 msgid "(invalid)" msgstr "(geçersiz)" @@ -595,65 +594,96 @@ msgid "Modem" msgstr "Modem" -#: spa/plugins/bluez5/bluez5-device.c:1170 +#: spa/plugins/bluez5/bluez5-device.c:1247 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" msgstr "Ses Geçidi (A2DP Kaynak & HSP/HFP AG)" -#: spa/plugins/bluez5/bluez5-device.c:1195 +#: spa/plugins/bluez5/bluez5-device.c:1272 #, c-format msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgstr "Yüksek Kaliteli Çalma (A2DP Alıcı, çözücü %s)" -#: spa/plugins/bluez5/bluez5-device.c:1198 +#: spa/plugins/bluez5/bluez5-device.c:1275 #, c-format msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgstr "Yüksek Kaliteli İkili (A2DP Kaynak/Alıcı, çözücü %s)" -#: spa/plugins/bluez5/bluez5-device.c:1206 +#: spa/plugins/bluez5/bluez5-device.c:1283 msgid "High Fidelity Playback (A2DP Sink)" msgstr "Yüksek Kaliteli Çalma (A2DP Alıcı)" -#: spa/plugins/bluez5/bluez5-device.c:1208 +#: spa/plugins/bluez5/bluez5-device.c:1285 msgid "High Fidelity Duplex (A2DP Source/Sink)" msgstr "Yüksek Kaliteli İkili (A2DP Kaynak/Alıcı)" -#: spa/plugins/bluez5/bluez5-device.c:1236 +#: spa/plugins/bluez5/bluez5-device.c:1322 +#, c-format +msgid "High Fidelity Playback (BAP Sink, codec %s)" +msgstr "Yüksek Kaliteli Çalma (BAP Alıcı, çözücü %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1326 +#, c-format +msgid "High Fidelity Input (BAP Source, codec %s)" +msgstr "Yüksek Kaliteli Giriş (BAP Kaynak, çözücü %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1330 +#, c-format +msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)" +msgstr "Yüksek Kaliteli İkili (BAP Kaynak/Alıcı, çözücü %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1359 #, c-format msgid "Headset Head Unit (HSP/HFP, codec %s)" msgstr "Kulaklık Ana Birimi (HSP/HFP, çözücü %s)" -#: spa/plugins/bluez5/bluez5-device.c:1241 +#: spa/plugins/bluez5/bluez5-device.c:1364 msgid "Headset Head Unit (HSP/HFP)" msgstr "Kulaklık Ana Birimi (HSP/HFP)" -#: spa/plugins/bluez5/bluez5-device.c:1323 +#: 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 "Ahizesiz" -#: spa/plugins/bluez5/bluez5-device.c:1338 +#: spa/plugins/bluez5/bluez5-device.c:1449 +msgid "Handsfree (HFP)" +msgstr "Ahizesiz (HFP)" + +#: spa/plugins/bluez5/bluez5-device.c:1466 msgid "Headphone" msgstr "Kulaklık" -#: spa/plugins/bluez5/bluez5-device.c:1343 +#: spa/plugins/bluez5/bluez5-device.c:1472 msgid "Portable" msgstr "Taşınabilir" -#: spa/plugins/bluez5/bluez5-device.c:1348 +#: spa/plugins/bluez5/bluez5-device.c:1478 msgid "Car" msgstr "Araba" -#: spa/plugins/bluez5/bluez5-device.c:1353 +#: spa/plugins/bluez5/bluez5-device.c:1484 msgid "HiFi" msgstr "Yüksek Kalite" -#: spa/plugins/bluez5/bluez5-device.c:1358 +#: spa/plugins/bluez5/bluez5-device.c:1490 msgid "Phone" msgstr "Telefon" -#: spa/plugins/bluez5/bluez5-device.c:1364 +#: spa/plugins/bluez5/bluez5-device.c:1497 msgid "Bluetooth" msgstr "Bluetooth" +#: spa/plugins/bluez5/bluez5-device.c:1498 +msgid "Bluetooth (HFP)" +msgstr "Bluetooth (HFP)" + #~ msgid "PipeWire Media System" #~ msgstr "PipeWire Ortam Sistemi"
View file
pipewire-0.3.59.tar.gz/pw-uninstalled.sh -> pipewire-0.3.60.tar.gz/pw-uninstalled.sh
Changed
@@ -41,7 +41,7 @@ export SPA_DATA_DIR="${SCRIPT_DIR}/spa/plugins" # the directory with pipewire modules export PIPEWIRE_MODULE_DIR="${BUILDDIR}/src/modules" -export PATH="${BUILDDIR}/src/daemon:${BUILDDIR}/src/tools:${BUILDDIR}/src/media-session:${BUILDDIR}/src/examples:${PATH}" +export PATH="${BUILDDIR}/src/daemon:${BUILDDIR}/src/tools:${BUILDDIR}/src/media-session:${BUILDDIR}/src/examples:${BUILDDIR}/pipewire-v4l2/src:${PATH}" export LD_LIBRARY_PATH="${BUILDDIR}/src/pipewire/:${BUILDDIR}/pipewire-jack/src/${LD_LIBRARY_PATH+":$LD_LIBRARY_PATH"}" export GST_PLUGIN_PATH="${BUILDDIR}/src/gst/${GST_PLUGIN_PATH+":${GST_PLUGIN_PATH}"}" # the directory with card profiles and paths @@ -50,6 +50,7 @@ # ALSA plugin directory export ALSA_PLUGIN_DIR="${BUILDDIR}/pipewire-alsa/alsa-plugins" +export PW_BUILDDIR=$BUILDDIR export PW_UNINSTALLED=1 export PKG_CONFIG_PATH="${BUILDDIR}/meson-uninstalled/:${PKG_CONFIG_PATH}"
View file
pipewire-0.3.59.tar.gz/spa/include/spa/interfaces/audio/aec.h -> pipewire-0.3.60.tar.gz/spa/include/spa/interfaces/audio/aec.h
Changed
@@ -23,6 +23,7 @@ */ +#include <spa/pod/builder.h> #include <spa/utils/dict.h> #include <spa/utils/hook.h> #include <spa/param/audio/raw.h> @@ -60,7 +61,7 @@ }; struct spa_audio_aec_methods { -#define SPA_VERSION_AUDIO_AEC_METHODS 1 +#define SPA_VERSION_AUDIO_AEC_METHODS 2 uint32_t version; int (*add_listener) (void *object, @@ -75,6 +76,11 @@ int (*activate) (void *object); /* since 0.3.58, version 1:1 */ int (*deactivate) (void *object); + + /* version 1:2 */ + int (*enum_props) (void* object, int index, struct spa_pod_builder* builder); + int (*get_params) (void* object, struct spa_pod_builder* builder); + int (*set_params) (void *object, const struct spa_pod *args); }; #define spa_audio_aec_method(o,method,version,...) \ @@ -93,6 +99,9 @@ #define spa_audio_aec_set_props(o,...) spa_audio_aec_method(o, set_props, 0, __VA_ARGS__) #define spa_audio_aec_activate(o) spa_audio_aec_method(o, activate, 1) #define spa_audio_aec_deactivate(o) spa_audio_aec_method(o, deactivate, 1) +#define spa_audio_aec_enum_props(o,...) spa_audio_aec_method(o, enum_props, 2, __VA_ARGS__) +#define spa_audio_aec_get_params(o,...) spa_audio_aec_method(o, get_params, 2, __VA_ARGS__) +#define spa_audio_aec_set_params(o,...) spa_audio_aec_method(o, set_params, 2, __VA_ARGS__) #ifdef __cplusplus } /* extern "C" */
View file
pipewire-0.3.59.tar.gz/spa/include/spa/param/audio/format-utils.h -> pipewire-0.3.60.tar.gz/spa/include/spa/param/audio/format-utils.h
Changed
@@ -47,9 +47,9 @@ info->flags = 0; res = spa_pod_parse_object(format, SPA_TYPE_OBJECT_Format, NULL, - SPA_FORMAT_AUDIO_format, SPA_POD_Id(&info->format), - SPA_FORMAT_AUDIO_rate, SPA_POD_Int(&info->rate), - SPA_FORMAT_AUDIO_channels, SPA_POD_Int(&info->channels), + SPA_FORMAT_AUDIO_format, SPA_POD_OPT_Id(&info->format), + SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate), + SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); if (position == NULL || !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS)) @@ -64,7 +64,7 @@ int res; res = spa_pod_parse_object(format, SPA_TYPE_OBJECT_Format, NULL, - SPA_FORMAT_AUDIO_format, SPA_POD_Id(&info->format)); + SPA_FORMAT_AUDIO_format, SPA_POD_OPT_Id(&info->format)); return res; } @@ -74,8 +74,8 @@ int res; res = spa_pod_parse_object(format, SPA_TYPE_OBJECT_Format, NULL, - SPA_FORMAT_AUDIO_iec958Codec, SPA_POD_Id(&info->codec), - SPA_FORMAT_AUDIO_rate, SPA_POD_Int(&info->rate)); + SPA_FORMAT_AUDIO_iec958Codec, SPA_POD_OPT_Id(&info->codec), + SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate)); return res; } @@ -87,10 +87,10 @@ info->flags = 0; res = spa_pod_parse_object(format, SPA_TYPE_OBJECT_Format, NULL, - SPA_FORMAT_AUDIO_bitorder, SPA_POD_Id(&info->bitorder), - SPA_FORMAT_AUDIO_interleave, SPA_POD_Int(&info->interleave), - SPA_FORMAT_AUDIO_rate, SPA_POD_Int(&info->rate), - SPA_FORMAT_AUDIO_channels, SPA_POD_Int(&info->channels), + SPA_FORMAT_AUDIO_bitorder, SPA_POD_OPT_Id(&info->bitorder), + SPA_FORMAT_AUDIO_interleave, SPA_POD_OPT_Int(&info->interleave), + SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate), + SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); if (position == NULL || !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS))
View file
pipewire-0.3.59.tar.gz/spa/include/spa/param/param.h -> pipewire-0.3.60.tar.gz/spa/include/spa/param/param.h
Changed
@@ -72,7 +72,9 @@ uint32_t flags; uint32_t user; /**< private user field. You can use this to keep * state. */ - uint32_t padding5; + int32_t seq; /**< private seq field. You can use this to keep + * state of a pending update. */ + uint32_t padding4; }; #define SPA_PARAM_INFO(id,flags) ((struct spa_param_info){ (id), (flags) })
View file
pipewire-0.3.59.tar.gz/spa/include/spa/param/video/format-utils.h -> pipewire-0.3.60.tar.gz/spa/include/spa/param/video/format-utils.h
Changed
@@ -44,10 +44,10 @@ { return spa_pod_parse_object(format, SPA_TYPE_OBJECT_Format, NULL, - SPA_FORMAT_VIDEO_format, SPA_POD_Id(&info->format), + SPA_FORMAT_VIDEO_format, SPA_POD_OPT_Id(&info->format), SPA_FORMAT_VIDEO_modifier, SPA_POD_OPT_Long(&info->modifier), - SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&info->size), - SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate), + SPA_FORMAT_VIDEO_size, SPA_POD_OPT_Rectangle(&info->size), + SPA_FORMAT_VIDEO_framerate, SPA_POD_OPT_Fraction(&info->framerate), SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_OPT_Fraction(&info->max_framerate), SPA_FORMAT_VIDEO_views, SPA_POD_OPT_Int(&info->views), SPA_FORMAT_VIDEO_interlaceMode, SPA_POD_OPT_Id(&info->interlace_mode), @@ -67,7 +67,7 @@ { return spa_pod_parse_object(format, SPA_TYPE_OBJECT_Format, NULL, - SPA_FORMAT_VIDEO_format, SPA_POD_Id(&info->format), + SPA_FORMAT_VIDEO_format, SPA_POD_OPT_Id(&info->format), SPA_FORMAT_VIDEO_modifier, SPA_POD_OPT_Long(&info->modifier)); }
View file
pipewire-0.3.59.tar.gz/spa/include/spa/pod/event.h -> pipewire-0.3.60.tar.gz/spa/include/spa/pod/event.h
Changed
@@ -49,9 +49,9 @@ #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) \ - { { size, SPA_TYPE_OBJECT }, \ - { { type, id }, ##__VA_ARGS__ } } \ +#define SPA_EVENT_INIT_FULL(t,size,type,id,...) ((t) \ + { { (size), SPA_TYPE_OBJECT }, \ + { { (type), (id) }, ##__VA_ARGS__ } }) \ #define SPA_EVENT_INIT(type,id) \ SPA_EVENT_INIT_FULL(struct spa_event, \
View file
pipewire-0.3.59.tar.gz/spa/include/spa/utils/defs.h -> pipewire-0.3.60.tar.gz/spa/include/spa/utils/defs.h
Changed
@@ -82,7 +82,8 @@ #endif #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_IS_SET(field,flag) SPA_FLAG_MASK(field, flag, flag) + #define SPA_FLAG_SET(field,flag) ((field) |= (flag)) #define SPA_FLAG_CLEAR(field, flag) \ ({ \ @@ -140,21 +141,24 @@ #define SPA_FOR_EACH_ELEMENT(arr, ptr) \ for ((ptr) = arr; (void*)(ptr) < SPA_PTROFF(arr, sizeof(arr), void); (ptr)++) +#define SPA_FOR_EACH_ELEMENT_VAR(arr, var) \ + for (__typeof__((arr)0)* (var) = arr; (void*)(var) < SPA_PTROFF(arr, sizeof(arr), void); (var)++) + #define SPA_ABS(a) \ ({ \ __typeof__(a) _a = (a); \ SPA_LIKELY(_a >= 0) ? _a : -_a; \ }) -#define SPA_MIN(a,b) \ -({ \ - __typeof__(a) _min_a = (a); \ - __typeof__(b) _min_b = (b); \ +#define SPA_MIN(a,b) \ +({ \ + __typeof__(a) _min_a = (a); \ + __typeof__(b) _min_b = (b); \ SPA_LIKELY(_min_a <= _min_b) ? _min_a : _min_b; \ }) -#define SPA_MAX(a,b) \ -({ \ - __typeof__(a) _max_a = (a); \ - __typeof__(b) _max_b = (b); \ +#define SPA_MAX(a,b) \ +({ \ + __typeof__(a) _max_a = (a); \ + __typeof__(b) _max_b = (b); \ SPA_LIKELY(_max_a >= _max_b) ? _max_a : _max_b; \ }) #define SPA_CLAMP(v,low,high) \ @@ -174,7 +178,7 @@ #define SPA_SWAP(a,b) \ ({ \ __typeof__(a) _t = (a); \ - (a) = b; (b) = _t; \ + (a) = b; (b) = _t; \ }) #define SPA_TYPECHECK(type,x) \ @@ -254,21 +258,21 @@ #define SPA_RESTRICT #endif -#define SPA_ROUND_DOWN(num,value) ((num) - ((num) % (value))) -#define SPA_ROUND_UP(num,value) ((((num) + (value) - 1) / (value)) * (value)) - -#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(num,value) \ +({ \ + __typeof__(num) _num = (num); \ + ((_num) - ((_num) % (value))); \ }) +#define SPA_ROUND_UP(num,value) \ +({ \ + __typeof__(value) _v = (value); \ + ((((num) + (_v) - 1) / (_v)) * (_v)); \ +}) + +#define SPA_ROUND_MASK(num,mask) ((__typeof__(num))((mask)-1)) -#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_ROUND_DOWN_N(num,align) ((num) & ~SPA_ROUND_MASK(num, align)) +#define SPA_ROUND_UP_N(num,align) ((((num)-1) | SPA_ROUND_MASK(num, align))+1) #define SPA_PTR_ALIGNMENT(p,align) ((intptr_t)(p) & ((align)-1)) #define SPA_IS_ALIGNED(p,align) (SPA_PTR_ALIGNMENT(p,align) == 0)
View file
pipewire-0.3.59.tar.gz/spa/include/spa/utils/hook.h -> pipewire-0.3.60.tar.gz/spa/include/spa/utils/hook.h
Changed
@@ -166,7 +166,7 @@ * */ #define SPA_INTERFACE_INIT(_type,_version,_funcs,_data) \ - (struct spa_interface){ _type, _version, SPA_CALLBACKS_INIT(_funcs,_data), } + ((struct spa_interface){ (_type), (_version), SPA_CALLBACKS_INIT(_funcs,_data), }) /** * Invoke method named \a method in the \a callbacks.
View file
pipewire-0.3.59.tar.gz/spa/meson.build -> pipewire-0.3.60.tar.gz/spa/meson.build
Changed
@@ -66,6 +66,10 @@ 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') + if get_option('bluez5-backend-hsp-native').allowed() or get_option('bluez5-backend-hfp-native').allowed() + mm_dep = dependency('ModemManager', version : '>= 1.10.0', required : get_option('bluez5-backend-native-mm')) + summary({'ModemManager': mm_dep.found()}, bool_yn: true, section: 'Bluetooth backends') + endif 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.59.tar.gz/spa/plugins/alsa/acp/acp.c -> pipewire-0.3.60.tar.gz/spa/plugins/alsa/acp/acp.c
Changed
@@ -624,7 +624,7 @@ pa_card *impl = snd_mixer_elem_get_callback_private(melem); snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem); snd_ctl_elem_value_t *elem_value; - bool plugged_in; + bool plugged_in, any_input_port_available; void *state; pa_alsa_jack *jack; struct temp_port_avail *tp, *tports; @@ -735,6 +735,31 @@ if (impl->card.active_profile_index != ACP_INVALID_INDEX) active_available = impl->card.profilesimpl->card.active_profile_index->available; + /* First round - detect, if we have any input port available. + If the hardware can report the state for all I/O jacks, only speakers + may be plugged in. */ + any_input_port_available = false; + PA_HASHMAP_FOREACH(profile, impl->profiles, state) { + pa_device_port *port; + void *state2; + + if (profile->profile.flags & ACP_PROFILE_OFF) + continue; + + PA_HASHMAP_FOREACH(port, impl->ports, state2) { + if (!pa_hashmap_get(port->profiles, profile->profile.name)) + continue; + + if (port->port.direction == ACP_DIRECTION_CAPTURE && + port->port.available != ACP_AVAILABLE_NO) { + any_input_port_available = true; + goto input_port_found; + } + } + } +input_port_found: + + /* Second round */ PA_HASHMAP_FOREACH(profile, impl->profiles, state) { pa_device_port *port; void *state2; @@ -768,7 +793,7 @@ if (has_input_port && !has_output_port && found_available_input_port) available = ACP_AVAILABLE_YES; - if (has_output_port && !has_input_port && found_available_output_port) + if (has_output_port && (!has_input_port || !any_input_port_available) && found_available_output_port) available = ACP_AVAILABLE_YES; if (has_output_port && has_input_port && found_available_output_port && found_available_input_port) available = ACP_AVAILABLE_YES;
View file
pipewire-0.3.59.tar.gz/spa/plugins/alsa/alsa-acp-device.c -> pipewire-0.3.60.tar.gz/spa/plugins/alsa/alsa-acp-device.c
Changed
@@ -216,7 +216,7 @@ { int err = 0; struct spa_dict_item *items; - uint32_t i, n_items; + uint32_t n_items; const struct acp_dict_item *it; struct acp_card *card = this->card; char path128; @@ -241,10 +241,10 @@ #undef ADD_ITEM if (this->info.change_mask & SPA_DEVICE_CHANGE_MASK_PARAMS) { - for (i = 0; i < SPA_N_ELEMENTS(this->params); i++) { - if (this->paramsi.user > 0) { - this->paramsi.flags ^= SPA_PARAM_INFO_SERIAL; - this->paramsi.user = 0; + SPA_FOR_EACH_ELEMENT_VAR(this->params, p) { + if (p->user > 0) { + p->flags ^= SPA_PARAM_INFO_SERIAL; + p->user = 0; } } }
View file
pipewire-0.3.59.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.60.tar.gz/spa/plugins/alsa/alsa-pcm.c
Changed
@@ -638,12 +638,10 @@ static snd_pcm_format_t spa_format_to_alsa(uint32_t format, bool *planar) { - size_t i; - - for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) { - *planar = format_infoi.spa_pformat == format; - if (format_infoi.spa_format == format || *planar) - return format_infoi.format; + SPA_FOR_EACH_ELEMENT_VAR(format_info, i) { + *planar = i->spa_pformat == format; + if (i->spa_format == format || *planar) + return i->format; } return SND_PCM_FORMAT_UNKNOWN; } @@ -969,7 +967,7 @@ struct spa_pod **result, struct spa_pod_builder *b) { int res, err; - size_t i, j; + size_t j; snd_pcm_t *hndl; snd_pcm_hw_params_t *params; struct spa_pod_frame f2; @@ -1020,8 +1018,10 @@ spa_pod_builder_push_choice(b, &f1, SPA_CHOICE_None, 0); choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f1); - for (i = 1, j = 0; i < SPA_N_ELEMENTS(format_info); i++) { - const struct format_info *fi = &format_infoi; + j = 0; + SPA_FOR_EACH_ELEMENT_VAR(format_info, fi) { + if (fi->format == SND_PCM_FORMAT_UNKNOWN) + continue; if (snd_pcm_format_mask_test(fmask, fi->format)) { if ((snd_pcm_access_mask_test(amask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) || @@ -1775,7 +1775,7 @@ state->alsa_started = false; if (state->stream == SND_PCM_STREAM_PLAYBACK) - spa_alsa_silence(state, state->start_delay + state->threshold * 2 + state->headroom); + spa_alsa_silence(state, state->start_delay + state->threshold + state->headroom); return do_start(state); } @@ -2355,7 +2355,7 @@ { int res; - if (SPA_UNLIKELY(delay > target + state->max_error)) { + if (state->alsa_started && SPA_UNLIKELY(delay > target + state->max_error)) { spa_log_trace(state->log, "%p: early wakeup %lu %lu", state, delay, target); if (delay > target * 3) delay = target * 3; @@ -2558,10 +2558,10 @@ state->alsa_recovering = false; state->alsa_started = false; + /* start capture now, playback will start after first write */ if (state->stream == SND_PCM_STREAM_PLAYBACK) - spa_alsa_silence(state, state->start_delay + state->threshold * 2 + state->headroom); - - if ((err = do_start(state)) < 0) + spa_alsa_silence(state, state->start_delay + state->threshold + state->headroom); + else if ((err = do_start(state)) < 0) return err; set_timers(state);
View file
pipewire-0.3.59.tar.gz/spa/plugins/alsa/alsa-seq-bridge.c -> pipewire-0.3.60.tar.gz/spa/plugins/alsa/alsa-seq-bridge.c
Changed
@@ -566,7 +566,7 @@ if (result.index > 0) return 0; param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application), SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control)); break;
View file
pipewire-0.3.59.tar.gz/spa/plugins/alsa/alsa-seq.c -> pipewire-0.3.60.tar.gz/spa/plugins/alsa/alsa-seq.c
Changed
@@ -586,11 +586,11 @@ continue; if (prepare_buffer(state, port) >= 0) { - port->buffer->buf->datas0.chunk->offset = 0; - port->buffer->buf->datas0.chunk->size = port->builder.state.offset, - spa_pod_builder_pop(&port->builder, &port->frame); + port->buffer->buf->datas0.chunk->offset = 0; + port->buffer->buf->datas0.chunk->size = port->builder.state.offset; + /* move buffer to ready queue */ spa_list_remove(&port->buffer->link); SPA_FLAG_SET(port->buffer->flags, BUFFER_FLAG_OUT); @@ -702,23 +702,28 @@ return res; } -static int update_time(struct seq_state *state, uint64_t nsec, bool follower) +static void update_position(struct seq_state *state) { - snd_seq_queue_status_t *status; - const snd_seq_real_time_t* queue_time; - uint64_t queue_real; - double err, corr; - uint64_t queue_elapsed; - if (state->position) { struct spa_io_clock *clock = &state->position->clock; state->rate = clock->rate; + if (state->rate.num == 0 || state->rate.denom == 0) + state->rate = SPA_FRACTION(1, 48000); state->duration = clock->duration; } else { state->rate = SPA_FRACTION(1, 48000); state->duration = 1024; } state->threshold = state->duration; +} + +static int update_time(struct seq_state *state, uint64_t nsec, bool follower) +{ + snd_seq_queue_status_t *status; + const snd_seq_real_time_t* queue_time; + uint64_t queue_real; + double err, corr; + uint64_t queue_elapsed; corr = 1.0 - (state->dll.z2 + state->dll.z3); @@ -776,6 +781,8 @@ { int res; + update_position(state); + res = process_recycle(state); if (state->following && state->position) { @@ -800,11 +807,13 @@ spa_log_trace(state->log, "timeout %"PRIu64, state->current_time); + update_position(state); + update_time(state, state->current_time, false); res = process_read(state); - if (res > 0) - spa_node_call_ready(&state->callbacks, res); + if (res >= 0) + spa_node_call_ready(&state->callbacks, res | SPA_STATUS_NEED_DATA); set_timeout(state, state->next_time); } @@ -878,15 +887,7 @@ while (snd_seq_drain_output(state->event.hndl) > 0) sleep(1); - if (state->position) { - struct spa_io_clock *clock = &state->position->clock; - state->rate = clock->rate; - state->duration = clock->duration; - } else { - state->rate = SPA_FRACTION(1, 48000); - state->duration = 1024; - } - state->threshold = state->duration; + update_position(state); state->started = true;
View file
pipewire-0.3.59.tar.gz/spa/plugins/alsa/alsa-udev.c -> pipewire-0.3.60.tar.gz/spa/plugins/alsa/alsa-udev.c
Changed
@@ -465,7 +465,7 @@ if (str && *str) { itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_PATH, str); } - if ((str = udev_device_get_syspath(dev)) && *str) { + if ((str = udev_device_get_devpath(dev)) && *str) { itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SYSFS_PATH, str); } if ((str = udev_device_get_property_value(dev, "ID_ID")) && *str) { @@ -651,9 +651,9 @@ { bool deleted = false; struct impl *this = source->data; - struct { + union { struct inotify_event e; - char nameNAME_MAX+1; + char nameNAME_MAX+1+sizeof(struct inotify_event); } buf; while (true) { @@ -670,17 +670,20 @@ e = SPA_PTROFF(&buf, len, void); for (p = &buf; p < e; - p = SPA_PTROFF(p, sizeof(struct inotify_event) + event->len, void)) { + p = SPA_PTROFF(p, sizeof(struct inotify_event) + event->len, void)) { unsigned int id; struct device *device; event = (const struct inotify_event *) p; + spa_assert_se(SPA_PTRDIFF(e, p) >= (ptrdiff_t)sizeof(struct inotify_event) && + SPA_PTRDIFF(e, p) - sizeof(struct inotify_event) >= event->len && + "bad event from kernel"); /* Device becomes accessible or not busy */ if ((event->mask & (IN_ATTRIB | IN_CLOSE_WRITE))) { bool access; if (sscanf(event->name, "controlC%u", &id) != 1 && - sscanf(event->name, "pcmC%uD", &id) != 1) + sscanf(event->name, "pcmC%uD", &id) != 1) continue; if ((device = find_device(this, id)) == NULL) continue;
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/audioadapter.c
Changed
@@ -556,30 +556,6 @@ return 0; } -static int format_audio_raw_parse_opt(const struct spa_pod *format, struct spa_audio_info_raw *info) -{ - struct spa_pod *position = NULL; - uint32_t media_type, media_subtype; - int res; - if ((res = spa_format_parse(format, &media_type, &media_subtype)) < 0) - return res; - if (media_type != SPA_MEDIA_TYPE_audio || - media_subtype != SPA_MEDIA_SUBTYPE_raw) - return -ENOTSUP; - - spa_zero(*info); - res = spa_pod_parse_object(format, - SPA_TYPE_OBJECT_Format, NULL, - SPA_FORMAT_AUDIO_format, SPA_POD_OPT_Id(&info->format), - SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), - SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); - if (position == NULL || - !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS)) - SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); - - return res; -} - static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param) { @@ -627,8 +603,18 @@ if (format) { struct spa_audio_info info; - if (format_audio_raw_parse_opt(format, &info.info.raw) >= 0) + + spa_zero(info); + if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) + return res; + if (info.media_type != SPA_MEDIA_TYPE_audio || + info.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return -ENOTSUP; + + if (spa_format_audio_raw_parse(format, &info.info.raw) >= 0) { + info.info.raw.rate = 0; this->default_format = info; + } } switch (mode) { @@ -814,21 +800,23 @@ switch (SPA_NODE_COMMAND_ID(command)) { case SPA_NODE_COMMAND_Start: + spa_log_debug(this->log, "%p: starting %d", this, this->started); if (this->started) return 0; if ((res = negotiate_format(this)) < 0) return res; if ((res = negotiate_buffers(this)) < 0) return res; + this->started = true; break; case SPA_NODE_COMMAND_Suspend: - configure_format(this, 0, NULL); - SPA_FALLTHROUGH + spa_log_debug(this->log, "%p: suspending", this); + break; case SPA_NODE_COMMAND_Pause: - this->started = false; - spa_log_debug(this->log, "%p: stopped", this); + spa_log_debug(this->log, "%p: pausing", this); break; case SPA_NODE_COMMAND_Flush: + spa_log_debug(this->log, "%p: flushing", this); this->io_buffers.status = SPA_STATUS_OK; break; default: @@ -852,9 +840,18 @@ } switch (SPA_NODE_COMMAND_ID(command)) { case SPA_NODE_COMMAND_Start: - this->started = true; spa_log_debug(this->log, "%p: started", this); break; + case SPA_NODE_COMMAND_Suspend: + configure_format(this, 0, NULL); + SPA_FALLTHROUGH + case SPA_NODE_COMMAND_Pause: + this->started = false; + spa_log_debug(this->log, "%p: stopped", this); + break; + case SPA_NODE_COMMAND_Flush: + spa_log_debug(this->log, "%p: flushed", this); + break; } return res; } @@ -1155,6 +1152,11 @@ spa_log_trace_fp(this->log, "%p: ready %d", this, status); + if (!this->started) { + spa_log_warn(this->log, "%p: ready stopped node", this); + return -EIO; + } + if (this->target != this->follower) { this->driver = true;
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/audioconvert.c
Changed
@@ -220,6 +220,7 @@ unsigned int resample_peaks:1; unsigned int is_passthrough:1; unsigned int drained:1; + unsigned int rate_adjust:1; uint32_t empty_size; float *empty; @@ -241,16 +242,15 @@ static void emit_node_info(struct impl *this, bool full) { uint64_t old = full ? this->info.change_mask : 0; - uint32_t i; if (full) this->info.change_mask = this->info_all; if (this->info.change_mask) { if (this->info.change_mask & SPA_NODE_CHANGE_MASK_PARAMS) { - for (i = 0; i < SPA_N_ELEMENTS(this->params); i++) { - if (this->paramsi.user > 0) { - this->paramsi.flags ^= SPA_PARAM_INFO_SERIAL; - this->paramsi.user = 0; + SPA_FOR_EACH_ELEMENT_VAR(this->params, p) { + if (p->user > 0) { + p->flags ^= SPA_PARAM_INFO_SERIAL; + p->user = 0; } } } @@ -262,7 +262,6 @@ static void emit_port_info(struct impl *this, struct port *port, bool full) { uint64_t old = full ? port->info.change_mask : 0; - uint32_t i; if (full) port->info.change_mask = port->info_all; @@ -282,10 +281,10 @@ port->info.props = &SPA_DICT_INIT(items, n_items); if (port->info.change_mask & SPA_PORT_CHANGE_MASK_PARAMS) { - for (i = 0; i < SPA_N_ELEMENTS(port->params); i++) { - if (port->paramsi.user > 0) { - port->paramsi.flags ^= SPA_PARAM_INFO_SERIAL; - port->paramsi.user = 0; + SPA_FOR_EACH_ELEMENT_VAR(port->params, p) { + if (p->user > 0) { + p->flags ^= SPA_PARAM_INFO_SERIAL; + p->user = 0; } } } @@ -432,7 +431,6 @@ { struct props *p = &this->props; struct spa_pod_frame f2; - uint32_t i; switch (result.index) { case 0: @@ -596,9 +594,9 @@ spa_pod_builder_prop(&b, SPA_PROP_INFO_labels, 0); spa_pod_builder_push_struct(&b, &f1); - for (i = 0; i < SPA_N_ELEMENTS(channelmix_upmix_info); i++) { - spa_pod_builder_string(&b, channelmix_upmix_infoi.label); - spa_pod_builder_string(&b, channelmix_upmix_infoi.description); + SPA_FOR_EACH_ELEMENT_VAR(channelmix_upmix_info, i) { + spa_pod_builder_string(&b, i->label); + spa_pod_builder_string(&b, i->description); } spa_pod_builder_pop(&b, &f1); param = spa_pod_builder_pop(&b, &f0); @@ -646,9 +644,9 @@ 0); spa_pod_builder_prop(&b, SPA_PROP_INFO_labels, 0); spa_pod_builder_push_struct(&b, &f1); - for (i = 0; i < SPA_N_ELEMENTS(dither_method_info); i++) { - spa_pod_builder_string(&b, dither_method_infoi.label); - spa_pod_builder_string(&b, dither_method_infoi.description); + SPA_FOR_EACH_ELEMENT_VAR(dither_method_info, i) { + spa_pod_builder_string(&b, i->label); + spa_pod_builder_string(&b, i->description); } spa_pod_builder_pop(&b, &f1); param = spa_pod_builder_pop(&b, &f0); @@ -827,8 +825,11 @@ if (spa_pod_is_string(pod)) { spa_pod_copy_string(pod, sizeof(value), value); } else if (spa_pod_is_float(pod)) { - snprintf(value, sizeof(value), "%f", + spa_dtoa(value, sizeof(value), SPA_POD_VALUE(struct spa_pod_float, pod)); + } else if (spa_pod_is_double(pod)) { + spa_dtoa(value, sizeof(value), + SPA_POD_VALUE(struct spa_pod_double, pod)); } else if (spa_pod_is_int(pod)) { snprintf(value, sizeof(value), "%d", SPA_POD_VALUE(struct spa_pod_int, pod)); @@ -844,6 +845,9 @@ spa_log_info(this->log, "key:'%s' val:'%s'", name, value); changed += audioconvert_set_param(this, name, value); } + if (changed) { + channelmix_init(&this->mix); + } return changed; } @@ -911,6 +915,11 @@ break; case SPA_PROP_rate: spa_pod_get_double(&prop->value, &p->rate); + if (!this->rate_adjust && p->rate != 1.0) { + this->rate_adjust = true; + spa_log_info(this->log, "%p: activating adaptive resampler", + this); + } break; case SPA_PROP_params: changed += parse_prop_params(this, &prop->value); @@ -925,7 +934,6 @@ else if (have_channel_volume) p->have_soft_volume = false; - channelmix_init(&this->mix); set_volume(this); } return changed; @@ -1064,7 +1072,10 @@ if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) return -EINVAL; - if (info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + if (info.info.raw.format == 0 || + info.info.raw.rate == 0 || + info.info.raw.channels == 0 || + info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) return -EINVAL; infop = &info; @@ -1280,12 +1291,23 @@ this->paramsIDX_Props.user++; } +static char *format_position(char *str, size_t len, uint32_t channels, uint32_t *position) +{ + uint32_t i, idx = 0; + for (i = 0; i < channels; i++) + idx += snprintf(str + idx, len - idx, "%s%s", i == 0 ? "" : " ", + spa_debug_type_find_short_name(spa_type_audio_channel, + positioni)); + return str; +} + static int setup_channelmix(struct impl *this) { struct dir *in = &this->dirSPA_DIRECTION_INPUT; struct dir *out = &this->dirSPA_DIRECTION_OUTPUT; uint32_t i, src_chan, dst_chan, p; uint64_t src_mask, dst_mask; + char str1024; int res; src_chan = in->format.info.raw.channels; @@ -1300,6 +1322,11 @@ dst_mask |= 1ULL << (p < 64 ? p : 0); } + spa_log_info(this->log, "in %s (%016"PRIx64")", format_position(str, sizeof(str), + src_chan, in->format.info.raw.position), src_mask); + spa_log_info(this->log, "out %s (%016"PRIx64")", format_position(str, sizeof(str), + dst_chan, out->format.info.raw.position), dst_mask); + if (src_mask & 1) src_mask = default_mask(src_chan); if (dst_mask & 1) @@ -1357,6 +1384,8 @@ this->resample.quality = this->props.resample_quality; this->resample.cpu_flags = this->cpu_flags; + this->rate_adjust = this->props.rate != 1.0; + if (this->resample_peaks) res = resample_peaks_init(&this->resample); else @@ -1941,9 +1970,13 @@ spa_log_error(this->log, "can't parse format %s", spa_strerror(res)); return res; } - if (info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) { - spa_log_error(this->log, "too many channels %d > %d", - info.info.raw.channels, SPA_AUDIO_MAX_CHANNELS); + if (info.info.raw.format == 0 || + info.info.raw.rate == 0 || + info.info.raw.channels == 0 || + info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) { + spa_log_error(this->log, "invalid format:%d rate:%d channels:%d", + info.info.raw.format, info.info.raw.rate, + info.info.raw.channels); return -EINVAL; } port->stride = calc_width(&info); @@ -2272,8 +2305,7 @@ static inline bool resample_is_passthrough(struct impl *this) { return this->resample.i_rate == this->resample.o_rate && this->rate_scale == 1.0 && - this->props.rate == 1.0 && - (this->io_rate_match == NULL || + !this->rate_adjust && (this->io_rate_match == NULL || !SPA_FLAG_IS_SET(this->io_rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE)); }
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/benchmark-fmt-ops.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/benchmark-fmt-ops.c
Changed
@@ -105,21 +105,17 @@ static void run_testc(const char *name, const char *impl, bool in_packed, bool out_packed, convert_func_t func, int channel_count) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(sample_sizes); i++) { + SPA_FOR_EACH_ELEMENT_VAR(sample_sizes, s) { run_test1(name, impl, in_packed, out_packed, func, channel_count, - (sample_sizesi + (channel_count -1)) / channel_count); + (*s + (channel_count -1)) / channel_count); } } static void run_test(const char *name, const char *impl, bool in_packed, bool out_packed, convert_func_t func) { - size_t i, j; - - for (i = 0; i < SPA_N_ELEMENTS(sample_sizes); i++) { - for (j = 0; j < SPA_N_ELEMENTS(channel_counts); j++) { - run_test1(name, impl, in_packed, out_packed, func, channel_countsj, - (sample_sizesi + (channel_countsj -1)) / channel_countsj); + SPA_FOR_EACH_ELEMENT_VAR(sample_sizes, s) { + SPA_FOR_EACH_ELEMENT_VAR(channel_counts, c) { + run_test1(name, impl, in_packed, out_packed, func, *c, (*s + (*c -1)) / *c); } } }
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/channelmix-ops-sse.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/channelmix-ops-sse.c
Changed
@@ -101,6 +101,64 @@ } } +static inline void avg_sse(float *d, const float *s0, const float *s1, uint32_t n_samples) +{ + uint32_t n, unrolled; + __m128 half = _mm_set1_ps(0.5f); + + if (SPA_IS_ALIGNED(d, 16) && + SPA_IS_ALIGNED(s0, 16) && + SPA_IS_ALIGNED(s1, 16)) + unrolled = n_samples & ~7; + else + unrolled = 0; + + for (n = 0; n < unrolled; n += 8) { + _mm_store_ps(&dn + 0, + _mm_mul_ps( + _mm_add_ps( + _mm_load_ps(&s0n + 0), + _mm_load_ps(&s1n + 0)), + half)); + _mm_store_ps(&dn + 4, + _mm_mul_ps( + _mm_add_ps( + _mm_load_ps(&s0n + 4), + _mm_load_ps(&s1n + 4)), + half)); + } + + for (; n < n_samples; n++) + _mm_store_ss(&dn, + _mm_mul_ss( + _mm_add_ss( + _mm_load_ss(&s0n), + _mm_load_ss(&s1n)), + half)); +} + +static inline void sub_sse(float *d, const float *s0, const float *s1, uint32_t n_samples) +{ + uint32_t n, unrolled; + + if (SPA_IS_ALIGNED(d, 16) && + SPA_IS_ALIGNED(s0, 16) && + SPA_IS_ALIGNED(s1, 16)) + unrolled = n_samples & ~7; + else + unrolled = 0; + + for (n = 0; n < unrolled; n += 8) { + _mm_store_ps(&dn + 0, + _mm_sub_ps(_mm_load_ps(&s0n + 0), _mm_load_ps(&s1n + 0))); + _mm_store_ps(&dn + 4, + _mm_sub_ps(_mm_load_ps(&s0n + 4), _mm_load_ps(&s1n + 4))); + } + for (; n < n_samples; n++) + _mm_store_ss(&dn, + _mm_sub_ss(_mm_load_ss(&s0n), _mm_load_ss(&s1n))); +} + void channelmix_copy_sse(struct channelmix *mix, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_samples) { @@ -145,6 +203,133 @@ } } +void +channelmix_f32_2_3p1_sse(struct channelmix *mix, void * SPA_RESTRICT dst, + const void * SPA_RESTRICT src, uint32_t n_samples) +{ + uint32_t i, n, unrolled, n_dst = mix->dst_chan; + float **d = (float **)dst; + const float **s = (const float **)src; + const float v0 = mix->matrix00; + const float v1 = mix->matrix11; + const float v2 = (mix->matrix20 + mix->matrix21) * 0.5f; + const float v3 = (mix->matrix30 + mix->matrix31) * 0.5f; + + if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) { + for (i = 0; i < n_dst; i++) + clear_sse(di, n_samples); + } + else { + if (mix->widen == 0.0f) { + vol_sse(d0, s0, v0, n_samples); + vol_sse(d1, s1, v1, n_samples); + avg_sse(d2, s0, s1, n_samples); + } else { + const __m128 mv0 = _mm_set1_ps(mix->matrix00); + const __m128 mv1 = _mm_set1_ps(mix->matrix11); + const __m128 mw = _mm_set1_ps(mix->widen); + const __m128 mh = _mm_set1_ps(0.5f); + __m128 t01, t11, w1, c1; + + if (SPA_IS_ALIGNED(s0, 16) && + SPA_IS_ALIGNED(s1, 16) && + SPA_IS_ALIGNED(d0, 16) && + SPA_IS_ALIGNED(d1, 16) && + SPA_IS_ALIGNED(d2, 16)) + unrolled = n_samples & ~3; + else + unrolled = 0; + + for(n = 0; n < unrolled; n += 4) { + t00 = _mm_load_ps(&s0n); + t10 = _mm_load_ps(&s1n); + c0 = _mm_add_ps(t00, t10); + w0 = _mm_mul_ps(c0, mw); + _mm_store_ps(&d0n, _mm_mul_ps(_mm_sub_ps(t00, w0), mv0)); + _mm_store_ps(&d1n, _mm_mul_ps(_mm_sub_ps(t10, w0), mv1)); + _mm_store_ps(&d2n, _mm_mul_ps(c0, mh)); + } + for (; n < n_samples; n++) { + t00 = _mm_load_ss(&s0n); + t10 = _mm_load_ss(&s1n); + c0 = _mm_add_ss(t00, t10); + w0 = _mm_mul_ss(c0, mw); + _mm_store_ss(&d0n, _mm_mul_ss(_mm_sub_ss(t00, w0), mv0)); + _mm_store_ss(&d1n, _mm_mul_ss(_mm_sub_ss(t10, w0), mv1)); + _mm_store_ss(&d2n, _mm_mul_ss(c0, mh)); + } + } + lr4_process(&mix->lr43, d3, d2, v3, n_samples); + lr4_process(&mix->lr42, d2, d2, v2, n_samples); + } +} + +void +channelmix_f32_2_5p1_sse(struct channelmix *mix, void * SPA_RESTRICT dst, + const void * SPA_RESTRICT src, uint32_t n_samples) +{ + uint32_t i, n_dst = mix->dst_chan; + float **d = (float **)dst; + const float **s = (const float **)src; + const float v4 = mix->matrix40; + const float v5 = mix->matrix51; + + if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) { + for (i = 0; i < n_dst; i++) + clear_sse(di, n_samples); + } + else { + channelmix_f32_2_3p1_sse(mix, dst, src, n_samples); + + if (mix->upmix != CHANNELMIX_UPMIX_PSD) { + vol_sse(d4, s0, v4, n_samples); + vol_sse(d5, s1, v5, n_samples); + } else { + sub_sse(d4, s0, s1, n_samples); + + delay_convolve_run(mix->buffer1, &mix->pos1, BUFFER_SIZE, mix->delay, + mix->taps, mix->n_taps, d5, d4, -v5, n_samples); + delay_convolve_run(mix->buffer0, &mix->pos0, BUFFER_SIZE, mix->delay, + mix->taps, mix->n_taps, d4, d4, v4, n_samples); + } + } +} + +void +channelmix_f32_2_7p1_sse(struct channelmix *mix, void * SPA_RESTRICT dst, + const void * SPA_RESTRICT src, uint32_t n_samples) +{ + uint32_t i, n_dst = mix->dst_chan; + float **d = (float **)dst; + const float **s = (const float **)src; + const float v4 = mix->matrix40; + const float v5 = mix->matrix51; + const float v6 = mix->matrix60; + const float v7 = mix->matrix71; + + if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) { + for (i = 0; i < n_dst; i++) + clear_sse(di, n_samples); + } + else { + channelmix_f32_2_3p1_sse(mix, dst, src, n_samples); + + vol_sse(d4, s0, v4, n_samples); + vol_sse(d5, s1, v5, n_samples); + + if (mix->upmix != CHANNELMIX_UPMIX_PSD) { + vol_sse(d6, s0, v6, n_samples); + vol_sse(d7, s1, v7, n_samples); + } else { + sub_sse(d6, s0, s1, n_samples); + + delay_convolve_run(mix->buffer1, &mix->pos1, BUFFER_SIZE, mix->delay, + mix->taps, mix->n_taps, d7, d6, -v7, n_samples); + delay_convolve_run(mix->buffer0, &mix->pos0, BUFFER_SIZE, mix->delay, + mix->taps, mix->n_taps, d6, d6, v6, 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.59.tar.gz/spa/plugins/audioconvert/channelmix-ops.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/channelmix-ops.c
Changed
@@ -30,6 +30,7 @@ #include <spa/support/cpu.h> #include <spa/support/log.h> #include <spa/utils/defs.h> +#include <spa/debug/types.h> #include "channelmix-ops.h" #include "hilbert.h" @@ -69,8 +70,17 @@ MAKE(4, MASK_QUAD, 1, MASK_MONO, channelmix_f32_4_1_c), MAKE(4, MASK_3_1, 1, MASK_MONO, channelmix_f32_4_1_c), MAKE(2, MASK_STEREO, 4, MASK_QUAD, channelmix_f32_2_4_c), +#if defined (HAVE_SSE) + MAKE(2, MASK_STEREO, 4, MASK_3_1, channelmix_f32_2_3p1_sse, SPA_CPU_FLAG_SSE), +#endif MAKE(2, MASK_STEREO, 4, MASK_3_1, channelmix_f32_2_3p1_c), +#if defined (HAVE_SSE) + MAKE(2, MASK_STEREO, 6, MASK_5_1, channelmix_f32_2_5p1_sse, SPA_CPU_FLAG_SSE), +#endif MAKE(2, MASK_STEREO, 6, MASK_5_1, channelmix_f32_2_5p1_c), +#if defined (HAVE_SSE) + MAKE(2, MASK_STEREO, 8, MASK_7_1, channelmix_f32_2_7p1_sse, SPA_CPU_FLAG_SSE), +#endif MAKE(2, MASK_STEREO, 8, MASK_7_1, channelmix_f32_2_7p1_c), #if defined (HAVE_SSE) MAKE(4, MASK_3_1, 2, MASK_STEREO, channelmix_f32_3p1_2_sse, SPA_CPU_FLAG_SSE), @@ -95,7 +105,7 @@ 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), + MAKE(ANY, 0, ANY, 0, channelmix_f32_n_m_sse, SPA_CPU_FLAG_SSE), #endif MAKE(ANY, 0, ANY, 0, channelmix_f32_n_m_c), }; @@ -108,19 +118,18 @@ static const struct channelmix_info *find_channelmix_info(uint32_t src_chan, uint64_t src_mask, uint32_t dst_chan, uint64_t dst_mask, uint32_t cpu_flags) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(channelmix_table); i++) { - if (!MATCH_CPU_FLAGS(channelmix_tablei.cpu_flags, cpu_flags)) + SPA_FOR_EACH_ELEMENT_VAR(channelmix_table, info) { + if (!MATCH_CPU_FLAGS(info->cpu_flags, cpu_flags)) continue; if (src_chan == dst_chan && src_mask == dst_mask) - return &channelmix_tablei; + return info; - if (MATCH_CHAN(channelmix_tablei.src_chan, src_chan) && - MATCH_CHAN(channelmix_tablei.dst_chan, dst_chan) && - MATCH_MASK(channelmix_tablei.src_mask, src_mask) && - MATCH_MASK(channelmix_tablei.dst_mask, dst_mask)) - return &channelmix_tablei; + if (MATCH_CHAN(info->src_chan, src_chan) && + MATCH_CHAN(info->dst_chan, dst_chan) && + MATCH_MASK(info->src_mask, src_mask) && + MATCH_MASK(info->dst_mask, dst_mask)) + return info; } return NULL; } @@ -302,6 +311,7 @@ _MATRIX(SL,RL) += 1.0f; _MATRIX(SR,RR) += 1.0f; } + keep &= ~SIDE; } else if (dst_mask & STEREO) { spa_log_debug(mix->log, "assign RL+RR to FL+FR (%f)", slev); if (matrix_encoding == MATRIX_DOLBY) { @@ -339,6 +349,7 @@ _MATRIX(RL,SL) += 1.0f; _MATRIX(RR,SR) += 1.0f; } + keep &= ~REAR; } else if (dst_mask & _MASK(RC)) { spa_log_debug(mix->log, "assign SL+SR to RC (%f)", SQRT1_2); _MATRIX(RC,SL)+= SQRT1_2; @@ -474,10 +485,33 @@ spa_log_debug(mix->log, "won't produce SIDE"); } } + if (unassigned & _MASK(RC)) { + if ((src_mask & REAR) == REAR) { + spa_log_debug(mix->log, "produce RC from REAR (%f)", 0.5f); + _MATRIX(RC,RL) += 0.5f; + _MATRIX(RC,RR) += 0.5f; + } else if ((src_mask & SIDE) == SIDE) { + spa_log_debug(mix->log, "produce RC from SIDE (%f)", 0.5f); + _MATRIX(RC,SL) += 0.5f; + _MATRIX(RC,SR) += 0.5f; + } else if ((src_mask & STEREO) == STEREO) { + spa_log_debug(mix->log, "produce RC from STEREO (%f)", 0.5f); + _MATRIX(RC,FL) += 0.5f; + _MATRIX(RC,FR) += 0.5f; + } else if ((src_mask & FRONT) == FRONT && + mix->upmix == CHANNELMIX_UPMIX_SIMPLE) { + spa_log_debug(mix->log, "produce RC from FC (%f)", slev); + _MATRIX(RC,FC) += slev; + } else { + spa_log_debug(mix->log, "won't produce RC"); + } + } done: for (jc = 0, ic = 0, i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { float sum = 0.0f; + char str1024, str21024; + int idx = 0, idx2 = 0; if ((dst_mask & (1UL << i)) == 0) continue; for (jc = 0, j = 0; j < SPA_AUDIO_MAX_CHANNELS; j++) { @@ -485,9 +519,27 @@ continue; if (ic >= dst_chan || jc >= src_chan) continue; + + if (i == 0) + idx2 += snprintf(str2 + idx2, sizeof(str2) - idx2, "%-4.4s ", + spa_debug_type_find_short_name(spa_type_audio_channel, j + 3)); + mix->matrix_origicjc++ = matrixij; sum += fabs(matrixij); + + if (matrixij == 0.0f) + idx += snprintf(str + idx, sizeof(str) - idx, " "); + else + idx += snprintf(str + idx, sizeof(str) - idx, "%1.3f ", matrixij); } + if (dst_mask != 0 && src_mask != 0 && sum > 0.0f) { + if (i == 0) + spa_log_info(mix->log, " %s", str2); + spa_log_info(mix->log, "%-4.4s %s %f", + spa_debug_type_find_short_name(spa_type_audio_channel, i + 3), + str, sum); + } + maxsum = SPA_MAX(maxsum, sum); if (i == _CH(LFE) && mix->lfe_cutoff > 0.0f && filter_lfe) { spa_log_info(mix->log, "channel %d is LFE cutoff:%f", ic, mix->lfe_cutoff);
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/channelmix-ops.h -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/channelmix-ops.h
Changed
@@ -109,10 +109,9 @@ static inline uint32_t channelmix_upmix_from_label(const char *label) { - uint32_t i; - for (i = 0; i < SPA_N_ELEMENTS(channelmix_upmix_info); i++) { - if (spa_streq(channelmix_upmix_infoi.label, label)) - return channelmix_upmix_infoi.upmix; + SPA_FOR_EACH_ELEMENT_VAR(channelmix_upmix_info, i) { + if (spa_streq(i->label, label)) + return i->upmix; } return CHANNELMIX_UPMIX_NONE; } @@ -148,6 +147,9 @@ #if defined (HAVE_SSE) DEFINE_FUNCTION(copy, sse); DEFINE_FUNCTION(f32_n_m, sse); +DEFINE_FUNCTION(f32_2_3p1, sse); +DEFINE_FUNCTION(f32_2_5p1, sse); +DEFINE_FUNCTION(f32_2_7p1, sse); DEFINE_FUNCTION(f32_3p1_2, sse); DEFINE_FUNCTION(f32_5p1_2, sse); DEFINE_FUNCTION(f32_5p1_3p1, sse);
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/fmt-ops.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/fmt-ops.c
Changed
@@ -359,14 +359,13 @@ static const struct conv_info *find_conv_info(uint32_t src_fmt, uint32_t dst_fmt, uint32_t n_channels, uint32_t cpu_flags, uint32_t 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 && - MATCH_CHAN(conv_tablei.n_channels, n_channels) && - MATCH_CPU_FLAGS(conv_tablei.cpu_flags, cpu_flags) && - MATCH_DITHER(conv_tablei.conv_flags, conv_flags)) - return &conv_tablei; + SPA_FOR_EACH_ELEMENT_VAR(conv_table, c) { + if (c->src_fmt == src_fmt && + c->dst_fmt == dst_fmt && + MATCH_CHAN(c->n_channels, n_channels) && + MATCH_CPU_FLAGS(c->cpu_flags, cpu_flags) && + MATCH_DITHER(c->conv_flags, conv_flags)) + return c; } return NULL; } @@ -403,11 +402,10 @@ 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; + SPA_FOR_EACH_ELEMENT_VAR(noise_table, t) { + if (t->method == method && + MATCH_CPU_FLAGS(t->cpu_flags, cpu_flags)) + return t; } return NULL; } @@ -471,17 +469,14 @@ static const struct dither_info *find_dither_info(uint32_t method, uint32_t rate) { - size_t i; - - for (i = 0; i < SPA_N_ELEMENTS(dither_info); i++) { - const struct dither_info *di = &dither_infoi; + SPA_FOR_EACH_ELEMENT_VAR(dither_info, di) { if (di->method != method) continue; /* don't use shaped for too low rates, it moves the noise to * audible ranges */ if (di->ns != NULL && rate < di->rate * 3 / 4) return find_dither_info(DITHER_METHOD_TRIANGULAR_HF, rate); - return &dither_infoi; + return di; } return NULL; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/fmt-ops.h -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/fmt-ops.h
Changed
@@ -271,10 +271,9 @@ static inline uint32_t dither_method_from_label(const char *label) { - uint32_t i; - for (i = 0; i < SPA_N_ELEMENTS(dither_method_info); i++) { - if (spa_streq(dither_method_infoi.label, label)) - return dither_method_infoi.method; + SPA_FOR_EACH_ELEMENT_VAR(dither_method_info, i) { + if (spa_streq(i->label, label)) + return i->method; } return DITHER_METHOD_NONE; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/peaks-ops.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/peaks-ops.c
Changed
@@ -59,11 +59,9 @@ 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; + SPA_FOR_EACH_ELEMENT_VAR(peaks_table, t) { + if (MATCH_CPU_FLAGS(t->cpu_flags, cpu_flags)) + return t; } return NULL; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/resample-native.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/resample-native.c
Changed
@@ -66,6 +66,7 @@ (alpha / 2.0) * cos(2.0 * x); return r; } + static inline double window_cosh(double x, double n_taps) { double r; @@ -80,7 +81,7 @@ return r; } -#define window window_cosh +#define window (1 ? window_cosh : window_blackman) static int build_filter(float *taps, uint32_t stride, uint32_t n_taps, uint32_t n_phases, double cutoff) { @@ -125,11 +126,10 @@ #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; + SPA_FOR_EACH_ELEMENT_VAR(resample_table, t) { + if (t->format == format && + MATCH_CPU_FLAGS(t->cpu_flags, cpu_flags)) + return t; } return NULL; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/test-audioconvert.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/test-audioconvert.c
Changed
@@ -681,9 +681,9 @@ res = memcmp(b->datasj.data, out_data->datak, out_data->size); if (res != 0) { - fprintf(stderr, "error plane %d\n", j); + fprintf(stderr, "error port %d plane %d\n", i, j); spa_debug_mem(0, b->datasj.data, out_data->size); - spa_debug_mem(0, out_data->dataj, out_data->size); + spa_debug_mem(0, out_data->datak, out_data->size); } spa_assert_se(res == 0); @@ -702,7 +702,9 @@ static const float data_f32p_3 = { 0.3f, 0.3f, 0.3f, 0.3f }; static const float data_f32p_4 = { 0.4f, 0.4f, 0.4f, 0.4f }; static const float data_f32p_5 = { 0.5f, 0.5f, 0.5f, 0.5f }; +static const float data_f32p_5_6p1 = { 0.953553438f, 0.953553438f, 0.953553438f, 0.953553438f }; static const float data_f32p_6 = { 0.6f, 0.6f, 0.6f, 0.6f }; +static const float data_f32p_6_6p1 = { 1.053553343f, 1.053553343f, 1.053553343f, 1.053553343f }; static const float data_f32p_7 = { 0.7f, 0.7f, 0.7f, 0.7f }; static const float data_f32p_8 = { 0.8f, 0.8f, 0.8f, 0.8f }; @@ -710,6 +712,14 @@ 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f }; +static const float data_f32_6p1 = { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, + 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, + 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, + 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f }; +static const float data_f32_6p1_from_5p1 = { 0.1f, 0.2f, 0.3f, 0.4f, 0.55f, 0.5f, 0.6f, + 0.1f, 0.2f, 0.3f, 0.4f, 0.55f, 0.5f, 0.6f, + 0.1f, 0.2f, 0.3f, 0.4f, 0.55f, 0.5f, 0.6f, + 0.1f, 0.2f, 0.3f, 0.4f, 0.55f, 0.5f, 0.6f }; static const float data_f32_7p1_remapped = { 0.1f, 0.2f, 0.5f, 0.6f, 0.7f, 0.8f, 0.3f, 0.4f, 0.1f, 0.2f, 0.5f, 0.6f, 0.7f, 0.8f, 0.3f, 0.4f, @@ -740,6 +750,27 @@ .size = sizeof(float) * 4 }; +struct data dsp_5p1_from_6p1 = { + .mode = SPA_PARAM_PORT_CONFIG_MODE_dsp, + .info = SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 6, + .position = { + SPA_AUDIO_CHANNEL_FL, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + SPA_AUDIO_CHANNEL_RL, + SPA_AUDIO_CHANNEL_RR, + }), + .ports = 6, + .planes = 1, + .data = { data_f32p_1, data_f32p_2, data_f32p_3, data_f32p_4, data_f32p_5_6p1, data_f32p_6_6p1, }, + .size = sizeof(float) * 4 +}; + + struct data dsp_5p1_remapped = { .mode = SPA_PARAM_PORT_CONFIG_MODE_dsp, .info = SPA_AUDIO_INFO_RAW_INIT( @@ -760,6 +791,68 @@ .size = sizeof(float) * 4 }; +struct data dsp_5p1_remapped_from_6p1 = { + .mode = SPA_PARAM_PORT_CONFIG_MODE_dsp, + .info = SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 6, + .position = { + SPA_AUDIO_CHANNEL_FL, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_RL, + SPA_AUDIO_CHANNEL_RR, + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + }), + .ports = 6, + .planes = 1, + .data = { data_f32p_1, data_f32p_2, data_f32p_5_6p1, data_f32p_6_6p1, data_f32p_3, data_f32p_4, }, + .size = sizeof(float) * 4 +}; + +struct data dsp_6p1 = { + .mode = SPA_PARAM_PORT_CONFIG_MODE_dsp, + .info = SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 7, + .position = { + SPA_AUDIO_CHANNEL_FL, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + SPA_AUDIO_CHANNEL_RC, + SPA_AUDIO_CHANNEL_RL, + SPA_AUDIO_CHANNEL_RR, + }), + .ports = 7, + .planes = 1, + .data = { data_f32p_1, data_f32p_2, data_f32p_3, data_f32p_4, data_f32p_5, data_f32p_6, data_f32p_7 }, + .size = sizeof(float) * 4 +}; + +struct data dsp_6p1_side = { + .mode = SPA_PARAM_PORT_CONFIG_MODE_dsp, + .info = SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 7, + .position = { + SPA_AUDIO_CHANNEL_FL, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + SPA_AUDIO_CHANNEL_RC, + SPA_AUDIO_CHANNEL_SL, + SPA_AUDIO_CHANNEL_SR, + }), + .ports = 7, + .planes = 1, + .data = { data_f32p_1, data_f32p_2, data_f32p_3, data_f32p_4, data_f32p_5, data_f32p_6, data_f32p_7 }, + .size = sizeof(float) * 4 +}; + struct data dsp_7p1_remapped = { .mode = SPA_PARAM_PORT_CONFIG_MODE_dsp, .info = SPA_AUDIO_INFO_RAW_INIT( @@ -862,6 +955,90 @@ .size = sizeof(float) * 4 }; +struct data conv_f32_48000_6p1 = { + .mode = SPA_PARAM_PORT_CONFIG_MODE_convert, + .info = SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 7, + .position = { + SPA_AUDIO_CHANNEL_FL, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + SPA_AUDIO_CHANNEL_RC, + SPA_AUDIO_CHANNEL_RL, + SPA_AUDIO_CHANNEL_RR, + }), + .ports = 1, + .planes = 1, + .data = { data_f32_6p1 }, + .size = sizeof(data_f32_6p1) +}; + +struct data conv_f32_48000_6p1_from_5p1 = { + .mode = SPA_PARAM_PORT_CONFIG_MODE_convert, + .info = SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 7, + .position = { + SPA_AUDIO_CHANNEL_FL, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + SPA_AUDIO_CHANNEL_RC, + SPA_AUDIO_CHANNEL_RL, + SPA_AUDIO_CHANNEL_RR, + }), + .ports = 1, + .planes = 1, + .data = { data_f32_6p1_from_5p1 }, + .size = sizeof(data_f32_6p1_from_5p1) +}; + +struct data conv_f32_48000_6p1_side = { + .mode = SPA_PARAM_PORT_CONFIG_MODE_convert, + .info = SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 7, + .position = { + SPA_AUDIO_CHANNEL_FL, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + SPA_AUDIO_CHANNEL_RC, + SPA_AUDIO_CHANNEL_SL, + SPA_AUDIO_CHANNEL_SR, + }), + .ports = 1, + .planes = 1, + .data = { data_f32_6p1 }, + .size = sizeof(data_f32_6p1) +}; + +struct data conv_f32p_48000_6p1 = { + .mode = SPA_PARAM_PORT_CONFIG_MODE_convert, + .info = SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32P, + .rate = 48000, + .channels = 7, + .position = { + SPA_AUDIO_CHANNEL_FL, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + SPA_AUDIO_CHANNEL_RC, + SPA_AUDIO_CHANNEL_RL, + SPA_AUDIO_CHANNEL_RR, + }), + .ports = 1, + .planes = 7, + .data = { data_f32p_1, data_f32p_2, data_f32p_3, data_f32p_4, data_f32p_5, data_f32p_6, data_f32p_7 }, + .size = sizeof(float) * 4 +}; + struct data conv_f32p_48000_5p1_remapped = { .mode = SPA_PARAM_PORT_CONFIG_MODE_convert, .info = SPA_AUDIO_INFO_RAW_INIT( @@ -918,6 +1095,11 @@ run_convert(ctx, &dsp_5p1_remapped_2, &conv_f32p_48000_5p1); run_convert(ctx, &dsp_5p1_remapped_2, &conv_f32_48000_5p1_remapped); run_convert(ctx, &dsp_5p1_remapped_2, &conv_f32p_48000_5p1_remapped); + run_convert(ctx, &dsp_6p1, &conv_f32p_48000_6p1); + run_convert(ctx, &dsp_6p1, &conv_f32_48000_6p1); + run_convert(ctx, &dsp_6p1_side, &conv_f32_48000_6p1_side); + + run_convert(ctx, &dsp_5p1, &conv_f32_48000_6p1_from_5p1); return 0; } @@ -933,9 +1115,16 @@ run_convert(ctx, &conv_f32_48000_5p1_remapped, &dsp_5p1_remapped); run_convert(ctx, &conv_f32_48000_5p1_remapped, &dsp_5p1_remapped_2); run_convert(ctx, &conv_f32p_48000_5p1_remapped, &dsp_5p1); + run_convert(ctx, &conv_f32p_48000_6p1, &dsp_6p1); + run_convert(ctx, &conv_f32_48000_6p1, &dsp_6p1); + run_convert(ctx, &conv_f32_48000_6p1_side, &dsp_6p1_side); run_convert(ctx, &conv_f32p_48000_5p1_remapped, &dsp_5p1_remapped); run_convert(ctx, &conv_f32_48000_7p1_remapped, &dsp_7p1_remapped); run_convert(ctx, &conv_f32p_48000_5p1_remapped, &dsp_5p1_remapped_2); + + run_convert(ctx, &conv_f32_48000_6p1, &dsp_5p1_from_6p1); + run_convert(ctx, &conv_f32_48000_6p1_side, &dsp_5p1_from_6p1); + run_convert(ctx, &conv_f32_48000_6p1, &dsp_5p1_remapped_from_6p1); return 0; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/test-channelmix.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/test-channelmix.c
Changed
@@ -220,6 +220,46 @@ 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)); } +static void test_6p1_N(void) +{ + test_mix(7, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(RC)|_M(SL)|_M(SR), 1, _M(MONO), 0, + MATRIX(0.707107, 0.707107, 1.0, 0.0, 0.5, 0.5, 0.5)); + test_mix(7, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RC), + 6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 0, + MATRIX(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.707107, + 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.707107)); + test_mix(7, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RC), + 6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(RL)|_M(RR), 0, + MATRIX(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.707107, + 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.707107)); + test_mix(7, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(RC)|_M(RL)|_M(RR), + 6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(RL)|_M(RR), 0, + MATRIX(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.707107, 1.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.707107, 0.0, 1.0)); + test_mix(7, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RC), + 8, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RL)|_M(RR), 0, + MATRIX(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.707107, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.707107)); +} + static void test_7p1_N(void) { test_mix(8, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RL)|_M(RR), 1, _M(MONO), 0, @@ -316,6 +356,7 @@ test_3p1_N(); test_4_N(); test_5p1_N(); + test_6p1_N(); test_7p1_N(); test_n_m_impl();
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/test-source.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/test-source.c
Changed
@@ -535,8 +535,12 @@ case SPA_AUDIO_FORMAT_S24: case SPA_AUDIO_FORMAT_S24_OE: return 3; - default: + case SPA_AUDIO_FORMAT_S32P: + case SPA_AUDIO_FORMAT_S32: + case SPA_AUDIO_FORMAT_S32_OE: return 4; + default: + return 0; } } @@ -571,6 +575,12 @@ return res; port->stride = calc_width(&info); + if (port->stride == 0) + return -EINVAL; + if (info.info.raw.rate == 0 || + info.info.raw.channels == 0) + return -EINVAL; + if (SPA_AUDIO_FORMAT_IS_PLANAR(info.info.raw.format)) { port->blocks = info.info.raw.channels; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/volume-ops.c -> pipewire-0.3.60.tar.gz/spa/plugins/audioconvert/volume-ops.c
Changed
@@ -56,11 +56,9 @@ static const struct volume_info *find_volume_info(uint32_t cpu_flags) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(volume_table); i++) { - if (!MATCH_CPU_FLAGS(volume_tablei.cpu_flags, cpu_flags)) - continue; - return &volume_tablei; + SPA_FOR_EACH_ELEMENT_VAR(volume_table, t) { + if (MATCH_CPU_FLAGS(t->cpu_flags, cpu_flags)) + return t; } return NULL; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/audiomixer/audiomixer.c -> pipewire-0.3.60.tar.gz/spa/plugins/audiomixer/audiomixer.c
Changed
@@ -566,6 +566,10 @@ if (memcmp(&info, &this->format, sizeof(struct spa_audio_info))) return -EINVAL; } else { + if (info.info.raw.format == 0 || + info.info.raw.channels == 0) + return -EINVAL; + this->ops.fmt = info.info.raw.format; this->ops.n_channels = info.info.raw.channels; this->ops.cpu_flags = this->cpu_flags;
View file
pipewire-0.3.59.tar.gz/spa/plugins/audiomixer/mix-ops-avx.c -> pipewire-0.3.60.tar.gz/spa/plugins/audiomixer/mix-ops-avx.c
Changed
@@ -32,62 +32,6 @@ #include <immintrin.h> -static inline void mix_4(float * dst, - const float * SPA_RESTRICT src0, - const float * SPA_RESTRICT src1, - const float * SPA_RESTRICT src2, - uint32_t n_samples) -{ - uint32_t n, unrolled; - - if (SPA_IS_ALIGNED(src0, 32) && - SPA_IS_ALIGNED(src1, 32) && - SPA_IS_ALIGNED(src2, 32) && - SPA_IS_ALIGNED(dst, 32)) - unrolled = n_samples & ~15; - else - unrolled = 0; - - for (n = 0; n < unrolled; n += 16) { - __m256 in14, in24; - - in10 = _mm256_load_ps(&dstn + 0); - in20 = _mm256_load_ps(&dstn + 8); - in11 = _mm256_load_ps(&src0n + 0); - in21 = _mm256_load_ps(&src0n + 8); - in12 = _mm256_load_ps(&src1n + 0); - in22 = _mm256_load_ps(&src1n + 8); - in13 = _mm256_load_ps(&src2n + 0); - in23 = _mm256_load_ps(&src2n + 8); - - in10 = _mm256_add_ps(in10, in11); - in20 = _mm256_add_ps(in20, in21); - in12 = _mm256_add_ps(in12, in13); - in22 = _mm256_add_ps(in22, in23); - in10 = _mm256_add_ps(in10, in12); - in20 = _mm256_add_ps(in20, in22); - - _mm256_store_ps(&dstn + 0, in10); - _mm256_store_ps(&dstn + 8, in20); - } - for (; n < n_samples; n++) { - __m128 in4; - in0 = _mm_load_ss(&dstn), - in1 = _mm_load_ss(&src0n), - in2 = _mm_load_ss(&src1n), - in3 = _mm_load_ss(&src2n), - in0 = _mm_add_ss(in0, in1); - in2 = _mm_add_ss(in2, in3); - in0 = _mm_add_ss(in0, in2); - _mm_store_ss(&dstn, in0); - } -} - - -static inline void mix_2(float * dst, const float * SPA_RESTRICT src, uint32_t n_samples) -{ -} - void mix_f32_avx(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_src, uint32_t n_samples)
View file
pipewire-0.3.59.tar.gz/spa/plugins/audiomixer/mix-ops.c -> pipewire-0.3.60.tar.gz/spa/plugins/audiomixer/mix-ops.c
Changed
@@ -98,13 +98,11 @@ static const struct mix_info *find_mix_info(uint32_t fmt, uint32_t n_channels, uint32_t cpu_flags) { - size_t i; - - for (i = 0; i < SPA_N_ELEMENTS(mix_table); i++) { - if (mix_tablei.fmt == fmt && - MATCH_CHAN(mix_tablei.n_channels, n_channels) && - MATCH_CPU_FLAGS(mix_tablei.cpu_flags, cpu_flags)) - return &mix_tablei; + SPA_FOR_EACH_ELEMENT_VAR(mix_table, t) { + if (t->fmt == fmt && + MATCH_CHAN(t->n_channels, n_channels) && + MATCH_CPU_FLAGS(t->cpu_flags, cpu_flags)) + return t; } return NULL; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/audiotestsrc/audiotestsrc.c -> pipewire-0.3.60.tar.gz/spa/plugins/audiotestsrc/audiotestsrc.c
Changed
@@ -743,6 +743,10 @@ if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) return -EINVAL; + if (info.info.raw.rate == 0 || + info.info.raw.channels == 0) + return -EINVAL; + switch (info.info.raw.format) { case SPA_AUDIO_FORMAT_S16: idx = 0;
View file
pipewire-0.3.59.tar.gz/spa/plugins/avb/avb-pcm.c -> pipewire-0.3.60.tar.gz/spa/plugins/avb/avb-pcm.c
Changed
@@ -439,7 +439,7 @@ } } -static int frame_size(uint32_t format) +static int calc_frame_size(uint32_t format) { switch(format) { case SPA_AUDIO_FORMAT_F32_BE: @@ -647,7 +647,7 @@ SPA_AVBTP_PACKET_AAF_SET_FORMAT(pdu, spa_format_to_aaf(state->format)); SPA_AVBTP_PACKET_AAF_SET_NSR(pdu, spa_rate_to_aaf(state->rate)); SPA_AVBTP_PACKET_AAF_SET_CHAN_PER_FRAME(pdu, state->channels); - SPA_AVBTP_PACKET_AAF_SET_BIT_DEPTH(pdu, frame_size(state->format)*8); + SPA_AVBTP_PACKET_AAF_SET_BIT_DEPTH(pdu, calc_frame_size(state->format)*8); SPA_AVBTP_PACKET_AAF_SET_DATA_LEN(pdu, payload_size); SPA_AVBTP_PACKET_AAF_SET_SP(pdu, SPA_AVBTP_AAF_PCM_SP_NORMAL); } @@ -690,14 +690,22 @@ int spa_avb_set_format(struct state *state, struct spa_audio_info *fmt, uint32_t flags) { - int res; + int res, frame_size; struct props *p = &state->props; + frame_size = calc_frame_size(fmt->info.raw.format); + if (frame_size == 0) + return -EINVAL; + + if (fmt->info.raw.rate == 0 || + fmt->info.raw.channels == 0) + return -EINVAL; + state->format = fmt->info.raw.format; state->rate = fmt->info.raw.rate; state->channels = fmt->info.raw.channels; state->blocks = 1; - state->stride = state->channels * frame_size(state->format); + state->stride = state->channels * frame_size; if ((res = setup_socket(state)) < 0) return res;
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/a2dp-codec-aac.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/a2dp-codec-aac.c
Changed
@@ -206,11 +206,11 @@ spa_pod_builder_push_choice(b, &f1, SPA_CHOICE_None, 0); choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f1); i = 0; - for (size_t j = 0; j < SPA_N_ELEMENTS(aac_frequencies); j++) { - if (AAC_GET_FREQUENCY(conf) & aac_frequenciesj.config) { + SPA_FOR_EACH_ELEMENT_VAR(aac_frequencies, f) { + if (AAC_GET_FREQUENCY(conf) & f->config) { if (i++ == 0) - spa_pod_builder_int(b, aac_frequenciesj.value); - spa_pod_builder_int(b, aac_frequenciesj.value); + spa_pod_builder_int(b, f->value); + spa_pod_builder_int(b, f->value); } } if (i == 0) @@ -272,14 +272,15 @@ if (!(conf.object_type & (AAC_OBJECT_TYPE_MPEG2_AAC_LC | AAC_OBJECT_TYPE_MPEG4_AAC_LC))) return -EINVAL; - - for (j = 0; j < SPA_N_ELEMENTS(aac_frequencies); ++j) { - if (AAC_GET_FREQUENCY(conf) & aac_frequenciesj.config) { - info->info.raw.rate = aac_frequenciesj.value; + j = 0; + SPA_FOR_EACH_ELEMENT_VAR(aac_frequencies, f) { + if (AAC_GET_FREQUENCY(conf) & f->config) { + info->info.raw.rate = f->value; + j++; break; } } - if (j == SPA_N_ELEMENTS(aac_frequencies)) + if (j == 0) return -EINVAL; if (conf.channels & AAC_CHANNELS_2) { @@ -609,7 +610,7 @@ if (res != AACENC_OK) return -EINVAL; - return 0; + return this->cur_bitrate; } static int codec_reduce_bitpool(void *data)
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/a2dp-codec-lc3plus.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/a2dp-codec-lc3plus.c
Changed
@@ -747,15 +747,17 @@ static int codec_reduce_bitpool(void *data) { struct impl *this = data; - this->e.next_bitrate = this->bitrate * 3 / 4; - return 0; + this->e.next_bitrate = SPA_CLAMP(this->bitrate * 3 / 4, + BITRATE_MIN * this->channels, BITRATE_MAX * this->channels); + return this->e.next_bitrate; } static int codec_increase_bitpool(void *data) { struct impl *this = data; - this->e.next_bitrate = this->bitrate * 5 / 4; - return 0; + this->e.next_bitrate = SPA_CLAMP(this->bitrate * 5 / 4, + BITRATE_MIN * this->channels, BITRATE_MAX * this->channels); + return this->e.next_bitrate; } const struct media_codec a2dp_codec_lc3plus_hr = {
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/a2dp-codec-opus.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/a2dp-codec-opus.c
Changed
@@ -1360,7 +1360,6 @@ .codec_id = A2DP_CODEC_VENDOR, \ .vendor = { .vendor_id = OPUS_05_VENDOR_ID, \ .codec_id = OPUS_05_CODEC_ID }, \ - .fill_caps = codec_fill_caps, \ .select_config = codec_select_config, \ .enum_config = codec_enum_config, \ .validate_config = codec_validate_config, \ @@ -1385,6 +1384,7 @@ .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05, .name = "opus_05", .description = "Opus", + .fill_caps = codec_fill_caps, }; const struct media_codec a2dp_codec_opus_05_51 = { @@ -1392,6 +1392,8 @@ .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_51, .name = "opus_05_51", .description = "Opus 5.1 Surround", + .endpoint_name = "opus_05", + .fill_caps = NULL, }; const struct media_codec a2dp_codec_opus_05_71 = { @@ -1399,6 +1401,8 @@ .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_71, .name = "opus_05_71", .description = "Opus 7.1 Surround", + .endpoint_name = "opus_05", + .fill_caps = NULL, }; /* Bidi return channel codec: doesn't have endpoints */ @@ -1415,6 +1419,7 @@ .name = "opus_05_duplex", .description = "Opus Duplex", .duplex_codec = &a2dp_codec_opus_05_return, + .fill_caps = codec_fill_caps, }; const struct media_codec a2dp_codec_opus_05_pro = { @@ -1425,6 +1430,8 @@ .init_props = codec_init_props, .clear_props = codec_clear_props, .duplex_codec = &a2dp_codec_opus_05_return, + .endpoint_name = "opus_05_duplex", + .fill_caps = NULL, }; MEDIA_CODEC_EXPORT_DEF(
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/backend-native.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/backend-native.c
Changed
@@ -51,6 +51,9 @@ #include <libusb.h> #endif +#include "modemmanager.h" +#include "upower.h" + static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.native"); #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT &log_topic @@ -60,12 +63,39 @@ #define HFP_CODEC_SWITCH_INITIAL_TIMEOUT_MSEC 5000 #define HFP_CODEC_SWITCH_TIMEOUT_MSEC 20000 +#define INTERNATIONAL_NUMBER 145 +#define NATIONAL_NUMBER 129 + +#define MAX_HF_INDICATORS 16 + enum { HFP_AG_INITIAL_CODEC_SETUP_NONE = 0, HFP_AG_INITIAL_CODEC_SETUP_SEND, HFP_AG_INITIAL_CODEC_SETUP_WAIT }; +#define CIND_INDICATORS "(\"service\",(0-1)),(\"call\",(0-1)),(\"callsetup\",(0-3)),(\"callheld\",(0-2)),(\"signal\",(0-5)),(\"roam\",(0-1)),\"battchg\",(0-5))" +enum { + CIND_SERVICE = 1, + CIND_CALL, + CIND_CALLSETUP, + CIND_CALLHELD, + CIND_SIGNAL, + CIND_ROAM, + CIND_BATTERY_LEVEL, + CIND_MAX +}; + +struct modem { + bool network_has_service; + unsigned int signal_strength; + bool network_is_roaming; + char *operator_name; + char *own_number; + bool active_call; + unsigned int call_setup; +}; + struct impl { struct spa_bt_backend this; @@ -87,6 +117,13 @@ struct spa_list rfcomm_list; unsigned int defer_setup_enabled:1; + + struct modem modem; + unsigned int battery_level; + + void *modemmanager; + struct spa_source *ring_timer; + void *upower; }; struct transport_data { @@ -142,9 +179,13 @@ unsigned int hfp_ag_initial_codec_setup:2; unsigned int cind_call_active:1; unsigned int cind_call_notify:1; + unsigned int extended_error_reporting:1; + unsigned int clip_notify:1; enum hfp_hf_state hf_state; enum hsp_hs_state hs_state; unsigned int codec; + uint32_t cind_enabled_indicators; + char *hf_indicatorsMAX_HF_INDICATORS; #endif }; @@ -233,6 +274,11 @@ static void rfcomm_free(struct rfcomm *rfcomm) { codec_switch_stop_timer(rfcomm); + for (int i = 0; i < MAX_HF_INDICATORS; i++) { + if (rfcomm->hf_indicatorsi) { + free(rfcomm->hf_indicatorsi); + } + } spa_list_remove(&rfcomm->link); if (rfcomm->path) free(rfcomm->path); @@ -351,6 +397,14 @@ return len; } +static void rfcomm_send_error(const struct rfcomm *rfcomm, enum cmee_error error) +{ + if (rfcomm->extended_error_reporting) + rfcomm_send_reply(rfcomm, "+CME ERROR: %d", error); + else + rfcomm_send_reply(rfcomm, "ERROR"); +} + static bool rfcomm_volume_enabled(struct rfcomm *rfcomm) { return rfcomm->device != NULL @@ -720,6 +774,7 @@ unsigned int selected_codec; unsigned int indicator; unsigned int indicator_value; + unsigned int value; int xapl_vendor; int xapl_product; int xapl_features; @@ -761,6 +816,7 @@ } /* send reply to HF with the features supported by Audio Gateway (=computer) */ + ag_features |= mm_supported_features(); ag_features |= SPA_BT_HFP_AG_FEATURE_HF_INDICATORS; rfcomm_send_reply(rfcomm, "+BRSF: %u", ag_features); rfcomm_send_reply(rfcomm, "OK"); @@ -788,10 +844,12 @@ rfcomm_send_reply(rfcomm, "OK"); } else if (spa_strstartswith(buf, "AT+CIND=?")) { - rfcomm_send_reply(rfcomm, "+CIND:(\"service\",(0-1)),(\"call\",(0-1)),(\"callsetup\",(0-3)),(\"callheld\",(0-2))"); + rfcomm_send_reply(rfcomm, "+CIND:%s", CIND_INDICATORS); rfcomm_send_reply(rfcomm, "OK"); } else if (spa_strstartswith(buf, "AT+CIND?")) { - rfcomm_send_reply(rfcomm, "+CIND: 0,%d,0,0", rfcomm->cind_call_active); + rfcomm_send_reply(rfcomm, "+CIND: %d,%d,%d,0,%d,%d,%d", backend->modem.network_has_service, + backend->modem.active_call, backend->modem.call_setup, backend->modem.signal_strength, + backend->modem.network_is_roaming, backend->battery_level); rfcomm_send_reply(rfcomm, "OK"); } else if (spa_strstartswith(buf, "AT+CMER")) { int mode, keyp, disp, ind; @@ -824,8 +882,13 @@ } } else if (!rfcomm->slc_configured) { spa_log_warn(backend->log, "RFCOMM receive command before SLC completed: %s", buf); - rfcomm_send_reply(rfcomm, "ERROR"); + rfcomm_send_error(rfcomm, CMEE_AG_FAILURE); return true; + + /* ***** + * Following commands requires a Service Level Connection + * ***** */ + } 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); @@ -836,7 +899,7 @@ if (selected_codec != HFP_AUDIO_CODEC_CVSD && selected_codec != HFP_AUDIO_CODEC_MSBC) { spa_log_warn(backend->log, "unsupported codec negotiation: %d", selected_codec); - rfcomm_send_reply(rfcomm, "ERROR"); + rfcomm_send_error(rfcomm, CMEE_AG_FAILURE); if (was_switching_codec) spa_bt_device_emit_codec_switched(rfcomm->device, -EIO); return true; @@ -854,7 +917,7 @@ if (rfcomm->transport == NULL) { spa_log_warn(backend->log, "can't create transport: %m"); // TODO: We should manage the missing transport - rfcomm_send_reply(rfcomm, "ERROR"); + rfcomm_send_error(rfcomm, CMEE_AG_FAILURE); if (was_switching_codec) spa_bt_device_emit_codec_switched(rfcomm->device, -ENOMEM); return true; @@ -867,10 +930,105 @@ if (was_switching_codec) spa_bt_device_emit_codec_switched(rfcomm->device, 0); } else if (spa_strstartswith(buf, "AT+BIA=")) { - /* We only support 'call' indicator, which HFP 4.35.1 defines as - always active (assuming CMER enabled it), so we don't need to - parse anything here. */ + /* retrieve indicators activation + * form: AT+BIA=indrep1,indrep2,indrepx */ + char *str = buf + 7; + unsigned int ind = 1; + + while (*str && ind < CIND_MAX && *str != '\r' && *str != '\n') { + if (*str == ',') { + ind++; + goto next_indicator; + } + + /* Ignore updates to mandantory indicators which are always ON */ + if (ind == CIND_CALL || ind == CIND_CALLSETUP || ind == CIND_CALLHELD) + goto next_indicator; + + switch (*str) { + case '0': + rfcomm->cind_enabled_indicators &= ~(1 << ind); + break; + case '1': + rfcomm->cind_enabled_indicators |= (1 << ind); + break; + default: + spa_log_warn(backend->log, "Unsupported entry in %s: %c", buf, *str); + } +next_indicator: + str++; + } + + rfcomm_send_reply(rfcomm, "OK"); + } else if (spa_strstartswith(buf, "AT+CLCC")) { + struct spa_list *calls; + struct call *call; + unsigned int type; + + if (backend->modemmanager) { + calls = mm_get_calls(backend->modemmanager); + spa_list_for_each(call, calls, link) { + if (!call->number) { + rfcomm_send_reply(rfcomm, "+CLCC: %u,%u,%u,0,%u", call->index, call->direction, call->state, call->multiparty); + } else { + if (spa_strstartswith(call->number, "+")) + type = INTERNATIONAL_NUMBER; + else + type = NATIONAL_NUMBER; + rfcomm_send_reply(rfcomm, "+CLCC: %u,%u,%u,0,%u,\"%s\",%d", call->index, call->direction, call->state, + call->multiparty, call->number, type); + } + } + } + + rfcomm_send_reply(rfcomm, "OK"); + } else if (sscanf(buf, "AT+CLIP=%u", &value) == 1) { + if (value > 1) { + spa_log_debug(backend->log, "Unsupported AT+CLIP value: %u", value); + rfcomm_send_error(rfcomm, CMEE_AG_FAILURE); + return true; + } + + rfcomm->clip_notify = value; rfcomm_send_reply(rfcomm, "OK"); + } else if (sscanf(buf, "AT+CMEE=%u", &value) == 1) { + if (value > 1) { + spa_log_debug(backend->log, "Unsupported AT+CMEE value: %u", value); + rfcomm_send_error(rfcomm, CMEE_AG_FAILURE); + return true; + } + + rfcomm->extended_error_reporting = value; + rfcomm_send_reply(rfcomm, "OK"); + } else if (spa_strstartswith(buf, "AT+CNUM")) { + if (backend->modem.own_number) { + unsigned int type; + if (spa_strstartswith(backend->modem.own_number, "+")) + type = INTERNATIONAL_NUMBER; + else + type = NATIONAL_NUMBER; + rfcomm_send_reply(rfcomm, "+CNUM: ,\"%s\",%u", backend->modem.own_number, type); + } + rfcomm_send_reply(rfcomm, "OK"); + } else if (spa_strstartswith(buf, "AT+COPS=")) { + unsigned int mode, val; + + if (sscanf(buf, "AT+COPS=%u,%u", &mode, &val) != 2 || + mode != 3 || val != 0) { + rfcomm_send_error(rfcomm, CMEE_AG_FAILURE); + } else { + rfcomm_send_reply(rfcomm, "OK"); + } + } else if (spa_strstartswith(buf, "AT+COPS?")) { + if (!backend->modem.network_has_service) { + rfcomm_send_error(rfcomm, CMEE_NO_NETWORK_SERVICE); + } else { + if (backend->modem.operator_name) + rfcomm_send_reply(rfcomm, "+COPS: 0,0,\"%s\"", backend->modem.operator_name); + else + rfcomm_send_reply(rfcomm, "+COPS: 0,,"); + rfcomm_send_reply(rfcomm, "OK"); + } } else if (sscanf(buf, "AT+VGM=%u", &gain) == 1) { if (gain <= SPA_BT_VOLUME_HS_MAX) { if (!rfcomm->broken_mic_hw_volume) @@ -878,7 +1036,7 @@ rfcomm_send_reply(rfcomm, "OK"); } else { spa_log_debug(backend->log, "RFCOMM receive unsupported VGM gain: %s", buf); - rfcomm_send_reply(rfcomm, "ERROR"); + rfcomm_send_error(rfcomm, CMEE_OPERATION_NOT_ALLOWED); } } else if (sscanf(buf, "AT+VGS=%u", &gain) == 1) { if (gain <= SPA_BT_VOLUME_HS_MAX) { @@ -886,7 +1044,7 @@ rfcomm_send_reply(rfcomm, "OK"); } else { spa_log_debug(backend->log, "RFCOMM receive unsupported VGS gain: %s", buf); - rfcomm_send_reply(rfcomm, "ERROR"); + rfcomm_send_error(rfcomm, CMEE_OPERATION_NOT_ALLOWED); } } else if (spa_strstartswith(buf, "AT+BIND=?")) { rfcomm_send_reply(rfcomm, "+BIND: (2)"); @@ -925,6 +1083,69 @@ } else if (spa_strstartswith(buf, "AT+APLSIRI?")) { // This command is sent when we activate Apple extensions rfcomm_send_reply(rfcomm, "OK"); + } else if (!mm_is_available(backend->modemmanager)) { + spa_log_warn(backend->log, "RFCOMM receive command but modem not available: %s", buf); + rfcomm_send_error(rfcomm, CMEE_NO_CONNECTION_TO_PHONE); + return true; + + /* ***** + * Following commands requires a Service Level Connection + * and acces to a modem + * ***** */ + + } else if (!backend->modem.network_has_service) { + spa_log_warn(backend->log, "RFCOMM receive command but network not available: %s", buf); + rfcomm_send_error(rfcomm, CMEE_NO_NETWORK_SERVICE); + return true; + + /* ***** + * Following commands requires a Service Level Connection, + * acces to a modem and to the network + * ***** */ + + } else if (spa_strstartswith(buf, "ATA")) { + enum cmee_error error; + + if (!mm_answer_call(backend->modemmanager, rfcomm, &error)) { + rfcomm_send_error(rfcomm, error); + return true; + } + } else if (spa_strstartswith(buf, "ATD")) { + char number30; + enum cmee_error error; + + if (sscanf(buf, "ATD%30s;", number) != 1) { + spa_log_debug(backend->log, "Failed to parse ATD: \"%s\"", buf); + rfcomm_send_error(rfcomm, CMEE_AG_FAILURE); + return true; + } + + if (!mm_do_call(backend->modemmanager, number, rfcomm, &error)) { + rfcomm_send_error(rfcomm, error); + return true; + } + } else if (spa_strstartswith(buf, "AT+CHUP")) { + enum cmee_error error; + + if (!mm_hangup_call(backend->modemmanager, rfcomm, &error)) { + rfcomm_send_error(rfcomm, error); + return true; + } + } else if (spa_strstartswith(buf, "AT+VTS=")) { + char *dtmf; + enum cmee_error error; + + dtmf = calloc(1, 2); + if (sscanf(buf, "AT+VTS=%1s", dtmf) != 1) { + spa_log_debug(backend->log, "Failed to parse AT+VTS: \"%s\"", buf); + rfcomm_send_error(rfcomm, CMEE_AG_FAILURE); + return true; + } + + if (!mm_send_dtmf(backend->modemmanager, dtmf, rfcomm, &error)) { + rfcomm_send_error(rfcomm, error); + return true; + } } else { return false; } @@ -935,9 +1156,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* buf) { struct impl *backend = rfcomm->backend; - unsigned int features; - unsigned int gain; - unsigned int selected_codec; + unsigned int features, gain, selected_codec, indicator, value; char* token; while ((token = strsep(&buf, "\r\n"))) { @@ -982,6 +1201,47 @@ } else { spa_log_debug(backend->log, "RFCOMM receive unsupported VGS gain: %s", token); } + } else if (spa_strstartswith(token, "+CIND: (")) { + uint8_t i = 1; + while (strstr(token, "\"")) { + token += strcspn(token, "\"") + 1; + tokenstrcspn(token, "\"") = 0; + rfcomm->hf_indicatorsi = strdup(token); + token += strcspn(token, "\"") + 1; + i++; + if (i == MAX_HF_INDICATORS) { + break; + } + } + } else if (spa_strstartswith(token, "+CIND: ")) { + tokenstrcspn(token, "\r") = 0; + tokenstrcspn(token, "\n") = 0; + token += strlen("+CIND: "); + uint8_t i = 1; + while (strlen(token)) { + if (i >= MAX_HF_INDICATORS || !rfcomm->hf_indicatorsi) { + break; + } + tokenstrcspn(token, ",") = 0; + spa_log_info(backend->log, "AG indicator state: %s = %i", rfcomm->hf_indicatorsi, atoi(token)); + + if (spa_streq(rfcomm->hf_indicatorsi, "battchg")) { + spa_bt_device_report_battery_level(rfcomm->device, atoi(token) * 100 / 5); + } + + token += strcspn(token, "\0") + 1; + i++; + } + } else if (sscanf(token, "+CIEV: %u,%u", &indicator, &value) == 2) { + if (indicator >= MAX_HF_INDICATORS || !rfcomm->hf_indicatorsindicator) { + spa_log_warn(backend->log, "indicator %u has not been registered, ignoring", indicator); + } else { + spa_log_info(backend->log, "AG indicator update: %s = %u", rfcomm->hf_indicatorsindicator, value); + + if (spa_streq(rfcomm->hf_indicatorsindicator, "battchg")) { + spa_bt_device_report_battery_level(rfcomm->device, value * 100 / 5); + } + } } else if (spa_strstartswith(token, "OK")) { switch(rfcomm->hf_state) { case hfp_hf_brsf: @@ -1002,7 +1262,7 @@ rfcomm->hf_state = hfp_hf_cind2; break; case hfp_hf_cind2: - rfcomm_send_cmd(rfcomm, "AT+CMER=3,0,0,0"); + rfcomm_send_cmd(rfcomm, "AT+CMER=3,0,0,1"); rfcomm->hf_state = hfp_hf_cmer; break; case hfp_hf_cmer: @@ -1089,7 +1349,7 @@ if (!res) { spa_log_debug(backend->log, "RFCOMM received unsupported command: %s", buf); - rfcomm_send_reply(rfcomm, "ERROR"); + rfcomm_send_error(rfcomm, CMEE_OPERATION_NOT_SUPPORTED); } } } @@ -1858,6 +2118,9 @@ rfcomm->source.fd = fd; rfcomm->source.mask = SPA_IO_IN; rfcomm->source.rmask = 0; + /* By default all indicators are enabled */ + rfcomm->cind_enabled_indicators = 0xFFFFFFFF; + memset(rfcomm->hf_indicators, 0, sizeof rfcomm->hf_indicators); for (int i = 0; i < SPA_BT_VOLUME_ID_TERM; ++i) { if (rfcomm->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY) @@ -2242,6 +2505,161 @@ return 0; } +static void send_ciev_for_each_rfcomm(struct impl *backend, int indicator, int value) +{ + struct rfcomm *rfcomm; + + spa_list_for_each(rfcomm, &backend->rfcomm_list, link) { + if (rfcomm->slc_configured && + ((indicator == CIND_CALL || indicator == CIND_CALLSETUP || indicator == CIND_CALLHELD) || + (rfcomm->cind_call_notify && (rfcomm->cind_enabled_indicators & (1 << indicator))))) + rfcomm_send_reply(rfcomm, "+CIEV: %d,%d", indicator, value); + } +} + +static void ring_timer_event(void *data, uint64_t expirations) +{ + struct impl *backend = data; + const char *number; + unsigned int type; + struct timespec ts; + const uint64_t timeout = 1 * SPA_NSEC_PER_SEC; + struct rfcomm *rfcomm; + + number = mm_get_incoming_call_number(backend->modemmanager); + if (number) { + if (spa_strstartswith(number, "+")) + type = INTERNATIONAL_NUMBER; + else + type = NATIONAL_NUMBER; + } + + ts.tv_sec = timeout / SPA_NSEC_PER_SEC; + ts.tv_nsec = timeout % SPA_NSEC_PER_SEC; + spa_loop_utils_update_timer(backend->loop_utils, backend->ring_timer, &ts, NULL, false); + + spa_list_for_each(rfcomm, &backend->rfcomm_list, link) { + if (rfcomm->slc_configured) { + rfcomm_send_reply(rfcomm, "RING"); + if (rfcomm->clip_notify && number) + rfcomm_send_reply(rfcomm, "+CLIP: \"%s\",%u", number, type); + } + } +} + +static void set_call_active(bool active, void *user_data) +{ + struct impl *backend = user_data; + + if (backend->modem.active_call != active) { + backend->modem.active_call = active; + send_ciev_for_each_rfcomm(backend, CIND_CALL, active); + } +} + +static void set_call_setup(enum call_setup value, void *user_data) +{ + struct impl *backend = user_data; + enum call_setup old = backend->modem.call_setup; + + if (backend->modem.call_setup != value) { + backend->modem.call_setup = value; + send_ciev_for_each_rfcomm(backend, CIND_CALLSETUP, value); + } + + if (value == CIND_CALLSETUP_INCOMING) { + if (backend->ring_timer == NULL) + backend->ring_timer = spa_loop_utils_add_timer(backend->loop_utils, ring_timer_event, backend); + + if (backend->ring_timer == NULL) { + spa_log_warn(backend->log, "Failed to create ring timer"); + return; + } + + ring_timer_event(backend, 0); + } else if (old == CIND_CALLSETUP_INCOMING) { + spa_loop_utils_update_timer(backend->loop_utils, backend->ring_timer, NULL, NULL, false); + } +} + +void set_battery_level(unsigned int level, void *user_data) +{ + struct impl *backend = user_data; + + if (backend->battery_level != level) { + backend->battery_level = level; + send_ciev_for_each_rfcomm(backend, CIND_BATTERY_LEVEL, level); + } +} + +static void set_modem_operator_name(const char *name, void *user_data) +{ + struct impl *backend = user_data; + + if (backend->modem.operator_name) { + free(backend->modem.operator_name); + backend->modem.operator_name = NULL; + } + + if (name) + backend->modem.operator_name = strdup(name); +} + +static void set_modem_roaming(bool is_roaming, void *user_data) +{ + struct impl *backend = user_data; + + if (backend->modem.network_is_roaming != is_roaming) { + backend->modem.network_is_roaming = is_roaming; + send_ciev_for_each_rfcomm(backend, CIND_ROAM, is_roaming); + } +} + +static void set_modem_own_number(const char *number, void *user_data) +{ + struct impl *backend = user_data; + + if (backend->modem.own_number) { + free(backend->modem.own_number); + backend->modem.own_number = NULL; + } + + if (number) + backend->modem.own_number = strdup(number); +} + +static void set_modem_service(bool available, void *user_data) +{ + struct impl *backend = user_data; + + if (backend->modem.network_has_service != available) { + backend->modem.network_has_service = available; + send_ciev_for_each_rfcomm(backend, CIND_SERVICE, available); + } +} + +static void set_modem_signal_strength(unsigned int strength, void *user_data) +{ + struct impl *backend = user_data; + + if (backend->modem.signal_strength != strength) { + backend->modem.signal_strength = strength; + send_ciev_for_each_rfcomm(backend, CIND_SIGNAL, strength); + } +} + +static void send_cmd_result(bool success, enum cmee_error error, void *user_data) +{ + struct rfcomm *rfcomm = user_data; + + if (success) { + rfcomm_send_reply(rfcomm, "OK"); + return; + } + + rfcomm_send_error(rfcomm, error); +} + static int backend_native_free(void *data) { struct impl *backend = data; @@ -2250,6 +2668,19 @@ sco_close(backend); + if (backend->modemmanager) { + mm_unregister(backend); + backend->modemmanager = NULL; + } + + if (backend->upower) { + upower_unregister(backend->upower); + backend->upower = NULL; + } + + if (backend->ring_timer) + spa_loop_utils_destroy_source(backend->loop_utils, backend->ring_timer); + #ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE dbus_connection_unregister_object_path(backend->conn, PROFILE_HSP_AG); dbus_connection_unregister_object_path(backend->conn, PROFILE_HSP_HS); @@ -2263,6 +2694,8 @@ spa_list_consume(rfcomm, &backend->rfcomm_list, link) rfcomm_free(rfcomm); + if (backend->modem.operator_name) + free(backend->modem.operator_name); free(backend); return 0; @@ -2297,6 +2730,17 @@ .supports_codec = backend_native_supports_codec, }; +static const struct mm_ops mm_ops = { + .send_cmd_result = send_cmd_result, + .set_modem_service = set_modem_service, + .set_modem_signal_strength = set_modem_signal_strength, + .set_modem_operator_name = set_modem_operator_name, + .set_modem_own_number = set_modem_own_number, + .set_modem_roaming = set_modem_roaming, + .set_call_active = set_call_active, + .set_call_setup = set_call_setup, +}; + struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor, void *dbus_connection, const struct spa_dict *info, @@ -2362,6 +2806,9 @@ } #endif + backend->modemmanager = mm_register(backend->log, backend->conn, info, &mm_ops, backend); + backend->upower = upower_register(backend->log, backend->conn, set_battery_level, backend); + return &backend->this; #ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/bap-codec-caps.h -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/bap-codec-caps.h
Changed
@@ -110,4 +110,33 @@ uint8_t n_blks; } __attribute__ ((packed)) bap_lc3_t; +#define BT_ISO_QOS_CIG_UNSET 0xff +#define BT_ISO_QOS_CIS_UNSET 0xff + +#define BT_ISO_QOS_TARGET_LATENCY_LOW 0x01 +#define BT_ISO_QOS_TARGET_LATENCY_BALANCED 0x02 +#define BT_ISO_QOS_TARGET_LATENCY_RELIABILITY 0x03 + +struct bap_endpoint_qos { + uint8_t framing; + uint8_t phy; + uint8_t retransmission; + uint16_t latency; + uint32_t delay_min; + uint32_t delay_max; + uint32_t preferred_delay_min; + uint32_t preferred_delay_max; +}; + +struct bap_codec_qos { + uint32_t interval; + uint8_t framing; + uint8_t phy; + uint16_t sdu; + uint8_t retransmission; + uint16_t latency; + uint32_t delay; + uint8_t target_latency; +}; + #endif
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c
Changed
@@ -501,24 +501,31 @@ return 0; } -static void codec_get_qos(const struct media_codec *codec, - const void *config, size_t config_size, - struct codec_qos *qos) +static int codec_get_qos(const struct media_codec *codec, + const void *config, size_t config_size, + const struct bap_endpoint_qos *endpoint_qos, + struct bap_codec_qos *qos) { bap_lc3_t conf; - memset(qos, 0, sizeof(*qos)); + spa_zero(*qos); if (!parse_conf(&conf, config, config_size)) - return; + return -EINVAL; qos->framing = false; - qos->phy = "2M"; + if (endpoint_qos->phy & 0x2) + qos->phy = 0x2; + else if (endpoint_qos->phy & 0x1) + qos->phy = 0x1; + else + qos->phy = 0x2; 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); + qos->target_latency = BT_ISO_QOS_TARGET_LATENCY_BALANCED; switch (conf.rate) { case LC3_CONFIG_FREQ_8KHZ: @@ -533,6 +540,18 @@ qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 15 : 20); break; } + + /* Clamp to ASE values */ + if (endpoint_qos->latency >= 0x0005 && endpoint_qos->latency <= 0x0FA0) + /* Values outside the range are RFU */ + qos->latency = SPA_MAX(qos->latency, endpoint_qos->latency); + + if (endpoint_qos->delay_min) + qos->delay = SPA_MAX(qos->delay, endpoint_qos->delay_min); + if (endpoint_qos->delay_max) + qos->delay = SPA_MIN(qos->delay, endpoint_qos->delay_max); + + return 0; } static void *codec_init(const struct media_codec *codec, uint32_t flags, @@ -586,7 +605,7 @@ } this->codesize = this->samples * this->channels * sizeof(int32_t); - if (flags & MEDIA_CODEC_FLAG_SINK) { + 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) {
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/bluez5-dbus.c
Changed
@@ -113,6 +113,7 @@ unsigned int filters_added:1; unsigned int objects_listed:1; + DBusPendingCall *get_managed_objects_call; struct spa_bt_backend *backend; struct spa_bt_backend *backendsBACKEND_NUM; @@ -451,10 +452,11 @@ return 0; } -static const struct media_codec *media_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 struct media_codec *preferred) { const char *ep_name; const struct media_codec * const * const media_codecs = monitor->media_codecs; + const struct media_codec *found = NULL; int i; if (spa_strstartswith(endpoint, A2DP_SINK_ENDPOINT "/")) { @@ -478,10 +480,20 @@ 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)) - return codec; + + if (!spa_streq(ep_name, codec_ep_name)) + continue; + if ((*sink && !codec->decode) || (!*sink && !codec->encode)) + continue; + + /* Same endpoint may be shared with multiple codec objects, + * which may e.g. correspond to different encoder settings. + * Look up which one we selected. + */ + if ((preferred && codec == preferred) || found == NULL) + found = codec; } - return NULL; + return found; } static int media_endpoint_to_profile(const char *endpoint) @@ -504,6 +516,30 @@ return spa_dict_lookup(&monitor->enabled_codecs, codec->name) != NULL; } +static bool codec_has_direction(const struct media_codec *codec, enum spa_bt_media_direction direction) +{ + switch (direction) { + case SPA_BT_MEDIA_SOURCE: + return codec->encode; + case SPA_BT_MEDIA_SINK: + return codec->decode; + default: + spa_assert_not_reached(); + } +} + +static bool endpoint_should_be_registered(struct spa_bt_monitor *monitor, + const struct media_codec *codec, + enum spa_bt_media_direction direction) +{ + /* Codecs with fill_caps == NULL share endpoint with another codec, + * and don't have their own endpoint + */ + return is_media_codec_enabled(monitor, codec) && + codec_has_direction(codec, direction) && + codec->fill_caps; +} + static DBusHandlerResult endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) { struct spa_bt_monitor *monitor = userdata; @@ -529,7 +565,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 = media_endpoint_to_codec(monitor, path, &sink); + /* For codecs sharing the same endpoint, BlueZ-initiated connections + * always pick the default one. The session manager will + * switch the codec to a saved value after connection, so this generally + * does not matter. + */ + codec = media_endpoint_to_codec(monitor, path, &sink, NULL); + spa_log_debug(monitor->log, "%p: %s codec:%s", monitor, path, codec ? codec->name : "<null>"); + if (codec != NULL) /* FIXME: We can't determine which device the SelectConfiguration() * call is associated with, therefore device settings are not passed. @@ -557,7 +600,7 @@ DBUS_TYPE_BYTE, &pconf, size, DBUS_TYPE_INVALID)) return DBUS_HANDLER_RESULT_NEED_MEMORY; - exit_send: +exit_send: if (!dbus_connection_send(conn, r, NULL)) return DBUS_HANDLER_RESULT_NEED_MEMORY; @@ -574,12 +617,21 @@ { struct spa_bt_monitor *monitor = userdata; const char *path; - const char *object_path; DBusMessageIter args, props, iter; DBusMessage *r = NULL; - int size, res; + int res; const struct media_codec *codec; bool sink; + const char *err_msg = "Unknown error"; + + const char *endpoint_path = NULL; + uint8_t capsA2DP_MAX_CAPS_SIZE; + uint8_t configA2DP_MAX_CAPS_SIZE; + int caps_size = 0; + DBusMessageIter dict; + struct bap_endpoint_qos endpoint_qos; + + spa_zero(endpoint_qos); 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()"); @@ -592,22 +644,24 @@ path = dbus_message_get_path(m); - codec = media_endpoint_to_codec(monitor, path, &sink); + /* TODO: for codecs with shared endpoint, this currently always picks the default + * one. However, currently we don't have BAP codecs with shared endpoint, so + * this does not matter, but in case they are needed later we should pick the + * right one here. + */ + codec = media_endpoint_to_codec(monitor, path, &sink, NULL); + spa_log_debug(monitor->log, "%p: %s codec:%s", monitor, path, codec ? codec->name : "<null>"); 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; + spa_log_error(monitor->log, "Unsupported codec"); + err_msg = "Unsupported codec"; + goto error; } - /* Read transport properties */ + /* Parse transport properties */ while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; - int var; + int type; dbus_message_iter_recurse(&props, &entry); dbus_message_iter_get_basic(&entry, &key); @@ -615,112 +669,156 @@ dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - var = dbus_message_iter_get_arg_type(&value); + type = 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; - } + DBusMessageIter array; + uint8_t *buf; - 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; + if (type != DBUS_TYPE_ARRAY) { + spa_log_error(monitor->log, "Property %s of wrong type %c", key, (char)type); + goto error_invalid; } 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); + type = dbus_message_iter_get_arg_type(&array); + if (type != DBUS_TYPE_BYTE) { + spa_log_error(monitor->log, "%s is an array of wrong type %c", key, (char)type); + goto error_invalid; } - dbus_message_iter_close_container(&iter, &dict); + dbus_message_iter_get_fixed_array(&array, &buf, &caps_size); + memcpy(caps, buf, caps_size); + + spa_log_info(monitor->log, "%p: %s %s size:%d", monitor, path, key, caps_size); + spa_log_hexdump(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', caps, (size_t)caps_size); } else if (spa_streq(key, "Endpoint")) { - dbus_message_iter_get_basic(&value, &object_path); + if (type != DBUS_TYPE_OBJECT_PATH) { + spa_log_error(monitor->log, "Property %s of wrong type %c", key, (char)type); + goto error_invalid; + } - if (codec->bap) { - struct spa_bt_remote_endpoint *ep; + dbus_message_iter_get_basic(&value, &endpoint_path); - 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; - } + spa_log_info(monitor->log, "%p: %s %s %s", monitor, path, key, endpoint_path); + } else if (type == DBUS_TYPE_BYTE) { + uint8_t v; + dbus_message_iter_get_basic(&value, &v); - /* Call of SelectProperties means that local device acts as an initiator - * and therefor remote endpoint is an acceptor - */ - ep->acceptor = true; - } + spa_log_info(monitor->log, "%p: %s %s 0x%x", monitor, path, key, (unsigned int)v); + + if (spa_streq(key, "Framing")) + endpoint_qos.framing = v; + else if (spa_streq(key, "PHY")) + endpoint_qos.phy = v; + else + spa_log_info(monitor->log, "Unknown property %s", key); + } else if (type == DBUS_TYPE_UINT16) { + dbus_uint16_t v; + dbus_message_iter_get_basic(&value, &v); + + spa_log_info(monitor->log, "%p: %s %s 0x%x", monitor, path, key, (unsigned int)v); + + if (spa_streq(key, "Latency")) + endpoint_qos.latency = v; + else + spa_log_info(monitor->log, "Unknown property %s", key); + } else if (type == DBUS_TYPE_UINT32) { + dbus_uint32_t v; + dbus_message_iter_get_basic(&value, &v); + + spa_log_info(monitor->log, "%p: %s %s 0x%x", monitor, path, key, (unsigned int)v); + + if (spa_streq(key, "MinimumDelay")) + endpoint_qos.delay_min = v; + else if (spa_streq(key, "MaximumDelay")) + endpoint_qos.delay_max = v; + else if (spa_streq(key, "PreferredMinimumDelay")) + endpoint_qos.preferred_delay_min = v; + else if (spa_streq(key, "PreferredMaximumDelay")) + endpoint_qos.preferred_delay_max = v; + else + spa_log_info(monitor->log, "Unknown property %s", key); + } else { + spa_log_info(monitor->log, "Unknown property %s", key); } -next_entry: dbus_message_iter_next(&props); } -exit_send: + if (codec->bap) { + struct spa_bt_remote_endpoint *ep; + + ep = remote_endpoint_find(monitor, endpoint_path); + if (!ep) { + spa_log_warn(monitor->log, "Unable to find remote endpoint for %s", endpoint_path); + goto error_invalid; + } + + /* Call of SelectProperties means that local device acts as an initiator + * and therefor remote endpoint is an acceptor + */ + ep->acceptor = true; + } + + /* TODO: determine which device the SelectConfiguration() call is associated + * with; it's known here based on the remote endpoint. + */ + res = codec->select_config(codec, 0, caps, caps_size, &monitor->default_audio_info, NULL, config); + + if (res < 0 || res != caps_size) { + spa_log_error(monitor->log, "can't select config: %d (%s)", + res, spa_strerror(res)); + goto error_invalid; + } + spa_log_info(monitor->log, "%p: selected conf %d", monitor, caps_size); + spa_log_hexdump(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', (uint8_t *)config, (size_t)caps_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, caps_size); + + if (codec->get_qos) { + struct bap_codec_qos qos; + dbus_bool_t framing; + const char *phy_str; + + spa_zero(qos); + + res = codec->get_qos(codec, config, caps_size, &endpoint_qos, &qos); + if (res < 0) { + spa_log_error(monitor->log, "can't select QOS config: %d (%s)", + res, spa_strerror(res)); + goto error_invalid; + } + + 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); + if (qos.phy == 0x1) + phy_str = "1M"; + else if (qos.phy == 0x2) + phy_str = "2M"; + else + spa_assert_not_reached(); + append_basic_variant_dict_entry(&dict, "PHY", DBUS_TYPE_STRING, "s", &phy_str); + 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); + append_basic_variant_dict_entry(&dict, "TargetLatency", DBUS_TYPE_BYTE, "y", &qos.target_latency); + } + + dbus_message_iter_close_container(&iter, &dict); + if (r) { if (!dbus_connection_send(conn, r, NULL)) return DBUS_HANDLER_RESULT_NEED_MEMORY; @@ -729,6 +827,22 @@ } return DBUS_HANDLER_RESULT_HANDLED; + +error_invalid: + err_msg = "Invalid property"; + goto error; + +error: + if (r) + dbus_message_unref(r); + if ((r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", err_msg)) == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + if (!dbus_connection_send(conn, r, NULL)) { + dbus_message_unref(r); + 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) @@ -756,9 +870,9 @@ char *pos; unsigned int src, i, j, k; - if (strncmp(modalias, "bluetooth:", strlen("bluetooth:")) == 0) + if (spa_strstartswith(modalias, "bluetooth:")) src = SOURCE_ID_BLUETOOTH; - else if (strncmp(modalias, "usb:", strlen("usb:")) == 0) + else if (spa_strstartswith(modalias, "usb:")) src = SOURCE_ID_USB; else return -EINVAL; @@ -882,7 +996,7 @@ else spa_log_debug(monitor->log, "adapter %p: unhandled key %s", adapter, key); - next: +next: dbus_message_iter_next(props_iter); } return 0; @@ -1327,7 +1441,7 @@ static int device_try_connect_profile(struct spa_bt_device *device, - const char *profile_uuid) + const char *profile_uuid) { struct spa_bt_monitor *monitor = device->monitor; DBusMessage *m; @@ -1338,9 +1452,9 @@ /* Call org.bluez.Device1.ConnectProfile() on device, ignoring result */ m = dbus_message_new_method_call(BLUEZ_SERVICE, - device->path, - BLUEZ_DEVICE_INTERFACE, - "ConnectProfile"); + device->path, + BLUEZ_DEVICE_INTERFACE, + "ConnectProfile"); if (m == NULL) return -ENOMEM; dbus_message_append_args(m, DBUS_TYPE_STRING, &profile_uuid, DBUS_TYPE_INVALID); @@ -1423,7 +1537,7 @@ uint64_t exp; 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_warn(monitor->log, "error reading timerfd: %s", strerror(errno)); spa_log_debug(monitor->log, "device %p: timeout %08x %08x", device, device->profiles, device->connected_profiles); @@ -1479,11 +1593,11 @@ spa_log_debug(monitor->log, "device %p: stop timer", device); spa_loop_remove_source(monitor->main_loop, &device->timer); - 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(monitor->main_system, device->timer.fd, 0, &ts, NULL); + 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(monitor->main_system, device->timer.fd, 0, &ts, NULL); spa_system_close(monitor->main_system, device->timer.fd); device->timer.data = NULL; return 0; @@ -1736,7 +1850,7 @@ else spa_log_debug(monitor->log, "device %p: unhandled key %s type %d", device, key, type); - next: +next: dbus_message_iter_next(props_iter); } return 0; @@ -1960,7 +2074,7 @@ else spa_log_debug(monitor->log, "remote_endpoint %p: unhandled key %s", remote_endpoint, key); - next: +next: dbus_message_iter_next(props_iter); } return 0; @@ -2009,8 +2123,8 @@ } struct spa_bt_transport *spa_bt_transport_find_full(struct spa_bt_monitor *monitor, - bool (*callback) (struct spa_bt_transport *t, const void *data), - const void *data) + bool (*callback) (struct spa_bt_transport *t, const void *data), + const void *data) { struct spa_bt_transport *t; @@ -2339,7 +2453,7 @@ uint64_t exp; 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_warn(monitor->log, "error reading timerfd: %s", strerror(errno)); spa_bt_transport_volume_changed(transport); } @@ -2587,7 +2701,7 @@ dbus_message_iter_next(&iter); } } - next: +next: dbus_message_iter_next(props_iter); } return 0; @@ -2959,6 +3073,9 @@ for (i = 0; i < config_size; i++) spa_log_debug(sw->device->monitor->log, "media codec switch %p: %d: %02x", sw, i, configi); + /* Codecs may share the same endpoint, so indicate which one we are using */ + sw->device->preferred_codec = codec; + /* 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) { @@ -3073,7 +3190,7 @@ uint64_t exp; 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_warn(monitor->log, "error reading timerfd: %s", strerror(errno)); spa_log_debug(monitor->log, "media codec switch %p: rate limit timer event", sw); @@ -3365,7 +3482,7 @@ endpoint = dbus_message_get_path(m); profile = media_endpoint_to_profile(endpoint); - codec = media_endpoint_to_codec(monitor, endpoint, &sink); + codec = media_endpoint_to_codec(monitor, endpoint, &sink, NULL); if (codec == NULL) { spa_log_warn(monitor->log, "unknown SetConfiguration() codec"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -3414,6 +3531,12 @@ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } + /* If multiple codecs share the endpoint, pick the one we wanted */ + transport->media_codec = codec = media_endpoint_to_codec(monitor, endpoint, &sink, + transport->device->preferred_codec); + spa_assert(codec != NULL); + spa_log_debug(monitor->log, "%p: %s codec:%s", monitor, path, codec ? codec->name : "<null>"); + spa_bt_device_update_last_bluez_action_time(transport->device); if (profile & SPA_BT_PROFILE_A2DP_SOURCE) { @@ -3506,8 +3629,9 @@ { DBusMessage *r; - r = dbus_message_new_error(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE ".Error.NotImplemented", - "Method not implemented"); + r = dbus_message_new_error(m, + BLUEZ_MEDIA_ENDPOINT_INTERFACE ".Error.NotImplemented", + "Method not implemented"); if (r == NULL) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (!dbus_connection_send(conn, r, NULL)) @@ -3566,6 +3690,8 @@ DBusMessage *r; r = dbus_pending_call_steal_reply(pending); + dbus_pending_call_unref(pending); + if (r == NULL) return; @@ -3579,9 +3705,8 @@ goto finish; } - finish: +finish: dbus_message_unref(r); - dbus_pending_call_unref(pending); } static void append_basic_variant_dict_entry(DBusMessageIter *dict, const char* key, int variant_type_int, const char* variant_type_str, void* variant) { @@ -3609,8 +3734,9 @@ } static int bluez_register_endpoint(struct spa_bt_monitor *monitor, - const char *path, enum spa_bt_media_direction direction, - const char *uuid, const struct media_codec *codec) { + 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; @@ -3620,6 +3746,8 @@ uint16_t codec_id = codec->codec_id; bool sink = (direction == SPA_BT_MEDIA_SINK); + spa_assert(codec->fill_caps); + ret = media_codec_to_endpoint(codec, direction, &object_path); if (ret < 0) goto error; @@ -3661,33 +3789,6 @@ return ret; } -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; - const DBusObjectPathVTable vtable_endpoint = { - .message_function = endpoint_handler, - }; - - ret = media_codec_to_endpoint(codec, direction, &object_path); - if (ret < 0) - return ret; - - spa_log_info(monitor->log, "Registering endpoint: %s", object_path); - - if (!dbus_connection_register_object_path(monitor->conn, - object_path, - &vtable_endpoint, monitor)) { - free(object_path); - return -EIO; - } - - free(object_path); - return 0; - -} - static int adapter_register_endpoints(struct spa_bt_adapter *a) { struct spa_bt_monitor *monitor = a->monitor; @@ -3704,29 +3805,31 @@ * let's incentivize users to upgrade their bluez5 daemon * 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."); + spa_log_warn(monitor->log, + "Using legacy bluez5 API for A2DP - only SBC will be supported. " + "Please upgrade bluez5."); for (i = 0; media_codecsi; i++) { const struct media_codec *codec = media_codecsi; - if (!is_media_codec_enabled(monitor, codec)) + if (codec->id != SPA_BLUETOOTH_AUDIO_CODEC_SBC) continue; - if (!(codec->codec_id == A2DP_CODEC_SBC && spa_streq(codec->name, "sbc"))) - continue; + if (endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SOURCE)) { + if ((err = bluez_register_endpoint(monitor, a->path, + SPA_BT_MEDIA_SOURCE, + SPA_BT_UUID_A2DP_SOURCE, + codec))) + goto out; + } - if ((err = bluez_register_endpoint(monitor, a->path, - SPA_BT_MEDIA_SOURCE, - SPA_BT_UUID_A2DP_SOURCE, - codec))) - goto out; - - if ((err = bluez_register_endpoint(monitor, a->path, - SPA_BT_MEDIA_SINK, - SPA_BT_UUID_A2DP_SINK, - codec))) - goto out; + if (endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SINK)) { + if ((err = bluez_register_endpoint(monitor, a->path, + SPA_BT_MEDIA_SINK, + SPA_BT_UUID_A2DP_SINK, + codec))) + goto out; + } a->endpoints_registered = true; break; @@ -3738,7 +3841,7 @@ err = -ENOSYS; } - out: +out: if (err) { spa_log_error(monitor->log, "Failed to register bluez5 endpoints"); } @@ -3822,10 +3925,6 @@ 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 @@ -3838,7 +3937,7 @@ continue; } - if (codec->decode != NULL) { + if (endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SINK)) { caps_size = codec->fill_caps(codec, MEDIA_CODEC_FLAG_SINK, caps); if (caps_size < 0) continue; @@ -3853,7 +3952,7 @@ } } - if (codec->encode != NULL) { + if (endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SOURCE)) { caps_size = codec->fill_caps(codec, 0, caps); if (caps_size < 0) continue; @@ -3888,6 +3987,8 @@ bool fallback = true; r = dbus_pending_call_steal_reply(pending); + dbus_pending_call_unref(pending); + if (r == NULL) return; @@ -3907,12 +4008,40 @@ finish: dbus_message_unref(r); - dbus_pending_call_unref(pending); if (fallback) adapter_register_endpoints(adapter); } +static int register_media_endpoint(struct spa_bt_monitor *monitor, + const struct media_codec *codec, + enum spa_bt_media_direction direction) +{ + static const DBusObjectPathVTable vtable_endpoint = { + .message_function = endpoint_handler, + }; + + if (!endpoint_should_be_registered(monitor, codec, direction)) + return 0; + + char *object_path = NULL; + int ret = media_codec_to_endpoint(codec, direction, &object_path); + if (ret < 0) + return ret; + + spa_log_info(monitor->log, "registering endpoint: %s", object_path); + + if (!dbus_connection_register_object_path(monitor->conn, + object_path, + &vtable_endpoint, monitor)) + { + ret = -EIO; + } + + free(object_path); + return ret; +} + static int register_media_application(struct spa_bt_monitor * monitor) { const struct media_codec * const * const media_codecs = monitor->media_codecs; @@ -3930,41 +4059,42 @@ for (int i = 0; media_codecsi; i++) { const struct media_codec *codec = media_codecsi; - if (!is_media_codec_enabled(monitor, codec)) - continue; - - if (codec->encode != NULL) - register_media_endpoint(monitor, codec, SPA_BT_MEDIA_SOURCE); - if (codec->decode != NULL) - register_media_endpoint(monitor, codec, SPA_BT_MEDIA_SINK); + register_media_endpoint(monitor, codec, SPA_BT_MEDIA_SOURCE); + register_media_endpoint(monitor, codec, SPA_BT_MEDIA_SINK); } return 0; } +static void unregister_media_endpoint(struct spa_bt_monitor *monitor, + const struct media_codec *codec, + enum spa_bt_media_direction direction) +{ + if (!endpoint_should_be_registered(monitor, codec, direction)) + return; + + char *object_path = NULL; + int ret = media_codec_to_endpoint(codec, direction, &object_path); + if (ret < 0) + return; + + spa_log_info(monitor->log, "unregistering endpoint: %s", object_path); + + if (!dbus_connection_unregister_object_path(monitor->conn, object_path)) + spa_log_warn(monitor->log, "failed to unregister %s\n", object_path); + + free(object_path); +} + static void unregister_media_application(struct spa_bt_monitor * monitor) { const struct media_codec * const * const media_codecs = monitor->media_codecs; - int ret; - char *object_path = NULL; for (int i = 0; media_codecsi; i++) { const struct media_codec *codec = media_codecsi; - if (!is_media_codec_enabled(monitor, codec)) - continue; - - 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 = 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); - } + unregister_media_endpoint(monitor, codec, SPA_BT_MEDIA_SOURCE); + unregister_media_endpoint(monitor, codec, SPA_BT_MEDIA_SINK); } dbus_connection_unregister_object_path(monitor->conn, MEDIA_OBJECT_MANAGER_PATH); @@ -4103,7 +4233,7 @@ else spa_log_debug(monitor->log, "media: unhandled key %s", key); - next: +next: dbus_message_iter_next(props_iter); } return 0; @@ -4259,7 +4389,12 @@ DBusMessage *r; DBusMessageIter it6; + spa_assert(pending == monitor->get_managed_objects_call); + monitor->get_managed_objects_call = NULL; + r = dbus_pending_call_steal_reply(pending); + dbus_pending_call_unref(pending); + if (r == NULL) return; @@ -4294,14 +4429,16 @@ monitor->objects_listed = true; - finish: +finish: dbus_message_unref(r); - dbus_pending_call_unref(pending); return; } static void get_managed_objects(struct spa_bt_monitor *monitor) { + if (monitor->objects_listed || monitor->get_managed_objects_call) + return; + DBusMessage *m; DBusPendingCall *call; @@ -4310,74 +4447,13 @@ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - dbus_connection_send_with_reply(monitor->conn, m, &call, -1); - dbus_pending_call_set_notify(call, get_managed_objects_reply, monitor, NULL); - dbus_message_unref(m); -} - -static void check_name_owner_reply(DBusPendingCall *pending, void *user_data) -{ - struct spa_bt_monitor *monitor = user_data; - DBusMessage *r; - DBusError error; - bool running; - - r = dbus_pending_call_steal_reply(pending); - if (r == NULL) - return; - - if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) { - spa_log_warn(monitor->log, "BlueZ D-Bus ObjectManager not available"); - goto finish; - } - if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { - spa_log_error(monitor->log, "NameHasOwner() failed: %s", - dbus_message_get_error_name(r)); - goto finish; - } - if (!spa_streq(dbus_message_get_signature(r), "b")) { - spa_log_error(monitor->log, "Invalid reply signature for NameHasOwner()"); - goto finish; - } - - dbus_error_init(&error); - dbus_message_get_args(r, &error, DBUS_TYPE_BOOLEAN, &running, DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&error)) { - spa_log_error(monitor->log, "Could not check bluetooth service: %s", error.message); - dbus_error_free(&error); - goto finish; - } - - spa_log_info(monitor->log, "bluetooth service running: %s", - running ? "yes" : "no"); - if (running) - get_managed_objects(monitor); - -finish: - dbus_message_unref(r); - dbus_pending_call_unref(pending); - return; -} - -static void check_name_owner(struct spa_bt_monitor *monitor) -{ - DBusMessage *m; - DBusPendingCall *call; - const char *service = BLUEZ_SERVICE; - - m = dbus_message_new_method_call("org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "NameHasOwner"); - if (m == NULL) - return; - - dbus_message_append_args(m, DBUS_TYPE_STRING, &service, DBUS_TYPE_INVALID); + dbus_message_set_auto_start(m, false); dbus_connection_send_with_reply(monitor->conn, m, &call, -1); - dbus_pending_call_set_notify(call, check_name_owner_reply, monitor, NULL); + dbus_pending_call_set_notify(call, get_managed_objects_reply, monitor, NULL); dbus_message_unref(m); + + monitor->get_managed_objects_call = call; } static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *user_data) @@ -4638,7 +4714,13 @@ spa_hook_list_isolate(&this->hooks, &save, listener, events, data); add_filters(this); - check_name_owner(this); + get_managed_objects(this); + + struct spa_bt_device *device; + spa_list_for_each(device, &this->device_list, link) { + if (device->added) + emit_device_info(this, device, this->connection_info_supported); + } spa_hook_list_join(&this->hooks, &save); @@ -4692,6 +4774,11 @@ monitor->filters_added = false; } + if (monitor->get_managed_objects_call) { + dbus_pending_call_cancel(monitor->get_managed_objects_call); + dbus_pending_call_unref(monitor->get_managed_objects_call); + } + spa_list_consume(t, &monitor->transport_list, link) spa_bt_transport_free(t); spa_list_consume(ep, &monitor->remote_endpoint_list, link)
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/bluez5-device.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/bluez5-device.c
Changed
@@ -74,6 +74,7 @@ DEVICE_PROFILE_A2DP = 2, DEVICE_PROFILE_HSP_HFP = 3, DEVICE_PROFILE_BAP = 4, + DEVICE_PROFILE_LAST = DEVICE_PROFILE_BAP, }; struct props { @@ -1092,18 +1093,18 @@ *codec = 0; *next = index + 1; - if (index <= 3) { + if (index <= DEVICE_PROFILE_LAST) { return index; } else if (index != SPA_ID_INVALID) { const struct spa_type_info *info; uint32_t profile; - *codec = index - 3; + *codec = index - DEVICE_PROFILE_LAST; *next = SPA_ID_INVALID; for (info = spa_type_bluetooth_audio_codec; info->type; ++info) if (info->type > *codec) - *next = SPA_MIN(info->type + 3, *next); + *next = SPA_MIN(info->type + DEVICE_PROFILE_LAST, *next); if (get_hfp_codec(*codec)) profile = DEVICE_PROFILE_HSP_HFP; @@ -1128,14 +1129,14 @@ if (codec == 0 || (this->bt_dev->connected_profiles & SPA_BT_PROFILE_MEDIA_SOURCE)) return profile; - return codec + 3; + return codec + DEVICE_PROFILE_LAST; } if (profile == DEVICE_PROFILE_HSP_HFP) { if (codec == 0 || (this->bt_dev->connected_profiles & SPA_BT_PROFILE_HFP_AG)) return profile; - return codec + 3; + return codec + DEVICE_PROFILE_LAST; } return SPA_ID_INVALID;
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/codec-loader.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/codec-loader.c
Changed
@@ -126,8 +126,12 @@ for (i = 0; bluez5_codec_a2dp->codecsi; ++i) { const struct media_codec *c = bluez5_codec_a2dp->codecsi; + const char *ep = c->endpoint_name ? c->endpoint_name : c->name; size_t j; + if (!ep) + goto next_codec; + if (impl->n_codecs >= MAX_CODECS) { spa_log_error(impl->log, "too many A2DP codecs"); break; @@ -136,13 +140,16 @@ /* Don't load duplicate endpoints */ for (j = 0; j < impl->n_codecs; ++j) { 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)) + if (spa_streq(ep, ep2) && c->fill_caps && c2->fill_caps) { + spa_log_debug(impl->log, "media codec %s from %s duplicate endpoint %s", + c->name, factory_name, ep); goto next_codec; + } } - spa_log_debug(impl->log, "loaded media codec %s from %s", c->name, factory_name); + spa_log_debug(impl->log, "loaded media codec %s from %s, endpoint:%s", + c->name, factory_name, ep); if (c->set_log) c->set_log(impl->log);
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/defs.h
Changed
@@ -459,6 +459,8 @@ void (*destroy) (void *data); }; +struct media_codec; + struct spa_bt_device { struct spa_list link; struct spa_bt_monitor *monitor; @@ -507,9 +509,9 @@ const struct spa_dict *settings; DBusPendingCall *battery_pending_call; -}; -struct media_codec; + const struct media_codec *preferred_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);
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/media-codecs.h -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/media-codecs.h
Changed
@@ -36,6 +36,7 @@ #include <spa/support/log.h> #include "a2dp-codec-caps.h" +#include "bap-codec-caps.h" /* * The codec plugin SPA interface is private. The version should be incremented @@ -44,7 +45,7 @@ #define SPA_TYPE_INTERFACE_Bluez5CodecMedia SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:Media:Private" -#define SPA_VERSION_BLUEZ5_CODEC_MEDIA 5 +#define SPA_VERSION_BLUEZ5_CODEC_MEDIA 7 struct spa_bluez5_codec_a2dp { struct spa_interface iface; @@ -79,16 +80,6 @@ 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; @@ -107,8 +98,10 @@ struct spa_log *log; + /** If fill_caps is NULL, no endpoint is registered (for sharing with another codec). */ 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, @@ -119,9 +112,10 @@ 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, + int (*get_qos)(const struct media_codec *codec, const void *config, size_t config_size, - struct codec_qos *qos); + const struct bap_endpoint_qos *endpoint_qos, + struct bap_codec_qos *qos); /** qsort comparison sorting caps in order of preference for the codec. * Used in codec switching to select best remote endpoints.
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/media-sink.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/media-sink.c
Changed
@@ -62,17 +62,14 @@ #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 FILL_FRAMES 4 +#define MIN_BUFFERS 2 #define MAX_BUFFERS 32 -#define MIN_LATENCY 128 -#define MAX_LATENCY 8192 -#define BUFFER_SIZE (MAX_LATENCY*8) +#define BUFFER_SIZE (8192*8) struct buffer { uint32_t id; @@ -121,6 +118,8 @@ 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 @@ -137,6 +136,7 @@ unsigned int started:1; unsigned int following:1; unsigned int is_output:1; + unsigned int flush_pending:1; unsigned int is_duplex:1; @@ -152,6 +152,10 @@ uint64_t current_time; uint64_t next_time; uint64_t last_error; + uint64_t process_time; + + uint64_t prev_flush_time; + uint64_t next_flush_time; const struct media_codec *codec; bool codec_props_changed; @@ -161,30 +165,23 @@ 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; + uint32_t block_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) +#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)); } @@ -214,33 +211,17 @@ 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; + index_offset = 1; } break; } @@ -252,8 +233,6 @@ 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: @@ -391,8 +370,6 @@ } 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)); } @@ -444,7 +421,7 @@ this->codec_props_changed = false; } this->need_flush = 0; - this->frame_count = 0; + this->block_count = 0; this->fragment = false; this->buffer_used = this->codec->start_encode(this->codec_data, this->buffer, sizeof(this->buffer), @@ -469,20 +446,32 @@ 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 (SPA_UNLIKELY(spa_log_level_topic_enabled(this->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_TRACE))) { + struct timespec ts; + uint64_t now; + uint64_t dt; + + spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &ts); + now = SPA_TIMESPEC_TO_NSEC(&ts); + dt = now - this->prev_flush_time; + this->prev_flush_time = now; + + spa_log_trace(this->log, + "%p: send blocks:%d block:%u seq:%u ts:%u size:%u " + "wrote:%d dt:%"PRIu64, + this, this->block_count, this->block_size, this->seqnum, + this->timestamp, this->buffer_used, written, dt); + } if (written < 0) { spa_log_debug(this->log, "%p: %m", this); @@ -502,7 +491,7 @@ 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); + this->block_count); if (this->need_flush) return 0; @@ -530,7 +519,7 @@ return processed; this->sample_count += processed / port->frame_size; - this->frame_count += processed / this->block_size; + this->block_count += processed / this->block_size; this->buffer_used += out_encoded; spa_log_trace(this->log, "%p: processed %d %zd used %d", @@ -551,7 +540,7 @@ 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); + this->block_count); if (this->need_flush) return 0; @@ -602,46 +591,41 @@ return total; } -static void enable_flush(struct impl *this, bool enabled, uint64_t timeout) +static void enable_flush_timer(struct impl *this, bool enabled) { - 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; + this->next_flush_time = 0; - ts.it_value.tv_sec = timeout / SPA_NSEC_PER_SEC; - ts.it_value.tv_nsec = timeout % SPA_NSEC_PER_SEC; + ts.it_value.tv_sec = this->next_flush_time / SPA_NSEC_PER_SEC; + ts.it_value.tv_nsec = this->next_flush_time % 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); + this->flush_timerfd, SPA_FD_TIMER_ABSTIME, &ts, NULL); + + this->flush_pending = enabled; } -static uint64_t get_next_bap_timeout(struct impl *this) +static uint32_t get_queued_frames(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; + uint32_t bytes = 0; + struct buffer *b; - 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; + spa_list_for_each(b, &port->ready, link) { + struct spa_data *d = b->buf->datas; - 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; + bytes += d0.chunk->size; + } - return next_time; + if (bytes > port->ready_offset) + bytes -= port->ready_offset; + else + bytes = 0; + + return bytes / port->frame_size; } static int flush_data(struct impl *this, uint64_t now_time) @@ -649,6 +633,7 @@ int written; uint32_t total_frames; struct port *port = &this->port; + int unused_buffer; if (!this->flush_source.loop) { /* I/O in error state */ @@ -720,23 +705,35 @@ 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); + enable_flush_timer(this, false); + return 0; + } + + if (this->flush_pending) { + spa_log_trace(this->log, "%p: wait for flush timer", this); return 0; } + /* + * Get socket queue size before writing to it. + * This should be the same as buffer size to increase bitpool + * Bitpool shouldn't be increased when data is left over in the buffer + */ + unused_buffer = get_transport_unused_size(this); + 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); + int res = this->codec->reduce_bitpool(this->codec_data); + + spa_log_debug(this->log, "%p: reduce bitpool: %i", this, res); this->last_error = now_time; } @@ -746,109 +743,96 @@ * 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); + enable_flush_timer(this, false); return written; } else if (written > 0) { - if (this->codec->bap) { - uint64_t timeout = get_next_bap_timeout(this); + /* + * We cannot write all data we have at once, since this can exceed device + * buffers (esp. for the A2DP low-latency codecs) and socket buffers, so + * flush needs to be delayed. + */ + uint32_t packet_samples = this->block_count * this->block_size + / port->frame_size; + uint64_t packet_time = (uint64_t)packet_samples * SPA_NSEC_PER_SEC + / port->current_format.info.raw.rate; + + if (SPA_LIKELY(this->position)) { + uint32_t frames = get_queued_frames(this); + uint64_t duration_ns; - 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; - } + * Flush at the time position of the next buffered sample. + */ + duration_ns = ((uint64_t)this->position->clock.duration * SPA_NSEC_PER_SEC + / this->position->clock.rate.denom); + this->next_flush_time = this->process_time + duration_ns + - ((uint64_t)frames * SPA_NSEC_PER_SEC + / port->current_format.info.raw.rate); + /* + * We could delay the output by one packet to avoid waiting + * for the next buffer and so make send intervals exactly regular. + * However, this is not needed for A2DP or BAP. The controller + * will do the scheduling for us, and there's also the socket buffer + * in between. + */ +#if 0 + this->next_flush_time += SPA_MIN(packet_time, + duration_ns * (port->n_buffers - 1)); +#endif + } else { + if (this->next_flush_time == 0) + this->next_flush_time = this->process_time; + this->next_flush_time += packet_time; + } + + if (this->need_flush == NEED_FLUSH_FRAGMENT) { 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); + this->fragment = true; + goto again; + } + + if (now_time - this->last_error > SPA_NSEC_PER_SEC) { + if (unused_buffer == (int)this->fd_buffer_size) { + int res = this->codec->increase_bitpool(this->codec_data); + + spa_log_debug(this->log, "%p: increase bitpool: %i", this, res); } + this->last_error = now_time; } + + spa_log_trace(this->log, "%p: flush at:%"PRIu64" process:%"PRIu64, this, + this->next_flush_time, this->process_time); + reset_buffer(this); + enable_flush_timer(this, true); } 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); + enable_flush_timer(this, false); } return 0; } -static void media_on_flush(struct spa_source *source) +static void media_on_flush_error(struct spa_source *source) { struct impl *this = source->data; - spa_log_trace(this->log, "%p: flushing", this); + spa_log_trace(this->log, "%p: flush event", this); - if (!SPA_FLAG_IS_SET(source->rmask, SPA_IO_OUT)) { + if (source->rmask & (SPA_IO_ERR | SPA_IO_HUP)) { 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) @@ -862,11 +846,14 @@ spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); if (this->transport == NULL) { - enable_flush(this, false, 0); + enable_flush_timer(this, false); return; } - flush_data(this, this->current_time); + while (exp-- > 0) { + this->flush_pending = false; + flush_data(this, this->current_time); + } } static void media_on_timeout(struct spa_source *source) @@ -953,10 +940,7 @@ 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; + flags = this->is_duplex ? MEDIA_CODEC_FLAG_SINK : 0; this->codec_data = this->codec->init(this->codec, flags, @@ -969,8 +953,8 @@ 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->codec->bap ? "BAP" : "A2DP", this->codec->description, + (int64_t)(spa_bt_transport_get_delay_nsec(this->transport) / SPA_NSEC_PER_MSEC)); this->seqnum = 0; @@ -1025,11 +1009,13 @@ 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.func = media_on_flush_error; + this->flush_source.mask = SPA_IO_ERR | SPA_IO_HUP; this->flush_source.rmask = 0; spa_loop_add_source(this->data_loop, &this->flush_source); + this->flush_pending = false; + set_timers(this); this->started = true; @@ -1046,9 +1032,6 @@ 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; @@ -1078,7 +1061,7 @@ if (!this->started) return 0; - spa_log_trace(this->log, "%p: stop", this); + spa_log_trace(this->log, "%p: stop", this); spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, this); @@ -1275,11 +1258,14 @@ 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_buffers, SPA_POD_CHOICE_RANGE_Int( + MIN_BUFFERS, + MIN_BUFFERS, + 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, + 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; @@ -1368,6 +1354,11 @@ if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) return -EINVAL; + if (info.info.raw.rate == 0 || + info.info.raw.channels == 0 || + info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + return -EINVAL; + port->frame_size = info.info.raw.channels; switch (info.info.raw.format) { case SPA_AUDIO_FORMAT_S16: @@ -1539,18 +1530,21 @@ 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->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); + } + + this->process_time = this->current_time; + + if (!spa_list_is_empty(&port->ready)) { + spa_log_trace(this->log, "%p: flush on process", this); flush_data(this, this->current_time); } @@ -1663,8 +1657,7 @@ { struct impl *this = (struct impl *) handle; - if (this->codec_data) - this->codec->deinit(this->codec_data); + do_stop(this); if (this->codec_props && this->codec->clear_props) this->codec->clear_props(this->codec_props); if (this->transport) @@ -1754,6 +1747,11 @@ spa_list_init(&port->ready); + this->quantum_limit = 8192; + + if (info && (str = spa_dict_lookup(info, "clock.quantum-limit"))) + spa_atou32(str, &this->quantum_limit, 0); + if (info && (str = spa_dict_lookup(info, "api.bluez5.a2dp-duplex")) != NULL) this->is_duplex = spa_atob(str);
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/media-source.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/media-source.c
Changed
@@ -276,6 +276,7 @@ struct impl *this = user_data; struct port *port = &this->port; + set_timers(this); spa_bt_decode_buffer_recover(&port->buffer); return 0; } @@ -567,13 +568,14 @@ return 0; } +static int produce_buffer(struct impl *this); + 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) @@ -608,8 +610,11 @@ this->clock->next_nsec = this->next_time; } - spa_log_trace(this->log, "%p: %d", this, io->status); - io->status = SPA_STATUS_HAVE_DATA; + if (port->io) { + int status = produce_buffer(this); + spa_log_trace(this->log, "%p: io:%d status:%d", this, port->io->status, status); + } + spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA); set_timeout(this, this->next_time); @@ -631,10 +636,7 @@ this->transport_acquired = true; - if (this->codec->bap) - flags = 0; - else - flags = this->is_duplex ? 0 : MEDIA_CODEC_FLAG_SINK; + flags = this->is_duplex ? 0 : MEDIA_CODEC_FLAG_SINK; this->codec_data = this->codec->init(this->codec, flags, @@ -1099,6 +1101,11 @@ if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) return -EINVAL; + if (info.info.raw.rate == 0 || + info.info.raw.channels == 0 || + info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + return -EINVAL; + port->frame_size = info.info.raw.channels; switch (info.info.raw.format) { @@ -1342,21 +1349,15 @@ } } -static int impl_node_process(void *object) +static int produce_buffer(struct impl *this) { - struct impl *this = object; - struct port *port; - struct spa_io_buffers *io; struct buffer *buffer; + struct port *port = &this->port; + struct spa_io_buffers *io = port->io; - spa_return_val_if_fail(this != NULL, -EINVAL); - - port = &this->port; - if ((io = port->io) == NULL) + if (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; @@ -1367,7 +1368,7 @@ io->buffer_id = SPA_ID_INVALID; } - /* Handle buffering delay */ + /* Handle buffering */ process_buffering(this); /* Return if there are no buffers ready to be processed */ @@ -1387,6 +1388,37 @@ return SPA_STATUS_HAVE_DATA; } +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; + + 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; + } + + /* Follower produces buffers here, driver in timeout */ + if (this->following) + return produce_buffer(this); + else + return SPA_STATUS_OK; +} + static const struct spa_node_methods impl_node = { SPA_VERSION_NODE_METHODS, .add_listener = impl_node_add_listener, @@ -1452,8 +1484,8 @@ { struct impl *this = (struct impl *) handle; struct port *port = &this->port; - if (this->codec_data) - this->codec->deinit(this->codec_data); + + do_stop(this); if (this->codec_props && this->codec->clear_props) this->codec->clear_props(this->codec_props); if (this->transport)
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/meson.build -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/meson.build
Changed
@@ -10,6 +10,7 @@ get_option('bluez5-backend-hfp-native').allowed()) cdata.set('HAVE_BLUEZ_5_BACKEND_HSP_NATIVE', get_option('bluez5-backend-hsp-native').allowed()) cdata.set('HAVE_BLUEZ_5_BACKEND_HFP_NATIVE', get_option('bluez5-backend-hfp-native').allowed()) +cdata.set('HAVE_BLUEZ_5_BACKEND_NATIVE_MM', get_option('bluez5-backend-native-mm').allowed()) cdata.set('HAVE_BLUEZ_5_BACKEND_OFONO', get_option('bluez5-backend-ofono').allowed()) cdata.set('HAVE_BLUEZ_5_BACKEND_HSPHFPD', get_option('bluez5-backend-hsphfpd').allowed()) cdata.set('HAVE_BLUEZ_5_HCI', dependency('bluez', version: '< 6', required: false).found()) @@ -38,7 +39,11 @@ if libusb_dep.found() bluez5_deps += libusb_dep endif - bluez5_sources += 'backend-native.c' + if mm_dep.found() + bluez5_deps += mm_dep + bluez5_sources += 'modemmanager.c' + endif + bluez5_sources += 'backend-native.c', 'upower.c' endif if get_option('bluez5-backend-ofono').allowed()
View file
pipewire-0.3.60.tar.gz/spa/plugins/bluez5/modemmanager.c
Added
@@ -0,0 +1,1259 @@ +/* Spa Bluez5 ModemManager proxy + * + * 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 <errno.h> +#include <spa/utils/string.h> + +#include <ModemManager.h> + +#include "modemmanager.h" + +#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager" + +struct modem { + char *path; + bool network_has_service; + unsigned int signal_strength; +}; + +struct impl { + struct spa_bt_monitor *monitor; + + struct spa_log *log; + DBusConnection *conn; + + char *allowed_modem_device; + bool filters_added; + DBusPendingCall *pending; + DBusPendingCall *voice_pending; + + const struct mm_ops *ops; + void *user_data; + + struct modem modem; + struct spa_list call_list; +}; + +struct dbus_cmd_data { + struct impl *this; + struct call *call; + void *user_data; +}; + +static bool mm_dbus_connection_send_with_reply(struct impl *this, DBusMessage *m, DBusPendingCall **pending_return, + DBusPendingCallNotifyFunction function, void *user_data) +{ + dbus_bool_t dbus_ret; + + spa_assert(*pending_return == NULL); + + dbus_ret = dbus_connection_send_with_reply(this->conn, m, pending_return, -1); + if (!dbus_ret || *pending_return == NULL) { + spa_log_debug(this->log, "dbus call failure"); + return false; + } + + dbus_ret = dbus_pending_call_set_notify(*pending_return, function, user_data, NULL); + if (!dbus_ret) { + spa_log_debug(this->log, "dbus set notify failure"); + dbus_pending_call_cancel(*pending_return); + dbus_pending_call_unref(*pending_return); + *pending_return = NULL; + return false; + } + + return true; +} + +static int mm_state_to_clcc(struct impl *this, MMCallState state) +{ + switch (state) { + case MM_CALL_STATE_DIALING: + return CLCC_DIALING; + case MM_CALL_STATE_RINGING_OUT: + return CLCC_ALERTING; + case MM_CALL_STATE_RINGING_IN: + return CLCC_INCOMING; + case MM_CALL_STATE_ACTIVE: + return CLCC_ACTIVE; + case MM_CALL_STATE_HELD: + return CLCC_HELD; + case MM_CALL_STATE_WAITING: + return CLCC_WAITING; + case MM_CALL_STATE_TERMINATED: + case MM_CALL_STATE_UNKNOWN: + default: + return -1; + } +} + +static void mm_call_state_changed(struct impl *this) +{ + struct call *call; + bool call_indicator = false; + enum call_setup call_setup_indicator = CIND_CALLSETUP_NONE; + + spa_list_for_each(call, &this->call_list, link) { + call_indicator |= (call->state == CLCC_ACTIVE); + + if (call->state == CLCC_INCOMING && call_setup_indicator < CIND_CALLSETUP_INCOMING) + call_setup_indicator = CIND_CALLSETUP_INCOMING; + else if (call->state == CLCC_DIALING && call_setup_indicator < CIND_CALLSETUP_DIALING) + call_setup_indicator = CIND_CALLSETUP_DIALING; + else if (call->state == CLCC_ALERTING && call_setup_indicator < CIND_CALLSETUP_ALERTING) + call_setup_indicator = CIND_CALLSETUP_ALERTING; + } + + if (this->ops->set_call_active) + this->ops->set_call_active(call_indicator, this->user_data); + + if (this->ops->set_call_setup) + this->ops->set_call_setup(call_setup_indicator, this->user_data); +} + +static void mm_get_call_properties_reply(DBusPendingCall *pending, void *user_data) +{ + struct call *call = user_data; + struct impl *this = call->this; + DBusMessage *r; + DBusMessageIter arg_i, element_i; + MMCallDirection direction; + MMCallState state; + + spa_assert(call->pending == pending); + dbus_pending_call_unref(pending); + call->pending = NULL; + + r = dbus_pending_call_steal_reply(pending); + if (r == NULL) + return; + + if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) { + spa_log_warn(this->log, "ModemManager D-Bus Call not available"); + goto finish; + } + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + spa_log_error(this->log, "GetAll() failed: %s", dbus_message_get_error_name(r)); + goto finish; + } + + if (!dbus_message_iter_init(r, &arg_i) || !spa_streq(dbus_message_get_signature(r), "a{sv}")) { + spa_log_error(this->log, "Invalid arguments in GetAll() reply"); + goto finish; + } + + spa_log_debug(this->log, "Call path: %s", call->path); + + dbus_message_iter_recurse(&arg_i, &element_i); + while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) { + DBusMessageIter i, value_i; + const char *key; + + dbus_message_iter_recurse(&element_i, &i); + + dbus_message_iter_get_basic(&i, &key); + dbus_message_iter_next(&i); + dbus_message_iter_recurse(&i, &value_i); + + if (spa_streq(key, MM_CALL_PROPERTY_DIRECTION)) { + dbus_message_iter_get_basic(&value_i, &direction); + spa_log_debug(this->log, "Call direction: %u", direction); + call->direction = (direction == MM_CALL_DIRECTION_INCOMING) ? CALL_INCOMING : CALL_OUTGOING; + } else if (spa_streq(key, MM_CALL_PROPERTY_NUMBER)) { + char *number; + + dbus_message_iter_get_basic(&value_i, &number); + spa_log_debug(this->log, "Call number: %s", number); + if (call->number) + free(call->number); + call->number = strdup(number); + } else if (spa_streq(key, MM_CALL_PROPERTY_STATE)) { + int clcc_state; + + dbus_message_iter_get_basic(&value_i, &state); + spa_log_debug(this->log, "Call state: %u", state); + clcc_state = mm_state_to_clcc(this, state); + if (clcc_state < 0) { + spa_log_debug(this->log, "Unsupported modem state: %s, state=%d", call->path, call->state); + } else { + call->state = clcc_state; + mm_call_state_changed(this); + } + } + + dbus_message_iter_next(&element_i); + } + +finish: + dbus_message_unref(r); +} + +static DBusHandlerResult mm_parse_voice_properties(struct impl *this, DBusMessageIter *props_i) +{ + while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) { + DBusMessageIter i, value_i, element_i; + const char *key; + + dbus_message_iter_recurse(props_i, &i); + + dbus_message_iter_get_basic(&i, &key); + dbus_message_iter_next(&i); + dbus_message_iter_recurse(&i, &value_i); + + if (spa_streq(key, MM_MODEM_VOICE_PROPERTY_CALLS)) { + spa_log_debug(this->log, "Voice properties"); + dbus_message_iter_recurse(&value_i, &element_i); + + while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) { + const char *call_object; + + dbus_message_iter_get_basic(&element_i, &call_object); + spa_log_debug(this->log, " Call: %s", call_object); + + dbus_message_iter_next(&element_i); + } + } + + dbus_message_iter_next(props_i); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult mm_parse_modem3gpp_properties(struct impl *this, DBusMessageIter *props_i) +{ + while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) { + DBusMessageIter i, value_i; + const char *key; + + dbus_message_iter_recurse(props_i, &i); + + dbus_message_iter_get_basic(&i, &key); + dbus_message_iter_next(&i); + dbus_message_iter_recurse(&i, &value_i); + + if (spa_streq(key, MM_MODEM_MODEM3GPP_PROPERTY_OPERATORNAME)) { + char *operator_name; + + dbus_message_iter_get_basic(&value_i, &operator_name); + spa_log_debug(this->log, "Network operator code: %s", operator_name); + if (this->ops->set_modem_operator_name) + this->ops->set_modem_operator_name(operator_name, this->user_data); + } else if (spa_streq(key, MM_MODEM_MODEM3GPP_PROPERTY_REGISTRATIONSTATE)) { + MMModem3gppRegistrationState state; + bool is_roaming; + + dbus_message_iter_get_basic(&value_i, &state); + spa_log_debug(this->log, "Registration state: %d", state); + + if (state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING || + state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED || + state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY) + is_roaming = true; + else + is_roaming = false; + + if (this->ops->set_modem_roaming) + this->ops->set_modem_roaming(is_roaming, this->user_data); + } + + dbus_message_iter_next(props_i); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult mm_parse_modem_properties(struct impl *this, DBusMessageIter *props_i) +{ + while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) { + DBusMessageIter i, value_i; + const char *key; + + dbus_message_iter_recurse(props_i, &i); + + dbus_message_iter_get_basic(&i, &key); + dbus_message_iter_next(&i); + dbus_message_iter_recurse(&i, &value_i); + + if(spa_streq(key, MM_MODEM_PROPERTY_EQUIPMENTIDENTIFIER)) { + char *imei; + + dbus_message_iter_get_basic(&value_i, &imei); + spa_log_debug(this->log, "Modem IMEI: %s", imei); + } else if(spa_streq(key, MM_MODEM_PROPERTY_MANUFACTURER)) { + char *manufacturer; + + dbus_message_iter_get_basic(&value_i, &manufacturer); + spa_log_debug(this->log, "Modem manufacturer: %s", manufacturer); + } else if(spa_streq(key, MM_MODEM_PROPERTY_MODEL)) { + char *model; + + dbus_message_iter_get_basic(&value_i, &model); + spa_log_debug(this->log, "Modem model: %s", model); + } else if (spa_streq(key, MM_MODEM_PROPERTY_OWNNUMBERS)) { + char *number; + DBusMessageIter array_i; + + dbus_message_iter_recurse(&value_i, &array_i); + if (dbus_message_iter_get_arg_type(&array_i) == DBUS_TYPE_STRING) { + dbus_message_iter_get_basic(&array_i, &number); + spa_log_debug(this->log, "Modem own number: %s", number); + if (this->ops->set_modem_own_number) + this->ops->set_modem_own_number(number, this->user_data); + } + } else if(spa_streq(key, MM_MODEM_PROPERTY_REVISION)) { + char *revision; + + dbus_message_iter_get_basic(&value_i, &revision); + spa_log_debug(this->log, "Modem revision: %s", revision); + } else if(spa_streq(key, MM_MODEM_PROPERTY_SIGNALQUALITY)) { + unsigned int percentage, signal_strength; + DBusMessageIter struct_i; + + dbus_message_iter_recurse(&value_i, &struct_i); + if (dbus_message_iter_get_arg_type(&struct_i) == DBUS_TYPE_UINT32) { + dbus_message_iter_get_basic(&struct_i, &percentage); + signal_strength = (unsigned int) round(percentage / 20.0); + spa_log_debug(this->log, "Network signal strength: %d (%d)", percentage, signal_strength); + if(this->ops->set_modem_signal_strength) + this->ops->set_modem_signal_strength(signal_strength, this->user_data); + } + } else if(spa_streq(key, MM_MODEM_PROPERTY_STATE)) { + MMModemState state; + bool has_service; + + dbus_message_iter_get_basic(&value_i, &state); + spa_log_debug(this->log, "Network state: %d", state); + + has_service = (state >= MM_MODEM_STATE_REGISTERED); + if (this->ops->set_modem_service) + this->ops->set_modem_service(has_service, this->user_data); + } + + dbus_message_iter_next(props_i); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult mm_parse_interfaces(struct impl *this, DBusMessageIter *dict_i) +{ + DBusMessageIter element_i, props_i; + const char *path; + + spa_assert(this); + spa_assert(dict_i); + + dbus_message_iter_get_basic(dict_i, &path); + dbus_message_iter_next(dict_i); + dbus_message_iter_recurse(dict_i, &element_i); + + while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter iface_i; + const char *interface; + + dbus_message_iter_recurse(&element_i, &iface_i); + dbus_message_iter_get_basic(&iface_i, &interface); + dbus_message_iter_next(&iface_i); + spa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY); + + dbus_message_iter_recurse(&iface_i, &props_i); + + if (spa_streq(interface, MM_DBUS_INTERFACE_MODEM)) { + spa_log_debug(this->log, "Found Modem interface %s, path %s", interface, path); + if (this->modem.path == NULL) { + if (this->allowed_modem_device) { + DBusMessageIter i; + + dbus_message_iter_recurse(&iface_i, &i); + while (dbus_message_iter_get_arg_type(&i) != DBUS_TYPE_INVALID) { + DBusMessageIter key_i, value_i; + const char *key; + + dbus_message_iter_recurse(&i, &key_i); + + dbus_message_iter_get_basic(&key_i, &key); + dbus_message_iter_next(&key_i); + dbus_message_iter_recurse(&key_i, &value_i); + + if (spa_streq(key, MM_MODEM_PROPERTY_DEVICE)) { + char *device; + + dbus_message_iter_get_basic(&value_i, &device); + if (!spa_streq(this->allowed_modem_device, device)) { + spa_log_debug(this->log, "Modem not allowed: %s", device); + goto next; + } + } + dbus_message_iter_next(&i); + } + } + this->modem.path = strdup(path); + } else if (!spa_streq(this->modem.path, path)) { + spa_log_debug(this->log, "A modem is already registered"); + goto next; + } + mm_parse_modem_properties(this, &props_i); + } else if (spa_streq(interface, MM_DBUS_INTERFACE_MODEM_MODEM3GPP)) { + if (spa_streq(this->modem.path, path)) { + spa_log_debug(this->log, "Found Modem3GPP interface %s, path %s", interface, path); + mm_parse_modem3gpp_properties(this, &props_i); + } + } else if (spa_streq(interface, MM_DBUS_INTERFACE_MODEM_VOICE)) { + if (spa_streq(this->modem.path, path)) { + spa_log_debug(this->log, "Found Voice interface %s, path %s", interface, path); + mm_parse_voice_properties(this, &props_i); + } + } + +next: + dbus_message_iter_next(&element_i); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void mm_get_managed_objects_reply(DBusPendingCall *pending, void *user_data) +{ + struct impl *this = user_data; + DBusMessage *r; + DBusMessageIter i, array_i; + + spa_assert(this->pending == pending); + dbus_pending_call_unref(pending); + this->pending = NULL; + + r = dbus_pending_call_steal_reply(pending); + if (r == NULL) + return; + + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + spa_log_error(this->log, "Failed to get a list of endpoints from ModemManager: %s", + dbus_message_get_error_name(r)); + goto finish; + } + + if (!dbus_message_iter_init(r, &i) || !spa_streq(dbus_message_get_signature(r), "a{oa{sa{sv}}}")) { + spa_log_error(this->log, "Invalid arguments in GetManagedObjects() reply"); + goto finish; + } + + dbus_message_iter_recurse(&i, &array_i); + while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) { + DBusMessageIter dict_i; + + dbus_message_iter_recurse(&array_i, &dict_i); + mm_parse_interfaces(this, &dict_i); + dbus_message_iter_next(&array_i); + } + +finish: + dbus_message_unref(r); +} + +static void call_free(struct call *call) { + spa_list_remove(&call->link); + + if (call->pending != NULL) { + dbus_pending_call_cancel(call->pending); + dbus_pending_call_unref(call->pending); + } + + if (call->number) + free(call->number); + if (call->path) + free(call->path); + free(call); +} + +static void mm_clean_voice(struct impl *this) +{ + struct call *call; + + spa_list_consume(call, &this->call_list, link) + call_free(call); + + if (this->voice_pending != NULL) { + dbus_pending_call_cancel(this->voice_pending); + dbus_pending_call_unref(this->voice_pending); + } + + if (this->ops->set_call_setup) + this->ops->set_call_setup(CIND_CALLSETUP_NONE, this->user_data); + if (this->ops->set_call_active) + this->ops->set_call_active(false, this->user_data); +} + +static void mm_clean_modem3gpp(struct impl *this) +{ + if (this->ops->set_modem_operator_name) + this->ops->set_modem_operator_name(NULL, this->user_data); + if (this->ops->set_modem_roaming) + this->ops->set_modem_roaming(false, this->user_data); +} + +static void mm_clean_modem(struct impl *this) +{ + if (this->modem.path) { + free(this->modem.path); + this->modem.path = NULL; + } + if(this->ops->set_modem_signal_strength) + this->ops->set_modem_signal_strength(0, this->user_data); + if (this->ops->set_modem_service) + this->ops->set_modem_service(false, this->user_data); + this->modem.network_has_service = false; +} + +static DBusHandlerResult mm_filter_cb(DBusConnection *bus, DBusMessage *m, void *user_data) +{ + struct impl *this = user_data; + DBusError err; + + dbus_error_init(&err); + + if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) { + const char *name, *old_owner, *new_owner; + + spa_log_debug(this->log, "Name owner changed %s", dbus_message_get_path(m)); + + if (!dbus_message_get_args(m, &err, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old_owner, + DBUS_TYPE_STRING, &new_owner, + DBUS_TYPE_INVALID)) { + spa_log_error(this->log, "Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message); + goto finish; + } + + if (spa_streq(name, MM_DBUS_SERVICE)) { + if (old_owner && *old_owner) { + spa_log_debug(this->log, "ModemManager daemon disappeared (%s)", old_owner); + mm_clean_voice(this); + mm_clean_modem3gpp(this); + mm_clean_modem(this); + } + + if (new_owner && *new_owner) + spa_log_debug(this->log, "ModemManager daemon appeared (%s)", new_owner); + } + } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECTMANAGER, DBUS_SIGNAL_INTERFACES_ADDED)) { + DBusMessageIter arg_i; + + spa_log_warn(this->log, "sender: %s", dbus_message_get_sender(m)); + + if (!dbus_message_iter_init(m, &arg_i) || !spa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) { + spa_log_error(this->log, "Invalid signature found in InterfacesAdded"); + goto finish; + } + + mm_parse_interfaces(this, &arg_i); + } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECTMANAGER, DBUS_SIGNAL_INTERFACES_REMOVED)) { + const char *path; + DBusMessageIter arg_i, element_i; + + if (!dbus_message_iter_init(m, &arg_i) || !spa_streq(dbus_message_get_signature(m), "oas")) { + spa_log_error(this->log, "Invalid signature found in InterfacesRemoved"); + goto finish; + } + + dbus_message_iter_get_basic(&arg_i, &path); + if (!spa_streq(this->modem.path, path)) + goto finish; + + dbus_message_iter_next(&arg_i); + dbus_message_iter_recurse(&arg_i, &element_i); + + while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) { + const char *iface; + + dbus_message_iter_get_basic(&element_i, &iface); + + spa_log_debug(this->log, "Interface removed %s", path); + if (spa_streq(iface, MM_DBUS_INTERFACE_MODEM)) { + spa_log_debug(this->log, "Modem interface %s removed, path %s", iface, path); + mm_clean_modem(this); + } else if (spa_streq(iface, MM_DBUS_INTERFACE_MODEM_MODEM3GPP)) { + spa_log_debug(this->log, "Modem3GPP interface %s removed, path %s", iface, path); + mm_clean_modem3gpp(this); + } else if (spa_streq(iface, MM_DBUS_INTERFACE_MODEM_VOICE)) { + spa_log_debug(this->log, "Voice interface %s removed, path %s", iface, path); + mm_clean_voice(this); + } + + dbus_message_iter_next(&element_i); + } + } else if (dbus_message_is_signal(m, DBUS_INTERFACE_PROPERTIES, DBUS_SIGNAL_PROPERTIES_CHANGED)) { + const char *path; + DBusMessageIter iface_i, props_i; + const char *interface; + + path = dbus_message_get_path(m); + if (!spa_streq(this->modem.path, path)) + goto finish; + + if (!dbus_message_iter_init(m, &iface_i) || !spa_streq(dbus_message_get_signature(m), "sa{sv}as")) { + spa_log_error(this->log, "Invalid signature found in PropertiesChanged"); + goto finish; + } + + dbus_message_iter_get_basic(&iface_i, &interface); + dbus_message_iter_next(&iface_i); + spa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY); + + dbus_message_iter_recurse(&iface_i, &props_i); + + if (spa_streq(interface, MM_DBUS_INTERFACE_MODEM)) { + spa_log_debug(this->log, "Properties changed on %s", path); + mm_parse_modem_properties(this, &props_i); + } else if (spa_streq(interface, MM_DBUS_INTERFACE_MODEM_MODEM3GPP)) { + spa_log_debug(this->log, "Properties changed on %s", path); + mm_parse_modem3gpp_properties(this, &props_i); + } else if (spa_streq(interface, MM_DBUS_INTERFACE_MODEM_VOICE)) { + spa_log_debug(this->log, "Properties changed on %s", path); + mm_parse_voice_properties(this, &props_i); + } + } else if (dbus_message_is_signal(m, MM_DBUS_INTERFACE_MODEM_VOICE, MM_MODEM_VOICE_SIGNAL_CALLADDED)) { + DBusMessageIter iface_i; + const char *path; + struct call *call_object; + const char *mm_call_interface = MM_DBUS_INTERFACE_CALL; + + if (!spa_streq(this->modem.path, dbus_message_get_path(m))) + goto finish; + + if (!dbus_message_iter_init(m, &iface_i) || !spa_streq(dbus_message_get_signature(m), "o")) { + spa_log_error(this->log, "Invalid signature found in %s", MM_MODEM_VOICE_SIGNAL_CALLADDED); + goto finish; + } + + dbus_message_iter_get_basic(&iface_i, &path); + spa_log_debug(this->log, "New call: %s", path); + + call_object = calloc(1, sizeof(struct call)); + if (call_object == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + call_object->this = this; + call_object->path = strdup(path); + spa_list_append(&this->call_list, &call_object->link); + + m = dbus_message_new_method_call(MM_DBUS_SERVICE, path, DBUS_INTERFACE_PROPERTIES, "GetAll"); + if (m == NULL) + goto finish; + dbus_message_append_args(m, DBUS_TYPE_STRING, &mm_call_interface, DBUS_TYPE_INVALID); + if (!mm_dbus_connection_send_with_reply(this, m, &call_object->pending, mm_get_call_properties_reply, call_object)) { + spa_log_error(this->log, "dbus call failure"); + dbus_message_unref(m); + goto finish; + } + } else if (dbus_message_is_signal(m, MM_DBUS_INTERFACE_MODEM_VOICE, MM_MODEM_VOICE_SIGNAL_CALLDELETED)) { + const char *path; + DBusMessageIter iface_i; + struct call *call, *call_tmp; + + if (!spa_streq(this->modem.path, dbus_message_get_path(m))) + goto finish; + + if (!dbus_message_iter_init(m, &iface_i) || !spa_streq(dbus_message_get_signature(m), "o")) { + spa_log_error(this->log, "Invalid signature found in %s", MM_MODEM_VOICE_SIGNAL_CALLDELETED); + goto finish; + } + + dbus_message_iter_get_basic(&iface_i, &path); + spa_log_debug(this->log, "Call ended: %s", path); + + spa_list_for_each_safe(call, call_tmp, &this->call_list, link) { + if (spa_streq(call->path, path)) + call_free(call); + } + mm_call_state_changed(this); + } else if (dbus_message_is_signal(m, MM_DBUS_INTERFACE_CALL, MM_CALL_SIGNAL_STATECHANGED)) { + const char *path; + DBusMessageIter iface_i; + MMCallState old, new; + MMCallStateReason reason; + struct call *call = NULL, *call_tmp; + int clcc_state; + + if (!dbus_message_iter_init(m, &iface_i) || !spa_streq(dbus_message_get_signature(m), "iiu")) { + spa_log_error(this->log, "Invalid signature found in %s", MM_CALL_SIGNAL_STATECHANGED); + goto finish; + } + + path = dbus_message_get_path(m); + + dbus_message_iter_get_basic(&iface_i, &old); + dbus_message_iter_next(&iface_i); + dbus_message_iter_get_basic(&iface_i, &new); + dbus_message_iter_next(&iface_i); + dbus_message_iter_get_basic(&iface_i, &reason); + + spa_log_debug(this->log, "Call state %s changed to %d (old = %d, reason = %u)", path, new, old, reason); + + spa_list_for_each(call_tmp, &this->call_list, link) { + if (spa_streq(call_tmp->path, path)) { + call = call_tmp; + break; + } + } + + if (call == NULL) { + spa_log_warn(this->log, "No call reference for %s", path); + goto finish; + } + + clcc_state = mm_state_to_clcc(this, new); + if (clcc_state < 0) { + spa_log_debug(this->log, "Unsupported modem state: %s, state=%d", call->path, call->state); + } else { + call->state = clcc_state; + mm_call_state_changed(this); + } + } + +finish: + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static int add_filters(struct impl *this) +{ + DBusError err; + + if (this->filters_added) + return 0; + + dbus_error_init(&err); + + if (!dbus_connection_add_filter(this->conn, mm_filter_cb, this, NULL)) { + spa_log_error(this->log, "failed to add filter function"); + goto fail; + } + + dbus_bus_add_match(this->conn, + "type='signal',sender='org.freedesktop.DBus'," + "interface='org.freedesktop.DBus',member='NameOwnerChanged'," "arg0='" MM_DBUS_SERVICE "'", &err); + dbus_bus_add_match(this->conn, + "type='signal',sender='" MM_DBUS_SERVICE "'," + "interface='" DBUS_INTERFACE_OBJECTMANAGER "',member='" DBUS_SIGNAL_INTERFACES_ADDED "'", &err); + dbus_bus_add_match(this->conn, + "type='signal',sender='" MM_DBUS_SERVICE "'," + "interface='" DBUS_INTERFACE_OBJECTMANAGER "',member='" DBUS_SIGNAL_INTERFACES_REMOVED "'", &err); + dbus_bus_add_match(this->conn, + "type='signal',sender='" MM_DBUS_SERVICE "'," + "interface='" DBUS_INTERFACE_PROPERTIES "',member='" DBUS_SIGNAL_PROPERTIES_CHANGED "'", &err); + dbus_bus_add_match(this->conn, + "type='signal',sender='" MM_DBUS_SERVICE "'," + "interface='" MM_DBUS_INTERFACE_MODEM_VOICE "',member='" MM_MODEM_VOICE_SIGNAL_CALLADDED "'", &err); + dbus_bus_add_match(this->conn, + "type='signal',sender='" MM_DBUS_SERVICE "'," + "interface='" MM_DBUS_INTERFACE_MODEM_VOICE "',member='" MM_MODEM_VOICE_SIGNAL_CALLDELETED "'", &err); + dbus_bus_add_match(this->conn, + "type='signal',sender='" MM_DBUS_SERVICE "'," + "interface='" MM_DBUS_INTERFACE_CALL "',member='" MM_CALL_SIGNAL_STATECHANGED "'", &err); + + this->filters_added = true; + + return 0; + +fail: + dbus_error_free(&err); + return -EIO; +} + +static bool is_dbus_service_available(struct impl *this, const char *service) +{ + DBusMessage *m, *r; + DBusError err; + bool success = false; + + m = dbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus", + "org.freedesktop.DBus", "NameHasOwner"); + if (m == NULL) + return false; + dbus_message_append_args(m, DBUS_TYPE_STRING, &service, DBUS_TYPE_INVALID); + + dbus_error_init(&err); + r = dbus_connection_send_with_reply_and_block(this->conn, m, -1, &err); + dbus_message_unref(m); + m = NULL; + + if (r == NULL) { + spa_log_info(this->log, "NameHasOwner failed for %s", service); + dbus_error_free(&err); + goto finish; + } + + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + spa_log_error(this->log, "NameHasOwner() returned error: %s", dbus_message_get_error_name(r)); + goto finish; + } + + if (!dbus_message_get_args(r, &err, + DBUS_TYPE_BOOLEAN, &success, + DBUS_TYPE_INVALID)) { + spa_log_error(this->log, "Failed to parse NameHasOwner() reply: %s", err.message); + dbus_error_free(&err); + goto finish; + } + +finish: + if (r) + dbus_message_unref(r); + + return success; +} + +bool mm_is_available(void *modemmanager) +{ + struct impl *this = modemmanager; + + if (this == NULL) + return false; + + return this->modem.path != NULL; +} + +unsigned int mm_supported_features() +{ + return SPA_BT_HFP_AG_FEATURE_REJECT_CALL | SPA_BT_HFP_AG_FEATURE_ENHANCED_CALL_STATUS; +} + +static void mm_get_call_simple_reply(DBusPendingCall *pending, void *data) +{ + struct dbus_cmd_data *dbus_cmd_data = data; + struct impl *this = dbus_cmd_data->this; + struct call *call = dbus_cmd_data->call; + void *user_data = dbus_cmd_data->user_data; + DBusMessage *r; + + free(data); + + spa_assert(call->pending == pending); + dbus_pending_call_unref(pending); + call->pending = NULL; + + r = dbus_pending_call_steal_reply(pending); + if (r == NULL) + return; + + if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) { + spa_log_warn(this->log, "ModemManager D-Bus method not available"); + goto finish; + } + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + spa_log_error(this->log, "ModemManager method failed: %s", dbus_message_get_error_name(r)); + goto finish; + } + + this->ops->send_cmd_result(true, 0, user_data); + return; + +finish: + this->ops->send_cmd_result(false, CMEE_AG_FAILURE, user_data); +} + +static void mm_get_call_create_reply(DBusPendingCall *pending, void *data) +{ + struct dbus_cmd_data *dbus_cmd_data = data; + struct impl *this = dbus_cmd_data->this; + void *user_data = dbus_cmd_data->user_data; + DBusMessage *r; + + free(data); + + spa_assert(this->voice_pending == pending); + dbus_pending_call_unref(pending); + this->voice_pending = NULL; + + r = dbus_pending_call_steal_reply(pending); + if (r == NULL) + return; + + if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) { + spa_log_warn(this->log, "ModemManager D-Bus method not available"); + goto finish; + } + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + spa_log_error(this->log, "ModemManager method failed: %s", dbus_message_get_error_name(r)); + goto finish; + } + + this->ops->send_cmd_result(true, 0, user_data); + return; + +finish: + this->ops->send_cmd_result(false, CMEE_AG_FAILURE, user_data); +} + +bool mm_answer_call(void *modemmanager, void *user_data, enum cmee_error *error) +{ + struct impl *this = modemmanager; + struct call *call_object, *call_tmp; + struct dbus_cmd_data *data; + DBusMessage *m; + + call_object = NULL; + spa_list_for_each(call_tmp, &this->call_list, link) { + if (call_tmp->state == CLCC_INCOMING) { + call_object = call_tmp; + break; + } + } + if (!call_object) { + spa_log_debug(this->log, "No ringing in call"); + if (error) + *error = CMEE_OPERATION_NOT_ALLOWED; + return false; + } + + data = malloc(sizeof(struct dbus_cmd_data)); + if (!data) { + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + data->this = this; + data->call = call_object; + data->user_data = user_data; + + m = dbus_message_new_method_call(MM_DBUS_SERVICE, call_object->path, MM_DBUS_INTERFACE_CALL, MM_CALL_METHOD_ACCEPT); + if (m == NULL) { + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + if (!mm_dbus_connection_send_with_reply(this, m, &call_object->pending, mm_get_call_simple_reply, data)) { + spa_log_error(this->log, "dbus call failure"); + dbus_message_unref(m); + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + + return true; +} + +bool mm_hangup_call(void *modemmanager, void *user_data, enum cmee_error *error) +{ + struct impl *this = modemmanager; + struct call *call_object, *call_tmp; + struct dbus_cmd_data *data; + DBusMessage *m; + + call_object = NULL; + spa_list_for_each(call_tmp, &this->call_list, link) { + if (call_tmp->state == CLCC_ACTIVE) { + call_object = call_tmp; + break; + } + } + if (!call_object) { + spa_list_for_each(call_tmp, &this->call_list, link) { + if (call_tmp->state == CLCC_DIALING || + call_tmp->state == CLCC_ALERTING || + call_tmp->state == CLCC_INCOMING) { + call_object = call_tmp; + break; + } + } + } + if (!call_object) { + spa_log_debug(this->log, "No call to reject or hang up"); + if (error) + *error = CMEE_OPERATION_NOT_ALLOWED; + return false; + } + + data = malloc(sizeof(struct dbus_cmd_data)); + if (!data) { + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + data->this = this; + data->call = call_object; + data->user_data = user_data; + + m = dbus_message_new_method_call(MM_DBUS_SERVICE, call_object->path, MM_DBUS_INTERFACE_CALL, MM_CALL_METHOD_HANGUP); + if (m == NULL) { + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + if (!mm_dbus_connection_send_with_reply(this, m, &call_object->pending, mm_get_call_simple_reply, data)) { + spa_log_error(this->log, "dbus call failure"); + dbus_message_unref(m); + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + + return true; +} + +static void append_basic_variant_dict_entry(DBusMessageIter *dict, const char* key, int variant_type_int, const char* variant_type_str, void* variant) { + DBusMessageIter dict_entry_it, variant_it; + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_it); + dbus_message_iter_append_basic(&dict_entry_it, DBUS_TYPE_STRING, &key); + + dbus_message_iter_open_container(&dict_entry_it, DBUS_TYPE_VARIANT, variant_type_str, &variant_it); + dbus_message_iter_append_basic(&variant_it, variant_type_int, variant); + dbus_message_iter_close_container(&dict_entry_it, &variant_it); + dbus_message_iter_close_container(dict, &dict_entry_it); +} + +bool mm_do_call(void *modemmanager, const char* number, void *user_data, enum cmee_error *error) +{ + struct impl *this = modemmanager; + unsigned int k, j; + char *number_filtered; + struct dbus_cmd_data *data; + DBusMessage *m; + DBusMessageIter iter, dict; + + /* Filter extracted number from invalid characters + * Allowed characters: 0-9, *, #, +, A-C + */ + k=0; + number_filtered = calloc(1, 30); + for (j=0; j < 30; j++) { + if ((numberj >= '0' && numberj <= '9') + || (numberj == '*') + || (numberj == '#') + || (numberj == '+') + || (numberj >= 'A' && numberj <= 'C')) { + number_filteredk = numberj; + k++; + } + /* ATD commands ends with ';' */ + else if (numberj == ';') + break; + /* Send error for invalid characters */ + else { + spa_log_warn(this->log, "Call creation canceled, invalid character found in dial string: %c", numberj); + if (error) + *error = CMEE_INVALID_CHARACTERS_DIAL_STRING; + return false; + } + } + + data = malloc(sizeof(struct dbus_cmd_data)); + if (!data) { + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + data->this = this; + data->user_data = user_data; + + m = dbus_message_new_method_call(MM_DBUS_SERVICE, this->modem.path, MM_DBUS_INTERFACE_MODEM_VOICE, MM_MODEM_VOICE_METHOD_CREATECALL); + if (m == NULL) { + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + dbus_message_iter_init_append(m, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); + append_basic_variant_dict_entry(&dict, "number", DBUS_TYPE_STRING, "s", &number_filtered); + dbus_message_iter_close_container(&iter, &dict); + if (!mm_dbus_connection_send_with_reply(this, m, &this->voice_pending, mm_get_call_create_reply, data)) { + spa_log_error(this->log, "dbus call failure"); + dbus_message_unref(m); + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + + return true; +} + +bool mm_send_dtmf(void *modemmanager, const char *dtmf, void *user_data, enum cmee_error *error) +{ + struct impl *this = modemmanager; + struct call *call_object, *call_tmp; + struct dbus_cmd_data *data; + DBusMessage *m; + + call_object = NULL; + spa_list_for_each(call_tmp, &this->call_list, link) { + if (call_tmp->state == CLCC_ACTIVE) { + call_object = call_tmp; + break; + } + } + if (!call_object) { + spa_log_debug(this->log, "No active call"); + if (error) + *error = CMEE_OPERATION_NOT_ALLOWED; + return false; + } + + /* Allowed dtmf characters: 0-9, *, #, A-D */ + if (!((dtmf0 >= '0' && dtmf0 <= '9') + || (dtmf0 == '*') + || (dtmf0 == '#') + || (dtmf0 >= 'A' && dtmf0 <= 'D'))) { + spa_log_debug(this->log, "Invalid DTMF character: %s", dtmf); + if (error) + *error = CMEE_INVALID_CHARACTERS_TEXT_STRING; + return false; + } + + data = malloc(sizeof(struct dbus_cmd_data)); + if (!data) { + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + data->this = this; + data->call = call_object; + data->user_data = user_data; + + m = dbus_message_new_method_call(MM_DBUS_SERVICE, call_object->path, MM_DBUS_INTERFACE_CALL, MM_CALL_METHOD_SENDDTMF); + if (m == NULL) { + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + dbus_message_append_args(m, DBUS_TYPE_STRING, &dtmf, DBUS_TYPE_INVALID); + if (!mm_dbus_connection_send_with_reply(this, m, &call_object->pending, mm_get_call_simple_reply, data)) { + spa_log_error(this->log, "dbus call failure"); + dbus_message_unref(m); + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + + return true; +} + +const char *mm_get_incoming_call_number(void *modemmanager) +{ + struct impl *this = modemmanager; + struct call *call_object, *call_tmp; + + call_object = NULL; + spa_list_for_each(call_tmp, &this->call_list, link) { + if (call_tmp->state == CLCC_INCOMING) { + call_object = call_tmp; + break; + } + } + if (!call_object) { + spa_log_debug(this->log, "No ringing in call"); + return NULL; + } + + return call_object->number; +} + +struct spa_list *mm_get_calls(void *modemmanager) +{ + struct impl *this = modemmanager; + + return &this->call_list; +} + +void *mm_register(struct spa_log *log, void *dbus_connection, const struct spa_dict *info, + const struct mm_ops *ops, void *user_data) +{ + struct impl *this; + const char *modem_device_str = NULL; + bool modem_device_found = false; + + spa_assert(log); + spa_assert(dbus_connection); + + if (info) { + if ((modem_device_str = spa_dict_lookup(info, "bluez5.hfphsp-backend-native-modem")) != NULL) { + if (!spa_streq(modem_device_str, "none")) + modem_device_found = true; + } + } + if (!modem_device_found) { + spa_log_info(log, "No modem allowed, doesn't link to ModemManager"); + return NULL; + } + + this = calloc(1, sizeof(struct impl)); + if (this == NULL) + return NULL; + + this->log = log; + this->conn = dbus_connection; + this->ops = ops; + this->user_data = user_data; + if (modem_device_str && !spa_streq(modem_device_str, "any")) + this->allowed_modem_device = strdup(modem_device_str); + spa_list_init(&this->call_list); + + if (add_filters(this) < 0) { + goto fail; + } + + if (is_dbus_service_available(this, MM_DBUS_SERVICE)) { + DBusMessage *m; + + m = dbus_message_new_method_call(MM_DBUS_SERVICE, "/org/freedesktop/ModemManager1", + DBUS_INTERFACE_OBJECTMANAGER, "GetManagedObjects"); + if (m == NULL) + goto fail; + + if (!mm_dbus_connection_send_with_reply(this, m, &this->pending, mm_get_managed_objects_reply, this)) { + spa_log_error(this->log, "dbus call failure"); + dbus_message_unref(m); + goto fail; + } + } + + return this; + +fail: + free(this); + return NULL; +} + +void mm_unregister(void *data) +{ + struct impl *this = data; + + if (this->pending != NULL) { + dbus_pending_call_cancel(this->pending); + dbus_pending_call_unref(this->pending); + } + + mm_clean_voice(this); + mm_clean_modem3gpp(this); + mm_clean_modem(this); + + if (this->filters_added) { + dbus_connection_remove_filter(this->conn, mm_filter_cb, this); + this->filters_added = false; + } + + if (this->allowed_modem_device) + free(this->allowed_modem_device); + + free(this); +}
View file
pipewire-0.3.60.tar.gz/spa/plugins/bluez5/modemmanager.h
Added
@@ -0,0 +1,161 @@ +/* Spa Bluez5 ModemManager proxy + * + * 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_MODEMMANAGER_H_ +#define SPA_BLUEZ5_MODEMMANAGER_H_ + +#include <spa/utils/list.h> + +#include "defs.h" + +enum cmee_error { + CMEE_AG_FAILURE = 0, + CMEE_NO_CONNECTION_TO_PHONE = 1, + CMEE_OPERATION_NOT_ALLOWED = 3, + CMEE_OPERATION_NOT_SUPPORTED = 4, + CMEE_INVALID_CHARACTERS_TEXT_STRING = 25, + CMEE_INVALID_CHARACTERS_DIAL_STRING = 27, + CMEE_NO_NETWORK_SERVICE = 30 +}; + +enum call_setup { + CIND_CALLSETUP_NONE = 0, + CIND_CALLSETUP_INCOMING, + CIND_CALLSETUP_DIALING, + CIND_CALLSETUP_ALERTING +}; + +enum call_direction { + CALL_OUTGOING, + CALL_INCOMING +}; + +enum call_state { + CLCC_ACTIVE, + CLCC_HELD, + CLCC_DIALING, + CLCC_ALERTING, + CLCC_INCOMING, + CLCC_WAITING, + CLCC_RESPONSE_AND_HOLD +}; + +struct call { + struct spa_list link; + unsigned int index; + struct impl *this; + DBusPendingCall *pending; + + char *path; + char *number; + bool call_indicator; + enum call_direction direction; + enum call_state state; + bool multiparty; +}; + +struct mm_ops { + void (*send_cmd_result)(bool success, enum cmee_error error, void *user_data); + void (*set_modem_service)(bool available, void *user_data); + void (*set_modem_signal_strength)(unsigned int strength, void *user_data); + void (*set_modem_operator_name)(const char *name, void *user_data); + void (*set_modem_own_number)(const char *number, void *user_data); + void (*set_modem_roaming)(bool is_roaming, void *user_data); + void (*set_call_active)(bool active, void *user_data); + void (*set_call_setup)(enum call_setup value, void *user_data); +}; + +#ifdef HAVE_BLUEZ_5_BACKEND_NATIVE_MM +void *mm_register(struct spa_log *log, void *dbus_connection, const struct spa_dict *info, + const struct mm_ops *ops, void *user_data); +void mm_unregister(void *data); +bool mm_is_available(void *modemmanager); +unsigned int mm_supported_features(); +bool mm_answer_call(void *modemmanager, void *user_data, enum cmee_error *error); +bool mm_hangup_call(void *modemmanager, void *user_data, enum cmee_error *error); +bool mm_do_call(void *modemmanager, const char* number, void *user_data, enum cmee_error *error); +bool mm_send_dtmf(void *modemmanager, const char *dtmf, void *user_data, enum cmee_error *error); +const char *mm_get_incoming_call_number(void *modemmanager); +struct spa_list *mm_get_calls(void *modemmanager); +#else +void *mm_register(struct spa_log *log, void *dbus_connection, const struct spa_dict *info, + const struct mm_ops *ops, void *user_data) +{ + return NULL; +} + +void mm_unregister(void *data) +{ +} + +bool mm_is_available(void *modemmanager) +{ + return false; +} + +unsigned int mm_supported_features(void) +{ + return 0; +} + +bool mm_answer_call(void *modemmanager, void *user_data, enum cmee_error *error) +{ + if (error) + *error = CMEE_OPERATION_NOT_SUPPORTED; + return false; +} + +bool mm_hangup_call(void *modemmanager, void *user_data, enum cmee_error *error) +{ + if (error) + *error = CMEE_OPERATION_NOT_SUPPORTED; + return false; +} + +bool mm_do_call(void *modemmanager, const char* number, void *user_data, enum cmee_error *error) +{ + if (error) + *error = CMEE_OPERATION_NOT_SUPPORTED; + return false; +} + +bool mm_send_dtmf(void *modemmanager, const char *dtmf, void *user_data, enum cmee_error *error) +{ + if (error) + *error = CMEE_OPERATION_NOT_SUPPORTED; + return false; +} + +const char *mm_get_incoming_call_number(void *modemmanager) +{ + return NULL; +} + +struct spa_list *mm_get_calls(void *modemmanager) +{ + return NULL; +} +#endif + +#endif
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/quirks.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/quirks.c
Changed
@@ -88,10 +88,9 @@ { "faststream", SPA_BT_FEATURE_FASTSTREAM }, { "a2dp-duplex", SPA_BT_FEATURE_A2DP_DUPLEX }, }; - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(feature_keys); ++i) { - if (spa_streq(str, feature_keysi.key)) - return feature_keysi.value; + SPA_FOR_EACH_ELEMENT_VAR(feature_keys, f) { + if (spa_streq(str, f->key)) + return f->value; } return 0; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/sco-sink.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/sco-sink.c
Changed
@@ -60,14 +60,10 @@ #define DEFAULT_CLOCK_NAME "clock.system.monotonic" struct props { - uint32_t min_latency; - uint32_t max_latency; char clock_name64; }; #define MAX_BUFFERS 32 -#define MIN_LATENCY 512 -#define MAX_LATENCY 1024 struct buffer { uint32_t id; @@ -130,6 +126,8 @@ struct spa_param_info paramsN_NODE_PARAMS; struct props props; + uint32_t quantum_limit; + /* Transport */ struct spa_bt_transport *transport; struct spa_hook transport_listener; @@ -140,38 +138,39 @@ /* Flags */ unsigned int started:1; unsigned int following:1; + unsigned int flush_pending:1; /* Sources */ struct spa_source source; + struct spa_source flush_timer_source; /* Timer */ int timerfd; - struct timespec now; + int flush_timerfd; struct spa_io_clock *clock; struct spa_io_position *position; + uint64_t current_time; + uint64_t next_time; + uint64_t process_time; + uint64_t prev_flush_time; + uint64_t next_flush_time; + /* mSBC */ sbc_t msbc; uint8_t *buffer; uint8_t *buffer_head; uint8_t *buffer_next; int buffer_size; - - /* Times */ - uint64_t start_time; - uint64_t total_samples; + int msbc_seq; }; #define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) == 0) -static const uint32_t default_min_latency = MIN_LATENCY; -static const uint32_t default_max_latency = MAX_LATENCY; static const char sntable4 = { 0x08, 0x38, 0xC8, 0xF8 }; static void reset_props(struct props *props) { - props->min_latency = default_min_latency; - props->max_latency = default_max_latency; strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name)); } @@ -199,23 +198,7 @@ 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; default: return 0; } @@ -223,14 +206,10 @@ } 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_TYPE_OBJECT_Props, id); break; default: return 0; @@ -260,28 +239,17 @@ ts.it_interval.tv_sec = 0; ts.it_interval.tv_nsec = 0; return spa_system_timerfd_settime(this->data_system, - this->timerfd, 0, &ts, NULL); + this->timerfd, SPA_FD_TIMER_ABSTIME, &ts, NULL); } static int set_timers(struct impl *this) { - return set_timeout(this, this->following ? 0 : 1); -} - -static uint64_t get_next_timeout(struct impl *this, uint64_t now_time, uint64_t processed_samples) -{ - struct port *port = &this->port; - uint64_t playback_time = 0, elapsed_time = 0, next_time = 0; - - this->total_samples += processed_samples; + struct timespec now; - 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; + spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now); + this->next_time = SPA_TIMESPEC_TO_NSEC(&now); - return next_time; + return set_timeout(this, this->following ? 0 : this->next_time); } static int do_reassign_follower(struct spa_loop *loop, @@ -344,9 +312,7 @@ reset_props(&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_TYPE_OBJECT_Props, NULL); } changed = (memcmp(&new_props, &this->props, sizeof(struct props)) != 0); @@ -378,22 +344,64 @@ return 0; } +static void enable_flush_timer(struct impl *this, bool enabled) +{ + struct itimerspec ts; + + if (!enabled) + this->next_flush_time = 0; + + ts.it_value.tv_sec = this->next_flush_time / SPA_NSEC_PER_SEC; + ts.it_value.tv_nsec = this->next_flush_time % 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, SPA_FD_TIMER_ABSTIME, &ts, NULL); + + this->flush_pending = enabled; +} + +static uint32_t get_queued_frames(struct impl *this) +{ + struct port *port = &this->port; + uint32_t bytes = 0; + struct buffer *b; + + spa_list_for_each(b, &port->ready, link) { + struct spa_data *d = b->buf->datas; + + bytes += d0.chunk->size; + } + + if (bytes > port->ready_offset) + bytes -= port->ready_offset; + else + bytes = 0; + + return bytes / port->frame_size; +} + static void flush_data(struct impl *this) { struct port *port = &this->port; - struct spa_data *datas; const uint32_t min_in_size = (this->transport->codec == HFP_AUDIO_CODEC_MSBC) ? MSBC_DECODED_SIZE : this->transport->write_mtu; uint8_t * const packet = (this->transport->codec == HFP_AUDIO_CODEC_MSBC) ? this->buffer_head : port->write_buffer; + const uint32_t packet_samples = min_in_size / port->frame_size; + const uint64_t packet_time = (uint64_t)packet_samples * SPA_NSEC_PER_SEC + / port->current_format.info.raw.rate; + int processed = 0; + int written; if (this->transport == NULL || this->transport->sco_io == NULL) return; -again: while (!spa_list_is_empty(&port->ready) && port->write_buffer_size < min_in_size) { + struct spa_data *datas; + /* get buffer */ if (!port->current_buffer) { spa_return_if_fail(!spa_list_is_empty(&port->ready)); @@ -429,147 +437,212 @@ } } - /* send the data if the write buffer is full */ - if (port->write_buffer_size >= min_in_size) { - uint64_t now_time; - static int sn = 0; - int processed = 0; + if (this->flush_pending) { + spa_log_trace(this->log, "%p: wait for flush timer", this); + return; + } + + if (port->write_buffer_size < min_in_size) { + /* wait for more data */ + spa_log_trace(this->log, "%p: skip flush", this); + enable_flush_timer(this, false); + return; + } + + if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) { ssize_t out_encoded; - int written; - uint64_t next_timeout; - - spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &this->now); - now_time = SPA_TIMESPEC_TO_NSEC(&this->now); - if (this->start_time == 0) - this->start_time = now_time; - - if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) { - /* Encode */ - if (this->buffer_next + MSBC_ENCODED_SIZE > this->buffer + this->buffer_size) { - /* Buffer overrun; shouldn't usually happen. Drop data and reset. */ - this->buffer_head = this->buffer_next = this->buffer; - spa_log_warn(this->log, "sco-sink: mSBC buffer overrun, dropping data"); - } - this->buffer_next0 = 0x01; - this->buffer_next1 = sntablesn; - this->buffer_next59 = 0x00; - sn = (sn + 1) % 4; - processed = sbc_encode(&this->msbc, port->write_buffer, port->write_buffer_size, - this->buffer_next + 2, MSBC_ENCODED_SIZE - 3, &out_encoded); - if (processed < 0) { - spa_log_warn(this->log, "sbc_encode failed: %d", processed); - return; - } - this->buffer_next += out_encoded + 3; - port->write_buffer_size = 0; - - /* Write */ - written = spa_bt_sco_io_write(this->transport->sco_io, packet, - this->buffer_next - this->buffer_head); - if (written < 0) { - spa_log_warn(this->log, "failed to write data: %d (%s)", - written, spa_strerror(written)); - goto stop; - } - - this->buffer_head += written; - - if (this->buffer_head == this->buffer_next) - this->buffer_head = this->buffer_next = this->buffer; - else if (this->buffer_next + MSBC_ENCODED_SIZE > this->buffer + this->buffer_size) { - /* Written bytes is not necessarily commensurate - * with MSBC_ENCODED_SIZE. If this occurs, copy data. - */ - int size = this->buffer_next - this->buffer_head; - spa_memmove(this->buffer, this->buffer_head, size); - this->buffer_next = this->buffer + size; - this->buffer_head = this->buffer; - } - } else { - written = spa_bt_sco_io_write(this->transport->sco_io, packet, - port->write_buffer_size); - if (written < 0) { - spa_log_warn(this->log, "sco-sink: write failure: %d (%s)", - written, spa_strerror(written)); - goto stop; - } else if (written == 0) { - /* EAGAIN or similar, just skip ahead */ - written = SPA_MIN(port->write_buffer_size, (uint32_t)48); - } - - processed = written; - port->write_buffer_size -= written; - - if (port->write_buffer_size > 0 && written > 0) { - spa_memmove(port->write_buffer, port->write_buffer + written, port->write_buffer_size); - } + + /* Encode */ + if (this->buffer_next + MSBC_ENCODED_SIZE > this->buffer + this->buffer_size) { + /* Buffer overrun; shouldn't usually happen. Drop data and reset. */ + this->buffer_head = this->buffer_next = this->buffer; + spa_log_warn(this->log, "sco-sink: mSBC buffer overrun, dropping data"); + } + this->buffer_next0 = 0x01; + this->buffer_next1 = sntablethis->msbc_seq % 4; + this->buffer_next59 = 0x00; + this->msbc_seq = (this->msbc_seq + 1) % 4; + processed = sbc_encode(&this->msbc, port->write_buffer, port->write_buffer_size, + this->buffer_next + 2, MSBC_ENCODED_SIZE - 3, &out_encoded); + if (processed < 0) { + spa_log_warn(this->log, "sbc_encode failed: %d", processed); + return; + } + this->buffer_next += out_encoded + 3; + port->write_buffer_size = 0; + + /* Write */ + written = spa_bt_sco_io_write(this->transport->sco_io, packet, + this->buffer_next - this->buffer_head); + if (written < 0) { + spa_log_warn(this->log, "failed to write data: %d (%s)", + written, spa_strerror(written)); + goto stop; } - spa_log_trace(this->log, "write socket data %d", written); + this->buffer_head += written; + + if (this->buffer_head == this->buffer_next) + this->buffer_head = this->buffer_next = this->buffer; + else if (this->buffer_next + MSBC_ENCODED_SIZE > this->buffer + this->buffer_size) { + /* Written bytes is not necessarily commensurate + * with MSBC_ENCODED_SIZE. If this occurs, copy data. + */ + int size = this->buffer_next - this->buffer_head; + spa_memmove(this->buffer, this->buffer_head, size); + this->buffer_next = this->buffer + size; + this->buffer_head = this->buffer; + } + } else { + written = spa_bt_sco_io_write(this->transport->sco_io, packet, + port->write_buffer_size); + if (written < 0) { + spa_log_warn(this->log, "sco-sink: write failure: %d (%s)", + written, spa_strerror(written)); + goto stop; + } else if (written == 0) { + /* EAGAIN or similar, just skip ahead */ + written = SPA_MIN(port->write_buffer_size, (uint32_t)48); + } - next_timeout = get_next_timeout(this, now_time, processed / port->frame_size); + processed = written; + port->write_buffer_size -= written; - if (!this->following && this->clock) { - this->clock->nsec = now_time; - this->clock->position = this->total_samples; - this->clock->delay = processed / port->frame_size; - this->clock->rate_diff = 1.0f; - this->clock->next_nsec = now_time + next_timeout; + if (port->write_buffer_size > 0 && written > 0) { + spa_memmove(port->write_buffer, port->write_buffer + written, port->write_buffer_size); } + } - if (next_timeout == 0) - goto again; + if (SPA_UNLIKELY(spa_log_level_topic_enabled(this->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_TRACE))) { + struct timespec ts; + uint64_t now; + uint64_t dt; - spa_log_trace(this->log, "timeout %"PRIu64" ns", next_timeout); - set_timeout(this, next_timeout); - } else { - /* As follower, driver will wake us up when there is data */ - if (this->following) - return; + spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &ts); + now = SPA_TIMESPEC_TO_NSEC(&ts); + dt = now - this->prev_flush_time; + this->prev_flush_time = now; + + spa_log_trace(this->log, + "%p: send wrote:%d dt:%"PRIu64, + this, written, dt); + } + + spa_log_trace(this->log, "write socket data %d", written); - /* As driver, run timeout now to schedule data */ - spa_log_trace(this->log, "timeout 1 ns (driver: schedule now)"); - set_timeout(this, 1); + if (SPA_LIKELY(this->position)) { + uint32_t frames = get_queued_frames(this); + uint64_t duration_ns; + + /* + * Flush at the time position of the next buffered sample. + */ + duration_ns = ((uint64_t)this->position->clock.duration * SPA_NSEC_PER_SEC + / this->position->clock.rate.denom); + this->next_flush_time = this->process_time + duration_ns + - ((uint64_t)frames * SPA_NSEC_PER_SEC + / port->current_format.info.raw.rate); + + /* + * We could delay the output by one packet to avoid waiting + * for the next buffer and so make send intervals more regular. + * However, this appears not needed in practice, and it's better + * to not add latency if not needed. + */ +#if 0 + this->next_flush_time += SPA_MIN(packet_time, + duration_ns * (port->n_buffers - 1)); +#endif + } else { + if (this->next_flush_time == 0) + this->next_flush_time = this->process_time; + this->next_flush_time += packet_time; } + enable_flush_timer(this, true); return; stop: if (this->source.loop) spa_loop_remove_source(this->data_loop, &this->source); + enable_flush_timer(this, false); +} + + +static void sco_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_timer(this, false); + return; + } + + while (exp-- > 0) { + this->flush_pending = false; + flush_data(this); + } } static void sco_on_timeout(struct spa_source *source) { struct impl *this = source->data; struct port *port = &this->port; - uint64_t exp; + 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; - /* Read the timerfd */ 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)); - /* delay if no buffers available */ - if (!this->following && spa_list_is_empty(&port->ready)) { - set_timeout(this, this->transport->write_mtu / port->frame_size * SPA_NSEC_PER_SEC / port->current_format.info.raw.rate); - port->io->status = SPA_STATUS_NEED_DATA; - spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_DATA); - return; + 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)) { + 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; + this->clock->delay = 0; } - /* Flush data */ - flush_data(this); + 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); } /* greater common divider */ static int gcd(int a, int b) { while(b) { - int c = b; - b = a % b; - a = c; + int c = b; + b = a % b; + a = c; } return a; } @@ -616,6 +689,10 @@ this->buffer_size = lcm(24, lcm(60, lcm(this->transport->write_mtu, 2 * MSBC_ENCODED_SIZE))); this->buffer = calloc(this->buffer_size, sizeof(uint8_t)); this->buffer_head = this->buffer_next = this->buffer; + if (this->buffer == NULL) { + res = -errno; + goto fail; + } } spa_return_val_if_fail(this->transport->write_mtu <= sizeof(this->port.write_buffer), -EINVAL); @@ -632,7 +709,15 @@ 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 = sco_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); + /* start processing */ + this->flush_pending = false; set_timers(this); /* Set the started flag */ @@ -675,13 +760,20 @@ void *user_data) { struct impl *this = user_data; + struct itimerspec ts; - this->start_time = 0; - this->total_samples = 0; set_timeout(this, 0); if (this->source.loop) spa_loop_remove_source(this->data_loop, &this->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); + /* Drop buffered data in the ready queue. Ideally there shouldn't be any. */ drop_port_output(this); @@ -758,7 +850,7 @@ { SPA_KEY_DEVICE_API, "bluez5" }, { SPA_KEY_MEDIA_CLASS, "Stream/Input/Audio" }, { "media.name", ((this->transport && this->transport->device->name) ? - this->transport->device->name : "HSP/HFP") }, + this->transport->device->name : "HSP/HFP") }, { SPA_KEY_MEDIA_ROLE, "Communication" }, }; bool is_ag = this->transport && @@ -917,9 +1009,9 @@ 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->props.max_latency * port->frame_size, - this->props.min_latency * port->frame_size, + 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; @@ -1014,6 +1106,11 @@ if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) return -EINVAL; + if (info.info.raw.format != SPA_AUDIO_FORMAT_S16_LE || + info.info.raw.rate == 0 || + info.info.raw.channels != 1) + return -EINVAL; + port->frame_size = info.info.raw.channels * 2; port->current_format = info; port->have_format = true; @@ -1150,6 +1247,11 @@ 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; @@ -1167,8 +1269,22 @@ 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); + } + } + + this->process_time = this->current_time; + + if (!spa_list_is_empty(&port->ready)) { + spa_log_trace(this->log, "%p: flush on process", this); flush_data(this); + } return SPA_STATUS_HAVE_DATA; } @@ -1236,9 +1352,12 @@ static int impl_clear(struct spa_handle *handle) { struct impl *this = (struct impl *) handle; + + do_stop(this); 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; } @@ -1323,6 +1442,11 @@ spa_list_init(&port->ready); + this->quantum_limit = 8192; + + if (info && (str = spa_dict_lookup(info, "clock.quantum-limit"))) + spa_atou32(str, &this->quantum_limit, 0); + if (info && (str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT))) sscanf(str, "pointer:%p", &this->transport); @@ -1336,6 +1460,9 @@ 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; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/sco-source.c -> pipewire-0.3.60.tar.gz/spa/plugins/bluez5/sco-source.c
Changed
@@ -248,6 +248,7 @@ struct impl *this = user_data; struct port *port = &this->port; + set_timers(this); spa_bt_decode_buffer_recover(&port->buffer); return 0; } @@ -590,13 +591,14 @@ return 0; } +static int produce_buffer(struct impl *this); + static void sco_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) @@ -631,8 +633,11 @@ this->clock->next_nsec = this->next_time; } - spa_log_trace(this->log, "%p: %d", this, io->status); - io->status = SPA_STATUS_HAVE_DATA; + if (port->io) { + int status = produce_buffer(this); + spa_log_trace(this->log, "%p: io:%d status:%d", this, port->io->status, status); + } + spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA); set_timeout(this, this->next_time); @@ -1068,6 +1073,11 @@ if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) return -EINVAL; + if (info.info.raw.format != SPA_AUDIO_FORMAT_S16_LE || + info.info.raw.rate == 0 || + info.info.raw.channels != 1) + return -EINVAL; + port->frame_size = info.info.raw.channels * 2; port->current_format = info; port->have_format = true; @@ -1285,17 +1295,13 @@ } } -static int impl_node_process(void *object) +static int produce_buffer(struct impl *this) { - struct impl *this = object; - struct port *port; - struct spa_io_buffers *io; struct buffer *buffer; + struct port *port = &this->port; + struct spa_io_buffers *io = port->io; - spa_return_val_if_fail(this != NULL, -EINVAL); - - port = &this->port; - if ((io = port->io) == NULL) + if (io == NULL) return -EIO; /* Return if we already have a buffer */ @@ -1308,7 +1314,7 @@ io->buffer_id = SPA_ID_INVALID; } - /* Produce data */ + /* Handle buffering */ process_buffering(this); /* Return if there are no buffers ready to be processed */ @@ -1328,6 +1334,35 @@ return SPA_STATUS_HAVE_DATA; } +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; + + /* 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; + } + + /* Follower produces buffers here, driver in timeout */ + if (this->following) + return produce_buffer(this); + else + return SPA_STATUS_OK; +} + static const struct spa_node_methods impl_node = { SPA_VERSION_NODE_METHODS, .add_listener = impl_node_add_listener, @@ -1391,6 +1426,8 @@ static int impl_clear(struct spa_handle *handle) { struct impl *this = (struct impl *) handle; + + do_stop(this); if (this->transport) spa_hook_remove(&this->transport_listener); spa_system_close(this->data_system, this->timerfd);
View file
pipewire-0.3.60.tar.gz/spa/plugins/bluez5/upower.c
Added
@@ -0,0 +1,311 @@ +/* Spa Bluez5 UPower proxy + * + * 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 <errno.h> +#include <spa/utils/string.h> + +#include "upower.h" + +#define UPOWER_SERVICE "org.freedesktop.UPower" +#define UPOWER_DEVICE_INTERFACE UPOWER_SERVICE ".Device" +#define UPOWER_DISPLAY_DEVICE_OBJECT "/org/freedesktop/UPower/devices/DisplayDevice" + +struct impl { + struct spa_bt_monitor *monitor; + + struct spa_log *log; + DBusConnection *conn; + + bool filters_added; + + void *user_data; + void (*set_battery_level)(unsigned int level, void *user_data); +}; + +static DBusHandlerResult upower_parse_percentage(struct impl *this, DBusMessageIter *variant_i) +{ + double percentage; + unsigned int battery_level; + + dbus_message_iter_get_basic(variant_i, &percentage); + spa_log_debug(this->log, "Battery level: %f %%", percentage); + + battery_level = (unsigned int) round(percentage / 20.0); + this->set_battery_level(battery_level, this->user_data); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void upower_get_percentage_properties_reply(DBusPendingCall *pending, void *user_data) +{ + struct impl *backend = user_data; + DBusMessage *r; + DBusMessageIter i, variant_i; + + r = dbus_pending_call_steal_reply(pending); + if (r == NULL) + return; + + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + spa_log_error(backend->log, "Failed to get percentage from UPower: %s", + dbus_message_get_error_name(r)); + goto finish; + } + + if (!dbus_message_iter_init(r, &i) || !spa_streq(dbus_message_get_signature(r), "v")) { + spa_log_error(backend->log, "Invalid arguments in Get() reply"); + goto finish; + } + + dbus_message_iter_recurse(&i, &variant_i); + upower_parse_percentage(backend, &variant_i); + +finish: + dbus_message_unref(r); +} + +static void upower_clean(struct impl *this) +{ + this->set_battery_level(0, this->user_data); +} + +static DBusHandlerResult upower_filter_cb(DBusConnection *bus, DBusMessage *m, void *user_data) +{ + struct impl *this = user_data; + DBusError err; + + dbus_error_init(&err); + + if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) { + const char *name, *old_owner, *new_owner; + + spa_log_debug(this->log, "Name owner changed %s", dbus_message_get_path(m)); + + if (!dbus_message_get_args(m, &err, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old_owner, + DBUS_TYPE_STRING, &new_owner, + DBUS_TYPE_INVALID)) { + spa_log_error(this->log, "Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message); + goto finish; + } + + if (spa_streq(name, UPOWER_SERVICE)) { + if (old_owner && *old_owner) { + spa_log_debug(this->log, "UPower daemon disappeared (%s)", old_owner); + upower_clean(this); + } + + if (new_owner && *new_owner) { + DBusPendingCall *call; + static const char* upower_device_interface = UPOWER_DEVICE_INTERFACE; + static const char* percentage_property = "Percentage"; + + spa_log_debug(this->log, "UPower daemon appeared (%s)", new_owner); + + m = dbus_message_new_method_call(UPOWER_SERVICE, UPOWER_DISPLAY_DEVICE_OBJECT, DBUS_INTERFACE_PROPERTIES, "Get"); + if (m == NULL) + goto finish; + dbus_message_append_args(m, DBUS_TYPE_STRING, &upower_device_interface, + DBUS_TYPE_STRING, &percentage_property, DBUS_TYPE_INVALID); + dbus_connection_send_with_reply(this->conn, m, &call, -1); + dbus_pending_call_set_notify(call, upower_get_percentage_properties_reply, this, NULL); + dbus_message_unref(m); + } + } + } else if (dbus_message_is_signal(m, DBUS_INTERFACE_PROPERTIES, DBUS_SIGNAL_PROPERTIES_CHANGED)) { + const char *path; + DBusMessageIter iface_i, props_i; + const char *interface; + + if (!dbus_message_iter_init(m, &iface_i) || !spa_streq(dbus_message_get_signature(m), "sa{sv}as")) { + spa_log_error(this->log, "Invalid signature found in PropertiesChanged"); + goto finish; + } + + dbus_message_iter_get_basic(&iface_i, &interface); + dbus_message_iter_next(&iface_i); + spa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY); + + dbus_message_iter_recurse(&iface_i, &props_i); + + path = dbus_message_get_path(m); + + if (spa_streq(interface, UPOWER_DEVICE_INTERFACE)) { + spa_log_debug(this->log, "Properties changed on %s", path); + + while (dbus_message_iter_get_arg_type(&props_i) != DBUS_TYPE_INVALID) { + DBusMessageIter i, value_i; + const char *key; + + dbus_message_iter_recurse(&props_i, &i); + + dbus_message_iter_get_basic(&i, &key); + dbus_message_iter_next(&i); + dbus_message_iter_recurse(&i, &value_i); + + if(spa_streq(key, "Percentage")) + upower_parse_percentage(this, &value_i); + + dbus_message_iter_next(&props_i); + } + } + } + +finish: + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static int add_filters(struct impl *this) +{ + DBusError err; + + if (this->filters_added) + return 0; + + dbus_error_init(&err); + + if (!dbus_connection_add_filter(this->conn, upower_filter_cb, this, NULL)) { + spa_log_error(this->log, "failed to add filter function"); + goto fail; + } + + dbus_bus_add_match(this->conn, + "type='signal',sender='org.freedesktop.DBus'," + "interface='org.freedesktop.DBus',member='NameOwnerChanged'," "arg0='" UPOWER_SERVICE "'", &err); + dbus_bus_add_match(this->conn, + "type='signal',sender='" UPOWER_SERVICE "'," + "interface='" DBUS_INTERFACE_PROPERTIES "',member='" DBUS_SIGNAL_PROPERTIES_CHANGED "'," + "path='" UPOWER_DISPLAY_DEVICE_OBJECT "',arg0='" UPOWER_DEVICE_INTERFACE "'", &err); + + this->filters_added = true; + + return 0; + +fail: + dbus_error_free(&err); + return -EIO; +} + +static bool is_dbus_service_available(struct impl *this, const char *service) +{ + DBusMessage *m, *r; + DBusError err; + bool success = false; + + m = dbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus", + "org.freedesktop.DBus", "NameHasOwner"); + if (m == NULL) + return false; + dbus_message_append_args(m, DBUS_TYPE_STRING, &service, DBUS_TYPE_INVALID); + + dbus_error_init(&err); + r = dbus_connection_send_with_reply_and_block(this->conn, m, -1, &err); + dbus_message_unref(m); + m = NULL; + + if (r == NULL) { + spa_log_info(this->log, "NameHasOwner failed for %s", service); + dbus_error_free(&err); + goto finish; + } + + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + spa_log_error(this->log, "NameHasOwner() returned error: %s", dbus_message_get_error_name(r)); + goto finish; + } + + if (!dbus_message_get_args(r, &err, + DBUS_TYPE_BOOLEAN, &success, + DBUS_TYPE_INVALID)) { + spa_log_error(this->log, "Failed to parse NameHasOwner() reply: %s", err.message); + dbus_error_free(&err); + goto finish; + } + +finish: + if (r) + dbus_message_unref(r); + + return success; +} + +void *upower_register(struct spa_log *log, + void *dbus_connection, + void (*set_battery_level)(unsigned int level, void *user_data), + void *user_data) +{ + struct impl *this; + + spa_assert(log); + spa_assert(dbus_connection); + spa_assert(set_battery_level); + spa_assert(user_data); + + this = calloc(1, sizeof(struct impl)); + if (this == NULL) + return NULL; + + this->log = log; + this->conn = dbus_connection; + this->set_battery_level = set_battery_level; + this->user_data = user_data; + + if (add_filters(this) < 0) { + goto fail4; + } + + if (is_dbus_service_available(this, UPOWER_SERVICE)) { + DBusMessage *m; + DBusPendingCall *call; + static const char* upower_device_interface = UPOWER_DEVICE_INTERFACE; + static const char* percentage_property = "Percentage"; + + m = dbus_message_new_method_call(UPOWER_SERVICE, UPOWER_DISPLAY_DEVICE_OBJECT, DBUS_INTERFACE_PROPERTIES, "Get"); + if (m == NULL) + goto fail4; + dbus_message_append_args(m, DBUS_TYPE_STRING, &upower_device_interface, + DBUS_TYPE_STRING, &percentage_property, DBUS_TYPE_INVALID); + dbus_connection_send_with_reply(this->conn, m, &call, -1); + dbus_pending_call_set_notify(call, upower_get_percentage_properties_reply, this, NULL); + dbus_message_unref(m); + } + + return this; + +fail4: + free(this); + return NULL; +} + +void upower_unregister(void *data) +{ + struct impl *this = data; + + if (this->filters_added) { + dbus_connection_remove_filter(this->conn, upower_filter_cb, this); + this->filters_added = false; + } + free(this); +}
View file
pipewire-0.3.60.tar.gz/spa/plugins/bluez5/upower.h
Added
@@ -0,0 +1,36 @@ +/* Spa Bluez5 UPower proxy + * + * 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_UPOWER_H_ +#define SPA_BLUEZ5_UPOWER_H_ + +#include "defs.h" + +void *upower_register(struct spa_log *log, + void *dbus_connection, + void (*set_battery_level)(unsigned int level, void *user_data), + void *user_data); +void upower_unregister(void *data); + +#endif \ No newline at end of file
View file
pipewire-0.3.59.tar.gz/spa/plugins/libcamera/libcamera-source.cpp -> pipewire-0.3.60.tar.gz/spa/plugins/libcamera/libcamera-source.cpp
Changed
@@ -112,7 +112,14 @@ 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; +#define PORT_PropInfo 0 +#define PORT_EnumFormat 1 +#define PORT_Meta 2 +#define PORT_IO 3 +#define PORT_Format 4 +#define PORT_Buffers 5 +#define N_PORT_PARAMS 6 + struct spa_param_info paramsN_PORT_PARAMS; uint32_t fmt_index = 0; PixelFormat enum_fmt; @@ -123,16 +130,16 @@ { 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); + paramsPORT_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); + paramsPORT_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); + paramsPORT_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); + paramsPORT_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); + paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + paramsPORT_Buffers = 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; + info.n_params = N_PORT_PARAMS; } }; @@ -149,7 +156,12 @@ SPA_NODE_CHANGE_MASK_PROPS | SPA_NODE_CHANGE_MASK_PARAMS; struct spa_node_info info = SPA_NODE_INFO_INIT(); - struct spa_param_info params8; +#define NODE_PropInfo 0 +#define NODE_Props 1 +#define NODE_EnumFormat 2 +#define NODE_Format 3 +#define N_NODE_PARAMS 4 + struct spa_param_info paramsN_NODE_PARAMS; std::string device_id; std::string device_name; @@ -175,6 +187,7 @@ struct spa_source source = {}; + ControlList ctrls; bool active = false; bool acquired = false; @@ -191,6 +204,55 @@ #include "libcamera-utils.cpp" +static int port_get_format(struct impl *impl, struct port *port, + uint32_t index, + const struct spa_pod *filter, + struct spa_pod **param, + struct spa_pod_builder *builder) +{ + struct spa_pod_frame f; + + 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), + 0); + + 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), + 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), + 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), + 0); + break; + default: + return -EIO; + } + + *param = (struct spa_pod*)spa_pod_builder_pop(builder, &f); + + return 1; +} + static int impl_node_enum_params(void *object, int seq, uint32_t id, uint32_t start, uint32_t num, const struct spa_pod *filter) @@ -201,6 +263,7 @@ uint8_t buffer1024; struct spa_result_node_params result; uint32_t count = 0; + int res; spa_return_val_if_fail(impl != NULL, -EINVAL); spa_return_val_if_fail(num != 0, -EINVAL); @@ -231,7 +294,9 @@ SPA_PROP_INFO_type, SPA_POD_String(impl->device_name.c_str())); break; default: - return 0; + return spa_libcamera_enum_controls(impl, + GET_OUT_PORT(impl, 0), + seq, result.index - 2, num, filter); } break; } @@ -249,6 +314,13 @@ } break; } + case SPA_PARAM_EnumFormat: + return spa_libcamera_enum_format(impl, GET_OUT_PORT(impl, 0), + seq, start, num, filter); + case SPA_PARAM_Format: + if ((res = port_get_format(impl, GET_OUT_PORT(impl, 0), result.index, filter, ¶m, &b)) <= 0) + return res; + break; default: return -ENOENT; } @@ -275,22 +347,28 @@ switch (id) { case SPA_PARAM_Props: { + struct spa_pod_object *obj = (struct spa_pod_object *) param; + struct spa_pod_prop *prop; + if (param == NULL) { impl->device_id.clear(); impl->device_name.clear(); return 0; } - - char device128; - int res = spa_pod_parse_object(param, - SPA_TYPE_OBJECT_Props, NULL, - SPA_PROP_device, SPA_POD_OPT_Stringn(device, sizeof(device))); - - if (res < 0) - return res; - - impl->device_id = device; - + SPA_POD_OBJECT_FOREACH(obj, prop) { + char device128; + + switch (prop->key) { + case SPA_PROP_device: + strncpy(device, (char *)SPA_POD_CONTENTS(struct spa_pod_string, &prop->value), + sizeof(device)-1); + impl->device_id = device; + break; + default: + spa_libcamera_set_control(impl, prop); + break; + } + } break; } default: @@ -356,7 +434,6 @@ { SPA_KEY_DEVICE_API, "libcamera" }, { SPA_KEY_MEDIA_CLASS, "Video/Source" }, { SPA_KEY_MEDIA_ROLE, "Camera" }, - { SPA_KEY_NODE_PAUSE_ON_IDLE, "false" }, { SPA_KEY_NODE_DRIVER, "true" }, }; @@ -444,55 +521,6 @@ return -ENOTSUP; } -static int port_get_format(struct impl *impl, struct port *port, - uint32_t index, - const struct spa_pod *filter, - struct spa_pod **param, - struct spa_pod_builder *builder) -{ - struct spa_pod_frame f; - - 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), - 0); - - 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), - 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), - 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), - 0); - break; - default: - return -EIO; - } - - *param = (struct spa_pod*)spa_pod_builder_pop(builder, &f); - - return 1; -} - static int impl_node_port_enum_params(void *object, int seq, enum spa_direction direction, uint32_t port_id, @@ -621,6 +649,7 @@ spa_libcamera_close(impl); goto done; } else { + spa_zero(info); if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) return res; @@ -681,15 +710,19 @@ } done: + impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; 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); + impl->paramsNODE_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); + port->paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); + port->paramsPORT_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); } else { - port->params4 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params5 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); + impl->paramsNODE_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + port->paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + port->paramsPORT_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); } emit_port_info(impl, port, false); + emit_node_info(impl, false); return 0; } @@ -939,13 +972,15 @@ 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); + paramsNODE_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); + paramsNODE_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); + paramsNODE_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); + paramsNODE_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); info.max_output_ports = 1; info.flags = SPA_NODE_FLAG_RT; info.params = params; - info.n_params = 2; + info.n_params = N_NODE_PARAMS; } static size_t
View file
pipewire-0.3.59.tar.gz/spa/plugins/libcamera/libcamera-utils.cpp -> pipewire-0.3.60.tar.gz/spa/plugins/libcamera/libcamera-utils.cpp
Changed
@@ -32,6 +32,7 @@ #include <errno.h> #include <sys/mman.h> #include <poll.h> +#include <limits.h> #include <linux/media.h> @@ -104,6 +105,8 @@ impl->pendingRequests.push_back(request); return 0; } else { + request->controls().merge(impl->ctrls); + impl->ctrls.clear(); if ((res = impl->camera->queueRequest(request)) < 0) { spa_log_warn(impl->log, "can't queue buffer %u: %s", buffer_id, spa_strerror(res)); @@ -238,6 +241,14 @@ return NULL; } +static int score_size(Size &a, Size &b) +{ + int x, y; + x = (int)a.width - (int)b.width; + y = (int)a.height - (int)b.height; + return x * x + y * y; +} + static int spa_libcamera_enum_format(struct impl *impl, struct port *port, int seq, uint32_t start, uint32_t num, const struct spa_pod *filter) @@ -249,7 +260,7 @@ struct spa_pod_frame f2; struct spa_result_node_params result; struct spa_pod *fmt; - uint32_t count = 0; + uint32_t i, count = 0, num_sizes; PixelFormat format; Size frameSize; SizeRange sizeRange = SizeRange(); @@ -284,8 +295,23 @@ goto next_fmt; } - if (port->size_index < formats.sizes(format).size()) { - frameSize = formats.sizes(format)port->size_index; + num_sizes = formats.sizes(format).size(); + if (num_sizes > 0 && port->size_index <= num_sizes) { + if (port->size_index == 0) { + Size wanted = Size(640, 480), test; + int score, best = INT_MAX; + for (i = 0; i < num_sizes; i++) { + test = formats.sizes(format)i; + score = score_size(wanted, test); + if (score < best) { + best = score; + frameSize = test; + } + } + } + else { + frameSize = formats.sizes(format)port->size_index - 1; + } } else if (port->size_index < 1) { sizeRange = formats.range(format); if (sizeRange.hStep == 0 || sizeRange.vStep == 0) { @@ -437,9 +463,158 @@ uint32_t start, uint32_t num, const struct spa_pod *filter) { + const ControlInfoMap &info = impl->camera->controls(); + uint8_t buffer1024; + struct spa_pod_builder b = { 0 }; + struct spa_pod_frame f2; + struct spa_result_node_params result; + struct spa_pod *ctrl; + uint32_t count = 0, skip; + int res; + const ControlId *ctrl_id; + ControlInfo ctrl_info; + + result.id = SPA_PARAM_PropInfo; + result.next = start; + + auto it = info.begin(); + for (skip = result.next; skip; skip--) + it++; + + if (false) { +next: + it++; + } + result.index = result.next++; + if (it == info.end()) + goto enum_end; + + ctrl_id = it->first; + ctrl_info = it->second; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + 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(ctrl_id->id()), + SPA_PROP_INFO_description, SPA_POD_String(ctrl_id->name().c_str()), + 0); + + switch (ctrl_id->type()) { + case ControlTypeBool: + spa_pod_builder_add(&b, + SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool( + (bool)ctrl_info.def().get<bool>()), + 0); + break; + case ControlTypeFloat: + spa_pod_builder_add(&b, + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float( + (float)ctrl_info.def().get<float>(), + (float)ctrl_info.min().get<float>(), + (float)ctrl_info.max().get<float>()), + 0); + break; + case ControlTypeInteger32: + spa_pod_builder_add(&b, + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int( + (int32_t)ctrl_info.def().get<int32_t>(), + (int32_t)ctrl_info.min().get<int32_t>(), + (int32_t)ctrl_info.max().get<int32_t>()), + 0); + break; + default: + goto next; + } + + ctrl = (struct spa_pod*) spa_pod_builder_pop(&b, &f0); + + if (spa_pod_filter(&b, &result.param, ctrl, filter) < 0) + goto next; + + spa_node_emit_result(&impl->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); + + if (++count != num) + goto next; + +enum_end: + res = 0; + return res; +} + +struct val { + uint32_t type; + float f_val; + int32_t i_val; + bool b_val; + uint32_t id; +}; + +static int do_update_ctrls(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct impl *impl = (struct impl *)user_data; + const struct val *d = (const struct val *)data; + switch (d->type) { + case ControlTypeBool: + impl->ctrls.set(d->id, d->b_val); + break; + case ControlTypeFloat: + impl->ctrls.set(d->id, d->f_val); + break; + case ControlTypeInteger32: + //impl->ctrls.set(d->id, (int32_t)d->i_val); + break; + default: + break; + } return 0; } +static int +spa_libcamera_set_control(struct impl *impl, const struct spa_pod_prop *prop) +{ + const ControlInfoMap &info = impl->camera->controls(); + const ControlId *ctrl_id; + int res; + struct val d; + + auto v = info.idmap().find(prop->key); + if (v == info.idmap().end()) + return -ENOENT; + + ctrl_id = v->second; + + d.type = ctrl_id->type(); + d.id = ctrl_id->id(); + + switch (d.type) { + case ControlTypeBool: + if ((res = spa_pod_get_bool(&prop->value, &d.b_val)) < 0) + goto done; + break; + case ControlTypeFloat: + if ((res = spa_pod_get_float(&prop->value, &d.f_val)) < 0) + goto done; + break; + case ControlTypeInteger32: + if ((res = spa_pod_get_int(&prop->value, &d.i_val)) < 0) + goto done; + break; + default: + res = -EINVAL; + goto done; + } + spa_loop_invoke(impl->data_loop, do_update_ctrls, 0, &d, sizeof(d), true, impl); + res = 0; +done: + return res; +} + + static void libcamera_on_fd_events(struct spa_source *source) { struct impl *impl = (struct impl*) source->data; @@ -477,7 +652,12 @@ spa_list_append(&port->queue, &b->link); io = port->io; - if (io != NULL && io->status != SPA_STATUS_HAVE_DATA) { + if (io == NULL) { + b = spa_list_first(&port->queue, struct buffer, link); + spa_list_remove(&b->link); + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING); + spa_libcamera_buffer_recycle(impl, port, b->id); + } else if (io->status != SPA_STATUS_HAVE_DATA) { if (io->buffer_id < port->n_buffers) spa_libcamera_buffer_recycle(impl, port, io->buffer_id); @@ -614,8 +794,14 @@ spa_log_debug(impl->log, "request complete"); + buffer_id = request->cookie(); + b = &port->buffersbuffer_id; + if ((request->status() == Request::RequestCancelled)) { spa_log_debug(impl->log, "Request was cancelled"); + request->reuse(); + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING); + spa_libcamera_buffer_recycle(impl, port, b->id); return; } FrameBuffer *buffer = request->findBuffer(stream); @@ -625,9 +811,7 @@ } const FrameMetadata &fmd = buffer->metadata(); - buffer_id = request->cookie(); - b = &port->buffersbuffer_id; if (impl->clock) { impl->clock->nsec = fmd.timestamp; @@ -721,11 +905,14 @@ return 0; } + impl->active = false; spa_log_info(impl->log, "stopping camera %s", impl->device_id.c_str()); impl->pendingRequests.clear(); - if ((res = impl->camera->stop()) < 0) - return res == -EACCES ? -EBUSY : res; + if ((res = impl->camera->stop()) < 0) { + spa_log_warn(impl->log, "error stopping camera %s: %s", + impl->device_id.c_str(), spa_strerror(res)); + } impl->camera->requestCompleted.disconnect(impl, &impl::requestComplete); @@ -736,7 +923,6 @@ } spa_list_init(&port->queue); - impl->active = false; return 0; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/support/cpu-x86.c -> pipewire-0.3.60.tar.gz/spa/plugins/support/cpu-x86.c
Changed
@@ -56,6 +56,7 @@ } else if (family == 0x06) model += extended_model; } + (void)model; flags = 0; if (ecx & bit_SSE3)
View file
pipewire-0.3.59.tar.gz/spa/plugins/support/cpu.c -> pipewire-0.3.60.tar.gz/spa/plugins/support/cpu.c
Changed
@@ -171,19 +171,18 @@ /* https://wiki.freebsd.org/bhyve */ { "BHYVE", SPA_CPU_VM_BHYVE }, }; - uint32_t i, j; - for (i = 0; i < SPA_N_ELEMENTS(dmi_vendors); i++) { + SPA_FOR_EACH_ELEMENT_VAR(dmi_vendors, dv) { char buffer256, *s; - if ((s = read_file(dmi_vendorsi, buffer, sizeof(buffer))) == NULL) + if ((s = read_file(*dv, buffer, sizeof(buffer))) == NULL) continue; - for (j = 0; j < SPA_N_ELEMENTS(dmi_vendor_table); j++) { - if (spa_strstartswith(s, dmi_vendor_tablej.vendor)) { + SPA_FOR_EACH_ELEMENT_VAR(dmi_vendor_table, t) { + if (spa_strstartswith(s, t->vendor)) { spa_log_debug(impl->log, "Virtualization %s found in DMI (%s)", - s, dmi_vendorsi); - impl->vm_type = dmi_vendor_tablej.id; + s, *dv); + impl->vm_type = t->id; goto done; } }
View file
pipewire-0.3.59.tar.gz/spa/plugins/support/loop.c -> pipewire-0.3.60.tar.gz/spa/plugins/support/loop.c
Changed
@@ -186,8 +186,8 @@ if (block) { if ((res = spa_system_eventfd_write(impl->system, impl->ack_fd, 1)) < 0) - spa_log_warn(impl->log, "%p: failed to write event fd: %s", - impl, spa_strerror(res)); + spa_log_warn(impl->log, "%p: failed to write event fd:%d: %s", + impl, impl->ack_fd, spa_strerror(res)); } } impl->flushing = false; @@ -283,8 +283,8 @@ spa_loop_control_hook_before(&impl->hooks_list); if ((res = spa_system_eventfd_read(impl->system, impl->ack_fd, &count)) < 0) - spa_log_warn(impl->log, "%p: failed to read event fd: %s", - impl, spa_strerror(res)); + spa_log_warn(impl->log, "%p: failed to read event fd:%d: %s", + impl, impl->ack_fd, spa_strerror(res)); spa_loop_control_hook_after(&impl->hooks_list); @@ -526,12 +526,12 @@ if (enabled && !s->enabled) { if ((res = spa_system_eventfd_write(s->impl->system, source->fd, 1)) < 0) - spa_log_warn(s->impl->log, "%p: failed to write idle fd %d: %s", + spa_log_warn(s->impl->log, "%p: failed to write idle fd:%d: %s", source, source->fd, spa_strerror(res)); } else if (!enabled && s->enabled) { uint64_t count; if ((res = spa_system_eventfd_read(s->impl->system, source->fd, &count)) < 0) - spa_log_warn(s->impl->log, "%p: failed to read idle fd %d: %s", + spa_log_warn(s->impl->log, "%p: failed to read idle fd:%d: %s", source, source->fd, spa_strerror(res)); } s->enabled = enabled; @@ -586,7 +586,7 @@ int res; if ((res = spa_system_eventfd_read(s->impl->system, source->fd, &count)) < 0) - spa_log_warn(s->impl->log, "%p: failed to read event fd %d: %s", + spa_log_warn(s->impl->log, "%p: failed to read event fd:%d: %s", source, source->fd, spa_strerror(res)); s->func.event(source->data, count); @@ -639,7 +639,7 @@ spa_assert(source->func == source_event_func); if (SPA_UNLIKELY((res = spa_system_eventfd_write(s->impl->system, source->fd, 1)) < 0)) - spa_log_warn(s->impl->log, "%p: failed to write event fd %d: %s", + spa_log_warn(s->impl->log, "%p: failed to write event fd:%d: %s", source, source->fd, spa_strerror(res)); return res; } @@ -652,7 +652,7 @@ if (SPA_UNLIKELY((res = spa_system_timerfd_read(s->impl->system, source->fd, &expirations)) < 0)) - spa_log_warn(s->impl->log, "%p: failed to read timer fd %d: %s", + spa_log_warn(s->impl->log, "%p: failed to read timer fd:%d: %s", source, source->fd, spa_strerror(res)); s->func.timer(source->data, expirations); @@ -732,7 +732,7 @@ int res, signal_number = 0; if ((res = spa_system_signalfd_read(s->impl->system, source->fd, &signal_number)) < 0) - spa_log_warn(s->impl->log, "%p: failed to read signal fd %d: %s", + spa_log_warn(s->impl->log, "%p: failed to read signal fd:%d: %s", source, source->fd, spa_strerror(res)); s->func.signal(source->data, signal_number); @@ -865,11 +865,9 @@ impl = (struct impl *) handle; - if (impl->enter_count != 0) - spa_log_warn(impl->log, "%p: loop is entered %d times", - impl, impl->enter_count); - - spa_assert(!impl->polling); + if (impl->enter_count != 0 || impl->polling) + spa_log_warn(impl->log, "%p: loop is entered %d times polling:%d", + impl, impl->enter_count, impl->polling); spa_list_consume(source, &impl->source_list, link) loop_destroy_source(impl, &source->source);
View file
pipewire-0.3.59.tar.gz/spa/plugins/support/null-audio-sink.c -> pipewire-0.3.60.tar.gz/spa/plugins/support/null-audio-sink.c
Changed
@@ -610,6 +610,11 @@ if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) return -EINVAL; + if (info.info.raw.rate == 0 || + info.info.raw.channels == 0 || + info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + return -EINVAL; + if (info.info.raw.format == SPA_AUDIO_FORMAT_F32) { port->bpf = 4 * info.info.raw.channels; port->blocks = 1;
View file
pipewire-0.3.59.tar.gz/spa/plugins/v4l2/v4l2-source.c -> pipewire-0.3.60.tar.gz/spa/plugins/v4l2/v4l2-source.c
Changed
@@ -145,7 +145,8 @@ #define NODE_PropInfo 0 #define NODE_Props 1 #define NODE_EnumFormat 2 -#define N_NODE_PARAMS 3 +#define NODE_Format 3 +#define N_NODE_PARAMS 4 struct spa_param_info paramsN_NODE_PARAMS; struct props props; @@ -167,6 +168,56 @@ #include "v4l2-utils.c" +static int port_get_format(struct port *port, + uint32_t index, + const struct spa_pod *filter, + struct spa_pod **param, + struct spa_pod_builder *builder) +{ + struct spa_pod_frame f; + + if (!port->have_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), + 0); + + 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), + 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), + 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), + 0); + break; + default: + return -EIO; + } + + *param = spa_pod_builder_pop(builder, &f); + + return 1; +} + + static int impl_node_enum_params(void *object, int seq, uint32_t id, uint32_t start, uint32_t num, const struct spa_pod *filter) @@ -177,6 +228,7 @@ 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); @@ -216,9 +268,9 @@ SPA_PROP_INFO_type, SPA_POD_Int(p->device_fd)); break; default: - return 0; + return spa_v4l2_enum_controls(this, seq, result.index - 3, num, filter); } - return spa_v4l2_enum_controls(this, seq, start, num, filter); + break; } case SPA_PARAM_Props: { @@ -239,6 +291,11 @@ } case SPA_PARAM_EnumFormat: return spa_v4l2_enum_format(this, seq, start, num, filter); + case SPA_PARAM_Format: + if((res = port_get_format(GET_OUT_PORT(this, 0), + result.index, filter, ¶m, &b)) <= 0) + return res; + break; default: return -ENOENT; } @@ -266,14 +323,29 @@ case SPA_PARAM_Props: { struct props *p = &this->props; + struct spa_pod_object *obj = (struct spa_pod_object *) param; + struct spa_pod_prop *prop; + int res = 0; if (param == NULL) { reset_props(p); return 0; } - spa_pod_parse_object(param, - SPA_TYPE_OBJECT_Props, NULL, - SPA_PROP_device, SPA_POD_OPT_Stringn(p->device, sizeof(p->device))); + SPA_POD_OBJECT_FOREACH(obj, prop) { + switch (prop->key) { + case SPA_PROP_device: + strncpy(p->device, + (char *)SPA_POD_CONTENTS(struct spa_pod_string, &prop->value), + sizeof(p->device)-1); + break; + default: + res = spa_v4l2_set_control(this, prop->key, prop); + break; + } + if (res < 0) + return res; + } + break; } default: @@ -440,58 +512,6 @@ return -ENOTSUP; } -static int port_get_format(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t index, - const struct spa_pod *filter, - struct spa_pod **param, - struct spa_pod_builder *builder) -{ - struct impl *this = object; - struct port *port = GET_PORT(this, direction, port_id); - struct spa_pod_frame f; - - if (!port->have_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), - 0); - - 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), - 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), - 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), - 0); - break; - default: - return -EIO; - } - - *param = spa_pod_builder_pop(builder, &f); - - return 1; -} - static int impl_node_port_enum_params(void *object, int seq, enum spa_direction direction, uint32_t port_id, @@ -529,8 +549,7 @@ return spa_v4l2_enum_format(this, seq, start, num, filter); case SPA_PARAM_Format: - if((res = port_get_format(this, direction, port_id, - result.index, filter, ¶m, &b)) <= 0) + if((res = port_get_format(port, result.index, filter, ¶m, &b)) <= 0) return res; break; case SPA_PARAM_Buffers: @@ -541,7 +560,7 @@ param = spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(MAX_BUFFERS, 2, MAX_BUFFERS), + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(4, 1, MAX_BUFFERS), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), SPA_PARAM_BUFFERS_size, SPA_POD_Int(port->fmt.fmt.pix.sizeimage), SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->fmt.fmt.pix.bytesperline)); @@ -668,15 +687,19 @@ } done: + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; if (port->have_format) { port->paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); port->paramsPORT_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); + this->paramsNODE_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READ); } else { port->paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); port->paramsPORT_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); + this->paramsNODE_Format = SPA_PARAM_INFO(SPA_PARAM_Format, 0); } emit_port_info(this, port, false); + emit_node_info(this, false); return 0; } @@ -1005,8 +1028,9 @@ this->info.max_output_ports = 1; this->info.flags = SPA_NODE_FLAG_RT; this->paramsNODE_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); - this->paramsNODE_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); this->paramsNODE_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); + this->paramsNODE_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); + this->paramsNODE_Format = SPA_PARAM_INFO(SPA_PARAM_Format, 0); this->info.params = this->params; this->info.n_params = N_NODE_PARAMS; reset_props(&this->props);
View file
pipewire-0.3.59.tar.gz/spa/plugins/v4l2/v4l2-udev.c -> pipewire-0.3.60.tar.gz/spa/plugins/v4l2/v4l2-udev.c
Changed
@@ -266,7 +266,7 @@ if (str && *str) { itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_PATH, str); } - if ((str = udev_device_get_syspath(dev)) && *str) { + if ((str = udev_device_get_devpath(dev)) && *str) { itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SYSFS_PATH, str); } if ((str = udev_device_get_property_value(dev, "ID_ID")) && *str) {
View file
pipewire-0.3.59.tar.gz/spa/plugins/v4l2/v4l2-utils.c -> pipewire-0.3.60.tar.gz/spa/plugins/v4l2/v4l2-utils.c
Changed
@@ -30,7 +30,7 @@ #include <sys/mman.h> #include <poll.h> -static void v4l2_on_fd_events(struct spa_source *source); +#include <spa/utils/result.h> static int xioctl(int fd, int request, void *arg) { @@ -43,7 +43,6 @@ return err; } - int spa_v4l2_open(struct spa_v4l2_device *dev, const char *path) { struct stat st; @@ -351,11 +350,9 @@ static const struct format_info *fourcc_to_format_info(uint32_t fourcc) { - size_t i; - - for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) { - if (format_infoi.fourcc == fourcc) - return &format_infoi; + SPA_FOR_EACH_ELEMENT_VAR(format_info, i) { + if (i->fourcc == fourcc) + return i; } return NULL; } @@ -363,11 +360,9 @@ #if 0 static const struct format_info *video_format_to_format_info(uint32_t format) { - int i; - - for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) { - if (format_infoi.format == format) - return &format_infoi; + SPA_FOR_EACH_ELEMENT_VAR(format_info, i) { + if (i->format == format) + return i; } return NULL; } @@ -381,10 +376,11 @@ size_t i; for (i = startidx; i < SPA_N_ELEMENTS(format_info); i++) { - if ((format_infoi.media_type == type) && - (format_infoi.media_subtype == subtype) && - (format == 0 || format_infoi.format == format)) - return &format_infoi; + const struct format_info *fi = &format_infoi; + if ((fi->media_type == type) && + (fi->media_subtype == subtype) && + (format == 0 || fi->format == format)) + return fi; } return NULL; } @@ -895,8 +891,9 @@ bool match; spa_zero(fmt); - spa_zero(streamparm); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + spa_zero(streamparm); streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; switch (format->media_subtype) { @@ -929,7 +926,6 @@ return -EINVAL; } - fmt.fmt.pix.pixelformat = info->fourcc; fmt.fmt.pix.field = V4L2_FIELD_ANY; fmt.fmt.pix.width = size->width; @@ -1046,28 +1042,38 @@ return res; } +static struct { + uint32_t v4l2_id; + uint32_t spa_id; +} control_map = { + { V4L2_CID_BRIGHTNESS, SPA_PROP_brightness }, + { V4L2_CID_CONTRAST, SPA_PROP_contrast }, + { V4L2_CID_SATURATION, SPA_PROP_saturation }, + { V4L2_CID_HUE, SPA_PROP_hue }, + { V4L2_CID_GAMMA, SPA_PROP_gamma }, + { V4L2_CID_EXPOSURE, SPA_PROP_exposure }, + { V4L2_CID_GAIN, SPA_PROP_gain }, + { V4L2_CID_SHARPNESS, SPA_PROP_sharpness }, +}; + static uint32_t control_to_prop_id(struct impl *impl, uint32_t control_id) { - switch (control_id) { - case V4L2_CID_BRIGHTNESS: - return SPA_PROP_brightness; - case V4L2_CID_CONTRAST: - return SPA_PROP_contrast; - case V4L2_CID_SATURATION: - return SPA_PROP_saturation; - case V4L2_CID_HUE: - return SPA_PROP_hue; - case V4L2_CID_GAMMA: - return SPA_PROP_gamma; - case V4L2_CID_EXPOSURE: - return SPA_PROP_exposure; - case V4L2_CID_GAIN: - return SPA_PROP_gain; - case V4L2_CID_SHARPNESS: - return SPA_PROP_sharpness; - default: - return SPA_PROP_START_CUSTOM + control_id; + SPA_FOR_EACH_ELEMENT_VAR(control_map, c) { + if (c->v4l2_id == control_id) + return c->spa_id; + } + return SPA_PROP_START_CUSTOM + control_id; +} + +static uint32_t prop_id_to_control(struct impl *impl, uint32_t prop_id) +{ + SPA_FOR_EACH_ELEMENT_VAR(control_map, c) { + if (c->spa_id == prop_id) + return c->v4l2_id; } + if (prop_id >= SPA_PROP_START_CUSTOM) + return prop_id - SPA_PROP_START_CUSTOM; + return SPA_ID_INVALID; } static int @@ -1231,6 +1237,56 @@ return res; } +static int +spa_v4l2_set_control(struct impl *this, uint32_t id, + const struct spa_pod_prop *prop) +{ + struct port *port = &this->out_ports0; + struct spa_v4l2_device *dev = &port->dev; + struct v4l2_control control; + int res; + + spa_zero(control); + control.id = prop_id_to_control(this, prop->key); + if (control.id == SPA_ID_INVALID) + return -ENOENT; + + if ((res = spa_v4l2_open(dev, this->props.device)) < 0) + return res; + + switch (SPA_POD_TYPE(&prop->value)) { + case SPA_TYPE_Bool: + { + bool val; + if ((res = spa_pod_get_bool(&prop->value, &val)) < 0) + goto done; + control.value = val; + break; + } + case SPA_TYPE_Int: + { + int32_t val; + if ((res = spa_pod_get_int(&prop->value, &val)) < 0) + goto done; + control.value = val; + break; + } + default: + res = -EINVAL; + goto done; + } + if (xioctl(dev->fd, VIDIOC_S_CTRL, &control) < 0) { + res = -errno; + goto done; + } + + res = 0; + +done: + spa_v4l2_close(dev); + return res; +} + static int mmap_read(struct impl *this) { struct port *port = &this->out_ports0; @@ -1310,7 +1366,13 @@ return; io = port->io; - if (io != NULL && io->status != SPA_STATUS_HAVE_DATA) { + if (io == NULL) { + b = spa_list_first(&port->queue, struct buffer, link); + spa_list_remove(&b->link); + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING); + spa_v4l2_buffer_recycle(this, b->id); + } + else if (io->status != SPA_STATUS_HAVE_DATA) { if (io->buffer_id < port->n_buffers) spa_v4l2_buffer_recycle(this, io->buffer_id); @@ -1445,11 +1507,10 @@ } spa_log_debug(this->log, "got %d buffers", reqbuf.count); - n_buffers = reqbuf.count; - if (n_buffers < 2) { - spa_log_error(this->log, "'%s' can't allocate enough buffers (%d)", - this->props.device, n_buffers); + if (reqbuf.count < n_buffers) { + spa_log_error(this->log, "'%s' can't allocate enough buffers (%d < %d)", + this->props.device, reqbuf.count, n_buffers); return -ENOMEM; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/videoconvert/videoadapter.c -> pipewire-0.3.60.tar.gz/spa/plugins/videoconvert/videoadapter.c
Changed
@@ -562,24 +562,6 @@ return 0; } -static int format_video_raw_parse_opt(const struct spa_pod *format, struct spa_video_info_raw *info) -{ - uint32_t media_type, media_subtype; - int res; - if ((res = spa_format_parse(format, &media_type, &media_subtype)) < 0) - return res; - if (media_type != SPA_MEDIA_TYPE_video || - media_subtype != SPA_MEDIA_SUBTYPE_raw) - return -ENOTSUP; - - spa_zero(*info); - res = spa_pod_parse_object(format, - SPA_TYPE_OBJECT_Format, NULL, - SPA_FORMAT_VIDEO_format, SPA_POD_OPT_Id(&info->format), - SPA_FORMAT_VIDEO_size, SPA_POD_OPT_Int(&info->size)); - return res; -} - static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param) { @@ -599,8 +581,9 @@ if ((res = spa_format_parse(param, &info.media_type, &info.media_subtype)) < 0) return res; if (info.media_type != SPA_MEDIA_TYPE_video || - info.media_subtype != SPA_MEDIA_SUBTYPE_raw) - return -EINVAL; + info.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return -EINVAL; + if (spa_format_video_raw_parse(param, &info.info.raw) < 0) return -EINVAL; @@ -627,7 +610,15 @@ if (format) { struct spa_video_info info; - if (format_video_raw_parse_opt(format, &info.info.raw) >= 0) + + spa_zero(info); + if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) + return res; + if (info.media_type != SPA_MEDIA_TYPE_video || + info.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return -ENOTSUP; + + if (spa_format_video_raw_parse(format, &info.info.raw) >= 0) this->default_format = info; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/videotestsrc/videotestsrc.c -> pipewire-0.3.60.tar.gz/spa/plugins/videotestsrc/videotestsrc.c
Changed
@@ -643,6 +643,12 @@ else return -EINVAL; + if (info.info.raw.size.width == 0 || + info.info.raw.size.height == 0 || + info.info.raw.framerate.num == 0 || + info.info.raw.framerate.denom == 0) + return -EINVAL; + port->current_format = info; port->have_format = true; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/volume/volume.c -> pipewire-0.3.60.tar.gz/spa/plugins/volume/volume.c
Changed
@@ -321,10 +321,9 @@ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, 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_CHOICE_ENUM_Id(3, + SPA_FORMAT_AUDIO_format, SPA_POD_CHOICE_ENUM_Id(2, SPA_AUDIO_FORMAT_S16, - SPA_AUDIO_FORMAT_S16, - SPA_AUDIO_FORMAT_S32), + SPA_AUDIO_FORMAT_S16), SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_RANGE_Int( DEFAULT_RATE, 1, INT32_MAX), SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int( @@ -472,6 +471,11 @@ if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) return -EINVAL; + if (info.info.raw.format != SPA_AUDIO_FORMAT_S16 || + info.info.raw.channels == 0 || + info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + return -EINVAL; + this->bpf = 2 * info.info.raw.channels; this->current_format = info; port->have_format = true;
View file
pipewire-0.3.59.tar.gz/spa/tests/benchmark-dict.c -> pipewire-0.3.60.tar.gz/spa/tests/benchmark-dict.c
Changed
@@ -39,7 +39,7 @@ static struct spa_dict_item itemsMAX_ITEMS; static char valuesMAX_ITEMS32; -static void gen_values() +static void gen_values(void) { uint32_t i, j, idx; static const char chars = "abcdefghijklmnopqrstuvwxyz.:*ABCDEFGHIJKLMNOPQRSTUVWXYZ";
View file
pipewire-0.3.59.tar.gz/spa/tests/benchmark-pod.c -> pipewire-0.3.60.tar.gz/spa/tests/benchmark-pod.c
Changed
@@ -37,7 +37,7 @@ #define MAX_COUNT 10000000 -static void test_builder() +static void test_builder(void) { uint8_t buffer1024; struct spa_pod_builder b = { NULL, }; @@ -90,7 +90,7 @@ t2 - t1, count, count * (uint64_t)SPA_NSEC_PER_SEC / (t2 - t1)); } -static void test_builder2() +static void test_builder2(void) { uint8_t buffer1024; struct spa_pod_builder b = { NULL, }; @@ -131,7 +131,7 @@ t2 - t1, count, count * (uint64_t)SPA_NSEC_PER_SEC / (t2 - t1)); } -static void test_parse() +static void test_parse(void) { uint8_t buffer1024; struct spa_pod_builder b = { NULL, }; @@ -216,7 +216,7 @@ t2 - t1, count, count * (uint64_t)SPA_NSEC_PER_SEC / (t2 - t1)); } -static void test_parser() +static void test_parser(void) { uint8_t buffer1024; struct spa_pod_builder b = { NULL, };
View file
pipewire-0.3.59.tar.gz/src/daemon/jack.conf.in -> pipewire-0.3.60.tar.gz/src/daemon/jack.conf.in
Changed
@@ -83,6 +83,7 @@ #jack.locked-process = true #jack.default-as-system = false #jack.fix-midi-events = true + #jack.global-buffer-size = false } # client specific properties @@ -102,4 +103,16 @@ } } } + { + matches = + { + application.process.binary = "jack_bufsize" + } + + actions = { + update-props = { + jack.global-buffer-size = true + } + } + }
View file
pipewire-0.3.59.tar.gz/src/daemon/pipewire.conf.in -> pipewire-0.3.60.tar.gz/src/daemon/pipewire.conf.in
Changed
@@ -159,14 +159,15 @@ { name = libpipewire-module-session-manager } # Use libcanberra to play X11 Bell - #{ name = libpipewire-module-x11-bell - # args = { - # #sink.name = "@DEFAULT_SINK@" - # #sample.name = "bell-window-system" - # #x11.display = null - # #x11.xauthority = null - # } - #} + { name = libpipewire-module-x11-bell + args = { + #sink.name = "@DEFAULT_SINK@" + #sample.name = "bell-window-system" + #x11.display = null + #x11.xauthority = null + } + flags = ifexists nofail + } context.objects =
View file
pipewire-0.3.59.tar.gz/src/examples/export-sink.c -> pipewire-0.3.60.tar.gz/src/examples/export-sink.c
Changed
@@ -303,11 +303,10 @@ Uint32 sdl_format; void *dest; - d->info.change_mask = SPA_PORT_CHANGE_MASK_PARAMS; if (format == NULL) { + spa_zero(d->format); SDL_DestroyTexture(d->texture); - d->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - d->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); + d->texture = NULL; } else { spa_debug_format(0, NULL, format); @@ -316,6 +315,9 @@ sdl_format = id_to_sdl_format(d->format.format); if (sdl_format == SDL_PIXELFORMAT_UNKNOWN) return -EINVAL; + if (d->format.size.width == 0 || + d->format.size.height == 0) + return -EINVAL; d->texture = SDL_CreateTexture(d->renderer, sdl_format, @@ -325,9 +327,16 @@ SDL_LockTexture(d->texture, NULL, &dest, &d->stride); SDL_UnlockTexture(d->texture); + } + d->info.change_mask = SPA_PORT_CHANGE_MASK_PARAMS; + if (format) { d->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); d->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); + } else { + d->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + d->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); } + spa_node_emit_port_info(&d->hooks, direction, port_id, &d->info); d->info.change_mask = 0;
View file
pipewire-0.3.59.tar.gz/src/examples/export-source.c -> pipewire-0.3.60.tar.gz/src/examples/export-source.c
Changed
@@ -275,26 +275,31 @@ { struct data *d = object; - d->info.change_mask = SPA_PORT_CHANGE_MASK_PARAMS; if (format == NULL) { - d->format.format = 0; - d->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - d->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - spa_node_emit_port_info(&d->hooks, SPA_DIRECTION_OUTPUT, 0, &d->info); - return 0; - } + spa_zero(d->format); + } else { + spa_debug_format(0, NULL, format); - spa_debug_format(0, NULL, format); - - if (spa_format_audio_raw_parse(format, &d->format) < 0) - return -EINVAL; + if (spa_format_audio_raw_parse(format, &d->format) < 0) + return -EINVAL; - if (d->format.format != SPA_AUDIO_FORMAT_S16 && - d->format.format != SPA_AUDIO_FORMAT_F32) - return -EINVAL; + if (d->format.format != SPA_AUDIO_FORMAT_S16 && + d->format.format != SPA_AUDIO_FORMAT_F32) + return -EINVAL; + if (d->format.rate == 0 || + d->format.channels == 0 || + d->format.channels > SPA_AUDIO_MAX_CHANNELS) + return -EINVAL; + } - d->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); - d->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); + d->info.change_mask = SPA_PORT_CHANGE_MASK_PARAMS; + if (format) { + d->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); + d->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); + } else { + d->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + d->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); + } spa_node_emit_port_info(&d->hooks, SPA_DIRECTION_OUTPUT, 0, &d->info); return 0;
View file
pipewire-0.3.59.tar.gz/src/examples/local-v4l2.c -> pipewire-0.3.60.tar.gz/src/examples/local-v4l2.c
Changed
@@ -207,28 +207,39 @@ Uint32 sdl_format; void *dest; - if (format == NULL) - return 0; - - spa_debug_format(0, NULL, format); - - spa_format_video_raw_parse(format, &d->format); - - sdl_format = id_to_sdl_format(d->format.format); - if (sdl_format == SDL_PIXELFORMAT_UNKNOWN) - return -EINVAL; - - d->texture = SDL_CreateTexture(d->renderer, - sdl_format, - SDL_TEXTUREACCESS_STREAMING, - d->format.size.width, - d->format.size.height); - SDL_LockTexture(d->texture, NULL, &dest, &d->stride); - SDL_UnlockTexture(d->texture); + if (format == NULL) { + spa_zero(d->format); + SDL_DestroyTexture(d->texture); + d->texture = NULL; + } else { + spa_debug_format(0, NULL, format); + + spa_format_video_raw_parse(format, &d->format); + + sdl_format = id_to_sdl_format(d->format.format); + if (sdl_format == SDL_PIXELFORMAT_UNKNOWN) + return -EINVAL; + if (d->format.size.width == 0 || + d->format.size.height == 0) + return -EINVAL; + + d->texture = SDL_CreateTexture(d->renderer, + sdl_format, + SDL_TEXTUREACCESS_STREAMING, + d->format.size.width, + d->format.size.height); + SDL_LockTexture(d->texture, NULL, &dest, &d->stride); + SDL_UnlockTexture(d->texture); + } d->info.change_mask = SPA_PORT_CHANGE_MASK_PARAMS; - d->params1 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); - d->params2 = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); + if (format) { + d->params1 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); + d->params2 = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); + } else { + d->params1 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + d->params2 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); + } spa_node_emit_port_info(&d->hooks, SPA_DIRECTION_INPUT, 0, &d->info); return 0;
View file
pipewire-0.3.59.tar.gz/src/examples/sdl.h -> pipewire-0.3.60.tar.gz/src/examples/sdl.h
Changed
@@ -130,25 +130,22 @@ static inline uint32_t sdl_format_to_id(Uint32 format) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(sdl_video_formats); i++) { - if (sdl_video_formatsi.format == format) - return sdl_video_formatsi.id; + SPA_FOR_EACH_ELEMENT_VAR(sdl_video_formats, f) { + if (f->format == format) + return f->id; } return SPA_VIDEO_FORMAT_UNKNOWN; } static inline Uint32 id_to_sdl_format(uint32_t id) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(sdl_video_formats); i++) { - if (sdl_video_formatsi.id == id) - return sdl_video_formatsi.format; + SPA_FOR_EACH_ELEMENT_VAR(sdl_video_formats, f) { + if (f->id == id) + return f->format; } return SDL_PIXELFORMAT_UNKNOWN; } - static inline struct spa_pod *sdl_build_formats(SDL_RendererInfo *info, struct spa_pod_builder *b) { uint32_t i, c; @@ -178,8 +175,8 @@ spa_pod_builder_id(b, id); } /* then all the other ones SDL can convert from/to */ - for (i = 0; i < SPA_N_ELEMENTS(sdl_video_formats); i++) { - uint32_t id = sdl_video_formatsi.id; + SPA_FOR_EACH_ELEMENT_VAR(sdl_video_formats, f) { + uint32_t id = f->id; if (id != SPA_VIDEO_FORMAT_UNKNOWN) spa_pod_builder_id(b, id); }
View file
pipewire-0.3.59.tar.gz/src/examples/video-play-fixate.c -> pipewire-0.3.60.tar.gz/src/examples/video-play-fixate.c
Changed
@@ -340,6 +340,10 @@ pw_stream_set_error(stream, -EINVAL, "unknown pixel format"); return; } + if (data->size.width == 0 || data->size.height == 0) { + pw_stream_set_error(stream, -EINVAL, "invalid size"); + return; + } data->texture = SDL_CreateTexture(data->renderer, sdl_format,
View file
pipewire-0.3.59.tar.gz/src/examples/video-play-pull.c -> pipewire-0.3.60.tar.gz/src/examples/video-play-pull.c
Changed
@@ -388,6 +388,10 @@ pw_stream_set_error(stream, -EINVAL, "unknown pixel format"); return; } + if (data->size.width == 0 || data->size.height == 0) { + pw_stream_set_error(stream, -EINVAL, "invalid size"); + return; + } data->texture = SDL_CreateTexture(data->renderer, sdl_format,
View file
pipewire-0.3.59.tar.gz/src/examples/video-play-reneg.c -> pipewire-0.3.60.tar.gz/src/examples/video-play-reneg.c
Changed
@@ -233,6 +233,10 @@ pw_stream_set_error(stream, -EINVAL, "unknown pixel format"); return; } + if (data->size.width == 0 || data->size.height == 0) { + pw_stream_set_error(stream, -EINVAL, "invalid size"); + return; + } data->texture = SDL_CreateTexture(data->renderer, sdl_format,
View file
pipewire-0.3.59.tar.gz/src/examples/video-play.c -> pipewire-0.3.60.tar.gz/src/examples/video-play.c
Changed
@@ -336,6 +336,10 @@ pw_stream_set_error(stream, -EINVAL, "unknown pixel format"); return; } + if (data->size.width == 0 || data->size.height == 0) { + pw_stream_set_error(stream, -EINVAL, "invalid size"); + return; + } data->texture = SDL_CreateTexture(data->renderer, sdl_format,
View file
pipewire-0.3.59.tar.gz/src/gst/gstpipewiredeviceprovider.c -> pipewire-0.3.60.tar.gz/src/gst/gstpipewiredeviceprovider.c
Changed
@@ -491,6 +491,8 @@ gst_device_provider_hide_provider (provider, "pulsedeviceprovider"); else if (g_str_has_prefix(str, "v4l2:")) gst_device_provider_hide_provider (provider, "v4l2deviceprovider"); + else if (g_str_has_prefix(str, "libcamera:")) + gst_device_provider_hide_provider (provider, "libcameraprovider"); } }
View file
pipewire-0.3.59.tar.gz/src/modules/meson.build -> pipewire-0.3.60.tar.gz/src/modules/meson.build
Changed
@@ -26,6 +26,8 @@ 'module-rt.c', 'module-raop-discover.c', 'module-raop-sink.c', + 'module-rtp-source.c', + 'module-rtp-sink.c', 'module-session-manager.c', 'module-zeroconf-discover.c', 'module-roc-source.c', @@ -258,6 +260,8 @@ 'module-protocol-pulse/modules/module-roc-sink.c', 'module-protocol-pulse/modules/module-roc-sink-input.c', 'module-protocol-pulse/modules/module-roc-source.c', + 'module-protocol-pulse/modules/module-rtp-recv.c', + 'module-protocol-pulse/modules/module-rtp-send.c', 'module-protocol-pulse/modules/module-simple-protocol-tcp.c', 'module-protocol-pulse/modules/module-switch-on-connect.c', 'module-protocol-pulse/modules/module-tunnel-sink.c', @@ -482,6 +486,24 @@ roc_lib = cc.find_library('roc', has_headers: 'roc/config.h' , required: get_option('roc')) summary({'ROC': roc_lib.found()}, bool_yn: true, section: 'Streaming between daemons') +pipewire_module_rtp_source = shared_library('pipewire-module-rtp-source', + 'module-rtp-source.c' , + include_directories : configinc, + install : true, + install_dir : modules_install_dir, + install_rpath: modules_install_dir, + dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, +) + +pipewire_module_rtp_sink = shared_library('pipewire-module-rtp-sink', + 'module-rtp-sink.c' , + include_directories : configinc, + install : true, + install_dir : modules_install_dir, + install_rpath: modules_install_dir, + dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, +) + build_module_roc = roc_lib.found() if build_module_roc pipewire_module_roc_sink = shared_library('pipewire-module-roc-sink',
View file
pipewire-0.3.59.tar.gz/src/modules/module-avb/acmp.c -> pipewire-0.3.60.tar.gz/src/modules/module-avb/acmp.c
Changed
@@ -345,11 +345,10 @@ static inline const struct msg_info *find_msg_info(uint16_t type, const char *name) { - uint32_t i; - for (i = 0; i < SPA_N_ELEMENTS(msg_info); i++) { - if ((name == NULL && type == msg_infoi.type) || - (name != NULL && spa_streq(name, msg_infoi.name))) - return &msg_infoi; + SPA_FOR_EACH_ELEMENT_VAR(msg_info, i) { + if ((name == NULL && type == i->type) || + (name != NULL && spa_streq(name, i->name))) + return i; } return NULL; }
View file
pipewire-0.3.59.tar.gz/src/modules/module-avb/aecp-aem.c -> pipewire-0.3.60.tar.gz/src/modules/module-avb/aecp-aem.c
Changed
@@ -250,11 +250,10 @@ static inline const struct cmd_info *find_cmd_info(uint16_t type, const char *name) { - uint32_t i; - for (i = 0; i < SPA_N_ELEMENTS(cmd_info); i++) { - if ((name == NULL && type == cmd_infoi.type) || - (name != NULL && spa_streq(name, cmd_infoi.name))) - return &cmd_infoi; + SPA_FOR_EACH_ELEMENT_VAR(cmd_info, i) { + if ((name == NULL && type == i->type) || + (name != NULL && spa_streq(name, i->name))) + return i; } return NULL; }
View file
pipewire-0.3.59.tar.gz/src/modules/module-avb/aecp.c -> pipewire-0.3.60.tar.gz/src/modules/module-avb/aecp.c
Changed
@@ -67,11 +67,10 @@ static inline const struct msg_info *find_msg_info(uint16_t type, const char *name) { - uint32_t i; - for (i = 0; i < SPA_N_ELEMENTS(msg_info); i++) { - if ((name == NULL && type == msg_infoi.type) || - (name != NULL && spa_streq(name, msg_infoi.name))) - return &msg_infoi; + SPA_FOR_EACH_ELEMENT_VAR(msg_info, i) { + if ((name == NULL && type == i->type) || + (name != NULL && spa_streq(name, i->name))) + return i; } return NULL; }
View file
pipewire-0.3.59.tar.gz/src/modules/module-client-node/v0/client-node.c -> pipewire-0.3.60.tar.gz/src/modules/module-client-node/v0/client-node.c
Changed
@@ -1333,13 +1333,12 @@ { "pipewire.target.node", PW_KEY_NODE_TARGET, } }; - uint32_t i; const char *str; - for(i = 0; i < SPA_N_ELEMENTS(props); i++) { - if ((str = pw_properties_get(properties, propsi.from)) != NULL) { - pw_properties_set(properties, propsi.to, str); - pw_properties_set(properties, propsi.from, NULL); + SPA_FOR_EACH_ELEMENT_VAR(props, p) { + if ((str = pw_properties_get(properties, p->from)) != NULL) { + pw_properties_set(properties, p->to, str); + pw_properties_set(properties, p->from, NULL); } } }
View file
pipewire-0.3.59.tar.gz/src/modules/module-echo-cancel.c -> pipewire-0.3.60.tar.gz/src/modules/module-echo-cancel.c
Changed
@@ -43,6 +43,7 @@ #include <spa/param/audio/raw.h> #include <spa/param/profiler.h> #include <spa/pod/builder.h> +#include <spa/pod/dynamic.h> #include <spa/support/plugin.h> #include <spa/utils/json.h> #include <spa/utils/names.h> @@ -196,15 +197,18 @@ uint32_t max_buffer_size; uint32_t buffer_delay; + uint32_t current_delay; struct spa_handle *spa_handle; struct spa_plugin_loader *loader; + + bool monitor_mode; }; static void process(struct impl *impl) { struct pw_buffer *cout; - struct pw_buffer *pout; + struct pw_buffer *pout = NULL; float rec_bufimpl->info.channelsimpl->aec_blocksize / sizeof(float); float play_bufimpl->info.channelsimpl->aec_blocksize / sizeof(float); float play_delayed_bufimpl->info.channelsimpl->aec_blocksize / sizeof(float); @@ -218,7 +222,7 @@ uint32_t rindex, pindex, oindex, pdindex, avail; int32_t stride = 0; - if ((pout = pw_stream_dequeue_buffer(impl->playback)) == NULL) { + if (impl->playback != NULL && (pout = pw_stream_dequeue_buffer(impl->playback)) == NULL) { pw_log_debug("out of playback buffers: %m"); goto done; } @@ -256,23 +260,50 @@ impl->play_ringsize, pdindex % impl->play_ringsize, (void *)play_delayedi, size); - /* output to sink, just copy */ - dd = &pout->buffer->datasi; - memcpy(dd->data, playi, size); + if (pout != NULL) { + /* output to sink, just copy */ + dd = &pout->buffer->datasi; + memcpy(dd->data, playi, size); - dd->chunk->offset = 0; - dd->chunk->size = size; - dd->chunk->stride = stride; + dd->chunk->offset = 0; + dd->chunk->size = size; + dd->chunk->stride = stride; + } } spa_ringbuffer_read_update(&impl->rec_ring, rindex + size); spa_ringbuffer_read_update(&impl->play_ring, pindex + size); spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size); - pw_stream_queue_buffer(impl->playback, pout); + if (impl->playback != NULL) + pw_stream_queue_buffer(impl->playback, pout); - /* Now run the canceller */ - spa_audio_aec_run(impl->aec, rec, play_delayed, out, size / sizeof(float)); + if (SPA_UNLIKELY (impl->current_delay < impl->buffer_delay)) { + uint32_t delay_left = impl->buffer_delay - impl->current_delay; + uint32_t silence_size; + + /* don't run the canceller until play_buffer has been filled, + * copy silence to output in the meantime */ + silence_size = SPA_MIN(size, delay_left * sizeof(float)); + for (i = 0; i < impl->info.channels; i++) + memset(outi, 0, silence_size); + impl->current_delay += silence_size / sizeof(float); + pw_log_debug("current_delay %d", impl->current_delay); + + if (silence_size != size) { + const float *pdimpl->info.channels; + float *oimpl->info.channels; + + for (i = 0; i < impl->info.channels; i++) { + pdi = play_delayedi + delay_left; + oi = outi + delay_left; + } + spa_audio_aec_run(impl->aec, rec, pd, o, size / sizeof(float) - delay_left); + } + } else { + /* run the canceller */ + spa_audio_aec_run(impl->aec, rec, play_delayed, out, size / sizeof(float)); + } /* Next, copy over the output to the output ringbuffer */ avail = spa_ringbuffer_get_write_index(&impl->out_ring, &oindex); @@ -479,14 +510,55 @@ else pw_stream_update_params(impl->capture, params, 1); } +static struct spa_pod* get_props_param(struct impl* impl, struct spa_pod_builder* b) +{ + if (spa_audio_aec_get_params(impl->aec, NULL) > 0) { + struct spa_pod_frame f2; + spa_pod_builder_push_object( + b, &f0, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); + spa_pod_builder_prop(b, SPA_PROP_params, 0); + spa_pod_builder_push_struct(b, &f1); + + spa_audio_aec_get_params(impl->aec, b); + + spa_pod_builder_pop(b, &f1); + return spa_pod_builder_pop(b, &f0); + } + return NULL; +} -static void input_param_changed(void *data, uint32_t id, const struct spa_pod *param) +static void input_param_changed(void *data, uint32_t id, const struct spa_pod* param) { - struct impl *impl = data; + struct spa_pod_object* obj = (struct spa_pod_object*)param; + const struct spa_pod_prop* prop; + struct impl* impl = data; switch (id) { case SPA_PARAM_Latency: input_param_latency_changed(impl, param); break; + case SPA_PARAM_Props: + if (param != NULL) { + uint8_t buffer1024; + struct spa_pod_dynamic_builder b; + const struct spa_pod* params1; + SPA_POD_OBJECT_FOREACH(obj, prop) + { + if (prop->key == SPA_PROP_params) { + spa_audio_aec_set_params(impl->aec, &prop->value); + } + } + + spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); + params0 = get_props_param(impl, &b.b); + if (params0) { + pw_stream_update_params(impl->capture, params, 1); + pw_stream_update_params(impl->playback, params, 1); + } + spa_pod_dynamic_builder_clean(&b); + } else { + pw_log_warn("param is null"); + } + break; } } @@ -519,7 +591,11 @@ switch (state) { case PW_STREAM_STATE_PAUSED: pw_stream_flush(impl->sink, false); - pw_stream_flush(impl->playback, false); + if (impl->playback != NULL) + pw_stream_flush(impl->playback, false); + if (old == PW_STREAM_STATE_STREAMING) { + impl->current_delay = 0; + } break; case PW_STREAM_STATE_UNCONNECTED: pw_log_info("%p: output unconnected", impl); @@ -548,17 +624,40 @@ if (latency.direction == SPA_DIRECTION_INPUT) pw_stream_update_params(impl->sink, params, 1); - else + else if (impl->playback != NULL) pw_stream_update_params(impl->playback, params, 1); } static void output_param_changed(void *data, uint32_t id, const struct spa_pod *param) { + struct spa_pod_object *obj = (struct spa_pod_object *) param; + const struct spa_pod_prop *prop; struct impl *impl = data; switch (id) { case SPA_PARAM_Latency: output_param_latency_changed(impl, param); break; + case SPA_PARAM_Props: + if (param != NULL) { + uint8_t buffer1024; + struct spa_pod_dynamic_builder b; + const struct spa_pod* params1; + + SPA_POD_OBJECT_FOREACH(obj, prop) + { + if (prop->key == SPA_PROP_params) { + spa_audio_aec_set_params(impl->aec, &prop->value); + } + } + spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); + params0 = get_props_param(impl, &b.b); + if (params0 != NULL) { + pw_stream_update_params(impl->capture, params, 1); + pw_stream_update_params(impl->playback, params, 1); + } + spa_pod_dynamic_builder_clean(&b); + } + break; } } @@ -635,8 +734,10 @@ static void playback_destroy(void *d) { struct impl *impl = d; - spa_hook_remove(&impl->playback_listener); - impl->playback = NULL; + if (impl->playback != NULL) { + spa_hook_remove(&impl->playback_listener); + impl->playback = NULL; + } } static const struct pw_stream_events playback_events = { @@ -657,9 +758,9 @@ { int res; uint32_t n_params, i; - const struct spa_pod *params1; - uint8_t buffer1024; - struct spa_pod_builder b; + uint32_t offsets512; + const struct spa_pod *params512; + struct spa_pod_dynamic_builder b; struct pw_properties *props; const char *str; uint32_t index; @@ -681,6 +782,8 @@ pw_properties_set(props, SPA_KEY_AUDIO_CHANNELS, str); if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL) pw_properties_set(props, SPA_KEY_AUDIO_POSITION, str); + if ((str = pw_properties_get(impl->source_props, "resample.prefill")) != NULL) + pw_properties_set(props, "resample.prefill", str); impl->capture = pw_stream_new(impl->core, "Echo-Cancel Capture", props); @@ -701,32 +804,38 @@ &impl->source_listener, &source_events, impl); - props = pw_properties_new( + if (impl->monitor_mode) { + impl->playback = NULL; + } else { + props = pw_properties_new( PW_KEY_NODE_NAME, "echo-cancel-playback", PW_KEY_NODE_VIRTUAL, "true", PW_KEY_NODE_PASSIVE, "true", NULL); - if ((str = pw_properties_get(impl->sink_props, PW_KEY_NODE_GROUP)) != NULL) - pw_properties_set(props, PW_KEY_NODE_GROUP, str); - if ((str = pw_properties_get(impl->sink_props, PW_KEY_NODE_LINK_GROUP)) != NULL) - pw_properties_set(props, PW_KEY_NODE_LINK_GROUP, str); - if ((str = pw_properties_get(impl->sink_props, PW_KEY_NODE_LATENCY)) != NULL) - pw_properties_set(props, PW_KEY_NODE_LATENCY, str); - else if (impl->aec->latency) - pw_properties_set(props, PW_KEY_NODE_LATENCY, impl->aec->latency); - if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_CHANNELS)) != NULL) - pw_properties_set(props, SPA_KEY_AUDIO_CHANNELS, str); - if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL) - pw_properties_set(props, SPA_KEY_AUDIO_POSITION, str); - - impl->playback = pw_stream_new(impl->core, - "Echo-Cancel Playback", props); - if (impl->playback == NULL) - return -errno; - - pw_stream_add_listener(impl->playback, - &impl->playback_listener, - &playback_events, impl); + if ((str = pw_properties_get(impl->sink_props, PW_KEY_NODE_GROUP)) != NULL) + pw_properties_set(props, PW_KEY_NODE_GROUP, str); + if ((str = pw_properties_get(impl->sink_props, PW_KEY_NODE_LINK_GROUP)) != NULL) + pw_properties_set(props, PW_KEY_NODE_LINK_GROUP, str); + if ((str = pw_properties_get(impl->sink_props, PW_KEY_NODE_LATENCY)) != NULL) + pw_properties_set(props, PW_KEY_NODE_LATENCY, str); + else if (impl->aec->latency) + pw_properties_set(props, PW_KEY_NODE_LATENCY, impl->aec->latency); + if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_CHANNELS)) != NULL) + pw_properties_set(props, SPA_KEY_AUDIO_CHANNELS, str); + if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL) + pw_properties_set(props, SPA_KEY_AUDIO_POSITION, str); + if ((str = pw_properties_get(impl->sink_props, "resample.prefill")) != NULL) + pw_properties_set(props, "resample.prefill", str); + + impl->playback = pw_stream_new(impl->core, + "Echo-Cancel Playback", props); + if (impl->playback == NULL) + return -errno; + + pw_stream_add_listener(impl->playback, + &impl->playback_listener, + &playback_events, impl); + } impl->sink = pw_stream_new(impl->core, "Echo-Cancel Sink", impl->sink_props); @@ -739,9 +848,23 @@ &sink_events, impl); n_params = 0; - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - paramsn_params++ = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, - &impl->info); + spa_pod_dynamic_builder_init(&b, NULL, 0, 4096); + + offsetsn_params++ = b.b.state.offset; + spa_format_audio_raw_build(&b.b, SPA_PARAM_EnumFormat, &impl->info); + + int nbr_of_external_props = spa_audio_aec_enum_props(impl->aec, 0, NULL); + if (nbr_of_external_props > 0) { + for (int i = 0; i < nbr_of_external_props; i++) { + offsetsn_params++ = b.b.state.offset; + spa_audio_aec_enum_props(impl->aec, i, &b.b); + } + get_props_param(impl, &b.b); + } + + for (i = 0; i < n_params; i++) { + paramsi = spa_pod_builder_deref(&b.b, offsetsi); + } if ((res = pw_stream_connect(impl->capture, PW_DIRECTION_INPUT, @@ -749,33 +872,44 @@ PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS, - params, n_params)) < 0) + params, n_params)) < 0) { + spa_pod_dynamic_builder_clean(&b); return res; + } if ((res = pw_stream_connect(impl->source, PW_DIRECTION_OUTPUT, PW_ID_ANY, PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS, - params, n_params)) < 0) + params, n_params)) < 0) { + spa_pod_dynamic_builder_clean(&b); return res; + } if ((res = pw_stream_connect(impl->sink, PW_DIRECTION_INPUT, PW_ID_ANY, - PW_STREAM_FLAG_MAP_BUFFERS | - PW_STREAM_FLAG_RT_PROCESS, - params, n_params)) < 0) + impl->playback != NULL ? + PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS : + PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS, + params, n_params)) < 0) { + spa_pod_dynamic_builder_clean(&b); return res; + } - if ((res = pw_stream_connect(impl->playback, + if (impl->playback != NULL && (res = pw_stream_connect(impl->playback, PW_DIRECTION_OUTPUT, PW_ID_ANY, PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS, - params, n_params)) < 0) + params, n_params)) < 0) { + spa_pod_dynamic_builder_clean(&b); return res; + } + + spa_pod_dynamic_builder_clean(&b); impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000; impl->play_ringsize = sizeof(float) * ((impl->max_buffer_size * impl->info.rate / 1000) + impl->buffer_delay); @@ -963,6 +1097,10 @@ goto error; } + impl->monitor_mode = false; + if ((str = pw_properties_get(props, "monitor.mode")) != NULL) + impl->monitor_mode = pw_properties_parse_bool(str); + impl->module = module; impl->context = context; @@ -986,13 +1124,23 @@ pw_properties_set(impl->source_props, PW_KEY_NODE_DESCRIPTION, "Echo-Cancel Source"); if (pw_properties_get(impl->source_props, PW_KEY_MEDIA_CLASS) == NULL) pw_properties_set(impl->source_props, PW_KEY_MEDIA_CLASS, "Audio/Source"); + if (pw_properties_get(impl->source_props, "resample.prefill") == NULL) + pw_properties_set(impl->source_props, "resample.prefill", "true"); if (pw_properties_get(impl->sink_props, PW_KEY_NODE_NAME) == NULL) pw_properties_set(impl->sink_props, PW_KEY_NODE_NAME, "echo-cancel-sink"); if (pw_properties_get(impl->sink_props, PW_KEY_NODE_DESCRIPTION) == NULL) pw_properties_set(impl->sink_props, PW_KEY_NODE_DESCRIPTION, "Echo-Cancel Sink"); if (pw_properties_get(impl->sink_props, PW_KEY_MEDIA_CLASS) == NULL) - pw_properties_set(impl->sink_props, PW_KEY_MEDIA_CLASS, "Audio/Sink"); + pw_properties_set(impl->sink_props, PW_KEY_MEDIA_CLASS, + impl->monitor_mode ? "Stream/Input/Audio" : "Audio/Sink"); + if (pw_properties_get(impl->sink_props, "resample.prefill") == NULL) + pw_properties_set(impl->sink_props, "resample.prefill", "true"); + if (impl->monitor_mode) { + pw_properties_set(impl->sink_props, PW_KEY_NODE_PASSIVE, "true"); + pw_properties_set(impl->sink_props, PW_KEY_STREAM_MONITOR, "true"); + pw_properties_set(impl->sink_props, PW_KEY_STREAM_CAPTURE_SINK, "true"); + } if ((str = pw_properties_get(props, "aec.method")) != NULL) pw_log_warn("aec.method is not supported anymore use library.name");
View file
pipewire-0.3.59.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.60.tar.gz/src/modules/module-filter-chain.c
Changed
@@ -785,10 +785,10 @@ spa_pod_builder_prop(b, SPA_PROP_INFO_type, 0); if (p->hint & FC_HINT_BOOLEAN) { if (min == max) { - spa_pod_builder_bool(b, def <= 0.0 ? false : true); + spa_pod_builder_bool(b, def <= 0.0f ? false : true); } else { spa_pod_builder_push_choice(b, &f1, SPA_CHOICE_Enum, 0); - spa_pod_builder_bool(b, def <= 0.0 ? false : true); + spa_pod_builder_bool(b, def <= 0.0f ? false : true); spa_pod_builder_bool(b, false); spa_pod_builder_bool(b, true); spa_pod_builder_pop(b, &f1); @@ -844,7 +844,7 @@ spa_pod_builder_string(b, name); if (p->hint & FC_HINT_BOOLEAN) { - spa_pod_builder_bool(b, port->control_data <= 0.0 ? false : true); + spa_pod_builder_bool(b, port->control_data <= 0.0f ? false : true); } else if (p->hint & FC_HINT_INTEGER) { spa_pod_builder_int(b, port->control_data); } else { @@ -1002,6 +1002,7 @@ { struct impl *impl = data; struct graph *graph = &impl->graph; + int res; switch (id) { case SPA_PARAM_Format: @@ -1010,9 +1011,15 @@ } else { struct spa_audio_info_raw info; spa_zero(info); - spa_format_audio_raw_parse(param, &info); + if ((res = spa_format_audio_raw_parse(param, &info)) < 0) + goto error; + if (info.rate == 0) { + res = -EINVAL; + goto error; + } impl->rate = info.rate; - graph_instantiate(graph); + if ((res = graph_instantiate(graph)) < 0) + goto error; } break; case SPA_PARAM_Props: @@ -1023,6 +1030,11 @@ param_latency_changed(impl, param); break; } + return; + +error: + pw_stream_set_error(impl->capture, res, "can't start graph: %s", + spa_strerror(res)); } static const struct pw_stream_events in_stream_events = { @@ -1172,16 +1184,19 @@ else if (spa_streq(type, "ladspa")) { pl = load_ladspa_plugin(support, n_support, path, NULL); } -#ifdef HAVE_LILV else if (spa_streq(type, "lv2")) { +#ifdef HAVE_LILV pl = load_lv2_plugin(support, n_support, path, NULL); - } +#else + pw_log_error("filter-chain is compiled without lv2 support"); + pl = NULL; + errno = ENOTSUP; #endif - else { + } else { + pw_log_error("invalid plugin type '%s'", type); pl = NULL; errno = EINVAL; } - if (pl == NULL) goto exit; @@ -1501,7 +1516,6 @@ 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)) { @@ -1539,10 +1553,8 @@ break; } - if (spa_streq(type, "builtin")) { + if (spa_streq(type, "builtin")) snprintf(plugin, sizeof(plugin), "%s", "builtin"); - } else if (!spa_streq(type, "ladspa") && !spa_streq(type, "lv2")) - return -ENOTSUP; pw_log_info("loading type:%s plugin:%s label:%s", type, plugin, label); @@ -1562,6 +1574,10 @@ node->control_port = calloc(desc->n_control, sizeof(struct port)); node->notify_port = calloc(desc->n_notify, sizeof(struct port)); + pw_log_info("loaded n_input:%d n_output:%d n_control:%d n_notify:%d", + desc->n_input, desc->n_output, + desc->n_control, desc->n_notify); + for (i = 0; i < desc->n_input; i++) { struct port *port = &node->input_porti; port->node = node; @@ -1576,14 +1592,6 @@ 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++) { @@ -1629,6 +1637,26 @@ } } +static int port_ensure_data(struct port *port, uint32_t i) +{ + float *data; + 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; + return 0; +} + +static void port_free_data(struct port *port, uint32_t i) +{ + free(port->audio_datai); + port->audio_datai = NULL; +} + static void node_free(struct node *node) { uint32_t i, j; @@ -1636,7 +1664,7 @@ 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); + port_free_data(&node->output_portj, i); } node_cleanup(node); descriptor_unref(node->desc); @@ -1688,6 +1716,8 @@ spa_list_for_each(link, &port->link_list, input_link) { struct port *peer = link->output; + if ((res = port_ensure_data(peer, i)) < 0) + goto error; pw_log_info("connect input port %s%d:%s %p", node->name, i, d->portsport->p.name, peer->audio_datai); @@ -1696,6 +1726,8 @@ } for (j = 0; j < desc->n_output; j++) { port = &node->output_portj; + if ((res = port_ensure_data(port, i)) < 0) + goto error; pw_log_info("connect output port %s%d:%s %p", node->name, i, d->portsport->p.name, port->audio_datai); @@ -1938,6 +1970,8 @@ gh->hndl = &node->hndli; gh->desc = d; + } + for (i = 0; i < desc->n_output; i++) { spa_list_for_each(link, &node->output_porti.link_list, output_link) link->input->node->n_deps--; }
View file
pipewire-0.3.59.tar.gz/src/modules/module-filter-chain/biquad.c -> pipewire-0.3.60.tar.gz/src/modules/module-filter-chain/biquad.c
Changed
@@ -11,24 +11,6 @@ #include <math.h> #include "biquad.h" -#ifndef max -#define max(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a > _b ? _a : _b; \ - }) -#endif - -#ifndef min -#define min(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a < _b ? _a : _b; \ - }) -#endif - #ifndef M_PI #define M_PI 3.14159265358979323846 #endif @@ -47,7 +29,7 @@ static void biquad_lowpass(struct biquad *bq, double cutoff, double resonance) { /* Limit cutoff to 0 to 1. */ - cutoff = max(0.0, min(cutoff, 1.0)); + cutoff = fmax(0.0, fmin(cutoff, 1.0)); if (cutoff == 1 || cutoff == 0) { /* When cutoff is 1, the z-transform is 1. @@ -59,7 +41,7 @@ } /* Compute biquad coefficients for lowpass filter */ - resonance = max(0.0, resonance); /* can't go negative */ + resonance = fmax(0.0, resonance); /* can't go negative */ double g = pow(10.0, 0.05 * resonance); double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2); @@ -81,7 +63,7 @@ static void biquad_highpass(struct biquad *bq, double cutoff, double resonance) { /* Limit cutoff to 0 to 1. */ - cutoff = max(0.0, min(cutoff, 1.0)); + cutoff = fmax(0.0, fmin(cutoff, 1.0)); if (cutoff == 1 || cutoff == 0) { /* When cutoff is one, the z-transform is 0. */ @@ -95,7 +77,7 @@ } /* Compute biquad coefficients for highpass filter */ - resonance = max(0.0, resonance); /* can't go negative */ + resonance = fmax(0.0, resonance); /* can't go negative */ double g = pow(10.0, 0.05 * resonance); double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2); @@ -117,10 +99,10 @@ static void biquad_bandpass(struct biquad *bq, double frequency, double Q) { /* No negative frequencies allowed. */ - frequency = max(0.0, frequency); + frequency = fmax(0.0, frequency); /* Don't let Q go negative, which causes an unstable filter. */ - Q = max(0.0, Q); + Q = fmax(0.0, Q); if (frequency <= 0 || frequency >= 1) { /* When the cutoff is zero, the z-transform approaches 0, if Q @@ -158,7 +140,7 @@ static void biquad_lowshelf(struct biquad *bq, double frequency, double db_gain) { /* Clip frequencies to between 0 and 1, inclusive. */ - frequency = max(0.0, min(frequency, 1.0)); + frequency = fmax(0.0, fmin(frequency, 1.0)); double A = pow(10.0, db_gain / 40); @@ -195,7 +177,7 @@ double db_gain) { /* Clip frequencies to between 0 and 1, inclusive. */ - frequency = max(0.0, min(frequency, 1.0)); + frequency = fmax(0.0, fmin(frequency, 1.0)); double A = pow(10.0, db_gain / 40); @@ -232,10 +214,10 @@ double db_gain) { /* Clip frequencies to between 0 and 1, inclusive. */ - frequency = max(0.0, min(frequency, 1.0)); + frequency = fmax(0.0, fmin(frequency, 1.0)); /* Don't let Q go negative, which causes an unstable filter. */ - Q = max(0.0, Q); + Q = fmax(0.0, Q); double A = pow(10.0, db_gain / 40); @@ -270,10 +252,10 @@ static void biquad_notch(struct biquad *bq, double frequency, double Q) { /* Clip frequencies to between 0 and 1, inclusive. */ - frequency = max(0.0, min(frequency, 1.0)); + frequency = fmax(0.0, fmin(frequency, 1.0)); /* Don't let Q go negative, which causes an unstable filter. */ - Q = max(0.0, Q); + Q = fmax(0.0, Q); if (frequency <= 0 || frequency >= 1) { /* When frequency is 0 or 1, the z-transform is 1. */ @@ -306,10 +288,10 @@ static void biquad_allpass(struct biquad *bq, double frequency, double Q) { /* Clip frequencies to between 0 and 1, inclusive. */ - frequency = max(0.0, min(frequency, 1.0)); + frequency = fmax(0.0, fmin(frequency, 1.0)); /* Don't let Q go negative, which causes an unstable filter. */ - Q = max(0.0, Q); + Q = fmax(0.0, Q); if (frequency <= 0 || frequency >= 1) { /* When frequency is 0 or 1, the z-transform is 1. */
View file
pipewire-0.3.59.tar.gz/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-0.3.60.tar.gz/src/modules/module-filter-chain/builtin_plugin.c
Changed
@@ -590,6 +590,7 @@ float gain = 1.0f; unsigned long rate; + errno = EINVAL; if (config == NULL) return NULL; @@ -599,42 +600,60 @@ while (spa_json_get_string(&it1, key, sizeof(key)) > 0) { if (spa_streq(key, "blocksize")) { - if (spa_json_get_int(&it1, &blocksize) <= 0) + if (spa_json_get_int(&it1, &blocksize) <= 0) { + pw_log_error("convolver:blocksize requires a number"); return NULL; + } } else if (spa_streq(key, "tailsize")) { - if (spa_json_get_int(&it1, &tailsize) <= 0) + if (spa_json_get_int(&it1, &tailsize) <= 0) { + pw_log_error("convolver:tailsize requires a number"); return NULL; + } } else if (spa_streq(key, "gain")) { - if (spa_json_get_float(&it1, &gain) <= 0) + if (spa_json_get_float(&it1, &gain) <= 0) { + pw_log_error("convolver:gain requires a number"); return NULL; + } } else if (spa_streq(key, "delay")) { - if (spa_json_get_int(&it1, &delay) <= 0) + if (spa_json_get_int(&it1, &delay) <= 0) { + pw_log_error("convolver:delay requires a number"); return NULL; + } } else if (spa_streq(key, "filename")) { - if (spa_json_get_string(&it1, filename, sizeof(filename)) <= 0) + if (spa_json_get_string(&it1, filename, sizeof(filename)) <= 0) { + pw_log_error("convolver:filename requires a string"); return NULL; + } } else if (spa_streq(key, "offset")) { - if (spa_json_get_int(&it1, &offset) <= 0) + if (spa_json_get_int(&it1, &offset) <= 0) { + pw_log_error("convolver:offset requires a number"); return NULL; + } } else if (spa_streq(key, "length")) { - if (spa_json_get_int(&it1, &length) <= 0) + if (spa_json_get_int(&it1, &length) <= 0) { + pw_log_error("convolver:length requires a number"); return NULL; + } } else if (spa_streq(key, "channel")) { - if (spa_json_get_int(&it1, &channel) <= 0) + if (spa_json_get_int(&it1, &channel) <= 0) { + pw_log_error("convolver:channel requires a number"); return NULL; + } } else if (spa_json_next(&it1, &val) < 0) break; } - if (!filename0) + if (!filename0) { + pw_log_error("convolver:filename was not given"); return NULL; + } if (delay < 0) delay = 0; @@ -656,15 +675,18 @@ "Consider forcing a filter rate.", rate, SampleRate); } } - if (samples == NULL) + if (samples == NULL) { + errno = ENOENT; return NULL; + } if (blocksize <= 0) blocksize = SPA_CLAMP(n_samples, 64, 256); if (tailsize <= 0) - tailsize = SPA_CLAMP(4096, blocksize, 4096); + tailsize = SPA_CLAMP(4096, blocksize, 32768); - pw_log_info("using %d:%d blocksize ir:%s", blocksize, tailsize, filename); + pw_log_info("using n_samples:%u %d:%d blocksize ir:%s", n_samples, + blocksize, tailsize, filename); impl = calloc(1, sizeof(*impl)); if (impl == NULL) @@ -775,8 +797,10 @@ while (spa_json_get_string(&it1, key, sizeof(key)) > 0) { if (spa_streq(key, "max-delay")) { - if (spa_json_get_float(&it1, &max_delay) <= 0) + if (spa_json_get_float(&it1, &max_delay) <= 0) { + pw_log_error("delay:max-delay requires a number"); return NULL; + } } else if (spa_json_next(&it1, &val) < 0) break; @@ -790,7 +814,7 @@ impl->rate = SampleRate; impl->buffer_samples = max_delay * impl->rate; - pw_log_info("%lu %d", impl->rate, impl->buffer_samples); + pw_log_info("max-delay:%f seconds rate:%lu samples:%d", max_delay, impl->rate, impl->buffer_samples); impl->buffer = calloc(impl->buffer_samples, sizeof(float)); if (impl->buffer == NULL) {
View file
pipewire-0.3.59.tar.gz/src/modules/module-filter-chain/convolver.c -> pipewire-0.3.60.tar.gz/src/modules/module-filter-chain/convolver.c
Changed
@@ -251,7 +251,7 @@ { int i, processed = 0; - if (conv->segCount == 0) { + if (conv == NULL || conv->segCount == 0) { fft_clear(output, len); return len; }
View file
pipewire-0.3.59.tar.gz/src/modules/module-filter-chain/ladspa_plugin.c -> pipewire-0.3.60.tar.gz/src/modules/module-filter-chain/ladspa_plugin.c
Changed
@@ -22,6 +22,8 @@ * DEALINGS IN THE SOFTWARE. */ +#include "config.h" + #include <dlfcn.h> #include <math.h> @@ -80,39 +82,39 @@ break; case LADSPA_HINT_DEFAULT_LOW: if (LADSPA_IS_HINT_LOGARITHMIC(hint)) - def = (LADSPA_Data) exp(log(lower) * 0.75 + log(upper) * 0.25); + def = (LADSPA_Data) expf(logf(lower) * 0.75f + logf(upper) * 0.25f); else - def = (LADSPA_Data) (lower * 0.75 + upper * 0.25); + def = (LADSPA_Data) (lower * 0.75f + upper * 0.25f); break; case LADSPA_HINT_DEFAULT_MIDDLE: if (LADSPA_IS_HINT_LOGARITHMIC(hint)) - def = (LADSPA_Data) exp(log(lower) * 0.5 + log(upper) * 0.5); + def = (LADSPA_Data) expf(logf(lower) * 0.5f + logf(upper) * 0.5f); else - def = (LADSPA_Data) (lower * 0.5 + upper * 0.5); + def = (LADSPA_Data) (lower * 0.5f + upper * 0.5f); break; case LADSPA_HINT_DEFAULT_HIGH: if (LADSPA_IS_HINT_LOGARITHMIC(hint)) - def = (LADSPA_Data) exp(log(lower) * 0.25 + log(upper) * 0.75); + def = (LADSPA_Data) expf(logf(lower) * 0.25f + logf(upper) * 0.75f); else - def = (LADSPA_Data) (lower * 0.25 + upper * 0.75); + def = (LADSPA_Data) (lower * 0.25f + upper * 0.75f); break; case LADSPA_HINT_DEFAULT_0: - def = 0; + def = 0.0f; break; case LADSPA_HINT_DEFAULT_1: - def = 1; + def = 1.0f; break; case LADSPA_HINT_DEFAULT_100: - def = 100; + def = 100.0f; break; case LADSPA_HINT_DEFAULT_440: - def = 440; + def = 440.0f; break; default: if (upper == lower) def = upper; else - def = SPA_CLAMP(0.5 * upper, lower, upper); + def = SPA_CLAMPF(0.5f * upper, lower, upper); break; } if (LADSPA_IS_HINT_INTEGER(hint)) @@ -238,7 +240,7 @@ search_dirs = getenv("LADSPA_PATH"); if (!search_dirs) - search_dirs = "/usr/lib64/ladspa"; + search_dirs = "/usr/lib64/ladspa:/usr/lib/ladspa:" LIBDIR; /* * set the errno for the case when `ladspa_handle_load_by_path()`
View file
pipewire-0.3.59.tar.gz/src/modules/module-filter-chain/pffft.c -> pipewire-0.3.60.tar.gz/src/modules/module-filter-chain/pffft.c
Changed
@@ -25,7 +25,7 @@ Laboratory, the University Corporation for Atmospheric Research, nor the names of its sponsors or contributors may be used to endorse or promote products derived from this Software without - specific prior written permission. + specific prior written permission. - Redistributions of source code must retain the above copyright notices, this list of conditions, and the disclaimer below. @@ -52,7 +52,7 @@ */ /* - ChangeLog: + ChangeLog: - 2011/10/02, version 1: This is the very first release of this file. */ @@ -243,7 +243,7 @@ #define assertv4(v,f0,f1,f2,f3) assert(v.f0 == (f0) && v.f1 == (f1) && v.f2 == (f2) && v.f3 == (f3)) /* detect bugs with the vector support macros */ -static void validate_pffft_simd() +static void validate_pffft_simd(void) { float f16 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; v4sf_union a0, a1, a2, a3, t, u; @@ -301,7 +301,7 @@ assertv4(a3, 3, 7, 11, 15); } #else -static void validate_pffft_simd() +static void validate_pffft_simd(void) { } // allow test_pffft.c to call this function even when simd is not available.. #endif //!PFFFT_SIMD_DISABLE @@ -1421,7 +1421,7 @@ { PFFFT_Setup *s = (PFFFT_Setup *) malloc(sizeof(PFFFT_Setup)); int k, m; - /* unfortunately, the fft size must be a multiple of 16 for complex FFTs + /* unfortunately, the fft size must be a multiple of 16 for complex FFTs and 32 for real FFTs -- a lot of stuff would need to be rewritten to handle other cases (or maybe just switch to a scalar fft, I don't know..) */ if (transform == PFFFT_REAL) { @@ -1615,7 +1615,7 @@ 0 0 0 0 1 1 1 1 * i0 0 1 0 -1 1 0 -1 0 i1 0 0 0 0 1 -1 1 -1 i2 - 0 -1 0 1 1 0 -1 0 i3 + 0 -1 0 1 1 0 -1 0 i3 */ r0 = VADD(sr0, sr1); @@ -1719,7 +1719,7 @@ 0 0 0 0 1 1 1 1 * i0 0 -1 0 1 -1 0 1 0 i1 0 -1 0 1 1 0 -1 0 i2 - 0 0 0 0 -1 1 -1 1 i3 + 0 0 0 0 -1 1 -1 1 i3 */ //cerr << "matrix initial, before e , REAL:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n"; @@ -1832,7 +1832,7 @@ 0 0 0 0 1 -1 1 -1 * i0 0 -1 1 0 1 0 0 1 i1 0 0 0 0 1 1 -1 -1 i2 - 0 1 -1 0 1 0 0 1 i3 + 0 1 -1 0 1 0 0 1 i3 */ v4sf sr0 = VADD(r0, r3), dr0 = VSUB(r0, r3);
View file
pipewire-0.3.59.tar.gz/src/modules/module-filter-chain/pffft.h -> pipewire-0.3.60.tar.gz/src/modules/module-filter-chain/pffft.h
Changed
@@ -165,13 +165,13 @@ /* the float buffers must have the correct alignment (16-byte boundary on intel and powerpc). This function may be used to obtain such - correctly aligned buffers. + correctly aligned buffers. */ void *pffft_aligned_malloc(size_t nb_bytes); void pffft_aligned_free(void *); /* return 4 or 1 depending on whether support for SSE/Altivec instructions was enabled when building pffft.c */ - int pffft_simd_size(); + int pffft_simd_size(void); void pffft_select_cpu(int flags);
View file
pipewire-0.3.59.tar.gz/src/modules/module-loopback.c -> pipewire-0.3.60.tar.gz/src/modules/module-loopback.c
Changed
@@ -35,6 +35,7 @@ #include <spa/utils/result.h> #include <spa/utils/string.h> #include <spa/utils/json.h> +#include <spa/utils/ringbuffer.h> #include <spa/param/profiler.h> #include <spa/debug/pod.h> @@ -54,6 +55,7 @@ * ## Module Options * * - `node.description`: a human readable name for the loopback streams + * - `target.delay.sec`: delay in seconds as float (Since 0.3.60) * - `capture.props = {}`: properties to be passed to the input stream * - `playback.props = {}`: properties to be passed to the output stream * @@ -91,6 +93,7 @@ * { name = libpipewire-module-loopback * args = { * node.description = "CM106 Stereo Pair 2" + * #target.delay.sec = 1.5 * capture.props = { * node.name = "CM106_stereo_pair_2" * media.class = "Audio/Sink" @@ -128,6 +131,7 @@ " audio.rate=<sample rate> " " audio.channels=<number of channels> " " audio.position=<channel map> " + " target.delay.sec=<delay as seconds in float> " " capture.props=<properties> " " playback.props=<properties> " }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, @@ -160,13 +164,21 @@ struct pw_stream *capture; struct spa_hook capture_listener; struct spa_audio_info_raw capture_info; + struct spa_latency_info capture_latency; struct pw_properties *playback_props; struct pw_stream *playback; struct spa_hook playback_listener; struct spa_audio_info_raw playback_info; + struct spa_latency_info playback_latency; unsigned int do_disconnect:1; + unsigned int recalc_delay:1; + + float target_delay; + struct spa_ringbuffer buffer; + uint8_t *buffer_data; + uint32_t buffer_size; }; static void capture_destroy(void *d) @@ -176,6 +188,28 @@ impl->capture = NULL; } +static void recalculate_delay(struct impl *impl) +{ + uint32_t target = impl->capture_info.rate * impl->target_delay, cdelay, pdelay; + uint32_t delay, w; + struct pw_time pwt; + + pw_stream_get_time_n(impl->playback, &pwt, sizeof(pwt)); + pdelay = pwt.delay; + pw_stream_get_time_n(impl->capture, &pwt, sizeof(pwt)); + cdelay = pwt.delay; + + delay = target - SPA_MIN(target, pdelay + cdelay); + delay = SPA_MIN(delay, impl->buffer_size / 4); + + spa_ringbuffer_get_write_index(&impl->buffer, &w); + spa_ringbuffer_read_update(&impl->buffer, w - (delay * 4)); + + pw_log_info("target:%d c:%d + p:%d + delay:%d = (%d)", + target, cdelay, pdelay, delay, + cdelay + pdelay + delay); +} + static void capture_process(void *d) { struct impl *impl = d; @@ -188,6 +222,11 @@ struct pw_buffer *in, *out; uint32_t i; + if (impl->recalc_delay) { + recalculate_delay(impl); + impl->recalc_delay = false; + } + if ((in = pw_stream_dequeue_buffer(impl->capture)) == NULL) pw_log_debug("out of capture buffers: %m"); @@ -199,6 +238,7 @@ int32_t stride = 0; struct spa_data *d; const void *srcin->buffer->n_datas; + uint32_t r, w, buffer_size; for (i = 0; i < in->buffer->n_datas; i++) { uint32_t offs, size; @@ -211,13 +251,33 @@ outsize = SPA_MIN(outsize, size); stride = SPA_MAX(stride, d->chunk->stride); } + if (impl->buffer_size > 0) { + buffer_size = impl->buffer_size; + spa_ringbuffer_get_write_index(&impl->buffer, &w); + for (i = 0; i < in->buffer->n_datas; i++) { + void *buffer_data = &impl->buffer_datai * buffer_size; + spa_ringbuffer_write_data(&impl->buffer, + buffer_data, buffer_size, + w % buffer_size, srci, outsize); + srci = buffer_data; + } + w += outsize; + spa_ringbuffer_write_update(&impl->buffer, w); + spa_ringbuffer_get_read_index(&impl->buffer, &r); + } else { + r = 0; + buffer_size = outsize; + } for (i = 0; i < out->buffer->n_datas; i++) { d = &out->buffer->datasi; outsize = SPA_MIN(outsize, d->maxsize); if (i < in->buffer->n_datas) - memcpy(d->data, srci, outsize); + spa_ringbuffer_read_data(&impl->buffer, + srci, buffer_size, + r % buffer_size, + d->data, outsize); else memset(d->data, 0, outsize); @@ -225,6 +285,10 @@ d->chunk->size = outsize; d->chunk->stride = stride; } + if (impl->buffer_size > 0) { + r += outsize; + spa_ringbuffer_read_update(&impl->buffer, r); + } } if (in != NULL) @@ -234,7 +298,7 @@ } static void param_latency_changed(struct impl *impl, const struct spa_pod *param, - struct pw_stream *other) + struct spa_latency_info *info, struct pw_stream *other) { struct spa_latency_info latency; uint8_t buffer1024; @@ -244,9 +308,13 @@ if (spa_latency_parse(param, &latency) < 0) return; + *info = latency; + spa_pod_builder_init(&b, buffer, sizeof(buffer)); params0 = spa_latency_build(&b, SPA_PARAM_Latency, &latency); pw_stream_update_params(other, params, 1); + + impl->recalc_delay = true; } static void stream_state_changed(void *data, enum pw_stream_state old, @@ -257,6 +325,7 @@ case PW_STREAM_STATE_PAUSED: pw_stream_flush(impl->playback, false); pw_stream_flush(impl->capture, false); + impl->recalc_delay = true; break; case PW_STREAM_STATE_UNCONNECTED: pw_log_info("module %p: unconnected", impl); @@ -270,13 +339,53 @@ } } +static void recalculate_buffer(struct impl *impl) +{ + if (impl->target_delay > 0.0f) { + uint32_t delay = impl->capture_info.rate * impl->target_delay; + void *data; + + impl->buffer_size = (delay + (1u<<15)) * 4; + data = realloc(impl->buffer_data, impl->buffer_size * impl->capture_info.channels); + if (data == NULL) { + pw_log_warn("can't allocate delay buffer, delay disabled: %m"); + impl->buffer_size = 0; + free(impl->buffer_data); + } + impl->buffer_data = data; + spa_ringbuffer_init(&impl->buffer); + } else { + impl->buffer_size = 0; + free(impl->buffer_data); + impl->buffer_data = NULL; + } + pw_log_info("configured delay:%f buffer:%d", impl->target_delay, impl->buffer_size); + impl->recalc_delay = true; +} + static void capture_param_changed(void *data, uint32_t id, const struct spa_pod *param) { struct impl *impl = data; switch (id) { + case SPA_PARAM_Format: + { + struct spa_audio_info_raw info; + if (param == NULL) + return; + if (spa_format_audio_raw_parse(param, &info) < 0) + return; + if (info.rate == 0 || + info.channels == 0 || + info.channels > SPA_AUDIO_MAX_CHANNELS) + return; + + impl->capture_info = info; + recalculate_buffer(impl); + break; + } case SPA_PARAM_Latency: - param_latency_changed(impl, param, impl->playback); + param_latency_changed(impl, param, &impl->capture_latency, impl->playback); break; } } @@ -302,7 +411,7 @@ switch (id) { case SPA_PARAM_Latency: - param_latency_changed(impl, param, impl->capture); + param_latency_changed(impl, param, &impl->playback_latency, impl->capture); break; } } @@ -539,6 +648,15 @@ if ((str = pw_properties_get(props, "playback.props")) != NULL) pw_properties_update_string(impl->playback_props, str, strlen(str)); + if ((str = pw_properties_get(props, "target.delay.sec")) != NULL) + spa_atof(str, &impl->target_delay); + if (impl->target_delay > 0.0f && + pw_properties_get(props, PW_KEY_NODE_LATENCY) == NULL) + /* a source and sink (USB) usually have a 1.5 quantum delay, so we use + * a 2 times smaller quantum to compensate */ + pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", + (unsigned)(impl->target_delay * 48000 / 3), 48000); + copy_props(impl, props, PW_KEY_AUDIO_RATE); copy_props(impl, props, PW_KEY_AUDIO_CHANNELS); copy_props(impl, props, SPA_KEY_AUDIO_POSITION);
View file
pipewire-0.3.59.tar.gz/src/modules/module-pipe-tunnel.c -> pipewire-0.3.60.tar.gz/src/modules/module-pipe-tunnel.c
Changed
@@ -55,7 +55,7 @@ /** \page page_module_pipe_tunnel PipeWire Module: Unix Pipe Tunnel * * The pipe-tunnel module provides a source or sink that tunnels all audio to - * a unix pipe. + * or from a unix pipe respectively. * * ## Module Options * @@ -315,7 +315,7 @@ .process = capture_stream_process }; -static int create_stream(struct impl *impl) +static int create_stream(struct impl *impl) { int res; uint32_t n_params; @@ -664,6 +664,7 @@ copy_props(impl, props, PW_KEY_NODE_LATENCY); copy_props(impl, props, PW_KEY_NODE_VIRTUAL); copy_props(impl, props, PW_KEY_MEDIA_CLASS); + copy_props(impl, props, PW_KEY_TARGET_OBJECT); parse_audio_info(impl->stream_props, &impl->info);
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-native.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-native.c
Changed
@@ -228,6 +228,9 @@ hex = true; if (hex) spa_debug_mem(0, msg->data, msg->size); + + pw_logt_debug(mod_topic_connection, "%s ****", prefix); + } static void pre_demarshal(struct pw_protocol_native_connection *conn,
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-native/connection.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-native/connection.c
Changed
@@ -728,9 +728,12 @@ buf->n_fds = buf->msg.n_fds; if (mod_topic_connection->level >= SPA_LOG_LEVEL_DEBUG) { - pw_log_debug(">>>>>>>>> out: id:%d op:%d size:%d seq:%d", + pw_logt_debug(mod_topic_connection, + ">>>>>>>>> out: id:%d op:%d size:%d seq:%d", buf->msg.id, buf->msg.opcode, size, buf->msg.seq); spa_debug_pod(0, NULL, SPA_PTROFF(p, impl->hdr_size, struct spa_pod)); + pw_logt_debug(mod_topic_connection, + ">>>>>>>>> out: done"); } buf->seq = (buf->seq + 1) & SPA_ASYNC_SEQ_MASK;
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/client.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/client.c
Changed
@@ -266,17 +266,12 @@ int res = -errno; if (res == -EINTR) continue; - if (res != -EAGAIN && res != -EWOULDBLOCK) - pw_log_warn("client %p: send channel:%u %zu, error %d: %m", - client, m->channel, size, res); return res; } - client->out_index += sent; break; } } - return 0; } @@ -296,7 +291,6 @@ if (res != -EAGAIN && res != -EWOULDBLOCK) return res; } - return 0; }
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/extension.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/extension.c
Changed
@@ -37,12 +37,9 @@ const struct extension *extension_find(uint32_t index, const char *name) { - const struct extension *ext; - - SPA_FOR_EACH_ELEMENT(extensions, ext) { + SPA_FOR_EACH_ELEMENT_VAR(extensions, ext) { if (index == ext->index || spa_streq(name, ext->name)) return ext; } - return NULL; }
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/format.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/format.c
Changed
@@ -159,32 +159,28 @@ uint32_t format_paname2id(const char *name, size_t size) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) { - if (audio_formatsi.name != NULL && - strncmp(name, audio_formatsi.name, size) == 0) - return audio_formatsi.id; + SPA_FOR_EACH_ELEMENT_VAR(audio_formats, f) { + if (f->name != NULL && + strncmp(name, f->name, size) == 0) + return f->id; } return SPA_AUDIO_FORMAT_UNKNOWN; } enum sample_format format_id2pa(uint32_t id) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) { - if (id == audio_formatsi.id) - return audio_formatsi.pa; + SPA_FOR_EACH_ELEMENT_VAR(audio_formats, f) { + if (id == f->id) + return f->pa; } return SAMPLE_INVALID; } const char *format_id2paname(uint32_t id) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) { - if (id == audio_formatsi.id && - audio_formatsi.name != NULL) - return audio_formatsi.name; + SPA_FOR_EACH_ELEMENT_VAR(audio_formats, f) { + if (id == f->id && f->name != NULL) + return f->name; } return "invalid"; } @@ -266,21 +262,18 @@ const char *channel_id2paname(uint32_t id, uint32_t *aux) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(audio_channels); i++) { - if (id == audio_channelsi.channel && - audio_channelsi.name != NULL) - return audio_channelsi.name; + SPA_FOR_EACH_ELEMENT_VAR(audio_channels, i) { + if (id == i->channel && i->name != NULL) + return i->name; } return audio_channelsCHANNEL_POSITION_AUX0 + ((*aux)++ & 31).name; } uint32_t channel_paname2id(const char *name, size_t size) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(audio_channels); i++) { - if (strncmp(name, audio_channelsi.name, size) == 0) - return audio_channelsi.channel; + SPA_FOR_EACH_ELEMENT_VAR(audio_channels, i) { + if (strncmp(name, i->name, size) == 0) + return i->channel; } return SPA_AUDIO_CHANNEL_UNKNOWN; } @@ -424,25 +417,6 @@ return ENCODING_ANY; } -static inline int -audio_raw_parse_opt(const struct spa_pod *format, struct spa_audio_info_raw *info) -{ - struct spa_pod *position = NULL; - int res; - info->flags = 0; - res = spa_pod_parse_object(format, - SPA_TYPE_OBJECT_Format, NULL, - SPA_FORMAT_AUDIO_format, SPA_POD_OPT_Id(&info->format), - SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate), - SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), - SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); - if (position == NULL || - !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS)) - SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); - - return res; -} - int format_parse_param(const struct spa_pod *param, bool collect, struct sample_spec *ss, struct channel_map *map, const struct sample_spec *def_ss, const struct channel_map *def_map) @@ -458,14 +432,17 @@ switch (info.media_subtype) { case SPA_MEDIA_SUBTYPE_raw: + if (spa_format_audio_raw_parse(param, &info.info.raw) < 0) + return -ENOTSUP; if (def_ss != NULL) { if (ss != NULL) *ss = *def_ss; - if (audio_raw_parse_opt(param, &info.info.raw) < 0) - return -ENOTSUP; } else { - if (spa_format_audio_raw_parse(param, &info.info.raw) < 0) - return -ENOTSUP; + if (info.info.raw.format == 0 || + info.info.raw.rate == 0 || + info.info.raw.channels == 0 || + info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + return -ENOTSUP; } break; case SPA_MEDIA_SUBTYPE_iec958:
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/manager.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/manager.c
Changed
@@ -33,8 +33,6 @@ #include "log.h" #include "module-protocol-pulse/server.h" -#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) @@ -85,8 +83,6 @@ struct spa_hook proxy_listener; struct spa_hook object_listener; - int param_seqMAX_PARAMS; - struct spa_list data_list; }; @@ -113,7 +109,7 @@ } static struct pw_manager_param *add_param(struct spa_list *params, - int seq, int *param_seq, uint32_t id, const struct spa_pod *param) + int seq, uint32_t id, const struct spa_pod *param) { struct pw_manager_param *p; @@ -125,24 +121,12 @@ id = SPA_POD_OBJECT_ID(param); } - if (id >= MAX_PARAMS) { - pw_log_error("too big param id %d", id); - errno = EINVAL; - return NULL; - } - - if (seq != param_seqid) { - pw_log_debug("ignoring param %d, seq:%d != current_seq:%d", - id, seq, param_seqid); - errno = EBUSY; - return NULL; - } - p = malloc(sizeof(*p) + (param != NULL ? SPA_POD_SIZE(param) : 0)); if (p == NULL) return NULL; p->id = id; + p->seq = seq; if (param != NULL) { p->param = SPA_PTROFF(p, sizeof(*p), struct spa_pod); memcpy(p->param, param, SPA_POD_SIZE(param)); @@ -167,7 +151,6 @@ return false; } - static struct object *find_object_by_id(struct manager *m, uint32_t id) { struct object *o; @@ -180,7 +163,19 @@ static void object_update_params(struct object *o) { - struct pw_manager_param *p; + struct pw_manager_param *p, *t; + uint32_t i; + + for (i = 0; i < o->this.n_params; i++) { + spa_list_for_each_safe(p, t, &o->pending_list, link) { + if (p->id == o->this.paramsi.id && + p->seq != o->this.paramsi.seq && + p->param != NULL) { + spa_list_remove(&p->link); + free(p); + } + } + } spa_list_consume(p, &o->pending_list, link) { spa_list_remove(&p->link); @@ -236,6 +231,8 @@ pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask); info = o->this.info = pw_client_info_merge(o->this.info, info, o->this.changed == 0); + if (info == NULL) + return; if (info->change_mask & PW_CLIENT_CHANGE_MASK_PROPS) changed++; @@ -275,6 +272,8 @@ pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask); info = o->this.info = pw_module_info_merge(o->this.info, info, o->this.changed == 0); + if (info == NULL) + return; if (info->change_mask & PW_MODULE_CHANGE_MASK_PROPS) changed++; @@ -314,6 +313,11 @@ pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask); info = o->this.info = pw_device_info_merge(o->this.info, info, o->this.changed == 0); + if (info == NULL) + return; + + o->this.n_params = info->n_params; + o->this.params = info->params; if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS) changed++; @@ -327,11 +331,6 @@ continue; info->paramsi.user = 0; - if (id >= MAX_PARAMS) { - pw_log_error("too big param id %d", id); - continue; - } - switch (id) { case SPA_PARAM_EnumProfile: case SPA_PARAM_Profile: @@ -341,14 +340,14 @@ case SPA_PARAM_Route: break; } - add_param(&o->pending_list, o->param_seqid, o->param_seq, id, NULL); + add_param(&o->pending_list, info->paramsi.seq, id, NULL); if (!(info->paramsi.flags & SPA_PARAM_INFO_READ)) continue; res = pw_device_enum_params((struct pw_device*)o->this.proxy, - ++o->param_seqid, id, 0, -1, NULL); + ++info->paramsi.seq, id, 0, -1, NULL); if (SPA_RESULT_IS_ASYNC(res)) - o->param_seqid = res; + info->paramsi.seq = res; } } if (changed) { @@ -385,7 +384,7 @@ struct manager *m = o->manager; struct pw_manager_param *p; - p = add_param(&o->pending_list, seq, o->param_seq, id, param); + p = add_param(&o->pending_list, seq, id, param); if (p == NULL) return; @@ -434,6 +433,11 @@ pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask); info = o->this.info = pw_node_info_merge(o->this.info, info, o->this.changed == 0); + if (info == NULL) + return; + + o->this.n_params = info->n_params; + o->this.params = info->params; if (info->change_mask & PW_NODE_CHANGE_MASK_STATE) changed++; @@ -450,20 +454,15 @@ continue; info->paramsi.user = 0; - if (id >= MAX_PARAMS) { - pw_log_error("too big param id %d", id); - continue; - } - changed++; - add_param(&o->pending_list, o->param_seqid, o->param_seq, id, NULL); + add_param(&o->pending_list, info->paramsi.seq, id, NULL); if (!(info->paramsi.flags & SPA_PARAM_INFO_READ)) continue; res = pw_node_enum_params((struct pw_node*)o->this.proxy, - ++o->param_seqid, id, 0, -1, NULL); + ++info->paramsi.seq, id, 0, -1, NULL); if (SPA_RESULT_IS_ASYNC(res)) - o->param_seqid = res; + info->paramsi.seq = res; } } if (changed) { @@ -477,7 +476,7 @@ const struct spa_pod *param) { struct object *o = data; - add_param(&o->pending_list, seq, o->param_seq, id, param); + add_param(&o->pending_list, seq, id, param); } static const struct pw_node_events node_events = { @@ -553,11 +552,10 @@ static const struct object_info *find_info(const char *type, uint32_t version) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(objects); i++) { - if (spa_streq(objectsi->type, type) && - objectsi->version <= version) - return objectsi; + SPA_FOR_EACH_ELEMENT_VAR(objects, i) { + if (spa_streq((*i)->type, type) && + (*i)->version <= version) + return *i; } return NULL; }
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/manager.h -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/manager.h
Changed
@@ -72,6 +72,7 @@ struct pw_manager_param { uint32_t id; + int32_t seq; struct spa_list link; /**< link in manager_object param_list */ struct spa_pod *param; }; @@ -92,6 +93,9 @@ int changed; void *info; + struct spa_param_info *params; + uint32_t n_params; + struct spa_list param_list; unsigned int creating:1; unsigned int removing:1;
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/modules/module-loopback.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/modules/module-loopback.c
Changed
@@ -47,6 +47,7 @@ struct pw_properties *playback_props; struct spa_audio_info_raw info; + uint32_t latency_msec; }; static void module_destroy(void *data) @@ -68,6 +69,7 @@ FILE *f; char *args; size_t size, i; + char val256; pw_properties_setf(data->capture_props, PW_KEY_NODE_GROUP, "loopback-%u", module->index); pw_properties_setf(data->playback_props, PW_KEY_NODE_GROUP, "loopback-%u", module->index); @@ -88,6 +90,10 @@ fprintf(f, " "); } } + if (data->latency_msec != 0) + fprintf(f, " target.delay.sec = %s", + spa_json_format_float(val, sizeof(val), + data->latency_msec / 1000.0f)); fprintf(f, " capture.props = {"); pw_properties_serialize_dict(f, &data->capture_props->dict, 0); fprintf(f, " } playback.props = {"); @@ -204,15 +210,8 @@ pw_properties_set(props, "remix", NULL); } - if ((str = pw_properties_get(props, "latency_msec")) != NULL) { - /* Half the latency on each of the playback and capture streams */ - pw_properties_setf(capture_props, PW_KEY_NODE_LATENCY, "%s/2000", str); - pw_properties_setf(playback_props, PW_KEY_NODE_LATENCY, "%s/2000", str); - pw_properties_set(props, "latency_msec", NULL); - } else { - pw_properties_set(capture_props, PW_KEY_NODE_LATENCY, "100/1000"); - pw_properties_set(playback_props, PW_KEY_NODE_LATENCY, "100/1000"); - } + if ((str = pw_properties_get(props, "latency_msec")) != NULL) + d->latency_msec = atoi(str); if ((str = pw_properties_get(props, "sink_input_properties")) != NULL) { module_args_add_props(playback_props, str);
View file
pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/modules/module-rtp-recv.c
Added
@@ -0,0 +1,165 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <spa/utils/hook.h> +#include <pipewire/pipewire.h> +#include <pipewire/private.h> + +#include "../defs.h" +#include "../module.h" + +#define NAME "rtp-recv" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +struct module_rtp_recv_data { + struct module *module; + + struct spa_hook mod_listener; + struct pw_impl_module *mod; + + struct pw_properties *stream_props; + struct pw_properties *global_props; +}; + +static void module_destroy(void *data) +{ + struct module_rtp_recv_data *d = data; + spa_hook_remove(&d->mod_listener); + d->mod = NULL; + module_schedule_unload(d->module); +} + +static const struct pw_impl_module_events module_events = { + PW_VERSION_IMPL_MODULE_EVENTS, + .destroy = module_destroy +}; + +static int module_rtp_recv_load(struct client *client, struct module *module) +{ + struct module_rtp_recv_data *data = module->user_data; + FILE *f; + char *args; + size_t size; + + pw_properties_setf(data->stream_props, "pulse.module.id", + "%u", module->index); + + if ((f = open_memstream(&args, &size)) == NULL) + return -errno; + + fprintf(f, "{"); + pw_properties_serialize_dict(f, &data->global_props->dict, 0); + fprintf(f, " stream.props = {"); + pw_properties_serialize_dict(f, &data->stream_props->dict, 0); + fprintf(f, " } }"); + fclose(f); + + data->mod = pw_context_load_module(module->impl->context, + "libpipewire-module-rtp-source", + args, NULL); + + free(args); + + if (data->mod == NULL) + return -errno; + + pw_impl_module_add_listener(data->mod, + &data->mod_listener, + &module_events, data); + + return 0; +} + +static int module_rtp_recv_unload(struct module *module) +{ + struct module_rtp_recv_data *d = module->user_data; + + if (d->mod) { + spa_hook_remove(&d->mod_listener); + pw_impl_module_destroy(d->mod); + d->mod = NULL; + } + + pw_properties_free(d->global_props); + pw_properties_free(d->stream_props); + + return 0; +} + +static const struct spa_dict_item module_rtp_recv_info = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, + { PW_KEY_MODULE_DESCRIPTION, "Receive data from a network via RTP/SAP/SDP" }, + { PW_KEY_MODULE_USAGE, "sink=<name of the sink> " + "sap_address=<multicast address to listen on> " + "latency_msec=<latency in ms> " }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + +static int module_rtp_recv_prepare(struct module * const module) +{ + struct module_rtp_recv_data * const d = module->user_data; + struct pw_properties * const props = module->props; + struct pw_properties *stream_props = NULL, *global_props = NULL; + const char *str; + int res; + + PW_LOG_TOPIC_INIT(mod_topic); + + stream_props = pw_properties_new(NULL, NULL); + global_props = pw_properties_new(NULL, NULL); + if (!stream_props || !global_props) { + res = -errno; + goto out; + } + if ((str = pw_properties_get(props, "sink")) != NULL) + pw_properties_set(stream_props, PW_KEY_NODE_TARGET, str); + + if ((str = pw_properties_get(props, "sap_address")) != NULL) + pw_properties_set(global_props, "sap.ip", str); + + if ((str = pw_properties_get(props, "latency_msec")) != NULL) + pw_properties_set(global_props, "sess.latency.msec", str); + + d->module = module; + d->stream_props = stream_props; + d->global_props = global_props; + + return 0; +out: + pw_properties_free(stream_props); + pw_properties_free(global_props); + + return res; +} + +DEFINE_MODULE_INFO(module_rtp_recv) = { + .name = "module-rtp-recv", + .prepare = module_rtp_recv_prepare, + .load = module_rtp_recv_load, + .unload = module_rtp_recv_unload, + .properties = &SPA_DICT_INIT_ARRAY(module_rtp_recv_info), + .data_size = sizeof(struct module_rtp_recv_data), +};
View file
pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/modules/module-rtp-send.c
Added
@@ -0,0 +1,226 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <spa/utils/hook.h> +#include <pipewire/pipewire.h> +#include <pipewire/private.h> + +#include "../defs.h" +#include "../module.h" + +#define NAME "rtp-send" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +struct module_rtp_send_data { + struct module *module; + + struct spa_hook mod_listener; + struct pw_impl_module *mod; + + struct pw_properties *stream_props; + struct pw_properties *global_props; + struct spa_audio_info_raw info; +}; + +static void module_destroy(void *data) +{ + struct module_rtp_send_data *d = data; + spa_hook_remove(&d->mod_listener); + d->mod = NULL; + module_schedule_unload(d->module); +} + +static const struct pw_impl_module_events module_events = { + PW_VERSION_IMPL_MODULE_EVENTS, + .destroy = module_destroy +}; + +static int module_rtp_send_load(struct client *client, struct module *module) +{ + struct module_rtp_send_data *data = module->user_data; + FILE *f; + char *args; + size_t size; + uint32_t i; + + pw_properties_setf(data->stream_props, "pulse.module.id", + "%u", module->index); + + if ((f = open_memstream(&args, &size)) == NULL) + return -errno; + + fprintf(f, "{"); + pw_properties_serialize_dict(f, &data->global_props->dict, 0); + if (data->info.format != 0) + fprintf(f, " \"audio.format\": \"%s\"", format_id2name(data->info.format)); + if (data->info.rate != 0) + fprintf(f, " \"audio.rate\": %u,", data->info.rate); + if (data->info.channels != 0) { + fprintf(f, " \"audio.channels\": %u,", data->info.channels); + if (!(data->info.flags & SPA_AUDIO_FLAG_UNPOSITIONED)) { + fprintf(f, " \"audio.position\": "); + for (i = 0; i < data->info.channels; i++) + fprintf(f, "%s\"%s\"", i == 0 ? "" : ",", + channel_id2name(data->info.positioni)); + fprintf(f, " ,"); + } + } + fprintf(f, " stream.props = {"); + pw_properties_serialize_dict(f, &data->stream_props->dict, 0); + fprintf(f, " } }"); + fclose(f); + + data->mod = pw_context_load_module(module->impl->context, + "libpipewire-module-rtp-sink", + args, NULL); + + free(args); + + if (data->mod == NULL) + return -errno; + + pw_impl_module_add_listener(data->mod, + &data->mod_listener, + &module_events, data); + + return 0; +} + +static int module_rtp_send_unload(struct module *module) +{ + struct module_rtp_send_data *d = module->user_data; + + if (d->mod) { + spa_hook_remove(&d->mod_listener); + pw_impl_module_destroy(d->mod); + d->mod = NULL; + } + + pw_properties_free(d->global_props); + pw_properties_free(d->stream_props); + + return 0; +} + +static const struct spa_dict_item module_rtp_send_info = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, + { PW_KEY_MODULE_DESCRIPTION, "Read data from source and send it to the network via RTP/SAP/SDP" }, + { PW_KEY_MODULE_USAGE, "source=<name of the source> " + "format=<sample format> " + "channels=<number of channels> " + "rate=<sample rate> " + "destination_ip=<destination IP address> " + "source_ip=<source IP address> " + "port=<port number> " + "mtu=<maximum transfer unit> " + "loop=<loopback to local host?> " + "ttl=<ttl value> " + "inhibit_auto_suspend=<always|never|only_with_non_monitor_sources> " + "stream_name=<name of the stream> " + "enable_opus=<enable OPUS codec>" }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + +static int module_rtp_send_prepare(struct module * const module) +{ + struct module_rtp_send_data * const d = module->user_data; + struct pw_properties * const props = module->props; + struct pw_properties *stream_props = NULL, *global_props = NULL; + struct spa_audio_info_raw info = { 0 }; + const char *str; + int res; + + PW_LOG_TOPIC_INIT(mod_topic); + + stream_props = pw_properties_new(NULL, NULL); + global_props = pw_properties_new(NULL, NULL); + if (!stream_props || !global_props) { + res = -errno; + goto out; + } + + if ((str = pw_properties_get(props, "source")) != NULL) { + pw_properties_set(stream_props, PW_KEY_NODE_TARGET, str); + if (spa_strendswith(str, ".monitor")) { + pw_properties_setf(stream_props, PW_KEY_NODE_TARGET, + "%.*s", (int)strlen(str)-8, str); + pw_properties_set(stream_props, PW_KEY_STREAM_CAPTURE_SINK, + "true"); + } else { + pw_properties_set(stream_props, PW_KEY_NODE_TARGET, str); + } + } + if (module_args_to_audioinfo(module->impl, props, &info) < 0) { + res = -EINVAL; + goto out; + } + info.format = 0; + if ((str = pw_properties_get(props, "format")) != NULL) { + if ((info.format = format_paname2id(str, strlen(str))) == + SPA_AUDIO_FORMAT_UNKNOWN) { + pw_log_error("unknown format %s", str); + res = -EINVAL; + goto out; + } + } + + if ((str = pw_properties_get(props, "destination_ip")) != NULL) + pw_properties_set(global_props, "destination.ip", str); + if ((str = pw_properties_get(props, "source_ip")) != NULL) + pw_properties_set(global_props, "source.ip", str); + if ((str = pw_properties_get(props, "port")) != NULL) + pw_properties_set(global_props, "destination.port", str); + if ((str = pw_properties_get(props, "mtu")) != NULL) + pw_properties_set(global_props, "net.mtu", str); + if ((str = pw_properties_get(props, "loop")) != NULL) + pw_properties_set(global_props, "net.loop", + module_args_parse_bool(str) ? "true" : "false"); + if ((str = pw_properties_get(props, "ttl")) != NULL) + pw_properties_set(global_props, "net.ttl", str); + if ((str = pw_properties_get(props, "stream_name")) != NULL) + pw_properties_set(global_props, "sess.name", str); + + d->module = module; + d->stream_props = stream_props; + d->global_props = global_props; + d->info = info; + + return 0; +out: + pw_properties_free(stream_props); + pw_properties_free(global_props); + + return res; +} + +DEFINE_MODULE_INFO(module_rtp_send) = { + .name = "module-rtp-send", + .prepare = module_rtp_send_prepare, + .load = module_rtp_send_load, + .unload = module_rtp_send_unload, + .properties = &SPA_DICT_INIT_ARRAY(module_rtp_send_info), + .data_size = sizeof(struct module_rtp_send_data), +};
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/modules/module-zeroconf-discover.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/modules/module-zeroconf-discover.c
Changed
@@ -40,6 +40,8 @@ struct spa_hook mod_listener; struct pw_impl_module *mod; + + uint32_t latency_msec; }; static void module_destroy(void *data) @@ -58,10 +60,25 @@ static int module_zeroconf_discover_load(struct client *client, struct module *module) { struct module_zeroconf_discover_data *data = module->user_data; + FILE *f; + char *args; + size_t size; + + if ((f = open_memstream(&args, &size)) == NULL) + return -errno; + + fprintf(f, "{"); + if (data->latency_msec > 0) + fprintf(f, " pulse.latency = %u ", data->latency_msec); + fprintf(f, "}"); + fclose(f); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-zeroconf-discover", - NULL, NULL); + args, NULL); + + free(args); + if (data->mod == NULL) return -errno; @@ -88,7 +105,8 @@ static const struct spa_dict_item module_zeroconf_discover_info = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.con>" }, { PW_KEY_MODULE_DESCRIPTION, "mDNS/DNS-SD Service Discovery" }, - { PW_KEY_MODULE_USAGE, "" }, + { PW_KEY_MODULE_USAGE, + "latency_msec=<fixed latency in ms> " }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; @@ -96,9 +114,12 @@ { PW_LOG_TOPIC_INIT(mod_topic); + struct pw_properties * const props = module->props; struct module_zeroconf_discover_data * const data = module->user_data; data->module = module; + pw_properties_fetch_uint32(props, "latency_msec", &data->latency_msec); + return 0; }
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/modules/module-zeroconf-publish.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/modules/module-zeroconf-publish.c
Changed
@@ -380,8 +380,7 @@ txt = avahi_string_list_add_pair(txt, "channel_map", channel_map_snprint(cm, sizeof(cm), &s->cm)); txt = avahi_string_list_add_pair(txt, "subtype", subtype_texts->subtype); - const struct mapping *m; - SPA_FOR_EACH_ELEMENT(mappings, m) { + SPA_FOR_EACH_ELEMENT_VAR(mappings, m) { const char *value = pw_properties_get(s->props, m->pw_key); if (value != NULL) txt = avahi_string_list_add_pair(txt, m->txt_key, value);
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/pulse-server.c
Changed
@@ -1428,15 +1428,16 @@ } if ((stream->attr.prebuf == 0 || do_flush) && !stream->corked) { if (avail > 0) { + avail = SPA_MIN((uint32_t)avail, size); spa_ringbuffer_read_data(&stream->ring, stream->buffer, MAXLENGTH, index % MAXLENGTH, - p, SPA_MIN((uint32_t)avail, size)); + p, avail); index += avail; + pd.read_inc = avail; + spa_ringbuffer_read_update(&stream->ring, index); } pd.playing_for = size; - pd.read_inc = size; - spa_ringbuffer_read_update(&stream->ring, index); } pw_log_debug("%p: %s underrun read:%u avail:%d max:%u", stream, client->name, index, avail, minreq);
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/quirks.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/quirks.c
Changed
@@ -38,10 +38,9 @@ { "force-s16-info", QUIRK_FORCE_S16_FORMAT }, { "remove-capture-dont-move", QUIRK_REMOVE_CAPTURE_DONT_MOVE }, }; - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(quirk_keys); ++i) { - if (spa_streq(str, quirk_keysi.key)) - return quirk_keysi.value; + SPA_FOR_EACH_ELEMENT_VAR(quirk_keys, i) { + if (spa_streq(str, i->key)) + return i->value; } return 0; }
View file
pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/server.c -> pipewire-0.3.60.tar.gz/src/modules/module-protocol-pulse/server.c
Changed
@@ -616,7 +616,6 @@ pw_log_warn("server %p: unlink('%s') failed: %m", server, addr_un->sun_path); } - if (bind(fd, (const struct sockaddr *) addr_un, SUN_LEN(addr_un)) < 0) { res = -errno; pw_log_warn("server %p: bind() to '%s' failed: %m", @@ -624,6 +623,10 @@ goto error_close; } + if (chmod(addr_un->sun_path, 0777) < 0) + pw_log_warn("server %p: chmod('%s') failed: %m", + server, addr_un->sun_path); + if (listen(fd, server->listen_backlog) < 0) { res = -errno; pw_log_warn("server %p: listen() on '%s' failed: %m",
View file
pipewire-0.3.59.tar.gz/src/modules/module-pulse-tunnel.c -> pipewire-0.3.60.tar.gz/src/modules/module-pulse-tunnel.c
Changed
@@ -183,6 +183,8 @@ pa_context *pa_context; pa_stream *pa_stream; + struct ratelimit rate_limit; + uint32_t target_latency; uint32_t current_latency; uint32_t target_buffer; @@ -524,9 +526,10 @@ { struct impl *impl = userdata; int32_t avail; - uint32_t index, len, offset, l0, l1; + uint32_t index; + size_t size; pa_usec_t latency; - int negative; + int negative, res; if (impl->resync) { impl->resync = false; @@ -542,43 +545,54 @@ impl->current_latency += avail / impl->frame_size; while (avail < (int32_t)length) { + uint32_t maxsize = SPA_ROUND_DOWN(sizeof(impl->empty), impl->frame_size); /* send silence for the data we don't have */ - len = SPA_MIN(length - avail, sizeof(impl->empty)); - pa_stream_write(impl->pa_stream, - impl->empty, len, - NULL, 0, PA_SEEK_RELATIVE); - length -= len; + size = SPA_MIN(length - avail, maxsize); + if ((res = pa_stream_write(impl->pa_stream, + impl->empty, size, + NULL, 0, PA_SEEK_RELATIVE)) != 0) + pw_log_warn("error writing stream: %s", pa_strerror(res)); + length -= size; } - if (length > 0 && avail >= (int32_t)length) { - /* always send as much as is requested */ - len = length; - offset = index & RINGBUFFER_MASK; - l0 = SPA_MIN(len, RINGBUFFER_SIZE - offset); - l1 = len - l0; - - pa_stream_write(impl->pa_stream, - SPA_PTROFF(impl->buffer, offset, void), l0, - NULL, 0, PA_SEEK_RELATIVE); - - if (SPA_UNLIKELY(l1 > 0)) { - pa_stream_write(impl->pa_stream, - impl->buffer, l1, - NULL, 0, PA_SEEK_RELATIVE); - } - index += len; + while (length > 0 && avail >= (int32_t)length) { + void *data; + + size = length; + pa_stream_begin_write(impl->pa_stream, &data, &size); + + spa_ringbuffer_read_data(&impl->ring, + impl->buffer, RINGBUFFER_SIZE, + index & RINGBUFFER_MASK, + data, size); + + if ((res = pa_stream_write(impl->pa_stream, + data, size, NULL, 0, PA_SEEK_RELATIVE)) != 0) + pw_log_warn("error writing stream: %zd %s", size, + pa_strerror(res)); + + index += size; + length -= size; + avail -= size; spa_ringbuffer_read_update(&impl->ring, index); } } static void stream_underflow_cb(pa_stream *s, void *userdata) { struct impl *impl = userdata; - pw_log_warn("underflow"); + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + if (ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts), SPA_LOG_LEVEL_WARN)) + pw_log_warn("underflow"); impl->resync = true; } static void stream_overflow_cb(pa_stream *s, void *userdata) { struct impl *impl = userdata; - pw_log_warn("overflow"); + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + if (ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts), SPA_LOG_LEVEL_WARN)) + pw_log_warn("overflow"); impl->resync = true; } @@ -606,13 +620,14 @@ static int create_pulse_stream(struct impl *impl) { pa_sample_spec ss; + pa_channel_map map; const char *server_address, *remote_node_target; pa_proplist *props = NULL; pa_mainloop_api *api; char stream_name1024; pa_buffer_attr bufferattr; int res = -EIO; - uint32_t latency_bytes; + uint32_t latency_bytes, i, aux = 0; if ((impl->pa_mainloop = pa_threaded_mainloop_new()) == NULL) goto error; @@ -659,10 +674,14 @@ ss.channels = impl->info.channels; ss.rate = impl->info.rate; + map.channels = impl->info.channels; + for (i = 0; i < map.channels; i++) + map.mapi = (pa_channel_position_t)channel_id2pa(impl->info.positioni, &aux); + snprintf(stream_name, sizeof(stream_name), _("Tunnel for %s@%s"), pw_get_user_name(), pw_get_host_name()); - if (!(impl->pa_stream = pa_stream_new(impl->pa_context, stream_name, &ss, NULL))) { + if (!(impl->pa_stream = pa_stream_new(impl->pa_context, stream_name, &ss, &map))) { res = pa_context_errno(impl->pa_context); goto error_unlock; } @@ -701,6 +720,8 @@ PA_STREAM_AUTO_TIMING_UPDATE); } else { bufferattr.tlength = latency_bytes / 2; + bufferattr.minreq = bufferattr.tlength / 4; + bufferattr.prebuf = bufferattr.tlength; res = pa_stream_connect_playback(impl->pa_stream, remote_node_target, &bufferattr, @@ -951,6 +972,8 @@ spa_ringbuffer_init(&impl->ring); impl->buffer = calloc(1, RINGBUFFER_SIZE); spa_dll_init(&impl->dll); + impl->rate_limit.interval = 2 * SPA_NSEC_PER_SEC; + impl->rate_limit.burst = 1; if ((str = pw_properties_get(props, "tunnel.mode")) != NULL) { if (spa_streq(str, "source")) {
View file
pipewire-0.3.59.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.60.tar.gz/src/modules/module-raop-sink.c
Changed
@@ -108,12 +108,12 @@ * raop.hostname = "my-raop-device" * raop.port = 8190 * #raop.transport = "udp" - * raop.encryption = "RSA" + * raop.encryption.type = "RSA" * #raop.audio.codec = "PCM" * #raop.password = "****" * #audio.format = "S16" * #audio.rate = 44100 - * #audio.channels = 22 + * #audio.channels = 2 * #audio.position = FL FR * stream.props = { * # extra sink properties @@ -157,7 +157,7 @@ #define DEFAULT_CHANNELS 2 #define DEFAULT_POSITION " FL FR " -#define DEFAULT_LATENCY (DEFAULT_RATE*2) +#define DEFAULT_LATENCY 22050 #define MODULE_USAGE " raop.hostname=<name of host> " \ " raop.port=<remote port> " \ @@ -429,7 +429,7 @@ memset(dst, 0, len); break; } - if (impl->encryption != CRYPTO_NONE) + if (impl->encryption == CRYPTO_RSA) aes_encrypt(impl, dst, len); impl->rtptime += n_frames; @@ -472,7 +472,7 @@ memset(dst, 0, len); break; } - if (impl->encryption != CRYPTO_NONE) + if (impl->encryption == CRYPTO_RSA) aes_encrypt(impl, dst, len); pkt0 |= htonl((uint32_t) len + 12); @@ -724,9 +724,10 @@ } } -static void rtsp_flush_reply(void *data, int status, const struct spa_dict *headers) +static int rtsp_flush_reply(void *data, int status, const struct spa_dict *headers) { pw_log_info("reply %d", status); + return 0; } static int rtsp_do_flush(struct impl *impl) @@ -751,7 +752,7 @@ return res; } -static void rtsp_record_reply(void *data, int status, const struct spa_dict *headers) +static int rtsp_record_reply(void *data, int status, const struct spa_dict *headers) { struct impl *impl = data; const char *str; @@ -760,6 +761,7 @@ uint8_t buffer1024; struct spa_pod_builder b; struct spa_latency_info latency; + char progress128; pw_log_info("reply %d", status); @@ -784,6 +786,10 @@ impl->sync = 0; impl->sync_period = impl->info.rate / (impl->block_size / impl->frame_size); impl->recording = true; + + snprintf(progress, sizeof(progress), "progress: %s/%s/%s\r\n", "0", "0", "0"); + return pw_rtsp_client_send(impl->rtsp, "SET_PARAMETER", NULL, + "text/parameters", progress, NULL, NULL); } static int rtsp_do_record(struct impl *impl) @@ -837,7 +843,7 @@ pw_loop_update_io(impl->loop, impl->server_source, 0); } -static void rtsp_setup_reply(void *data, int status, const struct spa_dict *headers) +static int rtsp_setup_reply(void *data, int status, const struct spa_dict *headers) { struct impl *impl = data; const char *str, *state = NULL, *s; @@ -849,13 +855,13 @@ if ((str = spa_dict_lookup(headers, "Session")) == NULL) { pw_log_error("missing Session header"); - return; + return 0; } pw_properties_set(impl->headers, "Session", str); if ((str = spa_dict_lookup(headers, "Transport")) == NULL) { pw_log_error("missing Transport header"); - return; + return 0; } impl->server_port = control_port = timing_port = 0; @@ -872,18 +878,21 @@ } if (impl->server_port == 0) { pw_log_error("missing server port in Transport"); - return; + return 0; } - pw_getrandom(&impl->seq, sizeof(impl->seq), 0); - pw_getrandom(&impl->rtptime, sizeof(impl->rtptime), 0); + if (pw_getrandom(&impl->seq, sizeof(impl->seq), 0) < 0 || + pw_getrandom(&impl->rtptime, sizeof(impl->rtptime), 0) < 0) { + pw_log_error("error generating random seq and rtptime: %m"); + return 0; + } pw_log_info("server port:%u", impl->server_port); switch (impl->protocol) { case PROTO_TCP: - if ((impl->server_fd = connect_socket(impl, SOCK_STREAM, -1, impl->server_port)) <= 0) - return; + if ((impl->server_fd = connect_socket(impl, SOCK_STREAM, -1, impl->server_port)) < 0) + return impl->server_fd; impl->server_source = pw_loop_add_io(impl->loop, impl->server_fd, SPA_IO_OUT, false, on_server_source_io, impl); @@ -892,16 +901,16 @@ case PROTO_UDP: if (control_port == 0 || timing_port == 0) { pw_log_error("missing UDP ports in Transport"); - return; + return 0; } pw_log_info("control:%u timing:%u", control_port, timing_port); - if ((impl->server_fd = connect_socket(impl, SOCK_DGRAM, -1, impl->server_port)) <= 0) - return; - if ((impl->control_fd = connect_socket(impl, SOCK_DGRAM, impl->control_fd, control_port)) <= 0) - return; - if ((impl->timing_fd = connect_socket(impl, SOCK_DGRAM, impl->timing_fd, timing_port)) <= 0) - return; + if ((impl->server_fd = connect_socket(impl, SOCK_DGRAM, -1, impl->server_port)) < 0) + return impl->server_fd; + if ((impl->control_fd = connect_socket(impl, SOCK_DGRAM, impl->control_fd, control_port)) < 0) + return impl->control_fd; + if ((impl->timing_fd = connect_socket(impl, SOCK_DGRAM, impl->timing_fd, timing_port)) < 0) + return impl->timing_fd; ntp = ntp_now(CLOCK_MONOTONIC); send_udp_timing_packet(impl, ntp, ntp, NULL, 0); @@ -914,8 +923,9 @@ rtsp_do_record(impl); break; default: - return; + return 0; } + return 0; } static int rtsp_do_setup(struct impl *impl) @@ -966,7 +976,7 @@ return -EIO; } -static void rtsp_announce_reply(void *data, int status, const struct spa_dict *headers) +static int rtsp_announce_reply(void *data, int status, const struct spa_dict *headers) { struct impl *impl = data; @@ -974,7 +984,7 @@ pw_properties_set(impl->headers, "Apple-Challenge", NULL); - rtsp_do_setup(impl); + return rtsp_do_setup(impl); } static void base64_encode(const uint8_t *data, size_t len, char *enc, char pad) @@ -1061,7 +1071,8 @@ int res, frames, i, ip_version; char *sdp; char local_ip256; - + int min_latency; + min_latency = DEFAULT_LATENCY; host = pw_properties_get(impl->props, "raop.hostname"); if (impl->protocol == PROTO_TCP) @@ -1088,10 +1099,26 @@ ip_version, host, frames); break; + case CRYPTO_AUTH_SETUP: + asprintf(&sdp, "v=0\r\n" + "o=iTunes %s 0 IN IP%d %s\r\n" + "s=iTunes\r\n" + "c=IN IP%d %s\r\n" + "t=0 0\r\n" + "m=audio 0 RTP/AVP 96\r\n" + "a=rtpmap:96 AppleLossless\r\n" + "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n" + "a=min-latency:%d", + impl->session_id, ip_version, local_ip, + ip_version, host, frames, min_latency); + break; + case CRYPTO_RSA: - pw_getrandom(impl->key, sizeof(impl->key), 0); + if (pw_getrandom(impl->key, sizeof(impl->key), 0) < 0 || + pw_getrandom(impl->iv, sizeof(impl->iv), 0) < 0) + return -errno; + AES_set_encrypt_key(impl->key, 128, &impl->aes); - pw_getrandom(impl->iv, sizeof(impl->iv), 0); i = rsa_encrypt(impl->key, 16, rsakey); base64_encode(rsakey, i, key, '='); @@ -1120,15 +1147,13 @@ return res; } -static void rtsp_auth_setup_reply(void *data, int status, const struct spa_dict *headers) +static int rtsp_auth_setup_reply(void *data, int status, const struct spa_dict *headers) { struct impl *impl = data; pw_log_info("reply %d", status); - impl->encryption = CRYPTO_NONE; - - rtsp_do_announce(impl); + return rtsp_do_announce(impl); } static int rtsp_do_auth_setup(struct impl *impl) @@ -1161,16 +1186,22 @@ return NULL; } -static void rtsp_auth_reply(void *data, int status, const struct spa_dict *headers) +static int rtsp_auth_reply(void *data, int status, const struct spa_dict *headers) { struct impl *impl = data; + int res = 0; + pw_log_info("auth %d", status); switch (status) { case 200: - rtsp_do_announce(impl); + if (impl->encryption == CRYPTO_AUTH_SETUP) + res = rtsp_do_auth_setup(impl); + else + res = rtsp_do_announce(impl); break; } + return res; } SPA_PRINTF_FUNC(2,3) @@ -1219,7 +1250,7 @@ spa_scnprintf(auth, sizeof(auth), "Basic %s", enc); } else if (spa_streq(tokens0, "Digest")) { - const char *realm, *nonce; + const char *realm, *nonce, *url; char h1MD5_HASH_LENGTH+1; char h2MD5_HASH_LENGTH+1; char respMD5_HASH_LENGTH+1; @@ -1229,13 +1260,15 @@ if (realm == NULL || nonce == NULL) goto error; + url = pw_rtsp_client_get_url(impl->rtsp); + MD5_hash(h1, "%s:%s:%s", DEFAULT_USER_NAME, realm, impl->password); - MD5_hash(h2, "OPTIONS:*"); + MD5_hash(h2, "OPTIONS:%s", url); MD5_hash(resp, "%s:%s:%s", h1, nonce, h2); spa_scnprintf(auth, sizeof(auth), - "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"*\", response=\"%s\"", - DEFAULT_USER_NAME, realm, nonce, resp); + "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"", + DEFAULT_USER_NAME, realm, nonce, url, resp); } else goto error; @@ -1253,22 +1286,25 @@ return -EINVAL; } -static void rtsp_options_reply(void *data, int status, const struct spa_dict *headers) +static int rtsp_options_reply(void *data, int status, const struct spa_dict *headers) { struct impl *impl = data; + int res = 0; + pw_log_info("options %d", status); switch (status) { case 401: - rtsp_do_auth(impl, headers); + res = rtsp_do_auth(impl, headers); break; case 200: if (impl->encryption == CRYPTO_AUTH_SETUP) - rtsp_do_auth_setup(impl); + res = rtsp_do_auth_setup(impl); else - rtsp_do_announce(impl); + res = rtsp_do_announce(impl); break; } + return res; } static void rtsp_connected(void *data) @@ -1282,11 +1318,15 @@ impl->connected = true; - pw_getrandom(sci, sizeof(sci), 0); + if (pw_getrandom(sci, sizeof(sci), 0) < 0 || + pw_getrandom(rac, sizeof(rac), 0) < 0) { + pw_log_error("error generating random data: %m"); + return; + } + pw_properties_setf(impl->headers, "Client-Instance", "%08x%08x", sci0, sci1); - pw_getrandom(rac, sizeof(rac), 0); base64_encode(rac, sizeof(rac), sac, '\0'); pw_properties_set(impl->headers, "Apple-Challenge", sac); @@ -1299,29 +1339,29 @@ static void connection_cleanup(struct impl *impl) { impl->ready = false; - if (impl->server_fd != -1) { + if (impl->server_source != NULL) { + pw_loop_destroy_source(impl->loop, impl->server_source); + impl->server_source = NULL; + } + if (impl->server_fd >= 0) { close(impl->server_fd); impl->server_fd = -1; } - if (impl->control_fd != -1) { + if (impl->control_source != NULL) { + pw_loop_destroy_source(impl->loop, impl->control_source); + impl->control_source = NULL; + } + if (impl->control_fd >= 0) { close(impl->control_fd); impl->control_fd = -1; } - if (impl->timing_fd != -1) { - close(impl->timing_fd); - impl->timing_fd = -1; - } - if (impl->server_source != NULL) { - pw_loop_destroy_source(impl->loop, impl->server_source); - impl->server_source = NULL; - } if (impl->timing_source != NULL) { pw_loop_destroy_source(impl->loop, impl->timing_source); impl->timing_source = NULL; } - if (impl->control_source != NULL) { - pw_loop_destroy_source(impl->loop, impl->control_source); - impl->control_source = NULL; + if (impl->timing_fd >= 0) { + close(impl->timing_fd); + impl->timing_fd = -1; } } @@ -1392,13 +1432,15 @@ if (hostname == NULL || port == NULL) return -EINVAL; - pw_getrandom(&session_id, sizeof(session_id), 0); + if (pw_getrandom(&session_id, sizeof(session_id), 0) < 0) + return -errno; + spa_scnprintf(impl->session_id, sizeof(impl->session_id), "%u", session_id); return pw_rtsp_client_connect(impl->rtsp, hostname, atoi(port), impl->session_id); } -static void rtsp_teardown_reply(void *data, int status, const struct spa_dict *headers) +static int rtsp_teardown_reply(void *data, int status, const struct spa_dict *headers) { struct impl *impl = data; const char *str; @@ -1411,6 +1453,7 @@ if (spa_streq(str, "close")) pw_rtsp_client_disconnect(impl->rtsp); } + return 0; } static int rtsp_do_teardown(struct impl *impl)
View file
pipewire-0.3.59.tar.gz/src/modules/module-raop/rtsp-client.c -> pipewire-0.3.60.tar.gz/src/modules/module-raop/rtsp-client.c
Changed
@@ -45,7 +45,7 @@ size_t len; size_t offset; uint32_t cseq; - void (*reply) (void *user_data, int status, const struct spa_dict *headers); + int (*reply) (void *user_data, int status, const struct spa_dict *headers); void *user_data; }; @@ -133,6 +133,11 @@ return client->user_data; } +const char *pw_rtsp_client_get_url(struct pw_rtsp_client *client) +{ + return client->url; +} + void pw_rtsp_client_add_listener(struct pw_rtsp_client *client, struct spa_hook *listener, const struct pw_rtsp_client_events *events, void *data) @@ -282,16 +287,23 @@ static void dispatch_handler(struct pw_rtsp_client *client) { uint32_t cseq; + int res; + struct message *msg; + if (pw_properties_fetch_uint32(client->headers, "CSeq", &cseq) < 0) return; pw_log_info("received reply to request with cseq:%" PRIu32, cseq); - struct message *msg = find_pending(client, cseq); + msg = find_pending(client, cseq); if (msg) { - msg->reply(msg->user_data, client->status, &client->headers->dict); + res = msg->reply(msg->user_data, client->status, &client->headers->dict); spa_list_remove(&msg->link); free(msg); + + if (res < 0) + pw_log_warn("client %p: handle reply cseq:%u error: %s", + client, cseq, spa_strerror(res)); } else { pw_rtsp_client_emit_message(client, client->status, &client->headers->dict); @@ -554,7 +566,7 @@ int pw_rtsp_client_url_send(struct pw_rtsp_client *client, const char *url, const char *cmd, const struct spa_dict *headers, const char *content_type, const void *content, size_t content_length, - void (*reply) (void *user_data, int status, const struct spa_dict *headers), + int (*reply) (void *user_data, int status, const struct spa_dict *headers), void *user_data) { FILE *f; @@ -608,7 +620,7 @@ int pw_rtsp_client_send(struct pw_rtsp_client *client, const char *cmd, const struct spa_dict *headers, const char *content_type, const char *content, - void (*reply) (void *user_data, int status, const struct spa_dict *headers), + int (*reply) (void *user_data, int status, const struct spa_dict *headers), void *user_data) { const size_t content_length = content ? strlen(content) : 0;
View file
pipewire-0.3.59.tar.gz/src/modules/module-raop/rtsp-client.h -> pipewire-0.3.60.tar.gz/src/modules/module-raop/rtsp-client.h
Changed
@@ -57,6 +57,7 @@ void pw_rtsp_client_destroy(struct pw_rtsp_client *client); void *pw_rtsp_client_get_user_data(struct pw_rtsp_client *client); +const char *pw_rtsp_client_get_url(struct pw_rtsp_client *client); void pw_rtsp_client_add_listener(struct pw_rtsp_client *client, struct spa_hook *listener, @@ -74,13 +75,13 @@ int pw_rtsp_client_url_send(struct pw_rtsp_client *client, const char *url, const char *cmd, const struct spa_dict *headers, const char *content_type, const void *content, size_t content_length, - void (*reply) (void *user_data, int status, const struct spa_dict *headers), + int (*reply) (void *user_data, int status, const struct spa_dict *headers), void *user_data); int pw_rtsp_client_send(struct pw_rtsp_client *client, const char *cmd, const struct spa_dict *headers, const char *content_type, const char *content, - void (*reply) (void *user_data, int status, const struct spa_dict *headers), + int (*reply) (void *user_data, int status, const struct spa_dict *headers), void *user_data);
View file
pipewire-0.3.60.tar.gz/src/modules/module-rtp
Added
+(directory)
View file
pipewire-0.3.60.tar.gz/src/modules/module-rtp-sink.c
Added
@@ -0,0 +1,944 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> + * + * 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 <limits.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <netinet/ip.h> +#include <net/if.h> +#include <ctype.h> + +#include <spa/param/audio/format-utils.h> +#include <spa/utils/hook.h> +#include <spa/utils/ringbuffer.h> +#include <spa/utils/json.h> +#include <spa/debug/pod.h> + +#include <pipewire/pipewire.h> +#include <pipewire/private.h> + +#include <module-rtp/sap.h> +#include <module-rtp/rtp.h> + + +/** \page page_module_rtp_sink PipeWire Module: RTP sink + * + * The `rtp-sink` module creates a PipeWire sink that sends audio + * RTP packets. + * + * ## Module Options + * + * Options specific to the behavior of this module + * + * - `sap.ip = <str>`: IP address of the SAP messages, default "224.0.0.56" + * - `sap.port = <int>`: port of the SAP messages, default 9875 + * - `source.ip =<str>`: source IP address, default "0.0.0.0" + * - `destination.ip =<str>`: destination IP address, default "224.0.0.56" + * - `destination.port =<int>`: destination port, default random beteen 46000 and 47024 + * - `local.ifname = <str>`: interface name to use + * - `net.mtu = <int>`: MTU to use, default 1280 + * - `net.ttl = <int>`: TTL to use, default 1 + * - `net.loop = <bool>`: loopback multicast, default false + * - `sess.name = <str>`: a session name + * - `stream.props = {}`: properties to be passed to the stream + * + * ## General options + * + * Options with well-known behavior: + * + * - \ref PW_KEY_REMOTE_NAME + * - \ref PW_KEY_AUDIO_FORMAT + * - \ref PW_KEY_AUDIO_RATE + * - \ref PW_KEY_AUDIO_CHANNELS + * - \ref SPA_KEY_AUDIO_POSITION + * - \ref PW_KEY_NODE_NAME + * - \ref PW_KEY_NODE_DESCRIPTION + * - \ref PW_KEY_MEDIA_NAME + * - \ref PW_KEY_NODE_GROUP + * - \ref PW_KEY_NODE_LATENCY + * - \ref PW_KEY_NODE_VIRTUAL + * - \ref PW_KEY_MEDIA_CLASS + * + * ## Example configuration + *\code{.unparsed} + * context.modules = + * { name = libpipewire-module-rtp-sink + * args = { + * #sap.ip = "224.0.0.56" + * #sap.port = 9875 + * #source.ip = "0.0.0.0" + * #destination.ip = "224.0.0.56" + * #destination.port = 46000 + * #local.ifname = "eth0" + * #net.mtu = 1280 + * #net.ttl = 1 + * #net.loop = false + * #sess.name = "PipeWire RTP stream" + * #audio.format = "S16BE" + * #audio.rate = 48000 + * #audio.channels = 2 + * #audio.position = FL FR + * stream.props = { + * node.name = "rtp-sink" + * } + * } + *} + * + *\endcode + * + * \since 0.3.60 + */ + +#define NAME "rtp-sink" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +#define SAP_INTERVAL_SEC 5 +#define SAP_MIME_TYPE "application/sdp" + +#define BUFFER_SIZE (1u<<20) +#define BUFFER_MASK (BUFFER_SIZE-1) + +#define DEFAULT_SAP_IP "224.0.0.56" +#define DEFAULT_SAP_PORT 9875 + +#define DEFAULT_FORMAT "S16BE" +#define DEFAULT_RATE 48000 +#define DEFAULT_CHANNELS 2 +#define DEFAULT_POSITION " FL FR " + +#define DEFAULT_PORT 46000 +#define DEFAULT_SOURCE_IP "0.0.0.0" +#define DEFAULT_DESTINATION_IP "224.0.0.56" +#define DEFAULT_TTL 1 +#define DEFAULT_MTU 1280 +#define DEFAULT_LOOP false + +#define DEFAULT_MIN_PTIME 2 +#define DEFAULT_MAX_PTIME 20 + +#define USAGE "sap.ip=<SAP IP address to send announce, default:"DEFAULT_SAP_IP"> " \ + "sap.port=<SAP port to send on, default:"SPA_STRINGIFY(DEFAULT_SAP_PORT)"> " \ + "source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> " \ + "destination.ip=<destination IP address, default:"DEFAULT_DESTINATION_IP"> " \ + "local.ifname=<local interface name to use> " \ + "net.mtu=<desired MTU, default:"SPA_STRINGIFY(DEFAULT_MTU)"> " \ + "net.ttl=<desired TTL, default:"SPA_STRINGIFY(DEFAULT_TTL)"> " \ + "net.loop=<desired loopback, default:"SPA_STRINGIFY(DEFAULT_LOOP)"> " \ + "sess.name=<a name for the session> " \ + "audio.format=<format, default:"DEFAULT_FORMAT"> " \ + "audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> " \ + "audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> "\ + "audio.position=<channel map, default:"DEFAULT_POSITION"> " \ + "stream.props= { key=value ... }" + +static const struct spa_dict_item module_info = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, + { PW_KEY_MODULE_DESCRIPTION, "RTP Sink" }, + { PW_KEY_MODULE_USAGE, USAGE }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + +static const struct format_info { + uint32_t format; + uint32_t size; + const char *mime; +} format_info = { + { SPA_AUDIO_FORMAT_U8, 1, "L8" }, + { SPA_AUDIO_FORMAT_ALAW, 1, "PCMA" }, + { SPA_AUDIO_FORMAT_ULAW, 1, "PCMU" }, + { SPA_AUDIO_FORMAT_S16_BE, 2, "L16" }, + { SPA_AUDIO_FORMAT_S24_BE, 3, "L24" }, +}; + +static const struct format_info *find_format_info(uint32_t format) +{ + SPA_FOR_EACH_ELEMENT_VAR(format_info, f) + if (f->format == format) + return f; + return NULL; +} + +struct impl { + struct pw_impl_module *module; + struct spa_hook module_listener; + struct pw_properties *props; + struct pw_context *module_context; + + struct pw_loop *loop; + + struct pw_core *core; + struct spa_hook core_listener; + struct spa_hook core_proxy_listener; + + struct spa_source *timer; + + struct pw_properties *stream_props; + struct pw_stream *stream; + struct spa_hook stream_listener; + + unsigned int do_disconnect:1; + + char *ifname; + char *session_name; + int sess_latency_msec; + int mtu; + bool ttl; + bool mcast_loop; + uint32_t min_ptime; + uint32_t max_ptime; + + struct sockaddr_storage src_addr; + socklen_t src_len; + + uint16_t port; + struct sockaddr_storage dst_addr; + socklen_t dst_len; + + uint16_t sap_port; + struct sockaddr_storage sap_addr; + socklen_t sap_len; + + uint16_t msg_id_hash; + uint32_t ntp; + + struct spa_audio_info_raw info; + const struct format_info *format_info; + uint32_t frame_size; + int payload; + uint16_t seq; + uint32_t timestamp; + uint32_t ssrc; + + struct spa_ringbuffer ring; + uint8_t bufferBUFFER_SIZE; + + int rtp_fd; + int sap_fd; +}; + + +static void stream_destroy(void *d) +{ + struct impl *impl = d; + spa_hook_remove(&impl->stream_listener); + impl->stream = NULL; +} + +static inline void +set_iovec(struct spa_ringbuffer *rbuf, void *buffer, uint32_t size, + uint32_t offset, struct iovec *iov, uint32_t len) +{ + iov0.iov_len = SPA_MIN(len, size - offset); + iov0.iov_base = SPA_PTROFF(buffer, offset, void); + iov1.iov_len = len - iov0.iov_len; + iov1.iov_base = buffer; +} + +static void flush_packets(struct impl *impl) +{ + int32_t avail; + uint32_t index; + struct iovec iov3; + struct msghdr msg; + ssize_t n; + struct rtp_header header; + int32_t tosend; + + avail = spa_ringbuffer_get_read_index(&impl->ring, &index); + + tosend = SPA_ROUND_DOWN(impl->mtu, impl->frame_size); + + if (avail < tosend) + return; + + spa_zero(header); + header.v = 2; + header.pt = impl->payload; + header.ssrc = htonl(impl->ssrc); + + iov0.iov_base = &header; + iov0.iov_len = sizeof(header); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 3; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + while (avail >= tosend) { + header.sequence_number = htons(impl->seq); + header.timestamp = htonl(impl->timestamp); + + set_iovec(&impl->ring, + impl->buffer, BUFFER_SIZE, + index & BUFFER_MASK, + &iov1, tosend); + + n = sendmsg(impl->rtp_fd, &msg, MSG_NOSIGNAL); + if (n < 0) + pw_log_warn("sendmsg() failed: %m"); + + impl->seq++; + impl->timestamp += tosend / impl->frame_size; + + index += tosend; + avail -= tosend; + } + spa_ringbuffer_read_update(&impl->ring, index); +} + +static void stream_process(void *data) +{ + struct impl *impl = data; + struct pw_buffer *buf; + struct spa_data *d; + uint32_t index; + int32_t filled, wanted; + + if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) { + pw_log_debug("Out of stream buffers: %m"); + return; + } + d = buf->buffer->datas; + + wanted = d0.chunk->size; + + filled = spa_ringbuffer_get_write_index(&impl->ring, &index); + + if (filled + wanted > (int32_t)BUFFER_SIZE) { + pw_log_warn("overrun %u + %u > %u", filled, wanted, BUFFER_SIZE); + } else { + spa_ringbuffer_write_data(&impl->ring, + impl->buffer, + BUFFER_SIZE, + index & BUFFER_MASK, + d0.data, wanted); + + index += wanted; + spa_ringbuffer_write_update(&impl->ring, index); + } + pw_stream_queue_buffer(impl->stream, buf); + + flush_packets(impl); +} + +static void on_stream_state_changed(void *d, enum pw_stream_state old, + enum pw_stream_state state, const char *error) +{ + struct impl *impl = d; + + switch (state) { + case PW_STREAM_STATE_UNCONNECTED: + pw_log_info("stream disconnected, unloading"); + pw_impl_module_schedule_destroy(impl->module); + break; + case PW_STREAM_STATE_ERROR: + pw_log_error("stream error: %s", error); + break; + default: + break; + } +} + +static const struct pw_stream_events in_stream_events = { + PW_VERSION_STREAM_EVENTS, + .destroy = stream_destroy, + .state_changed = on_stream_state_changed, + .process = stream_process +}; + +static int parse_address(const char *address, uint16_t port, + struct sockaddr_storage *addr, socklen_t *len) +{ + struct sockaddr_in *sa4 = (struct sockaddr_in*)addr; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)addr; + + if (inet_pton(AF_INET, address, &sa4->sin_addr) > 0) { + sa4->sin_family = AF_INET; + sa4->sin_port = htons(port); + *len = sizeof(*sa4); + } else if (inet_pton(AF_INET6, address, &sa6->sin6_addr) > 0) { + sa6->sin6_family = AF_INET6; + sa6->sin6_port = htons(port); + *len = sizeof(*sa6); + } else + return -EINVAL; + + return 0; +} + +static bool is_multicast(struct sockaddr *sa, socklen_t salen) +{ + if (sa->sa_family == AF_INET) { + static const uint32_t ipv4_mcast_mask = 0xe0000000; + struct sockaddr_in *sa4 = (struct sockaddr_in*)sa; + return (ntohl(sa4->sin_addr.s_addr) & ipv4_mcast_mask) == ipv4_mcast_mask; + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa; + return sa6->sin6_addr.s6_addr0 == 0xff; + } + return false; +} + +static int make_socket(struct sockaddr_storage *src, socklen_t src_len, + struct sockaddr_storage *dst, socklen_t dst_len, + bool loop, int ttl) +{ + int af, fd, val, res; + + af = src->ss_family; + if ((fd = socket(af, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) { + pw_log_error("socket failed: %m"); + return -errno; + } + if (bind(fd, (struct sockaddr*)src, src_len) < 0) { + res = -errno; + pw_log_error("bind() failed: %m"); + goto error; + } + if (connect(fd, (struct sockaddr*)dst, dst_len) < 0) { + res = -errno; + pw_log_error("connect() failed: %m"); + goto error; + } + if (is_multicast((struct sockaddr*)dst, dst_len)) { + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val)) < 0) + pw_log_warn("setsockopt(IP_MULTICAST_LOOP) failed: %m"); + + val = ttl; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, sizeof(val)) < 0) + pw_log_warn("setsockopt(IP_MULTICAST_TTL) failed: %m"); + } + + val = 6; + if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val)) < 0) + pw_log_warn("setsockopt(SO_PRIORITY) failed: %m"); + + val = IPTOS_LOWDELAY; + if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)) < 0) + pw_log_warn("setsockopt(SO_PRIORITY) failed: %m"); + + + return fd; +error: + close(fd); + return res; +} + +static int setup_stream(struct impl *impl) +{ + const struct spa_pod *params1; + struct spa_pod_builder b; + uint32_t n_params; + uint8_t buffer1024; + struct pw_properties *props; + int res, fd; + + props = pw_properties_copy(impl->stream_props); + if (props == NULL) + return -errno; + + if (pw_properties_get(props, PW_KEY_NODE_LATENCY) == NULL) { + pw_properties_setf(props, PW_KEY_NODE_LATENCY, + "%d/%d", impl->mtu / impl->frame_size, + impl->info.rate); + } + pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%d", impl->info.rate); + + impl->stream = pw_stream_new(impl->core, + "rtp-sink capture", props); + if (impl->stream == NULL) + return -errno; + + pw_stream_add_listener(impl->stream, + &impl->stream_listener, + &in_stream_events, impl); + + n_params = 0; + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + paramsn_params++ = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, + &impl->info); + + if ((res = pw_stream_connect(impl->stream, + PW_DIRECTION_INPUT, + PW_ID_ANY, + PW_STREAM_FLAG_MAP_BUFFERS | + PW_STREAM_FLAG_AUTOCONNECT | + PW_STREAM_FLAG_RT_PROCESS, + params, n_params)) < 0) + return res; + + + if ((fd = make_socket(&impl->src_addr, impl->src_len, + &impl->dst_addr, impl->dst_len, + impl->mcast_loop, impl->ttl)) < 0) + return fd; + + impl->rtp_fd = fd; + + return 0; +} + +static int get_ip(const struct sockaddr_storage *sa, char *ip, size_t len) +{ + if (sa->ss_family == AF_INET) { + struct sockaddr_in *in = (struct sockaddr_in*)sa; + inet_ntop(sa->ss_family, &in->sin_addr, ip, len); + } else if (sa->ss_family == AF_INET6) { + struct sockaddr_in6 *in = (struct sockaddr_in6*)sa; + inet_ntop(sa->ss_family, &in->sin6_addr, ip, len); + } else + return -EIO; + return 0; +} +static void send_sap(struct impl *impl, bool bye) +{ + char buffer2048, src_addr64, dst_addr64, dst_ttl8; + const char *user_name, *af; + struct sockaddr *sa = (struct sockaddr*)&impl->src_addr; + struct sap_header header; + struct iovec iov4; + struct msghdr msg; + + spa_zero(header); + header.v = 1; + header.t = bye; + header.msg_id_hash = impl->msg_id_hash; + + iov0.iov_base = &header; + iov0.iov_len = sizeof(header); + + if (sa->sa_family == AF_INET) { + iov1.iov_base = &((struct sockaddr_in*) sa)->sin_addr; + iov1.iov_len = 4U; + af = "IP4"; + } else { + iov1.iov_base = &((struct sockaddr_in6*) sa)->sin6_addr; + iov1.iov_len = 16U; + header.a = 1; + af = "IP6"; + } + iov2.iov_base = SAP_MIME_TYPE; + iov2.iov_len = sizeof(SAP_MIME_TYPE); + + get_ip(&impl->src_addr, src_addr, sizeof(src_addr)); + get_ip(&impl->dst_addr, dst_addr, sizeof(dst_addr)); + + if ((user_name = pw_get_user_name()) == NULL) + user_name = "-"; + + spa_zero(dst_ttl); + if (is_multicast((struct sockaddr*)&impl->dst_addr, impl->dst_len)) + snprintf(dst_ttl, sizeof(dst_ttl), "/%d", impl->ttl); + + snprintf(buffer, sizeof(buffer), + "v=0\n" + "o=%s %u 0 IN %s %s\n" + "s=%s\n" + "c=IN %s %s%s\n" + "t=%u 0\n" + "a=recvonly\n" + "m=audio %u RTP/AVP %i\n" + "a=rtpmap:%i %s/%u/%u\n" + "a=type:broadcast\n", + user_name, impl->ntp, af, src_addr, + impl->session_name, + af, dst_addr, dst_ttl, + impl->ntp, + impl->port, impl->payload, + impl->payload, impl->format_info->mime, + impl->info.rate, impl->info.channels); + + iov3.iov_base = buffer; + iov3.iov_len = strlen(buffer); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 4; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + sendmsg(impl->sap_fd, &msg, MSG_NOSIGNAL); +} + +static void on_timer_event(void *data, uint64_t expirations) +{ + struct impl *impl = data; + send_sap(impl, 0); +} + +static int start_sap_announce(struct impl *impl) +{ + int fd, res; + struct timespec value, interval; + + if ((fd = make_socket(&impl->src_addr, impl->src_len, + &impl->sap_addr, impl->sap_len, + impl->mcast_loop, impl->ttl)) < 0) + return fd; + + impl->sap_fd = fd; + + pw_log_info("starting SAP timer"); + impl->timer = pw_loop_add_timer(impl->loop, on_timer_event, impl); + if (impl->timer == NULL) { + res = -errno; + pw_log_error("can't create timer source: %m"); + goto error; + } + value.tv_sec = 0; + value.tv_nsec = 1; + interval.tv_sec = SAP_INTERVAL_SEC; + interval.tv_nsec = 0; + pw_loop_update_timer(impl->loop, impl->timer, &value, &interval, false); + + return 0; +error: + close(fd); + return res; + +} + +static void core_destroy(void *d) +{ + struct impl *impl = d; + spa_hook_remove(&impl->core_listener); + impl->core = NULL; + pw_impl_module_schedule_destroy(impl->module); +} + +static const struct pw_proxy_events core_proxy_events = { + .destroy = core_destroy, +}; + +static void impl_destroy(struct impl *impl) +{ + send_sap(impl, 1); + + if (impl->stream) + pw_stream_destroy(impl->stream); + + if (impl->core && impl->do_disconnect) + pw_core_disconnect(impl->core); + + if (impl->timer) + pw_loop_destroy_source(impl->loop, impl->timer); + + if (impl->rtp_fd != -1) + close(impl->rtp_fd); + if (impl->sap_fd != -1) + close(impl->sap_fd); + + pw_properties_free(impl->stream_props); + pw_properties_free(impl->props); + + free(impl->ifname); + free(impl->session_name); + free(impl); +} + +static void module_destroy(void *d) +{ + struct impl *impl = d; + spa_hook_remove(&impl->module_listener); + impl_destroy(impl); +} + +static const struct pw_impl_module_events module_events = { + PW_VERSION_IMPL_MODULE_EVENTS, + .destroy = module_destroy, +}; + +static void on_core_error(void *d, uint32_t id, int seq, int res, const char *message) +{ + struct impl *impl = d; + + pw_log_error("error id:%u seq:%d res:%d (%s): %s", + id, seq, res, spa_strerror(res), message); + + if (id == PW_ID_CORE && res == -EPIPE) + pw_impl_module_schedule_destroy(impl->module); +} + +static const struct pw_core_events core_events = { + PW_VERSION_CORE_EVENTS, + .error = on_core_error, +}; + +static inline uint32_t format_from_name(const char *name, size_t len) +{ + int i; + for (i = 0; spa_type_audio_formati.name; i++) { + if (strncmp(name, spa_debug_type_short_name(spa_type_audio_formati.name), len) == 0) + return spa_type_audio_formati.type; + } + return SPA_AUDIO_FORMAT_UNKNOWN; +} + +static uint32_t channel_from_name(const char *name) +{ + int i; + for (i = 0; spa_type_audio_channeli.name; i++) { + if (spa_streq(name, spa_debug_type_short_name(spa_type_audio_channeli.name))) + return spa_type_audio_channeli.type; + } + return SPA_AUDIO_CHANNEL_UNKNOWN; +} + +static void parse_position(struct spa_audio_info_raw *info, const char *val, size_t len) +{ + struct spa_json it2; + char v256; + + spa_json_init(&it0, val, len); + if (spa_json_enter_array(&it0, &it1) <= 0) + spa_json_init(&it1, val, len); + + info->channels = 0; + while (spa_json_get_string(&it1, v, sizeof(v)) > 0 && + info->channels < SPA_AUDIO_MAX_CHANNELS) { + info->positioninfo->channels++ = channel_from_name(v); + } +} + +static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +{ + const char *str; + + spa_zero(*info); + if ((str = pw_properties_get(props, PW_KEY_AUDIO_FORMAT)) == NULL) + str = DEFAULT_FORMAT; + info->format = format_from_name(str, strlen(str)); + + info->rate = pw_properties_get_uint32(props, PW_KEY_AUDIO_RATE, info->rate); + if (info->rate == 0) + info->rate = DEFAULT_RATE; + + info->channels = pw_properties_get_uint32(props, PW_KEY_AUDIO_CHANNELS, info->channels); + info->channels = SPA_MIN(info->channels, SPA_AUDIO_MAX_CHANNELS); + if ((str = pw_properties_get(props, SPA_KEY_AUDIO_POSITION)) != NULL) + parse_position(info, str, strlen(str)); + if (info->channels == 0) + parse_position(info, DEFAULT_POSITION, strlen(DEFAULT_POSITION)); +} + +static void copy_props(struct impl *impl, struct pw_properties *props, const char *key) +{ + const char *str; + if ((str = pw_properties_get(props, key)) != NULL) { + if (pw_properties_get(impl->stream_props, key) == NULL) + pw_properties_set(impl->stream_props, key, str); + } +} + +SPA_EXPORT +int pipewire__module_init(struct pw_impl_module *module, const char *args) +{ + struct pw_context *context = pw_impl_module_get_context(module); + struct impl *impl; + struct pw_properties *props = NULL, *stream_props = NULL; + uint32_t id = pw_global_get_id(pw_impl_module_get_global(module)); + uint32_t pid = getpid(), port; + char addr64; + const char *str; + int res = 0; + + PW_LOG_TOPIC_INIT(mod_topic); + + impl = calloc(1, sizeof(struct impl)); + if (impl == NULL) + return -errno; + + impl->rtp_fd = -1; + impl->sap_fd = -1; + + if (args == NULL) + args = ""; + + props = pw_properties_new_string(args); + if (props == NULL) { + res = -errno; + pw_log_error( "can't create properties: %m"); + goto out; + } + impl->props = props; + + stream_props = pw_properties_new(NULL, NULL); + if (stream_props == NULL) { + res = -errno; + pw_log_error( "can't create properties: %m"); + goto out; + } + impl->stream_props = stream_props; + + impl->module = module; + impl->module_context = context; + impl->loop = pw_context_get_main_loop(context); + + if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL) + pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true"); + if (pw_properties_get(stream_props, PW_KEY_NODE_NETWORK) == NULL) + pw_properties_set(stream_props, PW_KEY_NODE_NETWORK, "true"); + + if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL) + pw_properties_setf(props, PW_KEY_NODE_NAME, "rtp-sink-%u-%u", pid, id); + if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL) + pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, + pw_properties_get(props, PW_KEY_NODE_NAME)); + if (pw_properties_get(props, PW_KEY_MEDIA_NAME) == NULL) + pw_properties_set(props, PW_KEY_MEDIA_NAME, "RTP Sender Stream"); + + if ((str = pw_properties_get(props, "stream.props")) != NULL) + pw_properties_update_string(stream_props, str, strlen(str)); + + copy_props(impl, props, PW_KEY_AUDIO_FORMAT); + copy_props(impl, props, PW_KEY_AUDIO_RATE); + copy_props(impl, props, PW_KEY_AUDIO_CHANNELS); + copy_props(impl, props, SPA_KEY_AUDIO_POSITION); + copy_props(impl, props, PW_KEY_NODE_NAME); + copy_props(impl, props, PW_KEY_NODE_DESCRIPTION); + copy_props(impl, props, PW_KEY_NODE_GROUP); + 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, PW_KEY_MEDIA_CLASS); + + parse_audio_info(impl->stream_props, &impl->info); + + impl->format_info = find_format_info(impl->info.format); + if (impl->format_info == NULL) { + pw_log_error("unsupported audio format:%d channels:%d", + impl->info.format, impl->info.channels); + res = -EINVAL; + goto out; + } + impl->frame_size = impl->format_info->size * impl->info.channels; + impl->msg_id_hash = rand(); + impl->ntp = (uint32_t) time(NULL) + 2208988800U; + + impl->payload = 127; + impl->seq = rand(); + impl->timestamp = rand(); + impl->ssrc = rand(); + + str = pw_properties_get(props, "local.ifname"); + impl->ifname = str ? strdup(str) : NULL; + + if ((str = pw_properties_get(props, "sap.ip")) == NULL) + str = DEFAULT_SAP_IP; + port = pw_properties_get_uint32(props, "sap.port", DEFAULT_SAP_PORT); + if ((res = parse_address(str, port, &impl->sap_addr, &impl->sap_len)) < 0) { + pw_log_error("invalid sap.ip %s: %s", str, spa_strerror(res)); + goto out; + } + + if ((str = pw_properties_get(props, "source.ip")) == NULL) + str = DEFAULT_SOURCE_IP; + if ((res = parse_address(str, 0, &impl->src_addr, &impl->src_len)) < 0) { + pw_log_error("invalid source.ip %s: %s", str, spa_strerror(res)); + goto out; + } + + impl->port = DEFAULT_PORT + ((uint32_t) (rand() % 512) << 1); + impl->port = pw_properties_get_uint32(props, "destination.port", impl->port); + if ((str = pw_properties_get(props, "destination.ip")) == NULL) + str = DEFAULT_DESTINATION_IP; + if ((res = parse_address(str, impl->port, &impl->dst_addr, &impl->dst_len)) < 0) { + pw_log_error("invalid destination.ip %s: %s", str, spa_strerror(res)); + goto out; + } + + impl->mtu = pw_properties_get_uint32(props, "net.mtu", DEFAULT_MTU); + impl->ttl = pw_properties_get_uint32(props, "net.ttl", DEFAULT_TTL); + impl->mcast_loop = pw_properties_get_bool(props, "net.loop", DEFAULT_LOOP); + + impl->min_ptime = pw_properties_get_uint32(props, "sess.min-ptime", DEFAULT_MIN_PTIME); + impl->max_ptime = pw_properties_get_uint32(props, "sess.max-ptime", DEFAULT_MAX_PTIME); + + if ((str = pw_properties_get(props, "sess.name")) == NULL) + pw_properties_setf(props, "sess.name", "PipeWire RTP Stream on %s", + pw_get_host_name()); + str = pw_properties_get(props, "sess.name"); + impl->session_name = str ? strdup(str) : NULL; + + pw_properties_set(stream_props, "rtp.session", impl->session_name); + get_ip(&impl->src_addr, addr, sizeof(addr)); + pw_properties_set(stream_props, "rtp.source.ip", addr); + get_ip(&impl->dst_addr, addr, sizeof(addr)); + pw_properties_set(stream_props, "rtp.destination.ip", addr); + pw_properties_setf(stream_props, "rtp.destination.port", "%u", impl->port); + pw_properties_setf(stream_props, "rtp.mtu", "%u", impl->mtu); + pw_properties_setf(stream_props, "rtp.ttl", "%u", impl->ttl); + + impl->core = pw_context_get_object(impl->module_context, PW_TYPE_INTERFACE_Core); + if (impl->core == NULL) { + str = pw_properties_get(props, PW_KEY_REMOTE_NAME); + impl->core = pw_context_connect(impl->module_context, + pw_properties_new( + PW_KEY_REMOTE_NAME, str, + NULL), + 0); + impl->do_disconnect = true; + } + if (impl->core == NULL) { + res = -errno; + pw_log_error("can't connect: %m"); + goto out; + } + + pw_proxy_add_listener((struct pw_proxy*)impl->core, + &impl->core_proxy_listener, + &core_proxy_events, impl); + pw_core_add_listener(impl->core, + &impl->core_listener, + &core_events, impl); + + if ((res = setup_stream(impl)) < 0) + goto out; + + if ((res = start_sap_announce(impl)) < 0) + goto out; + + pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl); + + pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_info)); + + pw_log_info("Successfully loaded module-rtp-sink"); + + return 0; +out: + impl_destroy(impl); + return res; +}
View file
pipewire-0.3.60.tar.gz/src/modules/module-rtp-source.c
Added
@@ -0,0 +1,1101 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> + * + * 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 <limits.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <net/if.h> +#include <ctype.h> + +#include <spa/param/audio/format-utils.h> +#include <spa/utils/hook.h> +#include <spa/utils/ringbuffer.h> +#include <spa/utils/dll.h> +#include <spa/debug/mem.h> + +#include <pipewire/pipewire.h> +#include <pipewire/private.h> + +#include <module-rtp/sap.h> +#include <module-rtp/rtp.h> + + +/** \page page_module_rtp_source PipeWire Module: RTP source + * + * The `rtp-source` module creates a PipeWire source that receives audio + * RTP packets. + * + * ## Module Options + * + * Options specific to the behavior of this module + * + * - `sap.ip = <str>`: IP address of the SAP messages, default "224.0.0.56" + * - `sap.port = <str>`: port of the SAP messages, default 9875 + * - `local.ifname = <str>`: interface name to use + * - `sess.latency.msec = <str>`: target network latency in milliseconds, default 100 + * - `stream.props = {}`: properties to be passed to the stream + * + * ## General options + * + * Options with well-known behavior: + * + * - \ref PW_KEY_NODE_NAME + * - \ref PW_KEY_NODE_DESCRIPTION + * - \ref PW_KEY_MEDIA_NAME + * - \ref PW_KEY_MEDIA_CLASS + * + * ## Example configuration + *\code{.unparsed} + * context.modules = + * { name = libpipewire-module-rtp-source + * args = { + * #sap.ip = 224.0.0.56 + * #sap.port = 9875 + * #local.ifname = eth0 + * sess.latency.msec = 100 + * stream.props = { + * node.name = "rtp-source" + * #media.class = "Audio/Source" + * } + * } + * } + * + *\endcode + * + * \since 0.3.60 + */ + +#define NAME "rtp-source" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +#define SAP_MIME_TYPE "application/sdp" + +#define ERROR_MSEC 2 +#define MAX_SESSIONS 16 +#define CLEANUP_INTERVAL_SEC 20 + +#define DEFAULT_SAP_IP "224.0.0.56" +#define DEFAULT_SAP_PORT 9875 +#define DEFAULT_SESS_LATENCY 100 + +#define BUFFER_SIZE (1u<<22) +#define BUFFER_MASK (BUFFER_SIZE-1) + +#define USAGE "sap.ip=<SAP IP address to listen on, default "DEFAULT_SAP_IP"> " \ + "sap.port=<SAP port to listen on, default "SPA_STRINGIFY(DEFAULT_SAP_PORT)"> " \ + "local.ifname=<local interface name to use> " \ + "sess.latency.msec=<target network latency, default "SPA_STRINGIFY(DEFAULT_SESS_LATENCY)"> " \ + "stream.props= { key=value ... }" + +static const struct spa_dict_item module_info = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, + { PW_KEY_MODULE_DESCRIPTION, "RTP Source" }, + { PW_KEY_MODULE_USAGE, USAGE }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + +struct impl { + struct pw_impl_module *module; + struct spa_hook module_listener; + struct pw_properties *props; + struct pw_context *module_context; + + struct pw_loop *loop; + struct pw_loop *data_loop; + + struct pw_core *core; + struct spa_hook core_listener; + struct spa_hook core_proxy_listener; + + struct spa_source *timer; + struct spa_source *sap_source; + + struct pw_properties *stream_props; + + unsigned int do_disconnect:1; + + char *ifname; + char *sap_ip; + int sap_port; + int sess_latency_msec; + + struct spa_list sessions; + uint32_t n_sessions; +}; + +static const struct format_info { + uint32_t format; + uint32_t size; + const char *mime; +} format_info = { + { SPA_AUDIO_FORMAT_U8, 1, "L8" }, + { SPA_AUDIO_FORMAT_ALAW, 1, "PCMA" }, + { SPA_AUDIO_FORMAT_ULAW, 1, "PCMU" }, + { SPA_AUDIO_FORMAT_S16_BE, 2, "L16" }, + { SPA_AUDIO_FORMAT_S24_BE, 3, "L24" }, +}; + +static const struct format_info *find_format_info(const char *mime) +{ + SPA_FOR_EACH_ELEMENT_VAR(format_info, f) + if (spa_streq(f->mime, mime)) + return f; + return NULL; +} + +struct sdp_info { + uint16_t hash; + + char origin128; + char session256; + + struct sockaddr_storage sa; + socklen_t salen; + + uint16_t port; + uint8_t payload; + + const struct format_info *format_info; + struct spa_audio_info_raw info; + uint32_t stride; +}; + +struct session { + struct impl *impl; + struct spa_list link; + + uint64_t timestamp; + + struct sdp_info info; + + struct spa_source *source; + + struct pw_stream *stream; + struct spa_hook stream_listener; + + uint32_t expected_ssrc; + uint16_t expected_seq; + unsigned have_ssrc:1; + unsigned have_seq:1; + unsigned have_sync:1; + + struct spa_ringbuffer ring; + uint8_t bufferBUFFER_SIZE; + + struct spa_dll dll; + uint32_t target_buffer; + float max_error; + unsigned buffering:1; +}; + +static void stream_destroy(void *d) +{ + struct session *sess = d; + spa_hook_remove(&sess->stream_listener); + sess->stream = NULL; +} + +static void stream_process(void *data) +{ + struct session *sess = data; + struct pw_buffer *buf; + struct spa_data *d; + uint32_t index; + int32_t avail, wanted; + + if ((buf = pw_stream_dequeue_buffer(sess->stream)) == NULL) { + pw_log_debug("Out of stream buffers: %m"); + return; + } + d = buf->buffer->datas; + + wanted = buf->requested ? + SPA_MIN(buf->requested * sess->info.stride, d0.maxsize) + : d0.maxsize; + + avail = spa_ringbuffer_get_read_index(&sess->ring, &index); + + if (avail < wanted || sess->buffering) { + memset(d0.data, 0, wanted); + if (!sess->buffering && sess->have_sync) { + pw_log_info("underrun %u/%u < %u, buffering...", + avail, sess->target_buffer, wanted); + sess->buffering = true; + } + } else { + float error, corr; + if (avail > (int32_t)BUFFER_SIZE) { + index += avail - sess->target_buffer; + avail = sess->target_buffer; + pw_log_warn("overrun %u > %u", avail, BUFFER_SIZE); + } else { + error = (float)sess->target_buffer - (float)avail; + error = SPA_CLAMP(error, -sess->max_error, sess->max_error); + + corr = spa_dll_update(&sess->dll, error); + + pw_log_debug("avail:%u target:%u error:%f corr:%f", avail, + sess->target_buffer, error, corr); + + pw_stream_set_control(sess->stream, + SPA_PROP_rate, 1, &corr, NULL); + } + spa_ringbuffer_read_data(&sess->ring, + sess->buffer, + BUFFER_SIZE, + index & BUFFER_MASK, + d0.data, wanted); + + index += wanted; + spa_ringbuffer_read_update(&sess->ring, index); + } + d0.chunk->size = wanted; + d0.chunk->stride = sess->info.stride; + d0.chunk->offset = 0; + buf->size = wanted / sess->info.stride; + + pw_stream_queue_buffer(sess->stream, buf); +} + +static void on_stream_state_changed(void *d, enum pw_stream_state old, + enum pw_stream_state state, const char *error) +{ + struct session *sess = d; + struct impl *impl = sess->impl; + + switch (state) { + case PW_STREAM_STATE_UNCONNECTED: + pw_log_info("stream disconnected, unloading"); + pw_impl_module_schedule_destroy(impl->module); + break; + case PW_STREAM_STATE_ERROR: + pw_log_error("stream error: %s", error); + break; + default: + break; + } +} + +static const struct pw_stream_events out_stream_events = { + PW_VERSION_STREAM_EVENTS, + .destroy = stream_destroy, + .state_changed = on_stream_state_changed, + .process = stream_process +}; + +static void +on_rtp_io(void *data, int fd, uint32_t mask) +{ + struct session *sess = data; + struct rtp_header *hdr; + ssize_t len, hlen; + uint8_t buffer2048, *payload; + + if (mask & SPA_IO_IN) { + uint32_t index, expected_index, timestamp; + uint16_t seq; + int32_t filled; + + pw_log_trace("got rtp"); + if ((len = recv(fd, buffer, sizeof(buffer), 0)) < 0) + goto receive_error; + + if (len < 12) + goto short_packet; + + hdr = (struct rtp_header*)buffer; + if (hdr->v != 2) + goto invalid_version; + + hlen = 12 + hdr->cc * 4; + if (hlen > len) + goto invalid_len; + + if (sess->have_ssrc && sess->expected_ssrc != hdr->ssrc) + goto unexpected_ssrc; + sess->expected_ssrc = hdr->ssrc; + sess->have_ssrc = true; + + seq = ntohs(hdr->sequence_number); + if (sess->have_seq && sess->expected_seq != seq) { + pw_log_warn("unexpected seq (%d != %d)", seq, sess->expected_seq); + } + sess->expected_seq = seq + 1; + sess->have_seq = true; + + len = SPA_ROUND_DOWN(len - hlen, sess->info.stride); + payload = &bufferhlen; + + filled = spa_ringbuffer_get_write_index(&sess->ring, &index); + + timestamp = ntohl(hdr->timestamp); + expected_index = timestamp * sess->info.stride; + + if (!sess->have_sync) { + sess->ring.readindex = sess->ring.writeindex = + index = expected_index; + filled = 0; + sess->have_sync = true; + sess->buffering = true; + pw_log_info("sync to timestamp %u", index); + + spa_dll_init(&sess->dll); + spa_dll_set_bw(&sess->dll, SPA_DLL_BW_MIN, 128, sess->info.info.rate); + + } else if (expected_index != index) { + pw_log_debug("unexpected timestamp (%u != %u)", + index / sess->info.stride, + expected_index / sess->info.stride); + index = expected_index; + filled = 0; + } + + if (filled + len > BUFFER_SIZE) { + pw_log_warn("capture overrun %u %zd", filled, len); + sess->have_sync = false; + } else { + spa_ringbuffer_write_data(&sess->ring, + sess->buffer, + BUFFER_SIZE, + index & BUFFER_MASK, + payload, len); + index += len; + filled += len; + spa_ringbuffer_write_update(&sess->ring, index); + + if (sess->buffering && (uint32_t)filled > sess->target_buffer) { + sess->buffering = false; + pw_log_info("buffering done %u > %u", + filled, sess->target_buffer); + } + } + } + return; + +receive_error: + pw_log_warn("recv error: %m"); + return; +short_packet: + pw_log_warn("short packet received"); + return; +invalid_version: + pw_log_warn("invalid RTP version"); + return; +invalid_len: + pw_log_warn("invalid RTP length"); + return; +unexpected_ssrc: + pw_log_warn("unexpected SSRC (expected %u != %u)", + sess->expected_ssrc, hdr->ssrc); + return; +} + +static int make_socket(const struct sockaddr* sa, socklen_t salen, char *ifname) +{ + int af, fd, val, res; + struct ifreq req; + + af = sa->sa_family; + if ((fd = socket(af, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) { + pw_log_error("socket failed: %m"); + return -errno; + } +#ifdef SO_TIMESTAMP + val = 1; + if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &val, sizeof(val)) < 0) { + res = -errno; + pw_log_error("setsockopt failed: %m"); + goto error; + } +#endif + val = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { + res = -errno; + pw_log_error("setsockopt failed: %m"); + goto error; + } + + spa_zero(req); + if (ifname) { + snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", ifname); + res = ioctl(fd, SIOCGIFINDEX, &req); + if (res < 0) + pw_log_warn("SIOCGIFINDEX %s failed: %m", ifname); + } + res = 0; + if (af == AF_INET) { + static const uint32_t ipv4_mcast_mask = 0xe0000000; + struct sockaddr_in *sa4 = (struct sockaddr_in*)sa; + if ((ntohl(sa4->sin_addr.s_addr) & ipv4_mcast_mask) == ipv4_mcast_mask) { + struct ip_mreqn mr4; + memset(&mr4, 0, sizeof(mr4)); + mr4.imr_multiaddr = sa4->sin_addr; + mr4.imr_ifindex = req.ifr_ifindex; + res = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr4, sizeof(mr4)); + } else { + sa4->sin_addr.s_addr = INADDR_ANY; + } + } else if (af == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa; + if (sa6->sin6_addr.s6_addr0 == 0xff) { + struct ipv6_mreq mr6; + memset(&mr6, 0, sizeof(mr6)); + mr6.ipv6mr_multiaddr = sa6->sin6_addr; + mr6.ipv6mr_interface = req.ifr_ifindex; + res = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mr6, sizeof(mr6)); + } else { + sa6->sin6_addr = in6addr_any; + } + } else { + res = -EINVAL; + goto error; + } + + if (res < 0) { + res = -errno; + pw_log_error("join mcast failed: %m"); + goto error; + } + + if (bind(fd, sa, salen) < 0) { + res = -errno; + pw_log_error("bind() failed: %m"); + goto error; + } + return fd; +error: + return res; +} + +static uint32_t msec_to_bytes(struct sdp_info *info, uint32_t msec) +{ + return msec * info->stride * info->info.rate / 1000; +} + +static void session_touch(struct session *sess) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + sess->timestamp = SPA_TIMESPEC_TO_NSEC(&ts); +} + +static void session_free(struct session *sess) +{ + pw_log_info("free session %s %s", sess->info.origin, sess->info.session); + if (sess->impl) { + sess->impl->n_sessions--; + spa_list_remove(&sess->link); + } + if (sess->stream) + pw_stream_destroy(sess->stream); + if (sess->source) + pw_loop_destroy_source(sess->impl->data_loop, sess->source); + free(sess); +} + +static int session_new(struct impl *impl, struct sdp_info *info) +{ + struct session *session; + const struct spa_pod *params1; + struct spa_pod_builder b; + uint32_t n_params; + uint8_t buffer1024; + struct pw_properties *props; + int res, fd; + + if (impl->n_sessions >= MAX_SESSIONS) { + pw_log_warn("too many sessions (%u >= %u)", impl->n_sessions, MAX_SESSIONS); + return -EMFILE; + } + + session = calloc(1, sizeof(struct session)); + if (session == NULL) + return -errno; + + session->info = *info; + + session->target_buffer = msec_to_bytes(info, impl->sess_latency_msec); + session->max_error = msec_to_bytes(info, ERROR_MSEC); + + spa_dll_init(&session->dll); + spa_dll_set_bw(&session->dll, SPA_DLL_BW_MIN, 128, session->info.info.rate); + + props = pw_properties_copy(impl->stream_props); + if (props == NULL) { + res = -errno; + goto error; + } + + pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%d", info->info.rate); + pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%d/%d", + session->target_buffer / (2 * info->stride), info->info.rate); + pw_properties_set(props, "rtp.origin", info->origin); + pw_properties_setf(props, "rtp.payload", "%u", info->payload); + pw_properties_setf(props, "rtp.fmt", "%s/%u/%u", info->format_info->mime, + info->info.rate, info->info.channels); + if (info->session0) { + pw_properties_set(props, "rtp.session", info->session); + pw_properties_setf(props, PW_KEY_MEDIA_NAME, "RTP Stream (%s)", + info->session); + } else { + pw_properties_set(props, PW_KEY_MEDIA_NAME, "RTP Stream"); + } + + pw_log_info("new session %s %s", info->origin, info->session); + + session->stream = pw_stream_new(impl->core, + "rtp-source playback", props); + if (session->stream == NULL) { + res = -errno; + pw_log_error("can't create stream: %m"); + goto error; + } + + pw_stream_add_listener(session->stream, + &session->stream_listener, + &out_stream_events, session); + + n_params = 0; + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + paramsn_params++ = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, + &info->info); + + if ((res = pw_stream_connect(session->stream, + PW_DIRECTION_OUTPUT, + PW_ID_ANY, + PW_STREAM_FLAG_MAP_BUFFERS | + PW_STREAM_FLAG_AUTOCONNECT | + PW_STREAM_FLAG_RT_PROCESS, + params, n_params)) < 0) { + pw_log_error("can't connect stream: %s", spa_strerror(res)); + goto error; + } + + if ((fd = make_socket((const struct sockaddr *)&info->sa, + info->salen, impl->ifname)) < 0) { + res = fd; + goto error; + } + + session->source = pw_loop_add_io(impl->data_loop, fd, + SPA_IO_IN, true, on_rtp_io, session); + if (session->source == NULL) { + res = -errno; + pw_log_error("can't create io source: %m"); + goto error; + } + + pw_log_info("starting RTP listener"); + session_touch(session); + + session->impl = impl; + spa_list_append(&impl->sessions, &session->link); + impl->n_sessions++; + + return 0; +error: + session_free(session); + return res; +} + +static struct session *session_find(struct impl *impl, struct sdp_info *info) +{ + struct session *sess; + spa_list_for_each(sess, &impl->sessions, link) { + if (info->hash == sess->info.hash && + spa_streq(info->origin, sess->info.origin)) + return sess; + } + return NULL; +} + +static int parse_sdp_c(struct impl *impl, char *c, struct sdp_info *info) +{ + int res; + + cstrcspn(c, "/") = 0; + if (spa_strstartswith(c, "c=IN IP4 ")) { + struct sockaddr_in *sa = (struct sockaddr_in*) &info->sa; + + c += strlen("c=IN IP4 "); + if (inet_pton(AF_INET, c, &sa->sin_addr) <= 0) { + res = -errno; + pw_log_warn("inet_pton(%s) failed: %m", c); + goto error; + } + sa->sin_family = AF_INET; + info->salen = sizeof(struct sockaddr_in); + } + else if (spa_strstartswith(c, "c=IN IP6 ")) { + struct sockaddr_in6 *sa = (struct sockaddr_in6*) &info->sa; + + c += strlen("c=IN IP6 "); + if (inet_pton(AF_INET6, c, &sa->sin6_addr) <= 0) { + res = -errno; + pw_log_warn("inet_pton(%s) failed: %m", c); + goto error; + } + + sa->sin6_family = AF_INET6; + info->salen = sizeof(struct sockaddr_in6); + } else + return -EINVAL; + + + res= 0; +error: + return res; +} + +static int parse_sdp_m(struct impl *impl, char *c, struct sdp_info *info) +{ + int port, payload; + + if (!spa_strstartswith(c, "m=audio ")) + return -EINVAL; + + c += strlen("m=audio "); + if (sscanf(c, "%i RTP/AVP %i", &port, &payload) != 2) + return -EINVAL; + + if (port <= 0 || port > 0xFFFF) + return -EINVAL; + + if (payload < 0 || payload > 127) + return -EINVAL; + + info->port = (uint16_t) port; + info->payload = (uint8_t) payload; + + return 0; +} + +static int parse_sdp_a(struct impl *impl, char *c, struct sdp_info *info) +{ + int payload, len, rate, channels; + + if (!spa_strstartswith(c, "a=rtpmap:")) + return 0; + + c += strlen("a=rtpmap:"); + + if (sscanf(c, "%i %n", &payload, &len) != 1) + return -EINVAL; + + if (payload < 0 || payload > 127) + return -EINVAL; + + if (payload != info->payload) + return 0; + + c += len; + cstrcspn(c, "/") = 0; + + info->format_info = find_format_info(c); + if (info->format_info == NULL) + return -EINVAL; + + info->info.format = info->format_info->format; + info->stride = info->format_info->size; + + c += strlen(c) + 1; + if (sscanf(c, "%u/%u", &rate, &channels) == 2) { + info->info.rate = rate; + info->info.channels = channels; + if (channels == 2) { + info->info.position0 = SPA_AUDIO_CHANNEL_FL; + info->info.position1 = SPA_AUDIO_CHANNEL_FR; + } + } else if (sscanf(c, "%u", &rate) == 1) { + info->info.rate = rate; + info->info.channels = 1; + } else + return -EINVAL; + + info->stride *= info->info.channels; + + return 0; +} + +static int parse_sdp(struct impl *impl, char *sdp, struct sdp_info *info) +{ + char *s = sdp; + int count = 0, res = 0; + size_t l; + + while (*s) { + if ((l = strcspn(s, "\r\n")) < 2) + goto too_short; + + sl = 0; + pw_log_debug("%d: %s", count, s); + + if (count++ == 0 && strcmp(s, "v=0") != 0) + goto invalid_version; + + if (spa_strstartswith(s, "o=")) + snprintf(info->origin, sizeof(info->origin), "%s", &s2); + else if (spa_strstartswith(s, "s=")) + snprintf(info->session, sizeof(info->session), "%s", &s2); + else if (spa_strstartswith(s, "c=")) + res = parse_sdp_c(impl, s, info); + else if (spa_strstartswith(s, "m=")) + res = parse_sdp_m(impl, s, info); + else if (spa_strstartswith(s, "a=")) + res = parse_sdp_a(impl, s, info); + + if (res < 0) + goto error; + s += l + 1; + while (isspace(*s)) + s++; + } + if (((struct sockaddr*) &info->sa)->sa_family == AF_INET) + ((struct sockaddr_in*) &info->sa)->sin_port = htons(info->port); + else + ((struct sockaddr_in6*) &info->sa)->sin6_port = htons(info->port); + + return 0; +too_short: + pw_log_warn("SDP: line starting with `%.6s...' too short", s); + return -EINVAL; +invalid_version: + pw_log_warn("SDP: invalid first version line `%*s'", (int)l, s); + return -EINVAL; +error: + pw_log_warn("SDP: error: %s", spa_strerror(res)); + return res; +} + +static int parse_sap(struct impl *impl, void *data, size_t len) +{ + struct sap_header *header; + char *mime, *sdp; + struct sdp_info info; + struct session *sess; + int res; + size_t offs; + bool bye; + + if (len < 8) + return -EINVAL; + + header = (struct sap_header*) data; + if (header->v != 1) + return -EINVAL; + + if (header->e) + return -ENOTSUP; + if (header->c) + return -ENOTSUP; + + offs = header->a ? 12 : 8; + offs += header->auth_len * 4; + if (len <= offs) + return -EINVAL; + + mime = SPA_PTROFF(data, offs, char); + if (spa_strstartswith(mime, "v=0")) { + sdp = mime; + mime = SAP_MIME_TYPE; + } else if (spa_streq(mime, SAP_MIME_TYPE)) + sdp = SPA_PTROFF(mime, strlen(mime)+1, char); + else + return -EINVAL; + + pw_log_debug("got sap: %s %s", mime, sdp); + + spa_zero(info); + if ((res = parse_sdp(impl, sdp, &info)) < 0) + return res; + + bye = header->t; + + sess = session_find(impl, &info); + if (sess == NULL) { + if (!bye) + session_new(impl, &info); + } else { + if (bye) + session_free(sess); + else + session_touch(sess); + } + return res; +} + +static void +on_sap_io(void *data, int fd, uint32_t mask) +{ + struct impl *impl = data; + + if (mask & SPA_IO_IN) { + uint8_t buffer2048; + ssize_t len; + + if ((len = recv(fd, buffer, sizeof(buffer), 0)) < 0) { + pw_log_warn("recv error: %m"); + return; + } + if ((size_t)len >= sizeof(buffer)) + return; + + bufferlen = 0; + parse_sap(impl, buffer, len); + } +} + +static int start_sap_listener(struct impl *impl) +{ + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; + struct sockaddr *sa; + socklen_t salen; + int fd, res; + + if (inet_pton(AF_INET, impl->sap_ip, &sa4.sin_addr) > 0) { + sa4.sin_family = AF_INET; + sa4.sin_port = htons(impl->sap_port); + sa = (struct sockaddr*) &sa4; + salen = sizeof(sa4); + } else if (inet_pton(AF_INET6, impl->sap_ip, &sa6.sin6_addr) > 0) { + sa6.sin6_family = AF_INET6; + sa6.sin6_port = htons(impl->sap_port); + sa = (struct sockaddr*) &sa6; + salen = sizeof(sa6); + } else + return -EINVAL; + + if ((fd = make_socket(sa, salen, impl->ifname)) < 0) + return fd; + + pw_log_info("starting SAP listener"); + impl->sap_source = pw_loop_add_io(impl->loop, fd, + SPA_IO_IN, true, on_sap_io, impl); + if (impl->sap_source == NULL) { + res = -errno; + goto error; + } + return 0; +error: + close(fd); + return res; + +} + +static void on_timer_event(void *data, uint64_t expirations) +{ + struct impl *impl = data; + struct timespec now; + struct session *sess, *tmp; + uint64_t timestamp, interval; + + clock_gettime(CLOCK_MONOTONIC, &now); + timestamp = SPA_TIMESPEC_TO_NSEC(&now); + interval = CLEANUP_INTERVAL_SEC * SPA_NSEC_PER_SEC; + + spa_list_for_each_safe(sess, tmp, &impl->sessions, link) { + if (sess->timestamp + interval < timestamp) + session_free(sess); + } +} + +static void core_destroy(void *d) +{ + struct impl *impl = d; + spa_hook_remove(&impl->core_listener); + impl->core = NULL; + pw_impl_module_schedule_destroy(impl->module); +} + +static const struct pw_proxy_events core_proxy_events = { + .destroy = core_destroy, +}; + +static void impl_destroy(struct impl *impl) +{ + struct session *sess; + spa_list_consume(sess, &impl->sessions, link) + session_free(sess); + + if (impl->core && impl->do_disconnect) + pw_core_disconnect(impl->core); + + if (impl->sap_source) + pw_loop_destroy_source(impl->loop, impl->sap_source); + if (impl->timer) + pw_loop_destroy_source(impl->loop, impl->timer); + + pw_properties_free(impl->stream_props); + pw_properties_free(impl->props); + + free(impl->ifname); + free(impl->sap_ip); + free(impl); +} + +static void module_destroy(void *d) +{ + struct impl *impl = d; + spa_hook_remove(&impl->module_listener); + impl_destroy(impl); +} + +static const struct pw_impl_module_events module_events = { + PW_VERSION_IMPL_MODULE_EVENTS, + .destroy = module_destroy, +}; + +static void on_core_error(void *d, uint32_t id, int seq, int res, const char *message) +{ + struct impl *impl = d; + + pw_log_error("error id:%u seq:%d res:%d (%s): %s", + id, seq, res, spa_strerror(res), message); + + if (id == PW_ID_CORE && res == -EPIPE) + pw_impl_module_schedule_destroy(impl->module); +} + +static const struct pw_core_events core_events = { + PW_VERSION_CORE_EVENTS, + .error = on_core_error, +}; + +SPA_EXPORT +int pipewire__module_init(struct pw_impl_module *module, const char *args) +{ + struct pw_context *context = pw_impl_module_get_context(module); + struct impl *impl; + struct pw_properties *props = NULL, *stream_props = NULL; + const char *str; + struct timespec value, interval; + int res = 0; + + PW_LOG_TOPIC_INIT(mod_topic); + + impl = calloc(1, sizeof(struct impl)); + if (impl == NULL) + return -errno; + + if (args == NULL) + args = ""; + + props = pw_properties_new_string(args); + if (props == NULL) { + res = -errno; + pw_log_error( "can't create properties: %m"); + goto out; + } + spa_list_init(&impl->sessions); + impl->props = props; + + stream_props = pw_properties_new(NULL, NULL); + if (stream_props == NULL) { + res = -errno; + pw_log_error( "can't create properties: %m"); + goto out; + } + impl->stream_props = stream_props; + + impl->module = module; + impl->module_context = context; + impl->loop = pw_context_get_main_loop(context); + impl->data_loop = pw_data_loop_get_loop(pw_context_get_data_loop(context)); + + if (pw_properties_get(stream_props, PW_KEY_NODE_VIRTUAL) == NULL) + pw_properties_set(stream_props, PW_KEY_NODE_VIRTUAL, "true"); + if (pw_properties_get(stream_props, PW_KEY_NODE_NETWORK) == NULL) + pw_properties_set(stream_props, PW_KEY_NODE_NETWORK, "true"); + + if ((str = pw_properties_get(props, "stream.props")) != NULL) + pw_properties_update_string(stream_props, str, strlen(str)); + + str = pw_properties_get(props, "local.ifname"); + impl->ifname = str ? strdup(str) : NULL; + + str = pw_properties_get(props, "sap.ip"); + impl->sap_ip = strdup(str ? str : DEFAULT_SAP_IP); + impl->sap_port = pw_properties_get_uint32(props, + "sap.port", DEFAULT_SAP_PORT); + impl->sess_latency_msec = pw_properties_get_uint32(props, + "sess.latency.msec", DEFAULT_SESS_LATENCY); + + impl->core = pw_context_get_object(impl->module_context, PW_TYPE_INTERFACE_Core); + if (impl->core == NULL) { + str = pw_properties_get(props, PW_KEY_REMOTE_NAME); + impl->core = pw_context_connect(impl->module_context, + pw_properties_new( + PW_KEY_REMOTE_NAME, str, + NULL), + 0); + impl->do_disconnect = true; + } + if (impl->core == NULL) { + res = -errno; + pw_log_error("can't connect: %m"); + goto out; + } + + pw_proxy_add_listener((struct pw_proxy*)impl->core, + &impl->core_proxy_listener, + &core_proxy_events, impl); + pw_core_add_listener(impl->core, + &impl->core_listener, + &core_events, impl); + + impl->timer = pw_loop_add_timer(impl->loop, on_timer_event, impl); + if (impl->timer == NULL) { + res = -errno; + pw_log_error("can't create timer source: %m"); + goto out; + } + value.tv_sec = 0; + value.tv_nsec = 1; + interval.tv_sec = CLEANUP_INTERVAL_SEC; + interval.tv_nsec = 0; + pw_loop_update_timer(impl->loop, impl->timer, &value, &interval, false); + + if ((res = start_sap_listener(impl)) < 0) + goto out; + + pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl); + + pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_info)); + + pw_log_info("Successfully loaded module-rtp-source"); + + return 0; +out: + impl_destroy(impl); + return res; +}
View file
pipewire-0.3.60.tar.gz/src/modules/module-rtp/rtp.h
Added
@@ -0,0 +1,78 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef PIPEWIRE_RTP_H +#define PIPEWIRE_RTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtp_header { +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned cc:4; + unsigned x:1; + unsigned p:1; + unsigned v:2; + + unsigned pt:7; + unsigned m:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned v:2; + unsigned p:1; + unsigned x:1; + unsigned cc:4; + + unsigned m:1; + unsigned pt:7; +#else +#error "Unknown byte order" +#endif + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc0; +} __attribute__ ((packed)); + +struct rtp_payload { +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned frame_count:4; + unsigned rfa0:1; + unsigned is_last_fragment:1; + unsigned is_first_fragment:1; + unsigned is_fragmented:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned is_fragmented:1; + unsigned is_first_fragment:1; + unsigned is_last_fragment:1; + unsigned rfa0:1; + unsigned frame_count:4; +#endif +} __attribute__ ((packed)); + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_RTP_H */
View file
pipewire-0.3.60.tar.gz/src/modules/module-rtp/sap.h
Added
@@ -0,0 +1,58 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef PIPEWIRE_SAP_H +#define PIPEWIRE_SAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct sap_header { +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned c:1; + unsigned e:1; + unsigned t:1; + unsigned r:1; + unsigned a:1; + unsigned v:3; +#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned v:3; + unsigned a:1; + unsigned r:1; + unsigned t:1; + unsigned e:1; + unsigned c:1; +#else +#error "Unknown byte order" +#endif + uint8_t auth_len; + uint16_t msg_id_hash; +} __attribute__ ((packed)); + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_SAP_H */
View file
pipewire-0.3.59.tar.gz/src/modules/module-x11-bell.c -> pipewire-0.3.60.tar.gz/src/modules/module-x11-bell.c
Changed
@@ -109,7 +109,7 @@ if (sample == NULL) sample = "bell-window-system"; - pw_log_debug("play sample %s", sample); + pw_log_info("play sample %s", sample); if ((res = ca_context_create(&ca)) < 0) { pw_log_error("canberra context create error: %s", ca_strerror(res));
View file
pipewire-0.3.59.tar.gz/src/modules/module-zeroconf-discover.c -> pipewire-0.3.60.tar.gz/src/modules/module-zeroconf-discover.c
Changed
@@ -54,7 +54,10 @@ * audio to/from remote PulseAudio servers. It also works with * module-protocol-pulse. * - * This module has no options. + * ## Module Options + * + * - `pulse.latency`: the latency to end-to-end latency in milliseconds to + * maintain (Default 200ms). * * ## Example configuration * @@ -72,7 +75,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic -#define MODULE_USAGE " " +#define MODULE_USAGE "pulse.latency=<latency in msec> " static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, @@ -348,6 +351,9 @@ _("%s on %s"), desc, fqdn); } + if ((str = pw_properties_get(impl->properties, "pulse.latency")) != NULL) + pw_properties_set(props, "pulse.latency", str); + if ((f = open_memstream(&args, &size)) == NULL) { pw_log_error("Can't open memstream: %m"); goto done;
View file
pipewire-0.3.59.tar.gz/src/pipewire/buffers.c -> pipewire-0.3.60.tar.gz/src/pipewire/buffers.c
Changed
@@ -255,6 +255,12 @@ struct port input = { innode, SPA_DIRECTION_INPUT, in_port_id }; int res; + if (flags & PW_BUFFERS_FLAG_IN_PRIORITY) { + struct port tmp = output; + output = input; + input = tmp; + } + res = param_filter(result, &input, &output, SPA_PARAM_Buffers, &b); if (res < 0) { pw_context_debug_port_params(context, input.node, input.direction,
View file
pipewire-0.3.59.tar.gz/src/pipewire/buffers.h -> pipewire-0.3.60.tar.gz/src/pipewire/buffers.h
Changed
@@ -48,6 +48,7 @@ #define PW_BUFFERS_FLAG_SHARED (1<<1) /**< buffers can be shared */ #define PW_BUFFERS_FLAG_DYNAMIC (1<<2) /**< buffers have dynamic data */ #define PW_BUFFERS_FLAG_SHARED_MEM (1<<3) /**< buffers need shared memory */ +#define PW_BUFFERS_FLAG_IN_PRIORITY (1<<4) /**< input parameters have priority */ struct pw_buffers { struct pw_memblock *mem; /**< allocated buffer memory */
View file
pipewire-0.3.59.tar.gz/src/pipewire/context.c -> pipewire-0.3.60.tar.gz/src/pipewire/context.c
Changed
@@ -968,13 +968,15 @@ return fa < fb ? -1 : (fa > fb ? 1 : 0); } -static bool rates_contains(uint32_t *rates, uint32_t n_rates, uint32_t rate) +static uint32_t find_best_rate(uint32_t *rates, uint32_t n_rates, uint32_t rate, uint32_t best) { uint32_t i; - for (i = 0; i < n_rates; i++) - if (ratesi == rate) - return true; - return false; + for (i = 0; i < n_rates; i++) { + if (SPA_ABS((int32_t)rate - (int32_t)ratesi) < + SPA_ABS((int32_t)rate - (int32_t)best)) + best = ratesi; + } + return best; } /* here we evaluate the complete state of the graph. @@ -1187,10 +1189,9 @@ * Start with the default rate. If the desired rate is * allowed, switch to it */ target_rate = def_rate; - if (rate.denom != 0 && rate.num == 1) { - if (rates_contains(rates, n_rates, rate.denom)) - target_rate = rate.denom; - } + if (rate.denom != 0 && rate.num == 1) + target_rate = find_best_rate(rates, n_rates, + rate.denom, target_rate); } if (target_rate != current_rate) {
View file
pipewire-0.3.59.tar.gz/src/pipewire/filter.c -> pipewire-0.3.60.tar.gz/src/pipewire/filter.c
Changed
@@ -419,16 +419,16 @@ spa_list_for_each(p, param_list, link) { struct spa_pod *param; - result.index = result.next++; - if (result.index < start) - continue; - param = p->param; if (param == NULL || p->id != id) continue; found = true; + result.index = result.next++; + if (result.index < start) + continue; + spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); if (spa_pod_filter(&b.b, &result.param, param, filter) == 0) { spa_node_emit_result(&d->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
View file
pipewire-0.3.59.tar.gz/src/pipewire/impl-device.c -> pipewire-0.3.60.tar.gz/src/pipewire/impl-device.c
Changed
@@ -235,7 +235,7 @@ if (d->end != -1) { if (d->pi && d->data.cache) { - pw_param_update(&impl->param_list, &impl->pending_list); + pw_param_update(&impl->param_list, &impl->pending_list, 0, NULL); d->pi->user = 1; d->pi = NULL; } @@ -281,8 +281,8 @@ if (d->cache) { pw_log_debug("%p: add param %d", impl, r->id); if (d->count++ == 0) - pw_param_add(&impl->pending_list, r->id, NULL); - pw_param_add(&impl->pending_list, r->id, r->param); + pw_param_add(&impl->pending_list, seq, r->id, NULL); + pw_param_add(&impl->pending_list, seq, r->id, r->param); } break; } @@ -333,10 +333,10 @@ result.next = 0; spa_list_for_each(p, &impl->param_list, link) { - result.index = result.next++; if (p->id != param_id) continue; + result.index = result.next++; if (result.index < index) continue; @@ -364,7 +364,7 @@ spa_hook_remove(&listener); if (!SPA_RESULT_IS_ASYNC(res) && user_data.cache) { - pw_param_update(&impl->param_list, &impl->pending_list); + pw_param_update(&impl->param_list, &impl->pending_list, 0, NULL); pi->user = 1; } }
View file
pipewire-0.3.59.tar.gz/src/pipewire/impl-link.c -> pipewire-0.3.60.tar.gz/src/pipewire/impl-link.c
Changed
@@ -517,6 +517,9 @@ if (output->node->remote || input->node->remote) alloc_flags |= PW_BUFFERS_FLAG_SHARED_MEM; + if (output->node->driver) + alloc_flags |= PW_BUFFERS_FLAG_IN_PRIORITY; + /* if output port can alloc buffers, alloc skeleton buffers */ if (SPA_FLAG_IS_SET(out_flags, SPA_PORT_FLAG_CAN_ALLOC_BUFFERS)) { SPA_FLAG_SET(alloc_flags, PW_BUFFERS_FLAG_NO_MEM); @@ -619,7 +622,8 @@ pw_log_debug("%p: activate activated:%d state:%s", this, impl->activated, pw_link_state_as_string(this->info.state)); - if (impl->activated || !this->prepared || !impl->inode->added || !impl->onode->active) + if (impl->activated || !this->prepared || !impl->inode->active || + !impl->inode->added || !impl->onode->active) return 0; if (!impl->io_set) {
View file
pipewire-0.3.59.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.60.tar.gz/src/pipewire/impl-node.c
Changed
@@ -158,19 +158,6 @@ this->rt.driver_target.node = NULL; } -static int -do_node_remove(struct spa_loop *loop, - bool async, uint32_t seq, const void *data, size_t size, void *user_data) -{ - struct pw_impl_node *this = user_data; - if (this->source.loop != NULL) { - spa_loop_remove_source(loop, &this->source); - remove_node(this); - } - this->added = false; - return 0; -} - static void node_deactivate(struct pw_impl_node *this) { struct pw_impl_port *port; @@ -185,7 +172,6 @@ spa_list_for_each(link, &port->links, output_link) pw_impl_link_deactivate(link); } - pw_loop_invoke(this->data_loop, do_node_remove, 1, NULL, 0, true, this); } static int idle_node(struct pw_impl_node *this) @@ -354,6 +340,19 @@ return 0; } +static int +do_node_remove(struct spa_loop *loop, + bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct pw_impl_node *this = user_data; + if (this->source.loop != NULL) { + spa_loop_remove_source(loop, &this->source); + remove_node(this); + } + this->added = false; + return 0; +} + static void node_update_state(struct pw_impl_node *node, enum pw_node_state state, int res, char *error) { struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this); @@ -364,20 +363,29 @@ pw_log_debug("%p: start node driving:%d driver:%d added:%d", node, node->driving, node->driver, node->added); + if (res >= 0) { + pw_loop_invoke(node->data_loop, do_node_add, 1, NULL, 0, true, node); + } if (node->driving && node->driver) { res = spa_node_send_command(node->node, &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Start)); if (res < 0) { state = PW_NODE_STATE_ERROR; error = spa_aprintf("Start error: %s", spa_strerror(res)); + pw_loop_invoke(node->data_loop, do_node_remove, 1, NULL, 0, true, node); } } if (res >= 0) { - pw_loop_invoke(node->data_loop, do_node_add, 1, NULL, 0, true, node); /* now activate the inputs */ node_activate_inputs(node); } break; + case PW_NODE_STATE_IDLE: + case PW_NODE_STATE_SUSPENDED: + case PW_NODE_STATE_ERROR: + if (state != PW_NODE_STATE_IDLE || impl->pause_on_idle) + pw_loop_invoke(node->data_loop, do_node_remove, 1, NULL, 0, true, node); + break; default: break; } @@ -1624,8 +1632,13 @@ struct pw_node_target *t; struct pw_impl_port *p; - pw_log_trace_fp("%p: ready driver:%d exported:%d %p status:%d", node, - node->driver, node->exported, driver, status); + pw_log_trace_fp("%p: ready driver:%d exported:%d %p status:%d added:%d", node, + node->driver, node->exported, driver, status, node->added); + + if (!node->added) { + pw_log_warn("%p: ready non-active node", node); + return -EIO; + } if (SPA_UNLIKELY(node == driver)) { struct pw_node_activation *a = node->rt.activation; @@ -1954,8 +1967,8 @@ d->callback(d->data, seq, r->id, r->index, r->next, r->param); if (d->cache) { if (d->count++ == 0) - pw_param_add(&impl->pending_list, r->id, NULL); - pw_param_add(&impl->pending_list, r->id, r->param); + pw_param_add(&impl->pending_list, seq, r->id, NULL); + pw_param_add(&impl->pending_list, seq, r->id, r->param); } } break; @@ -2007,10 +2020,10 @@ result.next = 0; spa_list_for_each(p, &impl->param_list, link) { - result.index = result.next++; if (p->id != param_id) continue; + result.index = result.next++; if (result.index < index) continue; @@ -2039,7 +2052,7 @@ spa_hook_remove(&listener); if (user_data.cache) { - pw_param_update(&impl->param_list, &impl->pending_list); + pw_param_update(&impl->param_list, &impl->pending_list, 0, NULL); pi->user = 1; } }
View file
pipewire-0.3.59.tar.gz/src/pipewire/impl-port.c -> pipewire-0.3.60.tar.gz/src/pipewire/impl-port.c
Changed
@@ -1199,8 +1199,8 @@ d->callback(d->data, seq, r->id, r->index, r->next, r->param); if (d->cache) { if (d->count++ == 0) - pw_param_add(&impl->pending_list, r->id, NULL); - pw_param_add(&impl->pending_list, r->id, r->param); + pw_param_add(&impl->pending_list, seq, r->id, NULL); + pw_param_add(&impl->pending_list, seq, r->id, r->param); } } break; @@ -1253,10 +1253,10 @@ result.next = 0; spa_list_for_each(p, &impl->param_list, link) { - result.index = result.next++; if (p->id != param_id) continue; + result.index = result.next++; if (result.index < index) continue; @@ -1286,7 +1286,7 @@ spa_hook_remove(&listener); if (user_data.cache) { - pw_param_update(&impl->param_list, &impl->pending_list); + pw_param_update(&impl->param_list, &impl->pending_list, 0, NULL); pi->user = 1; } }
View file
pipewire-0.3.59.tar.gz/src/pipewire/introspect.c -> pipewire-0.3.60.tar.gz/src/pipewire/introspect.c
Changed
@@ -210,7 +210,7 @@ info->props = pw_spa_dict_copy(update->props); } if (update->change_mask & PW_NODE_CHANGE_MASK_PARAMS) { - uint32_t i, user, n_params = update->n_params; + uint32_t i, n_params = update->n_params; void *np; np = pw_reallocarray(info->params, n_params, sizeof(struct spa_param_info)); @@ -222,15 +222,18 @@ info->params = np; for (i = 0; i < SPA_MIN(info->n_params, n_params); i++) { - user = reset ? 0 : info->paramsi.user; - if (info->paramsi.flags != update->paramsi.flags) - user++; - info->paramsi = update->paramsi; - info->paramsi.user = user; + info->paramsi.id = update->paramsi.id; + if (reset) + info->paramsi.user = 0; + if (info->paramsi.flags != update->paramsi.flags) { + info->paramsi.flags = update->paramsi.flags; + info->paramsi.user++; + } } info->n_params = n_params; for (; i < info->n_params; i++) { - info->paramsi = update->paramsi; + info->paramsi.id = update->paramsi.id; + info->paramsi.flags = update->paramsi.flags; info->paramsi.user = 1; } } @@ -280,7 +283,7 @@ info->props = pw_spa_dict_copy(update->props); } if (update->change_mask & PW_PORT_CHANGE_MASK_PARAMS) { - uint32_t i, user, n_params = update->n_params; + uint32_t i, n_params = update->n_params; void *np; np = pw_reallocarray(info->params, n_params, sizeof(struct spa_param_info)); @@ -292,15 +295,18 @@ info->params = np; for (i = 0; i < SPA_MIN(info->n_params, n_params); i++) { - user = reset ? 0 : info->paramsi.user; - if (info->paramsi.flags != update->paramsi.flags) - user++; - info->paramsi = update->paramsi; - info->paramsi.user = user; + info->paramsi.id = update->paramsi.id; + if (reset) + info->paramsi.user = 0; + if (info->paramsi.flags != update->paramsi.flags) { + info->paramsi.flags = update->paramsi.flags; + info->paramsi.user++; + } } info->n_params = n_params; for (; i < info->n_params; i++) { - info->paramsi = update->paramsi; + info->paramsi.id = update->paramsi.id; + info->paramsi.flags = update->paramsi.flags; info->paramsi.user = 1; } } @@ -440,7 +446,7 @@ info->props = pw_spa_dict_copy(update->props); } if (update->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) { - uint32_t i, user, n_params = update->n_params; + uint32_t i, n_params = update->n_params; void *np; np = pw_reallocarray(info->params, n_params, sizeof(struct spa_param_info)); @@ -452,15 +458,18 @@ info->params = np; for (i = 0; i < SPA_MIN(info->n_params, n_params); i++) { - user = reset ? 0 : info->paramsi.user; - if (info->paramsi.flags != update->paramsi.flags) - user++; - info->paramsi = update->paramsi; - info->paramsi.user = user; + info->paramsi.id = update->paramsi.id; + if (reset) + info->paramsi.user = 0; + if (info->paramsi.flags != update->paramsi.flags) { + info->paramsi.flags = update->paramsi.flags; + info->paramsi.user++; + } } info->n_params = n_params; for (; i < info->n_params; i++) { - info->paramsi = update->paramsi; + info->paramsi.id = update->paramsi.id; + info->paramsi.flags = update->paramsi.flags; info->paramsi.user = 1; } }
View file
pipewire-0.3.59.tar.gz/src/pipewire/private.h -> pipewire-0.3.60.tar.gz/src/pipewire/private.h
Changed
@@ -104,6 +104,7 @@ struct pw_param { uint32_t id; + int32_t seq; struct spa_list link; struct spa_pod *param; }; @@ -123,7 +124,7 @@ return count; } -static inline struct pw_param *pw_param_add(struct spa_list *params, +static inline struct pw_param *pw_param_add(struct spa_list *params, int32_t seq, uint32_t id, const struct spa_pod *param) { struct pw_param *p; @@ -140,6 +141,7 @@ return NULL; p->id = id; + p->seq = seq; if (param != NULL) { p->param = SPA_PTROFF(p, sizeof(*p), struct spa_pod); memcpy(p->param, param, SPA_POD_SIZE(param)); @@ -151,10 +153,22 @@ return p; } -static inline void pw_param_update(struct spa_list *param_list, struct spa_list *pending_list) +static inline void pw_param_update(struct spa_list *param_list, struct spa_list *pending_list, + uint32_t n_params, struct spa_param_info *params) { - struct pw_param *p; + struct pw_param *p, *t; + uint32_t i; + for (i = 0; i < n_params; i++) { + spa_list_for_each_safe(p, t, pending_list, link) { + if (p->id == paramsi.id && + p->seq != paramsi.seq && + p->param != NULL) { + spa_list_remove(&p->link); + free(p); + } + } + } spa_list_consume(p, pending_list, link) { spa_list_remove(&p->link); if (p->param == NULL) {
View file
pipewire-0.3.59.tar.gz/src/pipewire/stream.c -> pipewire-0.3.60.tar.gz/src/pipewire/stream.c
Changed
@@ -544,16 +544,16 @@ spa_list_for_each(p, &d->param_list, link) { struct spa_pod *param; - result.index = result.next++; - if (result.index < start) - continue; - param = p->param; if (param == NULL || p->id != id) continue; found = true; + result.index = result.next++; + if (result.index < start) + continue; + spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); if (spa_pod_filter(&b.b, &result.param, param, filter) == 0) { spa_node_emit_result(&d->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); @@ -2330,7 +2330,7 @@ struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); pw_loop_invoke(impl->context->data_loop, drain ? do_drain : do_flush, 1, NULL, 0, true, impl); - if (!drain) + if (!drain && impl->node != NULL) spa_node_send_command(impl->node->node, &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Flush)); return 0;
View file
pipewire-0.3.59.tar.gz/src/pipewire/thread.c -> pipewire-0.3.60.tar.gz/src/pipewire/thread.c
Changed
@@ -116,6 +116,17 @@ *max = sched_get_priority_max(SCHED_OTHER); return 0; } +static int impl_acquire_rt(void *object, struct spa_thread *thread, int priority) +{ + pw_log_warn("acquire_rt thread:%p prio:%d not implemented", thread, priority); + return -ENOTSUP; +} + +static int impl_drop_rt(void *object, struct spa_thread *thread) +{ + pw_log_warn("drop_rt thread:%p not implemented", thread); + return -ENOTSUP; +} static struct { struct spa_thread_utils utils; @@ -128,7 +139,9 @@ { SPA_VERSION_THREAD_UTILS_METHODS, .create = impl_create, .join = impl_join, - .get_rt_range = impl_get_rt_range + .get_rt_range = impl_get_rt_range, + .acquire_rt = impl_acquire_rt, + .drop_rt = impl_drop_rt, } };
View file
pipewire-0.3.59.tar.gz/src/tools/pw-cat.c -> pipewire-0.3.60.tar.gz/src/tools/pw-cat.c
Changed
@@ -174,20 +174,18 @@ static const struct format_info *format_info_by_name(const char *str) { - uint32_t i; - for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) - if (spa_streq(str, format_infoi.name)) - return &format_infoi; + SPA_FOR_EACH_ELEMENT_VAR(format_info, i) + if (spa_streq(str, i->name)) + return i; return NULL; } static const struct format_info *format_info_by_sf_format(int format) { - uint32_t i; int sub_type = (format & SF_FORMAT_SUBMASK); - for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) - if (format_infoi.sf_format == sub_type) - return &format_infoi; + SPA_FOR_EACH_ELEMENT_VAR(format_info, i) + if (i->sf_format == sub_type) + return i; return NULL; } @@ -431,10 +429,10 @@ int i, nch; char **ch; - for (i = 0; i < (int) SPA_N_ELEMENTS(maps); i++) { - if (spa_streq(mapsi.name, channel_map)) { - map->n_channels = mapsi.channels; - spa_memcpy(map->channels, &mapsi.values, + SPA_FOR_EACH_ELEMENT_VAR(maps, m) { + if (spa_streq(m->name, channel_map)) { + map->n_channels = m->channels; + spa_memcpy(map->channels, &m->values, map->n_channels * sizeof(unsigned int)); return 0; } @@ -1589,17 +1587,16 @@ case TYPE_DSD: { struct spa_audio_info_dsd info; - size_t i; spa_zero(info); info.channels = data.dsf.info.channels; info.rate = data.dsf.info.rate / 8; - for (i = 0; i < SPA_N_ELEMENTS(dsd_layouts); i++) { - if (dsd_layoutsi.type != data.dsf.info.channel_type) + SPA_FOR_EACH_ELEMENT_VAR(dsd_layouts, i) { + if (i->type != data.dsf.info.channel_type) continue; - info.channels = dsd_layoutsi.info.n_channels; - memcpy(info.position, dsd_layoutsi.info.position, + info.channels = i->info.n_channels; + memcpy(info.position, i->info.position, info.channels * sizeof(uint32_t)); } params0 = spa_format_audio_dsd_build(&b, SPA_PARAM_EnumFormat, &info);
View file
pipewire-0.3.59.tar.gz/src/tools/pw-cli.c -> pipewire-0.3.60.tar.gz/src/tools/pw-cli.c
Changed
@@ -262,14 +262,11 @@ static bool do_help(struct data *data, const char *cmd, char *args, char **error) { - size_t i; - printf("Available commands:\n"); - for (i = 0; i < SPA_N_ELEMENTS(command_list); i++) { + SPA_FOR_EACH_ELEMENT_VAR(command_list, c) { char cmd256; - snprintf(cmd, sizeof(cmd), "%s | %s", - command_listi.name, command_listi.alias); - printf("\t%-20.20s\t%s\n", cmd, command_listi.description); + snprintf(cmd, sizeof(cmd), "%s | %s", c->name, c->alias); + printf("\t%-20.20s\t%s\n", cmd, c->description); } return true; } @@ -1312,11 +1309,10 @@ static const struct class *find_class(const char *type, uint32_t version) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(classes); i++) { - if (spa_streq(classesi->type, type) && - classesi->version <= version) - return classesi; + SPA_FOR_EACH_ELEMENT_VAR(classes, c) { + if (spa_streq((*c)->type, type) && + (*c)->version <= version) + return *c; } return NULL; } @@ -2110,7 +2106,6 @@ { char *a2; int n; - size_t i; char *p, *cmd, *args; if ((p = strchr(buf, '#'))) @@ -2128,10 +2123,10 @@ cmd = a0; args = n > 1 ? a1 : ""; - for (i = 0; i < SPA_N_ELEMENTS(command_list); i++) { - if (spa_streq(command_listi.name, cmd) || - spa_streq(command_listi.alias, cmd)) { - return command_listi.func(data, cmd, args, error); + SPA_FOR_EACH_ELEMENT_VAR(command_list, c) { + if (spa_streq(c->name, cmd) || + spa_streq(c->alias, cmd)) { + return c->func(data, cmd, args, error); } } *error = spa_aprintf("Command \"%s\" does not exist. Type 'help' for usage.", cmd); @@ -2231,13 +2226,13 @@ return matches; } -static void readline_init() +static void readline_init(void) { rl_attempted_completion_function = readline_command_completion; rl_callback_handler_install(">> ", input_process_line); } -static void readline_cleanup() +static void readline_cleanup(void) { rl_callback_handler_remove(); }
View file
pipewire-0.3.59.tar.gz/src/tools/pw-dot.c -> pipewire-0.3.60.tar.gz/src/tools/pw-dot.c
Changed
@@ -94,7 +94,7 @@ struct spa_hook object_listener; }; -static char *dot_str_new() +static char *dot_str_new(void) { return strdup(""); }
View file
pipewire-0.3.59.tar.gz/src/tools/pw-dump.c -> pipewire-0.3.60.tar.gz/src/tools/pw-dump.c
Changed
@@ -88,6 +88,7 @@ struct param { uint32_t id; + int32_t seq; struct spa_list link; struct spa_pod *param; }; @@ -116,6 +117,8 @@ const struct class *class; void *info; + struct spa_param_info *params; + uint32_t n_params; int changed; struct spa_list param_list; @@ -148,7 +151,8 @@ return count; } -static struct param *add_param(struct spa_list *params, uint32_t id, const struct spa_pod *param) +static struct param *add_param(struct spa_list *params, int seq, + uint32_t id, const struct spa_pod *param) { struct param *p; @@ -165,6 +169,7 @@ return NULL; p->id = id; + p->seq = seq; if (param != NULL) { p->param = SPA_PTROFF(p, sizeof(*p), struct spa_pod); memcpy(p->param, param, SPA_POD_SIZE(param)); @@ -187,17 +192,30 @@ return NULL; } -static void object_update_params(struct object *o) +static void object_update_params(struct spa_list *param_list, struct spa_list *pending_list, + uint32_t n_params, struct spa_param_info *params) { - struct param *p; + struct param *p, *t; + uint32_t i; + + for (i = 0; i < n_params; i++) { + spa_list_for_each_safe(p, t, pending_list, link) { + if (p->id == paramsi.id && + p->seq != paramsi.seq && + p->param != NULL) { + spa_list_remove(&p->link); + free(p); + } + } + } - spa_list_consume(p, &o->pending_list, link) { + spa_list_consume(p, pending_list, link) { spa_list_remove(&p->link); if (p->param == NULL) { - clear_params(&o->param_list, p->id); + clear_params(param_list, p->id); free(p); } else { - spa_list_append(&o->param_list, &p->link); + spa_list_append(param_list, &p->link); } } } @@ -587,9 +605,11 @@ struct object *o = data; int changed = 0; - pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->id, info->change_mask); + pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->id, info->change_mask); - info = o->info = pw_client_info_update(o->info, info); + info = o->info = pw_client_info_update(o->info, info); + if (info == NULL) + return; if (info->change_mask & PW_CLIENT_CHANGE_MASK_PROPS) changed++; @@ -644,12 +664,14 @@ static void module_event_info(void *data, const struct pw_module_info *info) { - struct object *o = data; + struct object *o = data; int changed = 0; - pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->id, info->change_mask); + pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->id, info->change_mask); - info = o->info = pw_module_info_update(o->info, info); + info = o->info = pw_module_info_update(o->info, info); + if (info == NULL) + return; if (info->change_mask & PW_MODULE_CHANGE_MASK_PROPS) changed++; @@ -704,12 +726,14 @@ static void factory_event_info(void *data, const struct pw_factory_info *info) { - struct object *o = data; + struct object *o = data; int changed = 0; - pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->id, info->change_mask); + pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->id, info->change_mask); - info = o->info = pw_factory_info_update(o->info, info); + info = o->info = pw_factory_info_update(o->info, info); + if (info == NULL) + return; if (info->change_mask & PW_FACTORY_CHANGE_MASK_PROPS) changed++; @@ -765,10 +789,16 @@ { struct object *o = data; uint32_t i, changed = 0; + int res; pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->id, info->change_mask); info = o->info = pw_device_info_update(o->info, info); + if (info == NULL) + return; + + o->params = info->params; + o->n_params = info->n_params; if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS) changed++; @@ -782,12 +812,14 @@ info->paramsi.user = 0; changed++; - clear_params(&o->pending_list, id); + add_param(&o->pending_list, 0, id, NULL); if (!(info->paramsi.flags & SPA_PARAM_INFO_READ)) continue; - pw_device_enum_params((struct pw_device*)o->proxy, - 0, id, 0, -1, NULL); + res = pw_device_enum_params((struct pw_device*)o->proxy, + ++info->paramsi.seq, id, 0, -1, NULL); + if (SPA_RESULT_IS_ASYNC(res)) + info->paramsi.seq = res; } } if (changed) { @@ -801,7 +833,7 @@ const struct spa_pod *param) { struct object *o = data; - add_param(&o->pending_list, id, param); + add_param(&o->pending_list, seq, id, param); } static const struct pw_device_events device_events = { @@ -859,10 +891,16 @@ { struct object *o = data; uint32_t i, changed = 0; + int res; pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->id, info->change_mask); info = o->info = pw_node_info_update(o->info, info); + if (info == NULL) + return; + + o->params = info->params; + o->n_params = info->n_params; if (info->change_mask & PW_NODE_CHANGE_MASK_STATE) changed++; @@ -879,12 +917,14 @@ info->paramsi.user = 0; changed++; - add_param(&o->pending_list, id, NULL); + add_param(&o->pending_list, 0, id, NULL); if (!(info->paramsi.flags & SPA_PARAM_INFO_READ)) continue; - pw_node_enum_params((struct pw_node*)o->proxy, - 0, id, 0, -1, NULL); + res = pw_node_enum_params((struct pw_node*)o->proxy, + ++info->paramsi.seq, id, 0, -1, NULL); + if (SPA_RESULT_IS_ASYNC(res)) + info->paramsi.seq = res; } } if (changed) { @@ -898,7 +938,7 @@ const struct spa_pod *param) { struct object *o = data; - add_param(&o->pending_list, id, param); + add_param(&o->pending_list, seq, id, param); } static const struct pw_node_events node_events = { @@ -948,10 +988,16 @@ { struct object *o = data; uint32_t i, changed = 0; + int res; pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->id, info->change_mask); info = o->info = pw_port_info_update(o->info, info); + if (info == NULL) + return; + + o->params = info->params; + o->n_params = info->n_params; if (info->change_mask & PW_PORT_CHANGE_MASK_PROPS) changed++; @@ -965,12 +1011,14 @@ info->paramsi.user = 0; changed++; - add_param(&o->pending_list, id, NULL); + add_param(&o->pending_list, 0, id, NULL); if (!(info->paramsi.flags & SPA_PARAM_INFO_READ)) continue; - pw_port_enum_params((struct pw_port*)o->proxy, - 0, id, 0, -1, NULL); + res = pw_port_enum_params((struct pw_port*)o->proxy, + ++info->paramsi.seq, id, 0, -1, NULL); + if (SPA_RESULT_IS_ASYNC(res)) + info->paramsi.seq = res; } } if (changed) { @@ -984,7 +1032,7 @@ const struct spa_pod *param) { struct object *o = data; - add_param(&o->pending_list, id, param); + add_param(&o->pending_list, seq, id, param); } static const struct pw_port_events port_events = { @@ -1044,6 +1092,8 @@ pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->id, info->change_mask); info = o->info = pw_link_info_update(o->info, info); + if (info == NULL) + return; if (info->change_mask & PW_LINK_CHANGE_MASK_STATE) changed++; @@ -1241,11 +1291,10 @@ static const struct class *find_class(const char *type, uint32_t version) { - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(classes); i++) { - if (spa_streq(classesi->type, type) && - classesi->version <= version) - return classesi; + SPA_FOR_EACH_ELEMENT_VAR(classes, c) { + if (spa_streq((*c)->type, type) && + (*c)->version <= version) + return *c; } return NULL; } @@ -1455,7 +1504,8 @@ pw_log_debug("sync end %u/%u", d->sync_seq, seq); spa_list_for_each(o, &d->object_list, link) - object_update_params(o); + object_update_params(&o->param_list, &o->pending_list, + o->n_params, o->params); dump_objects(d); if (!d->monitor)
View file
pipewire-0.3.59.tar.gz/src/tools/pw-link.c -> pipewire-0.3.60.tar.gz/src/tools/pw-link.c
Changed
@@ -400,8 +400,16 @@ struct object *port_out = find_node_port(data, out_node, PW_DIRECTION_OUTPUT, port_id); struct object *port_in = find_node_port(data, in_node, PW_DIRECTION_INPUT, port_id); - if (!port_out || !port_in) - break; + if (!port_out && !port_in) { + fprintf(stderr, "Input & output port do not exist\n"); + goto no_port; + } else if (!port_in) { + fprintf(stderr, "Input port does not exist\n"); + goto no_port; + } else if (!port_out) { + fprintf(stderr, "Output port does not exist\n"); + goto no_port; + } pw_properties_setf(data->props, PW_KEY_LINK_OUTPUT_PORT, "%u", port_out->id); pw_properties_setf(data->props, PW_KEY_LINK_INPUT_PORT, "%u", port_in->id); @@ -422,6 +430,9 @@ pw_properties_setf(data->props, PW_KEY_LINK_INPUT_PORT, "%u", in_port); return create_link(data); + +no_port: + return -ENOENT; } static int do_unlink_ports(struct data *data)
View file
pipewire-0.3.59.tar.gz/src/tools/pw-loopback.c -> pipewire-0.3.60.tar.gz/src/tools/pw-loopback.c
Changed
@@ -57,6 +57,7 @@ uint32_t channels; uint32_t latency; + float delay; struct pw_properties *capture_props; struct pw_properties *playback_props; @@ -93,6 +94,7 @@ " -c, --channels Number of channels (default %d)\n" " -m, --channel-map Channel map (default '%s')\n" " -l, --latency Desired latency in ms\n" + " -d, --delay Desired delay in float s\n" " -C --capture Capture source to connect to\n" " --capture-props Capture stream properties\n" " -P --playback Playback sink to connect to\n" @@ -109,7 +111,7 @@ struct data data = { 0 }; struct pw_loop *l; const char *opt_remote = NULL; - char cname256; + char cname256, value256; char *args; size_t size; FILE *f; @@ -121,6 +123,7 @@ { "name", required_argument, NULL, 'n' }, { "channels", required_argument, NULL, 'c' }, { "latency", required_argument, NULL, 'l' }, + { "delay", required_argument, NULL, 'd' }, { "capture", required_argument, NULL, 'C' }, { "playback", required_argument, NULL, 'P' }, { "capture-props", required_argument, NULL, 'i' }, @@ -146,7 +149,7 @@ goto exit; } - while ((c = getopt_long(argc, argv, "hVr:n:g:c:m:l:C:P:i:o:", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "hVr:n:g:c:m:l:d:C:P:i:o:", long_options, NULL)) != -1) { switch (c) { case 'h': show_help(&data, argv0, false); @@ -177,6 +180,9 @@ case 'l': data.latency = atoi(optarg) * DEFAULT_RATE / SPA_MSEC_PER_SEC; break; + case 'd': + data.delay = atof(optarg); + break; case 'C': pw_properties_set(data.capture_props, PW_KEY_NODE_TARGET, optarg); break; @@ -223,6 +229,9 @@ fprintf(f, " remote.name = \"%s\"", opt_remote); if (data.latency != 0) fprintf(f, " node.latency = %u/%u", data.latency, DEFAULT_RATE); + if (data.delay != 0.0f) + fprintf(f, " target.delay.sec = %s", + spa_json_format_float(value, sizeof(value), data.delay)); if (data.channels != 0) fprintf(f, " audio.channels = %u", data.channels); if (data.opt_channel_map != NULL)
View file
pipewire-0.3.59.tar.gz/src/tools/pw-top.c -> pipewire-0.3.60.tar.gz/src/tools/pw-top.c
Changed
@@ -62,8 +62,10 @@ struct node { struct spa_list link; + struct data *data; uint32_t id; char nameMAX_NAME+1; + enum pw_node_state state; struct measurement measurement; struct driver info; struct node *driver; @@ -73,6 +75,7 @@ char formatMAX_FORMAT+1; struct pw_proxy *proxy; struct spa_hook proxy_listener; + unsigned int inactive:1; struct spa_hook object_listener; }; @@ -95,6 +98,7 @@ int n_nodes; struct spa_list node_list; uint32_t generation; + unsigned pending_refresh:1; WINDOW *win; }; @@ -159,12 +163,29 @@ .destroy = on_node_destroy, }; +static void do_refresh(struct data *d); + +static void node_info(void *data, const struct pw_node_info *info) +{ + struct node *n = data; + + if (n->state != info->state) { + n->state = info->state; + do_refresh(n->data); + } +} + static void node_param(void *data, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { struct node *n = data; + if (param == NULL) { + spa_zero(n->format); + goto done; + } + switch (id) { case SPA_PARAM_Format: { @@ -177,17 +198,18 @@ switch(media_subtype) { case SPA_MEDIA_SUBTYPE_raw: { - struct spa_audio_info_raw info; + struct spa_audio_info_raw info = { 0 }; if (spa_format_audio_raw_parse(param, &info) >= 0) { snprintf(n->format, sizeof(n->format), "%6.6s %d %d", - spa_debug_type_find_short_name(spa_type_audio_format, info.format), + spa_debug_type_find_short_name( + spa_type_audio_format, info.format), info.channels, info.rate); } break; } case SPA_MEDIA_SUBTYPE_dsd: { - struct spa_audio_info_dsd info; + struct spa_audio_info_dsd info = { 0 }; if (spa_format_audio_dsd_parse(param, &info) >= 0) { snprintf(n->format, sizeof(n->format), "DSD%d %d ", 8 * info.rate / 44100, info.channels); @@ -195,13 +217,25 @@ } break; } + case SPA_MEDIA_SUBTYPE_iec958: + { + struct spa_audio_info_iec958 info = { 0 }; + if (spa_format_audio_iec958_parse(param, &info) >= 0) { + snprintf(n->format, sizeof(n->format), "IEC958 %s %d", + spa_debug_type_find_short_name( + spa_type_audio_iec958_codec, info.codec), + info.rate); + + } + break; + } } break; case SPA_MEDIA_TYPE_video: switch(media_subtype) { case SPA_MEDIA_SUBTYPE_raw: { - struct spa_video_info_raw info; + struct spa_video_info_raw info = { 0 }; if (spa_format_video_raw_parse(param, &info) >= 0) { snprintf(n->format, sizeof(n->format), "%6.6s %dx%d", spa_debug_type_find_short_name(spa_type_video_format, info.format), @@ -209,6 +243,24 @@ } break; } + case SPA_MEDIA_SUBTYPE_mjpg: + { + struct spa_video_info_mjpg info = { 0 }; + if (spa_format_video_mjpg_parse(param, &info) >= 0) { + snprintf(n->format, sizeof(n->format), "MJPG %dx%d", + info.size.width, info.size.height); + } + break; + } + case SPA_MEDIA_SUBTYPE_h264: + { + struct spa_video_info_h264 info = { 0 }; + if (spa_format_video_h264_parse(param, &info) >= 0) { + snprintf(n->format, sizeof(n->format), "H264 %dx%d", + info.size.width, info.size.height); + } + break; + } } break; case SPA_MEDIA_TYPE_application: @@ -224,10 +276,13 @@ default: break; } +done: + do_refresh(n->data); } static const struct pw_node_events node_events = { PW_VERSION_NODE, + .info = node_info, .param = node_param, }; @@ -242,6 +297,7 @@ strncpy(n->name, name, MAX_NAME); else snprintf(n->name, sizeof(n->name), "%u", id); + n->data = d; n->id = id; n->driver = n; n->proxy = pw_registry_bind(d->registry, id, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0); @@ -258,6 +314,7 @@ } spa_list_append(&d->node_list, &n->link); d->n_nodes++; + d->pending_refresh = true; return n; } @@ -268,6 +325,7 @@ pw_proxy_destroy(n->proxy); spa_list_remove(&n->link); d->n_nodes--; + d->pending_refresh = true; free(n); } @@ -332,7 +390,10 @@ return -ENOENT; n->measurement = m; - n->driver = point->driver; + if (n->driver != point->driver) { + n->driver = point->driver; + d->pending_refresh = true; + } n->generation = d->generation; if (m.status != 3) { n->errors++; @@ -342,9 +403,9 @@ return 0; } -static const char *print_time(char *buf, size_t len, uint64_t val) +static const char *print_time(char *buf, bool active, size_t len, uint64_t val) { - if (val == (uint64_t)-1) + if (val == (uint64_t)-1 || !active) snprintf(buf, len, " --- "); else if (val == (uint64_t)-2) snprintf(buf, len, " +++ "); @@ -357,9 +418,9 @@ return buf; } -static const char *print_perc(char *buf, size_t len, uint64_t val, float quantum) +static const char *print_perc(char *buf, bool active, size_t len, uint64_t val, float quantum) { - if (val == (uint64_t)-1) { + if (val == (uint64_t)-1 || !active) { snprintf(buf, len, " --- "); } else if (val == (uint64_t)-2) { snprintf(buf, len, " +++ "); @@ -370,6 +431,23 @@ return buf; } +static const char *state_as_string(enum pw_node_state state) +{ + switch (state) { + case PW_NODE_STATE_ERROR: + return "E"; + case PW_NODE_STATE_CREATING: + return "C"; + case PW_NODE_STATE_SUSPENDED: + return "S"; + case PW_NODE_STATE_IDLE: + return "I"; + case PW_NODE_STATE_RUNNING: + return "R"; + } + return "!"; +} + static void print_node(struct data *d, struct driver *i, struct node *n, int y) { char buf164; @@ -379,8 +457,13 @@ uint64_t waiting, busy; float quantum; struct spa_fraction frac; + bool active; + + active = n->state == PW_NODE_STATE_RUNNING || n->state == PW_NODE_STATE_IDLE; - if (n->driver == n) + if (!active) + frac = SPA_FRACTION(0, 0); + else if (n->driver == n) frac = SPA_FRACTION((uint32_t)(i->clock.duration * i->clock.rate.num), i->clock.rate.denom); else frac = SPA_FRACTION(n->measurement.latency.num, n->measurement.latency.denom); @@ -405,19 +488,28 @@ busy = -1; mvwprintw(d->win, y, 0, "%s %4.1u %6.1u %6.1u %s %s %s %s %3.1u %16.16s %s%s", - n->measurement.status != 3 ? "!" : " ", + state_as_string(n->state), n->id, frac.num, frac.denom, - print_time(buf1, 64, waiting), - print_time(buf2, 64, busy), - print_perc(buf3, 64, waiting, quantum), - print_perc(buf4, 64, busy, quantum), + print_time(buf1, active, 64, waiting), + print_time(buf2, active, 64, busy), + print_perc(buf3, active, 64, waiting, quantum), + print_perc(buf4, active, 64, busy, quantum), i->xrun_count + n->errors, - n->measurement.status != 3 ? "" : n->format, + active ? n->format : "", n->driver == n ? "" : " + ", n->name); } +static void clear_node(struct node *n) +{ + n->driver = n; + spa_zero(n->measurement); + spa_zero(n->info); + n->errors = 0; + n->last_error_status = 0; +} + static void do_refresh(struct data *d) { struct node *n, *t, *f; @@ -438,14 +530,8 @@ break; spa_list_for_each(f, &d->node_list, link) { - if (d->generation > f->generation + 2) { - f->driver = f; - spa_zero(f->measurement); - spa_zero(f->info); - spa_zero(f->format); - f->errors = 0; - f->last_error_status = 0; - } + if (d->generation > f->generation + 22) + clear_node(f); if (f->driver != n || f == n) continue; @@ -462,6 +548,7 @@ wclrtobot(d->win); wrefresh(d->win); + d->pending_refresh = false; } static void do_timeout(void *data, uint64_t expirations) @@ -507,6 +594,8 @@ if (res < 0) continue; } + if (d->pending_refresh) + do_refresh(d); } static const struct pw_profiler_events profiler_events = { @@ -545,7 +634,8 @@ d->profiler = proxy; pw_proxy_add_object_listener(proxy, &d->profiler_listener, &profiler_events, d); } - + if (d->pending_refresh) + do_refresh(d); return; error_proxy: @@ -559,6 +649,8 @@ struct node *n; if ((n = find_node(d, id)) != NULL) remove_node(d, n); + if (d->pending_refresh) + do_refresh(d); } static const struct pw_registry_events registry_events = { @@ -586,8 +678,9 @@ if (d->profiler == NULL) { pw_log_error("no Profiler Interface found, please load one in the server"); pw_main_loop_quit(d->loop); - } else + } else { do_refresh(d); + } } } @@ -612,7 +705,7 @@ name); } -static void terminal_start() +static void terminal_start(void) { initscr(); cbreak(); @@ -620,7 +713,7 @@ refresh(); } -static void terminal_stop() +static void terminal_stop(void) { endwin(); }
View file
pipewire-0.3.59.tar.gz/test/pwtest.c -> pipewire-0.3.60.tar.gz/test/pwtest.c
Changed
@@ -358,16 +358,13 @@ void pwtest_spa_plugin_destroy(struct pwtest_spa_plugin *plugin) { - void **dll; - struct spa_handle **hnd; - - SPA_FOR_EACH_ELEMENT(plugin->handles, hnd) { + SPA_FOR_EACH_ELEMENT_VAR(plugin->handles, hnd) { if (*hnd) { spa_handle_clear(*hnd); free(*hnd); } } - SPA_FOR_EACH_ELEMENT(plugin->dlls, dll) { + SPA_FOR_EACH_ELEMENT_VAR(plugin->dlls, dll) { if (*dll) dlclose(*dll); }
View file
pipewire-0.3.59.tar.gz/test/test-functional.c -> pipewire-0.3.60.tar.gz/test/test-functional.c
Changed
@@ -29,10 +29,8 @@ PWTEST(openal_info_test) { - int status; - #ifdef OPENAL_INFO_PATH - status = pwtest_spawn(OPENAL_INFO_PATH, (char *){ "openal-info", NULL }); + int status = pwtest_spawn(OPENAL_INFO_PATH, (char *){ "openal-info", NULL }); pwtest_int_eq(WEXITSTATUS(status), 0); return PWTEST_PASS; #else @@ -42,10 +40,8 @@ PWTEST(pactl_test) { - int status; - #ifdef PACTL_PATH - status = pwtest_spawn(PACTL_PATH, (char *){ "pactl", "info", NULL }); + int status = pwtest_spawn(PACTL_PATH, (char *){ "pactl", "info", NULL }); pwtest_int_eq(WEXITSTATUS(status), 0); return PWTEST_PASS; #else
View file
pipewire-0.3.59.tar.gz/test/test-spa-utils.c -> pipewire-0.3.60.tar.gz/test/test-spa-utils.c
Changed
@@ -184,9 +184,8 @@ #define check_traversal(array_) \ { \ - __typeof__(array_0) *it; \ int count = 0; \ - SPA_FOR_EACH_ELEMENT(array_, it) \ + SPA_FOR_EACH_ELEMENT_VAR(array_, it) \ *it = count++; \ for (size_t i = 0; i < SPA_N_ELEMENTS(array_); i++) \ pwtest_int_eq(array_i, i); \
View file
pipewire-0.3.59.tar.gz/test/test-utils.c -> pipewire-0.3.60.tar.gz/test/test-utils.c
Changed
@@ -172,9 +172,7 @@ }, }; - const struct test_case *tc; - - SPA_FOR_EACH_ELEMENT(test_cases, tc) { + SPA_FOR_EACH_ELEMENT_VAR(test_cases, tc) { const char *str = tc->input, *s; const char *state = NULL; size_t j = 0, len;
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
.