From faa5984fcefa5e02cabb5a31b1912365350efaa3 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 1 Jun 2018 11:28:31 +0200 Subject: [PATCH 001/116] Initial import --- LICENSE | 504 +++++++++++++++++++ README.md | 2 + src/bitset.c | 64 +++ src/bitset.h | 34 ++ src/channelmap.c | 830 ++++++++++++++++++++++++++++++++ src/context.c | 453 +++++++++++++++++ src/core-format.c | 241 ++++++++++ src/core-format.h | 79 +++ src/direction.c | 47 ++ src/error.c | 74 +++ src/ext-device-manager.c | 105 ++++ src/ext-device-restore.c | 84 ++++ src/format.c | 679 ++++++++++++++++++++++++++ src/hashmap.c | 348 ++++++++++++++ src/hashmap.h | 105 ++++ src/internal.h | 306 ++++++++++++ src/introspect.c | 839 ++++++++++++++++++++++++++++++++ src/json.c | 648 +++++++++++++++++++++++++ src/json.h | 53 ++ src/mainloop-signal.c | 109 +++++ src/mainloop.c | 251 ++++++++++ src/map-file | 386 +++++++++++++++ src/meson.build | 47 ++ src/operation.c | 153 ++++++ src/pipewire-pulseaudio.c | 30 ++ src/proplist.c | 271 +++++++++++ src/rtclock.c | 34 ++ src/sample-util.h | 134 ++++++ src/sample.c | 319 ++++++++++++ src/scache.c | 57 +++ src/strbuf.c | 193 ++++++++ src/strbuf.h | 39 ++ src/stream.c | 908 ++++++++++++++++++++++++++++++++++ src/subscribe.c | 35 ++ src/utf8.c | 287 +++++++++++ src/util.c | 77 +++ src/version.c | 26 + src/volume.c | 989 ++++++++++++++++++++++++++++++++++++++ src/xmalloc.c | 115 +++++ 39 files changed, 9955 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/bitset.c create mode 100644 src/bitset.h create mode 100644 src/channelmap.c create mode 100644 src/context.c create mode 100644 src/core-format.c create mode 100644 src/core-format.h create mode 100644 src/direction.c create mode 100644 src/error.c create mode 100644 src/ext-device-manager.c create mode 100644 src/ext-device-restore.c create mode 100644 src/format.c create mode 100644 src/hashmap.c create mode 100644 src/hashmap.h create mode 100644 src/internal.h create mode 100644 src/introspect.c create mode 100644 src/json.c create mode 100644 src/json.h create mode 100644 src/mainloop-signal.c create mode 100644 src/mainloop.c create mode 100644 src/map-file create mode 100644 src/meson.build create mode 100644 src/operation.c create mode 100644 src/pipewire-pulseaudio.c create mode 100644 src/proplist.c create mode 100644 src/rtclock.c create mode 100644 src/sample-util.h create mode 100644 src/sample.c create mode 100644 src/scache.c create mode 100644 src/strbuf.c create mode 100644 src/strbuf.h create mode 100644 src/stream.c create mode 100644 src/subscribe.c create mode 100644 src/utf8.c create mode 100644 src/util.c create mode 100644 src/version.c create mode 100644 src/volume.c create mode 100644 src/xmalloc.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..19e307187 --- /dev/null +++ b/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +(This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.) + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + {signature of Ty Coon}, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/README.md b/README.md new file mode 100644 index 000000000..017669f60 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# pipewire-pulseaudio +PulseAudio client library for PipeWire diff --git a/src/bitset.c b/src/bitset.c new file mode 100644 index 000000000..6f3247c4f --- /dev/null +++ b/src/bitset.c @@ -0,0 +1,64 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "internal.h" +#include "bitset.h" + +void pa_bitset_set(pa_bitset_t *b, unsigned k, bool v) { + pa_assert(b); + + if (v) + b[k >> 5] |= 1 << (k & 31); + else + b[k >> 5] &= ~((uint32_t) (1 << (k & 31))); +} + +bool pa_bitset_get(const pa_bitset_t *b, unsigned k) { + return !!(b[k >> 5] & (1 << (k & 31))); +} + +bool pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...) { + va_list ap; + pa_bitset_t *a; + bool equal; + + a = alloca(PA_BITSET_ELEMENTS(n)); + + va_start(ap, n); + for (;;) { + int j = va_arg(ap, int); + + if (j < 0) + break; + + pa_bitset_set(a, j, true); + } + va_end(ap); + + equal = memcmp(a, b, PA_BITSET_SIZE(n)) == 0; + + return equal; +} diff --git a/src/bitset.h b/src/bitset.h new file mode 100644 index 000000000..314dafc2a --- /dev/null +++ b/src/bitset.h @@ -0,0 +1,34 @@ +#ifndef foopulsecorebitsethfoo +#define foopulsecorebitsethfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#include + +#define PA_BITSET_ELEMENTS(n) (((n)+31)/32) +#define PA_BITSET_SIZE(n) (PA_BITSET_ELEMENTS(n)*4) + +typedef uint32_t pa_bitset_t; + +void pa_bitset_set(pa_bitset_t *b, unsigned k, bool v); +bool pa_bitset_get(const pa_bitset_t *b, unsigned k); +bool pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...); + +#endif diff --git a/src/channelmap.c b/src/channelmap.c new file mode 100644 index 000000000..894774754 --- /dev/null +++ b/src/channelmap.c @@ -0,0 +1,830 @@ +/*** + This file is part of PulseAudio. + + Copyright 2005-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +#include "internal.h" +#include "bitset.h" +#include "sample-util.h" + +const char *const table[PA_CHANNEL_POSITION_MAX] = { + [PA_CHANNEL_POSITION_MONO] = "mono", + + [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center", + [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left", + [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right", + + [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center", + [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left", + [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right", + + [PA_CHANNEL_POSITION_LFE] = "lfe", + + [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center", + [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center", + + [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left", + [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right", + + [PA_CHANNEL_POSITION_AUX0] = "aux0", + [PA_CHANNEL_POSITION_AUX1] = "aux1", + [PA_CHANNEL_POSITION_AUX2] = "aux2", + [PA_CHANNEL_POSITION_AUX3] = "aux3", + [PA_CHANNEL_POSITION_AUX4] = "aux4", + [PA_CHANNEL_POSITION_AUX5] = "aux5", + [PA_CHANNEL_POSITION_AUX6] = "aux6", + [PA_CHANNEL_POSITION_AUX7] = "aux7", + [PA_CHANNEL_POSITION_AUX8] = "aux8", + [PA_CHANNEL_POSITION_AUX9] = "aux9", + [PA_CHANNEL_POSITION_AUX10] = "aux10", + [PA_CHANNEL_POSITION_AUX11] = "aux11", + [PA_CHANNEL_POSITION_AUX12] = "aux12", + [PA_CHANNEL_POSITION_AUX13] = "aux13", + [PA_CHANNEL_POSITION_AUX14] = "aux14", + [PA_CHANNEL_POSITION_AUX15] = "aux15", + [PA_CHANNEL_POSITION_AUX16] = "aux16", + [PA_CHANNEL_POSITION_AUX17] = "aux17", + [PA_CHANNEL_POSITION_AUX18] = "aux18", + [PA_CHANNEL_POSITION_AUX19] = "aux19", + [PA_CHANNEL_POSITION_AUX20] = "aux20", + [PA_CHANNEL_POSITION_AUX21] = "aux21", + [PA_CHANNEL_POSITION_AUX22] = "aux22", + [PA_CHANNEL_POSITION_AUX23] = "aux23", + [PA_CHANNEL_POSITION_AUX24] = "aux24", + [PA_CHANNEL_POSITION_AUX25] = "aux25", + [PA_CHANNEL_POSITION_AUX26] = "aux26", + [PA_CHANNEL_POSITION_AUX27] = "aux27", + [PA_CHANNEL_POSITION_AUX28] = "aux28", + [PA_CHANNEL_POSITION_AUX29] = "aux29", + [PA_CHANNEL_POSITION_AUX30] = "aux30", + [PA_CHANNEL_POSITION_AUX31] = "aux31", + + [PA_CHANNEL_POSITION_TOP_CENTER] = "top-center", + + [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center", + [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left", + [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right", + + [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center", + [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left", + [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right" +}; + +const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = { + [PA_CHANNEL_POSITION_MONO] = N_("Mono"), + + [PA_CHANNEL_POSITION_FRONT_CENTER] = N_("Front Center"), + [PA_CHANNEL_POSITION_FRONT_LEFT] = N_("Front Left"), + [PA_CHANNEL_POSITION_FRONT_RIGHT] = N_("Front Right"), + + [PA_CHANNEL_POSITION_REAR_CENTER] = N_("Rear Center"), + [PA_CHANNEL_POSITION_REAR_LEFT] = N_("Rear Left"), + [PA_CHANNEL_POSITION_REAR_RIGHT] = N_("Rear Right"), + + [PA_CHANNEL_POSITION_LFE] = N_("Subwoofer"), + + [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = N_("Front Left-of-center"), + [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = N_("Front Right-of-center"), + + [PA_CHANNEL_POSITION_SIDE_LEFT] = N_("Side Left"), + [PA_CHANNEL_POSITION_SIDE_RIGHT] = N_("Side Right"), + + [PA_CHANNEL_POSITION_AUX0] = N_("Auxiliary 0"), + [PA_CHANNEL_POSITION_AUX1] = N_("Auxiliary 1"), + [PA_CHANNEL_POSITION_AUX2] = N_("Auxiliary 2"), + [PA_CHANNEL_POSITION_AUX3] = N_("Auxiliary 3"), + [PA_CHANNEL_POSITION_AUX4] = N_("Auxiliary 4"), + [PA_CHANNEL_POSITION_AUX5] = N_("Auxiliary 5"), + [PA_CHANNEL_POSITION_AUX6] = N_("Auxiliary 6"), + [PA_CHANNEL_POSITION_AUX7] = N_("Auxiliary 7"), + [PA_CHANNEL_POSITION_AUX8] = N_("Auxiliary 8"), + [PA_CHANNEL_POSITION_AUX9] = N_("Auxiliary 9"), + [PA_CHANNEL_POSITION_AUX10] = N_("Auxiliary 10"), + [PA_CHANNEL_POSITION_AUX11] = N_("Auxiliary 11"), + [PA_CHANNEL_POSITION_AUX12] = N_("Auxiliary 12"), + [PA_CHANNEL_POSITION_AUX13] = N_("Auxiliary 13"), + [PA_CHANNEL_POSITION_AUX14] = N_("Auxiliary 14"), + [PA_CHANNEL_POSITION_AUX15] = N_("Auxiliary 15"), + [PA_CHANNEL_POSITION_AUX16] = N_("Auxiliary 16"), + [PA_CHANNEL_POSITION_AUX17] = N_("Auxiliary 17"), + [PA_CHANNEL_POSITION_AUX18] = N_("Auxiliary 18"), + [PA_CHANNEL_POSITION_AUX19] = N_("Auxiliary 19"), + [PA_CHANNEL_POSITION_AUX20] = N_("Auxiliary 20"), + [PA_CHANNEL_POSITION_AUX21] = N_("Auxiliary 21"), + [PA_CHANNEL_POSITION_AUX22] = N_("Auxiliary 22"), + [PA_CHANNEL_POSITION_AUX23] = N_("Auxiliary 23"), + [PA_CHANNEL_POSITION_AUX24] = N_("Auxiliary 24"), + [PA_CHANNEL_POSITION_AUX25] = N_("Auxiliary 25"), + [PA_CHANNEL_POSITION_AUX26] = N_("Auxiliary 26"), + [PA_CHANNEL_POSITION_AUX27] = N_("Auxiliary 27"), + [PA_CHANNEL_POSITION_AUX28] = N_("Auxiliary 28"), + [PA_CHANNEL_POSITION_AUX29] = N_("Auxiliary 29"), + [PA_CHANNEL_POSITION_AUX30] = N_("Auxiliary 30"), + [PA_CHANNEL_POSITION_AUX31] = N_("Auxiliary 31"), + + [PA_CHANNEL_POSITION_TOP_CENTER] = N_("Top Center"), + + [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = N_("Top Front Center"), + [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = N_("Top Front Left"), + [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = N_("Top Front Right"), + + [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = N_("Top Rear Center"), + [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = N_("Top Rear Left"), + [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = N_("Top Rear Right") +}; + +pa_channel_map* pa_channel_map_init(pa_channel_map *m) { + unsigned c; + pa_assert(m); + + m->channels = 0; + + for (c = 0; c < PA_CHANNELS_MAX; c++) + m->map[c] = PA_CHANNEL_POSITION_INVALID; + + return m; +} + +pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) { + pa_assert(m); + + pa_channel_map_init(m); + + m->channels = 1; + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; +} + +pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) { + pa_assert(m); + + pa_channel_map_init(m); + + m->channels = 2; + m->map[0] = PA_CHANNEL_POSITION_LEFT; + m->map[1] = PA_CHANNEL_POSITION_RIGHT; + return m; +} + +pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) { + pa_assert(m); + pa_assert(pa_channels_valid(channels)); + pa_assert(def < PA_CHANNEL_MAP_DEF_MAX); + + pa_channel_map_init(m); + + m->channels = (uint8_t) channels; + + switch (def) { + case PA_CHANNEL_MAP_AIFF: + + /* This is somewhat compatible with RFC3551 */ + + switch (channels) { + case 1: + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; + + case 6: + m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + m->map[1] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; + m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; + m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT; + m->map[4] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; + m->map[5] = PA_CHANNEL_POSITION_REAR_CENTER; + return m; + + case 5: + m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; + m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT; + m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT; + /* Fall through */ + + case 2: + m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + return m; + + case 3: + m->map[0] = PA_CHANNEL_POSITION_LEFT; + m->map[1] = PA_CHANNEL_POSITION_RIGHT; + m->map[2] = PA_CHANNEL_POSITION_CENTER; + return m; + + case 4: + m->map[0] = PA_CHANNEL_POSITION_LEFT; + m->map[1] = PA_CHANNEL_POSITION_CENTER; + m->map[2] = PA_CHANNEL_POSITION_RIGHT; + m->map[3] = PA_CHANNEL_POSITION_REAR_CENTER; + return m; + + default: + return NULL; + } + + case PA_CHANNEL_MAP_ALSA: + + switch (channels) { + case 1: + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; + + case 8: + m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; + m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; + /* Fall through */ + + case 6: + m->map[5] = PA_CHANNEL_POSITION_LFE; + /* Fall through */ + + case 5: + m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; + /* Fall through */ + + case 4: + m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + /* Fall through */ + + case 2: + m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + return m; + + default: + return NULL; + } + + case PA_CHANNEL_MAP_AUX: { + unsigned i; + + for (i = 0; i < channels; i++) + m->map[i] = PA_CHANNEL_POSITION_AUX0 + i; + + return m; + } + + case PA_CHANNEL_MAP_WAVEEX: + + /* Following http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EKLAC */ + + switch (channels) { + case 1: + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; + + case 18: + m->map[15] = PA_CHANNEL_POSITION_TOP_REAR_LEFT; + m->map[16] = PA_CHANNEL_POSITION_TOP_REAR_CENTER; + m->map[17] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT; + /* Fall through */ + + case 15: + m->map[12] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT; + m->map[13] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER; + m->map[14] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; + /* Fall through */ + + case 12: + m->map[11] = PA_CHANNEL_POSITION_TOP_CENTER; + /* Fall through */ + + case 11: + m->map[9] = PA_CHANNEL_POSITION_SIDE_LEFT; + m->map[10] = PA_CHANNEL_POSITION_SIDE_RIGHT; + /* Fall through */ + + case 9: + m->map[8] = PA_CHANNEL_POSITION_REAR_CENTER; + /* Fall through */ + + case 8: + m->map[6] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; + m->map[7] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; + /* Fall through */ + + case 6: + m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT; + m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT; + /* Fall through */ + + case 4: + m->map[3] = PA_CHANNEL_POSITION_LFE; + /* Fall through */ + + case 3: + m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; + /* Fall through */ + + case 2: + m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + return m; + + default: + return NULL; + } + + case PA_CHANNEL_MAP_OSS: + + switch (channels) { + case 1: + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; + + case 8: + m->map[6] = PA_CHANNEL_POSITION_REAR_LEFT; + m->map[7] = PA_CHANNEL_POSITION_REAR_RIGHT; + /* Fall through */ + + case 6: + m->map[4] = PA_CHANNEL_POSITION_SIDE_LEFT; + m->map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT; + /* Fall through */ + + case 4: + m->map[3] = PA_CHANNEL_POSITION_LFE; + /* Fall through */ + + case 3: + m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; + /* Fall through */ + + case 2: + m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + return m; + + default: + return NULL; + } + + default: + pa_assert_not_reached(); + } +} + +pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) { + unsigned c; + + pa_assert(m); + pa_assert(pa_channels_valid(channels)); + pa_assert(def < PA_CHANNEL_MAP_DEF_MAX); + + pa_channel_map_init(m); + + for (c = channels; c > 0; c--) { + + if (pa_channel_map_init_auto(m, c, def)) { + unsigned i = 0; + + for (; c < channels; c++) { + + m->map[c] = PA_CHANNEL_POSITION_AUX0 + i; + i++; + } + + m->channels = (uint8_t) channels; + + return m; + } + } + + return NULL; +} + +const char* pa_channel_position_to_string(pa_channel_position_t pos) { + + if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) + return NULL; + + return table[pos]; +} + +const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) { + + if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) + return NULL; + + pa_init_i18n(); + + return _(pretty_table[pos]); +} + +int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) { + unsigned c; + + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_channel_map_valid(a), 0); + + if (PA_UNLIKELY(a == b)) + return 1; + + pa_return_val_if_fail(pa_channel_map_valid(b), 0); + + if (a->channels != b->channels) + return 0; + + for (c = 0; c < a->channels; c++) + if (a->map[c] != b->map[c]) + return 0; + + return 1; +} + +char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { + unsigned channel; + bool first = true; + char *e; + + pa_assert(s); + pa_assert(l > 0); + pa_assert(map); + + pa_init_i18n(); + + if (!pa_channel_map_valid(map)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + *(e = s) = 0; + + for (channel = 0; channel < map->channels && l > 1; channel++) { + l -= pa_snprintf(e, l, "%s%s", + first ? "" : ",", + pa_channel_position_to_string(map->map[channel])); + + e = strchr(e, 0); + first = false; + } + + return s; +} + +pa_channel_position_t pa_channel_position_from_string(const char *p) { + pa_channel_position_t i; + pa_assert(p); + + /* Some special aliases */ + if (pa_streq(p, "left")) + return PA_CHANNEL_POSITION_LEFT; + else if (pa_streq(p, "right")) + return PA_CHANNEL_POSITION_RIGHT; + else if (pa_streq(p, "center")) + return PA_CHANNEL_POSITION_CENTER; + else if (pa_streq(p, "subwoofer")) + return PA_CHANNEL_POSITION_SUBWOOFER; + + for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++) + if (pa_streq(p, table[i])) + return i; + + return PA_CHANNEL_POSITION_INVALID; +} + +pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { + pa_channel_map map; + char **tokens; + int i, n_tokens; + + pa_assert(rmap); + pa_assert(s); + + pa_channel_map_init(&map); + + /* We don't need to match against the well known channel mapping + * "mono" here explicitly, because that can be understood as + * listing with one channel called "mono". */ + + if (pa_streq(s, "stereo")) { + map.channels = 2; + map.map[0] = PA_CHANNEL_POSITION_LEFT; + map.map[1] = PA_CHANNEL_POSITION_RIGHT; + goto finish; + } else if (pa_streq(s, "surround-21")) { + map.channels = 3; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + map.map[2] = PA_CHANNEL_POSITION_LFE; + goto finish; + } else if (pa_streq(s, "surround-40")) { + map.channels = 4; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + goto finish; + } else if (pa_streq(s, "surround-41")) { + map.channels = 5; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + map.map[4] = PA_CHANNEL_POSITION_LFE; + goto finish; + } else if (pa_streq(s, "surround-50")) { + map.channels = 5; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; + goto finish; + } else if (pa_streq(s, "surround-51")) { + map.channels = 6; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; + map.map[5] = PA_CHANNEL_POSITION_LFE; + goto finish; + } else if (pa_streq(s, "surround-71")) { + map.channels = 8; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; + map.map[5] = PA_CHANNEL_POSITION_LFE; + map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; + map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; + goto finish; + } + + map.channels = 0; + + tokens = pw_split_strv(s, ",", -1, &n_tokens); + + for (i = 0; i < n_tokens; i++) { + pa_channel_position_t f; + + if (map.channels >= PA_CHANNELS_MAX) { + pw_free_strv(tokens); + return NULL; + } + + if ((f = pa_channel_position_from_string(tokens[i])) == PA_CHANNEL_POSITION_INVALID) { + pw_free_strv(tokens); + return NULL; + } + map.map[map.channels++] = f; + } + pw_free_strv(tokens); + +finish: + + if (!pa_channel_map_valid(&map)) + return NULL; + + *rmap = map; + return rmap; +} + +int pa_channel_map_valid(const pa_channel_map *map) { + unsigned c; + + pa_assert(map); + + if (!pa_channels_valid(map->channels)) + return 0; + + for (c = 0; c < map->channels; c++) + if (map->map[c] < 0 || map->map[c] >= PA_CHANNEL_POSITION_MAX) + return 0; + + return 1; +} + +int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) { + pa_assert(map); + pa_assert(ss); + + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + pa_return_val_if_fail(pa_sample_spec_valid(ss), 0); + + return map->channels == ss->channels; +} + +int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) { + pa_channel_position_mask_t am, bm; + + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_channel_map_valid(a), 0); + + if (PA_UNLIKELY(a == b)) + return 1; + + pa_return_val_if_fail(pa_channel_map_valid(b), 0); + + am = pa_channel_map_mask(a); + bm = pa_channel_map_mask(b); + + return (bm & am) == bm; +} + +int pa_channel_map_can_balance(const pa_channel_map *map) { + pa_channel_position_mask_t m; + + pa_assert(map); + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + + m = pa_channel_map_mask(map); + + return + (PA_CHANNEL_POSITION_MASK_LEFT & m) && + (PA_CHANNEL_POSITION_MASK_RIGHT & m); +} + +int pa_channel_map_can_fade(const pa_channel_map *map) { + pa_channel_position_mask_t m; + + pa_assert(map); + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + + m = pa_channel_map_mask(map); + + return + (PA_CHANNEL_POSITION_MASK_FRONT & m) && + (PA_CHANNEL_POSITION_MASK_REAR & m); +} + +int pa_channel_map_can_lfe_balance(const pa_channel_map *map) { + pa_channel_position_mask_t m; + + pa_assert(map); + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + + m = pa_channel_map_mask(map); + + return + (PA_CHANNEL_POSITION_MASK_LFE & m) && + (PA_CHANNEL_POSITION_MASK_HFE & m); +} + +const char* pa_channel_map_to_name(const pa_channel_map *map) { + pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)]; + unsigned c; + + pa_assert(map); + + pa_return_val_if_fail(pa_channel_map_valid(map), NULL); + + memset(in_map, 0, sizeof(in_map)); + + for (c = 0; c < map->channels; c++) + pa_bitset_set(in_map, map->map[c], true); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_MONO, -1)) + return "mono"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1)) + return "stereo"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1)) + return "surround-40"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_LFE, -1)) + return "surround-41"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, -1)) + return "surround-50"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1)) + return "surround-51"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, + PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1)) + return "surround-71"; + + return NULL; +} + +const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) { + pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)]; + unsigned c; + + pa_assert(map); + + pa_return_val_if_fail(pa_channel_map_valid(map), NULL); + + memset(in_map, 0, sizeof(in_map)); + + for (c = 0; c < map->channels; c++) + pa_bitset_set(in_map, map->map[c], true); + + pa_init_i18n(); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_MONO, -1)) + return _("Mono"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1)) + return _("Stereo"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1)) + return _("Surround 4.0"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_LFE, -1)) + return _("Surround 4.1"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, -1)) + return _("Surround 5.0"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1)) + return _("Surround 5.1"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, + PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1)) + return _("Surround 7.1"); + + return NULL; +} + +int pa_channel_map_has_position(const pa_channel_map *map, pa_channel_position_t p) { + unsigned c; + + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + pa_return_val_if_fail(p < PA_CHANNEL_POSITION_MAX, 0); + + for (c = 0; c < map->channels; c++) + if (map->map[c] == p) + return 1; + + return 0; +} + +pa_channel_position_mask_t pa_channel_map_mask(const pa_channel_map *map) { + unsigned c; + pa_channel_position_mask_t r = 0; + + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + + for (c = 0; c < map->channels; c++) + r |= PA_CHANNEL_POSITION_MASK(map->map[c]); + + return r; +} diff --git a/src/context.c b/src/context.c new file mode 100644 index 000000000..c44c83701 --- /dev/null +++ b/src/context.c @@ -0,0 +1,453 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "internal.h" + +int pa_context_set_error(pa_context *c, int error) { + spa_assert(error >= 0); + spa_assert(error < PA_ERR_MAX); + if (c) + c->error = error; + return error; +} + +static void context_unlink(pa_context *c) +{ + pa_stream *s, *t; + + spa_list_for_each_safe(s, t, &c->streams, link) { + pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? + PA_STREAM_FAILED : PA_STREAM_TERMINATED); + pa_stream_unref(s); + } + + while(!spa_list_is_empty(&c->operations)) + pa_operation_cancel(spa_list_first(&c->operations, pa_operation, link)); +} + +void pa_context_set_state(pa_context *c, pa_context_state_t st) { + spa_assert(c); + spa_assert(c->refcount >= 1); + + if (c->state == st) + return; + + pa_context_ref(c); + + c->state = st; + + if (c->state_callback) + c->state_callback(c, c->state_userdata); + + if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) + context_unlink(c); + + pa_context_unref(c); +} + +static void context_fail(pa_context *c, int error) { + spa_assert(c); + spa_assert(c->refcount >= 1); + + pa_context_set_error(c, error); + pa_context_set_state(c, PA_CONTEXT_FAILED); +} + +pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) +{ + return pa_context_new_with_proplist(mainloop, name, NULL); +} + + +static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, + uint32_t permissions, uint32_t type, uint32_t version, + const struct spa_dict *props) +{ + pa_context *c = data; + struct global *g; + + pw_log_debug("global %d", id); + g = calloc(1, sizeof(struct global)); + g->id = id; + g->parent_id = parent_id; + g->type = type; + g->props = props ? pw_properties_new_dict(props) : NULL; + + spa_list_append(&c->globals, &g->link); +} + +struct global *pa_context_find_global(pa_context *c, uint32_t id) +{ + struct global *g; + spa_list_for_each(g, &c->globals, link) { + if (g->id == id) + return g; + } + return NULL; +} + +static void registry_event_global_remove(void *object, uint32_t id) +{ + pa_context *c = object; + struct global *g; + + pw_log_debug("remove %d", id); + if ((g = pa_context_find_global(c, id)) == NULL) + return; + + spa_list_remove(&g->link); + pw_properties_free(g->props); + + if (g->destroy && g->info) + g->destroy(g->info); + free(g); +} + +static const struct pw_registry_proxy_events registry_events = +{ + PW_VERSION_REGISTRY_PROXY_EVENTS, + .global = registry_event_global, + .global_remove = registry_event_global_remove, +}; + +struct ready_data +{ + pa_context *context; +}; + +static void on_ready(pa_operation *o, void *userdata) +{ + struct ready_data *d = userdata; + pa_context_set_state(d->context, PA_CONTEXT_READY); +} + +static void remote_state_changed(void *data, enum pw_remote_state old, + enum pw_remote_state state, const char *error) +{ + pa_context *c = data; + pa_operation *o; + struct ready_data *d; + + switch(state) { + case PW_REMOTE_STATE_ERROR: + context_fail(c, PA_ERR_CONNECTIONTERMINATED); + break; + case PW_REMOTE_STATE_UNCONNECTED: + break; + case PW_REMOTE_STATE_CONNECTING: + pa_context_set_state(c, PA_CONTEXT_CONNECTING); + break; + case PW_REMOTE_STATE_CONNECTED: + pa_context_set_state(c, PA_CONTEXT_AUTHORIZING); + pa_context_set_state(c, PA_CONTEXT_SETTING_NAME); + + c->core_proxy = pw_remote_get_core_proxy(c->remote); + c->registry_proxy = pw_core_proxy_get_registry(c->core_proxy, + c->t->registry, + PW_VERSION_REGISTRY, 0); + pw_registry_proxy_add_listener(c->registry_proxy, + &c->registry_listener, + ®istry_events, c); + + o = pa_operation_new(c, NULL, on_ready, sizeof(struct ready_data)); + d = o->userdata; + d->context = c; + break; + } +} + +static void remote_sync_reply(void *data, uint32_t seq) +{ + pa_context *c = data; + pa_operation *o; + + pw_log_debug("done %d", seq); + spa_list_for_each(o, &c->operations, link) { + if (o->seq != seq) + continue; + pa_operation_ref(o); + if (o->callback) + o->callback(o, o->userdata); + pa_operation_unref(o); + break; + } +} + +static const struct pw_remote_events remote_events = { + PW_VERSION_REMOTE_EVENTS, + .state_changed = remote_state_changed, + .sync_reply = remote_sync_reply +}; + +pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) +{ + struct pw_core *core; + struct pw_loop *loop; + struct pw_remote *r; + struct pw_properties *props; + pa_context *c; + + spa_assert(mainloop); + + props = pw_properties_new(NULL, NULL); + if (name) + pw_properties_set(props, PA_PROP_APPLICATION_NAME, name); + + loop = mainloop->userdata; + core = pw_core_new(loop, NULL); + + r = pw_remote_new(core, props, sizeof(struct pa_context)); + if (r == NULL) + return NULL; + + + c = pw_remote_get_user_data(r); + c->loop = loop; + c->core = core; + c->t = pw_core_get_type(core); + c->remote = r; + + pw_remote_add_listener(r, &c->remote_listener, &remote_events, c); + + c->proplist = p ? pa_proplist_copy(p) : pa_proplist_new(); + c->refcount = 1; + c->client_index = PA_INVALID_INDEX; + + if (name) + pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name); + + c->mainloop = mainloop; + c->error = 0; + c->state = PA_CONTEXT_UNCONNECTED; + + spa_list_init(&c->globals); + + spa_list_init(&c->streams); + spa_list_init(&c->operations); + + return c; +} + +static void context_free(pa_context *c) +{ + context_unlink(c); + + if (c->proplist) + pa_proplist_free(c->proplist); + free(c); +} + +void pa_context_unref(pa_context *c) +{ + spa_assert(c); + spa_assert(c->refcount >= 1); + + if (--c->refcount == 0) + context_free(c); +} + +pa_context* pa_context_ref(pa_context *c) +{ + spa_assert(c); + spa_assert(c->refcount >= 1); + c->refcount++; + return c; +} + +void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) +{ + spa_assert(c); + spa_assert(c->refcount >= 1); + + if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED) + return; + + c->state_callback = cb; + c->state_userdata = userdata; +} + +void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata) +{ + spa_assert(c); + spa_assert(c->refcount >= 1); + + if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED) + return; + + c->event_callback = cb; + c->event_userdata = userdata; +} + +int pa_context_errno(pa_context *c) +{ + if (!c) + return PA_ERR_INVALID; + + spa_assert(c->refcount >= 1); + + return c->error; +} + +int pa_context_is_pending(pa_context *c) +{ + pw_log_warn("Not Implemented"); + return 0; +} + +pa_context_state_t pa_context_get_state(pa_context *c) +{ + spa_assert(c); + spa_assert(c->refcount >= 1); + return c->state; +} + +int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api) +{ + int res; + + spa_assert(c); + spa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(c, !(flags & ~(PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL)), PA_ERR_INVALID); + PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID); + + pa_context_ref(c); + + c->no_fail = !!(flags & PA_CONTEXT_NOFAIL); + + res = pw_remote_connect(c->remote); + + pa_context_unref(c); + + return res; +} + +void pa_context_disconnect(pa_context *c) +{ + spa_assert(c); + spa_assert(c->refcount >= 1); + + pw_remote_disconnect(c->remote); + + if (PA_CONTEXT_IS_GOOD(c->state)) + pa_context_set_state(c, PA_CONTEXT_TERMINATED); +} + +pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +int pa_context_is_local(pa_context *c) +{ + pw_log_warn("Not Implemented"); + return 0; +} + +pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +const char* pa_context_get_server(pa_context *c) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +uint32_t pa_context_get_protocol_version(pa_context *c) +{ + return PA_PROTOCOL_VERSION; +} + +uint32_t pa_context_get_server_protocol_version(pa_context *c) +{ + return PA_PROTOCOL_VERSION; +} + +pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +uint32_t pa_context_get_index(pa_context *c) +{ + return c->client_index; +} + +pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) +{ + pw_log_warn("Not Implemented"); +} + +size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss) +{ + pw_log_warn("Not Implemented"); + return 1024; +} + +int pa_context_load_cookie_from_file(pa_context *c, const char *cookie_file_path) +{ + pw_log_warn("Not Implemented"); + return -ENOTSUP; +} diff --git a/src/core-format.c b/src/core-format.c new file mode 100644 index 000000000..baa19a480 --- /dev/null +++ b/src/core-format.c @@ -0,0 +1,241 @@ +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "core-format.h" + +#include +#include + +#include "internal.h" + +int pa_format_info_get_sample_format(const pa_format_info *f, pa_sample_format_t *sf) { + int r; + char *sf_str; + pa_sample_format_t sf_local; + + pa_assert(f); + pa_assert(sf); + + r = pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf_str); + if (r < 0) + return r; + + sf_local = pa_parse_sample_format(sf_str); + pa_xfree(sf_str); + + if (!pa_sample_format_valid(sf_local)) { + pa_log_debug("Invalid sample format."); + return -PA_ERR_INVALID; + } + + *sf = sf_local; + + return 0; +} + +int pa_format_info_get_rate(const pa_format_info *f, uint32_t *rate) { + int r; + int rate_local; + + pa_assert(f); + pa_assert(rate); + + r = pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate_local); + if (r < 0) + return r; + + if (!pa_sample_rate_valid(rate_local)) { + pa_log_debug("Invalid sample rate: %i", rate_local); + return -PA_ERR_INVALID; + } + + *rate = rate_local; + + return 0; +} + +int pa_format_info_get_channels(const pa_format_info *f, uint8_t *channels) { + int r; + int channels_local; + + pa_assert(f); + pa_assert(channels); + + r = pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels_local); + if (r < 0) + return r; + + if (!pa_channels_valid(channels_local)) { + pa_log_debug("Invalid channel count: %i", channels_local); + return -PA_ERR_INVALID; + } + + *channels = channels_local; + + return 0; +} + +int pa_format_info_get_channel_map(const pa_format_info *f, pa_channel_map *map) { + int r; + char *map_str; + + pa_assert(f); + pa_assert(map); + + r = pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &map_str); + if (r < 0) + return r; + + map = pa_channel_map_parse(map, map_str); + pa_xfree(map_str); + + if (!map) { + pa_log_debug("Failed to parse channel map."); + return -PA_ERR_INVALID; + } + + return 0; +} + +pa_format_info *pa_format_info_from_sample_spec2(const pa_sample_spec *ss, const pa_channel_map *map, bool set_format, + bool set_rate, bool set_channels) { + pa_format_info *format = NULL; + + pa_assert(ss); + + format = pa_format_info_new(); + format->encoding = PA_ENCODING_PCM; + + if (set_format) + pa_format_info_set_sample_format(format, ss->format); + + if (set_rate) + pa_format_info_set_rate(format, ss->rate); + + if (set_channels) { + pa_format_info_set_channels(format, ss->channels); + + if (map) { + if (map->channels != ss->channels) { + pa_log_debug("Channel map is incompatible with the sample spec."); + goto fail; + } + + pa_format_info_set_channel_map(format, map); + } + } + + return format; + +fail: + if (format) + pa_format_info_free(format); + + return NULL; +} + +int pa_format_info_to_sample_spec2(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map, + const pa_sample_spec *fallback_ss, const pa_channel_map *fallback_map) { + int r, r2; + pa_sample_spec ss_local; + pa_channel_map map_local; + + pa_assert(f); + pa_assert(ss); + pa_assert(map); + pa_assert(fallback_ss); + pa_assert(fallback_map); + + if (!pa_format_info_is_pcm(f)) + return pa_format_info_to_sample_spec_fake(f, ss, map); + + r = pa_format_info_get_sample_format(f, &ss_local.format); + if (r == -PA_ERR_NOENTITY) + ss_local.format = fallback_ss->format; + else if (r < 0) + return r; + + pa_assert(pa_sample_format_valid(ss_local.format)); + + r = pa_format_info_get_rate(f, &ss_local.rate); + if (r == -PA_ERR_NOENTITY) + ss_local.rate = fallback_ss->rate; + else if (r < 0) + return r; + + pa_assert(pa_sample_rate_valid(ss_local.rate)); + + r = pa_format_info_get_channels(f, &ss_local.channels); + r2 = pa_format_info_get_channel_map(f, &map_local); + if (r == -PA_ERR_NOENTITY && r2 >= 0) + ss_local.channels = map_local.channels; + else if (r == -PA_ERR_NOENTITY) + ss_local.channels = fallback_ss->channels; + else if (r < 0) + return r; + + pa_assert(pa_channels_valid(ss_local.channels)); + + if (r2 >= 0 && map_local.channels != ss_local.channels) { + pa_log_debug("Channel map is not compatible with the sample spec."); + return -PA_ERR_INVALID; + } + + if (r2 == -PA_ERR_NOENTITY) { + if (fallback_map->channels == ss_local.channels) + map_local = *fallback_map; + else + pa_channel_map_init_extend(&map_local, ss_local.channels, PA_CHANNEL_MAP_DEFAULT); + } else if (r2 < 0) + return r2; + + pa_assert(pa_channel_map_valid(&map_local)); + pa_assert(ss_local.channels == map_local.channels); + + *ss = ss_local; + *map = map_local; + + return 0; +} + +int pa_format_info_to_sample_spec_fake(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) { + int rate; + + pa_assert(f); + pa_assert(ss); + + /* Note: When we add support for non-IEC61937 encapsulated compressed + * formats, this function should return a non-zero values for these. */ + + ss->format = PA_SAMPLE_S16LE; + ss->channels = 2; + + if (map) + pa_channel_map_init_stereo(map); + + pa_return_val_if_fail(pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate) == 0, -PA_ERR_INVALID); + ss->rate = (uint32_t) rate; + + if (f->encoding == PA_ENCODING_EAC3_IEC61937) + ss->rate *= 4; + + return 0; +} diff --git a/src/core-format.h b/src/core-format.h new file mode 100644 index 000000000..37503041b --- /dev/null +++ b/src/core-format.h @@ -0,0 +1,79 @@ +#ifndef foocoreformathfoo +#define foocoreformathfoo + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#include + +#include + +/* Gets the sample format stored in the format info. Returns a negative error + * code on failure. If the sample format property is not set at all, returns + * -PA_ERR_NOENTITY. */ +int pa_format_info_get_sample_format(const pa_format_info *f, pa_sample_format_t *sf); + +/* Gets the sample rate stored in the format info. Returns a negative error + * code on failure. If the sample rate property is not set at all, returns + * -PA_ERR_NOENTITY. */ +int pa_format_info_get_rate(const pa_format_info *f, uint32_t *rate); + +/* Gets the channel count stored in the format info. Returns a negative error + * code on failure. If the channels property is not set at all, returns + * -PA_ERR_NOENTITY. */ +int pa_format_info_get_channels(const pa_format_info *f, uint8_t *channels); + +/* Gets the channel map stored in the format info. Returns a negative error + * code on failure. If the channel map property is not set at all, returns + * -PA_ERR_NOENTITY. */ +int pa_format_info_get_channel_map(const pa_format_info *f, pa_channel_map *map); + +/* Convert a sample spec and an optional channel map to a new PCM format info + * object (remember to free it). If map is NULL, then the channel map will be + * left unspecified. If some fields of the sample spec should be ignored, pass + * false for set_format, set_rate and set_channels as appropriate, then those + * fields will be left unspecified. This function returns NULL if the input is + * invalid (for example, setting the sample rate was requested, but the rate + * in ss is invalid). + * + * pa_format_info_from_sample_spec() exists too. This "version 2" was created, + * because the original function doesn't provide the possibility of ignoring + * some of the sample spec fields. That functionality can't be added to the + * original function, because the function is a part of the public API and + * adding parameters to it would break the API. */ +pa_format_info *pa_format_info_from_sample_spec2(const pa_sample_spec *ss, const pa_channel_map *map, bool set_format, + bool set_rate, bool set_channels); + +/* Convert the format info into a sample spec and a channel map. If the format + * info doesn't contain some information, the fallback sample spec and channel + * map are used to populate the output. + * + * pa_format_info_to_sample_spec() exists too. This "version 2" was created, + * because the original function doesn't provide the possibility of specifying + * a fallback sample spec and channel map. That functionality can't be added to + * the original function, because the function is part of the public API and + * adding parameters to it would break the API. */ +int pa_format_info_to_sample_spec2(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map, + const pa_sample_spec *fallback_ss, const pa_channel_map *fallback_map); + +/* For compressed formats. Converts the format info into a sample spec and a + * channel map that an ALSA device can use as its configuration parameters when + * playing back the compressed data. That is, the returned sample spec doesn't + * describe the audio content, but the device parameters. */ +int pa_format_info_to_sample_spec_fake(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map); + +#endif diff --git a/src/direction.c b/src/direction.c new file mode 100644 index 000000000..b9ed061fb --- /dev/null +++ b/src/direction.c @@ -0,0 +1,47 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#define pa_init_i18n() +#define _(String) (String) + +int pa_direction_valid(pa_direction_t direction) +{ + if (direction != PA_DIRECTION_INPUT + && direction != PA_DIRECTION_OUTPUT + && direction != (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT)) + return 0; + return 1; +} + +const char *pa_direction_to_string(pa_direction_t direction) { + pa_init_i18n(); + + if (direction == PA_DIRECTION_INPUT) + return _("input"); + if (direction == PA_DIRECTION_OUTPUT) + return _("output"); + if (direction == (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT)) + return _("bidirectional"); + + return _("invalid"); +} diff --git a/src/error.c b/src/error.c new file mode 100644 index 000000000..f6b707c89 --- /dev/null +++ b/src/error.c @@ -0,0 +1,74 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include + +#include +#include + +#define N_(String) (String) +#define _(String) (String) +#define pa_init_i18n() + +const char*pa_strerror(int error) +{ + static const char* const errortab[PA_ERR_MAX] = { + [PA_OK] = N_("OK"), + [PA_ERR_ACCESS] = N_("Access denied"), + [PA_ERR_COMMAND] = N_("Unknown command"), + [PA_ERR_INVALID] = N_("Invalid argument"), + [PA_ERR_EXIST] = N_("Entity exists"), + [PA_ERR_NOENTITY] = N_("No such entity"), + [PA_ERR_CONNECTIONREFUSED] = N_("Connection refused"), + [PA_ERR_PROTOCOL] = N_("Protocol error"), + [PA_ERR_TIMEOUT] = N_("Timeout"), + [PA_ERR_AUTHKEY] = N_("No authentication key"), + [PA_ERR_INTERNAL] = N_("Internal error"), + [PA_ERR_CONNECTIONTERMINATED] = N_("Connection terminated"), + [PA_ERR_KILLED] = N_("Entity killed"), + [PA_ERR_INVALIDSERVER] = N_("Invalid server"), + [PA_ERR_MODINITFAILED] = N_("Module initialization failed"), + [PA_ERR_BADSTATE] = N_("Bad state"), + [PA_ERR_NODATA] = N_("No data"), + [PA_ERR_VERSION] = N_("Incompatible protocol version"), + [PA_ERR_TOOLARGE] = N_("Too large"), + [PA_ERR_NOTSUPPORTED] = N_("Not supported"), + [PA_ERR_UNKNOWN] = N_("Unknown error code"), + [PA_ERR_NOEXTENSION] = N_("No such extension"), + [PA_ERR_OBSOLETE] = N_("Obsolete functionality"), + [PA_ERR_NOTIMPLEMENTED] = N_("Missing implementation"), + [PA_ERR_FORKED] = N_("Client forked"), + [PA_ERR_IO] = N_("Input/Output error"), + [PA_ERR_BUSY] = N_("Device or resource busy") + }; + + pa_init_i18n(); + + if (error < 0) + error = -error; + + if (error >= PA_ERR_MAX) + return NULL; + + return _(errortab[error]); +} + diff --git a/src/ext-device-manager.c b/src/ext-device-manager.c new file mode 100644 index 000000000..31578ad59 --- /dev/null +++ b/src/ext-device-manager.c @@ -0,0 +1,105 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include + +#include "internal.h" + + +pa_operation *pa_ext_device_manager_test( + pa_context *c, + pa_ext_device_manager_test_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation *pa_ext_device_manager_read( + pa_context *c, + pa_ext_device_manager_read_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation *pa_ext_device_manager_set_device_description( + pa_context *c, + const char* device, + const char* description, + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation *pa_ext_device_manager_delete( + pa_context *c, + const char *const s[], + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation *pa_ext_device_manager_enable_role_device_priority_routing( + pa_context *c, + int enable, + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation *pa_ext_device_manager_reorder_devices_for_role( + pa_context *c, + const char* role, + const char** devices, + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation *pa_ext_device_manager_subscribe( + pa_context *c, + int enable, + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +void pa_ext_device_manager_set_subscribe_cb( + pa_context *c, + pa_ext_device_manager_subscribe_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); +} diff --git a/src/ext-device-restore.c b/src/ext-device-restore.c new file mode 100644 index 000000000..2f7977559 --- /dev/null +++ b/src/ext-device-restore.c @@ -0,0 +1,84 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include "internal.h" + +pa_operation *pa_ext_device_restore_test( + pa_context *c, + pa_ext_device_restore_test_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation *pa_ext_device_restore_subscribe( + pa_context *c, + int enable, + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +void pa_ext_device_restore_set_subscribe_cb( + pa_context *c, + pa_ext_device_restore_subscribe_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); +} + +pa_operation *pa_ext_device_restore_read_formats_all( + pa_context *c, + pa_ext_device_restore_read_device_formats_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation *pa_ext_device_restore_read_formats( + pa_context *c, + pa_device_type_t type, + uint32_t idx, + pa_ext_device_restore_read_device_formats_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation *pa_ext_device_restore_save_formats( + pa_context *c, + pa_device_type_t type, + uint32_t idx, + uint8_t n_formats, + pa_format_info **formats, + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} diff --git a/src/format.c b/src/format.c new file mode 100644 index 000000000..1cc2b8bf5 --- /dev/null +++ b/src/format.c @@ -0,0 +1,679 @@ +/*** + This file is part of PulseAudio. + + Copyright 2011 Intel Corporation + Copyright 2011 Collabora Multimedia + Copyright 2011 Arun Raghavan + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "core-format.h" +#include "json.h" +#include "internal.h" +#include "strbuf.h" + +#define PA_JSON_MIN_KEY "min" +#define PA_JSON_MAX_KEY "max" + +static int pa_format_info_prop_compatible(const char *one, const char *two); + +static const char* const _encoding_str_table[]= { + [PA_ENCODING_PCM] = "pcm", + [PA_ENCODING_AC3_IEC61937] = "ac3-iec61937", + [PA_ENCODING_EAC3_IEC61937] = "eac3-iec61937", + [PA_ENCODING_MPEG_IEC61937] = "mpeg-iec61937", + [PA_ENCODING_DTS_IEC61937] = "dts-iec61937", + [PA_ENCODING_MPEG2_AAC_IEC61937] = "mpeg2-aac-iec61937", + [PA_ENCODING_ANY] = "any", +}; + +const char *pa_encoding_to_string(pa_encoding_t e) { + if (e < 0 || e >= PA_ENCODING_MAX) + return NULL; + + return _encoding_str_table[e]; +} + +pa_encoding_t pa_encoding_from_string(const char *encoding) { + pa_encoding_t e; + + for (e = PA_ENCODING_ANY; e < PA_ENCODING_MAX; e++) + if (pa_streq(_encoding_str_table[e], encoding)) + return e; + + return PA_ENCODING_INVALID; +} + +pa_format_info* pa_format_info_new(void) { + pa_format_info *f = pa_xnew(pa_format_info, 1); + + f->encoding = PA_ENCODING_INVALID; + f->plist = pa_proplist_new(); + + return f; +} + +pa_format_info* pa_format_info_copy(const pa_format_info *src) { + pa_format_info *dest; + + pa_assert(src); + + dest = pa_xnew(pa_format_info, 1); + + dest->encoding = src->encoding; + + if (src->plist) + dest->plist = pa_proplist_copy(src->plist); + else + dest->plist = NULL; + + return dest; +} + +void pa_format_info_free(pa_format_info *f) { + pa_assert(f); + + pa_proplist_free(f->plist); + pa_xfree(f); +} + +int pa_format_info_valid(const pa_format_info *f) { + return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL); +} + +int pa_format_info_is_pcm(const pa_format_info *f) { + return f->encoding == PA_ENCODING_PCM; +} + +char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) { + char *tmp; + + pa_assert(s); + pa_assert(l > 0); + pa_assert(f); + + pa_init_i18n(); + + if (!pa_format_info_valid(f)) + pa_snprintf(s, l, _("(invalid)")); + else { + tmp = pa_proplist_to_string_sep(f->plist, " "); + if (tmp[0]) + pa_snprintf(s, l, "%s, %s", pa_encoding_to_string(f->encoding), tmp); + else + pa_snprintf(s, l, "%s", pa_encoding_to_string(f->encoding)); + pa_xfree(tmp); + } + + return s; +} + +pa_format_info* pa_format_info_from_string(const char *str) { + pa_format_info *f = pa_format_info_new(); + char *encoding = NULL, *properties = NULL; + size_t pos; + + pos = strcspn(str, ","); + + encoding = pa_xstrndup(str, pos); + f->encoding = pa_encoding_from_string(pa_strip(encoding)); + if (f->encoding == PA_ENCODING_INVALID) + goto error; + + if (pos != strlen(str)) { + pa_proplist *plist; + + properties = pa_xstrdup(&str[pos+1]); + plist = pa_proplist_from_string(properties); + + if (!plist) + goto error; + + pa_proplist_free(f->plist); + f->plist = plist; + } + +out: + if (encoding) + pa_xfree(encoding); + if (properties) + pa_xfree(properties); + return f; + +error: + pa_format_info_free(f); + f = NULL; + goto out; +} + +int pa_format_info_is_compatible(const pa_format_info *first, const pa_format_info *second) { + const char *key; + void *state = NULL; + + pa_assert(first); + pa_assert(second); + + if (first->encoding != second->encoding) + return false; + + while ((key = pa_proplist_iterate(first->plist, &state))) { + const char *value_one, *value_two; + + value_one = pa_proplist_gets(first->plist, key); + value_two = pa_proplist_gets(second->plist, key); + + if (!value_two || !pa_format_info_prop_compatible(value_one, value_two)) + return false; + } + + return true; +} + +pa_format_info* pa_format_info_from_sample_spec(const pa_sample_spec *ss, const pa_channel_map *map) { + char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + pa_format_info *f; + + pa_assert(ss && pa_sample_spec_valid(ss)); + pa_assert(!map || pa_channel_map_valid(map)); + + f = pa_format_info_new(); + f->encoding = PA_ENCODING_PCM; + + pa_format_info_set_sample_format(f, ss->format); + pa_format_info_set_rate(f, ss->rate); + pa_format_info_set_channels(f, ss->channels); + + if (map) { + pa_channel_map_snprint(cm, sizeof(cm), map); + pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm); + } + + return f; +} + +/* For PCM streams */ +int pa_format_info_to_sample_spec(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) { + pa_assert(f); + pa_assert(ss); + + if (!pa_format_info_is_pcm(f)) + return pa_format_info_to_sample_spec_fake(f, ss, map); + + if (pa_format_info_get_sample_format(f, &ss->format) < 0) + return -PA_ERR_INVALID; + if (pa_format_info_get_rate(f, &ss->rate) < 0) + return -PA_ERR_INVALID; + if (pa_format_info_get_channels(f, &ss->channels) < 0) + return -PA_ERR_INVALID; + if (map && pa_format_info_get_channel_map(f, map) < 0) + return -PA_ERR_INVALID; + + return 0; +} + +pa_prop_type_t pa_format_info_get_prop_type(const pa_format_info *f, const char *key) { + const char *str; + pa_json_object *o; + const pa_json_object *o1; + pa_prop_type_t type; + + pa_assert(f); + pa_assert(key); + + str = pa_proplist_gets(f->plist, key); + if (!str) + return PA_PROP_TYPE_INVALID; + + o = pa_json_parse(str); + if (!o) + return PA_PROP_TYPE_INVALID; + + switch (pa_json_object_get_type(o)) { + case PA_JSON_TYPE_INT: + type = PA_PROP_TYPE_INT; + break; + + case PA_JSON_TYPE_STRING: + type = PA_PROP_TYPE_STRING; + break; + + case PA_JSON_TYPE_ARRAY: + if (pa_json_object_get_array_length(o) == 0) { + /* Unlikely, but let's account for this anyway. We need at + * least one element to figure out the array type. */ + type = PA_PROP_TYPE_INVALID; + break; + } + + o1 = pa_json_object_get_array_member(o, 0); + + if (pa_json_object_get_type(o1) == PA_JSON_TYPE_INT) + type = PA_PROP_TYPE_INT_ARRAY; + else if (pa_json_object_get_type(o1) == PA_JSON_TYPE_STRING) + type = PA_PROP_TYPE_STRING_ARRAY; + else + type = PA_PROP_TYPE_INVALID; + + break; + + case PA_JSON_TYPE_OBJECT: + /* We actually know at this point that it's a int range, but let's + * confirm. */ + if (!pa_json_object_get_object_member(o, PA_JSON_MIN_KEY)) { + type = PA_PROP_TYPE_INVALID; + break; + } + + if (!pa_json_object_get_object_member(o, PA_JSON_MAX_KEY)) { + type = PA_PROP_TYPE_INVALID; + break; + } + + type = PA_PROP_TYPE_INT_RANGE; + break; + + default: + type = PA_PROP_TYPE_INVALID; + break; + } + + pa_json_object_free(o); + return type; +} + +int pa_format_info_get_prop_int(const pa_format_info *f, const char *key, int *v) { + const char *str; + pa_json_object *o; + + pa_assert(f); + pa_assert(key); + pa_assert(v); + + str = pa_proplist_gets(f->plist, key); + if (!str) + return -PA_ERR_NOENTITY; + + o = pa_json_parse(str); + if (!o) { + pa_log_debug("Failed to parse format info property '%s'.", key); + return -PA_ERR_INVALID; + } + + if (pa_json_object_get_type(o) != PA_JSON_TYPE_INT) { + pa_log_debug("Format info property '%s' type is not int.", key); + pa_json_object_free(o); + return -PA_ERR_INVALID; + } + + *v = pa_json_object_get_int(o); + pa_json_object_free(o); + + return 0; +} + +int pa_format_info_get_prop_int_range(const pa_format_info *f, const char *key, int *min, int *max) { + const char *str; + pa_json_object *o; + const pa_json_object *o1; + int ret = -PA_ERR_INVALID; + + pa_assert(f); + pa_assert(key); + pa_assert(min); + pa_assert(max); + + str = pa_proplist_gets(f->plist, key); + if (!str) + return -PA_ERR_NOENTITY; + + o = pa_json_parse(str); + if (!o) { + pa_log_debug("Failed to parse format info property '%s'.", key); + return -PA_ERR_INVALID; + } + + if (pa_json_object_get_type(o) != PA_JSON_TYPE_OBJECT) + goto out; + + if (!(o1 = pa_json_object_get_object_member(o, PA_JSON_MIN_KEY)) || + (pa_json_object_get_type(o1) != PA_JSON_TYPE_INT)) + goto out; + + *min = pa_json_object_get_int(o1); + + if (!(o1 = pa_json_object_get_object_member(o, PA_JSON_MAX_KEY)) || + (pa_json_object_get_type(o1) != PA_JSON_TYPE_INT)) + goto out; + + *max = pa_json_object_get_int(o1); + + ret = 0; + +out: + if (ret < 0) + pa_log_debug("Format info property '%s' is not a valid int range.", key); + + pa_json_object_free(o); + return ret; +} + +int pa_format_info_get_prop_int_array(const pa_format_info *f, const char *key, int **values, int *n_values) { + const char *str; + pa_json_object *o; + const pa_json_object *o1; + int i, ret = -PA_ERR_INVALID; + + pa_assert(f); + pa_assert(key); + pa_assert(values); + pa_assert(n_values); + + str = pa_proplist_gets(f->plist, key); + if (!str) + return -PA_ERR_NOENTITY; + + o = pa_json_parse(str); + if (!o) { + pa_log_debug("Failed to parse format info property '%s'.", key); + return -PA_ERR_INVALID; + } + + if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY) + goto out; + + *n_values = pa_json_object_get_array_length(o); + *values = pa_xnew(int, *n_values); + + for (i = 0; i < *n_values; i++) { + o1 = pa_json_object_get_array_member(o, i); + + if (pa_json_object_get_type(o1) != PA_JSON_TYPE_INT) { + goto out; + } + + (*values)[i] = pa_json_object_get_int(o1); + } + + ret = 0; + +out: + if (ret < 0) + pa_log_debug("Format info property '%s' is not a valid int array.", key); + + pa_json_object_free(o); + return ret; +} + +int pa_format_info_get_prop_string(const pa_format_info *f, const char *key, char **v) { + const char *str = NULL; + pa_json_object *o; + + pa_assert(f); + pa_assert(key); + pa_assert(v); + + str = pa_proplist_gets(f->plist, key); + if (!str) + return -PA_ERR_NOENTITY; + + o = pa_json_parse(str); + if (!o) { + pa_log_debug("Failed to parse format info property '%s'.", key); + return -PA_ERR_INVALID; + } + + if (pa_json_object_get_type(o) != PA_JSON_TYPE_STRING) { + pa_log_debug("Format info property '%s' type is not string.", key); + pa_json_object_free(o); + return -PA_ERR_INVALID; + } + + *v = pa_xstrdup(pa_json_object_get_string(o)); + pa_json_object_free(o); + + return 0; +} + +int pa_format_info_get_prop_string_array(const pa_format_info *f, const char *key, char ***values, int *n_values) { + const char *str; + pa_json_object *o; + const pa_json_object *o1; + int i, ret = -PA_ERR_INVALID; + + pa_assert(f); + pa_assert(key); + pa_assert(values); + pa_assert(n_values); + + str = pa_proplist_gets(f->plist, key); + if (!str) + return -PA_ERR_NOENTITY; + + o = pa_json_parse(str); + if (!o) { + pa_log_debug("Failed to parse format info property '%s'.", key); + return -PA_ERR_INVALID; + } + + if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY) + goto out; + + *n_values = pa_json_object_get_array_length(o); + *values = pa_xnew(char *, *n_values); + + for (i = 0; i < *n_values; i++) { + o1 = pa_json_object_get_array_member(o, i); + + if (pa_json_object_get_type(o1) != PA_JSON_TYPE_STRING) { + goto out; + } + + (*values)[i] = pa_xstrdup(pa_json_object_get_string(o1)); + } + + ret = 0; + +out: + if (ret < 0) + pa_log_debug("Format info property '%s' is not a valid string array.", key); + + pa_json_object_free(o); + return ret; +} + +void pa_format_info_free_string_array(char **values, int n_values) { + int i; + + for (i = 0; i < n_values; i++) + pa_xfree(values[i]); + + pa_xfree(values); +} + +void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf) { + pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(sf)); +} + +void pa_format_info_set_rate(pa_format_info *f, int rate) { + pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, rate); +} + +void pa_format_info_set_channels(pa_format_info *f, int channels) { + pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, channels); +} + +void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map) { + char map_str[PA_CHANNEL_MAP_SNPRINT_MAX]; + + pa_channel_map_snprint(map_str, sizeof(map_str), map); + + pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, map_str); +} + +void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) { + pa_assert(f); + pa_assert(key); + + pa_proplist_setf(f->plist, key, "%d", value); +} + +void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) { + pa_strbuf *buf; + char *str; + int i; + + pa_assert(f); + pa_assert(key); + pa_assert(n_values > 0); + + buf = pa_strbuf_new(); + + pa_strbuf_printf(buf, "[ %d", values[0]); + + for (i = 1; i < n_values; i++) + pa_strbuf_printf(buf, ", %d", values[i]); + + pa_strbuf_printf(buf, " ]"); + str = pa_strbuf_to_string_free(buf); + + pa_proplist_sets(f->plist, key, str); + pa_xfree (str); +} + +void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) { + pa_assert(f); + pa_assert(key); + + pa_proplist_setf(f->plist, key, "{ \"" PA_JSON_MIN_KEY "\": %d, \"" PA_JSON_MAX_KEY "\": %d }", + min, max); +} + +void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) { + pa_assert(f); + pa_assert(key); + + pa_proplist_setf(f->plist, key, "\"%s\"", value); +} + +void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) { + pa_strbuf *buf; + char *str; + int i; + + pa_assert(f); + pa_assert(key); + + buf = pa_strbuf_new(); + + pa_strbuf_printf(buf, "[ \"%s\"", values[0]); + + for (i = 1; i < n_values; i++) + pa_strbuf_printf(buf, ", \"%s\"", values[i]); + + pa_strbuf_printf(buf, " ]"); + str = pa_strbuf_to_string_free(buf); + + pa_proplist_sets(f->plist, key, str); + pa_xfree (str); +} + +static bool pa_json_is_fixed_type(pa_json_object *o) { + switch(pa_json_object_get_type(o)) { + case PA_JSON_TYPE_OBJECT: + case PA_JSON_TYPE_ARRAY: + return false; + + default: + return true; + } +} + +static int pa_format_info_prop_compatible(const char *one, const char *two) { + pa_json_object *o1 = NULL, *o2 = NULL; + int i, ret = 0; + + o1 = pa_json_parse(one); + if (!o1) + goto out; + + o2 = pa_json_parse(two); + if (!o2) + goto out; + + /* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */ + pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), false); + + if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) { + ret = pa_json_object_equal(o1, o2); + goto out; + } + + if (pa_json_is_fixed_type(o1)) { + pa_json_object *tmp = o2; + o2 = o1; + o1 = tmp; + } + + /* o2 is now a fixed type, and o1 is not */ + + if (pa_json_object_get_type(o1) == PA_JSON_TYPE_ARRAY) { + for (i = 0; i < pa_json_object_get_array_length(o1); i++) { + if (pa_json_object_equal(pa_json_object_get_array_member(o1, i), o2)) { + ret = 1; + break; + } + } + } else if (pa_json_object_get_type(o1) == PA_JSON_TYPE_OBJECT) { + /* o1 should be a range type */ + int min, max, v; + const pa_json_object *o_min = NULL, *o_max = NULL; + + if (pa_json_object_get_type(o2) != PA_JSON_TYPE_INT) { + /* We don't support non-integer ranges */ + goto out; + } + + if (!(o_min = pa_json_object_get_object_member(o1, PA_JSON_MIN_KEY)) || + pa_json_object_get_type(o_min) != PA_JSON_TYPE_INT) + goto out; + + if (!(o_max = pa_json_object_get_object_member(o1, PA_JSON_MAX_KEY)) || + pa_json_object_get_type(o_max) != PA_JSON_TYPE_INT) + goto out; + + v = pa_json_object_get_int(o2); + min = pa_json_object_get_int(o_min); + max = pa_json_object_get_int(o_max); + + ret = v >= min && v <= max; + } else { + pa_log_warn("Got a format type that we don't support"); + } + +out: + if (o1) + pa_json_object_free(o1); + if (o2) + pa_json_object_free(o2); + + return ret; +} diff --git a/src/hashmap.c b/src/hashmap.c new file mode 100644 index 000000000..f3dd73fdd --- /dev/null +++ b/src/hashmap.c @@ -0,0 +1,348 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2008 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include "internal.h" +#include "hashmap.h" + +#define NBUCKETS 127 + +struct hashmap_entry { + void *key; + void *value; + + struct hashmap_entry *bucket_next, *bucket_previous; + struct hashmap_entry *iterate_next, *iterate_previous; +}; + +struct pa_hashmap { + pa_hash_func_t hash_func; + pa_compare_func_t compare_func; + + pa_free_cb_t key_free_func; + pa_free_cb_t value_free_func; + + struct hashmap_entry *iterate_list_head, *iterate_list_tail; + unsigned n_entries; +}; + +#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + PA_ALIGN(sizeof(pa_hashmap)))) + +PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree); + +static unsigned pa_idxset_trivial_hash_func(const void *p) { + return PA_PTR_TO_UINT(p); +} + +static int pa_idxset_trivial_compare_func(const void *a, const void *b) { + return a < b ? -1 : (a > b ? 1 : 0); +} + +pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func, pa_free_cb_t key_free_func, pa_free_cb_t value_free_func) { + pa_hashmap *h; + + h = pa_xmalloc0(PA_ALIGN(sizeof(pa_hashmap)) + NBUCKETS*sizeof(struct hashmap_entry*)); + + h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; + h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; + + h->key_free_func = key_free_func; + h->value_free_func = value_free_func; + + h->n_entries = 0; + h->iterate_list_head = h->iterate_list_tail = NULL; + + return h; +} + +pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) { + return pa_hashmap_new_full(hash_func, compare_func, NULL, NULL); +} + +static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) { + pa_assert(h); + pa_assert(e); + + /* Remove from iteration list */ + if (e->iterate_next) + e->iterate_next->iterate_previous = e->iterate_previous; + else + h->iterate_list_tail = e->iterate_previous; + + if (e->iterate_previous) + e->iterate_previous->iterate_next = e->iterate_next; + else + h->iterate_list_head = e->iterate_next; + + /* Remove from hash table bucket list */ + if (e->bucket_next) + e->bucket_next->bucket_previous = e->bucket_previous; + + if (e->bucket_previous) + e->bucket_previous->bucket_next = e->bucket_next; + else { + unsigned hash = h->hash_func(e->key) % NBUCKETS; + BY_HASH(h)[hash] = e->bucket_next; + } + + if (h->key_free_func) + h->key_free_func(e->key); + + if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0) + pa_xfree(e); + + pa_assert(h->n_entries >= 1); + h->n_entries--; +} + +void pa_hashmap_free(pa_hashmap *h) { + pa_assert(h); + + pa_hashmap_remove_all(h); + pa_xfree(h); +} + +static struct hashmap_entry *hash_scan(pa_hashmap *h, unsigned hash, const void *key) { + struct hashmap_entry *e; + pa_assert(h); + pa_assert(hash < NBUCKETS); + + for (e = BY_HASH(h)[hash]; e; e = e->bucket_next) + if (h->compare_func(e->key, key) == 0) + return e; + + return NULL; +} + +int pa_hashmap_put(pa_hashmap *h, void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + + pa_assert(h); + + hash = h->hash_func(key) % NBUCKETS; + + if (hash_scan(h, hash, key)) + return -1; + + if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries)))) + e = pa_xnew(struct hashmap_entry, 1); + + e->key = key; + e->value = value; + + /* Insert into hash table */ + e->bucket_next = BY_HASH(h)[hash]; + e->bucket_previous = NULL; + if (BY_HASH(h)[hash]) + BY_HASH(h)[hash]->bucket_previous = e; + BY_HASH(h)[hash] = e; + + /* Insert into iteration list */ + e->iterate_previous = h->iterate_list_tail; + e->iterate_next = NULL; + if (h->iterate_list_tail) { + pa_assert(h->iterate_list_head); + h->iterate_list_tail->iterate_next = e; + } else { + pa_assert(!h->iterate_list_head); + h->iterate_list_head = e; + } + h->iterate_list_tail = e; + + h->n_entries++; + pa_assert(h->n_entries >= 1); + + return 0; +} + +void* pa_hashmap_get(pa_hashmap *h, const void *key) { + unsigned hash; + struct hashmap_entry *e; + + pa_assert(h); + + hash = h->hash_func(key) % NBUCKETS; + + if (!(e = hash_scan(h, hash, key))) + return NULL; + + return e->value; +} + +void* pa_hashmap_remove(pa_hashmap *h, const void *key) { + struct hashmap_entry *e; + unsigned hash; + void *data; + + pa_assert(h); + + hash = h->hash_func(key) % NBUCKETS; + + if (!(e = hash_scan(h, hash, key))) + return NULL; + + data = e->value; + remove_entry(h, e); + + return data; +} + +int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key) { + void *data; + + pa_assert(h); + + data = pa_hashmap_remove(h, key); + + if (data && h->value_free_func) + h->value_free_func(data); + + return data ? 0 : -1; +} + +void pa_hashmap_remove_all(pa_hashmap *h) { + pa_assert(h); + + while (h->iterate_list_head) { + void *data; + data = h->iterate_list_head->value; + remove_entry(h, h->iterate_list_head); + + if (h->value_free_func) + h->value_free_func(data); + } +} + +void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) { + struct hashmap_entry *e; + + pa_assert(h); + pa_assert(state); + + if (*state == (void*) -1) + goto at_end; + + if (!*state && !h->iterate_list_head) + goto at_end; + + e = *state ? *state : h->iterate_list_head; + + if (e->iterate_next) + *state = e->iterate_next; + else + *state = (void*) -1; + + if (key) + *key = e->key; + + return e->value; + +at_end: + *state = (void *) -1; + + if (key) + *key = NULL; + + return NULL; +} + +void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void **key) { + struct hashmap_entry *e; + + pa_assert(h); + pa_assert(state); + + if (*state == (void*) -1) + goto at_beginning; + + if (!*state && !h->iterate_list_tail) + goto at_beginning; + + e = *state ? *state : h->iterate_list_tail; + + if (e->iterate_previous) + *state = e->iterate_previous; + else + *state = (void*) -1; + + if (key) + *key = e->key; + + return e->value; + +at_beginning: + *state = (void *) -1; + + if (key) + *key = NULL; + + return NULL; +} + +void* pa_hashmap_first(pa_hashmap *h) { + pa_assert(h); + + if (!h->iterate_list_head) + return NULL; + + return h->iterate_list_head->value; +} + +void* pa_hashmap_last(pa_hashmap *h) { + pa_assert(h); + + if (!h->iterate_list_tail) + return NULL; + + return h->iterate_list_tail->value; +} + +void* pa_hashmap_steal_first(pa_hashmap *h) { + void *data; + + pa_assert(h); + + if (!h->iterate_list_head) + return NULL; + + data = h->iterate_list_head->value; + remove_entry(h, h->iterate_list_head); + + return data; +} + +unsigned pa_hashmap_size(pa_hashmap *h) { + pa_assert(h); + + return h->n_entries; +} + +bool pa_hashmap_isempty(pa_hashmap *h) { + pa_assert(h); + + return h->n_entries == 0; +} diff --git a/src/hashmap.h b/src/hashmap.h new file mode 100644 index 000000000..f857c6406 --- /dev/null +++ b/src/hashmap.h @@ -0,0 +1,105 @@ +#ifndef foopulsecorehashmaphfoo +#define foopulsecorehashmaphfoo + +/*** + This file is part of PulseAudio. + + Copyright 2004-2008 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#include + +#include + +typedef unsigned (*pa_hash_func_t)(const void *p); +typedef int (*pa_compare_func_t)(const void *a, const void *b); +typedef void *(*pa_copy_func_t)(const void *p); + +/* Simple Implementation of a hash table. Memory management is the + * user's job. It's a good idea to have the key pointer point to a + * string in the value data. The insertion order is preserved when + * iterating. */ + +typedef struct pa_hashmap pa_hashmap; + +/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */ +pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func); + +/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map, and functions to free the key + * and value (either or both can be NULL). */ +pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func, pa_free_cb_t key_free_func, pa_free_cb_t value_free_func); + +/* Free the hash table. */ +void pa_hashmap_free(pa_hashmap*); + +/* Add an entry to the hashmap. Returns non-zero when the entry already exists */ +int pa_hashmap_put(pa_hashmap *h, void *key, void *value); + +/* Return an entry from the hashmap */ +void* pa_hashmap_get(pa_hashmap *h, const void *key); + +/* Returns the data of the entry while removing */ +void* pa_hashmap_remove(pa_hashmap *h, const void *key); + +/* Removes the entry and frees the entry data. Returns a negative value if the + * entry is not found. FIXME: This function shouldn't be needed. + * pa_hashmap_remove() should free the entry data, and the current semantics of + * pa_hashmap_remove() should be implemented by a function called + * pa_hashmap_steal(). */ +int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key); + +/* Remove all entries but don't free the hashmap */ +void pa_hashmap_remove_all(pa_hashmap *h); + +/* Return the current number of entries of the hashmap */ +unsigned pa_hashmap_size(pa_hashmap *h); + +/* Return true if the hashmap is empty */ +bool pa_hashmap_isempty(pa_hashmap *h); + +/* May be used to iterate through the hashmap. Initially the opaque + pointer *state has to be set to NULL. The hashmap may not be + modified during iteration -- except for deleting the current entry + via pa_hashmap_remove(). The key of the entry is returned in *key, + if key is non-NULL. After the last entry in the hashmap NULL is + returned. */ +void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key); + +/* Same as pa_hashmap_iterate() but goes backwards */ +void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void**key); + +/* Remove the oldest entry in the hashmap and return it */ +void *pa_hashmap_steal_first(pa_hashmap *h); + +/* Return the oldest entry in the hashmap */ +void* pa_hashmap_first(pa_hashmap *h); + +/* Return the newest entry in the hashmap */ +void* pa_hashmap_last(pa_hashmap *h); + +/* A macro to ease iteration through all entries */ +#define PA_HASHMAP_FOREACH(e, h, state) \ + for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL)) + +/* A macro to ease itration through all key, value pairs */ +#define PA_HASHMAP_FOREACH_KV(k, e, h, state) \ + for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k)); (e); (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k))) + +/* A macro to ease iteration through all entries, backwards */ +#define PA_HASHMAP_FOREACH_BACKWARDS(e, h, state) \ + for ((state) = NULL, (e) = pa_hashmap_iterate_backwards((h), &(state), NULL); (e); (e) = pa_hashmap_iterate_backwards((h), &(state), NULL)) + +#endif diff --git a/src/internal.h b/src/internal.h new file mode 100644 index 000000000..da42139d2 --- /dev/null +++ b/src/internal.h @@ -0,0 +1,306 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PIPEWIRE_PULSEAUDIO_INTERNAL_H__ +#define __PIPEWIRE_PULSEAUDIO_INTERNAL_H__ + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define PA_MAX_FORMATS (PA_ENCODING_MAX) + +#ifdef __cplusplus +extern "C" { +#endif + +#define pa_streq(a,b) (!strcmp((a),(b))) +#define pa_strneq(a,b,n) (!strncmp((a),(b),(n))) + +#define PA_UNLIKELY SPA_UNLIKELY +#define PA_MIN SPA_MIN +#define PA_MAX SPA_MAX +#define pa_assert spa_assert +#define pa_return_val_if_fail spa_return_val_if_fail +#define pa_assert_not_reached spa_assert_not_reached + +#define PA_USEC_PER_MSEC SPA_USEC_PER_MSEC + +#ifdef __GNUC__ +#define PA_CLAMP_UNLIKELY(x, low, high) \ + __extension__ ({ \ + typeof(x) _x = (x); \ + typeof(low) _low = (low); \ + typeof(high) _high = (high); \ + (PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \ + }) +#else +#define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x))) +#endif + + +#define pa_init_i18n() +#define _(String) (String) +#define N_(String) (String) + +#define pa_snprintf snprintf +#define pa_strip(n) pw_strip(n,"\n\r \t") + +#define pa_log pw_log_info +#define pa_log_debug pw_log_debug +#define pa_log_warn pw_log_warn + +static inline void* PA_ALIGN_PTR(const void *p) { + return (void*) (((size_t) p) & ~(sizeof(void*) - 1)); +} + +/* Rounds up */ +static inline size_t PA_ALIGN(size_t l) { + return ((l + sizeof(void*) - 1) & ~(sizeof(void*) - 1)); +} + +static inline const char *pa_strnull(const char *x) { + return x ? x : "(null)"; +} + +int pa_context_set_error(pa_context *c, int error); + +#define PA_CHECK_VALIDITY(context, expression, error) \ + do { \ + if (!(expression)) \ + return -pa_context_set_error((context), (error)); \ + } while(false) + +#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) \ + do { \ + if (!(expression)) { \ + pa_context_set_error((context), (error)); \ + return value; \ + } \ + } while(false) + +#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) \ + PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL) + +#define PA_FAIL(context, error) \ + do { \ + return -pa_context_set_error((context), (error)); \ + } while(false) + +#define PA_FAIL_RETURN_ANY(context, error, value) \ + do { \ + pa_context_set_error((context), (error)); \ + return value; \ + } while(false) + +#define PA_FAIL_RETURN_NULL(context, error) \ + PA_FAIL_RETURN_ANY(context, error, NULL) + +struct pa_proplist { + struct pw_properties *props; +}; + +pa_proplist* pa_proplist_new_props(struct pw_properties *props); +pa_proplist* pa_proplist_new_dict(struct spa_dict *dict); + +struct pa_mainloop { + struct pw_loop *loop; + struct spa_source *event; + + pa_mainloop_api api; + + bool quit; + int retval; + + int timeout; + int n_events; +}; + +struct global { + struct spa_list link; + uint32_t id; + uint32_t parent_id; + uint32_t type; + struct pw_properties *props; + + void *info; + pw_destroy_t destroy; + + struct pw_proxy *proxy; + struct spa_hook proxy_listener; + struct spa_hook proxy_proxy_listener; +}; + +struct pa_context { + int refcount; + + struct pw_loop *loop; + struct pw_core *core; + struct pw_type *t; + struct pw_remote *remote; + struct spa_hook remote_listener; + + struct pw_core_proxy *core_proxy; + + struct pw_registry_proxy *registry_proxy; + struct spa_hook registry_listener; + + pa_proplist *proplist; + pa_mainloop_api *mainloop; + + uint32_t seq; + + int error; + pa_context_state_t state; + + pa_context_notify_cb_t state_callback; + void *state_userdata; + pa_context_event_cb_t event_callback; + void *event_userdata; + + bool no_fail; + uint32_t client_index; + + struct spa_list globals; + + struct spa_list streams; + struct spa_list operations; +}; + +struct global *pa_context_find_global(pa_context *c, uint32_t id); + +struct type { + struct spa_type_media_type media_type; + struct spa_type_media_subtype media_subtype; + struct spa_type_format_audio format_audio; + struct spa_type_audio_format audio_format; +}; + +static inline void init_type(struct type *type, struct spa_type_map *map) +{ + spa_type_media_type_map(map, &type->media_type); + spa_type_media_subtype_map(map, &type->media_subtype); + spa_type_format_audio_map(map, &type->format_audio); + spa_type_audio_format_map(map, &type->audio_format); +} + +struct pa_stream { + struct spa_list link; + int refcount; + + struct pw_stream *stream; + struct spa_hook stream_listener; + + struct type type; + + pa_context *context; + pa_proplist *proplist; + + pa_stream_direction_t direction; + pa_stream_state_t state; + pa_stream_flags_t flags; + + pa_sample_spec sample_spec; + pa_channel_map channel_map; + uint8_t n_formats; + pa_format_info *req_formats[PA_MAX_FORMATS]; + pa_format_info *format; + + uint32_t stream_index; + + pa_buffer_attr buffer_attr; + + uint32_t device_index; + char *device_name; + + pa_timing_info timing_info; + + uint32_t direct_on_input; + + bool suspended:1; + bool corked:1; + bool timing_info_valid:1; + + pa_stream_notify_cb_t state_callback; + void *state_userdata; + pa_stream_request_cb_t read_callback; + void *read_userdata; + pa_stream_request_cb_t write_callback; + void *write_userdata; + pa_stream_notify_cb_t overflow_callback; + void *overflow_userdata; + pa_stream_notify_cb_t underflow_callback; + void *underflow_userdata; + pa_stream_notify_cb_t latency_update_callback; + void *latency_update_userdata; + pa_stream_notify_cb_t moved_callback; + void *moved_userdata; + pa_stream_notify_cb_t suspended_callback; + void *suspended_userdata; + pa_stream_notify_cb_t started_callback; + void *started_userdata; + pa_stream_event_cb_t event_callback; + void *event_userdata; + pa_stream_notify_cb_t buffer_attr_callback; + void *buffer_attr_userdata; + + struct pw_buffer *buffer; +}; + +void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); + +typedef void (*pa_operation_cb_t)(pa_operation *o, void *userdata); + +struct pa_operation +{ + struct spa_list link; + + int refcount; + pa_context *context; + pa_stream *stream; + + uint32_t seq; + pa_operation_state_t state; + + pa_operation_cb_t callback; + void *userdata; + + pa_operation_notify_cb_t state_callback; + void *state_userdata; +}; + + +pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, size_t userdata_size); +void pa_operation_done(pa_operation *o); + +#ifdef __cplusplus +} +#endif + +#endif /* __PIPEWIRE_PULSEAUDIO_INTERNAL_H__ */ diff --git a/src/introspect.c b/src/introspect.c new file mode 100644 index 000000000..db8982c89 --- /dev/null +++ b/src/introspect.c @@ -0,0 +1,839 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include + +#include "internal.h" + +typedef int (*global_filter_t)(pa_context *c, struct global *g); + +static void node_event_info(void *object, struct pw_node_info *info) +{ + struct global *g = object; + pw_log_debug("update"); + g->info = pw_node_info_update(g->info, info); +} + +static const struct pw_node_proxy_events node_events = { + PW_VERSION_NODE_PROXY_EVENTS, + .info = node_event_info, +}; + +static void module_event_info(void *object, struct pw_module_info *info) +{ + struct global *g = object; + pw_log_debug("update"); + g->info = pw_module_info_update(g->info, info); +} + +static const struct pw_module_proxy_events module_events = { + PW_VERSION_MODULE_PROXY_EVENTS, + .info = module_event_info, +}; + +static void client_event_info(void *object, struct pw_client_info *info) +{ + struct global *g = object; + pw_log_debug("update"); + g->info = pw_client_info_update(g->info, info); +} + +static const struct pw_client_proxy_events client_events = { + PW_VERSION_CLIENT_PROXY_EVENTS, + .info = client_event_info, +}; + +static int ensure_global(pa_context *c, struct global *g) +{ + uint32_t client_version; + const void *events; + pw_destroy_t destroy; + struct pw_type *t = c->t; + + if (g->proxy != NULL) + return 0; + + if (g->type == t->node) { + events = &node_events; + client_version = PW_VERSION_NODE; + destroy = (pw_destroy_t) pw_node_info_free; + } + else if (g->type == t->module) { + events = &module_events; + client_version = PW_VERSION_MODULE; + destroy = (pw_destroy_t) pw_module_info_free; + } + else if (g->type == t->client) { + events = &client_events; + client_version = PW_VERSION_CLIENT; + destroy = (pw_destroy_t) pw_client_info_free; + } + else + return -EINVAL; + + g->proxy = pw_registry_proxy_bind(c->registry_proxy, g->id, g->type, + client_version, 0); + if (g->proxy == NULL) + return -ENOMEM; + + pw_proxy_add_proxy_listener(g->proxy, &g->proxy_proxy_listener, events, g); + g->destroy = destroy; + + return 0; +} + +static void ensure_types(pa_context *c, uint32_t type, global_filter_t filter) +{ + struct global *g; + spa_list_for_each(g, &c->globals, link) { + if (!filter(c, g)) + continue; + ensure_global(c, g); + } +} + +struct sink_data { + pa_context *context; + pa_sink_info_cb_t cb; + void *userdata; + struct global *global; +}; + +static void sink_callback(struct sink_data *d) +{ + struct global *g = d->global; + struct pw_node_info *info = g->info; + pa_sink_info i; + + spa_zero(i); + i.index = g->id; + i.name = info->name; + i.proplist = pa_proplist_new_dict(info->props); + i.owner_module = g->parent_id; + i.base_volume = PA_VOLUME_NORM; + i.n_volume_steps = PA_VOLUME_NORM+1; + + d->cb(d->context, &i, 0, d->userdata); +} + +static void sink_info(pa_operation *o, void *userdata) +{ + struct sink_data *d = userdata; + sink_callback(d); + d->cb(d->context, NULL, 1, d->userdata); +} + +static int sink_filter(pa_context *c, struct global *g) +{ + const char *str; + + if (g->type != c->t->node) + return 0; + if (g->props == NULL) + return 0; + if ((str = pw_properties_get(g->props, "media.class")) == NULL) + return 0; + if (strcmp(str, "Audio/Sink") != 0) + return 0; + return 1; +} + +pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata) +{ + pa_operation *o; + struct global *g; + struct sink_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!sink_filter(c, g)) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data)); + d = o->userdata; + d->global = g; + return o; +} + +static void sink_info_list(pa_operation *o, void *userdata) +{ + struct sink_data *d = userdata; + pa_context *c = d->context; + struct global *g; + + spa_list_for_each(g, &c->globals, link) { + if (!sink_filter(c, g)) + continue; + d->global = g; + sink_callback(d); + } + d->cb(c, NULL, 1, d->userdata); +} + +pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata) +{ + pa_operation *o; + struct sink_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + ensure_types(c, c->t->node, sink_filter); + o = pa_operation_new(c, NULL, sink_info_list, sizeof(struct sink_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + + return o; +} + +pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_sink_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + + +struct source_data { + pa_context *context; + pa_source_info_cb_t cb; + void *userdata; + struct global *global; +}; + +static void source_callback(struct source_data *d) +{ + struct global *g = d->global; + struct pw_node_info *info = g->info; + pa_source_info i; + + spa_zero(i); + i.index = g->id; + i.name = info->name; + i.proplist = pa_proplist_new_dict(info->props); + i.owner_module = g->parent_id; + i.base_volume = PA_VOLUME_NORM; + i.n_volume_steps = PA_VOLUME_NORM+1; + + d->cb(d->context, &i, 0, d->userdata); +} + +static void source_info(pa_operation *o, void *userdata) +{ + struct source_data *d = userdata; + source_callback(d); + d->cb(d->context, NULL, 1, d->userdata); +} + +static int source_filter(pa_context *c, struct global *g) +{ + const char *str; + + if (g->type != c->t->node) + return 0; + if (g->props == NULL) + return 0; + if ((str = pw_properties_get(g->props, "media.class")) == NULL) + return 0; + if (strcmp(str, "Audio/Source") != 0) + return 0; + return 1; +} + +pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata) +{ + pa_operation *o; + struct global *g; + struct source_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!source_filter(c, g)) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, source_info, sizeof(struct source_data)); + d = o->userdata; + d->global = g; + return o; +} + +static void source_info_list(pa_operation *o, void *userdata) +{ + struct source_data *d = userdata; + pa_context *c = d->context; + struct global *g; + + spa_list_for_each(g, &c->globals, link) { + if (!source_filter(c, g)) + continue; + d->global = g; + source_callback(d); + } + d->cb(c, NULL, 1, d->userdata); +} + +pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata) +{ + pa_operation *o; + struct source_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + ensure_types(c, c->t->node, source_filter); + o = pa_operation_new(c, NULL, source_info_list, sizeof(struct source_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + + return o; +} + +pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +struct module_data { + pa_context *context; + pa_module_info_cb_t cb; + void *userdata; + struct global *global; +}; + +static void module_callback(struct module_data *d) +{ + struct global *g = d->global; + struct pw_module_info *info = g->info; + pa_module_info i; + + spa_zero(i); + i.proplist = pa_proplist_new_dict(info->props); + i.index = g->id; + i.name = info->name; + i.argument = info->args; + i.n_used = -1; + i.auto_unload = false; + d->cb(d->context, &i, 0, d->userdata); +} + +static void module_info(pa_operation *o, void *userdata) +{ + struct module_data *d = userdata; + module_callback(d); + d->cb(d->context, NULL, 1, d->userdata); +} + +static int module_filter(pa_context *c, struct global *g) +{ + if (g->type != c->t->module) + return 0; + return 1; +} + +pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata) +{ + pa_operation *o; + struct global *g; + struct module_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!module_filter(c, g)) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, module_info, sizeof(struct module_data)); + d = o->userdata; + d->global = g; + + return o; +} + +static void module_info_list(pa_operation *o, void *userdata) +{ + struct module_data *d = userdata; + pa_context *c = d->context; + struct global *g; + + spa_list_for_each(g, &c->globals, link) { + if (!module_filter(c, g)) + continue; + d->global = g; + module_callback(d); + } + d->cb(c, NULL, 1, d->userdata); +} + +pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata) +{ + pa_operation *o; + struct module_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + ensure_types(c, c->t->module, module_filter); + o = pa_operation_new(c, NULL, module_info_list, sizeof(struct module_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + + return o; +} + +pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +struct client_data { + pa_context *context; + pa_client_info_cb_t cb; + void *userdata; + struct global *global; +}; + +static void client_callback(struct client_data *d) +{ + struct global *g = d->global; + struct pw_client_info *info = g->info; + pa_client_info i; + + spa_zero(i); + i.proplist = pa_proplist_new_dict(info->props); + i.index = g->id; + i.name = info->props ? + spa_dict_lookup(info->props, "application.prgname") : NULL; + i.owner_module = g->parent_id; + i.driver = info->props ? + spa_dict_lookup(info->props, PW_CLIENT_PROP_PROTOCOL) : NULL; + d->cb(d->context, &i, 0, d->userdata); +} + +static void client_info(pa_operation *o, void *userdata) +{ + struct client_data *d = userdata; + client_callback(d); + d->cb(d->context, NULL, 1, d->userdata); +} + +static int client_filter(pa_context *c, struct global *g) +{ + if (g->type != c->t->client) + return 0; + return 1; +} + +pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata) +{ + pa_operation *o; + struct global *g; + struct client_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!client_filter(c, g)) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, client_info, sizeof(struct client_data)); + d = o->userdata; + d->global = g; + + return o; +} + +static void client_info_list(pa_operation *o, void *userdata) +{ + struct client_data *d = userdata; + pa_context *c = d->context; + struct global *g; + + spa_list_for_each(g, &c->globals, link) { + if (!client_filter(c, g)) + continue; + d->global = g; + client_callback(d); + } + d->cb(c, NULL, 1, d->userdata); +} + +pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata) +{ + pa_operation *o; + struct client_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + ensure_types(c, c->t->client, client_filter); + o = pa_operation_new(c, NULL, client_info_list, sizeof(struct client_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + + return o; +} + +pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, pa_card_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name, const char*profile, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card_name, const char *port_name, int64_t offset, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) +{ + pw_log_warn("Deprecated: Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) +{ + pw_log_warn("Deprecated: Not Implemented"); + return NULL; +} + +pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) +{ + pw_log_warn("Deprecated: Not Implemented"); + return NULL; +} + +pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) +{ + pw_log_warn("Deprecated: Not Implemented"); + return NULL; +} + +pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) +{ + pw_log_warn("Deprecated: Not Implemented"); + return NULL; +} + +pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) +{ + pw_log_warn("Deprecated: Not Implemented"); + return NULL; +} diff --git a/src/json.c b/src/json.c new file mode 100644 index 000000000..b242e21b2 --- /dev/null +++ b/src/json.c @@ -0,0 +1,648 @@ +/*** + This file is part of PulseAudio. + + Copyright 2016 Arun Raghavan + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include +#include + +#include "internal.h" +#include "json.h" +#include "strbuf.h" + +#define MAX_NESTING_DEPTH 20 /* Arbitrary number to make sure we don't have a stack overflow */ + +typedef struct pa_json_item { + char *key; + pa_json_object *value; +} pa_json_item; + +struct pa_json_object { + pa_json_type type; + + union { + int int_value; + double double_value; + bool bool_value; + char *string_value; + struct pw_array values; /* objects */ + }; +}; + +static void clear_array(struct pw_array *array) +{ + pa_json_object *value; + pw_array_for_each(value, array) + pa_json_object_free(value); + pw_array_clear(array); +} + +static void clear_item(pa_json_item *item) +{ + free(item->key); + pa_json_object_free(item->value); +} + +static void clear_object(struct pw_array *array) +{ + pa_json_item *item; + pw_array_for_each(item, array) + clear_item(item); + pw_array_clear(array); +} + +static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth); + +static pa_json_object* json_object_new(void) { + pa_json_object *obj; + + obj = pa_xnew0(pa_json_object, 1); + + return obj; +} + +static bool is_whitespace(char c) { + return c == '\t' || c == '\n' || c == '\r' || c == ' '; +} + +static bool is_digit(char c) { + return c >= '0' && c <= '9'; +} + +static bool is_end(const char c, const char *end) { + if (!end) + return c == '\0'; + else { + while (*end) { + if (c == *end) + return true; + end++; + } + } + + return false; +} + +static const char* consume_string(const char *str, const char *expect) { + while (*expect) { + if (*str != *expect) + return NULL; + + str++; + expect++; + } + + return str; +} + +static const char* parse_null(const char *str, pa_json_object *obj) { + str = consume_string(str, "null"); + + if (str) + obj->type = PA_JSON_TYPE_NULL; + + return str; +} + +static const char* parse_boolean(const char *str, pa_json_object *obj) { + const char *tmp; + + tmp = consume_string(str, "true"); + + if (tmp) { + obj->type = PA_JSON_TYPE_BOOL; + obj->bool_value = true; + } else { + tmp = consume_string(str, "false"); + + if (str) { + obj->type = PA_JSON_TYPE_BOOL; + obj->bool_value = false; + } + } + + return tmp; +} + +static const char* parse_string(const char *str, pa_json_object *obj) { + pa_strbuf *buf = pa_strbuf_new(); + + str++; /* Consume leading '"' */ + + while (*str && *str != '"') { + if (*str != '\\') { + /* We only accept ASCII printable characters. */ + if (*str < 0x20 || *str > 0x7E) { + pa_log("Invalid non-ASCII character: 0x%x", (unsigned int) *str); + goto error; + } + + /* Normal character, juts consume */ + pa_strbuf_putc(buf, *str); + } else { + /* Need to unescape */ + str++; + + switch (*str) { + case '"': + case '\\': + case '/': + pa_strbuf_putc(buf, *str); + break; + + case 'b': + pa_strbuf_putc(buf, '\b' /* backspace */); + break; + + case 'f': + pa_strbuf_putc(buf, '\f' /* form feed */); + break; + + case 'n': + pa_strbuf_putc(buf, '\n' /* new line */); + break; + + case 'r': + pa_strbuf_putc(buf, '\r' /* carriage return */); + break; + + case 't': + pa_strbuf_putc(buf, '\t' /* horizontal tab */); + break; + + case 'u': + pa_log("Unicode code points are currently unsupported"); + goto error; + + default: + pa_log("Unexepcted escape value: %c", *str); + goto error; + } + } + + str++; + } + + if (*str != '"') { + pa_log("Failed to parse remainder of string: %s", str); + goto error; + } + + str++; + + obj->type = PA_JSON_TYPE_STRING; + obj->string_value = pa_strbuf_to_string_free(buf); + + return str; + +error: + pa_strbuf_free(buf); + return NULL; +} + +static const char* parse_number(const char *str, pa_json_object *obj) { + bool negative = false, has_fraction = false, has_exponent = false, valid = false; + unsigned int integer = 0; + unsigned int fraction = 0; + unsigned int fraction_digits = 0; + int exponent = 0; + + if (*str == '-') { + negative = true; + str++; + } + + if (*str == '0') { + valid = true; + str++; + goto fraction; + } + + while (is_digit(*str)) { + valid = true; + + if (integer > ((negative ? INT_MAX : UINT_MAX) / 10)) { + pa_log("Integer overflow while parsing number"); + goto error; + } + + integer = (integer * 10) + (*str - '0'); + str++; + } + +fraction: + + if (!valid) { + pa_log("Missing digits while parsing number"); + goto error; + } + + if (*str == '.') { + has_fraction = true; + str++; + valid = false; + + while (is_digit(*str)) { + valid = true; + + if (fraction > (UINT_MAX / 10)) { + pa_log("Integer overflow while parsing fractional part of number"); + goto error; + } + + fraction = (fraction * 10) + (*str - '0'); + fraction_digits++; + str++; + } + + if (!valid) { + pa_log("No digit after '.' while parsing fraction"); + goto error; + } + } + + if (*str == 'e' || *str == 'E') { + bool exponent_negative = false; + + has_exponent = true; + str++; + valid = false; + + if (*str == '-') { + exponent_negative = true; + str++; + } else if (*str == '+') + str++; + + while (is_digit(*str)) { + valid = true; + + if (exponent > (INT_MAX / 10)) { + pa_log("Integer overflow while parsing exponent part of number"); + goto error; + } + + exponent = (exponent * 10) + (*str - '0'); + str++; + } + + if (!valid) { + pa_log("No digit in exponent while parsing fraction"); + goto error; + } + + if (exponent_negative) + exponent *= -1; + } + + if (has_fraction || has_exponent) { + obj->type = PA_JSON_TYPE_DOUBLE; + obj->double_value = + (negative ? -1.0 : 1.0) * (integer + (double) fraction / pow(10, fraction_digits)) * pow(10, exponent); + } else { + obj->type = PA_JSON_TYPE_INT; + obj->int_value = (negative ? -1 : 1) * integer; + } + + return str; + +error: + return NULL; +} + +static const char *parse_object(const char *str, pa_json_object *obj, unsigned int depth) { + pa_json_object *name = NULL, *value = NULL; + pa_json_item *item; + + obj->values = PW_ARRAY_INIT(64); + + while (*str != '}') { + str++; /* Consume leading '{' or ',' */ + + str = parse_value(str, ":", &name, depth + 1); + if (!str || pa_json_object_get_type(name) != PA_JSON_TYPE_STRING) { + pa_log("Could not parse key for object"); + goto error; + } + + /* Consume the ':' */ + str++; + + str = parse_value(str, ",}", &value, depth + 1); + if (!str) { + pa_log("Could not parse value for object"); + goto error; + } + + item = pw_array_add(&obj->values, sizeof(pa_json_item)); + item->key = strdup(pa_json_object_get_string(name)); + item->value = value; + pa_json_object_free(name); + + name = NULL; + value = NULL; + } + + /* Drop trailing '}' */ + str++; + + /* We now know the value was correctly parsed */ + obj->type = PA_JSON_TYPE_OBJECT; + + return str; + +error: + clear_object(&obj->values); + + if (name) + pa_json_object_free(name); + if (value) + pa_json_object_free(value); + + return NULL; +} + +static const char *parse_array(const char *str, pa_json_object *obj, unsigned int depth) { + pa_json_object *value; + + obj->values = PW_ARRAY_INIT(64); + + while (*str != ']') { + str++; /* Consume leading '[' or ',' */ + + /* Need to chew up whitespaces as a special case to deal with the + * possibility of an empty array */ + while (is_whitespace(*str)) + str++; + + if (*str == ']') + break; + + str = parse_value(str, ",]", &value, depth + 1); + if (!str) { + pa_log("Could not parse value for array"); + goto error; + } + + pw_array_add_ptr(&obj->values, value); + } + + /* Drop trailing ']' */ + str++; + + /* We now know the value was correctly parsed */ + obj->type = PA_JSON_TYPE_ARRAY; + + return str; + +error: + clear_array(&obj->values); + return NULL; +} + +typedef enum { + JSON_PARSER_STATE_INIT, + JSON_PARSER_STATE_FINISH, +} json_parser_state; + +static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth) { + json_parser_state state = JSON_PARSER_STATE_INIT; + pa_json_object *o; + + pa_assert(str != NULL); + + o = json_object_new(); + + if (depth > MAX_NESTING_DEPTH) { + pa_log("Exceeded maximum permitted nesting depth of objects (%u)", MAX_NESTING_DEPTH); + goto error; + } + + while (!is_end(*str, end)) { + switch (state) { + case JSON_PARSER_STATE_INIT: + if (is_whitespace(*str)) { + str++; + } else if (*str == 'n') { + str = parse_null(str, o); + state = JSON_PARSER_STATE_FINISH; + } else if (*str == 't' || *str == 'f') { + str = parse_boolean(str, o); + state = JSON_PARSER_STATE_FINISH; + } else if (*str == '"') { + str = parse_string(str, o); + state = JSON_PARSER_STATE_FINISH; + } else if (is_digit(*str) || *str == '-') { + str = parse_number(str, o); + state = JSON_PARSER_STATE_FINISH; + } else if (*str == '{') { + str = parse_object(str, o, depth); + state = JSON_PARSER_STATE_FINISH; + } else if (*str == '[') { + str = parse_array(str, o, depth); + state = JSON_PARSER_STATE_FINISH; + } else { + pa_log("Invalid JSON string: %s", str); + goto error; + } + + if (!str) + goto error; + + break; + + case JSON_PARSER_STATE_FINISH: + /* Consume trailing whitespaces */ + if (is_whitespace(*str)) { + str++; + } else { + goto error; + } + } + } + + if (pa_json_object_get_type(o) == PA_JSON_TYPE_INIT) { + /* We didn't actually get any data */ + pa_log("No data while parsing json string: '%s' till '%s'", str, pa_strnull(end)); + goto error; + } + + *obj = o; + + return str; + +error: + pa_json_object_free(o); + return NULL; +} + + +pa_json_object* pa_json_parse(const char *str) { + pa_json_object *obj; + + str = parse_value(str, NULL, &obj, 0); + + if (!str) { + pa_log("JSON parsing failed"); + return NULL; + } + + if (*str != '\0') { + pa_log("Unable to parse complete JSON string, remainder is: %s", str); + pa_json_object_free(obj); + return NULL; + } + + return obj; +} + +pa_json_type pa_json_object_get_type(const pa_json_object *obj) { + return obj->type; +} + +void pa_json_object_free(pa_json_object *obj) { + + switch (pa_json_object_get_type(obj)) { + case PA_JSON_TYPE_INIT: + case PA_JSON_TYPE_INT: + case PA_JSON_TYPE_DOUBLE: + case PA_JSON_TYPE_BOOL: + case PA_JSON_TYPE_NULL: + break; + + case PA_JSON_TYPE_STRING: + pa_xfree(obj->string_value); + break; + + case PA_JSON_TYPE_OBJECT: + clear_object(&obj->values); + break; + + case PA_JSON_TYPE_ARRAY: + clear_array(&obj->values); + break; + + default: + pa_assert_not_reached(); + } + + pa_xfree(obj); +} + +int pa_json_object_get_int(const pa_json_object *o) { + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT); + return o->int_value; +} + +double pa_json_object_get_double(const pa_json_object *o) { + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_DOUBLE); + return o->double_value; +} + +bool pa_json_object_get_bool(const pa_json_object *o) { + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_BOOL); + return o->bool_value; +} + +const char* pa_json_object_get_string(const pa_json_object *o) { + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_STRING); + return o->string_value; +} + +const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name) { + pa_json_item *item; + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT); + pw_array_for_each(item, &o->values) { + if (pa_streq(item->key, name)) + return item->value; + } + return NULL; +} + +int pa_json_object_get_array_length(const pa_json_object *o) { + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY); + return pw_array_get_len(&o->values, const pa_json_object*); +} + +const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index) { + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY); + return pw_array_get_unchecked_s(&o->values, index, sizeof(pa_json_object*), + const pa_json_object); +} + +bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2) { + int i; + + if (pa_json_object_get_type(o1) != pa_json_object_get_type(o2)) + return false; + + switch (pa_json_object_get_type(o1)) { + case PA_JSON_TYPE_NULL: + return true; + + case PA_JSON_TYPE_BOOL: + return o1->bool_value == o2->bool_value; + + case PA_JSON_TYPE_INT: + return o1->int_value == o2->int_value; + + case PA_JSON_TYPE_DOUBLE: + return PA_DOUBLE_IS_EQUAL(o1->double_value, o2->double_value); + + case PA_JSON_TYPE_STRING: + return pa_streq(o1->string_value, o2->string_value); + + case PA_JSON_TYPE_ARRAY: + if (pa_json_object_get_array_length(o1) != pa_json_object_get_array_length(o2)) + return false; + + for (i = 0; i < pa_json_object_get_array_length(o1); i++) { + if (!pa_json_object_equal(pa_json_object_get_array_member(o1, i), + pa_json_object_get_array_member(o2, i))) + return false; + } + + return true; + + case PA_JSON_TYPE_OBJECT: { + const pa_json_object *value; + const pa_json_item *item; + + if (o1->values.size != o2->values.size) + return false; + + pw_array_for_each(item, &o1->values) { + value = pa_json_object_get_object_member(o2, item->key); + if (!value || !pa_json_object_equal(item->value, value)) + return false; + } + + return true; + } + + default: + pa_assert_not_reached(); + } +} diff --git a/src/json.h b/src/json.h new file mode 100644 index 000000000..7759bf2db --- /dev/null +++ b/src/json.h @@ -0,0 +1,53 @@ +/*** + This file is part of PulseAudio. + + Copyright 2016 Arun Raghavan + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#include + +#define PA_DOUBLE_IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001) + +typedef enum { + PA_JSON_TYPE_INIT = 0, + PA_JSON_TYPE_NULL, + PA_JSON_TYPE_INT, + PA_JSON_TYPE_DOUBLE, + PA_JSON_TYPE_BOOL, + PA_JSON_TYPE_STRING, + PA_JSON_TYPE_ARRAY, + PA_JSON_TYPE_OBJECT, +} pa_json_type; + +typedef struct pa_json_object pa_json_object; + +pa_json_object* pa_json_parse(const char *str); +pa_json_type pa_json_object_get_type(const pa_json_object *obj); +void pa_json_object_free(pa_json_object *obj); + +/* All pointer members that are returned are valid while the corresponding object is valid */ + +int pa_json_object_get_int(const pa_json_object *o); +double pa_json_object_get_double(const pa_json_object *o); +bool pa_json_object_get_bool(const pa_json_object *o); +const char* pa_json_object_get_string(const pa_json_object *o); + +const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name); + +int pa_json_object_get_array_length(const pa_json_object *o); +const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index); + +bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2); diff --git a/src/mainloop-signal.c b/src/mainloop-signal.c new file mode 100644 index 000000000..8fb1b1ae1 --- /dev/null +++ b/src/mainloop-signal.c @@ -0,0 +1,109 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include + +#include +#include + +#include + +#include "internal.h" + + +static pa_mainloop_api *api = NULL; +static struct spa_list signals; +static struct pw_loop *loop = NULL; + +struct pa_signal_event { + struct spa_list link; + struct spa_source *source; + pa_signal_cb_t callback; + pa_signal_destroy_cb_t destroy; + void *userdata; +}; + +int pa_signal_init(pa_mainloop_api *a) +{ + pa_assert(a); + pa_assert(!api); + + api = a; + spa_list_init(&signals); + loop = a->userdata; + + return 0; +} + +void pa_signal_done(void) +{ + pa_signal_event *ev, *t; + + pa_assert(api); + + spa_list_for_each_safe(ev, t, &signals, link) + pa_signal_free(ev); + spa_list_init(&signals); + + api = NULL; +} + +static void source_signal_func (void *data, int signal_number) +{ + pa_signal_event *ev = data; + if (ev->callback) + ev->callback(api, ev, signal_number, ev->userdata); +} + +pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t callback, void *userdata) +{ + pa_signal_event *ev; + + pa_assert(sig > 0); + pa_assert(callback); + + ev = calloc(1, sizeof(pa_signal_event)); + ev->source = spa_loop_utils_add_signal(loop->utils, sig, source_signal_func, ev); + ev->callback = callback; + ev->userdata = userdata; + + spa_list_append(&signals, &ev->link); + + return ev; +} + +void pa_signal_free(pa_signal_event *e) +{ + pa_assert(e); + + spa_list_remove(&e->link); + spa_loop_utils_destroy_source(loop->utils, e->source); + if (e->destroy) + e->destroy(api, e, e->userdata); + free(e); +} + +void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t callback) +{ + pa_assert(e); + e->destroy = callback; +} diff --git a/src/mainloop.c b/src/mainloop.c new file mode 100644 index 000000000..da1ae450b --- /dev/null +++ b/src/mainloop.c @@ -0,0 +1,251 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include + +#include + +#include "internal.h" + +static void do_stop(void *data, uint64_t count) +{ + struct pa_mainloop *this = data; + this->quit = true; +} + +static pa_io_event* api_io_new(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +static void api_io_enable(pa_io_event* e, pa_io_event_flags_t events) +{ + pw_log_warn("Not Implemented"); +} + +static void api_io_free(pa_io_event* e) +{ + pw_log_warn("Not Implemented"); +} + +static void api_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb) +{ + pw_log_warn("Not Implemented"); +} + +static pa_time_event* api_time_new(pa_mainloop_api*a, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +static void api_time_restart(pa_time_event* e, const struct timeval *tv) +{ + pw_log_warn("Not Implemented"); +} + +static void api_time_free(pa_time_event* e) +{ + pw_log_warn("Not Implemented"); +} + +static void api_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) +{ + pw_log_warn("Not Implemented"); +} + + +static pa_defer_event* api_defer_new(pa_mainloop_api*a, pa_defer_event_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +static void api_defer_enable(pa_defer_event* e, int b) +{ + pw_log_warn("Not Implemented"); +} + +static void api_defer_free(pa_defer_event* e) +{ + pw_log_warn("Not Implemented"); +} + +static void api_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb) +{ + pw_log_warn("Not Implemented"); +} + +static void api_quit(pa_mainloop_api*a, int retval) +{ + pa_mainloop *m = SPA_CONTAINER_OF(a, pa_mainloop, api); + m->quit = true; + m->retval = retval; + pa_mainloop_wakeup(m); +} + +static const pa_mainloop_api api = +{ + .io_new = api_io_new, + .io_enable = api_io_enable, + .io_free = api_io_free, + .io_set_destroy = api_io_set_destroy, + + .time_new = api_time_new, + .time_restart = api_time_restart, + .time_free = api_time_free, + .time_set_destroy = api_time_set_destroy, + + .defer_new = api_defer_new, + .defer_enable = api_defer_enable, + .defer_free = api_defer_free, + .defer_set_destroy = api_defer_set_destroy, + + .quit = api_quit, + +}; + +pa_mainloop *pa_mainloop_new(void) +{ + pa_mainloop *loop; + + loop = calloc(1, sizeof(pa_mainloop)); + if (loop == NULL) + return NULL; + + loop->loop = pw_loop_new(NULL); + if (loop->loop == NULL) + goto no_loop; + + loop->event = pw_loop_add_event(loop->loop, do_stop, loop); + loop->api = api; + loop->api.userdata = loop->loop; + + return loop; + + no_loop: + free(loop); + return NULL; +} + +void pa_mainloop_free(pa_mainloop* m) +{ + pw_loop_destroy(m->loop); + free(m); +} + +int pa_mainloop_prepare(pa_mainloop *m, int timeout) +{ + if (m->quit) + return -2; + m->timeout = timeout; + m->n_events = -EIO; + return 0; +} + +/** Execute the previously prepared poll. Returns a negative value on error.*/ +int pa_mainloop_poll(pa_mainloop *m) +{ + if (m->quit) + return -2; + + return m->n_events = pw_loop_iterate(m->loop, m->timeout); +} + +int pa_mainloop_dispatch(pa_mainloop *m) +{ + if (m->quit) + return -2; + + return m->n_events; +} + +int pa_mainloop_get_retval(pa_mainloop *m) +{ + return m->retval; +} + +/** Run a single iteration of the main loop. This is a convenience function +for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch(). +Returns a negative value on error or exit request. If block is nonzero, +block for events if none are queued. Optionally return the return value as +specified with the main loop's quit() routine in the integer variable retval points +to. On success returns the number of sources dispatched in this iteration. */ +int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) +{ + int r; + pa_assert(m); + + if ((r = pa_mainloop_prepare(m, block ? -1 : 0)) < 0) + goto quit; + + if ((r = pa_mainloop_poll(m)) < 0) + goto quit; + + if ((r = pa_mainloop_dispatch(m)) < 0) + goto quit; + + return r; + + quit: + if ((r == -2) && retval) + *retval = pa_mainloop_get_retval(m); + return r; +} + +int pa_mainloop_run(pa_mainloop *m, int *retval) +{ + int r; + + while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0) + ; + + if (r == -2) + return 1; + else + return -1; +} + + +pa_mainloop_api* pa_mainloop_get_api(pa_mainloop *m) +{ + pa_assert(m); + return &m->api; +} + +void pa_mainloop_quit(pa_mainloop *m, int retval) +{ + pa_assert(m); + m->api.quit(&m->api, retval); +} + +void pa_mainloop_wakeup(pa_mainloop *m) +{ + pa_assert(m); + pw_loop_signal_event(m->loop, m->event); +} + +void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) +{ + pw_log_warn("Not Implemented"); +} diff --git a/src/map-file b/src/map-file new file mode 100644 index 000000000..9b6cba223 --- /dev/null +++ b/src/map-file @@ -0,0 +1,386 @@ +PULSE_0 { +global: +pa_ascii_filter; +pa_ascii_valid; +pa_bytes_per_second; +pa_bytes_snprint; +pa_bytes_to_usec; +pa_channel_map_can_balance; +pa_channel_map_can_fade; +pa_channel_map_can_lfe_balance; +pa_channel_map_compatible; +pa_channel_map_equal; +pa_channel_map_has_position; +pa_channel_map_init; +pa_channel_map_init_auto; +pa_channel_map_init_extend; +pa_channel_map_init_mono; +pa_channel_map_init_stereo; +pa_channel_map_mask; +pa_channel_map_parse; +pa_channel_map_snprint; +pa_channel_map_superset; +pa_channel_map_to_name; +pa_channel_map_to_pretty_name; +pa_channel_map_valid; +pa_channel_position_from_string; +pa_channel_position_to_pretty_string; +pa_channel_position_to_string; +pa_channels_valid; +pa_context_add_autoload; +pa_context_connect; +pa_context_disconnect; +pa_context_drain; +pa_context_errno; +pa_context_exit_daemon; +pa_context_get_autoload_info_by_index; +pa_context_get_autoload_info_by_name; +pa_context_get_autoload_info_list; +pa_context_get_card_info_by_index; +pa_context_get_card_info_by_name; +pa_context_get_card_info_list; +pa_context_get_client_info; +pa_context_get_client_info_list; +pa_context_get_index; +pa_context_get_module_info; +pa_context_get_module_info_list; +pa_context_get_protocol_version; +pa_context_get_sample_info_by_index; +pa_context_get_sample_info_by_name; +pa_context_get_sample_info_list; +pa_context_get_server; +pa_context_get_server_info; +pa_context_get_server_protocol_version; +pa_context_get_sink_info_by_index; +pa_context_get_sink_info_by_name; +pa_context_get_sink_info_list; +pa_context_get_sink_input_info; +pa_context_get_sink_input_info_list; +pa_context_get_source_info_by_index; +pa_context_get_source_info_by_name; +pa_context_get_source_info_list; +pa_context_get_source_output_info; +pa_context_get_source_output_info_list; +pa_context_set_port_latency_offset; +pa_context_get_state; +pa_context_get_tile_size; +pa_context_is_local; +pa_context_is_pending; +pa_context_kill_client; +pa_context_kill_sink_input; +pa_context_kill_source_output; +pa_context_load_cookie_from_file; +pa_context_load_module; +pa_context_move_sink_input_by_index; +pa_context_move_sink_input_by_name; +pa_context_move_source_output_by_index; +pa_context_move_source_output_by_name; +pa_context_new; +pa_context_new_with_proplist; +pa_context_play_sample; +pa_context_play_sample_with_proplist; +pa_context_proplist_remove; +pa_context_proplist_update; +pa_context_ref; +pa_context_remove_autoload_by_index; +pa_context_remove_autoload_by_name; +pa_context_remove_sample; +pa_context_rttime_new; +pa_context_rttime_restart; +pa_context_set_card_profile_by_index; +pa_context_set_card_profile_by_name; +pa_context_set_default_sink; +pa_context_set_default_source; +pa_context_set_event_callback; +pa_context_set_name; +pa_context_set_sink_input_mute; +pa_context_set_sink_input_volume; +pa_context_set_sink_mute_by_index; +pa_context_set_sink_mute_by_name; +pa_context_set_sink_port_by_index; +pa_context_set_sink_port_by_name; +pa_context_set_sink_volume_by_index; +pa_context_set_sink_volume_by_name; +pa_context_set_source_output_mute; +pa_context_set_source_output_volume; +pa_context_set_source_mute_by_index; +pa_context_set_source_mute_by_name; +pa_context_set_source_port_by_index; +pa_context_set_source_port_by_name; +pa_context_set_source_volume_by_index; +pa_context_set_source_volume_by_name; +pa_context_set_state_callback; +pa_context_set_subscribe_callback; +pa_context_stat; +pa_context_subscribe; +pa_context_suspend_sink_by_index; +pa_context_suspend_sink_by_name; +pa_context_suspend_source_by_index; +pa_context_suspend_source_by_name; +pa_context_unload_module; +pa_context_unref; +pa_cvolume_avg; +pa_cvolume_avg_mask; +pa_cvolume_channels_equal_to; +pa_cvolume_compatible; +pa_cvolume_compatible_with_channel_map; +pa_cvolume_dec; +pa_cvolume_equal; +pa_cvolume_get_balance; +pa_cvolume_get_fade; +pa_cvolume_get_lfe_balance; +pa_cvolume_get_position; +pa_cvolume_inc; +pa_cvolume_inc_clamp; +pa_cvolume_init; +pa_cvolume_max; +pa_cvolume_max_mask; +pa_cvolume_merge; +pa_cvolume_min; +pa_cvolume_min_mask; +pa_cvolume_remap; +pa_cvolume_scale; +pa_cvolume_scale_mask; +pa_cvolume_set; +pa_cvolume_set_balance; +pa_cvolume_set_fade; +pa_cvolume_set_lfe_balance; +pa_cvolume_set_position; +pa_cvolume_snprint; +pa_cvolume_snprint_verbose; +pa_cvolume_valid; +pa_direction_to_string; +pa_direction_valid; +pa_encoding_from_string; +pa_encoding_to_string; +pa_ext_device_manager_delete; +pa_ext_device_manager_enable_role_device_priority_routing; +pa_ext_device_manager_read; +pa_ext_device_manager_reorder_devices_for_role; +pa_ext_device_manager_set_device_description; +pa_ext_device_manager_set_subscribe_cb; +pa_ext_device_manager_subscribe; +pa_ext_device_manager_test; +pa_ext_device_restore_read_formats; +pa_ext_device_restore_read_formats_all; +pa_ext_device_restore_save_formats; +pa_ext_device_restore_set_subscribe_cb; +pa_ext_device_restore_subscribe; +pa_ext_device_restore_test; +pa_ext_stream_restore_delete; +pa_ext_stream_restore_read; +pa_ext_stream_restore_set_subscribe_cb; +pa_ext_stream_restore_subscribe; +pa_ext_stream_restore_test; +pa_ext_stream_restore_write; +pa_format_info_copy; +pa_format_info_free; +pa_format_info_from_string; +pa_format_info_from_sample_spec; +pa_format_info_get_prop_type; +pa_format_info_get_prop_int; +pa_format_info_get_prop_int_range; +pa_format_info_get_prop_int_array; +pa_format_info_get_prop_string; +pa_format_info_get_prop_string_array; +pa_format_info_free_string_array; +pa_format_info_is_compatible; +pa_format_info_is_pcm; +pa_format_info_new; +pa_format_info_set_channel_map; +pa_format_info_set_channels; +pa_format_info_set_prop_int; +pa_format_info_set_prop_int_array; +pa_format_info_set_prop_int_range; +pa_format_info_set_prop_string; +pa_format_info_set_prop_string_array; +pa_format_info_set_rate; +pa_format_info_set_sample_format; +pa_format_info_snprint; +pa_format_info_to_sample_spec; +pa_format_info_valid; +pa_frame_size; +pa_get_binary_name; +pa_get_fqdn; +pa_get_home_dir; +pa_get_host_name; +pa_get_library_version; +pa_gettimeofday; +pa_get_user_name; +pa_glib_mainloop_free; +pa_glib_mainloop_get_api; +pa_glib_mainloop_new; +pa_locale_to_utf8; +pa_mainloop_api_once; +pa_mainloop_dispatch; +pa_mainloop_free; +pa_mainloop_get_api; +pa_mainloop_get_retval; +pa_mainloop_iterate; +pa_mainloop_new; +pa_mainloop_poll; +pa_mainloop_prepare; +pa_mainloop_quit; +pa_mainloop_run; +pa_mainloop_set_poll_func; +pa_mainloop_wakeup; +pa_msleep; +pa_operation_cancel; +pa_operation_get_state; +pa_operation_ref; +pa_operation_set_state_callback; +pa_operation_unref; +pa_parse_sample_format; +pa_path_get_filename; +pa_proplist_clear; +pa_proplist_contains; +pa_proplist_copy; +pa_proplist_equal; +pa_proplist_free; +pa_proplist_from_string; +pa_proplist_get; +pa_proplist_gets; +pa_proplist_isempty; +pa_proplist_iterate; +pa_proplist_key_valid; +pa_proplist_new; +pa_proplist_set; +pa_proplist_setf; +pa_proplist_setp; +pa_proplist_sets; +pa_proplist_size; +pa_proplist_to_string; +pa_proplist_to_string_sep; +pa_proplist_unset; +pa_proplist_unset_many; +pa_proplist_update; +pa_rtclock_now; +pa_sample_format_is_be; +pa_sample_format_is_le; +pa_sample_format_to_string; +pa_sample_format_valid; +pa_sample_rate_valid; +pa_sample_size; +pa_sample_size_of_format; +pa_sample_spec_equal; +pa_sample_spec_init; +pa_sample_spec_snprint; +pa_sample_spec_valid; +pa_signal_done; +pa_signal_free; +pa_signal_init; +pa_signal_new; +pa_signal_set_destroy; +pa_simple_drain; +pa_simple_flush; +pa_simple_free; +pa_simple_get_latency; +pa_simple_new; +pa_simple_read; +pa_simple_write; +pa_stream_begin_write; +pa_stream_cancel_write; +pa_stream_connect_playback; +pa_stream_connect_record; +pa_stream_connect_upload; +pa_stream_cork; +pa_stream_disconnect; +pa_stream_drain; +pa_stream_drop; +pa_stream_finish_upload; +pa_stream_flush; +pa_stream_get_buffer_attr; +pa_stream_get_channel_map; +pa_stream_get_context; +pa_stream_get_device_index; +pa_stream_get_device_name; +pa_stream_get_format_info; +pa_stream_get_index; +pa_stream_get_latency; +pa_stream_get_monitor_stream; +pa_stream_get_sample_spec; +pa_stream_get_state; +pa_stream_get_time; +pa_stream_get_timing_info; +pa_stream_get_underflow_index; +pa_stream_is_corked; +pa_stream_is_suspended; +pa_stream_new; +pa_stream_new_extended; +pa_stream_new_with_proplist; +pa_stream_peek; +pa_stream_prebuf; +pa_stream_proplist_remove; +pa_stream_proplist_update; +pa_stream_readable_size; +pa_stream_ref; +pa_stream_set_buffer_attr; +pa_stream_set_buffer_attr_callback; +pa_stream_set_event_callback; +pa_stream_set_latency_update_callback; +pa_stream_set_monitor_stream; +pa_stream_set_moved_callback; +pa_stream_set_name; +pa_stream_set_overflow_callback; +pa_stream_set_read_callback; +pa_stream_set_started_callback; +pa_stream_set_state_callback; +pa_stream_set_suspended_callback; +pa_stream_set_underflow_callback; +pa_stream_set_write_callback; +pa_stream_trigger; +pa_stream_unref; +pa_stream_update_sample_rate; +pa_stream_update_timing_info; +pa_stream_writable_size; +pa_stream_write; +pa_stream_write_ext_free; +pa_strerror; +pa_sw_cvolume_divide; +pa_sw_cvolume_divide_scalar; +pa_sw_cvolume_multiply; +pa_sw_cvolume_multiply_scalar; +pa_sw_cvolume_snprint_dB; +pa_sw_volume_divide; +pa_sw_volume_from_dB; +pa_sw_volume_from_linear; +pa_sw_volume_multiply; +pa_sw_volume_snprint_dB; +pa_sw_volume_to_dB; +pa_sw_volume_to_linear; +pa_threaded_mainloop_accept; +pa_threaded_mainloop_free; +pa_threaded_mainloop_get_api; +pa_threaded_mainloop_get_retval; +pa_threaded_mainloop_in_thread; +pa_threaded_mainloop_lock; +pa_threaded_mainloop_new; +pa_threaded_mainloop_set_name; +pa_threaded_mainloop_signal; +pa_threaded_mainloop_start; +pa_threaded_mainloop_stop; +pa_threaded_mainloop_unlock; +pa_threaded_mainloop_wait; +pa_timeval_add; +pa_timeval_age; +pa_timeval_cmp; +pa_timeval_diff; +pa_timeval_load; +pa_timeval_store; +pa_timeval_sub; +pa_usec_to_bytes; +pa_utf8_filter; +pa_utf8_to_locale; +pa_utf8_valid; +pa_volume_snprint; +pa_volume_snprint_verbose; +pa_xfree; +pa_xmalloc; +pa_xmalloc0; +pa_xmemdup; +pa_xrealloc; +pa_xstrdup; +pa_xstrndup; +local: +*; +}; diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 000000000..1a7e58c63 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,47 @@ +pipewire_pulseaudio_sources = [ + 'bitset.c', + 'channelmap.c', + 'context.c', + 'core-format.c', + 'direction.c', + 'error.c', + 'ext-device-manager.c', + 'ext-device-restore.c', + 'format.c', + 'introspect.c', + 'json.c', + 'mainloop.c', + 'mainloop-signal.c', + 'operation.c', + 'proplist.c', + 'rtclock.c', + 'sample.c', + 'scache.c', + 'stream.c', + 'strbuf.c', + 'subscribe.c', + 'utf8.c', + 'util.c', + 'version.c', + 'volume.c', + 'xmalloc.c', + 'pipewire-pulseaudio.c', +] + +pipewire_pulseaudio_c_args = [ + '-DHAVE_CONFIG_H', + '-DPIC', +] + +mapfile = 'map-file' +vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile) + +pipewire_pulseaudio = shared_library('pulse', + pipewire_pulseaudio_sources, + soversion : '0', + c_args : pipewire_pulseaudio_c_args, + link_args : vflag, + include_directories : [configinc], + dependencies : [pipewire_dep, pulseaudio_dep, mathlib], + install : false, +) diff --git a/src/operation.c b/src/operation.c new file mode 100644 index 000000000..fa0c58735 --- /dev/null +++ b/src/operation.c @@ -0,0 +1,153 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include + +#include + +#include "internal.h" + +pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, size_t userdata_size) { + pa_operation *o; + pa_assert(c); + + o = calloc(1, sizeof(pa_operation) + userdata_size); + + o->refcount = 1; + o->context = c; + o->stream = s; + o->seq = ++c->seq; + + o->state = PA_OPERATION_RUNNING; + o->callback = cb; + o->userdata = SPA_MEMBER(o, sizeof(pa_operation), void); + + spa_list_append(&c->operations, &o->link); + pa_operation_ref(o); + + pw_log_debug("new %p %d", o, o->seq); + pw_core_proxy_sync(c->core_proxy, o->seq); + + return o; +} + + +pa_operation *pa_operation_ref(pa_operation *o) +{ + pa_assert(o); + pa_assert(o->refcount >= 1); + o->refcount++; + return o; +} + +static void operation_free(pa_operation *o) +{ + pa_assert(!o->context); + pa_assert(!o->stream); + pw_log_debug("%p %d", o, o->seq); + free(o); +} + +static void operation_unlink(pa_operation *o) { + pa_assert(o); + + pw_log_debug("%p %d", o, o->seq); + if (o->context) { + pa_assert(o->refcount >= 2); + + spa_list_remove(&o->link); + pa_operation_unref(o); + + o->context = NULL; + } + + o->stream = NULL; + o->callback = NULL; + o->userdata = NULL; + o->state_callback = NULL; + o->state_userdata = NULL; +} + + +void pa_operation_unref(pa_operation *o) +{ + pa_assert(o); + pa_assert(o->refcount >= 1); + if (--o->refcount == 0) + operation_free(o); +} + +static void operation_set_state(pa_operation *o, pa_operation_state_t st) { + pa_assert(o); + pa_assert(o->refcount >= 1); + + if (st == o->state) + return; + + pa_operation_ref(o); + + pw_log_debug("new state %p %d %d", o, o->seq, st); + o->state = st; + + if (o->state_callback) + o->state_callback(o, o->state_userdata); + + if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) + operation_unlink(o); + + pa_operation_unref(o); +} + + +void pa_operation_cancel(pa_operation *o) +{ + pa_assert(o); + pa_assert(o->refcount >= 1); + operation_set_state(o, PA_OPERATION_CANCELED); +} + +void pa_operation_done(pa_operation *o) { + pa_assert(o); + pa_assert(o->refcount >= 1); + operation_set_state(o, PA_OPERATION_DONE); +} + + +pa_operation_state_t pa_operation_get_state(pa_operation *o) +{ + pa_assert(o); + pa_assert(o->refcount >= 1); + return o->state; +} + +void pa_operation_set_state_callback(pa_operation *o, pa_operation_notify_cb_t cb, void *userdata) +{ + pa_assert(o); + pa_assert(o->refcount >= 1); + + if (o->state == PA_OPERATION_DONE || o->state == PA_OPERATION_CANCELED) + return; + + o->state_callback = cb; + o->state_userdata = userdata; +} diff --git a/src/pipewire-pulseaudio.c b/src/pipewire-pulseaudio.c new file mode 100644 index 000000000..3b3405984 --- /dev/null +++ b/src/pipewire-pulseaudio.c @@ -0,0 +1,30 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +static void reg(void) __attribute__ ((constructor)); +static void reg(void) +{ + pw_init(NULL, NULL); +} diff --git a/src/proplist.c b/src/proplist.c new file mode 100644 index 000000000..999b86d6e --- /dev/null +++ b/src/proplist.c @@ -0,0 +1,271 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include + +#include + +#include "internal.h" +#include "strbuf.h" + +pa_proplist* pa_proplist_new_dict(struct spa_dict *dict) +{ + pa_proplist *p; + + p = calloc(1, sizeof(struct pa_proplist)); + if (p == NULL) + return NULL; + + if (dict) + p->props = pw_properties_new_dict(dict); + else + p->props = pw_properties_new(NULL, NULL); + + return p; +} +pa_proplist* pa_proplist_new_props(struct pw_properties *props) +{ + return pa_proplist_new_dict(&props->dict); +} + +pa_proplist* pa_proplist_new(void) +{ + return pa_proplist_new_dict(NULL); +} + +void pa_proplist_free(pa_proplist* p) +{ + pw_properties_free(p->props); + free(p); +} + +int pa_proplist_key_valid(const char *key) +{ + const char *p; + for (p = key; *p; p++) + if ((unsigned char) *p >= 128) + return 0; + + if (strlen(key) < 1) + return 0; + + return 1; +} + +int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) +{ + pw_properties_set(p->props, key, value); + return 0; +} + +int pa_proplist_setp(pa_proplist *p, const char *pair) +{ + pw_log_warn("Not Implemented"); + return -1; +} + +int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) +{ + pw_log_warn("Not Implemented"); + return -1; +} + +int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) +{ + pw_log_warn("Not Implemented"); + return -1; +} + +const char *pa_proplist_gets(pa_proplist *p, const char *key) +{ + return pw_properties_get(p->props, key); +} + +int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes) +{ + const char *val; + + spa_assert(p); + spa_assert(key); + + val = pw_properties_get(p->props, key); + + *data = val; + *nbytes = val ? strlen(val) : 0; + return 0; +} + +void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other) +{ + spa_assert(p); + pw_log_warn("Not Implemented"); +} + +int pa_proplist_unset(pa_proplist *p, const char *key) +{ + spa_assert(p); + spa_assert(key); + + if (!pa_proplist_key_valid(key)) + return -1; + + return pw_properties_set(p->props, key, NULL); +} + +int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) +{ + const char * const * k; + int n = 0; + + spa_assert(p); + spa_assert(keys); + + for (k = keys; *k; k++) + if (!pa_proplist_key_valid(*k)) + return -1; + + for (k = keys; *k; k++) + if (pa_proplist_unset(p, *k) >= 0) + n++; + return n; +} + +const char *pa_proplist_iterate(pa_proplist *p, void **state) +{ + spa_assert(p); + spa_assert(state); + return pw_properties_iterate(p->props, state); +} + +char *pa_proplist_to_string(pa_proplist *p) +{ + spa_assert(p); + pw_log_warn("Not Implemented"); + return NULL; +} + +char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) +{ + const char *key; + void *state = NULL; + pa_strbuf *buf; + + spa_assert(p); + spa_assert(sep); + + buf = pa_strbuf_new(); + + while ((key = pa_proplist_iterate(p, &state))) { + const char *v; + const char *t; + + if (!pa_strbuf_isempty(buf)) + pa_strbuf_puts(buf, sep); + + if ((v = pa_proplist_gets(p, key)) == NULL) + continue; + + pa_strbuf_printf(buf, "%s = \"", key); + + for (t = v;;) { + size_t h; + + h = strcspn(t, "\""); + + if (h > 0) + pa_strbuf_putsn(buf, t, h); + + t += h; + + if (*t == 0) + break; + + pa_assert(*t == '"'); + pa_strbuf_puts(buf, "\\\""); + + t++; + } + pa_strbuf_puts(buf, "\""); + } + return pa_strbuf_to_string_free(buf); +} + +pa_proplist *pa_proplist_from_string(const char *str) +{ + spa_assert(str); + pw_log_warn("Not Implemented"); + return NULL; +} + +int pa_proplist_contains(pa_proplist *p, const char *key) +{ + spa_assert(p); + spa_assert(key); + + if (!pa_proplist_key_valid(key)) + return -1; + + if (pw_properties_get(p->props, key) == NULL) + return 0; + + return 1; +} + +void pa_proplist_clear(pa_proplist *p) +{ + spa_assert(p); + pw_log_warn("Not Implemented"); +} + +pa_proplist* pa_proplist_copy(const pa_proplist *p) +{ + pa_proplist *c; + + spa_assert(p); + + c = calloc(1, sizeof(struct pa_proplist)); + if (c == NULL) + return NULL; + + c->props = pw_properties_copy(p->props); + return c; +} + +unsigned pa_proplist_size(pa_proplist *p) +{ + spa_assert(p); + return p->props->dict.n_items; +} + +int pa_proplist_isempty(pa_proplist *p) +{ + spa_assert(p); + return p->props->dict.n_items == 0 ? 1 : 0; +} + +int pa_proplist_equal(pa_proplist *a, pa_proplist *b) +{ + spa_assert(a); + spa_assert(b); + pw_log_warn("Not Implemented"); + return 0; +} diff --git a/src/rtclock.c b/src/rtclock.c new file mode 100644 index 000000000..96ef0fedf --- /dev/null +++ b/src/rtclock.c @@ -0,0 +1,34 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include + +pa_usec_t pa_rtclock_now(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * SPA_USEC_PER_SEC + + ts.tv_nsec * SPA_NSEC_PER_USEC; + +} + diff --git a/src/sample-util.h b/src/sample-util.h new file mode 100644 index 000000000..45528ef6d --- /dev/null +++ b/src/sample-util.h @@ -0,0 +1,134 @@ +#ifndef foosampleutilhfoo +#define foosampleutilhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#include +#include + +#include +#include +#include +#include + +size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE; + +bool pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE; + +void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n); +void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n); + +void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n); + +static inline int32_t pa_mult_s16_volume(int16_t v, int32_t cv) { +#ifdef HAVE_FAST_64BIT_OPERATIONS + /* Multiply with 64 bit integers on 64 bit platforms */ + return (v * (int64_t) cv) >> 16; +#else + /* Multiplying the 32 bit volume factor with the + * 16 bit sample might result in an 48 bit value. We + * want to do without 64 bit integers and hence do + * the multiplication independently for the HI and + * LO part of the volume. */ + + int32_t hi = cv >> 16; + int32_t lo = cv & 0xFFFF; + return ((v * lo) >> 16) + (v * hi); +#endif +} + +pa_usec_t pa_bytes_to_usec_round_up(uint64_t length, const pa_sample_spec *spec); +size_t pa_usec_to_bytes_round_up(pa_usec_t t, const pa_sample_spec *spec); + +typedef void (*pa_do_volume_func_t) (void *samples, const void *volumes, unsigned channels, unsigned length); + +pa_do_volume_func_t pa_get_volume_func(pa_sample_format_t f); +void pa_set_volume_func(pa_sample_format_t f, pa_do_volume_func_t func); + +size_t pa_convert_size(size_t size, const pa_sample_spec *from, const pa_sample_spec *to); + +#define PA_CHANNEL_POSITION_MASK_LEFT \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)) \ + +#define PA_CHANNEL_POSITION_MASK_RIGHT \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT)) + +#define PA_CHANNEL_POSITION_MASK_CENTER \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER)) + +#define PA_CHANNEL_POSITION_MASK_FRONT \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER)) + +#define PA_CHANNEL_POSITION_MASK_REAR \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER)) + +#define PA_CHANNEL_POSITION_MASK_LFE \ + PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE) + +#define PA_CHANNEL_POSITION_MASK_HFE \ + (PA_CHANNEL_POSITION_MASK_REAR | PA_CHANNEL_POSITION_MASK_FRONT \ + | PA_CHANNEL_POSITION_MASK_LEFT | PA_CHANNEL_POSITION_MASK_RIGHT \ + | PA_CHANNEL_POSITION_MASK_CENTER) + +#define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER)) + +#define PA_CHANNEL_POSITION_MASK_TOP \ + (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \ + | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER)) + +#define PA_CHANNEL_POSITION_MASK_ALL \ + ((pa_channel_position_mask_t) (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_MAX)-1)) + +#endif diff --git a/src/sample.c b/src/sample.c new file mode 100644 index 000000000..c5e6437b8 --- /dev/null +++ b/src/sample.c @@ -0,0 +1,319 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + Copyright 2018 Wim Taymans + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include +#include + +#define pa_init_i18n() +#define _(String) (String) + +static const size_t size_table[] = { + [PA_SAMPLE_U8] = 1, + [PA_SAMPLE_ULAW] = 1, + [PA_SAMPLE_ALAW] = 1, + [PA_SAMPLE_S16LE] = 2, + [PA_SAMPLE_S16BE] = 2, + [PA_SAMPLE_FLOAT32LE] = 4, + [PA_SAMPLE_FLOAT32BE] = 4, + [PA_SAMPLE_S32LE] = 4, + [PA_SAMPLE_S32BE] = 4, + [PA_SAMPLE_S24LE] = 3, + [PA_SAMPLE_S24BE] = 3, + [PA_SAMPLE_S24_32LE] = 4, + [PA_SAMPLE_S24_32BE] = 4 +}; + +size_t pa_sample_size_of_format(pa_sample_format_t f) +{ + spa_assert(pa_sample_format_valid(f)); + + return size_table[f]; +} + +size_t pa_sample_size(const pa_sample_spec * spec) +{ + spa_assert(spec); + spa_assert(pa_sample_spec_valid(spec)); + + return size_table[spec->format]; +} + +size_t pa_frame_size(const pa_sample_spec * spec) +{ + spa_assert(spec); + spa_assert(pa_sample_spec_valid(spec)); + + return size_table[spec->format] * spec->channels; +} + +size_t pa_bytes_per_second(const pa_sample_spec * spec) +{ + spa_assert(spec); + spa_assert(pa_sample_spec_valid(spec)); + + return spec->rate * size_table[spec->format] * spec->channels; +} + +pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec * spec) +{ + spa_assert(spec); + spa_assert(pa_sample_spec_valid(spec)); + + return (((pa_usec_t) + (length / (size_table[spec->format] * spec->channels)) + * PA_USEC_PER_SEC) / spec->rate); +} + +size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec * spec) +{ + spa_assert(spec); + spa_assert(pa_sample_spec_valid(spec)); + + return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * + (size_table[spec->format] * spec->channels); +} + +pa_sample_spec *pa_sample_spec_init(pa_sample_spec * spec) +{ + spa_assert(spec); + + spec->format = PA_SAMPLE_INVALID; + spec->rate = 0; + spec->channels = 0; + + return spec; +} + +int pa_sample_format_valid(unsigned format) +{ + return format < PA_SAMPLE_MAX; +} + +int pa_sample_rate_valid(uint32_t rate) +{ + /* The extra 1% is due to module-loopback: it temporarily sets + * a higher-than-nominal rate to get rid of excessive buffer + * latency */ + return rate > 0 && rate <= PA_RATE_MAX * 101 / 100; +} + +int pa_channels_valid(uint8_t channels) +{ + return channels > 0 && channels <= PA_CHANNELS_MAX; +} + +int pa_sample_spec_valid(const pa_sample_spec * spec) +{ + spa_assert(spec); + + if (SPA_UNLIKELY(!pa_sample_rate_valid(spec->rate) || + !pa_channels_valid(spec->channels) || + !pa_sample_format_valid(spec->format))) + return 0; + + return 1; +} + +int pa_sample_spec_equal(const pa_sample_spec * a, const pa_sample_spec * b) +{ + spa_assert(a); + spa_assert(b); + + spa_return_val_if_fail(pa_sample_spec_valid(a), 0); + + if (SPA_UNLIKELY(a == b)) + return 1; + + spa_return_val_if_fail(pa_sample_spec_valid(b), 0); + + return + (a->format == b->format) && + (a->rate == b->rate) && (a->channels == b->channels); +} + +const char *pa_sample_format_to_string(pa_sample_format_t f) +{ + static const char *const table[] = { + [PA_SAMPLE_U8] = "u8", + [PA_SAMPLE_ALAW] = "aLaw", + [PA_SAMPLE_ULAW] = "uLaw", + [PA_SAMPLE_S16LE] = "s16le", + [PA_SAMPLE_S16BE] = "s16be", + [PA_SAMPLE_FLOAT32LE] = "float32le", + [PA_SAMPLE_FLOAT32BE] = "float32be", + [PA_SAMPLE_S32LE] = "s32le", + [PA_SAMPLE_S32BE] = "s32be", + [PA_SAMPLE_S24LE] = "s24le", + [PA_SAMPLE_S24BE] = "s24be", + [PA_SAMPLE_S24_32LE] = "s24-32le", + [PA_SAMPLE_S24_32BE] = "s24-32be", + }; + + if (!pa_sample_format_valid(f)) + return NULL; + + return table[f]; +} + +char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec * spec) +{ + spa_assert(s); + spa_assert(l > 0); + spa_assert(spec); + + pa_init_i18n(); + + if (!pa_sample_spec_valid(spec)) + snprintf(s, l, _("(invalid)")); + else + snprintf(s, l, _("%s %uch %uHz"), + pa_sample_format_to_string(spec->format), + spec->channels, spec->rate); + + return s; +} + +char *pa_bytes_snprint(char *s, size_t l, unsigned v) +{ + spa_assert(s); + spa_assert(l > 0); + + pa_init_i18n(); + + if (v >= ((unsigned)1024) * 1024 * 1024) + snprintf(s, l, _("%0.1f GiB"), + ((double)v) / 1024 / 1024 / 1024); + else if (v >= ((unsigned)1024) * 1024) + snprintf(s, l, _("%0.1f MiB"), ((double)v) / 1024 / 1024); + else if (v >= (unsigned)1024) + snprintf(s, l, _("%0.1f KiB"), ((double)v) / 1024); + else + snprintf(s, l, _("%u B"), (unsigned)v); + + return s; +} + +pa_sample_format_t pa_parse_sample_format(const char *format) +{ + spa_assert(format); + + if (strcasecmp(format, "s16le") == 0) + return PA_SAMPLE_S16LE; + else if (strcasecmp(format, "s16be") == 0) + return PA_SAMPLE_S16BE; + else if (strcasecmp(format, "s16ne") == 0 + || strcasecmp(format, "s16") == 0 + || strcasecmp(format, "16") == 0) + return PA_SAMPLE_S16NE; + else if (strcasecmp(format, "s16re") == 0) + return PA_SAMPLE_S16RE; + else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0) + return PA_SAMPLE_U8; + else if (strcasecmp(format, "float32") == 0 + || strcasecmp(format, "float32ne") == 0 + || strcasecmp(format, "float") == 0) + return PA_SAMPLE_FLOAT32NE; + else if (strcasecmp(format, "float32re") == 0) + return PA_SAMPLE_FLOAT32RE; + else if (strcasecmp(format, "float32le") == 0) + return PA_SAMPLE_FLOAT32LE; + else if (strcasecmp(format, "float32be") == 0) + return PA_SAMPLE_FLOAT32BE; + else if (strcasecmp(format, "ulaw") == 0 + || strcasecmp(format, "mulaw") == 0) + return PA_SAMPLE_ULAW; + else if (strcasecmp(format, "alaw") == 0) + return PA_SAMPLE_ALAW; + else if (strcasecmp(format, "s32le") == 0) + return PA_SAMPLE_S32LE; + else if (strcasecmp(format, "s32be") == 0) + return PA_SAMPLE_S32BE; + else if (strcasecmp(format, "s32ne") == 0 + || strcasecmp(format, "s32") == 0 + || strcasecmp(format, "32") == 0) + return PA_SAMPLE_S32NE; + else if (strcasecmp(format, "s32re") == 0) + return PA_SAMPLE_S24RE; + else if (strcasecmp(format, "s24le") == 0) + return PA_SAMPLE_S24LE; + else if (strcasecmp(format, "s24be") == 0) + return PA_SAMPLE_S24BE; + else if (strcasecmp(format, "s24ne") == 0 + || strcasecmp(format, "s24") == 0 + || strcasecmp(format, "24") == 0) + return PA_SAMPLE_S24NE; + else if (strcasecmp(format, "s24re") == 0) + return PA_SAMPLE_S24RE; + else if (strcasecmp(format, "s24-32le") == 0) + return PA_SAMPLE_S24_32LE; + else if (strcasecmp(format, "s24-32be") == 0) + return PA_SAMPLE_S24_32BE; + else if (strcasecmp(format, "s24-32ne") == 0 + || strcasecmp(format, "s24-32") == 0) + return PA_SAMPLE_S24_32NE; + else if (strcasecmp(format, "s24-32re") == 0) + return PA_SAMPLE_S24_32RE; + + return PA_SAMPLE_INVALID; +} + +int pa_sample_format_is_le(pa_sample_format_t f) +{ + spa_assert(pa_sample_format_valid(f)); + + switch (f) { + case PA_SAMPLE_S16LE: + case PA_SAMPLE_S24LE: + case PA_SAMPLE_S32LE: + case PA_SAMPLE_S24_32LE: + case PA_SAMPLE_FLOAT32LE: + return 1; + + case PA_SAMPLE_S16BE: + case PA_SAMPLE_S24BE: + case PA_SAMPLE_S32BE: + case PA_SAMPLE_S24_32BE: + case PA_SAMPLE_FLOAT32BE: + return 0; + + default: + return -1; + } +} + +int pa_sample_format_is_be(pa_sample_format_t f) +{ + int r; + + if ((r = pa_sample_format_is_le(f)) < 0) + return r; + + return !r; +} diff --git a/src/scache.c b/src/scache.c new file mode 100644 index 000000000..72c8c9494 --- /dev/null +++ b/src/scache.c @@ -0,0 +1,57 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include "internal.h" + +int pa_stream_connect_upload(pa_stream *s, size_t length) +{ + pw_log_warn("Not Implemented"); + return 0; +} + +int pa_stream_finish_upload(pa_stream *s) +{ + pw_log_warn("Not Implemented"); + return 0; +} + +pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_play_sample(pa_context *c, const char *name, const char *dev, + pa_volume_t volume, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_context_play_sample_with_proplist(pa_context *c, const char *name, + const char *dev, pa_volume_t volume, pa_proplist *proplist, + pa_context_play_sample_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} diff --git a/src/strbuf.c b/src/strbuf.c new file mode 100644 index 000000000..9e281d578 --- /dev/null +++ b/src/strbuf.c @@ -0,0 +1,193 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include "internal.h" +#include "strbuf.h" + +/* A chunk of the linked list that makes up the string */ +struct chunk { + struct chunk *next; + size_t length; +}; + +#define CHUNK_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(struct chunk))) + +struct pa_strbuf { + size_t length; + struct chunk *head, *tail; +}; + +pa_strbuf *pa_strbuf_new(void) { + pa_strbuf *sb; + + sb = pa_xnew(pa_strbuf, 1); + sb->length = 0; + sb->head = sb->tail = NULL; + + return sb; +} + +void pa_strbuf_free(pa_strbuf *sb) { + pa_assert(sb); + + while (sb->head) { + struct chunk *c = sb->head; + sb->head = sb->head->next; + pa_xfree(c); + } + + pa_xfree(sb); +} + +/* Make a C string from the string buffer. The caller has to free + * string with pa_xfree(). */ +char *pa_strbuf_to_string(pa_strbuf *sb) { + char *t, *e; + struct chunk *c; + + pa_assert(sb); + + e = t = pa_xmalloc(sb->length+1); + + for (c = sb->head; c; c = c->next) { + pa_assert((size_t) (e-t) <= sb->length); + memcpy(e, CHUNK_TO_TEXT(c), c->length); + e += c->length; + } + + /* Trailing NUL */ + *e = 0; + + pa_assert(e == t+sb->length); + + return t; +} + +/* Combination of pa_strbuf_free() and pa_strbuf_to_string() */ +char *pa_strbuf_to_string_free(pa_strbuf *sb) { + char *t; + + pa_assert(sb); + t = pa_strbuf_to_string(sb); + pa_strbuf_free(sb); + + return t; +} + +/* Append a string to the string buffer */ +void pa_strbuf_puts(pa_strbuf *sb, const char *t) { + + pa_assert(sb); + pa_assert(t); + + pa_strbuf_putsn(sb, t, strlen(t)); +} + +/* Append a character to the string buffer */ +void pa_strbuf_putc(pa_strbuf *sb, char c) { + pa_assert(sb); + + pa_strbuf_putsn(sb, &c, 1); +} + +/* Append a new chunk to the linked list */ +static void append(pa_strbuf *sb, struct chunk *c) { + pa_assert(sb); + pa_assert(c); + + if (sb->tail) { + pa_assert(sb->head); + sb->tail->next = c; + } else { + pa_assert(!sb->head); + sb->head = c; + } + + sb->tail = c; + sb->length += c->length; + c->next = NULL; +} + +/* Append up to l bytes of a string to the string buffer */ +void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) { + struct chunk *c; + + pa_assert(sb); + pa_assert(t); + + if (!l) + return; + + c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l); + c->length = l; + memcpy(CHUNK_TO_TEXT(c), t, l); + + append(sb, c); +} + +/* Append a printf() style formatted string to the string buffer. */ +/* The following is based on an example from the GNU libc documentation */ +size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) { + size_t size = 100; + struct chunk *c = NULL; + + pa_assert(sb); + pa_assert(format); + + for(;;) { + va_list ap; + int r; + + c = pa_xrealloc(c, PA_ALIGN(sizeof(struct chunk)) + size); + + va_start(ap, format); + r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap); + CHUNK_TO_TEXT(c)[size-1] = 0; + va_end(ap); + + if (r > -1 && (size_t) r < size) { + c->length = (size_t) r; + append(sb, c); + return (size_t) r; + } + + if (r > -1) /* glibc 2.1 */ + size = (size_t) r+1; + else /* glibc 2.0 */ + size *= 2; + } +} + +bool pa_strbuf_isempty(pa_strbuf *sb) { + pa_assert(sb); + + return sb->length <= 0; +} diff --git a/src/strbuf.h b/src/strbuf.h new file mode 100644 index 000000000..f5d9921e6 --- /dev/null +++ b/src/strbuf.h @@ -0,0 +1,39 @@ +#ifndef foostrbufhfoo +#define foostrbufhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#include + +typedef struct pa_strbuf pa_strbuf; + +pa_strbuf *pa_strbuf_new(void); +void pa_strbuf_free(pa_strbuf *sb); +char *pa_strbuf_to_string(pa_strbuf *sb); +char *pa_strbuf_to_string_free(pa_strbuf *sb); + +size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); +void pa_strbuf_puts(pa_strbuf *sb, const char *t); +void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m); +void pa_strbuf_putc(pa_strbuf *sb, char c); + +bool pa_strbuf_isempty(pa_strbuf *sb); + +#endif diff --git a/src/stream.c b/src/stream.c new file mode 100644 index 000000000..0256be281 --- /dev/null +++ b/src/stream.c @@ -0,0 +1,908 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include + +#include + +#include +#include "internal.h" + + +static void stream_state_changed(void *data, enum pw_stream_state old, + enum pw_stream_state state, const char *error) +{ + pa_stream *s = data; + + switch(state) { + case PW_STREAM_STATE_ERROR: + pa_stream_set_state(s, PA_STREAM_FAILED); + break; + case PW_STREAM_STATE_UNCONNECTED: + pa_stream_set_state(s, PA_STREAM_UNCONNECTED); + break; + case PW_STREAM_STATE_CONNECTING: + pa_stream_set_state(s, PA_STREAM_CREATING); + break; + case PW_STREAM_STATE_CONFIGURE: + case PW_STREAM_STATE_READY: + break; + case PW_STREAM_STATE_PAUSED: + pa_stream_set_state(s, PA_STREAM_READY); + break; + case PW_STREAM_STATE_STREAMING: + break; + } +} + +static void stream_format_changed(void *data, const struct spa_pod *format) +{ +} + + +static void stream_process(void *data) +{ + pa_stream *s = data; + + if (s->direction == PA_STREAM_PLAYBACK) { + if (s->write_callback) + s->write_callback(s, 4096, s->write_userdata); + } + else { + if (s->read_callback) + s->read_callback(s, 4096, s->read_userdata); + } +} + +static const struct pw_stream_events stream_events = +{ + PW_VERSION_STREAM_EVENTS, + .state_changed = stream_state_changed, + .format_changed = stream_format_changed, + .process = stream_process, +}; + +pa_stream* stream_new(pa_context *c, const char *name, + const pa_sample_spec *ss, const pa_channel_map *map, + pa_format_info * const * formats, unsigned int n_formats, + pa_proplist *p) +{ + pa_stream *s; + int i; + + spa_assert(c); + spa_assert(c->refcount >= 1); + pa_assert((ss == NULL && map == NULL) || (formats == NULL && n_formats == 0)); + pa_assert(n_formats < PA_MAX_FORMATS); + + PA_CHECK_VALIDITY_RETURN_NULL(c, name || + (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID); + + s = calloc(1, sizeof(pa_stream)); + if (s == NULL) + return NULL; + + s->stream = pw_stream_new(c->remote, name, NULL); + s->refcount = 1; + s->context = c; + init_type(&s->type, pw_core_get_type(c->core)->map); + + pw_stream_add_listener(s->stream, &s->stream_listener, &stream_events, s); + + s->direction = PA_STREAM_NODIRECTION; + s->state = PA_STREAM_UNCONNECTED; + s->flags = 0; + + if (ss) + s->sample_spec = *ss; + else + pa_sample_spec_init(&s->sample_spec); + + if (map) + s->channel_map = *map; + else + pa_channel_map_init(&s->channel_map); + + s->n_formats = 0; + if (formats) { + s->n_formats = n_formats; + for (i = 0; i < n_formats; i++) + s->req_formats[i] = pa_format_info_copy(formats[i]); + } + s->format = NULL; + + s->direct_on_input = PA_INVALID_INDEX; + + s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new(); + if (name) + pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name); + + s->stream_index = PA_INVALID_INDEX; + + s->buffer_attr.maxlength = (uint32_t) -1; + if (ss) + s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */ + else { + /* FIXME: We assume a worst-case compressed format corresponding to + * 48000 Hz, 2 ch, S16 PCM, but this can very well be incorrect */ + pa_sample_spec tmp_ss = { + .format = PA_SAMPLE_S16NE, + .rate = 48000, + .channels = 2, + }; + s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &tmp_ss); /* 250ms of buffering */ + } + s->buffer_attr.minreq = (uint32_t) -1; + s->buffer_attr.prebuf = (uint32_t) -1; + s->buffer_attr.fragsize = (uint32_t) -1; + + s->device_index = PA_INVALID_INDEX; + + s->timing_info_valid = false; + + spa_list_append(&c->streams, &s->link); + pa_stream_ref(s); + + return s; +} + +pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, + const pa_channel_map *map) +{ + return stream_new(c, name, ss, map, NULL, 0, NULL); +} + +pa_stream* pa_stream_new_with_proplist(pa_context *c, const char *name, + const pa_sample_spec *ss, const pa_channel_map *map, pa_proplist *p) +{ + return stream_new(c, name, ss, map, NULL, 0, p); +} + +pa_stream *pa_stream_new_extended(pa_context *c, const char *name, + pa_format_info * const * formats, unsigned int n_formats, pa_proplist *p) +{ + return stream_new(c, name, NULL, NULL, formats, n_formats, p); +} + +static void stream_unlink(pa_stream *s) +{ + spa_list_remove(&s->link); +} + +static void stream_free(pa_stream *s) +{ + int i; + + if (s->proplist) + pa_proplist_free(s->proplist); + + for (i = 0; i < s->n_formats; i++) + pa_format_info_free(s->req_formats[i]); + + if (s->format) + pa_format_info_free(s->format); + + free(s->device_name); + free(s); +} + +void pa_stream_unref(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (--s->refcount == 0) + stream_free(s); +} + +pa_stream *pa_stream_ref(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + s->refcount++; + return s; +} + +pa_stream_state_t pa_stream_get_state(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + return s->state; +} + +pa_context* pa_stream_get_context(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + return s->context; +} + +uint32_t pa_stream_get_index(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(s->context, + s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX); + + return s->stream_index; +} + +void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == st) + return; + + pa_stream_ref(s); + + s->state = st; + + if (s->state_callback) + s->state_callback(s, s->state_userdata); + + if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED)) + stream_unlink(s); + + pa_stream_unref(s); +} + + +uint32_t pa_stream_get_device_index(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, + PA_ERR_BADSTATE, PA_INVALID_INDEX); + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, + PA_ERR_BADSTATE, PA_INVALID_INDEX); + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->device_index != PA_INVALID_INDEX, + PA_ERR_BADSTATE, PA_INVALID_INDEX); + + return s->device_index; +} + +const char *pa_stream_get_device_name(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->device_name, PA_ERR_BADSTATE); + + return s->device_name; +} + +int pa_stream_is_suspended(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + return s->suspended; +} + +int pa_stream_is_corked(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + return s->corked; +} + +static int create_stream(pa_stream_direction_t direction, + pa_stream *s, + const char *dev, + const pa_buffer_attr *attr, + pa_stream_flags_t flags, + const pa_cvolume *volume, + pa_stream *sync_stream) +{ + int res; + enum pw_stream_flags fl; + const struct spa_pod *params[1]; + uint8_t buffer[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + struct pw_type *t; + + spa_assert(s); + spa_assert(s->refcount >= 1); + + s->direction = direction; + + t = pw_core_get_type(s->context->core); + + pa_stream_set_state(s, PA_STREAM_CREATING); + + fl = PW_STREAM_FLAG_AUTOCONNECT | + PW_STREAM_FLAG_MAP_BUFFERS | + PW_STREAM_FLAG_RT_PROCESS; + + if (flags & PA_STREAM_PASSTHROUGH) + fl |= PW_STREAM_FLAG_EXCLUSIVE; + + params[0] = spa_pod_builder_object(&b, + t->param.idEnumFormat, t->spa_format, + "I", s->type.media_type.audio, + "I", s->type.media_subtype.raw, + ":", s->type.format_audio.format, "I", s->type.audio_format.S16, + ":", s->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_INTERLEAVED, + ":", s->type.format_audio.channels, "i", 2, + ":", s->type.format_audio.rate, "i", 44100); + + res = pw_stream_connect(s->stream, + direction == PA_STREAM_PLAYBACK ? + PW_DIRECTION_OUTPUT : + PW_DIRECTION_INPUT, + dev, + fl, + params, 1); + + return res; +} + +int pa_stream_connect_playback( + pa_stream *s, + const char *dev, + const pa_buffer_attr *attr, + pa_stream_flags_t flags, + const pa_cvolume *volume, + pa_stream *sync_stream) +{ + return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream); +} + +int pa_stream_connect_record( + pa_stream *s, + const char *dev, + const pa_buffer_attr *attr, + pa_stream_flags_t flags) +{ + return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL); +} + +int pa_stream_disconnect(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + pa_stream_set_state(s, PA_STREAM_TERMINATED); + return 0; +} + +int pa_stream_begin_write( + pa_stream *s, + void **data, + size_t *nbytes) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || + s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, data, PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID); + + if (s->buffer == NULL) { + s->buffer = pw_stream_dequeue_buffer(s->stream); + } + if (s->buffer == NULL) { + *data = NULL; + *nbytes = 0; + } + else { + *data = s->buffer->buffer->datas[0].data; + *nbytes = s->buffer->buffer->datas[0].maxsize; + } + return 0; +} + +int pa_stream_cancel_write(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || + s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + pw_log_warn("Not Implemented"); + return 0; +} + +int pa_stream_write(pa_stream *s, + const void *data, + size_t nbytes, + pa_free_cb_t free_cb, + int64_t offset, + pa_seek_mode_t seek) +{ + return pa_stream_write_ext_free(s, data, nbytes, free_cb, (void*) data, offset, seek); +} + +int pa_stream_write_ext_free(pa_stream *s, + const void *data, + size_t nbytes, + pa_free_cb_t free_cb, + void *free_cb_data, + int64_t offset, + pa_seek_mode_t seek) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + spa_assert(data); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || + s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, seek <= PA_SEEK_RELATIVE_END, PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || + (seek == PA_SEEK_RELATIVE && offset == 0), PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, offset % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, nbytes % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID); + + if (s->buffer == NULL) { + pw_log_warn("Not Implemented"); + return PA_ERR_INVALID; + } + else { + s->buffer->buffer->datas[0].chunk->offset = 0; + s->buffer->buffer->datas[0].chunk->size = nbytes; + } + pw_stream_queue_buffer(s->stream, s->buffer); + return 0; +} + +int pa_stream_peek(pa_stream *s, + const void **data, + size_t *nbytes) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + spa_assert(data); + spa_assert(nbytes); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); + + pw_log_warn("Not Implemented"); + return 0; +} + +int pa_stream_drop(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); + pw_log_warn("Not Implemented"); + return 0; +} + +size_t pa_stream_writable_size(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, + PA_ERR_BADSTATE, (size_t) -1); + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, + PA_ERR_BADSTATE, (size_t) -1); + + pw_log_warn("Not Implemented"); + return 0; +} + +size_t pa_stream_readable_size(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, + PA_ERR_BADSTATE, (size_t) -1); + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction == PA_STREAM_RECORD, + PA_ERR_BADSTATE, (size_t) -1); + pw_log_warn("Not Implemented"); + return 0; +} + +pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); + + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + pw_log_warn("Not Implemented"); + return NULL; +} + +void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->state_callback = cb; + s->state_userdata = userdata; +} + +void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->write_callback = cb; + s->write_userdata = userdata; +} + +void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->read_callback = cb; + s->read_userdata = userdata; +} + +void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->overflow_callback = cb; + s->overflow_userdata = userdata; +} + +int64_t pa_stream_get_underflow_index(pa_stream *s) +{ + pw_log_warn("Not Implemented"); + return 0; +} + +void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->underflow_callback = cb; + s->underflow_userdata = userdata; +} + +void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->started_callback = cb; + s->started_userdata = userdata; +} + +void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->latency_update_callback = cb; + s->latency_update_userdata = userdata; +} + +void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->moved_callback = cb; + s->moved_userdata = userdata; +} + +void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->suspended_callback = cb; + s->suspended_userdata = userdata; +} + +void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->event_callback = cb; + s->event_userdata = userdata; +} + +void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->buffer_attr_callback = cb; + s->buffer_attr_userdata = userdata; +} + +pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + s->corked = b; + pw_log_warn("Not Implemented"); + + return NULL; +} + +pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + pw_log_warn("Not Implemented"); + return NULL; +} + +pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); + + return NULL; +} + +pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); + + return NULL; +} + +pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + spa_assert(name); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + return NULL; +} + +int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); + + return 0; +} + +int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + spa_assert(r_usec); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); + + return 0; +} + +const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_NODATA); + + return &s->timing_info; +} + +const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + return &s->sample_spec; +} + +const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + return &s->channel_map; +} + +const pa_format_info* pa_stream_get_format_info(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + + return s->format; +} + +const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + return &s->buffer_attr; +} + +pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + spa_assert(attr); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + return NULL; +} + +pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, pa_sample_rate_valid(rate), PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->flags & PA_STREAM_VARIABLE_RATE, PA_ERR_BADSTATE); + + return NULL; +} + +pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, mode == PA_UPDATE_SET || + mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + return NULL; +} + +pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(s->context, keys && keys[0], PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + return NULL; +} + +int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY(s->context, sink_input_idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); + + s->direct_on_input = sink_input_idx; + return 0; +} + +uint32_t pa_stream_get_monitor_stream(pa_stream *s) +{ + spa_assert(s); + spa_assert(s->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direct_on_input != PA_INVALID_INDEX, + PA_ERR_BADSTATE, PA_INVALID_INDEX); + + return s->direct_on_input; +} diff --git a/src/subscribe.c b/src/subscribe.c new file mode 100644 index 000000000..de749b587 --- /dev/null +++ b/src/subscribe.c @@ -0,0 +1,35 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include "internal.h" + +pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) +{ + pw_log_warn("Not Implemented"); +} diff --git a/src/utf8.c b/src/utf8.c new file mode 100644 index 000000000..7eeed7022 --- /dev/null +++ b/src/utf8.c @@ -0,0 +1,287 @@ +/*** + This file is part of PulseAudio. + + Copyright 2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, see . +***/ + +/* This file is based on the GLIB utf8 validation functions. The + * original license text follows. */ + +/* gutf8.c - Operations on UTF-8 strings. + * + * Copyright (C) 1999 Tom Tromey + * Copyright (C) 2000 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_ICONV +#include +#endif + +#include +#include + +#include "internal.h" + +#define FILTER_CHAR '_' + +static inline bool is_unicode_valid(uint32_t ch) { + + if (ch >= 0x110000) /* End of unicode space */ + return false; + if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ + return false; + if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */ + return false; + if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */ + return false; + + return true; +} + +static inline bool is_continuation_char(uint8_t ch) { + if ((ch & 0xc0) != 0x80) /* 10xxxxxx */ + return false; + return true; +} + +static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) { + *u_ch <<= 6; + *u_ch |= ch & 0x3f; +} + +static char* utf8_validate(const char *str, char *output) { + uint32_t val = 0; + uint32_t min = 0; + const uint8_t *p, *last; + int size; + uint8_t *o; + + pa_assert(str); + + o = (uint8_t*) output; + for (p = (const uint8_t*) str; *p; p++) { + if (*p < 128) { + if (o) + *o = *p; + } else { + last = p; + + if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */ + size = 2; + min = 128; + val = (uint32_t) (*p & 0x1e); + goto ONE_REMAINING; + } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/ + size = 3; + min = (1 << 11); + val = (uint32_t) (*p & 0x0f); + goto TWO_REMAINING; + } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */ + size = 4; + min = (1 << 16); + val = (uint32_t) (*p & 0x07); + } else + goto error; + + p++; + if (!is_continuation_char(*p)) + goto error; + merge_continuation_char(&val, *p); + +TWO_REMAINING: + p++; + if (!is_continuation_char(*p)) + goto error; + merge_continuation_char(&val, *p); + +ONE_REMAINING: + p++; + if (!is_continuation_char(*p)) + goto error; + merge_continuation_char(&val, *p); + + if (val < min) + goto error; + + if (!is_unicode_valid(val)) + goto error; + + if (o) { + memcpy(o, last, (size_t) size); + o += size; + } + + continue; + +error: + if (o) { + *o = FILTER_CHAR; + p = last; /* We retry at the next character */ + } else + goto failure; + } + + if (o) + o++; + } + + if (o) { + *o = '\0'; + return output; + } + + return (char*) str; + +failure: + return NULL; +} + +char* pa_utf8_valid (const char *str) { + return utf8_validate(str, NULL); +} + +char* pa_utf8_filter (const char *str) { + char *new_str; + + pa_assert(str); + new_str = pa_xmalloc(strlen(str) + 1); + return utf8_validate(str, new_str); +} + +#ifdef HAVE_ICONV + +static char* iconv_simple(const char *str, const char *to, const char *from) { + char *new_str; + size_t len, inlen; + iconv_t cd; + ICONV_CONST char *inbuf; + char *outbuf; + size_t res, inbytes, outbytes; + + pa_assert(str); + pa_assert(to); + pa_assert(from); + + cd = iconv_open(to, from); + if (cd == (iconv_t)-1) + return NULL; + + inlen = len = strlen(str) + 1; + new_str = pa_xmalloc(len); + + for (;;) { + inbuf = (ICONV_CONST char*) str; /* Brain dead prototype for iconv() */ + inbytes = inlen; + outbuf = new_str; + outbytes = len; + + res = iconv(cd, &inbuf, &inbytes, &outbuf, &outbytes); + + if (res != (size_t)-1) + break; + + if (errno != E2BIG) { + pa_xfree(new_str); + new_str = NULL; + break; + } + + pa_assert(inbytes != 0); + + len += inbytes; + new_str = pa_xrealloc(new_str, len); + } + + iconv_close(cd); + + return new_str; +} + +char* pa_utf8_to_locale (const char *str) { + return iconv_simple(str, "", "UTF-8"); +} + +char* pa_locale_to_utf8 (const char *str) { + return iconv_simple(str, "UTF-8", ""); +} + +#else + +char* pa_utf8_to_locale (const char *str) { + pa_assert(str); + + return pa_ascii_filter(str); +} + +char* pa_locale_to_utf8 (const char *str) { + pa_assert(str); + + if (pa_utf8_valid(str)) + return pa_xstrdup(str); + + return NULL; +} + +#endif + +char *pa_ascii_valid(const char *str) { + const char *p; + pa_assert(str); + + for (p = str; *p; p++) + if ((unsigned char) *p >= 128) + return NULL; + + return (char*) str; +} + +char *pa_ascii_filter(const char *str) { + char *r, *s, *d; + pa_assert(str); + + r = pa_xstrdup(str); + + for (s = r, d = r; *s; s++) + if ((unsigned char) *s < 128) + *(d++) = *s; + + *d = 0; + + return r; +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 000000000..763fab802 --- /dev/null +++ b/src/util.c @@ -0,0 +1,77 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include + +#include + +#define PA_PATH_SEP_CHAR '/' + +char *pa_get_user_name(char *s, size_t l) +{ + return strncpy(s, pw_get_user_name(), l); +} + + +char *pa_get_host_name(char *s, size_t l) +{ + return strncpy(s, pw_get_host_name(), l); +} + +char *pa_get_fqdn(char *s, size_t l) +{ + return strncpy(s, pw_get_host_name(), l); +} + +char *pa_get_home_dir(char *s, size_t l) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +char *pa_get_binary_name(char *s, size_t l) +{ + return strncpy(s, pw_get_prgname(), l); +} + +char *pa_path_get_filename(const char *p) +{ + char *fn; + + if (!p) + return NULL; + + if ((fn = strrchr(p, PA_PATH_SEP_CHAR))) + return fn+1; + + return (char*) p; +} + +int pa_msleep(unsigned long t) +{ + struct timespec ts; + + ts.tv_sec = (time_t) (t / SPA_MSEC_PER_SEC); + ts.tv_nsec = (long) ((t % SPA_MSEC_PER_SEC) * SPA_NSEC_PER_MSEC); + + return nanosleep(&ts, NULL); +} diff --git a/src/version.c b/src/version.c new file mode 100644 index 000000000..8c9d21c9e --- /dev/null +++ b/src/version.c @@ -0,0 +1,26 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +const char* pa_get_library_version(void) +{ + return pa_get_headers_version(); +} + diff --git a/src/volume.c b/src/volume.c new file mode 100644 index 000000000..21227e3e6 --- /dev/null +++ b/src/volume.c @@ -0,0 +1,989 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include "internal.h" +#include "sample-util.h" + +int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { + int i; + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_cvolume_valid(a), 0); + + if (PA_UNLIKELY(a == b)) + return 1; + + pa_return_val_if_fail(pa_cvolume_valid(b), 0); + + if (a->channels != b->channels) + return 0; + + for (i = 0; i < a->channels; i++) + if (a->values[i] != b->values[i]) + return 0; + + return 1; +} + +pa_cvolume* pa_cvolume_init(pa_cvolume *a) { + unsigned c; + + pa_assert(a); + + a->channels = 0; + + for (c = 0; c < PA_CHANNELS_MAX; c++) + a->values[c] = PA_VOLUME_INVALID; + + return a; +} + +pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { + int i; + + pa_assert(a); + pa_assert(pa_channels_valid(channels)); + + a->channels = (uint8_t) channels; + + for (i = 0; i < a->channels; i++) + /* Clamp in case there is stale data that exceeds the current + * PA_VOLUME_MAX */ + a->values[i] = PA_CLAMP_VOLUME(v); + + return a; +} + +pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { + uint64_t sum = 0; + unsigned c; + + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) + sum += a->values[c]; + + sum /= a->channels; + + return (pa_volume_t) sum; +} + +pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { + uint64_t sum = 0; + unsigned c, n; + + pa_assert(a); + + if (!cm) + return pa_cvolume_avg(a); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED); + + for (c = n = 0; c < a->channels; c++) { + + if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask)) + continue; + + sum += a->values[c]; + n ++; + } + + if (n > 0) + sum /= n; + + return (pa_volume_t) sum; +} + +pa_volume_t pa_cvolume_max(const pa_cvolume *a) { + pa_volume_t m = PA_VOLUME_MUTED; + unsigned c; + + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) + if (a->values[c] > m) + m = a->values[c]; + + return m; +} + +pa_volume_t pa_cvolume_min(const pa_cvolume *a) { + pa_volume_t m = PA_VOLUME_MAX; + unsigned c; + + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) + if (a->values[c] < m) + m = a->values[c]; + + return m; +} + +pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { + pa_volume_t m = PA_VOLUME_MUTED; + unsigned c; + + pa_assert(a); + + if (!cm) + return pa_cvolume_max(a); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) { + + if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask)) + continue; + + if (a->values[c] > m) + m = a->values[c]; + } + + return m; +} + +pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { + pa_volume_t m = PA_VOLUME_MAX; + unsigned c; + + pa_assert(a); + + if (!cm) + return pa_cvolume_min(a); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) { + + if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask)) + continue; + + if (a->values[c] < m) + m = a->values[c]; + } + + return m; +} + +pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { + uint64_t result; + + pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID); + + /* cbrt((a/PA_VOLUME_NORM)^3*(b/PA_VOLUME_NORM)^3)*PA_VOLUME_NORM = a*b/PA_VOLUME_NORM */ + + result = ((uint64_t) a * (uint64_t) b + (uint64_t) PA_VOLUME_NORM / 2ULL) / (uint64_t) PA_VOLUME_NORM; + + if (result > (uint64_t)PA_VOLUME_MAX) + pa_log_warn("pa_sw_volume_multiply: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings."); + + return (pa_volume_t) PA_CLAMP_VOLUME(result); +} + +pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) { + uint64_t result; + + pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID); + + if (b <= PA_VOLUME_MUTED) + return 0; + + result = ((uint64_t) a * (uint64_t) PA_VOLUME_NORM + (uint64_t) b / 2ULL) / (uint64_t) b; + + if (result > (uint64_t)PA_VOLUME_MAX) + pa_log_warn("pa_sw_volume_divide: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings."); + + return (pa_volume_t) PA_CLAMP_VOLUME(result); +} + +/* Amplitude, not power */ +static double linear_to_dB(double v) { + return 20.0 * log10(v); +} + +static double dB_to_linear(double v) { + return pow(10.0, v / 20.0); +} + +pa_volume_t pa_sw_volume_from_dB(double dB) { + if (isinf(dB) < 0 || dB <= PA_DECIBEL_MININFTY) + return PA_VOLUME_MUTED; + + return pa_sw_volume_from_linear(dB_to_linear(dB)); +} + +double pa_sw_volume_to_dB(pa_volume_t v) { + + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), PA_DECIBEL_MININFTY); + + if (v <= PA_VOLUME_MUTED) + return PA_DECIBEL_MININFTY; + + return linear_to_dB(pa_sw_volume_to_linear(v)); +} + +pa_volume_t pa_sw_volume_from_linear(double v) { + + if (v <= 0.0) + return PA_VOLUME_MUTED; + + /* + * We use a cubic mapping here, as suggested and discussed here: + * + * http://www.robotplanet.dk/audio/audio_gui_design/ + * http://lists.linuxaudio.org/pipermail/linux-audio-dev/2009-May/thread.html#23151 + * + * We make sure that the conversion to linear and back yields the + * same volume value! That's why we need the lround() below! + */ + + return (pa_volume_t) PA_CLAMP_VOLUME((uint64_t) lround(cbrt(v) * PA_VOLUME_NORM)); +} + +double pa_sw_volume_to_linear(pa_volume_t v) { + double f; + + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0.0); + + if (v <= PA_VOLUME_MUTED) + return 0.0; + + if (v == PA_VOLUME_NORM) + return 1.0; + + f = ((double) v / PA_VOLUME_NORM); + + return f*f*f; +} + +char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { + unsigned channel; + bool first = true; + char *e; + + pa_assert(s); + pa_assert(l > 0); + pa_assert(c); + + pa_init_i18n(); + + if (!pa_cvolume_valid(c)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + *(e = s) = 0; + + for (channel = 0; channel < c->channels && l > 1; channel++) { + l -= pa_snprintf(e, l, "%s%u: %3u%%", + first ? "" : " ", + channel, + (unsigned)(((uint64_t)c->values[channel] * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM)); + + e = strchr(e, 0); + first = false; + } + + return s; +} + +char *pa_volume_snprint(char *s, size_t l, pa_volume_t v) { + pa_assert(s); + pa_assert(l > 0); + + pa_init_i18n(); + + if (!PA_VOLUME_IS_VALID(v)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + pa_snprintf(s, l, "%3u%%", (unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM)); + return s; +} + +char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c) { + unsigned channel; + bool first = true; + char *e; + + pa_assert(s); + pa_assert(l > 0); + pa_assert(c); + + pa_init_i18n(); + + if (!pa_cvolume_valid(c)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + *(e = s) = 0; + + for (channel = 0; channel < c->channels && l > 1; channel++) { + double f = pa_sw_volume_to_dB(c->values[channel]); + + l -= pa_snprintf(e, l, "%s%u: %0.2f dB", + first ? "" : " ", + channel, + isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f); + + e = strchr(e, 0); + first = false; + } + + return s; +} + +char *pa_cvolume_snprint_verbose(char *s, size_t l, const pa_cvolume *c, const pa_channel_map *map, int print_dB) { + char *current = s; + bool first = true; + + pa_assert(s); + pa_assert(l > 0); + pa_assert(c); + + pa_init_i18n(); + + if (!pa_cvolume_valid(c)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + pa_assert(!map || (map->channels == c->channels)); + pa_assert(!map || pa_channel_map_valid(map)); + + current[0] = 0; + + for (unsigned channel = 0; channel < c->channels && l > 1; channel++) { + char channel_position[32]; + size_t bytes_printed; + char buf[PA_VOLUME_SNPRINT_VERBOSE_MAX]; + + if (map) + pa_snprintf(channel_position, sizeof(channel_position), "%s", pa_channel_position_to_string(map->map[channel])); + else + pa_snprintf(channel_position, sizeof(channel_position), "%u", channel); + + bytes_printed = pa_snprintf(current, l, "%s%s: %s", + first ? "" : ", ", + channel_position, + pa_volume_snprint_verbose(buf, sizeof(buf), c->values[channel], print_dB)); + l -= bytes_printed; + current += bytes_printed; + first = false; + } + + return s; +} + +char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) { + double f; + + pa_assert(s); + pa_assert(l > 0); + + pa_init_i18n(); + + if (!PA_VOLUME_IS_VALID(v)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + f = pa_sw_volume_to_dB(v); + pa_snprintf(s, l, "%0.2f dB", isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f); + + return s; +} + +char *pa_volume_snprint_verbose(char *s, size_t l, pa_volume_t v, int print_dB) { + char dB[PA_SW_VOLUME_SNPRINT_DB_MAX]; + + pa_assert(s); + pa_assert(l > 0); + + pa_init_i18n(); + + if (!PA_VOLUME_IS_VALID(v)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + pa_snprintf(s, l, "%" PRIu32 " / %3u%%%s%s", + v, + (unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM), + print_dB ? " / " : "", + print_dB ? pa_sw_volume_snprint_dB(dB, sizeof(dB), v) : ""); + + return s; +} + +int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { + unsigned c; + pa_assert(a); + + pa_return_val_if_fail(pa_cvolume_valid(a), 0); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0); + + for (c = 0; c < a->channels; c++) + if (a->values[c] != v) + return 0; + + return 1; +} + +pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + + dest->channels = PA_MIN(a->channels, b->channels); + + for (i = 0; i < dest->channels; i++) + dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]); + + return dest; +} + +pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL); + + for (i = 0; i < a->channels; i++) + dest->values[i] = pa_sw_volume_multiply(a->values[i], b); + + dest->channels = (uint8_t) i; + + return dest; +} + +pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + + dest->channels = PA_MIN(a->channels, b->channels); + + for (i = 0; i < dest->channels; i++) + dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]); + + return dest; +} + +pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL); + + for (i = 0; i < a->channels; i++) + dest->values[i] = pa_sw_volume_divide(a->values[i], b); + + dest->channels = (uint8_t) i; + + return dest; +} + +int pa_cvolume_valid(const pa_cvolume *v) { + unsigned c; + + pa_assert(v); + + if (!pa_channels_valid(v->channels)) + return 0; + + for (c = 0; c < v->channels; c++) + if (!PA_VOLUME_IS_VALID(v->values[c])) + return 0; + + return 1; +} + +static bool on_left(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LEFT); +} + +static bool on_right(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_RIGHT); +} + +static bool on_center(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_CENTER); +} + +static bool on_hfe(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_HFE); +} + +static bool on_lfe(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LFE); +} + +static bool on_front(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_FRONT); +} + +static bool on_rear(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_REAR); +} + +pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) { + int a, b; + pa_cvolume result; + + pa_assert(v); + pa_assert(from); + pa_assert(to); + + pa_return_val_if_fail(pa_channel_map_valid(to), NULL); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL); + + if (pa_channel_map_equal(from, to)) + return v; + + result.channels = to->channels; + + for (b = 0; b < to->channels; b++) { + pa_volume_t k = 0; + int n = 0; + + for (a = 0; a < from->channels; a++) + if (from->map[a] == to->map[b]) { + k += v->values[a]; + n ++; + } + + if (n <= 0) { + for (a = 0; a < from->channels; a++) + if ((on_left(from->map[a]) && on_left(to->map[b])) || + (on_right(from->map[a]) && on_right(to->map[b])) || + (on_center(from->map[a]) && on_center(to->map[b])) || + (on_lfe(from->map[a]) && on_lfe(to->map[b]))) { + + k += v->values[a]; + n ++; + } + } + + if (n <= 0) + k = pa_cvolume_avg(v); + else + k /= n; + + result.values[b] = k; + } + + *v = result; + return v; +} + +int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) { + + pa_assert(v); + pa_assert(ss); + + pa_return_val_if_fail(pa_cvolume_valid(v), 0); + pa_return_val_if_fail(pa_sample_spec_valid(ss), 0); + + return v->channels == ss->channels; +} + +int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) { + pa_assert(v); + pa_assert(cm); + + pa_return_val_if_fail(pa_cvolume_valid(v), 0); + pa_return_val_if_fail(pa_channel_map_valid(cm), 0); + + return v->channels == cm->channels; +} + +/* + * Returns the average volume of l and r, where l and r are two disjoint sets of channels + * (e g left and right, or front and rear). + */ +static void get_avg(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r, + bool (*on_l)(pa_channel_position_t), bool (*on_r)(pa_channel_position_t)) { + int c; + pa_volume_t left = 0, right = 0; + unsigned n_left = 0, n_right = 0; + + pa_assert(v); + pa_assert(map); + pa_assert(map->channels == v->channels); + pa_assert(l); + pa_assert(r); + + for (c = 0; c < map->channels; c++) { + if (on_l(map->map[c])) { + left += v->values[c]; + n_left++; + } else if (on_r(map->map[c])) { + right += v->values[c]; + n_right++; + } + } + + if (n_left <= 0) + *l = PA_VOLUME_NORM; + else + *l = left / n_left; + + if (n_right <= 0) + *r = PA_VOLUME_NORM; + else + *r = right / n_right; +} + +float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) { + pa_volume_t left, right; + + pa_assert(v); + pa_assert(map); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); + + if (!pa_channel_map_can_balance(map)) + return 0.0f; + + get_avg(map, v, &left, &right, on_left, on_right); + + if (left == right) + return 0.0f; + + /* 1.0, 0.0 => -1.0 + 0.0, 1.0 => 1.0 + 0.0, 0.0 => 0.0 + 0.5, 0.5 => 0.0 + 1.0, 0.5 => -0.5 + 1.0, 0.25 => -0.75 + 0.75, 0.25 => -0.66 + 0.5, 0.25 => -0.5 */ + + if (left > right) + return -1.0f + ((float) right / (float) left); + else + return 1.0f - ((float) left / (float) right); +} + +static pa_cvolume* set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance, + bool (*on_l)(pa_channel_position_t), bool (*on_r)(pa_channel_position_t)) { + + pa_volume_t left, nleft, right, nright, m; + unsigned c; + + get_avg(map, v, &left, &right, on_l, on_r); + + m = PA_MAX(left, right); + + if (new_balance <= 0) { + nright = (new_balance + 1.0f) * m; + nleft = m; + } else { + nleft = (1.0f - new_balance) * m; + nright = m; + } + + for (c = 0; c < map->channels; c++) { + if (on_l(map->map[c])) { + if (left == 0) + v->values[c] = nleft; + else + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left); + } else if (on_r(map->map[c])) { + if (right == 0) + v->values[c] = nright; + else + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right); + } + } + + return v; +} + + +pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) { + pa_assert(map); + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + pa_return_val_if_fail(new_balance >= -1.0f, NULL); + pa_return_val_if_fail(new_balance <= 1.0f, NULL); + + if (!pa_channel_map_can_balance(map)) + return v; + + return set_balance(v, map, new_balance, on_left, on_right); +} + +pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) { + unsigned c; + pa_volume_t t = 0; + + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL); + + t = pa_cvolume_max(v); + + if (t <= PA_VOLUME_MUTED) + return pa_cvolume_set(v, v->channels, max); + + for (c = 0; c < v->channels; c++) + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t); + + return v; +} + +pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map *cm, pa_channel_position_mask_t mask) { + unsigned c; + pa_volume_t t = 0; + + pa_assert(v); + + pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL); + + if (!cm) + return pa_cvolume_scale(v, max); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, cm), NULL); + + t = pa_cvolume_max_mask(v, cm, mask); + + if (t <= PA_VOLUME_MUTED) + return pa_cvolume_set(v, v->channels, max); + + for (c = 0; c < v->channels; c++) + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t); + + return v; +} + +float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) { + pa_volume_t rear, front; + + pa_assert(v); + pa_assert(map); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); + + if (!pa_channel_map_can_fade(map)) + return 0.0f; + + get_avg(map, v, &rear, &front, on_rear, on_front); + + if (front == rear) + return 0.0f; + + if (rear > front) + return -1.0f + ((float) front / (float) rear); + else + return 1.0f - ((float) rear / (float) front); +} + +pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float new_fade) { + pa_assert(map); + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + pa_return_val_if_fail(new_fade >= -1.0f, NULL); + pa_return_val_if_fail(new_fade <= 1.0f, NULL); + + if (!pa_channel_map_can_fade(map)) + return v; + + return set_balance(v, map, new_fade, on_rear, on_front); +} + +float pa_cvolume_get_lfe_balance(const pa_cvolume *v, const pa_channel_map *map) { + pa_volume_t hfe, lfe; + + pa_assert(v); + pa_assert(map); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); + + if (!pa_channel_map_can_lfe_balance(map)) + return 0.0f; + + get_avg(map, v, &hfe, &lfe, on_hfe, on_lfe); + + if (hfe == lfe) + return 0.0f; + + if (hfe > lfe) + return -1.0f + ((float) lfe / (float) hfe); + else + return 1.0f - ((float) hfe / (float) lfe); +} + +pa_cvolume* pa_cvolume_set_lfe_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) { + pa_assert(map); + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + pa_return_val_if_fail(new_balance >= -1.0f, NULL); + pa_return_val_if_fail(new_balance <= 1.0f, NULL); + + if (!pa_channel_map_can_lfe_balance(map)) + return v; + + return set_balance(v, map, new_balance, on_hfe, on_lfe); +} + +pa_cvolume* pa_cvolume_set_position( + pa_cvolume *cv, + const pa_channel_map *map, + pa_channel_position_t t, + pa_volume_t v) { + + unsigned c; + bool good = false; + + pa_assert(cv); + pa_assert(map); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), NULL); + pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), NULL); + + for (c = 0; c < map->channels; c++) + if (map->map[c] == t) { + cv->values[c] = v; + good = true; + } + + return good ? cv : NULL; +} + +pa_volume_t pa_cvolume_get_position( + pa_cvolume *cv, + const pa_channel_map *map, + pa_channel_position_t t) { + + unsigned c; + pa_volume_t v = PA_VOLUME_MUTED; + + pa_assert(cv); + pa_assert(map); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), PA_VOLUME_MUTED); + pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, PA_VOLUME_MUTED); + + for (c = 0; c < map->channels; c++) + if (map->map[c] == t) + if (cv->values[c] > v) + v = cv->values[c]; + + return v; +} + +pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + + dest->channels = PA_MIN(a->channels, b->channels); + + for (i = 0; i < dest->channels; i++) + dest->values[i] = PA_MAX(a->values[i], b->values[i]); + + return dest; +} + +pa_cvolume* pa_cvolume_inc_clamp(pa_cvolume *v, pa_volume_t inc, pa_volume_t limit) { + pa_volume_t m; + + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(inc), NULL); + + m = pa_cvolume_max(v); + + if (m >= limit - inc) + m = limit; + else + m += inc; + + return pa_cvolume_scale(v, m); +} + +pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc) { + return pa_cvolume_inc_clamp(v, inc, PA_VOLUME_MAX); +} + +pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) { + pa_volume_t m; + + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(dec), NULL); + + m = pa_cvolume_max(v); + + if (m <= PA_VOLUME_MUTED + dec) + m = PA_VOLUME_MUTED; + else + m -= dec; + + return pa_cvolume_scale(v, m); +} diff --git a/src/xmalloc.c b/src/xmalloc.c new file mode 100644 index 000000000..30e6ca6b4 --- /dev/null +++ b/src/xmalloc.c @@ -0,0 +1,115 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include + +#include + +#define MAX_ALLOC_SIZE (1024*1024*96) /* 96MB */ + +static void oom(void) { + static const char e[] = "Not enough memory\n"; + write(STDERR_FILENO, e, sizeof(e)-1); +#ifdef SIGQUIT + raise(SIGQUIT); +#endif + _exit(1); +} + +void* pa_xmalloc(size_t l) +{ + void *p; + spa_assert(l > 0); + spa_assert(l < MAX_ALLOC_SIZE); + + if (!(p = malloc(l))) + oom(); + + return p; +} + +void *pa_xmalloc0(size_t l) +{ + void *p; + spa_assert(l > 0); + spa_assert(l < MAX_ALLOC_SIZE); + + if (!(p = calloc(1, l))) + oom(); + + return p; +} + +void *pa_xrealloc(void *ptr, size_t size) +{ + void *p; + spa_assert(size > 0); + spa_assert(size < MAX_ALLOC_SIZE); + + if (!(p = realloc(ptr, size))) + oom(); + return p; +} + +void pa_xfree(void *p) +{ + int saved_errno; + if (!p) + return; + saved_errno = errno; + free(p); + errno = saved_errno; +} + +char *pa_xstrdup(const char *s) +{ + if (!s) + return NULL; + return pa_xmemdup(s, strlen(s)+1); +} + +char *pa_xstrndup(const char *s, size_t l) +{ + char *e, *r; + + if (!s) + return NULL; + + if ((e = memchr(s, 0, l))) + return pa_xmemdup(s, (size_t) (e-s+1)); + + r = pa_xmalloc(l+1); + memcpy(r, s, l); + r[l] = 0; + return r; +} + +void* pa_xmemdup(const void *p, size_t l) +{ + if (!p) + return NULL; + else { + char *r = pa_xmalloc(l); + memcpy(r, p, l); + return r; + } +} From 5a0594db3543035df2f3b4c55ab4c0e6ef126b36 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 1 Jun 2018 11:32:07 +0200 Subject: [PATCH 002/116] remove unused files --- src/hashmap.c | 348 -------------------------------------------------- src/hashmap.h | 105 --------------- 2 files changed, 453 deletions(-) delete mode 100644 src/hashmap.c delete mode 100644 src/hashmap.h diff --git a/src/hashmap.c b/src/hashmap.c deleted file mode 100644 index f3dd73fdd..000000000 --- a/src/hashmap.c +++ /dev/null @@ -1,348 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2004-2008 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, see . -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -#include "internal.h" -#include "hashmap.h" - -#define NBUCKETS 127 - -struct hashmap_entry { - void *key; - void *value; - - struct hashmap_entry *bucket_next, *bucket_previous; - struct hashmap_entry *iterate_next, *iterate_previous; -}; - -struct pa_hashmap { - pa_hash_func_t hash_func; - pa_compare_func_t compare_func; - - pa_free_cb_t key_free_func; - pa_free_cb_t value_free_func; - - struct hashmap_entry *iterate_list_head, *iterate_list_tail; - unsigned n_entries; -}; - -#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + PA_ALIGN(sizeof(pa_hashmap)))) - -PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree); - -static unsigned pa_idxset_trivial_hash_func(const void *p) { - return PA_PTR_TO_UINT(p); -} - -static int pa_idxset_trivial_compare_func(const void *a, const void *b) { - return a < b ? -1 : (a > b ? 1 : 0); -} - -pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func, pa_free_cb_t key_free_func, pa_free_cb_t value_free_func) { - pa_hashmap *h; - - h = pa_xmalloc0(PA_ALIGN(sizeof(pa_hashmap)) + NBUCKETS*sizeof(struct hashmap_entry*)); - - h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; - h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; - - h->key_free_func = key_free_func; - h->value_free_func = value_free_func; - - h->n_entries = 0; - h->iterate_list_head = h->iterate_list_tail = NULL; - - return h; -} - -pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) { - return pa_hashmap_new_full(hash_func, compare_func, NULL, NULL); -} - -static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) { - pa_assert(h); - pa_assert(e); - - /* Remove from iteration list */ - if (e->iterate_next) - e->iterate_next->iterate_previous = e->iterate_previous; - else - h->iterate_list_tail = e->iterate_previous; - - if (e->iterate_previous) - e->iterate_previous->iterate_next = e->iterate_next; - else - h->iterate_list_head = e->iterate_next; - - /* Remove from hash table bucket list */ - if (e->bucket_next) - e->bucket_next->bucket_previous = e->bucket_previous; - - if (e->bucket_previous) - e->bucket_previous->bucket_next = e->bucket_next; - else { - unsigned hash = h->hash_func(e->key) % NBUCKETS; - BY_HASH(h)[hash] = e->bucket_next; - } - - if (h->key_free_func) - h->key_free_func(e->key); - - if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0) - pa_xfree(e); - - pa_assert(h->n_entries >= 1); - h->n_entries--; -} - -void pa_hashmap_free(pa_hashmap *h) { - pa_assert(h); - - pa_hashmap_remove_all(h); - pa_xfree(h); -} - -static struct hashmap_entry *hash_scan(pa_hashmap *h, unsigned hash, const void *key) { - struct hashmap_entry *e; - pa_assert(h); - pa_assert(hash < NBUCKETS); - - for (e = BY_HASH(h)[hash]; e; e = e->bucket_next) - if (h->compare_func(e->key, key) == 0) - return e; - - return NULL; -} - -int pa_hashmap_put(pa_hashmap *h, void *key, void *value) { - struct hashmap_entry *e; - unsigned hash; - - pa_assert(h); - - hash = h->hash_func(key) % NBUCKETS; - - if (hash_scan(h, hash, key)) - return -1; - - if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries)))) - e = pa_xnew(struct hashmap_entry, 1); - - e->key = key; - e->value = value; - - /* Insert into hash table */ - e->bucket_next = BY_HASH(h)[hash]; - e->bucket_previous = NULL; - if (BY_HASH(h)[hash]) - BY_HASH(h)[hash]->bucket_previous = e; - BY_HASH(h)[hash] = e; - - /* Insert into iteration list */ - e->iterate_previous = h->iterate_list_tail; - e->iterate_next = NULL; - if (h->iterate_list_tail) { - pa_assert(h->iterate_list_head); - h->iterate_list_tail->iterate_next = e; - } else { - pa_assert(!h->iterate_list_head); - h->iterate_list_head = e; - } - h->iterate_list_tail = e; - - h->n_entries++; - pa_assert(h->n_entries >= 1); - - return 0; -} - -void* pa_hashmap_get(pa_hashmap *h, const void *key) { - unsigned hash; - struct hashmap_entry *e; - - pa_assert(h); - - hash = h->hash_func(key) % NBUCKETS; - - if (!(e = hash_scan(h, hash, key))) - return NULL; - - return e->value; -} - -void* pa_hashmap_remove(pa_hashmap *h, const void *key) { - struct hashmap_entry *e; - unsigned hash; - void *data; - - pa_assert(h); - - hash = h->hash_func(key) % NBUCKETS; - - if (!(e = hash_scan(h, hash, key))) - return NULL; - - data = e->value; - remove_entry(h, e); - - return data; -} - -int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key) { - void *data; - - pa_assert(h); - - data = pa_hashmap_remove(h, key); - - if (data && h->value_free_func) - h->value_free_func(data); - - return data ? 0 : -1; -} - -void pa_hashmap_remove_all(pa_hashmap *h) { - pa_assert(h); - - while (h->iterate_list_head) { - void *data; - data = h->iterate_list_head->value; - remove_entry(h, h->iterate_list_head); - - if (h->value_free_func) - h->value_free_func(data); - } -} - -void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) { - struct hashmap_entry *e; - - pa_assert(h); - pa_assert(state); - - if (*state == (void*) -1) - goto at_end; - - if (!*state && !h->iterate_list_head) - goto at_end; - - e = *state ? *state : h->iterate_list_head; - - if (e->iterate_next) - *state = e->iterate_next; - else - *state = (void*) -1; - - if (key) - *key = e->key; - - return e->value; - -at_end: - *state = (void *) -1; - - if (key) - *key = NULL; - - return NULL; -} - -void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void **key) { - struct hashmap_entry *e; - - pa_assert(h); - pa_assert(state); - - if (*state == (void*) -1) - goto at_beginning; - - if (!*state && !h->iterate_list_tail) - goto at_beginning; - - e = *state ? *state : h->iterate_list_tail; - - if (e->iterate_previous) - *state = e->iterate_previous; - else - *state = (void*) -1; - - if (key) - *key = e->key; - - return e->value; - -at_beginning: - *state = (void *) -1; - - if (key) - *key = NULL; - - return NULL; -} - -void* pa_hashmap_first(pa_hashmap *h) { - pa_assert(h); - - if (!h->iterate_list_head) - return NULL; - - return h->iterate_list_head->value; -} - -void* pa_hashmap_last(pa_hashmap *h) { - pa_assert(h); - - if (!h->iterate_list_tail) - return NULL; - - return h->iterate_list_tail->value; -} - -void* pa_hashmap_steal_first(pa_hashmap *h) { - void *data; - - pa_assert(h); - - if (!h->iterate_list_head) - return NULL; - - data = h->iterate_list_head->value; - remove_entry(h, h->iterate_list_head); - - return data; -} - -unsigned pa_hashmap_size(pa_hashmap *h) { - pa_assert(h); - - return h->n_entries; -} - -bool pa_hashmap_isempty(pa_hashmap *h) { - pa_assert(h); - - return h->n_entries == 0; -} diff --git a/src/hashmap.h b/src/hashmap.h deleted file mode 100644 index f857c6406..000000000 --- a/src/hashmap.h +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef foopulsecorehashmaphfoo -#define foopulsecorehashmaphfoo - -/*** - This file is part of PulseAudio. - - Copyright 2004-2008 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, see . -***/ - -#include - -#include - -typedef unsigned (*pa_hash_func_t)(const void *p); -typedef int (*pa_compare_func_t)(const void *a, const void *b); -typedef void *(*pa_copy_func_t)(const void *p); - -/* Simple Implementation of a hash table. Memory management is the - * user's job. It's a good idea to have the key pointer point to a - * string in the value data. The insertion order is preserved when - * iterating. */ - -typedef struct pa_hashmap pa_hashmap; - -/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */ -pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func); - -/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map, and functions to free the key - * and value (either or both can be NULL). */ -pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func, pa_free_cb_t key_free_func, pa_free_cb_t value_free_func); - -/* Free the hash table. */ -void pa_hashmap_free(pa_hashmap*); - -/* Add an entry to the hashmap. Returns non-zero when the entry already exists */ -int pa_hashmap_put(pa_hashmap *h, void *key, void *value); - -/* Return an entry from the hashmap */ -void* pa_hashmap_get(pa_hashmap *h, const void *key); - -/* Returns the data of the entry while removing */ -void* pa_hashmap_remove(pa_hashmap *h, const void *key); - -/* Removes the entry and frees the entry data. Returns a negative value if the - * entry is not found. FIXME: This function shouldn't be needed. - * pa_hashmap_remove() should free the entry data, and the current semantics of - * pa_hashmap_remove() should be implemented by a function called - * pa_hashmap_steal(). */ -int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key); - -/* Remove all entries but don't free the hashmap */ -void pa_hashmap_remove_all(pa_hashmap *h); - -/* Return the current number of entries of the hashmap */ -unsigned pa_hashmap_size(pa_hashmap *h); - -/* Return true if the hashmap is empty */ -bool pa_hashmap_isempty(pa_hashmap *h); - -/* May be used to iterate through the hashmap. Initially the opaque - pointer *state has to be set to NULL. The hashmap may not be - modified during iteration -- except for deleting the current entry - via pa_hashmap_remove(). The key of the entry is returned in *key, - if key is non-NULL. After the last entry in the hashmap NULL is - returned. */ -void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key); - -/* Same as pa_hashmap_iterate() but goes backwards */ -void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void**key); - -/* Remove the oldest entry in the hashmap and return it */ -void *pa_hashmap_steal_first(pa_hashmap *h); - -/* Return the oldest entry in the hashmap */ -void* pa_hashmap_first(pa_hashmap *h); - -/* Return the newest entry in the hashmap */ -void* pa_hashmap_last(pa_hashmap *h); - -/* A macro to ease iteration through all entries */ -#define PA_HASHMAP_FOREACH(e, h, state) \ - for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL)) - -/* A macro to ease itration through all key, value pairs */ -#define PA_HASHMAP_FOREACH_KV(k, e, h, state) \ - for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k)); (e); (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k))) - -/* A macro to ease iteration through all entries, backwards */ -#define PA_HASHMAP_FOREACH_BACKWARDS(e, h, state) \ - for ((state) = NULL, (e) = pa_hashmap_iterate_backwards((h), &(state), NULL); (e); (e) = pa_hashmap_iterate_backwards((h), &(state), NULL)) - -#endif From 68643fd25ab743fe87b33fdc1416357edff3a3dd Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 1 Jun 2018 11:42:23 +0200 Subject: [PATCH 003/116] context: don't free NULL properties --- src/context.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/context.c b/src/context.c index c44c83701..5dda95852 100644 --- a/src/context.c +++ b/src/context.c @@ -122,8 +122,8 @@ static void registry_event_global_remove(void *object, uint32_t id) return; spa_list_remove(&g->link); - pw_properties_free(g->props); - + if (g->props) + pw_properties_free(g->props); if (g->destroy && g->info) g->destroy(g->info); free(g); From fe932db2c6ab3d57f36d205b767e86d6b15107ea Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 5 Jun 2018 20:10:31 +0200 Subject: [PATCH 004/116] Various improvements context: fix memory free subscribe: implement dummy methods introspect: implement more stream: keep track of dequeued buffers ourselves because we need to be able to cancel and keep track of writable size. --- src/context.c | 1 - src/ext-stream-restore.c | 90 +++++++++++++++++ src/internal.h | 35 ++++++- src/introspect.c | 195 ++++++++++++++++++++++++++++++----- src/meson.build | 3 + src/proplist.c | 9 +- src/stream.c | 162 +++++++++++++++++++++++++----- src/subscribe.c | 36 ++++++- src/thread-mainloop.c | 118 ++++++++++++++++++++++ src/timeval.c | 212 +++++++++++++++++++++++++++++++++++++++ 10 files changed, 804 insertions(+), 57 deletions(-) create mode 100644 src/ext-stream-restore.c create mode 100644 src/thread-mainloop.c create mode 100644 src/timeval.c diff --git a/src/context.c b/src/context.c index 5dda95852..1df2625de 100644 --- a/src/context.c +++ b/src/context.c @@ -260,7 +260,6 @@ static void context_free(pa_context *c) if (c->proplist) pa_proplist_free(c->proplist); - free(c); } void pa_context_unref(pa_context *c) diff --git a/src/ext-stream-restore.c b/src/ext-stream-restore.c new file mode 100644 index 000000000..039c6bfb6 --- /dev/null +++ b/src/ext-stream-restore.c @@ -0,0 +1,90 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include "internal.h" + +/** Test if this extension module is available in the server. \since 0.9.12 */ +pa_operation *pa_ext_stream_restore_test( + pa_context *c, + pa_ext_stream_restore_test_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +/** Read all entries from the stream database. \since 0.9.12 */ +pa_operation *pa_ext_stream_restore_read( + pa_context *c, + pa_ext_stream_restore_read_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +/** Store entries in the stream database. \since 0.9.12 */ +pa_operation *pa_ext_stream_restore_write( + pa_context *c, + pa_update_mode_t mode, + const pa_ext_stream_restore_info data[], + unsigned n, + int apply_immediately, + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +/** Delete entries from the stream database. \since 0.9.12 */ +pa_operation *pa_ext_stream_restore_delete( + pa_context *c, + const char *const s[], + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +/** Subscribe to changes in the stream database. \since 0.9.12 */ +pa_operation *pa_ext_stream_restore_subscribe( + pa_context *c, + int enable, + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +/** Set the subscription callback that is called when + * pa_ext_stream_restore_subscribe() was called. \since 0.9.12 */ +void pa_ext_stream_restore_set_subscribe_cb( + pa_context *c, + pa_ext_stream_restore_subscribe_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); +} diff --git a/src/internal.h b/src/internal.h index da42139d2..05671af75 100644 --- a/src/internal.h +++ b/src/internal.h @@ -24,11 +24,13 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -44,13 +46,28 @@ extern "C" { #define pa_strneq(a,b,n) (!strncmp((a),(b),(n))) #define PA_UNLIKELY SPA_UNLIKELY +#define PA_LIKELY SPA_LIKELY #define PA_MIN SPA_MIN #define PA_MAX SPA_MAX #define pa_assert spa_assert +#define pa_assert_se spa_assert #define pa_return_val_if_fail spa_return_val_if_fail #define pa_assert_not_reached spa_assert_not_reached -#define PA_USEC_PER_MSEC SPA_USEC_PER_MSEC +#define PA_INT_TYPE_SIGNED(type) (!!((type) 0 > (type) -1)) + +#define PA_INT_TYPE_HALF(type) ((type) 1 << (sizeof(type)*8 - 2)) + +#define PA_INT_TYPE_MAX(type) \ + ((type) (PA_INT_TYPE_SIGNED(type) \ + ? (PA_INT_TYPE_HALF(type) - 1 + PA_INT_TYPE_HALF(type)) \ + : (type) -1)) + +#define PA_INT_TYPE_MIN(type) \ + ((type) (PA_INT_TYPE_SIGNED(type) \ + ? (-1 - PA_INT_TYPE_MAX(type)) \ + : (type) 0)) + #ifdef __GNUC__ #define PA_CLAMP_UNLIKELY(x, low, high) \ @@ -183,6 +200,9 @@ struct pa_context { void *state_userdata; pa_context_event_cb_t event_callback; void *event_userdata; + pa_context_subscribe_cb_t subscribe_callback; + void *subscribe_userdata; + pa_subscription_mask_t subscribe_mask; bool no_fail; uint32_t client_index; @@ -210,6 +230,9 @@ static inline void init_type(struct type *type, struct spa_type_map *map) spa_type_audio_format_map(map, &type->audio_format); } +#define MAX_BUFFERS 64 +#define MASK_BUFFERS (MAX_BUFFERS-1) + struct pa_stream { struct spa_list link; int refcount; @@ -270,7 +293,17 @@ struct pa_stream { pa_stream_notify_cb_t buffer_attr_callback; void *buffer_attr_userdata; + int64_t offset; + + struct pw_buffer *dequeued[MAX_BUFFERS]; + struct spa_ringbuffer dequeued_ring; + size_t dequeued_size; + struct pw_buffer *buffer; + void *buffer_data; + uint32_t buffer_index; + int64_t buffer_size; + int64_t buffer_offset; }; void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); diff --git a/src/introspect.c b/src/introspect.c index db8982c89..ce2af79a8 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -25,12 +25,12 @@ #include "internal.h" -typedef int (*global_filter_t)(pa_context *c, struct global *g); +typedef int (*global_filter_t)(pa_context *c, struct global *g, bool full); static void node_event_info(void *object, struct pw_node_info *info) { struct global *g = object; - pw_log_debug("update"); + pw_log_debug("update %d", g->id); g->info = pw_node_info_update(g->info, info); } @@ -42,7 +42,7 @@ static const struct pw_node_proxy_events node_events = { static void module_event_info(void *object, struct pw_module_info *info) { struct global *g = object; - pw_log_debug("update"); + pw_log_debug("update %d", g->id); g->info = pw_module_info_update(g->info, info); } @@ -54,7 +54,7 @@ static const struct pw_module_proxy_events module_events = { static void client_event_info(void *object, struct pw_client_info *info) { struct global *g = object; - pw_log_debug("update"); + pw_log_debug("update %d", g->id); g->info = pw_client_info_update(g->info, info); } @@ -91,6 +91,8 @@ static int ensure_global(pa_context *c, struct global *g) else return -EINVAL; + pw_log_debug("bind %d", g->id); + g->proxy = pw_registry_proxy_bind(c->registry_proxy, g->id, g->type, client_version, 0); if (g->proxy == NULL) @@ -106,7 +108,7 @@ static void ensure_types(pa_context *c, uint32_t type, global_filter_t filter) { struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!filter(c, g)) + if (!filter(c, g, false)) continue; ensure_global(c, g); } @@ -124,6 +126,8 @@ static void sink_callback(struct sink_data *d) struct global *g = d->global; struct pw_node_info *info = g->info; pa_sink_info i; + pa_format_info ii[1]; + pa_format_info *ip[1]; spa_zero(i); i.index = g->id; @@ -132,7 +136,11 @@ static void sink_callback(struct sink_data *d) i.owner_module = g->parent_id; i.base_volume = PA_VOLUME_NORM; i.n_volume_steps = PA_VOLUME_NORM+1; - + i.n_formats = 1; + ii[0].encoding = PA_ENCODING_PCM; + ii[0].plist = pa_proplist_new(); + ip[0] = ii; + i.formats = ip; d->cb(d->context, &i, 0, d->userdata); } @@ -141,9 +149,10 @@ static void sink_info(pa_operation *o, void *userdata) struct sink_data *d = userdata; sink_callback(d); d->cb(d->context, NULL, 1, d->userdata); + pa_operation_done(o); } -static int sink_filter(pa_context *c, struct global *g) +static int sink_filter(pa_context *c, struct global *g, bool full) { const char *str; @@ -158,10 +167,41 @@ static int sink_filter(pa_context *c, struct global *g) return 1; } +static struct global *find_sink_by_name(pa_context *c, const char *name) +{ + struct global *g; + spa_list_for_each(g, &c->globals, link) { + if (sink_filter(c, g, true)) + return g; + } + return NULL; +} + pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct sink_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + if ((g = find_sink_by_name(c, name)) == NULL) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + d->global = g; + return o; } pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata) @@ -174,17 +214,21 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_ pa_assert(c->refcount >= 1); pa_assert(cb); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!sink_filter(c, g)) + if (!sink_filter(c, g, false)) return NULL; ensure_global(c, g); o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data)); d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; d->global = g; return o; } @@ -196,12 +240,13 @@ static void sink_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!sink_filter(c, g)) + if (!sink_filter(c, g, true)) continue; d->global = g; sink_callback(d); } d->cb(c, NULL, 1, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata) @@ -305,7 +350,7 @@ static void source_info(pa_operation *o, void *userdata) d->cb(d->context, NULL, 1, d->userdata); } -static int source_filter(pa_context *c, struct global *g) +static int source_filter(pa_context *c, struct global *g, bool full) { const char *str; @@ -340,7 +385,7 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, p if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!source_filter(c, g)) + if (!source_filter(c, g, false)) return NULL; ensure_global(c, g); @@ -358,7 +403,7 @@ static void source_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!source_filter(c, g)) + if (!source_filter(c, g, true)) continue; d->global = g; source_callback(d); @@ -471,7 +516,7 @@ static void module_info(pa_operation *o, void *userdata) d->cb(d->context, NULL, 1, d->userdata); } -static int module_filter(pa_context *c, struct global *g) +static int module_filter(pa_context *c, struct global *g, bool full) { if (g->type != c->t->module) return 0; @@ -492,7 +537,7 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_ if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!module_filter(c, g)) + if (!module_filter(c, g, false)) return NULL; ensure_global(c, g); @@ -511,7 +556,7 @@ static void module_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!module_filter(c, g)) + if (!module_filter(c, g, true)) continue; d->global = g; module_callback(d); @@ -583,7 +628,7 @@ static void client_info(pa_operation *o, void *userdata) d->cb(d->context, NULL, 1, d->userdata); } -static int client_filter(pa_context *c, struct global *g) +static int client_filter(pa_context *c, struct global *g, bool full) { if (g->type != c->t->client) return 0; @@ -604,7 +649,7 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_ if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!client_filter(c, g)) + if (!client_filter(c, g, false)) return NULL; ensure_global(c, g); @@ -623,7 +668,7 @@ static void client_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!client_filter(c, g)) + if (!client_filter(c, g, true)) continue; d->global = g; client_callback(d); @@ -694,16 +739,118 @@ pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card return NULL; } +struct sink_input_data { + pa_context *context; + pa_sink_input_info_cb_t cb; + void *userdata; + struct global *global; +}; + +static void sink_input_callback(struct sink_input_data *d) +{ + struct global *g = d->global; + struct pw_node_info *info = g->info; + pa_sink_input_info i; + pa_format_info ii[1]; + + spa_zero(i); + i.index = g->id; + i.name = info->name; + i.proplist = pa_proplist_new_dict(info->props); + i.owner_module = g->parent_id; + ii[0].encoding = PA_ENCODING_PCM; + ii[0].plist = pa_proplist_new(); + i.format = ii; + + d->cb(d->context, &i, 0, d->userdata); +} + +static void sink_input_info(pa_operation *o, void *userdata) +{ + struct sink_input_data *d = userdata; + sink_input_callback(d); + d->cb(d->context, NULL, 1, d->userdata); +} + +static int sink_input_filter(pa_context *c, struct global *g, bool full) +{ + const char *str; + struct pw_node_info *info = g->info; + + if (g->type != c->t->node) + return 0; + + if (full) { + if (info == NULL || info->props == NULL) + return 0; + if ((str = spa_dict_lookup(info->props, "node.stream")) == NULL) + return 0; + if (pw_properties_parse_bool(str) == false) + return 0; + if (info->n_output_ports == 0) + return 0; + } + return 1; +} + pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct sink_input_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!sink_input_filter(c, g, false)) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, sink_input_info, sizeof(struct sink_input_data)); + d = o->userdata; + d->global = g; + return o; +} + +static void sink_input_info_list(pa_operation *o, void *userdata) +{ + struct sink_input_data *d = userdata; + pa_context *c = d->context; + struct global *g; + + spa_list_for_each(g, &c->globals, link) { + if (!sink_input_filter(c, g, true)) + continue; + d->global = g; + sink_input_callback(d); + } + d->cb(c, NULL, 1, d->userdata); } pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct sink_input_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + ensure_types(c, c->t->node, sink_input_filter); + o = pa_operation_new(c, NULL, sink_input_info_list, sizeof(struct sink_input_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + return o; } pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) diff --git a/src/meson.build b/src/meson.build index 1a7e58c63..862c8670f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,6 +7,7 @@ pipewire_pulseaudio_sources = [ 'error.c', 'ext-device-manager.c', 'ext-device-restore.c', + 'ext-stream-restore.c', 'format.c', 'introspect.c', 'json.c', @@ -20,6 +21,8 @@ pipewire_pulseaudio_sources = [ 'stream.c', 'strbuf.c', 'subscribe.c', + 'thread-mainloop.c', + 'timeval.c', 'utf8.c', 'util.c', 'version.c', diff --git a/src/proplist.c b/src/proplist.c index 999b86d6e..4ed222c3b 100644 --- a/src/proplist.c +++ b/src/proplist.c @@ -85,8 +85,13 @@ int pa_proplist_setp(pa_proplist *p, const char *pair) int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) { - pw_log_warn("Not Implemented"); - return -1; + va_list varargs; + + va_start(varargs, format); + pw_properties_setva(p->props, key, format, varargs); + va_end(varargs); + + return 0; } int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) diff --git a/src/stream.c b/src/stream.c index 0256be281..9820e2528 100644 --- a/src/stream.c +++ b/src/stream.c @@ -23,6 +23,7 @@ #include #include +#include #include #include "internal.h" @@ -56,16 +57,36 @@ static void stream_state_changed(void *data, enum pw_stream_state old, static void stream_format_changed(void *data, const struct spa_pod *format) { -} + pa_stream *s = data; + s->sample_spec.format = PA_SAMPLE_S16NE, + s->sample_spec.rate = 44100; + s->sample_spec.channels = 2; + + if (s->format) + pa_format_info_free(s->format); + s->format = pa_format_info_from_sample_spec(&s->sample_spec, NULL); +} static void stream_process(void *data) { pa_stream *s = data; if (s->direction == PA_STREAM_PLAYBACK) { + struct pw_buffer *buf; + uint32_t index; + + buf = pw_stream_dequeue_buffer(s->stream); + if (buf != NULL) { + spa_ringbuffer_get_write_index(&s->dequeued_ring, &index); + s->dequeued[index & MASK_BUFFERS] = buf; + spa_ringbuffer_write_update(&s->dequeued_ring, index + 1); + + s->dequeued_size += buf->buffer->datas[0].maxsize; + } + if (s->write_callback) - s->write_callback(s, 4096, s->write_userdata); + s->write_callback(s, s->dequeued_size, s->write_userdata); } else { if (s->read_callback) @@ -159,6 +180,8 @@ pa_stream* stream_new(pa_context *c, const char *name, s->timing_info_valid = false; + spa_ringbuffer_init(&s->dequeued_ring); + spa_list_append(&c->streams, &s->link); pa_stream_ref(s); @@ -358,6 +381,12 @@ static int create_stream(pa_stream_direction_t direction, ":", s->type.format_audio.channels, "i", 2, ":", s->type.format_audio.rate, "i", 44100); + if (attr) + s->buffer_attr = *attr; + + if (dev == NULL) + dev = getenv("PIPEWIRE_NODE"); + res = pw_stream_connect(s->stream, direction == PA_STREAM_PLAYBACK ? PW_DIRECTION_OUTPUT : @@ -389,14 +418,23 @@ int pa_stream_connect_record( return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL); } +static void on_disconnected(pa_operation *o, void *userdata) +{ + pa_stream_set_state(o->stream, PA_STREAM_TERMINATED); +} + int pa_stream_disconnect(pa_stream *s) { + pa_operation *o; + spa_assert(s); spa_assert(s->refcount >= 1); PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - pa_stream_set_state(s, PA_STREAM_TERMINATED); + pw_stream_disconnect(s->stream); + o = pa_operation_new(s->context, s, on_disconnected, 0); + pa_operation_unref(o); return 0; } @@ -405,6 +443,9 @@ int pa_stream_begin_write( void **data, size_t *nbytes) { + int32_t avail; + uint32_t index; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -415,16 +456,21 @@ int pa_stream_begin_write( PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID); if (s->buffer == NULL) { - s->buffer = pw_stream_dequeue_buffer(s->stream); - } - if (s->buffer == NULL) { - *data = NULL; - *nbytes = 0; - } - else { - *data = s->buffer->buffer->datas[0].data; - *nbytes = s->buffer->buffer->datas[0].maxsize; + if ((avail = spa_ringbuffer_get_read_index(&s->dequeued_ring, &index)) <= 0) { + *data = NULL; + *nbytes = 0; + return 0; + } + s->buffer = s->dequeued[index & MASK_BUFFERS]; + s->buffer_index = index; + s->buffer_data = s->buffer->buffer->datas[0].data; + s->buffer_size = s->buffer->buffer->datas[0].maxsize; + s->buffer_offset = 0; } + + *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); + *nbytes = s->buffer_size - s->buffer_offset; + return 0; } @@ -436,7 +482,9 @@ int pa_stream_cancel_write(pa_stream *s) PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - pw_log_warn("Not Implemented"); + + s->buffer = NULL; + return 0; } @@ -475,11 +523,16 @@ int pa_stream_write_ext_free(pa_stream *s, pw_log_warn("Not Implemented"); return PA_ERR_INVALID; } - else { - s->buffer->buffer->datas[0].chunk->offset = 0; - s->buffer->buffer->datas[0].chunk->size = nbytes; - } + + s->buffer->buffer->datas[0].chunk->offset = 0; + s->buffer->buffer->datas[0].chunk->size = nbytes; + + s->dequeued_size -= s->buffer_size; + spa_ringbuffer_read_update(&s->dequeued_ring, s->buffer_index + 1); + pw_stream_queue_buffer(s->stream, s->buffer); + s->buffer = NULL; + return 0; } @@ -520,8 +573,7 @@ size_t pa_stream_writable_size(pa_stream *s) PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1); - pw_log_warn("Not Implemented"); - return 0; + return s->dequeued_size; } size_t pa_stream_readable_size(pa_stream *s) @@ -699,8 +751,23 @@ void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, s->buffer_attr_userdata = userdata; } +struct success_ack { + pa_stream_success_cb_t cb; + void *userdata; +}; + +static void on_success(pa_operation *o, void *userdata) +{ + struct success_ack *d = userdata; + if (d->cb) + d->cb(o->stream, PA_OK, d->userdata); +} + pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -708,13 +775,21 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); s->corked = b; - pw_log_warn("Not Implemented"); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -722,24 +797,40 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); pw_log_warn("Not Implemented"); - return NULL; + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); - PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -747,11 +838,20 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); spa_assert(name); @@ -759,7 +859,13 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) @@ -771,6 +877,8 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); + pw_log_warn("Not Implemented"); + return 0; } @@ -784,6 +892,8 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); + pw_log_warn("Not Implemented"); + return 0; } diff --git a/src/subscribe.c b/src/subscribe.c index de749b587..a78a4d009 100644 --- a/src/subscribe.c +++ b/src/subscribe.c @@ -23,13 +23,43 @@ #include "internal.h" +struct subscribe_data +{ + pa_context_success_cb_t cb; + void *userdata; +}; + +static void on_subscribed(pa_operation *o, void *userdata) +{ + struct subscribe_data *d = userdata; + if (d->cb) + d->cb(o->context, PA_OK, d->userdata); +} + pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct subscribe_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + o = pa_operation_new(c, NULL, on_subscribed, sizeof(struct subscribe_data)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); + pa_assert(c); + pa_assert(c->refcount >= 1); + + if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED) + return; + + c->subscribe_callback = cb; + c->subscribe_userdata = userdata; } diff --git a/src/thread-mainloop.c b/src/thread-mainloop.c new file mode 100644 index 000000000..964f05272 --- /dev/null +++ b/src/thread-mainloop.c @@ -0,0 +1,118 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include +#include + +#include "internal.h" + +struct pa_threaded_mainloop +{ + pa_mainloop *loop; + struct pw_thread_loop *tloop; +}; + +pa_threaded_mainloop *pa_threaded_mainloop_new(void) +{ + pa_threaded_mainloop *m; + + m = calloc(1, sizeof(pa_threaded_mainloop)); + if (m == NULL) + return NULL; + + m->loop = pa_mainloop_new(); + if (m->loop == NULL) + goto no_mem; + + m->tloop = pw_thread_loop_new(m->loop->loop, NULL); + if (m->tloop == NULL) + goto no_mem; + + return m; + + no_mem: + if (m->loop) + pa_mainloop_free(m->loop); + free(m); + return NULL; +} + +void pa_threaded_mainloop_free(pa_threaded_mainloop* m) +{ + pw_thread_loop_destroy(m->tloop); + pa_mainloop_free(m->loop); + free(m); +} + +int pa_threaded_mainloop_start(pa_threaded_mainloop *m) +{ + return pw_thread_loop_start(m->tloop); +} + +void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) +{ + pw_thread_loop_stop(m->tloop); +} + +void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) +{ + pw_thread_loop_lock(m->tloop); +} + +void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) +{ + pw_thread_loop_unlock(m->tloop); +} + +void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) +{ + pw_thread_loop_wait(m->tloop); +} + +void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) +{ + pw_thread_loop_signal(m->tloop, wait_for_accept); +} + +void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) +{ + pw_thread_loop_accept(m->tloop); +} + +int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) +{ + return pa_mainloop_get_retval(m->loop); +} + +pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) +{ + return pa_mainloop_get_api(m->loop); +} + +int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m) +{ + return pw_thread_loop_in_thread(m->tloop); +} + +void pa_threaded_mainloop_set_name(pa_threaded_mainloop *m, const char *name) +{ +} diff --git a/src/timeval.c b/src/timeval.c new file mode 100644 index 000000000..dfdeb782d --- /dev/null +++ b/src/timeval.c @@ -0,0 +1,212 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include + +#include "internal.h" + +#define HAVE_GETTIMEOFDAY + +struct timeval *pa_gettimeofday(struct timeval *tv) { + pa_assert(tv); + +#if defined(OS_IS_WIN32) + /* + * Copied from implementation by Steven Edwards (LGPL). + * Found on wine mailing list. + */ +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define EPOCHFILETIME (116444736000000000i64) +#else +#define EPOCHFILETIME (116444736000000000LL) +#endif +{ + FILETIME ft; + LARGE_INTEGER li; + int64_t t; + + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + t = li.QuadPart; /* In 100-nanosecond intervals */ + t -= EPOCHFILETIME; /* Offset to the Epoch time */ + t /= 10; /* In microseconds */ + tv->tv_sec = (time_t) (t / PA_USEC_PER_SEC); + tv->tv_usec = (suseconds_t) (t % PA_USEC_PER_SEC); +} +#elif defined(HAVE_GETTIMEOFDAY) + pa_assert_se(gettimeofday(tv, NULL) == 0); +#else +#error "Platform lacks gettimeofday() or equivalent function." +#endif + + return tv; +} + +pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { + pa_usec_t r; + + pa_assert(a); + pa_assert(b); + + /* Check which is the earlier time and swap the two arguments if required. */ + if (PA_UNLIKELY(pa_timeval_cmp(a, b) < 0)) { + const struct timeval *c; + c = a; + a = b; + b = c; + } + + /* Calculate the second difference*/ + r = ((pa_usec_t) a->tv_sec - (pa_usec_t) b->tv_sec) * PA_USEC_PER_SEC; + + /* Calculate the microsecond difference */ + if (a->tv_usec > b->tv_usec) + r += (pa_usec_t) a->tv_usec - (pa_usec_t) b->tv_usec; + else if (a->tv_usec < b->tv_usec) + r -= (pa_usec_t) b->tv_usec - (pa_usec_t) a->tv_usec; + + return r; +} + +int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { + pa_assert(a); + pa_assert(b); + + if (a->tv_sec < b->tv_sec) + return -1; + + if (a->tv_sec > b->tv_sec) + return 1; + + if (a->tv_usec < b->tv_usec) + return -1; + + if (a->tv_usec > b->tv_usec) + return 1; + + return 0; +} + +pa_usec_t pa_timeval_age(const struct timeval *tv) { + struct timeval now; + pa_assert(tv); + + return pa_timeval_diff(pa_gettimeofday(&now), tv); +} + +struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) { + time_t secs; + pa_assert(tv); + + secs = (time_t) (v/PA_USEC_PER_SEC); + + if (PA_UNLIKELY(tv->tv_sec > PA_INT_TYPE_MAX(time_t) - secs)) + goto overflow; + + tv->tv_sec += secs; + v -= (pa_usec_t) secs * PA_USEC_PER_SEC; + tv->tv_usec += (suseconds_t) v; + + /* Normalize */ + while ((pa_usec_t) tv->tv_usec >= PA_USEC_PER_SEC) { + + if (PA_UNLIKELY(tv->tv_sec >= PA_INT_TYPE_MAX(time_t))) + goto overflow; + + tv->tv_sec++; + tv->tv_usec -= (suseconds_t) PA_USEC_PER_SEC; + } + + return tv; + +overflow: + tv->tv_sec = PA_INT_TYPE_MAX(time_t); + tv->tv_usec = (suseconds_t) (PA_USEC_PER_SEC-1); + return tv; +} + +struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v) { + time_t secs; + pa_assert(tv); + + secs = (time_t) (v/PA_USEC_PER_SEC); + + if (PA_UNLIKELY(tv->tv_sec < secs)) + goto underflow; + + tv->tv_sec -= secs; + v -= (pa_usec_t) secs * PA_USEC_PER_SEC; + + if (tv->tv_usec >= (suseconds_t) v) + tv->tv_usec -= (suseconds_t) v; + else { + + if (PA_UNLIKELY(tv->tv_sec <= 0)) + goto underflow; + + tv->tv_sec --; + tv->tv_usec += (suseconds_t) (PA_USEC_PER_SEC - v); + } + + return tv; + +underflow: + tv->tv_sec = 0; + tv->tv_usec = 0; + return tv; +} + +struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) { + pa_assert(tv); + + if (PA_UNLIKELY(v == PA_USEC_INVALID)) { + tv->tv_sec = PA_INT_TYPE_MAX(time_t); + tv->tv_usec = (suseconds_t) (PA_USEC_PER_SEC-1); + + return tv; + } + + tv->tv_sec = (time_t) (v / PA_USEC_PER_SEC); + tv->tv_usec = (suseconds_t) (v % PA_USEC_PER_SEC); + + return tv; +} + +pa_usec_t pa_timeval_load(const struct timeval *tv) { + + if (PA_UNLIKELY(!tv)) + return PA_USEC_INVALID; + + return + (pa_usec_t) tv->tv_sec * PA_USEC_PER_SEC + + (pa_usec_t) tv->tv_usec; +} From b90101bf5b03ad33bd1b9bfe47e651bb120d7b86 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 7 Jun 2018 11:04:52 +0200 Subject: [PATCH 005/116] channelmap: fix parse --- src/channelmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/channelmap.c b/src/channelmap.c index 894774754..588f0ea2d 100644 --- a/src/channelmap.c +++ b/src/channelmap.c @@ -584,7 +584,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { map.channels = 0; - tokens = pw_split_strv(s, ",", -1, &n_tokens); + tokens = pw_split_strv(s, ",", INT_MAX, &n_tokens); for (i = 0; i < n_tokens; i++) { pa_channel_position_t f; From 2254a124af849ab2c4b623ffdb2c312eacc363cf Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 7 Jun 2018 11:16:09 +0200 Subject: [PATCH 006/116] implement more api --- src/context.c | 180 ++++++++++++++++++----- src/internal.h | 33 ++++- src/mainloop.c | 146 ++++++++++++++++--- src/stream.c | 383 ++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 603 insertions(+), 139 deletions(-) diff --git a/src/context.c b/src/context.c index 1df2625de..0a4b3a812 100644 --- a/src/context.c +++ b/src/context.c @@ -26,12 +26,13 @@ #include #include +#include #include "internal.h" int pa_context_set_error(pa_context *c, int error) { - spa_assert(error >= 0); - spa_assert(error < PA_ERR_MAX); + pa_assert(error >= 0); + pa_assert(error < PA_ERR_MAX); if (c) c->error = error; return error; @@ -52,8 +53,8 @@ static void context_unlink(pa_context *c) } void pa_context_set_state(pa_context *c, pa_context_state_t st) { - spa_assert(c); - spa_assert(c->refcount >= 1); + pa_assert(c); + pa_assert(c->refcount >= 1); if (c->state == st) return; @@ -72,8 +73,8 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) { } static void context_fail(pa_context *c, int error) { - spa_assert(c); - spa_assert(c->refcount >= 1); + pa_assert(c); + pa_assert(c->refcount >= 1); pa_context_set_error(c, error); pa_context_set_state(c, PA_CONTEXT_FAILED); @@ -178,6 +179,7 @@ static void remote_state_changed(void *data, enum pw_remote_state old, o = pa_operation_new(c, NULL, on_ready, sizeof(struct ready_data)); d = o->userdata; d->context = c; + pa_operation_unref(o); break; } } @@ -213,11 +215,12 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * struct pw_properties *props; pa_context *c; - spa_assert(mainloop); + pa_assert(mainloop); props = pw_properties_new(NULL, NULL); if (name) pw_properties_set(props, PA_PROP_APPLICATION_NAME, name); + pw_properties_set(props, "client.api", "pulseaudio"); loop = mainloop->userdata; core = pw_core_new(loop, NULL); @@ -264,8 +267,8 @@ static void context_free(pa_context *c) void pa_context_unref(pa_context *c) { - spa_assert(c); - spa_assert(c->refcount >= 1); + pa_assert(c); + pa_assert(c->refcount >= 1); if (--c->refcount == 0) context_free(c); @@ -273,16 +276,16 @@ void pa_context_unref(pa_context *c) pa_context* pa_context_ref(pa_context *c) { - spa_assert(c); - spa_assert(c->refcount >= 1); + pa_assert(c); + pa_assert(c->refcount >= 1); c->refcount++; return c; } void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) { - spa_assert(c); - spa_assert(c->refcount >= 1); + pa_assert(c); + pa_assert(c->refcount >= 1); if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED) return; @@ -293,8 +296,8 @@ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, voi void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata) { - spa_assert(c); - spa_assert(c->refcount >= 1); + pa_assert(c); + pa_assert(c->refcount >= 1); if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED) return; @@ -308,21 +311,25 @@ int pa_context_errno(pa_context *c) if (!c) return PA_ERR_INVALID; - spa_assert(c->refcount >= 1); + pa_assert(c->refcount >= 1); return c->error; } int pa_context_is_pending(pa_context *c) { - pw_log_warn("Not Implemented"); - return 0; + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE); + + return !spa_list_is_empty(&c->operations); } pa_context_state_t pa_context_get_state(pa_context *c) { - spa_assert(c); - spa_assert(c->refcount >= 1); + pa_assert(c); + pa_assert(c->refcount >= 1); return c->state; } @@ -330,8 +337,8 @@ int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t fla { int res; - spa_assert(c); - spa_assert(c->refcount >= 1); + pa_assert(c); + pa_assert(c->refcount >= 1); PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(c, !(flags & ~(PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL)), PA_ERR_INVALID); @@ -350,8 +357,8 @@ int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t fla void pa_context_disconnect(pa_context *c) { - spa_assert(c); - spa_assert(c->refcount >= 1); + pa_assert(c); + pa_assert(c->refcount >= 1); pw_remote_disconnect(c->remote); @@ -359,16 +366,58 @@ void pa_context_disconnect(pa_context *c) pa_context_set_state(c, PA_CONTEXT_TERMINATED); } +struct notify_data { + pa_context_notify_cb_t cb; + void *userdata; +}; + +static void on_notify(pa_operation *o, void *userdata) +{ + struct notify_data *d = userdata; + pa_operation_done(o); + if (d->cb) + d->cb(o->context, d->userdata); +} + pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct notify_data *d; + + o = pa_operation_new(c, NULL, on_notify, sizeof(struct notify_data)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; +} + +struct success_data { + pa_context_success_cb_t cb; + void *userdata; + int ret; +}; + +static void on_success(pa_operation *o, void *userdata) +{ + struct success_data *d = userdata; + pa_operation_done(o); + if (d->cb) + d->cb(o->context, d->ret, d->userdata); } pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct success_data *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_data)); + d = o->userdata; + d->ret = PA_ERR_ACCESS; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) @@ -385,20 +434,51 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_ int pa_context_is_local(pa_context *c) { - pw_log_warn("Not Implemented"); - return 0; + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, -1); + + return 1; } pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + struct spa_dict dict; + struct spa_dict_item items[1]; + pa_operation *o; + struct success_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(name); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + items[0] = SPA_DICT_ITEM_INIT(PA_PROP_APPLICATION_NAME, name); + dict = SPA_DICT_INIT(items, 1); + pw_remote_update_properties(c->remote, &dict); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_data)); + d = o->userdata; + d->ret = PA_ERR_ACCESS; + d->cb = cb; + d->userdata = userdata; + + return o; } const char* pa_context_get_server(pa_context *c) { - pw_log_warn("Not Implemented"); - return NULL; + const struct pw_core_info *info; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + info = pw_remote_get_core_info(c->remote); + PA_CHECK_VALIDITY_RETURN_NULL(c, info && info->name, PA_ERR_NOENTITY); + + return info->name; } uint32_t pa_context_get_protocol_version(pa_context *c) @@ -408,6 +488,11 @@ uint32_t pa_context_get_protocol_version(pa_context *c) uint32_t pa_context_get_server_protocol_version(pa_context *c) { + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, PA_INVALID_INDEX); + return PA_PROTOCOL_VERSION; } @@ -430,13 +515,34 @@ uint32_t pa_context_get_index(pa_context *c) pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + struct timeval tv; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(c->mainloop); + + if (usec == PA_USEC_INVALID) + return c->mainloop->time_new(c->mainloop, NULL, cb, userdata); + + pa_timeval_store(&tv, usec); + + return c->mainloop->time_new(c->mainloop, &tv, cb, userdata); } void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) { - pw_log_warn("Not Implemented"); + struct timeval tv; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(c->mainloop); + + if (usec == PA_USEC_INVALID) + c->mainloop->time_restart(e, NULL); + else { + pa_timeval_store(&tv, usec); + c->mainloop->time_restart(e, &tv); + } } size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss) diff --git a/src/internal.h b/src/internal.h index 05671af75..443ba860d 100644 --- a/src/internal.h +++ b/src/internal.h @@ -146,6 +146,32 @@ struct pa_proplist { pa_proplist* pa_proplist_new_props(struct pw_properties *props); pa_proplist* pa_proplist_new_dict(struct spa_dict *dict); +struct pa_io_event { + struct spa_source *source; + struct pa_mainloop *mainloop; + int fd; + pa_io_event_flags_t events; + pa_io_event_cb_t cb; + void *userdata; + pa_io_event_destroy_cb_t destroy; +}; + +struct pa_time_event { + struct spa_source *source; + struct pa_mainloop *mainloop; + pa_time_event_cb_t cb; + void *userdata; + pa_time_event_destroy_cb_t destroy; +}; + +struct pa_defer_event { + struct spa_source *source; + struct pa_mainloop *mainloop; + pa_defer_event_cb_t cb; + void *userdata; + pa_defer_event_destroy_cb_t destroy; +}; + struct pa_mainloop { struct pw_loop *loop; struct spa_source *event; @@ -248,6 +274,7 @@ struct pa_stream { pa_stream_direction_t direction; pa_stream_state_t state; pa_stream_flags_t flags; + bool disconnecting; pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -300,10 +327,10 @@ struct pa_stream { size_t dequeued_size; struct pw_buffer *buffer; - void *buffer_data; uint32_t buffer_index; - int64_t buffer_size; - int64_t buffer_offset; + void *buffer_data; + uint32_t buffer_size; + uint32_t buffer_offset; }; void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); diff --git a/src/mainloop.c b/src/mainloop.c index da1ae450b..7663dedad 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -32,68 +32,183 @@ static void do_stop(void *data, uint64_t count) this->quit = true; } +static enum spa_io map_flags_to_spa(pa_io_event_flags_t flags) { + return (enum spa_io) + ((flags & PA_IO_EVENT_INPUT ? SPA_IO_IN : 0) | + (flags & PA_IO_EVENT_OUTPUT ? SPA_IO_OUT : 0) | + (flags & PA_IO_EVENT_ERROR ? SPA_IO_ERR : 0) | + (flags & PA_IO_EVENT_HANGUP ? SPA_IO_HUP : 0)); +} + +static pa_io_event_flags_t map_flags_from_spa(enum spa_io flags) { + return (flags & SPA_IO_IN ? PA_IO_EVENT_INPUT : 0) | + (flags & SPA_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | + (flags & SPA_IO_ERR ? PA_IO_EVENT_ERROR : 0) | + (flags & SPA_IO_HUP ? PA_IO_EVENT_HANGUP : 0); +} + +static void source_io_func(void *data, int fd, enum spa_io mask) +{ + pa_io_event *ev = data; + if (ev->cb) + ev->cb(&ev->mainloop->api, ev, ev->fd, map_flags_from_spa(mask), ev->userdata); +} + static pa_io_event* api_io_new(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_mainloop *mainloop = SPA_CONTAINER_OF(a, pa_mainloop, api); + pa_io_event *ev; + + pa_assert(a); + pa_assert(fd >= 0); + pa_assert(cb); + + ev = calloc(1, sizeof(pa_io_event)); + ev->source = pw_loop_add_io(mainloop->loop, fd, + map_flags_to_spa(events), false, source_io_func, ev); + ev->fd = fd; + ev->events = events; + ev->mainloop = mainloop; + ev->cb = cb; + ev->userdata = userdata; + + return ev; } static void api_io_enable(pa_io_event* e, pa_io_event_flags_t events) { - pw_log_warn("Not Implemented"); + pa_assert(e); + + if (e->events == events) + return; + + e->events = events; + pw_loop_update_io(e->mainloop->loop, e->source, map_flags_to_spa(events)); } static void api_io_free(pa_io_event* e) { - pw_log_warn("Not Implemented"); + pa_assert(e); + pw_loop_destroy_source(e->mainloop->loop, e->source); + if (e->destroy) + e->destroy(&e->mainloop->api, e, e->userdata); + free(e); } static void api_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb) { - pw_log_warn("Not Implemented"); + pa_assert(e); + e->destroy = cb; +} + +static void source_timer_func(void *data, uint64_t expirations) +{ + pa_time_event *ev = data; + struct timeval tv; + if (ev->cb) + ev->cb(&ev->mainloop->api, ev, &tv, ev->userdata); } static pa_time_event* api_time_new(pa_mainloop_api*a, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_mainloop *mainloop = SPA_CONTAINER_OF(a, pa_mainloop, api); + pa_time_event *ev; + struct timespec ts; + + ev = calloc(1, sizeof(pa_time_event)); + ev->source = pw_loop_add_timer(mainloop->loop, source_timer_func, ev); + ev->mainloop = mainloop; + ev->cb = cb; + ev->userdata = userdata; + + if (tv == NULL) { + ts.tv_sec = 0; + ts.tv_nsec = 1; + } + else { + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000LL; + } + pw_loop_update_timer(mainloop->loop, ev->source, &ts, NULL, true); + + return ev; } static void api_time_restart(pa_time_event* e, const struct timeval *tv) { - pw_log_warn("Not Implemented"); + struct timespec ts; + + pa_assert(e); + + if (tv == NULL) { + ts.tv_sec = 0; + ts.tv_nsec = 1; + } + else { + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000LL; + } + pw_loop_update_timer(e->mainloop->loop, e->source, &ts, NULL, true); } static void api_time_free(pa_time_event* e) { - pw_log_warn("Not Implemented"); + pa_assert(e); + pw_loop_destroy_source(e->mainloop->loop, e->source); + if (e->destroy) + e->destroy(&e->mainloop->api, e, e->userdata); + free(e); } static void api_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) { - pw_log_warn("Not Implemented"); + pa_assert(e); + e->destroy = cb; } +static void source_idle_func(void *data) +{ + pa_defer_event *ev = data; + if (ev->cb) + ev->cb(&ev->mainloop->api, ev, ev->userdata); +} static pa_defer_event* api_defer_new(pa_mainloop_api*a, pa_defer_event_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_mainloop *mainloop = SPA_CONTAINER_OF(a, pa_mainloop, api); + pa_defer_event *ev; + + pa_assert(a); + pa_assert(cb); + + ev = calloc(1, sizeof(pa_defer_event)); + ev->source = pw_loop_add_idle(mainloop->loop, true, source_idle_func, ev); + ev->mainloop = mainloop; + ev->cb = cb; + ev->userdata = userdata; + + return ev; } static void api_defer_enable(pa_defer_event* e, int b) { - pw_log_warn("Not Implemented"); + pa_assert(e); + pw_loop_enable_idle(e->mainloop->loop, e->source, b ? true : false); } static void api_defer_free(pa_defer_event* e) { - pw_log_warn("Not Implemented"); + pa_assert(e); + pw_loop_destroy_source(e->mainloop->loop, e->source); + if (e->destroy) + e->destroy(&e->mainloop->api, e, e->userdata); + free(e); } static void api_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb) { - pw_log_warn("Not Implemented"); + pa_assert(e); + e->destroy = cb; } static void api_quit(pa_mainloop_api*a, int retval) @@ -122,7 +237,6 @@ static const pa_mainloop_api api = .defer_set_destroy = api_defer_set_destroy, .quit = api_quit, - }; pa_mainloop *pa_mainloop_new(void) diff --git a/src/stream.c b/src/stream.c index 9820e2528..036cb5da1 100644 --- a/src/stream.c +++ b/src/stream.c @@ -39,7 +39,8 @@ static void stream_state_changed(void *data, enum pw_stream_state old, pa_stream_set_state(s, PA_STREAM_FAILED); break; case PW_STREAM_STATE_UNCONNECTED: - pa_stream_set_state(s, PA_STREAM_UNCONNECTED); + if (!s->disconnecting) + pa_stream_set_state(s, PA_STREAM_UNCONNECTED); break; case PW_STREAM_STATE_CONNECTING: pa_stream_set_state(s, PA_STREAM_CREATING); @@ -71,26 +72,28 @@ static void stream_format_changed(void *data, const struct spa_pod *format) static void stream_process(void *data) { pa_stream *s = data; + struct pw_buffer *buf; + uint32_t index; + + s->timing_info_valid = true; + + buf = pw_stream_dequeue_buffer(s->stream); + if (buf == NULL) + return; + + spa_ringbuffer_get_write_index(&s->dequeued_ring, &index); + s->dequeued[index & MASK_BUFFERS] = buf; + spa_ringbuffer_write_update(&s->dequeued_ring, index + 1); if (s->direction == PA_STREAM_PLAYBACK) { - struct pw_buffer *buf; - uint32_t index; - - buf = pw_stream_dequeue_buffer(s->stream); - if (buf != NULL) { - spa_ringbuffer_get_write_index(&s->dequeued_ring, &index); - s->dequeued[index & MASK_BUFFERS] = buf; - spa_ringbuffer_write_update(&s->dequeued_ring, index + 1); - - s->dequeued_size += buf->buffer->datas[0].maxsize; - } - + s->dequeued_size += buf->buffer->datas[0].maxsize; if (s->write_callback) s->write_callback(s, s->dequeued_size, s->write_userdata); } else { + s->dequeued_size += buf->buffer->datas[0].chunk->size; if (s->read_callback) - s->read_callback(s, 4096, s->read_userdata); + s->read_callback(s, s->dequeued_size, s->read_userdata); } } @@ -122,7 +125,11 @@ pa_stream* stream_new(pa_context *c, const char *name, if (s == NULL) return NULL; - s->stream = pw_stream_new(c->remote, name, NULL); + + s->stream = pw_stream_new(c->remote, name, + pw_properties_new( + "client.api", "pulseaudio", + NULL)); s->refcount = 1; s->context = c; init_type(&s->type, pw_core_get_type(c->core)->map); @@ -178,8 +185,6 @@ pa_stream* stream_new(pa_context *c, const char *name, s->device_index = PA_INVALID_INDEX; - s->timing_info_valid = false; - spa_ringbuffer_init(&s->dequeued_ring); spa_list_append(&c->streams, &s->link); @@ -341,6 +346,97 @@ int pa_stream_is_corked(pa_stream *s) return s->corked; } +static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flags_t *flags) { + const char *e; + + pa_assert(s); + pa_assert(attr); + + if ((e = getenv("PULSE_LATENCY_MSEC"))) { + uint32_t ms; + pa_sample_spec ss; + + pa_sample_spec_init(&ss); + + if (pa_sample_spec_valid(&s->sample_spec)) + ss = s->sample_spec; + else if (s->n_formats == 1) + pa_format_info_to_sample_spec(s->req_formats[0], &ss, NULL); + + if ((ms = atoi(e)) < 0 || ms <= 0) { + pa_log_debug("Failed to parse $PULSE_LATENCY_MSEC: %s", e); + } + else if (!pa_sample_spec_valid(&s->sample_spec)) { + pa_log_debug("Ignoring $PULSE_LATENCY_MSEC: %s (invalid sample spec)", e); + } + else { + attr->maxlength = (uint32_t) -1; + attr->tlength = pa_usec_to_bytes(ms * PA_USEC_PER_MSEC, &ss); + attr->minreq = (uint32_t) -1; + attr->prebuf = (uint32_t) -1; + attr->fragsize = attr->tlength; + + if (flags) + *flags |= PA_STREAM_ADJUST_LATENCY; + } + } + + if (attr->maxlength == (uint32_t) -1) + attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */ + + if (attr->tlength == (uint32_t) -1) + attr->tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &s->sample_spec); /* 250ms of buffering */ + + if (attr->minreq == (uint32_t) -1) + attr->minreq = (attr->tlength)/5; /* Ask for more data when there are only 200ms left in the playback buffer */ + + if (attr->prebuf == (uint32_t) -1) + attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */ + + if (attr->fragsize == (uint32_t) -1) + attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */ +} + +static const uint32_t audio_formats[] = { + [PA_SAMPLE_U8] = offsetof(struct spa_type_audio_format, U8), + [PA_SAMPLE_ALAW] = offsetof(struct spa_type_audio_format, UNKNOWN), + [PA_SAMPLE_ULAW] = offsetof(struct spa_type_audio_format, UNKNOWN), + [PA_SAMPLE_S16NE] = offsetof(struct spa_type_audio_format, S16), + [PA_SAMPLE_S16RE] = offsetof(struct spa_type_audio_format, S16_OE), + [PA_SAMPLE_FLOAT32NE] = offsetof(struct spa_type_audio_format, F32), + [PA_SAMPLE_FLOAT32RE] = offsetof(struct spa_type_audio_format, F32_OE), + [PA_SAMPLE_S32NE] = offsetof(struct spa_type_audio_format, S32), + [PA_SAMPLE_S32RE] = offsetof(struct spa_type_audio_format, S32_OE), + [PA_SAMPLE_S24NE] = offsetof(struct spa_type_audio_format, S24), + [PA_SAMPLE_S24RE] = offsetof(struct spa_type_audio_format, S24_OE), + [PA_SAMPLE_S24_32NE] = offsetof(struct spa_type_audio_format, S24_32), + [PA_SAMPLE_S24_32RE] = offsetof(struct spa_type_audio_format, S24_32_OE), +}; + +static inline uint32_t get_format(pa_stream *s, pa_sample_format_t format) +{ + if (format < 0 || format >= SPA_N_ELEMENTS(audio_formats)) + return s->type.audio_format.UNKNOWN; + return *SPA_MEMBER(&s->type.audio_format, audio_formats[format], uint32_t); +} + +static const struct spa_pod *get_param(pa_stream *s, pa_sample_spec *ss, pa_channel_map *map, + struct spa_pod_builder *b) +{ + const struct spa_pod *param; + struct pw_type *t = pw_core_get_type(s->context->core); + + param = spa_pod_builder_object(b, + t->param.idEnumFormat, t->spa_format, + "I", s->type.media_type.audio, + "I", s->type.media_subtype.raw, + ":", s->type.format_audio.format, "I", get_format(s, ss->format), + ":", s->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_INTERLEAVED, + ":", s->type.format_audio.channels, "i", ss->channels, + ":", s->type.format_audio.rate, "i", ss->rate); + return param; +} + static int create_stream(pa_stream_direction_t direction, pa_stream *s, const char *dev, @@ -351,49 +447,68 @@ static int create_stream(pa_stream_direction_t direction, { int res; enum pw_stream_flags fl; - const struct spa_pod *params[1]; - uint8_t buffer[1024]; + const struct spa_pod *params[16]; + uint32_t n_params = 0; + uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - struct pw_type *t; + struct pw_properties *props; spa_assert(s); spa_assert(s->refcount >= 1); s->direction = direction; - - t = pw_core_get_type(s->context->core); + s->timing_info_valid = false; + s->disconnecting = false; pa_stream_set_state(s, PA_STREAM_CREATING); fl = PW_STREAM_FLAG_AUTOCONNECT | - PW_STREAM_FLAG_MAP_BUFFERS | - PW_STREAM_FLAG_RT_PROCESS; + PW_STREAM_FLAG_MAP_BUFFERS; + s->corked = SPA_FLAG_CHECK(flags, PA_STREAM_START_CORKED); + + if (s->corked) + fl |= PW_STREAM_FLAG_INACTIVE; if (flags & PA_STREAM_PASSTHROUGH) fl |= PW_STREAM_FLAG_EXCLUSIVE; - params[0] = spa_pod_builder_object(&b, - t->param.idEnumFormat, t->spa_format, - "I", s->type.media_type.audio, - "I", s->type.media_subtype.raw, - ":", s->type.format_audio.format, "I", s->type.audio_format.S16, - ":", s->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_INTERLEAVED, - ":", s->type.format_audio.channels, "i", 2, - ":", s->type.format_audio.rate, "i", 44100); + if (pa_sample_spec_valid(&s->sample_spec)) { + params[n_params++] = get_param(s, &s->sample_spec, &s->channel_map, &b); + } + else { + pa_sample_spec ss; + pa_channel_map map; + int i; + + for (i = 0; i < s->n_formats; i++) { + if (pa_format_info_to_sample_spec(s->req_formats[i], &ss, NULL) < 0) { + char buf[4096]; + pw_log_warn("can't convert format %s", + pa_format_info_snprint(buf,4096,s->req_formats[i])); + continue; + } + + params[n_params++] = get_param(s, &ss, &map, &b); + } + } if (attr) s->buffer_attr = *attr; + patch_buffer_attr(s, &s->buffer_attr, &flags); if (dev == NULL) dev = getenv("PIPEWIRE_NODE"); + props = (struct pw_properties *) pw_stream_get_properties(s->stream); + pw_properties_setf(props, "node.latency", "%u", s->buffer_attr.minreq); + res = pw_stream_connect(s->stream, direction == PA_STREAM_PLAYBACK ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT, dev, fl, - params, 1); + params, n_params); return res; } @@ -420,7 +535,7 @@ int pa_stream_connect_record( static void on_disconnected(pa_operation *o, void *userdata) { - pa_stream_set_state(o->stream, PA_STREAM_TERMINATED); + pa_stream_set_state(o->stream, PA_STREAM_TERMINATED); } int pa_stream_disconnect(pa_stream *s) @@ -432,9 +547,49 @@ int pa_stream_disconnect(pa_stream *s) PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + s->disconnecting = true; pw_stream_disconnect(s->stream); o = pa_operation_new(s->context, s, on_disconnected, 0); pa_operation_unref(o); + + return 0; +} + +int peek_buffer(pa_stream *s) +{ + int32_t avail; + uint32_t index; + + if (s->buffer != NULL) + return 0; + + if ((avail = spa_ringbuffer_get_read_index(&s->dequeued_ring, &index)) <= 0) + return -EPIPE; + + s->buffer = s->dequeued[index & MASK_BUFFERS]; + s->buffer_index = index; + s->buffer_data = s->buffer->buffer->datas[0].data; + if (s->direction == PA_STREAM_RECORD) { + s->buffer_size = s->buffer->buffer->datas[0].chunk->size; + s->buffer_offset = s->buffer->buffer->datas[0].chunk->offset; + } + else { + s->buffer_size = s->buffer->buffer->datas[0].maxsize; + s->buffer_offset = 0; + } + return 0; +} + +int queue_buffer(pa_stream *s) +{ + if (s->buffer == NULL) + return 0; + + s->dequeued_size -= s->buffer_size; + spa_ringbuffer_read_update(&s->dequeued_ring, s->buffer_index + 1); + + pw_stream_queue_buffer(s->stream, s->buffer); + s->buffer = NULL; return 0; } @@ -443,8 +598,6 @@ int pa_stream_begin_write( void **data, size_t *nbytes) { - int32_t avail; - uint32_t index; spa_assert(s); spa_assert(s->refcount >= 1); @@ -455,19 +608,11 @@ int pa_stream_begin_write( PA_CHECK_VALIDITY(s->context, data, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID); - if (s->buffer == NULL) { - if ((avail = spa_ringbuffer_get_read_index(&s->dequeued_ring, &index)) <= 0) { - *data = NULL; - *nbytes = 0; - return 0; - } - s->buffer = s->dequeued[index & MASK_BUFFERS]; - s->buffer_index = index; - s->buffer_data = s->buffer->buffer->datas[0].data; - s->buffer_size = s->buffer->buffer->datas[0].maxsize; - s->buffer_offset = 0; + if (peek_buffer(s) < 0) { + *data = NULL; + *nbytes = 0; + return 0; } - *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); *nbytes = s->buffer_size - s->buffer_offset; @@ -516,23 +661,26 @@ int pa_stream_write_ext_free(pa_stream *s, PA_CHECK_VALIDITY(s->context, seek <= PA_SEEK_RELATIVE_END, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || (seek == PA_SEEK_RELATIVE && offset == 0), PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, + !s->buffer || + ((data >= s->buffer_data) && + ((const char*) data + nbytes <= (const char*) s->buffer_data + s->buffer_size)), + PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, offset % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, nbytes % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, !free_cb || !s->buffer, PA_ERR_INVALID); if (s->buffer == NULL) { pw_log_warn("Not Implemented"); + if (free_cb) + free_cb(free_cb_data); + return PA_ERR_INVALID; + } else { + s->buffer->buffer->datas[0].chunk->offset = data - s->buffer_data; + s->buffer->buffer->datas[0].chunk->size = nbytes; + queue_buffer(s); } - - s->buffer->buffer->datas[0].chunk->offset = 0; - s->buffer->buffer->datas[0].chunk->size = nbytes; - - s->dequeued_size -= s->buffer_size; - spa_ringbuffer_read_update(&s->dequeued_ring, s->buffer_index + 1); - - pw_stream_queue_buffer(s->stream, s->buffer); - s->buffer = NULL; - return 0; } @@ -548,7 +696,14 @@ int pa_stream_peek(pa_stream *s, PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); - pw_log_warn("Not Implemented"); + if (peek_buffer(s) < 0) { + *data = NULL; + *nbytes = 0; + return 0; + } + *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); + *nbytes = s->buffer_size; + return 0; } @@ -559,7 +714,10 @@ int pa_stream_drop(pa_stream *s) PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); - pw_log_warn("Not Implemented"); + PA_CHECK_VALIDITY(s->context, s->buffer, PA_ERR_BADSTATE); + + queue_buffer(s); + return 0; } @@ -585,32 +743,59 @@ size_t pa_stream_readable_size(pa_stream *s) PA_ERR_BADSTATE, (size_t) -1); PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1); - pw_log_warn("Not Implemented"); - return 0; + + return s->dequeued_size; +} + +struct success_ack { + pa_stream_success_cb_t cb; + void *userdata; +}; + +static void on_success(pa_operation *o, void *userdata) +{ + struct success_ack *d = userdata; + pa_operation_done(o); + if (d->cb) + d->cb(o->stream, PA_OK, d->userdata); } pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); - pw_log_warn("Not Implemented"); - return NULL; + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - pw_log_warn("Not Implemented"); - return NULL; + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) @@ -751,18 +936,6 @@ void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, s->buffer_attr_userdata = userdata; } -struct success_ack { - pa_stream_success_cb_t cb; - void *userdata; -}; - -static void on_success(pa_operation *o, void *userdata) -{ - struct success_ack *d = userdata; - if (d->cb) - d->cb(o->stream, PA_OK, d->userdata); -} - pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -870,6 +1043,9 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { + struct pw_time t; + pa_usec_t res; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -877,7 +1053,12 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); - pw_log_warn("Not Implemented"); + pw_stream_get_time(s->stream, &t); + + res = (t.ticks * t.rate.num * PA_USEC_PER_SEC) / t.rate.denom; + + if (r_usec) + *r_usec = res; return 0; } @@ -893,6 +1074,10 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); pw_log_warn("Not Implemented"); + if (r_usec) + *r_usec = 0; + if (negative) + *negative = 0; return 0; } @@ -946,6 +1131,9 @@ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); spa_assert(attr); @@ -953,11 +1141,19 @@ pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + return o; } pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -966,11 +1162,19 @@ pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_strea PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->flags & PA_STREAM_VARIABLE_RATE, PA_ERR_BADSTATE); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + return o; } pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -979,11 +1183,19 @@ pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + return o; } pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -991,7 +1203,12 @@ pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + return o; } int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) From a30722c442f065d6178405d275167324ad3944fc Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 22 Jun 2018 17:41:12 +0200 Subject: [PATCH 007/116] pulse: various fixes and improvements --- src/context.c | 1 + src/internal.h | 29 +++--- src/mainloop.c | 1 + src/rtclock.c | 9 +- src/stream.c | 266 +++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 241 insertions(+), 65 deletions(-) diff --git a/src/context.c b/src/context.c index 0a4b3a812..3087fa082 100644 --- a/src/context.c +++ b/src/context.c @@ -33,6 +33,7 @@ int pa_context_set_error(pa_context *c, int error) { pa_assert(error >= 0); pa_assert(error < PA_ERR_MAX); + pw_log_error("context %p: error %d", c, error); if (c) c->error = error; return error; diff --git a/src/internal.h b/src/internal.h index 443ba860d..ed702f946 100644 --- a/src/internal.h +++ b/src/internal.h @@ -108,19 +108,24 @@ static inline const char *pa_strnull(const char *x) { int pa_context_set_error(pa_context *c, int error); -#define PA_CHECK_VALIDITY(context, expression, error) \ - do { \ - if (!(expression)) \ - return -pa_context_set_error((context), (error)); \ - } while(false) +#define PA_CHECK_VALIDITY(context, expression, error) \ +do { \ + if (!(expression)) { \ + fprintf(stderr, "'%s' failed at %s:%u %s()", \ + #expression , __FILE__, __LINE__, __func__); \ + return -pa_context_set_error((context), (error)); \ + } \ +} while(false) -#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) \ - do { \ - if (!(expression)) { \ - pa_context_set_error((context), (error)); \ - return value; \ - } \ - } while(false) +#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) \ +do { \ + if (!(expression)) { \ + fprintf(stderr, "'%s' failed at %s:%u %s()", \ + #expression , __FILE__, __LINE__, __func__); \ + pa_context_set_error((context), (error)); \ + return value; \ + } \ +} while(false) #define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) \ PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL) diff --git a/src/mainloop.c b/src/mainloop.c index 7663dedad..578b94d6b 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -129,6 +129,7 @@ static pa_time_event* api_time_new(pa_mainloop_api*a, const struct timeval *tv, ts.tv_sec = tv->tv_sec; ts.tv_nsec = tv->tv_usec * 1000LL; } + pw_log_debug("new timer %p %ld %ld", ev, ts.tv_sec, ts.tv_nsec); pw_loop_update_timer(mainloop->loop, ev->source, &ts, NULL, true); return ev; diff --git a/src/rtclock.c b/src/rtclock.c index 96ef0fedf..a0b97c831 100644 --- a/src/rtclock.c +++ b/src/rtclock.c @@ -21,14 +21,17 @@ #include +#include + #include pa_usec_t pa_rtclock_now(void) { struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return ts.tv_sec * SPA_USEC_PER_SEC + - ts.tv_nsec * SPA_NSEC_PER_USEC; + pa_usec_t res; + clock_gettime(CLOCK_MONOTONIC, &ts); + res = (ts.tv_sec * SPA_USEC_PER_SEC) + (ts.tv_nsec / SPA_NSEC_PER_USEC); + return res; } diff --git a/src/stream.c b/src/stream.c index 036cb5da1..e6421420f 100644 --- a/src/stream.c +++ b/src/stream.c @@ -19,6 +19,7 @@ #include #include +#include #include @@ -28,6 +29,65 @@ #include #include "internal.h" +static const uint32_t audio_formats[] = { + [PA_SAMPLE_U8] = offsetof(struct spa_type_audio_format, U8), + [PA_SAMPLE_ALAW] = offsetof(struct spa_type_audio_format, UNKNOWN), + [PA_SAMPLE_ULAW] = offsetof(struct spa_type_audio_format, UNKNOWN), + [PA_SAMPLE_S16NE] = offsetof(struct spa_type_audio_format, S16), + [PA_SAMPLE_S16RE] = offsetof(struct spa_type_audio_format, S16_OE), + [PA_SAMPLE_FLOAT32NE] = offsetof(struct spa_type_audio_format, F32), + [PA_SAMPLE_FLOAT32RE] = offsetof(struct spa_type_audio_format, F32_OE), + [PA_SAMPLE_S32NE] = offsetof(struct spa_type_audio_format, S32), + [PA_SAMPLE_S32RE] = offsetof(struct spa_type_audio_format, S32_OE), + [PA_SAMPLE_S24NE] = offsetof(struct spa_type_audio_format, S24), + [PA_SAMPLE_S24RE] = offsetof(struct spa_type_audio_format, S24_OE), + [PA_SAMPLE_S24_32NE] = offsetof(struct spa_type_audio_format, S24_32), + [PA_SAMPLE_S24_32RE] = offsetof(struct spa_type_audio_format, S24_32_OE), +}; + +static inline uint32_t format_pa2id(pa_stream *s, pa_sample_format_t format) +{ + if (format < 0 || format >= SPA_N_ELEMENTS(audio_formats)) + return s->type.audio_format.UNKNOWN; + return *SPA_MEMBER(&s->type.audio_format, audio_formats[format], uint32_t); +} + +static inline pa_sample_format_t format_id2pa(pa_stream *s, uint32_t id) +{ + int i; + for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) { + if (id == *SPA_MEMBER(&s->type.audio_format, audio_formats[i], uint32_t)) + return i; + } + return PA_SAMPLE_INVALID; +} + +static int dequeue_buffer(pa_stream *s) +{ + struct pw_buffer *buf; + uint32_t index; + + buf = pw_stream_dequeue_buffer(s->stream); + if (buf == NULL) + return -EPIPE; + + spa_ringbuffer_get_write_index(&s->dequeued_ring, &index); + s->dequeued[index & MASK_BUFFERS] = buf; + spa_ringbuffer_write_update(&s->dequeued_ring, index + 1); + + if (s->direction == PA_STREAM_PLAYBACK) + s->dequeued_size += buf->buffer->datas[0].maxsize; + else + s->dequeued_size += buf->buffer->datas[0].chunk->size; + + return 0; +} + +static void configure_buffers(pa_stream *s) +{ + s->buffer_attr.maxlength = 65536; + s->buffer_attr.prebuf = s->buffer_attr.minreq; +} static void stream_state_changed(void *data, enum pw_stream_state old, enum pw_stream_state state, const char *error) @@ -49,6 +109,7 @@ static void stream_state_changed(void *data, enum pw_stream_state old, case PW_STREAM_STATE_READY: break; case PW_STREAM_STATE_PAUSED: + configure_buffers(s); pa_stream_set_state(s, PA_STREAM_READY); break; case PW_STREAM_STATE_STREAMING: @@ -56,42 +117,98 @@ static void stream_state_changed(void *data, enum pw_stream_state old, } } +static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *attr, struct spa_pod_builder *b) +{ + const struct spa_pod *param; + struct pw_type *t = pw_core_get_type(s->context->core); + int32_t blocks, buffers, size, maxsize, stride; + + blocks = 1; + stride = pa_frame_size(&s->sample_spec); + + if (attr->tlength == -1) + maxsize = 1024; + else + maxsize = (attr->tlength / stride); + + if (attr->minreq == -1) + size = SPA_MIN(1024, maxsize); + else + size = SPA_MIN(attr->minreq / stride, maxsize); + + if (attr->maxlength == -1) + buffers = 3; + else + buffers = SPA_CLAMP(attr->maxlength / (maxsize * stride), 3, 64); + + param = spa_pod_builder_object(b, + t->param.idBuffers, t->param_buffers.Buffers, + ":", t->param_buffers.buffers, "iru", buffers, + SPA_POD_PROP_MIN_MAX(3, 64), + ":", t->param_buffers.blocks, "i", blocks, + ":", t->param_buffers.size, "iru", size * stride, + SPA_POD_PROP_MIN_MAX(size * stride, maxsize * stride), + ":", t->param_buffers.stride, "i", stride, + ":", t->param_buffers.align, "i", 16); + return param; +} + static void stream_format_changed(void *data, const struct spa_pod *format) { pa_stream *s = data; + const struct spa_pod *params[4]; + uint32_t n_params = 0; + uint8_t buffer[4096]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + struct spa_audio_info info = { 0 }; + int res; - s->sample_spec.format = PA_SAMPLE_S16NE, - s->sample_spec.rate = 44100; - s->sample_spec.channels = 2; + spa_pod_object_parse(format, + "I", &info.media_type, + "I", &info.media_subtype); + + if (info.media_type != s->type.media_type.audio || + info.media_subtype != s->type.media_subtype.raw || + spa_format_audio_raw_parse(format, &info.info.raw, &s->type.format_audio) < 0 || + info.info.raw.layout != SPA_AUDIO_LAYOUT_INTERLEAVED) { + res = -EINVAL; + goto done; + } + + s->sample_spec.format = format_id2pa(s, info.info.raw.format); + if (s->sample_spec.format == PA_SAMPLE_INVALID) { + res = -EINVAL; + goto done; + } + s->sample_spec.rate = info.info.raw.rate; + s->sample_spec.channels = info.info.raw.channels; if (s->format) pa_format_info_free(s->format); s->format = pa_format_info_from_sample_spec(&s->sample_spec, NULL); + + params[n_params++] = get_buffers_param(s, &s->buffer_attr, &b); + + res = 0; + + done: + pw_stream_finish_format(s->stream, res, params, n_params); } static void stream_process(void *data) { pa_stream *s = data; - struct pw_buffer *buf; - uint32_t index; s->timing_info_valid = true; - buf = pw_stream_dequeue_buffer(s->stream); - if (buf == NULL) + if (dequeue_buffer(s) < 0 && s->dequeued_size == 0) return; - spa_ringbuffer_get_write_index(&s->dequeued_ring, &index); - s->dequeued[index & MASK_BUFFERS] = buf; - spa_ringbuffer_write_update(&s->dequeued_ring, index + 1); - if (s->direction == PA_STREAM_PLAYBACK) { - s->dequeued_size += buf->buffer->datas[0].maxsize; if (s->write_callback) s->write_callback(s, s->dequeued_size, s->write_userdata); } else { - s->dequeued_size += buf->buffer->datas[0].chunk->size; if (s->read_callback) s->read_callback(s, s->dequeued_size, s->read_userdata); } @@ -185,6 +302,9 @@ pa_stream* stream_new(pa_context *c, const char *name, s->device_index = PA_INVALID_INDEX; + s->device_index = 0; + s->device_name = strdup("unknown"); + spa_ringbuffer_init(&s->dequeued_ring); spa_list_append(&c->streams, &s->link); @@ -343,6 +463,7 @@ int pa_stream_is_corked(pa_stream *s) PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + pw_log_debug("stream %p: corked %d", s, s->corked); return s->corked; } @@ -395,30 +516,14 @@ static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flag if (attr->fragsize == (uint32_t) -1) attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */ + + pw_log_info("stream %p: maxlength: %u", s, attr->maxlength); + pw_log_info("stream %p: tlength: %u", s, attr->tlength); + pw_log_info("stream %p: minreq: %u", s, attr->minreq); + pw_log_info("stream %p: prebuf: %u", s, attr->prebuf); + pw_log_info("stream %p: fragsize: %u", s, attr->fragsize); } -static const uint32_t audio_formats[] = { - [PA_SAMPLE_U8] = offsetof(struct spa_type_audio_format, U8), - [PA_SAMPLE_ALAW] = offsetof(struct spa_type_audio_format, UNKNOWN), - [PA_SAMPLE_ULAW] = offsetof(struct spa_type_audio_format, UNKNOWN), - [PA_SAMPLE_S16NE] = offsetof(struct spa_type_audio_format, S16), - [PA_SAMPLE_S16RE] = offsetof(struct spa_type_audio_format, S16_OE), - [PA_SAMPLE_FLOAT32NE] = offsetof(struct spa_type_audio_format, F32), - [PA_SAMPLE_FLOAT32RE] = offsetof(struct spa_type_audio_format, F32_OE), - [PA_SAMPLE_S32NE] = offsetof(struct spa_type_audio_format, S32), - [PA_SAMPLE_S32RE] = offsetof(struct spa_type_audio_format, S32_OE), - [PA_SAMPLE_S24NE] = offsetof(struct spa_type_audio_format, S24), - [PA_SAMPLE_S24RE] = offsetof(struct spa_type_audio_format, S24_OE), - [PA_SAMPLE_S24_32NE] = offsetof(struct spa_type_audio_format, S24_32), - [PA_SAMPLE_S24_32RE] = offsetof(struct spa_type_audio_format, S24_32_OE), -}; - -static inline uint32_t get_format(pa_stream *s, pa_sample_format_t format) -{ - if (format < 0 || format >= SPA_N_ELEMENTS(audio_formats)) - return s->type.audio_format.UNKNOWN; - return *SPA_MEMBER(&s->type.audio_format, audio_formats[format], uint32_t); -} static const struct spa_pod *get_param(pa_stream *s, pa_sample_spec *ss, pa_channel_map *map, struct spa_pod_builder *b) @@ -430,7 +535,7 @@ static const struct spa_pod *get_param(pa_stream *s, pa_sample_spec *ss, pa_chan t->param.idEnumFormat, t->spa_format, "I", s->type.media_type.audio, "I", s->type.media_subtype.raw, - ":", s->type.format_audio.format, "I", get_format(s, ss->format), + ":", s->type.format_audio.format, "I", format_pa2id(s, ss->format), ":", s->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_INTERLEAVED, ":", s->type.format_audio.channels, "i", ss->channels, ":", s->type.format_audio.rate, "i", ss->rate); @@ -481,7 +586,7 @@ static int create_stream(pa_stream_direction_t direction, int i; for (i = 0; i < s->n_formats; i++) { - if (pa_format_info_to_sample_spec(s->req_formats[i], &ss, NULL) < 0) { + if (pa_format_info_to_sample_spec(s->req_formats[i], &ss, &map) < 0) { char buf[4096]; pw_log_warn("can't convert format %s", pa_format_info_snprint(buf,4096,s->req_formats[i])); @@ -500,7 +605,7 @@ static int create_stream(pa_stream_direction_t direction, dev = getenv("PIPEWIRE_NODE"); props = (struct pw_properties *) pw_stream_get_properties(s->stream); - pw_properties_setf(props, "node.latency", "%u", s->buffer_attr.minreq); + pw_properties_setf(props, "node.latency", "%u/44100", s->buffer_attr.minreq); res = pw_stream_connect(s->stream, direction == PA_STREAM_PLAYBACK ? @@ -563,6 +668,8 @@ int peek_buffer(pa_stream *s) if (s->buffer != NULL) return 0; + dequeue_buffer(s); + if ((avail = spa_ringbuffer_get_read_index(&s->dequeued_ring, &index)) <= 0) return -EPIPE; @@ -585,9 +692,13 @@ int queue_buffer(pa_stream *s) if (s->buffer == NULL) return 0; - s->dequeued_size -= s->buffer_size; spa_ringbuffer_read_update(&s->dequeued_ring, s->buffer_index + 1); + if (s->direction == PA_STREAM_PLAYBACK) + s->dequeued_size -= s->buffer->buffer->datas[0].maxsize; + else + s->dequeued_size -= s->buffer->buffer->datas[0].chunk->size; + pw_stream_queue_buffer(s->stream, s->buffer); s->buffer = NULL; return 0; @@ -598,6 +709,7 @@ int pa_stream_begin_write( void **data, size_t *nbytes) { + int res; spa_assert(s); spa_assert(s->refcount >= 1); @@ -608,7 +720,8 @@ int pa_stream_begin_write( PA_CHECK_VALIDITY(s->context, data, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID); - if (peek_buffer(s) < 0) { + if ((res = peek_buffer(s)) < 0) { + pw_log_warn("stream %p: no buffer", s); *data = NULL; *nbytes = 0; return 0; @@ -651,6 +764,8 @@ int pa_stream_write_ext_free(pa_stream *s, int64_t offset, pa_seek_mode_t seek) { + int res; + spa_assert(s); spa_assert(s->refcount >= 1); spa_assert(data); @@ -671,16 +786,39 @@ int pa_stream_write_ext_free(pa_stream *s, PA_CHECK_VALIDITY(s->context, !free_cb || !s->buffer, PA_ERR_INVALID); if (s->buffer == NULL) { - pw_log_warn("Not Implemented"); + void *dst; + size_t dlen; + + if ((res = pa_stream_begin_write(s, &dst, &dlen)) < 0) + return res; + + if (dst == NULL || dlen == 0) + return 0; + + nbytes = SPA_MIN(nbytes, dlen); + memcpy(dst, data, nbytes); + data = dst; + if (free_cb) free_cb(free_cb_data); - - return PA_ERR_INVALID; - } else { - s->buffer->buffer->datas[0].chunk->offset = data - s->buffer_data; - s->buffer->buffer->datas[0].chunk->size = nbytes; - queue_buffer(s); } + + s->buffer->buffer->datas[0].chunk->offset = data - s->buffer_data; + s->buffer->buffer->datas[0].chunk->size = nbytes; + + /* Update the write index in the already available latency data */ + if (s->timing_info_valid) { + if (seek == PA_SEEK_ABSOLUTE) { + s->timing_info.write_index_corrupt = false; + s->timing_info.write_index = offset + (int64_t) nbytes; + } else if (seek == PA_SEEK_RELATIVE) { + if (!s->timing_info.write_index_corrupt) + s->timing_info.write_index += offset + (int64_t) nbytes; + } else + s->timing_info.write_index_corrupt = true; + } + queue_buffer(s); + return 0; } @@ -699,10 +837,12 @@ int pa_stream_peek(pa_stream *s, if (peek_buffer(s) < 0) { *data = NULL; *nbytes = 0; + pw_log_debug("stream %p: no buffer", s); return 0; } *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); *nbytes = s->buffer_size; + pw_log_debug("stream %p: %p %zd", s, *data, *nbytes); return 0; } @@ -716,6 +856,7 @@ int pa_stream_drop(pa_stream *s) PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->buffer, PA_ERR_BADSTATE); + pw_log_debug("stream %p", s); queue_buffer(s); return 0; @@ -755,9 +896,10 @@ struct success_ack { static void on_success(pa_operation *o, void *userdata) { struct success_ack *d = userdata; + pa_stream *s = o->stream; pa_operation_done(o); if (d->cb) - d->cb(o->stream, PA_OK, d->userdata); + d->cb(s, PA_OK, d->userdata); } pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) @@ -779,6 +921,17 @@ pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *use return o; } +static void on_timing_success(pa_operation *o, void *userdata) +{ + struct success_ack *d = userdata; + pa_stream *s = o->stream; + pa_operation_done(o); + s->timing_info_valid = true; + + if (d->cb) + d->cb(s, s->timing_info_valid, d->userdata); +} + pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -790,7 +943,7 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + o = pa_operation_new(s->context, s, on_timing_success, sizeof(struct success_ack)); d = o->userdata; d->cb = cb; d->userdata = userdata; @@ -1045,6 +1198,8 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { struct pw_time t; pa_usec_t res; + struct timespec ts; + uint64_t now, delay; spa_assert(s); spa_assert(s->refcount >= 1); @@ -1055,11 +1210,20 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) pw_stream_get_time(s->stream, &t); - res = (t.ticks * t.rate.num * PA_USEC_PER_SEC) / t.rate.denom; + clock_gettime(CLOCK_MONOTONIC, &ts); + now = ts.tv_sec * SPA_NSEC_PER_SEC + ts.tv_nsec; + delay = (now - t.now) / PA_NSEC_PER_USEC; + + if (t.rate.denom != 0) + res = delay + ((t.ticks * t.rate.num * PA_USEC_PER_SEC) / t.rate.denom); + else + res = 0; if (r_usec) *r_usec = res; + pw_log_debug("stream %p: %ld %ld %ld %ld %d/%d %ld", s, now, t.now, delay, t.ticks, t.rate.num, t.rate.denom, res); + return 0; } @@ -1091,6 +1255,8 @@ const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_NODATA); + pw_log_warn("Not Implemented"); + return &s->timing_info; } From c3a1807effdcfd2fa371eb7c9d1c3c905ebeec81 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 26 Jun 2018 15:04:08 +0200 Subject: [PATCH 008/116] stream: improve latency requirements --- src/stream.c | 142 ++++++++++++++++++++++++++++----------------------- 1 file changed, 79 insertions(+), 63 deletions(-) diff --git a/src/stream.c b/src/stream.c index e6421420f..354d4db7c 100644 --- a/src/stream.c +++ b/src/stream.c @@ -141,6 +141,9 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att else buffers = SPA_CLAMP(attr->maxlength / (maxsize * stride), 3, 64); + pw_log_info("stream %p: stride %d maxsize %d size %u buffers %d", s, stride, maxsize, + size, buffers); + param = spa_pod_builder_object(b, t->param.idBuffers, t->param_buffers.Buffers, ":", t->param_buffers.buffers, "iru", buffers, @@ -153,6 +156,64 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att return param; } +static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flags_t *flags) { + const char *e; + + pa_assert(s); + pa_assert(attr); + + if ((e = getenv("PULSE_LATENCY_MSEC"))) { + uint32_t ms; + pa_sample_spec ss; + + pa_sample_spec_init(&ss); + + if (pa_sample_spec_valid(&s->sample_spec)) + ss = s->sample_spec; + else if (s->n_formats == 1) + pa_format_info_to_sample_spec(s->req_formats[0], &ss, NULL); + + if ((ms = atoi(e)) < 0 || ms <= 0) { + pa_log_debug("Failed to parse $PULSE_LATENCY_MSEC: %s", e); + } + else if (!pa_sample_spec_valid(&s->sample_spec)) { + pa_log_debug("Ignoring $PULSE_LATENCY_MSEC: %s (invalid sample spec)", e); + } + else { + attr->maxlength = (uint32_t) -1; + attr->tlength = pa_usec_to_bytes(ms * PA_USEC_PER_MSEC, &ss); + attr->minreq = (uint32_t) -1; + attr->prebuf = (uint32_t) -1; + attr->fragsize = attr->tlength; + + if (flags) + *flags |= PA_STREAM_ADJUST_LATENCY; + } + } + + if (attr->maxlength == (uint32_t) -1) + attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */ + + if (attr->tlength == (uint32_t) -1) + attr->tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &s->sample_spec); /* 250ms of buffering */ + + if (attr->minreq == (uint32_t) -1) + attr->minreq = (attr->tlength)/5; /* Ask for more data when there are only 200ms left in the playback buffer */ + + if (attr->prebuf == (uint32_t) -1) + attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */ + + if (attr->fragsize == (uint32_t) -1) + attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */ + + pw_log_info("stream %p: maxlength: %u", s, attr->maxlength); + pw_log_info("stream %p: tlength: %u", s, attr->tlength); + pw_log_info("stream %p: minreq: %u", s, attr->minreq); + pw_log_info("stream %p: prebuf: %u", s, attr->prebuf); + pw_log_info("stream %p: fragsize: %u", s, attr->fragsize); +} + + static void stream_format_changed(void *data, const struct spa_pod *format) { pa_stream *s = data; @@ -187,6 +248,8 @@ static void stream_format_changed(void *data, const struct spa_pod *format) pa_format_info_free(s->format); s->format = pa_format_info_from_sample_spec(&s->sample_spec, NULL); + patch_buffer_attr(s, &s->buffer_attr, NULL); + params[n_params++] = get_buffers_param(s, &s->buffer_attr, &b); res = 0; @@ -467,64 +530,6 @@ int pa_stream_is_corked(pa_stream *s) return s->corked; } -static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flags_t *flags) { - const char *e; - - pa_assert(s); - pa_assert(attr); - - if ((e = getenv("PULSE_LATENCY_MSEC"))) { - uint32_t ms; - pa_sample_spec ss; - - pa_sample_spec_init(&ss); - - if (pa_sample_spec_valid(&s->sample_spec)) - ss = s->sample_spec; - else if (s->n_formats == 1) - pa_format_info_to_sample_spec(s->req_formats[0], &ss, NULL); - - if ((ms = atoi(e)) < 0 || ms <= 0) { - pa_log_debug("Failed to parse $PULSE_LATENCY_MSEC: %s", e); - } - else if (!pa_sample_spec_valid(&s->sample_spec)) { - pa_log_debug("Ignoring $PULSE_LATENCY_MSEC: %s (invalid sample spec)", e); - } - else { - attr->maxlength = (uint32_t) -1; - attr->tlength = pa_usec_to_bytes(ms * PA_USEC_PER_MSEC, &ss); - attr->minreq = (uint32_t) -1; - attr->prebuf = (uint32_t) -1; - attr->fragsize = attr->tlength; - - if (flags) - *flags |= PA_STREAM_ADJUST_LATENCY; - } - } - - if (attr->maxlength == (uint32_t) -1) - attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */ - - if (attr->tlength == (uint32_t) -1) - attr->tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &s->sample_spec); /* 250ms of buffering */ - - if (attr->minreq == (uint32_t) -1) - attr->minreq = (attr->tlength)/5; /* Ask for more data when there are only 200ms left in the playback buffer */ - - if (attr->prebuf == (uint32_t) -1) - attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */ - - if (attr->fragsize == (uint32_t) -1) - attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */ - - pw_log_info("stream %p: maxlength: %u", s, attr->maxlength); - pw_log_info("stream %p: tlength: %u", s, attr->tlength); - pw_log_info("stream %p: minreq: %u", s, attr->minreq); - pw_log_info("stream %p: prebuf: %u", s, attr->prebuf); - pw_log_info("stream %p: fragsize: %u", s, attr->fragsize); -} - - static const struct spa_pod *get_param(pa_stream *s, pa_sample_spec *ss, pa_channel_map *map, struct spa_pod_builder *b) { @@ -557,6 +562,7 @@ static int create_stream(pa_stream_direction_t direction, uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); struct pw_properties *props; + uint32_t sample_rate = 0, stride = 0; spa_assert(s); spa_assert(s->refcount >= 1); @@ -579,23 +585,32 @@ static int create_stream(pa_stream_direction_t direction, if (pa_sample_spec_valid(&s->sample_spec)) { params[n_params++] = get_param(s, &s->sample_spec, &s->channel_map, &b); + sample_rate = s->sample_spec.rate; + stride = pa_frame_size(&s->sample_spec); } else { pa_sample_spec ss; - pa_channel_map map; int i; for (i = 0; i < s->n_formats; i++) { - if (pa_format_info_to_sample_spec(s->req_formats[i], &ss, &map) < 0) { + if ((res = pa_format_info_to_sample_spec(s->req_formats[i], &ss, NULL)) < 0) { char buf[4096]; - pw_log_warn("can't convert format %s", + pw_log_warn("can't convert format %d %s", res, pa_format_info_snprint(buf,4096,s->req_formats[i])); continue; } - params[n_params++] = get_param(s, &ss, &map, &b); + params[n_params++] = get_param(s, &ss, NULL, &b); + if (ss.rate > sample_rate) { + sample_rate = ss.rate; + stride = pa_frame_size(&ss); + } } } + if (sample_rate == 0) { + sample_rate = 48000; + stride = sizeof(int16_t) * 2; + } if (attr) s->buffer_attr = *attr; @@ -605,7 +620,8 @@ static int create_stream(pa_stream_direction_t direction, dev = getenv("PIPEWIRE_NODE"); props = (struct pw_properties *) pw_stream_get_properties(s->stream); - pw_properties_setf(props, "node.latency", "%u/44100", s->buffer_attr.minreq); + pw_properties_setf(props, "node.latency", "%u/%u", + s->buffer_attr.minreq / stride, sample_rate); res = pw_stream_connect(s->stream, direction == PA_STREAM_PLAYBACK ? From e29a35f0cabfaed3eb6ad2338fe3d664916f2b30 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 26 Jun 2018 15:04:33 +0200 Subject: [PATCH 009/116] stream: don't dequeue in peek --- src/stream.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/stream.c b/src/stream.c index 354d4db7c..bef1cc2db 100644 --- a/src/stream.c +++ b/src/stream.c @@ -684,8 +684,6 @@ int peek_buffer(pa_stream *s) if (s->buffer != NULL) return 0; - dequeue_buffer(s); - if ((avail = spa_ringbuffer_get_read_index(&s->dequeued_ring, &index)) <= 0) return -EPIPE; From 11ee416bb0b78dffcd0a0dc0a3df5c3bc882ba3c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 26 Jun 2018 16:57:49 +0200 Subject: [PATCH 010/116] stream: work on pending buffers --- src/internal.h | 1 + src/stream.c | 84 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/internal.h b/src/internal.h index ed702f946..fb96bb108 100644 --- a/src/internal.h +++ b/src/internal.h @@ -330,6 +330,7 @@ struct pa_stream { struct pw_buffer *dequeued[MAX_BUFFERS]; struct spa_ringbuffer dequeued_ring; size_t dequeued_size; + struct spa_list pending; struct pw_buffer *buffer; uint32_t buffer_index; diff --git a/src/stream.c b/src/stream.c index bef1cc2db..ea44a27ed 100644 --- a/src/stream.c +++ b/src/stream.c @@ -25,10 +25,22 @@ #include #include +#include #include #include "internal.h" +struct pending_data { + struct spa_list link; + + const void *data; + size_t nbytes; + size_t offset; + + pa_free_cb_t free_cb; + void *free_cb_data; +}; + static const uint32_t audio_formats[] = { [PA_SAMPLE_U8] = offsetof(struct spa_type_audio_format, U8), [PA_SAMPLE_ALAW] = offsetof(struct spa_type_audio_format, UNKNOWN), @@ -313,6 +325,7 @@ pa_stream* stream_new(pa_context *c, const char *name, s->refcount = 1; s->context = c; init_type(&s->type, pw_core_get_type(c->core)->map); + spa_list_init(&s->pending); pw_stream_add_listener(s->stream, &s->stream_listener, &stream_events, s); @@ -760,6 +773,41 @@ int pa_stream_cancel_write(pa_stream *s) return 0; } +static void flush_pending(pa_stream *s) +{ + struct pending_data *p; + void *data; + size_t nbytes; + bool flush; + + while(!spa_list_is_empty(&s->pending)) { + p = spa_list_first(&s->pending, struct pending_data, link); + + pa_stream_begin_write(s, &data, &nbytes); + if (data == NULL || nbytes == 0) + break; + + nbytes = SPA_MIN(nbytes, p->nbytes - p->offset); + memcpy(data, p->data + p->offset, nbytes); + + p->offset += nbytes; + s->buffer_offset += nbytes; + + flush = p->offset >= p->nbytes; + + if (flush) { + spa_list_remove(&p->link); + if (p->free_cb) + p->free_cb(p->free_cb_data); + pa_xfree(p); + } + if (flush || s->buffer_offset >= s->buffer_size) { + s->buffer->buffer->datas[0].chunk->size = s->buffer_offset; + queue_buffer(s); + } + } +} + int pa_stream_write(pa_stream *s, const void *data, size_t nbytes, @@ -778,8 +826,6 @@ int pa_stream_write_ext_free(pa_stream *s, int64_t offset, pa_seek_mode_t seek) { - int res; - spa_assert(s); spa_assert(s->refcount >= 1); spa_assert(data); @@ -800,25 +846,23 @@ int pa_stream_write_ext_free(pa_stream *s, PA_CHECK_VALIDITY(s->context, !free_cb || !s->buffer, PA_ERR_INVALID); if (s->buffer == NULL) { - void *dst; - size_t dlen; + struct pending_data *p; - if ((res = pa_stream_begin_write(s, &dst, &dlen)) < 0) - return res; + p = pa_xmalloc(sizeof(struct pending_data)); + p->data = data; + p->nbytes = nbytes; + p->offset = 0; + p->free_cb = free_cb; + p->free_cb_data = free_cb_data; + spa_list_append(&s->pending, &p->link); - if (dst == NULL || dlen == 0) - return 0; - - nbytes = SPA_MIN(nbytes, dlen); - memcpy(dst, data, nbytes); - data = dst; - - if (free_cb) - free_cb(free_cb_data); + flush_pending(s); + } + else { + s->buffer->buffer->datas[0].chunk->offset = data - s->buffer_data; + s->buffer->buffer->datas[0].chunk->size = nbytes; + queue_buffer(s); } - - s->buffer->buffer->datas[0].chunk->offset = data - s->buffer_data; - s->buffer->buffer->datas[0].chunk->size = nbytes; /* Update the write index in the already available latency data */ if (s->timing_info_valid) { @@ -831,8 +875,6 @@ int pa_stream_write_ext_free(pa_stream *s, } else s->timing_info.write_index_corrupt = true; } - queue_buffer(s); - return 0; } @@ -1116,7 +1158,7 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi s->corked = b; - pw_log_warn("Not Implemented"); + pw_log_warn("Not Implemented %d", b); o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); d = o->userdata; d->cb = cb; From b98c98cc8d924e506f3554e996685c484d0ad566 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 3 Jul 2018 22:03:25 +0200 Subject: [PATCH 011/116] context: rework how we introspect Use the properties of the global to mark the global type, use this to filter the objects. Hook up the volume control --- src/context.c | 61 ++++++++++++++ src/internal.h | 5 ++ src/introspect.c | 212 +++++++++++++++++++++++++---------------------- src/stream.c | 27 +++--- src/subscribe.c | 2 + 5 files changed, 196 insertions(+), 111 deletions(-) diff --git a/src/context.c b/src/context.c index 3087fa082..826002b81 100644 --- a/src/context.c +++ b/src/context.c @@ -86,6 +86,48 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) return pa_context_new_with_proplist(mainloop, name, NULL); } +static int set_mask(pa_context *c, struct global *g) +{ + const char *str; + + if (g->type == c->t->node) { + if (g->props == NULL) + return 0; + if ((str = pw_properties_get(g->props, "media.class")) == NULL) + return 0; + + if (strcmp(str, "Audio/Sink") == 0) { + g->mask = PA_SUBSCRIPTION_MASK_SINK; + g->event = PA_SUBSCRIPTION_EVENT_SINK; + } + else if (strcmp(str, "Audio/Source") == 0) { + g->mask = PA_SUBSCRIPTION_MASK_SOURCE; + g->event = PA_SUBSCRIPTION_EVENT_SOURCE; + } + else if (strcmp(str, "Stream/Output/Audio") == 0) { + g->mask = PA_SUBSCRIPTION_MASK_SINK_INPUT; + g->event = PA_SUBSCRIPTION_EVENT_SINK_INPUT; + } + else if (strcmp(str, "Stream/Input/Audio") == 0) { + g->mask = PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT; + g->event = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT; + } + else + return 0; + } + else if (g->type == c->t->module) { + g->mask = PA_SUBSCRIPTION_MASK_MODULE; + g->event = PA_SUBSCRIPTION_EVENT_MODULE; + } + else if (g->type == c->t->client) { + g->mask = PA_SUBSCRIPTION_MASK_CLIENT; + g->event = PA_SUBSCRIPTION_EVENT_CLIENT; + } + else + return 0; + + return 1; +} static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, uint32_t permissions, uint32_t type, uint32_t version, @@ -101,7 +143,18 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, g->type = type; g->props = props ? pw_properties_new_dict(props) : NULL; + if (set_mask(c, g) == 0) + return; + spa_list_append(&c->globals, &g->link); + + if (c->subscribe_mask & g->mask) { + if (c->subscribe_callback) + c->subscribe_callback(c, + PA_SUBSCRIPTION_EVENT_NEW | g->event, + g->id, + c->subscribe_userdata); + } } struct global *pa_context_find_global(pa_context *c, uint32_t id) @@ -123,6 +176,14 @@ static void registry_event_global_remove(void *object, uint32_t id) if ((g = pa_context_find_global(c, id)) == NULL) return; + if (c->subscribe_mask & g->mask) { + if (c->subscribe_callback) + c->subscribe_callback(c, + PA_SUBSCRIPTION_EVENT_REMOVE | g->event, + g->id, + c->subscribe_userdata); + } + spa_list_remove(&g->link); if (g->props) pw_properties_free(g->props); diff --git a/src/internal.h b/src/internal.h index fb96bb108..c14c6ad74 100644 --- a/src/internal.h +++ b/src/internal.h @@ -197,6 +197,9 @@ struct global { uint32_t type; struct pw_properties *props; + pa_subscription_mask_t mask; + pa_subscription_event_type_t event; + void *info; pw_destroy_t destroy; @@ -337,6 +340,8 @@ struct pa_stream { void *buffer_data; uint32_t buffer_size; uint32_t buffer_offset; + + float volume; }; void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); diff --git a/src/introspect.c b/src/introspect.c index ce2af79a8..4b2f4fb2f 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -20,13 +20,12 @@ #include #include +#include #include #include "internal.h" -typedef int (*global_filter_t)(pa_context *c, struct global *g, bool full); - static void node_event_info(void *object, struct pw_node_info *info) { struct global *g = object; @@ -104,13 +103,12 @@ static int ensure_global(pa_context *c, struct global *g) return 0; } -static void ensure_types(pa_context *c, uint32_t type, global_filter_t filter) +static void ensure_types(pa_context *c, uint32_t mask) { struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!filter(c, g, false)) - continue; - ensure_global(c, g); + if (g->mask & mask) + ensure_global(c, g); } } @@ -152,26 +150,19 @@ static void sink_info(pa_operation *o, void *userdata) pa_operation_done(o); } -static int sink_filter(pa_context *c, struct global *g, bool full) -{ - const char *str; - - if (g->type != c->t->node) - return 0; - if (g->props == NULL) - return 0; - if ((str = pw_properties_get(g->props, "media.class")) == NULL) - return 0; - if (strcmp(str, "Audio/Sink") != 0) - return 0; - return 1; -} - -static struct global *find_sink_by_name(pa_context *c, const char *name) +static struct global *find_type_by_name(pa_context *c, uint32_t mask, const char *name) { struct global *g; + const char *str; + spa_list_for_each(g, &c->globals, link) { - if (sink_filter(c, g, true)) + if (!(g->mask & mask)) + continue; + if (g->props == NULL) + continue; + if ((str = pw_properties_get(g->props, "node.name")) == NULL) + continue; + if (strcmp(str, name) == 0) return g; } return NULL; @@ -190,7 +181,7 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); - if ((g = find_sink_by_name(c, name)) == NULL) + if ((g = find_type_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) return NULL; ensure_global(c, g); @@ -219,7 +210,7 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_ if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!sink_filter(c, g, false)) + if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) return NULL; ensure_global(c, g); @@ -240,7 +231,7 @@ static void sink_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!sink_filter(c, g, true)) + if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) continue; d->global = g; sink_callback(d); @@ -260,7 +251,7 @@ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - ensure_types(c, c->t->node, sink_filter); + ensure_types(c, PA_SUBSCRIPTION_MASK_SINK); o = pa_operation_new(c, NULL, sink_info_list, sizeof(struct sink_data)); d = o->userdata; d->context = c; @@ -272,25 +263,25 @@ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); + pw_log_warn("Not Implemented %d", idx); return NULL; } pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); + pw_log_warn("Not Implemented %s", name); return NULL; } pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); + pw_log_warn("Not Implemented %d", mute); return NULL; } pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); + pw_log_warn("Not Implemented %s", name); return NULL; } @@ -350,21 +341,6 @@ static void source_info(pa_operation *o, void *userdata) d->cb(d->context, NULL, 1, d->userdata); } -static int source_filter(pa_context *c, struct global *g, bool full) -{ - const char *str; - - if (g->type != c->t->node) - return 0; - if (g->props == NULL) - return 0; - if ((str = pw_properties_get(g->props, "media.class")) == NULL) - return 0; - if (strcmp(str, "Audio/Source") != 0) - return 0; - return 1; -} - pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); @@ -385,13 +361,16 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, p if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!source_filter(c, g, false)) + if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) return NULL; ensure_global(c, g); o = pa_operation_new(c, NULL, source_info, sizeof(struct source_data)); d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; d->global = g; return o; } @@ -403,7 +382,7 @@ static void source_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!source_filter(c, g, true)) + if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) continue; d->global = g; source_callback(d); @@ -422,7 +401,7 @@ pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - ensure_types(c, c->t->node, source_filter); + ensure_types(c, PA_SUBSCRIPTION_MASK_SOURCE); o = pa_operation_new(c, NULL, source_info_list, sizeof(struct source_data)); d = o->userdata; d->context = c; @@ -516,13 +495,6 @@ static void module_info(pa_operation *o, void *userdata) d->cb(d->context, NULL, 1, d->userdata); } -static int module_filter(pa_context *c, struct global *g, bool full) -{ - if (g->type != c->t->module) - return 0; - return 1; -} - pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata) { pa_operation *o; @@ -537,13 +509,16 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_ if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!module_filter(c, g, false)) + if (!(g->mask & PA_SUBSCRIPTION_MASK_MODULE)) return NULL; ensure_global(c, g); o = pa_operation_new(c, NULL, module_info, sizeof(struct module_data)); d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; d->global = g; return o; @@ -556,7 +531,7 @@ static void module_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!module_filter(c, g, true)) + if (!(g->mask & PA_SUBSCRIPTION_MASK_MODULE)) continue; d->global = g; module_callback(d); @@ -575,7 +550,7 @@ pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - ensure_types(c, c->t->module, module_filter); + ensure_types(c, PA_SUBSCRIPTION_MASK_MODULE); o = pa_operation_new(c, NULL, module_info_list, sizeof(struct module_data)); d = o->userdata; d->context = c; @@ -628,13 +603,6 @@ static void client_info(pa_operation *o, void *userdata) d->cb(d->context, NULL, 1, d->userdata); } -static int client_filter(pa_context *c, struct global *g, bool full) -{ - if (g->type != c->t->client) - return 0; - return 1; -} - pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata) { pa_operation *o; @@ -649,13 +617,16 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_ if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!client_filter(c, g, false)) + if (!(g->mask & PA_SUBSCRIPTION_MASK_CLIENT)) return NULL; ensure_global(c, g); o = pa_operation_new(c, NULL, client_info, sizeof(struct client_data)); d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; d->global = g; return o; @@ -668,7 +639,7 @@ static void client_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!client_filter(c, g, true)) + if (!(g->mask & PA_SUBSCRIPTION_MASK_CLIENT)) continue; d->global = g; client_callback(d); @@ -687,7 +658,7 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - ensure_types(c, c->t->client, client_filter); + ensure_types(c, PA_SUBSCRIPTION_MASK_CLIENT); o = pa_operation_new(c, NULL, client_info_list, sizeof(struct client_data)); d = o->userdata; d->context = c; @@ -753,6 +724,7 @@ static void sink_input_callback(struct sink_input_data *d) pa_sink_input_info i; pa_format_info ii[1]; + pw_log_debug("index %d", g->id); spa_zero(i); i.index = g->id; i.name = info->name; @@ -761,7 +733,14 @@ static void sink_input_callback(struct sink_input_data *d) ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); i.format = ii; - + i.resample_method = "PipeWire resampler"; + i.driver = "PipeWire"; + i.mute = false; + i.corked = false; + i.has_volume = true; + i.volume_writable = true; + i.volume.channels = 1; + i.volume.values[0] = PA_VOLUME_NORM; d->cb(d->context, &i, 0, d->userdata); } @@ -772,27 +751,6 @@ static void sink_input_info(pa_operation *o, void *userdata) d->cb(d->context, NULL, 1, d->userdata); } -static int sink_input_filter(pa_context *c, struct global *g, bool full) -{ - const char *str; - struct pw_node_info *info = g->info; - - if (g->type != c->t->node) - return 0; - - if (full) { - if (info == NULL || info->props == NULL) - return 0; - if ((str = spa_dict_lookup(info->props, "node.stream")) == NULL) - return 0; - if (pw_properties_parse_bool(str) == false) - return 0; - if (info->n_output_ports == 0) - return 0; - } - return 1; -} - pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata) { pa_operation *o; @@ -807,13 +765,16 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!sink_input_filter(c, g, false)) + if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK_INPUT)) return NULL; ensure_global(c, g); o = pa_operation_new(c, NULL, sink_input_info, sizeof(struct sink_input_data)); d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; d->global = g; return o; } @@ -825,7 +786,7 @@ static void sink_input_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!sink_input_filter(c, g, true)) + if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK_INPUT)) continue; d->global = g; sink_input_callback(d); @@ -844,7 +805,7 @@ pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_i PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - ensure_types(c, c->t->node, sink_input_filter); + ensure_types(c, PA_SUBSCRIPTION_MASK_SINK_INPUT); o = pa_operation_new(c, NULL, sink_input_info_list, sizeof(struct sink_input_data)); d = o->userdata; d->context = c; @@ -865,16 +826,71 @@ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, u return NULL; } +static pa_stream *find_stream(pa_context *c, uint32_t idx) +{ + pa_stream *s; + spa_list_for_each(s, &c->streams, link) { + if (pw_stream_get_node_id(s->stream) == idx) + return s; + } + return NULL; +} + +struct success_ack { + pa_context_success_cb_t cb; + void *userdata; +}; + +static void on_success(pa_operation *o, void *userdata) +{ + struct success_ack *d = userdata; + pa_context *c = o->context; + pa_operation_done(o); + if (d->cb) + d->cb(c, PA_OK, d->userdata); +} + pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_stream *s; + pa_operation *o; + struct success_ack *d; + + if ((s = find_stream(c, idx)) == NULL) + return NULL; + + s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; + pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->volume); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_stream *s; + pa_operation *o; + struct success_ack *d; + + if ((s = find_stream(c, idx)) == NULL) + return NULL; + + s->mute = mute; + if (mute) + pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, 0.0); + else + pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->volume); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) diff --git a/src/stream.c b/src/stream.c index ea44a27ed..1a7dee4a6 100644 --- a/src/stream.c +++ b/src/stream.c @@ -30,6 +30,8 @@ #include #include "internal.h" +#define MIN_QUEUED 1 + struct pending_data { struct spa_list link; @@ -85,12 +87,11 @@ static int dequeue_buffer(pa_stream *s) spa_ringbuffer_get_write_index(&s->dequeued_ring, &index); s->dequeued[index & MASK_BUFFERS] = buf; - spa_ringbuffer_write_update(&s->dequeued_ring, index + 1); - if (s->direction == PA_STREAM_PLAYBACK) s->dequeued_size += buf->buffer->datas[0].maxsize; else s->dequeued_size += buf->buffer->datas[0].chunk->size; + spa_ringbuffer_write_update(&s->dequeued_ring, index + 1); return 0; } @@ -151,7 +152,7 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att if (attr->maxlength == -1) buffers = 3; else - buffers = SPA_CLAMP(attr->maxlength / (maxsize * stride), 3, 64); + buffers = SPA_CLAMP(attr->maxlength / (maxsize * stride), 3, MAX_BUFFERS); pw_log_info("stream %p: stride %d maxsize %d size %u buffers %d", s, stride, maxsize, size, buffers); @@ -159,7 +160,7 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att param = spa_pod_builder_object(b, t->param.idBuffers, t->param_buffers.Buffers, ":", t->param_buffers.buffers, "iru", buffers, - SPA_POD_PROP_MIN_MAX(3, 64), + SPA_POD_PROP_MIN_MAX(3, MAX_BUFFERS), ":", t->param_buffers.blocks, "i", blocks, ":", t->param_buffers.size, "iru", size * stride, SPA_POD_PROP_MIN_MAX(size * stride, maxsize * stride), @@ -210,7 +211,7 @@ static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flag attr->tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &s->sample_spec); /* 250ms of buffering */ if (attr->minreq == (uint32_t) -1) - attr->minreq = (attr->tlength)/5; /* Ask for more data when there are only 200ms left in the playback buffer */ + attr->minreq = attr->tlength; /* Ask for more data when there are only 200ms left in the playback buffer */ if (attr->prebuf == (uint32_t) -1) attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */ @@ -276,7 +277,7 @@ static void stream_process(void *data) s->timing_info_valid = true; - if (dequeue_buffer(s) < 0 && s->dequeued_size == 0) + if (dequeue_buffer(s) < 0 && s->dequeued_size <= 0) return; if (s->direction == PA_STREAM_PLAYBACK) { @@ -466,10 +467,7 @@ uint32_t pa_stream_get_index(pa_stream *s) spa_assert(s); spa_assert(s->refcount >= 1); - PA_CHECK_VALIDITY_RETURN_ANY(s->context, - s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX); - - return s->stream_index; + return pw_stream_get_node_id(s->stream); } void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { @@ -583,6 +581,10 @@ static int create_stream(pa_stream_direction_t direction, s->direction = direction; s->timing_info_valid = false; s->disconnecting = false; + if (volume) + s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; + else + s->volume = 1.0; pa_stream_set_state(s, PA_STREAM_CREATING); @@ -697,7 +699,7 @@ int peek_buffer(pa_stream *s) if (s->buffer != NULL) return 0; - if ((avail = spa_ringbuffer_get_read_index(&s->dequeued_ring, &index)) <= 0) + if ((avail = spa_ringbuffer_get_read_index(&s->dequeued_ring, &index)) < MIN_QUEUED) return -EPIPE; s->buffer = s->dequeued[index & MASK_BUFFERS]; @@ -719,12 +721,11 @@ int queue_buffer(pa_stream *s) if (s->buffer == NULL) return 0; - spa_ringbuffer_read_update(&s->dequeued_ring, s->buffer_index + 1); - if (s->direction == PA_STREAM_PLAYBACK) s->dequeued_size -= s->buffer->buffer->datas[0].maxsize; else s->dequeued_size -= s->buffer->buffer->datas[0].chunk->size; + spa_ringbuffer_read_update(&s->dequeued_ring, s->buffer_index + 1); pw_stream_queue_buffer(s->stream, s->buffer); s->buffer = NULL; diff --git a/src/subscribe.c b/src/subscribe.c index a78a4d009..953cfd1c7 100644 --- a/src/subscribe.c +++ b/src/subscribe.c @@ -44,6 +44,8 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c pa_assert(c); pa_assert(c->refcount >= 1); + c->subscribe_mask = m; + o = pa_operation_new(c, NULL, on_subscribed, sizeof(struct subscribe_data)); d = o->userdata; d->cb = cb; From 3cfbdc750aa15af46306c29a085267524aa80caf Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 3 Jul 2018 22:09:53 +0200 Subject: [PATCH 012/116] introspect: don't store mute state --- src/introspect.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/introspect.c b/src/introspect.c index 4b2f4fb2f..cbe9013ed 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -879,7 +879,6 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu if ((s = find_stream(c, idx)) == NULL) return NULL; - s->mute = mute; if (mute) pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, 0.0); else From 71d87c589da3febd3b66171184624693c3032d57 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 4 Jul 2018 15:33:20 +0200 Subject: [PATCH 013/116] stream: fix introspection of linked device --- src/context.c | 54 ++++++++++++++++++++++------- src/internal.h | 16 +++++++++ src/introspect.c | 40 ++++++++++++++------- src/stream.c | 90 ++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 165 insertions(+), 35 deletions(-) diff --git a/src/context.c b/src/context.c index 826002b81..56990cde2 100644 --- a/src/context.c +++ b/src/context.c @@ -86,6 +86,16 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) return pa_context_new_with_proplist(mainloop, name, NULL); } +struct global *pa_context_find_global(pa_context *c, uint32_t id) +{ + struct global *g; + spa_list_for_each(g, &c->globals, link) { + if (g->id == id) + return g; + } + return NULL; +} + static int set_mask(pa_context *c, struct global *g) { const char *str; @@ -100,10 +110,24 @@ static int set_mask(pa_context *c, struct global *g) g->mask = PA_SUBSCRIPTION_MASK_SINK; g->event = PA_SUBSCRIPTION_EVENT_SINK; } + else if (strcmp(str, "Audio/DSP/Playback") == 0) { + if ((str = pw_properties_get(g->props, "node.session")) == NULL) + return 0; + g->mask = PA_SUBSCRIPTION_MASK_DSP_SINK; + g->dsp_info.session = pa_context_find_global(c, + pw_properties_parse_int(str)); + } else if (strcmp(str, "Audio/Source") == 0) { g->mask = PA_SUBSCRIPTION_MASK_SOURCE; g->event = PA_SUBSCRIPTION_EVENT_SOURCE; } + else if (strcmp(str, "Audio/DSP/Capture") == 0) { + if ((str = pw_properties_get(g->props, "node.session")) == NULL) + return 0; + g->mask = PA_SUBSCRIPTION_MASK_DSP_SOURCE; + g->dsp_info.session = pa_context_find_global(c, + pw_properties_parse_int(str)); + } else if (strcmp(str, "Stream/Output/Audio") == 0) { g->mask = PA_SUBSCRIPTION_MASK_SINK_INPUT; g->event = PA_SUBSCRIPTION_EVENT_SINK_INPUT; @@ -112,8 +136,6 @@ static int set_mask(pa_context *c, struct global *g) g->mask = PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT; g->event = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT; } - else - return 0; } else if (g->type == c->t->module) { g->mask = PA_SUBSCRIPTION_MASK_MODULE; @@ -123,6 +145,23 @@ static int set_mask(pa_context *c, struct global *g) g->mask = PA_SUBSCRIPTION_MASK_CLIENT; g->event = PA_SUBSCRIPTION_EVENT_CLIENT; } + else if (g->type == c->t->port) { + pw_log_debug("found port %d", g->id); + } + else if (g->type == c->t->link) { + if ((str = pw_properties_get(g->props, "link.output")) == NULL) + return 0; + g->link_info.src = pa_context_find_global(c, pw_properties_parse_int(str)); + if ((str = pw_properties_get(g->props, "link.input")) == NULL) + return 0; + g->link_info.dst = pa_context_find_global(c, pw_properties_parse_int(str)); + + if (g->link_info.src == NULL || g->link_info.dst == NULL) + return 0; + + pw_log_debug("link %d->%d", g->link_info.src->parent_id, + g->link_info.dst->parent_id); + } else return 0; @@ -146,6 +185,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, if (set_mask(c, g) == 0) return; + pw_log_debug("mask %d/%d", g->mask, g->event); spa_list_append(&c->globals, &g->link); if (c->subscribe_mask & g->mask) { @@ -157,16 +197,6 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, } } -struct global *pa_context_find_global(pa_context *c, uint32_t id) -{ - struct global *g; - spa_list_for_each(g, &c->globals, link) { - if (g->id == id) - return g; - } - return NULL; -} - static void registry_event_global_remove(void *object, uint32_t id) { pa_context *c = object; diff --git a/src/internal.h b/src/internal.h index c14c6ad74..a478cab63 100644 --- a/src/internal.h +++ b/src/internal.h @@ -190,6 +190,10 @@ struct pa_mainloop { int n_events; }; +#define PA_SUBSCRIPTION_MASK_DSP_SINK 0x1000U +#define PA_SUBSCRIPTION_MASK_DSP_SOURCE 0x2000U +#define PA_SUBSCRIPTION_MASK_DSP (PA_SUBSCRIPTION_MASK_DSP_SINK | PA_SUBSCRIPTION_MASK_DSP_SOURCE) + struct global { struct spa_list link; uint32_t id; @@ -206,6 +210,17 @@ struct global { struct pw_proxy *proxy; struct spa_hook proxy_listener; struct spa_hook proxy_proxy_listener; + + /* for links */ + union { + struct { + struct global *src; + struct global *dst; + } link_info; + struct { + struct global *session; + } dsp_info; + }; }; struct pa_context { @@ -333,6 +348,7 @@ struct pa_stream { struct pw_buffer *dequeued[MAX_BUFFERS]; struct spa_ringbuffer dequeued_ring; size_t dequeued_size; + size_t maxsize; struct spa_list pending; struct pw_buffer *buffer; diff --git a/src/introspect.c b/src/introspect.c index cbe9013ed..36af7249d 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -130,6 +130,7 @@ static void sink_callback(struct sink_data *d) spa_zero(i); i.index = g->id; i.name = info->name; + i.description = info->name; i.proplist = pa_proplist_new_dict(info->props); i.owner_module = g->parent_id; i.base_volume = PA_VOLUME_NORM; @@ -339,6 +340,7 @@ static void source_info(pa_operation *o, void *userdata) struct source_data *d = userdata; source_callback(d); d->cb(d->context, NULL, 1, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata) @@ -388,6 +390,7 @@ static void source_info_list(pa_operation *o, void *userdata) source_callback(d); } d->cb(c, NULL, 1, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata) @@ -493,6 +496,7 @@ static void module_info(pa_operation *o, void *userdata) struct module_data *d = userdata; module_callback(d); d->cb(d->context, NULL, 1, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata) @@ -537,6 +541,7 @@ static void module_info_list(pa_operation *o, void *userdata) module_callback(d); } d->cb(c, NULL, 1, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata) @@ -601,6 +606,7 @@ static void client_info(pa_operation *o, void *userdata) struct client_data *d = userdata; client_callback(d); d->cb(d->context, NULL, 1, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata) @@ -645,6 +651,7 @@ static void client_info_list(pa_operation *o, void *userdata) client_callback(d); } d->cb(c, NULL, 1, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata) @@ -717,14 +724,27 @@ struct sink_input_data { struct global *global; }; +static pa_stream *find_stream(pa_context *c, uint32_t idx) +{ + pa_stream *s; + spa_list_for_each(s, &c->streams, link) { + if (pw_stream_get_node_id(s->stream) == idx) + return s; + } + return NULL; +} + static void sink_input_callback(struct sink_input_data *d) { struct global *g = d->global; struct pw_node_info *info = g->info; pa_sink_input_info i; pa_format_info ii[1]; + pa_stream *s; pw_log_debug("index %d", g->id); + s = find_stream(d->context, g->id); + spa_zero(i); i.index = g->id; i.name = info->name; @@ -740,7 +760,11 @@ static void sink_input_callback(struct sink_input_data *d) i.has_volume = true; i.volume_writable = true; i.volume.channels = 1; - i.volume.values[0] = PA_VOLUME_NORM; + if (s) + i.volume.values[0] = s->volume * PA_VOLUME_NORM; + else + i.volume.values[0] = PA_VOLUME_NORM; + d->cb(d->context, &i, 0, d->userdata); } @@ -749,6 +773,7 @@ static void sink_input_info(pa_operation *o, void *userdata) struct sink_input_data *d = userdata; sink_input_callback(d); d->cb(d->context, NULL, 1, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata) @@ -792,6 +817,7 @@ static void sink_input_info_list(pa_operation *o, void *userdata) sink_input_callback(d); } d->cb(c, NULL, 1, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata) @@ -826,16 +852,6 @@ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, u return NULL; } -static pa_stream *find_stream(pa_context *c, uint32_t idx) -{ - pa_stream *s; - spa_list_for_each(s, &c->streams, link) { - if (pw_stream_get_node_id(s->stream) == idx) - return s; - } - return NULL; -} - struct success_ack { pa_context_success_cb_t cb; void *userdata; @@ -845,9 +861,9 @@ static void on_success(pa_operation *o, void *userdata) { struct success_ack *d = userdata; pa_context *c = o->context; - pa_operation_done(o); if (d->cb) d->cb(c, PA_OK, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) diff --git a/src/stream.c b/src/stream.c index 1a7dee4a6..6a6af3ee2 100644 --- a/src/stream.c +++ b/src/stream.c @@ -96,10 +96,70 @@ static int dequeue_buffer(pa_stream *s) return 0; } +static void dump_buffer_attr(pa_stream *s, pa_buffer_attr *attr) +{ + pw_log_info("stream %p: maxlength: %u", s, attr->maxlength); + pw_log_info("stream %p: tlength: %u", s, attr->tlength); + pw_log_info("stream %p: minreq: %u", s, attr->minreq); + pw_log_info("stream %p: prebuf: %u", s, attr->prebuf); + pw_log_info("stream %p: fragsize: %u", s, attr->fragsize); +} + static void configure_buffers(pa_stream *s) { - s->buffer_attr.maxlength = 65536; + s->buffer_attr.maxlength = s->maxsize; s->buffer_attr.prebuf = s->buffer_attr.minreq; + s->buffer_attr.fragsize = s->buffer_attr.minreq; + dump_buffer_attr(s, &s->buffer_attr); +} + +static struct global *find_linked(pa_stream *s, uint32_t idx) +{ + struct global *g, *f; + pa_context *c = s->context; + + spa_list_for_each(g, &c->globals, link) { + if (g->type != c->t->link) + continue; + + pw_log_debug("%d %d %d", idx, + g->link_info.src->parent_id, + g->link_info.dst->parent_id); + + if (g->link_info.src->parent_id == idx) + f = pa_context_find_global(c, g->link_info.dst->parent_id); + else if (g->link_info.dst->parent_id == idx) + f = pa_context_find_global(c, g->link_info.src->parent_id); + else + continue; + + if (f == NULL) + continue; + if (f->mask & PA_SUBSCRIPTION_MASK_DSP) { + f = f->dsp_info.session; + } + return f; + } + return NULL; +} +static void configure_device(pa_stream *s) +{ + struct global *g; + const char *str; + + g = find_linked(s, pa_stream_get_index(s)); + if (g == NULL) { + s->device_index = PA_INVALID_INDEX; + s->device_name = NULL; + } + else { + s->device_index = g->id; + if ((str = pw_properties_get(g->props, "node.name")) == NULL) + s->device_name = strdup("unknown"); + else + s->device_name = strdup(str); + } + pw_log_debug("linked to %d '%s'", s->device_index, s->device_name); } static void stream_state_changed(void *data, enum pw_stream_state old, @@ -122,6 +182,7 @@ static void stream_state_changed(void *data, enum pw_stream_state old, case PW_STREAM_STATE_READY: break; case PW_STREAM_STATE_PAUSED: + configure_device(s); configure_buffers(s); pa_stream_set_state(s, PA_STREAM_READY); break; @@ -219,14 +280,9 @@ static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flag if (attr->fragsize == (uint32_t) -1) attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */ - pw_log_info("stream %p: maxlength: %u", s, attr->maxlength); - pw_log_info("stream %p: tlength: %u", s, attr->tlength); - pw_log_info("stream %p: minreq: %u", s, attr->minreq); - pw_log_info("stream %p: prebuf: %u", s, attr->prebuf); - pw_log_info("stream %p: fragsize: %u", s, attr->fragsize); + dump_buffer_attr(s, attr); } - static void stream_format_changed(void *data, const struct spa_pod *format) { pa_stream *s = data; @@ -271,6 +327,17 @@ static void stream_format_changed(void *data, const struct spa_pod *format) pw_stream_finish_format(s->stream, res, params, n_params); } +static void stream_add_buffer(void *data, struct pw_buffer *buffer) +{ + pa_stream *s = data; + s->maxsize += buffer->buffer->datas[0].maxsize; +} +static void stream_remove_buffer(void *data, struct pw_buffer *buffer) +{ + pa_stream *s = data; + s->maxsize -= buffer->buffer->datas[0].maxsize; +} + static void stream_process(void *data) { pa_stream *s = data; @@ -295,6 +362,8 @@ static const struct pw_stream_events stream_events = PW_VERSION_STREAM_EVENTS, .state_changed = stream_state_changed, .format_changed = stream_format_changed, + .add_buffer = stream_add_buffer, + .remove_buffer = stream_remove_buffer, .process = stream_process, }; @@ -378,9 +447,7 @@ pa_stream* stream_new(pa_context *c, const char *name, s->buffer_attr.fragsize = (uint32_t) -1; s->device_index = PA_INVALID_INDEX; - - s->device_index = 0; - s->device_name = strdup("unknown"); + s->device_name = NULL; spa_ringbuffer_init(&s->dequeued_ring); @@ -479,6 +546,7 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { pa_stream_ref(s); + pw_log_debug("stream %p: state %d -> %d", s, s->state, st); s->state = st; if (s->state_callback) @@ -1279,7 +1347,7 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) if (r_usec) *r_usec = res; - pw_log_debug("stream %p: %ld %ld %ld %ld %d/%d %ld", s, now, t.now, delay, t.ticks, t.rate.num, t.rate.denom, res); + pw_log_trace("stream %p: %ld %ld %ld %ld %d/%d %ld", s, now, t.now, delay, t.ticks, t.rate.num, t.rate.denom, res); return 0; } From 27d34dde886eb9d58db34caa721f5fc209f5d4c9 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 4 Jul 2018 18:43:45 +0200 Subject: [PATCH 014/116] introspect: improve introspection stream: set roles --- src/introspect.c | 75 +++++++++++++++++++++++++++++++++++++++++------- src/stream.c | 33 ++++++++++++++++++++- 2 files changed, 96 insertions(+), 12 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index 36af7249d..91aca28e7 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -119,6 +119,24 @@ struct sink_data { struct global *global; }; +static pa_sink_state_t node_state_to_sink(enum pw_node_state s) +{ + switch(s) { + case PW_NODE_STATE_ERROR: + return PA_SINK_UNLINKED; + case PW_NODE_STATE_CREATING: + return PA_SINK_INIT; + case PW_NODE_STATE_SUSPENDED: + return PA_SINK_SUSPENDED; + case PW_NODE_STATE_IDLE: + return PA_SINK_IDLE; + case PW_NODE_STATE_RUNNING: + return PA_SINK_RUNNING; + default: + return PA_SINK_INVALID_STATE; + } +} + static void sink_callback(struct sink_data *d) { struct global *g = d->global; @@ -134,6 +152,7 @@ static void sink_callback(struct sink_data *d) i.proplist = pa_proplist_new_dict(info->props); i.owner_module = g->parent_id; i.base_volume = PA_VOLUME_NORM; + i.state = node_state_to_sink(info->state); i.n_volume_steps = PA_VOLUME_NORM+1; i.n_formats = 1; ii[0].encoding = PA_ENCODING_PCM; @@ -318,20 +337,45 @@ struct source_data { struct global *global; }; +static pa_source_state_t node_state_to_source(enum pw_node_state s) +{ + switch(s) { + case PW_NODE_STATE_ERROR: + return PA_SOURCE_UNLINKED; + case PW_NODE_STATE_CREATING: + return PA_SOURCE_INIT; + case PW_NODE_STATE_SUSPENDED: + return PA_SOURCE_SUSPENDED; + case PW_NODE_STATE_IDLE: + return PA_SOURCE_IDLE; + case PW_NODE_STATE_RUNNING: + return PA_SOURCE_RUNNING; + default: + return PA_SOURCE_INVALID_STATE; + } +} static void source_callback(struct source_data *d) { struct global *g = d->global; struct pw_node_info *info = g->info; pa_source_info i; + pa_format_info ii[1]; + pa_format_info *ip[1]; spa_zero(i); i.index = g->id; i.name = info->name; + i.description = info->name; i.proplist = pa_proplist_new_dict(info->props); i.owner_module = g->parent_id; i.base_volume = PA_VOLUME_NORM; + i.state = node_state_to_source(info->state); i.n_volume_steps = PA_VOLUME_NORM+1; - + i.n_formats = 1; + ii[0].encoding = PA_ENCODING_PCM; + ii[0].plist = pa_proplist_new(); + ip[0] = ii; + i.formats = ip; d->cb(d->context, &i, 0, d->userdata); } @@ -748,23 +792,32 @@ static void sink_input_callback(struct sink_input_data *d) spa_zero(i); i.index = g->id; i.name = info->name; - i.proplist = pa_proplist_new_dict(info->props); i.owner_module = g->parent_id; - ii[0].encoding = PA_ENCODING_PCM; - ii[0].plist = pa_proplist_new(); - i.format = ii; + i.client = PA_INVALID_INDEX; + i.sink = PA_INVALID_INDEX; + pa_cvolume_init(&i.volume); + if (s) { + i.sample_spec = s->sample_spec; + i.channel_map = s->channel_map; + pa_cvolume_set(&i.volume, 1, s->volume * PA_VOLUME_NORM); + i.format = s->format; + } + else { + pa_channel_map_init(&i.channel_map); + pa_sample_spec_init(&i.sample_spec); + ii[0].encoding = PA_ENCODING_PCM; + ii[0].plist = pa_proplist_new(); + i.format = ii; + } + i.buffer_usec = 0; + i.sink_usec = 0; i.resample_method = "PipeWire resampler"; i.driver = "PipeWire"; i.mute = false; + i.proplist = pa_proplist_new_dict(info->props); i.corked = false; i.has_volume = true; i.volume_writable = true; - i.volume.channels = 1; - if (s) - i.volume.values[0] = s->volume * PA_VOLUME_NORM; - else - i.volume.values[0] = PA_VOLUME_NORM; - d->cb(d->context, &i, 0, d->userdata); } diff --git a/src/stream.c b/src/stream.c index 6a6af3ee2..440ec6382 100644 --- a/src/stream.c +++ b/src/stream.c @@ -313,9 +313,11 @@ static void stream_format_changed(void *data, const struct spa_pod *format) s->sample_spec.rate = info.info.raw.rate; s->sample_spec.channels = info.info.raw.channels; + pa_channel_map_init_auto(&s->channel_map, info.info.raw.channels, PA_CHANNEL_MAP_ALSA); + if (s->format) pa_format_info_free(s->format); - s->format = pa_format_info_from_sample_spec(&s->sample_spec, NULL); + s->format = pa_format_info_from_sample_spec(&s->sample_spec, &s->channel_map); patch_buffer_attr(s, &s->buffer_attr, NULL); @@ -642,6 +644,7 @@ static int create_stream(pa_stream_direction_t direction, struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); struct pw_properties *props; uint32_t sample_rate = 0, stride = 0; + const char *str; spa_assert(s); spa_assert(s->refcount >= 1); @@ -705,6 +708,34 @@ static int create_stream(pa_stream_direction_t direction, props = (struct pw_properties *) pw_stream_get_properties(s->stream); pw_properties_setf(props, "node.latency", "%u/%u", s->buffer_attr.minreq / stride, sample_rate); + pw_properties_set(props, PW_NODE_PROP_MEDIA, "Audio"); + pw_properties_set(props, PW_NODE_PROP_CATEGORY, + direction == PA_STREAM_PLAYBACK ? + "Playback" : "Capture"); + + if ((str = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_ROLE)) == NULL) + str = "Music"; + else if (strcmp(str, "video") == 0) + str = "Movie"; + else if (strcmp(str, "music") == 0) + str = "Music"; + else if (strcmp(str, "game") == 0) + str = "Game"; + else if (strcmp(str, "event") == 0) + str = "Notification"; + else if (strcmp(str, "phone") == 0) + str = "Communication"; + else if (strcmp(str, "animation") == 0) + str = "Movie"; + else if (strcmp(str, "production") == 0) + str = "Production"; + else if (strcmp(str, "a11y") == 0) + str = "Accessibility"; + else if (strcmp(str, "test") == 0) + str = "Test"; + else + str = "Music"; + pw_properties_set(props, PW_NODE_PROP_ROLE, str); res = pw_stream_connect(s->stream, direction == PA_STREAM_PLAYBACK ? From ffde111099eb4f25a6ff738940ec81a4c3309b02 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 17 Jul 2018 17:40:33 +0200 Subject: [PATCH 015/116] stream: update for rate --- src/stream.c | 9 +++++---- src/volume.c | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/stream.c b/src/stream.c index 440ec6382..0692f8ca5 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1367,18 +1367,19 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) pw_stream_get_time(s->stream, &t); clock_gettime(CLOCK_MONOTONIC, &ts); - now = ts.tv_sec * SPA_NSEC_PER_SEC + ts.tv_nsec; + now = SPA_TIMESPEC_TO_TIME(&ts); delay = (now - t.now) / PA_NSEC_PER_USEC; - if (t.rate.denom != 0) - res = delay + ((t.ticks * t.rate.num * PA_USEC_PER_SEC) / t.rate.denom); + if (t.rate.num != 0) + res = delay + ((t.ticks * t.rate.denom * PA_USEC_PER_SEC) / t.rate.num); else res = 0; if (r_usec) *r_usec = res; - pw_log_trace("stream %p: %ld %ld %ld %ld %d/%d %ld", s, now, t.now, delay, t.ticks, t.rate.num, t.rate.denom, res); + pw_log_debug("stream %p: %ld %ld %ld %ld %d/%d %ld", + s, now, t.now, delay, t.ticks, t.rate.num, t.rate.denom, res); return 0; } diff --git a/src/volume.c b/src/volume.c index 21227e3e6..cb99a1857 100644 --- a/src/volume.c +++ b/src/volume.c @@ -787,7 +787,7 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) { return v; } -pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map *cm, pa_channel_position_mask_t mask) { +pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, const pa_channel_map *cm, pa_channel_position_mask_t mask) { unsigned c; pa_volume_t t = 0; From 6eb6e31bebcbb327256a96342e405c654afda2f6 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 19 Jul 2018 13:35:46 +0200 Subject: [PATCH 016/116] stream: improve timing Use the new stream time information to get more accurate read and write pointers. --- src/stream.c | 214 +++++++++++++++++++++++++++++---------------------- 1 file changed, 124 insertions(+), 90 deletions(-) diff --git a/src/stream.c b/src/stream.c index 0692f8ca5..25d0f79a7 100644 --- a/src/stream.c +++ b/src/stream.c @@ -32,16 +32,7 @@ #define MIN_QUEUED 1 -struct pending_data { - struct spa_list link; - - const void *data; - size_t nbytes; - size_t offset; - - pa_free_cb_t free_cb; - void *free_cb_data; -}; +#define MAX_SIZE (4*1024*1024) static const uint32_t audio_formats[] = { [PA_SAMPLE_U8] = offsetof(struct spa_type_audio_format, U8), @@ -76,7 +67,7 @@ static inline pa_sample_format_t format_id2pa(pa_stream *s, uint32_t id) return PA_SAMPLE_INVALID; } -static int dequeue_buffer(pa_stream *s) +static inline int dequeue_buffer(pa_stream *s) { struct pw_buffer *buf; uint32_t index; @@ -340,13 +331,46 @@ static void stream_remove_buffer(void *data, struct pw_buffer *buffer) s->maxsize -= buffer->buffer->datas[0].maxsize; } +static void update_timing_info(pa_stream *s) +{ + struct pw_time pwt; + pa_timing_info *ti = &s->timing_info; + size_t stride = pa_frame_size(&s->sample_spec); + + pw_stream_get_time(s->stream, &pwt); + s->timing_info_valid = false; + + if (pwt.rate.num == 0) + return; + + pa_timeval_store(&ti->timestamp, pwt.now / SPA_NSEC_PER_USEC); + ti->synchronized_clocks = true; + if (s->direction == PA_STREAM_PLAYBACK) + ti->sink_usec = (-pwt.delay * SPA_USEC_PER_SEC / pwt.rate.num); + else + ti->source_usec = (pwt.delay * SPA_USEC_PER_SEC / pwt.rate.num); + ti->transport_usec = 0; + ti->playing = 1; + ti->write_index_corrupt = false; + ti->write_index = pwt.queued + (pwt.ticks * s->sample_spec.rate / pwt.rate.num) * stride; + ti->read_index_corrupt = false; + ti->read_index = ((pwt.ticks + pwt.delay) * s->sample_spec.rate / pwt.rate.num) * stride; + + ti->configured_sink_usec = 0; + ti->configured_source_usec = 0; + ti->since_underrun = 0; + s->timing_info_valid = true; +} + static void stream_process(void *data) { pa_stream *s = data; - s->timing_info_valid = true; + update_timing_info(s); - if (dequeue_buffer(s) < 0 && s->dequeued_size <= 0) + while (dequeue_buffer(s) == 0); + + if (s->dequeued_size <= 0) return; if (s->direction == PA_STREAM_PLAYBACK) { @@ -826,6 +850,9 @@ int queue_buffer(pa_stream *s) s->dequeued_size -= s->buffer->buffer->datas[0].chunk->size; spa_ringbuffer_read_update(&s->dequeued_ring, s->buffer_index + 1); + s->buffer->size = s->buffer->buffer->datas[0].chunk->size; + pw_log_debug("%d %"PRIu64, s->buffer->buffer->id, s->buffer->size); + pw_stream_queue_buffer(s->stream, s->buffer); s->buffer = NULL; return 0; @@ -848,13 +875,15 @@ int pa_stream_begin_write( PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID); if ((res = peek_buffer(s)) < 0) { - pw_log_warn("stream %p: no buffer", s); *data = NULL; *nbytes = 0; - return 0; } - *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); - *nbytes = s->buffer_size - s->buffer_offset; + else { + size_t max = s->buffer_size - s->buffer_offset; + *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); + *nbytes = *nbytes != -1 ? SPA_MIN(*nbytes, max) : max; + } + pw_log_debug("peek buffer %p %zd", *data, *nbytes); return 0; } @@ -868,46 +897,12 @@ int pa_stream_cancel_write(pa_stream *s) PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + pw_log_debug("cancel %p %p %d", s->buffer, s->buffer_data, s->buffer_size); s->buffer = NULL; return 0; } -static void flush_pending(pa_stream *s) -{ - struct pending_data *p; - void *data; - size_t nbytes; - bool flush; - - while(!spa_list_is_empty(&s->pending)) { - p = spa_list_first(&s->pending, struct pending_data, link); - - pa_stream_begin_write(s, &data, &nbytes); - if (data == NULL || nbytes == 0) - break; - - nbytes = SPA_MIN(nbytes, p->nbytes - p->offset); - memcpy(data, p->data + p->offset, nbytes); - - p->offset += nbytes; - s->buffer_offset += nbytes; - - flush = p->offset >= p->nbytes; - - if (flush) { - spa_list_remove(&p->link); - if (p->free_cb) - p->free_cb(p->free_cb_data); - pa_xfree(p); - } - if (flush || s->buffer_offset >= s->buffer_size) { - s->buffer->buffer->datas[0].chunk->size = s->buffer_offset; - queue_buffer(s); - } - } -} - int pa_stream_write(pa_stream *s, const void *data, size_t nbytes, @@ -946,17 +941,30 @@ int pa_stream_write_ext_free(pa_stream *s, PA_CHECK_VALIDITY(s->context, !free_cb || !s->buffer, PA_ERR_INVALID); if (s->buffer == NULL) { - struct pending_data *p; + void *dst; + const void *src = data; + size_t towrite = nbytes, dsize; - p = pa_xmalloc(sizeof(struct pending_data)); - p->data = data; - p->nbytes = nbytes; - p->offset = 0; - p->free_cb = free_cb; - p->free_cb_data = free_cb_data; - spa_list_append(&s->pending, &p->link); + while (towrite > 0) { + dsize = towrite; - flush_pending(s); + if (pa_stream_begin_write(s, &dst, &dsize) < 0 || + dst == NULL || dsize == 0) { + pw_log_debug("out of buffers"); + break; + } + + memcpy(dst, src, dsize); + + s->buffer->buffer->datas[0].chunk->offset = 0; + s->buffer->buffer->datas[0].chunk->size = dsize; + queue_buffer(s); + + towrite -= dsize; + src += dsize; + } + if (free_cb) + free_cb(free_cb_data); } else { s->buffer->buffer->datas[0].chunk->offset = data - s->buffer_data; @@ -964,17 +972,8 @@ int pa_stream_write_ext_free(pa_stream *s, queue_buffer(s); } - /* Update the write index in the already available latency data */ - if (s->timing_info_valid) { - if (seek == PA_SEEK_ABSOLUTE) { - s->timing_info.write_index_corrupt = false; - s->timing_info.write_index = offset + (int64_t) nbytes; - } else if (seek == PA_SEEK_RELATIVE) { - if (!s->timing_info.write_index_corrupt) - s->timing_info.write_index += offset + (int64_t) nbytes; - } else - s->timing_info.write_index_corrupt = true; - } + update_timing_info(s); + return 0; } @@ -1081,8 +1080,9 @@ static void on_timing_success(pa_operation *o, void *userdata) { struct success_ack *d = userdata; pa_stream *s = o->stream; + + update_timing_info(s); pa_operation_done(o); - s->timing_info_valid = true; if (d->cb) d->cb(s, s->timing_info_valid, d->userdata); @@ -1352,10 +1352,10 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { - struct pw_time t; pa_usec_t res; struct timespec ts; - uint64_t now, delay; + uint64_t now, delay, read_time; + pa_timing_info *i; spa_assert(s); spa_assert(s->refcount >= 1); @@ -1364,28 +1364,49 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); - pw_stream_get_time(s->stream, &t); - clock_gettime(CLOCK_MONOTONIC, &ts); now = SPA_TIMESPEC_TO_TIME(&ts); - delay = (now - t.now) / PA_NSEC_PER_USEC; - if (t.rate.num != 0) - res = delay + ((t.ticks * t.rate.denom * PA_USEC_PER_SEC) / t.rate.num); - else - res = 0; + i = &s->timing_info; + delay = (now - SPA_TIMEVAL_TO_TIME(&i->timestamp)) / SPA_NSEC_PER_USEC; + read_time = pa_bytes_to_usec((uint64_t) i->read_index, &s->sample_spec); + + res = delay + read_time; if (r_usec) *r_usec = res; - pw_log_debug("stream %p: %ld %ld %ld %ld %d/%d %ld", - s, now, t.now, delay, t.ticks, t.rate.num, t.rate.denom, res); + pw_log_debug("stream %p: %ld %ld %ld %ld %ld %ld %ld", s, now, delay, read_time, + i->write_index, i->read_index, + i->write_index - i->read_index, + res); return 0; } +static pa_usec_t time_counter_diff(const pa_stream *s, pa_usec_t a, pa_usec_t b, int *negative) { + pa_assert(s); + pa_assert(s->refcount >= 1); + + if (negative) + *negative = 0; + + if (a >= b) + return a-b; + else { + if (negative && s->direction == PA_STREAM_RECORD) { + *negative = 1; + return b-a; + } else + return 0; + } +} + int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) { + pa_usec_t t, c; + int64_t cindex; + spa_assert(s); spa_assert(s->refcount >= 1); spa_assert(r_usec); @@ -1394,11 +1415,22 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); - pw_log_warn("Not Implemented"); - if (r_usec) - *r_usec = 0; - if (negative) - *negative = 0; + pa_stream_get_time(s, &t); + + if (s->direction == PA_STREAM_PLAYBACK) + cindex = s->timing_info.write_index; + else + cindex = s->timing_info.read_index; + + if (cindex < 0) + cindex = 0; + + c = pa_bytes_to_usec((uint64_t) cindex, &s->sample_spec); + + if (s->direction == PA_STREAM_PLAYBACK) + *r_usec = time_counter_diff(s, c, t, negative); + else + *r_usec = time_counter_diff(s, t, c, negative); return 0; } @@ -1412,7 +1444,9 @@ const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_NODATA); - pw_log_warn("Not Implemented"); + pw_log_debug("stream %p: %ld %ld %ld", s, + s->timing_info.write_index, s->timing_info.read_index, + (s->timing_info.write_index - s->timing_info.read_index)); return &s->timing_info; } From f73d640cb84fca07f3622d2fe420c61530f4e51a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 31 Jul 2018 21:37:41 +0200 Subject: [PATCH 017/116] stream: fix read and write index for capture --- src/stream.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/stream.c b/src/stream.c index 25d0f79a7..5f1f5e991 100644 --- a/src/stream.c +++ b/src/stream.c @@ -336,6 +336,7 @@ static void update_timing_info(pa_stream *s) struct pw_time pwt; pa_timing_info *ti = &s->timing_info; size_t stride = pa_frame_size(&s->sample_spec); + uint64_t delay; pw_stream_get_time(s->stream, &pwt); s->timing_info_valid = false; @@ -345,16 +346,22 @@ static void update_timing_info(pa_stream *s) pa_timeval_store(&ti->timestamp, pwt.now / SPA_NSEC_PER_USEC); ti->synchronized_clocks = true; - if (s->direction == PA_STREAM_PLAYBACK) - ti->sink_usec = (-pwt.delay * SPA_USEC_PER_SEC / pwt.rate.num); - else - ti->source_usec = (pwt.delay * SPA_USEC_PER_SEC / pwt.rate.num); ti->transport_usec = 0; ti->playing = 1; ti->write_index_corrupt = false; - ti->write_index = pwt.queued + (pwt.ticks * s->sample_spec.rate / pwt.rate.num) * stride; ti->read_index_corrupt = false; - ti->read_index = ((pwt.ticks + pwt.delay) * s->sample_spec.rate / pwt.rate.num) * stride; + + delay = pwt.delay * SPA_USEC_PER_SEC / pwt.rate.num; + if (s->direction == PA_STREAM_PLAYBACK) { + ti->sink_usec = delay; + ti->write_index = pwt.queued + (pwt.ticks * s->sample_spec.rate / pwt.rate.num) * stride; + ti->read_index = ((pwt.ticks - pwt.delay) * s->sample_spec.rate / pwt.rate.num) * stride; + } + else { + ti->source_usec = delay; + ti->read_index = pwt.queued + (pwt.ticks * s->sample_spec.rate / pwt.rate.num) * stride; + ti->write_index = ((pwt.ticks + pwt.delay) * s->sample_spec.rate / pwt.rate.num) * stride; + } ti->configured_sink_usec = 0; ti->configured_source_usec = 0; From e64413fee1c0e99553ac3a418d42dae57a8bcd9c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 31 Jul 2018 21:38:01 +0200 Subject: [PATCH 018/116] stream: move some debug to trace --- src/stream.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/stream.c b/src/stream.c index 5f1f5e991..77e60522f 100644 --- a/src/stream.c +++ b/src/stream.c @@ -858,7 +858,8 @@ int queue_buffer(pa_stream *s) spa_ringbuffer_read_update(&s->dequeued_ring, s->buffer_index + 1); s->buffer->size = s->buffer->buffer->datas[0].chunk->size; - pw_log_debug("%d %"PRIu64, s->buffer->buffer->id, s->buffer->size); + pw_log_trace("%d %"PRIu64"/%d", s->buffer->buffer->id, s->buffer->size, + s->buffer->buffer->datas[0].chunk->offset); pw_stream_queue_buffer(s->stream, s->buffer); s->buffer = NULL; @@ -890,7 +891,7 @@ int pa_stream_begin_write( *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); *nbytes = *nbytes != -1 ? SPA_MIN(*nbytes, max) : max; } - pw_log_debug("peek buffer %p %zd", *data, *nbytes); + pw_log_trace("peek buffer %p %zd", *data, *nbytes); return 0; } @@ -1004,7 +1005,7 @@ int pa_stream_peek(pa_stream *s, } *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); *nbytes = s->buffer_size; - pw_log_debug("stream %p: %p %zd", s, *data, *nbytes); + pw_log_trace("stream %p: %p %zd", s, *data, *nbytes); return 0; } @@ -1018,7 +1019,7 @@ int pa_stream_drop(pa_stream *s) PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->buffer, PA_ERR_BADSTATE); - pw_log_debug("stream %p", s); + pw_log_trace("stream %p", s); queue_buffer(s); return 0; @@ -1383,7 +1384,7 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) if (r_usec) *r_usec = res; - pw_log_debug("stream %p: %ld %ld %ld %ld %ld %ld %ld", s, now, delay, read_time, + pw_log_trace("stream %p: %ld %ld %ld %ld %ld %ld %ld", s, now, delay, read_time, i->write_index, i->read_index, i->write_index - i->read_index, res); @@ -1451,7 +1452,7 @@ const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_NODATA); - pw_log_debug("stream %p: %ld %ld %ld", s, + pw_log_trace("stream %p: %ld %ld %ld", s, s->timing_info.write_index, s->timing_info.read_index, (s->timing_info.write_index - s->timing_info.read_index)); From 1ea54ac9c5a02a3308db4d6bcc4dac26d9da4dcb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 2 Aug 2018 10:31:29 +0200 Subject: [PATCH 019/116] pulse: improve remote sync Add an explicit method to make the operation to a roundtrip to sync pending actions. Implement drain. --- src/context.c | 26 +++++++++++++++++--------- src/internal.h | 3 +++ src/introspect.c | 13 +++++++++++++ src/operation.c | 14 ++++++++++---- src/stream.c | 28 +++++++++++++++++++++++++++- 5 files changed, 70 insertions(+), 14 deletions(-) diff --git a/src/context.c b/src/context.c index 56990cde2..47ad90353 100644 --- a/src/context.c +++ b/src/context.c @@ -271,28 +271,32 @@ static void remote_state_changed(void *data, enum pw_remote_state old, o = pa_operation_new(c, NULL, on_ready, sizeof(struct ready_data)); d = o->userdata; d->context = c; + pa_operation_sync(o); pa_operation_unref(o); break; } } -static void remote_sync_reply(void *data, uint32_t seq) +static void complete_operations(pa_context *c, uint32_t seq) { - pa_context *c = data; - pa_operation *o; - - pw_log_debug("done %d", seq); - spa_list_for_each(o, &c->operations, link) { + pa_operation *o, *t; + spa_list_for_each_safe(o, t, &c->operations, link) { if (o->seq != seq) continue; pa_operation_ref(o); if (o->callback) o->callback(o, o->userdata); pa_operation_unref(o); - break; } } +static void remote_sync_reply(void *data, uint32_t seq) +{ + pa_context *c = data; + pw_log_debug("done %d", seq); + complete_operations(c, seq); +} + static const struct pw_remote_events remote_events = { PW_VERSION_REMOTE_EVENTS, .state_changed = remote_state_changed, @@ -466,9 +470,10 @@ struct notify_data { static void on_notify(pa_operation *o, void *userdata) { struct notify_data *d = userdata; + pa_context *c = o->context; pa_operation_done(o); if (d->cb) - d->cb(o->context, d->userdata); + d->cb(c, d->userdata); } pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) @@ -480,6 +485,7 @@ pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *u d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -493,9 +499,10 @@ struct success_data { static void on_success(pa_operation *o, void *userdata) { struct success_data *d = userdata; + pa_context *c = o->context; pa_operation_done(o); if (d->cb) - d->cb(o->context, d->ret, d->userdata); + d->cb(c, d->ret, d->userdata); } pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) @@ -556,6 +563,7 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su d->ret = PA_ERR_ACCESS; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } diff --git a/src/internal.h b/src/internal.h index a478cab63..0b9b6d681 100644 --- a/src/internal.h +++ b/src/internal.h @@ -358,6 +358,8 @@ struct pa_stream { uint32_t buffer_offset; float volume; + pa_operation *drain; + uint64_t queued; }; void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); @@ -385,6 +387,7 @@ struct pa_operation pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, size_t userdata_size); void pa_operation_done(pa_operation *o); +int pa_operation_sync(pa_operation *o); #ifdef __cplusplus } diff --git a/src/introspect.c b/src/introspect.c index 91aca28e7..0d9dcb5e5 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -212,6 +212,7 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, d->cb = cb; d->userdata = userdata; d->global = g; + pa_operation_sync(o); return o; } @@ -241,6 +242,7 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_ d->cb = cb; d->userdata = userdata; d->global = g; + pa_operation_sync(o); return o; } @@ -277,6 +279,7 @@ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, d->context = c; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -418,6 +421,7 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, p d->cb = cb; d->userdata = userdata; d->global = g; + pa_operation_sync(o); return o; } @@ -454,6 +458,7 @@ pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t d->context = c; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -568,6 +573,7 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_ d->cb = cb; d->userdata = userdata; d->global = g; + pa_operation_sync(o); return o; } @@ -605,6 +611,7 @@ pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t d->context = c; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -678,6 +685,7 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_ d->cb = cb; d->userdata = userdata; d->global = g; + pa_operation_sync(o); return o; } @@ -715,6 +723,7 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t d->context = c; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -854,6 +863,7 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin d->cb = cb; d->userdata = userdata; d->global = g; + pa_operation_sync(o); return o; } @@ -890,6 +900,7 @@ pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_i d->context = c; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -935,6 +946,7 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -957,6 +969,7 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } diff --git a/src/operation.c b/src/operation.c index fa0c58735..99ee48531 100644 --- a/src/operation.c +++ b/src/operation.c @@ -36,7 +36,7 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb o->refcount = 1; o->context = c; o->stream = s; - o->seq = ++c->seq; + o->seq = SPA_ID_INVALID; o->state = PA_OPERATION_RUNNING; o->callback = cb; @@ -44,13 +44,19 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb spa_list_append(&c->operations, &o->link); pa_operation_ref(o); - - pw_log_debug("new %p %d", o, o->seq); - pw_core_proxy_sync(c->core_proxy, o->seq); + pw_log_debug("new %p", o); return o; } +int pa_operation_sync(pa_operation *o) +{ + pa_context *c = o->context; + o->seq = ++c->seq; + pw_log_debug("operation %p: sync %d", o, o->seq); + pw_core_proxy_sync(c->core_proxy, o->seq); + return 0; +} pa_operation *pa_operation_ref(pa_operation *o) { diff --git a/src/stream.c b/src/stream.c index 77e60522f..7886e1c33 100644 --- a/src/stream.c +++ b/src/stream.c @@ -340,6 +340,8 @@ static void update_timing_info(pa_stream *s) pw_stream_get_time(s->stream, &pwt); s->timing_info_valid = false; + s->queued = pwt.queued; + pw_log_debug("stream %p: %"PRIu64, s, s->queued); if (pwt.rate.num == 0) return; @@ -375,6 +377,15 @@ static void stream_process(void *data) update_timing_info(s); + if (s->drain && s->queued == 0) { + pa_operation *o = s->drain; + pa_operation_ref(o); + if (o->callback) + o->callback(o, o->userdata); + pa_operation_unref(o); + s->drain = NULL; + } + while (dequeue_buffer(s) == 0); if (s->dequeued_size <= 0) @@ -816,6 +827,7 @@ int pa_stream_disconnect(pa_stream *s) s->disconnecting = true; pw_stream_disconnect(s->stream); o = pa_operation_new(s->context, s, on_disconnected, 0); + pa_operation_sync(o); pa_operation_unref(o); return 0; @@ -1062,7 +1074,7 @@ static void on_success(pa_operation *o, void *userdata) pa_stream *s = o->stream; pa_operation_done(o); if (d->cb) - d->cb(s, PA_OK, d->userdata); + d->cb(s, 1, d->userdata); } pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) @@ -1076,10 +1088,14 @@ pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *use PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); + pw_log_debug("stream %p", s); o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); d = o->userdata; d->cb = cb; d->userdata = userdata; + if (s->drain) + pa_operation_cancel(s->drain); + s->drain = o; return o; } @@ -1111,6 +1127,7 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -1271,6 +1288,7 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -1291,6 +1309,7 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -1312,6 +1331,7 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -1333,6 +1353,7 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -1354,6 +1375,7 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -1511,6 +1533,7 @@ pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -1532,6 +1555,7 @@ pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_strea d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -1553,6 +1577,7 @@ pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_ d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } @@ -1573,6 +1598,7 @@ pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], d = o->userdata; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } From ffa3b03982bfe9d913441819db466e2423256bd0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 15 Aug 2018 21:46:50 +0200 Subject: [PATCH 020/116] stream: update for rate changes --- src/stream.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/stream.c b/src/stream.c index 7886e1c33..6b0c83806 100644 --- a/src/stream.c +++ b/src/stream.c @@ -341,9 +341,9 @@ static void update_timing_info(pa_stream *s) pw_stream_get_time(s->stream, &pwt); s->timing_info_valid = false; s->queued = pwt.queued; - pw_log_debug("stream %p: %"PRIu64, s, s->queued); + pw_log_trace("stream %p: %"PRIu64, s, s->queued); - if (pwt.rate.num == 0) + if (pwt.rate.denom == 0) return; pa_timeval_store(&ti->timestamp, pwt.now / SPA_NSEC_PER_USEC); @@ -353,16 +353,16 @@ static void update_timing_info(pa_stream *s) ti->write_index_corrupt = false; ti->read_index_corrupt = false; - delay = pwt.delay * SPA_USEC_PER_SEC / pwt.rate.num; + delay = pwt.delay * SPA_USEC_PER_SEC / pwt.rate.denom; if (s->direction == PA_STREAM_PLAYBACK) { ti->sink_usec = delay; - ti->write_index = pwt.queued + (pwt.ticks * s->sample_spec.rate / pwt.rate.num) * stride; - ti->read_index = ((pwt.ticks - pwt.delay) * s->sample_spec.rate / pwt.rate.num) * stride; + ti->write_index = pwt.queued + (pwt.ticks * s->sample_spec.rate / pwt.rate.denom) * stride; + ti->read_index = ((pwt.ticks - pwt.delay) * s->sample_spec.rate / pwt.rate.denom) * stride; } else { ti->source_usec = delay; - ti->read_index = pwt.queued + (pwt.ticks * s->sample_spec.rate / pwt.rate.num) * stride; - ti->write_index = ((pwt.ticks + pwt.delay) * s->sample_spec.rate / pwt.rate.num) * stride; + ti->read_index = pwt.queued + (pwt.ticks * s->sample_spec.rate / pwt.rate.denom) * stride; + ti->write_index = ((pwt.ticks + pwt.delay) * s->sample_spec.rate / pwt.rate.denom) * stride; } ti->configured_sink_usec = 0; From be2dee53bcb764fbd7bf1e3b215728036650fa3c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 Aug 2018 11:00:33 +0200 Subject: [PATCH 021/116] update to new types --- src/context.c | 13 +++++---- src/internal.h | 19 ------------- src/introspect.c | 7 +++-- src/stream.c | 70 +++++++++++++++++++++++------------------------- 4 files changed, 42 insertions(+), 67 deletions(-) diff --git a/src/context.c b/src/context.c index 47ad90353..24fdf0b06 100644 --- a/src/context.c +++ b/src/context.c @@ -100,7 +100,7 @@ static int set_mask(pa_context *c, struct global *g) { const char *str; - if (g->type == c->t->node) { + if (g->type == PW_ID_INTERFACE_Node) { if (g->props == NULL) return 0; if ((str = pw_properties_get(g->props, "media.class")) == NULL) @@ -137,18 +137,18 @@ static int set_mask(pa_context *c, struct global *g) g->event = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT; } } - else if (g->type == c->t->module) { + else if (g->type == PW_ID_INTERFACE_Module) { g->mask = PA_SUBSCRIPTION_MASK_MODULE; g->event = PA_SUBSCRIPTION_EVENT_MODULE; } - else if (g->type == c->t->client) { + else if (g->type == PW_ID_INTERFACE_Client) { g->mask = PA_SUBSCRIPTION_MASK_CLIENT; g->event = PA_SUBSCRIPTION_EVENT_CLIENT; } - else if (g->type == c->t->port) { + else if (g->type == PW_ID_INTERFACE_Port) { pw_log_debug("found port %d", g->id); } - else if (g->type == c->t->link) { + else if (g->type == PW_ID_INTERFACE_Link) { if ((str = pw_properties_get(g->props, "link.output")) == NULL) return 0; g->link_info.src = pa_context_find_global(c, pw_properties_parse_int(str)); @@ -262,7 +262,7 @@ static void remote_state_changed(void *data, enum pw_remote_state old, c->core_proxy = pw_remote_get_core_proxy(c->remote); c->registry_proxy = pw_core_proxy_get_registry(c->core_proxy, - c->t->registry, + PW_ID_INTERFACE_Registry, PW_VERSION_REGISTRY, 0); pw_registry_proxy_add_listener(c->registry_proxy, &c->registry_listener, @@ -329,7 +329,6 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * c = pw_remote_get_user_data(r); c->loop = loop; c->core = core; - c->t = pw_core_get_type(core); c->remote = r; pw_remote_add_listener(r, &c->remote_listener, &remote_events, c); diff --git a/src/internal.h b/src/internal.h index 0b9b6d681..7bdd5d1d0 100644 --- a/src/internal.h +++ b/src/internal.h @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -228,7 +227,6 @@ struct pa_context { struct pw_loop *loop; struct pw_core *core; - struct pw_type *t; struct pw_remote *remote; struct spa_hook remote_listener; @@ -264,21 +262,6 @@ struct pa_context { struct global *pa_context_find_global(pa_context *c, uint32_t id); -struct type { - struct spa_type_media_type media_type; - struct spa_type_media_subtype media_subtype; - struct spa_type_format_audio format_audio; - struct spa_type_audio_format audio_format; -}; - -static inline void init_type(struct type *type, struct spa_type_map *map) -{ - spa_type_media_type_map(map, &type->media_type); - spa_type_media_subtype_map(map, &type->media_subtype); - spa_type_format_audio_map(map, &type->format_audio); - spa_type_audio_format_map(map, &type->audio_format); -} - #define MAX_BUFFERS 64 #define MASK_BUFFERS (MAX_BUFFERS-1) @@ -289,8 +272,6 @@ struct pa_stream { struct pw_stream *stream; struct spa_hook stream_listener; - struct type type; - pa_context *context; pa_proplist *proplist; diff --git a/src/introspect.c b/src/introspect.c index 0d9dcb5e5..2ca522784 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -67,22 +67,21 @@ static int ensure_global(pa_context *c, struct global *g) uint32_t client_version; const void *events; pw_destroy_t destroy; - struct pw_type *t = c->t; if (g->proxy != NULL) return 0; - if (g->type == t->node) { + if (g->type == PW_ID_INTERFACE_Node) { events = &node_events; client_version = PW_VERSION_NODE; destroy = (pw_destroy_t) pw_node_info_free; } - else if (g->type == t->module) { + else if (g->type == PW_ID_INTERFACE_Module) { events = &module_events; client_version = PW_VERSION_MODULE; destroy = (pw_destroy_t) pw_module_info_free; } - else if (g->type == t->client) { + else if (g->type == PW_ID_INTERFACE_Client) { events = &client_events; client_version = PW_VERSION_CLIENT; destroy = (pw_destroy_t) pw_client_info_free; diff --git a/src/stream.c b/src/stream.c index 6b0c83806..08993b434 100644 --- a/src/stream.c +++ b/src/stream.c @@ -35,33 +35,33 @@ #define MAX_SIZE (4*1024*1024) static const uint32_t audio_formats[] = { - [PA_SAMPLE_U8] = offsetof(struct spa_type_audio_format, U8), - [PA_SAMPLE_ALAW] = offsetof(struct spa_type_audio_format, UNKNOWN), - [PA_SAMPLE_ULAW] = offsetof(struct spa_type_audio_format, UNKNOWN), - [PA_SAMPLE_S16NE] = offsetof(struct spa_type_audio_format, S16), - [PA_SAMPLE_S16RE] = offsetof(struct spa_type_audio_format, S16_OE), - [PA_SAMPLE_FLOAT32NE] = offsetof(struct spa_type_audio_format, F32), - [PA_SAMPLE_FLOAT32RE] = offsetof(struct spa_type_audio_format, F32_OE), - [PA_SAMPLE_S32NE] = offsetof(struct spa_type_audio_format, S32), - [PA_SAMPLE_S32RE] = offsetof(struct spa_type_audio_format, S32_OE), - [PA_SAMPLE_S24NE] = offsetof(struct spa_type_audio_format, S24), - [PA_SAMPLE_S24RE] = offsetof(struct spa_type_audio_format, S24_OE), - [PA_SAMPLE_S24_32NE] = offsetof(struct spa_type_audio_format, S24_32), - [PA_SAMPLE_S24_32RE] = offsetof(struct spa_type_audio_format, S24_32_OE), + [PA_SAMPLE_U8] = SPA_AUDIO_FORMAT_U8, + [PA_SAMPLE_ALAW] = SPA_AUDIO_FORMAT_UNKNOWN, + [PA_SAMPLE_ULAW] = SPA_AUDIO_FORMAT_UNKNOWN, + [PA_SAMPLE_S16NE] = SPA_AUDIO_FORMAT_S16, + [PA_SAMPLE_S16RE] = SPA_AUDIO_FORMAT_S16_OE, + [PA_SAMPLE_FLOAT32NE] = SPA_AUDIO_FORMAT_F32, + [PA_SAMPLE_FLOAT32RE] = SPA_AUDIO_FORMAT_F32_OE, + [PA_SAMPLE_S32NE] = SPA_AUDIO_FORMAT_S32, + [PA_SAMPLE_S32RE] = SPA_AUDIO_FORMAT_S32_OE, + [PA_SAMPLE_S24NE] = SPA_AUDIO_FORMAT_S24, + [PA_SAMPLE_S24RE] = SPA_AUDIO_FORMAT_S24_OE, + [PA_SAMPLE_S24_32NE] = SPA_AUDIO_FORMAT_S24_32, + [PA_SAMPLE_S24_32RE] = SPA_AUDIO_FORMAT_S24_32_OE, }; static inline uint32_t format_pa2id(pa_stream *s, pa_sample_format_t format) { if (format < 0 || format >= SPA_N_ELEMENTS(audio_formats)) - return s->type.audio_format.UNKNOWN; - return *SPA_MEMBER(&s->type.audio_format, audio_formats[format], uint32_t); + return SPA_AUDIO_FORMAT_UNKNOWN; + return audio_formats[format]; } static inline pa_sample_format_t format_id2pa(pa_stream *s, uint32_t id) { int i; for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) { - if (id == *SPA_MEMBER(&s->type.audio_format, audio_formats[i], uint32_t)) + if (id == audio_formats[i]) return i; } return PA_SAMPLE_INVALID; @@ -110,7 +110,7 @@ static struct global *find_linked(pa_stream *s, uint32_t idx) pa_context *c = s->context; spa_list_for_each(g, &c->globals, link) { - if (g->type != c->t->link) + if (g->type != PW_ID_INTERFACE_Link) continue; pw_log_debug("%d %d %d", idx, @@ -185,7 +185,6 @@ static void stream_state_changed(void *data, enum pw_stream_state old, static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *attr, struct spa_pod_builder *b) { const struct spa_pod *param; - struct pw_type *t = pw_core_get_type(s->context->core); int32_t blocks, buffers, size, maxsize, stride; blocks = 1; @@ -210,14 +209,14 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att size, buffers); param = spa_pod_builder_object(b, - t->param.idBuffers, t->param_buffers.Buffers, - ":", t->param_buffers.buffers, "iru", buffers, + SPA_ID_PARAM_Buffers, SPA_ID_OBJECT_ParamBuffers, + ":", SPA_PARAM_BUFFERS_buffers, "iru", buffers, SPA_POD_PROP_MIN_MAX(3, MAX_BUFFERS), - ":", t->param_buffers.blocks, "i", blocks, - ":", t->param_buffers.size, "iru", size * stride, + ":", SPA_PARAM_BUFFERS_blocks, "i", blocks, + ":", SPA_PARAM_BUFFERS_size, "iru", size * stride, SPA_POD_PROP_MIN_MAX(size * stride, maxsize * stride), - ":", t->param_buffers.stride, "i", stride, - ":", t->param_buffers.align, "i", 16); + ":", SPA_PARAM_BUFFERS_stride, "i", stride, + ":", SPA_PARAM_BUFFERS_align, "i", 16); return param; } @@ -288,9 +287,9 @@ static void stream_format_changed(void *data, const struct spa_pod *format) "I", &info.media_type, "I", &info.media_subtype); - if (info.media_type != s->type.media_type.audio || - info.media_subtype != s->type.media_subtype.raw || - spa_format_audio_raw_parse(format, &info.info.raw, &s->type.format_audio) < 0 || + if (info.media_type != SPA_MEDIA_TYPE_audio || + info.media_subtype != SPA_MEDIA_SUBTYPE_raw || + spa_format_audio_raw_parse(format, &info.info.raw) < 0 || info.info.raw.layout != SPA_AUDIO_LAYOUT_INTERLEAVED) { res = -EINVAL; goto done; @@ -438,7 +437,6 @@ pa_stream* stream_new(pa_context *c, const char *name, NULL)); s->refcount = 1; s->context = c; - init_type(&s->type, pw_core_get_type(c->core)->map); spa_list_init(&s->pending); pw_stream_add_listener(s->stream, &s->stream_listener, &stream_events, s); @@ -657,16 +655,14 @@ static const struct spa_pod *get_param(pa_stream *s, pa_sample_spec *ss, pa_chan struct spa_pod_builder *b) { const struct spa_pod *param; - struct pw_type *t = pw_core_get_type(s->context->core); - param = spa_pod_builder_object(b, - t->param.idEnumFormat, t->spa_format, - "I", s->type.media_type.audio, - "I", s->type.media_subtype.raw, - ":", s->type.format_audio.format, "I", format_pa2id(s, ss->format), - ":", s->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_INTERLEAVED, - ":", s->type.format_audio.channels, "i", ss->channels, - ":", s->type.format_audio.rate, "i", ss->rate); + SPA_ID_PARAM_EnumFormat, SPA_ID_OBJECT_Format, + "I", SPA_MEDIA_TYPE_audio, + "I", SPA_MEDIA_SUBTYPE_raw, + ":", SPA_FORMAT_AUDIO_format, "I", format_pa2id(s, ss->format), + ":", SPA_FORMAT_AUDIO_layout, "i", SPA_AUDIO_LAYOUT_INTERLEAVED, + ":", SPA_FORMAT_AUDIO_channels, "i", ss->channels, + ":", SPA_FORMAT_AUDIO_rate, "i", ss->rate); return param; } From 4c7fd98479a2da027f949c1e4c089ae58cd7c890 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 27 Aug 2018 09:06:57 +0200 Subject: [PATCH 022/116] stream: update for api changes --- src/stream.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stream.c b/src/stream.c index 08993b434..c103e1dd2 100644 --- a/src/stream.c +++ b/src/stream.c @@ -209,7 +209,7 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att size, buffers); param = spa_pod_builder_object(b, - SPA_ID_PARAM_Buffers, SPA_ID_OBJECT_ParamBuffers, + SPA_PARAM_Buffers, SPA_ID_OBJECT_ParamBuffers, ":", SPA_PARAM_BUFFERS_buffers, "iru", buffers, SPA_POD_PROP_MIN_MAX(3, MAX_BUFFERS), ":", SPA_PARAM_BUFFERS_blocks, "i", blocks, @@ -656,11 +656,11 @@ static const struct spa_pod *get_param(pa_stream *s, pa_sample_spec *ss, pa_chan { const struct spa_pod *param; param = spa_pod_builder_object(b, - SPA_ID_PARAM_EnumFormat, SPA_ID_OBJECT_Format, + SPA_PARAM_EnumFormat, SPA_ID_OBJECT_Format, "I", SPA_MEDIA_TYPE_audio, "I", SPA_MEDIA_SUBTYPE_raw, ":", SPA_FORMAT_AUDIO_format, "I", format_pa2id(s, ss->format), - ":", SPA_FORMAT_AUDIO_layout, "i", SPA_AUDIO_LAYOUT_INTERLEAVED, + ":", SPA_FORMAT_AUDIO_layout, "I", SPA_AUDIO_LAYOUT_INTERLEAVED, ":", SPA_FORMAT_AUDIO_channels, "i", ss->channels, ":", SPA_FORMAT_AUDIO_rate, "i", ss->rate); return param; From 5fb984a64787a875aaa33d5ac551cff54eb7e87c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 29 Aug 2018 09:54:54 +0200 Subject: [PATCH 023/116] update for type changes --- src/context.c | 12 ++++++------ src/introspect.c | 6 +++--- src/stream.c | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/context.c b/src/context.c index 24fdf0b06..f18fb6eab 100644 --- a/src/context.c +++ b/src/context.c @@ -100,7 +100,7 @@ static int set_mask(pa_context *c, struct global *g) { const char *str; - if (g->type == PW_ID_INTERFACE_Node) { + if (g->type == PW_TYPE_INTERFACE_Node) { if (g->props == NULL) return 0; if ((str = pw_properties_get(g->props, "media.class")) == NULL) @@ -137,18 +137,18 @@ static int set_mask(pa_context *c, struct global *g) g->event = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT; } } - else if (g->type == PW_ID_INTERFACE_Module) { + else if (g->type == PW_TYPE_INTERFACE_Module) { g->mask = PA_SUBSCRIPTION_MASK_MODULE; g->event = PA_SUBSCRIPTION_EVENT_MODULE; } - else if (g->type == PW_ID_INTERFACE_Client) { + else if (g->type == PW_TYPE_INTERFACE_Client) { g->mask = PA_SUBSCRIPTION_MASK_CLIENT; g->event = PA_SUBSCRIPTION_EVENT_CLIENT; } - else if (g->type == PW_ID_INTERFACE_Port) { + else if (g->type == PW_TYPE_INTERFACE_Port) { pw_log_debug("found port %d", g->id); } - else if (g->type == PW_ID_INTERFACE_Link) { + else if (g->type == PW_TYPE_INTERFACE_Link) { if ((str = pw_properties_get(g->props, "link.output")) == NULL) return 0; g->link_info.src = pa_context_find_global(c, pw_properties_parse_int(str)); @@ -262,7 +262,7 @@ static void remote_state_changed(void *data, enum pw_remote_state old, c->core_proxy = pw_remote_get_core_proxy(c->remote); c->registry_proxy = pw_core_proxy_get_registry(c->core_proxy, - PW_ID_INTERFACE_Registry, + PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0); pw_registry_proxy_add_listener(c->registry_proxy, &c->registry_listener, diff --git a/src/introspect.c b/src/introspect.c index 2ca522784..fffd689c5 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -71,17 +71,17 @@ static int ensure_global(pa_context *c, struct global *g) if (g->proxy != NULL) return 0; - if (g->type == PW_ID_INTERFACE_Node) { + if (g->type == PW_TYPE_INTERFACE_Node) { events = &node_events; client_version = PW_VERSION_NODE; destroy = (pw_destroy_t) pw_node_info_free; } - else if (g->type == PW_ID_INTERFACE_Module) { + else if (g->type == PW_TYPE_INTERFACE_Module) { events = &module_events; client_version = PW_VERSION_MODULE; destroy = (pw_destroy_t) pw_module_info_free; } - else if (g->type == PW_ID_INTERFACE_Client) { + else if (g->type == PW_TYPE_INTERFACE_Client) { events = &client_events; client_version = PW_VERSION_CLIENT; destroy = (pw_destroy_t) pw_client_info_free; diff --git a/src/stream.c b/src/stream.c index c103e1dd2..510a5ebf8 100644 --- a/src/stream.c +++ b/src/stream.c @@ -110,7 +110,7 @@ static struct global *find_linked(pa_stream *s, uint32_t idx) pa_context *c = s->context; spa_list_for_each(g, &c->globals, link) { - if (g->type != PW_ID_INTERFACE_Link) + if (g->type != PW_TYPE_INTERFACE_Link) continue; pw_log_debug("%d %d %d", idx, @@ -209,7 +209,7 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att size, buffers); param = spa_pod_builder_object(b, - SPA_PARAM_Buffers, SPA_ID_OBJECT_ParamBuffers, + SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, ":", SPA_PARAM_BUFFERS_buffers, "iru", buffers, SPA_POD_PROP_MIN_MAX(3, MAX_BUFFERS), ":", SPA_PARAM_BUFFERS_blocks, "i", blocks, @@ -656,7 +656,7 @@ static const struct spa_pod *get_param(pa_stream *s, pa_sample_spec *ss, pa_chan { const struct spa_pod *param; param = spa_pod_builder_object(b, - SPA_PARAM_EnumFormat, SPA_ID_OBJECT_Format, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, "I", SPA_MEDIA_TYPE_audio, "I", SPA_MEDIA_SUBTYPE_raw, ":", SPA_FORMAT_AUDIO_format, "I", format_pa2id(s, ss->format), From 814a9592f3a6cfc3744ca339d6059780e2e91b12 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 5 Sep 2018 16:48:13 +0200 Subject: [PATCH 024/116] stream: update for API changes --- src/stream.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/stream.c b/src/stream.c index 510a5ebf8..199c96d4e 100644 --- a/src/stream.c +++ b/src/stream.c @@ -210,13 +210,15 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att param = spa_pod_builder_object(b, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - ":", SPA_PARAM_BUFFERS_buffers, "iru", buffers, - SPA_POD_PROP_MIN_MAX(3, MAX_BUFFERS), - ":", SPA_PARAM_BUFFERS_blocks, "i", blocks, - ":", SPA_PARAM_BUFFERS_size, "iru", size * stride, - SPA_POD_PROP_MIN_MAX(size * stride, maxsize * stride), - ":", SPA_PARAM_BUFFERS_stride, "i", stride, - ":", SPA_PARAM_BUFFERS_align, "i", 16); + SPA_PARAM_BUFFERS_buffers, &SPA_POD_CHOICE_RANGE_Int(buffers, 3, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, &SPA_POD_Int(blocks), + SPA_PARAM_BUFFERS_size, &SPA_POD_CHOICE_RANGE_Int( + size * stride, + size * stride, + maxsize * stride), + SPA_PARAM_BUFFERS_stride, &SPA_POD_Int(stride), + SPA_PARAM_BUFFERS_align, &SPA_POD_Int(16), + 0); return param; } @@ -283,9 +285,7 @@ static void stream_format_changed(void *data, const struct spa_pod *format) struct spa_audio_info info = { 0 }; int res; - spa_pod_object_parse(format, - "I", &info.media_type, - "I", &info.media_subtype); + spa_format_parse(format, &info.media_type, &info.media_subtype); if (info.media_type != SPA_MEDIA_TYPE_audio || info.media_subtype != SPA_MEDIA_SUBTYPE_raw || @@ -647,7 +647,7 @@ int pa_stream_is_corked(pa_stream *s) PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - pw_log_debug("stream %p: corked %d", s, s->corked); + pw_log_trace("stream %p: corked %d", s, s->corked); return s->corked; } @@ -655,14 +655,13 @@ static const struct spa_pod *get_param(pa_stream *s, pa_sample_spec *ss, pa_chan struct spa_pod_builder *b) { const struct spa_pod *param; - param = spa_pod_builder_object(b, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - "I", SPA_MEDIA_TYPE_audio, - "I", SPA_MEDIA_SUBTYPE_raw, - ":", SPA_FORMAT_AUDIO_format, "I", format_pa2id(s, ss->format), - ":", SPA_FORMAT_AUDIO_layout, "I", SPA_AUDIO_LAYOUT_INTERLEAVED, - ":", SPA_FORMAT_AUDIO_channels, "i", ss->channels, - ":", SPA_FORMAT_AUDIO_rate, "i", ss->rate); + + param = spa_format_audio_raw_build(b, SPA_PARAM_EnumFormat, + &SPA_AUDIO_INFO_RAW_INIT( + .format = format_pa2id(s, ss->format), + .layout = SPA_AUDIO_LAYOUT_INTERLEAVED, + .channels = ss->channels, + .rate = ss->rate)); return param; } From d41cde1bb89404136bc94015204f2d00fb7b24be Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 17 Sep 2018 09:51:39 +0200 Subject: [PATCH 025/116] stream: add channel mappings --- src/stream.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 10 deletions(-) diff --git a/src/stream.c b/src/stream.c index 199c96d4e..fb39601e9 100644 --- a/src/stream.c +++ b/src/stream.c @@ -67,6 +67,85 @@ static inline pa_sample_format_t format_id2pa(pa_stream *s, uint32_t id) return PA_SAMPLE_INVALID; } +static const uint32_t audio_channels[] = { + [PA_CHANNEL_POSITION_MONO] = SPA_AUDIO_CHANNEL_MONO, + + [PA_CHANNEL_POSITION_FRONT_LEFT] = SPA_AUDIO_CHANNEL_FL, + [PA_CHANNEL_POSITION_FRONT_RIGHT] = SPA_AUDIO_CHANNEL_FR, + [PA_CHANNEL_POSITION_FRONT_CENTER] = SPA_AUDIO_CHANNEL_FC, + + [PA_CHANNEL_POSITION_REAR_CENTER] = SPA_AUDIO_CHANNEL_RC, + [PA_CHANNEL_POSITION_REAR_LEFT] = SPA_AUDIO_CHANNEL_RL, + [PA_CHANNEL_POSITION_REAR_RIGHT] = SPA_AUDIO_CHANNEL_RR, + + [PA_CHANNEL_POSITION_LFE] = SPA_AUDIO_CHANNEL_LFE, + [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SPA_AUDIO_CHANNEL_FLC, + [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SPA_AUDIO_CHANNEL_FRC, + + [PA_CHANNEL_POSITION_SIDE_LEFT] = SPA_AUDIO_CHANNEL_SL, + [PA_CHANNEL_POSITION_SIDE_RIGHT] = SPA_AUDIO_CHANNEL_SR, + + [PA_CHANNEL_POSITION_AUX0] = SPA_AUDIO_CHANNEL_CUSTOM_START + 1, + [PA_CHANNEL_POSITION_AUX1] = SPA_AUDIO_CHANNEL_CUSTOM_START + 2, + [PA_CHANNEL_POSITION_AUX2] = SPA_AUDIO_CHANNEL_CUSTOM_START + 3, + [PA_CHANNEL_POSITION_AUX3] = SPA_AUDIO_CHANNEL_CUSTOM_START + 4, + [PA_CHANNEL_POSITION_AUX4] = SPA_AUDIO_CHANNEL_CUSTOM_START + 5, + [PA_CHANNEL_POSITION_AUX5] = SPA_AUDIO_CHANNEL_CUSTOM_START + 6, + [PA_CHANNEL_POSITION_AUX6] = SPA_AUDIO_CHANNEL_CUSTOM_START + 7, + [PA_CHANNEL_POSITION_AUX7] = SPA_AUDIO_CHANNEL_CUSTOM_START + 8, + [PA_CHANNEL_POSITION_AUX8] = SPA_AUDIO_CHANNEL_CUSTOM_START + 9, + [PA_CHANNEL_POSITION_AUX9] = SPA_AUDIO_CHANNEL_CUSTOM_START + 10, + [PA_CHANNEL_POSITION_AUX10] = SPA_AUDIO_CHANNEL_CUSTOM_START + 11, + [PA_CHANNEL_POSITION_AUX11] = SPA_AUDIO_CHANNEL_CUSTOM_START + 12, + [PA_CHANNEL_POSITION_AUX12] = SPA_AUDIO_CHANNEL_CUSTOM_START + 13, + [PA_CHANNEL_POSITION_AUX13] = SPA_AUDIO_CHANNEL_CUSTOM_START + 14, + [PA_CHANNEL_POSITION_AUX14] = SPA_AUDIO_CHANNEL_CUSTOM_START + 15, + [PA_CHANNEL_POSITION_AUX15] = SPA_AUDIO_CHANNEL_CUSTOM_START + 16, + [PA_CHANNEL_POSITION_AUX16] = SPA_AUDIO_CHANNEL_CUSTOM_START + 17, + [PA_CHANNEL_POSITION_AUX17] = SPA_AUDIO_CHANNEL_CUSTOM_START + 18, + [PA_CHANNEL_POSITION_AUX18] = SPA_AUDIO_CHANNEL_CUSTOM_START + 19, + [PA_CHANNEL_POSITION_AUX19] = SPA_AUDIO_CHANNEL_CUSTOM_START + 20, + [PA_CHANNEL_POSITION_AUX20] = SPA_AUDIO_CHANNEL_CUSTOM_START + 21, + [PA_CHANNEL_POSITION_AUX21] = SPA_AUDIO_CHANNEL_CUSTOM_START + 22, + [PA_CHANNEL_POSITION_AUX22] = SPA_AUDIO_CHANNEL_CUSTOM_START + 23, + [PA_CHANNEL_POSITION_AUX23] = SPA_AUDIO_CHANNEL_CUSTOM_START + 24, + [PA_CHANNEL_POSITION_AUX24] = SPA_AUDIO_CHANNEL_CUSTOM_START + 25, + [PA_CHANNEL_POSITION_AUX25] = SPA_AUDIO_CHANNEL_CUSTOM_START + 26, + [PA_CHANNEL_POSITION_AUX26] = SPA_AUDIO_CHANNEL_CUSTOM_START + 27, + [PA_CHANNEL_POSITION_AUX27] = SPA_AUDIO_CHANNEL_CUSTOM_START + 28, + [PA_CHANNEL_POSITION_AUX28] = SPA_AUDIO_CHANNEL_CUSTOM_START + 29, + [PA_CHANNEL_POSITION_AUX29] = SPA_AUDIO_CHANNEL_CUSTOM_START + 30, + [PA_CHANNEL_POSITION_AUX30] = SPA_AUDIO_CHANNEL_CUSTOM_START + 31, + [PA_CHANNEL_POSITION_AUX31] = SPA_AUDIO_CHANNEL_CUSTOM_START + 32, + + [PA_CHANNEL_POSITION_TOP_CENTER] = SPA_AUDIO_CHANNEL_TC, + + [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SPA_AUDIO_CHANNEL_TFL, + [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SPA_AUDIO_CHANNEL_TFR, + [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SPA_AUDIO_CHANNEL_TFC, + + [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SPA_AUDIO_CHANNEL_TRL, + [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SPA_AUDIO_CHANNEL_TRR, + [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SPA_AUDIO_CHANNEL_TRC, +}; + +static inline uint32_t channel_pa2id(pa_stream *s, pa_channel_position_t channel) +{ + if (channel < 0 || channel >= SPA_N_ELEMENTS(audio_channels)) + return SPA_AUDIO_CHANNEL_UNKNOWN; + return audio_channels[channel]; +} + +static inline pa_channel_position_t channel_id2pa(pa_stream *s, uint32_t id) +{ + int i; + for (i = 0; i < SPA_N_ELEMENTS(audio_channels); i++) { + if (id == audio_channels[i]) + return i; + } + return PA_CHANNEL_POSITION_INVALID; +} + static inline int dequeue_buffer(pa_stream *s) { struct pw_buffer *buf; @@ -283,14 +362,14 @@ static void stream_format_changed(void *data, const struct spa_pod *format) uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); struct spa_audio_info info = { 0 }; - int res; + int i, res; spa_format_parse(format, &info.media_type, &info.media_subtype); if (info.media_type != SPA_MEDIA_TYPE_audio || info.media_subtype != SPA_MEDIA_SUBTYPE_raw || spa_format_audio_raw_parse(format, &info.info.raw) < 0 || - info.info.raw.layout != SPA_AUDIO_LAYOUT_INTERLEAVED) { + !SPA_AUDIO_FORMAT_IS_INTERLEAVED(info.info.raw.format)) { res = -EINVAL; goto done; } @@ -303,7 +382,10 @@ static void stream_format_changed(void *data, const struct spa_pod *format) s->sample_spec.rate = info.info.raw.rate; s->sample_spec.channels = info.info.raw.channels; - pa_channel_map_init_auto(&s->channel_map, info.info.raw.channels, PA_CHANNEL_MAP_ALSA); + pa_channel_map_init(&s->channel_map); + s->channel_map.channels = info.info.raw.channels; + for (i = 0; i < info.info.raw.channels; i++) + s->channel_map.map[i] = channel_id2pa(s, info.info.raw.position[i]); if (s->format) pa_format_info_free(s->format); @@ -416,6 +498,7 @@ pa_stream* stream_new(pa_context *c, const char *name, pa_proplist *p) { pa_stream *s; + char str[1024]; int i; spa_assert(c); @@ -455,6 +538,8 @@ pa_stream* stream_new(pa_context *c, const char *name, else pa_channel_map_init(&s->channel_map); + pw_log_debug("channel map: %p %s", map, pa_channel_map_snprint(str, sizeof(str), &s->channel_map)); + s->n_formats = 0; if (formats) { s->n_formats = n_formats; @@ -654,15 +739,17 @@ int pa_stream_is_corked(pa_stream *s) static const struct spa_pod *get_param(pa_stream *s, pa_sample_spec *ss, pa_channel_map *map, struct spa_pod_builder *b) { - const struct spa_pod *param; + struct spa_audio_info_raw info; - param = spa_format_audio_raw_build(b, SPA_PARAM_EnumFormat, - &SPA_AUDIO_INFO_RAW_INIT( - .format = format_pa2id(s, ss->format), - .layout = SPA_AUDIO_LAYOUT_INTERLEAVED, + info = SPA_AUDIO_INFO_RAW_INIT( .format = format_pa2id(s, ss->format), .channels = ss->channels, - .rate = ss->rate)); - return param; + .rate = ss->rate); + if (map) { + int i; + for (i = 0; i < map->channels; i++) + info.position[i] = channel_pa2id(s, map->map[i]); + } + return spa_format_audio_raw_build(b, SPA_PARAM_EnumFormat, &info); } static int create_stream(pa_stream_direction_t direction, From 6df567e6d484cb62aedadee980788f12d25cd41e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 19 Sep 2018 17:30:59 +0200 Subject: [PATCH 026/116] stream: assume default channels when invalid --- src/stream.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/stream.c b/src/stream.c index fb39601e9..494643df1 100644 --- a/src/stream.c +++ b/src/stream.c @@ -387,6 +387,9 @@ static void stream_format_changed(void *data, const struct spa_pod *format) for (i = 0; i < info.info.raw.channels; i++) s->channel_map.map[i] = channel_id2pa(s, info.info.raw.position[i]); + if (!pa_channel_map_valid(&s->channel_map)) + pa_channel_map_init_auto(&s->channel_map, info.info.raw.channels, PA_CHANNEL_MAP_DEFAULT); + if (s->format) pa_format_info_free(s->format); s->format = pa_format_info_from_sample_spec(&s->sample_spec, &s->channel_map); From 520a9831e70a0e1db6374a403f2b2cc2e17e7a1c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 21 Sep 2018 16:46:51 +0200 Subject: [PATCH 027/116] stream: fix connect by name Look up the node id for the given device name and use that to connect. --- src/internal.h | 1 + src/introspect.c | 5 +++-- src/stream.c | 9 ++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/internal.h b/src/internal.h index 7bdd5d1d0..bb2483ae6 100644 --- a/src/internal.h +++ b/src/internal.h @@ -261,6 +261,7 @@ struct pa_context { }; struct global *pa_context_find_global(pa_context *c, uint32_t id); +struct global *pa_context_find_global_by_name(pa_context *c, uint32_t mask, const char *name); #define MAX_BUFFERS 64 #define MASK_BUFFERS (MAX_BUFFERS-1) diff --git a/src/introspect.c b/src/introspect.c index fffd689c5..876c9d240 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -157,6 +157,7 @@ static void sink_callback(struct sink_data *d) ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); ip[0] = ii; + pa_channel_map_init_auto(&i.channel_map, 2, PA_CHANNEL_MAP_DEFAULT); i.formats = ip; d->cb(d->context, &i, 0, d->userdata); } @@ -169,7 +170,7 @@ static void sink_info(pa_operation *o, void *userdata) pa_operation_done(o); } -static struct global *find_type_by_name(pa_context *c, uint32_t mask, const char *name) +struct global *pa_context_find_global_by_name(pa_context *c, uint32_t mask, const char *name) { struct global *g; const char *str; @@ -200,7 +201,7 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); - if ((g = find_type_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) + if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) return NULL; ensure_global(c, g); diff --git a/src/stream.c b/src/stream.c index 494643df1..471a06a5d 100644 --- a/src/stream.c +++ b/src/stream.c @@ -178,7 +178,8 @@ static void dump_buffer_attr(pa_stream *s, pa_buffer_attr *attr) static void configure_buffers(pa_stream *s) { s->buffer_attr.maxlength = s->maxsize; - s->buffer_attr.prebuf = s->buffer_attr.minreq; + if (s->buffer_attr.prebuf == -1) + s->buffer_attr.prebuf = s->buffer_attr.minreq; s->buffer_attr.fragsize = s->buffer_attr.minreq; dump_buffer_attr(s, &s->buffer_attr); } @@ -772,6 +773,8 @@ static int create_stream(pa_stream_direction_t direction, struct pw_properties *props; uint32_t sample_rate = 0, stride = 0; const char *str; + char devid[16]; + struct global *g; spa_assert(s); spa_assert(s->refcount >= 1); @@ -831,6 +834,10 @@ static int create_stream(pa_stream_direction_t direction, if (dev == NULL) dev = getenv("PIPEWIRE_NODE"); + else if ((g = pa_context_find_global_by_name(s->context, PA_SUBSCRIPTION_MASK_SINK, dev)) != NULL) { + snprintf(devid, 15, "%d", g->id); + dev = devid; + } props = (struct pw_properties *) pw_stream_get_properties(s->stream); pw_properties_setf(props, "node.latency", "%u/%u", From 6a4da8a6b31e5ed2e455608d853de159f470ba2e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 3 Oct 2018 20:14:34 +0200 Subject: [PATCH 028/116] context: improve properties --- src/context.c | 1 - src/proplist.c | 3 +-- src/stream.c | 18 ++++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/context.c b/src/context.c index f18fb6eab..b4bc10afe 100644 --- a/src/context.c +++ b/src/context.c @@ -325,7 +325,6 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * if (r == NULL) return NULL; - c = pw_remote_get_user_data(r); c->loop = loop; c->core = core; diff --git a/src/proplist.c b/src/proplist.c index 4ed222c3b..4cbc81211 100644 --- a/src/proplist.c +++ b/src/proplist.c @@ -164,8 +164,7 @@ const char *pa_proplist_iterate(pa_proplist *p, void **state) char *pa_proplist_to_string(pa_proplist *p) { spa_assert(p); - pw_log_warn("Not Implemented"); - return NULL; + return pa_proplist_to_string_sep(p, ","); } char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) diff --git a/src/stream.c b/src/stream.c index 471a06a5d..22da3bac5 100644 --- a/src/stream.c +++ b/src/stream.c @@ -504,6 +504,7 @@ pa_stream* stream_new(pa_context *c, const char *name, pa_stream *s; char str[1024]; int i; + struct pw_properties *props; spa_assert(c); spa_assert(c->refcount >= 1); @@ -517,11 +518,16 @@ pa_stream* stream_new(pa_context *c, const char *name, if (s == NULL) return NULL; + s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new(); + if (name) + pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name); + else + name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME); - s->stream = pw_stream_new(c->remote, name, - pw_properties_new( - "client.api", "pulseaudio", - NULL)); + props = pw_properties_new("client.api", "pulseaudio", + NULL); + + s->stream = pw_stream_new(c->remote, name, props); s->refcount = 1; s->context = c; spa_list_init(&s->pending); @@ -554,10 +560,6 @@ pa_stream* stream_new(pa_context *c, const char *name, s->direct_on_input = PA_INVALID_INDEX; - s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new(); - if (name) - pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name); - s->stream_index = PA_INVALID_INDEX; s->buffer_attr.maxlength = (uint32_t) -1; From a82a5dc26a1c9686341d8f42ea6d5dcdacf0aa7c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 9 Oct 2018 16:36:45 +0200 Subject: [PATCH 029/116] context: make some errors less verbose, like libpulse --- src/context.c | 2 +- src/internal.h | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/context.c b/src/context.c index b4bc10afe..3eedcfca5 100644 --- a/src/context.c +++ b/src/context.c @@ -33,7 +33,7 @@ int pa_context_set_error(pa_context *c, int error) { pa_assert(error >= 0); pa_assert(error < PA_ERR_MAX); - pw_log_error("context %p: error %d", c, error); + pw_log_debug("context %p: error %d", c, error); if (c) c->error = error; return error; diff --git a/src/internal.h b/src/internal.h index bb2483ae6..f7f5b3741 100644 --- a/src/internal.h +++ b/src/internal.h @@ -110,8 +110,6 @@ int pa_context_set_error(pa_context *c, int error); #define PA_CHECK_VALIDITY(context, expression, error) \ do { \ if (!(expression)) { \ - fprintf(stderr, "'%s' failed at %s:%u %s()", \ - #expression , __FILE__, __LINE__, __func__); \ return -pa_context_set_error((context), (error)); \ } \ } while(false) @@ -119,8 +117,6 @@ do { \ #define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) \ do { \ if (!(expression)) { \ - fprintf(stderr, "'%s' failed at %s:%u %s()", \ - #expression , __FILE__, __LINE__, __func__); \ pa_context_set_error((context), (error)); \ return value; \ } \ From 1509b51966293de9d033208337814fbc8b8a4f6b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 9 Oct 2018 16:37:23 +0200 Subject: [PATCH 030/116] stream: handle channelmap in _new_extended --- src/stream.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/stream.c b/src/stream.c index 22da3bac5..ace3c65b7 100644 --- a/src/stream.c +++ b/src/stream.c @@ -599,6 +599,11 @@ pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * pa_stream* pa_stream_new_with_proplist(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_proplist *p) { + pa_channel_map tmap; + + if (!map) + PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID); + return stream_new(c, name, ss, map, NULL, 0, p); } @@ -808,17 +813,18 @@ static int create_stream(pa_stream_direction_t direction, } else { pa_sample_spec ss; + pa_channel_map chmap; int i; for (i = 0; i < s->n_formats; i++) { - if ((res = pa_format_info_to_sample_spec(s->req_formats[i], &ss, NULL)) < 0) { + if ((res = pa_format_info_to_sample_spec(s->req_formats[i], &ss, &chmap)) < 0) { char buf[4096]; pw_log_warn("can't convert format %d %s", res, pa_format_info_snprint(buf,4096,s->req_formats[i])); continue; } - params[n_params++] = get_param(s, &ss, NULL, &b); + params[n_params++] = get_param(s, &ss, &chmap, &b); if (ss.rate > sample_rate) { sample_rate = ss.rate; stride = pa_frame_size(&ss); From 0870fc8673f7e7815592c4dd7d946d16e46dbc66 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 9 Oct 2018 16:37:49 +0200 Subject: [PATCH 031/116] stream: use latency of minreq/2 Because we don't buffer tlength on the server side, we need to use half of the minreq as headroom in the server. --- src/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream.c b/src/stream.c index ace3c65b7..0708e617e 100644 --- a/src/stream.c +++ b/src/stream.c @@ -849,7 +849,7 @@ static int create_stream(pa_stream_direction_t direction, props = (struct pw_properties *) pw_stream_get_properties(s->stream); pw_properties_setf(props, "node.latency", "%u/%u", - s->buffer_attr.minreq / stride, sample_rate); + (s->buffer_attr.minreq / 2) / stride, sample_rate); pw_properties_set(props, PW_NODE_PROP_MEDIA, "Audio"); pw_properties_set(props, PW_NODE_PROP_CATEGORY, direction == PA_STREAM_PLAYBACK ? From a91dea00d0b50e759c8a7421346961a2cc27a67b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 9 Oct 2018 16:39:19 +0200 Subject: [PATCH 032/116] stream: implement cork and flush Implement cork with _set_active and flush with the new _flush method. --- src/stream.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/stream.c b/src/stream.c index 0708e617e..3472c46fe 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1383,7 +1383,7 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi s->corked = b; - pw_log_warn("Not Implemented %d", b); + pw_stream_set_active(s->stream, !b); o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); d = o->userdata; d->cb = cb; @@ -1404,7 +1404,8 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - pw_log_warn("Not Implemented"); + pw_stream_flush(s->stream, false); + update_timing_info(s); o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); d = o->userdata; d->cb = cb; From 9d2cf17516abeaf588bbb810c8c451e9b0a62fee Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 9 Oct 2018 17:30:37 +0200 Subject: [PATCH 033/116] bitset: init and clear bitset correctly --- src/bitset.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bitset.c b/src/bitset.c index 6f3247c4f..be834f5eb 100644 --- a/src/bitset.c +++ b/src/bitset.c @@ -45,7 +45,8 @@ bool pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...) { pa_bitset_t *a; bool equal; - a = alloca(PA_BITSET_ELEMENTS(n)); + a = alloca(PA_BITSET_SIZE(n)); + spa_memzero(a, PA_BITSET_SIZE(n)); va_start(ap, n); for (;;) { From a00c00baed6984b07e396cacfeba14c09e43102e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 9 Oct 2018 17:31:06 +0200 Subject: [PATCH 034/116] stream: maxlength / size is the number of buffers we want tlength is what the server buffers and is of no importance for deciding the number of buffers. --- src/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream.c b/src/stream.c index 3472c46fe..97df07eb9 100644 --- a/src/stream.c +++ b/src/stream.c @@ -283,7 +283,7 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att if (attr->maxlength == -1) buffers = 3; else - buffers = SPA_CLAMP(attr->maxlength / (maxsize * stride), 3, MAX_BUFFERS); + buffers = SPA_CLAMP(attr->maxlength / (size * stride), 3, MAX_BUFFERS); pw_log_info("stream %p: stride %d maxsize %d size %u buffers %d", s, stride, maxsize, size, buffers); From f74a0fb9bde9e23be07e5f38741aa75abb094d78 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 16 Oct 2018 09:56:12 +0200 Subject: [PATCH 035/116] stream: implement _set_name --- src/stream.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/stream.c b/src/stream.c index 97df07eb9..00f51f460 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1463,6 +1463,8 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe { pa_operation *o; struct success_ack *d; + struct spa_dict dict; + struct spa_dict_item items[1]; spa_assert(s); spa_assert(s->refcount >= 1); @@ -1471,7 +1473,10 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - pw_log_warn("Not Implemented"); + items[0] = SPA_DICT_ITEM_INIT("media.name", name); + dict = SPA_DICT_INIT(items, 1); + pw_stream_update_properties(s->stream, &dict); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); d = o->userdata; d->cb = cb; From b742d7533e99117e06712dfd62802e60452c0cde Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 16 Oct 2018 09:58:14 +0200 Subject: [PATCH 036/116] stream: only queue buffer when filled We need to completely fill the buffer before we queue the buffer. If we don't do this, we might have reported a larger writable size than what we actually can do and that makes clients fail. This is not entirely completely correct because a client can expect to have its buffer sent as soon as it completes the write. We should really just queue the data in a separate queue when we run out of buffers. --- src/stream.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/stream.c b/src/stream.c index 00f51f460..54e7e4f85 100644 --- a/src/stream.c +++ b/src/stream.c @@ -953,7 +953,6 @@ int peek_buffer(pa_stream *s) } else { s->buffer_size = s->buffer->buffer->datas[0].maxsize; - s->buffer_offset = 0; } return 0; } @@ -975,6 +974,7 @@ int queue_buffer(pa_stream *s) pw_stream_queue_buffer(s->stream, s->buffer); s->buffer = NULL; + s->buffer_offset = 0; return 0; } @@ -1070,21 +1070,26 @@ int pa_stream_write_ext_free(pa_stream *s, if (pa_stream_begin_write(s, &dst, &dsize) < 0 || dst == NULL || dsize == 0) { - pw_log_debug("out of buffers"); + pw_log_debug("stream %p: out of buffers, wanted %zd bytes", s, nbytes); break; } memcpy(dst, src, dsize); - s->buffer->buffer->datas[0].chunk->offset = 0; - s->buffer->buffer->datas[0].chunk->size = dsize; - queue_buffer(s); + s->buffer_offset += dsize; + if (s->buffer_offset >= s->buffer_size) { + s->buffer->buffer->datas[0].chunk->offset = 0; + s->buffer->buffer->datas[0].chunk->size = s->buffer_offset; + queue_buffer(s); + } towrite -= dsize; src += dsize; } if (free_cb) free_cb(free_cb_data); + + s->buffer = NULL; } else { s->buffer->buffer->datas[0].chunk->offset = data - s->buffer_data; @@ -1147,6 +1152,7 @@ size_t pa_stream_writable_size(pa_stream *s) PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1); + pw_log_trace("stream %p: %zd", s, s->dequeued_size); return s->dequeued_size; } From 5498d9c72671930b63578be94567ab9c66a857f7 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 19 Oct 2018 13:27:58 +0200 Subject: [PATCH 037/116] glib-mainloop: implement glib mainloop support --- src/mainloop-glib.c | 112 ++++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 14 ++++++ 2 files changed, 126 insertions(+) create mode 100644 src/mainloop-glib.c diff --git a/src/mainloop-glib.c b/src/mainloop-glib.c new file mode 100644 index 000000000..136ac81c8 --- /dev/null +++ b/src/mainloop-glib.c @@ -0,0 +1,112 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include + +#include +#include + +#include "internal.h" + +struct source { + GSource base; + struct pw_loop *loop; +}; + +struct pa_glib_mainloop { + GMainContext *context; + pa_mainloop *loop; + struct source *source; + guint id; +}; + +static gboolean source_prepare (GSource *base, int *timeout) +{ + *timeout = -1; + return FALSE; +} + +static gboolean source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) +{ + struct source *s = (struct source *) source; + int result; + + pw_loop_enter (s->loop); + result = pw_loop_iterate (s->loop, 0); + pw_loop_leave (s->loop); + + if (result < 0) + g_warning ("pipewire_loop_iterate failed: %s", spa_strerror (result)); + + return TRUE; +} + +static GSourceFuncs source_funcs = +{ + source_prepare, + NULL, + source_dispatch, + NULL +}; + +pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) +{ + + pa_glib_mainloop *loop; + + loop = calloc(1, sizeof(pa_glib_mainloop)); + if (loop == NULL) + goto error; + + loop->context = c; + loop->loop = pa_mainloop_new(); + if (loop->loop == NULL) + goto error_free; + + loop->source = (struct source *) g_source_new(&source_funcs, sizeof(struct source)); + loop->source->loop = loop->loop->loop; + + g_source_add_unix_fd (&loop->source->base, + pw_loop_get_fd(loop->source->loop), + G_IO_IN | G_IO_ERR); + + loop->id = g_source_attach (&loop->source->base, loop->context); + + return loop; + + error_free: + free(loop); + error: + return NULL; + +} + +void pa_glib_mainloop_free(pa_glib_mainloop* g) +{ + pa_mainloop_free(g->loop); + free(g); +} + +pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) +{ + return pa_mainloop_get_api(g->loop); +} diff --git a/src/meson.build b/src/meson.build index 862c8670f..88ae3a5e2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -31,6 +31,10 @@ pipewire_pulseaudio_sources = [ 'pipewire-pulseaudio.c', ] +pipewire_mainloop_glib_sources = [ + 'mainloop-glib.c', +] + pipewire_pulseaudio_c_args = [ '-DHAVE_CONFIG_H', '-DPIC', @@ -48,3 +52,13 @@ pipewire_pulseaudio = shared_library('pulse', dependencies : [pipewire_dep, pulseaudio_dep, mathlib], install : false, ) + +pipewire_pulseaudio = shared_library('pulse-mainloop-glib', + pipewire_mainloop_glib_sources, + soversion : '0', + c_args : pipewire_pulseaudio_c_args, + link_args : vflag, + include_directories : [configinc], + dependencies : [pipewire_dep, pulseaudio_dep, mathlib, glib_dep], + install : false, +) From faccc8d506b4bbb2212ab796dfbba0887b1f5202 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 19 Oct 2018 13:30:20 +0200 Subject: [PATCH 038/116] implement more methods Implement extensions with dummies Implement some more introspection, enough to make pavucontrol start Implement volume changes on sink_input --- src/context.c | 30 ++-- src/ext-device-restore.c | 124 ++++++++++++-- src/ext-stream-restore.c | 131 +++++++++++++-- src/introspect.c | 338 +++++++++++++++++++++++++++++++++++---- src/stream.c | 10 +- 5 files changed, 559 insertions(+), 74 deletions(-) diff --git a/src/context.c b/src/context.c index 3eedcfca5..e9d84245c 100644 --- a/src/context.c +++ b/src/context.c @@ -100,7 +100,8 @@ static int set_mask(pa_context *c, struct global *g) { const char *str; - if (g->type == PW_TYPE_INTERFACE_Node) { + switch (g->type) { + case PW_TYPE_INTERFACE_Node: if (g->props == NULL) return 0; if ((str = pw_properties_get(g->props, "media.class")) == NULL) @@ -136,19 +137,23 @@ static int set_mask(pa_context *c, struct global *g) g->mask = PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT; g->event = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT; } - } - else if (g->type == PW_TYPE_INTERFACE_Module) { + break; + + case PW_TYPE_INTERFACE_Module: g->mask = PA_SUBSCRIPTION_MASK_MODULE; g->event = PA_SUBSCRIPTION_EVENT_MODULE; - } - else if (g->type == PW_TYPE_INTERFACE_Client) { + break; + + case PW_TYPE_INTERFACE_Client: g->mask = PA_SUBSCRIPTION_MASK_CLIENT; g->event = PA_SUBSCRIPTION_EVENT_CLIENT; - } - else if (g->type == PW_TYPE_INTERFACE_Port) { + break; + + case PW_TYPE_INTERFACE_Port: pw_log_debug("found port %d", g->id); - } - else if (g->type == PW_TYPE_INTERFACE_Link) { + break; + + case PW_TYPE_INTERFACE_Link: if ((str = pw_properties_get(g->props, "link.output")) == NULL) return 0; g->link_info.src = pa_context_find_global(c, pw_properties_parse_int(str)); @@ -161,10 +166,11 @@ static int set_mask(pa_context *c, struct global *g) pw_log_debug("link %d->%d", g->link_info.src->parent_id, g->link_info.dst->parent_id); - } - else - return 0; + break; + default: + return 0; + } return 1; } diff --git a/src/ext-device-restore.c b/src/ext-device-restore.c index 2f7977559..b2c67da04 100644 --- a/src/ext-device-restore.c +++ b/src/ext-device-restore.c @@ -23,13 +23,53 @@ #include "internal.h" +#define EXT_VERSION 1 + +struct ext_data { + pa_context *context; + pa_ext_device_restore_test_cb_t test_cb; + pa_ext_device_restore_read_device_formats_cb_t read_cb; + pa_context_success_cb_t success_cb; + void *userdata; +}; + +static void restore_test(pa_operation *o, void *userdata) +{ + struct ext_data *d = userdata; + if (d->test_cb) + d->test_cb(o->context, EXT_VERSION, d->userdata); + pa_operation_done(o); +} + pa_operation *pa_ext_device_restore_test( pa_context *c, pa_ext_device_restore_test_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct ext_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, restore_test, sizeof(struct ext_data)); + d = o->userdata; + d->context = c; + d->test_cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; +} + +static void on_success(pa_operation *o, void *userdata) +{ + struct ext_data *d = userdata; + if (d->success_cb) + d->success_cb(o->context, PA_OK, d->userdata); + pa_operation_done(o); } pa_operation *pa_ext_device_restore_subscribe( @@ -38,8 +78,22 @@ pa_operation *pa_ext_device_restore_subscribe( pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct ext_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct ext_data)); + d = o->userdata; + d->context = c; + d->success_cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } void pa_ext_device_restore_set_subscribe_cb( @@ -50,13 +104,35 @@ void pa_ext_device_restore_set_subscribe_cb( pw_log_warn("Not Implemented"); } +static void read_formats(pa_operation *o, void *userdata) +{ + struct ext_data *d = userdata; + if (d->read_cb) + d->read_cb(o->context, NULL, 1, d->userdata); + pa_operation_done(o); +} + pa_operation *pa_ext_device_restore_read_formats_all( pa_context *c, pa_ext_device_restore_read_device_formats_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct ext_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, read_formats, sizeof(struct ext_data)); + d = o->userdata; + d->context = c; + d->read_cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } pa_operation *pa_ext_device_restore_read_formats( @@ -66,8 +142,22 @@ pa_operation *pa_ext_device_restore_read_formats( pa_ext_device_restore_read_device_formats_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct ext_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, read_formats, sizeof(struct ext_data)); + d = o->userdata; + d->context = c; + d->read_cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } pa_operation *pa_ext_device_restore_save_formats( @@ -79,6 +169,20 @@ pa_operation *pa_ext_device_restore_save_formats( pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct ext_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct ext_data)); + d = o->userdata; + d->context = c; + d->success_cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } diff --git a/src/ext-stream-restore.c b/src/ext-stream-restore.c index 039c6bfb6..bd9e2f95e 100644 --- a/src/ext-stream-restore.c +++ b/src/ext-stream-restore.c @@ -23,27 +23,90 @@ #include "internal.h" -/** Test if this extension module is available in the server. \since 0.9.12 */ +#define EXT_VERSION 1 + +struct stream_data { + pa_context *context; + pa_ext_stream_restore_test_cb_t test_cb; + pa_ext_stream_restore_read_cb_t read_cb; + pa_context_success_cb_t success_cb; + void *userdata; +}; + +static void restore_test(pa_operation *o, void *userdata) +{ + struct stream_data *d = userdata; + + if (d->test_cb) + d->test_cb(o->context, EXT_VERSION, d->userdata); + + pa_operation_done(o); +} + pa_operation *pa_ext_stream_restore_test( pa_context *c, pa_ext_stream_restore_test_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct stream_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, restore_test, sizeof(struct stream_data)); + d = o->userdata; + d->context = c; + d->test_cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; +} + +static void restore_read(pa_operation *o, void *userdata) +{ + struct stream_data *d = userdata; + + if (d->read_cb) + d->read_cb(o->context, NULL, 1, d->userdata); + + pa_operation_done(o); } -/** Read all entries from the stream database. \since 0.9.12 */ pa_operation *pa_ext_stream_restore_read( pa_context *c, pa_ext_stream_restore_read_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct stream_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, restore_read, sizeof(struct stream_data)); + d = o->userdata; + d->context = c; + d->read_cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; +} + +static void on_success(pa_operation *o, void *userdata) +{ + struct stream_data *d = userdata; + if (d->success_cb) + d->success_cb(o->context, PA_OK, d->userdata); + pa_operation_done(o); } -/** Store entries in the stream database. \since 0.9.12 */ pa_operation *pa_ext_stream_restore_write( pa_context *c, pa_update_mode_t mode, @@ -53,8 +116,22 @@ pa_operation *pa_ext_stream_restore_write( pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct stream_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct stream_data)); + d = o->userdata; + d->context = c; + d->success_cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } /** Delete entries from the stream database. \since 0.9.12 */ @@ -64,8 +141,22 @@ pa_operation *pa_ext_stream_restore_delete( pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct stream_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct stream_data)); + d = o->userdata; + d->context = c; + d->success_cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } /** Subscribe to changes in the stream database. \since 0.9.12 */ @@ -75,8 +166,22 @@ pa_operation *pa_ext_stream_restore_subscribe( pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct stream_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct stream_data)); + d = o->userdata; + d->context = c; + d->success_cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } /** Set the subscription callback that is called when diff --git a/src/introspect.c b/src/introspect.c index 876c9d240..81687f3ed 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -19,6 +19,8 @@ #include +#include + #include #include @@ -33,9 +35,18 @@ static void node_event_info(void *object, struct pw_node_info *info) g->info = pw_node_info_update(g->info, info); } +static void node_event_param(void *object, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct global *g = object; + pw_log_debug("update param %d", g->id); +} + static const struct pw_node_proxy_events node_events = { PW_VERSION_NODE_PROXY_EVENTS, .info = node_event_info, + .param = node_event_param, }; static void module_event_info(void *object, struct pw_module_info *info) @@ -71,23 +82,25 @@ static int ensure_global(pa_context *c, struct global *g) if (g->proxy != NULL) return 0; - if (g->type == PW_TYPE_INTERFACE_Node) { + switch (g->type) { + case PW_TYPE_INTERFACE_Node: events = &node_events; client_version = PW_VERSION_NODE; destroy = (pw_destroy_t) pw_node_info_free; - } - else if (g->type == PW_TYPE_INTERFACE_Module) { + break; + case PW_TYPE_INTERFACE_Module: events = &module_events; client_version = PW_VERSION_MODULE; destroy = (pw_destroy_t) pw_module_info_free; - } - else if (g->type == PW_TYPE_INTERFACE_Client) { + break; + case PW_TYPE_INTERFACE_Client: events = &client_events; client_version = PW_VERSION_CLIENT; destroy = (pw_destroy_t) pw_client_info_free; - } - else + break; + default: return -EINVAL; + } pw_log_debug("bind %d", g->id); @@ -99,6 +112,15 @@ static int ensure_global(pa_context *c, struct global *g) pw_proxy_add_proxy_listener(g->proxy, &g->proxy_proxy_listener, events, g); g->destroy = destroy; + switch (g->type) { + case PW_TYPE_INTERFACE_Node: + pw_node_proxy_enum_params((struct pw_node_proxy*)g->proxy, + SPA_PARAM_EnumFormat, + 0, -1, NULL); + break; + default: + break; + } return 0; } @@ -145,19 +167,34 @@ static void sink_callback(struct sink_data *d) pa_format_info *ip[1]; spa_zero(i); - i.index = g->id; i.name = info->name; + i.index = g->id; i.description = info->name; - i.proplist = pa_proplist_new_dict(info->props); + i.sample_spec.format = PA_SAMPLE_S16LE; + i.sample_spec.rate = 44100; + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, 2, PA_CHANNEL_MAP_DEFAULT); i.owner_module = g->parent_id; + pa_cvolume_set(&i.volume, 2, PA_VOLUME_NORM); + i.mute = false; + i.monitor_source = PA_INVALID_INDEX; + i.monitor_source_name = "unknown"; + i.latency = 0; + i.driver = "PipeWire"; + i.flags = 0; + i.proplist = pa_proplist_new_dict(info->props); + i.configured_latency = 0; i.base_volume = PA_VOLUME_NORM; i.state = node_state_to_sink(info->state); i.n_volume_steps = PA_VOLUME_NORM+1; + i.card = PA_INVALID_INDEX; + i.n_ports = 0; + i.ports = NULL; + i.active_port = NULL; i.n_formats = 1; ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); ip[0] = ii; - pa_channel_map_init_auto(&i.channel_map, 2, PA_CHANNEL_MAP_DEFAULT); i.formats = ip; d->cb(d->context, &i, 0, d->userdata); } @@ -366,14 +403,30 @@ static void source_callback(struct source_data *d) pa_format_info *ip[1]; spa_zero(i); - i.index = g->id; i.name = info->name; + i.index = g->id; i.description = info->name; - i.proplist = pa_proplist_new_dict(info->props); + i.sample_spec.format = PA_SAMPLE_S16LE; + i.sample_spec.rate = 44100; + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, 2, PA_CHANNEL_MAP_DEFAULT); i.owner_module = g->parent_id; + pa_cvolume_set(&i.volume, 2, PA_VOLUME_NORM); + i.mute = false; + i.monitor_of_sink = PA_INVALID_INDEX; + i.monitor_of_sink_name = "unknown"; + i.latency = 0; + i.driver = "PipeWire"; + i.flags = 0; + i.proplist = pa_proplist_new_dict(info->props); + i.configured_latency = 0; i.base_volume = PA_VOLUME_NORM; i.state = node_state_to_source(info->state); i.n_volume_steps = PA_VOLUME_NORM+1; + i.card = PA_INVALID_INDEX; + i.n_ports = 0; + i.ports = NULL; + i.active_port = NULL; i.n_formats = 1; ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); @@ -511,10 +564,58 @@ pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char*name, return NULL; } +struct server_data { + pa_context *context; + pa_server_info_cb_t cb; + void *userdata; + struct global *global; +}; + +static void server_callback(struct server_data *d) +{ + pa_context *c = d->context; + const struct pw_core_info *info = pw_remote_get_core_info(c->remote); + pa_server_info i; + + spa_zero(i); + i.user_name = info->user_name; + i.host_name = info->host_name; + i.server_version = info->version; + i.server_name = info->name; + i.sample_spec.format = PA_SAMPLE_S16LE; + i.sample_spec.rate = 44100; + i.sample_spec.channels = 2; + i.default_sink_name = "unknown"; + i.default_source_name = "unknown"; + i.cookie = info->cookie; + pa_channel_map_init_extend(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + d->cb(d->context, &i, d->userdata); +} + +static void server_info(pa_operation *o, void *userdata) +{ + struct server_data *d = userdata; + server_callback(d); + pa_operation_done(o); +} + pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct server_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + o = pa_operation_new(c, NULL, server_info, sizeof(struct server_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } struct module_data { @@ -746,10 +847,36 @@ pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, return NULL; } +struct card_data { + pa_context *context; + pa_card_info_cb_t cb; + void *userdata; +}; + +static void card_info(pa_operation *o, void *userdata) +{ + struct card_data *d = userdata; + d->cb(d->context, NULL, 1, d->userdata); + pa_operation_done(o); +} + pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct card_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + o = pa_operation_new(c, NULL, card_info, sizeof(struct card_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata) @@ -770,13 +897,6 @@ pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card return NULL; } -struct sink_input_data { - pa_context *context; - pa_sink_input_info_cb_t cb; - void *userdata; - struct global *global; -}; - static pa_stream *find_stream(pa_context *c, uint32_t idx) { pa_stream *s; @@ -787,6 +907,13 @@ static pa_stream *find_stream(pa_context *c, uint32_t idx) return NULL; } +struct sink_input_data { + pa_context *context; + pa_sink_input_info_cb_t cb; + void *userdata; + struct global *global; +}; + static void sink_input_callback(struct sink_input_data *d) { struct global *g = d->global; @@ -805,15 +932,18 @@ static void sink_input_callback(struct sink_input_data *d) i.client = PA_INVALID_INDEX; i.sink = PA_INVALID_INDEX; pa_cvolume_init(&i.volume); - if (s) { + if (s && s->sample_spec.channels > 0) { i.sample_spec = s->sample_spec; i.channel_map = s->channel_map; - pa_cvolume_set(&i.volume, 1, s->volume * PA_VOLUME_NORM); + pa_cvolume_set(&i.volume, i.sample_spec.channels, s->volume * PA_VOLUME_NORM); i.format = s->format; } else { - pa_channel_map_init(&i.channel_map); - pa_sample_spec_init(&i.sample_spec); + i.sample_spec.format = PA_SAMPLE_S16LE; + i.sample_spec.rate = 44100; + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + pa_cvolume_set(&i.volume, i.sample_spec.channels, PA_VOLUME_NORM); ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); i.format = ii; @@ -933,15 +1063,36 @@ static void on_success(pa_operation *o, void *userdata) pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_stream *s; + struct global *g; pa_operation *o; struct success_ack *d; + float v; - if ((s = find_stream(c, idx)) == NULL) - return NULL; + v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->volume); + pw_log_debug("contex %p: index %d volume %f", c, idx, v); + if ((s = find_stream(c, idx)) == NULL) { + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + } + + if (s) { + s->volume = v; + pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->volume); + } + else if (g) { + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + + pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_volume, &SPA_POD_Float(v), + SPA_PROP_mute, &SPA_POD_Bool(false), + 0)); + } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; d->cb = cb; @@ -980,16 +1131,133 @@ pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context return NULL; } +struct source_output_data { + pa_context *context; + pa_source_output_info_cb_t cb; + void *userdata; + struct global *global; +}; + +static void source_output_callback(struct source_output_data *d) +{ + struct global *g = d->global; + struct pw_node_info *info = g->info; + pa_source_output_info i; + pa_format_info ii[1]; + pa_stream *s; + + pw_log_debug("index %d", g->id); + s = find_stream(d->context, g->id); + + spa_zero(i); + i.index = g->id; + i.name = info->name; + i.owner_module = g->parent_id; + i.client = PA_INVALID_INDEX; + i.source = PA_INVALID_INDEX; + pa_cvolume_init(&i.volume); + if (s && s->sample_spec.channels > 0) { + i.sample_spec = s->sample_spec; + i.channel_map = s->channel_map; + pa_cvolume_set(&i.volume, i.sample_spec.channels, s->volume * PA_VOLUME_NORM); + i.format = s->format; + } + else { + i.sample_spec.format = PA_SAMPLE_S16LE; + i.sample_spec.rate = 44100; + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + pa_cvolume_set(&i.volume, i.sample_spec.channels, PA_VOLUME_NORM); + ii[0].encoding = PA_ENCODING_PCM; + ii[0].plist = pa_proplist_new(); + i.format = ii; + } + i.buffer_usec = 0; + i.source_usec = 0; + i.resample_method = "PipeWire resampler"; + i.driver = "PipeWire"; + i.mute = false; + i.proplist = pa_proplist_new_dict(info->props); + i.corked = false; + i.has_volume = true; + i.volume_writable = true; + d->cb(d->context, &i, 0, d->userdata); +} + +static void source_output_info(pa_operation *o, void *userdata) +{ + struct source_output_data *d = userdata; + source_output_callback(d); + d->cb(d->context, NULL, 1, d->userdata); + pa_operation_done(o); +} + pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct source_output_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, source_output_info, sizeof(struct source_output_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + d->global = g; + pa_operation_sync(o); + + return o; +} + +static void source_output_info_list(pa_operation *o, void *userdata) +{ + struct source_output_data *d = userdata; + pa_context *c = d->context; + struct global *g; + + spa_list_for_each(g, &c->globals, link) { + if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)) + continue; + d->global = g; + source_output_callback(d); + } + d->cb(c, NULL, 1, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct source_output_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + ensure_types(c, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT); + o = pa_operation_new(c, NULL, source_output_info_list, sizeof(struct source_output_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata) diff --git a/src/stream.c b/src/stream.c index 54e7e4f85..a8ba7ccfc 100644 --- a/src/stream.c +++ b/src/stream.c @@ -270,13 +270,13 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att blocks = 1; stride = pa_frame_size(&s->sample_spec); - if (attr->tlength == -1) + if (attr->tlength == -1 || attr->tlength == 0) maxsize = 1024; else maxsize = (attr->tlength / stride); - if (attr->minreq == -1) - size = SPA_MIN(1024, maxsize); + if (attr->minreq == -1 || attr->minreq == 0) + size = maxsize; else size = SPA_MIN(attr->minreq / stride, maxsize); @@ -1121,7 +1121,7 @@ int pa_stream_peek(pa_stream *s, return 0; } *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); - *nbytes = s->buffer_size; + *nbytes = SPA_MAX(s->buffer_size, 4); pw_log_trace("stream %p: %p %zd", s, *data, *nbytes); return 0; @@ -1719,6 +1719,8 @@ int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) spa_assert(s); spa_assert(s->refcount >= 1); + pw_log_debug("stream %p: %d", s, sink_input_idx); + PA_CHECK_VALIDITY(s->context, sink_input_idx != PA_INVALID_INDEX, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); From 01aa9a5960545ca21730b2c06685f3b22beab8a1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 19 Oct 2018 16:57:03 +0200 Subject: [PATCH 039/116] introspect: implement enough to make gnome-control-center work We can see sources and sink and change the volume/mute of streams --- src/internal.h | 1 + src/introspect.c | 56 +++++++++++++++++++++++++++++++++--------------- src/proplist.c | 39 +++++++++++++++++++++++++++++---- src/stream.c | 1 + 4 files changed, 76 insertions(+), 21 deletions(-) diff --git a/src/internal.h b/src/internal.h index f7f5b3741..389d6f8be 100644 --- a/src/internal.h +++ b/src/internal.h @@ -336,6 +336,7 @@ struct pa_stream { uint32_t buffer_offset; float volume; + bool mute; pa_operation *drain; uint64_t queued; }; diff --git a/src/introspect.c b/src/introspect.c index 81687f3ed..99bde7cb3 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -923,13 +923,16 @@ static void sink_input_callback(struct sink_input_data *d) pa_stream *s; pw_log_debug("index %d", g->id); + if (info == NULL) + return; + s = find_stream(d->context, g->id); spa_zero(i); i.index = g->id; i.name = info->name; - i.owner_module = g->parent_id; - i.client = PA_INVALID_INDEX; + i.owner_module = PA_INVALID_INDEX; + i.client = g->parent_id; i.sink = PA_INVALID_INDEX; pa_cvolume_init(&i.volume); if (s && s->sample_spec.channels > 0) { @@ -980,6 +983,8 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + pw_log_debug("context %p: info for %d", c, idx); + if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK_INPUT)) @@ -1024,6 +1029,8 @@ pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_i PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + pw_log_debug("context %p", c); + ensure_types(c, PA_SUBSCRIPTION_MASK_SINK_INPUT); o = pa_operation_new(c, NULL, sink_input_info_list, sizeof(struct sink_input_data)); d = o->userdata; @@ -1079,19 +1086,18 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons if (s) { s->volume = v; - pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->volume); + pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); } else if (g) { char buf[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, - SPA_PARAM_Props, 0, - spa_pod_builder_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_volume, &SPA_POD_Float(v), - SPA_PROP_mute, &SPA_POD_Bool(false), - 0)); + SPA_PARAM_Props, 0, + spa_pod_builder_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_volume, &SPA_POD_Float(v), + 0)); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1105,17 +1111,30 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { pa_stream *s; + struct global *g; pa_operation *o; struct success_ack *d; - if ((s = find_stream(c, idx)) == NULL) - return NULL; + if ((s = find_stream(c, idx)) == NULL) { + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + } - if (mute) - pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, 0.0); - else - pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->volume); + if (s) { + s->mute = mute; + pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); + } + else if (g) { + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_mute, &SPA_POD_Bool(mute), + 0)); + } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; d->cb = cb; @@ -1147,13 +1166,16 @@ static void source_output_callback(struct source_output_data *d) pa_stream *s; pw_log_debug("index %d", g->id); + if (info == NULL) + return; + s = find_stream(d->context, g->id); spa_zero(i); i.index = g->id; i.name = info->name; - i.owner_module = g->parent_id; - i.client = PA_INVALID_INDEX; + i.owner_module = PA_INVALID_INDEX; + i.client = g->parent_id; i.source = PA_INVALID_INDEX; pa_cvolume_init(&i.volume); if (s && s->sample_spec.channels > 0) { diff --git a/src/proplist.c b/src/proplist.c index 4cbc81211..3d94b50ef 100644 --- a/src/proplist.c +++ b/src/proplist.c @@ -19,6 +19,8 @@ #include +#include + #include #include @@ -73,14 +75,36 @@ int pa_proplist_key_valid(const char *key) int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) { + pa_assert(p); + pa_assert(key); + pa_assert(value); + + if (!pa_proplist_key_valid(key)) + return -1; + pw_properties_set(p->props, key, value); return 0; } int pa_proplist_setp(pa_proplist *p, const char *pair) { - pw_log_warn("Not Implemented"); - return -1; + const char *t; + char *c; + int idx; + + pa_assert(p); + pa_assert(pair); + + if (!(t = strchr(pair, '='))) + return -1; + + idx = pair - t; + c = strdup(pair); + c[idx] = 0; + pa_properties_sets(p, c, &c[idx]+1); + free(c); + + return 0; } int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) @@ -96,8 +120,15 @@ int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) { - pw_log_warn("Not Implemented"); - return -1; + pa_assert(p); + pa_assert(key); + pa_assert(data || nbytes == 0); + + if (!pa_proplist_key_valid(key)) + return -1; + + pw_properties_set(p->props, key, data); + return 0; } const char *pa_proplist_gets(pa_proplist *p, const char *key) diff --git a/src/stream.c b/src/stream.c index a8ba7ccfc..e0fd0af46 100644 --- a/src/stream.c +++ b/src/stream.c @@ -793,6 +793,7 @@ static int create_stream(pa_stream_direction_t direction, s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; else s->volume = 1.0; + s->mute = false; pa_stream_set_state(s, PA_STREAM_CREATING); From e4c94bf12d2425f37c53a7b4da76cd5db988b89e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 19 Oct 2018 17:00:25 +0200 Subject: [PATCH 040/116] proplist: fix compilation --- src/proplist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proplist.c b/src/proplist.c index 3d94b50ef..457024985 100644 --- a/src/proplist.c +++ b/src/proplist.c @@ -101,7 +101,7 @@ int pa_proplist_setp(pa_proplist *p, const char *pair) idx = pair - t; c = strdup(pair); c[idx] = 0; - pa_properties_sets(p, c, &c[idx]+1); + pa_proplist_sets(p, c, &c[idx]+1); free(c); return 0; From a14e16806f2f0c7e6300256c08761cac0393b461 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 19 Oct 2018 17:15:19 +0200 Subject: [PATCH 041/116] mainloop-glib: destroy the source --- src/mainloop-glib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mainloop-glib.c b/src/mainloop-glib.c index 136ac81c8..2cb0767c9 100644 --- a/src/mainloop-glib.c +++ b/src/mainloop-glib.c @@ -102,6 +102,7 @@ pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) void pa_glib_mainloop_free(pa_glib_mainloop* g) { + g_source_destroy(&g->source->base); pa_mainloop_free(g->loop); free(g); } From 2a1328533f1c4c53721767ac48f5c5b76d4c508f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 23 Oct 2018 16:50:17 +0200 Subject: [PATCH 042/116] stream: use pw_stream_update_properties --- src/stream.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/stream.c b/src/stream.c index e0fd0af46..aa6b7ab2b 100644 --- a/src/stream.c +++ b/src/stream.c @@ -28,6 +28,7 @@ #include #include +#include "core-format.h" #include "internal.h" #define MIN_QUEUED 1 @@ -777,11 +778,12 @@ static int create_stream(pa_stream_direction_t direction, uint32_t n_params = 0; uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - struct pw_properties *props; uint32_t sample_rate = 0, stride = 0; const char *str; char devid[16]; struct global *g; + struct spa_dict_item items[5]; + char latency[64]; spa_assert(s); spa_assert(s->refcount >= 1); @@ -818,12 +820,14 @@ static int create_stream(pa_stream_direction_t direction, int i; for (i = 0; i < s->n_formats; i++) { - if ((res = pa_format_info_to_sample_spec(s->req_formats[i], &ss, &chmap)) < 0) { + if ((res = pa_format_info_to_sample_spec(s->req_formats[i], &ss, NULL)) < 0) { char buf[4096]; pw_log_warn("can't convert format %d %s", res, pa_format_info_snprint(buf,4096,s->req_formats[i])); continue; } + if (pa_format_info_get_channel_map(s->req_formats[i], &chmap) < 0) + pa_channel_map_init_auto(&chmap, ss.channels, PA_CHANNEL_MAP_DEFAULT); params[n_params++] = get_param(s, &ss, &chmap, &b); if (ss.rate > sample_rate) { @@ -848,14 +852,6 @@ static int create_stream(pa_stream_direction_t direction, dev = devid; } - props = (struct pw_properties *) pw_stream_get_properties(s->stream); - pw_properties_setf(props, "node.latency", "%u/%u", - (s->buffer_attr.minreq / 2) / stride, sample_rate); - pw_properties_set(props, PW_NODE_PROP_MEDIA, "Audio"); - pw_properties_set(props, PW_NODE_PROP_CATEGORY, - direction == PA_STREAM_PLAYBACK ? - "Playback" : "Capture"); - if ((str = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_ROLE)) == NULL) str = "Music"; else if (strcmp(str, "video") == 0) @@ -878,7 +874,16 @@ static int create_stream(pa_stream_direction_t direction, str = "Test"; else str = "Music"; - pw_properties_set(props, PW_NODE_PROP_ROLE, str); + + sprintf(latency, "%u/%u", (s->buffer_attr.minreq / 2) / stride, sample_rate); + items[0] = SPA_DICT_ITEM_INIT("node.latency", latency); + items[1] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_MEDIA, "Audio"); + items[2] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_CATEGORY, + direction == PA_STREAM_PLAYBACK ? + "Playback" : "Capture"); + items[3] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_ROLE, str); + + pw_stream_update_properties(s->stream, &SPA_DICT_INIT(items, 4)); res = pw_stream_connect(s->stream, direction == PA_STREAM_PLAYBACK ? @@ -1122,7 +1127,7 @@ int pa_stream_peek(pa_stream *s, return 0; } *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); - *nbytes = SPA_MAX(s->buffer_size, 4); + *nbytes = s->buffer_size; pw_log_trace("stream %p: %p %zd", s, *data, *nbytes); return 0; @@ -1676,6 +1681,7 @@ pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_ { pa_operation *o; struct success_ack *d; + char *str; spa_assert(s); spa_assert(s->refcount >= 1); @@ -1685,7 +1691,10 @@ pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - pw_log_warn("Not Implemented"); + str = pa_proplist_to_string(p); + pw_log_warn("Not Implemented %s", str); + free(str); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); d = o->userdata; d->cb = cb; From ed2f054e76621488bacf56ad60290dc1b05b1396 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 30 Oct 2018 09:50:44 +0000 Subject: [PATCH 043/116] volume: fix for old api --- src/volume.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/volume.c b/src/volume.c index cb99a1857..8e3f382c9 100644 --- a/src/volume.c +++ b/src/volume.c @@ -787,7 +787,11 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) { return v; } +#if PA_CHECK_VERSION(12, 0, 0) pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, const pa_channel_map *cm, pa_channel_position_mask_t mask) { +#else +pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map *cm, pa_channel_position_mask_t mask) { +#endif unsigned c; pa_volume_t t = 0; From a70b02064cdd92e352ff545908c27a2188d245b3 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 2 Nov 2018 12:31:43 +0100 Subject: [PATCH 044/116] stream: fix for target_id --- src/stream.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/stream.c b/src/stream.c index aa6b7ab2b..af448fe55 100644 --- a/src/stream.c +++ b/src/stream.c @@ -780,7 +780,7 @@ static int create_stream(pa_stream_direction_t direction, struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); uint32_t sample_rate = 0, stride = 0; const char *str; - char devid[16]; + uint32_t devid; struct global *g; struct spa_dict_item items[5]; char latency[64]; @@ -845,11 +845,13 @@ static int create_stream(pa_stream_direction_t direction, s->buffer_attr = *attr; patch_buffer_attr(s, &s->buffer_attr, &flags); - if (dev == NULL) - dev = getenv("PIPEWIRE_NODE"); + devid = SPA_ID_INVALID; + if (dev == NULL) { + if ((str = getenv("PIPEWIRE_NODE")) != NULL) + devid = atoi(str); + } else if ((g = pa_context_find_global_by_name(s->context, PA_SUBSCRIPTION_MASK_SINK, dev)) != NULL) { - snprintf(devid, 15, "%d", g->id); - dev = devid; + devid = g->id; } if ((str = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_ROLE)) == NULL) @@ -889,7 +891,7 @@ static int create_stream(pa_stream_direction_t direction, direction == PA_STREAM_PLAYBACK ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT, - dev, + devid, fl, params, n_params); From 88a786fdad0ed7b78071d4d628b5716c1aff1b84 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 7 Nov 2018 09:57:22 +0100 Subject: [PATCH 045/116] stream: use TIMESPEC_TO_USEC --- src/stream.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream.c b/src/stream.c index af448fe55..c30bc2a31 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1515,10 +1515,10 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); clock_gettime(CLOCK_MONOTONIC, &ts); - now = SPA_TIMESPEC_TO_TIME(&ts); + now = SPA_TIMESPEC_TO_USEC(&ts); i = &s->timing_info; - delay = (now - SPA_TIMEVAL_TO_TIME(&i->timestamp)) / SPA_NSEC_PER_USEC; + delay = now - SPA_TIMEVAL_TO_USEC(&i->timestamp); read_time = pa_bytes_to_usec((uint64_t) i->read_index, &s->sample_spec); res = delay + read_time; From e8dfd22a6bd9c784976985b78283734ac2b24e25 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 9 Nov 2018 15:25:39 +0100 Subject: [PATCH 046/116] stream: update for delay sign change --- src/stream.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/stream.c b/src/stream.c index c30bc2a31..ee3aca30d 100644 --- a/src/stream.c +++ b/src/stream.c @@ -422,7 +422,7 @@ static void update_timing_info(pa_stream *s) struct pw_time pwt; pa_timing_info *ti = &s->timing_info; size_t stride = pa_frame_size(&s->sample_spec); - uint64_t delay; + int64_t delay, queued, ticks; pw_stream_get_time(s->stream, &pwt); s->timing_info_valid = false; @@ -439,16 +439,19 @@ static void update_timing_info(pa_stream *s) ti->write_index_corrupt = false; ti->read_index_corrupt = false; + queued = pwt.queued + (pwt.ticks * s->sample_spec.rate / pwt.rate.denom) * stride; + ticks = ((pwt.ticks + pwt.delay) * s->sample_spec.rate / pwt.rate.denom) * stride; + delay = pwt.delay * SPA_USEC_PER_SEC / pwt.rate.denom; if (s->direction == PA_STREAM_PLAYBACK) { - ti->sink_usec = delay; - ti->write_index = pwt.queued + (pwt.ticks * s->sample_spec.rate / pwt.rate.denom) * stride; - ti->read_index = ((pwt.ticks - pwt.delay) * s->sample_spec.rate / pwt.rate.denom) * stride; + ti->sink_usec = -delay; + ti->write_index = queued; + ti->read_index = ticks; } else { ti->source_usec = delay; - ti->read_index = pwt.queued + (pwt.ticks * s->sample_spec.rate / pwt.rate.denom) * stride; - ti->write_index = ((pwt.ticks + pwt.delay) * s->sample_spec.rate / pwt.rate.denom) * stride; + ti->read_index = queued; + ti->write_index = ticks; } ti->configured_sink_usec = 0; From 08d60716933acf35690a088af4f3802c396360b8 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 28 Nov 2018 11:13:21 +0100 Subject: [PATCH 047/116] context: list audio devices as cards Find the sink/source a stream is linked to --- src/context.c | 42 ++++++++++++++++++++++ src/internal.h | 1 + src/introspect.c | 91 +++++++++++++++++++++++++++++++++++++++++++----- src/stream.c | 31 +---------------- 4 files changed, 126 insertions(+), 39 deletions(-) diff --git a/src/context.c b/src/context.c index e9d84245c..299d3ab82 100644 --- a/src/context.c +++ b/src/context.c @@ -96,11 +96,53 @@ struct global *pa_context_find_global(pa_context *c, uint32_t id) return NULL; } +struct global *pa_context_find_linked(pa_context *c, uint32_t idx) +{ + struct global *g, *f; + + spa_list_for_each(g, &c->globals, link) { + if (g->type != PW_TYPE_INTERFACE_Link) + continue; + + pw_log_debug("%d %d %d", idx, + g->link_info.src->parent_id, + g->link_info.dst->parent_id); + + if (g->link_info.src->parent_id == idx) + f = pa_context_find_global(c, g->link_info.dst->parent_id); + else if (g->link_info.dst->parent_id == idx) + f = pa_context_find_global(c, g->link_info.src->parent_id); + else + continue; + + if (f == NULL) + continue; + if (f->mask & PA_SUBSCRIPTION_MASK_DSP) { + f = f->dsp_info.session; + } + return f; + } + return NULL; +} + static int set_mask(pa_context *c, struct global *g) { const char *str; switch (g->type) { + case PW_TYPE_INTERFACE_Device: + if (g->props == NULL) + return 0; + if ((str = pw_properties_get(g->props, "media.class")) == NULL) + return 0; + if (strcmp(str, "Audio/Device") != 0) + return 0; + + pw_log_debug("found card %d", g->id); + g->mask = PA_SUBSCRIPTION_MASK_CARD; + g->event = PA_SUBSCRIPTION_EVENT_CARD; + break; + case PW_TYPE_INTERFACE_Node: if (g->props == NULL) return 0; diff --git a/src/internal.h b/src/internal.h index 389d6f8be..a37c83c8e 100644 --- a/src/internal.h +++ b/src/internal.h @@ -258,6 +258,7 @@ struct pa_context { struct global *pa_context_find_global(pa_context *c, uint32_t id); struct global *pa_context_find_global_by_name(pa_context *c, uint32_t mask, const char *name); +struct global *pa_context_find_linked(pa_context *c, uint32_t id); #define MAX_BUFFERS 64 #define MASK_BUFFERS (MAX_BUFFERS-1) diff --git a/src/introspect.c b/src/introspect.c index 99bde7cb3..512489fd1 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -73,6 +73,18 @@ static const struct pw_client_proxy_events client_events = { .info = client_event_info, }; +static void device_event_info(void *object, struct pw_device_info *info) +{ + struct global *g = object; + pw_log_debug("update %d", g->id); + g->info = pw_device_info_update(g->info, info); +} + +static const struct pw_device_proxy_events device_events = { + PW_VERSION_DEVICE_PROXY_EVENTS, + .info = device_event_info, +}; + static int ensure_global(pa_context *c, struct global *g) { uint32_t client_version; @@ -98,6 +110,11 @@ static int ensure_global(pa_context *c, struct global *g) client_version = PW_VERSION_CLIENT; destroy = (pw_destroy_t) pw_client_info_free; break; + case PW_TYPE_INTERFACE_Device: + events = &device_events; + client_version = PW_VERSION_DEVICE; + destroy = (pw_destroy_t) pw_device_info_free; + break; default: return -EINVAL; } @@ -851,12 +868,45 @@ struct card_data { pa_context *context; pa_card_info_cb_t cb; void *userdata; + struct global *global; }; -static void card_info(pa_operation *o, void *userdata) +static void card_callback(struct card_data *d) +{ + struct global *g = d->global; + struct pw_device_info *info = g->info; + pa_card_info i; + + spa_zero(i); + i.index = g->id; + i.name = info->name; + i.owner_module = g->parent_id; + i.driver = info->props ? + spa_dict_lookup(info->props, "device.api") : NULL; + i.n_profiles = 0; + i.profiles = NULL; + i.active_profile = NULL; + i.proplist = pa_proplist_new_dict(info->props); + i.n_ports = 0; + i.ports = NULL; + i.profiles2 = NULL; + i.active_profile = NULL; + d->cb(d->context, &i, 0, d->userdata); +} + +static void card_info_list(pa_operation *o, void *userdata) { struct card_data *d = userdata; - d->cb(d->context, NULL, 1, d->userdata); + pa_context *c = d->context; + struct global *g; + + spa_list_for_each(g, &c->globals, link) { + if (!(g->mask & PA_SUBSCRIPTION_MASK_CARD)) + continue; + d->global = g; + card_callback(d); + } + d->cb(c, NULL, 1, d->userdata); pa_operation_done(o); } @@ -869,7 +919,10 @@ pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, pa_assert(c->refcount >= 1); pa_assert(cb); - o = pa_operation_new(c, NULL, card_info, sizeof(struct card_data)); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + ensure_types(c, PA_SUBSCRIPTION_MASK_CARD); + o = pa_operation_new(c, NULL, card_info_list, sizeof(struct card_data)); d = o->userdata; d->context = c; d->cb = cb; @@ -916,7 +969,7 @@ struct sink_input_data { static void sink_input_callback(struct sink_input_data *d) { - struct global *g = d->global; + struct global *g = d->global, *l; struct pw_node_info *info = g->info; pa_sink_input_info i; pa_format_info ii[1]; @@ -933,11 +986,21 @@ static void sink_input_callback(struct sink_input_data *d) i.name = info->name; i.owner_module = PA_INVALID_INDEX; i.client = g->parent_id; - i.sink = PA_INVALID_INDEX; + if (s) { + i.sink = s->device_index; + } + else { + l = pa_context_find_linked(d->context, g->id); + i.sink = l ? l->id : PA_INVALID_INDEX; + } pa_cvolume_init(&i.volume); if (s && s->sample_spec.channels > 0) { i.sample_spec = s->sample_spec; - i.channel_map = s->channel_map; + if (s->channel_map.channels == s->sample_spec.channels) + i.channel_map = s->channel_map; + else + pa_channel_map_init_auto(&i.channel_map, + i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); pa_cvolume_set(&i.volume, i.sample_spec.channels, s->volume * PA_VOLUME_NORM); i.format = s->format; } @@ -1159,7 +1222,7 @@ struct source_output_data { static void source_output_callback(struct source_output_data *d) { - struct global *g = d->global; + struct global *g = d->global, *l; struct pw_node_info *info = g->info; pa_source_output_info i; pa_format_info ii[1]; @@ -1176,11 +1239,21 @@ static void source_output_callback(struct source_output_data *d) i.name = info->name; i.owner_module = PA_INVALID_INDEX; i.client = g->parent_id; - i.source = PA_INVALID_INDEX; + if (s) { + i.source = s->device_index; + } + else { + l = pa_context_find_linked(d->context, g->id); + i.source = l ? l->id : PA_INVALID_INDEX; + } pa_cvolume_init(&i.volume); if (s && s->sample_spec.channels > 0) { i.sample_spec = s->sample_spec; - i.channel_map = s->channel_map; + if (s->channel_map.channels == s->sample_spec.channels) + i.channel_map = s->channel_map; + else + pa_channel_map_init_auto(&i.channel_map, + i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); pa_cvolume_set(&i.volume, i.sample_spec.channels, s->volume * PA_VOLUME_NORM); i.format = s->format; } diff --git a/src/stream.c b/src/stream.c index ee3aca30d..1ed347c88 100644 --- a/src/stream.c +++ b/src/stream.c @@ -185,41 +185,12 @@ static void configure_buffers(pa_stream *s) dump_buffer_attr(s, &s->buffer_attr); } -static struct global *find_linked(pa_stream *s, uint32_t idx) -{ - struct global *g, *f; - pa_context *c = s->context; - - spa_list_for_each(g, &c->globals, link) { - if (g->type != PW_TYPE_INTERFACE_Link) - continue; - - pw_log_debug("%d %d %d", idx, - g->link_info.src->parent_id, - g->link_info.dst->parent_id); - - if (g->link_info.src->parent_id == idx) - f = pa_context_find_global(c, g->link_info.dst->parent_id); - else if (g->link_info.dst->parent_id == idx) - f = pa_context_find_global(c, g->link_info.src->parent_id); - else - continue; - - if (f == NULL) - continue; - if (f->mask & PA_SUBSCRIPTION_MASK_DSP) { - f = f->dsp_info.session; - } - return f; - } - return NULL; -} static void configure_device(pa_stream *s) { struct global *g; const char *str; - g = find_linked(s, pa_stream_get_index(s)); + g = pa_context_find_linked(s->context, pa_stream_get_index(s)); if (g == NULL) { s->device_index = PA_INVALID_INDEX; s->device_name = NULL; From d02a8375c3c5c59303cb31e43f5addc3517f1f39 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 28 Nov 2018 13:38:37 +0100 Subject: [PATCH 048/116] introspect: implement more introspect --- src/introspect.c | 104 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 14 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index 512489fd1..65dc050f8 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -462,8 +462,30 @@ static void source_info(pa_operation *o, void *userdata) pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct source_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, source_info, sizeof(struct source_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + d->global = g; + pa_operation_sync(o); + return o; } pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata) @@ -852,18 +874,6 @@ pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_suc return NULL; } -pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata) -{ - pw_log_warn("Not Implemented"); - return NULL; -} - -pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, pa_card_info_cb_t cb, void *userdata) -{ - pw_log_warn("Not Implemented"); - return NULL; -} - struct card_data { pa_context *context; pa_card_info_cb_t cb; @@ -894,6 +904,72 @@ static void card_callback(struct card_data *d) d->cb(d->context, &i, 0, d->userdata); } +static void card_info(pa_operation *o, void *userdata) +{ + struct card_data *d = userdata; + card_callback(d); + d->cb(d->context, NULL, 1, d->userdata); + pa_operation_done(o); +} + +pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata) +{ + pa_operation *o; + struct global *g; + struct card_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_CARD)) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, card_info, sizeof(struct card_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + d->global = g; + pa_operation_sync(o); + return o; +} + +pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, pa_card_info_cb_t cb, void *userdata) +{ + pa_operation *o; + struct global *g; + struct card_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_CARD, name)) == NULL) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, card_info, sizeof(struct card_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + d->global = g; + pa_operation_sync(o); + return o; +} + static void card_info_list(pa_operation *o, void *userdata) { struct card_data *d = userdata; From 2864e7ec75602534795240a376804007d16db213 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 29 Nov 2018 17:30:24 +0100 Subject: [PATCH 049/116] introspect: improve introspection Update introspection structures when we get the info from pipewire. --- src/context.c | 19 ++- src/internal.h | 19 ++- src/introspect.c | 330 +++++++++++++++++++++++++++++++++++++++-------- src/proplist.c | 6 + 4 files changed, 311 insertions(+), 63 deletions(-) diff --git a/src/context.c b/src/context.c index 299d3ab82..55091a02b 100644 --- a/src/context.c +++ b/src/context.c @@ -104,7 +104,7 @@ struct global *pa_context_find_linked(pa_context *c, uint32_t idx) if (g->type != PW_TYPE_INTERFACE_Link) continue; - pw_log_debug("%d %d %d", idx, + pw_log_debug("context %p: %p %d %d %d", c, g, idx, g->link_info.src->parent_id, g->link_info.dst->parent_id); @@ -118,7 +118,7 @@ struct global *pa_context_find_linked(pa_context *c, uint32_t idx) if (f == NULL) continue; if (f->mask & PA_SUBSCRIPTION_MASK_DSP) { - f = f->dsp_info.session; + f = pa_context_find_global(c, f->dsp_info.session); } return f; } @@ -157,8 +157,7 @@ static int set_mask(pa_context *c, struct global *g) if ((str = pw_properties_get(g->props, "node.session")) == NULL) return 0; g->mask = PA_SUBSCRIPTION_MASK_DSP_SINK; - g->dsp_info.session = pa_context_find_global(c, - pw_properties_parse_int(str)); + g->dsp_info.session = pw_properties_parse_int(str); } else if (strcmp(str, "Audio/Source") == 0) { g->mask = PA_SUBSCRIPTION_MASK_SOURCE; @@ -168,8 +167,7 @@ static int set_mask(pa_context *c, struct global *g) if ((str = pw_properties_get(g->props, "node.session")) == NULL) return 0; g->mask = PA_SUBSCRIPTION_MASK_DSP_SOURCE; - g->dsp_info.session = pa_context_find_global(c, - pw_properties_parse_int(str)); + g->dsp_info.session = pw_properties_parse_int(str); } else if (strcmp(str, "Stream/Output/Audio") == 0) { g->mask = PA_SUBSCRIPTION_MASK_SINK_INPUT; @@ -223,8 +221,8 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, pa_context *c = data; struct global *g; - pw_log_debug("global %d", id); g = calloc(1, sizeof(struct global)); + pw_log_debug("context %p: global %d %p", c, id, g); g->id = id; g->parent_id = parent_id; g->type = type; @@ -250,7 +248,7 @@ static void registry_event_global_remove(void *object, uint32_t id) pa_context *c = object; struct global *g; - pw_log_debug("remove %d", id); + pw_log_debug("context %p: remove %d", c, id); if ((g = pa_context_find_global(c, id)) == NULL) return; @@ -262,11 +260,12 @@ static void registry_event_global_remove(void *object, uint32_t id) c->subscribe_userdata); } + pw_log_debug("context %p: free %d %p", c, id, g); spa_list_remove(&g->link); if (g->props) pw_properties_free(g->props); - if (g->destroy && g->info) - g->destroy(g->info); + if (g->destroy) + g->destroy(g); free(g); } diff --git a/src/internal.h b/src/internal.h index a37c83c8e..a5fc655ae 100644 --- a/src/internal.h +++ b/src/internal.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -145,6 +146,7 @@ struct pa_proplist { pa_proplist* pa_proplist_new_props(struct pw_properties *props); pa_proplist* pa_proplist_new_dict(struct spa_dict *dict); +int pa_proplist_update_dict(pa_proplist *p, struct spa_dict *dict); struct pa_io_event { struct spa_source *source; @@ -206,15 +208,28 @@ struct global { struct spa_hook proxy_listener; struct spa_hook proxy_proxy_listener; - /* for links */ union { + /* for links */ struct { struct global *src; struct global *dst; } link_info; + /* for dsp source and sink */ struct { - struct global *session; + uint32_t session; } dsp_info; + /* for devices */ + struct { + struct pw_array profiles; + uint32_t active_profile; + pa_card_info info; + } card_info; + struct { + pa_module_info info; + } module_info; + struct { + pa_client_info info; + } client_info; }; }; diff --git a/src/introspect.c b/src/introspect.c index 65dc050f8..426a52eda 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -17,6 +17,8 @@ * Boston, MA 02110-1301, USA. */ +#define _GNU_SOURCE + #include #include @@ -52,8 +54,26 @@ static const struct pw_node_proxy_events node_events = { static void module_event_info(void *object, struct pw_module_info *info) { struct global *g = object; + pa_module_info *i = &g->module_info.info; + pw_log_debug("update %d", g->id); - g->info = pw_module_info_update(g->info, info); + + info = g->info = pw_module_info_update(g->info, info); + + i->index = g->id; + if (info->change_mask & PW_MODULE_CHANGE_MASK_PROPS) { + if (i->proplist) + pa_proplist_update_dict(i->proplist, info->props); + else + i->proplist = pa_proplist_new_dict(info->props); + } + + if (info->change_mask & PW_MODULE_CHANGE_MASK_NAME) + i->name = info->name; + if (info->change_mask & PW_MODULE_CHANGE_MASK_ARGS) + i->argument = info->args; + i->n_used = -1; + i->auto_unload = false; } static const struct pw_module_proxy_events module_events = { @@ -64,8 +84,24 @@ static const struct pw_module_proxy_events module_events = { static void client_event_info(void *object, struct pw_client_info *info) { struct global *g = object; + pa_client_info *i = &g->client_info.info; + pw_log_debug("update %d", g->id); - g->info = pw_client_info_update(g->info, info); + info = g->info = pw_client_info_update(g->info, info); + + i->index = g->id; + i->owner_module = g->parent_id; + + if (info->change_mask & PW_CLIENT_CHANGE_MASK_PROPS) { + if (i->proplist) + pa_proplist_update_dict(i->proplist, info->props); + else + i->proplist = pa_proplist_new_dict(info->props); + i->name = info->props ? + spa_dict_lookup(info->props, "application.prgname") : NULL; + i->driver = info->props ? + spa_dict_lookup(info->props, PW_CLIENT_PROP_PROTOCOL) : NULL; + } } static const struct pw_client_proxy_events client_events = { @@ -73,18 +109,113 @@ static const struct pw_client_proxy_events client_events = { .info = client_event_info, }; +static void device_event_param(void *object, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct global *g = object; + + switch (id) { + case SPA_PARAM_EnumProfile: + { + uint32_t id; + const char *name; + + if (spa_pod_object_parse(param, + ":", SPA_PARAM_PROFILE_id, "i", &id, + ":", SPA_PARAM_PROFILE_name, "s", &name, + NULL) < 0) { + pw_log_warn("device %d: can't parse profile", g->id); + return; + } + pw_array_add_ptr(&g->card_info.profiles, pw_spa_pod_copy(param)); + pw_log_debug("device %d: enum profile %d: \"%s\"", g->id, id, name); + break; + } + case SPA_PARAM_Profile: + { + uint32_t id; + if (spa_pod_object_parse(param, + ":", SPA_PARAM_PROFILE_id, "i", &id, + NULL) < 0) { + pw_log_warn("device %d: can't parse profile", g->id); + return; + } + g->card_info.active_profile = id; + pw_log_debug("device %d: current profile %d", g->id, id); + break; + } + default: + break; + } +} + static void device_event_info(void *object, struct pw_device_info *info) { struct global *g = object; + pa_card_info *i = &g->card_info.info; + pw_log_debug("update %d", g->id); - g->info = pw_device_info_update(g->info, info); + info = g->info = pw_device_info_update(g->info, info); + + i->index = g->id; + i->name = info->name; + i->owner_module = g->parent_id; + if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS) { + i->driver = info->props ? + spa_dict_lookup(info->props, "device.api") : NULL; + if (i->proplist) + pa_proplist_update_dict(i->proplist, info->props); + else + i->proplist = pa_proplist_new_dict(info->props); + } } static const struct pw_device_proxy_events device_events = { PW_VERSION_DEVICE_PROXY_EVENTS, .info = device_event_info, + .param = device_event_param, }; +static void node_destroy(void *data) +{ + struct global *global = data; + if (global->info) + pw_node_info_free(global->info); +} + +static void module_destroy(void *data) +{ + struct global *global = data; + if (global->module_info.info.proplist) + pa_proplist_free(global->module_info.info.proplist); + if (global->info) + pw_module_info_free(global->info); +} + +static void client_destroy(void *data) +{ + struct global *global = data; + if (global->client_info.info.proplist) + pa_proplist_free(global->client_info.info.proplist); + if (global->info) + pw_client_info_free(global->info); +} + +static void device_destroy(void *data) +{ + struct global *global = data; + struct spa_pod *profile; + + if (global->card_info.info.proplist) + pa_proplist_free(global->card_info.info.proplist); + pw_array_for_each(profile, &global->card_info.profiles) + free(profile); + pw_array_clear(&global->card_info.profiles); + if (global->info) + pw_device_info_free(global->info); +} + static int ensure_global(pa_context *c, struct global *g) { uint32_t client_version; @@ -98,22 +229,23 @@ static int ensure_global(pa_context *c, struct global *g) case PW_TYPE_INTERFACE_Node: events = &node_events; client_version = PW_VERSION_NODE; - destroy = (pw_destroy_t) pw_node_info_free; + destroy = node_destroy; break; case PW_TYPE_INTERFACE_Module: events = &module_events; client_version = PW_VERSION_MODULE; - destroy = (pw_destroy_t) pw_module_info_free; + destroy = module_destroy; break; case PW_TYPE_INTERFACE_Client: events = &client_events; client_version = PW_VERSION_CLIENT; - destroy = (pw_destroy_t) pw_client_info_free; + destroy = client_destroy; break; case PW_TYPE_INTERFACE_Device: events = &device_events; client_version = PW_VERSION_DEVICE; - destroy = (pw_destroy_t) pw_device_info_free; + destroy = device_destroy; + pw_array_init(&g->card_info.profiles, 64); break; default: return -EINVAL; @@ -132,8 +264,13 @@ static int ensure_global(pa_context *c, struct global *g) switch (g->type) { case PW_TYPE_INTERFACE_Node: pw_node_proxy_enum_params((struct pw_node_proxy*)g->proxy, - SPA_PARAM_EnumFormat, - 0, -1, NULL); + SPA_PARAM_EnumFormat, 0, -1, NULL); + break; + case PW_TYPE_INTERFACE_Device: + pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, + SPA_PARAM_EnumProfile, 0, -1, NULL); + pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, + SPA_PARAM_Profile, 0, -1, NULL); break; default: break; @@ -214,6 +351,8 @@ static void sink_callback(struct sink_data *d) ip[0] = ii; i.formats = ip; d->cb(d->context, &i, 0, d->userdata); + pa_proplist_free(i.proplist); + pa_proplist_free(ii[0].plist); } static void sink_info(pa_operation *o, void *userdata) @@ -450,6 +589,8 @@ static void source_callback(struct source_data *d) ip[0] = ii; i.formats = ip; d->cb(d->context, &i, 0, d->userdata); + pa_proplist_free(i.proplist); + pa_proplist_free(ii[0].plist); } static void source_info(pa_operation *o, void *userdata) @@ -667,17 +808,7 @@ struct module_data { static void module_callback(struct module_data *d) { struct global *g = d->global; - struct pw_module_info *info = g->info; - pa_module_info i; - - spa_zero(i); - i.proplist = pa_proplist_new_dict(info->props); - i.index = g->id; - i.name = info->name; - i.argument = info->args; - i.n_used = -1; - i.auto_unload = false; - d->cb(d->context, &i, 0, d->userdata); + d->cb(d->context, &g->module_info.info, 0, d->userdata); } static void module_info(pa_operation *o, void *userdata) @@ -778,18 +909,7 @@ struct client_data { static void client_callback(struct client_data *d) { struct global *g = d->global; - struct pw_client_info *info = g->info; - pa_client_info i; - - spa_zero(i); - i.proplist = pa_proplist_new_dict(info->props); - i.index = g->id; - i.name = info->props ? - spa_dict_lookup(info->props, "application.prgname") : NULL; - i.owner_module = g->parent_id; - i.driver = info->props ? - spa_dict_lookup(info->props, PW_CLIENT_PROP_PROTOCOL) : NULL; - d->cb(d->context, &i, 0, d->userdata); + d->cb(d->context, &g->client_info.info, 0, d->userdata); } static void client_info(pa_operation *o, void *userdata) @@ -877,31 +997,59 @@ pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_suc struct card_data { pa_context *context; pa_card_info_cb_t cb; + pa_context_success_cb_t success_cb; void *userdata; struct global *global; + char *profile; }; static void card_callback(struct card_data *d) { struct global *g = d->global; - struct pw_device_info *info = g->info; - pa_card_info i; + pa_card_info *i = &g->card_info.info; + int n_profiles, j; + struct spa_pod **profiles; - spa_zero(i); - i.index = g->id; - i.name = info->name; - i.owner_module = g->parent_id; - i.driver = info->props ? - spa_dict_lookup(info->props, "device.api") : NULL; - i.n_profiles = 0; - i.profiles = NULL; - i.active_profile = NULL; - i.proplist = pa_proplist_new_dict(info->props); - i.n_ports = 0; - i.ports = NULL; - i.profiles2 = NULL; - i.active_profile = NULL; - d->cb(d->context, &i, 0, d->userdata); + n_profiles = pw_array_get_len(&g->card_info.profiles, struct spa_pod*); + profiles = g->card_info.profiles.data; + + i->profiles = alloca(sizeof(pa_card_profile_info) * n_profiles); + i->profiles2 = alloca(sizeof(pa_card_profile_info2 *) * n_profiles); + i->n_profiles = 0; + + for (j = 0; j < n_profiles; j++) { + uint32_t id; + const char *name; + + if (spa_pod_object_parse(profiles[j], + ":", SPA_PARAM_PROFILE_id, "i", &id, + ":", SPA_PARAM_PROFILE_name, "s", &name, + NULL) < 0) { + pw_log_warn("device %d: can't parse profile %d", g->id, j); + continue; + } + + i->profiles[j].name = name; + i->profiles[j].description = name; + i->profiles[j].n_sinks = 1; + i->profiles[j].n_sources = 1; + i->profiles[j].priority = 1; + + i->profiles2[j] = alloca(sizeof(pa_card_profile_info2)); + i->profiles2[j]->name = i->profiles[j].name; + i->profiles2[j]->description = i->profiles[j].description; + i->profiles2[j]->n_sinks = i->profiles[j].n_sinks; + i->profiles2[j]->n_sources = i->profiles[j].n_sources; + i->profiles2[j]->priority = i->profiles[j].priority; + i->profiles2[j]->available = 1; + + if (g->card_info.active_profile == id) { + i->active_profile = &i->profiles[j]; + i->active_profile2 = i->profiles2[j]; + } + i->n_profiles++; + } + d->cb(d->context, i, 0, d->userdata); } static void card_info(pa_operation *o, void *userdata) @@ -1008,10 +1156,84 @@ pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, return o; } +static void card_profile(pa_operation *o, void *userdata) +{ + struct card_data *d = userdata; + struct global *g = d->global; + pa_context *c = d->context; + int res = 0, n_profiles; + uint32_t i, id = SPA_ID_INVALID; + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + struct spa_pod **profiles; + + n_profiles = pw_array_get_len(&g->card_info.profiles, struct spa_pod*); + profiles = g->card_info.profiles.data; + + for (i = 0; i < n_profiles; i++) { + uint32_t test_id; + const char *name; + + if (spa_pod_object_parse(profiles[i], + ":", SPA_PARAM_PROFILE_id, "i", &test_id, + ":", SPA_PARAM_PROFILE_name, "s", &name, + NULL) < 0) { + pw_log_warn("device %d: can't parse profile %d", g->id, i); + continue; + } + if (strcmp(name, d->profile) == 0) { + id = test_id; + break; + } + } + if (id == SPA_ID_INVALID) + goto done;; + + pw_device_proxy_set_param((struct pw_device_proxy*)g->proxy, + SPA_PARAM_Profile, 0, + spa_pod_builder_object(&b, + SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, + SPA_PARAM_PROFILE_id, &SPA_POD_Int(id), + 0)); + res = 1; +done: + if (d->success_cb) + d->success_cb(c, res, d->userdata); + pa_operation_done(o); + free(d->profile); +} + pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct card_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_CARD)) + return NULL; + + ensure_global(c, g); + + pw_log_debug("Card set profile %s", profile); + + o = pa_operation_new(c, NULL, card_profile, sizeof(struct card_data)); + d = o->userdata; + d->context = c; + d->success_cb = cb; + d->userdata = userdata; + d->global = g; + d->profile = strdup(profile); + pa_operation_sync(o); + + return o; } pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name, const char*profile, pa_context_success_cb_t cb, void *userdata) @@ -1099,7 +1321,10 @@ static void sink_input_callback(struct sink_input_data *d) i.corked = false; i.has_volume = true; i.volume_writable = true; + d->cb(d->context, &i, 0, d->userdata); + + pa_proplist_free(i.proplist); } static void sink_input_info(pa_operation *o, void *userdata) @@ -1352,7 +1577,10 @@ static void source_output_callback(struct source_output_data *d) i.corked = false; i.has_volume = true; i.volume_writable = true; + d->cb(d->context, &i, 0, d->userdata); + + pa_proplist_free(i.proplist); } static void source_output_info(pa_operation *o, void *userdata) diff --git a/src/proplist.c b/src/proplist.c index 457024985..281898f1e 100644 --- a/src/proplist.c +++ b/src/proplist.c @@ -54,6 +54,12 @@ pa_proplist* pa_proplist_new(void) return pa_proplist_new_dict(NULL); } +int pa_proplist_update_dict(pa_proplist *p, struct spa_dict *dict) +{ + return pw_properties_update(p->props, dict); +} + + void pa_proplist_free(pa_proplist* p) { pw_properties_free(p->props); From ad723e379330a39498a8c7b9a1a17e0bf79d94d0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 3 Dec 2018 15:12:27 +0100 Subject: [PATCH 050/116] context: log more info on errors --- src/context.c | 3 ++- src/internal.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/context.c b/src/context.c index 55091a02b..7e87f3f91 100644 --- a/src/context.c +++ b/src/context.c @@ -27,13 +27,14 @@ #include #include +#include #include "internal.h" int pa_context_set_error(pa_context *c, int error) { pa_assert(error >= 0); pa_assert(error < PA_ERR_MAX); - pw_log_debug("context %p: error %d", c, error); + pw_log_debug("context %p: error %d %s", c, error, pa_strerror(error)); if (c) c->error = error; return error; diff --git a/src/internal.h b/src/internal.h index a5fc655ae..712868e03 100644 --- a/src/internal.h +++ b/src/internal.h @@ -111,6 +111,8 @@ int pa_context_set_error(pa_context *c, int error); #define PA_CHECK_VALIDITY(context, expression, error) \ do { \ if (!(expression)) { \ + pw_log_trace("'%s' failed at %s:%u %s()", \ + #expression, __FILE__, __LINE__, __func__); \ return -pa_context_set_error((context), (error)); \ } \ } while(false) @@ -118,6 +120,8 @@ do { \ #define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) \ do { \ if (!(expression)) { \ + pw_log_trace("'%s' failed at %s:%u %s()", \ + #expression, __FILE__, __LINE__, __func__); \ pa_context_set_error((context), (error)); \ return value; \ } \ From 84a7bf671ce5864158d0531f6ffb7070587a2969 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 4 Dec 2018 11:48:15 +0100 Subject: [PATCH 051/116] stream: connect to source and sink Stream: also connect to the right source, try to convert the name to index when lookup fails, just like what pulseaudio does. Implement more proplist functions, add some more info to the stream. stream: handle NULL format Improve introspection of the sink_input and source_output name, this should be the media.name when possible. --- src/context.c | 19 +++++++++++++++++++ src/introspect.c | 40 ++++++++++++++++++++-------------------- src/proplist.c | 33 +++++++++++++++++++++++++++++---- src/stream.c | 24 +++++++++++++++++++++--- 4 files changed, 89 insertions(+), 27 deletions(-) diff --git a/src/context.c b/src/context.c index 7e87f3f91..0f3193794 100644 --- a/src/context.c +++ b/src/context.c @@ -97,6 +97,25 @@ struct global *pa_context_find_global(pa_context *c, uint32_t id) return NULL; } +struct global *pa_context_find_global_by_name(pa_context *c, uint32_t mask, const char *name) +{ + struct global *g; + const char *str; + uint32_t id = atoi(name); + + spa_list_for_each(g, &c->globals, link) { + if (!(g->mask & mask)) + continue; + if (g->props != NULL && + (str = pw_properties_get(g->props, "node.name")) != NULL && + strcmp(str, name) == 0) + return g; + if (g->id == id) + return g; + } + return NULL; +} + struct global *pa_context_find_linked(pa_context *c, uint32_t idx) { struct global *g, *f; diff --git a/src/introspect.c b/src/introspect.c index 426a52eda..7ccf44b35 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -363,24 +363,6 @@ static void sink_info(pa_operation *o, void *userdata) pa_operation_done(o); } -struct global *pa_context_find_global_by_name(pa_context *c, uint32_t mask, const char *name) -{ - struct global *g; - const char *str; - - spa_list_for_each(g, &c->globals, link) { - if (!(g->mask & mask)) - continue; - if (g->props == NULL) - continue; - if ((str = pw_properties_get(g->props, "node.name")) == NULL) - continue; - if (strcmp(str, name) == 0) - return g; - } - return NULL; -} - pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1269,6 +1251,7 @@ static void sink_input_callback(struct sink_input_data *d) { struct global *g = d->global, *l; struct pw_node_info *info = g->info; + const char *name; pa_sink_input_info i; pa_format_info ii[1]; pa_stream *s; @@ -1279,9 +1262,17 @@ static void sink_input_callback(struct sink_input_data *d) s = find_stream(d->context, g->id); + if (info->props) { + if ((name = spa_dict_lookup(info->props, "media.name")) == NULL && + (name = spa_dict_lookup(info->props, "application.name")) == NULL) + name = info->name; + } + else + name = info->name; + spa_zero(i); i.index = g->id; - i.name = info->name; + i.name = name ? name : "Unknown"; i.owner_module = PA_INVALID_INDEX; i.client = g->parent_id; if (s) { @@ -1525,6 +1516,7 @@ static void source_output_callback(struct source_output_data *d) { struct global *g = d->global, *l; struct pw_node_info *info = g->info; + const char *name; pa_source_output_info i; pa_format_info ii[1]; pa_stream *s; @@ -1535,9 +1527,17 @@ static void source_output_callback(struct source_output_data *d) s = find_stream(d->context, g->id); + if (info->props) { + if ((name = spa_dict_lookup(info->props, "media.name")) == NULL && + (name = spa_dict_lookup(info->props, "application.name")) == NULL) + name = info->name; + } + else + name = info->name; + spa_zero(i); i.index = g->id; - i.name = info->name; + i.name = name ? name : "Unknown"; i.owner_module = PA_INVALID_INDEX; i.client = g->parent_id; if (s) { diff --git a/src/proplist.c b/src/proplist.c index 281898f1e..fad5c1a5f 100644 --- a/src/proplist.c +++ b/src/proplist.c @@ -159,7 +159,13 @@ int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t * void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other) { spa_assert(p); - pw_log_warn("Not Implemented"); + spa_assert(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE); + spa_assert(other); + + if (mode == PA_UPDATE_SET) + pa_proplist_clear(p); + + pa_proplist_update_dict(p, &other->props->dict); } int pa_proplist_unset(pa_proplist *p, const char *key) @@ -274,7 +280,7 @@ int pa_proplist_contains(pa_proplist *p, const char *key) void pa_proplist_clear(pa_proplist *p) { spa_assert(p); - pw_log_warn("Not Implemented"); + pw_properties_clear(p->props); } pa_proplist* pa_proplist_copy(const pa_proplist *p) @@ -305,8 +311,27 @@ int pa_proplist_isempty(pa_proplist *p) int pa_proplist_equal(pa_proplist *a, pa_proplist *b) { + int i; + spa_assert(a); spa_assert(b); - pw_log_warn("Not Implemented"); - return 0; + + if (a == b) + return 1; + + if (pa_proplist_size(a) != pa_proplist_size(b)) + return 0; + + for (i = 0; i < a->props->dict.n_items; i++) { + const struct spa_dict_item *ai, *bi; + + ai = &a->props->dict.items[i]; + bi = spa_dict_lookup_item(&b->props->dict, ai->key); + + if (bi == NULL || bi->value == NULL || ai->value == NULL) + return 0; + if (strcmp(ai->value, bi->value) != 0) + return 0; + } + return 1; } diff --git a/src/stream.c b/src/stream.c index 1ed347c88..ed5144af7 100644 --- a/src/stream.c +++ b/src/stream.c @@ -337,6 +337,11 @@ static void stream_format_changed(void *data, const struct spa_pod *format) struct spa_audio_info info = { 0 }; int i, res; + if (format == NULL) { + res = 0; + goto done; + } + spa_format_parse(format, &info.media_type, &info.media_subtype); if (info.media_type != SPA_MEDIA_TYPE_audio || @@ -497,10 +502,11 @@ pa_stream* stream_new(pa_context *c, const char *name, if (name) pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name); else - name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME); + name = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME); props = pw_properties_new("client.api", "pulseaudio", NULL); + pw_properties_update(props, &s->proplist->props->dict); s->stream = pw_stream_new(c->remote, name, props); s->refcount = 1; @@ -762,6 +768,8 @@ static int create_stream(pa_stream_direction_t direction, spa_assert(s); spa_assert(s->refcount >= 1); + pw_log_debug("stream %p: connect %s %08x", s, dev, flags); + s->direction = direction; s->timing_info_valid = false; s->disconnecting = false; @@ -824,8 +832,18 @@ static int create_stream(pa_stream_direction_t direction, if ((str = getenv("PIPEWIRE_NODE")) != NULL) devid = atoi(str); } - else if ((g = pa_context_find_global_by_name(s->context, PA_SUBSCRIPTION_MASK_SINK, dev)) != NULL) { - devid = g->id; + else { + uint32_t mask; + + if (direction == PA_STREAM_PLAYBACK) + mask = PA_SUBSCRIPTION_MASK_SINK; + else if (direction == PA_STREAM_RECORD) + mask = PA_SUBSCRIPTION_MASK_SOURCE; + else + mask = 0; + + if ((g = pa_context_find_global_by_name(s->context, mask, dev)) != NULL) + devid = g->id; } if ((str = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_ROLE)) == NULL) From 1efd5f185c4863e51f26146dd15d0d3886eacb1d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 4 Dec 2018 13:38:23 +0100 Subject: [PATCH 052/116] proplist: implement merge mode --- src/proplist.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/proplist.c b/src/proplist.c index fad5c1a5f..7aba67bed 100644 --- a/src/proplist.c +++ b/src/proplist.c @@ -158,14 +158,27 @@ int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t * void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other) { + int i; + spa_assert(p); spa_assert(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE); spa_assert(other); + if (mode == PA_UPDATE_REPLACE) { + pa_proplist_update_dict(p, &other->props->dict); + return; + } + if (mode == PA_UPDATE_SET) pa_proplist_clear(p); - pa_proplist_update_dict(p, &other->props->dict); + for (i = 0; i < other->props->dict.n_items; i++) { + const struct spa_dict_item *oi; + + oi = &other->props->dict.items[i]; + if (pw_properties_get(p->props, oi->key) == NULL) + pw_properties_set(p->props, oi->key, oi->value); + } } int pa_proplist_unset(pa_proplist *p, const char *key) From 2d4ecaf85d6bb5a5c78ee36d8fb11d59c4935aa0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 4 Dec 2018 13:38:58 +0100 Subject: [PATCH 053/116] introspect: use right client property --- src/introspect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/introspect.c b/src/introspect.c index 7ccf44b35..55716e147 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -98,7 +98,7 @@ static void client_event_info(void *object, struct pw_client_info *info) else i->proplist = pa_proplist_new_dict(info->props); i->name = info->props ? - spa_dict_lookup(info->props, "application.prgname") : NULL; + spa_dict_lookup(info->props, "application.name") : NULL; i->driver = info->props ? spa_dict_lookup(info->props, PW_CLIENT_PROP_PROTOCOL) : NULL; } From e0970386f034d7500c2a095dc185807cdad3a8da Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 4 Dec 2018 16:33:59 +0100 Subject: [PATCH 054/116] context: add properties --- src/context.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/context.c b/src/context.c index 0f3193794..6a317907e 100644 --- a/src/context.c +++ b/src/context.c @@ -384,6 +384,8 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * if (name) pw_properties_set(props, PA_PROP_APPLICATION_NAME, name); pw_properties_set(props, "client.api", "pulseaudio"); + if (p) + pw_properties_update(props, &p->props->dict); loop = mainloop->userdata; core = pw_core_new(loop, NULL); From bb101afa0c54c4b6d2ac9388a21837f0c3183773 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 4 Dec 2018 16:34:58 +0100 Subject: [PATCH 055/116] introspect: merge client properties in sink_input/source_output sink_input and source_output must contain the merged properties of the client. --- src/introspect.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index 55716e147..d859de714 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -1249,7 +1249,7 @@ struct sink_input_data { static void sink_input_callback(struct sink_input_data *d) { - struct global *g = d->global, *l; + struct global *g = d->global, *l, *cl; struct pw_node_info *info = g->info; const char *name; pa_sink_input_info i; @@ -1270,6 +1270,8 @@ static void sink_input_callback(struct sink_input_data *d) else name = info->name; + cl = pa_context_find_global(d->context, g->parent_id); + spa_zero(i); i.index = g->id; i.name = name ? name : "Unknown"; @@ -1309,6 +1311,8 @@ static void sink_input_callback(struct sink_input_data *d) i.driver = "PipeWire"; i.mute = false; i.proplist = pa_proplist_new_dict(info->props); + if (cl && cl->client_info.info.proplist) + pa_proplist_update(i.proplist, PA_UPDATE_MERGE, cl->client_info.info.proplist); i.corked = false; i.has_volume = true; i.volume_writable = true; @@ -1514,7 +1518,7 @@ struct source_output_data { static void source_output_callback(struct source_output_data *d) { - struct global *g = d->global, *l; + struct global *g = d->global, *l, *cl; struct pw_node_info *info = g->info; const char *name; pa_source_output_info i; @@ -1535,6 +1539,8 @@ static void source_output_callback(struct source_output_data *d) else name = info->name; + cl = pa_context_find_global(d->context, g->parent_id); + spa_zero(i); i.index = g->id; i.name = name ? name : "Unknown"; @@ -1574,6 +1580,8 @@ static void source_output_callback(struct source_output_data *d) i.driver = "PipeWire"; i.mute = false; i.proplist = pa_proplist_new_dict(info->props); + if (cl && cl->client_info.info.proplist) + pa_proplist_update(i.proplist, PA_UPDATE_MERGE, cl->client_info.info.proplist); i.corked = false; i.has_volume = true; i.volume_writable = true; From ece9cff49553d1ee56a9b24a604bd618331ad7d1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 4 Dec 2018 16:36:49 +0100 Subject: [PATCH 056/116] introspect: implement kill operations --- src/introspect.c | 108 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 88 insertions(+), 20 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index d859de714..b5358398d 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -287,6 +287,20 @@ static void ensure_types(pa_context *c, uint32_t mask) } } +struct success_ack { + pa_context_success_cb_t cb; + void *userdata; +}; + +static void on_success(pa_operation *o, void *userdata) +{ + struct success_ack *d = userdata; + pa_context *c = o->context; + if (d->cb) + d->cb(c, PA_OK, d->userdata); + pa_operation_done(o); +} + struct sink_data { pa_context *context; pa_sink_info_cb_t cb; @@ -972,8 +986,26 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + struct global *g; + pa_operation *o; + struct success_ack *d; + + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_CLIENT)) + return NULL; + + pw_registry_proxy_destroy(c->registry_proxy, g->id); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } struct card_data { @@ -1412,20 +1444,6 @@ pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, u return NULL; } -struct success_ack { - pa_context_success_cb_t cb; - void *userdata; -}; - -static void on_success(pa_operation *o, void *userdata) -{ - struct success_ack *d = userdata; - pa_context *c = o->context; - if (d->cb) - d->cb(c, PA_OK, d->userdata); - pa_operation_done(o); -} - pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_stream *s; @@ -1441,6 +1459,8 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons if ((s = find_stream(c, idx)) == NULL) { if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK_INPUT)) + return NULL; } if (s) { @@ -1477,6 +1497,8 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu if ((s = find_stream(c, idx)) == NULL) { if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK_INPUT)) + return NULL; } if (s) { @@ -1505,8 +1527,31 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_stream *s; + struct global *g; + pa_operation *o; + struct success_ack *d; + + if ((s = find_stream(c, idx)) == NULL) { + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK_INPUT)) + return NULL; + } + + if (s) { + pw_stream_destroy(s->stream); + } + else if (g) { + pw_registry_proxy_destroy(c->registry_proxy, g->id); + } + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } struct source_output_data { @@ -1693,8 +1738,31 @@ pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_stream *s; + struct global *g; + pa_operation *o; + struct success_ack *d; + + if ((s = find_stream(c, idx)) == NULL) { + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)) + return NULL; + } + + if (s) { + pw_stream_destroy(s->stream); + } + else if (g) { + pw_registry_proxy_destroy(c->registry_proxy, g->id); + } + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata) From c201a1e6669919803a191c168a445bdf38d7bb0a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 5 Dec 2018 16:02:03 +0100 Subject: [PATCH 057/116] stream: enable monitor when PEAK_DETECT flag is set --- src/introspect.c | 1 - src/stream.c | 15 ++++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index b5358398d..358e4a938 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -1288,7 +1288,6 @@ static void sink_input_callback(struct sink_input_data *d) pa_format_info ii[1]; pa_stream *s; - pw_log_debug("index %d", g->id); if (info == NULL) return; diff --git a/src/stream.c b/src/stream.c index ed5144af7..7ac6e8a7a 100644 --- a/src/stream.c +++ b/src/stream.c @@ -764,6 +764,7 @@ static int create_stream(pa_stream_direction_t direction, struct global *g; struct spa_dict_item items[5]; char latency[64]; + bool monitor; spa_assert(s); spa_assert(s->refcount >= 1); @@ -790,6 +791,9 @@ static int create_stream(pa_stream_direction_t direction, fl |= PW_STREAM_FLAG_INACTIVE; if (flags & PA_STREAM_PASSTHROUGH) fl |= PW_STREAM_FLAG_EXCLUSIVE; + if (flags & PA_STREAM_DONT_MOVE) + fl |= PW_STREAM_FLAG_DONT_RECONNECT; + monitor = (flags & PA_STREAM_PEAK_DETECT); if (pa_sample_spec_valid(&s->sample_spec)) { params[n_params++] = get_param(s, &s->sample_spec, &s->channel_map, &b); @@ -827,7 +831,11 @@ static int create_stream(pa_stream_direction_t direction, s->buffer_attr = *attr; patch_buffer_attr(s, &s->buffer_attr, &flags); - devid = SPA_ID_INVALID; + if (direction == PA_STREAM_RECORD) + devid = s->direct_on_input; + else + devid = SPA_ID_INVALID; + if (dev == NULL) { if ((str = getenv("PIPEWIRE_NODE")) != NULL) devid = atoi(str); @@ -876,8 +884,9 @@ static int create_stream(pa_stream_direction_t direction, direction == PA_STREAM_PLAYBACK ? "Playback" : "Capture"); items[3] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_ROLE, str); + items[4] = SPA_DICT_ITEM_INIT("pipewire.monitor", monitor ? "1" : "0"); - pw_stream_update_properties(s->stream, &SPA_DICT_INIT(items, 4)); + pw_stream_update_properties(s->stream, &SPA_DICT_INIT(items, 5)); res = pw_stream_connect(s->stream, direction == PA_STREAM_PLAYBACK ? @@ -1723,7 +1732,7 @@ int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) spa_assert(s); spa_assert(s->refcount >= 1); - pw_log_debug("stream %p: %d", s, sink_input_idx); + pw_log_warn("stream %p: Not implemented %d", s, sink_input_idx); PA_CHECK_VALIDITY(s->context, sink_input_idx != PA_INVALID_INDEX, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); From 72b61f614aa0da892e95416b10d273c5f0a1744f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 11 Dec 2018 16:37:30 +0100 Subject: [PATCH 058/116] stream: handle monitor sources --- src/context.c | 13 ++++++++++--- src/internal.h | 4 ++++ src/introspect.c | 19 +++++++++++++------ src/stream.c | 7 ++++--- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/context.c b/src/context.c index 6a317907e..545df2414 100644 --- a/src/context.c +++ b/src/context.c @@ -104,7 +104,7 @@ struct global *pa_context_find_global_by_name(pa_context *c, uint32_t mask, cons uint32_t id = atoi(name); spa_list_for_each(g, &c->globals, link) { - if (!(g->mask & mask)) + if ((g->mask & mask) == 0) continue; if (g->props != NULL && (str = pw_properties_get(g->props, "node.name")) != NULL && @@ -138,7 +138,9 @@ struct global *pa_context_find_linked(pa_context *c, uint32_t idx) if (f == NULL) continue; if (f->mask & PA_SUBSCRIPTION_MASK_DSP) { - f = pa_context_find_global(c, f->dsp_info.session); + if (!(f->mask & PA_SUBSCRIPTION_MASK_SOURCE) || + g->link_info.dst->parent_id != idx) + f = pa_context_find_global(c, f->dsp_info.session); } return f; } @@ -148,6 +150,7 @@ struct global *pa_context_find_linked(pa_context *c, uint32_t idx) static int set_mask(pa_context *c, struct global *g) { const char *str; + struct global *f; switch (g->type) { case PW_TYPE_INTERFACE_Device: @@ -172,12 +175,16 @@ static int set_mask(pa_context *c, struct global *g) if (strcmp(str, "Audio/Sink") == 0) { g->mask = PA_SUBSCRIPTION_MASK_SINK; g->event = PA_SUBSCRIPTION_EVENT_SINK; + g->node_info.monitor = SPA_ID_INVALID; } else if (strcmp(str, "Audio/DSP/Playback") == 0) { if ((str = pw_properties_get(g->props, "node.session")) == NULL) return 0; - g->mask = PA_SUBSCRIPTION_MASK_DSP_SINK; + g->mask = PA_SUBSCRIPTION_MASK_DSP_SINK | PA_SUBSCRIPTION_MASK_SOURCE; + g->event = PA_SUBSCRIPTION_EVENT_SOURCE; g->dsp_info.session = pw_properties_parse_int(str); + if ((f = pa_context_find_global(c, g->dsp_info.session)) != NULL) + f->node_info.monitor = g->id; } else if (strcmp(str, "Audio/Source") == 0) { g->mask = PA_SUBSCRIPTION_MASK_SOURCE; diff --git a/src/internal.h b/src/internal.h index 712868e03..ab99ec20e 100644 --- a/src/internal.h +++ b/src/internal.h @@ -222,6 +222,10 @@ struct global { struct { uint32_t session; } dsp_info; + /* for sink/source */ + struct { + uint32_t monitor; + } node_info; /* for devices */ struct { struct pw_array profiles; diff --git a/src/introspect.c b/src/introspect.c index 358e4a938..ec3809436 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -334,6 +334,8 @@ static void sink_callback(struct sink_data *d) pa_format_info ii[1]; pa_format_info *ip[1]; + pw_log_debug("sink %d %s monitor %d", g->id, info->name, g->node_info.monitor); + spa_zero(i); i.name = info->name; i.index = g->id; @@ -345,7 +347,7 @@ static void sink_callback(struct sink_data *d) i.owner_module = g->parent_id; pa_cvolume_set(&i.volume, 2, PA_VOLUME_NORM); i.mute = false; - i.monitor_source = PA_INVALID_INDEX; + i.monitor_source = g->node_info.monitor; i.monitor_source_name = "unknown"; i.latency = 0; i.driver = "PipeWire"; @@ -565,8 +567,13 @@ static void source_callback(struct source_data *d) i.owner_module = g->parent_id; pa_cvolume_set(&i.volume, 2, PA_VOLUME_NORM); i.mute = false; - i.monitor_of_sink = PA_INVALID_INDEX; - i.monitor_of_sink_name = "unknown"; + if (g->mask & PA_SUBSCRIPTION_MASK_DSP_SINK) { + i.monitor_of_sink = g->dsp_info.session; + i.monitor_of_sink_name = "unknown"; + } else { + i.monitor_of_sink = PA_INVALID_INDEX; + i.monitor_of_sink_name = NULL; + } i.latency = 0; i.driver = "PipeWire"; i.flags = 0; @@ -1473,8 +1480,8 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, SPA_PARAM_Props, 0, spa_pod_builder_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_volume, &SPA_POD_Float(v), + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_volume, &SPA_POD_Float(v), 0)); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); @@ -1511,7 +1518,7 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, SPA_PARAM_Props, 0, spa_pod_builder_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, SPA_PROP_mute, &SPA_POD_Bool(mute), 0)); } diff --git a/src/stream.c b/src/stream.c index 7ac6e8a7a..d17c0f0d3 100644 --- a/src/stream.c +++ b/src/stream.c @@ -202,7 +202,7 @@ static void configure_device(pa_stream *s) else s->device_name = strdup(str); } - pw_log_debug("linked to %d '%s'", s->device_index, s->device_name); + pw_log_debug("stream %p: linked to %d '%s'", s, s->device_index, s->device_name); } static void stream_state_changed(void *data, enum pw_stream_state old, @@ -690,6 +690,7 @@ uint32_t pa_stream_get_device_index(pa_stream *s) PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->device_index != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX); + pw_log_trace("stream %p: %d", s, s->device_index); return s->device_index; } @@ -840,7 +841,7 @@ static int create_stream(pa_stream_direction_t direction, if ((str = getenv("PIPEWIRE_NODE")) != NULL) devid = atoi(str); } - else { + else if (devid == SPA_ID_INVALID) { uint32_t mask; if (direction == PA_STREAM_PLAYBACK) @@ -1131,7 +1132,7 @@ int pa_stream_peek(pa_stream *s, } *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); *nbytes = s->buffer_size; - pw_log_trace("stream %p: %p %zd", s, *data, *nbytes); + pw_log_trace("stream %p: %p %zd %f", s, *data, *nbytes, *(float*)*data); return 0; } From c9ac5e6e1ef3b1bb9c0cb8a940913dded8890234 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 14 Dec 2018 16:39:19 +0100 Subject: [PATCH 059/116] context: report error only once --- src/context.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/context.c b/src/context.c index 545df2414..5b8eb9182 100644 --- a/src/context.c +++ b/src/context.c @@ -34,9 +34,10 @@ int pa_context_set_error(pa_context *c, int error) { pa_assert(error >= 0); pa_assert(error < PA_ERR_MAX); - pw_log_debug("context %p: error %d %s", c, error, pa_strerror(error)); - if (c) + if (c && c->error != error) { + pw_log_debug("context %p: error %d %s", c, error, pa_strerror(error)); c->error = error; + } return error; } From b177f1d146586f2134e1262d3fc6fd0679f84c47 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 14 Dec 2018 16:41:19 +0100 Subject: [PATCH 060/116] context: small cleanups --- src/context.c | 52 ++++++++++++++++++++++++++++++++----------------- src/operation.c | 1 + src/stream.c | 4 +++- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/context.c b/src/context.c index 5b8eb9182..1eb111dd6 100644 --- a/src/context.c +++ b/src/context.c @@ -44,15 +44,17 @@ int pa_context_set_error(pa_context *c, int error) { static void context_unlink(pa_context *c) { pa_stream *s, *t; + pa_operation *o; + + pw_log_debug("context %p: unlink %d", c, c->state); spa_list_for_each_safe(s, t, &c->streams, link) { pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED); - pa_stream_unref(s); } - while(!spa_list_is_empty(&c->operations)) - pa_operation_cancel(spa_list_first(&c->operations, pa_operation, link)); + spa_list_consume(o, &c->operations, link) + pa_operation_cancel(o); } void pa_context_set_state(pa_context *c, pa_context_state_t st) { @@ -62,6 +64,8 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) { if (c->state == st) return; + pw_log_debug("context %p: state %d", c, st); + pa_context_ref(c); c->state = st; @@ -79,6 +83,8 @@ static void context_fail(pa_context *c, int error) { pa_assert(c); pa_assert(c->refcount >= 1); + pw_log_debug("context %p: error %d", c, error); + pa_context_set_error(c, error); pa_context_set_state(c, PA_CONTEXT_FAILED); } @@ -140,7 +146,7 @@ struct global *pa_context_find_linked(pa_context *c, uint32_t idx) continue; if (f->mask & PA_SUBSCRIPTION_MASK_DSP) { if (!(f->mask & PA_SUBSCRIPTION_MASK_SOURCE) || - g->link_info.dst->parent_id != idx) + g->link_info.dst->parent_id != idx) f = pa_context_find_global(c, f->dsp_info.session); } return f; @@ -148,6 +154,19 @@ struct global *pa_context_find_linked(pa_context *c, uint32_t idx) return NULL; } +static void emit_event(pa_context *c, struct global *g, pa_subscription_event_type_t event) +{ + if (c->subscribe_mask & g->mask) { + if (c->subscribe_callback) { + pw_log_debug("context %p: obj %d: emit %d:%d", c, g->id, event, g->event); + c->subscribe_callback(c, + event | g->event, + g->id, + c->subscribe_userdata); + } + } +} + static int set_mask(pa_context *c, struct global *g) { const char *str; @@ -174,6 +193,7 @@ static int set_mask(pa_context *c, struct global *g) return 0; if (strcmp(str, "Audio/Sink") == 0) { + pw_log_debug("found sink %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_SINK; g->event = PA_SUBSCRIPTION_EVENT_SINK; g->node_info.monitor = SPA_ID_INVALID; @@ -181,6 +201,7 @@ static int set_mask(pa_context *c, struct global *g) else if (strcmp(str, "Audio/DSP/Playback") == 0) { if ((str = pw_properties_get(g->props, "node.session")) == NULL) return 0; + pw_log_debug("found monitor %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_DSP_SINK | PA_SUBSCRIPTION_MASK_SOURCE; g->event = PA_SUBSCRIPTION_EVENT_SOURCE; g->dsp_info.session = pw_properties_parse_int(str); @@ -188,6 +209,7 @@ static int set_mask(pa_context *c, struct global *g) f->node_info.monitor = g->id; } else if (strcmp(str, "Audio/Source") == 0) { + pw_log_debug("found source %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_SOURCE; g->event = PA_SUBSCRIPTION_EVENT_SOURCE; } @@ -198,21 +220,25 @@ static int set_mask(pa_context *c, struct global *g) g->dsp_info.session = pw_properties_parse_int(str); } else if (strcmp(str, "Stream/Output/Audio") == 0) { + pw_log_debug("found sink input %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_SINK_INPUT; g->event = PA_SUBSCRIPTION_EVENT_SINK_INPUT; } else if (strcmp(str, "Stream/Input/Audio") == 0) { + pw_log_debug("found source output %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT; g->event = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT; } break; case PW_TYPE_INTERFACE_Module: + pw_log_debug("found module %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_MODULE; g->event = PA_SUBSCRIPTION_EVENT_MODULE; break; case PW_TYPE_INTERFACE_Client: + pw_log_debug("found client %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_CLIENT; g->event = PA_SUBSCRIPTION_EVENT_CLIENT; break; @@ -262,13 +288,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, pw_log_debug("mask %d/%d", g->mask, g->event); spa_list_append(&c->globals, &g->link); - if (c->subscribe_mask & g->mask) { - if (c->subscribe_callback) - c->subscribe_callback(c, - PA_SUBSCRIPTION_EVENT_NEW | g->event, - g->id, - c->subscribe_userdata); - } + emit_event(c, g, PA_SUBSCRIPTION_EVENT_NEW); } static void registry_event_global_remove(void *object, uint32_t id) @@ -280,13 +300,7 @@ static void registry_event_global_remove(void *object, uint32_t id) if ((g = pa_context_find_global(c, id)) == NULL) return; - if (c->subscribe_mask & g->mask) { - if (c->subscribe_callback) - c->subscribe_callback(c, - PA_SUBSCRIPTION_EVENT_REMOVE | g->event, - g->id, - c->subscribe_userdata); - } + emit_event(c, g, PA_SUBSCRIPTION_EVENT_REMOVE); pw_log_debug("context %p: free %d %p", c, id, g); spa_list_remove(&g->link); @@ -430,6 +444,8 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * static void context_free(pa_context *c) { + pw_log_debug("context %p: free", c); + context_unlink(c); if (c->proplist) diff --git a/src/operation.c b/src/operation.c index 99ee48531..572d3817b 100644 --- a/src/operation.c +++ b/src/operation.c @@ -129,6 +129,7 @@ void pa_operation_cancel(pa_operation *o) { pa_assert(o); pa_assert(o->refcount >= 1); + pw_log_debug("%p %d", o, o->seq); operation_set_state(o, PA_OPERATION_CANCELED); } diff --git a/src/stream.c b/src/stream.c index d17c0f0d3..233d85707 100644 --- a/src/stream.c +++ b/src/stream.c @@ -922,7 +922,7 @@ int pa_stream_connect_record( static void on_disconnected(pa_operation *o, void *userdata) { - pa_stream_set_state(o->stream, PA_STREAM_TERMINATED); + pa_stream_set_state(o->stream, PA_STREAM_TERMINATED); } int pa_stream_disconnect(pa_stream *s) @@ -934,6 +934,8 @@ int pa_stream_disconnect(pa_stream *s) PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + pw_log_debug("stream %p: disconnect", s); + s->disconnecting = true; pw_stream_disconnect(s->stream); o = pa_operation_new(s->context, s, on_disconnected, 0); From 35edefdaafde091f3c5b257e8c5cfeabc71c180c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 14 Dec 2018 16:42:01 +0100 Subject: [PATCH 061/116] context: emit change event when nodes are linked --- src/context.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/context.c b/src/context.c index 1eb111dd6..1cba5886c 100644 --- a/src/context.c +++ b/src/context.c @@ -258,8 +258,17 @@ static int set_mask(pa_context *c, struct global *g) if (g->link_info.src == NULL || g->link_info.dst == NULL) return 0; - pw_log_debug("link %d->%d", g->link_info.src->parent_id, - g->link_info.dst->parent_id); + pw_log_debug("link %d:%d->%d:%d", + g->link_info.src->parent_id, + g->link_info.src->id, + g->link_info.dst->parent_id, + g->link_info.dst->id); + + if ((f = pa_context_find_global(c, g->link_info.src->parent_id)) != NULL) + emit_event(c, f, PA_SUBSCRIPTION_EVENT_CHANGE); + if ((f = pa_context_find_global(c, g->link_info.dst->parent_id)) != NULL) + emit_event(c, f, PA_SUBSCRIPTION_EVENT_CHANGE); + break; default: From 6db8e0525681f998964fc2347d8b05866c4eca6b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 14 Dec 2018 16:42:36 +0100 Subject: [PATCH 062/116] introspect: implement more --- src/introspect.c | 106 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index ec3809436..46dfe8c80 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -1259,8 +1259,33 @@ pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name, const char*profile, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct card_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_CARD, name)) == NULL) + return NULL; + + ensure_global(c, g); + + pw_log_debug("Card set profile %s", profile); + + o = pa_operation_new(c, NULL, card_profile, sizeof(struct card_data)); + d = o->userdata; + d->context = c; + d->success_cb = cb; + d->userdata = userdata; + d->global = g; + d->profile = strdup(profile); + pa_operation_sync(o); + + return o; } pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card_name, const char *port_name, int64_t offset, pa_context_success_cb_t cb, void *userdata) @@ -1732,14 +1757,83 @@ pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_stream *s; + struct global *g; + pa_operation *o; + struct success_ack *d; + float v; + + v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; + + pw_log_debug("contex %p: index %d volume %f", c, idx, v); + + if ((s = find_stream(c, idx)) == NULL) { + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)) + return NULL; + } + + if (s) { + s->volume = v; + pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); + } + else if (g) { + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + + pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_volume, &SPA_POD_Float(v), + 0)); + } + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_stream *s; + struct global *g; + pa_operation *o; + struct success_ack *d; + + if ((s = find_stream(c, idx)) == NULL) { + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)) + return NULL; + } + + if (s) { + s->mute = mute; + pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); + } + else if (g) { + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + + pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_mute, &SPA_POD_Bool(mute), + 0)); + } + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; } pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) From 21c11e70fb6a7c5e2905265e1b37315df9e5fd65 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 14 Dec 2018 16:42:57 +0100 Subject: [PATCH 063/116] operation: keep ref on stream --- src/operation.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/operation.c b/src/operation.c index 572d3817b..651023cb0 100644 --- a/src/operation.c +++ b/src/operation.c @@ -35,7 +35,7 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb o->refcount = 1; o->context = c; - o->stream = s; + o->stream = s ? pa_stream_ref(s) : NULL; o->seq = SPA_ID_INVALID; o->state = PA_OPERATION_RUNNING; @@ -86,7 +86,8 @@ static void operation_unlink(pa_operation *o) { o->context = NULL; } - + if (o->stream) + pa_stream_unref(o->stream); o->stream = NULL; o->callback = NULL; o->userdata = NULL; From 41513f11f3c0516d3b4cd00934a612239e69c9e4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 14 Dec 2018 16:43:34 +0100 Subject: [PATCH 064/116] context: improve cleanup --- src/context.c | 2 ++ src/stream.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/context.c b/src/context.c index 1cba5886c..9d5335fce 100644 --- a/src/context.c +++ b/src/context.c @@ -459,6 +459,8 @@ static void context_free(pa_context *c) if (c->proplist) pa_proplist_free(c->proplist); + + pw_remote_destroy(c->remote); } void pa_context_unref(pa_context *c) diff --git a/src/stream.c b/src/stream.c index 233d85707..281d07816 100644 --- a/src/stream.c +++ b/src/stream.c @@ -210,13 +210,20 @@ static void stream_state_changed(void *data, enum pw_stream_state old, { pa_stream *s = data; + pw_log_debug("stream %p: state '%s'->'%s'", s, pw_stream_state_as_string(old), + pw_stream_state_as_string(state)); + switch(state) { case PW_STREAM_STATE_ERROR: pa_stream_set_state(s, PA_STREAM_FAILED); break; case PW_STREAM_STATE_UNCONNECTED: - if (!s->disconnecting) - pa_stream_set_state(s, PA_STREAM_UNCONNECTED); + if (!s->disconnecting) { + pa_context_set_error(s->context, PA_ERR_KILLED); + pa_stream_set_state(s, PA_STREAM_FAILED); + } else { + pa_stream_set_state(s, PA_STREAM_TERMINATED); + } break; case PW_STREAM_STATE_CONNECTING: pa_stream_set_state(s, PA_STREAM_CREATING); @@ -225,9 +232,21 @@ static void stream_state_changed(void *data, enum pw_stream_state old, case PW_STREAM_STATE_READY: break; case PW_STREAM_STATE_PAUSED: - configure_device(s); - configure_buffers(s); - pa_stream_set_state(s, PA_STREAM_READY); + if (old < PW_STREAM_STATE_PAUSED) { + if (s->suspended && s->suspended_callback) { + s->suspended = false; + s->suspended_callback(s, s->suspended_userdata); + } + configure_device(s); + configure_buffers(s); + pa_stream_set_state(s, PA_STREAM_READY); + } + else { + if (!s->suspended && s->suspended_callback) { + s->suspended = true; + s->suspended_callback(s, s->suspended_userdata); + } + } break; case PW_STREAM_STATE_STREAMING: break; @@ -596,13 +615,36 @@ pa_stream *pa_stream_new_extended(pa_context *c, const char *name, static void stream_unlink(pa_stream *s) { + pa_context *c = s->context; + pa_operation *o, *t; + + if (c == NULL) + return; + + pw_log_debug("stream %p: unlink %d", s, s->refcount); + + spa_list_for_each_safe(o, t, &c->operations, link) { + if (o->stream == s) + pa_operation_cancel(o); + } + spa_list_remove(&s->link); + + s->context = NULL; + pa_stream_unref(s); } static void stream_free(pa_stream *s) { int i; + pw_log_debug("stream %p", s); + + if (s->stream) { + spa_hook_remove(&s->stream_listener); + pw_stream_destroy(s->stream); + } + if (s->proplist) pa_proplist_free(s->proplist); From 96205687c514c0440739e3f5cf900998df3244de Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 17 Dec 2018 13:27:27 +0100 Subject: [PATCH 065/116] stream: make sure we have a context Make sure we ref the stream because else it would be freed when disconnected and we can't get the context anymore. --- src/stream.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/stream.c b/src/stream.c index 281d07816..ef74f3fa0 100644 --- a/src/stream.c +++ b/src/stream.c @@ -970,19 +970,23 @@ static void on_disconnected(pa_operation *o, void *userdata) int pa_stream_disconnect(pa_stream *s) { pa_operation *o; + pa_context *c = s->context; spa_assert(s); spa_assert(s->refcount >= 1); - PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); pw_log_debug("stream %p: disconnect", s); + pa_stream_ref(s); s->disconnecting = true; pw_stream_disconnect(s->stream); - o = pa_operation_new(s->context, s, on_disconnected, 0); + + o = pa_operation_new(c, s, on_disconnected, 0); pa_operation_sync(o); pa_operation_unref(o); + pa_stream_unref(s); return 0; } From 3d34acd2f2e1cf9bd950a27c6b4f37d67745ece0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 20 Dec 2018 12:46:35 +0100 Subject: [PATCH 066/116] introspect: fix profiles free --- src/introspect.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index 46dfe8c80..ea1c3cee7 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -205,12 +205,12 @@ static void client_destroy(void *data) static void device_destroy(void *data) { struct global *global = data; - struct spa_pod *profile; + struct spa_pod **profile; if (global->card_info.info.proplist) pa_proplist_free(global->card_info.info.proplist); pw_array_for_each(profile, &global->card_info.profiles) - free(profile); + free(*profile); pw_array_clear(&global->card_info.profiles); if (global->info) pw_device_info_free(global->info); From 7c18cc431f687856ae3136542df4b3e1e4d2123d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 20 Dec 2018 13:07:53 +0100 Subject: [PATCH 067/116] json: fix free of arrays --- src/json.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/json.c b/src/json.c index b242e21b2..9444a607b 100644 --- a/src/json.c +++ b/src/json.c @@ -53,9 +53,9 @@ struct pa_json_object { static void clear_array(struct pw_array *array) { - pa_json_object *value; + pa_json_object **value; pw_array_for_each(value, array) - pa_json_object_free(value); + pa_json_object_free(*value); pw_array_clear(array); } From 3d95ea4d13faa3feda2c60af1c27c4af55eb517a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 20 Dec 2018 13:08:09 +0100 Subject: [PATCH 068/116] introspect: set sink/source flags --- src/introspect.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index ea1c3cee7..75279239d 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -351,7 +351,10 @@ static void sink_callback(struct sink_data *d) i.monitor_source_name = "unknown"; i.latency = 0; i.driver = "PipeWire"; - i.flags = 0; + i.flags = PA_SINK_HARDWARE | + PA_SINK_HW_VOLUME_CTRL | PA_SINK_HW_MUTE_CTRL | + PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY | + PA_SINK_DECIBEL_VOLUME; i.proplist = pa_proplist_new_dict(info->props); i.configured_latency = 0; i.base_volume = PA_VOLUME_NORM; @@ -555,6 +558,10 @@ static void source_callback(struct source_data *d) pa_source_info i; pa_format_info ii[1]; pa_format_info *ip[1]; + enum pa_sink_flags flags; + + flags = PA_SOURCE_LATENCY | PA_SOURCE_DYNAMIC_LATENCY | + PA_SOURCE_DECIBEL_VOLUME; spa_zero(i); i.name = info->name; @@ -573,10 +580,11 @@ static void source_callback(struct source_data *d) } else { i.monitor_of_sink = PA_INVALID_INDEX; i.monitor_of_sink_name = NULL; + flags |= PA_SOURCE_HARDWARE | PA_SOURCE_HW_VOLUME_CTRL | PA_SOURCE_HW_MUTE_CTRL; } i.latency = 0; i.driver = "PipeWire"; - i.flags = 0; + i.flags = flags; i.proplist = pa_proplist_new_dict(info->props); i.configured_latency = 0; i.base_volume = PA_VOLUME_NORM; From 9062145e138171034884ed3b8368a4863ae0cdbe Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 4 Jan 2019 10:00:57 +0100 Subject: [PATCH 069/116] implement more API Implement the sink/source volume/mute api --- src/context.c | 53 +++++++-- src/internal.h | 11 ++ src/introspect.c | 300 +++++++++++++++++++++++++++++++++++++---------- src/stream.c | 5 +- 4 files changed, 295 insertions(+), 74 deletions(-) diff --git a/src/context.c b/src/context.c index 9d5335fce..7f51123be 100644 --- a/src/context.c +++ b/src/context.c @@ -662,7 +662,6 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su o = pa_operation_new(c, NULL, on_success, sizeof(struct success_data)); d = o->userdata; - d->ret = PA_ERR_ACCESS; d->cb = cb; d->userdata = userdata; pa_operation_sync(o); @@ -700,14 +699,45 @@ uint32_t pa_context_get_server_protocol_version(pa_context *c) pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct success_data *d; + + spa_assert(c); + spa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, mode == PA_UPDATE_SET || + mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + pa_proplist_update(c->proplist, mode, p); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_data)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + return o; } pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_data *d; + + spa_assert(c); + spa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, keys && keys[0], PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + pw_log_warn("Not Implemented"); - return NULL; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_data)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + return o; } uint32_t pa_context_get_index(pa_context *c) @@ -749,12 +779,19 @@ void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss) { - pw_log_warn("Not Implemented"); - return 1024; + size_t fs, mbs; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_ANY(c, !ss || pa_sample_spec_valid(ss), PA_ERR_INVALID, (size_t) -1); + + fs = ss ? pa_frame_size(ss) : 1; + mbs = PA_ROUND_DOWN(4096, fs); + return PA_MAX(mbs, fs); } int pa_context_load_cookie_from_file(pa_context *c, const char *cookie_file_path) { - pw_log_warn("Not Implemented"); - return -ENOTSUP; + return 0; } diff --git a/src/internal.h b/src/internal.h index ab99ec20e..9e24df761 100644 --- a/src/internal.h +++ b/src/internal.h @@ -81,6 +81,17 @@ extern "C" { #define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x))) #endif +#ifdef __GNUC__ +#define PA_ROUND_DOWN(a, b) \ + __extension__ ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + (_a / _b) * _b; \ + }) +#else +#define PA_ROUND_DOWN(a, b) (((a) / (b)) * (b)) +#endif + #define pa_init_i18n() #define _(String) (String) diff --git a/src/introspect.c b/src/introspect.c index 75279239d..63c411586 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -478,28 +478,152 @@ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, return o; } +static void set_node_volume(pa_context *c, struct global *g, const pa_cvolume *volume) +{ + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + float v; + + v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; + + ensure_global(c, g); + + pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_volume, &SPA_POD_Float(v), + 0)); +} + +static void set_node_mute(pa_context *c, struct global *g, bool mute) +{ + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + + ensure_global(c, g); + + pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_mute, &SPA_POD_Bool(mute), + 0)); +} + + pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented %d", idx); - return NULL; + pa_operation *o; + struct global *g; + struct success_ack *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + pw_log_debug("context %p: index %d", c, idx); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) + return NULL; + + set_node_volume(c, g, volume); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + return o; } pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented %s", name); - return NULL; + pa_operation *o; + struct global *g; + struct success_ack *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); + + pw_log_debug("context %p: name %s", c, name); + + if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) + return NULL; + + set_node_volume(c, g, volume); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + return o; } pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented %d", mute); - return NULL; + pa_operation *o; + struct global *g; + struct success_ack *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + pw_log_debug("context %p: index %d", c, idx); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) + return NULL; + + set_node_mute(c, g, mute); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + return o; } pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented %s", name); - return NULL; + pa_operation *o; + struct global *g; + struct success_ack *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + pw_log_debug("context %p: name %s", c, name); + + if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) + return NULL; + + set_node_mute(c, g, mute); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + return o; } pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) @@ -709,26 +833,116 @@ pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct success_ack *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + pw_log_debug("context %p: index %d", c, idx); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) + return NULL; + + set_node_volume(c, g, volume); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + return o; } pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct success_ack *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID); + + pw_log_debug("context %p: name %s", c, name); + + if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) + return NULL; + + set_node_volume(c, g, volume); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + return o; } pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct success_ack *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + pw_log_debug("context %p: index %d", c, idx); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) + return NULL; + + set_node_mute(c, g, mute); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + return o; } pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct success_ack *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + pw_log_debug("context %p: name %s", c, name); + + if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) + return NULL; + + set_node_mute(c, g, mute); + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + return o; } pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) @@ -1489,11 +1703,8 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons struct global *g; pa_operation *o; struct success_ack *d; - float v; - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - - pw_log_debug("contex %p: index %d volume %f", c, idx, v); + pw_log_debug("contex %p: index %d", c, idx); if ((s = find_stream(c, idx)) == NULL) { if ((g = pa_context_find_global(c, idx)) == NULL) @@ -1503,19 +1714,11 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons } if (s) { - s->volume = v; + s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); } else if (g) { - char buf[1024]; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - - pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, - SPA_PARAM_Props, 0, - spa_pod_builder_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_volume, &SPA_POD_Float(v), - 0)); + set_node_volume(c, g, volume); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1545,15 +1748,7 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); } else if (g) { - char buf[1024]; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - - pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, - SPA_PARAM_Props, 0, - spa_pod_builder_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_mute, &SPA_POD_Bool(mute), - 0)); + set_node_mute(c, g, mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1769,11 +1964,8 @@ pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, c struct global *g; pa_operation *o; struct success_ack *d; - float v; - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - - pw_log_debug("contex %p: index %d volume %f", c, idx, v); + pw_log_debug("contex %p: index %d", c, idx); if ((s = find_stream(c, idx)) == NULL) { if ((g = pa_context_find_global(c, idx)) == NULL) @@ -1783,19 +1975,11 @@ pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, c } if (s) { - s->volume = v; + s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); } else if (g) { - char buf[1024]; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - - pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, - SPA_PARAM_Props, 0, - spa_pod_builder_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_volume, &SPA_POD_Float(v), - 0)); + set_node_volume(c, g, volume); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1825,15 +2009,7 @@ pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); } else if (g) { - char buf[1024]; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - - pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, - SPA_PARAM_Props, 0, - spa_pod_builder_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_mute, &SPA_POD_Bool(mute), - 0)); + set_node_mute(c, g, mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; diff --git a/src/stream.c b/src/stream.c index ef74f3fa0..073828b53 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1733,7 +1733,6 @@ pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_ { pa_operation *o; struct success_ack *d; - char *str; spa_assert(s); spa_assert(s->refcount >= 1); @@ -1743,9 +1742,7 @@ pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - str = pa_proplist_to_string(p); - pw_log_warn("Not Implemented %s", str); - free(str); + pa_proplist_update(s->proplist, mode, p); o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); d = o->userdata; From 89fb73a949d4ceb1c4d41269e29ffade8bc3e386 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 7 Jan 2019 15:04:34 +0100 Subject: [PATCH 070/116] fix signed and unsigned comparisons --- src/internal.h | 2 +- src/introspect.c | 7 ++++--- src/mainloop-glib.c | 4 +++- src/proplist.c | 4 ++-- src/stream.c | 29 +++++++++++++++-------------- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/internal.h b/src/internal.h index 9e24df761..c1d2193b4 100644 --- a/src/internal.h +++ b/src/internal.h @@ -294,7 +294,7 @@ struct global *pa_context_find_global(pa_context *c, uint32_t id); struct global *pa_context_find_global_by_name(pa_context *c, uint32_t mask, const char *name); struct global *pa_context_find_linked(pa_context *c, uint32_t id); -#define MAX_BUFFERS 64 +#define MAX_BUFFERS 64u #define MASK_BUFFERS (MAX_BUFFERS-1) struct pa_stream { diff --git a/src/introspect.c b/src/introspect.c index 63c411586..2a4192d05 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -1404,8 +1404,9 @@ static void card_profile(pa_operation *o, void *userdata) struct card_data *d = userdata; struct global *g = d->global; pa_context *c = d->context; - int res = 0, n_profiles; - uint32_t i, id = SPA_ID_INVALID; + size_t i, n_profiles; + int res = 0; + uint32_t id = SPA_ID_INVALID; char buf[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); struct spa_pod **profiles; @@ -1421,7 +1422,7 @@ static void card_profile(pa_operation *o, void *userdata) ":", SPA_PARAM_PROFILE_id, "i", &test_id, ":", SPA_PARAM_PROFILE_name, "s", &name, NULL) < 0) { - pw_log_warn("device %d: can't parse profile %d", g->id, i); + pw_log_warn("device %d: can't parse profile %zd", g->id, i); continue; } if (strcmp(name, d->profile) == 0) { diff --git a/src/mainloop-glib.c b/src/mainloop-glib.c index 2cb0767c9..440bd083f 100644 --- a/src/mainloop-glib.c +++ b/src/mainloop-glib.c @@ -65,7 +65,9 @@ static GSourceFuncs source_funcs = source_prepare, NULL, source_dispatch, - NULL + NULL, + NULL, + NULL, }; pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) diff --git a/src/proplist.c b/src/proplist.c index 7aba67bed..dde50939d 100644 --- a/src/proplist.c +++ b/src/proplist.c @@ -158,7 +158,7 @@ int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t * void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other) { - int i; + uint32_t i; spa_assert(p); spa_assert(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE); @@ -324,7 +324,7 @@ int pa_proplist_isempty(pa_proplist *p) int pa_proplist_equal(pa_proplist *a, pa_proplist *b) { - int i; + uint32_t i; spa_assert(a); spa_assert(b); diff --git a/src/stream.c b/src/stream.c index 073828b53..19dab794a 100644 --- a/src/stream.c +++ b/src/stream.c @@ -53,14 +53,14 @@ static const uint32_t audio_formats[] = { static inline uint32_t format_pa2id(pa_stream *s, pa_sample_format_t format) { - if (format < 0 || format >= SPA_N_ELEMENTS(audio_formats)) + if (format < 0 || (size_t)format >= SPA_N_ELEMENTS(audio_formats)) return SPA_AUDIO_FORMAT_UNKNOWN; return audio_formats[format]; } static inline pa_sample_format_t format_id2pa(pa_stream *s, uint32_t id) { - int i; + size_t i; for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) { if (id == audio_formats[i]) return i; @@ -132,14 +132,14 @@ static const uint32_t audio_channels[] = { static inline uint32_t channel_pa2id(pa_stream *s, pa_channel_position_t channel) { - if (channel < 0 || channel >= SPA_N_ELEMENTS(audio_channels)) + if (channel < 0 || (size_t)channel >= SPA_N_ELEMENTS(audio_channels)) return SPA_AUDIO_CHANNEL_UNKNOWN; return audio_channels[channel]; } static inline pa_channel_position_t channel_id2pa(pa_stream *s, uint32_t id) { - int i; + size_t i; for (i = 0; i < SPA_N_ELEMENTS(audio_channels); i++) { if (id == audio_channels[i]) return i; @@ -179,7 +179,7 @@ static void dump_buffer_attr(pa_stream *s, pa_buffer_attr *attr) static void configure_buffers(pa_stream *s) { s->buffer_attr.maxlength = s->maxsize; - if (s->buffer_attr.prebuf == -1) + if (s->buffer_attr.prebuf == (uint32_t)-1) s->buffer_attr.prebuf = s->buffer_attr.minreq; s->buffer_attr.fragsize = s->buffer_attr.minreq; dump_buffer_attr(s, &s->buffer_attr); @@ -256,25 +256,25 @@ static void stream_state_changed(void *data, enum pw_stream_state old, static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *attr, struct spa_pod_builder *b) { const struct spa_pod *param; - int32_t blocks, buffers, size, maxsize, stride; + uint32_t blocks, buffers, size, maxsize, stride; blocks = 1; stride = pa_frame_size(&s->sample_spec); - if (attr->tlength == -1 || attr->tlength == 0) + if (attr->tlength == (uint32_t)-1 || attr->tlength == 0) maxsize = 1024; else maxsize = (attr->tlength / stride); - if (attr->minreq == -1 || attr->minreq == 0) + if (attr->minreq == (uint32_t)-1 || attr->minreq == 0) size = maxsize; else size = SPA_MIN(attr->minreq / stride, maxsize); - if (attr->maxlength == -1) + if (attr->maxlength == (uint32_t)-1) buffers = 3; else - buffers = SPA_CLAMP(attr->maxlength / (size * stride), 3, MAX_BUFFERS); + buffers = SPA_CLAMP(attr->maxlength / (size * stride), 3u, MAX_BUFFERS); pw_log_info("stream %p: stride %d maxsize %d size %u buffers %d", s, stride, maxsize, size, buffers); @@ -310,7 +310,7 @@ static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flag else if (s->n_formats == 1) pa_format_info_to_sample_spec(s->req_formats[0], &ss, NULL); - if ((ms = atoi(e)) < 0 || ms <= 0) { + if ((ms = atoi(e)) == 0) { pa_log_debug("Failed to parse $PULSE_LATENCY_MSEC: %s", e); } else if (!pa_sample_spec_valid(&s->sample_spec)) { @@ -354,7 +354,8 @@ static void stream_format_changed(void *data, const struct spa_pod *format) uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); struct spa_audio_info info = { 0 }; - int i, res; + int res; + unsigned int i; if (format == NULL) { res = 0; @@ -502,7 +503,7 @@ pa_stream* stream_new(pa_context *c, const char *name, { pa_stream *s; char str[1024]; - int i; + unsigned int i; struct pw_properties *props; spa_assert(c); @@ -1059,7 +1060,7 @@ int pa_stream_begin_write( else { size_t max = s->buffer_size - s->buffer_offset; *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); - *nbytes = *nbytes != -1 ? SPA_MIN(*nbytes, max) : max; + *nbytes = *nbytes != (size_t)-1 ? SPA_MIN(*nbytes, max) : max; } pw_log_trace("peek buffer %p %zd", *data, *nbytes); From bebfcf0190c507103d998744fdddd9eac624d526 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 7 Jan 2019 17:54:31 +0100 Subject: [PATCH 071/116] stream: don't use buffer id --- src/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream.c b/src/stream.c index 19dab794a..84214962f 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1028,7 +1028,7 @@ int queue_buffer(pa_stream *s) spa_ringbuffer_read_update(&s->dequeued_ring, s->buffer_index + 1); s->buffer->size = s->buffer->buffer->datas[0].chunk->size; - pw_log_trace("%d %"PRIu64"/%d", s->buffer->buffer->id, s->buffer->size, + pw_log_trace("%p %"PRIu64"/%d", s->buffer, s->buffer->size, s->buffer->buffer->datas[0].chunk->offset); pw_stream_queue_buffer(s->stream, s->buffer); From ac4ad7c8f1e36e178b7d4a230b50fd5aafba5524 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 8 Jan 2019 11:55:42 +0100 Subject: [PATCH 072/116] stream: avoid void * arithmetics --- src/stream.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream.c b/src/stream.c index 84214962f..121f5ed0b 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1143,7 +1143,7 @@ int pa_stream_write_ext_free(pa_stream *s, queue_buffer(s); } towrite -= dsize; - src += dsize; + src = SPA_MEMBER(src, dsize, void); } if (free_cb) free_cb(free_cb_data); @@ -1151,7 +1151,7 @@ int pa_stream_write_ext_free(pa_stream *s, s->buffer = NULL; } else { - s->buffer->buffer->datas[0].chunk->offset = data - s->buffer_data; + s->buffer->buffer->datas[0].chunk->offset = SPA_PTRDIFF(data, s->buffer_data); s->buffer->buffer->datas[0].chunk->size = nbytes; queue_buffer(s); } From 5680e29716f3d621fa25b26bfd60234778ec02b9 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 8 Jan 2019 17:30:57 +0100 Subject: [PATCH 073/116] pulse: add user_data to core --- src/context.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context.c b/src/context.c index 7f51123be..cd95be29a 100644 --- a/src/context.c +++ b/src/context.c @@ -419,7 +419,7 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * pw_properties_update(props, &p->props->dict); loop = mainloop->userdata; - core = pw_core_new(loop, NULL); + core = pw_core_new(loop, NULL, 0); r = pw_remote_new(core, props, sizeof(struct pa_context)); if (r == NULL) From 491a7679f884f30a845e05b279abbd9ef66ae328 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 8 Jan 2019 17:35:54 +0100 Subject: [PATCH 074/116] add _GNU_SOURCE --- src/introspect.c | 2 -- src/meson.build | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index 2a4192d05..b6f774d4a 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -17,8 +17,6 @@ * Boston, MA 02110-1301, USA. */ -#define _GNU_SOURCE - #include #include diff --git a/src/meson.build b/src/meson.build index 88ae3a5e2..d0955941d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -37,6 +37,7 @@ pipewire_mainloop_glib_sources = [ pipewire_pulseaudio_c_args = [ '-DHAVE_CONFIG_H', + '-D_GNU_SOURCE', '-DPIC', ] From 0de0de299a53fe8ab561d3eefffbe6bc66645777 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 9 Jan 2019 17:48:51 +0100 Subject: [PATCH 075/116] add const to info --- src/introspect.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index b6f774d4a..1921a9ff0 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -28,7 +28,7 @@ #include "internal.h" -static void node_event_info(void *object, struct pw_node_info *info) +static void node_event_info(void *object, const struct pw_node_info *info) { struct global *g = object; pw_log_debug("update %d", g->id); @@ -49,7 +49,7 @@ static const struct pw_node_proxy_events node_events = { .param = node_event_param, }; -static void module_event_info(void *object, struct pw_module_info *info) +static void module_event_info(void *object, const struct pw_module_info *info) { struct global *g = object; pa_module_info *i = &g->module_info.info; @@ -79,7 +79,7 @@ static const struct pw_module_proxy_events module_events = { .info = module_event_info, }; -static void client_event_info(void *object, struct pw_client_info *info) +static void client_event_info(void *object, const struct pw_client_info *info) { struct global *g = object; pa_client_info *i = &g->client_info.info; @@ -148,7 +148,7 @@ static void device_event_param(void *object, } } -static void device_event_info(void *object, struct pw_device_info *info) +static void device_event_info(void *object, const struct pw_device_info *info) { struct global *g = object; pa_card_info *i = &g->card_info.info; From 14573f120124c237637b3129dc3f031b17f492b0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 10 Jan 2019 09:31:37 +0100 Subject: [PATCH 076/116] pulse: use core_proxy to listen for done and info messages --- src/context.c | 59 ++++++++++++++++++++++++++++++------------------ src/internal.h | 4 +++- src/introspect.c | 2 +- 3 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/context.c b/src/context.c index cd95be29a..df1db4a7e 100644 --- a/src/context.c +++ b/src/context.c @@ -338,6 +338,38 @@ static void on_ready(pa_operation *o, void *userdata) pa_context_set_state(d->context, PA_CONTEXT_READY); } +static void complete_operations(pa_context *c, uint32_t seq) +{ + pa_operation *o, *t; + spa_list_for_each_safe(o, t, &c->operations, link) { + if (o->seq != seq) + continue; + pa_operation_ref(o); + if (o->callback) + o->callback(o, o->userdata); + pa_operation_unref(o); + } +} + +static void core_info(void *data, const struct pw_core_info *info) +{ + pa_context *c = data; + c->core_info = pw_core_info_update(c->core_info, info); +} + +static void core_done(void *data, uint32_t seq) +{ + pa_context *c = data; + pw_log_debug("done %d", seq); + complete_operations(c, seq); +} + +static const struct pw_core_proxy_events core_events = { + PW_VERSION_CORE_EVENTS, + .info = core_info, + .done = core_done +}; + static void remote_state_changed(void *data, enum pw_remote_state old, enum pw_remote_state state, const char *error) { @@ -359,6 +391,8 @@ static void remote_state_changed(void *data, enum pw_remote_state old, pa_context_set_state(c, PA_CONTEXT_SETTING_NAME); c->core_proxy = pw_remote_get_core_proxy(c->remote); + pw_core_proxy_add_listener(c->core_proxy, &c->core_listener, &core_events, c); + c->registry_proxy = pw_core_proxy_get_registry(c->core_proxy, PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0); @@ -375,30 +409,9 @@ static void remote_state_changed(void *data, enum pw_remote_state old, } } -static void complete_operations(pa_context *c, uint32_t seq) -{ - pa_operation *o, *t; - spa_list_for_each_safe(o, t, &c->operations, link) { - if (o->seq != seq) - continue; - pa_operation_ref(o); - if (o->callback) - o->callback(o, o->userdata); - pa_operation_unref(o); - } -} - -static void remote_sync_reply(void *data, uint32_t seq) -{ - pa_context *c = data; - pw_log_debug("done %d", seq); - complete_operations(c, seq); -} - static const struct pw_remote_events remote_events = { PW_VERSION_REMOTE_EVENTS, .state_changed = remote_state_changed, - .sync_reply = remote_sync_reply }; pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) @@ -459,6 +472,8 @@ static void context_free(pa_context *c) if (c->proplist) pa_proplist_free(c->proplist); + if (c->core_info) + pw_core_info_free(c->core_info); pw_remote_destroy(c->remote); } @@ -676,7 +691,7 @@ const char* pa_context_get_server(pa_context *c) pa_assert(c); pa_assert(c->refcount >= 1); - info = pw_remote_get_core_info(c->remote); + info = c->core_info; PA_CHECK_VALIDITY_RETURN_NULL(c, info && info->name, PA_ERR_NOENTITY); return info->name; diff --git a/src/internal.h b/src/internal.h index c1d2193b4..c8de58a60 100644 --- a/src/internal.h +++ b/src/internal.h @@ -260,7 +260,9 @@ struct pa_context { struct pw_remote *remote; struct spa_hook remote_listener; - struct pw_core_proxy *core_proxy; + struct pw_core_proxy *core_proxy; + struct spa_hook core_listener; + struct pw_core_info *core_info; struct pw_registry_proxy *registry_proxy; struct spa_hook registry_listener; diff --git a/src/introspect.c b/src/introspect.c index 1921a9ff0..10e0e2341 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -977,7 +977,7 @@ struct server_data { static void server_callback(struct server_data *d) { pa_context *c = d->context; - const struct pw_core_info *info = pw_remote_get_core_info(c->remote); + const struct pw_core_info *info = c->core_info; pa_server_info i; spa_zero(i); From f509e8552d1836268c6f85a3d8e78aadcdfec197 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 14 Jan 2019 13:01:47 +0100 Subject: [PATCH 077/116] pulse: improve includes --- src/context.c | 6 +----- src/internal.h | 1 + src/introspect.c | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/context.c b/src/context.c index df1db4a7e..1d6813d6c 100644 --- a/src/context.c +++ b/src/context.c @@ -19,11 +19,7 @@ #include -#include -#include -#include -#include -#include +#include #include #include diff --git a/src/internal.h b/src/internal.h index c8de58a60..bf806424d 100644 --- a/src/internal.h +++ b/src/internal.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include diff --git a/src/introspect.c b/src/introspect.c index 10e0e2341..3e6535ac2 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -21,8 +21,7 @@ #include -#include -#include +#include #include From 386e5c966c7c44086cb81e3649e7d58193cde43d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 16 Jan 2019 11:04:22 +0100 Subject: [PATCH 078/116] pulse: update to new pod api --- src/introspect.c | 45 +++++++++++++++++++++------------------------ src/stream.c | 13 ++++++------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index 3e6535ac2..c7b3fc589 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -118,10 +118,10 @@ static void device_event_param(void *object, uint32_t id; const char *name; - if (spa_pod_object_parse(param, - ":", SPA_PARAM_PROFILE_id, "i", &id, - ":", SPA_PARAM_PROFILE_name, "s", &name, - NULL) < 0) { + if (spa_pod_parse_object(param, + SPA_TYPE_OBJECT_ParamProfile, NULL, + SPA_PARAM_PROFILE_id, SPA_POD_Int(&id), + SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { pw_log_warn("device %d: can't parse profile", g->id); return; } @@ -132,9 +132,9 @@ static void device_event_param(void *object, case SPA_PARAM_Profile: { uint32_t id; - if (spa_pod_object_parse(param, - ":", SPA_PARAM_PROFILE_id, "i", &id, - NULL) < 0) { + if (spa_pod_parse_object(param, + SPA_TYPE_OBJECT_ParamProfile, NULL, + SPA_PARAM_PROFILE_id, SPA_POD_Int(&id)) < 0) { pw_log_warn("device %d: can't parse profile", g->id); return; } @@ -487,10 +487,9 @@ static void set_node_volume(pa_context *c, struct global *g, const pa_cvolume *v pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, SPA_PARAM_Props, 0, - spa_pod_builder_object(&b, + spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_volume, &SPA_POD_Float(v), - 0)); + SPA_PROP_volume, SPA_POD_Float(v))); } static void set_node_mute(pa_context *c, struct global *g, bool mute) @@ -502,10 +501,9 @@ static void set_node_mute(pa_context *c, struct global *g, bool mute) pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, SPA_PARAM_Props, 0, - spa_pod_builder_object(&b, + spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_mute, &SPA_POD_Bool(mute), - 0)); + SPA_PROP_mute, SPA_POD_Bool(mute))); } @@ -1261,10 +1259,10 @@ static void card_callback(struct card_data *d) uint32_t id; const char *name; - if (spa_pod_object_parse(profiles[j], - ":", SPA_PARAM_PROFILE_id, "i", &id, - ":", SPA_PARAM_PROFILE_name, "s", &name, - NULL) < 0) { + if (spa_pod_parse_object(profiles[j], + SPA_TYPE_OBJECT_ParamProfile, NULL, + SPA_PARAM_PROFILE_id, SPA_POD_Int(&id), + SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { pw_log_warn("device %d: can't parse profile %d", g->id, j); continue; } @@ -1415,10 +1413,10 @@ static void card_profile(pa_operation *o, void *userdata) uint32_t test_id; const char *name; - if (spa_pod_object_parse(profiles[i], - ":", SPA_PARAM_PROFILE_id, "i", &test_id, - ":", SPA_PARAM_PROFILE_name, "s", &name, - NULL) < 0) { + if (spa_pod_parse_object(profiles[i], + SPA_TYPE_OBJECT_ParamProfile, NULL, + SPA_PARAM_PROFILE_id, SPA_POD_Int(&test_id), + SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { pw_log_warn("device %d: can't parse profile %zd", g->id, i); continue; } @@ -1432,10 +1430,9 @@ static void card_profile(pa_operation *o, void *userdata) pw_device_proxy_set_param((struct pw_device_proxy*)g->proxy, SPA_PARAM_Profile, 0, - spa_pod_builder_object(&b, + spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, - SPA_PARAM_PROFILE_id, &SPA_POD_Int(id), - 0)); + SPA_PARAM_PROFILE_id, SPA_POD_Int(id))); res = 1; done: if (d->success_cb) diff --git a/src/stream.c b/src/stream.c index 121f5ed0b..b4e71941f 100644 --- a/src/stream.c +++ b/src/stream.c @@ -279,17 +279,16 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att pw_log_info("stream %p: stride %d maxsize %d size %u buffers %d", s, stride, maxsize, size, buffers); - param = spa_pod_builder_object(b, + param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - SPA_PARAM_BUFFERS_buffers, &SPA_POD_CHOICE_RANGE_Int(buffers, 3, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, &SPA_POD_Int(blocks), - SPA_PARAM_BUFFERS_size, &SPA_POD_CHOICE_RANGE_Int( + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(buffers, 3, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(blocks), + SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( size * stride, size * stride, maxsize * stride), - SPA_PARAM_BUFFERS_stride, &SPA_POD_Int(stride), - SPA_PARAM_BUFFERS_align, &SPA_POD_Int(16), - 0); + SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride), + SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); return param; } From 1fd2e6beaeda756843aa2adf81297232a2919446 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 18 Jan 2019 13:39:37 +0100 Subject: [PATCH 079/116] update for param api change --- src/introspect.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index c7b3fc589..21fcf3238 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -120,8 +120,8 @@ static void device_event_param(void *object, if (spa_pod_parse_object(param, SPA_TYPE_OBJECT_ParamProfile, NULL, - SPA_PARAM_PROFILE_id, SPA_POD_Int(&id), - SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { + SPA_PARAM_PROFILE_index, SPA_POD_Int(&id), + SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { pw_log_warn("device %d: can't parse profile", g->id); return; } @@ -134,7 +134,7 @@ static void device_event_param(void *object, uint32_t id; if (spa_pod_parse_object(param, SPA_TYPE_OBJECT_ParamProfile, NULL, - SPA_PARAM_PROFILE_id, SPA_POD_Int(&id)) < 0) { + SPA_PARAM_PROFILE_index, SPA_POD_Int(&id)) < 0) { pw_log_warn("device %d: can't parse profile", g->id); return; } @@ -1261,8 +1261,8 @@ static void card_callback(struct card_data *d) if (spa_pod_parse_object(profiles[j], SPA_TYPE_OBJECT_ParamProfile, NULL, - SPA_PARAM_PROFILE_id, SPA_POD_Int(&id), - SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { + SPA_PARAM_PROFILE_index, SPA_POD_Int(&id), + SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { pw_log_warn("device %d: can't parse profile %d", g->id, j); continue; } @@ -1415,8 +1415,8 @@ static void card_profile(pa_operation *o, void *userdata) if (spa_pod_parse_object(profiles[i], SPA_TYPE_OBJECT_ParamProfile, NULL, - SPA_PARAM_PROFILE_id, SPA_POD_Int(&test_id), - SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { + SPA_PARAM_PROFILE_index, SPA_POD_Int(&test_id), + SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { pw_log_warn("device %d: can't parse profile %zd", g->id, i); continue; } @@ -1432,7 +1432,7 @@ static void card_profile(pa_operation *o, void *userdata) SPA_PARAM_Profile, 0, spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, - SPA_PARAM_PROFILE_id, SPA_POD_Int(id))); + SPA_PARAM_PROFILE_index, SPA_POD_Int(id))); res = 1; done: if (d->success_cb) From 6cd1104d0c9fb0213f371e7b4b12925d70e2f2d7 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 30 Jan 2019 15:24:07 +0100 Subject: [PATCH 080/116] context: fix some memory leaks --- src/context.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/context.c b/src/context.c index 1d6813d6c..4215e4140 100644 --- a/src/context.c +++ b/src/context.c @@ -37,9 +37,20 @@ int pa_context_set_error(pa_context *c, int error) { return error; } +static void global_free(struct global *g) +{ + spa_list_remove(&g->link); + if (g->props) + pw_properties_free(g->props); + if (g->destroy) + g->destroy(g); + free(g); +} + static void context_unlink(pa_context *c) { pa_stream *s, *t; + struct global *g; pa_operation *o; pw_log_debug("context %p: unlink %d", c, c->state); @@ -48,6 +59,8 @@ static void context_unlink(pa_context *c) pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED); } + spa_list_consume(g, &c->globals, link) + global_free(g); spa_list_consume(o, &c->operations, link) pa_operation_cancel(o); @@ -286,13 +299,14 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, g->parent_id = parent_id; g->type = type; g->props = props ? pw_properties_new_dict(props) : NULL; - - if (set_mask(c, g) == 0) - return; - - pw_log_debug("mask %d/%d", g->mask, g->event); spa_list_append(&c->globals, &g->link); + if (set_mask(c, g) == 0) { + global_free(g); + return; + } + + pw_log_debug("mask %d/%d", g->mask, g->event); emit_event(c, g, PA_SUBSCRIPTION_EVENT_NEW); } @@ -308,12 +322,7 @@ static void registry_event_global_remove(void *object, uint32_t id) emit_event(c, g, PA_SUBSCRIPTION_EVENT_REMOVE); pw_log_debug("context %p: free %d %p", c, id, g); - spa_list_remove(&g->link); - if (g->props) - pw_properties_free(g->props); - if (g->destroy) - g->destroy(g); - free(g); + global_free(g); } static const struct pw_registry_proxy_events registry_events = @@ -471,7 +480,7 @@ static void context_free(pa_context *c) if (c->core_info) pw_core_info_free(c->core_info); - pw_remote_destroy(c->remote); + pw_core_destroy(c->core); } void pa_context_unref(pa_context *c) From 5f9200d9ee58271e60ff24e91e07fa340d71d494 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 6 Feb 2019 13:23:43 +0100 Subject: [PATCH 081/116] pulse: add SPA_EXPORT --- src/channelmap.c | 21 ++++++++++++++ src/context.c | 27 ++++++++++++++++++ src/core-format.c | 7 +++++ src/direction.c | 3 ++ src/error.c | 1 + src/ext-device-manager.c | 8 ++++++ src/ext-device-restore.c | 6 ++++ src/ext-stream-restore.c | 6 ++++ src/format.c | 28 +++++++++++++++++++ src/introspect.c | 60 ++++++++++++++++++++++++++++++++++++++++ src/json.c | 11 ++++++++ src/mainloop-glib.c | 3 ++ src/mainloop-signal.c | 5 ++++ src/mainloop.c | 12 ++++++++ src/operation.c | 5 ++++ src/proplist.c | 22 +++++++++++++++ src/rtclock.c | 1 + src/sample.c | 18 ++++++++++++ src/scache.c | 5 ++++ src/stream.c | 57 +++++++++++++++++++++++++++++++++++++- src/subscribe.c | 2 ++ src/thread-mainloop.c | 13 +++++++++ src/timeval.c | 8 ++++++ src/utf8.c | 8 ++++++ src/util.c | 7 +++++ src/version.c | 3 ++ src/volume.c | 44 +++++++++++++++++++++++++++++ src/xmalloc.c | 7 +++++ 28 files changed, 397 insertions(+), 1 deletion(-) diff --git a/src/channelmap.c b/src/channelmap.c index 588f0ea2d..825d564ba 100644 --- a/src/channelmap.c +++ b/src/channelmap.c @@ -159,6 +159,7 @@ const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = { [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = N_("Top Rear Right") }; +SPA_EXPORT pa_channel_map* pa_channel_map_init(pa_channel_map *m) { unsigned c; pa_assert(m); @@ -171,6 +172,7 @@ pa_channel_map* pa_channel_map_init(pa_channel_map *m) { return m; } +SPA_EXPORT pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) { pa_assert(m); @@ -181,6 +183,7 @@ pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) { return m; } +SPA_EXPORT pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) { pa_assert(m); @@ -192,6 +195,7 @@ pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) { return m; } +SPA_EXPORT pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) { pa_assert(m); pa_assert(pa_channels_valid(channels)); @@ -391,6 +395,7 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p } } +SPA_EXPORT pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) { unsigned c; @@ -420,6 +425,7 @@ pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, return NULL; } +SPA_EXPORT const char* pa_channel_position_to_string(pa_channel_position_t pos) { if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) @@ -428,6 +434,7 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos) { return table[pos]; } +SPA_EXPORT const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) { if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) @@ -438,6 +445,7 @@ const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) { return _(pretty_table[pos]); } +SPA_EXPORT int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) { unsigned c; @@ -461,6 +469,7 @@ int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) { return 1; } +SPA_EXPORT char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { unsigned channel; bool first = true; @@ -491,6 +500,7 @@ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { return s; } +SPA_EXPORT pa_channel_position_t pa_channel_position_from_string(const char *p) { pa_channel_position_t i; pa_assert(p); @@ -512,6 +522,7 @@ pa_channel_position_t pa_channel_position_from_string(const char *p) { return PA_CHANNEL_POSITION_INVALID; } +SPA_EXPORT pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { pa_channel_map map; char **tokens; @@ -611,6 +622,7 @@ finish: return rmap; } +SPA_EXPORT int pa_channel_map_valid(const pa_channel_map *map) { unsigned c; @@ -626,6 +638,7 @@ int pa_channel_map_valid(const pa_channel_map *map) { return 1; } +SPA_EXPORT int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) { pa_assert(map); pa_assert(ss); @@ -636,6 +649,7 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s return map->channels == ss->channels; } +SPA_EXPORT int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) { pa_channel_position_mask_t am, bm; @@ -655,6 +669,7 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) { return (bm & am) == bm; } +SPA_EXPORT int pa_channel_map_can_balance(const pa_channel_map *map) { pa_channel_position_mask_t m; @@ -668,6 +683,7 @@ int pa_channel_map_can_balance(const pa_channel_map *map) { (PA_CHANNEL_POSITION_MASK_RIGHT & m); } +SPA_EXPORT int pa_channel_map_can_fade(const pa_channel_map *map) { pa_channel_position_mask_t m; @@ -681,6 +697,7 @@ int pa_channel_map_can_fade(const pa_channel_map *map) { (PA_CHANNEL_POSITION_MASK_REAR & m); } +SPA_EXPORT int pa_channel_map_can_lfe_balance(const pa_channel_map *map) { pa_channel_position_mask_t m; @@ -694,6 +711,7 @@ int pa_channel_map_can_lfe_balance(const pa_channel_map *map) { (PA_CHANNEL_POSITION_MASK_HFE & m); } +SPA_EXPORT const char* pa_channel_map_to_name(const pa_channel_map *map) { pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)]; unsigned c; @@ -748,6 +766,7 @@ const char* pa_channel_map_to_name(const pa_channel_map *map) { return NULL; } +SPA_EXPORT const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) { pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)]; unsigned c; @@ -804,6 +823,7 @@ const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) { return NULL; } +SPA_EXPORT int pa_channel_map_has_position(const pa_channel_map *map, pa_channel_position_t p) { unsigned c; @@ -817,6 +837,7 @@ int pa_channel_map_has_position(const pa_channel_map *map, pa_channel_position_t return 0; } +SPA_EXPORT pa_channel_position_mask_t pa_channel_map_mask(const pa_channel_map *map) { unsigned c; pa_channel_position_mask_t r = 0; diff --git a/src/context.c b/src/context.c index 4215e4140..0236e4be7 100644 --- a/src/context.c +++ b/src/context.c @@ -98,6 +98,7 @@ static void context_fail(pa_context *c, int error) { pa_context_set_state(c, PA_CONTEXT_FAILED); } +SPA_EXPORT pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { return pa_context_new_with_proplist(mainloop, name, NULL); @@ -419,6 +420,7 @@ static const struct pw_remote_events remote_events = { .state_changed = remote_state_changed, }; +SPA_EXPORT pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) { struct pw_core *core; @@ -483,6 +485,7 @@ static void context_free(pa_context *c) pw_core_destroy(c->core); } +SPA_EXPORT void pa_context_unref(pa_context *c) { pa_assert(c); @@ -492,6 +495,7 @@ void pa_context_unref(pa_context *c) context_free(c); } +SPA_EXPORT pa_context* pa_context_ref(pa_context *c) { pa_assert(c); @@ -500,6 +504,7 @@ pa_context* pa_context_ref(pa_context *c) return c; } +SPA_EXPORT void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) { pa_assert(c); @@ -512,6 +517,7 @@ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, voi c->state_userdata = userdata; } +SPA_EXPORT void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata) { pa_assert(c); @@ -524,6 +530,7 @@ void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void c->event_userdata = userdata; } +SPA_EXPORT int pa_context_errno(pa_context *c) { if (!c) @@ -534,6 +541,7 @@ int pa_context_errno(pa_context *c) return c->error; } +SPA_EXPORT int pa_context_is_pending(pa_context *c) { pa_assert(c); @@ -544,6 +552,7 @@ int pa_context_is_pending(pa_context *c) return !spa_list_is_empty(&c->operations); } +SPA_EXPORT pa_context_state_t pa_context_get_state(pa_context *c) { pa_assert(c); @@ -551,6 +560,7 @@ pa_context_state_t pa_context_get_state(pa_context *c) return c->state; } +SPA_EXPORT int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api) { int res; @@ -573,6 +583,7 @@ int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t fla return res; } +SPA_EXPORT void pa_context_disconnect(pa_context *c) { pa_assert(c); @@ -598,6 +609,7 @@ static void on_notify(pa_operation *o, void *userdata) d->cb(c, d->userdata); } +SPA_EXPORT pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) { pa_operation *o; @@ -627,6 +639,7 @@ static void on_success(pa_operation *o, void *userdata) d->cb(c, d->ret, d->userdata); } +SPA_EXPORT pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -641,18 +654,21 @@ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, return o; } +SPA_EXPORT pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT int pa_context_is_local(pa_context *c) { pa_assert(c); @@ -663,6 +679,7 @@ int pa_context_is_local(pa_context *c) return 1; } +SPA_EXPORT pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { struct spa_dict dict; @@ -689,6 +706,7 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su return o; } +SPA_EXPORT const char* pa_context_get_server(pa_context *c) { const struct pw_core_info *info; @@ -702,11 +720,13 @@ const char* pa_context_get_server(pa_context *c) return info->name; } +SPA_EXPORT uint32_t pa_context_get_protocol_version(pa_context *c) { return PA_PROTOCOL_VERSION; } +SPA_EXPORT uint32_t pa_context_get_server_protocol_version(pa_context *c) { pa_assert(c); @@ -717,6 +737,7 @@ uint32_t pa_context_get_server_protocol_version(pa_context *c) return PA_PROTOCOL_VERSION; } +SPA_EXPORT pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -739,6 +760,7 @@ pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, p return o; } +SPA_EXPORT pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -760,11 +782,13 @@ pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[] return o; } +SPA_EXPORT uint32_t pa_context_get_index(pa_context *c) { return c->client_index; } +SPA_EXPORT pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) { struct timeval tv; @@ -781,6 +805,7 @@ pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_even return c->mainloop->time_new(c->mainloop, &tv, cb, userdata); } +SPA_EXPORT void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) { struct timeval tv; @@ -797,6 +822,7 @@ void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) } } +SPA_EXPORT size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss) { size_t fs, mbs; @@ -811,6 +837,7 @@ size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss) return PA_MAX(mbs, fs); } +SPA_EXPORT int pa_context_load_cookie_from_file(pa_context *c, const char *cookie_file_path) { return 0; diff --git a/src/core-format.c b/src/core-format.c index baa19a480..4977a242d 100644 --- a/src/core-format.c +++ b/src/core-format.c @@ -26,6 +26,7 @@ #include "internal.h" +SPA_EXPORT int pa_format_info_get_sample_format(const pa_format_info *f, pa_sample_format_t *sf) { int r; char *sf_str; @@ -51,6 +52,7 @@ int pa_format_info_get_sample_format(const pa_format_info *f, pa_sample_format_t return 0; } +SPA_EXPORT int pa_format_info_get_rate(const pa_format_info *f, uint32_t *rate) { int r; int rate_local; @@ -72,6 +74,7 @@ int pa_format_info_get_rate(const pa_format_info *f, uint32_t *rate) { return 0; } +SPA_EXPORT int pa_format_info_get_channels(const pa_format_info *f, uint8_t *channels) { int r; int channels_local; @@ -93,6 +96,7 @@ int pa_format_info_get_channels(const pa_format_info *f, uint8_t *channels) { return 0; } +SPA_EXPORT int pa_format_info_get_channel_map(const pa_format_info *f, pa_channel_map *map) { int r; char *map_str; @@ -115,6 +119,7 @@ int pa_format_info_get_channel_map(const pa_format_info *f, pa_channel_map *map) return 0; } +SPA_EXPORT pa_format_info *pa_format_info_from_sample_spec2(const pa_sample_spec *ss, const pa_channel_map *map, bool set_format, bool set_rate, bool set_channels) { pa_format_info *format = NULL; @@ -152,6 +157,7 @@ fail: return NULL; } +SPA_EXPORT int pa_format_info_to_sample_spec2(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map, const pa_sample_spec *fallback_ss, const pa_channel_map *fallback_map) { int r, r2; @@ -216,6 +222,7 @@ int pa_format_info_to_sample_spec2(const pa_format_info *f, pa_sample_spec *ss, return 0; } +SPA_EXPORT int pa_format_info_to_sample_spec_fake(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) { int rate; diff --git a/src/direction.c b/src/direction.c index b9ed061fb..d1dc6d722 100644 --- a/src/direction.c +++ b/src/direction.c @@ -19,11 +19,13 @@ #include +#include #include #define pa_init_i18n() #define _(String) (String) +SPA_EXPORT int pa_direction_valid(pa_direction_t direction) { if (direction != PA_DIRECTION_INPUT @@ -33,6 +35,7 @@ int pa_direction_valid(pa_direction_t direction) return 1; } +SPA_EXPORT const char *pa_direction_to_string(pa_direction_t direction) { pa_init_i18n(); diff --git a/src/error.c b/src/error.c index f6b707c89..abf05b634 100644 --- a/src/error.c +++ b/src/error.c @@ -29,6 +29,7 @@ #define _(String) (String) #define pa_init_i18n() +SPA_EXPORT const char*pa_strerror(int error) { static const char* const errortab[PA_ERR_MAX] = { diff --git a/src/ext-device-manager.c b/src/ext-device-manager.c index 31578ad59..480afb308 100644 --- a/src/ext-device-manager.c +++ b/src/ext-device-manager.c @@ -26,6 +26,7 @@ #include "internal.h" +SPA_EXPORT pa_operation *pa_ext_device_manager_test( pa_context *c, pa_ext_device_manager_test_cb_t cb, @@ -35,6 +36,7 @@ pa_operation *pa_ext_device_manager_test( return NULL; } +SPA_EXPORT pa_operation *pa_ext_device_manager_read( pa_context *c, pa_ext_device_manager_read_cb_t cb, @@ -44,6 +46,7 @@ pa_operation *pa_ext_device_manager_read( return NULL; } +SPA_EXPORT pa_operation *pa_ext_device_manager_set_device_description( pa_context *c, const char* device, @@ -55,6 +58,7 @@ pa_operation *pa_ext_device_manager_set_device_description( return NULL; } +SPA_EXPORT pa_operation *pa_ext_device_manager_delete( pa_context *c, const char *const s[], @@ -65,6 +69,7 @@ pa_operation *pa_ext_device_manager_delete( return NULL; } +SPA_EXPORT pa_operation *pa_ext_device_manager_enable_role_device_priority_routing( pa_context *c, int enable, @@ -75,6 +80,7 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing( return NULL; } +SPA_EXPORT pa_operation *pa_ext_device_manager_reorder_devices_for_role( pa_context *c, const char* role, @@ -86,6 +92,7 @@ pa_operation *pa_ext_device_manager_reorder_devices_for_role( return NULL; } +SPA_EXPORT pa_operation *pa_ext_device_manager_subscribe( pa_context *c, int enable, @@ -96,6 +103,7 @@ pa_operation *pa_ext_device_manager_subscribe( return NULL; } +SPA_EXPORT void pa_ext_device_manager_set_subscribe_cb( pa_context *c, pa_ext_device_manager_subscribe_cb_t cb, diff --git a/src/ext-device-restore.c b/src/ext-device-restore.c index b2c67da04..a2c0e686a 100644 --- a/src/ext-device-restore.c +++ b/src/ext-device-restore.c @@ -41,6 +41,7 @@ static void restore_test(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation *pa_ext_device_restore_test( pa_context *c, pa_ext_device_restore_test_cb_t cb, @@ -72,6 +73,7 @@ static void on_success(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation *pa_ext_device_restore_subscribe( pa_context *c, int enable, @@ -96,6 +98,7 @@ pa_operation *pa_ext_device_restore_subscribe( return o; } +SPA_EXPORT void pa_ext_device_restore_set_subscribe_cb( pa_context *c, pa_ext_device_restore_subscribe_cb_t cb, @@ -112,6 +115,7 @@ static void read_formats(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation *pa_ext_device_restore_read_formats_all( pa_context *c, pa_ext_device_restore_read_device_formats_cb_t cb, @@ -135,6 +139,7 @@ pa_operation *pa_ext_device_restore_read_formats_all( return o; } +SPA_EXPORT pa_operation *pa_ext_device_restore_read_formats( pa_context *c, pa_device_type_t type, @@ -160,6 +165,7 @@ pa_operation *pa_ext_device_restore_read_formats( return o; } +SPA_EXPORT pa_operation *pa_ext_device_restore_save_formats( pa_context *c, pa_device_type_t type, diff --git a/src/ext-stream-restore.c b/src/ext-stream-restore.c index bd9e2f95e..e5c977518 100644 --- a/src/ext-stream-restore.c +++ b/src/ext-stream-restore.c @@ -43,6 +43,7 @@ static void restore_test(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation *pa_ext_stream_restore_test( pa_context *c, pa_ext_stream_restore_test_cb_t cb, @@ -76,6 +77,7 @@ static void restore_read(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation *pa_ext_stream_restore_read( pa_context *c, pa_ext_stream_restore_read_cb_t cb, @@ -107,6 +109,7 @@ static void on_success(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation *pa_ext_stream_restore_write( pa_context *c, pa_update_mode_t mode, @@ -135,6 +138,7 @@ pa_operation *pa_ext_stream_restore_write( } /** Delete entries from the stream database. \since 0.9.12 */ +SPA_EXPORT pa_operation *pa_ext_stream_restore_delete( pa_context *c, const char *const s[], @@ -160,6 +164,7 @@ pa_operation *pa_ext_stream_restore_delete( } /** Subscribe to changes in the stream database. \since 0.9.12 */ +SPA_EXPORT pa_operation *pa_ext_stream_restore_subscribe( pa_context *c, int enable, @@ -186,6 +191,7 @@ pa_operation *pa_ext_stream_restore_subscribe( /** Set the subscription callback that is called when * pa_ext_stream_restore_subscribe() was called. \since 0.9.12 */ +SPA_EXPORT void pa_ext_stream_restore_set_subscribe_cb( pa_context *c, pa_ext_stream_restore_subscribe_cb_t cb, diff --git a/src/format.c b/src/format.c index 1cc2b8bf5..a0160c360 100644 --- a/src/format.c +++ b/src/format.c @@ -46,6 +46,7 @@ static const char* const _encoding_str_table[]= { [PA_ENCODING_ANY] = "any", }; +SPA_EXPORT const char *pa_encoding_to_string(pa_encoding_t e) { if (e < 0 || e >= PA_ENCODING_MAX) return NULL; @@ -53,6 +54,7 @@ const char *pa_encoding_to_string(pa_encoding_t e) { return _encoding_str_table[e]; } +SPA_EXPORT pa_encoding_t pa_encoding_from_string(const char *encoding) { pa_encoding_t e; @@ -63,6 +65,7 @@ pa_encoding_t pa_encoding_from_string(const char *encoding) { return PA_ENCODING_INVALID; } +SPA_EXPORT pa_format_info* pa_format_info_new(void) { pa_format_info *f = pa_xnew(pa_format_info, 1); @@ -72,6 +75,7 @@ pa_format_info* pa_format_info_new(void) { return f; } +SPA_EXPORT pa_format_info* pa_format_info_copy(const pa_format_info *src) { pa_format_info *dest; @@ -89,6 +93,7 @@ pa_format_info* pa_format_info_copy(const pa_format_info *src) { return dest; } +SPA_EXPORT void pa_format_info_free(pa_format_info *f) { pa_assert(f); @@ -96,14 +101,17 @@ void pa_format_info_free(pa_format_info *f) { pa_xfree(f); } +SPA_EXPORT int pa_format_info_valid(const pa_format_info *f) { return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL); } +SPA_EXPORT int pa_format_info_is_pcm(const pa_format_info *f) { return f->encoding == PA_ENCODING_PCM; } +SPA_EXPORT char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) { char *tmp; @@ -127,6 +135,7 @@ char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) { return s; } +SPA_EXPORT pa_format_info* pa_format_info_from_string(const char *str) { pa_format_info *f = pa_format_info_new(); char *encoding = NULL, *properties = NULL; @@ -165,6 +174,7 @@ error: goto out; } +SPA_EXPORT int pa_format_info_is_compatible(const pa_format_info *first, const pa_format_info *second) { const char *key; void *state = NULL; @@ -188,6 +198,7 @@ int pa_format_info_is_compatible(const pa_format_info *first, const pa_format_in return true; } +SPA_EXPORT pa_format_info* pa_format_info_from_sample_spec(const pa_sample_spec *ss, const pa_channel_map *map) { char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_format_info *f; @@ -211,6 +222,7 @@ pa_format_info* pa_format_info_from_sample_spec(const pa_sample_spec *ss, const } /* For PCM streams */ +SPA_EXPORT int pa_format_info_to_sample_spec(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) { pa_assert(f); pa_assert(ss); @@ -230,6 +242,7 @@ int pa_format_info_to_sample_spec(const pa_format_info *f, pa_sample_spec *ss, p return 0; } +SPA_EXPORT pa_prop_type_t pa_format_info_get_prop_type(const pa_format_info *f, const char *key) { const char *str; pa_json_object *o; @@ -300,6 +313,7 @@ pa_prop_type_t pa_format_info_get_prop_type(const pa_format_info *f, const char return type; } +SPA_EXPORT int pa_format_info_get_prop_int(const pa_format_info *f, const char *key, int *v) { const char *str; pa_json_object *o; @@ -330,6 +344,7 @@ int pa_format_info_get_prop_int(const pa_format_info *f, const char *key, int *v return 0; } +SPA_EXPORT int pa_format_info_get_prop_int_range(const pa_format_info *f, const char *key, int *min, int *max) { const char *str; pa_json_object *o; @@ -376,6 +391,7 @@ out: return ret; } +SPA_EXPORT int pa_format_info_get_prop_int_array(const pa_format_info *f, const char *key, int **values, int *n_values) { const char *str; pa_json_object *o; @@ -423,6 +439,7 @@ out: return ret; } +SPA_EXPORT int pa_format_info_get_prop_string(const pa_format_info *f, const char *key, char **v) { const char *str = NULL; pa_json_object *o; @@ -453,6 +470,7 @@ int pa_format_info_get_prop_string(const pa_format_info *f, const char *key, cha return 0; } +SPA_EXPORT int pa_format_info_get_prop_string_array(const pa_format_info *f, const char *key, char ***values, int *n_values) { const char *str; pa_json_object *o; @@ -500,6 +518,7 @@ out: return ret; } +SPA_EXPORT void pa_format_info_free_string_array(char **values, int n_values) { int i; @@ -509,18 +528,22 @@ void pa_format_info_free_string_array(char **values, int n_values) { pa_xfree(values); } +SPA_EXPORT void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf) { pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(sf)); } +SPA_EXPORT void pa_format_info_set_rate(pa_format_info *f, int rate) { pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, rate); } +SPA_EXPORT void pa_format_info_set_channels(pa_format_info *f, int channels) { pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, channels); } +SPA_EXPORT void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map) { char map_str[PA_CHANNEL_MAP_SNPRINT_MAX]; @@ -529,6 +552,7 @@ void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, map_str); } +SPA_EXPORT void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) { pa_assert(f); pa_assert(key); @@ -536,6 +560,7 @@ void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) pa_proplist_setf(f->plist, key, "%d", value); } +SPA_EXPORT void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) { pa_strbuf *buf; char *str; @@ -559,6 +584,7 @@ void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const pa_xfree (str); } +SPA_EXPORT void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) { pa_assert(f); pa_assert(key); @@ -567,6 +593,7 @@ void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int m min, max); } +SPA_EXPORT void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) { pa_assert(f); pa_assert(key); @@ -574,6 +601,7 @@ void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const ch pa_proplist_setf(f->plist, key, "\"%s\"", value); } +SPA_EXPORT void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) { pa_strbuf *buf; char *str; diff --git a/src/introspect.c b/src/introspect.c index 21fcf3238..dc2814e5f 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -379,6 +379,7 @@ static void sink_info(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata) { pa_operation *o; @@ -407,6 +408,7 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, return o; } +SPA_EXPORT pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata) { pa_operation *o; @@ -453,6 +455,7 @@ static void sink_info_list(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata) { pa_operation *o; @@ -507,6 +510,7 @@ static void set_node_mute(pa_context *c, struct global *g, bool mute) } +SPA_EXPORT pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -537,6 +541,7 @@ pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, c return o; } +SPA_EXPORT pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -565,6 +570,7 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name return o; } +SPA_EXPORT pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -594,6 +600,7 @@ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int return o; } +SPA_EXPORT pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -621,24 +628,28 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, return o; } +SPA_EXPORT pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_set_sink_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); @@ -731,6 +742,7 @@ static void source_info(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata) { pa_operation *o; @@ -759,6 +771,7 @@ pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name return o; } +SPA_EXPORT pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata) { pa_operation *o; @@ -804,6 +817,7 @@ static void source_info_list(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata) { pa_operation *o; @@ -826,6 +840,7 @@ pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t return o; } +SPA_EXPORT pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -856,6 +871,7 @@ pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, return o; } +SPA_EXPORT pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -884,6 +900,7 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na return o; } +SPA_EXPORT pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -913,6 +930,7 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i return o; } +SPA_EXPORT pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -940,24 +958,28 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name return o; } +SPA_EXPORT pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); @@ -999,6 +1021,7 @@ static void server_info(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1039,6 +1062,7 @@ static void module_info(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1085,6 +1109,7 @@ static void module_info_list(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1107,12 +1132,14 @@ pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t return o; } +SPA_EXPORT pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); @@ -1140,6 +1167,7 @@ static void client_info(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1186,6 +1214,7 @@ static void client_info_list(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1208,6 +1237,7 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t return o; } +SPA_EXPORT pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { struct global *g; @@ -1298,6 +1328,7 @@ static void card_info(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1328,6 +1359,7 @@ pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_ return o; } +SPA_EXPORT pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, pa_card_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1372,6 +1404,7 @@ static void card_info_list(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1441,6 +1474,7 @@ done: free(d->profile); } +SPA_EXPORT pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1474,6 +1508,7 @@ pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, return o; } +SPA_EXPORT pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name, const char*profile, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1505,6 +1540,7 @@ pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name return o; } +SPA_EXPORT pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card_name, const char *port_name, int64_t offset, pa_context_success_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); @@ -1610,6 +1646,7 @@ static void sink_input_info(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1657,6 +1694,7 @@ static void sink_input_info_list(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1680,18 +1718,21 @@ pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_i return o; } +SPA_EXPORT pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_stream *s; @@ -1724,6 +1765,7 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons return o; } +SPA_EXPORT pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { pa_stream *s; @@ -1754,6 +1796,7 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu return o; } +SPA_EXPORT pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { pa_stream *s; @@ -1873,6 +1916,7 @@ static void source_output_info(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1919,6 +1963,7 @@ static void source_output_info_list(pa_operation *o, void *userdata) pa_operation_done(o); } +SPA_EXPORT pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata) { pa_operation *o; @@ -1941,18 +1986,21 @@ pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_ou return o; } +SPA_EXPORT pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_stream *s; @@ -1985,6 +2033,7 @@ pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, c return o; } +SPA_EXPORT pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { pa_stream *s; @@ -2015,6 +2064,7 @@ pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int return o; } +SPA_EXPORT pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { pa_stream *s; @@ -2044,60 +2094,70 @@ pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_cont return o; } +SPA_EXPORT pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) { pw_log_warn("Deprecated: Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) { pw_log_warn("Deprecated: Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) { pw_log_warn("Deprecated: Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) { pw_log_warn("Deprecated: Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) { pw_log_warn("Deprecated: Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) { pw_log_warn("Deprecated: Not Implemented"); diff --git a/src/json.c b/src/json.c index 9444a607b..9ca498bef 100644 --- a/src/json.c +++ b/src/json.c @@ -499,6 +499,7 @@ error: } +SPA_EXPORT pa_json_object* pa_json_parse(const char *str) { pa_json_object *obj; @@ -518,10 +519,12 @@ pa_json_object* pa_json_parse(const char *str) { return obj; } +SPA_EXPORT pa_json_type pa_json_object_get_type(const pa_json_object *obj) { return obj->type; } +SPA_EXPORT void pa_json_object_free(pa_json_object *obj) { switch (pa_json_object_get_type(obj)) { @@ -551,26 +554,31 @@ void pa_json_object_free(pa_json_object *obj) { pa_xfree(obj); } +SPA_EXPORT int pa_json_object_get_int(const pa_json_object *o) { pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT); return o->int_value; } +SPA_EXPORT double pa_json_object_get_double(const pa_json_object *o) { pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_DOUBLE); return o->double_value; } +SPA_EXPORT bool pa_json_object_get_bool(const pa_json_object *o) { pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_BOOL); return o->bool_value; } +SPA_EXPORT const char* pa_json_object_get_string(const pa_json_object *o) { pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_STRING); return o->string_value; } +SPA_EXPORT const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name) { pa_json_item *item; pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT); @@ -581,17 +589,20 @@ const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, return NULL; } +SPA_EXPORT int pa_json_object_get_array_length(const pa_json_object *o) { pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY); return pw_array_get_len(&o->values, const pa_json_object*); } +SPA_EXPORT const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index) { pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY); return pw_array_get_unchecked_s(&o->values, index, sizeof(pa_json_object*), const pa_json_object); } +SPA_EXPORT bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2) { int i; diff --git a/src/mainloop-glib.c b/src/mainloop-glib.c index 440bd083f..acf1989dd 100644 --- a/src/mainloop-glib.c +++ b/src/mainloop-glib.c @@ -70,6 +70,7 @@ static GSourceFuncs source_funcs = NULL, }; +SPA_EXPORT pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) { @@ -102,6 +103,7 @@ pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) } +SPA_EXPORT void pa_glib_mainloop_free(pa_glib_mainloop* g) { g_source_destroy(&g->source->base); @@ -109,6 +111,7 @@ void pa_glib_mainloop_free(pa_glib_mainloop* g) free(g); } +SPA_EXPORT pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { return pa_mainloop_get_api(g->loop); diff --git a/src/mainloop-signal.c b/src/mainloop-signal.c index 8fb1b1ae1..5caf59200 100644 --- a/src/mainloop-signal.c +++ b/src/mainloop-signal.c @@ -42,6 +42,7 @@ struct pa_signal_event { void *userdata; }; +SPA_EXPORT int pa_signal_init(pa_mainloop_api *a) { pa_assert(a); @@ -54,6 +55,7 @@ int pa_signal_init(pa_mainloop_api *a) return 0; } +SPA_EXPORT void pa_signal_done(void) { pa_signal_event *ev, *t; @@ -74,6 +76,7 @@ static void source_signal_func (void *data, int signal_number) ev->callback(api, ev, signal_number, ev->userdata); } +SPA_EXPORT pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t callback, void *userdata) { pa_signal_event *ev; @@ -91,6 +94,7 @@ pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t callback, void *userdata) return ev; } +SPA_EXPORT void pa_signal_free(pa_signal_event *e) { pa_assert(e); @@ -102,6 +106,7 @@ void pa_signal_free(pa_signal_event *e) free(e); } +SPA_EXPORT void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t callback) { pa_assert(e); diff --git a/src/mainloop.c b/src/mainloop.c index 578b94d6b..ef509ece1 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -240,6 +240,7 @@ static const pa_mainloop_api api = .quit = api_quit, }; +SPA_EXPORT pa_mainloop *pa_mainloop_new(void) { pa_mainloop *loop; @@ -263,12 +264,14 @@ pa_mainloop *pa_mainloop_new(void) return NULL; } +SPA_EXPORT void pa_mainloop_free(pa_mainloop* m) { pw_loop_destroy(m->loop); free(m); } +SPA_EXPORT int pa_mainloop_prepare(pa_mainloop *m, int timeout) { if (m->quit) @@ -279,6 +282,7 @@ int pa_mainloop_prepare(pa_mainloop *m, int timeout) } /** Execute the previously prepared poll. Returns a negative value on error.*/ +SPA_EXPORT int pa_mainloop_poll(pa_mainloop *m) { if (m->quit) @@ -287,6 +291,7 @@ int pa_mainloop_poll(pa_mainloop *m) return m->n_events = pw_loop_iterate(m->loop, m->timeout); } +SPA_EXPORT int pa_mainloop_dispatch(pa_mainloop *m) { if (m->quit) @@ -295,6 +300,7 @@ int pa_mainloop_dispatch(pa_mainloop *m) return m->n_events; } +SPA_EXPORT int pa_mainloop_get_retval(pa_mainloop *m) { return m->retval; @@ -306,6 +312,7 @@ Returns a negative value on error or exit request. If block is nonzero, block for events if none are queued. Optionally return the return value as specified with the main loop's quit() routine in the integer variable retval points to. On success returns the number of sources dispatched in this iteration. */ +SPA_EXPORT int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) { int r; @@ -328,6 +335,7 @@ int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) return r; } +SPA_EXPORT int pa_mainloop_run(pa_mainloop *m, int *retval) { int r; @@ -342,24 +350,28 @@ int pa_mainloop_run(pa_mainloop *m, int *retval) } +SPA_EXPORT pa_mainloop_api* pa_mainloop_get_api(pa_mainloop *m) { pa_assert(m); return &m->api; } +SPA_EXPORT void pa_mainloop_quit(pa_mainloop *m, int retval) { pa_assert(m); m->api.quit(&m->api, retval); } +SPA_EXPORT void pa_mainloop_wakeup(pa_mainloop *m) { pa_assert(m); pw_loop_signal_event(m->loop, m->event); } +SPA_EXPORT void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) { pw_log_warn("Not Implemented"); diff --git a/src/operation.c b/src/operation.c index 651023cb0..3fbb7af28 100644 --- a/src/operation.c +++ b/src/operation.c @@ -58,6 +58,7 @@ int pa_operation_sync(pa_operation *o) return 0; } +SPA_EXPORT pa_operation *pa_operation_ref(pa_operation *o) { pa_assert(o); @@ -96,6 +97,7 @@ static void operation_unlink(pa_operation *o) { } +SPA_EXPORT void pa_operation_unref(pa_operation *o) { pa_assert(o); @@ -126,6 +128,7 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) { } +SPA_EXPORT void pa_operation_cancel(pa_operation *o) { pa_assert(o); @@ -141,6 +144,7 @@ void pa_operation_done(pa_operation *o) { } +SPA_EXPORT pa_operation_state_t pa_operation_get_state(pa_operation *o) { pa_assert(o); @@ -148,6 +152,7 @@ pa_operation_state_t pa_operation_get_state(pa_operation *o) return o->state; } +SPA_EXPORT void pa_operation_set_state_callback(pa_operation *o, pa_operation_notify_cb_t cb, void *userdata) { pa_assert(o); diff --git a/src/proplist.c b/src/proplist.c index dde50939d..ebecc72df 100644 --- a/src/proplist.c +++ b/src/proplist.c @@ -49,6 +49,7 @@ pa_proplist* pa_proplist_new_props(struct pw_properties *props) return pa_proplist_new_dict(&props->dict); } +SPA_EXPORT pa_proplist* pa_proplist_new(void) { return pa_proplist_new_dict(NULL); @@ -60,12 +61,14 @@ int pa_proplist_update_dict(pa_proplist *p, struct spa_dict *dict) } +SPA_EXPORT void pa_proplist_free(pa_proplist* p) { pw_properties_free(p->props); free(p); } +SPA_EXPORT int pa_proplist_key_valid(const char *key) { const char *p; @@ -79,6 +82,7 @@ int pa_proplist_key_valid(const char *key) return 1; } +SPA_EXPORT int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) { pa_assert(p); @@ -92,6 +96,7 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) return 0; } +SPA_EXPORT int pa_proplist_setp(pa_proplist *p, const char *pair) { const char *t; @@ -113,6 +118,7 @@ int pa_proplist_setp(pa_proplist *p, const char *pair) return 0; } +SPA_EXPORT int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) { va_list varargs; @@ -124,6 +130,7 @@ int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) return 0; } +SPA_EXPORT int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) { pa_assert(p); @@ -137,11 +144,13 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb return 0; } +SPA_EXPORT const char *pa_proplist_gets(pa_proplist *p, const char *key) { return pw_properties_get(p->props, key); } +SPA_EXPORT int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes) { const char *val; @@ -156,6 +165,7 @@ int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t * return 0; } +SPA_EXPORT void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other) { uint32_t i; @@ -181,6 +191,7 @@ void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist } } +SPA_EXPORT int pa_proplist_unset(pa_proplist *p, const char *key) { spa_assert(p); @@ -192,6 +203,7 @@ int pa_proplist_unset(pa_proplist *p, const char *key) return pw_properties_set(p->props, key, NULL); } +SPA_EXPORT int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) { const char * const * k; @@ -210,6 +222,7 @@ int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) return n; } +SPA_EXPORT const char *pa_proplist_iterate(pa_proplist *p, void **state) { spa_assert(p); @@ -217,12 +230,14 @@ const char *pa_proplist_iterate(pa_proplist *p, void **state) return pw_properties_iterate(p->props, state); } +SPA_EXPORT char *pa_proplist_to_string(pa_proplist *p) { spa_assert(p); return pa_proplist_to_string_sep(p, ","); } +SPA_EXPORT char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) { const char *key; @@ -269,6 +284,7 @@ char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) return pa_strbuf_to_string_free(buf); } +SPA_EXPORT pa_proplist *pa_proplist_from_string(const char *str) { spa_assert(str); @@ -276,6 +292,7 @@ pa_proplist *pa_proplist_from_string(const char *str) return NULL; } +SPA_EXPORT int pa_proplist_contains(pa_proplist *p, const char *key) { spa_assert(p); @@ -290,12 +307,14 @@ int pa_proplist_contains(pa_proplist *p, const char *key) return 1; } +SPA_EXPORT void pa_proplist_clear(pa_proplist *p) { spa_assert(p); pw_properties_clear(p->props); } +SPA_EXPORT pa_proplist* pa_proplist_copy(const pa_proplist *p) { pa_proplist *c; @@ -310,18 +329,21 @@ pa_proplist* pa_proplist_copy(const pa_proplist *p) return c; } +SPA_EXPORT unsigned pa_proplist_size(pa_proplist *p) { spa_assert(p); return p->props->dict.n_items; } +SPA_EXPORT int pa_proplist_isempty(pa_proplist *p) { spa_assert(p); return p->props->dict.n_items == 0 ? 1 : 0; } +SPA_EXPORT int pa_proplist_equal(pa_proplist *a, pa_proplist *b) { uint32_t i; diff --git a/src/rtclock.c b/src/rtclock.c index a0b97c831..a3e5e4cb0 100644 --- a/src/rtclock.c +++ b/src/rtclock.c @@ -25,6 +25,7 @@ #include +SPA_EXPORT pa_usec_t pa_rtclock_now(void) { struct timespec ts; diff --git a/src/sample.c b/src/sample.c index c5e6437b8..07e903219 100644 --- a/src/sample.c +++ b/src/sample.c @@ -50,6 +50,7 @@ static const size_t size_table[] = { [PA_SAMPLE_S24_32BE] = 4 }; +SPA_EXPORT size_t pa_sample_size_of_format(pa_sample_format_t f) { spa_assert(pa_sample_format_valid(f)); @@ -57,6 +58,7 @@ size_t pa_sample_size_of_format(pa_sample_format_t f) return size_table[f]; } +SPA_EXPORT size_t pa_sample_size(const pa_sample_spec * spec) { spa_assert(spec); @@ -65,6 +67,7 @@ size_t pa_sample_size(const pa_sample_spec * spec) return size_table[spec->format]; } +SPA_EXPORT size_t pa_frame_size(const pa_sample_spec * spec) { spa_assert(spec); @@ -73,6 +76,7 @@ size_t pa_frame_size(const pa_sample_spec * spec) return size_table[spec->format] * spec->channels; } +SPA_EXPORT size_t pa_bytes_per_second(const pa_sample_spec * spec) { spa_assert(spec); @@ -81,6 +85,7 @@ size_t pa_bytes_per_second(const pa_sample_spec * spec) return spec->rate * size_table[spec->format] * spec->channels; } +SPA_EXPORT pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec * spec) { spa_assert(spec); @@ -91,6 +96,7 @@ pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec * spec) * PA_USEC_PER_SEC) / spec->rate); } +SPA_EXPORT size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec * spec) { spa_assert(spec); @@ -100,6 +106,7 @@ size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec * spec) (size_table[spec->format] * spec->channels); } +SPA_EXPORT pa_sample_spec *pa_sample_spec_init(pa_sample_spec * spec) { spa_assert(spec); @@ -111,11 +118,13 @@ pa_sample_spec *pa_sample_spec_init(pa_sample_spec * spec) return spec; } +SPA_EXPORT int pa_sample_format_valid(unsigned format) { return format < PA_SAMPLE_MAX; } +SPA_EXPORT int pa_sample_rate_valid(uint32_t rate) { /* The extra 1% is due to module-loopback: it temporarily sets @@ -124,11 +133,13 @@ int pa_sample_rate_valid(uint32_t rate) return rate > 0 && rate <= PA_RATE_MAX * 101 / 100; } +SPA_EXPORT int pa_channels_valid(uint8_t channels) { return channels > 0 && channels <= PA_CHANNELS_MAX; } +SPA_EXPORT int pa_sample_spec_valid(const pa_sample_spec * spec) { spa_assert(spec); @@ -141,6 +152,7 @@ int pa_sample_spec_valid(const pa_sample_spec * spec) return 1; } +SPA_EXPORT int pa_sample_spec_equal(const pa_sample_spec * a, const pa_sample_spec * b) { spa_assert(a); @@ -158,6 +170,7 @@ int pa_sample_spec_equal(const pa_sample_spec * a, const pa_sample_spec * b) (a->rate == b->rate) && (a->channels == b->channels); } +SPA_EXPORT const char *pa_sample_format_to_string(pa_sample_format_t f) { static const char *const table[] = { @@ -182,6 +195,7 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) return table[f]; } +SPA_EXPORT char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec * spec) { spa_assert(s); @@ -200,6 +214,7 @@ char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec * spec) return s; } +SPA_EXPORT char *pa_bytes_snprint(char *s, size_t l, unsigned v) { spa_assert(s); @@ -220,6 +235,7 @@ char *pa_bytes_snprint(char *s, size_t l, unsigned v) return s; } +SPA_EXPORT pa_sample_format_t pa_parse_sample_format(const char *format) { spa_assert(format); @@ -284,6 +300,7 @@ pa_sample_format_t pa_parse_sample_format(const char *format) return PA_SAMPLE_INVALID; } +SPA_EXPORT int pa_sample_format_is_le(pa_sample_format_t f) { spa_assert(pa_sample_format_valid(f)); @@ -308,6 +325,7 @@ int pa_sample_format_is_le(pa_sample_format_t f) } } +SPA_EXPORT int pa_sample_format_is_be(pa_sample_format_t f) { int r; diff --git a/src/scache.c b/src/scache.c index 72c8c9494..2f9ace166 100644 --- a/src/scache.c +++ b/src/scache.c @@ -23,24 +23,28 @@ #include "internal.h" +SPA_EXPORT int pa_stream_connect_upload(pa_stream *s, size_t length) { pw_log_warn("Not Implemented"); return 0; } +SPA_EXPORT int pa_stream_finish_upload(pa_stream *s) { pw_log_warn("Not Implemented"); return 0; } +SPA_EXPORT pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT pa_operation* pa_context_play_sample(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_context_success_cb_t cb, void *userdata) { @@ -48,6 +52,7 @@ pa_operation* pa_context_play_sample(pa_context *c, const char *name, const char return NULL; } +SPA_EXPORT pa_operation* pa_context_play_sample_with_proplist(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_proplist *proplist, pa_context_play_sample_cb_t cb, void *userdata) diff --git a/src/stream.c b/src/stream.c index b4e71941f..4a58af058 100644 --- a/src/stream.c +++ b/src/stream.c @@ -495,7 +495,7 @@ static const struct pw_stream_events stream_events = .process = stream_process, }; -pa_stream* stream_new(pa_context *c, const char *name, +static pa_stream* stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_format_info * const * formats, unsigned int n_formats, pa_proplist *p) @@ -590,12 +590,14 @@ pa_stream* stream_new(pa_context *c, const char *name, return s; } +SPA_EXPORT pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) { return stream_new(c, name, ss, map, NULL, 0, NULL); } +SPA_EXPORT pa_stream* pa_stream_new_with_proplist(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_proplist *p) { @@ -607,6 +609,7 @@ pa_stream* pa_stream_new_with_proplist(pa_context *c, const char *name, return stream_new(c, name, ss, map, NULL, 0, p); } +SPA_EXPORT pa_stream *pa_stream_new_extended(pa_context *c, const char *name, pa_format_info * const * formats, unsigned int n_formats, pa_proplist *p) { @@ -658,6 +661,7 @@ static void stream_free(pa_stream *s) free(s); } +SPA_EXPORT void pa_stream_unref(pa_stream *s) { spa_assert(s); @@ -667,6 +671,7 @@ void pa_stream_unref(pa_stream *s) stream_free(s); } +SPA_EXPORT pa_stream *pa_stream_ref(pa_stream *s) { spa_assert(s); @@ -676,6 +681,7 @@ pa_stream *pa_stream_ref(pa_stream *s) return s; } +SPA_EXPORT pa_stream_state_t pa_stream_get_state(pa_stream *s) { spa_assert(s); @@ -683,6 +689,7 @@ pa_stream_state_t pa_stream_get_state(pa_stream *s) return s->state; } +SPA_EXPORT pa_context* pa_stream_get_context(pa_stream *s) { spa_assert(s); @@ -690,6 +697,7 @@ pa_context* pa_stream_get_context(pa_stream *s) return s->context; } +SPA_EXPORT uint32_t pa_stream_get_index(pa_stream *s) { spa_assert(s); @@ -720,6 +728,7 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { } +SPA_EXPORT uint32_t pa_stream_get_device_index(pa_stream *s) { spa_assert(s); @@ -736,6 +745,7 @@ uint32_t pa_stream_get_device_index(pa_stream *s) return s->device_index; } +SPA_EXPORT const char *pa_stream_get_device_name(pa_stream *s) { spa_assert(s); @@ -748,6 +758,7 @@ const char *pa_stream_get_device_name(pa_stream *s) return s->device_name; } +SPA_EXPORT int pa_stream_is_suspended(pa_stream *s) { spa_assert(s); @@ -759,6 +770,7 @@ int pa_stream_is_suspended(pa_stream *s) return s->suspended; } +SPA_EXPORT int pa_stream_is_corked(pa_stream *s) { spa_assert(s); @@ -942,6 +954,7 @@ static int create_stream(pa_stream_direction_t direction, return res; } +SPA_EXPORT int pa_stream_connect_playback( pa_stream *s, const char *dev, @@ -953,6 +966,7 @@ int pa_stream_connect_playback( return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream); } +SPA_EXPORT int pa_stream_connect_record( pa_stream *s, const char *dev, @@ -967,6 +981,7 @@ static void on_disconnected(pa_operation *o, void *userdata) pa_stream_set_state(o->stream, PA_STREAM_TERMINATED); } +SPA_EXPORT int pa_stream_disconnect(pa_stream *s) { pa_operation *o; @@ -1036,6 +1051,7 @@ int queue_buffer(pa_stream *s) return 0; } +SPA_EXPORT int pa_stream_begin_write( pa_stream *s, void **data, @@ -1066,6 +1082,7 @@ int pa_stream_begin_write( return 0; } +SPA_EXPORT int pa_stream_cancel_write(pa_stream *s) { spa_assert(s); @@ -1081,6 +1098,7 @@ int pa_stream_cancel_write(pa_stream *s) return 0; } +SPA_EXPORT int pa_stream_write(pa_stream *s, const void *data, size_t nbytes, @@ -1091,6 +1109,7 @@ int pa_stream_write(pa_stream *s, return pa_stream_write_ext_free(s, data, nbytes, free_cb, (void*) data, offset, seek); } +SPA_EXPORT int pa_stream_write_ext_free(pa_stream *s, const void *data, size_t nbytes, @@ -1160,6 +1179,7 @@ int pa_stream_write_ext_free(pa_stream *s, return 0; } +SPA_EXPORT int pa_stream_peek(pa_stream *s, const void **data, size_t *nbytes) @@ -1185,6 +1205,7 @@ int pa_stream_peek(pa_stream *s, return 0; } +SPA_EXPORT int pa_stream_drop(pa_stream *s) { spa_assert(s); @@ -1200,6 +1221,7 @@ int pa_stream_drop(pa_stream *s) return 0; } +SPA_EXPORT size_t pa_stream_writable_size(pa_stream *s) { spa_assert(s); @@ -1214,6 +1236,7 @@ size_t pa_stream_writable_size(pa_stream *s) return s->dequeued_size; } +SPA_EXPORT size_t pa_stream_readable_size(pa_stream *s) { spa_assert(s); @@ -1241,6 +1264,7 @@ static void on_success(pa_operation *o, void *userdata) d->cb(s, 1, d->userdata); } +SPA_EXPORT pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1276,6 +1300,7 @@ static void on_timing_success(pa_operation *o, void *userdata) d->cb(s, s->timing_info_valid, d->userdata); } +SPA_EXPORT pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1296,6 +1321,7 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t return o; } +SPA_EXPORT void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { spa_assert(s); @@ -1308,6 +1334,7 @@ void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void * s->state_userdata = userdata; } +SPA_EXPORT void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) { spa_assert(s); @@ -1320,6 +1347,7 @@ void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void s->write_userdata = userdata; } +SPA_EXPORT void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) { spa_assert(s); @@ -1332,6 +1360,7 @@ void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void * s->read_userdata = userdata; } +SPA_EXPORT void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { spa_assert(s); @@ -1344,12 +1373,14 @@ void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, voi s->overflow_userdata = userdata; } +SPA_EXPORT int64_t pa_stream_get_underflow_index(pa_stream *s) { pw_log_warn("Not Implemented"); return 0; } +SPA_EXPORT void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { spa_assert(s); @@ -1362,6 +1393,7 @@ void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, vo s->underflow_userdata = userdata; } +SPA_EXPORT void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { spa_assert(s); @@ -1374,6 +1406,7 @@ void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void s->started_userdata = userdata; } +SPA_EXPORT void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { spa_assert(s); @@ -1386,6 +1419,7 @@ void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t c s->latency_update_userdata = userdata; } +SPA_EXPORT void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { spa_assert(s); @@ -1398,6 +1432,7 @@ void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void * s->moved_userdata = userdata; } +SPA_EXPORT void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { spa_assert(s); @@ -1410,6 +1445,7 @@ void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, vo s->suspended_userdata = userdata; } +SPA_EXPORT void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *userdata) { spa_assert(s); @@ -1422,6 +1458,7 @@ void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *u s->event_userdata = userdata; } +SPA_EXPORT void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { spa_assert(s); @@ -1434,6 +1471,7 @@ void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, s->buffer_attr_userdata = userdata; } +SPA_EXPORT pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1457,6 +1495,7 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi return o; } +SPA_EXPORT pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1479,6 +1518,7 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use return o; } +SPA_EXPORT pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1501,6 +1541,7 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us return o; } +SPA_EXPORT pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1523,6 +1564,7 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u return o; } +SPA_EXPORT pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1550,6 +1592,7 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe return o; } +SPA_EXPORT int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { pa_usec_t res; @@ -1602,6 +1645,7 @@ static pa_usec_t time_counter_diff(const pa_stream *s, pa_usec_t a, pa_usec_t b, } } +SPA_EXPORT int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) { pa_usec_t t, c; @@ -1635,6 +1679,7 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) return 0; } +SPA_EXPORT const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) { spa_assert(s); @@ -1651,6 +1696,7 @@ const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) return &s->timing_info; } +SPA_EXPORT const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) { spa_assert(s); @@ -1658,6 +1704,7 @@ const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) return &s->sample_spec; } +SPA_EXPORT const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) { spa_assert(s); @@ -1665,6 +1712,7 @@ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) return &s->channel_map; } +SPA_EXPORT const pa_format_info* pa_stream_get_format_info(pa_stream *s) { spa_assert(s); @@ -1675,6 +1723,7 @@ const pa_format_info* pa_stream_get_format_info(pa_stream *s) return s->format; } +SPA_EXPORT const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) { spa_assert(s); @@ -1686,6 +1735,7 @@ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) return &s->buffer_attr; } +SPA_EXPORT pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1707,6 +1757,7 @@ pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr return o; } +SPA_EXPORT pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1729,6 +1780,7 @@ pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_strea return o; } +SPA_EXPORT pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1752,6 +1804,7 @@ pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_ return o; } +SPA_EXPORT pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; @@ -1773,6 +1826,7 @@ pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], return o; } +SPA_EXPORT int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) { spa_assert(s); @@ -1787,6 +1841,7 @@ int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) return 0; } +SPA_EXPORT uint32_t pa_stream_get_monitor_stream(pa_stream *s) { spa_assert(s); diff --git a/src/subscribe.c b/src/subscribe.c index 953cfd1c7..3af7fcf5e 100644 --- a/src/subscribe.c +++ b/src/subscribe.c @@ -36,6 +36,7 @@ static void on_subscribed(pa_operation *o, void *userdata) d->cb(o->context, PA_OK, d->userdata); } +SPA_EXPORT pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; @@ -54,6 +55,7 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c return o; } +SPA_EXPORT void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) { pa_assert(c); diff --git a/src/thread-mainloop.c b/src/thread-mainloop.c index 964f05272..0b9e248f1 100644 --- a/src/thread-mainloop.c +++ b/src/thread-mainloop.c @@ -31,6 +31,7 @@ struct pa_threaded_mainloop struct pw_thread_loop *tloop; }; +SPA_EXPORT pa_threaded_mainloop *pa_threaded_mainloop_new(void) { pa_threaded_mainloop *m; @@ -56,6 +57,7 @@ pa_threaded_mainloop *pa_threaded_mainloop_new(void) return NULL; } +SPA_EXPORT void pa_threaded_mainloop_free(pa_threaded_mainloop* m) { pw_thread_loop_destroy(m->tloop); @@ -63,56 +65,67 @@ void pa_threaded_mainloop_free(pa_threaded_mainloop* m) free(m); } +SPA_EXPORT int pa_threaded_mainloop_start(pa_threaded_mainloop *m) { return pw_thread_loop_start(m->tloop); } +SPA_EXPORT void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) { pw_thread_loop_stop(m->tloop); } +SPA_EXPORT void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) { pw_thread_loop_lock(m->tloop); } +SPA_EXPORT void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) { pw_thread_loop_unlock(m->tloop); } +SPA_EXPORT void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) { pw_thread_loop_wait(m->tloop); } +SPA_EXPORT void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) { pw_thread_loop_signal(m->tloop, wait_for_accept); } +SPA_EXPORT void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) { pw_thread_loop_accept(m->tloop); } +SPA_EXPORT int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) { return pa_mainloop_get_retval(m->loop); } +SPA_EXPORT pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) { return pa_mainloop_get_api(m->loop); } +SPA_EXPORT int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m) { return pw_thread_loop_in_thread(m->tloop); } +SPA_EXPORT void pa_threaded_mainloop_set_name(pa_threaded_mainloop *m, const char *name) { } diff --git a/src/timeval.c b/src/timeval.c index dfdeb782d..e8ca09a9d 100644 --- a/src/timeval.c +++ b/src/timeval.c @@ -35,6 +35,7 @@ #define HAVE_GETTIMEOFDAY +SPA_EXPORT struct timeval *pa_gettimeofday(struct timeval *tv) { pa_assert(tv); @@ -71,6 +72,7 @@ struct timeval *pa_gettimeofday(struct timeval *tv) { return tv; } +SPA_EXPORT pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { pa_usec_t r; @@ -97,6 +99,7 @@ pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { return r; } +SPA_EXPORT int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { pa_assert(a); pa_assert(b); @@ -116,6 +119,7 @@ int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { return 0; } +SPA_EXPORT pa_usec_t pa_timeval_age(const struct timeval *tv) { struct timeval now; pa_assert(tv); @@ -123,6 +127,7 @@ pa_usec_t pa_timeval_age(const struct timeval *tv) { return pa_timeval_diff(pa_gettimeofday(&now), tv); } +SPA_EXPORT struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) { time_t secs; pa_assert(tv); @@ -154,6 +159,7 @@ overflow: return tv; } +SPA_EXPORT struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v) { time_t secs; pa_assert(tv); @@ -185,6 +191,7 @@ underflow: return tv; } +SPA_EXPORT struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) { pa_assert(tv); @@ -201,6 +208,7 @@ struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) { return tv; } +SPA_EXPORT pa_usec_t pa_timeval_load(const struct timeval *tv) { if (PA_UNLIKELY(!tv)) diff --git a/src/utf8.c b/src/utf8.c index 7eeed7022..35e4b6d5f 100644 --- a/src/utf8.c +++ b/src/utf8.c @@ -172,10 +172,12 @@ failure: return NULL; } +SPA_EXPORT char* pa_utf8_valid (const char *str) { return utf8_validate(str, NULL); } +SPA_EXPORT char* pa_utf8_filter (const char *str) { char *new_str; @@ -233,22 +235,26 @@ static char* iconv_simple(const char *str, const char *to, const char *from) { return new_str; } +SPA_EXPORT char* pa_utf8_to_locale (const char *str) { return iconv_simple(str, "", "UTF-8"); } +SPA_EXPORT char* pa_locale_to_utf8 (const char *str) { return iconv_simple(str, "UTF-8", ""); } #else +SPA_EXPORT char* pa_utf8_to_locale (const char *str) { pa_assert(str); return pa_ascii_filter(str); } +SPA_EXPORT char* pa_locale_to_utf8 (const char *str) { pa_assert(str); @@ -260,6 +266,7 @@ char* pa_locale_to_utf8 (const char *str) { #endif +SPA_EXPORT char *pa_ascii_valid(const char *str) { const char *p; pa_assert(str); @@ -271,6 +278,7 @@ char *pa_ascii_valid(const char *str) { return (char*) str; } +SPA_EXPORT char *pa_ascii_filter(const char *str) { char *r, *s, *d; pa_assert(str); diff --git a/src/util.c b/src/util.c index 763fab802..b457ccbba 100644 --- a/src/util.c +++ b/src/util.c @@ -26,33 +26,39 @@ #define PA_PATH_SEP_CHAR '/' +SPA_EXPORT char *pa_get_user_name(char *s, size_t l) { return strncpy(s, pw_get_user_name(), l); } +SPA_EXPORT char *pa_get_host_name(char *s, size_t l) { return strncpy(s, pw_get_host_name(), l); } +SPA_EXPORT char *pa_get_fqdn(char *s, size_t l) { return strncpy(s, pw_get_host_name(), l); } +SPA_EXPORT char *pa_get_home_dir(char *s, size_t l) { pw_log_warn("Not Implemented"); return NULL; } +SPA_EXPORT char *pa_get_binary_name(char *s, size_t l) { return strncpy(s, pw_get_prgname(), l); } +SPA_EXPORT char *pa_path_get_filename(const char *p) { char *fn; @@ -66,6 +72,7 @@ char *pa_path_get_filename(const char *p) return (char*) p; } +SPA_EXPORT int pa_msleep(unsigned long t) { struct timespec ts; diff --git a/src/version.c b/src/version.c index 8c9d21c9e..bb0e387ad 100644 --- a/src/version.c +++ b/src/version.c @@ -17,8 +17,11 @@ * Boston, MA 02110-1301, USA. */ +#include + #include +SPA_EXPORT const char* pa_get_library_version(void) { return pa_get_headers_version(); diff --git a/src/volume.c b/src/volume.c index 8e3f382c9..6469ff5eb 100644 --- a/src/volume.c +++ b/src/volume.c @@ -30,6 +30,7 @@ #include "internal.h" #include "sample-util.h" +SPA_EXPORT int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { int i; pa_assert(a); @@ -52,6 +53,7 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { return 1; } +SPA_EXPORT pa_cvolume* pa_cvolume_init(pa_cvolume *a) { unsigned c; @@ -65,6 +67,7 @@ pa_cvolume* pa_cvolume_init(pa_cvolume *a) { return a; } +SPA_EXPORT pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { int i; @@ -81,6 +84,7 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { return a; } +SPA_EXPORT pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { uint64_t sum = 0; unsigned c; @@ -96,6 +100,7 @@ pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { return (pa_volume_t) sum; } +SPA_EXPORT pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { uint64_t sum = 0; unsigned c, n; @@ -122,6 +127,7 @@ pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, p return (pa_volume_t) sum; } +SPA_EXPORT pa_volume_t pa_cvolume_max(const pa_cvolume *a) { pa_volume_t m = PA_VOLUME_MUTED; unsigned c; @@ -136,6 +142,7 @@ pa_volume_t pa_cvolume_max(const pa_cvolume *a) { return m; } +SPA_EXPORT pa_volume_t pa_cvolume_min(const pa_cvolume *a) { pa_volume_t m = PA_VOLUME_MAX; unsigned c; @@ -150,6 +157,7 @@ pa_volume_t pa_cvolume_min(const pa_cvolume *a) { return m; } +SPA_EXPORT pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { pa_volume_t m = PA_VOLUME_MUTED; unsigned c; @@ -173,6 +181,7 @@ pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, p return m; } +SPA_EXPORT pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { pa_volume_t m = PA_VOLUME_MAX; unsigned c; @@ -196,6 +205,7 @@ pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, p return m; } +SPA_EXPORT pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { uint64_t result; @@ -212,6 +222,7 @@ pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { return (pa_volume_t) PA_CLAMP_VOLUME(result); } +SPA_EXPORT pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) { uint64_t result; @@ -238,6 +249,7 @@ static double dB_to_linear(double v) { return pow(10.0, v / 20.0); } +SPA_EXPORT pa_volume_t pa_sw_volume_from_dB(double dB) { if (isinf(dB) < 0 || dB <= PA_DECIBEL_MININFTY) return PA_VOLUME_MUTED; @@ -245,6 +257,7 @@ pa_volume_t pa_sw_volume_from_dB(double dB) { return pa_sw_volume_from_linear(dB_to_linear(dB)); } +SPA_EXPORT double pa_sw_volume_to_dB(pa_volume_t v) { pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), PA_DECIBEL_MININFTY); @@ -255,6 +268,7 @@ double pa_sw_volume_to_dB(pa_volume_t v) { return linear_to_dB(pa_sw_volume_to_linear(v)); } +SPA_EXPORT pa_volume_t pa_sw_volume_from_linear(double v) { if (v <= 0.0) @@ -273,6 +287,7 @@ pa_volume_t pa_sw_volume_from_linear(double v) { return (pa_volume_t) PA_CLAMP_VOLUME((uint64_t) lround(cbrt(v) * PA_VOLUME_NORM)); } +SPA_EXPORT double pa_sw_volume_to_linear(pa_volume_t v) { double f; @@ -289,6 +304,7 @@ double pa_sw_volume_to_linear(pa_volume_t v) { return f*f*f; } +SPA_EXPORT char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { unsigned channel; bool first = true; @@ -320,6 +336,7 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { return s; } +SPA_EXPORT char *pa_volume_snprint(char *s, size_t l, pa_volume_t v) { pa_assert(s); pa_assert(l > 0); @@ -335,6 +352,7 @@ char *pa_volume_snprint(char *s, size_t l, pa_volume_t v) { return s; } +SPA_EXPORT char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c) { unsigned channel; bool first = true; @@ -368,6 +386,7 @@ char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c) { return s; } +SPA_EXPORT char *pa_cvolume_snprint_verbose(char *s, size_t l, const pa_cvolume *c, const pa_channel_map *map, int print_dB) { char *current = s; bool first = true; @@ -410,6 +429,7 @@ char *pa_cvolume_snprint_verbose(char *s, size_t l, const pa_cvolume *c, const p return s; } +SPA_EXPORT char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) { double f; @@ -429,6 +449,7 @@ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) { return s; } +SPA_EXPORT char *pa_volume_snprint_verbose(char *s, size_t l, pa_volume_t v, int print_dB) { char dB[PA_SW_VOLUME_SNPRINT_DB_MAX]; @@ -451,6 +472,7 @@ char *pa_volume_snprint_verbose(char *s, size_t l, pa_volume_t v, int print_dB) return s; } +SPA_EXPORT int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { unsigned c; pa_assert(a); @@ -465,6 +487,7 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { return 1; } +SPA_EXPORT pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { unsigned i; @@ -483,6 +506,7 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const return dest; } +SPA_EXPORT pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) { unsigned i; @@ -500,6 +524,7 @@ pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, return dest; } +SPA_EXPORT pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { unsigned i; @@ -518,6 +543,7 @@ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa return dest; } +SPA_EXPORT pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) { unsigned i; @@ -535,6 +561,7 @@ pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, p return dest; } +SPA_EXPORT int pa_cvolume_valid(const pa_cvolume *v) { unsigned c; @@ -578,6 +605,7 @@ static bool on_rear(pa_channel_position_t p) { return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_REAR); } +SPA_EXPORT pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) { int a, b; pa_cvolume result; @@ -628,6 +656,7 @@ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa return v; } +SPA_EXPORT int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) { pa_assert(v); @@ -639,6 +668,7 @@ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) { return v->channels == ss->channels; } +SPA_EXPORT int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) { pa_assert(v); pa_assert(cm); @@ -686,6 +716,7 @@ static void get_avg(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *r = right / n_right; } +SPA_EXPORT float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) { pa_volume_t left, right; @@ -753,6 +784,7 @@ static pa_cvolume* set_balance(pa_cvolume *v, const pa_channel_map *map, float n } +SPA_EXPORT pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) { pa_assert(map); pa_assert(v); @@ -767,6 +799,7 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo return set_balance(v, map, new_balance, on_left, on_right); } +SPA_EXPORT pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) { unsigned c; pa_volume_t t = 0; @@ -787,6 +820,7 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) { return v; } +SPA_EXPORT #if PA_CHECK_VERSION(12, 0, 0) pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, const pa_channel_map *cm, pa_channel_position_mask_t mask) { #else @@ -815,6 +849,7 @@ pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map return v; } +SPA_EXPORT float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) { pa_volume_t rear, front; @@ -837,6 +872,7 @@ float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) { return 1.0f - ((float) rear / (float) front); } +SPA_EXPORT pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float new_fade) { pa_assert(map); pa_assert(v); @@ -851,6 +887,7 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float return set_balance(v, map, new_fade, on_rear, on_front); } +SPA_EXPORT float pa_cvolume_get_lfe_balance(const pa_cvolume *v, const pa_channel_map *map) { pa_volume_t hfe, lfe; @@ -873,6 +910,7 @@ float pa_cvolume_get_lfe_balance(const pa_cvolume *v, const pa_channel_map *map) return 1.0f - ((float) hfe / (float) lfe); } +SPA_EXPORT pa_cvolume* pa_cvolume_set_lfe_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) { pa_assert(map); pa_assert(v); @@ -887,6 +925,7 @@ pa_cvolume* pa_cvolume_set_lfe_balance(pa_cvolume *v, const pa_channel_map *map, return set_balance(v, map, new_balance, on_hfe, on_lfe); } +SPA_EXPORT pa_cvolume* pa_cvolume_set_position( pa_cvolume *cv, const pa_channel_map *map, @@ -912,6 +951,7 @@ pa_cvolume* pa_cvolume_set_position( return good ? cv : NULL; } +SPA_EXPORT pa_volume_t pa_cvolume_get_position( pa_cvolume *cv, const pa_channel_map *map, @@ -934,6 +974,7 @@ pa_volume_t pa_cvolume_get_position( return v; } +SPA_EXPORT pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { unsigned i; @@ -952,6 +993,7 @@ pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvo return dest; } +SPA_EXPORT pa_cvolume* pa_cvolume_inc_clamp(pa_cvolume *v, pa_volume_t inc, pa_volume_t limit) { pa_volume_t m; @@ -970,10 +1012,12 @@ pa_cvolume* pa_cvolume_inc_clamp(pa_cvolume *v, pa_volume_t inc, pa_volume_t lim return pa_cvolume_scale(v, m); } +SPA_EXPORT pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc) { return pa_cvolume_inc_clamp(v, inc, PA_VOLUME_MAX); } +SPA_EXPORT pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) { pa_volume_t m; diff --git a/src/xmalloc.c b/src/xmalloc.c index 30e6ca6b4..b38af54d0 100644 --- a/src/xmalloc.c +++ b/src/xmalloc.c @@ -35,6 +35,7 @@ static void oom(void) { _exit(1); } +SPA_EXPORT void* pa_xmalloc(size_t l) { void *p; @@ -47,6 +48,7 @@ void* pa_xmalloc(size_t l) return p; } +SPA_EXPORT void *pa_xmalloc0(size_t l) { void *p; @@ -59,6 +61,7 @@ void *pa_xmalloc0(size_t l) return p; } +SPA_EXPORT void *pa_xrealloc(void *ptr, size_t size) { void *p; @@ -70,6 +73,7 @@ void *pa_xrealloc(void *ptr, size_t size) return p; } +SPA_EXPORT void pa_xfree(void *p) { int saved_errno; @@ -80,6 +84,7 @@ void pa_xfree(void *p) errno = saved_errno; } +SPA_EXPORT char *pa_xstrdup(const char *s) { if (!s) @@ -87,6 +92,7 @@ char *pa_xstrdup(const char *s) return pa_xmemdup(s, strlen(s)+1); } +SPA_EXPORT char *pa_xstrndup(const char *s, size_t l) { char *e, *r; @@ -103,6 +109,7 @@ char *pa_xstrndup(const char *s, size_t l) return r; } +SPA_EXPORT void* pa_xmemdup(const void *p, size_t l) { if (!p) From 61edd78bf4a3bba5edab0ba0c10a7d7f12ff3f7a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 18 Feb 2019 12:27:38 +0100 Subject: [PATCH 082/116] fix for api changes --- src/context.c | 16 ++++++++++------ src/introspect.c | 22 ++++++++++++++-------- src/operation.c | 2 +- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/context.c b/src/context.c index 0236e4be7..c6f285e2a 100644 --- a/src/context.c +++ b/src/context.c @@ -287,7 +287,7 @@ static int set_mask(pa_context *c, struct global *g) return 1; } -static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, +static int registry_event_global(void *data, uint32_t id, uint32_t parent_id, uint32_t permissions, uint32_t type, uint32_t version, const struct spa_dict *props) { @@ -304,26 +304,28 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, if (set_mask(c, g) == 0) { global_free(g); - return; + return 0; } pw_log_debug("mask %d/%d", g->mask, g->event); emit_event(c, g, PA_SUBSCRIPTION_EVENT_NEW); + return 0; } -static void registry_event_global_remove(void *object, uint32_t id) +static int registry_event_global_remove(void *object, uint32_t id) { pa_context *c = object; struct global *g; pw_log_debug("context %p: remove %d", c, id); if ((g = pa_context_find_global(c, id)) == NULL) - return; + return 0; emit_event(c, g, PA_SUBSCRIPTION_EVENT_REMOVE); pw_log_debug("context %p: free %d %p", c, id, g); global_free(g); + return 0; } static const struct pw_registry_proxy_events registry_events = @@ -357,17 +359,19 @@ static void complete_operations(pa_context *c, uint32_t seq) } } -static void core_info(void *data, const struct pw_core_info *info) +static int core_info(void *data, const struct pw_core_info *info) { pa_context *c = data; c->core_info = pw_core_info_update(c->core_info, info); + return 0; } -static void core_done(void *data, uint32_t seq) +static int core_done(void *data, uint32_t id, uint32_t seq) { pa_context *c = data; pw_log_debug("done %d", seq); complete_operations(c, seq); + return 0; } static const struct pw_core_proxy_events core_events = { diff --git a/src/introspect.c b/src/introspect.c index dc2814e5f..4f014ea78 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -27,19 +27,21 @@ #include "internal.h" -static void node_event_info(void *object, const struct pw_node_info *info) +static int node_event_info(void *object, const struct pw_node_info *info) { struct global *g = object; pw_log_debug("update %d", g->id); g->info = pw_node_info_update(g->info, info); + return 0; } -static void node_event_param(void *object, +static int node_event_param(void *object, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { struct global *g = object; pw_log_debug("update param %d", g->id); + return 0; } static const struct pw_node_proxy_events node_events = { @@ -48,7 +50,7 @@ static const struct pw_node_proxy_events node_events = { .param = node_event_param, }; -static void module_event_info(void *object, const struct pw_module_info *info) +static int module_event_info(void *object, const struct pw_module_info *info) { struct global *g = object; pa_module_info *i = &g->module_info.info; @@ -71,6 +73,7 @@ static void module_event_info(void *object, const struct pw_module_info *info) i->argument = info->args; i->n_used = -1; i->auto_unload = false; + return 0; } static const struct pw_module_proxy_events module_events = { @@ -78,7 +81,7 @@ static const struct pw_module_proxy_events module_events = { .info = module_event_info, }; -static void client_event_info(void *object, const struct pw_client_info *info) +static int client_event_info(void *object, const struct pw_client_info *info) { struct global *g = object; pa_client_info *i = &g->client_info.info; @@ -99,6 +102,7 @@ static void client_event_info(void *object, const struct pw_client_info *info) i->driver = info->props ? spa_dict_lookup(info->props, PW_CLIENT_PROP_PROTOCOL) : NULL; } + return 0; } static const struct pw_client_proxy_events client_events = { @@ -106,7 +110,7 @@ static const struct pw_client_proxy_events client_events = { .info = client_event_info, }; -static void device_event_param(void *object, +static int device_event_param(void *object, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { @@ -123,7 +127,7 @@ static void device_event_param(void *object, SPA_PARAM_PROFILE_index, SPA_POD_Int(&id), SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { pw_log_warn("device %d: can't parse profile", g->id); - return; + return -EINVAL; } pw_array_add_ptr(&g->card_info.profiles, pw_spa_pod_copy(param)); pw_log_debug("device %d: enum profile %d: \"%s\"", g->id, id, name); @@ -136,7 +140,7 @@ static void device_event_param(void *object, SPA_TYPE_OBJECT_ParamProfile, NULL, SPA_PARAM_PROFILE_index, SPA_POD_Int(&id)) < 0) { pw_log_warn("device %d: can't parse profile", g->id); - return; + return -EINVAL; } g->card_info.active_profile = id; pw_log_debug("device %d: current profile %d", g->id, id); @@ -145,9 +149,10 @@ static void device_event_param(void *object, default: break; } + return 0; } -static void device_event_info(void *object, const struct pw_device_info *info) +static int device_event_info(void *object, const struct pw_device_info *info) { struct global *g = object; pa_card_info *i = &g->card_info.info; @@ -166,6 +171,7 @@ static void device_event_info(void *object, const struct pw_device_info *info) else i->proplist = pa_proplist_new_dict(info->props); } + return 0; } static const struct pw_device_proxy_events device_events = { diff --git a/src/operation.c b/src/operation.c index 3fbb7af28..6b7507b53 100644 --- a/src/operation.c +++ b/src/operation.c @@ -54,7 +54,7 @@ int pa_operation_sync(pa_operation *o) pa_context *c = o->context; o->seq = ++c->seq; pw_log_debug("operation %p: sync %d", o, o->seq); - pw_core_proxy_sync(c->core_proxy, o->seq); + pw_core_proxy_sync(c->core_proxy, 0, o->seq); return 0; } From 71e5c131917b0cad04911f6cb868d44691dbae8d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 20 Feb 2019 17:50:22 +0100 Subject: [PATCH 083/116] operation: fix for async changes --- src/internal.h | 2 -- src/introspect.c | 2 +- src/operation.c | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/internal.h b/src/internal.h index bf806424d..970969043 100644 --- a/src/internal.h +++ b/src/internal.h @@ -271,8 +271,6 @@ struct pa_context { pa_proplist *proplist; pa_mainloop_api *mainloop; - uint32_t seq; - int error; pa_context_state_t state; diff --git a/src/introspect.c b/src/introspect.c index 4f014ea78..20acc9fce 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -129,7 +129,7 @@ static int device_event_param(void *object, pw_log_warn("device %d: can't parse profile", g->id); return -EINVAL; } - pw_array_add_ptr(&g->card_info.profiles, pw_spa_pod_copy(param)); + pw_array_add_ptr(&g->card_info.profiles, spa_pod_copy(param)); pw_log_debug("device %d: enum profile %d: \"%s\"", g->id, id, name); break; } diff --git a/src/operation.c b/src/operation.c index 6b7507b53..2e9e863f8 100644 --- a/src/operation.c +++ b/src/operation.c @@ -52,9 +52,8 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb int pa_operation_sync(pa_operation *o) { pa_context *c = o->context; - o->seq = ++c->seq; + o->seq = pw_core_proxy_sync(c->core_proxy, 0); pw_log_debug("operation %p: sync %d", o, o->seq); - pw_core_proxy_sync(c->core_proxy, 0, o->seq); return 0; } From bf91b8c002b51cf9af1c7be4b0e1e50bcc83cc1a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 25 Feb 2019 12:27:51 +0100 Subject: [PATCH 084/116] pulse: update for async changes --- src/introspect.c | 4 ++-- src/operation.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index 20acc9fce..742e27978 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -35,7 +35,7 @@ static int node_event_info(void *object, const struct pw_node_info *info) return 0; } -static int node_event_param(void *object, +static int node_event_param(void *object, uint32_t seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { @@ -110,7 +110,7 @@ static const struct pw_client_proxy_events client_events = { .info = client_event_info, }; -static int device_event_param(void *object, +static int device_event_param(void *object, uint32_t seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { diff --git a/src/operation.c b/src/operation.c index 2e9e863f8..5115815af 100644 --- a/src/operation.c +++ b/src/operation.c @@ -52,8 +52,8 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb int pa_operation_sync(pa_operation *o) { pa_context *c = o->context; - o->seq = pw_core_proxy_sync(c->core_proxy, 0); pw_log_debug("operation %p: sync %d", o, o->seq); + o->seq = pw_core_proxy_sync(c->core_proxy, 0, 0); return 0; } From 83b2aca07236cd29a2f6d138cadd324571b1c8d0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 25 Feb 2019 17:16:14 +0100 Subject: [PATCH 085/116] pulse: update for seq change --- src/context.c | 4 ++-- src/internal.h | 2 +- src/introspect.c | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/context.c b/src/context.c index c6f285e2a..0df4a0c0e 100644 --- a/src/context.c +++ b/src/context.c @@ -346,7 +346,7 @@ static void on_ready(pa_operation *o, void *userdata) pa_context_set_state(d->context, PA_CONTEXT_READY); } -static void complete_operations(pa_context *c, uint32_t seq) +static void complete_operations(pa_context *c, int seq) { pa_operation *o, *t; spa_list_for_each_safe(o, t, &c->operations, link) { @@ -366,7 +366,7 @@ static int core_info(void *data, const struct pw_core_info *info) return 0; } -static int core_done(void *data, uint32_t id, uint32_t seq) +static int core_done(void *data, uint32_t id, int seq) { pa_context *c = data; pw_log_debug("done %d", seq); diff --git a/src/internal.h b/src/internal.h index 970969043..ea8413c20 100644 --- a/src/internal.h +++ b/src/internal.h @@ -389,7 +389,7 @@ struct pa_operation pa_context *context; pa_stream *stream; - uint32_t seq; + int seq; pa_operation_state_t state; pa_operation_cb_t callback; diff --git a/src/introspect.c b/src/introspect.c index 742e27978..3a762033a 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -35,7 +35,7 @@ static int node_event_info(void *object, const struct pw_node_info *info) return 0; } -static int node_event_param(void *object, uint32_t seq, +static int node_event_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { @@ -110,7 +110,7 @@ static const struct pw_client_proxy_events client_events = { .info = client_event_info, }; -static int device_event_param(void *object, uint32_t seq, +static int device_event_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { @@ -267,13 +267,13 @@ static int ensure_global(pa_context *c, struct global *g) switch (g->type) { case PW_TYPE_INTERFACE_Node: pw_node_proxy_enum_params((struct pw_node_proxy*)g->proxy, - SPA_PARAM_EnumFormat, 0, -1, NULL); + 0, SPA_PARAM_EnumFormat, 0, -1, NULL); break; case PW_TYPE_INTERFACE_Device: pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, - SPA_PARAM_EnumProfile, 0, -1, NULL); + 0, SPA_PARAM_EnumProfile, 0, -1, NULL); pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, - SPA_PARAM_Profile, 0, -1, NULL); + 0, SPA_PARAM_Profile, 0, -1, NULL); break; default: break; From df5c1f4c6eae1e6fb17d0de6e6a0358c86ba3ffc Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 1 Mar 2019 14:03:28 +0100 Subject: [PATCH 086/116] pulse: events are void --- src/context.c | 16 ++++++---------- src/introspect.c | 22 ++++++++-------------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/src/context.c b/src/context.c index 0df4a0c0e..066f6da62 100644 --- a/src/context.c +++ b/src/context.c @@ -287,7 +287,7 @@ static int set_mask(pa_context *c, struct global *g) return 1; } -static int registry_event_global(void *data, uint32_t id, uint32_t parent_id, +static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, uint32_t permissions, uint32_t type, uint32_t version, const struct spa_dict *props) { @@ -304,28 +304,26 @@ static int registry_event_global(void *data, uint32_t id, uint32_t parent_id, if (set_mask(c, g) == 0) { global_free(g); - return 0; + return; } pw_log_debug("mask %d/%d", g->mask, g->event); emit_event(c, g, PA_SUBSCRIPTION_EVENT_NEW); - return 0; } -static int registry_event_global_remove(void *object, uint32_t id) +static void registry_event_global_remove(void *object, uint32_t id) { pa_context *c = object; struct global *g; pw_log_debug("context %p: remove %d", c, id); if ((g = pa_context_find_global(c, id)) == NULL) - return 0; + return; emit_event(c, g, PA_SUBSCRIPTION_EVENT_REMOVE); pw_log_debug("context %p: free %d %p", c, id, g); global_free(g); - return 0; } static const struct pw_registry_proxy_events registry_events = @@ -359,19 +357,17 @@ static void complete_operations(pa_context *c, int seq) } } -static int core_info(void *data, const struct pw_core_info *info) +static void core_info(void *data, const struct pw_core_info *info) { pa_context *c = data; c->core_info = pw_core_info_update(c->core_info, info); - return 0; } -static int core_done(void *data, uint32_t id, int seq) +static void core_done(void *data, uint32_t id, int seq) { pa_context *c = data; pw_log_debug("done %d", seq); complete_operations(c, seq); - return 0; } static const struct pw_core_proxy_events core_events = { diff --git a/src/introspect.c b/src/introspect.c index 3a762033a..106aa2bed 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -27,21 +27,19 @@ #include "internal.h" -static int node_event_info(void *object, const struct pw_node_info *info) +static void node_event_info(void *object, const struct pw_node_info *info) { struct global *g = object; pw_log_debug("update %d", g->id); g->info = pw_node_info_update(g->info, info); - return 0; } -static int node_event_param(void *object, int seq, +static void node_event_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { struct global *g = object; pw_log_debug("update param %d", g->id); - return 0; } static const struct pw_node_proxy_events node_events = { @@ -50,7 +48,7 @@ static const struct pw_node_proxy_events node_events = { .param = node_event_param, }; -static int module_event_info(void *object, const struct pw_module_info *info) +static void module_event_info(void *object, const struct pw_module_info *info) { struct global *g = object; pa_module_info *i = &g->module_info.info; @@ -73,7 +71,6 @@ static int module_event_info(void *object, const struct pw_module_info *info) i->argument = info->args; i->n_used = -1; i->auto_unload = false; - return 0; } static const struct pw_module_proxy_events module_events = { @@ -81,7 +78,7 @@ static const struct pw_module_proxy_events module_events = { .info = module_event_info, }; -static int client_event_info(void *object, const struct pw_client_info *info) +static void client_event_info(void *object, const struct pw_client_info *info) { struct global *g = object; pa_client_info *i = &g->client_info.info; @@ -102,7 +99,6 @@ static int client_event_info(void *object, const struct pw_client_info *info) i->driver = info->props ? spa_dict_lookup(info->props, PW_CLIENT_PROP_PROTOCOL) : NULL; } - return 0; } static const struct pw_client_proxy_events client_events = { @@ -110,7 +106,7 @@ static const struct pw_client_proxy_events client_events = { .info = client_event_info, }; -static int device_event_param(void *object, int seq, +static void device_event_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { @@ -127,7 +123,7 @@ static int device_event_param(void *object, int seq, SPA_PARAM_PROFILE_index, SPA_POD_Int(&id), SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { pw_log_warn("device %d: can't parse profile", g->id); - return -EINVAL; + return; } pw_array_add_ptr(&g->card_info.profiles, spa_pod_copy(param)); pw_log_debug("device %d: enum profile %d: \"%s\"", g->id, id, name); @@ -140,7 +136,7 @@ static int device_event_param(void *object, int seq, SPA_TYPE_OBJECT_ParamProfile, NULL, SPA_PARAM_PROFILE_index, SPA_POD_Int(&id)) < 0) { pw_log_warn("device %d: can't parse profile", g->id); - return -EINVAL; + return; } g->card_info.active_profile = id; pw_log_debug("device %d: current profile %d", g->id, id); @@ -149,10 +145,9 @@ static int device_event_param(void *object, int seq, default: break; } - return 0; } -static int device_event_info(void *object, const struct pw_device_info *info) +static void device_event_info(void *object, const struct pw_device_info *info) { struct global *g = object; pa_card_info *i = &g->card_info.info; @@ -171,7 +166,6 @@ static int device_event_info(void *object, const struct pw_device_info *info) else i->proplist = pa_proplist_new_dict(info->props); } - return 0; } static const struct pw_device_proxy_events device_events = { From d379fa348bc23f3ebfb02cb0be3439d83ee4b326 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 8 Mar 2019 17:01:38 +0100 Subject: [PATCH 087/116] stream: ignore state changes when terminated --- src/stream.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/stream.c b/src/stream.c index 4a58af058..458d4cdcf 100644 --- a/src/stream.c +++ b/src/stream.c @@ -213,6 +213,9 @@ static void stream_state_changed(void *data, enum pw_stream_state old, pw_log_debug("stream %p: state '%s'->'%s'", s, pw_stream_state_as_string(old), pw_stream_state_as_string(state)); + if (s->state == PA_STREAM_TERMINATED) + return; + switch(state) { case PW_STREAM_STATE_ERROR: pa_stream_set_state(s, PA_STREAM_FAILED); From f54412e722b292a50f155b64d0290f99e4c740c3 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 11 Mar 2019 16:53:48 +0100 Subject: [PATCH 088/116] introspect: only introspect readable node and device params Check the node and device params for READ access before attempting to enumerate them. --- src/introspect.c | 53 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index 106aa2bed..ad6a4cdbd 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -30,8 +30,26 @@ static void node_event_info(void *object, const struct pw_node_info *info) { struct global *g = object; + uint32_t i; + pw_log_debug("update %d", g->id); g->info = pw_node_info_update(g->info, info); + + if (info->change_mask & SPA_NODE_CHANGE_MASK_PARAMS) { + for (i = 0; i < info->n_params; i++) { + if (!(info->params[i].flags & SPA_PARAM_INFO_READ)) + continue; + + switch (info->params[i].id) { + case SPA_PARAM_EnumFormat: + pw_node_proxy_enum_params((struct pw_node_proxy*)g->proxy, + 0, SPA_PARAM_EnumFormat, 0, -1, NULL); + break; + default: + break; + } + } + } } static void node_event_param(void *object, int seq, @@ -151,6 +169,7 @@ static void device_event_info(void *object, const struct pw_device_info *info) { struct global *g = object; pa_card_info *i = &g->card_info.info; + uint32_t n; pw_log_debug("update %d", g->id); info = g->info = pw_device_info_update(g->info, info); @@ -166,6 +185,25 @@ static void device_event_info(void *object, const struct pw_device_info *info) else i->proplist = pa_proplist_new_dict(info->props); } + if (info->change_mask & SPA_DEVICE_CHANGE_MASK_PARAMS) { + for (n = 0; n < info->n_params; n++) { + if (!(info->params[n].flags & SPA_PARAM_INFO_READ)) + continue; + + switch (info->params[n].id) { + case SPA_PARAM_EnumProfile: + pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, + 0, SPA_PARAM_EnumProfile, 0, -1, NULL); + break; + case SPA_PARAM_Profile: + pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, + 0, SPA_PARAM_Profile, 0, -1, NULL); + break; + default: + break; + } + } + } } static const struct pw_device_proxy_events device_events = { @@ -257,21 +295,6 @@ static int ensure_global(pa_context *c, struct global *g) pw_proxy_add_proxy_listener(g->proxy, &g->proxy_proxy_listener, events, g); g->destroy = destroy; - - switch (g->type) { - case PW_TYPE_INTERFACE_Node: - pw_node_proxy_enum_params((struct pw_node_proxy*)g->proxy, - 0, SPA_PARAM_EnumFormat, 0, -1, NULL); - break; - case PW_TYPE_INTERFACE_Device: - pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, - 0, SPA_PARAM_EnumProfile, 0, -1, NULL); - pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, - 0, SPA_PARAM_Profile, 0, -1, NULL); - break; - default: - break; - } return 0; } From 3eb1a09cd2dc638fd0e8a838083800d85d68ad64 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 11 Mar 2019 18:05:24 +0100 Subject: [PATCH 089/116] introspect: wait for enum to complete Trigger another sync after starting node or device param enum --- src/internal.h | 1 + src/introspect.c | 86 +++++++++++++++++++++++++++--------------------- 2 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/internal.h b/src/internal.h index ea8413c20..2b980c469 100644 --- a/src/internal.h +++ b/src/internal.h @@ -217,6 +217,7 @@ struct global { pa_subscription_mask_t mask; pa_subscription_event_type_t event; + pa_operation *operation; void *info; pw_destroy_t destroy; diff --git a/src/introspect.c b/src/introspect.c index ad6a4cdbd..8d00ddaf2 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -50,6 +50,10 @@ static void node_event_info(void *object, const struct pw_node_info *info) } } } + if (g->operation) { + pa_operation_sync(g->operation); + g->operation = NULL; + } } static void node_event_param(void *object, int seq, @@ -171,7 +175,7 @@ static void device_event_info(void *object, const struct pw_device_info *info) pa_card_info *i = &g->card_info.info; uint32_t n; - pw_log_debug("update %d", g->id); + pw_log_debug("update %d %"PRIu64, g->id, info->change_mask); info = g->info = pw_device_info_update(g->info, info); i->index = g->id; @@ -185,7 +189,7 @@ static void device_event_info(void *object, const struct pw_device_info *info) else i->proplist = pa_proplist_new_dict(info->props); } - if (info->change_mask & SPA_DEVICE_CHANGE_MASK_PARAMS) { + if (info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) { for (n = 0; n < info->n_params; n++) { if (!(info->params[n].flags & SPA_PARAM_INFO_READ)) continue; @@ -204,6 +208,10 @@ static void device_event_info(void *object, const struct pw_device_info *info) } } } + if (g->operation) { + pa_operation_sync(g->operation); + g->operation = NULL; + } } static const struct pw_device_proxy_events device_events = { @@ -251,7 +259,7 @@ static void device_destroy(void *data) pw_device_info_free(global->info); } -static int ensure_global(pa_context *c, struct global *g) +static int ensure_global(pa_context *c, struct global *g, pa_operation *o) { uint32_t client_version; const void *events; @@ -295,15 +303,18 @@ static int ensure_global(pa_context *c, struct global *g) pw_proxy_add_proxy_listener(g->proxy, &g->proxy_proxy_listener, events, g); g->destroy = destroy; + if (o) + g->operation = o; + return 0; } -static void ensure_types(pa_context *c, uint32_t mask) +static void ensure_types(pa_context *c, uint32_t mask, pa_operation *o) { struct global *g; spa_list_for_each(g, &c->globals, link) { if (g->mask & mask) - ensure_global(c, g); + ensure_global(c, g, o); } } @@ -419,15 +430,15 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) return NULL; - ensure_global(c, g); - o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; d->global = g; + ensure_global(c, g, o); pa_operation_sync(o); + return o; } @@ -450,7 +461,6 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_ if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) return NULL; - ensure_global(c, g); o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data)); d = o->userdata; @@ -458,6 +468,7 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_ d->cb = cb; d->userdata = userdata; d->global = g; + ensure_global(c, g, o); pa_operation_sync(o); return o; } @@ -490,12 +501,13 @@ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - ensure_types(c, PA_SUBSCRIPTION_MASK_SINK); o = pa_operation_new(c, NULL, sink_info_list, sizeof(struct sink_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; + + ensure_types(c, PA_SUBSCRIPTION_MASK_SINK, o); pa_operation_sync(o); return o; @@ -509,7 +521,7 @@ static void set_node_volume(pa_context *c, struct global *g, const pa_cvolume *v v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - ensure_global(c, g); + ensure_global(c, g, NULL); pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, SPA_PARAM_Props, 0, @@ -523,7 +535,7 @@ static void set_node_mute(pa_context *c, struct global *g, bool mute) char buf[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - ensure_global(c, g); + ensure_global(c, g, NULL); pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, SPA_PARAM_Props, 0, @@ -782,15 +794,16 @@ pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) return NULL; - ensure_global(c, g); - o = pa_operation_new(c, NULL, source_info, sizeof(struct source_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; d->global = g; + + ensure_global(c, g, o); pa_operation_sync(o); + return o; } @@ -812,15 +825,15 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, p if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) return NULL; - ensure_global(c, g); - o = pa_operation_new(c, NULL, source_info, sizeof(struct source_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; d->global = g; + ensure_global(c, g, o); pa_operation_sync(o); + return o; } @@ -852,12 +865,13 @@ pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - ensure_types(c, PA_SUBSCRIPTION_MASK_SOURCE); o = pa_operation_new(c, NULL, source_info_list, sizeof(struct source_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; + + ensure_types(c, PA_SUBSCRIPTION_MASK_SOURCE, o); pa_operation_sync(o); return o; @@ -1103,14 +1117,13 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_ if (!(g->mask & PA_SUBSCRIPTION_MASK_MODULE)) return NULL; - ensure_global(c, g); - o = pa_operation_new(c, NULL, module_info, sizeof(struct module_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; d->global = g; + ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -1144,12 +1157,13 @@ pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - ensure_types(c, PA_SUBSCRIPTION_MASK_MODULE); o = pa_operation_new(c, NULL, module_info_list, sizeof(struct module_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; + + ensure_types(c, PA_SUBSCRIPTION_MASK_MODULE, o); pa_operation_sync(o); return o; @@ -1208,14 +1222,13 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_ if (!(g->mask & PA_SUBSCRIPTION_MASK_CLIENT)) return NULL; - ensure_global(c, g); - o = pa_operation_new(c, NULL, client_info, sizeof(struct client_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; d->global = g; + ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -1249,12 +1262,13 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - ensure_types(c, PA_SUBSCRIPTION_MASK_CLIENT); o = pa_operation_new(c, NULL, client_info_list, sizeof(struct client_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; + + ensure_types(c, PA_SUBSCRIPTION_MASK_CLIENT, o); pa_operation_sync(o); return o; @@ -1370,14 +1384,13 @@ pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_ if (!(g->mask & PA_SUBSCRIPTION_MASK_CARD)) return NULL; - ensure_global(c, g); - o = pa_operation_new(c, NULL, card_info, sizeof(struct card_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; d->global = g; + ensure_global(c, g, o); pa_operation_sync(o); return o; } @@ -1399,14 +1412,13 @@ pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_CARD, name)) == NULL) return NULL; - ensure_global(c, g); - o = pa_operation_new(c, NULL, card_info, sizeof(struct card_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; d->global = g; + ensure_global(c, g, o); pa_operation_sync(o); return o; } @@ -1439,12 +1451,13 @@ pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - ensure_types(c, PA_SUBSCRIPTION_MASK_CARD); o = pa_operation_new(c, NULL, card_info_list, sizeof(struct card_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; + + ensure_types(c, PA_SUBSCRIPTION_MASK_CARD, o); pa_operation_sync(o); return o; @@ -1515,8 +1528,6 @@ pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, if (!(g->mask & PA_SUBSCRIPTION_MASK_CARD)) return NULL; - ensure_global(c, g); - pw_log_debug("Card set profile %s", profile); o = pa_operation_new(c, NULL, card_profile, sizeof(struct card_data)); @@ -1526,6 +1537,7 @@ pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, d->userdata = userdata; d->global = g; d->profile = strdup(profile); + ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -1547,8 +1559,6 @@ pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_CARD, name)) == NULL) return NULL; - ensure_global(c, g); - pw_log_debug("Card set profile %s", profile); o = pa_operation_new(c, NULL, card_profile, sizeof(struct card_data)); @@ -1558,6 +1568,7 @@ pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name d->userdata = userdata; d->global = g; d->profile = strdup(profile); + ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -1689,14 +1700,13 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK_INPUT)) return NULL; - ensure_global(c, g); - o = pa_operation_new(c, NULL, sink_input_info, sizeof(struct sink_input_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; d->global = g; + ensure_global(c, g, o); pa_operation_sync(o); return o; } @@ -1731,13 +1741,15 @@ pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_i pw_log_debug("context %p", c); - ensure_types(c, PA_SUBSCRIPTION_MASK_SINK_INPUT); o = pa_operation_new(c, NULL, sink_input_info_list, sizeof(struct sink_input_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; + + ensure_types(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, o); pa_operation_sync(o); + return o; } @@ -1957,14 +1969,13 @@ pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_ if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)) return NULL; - ensure_global(c, g); - o = pa_operation_new(c, NULL, source_output_info, sizeof(struct source_output_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; d->global = g; + ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -1998,12 +2009,13 @@ pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_ou PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - ensure_types(c, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT); o = pa_operation_new(c, NULL, source_output_info_list, sizeof(struct source_output_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; + + ensure_types(c, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, o); pa_operation_sync(o); return o; From cb7b25277b866ca1de6b2dd2886f1cef9563b562 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 12 Mar 2019 12:15:28 +0100 Subject: [PATCH 090/116] context: cancel operations when globals are removed Keep track of all the operations pending for a global and cancel them when the global is removed. --- src/context.c | 17 +++++++++++++---- src/internal.h | 6 +++++- src/introspect.c | 23 +++++++++++++---------- src/operation.c | 2 ++ 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/context.c b/src/context.c index 066f6da62..b6f18da6f 100644 --- a/src/context.c +++ b/src/context.c @@ -37,9 +37,17 @@ int pa_context_set_error(pa_context *c, int error) { return error; } -static void global_free(struct global *g) +static void global_free(pa_context *c, struct global *g) { + pa_operation *o, *t; + spa_list_remove(&g->link); + + spa_list_for_each_safe(o, t, &g->operations, owner_link) + pa_operation_cancel(o); + + if (g->proxy) + spa_hook_remove(&g->proxy_proxy_listener); if (g->props) pw_properties_free(g->props); if (g->destroy) @@ -60,7 +68,7 @@ static void context_unlink(pa_context *c) PA_STREAM_FAILED : PA_STREAM_TERMINATED); } spa_list_consume(g, &c->globals, link) - global_free(g); + global_free(c, g); spa_list_consume(o, &c->operations, link) pa_operation_cancel(o); @@ -300,10 +308,11 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, g->parent_id = parent_id; g->type = type; g->props = props ? pw_properties_new_dict(props) : NULL; + spa_list_init(&g->operations); spa_list_append(&c->globals, &g->link); if (set_mask(c, g) == 0) { - global_free(g); + global_free(c, g); return; } @@ -323,7 +332,7 @@ static void registry_event_global_remove(void *object, uint32_t id) emit_event(c, g, PA_SUBSCRIPTION_EVENT_REMOVE); pw_log_debug("context %p: free %d %p", c, id, g); - global_free(g); + global_free(c, g); } static const struct pw_registry_proxy_events registry_events = diff --git a/src/internal.h b/src/internal.h index 2b980c469..c562531f2 100644 --- a/src/internal.h +++ b/src/internal.h @@ -217,7 +217,8 @@ struct global { pa_subscription_mask_t mask; pa_subscription_event_type_t event; - pa_operation *operation; + struct spa_list operations; + void *info; pw_destroy_t destroy; @@ -390,6 +391,9 @@ struct pa_operation pa_context *context; pa_stream *stream; + struct spa_list owner_link; + struct global *owner; + int seq; pa_operation_state_t state; diff --git a/src/introspect.c b/src/introspect.c index 8d00ddaf2..b6843f524 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -30,6 +30,7 @@ static void node_event_info(void *object, const struct pw_node_info *info) { struct global *g = object; + pa_operation *o; uint32_t i; pw_log_debug("update %d", g->id); @@ -50,10 +51,8 @@ static void node_event_info(void *object, const struct pw_node_info *info) } } } - if (g->operation) { - pa_operation_sync(g->operation); - g->operation = NULL; - } + spa_list_for_each(o, &g->operations, owner_link) + pa_operation_sync(o); } static void node_event_param(void *object, int seq, @@ -173,6 +172,7 @@ static void device_event_info(void *object, const struct pw_device_info *info) { struct global *g = object; pa_card_info *i = &g->card_info.info; + pa_operation *o; uint32_t n; pw_log_debug("update %d %"PRIu64, g->id, info->change_mask); @@ -208,10 +208,8 @@ static void device_event_info(void *object, const struct pw_device_info *info) } } } - if (g->operation) { - pa_operation_sync(g->operation); - g->operation = NULL; - } + spa_list_for_each(o, &g->operations, owner_link) + pa_operation_sync(o); } static const struct pw_device_proxy_events device_events = { @@ -265,6 +263,13 @@ static int ensure_global(pa_context *c, struct global *g, pa_operation *o) const void *events; pw_destroy_t destroy; + if (o) { + if (o->owner) + spa_list_remove(&o->owner_link); + o->owner = g; + spa_list_append(&g->operations, &o->owner_link); + } + if (g->proxy != NULL) return 0; @@ -303,8 +308,6 @@ static int ensure_global(pa_context *c, struct global *g, pa_operation *o) pw_proxy_add_proxy_listener(g->proxy, &g->proxy_proxy_listener, events, g); g->destroy = destroy; - if (o) - g->operation = o; return 0; } diff --git a/src/operation.c b/src/operation.c index 5115815af..e4dd582d9 100644 --- a/src/operation.c +++ b/src/operation.c @@ -88,6 +88,8 @@ static void operation_unlink(pa_operation *o) { } if (o->stream) pa_stream_unref(o->stream); + if (o->owner) + spa_list_remove(&o->owner_link); o->stream = NULL; o->callback = NULL; o->userdata = NULL; From 49c99f8dee4b42f3c53fe8bc20e977f6e6b3cb94 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 14 Mar 2019 13:23:20 +0100 Subject: [PATCH 091/116] context: handle context errors When the context is in error it might be unreffed. Make sure we don't ever unref the core from the callback because that is not allowed. Instead, add a defered to the mainloop to clean up later. --- src/context.c | 15 ++++++++++++++- src/internal.h | 7 ++++--- src/mainloop.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/context.c b/src/context.c index b6f18da6f..8c674b9b8 100644 --- a/src/context.c +++ b/src/context.c @@ -63,6 +63,10 @@ static void context_unlink(pa_context *c) pw_log_debug("context %p: unlink %d", c, c->state); + c->disconnect = true; + c->state_callback = NULL; + c->state_userdata = NULL; + spa_list_for_each_safe(s, t, &c->streams, link) { pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED); @@ -397,6 +401,8 @@ static void remote_state_changed(void *data, enum pw_remote_state old, context_fail(c, PA_ERR_CONNECTIONTERMINATED); break; case PW_REMOTE_STATE_UNCONNECTED: + if (!c->disconnect) + context_fail(c, PA_ERR_CONNECTIONTERMINATED); break; case PW_REMOTE_STATE_CONNECTING: pa_context_set_state(c, PA_CONTEXT_CONNECTING); @@ -480,6 +486,12 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * return c; } +static void do_core_destroy(pa_mainloop_api*m, void *userdata) +{ + pa_context *c = userdata; + pw_core_destroy(c->core); +} + static void context_free(pa_context *c) { pw_log_debug("context %p: free", c); @@ -491,7 +503,7 @@ static void context_free(pa_context *c) if (c->core_info) pw_core_info_free(c->core_info); - pw_core_destroy(c->core); + pa_mainloop_api_once(c->mainloop, do_core_destroy, c); } SPA_EXPORT @@ -598,6 +610,7 @@ void pa_context_disconnect(pa_context *c) pa_assert(c); pa_assert(c->refcount >= 1); + c->disconnect = true; pw_remote_disconnect(c->remote); if (PA_CONTEXT_IS_GOOD(c->state)) diff --git a/src/internal.h b/src/internal.h index c562531f2..bc9e30cca 100644 --- a/src/internal.h +++ b/src/internal.h @@ -257,6 +257,7 @@ struct global { struct pa_context { int refcount; + uint32_t client_index; struct pw_loop *loop; struct pw_core *core; @@ -284,13 +285,13 @@ struct pa_context { void *subscribe_userdata; pa_subscription_mask_t subscribe_mask; - bool no_fail; - uint32_t client_index; - struct spa_list globals; struct spa_list streams; struct spa_list operations; + + int no_fail:1; + int disconnect:1; }; struct global *pa_context_find_global(pa_context *c, uint32_t id); diff --git a/src/mainloop.c b/src/mainloop.c index ef509ece1..e5c85589c 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -23,6 +23,7 @@ #include #include +#include #include "internal.h" @@ -376,3 +377,49 @@ void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *use { pw_log_warn("Not Implemented"); } + + +struct once_info { + void (*callback)(pa_mainloop_api*m, void *userdata); + void *userdata; +}; + +static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { + struct once_info *i = userdata; + + pa_assert(m); + pa_assert(i); + + pa_assert(i->callback); + i->callback(m, i->userdata); + + pa_assert(m->defer_free); + m->defer_free(e); +} + +static void free_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { + struct once_info *i = userdata; + + pa_assert(m); + pa_assert(i); + pa_xfree(i); +} + + +void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) { + struct once_info *i; + pa_defer_event *e; + + pa_assert(m); + pa_assert(callback); + + pa_init_i18n(); + + i = pa_xnew(struct once_info, 1); + i->callback = callback; + i->userdata = userdata; + + pa_assert(m->defer_new); + pa_assert_se(e = m->defer_new(m, once_callback, i)); + m->defer_set_destroy(e, free_callback); +} From 571cb214d536936317c2750aaf1e1ec90eec25a4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 15 Mar 2019 20:31:20 +0100 Subject: [PATCH 092/116] context: improve introspection When subscribing, always bind to all objects we are interested in. Wait until all info is collected and then reply to introspection requests. --- src/context.c | 444 +++++++++++++++++++++++++++++++++++++++++------ src/internal.h | 19 +- src/introspect.c | 439 +++++++++------------------------------------- src/operation.c | 4 +- src/stream.c | 6 +- src/subscribe.c | 26 --- 6 files changed, 492 insertions(+), 446 deletions(-) diff --git a/src/context.c b/src/context.c index 8c674b9b8..d1fe8dfab 100644 --- a/src/context.c +++ b/src/context.c @@ -19,6 +19,8 @@ #include +#include + #include #include @@ -39,19 +41,17 @@ int pa_context_set_error(pa_context *c, int error) { static void global_free(pa_context *c, struct global *g) { - pa_operation *o, *t; - spa_list_remove(&g->link); - spa_list_for_each_safe(o, t, &g->operations, owner_link) - pa_operation_cancel(o); - - if (g->proxy) - spa_hook_remove(&g->proxy_proxy_listener); - if (g->props) - pw_properties_free(g->props); if (g->destroy) g->destroy(g); + if (g->proxy) { + spa_hook_remove(&g->proxy_proxy_listener); + spa_hook_remove(&g->proxy_listener); + pw_proxy_destroy(g->proxy); + } + if (g->props) + pw_properties_free(g->props); free(g); } @@ -189,10 +189,305 @@ static void emit_event(pa_context *c, struct global *g, pa_subscription_event_ty } } +static void device_event_info(void *object, const struct pw_device_info *info) +{ + struct global *g = object; + pa_card_info *i = &g->card_info.info; + uint32_t n; + + pw_log_debug("global %p: id:%d change-mask:%"PRIu64, g, g->id, info->change_mask); + info = g->info = pw_device_info_update(g->info, info); + + i->index = g->id; + i->name = info->name; + i->owner_module = g->parent_id; + if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS) { + i->driver = info->props ? + spa_dict_lookup(info->props, "device.api") : NULL; + if (i->proplist) + pa_proplist_update_dict(i->proplist, info->props); + else + i->proplist = pa_proplist_new_dict(info->props); + } + if (info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) { + for (n = 0; n < info->n_params; n++) { + if (!(info->params[n].flags & SPA_PARAM_INFO_READ)) + continue; + + switch (info->params[n].id) { + case SPA_PARAM_EnumProfile: + pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, + 0, SPA_PARAM_EnumProfile, 0, -1, NULL); + break; + case SPA_PARAM_Profile: + pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, + 0, SPA_PARAM_Profile, 0, -1, NULL); + break; + default: + break; + } + } + } + g->pending_seq = pw_proxy_sync(g->proxy, 0); +} + +static void device_event_param(void *object, int seq, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct global *g = object; + + switch (id) { + case SPA_PARAM_EnumProfile: + { + uint32_t id; + const char *name; + struct param *p; + + if (spa_pod_parse_object(param, + SPA_TYPE_OBJECT_ParamProfile, NULL, + SPA_PARAM_PROFILE_index, SPA_POD_Int(&id), + SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { + pw_log_warn("device %d: can't parse profile", g->id); + return; + } + p = malloc(sizeof(struct param) + SPA_POD_SIZE(param)); + if (p) { + p->id = id; + p->seq = seq; + p->param = SPA_MEMBER(p, sizeof(struct param), struct spa_pod); + memcpy(p->param, param, SPA_POD_SIZE(param)); + spa_list_append(&g->card_info.profiles, &p->link); + g->card_info.n_profiles++; + } + pw_log_debug("device %d: enum profile %d: \"%s\"", g->id, id, name); + break; + } + case SPA_PARAM_Profile: + { + uint32_t id; + if (spa_pod_parse_object(param, + SPA_TYPE_OBJECT_ParamProfile, NULL, + SPA_PARAM_PROFILE_index, SPA_POD_Int(&id)) < 0) { + pw_log_warn("device %d: can't parse profile", g->id); + return; + } + g->card_info.active_profile = id; + pw_log_debug("device %d: current profile %d", g->id, id); + break; + } + default: + break; + } +} + +static const struct pw_device_proxy_events device_events = { + PW_VERSION_DEVICE_PROXY_EVENTS, + .info = device_event_info, + .param = device_event_param, +}; + +static void device_destroy(void *data) +{ + struct global *global = data; + struct param *p; + + if (global->card_info.info.proplist) + pa_proplist_free(global->card_info.info.proplist); + spa_list_consume(p, &global->card_info.profiles, link) { + spa_list_remove(&p->link); + free(p); + } + if (global->info) + pw_device_info_free(global->info); +} + +static void node_event_info(void *object, const struct pw_node_info *info) +{ + struct global *g = object; + uint32_t i; + + pw_log_debug("update %d %"PRIu64, g->id, info->change_mask); + g->info = pw_node_info_update(g->info, info); + + if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) { + for (i = 0; i < info->n_params; i++) { + if (!(info->params[i].flags & SPA_PARAM_INFO_READ)) + continue; + + switch (info->params[i].id) { + case SPA_PARAM_EnumFormat: + case SPA_PARAM_Props: + pw_node_proxy_enum_params((struct pw_node_proxy*)g->proxy, + 0, info->params[i].id, 0, -1, NULL); + break; + default: + break; + } + } + } + g->pending_seq = pw_proxy_sync(g->proxy, 0); +} + +static void node_event_param(void *object, int seq, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct global *g = object; + pw_log_debug("update param %d %d", g->id, id); + + switch (id) { + case SPA_PARAM_Props: + { + struct spa_pod_prop *prop; + struct spa_pod_object *obj = (struct spa_pod_object *) param; + + SPA_POD_OBJECT_FOREACH(obj, prop) { + switch (prop->key) { + case SPA_PROP_volume: + spa_pod_get_float(&prop->value, &g->node_info.volume); + break; + case SPA_PROP_mute: + spa_pod_get_bool(&prop->value, &g->node_info.mute); + break; + default: + break; + } + } + break; + } + default: + break; + } +} + +static const struct pw_node_proxy_events node_events = { + PW_VERSION_NODE_PROXY_EVENTS, + .info = node_event_info, + .param = node_event_param, +}; + +static void node_destroy(void *data) +{ + struct global *global = data; + if (global->info) + pw_node_info_free(global->info); +} + +static void module_event_info(void *object, const struct pw_module_info *info) +{ + struct global *g = object; + pa_module_info *i = &g->module_info.info; + + pw_log_debug("update %d", g->id); + + info = g->info = pw_module_info_update(g->info, info); + + i->index = g->id; + if (info->change_mask & PW_MODULE_CHANGE_MASK_PROPS) { + if (i->proplist) + pa_proplist_update_dict(i->proplist, info->props); + else + i->proplist = pa_proplist_new_dict(info->props); + } + + if (info->change_mask & PW_MODULE_CHANGE_MASK_NAME) + i->name = info->name; + if (info->change_mask & PW_MODULE_CHANGE_MASK_ARGS) + i->argument = info->args; + i->n_used = -1; + i->auto_unload = false; + g->pending_seq = pw_proxy_sync(g->proxy, 0); +} + +static const struct pw_module_proxy_events module_events = { + PW_VERSION_MODULE_PROXY_EVENTS, + .info = module_event_info, +}; + +static void module_destroy(void *data) +{ + struct global *global = data; + if (global->module_info.info.proplist) + pa_proplist_free(global->module_info.info.proplist); + if (global->info) + pw_module_info_free(global->info); +} + +static void client_event_info(void *object, const struct pw_client_info *info) +{ + struct global *g = object; + pa_client_info *i = &g->client_info.info; + + pw_log_debug("update %d", g->id); + info = g->info = pw_client_info_update(g->info, info); + + i->index = g->id; + i->owner_module = g->parent_id; + + if (info->change_mask & PW_CLIENT_CHANGE_MASK_PROPS) { + if (i->proplist) + pa_proplist_update_dict(i->proplist, info->props); + else + i->proplist = pa_proplist_new_dict(info->props); + i->name = info->props ? + spa_dict_lookup(info->props, "application.name") : NULL; + i->driver = info->props ? + spa_dict_lookup(info->props, PW_CLIENT_PROP_PROTOCOL) : NULL; + } + g->pending_seq = pw_proxy_sync(g->proxy, 0); +} + +static const struct pw_client_proxy_events client_events = { + PW_VERSION_CLIENT_PROXY_EVENTS, + .info = client_event_info, +}; + +static void client_destroy(void *data) +{ + struct global *global = data; + if (global->client_info.info.proplist) + pa_proplist_free(global->client_info.info.proplist); + if (global->info) + pw_client_info_free(global->info); +} + +static void proxy_destroy(void *data) +{ + struct global *g = data; + spa_hook_remove(&g->proxy_listener); + g->proxy = NULL; +} + +static void proxy_done(void *data, int seq) +{ + struct global *g = data; + pa_subscription_event_type_t event; + + if (g->pending_seq == seq) { + if (g->init) { + g->init = false; + event = PA_SUBSCRIPTION_EVENT_NEW; + } else { + event = PA_SUBSCRIPTION_EVENT_CHANGE; + } + emit_event(g->context, g, event); + } +} + +static const struct pw_proxy_events proxy_events = { + PW_VERSION_PROXY_EVENTS, + .destroy = proxy_destroy, + .done = proxy_done, +}; + static int set_mask(pa_context *c, struct global *g) { const char *str; struct global *f; + const void *events = NULL; + pw_destroy_t destroy; + uint32_t client_version; switch (g->type) { case PW_TYPE_INTERFACE_Device: @@ -206,6 +501,11 @@ static int set_mask(pa_context *c, struct global *g) pw_log_debug("found card %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_CARD; g->event = PA_SUBSCRIPTION_EVENT_CARD; + + events = &device_events; + client_version = PW_VERSION_DEVICE; + destroy = device_destroy; + spa_list_init(&g->card_info.profiles); break; case PW_TYPE_INTERFACE_Node: @@ -251,18 +551,29 @@ static int set_mask(pa_context *c, struct global *g) g->mask = PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT; g->event = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT; } + events = &node_events; + client_version = PW_VERSION_NODE; + destroy = node_destroy; + g->node_info.volume = 1.0; + g->node_info.mute = false; break; case PW_TYPE_INTERFACE_Module: pw_log_debug("found module %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_MODULE; g->event = PA_SUBSCRIPTION_EVENT_MODULE; + events = &module_events; + client_version = PW_VERSION_MODULE; + destroy = module_destroy; break; case PW_TYPE_INTERFACE_Client: pw_log_debug("found client %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_CLIENT; g->event = PA_SUBSCRIPTION_EVENT_CLIENT; + events = &client_events; + client_version = PW_VERSION_CLIENT; + destroy = client_destroy; break; case PW_TYPE_INTERFACE_Port: @@ -296,6 +607,24 @@ static int set_mask(pa_context *c, struct global *g) default: return 0; } + + pw_log_debug("global %p: id:%u mask %d/%d", g, g->id, g->mask, g->event); + + if (events) { + pw_log_debug("bind %d", g->id); + + g->proxy = pw_registry_proxy_bind(c->registry_proxy, g->id, g->type, + client_version, 0); + if (g->proxy == NULL) + return -ENOMEM; + + pw_proxy_add_proxy_listener(g->proxy, &g->proxy_proxy_listener, events, g); + pw_proxy_add_listener(g->proxy, &g->proxy_listener, &proxy_events, g); + g->destroy = destroy; + } else { + emit_event(c, g, PA_SUBSCRIPTION_EVENT_NEW); + } + return 1; } @@ -308,20 +637,18 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, g = calloc(1, sizeof(struct global)); pw_log_debug("context %p: global %d %p", c, id, g); + g->context = c; g->id = id; g->parent_id = parent_id; g->type = type; + g->init = true; g->props = props ? pw_properties_new_dict(props) : NULL; - spa_list_init(&g->operations); spa_list_append(&c->globals, &g->link); - if (set_mask(c, g) == 0) { + if (set_mask(c, g) != 1) { global_free(c, g); return; } - - pw_log_debug("mask %d/%d", g->mask, g->event); - emit_event(c, g, PA_SUBSCRIPTION_EVENT_NEW); } static void registry_event_global_remove(void *object, uint32_t id) @@ -346,17 +673,6 @@ static const struct pw_registry_proxy_events registry_events = .global_remove = registry_event_global_remove, }; -struct ready_data -{ - pa_context *context; -}; - -static void on_ready(pa_operation *o, void *userdata) -{ - struct ready_data *d = userdata; - pa_context_set_state(d->context, PA_CONTEXT_READY); -} - static void complete_operations(pa_context *c, int seq) { pa_operation *o, *t; @@ -393,8 +709,6 @@ static void remote_state_changed(void *data, enum pw_remote_state old, enum pw_remote_state state, const char *error) { pa_context *c = data; - pa_operation *o; - struct ready_data *d; switch(state) { case PW_REMOTE_STATE_ERROR: @@ -414,18 +728,7 @@ static void remote_state_changed(void *data, enum pw_remote_state old, c->core_proxy = pw_remote_get_core_proxy(c->remote); pw_core_proxy_add_listener(c->core_proxy, &c->core_listener, &core_events, c); - c->registry_proxy = pw_core_proxy_get_registry(c->core_proxy, - PW_TYPE_INTERFACE_Registry, - PW_VERSION_REGISTRY, 0); - pw_registry_proxy_add_listener(c->registry_proxy, - &c->registry_listener, - ®istry_events, c); - - o = pa_operation_new(c, NULL, on_ready, sizeof(struct ready_data)); - d = o->userdata; - d->context = c; - pa_operation_sync(o); - pa_operation_unref(o); + pa_context_set_state(c, PA_CONTEXT_READY); break; } } @@ -435,6 +738,53 @@ static const struct pw_remote_events remote_events = { .state_changed = remote_state_changed, }; +struct success_data { + pa_context_success_cb_t cb; + void *userdata; + int ret; +}; + +static void on_success(pa_operation *o, void *userdata) +{ + struct success_data *d = userdata; + pa_context *c = o->context; + pa_operation_done(o); + if (d->cb) + d->cb(c, d->ret, d->userdata); +} + +SPA_EXPORT +pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) +{ + pa_operation *o; + struct success_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + c->subscribe_mask = m; + + if (c->registry_proxy == NULL) { + c->registry_proxy = pw_core_proxy_get_registry(c->core_proxy, + PW_TYPE_INTERFACE_Registry, + PW_VERSION_REGISTRY, 0); + pw_registry_proxy_add_listener(c->registry_proxy, + &c->registry_listener, + ®istry_events, c); + } + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_data)); + d = o->userdata; + d->ret = 0; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + + return o; +} + SPA_EXPORT pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) { @@ -646,21 +996,6 @@ pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *u return o; } -struct success_data { - pa_context_success_cb_t cb; - void *userdata; - int ret; -}; - -static void on_success(pa_operation *o, void *userdata) -{ - struct success_data *d = userdata; - pa_context *c = o->context; - pa_operation_done(o); - if (d->cb) - d->cb(c, d->ret, d->userdata); -} - SPA_EXPORT pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) { @@ -672,6 +1007,7 @@ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, d->ret = PA_ERR_ACCESS; d->cb = cb; d->userdata = userdata; + pa_operation_sync(o); return o; } diff --git a/src/internal.h b/src/internal.h index bc9e30cca..99fbcee21 100644 --- a/src/internal.h +++ b/src/internal.h @@ -203,6 +203,13 @@ struct pa_mainloop { int n_events; }; +struct param { + struct spa_list link; + uint32_t id; + int seq; + void *param; +}; + #define PA_SUBSCRIPTION_MASK_DSP_SINK 0x1000U #define PA_SUBSCRIPTION_MASK_DSP_SOURCE 0x2000U #define PA_SUBSCRIPTION_MASK_DSP (PA_SUBSCRIPTION_MASK_DSP_SINK | PA_SUBSCRIPTION_MASK_DSP_SOURCE) @@ -212,12 +219,14 @@ struct global { uint32_t id; uint32_t parent_id; uint32_t type; + int init:1; struct pw_properties *props; + pa_context *context; pa_subscription_mask_t mask; pa_subscription_event_type_t event; - struct spa_list operations; + int pending_seq; void *info; pw_destroy_t destroy; @@ -239,10 +248,13 @@ struct global { /* for sink/source */ struct { uint32_t monitor; + float volume; + bool mute; } node_info; /* for devices */ struct { - struct pw_array profiles; + struct spa_list profiles; + uint32_t n_profiles; uint32_t active_profile; pa_card_info info; } card_info; @@ -392,9 +404,6 @@ struct pa_operation pa_context *context; pa_stream *stream; - struct spa_list owner_link; - struct global *owner; - int seq; pa_operation_state_t state; diff --git a/src/introspect.c b/src/introspect.c index b6843f524..0b78ca23f 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -27,300 +27,6 @@ #include "internal.h" -static void node_event_info(void *object, const struct pw_node_info *info) -{ - struct global *g = object; - pa_operation *o; - uint32_t i; - - pw_log_debug("update %d", g->id); - g->info = pw_node_info_update(g->info, info); - - if (info->change_mask & SPA_NODE_CHANGE_MASK_PARAMS) { - for (i = 0; i < info->n_params; i++) { - if (!(info->params[i].flags & SPA_PARAM_INFO_READ)) - continue; - - switch (info->params[i].id) { - case SPA_PARAM_EnumFormat: - pw_node_proxy_enum_params((struct pw_node_proxy*)g->proxy, - 0, SPA_PARAM_EnumFormat, 0, -1, NULL); - break; - default: - break; - } - } - } - spa_list_for_each(o, &g->operations, owner_link) - pa_operation_sync(o); -} - -static void node_event_param(void *object, int seq, - uint32_t id, uint32_t index, uint32_t next, - const struct spa_pod *param) -{ - struct global *g = object; - pw_log_debug("update param %d", g->id); -} - -static const struct pw_node_proxy_events node_events = { - PW_VERSION_NODE_PROXY_EVENTS, - .info = node_event_info, - .param = node_event_param, -}; - -static void module_event_info(void *object, const struct pw_module_info *info) -{ - struct global *g = object; - pa_module_info *i = &g->module_info.info; - - pw_log_debug("update %d", g->id); - - info = g->info = pw_module_info_update(g->info, info); - - i->index = g->id; - if (info->change_mask & PW_MODULE_CHANGE_MASK_PROPS) { - if (i->proplist) - pa_proplist_update_dict(i->proplist, info->props); - else - i->proplist = pa_proplist_new_dict(info->props); - } - - if (info->change_mask & PW_MODULE_CHANGE_MASK_NAME) - i->name = info->name; - if (info->change_mask & PW_MODULE_CHANGE_MASK_ARGS) - i->argument = info->args; - i->n_used = -1; - i->auto_unload = false; -} - -static const struct pw_module_proxy_events module_events = { - PW_VERSION_MODULE_PROXY_EVENTS, - .info = module_event_info, -}; - -static void client_event_info(void *object, const struct pw_client_info *info) -{ - struct global *g = object; - pa_client_info *i = &g->client_info.info; - - pw_log_debug("update %d", g->id); - info = g->info = pw_client_info_update(g->info, info); - - i->index = g->id; - i->owner_module = g->parent_id; - - if (info->change_mask & PW_CLIENT_CHANGE_MASK_PROPS) { - if (i->proplist) - pa_proplist_update_dict(i->proplist, info->props); - else - i->proplist = pa_proplist_new_dict(info->props); - i->name = info->props ? - spa_dict_lookup(info->props, "application.name") : NULL; - i->driver = info->props ? - spa_dict_lookup(info->props, PW_CLIENT_PROP_PROTOCOL) : NULL; - } -} - -static const struct pw_client_proxy_events client_events = { - PW_VERSION_CLIENT_PROXY_EVENTS, - .info = client_event_info, -}; - -static void device_event_param(void *object, int seq, - uint32_t id, uint32_t index, uint32_t next, - const struct spa_pod *param) -{ - struct global *g = object; - - switch (id) { - case SPA_PARAM_EnumProfile: - { - uint32_t id; - const char *name; - - if (spa_pod_parse_object(param, - SPA_TYPE_OBJECT_ParamProfile, NULL, - SPA_PARAM_PROFILE_index, SPA_POD_Int(&id), - SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { - pw_log_warn("device %d: can't parse profile", g->id); - return; - } - pw_array_add_ptr(&g->card_info.profiles, spa_pod_copy(param)); - pw_log_debug("device %d: enum profile %d: \"%s\"", g->id, id, name); - break; - } - case SPA_PARAM_Profile: - { - uint32_t id; - if (spa_pod_parse_object(param, - SPA_TYPE_OBJECT_ParamProfile, NULL, - SPA_PARAM_PROFILE_index, SPA_POD_Int(&id)) < 0) { - pw_log_warn("device %d: can't parse profile", g->id); - return; - } - g->card_info.active_profile = id; - pw_log_debug("device %d: current profile %d", g->id, id); - break; - } - default: - break; - } -} - -static void device_event_info(void *object, const struct pw_device_info *info) -{ - struct global *g = object; - pa_card_info *i = &g->card_info.info; - pa_operation *o; - uint32_t n; - - pw_log_debug("update %d %"PRIu64, g->id, info->change_mask); - info = g->info = pw_device_info_update(g->info, info); - - i->index = g->id; - i->name = info->name; - i->owner_module = g->parent_id; - if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS) { - i->driver = info->props ? - spa_dict_lookup(info->props, "device.api") : NULL; - if (i->proplist) - pa_proplist_update_dict(i->proplist, info->props); - else - i->proplist = pa_proplist_new_dict(info->props); - } - if (info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) { - for (n = 0; n < info->n_params; n++) { - if (!(info->params[n].flags & SPA_PARAM_INFO_READ)) - continue; - - switch (info->params[n].id) { - case SPA_PARAM_EnumProfile: - pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, - 0, SPA_PARAM_EnumProfile, 0, -1, NULL); - break; - case SPA_PARAM_Profile: - pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, - 0, SPA_PARAM_Profile, 0, -1, NULL); - break; - default: - break; - } - } - } - spa_list_for_each(o, &g->operations, owner_link) - pa_operation_sync(o); -} - -static const struct pw_device_proxy_events device_events = { - PW_VERSION_DEVICE_PROXY_EVENTS, - .info = device_event_info, - .param = device_event_param, -}; - -static void node_destroy(void *data) -{ - struct global *global = data; - if (global->info) - pw_node_info_free(global->info); -} - -static void module_destroy(void *data) -{ - struct global *global = data; - if (global->module_info.info.proplist) - pa_proplist_free(global->module_info.info.proplist); - if (global->info) - pw_module_info_free(global->info); -} - -static void client_destroy(void *data) -{ - struct global *global = data; - if (global->client_info.info.proplist) - pa_proplist_free(global->client_info.info.proplist); - if (global->info) - pw_client_info_free(global->info); -} - -static void device_destroy(void *data) -{ - struct global *global = data; - struct spa_pod **profile; - - if (global->card_info.info.proplist) - pa_proplist_free(global->card_info.info.proplist); - pw_array_for_each(profile, &global->card_info.profiles) - free(*profile); - pw_array_clear(&global->card_info.profiles); - if (global->info) - pw_device_info_free(global->info); -} - -static int ensure_global(pa_context *c, struct global *g, pa_operation *o) -{ - uint32_t client_version; - const void *events; - pw_destroy_t destroy; - - if (o) { - if (o->owner) - spa_list_remove(&o->owner_link); - o->owner = g; - spa_list_append(&g->operations, &o->owner_link); - } - - if (g->proxy != NULL) - return 0; - - switch (g->type) { - case PW_TYPE_INTERFACE_Node: - events = &node_events; - client_version = PW_VERSION_NODE; - destroy = node_destroy; - break; - case PW_TYPE_INTERFACE_Module: - events = &module_events; - client_version = PW_VERSION_MODULE; - destroy = module_destroy; - break; - case PW_TYPE_INTERFACE_Client: - events = &client_events; - client_version = PW_VERSION_CLIENT; - destroy = client_destroy; - break; - case PW_TYPE_INTERFACE_Device: - events = &device_events; - client_version = PW_VERSION_DEVICE; - destroy = device_destroy; - pw_array_init(&g->card_info.profiles, 64); - break; - default: - return -EINVAL; - } - - pw_log_debug("bind %d", g->id); - - g->proxy = pw_registry_proxy_bind(c->registry_proxy, g->id, g->type, - client_version, 0); - if (g->proxy == NULL) - return -ENOMEM; - - pw_proxy_add_proxy_listener(g->proxy, &g->proxy_proxy_listener, events, g); - g->destroy = destroy; - - return 0; -} - -static void ensure_types(pa_context *c, uint32_t mask, pa_operation *o) -{ - struct global *g; - spa_list_for_each(g, &c->globals, link) { - if (g->mask & mask) - ensure_global(c, g, o); - } -} - struct success_ack { pa_context_success_cb_t cb; void *userdata; @@ -360,6 +66,27 @@ static pa_sink_state_t node_state_to_sink(enum pw_node_state s) } } +static int wait_global(pa_context *c, struct global *g, pa_operation *o) +{ + if (g->init) { + pa_operation_sync(o); + return -EBUSY; + } + return 0; +} + +static int wait_globals(pa_context *c, pa_subscription_mask_t mask, pa_operation *o) +{ + struct global *g; + spa_list_for_each(g, &c->globals, link) { + if (!(g->mask & mask)) + continue; + if (wait_global(c, g, o) < 0) + return -EBUSY; + } + return 0; +} + static void sink_callback(struct sink_data *d) { struct global *g = d->global; @@ -379,8 +106,8 @@ static void sink_callback(struct sink_data *d) i.sample_spec.channels = 2; pa_channel_map_init_auto(&i.channel_map, 2, PA_CHANNEL_MAP_DEFAULT); i.owner_module = g->parent_id; - pa_cvolume_set(&i.volume, 2, PA_VOLUME_NORM); - i.mute = false; + pa_cvolume_set(&i.volume, 2, g->node_info.volume * PA_VOLUME_NORM); + i.mute = g->node_info.mute; i.monitor_source = g->node_info.monitor; i.monitor_source_name = "unknown"; i.latency = 0; @@ -411,6 +138,8 @@ static void sink_callback(struct sink_data *d) static void sink_info(pa_operation *o, void *userdata) { struct sink_data *d = userdata; + if (wait_global(d->context, d->global, o) < 0) + return; sink_callback(d); d->cb(d->context, NULL, 1, d->userdata); pa_operation_done(o); @@ -439,7 +168,6 @@ pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, d->cb = cb; d->userdata = userdata; d->global = g; - ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -464,15 +192,14 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_ if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) return NULL; - o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; d->global = g; - ensure_global(c, g, o); pa_operation_sync(o); + return o; } @@ -482,6 +209,8 @@ static void sink_info_list(pa_operation *o, void *userdata) pa_context *c = d->context; struct global *g; + if (wait_globals(c, PA_SUBSCRIPTION_MASK_SINK, o) < 0) + return; spa_list_for_each(g, &c->globals, link) { if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) continue; @@ -509,13 +238,17 @@ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, d->context = c; d->cb = cb; d->userdata = userdata; - - ensure_types(c, PA_SUBSCRIPTION_MASK_SINK, o); pa_operation_sync(o); return o; } +static void set_stream_volume(pa_context *c, pa_stream *s) +{ + float v = s->mute ? 0.0f : s->volume; + pw_stream_set_control(s->stream, SPA_PROP_volume, v); +} + static void set_node_volume(pa_context *c, struct global *g, const pa_cvolume *volume) { char buf[1024]; @@ -524,8 +257,6 @@ static void set_node_volume(pa_context *c, struct global *g, const pa_cvolume *v v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - ensure_global(c, g, NULL); - pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, SPA_PARAM_Props, 0, spa_pod_builder_add_object(&b, @@ -538,8 +269,6 @@ static void set_node_mute(pa_context *c, struct global *g, bool mute) char buf[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - ensure_global(c, g, NULL); - pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, SPA_PARAM_Props, 0, spa_pod_builder_add_object(&b, @@ -740,8 +469,8 @@ static void source_callback(struct source_data *d) i.sample_spec.channels = 2; pa_channel_map_init_auto(&i.channel_map, 2, PA_CHANNEL_MAP_DEFAULT); i.owner_module = g->parent_id; - pa_cvolume_set(&i.volume, 2, PA_VOLUME_NORM); - i.mute = false; + pa_cvolume_set(&i.volume, 2, g->node_info.volume * PA_VOLUME_NORM); + i.mute = g->node_info.mute; if (g->mask & PA_SUBSCRIPTION_MASK_DSP_SINK) { i.monitor_of_sink = g->dsp_info.session; i.monitor_of_sink_name = "unknown"; @@ -775,6 +504,8 @@ static void source_callback(struct source_data *d) static void source_info(pa_operation *o, void *userdata) { struct source_data *d = userdata; + if (wait_global(d->context, d->global, o) < 0) + return; source_callback(d); d->cb(d->context, NULL, 1, d->userdata); pa_operation_done(o); @@ -803,8 +534,6 @@ pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name d->cb = cb; d->userdata = userdata; d->global = g; - - ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -834,7 +563,6 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, p d->cb = cb; d->userdata = userdata; d->global = g; - ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -846,6 +574,8 @@ static void source_info_list(pa_operation *o, void *userdata) pa_context *c = d->context; struct global *g; + if (wait_globals(c, PA_SUBSCRIPTION_MASK_SOURCE, o) < 0) + return; spa_list_for_each(g, &c->globals, link) { if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) continue; @@ -873,8 +603,6 @@ pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t d->context = c; d->cb = cb; d->userdata = userdata; - - ensure_types(c, PA_SUBSCRIPTION_MASK_SOURCE, o); pa_operation_sync(o); return o; @@ -1126,7 +854,6 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_ d->cb = cb; d->userdata = userdata; d->global = g; - ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -1165,8 +892,6 @@ pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t d->context = c; d->cb = cb; d->userdata = userdata; - - ensure_types(c, PA_SUBSCRIPTION_MASK_MODULE, o); pa_operation_sync(o); return o; @@ -1231,7 +956,6 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_ d->cb = cb; d->userdata = userdata; d->global = g; - ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -1270,8 +994,6 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t d->context = c; d->cb = cb; d->userdata = userdata; - - ensure_types(c, PA_SUBSCRIPTION_MASK_CLIENT, o); pa_operation_sync(o); return o; @@ -1316,27 +1038,29 @@ static void card_callback(struct card_data *d) struct global *g = d->global; pa_card_info *i = &g->card_info.info; int n_profiles, j; - struct spa_pod **profiles; + struct param *p; - n_profiles = pw_array_get_len(&g->card_info.profiles, struct spa_pod*); - profiles = g->card_info.profiles.data; + n_profiles = g->card_info.n_profiles; i->profiles = alloca(sizeof(pa_card_profile_info) * n_profiles); i->profiles2 = alloca(sizeof(pa_card_profile_info2 *) * n_profiles); i->n_profiles = 0; - for (j = 0; j < n_profiles; j++) { + pw_log_debug("context %p: info for %d", g->context, g->id); + + spa_list_for_each(p, &g->card_info.profiles, link) { uint32_t id; const char *name; - if (spa_pod_parse_object(profiles[j], + if (spa_pod_parse_object(p->param, SPA_TYPE_OBJECT_ParamProfile, NULL, SPA_PARAM_PROFILE_index, SPA_POD_Int(&id), SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { - pw_log_warn("device %d: can't parse profile %d", g->id, j); + pw_log_warn("device %d: can't parse profile", g->id); continue; } + j = i->n_profiles++; i->profiles[j].name = name; i->profiles[j].description = name; i->profiles[j].n_sinks = 1; @@ -1355,7 +1079,6 @@ static void card_callback(struct card_data *d) i->active_profile = &i->profiles[j]; i->active_profile2 = i->profiles2[j]; } - i->n_profiles++; } d->cb(d->context, i, 0, d->userdata); } @@ -1363,6 +1086,8 @@ static void card_callback(struct card_data *d) static void card_info(pa_operation *o, void *userdata) { struct card_data *d = userdata; + if (wait_global(d->context, d->global, o) < 0) + return; card_callback(d); d->cb(d->context, NULL, 1, d->userdata); pa_operation_done(o); @@ -1382,6 +1107,8 @@ pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + pw_log_debug("context %p: %u", c, idx); + if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; if (!(g->mask & PA_SUBSCRIPTION_MASK_CARD)) @@ -1393,7 +1120,6 @@ pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_ d->cb = cb; d->userdata = userdata; d->global = g; - ensure_global(c, g, o); pa_operation_sync(o); return o; } @@ -1412,6 +1138,8 @@ pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + pw_log_debug("context %p: %s", c, name); + if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_CARD, name)) == NULL) return NULL; @@ -1421,7 +1149,6 @@ pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, d->cb = cb; d->userdata = userdata; d->global = g; - ensure_global(c, g, o); pa_operation_sync(o); return o; } @@ -1432,6 +1159,8 @@ static void card_info_list(pa_operation *o, void *userdata) pa_context *c = d->context; struct global *g; + if (wait_globals(c, PA_SUBSCRIPTION_MASK_CARD, o) < 0) + return; spa_list_for_each(g, &c->globals, link) { if (!(g->mask & PA_SUBSCRIPTION_MASK_CARD)) continue; @@ -1454,13 +1183,13 @@ pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + pw_log_debug("context %p", c); + o = pa_operation_new(c, NULL, card_info_list, sizeof(struct card_data)); d = o->userdata; d->context = c; d->cb = cb; d->userdata = userdata; - - ensure_types(c, PA_SUBSCRIPTION_MASK_CARD, o); pa_operation_sync(o); return o; @@ -1471,25 +1200,21 @@ static void card_profile(pa_operation *o, void *userdata) struct card_data *d = userdata; struct global *g = d->global; pa_context *c = d->context; - size_t i, n_profiles; int res = 0; uint32_t id = SPA_ID_INVALID; char buf[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - struct spa_pod **profiles; + struct param *p; - n_profiles = pw_array_get_len(&g->card_info.profiles, struct spa_pod*); - profiles = g->card_info.profiles.data; - - for (i = 0; i < n_profiles; i++) { + spa_list_for_each(p, &g->card_info.profiles, link) { uint32_t test_id; const char *name; - if (spa_pod_parse_object(profiles[i], + if (spa_pod_parse_object(p->param, SPA_TYPE_OBJECT_ParamProfile, NULL, SPA_PARAM_PROFILE_index, SPA_POD_Int(&test_id), SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0) { - pw_log_warn("device %d: can't parse profile %zd", g->id, i); + pw_log_warn("device %d: can't parse profile", g->id); continue; } if (strcmp(name, d->profile) == 0) { @@ -1540,7 +1265,6 @@ pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, d->userdata = userdata; d->global = g; d->profile = strdup(profile); - ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -1571,7 +1295,6 @@ pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name d->userdata = userdata; d->global = g; d->profile = strdup(profile); - ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -1637,7 +1360,6 @@ static void sink_input_callback(struct sink_input_data *d) l = pa_context_find_linked(d->context, g->id); i.sink = l ? l->id : PA_INVALID_INDEX; } - pa_cvolume_init(&i.volume); if (s && s->sample_spec.channels > 0) { i.sample_spec = s->sample_spec; if (s->channel_map.channels == s->sample_spec.channels) @@ -1645,7 +1367,6 @@ static void sink_input_callback(struct sink_input_data *d) else pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); - pa_cvolume_set(&i.volume, i.sample_spec.channels, s->volume * PA_VOLUME_NORM); i.format = s->format; } else { @@ -1653,16 +1374,17 @@ static void sink_input_callback(struct sink_input_data *d) i.sample_spec.rate = 44100; i.sample_spec.channels = 2; pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); - pa_cvolume_set(&i.volume, i.sample_spec.channels, PA_VOLUME_NORM); ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); i.format = ii; } + pa_cvolume_init(&i.volume); + pa_cvolume_set(&i.volume, i.sample_spec.channels, g->node_info.volume * PA_VOLUME_NORM); + i.mute = g->node_info.mute; i.buffer_usec = 0; i.sink_usec = 0; i.resample_method = "PipeWire resampler"; i.driver = "PipeWire"; - i.mute = false; i.proplist = pa_proplist_new_dict(info->props); if (cl && cl->client_info.info.proplist) pa_proplist_update(i.proplist, PA_UPDATE_MERGE, cl->client_info.info.proplist); @@ -1678,6 +1400,8 @@ static void sink_input_callback(struct sink_input_data *d) static void sink_input_info(pa_operation *o, void *userdata) { struct sink_input_data *d = userdata; + if (wait_global(d->context, d->global, o) < 0) + return; sink_input_callback(d); d->cb(d->context, NULL, 1, d->userdata); pa_operation_done(o); @@ -1709,8 +1433,8 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin d->cb = cb; d->userdata = userdata; d->global = g; - ensure_global(c, g, o); pa_operation_sync(o); + return o; } @@ -1720,6 +1444,8 @@ static void sink_input_info_list(pa_operation *o, void *userdata) pa_context *c = d->context; struct global *g; + if (wait_globals(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, o) < 0) + return; spa_list_for_each(g, &c->globals, link) { if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK_INPUT)) continue; @@ -1749,8 +1475,6 @@ pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_i d->context = c; d->cb = cb; d->userdata = userdata; - - ensure_types(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, o); pa_operation_sync(o); return o; @@ -1789,7 +1513,7 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons if (s) { s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); + set_stream_volume(c, s); } else if (g) { set_node_volume(c, g, volume); @@ -1820,7 +1544,7 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu if (s) { s->mute = mute; - pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); + set_stream_volume(c, s); } else if (g) { set_node_mute(c, g, mute); @@ -1908,7 +1632,6 @@ static void source_output_callback(struct source_output_data *d) l = pa_context_find_linked(d->context, g->id); i.source = l ? l->id : PA_INVALID_INDEX; } - pa_cvolume_init(&i.volume); if (s && s->sample_spec.channels > 0) { i.sample_spec = s->sample_spec; if (s->channel_map.channels == s->sample_spec.channels) @@ -1916,7 +1639,6 @@ static void source_output_callback(struct source_output_data *d) else pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); - pa_cvolume_set(&i.volume, i.sample_spec.channels, s->volume * PA_VOLUME_NORM); i.format = s->format; } else { @@ -1924,16 +1646,17 @@ static void source_output_callback(struct source_output_data *d) i.sample_spec.rate = 44100; i.sample_spec.channels = 2; pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); - pa_cvolume_set(&i.volume, i.sample_spec.channels, PA_VOLUME_NORM); ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); i.format = ii; } + pa_cvolume_init(&i.volume); + pa_cvolume_set(&i.volume, i.sample_spec.channels, g->node_info.volume * PA_VOLUME_NORM); + i.mute = g->node_info.mute; i.buffer_usec = 0; i.source_usec = 0; i.resample_method = "PipeWire resampler"; i.driver = "PipeWire"; - i.mute = false; i.proplist = pa_proplist_new_dict(info->props); if (cl && cl->client_info.info.proplist) pa_proplist_update(i.proplist, PA_UPDATE_MERGE, cl->client_info.info.proplist); @@ -1949,6 +1672,8 @@ static void source_output_callback(struct source_output_data *d) static void source_output_info(pa_operation *o, void *userdata) { struct source_output_data *d = userdata; + if (wait_global(d->context, d->global, o) < 0) + return; source_output_callback(d); d->cb(d->context, NULL, 1, d->userdata); pa_operation_done(o); @@ -1978,7 +1703,6 @@ pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_ d->cb = cb; d->userdata = userdata; d->global = g; - ensure_global(c, g, o); pa_operation_sync(o); return o; @@ -1990,6 +1714,9 @@ static void source_output_info_list(pa_operation *o, void *userdata) pa_context *c = d->context; struct global *g; + if (wait_globals(c, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, o) < 0) + return; + spa_list_for_each(g, &c->globals, link) { if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)) continue; @@ -2017,8 +1744,6 @@ pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_ou d->context = c; d->cb = cb; d->userdata = userdata; - - ensure_types(c, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, o); pa_operation_sync(o); return o; @@ -2057,7 +1782,7 @@ pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, c if (s) { s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); + set_stream_volume(c, s); } else if (g) { set_node_volume(c, g, volume); @@ -2088,7 +1813,7 @@ pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int if (s) { s->mute = mute; - pw_stream_set_control(s->stream, PW_STREAM_CONTROL_VOLUME, s->mute ? 0.0 : s->volume); + set_stream_volume(c, s); } else if (g) { set_node_mute(c, g, mute); diff --git a/src/operation.c b/src/operation.c index e4dd582d9..0971f3cd9 100644 --- a/src/operation.c +++ b/src/operation.c @@ -52,8 +52,8 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb int pa_operation_sync(pa_operation *o) { pa_context *c = o->context; - pw_log_debug("operation %p: sync %d", o, o->seq); o->seq = pw_core_proxy_sync(c->core_proxy, 0, 0); + pw_log_debug("operation %p: sync %d", o, o->seq); return 0; } @@ -88,8 +88,6 @@ static void operation_unlink(pa_operation *o) { } if (o->stream) pa_stream_unref(o->stream); - if (o->owner) - spa_list_remove(&o->owner_link); o->stream = NULL; o->callback = NULL; o->userdata = NULL; diff --git a/src/stream.c b/src/stream.c index 458d4cdcf..142c2eff1 100644 --- a/src/stream.c +++ b/src/stream.c @@ -703,10 +703,14 @@ pa_context* pa_stream_get_context(pa_stream *s) SPA_EXPORT uint32_t pa_stream_get_index(pa_stream *s) { + uint32_t idx; + spa_assert(s); spa_assert(s->refcount >= 1); - return pw_stream_get_node_id(s->stream); + idx = pw_stream_get_node_id(s->stream); + pw_log_debug("stream %p: index %u", s, idx); + return idx; } void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { diff --git a/src/subscribe.c b/src/subscribe.c index 3af7fcf5e..4ccdc7c70 100644 --- a/src/subscribe.c +++ b/src/subscribe.c @@ -29,32 +29,6 @@ struct subscribe_data void *userdata; }; -static void on_subscribed(pa_operation *o, void *userdata) -{ - struct subscribe_data *d = userdata; - if (d->cb) - d->cb(o->context, PA_OK, d->userdata); -} - -SPA_EXPORT -pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) -{ - pa_operation *o; - struct subscribe_data *d; - - pa_assert(c); - pa_assert(c->refcount >= 1); - - c->subscribe_mask = m; - - o = pa_operation_new(c, NULL, on_subscribed, sizeof(struct subscribe_data)); - d = o->userdata; - d->cb = cb; - d->userdata = userdata; - - return o; -} - SPA_EXPORT void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) { From d4cf47ec558a0404bcc8c266dcab0c08cd5982ec Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 18 Mar 2019 16:11:23 +0100 Subject: [PATCH 093/116] context: use subscribe params Improve volume setting --- src/context.c | 15 +++++--- src/internal.h | 3 +- src/introspect.c | 98 ++++++++++++++++++++++++++---------------------- 3 files changed, 64 insertions(+), 52 deletions(-) diff --git a/src/context.c b/src/context.c index d1fe8dfab..c9772af11 100644 --- a/src/context.c +++ b/src/context.c @@ -310,21 +310,24 @@ static void node_event_info(void *object, const struct pw_node_info *info) pw_log_debug("update %d %"PRIu64, g->id, info->change_mask); g->info = pw_node_info_update(g->info, info); - if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) { - for (i = 0; i < info->n_params; i++) { - if (!(info->params[i].flags & SPA_PARAM_INFO_READ)) - continue; + if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS && !g->subscribed) { + uint32_t subscribed[32], n_subscribed = 0; + for (i = 0; i < info->n_params; i++) { switch (info->params[i].id) { case SPA_PARAM_EnumFormat: case SPA_PARAM_Props: - pw_node_proxy_enum_params((struct pw_node_proxy*)g->proxy, - 0, info->params[i].id, 0, -1, NULL); + subscribed[n_subscribed++] = info->params[i].id; break; default: break; } } + if (n_subscribed > 0) { + pw_node_proxy_subscribe_params((struct pw_node_proxy*)g->proxy, + subscribed, n_subscribed); + g->subscribed = true; + } } g->pending_seq = pw_proxy_sync(g->proxy, 0); } diff --git a/src/internal.h b/src/internal.h index 99fbcee21..2b57638ef 100644 --- a/src/internal.h +++ b/src/internal.h @@ -219,7 +219,6 @@ struct global { uint32_t id; uint32_t parent_id; uint32_t type; - int init:1; struct pw_properties *props; pa_context *context; @@ -227,6 +226,8 @@ struct global { pa_subscription_event_type_t event; int pending_seq; + int init:1; + int subscribed:1; void *info; pw_destroy_t destroy; diff --git a/src/introspect.c b/src/introspect.c index 0b78ca23f..cd8a411da 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -243,46 +243,43 @@ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, return o; } -static void set_stream_volume(pa_context *c, pa_stream *s) +static void set_stream_volume(pa_context *c, pa_stream *s, float volume, bool mute) { - float v = s->mute ? 0.0f : s->volume; - pw_stream_set_control(s->stream, SPA_PROP_volume, v); + if (s->volume != volume || s->mute != mute) { + s->volume = volume; + s->mute = mute; + + pw_stream_set_control(s->stream, + SPA_PROP_volume, s->volume, + SPA_PROP_mute, s->mute ? 1.0f : 0.0f, + 0); + } } -static void set_node_volume(pa_context *c, struct global *g, const pa_cvolume *volume) -{ - char buf[1024]; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - float v; - - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - - pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, - SPA_PARAM_Props, 0, - spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_volume, SPA_POD_Float(v))); -} - -static void set_node_mute(pa_context *c, struct global *g, bool mute) +static void set_node_volume(pa_context *c, struct global *g, float volume, bool mute) { char buf[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, - SPA_PARAM_Props, 0, - spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_mute, SPA_POD_Bool(mute))); + if (g->node_info.volume != volume || g->node_info.mute != mute) { + g->node_info.volume = volume; + g->node_info.mute = mute; + pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_volume, SPA_POD_Float(volume), + SPA_PROP_mute, SPA_POD_Bool(mute))); + } } - SPA_EXPORT pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; struct global *g; struct success_ack *d; + float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -298,7 +295,8 @@ pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, c if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) return NULL; - set_node_volume(c, g, volume); + v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; + set_node_volume(c, g, v, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -314,6 +312,7 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name pa_operation *o; struct global *g; struct success_ack *d; + float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -327,7 +326,8 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) return NULL; - set_node_volume(c, g, volume); + v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; + set_node_volume(c, g, v, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -357,7 +357,7 @@ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) return NULL; - set_node_mute(c, g, mute); + set_node_volume(c, g, g->node_info.volume, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -385,7 +385,7 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) return NULL; - set_node_mute(c, g, mute); + set_node_volume(c, g, g->node_info.volume, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -614,6 +614,7 @@ pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, pa_operation *o; struct global *g; struct success_ack *d; + float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -629,7 +630,8 @@ pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) return NULL; - set_node_volume(c, g, volume); + v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; + set_node_volume(c, g, v, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -645,6 +647,7 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na pa_operation *o; struct global *g; struct success_ack *d; + float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -658,7 +661,8 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) return NULL; - set_node_volume(c, g, volume); + v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; + set_node_volume(c, g, v, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -688,7 +692,7 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) return NULL; - set_node_mute(c, g, mute); + set_node_volume(c, g, g->node_info.volume, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -716,7 +720,7 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) return NULL; - set_node_mute(c, g, mute); + set_node_volume(c, g, g->node_info.volume, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1501,6 +1505,7 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons struct global *g; pa_operation *o; struct success_ack *d; + float v; pw_log_debug("contex %p: index %d", c, idx); @@ -1511,12 +1516,13 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons return NULL; } + v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; + if (s) { - s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - set_stream_volume(c, s); + set_stream_volume(c, s, v, s->mute); } else if (g) { - set_node_volume(c, g, volume); + set_node_volume(c, g, v, g->node_info.mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1535,6 +1541,8 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu pa_operation *o; struct success_ack *d; + pw_log_debug("contex %p: index %d", c, idx); + if ((s = find_stream(c, idx)) == NULL) { if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; @@ -1543,11 +1551,10 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu } if (s) { - s->mute = mute; - set_stream_volume(c, s); + set_stream_volume(c, s, s->volume, mute); } else if (g) { - set_node_mute(c, g, mute); + set_node_volume(c, g, g->node_info.volume, mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1770,6 +1777,7 @@ pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, c struct global *g; pa_operation *o; struct success_ack *d; + float v; pw_log_debug("contex %p: index %d", c, idx); @@ -1780,12 +1788,13 @@ pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, c return NULL; } + v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; + if (s) { - s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - set_stream_volume(c, s); + set_stream_volume(c, s, v, s->mute); } else if (g) { - set_node_volume(c, g, volume); + set_node_volume(c, g, v, g->node_info.mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1812,11 +1821,10 @@ pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int } if (s) { - s->mute = mute; - set_stream_volume(c, s); + set_stream_volume(c, s, s->volume, mute); } else if (g) { - set_node_mute(c, g, mute); + set_node_volume(c, g, g->node_info.volume, mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; From 093a068d34fdbd6c01a6a85e0cf0c4aded3bf65d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 17 Apr 2019 15:24:47 +0200 Subject: [PATCH 094/116] stream: deactivate a stream when unlinked --- src/stream.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stream.c b/src/stream.c index 142c2eff1..1e1c50a4c 100644 --- a/src/stream.c +++ b/src/stream.c @@ -635,6 +635,7 @@ static void stream_unlink(pa_stream *s) } spa_list_remove(&s->link); + pw_stream_set_active(s->stream, false); s->context = NULL; pa_stream_unref(s); From f99480c66e443ebe84727acefada5e2e09f0f7f3 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 17 Apr 2019 15:25:05 +0200 Subject: [PATCH 095/116] stream: we can use minreq directly --- src/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream.c b/src/stream.c index 1e1c50a4c..c79355ee2 100644 --- a/src/stream.c +++ b/src/stream.c @@ -940,7 +940,7 @@ static int create_stream(pa_stream_direction_t direction, else str = "Music"; - sprintf(latency, "%u/%u", (s->buffer_attr.minreq / 2) / stride, sample_rate); + sprintf(latency, "%u/%u", s->buffer_attr.minreq / stride, sample_rate); items[0] = SPA_DICT_ITEM_INIT("node.latency", latency); items[1] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_MEDIA, "Audio"); items[2] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_CATEGORY, From 71cab0c38554946e9241806a131a8fe76fc26924 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 17 Apr 2019 15:44:40 +0200 Subject: [PATCH 096/116] context: Improve not implemented methods Let the not implemented methods return an operation instead of NULL to make clients happy. --- src/context.c | 25 +++++++- src/introspect.c | 154 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 163 insertions(+), 16 deletions(-) diff --git a/src/context.c b/src/context.c index c9772af11..c42d47740 100644 --- a/src/context.c +++ b/src/context.c @@ -1011,6 +1011,7 @@ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, d->cb = cb; d->userdata = userdata; pa_operation_sync(o); + pw_log_warn("Not Implemented"); return o; } @@ -1018,15 +1019,35 @@ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, SPA_EXPORT pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_data *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_data)); + d = o->userdata; + d->ret = PA_ERR_ACCESS; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); pw_log_warn("Not Implemented"); - return NULL; + + return o; } SPA_EXPORT pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_data *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_data)); + d = o->userdata; + d->ret = PA_ERR_ACCESS; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); pw_log_warn("Not Implemented"); - return NULL; + + return o; } SPA_EXPORT diff --git a/src/introspect.c b/src/introspect.c index cd8a411da..975dda880 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -398,29 +398,65 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, SPA_EXPORT pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } SPA_EXPORT pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } SPA_EXPORT pa_operation* pa_context_set_sink_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } SPA_EXPORT pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } @@ -733,29 +769,65 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name SPA_EXPORT pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } SPA_EXPORT pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } SPA_EXPORT pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } SPA_EXPORT pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } struct server_data { @@ -911,8 +983,17 @@ pa_operation* pa_context_load_module(pa_context *c, const char*name, const char SPA_EXPORT pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } struct client_data { @@ -1307,8 +1388,17 @@ pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name SPA_EXPORT pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card_name, const char *port_name, int64_t offset, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } static pa_stream *find_stream(pa_context *c, uint32_t idx) @@ -1487,15 +1577,33 @@ pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_i SPA_EXPORT pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } SPA_EXPORT pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } SPA_EXPORT @@ -1759,15 +1867,33 @@ pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_ou SPA_EXPORT pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } SPA_EXPORT pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata) { + pa_operation *o; + struct success_ack *d; + + o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + pa_operation_sync(o); + pw_log_warn("Not Implemented"); - return NULL; + return o; } SPA_EXPORT From a1a5ae1d6859b7ef2d55678e36fa5c9d458b1c65 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 21 May 2019 15:36:44 +0200 Subject: [PATCH 097/116] update for new API --- src/context.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/context.c b/src/context.c index c42d47740..8cef58f87 100644 --- a/src/context.c +++ b/src/context.c @@ -771,7 +771,6 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c if (c->registry_proxy == NULL) { c->registry_proxy = pw_core_proxy_get_registry(c->core_proxy, - PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0); pw_registry_proxy_add_listener(c->registry_proxy, &c->registry_listener, From 2057d7955ce73db0e9cd16d38bbaf8c05b06fb80 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 23 May 2019 12:28:59 +0200 Subject: [PATCH 098/116] update for new api --- src/context.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/context.c b/src/context.c index 8cef58f87..af8d66ab5 100644 --- a/src/context.c +++ b/src/context.c @@ -506,7 +506,7 @@ static int set_mask(pa_context *c, struct global *g) g->event = PA_SUBSCRIPTION_EVENT_CARD; events = &device_events; - client_version = PW_VERSION_DEVICE; + client_version = PW_VERSION_DEVICE_PROXY; destroy = device_destroy; spa_list_init(&g->card_info.profiles); break; @@ -555,7 +555,7 @@ static int set_mask(pa_context *c, struct global *g) g->event = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT; } events = &node_events; - client_version = PW_VERSION_NODE; + client_version = PW_VERSION_NODE_PROXY; destroy = node_destroy; g->node_info.volume = 1.0; g->node_info.mute = false; @@ -566,7 +566,7 @@ static int set_mask(pa_context *c, struct global *g) g->mask = PA_SUBSCRIPTION_MASK_MODULE; g->event = PA_SUBSCRIPTION_EVENT_MODULE; events = &module_events; - client_version = PW_VERSION_MODULE; + client_version = PW_VERSION_MODULE_PROXY; destroy = module_destroy; break; @@ -575,7 +575,7 @@ static int set_mask(pa_context *c, struct global *g) g->mask = PA_SUBSCRIPTION_MASK_CLIENT; g->event = PA_SUBSCRIPTION_EVENT_CLIENT; events = &client_events; - client_version = PW_VERSION_CLIENT; + client_version = PW_VERSION_CLIENT_PROXY; destroy = client_destroy; break; @@ -771,7 +771,7 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c if (c->registry_proxy == NULL) { c->registry_proxy = pw_core_proxy_get_registry(c->core_proxy, - PW_VERSION_REGISTRY, 0); + PW_VERSION_REGISTRY_PROXY, 0); pw_registry_proxy_add_listener(c->registry_proxy, &c->registry_listener, ®istry_events, c); From 7d5e860d0c40383bbfa57b4a2ef84505e4071942 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 May 2019 15:47:05 +0200 Subject: [PATCH 099/116] update for keys --- src/context.c | 22 +++++++++++----------- src/introspect.c | 8 ++++---- src/stream.c | 17 +++++++++-------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/context.c b/src/context.c index af8d66ab5..33a2f6341 100644 --- a/src/context.c +++ b/src/context.c @@ -136,7 +136,7 @@ struct global *pa_context_find_global_by_name(pa_context *c, uint32_t mask, cons if ((g->mask & mask) == 0) continue; if (g->props != NULL && - (str = pw_properties_get(g->props, "node.name")) != NULL && + (str = pw_properties_get(g->props, PW_KEY_NODE_NAME)) != NULL && strcmp(str, name) == 0) return g; if (g->id == id) @@ -203,7 +203,7 @@ static void device_event_info(void *object, const struct pw_device_info *info) i->owner_module = g->parent_id; if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS) { i->driver = info->props ? - spa_dict_lookup(info->props, "device.api") : NULL; + spa_dict_lookup(info->props, PW_KEY_DEVICE_API) : NULL; if (i->proplist) pa_proplist_update_dict(i->proplist, info->props); else @@ -434,9 +434,9 @@ static void client_event_info(void *object, const struct pw_client_info *info) else i->proplist = pa_proplist_new_dict(info->props); i->name = info->props ? - spa_dict_lookup(info->props, "application.name") : NULL; + spa_dict_lookup(info->props, PW_KEY_APP_NAME) : NULL; i->driver = info->props ? - spa_dict_lookup(info->props, PW_CLIENT_PROP_PROTOCOL) : NULL; + spa_dict_lookup(info->props, PW_KEY_PROTOCOL) : NULL; } g->pending_seq = pw_proxy_sync(g->proxy, 0); } @@ -496,7 +496,7 @@ static int set_mask(pa_context *c, struct global *g) case PW_TYPE_INTERFACE_Device: if (g->props == NULL) return 0; - if ((str = pw_properties_get(g->props, "media.class")) == NULL) + if ((str = pw_properties_get(g->props, PW_KEY_MEDIA_CLASS)) == NULL) return 0; if (strcmp(str, "Audio/Device") != 0) return 0; @@ -514,7 +514,7 @@ static int set_mask(pa_context *c, struct global *g) case PW_TYPE_INTERFACE_Node: if (g->props == NULL) return 0; - if ((str = pw_properties_get(g->props, "media.class")) == NULL) + if ((str = pw_properties_get(g->props, PW_KEY_MEDIA_CLASS)) == NULL) return 0; if (strcmp(str, "Audio/Sink") == 0) { @@ -524,7 +524,7 @@ static int set_mask(pa_context *c, struct global *g) g->node_info.monitor = SPA_ID_INVALID; } else if (strcmp(str, "Audio/DSP/Playback") == 0) { - if ((str = pw_properties_get(g->props, "node.session")) == NULL) + if ((str = pw_properties_get(g->props, PW_KEY_NODE_SESSION)) == NULL) return 0; pw_log_debug("found monitor %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_DSP_SINK | PA_SUBSCRIPTION_MASK_SOURCE; @@ -539,7 +539,7 @@ static int set_mask(pa_context *c, struct global *g) g->event = PA_SUBSCRIPTION_EVENT_SOURCE; } else if (strcmp(str, "Audio/DSP/Capture") == 0) { - if ((str = pw_properties_get(g->props, "node.session")) == NULL) + if ((str = pw_properties_get(g->props, PW_KEY_NODE_SESSION)) == NULL) return 0; g->mask = PA_SUBSCRIPTION_MASK_DSP_SOURCE; g->dsp_info.session = pw_properties_parse_int(str); @@ -584,10 +584,10 @@ static int set_mask(pa_context *c, struct global *g) break; case PW_TYPE_INTERFACE_Link: - if ((str = pw_properties_get(g->props, "link.output")) == NULL) + if ((str = pw_properties_get(g->props, PW_KEY_LINK_OUTPUT_PORT)) == NULL) return 0; g->link_info.src = pa_context_find_global(c, pw_properties_parse_int(str)); - if ((str = pw_properties_get(g->props, "link.input")) == NULL) + if ((str = pw_properties_get(g->props, PW_KEY_LINK_OUTPUT_PORT)) == NULL) return 0; g->link_info.dst = pa_context_find_global(c, pw_properties_parse_int(str)); @@ -801,7 +801,7 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * props = pw_properties_new(NULL, NULL); if (name) pw_properties_set(props, PA_PROP_APPLICATION_NAME, name); - pw_properties_set(props, "client.api", "pulseaudio"); + pw_properties_set(props, PW_KEY_CLIENT_API, "pulseaudio"); if (p) pw_properties_update(props, &p->props->dict); diff --git a/src/introspect.c b/src/introspect.c index 975dda880..8f22d2785 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -1433,8 +1433,8 @@ static void sink_input_callback(struct sink_input_data *d) s = find_stream(d->context, g->id); if (info->props) { - if ((name = spa_dict_lookup(info->props, "media.name")) == NULL && - (name = spa_dict_lookup(info->props, "application.name")) == NULL) + if ((name = spa_dict_lookup(info->props, PW_KEY_MEDIA_NAME)) == NULL && + (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL) name = info->name; } else @@ -1726,8 +1726,8 @@ static void source_output_callback(struct source_output_data *d) s = find_stream(d->context, g->id); if (info->props) { - if ((name = spa_dict_lookup(info->props, "media.name")) == NULL && - (name = spa_dict_lookup(info->props, "application.name")) == NULL) + if ((name = spa_dict_lookup(info->props, PW_KEY_MEDIA_NAME)) == NULL && + (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL) name = info->name; } else diff --git a/src/stream.c b/src/stream.c index c79355ee2..ab69074dc 100644 --- a/src/stream.c +++ b/src/stream.c @@ -28,6 +28,7 @@ #include #include +#include #include "core-format.h" #include "internal.h" @@ -197,7 +198,7 @@ static void configure_device(pa_stream *s) } else { s->device_index = g->id; - if ((str = pw_properties_get(g->props, "node.name")) == NULL) + if ((str = pw_properties_get(g->props, PW_KEY_NODE_NAME)) == NULL) s->device_name = strdup("unknown"); else s->device_name = strdup(str); @@ -526,7 +527,7 @@ static pa_stream* stream_new(pa_context *c, const char *name, else name = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME); - props = pw_properties_new("client.api", "pulseaudio", + props = pw_properties_new(PW_KEY_CLIENT_API, "pulseaudio", NULL); pw_properties_update(props, &s->proplist->props->dict); @@ -941,13 +942,13 @@ static int create_stream(pa_stream_direction_t direction, str = "Music"; sprintf(latency, "%u/%u", s->buffer_attr.minreq / stride, sample_rate); - items[0] = SPA_DICT_ITEM_INIT("node.latency", latency); - items[1] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_MEDIA, "Audio"); - items[2] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_CATEGORY, + items[0] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_LATENCY, latency); + items[1] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_TYPE, "Audio"); + items[2] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_CATEGORY, direction == PA_STREAM_PLAYBACK ? "Playback" : "Capture"); - items[3] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_ROLE, str); - items[4] = SPA_DICT_ITEM_INIT("pipewire.monitor", monitor ? "1" : "0"); + items[3] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_ROLE, str); + items[4] = SPA_DICT_ITEM_INIT(PW_KEY_STREAM_MONITOR, monitor ? "1" : "0"); pw_stream_update_properties(s->stream, &SPA_DICT_INIT(items, 5)); @@ -1587,7 +1588,7 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - items[0] = SPA_DICT_ITEM_INIT("media.name", name); + items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_NAME, name); dict = SPA_DICT_INIT(items, 1); pw_stream_update_properties(s->stream, &dict); From af61dcebbfd05fa853cf15aa3798a98b4a087a92 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 May 2019 16:13:39 +0200 Subject: [PATCH 100/116] context: fix link property --- src/context.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context.c b/src/context.c index 33a2f6341..9910cc83c 100644 --- a/src/context.c +++ b/src/context.c @@ -587,7 +587,7 @@ static int set_mask(pa_context *c, struct global *g) if ((str = pw_properties_get(g->props, PW_KEY_LINK_OUTPUT_PORT)) == NULL) return 0; g->link_info.src = pa_context_find_global(c, pw_properties_parse_int(str)); - if ((str = pw_properties_get(g->props, PW_KEY_LINK_OUTPUT_PORT)) == NULL) + if ((str = pw_properties_get(g->props, PW_KEY_LINK_INPUT_PORT)) == NULL) return 0; g->link_info.dst = pa_context_find_global(c, pw_properties_parse_int(str)); From 531185845afd0844ee1e513e9c941e4f1501ef2d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 29 May 2019 10:38:49 +0200 Subject: [PATCH 101/116] fix for api change --- src/context.c | 6 +++--- src/internal.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/context.c b/src/context.c index 9910cc83c..123459a26 100644 --- a/src/context.c +++ b/src/context.c @@ -46,7 +46,7 @@ static void global_free(pa_context *c, struct global *g) if (g->destroy) g->destroy(g); if (g->proxy) { - spa_hook_remove(&g->proxy_proxy_listener); + spa_hook_remove(&g->object_listener); spa_hook_remove(&g->proxy_listener); pw_proxy_destroy(g->proxy); } @@ -621,7 +621,7 @@ static int set_mask(pa_context *c, struct global *g) if (g->proxy == NULL) return -ENOMEM; - pw_proxy_add_proxy_listener(g->proxy, &g->proxy_proxy_listener, events, g); + pw_proxy_add_object_listener(g->proxy, &g->object_listener, events, g); pw_proxy_add_listener(g->proxy, &g->proxy_listener, &proxy_events, g); g->destroy = destroy; } else { @@ -639,7 +639,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, struct global *g; g = calloc(1, sizeof(struct global)); - pw_log_debug("context %p: global %d %p", c, id, g); + pw_log_debug("context %p: global %d %u %p", c, id, type, g); g->context = c; g->id = id; g->parent_id = parent_id; diff --git a/src/internal.h b/src/internal.h index 2b57638ef..12d458673 100644 --- a/src/internal.h +++ b/src/internal.h @@ -234,7 +234,7 @@ struct global { struct pw_proxy *proxy; struct spa_hook proxy_listener; - struct spa_hook proxy_proxy_listener; + struct spa_hook object_listener; union { /* for links */ From 9ebbcd46f9bf87f27c97078c3736fef1407ee259 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 6 Jun 2019 15:02:03 +0200 Subject: [PATCH 102/116] pulse: update for api change --- src/mainloop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mainloop.c b/src/mainloop.c index e5c85589c..dbc4d7851 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -33,22 +33,22 @@ static void do_stop(void *data, uint64_t count) this->quit = true; } -static enum spa_io map_flags_to_spa(pa_io_event_flags_t flags) { - return (enum spa_io) +static uint32_t map_flags_to_spa(pa_io_event_flags_t flags) { + return (uint32_t) ((flags & PA_IO_EVENT_INPUT ? SPA_IO_IN : 0) | (flags & PA_IO_EVENT_OUTPUT ? SPA_IO_OUT : 0) | (flags & PA_IO_EVENT_ERROR ? SPA_IO_ERR : 0) | (flags & PA_IO_EVENT_HANGUP ? SPA_IO_HUP : 0)); } -static pa_io_event_flags_t map_flags_from_spa(enum spa_io flags) { +static pa_io_event_flags_t map_flags_from_spa(uint32_t flags) { return (flags & SPA_IO_IN ? PA_IO_EVENT_INPUT : 0) | (flags & SPA_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | (flags & SPA_IO_ERR ? PA_IO_EVENT_ERROR : 0) | (flags & SPA_IO_HUP ? PA_IO_EVENT_HANGUP : 0); } -static void source_io_func(void *data, int fd, enum spa_io mask) +static void source_io_func(void *data, int fd, uint32_t mask) { pa_io_event *ev = data; if (ev->cb) From d00b9f163594b76a3107fcfeee06982f93ac40b7 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 7 Jun 2019 17:03:49 +0200 Subject: [PATCH 103/116] some format fixes --- src/stream.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/stream.c b/src/stream.c index ab69074dc..5673bd7bf 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1628,7 +1628,8 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) if (r_usec) *r_usec = res; - pw_log_trace("stream %p: %ld %ld %ld %ld %ld %ld %ld", s, now, delay, read_time, + pw_log_trace("stream %p: %"PRIu64" %"PRIu64" %"PRIu64" %"PRIi64" %"PRIi64" %"PRIi64" %"PRIu64, + s, now, delay, read_time, i->write_index, i->read_index, i->write_index - i->read_index, res); @@ -1698,7 +1699,7 @@ const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_NODATA); - pw_log_trace("stream %p: %ld %ld %ld", s, + pw_log_trace("stream %p: %"PRIi64" %"PRIi64" %"PRIi64, s, s->timing_info.write_index, s->timing_info.read_index, (s->timing_info.write_index - s->timing_info.read_index)); From 67c1dca8978dcc69111058c2638be5dc03db158d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 2 Jul 2019 17:38:16 +0200 Subject: [PATCH 104/116] Update for new adapter nodes --- src/context.c | 46 ++++++++++++++++++++++------------------------ src/internal.h | 9 ++------- src/introspect.c | 14 +++++++++----- src/mainloop.c | 10 +++++++++- src/stream.c | 11 ++++++++++- 5 files changed, 52 insertions(+), 38 deletions(-) diff --git a/src/context.c b/src/context.c index 123459a26..69788cf28 100644 --- a/src/context.c +++ b/src/context.c @@ -139,7 +139,7 @@ struct global *pa_context_find_global_by_name(pa_context *c, uint32_t mask, cons (str = pw_properties_get(g->props, PW_KEY_NODE_NAME)) != NULL && strcmp(str, name) == 0) return g; - if (g->id == id) + if (g->id == id || (g->id == (id & PA_IDX_MASK_DSP))) return g; } return NULL; @@ -163,14 +163,8 @@ struct global *pa_context_find_linked(pa_context *c, uint32_t idx) f = pa_context_find_global(c, g->link_info.src->parent_id); else continue; - if (f == NULL) continue; - if (f->mask & PA_SUBSCRIPTION_MASK_DSP) { - if (!(f->mask & PA_SUBSCRIPTION_MASK_SOURCE) || - g->link_info.dst->parent_id != idx) - f = pa_context_find_global(c, f->dsp_info.session); - } return f; } return NULL; @@ -178,12 +172,19 @@ struct global *pa_context_find_linked(pa_context *c, uint32_t idx) static void emit_event(pa_context *c, struct global *g, pa_subscription_event_type_t event) { - if (c->subscribe_mask & g->mask) { - if (c->subscribe_callback) { - pw_log_debug("context %p: obj %d: emit %d:%d", c, g->id, event, g->event); + if (c->subscribe_callback && (c->subscribe_mask & g->mask)) { + pw_log_debug("context %p: obj %d: emit %d:%d", c, g->id, event, g->event); + c->subscribe_callback(c, + event | g->event, + g->id, + c->subscribe_userdata); + + if (g->mask == (PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE)) { + pw_log_debug("context %p: obj %d: emit %d:%d", c, g->node_info.monitor, + event, PA_SUBSCRIPTION_EVENT_SOURCE); c->subscribe_callback(c, - event | g->event, - g->id, + event | PA_SUBSCRIPTION_EVENT_SOURCE, + g->node_info.monitor, c->subscribe_userdata); } } @@ -518,31 +519,28 @@ static int set_mask(pa_context *c, struct global *g) return 0; if (strcmp(str, "Audio/Sink") == 0) { + return 0; pw_log_debug("found sink %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_SINK; g->event = PA_SUBSCRIPTION_EVENT_SINK; g->node_info.monitor = SPA_ID_INVALID; } else if (strcmp(str, "Audio/DSP/Playback") == 0) { - if ((str = pw_properties_get(g->props, PW_KEY_NODE_SESSION)) == NULL) - return 0; - pw_log_debug("found monitor %d", g->id); - g->mask = PA_SUBSCRIPTION_MASK_DSP_SINK | PA_SUBSCRIPTION_MASK_SOURCE; - g->event = PA_SUBSCRIPTION_EVENT_SOURCE; - g->dsp_info.session = pw_properties_parse_int(str); - if ((f = pa_context_find_global(c, g->dsp_info.session)) != NULL) - f->node_info.monitor = g->id; + pw_log_debug("found playback/monitor DSP %d", g->id); + g->mask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE; + g->event = PA_SUBSCRIPTION_EVENT_SINK; + g->node_info.monitor = g->id | PA_IDX_FLAG_DSP; } else if (strcmp(str, "Audio/Source") == 0) { + return 0; pw_log_debug("found source %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_SOURCE; g->event = PA_SUBSCRIPTION_EVENT_SOURCE; } else if (strcmp(str, "Audio/DSP/Capture") == 0) { - if ((str = pw_properties_get(g->props, PW_KEY_NODE_SESSION)) == NULL) - return 0; - g->mask = PA_SUBSCRIPTION_MASK_DSP_SOURCE; - g->dsp_info.session = pw_properties_parse_int(str); + pw_log_debug("found capture DSP %d", g->id); + g->mask = PA_SUBSCRIPTION_MASK_SOURCE; + g->event = PA_SUBSCRIPTION_EVENT_SOURCE; } else if (strcmp(str, "Stream/Output/Audio") == 0) { pw_log_debug("found sink input %d", g->id); diff --git a/src/internal.h b/src/internal.h index 12d458673..a6a8048ea 100644 --- a/src/internal.h +++ b/src/internal.h @@ -210,9 +210,8 @@ struct param { void *param; }; -#define PA_SUBSCRIPTION_MASK_DSP_SINK 0x1000U -#define PA_SUBSCRIPTION_MASK_DSP_SOURCE 0x2000U -#define PA_SUBSCRIPTION_MASK_DSP (PA_SUBSCRIPTION_MASK_DSP_SINK | PA_SUBSCRIPTION_MASK_DSP_SOURCE) +#define PA_IDX_FLAG_DSP 0x800000U +#define PA_IDX_MASK_DSP 0x7fffffU struct global { struct spa_list link; @@ -242,10 +241,6 @@ struct global { struct global *src; struct global *dst; } link_info; - /* for dsp source and sink */ - struct { - uint32_t session; - } dsp_info; /* for sink/source */ struct { uint32_t monitor; diff --git a/src/introspect.c b/src/introspect.c index 8f22d2785..ba3105756 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -507,9 +507,10 @@ static void source_callback(struct source_data *d) i.owner_module = g->parent_id; pa_cvolume_set(&i.volume, 2, g->node_info.volume * PA_VOLUME_NORM); i.mute = g->node_info.mute; - if (g->mask & PA_SUBSCRIPTION_MASK_DSP_SINK) { - i.monitor_of_sink = g->dsp_info.session; + if (g->mask & PA_SUBSCRIPTION_MASK_SINK) { + i.monitor_of_sink = g->id; i.monitor_of_sink_name = "unknown"; + i.index = g->node_info.monitor; } else { i.monitor_of_sink = PA_INVALID_INDEX; i.monitor_of_sink_name = NULL; @@ -588,9 +589,12 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, p PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); - if ((g = pa_context_find_global(c, idx)) == NULL) - return NULL; - if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) + pw_log_debug("context %p: index %d", c, idx); + + if (((g = pa_context_find_global(c, idx)) == NULL || + !(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) && + (((g = pa_context_find_global(c, idx & PA_IDX_MASK_DSP)) == NULL || + !(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)))) return NULL; o = pa_operation_new(c, NULL, source_info, sizeof(struct source_data)); diff --git a/src/mainloop.c b/src/mainloop.c index dbc4d7851..533e4deb5 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -286,10 +286,18 @@ int pa_mainloop_prepare(pa_mainloop *m, int timeout) SPA_EXPORT int pa_mainloop_poll(pa_mainloop *m) { + int res; + if (m->quit) return -2; - return m->n_events = pw_loop_iterate(m->loop, m->timeout); + res = pw_loop_iterate(m->loop, m->timeout); + if (res < 0) { + if (res == -EINTR) + res = 0; + } + m->n_events = res; + return res; } SPA_EXPORT diff --git a/src/stream.c b/src/stream.c index 5673bd7bf..d81022ea8 100644 --- a/src/stream.c +++ b/src/stream.c @@ -197,7 +197,16 @@ static void configure_device(pa_stream *s) s->device_name = NULL; } else { - s->device_index = g->id; + if (s->direction == PA_STREAM_RECORD) { + if (g->mask == (PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE)) + s->device_index = g->node_info.monitor; + else + s->device_index = g->id; + } + else { + s->device_index = g->id; + } + if ((str = pw_properties_get(g->props, PW_KEY_NODE_NAME)) == NULL) s->device_name = strdup("unknown"); else From bfd805a19e4a997064fb3fe3ee67557fcbe671c6 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 11 Jul 2019 11:29:05 +0200 Subject: [PATCH 105/116] pulse: update for removal of DSP nodes --- src/context.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/context.c b/src/context.c index 69788cf28..48c6821f1 100644 --- a/src/context.c +++ b/src/context.c @@ -515,33 +515,23 @@ static int set_mask(pa_context *c, struct global *g) case PW_TYPE_INTERFACE_Node: if (g->props == NULL) return 0; - if ((str = pw_properties_get(g->props, PW_KEY_MEDIA_CLASS)) == NULL) + if ((str = pw_properties_get(g->props, PW_KEY_MEDIA_CLASS)) == NULL) { + pw_log_debug("node %d without "PW_KEY_MEDIA_CLASS, g->id); return 0; + } + pw_log_debug("node %d "PW_KEY_MEDIA_CLASS" '%s'", g->id, str); if (strcmp(str, "Audio/Sink") == 0) { - return 0; pw_log_debug("found sink %d", g->id); - g->mask = PA_SUBSCRIPTION_MASK_SINK; - g->event = PA_SUBSCRIPTION_EVENT_SINK; - g->node_info.monitor = SPA_ID_INVALID; - } - else if (strcmp(str, "Audio/DSP/Playback") == 0) { - pw_log_debug("found playback/monitor DSP %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE; g->event = PA_SUBSCRIPTION_EVENT_SINK; g->node_info.monitor = g->id | PA_IDX_FLAG_DSP; } else if (strcmp(str, "Audio/Source") == 0) { - return 0; pw_log_debug("found source %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_SOURCE; g->event = PA_SUBSCRIPTION_EVENT_SOURCE; } - else if (strcmp(str, "Audio/DSP/Capture") == 0) { - pw_log_debug("found capture DSP %d", g->id); - g->mask = PA_SUBSCRIPTION_MASK_SOURCE; - g->event = PA_SUBSCRIPTION_EVENT_SOURCE; - } else if (strcmp(str, "Stream/Output/Audio") == 0) { pw_log_debug("found sink input %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_SINK_INPUT; From 5e2b740ee0ffc276911ef9217237dbb47da8be6f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 16 Jul 2019 18:53:34 +0200 Subject: [PATCH 106/116] context: wait till nodes are initialized before notify --- src/context.c | 6 ++++-- src/introspect.c | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/context.c b/src/context.c index 48c6821f1..5e502e92b 100644 --- a/src/context.c +++ b/src/context.c @@ -588,9 +588,11 @@ static int set_mask(pa_context *c, struct global *g) g->link_info.dst->parent_id, g->link_info.dst->id); - if ((f = pa_context_find_global(c, g->link_info.src->parent_id)) != NULL) + if ((f = pa_context_find_global(c, g->link_info.src->parent_id)) != NULL && + !f->init) emit_event(c, f, PA_SUBSCRIPTION_EVENT_CHANGE); - if ((f = pa_context_find_global(c, g->link_info.dst->parent_id)) != NULL) + if ((f = pa_context_find_global(c, g->link_info.dst->parent_id)) != NULL && + !f->init) emit_event(c, f, PA_SUBSCRIPTION_EVENT_CHANGE); break; diff --git a/src/introspect.c b/src/introspect.c index ba3105756..0567e4421 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -1424,7 +1424,7 @@ struct sink_input_data { static void sink_input_callback(struct sink_input_data *d) { - struct global *g = d->global, *l, *cl; + struct global *g = d->global, *cl; struct pw_node_info *info = g->info; const char *name; pa_sink_input_info i; @@ -1455,6 +1455,7 @@ static void sink_input_callback(struct sink_input_data *d) i.sink = s->device_index; } else { + struct global *l; l = pa_context_find_linked(d->context, g->id); i.sink = l ? l->id : PA_INVALID_INDEX; } @@ -1490,6 +1491,8 @@ static void sink_input_callback(struct sink_input_data *d) i.has_volume = true; i.volume_writable = true; + pw_log_debug("context %p: sink info for %d sink:%d", g->context, i.index, i.sink); + d->cb(d->context, &i, 0, d->userdata); pa_proplist_free(i.proplist); From 69ad904b63954257da772af1bea8deea27fca369 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 12 Aug 2019 12:31:55 +0200 Subject: [PATCH 107/116] pulse: update per channel volumes Use the channelVolume property to update the individual channel volumes. Update property values to match pulseaudio. Handle the case where the number of channels changes. In pulse this can normally never happen. Emit a remove and add of the node when this happens as to not confuse pulse clients. --- src/context.c | 74 +++++++++++----- src/internal.h | 5 +- src/introspect.c | 221 +++++++++++++++++++++++++++++++---------------- src/stream.c | 15 ++-- 4 files changed, 211 insertions(+), 104 deletions(-) diff --git a/src/context.c b/src/context.c index 5e502e92b..099b147c0 100644 --- a/src/context.c +++ b/src/context.c @@ -190,6 +190,15 @@ static void emit_event(pa_context *c, struct global *g, pa_subscription_event_ty } } +static void update_device_props(struct global *g) +{ + pa_card_info *i = &g->card_info.info; + const char *s; + + if ((s = pa_proplist_gets(i->proplist, PW_KEY_DEVICE_ICON_NAME))) + pa_proplist_sets(i->proplist, PA_PROP_DEVICE_ICON_NAME, s); +} + static void device_event_info(void *object, const struct pw_device_info *info) { struct global *g = object; @@ -200,15 +209,19 @@ static void device_event_info(void *object, const struct pw_device_info *info) info = g->info = pw_device_info_update(g->info, info); i->index = g->id; - i->name = info->name; + i->name = info->props ? + spa_dict_lookup(info->props, PW_KEY_DEVICE_NAME) : "unknown"; i->owner_module = g->parent_id; if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS) { i->driver = info->props ? spa_dict_lookup(info->props, PW_KEY_DEVICE_API) : NULL; + if (i->proplist) pa_proplist_update_dict(i->proplist, info->props); - else + else { i->proplist = pa_proplist_new_dict(info->props); + } + update_device_props(g); } if (info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) { for (n = 0; n < info->n_params; n++) { @@ -333,6 +346,39 @@ static void node_event_info(void *object, const struct pw_node_info *info) g->pending_seq = pw_proxy_sync(g->proxy, 0); } +static void parse_props(struct global *g, const struct spa_pod *param) +{ + struct spa_pod_prop *prop; + struct spa_pod_object *obj = (struct spa_pod_object *) param; + + SPA_POD_OBJECT_FOREACH(obj, prop) { + switch (prop->key) { + case SPA_PROP_volume: + spa_pod_get_float(&prop->value, &g->node_info.volume); + break; + case SPA_PROP_mute: + spa_pod_get_bool(&prop->value, &g->node_info.mute); + break; + case SPA_PROP_channelVolumes: + { + uint32_t n_vals; + + n_vals = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, + g->node_info.channel_volumes, SPA_AUDIO_MAX_CHANNELS); + + if (n_vals != g->node_info.n_channel_volumes) { + emit_event(g->context, g, PA_SUBSCRIPTION_EVENT_REMOVE); + emit_event(g->context, g, PA_SUBSCRIPTION_EVENT_NEW); + g->node_info.n_channel_volumes = n_vals; + } + break; + } + default: + break; + } + } +} + static void node_event_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) @@ -342,24 +388,8 @@ static void node_event_param(void *object, int seq, switch (id) { case SPA_PARAM_Props: - { - struct spa_pod_prop *prop; - struct spa_pod_object *obj = (struct spa_pod_object *) param; - - SPA_POD_OBJECT_FOREACH(obj, prop) { - switch (prop->key) { - case SPA_PROP_volume: - spa_pod_get_float(&prop->value, &g->node_info.volume); - break; - case SPA_PROP_mute: - spa_pod_get_bool(&prop->value, &g->node_info.mute); - break; - default: - break; - } - } + parse_props(g, param); break; - } default: break; } @@ -395,10 +425,8 @@ static void module_event_info(void *object, const struct pw_module_info *info) i->proplist = pa_proplist_new_dict(info->props); } - if (info->change_mask & PW_MODULE_CHANGE_MASK_NAME) - i->name = info->name; - if (info->change_mask & PW_MODULE_CHANGE_MASK_ARGS) - i->argument = info->args; + i->name = info->name; + i->argument = info->args; i->n_used = -1; i->auto_unload = false; g->pending_seq = pw_proxy_sync(g->proxy, 0); diff --git a/src/internal.h b/src/internal.h index a6a8048ea..d93d3eebb 100644 --- a/src/internal.h +++ b/src/internal.h @@ -246,6 +246,8 @@ struct global { uint32_t monitor; float volume; bool mute; + uint32_t n_channel_volumes; + float channel_volumes[SPA_AUDIO_MAX_CHANNELS]; } node_info; /* for devices */ struct { @@ -382,7 +384,8 @@ struct pa_stream { uint32_t buffer_size; uint32_t buffer_offset; - float volume; + uint32_t n_channel_volumes; + float channel_volumes[SPA_AUDIO_MAX_CHANNELS]; bool mute; pa_operation *drain; uint64_t queued; diff --git a/src/introspect.c b/src/introspect.c index 0567e4421..20f110687 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -91,22 +91,35 @@ static void sink_callback(struct sink_data *d) { struct global *g = d->global; struct pw_node_info *info = g->info; + const char *str; + uint32_t n; pa_sink_info i; pa_format_info ii[1]; pa_format_info *ip[1]; - pw_log_debug("sink %d %s monitor %d", g->id, info->name, g->node_info.monitor); - spa_zero(i); - i.name = info->name; + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_NAME))) + i.name = str; + else + i.name = "unknown"; + pw_log_debug("sink %d %s monitor %d", g->id, i.name, g->node_info.monitor); i.index = g->id; - i.description = info->name; + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION))) + i.description = str; + else + i.description = "unknown"; + i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - i.sample_spec.channels = 2; - pa_channel_map_init_auto(&i.channel_map, 2, PA_CHANNEL_MAP_DEFAULT); + if (g->node_info.n_channel_volumes) + i.sample_spec.channels = g->node_info.n_channel_volumes; + else + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); i.owner_module = g->parent_id; - pa_cvolume_set(&i.volume, 2, g->node_info.volume * PA_VOLUME_NORM); + i.volume.channels = i.sample_spec.channels; + for (n = 0; n < i.volume.channels; n++) + i.volume.values[n] = g->node_info.volume * g->node_info.channel_volumes[n] * PA_VOLUME_NORM; i.mute = g->node_info.mute; i.monitor_source = g->node_info.monitor; i.monitor_source_name = "unknown"; @@ -243,43 +256,87 @@ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, return o; } -static void set_stream_volume(pa_context *c, pa_stream *s, float volume, bool mute) +static void set_stream_volume(pa_context *c, pa_stream *s, const pa_cvolume *volume, bool mute) { - if (s->volume != volume || s->mute != mute) { - s->volume = volume; + uint32_t i, n_channel_volumes; + float channel_volumes[SPA_AUDIO_MAX_CHANNELS]; + float *vols; + + if (volume) { + for (i = 0; i < volume->channels; i++) + channel_volumes[i] = volume->values[i] / (float) PA_VOLUME_NORM; + vols = channel_volumes; + n_channel_volumes = volume->channels; + } else { + vols = s->channel_volumes; + n_channel_volumes = s->n_channel_volumes; + } + + if (n_channel_volumes != s->n_channel_volumes || + !memcmp(s->channel_volumes, vols, n_channel_volumes * sizeof(float)) || + s->mute != mute) { + float val; + + memcpy(s->channel_volumes, vols, n_channel_volumes * sizeof(float)); + s->n_channel_volumes = n_channel_volumes; s->mute = mute; + val = s->mute ? 1.0f : 0.0f; + pw_stream_set_control(s->stream, - SPA_PROP_volume, s->volume, - SPA_PROP_mute, s->mute ? 1.0f : 0.0f, + SPA_PROP_mute, 1, &val, + SPA_PROP_channelVolumes, n_channel_volumes, channel_volumes, 0); } } -static void set_node_volume(pa_context *c, struct global *g, float volume, bool mute) +static void set_node_volume(pa_context *c, struct global *g, const pa_cvolume *volume, bool mute) { char buf[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + uint32_t i, n_channel_volumes; + float channel_volumes[SPA_AUDIO_MAX_CHANNELS]; + float *vols; - if (g->node_info.volume != volume || g->node_info.mute != mute) { - g->node_info.volume = volume; - g->node_info.mute = mute; - pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, - SPA_PARAM_Props, 0, - spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_volume, SPA_POD_Float(volume), - SPA_PROP_mute, SPA_POD_Bool(mute))); + if (volume) { + for (i = 0; i < volume->channels; i++) + channel_volumes[i] = volume->values[i] / (float) PA_VOLUME_NORM; + vols = channel_volumes; + n_channel_volumes = volume->channels; + + if (n_channel_volumes == g->node_info.n_channel_volumes && + memcmp(g->node_info.channel_volumes, vols, n_channel_volumes * sizeof(float)) == 0 && + mute == g->node_info.mute) + return; + + memcpy(g->node_info.channel_volumes, vols, n_channel_volumes * sizeof(float)); + g->node_info.n_channel_volumes = n_channel_volumes; + } else { + n_channel_volumes = g->node_info.n_channel_volumes; + vols = g->node_info.channel_volumes; + if (mute == g->node_info.mute) + return; } + g->node_info.mute = mute; + + pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_mute, SPA_POD_Bool(mute), + SPA_PROP_channelVolumes, SPA_POD_Array(sizeof(float), + SPA_TYPE_Float, + n_channel_volumes, + vols))); } + SPA_EXPORT pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; struct global *g; struct success_ack *d; - float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -295,8 +352,7 @@ pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, c if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) return NULL; - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -312,7 +368,6 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name pa_operation *o; struct global *g; struct success_ack *d; - float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -326,8 +381,7 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) return NULL; - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -357,7 +411,7 @@ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) return NULL; - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -385,7 +439,7 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) return NULL; - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -488,6 +542,8 @@ static void source_callback(struct source_data *d) { struct global *g = d->global; struct pw_node_info *info = g->info; + const char *str; + uint32_t n; pa_source_info i; pa_format_info ii[1]; pa_format_info *ip[1]; @@ -497,15 +553,26 @@ static void source_callback(struct source_data *d) PA_SOURCE_DECIBEL_VOLUME; spa_zero(i); - i.name = info->name; + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_NAME))) + i.name = str; + else + i.name = "unknown"; i.index = g->id; - i.description = info->name; + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION))) + i.description = str; + else + i.description = "unknown"; i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - i.sample_spec.channels = 2; - pa_channel_map_init_auto(&i.channel_map, 2, PA_CHANNEL_MAP_DEFAULT); + if (g->node_info.n_channel_volumes) + i.sample_spec.channels = g->node_info.n_channel_volumes; + else + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); i.owner_module = g->parent_id; - pa_cvolume_set(&i.volume, 2, g->node_info.volume * PA_VOLUME_NORM); + i.volume.channels = i.sample_spec.channels; + for (n = 0; n < i.volume.channels; n++) + i.volume.values[n] = g->node_info.volume * g->node_info.channel_volumes[n] * PA_VOLUME_NORM; i.mute = g->node_info.mute; if (g->mask & PA_SUBSCRIPTION_MASK_SINK) { i.monitor_of_sink = g->id; @@ -654,7 +721,6 @@ pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, pa_operation *o; struct global *g; struct success_ack *d; - float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -670,8 +736,7 @@ pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) return NULL; - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -687,7 +752,6 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na pa_operation *o; struct global *g; struct success_ack *d; - float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -701,8 +765,7 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) return NULL; - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -732,7 +795,7 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) return NULL; - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -760,7 +823,7 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) return NULL; - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -858,7 +921,7 @@ static void server_callback(struct server_data *d) i.default_sink_name = "unknown"; i.default_source_name = "unknown"; i.cookie = info->cookie; - pa_channel_map_init_extend(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + pa_channel_map_init_extend(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); d->cb(d->context, &i, d->userdata); } @@ -1427,6 +1490,7 @@ static void sink_input_callback(struct sink_input_data *d) struct global *g = d->global, *cl; struct pw_node_info *info = g->info; const char *name; + uint32_t n; pa_sink_input_info i; pa_format_info ii[1]; pa_stream *s; @@ -1438,17 +1502,18 @@ static void sink_input_callback(struct sink_input_data *d) if (info->props) { if ((name = spa_dict_lookup(info->props, PW_KEY_MEDIA_NAME)) == NULL && - (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL) - name = info->name; + (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL && + (name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) == NULL) + name = "unknown"; } else - name = info->name; + name = "unknown"; cl = pa_context_find_global(d->context, g->parent_id); spa_zero(i); i.index = g->id; - i.name = name ? name : "Unknown"; + i.name = name; i.owner_module = PA_INVALID_INDEX; i.client = g->parent_id; if (s) { @@ -1465,20 +1530,25 @@ static void sink_input_callback(struct sink_input_data *d) i.channel_map = s->channel_map; else pa_channel_map_init_auto(&i.channel_map, - i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + i.sample_spec.channels, PA_CHANNEL_MAP_OSS); i.format = s->format; } else { i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - i.sample_spec.channels = 2; - pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + i.sample_spec.channels = g->node_info.n_channel_volumes; + if (i.sample_spec.channels == 0) + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); i.format = ii; } pa_cvolume_init(&i.volume); - pa_cvolume_set(&i.volume, i.sample_spec.channels, g->node_info.volume * PA_VOLUME_NORM); + i.volume.channels = i.sample_spec.channels; + for (n = 0; n < i.volume.channels; n++) + i.volume.values[n] = g->node_info.volume * g->node_info.channel_volumes[n] * PA_VOLUME_NORM; + i.mute = g->node_info.mute; i.buffer_usec = 0; i.sink_usec = 0; @@ -1620,7 +1690,6 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons struct global *g; pa_operation *o; struct success_ack *d; - float v; pw_log_debug("contex %p: index %d", c, idx); @@ -1631,13 +1700,11 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons return NULL; } - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - if (s) { - set_stream_volume(c, s, v, s->mute); + set_stream_volume(c, s, volume, s->mute); } else if (g) { - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1666,10 +1733,10 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu } if (s) { - set_stream_volume(c, s, s->volume, mute); + set_stream_volume(c, s, NULL, mute); } else if (g) { - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1721,7 +1788,8 @@ static void source_output_callback(struct source_output_data *d) { struct global *g = d->global, *l, *cl; struct pw_node_info *info = g->info; - const char *name; + const char *name = NULL; + uint32_t n; pa_source_output_info i; pa_format_info ii[1]; pa_stream *s; @@ -1734,11 +1802,12 @@ static void source_output_callback(struct source_output_data *d) if (info->props) { if ((name = spa_dict_lookup(info->props, PW_KEY_MEDIA_NAME)) == NULL && - (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL) - name = info->name; + (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL && + (name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) == NULL) + name = NULL; } - else - name = info->name; + if (name == NULL) + name = "unknown"; cl = pa_context_find_global(d->context, g->parent_id); @@ -1760,20 +1829,25 @@ static void source_output_callback(struct source_output_data *d) i.channel_map = s->channel_map; else pa_channel_map_init_auto(&i.channel_map, - i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + i.sample_spec.channels, PA_CHANNEL_MAP_OSS); i.format = s->format; } else { i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - i.sample_spec.channels = 2; - pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + i.sample_spec.channels = g->node_info.n_channel_volumes; + if (i.sample_spec.channels == 0) + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); i.format = ii; } pa_cvolume_init(&i.volume); - pa_cvolume_set(&i.volume, i.sample_spec.channels, g->node_info.volume * PA_VOLUME_NORM); + i.volume.channels = i.sample_spec.channels; + for (n = 0; n < i.volume.channels; n++) + i.volume.values[n] = g->node_info.volume * g->node_info.channel_volumes[n] * PA_VOLUME_NORM; + i.mute = g->node_info.mute; i.buffer_usec = 0; i.source_usec = 0; @@ -1910,7 +1984,6 @@ pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, c struct global *g; pa_operation *o; struct success_ack *d; - float v; pw_log_debug("contex %p: index %d", c, idx); @@ -1921,13 +1994,11 @@ pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, c return NULL; } - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - if (s) { - set_stream_volume(c, s, v, s->mute); + set_stream_volume(c, s, volume, s->mute); } else if (g) { - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1954,10 +2025,10 @@ pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int } if (s) { - set_stream_volume(c, s, s->volume, mute); + set_stream_volume(c, s, NULL, mute); } else if (g) { - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; diff --git a/src/stream.c b/src/stream.c index d81022ea8..35297f346 100644 --- a/src/stream.c +++ b/src/stream.c @@ -828,7 +828,7 @@ static int create_stream(pa_stream_direction_t direction, int res; enum pw_stream_flags fl; const struct spa_pod *params[16]; - uint32_t n_params = 0; + uint32_t i, n_params = 0; uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); uint32_t sample_rate = 0, stride = 0; @@ -847,10 +847,15 @@ static int create_stream(pa_stream_direction_t direction, s->direction = direction; s->timing_info_valid = false; s->disconnecting = false; - if (volume) - s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - else - s->volume = 1.0; + if (volume) { + for (i = 0; i < volume->channels; i++) + s->channel_volumes[i] = volume->values[i] / (float) PA_VOLUME_NORM; + s->n_channel_volumes = volume->channels; + } else { + for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + s->channel_volumes[i] = 1.0; + s->n_channel_volumes = 0; + } s->mute = false; pa_stream_set_state(s, PA_STREAM_CREATING); From d5b13a1de2ea886d6b9d6773bae694b24137fc8d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 13 Aug 2019 18:46:27 +0200 Subject: [PATCH 108/116] stream: improve control update When setting the volume, update the stream controls with the new values and update the internal state with the control_info event. --- src/introspect.c | 11 ++--------- src/stream.c | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/introspect.c b/src/introspect.c index 20f110687..d53149783 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -275,17 +275,10 @@ static void set_stream_volume(pa_context *c, pa_stream *s, const pa_cvolume *vol if (n_channel_volumes != s->n_channel_volumes || !memcmp(s->channel_volumes, vols, n_channel_volumes * sizeof(float)) || s->mute != mute) { - float val; - - memcpy(s->channel_volumes, vols, n_channel_volumes * sizeof(float)); - s->n_channel_volumes = n_channel_volumes; - s->mute = mute; - - val = s->mute ? 1.0f : 0.0f; - + float val = s->mute ? 1.0f : 0.0f; pw_stream_set_control(s->stream, SPA_PROP_mute, 1, &val, - SPA_PROP_channelVolumes, n_channel_volumes, channel_volumes, + SPA_PROP_channelVolumes, n_channel_volumes, vols, 0); } } diff --git a/src/stream.c b/src/stream.c index 35297f346..4dafcd698 100644 --- a/src/stream.c +++ b/src/stream.c @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -414,6 +415,24 @@ static void stream_format_changed(void *data, const struct spa_pod *format) pw_stream_finish_format(s->stream, res, params, n_params); } +static void stream_control_info(void *data, uint32_t id, const struct pw_stream_control *control) +{ + pa_stream *s = data; + + switch (id) { + case SPA_PROP_mute: + if (control->n_values > 0) + s->mute = control->values[0] >= 0.5f; + break; + case SPA_PROP_channelVolumes: + s->n_channel_volumes = SPA_MAX(SPA_AUDIO_MAX_CHANNELS, control->n_values); + memcpy(s->channel_volumes, control->values, s->n_channel_volumes * sizeof(float)); + break; + default: + break; + } +} + static void stream_add_buffer(void *data, struct pw_buffer *buffer) { pa_stream *s = data; @@ -503,6 +522,7 @@ static const struct pw_stream_events stream_events = PW_VERSION_STREAM_EVENTS, .state_changed = stream_state_changed, .format_changed = stream_format_changed, + .control_info = stream_control_info, .add_buffer = stream_add_buffer, .remove_buffer = stream_remove_buffer, .process = stream_process, From e4a6782c48bb6d6a1d8bc74a82d0fabb0b288a47 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 13 Aug 2019 18:47:30 +0200 Subject: [PATCH 109/116] stream: always require the amount of buffers --- src/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream.c b/src/stream.c index 4dafcd698..44a27fedb 100644 --- a/src/stream.c +++ b/src/stream.c @@ -295,7 +295,7 @@ static const struct spa_pod *get_buffers_param(pa_stream *s, pa_buffer_attr *att param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(buffers, 3, MAX_BUFFERS), + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(buffers, buffers, MAX_BUFFERS), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(blocks), SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( size * stride, From 23a875e2bf4eab785c88a7db24da7019b52f3ef1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 16 Aug 2019 15:22:38 +0200 Subject: [PATCH 110/116] context: remove core listeners --- src/context.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/context.c b/src/context.c index 099b147c0..27583e071 100644 --- a/src/context.c +++ b/src/context.c @@ -733,9 +733,17 @@ static void remote_state_changed(void *data, enum pw_remote_state old, switch(state) { case PW_REMOTE_STATE_ERROR: + if (c->core_proxy) { + spa_hook_remove(&c->core_listener); + c->core_proxy = NULL; + } context_fail(c, PA_ERR_CONNECTIONTERMINATED); break; case PW_REMOTE_STATE_UNCONNECTED: + if (c->core_proxy) { + spa_hook_remove(&c->core_listener); + c->core_proxy = NULL; + } if (!c->disconnect) context_fail(c, PA_ERR_CONNECTIONTERMINATED); break; From f9fce3cb3042486d8015925bf794acd5c4117ba7 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 16 Aug 2019 15:23:05 +0200 Subject: [PATCH 111/116] stream: use the stream drain method to drain --- src/stream.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/stream.c b/src/stream.c index 44a27fedb..ef1c155d0 100644 --- a/src/stream.c +++ b/src/stream.c @@ -493,15 +493,6 @@ static void stream_process(void *data) update_timing_info(s); - if (s->drain && s->queued == 0) { - pa_operation *o = s->drain; - pa_operation_ref(o); - if (o->callback) - o->callback(o, o->userdata); - pa_operation_unref(o); - s->drain = NULL; - } - while (dequeue_buffer(s) == 0); if (s->dequeued_size <= 0) @@ -517,6 +508,20 @@ static void stream_process(void *data) } } +static void stream_drained(void *data) +{ + pa_stream *s = data; + + if (s->drain) { + pa_operation *o = s->drain; + pa_operation_ref(o); + if (o->callback) + o->callback(o, o->userdata); + pa_operation_unref(o); + s->drain = NULL; + } +} + static const struct pw_stream_events stream_events = { PW_VERSION_STREAM_EVENTS, @@ -526,6 +531,7 @@ static const struct pw_stream_events stream_events = .add_buffer = stream_add_buffer, .remove_buffer = stream_remove_buffer, .process = stream_process, + .drained = stream_drained, }; static pa_stream* stream_new(pa_context *c, const char *name, @@ -1320,6 +1326,7 @@ pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *use PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); pw_log_debug("stream %p", s); + pw_stream_flush(s->stream, true); o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); d = o->userdata; d->cb = cb; From 2308318b39bd769b5059213958193448a4edea27 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 16 Aug 2019 22:10:08 +0200 Subject: [PATCH 112/116] use properties instead of parent_id --- src/context.c | 46 ++++++++++++++++++++++++++++++---------------- src/internal.h | 5 ++++- src/introspect.c | 12 ++++++------ src/stream.c | 5 ++++- 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/context.c b/src/context.c index 27583e071..0d031e4eb 100644 --- a/src/context.c +++ b/src/context.c @@ -150,19 +150,24 @@ struct global *pa_context_find_linked(pa_context *c, uint32_t idx) struct global *g, *f; spa_list_for_each(g, &c->globals, link) { + uint32_t src_node_id, dst_node_id; + if (g->type != PW_TYPE_INTERFACE_Link) continue; - pw_log_debug("context %p: %p %d %d %d", c, g, idx, - g->link_info.src->parent_id, - g->link_info.dst->parent_id); + src_node_id = g->link_info.src->port_info.node_id; + dst_node_id = g->link_info.dst->port_info.node_id; - if (g->link_info.src->parent_id == idx) - f = pa_context_find_global(c, g->link_info.dst->parent_id); - else if (g->link_info.dst->parent_id == idx) - f = pa_context_find_global(c, g->link_info.src->parent_id); + pw_log_debug("context %p: %p %d %d %d", c, g, idx, + src_node_id, dst_node_id); + + if (src_node_id == idx) + f = pa_context_find_global(c, dst_node_id); + else if (dst_node_id == idx) + f = pa_context_find_global(c, src_node_id); else continue; + if (f == NULL) continue; return f; @@ -203,6 +208,7 @@ static void device_event_info(void *object, const struct pw_device_info *info) { struct global *g = object; pa_card_info *i = &g->card_info.info; + const char *str; uint32_t n; pw_log_debug("global %p: id:%d change-mask:%"PRIu64, g, g->id, info->change_mask); @@ -211,7 +217,8 @@ static void device_event_info(void *object, const struct pw_device_info *info) i->index = g->id; i->name = info->props ? spa_dict_lookup(info->props, PW_KEY_DEVICE_NAME) : "unknown"; - i->owner_module = g->parent_id; + str = info->props ? spa_dict_lookup(info->props, PW_KEY_MODULE_ID) : NULL; + i->owner_module = str ? (unsigned)atoi(str) : SPA_ID_INVALID; if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS) { i->driver = info->props ? spa_dict_lookup(info->props, PW_KEY_DEVICE_API) : NULL; @@ -449,13 +456,15 @@ static void module_destroy(void *data) static void client_event_info(void *object, const struct pw_client_info *info) { struct global *g = object; + const char *str; pa_client_info *i = &g->client_info.info; pw_log_debug("update %d", g->id); info = g->info = pw_client_info_update(g->info, info); i->index = g->id; - i->owner_module = g->parent_id; + str = info->props ? spa_dict_lookup(info->props, PW_KEY_MODULE_ID) : NULL; + i->owner_module = str ? (unsigned)atoi(str) : SPA_ID_INVALID; if (info->change_mask & PW_CLIENT_CHANGE_MASK_PROPS) { if (i->proplist) @@ -570,6 +579,10 @@ static int set_mask(pa_context *c, struct global *g) g->mask = PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT; g->event = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT; } + + if ((str = pw_properties_get(g->props, PW_KEY_CLIENT_ID)) != NULL) + g->node_info.client_id = atoi(str); + events = &node_events; client_version = PW_VERSION_NODE_PROXY; destroy = node_destroy; @@ -596,7 +609,9 @@ static int set_mask(pa_context *c, struct global *g) break; case PW_TYPE_INTERFACE_Port: - pw_log_debug("found port %d", g->id); + if ((str = pw_properties_get(g->props, PW_KEY_NODE_ID)) != NULL) + g->port_info.node_id = atoi(str); + pw_log_debug("found port %d node %d", g->id, g->port_info.node_id); break; case PW_TYPE_INTERFACE_Link: @@ -611,15 +626,15 @@ static int set_mask(pa_context *c, struct global *g) return 0; pw_log_debug("link %d:%d->%d:%d", - g->link_info.src->parent_id, + g->link_info.src->port_info.node_id, g->link_info.src->id, - g->link_info.dst->parent_id, + g->link_info.dst->port_info.node_id, g->link_info.dst->id); - if ((f = pa_context_find_global(c, g->link_info.src->parent_id)) != NULL && + if ((f = pa_context_find_global(c, g->link_info.src->port_info.node_id)) != NULL && !f->init) emit_event(c, f, PA_SUBSCRIPTION_EVENT_CHANGE); - if ((f = pa_context_find_global(c, g->link_info.dst->parent_id)) != NULL && + if ((f = pa_context_find_global(c, g->link_info.dst->port_info.node_id)) != NULL && !f->init) emit_event(c, f, PA_SUBSCRIPTION_EVENT_CHANGE); @@ -649,7 +664,7 @@ static int set_mask(pa_context *c, struct global *g) return 1; } -static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, +static void registry_event_global(void *data, uint32_t id, uint32_t permissions, uint32_t type, uint32_t version, const struct spa_dict *props) { @@ -660,7 +675,6 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, pw_log_debug("context %p: global %d %u %p", c, id, type, g); g->context = c; g->id = id; - g->parent_id = parent_id; g->type = type; g->init = true; g->props = props ? pw_properties_new_dict(props) : NULL; diff --git a/src/internal.h b/src/internal.h index d93d3eebb..ce7ba5420 100644 --- a/src/internal.h +++ b/src/internal.h @@ -216,7 +216,6 @@ struct param { struct global { struct spa_list link; uint32_t id; - uint32_t parent_id; uint32_t type; struct pw_properties *props; @@ -243,12 +242,16 @@ struct global { } link_info; /* for sink/source */ struct { + uint32_t client_id; uint32_t monitor; float volume; bool mute; uint32_t n_channel_volumes; float channel_volumes[SPA_AUDIO_MAX_CHANNELS]; } node_info; + struct { + uint32_t node_id; + } port_info; /* for devices */ struct { struct spa_list profiles; diff --git a/src/introspect.c b/src/introspect.c index d53149783..419a0a069 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -116,7 +116,7 @@ static void sink_callback(struct sink_data *d) else i.sample_spec.channels = 2; pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); - i.owner_module = g->parent_id; + i.owner_module = 0; i.volume.channels = i.sample_spec.channels; for (n = 0; n < i.volume.channels; n++) i.volume.values[n] = g->node_info.volume * g->node_info.channel_volumes[n] * PA_VOLUME_NORM; @@ -562,7 +562,7 @@ static void source_callback(struct source_data *d) else i.sample_spec.channels = 2; pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); - i.owner_module = g->parent_id; + i.owner_module = 0; i.volume.channels = i.sample_spec.channels; for (n = 0; n < i.volume.channels; n++) i.volume.values[n] = g->node_info.volume * g->node_info.channel_volumes[n] * PA_VOLUME_NORM; @@ -1502,13 +1502,13 @@ static void sink_input_callback(struct sink_input_data *d) else name = "unknown"; - cl = pa_context_find_global(d->context, g->parent_id); + cl = pa_context_find_global(d->context, g->node_info.client_id); spa_zero(i); i.index = g->id; i.name = name; i.owner_module = PA_INVALID_INDEX; - i.client = g->parent_id; + i.client = g->node_info.client_id; if (s) { i.sink = s->device_index; } @@ -1802,13 +1802,13 @@ static void source_output_callback(struct source_output_data *d) if (name == NULL) name = "unknown"; - cl = pa_context_find_global(d->context, g->parent_id); + cl = pa_context_find_global(d->context, g->node_info.client_id); spa_zero(i); i.index = g->id; i.name = name ? name : "Unknown"; i.owner_module = PA_INVALID_INDEX; - i.client = g->parent_id; + i.client = g->node_info.client_id; if (s) { i.source = s->device_index; } diff --git a/src/stream.c b/src/stream.c index ef1c155d0..3b4a9f9c5 100644 --- a/src/stream.c +++ b/src/stream.c @@ -797,7 +797,10 @@ const char *pa_stream_get_device_name(pa_stream *s) PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->device_name, PA_ERR_BADSTATE); +// PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->device_name, PA_ERR_BADSTATE); + + if (s->device_name == NULL) + return "unnamed"; return s->device_name; } From 8666bcac65f00a3ad5f0fcc7d016ca9549c3fe44 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Fri, 27 Sep 2019 07:45:15 +0530 Subject: [PATCH 113/116] pulse: Deal with header consitification in PulseAudio 13.0 The 13.0 release included changes to constify various parameters in the public headers, which breaks our implementation. This adds an optional const qualifier based on the version we're compiling against to deal with that. There are some warnings caused by bad annotations upstream which should be fixed separately. --- src/context.c | 26 +++++++++++++------------- src/internal.h | 8 ++++++++ src/mainloop.c | 2 +- src/operation.c | 2 +- src/proplist.c | 18 +++++++++--------- src/scache.c | 2 +- src/stream.c | 24 ++++++++++++------------ src/thread-mainloop.c | 2 +- src/volume.c | 2 +- 9 files changed, 47 insertions(+), 39 deletions(-) diff --git a/src/context.c b/src/context.c index 0d031e4eb..6f9fa0483 100644 --- a/src/context.c +++ b/src/context.c @@ -828,7 +828,7 @@ pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_c } SPA_EXPORT -pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) +pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, PA_CONST pa_proplist *p) { struct pw_core *core; struct pw_loop *loop; @@ -944,7 +944,7 @@ void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void } SPA_EXPORT -int pa_context_errno(pa_context *c) +int pa_context_errno(PA_CONST pa_context *c) { if (!c) return PA_ERR_INVALID; @@ -955,7 +955,7 @@ int pa_context_errno(pa_context *c) } SPA_EXPORT -int pa_context_is_pending(pa_context *c) +int pa_context_is_pending(PA_CONST pa_context *c) { pa_assert(c); pa_assert(c->refcount >= 1); @@ -966,7 +966,7 @@ int pa_context_is_pending(pa_context *c) } SPA_EXPORT -pa_context_state_t pa_context_get_state(pa_context *c) +pa_context_state_t pa_context_get_state(PA_CONST pa_context *c) { pa_assert(c); pa_assert(c->refcount >= 1); @@ -1090,7 +1090,7 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_ } SPA_EXPORT -int pa_context_is_local(pa_context *c) +int pa_context_is_local(PA_CONST pa_context *c) { pa_assert(c); pa_assert(c->refcount >= 1); @@ -1128,7 +1128,7 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su } SPA_EXPORT -const char* pa_context_get_server(pa_context *c) +const char* pa_context_get_server(PA_CONST pa_context *c) { const struct pw_core_info *info; @@ -1142,13 +1142,13 @@ const char* pa_context_get_server(pa_context *c) } SPA_EXPORT -uint32_t pa_context_get_protocol_version(pa_context *c) +uint32_t pa_context_get_protocol_version(PA_CONST pa_context *c) { return PA_PROTOCOL_VERSION; } SPA_EXPORT -uint32_t pa_context_get_server_protocol_version(pa_context *c) +uint32_t pa_context_get_server_protocol_version(PA_CONST pa_context *c) { pa_assert(c); pa_assert(c->refcount >= 1); @@ -1159,7 +1159,7 @@ uint32_t pa_context_get_server_protocol_version(pa_context *c) } SPA_EXPORT -pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata) +pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, PA_CONST pa_proplist *p, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; struct success_data *d; @@ -1204,13 +1204,13 @@ pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[] } SPA_EXPORT -uint32_t pa_context_get_index(pa_context *c) +uint32_t pa_context_get_index(PA_CONST pa_context *c) { return c->client_index; } SPA_EXPORT -pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) +pa_time_event* pa_context_rttime_new(PA_CONST pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) { struct timeval tv; @@ -1227,7 +1227,7 @@ pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_even } SPA_EXPORT -void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) +void pa_context_rttime_restart(PA_CONST pa_context *c, pa_time_event *e, pa_usec_t usec) { struct timeval tv; @@ -1244,7 +1244,7 @@ void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) } SPA_EXPORT -size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss) +size_t pa_context_get_tile_size(PA_CONST pa_context *c, const pa_sample_spec *ss) { size_t fs, mbs; diff --git a/src/internal.h b/src/internal.h index ce7ba5420..338654e51 100644 --- a/src/internal.h +++ b/src/internal.h @@ -31,12 +31,20 @@ #include #include #include +#include #include #include #include #include +/* Some PulseAudio API added const qualifiers in 13.0 */ +#if PA_MAJOR >= 13 +#define PA_CONST const +#else +#define PA_CONST +#endif + #define PA_MAX_FORMATS (PA_ENCODING_MAX) #ifdef __cplusplus diff --git a/src/mainloop.c b/src/mainloop.c index 533e4deb5..6198bd8c5 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -310,7 +310,7 @@ int pa_mainloop_dispatch(pa_mainloop *m) } SPA_EXPORT -int pa_mainloop_get_retval(pa_mainloop *m) +int pa_mainloop_get_retval(PA_CONST pa_mainloop *m) { return m->retval; } diff --git a/src/operation.c b/src/operation.c index 0971f3cd9..669c9c037 100644 --- a/src/operation.c +++ b/src/operation.c @@ -144,7 +144,7 @@ void pa_operation_done(pa_operation *o) { SPA_EXPORT -pa_operation_state_t pa_operation_get_state(pa_operation *o) +pa_operation_state_t pa_operation_get_state(PA_CONST pa_operation *o) { pa_assert(o); pa_assert(o->refcount >= 1); diff --git a/src/proplist.c b/src/proplist.c index ebecc72df..247920d19 100644 --- a/src/proplist.c +++ b/src/proplist.c @@ -145,13 +145,13 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb } SPA_EXPORT -const char *pa_proplist_gets(pa_proplist *p, const char *key) +const char *pa_proplist_gets(PA_CONST pa_proplist *p, const char *key) { return pw_properties_get(p->props, key); } SPA_EXPORT -int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes) +int pa_proplist_get(PA_CONST pa_proplist *p, const char *key, const void **data, size_t *nbytes) { const char *val; @@ -223,7 +223,7 @@ int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) } SPA_EXPORT -const char *pa_proplist_iterate(pa_proplist *p, void **state) +const char *pa_proplist_iterate(PA_CONST pa_proplist *p, void **state) { spa_assert(p); spa_assert(state); @@ -231,14 +231,14 @@ const char *pa_proplist_iterate(pa_proplist *p, void **state) } SPA_EXPORT -char *pa_proplist_to_string(pa_proplist *p) +char *pa_proplist_to_string(PA_CONST pa_proplist *p) { spa_assert(p); return pa_proplist_to_string_sep(p, ","); } SPA_EXPORT -char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) +char *pa_proplist_to_string_sep(PA_CONST pa_proplist *p, const char *sep) { const char *key; void *state = NULL; @@ -293,7 +293,7 @@ pa_proplist *pa_proplist_from_string(const char *str) } SPA_EXPORT -int pa_proplist_contains(pa_proplist *p, const char *key) +int pa_proplist_contains(PA_CONST pa_proplist *p, const char *key) { spa_assert(p); spa_assert(key); @@ -330,21 +330,21 @@ pa_proplist* pa_proplist_copy(const pa_proplist *p) } SPA_EXPORT -unsigned pa_proplist_size(pa_proplist *p) +unsigned pa_proplist_size(PA_CONST pa_proplist *p) { spa_assert(p); return p->props->dict.n_items; } SPA_EXPORT -int pa_proplist_isempty(pa_proplist *p) +int pa_proplist_isempty(PA_CONST pa_proplist *p) { spa_assert(p); return p->props->dict.n_items == 0 ? 1 : 0; } SPA_EXPORT -int pa_proplist_equal(pa_proplist *a, pa_proplist *b) +int pa_proplist_equal(PA_CONST pa_proplist *a, PA_CONST pa_proplist *b) { uint32_t i; diff --git a/src/scache.c b/src/scache.c index 2f9ace166..65f725f2d 100644 --- a/src/scache.c +++ b/src/scache.c @@ -54,7 +54,7 @@ pa_operation* pa_context_play_sample(pa_context *c, const char *name, const char SPA_EXPORT pa_operation* pa_context_play_sample_with_proplist(pa_context *c, const char *name, - const char *dev, pa_volume_t volume, pa_proplist *proplist, + const char *dev, pa_volume_t volume, PA_CONST pa_proplist *proplist, pa_context_play_sample_cb_t cb, void *userdata) { pw_log_warn("Not Implemented"); diff --git a/src/stream.c b/src/stream.c index 3b4a9f9c5..76dc272ff 100644 --- a/src/stream.c +++ b/src/stream.c @@ -722,7 +722,7 @@ pa_stream *pa_stream_ref(pa_stream *s) } SPA_EXPORT -pa_stream_state_t pa_stream_get_state(pa_stream *s) +pa_stream_state_t pa_stream_get_state(PA_CONST pa_stream *s) { spa_assert(s); spa_assert(s->refcount >= 1); @@ -730,7 +730,7 @@ pa_stream_state_t pa_stream_get_state(pa_stream *s) } SPA_EXPORT -pa_context* pa_stream_get_context(pa_stream *s) +pa_context* pa_stream_get_context(PA_CONST pa_stream *s) { spa_assert(s); spa_assert(s->refcount >= 1); @@ -738,7 +738,7 @@ pa_context* pa_stream_get_context(pa_stream *s) } SPA_EXPORT -uint32_t pa_stream_get_index(pa_stream *s) +uint32_t pa_stream_get_index(PA_CONST pa_stream *s) { uint32_t idx; @@ -773,7 +773,7 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { SPA_EXPORT -uint32_t pa_stream_get_device_index(pa_stream *s) +uint32_t pa_stream_get_device_index(PA_CONST pa_stream *s) { spa_assert(s); spa_assert(s->refcount >= 1); @@ -790,7 +790,7 @@ uint32_t pa_stream_get_device_index(pa_stream *s) } SPA_EXPORT -const char *pa_stream_get_device_name(pa_stream *s) +const char *pa_stream_get_device_name(PA_CONST pa_stream *s) { spa_assert(s); spa_assert(s->refcount >= 1); @@ -806,7 +806,7 @@ const char *pa_stream_get_device_name(pa_stream *s) } SPA_EXPORT -int pa_stream_is_suspended(pa_stream *s) +int pa_stream_is_suspended(PA_CONST pa_stream *s) { spa_assert(s); spa_assert(s->refcount >= 1); @@ -818,7 +818,7 @@ int pa_stream_is_suspended(pa_stream *s) } SPA_EXPORT -int pa_stream_is_corked(pa_stream *s) +int pa_stream_is_corked(PA_CONST pa_stream *s) { spa_assert(s); spa_assert(s->refcount >= 1); @@ -1274,7 +1274,7 @@ int pa_stream_drop(pa_stream *s) } SPA_EXPORT -size_t pa_stream_writable_size(pa_stream *s) +size_t pa_stream_writable_size(PA_CONST pa_stream *s) { spa_assert(s); spa_assert(s->refcount >= 1); @@ -1289,7 +1289,7 @@ size_t pa_stream_writable_size(pa_stream *s) } SPA_EXPORT -size_t pa_stream_readable_size(pa_stream *s) +size_t pa_stream_readable_size(PA_CONST pa_stream *s) { spa_assert(s); spa_assert(s->refcount >= 1); @@ -1427,7 +1427,7 @@ void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, voi } SPA_EXPORT -int64_t pa_stream_get_underflow_index(pa_stream *s) +int64_t pa_stream_get_underflow_index(PA_CONST pa_stream *s) { pw_log_warn("Not Implemented"); return 0; @@ -1767,7 +1767,7 @@ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) } SPA_EXPORT -const pa_format_info* pa_stream_get_format_info(pa_stream *s) +const pa_format_info* pa_stream_get_format_info(PA_CONST pa_stream *s) { spa_assert(s); spa_assert(s->refcount >= 1); @@ -1896,7 +1896,7 @@ int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) } SPA_EXPORT -uint32_t pa_stream_get_monitor_stream(pa_stream *s) +uint32_t pa_stream_get_monitor_stream(PA_CONST pa_stream *s) { spa_assert(s); spa_assert(s->refcount >= 1); diff --git a/src/thread-mainloop.c b/src/thread-mainloop.c index 0b9e248f1..266625f8f 100644 --- a/src/thread-mainloop.c +++ b/src/thread-mainloop.c @@ -108,7 +108,7 @@ void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) } SPA_EXPORT -int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) +int pa_threaded_mainloop_get_retval(PA_CONST pa_threaded_mainloop *m) { return pa_mainloop_get_retval(m->loop); } diff --git a/src/volume.c b/src/volume.c index 6469ff5eb..35527ba75 100644 --- a/src/volume.c +++ b/src/volume.c @@ -953,7 +953,7 @@ pa_cvolume* pa_cvolume_set_position( SPA_EXPORT pa_volume_t pa_cvolume_get_position( - pa_cvolume *cv, + PA_CONST pa_cvolume *cv, const pa_channel_map *map, pa_channel_position_t t) { From 694ae6dd84dc4f98add11474607d8e211bd4ae4a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 2 Oct 2019 17:54:21 +0200 Subject: [PATCH 114/116] update for flags --- src/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream.c b/src/stream.c index 76dc272ff..71df4c296 100644 --- a/src/stream.c +++ b/src/stream.c @@ -892,7 +892,7 @@ static int create_stream(pa_stream_direction_t direction, fl = PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS; - s->corked = SPA_FLAG_CHECK(flags, PA_STREAM_START_CORKED); + s->corked = SPA_FLAG_IS_SET(flags, PA_STREAM_START_CORKED); if (s->corked) fl |= PW_STREAM_FLAG_INACTIVE; From 772ec76cd90b4ac089e6903d4a078088cd02e14d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 25 Oct 2019 14:58:40 +0200 Subject: [PATCH 115/116] fix includes --- src/mainloop-glib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mainloop-glib.c b/src/mainloop-glib.c index acf1989dd..7addd7d3d 100644 --- a/src/mainloop-glib.c +++ b/src/mainloop-glib.c @@ -19,6 +19,8 @@ #include +#include + #include #include From 0c106e677752438fb1446397bac10e578c79b82c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 31 Oct 2019 17:55:25 +0100 Subject: [PATCH 116/116] context: sort objects by priority --- src/context.c | 27 ++++++++++++++++++++++----- src/internal.h | 1 + 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/context.c b/src/context.c index 6f9fa0483..969803285 100644 --- a/src/context.c +++ b/src/context.c @@ -552,11 +552,14 @@ static int set_mask(pa_context *c, struct global *g) case PW_TYPE_INTERFACE_Node: if (g->props == NULL) return 0; + + if ((str = pw_properties_get(g->props, PW_KEY_PRIORITY_MASTER)) != NULL) + g->priority_master = pw_properties_parse_int(str); + if ((str = pw_properties_get(g->props, PW_KEY_MEDIA_CLASS)) == NULL) { pw_log_debug("node %d without "PW_KEY_MEDIA_CLASS, g->id); return 0; } - pw_log_debug("node %d "PW_KEY_MEDIA_CLASS" '%s'", g->id, str); if (strcmp(str, "Audio/Sink") == 0) { pw_log_debug("found sink %d", g->id); @@ -664,12 +667,26 @@ static int set_mask(pa_context *c, struct global *g) return 1; } +static inline void insert_global(pa_context *c, struct global *global) +{ + struct global *g, *t; + + spa_list_for_each_safe(g, t, &c->globals, link) { + if (g->priority_master < global->priority_master) { + g = g->link.prev; + break; + } + } + spa_list_prepend(&g->link, &global->link); +} + static void registry_event_global(void *data, uint32_t id, uint32_t permissions, uint32_t type, uint32_t version, const struct spa_dict *props) { pa_context *c = data; struct global *g; + int res; g = calloc(1, sizeof(struct global)); pw_log_debug("context %p: global %d %u %p", c, id, type, g); @@ -678,12 +695,12 @@ static void registry_event_global(void *data, uint32_t id, g->type = type; g->init = true; g->props = props ? pw_properties_new_dict(props) : NULL; - spa_list_append(&c->globals, &g->link); - if (set_mask(c, g) != 1) { + res = set_mask(c, g); + insert_global(c, g); + + if (res != 1) global_free(c, g); - return; - } } static void registry_event_global_remove(void *object, uint32_t id) diff --git a/src/internal.h b/src/internal.h index 338654e51..e407de66a 100644 --- a/src/internal.h +++ b/src/internal.h @@ -231,6 +231,7 @@ struct global { pa_subscription_mask_t mask; pa_subscription_event_type_t event; + int priority_master; int pending_seq; int init:1; int subscribed:1;