Projects
Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 28
View file
pipewire-aptx.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Sat May 20 12:08:17 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> + +- Update to version 0.3.71 + +------------------------------------------------------------------- Sun May 14 10:53:41 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> - Update to version 0.3.70
View file
pipewire-aptx.spec
Changed
@@ -7,7 +7,7 @@ %define soversion 0_2 Name: pipewire-aptx -Version: 0.3.70 +Version: 0.3.71 Release: 0 Summary: PipeWire Bluetooth aptX codec plugin License: MIT
View file
pipewire-0.3.70.tar.gz/NEWS -> pipewire-0.3.71.tar.gz/NEWS
Changed
@@ -1,3 +1,148 @@ +# PipeWire 0.3.71 (2023-05-17) + +This is a bugfix release that is API and ABI compatible with previous +0.3.x releases. + +## Highlights + - A new zero-latency jackdbus bridge was added. This works similar to what + PulseAudio has to offer and creates a sink/source when jackdbus is + started. It is however much more efficient and runs the complete PipeWire + graph as a synchronous JACK client with no added latency. + - Many performance improvements. Activation of remote nodes is more + efficient, fewer eventfds are required on the clients, less callback + overhead in performence critical paths and an optimized poll function + was added. This was mainly driven by the jackdbus module to get the lowest + possible overhead when running the graph. + - The JACK notify callback implementation was reworked to emulate better what + JACK does, improving compatibility with ardour7 and the JACK stress test. + - More work on BAP devices. Device latency is now passed on to + applications also for multi-device headsets, and channel allocation + is handled better. + - Many more improvements and bugfixes. + + +## PipeWire + - Remove the hardcoded limit on io_areas. This is used to link nodes together + and exchange buffers, it was limited to 2048 but now dynamically scales + based on requirements. + - Rate and quantum changes are now applied correctly in more cases. (#3159) + - Updates to client-node to more efficiently process the driver. + - The profiler information was improved to be more accurate. It should + now work better for remote drivers. + - Some potential memory map errors were fixed in the protocol because in some + case with large messages, some fds were closed too soon. + - pw-filter now implements the pw_filter_set_active() method. + - A potential out-of-buffers case was fixed in capture pw-streams where buffers + were not moved to the recycle queue when the node suspended. + - Nodes are now always woken up with the eventfd. Previously there were + some optimiztions in the server to directly call into the node process + function but that optimization is not necessary. Without this optimization + it is now possible to run nodes in different threads. + - pw-stream trigger is now implemented correctly in all cases. + - Remote nodes now use one eventfd less because they get triggered with the + node eventfd directly. + - Monitor ports are now ignored in latency updates. + - A potential race when reporting an error to a client was fixed. (#3192) + - Fix a bug where always_process nodes would sometimes IDLE. (#3189) + - Optimize peer activation. Nodes are now activated more efficiently and + independent of the number of links. It also reduces the number of eventfds + and memory in remote clients. + - A bug in property serialization was fixed. Values with spaces would only + serialize the first part of the value. + +## Modules + - Correctly handle the echo-canceler plugin init method fallback. The + samplerate was not correctly configured. This is only a regression for people + that have external echo-canceler plugins. + - RAOP sink now only sets the volume on the remote end when the stream is + recording. (#3175) + - RAOP discover now tries to deduplicate entries from the same host. + - A new zero-latency jackdbus bridge was added. This works similar to what + pulseaudio has to offer and creates a sink/source when jackdbus is + started. It is however much more efficient and runs the complete PipeWire + graph as a synchronous JACK client. + - The access module uses a more secure way to check the application + executable. + - module-combine-stream now has configurable delay and latency for each + stream. This can be used to align sinks/sources with different latencies. + - A potential crash in module-pulse-tunnel was fixed when shutting down. + (#3199) + - Module-rt will now clamp the nice value to the min allowed value to avoid + errors from rtkit. (#3186) + - Fix a bug with the session counters in module-rtp-sap. Also use the right + format for L24. Improve the AES67 example config. + - Improve some warning and info messages in module-rt. (#3194) + - module-rtp-session should now do something when started without arguments. + - A potential crash in module-rtp-session was fixed. (#3217) + - module-filter-chain has better error reporting when a convolver fails to + load. (#3223) + +## SPA + - Move some things around to avoid compiler warnings. (#3171) + - Increase mixer ports. Reorganize some things and bump mixer input ports + from 128 to 512. + - Fix a potential crash when a node is scheduled before it completes + the setup. + - The JACK sink and source SPA plugins have seen some improvements. + - Allow the peaks resampler still if we disabled resampling. + - Perform more cleanup in audioadapter when in error. + - An optimized non-cancellable loop implementation was added. + - Callbacks were optimized with a _fast() varsion that doesn't check the + version and method. When this check is performed earlier, it can + be skipped in performance critical places. + - Some of the callbacks and system methods are now using the fast function + calls in critical paths. + - A potential division by zero was fixed in the ALSA plugins. + - Improve rate and quantum when starting audioconvert. + - Make it possible to override node.driver in the SPA null-audio-driver. + (#3220) + +## pulse-server + - The audio info parameter parsing was refactored and improved. + - Fix some races with clients exiting when playing samples. + - An option was added to change or disable the dbus name registration. + (#2987) + +## Bluetooth + - Implement battery reporting using AT+XEVENT. + - Disable hardware volume for 3M WorkTunes. + - Implement BAP audio locations (channel positions) by using the new + bluez properties. + +## JACK + - Fix some errors reported by JACK test.cpp. (#2638) + - Add jack.show-midi option to show/hide midi ports. + - Add jack.max-client-ports option. JACK also has a port limit and so + PipeWire needs it as well to make the tests happy. + - Call the shutdown callback only when the server stopped, not when there + is a random error. (#3070) + - Avoid registering the same port name twice. + - Call port registration callbacks in activate/deactivate. + - Improve jack_port_connected(). + - Improve some error reporting. + - The JACK headers were updated to a newer version. + - JACK callbacks are now managed with an event queue to simulate + more what JACK does. This avoids emiting callbacks when a method is blocking + for a reply and causing deadlocks. (#3183) + - Assign unique names to JACK clients. (#2833) + - Fix a potential crash when the thread_utils was used after free. + - Aliases are now not filled in by default to improve JACK compatibility. + (#3154) + +# ALSA + - The ALSA plugin will now wait for negotiation to complete or an error + before _prepare() completes. This makes more applications deal correctly + with the potential errors. + +# Docs + - A new document about how scheduling is implemented was added. + - Update the pw-cli man page. (#2988) + - Document the SPA Pod serialization. + - Document the PipeWire native protocol. + + +Older versions: + # PipeWire 0.3.70 (2023-04-20) This is a quick bugfix release that is API and ABI compatible with previous @@ -62,9 +207,6 @@ - The GStreamer source now uses the BaseSrc clocking code to implement the clock and timing code. - -Older versions: - # PipeWire 0.3.69 (2023-04-13) This is a quick bugfix release that is API and ABI compatible with previous
View file
pipewire-0.3.70.tar.gz/doc/meson.build -> pipewire-0.3.71.tar.gz/doc/meson.build
Changed
@@ -30,6 +30,8 @@ 'pipewire-session-manager.dox', 'pipewire-objects-design.dox', 'pipewire-audio.dox', + 'pipewire-scheduling.dox', + 'pipewire-protocol.dox', 'tutorial.dox', 'tutorial1.dox', 'tutorial2.dox',
View file
pipewire-0.3.70.tar.gz/doc/pipewire-modules.dox -> pipewire-0.3.71.tar.gz/doc/pipewire-modules.dox
Changed
@@ -60,6 +60,8 @@ - \subpage page_module_example_source - \subpage page_module_fallback_sink - \subpage page_module_filter_chain +- \subpage page_module_jackdbus_detect +- \subpage page_module_jack_tunnel - \subpage page_module_link_factory - \subpage page_module_loopback - \subpage page_module_metadata
View file
pipewire-0.3.71.tar.gz/doc/pipewire-protocol.dox
Added
@@ -0,0 +1,1549 @@ +/** \page page_native_protocol Native Protocol + +PipeWire has a pluggable client/server IPC protocol. + +The reference implementation uses unix sockets and is implemented in +\ref page_module_protocol_native. + +We document the messages here. + +# Message header + +Each message on the unix socket contains a 16 bytes header and a +variable length payload size: + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Id | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | opcode | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | seq | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | n_fds | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | payload POD | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | optional footer POD | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + + +These are four uint32 words to be read in native endianness. + +- Id: the message id this is the destination resource/proxy id +- opcode: the opcode on the resource/proxy interface +- size: the size of the payload and optional footer of the message +- seq: an increasing sequence number for each message +- n_fds: number of file descriptors in this message. + +The sender should send along with each message the fds that belong to +the message. If there are more than the maximum number of fds in the +message than can fit in one message, the message is split into multiple +parts. + +The payload is a single POD see \ref page_spa_pod for details. + +After the payload, there is an optional footer POD object. + +# Making a connection + +First a connection is made to a unix domain socket. By default, the socket is +named as "pipewire-0" and searched in the following directories: + + - getenv("PIPEWIRE_RUNTIME_DIR") + - getenv("XDG_RUNTIME_DIR") + - getenv("USERPROFILE") + + +The client opens the socket and allocates a Core proxy with id 0 and a Client +proxy with id 1. + +The server will allocate a new Core resource with id 0. + +The client sends the Core::Hello message on the socket to start the +communication. + +The server binds the client resource with id 1 to the client. + +The client then sends client properties to the server. + +``` + client server + |---------------------------------------->| + | open socket | + | | + |---------------------------------------->| + | Core::Hello(version) | + | | + |---------------------------------------->| + | Client::UpdateProperties | + . . +``` + +This completes the setup of the client. The newly connected client will +appear in the registry at this point. + +# Core proxy/resource + +The core is always the object with Id 0. + +## Core Methods (Id 0) + +### Core::Hello (Opcode 1) + +The first message sent by a client is the Hello message and contains the +version number of the client. + +``` + Struct( + Int: version + ) +``` + +The version is 3. + +### Core::Sync (Opcode 2) + +The Sync message will result in a Done event from the server. When the Done +event is received, the client can be sure that all operations before the Sync +method have been completed. + +``` + Struct( + Int: id + Int: seq + ) +``` + +- id: the id will be returned in the Done event. +- seq: is usually generated automatically and will be returned in the Done event. + +### Core::Pong (Opcode 3) + +Is sent from the client to the server when the server emits the Ping event. +The id and seq should be copied from the Ping event. + +``` + Struct( + Int: id + Int: seq + ) +``` + +### Core::Error (Opcode 4) + +An error occured in an object on the client. + +``` + Struct( + Int: id + Int: seq + Int: res + String: message + ) +``` + +- id: The id of the proxy that is in error. +- seq: a seq number from the failing request (if any) +- res: a negative errno style error code +- message: an error message + +### Core::GetRegistry (Opcode 5) + +A client requests to bind to the registry object and list the available objects +on the server. + +Like with all bindings, first the client allocates a new proxy id and puts this +as the new_id field. Methods and Events can then be sent and received on the +new_id (in the message Id field). + +``` + Struct( + Int: version + Int: new_id + ) +``` +- version: the version of the registry interface used on the client +- new_id: the id of the new proxy with the registry interface + +After this method, the server will start sending Registry::Global events +to the proxy with new_id. + +``` + client server + | | + | new proxy(new_id) | + |---------------------------------------->| new resource(new_id) + | Core::GetRegistry(version) | + | | + |<----------------------------------------| new_id + | Registry::Global() | + |<----------------------------------------| + | Registry::Global() | + . . +``` + +There is no explicit last Global event to signal that the last object +has been received. The usual way of knowing this is to send a Core::Sync +method right after the Core::GetRegistry method and to wait for the +Core::Done event. + +``` + client server + | | + | new proxy(new_id) | + |---------------------------------------->| new resource(new_id) + | Core::GetRegistry(version) | + |---------------------------------------->| + | seq = Core::Sync | + | | + |<----------------------------------------| new_id + | Registry::Global() | + |<----------------------------------------| + | Registry::Global() | + . . + |<----------------------------------------| + | Core::Done(seq) | + . . +``` + +### Core::CreateObject (Opcode 6) + +Create a new object from a factory of a certain type. + +The client allocates a new_id for the proxy. The server will allocate a new +resource with the same new_id and from then on, Methods and Events will be +exchanged between the new object of the given type. + +``` + Struct( + String: factory_name + String: type + Int: version + Struct( + Int: n_items + (String: key + String: value)* + ): props + Int: new_id + ) +``` + +- factory_name: the name of a server factory object to use +- type: the type of the object to create, this is also the type of the + interface of the new_id proxy. +- props: extra properties to create the object +- new_id: the proxy id of the new object + +### Core::Destroy (Opcode 7) + +Destroy an object. + +``` + Struct( + Int: id + ) +``` + +- id: the proxy id of the object to destroy. + +## Core Events + +Core events are emited by the server. + +### Core::Info (Opcode 0) + +Emited by the server upon connection with the more information about the +server. + +``` + Struct( + Int: id + Int: cookie + String: user_name + String: host_name + String: version + String: name + Long: change_mask + Struct( + Int: n_items + (String: key + String: value)* + ): props + ) +``` + +- id: the id of the server (0) +- cookie: a unique cookie for this server +- user_name: the name of the user running the server +- host_name: the name of the host running the server +- version: a version string of the server +- name: the name of the server +- change_mask: a set of bits with changes to the info +- props: optional key/value properties, valid when change_mask has (1<<0) + +### Core::Done (Opcode 1) + +Emited as a result of a client Sync method. + +``` + Struct( + Int: id + Int: seq + ) +``` + +id and seq are passed from the client Sync request. + +### Core::Ping (Opcode 2) + +Emited by the server when it wants to check if a client is alive or ensure +that it has processed the previous events. + +``` + Struct( + Int: id + Int: seq + ) +``` + +- id: the object id to ping +- seq: usually automatically generated. The client should pass this in the Pong + method reply. + +### Core::Error (Opcode 3) + +The error event is sent out when a fatal (non-recoverable) +error has occurred. The id argument is the proxy object where +the error occurred, most often in response to a request to that +object. The message is a brief description of the error, +for (debugging) convenience. + +``` + Struct( + Int: id + Int: seq + Int: res + String: message + ) +``` + +- id: The id of the resource that is in error. +- seq: a seq number from the failing request (if any) +- res: a negative errno style error code +- message: an error message + + +### Core::RemoveId (Opcode 4) + +This event is used internally by the object ID management +logic. When a client deletes an object, the server will send +this event to acknowledge that it has seen the delete request. +When the client receives this event, it will know that it can +safely reuse the object ID. + +``` + Struct( + Int: id + ) +``` + +- id: a proxy id that was removed + +### Core::BoundId (Opcode 5) + +This event is emitted when a local object ID is bound to a +global ID. It is emitted before the global becomes visible in the +registry. This event is deprecated, the BoundProps event should +be used because it also contains extra properties. + +``` + Struct( + Int: id + Int: global_id + ) +``` + +- id: a proxy id +- global_id: the global_id as it will appear in the registry. + +### Core::AddMem (Opcode 6) + +Memory is given to a client as fd of a certain memory type. +Further references to this fd will be made with the per memory +unique identifier id. + +``` + Struct( + Int: id + Id: type + Fd: fd + Int: flags + ) +``` + +- id: a server allocated id for this memory +- type: the memory type, see enum spa_data_type +- fd: the index of the fd sent with this message +- flags: extra flags + +### Core::RemoveMem (Opcode 7) + +Remove memory for a client with the given id + +``` + Struct( + Int: id + ) +``` + +- id: the id of the memory to remove. This is the Id from AddMem. + +### Core::BoundProps (Opcode 8) + +This event is emitted when a local object ID is bound to a +global ID. It is emitted before the global becomes visible in the +registry. + +``` + Struct( + Int: id + Int: global_id + Struct( + Int: n_items + (String: key + String: value)* + ): props + ) +``` + +- id: a proxy id +- global_id: the global_id as it will appear in the registry. +- props: the properties of the global + +# Registry proxy/resource + +The registry is obtained with the GetRegistry method on the Core object. +The Id depends on the new_id that was provided. + +## Registry Methods + +### Registry::Bind (Opcode 1) + +Bind to the global object with id and use the client proxy +with new_id as the proxy. After this call, methods can be +send to the remote global object and events can be received. + +``` + Struct( + Int: id + String: type + Int: version + Int: new_id + ) +``` + +- id: the global_id to bind to +- type: the type of the global id +- version: the client version of the interface for type +- new_id: the client proxy id for the global object + +### Registry::Destroy (Opcode 2) + +Try to destroy the global object with id. This might fail when the +client does not have permission. + +``` + Struct( + Int: id + ) +``` + +- id: the global id to destroy. + +## Registry Events + +### Registry::Global (Opcode 0) + +Notify a client about a new global object. + +``` + Struct( + Int: id + Int: permissions + String: type + Int: version + Struct( + Int: n_items + (String: key + String: value)* + ): props + ) +``` + +- id: the global id +- permissions: permission bits +- type: the type of object +- version: the server version of the object +- props: extra global properties + +### Registry::GlobalRemove (Opcode 1) + +A global with id was removed + +``` + Struct( + Int: id + ) +``` + +- id: the global id that was removed. + +# PipeWire:Interface:Client + +The client object represents a client connect to the PipeWire server. +Permissions of the client can be managed. + +The currently connected client always has the Client object with +proxy id 1. + +## Client Methods + +### Client::Error (Opcode 1) + +Is used to send an error to a client. + +``` + Struct( + Int: id + Int: res + String: error + ) +``` + +- id: a client proxy id to send the error to +- res: a negative errno style error code +- error: an error message + +### Client::UpdateProperties (Opcode 2) + +Is used to update the properties of a client. + +``` + Struct( + Struct( + Int: n_items + (String: key + String: value)* + ): props + ) +``` +- props: properties to update on the client. + +### Client::GetPermissions (Opcode 3) + +Get the currently configured permissions on the client. + +``` + Struct( + Int: index + Int: num + ) +``` +- index: the start index of the permissions to get +- num: the number of permissions to get + +This method will result in at most num Permission Events. + +### Client::UpdatePermissions (Opcode 4) + +Update the permissions of the global objects using the +provided array with permissions + + +``` + Struct( + Int: n_permissions + ( Int: id + Int: permission + )* + ) +``` + +- n_permissions: number of permissions +- id: the global id +- permissions: the permissions for the global id + +## Client Events + +### Client::Info (Opcode 0) + +Get client information updates. This is emitted when binding to a client or +when the client info is updated later. + +``` + Struct( + Int: id + Long: change_mask + Struct( + Int: n_items + (String: key + String: value)* + ): props + ) +``` +- id: the global id of the client +- change_mask: the changes emitted by this event +- props: properties of this object, valid when change_mask has (1<<0) + +### Client::Permissions (Opcode 1) + +Emitted as the reply of the GetPermissions method. + +``` + Struct( + Int: index + Struct( + Int: n_permissions + (Int: id + Int: permission)* + ) + ) +``` +- index: index of the first permission +- n_permissions: the number of permission entries +- id: the global id of the object +- permissions: the permission for the given id + +# PipeWire:Interface:Device + +A device is an object that manages other devices or nodes. + +The usual flow is to bind to the Device object. This will result in an +Info event. From the Info event one can find the available params to +enumerate. + +## Device methods + +### Device::SubscribeParams (Opcode 1) + +Automatically emit Param events for the given ids when they are changed. + +``` + Struct( + ArrayId: ids + ) +``` +- ids: and array of param Id to subscribe to + +### Device::EnumParams (Opcode 2) + +Enumerate the values of a param. This will result in Param events. + +``` + Struct( + Int: seq + Id: id + Int: index + Int: num + Pod: filter + ) +``` +- seq: an automatically generated sequence number, will be copied into the reply +- id: the param id to enumerate. +- index: the first param index to retrieve +- num: the number of params to retrieve +- filter: an optional filter object for the param. + +### Device::SetParam (Opcode 3) + +Set a parameter on the Device. + +``` + Struct( + Id: id + Int: flags + Pod: param + ) +``` +- id: the param id to set. +- flags: extra flags +- param: the param object to set + +## Device events + +### Device::Info (Opcode 0) + +The info event is emitted when binding or when the device information changed. + +``` + Struct( + Int: id + Long: change_mask + Struct( + Int: n_items + ( String: key + String: value )* + ): props + Struct( + Int: n_params + ( Int: id + Int: flags )* + ): param_info + ) +``` + +- id: the id of the global +- change_mask: a bitmask of changed fields +- props: extra properties, valid when change_mask is (1<<0) +- param_info: info about the parameters, valid when change_mask is (1<<1) + For each parameter, the id and current flags are given. + - param_info.id : see enum spa_param_type + - param_info.flags: struct spa_param_info.flags + +### Device::Param (Opcode 1) + +Emitted as a result of EnumParams or SubscribeParams. + +``` + Struct( + Int: seq + Id: id + Int: index + Int: next + Pod: param + ) +``` +- seq: the sequence number send by the client EnumParams or server generated + in the SubscribeParams case. +- id: the param id that is reported, see enum spa_param_type +- index: the index of the parameter +- next: the index of the next parameter +- param: the parameter. The object type depends on the id + + +# PipeWire:Interface:Factory + +A factory is an object that allows one to create new objects. + +## Factory methods + +A factory has no methods + +## Factory events + +### Factory::Info (Opcode 0) + +Info is emitted when binding to the factory global or when the information changed. + +``` + Struct( + Int: id + String: name + String: type + Int: version + Long: change_mask + Struct( + Int: n_items + ( String: key + String: value )* + ): props + ) +``` +- id: the global id of the factory +- name: the name of the factory. This can be used as the name for Core::CreateObject +- type: the object type produced by this factory +- version: the version of the object interface +- change_mask: bitfield of changed values. +- props: optional properties of the factory, valid when change_mask is (1<<0) + + +# PipeWire:Interface:Link + +A link is a connection between 2 ports. + +## Link methods + +A link has no methods + +## Link events + +### Link::Info (Opcode 0) + +Info is emitted when binding to the link global or when the information changed. + +``` + Struct( + Int: id + Int: output_node_id + Int: output_port_id + Int: input_node_id + Int: input_port_id + Long: change_mask + Int: state + String: error + Pod: format + Struct( + Int: n_items + ( String: key + String: value )* + ): props + ) +``` + +- id: the global id of the link +- output_node_id: the global id of the output node +- output_port_id: the global id of the output port +- input_node_id: the global id of the input node +- input_port_id: the global id of the input port +- change_mask: bitfield of changed values. +- state: the state of the link, valid when change_mask has (1<<0) + - see enum pw_link_state for values +- error: an optional error string +- format: an optional format for the link, valid when change_mask has (1<<1) +- props: optional properties of the link, valid when change_mask is (1<<2) + + +# PipeWire:Interface:Module + +A Module provides dynamically loaded functionality + +## Module methods + +A module has no methods + +## Module events + +### Module::Info (Opcode 0) + +Info is emitted when binding to the module global or when the information changed. + +``` + Struct( + Int: id + String: name + String: filename + String: args + Long: change_mask + Struct( + Int: n_items + ( String: key + String: value )* + ): props + ) +``` + +- id: the global id of the module +- name: the name of the module +- filename: the filename of the module +- args: arguments passed when loading the module +- change_mask: bitfield of changed values. +- props: optional properties of the module, valid when change_mask has (1<<0) + + +# PipeWire:Interface:Node + +A Node is a processing element in the graph + +## Node methods + +### Node::SubscribeParams (Opcode 1) + +Automatically emit Param events for the given ids when they are changed. + +``` + Struct( + ArrayId: ids + ) +``` +- ids: and array of param Id to subscribe to + +### Node::EnumParams (Opcode 2) + +Enumerate the values of a param. This will result in Param events. + +``` + Struct( + Int: seq + Id: id + Int: index + Int: num + Pod: filter + ) +``` +- seq: an automatically generated sequence number, will be copied into the reply +- id: the param id to enumerate. +- index: the first param index to retrieve +- num: the number of params to retrieve +- filter: an optional filter object for the param. + +### Node::SetParam (Opcode 3) + +Set a parameter on the Node. + +``` + Struct( + Id: id + Int: flags + Pod: param + ) +``` +- id: the param id to set. +- flags: extra flags +- param: the param object to set + +### Node::SendCommand (Opcode 4) + +Send a Command to the node. + +``` + Struct( + Pod: command + ) +``` +- command: the command to send. See enum spa_node_command + +## Node events + +### Node::Info (Opcode 0) + +The info event is emitted when binding or when the node information changed. + +``` + Struct( + Int: id + Int: max_input_ports + Int: max_output_ports + Long: change_mask + Int: n_input_ports + Int: n_output_ports + Id: state + String: error + Struct( + Int: n_items + ( String: key + String: value )* + ): props + Struct( + Int: n_params + ( Int: id + Int: flags )* + ): param_info + ) +``` + +- id: the id of the node global +- max_input_port: the maximum input ports for the node +- max_output_port: the maximum output ports for the node +- change_mask: a bitmask of changed fields +- n_input_port: the number of input ports, when change_mask has (1<<0) +- n_output_port: the number of output ports, when change_mask has (1<<1) +- state: the current node state, when change_mask has (1<<2) + - See enum pw_node_state for values +- error: an error message. +- props: extra properties, valid when change_mask is (1<<3) +- param_info: info about the parameters, valid when change_mask is (1<<4) + For each parameter, the id and current flags are given. + - param_info.id : see enum spa_param_type + - param_info.flags: struct spa_param_info.flags + +### Node::Param (Opcode 1) + +Emitted as a result of EnumParams or SubscribeParams. + +``` + Struct( + Int: seq + Id: id + Int: index + Int: next + Pod: param + ) +``` +- seq: the sequence number send by the client EnumParams or server generated + in the SubscribeParams case. +- id: the param id that is reported, see enum spa_param_type +- index: the index of the parameter +- next: the index of the next parameter +- param: the parameter. The object type depends on the id + + +# PipeWire:Interface:Port + +A port is part of a node and allows links with other ports. + +## Port methods + +### Port::SubscribeParams (Opcode 1) + +Automatically emit Param events for the given ids when they are changed. + +``` + Struct( + ArrayId: ids + ) +``` +- ids: and array of param Id to subscribe to + +### Port::EnumParams (Opcode 2) + +Enumerate the values of a param. This will result in Param events. + +``` + Struct( + Int: seq + Id: id + Int: index + Int: num + Pod: filter + ) +``` +- seq: an automatically generated sequence number, will be copied into the reply +- id: the param id to enumerate. +- index: the first param index to retrieve +- num: the number of params to retrieve +- filter: an optional filter object for the param. + +## Port events + +### Port::Info (Opcode 0) + +The info event is emitted when binding or when the port information changed. + +``` + Struct( + Int: id + Int: direction + Long: change_mask + Struct( + Int: n_items + ( String: key + String: value )* + ): props + Struct( + Int: n_params + ( Int: id + Int: flags )* + ): param_info + ) +``` + +- id: the id of the port global +- direction: the direction of the port, see enum pw_direction +- change_mask: a bitmask of changed fields +- props: extra properties, valid when change_mask is (1<<0) +- param_info: info about the parameters, valid when change_mask is (1<<1) + For each parameter, the id and current flags are given. + - param_info.id : see enum spa_param_type + - param_info.flags: struct spa_param_info.flags + +### Port::Param (Opcode 1) + +Emitted as a result of EnumParams or SubscribeParams. + +``` + Struct( + Int: seq + Id: id + Int: index + Int: next + Pod: param + ) +``` +- seq: the sequence number send by the client EnumParams or server generated + in the SubscribeParams case. +- id: the param id that is reported, see enum spa_param_type +- index: the index of the parameter +- next: the index of the next parameter + + +# PipeWire:Interface:ClientNode + +The ClientNode object is created from the `client-node` factory that is provided +by the `libpipewire-module-client-node` module. + +It creates a server Node that can be controlled from a client. Processing will happen +in the client. It is used by pw-stream and pw-filter to implement the PipeWire media +processing nodes. + +## ClientNode methods + +### ClientNode::GetNode (Opcode 1) + +Get the node object associated with the client-node. This binds to the server side +Node object. + +``` + Struct( + Int: version + Int: new_id + ) +``` +- version: the Node version to bind as +- new_id: the proxy id + +### ClientNode::Update (Opcode 2) + +Update the params and info of the node. + +``` + Struct( + Int: change_mask + Int: n_params + ( Pod: param )* + Struct( + Int: max_input_ports + Int: max_output_ports + Long: change_mask + Long: flags + Int: n_items + ( String: key + String: value )* + Int: n_params + ( Id: id + Int: flags )* + ): info + ) +``` +- change_mask: a bitfield of changed items +- n_params: number of update params, valid when change_mask has (1<<0) +- param: an updated param +- info: an updated `struct spa_node_info`, valid when change_mask has (1<<1) + - max_input_ports: maximum input ports + - max_output_ports: maximum output ports + - change_mask: bitmask of changed items + - flags: flags, see `struct spa_node_info`, when change_mask has (1<<0) + - n_items: updated properties, valid when info.change_mask has (1<<1) + - n_params: updated `struct spa_param_info`, valid when info.change_mask has (1<<2) + + +### ClientNode::PortUpdate (Opcode 3) + +Create, Update or destroy a node port. + +When the port is not known on the server, the port is created. +When info is None, the port is destroyed. +Otherwise, the port information is updated. + +``` + Struct( + Int: direction + Int: port_id + Int: change_mask + Int: n_params + ( Pod: param )* + Struct( + Long: change_mask + Long: flags + Int: rate_num + Int: rate_denom + Int: n_items + ( String: key + String: value )* + Int: n_params + ( Id: id + Int: flags )* + ): info + ) +``` +- direction: the port direction +- port_id: the port id +- change_mask: a bitfield of changed items +- n_params: number of updated params, valid when change_mask has (1<<0) +- param: n_params updated params +- info: an updated `struct spa_port_info`, valid when change_mask has (1<<1) + - change_mask: bitmask of changed items + - flags: flags, see `struct spa_port_info`, when change_mask has (1<<0) + - rate_num: updated rate numerator + - rate_denom: updated rate denominator, when info.change_mask has (1<<1) + - n_items: updated properties, valid when info.change_mask has (1<<2) + - n_params: updated `struct spa_param_info`, valid when info.change_mask has (1<<3) + +### ClientNode::SetActive (Opcode 4) + +Set the node active or inactive. + +``` + Struct( + Bool: active + ) +``` +- active: the new state of the node + +### ClientNode::Event (Opcode 5) + +Emit an event on the node. + +``` + Struct( + Pod: event + ) +``` +- event: the event to emit. See `enum spa_node_event`. + +### ClientNode::PortBuffers (Opcode 6) + +This method is used by the client when it has allocated buffers for a port. +It is usually called right after the UseBuffers event to let the server know +about the the newly allocated buffer memory. + +``` + Struct( + Int: direction + Int: port_id + Int: mix_id + Int: n_buffers + ( Int: n_datas + ( Id: type + Fd: memfd + Int: flags + Int: mapoffset + Int: maxsize + )* + )* + ) +``` +- direction: the direction of the port +- port_id: the port id +- mix_id: the mix id of the port +- n_buffer: the number of buffers +- n_data: for each buffer the number of data planes +- type: the plane memory type +- memfd: the plane memfd +- mapoffset: the start offset of where the buffer memory starts +- maxsize: the maxiumum size of the memory. + +## ClientNode events + +### ClientNode::Transport (Opcode 0) + +The server will allocate the activation record and eventfd for the node and +transfer this to the client with the Transport event. + +``` + Struct( + Fd: readfd + Fd: writefd + Int: memfd + Int: offset + Int: size + ) +``` +- readfd: the eventfd to start processing +- writefd: the eventfd to signal driver start +- memfd: the index of the memfd of the activation record +- offset: the offset in memfd of the start of the activation record +- size: the size of the activation record + +The activation record is currently an internal data structure that is +not yet ABI stable. + + +### ClientNode::SetParam (Opcode 1) + +Set a parameter on the Node. + +``` + Struct( + Id: id + Int: flags + Pod: param + ) +``` +- id: the param id to set. +- flags: extra flags +- param: the param object to set + + +### ClientNode::SetIO (Opcode 2) + +Set an IO area on the node. + +``` + Struct( + Id: id + Int: memid + Int: offset + Int: size + ) +``` +- id: the io area id to set. +- the memid to use, this is signaled with Core::AddMem +- offset: the start offset in the memory area +- the size of the io area + +### ClientNode::Event (Opcode 3) + +Emit an event on the node. + +``` + Struct( + Pod: event + ) +``` +- event: the event to emit. See `enum spa_node_event`. + +### ClientNode::Command (Opcode 4) + +Send a command on the node. + +``` + Struct( + Pod: command + ) +``` +- command: the command to send. See `enum spa_node_command`. + +### ClientNode::AddPort (Opcode 5) + +Add a new port to the node + +``` + Struct( + Int: direction + Int: port_id + Struct( + Int: n_items + ( String: key + String: value )* + ): props + ) +``` +- direction: the direction of the new port +- port_id: the port id of the new port +- props: optional extra properties for the port + +### ClientNode::RemovePort (Opcode 6) + +Remove a port from the node + +``` + Struct( + Int: direction + Int: port_id + ) +``` +- direction: the direction of the port to remove +- port_id: the port id of the port to remove + + +### ClientNode::PortSetParam (Opcode 7) + +Set a parameter on the Port of the node. + +``` + Struct( + Int: direction + Int: port_id + Id: id + Int: flags + Pod: param + ) +``` +- direction: the direction of the port +- port_id: the port id of the port +- id: the param id to set. +- flags: extra flags +- param: the param object to set + +### ClientNode::UseBuffers (Opcode 8) + +Use a set of buffers on the mixer port + +``` + Struct( + Int: direction + Int: port_id + Int: mix_id + Int: flags + Int: n_buffers + ( Int: memid + Int: offset + Int: size + Int: n_metas + ( Id: type + Int: size + )*: meta + Int: n_datas + ( Id: type + Int: data + Int: flags + Int: mapoffset + Int: maxsize + )*: data + )*: buffer + ) +``` +- direction: the direction of the port +- port_id: the port id of the port +- mix_id: the mixer id of the port +- flags: extra flags +- id: the param id to set. +- flags: extra flags + - SPA_NODE_BUFFERS_FLAG_ALLOC (1<<0) to let the client allocate + buffers, in which case the PortBuffers method needs to be called + with the allocated buffer memory. +- n_buffers: the number of buffers +- memid: the memory id of the buffer metadata and or data +- offset: the offset in memid of the buffer +- size: the size of the buffer metadata or data +- n_metas: number of metadata. The buffer memory first contains this + number of metadata parts of the given type and size + - type: metadata type + - size: metadata size, round up with 8 to get to the next metadata +- n_data: the number of datablocks (planes) per buffer + - type: the data type, this can be: + - SPA_DATA_MemId to reference a memfd from Core:AddMem + - SPA_DATA_MemPtr to reference this buffer memid + - data: contains the memid or offset in the memid + - flags: extra flags for the data + - mapoffset: the offset in memfd + - maxsize: the maxsize of the memory in memfd + +### ClientNode::PortSetIO (Opcode 9) + +Set an IO area on a mixer port. + +``` + Struct( + Int: direction + Int: port_id + Int: mix_id + Id: id + Int: memid + Int: offset + Int: size + ) +``` +- direction: the direction of the port +- port_id: the port id of the port +- mix_id: the mix id of the port +- id: the IO area to set. See enum spa_io_type +- memid: the memid of the io area, added with Core::AddMem +- offset: the offset in the memid +- size: the size of the IO area + + +### ClientNode::SetActivation (Opcode 10) + +Notify the client of the activation record of a peer node. This activation record +should be triggered when this node finishes processing. + +``` + Struct( + Int: node_id + Fd: signalfd + Int: memid + Int: offset + Int: size + ) +``` +- node_id: the node_id of the peer node +- signalfd: the eventfd of the peer node +- memid: the memid of the activation record of the peer from Core:AddMem +- offset: the offset in memid +- size: the size of the activation record + +### ClientNode::PortSetMixInfo (Opcode 11) + +Notify the node of the peer of a mixer port. This can be used to track the peer +ports of a node. + +``` + Struct( + Int: direction + Int: port_id + Int: mix_id + Int: peer_id + Struct( + Int: n_items + ( String: key + String: value )* + ): props + ) +``` +- direction: the direction of the port +- port_id: the port id of the port +- mix_id: the mix id of the port +- peer_id: the id of the peer port +- props: optional properties + +# PipeWire:Interface:Metadata + +Metadata is a shared database of settings and properties. + +## Metadata methods + +### Metadata::SetProperty (Opcode 1) + +Set a property in the metadata store. + +``` + Struct( + Int: subject + String: key + String: type + String: value + ) +``` +- subject: the id of the object, this needs to be a valid global_id +- key: a key +- type: an optional type +- value: a value + + +### Metadata::Clear (Opcode 2) + +Clear the metadata store. + +``` + Struct( + None + ) +``` + +## Metadata events + +### Metadata::Property (Opcode 0) + +A metadata key changed. This is also emitted when binding to the metadata. + +``` + Struct( + Int: subject + String: key + String: type + String: value + ) +``` +- subject: the id of the object, this is a valid global_id +- key: a key +- type: an optional type +- value: a value + +# PipeWire:Interface:Profiler + +The profiler object allows one to receive profiler information of the pipewire +graph. + +## Profiler methods + +The profiler has no methods + +## Profiler events + +### Profiler::Profile (Opcode 0) + +``` + Struct( + Pod: object + ) +``` +- object: a SPA_TYPE_OBJECT_Profiler object. See enum spa_profiler + +*/
View file
pipewire-0.3.71.tar.gz/doc/pipewire-scheduling.dox
Added
@@ -0,0 +1,191 @@ +/** \page page_scheduling Graph Scheduling + +This document tries to explain how the PipeWire graph is scheduled. + +Graph are constructed from linked nodes together with their ports. This +results in a dependency graph between nodes. Special care is taken for +loopback links so that the graph remains a directed graph. + + +# Nodes + +Nodes are objects with 0 or more input and output ports. + +Each node also has: + +- an eventfd to signal the node that it can start processing +- an activation record that lives in shared memory with memfd. + +``` + evenfd + +-^---------+ + | | + in out + | | + +-v---------+ + activation { + status:OK, // bitmask of NEED_DATA, HAVE_DATA or OK + pending:0, // number of unsatisfied dependencies to be able to run + required:0 // number of dependencies with other nodes + } +``` + +The activation record has the following information: + + - processing state and pending dependencies. As long as there are pending dependencies + the node can not be processed. This is the only relevant information for actually + scheduling the graph and is shown in the above illustration. + - Current status of the node and profiling info (TRIGGERED, AWAKE, FINISHED, timestamps + when the node changed state). + - Timing information, mostly for drivers when the processing started, the time, duration + and rate (quantum) etc.. + - Information about repositions (seek) and timebase owners. + + +# links. + +When two nodes are linked together, the output node becomes a dependency for the input +node. This means the input node can only start processing when the output node is finished. + +This dependency is reflected in the required counter in the activation record. In below +illustration, B's required field is incremented with 1. The pending field is set to the +required field when the graph is started. Node A will keep a list of all targets (B) that it +is a dependency of. + +This dependency update is only performed when the link is ready (negotiated) and the nodes +are ready to schedule (runnable). + + +``` + evenfd eventfd + +-^---------+ +-^---------+ + | | link | | + in A out ---------------------> in B out + | | | | + +-v---------+ +-v---------+ + activation { target activation { + status:OK, --------------------> status:OK, + pending:0, pending:1, + required:0 required:1 + } } +``` + +Multiple links between A and B will only result in 1 target link between A and B. + + +# Drivers + +The graph can only run if there is a driver node that is in some way linked to an +active node. + +The driver is special because it will have to initiate the processing in the graph. It +will use a timer or some sort of interrupt from hardware to start the cycle. + +Any node can also be a candidate for a driver (when the node.driver property is true). +PipeWire will select the node with the highest priority.driver property as the driver. + +Nodes will be assigned to the driver node they will be scheduled with. Each node holds +a reference to the driver and increments the required field of the driver. + +When a node is ready to be scheduled, the driver adds the node to its list of targets +and increments the required field. + + +``` + evenfd eventfd + +-^---------+ +-^---------+ + | | link | | + in A out ---------------------> in B out + | | | | + +-v---------+ +-v---------+ + activation { target activation { + status:OK, --------------------> status:OK, + pending:0, pending:0, + required:1 required:2 + } } + | ^ ^ + | | / / + | | / / + | | / / + | | / / + | | / / + v | /-------------/ / + activation { / + status:OK, V---------------/ + pending:0, + required:2 + } + +-^---------+ + | | + | driver | + | | + +-v---------+ + eventfd +``` + +As seen in the illustration above, the driver holds a link to each node it needs to +schedule and each node holds a link to the driver. Some nodes hold a link to other +nodes. + +It is possible that the driver is the same as a node in the graph (for example node A) +but conceptually, the links above are still valid. + +The driver will then start processing the graph by emitting the ready signal. PipeWire +will then: + + - Perform some statistics about the previous cycle. Did it complete? compute processing + times, cpu usage etc. + - Perform reposition requests if any, timebase changes, etc.. + - The pending counter of each follower node is set to the required field. + - it then loops over all targets of the driver and atomically decrements the required + field of the activation record. When the required field is 0, the eventfd is signaled + and the node can be scheduled. + +In our example above, Node A and be will have their pending state decremented. Node A +will be 0 and will be triggered first (node B has 2 pending dependencies to start with and +will not be triggered yet). The driver itself also has 2 dependcies left and will not +be triggered (complete) yet. + +## Scheduling node A + +When the eventfd is signaled on a node, we say the node is triggered and it will be able +to process data. It consumes the input on the input ports and produces more data on the +output ports. + +After processing, node A goes through the list of targets and decrements each pending +field (node A has a reference to B and the driver). + +In our above example, the driver is decremented (from 2 to 1) but is not yet triggered. +node B is decremented (from 1 to 0) and is triggered by writing to the eventfd. + +## Scheduling node B + +Node B is scheduled and processes the input from node A. It then goes through the list of +targets and decrements the pending fields. It decrements the pending field of the +driver (from 1 to 0) and triggers the driver. + +## Scheduling the driver + +The graph always completes after the driver is triggered and scheduled. All required +fields from all the nodes in the target list of the driver are now 0. + +# Remote nodes. + +For remote nodes, the eventfd and the activation is transfered from the server +to the client. + +This means that writing to the remote client eventfd will wake the client directly +without going to the server first. + +All remote clients also get the activation and eventfd of the peer and driver they +are linked to and can directly trigger peers and drivers without going to the +server first. + +## Remote driver nodes. + +Currently the graph start cycle is managed by the server. + +Remote driver nodes therefore have an extra eventfd to wake up the server and signal +the graph start. + +*/
View file
pipewire-0.3.70.tar.gz/doc/pipewire.dox -> pipewire-0.3.71.tar.gz/doc/pipewire.dox
Changed
@@ -10,6 +10,8 @@ - \subpage page_objects_design - \subpage page_library - \subpage page_dma_buf +- \subpage page_scheduling +- \subpage page_native_protocol # Components
View file
pipewire-0.3.70.tar.gz/doc/spa-pod.dox -> pipewire-0.3.71.tar.gz/doc/spa-pod.dox
Changed
@@ -517,11 +517,515 @@ # POD Layout -Each POD has a 32 bits size field, followed by a 32 bits type field. The size -field specifies the size following the type field. - -Each POD is aligned to an 8 byte boundary. - +A POD always starts with a size/type pair of uint32_t in native endianness, +followed by size in bytes of the payload data and padding. See +\ref page_spa_pod for more details. + +The payload is always padded to 8 bytes so that a complete pod is always +a multiple of 8 bytes. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | payload ... | + . | ... padding . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +The total size of the POD is thus ROUND_UP_8(8 + size). + +# POD Types + +Here follows the layout of the POD types. + +## None (1) + +Type 1 is the None type or the null pointer. It has a size of 0 and thus +no payload. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 1 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## Bool (2) + +Type 2 is the Bool type. I contains a true or false value. The value is +stored in a int32, a value of 0 is false, any other value is true. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 2 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | value (int32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## Id (3) + +An id is stored as a uint32. The id refers to an index in a table where more +information about the value can be found. This is typically a type table +containing some well known ids. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 3 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | id (uint32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## Int (4) + +A 32 bit signed integer. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | value (int32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## Long (5) + +A 64 bit signed integer. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 5 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | value (int64) | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## Float (6) + +A 32 bit float value. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 6 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | value (float32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## Double (7) + +A 64 bit float value. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 7 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | value (float64) | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## String (8) + +A string. This does not have to be valid UTF8 but it is 0 terminated. +The size field is set to the length of the string, including the 0 +byte. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 8 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | chars .... | + . . + | ... 0 | padding.. | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + + +## Bytes (9) + +A byte array. The size field is set to the number of bytes. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 9 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | bytes .... | + . . + | | padding.. | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## Rectangle (10) + +A Rectangle. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 8 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 10 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | width (uint32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | height (uint32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## Fraction (11) + +A Fraction. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 8 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 11 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | num (uint32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | denom (uint32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## Bitmap (12) + +A bitmap. Stored as bits in uint8. size is the number of bytes with +bits. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 12 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | bits (uint8) ... | + . . + | | padding.. | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## Array (13) + +An array is an array of (basic) types. In principle the array can contain +any type as long as each item in the array has the same child_size and +child_type. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 13 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | child_size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | child_type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | child1 (child_size bytes) ... | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | childN (child_size bytes) ... | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +We describe Array types with a shortcut like: + +``` + ArrayInt(<val1>,<val2>,...) +``` + +## Struct (14) + +Multiple PODs can be combined into a struct: + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 14 (Struct) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size1 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | type1 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | payload 1... | + . | ... padding . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | sizeN | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | typeN | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | payloadN ... | + . | ... padding . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +We describe Struct types with a shortcut like: + +``` + Struct( + <pod1 type> : <pod1 description>, + <pod2 type> : <pod2 description>, + ...) +``` + +The type of a struct is 14 and the size the total sum in bytes of all +PODs (with padding) inside the struct. + +## Object (15) + +An object contains a set of of properties. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 15 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | object_type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | object_id | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | property1 | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | propertyN | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +object_type is one of the well defined object types. +object_id is extra information about the context of the object. + +Each property is as follows: + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | key (uint32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | flags (uint32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | POD value ... | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +Object are written with a shortcut as: + +``` + Objecttype,id( + key1: <pod1>, + key2: <pod1>, + ...) +``` + +## Sequence (16) + +A sequence is a series of times events. It is usually used for transporting +MIDI and control updates. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 16 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unit | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | pad | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | control1 | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | controlN | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +The unit field and pad is currently set to 0. + +Each control look like: + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | offset (uint32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | type (uint32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | POD value ... | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +- offset: the offset relative to the current graph clock time. +- type: the type of control, see enum spa_control_type + + +## Pointer (17) + +A generic pointer to some memory region. Pointer types are usually not serialized. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 17 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | padding (must be 0) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | native pointer value ... | + . | .. padding . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + + +## Fd (18) + +A file descriptor stored as int64. When serializing, the file descriptor +is modified to contain the index of the fd in the message. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 18 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | fd (int64) ... | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +## Choice (19) + +A choice contains an array of possible values. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 19 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | flags | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | child_size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | child_type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | child1 (child_size bytes) ... | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | childN (child_size bytes) ... | + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +- type: one of possible values, see enum spa_choice_type + - None (0) : only child1 is an valid option + - Range (1) : child1 is a default value, options are between + child2 and child3 in the value array. + - Step (2) : child1 is a default value, options are between + child2 and child3, in steps of child4 in the value array. + - Enum (3) : child1 is a default value, options are any value from + the value array, prefered values come first. + - Flags (4) : child1 is a default value, options are any value from + the value array, prefered values come first. +- flags: must be 0 + +## Pod (20) + +The value id the POD itself. \addtogroup spa_pod
View file
pipewire-0.3.70.tar.gz/man/pw-cli.1.rst.in -> pipewire-0.3.71.tar.gz/man/pw-cli.1.rst.in
Changed
@@ -26,8 +26,11 @@ *pipewire-0*. Connections to other, remote instances can be made. The current instance -name is displayed at the prompt. Some commands operate on the current -instance and some on the local instance. +name is displayed at the prompt. + +Note that **pw-cli** also creates a local PipeWire instance. Some commands +operate on the current (remote) instance and some on the local instance, such +as module loading. Use the 'help' command to list the available commands. @@ -50,12 +53,15 @@ | instance. load-module *name* *arguments...* - Load a module specified by its name and arguments. For most - modules it is OK to be loaded more than once. + Load a module specified by its name and arguments in the local instance. + For most modules it is OK to be loaded more than once. This command returns a module variable that can be used to unload the module. + The locally module is *not* visible in the remote instance. It is not + possible in PipeWire to load modules in a remote instance. + unload-module *module-var* Unload a module, specified either by its variable. @@ -82,6 +88,9 @@ If no remote name is specified, a connection is made to the default remote instance, usually *pipewire-0*. + The special remote name called *internal* can be used to connect to + the local **pw-cli** PipeWire instance. + This command returns a remote var that can be used to disconnect or switch remotes. @@ -96,7 +105,7 @@ switch-remote *remote-var* Make the specified *remote* the current instance. - If no remote name is specified, the local instance is made current. + If no remote name is specified, the first instance is made current. NODE MANAGEMENT ===============
View file
pipewire-0.3.70.tar.gz/meson.build -> pipewire-0.3.71.tar.gz/meson.build
Changed
@@ -1,5 +1,5 @@ project('pipewire', 'c' , - version : '0.3.70', + version : '0.3.71', license : 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' , meson_version : '>= 0.61.1', default_options : 'warning_level=3', @@ -191,6 +191,7 @@ versiondata.set_quoted('PIPEWIRE_API_VERSION', apiversion) cdata = configuration_data() +cdata.set_quoted('PREFIX', prefix) cdata.set_quoted('PIPEWIRE_CONFDATADIR', pipewire_confdatadir) cdata.set_quoted('LOCALEDIR', pipewire_localedir) cdata.set_quoted('LIBDIR', pipewire_libdir)
View file
pipewire-0.3.70.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c -> pipewire-0.3.71.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c
Changed
@@ -64,6 +64,7 @@ unsigned int xrun_detected:1; unsigned int hw_params_changed:1; unsigned int active:1; + unsigned int negotiated:1; snd_pcm_uframes_t hw_ptr; snd_pcm_uframes_t boundary; @@ -385,6 +386,20 @@ SPA_PARAM_BUFFERS_stride, SPA_POD_Int(pw->stride)); pw_stream_update_params(pw->stream, params, n_params); + + pw->negotiated = true; + pw_thread_loop_signal(pw->main_loop, false); +} + +static void on_stream_state_changed(void *data, enum pw_stream_state old, enum pw_stream_state state, const char *error) +{ + snd_pcm_pipewire_t *pw = data; + + if (state == PW_STREAM_STATE_ERROR) { + pw_log_warn("%s", error); + pw->error = -EIO; + update_active(&pw->io); + } } static void on_stream_drained(void *data) @@ -463,6 +478,7 @@ static const struct pw_stream_events stream_events = { PW_VERSION_STREAM_EVENTS, .param_changed = on_stream_param_changed, + .state_changed = on_stream_state_changed, .process = on_stream_process, .drained = on_stream_drained, }; @@ -546,6 +562,7 @@ pw->error = 0; + pw->negotiated = false; pw_stream_connect(pw->stream, io->stream == SND_PCM_STREAM_PLAYBACK ? PW_DIRECTION_OUTPUT : @@ -563,13 +580,18 @@ pw->drained = false; pw->draining = false; + while (!pw->negotiated && pw->error >= 0) + pw_thread_loop_wait(pw->main_loop); + if (pw->error < 0) + goto error; + pw_thread_loop_unlock(pw->main_loop); return 0; error: pw_thread_loop_unlock(pw->main_loop); - return -ENOMEM; + return pw->error < 0 ? pw->error : -ENOMEM; } static int snd_pcm_pipewire_start(snd_pcm_ioplug_t *io)
View file
pipewire-0.3.70.tar.gz/pipewire-jack/jack/control.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/control.h
Changed
@@ -646,7 +646,7 @@ const char *format, ...); -/* @} */ +/**@}*/ #if 0 { /* Adjust editor indent */
View file
pipewire-0.3.70.tar.gz/pipewire-jack/jack/jack.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/jack.h
Changed
@@ -230,7 +230,7 @@ */ jack_native_thread_t jack_client_thread_id (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; -/*@}*/ +/**@}*/ /** * @param client pointer to JACK client structure. @@ -289,7 +289,7 @@ */ int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; -/*@}*/ +/**@}*/ /** * @defgroup ClientCallbacks Setting Client Callbacks @@ -557,8 +557,6 @@ int jack_set_xrun_callback (jack_client_t *client, JackXRunCallback xrun_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT; -/*@}*/ - /** * Tell the Jack server to call @a latency_callback whenever it * is necessary to recompute the latencies for some or all @@ -609,7 +607,7 @@ * @ref jack_port_get_latency_range(), which only returns meaningful * values when ports get connected and latency values change. * - * See the documentation for @ref jack_port_set_latency_range() + * See the documentation for @ref jack_port_set_latency_range() * on how the callback should operate. Remember that the @a mode * argument given to the latency callback will need to be * passed into @ref jack_port_set_latency_range() @@ -619,7 +617,7 @@ int jack_set_latency_callback (jack_client_t *client, JackLatencyCallback latency_callback, void *) JACK_WEAK_EXPORT; -/*@}*/ +/**@}*/ /** * @defgroup ServerClientControl Controlling & querying JACK server operation @@ -706,7 +704,7 @@ */ float jack_cpu_load (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; -/*@}*/ +/**@}*/ /** * @defgroup PortFunctions Creating & manipulating ports @@ -1049,10 +1047,11 @@ */ size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_type) JACK_WEAK_EXPORT; -/*@}*/ +/**@}*/ /** * @defgroup LatencyFunctions Managing and determining latency + * @{ * * The purpose of JACK's latency API is to allow clients to * easily answer two questions: @@ -1097,7 +1096,6 @@ * clients that add latency to the signal path should interact * with JACK to ensure that the correct latency figures are * used. - * @{ */ /** @@ -1263,7 +1261,7 @@ */ int jack_recompute_total_latency (jack_client_t*, jack_port_t* port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT; -/*@}*/ +/**@}*/ /** * @defgroup PortSearching Looking up ports @@ -1304,7 +1302,7 @@ jack_port_t * jack_port_by_id (jack_client_t *client, jack_port_id_t port_id) JACK_OPTIONAL_WEAK_EXPORT; -/*@}*/ +/**@}*/ /** * @defgroup TimeFunctions Handling time @@ -1412,12 +1410,12 @@ */ jack_time_t jack_get_time(void) JACK_OPTIONAL_WEAK_EXPORT; -/*@}*/ +/**@}*/ /** * @defgroup ErrorOutput Controlling error/information output + * @{ */ -/*@{*/ /** * Display JACK error message. @@ -1457,7 +1455,7 @@ */ void jack_set_info_function (void (*func)(const char *)) JACK_OPTIONAL_WEAK_EXPORT; -/*@}*/ +/**@}*/ /** * The free function to be used on memory returned by jack_port_get_connections,
View file
pipewire-0.3.70.tar.gz/pipewire-jack/jack/midiport.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/midiport.h
Changed
@@ -185,7 +185,7 @@ uint32_t jack_midi_get_lost_event_count(void *port_buffer) JACK_OPTIONAL_WEAK_EXPORT; -/*@}*/ +/**@}*/ #ifdef __cplusplus }
View file
pipewire-0.3.70.tar.gz/pipewire-jack/jack/ringbuffer.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/ringbuffer.h
Changed
@@ -50,8 +50,8 @@ typedef struct { char *buf; - volatile size_t write_ptr; - volatile size_t read_ptr; + size_t write_ptr; + size_t read_ptr; size_t size; size_t size_mask; int mlocked;
View file
pipewire-0.3.70.tar.gz/pipewire-jack/jack/session.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/session.h
Changed
@@ -33,7 +33,7 @@ * * @deprecated Use of JACK-Session is currently deprecated and unsupported. * JACK developers recommend the use of NSM instead. - * See https://github.com/linuxaudio/new-session-manager + * See https://new-session-manager.jackaudio.org/ * @{ */
View file
pipewire-0.3.70.tar.gz/pipewire-jack/jack/thread.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/thread.h
Changed
@@ -151,7 +151,7 @@ #endif -/* @} */ +/**@}*/ #ifdef __cplusplus }
View file
pipewire-0.3.70.tar.gz/pipewire-jack/jack/transport.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/transport.h
Changed
@@ -238,7 +238,7 @@ void jack_set_transport_info (jack_client_t *client, jack_transport_info_t *tinfo) JACK_OPTIONAL_WEAK_EXPORT; -/*@}*/ +/**@}*/ #ifdef __cplusplus }
View file
pipewire-0.3.70.tar.gz/pipewire-jack/jack/types.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/types.h
Changed
@@ -538,11 +538,12 @@ */ typedef enum { - JackPositionBBT = 0x10, /**< Bar, Beat, Tick */ - JackPositionTimecode = 0x20, /**< External timecode */ - JackBBTFrameOffset = 0x40, /**< Frame offset of BBT information */ - JackAudioVideoRatio = 0x80, /**< audio frames per video frame */ - JackVideoFrameOffset = 0x100 /**< frame offset of first video frame */ + JackPositionBBT = 0x10, /**< Bar, Beat, Tick */ + JackPositionTimecode = 0x20, /**< External timecode */ + JackBBTFrameOffset = 0x40, /**< Frame offset of BBT information */ + JackAudioVideoRatio = 0x80, /**< audio frames per video frame */ + JackVideoFrameOffset = 0x100, /**< frame offset of first video frame */ + JackTickDouble = 0x200, /**< double-resolution tick */ } jack_position_bits_t; @@ -550,6 +551,9 @@ #define JACK_POSITION_MASK (JackPositionBBT|JackPositionTimecode) #define EXTENDED_TIME_INFO +/** transport tick_double member is available for use */ +#define JACK_TICK_DOUBLE + PRE_PACKED_STRUCTURE struct _jack_position { @@ -609,10 +613,18 @@ set, but the value is zero, there is no video frame within this cycle. */ + /* JACK extra transport fields */ + + double tick_double; /**< current tick-within-beat in double resolution. + Should be assumed zero if JackTickDouble is not set. + Since older versions of JACK do not expose this variable, + the macro JACK_TICK_DOUBLE is provided, + which can be used as build-time detection. */ + /* For binary compatibility, new fields should be allocated from * this padding area with new valid bits controlling access, so * the existing structure size and offsets are preserved. */ - int32_t padding7; + int32_t padding5; /* When (unique_1 == unique_2) the contents are consistent. */ jack_unique_t unique_2; /**< unique ID */
View file
pipewire-0.3.70.tar.gz/pipewire-jack/jack/weakjack.h -> pipewire-0.3.71.tar.gz/pipewire-jack/jack/weakjack.h
Changed
@@ -120,6 +120,6 @@ #endif #endif -/*@}*/ +/**@}*/ #endif /* weakjack */
View file
pipewire-0.3.70.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.71.tar.gz/pipewire-jack/src/pipewire-jack.c
Changed
@@ -24,6 +24,7 @@ #include <spa/debug/pod.h> #include <spa/utils/json.h> #include <spa/utils/string.h> +#include <spa/utils/ringbuffer.h> #include <pipewire/pipewire.h> #include <pipewire/private.h> @@ -50,6 +51,8 @@ #define MAX_MIX 1024 #define MAX_BUFFER_FRAMES 8192 +#define MAX_CLIENT_PORTS 768 + #define MAX_ALIGN 16 #define MAX_BUFFERS 2 #define MAX_BUFFER_DATAS 1u @@ -70,6 +73,28 @@ #define SELF_CONNECT_FAIL_ALL -2 #define SELF_CONNECT_IGNORE_ALL 2 +#define NOTIFY_BUFFER_SIZE (1u<<13) +#define NOTIFY_BUFFER_MASK (NOTIFY_BUFFER_SIZE-1) + +struct notify { +#define NOTIFY_ACTIVE_FLAG (1<<0) + +#define NOTIFY_TYPE_NONE ((0<<4)|NOTIFY_ACTIVE_FLAG) +#define NOTIFY_TYPE_REGISTRATION ((1<<4)) +#define NOTIFY_TYPE_PORTREGISTRATION ((2<<4)|NOTIFY_ACTIVE_FLAG) +#define NOTIFY_TYPE_CONNECT ((3<<4)|NOTIFY_ACTIVE_FLAG) +#define NOTIFY_TYPE_GRAPH ((4<<4)|NOTIFY_ACTIVE_FLAG) +#define NOTIFY_TYPE_BUFFER_FRAMES ((5<<4)|NOTIFY_ACTIVE_FLAG) +#define NOTIFY_TYPE_SAMPLE_RATE ((6<<4)|NOTIFY_ACTIVE_FLAG) +#define NOTIFY_TYPE_FREEWHEEL ((7<<4)|NOTIFY_ACTIVE_FLAG) +#define NOTIFY_TYPE_SHUTDOWN ((8<<4)|NOTIFY_ACTIVE_FLAG) +#define NOTIFY_TYPE_LATENCY ((9<<4)|NOTIFY_ACTIVE_FLAG) + int type; + struct object *object; + int arg1; + const char *msg; +}; + struct client; struct port; @@ -141,6 +166,7 @@ struct pw_proxy *proxy; struct spa_hook proxy_listener; struct spa_hook object_listener; + int registered; unsigned int removing:1; unsigned int removed:1; }; @@ -296,7 +322,12 @@ uint32_t node_id; uint32_t serial; + struct object *object; + struct spa_source *socket_source; + struct spa_source *notify_source; + void *notify_buffer; + struct spa_ringbuffer notify_ring; JackThreadCallback thread_callback; void *thread_arg; @@ -345,6 +376,7 @@ struct spa_list free_ports; struct pw_map ports2; + uint32_t n_ports; struct spa_list links; uint32_t driver_id; @@ -374,6 +406,7 @@ unsigned int warn_mlock:1; unsigned int timeowner_conditional:1; unsigned int show_monitor:1; + unsigned int show_midi:1; unsigned int merge_monitor:1; unsigned int short_name:1; unsigned int filter_name:1; @@ -385,12 +418,35 @@ unsigned int fix_midi_events:1; unsigned int global_buffer_size:1; unsigned int passive_links:1; + unsigned int graph_callback_pending:1; + unsigned int pending_callbacks:1; + int frozen_callbacks; char filter_char; + uint32_t max_ports; + unsigned int fill_aliases:1; jack_position_t jack_position; jack_transport_state_t jack_state; }; +#define return_val_if_fail(expr, val) \ +({ \ + if (SPA_UNLIKELY(!(expr))) { \ + pw_log_warn("'%s' failed at %s:%u %s()", \ + #expr , __FILE__, __LINE__, __func__); \ + return (val); \ + } \ +}) + +#define return_if_fail(expr) \ +({ \ + if (SPA_UNLIKELY(!(expr))) { \ + pw_log_warn("'%s' failed at %s:%u %s()", \ + #expr , __FILE__, __LINE__, __func__); \ + return; \ + } \ +}) + static int do_sync(struct client *client); static struct object *find_by_serial(struct client *c, uint32_t serial); @@ -556,6 +612,11 @@ struct object *o; uint32_t i; + if (c->n_ports >= c->max_ports) { + errno = ENOSPC; + return NULL; + } + if (spa_list_is_empty(&c->free_ports)) { p = calloc(OBJECT_CHUNK, sizeof(struct port)); if (p == NULL) @@ -567,6 +628,9 @@ spa_list_remove(&p->link); o = alloc_object(c, INTERFACE_Port); + if (o == NULL) + return NULL; + o->id = SPA_ID_INVALID; o->port.node_id = c->node_id; o->port.port = p; @@ -583,6 +647,7 @@ p->direction = direction; p->emptyptr = SPA_PTR_ALIGN(p->empty, MAX_ALIGN, float); p->port_id = pw_map_insert_new(&c->portsdirection, p); + c->n_ports++; pthread_mutex_lock(&c->context.lock); spa_list_append(&c->context.objects, &o->link); @@ -591,17 +656,21 @@ return p; } -static void free_port(struct client *c, struct port *p) +static void free_port(struct client *c, struct port *p, bool free) { struct mix *m; spa_list_consume(m, &p->mix, port_link) free_mix(c, m); + c->n_ports--; pw_map_remove(&c->portsp->direction, p->port_id); - free_object(c, p->object); pw_properties_free(p->props); spa_list_append(&c->free_ports, &p->link); + if (free) + free_object(c, p->object); + else + p->object->removing = true; } static struct object *find_node(struct client *c, const char *name) @@ -766,9 +835,9 @@ *proto_ptr = 0; } -#define do_callback_expr(c,expr,callback,...) \ +#define do_callback_expr(c,expr,callback,do_emit,...) \ ({ \ - if (c->callback && c->active) { \ + if (c->callback && do_emit) { \ pw_thread_loop_unlock(c->context.loop); \ if (c->locked_process) \ pthread_mutex_lock(&c->rt_lock); \ @@ -779,15 +848,14 @@ pthread_mutex_unlock(&c->rt_lock); \ pw_thread_loop_lock(c->context.loop); \ } else { \ - if (c->active) \ - (expr); \ + (expr); \ pw_log_debug("skip " #callback \ - " cb:%p active:%d", c->callback, \ - c->active); \ + " cb:%p do_emit:%d", c->callback, \ + do_emit); \ } \ }) -#define do_callback(c,callback,...) do_callback_expr(c,(void)0,callback,__VA_ARGS__) +#define do_callback(c,callback,do_emit,...) do_callback_expr(c,(void)0,callback,do_emit,__VA_ARGS__) #define do_rt_callback_res(c,callback,...) \ ({ \ @@ -815,6 +883,218 @@ return name; } +static void recompute_latencies(struct client *c) +{ + do_callback(c, latency_callback, c->active, JackCaptureLatency, c->latency_arg); + do_callback(c, latency_callback, c->active, JackPlaybackLatency, c->latency_arg); +} + +#define freeze_callbacks(c) \ +({ \ + (c)->frozen_callbacks++; \ + }) + +#define check_callbacks(c) \ +({ \ + if ((c)->frozen_callbacks == 0 && (c)->pending_callbacks) \ + pw_loop_signal_event((c)->context.l, (c)->notify_source); \ + }) +#define thaw_callbacks(c) \ +({ \ + (c)->frozen_callbacks--; \ + check_callbacks(c); \ + }) + +static void emit_callbacks(struct client *c) +{ + struct object *o; + int32_t avail; + uint32_t index; + struct notify *notify; + + if (c->frozen_callbacks != 0 || !c->pending_callbacks) + return; + + pw_log_debug("%p: enter active:%u", c, c->active); + + c->pending_callbacks = false; + + freeze_callbacks(c); + + avail = spa_ringbuffer_get_read_index(&c->notify_ring, &index); + while (avail > 0) { + notify = SPA_PTROFF(c->notify_buffer, index & NOTIFY_BUFFER_MASK, struct notify); + + o = notify->object; + pw_log_debug("%p: dequeue notify index:%08x %p type:%d %p arg1:%d\n", c, + index, notify, notify->type, o, notify->arg1); + + switch (notify->type) { + case NOTIFY_TYPE_REGISTRATION: + if (o->registered == notify->arg1) + break; + pw_log_debug("%p: node %u %s %u", c, o->serial, + o->node.name, notify->arg1); + do_callback(c, registration_callback, true, + o->node.name, + notify->arg1, + c->registration_arg); + break; + case NOTIFY_TYPE_PORTREGISTRATION: + if (o->registered == notify->arg1) + break; + pw_log_debug("%p: port %u %s %u", c, o->serial, + o->port.name, notify->arg1); + do_callback(c, portregistration_callback, c->active, + o->serial, + notify->arg1, + c->portregistration_arg); + break; + case NOTIFY_TYPE_CONNECT: + if (o->registered == notify->arg1) + break; + pw_log_debug("%p: link %u %u -> %u %u", c, o->serial, + o->port_link.src_serial, + o->port_link.dst, notify->arg1); + do_callback(c, connect_callback, c->active, + o->port_link.src_serial, + o->port_link.dst_serial, + notify->arg1, + c->connect_arg); + break; + case NOTIFY_TYPE_GRAPH: + pw_log_debug("%p: graph", c); + recompute_latencies(c); + do_callback(c, graph_callback, c->active, c->graph_arg); + break; + case NOTIFY_TYPE_BUFFER_FRAMES: + pw_log_debug("%p: buffer frames %d", c, notify->arg1); + if (c->buffer_frames != (uint32_t)notify->arg1) { + do_callback_expr(c, c->buffer_frames = notify->arg1, + bufsize_callback, c->active, + notify->arg1, c->bufsize_arg); + recompute_latencies(c); + } + break; + case NOTIFY_TYPE_SAMPLE_RATE: + pw_log_debug("%p: sample rate %d", c, notify->arg1); + if (c->sample_rate != (uint32_t)notify->arg1) { + do_callback_expr(c, c->sample_rate = notify->arg1, + srate_callback, c->active, + notify->arg1, c->srate_arg); + } + break; + case NOTIFY_TYPE_FREEWHEEL: + pw_log_debug("%p: freewheel %d", c, notify->arg1); + do_callback(c, freewheel_callback, c->active, + notify->arg1, c->freewheel_arg); + break; + case NOTIFY_TYPE_SHUTDOWN: + pw_log_debug("%p: shutdown %d %s", c, notify->arg1, notify->msg); + if (c->info_shutdown_callback) + do_callback(c, info_shutdown_callback, c->active, + notify->arg1, notify->msg, + c->info_shutdown_arg); + else + do_callback(c, shutdown_callback, c->active, c->shutdown_arg); + break; + case NOTIFY_TYPE_LATENCY: + pw_log_debug("%p: latency %d", c, notify->arg1); + do_callback(c, latency_callback, c->active, notify->arg1, c->latency_arg); + break; + default: + break; + } + if (o != NULL) { + o->registered = notify->arg1; + if (notify->arg1 == 0 && o->removing) { + o->removing = false; + free_object(c, o); + } + } + avail -= sizeof(struct notify); + index += sizeof(struct notify); + spa_ringbuffer_read_update(&c->notify_ring, index); + } + thaw_callbacks(c); + pw_log_debug("%p: leave", c); +} + +static int queue_notify(struct client *c, int type, struct object *o, int arg1, const char *msg) +{ + int32_t filled; + uint32_t index; + struct notify *notify; + bool emit = false;; + + if ((type & NOTIFY_ACTIVE_FLAG) && !c->active) + return 0; + switch (type) { + case NOTIFY_TYPE_REGISTRATION: + emit = c->registration_callback != NULL && o != NULL; + break; + case NOTIFY_TYPE_PORTREGISTRATION: + emit = c->portregistration_callback != NULL && o != NULL; + break; + case NOTIFY_TYPE_CONNECT: + emit = c->connect_callback != NULL && o != NULL; + break; + case NOTIFY_TYPE_GRAPH: + emit = c->graph_callback != NULL || c->latency_callback != NULL; + break; + case NOTIFY_TYPE_BUFFER_FRAMES: + emit = c->bufsize_callback != NULL; + break; + case NOTIFY_TYPE_SAMPLE_RATE: + emit = c->srate_callback != NULL; + break; + case NOTIFY_TYPE_FREEWHEEL: + emit = c->freewheel_callback != NULL; + break; + case NOTIFY_TYPE_SHUTDOWN: + emit = c->info_shutdown_callback != NULL || c->shutdown_callback != NULL; + break; + case NOTIFY_TYPE_LATENCY: + emit = c->latency_callback != NULL; + break; + default: + break; + } + if (!emit) { + pw_log_debug("%p: skip notify %d", c, type); + if (o != NULL && arg1 == 0 && o->removing) { + o->removing = false; + free_object(c, o); + } + return 0; + } + + filled = spa_ringbuffer_get_write_index(&c->notify_ring, &index); + if (filled < 0 || filled + sizeof(struct notify) > NOTIFY_BUFFER_SIZE) { + pw_log_warn("%p: notify queue full %d", c, type); + return -ENOSPC; + } + + notify = SPA_PTROFF(c->notify_buffer, index & NOTIFY_BUFFER_MASK, struct notify); + notify->type = type; + notify->object = o; + notify->arg1 = arg1; + notify->msg = msg; + pw_log_debug("%p: queue notify index:%08x %p type:%d %p arg1:%d msg:%s\n", c, + index, notify, notify->type, o, notify->arg1, notify->msg); + index += sizeof(struct notify); + spa_ringbuffer_write_update(&c->notify_ring, index); + c->pending_callbacks = true; + check_callbacks(c); + return 0; +} + +static void on_notify_event(void *data, uint64_t count) +{ + struct client *c = data; + emit_callbacks(c); +} + static void on_sync_reply(void *data, uint32_t id, int seq) { struct client *client = data; @@ -825,7 +1105,6 @@ pw_thread_loop_signal(client->context.loop, false); } - static void on_error(void *data, uint32_t id, int seq, int res, const char *message) { struct client *client = data; @@ -835,8 +1114,11 @@ if (id == PW_ID_CORE) { client->last_res = res; - if (!client->destroyed) - do_callback(client, shutdown_callback, client->shutdown_arg); + if (res == -EPIPE && !client->destroyed) { + queue_notify(client, NOTIFY_TYPE_SHUTDOWN, + NULL, JackFailure | JackServerError, + "JACK server has been closed"); + } } pw_thread_loop_signal(client->context.loop, false); } @@ -860,6 +1142,8 @@ client->last_res = 0; client->pending_sync = pw_proxy_sync((struct pw_proxy*)client->core, client->pending_sync); + if (client->pending_sync < 0) + return client->pending_sync; while (true) { if (in_data_thread) { @@ -932,15 +1216,6 @@ } } -static int -do_remove_sources(struct spa_loop *loop, - bool async, uint32_t seq, const void *data, size_t size, void *user_data) -{ - struct client *c = user_data; - client_remove_source(c); - return 0; -} - static inline void reuse_buffer(struct client *c, struct mix *mix, uint32_t id) { struct buffer *b; @@ -1302,24 +1577,6 @@ return state; } -static void recompute_latencies(struct client *c) -{ - do_callback(c, latency_callback, JackCaptureLatency, c->latency_arg); - do_callback(c, latency_callback, JackPlaybackLatency, c->latency_arg); -} - -static int -do_buffer_frames(struct spa_loop *loop, - bool async, uint32_t seq, const void *data, size_t size, void *user_data) -{ - uint32_t buffer_frames = *((uint32_t*)data); - struct client *c = user_data; - if (c->buffer_frames != buffer_frames) - do_callback_expr(c, c->buffer_frames = buffer_frames, bufsize_callback, buffer_frames, c->bufsize_arg); - recompute_latencies(c); - return 0; -} - static inline int check_buffer_frames(struct client *c, struct spa_io_position *pos) { uint32_t buffer_frames = pos->clock.duration; @@ -1327,24 +1584,13 @@ pw_log_info("%p: bufferframes old:%d new:%d cb:%p", c, c->buffer_frames, buffer_frames, c->bufsize_callback); if (c->buffer_frames != (uint32_t)-1) - pw_loop_invoke(c->context.l, do_buffer_frames, 0, - &buffer_frames, sizeof(buffer_frames), false, c); + queue_notify(c, NOTIFY_TYPE_BUFFER_FRAMES, NULL, buffer_frames, NULL); else c->buffer_frames = buffer_frames; } return c->buffer_frames == buffer_frames; } -static int -do_sample_rate(struct spa_loop *loop, - bool async, uint32_t seq, const void *data, size_t size, void *user_data) -{ - struct client *c = user_data; - uint32_t sample_rate = *((uint32_t*)data); - do_callback_expr(c, c->sample_rate = sample_rate, srate_callback, sample_rate, c->srate_arg); - return 0; -} - static inline int check_sample_rate(struct client *c, struct spa_io_position *pos) { uint32_t sample_rate = pos->clock.rate.denom; @@ -1352,8 +1598,7 @@ pw_log_info("%p: sample_rate old:%d new:%d cb:%p", c, c->sample_rate, sample_rate, c->srate_callback); if (c->srate_callback != NULL) { - pw_loop_invoke(c->context.l, do_sample_rate, 0, - &sample_rate, sizeof(sample_rate), false, c); + queue_notify(c, NOTIFY_TYPE_SAMPLE_RATE, NULL, sample_rate, NULL); } else { c->sample_rate = sample_rate; } @@ -1535,25 +1780,27 @@ } } -static int -do_clear_link(struct spa_loop *loop, - bool async, uint32_t seq, const void *data, size_t size, void *user_data) -{ - struct link *link = user_data; - spa_list_remove(&link->target_link); - return 0; -} - -static void clear_link(struct client *c, struct link *link) +static void free_link(struct link *link) { - pw_data_loop_invoke(c->loop, - do_clear_link, 1, NULL, 0, !c->data_locked, link); + pw_log_debug("free link %p", link); pw_memmap_free(link->mem); close(link->signalfd); - spa_list_remove(&link->link); free(link); } +static int +do_clean_transport(struct spa_loop *loop, + bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct client *c = user_data; + struct link *l; + pw_log_debug("%p: clean transport", c); + client_remove_source(c); + spa_list_consume(l, &c->rt.target_links, target_link) + spa_list_remove(&l->target_link); + return 0; +} + static void clean_transport(struct client *c) { struct link *l; @@ -1561,12 +1808,15 @@ if (!c->has_transport) return; - pw_data_loop_invoke(c->loop, - do_remove_sources, 1, NULL, 0, !c->data_locked, c); - - spa_list_consume(l, &c->links, link) - clear_link(c, l); - + /* We assume the data-loop is unlocked now and can process our + * clean function. This is reasonable, the cleanup function is run when + * closing the client, which should join the data-thread. */ + pw_data_loop_invoke(c->loop, do_clean_transport, 1, NULL, 0, true, c); + + spa_list_consume(l, &c->links, link) { + spa_list_remove(&l->link); + free_link(l); + } c->has_transport = false; } @@ -1677,7 +1927,7 @@ jack_drop_real_time_scheduling(thr); } - do_callback(c, freewheel_callback, freewheeling, c->freewheel_arg); + queue_notify(c, NOTIFY_TYPE_FREEWHEEL, NULL, freewheeling, NULL); if (!freewheeling && thr) { jack_acquire_real_time_scheduling(thr, @@ -1713,7 +1963,7 @@ mm = pw_mempool_map_id(c->pool, mem_id, PW_MEMMAP_FLAG_READWRITE, offset, size, tag); if (mm == NULL) { - pw_log_warn("%p: can't map memory id %u", c, mem_id); + pw_log_warn("%p: can't map memory id %u: %m", c, mem_id); return -errno; } ptr = mm->ptr; @@ -2025,6 +2275,26 @@ p->info.change_mask = 0; } +static void port_check_latency(struct port *p, const struct spa_latency_info *latency) +{ + struct spa_latency_info *current; + struct client *c = p->client; + struct object *o = p->object; + + current = &o->port.latencylatency->direction; + if (spa_latency_info_compare(current, latency) == 0) + return; + *current = *latency; + + pw_log_info("%p: %s update %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, c, + o->port.name, + latency->direction == SPA_DIRECTION_INPUT ? "playback" : "capture", + latency->min_quantum, latency->max_quantum, + latency->min_rate, latency->max_rate, + latency->min_ns, latency->max_ns); + port_update_latency(p); +} + /* called from thread-loop */ static void default_latency(struct client *c, enum spa_direction direction, struct spa_latency_info *latency) @@ -2050,9 +2320,9 @@ /* called from thread-loop */ static void default_latency_callback(jack_latency_callback_mode_t mode, struct client *c) { - struct spa_latency_info latency, *current; - enum spa_direction direction; + struct spa_latency_info latency; union pw_map_item *item; + enum spa_direction direction; struct port *p; if (mode == JackPlaybackLatency) @@ -2062,21 +2332,11 @@ default_latency(c, direction, &latency); - pw_log_info("client %p: update %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, c, - latency.direction == SPA_DIRECTION_INPUT ? "playback" : "capture", - latency.min_quantum, latency.max_quantum, - latency.min_rate, latency.max_rate, - latency.min_ns, latency.max_ns); - pw_array_for_each(item, &c->portsdirection.items) { if (pw_map_item_is_free(item)) continue; p = item->data; - current = &p->object->port.latencydirection; - if (spa_latency_info_compare(current, &latency) == 0) - continue; - *current = latency; - port_update_latency(p); + port_check_latency(p, &latency); } } @@ -2116,7 +2376,7 @@ mode = JackCaptureLatency; if (c->latency_callback) - do_callback(c, latency_callback, mode, c->latency_arg); + queue_notify(c, NOTIFY_TYPE_LATENCY, NULL, mode, NULL); else default_latency_callback(mode, c); @@ -2359,7 +2619,7 @@ mm = pw_mempool_map_id(c->pool, mem_id, PW_MEMMAP_FLAG_READWRITE, offset, size, tag); if (mm == NULL) { - pw_log_warn("%p: can't map memory id %u", c, mem_id); + pw_log_warn("%p: can't map memory id %u: %m", c, mem_id); res = -EINVAL; goto exit_free; } @@ -2395,6 +2655,17 @@ return 0; } +static int +do_deactivate_link(struct spa_loop *loop, + bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct link *link = user_data; + pw_log_trace("link %p activate", link); + spa_list_remove(&link->target_link); + free_link(link); + return 0; +} + static int client_node_set_activation(void *data, uint32_t node_id, int signalfd, @@ -2423,7 +2694,7 @@ mm = pw_mempool_map_id(c->pool, mem_id, PW_MEMMAP_FLAG_READWRITE, offset, size, NULL); if (mm == NULL) { - pw_log_warn("%p: can't map memory id %u", c, mem_id); + pw_log_warn("%p: can't map memory id %u: %m", c, mem_id); res = -EINVAL; goto exit; } @@ -2455,7 +2726,10 @@ res = -EINVAL; goto exit; } - clear_link(c, link); + spa_list_remove(&link->link); + + pw_data_loop_invoke(c->loop, + do_deactivate_link, SPA_ID_INVALID, NULL, 0, false, link); } if (c->driver_id == node_id) @@ -2514,10 +2788,9 @@ pw_log_info("%p: our link %u/%u -> %u/%u completed", c, l->port_link.src, l->port_link.src_serial, l->port_link.dst, l->port_link.dst_serial); - do_callback(c, connect_callback, - l->port_link.src_serial, l->port_link.dst_serial, 1, c->connect_arg); - recompute_latencies(c); - do_callback(c, graph_callback, c->graph_arg); + queue_notify(c, NOTIFY_TYPE_CONNECT, l, 1, NULL); + queue_notify(c, NOTIFY_TYPE_GRAPH, NULL, 0, NULL); + emit_callbacks(c); } } @@ -2832,7 +3105,7 @@ struct client *c = (struct client *) data; struct object *o, *ot, *op; const char *str; - bool is_first = false, graph_changed = false; + bool is_first = true; uint32_t serial; if (props == NULL) @@ -2849,18 +3122,14 @@ char tmpJACK_CLIENT_NAME_SIZE+1; o = alloc_object(c, INTERFACE_Node); + if (o == NULL) + goto exit; if ((str = spa_dict_lookup(props, PW_KEY_CLIENT_ID)) != NULL) o->node.client_id = atoi(str); node_name = spa_dict_lookup(props, PW_KEY_NODE_NAME); - if (id == c->node_id) { - pw_log_debug("%p: add our node %d", c, id); - if (node_name != NULL) - snprintf(c->name, sizeof(c->name), "%s", node_name); - c->serial = serial; - } snprintf(o->node.node_name, sizeof(o->node.node_name), "%s", node_name); @@ -2896,6 +3165,12 @@ is_first = ot == NULL; snprintf(o->node.name, sizeof(o->node.name), "%s", tmp); } + if (id == c->node_id) { + pw_log_debug("%p: add our node %d", c, id); + snprintf(c->name, sizeof(c->name), "%s", o->node.name); + c->object = o; + c->serial = serial; + } if ((str = spa_dict_lookup(props, PW_KEY_PRIORITY_SESSION)) != NULL) o->node.priority = pw_properties_parse_int(str); @@ -2956,6 +3231,8 @@ } if (is_monitor && !c->show_monitor) goto exit; + if (type_id == TYPE_ID_MIDI && !c->show_midi) + goto exit; o = NULL; if (node_id == c->node_id) { @@ -3013,11 +3290,13 @@ snprintf(o->port.name, sizeof(o->port.name), "%s", tmp); } - if ((str = spa_dict_lookup(props, PW_KEY_OBJECT_PATH)) != NULL) - snprintf(o->port.alias1, sizeof(o->port.alias1), "%s", str); + if (c->fill_aliases) { + if ((str = spa_dict_lookup(props, PW_KEY_OBJECT_PATH)) != NULL) + snprintf(o->port.alias1, sizeof(o->port.alias1), "%s", str); - if ((str = spa_dict_lookup(props, PW_KEY_PORT_ALIAS)) != NULL) - snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", str); + if ((str = spa_dict_lookup(props, PW_KEY_PORT_ALIAS)) != NULL) + snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", str); + } if ((str = spa_dict_lookup(props, PW_KEY_PORT_ID)) != NULL) { o->port.system_id = atoi(str); @@ -3039,6 +3318,8 @@ struct object *p; o = alloc_object(c, INTERFACE_Link); + if (o == NULL) + goto exit; pthread_mutex_lock(&c->context.lock); spa_list_append(&c->context.objects, &o->link); @@ -3121,17 +3402,13 @@ case INTERFACE_Node: if (is_first) { pw_log_info("%p: client added \"%s\"", c, o->node.name); - do_callback(c, registration_callback, - o->node.name, 1, c->registration_arg); - graph_changed = true; + queue_notify(c, NOTIFY_TYPE_REGISTRATION, o, 1, NULL); } break; case INTERFACE_Port: pw_log_info("%p: port added %u/%u \"%s\"", c, o->id, o->serial, o->port.name); - do_callback(c, portregistration_callback, - o->serial, 1, c->portregistration_arg); - graph_changed = true; + queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 1, NULL); break; case INTERFACE_Link: @@ -3140,17 +3417,12 @@ o->port_link.dst, o->port_link.dst_serial, o->port_link.is_complete); if (o->port_link.is_complete) { - do_callback(c, connect_callback, - o->port_link.src_serial, - o->port_link.dst_serial, 1, c->connect_arg); - graph_changed = true; + queue_notify(c, NOTIFY_TYPE_CONNECT, o, 1, NULL); + queue_notify(c, NOTIFY_TYPE_GRAPH, NULL, 0, NULL); } break; } - if (graph_changed) { - recompute_latencies(c); - do_callback(c, graph_callback, c->graph_arg); - } + emit_callbacks(c); exit: return; @@ -3163,7 +3435,6 @@ { struct client *c = (struct client *) data; struct object *o; - bool graph_changed = false; pw_log_debug("%p: removed: %u", c, id); @@ -3186,16 +3457,14 @@ } if (find_node(c, o->node.name) == NULL) { pw_log_info("%p: client %u removed \"%s\"", c, o->id, o->node.name); - do_callback(c, registration_callback, - o->node.name, 0, c->registration_arg); - graph_changed = true; + queue_notify(c, NOTIFY_TYPE_REGISTRATION, o, 0, NULL); + } else { + free_object(c, o); } break; case INTERFACE_Port: pw_log_info("%p: port %u/%u removed \"%s\"", c, o->id, o->serial, o->port.name); - do_callback(c, portregistration_callback, - o->serial, 0, c->portregistration_arg); - graph_changed = true; + queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 0, NULL); break; case INTERFACE_Link: if (o->port_link.is_complete && @@ -3205,21 +3474,16 @@ o->port_link.src, o->port_link.src_serial, o->port_link.dst, o->port_link.dst_serial); o->port_link.is_complete = false; - do_callback(c, connect_callback, - o->port_link.src_serial, o->port_link.dst_serial, 0, c->connect_arg); - graph_changed = true; - } else + queue_notify(c, NOTIFY_TYPE_CONNECT, o, 0, NULL); + queue_notify(c, NOTIFY_TYPE_GRAPH, NULL, 0, NULL); + } else { pw_log_warn("unlink between unknown ports %d and %d", o->port_link.src, o->port_link.dst); + free_object(c, o); + } break; } - if (graph_changed) { - recompute_latencies(c); - do_callback(c, graph_callback, c->graph_arg); - } - - o->removing = false; - free_object(c, o); + emit_callbacks(c); return; } @@ -3275,7 +3539,7 @@ strstr(pw_get_library_version(), "0.2") != NULL) goto disabled; - spa_return_val_if_fail(client_name != NULL, NULL); + return_val_if_fail(client_name != NULL, NULL); client = calloc(1, sizeof(struct client)); if (client == NULL) @@ -3334,6 +3598,11 @@ if (client->context.context == NULL) goto no_props; + client->notify_source = pw_loop_add_event(client->context.l, + on_notify_event, client); + client->notify_buffer = calloc(1, NOTIFY_BUFFER_SIZE + sizeof(struct notify)); + spa_ringbuffer_init(&client->notify_ring); + client->allow_mlock = client->context.context->settings.mem_allow_mlock; client->warn_mlock = client->context.context->settings.mem_warn_mlock; @@ -3367,7 +3636,7 @@ SPA_VERSION_THREAD_UTILS, &thread_utils_impl, client); - client->loop = client->context.context->data_loop_impl; + client->loop = pw_context_get_data_loop(client->context.context); pw_data_loop_stop(client->loop); pw_context_set_object(client->context.context, @@ -3466,6 +3735,7 @@ client->info.change_mask = 0; client->show_monitor = pw_properties_get_bool(client->props, "jack.show-monitor", true); + client->show_midi = pw_properties_get_bool(client->props, "jack.show-midi", true); client->merge_monitor = pw_properties_get_bool(client->props, "jack.merge-monitor", true); client->short_name = pw_properties_get_bool(client->props, "jack.short-name", false); client->filter_name = pw_properties_get_bool(client->props, "jack.filter-name", false); @@ -3477,6 +3747,8 @@ 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->max_ports = pw_properties_get_uint32(client->props, "jack.max-client-ports", MAX_CLIENT_PORTS); + client->fill_aliases = pw_properties_get_bool(client->props, "jack.fill-aliases", false); client->self_connect_mode = SELF_CONNECT_ALLOW; if ((str = pw_properties_get(client->props, "jack.self-connect-mode")) != NULL) { @@ -3533,6 +3805,7 @@ jack_client_close((jack_client_t *) client); return NULL; disabled: + pw_log_warn("JACK is disabled"); if (status) *status = JackFailure | JackInitFailure; return NULL; @@ -3557,7 +3830,7 @@ struct object *o; int res; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); pw_log_info("%p: close", client); @@ -3567,8 +3840,11 @@ clean_transport(c); - if (c->context.loop) + if (c->context.loop) { + queue_notify(c, NOTIFY_TYPE_REGISTRATION, c->object, 0, NULL); + pw_loop_invoke(c->context.l, NULL, 0, NULL, 0, false, c); pw_thread_loop_stop(c->context.loop); + } if (c->registry) { spa_hook_remove(&c->registry_listener); @@ -3586,9 +3862,15 @@ pw_core_disconnect(c->core); } + globals.thread_utils = pw_thread_utils_get(); + if (c->context.context) pw_context_destroy(c->context.context); + if (c->notify_source) + pw_loop_destroy_source(c->context.l, c->notify_source); + free(c->notify_buffer); + if (c->context.loop) pw_thread_loop_destroy(c->context.loop); @@ -3614,7 +3896,7 @@ const char *client_name, jack_status_t *status) { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, 0); + return_val_if_fail(c != NULL, 0); if (status) *status = JackNoSuchClient | JackFailure; return 0; @@ -3626,7 +3908,7 @@ jack_status_t *status, ...) { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, 0); + return_val_if_fail(c != NULL, 0); if (status) *status = JackNoSuchClient | JackFailure; return 0; @@ -3637,7 +3919,7 @@ jack_intclient_t intclient) { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, 0); + return_val_if_fail(c != NULL, 0); return JackFailure | JackNoSuchClient; } @@ -3646,7 +3928,7 @@ jack_intclient_t intclient) { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, NULL); + return_val_if_fail(c != NULL, NULL); return strdup(c->name); } @@ -3662,7 +3944,7 @@ char * jack_get_client_name (jack_client_t *client) { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, NULL); + return_val_if_fail(c != NULL, NULL); return c->name; } @@ -3675,8 +3957,8 @@ char *uuid = NULL; bool monitor; - spa_return_val_if_fail(c != NULL, NULL); - spa_return_val_if_fail(client_name != NULL, NULL); + return_val_if_fail(c != NULL, NULL); + return_val_if_fail(client_name != NULL, NULL); monitor = spa_strendswith(client_name, MONITOR_EXT); @@ -3707,8 +3989,8 @@ char *name = NULL; bool monitor; - spa_return_val_if_fail(c != NULL, NULL); - spa_return_val_if_fail(client_uuid != NULL, NULL); + return_val_if_fail(c != NULL, NULL); + return_val_if_fail(client_uuid != NULL, NULL); if (jack_uuid_parse(client_uuid, &uuid) < 0) return NULL; @@ -3757,9 +4039,10 @@ int jack_activate (jack_client_t *client) { struct client *c = (struct client *) client; + struct object *o; int res = 0; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); pw_log_info("%p: active:%d", c, c->active); @@ -3767,6 +4050,8 @@ return 0; pw_thread_loop_lock(c->context.loop); + freeze_callbacks(c); + pw_data_loop_start(c->loop); if ((res = do_activate(c)) < 0) @@ -3777,12 +4062,18 @@ c->active = true; - do_callback(c, graph_callback, c->graph_arg); - + spa_list_for_each(o, &c->context.objects, link) { + if (o->type != INTERFACE_Port || o->port.port == NULL || + o->port.port->client != c || !o->port.port->valid) + continue; + queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 1, NULL); + } done: if (res < 0) pw_data_loop_stop(c->loop); + pw_log_debug("%p: activate result:%d", c, res); + thaw_callbacks(c); pw_thread_loop_unlock(c->context.loop); return res; @@ -3791,11 +4082,11 @@ SPA_EXPORT int jack_deactivate (jack_client_t *client) { - struct object *l; + struct object *o; struct client *c = (struct client *) client; int res; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); pw_log_info("%p: active:%d", c, c->active); @@ -3803,24 +4094,33 @@ return 0; pw_thread_loop_lock(c->context.loop); - c->active = false; + freeze_callbacks(c); pw_data_loop_stop(c->loop); pw_client_node_set_active(c->node, false); - c->activation->pending_new_pos = false; - c->activation->pending_sync = false; + spa_list_for_each(o, &c->context.objects, link) { + if (o->type != INTERFACE_Link || o->removed) + continue; + if (o->port_link.src_ours || o->port_link.dst_ours) + pw_registry_destroy(c->registry, o->id); + } - spa_list_for_each(l, &c->context.objects, link) { - if (l->type != INTERFACE_Link || l->removed) + spa_list_for_each(o, &c->context.objects, link) { + if (o->type != INTERFACE_Port || o->port.port == NULL || + o->port.port->client != c || !o->port.port->valid) continue; - if (l->port_link.src_ours || l->port_link.dst_ours) - pw_registry_destroy(c->registry, l->id); + queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 0, NULL); } + c->activation->pending_new_pos = false; + c->activation->pending_sync = false; + + c->active = false; res = do_sync(c); + thaw_callbacks(c); pw_thread_loop_unlock(c->context.loop); return res; @@ -3838,7 +4138,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, (pthread_t){0}); + return_val_if_fail(c != NULL, (pthread_t){0}); return (jack_native_thread_t)pw_data_loop_get_thread(c->loop); } @@ -3846,7 +4146,9 @@ SPA_EXPORT int jack_is_realtime (jack_client_t *client) { - return 1; + struct client *c = (struct client *) client; + return_val_if_fail(c != NULL, 0); + return !c->freewheeling; } SPA_EXPORT @@ -3862,7 +4164,7 @@ struct client *c = (struct client *) client; jack_nframes_t res; - spa_return_val_if_fail(c != NULL, 0); + return_val_if_fail(c != NULL, 0); res = cycle_wait(c); pw_log_trace("%p: result:%d", c, res); @@ -3874,7 +4176,7 @@ { struct client *c = (struct client *) client; - spa_return_if_fail(c != NULL); + return_if_fail(c != NULL); pw_log_trace("%p: status:%d", c, status); cycle_signal(c, status); @@ -3885,7 +4187,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -3907,7 +4209,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); pw_log_debug("%p: %p %p", c, thread_init_callback, arg); c->thread_init_callback = thread_init_callback; @@ -3921,7 +4223,7 @@ { struct client *c = (struct client *) client; - spa_return_if_fail(c != NULL); + return_if_fail(c != NULL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -3938,7 +4240,7 @@ { struct client *c = (struct client *) client; - spa_return_if_fail(c != NULL); + return_if_fail(c != NULL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -3956,7 +4258,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -3979,7 +4281,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -3998,7 +4300,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -4017,7 +4319,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -4038,7 +4340,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -4057,7 +4359,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -4077,7 +4379,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -4096,7 +4398,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -4115,7 +4417,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -4133,7 +4435,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -4152,7 +4454,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -4192,7 +4494,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); pw_log_info("%p: buffer-size %u", client, nframes); @@ -4224,7 +4526,7 @@ struct client *c = (struct client *) client; jack_nframes_t res = -1; - spa_return_val_if_fail(c != NULL, 0); + return_val_if_fail(c != NULL, 0); if (!c->active) res = c->latency.denom; @@ -4247,7 +4549,7 @@ struct client *c = (struct client *) client; jack_nframes_t res = -1; - spa_return_val_if_fail(c != NULL, 0); + return_val_if_fail(c != NULL, 0); if (!c->active) res = c->latency.num; @@ -4278,7 +4580,7 @@ struct client *c = (struct client *) client; float res = 0.0f; - spa_return_val_if_fail(c != NULL, 0.0); + return_val_if_fail(c != NULL, 0.0); if (c->driver_activation) res = c->driver_activation->cpu_load0 * 100.0f; @@ -4312,11 +4614,12 @@ struct spa_pod *params6; uint32_t n_params = 0; struct port *p; - int res; + int res, len; + char nameREAL_JACK_PORT_NAME_SIZE+1; - spa_return_val_if_fail(c != NULL, NULL); - spa_return_val_if_fail(port_name != NULL, NULL); - spa_return_val_if_fail(port_type != NULL, NULL); + return_val_if_fail(c != NULL, NULL); + return_val_if_fail(port_name != NULL && strlen(port_name) != 0, NULL); + return_val_if_fail(port_type != NULL, NULL); pw_log_info("%p: port register \"%s:%s\" \"%s\" %08lx %ld", c, c->name, port_name, port_type, flags, buffer_frames); @@ -4334,6 +4637,19 @@ pw_log_warn("unknown port type %s", port_type); return NULL; } + len = snprintf(name, sizeof(name), "%s:%s", c->name, port_name); + if (len < 0 || (size_t)len >= sizeof(name)) { + pw_log_warn("%p: name \"%s:%s\" too long", c, + c->name, port_name); + return NULL; + } + pthread_mutex_lock(&c->context.lock); + o = find_port_by_name(c, name); + pthread_mutex_unlock(&c->context.lock); + if (o != NULL) { + pw_log_warn("%p: name \"%s\" already exists", c, name); + return NULL; + } if ((p = alloc_port(c, direction)) == NULL) { pw_log_warn("can't allocate port %s: %m", port_name); @@ -4342,7 +4658,7 @@ o = p->object; o->port.flags = flags; - snprintf(o->port.name, sizeof(o->port.name), "%s:%s", c->name, port_name); + strcpy(o->port.name, name); o->port.type_id = type_id; init_buffer(p); @@ -4411,6 +4727,7 @@ param_latency_other(c, p, ¶msn_params++, &b); pw_thread_loop_lock(c->context.loop); + freeze_callbacks(c); pw_client_node_port_update(c->node, direction, @@ -4425,15 +4742,31 @@ res = do_sync(c); + thaw_callbacks(c); + pw_log_debug("%p: port %p done", c, p); pw_thread_loop_unlock(c->context.loop); if (res < 0) { pw_log_warn("can't create port %s: %s", port_name, spa_strerror(res)); - return NULL; + goto error_free; } return (jack_port_t *) o; + +error_free: + free_port(c, p, true); + return NULL; +} + +static int +do_free_port(struct spa_loop *loop, + bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct port *p = user_data; + struct client *c = p->client; + free_port(c, p, !c->active); + return 0; } static int @@ -4441,7 +4774,9 @@ bool async, uint32_t seq, const void *data, size_t size, void *user_data) { struct port *p = user_data; + struct client *c = p->client; p->valid = false; + pw_loop_invoke(c->context.l, do_free_port, 0, NULL, 0, false, p); return 0; } @@ -4453,10 +4788,11 @@ struct port *p; int res; - spa_return_val_if_fail(c != NULL, -EINVAL); - spa_return_val_if_fail(o != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(o != NULL, -EINVAL); pw_thread_loop_lock(c->context.loop); + freeze_callbacks(c); p = o->port.port; if (o->type != INTERFACE_Port || p == NULL || !p->valid || @@ -4465,8 +4801,7 @@ res = -EINVAL; goto done; } - pw_data_loop_invoke(c->loop, - do_invalidate_port, 1, NULL, 0, !c->data_locked, p); + pw_data_loop_invoke(c->loop, do_invalidate_port, 1, NULL, 0, false, p); pw_log_info("%p: port %p unregister \"%s\"", client, port, o->port.name); @@ -4480,8 +4815,8 @@ pw_log_warn("can't unregister port %s: %s", o->port.name, spa_strerror(res)); } - free_port(c, p); done: + thaw_callbacks(c); pw_thread_loop_unlock(c->context.loop); return res; @@ -4618,7 +4953,7 @@ struct port *p; void *ptr; - spa_return_val_if_fail(o != NULL, NULL); + return_val_if_fail(o != NULL, NULL); if (o->type != INTERFACE_Port || o->client == NULL) return NULL; @@ -4657,7 +4992,7 @@ jack_uuid_t jack_port_uuid (const jack_port_t *port) { struct object *o = (struct object *) port; - spa_return_val_if_fail(o != NULL, 0); + return_val_if_fail(o != NULL, 0); return jack_port_uuid_generate(o->serial); } @@ -4665,6 +5000,8 @@ { const char *name; struct client *c = o->client; + if (c == NULL) + return NULL; if (c->default_as_system && is_port_default(c, o)) name = o->port.system; else @@ -4676,7 +5013,7 @@ const char * jack_port_name (const jack_port_t *port) { struct object *o = (struct object *) port; - spa_return_val_if_fail(o != NULL, NULL); + return_val_if_fail(o != NULL, NULL); return port_name(o); } @@ -4684,7 +5021,7 @@ const char * jack_port_short_name (const jack_port_t *port) { struct object *o = (struct object *) port; - spa_return_val_if_fail(o != NULL, NULL); + return_val_if_fail(o != NULL, NULL); return strchr(port_name(o), ':') + 1; } @@ -4692,7 +5029,7 @@ int jack_port_flags (const jack_port_t *port) { struct object *o = (struct object *) port; - spa_return_val_if_fail(o != NULL, 0); + return_val_if_fail(o != NULL, 0); return o->port.flags; } @@ -4700,7 +5037,7 @@ const char * jack_port_type (const jack_port_t *port) { struct object *o = (struct object *) port; - spa_return_val_if_fail(o != NULL, NULL); + return_val_if_fail(o != NULL, NULL); return type_to_string(o->port.type_id); } @@ -4708,7 +5045,7 @@ jack_port_type_id_t jack_port_type_id (const jack_port_t *port) { struct object *o = (struct object *) port; - spa_return_val_if_fail(o != NULL, 0); + return_val_if_fail(o != NULL, 0); return o->port.type_id; } @@ -4716,7 +5053,7 @@ int jack_port_is_mine (const jack_client_t *client, const jack_port_t *port) { struct object *o = (struct object *) port; - spa_return_val_if_fail(o != NULL, 0); + return_val_if_fail(o != NULL, 0); return o->type == INTERFACE_Port && o->port.port != NULL && o->port.port->client == (struct client*)client; @@ -4730,7 +5067,7 @@ struct object *l; int res = 0; - spa_return_val_if_fail(o != NULL, 0); + return_val_if_fail(o != NULL, 0); if (o->type != INTERFACE_Port || o->client == NULL) return 0; @@ -4740,8 +5077,6 @@ spa_list_for_each(l, &c->context.objects, link) { if (l->type != INTERFACE_Link || l->removed) continue; - if (!l->port_link.is_complete) - continue; if (l->port_link.src_serial == o->serial || l->port_link.dst_serial == o->serial) res++; @@ -4762,8 +5097,8 @@ struct object *p, *l; int res = 0; - spa_return_val_if_fail(o != NULL, 0); - spa_return_val_if_fail(port_name != NULL, 0); + return_val_if_fail(o != NULL, 0); + return_val_if_fail(port_name != NULL, 0); if (o->type != INTERFACE_Port || o->client == NULL) return 0; @@ -4783,8 +5118,7 @@ p = o; o = l; } - if ((l = find_link(c, o->id, p->id)) != NULL && - l->port_link.is_complete) + if ((l = find_link(c, o->id, p->id)) != NULL) res = 1; exit: @@ -4800,7 +5134,7 @@ { struct object *o = (struct object *) port; - spa_return_val_if_fail(o != NULL, NULL); + return_val_if_fail(o != NULL, NULL); if (o->type != INTERFACE_Port || o->client == NULL) return NULL; @@ -4818,8 +5152,8 @@ int count = 0; struct pw_array tmp; - spa_return_val_if_fail(c != NULL, NULL); - spa_return_val_if_fail(o != NULL, NULL); + return_val_if_fail(c != NULL, NULL); + return_val_if_fail(o != NULL, NULL); pw_array_init(&tmp, sizeof(void*) * 32); @@ -4881,9 +5215,9 @@ struct port *p; int res = 0; - spa_return_val_if_fail(c != NULL, -EINVAL); - spa_return_val_if_fail(o != NULL, -EINVAL); - spa_return_val_if_fail(port_name != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(o != NULL, -EINVAL); + return_val_if_fail(port_name != NULL, -EINVAL); pw_thread_loop_lock(c->context.loop); @@ -4925,8 +5259,8 @@ const char *key; int res = 0; - spa_return_val_if_fail(o != NULL, -EINVAL); - spa_return_val_if_fail(alias != NULL, -EINVAL); + return_val_if_fail(o != NULL, -EINVAL); + return_val_if_fail(alias != NULL, -EINVAL); c = o->client; if (o->type != INTERFACE_Port || c == NULL) @@ -4981,8 +5315,8 @@ const char *key; int res = 0; - spa_return_val_if_fail(o != NULL, -EINVAL); - spa_return_val_if_fail(alias != NULL, -EINVAL); + return_val_if_fail(o != NULL, -EINVAL); + return_val_if_fail(alias != NULL, -EINVAL); c = o->client; if (o->type != INTERFACE_Port || c == NULL) @@ -5029,10 +5363,10 @@ struct object *o = (struct object *) port; int res = 0; - spa_return_val_if_fail(o != NULL, -EINVAL); - spa_return_val_if_fail(aliases != NULL, -EINVAL); - spa_return_val_if_fail(aliases0 != NULL, -EINVAL); - spa_return_val_if_fail(aliases1 != NULL, -EINVAL); + return_val_if_fail(o != NULL, -EINVAL); + return_val_if_fail(aliases != NULL, -EINVAL); + return_val_if_fail(aliases0 != NULL, -EINVAL); + return_val_if_fail(aliases1 != NULL, -EINVAL); if (o->port.alias10 != '\0') { snprintf(aliases0, REAL_JACK_PORT_NAME_SIZE+1, "%s", o->port.alias1); @@ -5051,7 +5385,7 @@ { struct object *o = (struct object *) port; - spa_return_val_if_fail(o != NULL, -EINVAL); + return_val_if_fail(o != NULL, -EINVAL); if (onoff) o->port.monitor_requests++; @@ -5067,8 +5401,8 @@ struct client *c = (struct client *) client; struct object *p; - spa_return_val_if_fail(c != NULL, -EINVAL); - spa_return_val_if_fail(port_name != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(port_name != NULL, -EINVAL); pthread_mutex_lock(&c->context.lock); p = find_port_by_name(c, port_name); @@ -5088,7 +5422,7 @@ { struct object *o = (struct object *) port; - spa_return_val_if_fail(o != NULL, -EINVAL); + return_val_if_fail(o != NULL, -EINVAL); if (onoff) { if (o->port.monitor_requests == 0) @@ -5104,7 +5438,7 @@ int jack_port_monitoring_input (jack_port_t *port) { struct object *o = (struct object *) port; - spa_return_val_if_fail(o != NULL, -EINVAL); + return_val_if_fail(o != NULL, -EINVAL); return o->port.monitor_requests > 0; } @@ -5161,13 +5495,14 @@ char val416; int res, link_res = 0; - spa_return_val_if_fail(c != NULL, EINVAL); - spa_return_val_if_fail(source_port != NULL, EINVAL); - spa_return_val_if_fail(destination_port != NULL, EINVAL); + return_val_if_fail(c != NULL, EINVAL); + return_val_if_fail(source_port != NULL, EINVAL); + return_val_if_fail(destination_port != NULL, EINVAL); pw_log_info("%p: connect %s %s", client, source_port, destination_port); pw_thread_loop_lock(c->context.loop); + freeze_callbacks(c); src = find_port_by_name(c, source_port); dst = find_port_by_name(c, destination_port); @@ -5219,7 +5554,9 @@ pw_proxy_destroy(proxy); - exit: +exit: + pw_log_debug("%p: connect %s %s done %d", client, source_port, destination_port, res); + thaw_callbacks(c); pw_thread_loop_unlock(c->context.loop); return -res; @@ -5234,13 +5571,14 @@ struct object *src, *dst, *l; int res; - spa_return_val_if_fail(c != NULL, -EINVAL); - spa_return_val_if_fail(source_port != NULL, -EINVAL); - spa_return_val_if_fail(destination_port != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(source_port != NULL, -EINVAL); + return_val_if_fail(destination_port != NULL, -EINVAL); pw_log_info("%p: disconnect %s %s", client, source_port, destination_port); pw_thread_loop_lock(c->context.loop); + freeze_callbacks(c); src = find_port_by_name(c, source_port); dst = find_port_by_name(c, destination_port); @@ -5267,6 +5605,7 @@ res = do_sync(c); exit: + thaw_callbacks(c); pw_thread_loop_unlock(c->context.loop); return -res; @@ -5280,12 +5619,13 @@ struct object *l; int res; - spa_return_val_if_fail(c != NULL, -EINVAL); - spa_return_val_if_fail(o != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(o != NULL, -EINVAL); pw_log_debug("%p: disconnect %p", client, port); pw_thread_loop_lock(c->context.loop); + freeze_callbacks(c); spa_list_for_each(l, &c->context.objects, link) { if (l->type != INTERFACE_Link || l->removed) @@ -5297,6 +5637,7 @@ } res = do_sync(c); + thaw_callbacks(c); pw_thread_loop_unlock(c->context.loop); return -res; @@ -5317,8 +5658,8 @@ SPA_EXPORT size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_type) { - spa_return_val_if_fail(client != NULL, 0); - spa_return_val_if_fail(port_type != NULL, 0); + return_val_if_fail(client != NULL, 0); + return_val_if_fail(port_type != NULL, 0); if (spa_streq(JACK_DEFAULT_AUDIO_TYPE, port_type)) return jack_get_buffer_size(client) * sizeof(float); @@ -5337,7 +5678,7 @@ struct client *c; jack_latency_range_t range = { frames, frames }; - spa_return_if_fail(o != NULL); + return_if_fail(o != NULL); c = o->client; pw_log_debug("%p: %s set latency %d", c, o->port.name, frames); @@ -5359,7 +5700,7 @@ int direction; struct spa_latency_info *info; - spa_return_if_fail(o != NULL); + return_if_fail(o != NULL); if (o->type != INTERFACE_Port || o->client == NULL) return; c = o->client; @@ -5383,11 +5724,12 @@ } static int -do_port_update_latency(struct spa_loop *loop, +do_port_check_latency(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) { struct port *p = user_data; - port_update_latency(p); + const struct spa_latency_info *latency = data; + port_check_latency(p, latency); return 0; } @@ -5397,11 +5739,11 @@ struct object *o = (struct object *) port; struct client *c; enum spa_direction direction; - struct spa_latency_info *current, latency; + struct spa_latency_info latency; jack_nframes_t nframes; struct port *p; - spa_return_if_fail(o != NULL); + return_if_fail(o != NULL); if (o->type != INTERFACE_Port || o->client == NULL) return; c = o->client; @@ -5431,43 +5773,19 @@ latency.max_rate %= nframes; } - current = &o->port.latencydirection; - if ((p = o->port.port) == NULL) return; - if (spa_latency_info_compare(current, &latency) == 0) - return; - - pw_log_info("%p: %s update %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, c, - o->port.name, - latency.direction == SPA_DIRECTION_INPUT ? "playback" : "capture", - latency.min_quantum, latency.max_quantum, - latency.min_rate, latency.max_rate, - latency.min_ns, latency.max_ns); - *current = latency; - - pw_loop_invoke(c->context.l, do_port_update_latency, 0, - NULL, 0, false, p); -} - -static int -do_recompute_latencies(struct spa_loop *loop, - bool async, uint32_t seq, const void *data, size_t size, void *user_data) -{ - struct client *c = user_data; - pw_log_debug("start"); - recompute_latencies(c); - pw_log_debug("stop"); - return 0; + pw_loop_invoke(c->context.l, do_port_check_latency, 0, + &latency, sizeof(latency), false, p); } SPA_EXPORT int jack_recompute_total_latencies (jack_client_t *client) { struct client *c = (struct client *) client; - pw_loop_invoke(c->context.l, do_recompute_latencies, 0, - NULL, 0, false, c); + queue_notify(c, NOTIFY_TYPE_LATENCY, NULL, JackCaptureLatency, NULL); + queue_notify(c, NOTIFY_TYPE_LATENCY, NULL, JackPlaybackLatency, NULL); return 0; } @@ -5476,7 +5794,7 @@ struct object *o = (struct object *) port; jack_latency_range_t range = { 0, 0 }; - spa_return_val_if_fail(o != NULL, 0); + return_val_if_fail(o != NULL, 0); if (o->port.flags & JackPortIsOutput) { jack_port_get_latency_range(port, JackCaptureLatency, &range); @@ -5579,7 +5897,7 @@ int r; regex_t port_regex, type_regex; - spa_return_val_if_fail(c != NULL, NULL); + return_val_if_fail(c != NULL, NULL); str = getenv("PIPEWIRE_NODE"); @@ -5664,7 +5982,7 @@ struct client *c = (struct client *) client; struct object *res; - spa_return_val_if_fail(c != NULL, NULL); + return_val_if_fail(c != NULL, NULL); pthread_mutex_lock(&c->context.lock); res = find_port_by_name(c, port_name); @@ -5683,7 +6001,7 @@ struct client *c = (struct client *) client; struct object *res = NULL; - spa_return_val_if_fail(c != NULL, NULL); + return_val_if_fail(c != NULL, NULL); pthread_mutex_lock(&c->context.lock); res = find_by_serial(c, port_id); @@ -5705,7 +6023,7 @@ struct spa_io_position *pos; uint64_t diff; - spa_return_val_if_fail(c != NULL, 0); + return_val_if_fail(c != NULL, 0); if (SPA_UNLIKELY((pos = c->rt.position) == NULL)) return 0; @@ -5726,7 +6044,7 @@ struct client *c = (struct client *) client; struct spa_io_position *pos; - spa_return_val_if_fail(c != NULL, 0); + return_val_if_fail(c != NULL, 0); if (SPA_UNLIKELY((pos = c->rt.position) == NULL)) return 0; @@ -5744,7 +6062,7 @@ struct client *c = (struct client *) client; struct spa_io_position *pos; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (SPA_UNLIKELY((pos = c->rt.position) == NULL)) return -EIO; @@ -5765,7 +6083,7 @@ struct client *c = (struct client *) client; struct spa_io_position *pos; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (SPA_UNLIKELY((pos = c->rt.position) == NULL) || c->buffer_frames == 0) return 0; @@ -5784,7 +6102,7 @@ struct client *c = (struct client *) client; struct spa_io_position *pos; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (SPA_UNLIKELY((pos = c->rt.position) == NULL)) return 0; @@ -5856,7 +6174,7 @@ struct client *c = (struct client *) client; struct pw_node_activation *a; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if ((a = c->driver_activation) == NULL) return -EIO; @@ -5879,9 +6197,10 @@ int res = 0; struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); pw_thread_loop_lock(c->context.loop); + freeze_callbacks(c); c->sync_callback = sync_callback; c->sync_arg = arg; @@ -5891,6 +6210,7 @@ c->activation->pending_sync = true; done: + thaw_callbacks(c); pw_thread_loop_unlock(c->context.loop); return res; @@ -5904,7 +6224,7 @@ struct client *c = (struct client *) client; struct pw_node_activation *a; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); pw_thread_loop_lock(c->context.loop); @@ -5926,10 +6246,11 @@ int res = 0; struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); - spa_return_val_if_fail(timebase_callback != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(timebase_callback != NULL, -EINVAL); pw_thread_loop_lock(c->context.loop); + freeze_callbacks(c); c->timebase_callback = timebase_callback; c->timebase_arg = arg; @@ -5943,6 +6264,7 @@ c->activation->pending_new_pos = true; done: + thaw_callbacks(c); pw_thread_loop_unlock(c->context.loop); return res; @@ -5966,7 +6288,7 @@ struct pw_node_activation *a; jack_transport_state_t jack_state = JackTransportStopped; - spa_return_val_if_fail(c != NULL, JackTransportStopped); + return_val_if_fail(c != NULL, JackTransportStopped); if (SPA_LIKELY((a = c->rt.driver_activation) != NULL)) { jack_state = position_to_jack(a, pos); @@ -5988,7 +6310,7 @@ struct spa_io_segment *seg; uint64_t running; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (SPA_UNLIKELY((a = c->rt.driver_activation) == NULL)) return -EIO; @@ -6012,7 +6334,7 @@ struct client *c = (struct client *) client; struct pw_node_activation *a, *na; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); a = c->rt.driver_activation; na = c->activation; @@ -6046,7 +6368,7 @@ void jack_transport_start (jack_client_t *client) { struct client *c = (struct client *) client; - spa_return_if_fail(c != NULL); + return_if_fail(c != NULL); update_command(c, PW_NODE_ACTIVATION_COMMAND_START); } @@ -6054,7 +6376,7 @@ void jack_transport_stop (jack_client_t *client) { struct client *c = (struct client *) client; - spa_return_if_fail(c != NULL); + return_if_fail(c != NULL); update_command(c, PW_NODE_ACTIVATION_COMMAND_STOP); } @@ -6083,7 +6405,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -EINVAL); + return_val_if_fail(c != NULL, -EINVAL); if (c->active) { pw_log_error("%p: can't set callback on active client", c); @@ -6118,7 +6440,7 @@ { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, NULL); + return_val_if_fail(c != NULL, NULL); return spa_aprintf("%"PRIu64, client_make_uuid(c->serial, false)); } @@ -6132,7 +6454,7 @@ { struct client *c = (struct client *) client; jack_session_command_t *cmds; - spa_return_val_if_fail(c != NULL, NULL); + return_val_if_fail(c != NULL, NULL); pw_log_warn("not implemented"); cmds = calloc(1, sizeof(jack_session_command_t)); return cmds; @@ -6159,7 +6481,7 @@ const char *uuid) { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -1); + return_val_if_fail(c != NULL, -1); pw_log_warn("not implemented"); return 0; } @@ -6168,7 +6490,7 @@ int jack_client_has_session_callback (jack_client_t *client, const char *client_name) { struct client *c = (struct client *) client; - spa_return_val_if_fail(c != NULL, -1); + return_val_if_fail(c != NULL, -1); return 0; } @@ -6185,7 +6507,7 @@ struct client *c = (struct client *) client; int min, max; - spa_return_val_if_fail(c != NULL, -1); + return_val_if_fail(c != NULL, -1); spa_thread_utils_get_rt_range(&c->context.thread_utils, NULL, &min, &max); return SPA_MIN(max, c->rt_max) - 1; @@ -6196,8 +6518,8 @@ { 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_val_if_fail(globals.thread_utils != NULL, -1); + return_val_if_fail(t != NULL, -1); return spa_thread_utils_acquire_rt(globals.thread_utils, t, priority); } @@ -6206,8 +6528,8 @@ { 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_val_if_fail(globals.thread_utils != NULL, -1); + return_val_if_fail(t != NULL, -1); return spa_thread_utils_drop_rt(globals.thread_utils, t); } @@ -6239,9 +6561,9 @@ int res = 0; struct spa_thread *thr; - spa_return_val_if_fail(client != NULL, -EINVAL); - spa_return_val_if_fail(thread != NULL, -EINVAL); - spa_return_val_if_fail(start_routine != NULL, -EINVAL); + return_val_if_fail(client != NULL, -EINVAL); + return_val_if_fail(thread != NULL, -EINVAL); + return_val_if_fail(start_routine != NULL, -EINVAL); pw_log_info("client %p: create thread rt:%d prio:%d", client, realtime, priority); @@ -6270,7 +6592,7 @@ if (thread == (jack_native_thread_t)NULL) return -EINVAL; - spa_return_val_if_fail(client != NULL, -EINVAL); + return_val_if_fail(client != NULL, -EINVAL); pw_log_debug("join thread %p", (void *) thread); spa_thread_utils_join(&c->context.thread_utils, (struct spa_thread*)thread, &status); @@ -6287,7 +6609,7 @@ if (thread == (jack_native_thread_t)NULL) return -EINVAL; - spa_return_val_if_fail(client != NULL, -EINVAL); + return_val_if_fail(client != NULL, -EINVAL); pw_log_debug("cancel thread %p", (void *) thread); pthread_cancel(thread); @@ -6328,8 +6650,8 @@ { struct midi_buffer *mb = port_buffer; struct midi_event *ev = SPA_PTROFF(mb, sizeof(*mb), struct midi_event); - spa_return_val_if_fail(mb != NULL, -EINVAL); - spa_return_val_if_fail(ev != NULL, -EINVAL); + return_val_if_fail(mb != NULL, -EINVAL); + return_val_if_fail(ev != NULL, -EINVAL); if (event_index >= mb->event_count) return -ENOBUFS; ev += event_index; @@ -6343,7 +6665,7 @@ void jack_midi_clear_buffer(void *port_buffer) { struct midi_buffer *mb = port_buffer; - spa_return_if_fail(mb != NULL); + return_if_fail(mb != NULL); mb->event_count = 0; mb->write_pos = 0; mb->lost_events = 0; @@ -6361,7 +6683,7 @@ struct midi_buffer *mb = port_buffer; size_t buffer_size; - spa_return_val_if_fail(mb != NULL, 0); + return_val_if_fail(mb != NULL, 0); buffer_size = mb->buffer_size; @@ -6390,7 +6712,7 @@ struct midi_event *events = SPA_PTROFF(mb, sizeof(*mb), struct midi_event); size_t buffer_size; - spa_return_val_if_fail(mb != NULL, NULL); + return_val_if_fail(mb != NULL, NULL); buffer_size = mb->buffer_size; @@ -6449,7 +6771,7 @@ uint32_t jack_midi_get_lost_event_count(void *port_buffer) { struct midi_buffer *mb = port_buffer; - spa_return_val_if_fail(mb != NULL, 0); + return_val_if_fail(mb != NULL, 0); return mb->lost_events; } @@ -6461,7 +6783,7 @@ struct client *c = (struct client *) client; struct pw_node_activation *a; - spa_return_val_if_fail(c != NULL, 0); + return_val_if_fail(c != NULL, 0); a = c->rt.driver_activation; if (SPA_UNLIKELY(a == NULL))
View file
pipewire-0.3.70.tar.gz/spa/include/spa/node/keys.h -> pipewire-0.3.71.tar.gz/spa/include/spa/node/keys.h
Changed
@@ -31,6 +31,7 @@ #define SPA_KEY_PORT_NAME "port.name" /**< a port name */ #define SPA_KEY_PORT_ALIAS "port.alias" /**< a port alias */ #define SPA_KEY_PORT_MONITOR "port.monitor" /**< this port is a monitor port */ +#define SPA_KEY_PORT_IGNORE_LATENCY "port.ignore-latency" /**< latency ignored by peers */ /**
View file
pipewire-0.3.70.tar.gz/spa/include/spa/node/node.h -> pipewire-0.3.71.tar.gz/spa/include/spa/node/node.h
Changed
@@ -632,6 +632,16 @@ _res; \ }) +#define spa_node_method_fast(o,method,version,...) \ +({ \ + int _res; \ + struct spa_node *_n = o; \ + spa_interface_call_fast_res(&_n->iface, \ + struct spa_node_methods, _res, \ + method, version, ##__VA_ARGS__); \ + _res; \ +}) + #define spa_node_add_listener(n,...) spa_node_method(n, add_listener, 0, __VA_ARGS__) #define spa_node_set_callbacks(n,...) spa_node_method(n, set_callbacks, 0, __VA_ARGS__) #define spa_node_sync(n,...) spa_node_method(n, sync, 0, __VA_ARGS__) @@ -647,7 +657,9 @@ #define spa_node_port_set_io(n,...) spa_node_method(n, port_set_io, 0, __VA_ARGS__) #define spa_node_port_reuse_buffer(n,...) spa_node_method(n, port_reuse_buffer, 0, __VA_ARGS__) +#define spa_node_port_reuse_buffer_fast(n,...) spa_node_method_fast(n, port_reuse_buffer, 0, __VA_ARGS__) #define spa_node_process(n) spa_node_method(n, process, 0) +#define spa_node_process_fast(n) spa_node_method_fast(n, process, 0) /** * \}
View file
pipewire-0.3.70.tar.gz/spa/include/spa/node/utils.h -> pipewire-0.3.71.tar.gz/spa/include/spa/node/utils.h
Changed
@@ -117,8 +117,8 @@ #define spa_node_call(callbacks,method,version,...) \ ({ \ - int _res = -ENOTSUP; \ - spa_callbacks_call_res(callbacks, struct spa_node_callbacks, \ + int _res; \ + spa_callbacks_call_fast_res(callbacks, struct spa_node_callbacks, \ _res, method, version, ##__VA_ARGS__); \ _res; \ })
View file
pipewire-0.3.70.tar.gz/spa/include/spa/param/latency-utils.h -> pipewire-0.3.71.tar.gz/spa/include/spa/param/latency-utils.h
Changed
@@ -21,7 +21,7 @@ #include <spa/param/latency.h> static inline int -spa_latency_info_compare(const struct spa_latency_info *a, struct spa_latency_info *b) +spa_latency_info_compare(const struct spa_latency_info *a, const struct spa_latency_info *b) { if (a->min_quantum == b->min_quantum && a->max_quantum == b->max_quantum &&
View file
pipewire-0.3.70.tar.gz/spa/include/spa/pod/parser.h -> pipewire-0.3.71.tar.gz/spa/include/spa/pod/parser.h
Changed
@@ -455,7 +455,7 @@ const struct spa_pod *pod = NULL; const char *format; - if (ftype == SPA_TYPE_Object) { + if (f && ftype == SPA_TYPE_Object) { uint32_t key = va_arg(args, uint32_t); const struct spa_pod_object *object;
View file
pipewire-0.3.70.tar.gz/spa/include/spa/support/loop.h -> pipewire-0.3.71.tar.gz/spa/include/spa/support/loop.h
Changed
@@ -123,7 +123,7 @@ struct spa_hook_list *_l = l; \ struct spa_hook *_h; \ spa_list_for_each_reverse(_h, &_l->list, link) \ - spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, before, 0); \ + spa_callbacks_call_fast(&_h->cb, struct spa_loop_control_hooks, before, 0); \ }) #define spa_loop_control_hook_after(l) \ @@ -131,7 +131,7 @@ struct spa_hook_list *_l = l; \ struct spa_hook *_h; \ spa_list_for_each(_h, &_l->list, link) \ - spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, after, 0); \ + spa_callbacks_call_fast(&_h->cb, struct spa_loop_control_hooks, after, 0); \ }) /** @@ -212,6 +212,16 @@ _res; \ }) +#define spa_loop_control_method_fast_r(o,method,version,...) \ +({ \ + int _res; \ + struct spa_loop_control *_o = o; \ + spa_interface_call_fast_res(&_o->iface, \ + struct spa_loop_control_methods, _res, \ + method, version, ##__VA_ARGS__); \ + _res; \ +}) + #define spa_loop_control_get_fd(l) spa_loop_control_method_r(l,get_fd,0) #define spa_loop_control_add_hook(l,...) spa_loop_control_method_v(l,add_hook,0,__VA_ARGS__) #define spa_loop_control_enter(l) spa_loop_control_method_v(l,enter,0) @@ -219,6 +229,8 @@ #define spa_loop_control_iterate(l,...) spa_loop_control_method_r(l,iterate,0,__VA_ARGS__) #define spa_loop_control_check(l) spa_loop_control_method_r(l,check,1) +#define spa_loop_control_iterate_fast(l,...) spa_loop_control_method_fast_r(l,iterate,0,__VA_ARGS__) + typedef void (*spa_source_io_func_t) (void *data, int fd, uint32_t mask); typedef void (*spa_source_idle_func_t) (void *data); typedef void (*spa_source_event_func_t) (void *data, uint64_t count);
View file
pipewire-0.3.70.tar.gz/spa/include/spa/support/system.h -> pipewire-0.3.71.tar.gz/spa/include/spa/support/system.h
Changed
@@ -101,13 +101,12 @@ ({ \ volatile int _res = -ENOTSUP; \ struct spa_system *_o = o; \ - spa_interface_call_res(&_o->iface, \ + spa_interface_call_fast_res(&_o->iface, \ struct spa_system_methods, _res, \ method, version, ##__VA_ARGS__); \ _res; \ }) - #define spa_system_read(s,...) spa_system_method_r(s,read,0,__VA_ARGS__) #define spa_system_write(s,...) spa_system_method_r(s,write,0,__VA_ARGS__) #define spa_system_ioctl(s,...) spa_system_method_r(s,ioctl,0,__VA_ARGS__)
View file
pipewire-0.3.70.tar.gz/spa/include/spa/utils/hook.h -> pipewire-0.3.71.tar.gz/spa/include/spa/utils/hook.h
Changed
@@ -162,6 +162,14 @@ _res; \ }) +#define spa_callbacks_call_fast(callbacks,type,method,vers,...) \ +({ \ + const type *_f = (const type *) (callbacks)->funcs; \ + _f->method((callbacks)->data, ## __VA_ARGS__); \ + true; \ +}) + + /** * True if the \a callbacks are of version \a vers, false otherwise */ @@ -194,6 +202,11 @@ res = _f->method((callbacks)->data, ## __VA_ARGS__); \ res; \ }) +#define spa_callbacks_call_fast_res(callbacks,type,res,method,vers,...) \ +({ \ + const type *_f = (const type *) (callbacks)->funcs; \ + res = _f->method((callbacks)->data, ## __VA_ARGS__); \ +}) /** * True if the \a iface's callbacks are of version \a vers, false otherwise @@ -216,6 +229,9 @@ #define spa_interface_call(iface,method_type,method,vers,...) \ spa_callbacks_call(&(iface)->cb,method_type,method,vers,##__VA_ARGS__) +#define spa_interface_call_fast(iface,method_type,method,vers,...) \ + spa_callbacks_call_fast(&(iface)->cb,method_type,method,vers,##__VA_ARGS__) + /** * Invoke method named \a method in the callbacks on the given interface object. * The \a method_type defines the type of the method struct, not the interface @@ -226,6 +242,9 @@ #define spa_interface_call_res(iface,method_type,res,method,vers,...) \ spa_callbacks_call_res(&(iface)->cb,method_type,res,method,vers,##__VA_ARGS__) +#define spa_interface_call_fast_res(iface,method_type,res,method,vers,...) \ + spa_callbacks_call_fast_res(&(iface)->cb,method_type,res,method,vers,##__VA_ARGS__) + /** * \} */
View file
pipewire-0.3.70.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c -> pipewire-0.3.71.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c
Changed
@@ -47,7 +47,8 @@ itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, latency); snprintf(period, sizeof(period), "%lu", this->period_frames); itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-size", period); - snprintf(nperiods, sizeof(nperiods), "%lu", this->buffer_frames / this->period_frames); + snprintf(nperiods, sizeof(nperiods), "%lu", + this->period_frames != 0 ? this->buffer_frames / this->period_frames : 0); itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-num", nperiods); snprintf(headroom, sizeof(headroom), "%u", this->headroom); itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.headroom", headroom);
View file
pipewire-0.3.70.tar.gz/spa/plugins/alsa/alsa-pcm-source.c -> pipewire-0.3.71.tar.gz/spa/plugins/alsa/alsa-pcm-source.c
Changed
@@ -49,7 +49,8 @@ itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, latency); snprintf(period, sizeof(period), "%lu", this->period_frames); itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-size", period); - snprintf(nperiods, sizeof(nperiods), "%lu", this->buffer_frames / this->period_frames); + snprintf(nperiods, sizeof(nperiods), "%lu", + this->period_frames != 0 ? this->buffer_frames / this->period_frames : 0); itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-num", nperiods); snprintf(headroom, sizeof(headroom), "%u", this->headroom); itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.headroom", headroom);
View file
pipewire-0.3.70.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.71.tar.gz/spa/plugins/alsa/alsa-pcm.c
Changed
@@ -1062,6 +1062,10 @@ } } } + if (j > 1) + choice->body.type = SPA_CHOICE_Enum; + spa_pod_builder_pop(b, &f1); + if (j == 0) { char buf1024; int i, r, offs; @@ -1090,9 +1094,6 @@ spa_log_warn(state->log, "%s: access:%s", state->props.device, buf); return -ENOTSUP; } - if (j > 1) - choice->body.type = SPA_CHOICE_Enum; - spa_pod_builder_pop(b, &f1); if ((res = add_rate(state, 1, 1, false, index & 0xffff, next, 0, params, b)) != 1) return res;
View file
pipewire-0.3.70.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.71.tar.gz/spa/plugins/audioconvert/audioadapter.c
Changed
@@ -87,6 +87,7 @@ unsigned int add_listener:1; unsigned int have_format:1; unsigned int started:1; + unsigned int ready:1; unsigned int driver:1; unsigned int async:1; unsigned int passthrough:1; @@ -842,14 +843,16 @@ return res; if ((res = negotiate_buffers(this)) < 0) return res; - this->started = true; + this->ready = true; break; case SPA_NODE_COMMAND_Suspend: this->started = false; + this->ready = false; spa_log_debug(this->log, "%p: suspending", this); break; case SPA_NODE_COMMAND_Pause: this->started = false; + this->ready = false; spa_log_debug(this->log, "%p: pausing", this); break; case SPA_NODE_COMMAND_Flush: @@ -864,20 +867,25 @@ spa_log_error(this->log, "%p: can't send command %d: %s", this, SPA_NODE_COMMAND_ID(command), spa_strerror(res)); - return res; } - if (this->target != this->follower) { + if (res >= 0 && this->target != this->follower) { if ((res = spa_node_send_command(this->follower, command)) < 0) { spa_log_error(this->log, "%p: can't send command %d: %s", this, SPA_NODE_COMMAND_ID(command), spa_strerror(res)); - return res; } } switch (SPA_NODE_COMMAND_ID(command)) { case SPA_NODE_COMMAND_Start: - spa_log_debug(this->log, "%p: started", this); + if (res < 0) { + spa_log_debug(this->log, "%p: start failed", this); + this->ready = false; + configure_format(this, 0, NULL); + } else { + this->started = true; + spa_log_debug(this->log, "%p: started", this); + } break; case SPA_NODE_COMMAND_Suspend: configure_format(this, 0, NULL); @@ -1211,7 +1219,7 @@ spa_log_trace_fp(this->log, "%p: ready %d", this, status); - if (!this->started) { + if (!this->ready) { spa_log_info(this->log, "%p: ready stopped node", this); return -EIO; } @@ -1222,12 +1230,12 @@ if (this->direction == SPA_DIRECTION_OUTPUT) { int retry = 8; while (retry--) { - status = spa_node_process(this->convert); + status = spa_node_process_fast(this->convert); if (status & SPA_STATUS_HAVE_DATA) break; if (status & SPA_STATUS_NEED_DATA) { - status = spa_node_process(this->follower); + status = spa_node_process_fast(this->follower); if (!(status & SPA_STATUS_HAVE_DATA)) break; } @@ -1472,7 +1480,7 @@ if (this->target == this->follower) { if (this->io_position) this->io_rate_match.size = this->io_position->clock.duration; - return spa_node_process(this->follower); + return spa_node_process_fast(this->follower); } if (this->direction == SPA_DIRECTION_INPUT) { @@ -1480,7 +1488,7 @@ * First we run the converter to process the input for the follower * then if it produced data, we run the follower. */ while (retry--) { - status = spa_node_process(this->convert); + status = spa_node_process_fast(this->convert); /* schedule the follower when the converter needed * a recycled buffer */ if (status == -EPIPE || status == 0) @@ -1491,7 +1499,7 @@ if (status & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED)) { /* as long as the converter produced something or * is drained, process the follower. */ - fstatus = spa_node_process(this->follower); + fstatus = spa_node_process_fast(this->follower); if (fstatus < 0) { status = fstatus; break; @@ -1512,7 +1520,7 @@ /* output node (source). First run the converter to make * sure we push out any queued data. Then when it needs * more data, schedule the follower. */ - status = spa_node_process(this->convert); + status = spa_node_process_fast(this->convert); if (status == 0) status = SPA_STATUS_NEED_DATA; else if (status < 0) @@ -1529,7 +1537,7 @@ if (status & SPA_STATUS_NEED_DATA) { /* the converter needs more data, schedule the * follower */ - fstatus = spa_node_process(this->follower); + fstatus = spa_node_process_fast(this->follower); if (fstatus < 0) { status = fstatus; break; @@ -1548,7 +1556,7 @@ spa_node_call_xrun(&this->callbacks, 0, 0, NULL); } else { - status = spa_node_process(this->follower); + status = spa_node_process_fast(this->follower); } spa_log_trace_fp(this->log, "%p: process status:%d", this, status);
View file
pipewire-0.3.70.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.71.tar.gz/spa/plugins/audioconvert/audioconvert.c
Changed
@@ -218,6 +218,7 @@ unsigned int ramp_volume:1; unsigned int drained:1; unsigned int rate_adjust:1; + unsigned int port_ignore_latency:1; uint32_t empty_size; float *empty; @@ -265,7 +266,7 @@ if (full) port->info.change_mask = port->info_all; if (port->info.change_mask) { - struct spa_dict_item items3; + struct spa_dict_item items4; uint32_t n_items = 0; if (PORT_IS_DSP(this, port->direction, port->id)) { @@ -273,6 +274,8 @@ itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_CHANNEL, port->position); if (port->is_monitor) itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_MONITOR, "true"); + if (this->port_ignore_latency) + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_IGNORE_LATENCY, "true"); } else if (PORT_IS_CONTROL(this, port->direction, port->id)) { itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_NAME, "control"); itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "8 bit raw midi"); @@ -1563,7 +1566,7 @@ out->format.info.raw.channels, out->format.info.raw.rate); - if (this->props.resample_disabled && + if (this->props.resample_disabled && !this->resample_peaks && in->format.info.raw.rate != out->format.info.raw.rate) return -EPERM; @@ -1698,7 +1701,7 @@ if (!in->have_format || !out->have_format) return -EINVAL; - rate = this->io_position ? this->io_position->clock.rate.denom : DEFAULT_RATE; + rate = this->io_position ? this->io_position->clock.target_rate.denom : DEFAULT_RATE; /* in DSP mode we always convert to the DSP rate */ if (in->mode == SPA_PARAM_PORT_CONFIG_MODE_dsp) @@ -1853,7 +1856,7 @@ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control)); } else { uint32_t rate = this->io_position ? - this->io_position->clock.rate.denom : DEFAULT_RATE; + this->io_position->clock.target_rate.denom : DEFAULT_RATE; *param = spa_pod_builder_add_object(builder, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, @@ -1974,7 +1977,7 @@ /* collect the other port rate */ dir = &this->dirSPA_DIRECTION_REVERSE(direction); if (dir->mode == SPA_PARAM_PORT_CONFIG_MODE_dsp) - orate = this->io_position ? this->io_position->clock.rate.denom : DEFAULT_RATE; + orate = this->io_position ? this->io_position->clock.target_rate.denom : DEFAULT_RATE; else orate = dir->format.info.raw.rate; @@ -2672,11 +2675,21 @@ } } + resample_passthrough = resample_is_passthrough(this); + /* calculate how many samples we are going to produce. */ if (this->direction == SPA_DIRECTION_INPUT) { /* in split mode we need to output exactly the size of the * duration so we don't try to flush early */ max_out = quant_samples; + if (!in_avail || this->drained) { + n_out = max_out - SPA_MIN(max_out, this->out_offset); + /* no input, ask for more, update rate-match first */ + resample_update_rate_match(this, resample_passthrough, n_out, 0); + spa_log_trace_fp(this->log, "%p: no input drained:%d", this, this->drained); + res |= this->drained ? SPA_STATUS_DRAINED : SPA_STATUS_NEED_DATA; + return res; + } flush_out = false; } else { /* in merge mode we consume one duration of samples and @@ -2777,18 +2790,9 @@ /* we only need to output the remaining samples */ n_out = max_out - SPA_MIN(max_out, this->out_offset); - resample_passthrough = resample_is_passthrough(this); - /* calculate how many samples we are going to consume. */ if (this->direction == SPA_DIRECTION_INPUT) { - if (!in_avail || this->drained) { - /* no input, ask for more, update rate-match first */ - resample_update_rate_match(this, resample_passthrough, n_out, 0); - spa_log_trace_fp(this->log, "%p: no input drained:%d", this, this->drained); - res |= this->drained ? SPA_STATUS_DRAINED : SPA_STATUS_NEED_DATA; - return res; - } - /* else figure out how much input samples we need to consume */ + /* figure out how much input samples we need to consume */ n_samples = SPA_MIN(n_samples, resample_get_in_size(this, resample_passthrough, n_out)); } else { @@ -3144,6 +3148,8 @@ if (s != NULL) this->props.n_channels = parse_position(this->props.channel_map, s, strlen(s)); } + else if (spa_streq(k, SPA_KEY_PORT_IGNORE_LATENCY)) + this->port_ignore_latency = spa_atob(s); else audioconvert_set_param(this, k, s); }
View file
pipewire-0.3.70.tar.gz/spa/plugins/audiomixer/audiomixer.c -> pipewire-0.3.71.tar.gz/spa/plugins/audiomixer/audiomixer.c
Changed
@@ -29,7 +29,7 @@ #define DEFAULT_CHANNELS 2 #define MAX_BUFFERS 64 -#define MAX_PORTS 128 +#define MAX_PORTS 512 #define MAX_CHANNELS 64 #define MAX_ALIGN MIX_OPS_MAX_ALIGN @@ -103,6 +103,9 @@ struct port *in_portsMAX_PORTS; struct port out_ports1; + struct buffer *mix_buffersMAX_PORTS; + const void *mix_datasMAX_PORTS; + int n_formats; struct spa_audio_info format; @@ -737,9 +740,9 @@ outio->buffer_id = SPA_ID_INVALID; } - buffers = alloca(MAX_PORTS * sizeof(struct buffer *)); - datas = alloca(MAX_PORTS * sizeof(void *)); - n_buffers = 0; + buffers = this->mix_buffers; + datas = this->mix_datas; + n_buffers = 0; maxsize = UINT32_MAX;
View file
pipewire-0.3.70.tar.gz/spa/plugins/audiomixer/mixer-dsp.c -> pipewire-0.3.71.tar.gz/spa/plugins/audiomixer/mixer-dsp.c
Changed
@@ -26,7 +26,7 @@ static struct spa_log_topic *log_topic = &SPA_LOG_TOPIC(0, "spa.mixer-dsp"); #define MAX_BUFFERS 64 -#define MAX_PORTS 128 +#define MAX_PORTS 512 #define MAX_ALIGN MIX_OPS_MAX_ALIGN #define PORT_DEFAULT_VOLUME 1.0 @@ -100,6 +100,9 @@ struct port *in_portsMAX_PORTS; struct port out_ports1; + struct buffer *mix_buffersMAX_PORTS; + const void *mix_datasMAX_PORTS; + int n_formats; struct spa_audio_info format; uint32_t stride; @@ -673,8 +676,8 @@ outio->buffer_id = SPA_ID_INVALID; } - buffers = alloca(MAX_PORTS * sizeof(struct buffer *)); - datas = alloca(MAX_PORTS * sizeof(void *)); + buffers = this->mix_buffers; + datas = this->mix_datas; n_buffers = 0; maxsize = UINT32_MAX;
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/a2dp-codec-aac.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/a2dp-codec-aac.c
Changed
@@ -193,12 +193,12 @@ spa_pod_builder_int(b, f->value); } } - if (i == 0) - return -EINVAL; if (i > 1) choice->body.type = SPA_CHOICE_Enum; spa_pod_builder_pop(b, &f1); + if (i == 0) + return -EINVAL; if (SPA_FLAG_IS_SET(conf.channels, AAC_CHANNELS_1 | AAC_CHANNELS_2)) { spa_pod_builder_add(b,
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/a2dp-codec-aptx.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/a2dp-codec-aptx.c
Changed
@@ -247,12 +247,13 @@ spa_pod_builder_int(b, 16000); spa_pod_builder_int(b, 16000); } - if (i == 0) - return -EINVAL; if (i > 1) choice->body.type = SPA_CHOICE_Enum; spa_pod_builder_pop(b, &f1); + if (i == 0) + return -EINVAL; + if (SPA_FLAG_IS_SET(conf.channel_mode, APTX_CHANNEL_MODE_MONO | APTX_CHANNEL_MODE_STEREO)) { spa_pod_builder_add(b, SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(2, 1, 2),
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/a2dp-codec-faststream.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/a2dp-codec-faststream.c
Changed
@@ -148,11 +148,11 @@ spa_pod_builder_int(b, 44100); spa_pod_builder_int(b, 44100); } - if (i == 0) - return -EINVAL; if (i > 1) choice->body.type = SPA_CHOICE_Enum; spa_pod_builder_pop(b, &f1); + if (i == 0) + return -EINVAL; position0 = SPA_AUDIO_CHANNEL_FL; position1 = SPA_AUDIO_CHANNEL_FR;
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/a2dp-codec-ldac.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/a2dp-codec-ldac.c
Changed
@@ -184,12 +184,13 @@ spa_pod_builder_int(b, 96000); spa_pod_builder_int(b, 96000); } - if (i == 0) - return -EINVAL; if (i > 1) choice->body.type = SPA_CHOICE_Enum; spa_pod_builder_pop(b, &f1); + if (i == 0) + return -EINVAL; + if (conf.channel_mode & LDACBT_CHANNEL_MODE_MONO && conf.channel_mode & (LDACBT_CHANNEL_MODE_STEREO | LDACBT_CHANNEL_MODE_DUAL_CHANNEL)) {
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/backend-hsphfpd.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/backend-hsphfpd.c
Changed
@@ -495,7 +495,6 @@ { const char *interface; DBusMessageIter iter, array, dict, data; - const char *agent_codec_key = "AgentCodec"; const char *agent_codec; DBusMessage *r = NULL; @@ -527,9 +526,9 @@ return DBUS_HANDLER_RESULT_NEED_MEMORY; dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array); dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict); - dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &agent_codec_key); + dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &(const char *){ "AgentCodec" }); dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &data); - dbus_message_iter_append_basic(&data, DBUS_TYPE_BOOLEAN, &agent_codec); + dbus_message_iter_append_basic(&data, DBUS_TYPE_STRING, &agent_codec); dbus_message_iter_close_container(&dict, &data); dbus_message_iter_close_container(&array, &dict); dbus_message_iter_close_container(&iter, &array); @@ -1036,7 +1035,7 @@ case DBUS_TYPE_BOOLEAN: { - bool value; + dbus_bool_t value; dbus_message_iter_get_basic(&value_i, &value); if (spa_streq(key, "Connected")) endpoint->connected = value;
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/backend-native.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/backend-native.c
Changed
@@ -684,6 +684,21 @@ static int codec_switch_start_timer(struct rfcomm *rfcomm, int timeout_msec); +static void process_xevent_indicator(struct rfcomm *rfcomm, unsigned int level, unsigned int nlevels) +{ + struct impl *backend = rfcomm->backend; + uint8_t perc; + + spa_log_debug(backend->log, "AT+XEVENT level:%u nlevels:%u", level, nlevels); + + if (nlevels <= 1) + return; + + /* 0 <= level < nlevels */ + perc = SPA_MIN(level, nlevels - 1) * 100 / (nlevels - 1); + spa_bt_device_report_battery_level(rfcomm->device, perc); +} + static void process_iphoneaccev_indicator(struct rfcomm *rfcomm, unsigned int key, unsigned int value) { struct impl *backend = rfcomm->backend; @@ -760,6 +775,8 @@ unsigned int indicator; unsigned int indicator_value; unsigned int value; + unsigned int xevent_level; + unsigned int xevent_nlevels; int xapl_vendor; int xapl_product; int xapl_features; @@ -1069,6 +1086,14 @@ rfcomm_send_reply(rfcomm, "+XAPL=iPhone,%u", SPA_BT_HFP_HF_XAPL_FEATURE_BATTERY_REPORTING); } rfcomm_send_reply(rfcomm, "OK"); + } else if (spa_strstartswith(buf, "AT+XEVENT=USER-AGENT")) { + rfcomm_send_reply(rfcomm, "OK"); + } else if (sscanf(buf, "AT+XEVENT=BATTERY,%u,%u,%*u,%*u", &xevent_level, &xevent_nlevels) == 2) { + process_xevent_indicator(rfcomm, xevent_level, xevent_nlevels); + rfcomm_send_reply(rfcomm, "OK"); + } else if (sscanf(buf, "AT+XEVENT=BATTERY,%u", &xevent_level) == 1) { + process_xevent_indicator(rfcomm, xevent_level + 1, 11); + rfcomm_send_reply(rfcomm, "OK"); } else if (sscanf(buf, "AT+IPHONEACCEV=%u%n", &count, &r) == 1) { if (count < 1 || count > 100) return false; @@ -2769,7 +2794,7 @@ sco_close(backend); if (backend->modemmanager) { - mm_unregister(backend); + mm_unregister(backend->modemmanager); backend->modemmanager = NULL; }
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c
Changed
@@ -13,6 +13,7 @@ #include <spa/param/audio/format.h> #include <spa/param/audio/format-utils.h> +#include <spa/utils/string.h> #include <lc3.h> @@ -37,6 +38,7 @@ struct pac_data { const uint8_t *data; size_t size; + uint32_t locations; }; typedef struct { @@ -47,6 +49,40 @@ uint8_t n_blks; } bap_lc3_t; +static const struct { + uint32_t bit; + enum spa_audio_channel channel; +} channel_bits = { + { LC3_CONFIG_CHNL_FL, SPA_AUDIO_CHANNEL_FL }, + { LC3_CONFIG_CHNL_FR, SPA_AUDIO_CHANNEL_FR }, + { LC3_CONFIG_CHNL_FC, SPA_AUDIO_CHANNEL_FC }, + { LC3_CONFIG_CHNL_LFE, SPA_AUDIO_CHANNEL_LFE }, + { LC3_CONFIG_CHNL_BL, SPA_AUDIO_CHANNEL_RL }, + { LC3_CONFIG_CHNL_BR, SPA_AUDIO_CHANNEL_RR }, + { LC3_CONFIG_CHNL_FLC, SPA_AUDIO_CHANNEL_FLC }, + { LC3_CONFIG_CHNL_FRC, SPA_AUDIO_CHANNEL_FRC }, + { LC3_CONFIG_CHNL_BC, SPA_AUDIO_CHANNEL_BC }, + { LC3_CONFIG_CHNL_LFE2, SPA_AUDIO_CHANNEL_LFE2 }, + { LC3_CONFIG_CHNL_SL, SPA_AUDIO_CHANNEL_SL }, + { LC3_CONFIG_CHNL_SR, SPA_AUDIO_CHANNEL_SR }, + { LC3_CONFIG_CHNL_TFL, SPA_AUDIO_CHANNEL_TFL }, + { LC3_CONFIG_CHNL_TFR, SPA_AUDIO_CHANNEL_TFR }, + { LC3_CONFIG_CHNL_TFC, SPA_AUDIO_CHANNEL_TFC }, + { LC3_CONFIG_CHNL_TC, SPA_AUDIO_CHANNEL_TC }, + { LC3_CONFIG_CHNL_TBL, SPA_AUDIO_CHANNEL_TRL }, + { LC3_CONFIG_CHNL_TBR, SPA_AUDIO_CHANNEL_TRR }, + { LC3_CONFIG_CHNL_TSL, SPA_AUDIO_CHANNEL_TSL }, + { LC3_CONFIG_CHNL_TSR, SPA_AUDIO_CHANNEL_TSR }, + { LC3_CONFIG_CHNL_TBC, SPA_AUDIO_CHANNEL_TRC }, + { LC3_CONFIG_CHNL_BFC, SPA_AUDIO_CHANNEL_BC }, + { LC3_CONFIG_CHNL_BFL, SPA_AUDIO_CHANNEL_BLC }, + { LC3_CONFIG_CHNL_BFR, SPA_AUDIO_CHANNEL_BRC }, + { LC3_CONFIG_CHNL_FLW, SPA_AUDIO_CHANNEL_FLW }, + { LC3_CONFIG_CHNL_FRW, SPA_AUDIO_CHANNEL_FRW }, + { LC3_CONFIG_CHNL_LS, SPA_AUDIO_CHANNEL_SL }, /* is it the right mapping? */ + { LC3_CONFIG_CHNL_RS, SPA_AUDIO_CHANNEL_SR }, /* is it the right mapping? */ +}; + static int write_ltv(uint8_t *dest, uint8_t type, void* value, size_t len) { struct ltv *ltv = (struct ltv *)dest; @@ -136,6 +172,36 @@ return num; } +static int select_channels(uint8_t channels, uint32_t locations, uint32_t *mapping) +{ + unsigned int i, num; + + if (channels & LC3_CHAN_2) + num = 2; + else if (channels & LC3_CHAN_1) + num = 1; + else + return -1; + + if (!locations) { + *mapping = 0; /* mono (omit Audio_Channel_Allocation) */ + return 0; + } + + /* XXX: select some channels, but upper level should tell us what */ + *mapping = 0; + for (i = 0; i < SPA_N_ELEMENTS(channel_bits); ++i) { + if (locations & channel_bitsi.bit) { + *mapping |= channel_bitsi.bit; + --num; + if (num == 0) + break; + } + } + + return 0; +} + static bool select_config(bap_lc3_t *conf, const struct pac_data *pac) { const uint8_t *data = pac->data; @@ -191,12 +257,8 @@ spa_return_val_if_fail(ltv->len == 2, false); { uint8_t channels = ltv->value0; - /* XXX: we hardcode mono or stereo stream */ - if (channels & LC3_CHAN_2) - conf->channels = LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL; - else if (channels & LC3_CHAN_1) - conf->channels = 0; /* mono (omit Audio_Channel_Allocation) */ - else + + if (select_channels(channels, pac->locations, &conf->channels) < 0) return false; } break; @@ -369,10 +431,18 @@ int npacs; bap_lc3_t conf; uint8_t *data = config; + uint32_t locations = 0; + int i; if (caps == NULL) return -EINVAL; + if (settings) { + for (i = 0; i < (int)settings->n_items; ++i) + if (spa_streq(settings->itemsi.key, "bluez5.bap.locations")) + sscanf(settings->itemsi.value, "%"PRIu32, &locations); + } + /* Select best conf from those possible */ npacs = parse_bluez_pacs(caps, caps_size, pacs); if (npacs < 0) @@ -380,6 +450,9 @@ else if (npacs == 0) return -EINVAL; + for (i = 0; i < npacs; ++i) + pacsi.locations = locations; + qsort(pacs, npacs, sizeof(struct pac_data), pac_cmp); if (!select_config(&conf, &pacs0)) @@ -405,8 +478,8 @@ int res1, res2; /* Order selected configurations by preference */ - res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1); - res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2); + res1 = codec->select_config(codec, 0, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1); + res2 = codec->select_config(codec, 0, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2); return conf_cmp(&conf1, res1, &conf2, res2); } @@ -422,38 +495,11 @@ position0 = SPA_AUDIO_CHANNEL_MONO; n_positions = 1; } else { -#define CHANNEL_2_SPACHANNEL(channel,spa_channel) if (channels & channel) positionn_positions++ = spa_channel; - - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FL, SPA_AUDIO_CHANNEL_FL); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FR, SPA_AUDIO_CHANNEL_FR); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FC, SPA_AUDIO_CHANNEL_FC); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LFE, SPA_AUDIO_CHANNEL_LFE); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BL, SPA_AUDIO_CHANNEL_RL); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BR, SPA_AUDIO_CHANNEL_RR); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FLC, SPA_AUDIO_CHANNEL_FLC); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FRC, SPA_AUDIO_CHANNEL_FRC); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BC, SPA_AUDIO_CHANNEL_BC); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LFE2, SPA_AUDIO_CHANNEL_LFE2); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_SL, SPA_AUDIO_CHANNEL_SL); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_SR, SPA_AUDIO_CHANNEL_SR); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFL, SPA_AUDIO_CHANNEL_TFL); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFR, SPA_AUDIO_CHANNEL_TFR); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFC, SPA_AUDIO_CHANNEL_TFC); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TC, SPA_AUDIO_CHANNEL_TC); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBL, SPA_AUDIO_CHANNEL_TRL); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBR, SPA_AUDIO_CHANNEL_TRR); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TSL, SPA_AUDIO_CHANNEL_TSL); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TSR, SPA_AUDIO_CHANNEL_TSR); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBC, SPA_AUDIO_CHANNEL_TRC); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFC, SPA_AUDIO_CHANNEL_BC); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFL, SPA_AUDIO_CHANNEL_BLC); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFR, SPA_AUDIO_CHANNEL_BRC); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FLW, SPA_AUDIO_CHANNEL_FLW); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FRW, SPA_AUDIO_CHANNEL_FRW); - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LS, SPA_AUDIO_CHANNEL_LLFE); /* is it the right mapping? */ - CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_RS, SPA_AUDIO_CHANNEL_RLFE); /* is it the right mapping? */ - -#undef CHANNEL_2_SPACHANNEL + unsigned int i; + + for (i = 0; i < SPA_N_ELEMENTS(channel_bits); ++i) + if (channels & channel_bitsi.bit) + positionn_positions++ = channel_bitsi.channel; } if (n_positions != n_channels)
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/bluez-hardware.conf -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/bluez-hardware.conf
Changed
@@ -44,6 +44,7 @@ { name = "SoundCore 2", no-features = sbc-xq }, # #pipewire-2291 { name = "Tribit MAXSound Plus", no-features = hw-volume }, # #pipewire-1592 { name = "Urbanista Stockholm Plus", no-features = msbc-alt1, msbc-alt1-rtl }, + { name = "WorkTunes Connect", no-features = hw-volume }, # 3M WorkTunes Connect { address = "~^44:5e:cd:", no-features = faststream, a2dp-duplex }, # #pipewire-1756
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/bluez5-dbus.c
Changed
@@ -39,8 +39,6 @@ #include "iso-io.h" #include "defs.h" -#include "bap-codec-caps.h" - static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5"); #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT &log_topic @@ -632,10 +630,14 @@ const struct media_codec *codec; bool sink; const char *err_msg = "Unknown error"; + struct spa_dict settings; + struct spa_dict_item setting_itemsSPA_N_ELEMENTS(monitor->global_setting_items) + 1; + int i; const char *endpoint_path = NULL; uint8_t capsA2DP_MAX_CAPS_SIZE; uint8_t configA2DP_MAX_CAPS_SIZE; + char locations64 = {0}; int caps_size = 0; int conf_size; DBusMessageIter dict; @@ -751,6 +753,8 @@ endpoint_qos.preferred_delay_min = v; else if (spa_streq(key, "PreferredMaximumDelay")) endpoint_qos.preferred_delay_max = v; + else if (spa_streq(key, "Location")) + spa_scnprintf(locations, sizeof(locations), "%"PRIu32, v); else spa_log_info(monitor->log, "Unknown property %s", key); } else { @@ -775,10 +779,12 @@ ep->acceptor = true; } - /* TODO: determine which device the SelectConfiguration() call is associated - * with; it's known here based on the remote endpoint. - */ - conf_size = codec->select_config(codec, 0, caps, caps_size, &monitor->default_audio_info, NULL, config); + for (i = 0; i < (int)monitor->global_settings.n_items; ++i) + setting_itemsi = monitor->global_settings.itemsi; + setting_itemsi = SPA_DICT_ITEM_INIT("bluez5.bap.locations", locations); + settings = SPA_DICT_INIT(setting_items, monitor->global_settings.n_items + 1); + + conf_size = codec->select_config(codec, 0, caps, caps_size, &monitor->default_audio_info, &settings, config); if (conf_size < 0) { spa_log_error(monitor->log, "can't select config: %d (%s)", conf_size, spa_strerror(conf_size)); @@ -2626,9 +2632,16 @@ if (transport->acquire_call) { dbus_pending_call_cancel(transport->acquire_call); + dbus_pending_call_unref(transport->acquire_call); transport->acquire_call = NULL; } + if (transport->volume_call) { + dbus_pending_call_cancel(transport->volume_call); + dbus_pending_call_unref(transport->volume_call); + transport->volume_call = NULL; + } + if (transport->fd >= 0) { spa_bt_player_set_state(transport->device->adapter->dummy_player, SPA_BT_PLAYER_STOPPED); @@ -3189,38 +3202,62 @@ else transport->bap_cis = value; } - else if (spa_streq(key, "Location")) { - uint32_t value; - - if (type != DBUS_TYPE_UINT32) - goto next; - dbus_message_iter_get_basic(&it1, &value); - - spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); - transport->bap_location = value; - } next: dbus_message_iter_next(props_iter); } return 0; } -static int transport_set_property_volume(struct spa_bt_transport *transport, uint16_t value) +static void transport_set_property_volume_reply(DBusPendingCall *pending, void *user_data) +{ + struct spa_bt_transport *transport = user_data; + struct spa_bt_monitor *monitor = transport->monitor; + DBusError err = DBUS_ERROR_INIT; + DBusMessage *r; + + r = dbus_pending_call_steal_reply(pending); + + spa_assert(transport->volume_call == pending); + dbus_pending_call_unref(pending); + transport->volume_call = NULL; + + if (dbus_set_error_from_message(&err, r)) { + spa_log_info(monitor->log, "transport %p: set volume failed for transport %s: %s", + transport, transport->path, err.message); + dbus_error_free(&err); + } else { + spa_log_debug(monitor->log, "transport %p: set volume complete", + transport); + } + + dbus_message_unref(r); +} + +static void transport_set_property_volume(struct spa_bt_transport *transport, uint16_t value) { struct spa_bt_monitor *monitor = transport->monitor; - DBusMessage *m, *r; + DBusMessage *m; DBusMessageIter it2; DBusError err; const char *interface = BLUEZ_MEDIA_TRANSPORT_INTERFACE; const char *name = "Volume"; int res = 0; + dbus_bool_t ret; + + if (transport->volume_call) { + dbus_pending_call_cancel(transport->volume_call); + dbus_pending_call_unref(transport->volume_call); + transport->volume_call = NULL; + } m = dbus_message_new_method_call(BLUEZ_SERVICE, transport->path, DBUS_INTERFACE_PROPERTIES, "Set"); - if (m == NULL) - return -ENOMEM; + if (m == NULL) { + res = -ENOMEM; + goto fail; + } dbus_message_iter_init_append(m, &it0); dbus_message_iter_append_basic(&it0, DBUS_TYPE_STRING, &interface); @@ -3232,25 +3269,27 @@ dbus_error_init(&err); - r = dbus_connection_send_with_reply_and_block(monitor->conn, m, -1, &err); - + ret = dbus_connection_send_with_reply(monitor->conn, m, &transport->volume_call, -1); dbus_message_unref(m); - if (r == NULL) { - spa_log_error(monitor->log, "set volume %u failed for transport %s (%s)", - value, transport->path, err.message); - dbus_error_free(&err); - return -EIO; + if (!ret || !transport->volume_call) { + res = -EIO; + goto fail; } - if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) + ret = dbus_pending_call_set_notify(transport->volume_call, + transport_set_property_volume_reply, transport, NULL); + if (!ret) { res = -EIO; + goto fail; + } - dbus_message_unref(r); - - spa_log_debug(monitor->log, "transport %p: set volume to %d", transport, value); + spa_log_debug(monitor->log, "transport %p: setting volume to %d", transport, value); + return; - return res; +fail: + spa_log_debug(monitor->log, "transport %p: failed to set volume %d: %s", + transport, value, spa_strerror(res)); } static int transport_set_volume(void *data, int id, float volume) @@ -3555,6 +3594,7 @@ if (transport->acquire_call) { dbus_pending_call_cancel(transport->acquire_call); + dbus_pending_call_unref(transport->acquire_call); transport->acquire_call = NULL; } @@ -4187,64 +4227,6 @@ return spa_bt_backend_supports_codec(monitor->backend, device, codec); } -static void bap_update_codec_location(struct spa_bt_transport *t) -{ - uint8_t *data = t->configuration; - size_t size = t->configuration_len; - struct ltv *ltv; - uint32_t location; - int i; - - if (!t->bap_location) - return; - - /* - * Append channel location from BAP transport location, if no channel - * configuration is present in the configuration. - * - * XXX: The codec select_configuration should set the location - * XXX: for mono channels from the device location. We have to do - * XXX: this here because transport location is not know - * XXX: in SelectProperties (TODO: should be fixed in bluez). - */ - - while (size > 0) { - ltv = (struct ltv *)data; - - if (ltv->len < sizeof(struct ltv) || ltv->len >= size) - return; - - if (ltv->type == LC3_TYPE_CHAN) - return; /* already has the channel info */ - - size -= ltv->len + 1; - data += ltv->len + 1; - } - - /* Pick the first location bit set */ - location = t->bap_location; - for (i = 0; i < 32; ++i) { - if (location & (1 << i)) { - location = (1 << i); - break; - } - } - - /* Append LTV value to transport configuration */ - size = t->configuration_len + sizeof(struct ltv) + sizeof(uint32_t); - data = realloc(t->configuration, size); - if (!data) - return; - - ltv = SPA_PTROFF(data, t->configuration_len, struct ltv); - ltv->len = 5; - ltv->type = LC3_TYPE_CHAN; - memcpy(ltv->value, &location, sizeof(uint32_t)); - - t->configuration = data; - t->configuration_len = size; -} - static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn, const char *path, DBusMessage *m, void *userdata) { @@ -4329,9 +4311,6 @@ |= transport->device->a2dp_volume_activeSPA_BT_VOLUME_ID_TX; } - if (codec->bap) - bap_update_codec_location(transport); - if (codec->validate_config) { struct spa_audio_info info; if (codec->validate_config(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0,
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/dbus-monitor.h -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/dbus-monitor.h
Changed
@@ -61,4 +61,4 @@ void dbus_monitor_clear(struct dbus_monitor *monitor); -#endif DBUS_MONITOR_H_ +#endif // DBUS_MONITOR_H_
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/defs.h
Changed
@@ -630,7 +630,6 @@ unsigned int latency_us; uint8_t bap_cig; uint8_t bap_cis; - uint32_t bap_location; uint32_t bap_interval; struct spa_bt_iso_io *iso_io; @@ -639,6 +638,7 @@ struct spa_source volume_timer; struct spa_source release_timer; DBusPendingCall *acquire_call; + DBusPendingCall *volume_call; struct spa_hook_list listener_list; struct spa_callbacks impl;
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/media-source.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/media-source.c
Changed
@@ -123,7 +123,6 @@ unsigned int is_input:1; unsigned int is_duplex:1; unsigned int is_internal:1; - unsigned int use_duplex_source:1; unsigned int node_latency; @@ -148,9 +147,6 @@ uint8_t buffer_read4096; struct timespec now; uint64_t sample_count; - - int duplex_timerfd; - uint64_t duplex_timeout; }; #define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) == 0) @@ -532,34 +528,6 @@ spa_loop_remove_source(this->data_loop, &this->source); } -static int set_duplex_timeout(struct impl *this, uint64_t timeout) -{ - struct itimerspec ts; - ts.it_value.tv_sec = timeout / SPA_NSEC_PER_SEC; - ts.it_value.tv_nsec = timeout % SPA_NSEC_PER_SEC; - ts.it_interval.tv_sec = 0; - ts.it_interval.tv_nsec = 0; - return spa_system_timerfd_settime(this->data_system, - this->duplex_timerfd, 0, &ts, NULL); -} - -static void media_on_duplex_timeout(struct spa_source *source) -{ - struct impl *this = source->data; - uint64_t exp; - int res; - - if ((res = spa_system_timerfd_read(this->data_system, this->duplex_timerfd, &exp)) < 0) { - if (res != -EAGAIN) - spa_log_warn(this->log, "error reading timerfd: %s", spa_strerror(res)); - return; - } - - set_duplex_timeout(this, this->duplex_timeout); - - media_on_ready_read(source); -} - static int setup_matching(struct impl *this) { struct port *port = &this->port; @@ -703,33 +671,13 @@ this->source.data = this; - if (!this->use_duplex_source) { - this->source.fd = this->fd; - this->source.func = media_on_ready_read; - this->source.mask = SPA_IO_IN; - this->source.rmask = 0; - spa_loop_add_source(this->data_loop, &this->source); - } else { - /* - * XXX: For an unknown reason (on Linux 5.13.10), the socket when working with - * XXX: "duplex" stream sometimes stops waking up from the poll, even though - * XXX: you can recv() from the socket with no problem. - * XXX: - * XXX: The reason for this should be found and fixed. - * XXX: To work around this, for now we just do the stupid thing and poll - * XXX: on a timer, chosen so that it's fast enough for the aptX-LL codec - * XXX: we currently support (which sends mSBC data), and also for Opus - * XXX: forward stream. - */ - this->source.fd = this->duplex_timerfd; - this->source.func = media_on_duplex_timeout; - this->source.mask = SPA_IO_IN; - this->source.rmask = 0; - spa_loop_add_source(this->data_loop, &this->source); - - this->duplex_timeout = SPA_NSEC_PER_MSEC * 25/10; - set_duplex_timeout(this, this->duplex_timeout); - } + this->source.fd = this->fd; + this->source.func = media_on_ready_read; + this->source.mask = SPA_IO_IN; + this->source.rmask = 0; + if ((res = spa_loop_add_source(this->data_loop, &this->source)) < 0) + spa_log_error(this->log, "%p: failed to add poll source: %s", this, + spa_strerror(res)); this->sample_count = 0; @@ -807,8 +755,6 @@ this->transport_started = false; - set_duplex_timeout(this, 0); - if (this->source.loop) spa_loop_remove_source(this->data_loop, &this->source); @@ -1633,10 +1579,6 @@ if (this->transport) spa_hook_remove(&this->transport_listener); spa_system_close(this->data_system, this->timerfd); - if (this->duplex_timerfd >= 0) { - spa_system_close(this->data_system, this->duplex_timerfd); - this->duplex_timerfd = -1; - } spa_bt_decode_buffer_clear(&port->buffer); return 0; } @@ -1762,7 +1704,6 @@ this->codec = this->codec->duplex_codec; this->is_input = true; } - this->use_duplex_source = this->is_duplex || (this->codec->duplex_codec != NULL); if (this->codec->bap) this->is_input = this->transport->bap_initiator; @@ -1778,13 +1719,6 @@ this->timerfd = spa_system_timerfd_create(this->data_system, CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); - if (this->use_duplex_source) { - this->duplex_timerfd = spa_system_timerfd_create(this->data_system, - CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); - } else { - this->duplex_timerfd = -1; - } - this->node_latency = 512; set_latency(this, false);
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/modemmanager.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/modemmanager.c
Changed
@@ -18,8 +18,6 @@ }; struct impl { - struct spa_bt_monitor *monitor; - struct spa_log *log; DBusConnection *conn; @@ -44,26 +42,31 @@ 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) { + DBusPendingCall *pending_call; + bool ret = dbus_connection_send_with_reply(this->conn, m, &pending_call, -1); + if (!ret) { spa_log_debug(this->log, "dbus call failure"); - return false; + goto out; } - dbus_ret = dbus_pending_call_set_notify(*pending_return, function, user_data, NULL); - if (!dbus_ret) { + spa_assert(pending_call); + + ret = dbus_pending_call_set_notify(pending_call, function, user_data, NULL); + if (!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; + dbus_pending_call_cancel(pending_call); + dbus_pending_call_unref(pending_call); + goto out; } - return true; + *pending_return = pending_call; + +out: + dbus_message_unref(m); + + return ret; } static int mm_state_to_clcc(struct impl *this, MMCallState state) @@ -122,10 +125,10 @@ MMCallState state; spa_assert(call->pending == pending); - dbus_pending_call_unref(pending); call->pending = NULL; r = dbus_pending_call_steal_reply(pending); + dbus_pending_call_unref(pending); if (r == NULL) return; @@ -421,10 +424,10 @@ DBusMessageIter i, array_i; spa_assert(this->pending == pending); - dbus_pending_call_unref(pending); this->pending = NULL; r = dbus_pending_call_steal_reply(pending); + dbus_pending_call_unref(pending); if (r == NULL) return; @@ -541,8 +544,6 @@ } 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; @@ -644,7 +645,6 @@ 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)) { @@ -761,49 +761,6 @@ 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; @@ -830,10 +787,10 @@ free(data); spa_assert(call->pending == pending); - dbus_pending_call_unref(pending); call->pending = NULL; r = dbus_pending_call_steal_reply(pending); + dbus_pending_call_unref(pending); if (r == NULL) return; @@ -863,10 +820,10 @@ free(data); spa_assert(this->voice_pending == pending); - dbus_pending_call_unref(pending); this->voice_pending = NULL; r = dbus_pending_call_steal_reply(pending); + dbus_pending_call_unref(pending); if (r == NULL) return; @@ -925,7 +882,6 @@ } 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; @@ -983,7 +939,6 @@ } 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; @@ -1049,7 +1004,6 @@ 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; @@ -1109,7 +1063,6 @@ 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; @@ -1182,26 +1135,23 @@ goto fail; } - if (is_dbus_service_available(this, MM_DBUS_SERVICE)) { - DBusMessage *m; + DBusMessage *m = dbus_message_new_method_call(MM_DBUS_SERVICE, "/org/freedesktop/ModemManager1", + DBUS_INTERFACE_OBJECTMANAGER, "GetManagedObjects"); + if (m == NULL) + goto fail; - m = dbus_message_new_method_call(MM_DBUS_SERVICE, "/org/freedesktop/ModemManager1", - DBUS_INTERFACE_OBJECTMANAGER, "GetManagedObjects"); - if (m == NULL) - goto fail; + dbus_message_set_auto_start(m, false); - 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; - } + 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"); + goto fail; } - return this; + return this; fail: - free(this); - return NULL; + free(this); + return NULL; } void mm_unregister(void *data)
View file
pipewire-0.3.70.tar.gz/spa/plugins/bluez5/upower.c -> pipewire-0.3.71.tar.gz/spa/plugins/bluez5/upower.c
Changed
@@ -44,6 +44,7 @@ DBusMessageIter i, variant_i; r = dbus_pending_call_steal_reply(pending); + dbus_pending_call_unref(pending); if (r == NULL) return; @@ -188,49 +189,6 @@ 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), @@ -250,33 +208,33 @@ this->log = log; this->conn = dbus_connection; this->set_battery_level = set_battery_level; - this->user_data = user_data; + 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); - } + DBusMessage *m; + DBusPendingCall *call; - return this; + 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, &(const char *){ UPOWER_DEVICE_INTERFACE }, + DBUS_TYPE_STRING, &(const char *){ "Percentage" }, + DBUS_TYPE_INVALID); + dbus_message_set_auto_start(m, false); + 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; + free(this); + return NULL; } void upower_unregister(void *data)
View file
pipewire-0.3.70.tar.gz/spa/plugins/control/mixer.c -> pipewire-0.3.71.tar.gz/spa/plugins/control/mixer.c
Changed
@@ -23,7 +23,7 @@ #define NAME "control-mixer" #define MAX_BUFFERS 64 -#define MAX_PORTS 128 +#define MAX_PORTS 512 struct buffer { uint32_t id; @@ -70,6 +70,9 @@ struct port *in_portsMAX_PORTS; struct port out_ports1; + struct spa_pod_control *mix_ctrlMAX_PORTS; + struct spa_pod_sequence *mix_seqMAX_PORTS; + int n_formats; unsigned int have_format:1; @@ -624,9 +627,9 @@ return -EPIPE; } - ctrl = alloca(MAX_PORTS * sizeof(struct spa_pod_control *)); - seq = alloca(MAX_PORTS * sizeof(struct spa_pod_sequence *)); - n_seq = 0; + ctrl = this->mix_ctrl; + seq = this->mix_seq; + n_seq = 0; /* collect all sequence pod on input ports */ for (i = 0; i < this->last_port; i++) {
View file
pipewire-0.3.70.tar.gz/spa/plugins/jack/jack-client.c -> pipewire-0.3.71.tar.gz/spa/plugins/jack/jack-client.c
Changed
@@ -18,6 +18,8 @@ client->buffer_size = nframes; + spa_log_trace_fp(client->log, "frames %u", nframes); + spa_jack_client_emit_process(client); return 0; @@ -27,6 +29,8 @@ { struct spa_jack_client *client = arg; + spa_log_warn(client->log, "%p", client); + spa_jack_client_emit_shutdown(client); spa_hook_list_init(&client->listener_list); @@ -67,6 +71,8 @@ spa_hook_list_init(&client->listener_list); + spa_log_info(client->log, "%p: %s", client, client_name); + jack_set_process_callback(client->client, jack_process, client); jack_on_shutdown(client->client, jack_shutdown, client); client->frame_rate = jack_get_sample_rate(client->client); @@ -81,6 +87,8 @@ if (client->client == NULL) return 0; + spa_log_info(client->log, "%p:", client); + spa_jack_client_emit_destroy(client); if (jack_client_close(client->client) != 0)
View file
pipewire-0.3.70.tar.gz/spa/plugins/jack/jack-device.c -> pipewire-0.3.71.tar.gz/spa/plugins/jack/jack-device.c
Changed
@@ -393,6 +393,7 @@ this = (struct impl *) handle; this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + this->client.log = this->log; this->device.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_Device,
View file
pipewire-0.3.70.tar.gz/spa/plugins/jack/jack-sink.c -> pipewire-0.3.71.tar.gz/spa/plugins/jack/jack-sink.c
Changed
@@ -67,7 +67,6 @@ struct spa_node node; struct spa_log *log; - struct spa_loop *data_loop; uint64_t info_all; struct spa_node_info info; @@ -169,6 +168,11 @@ return 0; } +static inline bool is_following(struct impl *impl) +{ + return impl->position && impl->clock && impl->position->clock.id != impl->clock->id; +} + static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) { struct impl *this = object; @@ -235,15 +239,18 @@ if (full) this->info.change_mask = this->info_all; if (this->info.change_mask) { - struct spa_dict_item items5; + struct spa_dict_item items8; char latency64; snprintf(latency, sizeof(latency), "%d/%d", this->client->buffer_size, this->client->frame_rate); items0 = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Sink"); - items1 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_NAME, "JACK System"); + items1 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_NAME, "JACK Sink"); items2 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true"); items3 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_PAUSE_ON_IDLE, "false"); - items4 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_LATENCY, latency); + items4 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_ALWAYS_PROCESS, "true"); + items5 = SPA_DICT_ITEM_INIT("priority.driver", "30001"); + items6 = SPA_DICT_ITEM_INIT("node.group", "jack-group"); + items7 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_LATENCY, latency); this->info.props = &SPA_DICT_INIT_ARRAY(items); spa_node_emit_info(&this->hooks, &this->info); this->info.change_mask = old; @@ -319,6 +326,9 @@ { struct impl *this = data; + if (is_following(this)) + return; + if (this->clock) { struct spa_io_clock *c = this->clock; c->nsec = this->client->current_usecs * SPA_NSEC_PER_USEC; @@ -755,10 +765,8 @@ spa_memcpy(dst, src->data, n_frames * port->stride); io->status = SPA_STATUS_NEED_DATA; - - res |= SPA_STATUS_NEED_DATA; } - return res; + return res | SPA_STATUS_NEED_DATA; } static const struct spa_node_methods impl_node = {
View file
pipewire-0.3.70.tar.gz/spa/plugins/jack/jack-source.c -> pipewire-0.3.71.tar.gz/spa/plugins/jack/jack-source.c
Changed
@@ -69,7 +69,6 @@ struct spa_node node; struct spa_log *log; - struct spa_loop *data_loop; uint64_t info_all; struct spa_node_info info; @@ -263,15 +262,18 @@ if (full) this->info.change_mask = this->info_all; if (this->info.change_mask) { - struct spa_dict_item items5; + struct spa_dict_item items8; char latency64; snprintf(latency, sizeof(latency), "%d/%d", this->client->buffer_size, this->client->frame_rate); items0 = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Source"); - items1 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_NAME, "JACK System"); + items1 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_NAME, "JACK Source"); items2 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true"); items3 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_PAUSE_ON_IDLE, "false"); - items4 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_LATENCY, latency); + items4 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_ALWAYS_PROCESS, "true"); + items5 = SPA_DICT_ITEM_INIT("priority.driver", "30000"); + items6 = SPA_DICT_ITEM_INIT("node.group", "jack-group"); + items7 = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_LATENCY, latency); this->info.props = &SPA_DICT_INIT_ARRAY(items); spa_node_emit_info(&this->hooks, &this->info); this->info.change_mask = old; @@ -342,15 +344,24 @@ return 0; } +static inline bool is_following(struct impl *impl) +{ + return impl->position && impl->clock && impl->position->clock.id != impl->clock->id; +} + static void client_process(void *data) { struct impl *this = data; int res; + if (is_following(this)) + return; + + spa_log_trace_fp(this->log, "%p, process", this); + res = spa_node_process(&this->node); - if (res != SPA_STATUS_OK) - spa_node_call_ready(&this->callbacks, res); + spa_node_call_ready(&this->callbacks, res); } static const struct spa_jack_client_events client_events = { @@ -780,7 +791,7 @@ res |= SPA_STATUS_HAVE_DATA; } - return res; + return res | SPA_STATUS_HAVE_DATA; } static const struct spa_node_methods impl_node = {
View file
pipewire-0.3.70.tar.gz/spa/plugins/support/loop.c -> pipewire-0.3.71.tar.gz/spa/plugins/support/loop.c
Changed
@@ -319,6 +319,8 @@ void *data) { struct impl *impl = object; + spa_return_if_fail(SPA_CALLBACK_CHECK(hooks, before, 0)); + spa_return_if_fail(SPA_CALLBACK_CHECK(hooks, after, 0)); spa_hook_list_append(&impl->hooks_list, hook, hooks, data); } @@ -336,7 +338,7 @@ spa_return_if_fail(pthread_equal(impl->thread, thread_id)); impl->enter_count++; } - spa_log_trace(impl->log, "%p: enter %p", impl, (void *) impl->thread); + spa_log_trace_fp(impl->log, "%p: enter %p", impl, (void *) impl->thread); } static void loop_leave(void *object) @@ -347,7 +349,7 @@ spa_return_if_fail(impl->enter_count > 0); spa_return_if_fail(pthread_equal(impl->thread, thread_id)); - spa_log_trace(impl->log, "%p: leave %p", impl, (void *) impl->thread); + spa_log_trace_fp(impl->log, "%p: leave %p", impl, (void *) impl->thread); if (--impl->enter_count == 0) { impl->thread = 0; @@ -397,7 +399,7 @@ } } -static int loop_iterate(void *object, int timeout) +static int loop_iterate_cancel(void *object, int timeout) { struct impl *impl = object; struct spa_poll_event epMAX_EP, *e; @@ -444,6 +446,52 @@ return nfds; } +static int loop_iterate(void *object, int timeout) +{ + struct impl *impl = object; + struct spa_poll_event epMAX_EP, *e; + int i, nfds; + + impl->polling = true; + spa_loop_control_hook_before(&impl->hooks_list); + + nfds = spa_system_pollfd_wait(impl->system, impl->poll_fd, ep, SPA_N_ELEMENTS(ep), timeout); + + spa_loop_control_hook_after(&impl->hooks_list); + impl->polling = false; + + /* first we set all the rmasks, then call the callbacks. The reason is that + * some callback might also want to look at other sources it manages and + * can then reset the rmask to suppress the callback */ + for (i = 0; i < nfds; i++) { + struct spa_source *s = epi.data; + + s->rmask = epi.events; + /* already active in another iteration of the loop, + * remove it from that iteration */ + if (SPA_UNLIKELY(e = s->priv)) + e->data = NULL; + s->priv = &epi; + } + + if (SPA_UNLIKELY(!spa_list_is_empty(&impl->destroy_list))) + process_destroy(impl); + + for (i = 0; i < nfds; i++) { + struct spa_source *s = epi.data; + if (SPA_LIKELY(s && s->rmask)) + s->func(s); + } + for (i = 0; i < nfds; i++) { + struct spa_source *s = epi.data; + if (SPA_LIKELY(s)) { + s->rmask = 0; + s->priv = NULL; + } + } + return nfds; +} + static void source_io_func(struct spa_source *source) { struct source_impl *s = SPA_CONTAINER_OF(source, struct source_impl, source); @@ -826,6 +874,16 @@ .invoke = loop_invoke, }; +static const struct spa_loop_control_methods impl_loop_control_cancel = { + SPA_VERSION_LOOP_CONTROL_METHODS, + .get_fd = loop_get_fd, + .add_hook = loop_add_hook, + .enter = loop_enter, + .leave = loop_leave, + .iterate = loop_iterate_cancel, + .check = loop_check, +}; + static const struct spa_loop_control_methods impl_loop_control = { SPA_VERSION_LOOP_CONTROL_METHODS, .get_fd = loop_get_fd, @@ -908,6 +966,7 @@ uint32_t n_support) { struct impl *impl; + const char *str; int res; spa_return_val_if_fail(factory != NULL, -EINVAL); @@ -930,6 +989,12 @@ SPA_VERSION_LOOP_UTILS, &impl_loop_utils, impl); + if (info) { + if ((str = spa_dict_lookup(info, "loop.cancel")) != NULL && + spa_atob(str)) + impl->control.iface.cb.funcs = &impl_loop_control_cancel; + } + impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); spa_log_topic_init(impl->log, &log_topic); impl->system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System);
View file
pipewire-0.3.70.tar.gz/spa/plugins/support/null-audio-sink.c -> pipewire-0.3.71.tar.gz/spa/plugins/support/null-audio-sink.c
Changed
@@ -41,6 +41,7 @@ uint32_t posSPA_AUDIO_MAX_CHANNELS; char clock_name64; unsigned int debug:1; + unsigned int driver:1; }; static void reset_props(struct props *props) @@ -51,6 +52,7 @@ props->n_pos = 0; strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name)); props->debug = false; + props->driver = true; } #define DEFAULT_CHANNELS 2 @@ -356,9 +358,6 @@ return 0; } -static const struct spa_dict_item node_info_items = { - { SPA_KEY_NODE_DRIVER, "true" }, -}; static void emit_node_info(struct impl *this, bool full) { @@ -366,6 +365,9 @@ if (full) this->info.change_mask = this->info_all; if (this->info.change_mask) { + const struct spa_dict_item node_info_items = { + { SPA_KEY_NODE_DRIVER, this->props.driver ? "true" : "false" }, + }; this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items); spa_node_emit_info(&this->hooks, &this->info); this->info.change_mask = old; @@ -979,6 +981,8 @@ this->props.channels = atoi(s); } else if (spa_streq(k, SPA_KEY_AUDIO_RATE)) { this->props.rate = atoi(s); + } else if (spa_streq(k, SPA_KEY_NODE_DRIVER)) { + this->props.driver = spa_atob(s); } else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) { parse_position(this, s, strlen(s)); } else if (spa_streq(k, "clock.name")) {
View file
pipewire-0.3.70.tar.gz/src/daemon/jack.conf.in -> pipewire-0.3.71.tar.gz/src/daemon/jack.conf.in
Changed
@@ -72,6 +72,7 @@ #node.force-quantum = 0 #jack.show-monitor = true #jack.merge-monitor = true + #jack.show-midi = true #jack.short-name = false #jack.filter-name = false #jack.filter-char = " " @@ -86,6 +87,8 @@ #jack.default-as-system = false #jack.fix-midi-events = true #jack.global-buffer-size = false + #jack.max-client-ports = 768 + #jack.fill-aliases = false } # client specific properties
View file
pipewire-0.3.70.tar.gz/src/daemon/pipewire-aes67.conf.in -> pipewire-0.3.71.tar.gz/src/daemon/pipewire-aes67.conf.in
Changed
@@ -36,17 +36,29 @@ { name = libpipewire-module-protocol-native } { name = libpipewire-module-client-node } { name = libpipewire-module-adapter } - { name = libpipewire-module-rtp-source + { name = libpipewire-module-rtp-sap args = { + local.ifname = eth0 sap.ip = 239.255.255.255 sap.port = 9875 - sess.latency.msec = 10 - local.ifname = eth0 - stream.props = { - media.class = "Audio/Source" - node.virtual = false - device.api = aes67 - } + + stream.rules = + { + matches = + { + rtp.session = "~.*" + } + + actions = { + create-stream = { + node.virtual = false + media.class = "Audio/Source" + device.api = aes67 + sess.latency.msec = 10 + } + } + } + } }
View file
pipewire-0.3.70.tar.gz/src/daemon/pipewire-pulse.conf.in -> pipewire-0.3.71.tar.gz/src/daemon/pipewire-pulse.conf.in
Changed
@@ -95,6 +95,7 @@ # client.access = "restricted" # permissions for clients #} + #server.dbus-name = "org.pulseaudio.Server" #pulse.min.req = 128/48000 # 2.7ms #pulse.default.req = 960/48000 # 20 milliseconds #pulse.min.frag = 128/48000 # 2.7ms
View file
pipewire-0.3.70.tar.gz/src/modules/meson.build -> pipewire-0.3.71.tar.gz/src/modules/meson.build
Changed
@@ -15,6 +15,8 @@ 'module-example-source.c', 'module-fallback-sink.c', 'module-filter-chain.c', + 'module-jack-tunnel.c', + 'module-jackdbus-detect.c', 'module-link-factory.c', 'module-loopback.c', 'module-metadata.c', @@ -158,6 +160,33 @@ dependencies : mathlib, dl_lib, pipewire_dep, audioconvert_dep, ) +build_module_jack_tunnel = jack_dep.found() +if build_module_jack_tunnel + pipewire_module_jack_tunnel = shared_library('pipewire-module-jack-tunnel', + 'module-jack-tunnel.c' , + include_directories : configinc, + install : true, + install_dir : modules_install_dir, + install_rpath: modules_install_dir, + dependencies : mathlib, dl_lib, pipewire_dep, + ) + build_module_jackdbus_detect = dbus_dep.found() + if build_module_jackdbus_detect + pipewire_module_jack_tunnel = shared_library('pipewire-module-jackdbus-detect', + 'module-jackdbus-detect.c' , + include_directories : configinc, + install : true, + install_dir : modules_install_dir, + install_rpath: modules_install_dir, + dependencies : mathlib, dl_lib, pipewire_dep, dbus_dep, + ) + endif +endif + +summary({'jack-tunnel': build_module_jack_tunnel}, bool_yn: true, section: 'Optional Modules') + + + pipewire_module_profiler = shared_library('pipewire-module-profiler', 'module-profiler.c', 'module-profiler/protocol-native.c', , @@ -273,6 +302,7 @@ 'module-protocol-pulse/modules/module-always-sink.c', 'module-protocol-pulse/modules/module-combine-sink.c', 'module-protocol-pulse/modules/module-echo-cancel.c', + 'module-protocol-pulse/modules/module-jackdbus-detect.c', 'module-protocol-pulse/modules/module-ladspa-sink.c', 'module-protocol-pulse/modules/module-ladspa-source.c', 'module-protocol-pulse/modules/module-loopback.c',
View file
pipewire-0.3.70.tar.gz/src/modules/module-access.c -> pipewire-0.3.71.tar.gz/src/modules/module-access.c
Changed
@@ -9,6 +9,7 @@ #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> +#include <limits.h> #include "config.h" @@ -131,41 +132,57 @@ struct spa_hook module_listener; }; -static int check_cmdline(struct pw_impl_client *client, int pid, const char *str) +static int get_exe_name(int pid, char *buf, size_t buf_size) { - char path2048, key1024; - ssize_t len; - int fd, res; - struct spa_json it2; + char path256; + struct stat s1, s2; + int res; + + /* + * Find executable name, checking it is an existing file + * (in the current namespace). + */ + +#if defined(__linux__) + spa_scnprintf(path, sizeof(path), "/proc/%u/exe", pid); +#elif defined(__FreeBSD__) || defined(__MidnightBSD__) + spa_scnprintf(path, sizeof(path), "/proc/%u/file", pid); +#else + return -ENOTSUP; +#endif - sprintf(path, "/proc/%u/cmdline", pid); + res = readlink(path, buf, buf_size); + if (res < 0) + return -errno; + if ((size_t)res >= buf_size) + return -E2BIG; + bufres = '\0'; - fd = open(path, O_RDONLY); - if (fd < 0) { - res = -errno; - goto exit; - } - if ((len = read(fd, path, sizeof(path)-1)) < 0) { - res = -errno; - goto exit_close; - } - pathlen = '\0'; + /* Check the file exists (= not deleted, and is in current namespace) */ + if (stat(path, &s1) != 0 || stat(buf, &s2) != 0) + return -errno; + if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino) + return -ENXIO; + + return 0; +} + +static int check_exe(struct pw_impl_client *client, const char *path, const char *str) +{ + char key1024; + int res; + struct spa_json it2; spa_json_init(&it0, str, strlen(str)); if ((res = spa_json_enter_array(&it0, &it1)) <= 0) - goto exit_close; + return res; while (spa_json_get_string(&it1, key, sizeof(key)) > 0) { - if (spa_streq(path, key)) { - res = 1; - goto exit_close; - } + if (spa_streq(path, key)) + return 1; } - res = 0; -exit_close: - close(fd); -exit: - return res; + + return 0; } static void @@ -174,6 +191,7 @@ struct impl *impl = data; struct pw_permission permissions1; struct spa_dict_item items2; + char exe_pathPATH_MAX; const struct pw_properties *props; const char *str, *access; char *flatpak_app_id = NULL; @@ -195,10 +213,17 @@ goto granted; } else { pw_log_info("client %p has trusted pid %d", client, pid); + if ((res = get_exe_name(pid, exe_path, sizeof(exe_path))) >= 0) { + pw_log_info("client %p has trusted exe path '%s'", client, exe_path); + } else { + pw_log_info("client %p has no trusted exe path: %s", + client, spa_strerror(res)); + exe_path0 = '\0'; + } } if (impl->properties && (str = pw_properties_get(impl->properties, "access.allowed")) != NULL) { - res = check_cmdline(client, pid, str); + res = check_exe(client, exe_path, str); if (res < 0) { pw_log_warn("%p: client %p allowed check failed: %s", impl, client, spa_strerror(res)); @@ -209,7 +234,7 @@ } if (impl->properties && (str = pw_properties_get(impl->properties, "access.rejected")) != NULL) { - res = check_cmdline(client, pid, str); + res = check_exe(client, exe_path, str); if (res < 0) { pw_log_warn("%p: client %p rejected check failed: %s", impl, client, spa_strerror(res)); @@ -221,7 +246,7 @@ } if (impl->properties && (str = pw_properties_get(impl->properties, "access.restricted")) != NULL) { - res = check_cmdline(client, pid, str); + res = check_exe(client, exe_path, str); if (res < 0) { pw_log_warn("%p: client %p restricted check failed: %s", impl, client, spa_strerror(res));
View file
pipewire-0.3.70.tar.gz/src/modules/module-client-node/client-node.c -> pipewire-0.3.71.tar.gz/src/modules/module-client-node/client-node.c
Changed
@@ -31,13 +31,14 @@ #define MAX_BUFFERS 64 #define MAX_METAS 16u #define MAX_DATAS 64u -#define MAX_AREAS 2048 +#define AREA_SIZE (4096u / sizeof(struct spa_io_buffers)) +#define MAX_AREAS 32 -#define CHECK_FREE_PORT(this,d,p) (p <= pw_map_get_size(&this->portsd) && !CHECK_PORT(this,d,p)) -#define CHECK_PORT(this,d,p) (pw_map_lookup(&this->portsd, p) != NULL) -#define GET_PORT(this,d,p) (pw_map_lookup(&this->portsd, p)) +#define CHECK_FREE_PORT(impl,d,p) (p <= pw_map_get_size(&impl->portsd) && !CHECK_PORT(impl,d,p)) +#define CHECK_PORT(impl,d,p) (pw_map_lookup(&impl->portsd, p) != NULL) +#define GET_PORT(impl,d,p) (pw_map_lookup(&impl->portsd, p)) -#define CHECK_PORT_BUFFER(this,b,p) (b < p->n_buffers) +#define CHECK_PORT_BUFFER(impl,b,p) (b < p->n_buffers) struct buffer { struct spa_buffer *outbuf; @@ -63,7 +64,6 @@ struct port { struct pw_impl_port *port; - struct node *node; struct impl *impl; enum spa_direction direction; @@ -82,10 +82,12 @@ struct pw_array mix; }; -struct node { - struct spa_node node; +struct impl { + struct pw_impl_client_node this; - struct impl *impl; + struct pw_context *context; + + struct spa_node node; struct spa_log *log; struct spa_loop *data_loop; @@ -98,24 +100,15 @@ struct pw_impl_client *client; struct spa_source data_source; - int writefd; struct pw_map ports2; struct port dummy; struct params params; -}; - -struct impl { - struct pw_impl_client_node this; - - struct pw_context *context; - - struct node node; struct pw_map io_map; - struct pw_memblock *io_areas; + struct pw_array io_areas; struct pw_memblock *activation; @@ -127,9 +120,6 @@ uint32_t bind_node_version; uint32_t bind_node_id; - - int fds2; - int other_fds2; }; #define pw_client_node_resource(r,m,v,...) \ @@ -239,10 +229,8 @@ return mix; } -static void clear_data(struct node *this, struct spa_data *d) +static void clear_data(struct impl *impl, struct spa_data *d) { - struct impl *impl = this->impl; - switch (d->type) { case SPA_DATA_MemId: { @@ -250,7 +238,7 @@ struct pw_memblock *m; id = SPA_PTR_TO_UINT32(d->data); - m = pw_mempool_find_id(this->client->pool, id); + m = pw_mempool_find_id(impl->client->pool, id); if (m) { pw_log_debug("%p: mem %d", impl, m->id); pw_memblock_unref(m); @@ -267,18 +255,18 @@ } } -static int clear_buffers(struct node *this, struct mix *mix) +static int clear_buffers(struct impl *impl, struct mix *mix) { uint32_t i, j; for (i = 0; i < mix->n_buffers; i++) { struct buffer *b = &mix->buffersi; - spa_log_debug(this->log, "%p: clear buffer %d", this, i); + spa_log_debug(impl->log, "%p: clear buffer %d", impl, i); for (j = 0; j < b->buffer.n_datas; j++) { struct spa_data *d = &b->datasj; - clear_data(this, d); + clear_data(impl, d); } pw_memblock_unref(b->mem); } @@ -286,13 +274,13 @@ return 0; } -static void mix_clear(struct node *this, struct mix *mix) +static void mix_clear(struct impl *impl, struct mix *mix) { struct port *port = mix->port; if (!mix->valid) return; - do_port_use_buffers(this->impl, port->direction, port->id, + do_port_use_buffers(impl, port->direction, port->id, mix->id, 0, NULL, 0); mix->valid = false; } @@ -301,14 +289,14 @@ uint32_t id, uint32_t start, uint32_t num, const struct spa_pod *filter) { - struct node *this = object; + struct impl *impl = object; uint8_t buffer1024; struct spa_pod_dynamic_builder b; struct spa_result_node_params result; uint32_t count = 0; bool found = false; - spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(impl != NULL, -EINVAL); spa_return_val_if_fail(num != 0, -EINVAL); result.id = id; @@ -318,10 +306,10 @@ struct spa_pod *param; result.index = result.next++; - if (result.index >= this->params.n_params) + if (result.index >= impl->params.n_params) break; - param = this->params.paramsresult.index; + param = impl->params.paramsresult.index; if (param == NULL || !spa_pod_is_object_id(param, id)) continue; @@ -333,8 +321,8 @@ spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); if (spa_pod_filter(&b.b, &result.param, param, filter) == 0) { - pw_log_debug("%p: %d param %u", this, seq, result.index); - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); + pw_log_debug("%p: %d param %u", impl, seq, result.index); + spa_node_emit_result(&impl->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); count++; } spa_pod_dynamic_builder_clean(&b); @@ -348,20 +336,19 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param) { - struct node *this = object; + struct impl *impl = object; - spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(impl != NULL, -EINVAL); - if (this->resource == NULL) + if (impl->resource == NULL) return param == NULL ? 0 : -EIO; - return pw_client_node_resource_set_param(this->resource, id, flags, param); + return pw_client_node_resource_set_param(impl->resource, id, flags, param); } static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) { - struct node *this = object; - struct impl *impl = this->impl; + struct impl *impl = object; struct pw_memmap *mm, *old; uint32_t memid, mem_offset, mem_size; uint32_t tag5 = { impl->node_id, id, }; @@ -369,10 +356,10 @@ if (impl->this.flags & 1) return 0; - old = pw_mempool_find_tag(this->client->pool, tag, sizeof(tag)); + old = pw_mempool_find_tag(impl->client->pool, tag, sizeof(tag)); if (data) { - mm = pw_mempool_import_map(this->client->pool, + mm = pw_mempool_import_map(impl->client->pool, impl->context->pool, data, size, tag); if (mm == NULL) return -errno; @@ -387,10 +374,10 @@ } pw_memmap_free(old); - if (this->resource == NULL) + if (impl->resource == NULL) return data == NULL ? 0 : -EIO; - return pw_client_node_resource_set_io(this->resource, + return pw_client_node_resource_set_io(impl->resource, id, memid, mem_offset, mem_size); @@ -398,23 +385,26 @@ static int impl_node_send_command(void *object, const struct spa_command *command) { - struct node *this = object; + struct impl *impl = object; + uint32_t id; - spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(impl != NULL, -EINVAL); spa_return_val_if_fail(command != NULL, -EINVAL); - pw_log_debug("%p: send command %d", this, SPA_COMMAND_TYPE(command)); + id = SPA_NODE_COMMAND_ID(command); + pw_log_debug("%p: send command %d (%s)", impl, id, + spa_debug_type_find_name(spa_type_node_command_id, id)); - if (this->resource == NULL) + if (impl->resource == NULL) return -EIO; - return pw_client_node_resource_command(this->resource, command); + return pw_client_node_resource_command(impl->resource, command); } -static void emit_port_info(struct node *this, struct port *port) +static void emit_port_info(struct impl *impl, struct port *port) { - spa_node_emit_port_info(&this->hooks, + spa_node_emit_port_info(&impl->hooks, port->direction, port->id, &port->info); } @@ -423,23 +413,23 @@ const struct spa_node_events *events, void *data) { - struct node *this = object; + struct impl *impl = object; struct spa_hook_list save; union pw_map_item *item; - spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(impl != NULL, -EINVAL); - spa_hook_list_isolate(&this->hooks, &save, listener, events, data); + spa_hook_list_isolate(&impl->hooks, &save, listener, events, data); - pw_array_for_each(item, &this->portsSPA_DIRECTION_INPUT.items) { + pw_array_for_each(item, &impl->portsSPA_DIRECTION_INPUT.items) { if (item->data) - emit_port_info(this, item->data); + emit_port_info(impl, item->data); } - pw_array_for_each(item, &this->portsSPA_DIRECTION_OUTPUT.items) { + pw_array_for_each(item, &impl->portsSPA_DIRECTION_OUTPUT.items) { if (item->data) - emit_port_info(this, item->data); + emit_port_info(impl, item->data); } - spa_hook_list_join(&this->hooks, &save); + spa_hook_list_join(&impl->hooks, &save); return 0; } @@ -449,11 +439,11 @@ const struct spa_node_callbacks *callbacks, void *data) { - struct node *this = object; + struct impl *impl = object; - spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(impl != NULL, -EINVAL); - this->callbacks = SPA_CALLBACKS_INIT(callbacks, data); + impl->callbacks = SPA_CALLBACKS_INIT(callbacks, data); return 0; } @@ -461,20 +451,20 @@ static int impl_node_sync(void *object, int seq) { - struct node *this = object; + struct impl *impl = object; - spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(impl != NULL, -EINVAL); - pw_log_debug("%p: sync", this); + pw_log_debug("%p: sync", impl); - if (this->resource == NULL) + if (impl->resource == NULL) return -EIO; - return pw_resource_ping(this->resource, seq); + return pw_resource_ping(impl->resource, seq); } static void -do_update_port(struct node *this, +do_update_port(struct impl *impl, struct port *port, uint32_t change_mask, uint32_t n_params, @@ -482,7 +472,7 @@ const struct spa_port_info *info) { if (change_mask & PW_CLIENT_NODE_PORT_UPDATE_PARAMS) { - spa_log_debug(this->log, "%p: port %u update %d params", this, port->id, n_params); + spa_log_debug(impl->log, "%p: port %u update %d params", impl, port->id, n_params); update_params(&port->params, n_params, params); } @@ -501,60 +491,60 @@ } port->info.n_params = 0; port->info.params = NULL; - spa_node_emit_port_info(&this->hooks, port->direction, port->id, info); + spa_node_emit_port_info(&impl->hooks, port->direction, port->id, info); } } } static void -clear_port(struct node *this, struct port *port) +clear_port(struct impl *impl, struct port *port) { struct mix *mix; - spa_log_debug(this->log, "%p: clear port %p", this, port); + spa_log_debug(impl->log, "%p: clear port %p", impl, port); - do_update_port(this, port, + do_update_port(impl, port, PW_CLIENT_NODE_PORT_UPDATE_PARAMS | PW_CLIENT_NODE_PORT_UPDATE_INFO, 0, NULL, NULL); pw_array_for_each(mix, &port->mix) - mix_clear(this, mix); + mix_clear(impl, mix); pw_array_clear(&port->mix); pw_array_init(&port->mix, sizeof(struct mix) * 2); - pw_map_insert_at(&this->portsport->direction, port->id, NULL); + pw_map_insert_at(&impl->portsport->direction, port->id, NULL); if (!port->removed) - spa_node_emit_port_info(&this->hooks, port->direction, port->id, NULL); + spa_node_emit_port_info(&impl->hooks, port->direction, port->id, NULL); } static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, const struct spa_dict *props) { - struct node *this = object; + struct impl *impl = object; - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_FREE_PORT(this, direction, port_id), -EINVAL); + spa_return_val_if_fail(impl != NULL, -EINVAL); + spa_return_val_if_fail(CHECK_FREE_PORT(impl, direction, port_id), -EINVAL); - if (this->resource == NULL) + if (impl->resource == NULL) return -EIO; - return pw_client_node_resource_add_port(this->resource, direction, port_id, props); + return pw_client_node_resource_add_port(impl->resource, direction, port_id, props); } static int impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) { - struct node *this = object; + struct impl *impl = object; - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + spa_return_val_if_fail(impl != NULL, -EINVAL); + spa_return_val_if_fail(CHECK_PORT(impl, direction, port_id), -EINVAL); - if (this->resource == NULL) + if (impl->resource == NULL) return -EIO; - return pw_client_node_resource_remove_port(this->resource, direction, port_id); + return pw_client_node_resource_remove_port(impl->resource, direction, port_id); } static int @@ -563,7 +553,7 @@ uint32_t id, uint32_t start, uint32_t num, const struct spa_pod *filter) { - struct node *this = object; + struct impl *impl = object; struct port *port; uint8_t buffer1024; struct spa_pod_dynamic_builder b; @@ -571,14 +561,14 @@ uint32_t count = 0; bool found = false; - spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(impl != NULL, -EINVAL); spa_return_val_if_fail(num != 0, -EINVAL); - port = GET_PORT(this, direction, port_id); + port = GET_PORT(impl, direction, port_id); spa_return_val_if_fail(port != NULL, -EINVAL); pw_log_debug("%p: seq:%d port %d.%d id:%u start:%u num:%u n_params:%d", - this, seq, direction, port_id, id, start, num, port->params.n_params); + impl, seq, direction, port_id, id, start, num, port->params.n_params); result.id = id; result.next = 0; @@ -602,8 +592,8 @@ spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); if (spa_pod_filter(&b.b, &result.param, param, filter) == 0) { - pw_log_debug("%p: %d param %u", this, seq, result.index); - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); + pw_log_debug("%p: %d param %u", impl, seq, result.index); + spa_node_emit_result(&impl->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); count++; } spa_pod_dynamic_builder_clean(&b); @@ -620,28 +610,28 @@ uint32_t id, uint32_t flags, const struct spa_pod *param) { - struct node *this = object; + struct impl *impl = object; struct port *port; struct mix *mix; - spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(impl != NULL, -EINVAL); - port = GET_PORT(this, direction, port_id); + port = GET_PORT(impl, direction, port_id); if(port == NULL) return param == NULL ? 0 : -EINVAL; - pw_log_debug("%p: port %d.%d set param %s %d", this, + pw_log_debug("%p: port %d.%d set param %s %d", impl, direction, port_id, spa_debug_type_find_name(spa_type_param, id), id); if (id == SPA_PARAM_Format) { pw_array_for_each(mix, &port->mix) - clear_buffers(this, mix); + clear_buffers(impl, mix); } - if (this->resource == NULL) + if (impl->resource == NULL) return param == NULL ? 0 : -EIO; - return pw_client_node_resource_port_set_param(this->resource, + return pw_client_node_resource_port_set_param(impl->resource, direction, port_id, id, flags, param); @@ -652,28 +642,27 @@ uint32_t mix_id, uint32_t id, void *data, size_t size) { - struct node *this = &impl->node; uint32_t memid, mem_offset, mem_size; struct port *port; struct mix *mix; uint32_t tag5 = { impl->node_id, direction, port_id, mix_id, id }; struct pw_memmap *mm, *old; - pw_log_debug("%p: %s port %d.%d set io %p %zd", this, + pw_log_debug("%p: %s port %d.%d set io %p %zd", impl, direction == SPA_DIRECTION_INPUT ? "input" : "output", port_id, mix_id, data, size); - port = GET_PORT(this, direction, port_id); + port = GET_PORT(impl, direction, port_id); if (port == NULL) return data == NULL ? 0 : -EINVAL; if ((mix = find_mix(port, mix_id)) == NULL || !mix->valid) return -EINVAL; - old = pw_mempool_find_tag(this->client->pool, tag, sizeof(tag)); + old = pw_mempool_find_tag(impl->client->pool, tag, sizeof(tag)); if (data) { - mm = pw_mempool_import_map(this->client->pool, + mm = pw_mempool_import_map(impl->client->pool, impl->context->pool, data, size, tag); if (mm == NULL) return -errno; @@ -688,10 +677,10 @@ } pw_memmap_free(old); - if (this->resource == NULL) + if (impl->resource == NULL) return data == NULL ? 0 : -EIO; - return pw_client_node_resource_port_set_io(this->resource, + return pw_client_node_resource_port_set_io(impl->resource, direction, port_id, mix_id, id, @@ -721,20 +710,19 @@ struct spa_buffer **buffers, uint32_t n_buffers) { - struct node *this = &impl->node; struct port *p; struct mix *mix; uint32_t i, j; struct pw_client_node_buffer *mb; - p = GET_PORT(this, direction, port_id); + p = GET_PORT(impl, direction, port_id); if (p == NULL) return n_buffers == 0 ? 0 : -EINVAL; if (n_buffers > MAX_BUFFERS) return -ENOSPC; - spa_log_debug(this->log, "%p: %s port %d.%d use buffers %p %u flags:%08x", this, + spa_log_debug(impl->log, "%p: %s port %d.%d use buffers %p %u flags:%08x", impl, direction == SPA_DIRECTION_INPUT ? "input" : "output", port_id, mix_id, buffers, n_buffers, flags); @@ -747,7 +735,7 @@ return -EINVAL; } - clear_buffers(this, mix); + clear_buffers(impl, mix); if (n_buffers > 0) { mb = alloca(n_buffers * sizeof(struct pw_client_node_buffer)); @@ -755,7 +743,7 @@ mb = NULL; } - if (this->resource == NULL) + if (impl->resource == NULL) return n_buffers == 0 ? 0 : -EIO; if (p->destroyed) @@ -797,7 +785,7 @@ if (endptr > SPA_PTROFF(baseptr, mem->size, void)) return -EINVAL; - m = pw_mempool_import_block(this->client->pool, mem); + m = pw_mempool_import_block(impl->client->pool, mem); if (m == NULL) return -errno; @@ -807,7 +795,7 @@ mbi.mem_id = m->id; mbi.offset = SPA_PTRDIFF(baseptr, mem->map->ptr); mbi.size = SPA_PTRDIFF(endptr, baseptr); - spa_log_debug(this->log, "%p: buffer %d %d %d %d", this, i, mbi.mem_id, + spa_log_debug(impl->log, "%p: buffer %d %d %d %d", impl, i, mbi.mem_id, mbi.offset, mbi.size); b->buffer.n_metas = SPA_MIN(buffersi->n_metas, MAX_METAS); @@ -834,8 +822,8 @@ if (d->flags & SPA_DATA_FLAG_WRITABLE) flags |= PW_MEMBLOCK_FLAG_WRITABLE; - spa_log_debug(this->log, "mem %d type:%d fd:%d", j, d->type, (int)d->fd); - m = pw_mempool_import(this->client->pool, + spa_log_debug(impl->log, "mem %d type:%d fd:%d", j, d->type, (int)d->fd); + m = pw_mempool_import(impl->client->pool, flags, d->type, d->fd); if (m == NULL) return -errno; @@ -845,20 +833,20 @@ break; } case SPA_DATA_MemPtr: - spa_log_debug(this->log, "mem %d %zd", j, SPA_PTRDIFF(d->data, baseptr)); + spa_log_debug(impl->log, "mem %d %zd", j, SPA_PTRDIFF(d->data, baseptr)); b->datasj.data = SPA_INT_TO_PTR(SPA_PTRDIFF(d->data, baseptr)); break; default: b->datasj.type = SPA_ID_INVALID; b->datasj.data = NULL; - spa_log_error(this->log, "invalid memory type %d", d->type); + spa_log_error(impl->log, "invalid memory type %d", d->type); break; } } } mix->n_buffers = n_buffers; - return pw_client_node_resource_port_use_buffers(this->resource, + return pw_client_node_resource_port_use_buffers(impl->resource, direction, port_id, mix_id, flags, n_buffers, mb); } @@ -871,12 +859,9 @@ struct spa_buffer **buffers, uint32_t n_buffers) { - struct node *this = object; - struct impl *impl; - - spa_return_val_if_fail(this != NULL, -EINVAL); + struct impl *impl = object; - impl = this->impl; + spa_return_val_if_fail(impl != NULL, -EINVAL); return do_port_use_buffers(impl, direction, port_id, SPA_ID_INVALID, flags, buffers, n_buffers); @@ -885,33 +870,31 @@ static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) { - struct node *this = object; + struct impl *impl = object; - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); + spa_return_val_if_fail(impl != NULL, -EINVAL); + spa_return_val_if_fail(CHECK_PORT(impl, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); - spa_log_trace_fp(this->log, "reuse buffer %d", buffer_id); + spa_log_trace_fp(impl->log, "reuse buffer %d", buffer_id); return -ENOTSUP; } static int impl_node_process(void *object) { - struct node *this = object; - struct impl *impl = this->impl; + struct impl *impl = object; struct pw_impl_node *n = impl->this.node; struct timespec ts; - spa_log_trace_fp(this->log, "%p: send process driver:%p", this, impl->this.node->driver_node); - - if (SPA_UNLIKELY(spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &ts) < 0)) - spa_zero(ts); - + /* this should not be called, we call the exported node + * directly */ + spa_log_warn(impl->log, "exported node activation"); + spa_system_clock_gettime(impl->data_system, CLOCK_MONOTONIC, &ts); n->rt.activation->status = PW_NODE_ACTIVATION_TRIGGERED; n->rt.activation->signal_time = SPA_TIMESPEC_TO_NSEC(&ts); - if (SPA_UNLIKELY(spa_system_eventfd_write(this->data_system, this->writefd, 1) < 0)) - spa_log_warn(this->log, "%p: error %m", this); + if (SPA_UNLIKELY(spa_system_eventfd_write(n->rt.target.system, n->rt.target.fd, 1) < 0)) + pw_log_warn("%p: write failed %m", impl); return SPA_STATUS_OK; } @@ -922,14 +905,13 @@ size_t user_data_size) { struct impl *impl = data; - struct node *this = &impl->node; uint32_t new_id = user_data_size; - pw_log_debug("%p: bind %u/%u", this, new_id, version); + pw_log_debug("%p: bind %u/%u", impl, new_id, version); impl->bind_node_version = version; impl->bind_node_id = new_id; - pw_map_insert_at(&this->client->objects, new_id, NULL); + pw_map_insert_at(&impl->client->objects, new_id, NULL); return NULL; } @@ -942,16 +924,15 @@ const struct spa_node_info *info) { struct impl *impl = data; - struct node *this = &impl->node; if (change_mask & PW_CLIENT_NODE_UPDATE_PARAMS) { - pw_log_debug("%p: update %d params", this, n_params); - update_params(&this->params, n_params, params); + pw_log_debug("%p: update %d params", impl, n_params); + update_params(&impl->params, n_params, params); } if (change_mask & PW_CLIENT_NODE_UPDATE_INFO) { - spa_node_emit_info(&this->hooks, info); + spa_node_emit_info(&impl->hooks, info); } - pw_log_debug("%p: got node update", this); + pw_log_debug("%p: got node update", impl); return 0; } @@ -965,37 +946,36 @@ const struct spa_port_info *info) { struct impl *impl = data; - struct node *this = &impl->node; struct port *port; bool remove; - spa_log_debug(this->log, "%p: got port update change:%08x params:%d", - this, change_mask, n_params); + spa_log_debug(impl->log, "%p: got port update change:%08x params:%d", + impl, change_mask, n_params); remove = (change_mask == 0); - port = GET_PORT(this, direction, port_id); + port = GET_PORT(impl, direction, port_id); if (remove) { if (port == NULL) return 0; port->destroyed = true; - clear_port(this, port); + clear_port(impl, port); } else { struct port *target; if (port == NULL) { - if (!CHECK_FREE_PORT(this, direction, port_id)) + if (!CHECK_FREE_PORT(impl, direction, port_id)) return -EINVAL; - target = &this->dummy; - spa_zero(this->dummy); + target = &impl->dummy; + spa_zero(impl->dummy); target->direction = direction; target->id = port_id; } else target = port; - do_update_port(this, + do_update_port(impl, target, change_mask, n_params, params, @@ -1007,16 +987,14 @@ static int client_node_set_active(void *data, bool active) { struct impl *impl = data; - struct node *this = &impl->node; - spa_log_debug(this->log, "%p: active:%d", this, active); + spa_log_debug(impl->log, "%p: active:%d", impl, active); return pw_impl_node_set_active(impl->this.node, active); } static int client_node_event(void *data, const struct spa_event *event) { struct impl *impl = data; - struct node *this = &impl->node; - spa_node_emit_event(&this->hooks, event); + spa_node_emit_event(&impl->hooks, event); return 0; } @@ -1028,16 +1006,15 @@ struct spa_buffer **buffers) { struct impl *impl = data; - struct node *this = &impl->node; struct port *p; struct mix *mix; uint32_t i, j; - spa_log_debug(this->log, "%p: %s port %d.%d buffers %p %u", this, + spa_log_debug(impl->log, "%p: %s port %d.%d buffers %p %u", impl, direction == SPA_DIRECTION_INPUT ? "input" : "output", port_id, mix_id, buffers, n_buffers); - p = GET_PORT(this, direction, port_id); + p = GET_PORT(impl, direction, port_id); spa_return_val_if_fail(p != NULL, -EINVAL); if (direction == SPA_DIRECTION_OUTPUT) @@ -1056,7 +1033,7 @@ oldbuf = b->outbuf; newbuf = buffersi; - spa_log_debug(this->log, "buffer %d n_datas:%d", i, newbuf->n_datas); + spa_log_debug(impl->log, "buffer %d n_datas:%d", i, newbuf->n_datas); if (oldbuf->n_datas != newbuf->n_datas) return -EINVAL; @@ -1072,7 +1049,7 @@ b->datasj.type = d->type; b->datasj.fd = d->fd; - spa_log_debug(this->log, " data %d type:%d fl:%08x fd:%d, offs:%d max:%d", + spa_log_debug(impl->log, " data %d type:%d fl:%08x fd:%d, offs:%d max:%d", j, d->type, d->flags, (int) d->fd, d->mapoffset, d->maxsize); } @@ -1094,26 +1071,28 @@ static void node_on_data_fd_events(struct spa_source *source) { - struct node *this = source->data; + struct impl *impl = source->data; - if (source->rmask & (SPA_IO_ERR | SPA_IO_HUP)) { - spa_log_warn(this->log, "%p: got error", this); + if (SPA_UNLIKELY(source->rmask & (SPA_IO_ERR | SPA_IO_HUP))) { + spa_log_warn(impl->log, "%p: got error", impl); return; } - - if (source->rmask & SPA_IO_IN) { + if (SPA_LIKELY(source->rmask & SPA_IO_IN)) { uint64_t cmd; - struct pw_impl_node *node = this->impl->this.node; + struct pw_impl_node *node = impl->this.node; + struct pw_node_activation *a = node->rt.activation; + int status; - if (SPA_UNLIKELY(spa_system_eventfd_read(this->data_system, - this->data_source.fd, &cmd) < 0)) - pw_log_warn("%p: read failed %m", this); + if (SPA_UNLIKELY(spa_system_eventfd_read(impl->data_system, + impl->data_source.fd, &cmd) < 0)) + pw_log_warn("%p: read failed %m", impl); else if (SPA_UNLIKELY(cmd > 1)) pw_log_info("(%s-%u) client missed %"PRIu64" wakeups", node->name, node->info.id, cmd - 1); - spa_log_trace_fp(this->log, "%p: got ready", this); - spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA); + status = a->state0.status; + spa_log_trace_fp(impl->log, "%p: got ready %d", impl, status); + spa_node_call_ready(&impl->callbacks, status); } } @@ -1137,42 +1116,31 @@ }; static int -node_init(struct node *this, +impl_init(struct impl *impl, struct spa_dict *info, const struct spa_support *support, uint32_t n_support) { - this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); - this->data_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataSystem); + impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - if (this->data_loop == NULL) { - spa_log_error(this->log, "a data-loop is needed"); - return -EINVAL; - } - if (this->data_system == NULL) { - spa_log_error(this->log, "a data-system is needed"); - return -EINVAL; - } - - this->node.iface = SPA_INTERFACE_INIT( + impl->node.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_Node, SPA_VERSION_NODE, - &impl_node, this); - spa_hook_list_init(&this->hooks); + &impl_node, impl); + spa_hook_list_init(&impl->hooks); - this->data_source.func = node_on_data_fd_events; - this->data_source.data = this; - this->data_source.fd = -1; - this->data_source.mask = SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP; - this->data_source.rmask = 0; + impl->data_source.func = node_on_data_fd_events; + impl->data_source.data = impl; + impl->data_source.fd = -1; + impl->data_source.mask = SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP; + impl->data_source.rmask = 0; return 0; } -static int node_clear(struct node *this) +static int impl_clear(struct impl *impl) { - update_params(&this->params, 0, NULL); + update_params(&impl->params, 0, NULL); return 0; } @@ -1192,22 +1160,21 @@ { struct impl *impl = data; struct pw_impl_client_node *this = &impl->this; - struct node *node = &impl->node; - pw_log_debug("%p: destroy", node); + pw_log_debug("%p: destroy", impl); - impl->node.resource = this->resource = NULL; + impl->resource = this->resource = NULL; spa_hook_remove(&impl->resource_listener); spa_hook_remove(&impl->object_listener); - if (node->data_source.fd != -1) { - spa_loop_invoke(node->data_loop, + if (impl->data_source.fd != -1) { + spa_loop_invoke(impl->data_loop, do_remove_source, SPA_ID_INVALID, NULL, 0, true, - &node->data_source); + &impl->data_source); } if (this->node) pw_impl_node_destroy(this->node); @@ -1216,28 +1183,25 @@ static void client_node_resource_error(void *data, int seq, int res, const char *message) { struct impl *impl = data; - struct node *this = &impl->node; struct spa_result_node_error result; - pw_log_error("%p: error seq:%d %d (%s)", this, seq, res, message); + pw_log_error("%p: error seq:%d %d (%s)", impl, seq, res, message); result.message = message; - spa_node_emit_result(&this->hooks, seq, res, SPA_RESULT_TYPE_NODE_ERROR, &result); + spa_node_emit_result(&impl->hooks, seq, res, SPA_RESULT_TYPE_NODE_ERROR, &result); } static void client_node_resource_pong(void *data, int seq) { struct impl *impl = data; - struct node *this = &impl->node; - - pw_log_debug("%p: got pong, emit result %d", this, seq); - spa_node_emit_result(&this->hooks, seq, 0, 0, NULL); + pw_log_debug("%p: got pong, emit result %d", impl, seq); + spa_node_emit_result(&impl->hooks, seq, 0, 0, NULL); } void pw_impl_client_node_registered(struct pw_impl_client_node *this, struct pw_global *global) { struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); struct pw_impl_node *node = this->node; - struct pw_impl_client *client = impl->node.client; + struct pw_impl_client *client = impl->client; uint32_t node_id = global->id; pw_log_debug("%p: %d", &impl->node, node_id); @@ -1249,14 +1213,14 @@ } impl->node_id = node_id; - if (this->resource == NULL) + if (impl->resource == NULL) return; - pw_resource_set_bound_id(this->resource, node_id); + pw_resource_set_bound_id(impl->resource, node_id); - pw_client_node_resource_transport(this->resource, - impl->other_fds0, - impl->other_fds1, + pw_client_node_resource_transport(impl->resource, + this->node->source.fd, + impl->data_source.fd, impl->activation->id, 0, sizeof(struct pw_node_activation)); @@ -1267,36 +1231,45 @@ } } -static void node_initialized(void *data) +static int add_area(struct impl *impl) { - struct impl *impl = data; - struct pw_impl_client_node *this = &impl->this; - struct node *node = &impl->node; - struct pw_global *global; - struct spa_system *data_system = impl->node.data_system; size_t size; + struct pw_memblock *area; - impl->fds0 = spa_system_eventfd_create(data_system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); - impl->fds1 = spa_system_eventfd_create(data_system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); - impl->other_fds0 = impl->fds1; - impl->other_fds1 = impl->fds0; - node->data_source.fd = impl->fds0; - node->writefd = impl->fds1; - - spa_loop_add_source(node->data_loop, &node->data_source); - pw_log_debug("%p: transport read-fd:%d write-fd:%d", node, impl->fds0, impl->fds1); + size = sizeof(struct spa_io_buffers) * AREA_SIZE; - size = sizeof(struct spa_io_buffers) * MAX_AREAS; - - impl->io_areas = pw_mempool_alloc(impl->context->pool, + area = pw_mempool_alloc(impl->context->pool, PW_MEMBLOCK_FLAG_READWRITE | PW_MEMBLOCK_FLAG_MAP | PW_MEMBLOCK_FLAG_SEAL, SPA_DATA_MemFd, size); - if (impl->io_areas == NULL) - return; + if (area == NULL) + return -errno; + + pw_log_debug("%p: io area %u %p", impl, + (unsigned)pw_array_get_len(&impl->io_areas, struct pw_memblock*), + area->map->ptr); + + pw_array_add_ptr(&impl->io_areas, area); + return 0; +} + +static void node_initialized(void *data) +{ + struct impl *impl = data; + struct pw_impl_client_node *this = &impl->this; + struct pw_global *global; + struct spa_system *data_system = impl->data_system; + + impl->data_source.fd = spa_system_eventfd_create(data_system, + SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); - pw_log_debug("%p: io areas %p", node, impl->io_areas->map->ptr); + spa_loop_add_source(impl->data_loop, &impl->data_source); + pw_log_debug("%p: transport read-fd:%d write-fd:%d", impl, + impl->data_source.fd, this->node->source.fd); + + if (add_area(impl) < 0) + return; if ((global = pw_impl_node_get_global(this->node)) != NULL) pw_impl_client_node_registered(this, global); @@ -1306,37 +1279,39 @@ { struct impl *impl = data; struct pw_impl_client_node *this = &impl->this; - struct node *node = &impl->node; - struct spa_system *data_system = node->data_system; + struct spa_system *data_system = impl->data_system; uint32_t tag5 = { impl->node_id, }; struct pw_memmap *mm; + struct pw_memblock **area; this->node = NULL; - pw_log_debug("%p: free", node); - node_clear(node); + pw_log_debug("%p: free", impl); + impl_clear(impl); spa_hook_remove(&impl->node_listener); - while ((mm = pw_mempool_find_tag(node->client->pool, tag, sizeof(uint32_t))) != NULL) + while ((mm = pw_mempool_find_tag(impl->client->pool, tag, sizeof(uint32_t))) != NULL) pw_memmap_free(mm); - if (this->resource) - pw_resource_destroy(this->resource); + if (impl->resource) + pw_resource_destroy(impl->resource); if (impl->activation) pw_memblock_unref(impl->activation); - if (impl->io_areas) - pw_memblock_unref(impl->io_areas); - pw_map_clear(&impl->node.ports0); - pw_map_clear(&impl->node.ports1); + pw_array_for_each(area, &impl->io_areas) { + if (*area) + pw_memblock_unref(*area); + } + pw_array_clear(&impl->io_areas); + + pw_map_clear(&impl->ports0); + pw_map_clear(&impl->ports1); pw_map_clear(&impl->io_map); - if (impl->fds0 != -1) - spa_system_close(data_system, impl->fds0); - if (impl->fds1 != -1) - spa_system_close(data_system, impl->fds1); + if (impl->data_source.fd != -1) + spa_system_close(data_system, impl->data_source.fd); free(impl); } @@ -1345,6 +1320,8 @@ struct port *port = data; struct impl *impl = port->impl; struct mix *m; + uint32_t idx, pos, len; + struct pw_memblock *area; if ((m = ensure_mix(impl, port, mix->port.port_id)) == NULL) return -ENOMEM; @@ -1354,33 +1331,44 @@ m->valid = false; return -errno; } - if (mix->id >= MAX_AREAS) { - pw_map_remove(&impl->io_map, mix->id); - m->valid = false; - return -ENOMEM; + + idx = mix->id / AREA_SIZE; + pos = mix->id % AREA_SIZE; + + len = pw_array_get_len(&impl->io_areas, struct pw_memblock *); + if (idx > len) + goto no_mem; + if (idx == len) { + pw_log_debug("%p: extend area idx:%u pos:%u", impl, idx, pos); + if (add_area(impl) < 0) + goto no_mem; } + area = *pw_array_get_unchecked(&impl->io_areas, idx, struct pw_memblock*); - mix->io = SPA_PTROFF(impl->io_areas->map->ptr, - mix->id * sizeof(struct spa_io_buffers), void); + mix->io = SPA_PTROFF(area->map->ptr, + pos * sizeof(struct spa_io_buffers), void); *mix->io = SPA_IO_BUFFERS_INIT; m->peer_id = mix->peer_id; pw_log_debug("%p: init mix id:%d io:%p base:%p", impl, - mix->id, mix->io, impl->io_areas->map->ptr); + mix->id, mix->io, area->map->ptr); return 0; +no_mem: + pw_map_remove(&impl->io_map, mix->id); + m->valid = false; + return -ENOMEM; } static int port_release_mix(void *data, struct pw_impl_port_mix *mix) { struct port *port = data; struct impl *impl = port->impl; - struct node *this = &impl->node; struct mix *m; - pw_log_debug("%p: remove mix id:%d io:%p base:%p", - this, mix->id, mix->io, impl->io_areas->map->ptr); + pw_log_debug("%p: remove mix id:%d io:%p", + impl, mix->id, mix->io); if ((m = find_mix(port, mix->port.port_id)) == NULL || !m->valid) return -EINVAL; @@ -1408,7 +1396,7 @@ if (port->direction != direction) return -ENOTSUP; - return impl_node_port_enum_params(&port->node->node, seq, direction, port->id, + return impl_node_port_enum_params(&port->impl->node, seq, direction, port->id, id, start, num, filter); } @@ -1459,7 +1447,6 @@ struct port *p = object; struct pw_impl_port *port = p->port; struct impl *impl = port->owner_data; - struct node *this = &impl->node; struct pw_impl_port_mix *mix; mix = pw_map_lookup(&port->mix_port_map, mix_id); @@ -1472,8 +1459,8 @@ else mix->io = NULL; - if (mix->io != NULL && this->resource && this->resource->version >= 4) - pw_client_node_resource_port_set_mix_info(this->resource, + if (mix->io != NULL && impl->resource && impl->resource->version >= 4) + pw_client_node_resource_port_set_mix_info(impl->resource, direction, port->port_id, mix->port.port_id, mix->peer_id, NULL); } @@ -1487,7 +1474,7 @@ impl_mix_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) { struct port *p = object; - return impl_node_port_reuse_buffer(&p->node->node, p->id, buffer_id); + return impl_node_port_reuse_buffer(&p->impl->node, p->id, buffer_id); } static int impl_mix_process(void *object) @@ -1511,13 +1498,12 @@ { struct impl *impl = data; struct port *p = pw_impl_port_get_user_data(port); - struct node *this = &impl->node; - pw_log_debug("%p: port %p init", this, port); + pw_log_debug("%p: port %p init", impl, port); - *p = this->dummy; + *p = impl->dummy; p->port = port; - p->node = this; + p->impl = impl; p->direction = port->direction; p->id = port->port_id; p->impl = impl; @@ -1528,7 +1514,7 @@ &impl_port_mix, p); ensure_mix(impl, p, SPA_ID_INVALID); - pw_map_insert_at(&this->portsp->direction, p->id, p); + pw_map_insert_at(&impl->portsp->direction, p->id, p); return; } @@ -1550,36 +1536,34 @@ static void node_port_removed(void *data, struct pw_impl_port *port) { struct impl *impl = data; - struct node *this = &impl->node; struct port *p = pw_impl_port_get_user_data(port); - pw_log_debug("%p: port %p remove", this, port); + pw_log_debug("%p: port %p remove", impl, port); p->removed = true; - clear_port(this, p); + clear_port(impl, p); } static void node_peer_added(void *data, struct pw_impl_node *peer) { struct impl *impl = data; - struct node *this = &impl->node; struct pw_memblock *m; if (peer == impl->this.node) return; - m = pw_mempool_import_block(this->client->pool, peer->activation); + m = pw_mempool_import_block(impl->client->pool, peer->activation); if (m == NULL) { - pw_log_debug("%p: can't ensure mem: %m", this); + pw_log_debug("%p: can't ensure mem: %m", impl); return; } pw_log_debug("%p: peer %p id:%u added mem_id:%u", &impl->this, peer, peer->info.id, m->id); - if (this->resource == NULL) + if (impl->resource == NULL) return; - pw_client_node_resource_set_activation(this->resource, + pw_client_node_resource_set_activation(impl->resource, peer->info.id, peer->source.fd, m->id, @@ -1590,23 +1574,22 @@ static void node_peer_removed(void *data, struct pw_impl_node *peer) { struct impl *impl = data; - struct node *this = &impl->node; struct pw_memblock *m; if (peer == impl->this.node) return; - m = pw_mempool_find_fd(this->client->pool, peer->activation->fd); + m = pw_mempool_find_fd(impl->client->pool, peer->activation->fd); if (m == NULL) { - pw_log_warn("%p: unknown peer %p fd:%d", this, peer, + pw_log_warn("%p: unknown peer %p fd:%d", impl, peer, peer->source.fd); return; } - pw_log_debug("%p: peer %p %u removed", this, peer, + pw_log_debug("%p: peer %p %u removed", impl, peer, peer->info.id); - if (this->resource != NULL) { - pw_client_node_resource_set_activation(this->resource, + if (impl->resource != NULL) { + pw_client_node_resource_set_activation(impl->resource, peer->info.id, -1, SPA_ID_INVALID, @@ -1620,9 +1603,8 @@ static void node_driver_changed(void *data, struct pw_impl_node *old, struct pw_impl_node *driver) { struct impl *impl = data; - struct node *this = &impl->node; - pw_log_debug("%p: driver changed %p -> %p", this, old, driver); + pw_log_debug("%p: driver changed %p -> %p", impl, old, driver); node_peer_removed(data, old); node_peer_added(data, driver); @@ -1688,31 +1670,34 @@ this = &impl->this; impl->context = context; - impl->fds0 = impl->fds1 = -1; + impl->data_source.fd = -1; pw_log_debug("%p: new", &impl->node); support = pw_context_get_support(impl->context, &n_support); - node_init(&impl->node, NULL, support, n_support); - impl->node.impl = impl; - impl->node.resource = resource; - impl->node.client = client; + impl_init(impl, NULL, support, n_support); + impl->resource = resource; + impl->client = client; this->flags = do_register ? 0 : 1; - pw_map_init(&impl->node.ports0, 64, 64); - pw_map_init(&impl->node.ports1, 64, 64); + pw_map_init(&impl->ports0, 64, 64); + pw_map_init(&impl->ports1, 64, 64); pw_map_init(&impl->io_map, 64, 64); + pw_array_init(&impl->io_areas, 64 * sizeof(struct pw_memblock*)); this->resource = resource; this->node = pw_spa_node_new(context, PW_SPA_NODE_FLAG_ASYNC | (do_register ? 0 : PW_SPA_NODE_FLAG_NO_REGISTER), - (struct spa_node *)&impl->node.node, + (struct spa_node *)&impl->node, NULL, properties, 0); if (this->node == NULL) goto error_no_node; + impl->data_loop = this->node->data_loop->loop; + impl->data_system = this->node->data_loop->system; + this->node->remote = true; this->flags = 0; @@ -1733,7 +1718,7 @@ error_no_node: res = -errno; - node_clear(&impl->node); + impl_clear(impl); properties = NULL; goto error_exit_free;
View file
pipewire-0.3.70.tar.gz/src/modules/module-client-node/remote-node.c -> pipewire-0.3.71.tar.gz/src/modules/module-client-node/remote-node.c
Changed
@@ -47,6 +47,8 @@ struct node_data { struct pw_context *context; + struct pw_loop *data_loop; + struct spa_system *data_system; struct pw_mempool *pool; @@ -69,6 +71,9 @@ struct spa_hook proxy_client_node_listener; struct spa_list links; + + struct spa_io_clock *clock; + struct spa_io_position *position; }; struct link { @@ -77,7 +82,6 @@ struct pw_memmap *map; struct pw_node_target target; uint32_t node_id; - int signalfd; }; /** \endcond */ @@ -105,12 +109,11 @@ static void clear_link(struct node_data *data, struct link *link) { - struct pw_context *context = data->context; pw_log_debug("link %p", link); - pw_loop_invoke(context->data_loop, + pw_loop_invoke(data->data_loop, do_deactivate_link, SPA_ID_INVALID, NULL, 0, true, link); pw_memmap_free(link->map); - spa_system_close(context->data_system, link->signalfd); + spa_system_close(link->target.system, link->target.fd); spa_list_remove(&link->link); free(link); } @@ -137,7 +140,7 @@ pw_memmap_free(data->activation); data->node->rt.activation = data->node->activation->map->ptr; - spa_system_close(data->context->data_system, data->rtwritefd); + spa_system_close(data->data_system, data->rtwritefd); data->have_transport = false; } @@ -166,7 +169,7 @@ { if (mix->active) { pw_log_debug("node %p: mix %p deactivate", data, mix); - pw_loop_invoke(data->context->data_loop, + pw_loop_invoke(data->data_loop, do_deactivate_mix, SPA_ID_INVALID, NULL, 0, true, mix); mix->active = false; } @@ -188,7 +191,7 @@ { if (!mix->active) { pw_log_debug("node %p: mix %p activate", data, mix); - pw_loop_invoke(data->context->data_loop, + pw_loop_invoke(data->data_loop, do_activate_mix, SPA_ID_INVALID, NULL, 0, false, mix); mix->active = true; } @@ -243,6 +246,7 @@ int readfd, int writefd, uint32_t mem_id, uint32_t offset, uint32_t size) { struct node_data *data = _data; + struct pw_impl_node *node = data->node; struct pw_proxy *proxy = (struct pw_proxy*)data->client_node; clean_transport(data); @@ -254,18 +258,18 @@ return -errno; } - data->node->rt.activation = data->activation->ptr; + node->rt.activation = data->activation->ptr; pw_log_debug("remote-node %p: fds:%d %d node:%u activation:%p", proxy, readfd, writefd, data->remote_id, data->activation->ptr); data->rtwritefd = writefd; - spa_system_close(data->context->data_system, data->node->source.fd); - data->node->source.fd = readfd; + spa_system_close(data->data_system, node->source.fd); + node->source.fd = readfd; data->have_transport = true; - if (data->node->active) + if (node->active) pw_client_node_set_active(data->client_node, true); return 0; @@ -466,6 +470,17 @@ pw_log_debug("node %p: set io %s %p", proxy, spa_debug_type_find_name(spa_type_io, id), ptr); + switch(id) { + case SPA_IO_Clock: + data->clock = size >= sizeof(*data->clock) ? ptr : NULL; + break; + case SPA_IO_Position: + data->position = size >= sizeof(*data->position) ? ptr : NULL; + break; + } + data->node->driving = data->clock && data->position && + data->position->clock.id == data->clock->id; + res = spa_node_set_io(data->node->node, id, ptr, size); pw_memmap_free(old); @@ -479,7 +494,9 @@ static int client_node_event(void *data, const struct spa_event *event) { - pw_log_warn("unhandled node event %d", SPA_EVENT_TYPE(event)); + uint32_t id = SPA_NODE_EVENT_ID(event); + pw_log_warn("unhandled node event %d (%s)", id, + spa_debug_type_find_name(spa_type_node_event_id, id)); return -ENOTSUP; } @@ -488,11 +505,13 @@ struct node_data *data = _data; struct pw_proxy *proxy = (struct pw_proxy*)data->client_node; int res; + uint32_t id = SPA_NODE_COMMAND_ID(command); - switch (SPA_NODE_COMMAND_ID(command)) { - case SPA_NODE_COMMAND_Pause: - pw_log_debug("node %p: pause", proxy); + pw_log_debug("%p: got command %d (%s)", proxy, id, + spa_debug_type_find_name(spa_type_node_command_id, id)); + switch (id) { + case SPA_NODE_COMMAND_Pause: if ((res = pw_impl_node_set_state(data->node, PW_NODE_STATE_IDLE)) < 0) { pw_log_warn("node %p: pause failed", proxy); pw_proxy_error(proxy, res, "pause failed"); @@ -500,8 +519,6 @@ break; case SPA_NODE_COMMAND_Start: - pw_log_debug("node %p: start", proxy); - if ((res = pw_impl_node_set_state(data->node, PW_NODE_STATE_RUNNING)) < 0) { pw_log_warn("node %p: start failed", proxy); pw_proxy_error(proxy, res, "start failed"); @@ -509,7 +526,6 @@ break; case SPA_NODE_COMMAND_Suspend: - pw_log_debug("node %p: suspend", proxy); if ((res = pw_impl_node_set_state(data->node, PW_NODE_STATE_SUSPENDED)) < 0) { pw_log_warn("node %p: suspend failed", proxy); pw_proxy_error(proxy, res, "suspend failed"); @@ -519,9 +535,11 @@ res = pw_impl_node_send_command(data->node, command); break; default: - pw_log_warn("unhandled node command %d", SPA_NODE_COMMAND_ID(command)); + pw_log_warn("unhandled node command %d (%s)", id, + spa_debug_type_find_name(spa_type_node_command_id, id)); res = -ENOTSUP; - pw_proxy_errorf(proxy, res, "command %d not supported", SPA_NODE_COMMAND_ID(command)); + pw_proxy_errorf(proxy, res, "command %d (%s) not supported", id, + spa_debug_type_find_name(spa_type_node_command_id, id)); } return res; } @@ -833,18 +851,6 @@ return res; } -static int link_signal_func(void *user_data) -{ - struct link *link = user_data; - struct spa_system *data_system = link->data->context->data_system; - - pw_log_trace_fp("link %p: signal %p", link, link->target.activation); - if (SPA_UNLIKELY(spa_system_eventfd_write(data_system, link->signalfd, 1) < 0)) - pw_log_warn("link %p: write failed %m", link); - - return 0; -} - static int do_activate_link(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) @@ -875,7 +881,7 @@ if (data->remote_id == node_id) { pw_log_debug("node %p: our activation %u: %u %u %u", node, node_id, memid, offset, size); - spa_system_close(data->context->data_system, signalfd); + spa_system_close(data->data_system, signalfd); return 0; } @@ -903,13 +909,11 @@ link->node_id = node_id; link->map = mm; link->target.activation = ptr; - link->signalfd = signalfd; - link->target.signal_func = link_signal_func; - link->target.data = link; - link->target.node = NULL; + link->target.system = data->data_system; + link->target.fd = signalfd; spa_list_append(&data->links, &link->link); - pw_loop_invoke(data->context->data_loop, + pw_loop_invoke(data->data_loop, do_activate_link, SPA_ID_INVALID, NULL, 0, false, link); pw_log_debug("node %p: link %p: fd:%d id:%u state %p required %d, pending %d", @@ -1159,13 +1163,18 @@ .bound_props = client_node_bound_props, }; +static inline uint64_t get_time_ns(struct spa_system *system) +{ + struct timespec ts; + spa_system_clock_gettime(system, CLOCK_MONOTONIC, &ts); + return SPA_TIMESPEC_TO_NSEC(&ts); +} static int node_ready(void *d, int status) { struct node_data *data = d; struct pw_impl_node *node = data->node; struct pw_node_activation *a = node->rt.activation; - struct spa_system *data_system = data->context->data_system; - struct timespec ts; + struct spa_system *data_system = data->data_system; struct pw_impl_port *p; pw_log_trace_fp("node %p: ready driver:%d exported:%d status:%d", node, @@ -1173,12 +1182,11 @@ if (status & SPA_STATUS_HAVE_DATA) { spa_list_for_each(p, &node->rt.output_mix, rt.node_link) - spa_node_process(p->mix); + spa_node_process_fast(p->mix); } - spa_system_clock_gettime(data_system, CLOCK_MONOTONIC, &ts); - a->status = PW_NODE_ACTIVATION_TRIGGERED; - a->signal_time = SPA_TIMESPEC_TO_NSEC(&ts); + a->state0.status = status; + a->signal_time = get_time_ns(data_system); if (SPA_UNLIKELY(spa_system_eventfd_write(data_system, data->rtwritefd, 1) < 0)) pw_log_warn("node %p: write failed %m", node); @@ -1241,6 +1249,8 @@ data->node = node; data->do_free = do_free; data->context = pw_impl_node_get_context(node); + data->data_loop = node->data_loop; + data->data_system = data->data_loop->system; data->client_node = (struct pw_client_node *)client_node; data->remote_id = SPA_ID_INVALID;
View file
pipewire-0.3.70.tar.gz/src/modules/module-combine-stream.c -> pipewire-0.3.71.tar.gz/src/modules/module-combine-stream.c
Changed
@@ -24,6 +24,7 @@ #include <spa/pod/builder.h> #include <spa/param/audio/format-utils.h> #include <spa/param/audio/raw.h> +#include <spa/param/latency-utils.h> #include <pipewire/impl.h> #include <pipewire/i18n.h> @@ -44,6 +45,7 @@ * - `node.name`: a unique name for the stream * - `node.description`: a human readable name for the stream * - `combine.mode` = capture | playback | sink | source, default sink + * - `combine.latency-compensate`: use delay buffers to match stream latencies * - `combine.props = {}`: properties to be passed to the sink/source * - `stream.props = {}`: properties to be passed to the streams * - `stream.rules = {}`: rules for matching streams, use create-stream actions @@ -79,6 +81,7 @@ * combine.mode = sink * node.name = "combine_sink" * node.description = "My Combine Sink" + * combine.latency-compensate = false * combine.props = { * audio.position = FL FR * } @@ -118,6 +121,7 @@ * combine.mode = sink * node.name = "combine_sink_5_1" * node.description = "My 5.1 Combine Sink" + * combine.latency-compensate = false * combine.props = { * audio.position = FL FR FC LFE SL SR * } @@ -213,6 +217,8 @@ "( stream.props=<properties> ) " \ "( stream.rules=<properties> ) " +#define DELAYBUF_MAX_SIZE (20 * sizeof(float) * 96000) + static const struct spa_dict_item module_props = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, @@ -223,6 +229,7 @@ struct impl { struct pw_context *context; + struct pw_loop *main_loop; struct pw_data_loop *data_loop; struct pw_properties *props; @@ -243,6 +250,8 @@ struct pw_registry *registry; struct spa_hook registry_listener; + struct spa_source *update_delay_event; + struct pw_properties *combine_props; struct pw_stream *combine; struct spa_hook combine_listener; @@ -251,14 +260,25 @@ struct pw_properties *stream_props; + struct spa_latency_info latency; + + int64_t latency_offset; + struct spa_audio_info_raw info; unsigned int do_disconnect:1; + unsigned int latency_compensate:1; struct spa_list streams; uint32_t n_streams; }; +struct ringbuffer { + void *buf; + uint32_t idx; + uint32_t size; +}; + struct stream { uint32_t id; @@ -269,11 +289,21 @@ struct spa_hook stream_listener; struct pw_stream_events stream_events; + struct spa_latency_info latency; + struct spa_audio_info_raw info; uint32_t remapSPA_AUDIO_MAX_CHANNELS; + uint32_t rate; + + void *delaybuf; + struct ringbuffer delaySPA_AUDIO_MAX_CHANNELS; + + int64_t delay_nsec; /* for main loop */ + int64_t data_delay_nsec; /* for data loop */ unsigned int ready:1; unsigned int added:1; + unsigned int have_latency:1; }; static uint32_t channel_from_name(const char *name) @@ -316,6 +346,53 @@ parse_position(info, DEFAULT_POSITION, strlen(DEFAULT_POSITION)); } +static void ringbuffer_init(struct ringbuffer *r, void *buf, uint32_t size) +{ + r->buf = buf; + r->idx = 0; + r->size = size; +} + +static void ringbuffer_memcpy(struct ringbuffer *r, void *dst, void *src, uint32_t size) +{ + uint32_t avail; + + avail = SPA_MIN(size, r->size); + + /* buf to dst */ + if (dst && avail > 0) { + spa_ringbuffer_read_data(NULL, r->buf, r->size, r->idx, dst, avail); + dst = SPA_PTROFF(dst, avail, void); + } + + /* src to dst */ + if (size > avail) { + if (dst) + memcpy(dst, src, size - avail); + src = SPA_PTROFF(src, size - avail, void); + } + + /* src to buf */ + if (avail > 0) { + spa_ringbuffer_write_data(NULL, r->buf, r->size, r->idx, src, avail); + r->idx = (r->idx + avail) % r->size; + } +} + +static void ringbuffer_copy(struct ringbuffer *dst, struct ringbuffer *src) +{ + uint32_t l0, l1; + + if (dst->size == 0 || src->size == 0) + return; + + l0 = src->size - src->idx; + l1 = src->idx; + + ringbuffer_memcpy(dst, NULL, SPA_PTROFF(src->buf, src->idx, void), l0); + ringbuffer_memcpy(dst, NULL, src->buf, l1); +} + static struct stream *find_stream(struct impl *impl, uint32_t id) { struct stream *s; @@ -325,6 +402,192 @@ return NULL; } +static enum pw_direction get_combine_direction(struct impl *impl) +{ + if (impl->mode == MODE_SINK || impl->mode == MODE_CAPTURE) + return PW_DIRECTION_INPUT; + else + return PW_DIRECTION_OUTPUT; +} + +static void apply_latency_offset(struct spa_latency_info *latency, int64_t offset) +{ + latency->min_ns += SPA_MAX(offset, -(int64_t)latency->min_ns); + latency->max_ns += SPA_MAX(offset, -(int64_t)latency->max_ns); +} + +static int64_t get_stream_delay(struct stream *s) +{ + struct pw_time t; + + if (pw_stream_get_time_n(s->stream, &t, sizeof(t)) < 0 || + t.rate.denom == 0) + return INT64_MIN; + + return t.delay * SPA_NSEC_PER_SEC * t.rate.num / t.rate.denom; +} + +static void update_latency(struct impl *impl) +{ + struct spa_latency_info latency; + struct stream *s; + + if (impl->combine == NULL) + return; + + if (!impl->latency_compensate) { + spa_latency_info_combine_start(&latency, get_combine_direction(impl)); + + spa_list_for_each(s, &impl->streams, link) + if (s->have_latency) + spa_latency_info_combine(&latency, &s->latency); + + spa_latency_info_combine_finish(&latency); + } else { + int64_t max_delay = INT64_MIN; + + latency = SPA_LATENCY_INFO(get_combine_direction(impl)); + + spa_list_for_each(s, &impl->streams, link) { + int64_t delay = get_stream_delay(s); + + if (delay > max_delay && s->have_latency) { + latency = s->latency; + max_delay = delay; + } + } + } + + apply_latency_offset(&latency, impl->latency_offset); + + if (spa_latency_info_compare(&latency, &impl->latency) != 0) { + struct spa_pod_builder b = { 0 }; + uint8_t buffer1024; + const struct spa_pod *param; + + impl->latency = latency; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + param = spa_latency_build(&b, SPA_PARAM_Latency, &latency); + pw_stream_update_params(impl->combine, ¶m, 1); + } +} + +struct replace_delay_info { + struct stream *stream; + void *buf; + struct ringbuffer delaySPA_AUDIO_MAX_CHANNELS; +}; + +static int do_replace_delay(struct spa_loop *loop, bool async, uint32_t seq, + const void *data, size_t size, void *user_data) +{ + struct replace_delay_info *info = user_data; + unsigned int i; + + for (i = 0; i < SPA_N_ELEMENTS(info->stream->delay); ++i) { + ringbuffer_copy(&info->delayi, &info->stream->delayi); + info->stream->delayi = info->delayi; + } + + SPA_SWAP(info->stream->delaybuf, info->buf); + return 0; +} + +static void resize_delay(struct stream *stream, uint32_t size) +{ + struct replace_delay_info info; + uint32_t channels = stream->info.channels; + unsigned int i; + + size = SPA_MIN(size, DELAYBUF_MAX_SIZE); + + for (i = 0; i < channels; ++i) + if (stream->delayi.size != size) + break; + if (i == channels) + return; + + pw_log_info("stream %d latency compensation samples:%u", stream->id, + (unsigned int)(size / sizeof(float))); + + spa_zero(info); + info.stream = stream; + if (size > 0) + info.buf = calloc(channels, size); + if (!info.buf) + size = 0; + + for (i = 0; i < channels; ++i) + ringbuffer_init(&info.delayi, SPA_PTROFF(info.buf, i*size, void), size); + + pw_data_loop_invoke(stream->impl->data_loop, do_replace_delay, 0, NULL, 0, true, &info); + + free(info.buf); +} + +static void update_delay(struct impl *impl) +{ + struct stream *s; + int64_t max_delay = INT64_MIN; + + if (!impl->latency_compensate) + return; + + spa_list_for_each(s, &impl->streams, link) { + int64_t delay = get_stream_delay(s); + + if (delay != s->delay_nsec && delay != INT64_MIN) + pw_log_debug("stream %d delay:%"PRIi64" ns", s->id, delay); + + max_delay = SPA_MAX(max_delay, delay); + s->delay_nsec = delay; + } + + spa_list_for_each(s, &impl->streams, link) { + uint32_t size = 0; + + if (s->delay_nsec != INT64_MIN) { + int64_t delay = max_delay - s->delay_nsec; + size = delay * s->rate / SPA_NSEC_PER_SEC; + size *= sizeof(float); + } + + resize_delay(s, size); + } + + update_latency(impl); +} + +static void update_delay_event(void *data, uint64_t count) +{ + struct impl *impl = data; + + /* in main loop */ + update_delay(impl); +} + +static int do_clear_delaybuf(struct spa_loop *loop, bool async, uint32_t seq, + const void *data, size_t size, void *user_data) +{ + struct impl *impl = user_data; + struct stream *s; + unsigned int i; + + spa_list_for_each(s, &impl->streams, link) { + for (i = 0; i < SPA_N_ELEMENTS(s->delay); ++i) + if (s->delayi.size) + memset(s->delayi.buf, 0, s->delayi.size); + } + + return 0; +} + +static void clear_delaybuf(struct impl *impl) +{ + pw_data_loop_invoke(impl->data_loop, do_clear_delaybuf, 0, NULL, 0, true, impl); +} + static int do_add_stream(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) { @@ -350,25 +613,31 @@ return 0; } -static void destroy_stream(struct stream *s) +static void remove_stream(struct stream *s, bool destroy) { pw_log_debug("destroy stream %d", s->id); pw_data_loop_invoke(s->impl->data_loop, do_remove_stream, 0, NULL, 0, true, s); - if (s->stream) { + if (destroy && s->stream) { spa_hook_remove(&s->stream_listener); pw_stream_destroy(s->stream); } + + free(s->delaybuf); free(s); } +static void destroy_stream(struct stream *s) +{ + remove_stream(s, true); +} + static void stream_destroy(void *d) { struct stream *s = d; spa_hook_remove(&s->stream_listener); - s->stream = NULL; - destroy_stream(s); + remove_stream(s, false); } static void stream_input_process(void *d) @@ -405,10 +674,48 @@ } } +static void stream_param_changed(void *d, uint32_t id, const struct spa_pod *param) +{ + struct stream *s = d; + struct spa_latency_info latency; + struct spa_audio_info format = { 0 }; + + switch (id) { + case SPA_PARAM_Format: + if (!param) { + s->rate = 0; + } else { + if (spa_format_parse(param, &format.media_type, &format.media_subtype) < 0) + break; + if (format.media_type != SPA_MEDIA_TYPE_audio || + format.media_subtype != SPA_MEDIA_SUBTYPE_raw) + break; + if (spa_format_audio_raw_parse(param, &format.info.raw) < 0) + break; + s->rate = format.info.raw.rate; + } + update_delay(s->impl); + break; + case SPA_PARAM_Latency: + if (!param) { + s->have_latency = false; + } else if (spa_latency_parse(param, &latency) == 0 && + latency.direction == get_combine_direction(s->impl)) { + s->have_latency = true; + s->latency = latency; + } + update_latency(s->impl); + break; + default: + break; + } +} + static const struct pw_stream_events stream_events = { PW_VERSION_STREAM_EVENTS, .destroy = stream_destroy, .state_changed = stream_state_changed, + .param_changed = stream_param_changed, }; struct stream_info { @@ -527,6 +834,7 @@ goto error; pw_data_loop_invoke(impl->data_loop, do_add_stream, 0, NULL, 0, true, s); + update_delay(impl); return 0; error_errno: @@ -598,6 +906,7 @@ return; destroy_stream(s); + update_delay(impl); } static const struct pw_registry_events registry_events = { @@ -623,6 +932,7 @@ pw_impl_module_schedule_destroy(impl->module); break; case PW_STREAM_STATE_PAUSED: + clear_delaybuf(impl); impl->combine_id = pw_stream_get_node_id(impl->combine); pw_log_info("got combine id %d", impl->combine_id); break; @@ -633,11 +943,27 @@ } } +static bool check_stream_delay(struct stream *s) +{ + int64_t delay; + + if (!s->impl->latency_compensate) + return false; + + delay = get_stream_delay(s); + if (delay == INT64_MIN || delay == s->data_delay_nsec) + return false; + + s->data_delay_nsec = delay; + return true; +} + static void combine_input_process(void *d) { struct impl *impl = d; struct pw_buffer *in, *out; struct stream *s; + bool delay_changed = false; if ((in = pw_stream_dequeue_buffer(impl->combine)) == NULL) { pw_log_debug("out of buffers: %m"); @@ -650,6 +976,9 @@ if (s->stream == NULL) continue; + if (check_stream_delay(s)) + delay_changed = true; + if ((out = pw_stream_dequeue_buffer(s->stream)) == NULL) { pw_log_warn("out of playback buffers: %m"); goto do_trigger; @@ -671,8 +1000,8 @@ offs = SPA_MIN(ds->chunk->offset, ds->maxsize); size = SPA_MIN(ds->chunk->size, ds->maxsize - offs); - memcpy(dd->data, - SPA_PTROFF(ds->data, offs, void), size); + ringbuffer_memcpy(&s->delayj, + dd->data, SPA_PTROFF(ds->data, offs, void), size); outsize = SPA_MAX(outsize, size); stride = SPA_MAX(stride, ds->chunk->stride); @@ -688,6 +1017,12 @@ pw_stream_trigger_process(s->stream); } pw_stream_queue_buffer(impl->combine, in); + + /* Update delay if quantum etc. has changed. + * This should be rare enough so that doing it via main loop doesn't matter. + */ + if (impl->latency_compensate && delay_changed) + pw_loop_signal_event(impl->main_loop, impl->update_delay_event); } static void combine_output_process(void *d) @@ -695,6 +1030,7 @@ struct impl *impl = d; struct pw_buffer *in, *out; struct stream *s; + bool delay_changed = false; if ((out = pw_stream_dequeue_buffer(impl->combine)) == NULL) { pw_log_debug("out of buffers: %m"); @@ -707,6 +1043,9 @@ if (s->stream == NULL) continue; + if (check_stream_delay(s)) + delay_changed = true; + if ((in = pw_stream_dequeue_buffer(s->stream)) == NULL) { pw_log_warn("%p: out of capture buffers: %m", s); continue; @@ -731,8 +1070,8 @@ size = SPA_MIN(ds->chunk->size, ds->maxsize - offs); size = SPA_MIN(size, dd->maxsize); - memcpy(dd->data, - SPA_PTROFF(ds->data, offs, void), size); + ringbuffer_memcpy(&s->delayj, + dd->data, SPA_PTROFF(ds->data, offs, void), size); outsize = SPA_MAX(outsize, size); stride = SPA_MAX(stride, ds->chunk->stride); @@ -745,19 +1084,60 @@ pw_stream_queue_buffer(s->stream, in); } pw_stream_queue_buffer(impl->combine, out); + + if (impl->latency_compensate && delay_changed) + pw_loop_signal_event(impl->main_loop, impl->update_delay_event); +} + +static void combine_param_changed(void *d, uint32_t id, const struct spa_pod *param) +{ + struct impl *impl = d; + + switch (id) { + case SPA_PARAM_Props: { + int64_t latency_offset; + uint8_t buffer1024; + struct spa_pod_builder b; + const struct spa_pod *p; + + if (!param) + latency_offset = 0; + else if (spa_pod_parse_object(param, + SPA_TYPE_OBJECT_Props, NULL, + SPA_PROP_latencyOffsetNsec, SPA_POD_Long(&latency_offset)) < 0) + break; + + if (latency_offset == impl->latency_offset) + break; + + impl->latency_offset = latency_offset; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + p = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_latencyOffsetNsec, SPA_POD_Long(impl->latency_offset)); + pw_stream_update_params(impl->combine, &p, 1); + + update_latency(impl); + break; + } + default: + break; + } } static const struct pw_stream_events combine_events = { PW_VERSION_STREAM_EVENTS, .destroy = combine_destroy, .state_changed = combine_state_changed, + .param_changed = combine_param_changed, }; static int create_combine(struct impl *impl) { int res; uint32_t n_params; - const struct spa_pod *params1; + const struct spa_pod *params3; uint8_t buffer1024; struct spa_pod_builder b; enum pw_direction direction; @@ -792,6 +1172,14 @@ spa_pod_builder_init(&b, buffer, sizeof(buffer)); paramsn_params++ = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &impl->info); + paramsn_params++ = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + 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)); + paramsn_params++ = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_latencyOffsetNsec, SPA_POD_Long(impl->latency_offset)); if ((res = pw_stream_connect(impl->combine, direction, PW_ID_ANY, flags, params, n_params)) < 0) @@ -846,6 +1234,9 @@ if (impl->combine) pw_stream_destroy(impl->combine); + if (impl->update_delay_event) + pw_loop_destroy_source(impl->main_loop, impl->update_delay_event); + if (impl->registry) { spa_hook_remove(&impl->registry_listener); pw_proxy_destroy((struct pw_proxy*)impl->registry); @@ -905,6 +1296,7 @@ return -errno; pw_log_debug("module %p: new %s", impl, args); + impl->main_loop = pw_context_get_main_loop(context); impl->data_loop = pw_context_get_data_loop(context); spa_list_init(&impl->streams); @@ -941,6 +1333,9 @@ prefix = "sink"; } + if ((str = pw_properties_get(props, "combine.latency-compensate")) != NULL) + impl->latency_compensate = spa_atob(str); + impl->combine_props = pw_properties_new(NULL, NULL); impl->stream_props = pw_properties_new(NULL, NULL); if (impl->combine_props == NULL || impl->stream_props == NULL) { @@ -1007,6 +1402,16 @@ if (pw_properties_get(impl->stream_props, PW_KEY_NODE_DONT_RECONNECT) == NULL) pw_properties_set(impl->stream_props, PW_KEY_NODE_DONT_RECONNECT, "true"); + if (impl->latency_compensate) { + impl->update_delay_event = pw_loop_add_event(impl->main_loop, + update_delay_event, impl); + if (impl->update_delay_event == NULL) { + res = -errno; + pw_log_error("can't create event source: %m"); + goto error; + } + } + impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core); if (impl->core == NULL) { str = pw_properties_get(props, PW_KEY_REMOTE_NAME);
View file
pipewire-0.3.70.tar.gz/src/modules/module-echo-cancel.c -> pipewire-0.3.71.tar.gz/src/modules/module-echo-cancel.c
Changed
@@ -639,7 +639,6 @@ { struct spa_pod_parser prs; struct spa_pod_frame f; - int changed = 0; spa_pod_parser_pod(&prs, params); if (spa_pod_parser_push_struct(&prs, &f) < 0) @@ -668,7 +667,6 @@ if (spa_streq(name, "debug.aec.wav-path")) { spa_scnprintf(impl->wav_path, sizeof(impl->wav_path), "%s", value); - changed++; } } spa_audio_aec_set_params(impl->aec, params); @@ -1429,9 +1427,9 @@ res = spa_audio_aec_init(impl->aec, &aec_props->dict, &info); - impl->rec_info.channels = info.channels; - impl->out_info.channels = info.channels; - impl->play_info.channels = info.channels; + impl->rec_info = info; + impl->out_info = info; + impl->play_info = info; } pw_properties_free(aec_props);
View file
pipewire-0.3.70.tar.gz/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-0.3.71.tar.gz/src/modules/module-filter-chain/builtin_plugin.c
Changed
@@ -9,6 +9,7 @@ #ifdef HAVE_SNDFILE #include <sndfile.h> #endif +#include <unistd.h> #include <spa/utils/json.h> #include <spa/utils/result.h> @@ -686,10 +687,11 @@ int diff = INT_MAX; uint32_t best = 0, i; + float *samples = NULL; for (i = 0; i < MAX_RATES && filenamesi && filenamesi0; i++) { fsi = sf_open(filenamesi, SFM_READ, &infosi); - if (!fsi) + if (fsi == NULL) continue; if (labs((long)infosi.samplerate - (long)*rate) < diff) { @@ -698,13 +700,24 @@ pw_log_debug("new closest match: %d", infosi.samplerate); } } - - pw_log_debug("loading %s", filenamesbest); - float *samples = read_samples_from_sf(fsbest, infosbest, gain, delay, - offset, length, channel, rate, n_samples); - + if (fsbest != NULL) { + pw_log_info("loading best rate:%u %s", infosbest.samplerate, filenamesbest); + samples = read_samples_from_sf(fsbest, infosbest, gain, delay, + offset, length, channel, rate, n_samples); + } else { + char bufPATH_MAX; + pw_log_error("Can't open any sample file (CWD %s):", + getcwd(buf, sizeof(buf))); + for (i = 0; i < MAX_RATES && filenamesi && filenamesi0; i++) { + fsi = sf_open(filenamesi, SFM_READ, &infosi); + if (fsi == NULL) + pw_log_error(" failed file %s: %s", filenamesi, sf_strerror(fsi)); + else + pw_log_warn(" unexpectedly opened file %s", filenamesi); + } + } for (i = 0; i < MAX_RATES; i++) - if (fsi) + if (fsi != NULL) sf_close(fsi); return samples;
View file
pipewire-0.3.71.tar.gz/src/modules/module-jack-tunnel
Added
+(directory)
View file
pipewire-0.3.71.tar.gz/src/modules/module-jack-tunnel.c
Added
@@ -0,0 +1,1155 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <limits.h> +#include <math.h> + +#include "config.h" + +#include <spa/utils/result.h> +#include <spa/utils/string.h> +#include <spa/utils/json.h> +#include <spa/debug/types.h> +#include <spa/pod/builder.h> +#include <spa/param/audio/format-utils.h> +#include <spa/param/latency-utils.h> +#include <spa/param/audio/raw.h> + +#include <pipewire/impl.h> +#include <pipewire/i18n.h> +#include <pipewire/private.h> + +#include "module-jack-tunnel/weakjack.h" + +/** \page page_module_jack_tunnel PipeWire Module: JACK Tunnel + * + * The jack-tunnel module provides a source or sink that tunnels all audio to + * a JACK server. + * + * This module is usually used together with \ref page_module_jackdbus_detect that will + * automatically load the tunnel with the right parameters based on dbus + * information. + * + * ## Module Options + * + * - `jack.library`: the libjack to load, by default libjack.so.0 is searched in + * JACK_PATH directories and then some standard library paths. + * Can be an absolute path. + * - `jack.server`: the name of the JACK server to tunnel to. + * - `jack.client-name`: the name of the JACK client. + * - `jack.connect`: if jack ports should be connected automatically. Can also be + * placed per stream. + * - `tunnel.mode`: the tunnel mode, sink|source|duplex, default duplex + * - `midi.ports`: the number of midi ports. Can also be added to the stream props. + * - `source.props`: Extra properties for the source filter. + * - `sink.props`: Extra properties for the sink filter. + * + * ## General options + * + * Options with well-known behavior. + * + * - \ref PW_KEY_REMOTE_NAME + * - \ref PW_KEY_AUDIO_CHANNELS + * - \ref SPA_KEY_AUDIO_POSITION + * - \ref PW_KEY_NODE_NAME + * - \ref PW_KEY_NODE_DESCRIPTION + * - \ref PW_KEY_NODE_GROUP + * - \ref PW_KEY_NODE_VIRTUAL + * - \ref PW_KEY_MEDIA_CLASS + * - \ref PW_KEY_TARGET_OBJECT to specify the remote node.name or serial.id to link to + * + * ## Example configuration of a duplex sink/source + * + *\code{.unparsed} + * context.modules = + * { name = libpipewire-module-jack-tunnel + * args = { + * #jack.library = libjack.so.0 + * #jack.server = null + * #jack.client-name = PipeWire + * #jack.connect = true + * #tunnel.mode = duplex + * #midi.ports = 0 + * #audio.channels = 2 + * #audio.position = FL FR + * source.props = { + * # extra sink properties + * } + * sink.props = { + * # extra sink properties + * } + * } + * } + * + *\endcode + */ + +#define NAME "jack-tunnel" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +#define MAX_PORTS 128 + +#define DEFAULT_CLIENT_NAME "PipeWire" +#define DEFAULT_CHANNELS 2 +#define DEFAULT_POSITION " FL FR " +#define DEFAULT_MIDI_PORTS 1 + +#define MODULE_USAGE "( remote.name=<remote> " \ + "( jack.library=<jack library path> ) " \ + "( jack.server=<server name> ) " \ + "( jack.client-name=<name of the JACK client> " \ + "( jack.connect=<bool, autoconnect ports> " \ + "( tunnel.mode=<sink|source|duplex> " \ + "( midi.ports=<number of midi ports> " \ + "( audio.channels=<number of channels> " \ + "( audio.position=<channel map> " \ + "( source.props=<properties> ) " \ + "( sink.props=<properties> ) " + + +static const struct spa_dict_item module_props = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, + { PW_KEY_MODULE_DESCRIPTION, "Create a JACK tunnel" }, + { PW_KEY_MODULE_USAGE, MODULE_USAGE }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + +static struct weakjack jack; + +struct port { + jack_port_t *jack_port; + + enum spa_direction direction; + struct spa_latency_info latency2; + bool latency_changed2; + unsigned int is_midi:1; +}; + +struct volume { + bool mute; + uint32_t n_volumes; + float volumesSPA_AUDIO_MAX_CHANNELS; +}; + +struct stream { + struct impl *impl; + + enum spa_direction direction; + struct pw_properties *props; + struct pw_filter *filter; + struct spa_hook listener; + struct spa_audio_info_raw info; + uint32_t n_midi; + uint32_t n_ports; + struct port *portsMAX_PORTS; + struct volume volume; + + unsigned int running:1; + unsigned int connect:1; +}; + +struct impl { + struct pw_context *context; + struct pw_loop *main_loop; + struct spa_system *system; + +#define MODE_SINK (1<<0) +#define MODE_SOURCE (1<<1) +#define MODE_DUPLEX (MODE_SINK|MODE_SOURCE) + uint32_t mode; + struct pw_properties *props; + + struct pw_impl_module *module; + + struct spa_hook module_listener; + + struct pw_core *core; + struct spa_hook core_proxy_listener; + struct spa_hook core_listener; + + struct spa_io_position *position; + + struct stream source; + struct stream sink; + + uint32_t samplerate; + + jack_client_t *client; + jack_nframes_t frame_time; + + uint32_t pw_xrun; + uint32_t jack_xrun; + + unsigned int do_disconnect:1; + unsigned int done:1; + unsigned int new_xrun:1; + unsigned int fix_midi:1; +}; + +static void reset_volume(struct volume *vol, uint32_t n_volumes) +{ + uint32_t i; + vol->mute = false; + vol->n_volumes = n_volumes; + for (i = 0; i < n_volumes; i++) + vol->volumesi = 1.0f; +} + +static inline void do_volume(float *dst, const float *src, struct volume *vol, uint32_t ch, uint32_t n_samples) +{ + float v = vol->mute ? 0.0f : vol->volumesch; + + if (v == 0.0f || src == NULL) + memset(dst, 0, n_samples * sizeof(float)); + else if (v == 1.0f) + memcpy(dst, src, n_samples * sizeof(float)); + else { + uint32_t i; + for (i = 0; i < n_samples; i++) + dsti = srci * v; + } +} + +static inline void fix_midi_event(uint8_t *data, size_t size) +{ + /* fixup NoteOn with vel 0 */ + if (size > 2 && (data0 & 0xF0) == 0x90 && data2 == 0x00) { + data0 = 0x80 + (data0 & 0x0F); + data2 = 0x40; + } +} + +static void midi_to_jack(struct impl *impl, float *dst, float *src, uint32_t n_samples) +{ + struct spa_pod *pod; + struct spa_pod_sequence *seq; + struct spa_pod_control *c; + int res; + + jack.midi_clear_buffer(dst); + if (src == NULL) + return; + + if ((pod = spa_pod_from_data(src, n_samples * sizeof(float), 0, n_samples * sizeof(float))) == NULL) + return; + if (!spa_pod_is_sequence(pod)) + return; + + seq = (struct spa_pod_sequence*)pod; + + SPA_POD_SEQUENCE_FOREACH(seq, c) { + switch(c->type) { + case SPA_CONTROL_Midi: + { + uint8_t *data = SPA_POD_BODY(&c->value); + size_t size = SPA_POD_BODY_SIZE(&c->value); + + if (impl->fix_midi) + fix_midi_event(data, size); + + if ((res = jack.midi_event_write(dst, c->offset, data, size)) < 0) + pw_log_warn("midi %p: can't write event: %s", dst, + spa_strerror(res)); + break; + } + default: + break; + } + } +} + +static void jack_to_midi(float *dst, float *src, uint32_t size) +{ + struct spa_pod_builder b = { 0, }; + uint32_t i, count; + struct spa_pod_frame f; + + count = src ? jack.midi_get_event_count(src) : 0; + + spa_pod_builder_init(&b, dst, size); + spa_pod_builder_push_sequence(&b, &f, 0); + for (i = 0; i < count; i++) { + jack_midi_event_t ev; + jack.midi_event_get(&ev, src, i); + spa_pod_builder_control(&b, ev.time, SPA_CONTROL_Midi); + spa_pod_builder_bytes(&b, ev.buffer, ev.size); + } + spa_pod_builder_pop(&b, &f); +} + +static void stream_destroy(void *d) +{ + struct stream *s = d; + spa_hook_remove(&s->listener); + s->filter = NULL; +} + +static void stream_state_changed(void *d, enum pw_filter_state old, + enum pw_filter_state state, const char *error) +{ + struct stream *s = d; + struct impl *impl = s->impl; + switch (state) { + case PW_FILTER_STATE_ERROR: + case PW_FILTER_STATE_UNCONNECTED: + pw_impl_module_schedule_destroy(impl->module); + break; + case PW_FILTER_STATE_PAUSED: + s->running = false; + break; + case PW_FILTER_STATE_STREAMING: + s->running = true; + break; + default: + break; + } +} + +static void sink_process(void *d, struct spa_io_position *position) +{ + struct stream *s = d; + struct impl *impl = s->impl; + uint32_t i, n_samples = position->clock.duration; + + for (i = 0; i < s->n_ports; i++) { + struct port *p = s->portsi; + float *src, *dst; + if (p == NULL) + continue; + src = pw_filter_get_dsp_buffer(p, n_samples); + + if (p->jack_port == NULL) + continue; + + dst = jack.port_get_buffer(p->jack_port, n_samples); + if (dst == NULL) + continue; + + if (SPA_UNLIKELY(p->is_midi)) + midi_to_jack(impl, dst, src, n_samples); + else + do_volume(dst, src, &s->volume, i, n_samples); + } + pw_log_trace_fp("done %u", impl->frame_time); + if (impl->mode & MODE_SINK) { + impl->done = true; + jack.cycle_signal(impl->client, 0); + } +} + +static void source_process(void *d, struct spa_io_position *position) +{ + struct stream *s = d; + struct impl *impl = s->impl; + uint32_t i, n_samples = position->clock.duration; + + for (i = 0; i < s->n_ports; i++) { + struct port *p = s->portsi; + float *src, *dst; + + if (p == NULL) + continue; + + dst = pw_filter_get_dsp_buffer(p, n_samples); + if (dst == NULL || p->jack_port == NULL) + continue; + + src = jack.port_get_buffer (p->jack_port, n_samples); + + if (SPA_UNLIKELY(p->is_midi)) + jack_to_midi(dst, src, n_samples); + else + do_volume(dst, src, &s->volume, i, n_samples); + } + pw_log_trace_fp("done %u", impl->frame_time); + if (impl->mode == MODE_SOURCE) { + impl->done = true; + jack.cycle_signal(impl->client, 0); + } +} + +static void stream_io_changed(void *data, void *port_data, uint32_t id, void *area, uint32_t size) +{ + struct stream *s = data; + struct impl *impl = s->impl; + if (port_data == NULL) { + switch (id) { + case SPA_IO_Position: + impl->position = area; + break; + default: + break; + } + } +} + +static void param_latency_changed(struct stream *s, const struct spa_pod *param, + struct port *port) +{ + struct spa_latency_info latency; + bool update = false; + enum spa_direction direction = port->direction; + + if (spa_latency_parse(param, &latency) < 0) + return; + + if (spa_latency_info_compare(&port->latencydirection, &latency)) { + port->latencydirection = latency; + port->latency_changeddirection = update = true; + } + if (update) + jack.recompute_total_latencies(s->impl->client); +} + +static void make_stream_ports(struct stream *s) +{ + struct impl *impl = s->impl; + uint32_t i; + struct pw_properties *props; + const char *str, *prefix, *type; + char name256; + const char **audio_ports = NULL, **link_ports = NULL; + const char **midi_ports = NULL; + unsigned long jack_peer, jack_flags; + bool is_midi; + + if (s->direction == PW_DIRECTION_INPUT) { + /* sink */ + jack_peer = JackPortIsInput; + jack_flags = JackPortIsOutput; + prefix = "playback"; + } else { + /* source */ + jack_peer = JackPortIsOutput; + jack_flags = JackPortIsInput; + prefix = "capture"; + } + + if (s->connect) { + audio_ports = jack.get_ports(impl->client, NULL, JACK_DEFAULT_AUDIO_TYPE, + JackPortIsPhysical|jack_peer); + midi_ports = jack.get_ports(impl->client, NULL, JACK_DEFAULT_MIDI_TYPE, + JackPortIsPhysical|jack_peer); + } + for (i = 0; i < s->n_ports; i++) { + struct port *port = s->portsi; + if (port != NULL) { + s->portsi = NULL; + if (port->jack_port) + jack.port_unregister(impl->client, port->jack_port); + pw_filter_remove_port(port); + } + + if (i < s->info.channels) { + str = spa_debug_type_find_short_name(spa_type_audio_channel, + s->info.positioni); + if (str) + snprintf(name, sizeof(name), "%s_%s", prefix, str); + else + snprintf(name, sizeof(name), "%s_%d", prefix, i); + + props = pw_properties_new( + PW_KEY_FORMAT_DSP, "32 bit float mono audio", + PW_KEY_AUDIO_CHANNEL, str ? str : "UNK", + PW_KEY_PORT_PHYSICAL, "true", + PW_KEY_PORT_NAME, name, + NULL); + + type = JACK_DEFAULT_AUDIO_TYPE; + link_ports = audio_ports; + is_midi = false; + } else { + snprintf(name, sizeof(name), "%s_%d", prefix, i - s->info.channels); + props = pw_properties_new( + PW_KEY_FORMAT_DSP, "8 bit raw midi", + PW_KEY_PORT_NAME, name, + PW_KEY_PORT_PHYSICAL, "true", + NULL); + + type = JACK_DEFAULT_MIDI_TYPE; + link_ports = midi_ports; + is_midi = true; + } + + port = pw_filter_add_port(s->filter, + s->direction, + PW_FILTER_PORT_FLAG_MAP_BUFFERS, + sizeof(struct port), + props, NULL, 0); + + port->is_midi = is_midi; + port->jack_port = jack.port_register (impl->client, name, type, jack_flags, 0); + + if (link_ports != NULL && link_portsi != NULL) { + if (jack_flags & JackPortIsOutput) { + if (jack.connect(impl->client, jack.port_name(port->jack_port), link_portsi)) + pw_log_warn("cannot connect ports"); + } else { + if (jack.connect(impl->client, link_portsi, jack.port_name(port->jack_port))) + pw_log_warn("cannot connect ports"); + } + } + s->portsi = port; + } + if (audio_ports) + jack.free(audio_ports); + if (midi_ports) + jack.free(midi_ports); +} + +static struct spa_pod *make_props_param(struct spa_pod_builder *b, + struct volume *vol) +{ + return spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_mute, SPA_POD_Bool(vol->mute), + SPA_PROP_channelVolumes, SPA_POD_Array(sizeof(float), + SPA_TYPE_Float, vol->n_volumes, vol->volumes)); +} + +static void parse_props(struct stream *s, const struct spa_pod *param) +{ + struct spa_pod_object *obj = (struct spa_pod_object *) param; + struct spa_pod_prop *prop; + uint8_t buffer1024; + struct spa_pod_builder b; + const struct spa_pod *params1; + + SPA_POD_OBJECT_FOREACH(obj, prop) { + switch (prop->key) { + case SPA_PROP_mute: + { + bool mute; + if (spa_pod_get_bool(&prop->value, &mute) == 0) + s->volume.mute = mute; + break; + } + case SPA_PROP_channelVolumes: + { + uint32_t n; + float volsSPA_AUDIO_MAX_CHANNELS; + if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, + vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + s->volume.n_volumes = n; + for (n = 0; n < s->volume.n_volumes; n++) + s->volume.volumesn = volsn; + } + break; + } + default: + break; + } + } + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + params0 = make_props_param(&b, &s->volume); + + pw_filter_update_params(s->filter, NULL, params, 1); +} + +static void stream_param_changed(void *data, void *port_data, uint32_t id, + const struct spa_pod *param) +{ + struct stream *s = data; + if (port_data != NULL) { + switch (id) { + case SPA_PARAM_Latency: + param_latency_changed(s, param, port_data); + break; + } + } else { + switch (id) { + case SPA_PARAM_PortConfig: + pw_log_debug("PortConfig"); + make_stream_ports(s); + break; + case SPA_PARAM_Props: + pw_log_debug("Props"); + parse_props(s, param); + break; + } + } +} + +static const struct pw_filter_events sink_events = { + PW_VERSION_FILTER_EVENTS, + .destroy = stream_destroy, + .state_changed = stream_state_changed, + .param_changed = stream_param_changed, + .io_changed = stream_io_changed, + .process = sink_process +}; + +static const struct pw_filter_events source_events = { + PW_VERSION_FILTER_EVENTS, + .destroy = stream_destroy, + .state_changed = stream_state_changed, + .param_changed = stream_param_changed, + .io_changed = stream_io_changed, + .process = source_process, +}; + +static int make_stream(struct stream *s, const char *name) +{ + struct impl *impl = s->impl; + uint32_t n_params; + const struct spa_pod *params4; + uint8_t buffer1024; + struct spa_pod_builder b; + struct spa_latency_info latency; + + spa_zero(latency); + n_params = 0; + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + + s->filter = pw_filter_new(impl->core, name, s->props); + s->props = NULL; + if (s->filter == NULL) + return -errno; + + if (s->direction == PW_DIRECTION_INPUT) { + pw_filter_add_listener(s->filter, &s->listener, + &sink_events, s); + } else { + pw_filter_add_listener(s->filter, &s->listener, + &source_events, s); + } + + reset_volume(&s->volume, s->info.channels); + + n_params = 0; + paramsn_params++ = spa_format_audio_raw_build(&b, + SPA_PARAM_EnumFormat, &s->info); + paramsn_params++ = spa_format_audio_raw_build(&b, + SPA_PARAM_Format, &s->info); + paramsn_params++ = make_props_param(&b, &s->volume); + + return pw_filter_connect(s->filter, + PW_FILTER_FLAG_DRIVER | + PW_FILTER_FLAG_RT_PROCESS | + PW_FILTER_FLAG_CUSTOM_LATENCY, + params, n_params); +} + +static int create_filters(struct impl *impl) +{ + int res = 0; + + if (impl->mode & MODE_SINK) + res = make_stream(&impl->sink, "JACK Sink"); + + if (impl->mode & MODE_SOURCE) + res = make_stream(&impl->source, "JACK Source"); + + return res; +} + +static void *jack_process_thread(void *arg) +{ + struct impl *impl = arg; + bool source_running, sink_running; + jack_nframes_t nframes; + + while (true) { + nframes = jack.cycle_wait (impl->client); + + source_running = impl->source.running; + sink_running = impl->sink.running; + + impl->frame_time = jack.frame_time(impl->client); + + pw_log_trace_fp("process %d %u %u %p %d", nframes, source_running, + sink_running, impl->position, impl->frame_time); + + if (impl->new_xrun) { + pw_log_warn("Xrun JACK:%u PipeWire:%u", impl->jack_xrun, impl->pw_xrun); + impl->new_xrun = false; + } + + if (impl->position) { + struct spa_io_clock *c = &impl->position->clock; + jack_nframes_t current_frames; + jack_time_t current_usecs; + jack_time_t next_usecs; + float period_usecs; + jack_position_t pos; + + jack.get_cycle_times(impl->client, + ¤t_frames, ¤t_usecs, + &next_usecs, &period_usecs); + + c->nsec = current_usecs * SPA_NSEC_PER_USEC; + c->rate = SPA_FRACTION(1, impl->samplerate); + c->position = current_frames; + c->duration = nframes; + c->delay = 0; + c->rate_diff = 1.0; + c->next_nsec = next_usecs * SPA_NSEC_PER_USEC; + + c->target_rate = c->rate; + c->target_duration = c->duration; + + jack.transport_query (impl->client, &pos); + } + if (impl->mode & MODE_SINK && sink_running) { + impl->done = false; + pw_filter_trigger_process(impl->sink.filter); + } else if (impl->mode == MODE_SOURCE && source_running) { + impl->done = false; + pw_filter_trigger_process(impl->source.filter); + } else { + jack.cycle_signal(impl->client, 0); + } + } + return NULL; +} + +static int jack_xrun(void *arg) +{ + struct impl *impl = arg; + if (impl->done) + impl->jack_xrun++; + else + impl->pw_xrun++; + impl->new_xrun = true; + return 0; +} + +static int +do_schedule_destroy(struct spa_loop *loop, + bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct impl *impl = user_data; + pw_impl_module_schedule_destroy(impl->module); + return 0; +} + +void module_schedule_destroy(struct impl *impl) +{ + pw_loop_invoke(impl->main_loop, do_schedule_destroy, 1, NULL, 0, false, impl); +} + +static void jack_info_shutdown(jack_status_t code, const char* reason, void *arg) +{ + struct impl *impl = arg; + pw_log_warn("shutdown: %s (%08x)", reason, code); + module_schedule_destroy(impl); +} + +static void stream_update_latency(struct stream *s) +{ + uint8_t buffer1024; + struct spa_pod_builder b; + const struct spa_pod *params2; + uint32_t i, n_params = 0; + + for (i = 0; i < s->n_ports; i++) { + struct port *port = s->portsi; + if (port == NULL) + continue; + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + n_params = 0; + if (port->latency_changeds->direction) { + paramsn_params++ = spa_latency_build(&b, + SPA_PARAM_Latency, &port->latencys->direction); + port->latency_changeds->direction = false; + } + if (s->filter) + pw_filter_update_params(s->filter, port, params, n_params); + } +} + +static int +do_update_latency(struct spa_loop *loop, + bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct impl *impl = user_data; + + if ((impl->mode & MODE_SINK)) + stream_update_latency(&impl->sink); + + if ((impl->mode & MODE_SOURCE)) + stream_update_latency(&impl->source); + + return 0; +} + +static bool stream_handle_latency(struct stream *s, jack_latency_callback_mode_t mode) +{ + uint32_t i; + struct spa_latency_info latency; + jack_latency_range_t range; + bool update = false; + enum spa_direction other = SPA_DIRECTION_REVERSE(s->direction); + struct port *port; + + if (mode == JackPlaybackLatency) { + for (i = 0; i < s->n_ports; i++) { + port = s->portsi; + if (port == NULL || port->jack_port == NULL) + continue; + + jack.port_get_latency_range(port->jack_port, mode, &range); + + latency.direction = s->direction; + latency.min_rate = range.min; + latency.max_rate = range.max; + pw_log_debug("port latency %d %d %d", mode, range.min, range.max); + + if (spa_latency_info_compare(&latency, &port->latencys->direction)) { + port->latencys->direction = latency; + port->latency_changeds->direction = update = true; + } + } + } else if (mode == JackCaptureLatency) { + for (i = 0; i < s->n_ports; i++) { + port = s->portsi; + if (port == NULL || port->jack_port == NULL) + continue; + if (port->latency_changedother) { + range.min = port->latencyother.min_rate; + range.max = port->latencyother.max_rate; + jack.port_set_latency_range(port->jack_port, mode, &range); + port->latency_changedother = false; + } + } + } + return update; +} + + +static void jack_latency(jack_latency_callback_mode_t mode, void *arg) +{ + struct impl *impl = arg; + bool update = false; + + if ((impl->mode & MODE_SINK)) + update |= stream_handle_latency(&impl->sink, mode); + + if ((impl->mode & MODE_SOURCE)) + update |= stream_handle_latency(&impl->source, mode); + + if (update) + pw_loop_invoke(impl->main_loop, do_update_latency, 0, NULL, 0, false, impl); +} + +static int create_jack_client(struct impl *impl) +{ + const char *server_name, *client_name; + jack_options_t options = JackNullOption; + jack_status_t status; + + server_name = pw_properties_get(impl->props, "jack.server"); + if (server_name != NULL) + options |= JackServerName; + + client_name = pw_properties_get(impl->props, "jack.client-name"); + if (client_name == NULL) + client_name = DEFAULT_CLIENT_NAME; + + impl->client = jack.client_open(client_name, options, &status, server_name); + if (impl->client == NULL) { + pw_log_error ("jack_client_open() failed 0x%2.0x\n", status); + return -EIO; + } + jack.on_info_shutdown(impl->client, jack_info_shutdown, impl); + jack.set_process_thread(impl->client, jack_process_thread, impl); + jack.set_xrun_callback(impl->client, jack_xrun, impl); + jack.set_latency_callback(impl->client, jack_latency, impl); + + impl->samplerate = jack.get_sample_rate(impl->client); + impl->source.info.rate = impl->samplerate; + impl->sink.info.rate = impl->samplerate; + + return 0; +} + +static int start_jack_clients(struct impl *impl) +{ + jack.activate(impl->client); + return 0; +} + +static void core_error(void *data, uint32_t id, int seq, int res, const char *message) +{ + struct impl *impl = data; + + 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 = core_error, +}; + +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) +{ + if (impl->client) { + jack.deactivate(impl->client); + jack.client_close(impl->client); + } + if (impl->source.filter) + pw_filter_destroy(impl->source.filter); + if (impl->sink.filter) + pw_filter_destroy(impl->sink.filter); + if (impl->core && impl->do_disconnect) + pw_core_disconnect(impl->core); + + pw_properties_free(impl->sink.props); + pw_properties_free(impl->source.props); + pw_properties_free(impl->props); + + free(impl); +} + +static void module_destroy(void *data) +{ + struct impl *impl = data; + 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 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); + info->format = SPA_AUDIO_FORMAT_F32P; + info->rate = 0; + 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->sink.props, key) == NULL) + pw_properties_set(impl->sink.props, key, str); + if (pw_properties_get(impl->source.props, key) == NULL) + pw_properties_set(impl->source.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 pw_properties *props = NULL; + struct impl *impl; + const char *str; + int res; + + PW_LOG_TOPIC_INIT(mod_topic); + + impl = calloc(1, sizeof(struct impl)); + if (impl == NULL) + return -errno; + + pw_log_debug("module %p: new %s", impl, args); + + if (args == NULL) + args = ""; + + props = pw_properties_new_string(args); + if (props == NULL) { + res = -errno; + pw_log_error( "can't create properties: %m"); + goto error; + } + impl->props = props; + + if ((str = pw_properties_get(props, "jack.library")) == NULL) + str = "libjack.so.0"; + + if ((res = weakjack_load(&jack, str)) < 0) { + pw_log_error( "can't load '%s': %s", str, spa_strerror(res)); + goto error; + } + + impl->sink.props = pw_properties_new(NULL, NULL); + impl->source.props = pw_properties_new(NULL, NULL); + if (impl->source.props == NULL || impl->sink.props == NULL) { + res = -errno; + pw_log_error( "can't create properties: %m"); + goto error; + } + + impl->module = module; + impl->context = context; + impl->main_loop = pw_context_get_main_loop(context); + impl->system = impl->main_loop->system; + + impl->source.impl = impl; + impl->source.direction = PW_DIRECTION_OUTPUT; + impl->sink.impl = impl; + impl->sink.direction = PW_DIRECTION_INPUT; + + impl->mode = MODE_DUPLEX; + if ((str = pw_properties_get(props, "tunnel.mode")) != NULL) { + if (spa_streq(str, "source")) { + impl->mode = MODE_SOURCE; + } else if (spa_streq(str, "sink")) { + impl->mode = MODE_SINK; + } else if (spa_streq(str, "duplex")) { + impl->mode = MODE_DUPLEX; + } else { + pw_log_error("invalid tunnel.mode '%s'", str); + res = -EINVAL; + goto error; + } + } + + if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL) + pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true"); + if (pw_properties_get(props, PW_KEY_NODE_GROUP) == NULL) + pw_properties_set(props, PW_KEY_NODE_GROUP, "jack-group"); + if (pw_properties_get(props, PW_KEY_NODE_ALWAYS_PROCESS) == NULL) + pw_properties_set(props, PW_KEY_NODE_ALWAYS_PROCESS, "true"); + + pw_properties_set(impl->sink.props, PW_KEY_MEDIA_CLASS, "Audio/Sink"); + pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_DRIVER, "30001"); + pw_properties_set(impl->sink.props, PW_KEY_NODE_NAME, "jack_sink"); + pw_properties_set(impl->sink.props, PW_KEY_NODE_DESCRIPTION, "JACK Sink"); + + pw_properties_set(impl->source.props, PW_KEY_MEDIA_CLASS, "Audio/Source"); + pw_properties_set(impl->source.props, PW_KEY_PRIORITY_DRIVER, "30000"); + pw_properties_set(impl->source.props, PW_KEY_NODE_NAME, "jack_source"); + pw_properties_set(impl->source.props, PW_KEY_NODE_DESCRIPTION, "JACK Source"); + + if ((str = pw_properties_get(props, "sink.props")) != NULL) + pw_properties_update_string(impl->sink.props, str, strlen(str)); + if ((str = pw_properties_get(props, "source.props")) != NULL) + pw_properties_update_string(impl->source.props, str, strlen(str)); + + copy_props(impl, props, PW_KEY_AUDIO_CHANNELS); + copy_props(impl, props, SPA_KEY_AUDIO_POSITION); + copy_props(impl, props, PW_KEY_NODE_ALWAYS_PROCESS); + copy_props(impl, props, PW_KEY_NODE_GROUP); + copy_props(impl, props, PW_KEY_NODE_VIRTUAL); + copy_props(impl, props, "jack.connect"); + + parse_audio_info(impl->source.props, &impl->source.info); + parse_audio_info(impl->sink.props, &impl->sink.info); + + impl->source.n_midi = pw_properties_get_uint32(impl->source.props, + "midi.ports", DEFAULT_MIDI_PORTS); + impl->sink.n_midi = pw_properties_get_uint32(impl->sink.props, + "midi.ports", DEFAULT_MIDI_PORTS); + + impl->source.n_ports = impl->source.n_midi + impl->source.info.channels; + impl->sink.n_ports = impl->sink.n_midi + impl->sink.info.channels; + if (impl->source.n_ports > MAX_PORTS || impl->sink.n_ports > MAX_PORTS) { + pw_log_error("too many ports"); + res = -EINVAL; + goto error; + } + + impl->source.connect = pw_properties_get_bool(impl->source.props, + "jack.connect", true); + impl->sink.connect = pw_properties_get_bool(impl->sink.props, + "jack.connect", true); + + impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core); + if (impl->core == NULL) { + str = pw_properties_get(props, PW_KEY_REMOTE_NAME); + impl->core = pw_context_connect(impl->context, + pw_properties_new( + PW_KEY_REMOTE_NAME, str, + NULL), + 0); + impl->do_disconnect = true; + } + if (impl->core == NULL) { + res = -errno; + pw_log_error("can't connect: %m"); + goto error; + } + + 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 = create_jack_client(impl)) < 0) + goto error; + + if ((res = create_filters(impl)) < 0) + goto error; + + if ((res = start_jack_clients(impl)) < 0) + goto error; + + pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl); + + pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props)); + + return 0; + +error: + impl_destroy(impl); + return res; +}
View file
pipewire-0.3.71.tar.gz/src/modules/module-jack-tunnel/weakjack.h
Added
@@ -0,0 +1,195 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_WEAK_JACK_H +#define PIPEWIRE_WEAK_JACK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "config.h" + +#include <dlfcn.h> + +#include <jack/jack.h> +#include <jack/transport.h> +#include <jack/midiport.h> + +struct weakjack { + jack_nframes_t (*cycle_wait) (jack_client_t* client); + void (*cycle_signal) (jack_client_t* client, int status); + + jack_nframes_t (*frame_time) (const jack_client_t *); + int (*get_cycle_times) (const jack_client_t *client, + jack_nframes_t *current_frames, + jack_time_t *current_usecs, + jack_time_t *next_usecs, + float *period_usecs); + jack_transport_state_t (*transport_query) (const jack_client_t *client, + jack_position_t *pos); + + jack_client_t * (*client_open) (const char *client_name, + jack_options_t options, + jack_status_t *status, ...); + int (*client_close) (jack_client_t *client); + + + int (*activate) (jack_client_t *client); + int (*deactivate) (jack_client_t *client); + + jack_nframes_t (*get_sample_rate) (jack_client_t *); + + int (*recompute_total_latencies) (jack_client_t *client); + + jack_port_t * (*port_register) (jack_client_t *client, + const char *port_name, + const char *port_type, + unsigned long flags, + unsigned long buffer_size); + int (*port_unregister) (jack_client_t *client, jack_port_t *port); + void * (*port_get_buffer) (jack_port_t *port, jack_nframes_t); + const char * (*port_name) (const jack_port_t *port); + + void (*port_get_latency_range) (jack_port_t *port, + jack_latency_callback_mode_t mode, + jack_latency_range_t *range); + void (*port_set_latency_range) (jack_port_t *port, + jack_latency_callback_mode_t mode, + jack_latency_range_t *range); + + + int (*connect) (jack_client_t *client, + const char *source_port, + const char *destination_port); + int (*disconnect) (jack_client_t *client, + const char *source_port, + const char *destination_port); + + const char ** (*get_ports) (jack_client_t *client, + const char *port_name_pattern, + const char *type_name_pattern, + unsigned long flags); + void (*free) (void* ptr); + + int (*set_process_thread) (jack_client_t* client, + JackThreadCallback thread_callback, void *arg); + int (*set_xrun_callback) (jack_client_t *client, + JackXRunCallback xrun_callback, void *arg); + void (*on_info_shutdown) (jack_client_t *client, + JackInfoShutdownCallback shutdown_callback, void *arg); + int (*set_latency_callback) (jack_client_t *client, + JackLatencyCallback latency_callback, void *arg); + + void (*midi_clear_buffer) (void *port_buffer); + int (*midi_event_write) (void *port_buffer, + jack_nframes_t time, + const jack_midi_data_t *data, + size_t data_size); + uint32_t (*midi_get_event_count) (void* port_buffer); + int (*midi_event_get) (jack_midi_event_t *event, void *port_buffer, + uint32_t event_index); + +}; + + +static inline int weakjack_load_by_path(struct weakjack *jack, const char *path) +{ + void *hnd; + + hnd = dlopen(path, RTLD_NOW); + if (hnd == NULL) + return -errno; + + pw_log_info("opened libjack: %s", path); + +#define LOAD_SYM(name) ({ \ + if ((jack->name = dlsym(hnd, "jack_"#name )) == NULL) \ + return -ENOSYS; \ +}) + spa_zero(*jack); + LOAD_SYM(cycle_wait); + LOAD_SYM(cycle_signal); + LOAD_SYM(frame_time); + LOAD_SYM(get_cycle_times); + LOAD_SYM(transport_query); + + LOAD_SYM(client_open); + LOAD_SYM(client_close); + + LOAD_SYM(activate); + LOAD_SYM(deactivate); + + LOAD_SYM(get_sample_rate); + + LOAD_SYM(recompute_total_latencies); + + LOAD_SYM(port_register); + LOAD_SYM(port_unregister); + LOAD_SYM(port_get_buffer); + LOAD_SYM(port_name); + + LOAD_SYM(port_get_latency_range); + LOAD_SYM(port_set_latency_range); + + LOAD_SYM(connect); + LOAD_SYM(disconnect); + + LOAD_SYM(get_ports); + LOAD_SYM(free); + + LOAD_SYM(set_process_thread); + LOAD_SYM(set_xrun_callback); + LOAD_SYM(on_info_shutdown); + LOAD_SYM(set_latency_callback); + + LOAD_SYM(midi_clear_buffer); + LOAD_SYM(midi_event_write); + LOAD_SYM(midi_get_event_count); + LOAD_SYM(midi_event_get); +#undef LOAD_SYM + + return 0; +} + +static inline int weakjack_load(struct weakjack *jack, const char *lib) +{ + int res = -ENOENT; + + if (lib0 != '/') { + const char *search_dirs, *p, *state = NULL; + char pathPATH_MAX; + size_t len; + + search_dirs = getenv("LIBJACK_PATH"); + if (!search_dirs) + search_dirs = PREFIX "/lib64/:" PREFIX "/lib/:" + "/usr/lib64/:/usr/lib/:" LIBDIR; + + while ((p = pw_split_walk(search_dirs, ":", &len, &state))) { + int pathlen; + + if (len >= sizeof(path)) { + res = -ENAMETOOLONG; + continue; + } + pathlen = snprintf(path, sizeof(path), "%.*s/%s", (int) len, p, lib); + if (pathlen < 0 || (size_t) pathlen >= sizeof(path)) { + res = -ENAMETOOLONG; + continue; + } + if ((res = weakjack_load_by_path(jack, path)) == 0) + break; + } + } else { + res = weakjack_load_by_path(jack, lib); + } + return res; +} + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_WEAK_JACK_H */
View file
pipewire-0.3.71.tar.gz/src/modules/module-jackdbus-detect.c
Added
@@ -0,0 +1,394 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2016 Wim Taymans <wim.taymans@gmail.com> */ +/* SPDX-FileCopyrightText: Copyright © 2019 Red Hat Inc. */ +/* SPDX-License-Identifier: MIT */ + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "config.h" + +#include <dbus/dbus.h> + +#include <spa/utils/string.h> +#include <spa/utils/result.h> +#include <spa/support/dbus.h> + +#include "pipewire/context.h" +#include "pipewire/impl-client.h" +#include "pipewire/log.h" +#include "pipewire/module.h" +#include "pipewire/utils.h" + +/** \page page_module_jackdbus_detect PipeWire Module: JACK DBus detect + * + * Automaticall creates a sink/source when a jackdbus server is started + * and connect to JACK. + * + * ## Module Options + * + * There are no module-specific options, all arguments are passed to + * \ref page_module_jack_tunnel. + * + * ## Example configuration + *\code{.unparsed} + * context.modules = + * { name = libpipewire-jackdbus-detect + * args { + * #jack.server = null + * #tunnel.mode = duplex + * #audio.channels = 2 + * #audio.position = FL FR + * source.props = { + * # extra sink properties + * } + * sink.props = { + * # extra sink properties + * } + * } + * } + * + *\endcode + * + */ + +#define NAME "jackdbus-detect" + +#define JACK_SERVICE_NAME "org.jackaudio.service" +#define JACK_INTERFACE_NAME "org.jackaudio.JackControl" +#define JACK_INTERFACE_PATH "/org/jackaudio/Controller" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +struct impl { + struct pw_context *context; + struct pw_properties *properties; + + struct spa_dbus_connection *conn; + DBusConnection *bus; + + struct spa_hook module_listener; + + DBusPendingCall *pending_call; + bool is_started; + + struct pw_impl_module *jack_tunnel; + struct spa_hook tunnel_listener; +}; + +static void tunnelmodule_destroy(void *data) +{ + struct impl *impl = data; + spa_hook_remove(&impl->tunnel_listener); + impl->jack_tunnel = NULL; +} + +static const struct pw_impl_module_events tunnelmodule_events = { + PW_VERSION_IMPL_MODULE_EVENTS, + .destroy = tunnelmodule_destroy, +}; + +static int load_jack_tunnel(struct impl *impl) +{ + FILE *f; + char *args; + size_t size; + int res = 0; + + if ((f = open_memstream(&args, &size)) == NULL) { + res = -errno; + pw_log_error("Can't open memstream: %m"); + goto done; + } + + fprintf(f, "{"); + if (impl->properties != NULL) + pw_properties_serialize_dict(f, &impl->properties->dict, 0); + fprintf(f, " }"); + fclose(f); + + pw_log_info("loading module args:'%s'", args); + impl->jack_tunnel = pw_context_load_module(impl->context, + "libpipewire-module-jack-tunnel", + args, NULL); + free(args); + + if (impl->jack_tunnel == NULL) { + res = -errno; + pw_log_error("Can't create tunnel: %m"); + goto done; + } + + pw_impl_module_add_listener(impl->jack_tunnel, + &impl->tunnel_listener, &tunnelmodule_events, impl); +done: + return res; +} + +static void unload_jack_tunnel(struct impl *impl) +{ + if (impl->jack_tunnel) { + pw_impl_module_destroy(impl->jack_tunnel); + impl->jack_tunnel = NULL; + } +} +static void set_started(struct impl *impl, bool started) +{ + if (impl->is_started != started) { + pw_log_info("New state %d", started); + impl->is_started = started; + if (started) + load_jack_tunnel(impl); + else + unload_jack_tunnel(impl); + } +} + +static void impl_free(struct impl *impl) +{ + set_started(impl, false); + + if (impl->bus) + dbus_connection_unref(impl->bus); + spa_dbus_connection_destroy(impl->conn); + + pw_properties_free(impl->properties); + + free(impl); +} + +static void module_destroy(void *data) +{ + struct impl *impl = data; + spa_hook_remove(&impl->module_listener); + impl_free(impl); +} + +static const struct pw_impl_module_events module_events = { + PW_VERSION_IMPL_MODULE_EVENTS, + .destroy = module_destroy, +}; + +static void set_pending_call(struct impl *impl, DBusPendingCall *pending) +{ + if (impl->pending_call != NULL) { + dbus_pending_call_cancel(impl->pending_call); + dbus_pending_call_unref(impl->pending_call); + } + impl->pending_call = pending; +} + +static void on_is_started_received(DBusPendingCall *pending, + void *user_data) +{ + struct impl *impl = user_data; + DBusMessage *m; + DBusError error; + dbus_bool_t started = false; + + m = dbus_pending_call_steal_reply(pending); + dbus_pending_call_unref(pending); + impl->pending_call = NULL; + + dbus_error_init(&error); + + if (!m) { + pw_log_error("Failed to receive reply"); + goto error; + } + if (dbus_message_is_error(m, DBUS_ERROR_NAME_HAS_NO_OWNER)) { + pw_log_info("JACK DBus is not running"); + goto error; + } + if (dbus_message_get_type(m) == DBUS_MESSAGE_TYPE_ERROR) { + const char *message = "unknown"; + dbus_message_get_args(m, NULL, DBUS_TYPE_STRING, &message, DBUS_TYPE_INVALID); + pw_log_warn("Failed to receive jackdbus reply: %s: %s", + dbus_message_get_error_name(m), message); + goto error; + } + + dbus_message_get_args(m, &error, + DBUS_TYPE_BOOLEAN, &started, + DBUS_TYPE_INVALID); + dbus_message_unref(m); + + if (dbus_error_is_set(&error)) { + pw_log_warn("Could not get jackdbus state: %s", error.message); + goto error; + } + + pw_log_info("Got jackdbus state %d", started); + set_started(impl, started); + + return; +error: + impl->is_started = false; + dbus_error_free(&error); +} + +static void check_jack_running(struct impl *impl) +{ + DBusMessage *m; + DBusPendingCall *pending; + + impl->is_started = false; + + m = dbus_message_new_method_call(JACK_SERVICE_NAME, + JACK_INTERFACE_PATH, + JACK_INTERFACE_NAME, + "IsStarted"); + + dbus_connection_send_with_reply(impl->bus, m, &pending, -1); + dbus_pending_call_set_notify(pending, on_is_started_received, impl, NULL); + + set_pending_call(impl, pending); +} + +static DBusHandlerResult filter_handler(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct impl *impl = user_data; + DBusError error; + + dbus_error_init(&error); + + if (dbus_message_is_signal(message, "org.freedesktop.DBus", + "NameOwnerChanged")) { + const char *name, *old, *new; + if (!dbus_message_get_args(message, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) { + pw_log_error("Failed to get OwnerChanged args: %s", + error.message); + goto not_handled; + } + if (!spa_streq(name, JACK_SERVICE_NAME)) + goto not_handled; + + pw_log_info("NameOwnerChanged %s -> %s", old, new); + if (spa_streq(new, "")) { + set_pending_call(impl, NULL); + set_started(impl, false); + } else { + check_jack_running(impl); + } + } + else if (dbus_message_is_signal(message, JACK_INTERFACE_NAME, + "ServerStarted")) { + pw_log_info("ServerStarted"); + set_started(impl, true); + } + else if (dbus_message_is_signal(message, JACK_INTERFACE_NAME, + "ServerStopped")) { + pw_log_info("ServerStopped"); + set_started(impl, false); + } + return DBUS_HANDLER_RESULT_HANDLED; + +not_handled: + dbus_error_free(&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static int init_dbus_connection(struct impl *impl) +{ + DBusError error; + + impl->bus = spa_dbus_connection_get(impl->conn); + if (impl->bus == NULL) + return -EIO; + + dbus_error_init(&error); + + /* XXX: we don't handle dbus reconnection yet, so ref the handle instead */ + dbus_connection_ref(impl->bus); + + dbus_connection_add_filter(impl->bus, filter_handler, impl, NULL); + + dbus_bus_add_match(impl->bus, + "type='signal'," + "sender='org.freedesktop.DBus'," + "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'", &error); + if (dbus_error_is_set(&error)) + goto error; + + dbus_bus_add_match(impl->bus, + "type='signal'," + "sender='" JACK_SERVICE_NAME "'," + "interface='" JACK_INTERFACE_NAME "'," + "member='ServerStarted'", &error); + if (dbus_error_is_set(&error)) + goto error; + + dbus_bus_add_match(impl->bus, + "type='signal'," + "sender='" JACK_SERVICE_NAME "'," + "interface='" JACK_INTERFACE_NAME "'," + "member='ServerStopped'", &error); + if (dbus_error_is_set(&error)) + goto error; + + check_jack_running(impl); + + return 0; +error: + pw_log_error("Failed to add listener: %s", error.message); + dbus_error_free(&error); + return -EIO; +} + +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 spa_dbus *dbus; + const struct spa_support *support; + uint32_t n_support; + int res; + + PW_LOG_TOPIC_INIT(mod_topic); + + support = pw_context_get_support(context, &n_support); + + dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus); + if (dbus == NULL) + return -ENOTSUP; + + impl = calloc(1, sizeof(struct impl)); + if (impl == NULL) + return -ENOMEM; + + pw_log_debug("module %p: new", impl); + + impl->context = context; + impl->properties = args ? pw_properties_new_string(args) : NULL; + + impl->conn = spa_dbus_get_connection(dbus, SPA_DBUS_TYPE_SESSION); + if (impl->conn == NULL) { + res = -errno; + goto error; + } + + if ((res = init_dbus_connection(impl)) < 0) + goto error; + + pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl); + + return 0; + + error: + impl_free(impl); + pw_log_error("Failed to connect to session bus: %s", spa_strerror(res)); + return res; +}
View file
pipewire-0.3.70.tar.gz/src/modules/module-pipe-tunnel.c -> pipewire-0.3.71.tar.gz/src/modules/module-pipe-tunnel.c
Changed
@@ -43,16 +43,16 @@ * - `stream.props`: Extra properties for the local stream. * * When `tunnel.mode` is `capture`, a capture stream on the default source is - * created. Samples read from the pipe will be the contents of the captured source. + * created. The samples captured from the source will be written to the pipe. * - * When `tunnel.mode` is `sink`, a sink node is created. Samples read from the - * pipe will be the samples played on the sink. + * When `tunnel.mode` is `sink`, a sink node is created. Samples played on the + * sink will be written to the pipe. * * When `tunnel.mode` is `playback`, a playback stream on the default sink is - * created. Samples written to the pipe will be played on the sink. + * created. The samples read from the pipe will be played on the sink. * - * When `tunnel.mode` is `source`, a source node is created. Samples written to - * the pipe will be made available to streams connected to the source. + * When `tunnel.mode` is `source`, a source node is created. Samples read from + * the the pipe will be made available on the source. * * When `pipe.filename` is not given, a default fifo in `/tmp/fifo_input` or * `/tmp/fifo_output` will be created that can be written and read respectively, @@ -103,7 +103,7 @@ #define NAME "pipe-tunnel" -#define DEFAULT_CAPTURE_FILENAME "/tmp/fifo_input" +#define DEFAULT_CAPTURE_FILENAME "/tmp/fifo_input" #define DEFAULT_PLAYBACK_FILENAME "/tmp/fifo_output" #define DEFAULT_FORMAT "S16"
View file
pipewire-0.3.70.tar.gz/src/modules/module-profiler.c -> pipewire-0.3.71.tar.gz/src/modules/module-profiler.c
Changed
@@ -74,6 +74,8 @@ struct pw_context *context; struct pw_properties *properties; + struct pw_loop *data_loop; + struct spa_hook context_listener; struct spa_hook module_listener; @@ -284,7 +286,7 @@ static void stop_listener(struct impl *impl) { if (impl->listening) { - pw_loop_invoke(impl->context->data_loop, + pw_loop_invoke(impl->data_loop, do_stop, SPA_ID_INVALID, NULL, 0, true, impl); impl->listening = false; } @@ -338,7 +340,7 @@ if (++impl->busy == 1) { pw_log_info("%p: starting profiler", impl); - pw_loop_invoke(impl->context->data_loop, + pw_loop_invoke(impl->data_loop, do_start, SPA_ID_INVALID, NULL, 0, false, impl); impl->listening = true; } @@ -411,6 +413,7 @@ impl->context = context; impl->properties = props; + impl->data_loop = pw_context_get_data_loop(impl->context)->loop; spa_ringbuffer_init(&impl->buffer);
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-native.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-native.c
Changed
@@ -199,8 +199,8 @@ { struct spa_pod *pod; pw_logt_debug(mod_topic_connection, - "%s: id:%d op:%d size:%d seq:%d", prefix, - msg->id, msg->opcode, msg->size, msg->seq); + "%s: id:%d op:%d size:%d seq:%d fds:%d", prefix, + msg->id, msg->opcode, msg->size, msg->seq, msg->n_fds); if ((pod = get_first_pod_from_data(msg->data, msg->size, 0)) != NULL) spa_debug_pod(0, NULL, pod); @@ -285,6 +285,7 @@ break; if (client->core_resource == NULL) { + pw_log_debug("%p: no core resource", client); res = -EPROTO; goto error; }
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-native/connection.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-native/connection.c
Changed
@@ -258,10 +258,12 @@ return -errno; cmsgs_truncated: + pw_log_debug("connection %p: cmsg truncated", conn); close_all_fds(&msg, CMSG_FIRSTHDR(&msg)); return -EPROTO; too_many_fds: + pw_log_debug("connection %p: too many fds", conn); close_all_fds(&msg, cmsg); return -EPROTO; } @@ -269,16 +271,22 @@ static void clear_buffer(struct buffer *buf, bool fds) { uint32_t i; + + pw_log_debug("clear fds:%d", fds); if (fds) { for (i = 0; i < buf->n_fds; i++) { pw_log_debug("%p: close fd:%d", buf, buf->fdsi); close(buf->fdsi); } + buf->n_fds = 0; + buf->fds_offset = 0; + } else { + buf->n_fds -= SPA_MIN(buf->fds_offset, buf->n_fds); + memmove(buf->fds, &buf->fdsbuf->fds_offset, buf->n_fds * sizeof(int)); + buf->fds_offset = 0; } - buf->n_fds = 0; buf->buffer_size = 0; buf->offset = 0; - buf->fds_offset = 0; } /** Prepare connection for calling from reentered context. @@ -709,8 +717,9 @@ if (mod_topic_connection->level >= SPA_LOG_LEVEL_DEBUG) { 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); + ">>>>>>>>> out: id:%d op:%d size:%d seq:%d fds:%d", + buf->msg.id, buf->msg.opcode, size, buf->msg.seq, + buf->msg.n_fds); spa_debug_pod(0, NULL, SPA_PTROFF(p, impl->hdr_size, struct spa_pod)); pw_logt_debug(mod_topic_connection, ">>>>>>>>> out: done");
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-native/v0/protocol-native.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-native/v0/protocol-native.c
Changed
@@ -400,7 +400,7 @@ static int remap_from_v2(uint32_t type, void *body, uint32_t size, struct pw_impl_client *client, struct spa_pod_builder *builder) { - int res; + int res = 0; switch (type) { case SPA_TYPE_Id: @@ -445,17 +445,17 @@ if (b->value.type == SPA_TYPE_Id) { uint32_t id; if ((res = spa_pod_get_id(&b->value, &id)) < 0) - return res; + goto done; + spa_pod_builder_id(builder, pw_protocol_native0_type_from_v2(client, id)); SPA_POD_PROP_ALTERNATIVE_FOREACH0(b, size, alt) if ((res = remap_from_v2(b->value.type, alt, b->value.size, client, builder)) < 0) - return res; + break; } else { spa_pod_builder_raw(builder, &b->value, size - sizeof(struct spa_pod)); } - +done: spa_pod_builder_pop(builder, &f); - break; } case SPA_TYPE_Object: @@ -493,7 +493,7 @@ SPA_POD_BODY(p), p->size, client, builder)) < 0) - return res; + break; } spa_pod_builder_pop(builder, &f); break; @@ -506,14 +506,14 @@ spa_pod_builder_push_struct(builder, &f); SPA_POD_FOREACH(b, size, p) if ((res = remap_from_v2(p->type, SPA_POD_BODY(p), p->size, client, builder)) < 0) - return res; + break; spa_pod_builder_pop(builder, &f); break; } default: break; } - return 0; + return res; } static int remap_to_v2(struct pw_impl_client *client, const struct spa_type_info *info,
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/module.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/module.c
Changed
@@ -163,41 +163,48 @@ free(s); } -int module_args_to_audioinfo(struct impl *impl, struct pw_properties *props, struct spa_audio_info_raw *info) +int module_args_to_audioinfo_keys(struct impl *impl, struct pw_properties *props, + const char *key_format, const char *key_rate, + const char *key_channels, const char *key_channel_map, + struct spa_audio_info_raw *info) { const char *str; uint32_t i; - /* We don't use any incoming format setting and use our native format */ - spa_zero(*info); - info->flags = SPA_AUDIO_FLAG_UNPOSITIONED; - info->format = SPA_AUDIO_FORMAT_F32P; - - if ((str = pw_properties_get(props, "channels")) != NULL) { + if (key_format && (str = pw_properties_get(props, key_format)) != NULL) { + info->format = format_paname2id(str, strlen(str)); + if (info->format == SPA_AUDIO_FORMAT_UNKNOWN) { + pw_log_error("invalid %s '%s'", key_format, str); + return -EINVAL; + } + pw_properties_set(props, key_format, NULL); + } + if (key_channels && (str = pw_properties_get(props, key_channels)) != NULL) { info->channels = pw_properties_parse_int(str); if (info->channels == 0 || info->channels > SPA_AUDIO_MAX_CHANNELS) { - pw_log_error("invalid channels '%s'", str); + pw_log_error("invalid %s '%s'", key_channels, str); return -EINVAL; } - pw_properties_set(props, "channels", NULL); + pw_properties_set(props, key_channels, NULL); } - if ((str = pw_properties_get(props, "channel_map")) != NULL) { + if (key_channel_map && (str = pw_properties_get(props, key_channel_map)) != NULL) { struct channel_map map; channel_map_parse(str, &map); if (map.channels == 0 || map.channels > SPA_AUDIO_MAX_CHANNELS) { - pw_log_error("invalid channel_map '%s'", str); + pw_log_error("invalid %s '%s'", key_channel_map, str); return -EINVAL; } if (info->channels == 0) info->channels = map.channels; if (info->channels != map.channels) { - pw_log_error("Mismatched channel map"); + pw_log_error("Mismatched %s and %s (%d vs %d)", + key_channels, key_channel_map, + info->channels, map.channels); return -EINVAL; } channel_map_to_positions(&map, info->position); - info->flags &= ~SPA_AUDIO_FLAG_UNPOSITIONED; - pw_properties_set(props, "channel_map", NULL); + pw_properties_set(props, key_channel_map, NULL); } else { if (info->channels == 0) info->channels = impl->defs.sample_spec.channels; @@ -214,19 +221,25 @@ for (i = 0; i < info->channels; i++) info->positioni = SPA_AUDIO_CHANNEL_UNKNOWN; } - if (info->position0 != SPA_AUDIO_CHANNEL_UNKNOWN) - info->flags &= ~SPA_AUDIO_FLAG_UNPOSITIONED; + if (info->position0 == SPA_AUDIO_CHANNEL_UNKNOWN) + info->flags |= SPA_AUDIO_FLAG_UNPOSITIONED; } - - if ((str = pw_properties_get(props, "rate")) != NULL) { + if (key_rate && (str = pw_properties_get(props, key_rate)) != NULL) { info->rate = pw_properties_parse_int(str); - pw_properties_set(props, "rate", NULL); - } else { - info->rate = 0; + pw_properties_set(props, key_rate, NULL); } return 0; } +int module_args_to_audioinfo(struct impl *impl, struct pw_properties *props, struct spa_audio_info_raw *info) +{ + /* We don't use any incoming format setting and use our native format */ + spa_zero(*info); + info->format = SPA_AUDIO_FORMAT_F32P; + return module_args_to_audioinfo_keys(impl, props, + NULL, "rate", "channels", "channel_map", info); +} + bool module_args_parse_bool(const char *v) { if (spa_streq(v, "1") || !strcasecmp(v, "y") || !strcasecmp(v, "t") || @@ -235,6 +248,28 @@ return false; } +void audioinfo_to_properties(struct spa_audio_info_raw *info, struct pw_properties *props) +{ + uint32_t i; + + if (info->format) + pw_properties_setf(props, SPA_KEY_AUDIO_FORMAT, "%s", + format_id2name(info->format)); + if (info->rate) + pw_properties_setf(props, SPA_KEY_AUDIO_RATE, "%u", info->rate); + if (info->channels) { + char *s, *p; + + pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels); + + p = s = alloca(info->channels * 8); + for (i = 0; i < info->channels; i++) + p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ", ", + channel_id2name(info->positioni)); + pw_properties_setf(props, SPA_KEY_AUDIO_POSITION, " %s ", s); + } +} + static const struct module_info *find_module_info(const char *name) { extern const struct module_info __start_pw_mod_pulse_modules;
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/module.h -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/module.h
Changed
@@ -70,5 +70,12 @@ void module_args_add_props(struct pw_properties *props, const char *str); int module_args_to_audioinfo(struct impl *impl, struct pw_properties *props, struct spa_audio_info_raw *info); bool module_args_parse_bool(const char *str); +int module_args_to_audioinfo_keys(struct impl *impl, struct pw_properties *props, + const char *key_format, const char *key_rate, + const char *key_channels, const char *key_channel_map, + struct spa_audio_info_raw *info); + +void audioinfo_to_properties(struct spa_audio_info_raw *info, struct pw_properties *props); + #endif
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-combine-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-combine-sink.c
Changed
@@ -31,7 +31,8 @@ "rate=<sample rate> " "channels=<number of channels> " "channel_map=<channel map> " - "remix=<remix channels> " }, + "remix=<remix channels> " + "latency_compensate=<bool> " }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; @@ -48,16 +49,16 @@ struct pw_impl_module *mod; struct spa_hook mod_listener; - char *sink_name; char **sink_names; + struct pw_properties *props; struct pw_properties *combine_props; + struct pw_properties *stream_props; struct spa_source *sinks_timeout; struct spa_audio_info_raw info; unsigned int sinks_pending; - unsigned int remix:1; unsigned int load_emitted:1; unsigned int start_error:1; }; @@ -148,31 +149,20 @@ if (data->core == NULL) return -errno; + pw_properties_setf(data->combine_props, "pulse.module.id", "%u", + module->index); + pw_properties_setf(data->stream_props, "pulse.module.id", "%u", + module->index); + if ((f = open_memstream(&args, &size)) == NULL) return -errno; fprintf(f, "{"); - fprintf(f, " node.name = %s", data->sink_name); - fprintf(f, " node.description = %s", data->sink_name); - 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, " "); - } - } + pw_properties_serialize_dict(f, &data->props->dict, 0); fprintf(f, " combine.props = {"); - fprintf(f, " pulse.module.id = %u", module->index); pw_properties_serialize_dict(f, &data->combine_props->dict, 0); fprintf(f, " } stream.props = {"); - if (!data->remix) - fprintf(f, " "PW_KEY_STREAM_DONT_REMIX" = true"); - fprintf(f, " pulse.module.id = %u", module->index); + pw_properties_serialize_dict(f, &data->stream_props->dict, 0); fprintf(f, " } stream.rules = "); if (data->sink_names == NULL) { fprintf(f, " { matches = { media.class = \"Audio/Sink\" } "); @@ -240,8 +230,9 @@ pw_core_disconnect(d->core); } pw_free_strv(d->sink_names); - free(d->sink_name); + pw_properties_free(d->stream_props); pw_properties_free(d->combine_props); + pw_properties_free(d->props); return 0; } @@ -249,37 +240,52 @@ { struct module_combine_sink_data * const d = module->user_data; struct pw_properties * const props = module->props; - struct pw_properties *combine_props = NULL; + struct pw_properties *combine_props = NULL, *global_props = NULL, *stream_props = NULL; const char *str; - char *sink_name = NULL, **sink_names = NULL; + char **sink_names = NULL; struct spa_audio_info_raw info = { 0 }; int res; int num_sinks = 0; PW_LOG_TOPIC_INIT(mod_topic); + global_props = pw_properties_new(NULL, NULL); combine_props = pw_properties_new(NULL, NULL); + stream_props = pw_properties_new(NULL, NULL); + if (global_props == NULL || combine_props == NULL || stream_props == NULL) { + res = -ENOMEM; + goto out; + } if ((str = pw_properties_get(props, "sink_name")) != NULL) { - sink_name = strdup(str); + pw_properties_set(global_props, PW_KEY_NODE_NAME, str); + pw_properties_set(global_props, PW_KEY_NODE_DESCRIPTION, str); pw_properties_set(props, "sink_name", NULL); } else { - sink_name = strdup("combined"); + str = "combined"; + pw_properties_set(global_props, PW_KEY_NODE_NAME, str); + pw_properties_set(global_props, PW_KEY_NODE_DESCRIPTION, str); } - if ((str = pw_properties_get(module->props, "sink_properties")) != NULL) + if ((str = pw_properties_get(props, "sink_properties")) != NULL) module_args_add_props(combine_props, str); if ((str = pw_properties_get(props, "slaves")) != NULL) { sink_names = pw_split_strv(str, ",", MAX_SINKS, &num_sinks); pw_properties_set(props, "slaves", NULL); } - d->remix = true; if ((str = pw_properties_get(props, "remix")) != NULL) { - d->remix = pw_properties_parse_bool(str); + pw_properties_set(stream_props, PW_KEY_STREAM_DONT_REMIX, + module_args_parse_bool(str) ? "false" : "true"); pw_properties_set(props, "remix", NULL); } + if ((str = pw_properties_get(props, "latency_compensate")) != NULL) { + pw_properties_set(global_props, "combine.latency-compensate", + module_args_parse_bool(str) ? "true" : "false"); + pw_properties_set(props, "latency_compensate", NULL); + } + if ((str = pw_properties_get(props, "adjust_time")) != NULL) { pw_log_info("The `adjust_time` modarg is ignored"); pw_properties_set(props, "adjust_time", NULL); @@ -290,23 +296,27 @@ pw_properties_set(props, "resample_method", NULL); } - if (module_args_to_audioinfo(module->impl, props, &info) < 0) { + if (module_args_to_audioinfo_keys(module->impl, props, + NULL, "rate", "channels", "channel_map", &info) < 0) { res = -EINVAL; goto out; } + audioinfo_to_properties(&info, global_props); d->module = module; d->info = info; - d->sink_name = sink_name; d->sink_names = sink_names; d->sinks_pending = (sink_names == NULL) ? 0 : num_sinks; + d->stream_props = stream_props; d->combine_props = combine_props; + d->props = global_props; return 0; out: - free(sink_name); pw_free_strv(sink_names); + pw_properties_free(stream_props); pw_properties_free(combine_props); + pw_properties_free(global_props); return res; }
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c
Changed
@@ -22,6 +22,7 @@ struct pw_impl_module *mod; struct spa_hook mod_listener; + struct pw_properties *global_props; struct pw_properties *props; struct pw_properties *capture_props; struct pw_properties *source_props; @@ -46,38 +47,19 @@ static int module_echo_cancel_load(struct module *module) { - struct pw_properties * const props = module->props; struct module_echo_cancel_data *data = module->user_data; - const char *method; FILE *f; char *args; size_t size; - uint32_t i; if ((f = open_memstream(&args, &size)) == NULL) return -errno; fprintf(f, "{"); - if ((method = pw_properties_get(props, "aec_method")) == NULL) - method = "webrtc"; - - fprintf(f, " library.name = \"aec/libspa-aec-%s\"", method); - + pw_properties_serialize_dict(f, &data->global_props->dict, 0); fprintf(f, " aec.args = {"); pw_properties_serialize_dict(f, &data->props->dict, 0); fprintf(f, " }"); - 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, " capture.props = {"); pw_properties_serialize_dict(f, &data->capture_props->dict, 0); fprintf(f, " } source.props = {"); @@ -114,6 +96,7 @@ d->mod = NULL; } + pw_properties_free(d->global_props); pw_properties_free(d->props); pw_properties_free(d->capture_props); pw_properties_free(d->source_props); @@ -231,22 +214,28 @@ struct pw_properties * const props = module->props; struct pw_properties *aec_props = NULL, *sink_props = NULL, *source_props = NULL; struct pw_properties *playback_props = NULL, *capture_props = NULL; + struct pw_properties *global_props = NULL; const char *str, *method; struct spa_audio_info_raw info = { 0 }; int res; PW_LOG_TOPIC_INIT(mod_topic); + global_props = pw_properties_new(NULL, NULL); aec_props = pw_properties_new(NULL, NULL); capture_props = pw_properties_new(NULL, NULL); source_props = pw_properties_new(NULL, NULL); sink_props = pw_properties_new(NULL, NULL); playback_props = pw_properties_new(NULL, NULL); - if (!aec_props || !source_props || !sink_props || !capture_props || !playback_props) { + if (!global_props || !aec_props || !source_props || !sink_props || !capture_props || !playback_props) { res = -EINVAL; goto out; } + if ((str = pw_properties_get(props, "aec_method")) == NULL) + str = "webrtc"; + pw_properties_setf(global_props, "library.name", "aec/libspa-aec-%s", str); + if ((str = pw_properties_get(props, "source_name")) != NULL) { pw_properties_set(source_props, PW_KEY_NODE_NAME, str); pw_properties_set(props, "source_name", NULL); @@ -282,6 +271,7 @@ res = -EINVAL; goto out; } + audioinfo_to_properties(&info, global_props); if ((str = pw_properties_get(props, "source_properties")) != NULL) { module_args_add_props(source_props, str); @@ -314,6 +304,7 @@ } d->module = module; + d->global_props = global_props; d->props = aec_props; d->capture_props = capture_props; d->source_props = source_props; @@ -323,6 +314,7 @@ return 0; out: + pw_properties_free(global_props); pw_properties_free(aec_props); pw_properties_free(playback_props); pw_properties_free(sink_props);
View file
pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-jackdbus-detect.c
Added
@@ -0,0 +1,206 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans <wim.taymans@gmail.com> */ +/* SPDX-License-Identifier: MIT */ + +#include <spa/utils/hook.h> +#include <pipewire/pipewire.h> + +#include "../defs.h" +#include "../module.h" + +#define NAME "jackdbus-detect" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + + +struct module_jackdbus_detect_data { + struct module *module; + + struct spa_hook mod_listener; + struct pw_impl_module *mod; + + struct pw_properties *props; + struct pw_properties *sink_props; + struct pw_properties *source_props; +}; + +static void module_destroy(void *data) +{ + struct module_jackdbus_detect_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_jackdbus_detect_load(struct module *module) +{ + struct module_jackdbus_detect_data *data = module->user_data; + FILE *f; + char *args; + size_t size; + + pw_properties_setf(data->sink_props, "pulse.module.id", + "%u", module->index); + pw_properties_setf(data->source_props, "pulse.module.id", + "%u", module->index); + + if ((f = open_memstream(&args, &size)) == NULL) + return -errno; + + fprintf(f, "{"); + pw_properties_serialize_dict(f, &data->props->dict, 0); + fprintf(f, " source.props = {"); + pw_properties_serialize_dict(f, &data->source_props->dict, 0); + fprintf(f, " } sink.props = {"); + pw_properties_serialize_dict(f, &data->sink_props->dict, 0); + fprintf(f, " } }"); + fclose(f); + + data->mod = pw_context_load_module(module->impl->context, + "libpipewire-module-jackdbus-detect", + 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_jackdbus_detect_unload(struct module *module) +{ + struct module_jackdbus_detect_data *d = module->user_data; + + if (d->mod) { + spa_hook_remove(&d->mod_listener); + pw_impl_module_destroy(d->mod); + d->mod = NULL; + } + + return 0; +} + +static const struct spa_dict_item module_jackdbus_detect_info = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.con>" }, + { PW_KEY_MODULE_DESCRIPTION, "Creates a JACK client when jackdbus is started" }, + { PW_KEY_MODULE_USAGE, + "channels=<number of channels> " + "sink_name=<name for the sink> " + "sink_properties=<properties for the sink> " + "sink_client_name=<jack client name> " + "sink_channels=<number of channels> " + "sink_channel_map=<channel map> " + "source_name=<name for the source> " + "source_properties=<properties for the source> " + "source_client_name=<jack client name> " + "source_channels=<number of channels> " + "source_channel_map=<channel map> " + "connect=<connect ports?>" }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + +static int module_jackdbus_detect_prepare(struct module * const module) +{ + struct module_jackdbus_detect_data * const data = module->user_data; + struct pw_properties * const props = module->props; + struct pw_properties *jack_props = NULL, *sink_props = NULL, *source_props = NULL; + struct spa_audio_info_raw info; + const char *str; + int res; + + PW_LOG_TOPIC_INIT(mod_topic); + + jack_props = pw_properties_new(NULL, NULL); + sink_props = pw_properties_new(NULL, NULL); + source_props = pw_properties_new(NULL, NULL); + if (jack_props == NULL || sink_props == NULL || source_props == NULL) { + res = -ENOMEM; + goto out; + } + + if ((str = pw_properties_get(props, "channels")) != NULL) { + pw_properties_set(jack_props, PW_KEY_AUDIO_CHANNELS, str); + pw_properties_set(props, "channels", NULL); + } + if ((str = pw_properties_get(props, "connect")) != NULL) { + pw_properties_set(jack_props, "jack.connect", + module_args_parse_bool(str) ? "true" : "false"); + } + + if ((str = pw_properties_get(props, "sink_name")) != NULL) { + pw_properties_set(sink_props, PW_KEY_NODE_NAME, str); + pw_properties_set(props, "sink_name", NULL); + } else { + pw_properties_set(sink_props, PW_KEY_NODE_NAME, "jack_out"); + } + if ((str = pw_properties_get(props, "sink_client_name")) != NULL) { + pw_properties_set(jack_props, "jack.client-name", str); + pw_properties_set(props, "sink_client_name", NULL); + } + + spa_zero(info); + if ((res = module_args_to_audioinfo_keys(module->impl, props, NULL, NULL, + "sink_channels", "sink_channel_map", &info)) < 0) { + return res; + } else { + audioinfo_to_properties(&info, sink_props); + } + if ((str = pw_properties_get(props, "sink_properties")) != NULL) { + module_args_add_props(sink_props, str); + pw_properties_set(props, "sink_properties", NULL); + } + + if ((str = pw_properties_get(props, "source_name")) != NULL) { + pw_properties_set(source_props, PW_KEY_NODE_NAME, str); + pw_properties_set(props, "source_name", NULL); + } else { + pw_properties_set(source_props, PW_KEY_NODE_NAME, "jack_in"); + } + if ((str = pw_properties_get(props, "source_client_name")) != NULL) { + pw_properties_set(jack_props, "jack.client-name", str); + pw_properties_set(props, "source_client_name", NULL); + } + spa_zero(info); + if ((res = module_args_to_audioinfo_keys(module->impl, props, NULL, NULL, + "source_channels", "source_channel_map", &info)) < 0) { + return res; + } else { + audioinfo_to_properties(&info, source_props); + } + if ((str = pw_properties_get(props, "source_properties")) != NULL) { + module_args_add_props(source_props, str); + pw_properties_set(props, "source_properties", NULL); + } + + data->module = module; + data->props = jack_props; + data->sink_props = sink_props; + data->source_props = source_props; + + return 0; +out: + pw_properties_free(jack_props); + pw_properties_free(sink_props); + pw_properties_free(source_props); + return res; +} + +DEFINE_MODULE_INFO(module_jackdbus_detect) = { + .name = "module-jackdbus-detect", + .load_once = false, + .prepare = module_jackdbus_detect_prepare, + .load = module_jackdbus_detect_load, + .unload = module_jackdbus_detect_unload, + .properties = &SPA_DICT_INIT_ARRAY(module_jackdbus_detect_info), + .data_size = sizeof(struct module_jackdbus_detect_data), +};
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-ladspa-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-ladspa-sink.c
Changed
@@ -144,19 +144,6 @@ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; -static void position_to_props(struct spa_audio_info_raw *info, struct pw_properties *props) -{ - char *s, *p; - uint32_t i; - - pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels); - p = s = alloca(info->channels * 8); - for (i = 0; i < info->channels; i++) - p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",", - channel_id2name(info->positioni)); - pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s); -} - static int module_ladspa_sink_prepare(struct module * const module) { struct module_ladspa_sink_data * const d = module->user_data; @@ -203,14 +190,15 @@ pw_properties_set(props, "master", NULL); } - if (module_args_to_audioinfo(module->impl, props, &capture_info) < 0) { + if (module_args_to_audioinfo_keys(module->impl, props, + NULL, NULL, "channels", "channel_map", &capture_info) < 0) { res = -EINVAL; goto out; } playback_info = capture_info; - position_to_props(&capture_info, capture_props); - position_to_props(&playback_info, playback_props); + audioinfo_to_properties(&capture_info, capture_props); + audioinfo_to_properties(&playback_info, playback_props); if (pw_properties_get(playback_props, PW_KEY_NODE_PASSIVE) == NULL) pw_properties_set(playback_props, PW_KEY_NODE_PASSIVE, "true");
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-ladspa-source.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-ladspa-source.c
Changed
@@ -144,19 +144,6 @@ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; -static void position_to_props(struct spa_audio_info_raw *info, struct pw_properties *props) -{ - char *s, *p; - uint32_t i; - - pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels); - p = s = alloca(info->channels * 8); - for (i = 0; i < info->channels; i++) - p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",", - channel_id2name(info->positioni)); - pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s); -} - static int module_ladspa_source_prepare(struct module * const module) { struct module_ladspa_source_data * const d = module->user_data; @@ -211,14 +198,15 @@ pw_properties_set(props, "master", NULL); } - if (module_args_to_audioinfo(module->impl, props, &playback_info) < 0) { + if (module_args_to_audioinfo_keys(module->impl, props, + NULL, NULL, "channels", "channel_map", &playback_info) < 0) { res = -EINVAL; goto out; } capture_info = playback_info; - position_to_props(&capture_info, capture_props); - position_to_props(&playback_info, playback_props); + audioinfo_to_properties(&capture_info, capture_props); + audioinfo_to_properties(&playback_info, playback_props); if (pw_properties_get(capture_props, PW_KEY_NODE_PASSIVE) == NULL) pw_properties_set(capture_props, PW_KEY_NODE_PASSIVE, "true");
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-loopback.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-loopback.c
Changed
@@ -22,11 +22,9 @@ struct pw_impl_module *mod; struct spa_hook mod_listener; + struct pw_properties *global_props; struct pw_properties *capture_props; struct pw_properties *playback_props; - - struct spa_audio_info_raw info; - uint32_t latency_msec; }; static void module_destroy(void *data) @@ -47,8 +45,7 @@ struct module_loopback_data *data = module->user_data; FILE *f; char *args; - size_t size, i; - char val256; + size_t size; 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); @@ -59,20 +56,7 @@ return -errno; fprintf(f, "{"); - 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, " "); - } - } - if (data->latency_msec != 0) - fprintf(f, " target.delay.sec = %s", - spa_json_format_float(val, sizeof(val), - data->latency_msec / 1000.0f)); + pw_properties_serialize_dict(f, &data->global_props->dict, 0); fprintf(f, " capture.props = {"); pw_properties_serialize_dict(f, &data->capture_props->dict, 0); fprintf(f, " } playback.props = {"); @@ -107,6 +91,7 @@ pw_properties_free(d->capture_props); pw_properties_free(d->playback_props); + pw_properties_free(d->global_props); return 0; } @@ -132,16 +117,17 @@ { struct module_loopback_data * const d = module->user_data; struct pw_properties * const props = module->props; - struct pw_properties *playback_props = NULL, *capture_props = NULL; + struct pw_properties *global_props = NULL, *playback_props = NULL, *capture_props = NULL; const char *str; struct spa_audio_info_raw info = { 0 }; int res; PW_LOG_TOPIC_INIT(mod_topic); + global_props = pw_properties_new(NULL, NULL); capture_props = pw_properties_new(NULL, NULL); playback_props = pw_properties_new(NULL, NULL); - if (!capture_props || !playback_props) { + if (!global_props || !capture_props || !playback_props) { res = -EINVAL; goto out; } @@ -167,10 +153,12 @@ pw_properties_set(props, "sink", NULL); } - if (module_args_to_audioinfo(module->impl, props, &info) < 0) { + if (module_args_to_audioinfo_keys(module->impl, props, + NULL, NULL, "channels", "channel_map", &info) < 0) { res = -EINVAL; goto out; } + audioinfo_to_properties(&info, global_props); if ((str = pw_properties_get(props, "source_dont_move")) != NULL) { pw_properties_set(capture_props, PW_KEY_NODE_DONT_RECONNECT, str); @@ -189,8 +177,13 @@ pw_properties_set(props, "remix", NULL); } - if ((str = pw_properties_get(props, "latency_msec")) != NULL) - d->latency_msec = atoi(str); + if ((str = pw_properties_get(props, "latency_msec")) != NULL) { + uint32_t latency_msec = atoi(str); + char val256; + pw_properties_setf(global_props, "target.delay.sec", + "%s", spa_json_format_float(val, sizeof(val), + latency_msec / 1000.0f)); + } if ((str = pw_properties_get(props, "sink_input_properties")) != NULL) { module_args_add_props(playback_props, str); @@ -203,12 +196,13 @@ } d->module = module; + d->global_props = global_props; d->capture_props = capture_props; d->playback_props = playback_props; - d->info = info; return 0; out: + pw_properties_free(global_props); pw_properties_free(playback_props); pw_properties_free(capture_props);
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-null-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-null-sink.c
Changed
@@ -143,7 +143,6 @@ struct pw_properties * const props = module->props; const char *str; struct spa_audio_info_raw info = { 0 }; - uint32_t i; PW_LOG_TOPIC_INIT(mod_topic); @@ -160,35 +159,11 @@ pw_properties_set(props, "sink_properties", NULL); } - if (module_args_to_audioinfo(module->impl, props, &info) < 0) + if (module_args_to_audioinfo_keys(module->impl, props, + "format", "rate", "channels", "channel_map", &info) < 0) return -EINVAL; - info.format = module->impl->defs.sample_spec.format; - if ((str = pw_properties_get(props, "format")) != NULL) { - info.format = format_paname2id(str, strlen(str)); - if (info.format == SPA_AUDIO_FORMAT_UNKNOWN) { - pw_log_error("invalid format '%s'", str); - return -EINVAL; - } - pw_properties_set(props, "format", NULL); - } - - if (info.format) - pw_properties_setf(props, SPA_KEY_AUDIO_FORMAT, "%s", - format_id2name(info.format)); - if (info.rate) - pw_properties_setf(props, SPA_KEY_AUDIO_RATE, "%u", info.rate); - if (info.channels) { - char *s, *p; - - pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info.channels); - - p = s = alloca(info.channels * 8); - for (i = 0; i < info.channels; i++) - p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",", - channel_id2name(info.positioni)); - pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s); - } + audioinfo_to_properties(&info, props); if (pw_properties_get(props, PW_KEY_MEDIA_CLASS) == NULL) pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-sink.c
Changed
@@ -25,9 +25,8 @@ struct spa_hook mod_listener; struct pw_impl_module *mod; + struct pw_properties *global_props; struct pw_properties *capture_props; - struct spa_audio_info_raw info; - char *filename; }; static void module_destroy(void *data) @@ -49,7 +48,6 @@ FILE *f; char *args; size_t size; - uint32_t i; pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index); @@ -58,23 +56,7 @@ return -errno; fprintf(f, "{"); - fprintf(f, " \"tunnel.mode\" = \"sink\" "); - if (data->filename != NULL) - fprintf(f, " \"pipe.filename\": \"%s\"", data->filename); - 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, " ,"); - } - } + pw_properties_serialize_dict(f, &data->global_props->dict, 0); fprintf(f, " \"stream.props\": {"); pw_properties_serialize_dict(f, &data->capture_props->dict, 0); fprintf(f, " } }"); @@ -105,7 +87,7 @@ d->mod = NULL; } pw_properties_free(d->capture_props); - free(d->filename); + pw_properties_free(d->global_props); return 0; } @@ -126,35 +108,30 @@ { struct module_pipesink_data * const d = module->user_data; struct pw_properties * const props = module->props; - struct pw_properties *capture_props = NULL; + struct pw_properties *global_props = NULL, *capture_props = NULL; struct spa_audio_info_raw info = { 0 }; const char *str; - char *filename = NULL; int res = 0; PW_LOG_TOPIC_INIT(mod_topic); + global_props = pw_properties_new(NULL, NULL); capture_props = pw_properties_new(NULL, NULL); - if (!capture_props) { + if (!global_props || !capture_props) { res = -EINVAL; goto out; } - if (module_args_to_audioinfo(module->impl, props, &info) < 0) { + pw_properties_set(global_props, "tunnel.mode", "sink"); + + info.format = SPA_AUDIO_FORMAT_S16; + if (module_args_to_audioinfo_keys(module->impl, props, + "format", "rate", "channels", "channel_map", &info) < 0) { res = -EINVAL; goto out; } + audioinfo_to_properties(&info, global_props); - info.format = SPA_AUDIO_FORMAT_S16; - if ((str = pw_properties_get(props, "format")) != NULL) { - info.format = format_paname2id(str, strlen(str)); - if (info.format == SPA_AUDIO_FORMAT_UNKNOWN) { - pw_log_error("invalid format '%s'", str); - res = -EINVAL; - goto out; - } - pw_properties_set(props, "format", NULL); - } if ((str = pw_properties_get(props, "sink_name")) != NULL) { pw_properties_set(capture_props, PW_KEY_NODE_NAME, str); pw_properties_set(props, "sink_name", NULL); @@ -163,7 +140,7 @@ module_args_add_props(capture_props, str); if ((str = pw_properties_get(props, "file")) != NULL) { - filename = strdup(str); + pw_properties_set(global_props, "pipe.filename", str); pw_properties_set(props, "file", NULL); } if ((str = pw_properties_get(capture_props, PW_KEY_DEVICE_ICON_NAME)) == NULL) @@ -174,14 +151,13 @@ "fifo_output"); d->module = module; + d->global_props = global_props; d->capture_props = capture_props; - d->info = info; - d->filename = filename; return 0; out: + pw_properties_free(global_props); pw_properties_free(capture_props); - free(filename); return res; }
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-source.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-source.c
Changed
@@ -25,9 +25,8 @@ struct spa_hook mod_listener; struct pw_impl_module *mod; + struct pw_properties *global_props; struct pw_properties *playback_props; - struct spa_audio_info_raw info; - char *filename; }; static void module_destroy(void *data) @@ -49,7 +48,6 @@ FILE *f; char *args; size_t size; - uint32_t i; pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index); @@ -58,23 +56,7 @@ return -errno; fprintf(f, "{"); - fprintf(f, " \"tunnel.mode\" = \"source\" "); - if (data->filename != NULL) - fprintf(f, " \"pipe.filename\": \"%s\"", data->filename); - 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, " ,"); - } - } + pw_properties_serialize_dict(f, &data->global_props->dict, 0); fprintf(f, " \"stream.props\": {"); pw_properties_serialize_dict(f, &data->playback_props->dict, 0); fprintf(f, " } }"); @@ -105,7 +87,7 @@ d->mod = NULL; } pw_properties_free(d->playback_props); - free(d->filename); + pw_properties_free(d->global_props); return 0; } @@ -126,35 +108,30 @@ { struct module_pipesrc_data * const d = module->user_data; struct pw_properties * const props = module->props; - struct pw_properties *playback_props = NULL; + struct pw_properties *global_props = NULL, *playback_props = NULL; struct spa_audio_info_raw info = { 0 }; const char *str; - char *filename = NULL; int res = 0; PW_LOG_TOPIC_INIT(mod_topic); + global_props = pw_properties_new(NULL, NULL); playback_props = pw_properties_new(NULL, NULL); - if (!playback_props) { + if (!global_props || !playback_props) { res = -errno; goto out; } - if (module_args_to_audioinfo(module->impl, props, &info) < 0) { + pw_properties_set(global_props, "tunnel.mode", "source"); + + info.format = SPA_AUDIO_FORMAT_S16; + if (module_args_to_audioinfo_keys(module->impl, props, + "format", "rate", "channels", "channel_map", &info) < 0) { res = -EINVAL; goto out; } + audioinfo_to_properties(&info, global_props); - info.format = SPA_AUDIO_FORMAT_S16; - if ((str = pw_properties_get(props, "format")) != NULL) { - info.format = format_paname2id(str, strlen(str)); - if (info.format == SPA_AUDIO_FORMAT_UNKNOWN) { - pw_log_error("invalid format '%s'", str); - res = -EINVAL; - goto out; - } - pw_properties_set(props, "format", NULL); - } if ((str = pw_properties_get(props, "source_name")) != NULL) { pw_properties_set(playback_props, PW_KEY_NODE_NAME, str); pw_properties_set(props, "source_name", NULL); @@ -163,7 +140,7 @@ module_args_add_props(playback_props, str); if ((str = pw_properties_get(props, "file")) != NULL) { - filename = strdup(str); + pw_properties_set(global_props, "pipe.filename", str); pw_properties_set(props, "file", NULL); } if ((str = pw_properties_get(playback_props, PW_KEY_DEVICE_ICON_NAME)) == NULL) @@ -175,13 +152,12 @@ d->module = module; d->playback_props = playback_props; - d->info = info; - d->filename = filename; + d->global_props = global_props; return 0; out: + pw_properties_free(global_props); pw_properties_free(playback_props); - free(filename); return res; }
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-remap-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-remap-sink.c
Changed
@@ -109,19 +109,6 @@ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; -static void position_to_props(struct spa_audio_info_raw *info, struct pw_properties *props) -{ - char *s, *p; - uint32_t i; - - pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels); - p = s = alloca(info->channels * 8); - for (i = 0; i < info->channels; i++) - p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",", - channel_id2name(info->positioni)); - pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s); -} - static int module_remap_sink_prepare(struct module * const module) { struct module_remap_sink_data * const d = module->user_data; @@ -180,26 +167,19 @@ pw_properties_set(props, "master", NULL); } - if (module_args_to_audioinfo(module->impl, props, &capture_info) < 0) { + if (module_args_to_audioinfo_keys(module->impl, props, + NULL, NULL, "channels", "channel_map", &capture_info) < 0) { res = -EINVAL; goto out; } playback_info = capture_info; - - if ((str = pw_properties_get(props, "master_channel_map")) != NULL) { - struct channel_map map; - - channel_map_parse(str, &map); - if (map.channels == 0 || map.channels > SPA_AUDIO_MAX_CHANNELS) { - pw_log_error("invalid channel_map '%s'", str); - res = -EINVAL; - goto out; - } - channel_map_to_positions(&map, playback_info.position); - pw_properties_set(props, "master_channel_map", NULL); + if (module_args_to_audioinfo_keys(module->impl, props, + NULL, NULL, NULL, "master_channel_map", &playback_info) < 0) { + res = -EINVAL; + goto out; } - position_to_props(&capture_info, capture_props); - position_to_props(&playback_info, playback_props); + audioinfo_to_properties(&capture_info, capture_props); + audioinfo_to_properties(&playback_info, playback_props); if ((str = pw_properties_get(props, "remix")) != NULL) { /* Note that the boolean is inverted */ @@ -219,7 +199,6 @@ out: pw_properties_free(playback_props); pw_properties_free(capture_props); - return res; }
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-remap-source.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-remap-source.c
Changed
@@ -109,19 +109,6 @@ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; -static void position_to_props(struct spa_audio_info_raw *info, struct pw_properties *props) -{ - char *s, *p; - uint32_t i; - - pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels); - p = s = alloca(info->channels * 8); - for (i = 0; i < info->channels; i++) - p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",", - channel_id2name(info->positioni)); - pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s); -} - static int module_remap_source_prepare(struct module * const module) { struct module_remap_source_data * const d = module->user_data; @@ -187,26 +174,19 @@ pw_properties_set(props, "master", NULL); } - if (module_args_to_audioinfo(module->impl, props, &playback_info) < 0) { + if (module_args_to_audioinfo_keys(module->impl, props, + NULL, NULL, "channels", "channel_map", &playback_info) < 0) { res = -EINVAL; goto out; } capture_info = playback_info; - - if ((str = pw_properties_get(props, "master_channel_map")) != NULL) { - struct channel_map map; - - channel_map_parse(str, &map); - if (map.channels == 0 || map.channels > SPA_AUDIO_MAX_CHANNELS) { - pw_log_error("invalid channel_map '%s'", str); - res = -EINVAL; - goto out; - } - channel_map_to_positions(&map, capture_info.position); - pw_properties_set(props, "master_channel_map", NULL); + if (module_args_to_audioinfo_keys(module->impl, props, + NULL, NULL, NULL, "master_channel_map", &capture_info) < 0) { + res = -EINVAL; + goto out; } - position_to_props(&playback_info, playback_props); - position_to_props(&capture_info, capture_props); + audioinfo_to_properties(&playback_info, playback_props); + audioinfo_to_properties(&capture_info, capture_props); if ((str = pw_properties_get(props, "remix")) != NULL) { /* Note that the boolean is inverted */ @@ -226,7 +206,6 @@ out: pw_properties_free(playback_props); pw_properties_free(capture_props); - return res; }
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-rtp-send.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-rtp-send.c
Changed
@@ -25,8 +25,6 @@ struct pw_properties *stream_props; struct pw_properties *global_props; struct pw_properties *sap_props; - - struct spa_audio_info_raw info; }; static void module_destroy(void *data) @@ -61,7 +59,6 @@ FILE *f; char *args; size_t size; - uint32_t i; pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); @@ -71,20 +68,6 @@ 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, " } }"); @@ -198,19 +181,13 @@ pw_properties_set(stream_props, PW_KEY_TARGET_OBJECT, str); } } - if (module_args_to_audioinfo(module->impl, props, &info) < 0) { + + if (module_args_to_audioinfo_keys(module->impl, props, + "format", "rate", "channels", "channel_map", &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; - } - } + audioinfo_to_properties(&info, global_props); pw_properties_set(global_props, "sess.media", "audio"); if ((str = pw_properties_get(props, "enable_opus")) != NULL) { @@ -245,7 +222,6 @@ d->stream_props = stream_props; d->global_props = global_props; d->sap_props = sap_props; - d->info = info; return 0; out:
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-simple-protocol-tcp.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-simple-protocol-tcp.c
Changed
@@ -44,25 +44,12 @@ struct impl *impl = module->impl; char *args; size_t size; - uint32_t i; FILE *f; if ((f = open_memstream(&args, &size)) == NULL) return -errno; fprintf(f, "{"); - 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, " ,"); - } - } pw_properties_serialize_dict(f, &data->module_props->dict, 0); fprintf(f, "}"); fclose(f); @@ -127,15 +114,13 @@ goto out; } - if ((str = pw_properties_get(props, "format")) != NULL) { - pw_properties_set(module_props, "audio.format", - format_id2name(format_paname2id(str, strlen(str)))); - pw_properties_set(props, "format", NULL); - } - if (module_args_to_audioinfo(module->impl, props, &info) < 0) { + if (module_args_to_audioinfo_keys(module->impl, props, + "format", "rate", "channels", "channel_map", &info) < 0) { res = -EINVAL; goto out; } + audioinfo_to_properties(&info, module_props); + if ((str = pw_properties_get(props, "playback")) != NULL) { pw_properties_set(module_props, "playback", str); pw_properties_set(props, "playback", NULL);
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-tunnel-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-tunnel-sink.c
Changed
@@ -23,8 +23,6 @@ struct pw_impl_module *mod; struct spa_hook mod_listener; - uint32_t latency_msec; - struct pw_properties *stream_props; }; @@ -47,9 +45,6 @@ FILE *f; char *args; size_t size; - const char *server; - - server = pw_properties_get(module->props, "server"); pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); @@ -59,10 +54,6 @@ fprintf(f, "{"); pw_properties_serialize_dict(f, &module->props->dict, 0); - fprintf(f, " pulse.server.address = \"%s\" ", server); - fprintf(f, " tunnel.mode = sink "); - if (data->latency_msec > 0) - fprintf(f, " pulse.latency = %u ", data->latency_msec); fprintf(f, " stream.props = {"); pw_properties_serialize_dict(f, &data->stream_props->dict, 0); fprintf(f, " } }"); @@ -115,19 +106,6 @@ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; -static void audio_info_to_props(struct spa_audio_info_raw *info, struct pw_properties *props) -{ - char *s, *p; - uint32_t i; - - pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels); - p = s = alloca(info->channels * 8); - for (i = 0; i < info->channels; i++) - p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",", - channel_id2name(info->positioni)); - pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s); -} - static int module_tunnel_sink_prepare(struct module * const module) { struct module_tunnel_sink_data * const d = module->user_data; @@ -145,6 +123,8 @@ goto out; } + pw_properties_set(props, "tunnel.mode", "sink"); + remote_sink_name = pw_properties_get(props, "sink"); if (remote_sink_name) pw_properties_set(props, PW_KEY_TARGET_OBJECT, remote_sink_name); @@ -153,11 +133,15 @@ pw_log_error("no server given"); res = -EINVAL; goto out; + } else { + pw_properties_set(props, "pulse.server.address", server); } pw_properties_setf(stream_props, PW_KEY_NODE_DESCRIPTION, - _("Tunnel to %s/%s"), server, + _("Tunnel to %s%s%s"), server, + remote_sink_name ? "/" : "", remote_sink_name ? remote_sink_name : ""); + pw_properties_set(stream_props, PW_KEY_MEDIA_CLASS, "Audio/Sink"); if ((str = pw_properties_get(props, "sink_name")) != NULL) { @@ -167,36 +151,30 @@ pw_properties_setf(stream_props, PW_KEY_NODE_NAME, "tunnel-sink.%s", server); } + pw_properties_set(props, "server", NULL); if ((str = pw_properties_get(props, "sink_properties")) != NULL) { module_args_add_props(stream_props, str); pw_properties_set(props, "sink_properties", NULL); } - if (module_args_to_audioinfo(module->impl, props, &info) < 0) { + if (module_args_to_audioinfo_keys(module->impl, props, + "format", "rate", "channels", "channel_map", &info) < 0) { res = -EINVAL; goto out; } + audioinfo_to_properties(&info, stream_props); - audio_info_to_props(&info, stream_props); - if ((str = pw_properties_get(props, "format")) != NULL) { - uint32_t id = format_paname2id(str, strlen(str)); - if (id == SPA_AUDIO_FORMAT_UNKNOWN) { - res = -EINVAL; - goto out; - } - - pw_properties_set(stream_props, PW_KEY_AUDIO_FORMAT, format_id2name(id)); + if ((str = pw_properties_get(props, "latency_msec")) != NULL) { + pw_properties_set(props, "pulse.latency", str); + pw_properties_set(props, "latency_msec", NULL); } d->module = module; d->stream_props = stream_props; - pw_properties_fetch_uint32(props, "latency_msec", &d->latency_msec); - return 0; out: pw_properties_free(stream_props); - return res; }
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/modules/module-tunnel-source.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/modules/module-tunnel-source.c
Changed
@@ -23,8 +23,6 @@ struct pw_impl_module *mod; struct spa_hook mod_listener; - uint32_t latency_msec; - struct pw_properties *stream_props; }; @@ -47,22 +45,15 @@ FILE *f; char *args; size_t size; - const char *server; pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); - server = pw_properties_get(module->props, "server"); - if ((f = open_memstream(&args, &size)) == NULL) return -errno; fprintf(f, "{"); pw_properties_serialize_dict(f, &module->props->dict, 0); - fprintf(f, " pulse.server.address = \"%s\" ", server); - fprintf(f, " tunnel.mode = source "); - if (data->latency_msec > 0) - fprintf(f, " pulse.latency = %u ", data->latency_msec); fprintf(f, " stream.props = {"); pw_properties_serialize_dict(f, &data->stream_props->dict, 0); fprintf(f, " } }"); @@ -115,19 +106,6 @@ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; -static void audio_info_to_props(struct spa_audio_info_raw *info, struct pw_properties *props) -{ - char *s, *p; - uint32_t i; - - pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels); - p = s = alloca(info->channels * 8); - for (i = 0; i < info->channels; i++) - p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",", - channel_id2name(info->positioni)); - pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s); -} - static int module_tunnel_source_prepare(struct module * const module) { struct module_tunnel_source_data * const d = module->user_data; @@ -145,6 +123,8 @@ goto out; } + pw_properties_set(props, "tunnel.mode", "source"); + remote_source_name = pw_properties_get(props, "source"); if (remote_source_name) pw_properties_set(props, PW_KEY_TARGET_OBJECT, remote_source_name); @@ -153,10 +133,13 @@ pw_log_error("no server given"); res = -EINVAL; goto out; + } else { + pw_properties_set(props, "pulse.server.address", server); } pw_properties_setf(stream_props, PW_KEY_NODE_DESCRIPTION, - _("Tunnel to %s/%s"), server, + _("Tunnel to %s%s%s"), server, + remote_source_name ? "/" : "", remote_source_name ? remote_source_name : ""); pw_properties_set(stream_props, PW_KEY_MEDIA_CLASS, "Audio/Source"); @@ -171,22 +154,24 @@ module_args_add_props(stream_props, str); pw_properties_set(props, "source_properties", NULL); } - if (module_args_to_audioinfo(module->impl, props, &info) < 0) { + if (module_args_to_audioinfo_keys(module->impl, props, + "format", "rate", "channels", "channel_map", &info) < 0) { res = -EINVAL; goto out; } + audioinfo_to_properties(&info, stream_props); - audio_info_to_props(&info, stream_props); + if ((str = pw_properties_get(props, "latency_msec")) != NULL) { + pw_properties_set(props, "pulse.latency", str); + pw_properties_set(props, "latency_msec", NULL); + } d->module = module; d->stream_props = stream_props; - pw_properties_fetch_uint32(props, "latency_msec", &d->latency_msec); - return 0; out: pw_properties_free(stream_props); - return res; }
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/operation.h -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/operation.h
Changed
@@ -27,4 +27,11 @@ void operation_free(struct operation *o); void operation_complete(struct operation *o); +static inline void operation_free_by_tag(struct client *client, uint32_t tag) +{ + struct operation *o = operation_find(client, tag); + if (o) + operation_free(o); +} + #endif /* PULSER_SERVER_OPERATION_H */
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/pending-sample.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/pending-sample.c
Changed
@@ -7,24 +7,132 @@ #include <pipewire/work-queue.h> #include "client.h" +#include "collect.h" +#include "commands.h" #include "internal.h" #include "log.h" +#include "message.h" #include "operation.h" #include "pending-sample.h" +#include "reply.h" #include "sample-play.h" +static void do_pending_sample_finish(void *obj, void *data, int res, uint32_t id) +{ + struct pending_sample *ps = obj; + struct client *client = ps->client; + + pending_sample_free(ps); + client_unref(client); +} + +static void schedule_maybe_finish(struct pending_sample *ps) +{ + if (!ps->done || !ps->replied) + return; + + pw_work_queue_add(ps->client->impl->work_queue, ps, 0, + do_pending_sample_finish, NULL); +} + +static void sample_play_ready_reply(void *data, struct client *client, uint32_t tag) +{ + struct pending_sample *ps = data; + uint32_t index = id_to_index(client->manager, ps->play->id); + + pw_log_info("%s PLAY_SAMPLE tag:%u index:%u", + client->name, ps->tag, index); + + if (!ps->replied) { + struct message *reply = reply_new(client, ps->tag); + if (client->version >= 13) + message_put(reply, + TAG_U32, index, + TAG_INVALID); + + client_queue_message(client, reply); + ps->replied = true; + } + + schedule_maybe_finish(ps); +} + +static void on_sample_play_ready(void *data, uint32_t id) +{ + struct pending_sample *ps = data; + struct client *client = ps->client; + + if (!ps->replied) + operation_new_cb(client, ps->tag, sample_play_ready_reply, ps); +} + +static void on_sample_play_done(void *data, int res) +{ + struct pending_sample *ps = data; + struct client *client = ps->client; + + if (!ps->replied && res < 0) { + reply_error(client, COMMAND_PLAY_SAMPLE, ps->tag, res); + ps->replied = true; + } + + pw_log_info("%s PLAY_SAMPLE done tag:%u result:%d", client->name, ps->tag, res); + + ps->done = true; + schedule_maybe_finish(ps); +} + +static const struct sample_play_events sample_play_events = { + VERSION_SAMPLE_PLAY_EVENTS, + .ready = on_sample_play_ready, + .done = on_sample_play_done, +}; + +static void on_client_disconnect(void *data) +{ + struct pending_sample *ps = data; + + ps->replied = true; + operation_free_by_tag(ps->client, ps->tag); + + schedule_maybe_finish(ps); +} + +static const struct client_events client_events = { + VERSION_CLIENT_EVENTS, + .disconnect = on_client_disconnect, +}; + +int pending_sample_new(struct client *client, struct sample *sample, struct pw_properties *props, uint32_t tag) +{ + struct pending_sample *ps; + struct sample_play *p = sample_play_new(client->core, sample, props, sizeof(*ps)); + if (!p) + return -errno; + + ps = p->user_data; + ps->client = client; + ps->play = p; + ps->tag = tag; + sample_play_add_listener(p, &ps->listener, &sample_play_events, ps); + client_add_listener(client, &ps->client_listener, &client_events, ps); + spa_list_append(&client->pending_samples, &ps->link); + client->ref++; + + return 0; +} + void pending_sample_free(struct pending_sample *ps) { struct client * const client = ps->client; struct impl * const impl = client->impl; - struct operation *o; spa_list_remove(&ps->link); spa_hook_remove(&ps->listener); + spa_hook_remove(&ps->client_listener); pw_work_queue_cancel(impl->work_queue, ps, SPA_ID_INVALID); - if ((o = operation_find(client, ps->tag)) != NULL) - operation_free(o); + operation_free_by_tag(client, ps->tag); sample_play_destroy(ps->play); }
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/pending-sample.h -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/pending-sample.h
Changed
@@ -11,6 +11,8 @@ #include <spa/utils/hook.h> struct client; +struct pw_properties; +struct sample; struct sample_play; struct pending_sample { @@ -18,11 +20,13 @@ struct client *client; struct sample_play *play; struct spa_hook listener; + struct spa_hook client_listener; uint32_t tag; - unsigned ready:1; + unsigned replied:1; unsigned done:1; }; +int pending_sample_new(struct client *client, struct sample *sample, struct pw_properties *props, uint32_t tag); void pending_sample_free(struct pending_sample *ps); #endif /* PULSE_SERVER_PENDING_SAMPLE_H */
View file
pipewire-0.3.70.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.71.tar.gz/src/modules/module-protocol-pulse/pulse-server.c
Changed
@@ -50,7 +50,6 @@ #include "quirks.h" #include "reply.h" #include "sample.h" -#include "sample-play.h" #include "server.h" #include "stream.h" #include "utils.h" @@ -2502,81 +2501,13 @@ return o; } -static void sample_play_finish(struct pending_sample *ps) -{ - struct client *client = ps->client; - pending_sample_free(ps); - client_unref(client); -} - -static void sample_play_ready_reply(void *data, struct client *client, uint32_t tag) -{ - struct pending_sample *ps = data; - struct message *reply; - uint32_t index = id_to_index(client->manager, ps->play->id); - - pw_log_info("%s PLAY_SAMPLE tag:%u index:%u", - client->name, ps->tag, index); - - ps->ready = true; - - reply = reply_new(client, ps->tag); - if (client->version >= 13) - message_put(reply, - TAG_U32, index, - TAG_INVALID); - - client_queue_message(client, reply); - - if (ps->done) - sample_play_finish(ps); -} - -static void sample_play_ready(void *data, uint32_t id) -{ - struct pending_sample *ps = data; - struct client *client = ps->client; - operation_new_cb(client, ps->tag, sample_play_ready_reply, ps); -} - -static void on_sample_done(void *obj, void *data, int res, uint32_t id) -{ - struct pending_sample *ps = obj; - ps->done = true; - if (ps->ready) - sample_play_finish(ps); -} - -static void sample_play_done(void *data, int res) -{ - struct pending_sample *ps = data; - struct client *client = ps->client; - struct impl *impl = client->impl; - - if (res < 0) - reply_error(client, COMMAND_PLAY_SAMPLE, ps->tag, res); - else - pw_log_info("%s PLAY_SAMPLE done tag:%u", client->name, ps->tag); - - pw_work_queue_add(impl->work_queue, ps, 0, - on_sample_done, client); -} - -static const struct sample_play_events sample_play_events = { - VERSION_SAMPLE_PLAY_EVENTS, - .ready = sample_play_ready, - .done = sample_play_done, -}; - static int do_play_sample(struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct impl *impl = client->impl; uint32_t sink_index, volume; struct sample *sample; - struct sample_play *play; const char *sink_name, *name; struct pw_properties *props = NULL; - struct pending_sample *ps; struct pw_manager_object *o; int res; @@ -2617,20 +2548,7 @@ pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%"PRIu64, o->serial); - play = sample_play_new(client->core, sample, props, sizeof(struct pending_sample)); - props = NULL; - if (play == NULL) - goto error_errno; - - ps = play->user_data; - ps->client = client; - ps->play = play; - ps->tag = tag; - sample_play_add_listener(play, &ps->listener, &sample_play_events, ps); - spa_list_append(&client->pending_samples, &ps->link); - client->ref++; - - return 0; + return pending_sample_new(client, sample, props, tag); error_errno: res = -errno; @@ -5647,7 +5565,11 @@ &context_events, impl); #ifdef HAVE_DBUS - impl->dbus_name = dbus_request_name(context, "org.pulseaudio.Server"); + str = pw_properties_get(props, "server.dbus-name"); + if (str == NULL) + str = "org.pulseaudio.Server"; + if (strlen(str) > 0) + impl->dbus_name = dbus_request_name(context, str); #endif cmd_run(impl);
View file
pipewire-0.3.70.tar.gz/src/modules/module-pulse-tunnel.c -> pipewire-0.3.71.tar.gz/src/modules/module-pulse-tunnel.c
Changed
@@ -224,7 +224,8 @@ switch (state) { case PW_STREAM_STATE_ERROR: case PW_STREAM_STATE_UNCONNECTED: - pw_impl_module_schedule_destroy(impl->module); + if (impl->module) + pw_impl_module_schedule_destroy(impl->module); break; case PW_STREAM_STATE_PAUSED: cork_stream(impl, true); @@ -500,7 +501,8 @@ bool async, uint32_t seq, const void *data, size_t size, void *user_data) { struct impl *impl = user_data; - pw_impl_module_schedule_destroy(impl->module); + if (impl->module) + pw_impl_module_schedule_destroy(impl->module); return 0; } @@ -937,8 +939,10 @@ 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); + if (id == PW_ID_CORE && res == -EPIPE) { + if (impl->module) + pw_impl_module_schedule_destroy(impl->module); + } } static const struct pw_core_events core_events = { @@ -951,7 +955,8 @@ struct impl *impl = d; spa_hook_remove(&impl->core_listener); impl->core = NULL; - pw_impl_module_schedule_destroy(impl->module); + if (impl->module) + pw_impl_module_schedule_destroy(impl->module); } static const struct pw_proxy_events core_proxy_events = { @@ -977,6 +982,8 @@ if (impl->core && impl->do_disconnect) pw_core_disconnect(impl->core); + pw_loop_invoke(impl->main_loop, NULL, 0, NULL, 0, false, impl); + pw_properties_free(impl->stream_props); pw_properties_free(impl->props); @@ -988,6 +995,7 @@ { struct impl *impl = data; spa_hook_remove(&impl->module_listener); + impl->module = NULL; impl_destroy(impl); }
View file
pipewire-0.3.70.tar.gz/src/modules/module-raop-discover.c -> pipewire-0.3.71.tar.gz/src/modules/module-raop-discover.c
Changed
@@ -125,12 +125,10 @@ }; struct tunnel_info { - AvahiIfIndex interface; - AvahiProtocol protocol; const char *name; const char *host_name; - const char *type; - const char *domain; + const char *ip; + const char *port; }; #define TUNNEL_INFO(...) ((struct tunnel_info){ __VA_ARGS__ }) @@ -152,12 +150,10 @@ if (t == NULL) return NULL; - t->info.interface = info->interface; - t->info.protocol = info->protocol; t->info.name = strdup(info->name); t->info.host_name = strdup(info->host_name); - t->info.type = strdup(info->type); - t->info.domain = strdup(info->domain); + t->info.ip = strdup(info->ip); + t->info.port = strdup(info->port); spa_list_append(&impl->tunnel_list, &t->link); return t; @@ -167,11 +163,7 @@ { struct tunnel *t; spa_list_for_each(t, &impl->tunnel_list, link) { - if (t->info.interface == info->interface && - t->info.protocol == info->protocol && - spa_streq(t->info.name, info->name) && - spa_streq(t->info.type, info->type) && - spa_streq(t->info.domain, info->domain)) + if (spa_streq(t->info.name, info->name)) return t; } return NULL; @@ -298,8 +290,8 @@ free((char *) t->info.name); free((char *) t->info.host_name); - free((char *) t->info.type); - free((char *) t->info.domain); + free((char *) t->info.ip); + free((char *) t->info.port); free(t); } @@ -385,23 +377,18 @@ { struct impl *impl = userdata; struct tunnel_info tinfo; - const char *str; + const char *str, *port_str; AvahiStringList *l; struct pw_properties *props = NULL; char atAVAHI_ADDRESS_STR_MAX; - int ipv; if (event != AVAHI_RESOLVER_FOUND) { pw_log_error("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(impl->client))); goto done; } - tinfo = TUNNEL_INFO(.interface = interface, - .protocol = protocol, - .host_name = host_name, - .name = name, - .type = type, - .domain = domain); + + avahi_address_snprint(at, sizeof(at), a); props = pw_properties_new(NULL, NULL); if (props == NULL) { @@ -409,11 +396,7 @@ goto done; } - avahi_address_snprint(at, sizeof(at), a); - ipv = protocol == AVAHI_PROTO_INET ? 4 : 6; - pw_properties_setf(props, "raop.ip", "%s", at); - pw_properties_setf(props, "raop.ip.version", "%d", ipv); pw_properties_setf(props, "raop.port", "%u", port); pw_properties_setf(props, "raop.name", "%s", name); pw_properties_setf(props, "raop.hostname", "%s", host_name); @@ -430,6 +413,13 @@ avahi_free(value); } + port_str = pw_properties_get(props, "raop.port"); + + tinfo = TUNNEL_INFO(.name = name, + .host_name = host_name, + .ip = at, + .port = port_str); + if ((str = pw_properties_get(impl->properties, "stream.rules")) == NULL) str = DEFAULT_CREATE_RULES; if (str != NULL) { @@ -462,18 +452,16 @@ if (flags & AVAHI_LOOKUP_RESULT_LOCAL) return; - info = TUNNEL_INFO(.interface = interface, - .protocol = protocol, - .name = name, - .type = type, - .domain = domain); + info = TUNNEL_INFO(.name = name); t = find_tunnel(impl, &info); switch (event) { case AVAHI_BROWSER_NEW: - if (t != NULL) + if (t != NULL) { + pw_log_debug("found duplicate mdns entry - skipping tunnel creation"); return; + } if (!(avahi_service_resolver_new(impl->client, interface, protocol, name, type, domain,
View file
pipewire-0.3.70.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-raop-sink.c
Changed
@@ -857,6 +857,17 @@ return res; } +static int rtsp_send_volume(struct impl *impl) +{ + if (!impl->recording) + return 0; + + char header128, volstr64; + snprintf(header, sizeof(header), "volume: %s\r\n", + spa_dtoa(volstr, sizeof(volstr), impl->volume)); + return rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, NULL); +} + static int rtsp_record_reply(void *data, int status, const struct spa_dict *headers) { struct impl *impl = data; @@ -892,6 +903,8 @@ impl->sync_period = impl->info.rate / (impl->block_size / impl->frame_size); impl->recording = true; + rtsp_send_volume(impl); + snprintf(progress, sizeof(progress), "progress: %s/%s/%s\r\n", "0", "0", "0"); return rtsp_send(impl, "SET_PARAMETER", "text/parameters", progress, NULL); } @@ -1582,7 +1595,6 @@ uint32_t i, n_vols; float volsSPA_AUDIO_MAX_CHANNELS, volume; float soft_volsSPA_AUDIO_MAX_CHANNELS; - char header128, volstr64; if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { @@ -1595,10 +1607,9 @@ volume = SPA_CLAMPF(20.0 * log10(volume), VOLUME_MIN, VOLUME_MAX); impl->volume = volume; - snprintf(header, sizeof(header), "volume: %s\r\n", - spa_dtoa(volstr, sizeof(volstr), volume)); - rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, NULL); + rtsp_send_volume(impl); } + spa_pod_builder_prop(&b, SPA_PROP_softVolumes, 0); spa_pod_builder_array(&b, sizeof(float), SPA_TYPE_Float, n_vols, soft_vols); @@ -1632,6 +1643,7 @@ case SPA_PARAM_Props: if (param != NULL) stream_props_changed(impl, id, param); + break; default: break; } @@ -1854,7 +1866,7 @@ struct pw_context *context = pw_impl_module_get_context(module); struct pw_properties *props = NULL; struct impl *impl; - const char *str, *name, *hostname, *ipv; + const char *str, *name, *hostname, *ip, *port; int res; PW_LOG_TOPIC_INIT(mod_topic); @@ -1895,12 +1907,23 @@ impl->context = context; impl->loop = pw_context_get_main_loop(context); + ip = pw_properties_get(props, "raop.ip"); + port = pw_properties_get(props, "raop.port"); + if (ip == NULL || port == NULL) { + pw_log_error("Missing raop.ip or raop.port"); + res = -EINVAL; + goto error; + } + if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL) pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true"); if (pw_properties_get(props, PW_KEY_MEDIA_CLASS) == NULL) pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Sink"); + if (pw_properties_get(props, PW_KEY_DEVICE_ICON_NAME) == NULL) + pw_properties_set(props, PW_KEY_DEVICE_ICON_NAME, "audio-speakers"); + if ((name = pw_properties_get(props, "raop.name")) == NULL) name = "RAOP"; @@ -1909,17 +1932,15 @@ if (strlen(str) > 0) name = str; } - if ((ipv = pw_properties_get(props, "raop.ip.version")) == NULL) - ipv = "4"; if ((hostname = pw_properties_get(props, "raop.hostname")) == NULL) hostname = name; + if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL) + pw_properties_setf(props, PW_KEY_NODE_NAME, "raop_sink.%s.%s.%s", + hostname, ip, port); if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL) pw_properties_setf(props, PW_KEY_NODE_DESCRIPTION, - "%s (IPv%s)", name, ipv); - if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL) - pw_properties_setf(props, PW_KEY_NODE_NAME, "raop_sink.%s.ipv%s", - hostname, ipv); + "%s", name); if (pw_properties_get(props, PW_KEY_NODE_LATENCY) == NULL) pw_properties_set(props, PW_KEY_NODE_LATENCY, "352/44100"); @@ -1930,6 +1951,7 @@ 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_DEVICE_ICON_NAME); copy_props(impl, props, PW_KEY_NODE_NAME); copy_props(impl, props, PW_KEY_NODE_DESCRIPTION); copy_props(impl, props, PW_KEY_NODE_GROUP);
View file
pipewire-0.3.70.tar.gz/src/modules/module-roc-source.c -> pipewire-0.3.71.tar.gz/src/modules/module-roc-source.c
Changed
@@ -291,7 +291,7 @@ * See API reference: * https://roc-streaming.org/toolkit/docs/api/reference.html */ - receiver_config.target_latency = data->sess_latency_msec * 1000000; + receiver_config.target_latency = (unsigned long long)data->sess_latency_msec * 1000000ULL; res = roc_receiver_open(data->context, &receiver_config, &data->receiver); if (res) {
View file
pipewire-0.3.70.tar.gz/src/modules/module-rt.c -> pipewire-0.3.71.tar.gz/src/modules/module-rt.c
Changed
@@ -111,7 +111,9 @@ #define PW_SCHED_RESET_ON_FORK 0 #endif -#define IS_VALID_NICE_LEVEL(l) ((l)>=-20 && (l)<=19) +#define MIN_NICE_LEVEL -20 +#define MAX_NICE_LEVEL 19 +#define IS_VALID_NICE_LEVEL(l) ((l)>=MIN_NICE_LEVEL && (l)<=MAX_NICE_LEVEL) #define DEFAULT_NICE_LEVEL 20 #define DEFAULT_RT_PRIO_MIN 11 @@ -251,7 +253,7 @@ bool pw_rtkit_check_xdg_portal(struct pw_rtkit_bus *system_bus) { if (!dbus_bus_name_has_owner(system_bus->bus, XDG_PORTAL_SERVICE_NAME, NULL)) { - pw_log_warn("Can't find %s. Is xdg-desktop-portal running?", XDG_PORTAL_SERVICE_NAME); + pw_log_info("Can't find %s. Is xdg-desktop-portal running?", XDG_PORTAL_SERVICE_NAME); return false; } @@ -277,7 +279,14 @@ if (spa_streq(name, DBUS_ERROR_ACCESS_DENIED) || spa_streq(name, DBUS_ERROR_AUTH_FAILED)) return -EACCES; - + if (spa_streq(name, DBUS_ERROR_IO_ERROR)) + return -EIO; + if (spa_streq(name, DBUS_ERROR_NOT_SUPPORTED)) + return -ENOTSUP; + if (spa_streq(name, DBUS_ERROR_INVALID_ARGS)) + return -EINVAL; + if (spa_streq(name, DBUS_ERROR_TIMED_OUT)) + return -ETIMEDOUT; return -EIO; } @@ -624,8 +633,16 @@ int res = 0; #ifdef HAVE_DBUS - if (impl->use_rtkit) + if (impl->use_rtkit) { + int min_nice = nice_level; + pw_rtkit_get_min_nice_level(impl, &min_nice); + if (nice_level < min_nice) { + pw_log_info("clamped nice level %d to %d", + nice_level, min_nice); + nice_level = min_nice; + } res = pw_rtkit_make_high_priority(impl, 0, nice_level); + } else res = sched_set_nice(nice_level); #else @@ -979,6 +996,12 @@ bool can_use_rtkit = false, use_rtkit = false; + if (!IS_VALID_NICE_LEVEL(impl->nice_level)) { + pw_log_info("invalid nice level %d (not between %d and %d). " + "nice level will not be adjusted", + impl->nice_level, MIN_NICE_LEVEL, MAX_NICE_LEVEL); + } + #ifdef HAVE_DBUS spa_list_init(&impl->threads_list); pthread_mutex_init(&impl->lock, NULL); @@ -992,7 +1015,8 @@ if (!check_realtime_privileges(impl)) { if (!can_use_rtkit) { res = -ENOTSUP; - pw_log_warn("regular realtime scheduling not available (RTKit fallback disabled)"); + pw_log_warn("regular realtime scheduling not available" + " (Portal/RTKit fallback disabled)"); goto error; } use_rtkit = true; @@ -1014,7 +1038,7 @@ impl->object_path = XDG_PORTAL_OBJECT_PATH; impl->interface = XDG_PORTAL_INTERFACE; } else { - pw_log_warn("found session bus but no portal"); + pw_log_info("found session bus but no portal, trying RTKit fallback"); pw_rtkit_bus_free(impl->rtkit_bus); impl->rtkit_bus = NULL; } @@ -1028,7 +1052,8 @@ impl->interface = RTKIT_INTERFACE; } else { res = -errno; - pw_log_warn("could not get system bus: %m"); + pw_log_warn("Realtime scheduling disabled: unsufficient realtime privileges, " + "Portal not found on session bus, and no system bus for RTKit: %m"); goto error; } }
View file
pipewire-0.3.70.tar.gz/src/modules/module-rtp-sap.c -> pipewire-0.3.71.tar.gz/src/modules/module-rtp-sap.c
Changed
@@ -235,7 +235,7 @@ struct spa_source *timer; char *ifname; - bool ttl; + uint32_t ttl; bool mcast_loop; struct sockaddr_storage src_addr; @@ -266,7 +266,7 @@ { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ALAW, 1, "PCMA", "audio", "ALAW" }, { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_ULAW, 1, "PCMU", "audio", "ULAW" }, { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S16_BE, 2, "L16", "audio", "S16BE" }, - { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S24_BE, 3, "L24", "audio", "S16LE" }, + { SPA_MEDIA_SUBTYPE_raw, SPA_AUDIO_FORMAT_S24_BE, 3, "L24", "audio", "S24BE" }, { SPA_MEDIA_SUBTYPE_control, 0, 1, "rtp-midi", "midi", NULL }, { SPA_MEDIA_SUBTYPE_opus, 0, 1, "opus", "opus", NULL }, }; @@ -307,7 +307,7 @@ if (sess->announce) send_sap(impl, sess, 1); spa_list_remove(&sess->link); - impl->n_sessions++; + impl->n_sessions--; } if (sess->node && sess->node->session != NULL) sess->node->session = NULL;
View file
pipewire-0.3.70.tar.gz/src/modules/module-rtp-session.c -> pipewire-0.3.71.tar.gz/src/modules/module-rtp-session.c
Changed
@@ -564,9 +564,21 @@ .send_feedback = recv_send_feedback, }; -static void free_session(struct session *sess) +static int +do_unlink_session(struct spa_loop *loop, + bool async, uint32_t seq, const void *data, size_t size, void *user_data) { + struct session *sess = user_data; spa_list_remove(&sess->link); + return 0; +} + +static void free_session(struct session *sess) +{ + struct impl *impl = sess->impl; + + pw_loop_invoke(impl->data_loop, do_unlink_session, 1, NULL, 0, true, sess); + sess->impl->n_sessions--; if (sess->send) @@ -1710,6 +1722,9 @@ 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(props, "sess.media") == NULL) + pw_properties_set(props, "sess.media", "midi"); + if ((str = pw_properties_get(props, "stream.props")) != NULL) pw_properties_update_string(stream_props, str, strlen(str)); @@ -1735,10 +1750,8 @@ impl->ttl = pw_properties_get_uint32(props, "net.ttl", DEFAULT_TTL); impl->mcast_loop = pw_properties_get_bool(props, "net.loop", DEFAULT_LOOP); - if ((str = pw_properties_get(stream_props, "sess.media")) == NULL) { - str = "midi"; - pw_properties_set(stream_props, "sess.media", str); - } + str = pw_properties_get(stream_props, "sess.media"); + if (spa_streq(str, "audio")) { struct spa_dict_item items = { { "audio.format", DEFAULT_FORMAT },
View file
pipewire-0.3.70.tar.gz/src/modules/module-rtp-sink.c -> pipewire-0.3.71.tar.gz/src/modules/module-rtp-sink.c
Changed
@@ -163,7 +163,7 @@ char *ifname; char *session_name; - bool ttl; + uint32_t ttl; bool mcast_loop; uint32_t dscp;
View file
pipewire-0.3.70.tar.gz/src/pipewire/context.c -> pipewire-0.3.71.tar.gz/src/pipewire/context.c
Changed
@@ -38,6 +38,8 @@ struct spa_plugin_loader plugin_loader; unsigned int recalc:1; unsigned int recalc_pending:1; + + struct pw_data_loop *data_loop_impl; }; @@ -83,10 +85,11 @@ static int context_set_freewheel(struct pw_context *context, bool freewheel) { + struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this); struct spa_thread *thr; int res = 0; - if ((thr = pw_data_loop_get_thread(context->data_loop_impl)) == NULL) + if ((thr = pw_data_loop_get_thread(impl->data_loop_impl)) == NULL) return -EIO; if (freewheel) { @@ -274,9 +277,9 @@ if ((str = pw_properties_get(pr, "context.data-loop." PW_KEY_LIBRARY_NAME_SYSTEM))) pw_properties_set(pr, PW_KEY_LIBRARY_NAME_SYSTEM, str); - this->data_loop_impl = pw_data_loop_new(&pr->dict); + impl->data_loop_impl = pw_data_loop_new(&pr->dict); pw_properties_free(pr); - if (this->data_loop_impl == NULL) { + if (impl->data_loop_impl == NULL) { res = -errno; goto error_free; } @@ -287,7 +290,7 @@ goto error_free; } - this->data_loop = pw_data_loop_get_loop(this->data_loop_impl); + this->data_loop = pw_data_loop_get_loop(impl->data_loop_impl); this->data_system = this->data_loop->system; this->main_loop = main_loop; @@ -353,10 +356,10 @@ goto error_free; pw_log_info("%p: parsed %d context.exec items", this, res); - if ((res = pw_data_loop_start(this->data_loop_impl)) < 0) + if ((res = pw_data_loop_start(impl->data_loop_impl)) < 0) goto error_free; - pw_data_loop_invoke(this->data_loop_impl, + pw_data_loop_invoke(impl->data_loop_impl, do_data_loop_setup, 0, NULL, 0, false, this); pw_settings_expose(this); @@ -409,8 +412,8 @@ spa_list_consume(resource, &context->registry_resource_list, link) pw_resource_destroy(resource); - if (context->data_loop_impl) - pw_data_loop_stop(context->data_loop_impl); + if (impl->data_loop_impl) + pw_data_loop_stop(impl->data_loop_impl); spa_list_consume(module, &context->module_list, link) pw_impl_module_destroy(module); @@ -427,8 +430,8 @@ pw_log_debug("%p: free", context); pw_context_emit_free(context); - if (context->data_loop_impl) - pw_data_loop_destroy(context->data_loop_impl); + if (impl->data_loop_impl) + pw_data_loop_destroy(impl->data_loop_impl); if (context->pool) pw_mempool_destroy(context->pool); @@ -491,7 +494,8 @@ SPA_EXPORT struct pw_data_loop *pw_context_get_data_loop(struct pw_context *context) { - return context->data_loop_impl; + struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this); + return impl->data_loop_impl; } SPA_EXPORT @@ -799,7 +803,7 @@ pw_log_debug(" peer %p: '%s'", t, t->name); t->runnable = true; - if (!t->driver) + if (!t->driving) run_nodes(context, t, nodes); } } @@ -812,7 +816,7 @@ pw_log_debug(" peer %p: '%s'", t, t->name); t->runnable = true; - if (!t->driver) + if (!t->driving) run_nodes(context, t, nodes); } } @@ -830,7 +834,7 @@ pw_log_debug(" group %p: '%s'", t, t->name); t->runnable = true; - if (!t->driver) + if (!t->driving) run_nodes(context, t, nodes); } } @@ -933,7 +937,7 @@ pw_log_debug(" next node %p: '%s' runnable:%u", n, n->name, n->runnable); } spa_list_for_each(n, collect, sort_link) - if (!n->driver && n->runnable) + if (!n->driving && n->runnable) run_nodes(context, n, collect); return 0; @@ -1264,7 +1268,8 @@ driver = NULL; spa_list_for_each(t, &collect, sort_link) { /* is any active and want a driver */ - if (t->want_driver && t->active && t->runnable) { + if ((t->want_driver && t->active && t->runnable) || + t->always_process) { driver = target; driver->runnable = true; break; @@ -1609,6 +1614,7 @@ int pw_context_set_object(struct pw_context *context, const char *type, void *value) { struct object_entry *entry; + struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this); entry = find_object(context, type); @@ -1626,8 +1632,8 @@ } if (spa_streq(type, SPA_TYPE_INTERFACE_ThreadUtils)) { context->thread_utils = value; - if (context->data_loop_impl) - pw_data_loop_set_thread_utils(context->data_loop_impl, + if (impl->data_loop_impl) + pw_data_loop_set_thread_utils(impl->data_loop_impl, context->thread_utils); } return 0;
View file
pipewire-0.3.70.tar.gz/src/pipewire/data-loop.c -> pipewire-0.3.71.tar.gz/src/pipewire/data-loop.c
Changed
@@ -51,6 +51,10 @@ { struct pw_data_loop *this = user_data; int res; + struct spa_callbacks *cb = &this->loop->control->iface.cb; + const struct spa_loop_control_methods *m = cb->funcs; + void *data = cb->data; + int (*iterate) (void *object, int timeout) = m->iterate; pw_log_debug("%p: enter thread", this); pw_loop_enter(this->loop); @@ -58,7 +62,7 @@ pthread_cleanup_push(thread_cleanup, this); while (SPA_LIKELY(this->running)) { - if (SPA_UNLIKELY((res = pw_loop_iterate(this->loop, -1)) < 0)) { + if (SPA_UNLIKELY((res = iterate(data, -1)) < 0)) { if (res == -EINTR) continue; pw_log_error("%p: iterate error %d (%s)",
View file
pipewire-0.3.70.tar.gz/src/pipewire/filter.c -> pipewire-0.3.71.tar.gz/src/pipewire/filter.c
Changed
@@ -46,8 +46,6 @@ struct queue { uint32_t idsMAX_BUFFERS; struct spa_ringbuffer ring; - uint64_t incount; - uint64_t outcount; }; struct data { @@ -107,6 +105,8 @@ const char *path; struct pw_context *context; + struct pw_loop *main_loop; + struct pw_loop *data_loop; enum pw_filter_flags flags; @@ -129,13 +129,14 @@ #define NODE_PropInfo 0 #define NODE_Props 1 #define NODE_ProcessLatency 2 -#define N_NODE_PARAMS 3 +#define NODE_EnumFormat 3 +#define NODE_Format 4 +#define N_NODE_PARAMS 5 struct spa_param_info paramsN_NODE_PARAMS; struct spa_process_latency_info process_latency; struct data data; - uintptr_t seq; struct pw_time time; uint64_t base_pos; uint32_t clock_id; @@ -145,6 +146,7 @@ unsigned int disconnecting:1; unsigned int disconnect_core:1; unsigned int draining:1; + unsigned int drained:1; unsigned int allow_mlock:1; unsigned int warn_mlock:1; unsigned int process_rt:1; @@ -160,6 +162,10 @@ return NODE_Props; case SPA_PARAM_ProcessLatency: return NODE_ProcessLatency; + case SPA_PARAM_EnumFormat: + return NODE_EnumFormat; + case SPA_PARAM_Format: + return NODE_Format; default: return -1; } @@ -319,7 +325,6 @@ return -EINVAL; SPA_FLAG_SET(buffer->flags, BUFFER_FLAG_QUEUED); - queue->incount += buffer->this.size; spa_ringbuffer_get_write_index(&queue->ring, &index); queue->idsindex & MASK_BUFFERS = buffer->id; @@ -342,7 +347,6 @@ spa_ringbuffer_read_update(&queue->ring, index + 1); buffer = &port->buffersid; - queue->outcount += buffer->this.size; SPA_FLAG_CLEAR(buffer->flags, BUFFER_FLAG_QUEUED); return buffer; @@ -351,29 +355,30 @@ static inline void clear_queue(struct port *port, struct queue *queue) { spa_ringbuffer_init(&queue->ring); - queue->incount = queue->outcount; } -static bool filter_set_state(struct pw_filter *filter, enum pw_filter_state state, const char *error) +static bool filter_set_state(struct pw_filter *filter, enum pw_filter_state state, + int res, const char *error) { enum pw_filter_state old = filter->state; - bool res = old != state; + bool changed = old != state; - if (res) { + if (changed) { free(filter->error); filter->error = error ? strdup(error) : NULL; + filter->error_res = res; - pw_log_debug("%p: update state from %s -> %s (%s)", filter, + pw_log_debug("%p: update state from %s -> %s: (%d) %s", filter, pw_filter_state_as_string(old), - pw_filter_state_as_string(state), filter->error); + pw_filter_state_as_string(state), res, error); if (state == PW_FILTER_STATE_ERROR) - pw_log_error("%p: error %s", filter, error); + pw_log_error("%p: error (%d) %s", filter, res, error); filter->state = state; pw_filter_emit_state_changed(filter, old, state, error); } - return res; + return changed; } static int enum_params(struct filter *d, struct spa_list *param_list, int seq, @@ -433,9 +438,6 @@ struct filter *impl = object; struct pw_filter *filter = &impl->this; - if (id != SPA_PARAM_Props) - return -ENOTSUP; - pw_filter_emit_param_changed(filter, NULL, id, param); return 0; } @@ -467,7 +469,7 @@ impl->position = data; else impl->position = NULL; - pw_loop_invoke(impl->context->data_loop, + pw_loop_invoke(impl->data_loop, do_set_position, 1, NULL, 0, true, impl); break; } @@ -486,17 +488,17 @@ case SPA_NODE_COMMAND_Suspend: case SPA_NODE_COMMAND_Flush: case SPA_NODE_COMMAND_Pause: - pw_loop_invoke(impl->context->main_loop, + pw_loop_invoke(impl->main_loop, NULL, 0, NULL, 0, false, impl); if (filter->state == PW_FILTER_STATE_STREAMING) { pw_log_debug("%p: pause", filter); - filter_set_state(filter, PW_FILTER_STATE_PAUSED, NULL); + filter_set_state(filter, PW_FILTER_STATE_PAUSED, 0, NULL); } break; case SPA_NODE_COMMAND_Start: if (filter->state == PW_FILTER_STATE_PAUSED) { pw_log_debug("%p: start", filter); - filter_set_state(filter, PW_FILTER_STATE_STREAMING, NULL); + filter_set_state(filter, PW_FILTER_STATE_STREAMING, 0, NULL); } break; default: @@ -849,7 +851,7 @@ pw_filter_emit_param_changed(filter, port->user_data, id, param); if (filter->state == PW_FILTER_STATE_ERROR) - return -EIO; + return filter->error_res; emit_port_info(impl, port, false); @@ -951,23 +953,6 @@ return 0; } -static inline void copy_position(struct filter *impl) -{ - struct spa_io_position *p = impl->rt.position; - if (SPA_UNLIKELY(p != NULL)) { - SEQ_WRITE(impl->seq); - impl->time.now = p->clock.nsec; - impl->time.rate = p->clock.rate; - if (SPA_UNLIKELY(impl->clock_id != p->clock.id)) { - impl->base_pos = p->clock.position - impl->time.ticks; - impl->clock_id = p->clock.id; - } - impl->time.ticks = p->clock.position - impl->base_pos; - impl->time.delay = 0; - SEQ_WRITE(impl->seq); - } -} - static int do_call_process(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) @@ -987,7 +972,7 @@ process, 0, impl->rt.position); } else { - pw_loop_invoke(impl->context->main_loop, + pw_loop_invoke(impl->main_loop, do_call_process, 1, NULL, 0, false, impl); } } @@ -1000,13 +985,12 @@ struct pw_filter *filter = &impl->this; pw_log_trace("%p: drained", filter); pw_filter_emit_drained(filter); - impl->draining = false; return 0; } static void call_drained(struct filter *impl) { - pw_loop_invoke(impl->context->main_loop, + pw_loop_invoke(impl->main_loop, do_call_drained, 1, NULL, 0, false, impl); } @@ -1017,64 +1001,63 @@ struct buffer *b; bool drained = true; - pw_log_trace("%p: do process %p", impl, impl->rt.position); + pw_log_trace_fp("%p: do process %p", impl, impl->rt.position); /** first dequeue and recycle buffers */ spa_list_for_each(p, &impl->port_list, link) { struct spa_io_buffers *io = p->io; - if (io == NULL || - io->buffer_id >= p->n_buffers) + if (SPA_UNLIKELY(io == NULL || + io->buffer_id >= p->n_buffers)) continue; if (p->direction == SPA_DIRECTION_INPUT) { - if (io->status != SPA_STATUS_HAVE_DATA) + if (SPA_UNLIKELY(io->status != SPA_STATUS_HAVE_DATA)) continue; /* push new buffer */ b = &p->buffersio->buffer_id; - pw_log_trace("%p: dequeue buffer %d", impl, b->id); + pw_log_trace_fp("%p: dequeue buffer %d", impl, b->id); push_queue(p, &p->dequeued, b); drained = false; } else { - if (io->status == SPA_STATUS_HAVE_DATA) + if (SPA_UNLIKELY(io->status == SPA_STATUS_HAVE_DATA)) continue; /* recycle old buffer */ b = &p->buffersio->buffer_id; - pw_log_trace("%p: recycle buffer %d", impl, b->id); + pw_log_trace_fp("%p: recycle buffer %d", impl, b->id); push_queue(p, &p->dequeued, b); } } - copy_position(impl); call_process(impl); /** recycle/push queued buffers */ spa_list_for_each(p, &impl->port_list, link) { struct spa_io_buffers *io = p->io; - if (io == NULL) + if (SPA_UNLIKELY(io == NULL)) continue; if (p->direction == SPA_DIRECTION_INPUT) { - if (io->status != SPA_STATUS_HAVE_DATA) + if (SPA_UNLIKELY(io->status != SPA_STATUS_HAVE_DATA)) continue; /* pop buffer to recycle */ if ((b = pop_queue(p, &p->queued)) != NULL) { - pw_log_trace("%p: recycle buffer %d", impl, b->id); + pw_log_trace_fp("%p: recycle buffer %d", impl, b->id); io->buffer_id = b->id; } else { io->buffer_id = SPA_ID_INVALID; } io->status = SPA_STATUS_NEED_DATA; } else { - if (io->status == SPA_STATUS_HAVE_DATA) + if (SPA_UNLIKELY(io->status == SPA_STATUS_HAVE_DATA)) continue; if ((b = pop_queue(p, &p->queued)) != NULL) { - pw_log_trace("%p: pop %d %p", impl, b->id, io); + pw_log_trace_fp("%p: pop %d %p", impl, b->id, io); io->buffer_id = b->id; io->status = SPA_STATUS_HAVE_DATA; drained = false; @@ -1084,7 +1067,8 @@ } } } - if (drained && impl->draining) + impl->drained = drained; + if (SPA_UNLIKELY(drained && impl->draining)) call_drained(impl); return SPA_STATUS_NEED_DATA | SPA_STATUS_HAVE_DATA; @@ -1112,7 +1096,7 @@ pw_log_debug("%p: removed", filter); spa_hook_remove(&filter->proxy_listener); filter->node_id = SPA_ID_INVALID; - filter_set_state(filter, PW_FILTER_STATE_UNCONNECTED, NULL); + filter_set_state(filter, PW_FILTER_STATE_UNCONNECTED, 0, NULL); } static void proxy_destroy(void *_data) @@ -1127,7 +1111,7 @@ struct pw_filter *filter = _data; /* we just emit the state change here to inform the application. * If this is supposed to be a permanent error, the app should - * do a pw_stream_set_error() */ + * do a pw_filter_set_error() */ pw_filter_emit_state_changed(filter, filter->state, PW_FILTER_STATE_ERROR, message); } @@ -1138,7 +1122,7 @@ filter->node_id = global_id; if (props) pw_properties_update(filter->properties, props); - filter_set_state(filter, PW_FILTER_STATE_PAUSED, NULL); + filter_set_state(filter, PW_FILTER_STATE_PAUSED, 0, NULL); } static const struct pw_proxy_events proxy_events = { @@ -1157,7 +1141,7 @@ id, seq, res, spa_strerror(res), message); if (id == PW_ID_CORE && res == -EPIPE) { - filter_set_state(filter, PW_FILTER_STATE_UNCONNECTED, message); + filter_set_state(filter, PW_FILTER_STATE_UNCONNECTED, res, message); } } @@ -1200,6 +1184,8 @@ goto error_cleanup; } + impl->main_loop = pw_context_get_main_loop(context); + this = &impl->this; pw_log_debug("%p: new", impl); @@ -1367,11 +1353,17 @@ return -EBUSY; impl->disconnecting = true; + if (filter->node) + pw_impl_node_set_active(filter->node, false); if (filter->proxy) { pw_proxy_destroy(filter->proxy); filter->proxy = NULL; } + + if (filter->node) + pw_impl_node_destroy(filter->node); + if (impl->disconnect_core) { impl->disconnect_core = false; spa_hook_remove(&filter->core_listener); @@ -1399,7 +1391,7 @@ struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); struct port *p; - ensure_loop(impl->context->main_loop, return); + ensure_loop(impl->main_loop, return); pw_log_debug("%p: destroy", filter); @@ -1453,7 +1445,7 @@ { struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); - ensure_loop(impl->context->main_loop); + ensure_loop(impl->main_loop); spa_hook_list_append(&filter->listener_list, listener, events, data); if (events->process && impl->rt_callbacks.funcs == NULL) { @@ -1503,7 +1495,7 @@ struct port *port = SPA_CONTAINER_OF(port_data, struct port, user_data); int changed = 0; - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); if (port_data) { changed = pw_properties_update(port->props, dict); @@ -1530,19 +1522,30 @@ return changed; } -SPA_EXPORT -int +static void node_event_destroy(void *data) +{ + struct pw_filter *filter = data; + spa_hook_remove(&filter->node_listener); + filter->node = NULL; +} + +static const struct pw_impl_node_events node_events = { + PW_VERSION_IMPL_NODE_EVENTS, + .destroy = node_event_destroy, +}; + +SPA_EXPORT int pw_filter_connect(struct pw_filter *filter, enum pw_filter_flags flags, const struct spa_pod **params, uint32_t n_params) { struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); + struct pw_properties *props = NULL; int res; uint32_t i; - struct spa_dict_item items1; - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); if (filter->proxy != NULL || filter->state != PW_FILTER_STATE_UNCONNECTED) return -EBUSY; @@ -1572,6 +1575,8 @@ impl->paramsNODE_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, 0); impl->paramsNODE_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_WRITE); impl->paramsNODE_ProcessLatency = SPA_PARAM_INFO(SPA_PARAM_ProcessLatency, 0); + impl->paramsNODE_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, 0); + impl->paramsNODE_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); impl->info.params = impl->params; impl->info.n_params = N_NODE_PARAMS; impl->info.change_mask = impl->change_mask_all; @@ -1584,7 +1589,7 @@ impl->disconnecting = false; impl->draining = false; impl->driving = false; - filter_set_state(filter, PW_FILTER_STATE_CONNECTING, NULL); + filter_set_state(filter, PW_FILTER_STATE_CONNECTING, 0, NULL); if (flags & PW_FILTER_FLAG_DRIVER) pw_properties_set(filter->properties, PW_KEY_NODE_DRIVER, "true"); @@ -1604,12 +1609,27 @@ impl->disconnect_core = true; } - pw_log_debug("%p: export node %p", filter, &impl->impl_node); + pw_log_debug("%p: creating node", filter); + props = pw_properties_copy(filter->properties); + if (props == NULL) { + res = -errno; + goto error_node; + } + + filter->node = pw_context_create_node(impl->context, props, 0); + props = NULL; + if (filter->node == NULL) { + res = -errno; + goto error_node; + } + pw_impl_node_set_implementation(filter->node, &impl->impl_node); + + impl->data_loop = filter->node->data_loop; + + pw_log_debug("%p: export node %p", filter, filter->node); - items0 = SPA_DICT_ITEM_INIT(PW_KEY_OBJECT_REGISTER, "false"); filter->proxy = pw_core_export(filter->core, - SPA_TYPE_INTERFACE_Node, &SPA_DICT_INIT_ARRAY(items), - &impl->impl_node, 0); + PW_TYPE_INTERFACE_Node, NULL, filter->node, 0); if (filter->proxy == NULL) { res = -errno; goto error_proxy; @@ -1617,13 +1637,24 @@ pw_proxy_add_listener(filter->proxy, &filter->proxy_listener, &proxy_events, filter); + pw_impl_node_add_listener(filter->node, &filter->node_listener, &node_events, filter); + + pw_impl_node_set_active(filter->node, + !SPA_FLAG_IS_SET(impl->flags, PW_FILTER_FLAG_INACTIVE)); + return 0; error_connect: pw_log_error("%p: can't connect: %s", filter, spa_strerror(res)); - return res; + goto exit_cleanup; +error_node: + pw_log_error("%p: can't make node: %s", filter, spa_strerror(res)); + goto exit_cleanup; error_proxy: pw_log_error("%p: can't make proxy: %s", filter, spa_strerror(res)); + goto exit_cleanup; +exit_cleanup: + pw_properties_free(props); return res; } @@ -1637,7 +1668,7 @@ int pw_filter_disconnect(struct pw_filter *filter) { struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); return filter_disconnect(impl); } @@ -1720,7 +1751,7 @@ struct port *p; const char *str; - ensure_loop(impl->context->main_loop, return NULL); + ensure_loop(impl->main_loop, return NULL); if (props == NULL) props = pw_properties_new(NULL, NULL); @@ -1784,7 +1815,7 @@ struct port *port = SPA_CONTAINER_OF(port_data, struct port, user_data); struct filter *impl = port->filter; - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); free_port(impl, port); return 0; @@ -1796,7 +1827,7 @@ { struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); if (res < 0) { va_list args; @@ -1811,7 +1842,7 @@ if (filter->proxy) pw_proxy_error(filter->proxy, res, value); - filter_set_state(filter, PW_FILTER_STATE_ERROR, value); + filter_set_state(filter, PW_FILTER_STATE_ERROR, res, value); free(value); } @@ -1828,7 +1859,7 @@ struct port *port; int res; - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); pw_log_debug("%p: update params", filter); @@ -1851,9 +1882,16 @@ { struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); pw_log_debug("%p: active:%d", filter, active); + if (filter->node == NULL) + return -EIO; + + pw_impl_node_set_active(filter->node, active); + + if (!active || impl->drained) + impl->drained = impl->draining = false; return 0; } @@ -1861,18 +1899,22 @@ int pw_filter_get_time(struct pw_filter *filter, struct pw_time *time) { struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); - uintptr_t seq1, seq2; + struct spa_io_position *p = impl->position; - do { - seq1 = SEQ_READ(impl->seq); + if (SPA_LIKELY(p != NULL)) { + impl->time.now = p->clock.nsec; + impl->time.rate = p->clock.rate; + if (SPA_UNLIKELY(impl->clock_id != p->clock.id)) { + impl->base_pos = p->clock.position - impl->time.ticks; + impl->clock_id = p->clock.id; + } + impl->time.ticks = p->clock.position - impl->base_pos; + impl->time.delay = 0; *time = impl->time; - seq2 = SEQ_READ(impl->seq); - } while (!SEQ_READ_SUCCESS(seq1, seq2)); - + } pw_log_trace("%p: %"PRIi64" %"PRIi64" %"PRIu64" %d/%d ", filter, time->now, time->delay, time->ticks, time->rate.num, time->rate.denom); - return 0; } @@ -1884,13 +1926,13 @@ struct buffer *b; int res; - if ((b = pop_queue(p, &p->dequeued)) == NULL) { + if (SPA_UNLIKELY((b = pop_queue(p, &p->dequeued)) == NULL)) { res = -errno; - pw_log_trace("%p: no more buffers: %m", impl); + pw_log_debug("%p: no more buffers: %m", impl); errno = -res; return NULL; } - pw_log_trace("%p: dequeue buffer %d", impl, b->id); + pw_log_trace_fp("%p: dequeue buffer %d", impl, b->id); return &b->this; } @@ -1899,15 +1941,9 @@ int pw_filter_queue_buffer(void *port_data, struct pw_buffer *buffer) { struct port *p = SPA_CONTAINER_OF(port_data, struct port, user_data); - struct filter *impl = p->filter; struct buffer *b = SPA_CONTAINER_OF(buffer, struct buffer, this); - int res; - - pw_log_trace("%p: queue buffer %d", impl, b->id); - if ((res = push_queue(p, &p->queued, b)) < 0) - return res; - - return res; + pw_log_trace_fp("%p: queue buffer %d", p->filter, b->id); + return push_queue(p, &p->queued, b); } SPA_EXPORT @@ -1917,7 +1953,7 @@ struct pw_buffer *buf; struct spa_data *d; - if ((buf = pw_filter_dequeue_buffer(port_data)) == NULL) + if (SPA_UNLIKELY((buf = pw_filter_dequeue_buffer(port_data)) == NULL)) return NULL; d = &buf->buffer->datas0; @@ -1928,7 +1964,6 @@ d->chunk->stride = sizeof(float); d->chunk->flags = 0; } - pw_filter_queue_buffer(port_data, buf); return d->data; @@ -1949,10 +1984,6 @@ push_queue(impl, &impl->dequeued, b); } while (b); - - impl->time.queued = impl->queued.outcount = impl->dequeued.incount = - impl->dequeued.outcount = impl->queued.incount; - #endif return 0; } @@ -1962,6 +1993,7 @@ { struct filter *impl = user_data; impl->draining = true; + impl->drained = false; return 0; } @@ -1969,7 +2001,7 @@ int pw_filter_flush(struct pw_filter *filter, bool drain) { struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this); - pw_loop_invoke(impl->context->data_loop, + pw_loop_invoke(impl->data_loop, drain ? do_drain : do_flush, 1, NULL, 0, true, impl); return 0; } @@ -2013,10 +2045,10 @@ pw_log_trace_fp("%p", impl); if (!impl->driving) { - res = pw_loop_invoke(impl->context->main_loop, + res = pw_loop_invoke(impl->main_loop, do_trigger_request_process, 1, NULL, 0, false, impl); } else { - res = pw_loop_invoke(impl->context->data_loop, + res = pw_loop_invoke(impl->data_loop, do_trigger_process, 1, NULL, 0, false, impl); } return res;
View file
pipewire-0.3.70.tar.gz/src/pipewire/impl-client.c -> pipewire-0.3.71.tar.gz/src/pipewire/impl-client.c
Changed
@@ -101,20 +101,45 @@ { struct pw_resource *r = object; struct error_data *d = data; - if (r && r->bound_id == d->id) + + if (r && r->bound_id == d->id) { + pw_log_debug("%p: client error for global %u: %d (%s)", + r, d->id, d->res, d->error); pw_resource_error(r, d->res, d->error); + } return 0; } static int client_error(void *object, uint32_t id, int res, const char *error) { struct resource_data *data = object; + struct pw_resource *resource = data->resource; + struct pw_impl_client *sender = resource->client; struct pw_impl_client *client = data->client; struct error_data d = { id, res, error }; + struct pw_global *global; - pw_log_debug("%p: error for global %d", client, id); + /* Check the global id provided by sender refers to a registered global + * known to the sender. + */ + if ((global = pw_context_find_global(resource->context, id)) == NULL) + goto error_no_id; + if (sender->recv_generation != 0 && global->generation > sender->recv_generation) + goto error_stale_id; + + pw_log_debug("%p: sender %p: error for global %u", client, sender, id); pw_map_for_each(&client->objects, error_resource, &d); return 0; + +error_no_id: + pw_log_debug("%p: sender %p: error for invalid global %u", client, sender, id); + pw_resource_errorf(resource, -ENOENT, "no global %u", id); + return -ENOENT; +error_stale_id: + pw_log_debug("%p: sender %p: error for stale global %u generation:%"PRIu64" recv-generation:%"PRIu64, + client, sender, id, global->generation, sender->recv_generation); + pw_resource_errorf(resource, -ESTALE, "no global %u any more", id); + return -ESTALE; } static bool has_key(const char * const keys, const char *key)
View file
pipewire-0.3.70.tar.gz/src/pipewire/impl-link.c -> pipewire-0.3.71.tar.gz/src/pipewire/impl-link.c
Changed
@@ -52,6 +52,81 @@ /** \endcond */ +static struct pw_node_peer *pw_node_peer_ref(struct pw_impl_node *onode, struct pw_impl_node *inode) +{ + struct pw_node_peer *peer; + + spa_list_for_each(peer, &onode->peer_list, link) { + if (peer->target.node == inode) { + pw_log_debug("exiting peer %p from %p to %p", peer, onode, inode); + peer->ref++; + return peer; + } + } + peer = calloc(1, sizeof(*peer)); + if (peer == NULL) + return NULL; + + peer->ref = 1; + peer->output = onode; + peer->active_count = 0; + peer->target.node = inode; + peer->target.activation = inode->rt.activation; + peer->target.system = inode->data_system; + peer->target.fd = inode->source.fd; + + spa_list_append(&onode->peer_list, &peer->link); + pw_log_debug("new peer %p from %p to %p", peer, onode, inode); + pw_impl_node_emit_peer_added(onode, inode); + + return peer; +} + +static void pw_node_peer_unref(struct pw_node_peer *peer) +{ + if (--peer->ref > 0) + return; + spa_list_remove(&peer->link); + pw_log_debug("remove peer %p from %p to %p", peer, peer->output, peer->target.node); + pw_impl_node_emit_peer_removed(peer->output, peer->target.node); + free(peer); +} + +static void pw_node_peer_activate(struct pw_node_peer *peer) +{ + struct pw_node_activation_state *state; + + state = &peer->target.activation->state0; + + if (peer->active_count++ == 0) { + spa_list_append(&peer->output->rt.target_list, &peer->target.link); + if (!peer->target.active && peer->output->rt.driver_target.node != NULL) { + state->required++; + peer->target.active = true; + } + } + pw_log_trace("%p: node:%p state:%p pending:%d/%d", peer->output, + peer->target.node, state, state->pending, state->required); +} + +static void pw_node_peer_deactivate(struct pw_node_peer *peer) +{ + struct pw_node_activation_state *state; + state = &peer->target.activation->state0; + if (--peer->active_count == 0) { + + spa_list_remove(&peer->target.link); + + if (peer->target.active) { + state->required--; + peer->target.active = false; + } + } + pw_log_trace("%p: node:%p state:%p pending:%d/%d", peer->output, + peer->target.node, state, state->pending, state->required); +} + + static void info_changed(struct pw_impl_link *link) { struct pw_resource *resource; @@ -566,28 +641,15 @@ bool async, uint32_t seq, const void *data, size_t size, void *user_data) { struct pw_impl_link *this = user_data; - struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); pw_log_trace("%p: activate", this); spa_list_append(&this->output->rt.mix_list, &this->rt.out_mix.rt_link); spa_list_append(&this->input->rt.mix_list, &this->rt.in_mix.rt_link); - if (impl->inode != impl->onode) { - struct pw_node_activation_state *state; - - this->rt.target.activation = impl->inode->rt.activation; - spa_list_append(&impl->onode->rt.target_list, &this->rt.target.link); - - state = &this->rt.target.activation->state0; - if (!this->rt.target.active && impl->onode->rt.driver_target.node != NULL) { - state->required++; - this->rt.target.active = true; - } + if (this->peer) + pw_node_peer_activate(this->peer); - pw_log_trace("%p: node:%p state:%p pending:%d/%d", this, impl->inode, - state, state->pending, state->required); - } return 0; } @@ -600,7 +662,7 @@ pw_link_state_as_string(this->info.state)); if (impl->activated || !this->prepared || - !impl->inode->active || !impl->onode->active) + !impl->inode->runnable || !impl->onode->runnable) return 0; if (!impl->io_set) { @@ -782,26 +844,14 @@ bool async, uint32_t seq, const void *data, size_t size, void *user_data) { struct pw_impl_link *this = user_data; - struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); pw_log_trace("%p: disable %p and %p", this, &this->rt.in_mix, &this->rt.out_mix); spa_list_remove(&this->rt.out_mix.rt_link); spa_list_remove(&this->rt.in_mix.rt_link); - if (impl->inode != impl->onode) { - struct pw_node_activation_state *state; - - spa_list_remove(&this->rt.target.link); - state = &this->rt.target.activation->state0; - if (this->rt.target.active) { - state->required--; - this->rt.target.active = false; - } - - pw_log_trace("%p: node:%p state:%p pending:%d/%d", this, impl->inode, - state, state->pending, state->required); - } + if (this->peer) + pw_node_peer_deactivate(this->peer); return 0; } @@ -1252,9 +1302,6 @@ impl->inode = input_node; } - this->rt.target.signal_func = impl->inode->rt.target.signal_func; - this->rt.target.data = impl->inode->rt.target.data; - pw_log_debug("%p: constructed out:%p:%d.%d -> in:%p:%d.%d", impl, output_node, output->port_id, this->rt.out_mix.port.port_id, input_node, input->port_id, this->rt.in_mix.port.port_id); @@ -1272,7 +1319,8 @@ pw_impl_port_recalc_latency(output); pw_impl_port_recalc_latency(input); - pw_impl_node_emit_peer_added(impl->onode, impl->inode); + if (impl->onode != impl->inode) + this->peer = pw_node_peer_ref(impl->onode, impl->inode); return this; @@ -1407,7 +1455,8 @@ if (link->registered) spa_list_remove(&link->link); - pw_impl_node_emit_peer_removed(impl->onode, impl->inode); + if (link->peer) + pw_node_peer_unref(link->peer); try_unlink_controls(impl, link->output, link->input);
View file
pipewire-0.3.70.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.71.tar.gz/src/pipewire/impl-node.c
Changed
@@ -41,6 +41,8 @@ unsigned int cache_params:1; unsigned int pending_play:1; + + uint64_t prev_signal_time; }; #define pw_node_resource(r,m,v,...) pw_resource_call(r,struct pw_node_events,m,v,__VA_ARGS__) @@ -65,6 +67,19 @@ /** \endcond */ +/* Called from the node data loop when a node needs to be scheduled by + * the given driver. 3 things needs to happen: + * + * - the node is added to the driver target list and the required state + * is incremented. This makes sure the node is woken up when the driver + * starts a new cycle. + * - the node needs to trigger the driver when it completes. This means + * the driver is added to the target list. + * - the node targets (including the driver we added above) have their + * required state incremented. + * + * This code is called from the data-loop to ensure synchronization + */ static void add_node(struct pw_impl_node *this, struct pw_impl_node *driver) { struct pw_node_activation_state *dstate, *nstate; @@ -76,12 +91,7 @@ pw_log_trace("%p: add to driver %p %p %p", this, driver, driver->rt.activation, this->rt.activation); - /* signal the driver */ - this->rt.driver_target.activation = driver->rt.activation; - this->rt.driver_target.node = driver; - this->rt.driver_target.data = driver; - spa_list_append(&this->rt.target_list, &this->rt.driver_target.link); - + /* let the driver trigger us as part of the processing cycle */ spa_list_append(&driver->rt.target_list, &this->rt.target.link); nstate = &this->rt.activation->state0; if (!this->rt.target.active) { @@ -89,6 +99,15 @@ this->rt.target.active = true; } + /* trigger the driver when we complete */ + this->rt.driver_target.activation = driver->rt.activation; + this->rt.driver_target.node = driver; + this->rt.driver_target.system = driver->data_system; + this->rt.driver_target.fd = driver->source.fd; + spa_list_append(&this->rt.target_list, &this->rt.driver_target.link); + + /* now increment the required states of all this node targets, including + * the driver we added above */ spa_list_for_each(t, &this->rt.target_list, link) { dstate = &t->activation->state0; if (!t->active) { @@ -101,6 +120,7 @@ } } +/* called from the data loop and undoes the changes done in add_node. */ static void remove_node(struct pw_impl_node *this) { struct pw_node_activation_state *dstate, *nstate; @@ -110,7 +130,7 @@ return; pw_log_trace("%p: remove from driver %p %p %p", - this, this->rt.driver_target.data, + this, this->rt.driver_target.node, this->rt.driver_target.activation, this->rt.activation); spa_list_remove(&this->rt.target.link); @@ -137,37 +157,41 @@ } static int -do_node_add(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) +do_node_add(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; struct pw_impl_node *driver = this->driver_node; - this->added = true; - if (this->source.loop == NULL) { - struct spa_system *data_system = this->context->data_system; + if (!this->added) { uint64_t dummy; int res; /* clear the eventfd in case it was written to while the node was stopped */ - res = spa_system_eventfd_read(data_system, this->source.fd, &dummy); + res = spa_system_eventfd_read(this->data_system, this->source.fd, &dummy); if (SPA_UNLIKELY(res != -EAGAIN && res != 0)) pw_log_warn("%p: read failed %m", this); - spa_loop_add_source(loop, &this->source); + this->added = true; + /* remote nodes have their source added in client-node instead */ + if (!this->remote) + spa_loop_add_source(loop, &this->source); add_node(this, driver); } 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) +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); + if (this->added) { + if (!this->remote) + spa_loop_remove_source(loop, &this->source); remove_node(this); + this->added = false; } - this->added = false; return 0; } @@ -572,8 +596,12 @@ { struct resource_data *data = object; struct pw_impl_node *node = data->node; + uint32_t id = SPA_NODE_COMMAND_ID(command); - switch (SPA_NODE_COMMAND_ID(command)) { + pw_log_debug("%p: got command %d (%s)", node, id, + spa_debug_type_find_name(spa_type_node_command_id, id)); + + switch (id) { case SPA_NODE_COMMAND_Suspend: suspend_node(node); break; @@ -808,7 +836,7 @@ node->target_rate = node->rt.position->clock.target_rate; node->target_quantum = node->rt.position->clock.target_duration; - if (node->source.loop != NULL) { + if (node->added) { remove_node(node); add_node(node, driver); } @@ -841,18 +869,6 @@ remove_segment_owner(old, node->info.id); - if (old != node && old->driving && driver->info.state < PW_NODE_STATE_RUNNING) { - pw_log_info("move quantum:%"PRIu64"->%"PRIu64" rate:%d->%d (%s-%d -> %s-%d)", - driver->target_quantum, - old->target_quantum, - driver->target_rate.denom, - old->target_rate.denom, - old->name, old->info.id, - driver->name, driver->info.id); - driver->target_rate = old->target_rate; - driver->target_quantum = old->target_quantum; - driver->target_pending = true; - } was_driving = node->driving; node->driving = node->driver && driver == node; @@ -924,6 +940,8 @@ else spa_list_remove(&node->driver_link); } + if (driver && node->driver_node == node) + node->driving = true; recalc_reason = "driver changed"; } @@ -1090,20 +1108,26 @@ } } -static inline int resume_node(struct pw_impl_node *this, int status) +static inline uint64_t get_time_ns(struct spa_system *system) { - struct pw_node_target *t; struct timespec ts; - struct pw_node_activation *activation = this->rt.activation; - struct spa_system *data_system = this->context->data_system; - uint64_t nsec; + spa_system_clock_gettime(system, CLOCK_MONOTONIC, &ts); + return SPA_TIMESPEC_TO_NSEC(&ts); +} - spa_system_clock_gettime(data_system, CLOCK_MONOTONIC, &ts); - nsec = SPA_TIMESPEC_TO_NSEC(&ts); - activation->status = PW_NODE_ACTIVATION_FINISHED; - activation->finish_time = nsec; +static inline void node_trigger(struct pw_impl_node *this) +{ + pw_log_trace_fp("node %p %s", this, this->name); + if (SPA_UNLIKELY(spa_system_eventfd_write(this->data_system, this->source.fd, 1) < 0)) + pw_log_warn("node %p: write failed %m", this); +} - pw_log_trace_fp("%p: trigger peers %"PRIu64, this, nsec); +/* called from data-loop when all the targets of a node need to be triggered */ +static inline int trigger_targets(struct pw_impl_node *this, int status, uint64_t nsec) +{ + struct pw_node_target *t; + + pw_log_trace_fp("%p: %s trigger targets %"PRIu64, this, this->name, nsec); spa_list_for_each(t, &this->rt.target_list, link) { struct pw_node_activation *a = t->activation; @@ -1115,88 +1139,88 @@ if (pw_node_activation_state_dec(state, 1)) { a->status = PW_NODE_ACTIVATION_TRIGGERED; a->signal_time = nsec; - t->signal_func(t->data); + if (SPA_UNLIKELY(spa_system_eventfd_write(t->system, t->fd, 1) < 0)) + pw_log_warn("node %p: write failed %m", this); } } return 0; } -static inline void calculate_stats(struct pw_impl_node *this, struct pw_node_activation *a) -{ - if (SPA_LIKELY(a->signal_time > a->prev_signal_time)) { - uint64_t process_time = a->finish_time - a->signal_time; - uint64_t period_time = a->signal_time - a->prev_signal_time; - float load = (float) process_time / (float) period_time; - a->cpu_load0 = (a->cpu_load0 + load) / 2.0f; - a->cpu_load1 = (a->cpu_load1 * 7.0f + load) / 8.0f; - a->cpu_load2 = (a->cpu_load2 * 31.0f + load) / 32.0f; - } -} - +/* The main processing entry point of a node. This is called from the data-loop and usually + * as a result of signaling the eventfd of the node. + * + * This code runs on the client and the server, depending on where the node is. + */ static inline int process_node(void *data) { struct pw_impl_node *this = data; - struct timespec ts; - struct pw_impl_port *p; + struct pw_impl_port *p; struct pw_node_activation *a = this->rt.activation; - struct spa_system *data_system = this->context->data_system; + struct spa_system *data_system = this->data_system; int status; + uint64_t nsec; - spa_system_clock_gettime(data_system, CLOCK_MONOTONIC, &ts); + nsec = get_time_ns(data_system); + pw_log_trace_fp("%p: %s process remote:%u exported:%u %"PRIu64, + this, this->name, this->remote, this->exported, nsec); a->status = PW_NODE_ACTIVATION_AWAKE; - a->awake_time = SPA_TIMESPEC_TO_NSEC(&ts); - - pw_log_trace_fp("%p: %s process %"PRIu64, this, this->name, a->awake_time); + a->awake_time = nsec; /* when transport sync is not supported, just clear the flag */ - if (!this->transport_sync) + if (SPA_UNLIKELY(!this->transport_sync)) a->pending_sync = false; - if (this->added) { + if (SPA_LIKELY(this->added)) { + /* process input mixers */ spa_list_for_each(p, &this->rt.input_mix, rt.node_link) - spa_node_process(p->mix); + spa_node_process_fast(p->mix); - status = spa_node_process(this->node); + /* process the actual node */ + status = spa_node_process_fast(this->node); + /* process output tee */ if (status & SPA_STATUS_HAVE_DATA) { spa_list_for_each(p, &this->rt.output_mix, rt.node_link) - spa_node_process(p->mix); + spa_node_process_fast(p->mix); } } else { /* This can happen when we deactivated the node but some links are * still not shut down. We simply don't schedule the node and make - * sure we trigger the peers in resume_node below. */ + * sure we trigger the peers in trigger_targets below. */ pw_log_debug("%p: scheduling non-active node %s", this, this->name); status = SPA_STATUS_HAVE_DATA; } a->state0.status = status; - if (SPA_UNLIKELY(this == this->driver_node && !this->exported)) { - spa_system_clock_gettime(data_system, CLOCK_MONOTONIC, &ts); - a->status = PW_NODE_ACTIVATION_FINISHED; - a->signal_time = a->finish_time; - a->finish_time = SPA_TIMESPEC_TO_NSEC(&ts); - - /* calculate CPU time */ - calculate_stats(this, a); + nsec = get_time_ns(data_system); - pw_log_trace_fp("%p: graph completed wait:%"PRIu64" run:%"PRIu64 - " busy:%"PRIu64" period:%"PRIu64" cpu:%f:%f:%f", this, - a->awake_time - a->signal_time, - a->finish_time - a->awake_time, - a->finish_time - a->signal_time, - a->signal_time - a->prev_signal_time, - a->cpu_load0, a->cpu_load1, a->cpu_load2); + pw_log_trace_fp("%p: finished %"PRIu64, this, nsec); + a->status = PW_NODE_ACTIVATION_FINISHED; + a->finish_time = nsec; - pw_context_driver_emit_complete(this->context, this); + /* we don't need to trigger targets when the node was driving the + * graph because that means we finished the graph. Also don't schedule + * peers when the node returns OK, because that means the resume will + * happen asynchronously later (unimplemented though). */ + if (SPA_LIKELY(!this->driving && status != SPA_STATUS_OK)) + trigger_targets(this, status, nsec); - } else if (status == SPA_STATUS_OK) { - pw_log_trace_fp("%p: async continue", this); - } else { - resume_node(this, status); - } - if (status & SPA_STATUS_DRAINED) { + if (SPA_UNLIKELY(status & SPA_STATUS_DRAINED)) pw_context_driver_emit_drained(this->context, this); + + return status; +} + +int pw_impl_node_trigger(struct pw_impl_node *node) +{ + struct pw_node_activation *a = node->rt.activation; + struct pw_node_activation_state *state = &a->state0; + + if (pw_node_activation_state_dec(state, 1)) { + uint64_t nsec = get_time_ns(node->data_system); + a->status = PW_NODE_ACTIVATION_TRIGGERED; + a->signal_time = nsec; + node_trigger(node); } return 0; } @@ -1204,24 +1228,23 @@ static void node_on_fd_events(struct spa_source *source) { struct pw_impl_node *this = source->data; - struct spa_system *data_system = this->context->data_system; if (SPA_UNLIKELY(source->rmask & (SPA_IO_ERR | SPA_IO_HUP))) { pw_log_warn("%p: got socket error %08x", this, source->rmask); return; } - if (SPA_LIKELY(source->rmask & SPA_IO_IN)) { uint64_t cmd; - if (SPA_UNLIKELY(spa_system_eventfd_read(data_system, this->source.fd, &cmd) < 0)) + if (SPA_UNLIKELY(spa_system_eventfd_read(this->data_system, this->source.fd, &cmd) < 0)) pw_log_warn("%p: read failed %m", this); else if (SPA_UNLIKELY(cmd > 1)) pw_log_info("(%s-%u) client missed %"PRIu64" wakeups", this->name, this->info.id, cmd - 1); - pw_log_trace_fp("%p: got process", this); - this->rt.target.signal_func(this->rt.target.data); + pw_log_trace_fp("%p: remote:%u exported:%u %s got process", this, this->remote, + this->exported, this->name); + process_node(this); } } @@ -1262,7 +1285,6 @@ struct impl *impl; struct pw_impl_node *this; size_t size; - struct spa_system *data_system = context->data_system; int res; impl = calloc(1, sizeof(struct impl) + user_data_size); @@ -1278,6 +1300,9 @@ this->context = context; this->name = strdup("node"); + this->data_loop = pw_context_get_data_loop(context)->loop; + this->data_system = this->data_loop->system; + if (user_data_size > 0) this->user_data = SPA_PTROFF(impl, sizeof(struct impl), void); @@ -1290,7 +1315,9 @@ this->properties = properties; - if ((res = spa_system_eventfd_create(data_system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0) + /* the eventfd used to signal the node */ + if ((res = spa_system_eventfd_create(this->data_system, + SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0) goto error_clean; pw_log_debug("%p: new fd:%d", this, res); @@ -1316,9 +1343,8 @@ impl->work = pw_context_get_work_queue(this->context); impl->pending_id = SPA_ID_INVALID; - this->data_loop = context->data_loop; - spa_list_init(&this->follower_list); + spa_list_init(&this->peer_list); spa_hook_list_init(&this->listener_list); @@ -1338,9 +1364,8 @@ this->rt.activation = this->activation->map->ptr; this->rt.target.activation = this->rt.activation; this->rt.target.node = this; - this->rt.target.signal_func = process_node; - this->rt.target.data = this; - this->rt.driver_target.signal_func = process_node; + this->rt.target.system = this->data_system; + this->rt.target.fd = this->source.fd; reset_position(this, &this->rt.activation->position); this->rt.activation->sync_timeout = DEFAULT_SYNC_TIMEOUT; @@ -1353,7 +1378,7 @@ this->driver_node = this; spa_list_append(&this->follower_list, &this->follower_link); - this->driving = true; + this->driving = this->driver; return this; @@ -1361,7 +1386,7 @@ if (this->activation) pw_memblock_unref(this->activation); if (this->source.fd != -1) - spa_system_close(this->context->data_system, this->source.fd); + spa_system_close(this->data_system, this->source.fd); free(impl); error_exit: pw_properties_free(properties); @@ -1544,10 +1569,12 @@ { struct pw_impl_node *node = data; struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this); + uint32_t id = SPA_NODE_EVENT_ID(event); - pw_log_trace("%p: event %d", node, SPA_EVENT_TYPE(event)); + pw_log_debug("%p: event %d (%s)", node, id, + spa_debug_type_find_name(spa_type_node_event_id, id)); - switch (SPA_NODE_EVENT_ID(event)) { + switch (id) { case SPA_NODE_EVENT_Error: impl->last_error = -EFAULT; node_update_state(node, PW_NODE_STATE_ERROR, @@ -1575,11 +1602,25 @@ .event = node_event, }; +static inline void calculate_stats(struct pw_impl_node *this, struct pw_node_activation *a) +{ + uint64_t signal_time = a->signal_time; + uint64_t prev_signal_time = a->prev_signal_time; + if (SPA_LIKELY(signal_time > prev_signal_time)) { + uint64_t process_time = a->finish_time - a->signal_time; + uint64_t period_time = signal_time - prev_signal_time; + float load = (float) process_time / (float) period_time; + a->cpu_load0 = (a->cpu_load0 + load) / 2.0f; + a->cpu_load1 = (a->cpu_load1 * 7.0f + load) / 8.0f; + a->cpu_load2 = (a->cpu_load2 * 31.0f + load) / 32.0f; + } +} + #define SYNC_CHECK 0 #define SYNC_START 1 #define SYNC_STOP 2 -static int check_updates(struct pw_impl_node *node, uint32_t *reposition_owner) +static inline int check_updates(struct pw_impl_node *node, uint32_t *reposition_owner) { int res = SYNC_CHECK; struct pw_node_activation *a = node->rt.activation; @@ -1644,11 +1685,11 @@ } } -static void update_position(struct pw_impl_node *node, int all_ready) +static inline void update_position(struct pw_impl_node *node, int all_ready) { struct pw_node_activation *a = node->rt.activation; - if (a->position.state == SPA_IO_POSITION_STATE_STARTING) { + if (SPA_UNLIKELY(a->position.state == SPA_IO_POSITION_STATE_STARTING)) { if (!all_ready && --a->sync_left == 0) { pw_log_warn("(%s-%u) sync timeout, going to RUNNING", node->name, node->info.id); @@ -1659,21 +1700,28 @@ if (all_ready) a->position.state = SPA_IO_POSITION_STATE_RUNNING; } - if (a->position.state != SPA_IO_POSITION_STATE_RUNNING) + if (SPA_LIKELY(a->position.state != SPA_IO_POSITION_STATE_RUNNING)) a->position.offset += a->position.clock.duration; } +/* Called from the data-loop and it is the starting point for driver nodes. + * Most of the logic here is to check for reposition updates and transport changes. + */ static int node_ready(void *data, int status) { struct pw_impl_node *node = data, *reposition_node = NULL; + struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this); struct pw_impl_node *driver = node->driver_node; + struct pw_node_activation *a = node->rt.activation; + struct spa_system *data_system = node->data_system; struct pw_node_target *t; struct pw_impl_port *p; + uint64_t nsec; 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) { + if (SPA_UNLIKELY(!node->added)) { /* This can happen when we are stopping a node and removed it from the * graph but we still have not completed the Pause/Suspend command on * the node. In that case, the node might still emit ready events, @@ -1682,8 +1730,9 @@ return -EIO; } - if (SPA_UNLIKELY(node == driver)) { - struct pw_node_activation *a = node->rt.activation; + nsec = get_time_ns(data_system); + + if (SPA_LIKELY(node == driver)) { struct pw_node_activation_state *state = &a->state0; int sync_type, all_ready, update_sync, target_sync; uint32_t owner2, reposition_owner; @@ -1698,15 +1747,38 @@ state->pending, state->required); dump_states(node); } - node->rt.target.signal_func(node->rt.target.data); + node_trigger(node); + } else { + uint64_t signal_time = a->signal_time; + /* old nodes set the TRIGGERED status on node_ready, patch this + * up here to avoid errors in pw-top */ + a->status = PW_NODE_ACTIVATION_FINISHED; + a->signal_time = a->prev_signal_time; + a->prev_signal_time = impl->prev_signal_time; + + /* calculate CPU time */ + calculate_stats(node, a); + + pw_log_trace_fp("%p: graph completed wait:%"PRIu64" run:%"PRIu64 + " busy:%"PRIu64" period:%"PRIu64" cpu:%f:%f:%f", node, + a->awake_time - a->signal_time, + a->finish_time - a->awake_time, + a->finish_time - a->signal_time, + a->signal_time - a->prev_signal_time, + a->cpu_load0, a->cpu_load1, a->cpu_load2); + + pw_context_driver_emit_complete(node->context, node); + + a->prev_signal_time = a->signal_time; + a->signal_time = signal_time; } /* This update is done too late, the driver should do this * before calling the ready callback so that it can use the new target * duration and rate to schedule the next update. We do this here to * help drivers that don't support this yet */ - if (node->rt.position->clock.duration != node->rt.position->clock.target_duration || - node->rt.position->clock.rate.denom != node->rt.position->clock.target_rate.denom) { + if (SPA_UNLIKELY(node->rt.position->clock.duration != node->rt.position->clock.target_duration || + node->rt.position->clock.rate.denom != node->rt.position->clock.target_rate.denom)) { pw_log_warn("driver %s did not update duration/rate", node->name); node->rt.position->clock.duration = node->rt.position->clock.target_duration; node->rt.position->clock.rate = node->rt.position->clock.target_rate; @@ -1749,7 +1821,15 @@ all_ready &= ta->pending_sync == false; } } + + a->status = PW_NODE_ACTIVATION_TRIGGERED; + /* remote nodes set the signal_time before writing the ready + * eventfd */ + if (!node->remote) + a->signal_time = nsec; + impl->prev_signal_time = a->prev_signal_time; a->prev_signal_time = a->signal_time; + a->sync_timeout = SPA_MIN(min_timeout, DEFAULT_SYNC_TIMEOUT); if (SPA_UNLIKELY(reposition_node)) { @@ -1764,24 +1844,25 @@ pw_context_driver_emit_start(node->context, node); } + /* this should not happen, driver nodes that are not currently driving + * should not emit the ready callback */ if (SPA_UNLIKELY(node->driver && !node->driving)) return 0; - if (!node->driver) { - struct timespec ts; - struct pw_node_activation *a = node->rt.activation; - struct spa_system *data_system = node->context->data_system; - - spa_system_clock_gettime(data_system, CLOCK_MONOTONIC, &ts); - a->status = PW_NODE_ACTIVATION_AWAKE; - a->signal_time = a->awake_time = SPA_TIMESPEC_TO_NSEC(&ts); + if (SPA_UNLIKELY(!node->driver)) { + /* legacy, nodes should directly resume the graph by calling + * the peer eventfd directly, node_ready is only for drivers */ + a->status = PW_NODE_ACTIVATION_FINISHED; + a->finish_time = nsec; } - - if (status & SPA_STATUS_HAVE_DATA) { + if (!node->remote && (status & SPA_STATUS_HAVE_DATA)) { + /* remote nodes have done the output mix already before + * they wrote the ready eventfd */ spa_list_for_each(p, &node->rt.output_mix, rt.node_link) - spa_node_process(p->mix); + spa_node_process_fast(p->mix); } - return resume_node(node, status); + /* now signal all the nodes we drive */ + return trigger_targets(node, status, nsec); } static int node_reuse_buffer(void *data, uint32_t port_id, uint32_t buffer_id) @@ -1966,7 +2047,7 @@ clear_info(node); - spa_system_close(context->data_system, node->source.fd); + spa_system_close(node->data_system, node->source.fd); free(impl); }
View file
pipewire-0.3.70.tar.gz/src/pipewire/impl-port.c -> pipewire-0.3.71.tar.gz/src/pipewire/impl-port.c
Changed
@@ -914,6 +914,7 @@ PW_KEY_PORT_CONTROL, PW_KEY_PORT_ALIAS, PW_KEY_PORT_EXTRA, + PW_KEY_PORT_IGNORE_LATENCY, NULL }; @@ -992,6 +993,8 @@ is_monitor = pw_properties_get_bool(port->properties, PW_KEY_PORT_MONITOR, false); + port->ignore_latency = pw_properties_get_bool(port->properties, PW_KEY_PORT_IGNORE_LATENCY, false); + is_control = PW_IMPL_PORT_IS_CONTROL(port); if (is_control) { dir = port->direction == PW_DIRECTION_INPUT ? "control" : "notify"; @@ -1440,6 +1443,11 @@ if (port->direction == PW_DIRECTION_OUTPUT) { spa_list_for_each(l, &port->links, output_link) { other = l->input; + if (other->ignore_latency) { + pw_log_debug("port %d: peer %d: peer latency ignored", + port->info.id, other->info.id); + continue; + } spa_latency_info_combine(&latency, &other->latencyother->direction); pw_log_debug("port %d: peer %d: latency %f-%f %d-%d %"PRIu64"-%"PRIu64, port->info.id, other->info.id, @@ -1450,6 +1458,11 @@ } else { spa_list_for_each(l, &port->links, input_link) { other = l->output; + if (other->ignore_latency) { + pw_log_debug("port %d: peer %d: peer latency ignored", + port->info.id, other->info.id); + continue; + } spa_latency_info_combine(&latency, &other->latencyother->direction); pw_log_debug("port %d: peer %d: latency %f-%f %d-%d %"PRIu64"-%"PRIu64, port->info.id, other->info.id,
View file
pipewire-0.3.70.tar.gz/src/pipewire/keys.h -> pipewire-0.3.71.tar.gz/src/pipewire/keys.h
Changed
@@ -197,6 +197,7 @@ #define PW_KEY_PORT_EXTRA "port.extra" /**< api specific extra port info, API name * should be prefixed. "jack:flags:56" */ #define PW_KEY_PORT_PASSIVE "port.passive" /**< the ports wants passive links, since 0.3.67 */ +#define PW_KEY_PORT_IGNORE_LATENCY "port.ignore-latency" /**< latency ignored by peers, since 0.3.71 */ /** link properties */ #define PW_KEY_LINK_ID "link.id" /**< a link id */ @@ -274,7 +275,10 @@ #define PW_KEY_STREAM_LATENCY_MAX "stream.latency.max" /**< The maximum latency of the stream */ #define PW_KEY_STREAM_MONITOR "stream.monitor" /**< Indicates that the stream is monitoring * and might select a less accurate but faster - * conversion algorithm. */ + * conversion algorithm. Monitor streams are also + * ignored when calculating the latency of their peer + * ports (since 0.3.71). + */ #define PW_KEY_STREAM_DONT_REMIX "stream.dont-remix" /**< don't remix channels */ #define PW_KEY_STREAM_CAPTURE_SINK "stream.capture.sink" /**< Try to capture the sink output instead of * source output */
View file
pipewire-0.3.70.tar.gz/src/pipewire/loop.c -> pipewire-0.3.71.tar.gz/src/pipewire/loop.c
Changed
@@ -109,6 +109,12 @@ goto error_unload_loop; } this->control = iface; + if (!spa_interface_callback_check(&this->control->iface, + struct spa_loop_control_methods, iterate, 0)) { + res = -EINVAL; + pw_log_error("%p: loop does not support iterate", this); + goto error_unload_loop; + } if ((res = spa_handle_get_interface(impl->loop_handle, SPA_TYPE_INTERFACE_LoopUtils,
View file
pipewire-0.3.70.tar.gz/src/pipewire/loop.h -> pipewire-0.3.71.tar.gz/src/pipewire/loop.h
Changed
@@ -45,8 +45,8 @@ #define pw_loop_get_fd(l) spa_loop_control_get_fd((l)->control) #define pw_loop_add_hook(l,...) spa_loop_control_add_hook((l)->control,__VA_ARGS__) #define pw_loop_enter(l) spa_loop_control_enter((l)->control) -#define pw_loop_iterate(l,...) spa_loop_control_iterate((l)->control,__VA_ARGS__) #define pw_loop_leave(l) spa_loop_control_leave((l)->control) +#define pw_loop_iterate(l,...) spa_loop_control_iterate_fast((l)->control,__VA_ARGS__) #define pw_loop_add_io(l,...) spa_loop_utils_add_io((l)->utils,__VA_ARGS__) #define pw_loop_update_io(l,...) spa_loop_utils_update_io((l)->utils,__VA_ARGS__)
View file
pipewire-0.3.70.tar.gz/src/pipewire/private.h -> pipewire-0.3.71.tar.gz/src/pipewire/private.h
Changed
@@ -389,10 +389,10 @@ #define ensure_loop(loop,...) ({ \ int res = pw_loop_check(loop); \ if (res != 1) { \ - pw_log_warn("%s called from wrong context, check thread and locking: %s", \ - __func__, spa_strerror(res)); \ + pw_log_warn("%s called from wrong context, check thread and locking: %s", \ + __func__, res < 0 ? spa_strerror(res) : "Not in loop"); \ fprintf(stderr, "*** %s called from wrong context, check thread and locking: %s\n",\ - __func__, spa_strerror(res)); \ + __func__, res < 0 ? spa_strerror(res) : "Not in loop"); \ /* __VA_ARGS__ */ \ } \ }) @@ -475,7 +475,6 @@ struct spa_thread_utils *thread_utils; struct pw_loop *main_loop; /**< main loop for control */ struct pw_loop *data_loop; /**< data loop for data passing */ - struct pw_data_loop *data_loop_impl; struct spa_system *data_system; /**< data system for data passing */ struct pw_work_queue *work_queue; /**< work queue */ @@ -587,8 +586,8 @@ struct spa_list link; struct pw_impl_node *node; struct pw_node_activation *activation; - int (*signal_func) (void *data); - void *data; + struct spa_system *system; + int fd; unsigned int active:1; }; @@ -739,6 +738,8 @@ struct spa_list sort_link; /**< link used to sort nodes */ + struct spa_list peer_list; /* list of peers */ + struct spa_node *node; /**< SPA node implementation */ struct spa_hook listener; @@ -750,6 +751,7 @@ struct spa_hook_list listener_list; struct pw_loop *data_loop; /**< the data loop for this node */ + struct spa_system *data_system; struct spa_fraction latency; /**< requested latency */ struct spa_fraction max_latency; /**< maximum latency */ @@ -893,6 +895,7 @@ struct spa_latency_info latency2; /**< latencies */ unsigned int have_latency_param:1; + unsigned int ignore_latency:1; void *owner_data; /**< extra owner data */ void *user_data; /**< extra user data */ @@ -908,6 +911,14 @@ unsigned int valid:1; }; +struct pw_node_peer { + int ref; + int active_count; + struct spa_list link; /**< link in peer list */ + struct pw_impl_node *output; /**< the output node */ + struct pw_node_target target; /**< target of the input node */ +}; + #define pw_impl_link_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_impl_link_events, m, v, ##__VA_ARGS__) #define pw_impl_link_emit_destroy(l) pw_impl_link_emit(l, destroy, 0) #define pw_impl_link_emit_free(l) pw_impl_link_emit(l, free, 0) @@ -939,10 +950,11 @@ struct pw_control_link control; struct pw_control_link notify; + struct pw_node_peer *peer; + struct { struct pw_impl_port_mix out_mix; /**< port added to the output mixer */ struct pw_impl_port_mix in_mix; /**< port added to the input mixer */ - struct pw_node_target target; /**< target to trigger the input node */ } rt; void *user_data; @@ -1076,12 +1088,14 @@ * CONFIGURE state and higher */ enum pw_stream_state state; /**< stream state */ char *error; /**< error reason when state is in error */ + int error_res; /**< error code when in error */ struct spa_hook_list listener_list; struct pw_proxy *proxy; struct spa_hook proxy_listener; + struct pw_impl_node *node; struct spa_hook node_listener; struct spa_list controls; @@ -1112,12 +1126,16 @@ * CONFIGURE state and higher */ enum pw_filter_state state; /**< filter state */ char *error; /**< error reason when state is in error */ + int error_res; /**< error code when in error */ struct spa_hook_list listener_list; struct pw_proxy *proxy; struct spa_hook proxy_listener; + struct pw_impl_node *node; + struct spa_hook node_listener; + struct spa_list controls; }; @@ -1263,6 +1281,8 @@ int pw_impl_node_set_driver(struct pw_impl_node *node, struct pw_impl_node *driver); +int pw_impl_node_trigger(struct pw_impl_node *node); + /** Prepare a link * Starts the negotiation of formats and buffers on \a link */ int pw_impl_link_prepare(struct pw_impl_link *link);
View file
pipewire-0.3.70.tar.gz/src/pipewire/properties.c -> pipewire-0.3.71.tar.gz/src/pipewire/properties.c
Changed
@@ -788,6 +788,9 @@ if ((len = spa_json_next(&sub, &value)) < 0) break; + if (!spa_json_is_container(value, len)) + len = value ? strlen(value) : 0; + dump(c, c->indent, &sub, value, len); count++; }
View file
pipewire-0.3.70.tar.gz/src/pipewire/stream.c -> pipewire-0.3.71.tar.gz/src/pipewire/stream.c
Changed
@@ -85,11 +85,12 @@ struct pw_context *context; struct spa_hook context_listener; + struct pw_loop *main_loop; + struct pw_loop *data_loop; + enum spa_direction direction; enum pw_stream_flags flags; - struct pw_impl_node *node; - struct spa_node impl_node; struct spa_node_methods node_methods; struct spa_hook_list hooks; @@ -351,26 +352,28 @@ queue->incount = queue->outcount; } -static bool stream_set_state(struct pw_stream *stream, enum pw_stream_state state, const char *error) +static bool stream_set_state(struct pw_stream *stream, enum pw_stream_state state, + int res, const char *error) { enum pw_stream_state old = stream->state; - bool res = old != state; + bool changed = old != state; - if (res) { + if (changed) { free(stream->error); stream->error = error ? strdup(error) : NULL; + stream->error_res = res; - pw_log_debug("%p: update state from %s -> %s (%s)", stream, + pw_log_debug("%p: update state from %s -> %s (%d) %s", stream, pw_stream_state_as_string(old), - pw_stream_state_as_string(state), stream->error); + pw_stream_state_as_string(state), res, stream->error); if (state == PW_STREAM_STATE_ERROR) - pw_log_error("%p: error %s", stream, error); + pw_log_error("%p: error (%d) %s", stream, res, error); stream->state = state; pw_stream_emit_state_changed(stream, old, state, error); } - return res; + return changed; } static struct buffer *get_buffer(struct pw_stream *stream, uint32_t id) @@ -426,7 +429,7 @@ if (impl->process_rt) spa_callbacks_call(&impl->rt_callbacks, struct pw_stream_events, process, 0); else - pw_loop_invoke(impl->context->main_loop, + pw_loop_invoke(impl->main_loop, do_call_process, 1, NULL, 0, false, impl); } @@ -443,7 +446,7 @@ static void call_drained(struct stream *impl) { - pw_loop_invoke(impl->context->main_loop, + pw_loop_invoke(impl->main_loop, do_call_drained, 1, NULL, 0, false, impl); } @@ -460,7 +463,7 @@ static void call_trigger_done(struct stream *impl) { - pw_loop_invoke(impl->context->main_loop, + pw_loop_invoke(impl->main_loop, do_call_trigger_done, 1, NULL, 0, false, impl); } @@ -494,7 +497,7 @@ else impl->position = NULL; - pw_loop_invoke(impl->context->data_loop, + pw_loop_invoke(impl->data_loop, do_set_position, 1, NULL, 0, true, impl); break; default: @@ -607,12 +610,12 @@ case SPA_NODE_COMMAND_Suspend: case SPA_NODE_COMMAND_Flush: case SPA_NODE_COMMAND_Pause: - pw_loop_invoke(impl->context->main_loop, + pw_loop_invoke(impl->main_loop, NULL, 0, NULL, 0, false, impl); if (stream->state == PW_STREAM_STATE_STREAMING) { pw_log_debug("%p: pause", stream); - stream_set_state(stream, PW_STREAM_STATE_PAUSED, NULL); + stream_set_state(stream, PW_STREAM_STATE_PAUSED, 0, NULL); } break; case SPA_NODE_COMMAND_Start: @@ -628,7 +631,7 @@ call_process(impl); } - stream_set_state(stream, PW_STREAM_STATE_STREAMING, NULL); + stream_set_state(stream, PW_STREAM_STATE_STREAMING, 0, NULL); } break; default: @@ -883,7 +886,7 @@ pw_stream_emit_param_changed(stream, id, param); if (stream->state == PW_STREAM_STATE_ERROR) - return -EIO; + return stream->error_res; emit_node_info(impl, false); emit_port_info(impl, false); @@ -1119,7 +1122,7 @@ pw_log_debug("%p: removed", stream); spa_hook_remove(&stream->proxy_listener); stream->node_id = SPA_ID_INVALID; - stream_set_state(stream, PW_STREAM_STATE_UNCONNECTED, NULL); + stream_set_state(stream, PW_STREAM_STATE_UNCONNECTED, 0, NULL); } static void proxy_destroy(void *_data) @@ -1145,7 +1148,7 @@ stream->node_id = global_id; if (props) pw_properties_update(stream->properties, props); - stream_set_state(stream, PW_STREAM_STATE_PAUSED, NULL); + stream_set_state(stream, PW_STREAM_STATE_PAUSED, 0, NULL); } static const struct pw_proxy_events proxy_events = { @@ -1339,15 +1342,13 @@ static void node_event_destroy(void *data) { struct pw_stream *stream = data; - struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); spa_hook_remove(&stream->node_listener); - impl->node = NULL; + stream->node = NULL; } static void node_event_info(void *data, const struct pw_node_info *info) { struct pw_stream *stream = data; - struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); uint32_t i; if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) { @@ -1355,7 +1356,7 @@ switch (info->paramsi.id) { case SPA_PARAM_PropInfo: case SPA_PARAM_Props: - pw_impl_node_for_each_param(impl->node, + pw_impl_node_for_each_param(stream->node, 0, info->paramsi.id, 0, UINT32_MAX, NULL, @@ -1383,7 +1384,7 @@ id, seq, res, spa_strerror(res), message); if (id == PW_ID_CORE && res == -EPIPE) { - stream_set_state(stream, PW_STREAM_STATE_UNCONNECTED, message); + stream_set_state(stream, PW_STREAM_STATE_UNCONNECTED, res, message); } } @@ -1395,7 +1396,7 @@ static void context_drained(void *data, struct pw_impl_node *node) { struct stream *impl = data; - if (impl->node != node) + if (impl->this.node != node) return; if (impl->draining && impl->drained) { impl->draining = false; @@ -1448,6 +1449,7 @@ res = -errno; goto error_properties; } + impl->main_loop = pw_context_get_main_loop(context); this = &impl->this; pw_log_debug("%p: new \"%s\"", impl, name); @@ -1622,16 +1624,16 @@ impl->disconnecting = true; - if (impl->node) - pw_impl_node_set_active(impl->node, false); + if (stream->node) + pw_impl_node_set_active(stream->node, false); if (stream->proxy) { pw_proxy_destroy(stream->proxy); stream->proxy = NULL; } - if (impl->node) - pw_impl_node_destroy(impl->node); + if (stream->node) + pw_impl_node_destroy(stream->node); if (impl->disconnect_core) { impl->disconnect_core = false; @@ -1649,7 +1651,7 @@ struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); struct control *c; - ensure_loop(impl->context->main_loop, return); + ensure_loop(impl->main_loop, return); pw_log_debug("%p: destroy", stream); @@ -1706,7 +1708,7 @@ { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); - ensure_loop(impl->context->main_loop); + ensure_loop(impl->main_loop); spa_hook_list_append(&stream->listener_list, listener, events, data); @@ -1744,7 +1746,7 @@ int changed, res = 0; struct match match; - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); changed = pw_properties_update(stream->properties, dict); if (!changed) @@ -1754,8 +1756,8 @@ pw_context_conf_section_match_rules(impl->context, "stream.rules", &stream->properties->dict, execute_match, &match); - if (impl->node) - res = pw_impl_node_update_properties(impl->node, + if (stream->node) + res = pw_impl_node_update_properties(stream->node, match.count == 0 ? dict : &stream->properties->dict); @@ -1855,11 +1857,11 @@ uint32_t i; int res; - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); pw_log_debug("%p: connect target:%d", stream, target_id); - if (impl->node != NULL || stream->state != PW_STREAM_STATE_UNCONNECTED) + if (stream->node != NULL || stream->state != PW_STREAM_STATE_UNCONNECTED) return -EBUSY; impl->direction = @@ -1943,7 +1945,7 @@ impl->driving = false; impl->trigger = false; impl->using_trigger = false; - stream_set_state(stream, PW_STREAM_STATE_CONNECTING, NULL); + stream_set_state(stream, PW_STREAM_STATE_CONNECTING, 0, NULL); if ((str = getenv("PIPEWIRE_NODE")) != NULL) pw_properties_set(stream->properties, PW_KEY_TARGET_OBJECT, str); @@ -2012,6 +2014,7 @@ pw_properties_parse_bool(str)) { pw_properties_set(props, "resample.peaks", "true"); pw_properties_set(props, "channelmix.normalize", "true"); + pw_properties_set(props, PW_KEY_PORT_IGNORE_LATENCY, "true"); } if (impl->media_type == SPA_MEDIA_TYPE_audio) { @@ -2024,32 +2027,34 @@ pw_properties_setf(props, "adapt.follower.spa-node", "pointer:%p", &impl->impl_node); pw_properties_set(props, "object.register", "false"); - impl->node = pw_impl_factory_create_object(factory, + stream->node = pw_impl_factory_create_object(factory, NULL, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, props, 0); props = NULL; - if (impl->node == NULL) { + if (stream->node == NULL) { res = -errno; goto error_node; } } else { - impl->node = pw_context_create_node(impl->context, props, 0); + stream->node = pw_context_create_node(impl->context, props, 0); props = NULL; - if (impl->node == NULL) { + if (stream->node == NULL) { res = -errno; goto error_node; } - pw_impl_node_set_implementation(impl->node, &impl->impl_node); + pw_impl_node_set_implementation(stream->node, &impl->impl_node); } - pw_impl_node_set_active(impl->node, + pw_impl_node_set_active(stream->node, !SPA_FLAG_IS_SET(impl->flags, PW_STREAM_FLAG_INACTIVE)); - pw_log_debug("%p: export node %p", stream, impl->node); + impl->data_loop = stream->node->data_loop; + + pw_log_debug("%p: export node %p", stream, stream->node); stream->proxy = pw_core_export(stream->core, - PW_TYPE_INTERFACE_Node, NULL, impl->node, 0); + PW_TYPE_INTERFACE_Node, NULL, stream->node, 0); if (stream->proxy == NULL) { res = -errno; goto error_proxy; @@ -2057,7 +2062,7 @@ pw_proxy_add_listener(stream->proxy, &stream->proxy_listener, &proxy_events, stream); - pw_impl_node_add_listener(impl->node, &stream->node_listener, &node_events, stream); + pw_impl_node_add_listener(stream->node, &stream->node_listener, &node_events, stream); return 0; @@ -2086,7 +2091,7 @@ int pw_stream_disconnect(struct pw_stream *stream) { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); return stream_disconnect(impl); } @@ -2096,7 +2101,7 @@ { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); if (res < 0) { va_list args; @@ -2111,7 +2116,7 @@ if (stream->proxy) pw_proxy_error(stream->proxy, res, value); - stream_set_state(stream, PW_STREAM_STATE_ERROR, value); + stream_set_state(stream, PW_STREAM_STATE_ERROR, res, value); free(value); } @@ -2126,7 +2131,7 @@ struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); int res; - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); pw_log_debug("%p: update params", stream); if ((res = update_params(impl, SPA_ID_INVALID, params, n_params)) < 0) @@ -2142,7 +2147,7 @@ { int res = 0; impl->in_set_param++; - res = pw_impl_node_set_param(impl->node, id, 0, param); + res = pw_impl_node_set_param(impl->this.node, id, 0, param); impl->in_set_param--; return res; } @@ -2151,9 +2156,9 @@ int pw_stream_set_param(struct pw_stream *stream, uint32_t id, const struct spa_pod *param) { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); - if (impl->node == NULL) + if (stream->node == NULL) return -EIO; return stream_set_param(impl, id, param); @@ -2170,9 +2175,9 @@ struct spa_pod *pod; struct control *c; - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); - if (impl->node == NULL) + if (stream->node == NULL) return -EIO; va_start(varargs, values); @@ -2239,14 +2244,14 @@ { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); - ensure_loop(impl->context->main_loop, return -EIO); + ensure_loop(impl->main_loop, return -EIO); pw_log_debug("%p: active:%d", stream, active); - if (impl->node == NULL) + if (stream->node == NULL) return -EIO; - pw_impl_node_set_active(impl->node, active); + pw_impl_node_set_active(stream->node, active); if (!active || impl->drained) impl->drained = impl->draining = false; @@ -2360,7 +2365,7 @@ if (impl->direction == SPA_DIRECTION_OUTPUT && impl->driving && !impl->using_trigger) { pw_log_debug("deprecated: use pw_stream_trigger_process() to drive the stream."); - res = pw_loop_invoke(impl->context->data_loop, + res = pw_loop_invoke(impl->data_loop, do_trigger_deprecated, 1, NULL, 0, false, impl); } return res; @@ -2372,12 +2377,21 @@ { struct stream *impl = user_data; struct buffer *b; + struct queue *from, *to; pw_log_trace_fp("%p: flush", impl); + + if (impl->direction == SPA_DIRECTION_OUTPUT) { + from = &impl->queued; + to = &impl->dequeued; + } else { + from = &impl->dequeued; + to = &impl->queued; + } do { - b = queue_pop(impl, &impl->queued); + b = queue_pop(impl, from); if (b != NULL) - queue_push(impl, &impl->dequeued, b); + queue_push(impl, to, b); } while (b); @@ -2402,14 +2416,14 @@ { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); - if (impl->node == NULL) + if (stream->node == NULL) return -EIO; - pw_loop_invoke(impl->context->data_loop, + pw_loop_invoke(impl->data_loop, drain ? do_drain : do_flush, 1, NULL, 0, true, impl); if (!drain) - spa_node_send_command(impl->node->node, + spa_node_send_command(stream->node->node, &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Flush)); return 0; } @@ -2422,7 +2436,7 @@ } static int -do_trigger_process(struct spa_loop *loop, +do_trigger_driver(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) { struct stream *impl = user_data; @@ -2462,15 +2476,16 @@ /* flag to check for old or new behaviour */ impl->using_trigger = true; - if (!impl->driving && !impl->trigger) { - res = pw_loop_invoke(impl->context->main_loop, - do_trigger_request_process, 1, NULL, 0, false, impl); - } else { + if (impl->trigger) { + pw_impl_node_trigger(stream->node); + } else if (impl->driving) { if (!impl->process_rt) call_process(impl); - - res = pw_loop_invoke(impl->context->data_loop, - do_trigger_process, 1, NULL, 0, false, impl); + res = pw_loop_invoke(impl->data_loop, + do_trigger_driver, 1, NULL, 0, false, impl); + } else { + res = pw_loop_invoke(impl->main_loop, + do_trigger_request_process, 1, NULL, 0, false, impl); } return res; }
View file
pipewire-0.3.70.tar.gz/src/tools/pw-profiler.c -> pipewire-0.3.71.tar.gz/src/tools/pw-profiler.c
Changed
@@ -213,7 +213,7 @@ d4 > 0 ? d4 : 0, d5 > 0 ? d5 : 0, d6 > 0 ? d6 : 0, - (d5 > 0 && d4 > 0 && d5 > d4) ? d5 - d4 : 0, + (d5 > 0 && d4 >= 0 && d5 > d4) ? d5 - d4 : 0, (d6 > 0 && d5 > 0 && d6 > d5) ? d6 - d5 : 0, point->followeri.status); }
View file
pipewire-0.3.70.tar.gz/test/test-loop.c -> pipewire-0.3.71.tar.gz/test/test-loop.c
Changed
@@ -234,6 +234,10 @@ struct spa_hook hook; }; +static void dmsbd_before(void *data) +{ +} + static void dmsbd_after(void *data) { struct dmsbd_data *d = data; @@ -244,6 +248,7 @@ static const struct spa_loop_control_hooks dmsbd_hooks = { SPA_VERSION_LOOP_CONTROL_HOOKS, + .before = dmsbd_before, .after = dmsbd_after, };
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
.