From faa5984fcefa5e02cabb5a31b1912365350efaa3 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 1 Jun 2018 11:28:31 +0200 Subject: [PATCH] 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; + } +}