From: Andre Noll Date: Mon, 12 Apr 2010 16:12:31 +0000 (+0200) Subject: Merge branch 'maint' X-Git-Tag: v0.4.2~12 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=b0fe6d882dd35b01360a66e130d6714aff7b6a0d;hp=30ef430cdbf14c326fe72918fc48153fa005a41c Merge branch 'maint' --- diff --git a/.gitignore b/.gitignore index 0000ba41..d3a1fc5e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -*.[oa] +objects foo* bar* para_* @@ -30,4 +30,4 @@ web_sync confdefs.h conftest conftest.c - +GIT-VERSION-FILE diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 00000000..cf9b6b99 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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 +^L + 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. + + + + Copyright (C) + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/CREDITS b/CREDITS index e5f12ac4..15313f96 100644 --- a/CREDITS +++ b/CREDITS @@ -14,6 +14,8 @@ Karl Bartel (SFont) Gerd Becker (MacOs testing) +Fabrice Bellard (FFmpeg) + Lorenzo Bettini (gengetopt) Ricardo Cerqueira (mp3info) @@ -34,12 +36,16 @@ Robert Leslie (libmad) Ian McDonald (dccp example code) +Loren Merritt (FFmpeg) + Simon Morlat (ortp) Christof Müller (bug reports) M. Hari Nezumi (AudioCompress) +Michael Niedermayer (FFmpeg) + Manuel Odendahl (poc) Guillaume Outters (mosx-mpg123) diff --git a/Doxyfile b/Doxyfile index 43209a52..6b5cb4af 100644 --- a/Doxyfile +++ b/Doxyfile @@ -255,7 +255,7 @@ EXTRACT_STATIC = NO # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. -EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in @@ -1063,7 +1063,8 @@ PREDEFINED = HAVE_MAD \ HAVE_FAAD \ HAVE_OGGVORBIS \ __GNUC__=4 \ - __GNUC_MINOR__=4 + __GNUC_MINOR__=4 \ + HAVE_UCRED # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/FEATURES b/FEATURES index d054dfd1..dc7f8537 100644 --- a/FEATURES +++ b/FEATURES @@ -5,7 +5,7 @@ Features * Runs on Linux, Mac OS, FreeBSD, NetBSD, Solaris and probably other Unixes - * Mp3, ogg vorbis, aac(m4a) support + * Mp3, ogg vorbis, aac (m4a) and wma support * Local or remote http, dccp, and udp network audio streaming * IPv6 support * Forward error correction allows receivers to recover from packet losses diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN new file mode 100755 index 00000000..a64d7961 --- /dev/null +++ b/GIT-VERSION-GEN @@ -0,0 +1,45 @@ +#!/bin/sh + +if test $# -ne 1; then + echo >&2 "usage: $0 " + exit 1 +fi + +GVF="$1" +DEF_VER="unnamed_version" + +LF=' +' + +# First see if there is a version file (included in release tarballs), +# then try git-describe, then default. +if test -f VERSION +then + VN=$(cat VERSION) || VN="$DEF_VER" +elif test -d .git -o -f .git && + VN=$(git describe --abbrev=4 HEAD 2>/dev/null) && + case "$VN" in + *$LF*) (exit 1) ;; + v[0-9]*) + git update-index -q --refresh + test -z "$(git diff-index --name-only HEAD --)" || + VN="$VN-dirty" ;; + esac +then + VN=$(echo "$VN" | sed -e 's/-/./g'); +else + VN="$DEF_VER" +fi + +VN=$(expr "$VN" : v*'\(.*\)') + +if test -r $GVF +then + VC=$(sed -e 's/^GIT_VERSION = //' <$GVF) +else + VC=unset +fi +test "$VN" = "$VC" || { + echo >&2 "GIT_VERSION = $VN" + echo "GIT_VERSION = $VN" >$GVF +} diff --git a/INSTALL b/INSTALL index 09a2616f..aba5c35f 100644 --- a/INSTALL +++ b/INSTALL @@ -17,16 +17,17 @@ optional. The configure script will detect what is installed on your system and will only try to build those executables that can be built with your setup. -Note that no special library (not even the mp3 decoding library libmad) -is needed for para_server if you only want to stream mp3 files. -Also, it's fine to use para_server on a box without sound card as -para_server only sends the audio stream to connected clients. +Note that no special library (not even the mp3 decoding library +libmad) is needed for para_server if you only want to stream mp3 or +wma files. Also, it's fine to use para_server on a box without sound +card as para_server only sends the audio stream to connected clients. ------------------------- Install server and client ------------------------- -Install the package on all machines, you'd like this software to run on: +Install the paraslash package on all machines, you'd like this software +to run on: (./configure && make) > /dev/null @@ -43,12 +44,14 @@ successfully, execute as root, make install ----------------------------------- -Setup user list and create rsa keys +Setup user list and create RSA keys ----------------------------------- -If you already have your rsa keys, skip this step. If you are new -to paraslash, you have to generate an rsa key pair for each user you -want to allow to connect. You need at least one user. +Note that the RSA keys for paraslash 0.3.x will not work for version +0.4.x as the new version requires stronger (2048 bit) keys. If you +already have your 2048 bit keys, skip this step. If you are new to +paraslash, you have to generate a key pair for each user you want to +allow to connect. You need at least one user. Let's assume that you'd like to run the server on host server_host as user foo, and that you want to connect from client_host as user bar. @@ -70,7 +73,7 @@ with the commands key=~/.paraslash/key.$LOGNAME mkdir -p ~/.paraslash - (umask 077 && openssl genrsa -out $key) + (umask 077 && openssl genrsa -out $key 2048) Next, extract its public part: @@ -121,11 +124,11 @@ Create the database para_client init -This creates some empty tables under ~/.paraslash/afs_database. -You normally don't need to look at these tables, but it's good -to know that you can start from scratch with +This creates some empty tables under ~/.paraslash/afs_database-0.4. +You normally don't need to look at these tables, but it's good to +know that you can start from scratch with - rm -rf ~/.paraslash/afs_database + rm -rf ~/.paraslash/afs_database-0.4 in case something went wrong. @@ -149,7 +152,7 @@ Start streaming manually ------------------------ para_client play - para_client stat 2 + para_client -- stat -n=2 This starts streaming and dumps some information about the current audio file to stdout. @@ -165,7 +168,7 @@ Paraslash comes with its own receiving and playing software, which will be described next. Try the following on client_host (assuming Linux/ALSA and an mp3 stream): - para_recv -l info -r 'http -i server_host' > file.mp3 + para_recv -r 'http -i server_host' > file.mp3 # (interrupt with CTRL+C after a few seconds) ls -l file.mp3 # should not be empty para_filter -f mp3dec -f wav < file.mp3 > file.wav diff --git a/Makefile.in b/Makefile.in index d04d0ea3..cbca97a0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -8,12 +8,13 @@ MANDIR := @datarootdir@/man/man1 PACKAGE_VERSION := @PACKAGE_VERSION@ PACKAGE_STRING := @PACKAGE_STRING@ install_sh := @install_sh@ +cmdline_dir := @cmdline_dir@ build_date := $(shell date) uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS") uname_rs := $(shell uname -rs) cc_version := $(shell $(CC) --version | head -n 1) -codename := imaginary radiation +codename := associative expansion DEBUG_CPPFLAGS += -Wno-sign-compare -g -Wunused -Wundef -W DEBUG_CPPFLAGS += -Wredundant-decls @@ -55,151 +56,237 @@ CPPFLAGS += -DMAIN_INPUT_FILE_IS_$(*F) CPPFLAGS += @SSL_CPPFLAGS@ CPPFLAGS += @ncurses_cppflags@ CPPFLAGS += @arch_cppflags@ +CPPFLAGS += -I/usr/local/include +CPPFLAGS += -I$(cmdline_dir) +CPPFLAGS += @osl_cppflags@ +CPPFLAGS += -DGIT_VERSION='"$(GIT_VERSION)"' -BINARIES = para_server para_client para_audioc para_recv \ - para_filter para_write para_fsck para_afh @extra_binaries@ -man_binaries := $(BINARIES) -man_pages := $(patsubst %, man/man1/%.1, $(man_binaries)) -man_pages_in := $(patsubst %, web/%.man.in.html, $(man_binaries)) +man_pages := $(patsubst %, man/man1/%.1, @executables@) +man_pages_in := $(patsubst %, web/%.man.in.html, @executables@) ggo_dir := ggo +object_dir := objects +man_dir := man/man1 -m4_ggos := afh audioc audiod client filter fsck gui recv server write +m4_ggos := afh audioc audiod client filter gui recv server write all_ggos := $(m4_ggos) dccp_recv oggdec_filter alsa_write oss_write fade http_recv \ osx_write udp_recv amp_filter compress_filter file_write \ - grab_client mp3dec_filter -ggo_generated := $(addsuffix .cmdline.c, $(all_ggos)) $(addsuffix .cmdline.h, $(all_ggos)) \ - $(addsuffix .ggo, $(addprefix $(ggo_dir)/,$(m4_ggos))) + mp3dec_filter prebuffer_filter +ggo_generated := $(addsuffix .ggo, $(addprefix $(ggo_dir)/,$(m4_ggos))) +cmdline_generated := $(addprefix $(cmdline_dir)/,$(addsuffix .cmdline.c, $(all_ggos)) \ + $(addsuffix .cmdline.h, $(all_ggos))) autocrap := config.h.in configure tarball_pfx := @PACKAGE_TARNAME@-$(PACKAGE_VERSION) -tarball_delete = web versions pics .changelog_before_cvs .changelog_cvs .gitignore -tarball_delete := $(patsubst %,$(tarball_pfx)/%,$(tarball_delete)) -tarball_add := $(ggo_generated) $(autocrap) +tarball_delete := $(addprefix $(tarball_pfx)/,\ + web versions .changelog_before_cvs .changelog_cvs .gitignore\ + $(ggo_dir) skencil) tarball := @PACKAGE_TARNAME@-$(PACKAGE_VERSION).tar.bz2 -.PHONY: clean distclean maintainer-clean install man tarball -all: $(BINARIES) $(man_pages) +# To put more focus on warnings, be less verbose as default +# Use 'make V=1' to see the full commands +ifdef V + ifeq ("$(origin V)", "command line") + BUILD_VERBOSE = $(V) + endif +endif +ifndef BUILD_VERBOSE + BUILD_VERBOSE = 0 +endif +ifeq ($(BUILD_VERBOSE),1) + Q = +else + Q = @ +endif + +.PHONY: all clean distclean maintainer-clean install man tarball\ + .FORCE-GIT-VERSION-FILE +all: @executables@ $(man_pages) man: $(man_pages) tarball: $(tarball) -*.o: para.h config.h gcc-compat.h +GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE + @./GIT-VERSION-GEN GIT-VERSION-FILE +-include GIT-VERSION-FILE +@executables@: GIT-VERSION-FILE -include Makefile.deps -include $(ggo_dir)/makefile - -%_command_list.c %_command_list.h: %.cmd - ./command_util.sh c < $< >$@ - ./command_util.sh h < $< >$(@:%.c=%.h) +-include $(ggo_dir)/makefile +%_command_list.c: %.cmd + @[ -z "$(Q)" ] || echo 'GEN $@' + $(Q) ./command_util.sh c < $< >$@ +%_command_list.h: %.cmd + @[ -z "$(Q)" ] || echo 'GEN $@' + $(Q) ./command_util.sh h < $< >$@ %_command_list.man: %.cmd - ./command_util.sh man < $< > $@ - -server_command_lists = server_command_list.man afs_command_list.man -man/man1/para_server.1: para_server $(server_command_lists) - mkdir -p man/man1 - opts="-h --detailed-help -N `for i in $(server_command_lists); do printf "%s\n" "-i $$i"; done`"; \ + @[ -z "$(Q)" ] || echo 'GEN $@' + $(Q) ./command_util.sh man < $< > $@ + +server_command_lists_ch = server_command_list.c afs_command_list.c \ + server_command_list.h afs_command_list.h +server_command_lists_man = server_command_list.man afs_command_list.man +man/man1/para_server.1: para_server $(server_command_lists_man) | $(man_dir) + @[ -z "$(Q)" ] || echo 'HELP2MAN $<' + $(Q) opts="-h --detailed-help -N `for i in $(server_command_lists_man); do printf "%s\n" "-i $$i"; done`"; \ help2man $$opts ./para_server > $@ -man/man1/para_audiod.1: para_audiod audiod_command_list.man - mkdir -p man/man1 - help2man -h --detailed-help -N -i audiod_command_list.man ./para_audiod > $@ +man/man1/para_audiod.1: para_audiod audiod_command_list.man | $(man_dir) + @[ -z "$(Q)" ] || echo 'HELP2MAN $<' + $(Q) help2man -h --detailed-help -N -i audiod_command_list.man ./para_audiod > $@ -man/man1/%.1: % - mkdir -p man/man1 - help2man -h --detailed-help -N ./$< > $@ +man/man1/%.1: % | $(man_dir) + @[ -z "$(Q)" ] || echo 'HELP2MAN $<' + $(Q) help2man -h --detailed-help -N ./$< > $@ man/html/%.html: man/man1/%.1 - mkdir -p man/html - man2html $< > $@ + @[ -z "$(Q)" ] || echo 'MAN2HTML $<' + $(Q) mkdir -p man/html + $(Q) man2html $< > $@ web/%.man.in.html: man/man1/%.1 - man2html $< | sed -e '/^<\/BODY>/,$$d' -e '1,/<\/HEAD>/d' > $@ - - -oggdec_filter.o: oggdec_filter.c - $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @oggvorbis_cppflags@ $< -ogg_afh.o: ogg_afh.c - $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @oggvorbis_cppflags@ $< - -mp3dec_filter.o: mp3dec_filter.c - $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @mad_cppflags@ $< - -aacdec_filter.o: aacdec_filter.c - $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $< - -aac_common.o: aac_common.c - $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $< - -aac_afh.o: aac_afh.c - $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $< - -%.cmdline.o: %.cmdline.c - $(CC) -c $(CPPFLAGS) $< + @[ -z "$(Q)" ] || echo 'MAN2HTML $<' + $(Q) mkdir -p man/html + $(Q) man2html $< | sed -e '/^<\/BODY>/,$$d' -e '1,/<\/HEAD>/d' > $@ + +$(object_dir): + mkdir -p $@ +$(man_dir): + mkdir -p $@ + +$(object_dir)/oggdec_filter.o: oggdec_filter.c | $(object_dir) + @[ -z "$(Q)" ] || echo 'CC $<' + $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @oggvorbis_cppflags@ $< + +$(object_dir)/ogg_afh.o: ogg_afh.c | $(object_dir) + @[ -z "$(Q)" ] || echo 'CC $<' + $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @oggvorbis_cppflags@ $< + +$(object_dir)/mp3dec_filter.o: mp3dec_filter.c | $(object_dir) + @[ -z "$(Q)" ] || echo 'CC $<' + $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @mad_cppflags@ $< + +$(object_dir)/aacdec_filter.o: aacdec_filter.c | $(object_dir) + @[ -z "$(Q)" ] || echo 'CC $<' + $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $< + +$(object_dir)/aac_common.o: aac_common.c | $(object_dir) + @[ -z "$(Q)" ] || echo 'CC $<' + $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $< + +$(object_dir)/aac_afh.o: aac_afh.c | $(object_dir) + @[ -z "$(Q)" ] || echo 'CC $<' + $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $< + +$(object_dir)/%.cmdline.o: $(cmdline_dir)/%.cmdline.c $(cmdline_dir)/%.cmdline.h | $(object_dir) + @[ -z "$(Q)" ] || echo 'CC $<' + $(Q) $(CC) -c $(CPPFLAGS) -o $@ $< + +$(object_dir)/%.o: %.c | $(object_dir) + @[ -z "$(Q)" ] || echo 'CC $<' + $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) $< + +$(object_dir)/%.cmdline.d: $(cmdline_dir)/%.cmdline.c | $(object_dir) + @[ -z "$(Q)" ] || echo 'DEP $<' + $(Q) ./depend.sh $(object_dir) $(cmdline_dir) $(CPPFLAGS) $< > $@ + +$(object_dir)/%.d: %.c | $(object_dir) + @[ -z "$(Q)" ] || echo 'DEP $<' + $(Q) ./depend.sh $(object_dir) $(cmdline_dir) $(CPPFLAGS) $< > $@ + +recv_objs := $(addprefix $(object_dir)/, @recv_objs@) +filter_objs := $(addprefix $(object_dir)/, @filter_objs@) +client_objs := $(addprefix $(object_dir)/, @client_objs@) +gui_objs := $(addprefix $(object_dir)/, @gui_objs@) +audiod_objs := $(addprefix $(object_dir)/, @audiod_objs@) +audioc_objs := $(addprefix $(object_dir)/, @audioc_objs@) +fade_objs := $(addprefix $(object_dir)/, @fade_objs@) +server_objs := $(addprefix $(object_dir)/, @server_objs@) +write_objs := $(addprefix $(object_dir)/, @write_objs@) +afh_objs := $(addprefix $(object_dir)/, @afh_objs@) + +all_objs := $(recv_objs) $(filter_objs) $(client_objs) $(gui_objs) \ + $(audiod_objs) $(audioc_objs) $(fade_objs) $(server_objs) \ + $(write_objs) $(afh_objs) + +ifeq ($(findstring clean, $(MAKECMDGOALS)),) +-include $(all_objs:.o=.d) +endif -%.o: %.c - $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) $< +para_recv: $(recv_objs) + @[ -z "$(Q)" ] || echo 'LD $@' + $(Q) $(CC) $(LDFLAGS) $(recv_objs) -o $@ @recv_ldflags@ -para_recv: @recv_objs@ - $(CC) $(LDFLAGS) @recv_objs@ -o $@ @recv_ldflags@ +para_filter: $(filter_objs) + @[ -z "$(Q)" ] || echo 'LD $@' + $(Q) $(CC) $(LDFLAGS) $(filter_objs) -o $@ @filter_ldflags@ -para_filter: @filter_objs@ - $(CC) $(LDFLAGS) @filter_objs@ -o $@ @filter_ldflags@ +para_client: $(client_objs) + @[ -z "$(Q)" ] || echo 'LD $@' + $(Q) $(CC) $(LDFLAGS) -o $@ $(client_objs) @client_ldflags@ -para_client: @client_objs@ - $(CC) $(LDFLAGS) -o $@ @client_objs@ @client_ldflags@ +para_gui: $(gui_objs) + @[ -z "$(Q)" ] || echo 'LD $@' + $(Q) $(CC) $(LDFLAGS) -o $@ $(gui_objs) -lncurses -para_gui: @gui_objs@ - $(CC) $(LDFLAGS) -o $@ @gui_objs@ -lncurses +para_audiod: audiod_command_list.c audiod_command_list.h $(audiod_objs) + @[ -z "$(Q)" ] || echo 'LD $@' + $(Q) $(CC) $(LDFLAGS) -o $@ $(audiod_objs) @audiod_ldflags@ -para_audiod: @audiod_objs@ - $(CC) $(LDFLAGS) -o $@ @audiod_objs@ @audiod_ldflags@ +para_audioc: $(audioc_objs) + @[ -z "$(Q)" ] || echo 'LD $@' + $(Q) $(CC) $(LDFLAGS) -o $@ $(audioc_objs) @audioc_ldflags@ -para_audioc: @audioc_objs@ - $(CC) $(LDFLAGS) -o $@ @audioc_objs@ @audioc_ldflags@ +para_fade: $(fade_objs) + @[ -z "$(Q)" ] || echo 'LD $@' + $(Q) $(CC) $(LDFLAGS) -o $@ $(fade_objs) @fade_ldflags@ -para_fade: @fade_objs@ - $(CC) $(LDFLAGS) -o $@ @fade_objs@ @fade_ldflags@ +para_server: $(server_command_lists_ch) $(server_objs) + @[ -z "$(Q)" ] || echo 'LD $@' + $(Q) $(CC) $(LDFLAGS) -o $@ $(server_objs) @server_ldflags@ -para_server: @server_objs@ - $(CC) $(LDFLAGS) -o $@ @server_objs@ @server_ldflags@ +para_write: $(write_objs) + @[ -z "$(Q)" ] || echo 'LD $@' + $(Q) $(CC) $(LDFLAGS) -o $@ $(write_objs) @write_ldflags@ -para_fsck: @fsck_objs@ - $(CC) $(LDFLAGS) -o $@ @fsck_objs@ @fsck_ldflags@ +para_afh: $(afh_objs) + @[ -z "$(Q)" ] || echo 'LD $@' + $(Q) $(CC) $(LDFLAGS) -o $@ $(afh_objs) @afh_ldflags@ -para_write: @write_objs@ - $(CC) $(LDFLAGS) -o $@ @write_objs@ @write_ldflags@ +clean: + @[ -z "$(Q)" ] || echo 'CLEAN' + $(Q) rm -f @executables@ $(object_dir)/*.o -para_afh: @afh_objs@ - $(CC) $(LDFLAGS) -o $@ @afh_objs@ @afh_ldflags@ +clean2: clean + @[ -z "$(Q)" ] || echo 'CLEAN2' + $(Q) rm -rf man $(object_dir) + $(Q) rm -f *_command_list.* -clean: - rm -f *.o $(BINARIES) - rm -rf man -distclean: clean - rm -f Makefile autoscan.log config.status config.log && \ - rm -rf web/sync/* autom4te.cache aclocal.m4 - rm -f GPATH GRTAGS GSYMS GTAGS +distclean: clean2 + @[ -z "$(Q)" ] || echo 'DISTCLEAN' + $(Q) rm -f Makefile autoscan.log config.status config.log + $(Q) rm -rf autom4te.cache aclocal.m4 + $(Q) rm -f GPATH GRTAGS GSYMS GTAGS maintainer-clean: distclean rm -f $(ggo_generated) *.tar.bz2 \ config.h configure \ config.h.in skencil/*.pdf skencil/*.ps - rm -f *_command_list.* *.man man/man1/* - rm -rf web_sync + rm -rf web_sync $(cmdline_dir) install: all man mkdir -p $(BINDIR) $(MANDIR) - $(install_sh) -s -m 755 $(BINARIES) $(BINDIR) + $(install_sh) -s -m 755 @executables@ $(BINDIR) $(install_sh) -m 644 $(man_pages) $(MANDIR) mkdir -p $(VARDIR) >/dev/null 2>&1 || true # not fatal, so don't complain -@PACKAGE_TARNAME@-$(PACKAGE_VERSION).tar.bz2: $(tarball_add) +$(tarball): $(cmdline_generated) rm -rf $(tarball_pfx).tar.bz2 $(tarball_pfx) git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \ | tar --delete $(tarball_delete) > $(tarball_pfx).tar - mkdir $(tarball_pfx) - cp -r $(tarball_add) $(tarball_pfx) + mkdir -p $(tarball_pfx)/$(cmdline_dir) + echo $(GIT_VERSION) > $(tarball_pfx)/VERSION + cp -r $(autocrap) $(tarball_pfx) + cp -r $(cmdline_generated) $(tarball_pfx)/$(cmdline_dir) tar rf $(tarball_pfx).tar $(tarball_pfx)/* rm -rf $(tarball_pfx) bzip2 -9 $(tarball_pfx).tar diff --git a/NEWS b/NEWS index 66ce1f3f..264d2628 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,34 @@ NEWS ==== +----------------------------------------------- +0.4.2 (to be announced) "associative expansion" +----------------------------------------------- + + - The new buffer tree API. + - DCCP: Support for CCID negotiation (Gerrit Renker). + - The --bufsize option for mp3dec is gone as it no longer makes sense + for the new buffer tree API. + - Fix audible buffer underruns for wma streams. + - The alsa writer no longer prints meaningless underrun durations. + - audiod: Defaults work also for udp streams. If no filter is + given for an audio format that is received via upd, fecdec is + automatically added as the first filter (along with the decoder). + +--------------------------------------- +0.4.1 (2009-12-22) "concurrent horizon" +--------------------------------------- + +Support for another audio format, minor feature enhancements and lots of bug +fixes. All fixes that have been accumulated in the maint branch (in particular +those mentionened in the 0.3.6 release notes) appear in this release as well. + + - wma support. + - new afh option: --human to activate human-readable output. + - new server/audiod option: --log-timing to print timing information. + - build system improvements. + - source code documentation updates. + ------------------------------------- 0.3.6 (2009-12-07) "cubic continuity" ------------------------------------- @@ -19,6 +47,28 @@ another 0.3.x release. No new features. - http/dccp: Do not send the audio file header twice. - FEC: Timing improvements. +---------------------------------------------- +0.4.0 (2009-11-10) "simultaneous independence" +---------------------------------------------- + +Two significant changes which require the new version number: The +improved authentication dialog and the fact that the database code +has been moved to a library, libosl. To use the new version, you have +to generate new RSA keys, see INSTALL for details. A shell script is +provided for conversion of the 0.3 database to the new 0.4 format. + + - stronger crypto for client authentication + - the database code has been moved to a library + - improved status item handling + - cleanup of the build system + - The "-V" option now also prints the git version + - the new parser-friendly listing mode for the ls and stat commands + - mandatory rc4 encryption + - major audio format handler cleanups + - (id3,...) tags are no longer stored as a combined string in the database + - new mood methods: artist_matches, title_matches, comment_matches, + album_matches, year_maches, year. + -------------------------------------------- 0.3.5 (2009-09-21) "symplectic separability" -------------------------------------------- diff --git a/README b/README index 6cf34f27..883eeee1 100644 --- a/README +++ b/README @@ -12,7 +12,7 @@ It contains the following programs: para_server ----------- -para_server streams binary audio data (mp3/oggvorbis/m4a files) +para_server streams binary audio data (mp3/oggvorbis/m4a/wma files) over local and/or remote networks. It listens on a tcp port and accepts commands such as play, stop, pause, next from authenticated clients. However, there are many more commands. @@ -56,11 +56,6 @@ Its features include * rename detection. If files are moved or renamed, afs will recognize them despite of this change. -Despite of all these features, paraslash is lightweight. The -stripped binary of para_server with all its features compiled in -mp3/ogg/aac support, http/dccp/udp support) is about 160K on i386 -under Linux. para_audiod (see below) is even smaller. - ----------- para_client ----------- @@ -88,9 +83,10 @@ para_filter A filter program that converts from stdin and writes to stdout. -para_filter combines several decoders (mp3, oggvorbis, aac) and a -volume normalizer. New filters can be added easily. It is possible -to "chain" any number of filters, like UNIX pipes. +para_filter combines several decoders (mp3, oggvorbis, aac, wma), +a volume normalizer and a cpuple of other filters. New filters can +be added easily. It is possible to "chain" any number of filters, +like UNIX pipes. para_filter does not depend on other parts of paraslash, so it can be used as a stand-alone command line tool for audio decoding and @@ -107,7 +103,7 @@ the audio file in complete chunks 'just in time'. This allows third-party streaming software that is unaware of the particular audio format to send complete frames in real -time. Currently, mp3, ogg vorbis and aac are supported. +time. Currently, mp3, ogg vorbis, aac and wma are supported. ---------- para_write diff --git a/README.afs b/README.afs index ca01df6a..51625667 100644 --- a/README.afs +++ b/README.afs @@ -78,10 +78,10 @@ A mood consists of a unique name and its *mood definition*, which is a set of *mood lines* containing expressions in terms of attributes and other data contained in the database. -A mood defines a subset of audio files called the *admissible audio -files* for that mood. At any time, at most one mood can be *active* -which means that para_server is going to select only files from that -subset of admissible files. +A mood defines a subset of audio files called the *admissible audio files* +for that mood. At any time, at most one mood can be *active* which +means that para_server is going to select only files from that subset +of admissible files. So in order to create a mood definition one has to write a set of mood lines. Mood lines come in three flavours: Accept lines, deny @@ -156,20 +156,43 @@ List of mood_methods Takes no arguments and matches an audio file if and only if no attributes are set. - played_rarely - -Takes no arguments and matches all audio files where the number of -times this audio file was selected is below the average. - - is_set attribute_name + is_set Takes the name of an attribute and matches iff that attribute is set. - path_matches pattern + path_matches Takes a filename pattern and matches iff the path of the audio file matches the pattern. + artist_matches + album_matches + title_matches + comment_matches + +Takes an extended regular expression and matches iff the text of the +corresponding tag of the audio file matches the pattern. If the tag +is not set, the empty string is matched against the pattern. + + year ~ + bitrate ~ + frequency ~ + channels ~ + num_played ~ + +Takes a comparator ~ of the set {<, =, <=, >, >=, !=} and a number +. Matches an audio file iff the condition ~ is +satisfied where val is the corresponding value of the audio file +(value of the year tag, bitrate in kbit/s, frequency in Hz, channel +count, play count). + +The year tag is special as its value is undefined if the audio file +has no year tag or the content of the year tag is not a number. Such +audio files never match. Another difference is the special treatment +if the year tag is a two-digit number. In this case either 1900 or +2000 are added to the tag value depending on whether the number is +greater than 2000 plus the current year. + ---------- Mood usage @@ -230,27 +253,27 @@ debugging info. Almost all paraslash executables have a brief online help which is displayed by using the -h switch. The --detailed-help option prints the full help text. -para_fsck tries to fix your database. Use --force (even if your name -isn't Luke) to clean up after a crash. However, first make sure -para_server isn't running before executing para_fsck if para_fsck -complains about busy (dirty) tables. para_fsck also contains an option -to dump the contents of the database to the file system. +If para_server crashed or was killed by SIGKILL (signal 9), it +may refuse to start again because of "dirty osl tables". In this +case you'll have to run the oslfsck program of libosl to fix your +database. It might be necessary to use --force (even if your name +isn't Luke). However, make sure para_server isn't running before +executing oslfsck --force. If you don't mind to recreate your database you can start from scratch by removing the entire database directory, i.e. - rm -rf ~/.paraslash/afs_database + rm -rf ~/.paraslash/afs_database-0.4 -Note that this removes all tables, in particular attribute definitions -and data, and all playlist and mood definitions. +Be aware that this removes all attribute definitions, all playlists +and all mood definitions. -para_fsck operates on the osl-layer, i.e. it fixes inconsistencies -in the database but doesn't know about the contents of the tables -contained therein. Use +Although oslfsck fixes inconsistencies in database tables it doesn't +care about the table contents. To check for invalid table contents, use para_client check -to print out bad entries, e.g.missing audio files or invalid mood -definitions. +This prints out references to missing audio files as well as invalid +playlists and mood definitions. Still having problems? mailto: Andre Noll diff --git a/REQUIREMENTS b/REQUIREMENTS index c1743fd2..a9aedcda 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -1,20 +1,25 @@ Requirements ============ -In any case you need +In any case you'll need - - gcc, the gnu compiler collection (shipped with distro): gcc-3.3 + - *libosl*, the object storage layer: Used by para_server. It is + available from http://git.tuebingen.mpg.de/osl. Alternatively, + execute "git clone git://git.tuebingen.mpg.de/osl". + + - *gcc*, the gnu compiler collection (shipped with distro): gcc-3.3 or newer is required. - - gnu make (shipped with disto, might be called gmake) - - bash (most likely already installed) - - A decent version of grep. Solaris' /bin/grep is not good enough, - /usr/xpg4/bin/grep is fine though. - - openssl (needed by server, client): usually shipped with + + - *gnu make* (shipped with disto, might be called gmake on BSD systems) + + - *bash* (most likely already installed) + + - *openssl* (needed by server, client): usually shipped with distro, but you might have to install the "development package" (called libssl-dev on debian systems) as well: http://www.openssl.org/ - - help2man (for man page creation) ftp://ftp.gnu.org/pub/gnu/help2man - - software mixing, e.g. ALSA and the direct mixing plugin (dmix) + + - *help2man* (for man page creation) ftp://ftp.gnu.org/pub/gnu/help2man Optional features: diff --git a/aac.h b/aac.h index 1efe75ac..0a4db41f 100644 --- a/aac.h +++ b/aac.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/aac_afh.c b/aac_afh.c index 80b50a4d..3e394337 100644 --- a/aac_afh.c +++ b/aac_afh.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -10,12 +10,12 @@ /** \file aac_afh.c para_server's aac audio format handler */ +#include + #include "para.h" #include "error.h" -#include "string.h" #include "afh.h" -#include "afs.h" -#include "server.h" +#include "string.h" #include "aac.h" static int aac_find_stsz(unsigned char *buf, size_t buflen, off_t *skip) @@ -91,11 +91,9 @@ static char *get_tag(unsigned char *p, int size) return buf; } -static char *read_tags(unsigned char *buf, size_t buflen) +static void read_tags(unsigned char *buf, size_t buflen, struct afh_info *afhi) { unsigned char *p = buf; - char *title = NULL, *artist = NULL, *album = NULL, *year = NULL, - *comment = NULL, *result; while (p + 32 < buf + buflen) { unsigned char *q, type1[5], type2[5]; @@ -114,27 +112,20 @@ static char *read_tags(unsigned char *buf, size_t buflen) if (q + size2 > buf + buflen) break; if (!atom_cmp(type1, "©ART")) - artist = get_tag(q, size2); + afhi->tags.artist = get_tag(q, size2); else if (!atom_cmp(type1, "©alb")) - album = get_tag(q, size2); + afhi->tags.album = get_tag(q, size2); else if (!atom_cmp(type1, "©nam")) - title = get_tag(q, size2); + afhi->tags.title = get_tag(q, size2); else if (!atom_cmp(type1, "©cmt")) - comment = get_tag(q, size2); + afhi->tags.comment = get_tag(q, size2); else if (!atom_cmp(type1, "©day")) - year = get_tag(q, size2); + afhi->tags.year = get_tag(q, size2); p += size1; } - result = make_taginfo(title, artist, album, year, comment); - free(title); - free(artist); - free(album); - free(year); - free(comment); - return result; } -static char *read_meta(unsigned char *buf, size_t buflen) +static void read_meta(unsigned char *buf, size_t buflen, struct afh_info *afhi) { unsigned char *p = buf; @@ -145,12 +136,12 @@ static char *read_meta(unsigned char *buf, size_t buflen) continue; } p += 4; - return read_tags(p, buflen - (p - buf)); + return read_tags(p, buflen - (p - buf), afhi); } - return make_taginfo(NULL, NULL, NULL, NULL, NULL); } -static char *aac_get_taginfo(unsigned char *buf, size_t buflen) +static void aac_get_taginfo(unsigned char *buf, size_t buflen, + struct afh_info *afhi) { int i; uint64_t subsize; @@ -165,10 +156,9 @@ static char *aac_get_taginfo(unsigned char *buf, size_t buflen) p = buf + i; i += read_atom_header(p, &subsize, type); p = buf + i; - return read_meta(p, buflen - i); + return read_meta(p, buflen - i, afhi); } PARA_INFO_LOG("no meta data\n"); - return make_taginfo(NULL, NULL, NULL, NULL, NULL); } static ssize_t aac_compute_chunk_table(struct afh_info *afhi, @@ -232,12 +222,11 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd, mp4AudioSpecificConfig mp4ASC; NeAACDecHandle handle = NULL; unsigned char *umap = (unsigned char *) map; - char *taginfo; ret = aac_find_esds(umap, numbytes, &skip, &decoder_len); if (ret < 0) goto out; - taginfo = aac_get_taginfo(umap, numbytes); + aac_get_taginfo(umap, numbytes, afhi); handle = aac_open(); ret = -E_AAC_AFH_INIT; if (NeAACDecInit(handle, umap + skip, decoder_len, &rate, &channels)) @@ -268,11 +257,6 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd, ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */ ret += (channels * afhi->seconds_total * 500); /* avoid rounding error */ afhi->bitrate = ret / (channels * afhi->seconds_total * 1000); - afhi->info_string = make_message("%s:\n%s", - status_item_list[SI_AUDIO_FILE_INFO], - taginfo); - free(taginfo); - tv_scale(20, &afhi->chunk_tv, &afhi->eof_tv); ret = 1; out: if (handle) diff --git a/aac_common.c b/aac_common.c index 2235236c..68c3c89b 100644 --- a/aac_common.c +++ b/aac_common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/aacdec_filter.c b/aacdec_filter.c index 1ceca1fc..209c9a46 100644 --- a/aacdec_filter.c +++ b/aacdec_filter.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -10,20 +10,20 @@ /** \file aacdec_filter.c paraslash's aac (m4a) decoder. */ -#include "para.h" +#include +#include +#include "para.h" #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "error.h" #include "string.h" #include "aac.h" -/** the output buffer size */ -#define AAC_OUTBUF_SIZE (32 * 1024) - -/** give up decoding after that many errors */ +/** Give up decoding after that many errors. */ #define MAX_ERRORS 20 /** @@ -49,25 +49,71 @@ struct private_aacdec_data { size_t consumed_total; /** return value of aac_find_entry_point */ size_t entry; + /** The number of channels of the current stream. */ + unsigned int channels; + /** Current sample rate in Hz. */ + unsigned int samplerate; }; -static ssize_t aacdec(char *input_buffer, size_t len, struct filter_node *fn) +static int aacdec_execute(struct btr_node *btrn, const char *cmd, char **result) +{ + struct filter_node *fn = btr_context(btrn); + struct private_aacdec_data *padd = fn->private_data; + + if (!strcmp(cmd, "samplerate")) { + if (padd->samplerate == 0) + return -E_BTR_NAVAIL; + *result = make_message("%u", padd->samplerate); + return 1; + } + if (!strcmp(cmd, "channels")) { + if (padd->channels == 0) + return -E_BTR_NAVAIL; + *result = make_message("%u", padd->channels); + return 1; + } + return -ERRNO_TO_PARA_ERROR(ENOTSUP); +} + +static void aacdec_open(struct filter_node *fn) +{ + struct private_aacdec_data *padd = para_calloc(sizeof(*padd)); + + fn->private_data = padd; + fn->min_iqs = 2048; + padd->handle = aac_open(); +} + +static void aacdec_close(struct filter_node *fn) +{ + struct private_aacdec_data *padd = fn->private_data; + + NeAACDecClose(padd->handle); + free(padd); + fn->private_data = NULL; +} + +static void aacdec_post_select(__a_unused struct sched *s, struct task *t) { + struct filter_node *fn = container_of(t, struct filter_node, task); + struct btr_node *btrn = fn->btrn; struct private_aacdec_data *padd = fn->private_data; - struct filter_chain *fc = fn->fc; int i, ret; - unsigned char *p, *outbuffer; - unsigned char *inbuf = (unsigned char*)input_buffer; - size_t skip, consumed = 0; + unsigned char *p, *inbuf, *outbuffer; + char *btr_buf; + size_t len, skip, consumed, loaded, iqs; - if (fn->loaded > fn->bufsize * 3 / 5) - return 0; - ret = *fc->input_error; +next_buffer: + t->error = 0; + ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); if (ret < 0) - return ret; - if (len < 2048) - return 0; - + goto err; + if (ret == 0) + return; + btr_merge(btrn, fn->min_iqs); + len = btr_next_buffer(btrn, (char **)&inbuf); + consumed = 0; + iqs = btr_get_input_queue_size(btrn); if (!padd->initialized) { unsigned long rate = 0; unsigned char channels = 0; @@ -93,10 +139,10 @@ static ssize_t aacdec(char *input_buffer, size_t len, struct filter_node *fn) &channels) < 0) goto out; } - fc->samplerate = rate; - fc->channels = channels; + padd->samplerate = rate; + padd->channels = channels; PARA_INFO_LOG("rate: %u, channels: %d\n", - fc->samplerate, fc->channels); + padd->samplerate, padd->channels); padd->initialized = 1; } if (padd->decoder_length > 0) { @@ -124,72 +170,64 @@ static ssize_t aacdec(char *input_buffer, size_t len, struct filter_node *fn) if (consumed >= len) goto success; p = inbuf + consumed; + //PARA_CRIT_LOG("consumed: %zu (%zu + %zu), have: %zu\n", padd->consumed_total + consumed, + // padd->consumed_total, consumed, len - consumed); outbuffer = NeAACDecDecode(padd->handle, &padd->frame_info, p, len - consumed); if (padd->frame_info.error) { + int err = padd->frame_info.error; ret = -E_AAC_DECODE; if (padd->error_count++ > MAX_ERRORS) - goto out; - PARA_ERROR_LOG("frame_error: %d, consumed: %zu + %zd + %lu\n", - padd->frame_info.error, padd->consumed_total, - consumed, padd->frame_info.bytesconsumed); + goto err; + PARA_ERROR_LOG("frame_error: %d (%s), consumed: %zu + %zd + %lu\n", + err, NeAACDecGetErrorMessage(padd->frame_info.error), + padd->consumed_total, consumed, + padd->frame_info.bytesconsumed); PARA_ERROR_LOG("%s\n", NeAACDecGetErrorMessage( padd->frame_info.error)); consumed++; /* catch 21 */ goto success; } padd->error_count = 0; + //PARA_CRIT_LOG("decoder ate %lu\n", padd->frame_info.bytesconsumed); consumed += padd->frame_info.bytesconsumed; ret = consumed; if (!padd->frame_info.samples) goto out; - ret = -E_AAC_OVERRUN; - if (padd->frame_info.samples * 2 + fn->loaded > fn->bufsize) - goto out; + btr_buf = para_malloc(2 * padd->frame_info.samples); + loaded = 0; for (i = 0; i < padd->frame_info.samples; i++) { - short *s = (short *)outbuffer; - write_int16_host_endian(fn->buf + fn->loaded, s[i]); - fn->loaded += 2; + short sh = ((short *)outbuffer)[i]; + write_int16_host_endian(btr_buf + loaded, sh); + loaded += 2; } + btr_add_output(btr_buf, loaded, btrn); success: ret = consumed; out: - if (ret > 0) + if (ret >= 0) { padd->consumed_total += ret; - return ret; -} - -static void aacdec_open(struct filter_node *fn) -{ - struct private_aacdec_data *padd = para_calloc(sizeof(*padd)); - - fn->private_data = padd; - fn->bufsize = AAC_OUTBUF_SIZE; - fn->buf = para_calloc(fn->bufsize); - padd->handle = aac_open(); -} - -static void aacdec_close(struct filter_node *fn) -{ - struct private_aacdec_data *padd = fn->private_data; - - NeAACDecClose(padd->handle); - free(fn->buf); - fn->buf = NULL; - free(padd); - fn->private_data = NULL; + btr_consume(btrn, ret); + goto next_buffer; + } +err: + assert(ret < 0); + t->error = ret; + btr_remove_node(btrn); } /** - * the init function of the aacdec filter + * The init function of the aacdec filter. * - * \param f pointer to the filter struct to initialize + * \param f Pointer to the filter struct to initialize. * * \sa filter::init */ void aacdec_filter_init(struct filter *f) { f->open = aacdec_open; - f->convert = aacdec; f->close = aacdec_close; + f->pre_select = generic_filter_pre_select; + f->post_select = aacdec_post_select; + f->execute = aacdec_execute; } diff --git a/acl.c b/acl.c index ffcd1685..02df1e48 100644 --- a/acl.c +++ b/acl.c @@ -1,11 +1,13 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file acl.c Access control lists for paraslash senders. */ +#include + #include "para.h" #include "error.h" #include "string.h" diff --git a/acl.h b/acl.h index e6090a88..65ccc748 100644 --- a/acl.h +++ b/acl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 Andre Noll + * Copyright (C) 2008-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/afh.c b/afh.c index 8e1e6b31..b38dfbba 100644 --- a/afh.c +++ b/afh.c @@ -1,11 +1,12 @@ /* - * Copyright (C) 2008-2009 Andre Noll + * Copyright (C) 2008-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file afh.c Paraslash's standalone audio format handler tool. */ +#include #include #include @@ -32,18 +33,28 @@ static void print_info(int audio_format_num, struct afh_info *afhi) "%s: %dHz\n" /* frequency */ "%s: %d\n" /* channels */ "%s: %lu\n" /* seconds total */ - "%s" /* tag info */ "%s: %lu: %lu\n" /* chunk time */ - "%s: %lu\n", /* num chunks */ + "%s: %lu\n" /* num chunks */ + "%s: %s\n" /* techinfo */ + "%s: %s\n" /* artist */ + "%s: %s\n" /* title */ + "%s: %s\n" /* year */ + "%s: %s\n" /* album */ + "%s: %s\n", /* comment */ status_item_list[SI_BITRATE], afhi->bitrate, status_item_list[SI_FORMAT], audio_format_name(audio_format_num), status_item_list[SI_FREQUENCY], afhi->frequency, status_item_list[SI_CHANNELS], afhi->channels, status_item_list[SI_SECONDS_TOTAL], afhi->seconds_total, - afhi->info_string, status_item_list[SI_CHUNK_TIME], (long unsigned)afhi->chunk_tv.tv_sec, (long unsigned)afhi->chunk_tv.tv_usec, - status_item_list[SI_NUM_CHUNKS], afhi->chunks_total + status_item_list[SI_NUM_CHUNKS], afhi->chunks_total, + status_item_list[SI_TECHINFO], afhi->techinfo? afhi->techinfo : "", + status_item_list[SI_ARTIST], afhi->tags.artist? afhi->tags.artist : "", + status_item_list[SI_TITLE], afhi->tags.title? afhi->tags.title : "", + status_item_list[SI_YEAR], afhi->tags.year? afhi->tags.year : "", + status_item_list[SI_ALBUM], afhi->tags.album? afhi->tags.album : "", + status_item_list[SI_COMMENT], afhi->tags.comment? afhi->tags.comment : "" ); } @@ -51,10 +62,25 @@ static void print_chunk_table(struct afh_info *afhi) { int i; - printf("chunk_table: "); - for (i = 0; i <= afhi->chunks_total; i++) - printf("%u ", afhi->chunk_table[i]); - printf("\n"); + if (!conf.human_given) { + printf("chunk_table: "); + for (i = 0; i <= afhi->chunks_total; i++) + printf("%u ", afhi->chunk_table[i]); + printf("\n"); + return; + } + for (i = 1; i <= afhi->chunks_total; i++) { + struct timeval tv; + long unsigned from, to; + tv_scale(i - 1, &afhi->chunk_tv, &tv); + from = tv2ms(&tv); + tv_scale(i, &afhi->chunk_tv, &tv); + to = tv2ms(&tv); + printf("%d [%lu.%03lu - %lu.%03lu] %u - %u (%u)\n", i, + from / 1000, from % 1000, to / 1000, to % 1000, + afhi->chunk_table[i - 1], afhi->chunk_table[i], + afhi->chunk_table[i] - afhi->chunk_table[i - 1]); + } } static int cat_file(void *audio_file_data, struct afh_info *afhi) @@ -166,10 +192,15 @@ int main(int argc, char **argv) print_info(audio_format_num, &afhi); if (conf.chunk_table_given) print_chunk_table(&afhi); + free(afhi.techinfo); + free(afhi.tags.artist); + free(afhi.tags.title); + free(afhi.tags.year); + free(afhi.tags.album); + free(afhi.tags.comment); + free(afhi.chunk_table); printf("\n"); } - free(afhi.chunk_table); - free(afhi.info_string); ret2 = para_munmap(audio_file_data, audio_file_size); if (ret2 < 0 && ret >= 0) ret = ret2; diff --git a/afh.h b/afh.h index 35cc7ef1..95cd0514 100644 --- a/afh.h +++ b/afh.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -19,18 +19,39 @@ #define AAC_AUDIO_FORMAT "" #endif -#define SUPPORTED_AUDIO_FORMATS "mp3" OV_AUDIO_FORMAT AAC_AUDIO_FORMAT +#define SUPPORTED_AUDIO_FORMATS "mp3 " OV_AUDIO_FORMAT AAC_AUDIO_FORMAT " wma " /** \endcond */ +/** + * The tags used by all audio format handlers. + * + * Paraslash only uses the more common tags. These are recognized + * for all supported audio formats. + */ +struct taginfo { + /** TPE1 (id3v2) / ARTIST (vorbis) / ©ART (aac) */ + char *artist; + /** TIT2/TITLE/©nam */ + char *title; + /** TDRC/YEAR/©day */ + char *year; + /** TALB/ALBUM/©alb */ + char *album; + /** COMM/COMMENT/©cmt */ + char *comment; +}; + /** Audio format dependent information. */ struct afh_info { /** The number of chunks this audio file contains. */ long unsigned chunks_total; /** The length of the audio file in seconds. */ long unsigned seconds_total; - /** A string that gets filled in by the audio format handler. */ - char *info_string; + /** Audio handler specific info about the file. */ + char *techinfo; + /** Id3 tags, vorbis comments, aac tags. */ + struct taginfo tags; /** * The table that specifies the offset of the individual pieces in * the current audio file. @@ -38,8 +59,6 @@ struct afh_info { uint32_t *chunk_table; /** Period of time between sending data chunks. */ struct timeval chunk_tv; - /** End of file timeout - Do not load new audio file until this time. */ - struct timeval eof_tv; /** * The position of the header within the audio file. Ignored if \a * header_len equals zero. @@ -104,5 +123,3 @@ void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi, void *map, const char **buf, size_t *len); uint32_t afh_get_largest_chunk_size(struct afh_info *afhi); void afh_get_header(struct afh_info *afhi, void *map, const char **buf, size_t *len); -char *make_taginfo(char *title, char *artist, char *album, char *year, - char *comment); diff --git a/afh_common.c b/afh_common.c index 7d4ab089..f28acaaf 100644 --- a/afh_common.c +++ b/afh_common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2009 Andre Noll + * Copyright (C) 1997-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -10,6 +10,7 @@ #include /* gettimeofday */ #include #include +#include #include "para.h" #include "error.h" @@ -26,6 +27,7 @@ void mp3_init(struct audio_format_handler *); void aac_afh_init(struct audio_format_handler *); #endif +void wma_afh_init(struct audio_format_handler *); /** * The list of supported audio formats. * @@ -55,6 +57,10 @@ static struct audio_format_handler afl[] = { .init = aac_afh_init, #endif }, + { + .name = "wma", + .init = wma_afh_init, + }, { .name = NULL, } @@ -121,39 +127,6 @@ int guess_audio_format(const char *name) return -E_AUDIO_FORMAT; } -/** - * Pretty-print the given meta-info. - * - * \param title The title of the audio file. - * \param artist The artist. - * \param album The name of the album. - * \param year Year of release. - * \param comment Further comments. - * - * This function is called by each audio format handler to produce the tag info - * status items. Usually, the audio format handlers read this info from the - * audio file (id3 tags, vorbis comments, ...). - * - * It is OK to pass \p NULL pointers for any argument in which case a suitable - * string is inserted which indicates that this information is not available. - * - * \return The status item string. It must be freed by the caller. - */ -char *make_taginfo(char *title, char *artist, char *album, char *year, - char *comment) -{ - return make_message("%s: %s, by %s\n" /* taginfo1 */ - "%s: A: %s, Y: %s, C: %s\n", /* taginfo2 */ - status_item_list[SI_TAGINFO1], - (title && *title)? title : "(title tag not set)", - (artist && *artist)? artist : "(artist tag not set)", - status_item_list[SI_TAGINFO2], - (album && *album)? album : "(album tag not set)", - (year && *year)? year : "????", - (comment && *comment)? comment : "(comment tag not set)" - ); -} - /** * Call get_file_info() to obtain an afhi structure. * @@ -180,22 +153,52 @@ int compute_afhi(const char *path, char *data, size_t size, int fd, afhi->header_offset = 0; afhi->header_len = 0; + afhi->techinfo = NULL; + afhi->tags.artist = NULL; + afhi->tags.title = NULL; + afhi->tags.year = NULL; + afhi->tags.album = NULL; + afhi->tags.comment = NULL; format = guess_audio_format(path); if (format >= 0) { ret = afl[format].get_file_info(data, size, fd, afhi); - if (ret >= 0) - return format; + if (ret >= 0) { + ret = format; + goto success; + } } FOR_EACH_AUDIO_FORMAT(i) { if (i == format) /* we already tried this one to no avail */ continue; ret = afl[i].get_file_info(data, size, fd, afhi); - if (ret >= 0) - return i; + if (ret >= 0) { + ret = i; + goto success; + } PARA_WARNING_LOG("%s\n", para_strerror(-ret)); } return -E_AUDIO_FORMAT; +success: + if (!afhi->techinfo) + afhi->techinfo = para_strdup(NULL); + if (!afhi->tags.artist) + afhi->tags.artist = para_strdup(NULL); + if (!afhi->tags.title) + afhi->tags.title = para_strdup(NULL); + if (!afhi->tags.year) + afhi->tags.year = para_strdup(NULL); + if (!afhi->tags.album) + afhi->tags.album = para_strdup(NULL); + if (!afhi->tags.comment) + afhi->tags.comment = para_strdup(NULL); + PARA_DEBUG_LOG("techinfo: %s\n", afhi->techinfo); + PARA_DEBUG_LOG("artist: %s\n", afhi->tags.artist); + PARA_DEBUG_LOG("title: %s\n", afhi->tags.title); + PARA_DEBUG_LOG("year: %s\n", afhi->tags.year); + PARA_DEBUG_LOG("album: %s\n", afhi->tags.album); + PARA_DEBUG_LOG("comment: %s\n", afhi->tags.comment); + return ret; } /** @@ -233,6 +236,13 @@ void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi, *len = afhi->chunk_table[chunk_num + 1] - pos; } +/** + * Compute the size of the largest chunk of an audio file. + * + * \param afhi The audio format handler struct containing the chunk table. + * + * \return The number of bytes of the largest chunk. + */ uint32_t afh_get_largest_chunk_size(struct afh_info *afhi) { uint32_t n, largest = 0, *ct = afhi->chunk_table; @@ -252,7 +262,7 @@ uint32_t afh_get_largest_chunk_size(struct afh_info *afhi) * * This function sets \a buf to \p NULL and \a len to zero if \a map or \a * afhi is \p NULL, or if the current audio format does not need special - * header treamtment. + * header treatment. */ void afh_get_header(struct afh_info *afhi, void *map, const char **buf, size_t *len) { diff --git a/afs.c b/afs.c index ccda7cda..d738c3d9 100644 --- a/afs.c +++ b/afs.c @@ -1,16 +1,21 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file afs.c Paraslash's audio file selector. */ +#include #include #include +#include +#include + #include "server.cmdline.h" #include "para.h" #include "error.h" +#include "crypt.h" #include "string.h" #include "afh.h" #include "afs.h" @@ -24,6 +29,7 @@ #include "sched.h" #include "signal.h" #include "fd.h" +#include "mood.h" /** The osl tables used by afs. \sa blob.c. */ enum afs_table_num { @@ -85,7 +91,6 @@ static struct signal_task signal_task_struct; static enum play_mode current_play_mode; static char *current_mop; /* mode or playlist specifier. NULL means dummy mooe */ - /** * A random number used to "authenticate" the connection. * @@ -342,7 +347,7 @@ static int action_if_pattern_matches(struct osl_row *row, void *data) struct pattern_match_data *pmd = data; struct osl_object name_obj; const char *p, *name; - int ret = osl_get_object(pmd->table, row, pmd->match_col_num, &name_obj); + int ret = osl(osl_get_object(pmd->table, row, pmd->match_col_num, &name_obj)); const char *pattern_txt = (const char *)pmd->patterns.data; if (ret < 0) @@ -369,16 +374,15 @@ static int action_if_pattern_matches(struct osl_row *row, void *data) * * \param pmd Describes what to match and how. * - * \return The return value of the underlying call to osl_rbtree_loop() - * or osl_rbtree_loop_reverse(). + * \return Standard. */ int for_each_matching_row(struct pattern_match_data *pmd) { if (pmd->pm_flags & PM_REVERSE_LOOP) - return osl_rbtree_loop_reverse(pmd->table, pmd->loop_col_num, pmd, - action_if_pattern_matches); - return osl_rbtree_loop(pmd->table, pmd->loop_col_num, pmd, - action_if_pattern_matches); + return osl(osl_rbtree_loop_reverse(pmd->table, pmd->loop_col_num, pmd, + action_if_pattern_matches)); + return osl(osl_rbtree_loop(pmd->table, pmd->loop_col_num, pmd, + action_if_pattern_matches)); } /** @@ -403,80 +407,6 @@ int string_compare(const struct osl_object *obj1, const struct osl_object *obj2) return strncmp(str1, str2, PARA_MIN(obj1->size, obj2->size)); } -/* - * write input from fd to dynamically allocated buffer, - * but maximal max_size byte. - */ -static int fd2buf(int fd, unsigned max_size, struct osl_object *obj) -{ - const size_t chunk_size = 1024; - size_t size = 2048, received = 0; - int ret; - char *buf = para_malloc(size); - - for (;;) { - ret = recv_bin_buffer(fd, buf + received, chunk_size); - if (ret <= 0) - break; - received += ret; - if (received + chunk_size >= size) { - size *= 2; - ret = -E_INPUT_TOO_LARGE; - if (size > max_size) - break; - buf = para_realloc(buf, size); - } - } - obj->data = buf; - obj->size = received; - if (ret < 0) - free(buf); - return ret; -} - -/** - * Read data from a file descriptor, and send it to the afs process. - * - * \param fd File descriptor to read data from. - * \param arg_obj Pointer to the arguments to \a f. - * \param f The callback function. - * \param max_len Don't read more than that many bytes from stdin. - * \param result_handler See \ref send_callback_request. - * \param private_result_data See \ref send_callback_request. - * - * This function is used by commands that wish to let para_server store - * arbitrary data specified by the user (for instance the add_blob family of - * commands). First, at most \a max_len bytes are read from \a fd, the result - * is concatenated with the buffer given by \a arg_obj, and the combined buffer - * is made available to the afs process via the callback method. See \ref - * send_callback_request for details. - * - * \return Negative on errors, the return value of the underlying call to - * send_callback_request() otherwise. - */ -int stdin_command(int fd, struct osl_object *arg_obj, callback_function *f, - unsigned max_len, callback_result_handler *result_handler, - void *private_result_data) -{ - struct osl_object query, stdin_obj; - int ret; - - ret = send_buffer(fd, AWAITING_DATA_MSG); - if (ret < 0) - return ret; - ret = fd2buf(fd, max_len, &stdin_obj); - if (ret < 0) - return ret; - query.size = arg_obj->size + stdin_obj.size; - query.data = para_malloc(query.size); - memcpy(query.data, arg_obj->data, arg_obj->size); - memcpy((char *)query.data + arg_obj->size, stdin_obj.data, stdin_obj.size); - free(stdin_obj.data); - ret = send_callback_request(f, &query, result_handler, private_result_data); - free(query.data); - return ret; -} - static int pass_afd(int fd, char *buf, size_t size) { struct msghdr msg = {.msg_iov = NULL}; @@ -652,21 +582,22 @@ out: * Result handler for sending data to the para_client process. * * \param result The data to be sent. - * \param fd_ptr Pointer to the file descriptor. + * \param private Pointer to rc4 context. * - * \return The return value of the underlying call to send_bin_buffer(). + * \return The return value of the underlying call to rc4_send_bin_buffer(). * - * \sa \ref callback_result_handler. + * \sa \ref callback_result_handler, \ref rc4_send_bin_buffer(). */ -int send_result(struct osl_object *result, void *fd_ptr) +int rc4_send_result(struct osl_object *result, void *private) { - int fd = *(int *)fd_ptr; + struct rc4_context *rc4c = private; + if (!result->size) return 1; - return send_bin_buffer(fd, result->data, result->size); + return rc4_send_bin_buffer(rc4c, result->data, result->size); } -int com_select(int fd, int argc, char * const * const argv) +int com_select(struct rc4_context *rc4c, int argc, char * const * const argv) { struct osl_object query; @@ -675,7 +606,7 @@ int com_select(int fd, int argc, char * const * const argv) query.data = argv[1]; query.size = strlen(argv[1]) + 1; return send_callback_request(com_select_callback, &query, - &send_result, &fd); + &rc4_send_result, rc4c); } static void init_admissible_files(char *arg) @@ -730,7 +661,7 @@ static void get_database_dir(void) else { char *home = para_homedir(); database_dir = make_message( - "%s/.paraslash/afs_database", home); + "%s/.paraslash/afs_database-0.4", home); free(home); } } @@ -1096,7 +1027,7 @@ out: free(pb.buf); } -int com_init(int fd, int argc, char * const * const argv) +int com_init(struct rc4_context *rc4c, int argc, char * const * const argv) { int i, j, ret; uint32_t table_mask = (1 << (NUM_AFS_TABLES + 1)) - 1; @@ -1121,9 +1052,10 @@ int com_init(int fd, int argc, char * const * const argv) return -E_BAD_TABLE_NAME; } } - ret = send_callback_request(create_tables_callback, &query, &send_result, &fd); + ret = send_callback_request(create_tables_callback, &query, + rc4_send_result, rc4c); if (ret < 0) - return send_va_buffer(fd, "%s\n", para_strerror(-ret)); + return rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -1141,7 +1073,7 @@ enum com_check_flags { CHECK_PLAYLISTS = 4 }; -int com_check(int fd, int argc, char * const * const argv) +int com_check(struct rc4_context *rc4c, int argc, char * const * const argv) { unsigned flags = 0; int i, ret; @@ -1173,17 +1105,20 @@ int com_check(int fd, int argc, char * const * const argv) if (!flags) flags = ~0U; if (flags & CHECK_AFT) { - ret = send_callback_request(aft_check_callback, NULL, send_result, &fd); + ret = send_callback_request(aft_check_callback, NULL, + rc4_send_result, rc4c); if (ret < 0) return ret; } if (flags & CHECK_PLAYLISTS) { - ret = send_callback_request(playlist_check_callback, NULL, send_result, &fd); + ret = send_callback_request(playlist_check_callback, + NULL, rc4_send_result, rc4c); if (ret < 0) return ret; } if (flags & CHECK_MOODS) { - ret = send_callback_request(mood_check_callback, NULL, send_result, &fd); + ret = send_callback_request(mood_check_callback, NULL, + rc4_send_result, rc4c); if (ret < 0) return ret; } @@ -1216,12 +1151,30 @@ void afs_event(enum afs_events event, struct para_buffer *pb, } } +/** + * Dummy event handler for the images table. + * + * \param event Unused. + * \param pb Unused. + * \param data Unused. + * + * This table does not honor events. + */ int images_event_handler(__a_unused enum afs_events event, __a_unused struct para_buffer *pb, __a_unused void *data) { return 1; } +/** + * Dummy event handler for the lyrics table. + * + * \param event Unused. + * \param pb Unused. + * \param data Unused. + * + * This table does not honor events. + */ int lyrics_event_handler(__a_unused enum afs_events event, __a_unused struct para_buffer *pb, __a_unused void *data) { diff --git a/afs.cmd b/afs.cmd index 0af45113..f0551998 100644 --- a/afs.cmd +++ b/afs.cmd @@ -3,7 +3,8 @@ SF: afs.c aft.c attribute.c HC: Prototypes for the commands of the audio file selector. CC: Array of commands for the audio file selector. AT: server_command -IN: para error string afh afs server list user_list +SI: openssl/rc4 osl regex +IN: para error crypt command string afh afs server list user_list SN: list of afs commands TM: mood lyr img pl --- @@ -54,6 +55,8 @@ H: -ll: long listing mode (equivalent to -l) H: H: -lv: verbose listing mode H: +H: -lp: parser-friendly mode +H: H: -lm: mbox listing mode H: H: -lc: chunk-table listing mode @@ -94,7 +97,7 @@ H: -sa: sort by audio format. --- N: lsatt P: AFS_READ -D: List attributes +D: List attributes. U: lsatt [-i] [-l] [-r] [pattern] H: Print the list of all defined attributes which match the H: given pattern. If no pattern is given, the full list is @@ -184,7 +187,7 @@ H: a slash (see fnmatch(3)). N: touch P: AFS_READ | AFS_WRITE D: Manipulate the afs data for all audio files matching a pattern. -U: touch [-n numplayed] [-l lastplayed] [-y lyrics_id] [-i image_id] [-a amp] [-v] [-p] pattern +U: touch [-n=numplayed] [-l=lastplayed] [-y=lyrics_id] [-i=image_id] [-a=amp] [-v] [-p] pattern H: If no option is given, lastplayed is set to the current time H: and numplayed is increased by one. Otherwise, only the given H: options are taken into account. @@ -197,7 +200,7 @@ H: H: -l Set lastplayed time. The last time this audio file was selected. H: Must be given as the number of seconds since the epoch. Example: H: -H: touch -l $(date +%s) file +H: touch -l=$(date +%s) file H: H: sets the lastplayed time of 'file' to the current time. H: @@ -257,7 +260,7 @@ H: loads the mood named 'foo'. --- T: add N: add@member@ -O: int com_add@member@(int fd, int argc, char * const * const argv); +O: int com_add@member@(struct rc4_context *rc4c, int argc, char * const * const argv); P: AFS_READ | AFS_WRITE D: Read data from stdin and add it as a blob to the @member@ table. U: add@member@ @member@_name @@ -270,7 +273,7 @@ H: given name already exists, its contents are replaced by the new data. --- T: cat N: cat@member@ -O: int com_cat@member@(int fd, int argc, char * const * const argv); +O: int com_cat@member@(struct rc4_context *rc4c, int argc, char * const * const argv); P: AFS_READ D: Dump the contents of a blob of type @member@ to stdout. U: cat@member@ @member@_name @@ -280,7 +283,7 @@ H: they were previously added. --- T: ls N: ls@member@ -O: int com_ls@member@(int fd, int argc, char * const * const argv); +O: int com_ls@member@(struct rc4_context *rc4c, int argc, char * const * const argv); P: AFS_READ D: List blobs of type @member@ matching a pattern. U: ls@member@ [-i] [-l] [-r] [pattern] @@ -300,7 +303,7 @@ H: -r Reverse sort order. --- T: rm N: rm@member@ -O: int com_rm@member@(int fd, int argc, char * const * const argv); +O: int com_rm@member@(struct rc4_context *rc4c, int argc, char * const * const argv); P: AFS_READ | AFS_WRITE D: Remove blob(s) of type @member@ from the @member@ table. U: rm@member@ pattern... @@ -309,7 +312,7 @@ H: any given pattern. --- T: mv N: mv@member@ -O: int com_mv@member@(int fd, int argc, char * const * const argv); +O: int com_mv@member@(struct rc4_context *rc4c, int argc, char * const * const argv); P: AFS_READ | AFS_WRITE D: Rename a blob of type @member@. U: mv@member@ old_@member@_name new_@member@_name diff --git a/afs.h b/afs.h index bcc36309..76b81905 100644 --- a/afs.h +++ b/afs.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -7,7 +7,6 @@ /** \file afs.h Exported symbols of the audio file selector. */ #include -#include "osl.h" #include "hash.h" /** Audio file selector data stored in the audio file table. */ @@ -98,7 +97,13 @@ struct afs_table { void *data); }; -enum play_mode {PLAY_MODE_MOOD, PLAY_MODE_PLAYLIST}; +/** How audio files are selected by afs. */ +enum play_mode { + /** Admissible files are determined by a mood definition. */ + PLAY_MODE_MOOD, + /** All listed files are admissible. */ + PLAY_MODE_PLAYLIST, +}; /** * Data about one audio file. @@ -118,25 +123,33 @@ struct ls_data { HASH_TYPE *hash; }; -void make_empty_status_items(char *buf); - -/** At most that many bytes will be passed from afs to para_server. */ -#define VERBOSE_LS_OUTPUT_SIZE 4096 - /** Data about the current audio file, passed from afs to server. */ struct audio_file_data { - /** Same info as ls -lv -p current audio_file. */ - char verbose_ls_output[VERBOSE_LS_OUTPUT_SIZE]; /** The open file descriptor to the current audio file. */ int fd; /** Vss needs this for streaming. */ struct afh_info afhi; }; +/** + * Codes used for communication between the server and the afs process. + * + * Before forking the afs child, para_server creates a bidirectional pipe + * through which both processes communicate. Usually para_server requests a new + * audio in order to start streaming or when the end of the current audio file + * has been reached. The afs process responds to such a request by sending + * back an eight byte buffer. The first four bytes is the uint32_t + * representation of the code, usually \p NEXT_AUDIO_FILE if an admissible + * audio file was found, successfully opened and verified. The other four bytes + * represent the shared memory id of the shared memory area that contains + * details about the audio file to be streamed next. The open file descriptor + * of that file is also passed from afs to para_server through the same pipe. + */ enum afs_server_code { + /** An audio file was successfully opened. */ NEXT_AUDIO_FILE, + /** No admissible audio file was found. */ NO_ADMISSIBLE_FILES, - AFD_CHANGE }; /** Flags passed to for_each_matching_row(). */ @@ -188,7 +201,7 @@ typedef void callback_function(int fd, const struct osl_object *); * \sa \ref send_callback_request(). */ typedef int callback_result_handler(struct osl_object *result, void *private); -int send_result(struct osl_object *result, void *fd_ptr); +int rc4_send_result(struct osl_object *result, void *private); int pass_buffer_as_shm(char *buf, size_t size, void *fd_ptr); __noreturn void afs_init(uint32_t cookie, int socket_fd); @@ -204,9 +217,6 @@ int send_option_arg_callback_request(struct osl_object *options, int send_standard_callback_request(int argc, char * const * const argv, callback_function *f, callback_result_handler *result_handler, void *private_result_data); -int stdin_command(int fd, struct osl_object *arg_obj, callback_function *f, - unsigned max_len, callback_result_handler *result_handler, - void *private_result_data); int string_compare(const struct osl_object *obj1, const struct osl_object *obj2); int for_each_matching_row(struct pattern_match_data *pmd); @@ -245,13 +255,6 @@ int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj); int audio_file_loop(void *private_data, osl_rbtree_loop_func *func); void aft_check_callback(int fd, __a_unused const struct osl_object *query); -/* mood */ -int change_current_mood(char *mood_name); -void close_current_mood(void); -int reload_current_mood(void); -void mood_check_callback(int fd, __a_unused const struct osl_object *query); - - /* playlist */ int playlist_open(char *name); void playlist_close(void); @@ -289,21 +292,3 @@ enum blob_table_columns { /** A blob table has that many columns. */ NUM_BLOB_COLUMNS }; - -/** Define an osl table description for a blob table. */ -#define DEFINE_BLOB_TABLE_DESC(table_name) \ - struct osl_table_description table_name ## _table_desc = { \ - .name = #table_name, \ - .num_columns = NUM_BLOB_COLUMNS, \ - .flags = OSL_LARGE_TABLE, \ - .column_descriptions = blob_cols \ - }; - -/** Define a pointer to an osl blob table with a canonical name. */ -#define DEFINE_BLOB_TABLE_PTR(table_name) struct osl_table *table_name ## _table; - -/** Define a blob table. */ -#define INIT_BLOB_TABLE(table_name) \ - DEFINE_BLOB_TABLE_DESC(table_name); \ - DEFINE_BLOB_TABLE_PTR(table_name); - diff --git a/aft.c b/aft.c index 5fb04807..25058e7c 100644 --- a/aft.c +++ b/aft.c @@ -1,19 +1,23 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file aft.c Audio file table functions. */ +#include #include /* readdir() */ -#include "para.h" -#include "error.h" -#include "string.h" +#include #include #include #include +#include +#include "para.h" +#include "error.h" +#include "crypt.h" +#include "string.h" #include "afh.h" #include "afs.h" #include "net.h" @@ -23,6 +27,8 @@ #include "portable_io.h" static struct osl_table *audio_file_table; +static char *status_items; +static char *parser_friendly_status_items; /** The different sorting methods of the ls command. */ enum ls_sorting_method { @@ -63,7 +69,9 @@ enum ls_listing_mode { /** -lm */ LS_MODE_MBOX, /** -lc */ - LS_MODE_CHUNKS + LS_MODE_CHUNKS, + /** -lp */ + LS_MODE_PARSER, }; /** The flags accepted by the ls command. */ @@ -220,12 +228,27 @@ enum audio_file_table_columns { NUM_AFT_COLUMNS }; +/** + * Compare two osl objects pointing to hash values. + * + * \param obj1 Pointer to the first hash object. + * \param obj2 Pointer to the second hash object. + * + * \return The values required for an osl compare function. + * + * \sa osl_compare_func, uint32_compare(). + */ +static int aft_hash_compare(const struct osl_object *obj1, const struct osl_object *obj2) +{ + return hash_compare((HASH_TYPE *)obj1->data, (HASH_TYPE *)obj2->data); +} + static struct osl_column_description aft_cols[] = { [AFTCOL_HASH] = { .storage_type = OSL_MAPPED_STORAGE, .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE, .name = "hash", - .compare_function = osl_hash_compare, + .compare_function = aft_hash_compare, .data_size = HASH_SIZE }, [AFTCOL_PATH] = { @@ -338,23 +361,29 @@ enum afhi_offsets { CHUNK_TV_TV_USEC_OFFSET = 36, /** Number of channels is stored here. (1 byte) */ AFHI_CHANNELS_OFFSET = 40, - /** EOF timeout in ms. (2 byte) */ - AFHI_EOF_OFFSET = 41, /** The tag info position. */ - AFHI_INFO_STRING_OFFSET = 43, + AFHI_INFO_STRING_OFFSET = 41, /** Minimal on-disk size of a valid afhi struct. */ - MIN_AFHI_SIZE = 44 + MIN_AFHI_SIZE = 47, /* at least 6 null bytes for techinfo/tags */ }; static unsigned sizeof_afhi_buf(const struct afh_info *afhi) { if (!afhi) return 0; - return strlen(afhi->info_string) + MIN_AFHI_SIZE; + return MIN_AFHI_SIZE + + strlen(afhi->techinfo) + + strlen(afhi->tags.artist) + + strlen(afhi->tags.title) + + strlen(afhi->tags.year) + + strlen(afhi->tags.album) + + strlen(afhi->tags.comment); } static void save_afhi(struct afh_info *afhi, char *buf) { + char *p; + if (!afhi) return; write_u32(buf + AFHI_SECONDS_TOTAL_OFFSET, afhi->seconds_total); @@ -368,8 +397,14 @@ static void save_afhi(struct afh_info *afhi, char *buf) write_u32(buf + HEADER_OFFSET_OFFSET, afhi->header_offset); write_u32(buf + CHUNK_TV_TV_SEC_OFFSET, afhi->chunk_tv.tv_sec); write_u32(buf + CHUNK_TV_TV_USEC_OFFSET, afhi->chunk_tv.tv_usec); - write_u16(buf + AFHI_EOF_OFFSET, tv2ms(&afhi->eof_tv)); - strcpy(buf + AFHI_INFO_STRING_OFFSET, afhi->info_string); /* OK */ + p = buf + AFHI_INFO_STRING_OFFSET; + /* The sprintf's below are OK as our caller made sure that buf is large enough */ + p += sprintf(p, "%s", afhi->techinfo) + 1; + p += sprintf(p, "%s", afhi->tags.artist) + 1; + p += sprintf(p, "%s", afhi->tags.title) + 1; + p += sprintf(p, "%s", afhi->tags.year) + 1; + p += sprintf(p, "%s", afhi->tags.album) + 1; + sprintf(p, "%s", afhi->tags.comment); } static void load_afhi(const char *buf, struct afh_info *afhi) @@ -385,8 +420,12 @@ static void load_afhi(const char *buf, struct afh_info *afhi) afhi->header_offset = read_u32(buf + HEADER_OFFSET_OFFSET); afhi->chunk_tv.tv_sec = read_u32(buf + CHUNK_TV_TV_SEC_OFFSET); afhi->chunk_tv.tv_usec = read_u32(buf + CHUNK_TV_TV_USEC_OFFSET); - ms2tv(read_u16(buf + AFHI_EOF_OFFSET), &afhi->eof_tv); - afhi->info_string = para_strdup(buf + AFHI_INFO_STRING_OFFSET); + afhi->techinfo = (char *)buf + AFHI_INFO_STRING_OFFSET; + afhi->tags.artist = afhi->techinfo + strlen(afhi->techinfo) + 1; + afhi->tags.title = afhi->tags.artist + strlen(afhi->tags.artist) + 1; + afhi->tags.year = afhi->tags.title + strlen(afhi->tags.title) + 1; + afhi->tags.album = afhi->tags.year + strlen(afhi->tags.year) + 1; + afhi->tags.comment = afhi->tags.album + strlen(afhi->tags.album) + 1; } static unsigned sizeof_chunk_table(struct afh_info *afhi) @@ -419,27 +458,27 @@ static void load_chunk_table(struct afh_info *afhi, char *buf) * \param path The full path of the audio file. * \param row Result pointer. * - * \return The return value of the underlying call to osl_get_row(). + * \return Standard. */ int aft_get_row_of_path(const char *path, struct osl_row **row) { struct osl_object obj = {.data = (char *)path, .size = strlen(path) + 1}; - return osl_get_row(audio_file_table, AFTCOL_PATH, &obj, row); + return osl(osl_get_row(audio_file_table, AFTCOL_PATH, &obj, row)); } /** * Get the row of the audio file table corresponding to the given hash value. * * \param hash The hash value of the desired audio file. - * \param row resul pointer. + * \param row Result pointer. * - * \return The return value of the underlying call to osl_get_row(). + * \return Standard. */ -int aft_get_row_of_hash(HASH_TYPE *hash, struct osl_row **row) +static int aft_get_row_of_hash(HASH_TYPE *hash, struct osl_row **row) { const struct osl_object obj = {.data = hash, .size = HASH_SIZE}; - return osl_get_row(audio_file_table, AFTCOL_HASH, &obj, row); + return osl(osl_get_row(audio_file_table, AFTCOL_HASH, &obj, row)); } /** @@ -448,11 +487,11 @@ int aft_get_row_of_hash(HASH_TYPE *hash, struct osl_row **row) * \param row Pointer to a row in the audio file table. * \param obj Result pointer. * - * \return The return value of the underlying call to osl_get_object(). + * \return Standard. */ int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj) { - return osl_get_object(audio_file_table, row, AFTCOL_AFSI, obj); + return osl(osl_get_object(audio_file_table, row, AFTCOL_AFSI, obj)); } /** @@ -464,7 +503,7 @@ int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj) * * \return Positive on success, negative on errors. */ -int get_afsi_object_of_path(const char *path, struct osl_object *obj) +static int get_afsi_object_of_path(const char *path, struct osl_object *obj) { struct osl_row *row; int ret = aft_get_row_of_path(path, &row); @@ -521,8 +560,8 @@ int get_afsi_of_path(const char *path, struct afs_info *afsi) int get_audio_file_path_of_row(const struct osl_row *row, char **path) { struct osl_object path_obj; - int ret = osl_get_object(audio_file_table, row, AFTCOL_PATH, - &path_obj); + int ret = osl(osl_get_object(audio_file_table, row, AFTCOL_PATH, + &path_obj)); if (ret < 0) return ret; *path = path_obj.data; @@ -539,9 +578,10 @@ int get_audio_file_path_of_row(const struct osl_row *row, char **path) * * \sa get_hash_of_row(). */ -static int get_hash_object_of_aft_row(const struct osl_row *row, struct osl_object *obj) +static int get_hash_object_of_aft_row(const struct osl_row *row, + struct osl_object *obj) { - return osl_get_object(audio_file_table, row, AFTCOL_HASH, obj); + return osl(osl_get_object(audio_file_table, row, AFTCOL_HASH, obj)); } /** @@ -579,8 +619,8 @@ static int get_hash_of_row(const struct osl_row *row, HASH_TYPE **hash) int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi) { struct osl_object obj; - int ret = osl_get_object(audio_file_table, row, AFTCOL_AFHI, - &obj); + int ret = osl(osl_get_object(audio_file_table, row, AFTCOL_AFHI, + &obj)); if (ret < 0) return ret; load_afhi(obj.data, afhi); @@ -612,6 +652,18 @@ err: return ret; } +/** + * Extract a afd stored in a shared memory area. + * + * Attach the shared memory area given by \a shmid, load the audio file data + * stored therein and detach the area afterwards. Called by vss, after + * receiving a positive response to the request for the next audio file. + + + * \param shmid The identifier of the shared memory area containing the afd. + * \param afd Result pointer. + * + * \return Standard. + */ int load_afd(int shmid, struct audio_file_data *afd) { void *shm_afd; @@ -691,56 +743,68 @@ static void get_duration_buf(int seconds, char *buf, struct ls_options *opts) } } -static char *make_attribute_lines(const char *att_bitmap, struct afs_info *afsi) + +static int write_attribute_items(struct para_buffer *b, + const char *att_bitmap, struct afs_info *afsi) { - char *att_text, *att_lines; + char *att_text; + int ret; - get_attribute_text(&afsi->attributes, " ", &att_text); - if (!att_text) - return para_strdup(att_bitmap); - att_lines = make_message("%s: %s\n%s: %s", - status_item_list[SI_ATTRIBUTES_BITMAP], att_bitmap, - status_item_list[SI_ATTRIBUTES_TXT], att_text); + ret = WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_BITMAP, "%s\n", att_bitmap); + if (ret < 0) + return ret; + ret = get_attribute_text(&afsi->attributes, " ", &att_text); + if (ret < 0) + return ret; + ret = WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_TXT, "%s\n", att_text); free(att_text); - return att_lines; + return ret; } -static char *make_lyrics_lines(struct afs_info *afsi) +static int write_lyrics_items(struct para_buffer *b, struct afs_info *afsi) { char *lyrics_name; + int ret; + ret = WRITE_STATUS_ITEM(b, SI_LYRICS_ID, "%u\n", afsi->lyrics_id); + if (ret < 0) + return ret; lyr_get_name_by_id(afsi->lyrics_id, &lyrics_name); - return make_message("%s: %u\n%s: %s\n", - status_item_list[SI_LYRICS_ID], afsi->lyrics_id, - status_item_list[SI_LYRICS_NAME], lyrics_name? - lyrics_name : "(none)"); + return WRITE_STATUS_ITEM(b, SI_LYRICS_NAME, "%s\n", lyrics_name? + lyrics_name : "(none)"); } -static char *make_image_lines(struct afs_info *afsi) +static int write_image_items(struct para_buffer *b, struct afs_info *afsi) { char *image_name; + int ret; + + ret = WRITE_STATUS_ITEM(b, SI_IMAGE_ID, "%u\n", afsi->image_id); + if (ret < 0) + return ret; img_get_name_by_id(afsi->image_id, &image_name); - return make_message("%s: %u\n%s: %s\n", - status_item_list[SI_IMAGE_ID], afsi->image_id, - status_item_list[SI_IMAGE_NAME], image_name? - image_name : "(none)"); + return WRITE_STATUS_ITEM(b, SI_IMAGE_NAME, "%s\n", image_name? + image_name : "(none)"); } -static char *make_filename_lines(const char *path, unsigned flags) +static int write_filename_items(struct para_buffer *b, const char *path, + unsigned flags) { - char *dirname, *ret; - const char *basename; + char *val; + int ret; if (!(flags & LS_FLAG_FULL_PATH)) - return make_message("%s: %s\n", - status_item_list[SI_BASENAME], path); - basename = para_basename(path), - dirname = para_dirname(path); - ret = make_message("%s: %s\n%s: %s\n%s: %s\n", - status_item_list[SI_PATH], path, - status_item_list[SI_DIRECTORY], dirname? dirname : "?", - status_item_list[SI_BASENAME], basename? basename : "?"); - free(dirname); + return WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", path); + ret = WRITE_STATUS_ITEM(b, SI_PATH, "%s\n", path); + if (ret < 0) + return ret; + val = para_basename(path); + ret = WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", val? val : ""); + if (ret < 0) + return ret; + val = para_dirname(path); + ret = WRITE_STATUS_ITEM(b, SI_DIRECTORY, "%s\n", val? val : ""); + free(val); return ret; } @@ -778,6 +842,14 @@ out: return ret; } +static int write_score(struct para_buffer *b, struct ls_data *d, + struct ls_options *opts) +{ + if (!(opts->flags & LS_FLAG_ADMISSIBLE_ONLY)) /* no score*/ + return 0; + return WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score); +} + static int print_list_item(struct ls_data *d, struct ls_options *opts, struct para_buffer *b, time_t current_time) { @@ -785,13 +857,9 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, char att_buf[65]; char last_played_time[30]; char duration_buf[30]; /* nobody has an audio file long enough to overflow this */ - char score_buf[30] = ""; struct afs_info *afsi = &d->afsi; struct afh_info *afhi = &d->afhi; - struct ls_widths *w = &opts->widths; - int have_score = opts->flags & LS_FLAG_ADMISSIBLE_ONLY; char asc_hash[2 * HASH_SIZE + 1]; - char *att_lines, *lyrics_lines, *image_lines, *filename_lines; if (opts->mode == LS_MODE_SHORT) { ret = para_printf(b, "%s\n", d->path); @@ -812,16 +880,15 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, goto out; } get_duration_buf(afhi->seconds_total, duration_buf, opts); - if (have_score) { - if (opts->mode == LS_MODE_LONG) - sprintf(score_buf, "%*li ", w->score_width, d->score); - else - sprintf(score_buf, "%li ", d->score); - } - if (opts->mode == LS_MODE_LONG) { + struct ls_widths *w = &opts->widths; + if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY) { + ret = para_printf(b, "%*li ", + opts->widths.score_width, d->score); + if (ret < 0) + goto out; + } ret = para_printf(b, - "%s" /* score */ "%s " /* attributes */ "%*u " /* amp */ "%*d " /* image_id */ @@ -834,7 +901,6 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, "%*d " /* num_played */ "%s " /* last_played */ "%s\n", /* path */ - score_buf, att_buf, w->amp_width, afsi->amp, w->image_id_width, afsi->image_id, @@ -850,11 +916,6 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, ); goto out; } - hash_to_asc(d->hash, asc_hash); - att_lines = make_attribute_lines(att_buf, afsi); - lyrics_lines = make_lyrics_lines(afsi); - image_lines = make_image_lines(afsi); - filename_lines = make_filename_lines(d->path, opts->flags); if (opts->mode == LS_MODE_MBOX) { const char *bn = para_basename(d->path); ret = para_printf(b, @@ -866,47 +927,78 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, if (ret < 0) goto out; } - ret = para_printf(b, - "%s" /* filename stuff */ - "%s%s%s%s" /* score */ - "%s\n" /* attributes */ - "%s: %s\n" /* hash */ - "%s" /* image id, image name */ - "%s" /* lyrics */ - "%s: %dkbit/s\n" /* bitrate */ - "%s: %s\n" /* format */ - "%s: %dHz\n" /* frequency */ - "%s: %d\n" /* channels */ - "%s: %s\n" /* duration */ - "%s: %lu\n" /* seconds total */ - "%s: %s\n" /* last played time */ - "%s: %d\n" /* num_played */ - "%s: %u\n" /* ampplification */ - "%s" /* tag info */ - "%s: %lu\n" /* chunk time */ - "%s: %lu\n", /* num chunks */ - filename_lines, - have_score? status_item_list[SI_SCORE] : "", - have_score? ": " : "", - score_buf, - have_score? "\n" : "", - att_lines, - status_item_list[SI_HASH], asc_hash, - image_lines, - lyrics_lines, - status_item_list[SI_BITRATE], afhi->bitrate, - status_item_list[SI_FORMAT], audio_format_name(afsi->audio_format_id), - status_item_list[SI_FREQUENCY], afhi->frequency, - status_item_list[SI_CHANNELS], afhi->channels, - status_item_list[SI_DURATION], duration_buf, - status_item_list[SI_SECONDS_TOTAL], afhi->seconds_total, - status_item_list[SI_LAST_PLAYED], last_played_time, - status_item_list[SI_NUM_PLAYED], afsi->num_played, - status_item_list[SI_AMPLIFICATION], afsi->amp, - afhi->info_string, - status_item_list[SI_CHUNK_TIME], tv2ms(&afhi->chunk_tv), - status_item_list[SI_NUM_CHUNKS], afhi->chunks_total - ); + ret = write_filename_items(b, d->path, opts->flags); + if (ret < 0) + goto out; + ret = write_score(b, d, opts); + if (ret < 0) + goto out; + ret = write_attribute_items(b, att_buf, afsi); + if (ret < 0) + goto out; + ret = write_image_items(b, afsi); + if (ret < 0) + goto out; + ret = write_lyrics_items(b, afsi); + if (ret < 0) + goto out; + hash_to_asc(d->hash, asc_hash); + ret = WRITE_STATUS_ITEM(b, SI_HASH, "%s\n", asc_hash); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_BITRATE, "%dkbit/s\n", afhi->bitrate); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_FORMAT, "%s\n", + audio_format_name(afsi->audio_format_id)); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_FREQUENCY, "%dHz\n", afhi->frequency); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_CHANNELS, "%d\n", afhi->channels); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_DURATION, "%s\n", duration_buf); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%lu\n", + afhi->seconds_total); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_LAST_PLAYED, "%s\n", last_played_time); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%d\n", afsi->num_played); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_AMPLIFICATION, "%u\n", afsi->amp); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n", + tv2ms(&afhi->chunk_tv)); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%lu\n", + afhi->chunks_total); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_TECHINFO, "%s\n", afhi->techinfo); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_ARTIST, "%s\n", afhi->tags.artist); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_TITLE, "%s\n", afhi->tags.title); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_YEAR, "%s\n", afhi->tags.year); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_ALBUM, "%s\n", afhi->tags.album); + if (ret < 0) + goto out; + ret = WRITE_STATUS_ITEM(b, SI_COMMENT, "%s\n", afhi->tags.comment); if (ret < 0) goto out; if (opts->mode == LS_MODE_MBOX) { @@ -918,129 +1010,10 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, osl_close_disk_object(&lyrics_def); } } - free(att_lines); - free(lyrics_lines); - free(image_lines); - free(filename_lines); out: - free(afhi->info_string); return ret; } -/** - * Write a list of audio-file related status items with empty values. - * - * \param buf Result pointer. - * - * This is used by vss when currently no audio file is open. - */ -void make_empty_status_items(char *buf) -{ - sprintf(buf, - "%s: \n" /* path */ - "%s: \n" /* dirname */ - "%s: \n" /* basename */ - "%s: \n" /* score */ - "%s: \n" /* attributes bitmap */ - "%s: \n" /* attributes txt */ - "%s: \n" /* hash */ - "%s: \n" /* image id */ - "%s: \n" /* image name */ - "%s: \n" /* lyrics id */ - "%s: \n" /* lyrics name */ - "%s: \n" /* bitrate */ - "%s: \n" /* format */ - "%s: \n" /* frequency */ - "%s: \n" /* channels */ - "%s: \n" /* duration */ - "%s: \n" /* seconds total */ - "%s: \n" /* num played */ - "%s: \n" /* last played */ - "%s: \n" /* audio file info */ - "%s: \n" /* taginfo1 */ - "%s: \n" /* taginfo2 */ - "%s: \n" /* amplification */ - , - status_item_list[SI_PATH], - status_item_list[SI_DIRECTORY], - status_item_list[SI_BASENAME], - status_item_list[SI_SCORE], - status_item_list[SI_ATTRIBUTES_BITMAP], - status_item_list[SI_ATTRIBUTES_TXT], - status_item_list[SI_HASH], - status_item_list[SI_IMAGE_ID], - status_item_list[SI_IMAGE_NAME], - status_item_list[SI_LYRICS_ID], - status_item_list[SI_LYRICS_NAME], - status_item_list[SI_BITRATE], - status_item_list[SI_FORMAT], - status_item_list[SI_FREQUENCY], - status_item_list[SI_CHANNELS], - status_item_list[SI_DURATION], - status_item_list[SI_SECONDS_TOTAL], - status_item_list[SI_NUM_PLAYED], - status_item_list[SI_LAST_PLAYED], - status_item_list[SI_AUDIO_FILE_INFO], - status_item_list[SI_TAGINFO1], - status_item_list[SI_TAGINFO2], - status_item_list[SI_AMPLIFICATION] - ); -} - -static void fixup_taginfo(char *begin, char *end) -{ - char *p = begin; - - for (;;) { - p = strchr(p, '\n'); - if (!p) - break; - if (p >= end - 1) - break; - *p = ' '; - p++; - } -} - -/* crap, remove this ASAP. */ -static int fixup_info_string(char *info_string) -{ - char *t1, *t2, *end; - - if (strncmp(info_string, "audio_file_info:", 16)) - return -ERRNO_TO_PARA_ERROR(EINVAL); - t1 = strstr(info_string, "\ntaginfo1:"); - if (!t1) - return -ERRNO_TO_PARA_ERROR(EINVAL); - t2 = strstr(info_string, "\ntaginfo2: "); - if (!t2) - return -ERRNO_TO_PARA_ERROR(EINVAL); - - end = t2 + strlen(t2); - fixup_taginfo(info_string + 16, t1); - fixup_taginfo(t1 + 10, t2); - fixup_taginfo(t2 + 10, end); - - if (t1 - info_string < 80 && t2 - t1 < 80 && end - t2 < 80) - return 0; - if (t1 - info_string >= 80) { - memmove(info_string + 80, t1, end - t1); - t1 = info_string + 80; - t2 -= t1 - info_string - 80; - end -= t1 - info_string - 80; - } - if (t2 - t1 >= 80) { - memmove(t1 + 80, t2, end - t2); - end -= t2 - t1 - 80; - t2 = t1 + 80; - } - if (end - t2 >= 80) { - t2[78] = '\n'; - t2[79] = '\0'; - } - return 1; -} - static int make_status_items(struct audio_file_data *afd, struct afs_info *afsi, char *path, long score, HASH_TYPE *hash) @@ -1056,26 +1029,28 @@ static int make_status_items(struct audio_file_data *afd, .flags = LS_FLAG_FULL_PATH | LS_FLAG_ADMISSIBLE_ONLY, .mode = LS_MODE_VERBOSE, }; - struct para_buffer pb = {.max_size = VERBOSE_LS_OUTPUT_SIZE - 1}; + struct para_buffer pb = {.max_size = SHMMAX - 1}; time_t current_time; int ret; - ret = fixup_info_string(afd->afhi.info_string); - if (ret < 0) { - PARA_WARNING_LOG("ignoring invalid tag info\n"); - afd->afhi.info_string[0] = '\0'; - } else if (ret) - PARA_NOTICE_LOG("truncated overlong tag info\n"); time(¤t_time); - ret = print_list_item(&d, &opts, &pb, current_time); /* frees info string */ - afd->afhi.info_string = NULL; + ret = print_list_item(&d, &opts, &pb, current_time); if (ret < 0) - goto out; - strncpy(afd->verbose_ls_output, pb.buf, VERBOSE_LS_OUTPUT_SIZE); - afd->verbose_ls_output[VERBOSE_LS_OUTPUT_SIZE - 1] = '\0'; -out: - free(pb.buf); - return ret; + return ret; + free(status_items); + status_items = pb.buf; + memset(&pb, 0, sizeof(pb)); + pb.max_size = SHMMAX - 1; + pb.flags = PBF_SIZE_PREFIX; + ret = print_list_item(&d, &opts, &pb, current_time); + if (ret < 0) { + free(status_items); + status_items = NULL; + return ret; + } + free(parser_friendly_status_items); + parser_friendly_status_items = pb.buf; + return 1; } /** @@ -1107,6 +1082,7 @@ int open_and_update_audio_file(struct osl_row *aft_row, long score, ret = get_audio_file_path_of_row(aft_row, &path); if (ret < 0) return ret; + PARA_NOTICE_LOG("%s\n", path); ret = get_afsi_object_of_row(aft_row, &afsi_obj); if (ret < 0) return ret; @@ -1147,7 +1123,6 @@ int open_and_update_audio_file(struct osl_row *aft_row, long score, ret = save_afd(afd); err: free(afd->afhi.chunk_table); - free(afd->afhi.info_string); osl_close_disk_object(&chunk_table_obj); return ret; } @@ -1350,7 +1325,6 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts) } return 1; err: - free(d->afhi.info_string); return ret; } @@ -1359,11 +1333,11 @@ static void com_ls_callback(int fd, const struct osl_object *query) struct ls_options *opts = query->data; char *p, *pattern_start = (char *)query->data + sizeof(*opts); struct para_buffer b = {.max_size = SHMMAX, + .flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0, .max_size_handler = pass_buffer_as_shm, .private_data = &fd}; int i = 0, ret; time_t current_time; - if (opts->num_patterns) { opts->patterns = para_malloc(opts->num_patterns * sizeof(char *)); for (i = 0, p = pattern_start; i < opts->num_patterns; i++) { @@ -1375,8 +1349,8 @@ static void com_ls_callback(int fd, const struct osl_object *query) if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY) ret = admissible_file_loop(opts, prepare_ls_row); else - ret = osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts, - prepare_ls_row); + ret = osl(osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts, + prepare_ls_row)); if (ret < 0) goto out; if (!opts->num_matching_paths) @@ -1409,7 +1383,7 @@ out: /* * TODO: flags -h (sort by hash) */ -int com_ls(int fd, int argc, char * const * const argv) +int com_ls(struct rc4_context *rc4c, int argc, char * const * const argv) { int i, ret; unsigned flags = 0; @@ -1449,6 +1423,9 @@ int com_ls(int fd, int argc, char * const * const argv) case 'c': mode = LS_MODE_CHUNKS; continue; + case 'p': + mode = LS_MODE_PARSER; + continue; default: return -E_AFT_SYNTAX; } @@ -1518,7 +1495,7 @@ int com_ls(int fd, int argc, char * const * const argv) opts.mode = mode; opts.num_patterns = argc - i; ret = send_option_arg_callback_request(&query, opts.num_patterns, - argv + i, com_ls_callback, send_result, &fd); + argv + i, com_ls_callback, rc4_send_result, rc4c); return ret; } @@ -1528,12 +1505,12 @@ int com_ls(int fd, int argc, char * const * const argv) * \param private_data An arbitrary data pointer, passed to \a func. * \param func The custom function to be called. * - * \return The return value of the underlying call to osl_rbtree_loop(). + * \return Standard. */ int audio_file_loop(void *private_data, osl_rbtree_loop_func *func) { - return osl_rbtree_loop(audio_file_table, AFTCOL_HASH, private_data, - func); + return osl(osl_rbtree_loop(audio_file_table, AFTCOL_HASH, private_data, + func)); } static struct osl_row *find_hash_sister(HASH_TYPE *hash) @@ -1693,7 +1670,7 @@ static void com_add_callback(int fd, const struct osl_object *query) PARA_INFO_LOG("request to add %s\n", path); hs = find_hash_sister(hash); ret = aft_get_row_of_path(path, &pb); - if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND) + if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) goto out; if (hs && pb && hs == pb && !(flags & ADD_FLAG_FORCE)) { if (flags & ADD_FLAG_VERBOSE) @@ -1710,23 +1687,23 @@ static void com_add_callback(int fd, const struct osl_object *query) if (ret < 0) goto out; } - ret = osl_del_row(audio_file_table, pb); + ret = osl(osl_del_row(audio_file_table, pb)); if (ret < 0) goto out; pb = NULL; } /* file rename, update hs' path */ if (flags & ADD_FLAG_VERBOSE) { - ret = osl_get_object(audio_file_table, hs, - AFTCOL_PATH, &obj); + ret = osl(osl_get_object(audio_file_table, hs, + AFTCOL_PATH, &obj)); if (ret < 0) goto out; ret = para_printf(&msg, "renamed from %s\n", (char *)obj.data); if (ret < 0) goto out; } - ret = osl_update_object(audio_file_table, hs, AFTCOL_PATH, - &objs[AFTCOL_PATH]); + ret = osl(osl_update_object(audio_file_table, hs, AFTCOL_PATH, + &objs[AFTCOL_PATH])); if (ret < 0) goto out; afs_event(AUDIO_FILE_RENAME, &msg, hs); @@ -1770,12 +1747,12 @@ static void com_add_callback(int fd, const struct osl_object *query) if (ret < 0) goto out; } - ret = osl_update_object(audio_file_table, row, AFTCOL_AFHI, - &objs[AFTCOL_AFHI]); + ret = osl(osl_update_object(audio_file_table, row, AFTCOL_AFHI, + &objs[AFTCOL_AFHI])); if (ret < 0) goto out; - ret = osl_update_object(audio_file_table, row, AFTCOL_CHUNKS, - &objs[AFTCOL_CHUNKS]); + ret = osl(osl_update_object(audio_file_table, row, AFTCOL_CHUNKS, + &objs[AFTCOL_CHUNKS])); if (ret < 0) goto out; afs_event(AFHI_CHANGE, &msg, row); @@ -1793,7 +1770,7 @@ static void com_add_callback(int fd, const struct osl_object *query) objs[AFTCOL_AFSI].data = &afsi_buf; objs[AFTCOL_AFSI].size = AFSI_SIZE; save_afsi(&default_afsi, &objs[AFTCOL_AFSI]); - ret = osl_add_and_get_row(audio_file_table, objs, &aft_row); + ret = osl(osl_add_and_get_row(audio_file_table, objs, &aft_row)); afs_event(AUDIO_FILE_ADD, &msg, aft_row); out: if (ret < 0) @@ -1805,8 +1782,8 @@ out: /** Used by com_add(). */ struct private_add_data { - /** The socket file descriptor. */ - int fd; + /** The socket file descriptor, including rc4 keys. */ + struct rc4_context *rc4c; /** The given add flags. */ uint32_t flags; }; @@ -1856,12 +1833,13 @@ static int add_one_audio_file(const char *path, void *private_data) query.size = strlen(path) + 1; ret = send_callback_request(path_brother_callback, &query, get_row_pointer_from_result, &pb); - if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND) + if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) goto out_free; ret = 1; if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */ if (pad->flags & ADD_FLAG_VERBOSE) - send_ret = send_va_buffer(pad->fd, "lazy-ignore: %s\n", path); + send_ret = rc4_send_va_buffer(pad->rc4c, + "lazy-ignore: %s\n", path); goto out_free; } /* We still want to add this file. Compute its hash. */ @@ -1881,7 +1859,7 @@ static int add_one_audio_file(const char *path, void *private_data) ret = 1; if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) { if (pad->flags & ADD_FLAG_VERBOSE) - send_ret = send_va_buffer(pad->fd, + send_ret = rc4_send_va_buffer(pad->rc4c, "%s exists, not forcing update\n", path); goto out_unmap; } @@ -1899,13 +1877,13 @@ static int add_one_audio_file(const char *path, void *private_data) munmap(map.data, map.size); close(fd); if (pad->flags & ADD_FLAG_VERBOSE) { - send_ret = send_va_buffer(pad->fd, "adding %s\n", path); + send_ret = rc4_send_va_buffer(pad->rc4c, "adding %s\n", path); if (send_ret < 0) goto out_free; } save_add_callback_buffer(hash, path, afhi_ptr, pad->flags, format_num, &obj); /* Ask afs to consider this entry for adding. */ - ret = send_callback_request(com_add_callback, &obj, send_result, &pad->fd); + ret = send_callback_request(com_add_callback, &obj, rc4_send_result, pad->rc4c); goto out_free; out_unmap: @@ -1913,21 +1891,26 @@ out_unmap: munmap(map.data, map.size); out_free: if (ret < 0 && send_ret >= 0) - send_ret = send_va_buffer(pad->fd, "failed to add %s (%s)\n", path, - para_strerror(-ret)); + send_ret = rc4_send_va_buffer(pad->rc4c, + "failed to add %s (%s)\n", path, para_strerror(-ret)); free(obj.data); if (afhi_ptr) { free(afhi_ptr->chunk_table); - free(afhi_ptr->info_string); + free(afhi_ptr->techinfo); + free(afhi_ptr->tags.artist); + free(afhi_ptr->tags.title); + free(afhi_ptr->tags.year); + free(afhi_ptr->tags.album); + free(afhi_ptr->tags.comment); } /* Stop adding files only on send errors. */ return send_ret; } -int com_add(int fd, int argc, char * const * const argv) +int com_add(struct rc4_context *rc4c, int argc, char * const * const argv) { int i, ret; - struct private_add_data pad = {.fd = fd, .flags = 0}; + struct private_add_data pad = {.rc4c = rc4c, .flags = 0}; struct stat statbuf; for (i = 1; i < argc; i++) { @@ -1961,7 +1944,7 @@ int com_add(int fd, int argc, char * const * const argv) char *path; ret = verify_path(argv[i], &path); if (ret < 0) { - ret = send_va_buffer(fd, "%s: %s\n", argv[i], + ret = rc4_send_va_buffer(rc4c, "%s: %s\n", argv[i], para_strerror(-ret)); if (ret < 0) return ret; @@ -1969,7 +1952,7 @@ int com_add(int fd, int argc, char * const * const argv) } ret = stat(path, &statbuf); if (ret < 0) { - ret = send_va_buffer(fd, "failed to stat %s (%s)\n", path, + ret = rc4_send_va_buffer(rc4c, "failed to stat %s (%s)\n", path, strerror(errno)); free(path); if (ret < 0) @@ -1982,7 +1965,7 @@ int com_add(int fd, int argc, char * const * const argv) else ret = add_one_audio_file(path, &pad); if (ret < 0) { - send_va_buffer(fd, "%s: %s\n", path, para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s: %s\n", path, para_strerror(-ret)); free(path); return ret; } @@ -2114,7 +2097,7 @@ static void com_touch_callback(int fd, const struct osl_object *query) free(tad.pb.buf); } -int com_touch(int fd, int argc, char * const * const argv) +int com_touch(struct rc4_context *rc4c, int argc, char * const * const argv) { struct com_touch_options cto = { .num_played = -1, @@ -2135,33 +2118,33 @@ int com_touch(int fd, int argc, char * const * const argv) i++; break; } - if (!strncmp(arg, "-n", 2)) { - ret = para_atoi32(arg + 2, &cto.num_played); + if (!strncmp(arg, "-n=", 3)) { + ret = para_atoi32(arg + 3, &cto.num_played); if (ret < 0) return ret; continue; } - if (!strncmp(arg, "-l", 2)) { - ret = para_atoi64(arg + 2, &cto.last_played); + if (!strncmp(arg, "-l=", 3)) { + ret = para_atoi64(arg + 3, &cto.last_played); if (ret < 0) return ret; continue; } - if (!strncmp(arg, "-y", 2)) { - ret = para_atoi32(arg + 2, &cto.lyrics_id); + if (!strncmp(arg, "-y=", 3)) { + ret = para_atoi32(arg + 3, &cto.lyrics_id); if (ret < 0) return ret; continue; } - if (!strncmp(arg, "-i", 2)) { - ret = para_atoi32(arg + 2, &cto.image_id); + if (!strncmp(arg, "-i=", 3)) { + ret = para_atoi32(arg + 3, &cto.image_id); if (ret < 0) return ret; continue; } - if (!strncmp(arg, "-a", 2)) { + if (!strncmp(arg, "-a=", 3)) { int32_t val; - ret = para_atoi32(arg + 2, &val); + ret = para_atoi32(arg + 3, &val); if (ret < 0) return ret; if (val < 0 || val > 255) @@ -2182,9 +2165,9 @@ int com_touch(int fd, int argc, char * const * const argv) if (i >= argc) return -E_AFT_SYNTAX; ret = send_option_arg_callback_request(&query, argc - i, - argv + i, com_touch_callback, send_result, &fd); + argv + i, com_touch_callback, rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -2220,7 +2203,7 @@ static int remove_audio_file(__a_unused struct osl_table *table, return ret; } afs_event(AUDIO_FILE_REMOVE, &crd->pb, row); - ret = osl_del_row(audio_file_table, row); + ret = osl(osl_del_row(audio_file_table, row)); if (ret < 0) para_printf(&crd->pb, "%s: %s\n", name, para_strerror(-ret)); else @@ -2266,7 +2249,7 @@ static void com_rm_callback(int fd, const struct osl_object *query) } /* TODO options: -r (recursive) */ -int com_rm(int fd, int argc, char * const * const argv) +int com_rm(struct rc4_context *rc4c, int argc, char * const * const argv) { uint32_t flags = 0; struct osl_object query = {.data = &flags, .size = sizeof(flags)}; @@ -2297,9 +2280,9 @@ int com_rm(int fd, int argc, char * const * const argv) if (i >= argc) return -E_AFT_SYNTAX; ret = send_option_arg_callback_request(&query, argc - i, argv + i, - com_rm_callback, send_result, &fd); + com_rm_callback, rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -2414,7 +2397,7 @@ out: free(cad.pb.buf); } -int com_cpsi(int fd, int argc, char * const * const argv) +int com_cpsi(struct rc4_context *rc4c, int argc, char * const * const argv) { unsigned flags = 0; int i, ret; @@ -2454,17 +2437,48 @@ int com_cpsi(int fd, int argc, char * const * const argv) } break; } - if (i + 1 >= argc) /* need at least souce file and pattern */ + if (i + 1 >= argc) /* need at least source file and pattern */ return -E_AFT_SYNTAX; if (!(flags & ~CPSI_FLAG_VERBOSE)) /* no copy flags given */ flags = ~(unsigned)CPSI_FLAG_VERBOSE | flags; ret = send_option_arg_callback_request(&options, argc - i, argv + i, - com_cpsi_callback, send_result, &fd); + com_cpsi_callback, rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } +static void afs_stat_callback(int fd, const struct osl_object *query) +{ + int *parser_friendly = query->data; + char *buf = *parser_friendly? + parser_friendly_status_items : status_items; + + if (!buf) + return; + pass_buffer_as_shm(buf, strlen(buf), &fd); +} + +/** + * Get the current afs status items from the afs process and send it using RC4. + * + * \param rc4c The rc4 context for data encryption. + * \param parser_friendly Whether parser-friendly output format should be used. + * + * As the contents of the afs status items change in time and the command + * handler only has a COW version created at fork time, it can not send + * up-to-date afs status items directly. Therefore the usual callback mechanism + * is used to pass the status items from the afs process to the command handler + * via a shared memory area and a pipe. + */ +int send_afs_status(struct rc4_context *rc4c, int parser_friendly) +{ + struct osl_object query = {.data = &parser_friendly, + .size = sizeof(parser_friendly)}; + + return send_callback_request(afs_stat_callback, &query, rc4_send_result, rc4c); +} + /* TODO: optionally fix problems by removing offending rows */ static int check_audio_file(struct osl_row *row, void *data) { @@ -2543,6 +2557,10 @@ static void aft_close(void) { osl_close_table(audio_file_table, OSL_MARK_CLEAN); audio_file_table = NULL; + free(status_items); + status_items = NULL; + free(parser_friendly_status_items); + parser_friendly_status_items = NULL; } /** @@ -2559,7 +2577,7 @@ static int aft_open(const char *dir) int ret; audio_file_table_desc.dir = dir; - ret = osl_open_table(&audio_file_table_desc, &audio_file_table); + ret = osl(osl_open_table(&audio_file_table_desc, &audio_file_table)); if (ret >= 0) { unsigned num; osl_get_num_rows(audio_file_table, &num); @@ -2568,7 +2586,7 @@ static int aft_open(const char *dir) } PARA_INFO_LOG("failed to open audio file table\n"); audio_file_table = NULL; - if (ret >= 0 || is_errno(-ret, ENOENT)) + if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT)) return 1; return ret; } @@ -2576,7 +2594,7 @@ static int aft_open(const char *dir) static int aft_create(const char *dir) { audio_file_table_desc.dir = dir; - return osl_create_table(&audio_file_table_desc); + return osl(osl_create_table(&audio_file_table_desc)); } static int clear_attribute(struct osl_row *row, void *data) @@ -2617,6 +2635,11 @@ static int aft_event_handler(enum afs_events event, struct para_buffer *pb, } } +/** + * Initialize the audio file table. + * + * \param t Pointer to the structure to be initialized. + */ void aft_init(struct afs_table *t) { t->open = aft_open; diff --git a/alsa_write.c b/alsa_write.c index 53c97384..7a4b868d 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -12,9 +12,12 @@ * based on the vplay program by Michael Beck. */ +#include #include #include #include +#include +#include #include "para.h" #include "fd.h" @@ -22,7 +25,9 @@ #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "write.h" +#include "write_common.h" #include "alsa_write.cmdline.h" #include "error.h" @@ -38,7 +43,7 @@ struct private_alsa_write_data { /** The approximate maximum buffer duration in us. */ unsigned buffer_time; /* Number of frames that fit into the buffer. */ - unsigned buffer_frames; + snd_pcm_uframes_t buffer_frames; /** * The samplerate given by command line option or the decoder * of the writer node group. @@ -49,6 +54,7 @@ struct private_alsa_write_data { * decoder of the writer node group. */ unsigned channels; + struct timeval drain_barrier; }; /* Install PCM software and hardware configuration. */ @@ -57,10 +63,11 @@ static int alsa_init(struct private_alsa_write_data *pad, { snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; - snd_pcm_uframes_t buffer_size, start_threshold, stop_threshold; + snd_pcm_uframes_t start_threshold, stop_threshold; snd_pcm_uframes_t period_size; int err; + PARA_INFO_LOG("opening %s\n", conf->device_arg); err = snd_pcm_open(&pad->handle, conf->device_arg, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); if (err < 0) @@ -92,22 +99,22 @@ static int alsa_init(struct private_alsa_write_data *pad, if (snd_pcm_hw_params(pad->handle, hwparams) < 0) return -E_HW_PARAMS; snd_pcm_hw_params_get_period_size(hwparams, &period_size, NULL); - snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); - PARA_INFO_LOG("buffer size: %lu, period_size: %lu\n", buffer_size, + snd_pcm_hw_params_get_buffer_size(hwparams, &pad->buffer_frames); + PARA_INFO_LOG("buffer size: %lu, period_size: %lu\n", pad->buffer_frames, period_size); - if (period_size == buffer_size) + if (period_size == pad->buffer_frames) return -E_BAD_PERIOD; snd_pcm_sw_params_current(pad->handle, swparams); snd_pcm_sw_params_set_avail_min(pad->handle, swparams, period_size); - if (buffer_size < 1) + if (pad->buffer_frames < 1) start_threshold = 1; else - start_threshold = PARA_MIN(buffer_size, + start_threshold = PARA_MIN(pad->buffer_frames, (snd_pcm_uframes_t)pad->samplerate); if (snd_pcm_sw_params_set_start_threshold(pad->handle, swparams, start_threshold) < 0) return -E_START_THRESHOLD; - stop_threshold = buffer_size; + stop_threshold = pad->buffer_frames; if (snd_pcm_sw_params_set_stop_threshold(pad->handle, swparams, stop_threshold) < 0) return -E_STOP_THRESHOLD; @@ -120,42 +127,31 @@ static int alsa_init(struct private_alsa_write_data *pad, PARA_INFO_LOG("bytes per frame: %d\n", pad->bytes_per_frame); if (snd_pcm_nonblock(pad->handle, 1)) PARA_ERROR_LOG("failed to set nonblock mode\n"); - pad->buffer_frames = 1000 * pad->buffer_time / pad->samplerate; - PARA_INFO_LOG("max buffered frames: %d\n", pad->buffer_frames); return 1; } /* Open an instance of the alsa writer. */ static int alsa_open(struct writer_node *wn) { - struct alsa_write_args_info *conf = wn->conf; - struct writer_node_group *wng = wn->wng; - struct private_alsa_write_data *pad = para_calloc(sizeof(*pad)); - - wn->private_data = pad; - if (!conf->samplerate_given && wng->samplerate) - pad->samplerate = *wng->samplerate; - else - pad->samplerate = conf->samplerate_arg; - if (!conf->channels_given && wng->channels) - pad->channels = *wng->channels; - else - pad->channels = conf->channels_arg; - PARA_INFO_LOG("%d channel(s), %dHz\n", pad->channels, pad->samplerate); + wn->private_data = para_calloc(sizeof(struct private_alsa_write_data)); return 1; } -static int alsa_write_pre_select(struct sched *s, struct writer_node *wn) +static void alsa_write_pre_select(struct sched *s, struct task *t) { + struct writer_node *wn = container_of(t, struct writer_node, task); struct private_alsa_write_data *pad = wn->private_data; - struct writer_node_group *wng = wn->wng; struct timeval tv; snd_pcm_sframes_t avail, underrun; + int ret; if (!pad->handle) - return 1; - if (*wng->loaded - wn->written < pad->bytes_per_frame) - return 1; + return; + ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); + if (ret < 0) + sched_request_timeout_ms(20, s); + if (ret <= 0) + return; /* * Data is available to be written to the alsa handle. Compute number * of milliseconds until next buffer underrun would occur. @@ -177,49 +173,6 @@ static int alsa_write_pre_select(struct sched *s, struct writer_node *wn) ms2tv(underrun, &tv); if (tv_diff(&s->timeout, &tv, NULL) > 0) s->timeout = tv; - return 1; -} - -static int alsa_write_post_select(__a_unused struct sched *s, - struct writer_node *wn) -{ - struct private_alsa_write_data *pad = wn->private_data; - struct writer_node_group *wng = wn->wng; - size_t bytes = *wng->loaded - wn->written; - unsigned char *data = (unsigned char*)*wng->bufp + wn->written; - snd_pcm_sframes_t ret, frames, avail; - - if (*wng->input_error < 0 && (!pad->handle || bytes < pad->bytes_per_frame)) { - wn->written = *wng->loaded; - return *wng->input_error; - } - if (!bytes) /* no data available */ - return 0; - if (!pad->handle) { - int err = alsa_init(pad, wn->conf); - if (err < 0) - return err; - } - frames = bytes / pad->bytes_per_frame; - if (!frames) /* less than a single frame available */ - return 0; - avail = snd_pcm_avail_update(pad->handle); - if (avail <= 0) - return 0; - frames = PARA_MIN(frames, avail); - ret = snd_pcm_writei(pad->handle, data, frames); - if (ret >= 0) { - wn->written += ret * pad->bytes_per_frame; - return 1; - } - PARA_WARNING_LOG("%s\n", snd_strerror(-ret)); - if (ret == -EPIPE) { - snd_pcm_prepare(pad->handle); - return 0; - } - if (ret == -EAGAIN) - return 0; - return -E_ALSA_WRITE; } static void alsa_close(struct writer_node *wn) @@ -228,6 +181,11 @@ static void alsa_close(struct writer_node *wn) PARA_INFO_LOG("closing writer node %p\n", wn); if (pad->handle) { + /* + * It's OK to have a blocking operation here because we already + * made sure that the PCM output buffer is (nearly) empty. + */ + snd_pcm_nonblock(pad->handle, 0); snd_pcm_drain(pad->handle); snd_pcm_close(pad->handle); snd_config_update_free_global(); @@ -235,6 +193,84 @@ static void alsa_close(struct writer_node *wn) free(pad); } +static void alsa_write_post_select(__a_unused struct sched *s, + struct task *t) +{ + struct writer_node *wn = container_of(t, struct writer_node, task); + struct private_alsa_write_data *pad = wn->private_data; + struct btr_node *btrn = wn->btrn; + char *data; + size_t bytes; + snd_pcm_sframes_t frames; + int ret; + +again: + t->error = 0; + ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF); + if (ret == 0) + return; + btr_merge(btrn, wn->min_iqs); + bytes = btr_next_buffer(btrn, &data); + if (ret < 0 || bytes < pad->bytes_per_frame) { /* eof */ + assert(btr_no_parent(btrn)); + ret = -E_ALSA_EOF; + if (!pad->handle) + goto err; + /* wait until pending frames are played */ + if (pad->drain_barrier.tv_sec == 0) { + PARA_DEBUG_LOG("waiting for device to drain\n"); + tv_add(now, &(struct timeval)EMBRACE(0, 200 * 1000), + &pad->drain_barrier); + return; + } + if (tv_diff(now, &pad->drain_barrier, NULL) > 0) + goto err; + return; + } + if (!pad->handle) { + struct alsa_write_args_info *conf = wn->conf; + if (bytes == 0) /* no data available */ + return; + /* defaults */ + pad->samplerate = conf->samplerate_arg; + pad->channels = conf->channels_arg; + if (!conf->samplerate_given) { /* config option trumps btr_exec */ + int32_t rate; + if (get_btr_samplerate(btrn, &rate) >= 0) + pad->samplerate = rate; + } + if (!conf->channels_given) { + int32_t ch; + if (get_btr_channels(btrn, &ch) >= 0) + pad->channels = ch; + } + PARA_INFO_LOG("%d channel(s), %dHz\n", pad->channels, pad->samplerate); + ret = alsa_init(pad, wn->conf); + if (ret < 0) + goto err; + wn->min_iqs = pad->bytes_per_frame; + } + frames = bytes / pad->bytes_per_frame; + frames = snd_pcm_writei(pad->handle, data, frames); + if (frames >= 0) { + btr_consume(btrn, frames * pad->bytes_per_frame); + goto again; + } + if (frames == -EPIPE) { + PARA_WARNING_LOG("underrun (tried to write %zu bytes)\n", bytes); + snd_pcm_prepare(pad->handle); + return; + } + if (frames == -EAGAIN) + return; + PARA_WARNING_LOG("%s\n", snd_strerror(-frames)); + ret = -E_ALSA_WRITE; +err: + assert(ret < 0); + btr_remove_node(btrn); + t->error = ret; +} + __malloc static void *alsa_parse_config(const char *options) { int ret; @@ -252,12 +288,17 @@ err_out: return NULL; } +static void alsa_free_config(void *conf) +{ + alsa_cmdline_parser_free(conf); +} + /** - * the init function of the alsa writer + * The init function of the alsa writer. * - * \param w pointer to the writer to initialize + * \param w Pointer to the writer to initialize. * - * \sa struct writer + * \sa struct \ref writer. */ void alsa_write_init(struct writer *w) { @@ -270,6 +311,7 @@ void alsa_write_init(struct writer *w) w->post_select = alsa_write_post_select; w->parse_config = alsa_parse_config; w->shutdown = NULL; /* nothing to do */ + w->free_config = alsa_free_config; w->help = (struct ggo_help) { .short_help = alsa_write_args_info_help, .detailed_help = alsa_write_args_info_detailed_help diff --git a/amp_filter.c b/amp_filter.c index a011c411..29c62e3b 100644 --- a/amp_filter.c +++ b/amp_filter.c @@ -1,23 +1,24 @@ /* - * Copyright (C) 2009 Andre Noll + * Copyright (C) 2009-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file amp_filter.c Paraslash's amplify filter. */ +#include +#include + #include "para.h" #include "amp_filter.cmdline.h" #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "string.h" #include "error.h" -/** The size of the output data buffer. */ -#define AMP_CHUNK_SIZE 40960 - extern char *stat_item_values[NUM_STAT_ITEMS]; /** Data specific to the amplify filter. */ @@ -28,27 +29,9 @@ struct private_amp_data { unsigned amp; }; -static ssize_t amp_convert(char *inbuf, size_t inbuf_len, struct filter_node *fn) -{ - size_t i, length = PARA_MIN((inbuf_len / 2) * 2, - (fn->bufsize - fn->loaded) / 2 * 2); - struct private_amp_data *pad = fn->private_data; - int16_t *ip = (int16_t *)inbuf, *op = (int16_t *)(fn->buf + fn->loaded); - - if (!length) - return 0; - for (i = 0; i < length / 2; i++) { - int x = (PARA_ABS(*ip) * (64 + pad->amp)) >> 6; - *op++ = *ip++ > 0? PARA_MIN(x, 32767) : PARA_MAX(-x, -32768); - } - fn->loaded += length; - return length; -} - static void amp_close(struct filter_node *fn) { free(fn->private_data); - free(fn->buf); } static int amp_parse_config(int argc, char **argv, void **config) @@ -61,8 +44,6 @@ static int amp_parse_config(int argc, char **argv, void **config) ret = -ERRNO_TO_PARA_ERROR(EINVAL); if (amp_conf->amp_arg < 0) goto err; - PARA_NOTICE_LOG("amplification: %u (scaling factor: %1.2f)\n", - amp_conf->amp_arg, amp_conf->amp_arg / 64.0 + 1.0); *config = amp_conf; return 1; err: @@ -76,14 +57,74 @@ static void amp_open(struct filter_node *fn) pad->conf = fn->conf; fn->private_data = pad; - if (!pad->conf->amp_given && stat_item_values[SI_AMPLIFICATION]) { - int i = SI_AMPLIFICATION; - char *s = stat_item_values[i] + strlen(status_item_list[i]) + 1; - sscanf(s, "%u", &pad->amp); - } else + fn->min_iqs = 2; + if (!pad->conf->amp_given && stat_item_values[SI_AMPLIFICATION]) + sscanf(stat_item_values[SI_AMPLIFICATION], "%u", &pad->amp); + else pad->amp = pad->conf->amp_arg; - fn->bufsize = AMP_CHUNK_SIZE; - fn->buf = para_malloc(fn->bufsize); + PARA_NOTICE_LOG("amplification: %u (scaling factor: %1.2f)\n", + pad->amp, pad->amp / 64.0 + 1.0); +} + +static void amp_post_select(__a_unused struct sched *s, struct task *t) +{ + struct filter_node *fn = container_of(t, struct filter_node, task); + struct private_amp_data *pad = fn->private_data; + struct btr_node *btrn = fn->btrn; + int ret, factor = 64 + pad->amp; + size_t i, in_bytes, len; + int16_t *in, *out; + bool inplace = btr_inplace_ok(btrn); + + if (pad->amp == 0) { /* no amplification */ + t->error = -E_AMP_ZERO_AMP; + btr_splice_out_node(btrn); + return; + } +next_buffer: + ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); + if (ret < 0) + goto err; + if (ret == 0) + return; + btr_merge(btrn, fn->min_iqs); + in_bytes = btr_next_buffer(btrn, (char **)&in); + len = in_bytes / 2; + if (len == 0) { /* eof and in_bytes == 1 */ + ret = -E_AMP_EOF; + goto err; + } + + if (inplace) + out = in; + else + out = para_malloc(len * 2); + + for (i = 0; i < len; i++) { + int x = (in[i] * factor) >> 6; + + out[i] = x; + if (out[i] != x) /* overflow, clip */ + out[i] = (x >= 0)? 32767 : -32768; + } + + if (inplace) + btr_pushdown_one(btrn); + else { + btr_consume(btrn, len * 2); + btr_add_output((char *)out, len * 2, btrn); + } + t->error = 0; + goto next_buffer; +err: + assert(ret < 0); + t->error = ret; + btr_remove_node(btrn); +} + +static void amp_free_config(void *conf) +{ + amp_cmdline_parser_free(conf); } /** @@ -98,8 +139,10 @@ void amp_filter_init(struct filter *f) amp_cmdline_parser_init(&dummy); f->open = amp_open; f->close = amp_close; - f->convert = amp_convert; + f->pre_select = generic_filter_pre_select; + f->post_select = amp_post_select; f->parse_config = amp_parse_config; + f->free_config = amp_free_config; f->help = (struct ggo_help) { .short_help = amp_filter_args_info_help, .detailed_help = amp_filter_args_info_detailed_help diff --git a/attribute.c b/attribute.c index df66c9d0..a353f141 100644 --- a/attribute.c +++ b/attribute.c @@ -1,12 +1,18 @@ /* - * Copyright (C) 1997-2009 Andre Noll + * Copyright (C) 1997-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file attribute.c Attribute handling functions. */ + +#include +#include +#include + #include "para.h" #include "error.h" +#include "crypt.h" #include "string.h" #include "afh.h" #include "afs.h" @@ -89,11 +95,11 @@ int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum) struct osl_object obj = {.data = (char *)att_name, .size = strlen(att_name) + 1}; struct osl_row *row; - int ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row); + int ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row)); if (ret < 0) return ret; - ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj); + ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj)); if (ret < 0) return ret; *bitnum = *(unsigned char *)obj.data; @@ -131,7 +137,7 @@ static int print_attribute(struct osl_table *table, struct osl_row *row, if (!(laad->flags & LSATT_FLAG_LONG)) return para_printf(&laad->pb, "%s\n", name); - ret = osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj); + ret = osl(osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj)); if (ret < 0) { para_printf(&laad->pb, "%s: %s\n", name, para_strerror(-ret)); return ret; @@ -171,7 +177,7 @@ static void com_lsatt_callback(int fd, const struct osl_object *query) free(laad.pb.buf); } -int com_lsatt(int fd, int argc, char * const * const argv) +int com_lsatt(struct rc4_context *rc4c, int argc, char * const * const argv) { unsigned flags = 0; struct osl_object options = {.data = &flags, .size = sizeof(flags)}; @@ -199,12 +205,12 @@ int com_lsatt(int fd, int argc, char * const * const argv) } } ret = send_option_arg_callback_request(&options, argc - i, argv + i, - com_lsatt_callback, send_result, &fd); + com_lsatt_callback, rc4_send_result, rc4c); if (!ret) { if (argc > 1) - ret = send_va_buffer(fd, "no matches\n"); + ret = rc4_send_va_buffer(rc4c, "no matches\n"); } else if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -230,11 +236,11 @@ static void com_setatt_callback(__a_unused int fd, const struct osl_object *quer p[len - 1] = '\0'; obj.data = p; obj.size = len + 1; - ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row); + ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row)); if (ret < 0) goto out; - ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM, - &obj); + ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM, + &obj)); if (ret < 0) goto out; if (c == '+') @@ -272,7 +278,7 @@ out: PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); } -int com_setatt(__a_unused int fd, int argc, char * const * const argv) +int com_setatt(__a_unused struct rc4_context *rc4c, int argc, char * const * const argv) { if (argc < 3) return -E_ATTR_SYNTAX; @@ -317,15 +323,15 @@ static void com_addatt_callback(int fd, const struct osl_object *query) goto out; continue; } - if (ret != -E_RB_KEY_NOT_FOUND) /* error */ + if (ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) /* error */ goto out; objs[ATTCOL_BITNUM].size = 1; /* find smallest unused attribute */ for (bitnum = 0; bitnum < 64; bitnum++) { objs[ATTCOL_BITNUM].data = &bitnum; - ret = osl_get_row(attribute_table, ATTCOL_BITNUM, - &objs[ATTCOL_BITNUM], &row); - if (ret == -E_RB_KEY_NOT_FOUND) + ret = osl(osl_get_row(attribute_table, ATTCOL_BITNUM, + &objs[ATTCOL_BITNUM], &row)); + if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) break; /* this bitnum is unused, use it */ if (ret < 0) /* error */ goto out; @@ -337,7 +343,7 @@ static void com_addatt_callback(int fd, const struct osl_object *query) } objs[ATTCOL_NAME].data = p; objs[ATTCOL_NAME].size = len + 1; - ret = osl_add_row(attribute_table, objs); + ret = osl(osl_add_row(attribute_table, objs)); if (ret < 0) goto out; aed.name = p; @@ -353,16 +359,16 @@ out: free(pb.buf); } -int com_addatt(int fd, int argc, char * const * const argv) +int com_addatt(struct rc4_context *rc4c, int argc, char * const * const argv) { int ret; if (argc < 2) return -E_ATTR_SYNTAX; ret = send_standard_callback_request(argc - 1, argv + 1, com_addatt_callback, - send_result, &fd); + rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -380,12 +386,12 @@ static void com_mvatt_callback(int fd, const struct osl_object *query) }; int ret; - ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row); + ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row)); if (ret < 0) goto out; obj.data = new; obj.size = strlen(new) + 1; - ret = osl_update_object(attribute_table, row, ATTCOL_NAME, &obj); + ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj)); out: if (ret < 0) para_printf(&pb, "%s\n", para_strerror(-ret)); @@ -396,16 +402,16 @@ out: free(pb.buf); } -int com_mvatt(int fd, int argc, char * const * const argv) +int com_mvatt(struct rc4_context *rc4c, int argc, char * const * const argv) { int ret; if (argc != 3) return -E_ATTR_SYNTAX; ret = send_standard_callback_request(argc - 1, argv + 1, com_mvatt_callback, - send_result, &fd); + rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -429,7 +435,7 @@ static int remove_attribute(struct osl_table *table, struct osl_row *row, ret = get_attribute_bitnum_by_name(name, &red.bitnum); if (ret < 0) return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret)); - ret = osl_del_row(table, row); + ret = osl(osl_del_row(table, row)); if (ret < 0) return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret)); ret = para_printf(&raad->pb, "removed attribute %s\n", name); @@ -468,16 +474,16 @@ static void com_rmatt_callback(int fd, const struct osl_object *query) free(raad.pb.buf); } -int com_rmatt(int fd, int argc, char * const * const argv) +int com_rmatt(struct rc4_context *rc4c, int argc, char * const * const argv) { int ret; if (argc < 2) return -E_ATTR_SYNTAX; ret = send_standard_callback_request(argc - 1, argv + 1, com_rmatt_callback, - send_result, &fd); + rc4_send_result, rc4c); if (ret < 0) - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret)); return ret; } @@ -533,10 +539,10 @@ int get_attribute_text(uint64_t *atts, const char *delim, char **text) if (!(*atts & (one << i))) continue; - ret = osl_get_row(attribute_table, ATTCOL_BITNUM, &obj, &row); + ret = osl(osl_get_row(attribute_table, ATTCOL_BITNUM, &obj, &row)); if (ret < 0) goto err; - ret = osl_get_object(attribute_table, row, ATTCOL_NAME, &obj); + ret = osl(osl_get_object(attribute_table, row, ATTCOL_NAME, &obj)); if (ret < 0) goto err; if (*text) { @@ -559,7 +565,7 @@ err: * * \sa osl_close_table(). */ -void attribute_close(void) +static void attribute_close(void) { osl_close_table(attribute_table, OSL_MARK_CLEAN); attribute_table = NULL; @@ -579,14 +585,14 @@ static int attribute_open(const char *dir) int ret; attribute_table_desc.dir = dir; - ret = osl_open_table(&attribute_table_desc, &attribute_table); + ret = osl(osl_open_table(&attribute_table_desc, &attribute_table)); greatest_att_bitnum = -1; /* no atts available */ if (ret >= 0) { find_greatest_att_bitnum(); return ret; } attribute_table = NULL; - if (ret >= 0 || is_errno(-ret, ENOENT)) + if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT)) return 1; return ret; } @@ -594,7 +600,7 @@ static int attribute_open(const char *dir) static int attribute_create(const char *dir) { attribute_table_desc.dir = dir; - return osl_create_table(&attribute_table_desc); + return osl(osl_create_table(&attribute_table_desc)); } /** diff --git a/audioc.c b/audioc.c index 5c081da5..3134faa1 100644 --- a/audioc.c +++ b/audioc.c @@ -1,11 +1,12 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file audioc.c the client program used to connect to para_audiod */ +#include #include #include @@ -18,8 +19,8 @@ INIT_AUDIOC_ERRLISTS; -/** the gengetopt structure containing command line args */ -struct audioc_args_info conf; +/** The gengetopt structure containing command line args. */ +static struct audioc_args_info conf; static int loglevel; INIT_STDERR_LOGGING(loglevel); diff --git a/audiod.c b/audiod.c index 8fbd0a00..136557ad 100644 --- a/audiod.c +++ b/audiod.c @@ -1,23 +1,27 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file audiod.c the paraslash's audio daemon */ +#include #include #include #include +#include +#include #include "para.h" #include "error.h" +#include "crypt.h" #include "audiod.cmdline.h" #include "list.h" #include "sched.h" #include "ggo.h" #include "recv.h" +#include "buffer_tree.h" #include "filter.h" -#include "grab_client.cmdline.h" #include "grab_client.h" #include "client.cmdline.h" #include "client.h" @@ -106,6 +110,8 @@ struct status_task { struct timeval clock_diff_barrier; /** Number of the audio format as announced by para_server. */ int current_audio_format_num; + /* The status task btrn is the child of the client task. */ + struct btr_node *btrn; }; /** The array of status items sent by para_server. */ @@ -136,7 +142,6 @@ static struct status_task status_task_struct; * \sa struct status_task */ static struct status_task *stat_task = &status_task_struct; -static struct timeval initial_delay_barrier; /** * the task for handling audiod commands @@ -154,13 +159,14 @@ struct command_task { #define FOR_EACH_AUDIO_FORMAT(af) for (af = 0; af < NUM_AUDIO_FORMATS; af++) /** - * get the audio format number - * \param name the name of the audio format + * Get the audio format number. + * + * \param name The name of the audio format. * * \return The audio format number on success, -E_UNSUPPORTED_AUDIO_FORMAT if * \a name is not a supported audio format. */ -int get_audio_format_num(char *name) +int get_audio_format_num(const char *name) { int i; @@ -172,13 +178,37 @@ int get_audio_format_num(char *name) return -E_UNSUPPORTED_AUDIO_FORMAT; } +/** + * Compute the play time based on information of the given slot. + * + * \param slot_num The slot number (negative means: no slot). + * + * This computes a string of the form "0:07 [3:33] (3%/3:40)" using information + * from the status items received from para_server and the start time of the + * (first) writer of the given slot. + * + * It has to to take into account that probably the stream was not started at + * the beginning of the file, that the clock between the server and the client + * host may differ and that playback of the stream was delayed, e.g. because + * the prebuffer filter is used in the filter chain of the given slot. + * + * If no writer is active in the given slot, or \a slot_num is negative + * (indicating that para_audiod runs in standby mode), an approximation based + * only on the status items is computed and the returned string is prefixed + * with "~". + * + * \return A string that must be freed by the caller. + */ char *get_time_string(int slot_num) { int ret, seconds = 0, length; struct timeval *tmp, sum, sss, /* server stream start */ + rstime, /* receiver start time */ + wstime, /* writer start time */ wtime, /* now - writer start */ rskip; /* receiver start - sss */ struct slot_info *s = slot_num < 0? NULL : &slot[slot_num]; + char *msg; if (audiod_status == AUDIOD_OFF) goto empty; @@ -189,27 +219,41 @@ char *get_time_string(int slot_num) } if (audiod_status == AUDIOD_ON && !s) goto empty; - /* valid status items and playing */ - if (s) { /* writer active in this slot */ - length = s->seconds_total; - tmp = &s->server_stream_start; - } else { /* standby mode, rely on status items */ - length = stat_task->length_seconds; - tmp = &stat_task->server_stream_start; + /* + * Valid status items and playing, set length and tmp to the stream + * start. We use the slot info and fall back to the info from current + * status items if no slot info is available. + */ + length = stat_task->length_seconds; + tmp = &stat_task->server_stream_start; + if (s && s->wns) { /* writer active in this slot */ + btr_get_node_start(s->wns[0].btrn, &wstime); + if (wstime.tv_sec != 0) { /* writer wrote something */ + if (s->server_stream_start.tv_sec == 0) { + /* copy status info to slot */ + s->server_stream_start = stat_task->server_stream_start; + s->offset_seconds = stat_task->offset_seconds; + s->seconds_total = stat_task->length_seconds; + } + length = s->seconds_total; + tmp = &s->server_stream_start; + } } if (stat_task->sa_time_diff_sign > 0) tv_diff(tmp, &stat_task->sa_time_diff, &sss); else tv_add(tmp, &stat_task->sa_time_diff, &sss); - if (!s) { + if (!s || !s->wns) { struct timeval diff; tv_diff(now, &sss, &diff); seconds = diff.tv_sec + stat_task->offset_seconds; goto out; } - tv_diff(now, &s->wstime, &wtime); + tv_diff(now, &wstime, &wtime); + //PARA_CRIT_LOG("offset %d\n", s->offset_seconds); seconds = s->offset_seconds; - ret = tv_diff(&s->rstime, &sss, &rskip); + btr_get_node_start(s->receiver_node->btrn, &rstime); + ret = tv_diff(&rstime, &sss, &rskip); if (ret > 0) { /* audiod was started in the middle of the stream */ tv_add(&wtime, &rskip, &sum); seconds += sum.tv_sec; @@ -218,9 +262,8 @@ char *get_time_string(int slot_num) out: seconds = PARA_MIN(seconds, length); seconds = PARA_MAX(seconds, 0); - return make_message( - "%s: %s%d:%02d [%d:%02d] (%d%%/%d:%02d)\n", - status_item_list[SI_PLAY_TIME], + msg = make_message( + "%s%d:%02d [%d:%02d] (%d%%/%d:%02d)", s? "" : "~", seconds / 60, seconds % 60, @@ -230,8 +273,10 @@ out: length / 60, length % 60 ); + PARA_DEBUG_LOG("slot %d: %s\n", slot_num, msg); + return msg; empty: - return make_message("%s:\n", status_item_list[SI_PLAY_TIME]); + return para_strdup(NULL); } static int want_colors(void) @@ -309,30 +354,96 @@ static void close_receiver(int slot_num) PARA_NOTICE_LOG("closing %s receiver in slot %d\n", audio_formats[s->format], slot_num); a->receiver->close(s->receiver_node); + btr_free_node(s->receiver_node->btrn); free(s->receiver_node); s->receiver_node = NULL; - stat_task->current_audio_format_num = -1; } -static void kill_all_decoders(int error) +static void writer_cleanup(struct writer_node *wn) +{ + struct writer *w; + + if (!wn) + return; + w = writers + wn->writer_num; + PARA_INFO_LOG("closing %s\n", writer_names[wn->writer_num]); + w->close(wn); + btr_free_node(wn->btrn); +} + +static void close_writers(struct slot_info *s) { + struct audio_format_info *a; int i; + if (s->format < 0) + return; + assert(s->wns); + a = afi + s->format; + if (a->num_writers == 0) + writer_cleanup(s->wns); + else { + for (i = 0; i < a->num_writers; i++) + writer_cleanup(s->wns + i); + } + free(s->wns); + s->wns = NULL; +} + +static void close_filters(struct slot_info *s) +{ + int i; + struct audio_format_info *a = afi + s->format; + if (a->num_filters == 0) + return; + for (i = 0; i < a->num_filters; i++) { + struct filter_node *fn = s->fns + i; + struct filter *f; + + if (!fn) + continue; + f = filters + fn->filter_num; + if (f->close) + f->close(fn); + btr_free_node(fn->btrn); + } + free(s->fns); + s->fns = NULL; +} + +/* + * Whenever a task commits suicide by returning from post_select with t->error + * < 0, it also removes its btr node. We do exactly that to kill a running + * task. Note that the scheduler checks t->error also _before_ each pre/post + * select call, so the victim will never be scheduled again. + */ +static void kill_btrn(struct btr_node *btrn, struct task *t, int error) +{ + if (t->error < 0) + return; + t->error = error; + btr_remove_node(btrn); +} + +static void kill_all_decoders(int error) +{ + int i, j; + FOR_EACH_SLOT(i) { struct slot_info *s = &slot[i]; - if (s->wng && s->wng->task.error >= 0) { - PARA_INFO_LOG("deactivating wng in slot %d\n", - i); - s->wng->task.error = error; - } - if (s->fc && s->fc->task.error >= 0) { - PARA_INFO_LOG("deactivatimg filter chain in slot %d\n", i); - s->fc->task.error = error; - } - if (s->receiver_node && s->receiver_node->task.error >= 0) { - PARA_INFO_LOG("deactivating receiver_node in slot %d\n", i); - s->receiver_node->task.error = error; - } + struct audio_format_info *a; + if (s->format < 0) + continue; + a = afi + s->format; + if (s->wns) + for (j = 0; j < a->num_writers; j++) + kill_btrn(s->wns[j].btrn, &s->wns[j].task, error); + if (s->fns) + for (j = 0; j < a->num_writers; j++) + kill_btrn(s->fns[j].btrn, &s->wns[j].task, error); + if (s->receiver_node) + kill_btrn(s->receiver_node->btrn, &s->receiver_node->task, + error); } } @@ -347,7 +458,7 @@ static int get_empty_slot(void) clear_slot(i); return i; } - if (s->wng || s->receiver_node || s->fc) + if (s->wns || s->receiver_node || s->fns) continue; clear_slot(i); return i; @@ -369,172 +480,171 @@ int num_filters(int audio_format_num) return afi[audio_format_num].num_filters; } -static void open_filters(int slot_num) +static void open_filters(struct slot_info *s) { - struct slot_info *s = &slot[slot_num]; - struct audio_format_info *a = &afi[s->format]; + struct audio_format_info *a = afi + s->format; struct filter_node *fn; int nf = a->num_filters; + struct btr_node *parent; int i; - s->fc = NULL; - if (!nf) + if (nf == 0) return; PARA_INFO_LOG("opening %s filters\n", audio_formats[s->format]); - s->fc = para_calloc(sizeof(struct filter_chain)); - s->fc->filter_nodes = para_malloc(nf * sizeof(struct filter_node)); - s->fc->inbufp = &s->receiver_node->buf; - s->fc->in_loaded = &s->receiver_node->loaded; - s->fc->input_error = &s->receiver_node->task.error; - s->fc->task.pre_select = NULL; - s->fc->task.post_select = filter_post_select; - s->fc->task.error = 0; - s->fc->num_filters = nf; - - s->receiver_node->output_error = &s->fc->task.error; - sprintf(s->fc->task.status, "filter chain"); - FOR_EACH_FILTER_NODE(fn, s->fc, i) { + assert(s->fns == NULL); + s->fns = para_calloc(nf * sizeof(struct filter_node)); + parent = s->receiver_node->btrn; + for (i = 0; i < nf; i++) { struct filter *f = filters + a->filter_nums[i]; + fn = s->fns + i; fn->filter_num = a->filter_nums[i]; fn->conf = a->filter_conf[i]; - fn->fc = s->fc; - fn->loaded = 0; - INIT_LIST_HEAD(&fn->callbacks); + fn->task.pre_select = f->pre_select; + fn->task.post_select = f->post_select; + + fn->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = f->name, .parent = parent, + .handler = f->execute, .context = fn)); + f->open(fn); + register_task(&fn->task); + parent = fn->btrn; PARA_NOTICE_LOG("%s filter %d/%d (%s) started in slot %d\n", - audio_formats[s->format], i, nf, f->name, slot_num); - s->fc->outbufp = &fn->buf; - s->fc->out_loaded = &fn->loaded; + audio_formats[s->format], i, nf, f->name, (int)(s - slot)); + sprintf(fn->task.status, "%s (slot %d)", f->name, (int)(s - slot)); } - register_task(&s->fc->task); } -static void open_writers(int slot_num) +static void open_writers(struct slot_info *s) { - int ret, i; - struct slot_info *s = &slot[slot_num]; - struct audio_format_info *a = &afi[s->format]; - - PARA_INFO_LOG("opening %s writers\n", audio_formats[s->format]); - if (!a->num_writers) - s->wng = setup_default_wng(); - else - s->wng = wng_new(a->num_writers); - if (s->fc) { - s->wng->bufp = s->fc->outbufp; - s->wng->loaded = s->fc->out_loaded; - s->wng->input_error = &s->fc->task.error; - s->wng->channels = &s->fc->channels; - s->wng->samplerate = &s->fc->samplerate; - s->fc->output_error = &s->wng->task.error; - PARA_INFO_LOG("samplerate: %d\n", *s->wng->samplerate); - } else { - s->wng->bufp = &s->receiver_node->buf; - s->wng->loaded = &s->receiver_node->loaded; - s->wng->input_error = &s->receiver_node->task.error; - } - for (i = 0; i < a->num_writers; i++) { - s->wng->writer_nodes[i].conf = a->writer_conf[i]; - s->wng->writer_nodes[i].writer_num = a->writer_nums[i]; + int i; + struct audio_format_info *a = afi + s->format; + struct writer_node *wn; + struct btr_node *parent = s->fns[a->num_filters - 1].btrn; + + assert(s->wns == NULL); + s->wns = para_calloc(PARA_MAX(1U, a->num_writers) + * sizeof(struct writer_node)); + if (a->num_writers == 0) + setup_writer_node(NULL, parent, s->wns); + else { + PARA_INFO_LOG("opening %s writers\n", audio_formats[s->format]); + for (i = 0; i < a->num_writers; i++) { + wn = s->wns + i; + wn->conf = a->writer_conf[i]; + wn->writer_num = a->writer_nums[i]; + register_writer_node(wn, parent); + } } - ret = wng_open(s->wng); - if (ret < 0) - return; - s->wstime = *now; - s->server_stream_start = stat_task->server_stream_start.tv_sec? - stat_task->server_stream_start : *now; - s->offset_seconds = stat_task->offset_seconds; - s->seconds_total = stat_task->length_seconds; - activate_inactive_grab_clients(slot_num, s->format, s->fc); } +/* returns slot num on success */ static int open_receiver(int format) { struct audio_format_info *a = &afi[format]; struct slot_info *s; int ret, slot_num; + struct receiver *r = a->receiver; struct receiver_node *rn; - const struct timeval restart_delay = {2, 0}; + tv_add(now, &(struct timeval)EMBRACE(2, 0), &a->restart_barrier); ret = get_empty_slot(); if (ret < 0) - goto err; + return ret; slot_num = ret; - s = &slot[slot_num]; - s->format = format; - s->receiver_node = para_calloc(sizeof(struct receiver_node)); - rn = s->receiver_node; - rn->receiver = a->receiver; + rn = para_calloc(sizeof(*rn)); + rn->receiver = r; rn->conf = a->receiver_conf; - ret = a->receiver->open(s->receiver_node); + rn->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = r->name, .context = rn)); + ret = r->open(rn); if (ret < 0) { - free(s->receiver_node); - s->receiver_node = NULL; - goto err; + btr_free_node(rn->btrn); + free(rn); + return ret; } + s = &slot[slot_num]; + s->format = format; + s->receiver_node = rn; PARA_NOTICE_LOG("started %s: %s receiver in slot %d\n", - audio_formats[s->format], a->receiver->name, slot_num); - rn->task.pre_select = a->receiver->pre_select; - rn->task.post_select = a->receiver->post_select; - s->rstime = *now; - sprintf(rn->task.status, "%s receiver node", rn->receiver->name); + audio_formats[format], r->name, slot_num); + rn->task.pre_select = r->pre_select; + rn->task.post_select = r->post_select; + sprintf(rn->task.status, "%s receiver node", r->name); register_task(&rn->task); - ret = 1; -err: - if (ret < 0) - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - tv_add(now, &restart_delay, &afi[format].restart_barrier); - return ret; + return slot_num; } -/* return: 0: Not running, 1: Running, -1: Running but eof (or error) */ -static int receiver_running(int format) +static bool receiver_running(void) { - int i, ret = 0; + int i; + long unsigned ss1 = stat_task->server_stream_start.tv_sec; FOR_EACH_SLOT(i) { struct slot_info *s = &slot[i]; - if (s->format != format) - continue; + long unsigned ss2 = s->server_stream_start.tv_sec; + if (!s->receiver_node) continue; if (s->receiver_node->task.error >= 0) - return 1; - ret = -1; + return true; + if (ss1 == ss2) + return true; } - return ret; + return false; } -static void open_current_receiver(struct sched *s) +/** + * Return the root node of the current buffer tree. + * + * This is only used for stream grabbing. + * + * \return \p NULL if no slot is currently active. If more than one buffer tree + * exists, the node corresponding to the most recently started receiver is + * returned. + */ +struct btr_node *audiod_get_btr_root(void) { - struct timeval diff; - int ret, cafn = stat_task->current_audio_format_num; + int i, newest_slot = -1; + struct timeval newest_rstime = {0, 0}; - if (cafn < 0 || !stat_task->ct) - return; - /* Do nothing if the 'N' flag is set or the 'P' flag is unset */ - if (stat_task->vss_status != VSS_STATUS_FLAG_PLAYING) - return; - ret = receiver_running(cafn); - if (ret > 0) /* already running and not eof */ - return; - if (ret < 0) { /* eof */ - /* - * para_server uses a zero start time during the announcement - * period, i.e. before it sends the first chunk. Wait until - * this period begins to avoid restarting the receiver that - * belongs to the file just completed. - */ - if (stat_task->server_stream_start.tv_sec) - return; - } - if (tv_diff(now, &afi[cafn].restart_barrier, &diff) < 0) { - /* avoid busy loop */ - s->timeout = diff; - return; + FOR_EACH_SLOT(i) { + struct slot_info *s = &slot[i]; + struct timeval rstime; + if (!s->receiver_node) + continue; + if (s->receiver_node->task.error < 0) + continue; + btr_get_node_start(s->receiver_node->btrn, &rstime); + if (newest_slot >= 0 && tv_diff(&rstime, &newest_rstime, NULL) < 0) + continue; + newest_rstime = rstime; + newest_slot = i; } - /* start a new receiver */ - open_receiver(cafn); + if (newest_slot == -1) + return NULL; + return slot[newest_slot].receiver_node->btrn; +} + +/* whether a new instance of a decoder should be started. */ +static bool must_start_decoder(void) +{ + int cafn = stat_task->current_audio_format_num; + unsigned vs = stat_task->vss_status; + + if (cafn < 0) + return false; + if (!stat_task->ct) + return false; + if (vs & VSS_STATUS_FLAG_NEXT) + return false; + if (!(vs & VSS_STATUS_FLAG_PLAYING)) + return false; + if (receiver_running()) + return false; + if (tv_diff(now, &afi[cafn].restart_barrier, NULL) < 0) + return false; + return true; } static unsigned compute_time_diff(const struct timeval *status_time) @@ -569,9 +679,9 @@ static unsigned compute_time_diff(const struct timeval *status_time) &tmp); stat_task->sa_time_diff = tmp; PARA_INFO_LOG("time diff (cur/avg): %s%lums/%s%lums\n", - sign > 0? "+" : "-", + sign < 0? "-" : "+", tv2ms(&diff), - sa_time_diff_sign ? "+" : "-", + sa_time_diff_sign < 0? "-" : "+", tv2ms(&stat_task->sa_time_diff) ); out: @@ -579,70 +689,44 @@ out: return count; } -static int check_stat_line(char *line, __a_unused void *data) +static int update_item(int itemnum, char *buf) { - int itemnum; - size_t ilen = 0; long unsigned sec, usec; - char *tmp; - //PARA_INFO_LOG("line: %s\n", line); - if (!line) - return 1; - itemnum = stat_line_valid(line); - if (itemnum < 0) { - PARA_WARNING_LOG("invalid status line: %s\n", line); - return 1; - } if (stat_task->clock_diff_count && itemnum != SI_CURRENT_TIME) return 1; - tmp = make_message("%s\n", line); - stat_client_write(tmp, itemnum); - free(tmp); free(stat_item_values[itemnum]); - stat_item_values[itemnum] = para_strdup(line); - ilen = strlen(status_item_list[itemnum]); + stat_item_values[itemnum] = para_strdup(buf); + stat_client_write_item(itemnum); switch (itemnum) { case SI_STATUS_FLAGS: stat_task->vss_status = 0; - if (strchr(line, 'N')) + if (strchr(buf, 'N')) stat_task->vss_status |= VSS_STATUS_FLAG_NEXT; - if (strchr(line, 'P')) + if (strchr(buf, 'P')) stat_task->vss_status |= VSS_STATUS_FLAG_PLAYING; break; case SI_OFFSET: - stat_task->offset_seconds = atoi(line + ilen + 1); + stat_task->offset_seconds = atoi(buf); break; case SI_SECONDS_TOTAL: - stat_task->length_seconds = atoi(line + ilen + 1); + stat_task->length_seconds = atoi(buf); break; case SI_STREAM_START: - if (sscanf(line + ilen + 1, "%lu.%lu", &sec, &usec) == 2) { - struct timeval a_start, delay; - delay.tv_sec = conf.stream_delay_arg / 1000; - delay.tv_usec = (conf.stream_delay_arg % 1000) * 1000; + if (sscanf(buf, "%lu.%lu", &sec, &usec) == 2) { stat_task->server_stream_start.tv_sec = sec; stat_task->server_stream_start.tv_usec = usec; - if (compute_time_diff(NULL) > 2) { - if (stat_task->sa_time_diff_sign < 0) - tv_add(&stat_task->server_stream_start, - &stat_task->sa_time_diff, &a_start); - else - tv_diff(&stat_task->server_stream_start, - &stat_task->sa_time_diff, &a_start); - tv_add(&a_start, &delay, &initial_delay_barrier); - } } break; case SI_CURRENT_TIME: - if (sscanf(line + ilen + 1, "%lu.%lu", &sec, &usec) == 2) { + if (sscanf(buf, "%lu.%lu", &sec, &usec) == 2) { struct timeval tv = {sec, usec}; compute_time_diff(&tv); } break; case SI_FORMAT: - stat_task->current_audio_format_num = get_audio_format_num( - line + ilen + 1); + stat_task->current_audio_format_num + = get_audio_format_num(buf); } return 1; } @@ -772,6 +856,17 @@ static int init_default_filters(void) if (a->num_filters) continue; /* no default -- nothing to to */ + /* + * If udp is used to receive this audiod format, add fecdec as + * the first filter. + */ + if (strcmp(afi[i].receiver->name, "udp") == 0) { + tmp = para_strdup("fecdec"); + add_filter(i, tmp); + free(tmp); + if (ret < 0) + goto out; + } /* add "dec" to audio format name */ tmp = make_message("%sdec", audio_formats[i]); for (j = 0; filters[j].name; j++) @@ -797,7 +892,7 @@ static int parse_filter_args(void) { int i, ret, nf; - nf = PARA_MAX(1U, conf.filter_given); + nf = PARA_MAX(2U, conf.filter_given); PARA_INFO_LOG("maximal number of filters: %d\n", nf); FOR_EACH_AUDIO_FORMAT(i) { afi[i].filter_conf = para_malloc(nf * sizeof(void *)); @@ -916,8 +1011,15 @@ static void command_post_select(struct sched *s, struct task *t) { int ret; struct command_task *ct = container_of(t, struct command_task, task); + static struct timeval last_status_dump; + struct timeval tmp, delay = {0, 500 * 1000}; + + tv_add(&last_status_dump, &delay, &tmp); + if (tv_diff(&tmp, now, NULL) < 0) { + audiod_status_dump(); + last_status_dump = *now; + } - audiod_status_dump(); if (!FD_ISSET(ct->fd, &s->rfds)) return; ret = handle_connect(ct->fd); @@ -936,17 +1038,12 @@ static void init_command_task(struct command_task *ct) static void close_stat_pipe(void) { - int i; - if (!stat_task->ct) return; + btr_free_node(stat_task->ct->btrn); client_close(stat_task->ct); stat_task->ct = NULL; - FOR_EACH_STATUS_ITEM(i) { - free(stat_item_values[i]); - stat_item_values[i] = NULL; - } - dump_empty_status(); + clear_and_dump_items(); stat_task->length_seconds = 0; stat_task->offset_seconds = 0; stat_task->vss_status = 0; @@ -985,19 +1082,27 @@ static void set_stat_task_restart_barrier(unsigned seconds) static void try_to_close_slot(int slot_num) { struct slot_info *s = &slot[slot_num]; + struct audio_format_info *a = afi + s->format; + int i; if (s->format < 0) return; if (s->receiver_node && s->receiver_node->task.error != -E_TASK_UNREGISTERED) return; - if (s->fc && s->fc->task.error != -E_TASK_UNREGISTERED) - return; - if (s->wng && s->wng->task.error != -E_TASK_UNREGISTERED) - return; + for (i = 0; i < a->num_filters; i++) + if (s->fns && s->fns[i].task.error != -E_TASK_UNREGISTERED) + return; + if (a->num_writers > 0) { + for (i = 0; i < a->num_writers; i++) + if (s->wns && s->wns[i].task.error != -E_TASK_UNREGISTERED) + return; + } else { + if (s->wns && s->wns[0].task.error != -E_TASK_UNREGISTERED) + return; + } PARA_INFO_LOG("closing slot %d\n", slot_num); - wng_close(s->wng); - close_filters(s->fc); - free(s->fc); + close_writers(s); + close_filters(s); close_receiver(slot_num); clear_slot(slot_num); } @@ -1006,54 +1111,54 @@ static void try_to_close_slot(int slot_num) * Check if any receivers/filters/writers need to be started and do so if * necessary. */ -static void start_stop_decoders(struct sched *s) +static void start_stop_decoders(void) { - int i; + int i, ret; + struct slot_info *sl; + struct audio_format_info *a; FOR_EACH_SLOT(i) try_to_close_slot(i); if (audiod_status != AUDIOD_ON || !(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING)) return kill_all_decoders(-E_NOT_PLAYING); - open_current_receiver(s); - FOR_EACH_SLOT(i) { - struct slot_info *sl = &slot[i]; - struct audio_format_info *a; - struct timeval diff; - - if (sl->format < 0) - continue; - a = &afi[sl->format]; - if (!sl->receiver_node) - continue; - if ((!a->num_filters || sl->fc) && sl->wng) - continue; /* everything already started */ - if (!a->num_filters) { - if (sl->receiver_node->loaded && !sl->wng) { - open_writers(i); - } - continue; - } - if (sl->receiver_node->loaded && !sl->fc) { - open_filters(i); - continue; - } - if (sl->wng || !sl->fc || !*sl->fc->out_loaded) - continue; - if (tv_diff(now, &initial_delay_barrier, &diff) > 0) { - open_writers(i); - continue; - } - PARA_INFO_LOG("initial delay: %lu ms left\n", tv2ms(&diff)); - if (tv_diff(&s->timeout, &diff, NULL) > 0) { - s->timeout = diff; - } + if (!must_start_decoder()) + return; + ret = open_receiver(stat_task->current_audio_format_num); + if (ret < 0) { + PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + return; } + sl = slot + ret; + a = afi + sl->format; + if (a->num_filters) + open_filters(sl); + open_writers(sl); + activate_grab_clients(); + btr_log_tree(sl->receiver_node->btrn, LL_NOTICE); } +static void status_pre_select(struct sched *s, struct task *t) +{ + struct status_task *st = container_of(t, struct status_task, task); + int ret, cafn = stat_task->current_audio_format_num; + + if (must_start_decoder()) + goto min_delay; + ret = btr_node_status(st->btrn, 0, BTR_NT_LEAF); + if (ret > 0) + goto min_delay; + if (!st->ct) + sched_request_barrier_or_min_delay(&st->restart_barrier, s); + if (cafn >= 0) + sched_request_barrier(&afi[cafn].restart_barrier, s); + return; +min_delay: + sched_min_delay(s); +} /* restart the client task if necessary */ -static void status_pre_select(struct sched *s, struct task *t) +static void status_post_select(__a_unused struct sched *s, struct task *t) { struct status_task *st = container_of(t, struct status_task, task); @@ -1061,7 +1166,7 @@ static void status_pre_select(struct sched *s, struct task *t) if (!st->ct) goto out; if (st->ct->task.error >= 0) { - st->ct->task.error = -E_AUDIOD_OFF; + kill_btrn(st->ct->btrn, &st->ct->task, -E_AUDIOD_OFF); goto out; } if (st->ct->task.error != -E_TASK_UNREGISTERED) @@ -1071,7 +1176,9 @@ static void status_pre_select(struct sched *s, struct task *t) goto out; } if (st->ct) { - unsigned bytes_left; + char *buf; + size_t sz; + int ret; if (st->ct->task.error < 0) { if (st->ct->task.error != -E_TASK_UNREGISTERED) goto out; @@ -1080,54 +1187,63 @@ static void status_pre_select(struct sched *s, struct task *t) } if (st->ct->status != CL_RECEIVING) goto out; - bytes_left = for_each_line(st->ct->buf, st->ct->loaded, - &check_stat_line, NULL); - if (st->ct->loaded != bytes_left) { + ret = btr_node_status(st->btrn, 0, BTR_NT_LEAF); + if (ret <= 0) + goto out; + sz = btr_next_buffer(st->btrn, &buf); + ret = for_each_stat_item(buf, sz, update_item); + if (ret < 0) { + kill_btrn(st->ct->btrn, &st->ct->task, ret); + goto out; + } + if (sz != ret) st->last_status_read = *now; - st->ct->loaded = bytes_left; - } else { + else { struct timeval diff; tv_diff(now, &st->last_status_read, &diff); if (diff.tv_sec > 61) - st->ct->task.error = -E_STATUS_TIMEOUT; + kill_btrn(st->ct->btrn, &st->ct->task, + -E_STATUS_TIMEOUT); } + btr_consume(st->btrn, sz - ret); goto out; } if (tv_diff(now, &st->restart_barrier, NULL) < 0) goto out; if (st->clock_diff_count) { /* get status only one time */ - char *argv[] = {"audiod", "stat", "1", NULL}; - int argc = 3; + char *argv[] = {"audiod", "--", "stat", "-p", "-n=1", NULL}; + int argc = 5; PARA_INFO_LOG("clock diff count: %d\n", st->clock_diff_count); st->clock_diff_count--; - client_open(argc, argv, &st->ct, NULL); + client_open(argc, argv, &st->ct, NULL, NULL, st->btrn); set_stat_task_restart_barrier(2); } else { - char *argv[] = {"audiod", "stat", NULL}; - int argc = 2; - client_open(argc, argv, &st->ct, NULL); + char *argv[] = {"audiod", "--", "stat", "-p", NULL}; + int argc = 4; + client_open(argc, argv, &st->ct, NULL, NULL, st->btrn); set_stat_task_restart_barrier(5); } free(stat_item_values[SI_BASENAME]); - stat_item_values[SI_BASENAME] = make_message( - "%s: no connection to para_server\n", - status_item_list[SI_BASENAME]); - stat_client_write(stat_item_values[SI_BASENAME], - SI_BASENAME); + stat_item_values[SI_BASENAME] = para_strdup( + "no connection to para_server"); + stat_client_write_item(SI_BASENAME); st->last_status_read = *now; out: - start_stop_decoders(s); + start_stop_decoders(); } static void init_status_task(struct status_task *st) { memset(st, 0, sizeof(struct status_task)); st->task.pre_select = status_pre_select; + st->task.post_select = status_post_select; st->sa_time_diff_sign = 1; st->clock_diff_count = conf.clock_diff_count_arg; st->current_audio_format_num = -1; - sprintf(st->task.status, "status task"); + sprintf(st->task.status, "stat"); + st->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "stat")); } static void set_initial_status(void) @@ -1215,9 +1331,12 @@ int main(int argc, char *argv[]) drop_privileges_or_die(conf.user_arg, conf.group_arg); parse_config_or_die(); init_colors_or_die(); + init_random_seed_or_die(); daemon_set_flag(DF_LOG_TIME); daemon_set_flag(DF_LOG_HOSTNAME); daemon_set_flag(DF_LOG_LL); + if (conf.log_timing_given) + daemon_set_flag(DF_LOG_TIMING); if (conf.logfile_given) { daemon_set_logfile(conf.logfile_arg); daemon_open_log_or_die(); @@ -1232,7 +1351,6 @@ int main(int argc, char *argv[]) set_initial_status(); FOR_EACH_SLOT(i) clear_slot(i); - init_grabbing(); setup_signal_handling(); signal_setup_default(sig_task); @@ -1245,8 +1363,8 @@ int main(int argc, char *argv[]) register_task(&sig_task->task); register_task(&cmd_task->task); register_task(&stat_task->task); - s.default_timeout.tv_sec = 0; - s.default_timeout.tv_usec = 99 * 1000; + s.default_timeout.tv_sec = 2; + s.default_timeout.tv_usec = 999 * 1000; ret = schedule(&s); PARA_EMERG_LOG("%s\n", para_strerror(-ret)); diff --git a/audiod.cmd b/audiod.cmd index 5469476c..7ffe98e7 100644 --- a/audiod.cmd +++ b/audiod.cmd @@ -14,11 +14,32 @@ H: on -> standby -> off -> on N: grab D: grab the audio stream L: -U: -- grab [grab_options] -H: grab ('splice') the audio stream at any position in the filter -H: chain and send that data back to the client. Try -H: para_audioc -- grab -h -H: for the list of available options. +U: -- grab [-m[{s|p|a}]] [-p=] [-n=] [-o] +H: +H: grab ('splice') the audio stream at any position in the buffer +H: tree and send that data back to the client. +H: +H: Options: +H: +H: -m Change grab mode. Defaults to sloppy grab if not given. +H: +H: -ms: sloppy grab +H: +H: -mp: pedantic grab +H: +H: -ma: aggressive grab +H: +H: The various grab modes only differ in what happens if the +H: file descriptor to write the grabbed audio data to is not +H: ready for writing (i.e. would block). Sloppy mode ignores +H: the write, pedantic mode aborts and aggressive mode tries +H: to write anyway. +H: +H: -p Grab output of node of the buffer tree. +H: +H: -n Name of the new buffer tree node. Defaults to 'grab'. +H: +H: -o One-shot mode: Stop grabbing if audio file changes. --- N: help D: display command list or help for given command @@ -51,8 +72,9 @@ H: Stop all decoders but leave connection to para_server open. --- N: stat D: print status information -U: stat [item1 ...] -H: Dump given status items (all if none given) to stdout. +U: stat [-p] [item1 ...] +H: Dump given status items (all if none given) to stdout. If -p is given, use +H: parser-friendly mode. --- N: tasks D: list current tasks diff --git a/audiod.h b/audiod.h index 8b416ce5..f0cb6cae 100644 --- a/audiod.h +++ b/audiod.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -8,7 +8,7 @@ int num_filters(int audio_format_num); -int get_audio_format_num(char *name); +int get_audio_format_num(const char *name); /** enum of audio formats supported by para_audiod */ enum {AUDIOD_AUDIO_FORMATS_ENUM}; @@ -34,12 +34,6 @@ struct audiod_command { const char *name; /** pointer to the function that handles the command */ int (*handler)(int, int, char**); - /** - * if the command prefers to handle the full line (rather than the usual - * argv[] array), it stores a pointer to the corresponding line handling - * function here. In this case, the above \a handler pointer must be NULL. - */ - int (*line_handler)(int, char*); /** one-line description of the command */ const char *description; /** summary of the command line options */ @@ -51,16 +45,12 @@ struct audiod_command { /** * Describes one instance of a receiver-filter-writer chain. * - * \sa receiver_node, receiver, filter, filter_node, filter_chain, writer, - * writer_node, writer_node_group. + * \sa receiver_node, receiver, filter, filter_node, writer, writer_node, + * writer_node_group. */ struct slot_info { /** Number of the audio format in this slot. */ int format; - /** Receiver start time. */ - struct timeval rstime; - /** Writer start time. */ - struct timeval wstime; /** The stream_start status item announced by para_server. */ struct timeval server_stream_start; /** The offset status item announced by para_server. */ @@ -69,10 +59,10 @@ struct slot_info { unsigned seconds_total; /** The receiver info associated with this slot. */ struct receiver_node *receiver_node; - /** The active filter chain. */ - struct filter_chain *fc; - /** The active writer node group. */ - struct writer_node_group *wng; + /** The array of filter nodes. */ + struct filter_node *fns; + /** The array of writers attached to the last filter. */ + struct writer_node *wns; }; extern struct slot_info slot[MAX_STREAM_SLOTS]; @@ -82,8 +72,11 @@ extern int audiod_status; void __noreturn clean_exit(int status, const char *msg); int handle_connect(int accept_fd); void audiod_status_dump(void); -void dump_empty_status(void); char *get_time_string(int slot_num); +struct btr_node *audiod_get_btr_root(void); + +void stat_client_write_item(int item_num); +void clear_and_dump_items(void); /** iterate over all slots */ #define FOR_EACH_SLOT(_slot) for (_slot = 0; _slot < MAX_STREAM_SLOTS; _slot++) diff --git a/audiod_command.c b/audiod_command.c index 4706131d..8024ec56 100644 --- a/audiod_command.c +++ b/audiod_command.c @@ -1,41 +1,186 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file audiod_command.c commands for para_audiod */ +#include #include #include +#include #include "para.h" #include "audiod.cmdline.h" #include "list.h" -#include "close_on_fork.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" -#include "grab_client.cmdline.h" #include "grab_client.h" - #include "error.h" #include "audiod.h" #include "net.h" #include "daemon.h" #include "string.h" +#include "write.h" #include "fd.h" #include "audiod_command_list.h" extern char *stat_item_values[NUM_STAT_ITEMS]; - -/** iterate over the array of all audiod commands */ +/** Iterate over the array of all audiod commands. */ #define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++) +/** The maximal number of simultaneous connections. */ +#define MAX_STAT_CLIENTS 50 + +/** Flags used for the stat command of para_audiod. */ +enum stat_client_flags { + /** Enable parser-friendly output. */ + SCF_PARSER_FRIENDLY = 1, +}; + +/** + * Describes a status client of para_audiod. + * + * There's one such structure per audiod client that sent the 'stat' command. + * + * A status client is identified by its file descriptor. para_audiod + * keeps a list of connected status clients. + */ +struct stat_client { + /** The stat client's file descriptor. */ + int fd; + /** Bitmask of those status items the client is interested in. */ + uint64_t item_mask; + /** See \ref stat_client flags. s*/ + unsigned flags; + /** Its entry in the list of stat clients. */ + struct list_head node; +}; + +static INITIALIZED_LIST_HEAD(client_list); +static int num_clients; + +/** The list of all status items used by para_{server,audiod,gui}. */ +const char *status_item_list[] = {STATUS_ITEM_ARRAY}; + +static void dump_stat_client_list(void) +{ + struct stat_client *sc; + + list_for_each_entry(sc, &client_list, node) + PARA_INFO_LOG("stat client on fd %d\n", sc->fd); +} +/** + * Add a status client to the list. + * + * \param fd The file descriptor of the client. + * \param mask Bitfield of status items for this client. + * \param parser_friendly Enable parser-friendly output mode. + * + * Only those status items having the bit set in \a mask will be + * sent to the client. + * + * \return Positive value on success, or -E_TOO_MANY_CLIENTS if + * the number of connected clients exceeds #MAX_STAT_CLIENTS. + */ +static int stat_client_add(int fd, uint64_t mask, int parser_friendly) +{ + struct stat_client *new_client; + + if (num_clients >= MAX_STAT_CLIENTS) { + PARA_ERROR_LOG("maximal number of stat clients (%d) exceeded\n", + MAX_STAT_CLIENTS); + return -E_TOO_MANY_CLIENTS; + } + PARA_INFO_LOG("adding client on fd %d\n", fd); + new_client = para_calloc(sizeof(struct stat_client)); + new_client->fd = fd; + new_client->item_mask = mask; + if (parser_friendly) + new_client->flags = SCF_PARSER_FRIENDLY; + para_list_add(&new_client->node, &client_list); + dump_stat_client_list(); + num_clients++; + return 1; +} +/** + * Write a message to all connected status clients. + * + * \param item_num The number of the status item of \a msg. + * + * On write errors, remove the status client from the client list and close its + * file descriptor. + */ +void stat_client_write_item(int item_num) +{ + struct stat_client *sc, *tmp; + struct para_buffer pb = {.flags = 0}; + struct para_buffer pfpb = {.flags = PBF_SIZE_PREFIX}; + const uint64_t one = 1; + + list_for_each_entry_safe(sc, tmp, &client_list, node) { + int fd = sc->fd, ret; + + if (!((one << item_num) & sc->item_mask)) + continue; + if (write_ok(fd) > 0) { + struct para_buffer *b = + (sc->flags & SCF_PARSER_FRIENDLY)? &pfpb : &pb; + char *msg = stat_item_values[item_num]; + if (!b->buf) + WRITE_STATUS_ITEM(b, item_num, "%s\n", + msg? msg : ""); + ret = write(fd, b->buf, b->offset); + if (ret == b->offset) + continue; + } + /* write error or fd not ready for writing */ + close(fd); + num_clients--; + PARA_INFO_LOG("deleting client on fd %d\n", fd); + list_del(&sc->node); + free(sc); + dump_stat_client_list(); + } + free(pb.buf); + free(pfpb.buf); +// if (num_clients) +// PARA_DEBUG_LOG("%d client(s)\n", num_clients); +} + +/** + * Check if string is a known status item. + * + * \param item Buffer containing the text to check. + * + * \return If \a item is a valid status item, the number of that status item is + * returned. Otherwise, this function returns \p -E_UNKNOWN_STAT_ITEM. + */ +static int stat_item_valid(const char *item) +{ + int i; + if (!item || !*item) { + PARA_ERROR_LOG("%s\n", "no item"); + return -E_UNKNOWN_STAT_ITEM; + } + FOR_EACH_STATUS_ITEM(i) + if (!strcmp(status_item_list[i], item)) + return i; + PARA_ERROR_LOG("invalid stat item: %s\n", item); + return -E_UNKNOWN_STAT_ITEM; +} + static int client_write(int fd, const char *buf) { - size_t len = strlen(buf); + size_t len; + + if (!buf) + return 0; + len = strlen(buf); return write(fd, buf, len) != len? -E_CLIENT_WRITE: 1; } @@ -43,23 +188,27 @@ __malloc static char *audiod_status_string(void) { const char *status = (audiod_status == AUDIOD_ON)? "on" : (audiod_status == AUDIOD_OFF)? "off": "sb"; - return make_message("%s: %s\n", status_item_list[SI_AUDIOD_STATUS], status); + return para_strdup(status); } static int get_play_time_slot_num(void) { - int i, oldest = -1; + int i, oldest_slot = -1; + struct timeval oldest_wstime = {0, 0}; FOR_EACH_SLOT(i) { struct slot_info *s = &slot[i]; - if (!s->wng) + struct timeval wstime; + if (!s->wns) continue; - if (oldest >= 0 && tv_diff(&s->wstime, &slot[oldest].wstime, - NULL) > 0) + btr_get_node_start(s->wns[0].btrn, &wstime); + if (oldest_slot >= 0 && tv_diff(&wstime, &oldest_wstime, NULL) > 0) continue; - oldest = i; + oldest_wstime = wstime; + oldest_slot = i; } - return oldest; + //PARA_CRIT_LOG("oldest slot: %d\n", oldest_slot); + return oldest_slot; } __malloc static char *decoder_flags(void) @@ -72,13 +221,14 @@ __malloc static char *decoder_flags(void) char flag = '0'; if (s->receiver_node) flag += 1; - if (s->wng) + if (s->fns) flag += 2; + if (s->wns) + flag += 4; flags[i] = flag; } flags[MAX_STREAM_SLOTS] = '\0'; - return make_message("%s: %s\n", status_item_list[SI_DECODER_FLAGS], - flags); + return para_strdup(flags); } static int dump_commands(int fd) @@ -168,95 +318,50 @@ int com_kill(int fd, int argc, char **argv) int com_stat(int fd, int argc, char **argv) { - int i, ret; - char *buf = NULL; + int i, ret, parser_friendly = 0; uint64_t mask = 0; const uint64_t one = 1; + struct para_buffer b = {.flags = 0}; - if (argc > 1) { - for (i = 1; i < argc; i++) { - ret = stat_item_valid(argv[i]); - if (ret < 0) - return ret; - mask |= (one << ret); + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (arg[0] != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; } - } else + if (!strncmp(arg, "-p", 2)) { + parser_friendly = 1; + b.flags = PBF_SIZE_PREFIX; + continue; + } + } + if (i >= argc) mask--; /* set all bits */ + for (; i < argc; i++) { + ret = stat_item_valid(argv[i]); + if (ret < 0) + return ret; + mask |= (one << ret); + } PARA_INFO_LOG("mask: 0x%llx\n", (long long unsigned)mask); FOR_EACH_STATUS_ITEM(i) { - char *tmp, *v; + char *item = stat_item_values[i]; if (!((one << i) & mask)) continue; - v = stat_item_values[i]; - if (!v) - continue; - tmp = make_message("%s%s%s", buf? buf: "", v, - strrchr(v, '\n')? "" : "\n"); - free(buf); - buf = tmp; + WRITE_STATUS_ITEM(&b, i, "%s\n", item? item : ""); } - ret = client_write(fd, buf); - if (ret > 0) - ret = stat_client_add(fd, mask); - free(buf); + ret = client_write(fd, b.buf); + if (ret >= 0) + ret = stat_client_add(fd, mask, parser_friendly); + free(b.buf); return ret; } -static struct filter_node *find_filter_node(int slot_num, int format, int filternum) +int com_grab(int fd, int argc, char **argv) { - int i; - - FOR_EACH_SLOT(i) { - struct slot_info *s = &slot[i]; - if (s->format < 0 || !s->fc) - continue; - if (slot_num >= 0 && slot_num != i) - continue; - if (format >= 0 && s->format != format) - continue; - if (num_filters(i) <= filternum) - continue; - /* success */ - return s->fc->filter_nodes + filternum; - } - return NULL; -} - -int com_grab(int fd, char *cmdline) -{ - struct grab_client *gc; - struct filter_node *fn; - int i, err; - char *msg; - - gc = grab_client_new(fd, cmdline, &err); - if (!gc) - goto err_out; - fn = find_filter_node(gc->conf->slot_arg, gc->audio_format_num, gc->conf->filter_num_arg); - if (fn) - activate_grab_client(gc, fn); - return 1; -err_out: - if (err != -E_GC_HELP_GIVEN && err != -E_GC_VERSION_GIVEN) - return err; - if (err == -E_GC_HELP_GIVEN) { - msg = make_message("%s\n\n", grab_client_args_info_usage); - for (i = 0; grab_client_args_info_help[i]; i++) { - char *tmp = make_message("%s%s\n", msg, - grab_client_args_info_help[i]); - free(msg); - msg = tmp; - } - } else - msg = make_message("%s %s\n", - GRAB_CLIENT_CMDLINE_PARSER_PACKAGE, - GRAB_CLIENT_CMDLINE_PARSER_VERSION); - err = client_write(fd, msg); - free(msg); - if (err < 0) - return err; - close(fd); - return 1; + return grab_client_new(fd, argc, argv); } __noreturn int com_term(int fd, __a_unused int argc, __a_unused char **argv) @@ -333,7 +438,7 @@ static int check_perms(uid_t uid) int handle_connect(int accept_fd) { int i, argc, ret, clifd = -1; - char *cmd = NULL, *p, *buf = para_calloc(MAXLINE), **argv = NULL; + char buf[MAXLINE], **argv = NULL; struct sockaddr_un unix_addr; uid_t uid; @@ -341,7 +446,7 @@ int handle_connect(int accept_fd) if (ret < 0) goto out; clifd = ret; - ret = recv_cred_buffer(clifd, buf, MAXLINE - 1); + ret = recv_cred_buffer(clifd, buf, sizeof(buf) - 1); if (ret < 0) goto out; uid = ret; @@ -349,36 +454,20 @@ int handle_connect(int accept_fd) ret = check_perms(uid); if (ret < 0) goto out; - cmd = para_strdup(buf); - p = strchr(cmd, '\n'); - if (!p) - p = ""; - else { - *p = '\0'; - p++; - } - for (i = 0; audiod_cmds[i].name; i++) { - int j; - if (strcmp(audiod_cmds[i].name, cmd)) + ret = create_argv(buf, "\n", &argv); + if (ret < 0) + goto out; + argc = ret; + //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc); + FOR_EACH_COMMAND(i) { + if (strcmp(audiod_cmds[i].name, argv[0])) continue; - if (audiod_cmds[i].handler) { - argc = split_args(buf, &argv, "\n"); - PARA_INFO_LOG("argv[0]: %s, argc= %d\n", argv[0], argc); - ret = audiod_cmds[i].handler(clifd, argc, argv); - goto out; - } - for (j = 0; p[j]; j++) - if (p[j] == '\n') - p[j] = ' '; - PARA_INFO_LOG("cmd: %s, options: %s\n", cmd, p); - ret = audiod_cmds[i].line_handler(clifd, p); + ret = audiod_cmds[i].handler(clifd, argc, argv); goto out; } ret = -E_INVALID_AUDIOD_CMD; out: - free(cmd); - free(buf); - free(argv); + free_argv(argv); if (clifd > 0 && ret < 0 && ret != -E_CLIENT_WRITE) { char *tmp = make_message("%s\n", para_strerror(-ret)); client_write(clifd, tmp); @@ -387,34 +476,32 @@ out: } return ret; } + /** - * send the current audiod status to all connected stat clients + * Send the current audiod status to all connected stat clients. */ void audiod_status_dump(void) { int slot_num = get_play_time_slot_num(); - char *old, *new, *tmp; + char *old, *new; old = stat_item_values[SI_PLAY_TIME]; new = get_time_string(slot_num); if (new) { if (!old || strcmp(old, new)) { free(old); - stat_client_write(new, SI_PLAY_TIME); stat_item_values[SI_PLAY_TIME] = new; + stat_client_write_item(SI_PLAY_TIME); } else free(new); } - tmp = uptime_str(); - new = make_message("%s: %s\n", status_item_list[SI_AUDIOD_UPTIME], - tmp); - free(tmp); + new = uptime_str(); old = stat_item_values[SI_AUDIOD_UPTIME]; if (!old || strcmp(old, new)) { free(old); - stat_client_write(new, SI_AUDIOD_UPTIME); stat_item_values[SI_AUDIOD_UPTIME] = new; + stat_client_write_item(SI_AUDIOD_UPTIME); } else free(new); @@ -422,8 +509,8 @@ void audiod_status_dump(void) new = audiod_status_string(); if (!old || strcmp(old, new)) { free(old); - stat_client_write(new, SI_AUDIOD_STATUS); stat_item_values[SI_AUDIOD_STATUS] = new; + stat_client_write_item(SI_AUDIOD_STATUS); } else free(new); @@ -431,27 +518,25 @@ void audiod_status_dump(void) new = decoder_flags(); if (!old || strcmp(old, new)) { free(old); - stat_client_write(new, SI_DECODER_FLAGS); stat_item_values[SI_DECODER_FLAGS] = new; + stat_client_write_item(SI_DECODER_FLAGS); } else free(new); } /** - * send empty status list + * Flush and send all status items. * * Send to each connected client the full status item list * with empty values. */ -void dump_empty_status(void) +void clear_and_dump_items(void) { int i; FOR_EACH_STATUS_ITEM(i) { - char *tmp = make_message("%s:\n", status_item_list[i]); - stat_client_write(tmp, i); - free(tmp); free(stat_item_values[i]); stat_item_values[i] = NULL; + stat_client_write_item(i); } } diff --git a/autogen.sh b/autogen.sh index 5c3d931d..76eaef2f 100755 --- a/autogen.sh +++ b/autogen.sh @@ -5,8 +5,9 @@ if test -f Makefile; then fi aclocal -I . &> /dev/null autoconf -autoheader +autoheader echo configuring... ./configure $@ > /dev/null echo compiling... -make clean all > /dev/null +make clean2 &> /dev/null +make > /dev/null diff --git a/bitstream.c b/bitstream.c new file mode 100644 index 00000000..1139edc0 --- /dev/null +++ b/bitstream.c @@ -0,0 +1,207 @@ +/* + * Common bit I/O utils. + * + * Extracted 2009 from mplayer 2009-02-10 libavcodec/bitstream.c. + * + * Copyright (c) 2000, 2001 Fabrice Bellard + * Copyright (c) 2002-2004 Michael Niedermayer + * alternative bitstream reader & writer by Michael Niedermayer + * + * Licensed under the GNU Lesser General Public License. + * For licencing details see COPYING.LIB. + */ + +/** + * \file bitstream.c Bitstream API. + */ + +#include +#include +#include +#include +#include + +#include "para.h" +#include "error.h" +#include "string.h" +#include "wma.h" +#include "bitstream.h" + +#define GET_DATA(v, table, i, size) \ +{\ + const uint8_t *ptr = (const uint8_t *)table + i * size; \ + switch (size) { \ + case 1: \ + v = *(const uint8_t *)ptr; \ + break; \ + case 2: \ + v = *(const uint16_t *)ptr; \ + break; \ + default: \ + v = *(const uint32_t *)ptr; \ + break; \ + } \ +} + +static void alloc_table(struct vlc *vlc, int size) +{ + vlc->table_size += size; + if (vlc->table_size > vlc->table_allocated) { + vlc->table_allocated += (1 << vlc->bits); + vlc->table = para_realloc(vlc->table, + sizeof(VLC_TYPE) * 2 * vlc->table_allocated); + } +} + +static int build_table(struct vlc *vlc, int table_nb_bits, int nb_codes, + const void *bits, const void *codes, int codes_size, + uint32_t code_prefix, int n_prefix) +{ + int i, j, k, n, table_size, table_index, nb, n1, idx, code_prefix2, + symbol; + uint32_t code; + VLC_TYPE(*table)[2]; + + table_size = 1 << table_nb_bits; + table_index = vlc->table_size; + alloc_table(vlc, table_size); + table = &vlc->table[table_index]; + + for (i = 0; i < table_size; i++) { + table[i][1] = 0; //bits + table[i][0] = -1; //codes + } + + /* map codes and compute auxillary table sizes */ + for (i = 0; i < nb_codes; i++) { + GET_DATA(n, bits, i, 1); + GET_DATA(code, codes, i, codes_size); + /* we accept tables with holes */ + if (n <= 0) + continue; + symbol = i; + /* if code matches the prefix, it is in the table */ + n -= n_prefix; + code_prefix2 = code >> n; + if (n <= 0 || code_prefix2 != code_prefix) + continue; + if (n <= table_nb_bits) { + /* no need to add another table */ + j = (code << (table_nb_bits - n)) & (table_size - 1); + nb = 1 << (table_nb_bits - n); + for (k = 0; k < nb; k++) { + if (table[j][1] /* bits */ != 0) { + PARA_EMERG_LOG("incorrect code\n"); + exit(EXIT_FAILURE); + } + table[j][1] = n; //bits + table[j][0] = symbol; + j++; + } + } else { + n -= table_nb_bits; + j = (code >> n) & ((1 << table_nb_bits) - 1); + /* compute table size */ + n1 = -table[j][1]; //bits + if (n > n1) + n1 = n; + table[j][1] = -n1; //bits + } + } + + /* fill auxillary tables recursively */ + for (i = 0; i < table_size; i++) { + n = table[i][1]; //bits + if (n < 0) { + n = -n; + if (n > table_nb_bits) { + n = table_nb_bits; + table[i][1] = -n; //bits + } + idx = build_table(vlc, n, nb_codes, bits, codes, + codes_size, (code_prefix << table_nb_bits) | i, + n_prefix + table_nb_bits); + /* vlc->table might have changed */ + table = &vlc->table[table_index]; + table[i][0] = idx; //code + } + } + return table_index; +} + +/** + * Build VLC decoding tables suitable for use with get_vlc(). + * + * \param vlc The structure to be initialized. + * + * \param nb_bits Set the decoding table size (2^nb_bits) entries. The bigger + * it is, the faster is the decoding. But it should not be too big to save + * memory and L1 cache. '9' is a good compromise. + * + * \param nb_codes Number of vlcs codes. + * + * \param bits Table which gives the size (in bits) of each vlc code. + * + * \param codes Table which gives the bit pattern of of each vlc code. + * + * \param codes_size The number of bytes of each entry of the \a codes tables. + * + * The wrap and size parameters allow to use any memory configuration and + * types (byte/word/long) to store the bits and codes tables. + */ +void init_vlc(struct vlc *vlc, int nb_bits, int nb_codes, const void *bits, + const void *codes, int codes_size) +{ + PARA_INFO_LOG("nb_codes: %d\n", nb_codes); + vlc->bits = nb_bits; + vlc->table = NULL; + vlc->table_allocated = 0; + vlc->table_size = 0; + build_table(vlc, nb_bits, nb_codes, bits, codes, codes_size, 0, 0); +} + +void free_vlc(struct vlc *vlc) +{ + freep(&vlc->table); +} + +/** + * Parse a vlc code. + * + * \param gbc The getbit context structure. + * + * \param table The vlc tables to use. + * + * \param bits The number of bits which will be read at once, must be + * identical to nb_bits in init_vlc(). + * + * \param max_depth The number of times bits bits must be read to completely + * read the longest vlc code = (max_vlc_length + bits - 1) / bits. + * + * \return The vlc code. + */ +int get_vlc(struct getbit_context *gbc, VLC_TYPE(*table)[2], int bits, + int max_depth) +{ + int n, idx, nb_bits, code; + + idx = show_bits(gbc, bits); + code = table[idx][0]; + n = table[idx][1]; + if (max_depth > 1 && n < 0) { + skip_bits(gbc, bits); + nb_bits = -n; + idx = show_bits(gbc, nb_bits) + code; + code = table[idx][0]; + n = table[idx][1]; + if (max_depth > 2 && n < 0) { + skip_bits(gbc, nb_bits); + nb_bits = -n; + idx = show_bits(gbc, nb_bits) + code; + code = table[idx][0]; + n = table[idx][1]; + } + } + skip_bits(gbc, n); + return code >= 0? code : -E_VLC; +} diff --git a/bitstream.h b/bitstream.h new file mode 100644 index 00000000..5d6be3be --- /dev/null +++ b/bitstream.h @@ -0,0 +1,91 @@ +/* + * Extracted 2009 from mplayer 2009-02-10 libavcodec/bitstream.h. + * + * copyright (c) 2004 Michael Niedermayer + * + * Licensed under the GNU Lesser General Public License. + * For licencing details see COPYING.LIB. + */ + +/** \file bitstream.h Bitstream structures and inline functions. */ + +/** Structure for bistream I/O. */ +struct getbit_context { + /* Start of the internal buffer. */ + const uint8_t *buffer; + /* End of the internal buffer. */ + const uint8_t *buffer_end; + /** Bit counter. */ + int index; +}; + +#define VLC_TYPE int16_t + +/** A variable length code table. */ +struct vlc { + /** Number of bits of the table. */ + int bits; + /** The code and the bits table. */ + VLC_TYPE(*table)[2]; + /** The size of the table. */ + int table_size; + /** Amount of memory allocated so far. */ + int table_allocated; +}; + +static inline uint32_t show_bits(struct getbit_context *gbc, int num) +{ + int idx = gbc->index; + const uint8_t *p = gbc->buffer + (idx >> 3); + uint32_t x = ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); + return (x << (idx & 7)) >> (32 - num); +} + +static inline int get_bits_count(struct getbit_context *gbc) +{ + return gbc->index; +} + +static inline void skip_bits(struct getbit_context *gbc, int n) +{ + gbc->index += n; +} + +static inline unsigned int get_bits(struct getbit_context *gbc, int n) +{ + unsigned int ret = show_bits(gbc, n); + skip_bits(gbc, n); + return ret; +} + +/* This is rather hot, we can do better than get_bits(gbc, 1). */ +static inline unsigned int get_bit(struct getbit_context *gbc) +{ + int idx = gbc->index++; + uint8_t tmp = gbc->buffer[idx >> 3], mask = (1 << (7 - (idx & 7))); + return !!(tmp & mask); +} + +/** + * Initialize a getbit_context structure. + * + * \param buffer The bitstream buffer. It must be 4 bytes larger then the + * actual read bits because the bitstream reader might read 32 bits at once and + * could read over the end. + * + * \param bit_size The size of the buffer in bytes. + */ +static inline void init_get_bits(struct getbit_context *gbc, + const uint8_t *buffer, int size) +{ + gbc->buffer = buffer; + gbc->buffer_end = buffer + size; + gbc->index = 0; +} + +void init_vlc(struct vlc *vlc, int nb_bits, int nb_codes, const void *bits, + const void *codes, int codes_size); +void free_vlc(struct vlc *vlc); +int get_vlc(struct getbit_context *gbc, VLC_TYPE(*table)[2], int bits, + int max_depth); + diff --git a/blob.c b/blob.c index 5ab5c1ce..44159a6f 100644 --- a/blob.c +++ b/blob.c @@ -1,19 +1,47 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file blob.c Macros and functions for blob handling. */ +#include #include +#include +#include + #include "para.h" #include "error.h" +#include "crypt.h" #include "string.h" #include "afh.h" #include "afs.h" #include "net.h" #include "ipc.h" +#include "portable_io.h" + +/** + * Compare two osl objects pointing to unsigned integers of 32 bit size. + * + * \param obj1 Pointer to the first integer. + * \param obj2 Pointer to the second integer. + * + * \return The values required for an osl compare function. + * + * \sa osl_compare_func, osl_hash_compare(). + */ +static int uint32_compare(const struct osl_object *obj1, const struct osl_object *obj2) +{ + uint32_t d1 = read_u32((const char *)obj1->data); + uint32_t d2 = read_u32((const char *)obj2->data); + + if (d1 < d2) + return 1; + if (d1 > d2) + return -1; + return 0; +} static struct osl_column_description blob_cols[] = { [BLOBCOL_ID] = { @@ -36,6 +64,24 @@ static struct osl_column_description blob_cols[] = { } }; +/** Define an osl table description for a blob table. */ +#define DEFINE_BLOB_TABLE_DESC(table_name) \ + struct osl_table_description table_name ## _table_desc = { \ + .name = #table_name, \ + .num_columns = NUM_BLOB_COLUMNS, \ + .flags = OSL_LARGE_TABLE, \ + .column_descriptions = blob_cols \ + }; + +/** Define a pointer to an osl blob table with a canonical name. */ +#define DEFINE_BLOB_TABLE_PTR(table_name) struct osl_table *table_name ## _table; + + +/** Define a blob table. */ +#define INIT_BLOB_TABLE(table_name) \ + DEFINE_BLOB_TABLE_DESC(table_name); \ + DEFINE_BLOB_TABLE_PTR(table_name); + /** \cond doxygen isn't smart enough to recognize these */ INIT_BLOB_TABLE(lyrics); INIT_BLOB_TABLE(images); @@ -71,7 +117,7 @@ static int print_blob(struct osl_table *table, struct osl_row *row, if (!(lbad->flags & BLOB_LS_FLAG_LONG)) return para_printf(&lbad->pb, "%s\n", name); - ret = osl_get_object(table, row, BLOBCOL_ID, &obj); + ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj)); if (ret < 0) { para_printf(&lbad->pb, "%s: %s\n", name, para_strerror(-ret)); return ret; @@ -116,7 +162,7 @@ static void com_lsblob_callback(struct osl_table *table, free(lbad.pb.buf); } -static int com_lsblob(callback_function *f, int fd, int argc, char * const * const argv) +static int com_lsblob(callback_function *f, struct rc4_context *rc4c, int argc, char * const * const argv) { uint32_t flags = 0; struct osl_object options = {.data = &flags, .size = sizeof(flags)}; @@ -147,7 +193,7 @@ static int com_lsblob(callback_function *f, int fd, int argc, char * const * con // if (argc > i) // return -E_BLOB_SYNTAX; return send_option_arg_callback_request(&options, argc - i, - argv + i, f, send_result, &fd); + argv + i, f, rc4_send_result, rc4c); } static int cat_blob(struct osl_table *table, struct osl_row *row, @@ -156,12 +202,12 @@ static int cat_blob(struct osl_table *table, struct osl_row *row, int ret = 0, ret2; struct osl_object obj; - ret = osl_open_disk_object(table, row, BLOBCOL_DEF, &obj); + ret = osl(osl_open_disk_object(table, row, BLOBCOL_DEF, &obj)); if (ret < 0) return ret; if (obj.size) ret = pass_buffer_as_shm(obj.data, obj.size, data); - ret2 = osl_close_disk_object(&obj); + ret2 = osl(osl_close_disk_object(&obj)); return (ret < 0)? ret : ret2; } @@ -180,12 +226,13 @@ static void com_catblob_callback(struct osl_table *table, int fd, for_each_matching_row(&pmd); } -static int com_catblob(callback_function *f, int fd, int argc, +static int com_catblob(callback_function *f, struct rc4_context *rc4c, int argc, char * const * const argv) { if (argc < 2) return -E_BLOB_SYNTAX; - return send_standard_callback_request(argc - 1, argv + 1, f, send_result, &fd); + return send_standard_callback_request(argc - 1, argv + 1, f, + rc4_send_result, rc4c); } /** Used for removing rows from a blob table. */ @@ -200,7 +247,7 @@ static int remove_blob(struct osl_table *table, struct osl_row *row, const char *name, void *data) { struct rmblob_data *rmbd = data; - int ret = osl_del_row(table, row); + int ret = osl(osl_del_row(table, row)); if (ret < 0) { para_printf(&rmbd->pb, "%s: %s\n", name, para_strerror(-ret)); return ret; @@ -248,13 +295,13 @@ out: free(rmbd.pb.buf); } -static int com_rmblob(callback_function *f, int fd, int argc, +static int com_rmblob(callback_function *f, struct rc4_context *rc4c, int argc, char * const * const argv) { if (argc < 2) return -E_MOOD_SYNTAX; return send_option_arg_callback_request(NULL, argc - 1, argv + 1, f, - send_result, &fd); + rc4_send_result, rc4c); } static void com_addblob_callback(struct osl_table *table, __a_unused int fd, @@ -267,7 +314,7 @@ static void com_addblob_callback(struct osl_table *table, __a_unused int fd, unsigned num_rows; int ret; - ret = osl_get_num_rows(table, &num_rows); + ret = osl(osl_get_num_rows(table, &num_rows)); if (ret < 0) goto out; if (!num_rows) { /* this is the first entry ever added */ @@ -279,34 +326,34 @@ static void com_addblob_callback(struct osl_table *table, __a_unused int fd, objs[BLOBCOL_NAME].size = 1; objs[BLOBCOL_DEF].data = ""; objs[BLOBCOL_DEF].size = 1; - ret = osl_add_row(table, objs); + ret = osl(osl_add_row(table, objs)); if (ret < 0) goto out; } else { /* check if name already exists */ struct osl_row *row; struct osl_object obj = {.data = name, .size = name_len}; - ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row); - if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND) + ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row)); + if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) goto out; if (ret >= 0) { /* we already have a blob with this name */ obj.data = name + name_len; obj.size = query->size - name_len; - ret = osl_update_object(table, row, BLOBCOL_DEF, &obj); + ret = osl(osl_update_object(table, row, BLOBCOL_DEF, &obj)); goto out; } /* new blob, get id of the dummy row and increment it */ obj.data = ""; obj.size = 1; - ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row); + ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row)); if (ret < 0) goto out; - ret = osl_get_object(table, row, BLOBCOL_ID, &obj); + ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj)); if (ret < 0) goto out; id = *(uint32_t *)obj.data + 1; obj.data = &id; - ret = osl_update_object(table, row, BLOBCOL_ID, &obj); + ret = osl(osl_update_object(table, row, BLOBCOL_ID, &obj)); if (ret < 0) goto out; } @@ -317,7 +364,7 @@ static void com_addblob_callback(struct osl_table *table, __a_unused int fd, objs[BLOBCOL_NAME].size = name_len; objs[BLOBCOL_DEF].data = name + name_len; objs[BLOBCOL_DEF].size = query->size - name_len; - ret = osl_add_row(table, objs); + ret = osl(osl_add_row(table, objs)); if (ret < 0) goto out; afs_event(BLOB_ADD, NULL, table); @@ -326,7 +373,82 @@ out: PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); } -static int com_addblob(callback_function *f, int fd, int argc, +/* + * write input from fd to dynamically allocated buffer, + * but maximal max_size byte. + */ +static int fd2buf(struct rc4_context *rc4c, unsigned max_size, struct osl_object *obj) +{ + const size_t chunk_size = 1024; + size_t size = 2048, received = 0; + int ret; + char *buf = para_malloc(size); + + for (;;) { + ret = rc4_recv_bin_buffer(rc4c, buf + received, chunk_size); + if (ret <= 0) + break; + received += ret; + if (received + chunk_size >= size) { + size *= 2; + ret = -E_INPUT_TOO_LARGE; + if (size > max_size) + break; + buf = para_realloc(buf, size); + } + } + obj->data = buf; + obj->size = received; + if (ret < 0) + free(buf); + return ret; +} + +/* + * Read data from a file descriptor, and send it to the afs process. + * + * \param rc4c crypt context containing the file descriptor to read data from. + * \param arg_obj Pointer to the arguments to \a f. + * \param f The callback function. + * \param max_len Don't read more than that many bytes from stdin. + * \param result_handler See \ref send_callback_request. + * \param private_result_data See \ref send_callback_request. + * + * This function is used by commands that wish to let para_server store + * arbitrary data specified by the user (for instance the add_blob family of + * commands). First, at most \a max_len bytes are read and decrypted from the + * file descriptor given by \a rc4c. The result is concatenated with the buffer + * given by \a arg_obj, and the combined buffer is made available to the afs + * process via the callback method. See \ref send_callback_request for details. + * + * \return Negative on errors, the return value of the underlying call to + * send_callback_request() otherwise. + */ +static int stdin_command(struct rc4_context *rc4c, struct osl_object *arg_obj, + callback_function *f, unsigned max_len, + callback_result_handler *result_handler, + void *private_result_data) +{ + struct osl_object query, stdin_obj; + int ret; + + ret = rc4_send_buffer(rc4c, AWAITING_DATA_MSG); + if (ret < 0) + return ret; + ret = fd2buf(rc4c, max_len, &stdin_obj); + if (ret < 0) + return ret; + query.size = arg_obj->size + stdin_obj.size; + query.data = para_malloc(query.size); + memcpy(query.data, arg_obj->data, arg_obj->size); + memcpy((char *)query.data + arg_obj->size, stdin_obj.data, stdin_obj.size); + free(stdin_obj.data); + ret = send_callback_request(f, &query, result_handler, private_result_data); + free(query.data); + return ret; +} + +static int com_addblob(callback_function *f, struct rc4_context *rc4c, int argc, char * const * const argv) { struct osl_object arg_obj; @@ -337,7 +459,7 @@ static int com_addblob(callback_function *f, int fd, int argc, return -E_BLOB_SYNTAX; arg_obj.size = strlen(argv[1]) + 1; arg_obj.data = (char *)argv[1]; - return stdin_command(fd, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL); + return stdin_command(rc4c, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL); } /* FIXME: Print output to client, not to log file */ @@ -348,13 +470,13 @@ static void com_mvblob_callback(struct osl_table *table, __a_unused int fd, struct osl_object obj = {.data = src, .size = strlen(src) + 1}; char *dest = src + obj.size; struct osl_row *row; - int ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row); + int ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row)); if (ret < 0) goto out; obj.data = dest; obj.size = strlen(dest) + 1; - ret = osl_update_object(table, row, BLOBCOL_NAME, &obj); + ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj)); if (ret < 0) goto out; afs_event(BLOB_RENAME, NULL, table); @@ -363,7 +485,7 @@ out: PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); } -static int com_mvblob(callback_function *f, __a_unused int fd, +static int com_mvblob(callback_function *f, __a_unused struct rc4_context *rc4c, int argc, char * const * const argv) { if (argc != 3) @@ -377,9 +499,9 @@ static int com_mvblob(callback_function *f, __a_unused int fd, { \ return com_ ## cmd_name ## blob_callback(table_name ## _table, fd, query); \ } \ - int com_ ## cmd_name ## cmd_prefix(int fd, int argc, char * const * const argv) \ + int com_ ## cmd_name ## cmd_prefix(struct rc4_context *rc4c, int argc, char * const * const argv) \ { \ - return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, fd, argc, argv); \ + return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, rc4c, argc, argv); \ } static int blob_get_name_by_id(struct osl_table *table, uint32_t id, @@ -392,10 +514,10 @@ static int blob_get_name_by_id(struct osl_table *table, uint32_t id, *name = NULL; if (!id) return 1; - ret = osl_get_row(table, BLOBCOL_ID, &obj, &row); + ret = osl(osl_get_row(table, BLOBCOL_ID, &obj, &row)); if (ret < 0) return ret; - ret = osl_get_object(table, row, BLOBCOL_NAME, &obj); + ret = osl(osl_get_object(table, row, BLOBCOL_NAME, &obj)); if (ret < 0) return ret; *name = (char *)obj.data; @@ -420,10 +542,10 @@ static int blob_get_def_by_name(struct osl_table *table, char *name, def->data = NULL; if (!*name) return 1; - ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row); + ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row)); if (ret < 0) return ret; - return osl_open_disk_object(table, row, BLOBCOL_DEF, def); + return osl(osl_open_disk_object(table, row, BLOBCOL_DEF, def)); } /** Define the \p get_def_by_id function for this blob type. */ @@ -443,10 +565,10 @@ static int blob_get_def_by_id(struct osl_table *table, uint32_t id, def->data = NULL; if (!id) return 1; - ret = osl_get_row(table, BLOBCOL_ID, &obj, &row); + ret = osl(osl_get_row(table, BLOBCOL_ID, &obj, &row)); if (ret < 0) return ret; - return osl_open_disk_object(table, row, BLOBCOL_DEF, def); + return osl(osl_open_disk_object(table, row, BLOBCOL_DEF, def)); } /** Define the \p get_def_by_id function for this blob type. */ @@ -460,11 +582,11 @@ static int blob_get_name_and_def_by_row(struct osl_table *table, const struct osl_row *row, char **name, struct osl_object *def) { struct osl_object obj; - int ret = osl_get_object(table, row, BLOBCOL_NAME, &obj); + int ret = osl(osl_get_object(table, row, BLOBCOL_NAME, &obj)); if (ret < 0) return ret; *name = obj.data; - return osl_open_disk_object(table, row, BLOBCOL_DEF, def); + return osl(osl_open_disk_object(table, row, BLOBCOL_DEF, def)); } /** Define the \p get_name_and_def_by_row function for this blob type. */ #define DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, cmd_prefix) \ @@ -497,11 +619,11 @@ static int blob_open(struct osl_table **table, { int ret; desc->dir = dir; - ret = osl_open_table(desc, table); + ret = osl(osl_open_table(desc, table)); if (ret >= 0) return ret; *table = NULL; - if (ret >= 0 || is_errno(-ret, ENOENT)) + if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT)) return 1; return ret; } diff --git a/buffer_tree.c b/buffer_tree.c new file mode 100644 index 00000000..9884c2e2 --- /dev/null +++ b/buffer_tree.c @@ -0,0 +1,1142 @@ +/* + * Copyright (C) 2009-2010 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file buffer_tree.c Buffer tree and buffer pool implementations. */ +#include +#include + +#include "para.h" +#include "list.h" +#include "string.h" +#include "buffer_tree.h" +#include "error.h" +#include "sched.h" + +/* whead = NULL means area full */ +struct btr_pool { + char *name; + char *area_start; + char *area_end; + char *rhead; + char *whead; +}; + +struct btr_buffer { + char *buf; + size_t size; + /** The number of references to this buffer. */ + int refcount; + /* NULL means no buffer pool but a malloced buffer. */ + struct btr_pool *pool; +}; + +struct btr_buffer_reference { + struct btr_buffer *btrb; + size_t consumed; + /* Each buffer reference belongs to the buffer queue list of some buffer tree node. */ + struct list_head node; + size_t wrap_count; +}; + +struct btr_node { + char *name; + struct btr_node *parent; + /* The position of this btr node in the buffer tree. */ + struct list_head node; + /* The children nodes of this btr node are linked together in a list. */ + struct list_head children; + /* Time of first data transfer. */ + struct timeval start; + /** + * The input queue is a list of references to btr buffers. Each item on + * the list represents an input buffer which has not been completely + * used by this btr node. + */ + struct list_head input_queue; + btr_command_handler execute; + void *context; +}; + +/** + * Create a new buffer pool. + * + * \param name The name of the new buffer pool. + * \param area_size The size in bytes of the pool area. + * + * \return An opaque pointer to the newly created buffer pool. It must be + * passed to btr_pool_free() after it is no longer used to deallocate all + * resources. + */ +struct btr_pool *btr_pool_new(const char *name, size_t area_size) +{ + struct btr_pool *btrp; + + PARA_INFO_LOG("%s, %zu bytes\n", name, area_size); + btrp = para_malloc(sizeof(*btrp)); + btrp->area_start = para_malloc(area_size); + btrp->area_end = btrp->area_start + area_size; + btrp->rhead = btrp->area_start; + btrp->whead = btrp->area_start; + btrp->name = para_strdup(name); + return btrp; +} + +/** + * Deallocate resources used by a buffer pool. + * + * \param btrp A pointer obtained via btr_pool_new(). + */ +void btr_pool_free(struct btr_pool *btrp) +{ + if (!btrp) + return; + free(btrp->area_start); + free(btrp->name); + free(btrp); +} + +/** + * Return the size of the buffer pool area. + * + * \param btrp The buffer pool. + * + * \return The same value which was passed during creation time to + * btr_pool_new(). + */ +size_t btr_pool_size(struct btr_pool *btrp) +{ + return btrp->area_end - btrp->area_start; +} + +static size_t btr_pool_filled(struct btr_pool *btrp) +{ + if (!btrp->whead) + return btr_pool_size(btrp); + if (btrp->rhead <= btrp->whead) + return btrp->whead - btrp->rhead; + return btr_pool_size(btrp) - (btrp->rhead - btrp->whead); +} + +/** + * Get the number of unused bytes in the buffer pool. + * + * \param btrp The pool. + * + * \return The number of bytes that can currently be allocated. + * + * Note that in general the returned number of bytes is not available as a + * single contiguous buffer. Use btr_pool_available() to obtain the length of + * the largest contiguous buffer that can currently be allocated from the + * buffer pool. + */ +size_t btr_pool_unused(struct btr_pool *btrp) +{ + return btr_pool_size(btrp) - btr_pool_filled(btrp); +} + +/* + * Return maximal size available for one read. This is + * smaller than the value returned by btr_pool_unused(). + */ +static size_t btr_pool_available(struct btr_pool *btrp) +{ + if (!btrp->whead) + return 0; + if (btrp->rhead <= btrp->whead) + return btrp->area_end - btrp->whead; + return btrp->rhead - btrp->whead; +} + +/** + * Obtain the current write head. + * + * \param btrp The buffer pool. + * \param result The write head is returned here. + * + * \return The maximal amount of bytes that may be written to the returned + * buffer. + */ +size_t btr_pool_get_buffer(struct btr_pool *btrp, char **result) +{ + if (result) + *result = btrp->whead; + return btr_pool_available(btrp); +} + +/** + * Get references to buffers pointing to free space of the buffer pool area. + * + * \param btrp The buffer pool. + * \param iov The scatter array. + * + * \return Zero if the buffer pool is full, one if the free space of the buffer + * pool area is available as a single contiguous buffer, two if the free space + * consists of two buffers. If this function returns the value n, then n + * elements of \a iov are initialized. + */ +int btr_pool_get_buffers(struct btr_pool *btrp, struct iovec iov[2]) +{ + size_t sz, unused; + char *buf; + + sz = btr_pool_get_buffer(btrp, &buf); + if (sz == 0) + return 0; + iov[0].iov_len = sz; + iov[0].iov_base = buf; + unused = btr_pool_unused(btrp); + if (sz == unused) + return 1; + iov[1].iov_len = unused - sz; + iov[1].iov_base = btrp->area_start; + return 2; +} + +/** + * Mark a part of the buffer pool area as allocated. + * + * \param btrp The buffer pool. + * \param size The amount of bytes to be allocated. + * + * This is usually called after the caller wrote to the buffer obtained by + * btr_pool_get_buffer(). + */ +static void btr_pool_allocate(struct btr_pool *btrp, size_t size) +{ + char *end; + + if (size == 0) + return; + assert(size <= btr_pool_available(btrp)); + end = btrp->whead + size; + assert(end <= btrp->area_end); + + if (end == btrp->area_end) { + PARA_DEBUG_LOG("%s: end of pool area reached\n", btrp->name); + end = btrp->area_start; + } + if (end == btrp->rhead) { + PARA_DEBUG_LOG("%s btrp buffer full\n", btrp->name); + end = NULL; /* buffer full */ + } + btrp->whead = end; +} + +static void btr_pool_deallocate(struct btr_pool *btrp, size_t size) +{ + char *end = btrp->rhead + size; + + if (size == 0) + return; + assert(end <= btrp->area_end); + assert(size <= btr_pool_filled(btrp)); + if (end == btrp->area_end) + end = btrp->area_start; + if (!btrp->whead) + btrp->whead = btrp->rhead; + btrp->rhead = end; + if (btrp->rhead == btrp->whead) + btrp->rhead = btrp->whead = btrp->area_start; +} + +#define FOR_EACH_CHILD(_tn, _btrn) list_for_each_entry((_tn), \ + &((_btrn)->children), node) +#define FOR_EACH_CHILD_SAFE(_tn, _tmp, _btrn) \ + list_for_each_entry_safe((_tn), (_tmp), &((_btrn)->children), node) + +#define FOR_EACH_BUFFER_REF(_br, _btrn) \ + list_for_each_entry((_br), &(_btrn)->input_queue, node) +#define FOR_EACH_BUFFER_REF_SAFE(_br, _tmp, _btrn) \ + list_for_each_entry_safe((_br), (_tmp), &(_btrn)->input_queue, node) + +/** + * Create a new buffer tree node. + * + * \param bnd Specifies how to create the new node. + * + * This function always succeeds (or calls exit()). The returned pointer must + * be freed using btr_free_node() after the node has been removed from the + * buffer tree via btr_remove_node(). + */ +struct btr_node *btr_new_node(struct btr_node_description *bnd) +{ + struct btr_node *btrn = para_malloc(sizeof(*btrn)); + + btrn->name = para_strdup(bnd->name); + btrn->parent = bnd->parent; + btrn->execute = bnd->handler; + btrn->context = bnd->context; + btrn->start.tv_sec = 0; + btrn->start.tv_usec = 0; + INIT_LIST_HEAD(&btrn->children); + INIT_LIST_HEAD(&btrn->input_queue); + if (!bnd->child) { + if (bnd->parent) { + list_add_tail(&btrn->node, &bnd->parent->children); + PARA_INFO_LOG("new leaf node: %s (child of %s)\n", + bnd->name, bnd->parent->name); + } else + PARA_INFO_LOG("added %s as btr root\n", bnd->name); + goto out; + } + if (!bnd->parent) { + assert(!bnd->child->parent); + PARA_INFO_LOG("new root: %s (was %s)\n", + bnd->name, bnd->child->name); + btrn->parent = NULL; + list_add_tail(&bnd->child->node, &btrn->children); + /* link it in */ + bnd->child->parent = btrn; + goto out; + } + PARA_EMERG_LOG("inserting internal nodes not yet supported.\n"); + exit(EXIT_FAILURE); + assert(bnd->child->parent == bnd->parent); +out: + return btrn; +} + +/* + * Allocate a new btr buffer. + * + * The freshly allocated buffer will have a zero refcount and will + * not be associated with a btr pool. + */ +static struct btr_buffer *new_btrb(char *buf, size_t size) +{ + struct btr_buffer *btrb = para_calloc(sizeof(*btrb)); + + btrb->buf = buf; + btrb->size = size; + return btrb; +} + +static void dealloc_buffer(struct btr_buffer *btrb) +{ + if (btrb->pool) + btr_pool_deallocate(btrb->pool, btrb->size); + else + free(btrb->buf); +} + +static struct btr_buffer_reference *get_first_input_br(struct btr_node *btrn) +{ + if (list_empty(&btrn->input_queue)) + return NULL; + return list_first_entry(&btrn->input_queue, + struct btr_buffer_reference, node); +} + +/* + * Deallocate the reference, release the resources if refcount drops to zero. + */ +static void btr_drop_buffer_reference(struct btr_buffer_reference *br) +{ + struct btr_buffer *btrb = br->btrb; + + list_del(&br->node); + free(br); + btrb->refcount--; + if (btrb->refcount == 0) { + dealloc_buffer(btrb); + free(btrb); + } +} + +static void add_btrb_to_children(struct btr_buffer *btrb, + struct btr_node *btrn, size_t consumed) +{ + struct btr_node *ch; + + if (btrn->start.tv_sec == 0) + btrn->start = *now; + FOR_EACH_CHILD(ch, btrn) { + struct btr_buffer_reference *br = para_calloc(sizeof(*br)); + br->btrb = btrb; + br->consumed = consumed; + list_add_tail(&br->node, &ch->input_queue); + btrb->refcount++; + if (ch->start.tv_sec == 0) + ch->start = *now; + } +} + +/** + * Insert a malloced buffer into the buffer tree. + * + * \param buf The buffer to insert. + * \param size The size of \a buf in bytes. + * \param btrn Position in the buffer tree to create the output. + * + * This creates references to \a buf and adds these references to each child of + * \a btrn. The buffer will be freed using standard free() once no buffer tree + * node is referencing it any more. + * + * Note that this function must not be used if \a buf was obtained from a + * buffer pool. Use btr_add_output_pool() in this case. + */ +void btr_add_output(char *buf, size_t size, struct btr_node *btrn) +{ + struct btr_buffer *btrb; + + assert(size != 0); + if (list_empty(&btrn->children)) { + free(buf); + return; + } + btrb = new_btrb(buf, size); + add_btrb_to_children(btrb, btrn, 0); +} + +/** + * Feed data to child nodes of a buffer tree node. + * + * \param btrp The buffer pool. + * \param size The number of bytes to be allocated and fed to each child. + * \param btrn The node whose children are to be fed. + * + * This function allocates the amount of bytes from the buffer pool area, + * starting at the current value of the write head, and creates buffer + * references to the resulting part of the buffer pool area, one for each child + * of \a btrn. The references are then fed into the input queue of each child. + */ +void btr_add_output_pool(struct btr_pool *btrp, size_t size, + struct btr_node *btrn) +{ + struct btr_buffer *btrb; + char *buf; + size_t avail; + + assert(size != 0); + if (list_empty(&btrn->children)) + return; + avail = btr_pool_get_buffer(btrp, &buf); + assert(avail >= size); + btr_pool_allocate(btrp, size); + btrb = new_btrb(buf, size); + btrb->pool = btrp; + add_btrb_to_children(btrb, btrn, 0); +} + +/** + * Copy data to write head of a buffer pool and feed it to all children nodes. + * + * \param src The source buffer. + * \param n The size of the source buffer in bytes. + * \param btrp The destination buffer pool. + * \param btrn Add the data as output of this node. + * + * This is expensive. The caller must make sure the data fits into the buffer + * pool area. + */ +void btr_copy(const void *src, size_t n, struct btr_pool *btrp, + struct btr_node *btrn) +{ + char *buf; + size_t sz, copy; + + if (n == 0) + return; + assert(n <= btr_pool_unused(btrp)); + sz = btr_pool_get_buffer(btrp, &buf); + copy = PARA_MIN(sz, n); + memcpy(buf, src, copy); + btr_add_output_pool(btrp, copy, btrn); + if (copy == n) + return; + sz = btr_pool_get_buffer(btrp, &buf); + assert(sz >= n - copy); + memcpy(buf, src + copy, n - copy); + btr_add_output_pool(btrp, n - copy, btrn); +} + +static void btr_pushdown_br(struct btr_buffer_reference *br, struct btr_node *btrn) +{ + add_btrb_to_children(br->btrb, btrn, br->consumed); + btr_drop_buffer_reference(br); +} + +/** + * Feed all buffer references of the input queue through the output channel. + * + * \param btrn The node whose buffer references should be pushed down. + * + * This function is useful for filters that do not change the contents of the + * buffers at all, like the wav filter or the amp filter if no amplification + * was specified. This function is rather cheap. + * + * \sa \ref btr_pushdown_one(). + */ +void btr_pushdown(struct btr_node *btrn) +{ + struct btr_buffer_reference *br, *tmp; + + FOR_EACH_BUFFER_REF_SAFE(br, tmp, btrn) + btr_pushdown_br(br, btrn); +} + +/** + * Feed the next buffer of the input queue through the output channel. + * + * \param btrn The node whose first input queue buffer should be pushed down. + * + * This works like \ref btr_pushdown() but pushes down only one buffer + * reference. + */ +void btr_pushdown_one(struct btr_node *btrn) +{ + struct btr_buffer_reference *br; + + if (list_empty(&btrn->input_queue)) + return; + br = list_first_entry(&btrn->input_queue, struct btr_buffer_reference, node); + btr_pushdown_br(br, btrn); +} + +/* + * Find out whether a node is a leaf node. + * + * \param btrn The node to check. + * + * \return True if this node has no children. False otherwise. + */ +static bool btr_no_children(struct btr_node *btrn) +{ + return list_empty(&btrn->children); +} + +/** + * Find out whether a node is an orphan node. + * + * \param btrn The buffer tree node. + * + * \return True if \a btrn has no parent. + * + * This function will always return true for the root node. However in case + * nodes have been removed from the tree, other nodes may become orphans too. + */ +bool btr_no_parent(struct btr_node *btrn) +{ + return !btrn->parent; +} + +/** + * Find out whether it is OK to change an input buffer. + * + * \param btrn The buffer tree node to check. + * + * This is used by filters that produce exactly the same amount of output as + * there is input. The amp filter which multiplies each sample by some number + * is an example of such a filter. If there are no other nodes in the buffer + * tree that read the same input stream (i.e. if \a btrn has no siblings), a + * node may modify its input buffer directly and push down the modified buffer + * to its children, thereby avoiding to allocate a possibly large additional + * buffer. + * + * Since the buffer tree may change at any time, this function should be called + * during each post_select call. + * + * \return True if \a btrn has no siblings. + */ +bool btr_inplace_ok(struct btr_node *btrn) +{ + if (!btrn->parent) + return true; + return list_is_singular(&btrn->parent->children); +} + +static inline size_t br_available_bytes(struct btr_buffer_reference *br) +{ + return br->btrb->size - br->consumed; +} + +static size_t btr_get_buffer_by_reference(struct btr_buffer_reference *br, char **buf) +{ + if (buf) + *buf = br->btrb->buf + br->consumed; + return br_available_bytes(br); +} + +/** + * Obtain the next buffer of the input queue of a buffer tree node. + * + * \param btrn The node whose input queue is to be queried. + * \param bufp Result pointer. + * + * \return The number of bytes that can be read from buf. Zero if the input + * buffer queue is empty. In this case the value of \a bufp is undefined. + */ +size_t btr_next_buffer(struct btr_node *btrn, char **bufp) +{ + struct btr_buffer_reference *br; + char *buf, *result = NULL; + size_t sz, rv = 0; + + FOR_EACH_BUFFER_REF(br, btrn) { + sz = btr_get_buffer_by_reference(br, &buf); + if (!result) { + result = buf; + rv = sz; + if (!br->btrb->pool) + break; + continue; + } + if (!br->btrb->pool) + break; + if (result + rv != buf) + break; + rv += sz; + } + if (bufp) + *bufp = result; + return rv; +} + +/** + * Deallocate the given number of bytes from the input queue. + * + * \param btrn The buffer tree node. + * \param numbytes The number of bytes to be deallocated. + * + * This function must be used to get rid of existing buffer references in the + * node's input queue. If no references to a buffer remain, the underlying + * buffers are either freed (in the non-buffer tree case) or the read head of + * the buffer pool is being advanced. + * + * Note that \a numbytes may be smaller than the buffer size. In this case the + * buffer is not deallocated and subsequent calls to btr_next_buffer() return + * the remaining part of the buffer. + */ +void btr_consume(struct btr_node *btrn, size_t numbytes) +{ + struct btr_buffer_reference *br, *tmp; + size_t sz; + + if (numbytes == 0) + return; + br = get_first_input_br(btrn); + assert(br); + + if (br->wrap_count == 0) { + /* + * No wrap buffer. Drop buffer references whose buffer + * has been fully used. */ + FOR_EACH_BUFFER_REF_SAFE(br, tmp, btrn) { + if (br->consumed + numbytes <= br->btrb->size) { + br->consumed += numbytes; + if (br->consumed == br->btrb->size) + btr_drop_buffer_reference(br); + return; + } + numbytes -= br->btrb->size - br->consumed; + btr_drop_buffer_reference(br); + } + assert(true); + } + /* + * We have a wrap buffer, consume from it. If in total, i.e. including + * previous calls to brt_consume(), less than wrap_count has been + * consumed, there's nothing more we can do. + * + * Otherwise we drop the wrap buffer and consume from subsequent + * buffers of the input queue the correct amount of bytes. This is the + * total number of bytes that have been consumed from the wrap buffer. + */ + PARA_DEBUG_LOG("consuming %zu/%zu bytes from wrap buffer\n", numbytes, + br_available_bytes(br)); + + assert(numbytes <= br_available_bytes(br)); + if (br->consumed + numbytes < br->wrap_count) { + br->consumed += numbytes; + return; + } + PARA_DEBUG_LOG("dropping wrap buffer (%zu bytes)\n", br->btrb->size); + /* get rid of the wrap buffer */ + sz = br->consumed + numbytes; + btr_drop_buffer_reference(br); + return btr_consume(btrn, sz); +} + +static void flush_input_queue(struct btr_node *btrn) +{ + struct btr_buffer_reference *br, *tmp; + FOR_EACH_BUFFER_REF_SAFE(br, tmp, btrn) + btr_drop_buffer_reference(br); +} + +/** + * Free all resources allocated by btr_new_node(). + * + * Like free(3), it is OK to call this with a \p NULL pointer argument. + */ +void btr_free_node(struct btr_node *btrn) +{ + if (!btrn) + return; + free(btrn->name); + free(btrn); +} + +/** + * Remove a node from a buffer tree. + * + * \param btrn The node to remove. + * + * This makes all child nodes of \a btrn orphans and removes \a btrn from the + * list of children of its parent. Moreover, the input queue of \a btrn is + * flushed if it is not empty. + * + * \sa \ref btr_splice_out_node. + */ +void btr_remove_node(struct btr_node *btrn) +{ + struct btr_node *ch; + + if (!btrn) + return; + PARA_NOTICE_LOG("removing btr node %s from buffer tree\n", btrn->name); + FOR_EACH_CHILD(ch, btrn) + ch->parent = NULL; + flush_input_queue(btrn); + if (btrn->parent) + list_del(&btrn->node); +} + +/** + * Return the amount of available input bytes of a buffer tree node. + * + * \param btrn The node whose input size should be computed. + * + * \return The total number of bytes available in the node's input + * queue. + * + * This simply iterates over all buffer references in the input queue and + * returns the sum of the sizes of all references. + */ +size_t btr_get_input_queue_size(struct btr_node *btrn) +{ + struct btr_buffer_reference *br; + size_t size = 0, wrap_consumed = 0; + + FOR_EACH_BUFFER_REF(br, btrn) { + if (br->wrap_count != 0) { + wrap_consumed = br->consumed; + continue; + } + size += br_available_bytes(br); + } + assert(wrap_consumed <= size); + size -= wrap_consumed; + return size; +} + +/** + * Remove a node from the buffer tree, reconnecting parent and children. + * + * \param btrn The node to splice out. + * + * This function is used by buffer tree nodes that do not exist during the + * whole lifetime of the buffer tree. Unlike btr_remove_node(), calling + * btr_splice_out_node() does not split the tree into disconnected components + * but reconnects the buffer tree by making all child nodes of \a btrn children + * of the parent of \a btrn. + */ +void btr_splice_out_node(struct btr_node *btrn) +{ + struct btr_node *ch, *tmp; + + assert(btrn); + PARA_NOTICE_LOG("splicing out %s\n", btrn->name); + btr_pushdown(btrn); + if (btrn->parent) + list_del(&btrn->node); + FOR_EACH_CHILD_SAFE(ch, tmp, btrn) { + PARA_INFO_LOG("parent(%s): %s\n", ch->name, + btrn->parent? btrn->parent->name : "NULL"); + ch->parent = btrn->parent; + if (btrn->parent) + list_move(&ch->node, &btrn->parent->children); + } + assert(list_empty(&btrn->children)); +} + +/** + * Return number of queued output bytes of a buffer tree node. + * + * \param btrn The node whose output queue size should be computed. + * + * This function iterates over all children of the given node and returns the + * size of the largest input queue. + */ +size_t btr_get_output_queue_size(struct btr_node *btrn) +{ + size_t max_size = 0; + struct btr_node *ch; + + FOR_EACH_CHILD(ch, btrn) { + size_t size = btr_get_input_queue_size(ch); + max_size = PARA_MAX(max_size, size); + } + return max_size; +} + +int btr_exec(struct btr_node *btrn, const char *command, char **value_result) +{ + if (!btrn) + return -ERRNO_TO_PARA_ERROR(EINVAL); + if (!btrn->execute) + return -ERRNO_TO_PARA_ERROR(ENOTSUP); + return btrn->execute(btrn, command, value_result); +} + +/** + * Execute a inter-node command on a parent node. + * + * \param btrn The node to start looking. + * \param command The command to execute. + * \param value_result Additional arguments and result value. + * + * This function traverses the buffer tree upwards and looks for parent nodes + * of \a btrn that understands \a command. On the first such node the command + * is executed, and the result is stored in \a value_result. + * + * \return \p -ENOTSUP if no parent node of \a btrn understands \a command. + * Otherwise the return value of the command handler is returned. + */ +int btr_exec_up(struct btr_node *btrn, const char *command, char **value_result) +{ + int ret; + + for (; btrn; btrn = btrn->parent) { + struct btr_node *parent = btrn->parent; + if (!parent) + return -ERRNO_TO_PARA_ERROR(ENOTSUP); + if (!parent->execute) + continue; + PARA_INFO_LOG("parent: %s, cmd: %s\n", parent->name, command); + ret = parent->execute(parent, command, value_result); + if (ret == -ERRNO_TO_PARA_ERROR(ENOTSUP)) + continue; + if (ret < 0) + return ret; + if (value_result && *value_result) + PARA_NOTICE_LOG("%s(%s): %s\n", command, parent->name, + *value_result); + return 1; + } + return -ERRNO_TO_PARA_ERROR(ENOTSUP); +} + +/** + * Obtain the context of a buffer node tree. + * + * The returned pointer equals the context pointer used at creation time of the + * node. + * + * \sa btr_new_node(), struct \ref btr_node_description. + */ +void *btr_context(struct btr_node *btrn) +{ + return btrn->context; +} + +static bool need_buffer_pool_merge(struct btr_node *btrn) +{ + struct btr_buffer_reference *br = get_first_input_br(btrn); + + if (!br) + return false; + if (br->wrap_count != 0) + return true; + if (br->btrb->pool) + return true; + return false; +} + +static void merge_input_pool(struct btr_node *btrn, size_t dest_size) +{ + struct btr_buffer_reference *br, *wbr = NULL; + int num_refs; /* including wrap buffer */ + char *buf, *buf1 = NULL, *buf2 = NULL; + size_t sz, sz1 = 0, sz2 = 0, wb_consumed = 0; + + br = get_first_input_br(btrn); + if (!br || br_available_bytes(br) >= dest_size) + return; + num_refs = 0; + FOR_EACH_BUFFER_REF(br, btrn) { + num_refs++; + sz = btr_get_buffer_by_reference(br, &buf); + if (sz == 0) + break; + if (br->wrap_count != 0) { + assert(!wbr); + assert(num_refs == 1); + wbr = br; + if (sz >= dest_size) + return; + wb_consumed = br->consumed; + continue; + } + if (!buf1) { + buf1 = buf; + sz1 = sz; + goto next; + } + if (buf1 + sz1 == buf) { + sz1 += sz; + goto next; + } + if (!buf2) { + buf2 = buf; + sz2 = sz; + goto next; + } + assert(buf2 + sz2 == buf); + sz2 += sz; +next: + if (sz1 + sz2 >= dest_size + wb_consumed) + break; + } + if (!buf2) /* nothing to do */ + return; + assert(buf1 && sz2 > 0); + /* + * If the second buffer is large, we only take the first part of it to + * avoid having to memcpy() huge buffers. + */ + sz2 = PARA_MIN(sz2, (size_t)(64 * 1024)); + if (!wbr) { + /* Make a new wrap buffer combining buf1 and buf2. */ + sz = sz1 + sz2; + buf = para_malloc(sz); + PARA_DEBUG_LOG("merging input buffers: (%p:%zu, %p:%zu) -> %p:%zu\n", + buf1, sz1, buf2, sz2, buf, sz); + memcpy(buf, buf1, sz1); + memcpy(buf + sz1, buf2, sz2); + br = para_calloc(sizeof(*br)); + br->btrb = new_btrb(buf, sz); + br->btrb->refcount = 1; + br->consumed = 0; + /* This is a wrap buffer */ + br->wrap_count = sz1; + para_list_add(&br->node, &btrn->input_queue); + return; + } + /* + * We already have a wrap buffer, but it is too small. It might be + * partially used. + */ + if (wbr->wrap_count == sz1 && wbr->btrb->size >= sz1 + sz2) /* nothing we can do about it */ + return; + sz = sz1 + sz2 - wbr->btrb->size; /* amount of new data */ + PARA_DEBUG_LOG("increasing wrap buffer %zu -> %zu\n", wbr->btrb->size, + wbr->btrb->size + sz); + wbr->btrb->size += sz; + wbr->btrb->buf = para_realloc(wbr->btrb->buf, wbr->btrb->size); + /* copy the new data to the end of the reallocated buffer */ + assert(sz2 >= sz); + memcpy(wbr->btrb->buf + wbr->btrb->size - sz, buf2 + sz2 - sz, sz); +} + +/** + * Merge the first two input buffers into one. + * + * This is a quite expensive operation. + * + * \return The number of buffers that have been available (zero, one or two). + */ +static int merge_input(struct btr_node *btrn) +{ + struct btr_buffer_reference *brs[2], *br; + char *bufs[2], *buf; + size_t szs[2], sz; + int i; + + if (list_empty(&btrn->input_queue)) + return 0; + if (list_is_singular(&btrn->input_queue)) + return 1; + i = 0; + /* get references to the first two buffers */ + FOR_EACH_BUFFER_REF(br, btrn) { + brs[i] = br; + szs[i] = btr_get_buffer_by_reference(brs[i], bufs + i); + i++; + if (i == 2) + break; + } + assert(i == 2); + /* make a new btrb that combines the two buffers and a br to it. */ + sz = szs[0] + szs[1]; + buf = para_malloc(sz); + PARA_DEBUG_LOG("%s: memory merging input buffers: (%zu, %zu) -> %zu\n", + btrn->name, szs[0], szs[1], sz); + memcpy(buf, bufs[0], szs[0]); + memcpy(buf + szs[0], bufs[1], szs[1]); + + br = para_calloc(sizeof(*br)); + br->btrb = new_btrb(buf, sz); + br->btrb->refcount = 1; + + /* replace the first two refs by the new one */ + btr_drop_buffer_reference(brs[0]); + btr_drop_buffer_reference(brs[1]); + para_list_add(&br->node, &btrn->input_queue); + return 2; +} + +/** + * Combine input queue buffers. + * + * \param btrn The buffer tree node whose input should be merged. + * \param dest_size Stop merging if a buffer of at least this size exists. + * + * Used to combine as many buffers as needed into a single buffer whose size is + * at least \a dest_size. This function is rather cheap in case the parent node + * uses buffer pools and rather expensive otherwise. + * + * Note that if less than \a dest_size bytes are available in total, this + * function does nothing and subsequent calls to btr_next_buffer() will still + * return a buffer size less than \a dest_size. + */ +void btr_merge(struct btr_node *btrn, size_t dest_size) +{ + if (need_buffer_pool_merge(btrn)) + return merge_input_pool(btrn, dest_size); + for (;;) { + char *buf; + size_t len = btr_next_buffer(btrn, &buf); + if (len >= dest_size) + return; + PARA_DEBUG_LOG("input size = %zu < %zu = dest\n", len, dest_size); + if (merge_input(btrn) < 2) + return; + } +} + +static bool btr_eof(struct btr_node *btrn) +{ + char *buf; + size_t len = btr_next_buffer(btrn, &buf); + + return (len == 0 && btr_no_parent(btrn)); +} + +static void log_tree_recursively(struct btr_node *btrn, int loglevel, int depth) +{ + struct btr_node *ch; + const char spaces[] = " ", *space = spaces + 16 - depth; + + if (depth > 16) + return; + para_log(loglevel, "%s%s\n", space, btrn->name); + FOR_EACH_CHILD(ch, btrn) + log_tree_recursively(ch, loglevel, depth + 1); +} + +/** + * Write the current buffer (sub-)tree to the log. + * + * \param btrn Start logging at this node. + * \param loglevel Set severity with which the tree should be logged. + */ +void btr_log_tree(struct btr_node *btrn, int loglevel) +{ + return log_tree_recursively(btrn, loglevel, 0); +} + +/** + * Find the node with the given name in the buffer tree. + * + * \param name The name of the node to search. + * \param root Where to start the search. + * + * \return A pointer to the node with the given name on success. If \a name is + * \p NULL, the function returns \a root. If there is no node with the given + * name, \p NULL is returned. + */ +struct btr_node *btr_search_node(const char *name, struct btr_node *root) +{ + struct btr_node *ch; + + if (!name) + return root; + if (!strcmp(root->name, name)) + return root; + FOR_EACH_CHILD(ch, root) { + struct btr_node *result = btr_search_node(name, ch); + if (result) + return result; + } + return NULL; +} + +/** 640K ought to be enough for everybody ;) */ +#define BTRN_MAX_PENDING (640 * 1024) + +/** + * Return the current state of a buffer tree node. + * + * \param btrn The node whose state should be queried. + * \param min_iqs The minimal input queue size. + * \param type The supposed type of \a btrn. + * + * Most users of the buffer tree subsystem call this function from both + * their pre_select and the post_select methods. + * + * \return Negative if an error condition was detected, zero if there + * is nothing to do and positive otherwise. + * + * Examples: + * + * - If a non-root node has no parent and an empty input queue, the function + * returns \p -E_BTR_EOF. Similarly, if a non-leaf node has no children, \p + * -E_BTR_NO_CHILD is returned. + * + * - If less than \a min_iqs many bytes are available in the input queue and no + * EOF condition was detected, the function returns zero. + * + * - If there's plenty of data left in the input queue of the children of \a + * btrn, the function also returns zero in order to bound the memory usage of + * the buffer tree. + */ +int btr_node_status(struct btr_node *btrn, size_t min_iqs, + enum btr_node_type type) +{ + size_t iqs; + + assert(btrn); + if (type != BTR_NT_LEAF) { + if (btr_no_children(btrn)) + return -E_BTR_NO_CHILD; + if (btr_get_output_queue_size(btrn) > BTRN_MAX_PENDING) + return 0; + } + if (type != BTR_NT_ROOT) { + if (btr_eof(btrn)) + return -E_BTR_EOF; + iqs = btr_get_input_queue_size(btrn); + if (iqs == 0) /* we have a parent, because not eof */ + return 0; + if (iqs < min_iqs && !btr_no_parent(btrn)) + return 0; + } + return 1; +} + +/** + * Get the time of the first I/O for a buffer tree node. + * + * \param btrn The node whose I/O time should be obtained. + * \param tv Result pointer. + * + * Mainly useful for the time display of para_audiod. + */ +void btr_get_node_start(struct btr_node *btrn, struct timeval *tv) +{ + *tv = btrn->start; +} diff --git a/buffer_tree.h b/buffer_tree.h new file mode 100644 index 00000000..549f95a2 --- /dev/null +++ b/buffer_tree.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2009-2010 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** + * \file buffer_tree.h Buffer tree management. + * + * \par Buffer trees and buffer tree nodes. + * The buffer tree API offers a more powerful method than standard unix pipes + * for managing the data flow from the producer of the data (e.g. the network + * receiver) to its consumer(s) (e.g. a sound card). + * + * A buffer tree consists of buffer tree nodes linked via certain parent/child + * relationships. + * + * Each data buffer starts its way from the root of the buffer tree. At each + * node the data is investigated and possibly changed. New data is then fed to + * each child. Everything happens within one single-treaded process. There are + * no file descriptors and no calls to read() or write(). + * + * Whenever a node in the buffer tree creates output, either by creating a new + * buffer or by pushing down buffers received from its parent, references to + * that buffer are created for all children of the node. The buffer tree code + * tries hard to avoid to copy buffer contents, but is forced to do so in case + * there are alignment constraints. + * + * Communication between nodes is possible via the btr_exec_up() function. + * For example, in para_audiod the alsa writer asks all parent nodes + * for for the number of channels and the sample rate of the current + * audio file. + * + * Buffer pools - An alternative to malloc/free buffer management. + * + * Non-leaf nodes usually create output to be processed by their children. The + * data must be fed through the output channel(s) of the node in order to make + * that data available to each child. + * + * The easiest way to do so is to malloc() a buffer, fill it, and then call + * btr_add_output(). This adds references to that buffer to all children. The + * buffer is automatically freed if no buffer tree node is using it any more. + * + * This approach, while being simple, has some drawbacks, especially affecting + * the root nodes of the buffer tree. Often the data source which is + * represented by a root node does not know in advance how much data will be + * available. Therefore the allocated buffer is either larger than what can + * currently be read, or is too small so that multiple buffers have to be used. + * + * While this could be worked around by using a large buffer and calling + * realloc() afterwards to shrink the buffer according to how much has been + * read, there is a second problem which comes from the alignment constraints + * of some filters, mainly the decoders like mp3dec. These need a minimal + * amount of data to proceed, and most of them even need this amount as one + * contiguous buffer, i.e. not spread out over two or more buffers. + * + * Although the buffer tree code handles this case just fine, it can be + * expensive because two or more buffers must be merged by copying buffer + * contents around in order to satisfy the constraint. + * + * This is where buffer pools come into play. Buffer pools try to satisfy + * alignment constraints without copying buffer content whenever possible. To + * avoid spreading out the input data over the address space like in the + * malloc/free approach, a fixed large contiguous buffer (the area) is used + * instead. A buffer pool consists basically of an area and two pointers, the + * read head and the write head. + * + * Once a buffer pool has been created, its node, e.g. a receiver, obtains the + * current value of the write head and writes new data to this location. Then + * it calls btr_add_output_pool() to tell much data it has written. This + * advances the write head accordingly, and it also creates references to the + * newly written part of the area for the children of the node to consume. + * + * Child nodes consume data by working through their input queue, which is a + * list of buffer references. Once the content of a buffer is no longer needed + * by a child node, the child calls btr_consume() to indicate the amount of + * data which can be dropped from the child's point of view. If no reference + * to some region of the buffer pool area remains, the read head of the buffer + * pool advances, making space available for the receiver node to fill. + * + * No matter if malloc() or a buffer pool is used, the buffer tree code takes + * care of alignment constraints imposed by the consumers. In the buffer pool + * case, automatic merging of references to contiguous buffers is performed. + * memcpy is only used if a constraint can not be satisfied by using the + * remaining part of the area only. This only happens when the end of the area + * is reached. + */ + +struct btr_node; +struct btr_pool; + +/** + * The three different types of buffer tree nodes. + * + * Usually, there is exactly one node in the buffer tree, the root node, which + * has no parent. Every node different from the root node has exactly one + * parent. The root node represents a data source. Root nodes are thus used by + * the receivers of paraslash. Also, reading from stdin is realized as the root + * node of a buffer tree. + * + * Each node may have arbitrary many children, including none. Nodes with no + * children are called leaf nodes. They represent a data sink, like the alsa or + * the file writer. + * + * Hence there are three different types of buffer tree nodes: The root node + * and the leaf nodes and nodes which have both a parent and at least one + * child. Such a node is called an internal node. + * + * Internal nodes represent filters through which data buffers flow, possibly + * while being altered on their way to the children of the node. Examples of + * internal nodes are audio file decoders (mp3dec, oggdec, ...), but also the + * check for a wav header is implemented as an internal buffer tree node. + */ +enum btr_node_type { + /* This node has no parent. */ + BTR_NT_ROOT, + /* Node has parent and at least one child. */ + BTR_NT_INTERNAL, + /* Node has no children. */ + BTR_NT_LEAF, +}; + +/** + * Per node handler used for inter node communication. + * + * Each node in the buffer tree may optionally provide a command handler for + * execution of commands by other nodes of the tree. + * + * It is dependent on the node in question which commands are supported and how + * they work. In any case, the input for the command handler is some string and + * its output is also a string which is returned via the \a result pointer of + * the handler. + * + * This mechanism is used in para_audiod e.g. by the alsa writer which needs to + * know the sample rate of its input known to e.g. the mp3dec node further up + * in the buffer tree. + */ +typedef int (*btr_command_handler)(struct btr_node *btrn, + const char *command, char **result); + +/** + * Structure for creating new buffer tree nodes. + * + * btr_new_node() takes a pointer to such a structure. + * + * There are four different combinations of \a parent and child: + * + * 1. both \p NULL. This creates a new buffer tree with a single isolated node. + * + * 2. \a parent != \p NULL, \a child == NULL. This creates a new leaf node by + * adding the new node to the list of children of the given parent node. + * + * 3. \a parent == NULL, \a child != NULL. The new node becomes the new root of + * the buffer tree. The child must be old root. + * + * 4. both != NULL. This creates a new internal node. \a child must be child of + * p. This mode of operation is currently not needed and is thus not yet + * implemented. + */ +struct btr_node_description { + /** Name of the new node. */ + const char *name; + /** Parent of the new node. */ + struct btr_node *parent; + /** Child of the new node. */ + struct btr_node *child; + /** Used for inter node communication. Optional. */ + btr_command_handler handler; + /** Points usually to the struct that contains the node pointer. */ + void *context; +}; + +size_t btr_pool_size(struct btr_pool *btrp); +struct btr_pool *btr_pool_new(const char *name, size_t area_size); +void btr_pool_free(struct btr_pool *btrp); +size_t btr_pool_get_buffer(struct btr_pool *btrp, char **result); +int btr_pool_get_buffers(struct btr_pool *btrp, struct iovec iov[2]); +void btr_add_output_pool(struct btr_pool *btrp, size_t size, + struct btr_node *btrn); +size_t btr_pool_unused(struct btr_pool *btrp); +void btr_copy(const void *src, size_t n, struct btr_pool *btrp, + struct btr_node *btrn); + +struct btr_node *btr_new_node(struct btr_node_description *bnd); +void btr_remove_node(struct btr_node *btrn); +void btr_free_node(struct btr_node *btrn); +void btr_add_output(char *buf, size_t size, struct btr_node *btrn); +size_t btr_get_input_queue_size(struct btr_node *btrn); +size_t btr_get_output_queue_size(struct btr_node *btrn); +bool btr_no_parent(struct btr_node *btrn); +size_t btr_next_buffer(struct btr_node *btrn, char **bufp); +void btr_consume(struct btr_node *btrn, size_t numbytes); +int btr_exec_up(struct btr_node *btrn, const char *command, char **value_result); +void btr_splice_out_node(struct btr_node *btrn); +void btr_pushdown(struct btr_node *btrn); +void *btr_context(struct btr_node *btrn); +void btr_merge(struct btr_node *btrn, size_t dest_size); +void btr_log_tree(struct btr_node *btrn, int loglevel); +void btr_pushdown_one(struct btr_node *btrn); +bool btr_inplace_ok(struct btr_node *btrn); +int btr_node_status(struct btr_node *btrn, size_t min_iqs, + enum btr_node_type type); +void btr_get_node_start(struct btr_node *btrn, struct timeval *tv); +struct btr_node *btr_search_node(const char *name, struct btr_node *root); diff --git a/chunk_queue.c b/chunk_queue.c index d295b6c0..e8ea07d9 100644 --- a/chunk_queue.c +++ b/chunk_queue.c @@ -1,11 +1,13 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file chunk_queue.c Queuing functions for paraslash senders. */ +#include + #include "para.h" #include "list.h" #include "afh.h" diff --git a/chunk_queue.h b/chunk_queue.h index 2bed999c..3c138eb5 100644 --- a/chunk_queue.h +++ b/chunk_queue.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/client.c b/client.c index 003c1e60..0b4afbd3 100644 --- a/client.c +++ b/client.c @@ -1,19 +1,25 @@ /* - * Copyright (C) 1997-2009 Andre Noll + * Copyright (C) 1997-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file client.c the client program used to connect to para_server */ +#include +#include +#include + #include "para.h" #include "list.h" #include "sched.h" +#include "crypt.h" #include "client.cmdline.h" #include "string.h" #include "stdin.h" #include "stdout.h" #include "client.h" +#include "buffer_tree.h" #include "error.h" INIT_CLIENT_ERRLISTS; @@ -30,19 +36,12 @@ static void supervisor_post_select(__a_unused struct sched *s, struct task *t) } if (ct->status == CL_SENDING) { stdin_set_defaults(&sit); - sit.buf = para_malloc(sit.bufsize), register_task(&sit.task); - ct->inbuf = sit.buf; - ct->in_loaded = &sit.loaded; - ct->in_error = &sit.task.error; t->error = -E_TASK_STARTED; return; } if (ct->status == CL_RECEIVING) { stdout_set_defaults(&sot); - sot.bufp = &ct->buf; - sot.loaded = &ct->loaded; - sot.input_error = &ct->task.error; register_task(&sot.task); t->error = -E_TASK_STARTED; return; @@ -54,10 +53,9 @@ static struct task svt = { .status = "supervisor task" }; -static int client_loglevel; /* loglevel */ +static int client_loglevel = LL_ERROR; /* loglevel */ INIT_STDERR_LOGGING(client_loglevel); - /** * The client program to connect to para_server. * @@ -65,12 +63,16 @@ INIT_STDERR_LOGGING(client_loglevel); * \param argv Usual argument vector. * * It registers two tasks: The client task that communicates with para_server - * and the standard out task that writes any output produced by the client task - * to standard out. + * and the supervisor task that minitors whether the client task intends to + * read from stdin or write to stdout. + * + * Once it has been determined whether the client command corresponds to a + * stdin command (addmood, addimg, ..), either the stdin task or the stdout + * task is set up to replace the supervisor task. * * \return EXIT_SUCCESS or EXIT_FAILURE * - * \sa client_open(), stdout.c, stdout.h, para_client(1), para_server(1) + * \sa client_open(), stdin.c, stdout.c, para_client(1), para_server(1) */ int main(int argc, char *argv[]) { @@ -78,15 +80,31 @@ int main(int argc, char *argv[]) int ret; static struct sched s; + init_random_seed_or_die(); s.default_timeout.tv_sec = 1; s.default_timeout.tv_usec = 0; - ret = client_open(argc, argv, &ct, &client_loglevel); - if (ret < 0) /* can not use PARA_LOG here because ct is NULL */ - exit(EXIT_FAILURE); + /* + * We add buffer tree nodes for stdin and stdout even though + * only one of them will be needed. This simplifies the code + * a bit wrt. to the buffer tree setup. + */ + sit.btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "stdin")); + ret = client_open(argc, argv, &ct, &client_loglevel, sit.btrn, NULL); + if (ret < 0) + goto out; + sot.btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "stdout", .parent = ct->btrn)); register_task(&svt); ret = schedule(&s); - if (ret < 0) - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); +out: client_close(ct); - return ret >= 0? EXIT_SUCCESS: EXIT_FAILURE; + btr_free_node(sit.btrn); + btr_free_node(sot.btrn); + if (ret < 0) { + /* can not use PARA_LOG here because ct is NULL */ + fprintf(stderr, "%s\n", para_strerror(-ret)); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } diff --git a/client.h b/client.h index c6d5c75d..d4eae31a 100644 --- a/client.h +++ b/client.h @@ -1,80 +1,52 @@ /* - * Copyright (C) 1997-2009 Andre Noll + * Copyright (C) 1997-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ -/** \file client.h common client functions and exported symbols from client_common.c */ +/** \file client.h Common client functions and exported symbols from client_common.c. */ #include -/** - * the different states of a connection from the view of the client - */ +/** The different states of a connection from the view of the client. */ enum { - /** tcp connection is established */ + /** TCP connection is established. */ CL_CONNECTED, - /** server sends the welcome message */ + /** Server sends the welcome message. */ CL_RECEIVED_WELCOME, - /** client sends the authentification request */ + /** Client sends the authentification request. */ CL_SENT_AUTH, - /** server sends a challenge */ - CL_RECEIVED_CHALLENGE, - /** clientd solves the challenge and sends the result */ + /** Client solves the challenge and sends the result. */ CL_SENT_CH_RESPONSE, - /** server accepts this authentication */ + /** Server accepts this authentication. */ CL_RECEIVED_PROCEED, - /** client sends the command */ + /** Client sends the command. */ CL_SENT_COMMAND, - /** server expects data */ + /** Server expects data. */ CL_SENDING, - /** client expects data */ + /** Client expects data. */ CL_RECEIVING, }; -/** size of the receiving buffer */ -#define CLIENT_BUFSIZE 8192 - -/** - * data specific to a client task - */ +/** Data specific to a client task. */ struct client_task { - /** the state of the connection */ + /** The state of the connection. */ int status; - /** the file descriptor */ - int fd; - /** the configuration (including the command) */ + /** The file descriptor and the rc4 keys. */ + struct rc4_context rc4c; + /** The configuration (including the command). */ struct client_args_info conf; - /** the config file for client options */ + /** The config file for client options. */ char *config_file; - /** the RSA private key */ + /** The RSA private key. */ char *key_file; - /** paraslash user name */ + /** Paraslash user name. */ char *user; - /** session key for receiving data */ - RC4_KEY rc4_recv_key; - /** session key for sending data */ - RC4_KEY rc4_send_key; - /** the client task structure */ + /** The client task structure. */ struct task task; - /** the buffer used for handshake and receiving */ - char *buf; - /** number of bytes loaded in \p buf */ - size_t loaded; - /** non-zero if the pre_select hook added \p fd to the read fd set */ - int check_r; - /** non-zero if the pre_select hook added \p fd to the write fd set */ - int check_w; - /** the decrypted challenge */ - long unsigned challenge_nr; - /** pointer to the data to be sent to para_server */ - char *inbuf; - /** number of bytes loaded in \p inbuf */ - size_t *in_loaded; - /** Non-zero if input task encountered an eof or an error condition. */ - int *in_error; + struct btr_node *btrn; }; void client_close(struct client_task *ct); -int client_open(int argc, char *argv[], struct client_task **ct, - int *loglevel); +int client_open(int argc, char *argv[], struct client_task **ct_ptr, + int *loglevel, struct btr_node *parent, struct btr_node *child); diff --git a/client_common.c b/client_common.c index cf77acd8..f34f81bf 100644 --- a/client_common.c +++ b/client_common.c @@ -1,13 +1,15 @@ /* - * Copyright (C) 1997-2009 Andre Noll + * Copyright (C) 1997-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file client_common.c Common functions of para_client and para_audiod. */ +#include #include #include +#include #include "para.h" #include "error.h" @@ -21,33 +23,11 @@ #include "string.h" #include "client.cmdline.h" #include "client.h" +#include "hash.h" +#include "buffer_tree.h" -/* - * Rc4-encrypt data before sending. - * - * \param len The number of bytes to encrypt. - * \param indata Pointer to the input data of length \a len to be encrypted. - * \param outdata Result-pointer that holds the encrypted data. - * \param private_data Contains the rc4 key. - */ -static void rc4_send(unsigned long len, const unsigned char *indata, - unsigned char *outdata, void *private_data) -{ - struct client_task *ct = private_data; - RC4(&ct->rc4_send_key, len, indata, outdata); -} - -/* - * Rc4-decrypt received data. - * - * Parameters are identical to those of rc4_send. - */ -static void rc4_recv(unsigned long len, const unsigned char *indata, - unsigned char *outdata, void *private_data) -{ - struct client_task *ct = private_data; - RC4(&ct->rc4_recv_key, len, indata, outdata); -} +/** The size of the receiving buffer. */ +#define CLIENT_BUFSIZE 4000 /** * Close the connection to para_server and free all resources. @@ -60,11 +40,8 @@ void client_close(struct client_task *ct) { if (!ct) return; - if (ct->fd >= 0) { - disable_crypt(ct->fd); - close(ct->fd); - } - free(ct->buf); + if (ct->rc4c.fd >= 0) + close(ct->rc4c.fd); free(ct->user); free(ct->config_file); free(ct->key_file); @@ -88,62 +65,59 @@ void client_close(struct client_task *ct) */ static void client_pre_select(struct sched *s, struct task *t) { + int ret; struct client_task *ct = container_of(t, struct client_task, task); + struct btr_node *btrn = ct->btrn; - ct->check_r = 0; - ct->check_w = 0; - if (ct->fd < 0) + if (ct->rc4c.fd < 0) return; switch (ct->status) { case CL_CONNECTED: case CL_SENT_AUTH: case CL_SENT_CH_RESPONSE: case CL_SENT_COMMAND: - para_fd_set(ct->fd, &s->rfds, &s->max_fileno); - ct->check_r = 1; + para_fd_set(ct->rc4c.fd, &s->rfds, &s->max_fileno); return; case CL_RECEIVED_WELCOME: - case CL_RECEIVED_CHALLENGE: case CL_RECEIVED_PROCEED: - para_fd_set(ct->fd, &s->wfds, &s->max_fileno); - ct->check_w = 1; + para_fd_set(ct->rc4c.fd, &s->wfds, &s->max_fileno); return; case CL_RECEIVING: - if (ct->loaded < CLIENT_BUFSIZE - 1) { - para_fd_set(ct->fd, &s->rfds, &s->max_fileno); - ct->check_r = 1; + ret = btr_node_status(btrn, 0, BTR_NT_ROOT); + if (ret != 0) { + if (ret < 0) + sched_min_delay(s); + else + para_fd_set(ct->rc4c.fd, &s->rfds, + &s->max_fileno); } return; case CL_SENDING: - if (!ct->in_loaded) /* stdin task not yet started */ - return; - if (*ct->in_loaded) { - PARA_INFO_LOG("loaded: %zd\n", *ct->in_loaded); - para_fd_set(ct->fd, &s->wfds, &s->max_fileno); - ct->check_w = 1; - } else { - if (*ct->in_error) { - t->error = *ct->in_error; - s->timeout.tv_sec = 0; - s->timeout.tv_usec = 1; - } + ret = btr_node_status(btrn, 0, BTR_NT_LEAF); + if (ret != 0) { + if (ret < 0) + sched_min_delay(s); + else + para_fd_set(ct->rc4c.fd, &s->wfds, + &s->max_fileno); } return; } } -static ssize_t client_recv_buffer(struct client_task *ct) +static ssize_t client_recv_buffer(struct client_task *ct, char *buf, size_t len) { - ssize_t ret = recv_buffer(ct->fd, ct->buf + ct->loaded, - CLIENT_BUFSIZE - ct->loaded); - if (!ret) + ssize_t ret; + + if (ct->status < CL_SENT_CH_RESPONSE) + ret = recv_buffer(ct->rc4c.fd, buf, len); + else + ret = rc4_recv_buffer(&ct->rc4c, buf, len); + if (ret == 0) return -E_SERVER_EOF; - if (ret > 0) - ct->loaded += ret; return ret; - } /** @@ -162,87 +136,91 @@ static ssize_t client_recv_buffer(struct client_task *ct) static void client_post_select(struct sched *s, struct task *t) { struct client_task *ct = container_of(t, struct client_task, task); + struct btr_node *btrn = ct->btrn; + int ret = 0; + char buf[CLIENT_BUFSIZE]; t->error = 0; - if (ct->fd < 0) - return; - if (!ct->check_r && !ct->check_w) - return; - if (ct->check_r && !FD_ISSET(ct->fd, &s->rfds)) - return; - if (ct->check_w && !FD_ISSET(ct->fd, &s->wfds)) + if (ct->rc4c.fd < 0) return; switch (ct->status) { case CL_CONNECTED: /* receive welcome message */ - t->error = client_recv_buffer(ct); - if (t->error > 0) - ct->status = CL_RECEIVED_WELCOME; + if (!FD_ISSET(ct->rc4c.fd, &s->rfds)) + return; + ret = client_recv_buffer(ct, buf, sizeof(buf)); + if (ret < 0) + goto err; + ct->status = CL_RECEIVED_WELCOME; return; case CL_RECEIVED_WELCOME: /* send auth command */ - sprintf(ct->buf, "auth %s%s", ct->conf.plain_given? - "" : "rc4 ", ct->user); - PARA_INFO_LOG("--> %s\n", ct->buf); - t->error = send_buffer(ct->fd, ct->buf); - if (t->error >= 0) - ct->status = CL_SENT_AUTH; - return; - case CL_SENT_AUTH: /* receive challenge number */ - ct->loaded = 0; - t->error = client_recv_buffer(ct); - if (t->error < 0) - return; - if (t->error < 64) { - t->error = -E_INVALID_CHALLENGE; - PARA_ERROR_LOG("received the following: %s\n", ct->buf); + sprintf(buf, AUTH_REQUEST_MSG "%s", ct->user); + PARA_INFO_LOG("--> %s\n", buf); + if (!FD_ISSET(ct->rc4c.fd, &s->wfds)) return; - } - PARA_INFO_LOG("<-- [challenge] (%d bytes)\n", t->error); - /* decrypt challenge number */ - t->error = para_decrypt_challenge(ct->key_file, &ct->challenge_nr, - (unsigned char *) ct->buf, t->error); - if (t->error > 0) - ct->status = CL_RECEIVED_CHALLENGE; + ret = send_buffer(ct->rc4c.fd, buf); + if (ret < 0) + goto err; + ct->status = CL_SENT_AUTH; return; - case CL_RECEIVED_CHALLENGE: /* send decrypted challenge */ - PARA_INFO_LOG("--> %lu\n", ct->challenge_nr); - t->error = send_va_buffer(ct->fd, "%s%lu", CHALLENGE_RESPONSE_MSG, - ct->challenge_nr); - if (t->error > 0) - ct->status = CL_SENT_CH_RESPONSE; + case CL_SENT_AUTH: + /* + * Receive challenge and rc4 keys, decrypt the challenge and + * send back the hash of the decrypted challenge. + */ + { + /* decrypted challenge/rc4 buffer */ + unsigned char crypt_buf[1024]; + /* the SHA1 of the decrypted challenge */ + unsigned char challenge_sha1[HASH_SIZE]; + + if (!FD_ISSET(ct->rc4c.fd, &s->rfds)) + return; + ret = client_recv_buffer(ct, buf, sizeof(buf)); + if (ret < 0) + goto err; + PARA_INFO_LOG("<-- [challenge] (%d bytes)\n", ret); + ret = para_decrypt_buffer(ct->key_file, crypt_buf, + (unsigned char *)buf, ret); + if (ret < 0) + goto err; + sha1_hash((char *)crypt_buf, CHALLENGE_SIZE, challenge_sha1); + RC4_set_key(&ct->rc4c.send_key, RC4_KEY_LEN, + crypt_buf + CHALLENGE_SIZE); + RC4_set_key(&ct->rc4c.recv_key, RC4_KEY_LEN, + crypt_buf + CHALLENGE_SIZE + RC4_KEY_LEN); + hash_to_asc(challenge_sha1, buf); + PARA_INFO_LOG("--> %s\n", buf); + ret = send_bin_buffer(ct->rc4c.fd, (char *)challenge_sha1, + HASH_SIZE); + if (ret < 0) + goto err; + ct->status = CL_SENT_CH_RESPONSE; return; + } case CL_SENT_CH_RESPONSE: /* read server response */ { size_t bytes_received; - unsigned char rc4_buf[2 * RC4_KEY_LEN] = ""; - ct->loaded = 0; - t->error = client_recv_buffer(ct); - if (t->error < 0) + if (!FD_ISSET(ct->rc4c.fd, &s->rfds)) return; - bytes_received = t->error; - PARA_DEBUG_LOG("++++ server info ++++\n%s\n++++ end of server " - "info ++++\n", ct->buf); + ret = client_recv_buffer(ct, buf, sizeof(buf)); + if (ret < 0) + goto err; + bytes_received = ret; /* check if server has sent "Proceed" message */ - t->error = -E_CLIENT_AUTH; - if (!strstr(ct->buf, PROCEED_MSG)) - return; - t->error = 0; + ret = -E_CLIENT_AUTH; + if (bytes_received < PROCEED_MSG_LEN) + goto err; + if (!strstr(buf, PROCEED_MSG)) + goto err; ct->status = CL_RECEIVED_PROCEED; - if (bytes_received < PROCEED_MSG_LEN + 32) - return; - PARA_INFO_LOG("decrypting session key\n"); - t->error = para_decrypt_buffer(ct->key_file, rc4_buf, - (unsigned char *)ct->buf + PROCEED_MSG_LEN + 1, - bytes_received - PROCEED_MSG_LEN - 1); - if (t->error < 0) - return; - RC4_set_key(&ct->rc4_send_key, RC4_KEY_LEN, rc4_buf); - RC4_set_key(&ct->rc4_recv_key, RC4_KEY_LEN, rc4_buf + RC4_KEY_LEN); - enable_crypt(ct->fd, rc4_recv, rc4_send, ct); + return; } case CL_RECEIVED_PROCEED: /* concat args and send command */ { int i; char *command = NULL; + if (!FD_ISSET(ct->rc4c.fd, &s->wfds)) + return; for (i = 0; i < ct->conf.inputs_num; i++) { char *tmp = command; command = make_message("%s\n%s", command? @@ -251,32 +229,79 @@ static void client_post_select(struct sched *s, struct task *t) } command = para_strcat(command, EOC_MSG "\n"); PARA_DEBUG_LOG("--> %s\n", command); - t->error = send_buffer(ct->fd, command); + ret = rc4_send_buffer(&ct->rc4c, command); free(command); - if (t->error > 0) - ct->status = CL_SENT_COMMAND; + if (ret < 0) + goto err; + ct->status = CL_SENT_COMMAND; return; } case CL_SENT_COMMAND: - ct->loaded = 0; - t->error = client_recv_buffer(ct); - if (t->error < 0) + { + char *buf2; + if (!FD_ISSET(ct->rc4c.fd, &s->rfds)) return; - if (strstr(ct->buf, AWAITING_DATA_MSG)) + /* can not use "buf" here because we need a malloced buffer */ + buf2 = para_malloc(CLIENT_BUFSIZE); + ret = client_recv_buffer(ct, buf2, CLIENT_BUFSIZE); + if (ret < 0) { + free(buf2); + goto err; + } + if (strstr(buf2, AWAITING_DATA_MSG)) { + free(buf2); ct->status = CL_SENDING; - else - ct->status = CL_RECEIVING; + return; + } + ct->status = CL_RECEIVING; + btr_add_output(buf2, ret, btrn); return; - case CL_SENDING: /* FIXME: might block */ - PARA_INFO_LOG("loaded: %zd\n", *ct->in_loaded); - t->error = send_bin_buffer(ct->fd, ct->inbuf, *ct->in_loaded); - if (t->error < 0) + } + case CL_SENDING: + { + char *buf2; + size_t sz; + ret = btr_node_status(btrn, 0, BTR_NT_LEAF); + if (ret < 0) + goto err; + if (ret == 0) return; - *ct->in_loaded = 0; + if (!FD_ISSET(ct->rc4c.fd, &s->wfds)) + return; + sz = btr_next_buffer(btrn, &buf2); + ret = rc4_send_bin_buffer(&ct->rc4c, buf2, sz); + if (ret < 0) + goto err; + btr_consume(btrn, sz); return; + } case CL_RECEIVING: - t->error = client_recv_buffer(ct); + { + char *buf2; + ret = btr_node_status(btrn, 0, BTR_NT_ROOT); + if (ret < 0) + goto err; + if (ret == 0) + return; + if (!FD_ISSET(ct->rc4c.fd, &s->rfds)) + return; + buf2 = para_malloc(CLIENT_BUFSIZE); + ret = client_recv_buffer(ct, buf2, CLIENT_BUFSIZE); + if (ret < 0) { + free(buf2); + goto err; + } + buf2 = para_realloc(buf2, ret); + btr_add_output(buf2, ret, btrn); return; + } + } +err: + t->error = ret; + if (ret < 0) { + if (ret != -E_SERVER_EOF && ret != -E_BTR_EOF) + PARA_ERROR_LOG("%s\n", para_strerror(-t->error)); + btr_remove_node(btrn); } } @@ -285,14 +310,14 @@ static int client_connect(struct client_task *ct) { int ret; - ct->fd = -1; - ret = makesock(AF_UNSPEC, IPPROTO_TCP, 0, ct->conf.hostname_arg, - ct->conf.server_port_arg); + ct->rc4c.fd = -1; + ret = para_connect_simple(IPPROTO_TCP, ct->conf.hostname_arg, + ct->conf.server_port_arg); if (ret < 0) return ret; - ct->fd = ret; + ct->rc4c.fd = ret; ct->status = CL_CONNECTED; - ret = mark_fd_nonblocking(ct->fd); + ret = mark_fd_nonblocking(ct->rc4c.fd); if (ret < 0) goto err_out; ct->task.pre_select = client_pre_select; @@ -301,8 +326,8 @@ static int client_connect(struct client_task *ct) register_task(&ct->task); return 1; err_out: - close(ct->fd); - ct->fd = -1; + close(ct->rc4c.fd); + ct->rc4c.fd = -1; return ret; } @@ -314,6 +339,8 @@ err_out: * \param ct_ptr Points to dynamically allocated and initialized client task * struct upon successful return. * \param loglevel If not \p NULL, the number of the loglevel is stored here. + * \param parent Add the new buffer tree node as a child of this node. + * \param child Add the new buffer tree node as a parent of this node. * * Check the command line options given by \a argc and argv, set default values * for user name and rsa key file, read further option from the config file. @@ -322,15 +349,16 @@ err_out: * \return Standard. */ int client_open(int argc, char *argv[], struct client_task **ct_ptr, - int *loglevel) + int *loglevel, struct btr_node *parent, struct btr_node *child) { char *home = para_homedir(); int ret; struct client_task *ct = para_calloc(sizeof(struct client_task)); - ct->buf = para_malloc(CLIENT_BUFSIZE); + ct->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "client", .parent = parent, .child = child)); *ct_ptr = ct; - ct->fd = -1; + ct->rc4c.fd = -1; ret = -E_CLIENT_SYNTAX; if (client_cmdline_parser(argc, argv, &ct->conf)) goto out; @@ -338,12 +366,6 @@ int client_open(int argc, char *argv[], struct client_task **ct_ptr, ret = -E_CLIENT_SYNTAX; if (!ct->conf.inputs_num) goto out; - ct->user = ct->conf.user_given? - para_strdup(ct->conf.user_arg) : para_logname(); - - ct->key_file = ct->conf.key_file_given? - para_strdup(ct->conf.key_file_arg) : - make_message("%s/.paraslash/key.%s", home, ct->user); ct->config_file = ct->conf.config_file_given? para_strdup(ct->conf.config_file_arg) : @@ -366,6 +388,13 @@ int client_open(int argc, char *argv[], struct client_task **ct_ptr, &ct->conf, ¶ms)) goto out; } + ct->user = ct->conf.user_given? + para_strdup(ct->conf.user_arg) : para_logname(); + + ct->key_file = ct->conf.key_file_given? + para_strdup(ct->conf.key_file_arg) : + make_message("%s/.paraslash/key.%s", home, ct->user); + if (loglevel) *loglevel = get_loglevel_by_name(ct->conf.loglevel_arg); PARA_INFO_LOG("loglevel: %s\n", ct->conf.loglevel_arg); @@ -378,6 +407,8 @@ out: free(home); if (ret < 0) { PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + btr_remove_node(ct->btrn); + btr_free_node(ct->btrn); client_close(ct); *ct_ptr = NULL; } diff --git a/close_on_fork.c b/close_on_fork.c index 378799c7..ac204e37 100644 --- a/close_on_fork.c +++ b/close_on_fork.c @@ -1,10 +1,13 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file close_on_fork.c Manage a list of fds that should be closed on fork. */ + +#include + #include "para.h" #include "list.h" #include "string.h" diff --git a/command.c b/command.c index f61ea572..b7795a00 100644 --- a/command.c +++ b/command.c @@ -1,19 +1,23 @@ /* - * Copyright (C) 1997-2009 Andre Noll + * Copyright (C) 1997-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file command.c Client authentication and server commands. */ +#include #include #include #include #include #include +#include #include "para.h" #include "error.h" +#include "crypt.h" +#include "command.h" #include "server.cmdline.h" #include "string.h" #include "afh.h" @@ -36,13 +40,12 @@ /** Commands including options must be shorter than this. */ #define MAX_COMMAND_LEN 32768 -static RC4_KEY rc4_recv_key; -static RC4_KEY rc4_send_key; -static unsigned char rc4_buf[2 * RC4_KEY_LEN]; - extern int mmd_mutex; extern struct misc_meta_data *mmd; extern struct sender senders[]; +int send_afs_status(struct rc4_context *rc4c, int parser_friendly); + +const char *status_item_list[] = {STATUS_ITEM_ARRAY}; static void dummy(__a_unused int s) { @@ -102,14 +105,15 @@ static char *vss_get_status_flags(unsigned int flags) return msg; } -static char *get_status(struct misc_meta_data *nmmd) +static char *get_status(struct misc_meta_data *nmmd, int parser_friendly) { - char *ret, mtime[30] = ""; + char mtime[30] = ""; char *status, *flags; /* vss status info */ char *ut = uptime_str(); long offset = (nmmd->offset + 500) / 1000; struct timeval current_time; struct tm mtime_tm; + struct para_buffer b = {.flags = parser_friendly? PBF_SIZE_PREFIX : 0}; /* report real status */ status = vss_status_tohuman(nmmd->vss_status_flags); @@ -119,38 +123,22 @@ static char *get_status(struct misc_meta_data *nmmd) strftime(mtime, 29, "%b %d %Y", &mtime_tm); } gettimeofday(¤t_time, NULL); - ret = make_message( - "%s: %zu\n" /* file size */ - "%s: %s\n" /* mtime */ - "%s: %s\n" /* status */ - "%s: %s\n" /* status flags */ - "%s: %li\n" /* offset */ - "%s: %s\n" /* afs mode */ - "%s: %lu.%lu\n" /* stream start */ - "%s: %lu.%lu\n" /* current server time */ - "%s", /* afs status info */ - status_item_list[SI_FILE_SIZE], nmmd->size / 1024, - status_item_list[SI_MTIME], mtime, - status_item_list[SI_STATUS], status, - status_item_list[SI_STATUS_FLAGS], flags, - - status_item_list[SI_OFFSET], offset, - status_item_list[SI_AFS_MODE], mmd->afs_mode_string, - - status_item_list[SI_STREAM_START], - (long unsigned)nmmd->stream_start.tv_sec, - (long unsigned)nmmd->stream_start.tv_usec, - status_item_list[SI_CURRENT_TIME], - (long unsigned)current_time.tv_sec, - (long unsigned)current_time.tv_usec, - - nmmd->afd.verbose_ls_output - - ); + WRITE_STATUS_ITEM(&b, SI_FILE_SIZE, "%zu\n", nmmd->size / 1024); + WRITE_STATUS_ITEM(&b, SI_MTIME, "%s\n", mtime); + WRITE_STATUS_ITEM(&b, SI_STATUS, "%s\n", status); + WRITE_STATUS_ITEM(&b, SI_STATUS_FLAGS, "%s\n", flags); + WRITE_STATUS_ITEM(&b, SI_OFFSET, "%li\n", offset); + WRITE_STATUS_ITEM(&b, SI_AFS_MODE, "%s\n", mmd->afs_mode_string); + WRITE_STATUS_ITEM(&b, SI_STREAM_START, "%lu.%lu\n", + (long unsigned)nmmd->stream_start.tv_sec, + (long unsigned)nmmd->stream_start.tv_usec); + WRITE_STATUS_ITEM(&b, SI_CURRENT_TIME, "%lu.%lu\n", + (long unsigned)current_time.tv_sec, + (long unsigned)current_time.tv_usec); free(flags); free(status); free(ut); - return ret; + return b.buf; } static int check_sender_args(int argc, char * const * argv, struct sender_command_data *scd) @@ -200,7 +188,7 @@ static int check_sender_args(int argc, char * const * argv, struct sender_comman return 1; } -int com_sender(int fd, int argc, char * const * argv) +int com_sender(struct rc4_context *rc4c, int argc, char * const * argv) { int i, ret; struct sender_command_data scd; @@ -213,7 +201,7 @@ int com_sender(int fd, int argc, char * const * argv) free(msg); msg = tmp; } - ret = send_buffer(fd, msg); + ret = rc4_send_buffer(rc4c, msg); free(msg); return ret; } @@ -223,7 +211,7 @@ int com_sender(int fd, int argc, char * const * argv) if (scd.sender_num < 0) return ret; msg = senders[scd.sender_num].help(); - ret = send_buffer(fd, msg); + ret = rc4_send_buffer(rc4c, msg); free(msg); return ret; } @@ -242,11 +230,11 @@ int com_sender(int fd, int argc, char * const * argv) } /* server info */ -int com_si(int fd, int argc, __a_unused char * const * argv) +int com_si(struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { int i, ret; char *ut; - char *sender_info = NULL, *sender_list = NULL; + char *sender_info = NULL; if (argc != 1) return -E_COMMAND_SYNTAX; @@ -255,17 +243,14 @@ int com_si(int fd, int argc, __a_unused char * const * argv) char *info = senders[i].info(); sender_info = para_strcat(sender_info, info); free(info); - sender_list = para_strcat(sender_list, senders[i].name); - sender_list = para_strcat(sender_list, " "); } ut = uptime_str(); - ret = send_va_buffer(fd, "up: %s\nplayed: %u\n" + ret = rc4_send_va_buffer(rc4c, "up: %s\nplayed: %u\n" "server_pid: %d\n" "afs_pid: %d\n" "connections (active/accepted/total): %u/%u/%u\n" "current loglevel: %s\n" "supported audio formats: %s\n" - "supported senders: %s\n" "%s", ut, mmd->num_played, (int)getppid(), @@ -275,53 +260,129 @@ int com_si(int fd, int argc, __a_unused char * const * argv) mmd->num_connects, conf.loglevel_arg, supported_audio_formats(), - sender_list, sender_info ); mutex_unlock(mmd_mutex); free(ut); - free(sender_list); free(sender_info); return ret; } /* version */ -int com_version(int fd, int argc, __a_unused char * const * argv) +int com_version(struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; - return send_buffer(fd, VERSION_TEXT("server") + return rc4_send_buffer(rc4c, VERSION_TEXT("server") "built: " BUILD_DATE "\n" UNAME_RS ", " CC_VERSION "\n" ); } +#define EMPTY_STATUS_ITEMS \ + ITEM(PATH) \ + ITEM(DIRECTORY) \ + ITEM(BASENAME) \ + ITEM(SCORE) \ + ITEM(ATTRIBUTES_BITMAP) \ + ITEM(ATTRIBUTES_TXT) \ + ITEM(HASH) \ + ITEM(IMAGE_ID) \ + ITEM(IMAGE_NAME) \ + ITEM(LYRICS_ID) \ + ITEM(LYRICS_NAME) \ + ITEM(BITRATE) \ + ITEM(FORMAT) \ + ITEM(FREQUENCY) \ + ITEM(CHANNELS) \ + ITEM(DURATION) \ + ITEM(SECONDS_TOTAL) \ + ITEM(NUM_PLAYED) \ + ITEM(LAST_PLAYED) \ + ITEM(TECHINFO) \ + ITEM(ARTIST) \ + ITEM(TITLE) \ + ITEM(YEAR) \ + ITEM(ALBUM) \ + ITEM(COMMENT) \ + ITEM(AMPLIFICATION) + +/** + * Write a list of audio-file related status items with empty values. + * + * This is used by vss when currently no audio file is open. + */ +static char *empty_status_items(int parser_friendly) +{ + if (parser_friendly) + return make_message( + #define ITEM(x) "0004 %02x:\n" + EMPTY_STATUS_ITEMS + #undef ITEM + #define ITEM(x) , SI_ ## x + EMPTY_STATUS_ITEMS + #undef ITEM + ); + return make_message( + #define ITEM(x) "%s:\n" + EMPTY_STATUS_ITEMS + #undef ITEM + #define ITEM(x) ,status_item_list[SI_ ## x] + EMPTY_STATUS_ITEMS + #undef ITEM + ); +} +#undef EMPTY_STATUS_ITEMS + /* stat */ -int com_stat(int fd, int argc, char * const * argv) +int com_stat(struct rc4_context *rc4c, int argc, char * const * argv) { - int ret, num = 0;/* status will be printed that many - * times. num <= 0 means: print forever - */ + int i, ret; struct misc_meta_data tmp, *nmmd = &tmp; char *s; + int32_t num = 0; + int parser_friendly = 0; para_sigaction(SIGUSR1, dummy); - if (argc > 2) + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (arg[0] != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strncmp(arg, "-n=", 3)) { + ret = para_atoi32(arg + 3, &num); + if (ret < 0) + return ret; + continue; + } + if (!strcmp(arg, "-p")) { + parser_friendly = 1; + continue; + } return -E_COMMAND_SYNTAX; - if (argc > 1) { - ret = para_atoi32(argv[1], &num); - if (ret < 0) - goto out; } + if (i != argc) + return -E_COMMAND_SYNTAX; for (;;) { - mmd_dup(nmmd); - s = get_status(nmmd); - ret = send_buffer(fd, s); + s = get_status(nmmd, parser_friendly); + ret = rc4_send_buffer(rc4c, s); free(s); if (ret < 0) goto out; + if (nmmd->vss_status_flags & VSS_NEXT) { + static char *esi; + if (!esi) + esi = empty_status_items(parser_friendly); + ret = rc4_send_buffer(rc4c, esi); + if (ret < 0) + goto out; + } else + send_afs_status(rc4c, parser_friendly); ret = 1; if (num > 0 && !--num) goto out; @@ -333,14 +394,14 @@ out: return ret; } -static int send_list_of_commands(int fd, struct server_command *cmd, +static int send_list_of_commands(struct rc4_context *rc4c, struct server_command *cmd, const char *handler) { int ret, i; for (i = 1; cmd->name; cmd++, i++) { char *perms = cmd_perms_itohuman(cmd->perms); - ret = send_va_buffer(fd, "%s\t%s\t%s\t%s\n", cmd->name, + ret = rc4_send_va_buffer(rc4c, "%s\t%s\t%s\t%s\n", cmd->name, handler, perms, cmd->description); @@ -373,7 +434,7 @@ static struct server_command *get_cmd_ptr(const char *name, char **handler) } /* help */ -int com_help(int fd, int argc, char * const * argv) +int com_help(struct rc4_context *rc4c, int argc, char * const * argv) { struct server_command *cmd; char *perms, *handler; @@ -381,9 +442,9 @@ int com_help(int fd, int argc, char * const * argv) if (argc < 2) { /* no argument given, print list of commands */ - if ((ret = send_list_of_commands(fd, server_cmds, "server")) < 0) + if ((ret = send_list_of_commands(rc4c, server_cmds, "server")) < 0) return ret; - return send_list_of_commands(fd, afs_cmds, "afs"); + return send_list_of_commands(rc4c, afs_cmds, "afs"); } /* argument given for help */ cmd = get_cmd_ptr(argv[1], &handler); @@ -392,7 +453,7 @@ int com_help(int fd, int argc, char * const * argv) return -E_BAD_CMD; } perms = cmd_perms_itohuman(cmd->perms); - ret = send_va_buffer(fd, + ret = rc4_send_va_buffer(rc4c, "%s - %s\n\n" "handler: %s\n" "permissions: %s\n" @@ -411,7 +472,7 @@ int com_help(int fd, int argc, char * const * argv) } /* hup */ -int com_hup(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_hup(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -420,7 +481,7 @@ int com_hup(__a_unused int fd, int argc, __a_unused char * const * argv) } /* term */ -int com_term(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_term(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -428,7 +489,7 @@ int com_term(__a_unused int fd, int argc, __a_unused char * const * argv) return 1; } -int com_play(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_play(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -441,7 +502,7 @@ int com_play(__a_unused int fd, int argc, __a_unused char * const * argv) } /* stop */ -int com_stop(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_stop(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -454,7 +515,7 @@ int com_stop(__a_unused int fd, int argc, __a_unused char * const * argv) } /* pause */ -int com_pause(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_pause(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -469,7 +530,7 @@ int com_pause(__a_unused int fd, int argc, __a_unused char * const * argv) } /* next */ -int com_next(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_next(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -481,7 +542,7 @@ int com_next(__a_unused int fd, int argc, __a_unused char * const * argv) } /* nomore */ -int com_nomore(__a_unused int fd, int argc, __a_unused char * const * argv) +int com_nomore(__a_unused struct rc4_context *rc4c, int argc, __a_unused char * const * argv) { if (argc != 1) return -E_COMMAND_SYNTAX; @@ -493,7 +554,7 @@ int com_nomore(__a_unused int fd, int argc, __a_unused char * const * argv) } /* ff */ -int com_ff(__a_unused int fd, int argc, char * const * argv) +int com_ff(__a_unused struct rc4_context *rc4c, int argc, char * const * argv) { long promille; int ret, backwards = 0; @@ -532,7 +593,7 @@ out: } /* jmp */ -int com_jmp(__a_unused int fd, int argc, char * const * argv) +int com_jmp(__a_unused struct rc4_context *rc4c, int argc, char * const * argv) { long unsigned int i; int ret; @@ -586,32 +647,7 @@ static struct server_command *parse_cmd(const char *cmdstr) return get_cmd_ptr(buf, NULL); } -static void init_rc4_keys(void) -{ - int i; - - for (i = 0; i < 2 * RC4_KEY_LEN; i++) - rc4_buf[i] = para_random(256); - PARA_DEBUG_LOG("rc4 keys initialized (%u:%u)\n", - (unsigned char) rc4_buf[0], - (unsigned char) rc4_buf[RC4_KEY_LEN]); - RC4_set_key(&rc4_recv_key, RC4_KEY_LEN, rc4_buf); - RC4_set_key(&rc4_send_key, RC4_KEY_LEN, rc4_buf + RC4_KEY_LEN); -} - -static void rc4_recv(unsigned long len, const unsigned char *indata, - unsigned char *outdata, __a_unused void *private_data) -{ - RC4(&rc4_recv_key, len, indata, outdata); -} - -static void rc4_send(unsigned long len, const unsigned char *indata, - unsigned char *outdata, __a_unused void *private_data) -{ - RC4(&rc4_send_key, len, indata, outdata); -} - -static int read_command(int fd, char **result) +static int read_command(struct rc4_context *rc4c, char **result) { int ret; char buf[4096]; @@ -621,7 +657,7 @@ static int read_command(int fd, char **result) size_t numbytes; char *p; - ret = recv_buffer(fd, buf, sizeof(buf)); + ret = rc4_recv_buffer(rc4c, buf, sizeof(buf)); if (ret < 0) goto out; if (!ret) @@ -684,22 +720,22 @@ static void reset_signals(void) */ __noreturn void handle_connect(int fd, const char *peername) { - int ret, argc, use_rc4 = 0; + int ret, argc; char buf[4096]; - unsigned char crypt_buf[MAXLINE]; + unsigned char rand_buf[CHALLENGE_SIZE + 2 * RC4_KEY_LEN]; + unsigned char challenge_sha1[HASH_SIZE]; struct user *u; struct server_command *cmd = NULL; - long unsigned challenge_nr, chall_response; char **argv = NULL; char *p, *command = NULL; size_t numbytes; + struct rc4_context rc4c = {.fd = fd}; reset_signals(); /* we need a blocking fd here as recv() might return EAGAIN otherwise. */ ret = mark_fd_blocking(fd); if (ret < 0) goto err_out; - challenge_nr = random(); /* send Welcome message */ ret = send_va_buffer(fd, "This is para_server, version " PACKAGE_VERSION ".\n" ); @@ -709,65 +745,69 @@ __noreturn void handle_connect(int fd, const char *peername) ret = recv_buffer(fd, buf, sizeof(buf)); if (ret < 0) goto err_out; - if (ret <= 6) { - ret = -E_AUTH; + if (ret < 10) { + ret = -E_AUTH_REQUEST; goto err_out; } numbytes = ret; - ret = -E_AUTH; - if (strncmp(buf, "auth ", 5)) + ret = -E_AUTH_REQUEST; + if (strncmp(buf, AUTH_REQUEST_MSG, strlen(AUTH_REQUEST_MSG))) goto err_out; - - if (numbytes < 9 || strncmp(buf, "auth rc4 ", 9)) - p = buf + 5; /* client version < 0.2.6 */ - else { - p = buf + 9; /* client version >= 0.2.6 */ - use_rc4 = 1; - } - PARA_DEBUG_LOG("received %s request for user %s\n", - use_rc4? "rc4" : "auth", p); + p = buf + strlen(AUTH_REQUEST_MSG); + PARA_DEBUG_LOG("received auth request for user %s\n", p); ret = -E_BAD_USER; u = lookup_user(p); - if (!u) - goto err_out; - ret = para_encrypt_challenge(u->rsa, challenge_nr, crypt_buf); - if (ret <= 0) - goto err_out; - numbytes = ret; - PARA_DEBUG_LOG("sending %zu byte challenge\n", numbytes); - /* We can't use send_buffer here since buf may contain null bytes */ - ret = send_bin_buffer(fd,(char *) crypt_buf, numbytes); + if (u) { + get_random_bytes_or_die(rand_buf, sizeof(rand_buf)); + ret = para_encrypt_buffer(u->rsa, rand_buf, sizeof(rand_buf), + (unsigned char *)buf); + if (ret < 0) + goto err_out; + numbytes = ret; + } else { + /* + * We don't want to reveal our user names, so we send a + * challenge to the client even if the user does not exist, and + * fail the authentication later. + */ + numbytes = 256; + get_random_bytes_or_die((unsigned char *)buf, numbytes); + } + PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n", + CHALLENGE_SIZE, numbytes); + ret = send_bin_buffer(fd, buf, numbytes); if (ret < 0) goto net_err; - /* recv decrypted number */ - ret = recv_buffer(fd, buf, sizeof(buf)); + /* recv challenge response */ + ret = recv_bin_buffer(fd, buf, HASH_SIZE); if (ret < 0) goto net_err; numbytes = ret; - ret = -E_AUTH; - if (!numbytes) + PARA_DEBUG_LOG("received %d bytes challenge response\n", ret); + ret = -E_BAD_USER; + if (!u) goto net_err; - if (sscanf(buf, CHALLENGE_RESPONSE_MSG "%lu", &chall_response) < 1 - || chall_response != challenge_nr) - goto err_out; - /* auth successful, send 'Proceed' message */ - PARA_INFO_LOG("good auth for %s (%lu)\n", u->name, challenge_nr); - sprintf(buf, "%s", PROCEED_MSG); - if (use_rc4) { - init_rc4_keys(); - ret = para_encrypt_buffer(u->rsa, rc4_buf, 2 * RC4_KEY_LEN, - (unsigned char *)buf + PROCEED_MSG_LEN + 1); - if (ret <= 0) - goto err_out; - numbytes = ret + strlen(PROCEED_MSG) + 1; - } else - numbytes = strlen(buf); - ret = send_bin_buffer(fd, buf, numbytes); + /* + * The correct response is the sha1 of the first CHALLENGE_SIZE bytes + * of the random data. + */ + ret = -E_BAD_AUTH; + if (numbytes != HASH_SIZE) + goto net_err; + sha1_hash((char *)rand_buf, CHALLENGE_SIZE, challenge_sha1); + if (memcmp(challenge_sha1, buf, HASH_SIZE)) + goto net_err; + /* auth successful */ + alarm(0); + PARA_INFO_LOG("good auth for %s\n", u->name); + /* init rc4 keys with the second part of the random buffer */ + RC4_set_key(&rc4c.recv_key, RC4_KEY_LEN, rand_buf + CHALLENGE_SIZE); + RC4_set_key(&rc4c.send_key, RC4_KEY_LEN, rand_buf + CHALLENGE_SIZE + + RC4_KEY_LEN); + ret = rc4_send_buffer(&rc4c, PROCEED_MSG); if (ret < 0) goto net_err; - if (use_rc4) - enable_crypt(fd, rc4_recv, rc4_send, NULL); - ret = read_command(fd, &command); + ret = read_command(&rc4c, &command); if (ret == -E_COMMAND_SYNTAX) goto err_out; if (ret < 0) @@ -781,23 +821,25 @@ __noreturn void handle_connect(int fd, const char *peername) if (ret < 0) goto err_out; /* valid command and sufficient perms */ - alarm(0); - argc = split_args(command, &argv, "\n"); + ret = create_argv(command, "\n", &argv); + if (ret < 0) + goto err_out; + argc = ret; PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name, u->name, peername); - ret = cmd->handler(fd, argc, argv); + ret = cmd->handler(&rc4c, argc, argv); + free_argv(argv); mutex_lock(mmd_mutex); mmd->num_commands++; mutex_unlock(mmd_mutex); if (ret >= 0) goto out; err_out: - send_va_buffer(fd, "%s\n", para_strerror(-ret)); + rc4_send_va_buffer(&rc4c, "%s\n", para_strerror(-ret)); net_err: PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); out: free(command); - free(argv); mutex_lock(mmd_mutex); if (cmd && (cmd->perms & AFS_WRITE) && ret >= 0) mmd->events++; diff --git a/command.h b/command.h new file mode 100644 index 00000000..347dd81c --- /dev/null +++ b/command.h @@ -0,0 +1,19 @@ +/** \file command.h The structure of server and afs commands. */ + +/** + * Defines one command of para_server. + */ +struct server_command { + /** The name of the command. */ + const char *name; + /** Pointer to the function that handles the command. */ + int (*handler)(struct rc4_context *, int, char * const * const); + /** The privileges a user must have to execute this command. */ + unsigned int perms; + /** One-line description of the command. */ + const char *description; + /** Summary of the command line options. */ + const char *usage; + /** The long help text. */ + const char *help; +}; diff --git a/command_util.sh b/command_util.sh index 62ad9663..f388b2cf 100755 --- a/command_util.sh +++ b/command_util.sh @@ -26,6 +26,12 @@ read_header() AT:) array_type="$value" ;; + SI:) + for i in $value; do + system_includes="$system_includes +#include <$i.h>" + done + ;; IN:) for i in $value; do includes="$includes @@ -57,7 +63,6 @@ read_one_command() usage_txt="" help_txt="" perms_txt="" - line_handler=0 template=0 template_name="" template_prototype="" @@ -84,9 +89,6 @@ read_one_command() D:) desc_txt="$value" ;; - L:) - line_handler=1 - ;; U:) usage_txt="$value" ;; @@ -198,13 +200,13 @@ dump_proto() echo '/**' echo " * $desc_txt" echo ' *' - echo ' * \param fd The file descriptor to send output to.' - if test $line_handler -eq 0; then - echo ' * \param argc The number of arguments.' - echo ' * \param argv The argument vector.' + if [[ "$system_includes" =~ openssl/rc4.h ]]; then + echo ' * \param rc4c The rc4 crypt context.' else - echo ' * \param cmdline The full command line.' + echo ' * \param fd The file descriptor to send output to.' fi + echo ' * \param argc The number of arguments.' + echo ' * \param argv The argument vector.' echo ' * ' echo " * Usage: $usage_txt" echo ' * ' @@ -229,12 +231,7 @@ dump_array_member() { echo '{' echo ".name = \"$name_txt\"," - if test $line_handler -eq 0; then - echo ".handler = com_$name_txt," - else - echo ".handler = NULL," - echo ".line_handler = com_$name_txt," - fi + echo ".handler = com_$name_txt," if test -n "$perms_txt"; then echo ".perms = $perms_txt," fi @@ -273,6 +270,7 @@ template_loop() com_c_file() { echo "/** \file $output_file.c $c_file_comment */" + echo "$system_includes" echo "$includes" echo "struct $array_type $array_name[] = {" while : ; do diff --git a/compress_filter.c b/compress_filter.c index f853b633..eee37518 100644 --- a/compress_filter.c +++ b/compress_filter.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -10,20 +10,19 @@ * Uses ideas of AudioCompress, (C) 2002-2004 M. Hari Nezumi */ +#include +#include + #include "para.h" #include "compress_filter.cmdline.h" #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "string.h" #include "error.h" -/** The size of the output data buffer. */ -#define COMPRESS_CHUNK_SIZE 40960 - -extern char *stat_item_values[NUM_STAT_ITEMS]; - /** Data specific to the compress filter. */ struct private_compress_data { /** The current multiplier. */ @@ -38,22 +37,48 @@ struct private_compress_data { int peak; }; -static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn) +static void compress_close(struct filter_node *fn) +{ + free(fn->private_data); +} + +static void compress_post_select(__a_unused struct sched *s, struct task *t) { - size_t i, length = PARA_MIN((inbuf_len / 2) * 2, - (fn->bufsize - fn->loaded) / 2 * 2); + struct filter_node *fn = container_of(t, struct filter_node, task); struct private_compress_data *pcd = fn->private_data; - int16_t *ip = (int16_t *)inbuf, *op = (int16_t *)(fn->buf + fn->loaded); + struct btr_node *btrn = fn->btrn; + bool inplace = btr_inplace_ok(btrn); + int ret; + char *inbuf; + size_t length, i; + int16_t *ip, *op; unsigned gain_shift = pcd->conf->inertia_arg + pcd->conf->damp_arg, mask = (1 << pcd->conf->blocksize_arg) - 1; - if (!length) - return 0; + //inplace = false; +next_buffer: + t->error = 0; + ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); + if (ret < 0) + goto err; + if (ret == 0) + return; + btr_merge(btrn, fn->min_iqs); + length = btr_next_buffer(btrn, &inbuf) & ~(size_t)1; + if (length == 0) { /* eof and 1 byte available */ + ret = -E_COMPRESS_EOF; + goto err; + } + ip = (int16_t *)inbuf; + if (inplace) + op = ip; + else + op = para_malloc(length); for (i = 0; i < length / 2; i++) { /* be careful in that heat, my dear */ int sample = *ip++, adjusted_sample = (PARA_ABS(sample) * pcd->current_gain) >> gain_shift; - if (unlikely(adjusted_sample > 32767)) { /* clip */ + if (adjusted_sample > 32767) { /* clip */ PARA_NOTICE_LOG("clip: sample: %d, adjusted sample: %d\n", sample, adjusted_sample); adjusted_sample = 32767; @@ -62,8 +87,8 @@ static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn) pcd->peak = 0; } else pcd->peak = PARA_MAX(pcd->peak, adjusted_sample); - *op++ = sample >= 0? adjusted_sample : -adjusted_sample; - if (likely(++pcd->num_samples & mask)) + op[i] = sample >= 0? adjusted_sample : -adjusted_sample; + if (++pcd->num_samples & mask) continue; // PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain, // pcd->peak); @@ -75,14 +100,17 @@ static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn) 1U << pcd->conf->inertia_arg); pcd->peak = 0; } - fn->loaded += length; - return length; -} - -static void close_compress(struct filter_node *fn) -{ - free(fn->private_data); - free(fn->buf); + if (inplace) + btr_pushdown_one(btrn); + else { + btr_consume(btrn, length); + btr_add_output((char *)op, length, btrn); + } + goto next_buffer; +err: + assert(ret < 0); + t->error = ret; + btr_remove_node(btrn); } /** TODO: Add sanity checks */ @@ -102,18 +130,22 @@ err: return ret; } -static void open_compress(struct filter_node *fn) +static void compress_open(struct filter_node *fn) { struct private_compress_data *pcd = para_calloc( sizeof(struct private_compress_data)); pcd->conf = fn->conf; fn->private_data = pcd; - fn->bufsize = COMPRESS_CHUNK_SIZE; - fn->buf = para_malloc(fn->bufsize); + fn->min_iqs = 2; /* 16 bit audio */ pcd->current_gain = 1 << pcd->conf->inertia_arg; pcd->max_gain = 1 << (pcd->conf->inertia_arg + pcd->conf->aggressiveness_arg); } +static void compress_free_config(void *conf) +{ + compress_cmdline_parser_free(conf); +} + /** * The init function of the compress filter. * @@ -124,10 +156,12 @@ void compress_filter_init(struct filter *f) struct compress_filter_args_info dummy; compress_cmdline_parser_init(&dummy); - f->open = open_compress; - f->close = close_compress; - f->convert = compress; + f->open = compress_open; + f->close = compress_close; + f->pre_select = generic_filter_pre_select; + f->post_select = compress_post_select; f->parse_config = compress_parse_config; + f->free_config = compress_free_config; f->help = (struct ggo_help) { .short_help = compress_filter_args_info_help, .detailed_help = compress_filter_args_info_detailed_help diff --git a/configure.ac b/configure.ac index 74a3c742..80eaf095 100644 --- a/configure.ac +++ b/configure.ac @@ -78,82 +78,196 @@ AC_CHECK_FUNCS([atexit dup2 memchr memmove memset \ strncasecmp strrchr strspn alarm mkdir rmdir], [], [AC_MSG_ERROR([function not found, cannot live without it])]) +cmdline_dir="cmdline" +AC_SUBST(cmdline_dir) +AC_DEFUN([add_cmdline],[$(for i in $@; do printf "${i}.cmdline "; done)]) + + all_errlist_objs="server mp3_afh afh_common vss command net string signal time daemon stat crypt http_send close_on_fork ipc acl afh fade amp_filter -dccp_send fd user_list chunk_queue afs osl aft mood score attribute blob ringbuffer -playlist sha1 rbtree sched audiod grab_client filter_common wav_filter compress_filter +dccp_send fd user_list chunk_queue afs aft mood score attribute blob ringbuffer +playlist sha1 sched audiod grab_client filter_common wav_filter compress_filter http_recv dccp_recv recv_common write_common file_write audiod_command -client_common recv stdout filter stdin audioc write client fsck exec send_common ggo -udp_recv udp_send color fec fecdec_filter prebuffer_filter" +client_common recv stdout filter stdin audioc write client exec send_common ggo +udp_recv udp_send color fec fecdec_filter prebuffer_filter mm +server_command_list afs_command_list audiod_command_list bitstream imdct wma_afh +wma_common wmadec_filter buffer_tree +" + +executables="server recv filter audioc write client afh audiod" -all_executables="server recv filter audioc write client fsck afh" +recv_cmdline_objs="add_cmdline(recv http_recv dccp_recv udp_recv)" -recv_cmdline_objs="recv.cmdline http_recv.cmdline dccp_recv.cmdline udp_recv.cmdline" recv_errlist_objs="http_recv recv_common recv time string net dccp_recv - fd sched stdout ggo udp_recv fec" + fd sched stdout ggo udp_recv fec buffer_tree" recv_ldflags="" -receivers=" http dccp udp" -senders=" http dccp udp" - -filter_cmdline_objs="filter.cmdline compress_filter.cmdline amp_filter.cmdline - prebuffer_filter.cmdline" +filter_cmdline_objs="add_cmdline(filter compress_filter amp_filter prebuffer_filter)" filter_errlist_objs="filter_common wav_filter compress_filter filter string stdin stdout sched fd amp_filter ggo fecdec_filter fec - prebuffer_filter time" -filter_ldflags="" -filters=" compress wav amp fecdec prebuffer" + prebuffer_filter time bitstream imdct wma_common wmadec_filter buffer_tree" +filter_ldflags="-lm" +filters=" compress wav amp fecdec wmadec prebuffer" -audioc_cmdline_objs="audioc.cmdline" +audioc_cmdline_objs="add_cmdline(audioc)" audioc_errlist_objs="audioc string net fd" audioc_ldflags="" -audiod_cmdline_objs="audiod.cmdline grab_client.cmdline compress_filter.cmdline - http_recv.cmdline dccp_recv.cmdline file_write.cmdline client.cmdline - audiod_command_list amp_filter.cmdline udp_recv.cmdline - prebuffer_filter.cmdline" +audiod_cmdline_objs="add_cmdline(audiod compress_filter http_recv dccp_recv file_write client amp_filter udp_recv prebuffer_filter)" audiod_errlist_objs="audiod signal string daemon stat net time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv recv_common fd sched write_common file_write audiod_command crypt fecdec_filter - client_common ggo udp_recv color fec prebuffer_filter" -audiod_ldflags="" -audiod_audio_formats="" + client_common ggo udp_recv color fec prebuffer_filter sha1 audiod_command_list + bitstream imdct wma_common wmadec_filter buffer_tree" +audiod_ldflags="-lm" +audiod_audio_formats="wma" -afh_cmdline_objs="afh.cmdline" -afh_errlist_objs="afh string fd mp3_afh afh_common time" +afh_cmdline_objs="add_cmdline(afh)" +afh_errlist_objs="afh string fd mp3_afh afh_common time wma_afh wma_common" afh_ldflags="" -server_cmdline_objs="server.cmdline server_command_list afs_command_list" +server_cmdline_objs="add_cmdline(server)" server_errlist_objs="server afh_common mp3_afh vss command net string signal - time daemon stat crypt http_send close_on_fork - ipc dccp_send fd user_list chunk_queue afs osl aft mood score attribute - blob playlist sha1 rbtree sched acl send_common udp_send color fec" -server_ldflags="" -server_audio_formats=" mp3" - -write_cmdline_objs="write.cmdline file_write.cmdline" -write_errlist_objs="write write_common file_write time fd string sched stdin ggo" + time daemon crypt http_send close_on_fork mm + ipc dccp_send fd user_list chunk_queue afs aft mood score attribute + blob playlist sha1 sched acl send_common udp_send color fec + server_command_list afs_command_list wma_afh wma_common" +server_ldflags="-losl" +server_audio_formats=" mp3 wma" + +write_cmdline_objs="add_cmdline(write file_write)" +write_errlist_objs="write write_common file_write time fd string sched stdin + buffer_tree ggo" write_ldflags="" writers=" file" default_writer="FILE_WRITE" -client_cmdline_objs="client.cmdline" -client_errlist_objs="client net string crypt fd sched stdin stdout client_common" +client_cmdline_objs="add_cmdline(client)" +client_errlist_objs="client net string crypt fd sched stdin stdout time + client_common sha1 buffer_tree" client_ldflags="" -fsck_cmdline_objs="fsck.cmdline" -fsck_errlist_objs="osl rbtree fsck string sha1 fd" - -gui_cmdline_objs="gui.cmdline" +gui_cmdline_objs="add_cmdline(gui)" gui_errlist_objs="exec signal string stat ringbuffer fd" gui_other_objs="gui gui_theme" gui_objs="$gui_cmdline_objs $gui_errlist_objs $gui_other_objs" -fade_cmdline_objs="fade.cmdline" +fade_cmdline_objs="add_cmdline(fade)" fade_errlist_objs="fade exec string fd" +########################################################################### snprintf +# =========================================================================== +# http://www.nongnu.org/autoconf-archive/ax_func_snprintf.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_FUNC_SNPRINTF +# +# DESCRIPTION +# +# Checks for a fully C99 compliant snprintf, in particular checks whether +# it does bounds checking and returns the correct string length; does the +# same check for vsnprintf. If no working snprintf or vsnprintf is found, +# it prints an error message and aborts. +# +# LICENSE +# +# Copyright (c) 2008 Ruediger Kuhlmann +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. + +AU_ALIAS([AC_FUNC_SNPRINTF], [AX_FUNC_SNPRINTF]) +AC_DEFUN([AX_FUNC_SNPRINTF], +[AC_CHECK_FUNCS(snprintf vsnprintf) +AC_MSG_CHECKING(for working snprintf) +AC_CACHE_VAL(ac_cv_have_working_snprintf, +[AC_TRY_RUN( +[#include + +int main(void) +{ + char bufs[5] = { 'x', 'x', 'x', '\0', '\0' }; + char bufd[5] = { 'x', 'x', 'x', '\0', '\0' }; + int i; + i = snprintf (bufs, 2, "%s", "111"); + if (strcmp (bufs, "1")) exit (1); + if (i != 3) exit (1); + i = snprintf (bufd, 2, "%d", 111); + if (strcmp (bufd, "1")) exit (1); + if (i != 3) exit (1); + exit(0); +}], ac_cv_have_working_snprintf=yes, ac_cv_have_working_snprintf=no, ac_cv_have_working_snprintf=cross)]) +AC_MSG_RESULT([$ac_cv_have_working_snprintf]) +AC_MSG_CHECKING(for working vsnprintf) +AC_CACHE_VAL(ac_cv_have_working_vsnprintf, +[AC_TRY_RUN( +[#include +#include + +int my_vsnprintf (char *buf, const char *tmpl, ...) +{ + int i; + va_list args; + va_start (args, tmpl); + i = vsnprintf (buf, 2, tmpl, args); + va_end (args); + return i; +} + +int main(void) +{ + char bufs[5] = { 'x', 'x', 'x', '\0', '\0' }; + char bufd[5] = { 'x', 'x', 'x', '\0', '\0' }; + int i; + i = my_vsnprintf (bufs, "%s", "111"); + if (strcmp (bufs, "1")) exit (1); + if (i != 3) exit (1); + i = my_vsnprintf (bufd, "%d", 111); + if (strcmp (bufd, "1")) exit (1); + if (i != 3) exit (1); + exit(0); +}], ac_cv_have_working_vsnprintf=yes, ac_cv_have_working_vsnprintf=no, ac_cv_have_working_vsnprintf=cross)]) +AC_MSG_RESULT([$ac_cv_have_working_vsnprintf]) +if test x$ac_cv_have_working_snprintf$ac_cv_have_working_vsnprintf != "xyesyes"; then +AC_MSG_ERROR([fatal: buggy snprintf() detected]) +fi]) +AX_FUNC_SNPRINTF() +########################################################################### osl +have_osl=yes +OLD_CPPFLAGS="$CPPFLAGS" +OLD_LD_FLAGS="$LDFLAGS" +OLD_LIBS="$LIBS" +AC_ARG_WITH(osl_headers, [AC_HELP_STRING(--with-osl-headers=dir, + [look for osl.h also in dir])]) +if test -n "$with_osl_headers"; then + osl_cppflags="-I$with_osl_headers" + CPPFLAGS="$CPPFLAGS $osl_cppflags" +fi +AC_ARG_WITH(osl_libs, [AC_HELP_STRING(--with-osl-libs=dir, + [look for libosl also in dir])]) +if test -n "$with_osl_libs"; then + osl_libs="-L$with_osl_libs" + LDFLAGS="$LDFLAGS $osl_libs" +fi +AC_CHECK_HEADER(osl.h, [], have_osl=no) +AC_CHECK_LIB([osl], [osl_open_table], [], have_osl=no) +if test "$have_osl" = "no"; then + AC_MSG_ERROR([libosl not found, download it at + http://systemlinux.org/~maan/osl +or execute + git clone git://git.tuebingen.mpg.de/osl + ]) +fi +AC_SUBST(osl_cppflags) +server_ldflags="$server_ldflags -L$with_osl_libs" +CPPFLAGS="$OLD_CPPFLAGS" +LDFLAGS="$OLD_LDFLAGS" +LIBS="$OLD_LIBS" ########################################################################### ssl dnl @synopsis CHECK_SSL dnl @@ -184,19 +298,15 @@ AC_DEFUN([CHECK_SSL], SSL_LDFLAGS="-L$ssldir/lib"; fi AC_SUBST(SSL_CPPFLAGS) - AC_SUBST(SSL_CFLAGS) - AC_SUBST(SSL_LIBS) - AC_SUBST(SSL_LDFLAGS) ])dnl AC_ARG_ENABLE(ssldir, [AS_HELP_STRING(--enable-ssldir=path, [Search for openssl also in path.])]) if test "$enable_ssldir" = "yes"; then enable_ssldir=""; fi CHECK_SSL($enable_ssldir) -server_ldflags="$srver_ldflags $SSL_LDFLAGS $SSL_LIBS" +server_ldflags="$server_ldflags $SSL_LDFLAGS $SSL_LIBS" client_ldflags="$client_ldflags $SSL_LDFLAGS $SSL_LIBS" audiod_ldflags="$audiod_ldflags $SSL_LDFLAGS $SSL_LIBS" -fsck_ldflags="$fsck_ldflags $SSL_LDFLAGS $SSL_LIBS" ########################################################################### libsocket AC_CHECK_LIB([c], [socket], @@ -262,10 +372,9 @@ AC_CHECK_LIB([ncurses], [initscr], [], [ ]) if test "$have_ncurses" = "yes"; then AC_SUBST(ncurses_cppflags) - AC_SUBST(ncurses_libs) AC_DEFINE(HAVE_NCURSES, 1, [define to 1 to turn on ncurses support]) - extras="$extras para_gui" - all_executables="$all_executables gui" + extras="$extras gui" + executables="$executables gui" else AC_MSG_WARN([cannot build para_gui]) fi @@ -347,18 +456,17 @@ if test "$have_ogg" = "yes"; then audiod_ldflags="$audiod_ldflags $oggvorbis_libs -lvorbis -lvorbisfile" afh_ldflags="$afh_ldflags $oggvorbis_libs -logg -lvorbis -lvorbisfile" - filter_cmdline_objs="$filter_cmdline_objs oggdec_filter.cmdline" - audiod_cmdline_objs="$audiod_cmdline_objs oggdec_filter.cmdline" + filter_cmdline_objs="$filter_cmdline_objs add_cmdline(oggdec_filter)" + audiod_cmdline_objs="$audiod_cmdline_objs add_cmdline(oggdec_filter)" server_errlist_objs="$server_errlist_objs ogg_afh" filter_errlist_objs="$filter_errlist_objs oggdec_filter" audiod_errlist_objs="$audiod_errlist_objs oggdec_filter" afh_errlist_objs="$afh_errlist_objs ogg_afh" - audiod_audio_formats="ogg" + audiod_audio_formats="$audiod_audio_formats ogg" server_audio_formats="$server_audio_formats ogg" AC_SUBST(oggvorbis_cppflags) - AC_SUBST(oggvorbis_libs) else AC_MSG_WARN([no ogg vorbis support in para_server/para_filter]) fi @@ -399,7 +507,6 @@ if test "$have_faad" = "yes"; then server_audio_formats="$server_audio_formats aac" filters="$filters aacdec" AC_SUBST(faad_cppflags) - AC_SUBST(faad_libs) else AC_MSG_WARN([no aac support in para_audiod/para_filter]) fi @@ -432,8 +539,8 @@ AC_CHECK_LIB([mad], [mad_stream_init], [], [ ]) if test "$have_mad" = "yes"; then AC_DEFINE(HAVE_MAD, 1, define to 1 if you want to build the mp3dec filter) - filter_cmdline_objs="$filter_cmdline_objs mp3dec_filter.cmdline" - audiod_cmdline_objs="$audiod_cmdline_objs mp3dec_filter.cmdline" + filter_cmdline_objs="$filter_cmdline_objs add_cmdline(mp3dec_filter)" + audiod_cmdline_objs="$audiod_cmdline_objs add_cmdline(mp3dec_filter)" all_errlist_objs="$all_errlist_objs mp3dec_filter" filter_errlist_objs="$filter_errlist_objs mp3dec_filter" audiod_errlist_objs="$audiod_errlist_objs mp3dec_filter" @@ -442,16 +549,9 @@ if test "$have_mad" = "yes"; then audiod_audio_formats="$audiod_audio_formats mp3" filters="$filters mp3dec" AC_SUBST(mad_cppflags) - AC_SUBST(mad_libs) else AC_MSG_WARN([no mp3dec support in para_audiod/para_filter]) fi -if test -n "$audiod_audio_formats"; then - extras="$extras para_audiod" - all_executables="$all_executables audiod" -else - AC_MSG_WARN([can not build para_audiod (no supported audio formats)]) -fi CPPFLAGS="$OLD_CPPFLAGS" LDFLAGS="$OLD_LDFLAGS" LIBS="$OLD_LIBS" @@ -479,14 +579,14 @@ have_oss="yes" msg="=> will not build para_fade/oss writer" AC_CHECK_HEADER(sys/soundcard.h, [ - extras="$extras para_fade" - all_executables="$all_executables fade" + extras="$extras fade" + executables="$executables fade" all_errlist_objs="$all_errlist_objs oss_write" audiod_errlist_objs="$audiod_errlist_objs oss_write" - audiod_cmdline_objs="$audiod_cmdline_objs oss_write.cmdline" + audiod_cmdline_objs="$audiod_cmdline_objs add_cmdline(oss_write)" write_errlist_objs="$write_errlist_objs oss_write" - write_cmdline_objs="$write_cmdline_objs oss_write.cmdline" + write_cmdline_objs="$write_cmdline_objs add_cmdline(oss_write)" writers="$writers oss" default_writer="OSS_WRITE" AC_CHECK_LIB(ossaudio, _oss_ioctl, [ @@ -533,11 +633,11 @@ fi if test "$have_alsa" = "yes"; then all_errlist_objs="$all_errlist_objs alsa_write" audiod_errlist_objs="$audiod_errlist_objs alsa_write" - audiod_cmdline_objs="$audiod_cmdline_objs alsa_write.cmdline" + audiod_cmdline_objs="$audiod_cmdline_objs add_cmdline(alsa_write)" audiod_ldflags="$audiod_ldflags -lasound" write_errlist_objs="$write_errlist_objs alsa_write" - write_cmdline_objs="$write_cmdline_objs alsa_write.cmdline" + write_cmdline_objs="$write_cmdline_objs add_cmdline(alsa_write)" write_ldflags="$write_ldflags -lasound" writers="$writers alsa" default_writer="ALSA_WRITE" @@ -547,21 +647,18 @@ CPPFLAGS="$OLD_CPPFLAGS" LDFLAGS="$OLD_LDFLAGS" LIBS="$OLD_LIBS" -AC_SUBST(extra_binaries, [$extras]) -AC_SUBST(extra_filter_objs, [$extra_filter_objs]) -AC_SUBST(extra_filter_libs, [$extra_filter_libs]) AC_SUBST(install_sh, [$INSTALL]) AC_CONFIG_FILES([Makefile]) AC_DEFUN([add_dot_o],[$(for i in $@; do printf "$i.o "; done)]) +AC_DEFUN([add_para],[$(for i in $@; do printf "para_$i "; done)]) AC_DEFUN([objlist_to_errlist],[$(for i in $@; do printf "DEFINE_ERRLIST($(echo $i| tr 'a-z' 'A-Z'));"; done) [const char **para_errlist[[]]] = {$(for i in $@; do printf "PARA_ERRLIST($(echo $i | tr 'a-z' 'A-Z')), "; done) }]) - ############################################################# error2.h AC_DEFUN([define_safe_error_enums], [ exe="" - for i in $all_executables; do + for i in $executables; do # eval echo checking if $1 is linked into $i for j in $(eval echo \$${i}_errlist_objs); do if test $j = $1; then @@ -613,11 +710,12 @@ AC_DEFINE_UNQUOTED(DEFINE_ERRLIST_OBJECT_ENUM, ################################################################## status items status_items="basename status num_played mtime bitrate frequency file_size -status_flags format score audio_file_info taginfo1 taginfo2 afs_mode +status_flags format score techinfo afs_mode attributes_txt decoder_flags audiod_status play_time attributes_bitmap offset seconds_total stream_start current_time audiod_uptime image_id lyrics_id duration directory lyrics_name image_name path hash channels -last_played num_chunks chunk_time amplification" +last_played num_chunks chunk_time amplification artist title year album +comment" # $1: prefix, $2: items AC_DEFUN([make_enum_items], [$( @@ -643,13 +741,14 @@ AC_DEFINE_UNQUOTED(STATUS_ITEM_ARRAY, ) +AC_SUBST(executables, add_para($executables)) + recv_objs="$recv_cmdline_objs $recv_errlist_objs" filter_objs="$filter_cmdline_objs $filter_errlist_objs" audiod_objs="$audiod_cmdline_objs $audiod_errlist_objs" server_objs="$server_cmdline_objs $server_errlist_objs" write_objs="$write_cmdline_objs $write_errlist_objs" client_objs="$client_cmdline_objs $client_errlist_objs" -fsck_objs="$fsck_cmdline_objs $fsck_errlist_objs" audioc_objs="$audioc_cmdline_objs $audioc_errlist_objs" afh_objs="$afh_cmdline_objs $afh_errlist_objs" fade_objs="$fade_cmdline_objs $fade_errlist_objs" @@ -689,11 +788,6 @@ AC_SUBST(client_ldflags, $client_ldflags) AC_DEFINE_UNQUOTED(INIT_CLIENT_ERRLISTS, objlist_to_errlist($client_errlist_objs), errors used by para_client) -AC_SUBST(fsck_objs, add_dot_o($fsck_objs)) -AC_SUBST(fsck_ldflags, $fsck_ldflags) -AC_DEFINE_UNQUOTED(INIT_FSCK_ERRLISTS, - objlist_to_errlist($fsck_errlist_objs), errors used by para_fsck) - AC_SUBST(audioc_objs, add_dot_o($audioc_objs)) AC_SUBST(audioc_ldflags, $audioc_ldflags) AC_DEFINE_UNQUOTED(INIT_AUDIOC_ERRLISTS, @@ -735,16 +829,12 @@ names="$(for i in $audiod_audio_formats; do printf \"$i\",' ' ; done)" AC_DEFINE_UNQUOTED(AUDIOD_AUDIO_FORMAT_ARRAY, $names, array of audio formats supported by audiod) AC_OUTPUT -AC_MSG_NOTICE([creating Makefile.deps]) -gcc -MM -MG $faad_cppflags $mad_cppflags $oggvorbis_cppflags *.c > Makefile.deps AC_MSG_NOTICE([ paraslash configuration: ~~~~~~~~~~~~~~~~~~~~~~~~ unix socket credentials: $have_ucred audio formats supported by para_server/para_afh: $server_audio_formats id3 version2 support: $have_libid3tag -senders supported by para_server: $senders -receivers supported by para_audiod/para_recv: $receivers filters supported by para_audiod/para_filter: $filters writers supported by para_audiod/para_write: $writers optional executables: $extras diff --git a/convert_0.2-0.3.sh b/convert_0.2-0.3.sh deleted file mode 100755 index 24c937b9..00000000 --- a/convert_0.2-0.3.sh +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env bash - -#------------------------------------------------------------------------------- -## Script to convert the database of paraslash 0.2.x to version 0.3.x. -## -## Assumptions: -## - para_server 0.2.x is running and the mysql selector is active -## - para_server 0.3.x is running on another port -## - The database of paraslash 0.3.x has been initialized (i.e. -## para_client init has successfully been executed) -## - All audio files in the mysql database of paraslash 0.2.x. have -## already been added to the 0.3.x database (execute para_client add -## /my/audio/file/dir to do that) -## -## The script converts the attribute table, the set attributes for each audio -## file, the image table and all image ids, and finally the lastplayed and the -## numplayed data. -## -## However, it does not convert the paraslash stream definitions from 0.2.x to -## the moods of 0.3.x. You'll have to do this by hand. -#------------------------------------------------------------------------------- - -# Call this script without arguments to see usage info - -# How to connect to para_server 0.2.x. -client02=/usr/local/bin/para_client -port02=2991 -host02=localhost - -# How to connect to para_server 0.3.x. -client03=./para_client -port03=2990 -host03=localhost - -# Unset this to deactivate messages -debug=1 - - -client02_cmd="$client02 -p $port02 -i $host02" -client03_cmd="$client03 -p $port03 -i $host03" - -info_log() -{ - if test $debug -eq 1; then - echo "$@" - fi -} - -exec_client02_cmd() -{ - info_log "$client02_cmd -- $@" - result="$($client02_cmd -- "$@")" -} - -exec_client03_cmd() -{ - info_log "$client03_cmd -- $@" - result="$($client03_cmd -- "$@")" -} - -convert_attribute_table() -{ - local atts - exec_client02_cmd laa - atts="$result" - info_log "creating attributes: $atts" - exec_client03_cmd addatt $atts -} - -convert_attributes() -{ - local att atts current_atts cmd query="select dir.dir, dir.name" - exec_client02_cmd laa - atts="$result" - for att in $atts; do - query="$query, data.$att" - done - query="$query from dir,data where dir.name=data.name" - exec_client02_cmd verb "$query" - echo "$result" | while read dir name current_atts; do - cmd="setatt " - for att in $atts; do - if test "${current_atts#0}" = "$current_atts"; then - cmd="$cmd $att+" - current_atts=${current_atts#1 } - else - current_atts=${current_atts#0 } - fi - done - if test "$cmd" = "setatt "; then - continue - fi - exec_client03_cmd $cmd "$dir/$name" - done -} - -convert_lastplayed_numplayed() -{ - local query="select dir.dir, dir.name, unix_timestamp(data.lastplayed), data.numplayed from dir,data where data.name=dir.name" - local lp np data dir name - exec_client02_cmd verb "$query" - data="$result" - echo "$result" | while read dir name lp np; do - cmd="touch -n$np -l$lp $dir/$name" - exec_client03_cmd $cmd - done -} - -convert_image_table() -{ - local num size name - exec_client02_cmd piclist; - echo "$result" | while read num size name; do - info_log "converting $name" - $client02_cmd -- pic "#$num" | $client03_cmd -- addimg "$name" - done -} - -convert_image_ids() -{ - local query="select dir.dir, dir.name, pics.name from dir,data,pics where data.name=dir.name and pics.id=data.pic_id" - local img_ids_03 dir name img id - exec_client03_cmd lsimg -l - img_ids_03="$result" - exec_client02_cmd verb "$query" - echo "$result" | while read dir name img; do - id="$(echo "$img_ids_03" | grep " $img\$" | cut -f 1)" - exec_client03_cmd touch "-i$id" "$dir/$name" - done -} - - -usage() -{ - grep '^##' $0 | sed -e 's/^## *//' - echo ' -Usage: $0 command - -command is one of the following: - - attribute_table: create attributes - attributes: convert attributes for each audio file - lastplayed_numplayed: convert numplayed and lastplayed - data of each audio file - image_table: retrieve images from mysql and add them to the database - of paraslash-0.3.x - image_ids: convert image id of each audio file. - all: Do all of the above. - -Edit the top of the script to customize some options. -' -} - -if test $# -ne 1; then - usage - exit 1 -fi - -case "$1" in -attribute_table) - convert_attribute_table - ;; -attributes) - convert_attributes - ;; -lastplayed_numplayed) - convert_lastplayed_numplayed - ;; -image_table) - convert_image_table - ;; -image_ids) - convert_image_ids - ;; -all) - convert_attribute_table - convert_attributes - convert_lastplayed_numplayed - convert_image_table - convert_image_ids - ;; -*) - usage - exit 1 - ;; -esac diff --git a/convert_0.3-0.4.sh b/convert_0.3-0.4.sh new file mode 100755 index 00000000..aa0b239f --- /dev/null +++ b/convert_0.3-0.4.sh @@ -0,0 +1,221 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------- +## Script to convert the database of paraslash 0.3.5 to version 0.4.x. +## +## Assumptions: +## - para_server 0.3.5 is running +## - "para_client check" reports no errors +## - para_server 0.4.x is running, listens on another port and uses a +## different afs database and a different afs socket +## - The database of paraslash 0.4.x has been initialized (i.e. +## para_client init has successfully been executed) +## - All audio files in the 0.3.x database have already been added to +## the 0.4.x database (execute para_client add /my/audio/file/dir to +## do that) +## +#------------------------------------------------------------------------------- + +# Call this script without arguments to see usage info + +# How to connect to para_server 0.3.x. +client03=/usr/local/bin/para_client +port03=2991 +host03=localhost +database03=$HOME/.paraslash/afs_database + +# How to connect to para_server 0.4.x. +client04=$(pwd)/para_client +port04=2990 +host04=localhost +database04=$HOME/.paraslash/afs_database-0.4 + +# Any character that does not occur in any filename of an audio file +sep='|' + + +client03_cmd="$client03 -p $port03 -i $host03" +client04_cmd="$client04 -p $port04 -i $host04" + +exec_client03_cmd() +{ + result="$($client03_cmd -- "$@")" +} + +exec_client04_cmd() +{ + result="$($client04_cmd -- "$@")" +} + +convert_attribute_table() +{ + local atts + + echo "converting attribute table" + exec_client03_cmd lsatt + atts="$result" + exec_client04_cmd addatt $atts +} + +convert_attributes() +{ + local OIFS="$IFS" a p att atts + + printf "converting attributes: " + $client03_cmd -- ls -p -lv \ + | grep '^path:\|^attributes_txt:' \ + | sed -e "/^path:/N;s/\n/$sep/1" \ + | { + IFS="$sep" + while read p a; do + p=${p#path: } + a=${a#attributes_txt:} + IFS=" " + atts= + for att in $a; do + atts="$atts $att+" + done + IFS="$OIFS" + [[ -n "$atts" ]] && $client04_cmd -- setatt $atts "$p" + IFS="$sep" + printf "." + done + echo done + } + IFS="$OIFS" +} + +convert_lna() +{ + local OIFS="$IFS" p l n a + + printf "converting last_played, num_played, amplification values: " + $client03_cmd -- ls -p -d -lv \ + | grep '^path:\|^last_played: \|^num_played: \|^amplification: ' \ + | sed -e "/^path:/N;N;N;s/\n/$sep/g" \ + | { + IFS="$sep" + while read p l n a; do + #echo "p: $p, l:$l, n:$n a:$a" + p=${p#path: } + l=${l#last_played: } + n=${n#num_played: } + a=${a#amplification: } + IFS="$OIFS" + $client04_cmd -- touch "-l$l" "-n$n" "-a$a" "$p" + IFS="$sep" + printf "." + done + echo done + } + IFS="$OIFS" +} + +convert_blobs() +{ + local blob name + + for blob in img lyr mood pl; do + printf "converting $blob table: " + exec_client03_cmd ls$blob + echo "$result" | while read name; do + $client03_cmd -- cat$blob "$name" | $client04_cmd -- add$blob "$name" + printf "." + done + echo done + done +} + +convert_ids() +{ + local OIFS="$IFS" p i y iopts yopts + + printf "converting image and lyrics ids: " + $client03_cmd -- ls -p -lv \ + | grep '^path:\|^image_name: \|^lyrics_name: ' \ + | sed -e "/^path:/N;N;s/\n/$sep/g" \ + | { + IFS="$sep" + while read p i l; do + IFS="$OIFS" + iopts= + yopts= + p=${p#path: } + i=${i#image_name: } + l=${l#lyrics_name: } + if [[ "$i" != '(none)' ]]; then + exec_client04_cmd lsimg -l "$i" + iopts="-i${result%% *}" + fi + if [[ "$l" != '(none)' ]]; then + exec_client04_cmd lslyr -l "$l" + yopts="-y${result%% *}" + fi + if [[ -n "$iopts" && -n "$yopts" ]]; then + $client04_cmd -- touch "$iopts" "$yopts" "$p" + elif [[ -n "$iopts" ]]; then + $client04_cmd -- touch "$iopts" "$p" + elif [[ -n "$yopts" ]]; then + $client04_cmd -- touch "$yopts" "$p" + fi + printf "." + IFS="$sep" + done + echo done + } + IFS="$OIFS" +} + + +usage() +{ + grep '^##' $0 | sed -e 's/^## *//' + echo ' +Usage: $0 command + +command is one of the following: + + attribute_table: create attributes + attributes: convert attributes for each audio file + lna: convert the last_played/num_played/amplification values + blobs: convert the image/lyrics/moods/playlists tables + ids: convert image and the lyrics id of each audio file. + all: Do all of the above. + +Edit the top of the script to customize some options. +' +} + +if test $# -ne 1; then + usage + exit 1 +fi + +case "$1" in +attribute_table) + convert_attribute_table + ;; +attributes) + convert_attributes + ;; +lna) + convert_lna + ;; +blobs) + convert_blobs + ;; +ids) + convert_ids + ;; +all) + convert_attribute_table + convert_attributes + convert_lna + convert_blobs + convert_ids + ;; +*) + usage + exit 1 + ;; +esac diff --git a/crypt.c b/crypt.c index 73eebe18..b977a07a 100644 --- a/crypt.c +++ b/crypt.c @@ -1,21 +1,93 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file crypt.c openssl-based RSA encryption/decryption routines */ +#include +#include +#include +#include +#include +#include +#include + #include "para.h" #include "error.h" #include "string.h" #include "crypt.h" +#include "fd.h" +/** + * Fill a buffer with random content. + * + * \param buf The buffer to fill. + * \param num The size of \a buf in bytes. + * + * This function puts \a num cryptographically strong pseudo-random bytes into + * buf. If libssl can not guarantee an unpredictable byte sequence (for example + * because the PRNG has not been seeded with enough randomness) the function + * logs an error message and calls exit(). + */ +void get_random_bytes_or_die(unsigned char *buf, int num) +{ + unsigned long err; + + /* RAND_bytes() returns 1 on success, 0 otherwise. */ + if (RAND_bytes(buf, num) == 1) + return; + err = ERR_get_error(); + PARA_EMERG_LOG("%s\n", ERR_reason_error_string(err)); + exit(EXIT_FAILURE); +} + +/** + * Seed pseudo random number generators. + * + * This function reads 64 bytes from /dev/urandom and adds them to the SSL + * PRNG. It also seeds the PRNG used by random() with a random seed obtained + * from SSL. If /dev/random could not be read, an error message is logged and + * the function calls exit(). + * + * \sa RAND_load_file(3), \ref get_random_bytes_or_die(), srandom(3), + * random(3), \ref para_random(). + */ +void init_random_seed_or_die(void) +{ + int seed, ret = RAND_load_file("/dev/urandom", 64); + + if (ret != 64) { + PARA_EMERG_LOG("could not seed PRNG (ret = %d)\n", ret); + exit(EXIT_FAILURE); + } + get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed)); + srandom(seed); +} + +static int check_key_file(const char *file, int private) +{ + struct stat st; + + if (stat(file, &st) != 0) + return -ERRNO_TO_PARA_ERROR(errno); + if (private != LOAD_PRIVATE_KEY) + return 0; + if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) + return -E_KEY_PERM; + return 1; +} static EVP_PKEY *load_key(const char *file, int private) { BIO *key; EVP_PKEY *pkey = NULL; + int ret = check_key_file(file, private); + if (ret < 0) { + PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + return NULL; + } key = BIO_new(BIO_s_file()); if (!key) return NULL; @@ -92,35 +164,19 @@ int para_decrypt_buffer(char *key_file, unsigned char *outbuf, unsigned char *in ret = get_rsa_key(key_file, &rsa, LOAD_PRIVATE_KEY); if (ret < 0) return ret; - ret = RSA_private_decrypt(inlen, inbuf, outbuf, rsa, RSA_PKCS1_PADDING); + /* + * RSA is vulnerable to timing attacks. Generate a random blinding + * factor to protect against this kind of attack. + */ + ret = -E_BLINDING; + if (RSA_blinding_on(rsa, NULL) == 0) + goto out; + ret = RSA_private_decrypt(inlen, inbuf, outbuf, rsa, RSA_PKCS1_OAEP_PADDING); + RSA_blinding_off(rsa); + if (ret <= 0) + ret = -E_DECRYPT; +out: rsa_free(rsa); - return (ret > 0)? ret : -E_DECRYPT; -} - -/** - * decrypt the challenge number sent by para_server - * - * \param key_file full path of the rsa key - * \param challenge_nr result is stored here - * \param inbuf the input buffer - * \param rsa_inlen the length of \a inbuf - * - * \return positive on success, negative on errors - * - * \sa para_decrypt_buffer() - */ -int para_decrypt_challenge(char *key_file, long unsigned *challenge_nr, - unsigned char *inbuf, unsigned rsa_inlen) -{ - unsigned char *rsa_out = OPENSSL_malloc(rsa_inlen + 1); - int ret = para_decrypt_buffer(key_file, rsa_out, inbuf, rsa_inlen); - - if (ret >= 0) { - rsa_out[ret] = '\0'; - ret = sscanf((char *)rsa_out, "%lu", challenge_nr) == 1? - 1 : -E_CHALLENGE; - } - OPENSSL_free(rsa_out); return ret; } @@ -143,30 +199,114 @@ int para_encrypt_buffer(RSA *rsa, unsigned char *inbuf, if (flen < 0) return -E_ENCRYPT; - ret = RSA_public_encrypt(flen, inbuf, outbuf, rsa, RSA_PKCS1_PADDING); - return ret < 0? -E_ENCRYPT : ret; + ret = RSA_public_encrypt(flen, inbuf, outbuf, rsa, RSA_PKCS1_OAEP_PADDING); + return ret < 0? -E_ENCRYPT : ret; } /** - * encrypt the given challenge number + * Encrypt and send a buffer. * - * \param rsa: public rsa key - * \param challenge_nr the number to be encrypted - * \param outbuf the output buffer + * \param rc4c The rc4 crypt context. + * \param buf The buffer to send. + * \param len The size of \a buf in bytes. * - * \a outbuf must be at least 64 bytes long + * \return The return value of the underyling call to write_all(). * - * \return The size of the encrypted data on success, negative on errors + * \sa \ref write_all(), RC4(3). + */ +int rc4_send_bin_buffer(struct rc4_context *rc4c, const char *buf, size_t len) +{ + int ret; + unsigned char *tmp; + + assert(len); + tmp = para_malloc(len); + RC4(&rc4c->send_key, len, (const unsigned char *)buf, tmp); + ret = write_all(rc4c->fd, (char *)tmp, &len); + free(tmp); + return ret; +} + +/** + * Encrypt and send a \p NULL-terminated buffer. + * + * \param rc4c The rc4 crypt context. + * \param buf The buffer to send. + * + * \return The return value of the underyling call to rc4_send_bin_buffer(). + */ +int rc4_send_buffer(struct rc4_context *rc4c, const char *buf) +{ + return rc4_send_bin_buffer(rc4c, buf, strlen(buf)); +} + +/** + * Format, encrypt and send a buffer. + * + * \param rc4c The rc4 crypt context. + * \param fmt A format string. * - * \sa para_encrypt_buffer() + * \return The return value of the underyling call to rc4_send_buffer(). + */ +__printf_2_3 int rc4_send_va_buffer(struct rc4_context *rc4c, const char *fmt, ...) +{ + char *msg; + int ret; + + PARA_VSPRINTF(fmt, msg); + ret = rc4_send_buffer(rc4c, msg); + free(msg); + return ret; +} + +/** + * Receive a buffer and decrypt it. + * + * \param rc4c The rc4 crypt context. + * \param buf The buffer to write the decrypted data to. + * \param size The size of \a buf. + * + * \return The number of bytes received on success, negative on errors, zero if + * the peer has performed an orderly shutdown. * + * \sa recv(2), RC4(3). */ -int para_encrypt_challenge(RSA* rsa, long unsigned challenge_nr, - unsigned char *outbuf) +int rc4_recv_bin_buffer(struct rc4_context *rc4c, char *buf, size_t size) { - unsigned char *inbuf = (unsigned char*) make_message("%lu", challenge_nr); - int ret = para_encrypt_buffer(rsa, inbuf, strlen((char *)inbuf), outbuf); - free(inbuf); + unsigned char *tmp = para_malloc(size); + ssize_t ret = recv(rc4c->fd, tmp, size, 0); + + if (ret > 0) + RC4(&rc4c->recv_key, ret, tmp, (unsigned char *)buf); + else if (ret < 0) + ret = -ERRNO_TO_PARA_ERROR(errno); + free(tmp); return ret; } +/** + * Receive a buffer, decrypt it and write terminating NULL byte. + * + * \param rc4c The rc4 crypt context. + * \param buf The buffer to write the decrypted data to. + * \param size The size of \a buf. + * + * Read at most \a size - 1 bytes from file descriptor given by \a rc4c, + * decrypt the received data and write a NULL byte at the end of the decrypted + * data. + * + * \return The return value of the underlying call to \ref + * rc4_recv_bin_buffer(). + */ +int rc4_recv_buffer(struct rc4_context *rc4c, char *buf, size_t size) +{ + int n; + + assert(size); + n = rc4_recv_bin_buffer(rc4c, buf, size - 1); + if (n >= 0) + buf[n] = '\0'; + else + *buf = '\0'; + return n; +} diff --git a/crypt.h b/crypt.h index 88af9e89..8357b6d7 100644 --- a/crypt.h +++ b/crypt.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -7,10 +7,6 @@ /** \file crypt.h prototypes for the RSA crypt functions */ #include -int para_decrypt_challenge(char *key_file, long unsigned *challenge_nr, - unsigned char *buf, unsigned rsa_inlen); -int para_encrypt_challenge(RSA* rsa, long unsigned challenge_nr, - unsigned char *outbuf); int para_encrypt_buffer(RSA* rsa, unsigned char *inbuf, unsigned len, unsigned char *outbuf); int para_decrypt_buffer(char *key_file, unsigned char *outbuf, unsigned char *inbuf, @@ -18,8 +14,32 @@ int para_decrypt_buffer(char *key_file, unsigned char *outbuf, unsigned char *in int get_rsa_key(char *key_file, RSA **rsa, int private); void rsa_free(RSA *rsa); +void get_random_bytes_or_die(unsigned char *buf, int num); +void init_random_seed_or_die(void); + +/** + * Used on the server-side for client-server communication encryption. + * + * The traffic between (the forked child of) para_server and the remote + * client process is crypted by a RC4 session key. This structure contains + * the RC4 keys and the file descriptor for which these keys should be used. + */ +struct rc4_context { + /** The socket file descriptor. */ + int fd; + /** Key used for sending data. */ + RC4_KEY recv_key; + /** Key used for receiving data. */ + RC4_KEY send_key; +}; +int rc4_send_bin_buffer(struct rc4_context *rc4c, const char *buf, size_t len); +int rc4_send_buffer(struct rc4_context *rc4c, const char *buf); +__printf_2_3 int rc4_send_va_buffer(struct rc4_context *rc4c, const char *fmt, ...); +int rc4_recv_bin_buffer(struct rc4_context *rcc, char *buf, size_t size); +int rc4_recv_buffer(struct rc4_context *rcc, char *buf, size_t size); /** \cond used to distinguish between loading of private/public key */ #define LOAD_PUBLIC_KEY 0 #define LOAD_PRIVATE_KEY 1 +#define CHALLENGE_SIZE 64 /** \endcond **/ diff --git a/daemon.c b/daemon.c index 011ece6d..eef504f1 100644 --- a/daemon.c +++ b/daemon.c @@ -1,16 +1,20 @@ /* - * Copyright (C) 1997-2009 Andre Noll + * Copyright (C) 1997-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file daemon.c Some helpers for programs that detach from the console. */ -#include "para.h" -#include "daemon.h" + +#include #include #include /* getgrnam() */ #include +#include +#include +#include "para.h" +#include "daemon.h" #include "string.h" #include "color.h" @@ -139,7 +143,7 @@ void daemon_clear_flag(unsigned flag) me->flags &= ~flag; } -static unsigned daemon_test_flag(unsigned flag) +static bool daemon_test_flag(unsigned flag) { return me->flags & flag; } @@ -330,8 +334,9 @@ __printf_2_3 void para_log(int ll, const char* fmt,...) va_list argp; FILE *fp; struct tm *tm; - time_t t1; char *color; + bool log_time = daemon_test_flag(DF_LOG_TIME), log_timing = + daemon_test_flag(DF_LOG_TIMING); ll = PARA_MIN(ll, NUM_LOGLEVELS - 1); ll = PARA_MAX(ll, LL_DEBUG); @@ -342,12 +347,18 @@ __printf_2_3 void para_log(int ll, const char* fmt,...) color = daemon_test_flag(DF_COLOR_LOG)? me->log_colors[ll] : NULL; if (color) fprintf(fp, "%s", color); - if (daemon_test_flag(DF_LOG_TIME)) { /* print date and time */ - char str[100]; - time(&t1); - tm = localtime(&t1); - strftime(str, sizeof(str), "%b %d %H:%M:%S", tm); - fprintf(fp, "%s ", str); + if (log_time || log_timing) { + struct timeval tv; + gettimeofday(&tv, NULL); + if (daemon_test_flag(DF_LOG_TIME)) { /* print date and time */ + time_t t1 = tv.tv_sec; + char str[100]; + tm = localtime(&t1); + strftime(str, sizeof(str), "%b %d %H:%M:%S", tm); + fprintf(fp, "%s%s", str, log_timing? ":" : " "); + } + if (log_timing) /* print milliseconds */ + fprintf(fp, "%04lu ", (long unsigned)tv.tv_usec / 1000); } if (daemon_test_flag(DF_LOG_HOSTNAME)) { if (!me->hostname) diff --git a/daemon.h b/daemon.h index c8dd997f..3705b9da 100644 --- a/daemon.h +++ b/daemon.h @@ -29,4 +29,6 @@ enum daemon_flags { DF_LOG_LL = 8, /** Use colored output. */ DF_COLOR_LOG = 16, + /** Include milliseconds in log output. */ + DF_LOG_TIMING = 32 }; diff --git a/dccp_recv.c b/dccp_recv.c index 95444588..f71a7253 100644 --- a/dccp_recv.c +++ b/dccp_recv.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -11,6 +11,7 @@ * (C) 2005 Ian McDonald */ +#include #include #include @@ -23,12 +24,10 @@ #include "string.h" #include "net.h" #include "fd.h" +#include "buffer_tree.h" #include "dccp_recv.cmdline.h" -/** the size of the output buffer */ -#define DCCP_BUFSIZE 40960 - /** * data specific to the dccp receiver * @@ -37,6 +36,7 @@ struct private_dccp_recv_data { /** the file descriptor for the dccp socket */ int fd; + struct btr_pool *btrp; }; @@ -47,8 +47,7 @@ static void dccp_recv_close(struct receiver_node *rn) if (pdd && pdd->fd > 0) close(pdd->fd); - free(rn->buf); - rn->buf = NULL; + btr_pool_free(pdd->btrp); free(rn->private_data); rn->private_data = NULL; } @@ -58,12 +57,25 @@ static int dccp_recv_open(struct receiver_node *rn) { struct private_dccp_recv_data *pdd; struct dccp_recv_args_info *conf = rn->conf; - int fd, ret = makesock(AF_UNSPEC, IPPROTO_DCCP, 0, conf->host_arg, - conf->port_arg); + struct flowopts *fo = NULL; + uint8_t *ccids = NULL; + int fd, ret, i; - if (ret < 0) - return ret; - fd = ret; + /* Copy CCID preference list (u8 array required) */ + if (conf->ccid_given) { + ccids = para_malloc(conf->ccid_given); + fo = flowopt_new(); + + for (i = 0; i < conf->ccid_given; i++) + ccids[i] = conf->ccid_arg[i]; + + OPT_ADD(fo, SOL_DCCP, DCCP_SOCKOPT_CCID, ccids, i); + } + + fd = makesock(IPPROTO_DCCP, 0, conf->host_arg, conf->port_arg, fo); + free(ccids); + if (fd < 0) + return fd; /* * Disable unused CCIDs: the receiver does not send any application * data to the server. By shutting down this unused path we reduce @@ -77,8 +89,8 @@ static int dccp_recv_open(struct receiver_node *rn) ret = mark_fd_nonblocking(fd); if (ret < 0) goto err; - rn->buf = para_calloc(DCCP_BUFSIZE); rn->private_data = pdd = para_calloc(sizeof(struct private_dccp_recv_data)); + pdd->btrp = btr_pool_new("dccp_recv", 320 * 1024); pdd->fd = fd; return 1; err: @@ -86,16 +98,38 @@ err: return ret; } -static void dccp_shutdown(void) +/** + * Check whether the host supports the requested 'ccid' arguments. + * \param conf DCCP receiver arguments. + * \return True if all CCIDs requested in \a conf are supported. + */ +static bool dccp_recv_ccid_support_check(struct dccp_recv_args_info *conf) { - ; /* nothing to do */ + uint8_t *ccids; + int i, j, nccids; + + nccids = dccp_available_ccids(&ccids); + if (nccids <= 0) + return false; + + for (i = 0; i < conf->ccid_given; i++) { + for (j = 0; j < nccids && ccids[j] != conf->ccid_arg[i]; j++) + ; + if (j == nccids) { + PARA_ERROR_LOG("'CCID-%d' not supported on this host.\n", + conf->ccid_arg[i]); + return false; + } + } + return true; } static void *dccp_recv_parse_config(int argc, char **argv) { - struct dccp_recv_args_info *tmp = para_calloc(sizeof(struct dccp_recv_args_info)); + struct dccp_recv_args_info *tmp = para_calloc(sizeof(*tmp)); - if (!dccp_recv_cmdline_parser(argc, argv, tmp)) + if (!dccp_recv_cmdline_parser(argc, argv, tmp) && + dccp_recv_ccid_support_check(tmp)) return tmp; free(tmp); return NULL; @@ -107,6 +141,8 @@ static void dccp_recv_pre_select(struct sched *s, struct task *t) struct private_dccp_recv_data *pdd = rn->private_data; t->error = 0; + if (generic_recv_pre_select(s, t) <= 0) + return; para_fd_set(pdd->fd, &s->rfds, &s->max_fileno); } @@ -114,25 +150,45 @@ static void dccp_recv_post_select(struct sched *s, struct task *t) { struct receiver_node *rn = container_of(t, struct receiver_node, task); struct private_dccp_recv_data *pdd = rn->private_data; + struct btr_node *btrn = rn->btrn; + struct iovec iov[2]; + int ret, iovcnt; - if (rn->output_error && *rn->output_error < 0) { - t->error = *rn->output_error; + ret = btr_node_status(btrn, 0, BTR_NT_ROOT); + if (ret < 0) + goto err; + if (ret == 0) return; - } if (!FD_ISSET(pdd->fd, &s->rfds)) return; /* nothing to do */ - if (rn->loaded >= DCCP_BUFSIZE) { - t->error = -E_DCCP_OVERRUN; - return; - } - t->error = recv_bin_buffer(pdd->fd, rn->buf + rn->loaded, - DCCP_BUFSIZE - rn->loaded); - if (t->error > 0) { - rn->loaded += t->error; + iovcnt = btr_pool_get_buffers(pdd->btrp, iov); + ret = -E_DCCP_OVERRUN; + if (iovcnt == 0) + goto err; + ret = para_readv(pdd->fd, iov, iovcnt); + /* EAGAIN is possible even if FD_ISSET */ + if (ret < 0 && is_errno(-ret, EAGAIN)) return; + if (ret == 0) + ret = -E_RECV_EOF; + if (ret < 0) + goto err; + if (ret <= iov[0].iov_len) /* only the first buffer was filled */ + btr_add_output_pool(pdd->btrp, ret, btrn); + else { /* both buffers contain data */ + btr_add_output_pool(pdd->btrp, iov[0].iov_len, btrn); + btr_add_output_pool(pdd->btrp, ret - iov[0].iov_len, btrn); } - if (!t->error) - t->error = -E_RECV_EOF; + return; +err: + btr_remove_node(rn->btrn); + t->error = ret; +} + +static void dccp_recv_free_config(void *conf) +{ + dccp_recv_cmdline_parser_free(conf); + free(conf); } /** @@ -147,12 +203,12 @@ void dccp_recv_init(struct receiver *r) struct dccp_recv_args_info dummy; dccp_recv_cmdline_parser_init(&dummy); - r->shutdown = dccp_shutdown; r->open = dccp_recv_open; r->close = dccp_recv_close; r->pre_select = dccp_recv_pre_select; r->post_select = dccp_recv_post_select; r->parse_config = dccp_recv_parse_config; + r->free_config = dccp_recv_free_config; r->help = (struct ggo_help) { .short_help = dccp_recv_args_info_help, .detailed_help = dccp_recv_args_info_detailed_help diff --git a/dccp_send.c b/dccp_send.c index 9b72b7c9..fb2eafc9 100644 --- a/dccp_send.c +++ b/dccp_send.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -11,8 +11,10 @@ * (C) 2005 Ian McDonald */ +#include #include #include +#include #include "para.h" #include "error.h" @@ -42,15 +44,46 @@ static void dccp_pre_select(int *max_fileno, fd_set *rfds, para_fd_set(dss->listen_fd, rfds, max_fileno); } +/** + * Query the TX CCID used on the sender-client half connection. + * \param sockfd Server socket descriptor to query (after accept(2)). + * \return CCID number > 0, -1 on error/incompatibility. + * + * NB: This feature is only available on Linux > 2.6.30; on older kernels + * ENOPROTOOPT ("Protocol not available") will be returned. + */ +static int dccp_get_tx_ccid(int sockfd) +{ + int tx_ccid; + socklen_t len = sizeof(tx_ccid); + + if (getsockopt(sockfd, SOL_DCCP, + DCCP_SOCKOPT_TX_CCID, &tx_ccid, &len) < 0) { + PARA_WARNING_LOG("DCCP_SOCKOPT_TX_CCID: %s\n", strerror(errno)); + return -1; + } + return tx_ccid; +} + static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds) { struct sender_client *sc; + int tx_ccid; if (dss->listen_fd < 0 || !FD_ISSET(dss->listen_fd, rfds)) return; sc = accept_sender_client(dss); if (!sc) return; + + /* If CCID identifiable, present client as #~ */ + tx_ccid = dccp_get_tx_ccid(sc->fd); + if (tx_ccid != -1) { + char *tmp = make_message("%s~%d", sc->name, tx_ccid); + + free(sc->name); + sc->name = tmp; + } /* * Bypass unused CCID paths: the sender does not receive application data * from the client; by shutting down this unused communication path we can @@ -102,9 +135,34 @@ static int dccp_com_allow(struct sender_command_data *scd) return 1; } +/** + * Return list of available CCIDs or warning, in static buffer. + */ +static const char *dccp_list_available_ccids(void) +{ + /* Worst case length: n * 3 digits + n-1 spaces + '\0' */ + static char list[DCCP_MAX_HOST_CCIDS * 4]; + uint8_t *ccids; + int i, len, nccids; + + nccids = dccp_available_ccids(&ccids); + if (nccids < 0) { + snprintf(list, sizeof(list), "Unable to query available CCIDs"); + } else { + for (i = len = 0; i < nccids; i++) + len += snprintf(list + len, sizeof(list) - len, + "%s%d", i ? " " : "", ccids[i]); + } + return list; +} + static char *dccp_info(void) { - return get_sender_info(dss, "dccp"); + char *info = get_sender_info(dss, "dccp"); + char *ret = make_message("%s" "\tsupported ccids: %s\n", + info, dccp_list_available_ccids()); + free(info); + return ret; } /** diff --git a/depend.sh b/depend.sh new file mode 100755 index 00000000..aad45a3c --- /dev/null +++ b/depend.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# Call gcc to output a rule suitable for make describing the dependencies of +# the given input file and parse the output to add a *.d target with the same +# dependencies. + +# The first two arguments to that script are special: $1 is the object +# directory. This string is prefixed to both the .o and the .d target. $2 is +# the directory that contains the *.cmdline.h files generated by gengetopt. + +# As gcc outputs the dependencies on the *.cmdline.h files either as either +# foo.cmdline.h or as $cmdline_dir/foo,cmdline.h, depending on whether the +# latter file exists, we prefix the former with $2/ + +object_dir="$1" +cmdline_dir="$2" +shift +shift + +LC_ALL=C gcc -MM -MG "$@" \ + | sed -e "s@^\(.*\)\.o:@$object_dir/\1.d $object_dir/\1.o:@" \ + -e "s@[ ^]\([a-zA-Z0-9_]\+\.cmdline.h\)@ $cmdline_dir/\1@g" diff --git a/error.h b/error.h index 4ec9da85..f6dec480 100644 --- a/error.h +++ b/error.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -23,32 +23,75 @@ DEFINE_ERRLIST_OBJECT_ENUM; #define SHA1_ERRORS #define RBTREE_ERRORS #define RECV_ERRORS -#define STDOUT_ERRORS #define IPC_ERRORS #define DCCP_SEND_ERRORS #define HTTP_SEND_ERRORS #define GGO_ERRORS #define COLOR_ERRORS #define SIGNAL_ERRORS +#define FADE_ERRORS +#define SERVER_COMMAND_LIST_ERRORS +#define AFS_COMMAND_LIST_ERRORS +#define AUDIOD_COMMAND_LIST_ERRORS +#define STDOUT_ERRORS +#define FILE_WRITE_ERRORS extern const char **para_errlist[]; +#define BUFFER_TREE_ERRORS \ + PARA_ERROR(BTR_EOF, "buffer tree: end of file"), \ + PARA_ERROR(BTR_NO_CHILD, "btr node has no children"), \ + PARA_ERROR(BTR_NAVAIL, "btr node: value currently unavailable"), \ + + + +#define BITSTREAM_ERRORS \ + PARA_ERROR(VLC, "invalid vlc code"), \ + + +#define WMA_AFH_ERRORS \ + PARA_ERROR(NO_WMA, "asf/wma format not recognized"), \ + + +#define WMA_COMMON_ERRORS \ + PARA_ERROR(WMA_NO_GUID, "audio stream guid not found"), \ + + +#define WMADEC_FILTER_ERRORS \ + PARA_ERROR(WMA_BAD_PARAMS, "invalid WMA parameters"), \ + PARA_ERROR(WMA_OUTPUT_SPACE, "insufficient output space"), \ + PARA_ERROR(WMA_BAD_SUPERFRAME, "invalid superframe"), \ + PARA_ERROR(WMA_BLOCK_SIZE, "invalid block size"), \ + PARA_ERROR(INCOHERENT_BLOCK_LEN, "incoherent block length"), \ + PARA_ERROR(WMADEC_EOF, "wmadec: end of file"), \ + + +#define IMDCT_ERRORS \ + PARA_ERROR(FFT_BAD_PARAMS, "invalid params for fft"), \ + + #define PREBUFFER_FILTER_ERRORS \ PARA_ERROR(PREBUFFER_SYNTAX, "syntax error in prebuffer filter config"), \ + PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \ + #define OSS_WRITE_ERRORS \ PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \ PARA_ERROR(BAD_CHANNEL_COUNT, "channel count not supported"), \ PARA_ERROR(BAD_SAMPLERATE, "sample rate not supported"), \ + PARA_ERROR(OSS_EOF, "oss: end of file"), \ #define COMPRESS_FILTER_ERRORS \ PARA_ERROR(COMPRESS_SYNTAX, "syntax error in compress filter config"), \ + PARA_ERROR(COMPRESS_EOF, "compress: end of file"), \ #define WAV_FILTER_ERRORS \ PARA_ERROR(WAV_BAD_FC, "invalid filter chain configuration"), \ + PARA_ERROR(WAV_EOF, "wav filter: end of file"), \ + PARA_ERROR(WAV_SUCCESS, "successfully wrote wav header"), \ #define FEC_ERRORS \ @@ -68,16 +111,14 @@ extern const char **para_errlist[]; #define AMP_FILTER_ERRORS \ PARA_ERROR(AMP_SYNTAX, "syntax error in amp filter config"), \ + PARA_ERROR(AMP_ZERO_AMP, "no amplification necessary"), \ + PARA_ERROR(AMP_EOF, "amp: end of file"), \ #define SEND_COMMON_ERRORS \ PARA_ERROR(MAX_CLIENTS, "maximal number of clients exceeded"), \ -#define FADE_ERRORS \ - PARA_ERROR(FADE_SYNTAX, "fade syntax error"), \ - - #define CLIENT_ERRORS \ PARA_ERROR(TASK_STARTED, "task started"), \ @@ -95,66 +136,22 @@ extern const char **para_errlist[]; PARA_ERROR(ACL_PERM, "access denied by acl"), \ -#define FSCK_ERRORS \ - PARA_ERROR(FSCK_SYNTAX, "fsck syntax error"), \ - PARA_ERROR(RANGE_VIOLATION, "range violation detected, very bad"), \ - PARA_ERROR(NOT_A_REGULAR_FILE, "not a regular file"), \ - - -#define OSL_ERRORS \ - PARA_ERROR(BAD_DB_DIR, "invalid database directory"), \ - PARA_ERROR(NO_COLUMN_DESC, "missing column description"), \ - PARA_ERROR(BAD_NAME, "invalid name for a column/table"), \ - PARA_ERROR(BAD_STORAGE_TYPE, "invalid storage type"), \ - PARA_ERROR(BAD_STORAGE_FLAGS, "invalid storage flags"), \ - PARA_ERROR(NO_COLUMN_NAME, "missing column name"), \ - PARA_ERROR(NO_COLUMNS, "at least one column required"), \ - PARA_ERROR(BAD_COLUMN_NAME, "invalid name for a table column"), \ - PARA_ERROR(NO_UNIQUE_RBTREE_COLUMN, "need at least one mapped column with OSL_UNIQE and OSL_RBTREE OSL"), \ - PARA_ERROR(NO_RBTREE_COL, "at least one column needs an rbtree"), \ - PARA_ERROR(DUPLICATE_COL_NAME, "column name given twice"), \ - PARA_ERROR(BAD_STORAGE_SIZE, "invalid storage size"), \ - PARA_ERROR(NO_COMPARE_FUNC, "missing compare function"), \ - PARA_ERROR(BAD_DATA_SIZE, "wrong data size for fixed-size column"), \ - PARA_ERROR(NOT_MAPPED, "file not mapped"), \ - PARA_ERROR(ALREADY_MAPPED, "file already mapped"), \ - PARA_ERROR(BAD_SIZE, "invalid size specified"), \ - PARA_ERROR(TRUNC, "failed to truncate file"), \ - PARA_ERROR(BAD_TABLE, "table not open"), \ - PARA_ERROR(BAD_TABLE_DESC, "invalid table description"), \ - PARA_ERROR(RB_KEY_EXISTS, "key already exists in rbtree"), \ - PARA_ERROR(RB_KEY_NOT_FOUND, "key not found in rbtree"), \ - PARA_ERROR(BAD_ROW_NUM, "invalid row number"), \ - PARA_ERROR(INDEX_CORRUPTION, "index corruption detected"), \ - PARA_ERROR(INVALID_OBJECT, "reference to invalid object"), \ - PARA_ERROR(STAT, "can not stat file"), \ - PARA_ERROR(WRITE, "write error"), \ - PARA_ERROR(LSEEK, "lseek error"), \ - PARA_ERROR(BUSY, "table is busy"), \ - PARA_ERROR(SHORT_TABLE, "table too short"), \ - PARA_ERROR(NO_MAGIC, "missing table header magic"), \ - PARA_ERROR(VERSION_MISMATCH, "table version not supported"), \ - PARA_ERROR(BAD_COLUMN_NUM, "invalid column number"), \ - PARA_ERROR(BAD_TABLE_FLAGS, "invalid flags in table description"), \ - PARA_ERROR(BAD_ROW, "invalid row"), \ - - - #define AFS_ERRORS \ PARA_ERROR(BAD_TABLE_NAME, "invalid table"), \ - PARA_ERROR(INPUT_TOO_LARGE, "input too large for stdin command"), \ PARA_ERROR(AFS_SYNTAX, "afs syntax error"), \ PARA_ERROR(AFS_SIGNAL, "afs caught deadly signal"), \ PARA_ERROR(AFS_SOCKET, "afs socket not writable"), \ - PARA_ERROR(AFS_SHORT_READ, "short read from afs socket"), \ #define MOOD_ERRORS \ - PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \ PARA_ERROR(NO_MOOD, "no mood available"), \ PARA_ERROR(DUMMY_ROW, "attempted to access blob dummy object"), \ +#define MM_ERRORS \ + PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \ + + #define ATTRIBUTE_ERRORS \ PARA_ERROR(ATTR_SYNTAX, "attribute syntax error"), \ PARA_ERROR(NO_ATTRIBUTES, "no attributes defined yet"), \ @@ -163,6 +160,7 @@ extern const char **para_errlist[]; #define BLOB_ERRORS \ PARA_ERROR(BLOB_SYNTAX, "blob syntax error"), \ + PARA_ERROR(INPUT_TOO_LARGE, "input too large for stdin command"), \ #define PLAYLIST_ERRORS \ @@ -178,7 +176,6 @@ extern const char **para_errlist[]; PARA_ERROR(BAD_PATH, "invalid path"), \ PARA_ERROR(BAD_SORT, "invalid sorting method"), \ PARA_ERROR(FNMATCH, "fnmatch error"), \ - PARA_ERROR(NO_MATCH, "no matches"), \ PARA_ERROR(NO_AFHI, "audio format handler info required"), \ PARA_ERROR(AFT_SYNTAX, "audio file table syntax error"), \ PARA_ERROR(HASH_MISMATCH, "hash mismatch, consider re-add"), \ @@ -206,7 +203,6 @@ extern const char **para_errlist[]; #define CLIENT_COMMON_ERRORS \ PARA_ERROR(CLIENT_SYNTAX, "syntax error"), \ - PARA_ERROR(INVALID_CHALLENGE, "did not receive valid challenge"), \ PARA_ERROR(NO_CONFIG, "config file not found"), \ PARA_ERROR(BAD_CONFIG, "syntax error in config file"), \ PARA_ERROR(CLIENT_AUTH, "authentication failed"), \ @@ -237,9 +233,7 @@ extern const char **para_errlist[]; #define UDP_RECV_ERRORS \ PARA_ERROR(UDP_SYNTAX, "udp_recv syntax error"), \ - PARA_ERROR(UDP_BAD_HEADER, "invalid udp audio header"), \ PARA_ERROR(UDP_OVERRUN, "output buffer overrun"), \ - PARA_ERROR(UDP_BAD_STREAM_TYPE, "invalid stream type"), \ #define HTTP_RECV_ERRORS \ @@ -265,17 +259,17 @@ extern const char **para_errlist[]; PARA_ERROR(AUDIOD_SYNTAX, "syntax error"), \ PARA_ERROR(UCRED_PERM, "permission denied"), \ PARA_ERROR(INVALID_AUDIOD_CMD, "invalid command"), \ + PARA_ERROR(TOO_MANY_CLIENTS, "maximal number of stat clients exceeded"), \ + PARA_ERROR(UNKNOWN_STAT_ITEM, "status item not recognized"), \ #define FILTER_COMMON_ERRORS \ - PARA_ERROR(UNSUPPORTED_FILTER, "given filter not supported"), \ PARA_ERROR(BAD_FILTER_OPTIONS, "invalid filter option given"), \ - PARA_ERROR(FC_EOF, "filter chain: eof"), \ + PARA_ERROR(UNSUPPORTED_FILTER, "given filter not supported"), \ #define STAT_ERRORS \ - PARA_ERROR(TOO_MANY_CLIENTS, "maximal number of stat clients exceeded"), \ - PARA_ERROR(UNKNOWN_STAT_ITEM, "status item not recognized"), \ + PARA_ERROR(STAT_ITEM_PARSE, "failed to parse status item"), \ #define OGGDEC_FILTER_ERRORS \ @@ -286,22 +280,18 @@ extern const char **para_errlist[]; PARA_ERROR(OGGDEC_FAULT, "bug or heap/stack corruption"), \ PARA_ERROR(OGGDEC_BADLINK, "invalid stream section or requested link corrupt"), \ PARA_ERROR(OGGDEC_SYNTAX, "syntax error in oggdec config"), \ + PARA_ERROR(OGGDEC_EOF, "oggdec: end of file"), \ #define GRAB_CLIENT_ERRORS \ - PARA_ERROR(PEDANTIC_GRAB, "fd not ready and pedantic grab requested"), \ PARA_ERROR(GC_WRITE, "grab client write error"), \ - PARA_ERROR(BAD_GC_SLOT, "invalid slot requested by grab client"), \ - PARA_ERROR(BAD_GC_FILTER_NUM, "invalid filter number given"), \ PARA_ERROR(GC_SYNTAX, "grab client syntax error"), \ - PARA_ERROR(GC_HELP_GIVEN, ""), /* not really an error */ \ - PARA_ERROR(GC_VERSION_GIVEN, ""), /* not really an error */ \ #define MP3DEC_FILTER_ERRORS \ PARA_ERROR(MAD_FRAME_DECODE, "mad frame decode error"), \ - PARA_ERROR(MP3DEC_OVERRUN, "mp3 output buffer overrun"), \ PARA_ERROR(MP3DEC_SYNTAX, "syntax error in mp3dec config"), \ + PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \ #define FILTER_ERRORS \ @@ -314,6 +304,8 @@ extern const char **para_errlist[]; PARA_ERROR(STRTOLL, "unknown strtoll error"), \ PARA_ERROR(ATOI_NO_DIGITS, "no digits found in string"), \ PARA_ERROR(ATOI_JUNK_AT_END, "further characters after number"), \ + PARA_ERROR(SIZE_PREFIX, "bad size prefix"), \ + PARA_ERROR(REGEX, "regular expression error") \ #define EXEC_ERRORS \ @@ -350,7 +342,8 @@ extern const char **para_errlist[]; #define VSS_ERRORS \ PARA_ERROR(NOFD, "did not receive open fd from afs"), \ - PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration") + PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration"), \ + PARA_ERROR(AFS_SHORT_READ, "short read from afs socket"), \ #define CRYPT_ERRORS \ @@ -359,19 +352,21 @@ extern const char **para_errlist[]; PARA_ERROR(RSA, "RSA error"), \ PARA_ERROR(ENCRYPT, "encrypt error"), \ PARA_ERROR(DECRYPT, "decrypt error"), \ - PARA_ERROR(CHALLENGE, "failed to read challenge"), \ + PARA_ERROR(BLINDING, "failed to activate key blinding"), \ + PARA_ERROR(KEY_PERM, "unprotected private key"), \ #define COMMAND_ERRORS \ PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \ - PARA_ERROR(AUTH, "did not receive auth request"), \ + PARA_ERROR(AUTH_REQUEST, "did not receive auth request"), \ PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \ PARA_ERROR(BAD_CMD, "invalid command"), \ PARA_ERROR(PERM, "permission denied"), \ PARA_ERROR(LOCK, "lock error"), \ PARA_ERROR(SENDER_CMD, "command not supported by this sender"), \ PARA_ERROR(SERVER_CRASH, "para_server crashed -- can not live without it"), \ - PARA_ERROR(BAD_USER, "you don't exist. Go away."), \ + PARA_ERROR(BAD_USER, "auth request for invalid user"), \ + PARA_ERROR(BAD_AUTH, "authentication failure"), \ #define DCCP_RECV_ERRORS \ @@ -385,9 +380,6 @@ extern const char **para_errlist[]; #define WRITE_ERRORS \ PARA_ERROR(WRITE_SYNTAX, "para_write syntax error"), \ PARA_ERROR(NO_WAV_HEADER, "wave header not found"), \ - PARA_ERROR(WAV_HEADER_SUCCESS, "successfully read wave header"), \ - PARA_ERROR(NO_DELAY, "no initial delay"), \ - PARA_ERROR(DELAY_TIMEOUT, "initial delay timeout"), \ #define ALSA_WRITE_ERRORS \ @@ -405,13 +397,9 @@ extern const char **para_errlist[]; PARA_ERROR(SET_RATE, "snd_pcm_hw_params_set_rate_near failed"), \ PARA_ERROR(START_THRESHOLD, "snd_pcm_sw_params_set_start_threshold() failed"), \ PARA_ERROR(STOP_THRESHOLD, "snd_pcm_sw_params_set_stop_threshold() failed"), \ + PARA_ERROR(ALSA_EOF, "alsa: end of file"), \ -#define FILE_WRITE_ERRORS \ - PARA_ERROR(FW_WRITE, "file writer write error"), \ - PARA_ERROR(FW_OPEN, "file writer: can not open output file"), \ - PARA_ERROR(FW_NO_FILE, "task started without open file"), \ - #define WRITE_COMMON_ERRORS \ PARA_ERROR(WRITE_COMMON_SYNTAX, "syntax error in write option"), \ @@ -420,7 +408,6 @@ extern const char **para_errlist[]; #define AACDEC_FILTER_ERRORS \ PARA_ERROR(AACDEC_INIT, "failed to init aac decoder"), \ PARA_ERROR(AAC_DECODE, "aac decode error"), \ - PARA_ERROR(AAC_OVERRUN, "aac output buffer overrun"), \ #define CHUNK_QUEUE_ERRORS \ @@ -475,12 +462,23 @@ extern const char **para_errlist[]; */ #define SYSTEM_ERROR_BIT 30 +/** + * Like the SYSTEM_ERROR_BIT, but indicates an error from the osl library. + */ +#define OSL_ERROR_BIT 29 + /** Check whether the system error bit is set. */ #define IS_SYSTEM_ERROR(num) (!!((num) & (1 << SYSTEM_ERROR_BIT))) +/** Check whether the osl error bit is set. */ +#define IS_OSL_ERROR(num) (!!((num) & (1 << OSL_ERROR_BIT))) + /** Set the system error bit for the given number. */ #define ERRNO_TO_PARA_ERROR(num) ((num) | (1 << SYSTEM_ERROR_BIT)) +/** Set the osl error bit for the given number. */ +#define OSL_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << OSL_ERROR_BIT)) + /** Check whether a given number is a system error number. * * \param num The value to be checked. @@ -505,11 +503,33 @@ _static_inline_ int is_errno(int num, int _errno) _static_inline_ const char *para_strerror(int num) { assert(num > 0); +#ifdef _OSL_H + if (IS_OSL_ERROR(num)) + return osl_strerror(num & ((1 << OSL_ERROR_BIT) - 1)); +#endif if (IS_SYSTEM_ERROR(num)) - return strerror((num) & ((1 << SYSTEM_ERROR_BIT) - 1)); - else - return para_errlist[ERRNUM_TO_SS(num)][ERRNUM_TO_INDEX(num)]; + return strerror(num & ((1 << SYSTEM_ERROR_BIT) - 1)); + return para_errlist[ERRNUM_TO_SS(num)][ERRNUM_TO_INDEX(num)]; +} + +/** + * Wrapper for osl library calls. + * + * \param ret The return value of an osl library function. + * + * This should be used for all calls to osl functions that return an osl error + * code. It changes the return value appropriately so that it can be used for + * printing the correct error message vi para_strerror(). + * + * \return \a ret if \a ret >= 0, a paraslash error code otherwise. + */ +_static_inline_ int osl(int ret) +{ + if (ret >= 0) + return ret; + return -OSL_ERRNO_TO_PARA_ERROR(-ret); } + /** * Define the error list for one subsystem. # diff --git a/exec.c b/exec.c index 86a483cf..db05c022 100644 --- a/exec.c +++ b/exec.c @@ -1,16 +1,19 @@ /* - * Copyright (C) 2003-2009 Andre Noll + * Copyright (C) 2003-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file exec.c Helper functions for spawning new processes. */ + +#include #include + #include "para.h" #include "close_on_fork.h" #include "error.h" -#include "string.h" #include "fd.h" +#include "string.h" /** * Spawn a new process and redirect fd 0, 1, and 2. @@ -137,11 +140,11 @@ int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds) { int ret; char **argv; - char *tmp = para_strdup(cmdline); - split_args(tmp, &argv, " \t"); + ret = create_argv(cmdline, " \t", &argv); + if (ret < 0) + return ret; ret = para_exec(pid, argv[0], argv, fds); - free(argv); - free(tmp); + free_argv(argv); return ret; } diff --git a/fade.c b/fade.c index 78903e5f..38fcdfe7 100644 --- a/fade.c +++ b/fade.c @@ -1,18 +1,14 @@ /* - * Copyright (C) 1998-2009 Andre Noll + * Copyright (C) 1998-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ -/** \file fade.c A volume fader and alarm clock for linux. */ +/** \file fade.c A volume fader and alarm clock for OSS. */ +#include #include #include - -#include "fade.cmdline.h" -#include "para.h" -#include "fd.h" - #include #include #include /* EXIT_SUCCESS */ @@ -21,12 +17,15 @@ #include #include #include + +#include "fade.cmdline.h" +#include "para.h" +#include "fd.h" #include "string.h" #include "error.h" - INIT_FADE_ERRLISTS; -struct fade_args_info conf; +static struct fade_args_info conf; __printf_2_3 void para_log(__a_unused int ll, const char *fmt, ...) { @@ -43,7 +42,7 @@ __printf_2_3 void para_log(__a_unused int ll, const char *fmt, ...) } /* - * open mixer device + * Open the mixer device. */ static int open_mixer(void) { @@ -131,7 +130,7 @@ static void fixup_mixer_channel_arg(void) /* * Open mixer, get volume, fade to new_vol in secs seconds and - * close mixer + * close mixer. */ static int fade(int new_vol, int fade_time) { @@ -217,14 +216,14 @@ static int sweet_dreams(void) unsigned int delay; struct tm *tm; int ret, min = conf.wake_min_arg; - char *fa_mode = conf.fa_mode_arg; - char *wake_mode = conf.wake_mode_arg; - char *sleep_mode = conf.sleep_mode_arg; - int wf = conf.wake_fade_arg; - int sf = conf.fa_fade_arg; - int wv = conf.wake_vol_arg; - int sv = conf.fa_vol_arg; - int iv = conf.sleep_ivol_arg; + char *fo_mood = conf.fo_mood_arg; + char *fi_mood = conf.fi_mood_arg; + char *sleep_mood = conf.sleep_mood_arg; + int fit = conf.fi_time_arg; + int fot = conf.fo_time_arg; + int fiv = conf.fi_vol_arg; + int fov = conf.fo_vol_arg; + int iv = conf.ivol_arg; /* calculate wake time */ time(&t1); @@ -247,38 +246,38 @@ static int sweet_dreams(void) PARA_INFO_LOG("waketime: %s", asctime(tm)); client_cmd("stop"); sleep(1); - if (sf) { + if (fot) { PARA_INFO_LOG("initial volume: %d\n", iv); ret = open_and_set_mixer_channel(iv); if (ret < 0) return ret; - change_afs_mode_and_play(fa_mode); - ret = fade(sv, sf); + change_afs_mode_and_play(fo_mood); + ret = fade(fov, fot); if (ret < 0) return ret; } else { - ret = open_and_set_mixer_channel(sf); + ret = open_and_set_mixer_channel(fov); if (ret < 0) return ret; } - if (conf.sleep_mode_given) - change_afs_mode_and_play(sleep_mode); + if (conf.sleep_mood_given) + change_afs_mode_and_play(sleep_mood); else client_cmd("stop"); - if (!wf) + if (!fit) return 1; for (;;) { time(&t1); - if (wake_time_epoch <= t1 + wf) + if (wake_time_epoch <= t1 + fit) break; - delay = wake_time_epoch - t1 - wf; + delay = wake_time_epoch - t1 - fit; PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n", delay, delay / 3600, (delay % 3600) / 60); sleep(delay); } - change_afs_mode_and_play(wake_mode); - ret = fade(wv, wf); + change_afs_mode_and_play(fi_mood); + ret = fade(fiv, fit); PARA_INFO_LOG("fade complete, returning\n"); return ret; } @@ -288,20 +287,20 @@ static int snooze(void) int ret; unsigned sleep_time; - if (conf.snooze_time_arg <= 0) + if (conf.so_time_arg <= 0) return 1; - sleep_time = conf.snooze_time_arg; - if (open_and_get_mixer_channel() < conf.snooze_out_vol_arg) - ret = open_and_set_mixer_channel(conf.snooze_out_vol_arg); + sleep_time = conf.so_time_arg; + if (open_and_get_mixer_channel() < conf.so_vol_arg) + ret = open_and_set_mixer_channel(conf.so_vol_arg); else - ret = fade(conf.snooze_out_vol_arg, conf.snooze_out_fade_arg); + ret = fade(conf.so_vol_arg, conf.so_time_arg); if (ret < 0) return ret; client_cmd("pause"); PARA_NOTICE_LOG("%d seconds snooze time...\n", conf.snooze_time_arg); sleep(sleep_time); client_cmd("play"); - return fade(conf.snooze_in_vol_arg, conf.snooze_in_fade_arg); + return fade(conf.si_vol_arg, conf.si_time_arg); } static int configfile_exists(void) @@ -336,26 +335,24 @@ int main(int argc, char *argv[]) .override = 0, .initialize = 0, .check_required = 0, - .check_ambiguity = 0 + .check_ambiguity = 0, + .print_errors = 1 }; fade_cmdline_parser_config_file(conf.config_file_arg, &conf, ¶ms); } fixup_mixer_channel_arg(); - if (!strcmp(conf.mode_arg, "sleep")) { + switch (conf.mode_arg) { + case mode_arg_sleep: ret = sweet_dreams(); - goto out; - } - if (!strcmp(conf.mode_arg, "fade")) { + break; + case mode_arg_fade: ret = fade(conf.fade_vol_arg, conf.fade_time_arg); - goto out; - } - if (!strcmp(conf.mode_arg, "snooze")) { + break; + case mode_arg_snooze: ret = snooze(); - goto out; + break; } - ret = -E_FADE_SYNTAX; -out: if (ret < 0) PARA_EMERG_LOG("%s\n", para_strerror(-ret)); return ret < 0? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/fd.c b/fd.c index 31eb6388..46be2289 100644 --- a/fd.c +++ b/fd.c @@ -1,19 +1,22 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file fd.c Helper functions for file descriptor handling. */ +#include #include #include #include #include #include +#include #include "para.h" #include "error.h" +#include "string.h" /** * Write a buffer to a file descriptor, re-write on short writes. @@ -79,6 +82,27 @@ int write_nonblock(int fd, const char *buf, size_t len, return written; } +/** + * Simple wrapper for readv(). + * + * \param fd The file descriptor to read from. + * \param iov Scatter/gather array used in readv(). + * \param iovcnt Number of elements in \a iov. + * + * \return A negative error code on errors, the return value of the underlying + * call to readv() otherwise. + * + * \sa readv(2). + */ +int para_readv(int fd, struct iovec *iov, int iovcnt) +{ + int ret = readv(fd, iov, iovcnt); + + if (ret < 0) + return -ERRNO_TO_PARA_ERROR(errno); + return ret; +} + /** * Check whether a file exists. * @@ -114,11 +138,10 @@ int file_exists(const char *fn) int para_select(int n, fd_set *readfds, fd_set *writefds, struct timeval *timeout_tv) { - int ret, err; - do { + int ret; + do ret = select(n, readfds, writefds, NULL, timeout_tv); - err = errno; - } while (ret < 0 && err == EINTR); + while (ret < 0 && errno == EINTR); if (ret < 0) return -ERRNO_TO_PARA_ERROR(errno); return ret; @@ -446,18 +469,14 @@ int para_munmap(void *start, size_t length) int write_ok(int fd) { - struct timeval tv = {0, 0}; + struct timeval tv; fd_set wfds; - int ret; -again: + FD_ZERO(&wfds); FD_SET(fd, &wfds); tv.tv_sec = 0; tv.tv_usec = 0; - ret = select(fd + 1, NULL, &wfds, NULL, &tv); - if (ret < 0 && errno == EINTR) - goto again; - return ret; + return para_select(fd + 1, NULL, &wfds, &tv); } /** @@ -480,3 +499,65 @@ void valid_fd_012(void) } } } + +/** + * Traverse the given directory recursively. + * + * \param dirname The directory to traverse. + * \param func The function to call for each entry. + * \param private_data Pointer to an arbitrary data structure. + * + * For each regular file under \a dirname, the supplied function \a func is + * called. The full path of the regular file and the \a private_data pointer + * are passed to \a func. Directories for which the calling process has no + * permissions to change to are silently ignored. + * + * \return Standard. + */ +int for_each_file_in_dir(const char *dirname, + int (*func)(const char *, void *), void *private_data) +{ + DIR *dir; + struct dirent *entry; + int cwd_fd, ret2, ret = para_opendir(dirname, &dir, &cwd_fd); + + if (ret < 0) + return ret == -ERRNO_TO_PARA_ERROR(EACCES)? 1 : ret; + /* scan cwd recursively */ + while ((entry = readdir(dir))) { + mode_t m; + char *tmp; + struct stat s; + + if (!strcmp(entry->d_name, ".")) + continue; + if (!strcmp(entry->d_name, "..")) + continue; + if (lstat(entry->d_name, &s) == -1) + continue; + m = s.st_mode; + if (!S_ISREG(m) && !S_ISDIR(m)) + continue; + tmp = make_message("%s/%s", dirname, entry->d_name); + if (!S_ISDIR(m)) { + ret = func(tmp, private_data); + free(tmp); + if (ret < 0) + goto out; + continue; + } + /* directory */ + ret = for_each_file_in_dir(tmp, func, private_data); + free(tmp); + if (ret < 0) + goto out; + } + ret = 1; +out: + closedir(dir); + ret2 = para_fchdir(cwd_fd); + if (ret2 < 0 && ret >= 0) + ret = ret2; + close(cwd_fd); + return ret; +} diff --git a/fd.h b/fd.h index 7c364120..68092308 100644 --- a/fd.h +++ b/fd.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -7,6 +7,7 @@ /** \file fd.h exported symbols from fd.c */ int write_all(int fd, const char *buf, size_t *len); +int para_readv(int fd, struct iovec *iov, int iovcnt); int file_exists(const char *); int para_select(int n, fd_set *readfds, fd_set *writefds, struct timeval *timeout_tv); @@ -28,3 +29,5 @@ int write_ok(int fd); void valid_fd_012(void); int write_nonblock(int fd, const char *buf, size_t len, size_t max_bytes_per_write); +int for_each_file_in_dir(const char *dirname, + int (*func)(const char *, void *), void *private_data); diff --git a/fec.c b/fec.c index e4cdbaf3..dc6e7520 100644 --- a/fec.c +++ b/fec.c @@ -33,6 +33,7 @@ * OF SUCH DAMAGE. */ +#include #include "para.h" #include "error.h" @@ -40,20 +41,36 @@ #include "string.h" #include "fec.h" -#define GF_BITS 8 /* code over GF(256) */ +/** Code over GF(256). */ +#define GF_BITS 8 +/** The largest number in GF(256) */ #define GF_SIZE ((1 << GF_BITS) - 1) /* * To speed up computations, we have tables for logarithm, exponent and inverse - * of a number. We use a table for multiplication as well (it takes 64K, no big - * deal even on a PDA, especially because it can be pre-initialized an put into - * a ROM!). The macro gf_mul(x,y) takes care of multiplications. + * of a number. + */ + +/** Index->poly form conversion table. */ +static unsigned char gf_exp[2 * GF_SIZE]; + +/** Poly->index form conversion table. */ +static int gf_log[GF_SIZE + 1]; + +/** Inverse of a field element. */ +static unsigned char inverse[GF_SIZE + 1]; + +/** + * The multiplication table. + * + * We use a table for multiplication as well. It takes 64K, no big deal even on + * a PDA, especially because it can be pre-initialized and put into a ROM. + * + * \sa \ref gf_mul. */ -static unsigned char gf_exp[2 * GF_SIZE]; /* index->poly form conversion table */ -static int gf_log[GF_SIZE + 1]; /* Poly->index form conversion table */ -static unsigned char inverse[GF_SIZE + 1]; /* inverse of field elem. */ static unsigned char gf_mul_table[GF_SIZE + 1][GF_SIZE + 1]; -/* Multiply two numbers. */ + +/** Multiply two GF numbers. */ #define gf_mul(x,y) gf_mul_table[x][y] /* Compute x % GF_SIZE without a slow divide. */ @@ -153,13 +170,15 @@ static void generate_gf(void) inverse[i] = gf_exp[GF_SIZE - gf_log[i]]; } +/** How often the loop is unrolled. */ +#define UNROLL 16 + /* * Compute dst[] = dst[] + c * src[] * * This is used often, so better optimize it! Currently the loop is unrolled 16 * times. The case c=0 is also optimized, whereas c=1 is not. */ -#define UNROLL 16 static void addmul(unsigned char *dst1, const unsigned char const *src1, unsigned char c, int sz) { @@ -210,6 +229,7 @@ static void matmul(unsigned char *a, unsigned char *b, unsigned char *c, } } +/** Swap two numbers. */ #define FEC_SWAP(a,b) {typeof(a) tmp = a; a = b; b = tmp;} /* diff --git a/fec.h b/fec.h index 1e28e8a4..d09b035d 100644 --- a/fec.h +++ b/fec.h @@ -33,8 +33,10 @@ * OF SUCH DAMAGE. */ -#define FEC_MAGIC 0xFECC0DEC +/** Each FEC slice contains a FEC header of this size. */ #define FEC_HEADER_SIZE 32 +/** The FEC header starts with this magic value. */ +#define FEC_MAGIC 0xFECC0DEC struct fec_parms; diff --git a/fecdec_filter.c b/fecdec_filter.c index 8cb17ad6..10f1c642 100644 --- a/fecdec_filter.c +++ b/fecdec_filter.c @@ -1,17 +1,21 @@ /* - * Copyright (C) 2009 Andre Noll + * Copyright (C) 2009-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file fecdec_filter.c A filter that fec-decodes an audio stream. */ +#include +#include + #include #include "para.h" #include "error.h" #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "string.h" #include "portable_io.h" @@ -27,18 +31,13 @@ */ #define NUM_FEC_GROUPS 3 -/** Default size of the output buffer of the fecdec filter. */ -#define FECDEC_DEFAULT_OUTBUF_SIZE (3 * 1024) -/** Maximal size of the output buffer of the fecdec filter. */ -#define FECDEC_MAX_OUTBUF_SIZE (1024 * 1024) - /** Data read from the header of a slice. */ struct fec_header { /** Total number of slices in this group. */ uint8_t slices_per_group; /** Number of slices needed to start decoding. */ uint8_t data_slices_per_group; - /** Size of the ogg vorbis header (zero for mp3, aac). */ + /** Size of the ogg vorbis/wma header (zero for mp3, aac). */ uint32_t audio_header_size; /** Number of the FEC group this slice belongs to. */ uint32_t group_num; @@ -82,6 +81,7 @@ struct private_fecdec_data { int have_header; /** Points to the first received group. */ struct fecdec_group *first_complete_group; + struct btr_pool *btrp; }; /** Iterate over all fecdec groups. */ @@ -243,10 +243,21 @@ static int add_slice(char *buf, struct fecdec_group *fg) return 1; } +/** + * The different states of a complete FEC group. + * + * Even if a FEC group has been received successfully, it probably can not be + * used right away because some streams (ogg, wma) need to receive an audio + * file header before decoding can start. + */ enum fec_group_usability { + /** Drop the group (because we did not receive the header yet). */ FEC_GROUP_UNUSABLE, + /** Use all data in the group. */ FEC_GROUP_USABLE, + /** Use the group, but drop its audio file header. */ FEC_GROUP_USABLE_SKIP_HEADER, + /** Use the group, including its header. */ FEC_GROUP_USABLE_WITH_HEADER }; @@ -275,6 +286,7 @@ static int decode_group(struct fecdec_group *fg, struct filter_node *fn) size_t written, need; struct private_fecdec_data *pfd = fn->private_data; enum fec_group_usability u = group_is_usable(fg, pfd); + char *buf = NULL, *p; if (u == FEC_GROUP_UNUSABLE) { PARA_INFO_LOG("dropping unusable group %d\n", fg->h.group_num); @@ -295,14 +307,11 @@ static int decode_group(struct fecdec_group *fg, struct filter_node *fn) PARA_DEBUG_LOG("writing group %d (%d/%d decoded data bytes)\n", fg->h.group_num, fg->h.group_bytes, fg->h.data_slices_per_group * sb); - need = fn->loaded + (fg->h.data_slices_per_group - i)* sb; - if (need > fn->bufsize) { - fn->bufsize = PARA_MAX(fn->bufsize * 2, need); - if (fn->bufsize > FECDEC_MAX_OUTBUF_SIZE) - return -E_FECDEC_OVERRUN; - PARA_INFO_LOG("increasing fec buf to %zu\n", fn->bufsize); - fn->buf = para_realloc(fn->buf, fn->bufsize); - } + need = (fg->h.data_slices_per_group - i) * sb; + if (need > btr_pool_unused(pfd->btrp)) + return -E_FECDEC_OVERRUN; + btr_pool_get_buffer(pfd->btrp, &buf); + p = buf; if (u == FEC_GROUP_USABLE_WITH_HEADER) { PARA_INFO_LOG("writing audio file header\n"); written = 0; @@ -312,20 +321,20 @@ static int decode_group(struct fecdec_group *fg, struct filter_node *fn) break; if (sb + written > fg->h.audio_header_size) n = fg->h.audio_header_size - written; - memcpy(fn->buf + fn->loaded, fg->data[i], n); - fn->loaded += n; + btr_copy(fg->data[i], n, pfd->btrp, fn->btrn); written += n; } + p += written; } written = 0; for (; i < fg->h.data_slices_per_group; i++) { size_t n = sb; if (n + written > fg->h.group_bytes) n = fg->h.group_bytes - written; - memcpy(fn->buf + fn->loaded, fg->data[i], n); - fn->loaded += n; + btr_copy(fg->data[i], n, pfd->btrp, fn->btrn); written += n; } + p += written; return 0; } @@ -370,8 +379,10 @@ static int dispatch_slice(char *buf, size_t len, struct fec_header *h, int ret, k, n; struct private_fecdec_data *pfd = fn->private_data; - if (h->slice_bytes > len) /* can not use the thing, try to read more */ + if (h->slice_bytes > len) { /* can not use the thing, try to read more */ + fn->min_iqs = h->slice_bytes + FEC_HEADER_SIZE; return 0; + } ret = get_group(h, pfd, &fg); if (ret < 0) return ret; @@ -398,6 +409,7 @@ static int dispatch_slice(char *buf, size_t len, struct fec_header *h, ret = fec_new(k, n, &pfd->fec); if (ret < 0) return ret; + pfd->btrp = btr_pool_new("fecdec", 20 * k * h->slice_bytes); /* decode and clear the first group */ ret = decode_group(pfd->first_complete_group, fn); if (ret < 0) @@ -411,58 +423,73 @@ decode: return 1; } -static ssize_t fecdec(char *buf, size_t len, struct filter_node *fn) +static void fecdec_close(struct filter_node *fn) { + struct private_fecdec_data *pfd = fn->private_data; + struct fecdec_group *fg; + + FOR_EACH_FECDEC_GROUP(fg, pfd) + clear_group(fg); + fec_free(pfd->fec); + btr_pool_free(pfd->btrp); + free(fn->private_data); + fn->private_data = NULL; +} + +static void fecdec_post_select(__a_unused struct sched *s, struct task *t) +{ + struct filter_node *fn = container_of(t, struct filter_node, task); + struct btr_node *btrn = fn->btrn; int ret; struct fec_header h; + char *buf; + size_t len; +next_buffer: + ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); + if (ret <= 0) + goto out; + btr_merge(btrn, fn->min_iqs); + len = btr_next_buffer(btrn, &buf); ret = read_fec_header(buf, len, &h); if (ret <= 0) - return ret; + goto out; + ret = -E_BAD_SLICE_SIZE; if (!h.slice_bytes) - return -E_BAD_SLICE_SIZE; + goto out; + ret = -E_BAD_SLICE_NUM; if (h.slice_num > h.slices_per_group) - return -E_BAD_SLICE_NUM; + goto out; ret = dispatch_slice(buf + FEC_HEADER_SIZE, len - FEC_HEADER_SIZE, &h, fn); //PARA_INFO_LOG("ret: %d, len: %d, slice_bytes: %d\n", ret, len, h.slice_bytes); if (ret <= 0) - return ret; - return FEC_HEADER_SIZE + h.slice_bytes; -} - -static void fecdec_close(struct filter_node *fn) -{ - struct private_fecdec_data *pfd = fn->private_data; - struct fecdec_group *fg; - - FOR_EACH_FECDEC_GROUP(fg, pfd) - clear_group(fg); - free(fn->buf); - fn->buf = NULL; - fec_free(pfd->fec); - free(fn->private_data); - fn->private_data = NULL; + goto out; + btr_consume(btrn, FEC_HEADER_SIZE + h.slice_bytes); + goto next_buffer; +out: + t->error = ret; + if (ret < 0) + btr_remove_node(btrn); } static void fecdec_open(struct filter_node *fn) { struct private_fecdec_data *pfd; - fn->bufsize = FECDEC_DEFAULT_OUTBUF_SIZE; - fn->buf = para_malloc(fn->bufsize); pfd = para_calloc(sizeof(*pfd)); fn->private_data = pfd; - fn->loaded = 0; + fn->min_iqs = FEC_HEADER_SIZE; } /** * The init function of the fecdec filter. * - * \param f struct to initialize. + * \param f Struct to initialize. */ void fecdec_filter_init(struct filter *f) { - f->convert = fecdec; f->close = fecdec_close; f->open = fecdec_open; + f->pre_select = generic_filter_pre_select; + f->post_select = fecdec_post_select; } diff --git a/file_write.c b/file_write.c index e03cf8f6..0cee535a 100644 --- a/file_write.c +++ b/file_write.c @@ -1,97 +1,132 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file file_write.c simple output plugin for testing purposes */ +#include #include #include +#include +#include #include "para.h" #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "write.h" +#include "write_common.h" #include "string.h" #include "fd.h" #include "file_write.cmdline.h" #include "error.h" -/** data specific to the file writer */ +/** Data specific to the file writer. */ struct private_file_write_data { - /** the file descriptor of the output file */ + /** The file descriptor of the output file. */ int fd; - /** non-zero if \a fd was added to the write fd set */ - int check_fd; }; +/* + * Get a random filename. + * + * This is by no means a secure way to create temporary files in a hostile + * directory like \p /tmp. However, we use it only for creating temp files in + * ~/.paraslash, for which it is OK. Result must be freed by the caller. + */ +__must_check __malloc static char *random_filename(void) +{ + char *result, *home = para_homedir(); + struct timeval tv; + + gettimeofday(&tv, NULL); + srandom(tv.tv_usec); + result = make_message("%s/.paraslash/%08lu", home, + para_random(99999999)); + free(home); + return result; +} + static int file_write_open(struct writer_node *wn) { struct private_file_write_data *pfwd = para_calloc( sizeof(struct private_file_write_data)); struct file_write_args_info *conf = wn->conf; char *filename; + int ret; + if (conf->filename_given) filename = conf->filename_arg; - else { - char *tmp = para_tmpname(), *home = para_homedir(); - filename = make_message("%s/.paraslash/%s", home, tmp); - free(home); - free(tmp); - } + else + filename = random_filename(); wn->private_data = pfwd; - pfwd->fd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + ret = para_open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); if (!conf->filename_given) free(filename); - if (pfwd->fd >= 0) + if (ret < 0) + goto out; + pfwd->fd = ret; + ret = mark_fd_blocking(pfwd->fd); + if (ret >= 0) return 1; + close(pfwd->fd); +out: free(pfwd); - return -E_FW_OPEN; + return ret; } -static int file_write_pre_select(struct sched *s, struct writer_node *wn) +static void file_write_pre_select(struct sched *s, struct task *t) { + struct writer_node *wn = container_of(t, struct writer_node, task); struct private_file_write_data *pfwd = wn->private_data; - struct writer_node_group *wng = wn->wng; + int ret; - pfwd->check_fd = 0; - if (pfwd->fd <= 0) - return -E_FW_NO_FILE; - if (!*wng->loaded) - return 1; - para_fd_set(pfwd->fd, &s->wfds, &s->max_fileno); - pfwd->check_fd = 1; - return 1; + t->error = 0; + ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); + if (ret > 0) + para_fd_set(pfwd->fd, &s->wfds, &s->max_fileno); + else if (ret < 0) + sched_min_delay(s); } -static int file_write_post_select(struct sched *s, struct writer_node *wn) +static void file_write_close(struct writer_node *wn) { struct private_file_write_data *pfwd = wn->private_data; - struct writer_node_group *wng = wn->wng; - int ret; - if (!pfwd->check_fd) - return 1; - if (*wng->loaded <= wn->written) - return 1; - if (!FD_ISSET(pfwd->fd, &s->wfds)) - return 1; -// PARA_INFO_LOG("writing %zd\n", *wng->loaded); - ret = write(pfwd->fd, *wng->bufp + wn->written, - *wng->loaded - wn->written); - if (ret < 0) - return -E_FW_WRITE; - wn->written += ret; - return 1; + close(pfwd->fd); + free(pfwd); } -static void file_write_close(struct writer_node *wn) +static void file_write_post_select(__a_unused struct sched *s, + struct task *t) { + struct writer_node *wn = container_of(t, struct writer_node, task); struct private_file_write_data *pfwd = wn->private_data; - close(pfwd->fd); - free(pfwd); + struct btr_node *btrn = wn->btrn; + int ret; + char *buf; + size_t bytes; + + t->error = 0; + ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF); + if (ret <= 0) + goto out; + if (!FD_ISSET(pfwd->fd, &s->wfds)) + return; + bytes = btr_next_buffer(btrn, &buf); + assert(bytes > 0); + //PARA_INFO_LOG("writing %zu\n", bytes); + ret = write(pfwd->fd, buf, bytes); + if (ret < 0) + goto out; + btr_consume(btrn, ret); +out: + if (ret < 0) + btr_remove_node(btrn); + t->error = ret; } __malloc static void *file_write_parse_config(const char *options) @@ -107,6 +142,11 @@ __malloc static void *file_write_parse_config(const char *options) return NULL; } +static void file_write_free_config(void *conf) +{ + file_cmdline_parser_free(conf); +} + /** the init function of the file writer */ void file_write_init(struct writer *w) { @@ -117,6 +157,7 @@ void file_write_init(struct writer *w) w->pre_select = file_write_pre_select; w->post_select = file_write_post_select; w->parse_config = file_write_parse_config; + w->free_config = file_write_free_config; w->close = file_write_close; w->shutdown = NULL; /* nothing to do */ w->help = (struct ggo_help) { diff --git a/filter.c b/filter.c index 98cef2fa..7726e35f 100644 --- a/filter.c +++ b/filter.c @@ -1,17 +1,20 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file filter.c The stand-alone filter program. */ -#include "para.h" +#include +#include +#include "para.h" #include "filter.cmdline.h" #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "string.h" #include "stdin.h" @@ -31,11 +34,6 @@ static struct stdin_task stdin_task_struct; /** pointer to the stdin task. */ static struct stdin_task *sit = &stdin_task_struct; -/** The task that filters the data. */ -static struct filter_chain filter_chain_struct; -/** Pointer to the filter chain. */ -static struct filter_chain *fc = &filter_chain_struct; - /** The task that writes converted data to stdout. */ static struct stdout_task stdout_task_struct; /** Pointer to the stdout task. */ @@ -47,65 +45,6 @@ static struct filter_args_info conf; static int loglevel; INIT_STDERR_LOGGING(loglevel); -static void open_filters(void) -{ - int i; - struct filter_node *fn; - - FOR_EACH_FILTER_NODE(fn, fc, i) { - struct filter *f = filters + fn->filter_num; - f->open(fn); - PARA_INFO_LOG("opened %s filter\n", f->name); - fc->outbufp = &fn->buf; - fc->out_loaded = &fn->loaded; - } -} - -static void free_filter_confs(void) -{ - int i; - struct filter_node *fn; - - FOR_EACH_FILTER_NODE(fn, fc, i) - free(fn->conf); -} - -static int init_filter_chain(void) -{ - int i, ret; - struct filter_node *fn; - - if (!conf.filter_given) - return -E_NO_FILTERS; - fc->num_filters = conf.filter_given; - fc->filter_nodes = para_calloc(fc->num_filters * sizeof(struct filter_node)); - fc->inbufp = &sit->buf; - fc->in_loaded = &sit->loaded; - fc->input_error = &sit->task.error; - fc->task.error = 0; - fc->output_error = &sot->task.error; - fc->task.post_select = filter_post_select; - sprintf(fc->task.status, "filter chain"); - - FOR_EACH_FILTER_NODE(fn, fc, i) { - char *fa = conf.filter_arg[i]; - fn = fc->filter_nodes + i; - ret = check_filter_arg(fa, &fn->conf); - if (ret < 0) - goto err; - fn->filter_num = ret; - fn->fc = fc; - INIT_LIST_HEAD(&fn->callbacks); - PARA_DEBUG_LOG("filter #%d: %s\n", i, filters[fn->filter_num].name); - } - open_filters(); - return 1; -err: - free_filter_confs(); - free(fc->filter_nodes); - return ret; -} - __noreturn static void print_help_and_die(void) { int d = conf.detailed_help_given; @@ -148,6 +87,8 @@ static int parse_config(int argc, char *argv[]) if (filter_cmdline_parser_config_file(cf, &conf, ¶ms)) return -E_FILTER_SYNTAX; } + if (!conf.filter_given) + return -E_NO_FILTERS; return 1; } @@ -165,37 +106,70 @@ static int parse_config(int argc, char *argv[]) */ int main(int argc, char *argv[]) { - int ret; static struct sched s; - - stdin_set_defaults(sit); - sit->buf = para_malloc(sit->bufsize), + int i, ret; + struct filter *f; + struct btr_node *parent; + struct filter_node **fns; filter_init(); ret = parse_config(argc, argv); if (ret < 0) goto out; - ret = init_filter_chain(); - if (ret < 0) - goto out; - sit->output_error = &fc->task.error; + sit->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "stdin")); + stdin_set_defaults(sit); + register_task(&sit->task); - stdout_set_defaults(sot); - sot->bufp = fc->outbufp; - sot->loaded = fc->out_loaded; - sot->input_error = &fc->task.error; + fns = para_malloc(conf.filter_given * sizeof(*fns)); + for (i = 0, parent = sit->btrn; i < conf.filter_given; i++) { + char *fa = conf.filter_arg[i]; + struct filter_node *fn; - register_task(&sit->task); + fn = fns[i] = para_calloc(sizeof(*fn)); + ret = check_filter_arg(fa, &fn->conf); + if (ret < 0) { + free(fn); + goto out_cleanup; + } + fn->filter_num = ret; + f = filters + fn->filter_num; + sprintf(fn->task.status, "%s", f->name); + PARA_DEBUG_LOG("filter #%d: %s\n", i, f->name); + fn->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = f->name, .parent = parent, + .handler = f->execute, .context = fn)); + fn->task.pre_select = f->pre_select; + fn->task.post_select = f->post_select; + f->open(fn); + register_task(&fn->task); + parent = fn->btrn; + } + sot->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "stdout", .parent = parent)); + stdout_set_defaults(sot); register_task(&sot->task); - register_task(&fc->task); + s.default_timeout.tv_sec = 1; s.default_timeout.tv_usec = 0; + btr_log_tree(sit->btrn, LL_INFO); ret = schedule(&s); - free_filter_confs(); - close_filters(fc); +out_cleanup: + for (i--; i >= 0; i--) { + struct filter_node *fn = fns[i]; + + f = filters + fn->filter_num; + if (f->close) + f->close(fn); + btr_free_node(fn->btrn); + free(fn->conf); + free(fn); + } + free(fns); + btr_free_node(sit->btrn); + btr_free_node(sot->btrn); out: - free(sit->buf); if (ret < 0) PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - return ret < 0? EXIT_FAILURE : EXIT_SUCCESS; + exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS); } diff --git a/filter.h b/filter.h index b877b649..befcd338 100644 --- a/filter.h +++ b/filter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -15,123 +15,20 @@ enum filter_enum {FILTER_ENUM}; struct filter_node { /** The number in the array of available filters. */ unsigned filter_num; - /** The filter chain this filter node belongs to. */ - struct filter_chain *fc; /** * Each filter may store any filter-specific information about the particular * instance of the filter here. */ void *private_data; - /** The output buffer. */ - char *buf; - /** The size of the output buffer. */ - size_t bufsize; - /** The number of bytes currently loaded in \a buf. */ - size_t loaded; /** The list of registered callbacks. */ struct list_head callbacks; /** A pointer to the configuration of this instance. */ void *conf; -}; - -/** Describes one running instance of a chain of filters */ -struct filter_chain { - /** The length of the filter chain. */ - unsigned int num_filters; - /** - * The number of channels of the current stream. - * - * Set by the decoding filter. - */ - unsigned int channels; - /** - * Current sample rate in Hz. - * - * Set by the decoding filter. - */ - unsigned int samplerate; - /** The list containing all filter nodes in this filter chain. */ - struct filter_node *filter_nodes; - /** - * The input buffer of the filter chain. - * - * This is set to point to the output buffer of the receiving application (the - * buffer used to read from stdin for para_filter; the output buffer of the - * current receiver for para_audiod). - */ - char **inbufp; - /** - * The output buffer of the filter chain. - * - * Points to the output buffer of the last filter in the filter chain. - */ - char **outbufp; - /** Contains the number of bytes loaded in the input buffer. */ - size_t *in_loaded; - /** Contains the number of bytes loaded in the output buffer. */ - size_t *out_loaded; - /** Pointer to the error variable of the receiving application. */ - int *input_error; - /** Pointer to the error variable of the writing application. */ - int *output_error; - /** The task associated with the filter chain. */ + struct btr_node *btrn; struct task task; + size_t min_iqs; }; -#define FOR_EACH_FILTER_NODE(fn, fc, i) for (i = 0; i < (fc)->num_filters \ - && (fn = (fc)->filter_nodes + i); i++) - - -/** - * Used to manage grab clients. - * - * An application using paraslash's filter subsystem may register any number of - * callbacks for each filter_node. It is possible to attach a filter callback - * while the filter is running. This is used for stream grabbing in - * para_audiod: Whenever a client sends the 'grab' command, para_audiod adds a - * filter callback to the list of callbacks for the filter node specified in - * the grab command. - */ -struct filter_callback { - /** All callbacks are organized in a doubly linked list. */ - struct list_head node; - /** - * Private data. - * - * May be initialized by the application before registering the callback. This - * pointer is not used by the filter subsystem. It is provided for use within - * the input/output/close callback functions. - */ - void *data; - /** - * The input callback. - * - * In not \p NULL, the filter subsystem calls this function whenever the filter - * consumed some or all of its input buffer. A pointer to the buffer of consumed - * data, its length and a pointer to the own \a filter_callback structure are passed - * to \a input_cb. The input callback is expected to return a negative value on errors. - */ - int (*input_cb)(char *buf, size_t len, struct filter_callback *fc); - /** - * The output callback. - * - * If not NULL, this is called whenever the filter produces output. A pointer - * to the output data, its length and a pointer to the own \a filter_callback - * structure are passed to \a output_cb. Like the input callback, the output - * callback is expected to return a negative value on errors. - */ - int (*output_cb)(char *buf, size_t len, struct filter_callback *fc); - /** - * The callback close function. - * - * This gets called whenever the input/output callback returned an error, or if - * the filter chain is going to be destroyed, e.g. because the end of the - * stream was encountered. It is assumed to succeed. - */ - void (*close)(struct filter_callback *fc); -}; - - /** * The structure associated with a paraslash filter. * @@ -163,24 +60,12 @@ struct filter { * of \a fn suitably. The open function is assumed to succeed. */ void (*open)(struct filter_node *fn); - /** - * Convert (filter) the given data. - * - * Pointer to the converting function of the filter. It should convert the - * given input buffer \a inbuf which is of length \a len to the previously - * reserved output buffer of \a fn. On success, it must return the number of - * bytes it consumed from \a inbuf. On errors, a negative number indicating the - * kind of the error must be returned. - * - * A zero return value just means that nothing was converted (probably because - * the input buffer was too small). This is not interpreted as an error. - */ - ssize_t (*convert)(char *inbuf, size_t len, struct filter_node *fn); /** * Close one instance of this filter. * * Free all resources of associated with \a fn that were previously allocated - * by the open() function. + * by the open() function. It's OK to leave this alone if the filter does not + * need any cleanups. */ void (*close)(struct filter_node *fn); /** @@ -196,16 +81,26 @@ struct filter { * argv. On failure, a negative paraslash error code must be returned. */ int (*parse_config)(int argc, char **argv, void **config); + void (*free_config)(void *conf); /** The help texts for this filter. */ struct ggo_help help; + void (*pre_select)(struct sched *s, struct task *t); + /** + * Convert (filter) the given data. + * + * Pointer to the converting function of the filter. It should convert as + * input data as possible. On errors, the post_select function is supposed + * to set t->error to a (negative) error code. + */ + void (*post_select)(struct sched *s, struct task *t); + btr_command_handler execute; }; -void close_filters(struct filter_chain *fc); void filter_init(void); int check_filter_arg(char *filter_arg, void **conf); -void filter_post_select(__a_unused struct sched *s, struct task *t); void print_filter_helps(int detailed); +void generic_filter_pre_select(struct sched *s, struct task *t); static inline void write_int16_host_endian(char *buf, int val) { diff --git a/filter_common.c b/filter_common.c index 2fa774c0..06a1f52b 100644 --- a/filter_common.c +++ b/filter_common.c @@ -1,19 +1,22 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file filter_common.c Common helper functions for filter input/output. */ +#include #include #include +#include #include "para.h" #include "list.h" #include "sched.h" #include "fd.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "error.h" #include "string.h" @@ -33,153 +36,6 @@ void filter_init(void) filters[i].init(filters + i); } -/** - * Close and destroy a filter callback. - * - * \param fcb The filter callback to close. - * - * This removes \a fcb from the list of filter callbacks and calls - * the close callback associated with \a fcb. - */ -static void close_filter_callback(struct filter_callback *fcb) -{ - PARA_NOTICE_LOG("closing filter_callback %p, data: %p\n", fcb, fcb->data); - list_del(&fcb->node); - fcb->close(fcb); -} - -/** - * Close all callbacks of a filter node. - * - * \param fn The filter node which contains the filter callbacks to be closed. - * - * Call close_filter_callback() for each entry in the filter callback list - * of \a fn. - */ -static void close_callbacks(struct filter_node *fn) -{ - struct filter_callback *fcb, *tmp; - - list_for_each_entry_safe(fcb, tmp, &fn->callbacks, node) { - PARA_INFO_LOG("closing %s filter callback\n", - filters[fn->filter_num].name); - close_filter_callback(fcb); - } -} - -static void call_callbacks(struct filter_node *fn, char *inbuf, size_t inlen, - char *outbuf, size_t outlen) -{ - struct filter_callback *fcb, *tmp; - list_for_each_entry_safe(fcb, tmp, &fn->callbacks, node) { - int ret; - if (inlen && fcb->input_cb) { - ret = fcb->input_cb(inbuf, inlen, fcb); - if (ret < 0) { - close_filter_callback(fcb); - continue; - } - } - if (!outlen || !fcb->output_cb) - continue; - ret = fcb->output_cb(outbuf, outlen, fcb); - if (ret < 0) - close_filter_callback(fcb); - } -} - -/** - * Call the convert function of each filter. - * - * \param s Unused. - * \param t The task identifying the filter chain. - * - * This is the core function of the filter subsystem. It loops over the list of - * filter nodes determined by \a t and calls the filter's convert function if - * there is input available for the filter node in question. If the convert - * function consumed some or all of its input data, all registered input - * callbacks are called. Similarly, if a convert function produced output, all - * registered output callbacks get called. - * - * On errors a (negative) error code is stored in t->error. - * - * \sa filter_node, filter#convert, filter_callback. - */ -void filter_post_select(__a_unused struct sched *s, struct task *t) -{ - struct filter_chain *fc = container_of(t, struct filter_chain, task); - struct filter_node *fn; - char *ib; - size_t *loaded; - int i, conv, conv_total = 0; - - if (fc->output_error && *fc->output_error < 0) { - t->error = *fc->output_error; - return; - } -again: - ib = *fc->inbufp; - loaded = fc->in_loaded; - conv = 0; - FOR_EACH_FILTER_NODE(fn, fc, i) { - struct filter *f = filters + fn->filter_num; - if (fn->loaded < fn->bufsize) { - size_t size, old_fn_loaded = fn->loaded; - t->error = f->convert(ib, *loaded, fn); - if (t->error < 0) - return; - size = t->error; - call_callbacks(fn, ib, size, fn->buf + old_fn_loaded, - fn->loaded - old_fn_loaded); - *loaded -= size; - conv += size + fn->loaded - old_fn_loaded; - if (*loaded && size) - memmove(ib, ib + size, *loaded); - } - ib = fn->buf; - loaded = &fn->loaded; - } - conv_total += conv; - if (conv) - goto again; - if (*fc->input_error >= 0) - return; - if (*fc->out_loaded) - return; - if (*fc->in_loaded && conv_total) - return; - t->error = -E_FC_EOF; -} - -/** - * Close all filter nodes and their callbacks. - * - * \param fc The filter chain to close. - * - * For each filter node determined by \a fc, call the close function of each - * registered filter callback as well as the close function of the - * corresponding filter. Free all resources and destroy all callback lists and - * the filter node list. - * - * \sa filter::close, filter_callback::close - */ -void close_filters(struct filter_chain *fc) -{ - struct filter_node *fn; - int i; - - if (!fc) - return; - PARA_NOTICE_LOG("closing filter chain %p\n", fc); - FOR_EACH_FILTER_NODE(fn, fc, i) { - struct filter *f = filters + fn->filter_num; - close_callbacks(fn); - PARA_INFO_LOG("closing %s filter\n", f->name); - f->close(fn); - } - free(fc->filter_nodes); -} - /* * If the filter has a command line parser and options is not NULL, run it. * Returns filter_num on success, negative on errors @@ -195,15 +51,18 @@ static int parse_filter_args(int filter_num, char *options, void **conf) if (!f->parse_config) return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num; // PARA_DEBUG_LOG("options: %s\n", options); - argc = split_args(options, &argv, " \t"); -// PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]); + argc = create_argv(options, " \t", &argv); + if (argc < 0) + return -E_BAD_FILTER_OPTIONS; + PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]); for (i = argc - 1; i >= 0; i--) argv[i + 1] = argv[i]; argv[0] = para_strdup(f->name); - argc += 1; + argc++; ret = f->parse_config(argc, argv, conf); - free(argv[0]); - free(argv); + free(argv[argc - 1]); + argv[argc - 1] = NULL; + free_argv(argv); return ret < 0? ret : filter_num; } @@ -267,5 +126,13 @@ void print_filter_helps(int detailed) printf_or_die("Options for %s:\n", f->name); ggo_print_help(&f->help, detailed); } +} + +void generic_filter_pre_select(struct sched *s, struct task *t) +{ + struct filter_node *fn = container_of(t, struct filter_node, task); + t->error = 0; + if (btr_node_status(fn->btrn, fn->min_iqs, BTR_NT_INTERNAL) != 0) + sched_min_delay(s); } diff --git a/fsck.c b/fsck.c deleted file mode 100644 index 3b605c2d..00000000 --- a/fsck.c +++ /dev/null @@ -1,989 +0,0 @@ -/* - * Copyright (C) 1997-2009 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file fsck.c The program used to check an osl table. */ - - -#include -#include - -#include "para.h" -#include "fd.h" -#include "error.h" -#include "osl_core.h" -#include "fsck.cmdline.h" - -static struct fsck_args_info conf; - -INIT_FSCK_ERRLISTS; - -static int loglevel; -INIT_STDERR_LOGGING(loglevel); - -/* taken from git */ -signed char hexval_table[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */ - 0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */ - 8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */ - -1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */ - -1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */ - -1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */ - -1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */ - -1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */ - -1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */ - -1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */ -}; - -int asc_to_hash(const char *asc_hash, int len, HASH_TYPE *hash) -{ - int i = 0; - const unsigned char *asc = (const unsigned char *) asc_hash; - - while (*asc && i++ < len) { - unsigned int val = (hexval_table[asc[0]] << 4) | hexval_table[asc[1]]; - if (val & ~0xff) - return -1; - *hash++ = val; - asc += 2; - - } - return 1; -} - -/* - * check for object boundary violations - * - * test whether the range pointed to by the index entry for a given cell is - * contained in mapped data file. This should always be the case. Otherwise - * we are in real trouble. - */ -static int check_range(struct osl_table *t, uint32_t row_num, uint32_t col_num) -{ - char *index_entry; - struct osl_object obj; - struct osl_column *col; - int ret; - char *map_start, *obj_start; - - ret = get_cell_index(t, row_num, col_num, &index_entry); - if (ret < 0) - return ret; - ret = get_mapped_object(t, col_num, row_num, &obj); - if (ret < 0) - return ret; - col = t->columns + col_num; - obj_start = obj.data; - map_start = col->data_map.data; -// PARA_INFO_LOG("obj: %p..%p\n", obj_start, obj_start + obj.size); -// PARA_INFO_LOG("map: %p..%p\n", map_start, map_start + col->data_map.size); - if (obj_start < map_start || obj_start + obj.size > map_start + col->data_map.size) { - PARA_CRIT_LOG("range violation in row %u, col %u\n", row_num, - col_num); - return -E_RANGE_VIOLATION; - } - PARA_DEBUG_LOG("col %u: ok\n", col_num); - return 1; -} - -/* - * check all cells of the given table for boundary violations - */ -static int check_index_ranges(struct osl_table *t) -{ - int i, j, ret; - - PARA_INFO_LOG("checking for range violations in index\n"); - //PARA_DEBUG_LOG("%d rows. %d columns\n", t->num_rows, t->desc->num_columns); - t->num_invalid_rows = 0; - for (i = 0; i < t->num_rows; i++) { - if (row_is_invalid(t, i)) { - t->num_invalid_rows++; - continue; - } - for (j = 0; j < t->desc->num_columns; j++) { /* FXIME */ - const struct osl_column_description *cd = - get_column_description(t->desc, j); - if (cd->storage_type != OSL_MAPPED_STORAGE) - continue; - ret = check_range(t, i, j); - if (ret < 0) { - if (ret != -E_INVALID_OBJECT && - ret != -E_RANGE_VIOLATION) - goto err; - if (ret == -E_INVALID_OBJECT) { - PARA_CRIT_LOG("row %d, col %d maps to an " - "invalid object\n", i, j); - } - ret = mark_row_invalid(t, i); - if (ret < 0) - goto err; - t->num_invalid_rows++; - break; - } - } - - } - if (t->num_invalid_rows) - PARA_NOTICE_LOG("ranges OK. %d invalid row(s) detected\n", - t->num_invalid_rows); - else - PARA_INFO_LOG("no invalid rows, no range violations, good\n"); - return 1; -err: - return ret; -} - -static int move_index_entry(struct osl_table *t, uint32_t dest, uint32_t src) -{ - char *dest_ie, *src_ie; - int ret = get_row_index(t, dest, &dest_ie); - - if (ret < 0) - return ret; - ret = get_row_index(t, src, &src_ie); - if (ret < 0) - return ret; - PARA_INFO_LOG("moving entry #%u to position %u\n", src, dest); - memcpy(dest_ie, src_ie, t->row_index_size); - return 1; -} - -static int map_index(const struct osl_table_description *desc, struct osl_object *map) -{ - char *filename = index_filename(desc); - int ret; - - ret = mmap_full_file(filename, O_RDWR, &map->data, &map->size, NULL); - PARA_DEBUG_LOG("mapping index %s: ret: %d, size: %zu\n", filename, ret, map->size); - free(filename); - return ret; -} - -static int prune_invalid_rows_from_index(struct osl_table *t) -{ - uint32_t top = 0, bottom; - char *filename; - int ret; - - if (!t->num_invalid_rows) { - PARA_INFO_LOG("all rows are valid, good\n"); - return 1; - } - PARA_NOTICE_LOG("deleting %u invalid row(s) (%d bytes) from index\n", - t->num_invalid_rows, t->row_index_size * t->num_invalid_rows); - bottom = t->num_rows - 1; - while (top < bottom) { - if (!row_is_invalid(t, top)) { - top++; - continue; - } - while (bottom > top) { - if (row_is_invalid(t, bottom)) { - bottom--; - continue; - } - /* move bottom index entry to top */ - move_index_entry(t, top, bottom); - bottom--; - top++; - break; - } - } - PARA_DEBUG_LOG("unmapping index\n"); - para_munmap(t->index_map.data, t->index_map.size); - filename = index_filename(t->desc); - ret = para_truncate(filename, t->row_index_size - * t->num_invalid_rows); - free(filename); - if (ret < 0) - return ret; - ret = map_index(t->desc, &t->index_map); - if (ret < 0) - return ret; - t->num_rows = table_num_rows(t); - return 1; -} - -static int check_for_invalid_objects(struct osl_table *t, uint32_t **lost_bytes) -{ - int i, j, ret; - const struct osl_column_description *cd; - uint32_t *loss = para_malloc(sizeof(uint32_t) * t->desc->num_columns); - - PARA_INFO_LOG("looking for mapped objects not contained in index\n"); - /* first count used bytes */ - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - loss[i] = t->columns[i].data_map.size; - for (j = 0; j < t->num_rows; j++) { - struct osl_object obj; - ret = get_mapped_object(t, i, j, &obj); - if (ret >= 0) { - loss[i] -= obj.size + 1; /* add one for header byte */ - continue; - } - if (ret != -E_INVALID_OBJECT) - goto err; - PARA_CRIT_LOG("row %d, col %d points to an invalid " - "mapped object, bad\n", j, i); - } - } - ret = 0; - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - if (loss[i]) { - PARA_NOTICE_LOG("column %u contains %u lost bytes\n", - i, loss[i]); - ret = 1; - } - } - if (!ret) - PARA_INFO_LOG("all mapped objects are valid, good\n"); - *lost_bytes = loss; - return ret; -err: - free(loss); - return ret; -} - -/* prune_invalid_rows() must be run on the table before calling this */ -static int prune_mapped_column(struct osl_table *t, uint32_t col_num, int fd) -{ - int i, ret; - uint32_t written = 0; - struct osl_column *col = t->columns + col_num; - - PARA_INFO_LOG("pruning col %u\n", col_num); - for (i = 0; i < t->num_rows; i++) { - struct osl_object obj; - char *index_entry; - - PARA_DEBUG_LOG("checking row %u/%u\n", i, t->num_rows); - ret = get_mapped_object(t, col_num, i, &obj); - if (ret < 0) - return ret; - ret = para_write_all(fd, (char *)(obj.data) - 1, obj.size + 1); - if (ret < 0) - return ret; - written += obj.size + 1; - ret = get_row_index(t, i, &index_entry); - if (ret < 0) - return ret; - update_cell_index(index_entry, col, written, obj.size); - } - return 1; -} - -static int prune_objects(struct osl_table *t, uint32_t *lost_bytes) -{ - int i, ret; - const struct osl_column_description *cd; - char **col_filenames = para_calloc(t->desc->num_columns * sizeof(char *)); - char **new_col_filenames = para_calloc(t->desc->num_columns * sizeof(char *)); - char *idx_filename = index_filename(t->desc); - char *old_idx_filename = make_message("%s.bak", idx_filename); - int fd; - - PARA_NOTICE_LOG("removing unreferenced objects from data files\n"); - /* first make a copy of the index */ - ret = para_open(old_idx_filename, O_WRONLY | O_CREAT | O_EXCL, 0644); - if (ret < 0) - goto out_free; - fd = ret; - ret = para_write_all(fd, t->index_map.data, t->index_map.size); - close(fd); - if (ret < 0) - goto out_free; - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - if (!lost_bytes[i]) - continue; - col_filenames[i] = column_filename(t, i); - new_col_filenames[i] = make_message("%s.fsck", col_filenames[i]); - ret = para_open(new_col_filenames[i], O_WRONLY | O_CREAT | O_EXCL, 0644); - if (ret < 0) - goto out_unlink_data; - fd = ret; - ret = prune_mapped_column(t, i, fd); - close(fd); - if (ret < 0) - goto out_unlink_data; - } - ret = unmap_table(t, OSL_MARK_CLEAN); - if (ret < 0) - goto out_unlink_data; - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - if (!lost_bytes[i]) - continue; - ret = para_rename(new_col_filenames[i], col_filenames[i]); - if (ret < 0) { /* we're kinda screwed here */ - PARA_CRIT_LOG("rename of col %i failed: %s\n", i, - strerror(errno)); - goto out_free; - } - } - unlink(old_idx_filename); - ret = map_table(t, 0); - goto out_free; -out_unlink_data: - FOR_EACH_MAPPED_COLUMN(i, t, cd) - unlink(new_col_filenames[i]); -out_free: - free(old_idx_filename); - free(idx_filename); - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - free(col_filenames[i]); - free(new_col_filenames[i]); - } - free(col_filenames); - free(new_col_filenames); - return ret; -} - -static struct osl_column_description hash_tree_table_cols[] = { - { - .storage_type = OSL_NO_STORAGE, - .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE, - .name = "hash", - .compare_function = uint32_compare, - .data_size = HASH_SIZE - }, -}; - -static const struct osl_table_description hash_tree_table_desc = { - .dir = "/", /* irrelevant */ - .name = "hash_tree", - .num_columns = 1, - .flags = 0, - .column_descriptions = hash_tree_table_cols -}; - -/** - * The hash_tree table contains all hashes of the disk storage name column. - * of each row. It is used for checking if a disk storage file has a reference - * in the table. - */ -static struct osl_table *hash_tree_table; -static HASH_TYPE *hashes; - -static int check_disk_storage_column(struct osl_table *t, int row_num, - int col_num, char *ds_name, unsigned *num_missing_objects) -{ - int ret; - struct stat statbuf; - char *path = disk_storage_path(t, col_num, ds_name); - unsigned dsnc = t->disk_storage_name_column; - struct osl_object obj; - - PARA_DEBUG_LOG("checking if %s is a regular file\n", path); - ret = stat(path, &statbuf); - if (ret < 0 && errno == ENOENT) { - struct osl_row *row; - (*num_missing_objects)++; - PARA_ERROR_LOG("row %d: object %s is missing\n", row_num, path); - PARA_NOTICE_LOG("trying to delete row %d\n", row_num); - ret = osl_get_row(t, dsnc, &obj, &row); - if (ret < 0) { - PARA_CRIT_LOG("unable to get row %d\n", row_num); - mark_row_invalid(t, row_num); - PARA_CRIT_LOG("Please re-run fsck\n"); - goto out; - } - ret = osl_del_row(t, row); - if (ret < 0) - goto out; - } -out: - free(path); - if (ret < 0) - return ret; - ret = -E_NOT_A_REGULAR_FILE; - if (!(S_IFREG & statbuf.st_mode)) - return ret; - return 1; -} - -static int check_disk_storage_presence(struct osl_table *t) -{ - int ret, i, j; - struct osl_object obj, hash_obj = {.size = HASH_SIZE}; - char *ds_name; - const struct osl_column_description *cd; - unsigned dsnc = t->disk_storage_name_column, missing_objects = 0; - - if (!t->num_rows) - return 1; - hashes = para_malloc(t->num_rows * HASH_SIZE); - PARA_INFO_LOG("looking for missing disk storage objects\n"); - for (i = 0; i < t->num_rows; i++) { - if (row_is_invalid(t, i)) - continue; - ret = get_mapped_object(t, dsnc, i, &obj); - if (ret < 0) - return ret; - hash_object(&obj, hashes + i * HASH_SIZE); - hash_obj.data = hashes + i * HASH_SIZE; - osl_add_row(hash_tree_table, &hash_obj); - ds_name = disk_storage_name_of_hash(t, hashes + i * HASH_SIZE); - FOR_EACH_DISK_STORAGE_COLUMN(j, t, cd) { - ret = check_disk_storage_column(t, i, j, ds_name, - &missing_objects); - if (ret < 0) - goto err; - } - free(ds_name); - } - if (!missing_objects) - PARA_INFO_LOG("all referenced disk storage objects exist, good\n"); - else - PARA_NOTICE_LOG("%d missing object(s)\n", missing_objects); - return missing_objects; -err: - free(ds_name); - return ret; -} - -static int dummy_compare(const struct osl_object *obj1, const struct osl_object *obj2) -{ - if (obj1 < obj2) - return -1; - if (obj1 > obj2) - return 1; - return 0; -} - -static unsigned files_pruned; - -int prune_disk_storage_file(const char *path, void *private_data) -{ - HASH_TYPE hash[HASH_SIZE]; - unsigned flags = *(unsigned *)private_data; - struct osl_object obj = {.data = hash, .size = HASH_SIZE}; - struct osl_row *row; - int ret = -1; - size_t len = strlen(path); - - - PARA_DEBUG_LOG("path: %s\n", path); - if (flags & OSL_LARGE_TABLE) { - if (len < HASH_SIZE * 2 + 2) - goto invalid; -// PARA_NOTICE_LOG("p: %s\n", path + len - 2 * HASH_SIZE - 1); - ret = asc_to_hash(path + len - 2 * HASH_SIZE - 1, 1, hash); - if (ret < 0) - goto invalid; - ret = asc_to_hash(path + len - 2 * HASH_SIZE + 2, HASH_SIZE - 1, - hash + 1); - if (ret < 0) - goto invalid; -// PARA_INFO_LOG("high: %x, low: %x, hash: %x\n", high, low, hash); - } else { - if (len < 2 * HASH_SIZE + 1) - goto invalid; - ret = asc_to_hash(path + len - 2 * HASH_SIZE, 2 * HASH_SIZE, hash); - if (ret < 0) - goto invalid; -// PARA_INFO_LOG("hash: %x\n", hash); - } -#if 0 -{ - char asc[2 * HASH_SIZE + 1]; - hash_to_asc(hash, asc); - PARA_NOTICE_LOG("before: %s\nafter: %s\n", path, asc); -} -#endif - ret = osl_get_row(hash_tree_table, 0, &obj, &row); - if (ret >= 0) - return 1; - PARA_NOTICE_LOG("unreferenced file in hash dir: %s\n", path); - goto remove; -invalid: - PARA_ERROR_LOG("could not read hash value of %s\n", path); -remove: - PARA_NOTICE_LOG("removing %s\n", path); - unlink(path); - files_pruned++; - return 1; -} - -static int prune_disk_storage_files(struct osl_table *t) -{ - int i, ret = 1; - const struct osl_column_description *cd; - - PARA_INFO_LOG("looking for unreferenced disk storage files\n"); - FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) { - char *dirname = column_filename(t, i); - ret = for_each_file_in_dir(dirname, prune_disk_storage_file, - (unsigned *)&t->desc->flags); - free(dirname); - } - if (files_pruned) - PARA_NOTICE_LOG("%u disk storage files deleted\n", - files_pruned); - else - PARA_INFO_LOG("all files are are referenced, good\n"); - return ret; -} - -static int check_disk_storage_columns(struct osl_table *t) -{ - int ret, i; - const struct osl_column_description *cd; - - if (!t->num_disk_storage_columns) { - PARA_INFO_LOG("no disk storage columns in table '%s', " - "skipping checks\n", t->desc->name); - return 1; - } - FOR_EACH_COLUMN(i, t->desc, cd) - t->desc->column_descriptions[i].compare_function = dummy_compare; - ret = init_rbtrees(t); - if (ret < 0) - return ret; - PARA_INFO_LOG("creating rbtree for disk storage hash values\n"); - ret = osl_open_table(&hash_tree_table_desc, &hash_tree_table); - if (ret < 0) - goto out; - ret = check_disk_storage_presence(t); - if (ret < 0) - goto out_close_hash_tree; - ret = prune_disk_storage_files(t); -out_close_hash_tree: - osl_close_table(hash_tree_table, 0); - free(hashes); - hashes = NULL; -out: - clear_rbtrees(t); /* TODO why are we doing that here? Seems odd */ - return ret; -} - -static void set_dummy_contents(struct osl_table_description *desc) -{ - int i; - struct osl_column_description *cd; - - for (i = 0; i < desc->num_columns; i++) { - cd = get_column_description(desc, i); - cd->compare_function = dummy_compare; - } -} - -static int fsck_init(struct osl_table_description *desc, struct osl_table **t) -{ - struct osl_object map; - int ret = map_index(desc, &map); - - if (ret < 0) - goto out; - ret = read_table_desc(&map, desc); - if (ret < 0) { - para_munmap(map.data, map.size); - goto out; - } - set_dummy_contents(desc); - ret = init_table_structure(desc, t); - if (ret < 0) { - para_munmap(map.data, map.size); - goto out; - } - PARA_DEBUG_LOG("unmapping index\n"); - para_munmap(map.data, map.size); - if (conf.force_given) - ret = map_table(*t, (MAP_TBL_FL_IGNORE_DIRTY)); - else - ret = map_table(*t, 0); - if (ret >= 0) - (*t)->num_rows = table_num_rows(*t); -out: - return ret; -} - -static void fsck_cleanup(struct osl_table *t) -{ - int i; - - if (!t) - return; - if (t->desc->column_descriptions) { - struct osl_column_description *cd; - for (i = 0; i < t->desc->num_columns; i++) { - cd = get_column_description(t->desc, i); - free((char*)cd->name); - } - free(t->desc->column_descriptions); - } - free(t->columns); - free(t); - -} - -#define ST_CASE(st) case st: return #st - -const char *get_asc_storage_type(enum osl_storage_type st) -{ - switch (st) { - ST_CASE(OSL_MAPPED_STORAGE); - ST_CASE(OSL_DISK_STORAGE); - ST_CASE(OSL_NO_STORAGE); - } - return NULL; -} - -#define APPEND_ASC_SF(sf, flag, str) do { if (sf & flag) { \ - if (str) str = para_strcat(str, " | " # flag); \ - else str = para_strdup(#flag); }} while (0) - - -char *get_asc_storage_flags(enum osl_storage_type sf) -{ - char *asc_sf = NULL; - - APPEND_ASC_SF(sf, OSL_RBTREE, asc_sf); - APPEND_ASC_SF(sf, OSL_FIXED_SIZE, asc_sf); - APPEND_ASC_SF(sf, OSL_UNIQUE, asc_sf); - return asc_sf; -} - -static int dump_table_desc(struct osl_table *t, int fd) -{ - const struct osl_table_description *desc = t->desc; - int ret, i; - struct osl_column_description *cd; - char *msg = make_message("static struct osl_column_description cols[] = {\n"); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - FOR_EACH_COLUMN(i, desc, cd) { - const char *asc_st; - msg = make_message("\t[%d] = {\n", i); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - asc_st = get_asc_storage_type(cd->storage_type); - msg = make_message("\t\t.storage_type = %s,\n", asc_st); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - if (cd->storage_flags) { - char *asc_sf = get_asc_storage_flags(cd->storage_flags); - msg = make_message("\t\t,storage_flags = %s,\n", asc_sf); - free(asc_sf); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - } - if (cd->storage_flags & OSL_FIXED_SIZE) { - msg = make_message("\t\t.data_size = %u,\n", cd->data_size); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - } - msg = make_message("\t\t.name = \"%s\",\n", cd->name); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - if (cd->storage_flags & OSL_RBTREE) { - msg = make_message("\t\t.compare_function = compare_func,\n"); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - } - msg = make_message("\t},\n"); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - } - msg = make_message("};\n"); - ret = para_write_all(fd, msg, strlen(msg)); - if (ret < 0) - return ret; - free(msg); - return 1; -} - -static int dump_row(struct osl_table *t, unsigned row_num, const char *row_dir) -{ - int ret, i; - const struct osl_column_description *cd; - unsigned dsnc; - struct osl_object obj; - char *ds_name; - HASH_TYPE hash[HASH_SIZE]; - char *filename; - - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - ret = get_mapped_object(t, i, row_num, &obj); - if (ret < 0) - return ret; - filename = make_message("%s/col_%03u", row_dir, i); - ret = para_write_file(filename, obj.data, obj.size); - free(filename); - if (ret < 0) - return ret; - } - if (!t->num_disk_storage_columns) - return 1; - dsnc = t->disk_storage_name_column; - ret = get_mapped_object(t, dsnc, row_num, &obj); - if (ret < 0) - return ret; - hash_object(&obj, hash); - ds_name = disk_storage_name_of_hash(t, hash); - FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) { - filename = disk_storage_path(t, i, ds_name); - ret = mmap_full_file(filename, O_RDONLY, &obj.data, &obj.size, NULL); - free(filename); - if (ret < 0) - goto out; - filename = make_message("%s/col_%03u", row_dir, i); - ret = para_write_file(filename, obj.data, obj.size); - free(filename); - if (ret < 0) - goto out; - } - ret = 1; -out: - free(ds_name); - return ret; -} - -static int dump_rows(char *dump_dir, struct osl_table *t) -{ - unsigned i; - char *current_dir = NULL; - int ret = 0; - - for (i = 0; i < t->num_rows; i++) { - char *row_dir; - if (row_is_invalid(t, i)) - continue; - if (!(i % 1000)) { - free(current_dir); - current_dir = make_message("%s/rows_%u-%u", dump_dir, i, i + 999); - PARA_NOTICE_LOG("dumping rows %u - %u\n", i, i + 999); - ret = para_mkdir(current_dir, 0777); - if (ret < 0 && !is_errno(-ret, EEXIST)) - goto out; - } - row_dir = make_message("%s/row_%03u", current_dir, i); - ret = para_mkdir(row_dir, 0777); - if (ret < 0 && !is_errno(-ret, EEXIST)) { - free(row_dir); - goto out; - } - ret = dump_row(t, i, row_dir); - free(row_dir); - if (ret < 0) - goto out; - } -out: - free(current_dir); - return ret; -} - -static int dump_table(char *dump_dir, struct osl_table_description *desc) -{ - struct osl_table *t = NULL; - int fd, ret = fsck_init(desc, &t); - char *desc_file; - char *table_dump_dir = NULL; - - if (ret < 0) - goto out; - ret = para_mkdir(dump_dir, 0777); - if (ret < 0 && !is_errno(-ret, EEXIST)) - goto out; - table_dump_dir = make_message("%s/%s", dump_dir, desc->name); - ret = para_mkdir(table_dump_dir, 0777); - if (ret < 0 && !is_errno(-ret, EEXIST)) - goto out; - desc_file = make_message("%s/table_description.c", table_dump_dir); - ret = para_open(desc_file, O_WRONLY | O_CREAT | O_EXCL, 0644); - free(desc_file); - if (ret < 0) - goto out; - fd = ret; - ret = dump_table_desc(t, fd); - close(fd); - if (ret < 0) - goto out; - ret = dump_rows(table_dump_dir, t); -out: - free(table_dump_dir); - fsck_cleanup(t); - return ret; -} - -static int fsck(struct osl_table_description *desc) -{ - int ret; - struct osl_table *t = NULL; - uint32_t *lost_bytes = NULL; - - ret = fsck_init(desc, &t); - if (ret < 0) - goto out; - ret = check_index_ranges(t); - if (ret < 0) - goto out_unmap; - ret = check_disk_storage_columns(t); - if (ret < 0) - goto out_unmap; - ret = prune_invalid_rows_from_index(t); - if (ret < 0) - goto out_unmap; - ret = check_for_invalid_objects(t, &lost_bytes); - if (ret < 0) - goto out_unmap; - if (ret > 0) { /* at least one mapped data file needs pruning */ - ret = prune_objects(t, lost_bytes); - if (ret < 0) - goto out_unmap; - } - free(lost_bytes); -out_unmap: - unmap_table(t, OSL_MARK_CLEAN); -out: - fsck_cleanup(t); - return ret; -} - -static int check_table(char *base_dir, char *table_name) -{ - struct osl_table_description desc = { - .column_descriptions = NULL, - .dir = base_dir, - .name = table_name - }; - int ret; - - PARA_INFO_LOG("checking table %s\n", table_name); - if (!conf.no_fsck_given) { - ret = fsck(&desc); - if (ret < 0) - goto out; - } - ret = 1; - if (!conf.dump_dir_given || !*conf.dump_dir_arg) - goto out; - ret = dump_table(conf.dump_dir_arg, &desc); -out: - if (ret < 0) - PARA_ERROR_LOG("failed to check table %s\n", table_name); - else - PARA_NOTICE_LOG("successfully checked table %s\n", table_name); - return ret; -} - -static int check_all_tables(char *base_dir) -{ - DIR *dir; - struct dirent *entry; - int cwd_fd, ret2, ret = para_opendir(base_dir, &dir, &cwd_fd); - - if (ret < 0) - return ret; - while ((entry = readdir(dir))) { - mode_t m; - struct stat s; - if (!strcmp(entry->d_name, ".")) - continue; - if (!strcmp(entry->d_name, "..")) - continue; - if (lstat(entry->d_name, &s) == -1) - continue; - m = s.st_mode; - if (!S_ISDIR(m)) - continue; - ret = check_table(base_dir, entry->d_name); - if (ret < 0) - break; - } - closedir(dir); - ret2 = para_fchdir(cwd_fd); - if (ret2 < 0 && ret >= 0) - ret = ret2; - close(cwd_fd); - return ret; -} - -/** - * The praslash database check program. - * - * \param argc Usual arg count. - * \param argv Usual arg vector. - * - * \return \p EXIT_SUCCESS or \p EXIT_FAILURE. - */ -int main(int argc, char **argv) -{ - int i, ret; - char *base_dir = NULL; - - ret = fsck_cmdline_parser(argc, argv, &conf); - if (ret < 0) { - ret = -E_FSCK_SYNTAX; - goto out; - } - HANDLE_VERSION_FLAG("fsck", conf); - loglevel = get_loglevel_by_name(conf.loglevel_arg); - if (conf.base_dir_given) - base_dir = para_strdup(conf.base_dir_arg); - else { - char *home = para_homedir(); - base_dir = make_message("%s/.paraslash/afs_database", home); - free(home); - } - if (!conf.inputs_num) { - ret = check_all_tables(base_dir); - goto out; - } - for (i = 0; i < conf.inputs_num; i++) { - ret = check_table(base_dir, conf.inputs[i]); - if (ret < 0) - break; - } -out: - if (ret < 0) { - PARA_ERROR_LOG("%s%s: %s\n", - base_dir? "base_dir: " : "", - base_dir? base_dir : "", - para_strerror(-ret) - ); - } else - PARA_NOTICE_LOG("success\n"); - if (base_dir) - free(base_dir); - return ret < 0? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/gcc-compat.h b/gcc-compat.h index 61c3c88d..ffd714f7 100644 --- a/gcc-compat.h +++ b/gcc-compat.h @@ -1,12 +1,12 @@ -# define inline inline __attribute__ ((always_inline)) -# define __noreturn __attribute__ ((noreturn)) -# define __malloc __attribute__ ((malloc)) -# define __a_unused __attribute__ ((unused)) -# define likely(x) __builtin_expect (!!(x), 1) -# define unlikely(x) __builtin_expect (!!(x), 0) -/* - * p is the number of the "format string" parameter, and q is - * the number of the first variadic parameter +#define inline inline __attribute__ ((always_inline)) +#define __noreturn __attribute__ ((noreturn)) +#define __malloc __attribute__ ((malloc)) +#define __a_unused __attribute__ ((unused)) +#define __a_aligned(alignment) __attribute__((__aligned__(alignment))) + +/* + * p is the number of the "format string" parameter, and q is + * the number of the first variadic parameter. */ # define __printf(p,q) __attribute__ ((format (printf, p, q))) /* diff --git a/ggo.c b/ggo.c index b9852b99..a25c0809 100644 --- a/ggo.c +++ b/ggo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 Andre Noll + * Copyright (C) 2008-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/ggo.h b/ggo.h index 4d452b1d..cc0c96b3 100644 --- a/ggo.h +++ b/ggo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 Andre Noll + * Copyright (C) 2008-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/ggo/.gitignore b/ggo/.gitignore index a6d955c1..a0824cf8 100644 --- a/ggo/.gitignore +++ b/ggo/.gitignore @@ -8,4 +8,3 @@ gui.ggo recv.ggo server.ggo write.ggo - diff --git a/ggo/afh.m4 b/ggo/afh.m4 index 97b8f288..80df8948 100644 --- a/ggo/afh.m4 +++ b/ggo/afh.m4 @@ -62,6 +62,20 @@ option "chunk_table" c flag off dependon="info" +option "human" u +#~~~~~~~~~~~~~~~ +"use human-readable output format" +flag off +dependon = "info" +details = " + Currently this option only affects the format of the chunk table, + so it has no effect if --chunk_table is not given. + + The human-readable output consists of one output line per + chunk and the output contains also the chunk number, the + duration and the size of each chunk. +" + section "Options for stream mode" #================================ diff --git a/ggo/alsa_write.ggo b/ggo/alsa_write.ggo deleted file mode 100644 index 84f49d58..00000000 --- a/ggo/alsa_write.ggo +++ /dev/null @@ -1,34 +0,0 @@ -option "device" d -#~~~~~~~~~~~~~~~~ -"set PCM device" -string typestr="device" -default="default" -optional -details=" - On systems with dmix, a better choice than the default - value might be to use \"plug:swmix\". -" - -option "channels" c -#~~~~~~~~~~~~~~~~~~ -"specify number of channels" -int typestr="num" -default="2" -optional -details=" - This option is only necessary for playing raw audio with - para_write. In all other cases (plaing wav files with - para_write or using this writer with para_audiod), the number - of channels will be obtained from other resources. -" - -option "samplerate" s -#~~~~~~~~~~~~~~~~~~~~~ -"force given sample rate" -int typestr="num" -default="44100" -optional -details=" - Again, it is only necessary to specify this when playing raw - audio with para_write. -" diff --git a/ggo/alsa_write.m4 b/ggo/alsa_write.m4 new file mode 100644 index 00000000..afcefccf --- /dev/null +++ b/ggo/alsa_write.m4 @@ -0,0 +1,38 @@ +include(header.m4) + + +option "device" d +#~~~~~~~~~~~~~~~~ +"set PCM device" +string typestr="device" +default="default" +optional +details=" + On systems with dmix, a better choice than the default + value might be to use \"plug:swmix\". +" + +option "channels" c +#~~~~~~~~~~~~~~~~~~ +"specify number of channels" +int typestr="num" +default="2" +optional +details=" + This option is only necessary for playing raw audio with + para_write. In all other cases (plaing wav files with + para_write or using this writer with para_audiod), the number + of channels will be obtained from other resources. +" + +option "samplerate" s +#~~~~~~~~~~~~~~~~~~~~~ +"force given sample rate" +int typestr="num" +default="44100" +optional +details=" + Again, it is only necessary to specify this when playing raw + audio with para_write. +" + diff --git a/ggo/audiod.m4 b/ggo/audiod.m4 index 29bce100..7ff8806c 100644 --- a/ggo/audiod.m4 +++ b/ggo/audiod.m4 @@ -12,6 +12,7 @@ include(loglevel.m4) include(color.m4) include(config_file.m4) include(logfile.m4) +include(log_timing.m4) include(daemon.m4) include(user.m4) include(group.m4) diff --git a/ggo/client.m4 b/ggo/client.m4 index 1ae5a38a..5770ff0a 100644 --- a/ggo/client.m4 +++ b/ggo/client.m4 @@ -11,7 +11,3 @@ option "key_file" k "(default='~/.paraslash/key.')" string typestr="filena include(loglevel.m4) include(config_file.m4) - - -option "plain" - "request an uncrypted session" flag off - diff --git a/ggo/dccp_recv.ggo b/ggo/dccp_recv.ggo index 9717329d..80d31233 100644 --- a/ggo/dccp_recv.ggo +++ b/ggo/dccp_recv.ggo @@ -11,3 +11,27 @@ option "port" p int default="8000" optional + +option "ccid" c +"CCID preference(s) for this connection" +int +# restrict the maximum number of times this option can be passed +optional multiple(-10) +# currently known CCIDs: +# - CCID-2 (RFC 4341), +# - CCID-3 (RFC 4342), +# - CCID-4 (RFC 5622), +# - CCID-248 ... CCID-254 are experimental (RFC 4340, 19.5) +values="2", "3", "4", "248", "249", "250", "251", "252", "253", "254" +details=" + When present exactly once, this option mandates the CCID for the + sender-receiver connection. If it is passed more than once, it sets + a preference list where the order of appearance signifies descending + priority. For example, passing 4, 2, 3 creates the preference list + (CCID-4, CCID-2, CCID-3), assigning CCID-4 highest preference. + + The request is reconciled with the CCIDs on the server through the + 'server-priority' mechanism of RFC 4340 6.3.1/10. The server CCIDs + can be listed by calling 'para_client si'. + +" diff --git a/ggo/fade.ggo b/ggo/fade.ggo index 01eb6aa9..80f8b73c 100644 --- a/ggo/fade.ggo +++ b/ggo/fade.ggo @@ -1,190 +1,200 @@ -section "general options" +section "General options" ######################### option "mode" o #~~~~~~~~~~~~~~ -"{sleep|fade|snooze}" -string default="sleep" -optional -details=" - para_fade knows these three different modes. +"how to fade volume" + enum typestr = "mode" + values = "sleep", "snooze", "fade" + default = "sleep" + optional + details=" + para_fade knows three different fading modes: - sleep mode: Change to the initial volume and an initial afs - mode, then fade the volume down until the fade out volume is - reached. Switch to the afs sleep mode until the wake time is - reached. Then switch to the afs wake mode and and fade in to - the wake volume. + sleep mode: Change to the initial volume and select + the initial afs mood/playlist. Then fade out until + the fade-out volume is reached. Switch to the + sleep mood/playlist until wake time minus fade-in + time. Finally switch to the wake mood/playlist and + fade to the fade-in volume. - fade: Fade the volume to the given value in the given time. + fade: Fade the volume to the given value in the + given time. - snooze: Fade out, sleep a bit and fade in. + snooze: Fade out, sleep a bit and fade in. " -option "config_file" c +option "config-file" c #~~~~~~~~~~~~~~~~~~~~~ - "(default='~/.paraslash/fade.conf')" - string typestr="filename" +"(default='~/.paraslash/fade.conf')" + string typestr = "filename" optional -option "mixer_device" m +option "mixer-device" m #~~~~~~~~~~~~~~~~~~~~~~ - "mixer device file" - string typestr="device" - default="/dev/mixer" +"mixer device file" + string typestr = "device" + default = "/dev/mixer" optional -option "mixer_channel" C +option "mixer-channel" C #~~~~~~~~~~~~~~~~~~~~~~~ "select the analog mixer channel" - enum typestr="channel" - values="volume", "bass", "treble", "synth", "pcm", "speaker", "line", + enum typestr = "channel" + values = "volume", "bass", "treble", "synth", "pcm", "speaker", "line", "mic", "cd", "imix", "altpcm", "reclev", "igain", "ogain" - default="volume" + default = "volume" optional - details=" - Not all listed channels might be supported on any particular hardware. + details = " + Not all listed channels might be supported on any + particular hardware. " -section "sleep options (only relevant in sleep mode)" -##################################################### +section "Options for sleep mode" +################################ -option "sleep_ivol" - -#~~~~~~~~~~~~~~~~~~~~ +option "ivol" - +#~~~~~~~~~~~~~~ "set initial volume" -int typestr="volume" -default="60" -optional -details=" - Used as the start volume, before fading out to the fade out volume. -" + int typestr = "volume" + default = "60" + optional + details = " + Used as the start volume, before fading out to the + fade out volume. + " -option "fa_mode" - +option "fo-mood" - #~~~~~~~~~~~~~~~~~ -"fall asleep afs mode." -string typestr="afs_mode" -default="m/fade" -optional -details=" - Select this mode right after setting the volume. Example: - --fa_mode m/sleep +"afs mood/playlist during fade out" + string typestr = "mood_spec" + default = "m/fade" + optional + details = " + Select this mood right after setting the + volume. Example: --fo-mood m/sleep " -option "fa_fade" - +option "fo-time" - #~~~~~~~~~~~~~~~~~ - "fall asleep fading time" -int typestr="seconds" -default="1800" -optional -details=" - No fading if set to 0. -" +"fall asleep fade out time" + int typestr = "seconds" + default = "1800" + optional + details = " + No fading if set to 0. + " -option "fa_vol" - +option "fo-vol" - #~~~~~~~~~~~~~~~~ - "volume to fade to" - int typestr="volume" - default="20" +"volume to fade out to" + int typestr = "volume" + default = "20" optional -option "sleep_mode" - -#~~~~~~~~~~~~~~~~~~~~~~ -"sleep time afs mode" -details = " - Select the given afs mode after the fade out is complete. If - unset, the \"stop\" command is sent to para_server. -" -string typestr="afs_mode" -default="m/sleep" -optional +option "sleep-mood" - +#~~~~~~~~~~~~~~~~~~~~ +"sleep time afs mood/playlist" + string typestr = "mood_spec" + default = "m/sleep" + optional + details = " + Select the given afs mood/playlist after the fade + out is complete. If unset, the \"stop\" command is + sent to para_server. + " -option "wake_hour" H +option "wake-hour" H #~~~~~~~~~~~~~~~~~~~ - "(0-23) (default: now + 9 hours)" - int typestr="hour" +"(0-23) (default: now + 9 hours)" + int typestr = "hour" optional -option "wake_min" M +option "wake-min" M #~~~~~~~~~~~~~~~~~~ "(0-59)" - int typestr="minutes" - default="0" + int typestr = "minutes" + default = "0" optional -option "wake_mode" - -#~~~~~~~~~~~~~~~~~~~ -"wake time afs mode" -string typestr="afs_mode" -default="m/wake" -optional -details=" - Change to this afs mode on waketime. -" +option "fi-mood" - +#~~~~~~~~~~~~~~~~~ +"afs mood/playlist during fade in" + string typestr = "mood_spec" + default = "m/wake" + optional + details = " + Change to this afs mood/playlist on wake time. + " -option "wake_fade" - -#~~~~~~~~~~~~~~~~~~~ - "no fading in if set to 0" +option "fi-time" - +#~~~~~~~~~~~~~~~~~ +"wake up fade in time" int typestr="seconds" default="1200" optional + details = " + No fading in if set to 0. + " -option "wake_vol" - -#~~~~~~~~~~~~~~~~~~ - "vol to fade to at waketime" - int typestr="volume" - default="80" +option "fi-vol" - +#~~~~~~~~~~~~~~~~ +"volume to fade to at wake time" + int typestr = "volume" + default = "80" optional -section "snooze options" -######################## +section "Options for snooze mode" +################################# -option "snooze_out_fade" - -#~~~~~~~~~~~~~~~~~~~~~~~~~ - "fade out time" - int typestr="seconds" - default="30" +option "so-time" - +#~~~~~~~~~~~~~~~~~ +"snooze-out time" + int typestr = "seconds" + default = "30" optional -option "snooze_out_vol" - -#~~~~~~~~~~~~~~~~~~~~~~~~ - "vol to fade to before snooze" - int typestr="volume" - default="20" +option "so-vol" - +#~~~~~~~~~~~~~~~~ +"volume to fade to before snooze" + int typestr = "volume" + default = "20" optional -option "snooze_time" - +option "snooze-time" - #~~~~~~~~~~~~~~~~~~~~~ - "delay" - int typestr="seconds" - default="600" +"delay" + int typestr = "seconds" + default = "600" optional -option "snooze_in_fade" - -#~~~~~~~~~~~~~~~~~~~~~~~~ - "fade in time" - int typestr="seconds" - default="180" +option "si-time" - +#~~~~~~~~~~~~~~~~~ +"snooze-in time" + int typestr = "seconds" + default = "180" optional -option "snooze_in_vol" - -#~~~~~~~~~~~~~~~~~~~~~~~ - "vol to fade to after snooze" - int typestr="volume" - default="80" +option "si-vol" - +#~~~~~~~~~~~~~~~~ +"volume to fade to after snooze" + int typestr = "volume" + default = "80" optional -section "fade options" -###################### +section "Options for fade mode" +############################### -option "fade_vol" f +option "fade-vol" f #~~~~~~~~~~~~~~~~~~ - "volume to fade to" - int typestr="volume" - default="50" +"volume to fade to" + int typestr = "volume" + default = "50" optional -option "fade_time" t +option "fade-time" t #~~~~~~~~~~~~~~~~~~~ - "time to fade in" - int typestr="seconds" - default="5" +"fading time" + int typestr = "seconds" + default = "5" optional diff --git a/ggo/grab_client.ggo b/ggo/grab_client.ggo deleted file mode 100644 index 5527143b..00000000 --- a/ggo/grab_client.ggo +++ /dev/null @@ -1,33 +0,0 @@ -option "filter_num" f - "point of filter chain to grab" - int typestr="num" - default="0" - optional - -option "slot" s - "only grab this slot; grab any slot if negative" - int typestr="num" - default="-1" - optional - -option "audio_format" a - "only grab this type of input stream; - grab any if empty" - string typestr="name" - default="" - optional - -option "input_grab" i - "grab the filter input instead of its output" - flag off - -option "one_shot" o - "stop grabbing if audio file changes" - flag off - -option "mode" m - "select grab mode" - typestr="grab_mode" - values="sloppy","aggressive","pedantic" - default="sloppy" - optional diff --git a/ggo/gui.m4 b/ggo/gui.m4 index dd1e2496..3f31dc1a 100644 --- a/ggo/gui.m4 +++ b/ggo/gui.m4 @@ -25,7 +25,7 @@ option "stat_cmd" s data from" string typestr="command" - default="para_audioc stat" + default="para_audioc -- stat -p" optional #--------------------------------- diff --git a/ggo/http_recv.ggo b/ggo/http_recv.ggo deleted file mode 100644 index 2f1b4d61..00000000 --- a/ggo/http_recv.ggo +++ /dev/null @@ -1,15 +0,0 @@ -option "host" i -#~~~~~~~~~~~~~~ -"ip or host" -string -default="localhost" -optional -details=" - Both IPv4 and IPv6 addresses are supported. -" - -option "port" p -#~~~~~~~~~~~~~~ -"tcp port to connect to" -int default="8000" -optional diff --git a/ggo/http_recv.m4 b/ggo/http_recv.m4 new file mode 100644 index 00000000..14ae99ab --- /dev/null +++ b/ggo/http_recv.m4 @@ -0,0 +1,19 @@ +include(header.m4) + + +option "host" i +#~~~~~~~~~~~~~~ +"ip or host" +string +default="localhost" +optional +details=" + Both IPv4 and IPv6 addresses are supported. +" + +option "port" p +#~~~~~~~~~~~~~~ +"tcp port to connect to" +int default="8000" +optional + diff --git a/ggo/log_timing.m4 b/ggo/log_timing.m4 new file mode 100644 index 00000000..ac0ea841 --- /dev/null +++ b/ggo/log_timing.m4 @@ -0,0 +1,12 @@ + +option "log-timing" T +#~~~~~~~~~~~~~~~~~~~~ +"show milliseconds in log messages" +flag off +details = " + Selecting this option causes milliseconds to be included in + the log message output. This allows to measure the interval + between log messages in milliseconds which is useful for + identifying timing problems. +" + diff --git a/ggo/makefile b/ggo/makefile index a7715208..d4f3a8cd 100644 --- a/ggo/makefile +++ b/ggo/makefile @@ -1,31 +1,28 @@ module_ggo_opts := --set-version="($(PACKAGE_STRING), $(codename))" +$(cmdline_dir): + mkdir -p $(cmdline_dir) -grab_client.cmdline.h grab_client.cmdline.c: $(ggo_dir)/grab_client.ggo - gengetopt $(module_ggo_opts) \ - -S \ - --set-package=grab \ - --no-handle-help \ - --no-handle-error \ - --no-handle-version \ - --arg-struct-name=grab_client_args_info \ - --file-name=$(subst .ggo,,$( $(@F) + @[ -z "$(Q)" ] || echo 'M4 $<' + $(Q) cd $(ggo_dir); m4 $( $(@F) diff --git a/ggo/mp3dec_filter.ggo b/ggo/mp3dec_filter.ggo deleted file mode 100644 index a34c30ae..00000000 --- a/ggo/mp3dec_filter.ggo +++ /dev/null @@ -1,22 +0,0 @@ -option "bufsize" b -#~~~~~~~~~~~~~~~~~ -"size of output buffer" -int typestr="kilobyte" -default="128" -optional -details=" - Increase this if you encounter output buffer overrun - errors. Smaller values make the mp3dec filter use less - memory. The minimal size is 32K. -" - -option "ignore-crc" i -#~~~~~~~~~~~~~~~~~~~~ -"ignore CRC information in the audio stream." -flag off -details=" - This causes frames with CRC errors to be decoded and played - anyway. This option is not recommended, but since some encoders - have been known to generate bad CRC information, this option - is a work-around to play streams from such encoders. -" diff --git a/ggo/mp3dec_filter.m4 b/ggo/mp3dec_filter.m4 new file mode 100644 index 00000000..c02f1583 --- /dev/null +++ b/ggo/mp3dec_filter.m4 @@ -0,0 +1,14 @@ +include(header.m4) + + +option "ignore-crc" i +#~~~~~~~~~~~~~~~~~~~~ +"ignore CRC information in the audio stream." +flag off +details=" + This causes frames with CRC errors to be decoded and played + anyway. This option is not recommended, but since some encoders + have been known to generate bad CRC information, this option + is a work-around to play streams from such encoders. +" + diff --git a/ggo/osx_write.ggo b/ggo/osx_write.ggo index ce2792e5..c73ad2a8 100644 --- a/ggo/osx_write.ggo +++ b/ggo/osx_write.ggo @@ -3,8 +3,11 @@ section "osx options" option "channels" c #~~~~~~~~~~~~~~~~~~ -"number of channels (only neccessary for raw -audio)" +"channel count" + +details = " + Specify the number of channels. This is only neccessary for raw audio. +" int typestr="num" default="2" diff --git a/ggo/server.m4 b/ggo/server.m4 index f2738d37..4be4e2c9 100644 --- a/ggo/server.m4 +++ b/ggo/server.m4 @@ -9,6 +9,7 @@ section "General options" include(loglevel.m4) +include(log_timing.m4) include(color.m4) include(daemon.m4) include(user.m4) @@ -104,7 +105,7 @@ option "afs_socket" s #~~~~~~~~~~~~~~~~~~~~ "Command socket for afs" string typestr="path" -default="/var/paraslash/afs_command_socket" +default="/var/paraslash/afs_command_socket-0.4" optional details=" For each server command that is handled by the audio file diff --git a/ggo/write.m4 b/ggo/write.m4 index 41e44dd7..d954316a 100644 --- a/ggo/write.m4 +++ b/ggo/write.m4 @@ -1,13 +1,6 @@ include(header.m4) include(loglevel.m4) -option "bufsize" b -#~~~~~~~~~~~~~~~~~ -"input buffer size" -int typestr="kilobytes" -default="64" -optional - option "writer" w #~~~~~~~~~~~~~~~~ "select stream writer" @@ -19,13 +12,3 @@ details=" May be give multiple times. The same writer may be specified more than once. " - -option "start_time" t -#~~~~~~~~~~~~~~~~~~~~ -"defer playback" -string typestr="timeval" -optional -details=" - Start playback at given time which must be in a:b format where - a denotes seconds and b denotes microseconds since the epoch. -" diff --git a/grab_client.c b/grab_client.c index f3a0f484..9b475316 100644 --- a/grab_client.c +++ b/grab_client.c @@ -1,270 +1,298 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ -/** - * \file grab_client.c Functions for grabbing the stream at any position - * in a filter chain. - * - * \sa filter_chain filter_chain_info filter. - */ +/** \file grab_client.c Functions for grabbing the audio stream. */ +#include #include #include +#include #include "para.h" -#include "grab_client.cmdline.h" #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "grab_client.h" #include "audiod.h" #include "error.h" #include "string.h" #include "fd.h" -#include "crypt.h" -/** Grab clients that are not yet attached to a filter node. */ -struct list_head inactive_grab_client_list; +/** + * How to handle blocking writes for the grab client fds. + */ +enum grab_mode { + /** Ignore the data and do not write. */ + GM_SLOPPY, + /** Write anyway (default). */ + GM_AGGRESSIVE, + /** Close fd if write would block. */ + GM_PEDANTIC, +}; -static int max_num_filters(void) -{ - int i, ret = 0; - for (i = 0; audio_formats[i]; i++) { - PARA_INFO_LOG("%s filter chain length: %d\n", audio_formats[i], - num_filters(i)); - ret = PARA_MAX(ret, num_filters(i)); - } - PARA_INFO_LOG("maximal filter chain length: %d\n", ret); - return ret; -} +/** Flags specified as arguments to the grab command. */ +enum grab_flags { + /** Stop grabbing if audio file changes. */ + GF_ONE_SHOT = 1, +}; -static int gc_write(char *buf, size_t len, struct filter_callback *fcb) -{ - struct grab_client *gc = fcb->data; - struct timeval tv = {0, 100}; - int ret; +/** Describes one active grab client. */ +struct grab_client { + /* The value of the -p option. */ + char *parent; + /* The value of the -n option. */ + char *name; + /** The file descriptor to send the grabbed stream to. */ + int fd; + /** See \ref grab_mode. */ + enum grab_mode mode; + /** Flags given at the command line. */ + enum grab_flags flags; + /** The point of the grab client's node in the buffer tree. */ + struct btr_node *btrn; + /* The task of this grab client. */ + struct task task; + /** Belongs to either the active or the inactive list. */ + struct list_head node; +}; -// PARA_INFO_LOG("writing %d bytes to fd %d\n", len, gc->fd); - fd_set wfds; - FD_ZERO(&wfds); - FD_SET(gc->fd, &wfds); - ret = para_select(gc->fd + 1, NULL, &wfds, &tv); - if (ret <= 0) { - if (gc->mode == GRAB_PEDANTIC) - return -E_PEDANTIC_GRAB; - if (gc->mode == GRAB_SLOPPY) - return 1; - } -rewrite: - ret = write(gc->fd, buf, len); - if (ret < 0) { - ret = -E_GC_WRITE; - gc->error = E_GC_WRITE; - } else { - if (ret != len) { - if (gc->mode == GRAB_PEDANTIC) - return -E_PEDANTIC_GRAB; - if (gc->mode == GRAB_AGGRESSIVE) { - len -= ret; - memmove(buf, buf + ret, len); - goto rewrite; - } - } - } - return ret; -} +/* Grab clients that are attached to a btr node. */ +INITIALIZED_LIST_HEAD(active_grab_client_list); +/* Grab clients that are not currently attached any btr node. */ +INITIALIZED_LIST_HEAD(inactive_grab_client_list); -/* TODO: gengetopt can handle the grab client modes */ -static int check_gc_args(struct grab_client *gc) +static int gc_write(struct grab_client *gc, char *buf, size_t len) { - int i; - struct grab_client_args_info *c = gc->conf; - const char **mv = grab_client_cmdline_parser_mode_values; + int ret = write_ok(gc->fd); - PARA_INFO_LOG("filter_num: %d\n", c->filter_num_arg); - for (i = 0; mv[i]; i++) - if (!strcmp(c->mode_arg, mv[i])) - break; - if (!mv[i]) - return -E_GC_SYNTAX; - gc->mode = i; - gc->audio_format_num = -1; - if (c->audio_format_given) { - gc->audio_format_num = get_audio_format_num(c->audio_format_arg); - if (gc->audio_format_num < 0) - return gc->audio_format_num; + if (ret < 0) + goto err; + if (ret == 0) { /* fd not ready */ + if (gc->mode == GM_PEDANTIC) + goto err; + if (gc->mode == GM_SLOPPY) + return len; } - if (c->slot_arg >= MAX_STREAM_SLOTS) - return -E_BAD_GC_SLOT; - if (c->filter_num_arg < 0) - return -E_BAD_GC_FILTER_NUM; - if (c->audio_format_given) { - if (num_filters(gc->audio_format_num) <= c->filter_num_arg) - return -E_BAD_GC_FILTER_NUM; - } else - if (c->filter_num_arg >= max_num_filters()) - return -E_BAD_GC_FILTER_NUM; - - return 1; -} - -static void add_inactive_gc(struct grab_client *gc) -{ - PARA_INFO_LOG("adding grab client %p (fd %d) to inactive list\n", - gc, gc->fd); - para_list_add(&gc->node, &inactive_grab_client_list); -} - -static void gc_free(struct grab_client *gc) -{ - int i; - - for (i = 0; i < gc->argc; i++) - free(gc->argv[i]); - free(gc->argv); - free(gc->conf); - free(gc); - + ret = write_nonblock(gc->fd, buf, len, 0); + if (ret < 0) + goto err; + if (ret > 0) + return ret; + if (ret == 0) { + if (gc->mode == GM_PEDANTIC) + goto err; + if (gc->mode == GM_SLOPPY) + return len; + } + return 0; +err: + return -E_GC_WRITE; } -static void gc_close(struct filter_callback *fcb) +static void gc_pre_select(struct sched *s, struct task *t) { - struct grab_client *gc = fcb->data; + struct grab_client *gc = container_of(t, struct grab_client, task); + int ret = btr_node_status(gc->btrn, 0, BTR_NT_LEAF); - if (gc->conf->one_shot_given || gc->error) { - PARA_INFO_LOG("closing fd %d (grab client %p)\n", gc->fd, gc); - close(gc->fd); - gc_free(gc); - /* close on fork ?*/ + if (ret == 0) + return; + if (ret < 0) { + s->timeout.tv_sec = 0; + s->timeout.tv_usec = 0; return; } - add_inactive_gc(gc); + para_fd_set(gc->fd, &s->wfds, &s->max_fileno); } +/* + * We need this forward declaration as post_select() needs + * activate_grab_client and vice versa. + */ +static void gc_post_select(struct sched *s, struct task *t); + /** - * Move a grab client from the inactive list to a filter node. + * Move a grab client to the active list and start it. * * \param gc The grab client to activate. - * \param fn The filter node \a gc gets attached to. - * - * \sa filter_node::callbacks, inactive_grab_client_list. */ -void activate_grab_client(struct grab_client *gc, struct filter_node *fn) +static void gc_activate(struct grab_client *gc) { - PARA_INFO_LOG("activating %p (fd %d, filter node: %p)\n", gc, gc->fd, fn); - list_del(&gc->node); - para_list_add(&gc->fcb.node, &fn->callbacks); + struct btr_node *root = audiod_get_btr_root(), *parent; + char *name = gc->name? gc->name : "grab"; + + if (!root) + return; + parent = btr_search_node(gc->parent, root); + if (!parent) + return; + PARA_INFO_LOG("activating fd %d\n", gc->fd); + list_move(&gc->node, &active_grab_client_list); + gc->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = name, .parent = parent)); + if (!gc->task.pre_select) { + gc->task.pre_select = gc_pre_select; + gc->task.post_select = gc_post_select; + snprintf(gc->task.status, sizeof(gc->task.status) - 1, "%s", name); + gc->task.status[sizeof(gc->task.status) - 1] = '\0'; + register_task(&gc->task); + } } /** * Activate inactive grab clients if possible. * - * \param slot_num Audiod's slot for the new audio file. - * \param audio_format_num The number of the audio format of the new audio file. - * \param fc The filter chain containing the activated filters. - * * This is called from audiod.c when the current audio file changes. It loops * over all inactive grab clients and checks each grab client's configuration * to determine if the client in question wishes to grab the new stream. If - * yes, this grab client is moved from the inactive grab client list to an - * appropriate filter_node. + * yes, this grab client is moved from the inactive to the active grab client list. * - * \sa filter_chain_info::filters, inactive_grab_client_list, - * activate_grab_client. + * This function also garbage collects all grab clients whose tasks have been + * unscheduled. */ -void activate_inactive_grab_clients(int slot_num, int audio_format_num, - struct filter_chain *fc) +void activate_grab_clients(void) { struct grab_client *gc, *tmp; - int filter_num; - struct filter_node *fn; list_for_each_entry_safe(gc, tmp, &inactive_grab_client_list, node) { -// PARA_INFO_LOG("checking inactive grab client %p\n", gc); - if (gc->conf->slot_arg >= 0 && gc->conf->slot_arg != slot_num) + if (gc->task.error == -E_TASK_UNREGISTERED) { + list_del(&gc->node); + free(gc); continue; - if (gc->audio_format_num >= 0 && gc->audio_format_num != - audio_format_num) + } + gc_activate(gc); + } +} + +static int gc_close(struct grab_client *gc, int err) +{ + btr_remove_node(gc->btrn); + btr_free_node(gc->btrn); + gc->btrn = NULL; + PARA_INFO_LOG("closing gc: %s\n", para_strerror(-err)); + list_move(&gc->node, &inactive_grab_client_list); + if (err == -E_GC_WRITE || (gc->flags & GF_ONE_SHOT)) { + /* + * We must not free the gc structure here as it contains ->task + * which is still used because this function is called from + * post_select(). + */ + close(gc->fd); + free(gc->parent); + free(gc->name); + return 1; + } + gc_activate(gc); + return 0; +} + +static void gc_post_select(__a_unused struct sched *s, struct task *t) +{ + struct grab_client *gc = container_of(t, struct grab_client, task); + struct btr_node *btrn = gc->btrn; + int ret; + size_t sz; + char *buf; + + t->error = 0; + ret = btr_node_status(btrn, 0, BTR_NT_LEAF); + if (ret == 0) + return; + if (ret < 0) + goto err; + sz = btr_next_buffer(btrn, &buf); + assert(sz != 0); + ret = gc_write(gc, buf, sz); + if (ret < 0) + goto err; + if (ret > 0) + btr_consume(btrn, ret); + return; +err: + t->error = gc_close(gc, ret)? ret : 0; +} + +static int gc_check_args(int argc, char **argv, struct grab_client *gc) +{ + int i; + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (arg[0] != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strncmp(arg, "-m", 2)) { + if (*(arg + 3)) + return -E_GC_SYNTAX; + switch(*(arg + 2)) { + case 's': + gc->mode = GM_SLOPPY; + continue; + case 'a': + gc->mode = GM_AGGRESSIVE; + continue; + case 'p': + gc->mode = GM_PEDANTIC; + continue; + default: + return -E_GC_SYNTAX; + } + } + if (!strcmp(arg, "-o")) { + gc->flags |= GF_ONE_SHOT; continue; - filter_num = gc->conf->filter_num_arg; - if (filter_num >= num_filters(gc->audio_format_num)) + } + if (!strncmp(arg, "-p=", 3)) { + gc->parent = para_strdup(arg + 3); + continue; + } + if (!strncmp(arg, "-n=", 3)) { + gc->name = para_strdup(arg + 3); continue; - fn = fc->filter_nodes + filter_num; - activate_grab_client(gc, fn); + } + return -E_GC_SYNTAX; } + if (i != argc) + return -E_GC_SYNTAX; + return 1; } /** * Check the command line options and allocate a grab_client structure. * * \param fd The file descriptor of the client. - * \param line The command line. - * \param err Non-zero if an error occurred. + * \param argc Argument count. + * \param argv Argument vector. * * If the command line options given by \a argc and \a argv are valid. * allocate a struct grab_client and initialize it with this valid - * configuration. Moreover, add the new grab client to the inactive list. + * configuration. * - * \return On success, this function returns a pointer to the newly created - * struct. On errors, it returns NULL and sets \a err appropriately. + * If the new grab client can be added to an existing buffer tree, activate it. + * Otherwise, add it to the inactive list for later activation. * - * \sa grab_client, inactive_grab_client_list, activate_grab_client, - * filter_node::callbacks. - */ -/* - * argc, argv get freed when com_grab() returns, so we have to make a - * copy. + * \return Standard. */ -struct grab_client *grab_client_new(int fd, char *line, int *err) +int grab_client_new(int fd, int argc, char **argv) { int ret; struct grab_client *gc = para_calloc(sizeof(struct grab_client)); - gc->conf = para_calloc(sizeof(struct grab_client_args_info)); - - ret = grab_client_cmdline_parser_string(line, gc->conf, "grab"); - *err = -E_GC_SYNTAX; - if (ret) - goto err_out; - *err = -E_GC_HELP_GIVEN; - if (gc->conf->help_given) - goto err_out; - *err = -E_GC_VERSION_GIVEN; - if (gc->conf->version_given) + ret = gc_check_args(argc, argv, gc); + if (ret < 0) goto err_out; - *err = check_gc_args(gc); - if (*err < 0) - goto err_out; - if (gc->conf->input_grab_given) { - gc->fcb.input_cb = gc_write; - gc->fcb.output_cb = NULL; - } else { - gc->fcb.output_cb = gc_write; - gc->fcb.input_cb = NULL; - } gc->fd = fd; - gc->fcb.close = gc_close; - gc->fcb.data = gc; - add_inactive_gc(gc); - return gc; + para_list_add(&gc->node, &inactive_grab_client_list); + gc_activate(gc); + return 1; err_out: - free(gc->conf); free(gc); - return NULL; -} - -/** - * Initialize the grabbing subsystem. - * - * This has to be called once during startup before any other function from - * grab_client.c may be used. It initializes \a inactive_grab_client_list. - */ -void init_grabbing(void) -{ - PARA_INFO_LOG("grab init\n"); - INIT_LIST_HEAD(&inactive_grab_client_list); + return ret; } diff --git a/grab_client.h b/grab_client.h index 2832f093..75ba4c91 100644 --- a/grab_client.h +++ b/grab_client.h @@ -6,44 +6,5 @@ /** \file grab_client.h exported symbols from grab_client.c */ -#include "config.h" -/** - * handle blocking writes for the grab client fds - * - * - pedantic: close fd if write would block - * - sloppy: ignore the data and do not write - * - aggressive: write anyway (default) - * - */ -enum grab_mode {GRAB_SLOPPY, GRAB_AGGRESSIVE, GRAB_PEDANTIC}; - -/** describes one active grab client - * - * \sa filter_callback, filter_node::callbacks - */ -struct grab_client { -/** the file descriptor to send the grabbed stream to */ - int fd; -/** the command line options for this grab client */ - struct grab_client_args_info *conf; -/** pedantic, sloppy, or aggressive, computed from command line */ - enum grab_mode mode; -/** non-zero if the write() to \a fd failed */ - int error; -/** the number of the desired audio format, computed from command line */ - int audio_format_num; -/** the callback data which gets attached to a suitable filter_node */ - struct filter_callback fcb; -/** all grab clients belong either to a filter node or to the inactive list */ - struct list_head node; -/** the number of command line options */ - int argc; -/** pointers to the command line options */ - char **argv; -}; - -__malloc struct grab_client *grab_client_new(int fd, char *line, int *err); -void activate_inactive_grab_clients(int slot_num, int audio_format_num, - struct filter_chain *fc); -void activate_grab_client(struct grab_client *gc, struct filter_node *fn); -void init_grabbing(void); +int grab_client_new(int fd, int argc, char **argv); +void activate_grab_clients(void); diff --git a/gui.c b/gui.c index fc2cd9a1..45b477fb 100644 --- a/gui.c +++ b/gui.c @@ -1,19 +1,21 @@ /* - * Copyright (C) 1998-2009 Andre Noll + * Copyright (C) 1998-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file gui.c Curses-based interface for paraslash. */ +#include #include #include #include +#include + #include "gui.cmdline.h" #include "para.h" #include "gui.h" #include "string.h" -#include #include "ringbuffer.h" #include "fd.h" #include "error.h" @@ -43,7 +45,7 @@ struct rb_entry { size_t len; int color; }; -struct ringbuffer *bot_win_rb; +static struct ringbuffer *bot_win_rb; #define NUM_LINES(len) (1 + (len) / bot.cols) static unsigned scroll_position; @@ -89,8 +91,8 @@ struct stat_item { static struct gui_theme theme; -int _argc; -char **_argv; +static int _argc; +static char **_argv; static void com_help(void); static void com_reread_conf(void); @@ -108,7 +110,7 @@ static void com_scroll_down(void); static void com_page_up(void); static void com_page_down(void); -struct gui_command command_list[] = { +static struct gui_command command_list[] = { { .key = "?", .name = "help", @@ -203,24 +205,6 @@ static int para_open_audiod_pipe(char *cmd) return ret; } -static int read_audiod_pipe(int fd, line_handler_t *line_handler) -{ - static char buf[4096]; - const ssize_t bufsize = sizeof(buf) - 1; - static ssize_t loaded; - ssize_t ret; - - if (loaded >= bufsize) - loaded = 0; - ret = read(fd, buf + loaded, bufsize - loaded); - if (ret > 0) { - loaded += ret; - buf[loaded] = '\0'; - loaded = for_each_line(buf, loaded, line_handler, NULL); - } - return ret; -} - static int find_cmd_byname(char *name) { int i; @@ -312,20 +296,23 @@ static void add_spaces(WINDOW* win, unsigned int num) * print aligned string to curses window. This function always prints * exactly len chars. */ -static int align_str(WINDOW* win, const char *string, unsigned int len, +static int align_str(WINDOW* win, char *str, unsigned int len, unsigned int align) { - int num; /* of spaces */ - char *str; + int i, num; /* of spaces */ - if (!win || !string) + if (!win || !str) return -1; - num = len - strlen(string); - str = para_strdup(string); + num = len - strlen(str); if (num < 0) { str[len] = '\0'; num = 0; } + /* replace newlines by spaces */ + for (i = 0; i < len && str[i]; i++) { + if (str[i] == '\n') + str[i] = ' '; + } if (align == LEFT) { waddstr(win, str); add_spaces(win, num); @@ -337,7 +324,6 @@ static int align_str(WINDOW* win, const char *string, unsigned int len, waddstr(win, str[0]? str: ""); add_spaces(win, num - num / 2); } - free(str); return 1; } @@ -360,10 +346,14 @@ __printf_2_3 static void print_in_bar(int color, const char *fmt,...) */ static void print_status_bar(void) { + char *tmp; + if (!curses_active) return; + tmp = para_strdup(STANDARD_STATUS_BAR); wmove(sb.win, 0, 0); - align_str(sb.win, STANDARD_STATUS_BAR, sb.cols, CENTER); + align_str(sb.win, tmp, sb.cols, CENTER); + free(tmp); wrefresh(sb.win); } @@ -687,6 +677,63 @@ static void print_stat_item(int i) wrefresh(top.win); } +static int update_item(int item_num, char *buf) +{ + char **c = stat_content + item_num; + + free(*c); + if (buf && buf[0]) + goto dup; + switch (item_num) { + case SI_ARTIST: + *c = para_strdup("(artist tag not set)"); + goto print; + case SI_TITLE: + *c = para_strdup("(title tag not set)"); + goto print; + case SI_YEAR: + *c = para_strdup("????"); + goto print; + case SI_ALBUM: + *c = para_strdup("(album tag not set)"); + goto print; + case SI_COMMENT: + *c = para_strdup("(comment tag not set)"); + goto print; + } +dup: + *c = para_strdup(buf); +print: + print_stat_item(item_num); + return 1; +} + +static int read_audiod_pipe(int fd) +{ + static char *buf; + static int bufsize, loaded; + int ret; + + if (loaded >= bufsize) { + if (bufsize > 1000 * 1000) { + loaded = 0; + return 0; + } + bufsize += bufsize + 1000; + buf = para_realloc(buf, bufsize); + } + assert(loaded < bufsize); + ret = read(fd, buf + loaded, bufsize - loaded); + if (ret <= 0) + return ret; + loaded += ret; + ret = for_each_stat_item(buf, loaded, update_item); + if (ret < 0) + return ret; + loaded = ret; + return 1; +} + static void print_all_items(void) { int i; @@ -769,26 +816,6 @@ reap_next_child: goto reap_next_child; } -/* - * print status line if line starts with known command. - */ -static int check_stat_line(char *line, __a_unused void *data) -{ - int i; - -// PARA_INFO_LOG("%s: checking: %s\n", __func__, line); - i = stat_line_valid(line); - if (i >= 0) { - line += strlen(status_item_list[i]) + 1; - if (*line == ' ') - line++; - free(stat_content[i]); - stat_content[i] = para_strdup(line); - print_stat_item(i); - } - return 1; -} - /* * This sucker modifies its first argument. *handler and *arg are * pointers to 0-terminated strings (inside line). Crap. @@ -859,8 +886,8 @@ static void handle_signal(int sig) return; case SIGINT: PARA_WARNING_LOG("caught SIGINT, reset"); - /* Nothing to do. SIGINT killed our child, para_client stat. - * This get noticed by do_select which resets everything + /* Nothing to do. SIGINT killed our child which gets noticed + * by do_select and resets everything. */ return; case SIGUSR1: @@ -946,7 +973,7 @@ repeat: } } if (audiod_pipe >= 0 && FD_ISSET(audiod_pipe, &rfds)) - if (read_audiod_pipe(audiod_pipe, check_stat_line) <= 0) { + if (read_audiod_pipe(audiod_pipe) <= 0) { close(audiod_pipe); audiod_pipe = -1; clear_all_items(); @@ -1321,31 +1348,29 @@ static void handle_command(int c) /* first check user's key bindings */ for (i = 0; i < conf.key_map_given; ++i) { - char tmp[MAXLINE], *handler, *arg; + char *tmp, *handler, *arg; - strcpy(tmp, conf.key_map_arg[i]); - if (!split_key_map(tmp, &handler, &arg)) + tmp = para_strdup(conf.key_map_arg[i]); + if (!split_key_map(tmp, &handler, &arg)) { + free(tmp); return; - if (!strcmp(tmp, km_keyname(c))) { - if (*handler == 'd') { - display_cmd(arg); - return; - } - if (*handler == 'x') { - external_cmd(arg); - return; - } - if (*handler == 'p') { - client_cmd_cmdline(arg); - return; - } - if (*handler == 'i') { - int num = find_cmd_byname(arg); - if (num >= 0) - command_list[num].handler(); - return; - } } + if (strcmp(tmp, km_keyname(c))) { + free(tmp); + continue; + } + if (*handler == 'd') + display_cmd(arg); + else if (*handler == 'x') + external_cmd(arg); + else if (*handler == 'p') + client_cmd_cmdline(arg); + else if (*handler == 'i') { + int num = find_cmd_byname(arg); + if (num >= 0) + command_list[num].handler(); + } + free(tmp); } /* not found, check internal key bindings */ for (i = 0; command_list[i].handler; i++) { diff --git a/gui.h b/gui.h index abc3191b..65342a4d 100644 --- a/gui.h +++ b/gui.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/gui_theme.c b/gui_theme.c index 9345f0ed..647eb66f 100644 --- a/gui_theme.c +++ b/gui_theme.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -281,32 +281,59 @@ static void init_theme_colorful_blackness(struct gui_theme *t) d[SI_AMPLIFICATION].y = 27; d[SI_AMPLIFICATION].len = 8; - d[SI_AUDIO_FILE_INFO].prefix = ""; - d[SI_AUDIO_FILE_INFO].postfix = ""; - d[SI_AUDIO_FILE_INFO].fg = COLOR_GREEN; - d[SI_AUDIO_FILE_INFO].bg = COLOR_BLACK; - d[SI_AUDIO_FILE_INFO].align = CENTER; - d[SI_AUDIO_FILE_INFO].x = 0; - d[SI_AUDIO_FILE_INFO].y = 43; - d[SI_AUDIO_FILE_INFO].len = 100; - - d[SI_TAGINFO1].prefix = ""; - d[SI_TAGINFO1].postfix = ""; - d[SI_TAGINFO1].fg = COLOR_GREEN; - d[SI_TAGINFO1].bg = COLOR_BLACK; - d[SI_TAGINFO1].align = CENTER; - d[SI_TAGINFO1].x = 0; - d[SI_TAGINFO1].y = 53; - d[SI_TAGINFO1].len = 100; - - d[SI_TAGINFO2].prefix = ""; - d[SI_TAGINFO2].postfix = ""; - d[SI_TAGINFO2].fg = COLOR_GREEN; - d[SI_TAGINFO2].bg = COLOR_BLACK; - d[SI_TAGINFO2].align = CENTER; - d[SI_TAGINFO2].x = 0; - d[SI_TAGINFO2].y = 63; - d[SI_TAGINFO2].len = 100; + d[SI_TECHINFO].prefix = ""; + d[SI_TECHINFO].postfix = ""; + d[SI_TECHINFO].fg = COLOR_GREEN; + d[SI_TECHINFO].bg = COLOR_BLACK; + d[SI_TECHINFO].align = CENTER; + d[SI_TECHINFO].x = 0; + d[SI_TECHINFO].y = 43; + d[SI_TECHINFO].len = 100; + + d[SI_TITLE].prefix = ""; + d[SI_TITLE].postfix = ","; + d[SI_TITLE].fg = COLOR_GREEN; + d[SI_TITLE].bg = COLOR_BLACK; + d[SI_TITLE].align = RIGHT; + d[SI_TITLE].x = 0; + d[SI_TITLE].y = 53; + d[SI_TITLE].len = 45; + + d[SI_ARTIST].prefix = " by "; + d[SI_ARTIST].postfix = ""; + d[SI_ARTIST].fg = COLOR_GREEN; + d[SI_ARTIST].bg = COLOR_BLACK; + d[SI_ARTIST].align = LEFT; + d[SI_ARTIST].x = 45; + d[SI_ARTIST].y = 53; + d[SI_ARTIST].len = 45; + + d[SI_YEAR].prefix = "("; + d[SI_YEAR].postfix = ")"; + d[SI_YEAR].fg = COLOR_GREEN; + d[SI_YEAR].bg = COLOR_BLACK; + d[SI_YEAR].align = RIGHT; + d[SI_YEAR].x = 90; + d[SI_YEAR].y = 53; + d[SI_YEAR].len = 10; + + d[SI_ALBUM].prefix = "A: "; + d[SI_ALBUM].postfix = ","; + d[SI_ALBUM].fg = COLOR_GREEN; + d[SI_ALBUM].bg = COLOR_BLACK; + d[SI_ALBUM].align = RIGHT; + d[SI_ALBUM].x = 0; + d[SI_ALBUM].y = 63; + d[SI_ALBUM].len = 50; + + d[SI_COMMENT].prefix = " C: "; + d[SI_COMMENT].postfix = ""; + d[SI_COMMENT].fg = COLOR_GREEN; + d[SI_COMMENT].bg = COLOR_BLACK; + d[SI_COMMENT].align = LEFT; + d[SI_COMMENT].x = 50; + d[SI_COMMENT].y = 63; + d[SI_COMMENT].len = 50; d[SI_AFS_MODE].prefix = ""; d[SI_AFS_MODE].postfix = ""; diff --git a/hash.h b/hash.h index 8a14e3a7..be18a92a 100644 --- a/hash.h +++ b/hash.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/http_recv.c b/http_recv.c index 7a751e4a..cc376dd9 100644 --- a/http_recv.c +++ b/http_recv.c @@ -1,11 +1,12 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file http_recv.c paraslash's http receiver */ +#include #include #include @@ -20,9 +21,7 @@ #include "net.h" #include "string.h" #include "fd.h" - -/** the output buffer size of the http receiver */ -#define BUFSIZE (32 * 1024) +#include "buffer_tree.h" /** * the possible states of a http receiver node @@ -32,48 +31,40 @@ enum http_recv_status {HTTP_CONNECTED, HTTP_SENT_GET_REQUEST, HTTP_STREAMING}; /** - * data specific to the http receiver + * Data specific to the http receiver. * * Each running instance of the http receiver reserves space for one such struct. */ struct private_http_recv_data { -/** - * - * - * the current status of the http receiver node - * - * It gets initialized to \p HTTP_CONNECTED by the open function of the - * http receiver. - * - * \sa receiver::open, receiver_node. - */ + /** + * The current status of the http receiver node. + * + * It gets initialized to \p HTTP_CONNECTED by the open function of the + * http receiver. + * + * \sa receiver::open, receiver_node. + */ enum http_recv_status status; -/** - * - * - * the file descriptor used for receiving the http stream - * - * The pre_select function of the http receiver adds this file descriptor to - * the set of file decriptors which are checked for reading/writing (depending - * on the current status) by the select loop of the application (para_audiod or - * para_recv). - * - * The post_select function of the http receiver uses \a fd, if ready, to - * establish the http connection, and updates \a status according to the new - * state of the connection. As soon as \a status is \p HTTP_STREAMING, \a fd is - * going to be only checked for reading. If data is available, it is read into - * the output buffer of the receiver node by post_select. - * - * \sa receiver::pre_select receiver::post_select receiver_node, http_recv_status - */ + /** + * The file descriptor used for receiving the http stream. + * + * The pre_select function of the http receiver adds this file descriptor to + * the set of file decriptors which are checked for reading/writing (depending + * on the current status) by the select loop of the application (para_audiod or + * para_recv). + * + * The post_select function of the http receiver uses \a fd, if ready, to + * establish the http connection, and updates \a status according to the new + * state of the connection. As soon as \a status is \p HTTP_STREAMING, \a fd is + * going to be only checked for reading. If data is available, it is read into + * the output buffer of the receiver node by post_select. + * + * \sa receiver::pre_select receiver::post_select receiver_node, http_recv_status + */ int fd; + struct btr_pool *btrp; }; -static void http_shutdown(void) -{ - return; -} - static char *make_request_msg(void) { char *ret, *hn = para_hostname(); @@ -89,63 +80,74 @@ static void http_recv_pre_select(struct sched *s, struct task *t) struct private_http_recv_data *phd = rn->private_data; t->error = 0; + if (generic_recv_pre_select(s, t) <= 0) + return; if (phd->status == HTTP_CONNECTED) para_fd_set(phd->fd, &s->wfds, &s->max_fileno); else para_fd_set(phd->fd, &s->rfds, &s->max_fileno); } - static void http_recv_post_select(struct sched *s, struct task *t) { struct receiver_node *rn = container_of(t, struct receiver_node, task); struct private_http_recv_data *phd = rn->private_data; + struct btr_node *btrn = rn->btrn; + int ret; + char *buf; + size_t sz; - if (rn->output_error && *rn->output_error < 0) { - t->error = *rn->output_error; + t->error = 0; + ret = btr_node_status(btrn, 0, BTR_NT_ROOT); + if (ret < 0) + goto err; + if (ret == 0) return; - } - if (phd->status == HTTP_CONNECTED) { + if (phd->status == HTTP_CONNECTED) { char *rq; if (!FD_ISSET(phd->fd, &s->wfds)) return; rq = make_request_msg(); PARA_INFO_LOG("sending http request\n"); - t->error = send_va_buffer(phd->fd, "%s", rq); + ret = send_va_buffer(phd->fd, "%s", rq); free(rq); - if (t->error >= 0) - phd->status = HTTP_SENT_GET_REQUEST; + if (ret < 0) + goto err; + phd->status = HTTP_SENT_GET_REQUEST; return; } if (!FD_ISSET(phd->fd, &s->rfds)) return; if (phd->status == HTTP_SENT_GET_REQUEST) { - t->error = recv_pattern(phd->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG)); - if (t->error >= 0) { - PARA_INFO_LOG("received ok msg, streaming\n"); - phd->status = HTTP_STREAMING; - } - return; - } - if (rn->loaded >= BUFSIZE) { - t->error = -E_HTTP_RECV_OVERRUN; + ret = recv_pattern(phd->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG)); + if (ret < 0) + goto err; + PARA_INFO_LOG("received ok msg, streaming\n"); + phd->status = HTTP_STREAMING; return; } - t->error = recv_bin_buffer(phd->fd, rn->buf + rn->loaded, - BUFSIZE - rn->loaded); - if (t->error > 0) { - rn->loaded += t->error; - return; - } - if (!t->error) - t->error = -E_RECV_EOF; + ret = -E_HTTP_RECV_OVERRUN; + sz = btr_pool_get_buffer(phd->btrp, &buf); + if (sz == 0) + goto err; + ret = recv_bin_buffer(phd->fd, buf, sz); + if (ret == 0) + ret = -E_RECV_EOF; + if (ret < 0) + goto err; + btr_add_output_pool(phd->btrp, ret, btrn); + return; +err: + btr_remove_node(rn->btrn); + t->error = ret; } static void http_recv_close(struct receiver_node *rn) { struct private_http_recv_data *phd = rn->private_data; + close(phd->fd); - free(rn->buf); + btr_pool_free(phd->btrp); free(rn->private_data); } @@ -163,8 +165,8 @@ static int http_recv_open(struct receiver_node *rn) { struct private_http_recv_data *phd; struct http_recv_args_info *conf = rn->conf; - int fd, ret = makesock(AF_UNSPEC, IPPROTO_TCP, 0, conf->host_arg, - conf->port_arg); + int fd, ret = para_connect_simple(IPPROTO_TCP, conf->host_arg, + conf->port_arg); if (ret < 0) return ret; @@ -174,19 +176,25 @@ static int http_recv_open(struct receiver_node *rn) close(fd); return ret; } - rn->buf = para_calloc(BUFSIZE); rn->private_data = phd = para_calloc(sizeof(struct private_http_recv_data)); phd->fd = fd; phd->status = HTTP_CONNECTED; + phd->btrp = btr_pool_new("http_recv", 320 * 1024); return 1; } +static void http_recv_free_config(void *conf) +{ + http_recv_cmdline_parser_free(conf); + free(conf); +} + /** - * the init function of the http receiver + * The init function of the http receiver. * - * \param r pointer to the receiver struct to initialize + * \param r Pointer to the receiver struct to initialize. * - * Just initialize all function pointers of \a r. + * This initializes all function pointers of \a r. */ void http_recv_init(struct receiver *r) { @@ -197,8 +205,8 @@ void http_recv_init(struct receiver *r) r->close = http_recv_close; r->pre_select = http_recv_pre_select; r->post_select = http_recv_post_select; - r->shutdown = http_shutdown; r->parse_config = http_recv_parse_config; + r->free_config = http_recv_free_config; r->help = (struct ggo_help) { .short_help = http_recv_args_info_help, .detailed_help = http_recv_args_info_detailed_help diff --git a/http_send.c b/http_send.c index 0c48934a..828d99e2 100644 --- a/http_send.c +++ b/http_send.c @@ -1,13 +1,15 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file http_send.c paraslash's http sender */ +#include #include #include +#include #include "para.h" #include "error.h" diff --git a/imdct.c b/imdct.c new file mode 100644 index 00000000..5f48ba44 --- /dev/null +++ b/imdct.c @@ -0,0 +1,409 @@ +/* + * FFT/IFFT transforms. + * + * Extracted 2009 from mplayer 2009-02-10 libavcodec/fft.c and libavcodec/mdct.c + * + * Copyright (c) 2008 Loren Merritt + * Copyright (c) 2002 Fabrice Bellard + * Partly based on libdjbfft by D. J. Bernstein + * + * Licensed under the GNU Lesser General Public License. + * For licencing details see COPYING.LIB. + */ + +/** + * \file imdct.c Inverse modified discrete cosine transform. + */ + +#include +#include +#include +#include +#include + +#include "para.h" +#include "error.h" +#include "string.h" +#include "imdct.h" +#include "wma.h" + +typedef float fftsample_t; + +/** Canonical representation of a complex number. */ +struct fft_complex { + /** Real part. */ + fftsample_t re; + /** Imaginary part. */ + fftsample_t im; +}; + +/** FFT Lookup table. */ +struct fft_context { + /** Number of bits of this instance of the FFT. */ + int nbits; + /** The lookup table for cosine values. */ + uint16_t *revtab; +}; + +struct mdct_context { + /** Size of MDCT (number of input data * 2). */ + int n; + /** n = 2^n bits. */ + int nbits; + /** Cosine table for pre/post rotation. */ + fftsample_t *tcos; + /** Sine table for pre/post rotation. */ + fftsample_t *tsin; + /** The context for the underlying fast Fourier transform. */ + struct fft_context fft; +}; + +/** cos(2 * pi * x / n) for 0 <= x <= n / 4, followed by its reverse */ +#define COSINE_TAB(n) fftsample_t cos_ ## n[n / 2] __a_aligned(16) + +COSINE_TAB(16); +COSINE_TAB(32); +COSINE_TAB(64); +COSINE_TAB(128); +COSINE_TAB(256); +COSINE_TAB(512); +COSINE_TAB(1024); +COSINE_TAB(2048); +COSINE_TAB(4096); +COSINE_TAB(8192); +COSINE_TAB(16384); +COSINE_TAB(32768); +COSINE_TAB(65536); + +static fftsample_t *cos_tabs[] = { + cos_16, cos_32, cos_64, cos_128, cos_256, cos_512, cos_1024, cos_2048, + cos_4096, cos_8192, cos_16384, cos_32768, cos_65536, +}; + +static int split_radix_permutation(int i, int n) +{ + int m; + if (n <= 2) + return i & 1; + m = n >> 1; + if ((i & m) == 0) + return split_radix_permutation(i, m) * 2; + m >>= 1; + if ((i & m) == 0) + return split_radix_permutation(i, m) * 4 + 1; + else + return split_radix_permutation(i, m) * 4 - 1; +} + +#define BF(x, y, a, b) {\ + x = a - b;\ + y = a + b;\ +} + +#define BUTTERFLIES(a0, a1, a2, a3) {\ + BF(t3, t5, t5, t1);\ + BF(a2.re, a0.re, a0.re, t5);\ + BF(a3.im, a1.im, a1.im, t3);\ + BF(t4, t6, t2, t6);\ + BF(a3.re, a1.re, a1.re, t4);\ + BF(a2.im, a0.im, a0.im, t6);\ +} + +/* + * Force loading all the inputs before storing any. This is slightly slower for + * small data, but avoids store->load aliasing for addresses separated by large + * powers of 2. + */ +#define BUTTERFLIES_BIG(a0, a1, a2, a3) {\ + fftsample_t r0 = a0.re, i0 = a0.im, r1 = a1.re, i1 = a1.im;\ + BF(t3, t5, t5, t1);\ + BF(a2.re, a0.re, r0, t5);\ + BF(a3.im, a1.im, i1, t3);\ + BF(t4, t6, t2, t6);\ + BF(a3.re, a1.re, r1, t4);\ + BF(a2.im, a0.im, i0, t6);\ +} + +#define TRANSFORM(a0, a1, a2, a3, wre,wim) {\ + t1 = a2.re * wre + a2.im * wim;\ + t2 = a2.im * wre - a2.re * wim;\ + t5 = a3.re * wre - a3.im * wim;\ + t6 = a3.im * wre + a3.re * wim;\ + BUTTERFLIES(a0, a1, a2, a3)\ +} + +#define TRANSFORM_ZERO(a0, a1, a2, a3) {\ + t1 = a2.re;\ + t2 = a2.im;\ + t5 = a3.re;\ + t6 = a3.im;\ + BUTTERFLIES(a0, a1, a2, a3)\ +} + +/* z[0...8n - 1], w[1...2n - 1] */ +#define PASS(name)\ +static void name(struct fft_complex *z, const fftsample_t *wre, unsigned int n)\ +{\ + fftsample_t t1, t2, t3, t4, t5, t6;\ + int o1 = 2 * n;\ + int o2 = 4 * n;\ + int o3 = 6 * n;\ + const fftsample_t *wim = wre + o1;\ + n--;\ +\ + TRANSFORM_ZERO(z[0], z[o1], z[o2], z[o3]);\ + TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]);\ + do {\ + z += 2;\ + wre += 2;\ + wim -= 2;\ + TRANSFORM(z[0], z[o1], z[o2], z[o3], wre[0], wim[0]);\ + TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]);\ + } while (--n);\ +} + +PASS(pass) +#undef BUTTERFLIES +#define BUTTERFLIES BUTTERFLIES_BIG + +#define DECL_FFT(n, n2, n4)\ +static void fft##n(struct fft_complex *z)\ +{\ + fft ## n2(z);\ + fft ## n4(z + n4 * 2);\ + fft ## n4(z + n4 * 3);\ + pass(z, cos_ ## n, n4 / 2);\ +} + +static void fft4(struct fft_complex *z) +{ + fftsample_t t1, t2, t3, t4, t5, t6, t7, t8; + + BF(t3, t1, z[0].re, z[1].re); + BF(t8, t6, z[3].re, z[2].re); + BF(z[2].re, z[0].re, t1, t6); + BF(t4, t2, z[0].im, z[1].im); + BF(t7, t5, z[2].im, z[3].im); + BF(z[3].im, z[1].im, t4, t8); + BF(z[3].re, z[1].re, t3, t7); + BF(z[2].im, z[0].im, t2, t5); +} + +static void fft8(struct fft_complex *z) +{ + fftsample_t t1, t2, t3, t4, t5, t6, t7, t8; + + fft4(z); + + BF(t1, z[5].re, z[4].re, -z[5].re); + BF(t2, z[5].im, z[4].im, -z[5].im); + BF(t3, z[7].re, z[6].re, -z[7].re); + BF(t4, z[7].im, z[6].im, -z[7].im); + BF(t8, t1, t3, t1); + BF(t7, t2, t2, t4); + BF(z[4].re, z[0].re, z[0].re, t1); + BF(z[4].im, z[0].im, z[0].im, t2); + BF(z[6].re, z[2].re, z[2].re, t7); + BF(z[6].im, z[2].im, z[2].im, t8); + + TRANSFORM(z[1], z[3], z[5], z[7], M_SQRT1_2, M_SQRT1_2); +} + +static void fft16(struct fft_complex *z) +{ + fftsample_t t1, t2, t3, t4, t5, t6; + + fft8(z); + fft4(z + 8); + fft4(z + 12); + + TRANSFORM_ZERO(z[0], z[4], z[8], z[12]); + TRANSFORM(z[2], z[6], z[10], z[14], M_SQRT1_2, M_SQRT1_2); + TRANSFORM(z[1], z[5], z[9], z[13], cos_16[1], cos_16[3]); + TRANSFORM(z[3], z[7], z[11], z[15], cos_16[3], cos_16[1]); +} + +DECL_FFT(32, 16, 8) +DECL_FFT(64, 32, 16) +DECL_FFT(128, 64, 32) +DECL_FFT(256, 128, 64) +DECL_FFT(512, 256, 128) + +DECL_FFT(1024, 512, 256) +DECL_FFT(2048, 1024, 512) +DECL_FFT(4096, 2048, 1024) +DECL_FFT(8192, 4096, 2048) +DECL_FFT(16384, 8192, 4096) +DECL_FFT(32768, 16384, 8192) +DECL_FFT(65536, 32768, 16384) + +static void (*fft_dispatch[]) (struct fft_complex *) = { + fft4, fft8, fft16, fft32, fft64, fft128, fft256, fft512, fft1024, + fft2048, fft4096, fft8192, fft16384, fft32768, fft65536, +}; + +static void fft(struct fft_context *s, struct fft_complex *z) +{ + fft_dispatch[s->nbits - 2] (z); +} + +/* complex multiplication: p = a * b */ +#define CMUL(pre, pim, are, aim, bre, bim) \ +{\ + fftsample_t _are = (are);\ + fftsample_t _aim = (aim);\ + fftsample_t _bre = (bre);\ + fftsample_t _bim = (bim);\ + (pre) = _are * _bre - _aim * _bim;\ + (pim) = _are * _bim + _aim * _bre;\ +} + +/** + * Compute the middle half of the inverse MDCT of size N = 2^nbits + * + * Thus excluding the parts that can be derived by symmetry. + * + * \param output N/2 samples. + * \param input N/2 samples. + */ +static void imdct_half(struct mdct_context *s, fftsample_t *output, + const fftsample_t *input) +{ + int k, n8, n4, n2, n, j; + const uint16_t *revtab = s->fft.revtab; + const fftsample_t *tcos = s->tcos; + const fftsample_t *tsin = s->tsin; + const fftsample_t *in1, *in2; + struct fft_complex *z = (struct fft_complex *)output; + + n = 1 << s->nbits; + n2 = n >> 1; + n4 = n >> 2; + n8 = n >> 3; + + /* pre rotation */ + in1 = input; + in2 = input + n2 - 1; + for (k = 0; k < n4; k++) { + j = revtab[k]; + CMUL(z[j].re, z[j].im, *in2, *in1, tcos[k], tsin[k]); + in1 += 2; + in2 -= 2; + } + fft(&s->fft, z); + + /* post rotation + reordering */ + output += n4; + for (k = 0; k < n8; k++) { + fftsample_t r0, i0, r1, i1; + CMUL(r0, i1, z[n8 - k - 1].im, z[n8 - k - 1].re, + tsin[n8 - k - 1], tcos[n8 - k - 1]); + CMUL(r1, i0, z[n8 + k].im, z[n8 + k].re, tsin[n8 + k], + tcos[n8 + k]); + z[n8 - k - 1].re = r0; + z[n8 - k - 1].im = i0; + z[n8 + k].re = r1; + z[n8 + k].im = i1; + } +} + +/** + * Compute the inverse MDCT. + * + * \param ctx The initialized context structure. + * \param output N samples. + * \param input N/2 samples. + * + * \sa \ref imdct_init(). + */ +void imdct(struct mdct_context *ctx, float *output, const float *input) +{ + int k; + int n = 1 << ctx->nbits; + int n2 = n >> 1; + int n4 = n >> 2; + + imdct_half(ctx, output + n4, input); + + for (k = 0; k < n4; k++) { + output[k] = -output[n2 - k - 1]; + output[n - k - 1] = output[n2 + k]; + } +} + +static int fft_init(struct fft_context *s, int nbits) +{ + int i, j, n; + + if (nbits < 2 || nbits > 16) + return -E_FFT_BAD_PARAMS; + s->nbits = nbits; + n = 1 << nbits; + + s->revtab = para_malloc(n * sizeof(uint16_t)); + for (j = 4; j <= nbits; j++) { + int k = 1 << j; + double freq = 2 * M_PI / k; + fftsample_t *tab = cos_tabs[j - 4]; + for (i = 0; i <= k / 4; i++) + tab[i] = cos(i * freq); + for (i = 1; i < k / 4; i++) + tab[k / 2 - i] = tab[i]; + } + for (i = 0; i < n; i++) + s->revtab[-split_radix_permutation(i, n) & (n - 1)] = i; + return 0; +} + +/** + * Initialize the inverse modified cosine transform. + * + * \param nbits The number of bits to use (4 <= \a nbits <= 18). + * + * \param result Opaque structure that must be passed to \ref imdct(). + * + * \return Standard. + */ +int imdct_init(int nbits, struct mdct_context **result) +{ + int ret, n, n4, i; + double alpha; + struct mdct_context *s; + + s = para_calloc(sizeof(*s)); + n = 1 << nbits; + s->nbits = nbits; + s->n = n; + n4 = n >> 2; + s->tcos = para_malloc(n4 * sizeof(fftsample_t)); + s->tsin = para_malloc(n4 * sizeof(fftsample_t)); + + for (i = 0; i < n4; i++) { + alpha = 2 * M_PI * (i + 1.0 / 8.0) / n; + s->tcos[i] = -cos(alpha); + s->tsin[i] = -sin(alpha); + } + ret = fft_init(&s->fft, s->nbits - 2); + if (ret < 0) + goto fail; + *result = s; + return 0; +fail: + freep(&s->tcos); + freep(&s->tsin); + free(s); + return ret; +} + +/** + * Deallocate imdct resources. + * + * \param ctx The pointer obtained by imdct_init(). + */ +void imdct_end(struct mdct_context *ctx) +{ + free(ctx->tcos); + free(ctx->tsin); + free(ctx->fft.revtab); + free(ctx); +} diff --git a/imdct.h b/imdct.h new file mode 100644 index 00000000..442b98bf --- /dev/null +++ b/imdct.h @@ -0,0 +1,7 @@ +/** \file imdct.h Exported symbols from imdct.c. */ + +struct mdct_context; + +int imdct_init(int nbits, struct mdct_context **result); +void imdct(struct mdct_context *s, float *output, const float *input); +void imdct_end(struct mdct_context *s); diff --git a/ipc.c b/ipc.c index c64cc89c..6a0f00b5 100644 --- a/ipc.c +++ b/ipc.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/list.h b/list.h index de04ab9e..e4b5a1f6 100644 --- a/list.h +++ b/list.h @@ -33,6 +33,10 @@ struct list_head { struct list_head *prev; }; +/** Define an initialized list head. */ +#define INITIALIZED_LIST_HEAD(name) struct list_head name = { &(name), &(name) } + + /** must be called before using any other list functions */ #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ @@ -200,3 +204,25 @@ static inline int list_empty(const struct list_head *head) n = list_entry(pos->member.prev, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +/** + * Get the first element from a list + * \param ptr the list head to take the element from. + * \param type The type of the struct this is embedded in. + * \param member The name of the list_struct within the struct. + * + * Note that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * Test whether a list has just one entry. + * + * \param head The list to test. + */ +static inline int list_is_singular(const struct list_head *head) +{ + return !list_empty(head) && (head->next == head->prev); +} + diff --git a/mm.c b/mm.c new file mode 100644 index 00000000..57121a1c --- /dev/null +++ b/mm.c @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2007-2010 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file mm.c Paraslash's mood methods. */ + +#include +#include +#include +#include + +#include "para.h" +#include "error.h" +#include "string.h" +#include "afh.h" +#include "afs.h" +#include "mm.h" + +#define MOOD_COMPARATORS \ + MC(LESS, <) \ + MC(LESS_OR_EQUAL, <=) \ + MC(EQUAL, =) \ + MC(EQUAL2, ==) \ + MC(NOT_EQUAL, !=) \ + MC(NOT_EQUAL2, <>) \ + MC(GREATER, >) \ + MC(GREATER_OR_EQUAL, >=) \ + +#define MC(a, b) MC_ ## a, +enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS}; +#undef MC +#define MC(a, b) # b, +static const char const *mood_comparators[] = {MOOD_COMPARATORS}; +#undef MC + +static int parse_mood_comparator(const char *word) +{ + int i; + + for (i = 0; i < NUM_MOOD_COMPARATORS; i++) + if (!strcmp(word, mood_comparators[i])) + return i; + return -E_MOOD_SYNTAX; +} + +struct mm_compare_num_data { + /** <, <=, =, !=, >=, or >. */ + enum mood_comparator_id id; + /** The value given at the mood line. */ + int32_t arg; +}; + +static int mm_compare_num_score_function(int32_t val, + const struct mm_compare_num_data *cnd) +{ + int res; + int32_t arg = cnd->arg; + + switch (cnd->id) { + case MC_LESS: + res = val < arg; break; + case MC_LESS_OR_EQUAL: + res = val <= arg; break; + case MC_EQUAL: + case MC_EQUAL2: + res = val == arg; break; + case MC_NOT_EQUAL: + case MC_NOT_EQUAL2: + res = val != arg; break; + case MC_GREATER: + res = val > arg; break; + case MC_GREATER_OR_EQUAL: + res = val >= arg; break; + default: + PARA_EMERG_LOG("BUG: invalid mood comparator\n"); + exit(EXIT_FAILURE); + } + return res? 100 : -100; +} + +static int mm_compare_num_parser(int argc, char **argv, void **private) +{ + int ret; + enum mood_comparator_id id; + int32_t arg; + struct mm_compare_num_data *cnd; + if (argc != 2) + return -E_MOOD_SYNTAX; + ret = parse_mood_comparator(argv[1]); + if (ret < 0) + return ret; + id = ret; + ret = para_atoi32(argv[2], &arg); + if (ret < 0) + return ret; + cnd = para_malloc(sizeof(struct mm_compare_num_data)); + cnd->id = id; + cnd->arg = arg; + *private = cnd; + return 1; +} + +static int mm_regex_parser(int argc, char **argv, void **private) +{ + regex_t *preg; + int ret; + + if (argc != 1) + return -E_MOOD_SYNTAX; + preg = para_malloc(sizeof(*preg)); + ret = para_regcomp(preg, argv[1], REG_EXTENDED | REG_NOSUB); + if (ret < 0) { + free(preg); + return ret; + } + *private = preg; + return 1; +} + +static int mm_regex_score_function(const regex_t *preg, const char *pattern) +{ + return regexec(preg, pattern, 0, NULL, 0) == 0? 100 : -100; +} + +static void mm_regex_cleanup(void *private) +{ + regex_t *preg = private; + regfree(preg); + free(preg); +} + +static int mm_artist_matches_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + const struct afh_info *afhi, + const void *private) +{ + return mm_regex_score_function(private, afhi->tags.artist); +} + +static int mm_title_matches_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + const struct afh_info *afhi, + const void *private) +{ + return mm_regex_score_function(private, afhi->tags.title); +} + +static int mm_album_matches_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + const struct afh_info *afhi, + const void *private) +{ + return mm_regex_score_function(private, afhi->tags.album); +} + +static int mm_comment_matches_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + const struct afh_info *afhi, + const void *private) +{ + return mm_regex_score_function(private, afhi->tags.comment); +} + +static int mm_bitrate_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + const struct afh_info *afhi, + const void *private) +{ + return mm_compare_num_score_function(afhi->bitrate, private); +} + +static int mm_frequency_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + const struct afh_info *afhi, + const void *private) +{ + return mm_compare_num_score_function(afhi->frequency, private); +} + +static int mm_channels_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + const struct afh_info *afhi, + const void *private) +{ + return mm_compare_num_score_function(afhi->channels, private); +} + +static int mm_num_played_score_function(__a_unused const char *path, + const struct afs_info *afsi, + __a_unused const struct afh_info *afhi, + const void *private) +{ + return mm_compare_num_score_function(afsi->num_played, private); +} + +struct mm_year_data { + /** Comparator and year given at the mood line. */ + struct mm_compare_num_data *cnd; + /** Used to detect Y2K issues. */ + int32_t current_year; +}; + +static int mm_year_parser(int argc, char **argv, void **private) +{ + int ret = -E_MOOD_SYNTAX; + struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd)); + time_t current_time; + struct tm *gmt; + + ret = mm_compare_num_parser(argc, argv, (void **)&mmyd->cnd); + if (ret < 0) + goto err; + current_time = time(NULL); + gmt = gmtime(¤t_time); + /* tm_year is the number of years since 1900 */ + mmyd->current_year = gmt->tm_year + 1900; + *private = mmyd; + return 1; +err: + free(mmyd); + return ret; +} + +static int mm_year_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + const struct afh_info *afhi, + const void *private) +{ + const struct mm_year_data *mmyd = private; + int32_t tag_year; + int ret = para_atoi32(afhi->tags.year, &tag_year); + + if (ret < 0) /* year tag not present or not a number */ + return -100; + if (tag_year < 0) + return -100; + /* try to work around Y2K issues */ + if (tag_year < 100) { + tag_year += 1900; + if (tag_year + 100 <= mmyd->current_year) + tag_year += 100; /* assume tag_year >= 2000 */ + } + return mm_compare_num_score_function(tag_year, mmyd->cnd); +} + +static void mm_year_cleanup(void *private) +{ + struct mm_year_data *mmyd = private; + + free(mmyd->cnd); + free(mmyd); +} + +static int mm_no_attributes_set_parser(int argc, __a_unused char **argv, + __a_unused void **ignored) +{ + return argc? -E_MOOD_SYNTAX : 1; +} + +static int mm_no_attributes_set_score_function(__a_unused const char *path, + const struct afs_info *afsi, + __a_unused const struct afh_info *afhi, + __a_unused const void *data) +{ + if (!afsi->attributes) + return 100; + return -100; +} + +static int mm_path_matches_score_function(const char *path, + __a_unused const struct afs_info *afsi, + __a_unused const struct afh_info *afhi, + const void *data) +{ + if (fnmatch(data, path, 0)) + return -100; + return 100; +} + +static int mm_path_matches_parser(int argc, char **argv, void **data) +{ + if (argc != 1) + return -E_MOOD_SYNTAX; + *data = para_strdup(argv[1]); + return 1; +} + +static void mm_path_matches_cleanup(void *data) +{ + free(data); +} + +static int mm_is_set_parser(int argc, char **argv, void **bitnum) +{ + int ret; + unsigned char c, *res; + + if (argc != 1) + return -E_MOOD_SYNTAX; + ret = get_attribute_bitnum_by_name(argv[1], &c); + if (ret < 0) + return ret; + res = para_malloc(1); + *res = c; + *bitnum = res; + return 1; +} + +static int mm_is_set_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + __a_unused const struct afh_info *afhi, + const void *data) +{ + const unsigned char *bn = data; + if (afsi->attributes & (1ULL << *bn)) + return 100; + return -100; +} + +#define DEFINE_MOOD_METHOD(_name) \ +.parser = mm_ ## _name ## _parser, \ +.score_function = mm_ ## _name ## _score_function, \ +.name = #_name + +#define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \ + DEFINE_MOOD_METHOD(_name), \ + .cleanup = mm_ ## _name ## _cleanup + +#define DEFINE_REGEX_MOOD_METHOD(_name) \ + .name = #_name, \ + .parser = mm_regex_parser, \ + .score_function = mm_ ## _name ## _score_function, \ + .cleanup = mm_regex_cleanup + +#define DEFINE_COMPARE_NUM_MOOD_METHOD(_name) \ + .name = #_name, \ + .parser = mm_compare_num_parser, \ + .score_function = mm_ ## _name ## _score_function + +const struct mood_method mood_methods[] = { + {DEFINE_MOOD_METHOD(no_attributes_set)}, + {DEFINE_MOOD_METHOD(is_set)}, + {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)}, + {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)}, + {DEFINE_REGEX_MOOD_METHOD(artist_matches)}, + {DEFINE_REGEX_MOOD_METHOD(title_matches)}, + {DEFINE_REGEX_MOOD_METHOD(album_matches)}, + {DEFINE_REGEX_MOOD_METHOD(comment_matches)}, + {DEFINE_COMPARE_NUM_MOOD_METHOD(bitrate)}, + {DEFINE_COMPARE_NUM_MOOD_METHOD(frequency)}, + {DEFINE_COMPARE_NUM_MOOD_METHOD(channels)}, + {DEFINE_COMPARE_NUM_MOOD_METHOD(num_played)}, + {.parser = NULL} +}; diff --git a/mm.h b/mm.h new file mode 100644 index 00000000..55af744f --- /dev/null +++ b/mm.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2007-2010 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file mm.h Symbols and declarations for mood methods. */ + +/** + * Assign scores according to a mood_method. + * + * Each mood_method has its own mood_score_function. The first three parameters + * passed to that function are informations about the audio file whose score is + * to be computed. The data argument depends on the mood method this function + * is used for. It usually is the argument given at the end of a mood line. + * + * Mood score functions must return values between -100 and +100 inclusively. + * Boolean score functions should always return either -100 or +100. + * + * \sa struct mood_method, mood_parser. + */ +typedef int mood_score_function(const char *path, const struct afs_info *afsi, + const struct afh_info *afhi, const void *data); + +/** + * Pre-process a mood line. + * + * The mood_parser of a mood_method is called once at mood open time for each + * line of the current mood definition that contains the mood_method's name as + * a keyword. The line is passed to the mood_parser as the first argument. The + * mood_parser must determine whether the line is syntactically correct and + * return a positive value if so and a negative value otherwise. + * + * Some mood parsers pre-process the data given in the mood line to compute a + * structure which depends of the particular mood_method and which is used + * later in the mood_score_function of the mood_method. The mood_parser may + * store a pointer to its structure via the void** pointer. + * + * \sa mood_open(), mood_cleanup_function, mood_score_function. + */ +typedef int mood_parser(int, char **, void **); + +/** + * Deallocate resources which were allocated by the mood_parser. + * + * This optional function of a mood_method is used to free any resources + * allocated in mood_open() by the mood_parser. The argument passed is a + * pointer to the mood_method specific data structure that was returned by the + * mood_parser. + * + * \sa mood_parser. + */ +typedef void mood_cleanup_function(void *); + +/** + * Used for scoring and to determine whether a file is admissible. + */ +struct mood_method { + /** The name of the method. */ + const char *name; + /** Pointer to the mood parser. */ + mood_parser *parser; + /** Pointer to the score function */ + mood_score_function *score_function; + /** Optional cleanup function. */ + mood_cleanup_function *cleanup; +}; + +/** The array of available mood methods. */ +extern const struct mood_method mood_methods[]; diff --git a/mood.c b/mood.c index 9f1d7745..c03867c1 100644 --- a/mood.c +++ b/mood.c @@ -1,12 +1,14 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file mood.c Paraslash's mood handling functions. */ -#include +#include +#include + #include "para.h" #include "error.h" #include "string.h" @@ -14,6 +16,7 @@ #include "afs.h" #include "list.h" #include "ipc.h" +#include "mm.h" /** * Contains statistical data of the currently admissible audio files. @@ -32,67 +35,7 @@ struct afs_statistics { /** Number of admissible files */ unsigned num; }; -struct afs_statistics statistics; - -/** - * Assign scores according to a mood_method. - * - * Each mood_method has its own mood_score_function. The first three parameters - * passed to that function are informations about the audio file whose score is - * to be computed. The data argument depends on the mood method this function - * is used for. It usually is the argument given at the end of a mood line. - * - * Mood score functions must return values between -100 and +100 inclusively. - * Boolean score functions should always return either -100 or +100. - * - * \sa struct mood_method, mood_parser. - */ -typedef int mood_score_function(const char *path, const struct afs_info *afsi, - const struct afh_info *afhi, const void *data); - -/** - * Pre-process a mood line. - * - * The mood_parser of a mood_method is called once at mood open time for each - * line of the current mood definition that contains the mood_method's name as - * a keyword. The line is passed to the mood_parser as the first argument. The - * mood_parser must determine whether the line is syntactically correct and - * return a positive value if so and a negative value otherwise. - * - * Some mood parsers pre-process the data given in the mood line to compute a - * structure which depends of the particular mood_method and which is used - * later in the mood_score_function of the mood_method. The mood_parser may - * store a pointer to its structure via the second argument. - * - * \sa mood_open(), mood_cleanup_function, mood_score_function. - */ -typedef int mood_parser(const char *, void **); - -/** - * Deallocate resources which were allocated by the mood_parser. - * - * This optional function of a mood_method is used to free any resources - * allocated in mood_open() by the mood_parser. The argument passed is a - * pointer to the mood_method specific data structure that was returned by the - * mood_parser. - * - * \sa mood_parser. - */ -typedef void mood_cleanup_function(void *); - -/** - * Used for scoring and to determine whether a file is admissible. - */ -struct mood_method { - /** The name of the method. */ - const char *name; - /** Pointer to the mood parser. */ - mood_parser *parser; - /** Pointer to the score function */ - mood_score_function *score_function; - /** Optional cleanup function. */ - mood_cleanup_function *cleanup; -}; +static struct afs_statistics statistics; /** * Each line of the current mood corresponds to a mood_item. @@ -164,113 +107,17 @@ static uint64_t int_sqrt(uint64_t x) return res; } -static int mm_no_attributes_set_parser(const char *arg, __a_unused void **ignored) -{ - if (arg && *arg) - PARA_WARNING_LOG("ignored junk at eol: %s\n", arg); - return 1; -} - -static int mm_no_attributes_set_score_function(__a_unused const char *path, - const struct afs_info *afsi, - __a_unused const struct afh_info *afhi, - __a_unused const void *data) -{ - if (!afsi->attributes) - return 100; - return -100; -} - -static int mm_played_rarely_score_function(__a_unused const char *path, - const struct afs_info *afsi, - __a_unused const struct afh_info *afhi, - __a_unused const void *data) -{ - unsigned num; - int ret = get_num_admissible_files(&num); - - if (ret < 0) - return 0; - if (statistics.num_played_sum - num * afsi->num_played - > int_sqrt(statistics.num_played_qd * num)) - return 100; - return -100; -} - -static int mm_played_rarely_parser(const char *arg, __a_unused void **ignored) -{ - if (arg && *arg) - PARA_WARNING_LOG("ignored junk at eol: %s\n", arg); - return 1; -} - -static int mm_path_matches_score_function(const char *path, - __a_unused const struct afs_info *afsi, - __a_unused const struct afh_info *afhi, - const void *data) -{ - if (fnmatch(data, path, 0)) - return -100; - return 100; -} - -static int mm_path_matches_parser(const char *arg, void **data) +/* returns 1 if row matches score item, 0 if not. */ +static int get_item_score(struct mood_item *item, const struct afs_info *afsi, + const struct afh_info *afhi, const char *path, long *score, + long *score_arg_sum) { - *data = para_strdup(arg); - return 1; -} - -static void mm_path_matches_cleanup(void *data) -{ - free(data); -} - -static int mm_is_set_parser(const char *arg, void **bitnum) -{ - unsigned char *c = para_malloc(1); - int ret = get_attribute_bitnum_by_name(arg, c); - - if (ret >= 0) - *bitnum = c; - else - free(c); - return ret; -} - -static int mm_is_set_score_function(__a_unused const char *path, - __a_unused const struct afs_info *afsi, - __a_unused const struct afh_info *afhi, - const void *data) -{ - const unsigned char *bn = data; - if (afsi->attributes & (1ULL << *bn)) - return 100; - return -100; -} - -/* returns 1 if row matches score item, 0 if not, negative on errors */ -static int get_item_score(const struct osl_row *row, struct mood_item *item, - long *score, long *score_arg_sum) -{ - struct afs_info afsi; - struct afh_info afhi; - char *path; int ret, match = 1; *score_arg_sum += item->random_score? 100 : PARA_ABS(item->score_arg); ret = 100; if (item->method) { - ret = get_afsi_of_row(row, &afsi); - if (ret< 0) - return ret; - ret = get_afhi_of_row(row, &afhi); - if (ret< 0) - return ret; - free(afhi.info_string); /* don't need the tag info */ - ret = get_audio_file_path_of_row(row, &path); - if (ret< 0) - return ret; - ret = item->method->score_function(path, &afsi, &afhi, + ret = item->method->score_function(path, afsi, afhi, item->parser_data); if ((ret < 0 && !item->logical_not) || (ret >= 0 && item->logical_not)) match = 0; /* no match */ @@ -289,24 +136,32 @@ static int compute_mood_score(const struct osl_row *aft_row, struct mood *m, struct mood_item *item; int ret, match = 0; long score_arg_sum = 0, score = 0, item_score; + struct afs_info afsi; + struct afh_info afhi; + char *path; if (!m) return -E_NO_MOOD; + ret = get_afsi_of_row(aft_row, &afsi); + if (ret< 0) + return ret; + ret = get_afhi_of_row(aft_row, &afhi); + if (ret< 0) + return ret; + ret = get_audio_file_path_of_row(aft_row, &path); + if (ret< 0) + return ret; /* reject audio file if it matches any entry in the deny list */ list_for_each_entry(item, &m->deny_list, mood_item_node) { - ret = get_item_score(aft_row, item, &item_score, + ret = get_item_score(item, &afsi, &afhi, path, &item_score, &score_arg_sum); - if (ret < 0) - return ret; if (ret > 0) /* not admissible */ return 0; score += item_score; } list_for_each_entry(item, &m->accept_list, mood_item_node) { - ret = get_item_score(aft_row, item, &item_score, + ret = get_item_score(item, &afsi, &afhi, path, &item_score, &score_arg_sum); - if (ret < 0) - return ret; if (ret == 0) continue; match = 1; @@ -316,10 +171,8 @@ static int compute_mood_score(const struct osl_row *aft_row, struct mood *m, if (!match && !list_empty(&m->accept_list)) return 0; list_for_each_entry(item, &m->score_list, mood_item_node) { - ret = get_item_score(aft_row, item, &item_score, + ret = get_item_score(item, &afsi, &afhi, path, &item_score, &score_arg_sum); - if (ret < 0) - return ret; score += item_score; } if (score_arg_sum) @@ -328,23 +181,6 @@ static int compute_mood_score(const struct osl_row *aft_row, struct mood *m, return 1; } -#define DEFINE_MOOD_METHOD(_name) \ -.parser = mm_ ## _name ## _parser, \ -.score_function = mm_ ## _name ## _score_function, \ -.name = #_name - -#define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \ - DEFINE_MOOD_METHOD(_name), \ - .cleanup = mm_ ## _name ## _cleanup - -static const struct mood_method mood_methods[] = { - {DEFINE_MOOD_METHOD(no_attributes_set)}, - {DEFINE_MOOD_METHOD(played_rarely)}, - {DEFINE_MOOD_METHOD(is_set)}, - {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)}, - {.parser = NULL} -}; - static void cleanup_list_entry(struct mood_item *item) { if (item->method && item->method->cleanup) @@ -412,17 +248,17 @@ static int parse_mood_line(char *mood_line, void *data) { struct mood_line_parser_data *mlpd = data; char **argv; - char *delim = " \t"; unsigned num_words; char **w; int i, ret; enum mood_line_type mlt = ML_INVALID; struct mood_item *mi = NULL; - char *buf = para_strdup(mood_line); mlpd->line_num++; - num_words = split_args(buf, &argv, delim); - ret = 1; + ret = create_argv(mood_line, " \t", &argv); + if (ret < 0) + return ret; + num_words = ret; if (!num_words) /* empty line */ goto out; w = argv; @@ -495,8 +331,8 @@ check_for_if: ret = -E_MOOD_SYNTAX; if (!mood_methods[i].parser) goto out; - w++; - ret = mood_methods[i].parser(*w, &mi->parser_data); + ret = mood_methods[i].parser(num_words - 1 - (w - argv), w, + &mi->parser_data); if (ret < 0) goto out; mi->method = &mood_methods[i]; @@ -513,8 +349,7 @@ success: (mlt == ML_DENY? "deny" : "score"), mi->method); ret = 1; out: - free(argv); - free(buf); + free_argv(argv); if (ret >= 0) return ret; if (mi) { @@ -961,7 +796,6 @@ void close_current_mood(void) memset(&statistics, 0, sizeof(statistics)); } - /** * Change the current mood. * @@ -993,7 +827,7 @@ int change_current_mood(char *mood_name) .data = mood_name, .size = strlen(mood_name) + 1 }; - ret = osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row); + ret = osl(osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row)); if (ret < 0) { PARA_NOTICE_LOG("no such mood: %s\n", mood_name); return ret; diff --git a/mood.h b/mood.h new file mode 100644 index 00000000..4a4d206e --- /dev/null +++ b/mood.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2007-2010 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file mood.h Functions exported by mood.h. */ + +int change_current_mood(char *mood_name); +void close_current_mood(void); +int reload_current_mood(void); +void mood_check_callback(int fd, __a_unused const struct osl_object *query); diff --git a/mp3_afh.c b/mp3_afh.c index 2d18d5e5..980649c8 100644 --- a/mp3_afh.c +++ b/mp3_afh.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2009 Andre Noll + * Copyright (C) 2003-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -16,12 +16,12 @@ * Johannes Overmann */ +#include + #include "para.h" #include "error.h" #include "afh.h" #include "string.h" -#include "afs.h" -#include "server.h" /** \cond some defines and structs which are only used in this file */ @@ -126,62 +126,49 @@ static char *get_strings(struct id3_frame *fr) return NULL; } -static char *mp3_get_id3(__a_unused unsigned char *map, - __a_unused size_t numbytes, int fd) +static void mp3_get_id3(__a_unused unsigned char *map, + __a_unused size_t numbytes, int fd, struct taginfo *tags) { int i; struct id3_tag *id3_t; - char *title = NULL, *artist = NULL, *album = NULL, *year = NULL, - *comment = NULL, *result; struct id3_file *id3_f = id3_file_fdopen(fd, ID3_FILE_MODE_READONLY); if (!id3_f) - goto no_tag; + return; id3_t = id3_file_tag(id3_f); - if (!id3_t) - goto no_tag; + if (!id3_t) { + id3_file_close(id3_f); + return; + } for (i = 0; i < id3_t->nframes; i++) { struct id3_frame *fr = id3_t->frames[i]; if (!strcmp(fr->id, "TIT2")) { - if (!title) - title = get_strings(fr); + if (!tags->title) + tags->title = get_strings(fr); continue; } if (!strcmp(fr->id, "TPE1")) { - if (!artist) - artist = get_strings(fr); + if (!tags->artist) + tags->artist = get_strings(fr); continue; } if (!strcmp(fr->id, "TALB")) { - if (!album) - album = get_strings(fr); + if (!tags->album) + tags->album = get_strings(fr); continue; } if (!strcmp(fr->id, "TDRC")) { - if (!year) - year = get_strings(fr); + if (!tags->year) + tags->year = get_strings(fr); continue; } if (!strcmp(fr->id, "COMM")) { - if (!comment) - comment = get_strings(fr); + if (!tags->comment) + tags->comment = get_strings(fr); continue; } } id3_file_close(id3_f); - result = make_taginfo(title, artist, album, year, comment); - free(title); - free(artist); - free(album); - free(year); - free(comment); - return result; -no_tag: - if (id3_f) - id3_file_close(id3_f); - return make_message("%s: (no id3 v1/v2 tag)\n%s:\n", - status_item_list[SI_TAGINFO1], - status_item_list[SI_TAGINFO2]); } #else /* HAVE_LIBID3TAG */ @@ -197,16 +184,15 @@ static char *unpad(char *string) return string; } -static char *mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd) +static void mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd, + struct taginfo *tags) { char title[31], artist[31], album[31], year[5], comment[31]; off_t fpos; if (numbytes < 128 || strncmp("TAG", (char *)map + numbytes - 128, 3)) { PARA_DEBUG_LOG("no id3 v1 tag\n"); - return make_message("%s: (no id3 v1 tag)\n%s:\n", - status_item_list[SI_TAGINFO1], - status_item_list[SI_TAGINFO2]); + return; } fpos = numbytes - 125; memcpy(title, map + fpos, 30); @@ -228,7 +214,11 @@ static char *mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd) unpad(album); unpad(year); unpad(comment); - return make_taginfo(title, artist, album, year, comment); + tags->artist = para_strdup(artist); + tags->title = para_strdup(title); + tags->year = para_strdup(year); + tags->album = para_strdup(album); + tags->comment = para_strdup(comment); } #endif /* HAVE_LIBID3TAG */ @@ -400,11 +390,9 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd, unsigned chunk_table_size = 1000; /* gets increased on demand */ off_t fpos = 0; struct mp3header header; - char *taginfo; afhi->chunks_total = 0; afhi->chunk_table = para_malloc(chunk_table_size * sizeof(uint32_t)); - taginfo = mp3_get_id3(map, numbytes, fd); while (1) { int freq, br; struct timeval tmp, cct; /* current chunk time */ @@ -460,15 +448,11 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd, tv_divide(afhi->chunks_total, &total_time, &afhi->chunk_tv); PARA_DEBUG_LOG("%lu chunks, each %lums\n", afhi->chunks_total, tv2ms(&afhi->chunk_tv)); - tv_scale(3, &afhi->chunk_tv, &afhi->eof_tv); - PARA_DEBUG_LOG("eof timeout: %lu\n", tv2ms(&afhi->eof_tv)); - afhi->info_string = make_message("%s: %cbr, %s\n%s", - status_item_list[SI_AUDIO_FILE_INFO], vbr? 'v' : 'c', - header_mode(&header), taginfo); - free(taginfo); + afhi->techinfo = make_message("%cbr, %s", vbr? 'v' : 'c', + header_mode(&header)); + mp3_get_id3(map, numbytes, fd, &afhi->tags); return 1; err_out: - free(taginfo); PARA_ERROR_LOG("%s\n", para_strerror(-ret)); free(afhi->chunk_table); return ret; diff --git a/mp3dec_filter.c b/mp3dec_filter.c index 24123bc6..3344ea41 100644 --- a/mp3dec_filter.c +++ b/mp3dec_filter.c @@ -1,19 +1,23 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file mp3dec_filter.c Paraslash's mp3 decoder. */ +#include +#include +#include + #include "para.h" #include "mp3dec_filter.cmdline.h" #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "error.h" -#include #include "string.h" /** Convert a sample value from libmad to a signed short. */ @@ -26,6 +30,7 @@ enum mp3dec_flags { MP3DEC_FLAG_BAD_DATA = 1, /** Some output has already been produced. */ MP3DEC_FLAG_DECODE_STARTED = 2, + MP3DEC_FLAG_NEED_MORE = 4, }; /** Data specific to the mp3dec filter. */ @@ -42,6 +47,10 @@ struct private_mp3dec_data { struct timeval stream_start_barrier; /** Wait until this many input bytes are available. */ size_t input_len_barrier; + /** The number of channels of the current stream. */ + unsigned int channels; + /** Current sample rate in Hz. */ + unsigned int samplerate; }; static int need_bad_data_delay(struct private_mp3dec_data *pmd, @@ -64,6 +73,7 @@ static int need_bad_data_delay(struct private_mp3dec_data *pmd, */ static int handle_decode_error(struct private_mp3dec_data *pmd, size_t len) { + const struct timeval delay = {0, 60 * 1000}; if (!MAD_RECOVERABLE(pmd->stream.error) && pmd->stream.error != MAD_ERROR_BUFLEN) { PARA_ERROR_LOG("%s\n", mad_stream_errorstr(&pmd->stream)); @@ -80,86 +90,110 @@ static int handle_decode_error(struct private_mp3dec_data *pmd, size_t len) */ pmd->flags |= MP3DEC_FLAG_BAD_DATA; pmd->input_len_barrier = len; - tv_add(now, &(struct timeval){0, 60 * 1000}, - &pmd->stream_start_barrier); + tv_add(now, &delay, &pmd->stream_start_barrier); return 1; } -static ssize_t mp3dec(char *inbuffer, size_t len, struct filter_node *fn) +static size_t used_mad_buffer_bytes(struct mad_stream *s, size_t max) +{ + size_t rv; + + if (!s->next_frame) + return max; + /* we still have some data */ + rv = s->next_frame - s->buffer; + assert(rv <= max); + return rv; +} + +static void mp3dec_close(struct filter_node *fn) +{ + struct private_mp3dec_data *pmd = fn->private_data; + + mad_synth_finish(&pmd->synth); + mad_frame_finish(&pmd->frame); + mad_stream_finish(&pmd->stream); + + free(pmd); + fn->private_data = NULL; +} + +static void mp3dec_post_select(__a_unused struct sched *s, struct task *t) { + struct filter_node *fn = container_of(t, struct filter_node, task); int i, ret; struct private_mp3dec_data *pmd = fn->private_data; - size_t copy = PARA_MIN(len, (size_t)4096); + struct btr_node *btrn = fn->btrn; + size_t loaded, used, len, iqs; + char *inbuffer, *outbuffer; - if (fn->loaded + 16384 > fn->bufsize) - return 0; - if (need_bad_data_delay(pmd, len)) - return 0; - mad_stream_buffer(&pmd->stream, (unsigned char *) inbuffer, copy); +next_buffer: pmd->stream.error = 0; + t->error = 0; + iqs = btr_get_input_queue_size(btrn); + ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); + if (ret < 0) + goto err; + if (need_bad_data_delay(pmd, iqs)) + return; + if (ret == 0) + return; + btr_merge(btrn, fn->min_iqs); + len = btr_next_buffer(btrn, &inbuffer); + mad_stream_buffer(&pmd->stream, (unsigned char *)inbuffer, len); next_frame: ret = mad_header_decode(&pmd->frame.header, &pmd->stream); if (ret < 0) { - if (pmd->stream.error != MAD_ERROR_BUFLEN && - pmd->stream.error != MAD_ERROR_LOSTSYNC) + used = used_mad_buffer_bytes(&pmd->stream, len); + btr_consume(btrn, used); + if (pmd->stream.error == MAD_ERROR_BUFLEN) { + if (len == iqs && btr_no_parent(btrn)) { + ret = -E_MP3DEC_EOF; + goto err; + } + fn->min_iqs += 100; + goto next_buffer; + } else if (pmd->stream.error != MAD_ERROR_LOSTSYNC) PARA_DEBUG_LOG("header decode: %s\n", mad_stream_errorstr(&pmd->stream)); - goto out; + goto next_buffer; } - fn->fc->samplerate = pmd->frame.header.samplerate; - fn->fc->channels = MAD_NCHANNELS(&pmd->frame.header); + fn->min_iqs = 0; + pmd->samplerate = pmd->frame.header.samplerate; + pmd->channels = MAD_NCHANNELS(&pmd->frame.header); ret = mad_frame_decode(&pmd->frame, &pmd->stream); if (ret != 0) { - ret = handle_decode_error(pmd, len); + PARA_INFO_LOG("frame decode: %s\n", mad_stream_errorstr(&pmd->stream)); + used = used_mad_buffer_bytes(&pmd->stream, len); + ret = handle_decode_error(pmd, used); + btr_consume(btrn, used); if (ret < 0) - return ret; + goto err; if (ret == 0) - goto out; - ret = copy - (pmd->stream.bufend - pmd->stream.next_frame); - PARA_NOTICE_LOG("skipping %d input bytes\n", ret); - return ret; + goto next_buffer; + return; } mad_synth_frame(&pmd->synth, &pmd->frame); pmd->flags |= MP3DEC_FLAG_DECODE_STARTED; + outbuffer = para_malloc(pmd->synth.pcm.length * 4); + loaded = 0; for (i = 0; i < pmd->synth.pcm.length; i++) { - int s = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]); - write_int16_host_endian(fn->buf + fn->loaded, s); - fn->loaded += 2; + int sample = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]); + write_int16_host_endian(outbuffer + loaded, sample); + loaded += 2; if (MAD_NCHANNELS(&pmd->frame.header) == 2) { /* stereo */ - s = MAD_TO_SHORT(pmd->synth.pcm.samples[1][i]); - write_int16_host_endian(fn->buf + fn->loaded, s); - fn->loaded += 2; + sample = MAD_TO_SHORT(pmd->synth.pcm.samples[1][i]); + write_int16_host_endian(outbuffer + loaded, sample); + loaded += 2; } - if (fn->loaded != fn->bufsize) /* output buffer not full */ - continue; - PARA_ERROR_LOG("output buffer full: %zd\n", fn->loaded); - return -E_MP3DEC_OVERRUN; - } - if (fn->loaded + 16384 <= fn->bufsize) - goto next_frame; -out: - if (pmd->stream.next_frame) { /* we still have some data */ - size_t off = pmd->stream.bufend - pmd->stream.next_frame; - if (fn->loaded + 16384 <= fn->bufsize && off > 2048) - goto next_frame; - return copy - off; } - return copy; -} - -static void mp3dec_close(struct filter_node *fn) -{ - struct private_mp3dec_data *pmd = fn->private_data; - - mad_synth_finish(&pmd->synth); - mad_frame_finish(&pmd->frame); - mad_stream_finish(&pmd->stream); - - free(fn->buf); - fn->buf = NULL; - free(pmd); - fn->private_data = NULL; + btr_add_output(outbuffer, loaded, btrn); + goto next_frame; +err: + assert(ret < 0); + t->error = ret; + btr_remove_node(btrn); } static void mp3dec_open(struct filter_node *fn) @@ -171,9 +205,6 @@ static void mp3dec_open(struct filter_node *fn) mad_stream_init(&pmd->stream); mad_frame_init(&pmd->frame); mad_synth_init(&pmd->synth); - fn->loaded = 0; - fn->bufsize = mp3_conf->bufsize_arg * 1024; - fn->buf = para_calloc(fn->bufsize); if (mp3_conf->ignore_crc_given) mad_stream_options(&pmd->stream, MAD_OPTION_IGNORECRC); } @@ -187,11 +218,6 @@ static int mp3dec_parse_config(int argc, char **argv, void **config) ret = -E_MP3DEC_SYNTAX; if (mp3dec_cmdline_parser(argc, argv, mp3_conf)) goto err; - ret = -ERRNO_TO_PARA_ERROR(EINVAL); - if (mp3_conf->bufsize_arg < 32) - goto err; - if (mp3_conf->bufsize_arg >= INT_MAX / 1024) - goto err; *config = mp3_conf; return 1; err: @@ -199,6 +225,30 @@ err: return ret; } +static int mp3dec_execute(struct btr_node *btrn, const char *cmd, char **result) +{ + struct filter_node *fn = btr_context(btrn); + struct private_mp3dec_data *pmd = fn->private_data; + + if (!strcmp(cmd, "samplerate")) { + if (pmd->samplerate == 0) + return -E_BTR_NAVAIL; + *result = make_message("%u", pmd->samplerate); + return 1; + } + if (!strcmp(cmd, "channels")) { + if (pmd->channels == 0) + return -E_BTR_NAVAIL; + *result = make_message("%u", pmd->channels); + return 1; + } + return -ERRNO_TO_PARA_ERROR(ENOTSUP); +} + +static void mp3dec_free_config(void *conf) +{ + mp3dec_cmdline_parser_free(conf); +} /** * The init function of the mp3dec filter. * @@ -212,9 +262,12 @@ void mp3dec_filter_init(struct filter *f) mp3dec_cmdline_parser_init(&dummy); f->open = mp3dec_open; - f->convert = mp3dec; f->close = mp3dec_close; f->parse_config = mp3dec_parse_config; + f->free_config = mp3dec_free_config; + f->pre_select = generic_filter_pre_select; + f->post_select = mp3dec_post_select; + f->execute = mp3dec_execute; f->help = (struct ggo_help) { .short_help = mp3dec_filter_args_info_help, .detailed_help = mp3dec_filter_args_info_detailed_help diff --git a/net.c b/net.c index ab6a9894..92ae217d 100644 --- a/net.c +++ b/net.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -27,71 +27,16 @@ #include #include +#include #include "para.h" #include "error.h" +#include "crypt.h" #include "net.h" #include "string.h" +#include "list.h" #include "fd.h" - -/** Information about one encrypted connection. */ -struct crypt_data { - /** Function used to decrypt received data. */ - crypt_function *recv; - /** Function used to encrypt data to be sent. */ - crypt_function *send; - /** - * Context-dependent data (crypt keys), passed verbatim to the above - * crypt functions. - */ - void *private_data; -}; -/** Array holding per fd crypt data. */ -static struct crypt_data *crypt_data_array; -/** Current size of the crypt data array. */ -static unsigned cda_size = 0; - -/** - * Activate encryption for one file descriptor. - * - * \param fd The file descriptor. - * \param recv_f The function used for decrypting received data. - * \param send_f The function used for encrypting before sending. - * \param private_data User data supplied by the caller. - */ -void enable_crypt(int fd, crypt_function *recv_f, crypt_function *send_f, - void *private_data) -{ - if (fd + 1 > cda_size) { - crypt_data_array = para_realloc(crypt_data_array, - (fd + 1) * sizeof(struct crypt_data)); - memset(crypt_data_array + cda_size, 0, - (fd + 1 - cda_size) * sizeof(struct crypt_data)); - cda_size = fd + 1; - } - crypt_data_array[fd].recv = recv_f; - crypt_data_array[fd].send = send_f; - crypt_data_array[fd].private_data = private_data; - PARA_INFO_LOG("rc4 encryption activated for fd %d\n", fd); -} - -/** - * Deactivate encryption for a given fd. - * - * \param fd The file descriptor. - * - * This must be called if and only if \p fd was activated via enable_crypt(). - */ -void disable_crypt(int fd) -{ - if (cda_size < fd + 1) - return; - crypt_data_array[fd].recv = NULL; - crypt_data_array[fd].send = NULL; - crypt_data_array[fd].private_data = NULL; -} - /** * Parse and validate IPv4 address/netmask string. * @@ -144,7 +89,8 @@ static bool is_v4_dot_quad(const char *address) bool result; regex_t r; - assert(!regcomp(&r, "^([0-9]+\\.){3}[0-9]+$", REG_EXTENDED|REG_NOSUB)); + assert(para_regcomp(&r, "^([0-9]+\\.){3}[0-9]+$", + REG_EXTENDED | REG_NOSUB) >= 0); result = regexec(&r, address, 0, NULL, 0) == 0; regfree(&r); return result; @@ -242,6 +188,31 @@ failed: return NULL; } +/** + * Stringify port number, resolve into service name where defined. + * \param port 2-byte port number, in host-byte-order. + * \param transport Transport protocol name (e.g. "udp", "tcp"), or NULL. + * \return Pointer to static result buffer. + * + * \sa getservent(3), services(5), nsswitch.conf(5) + */ +const char *stringify_port(int port, const char *transport) +{ + static char service[NI_MAXSERV]; + + if (port < 0 || port > 0xFFFF) { + snprintf(service, sizeof(service), "undefined (%d)", port); + } else { + struct servent *se = getservbyport(htons(port), transport); + + if (se == NULL) + snprintf(service, sizeof(service), "%u", port); + else + snprintf(service, sizeof(service), "%s", se->s_name); + } + return service; +} + /** * Determine the socket type for a given layer-4 protocol. * @@ -272,14 +243,119 @@ static const char *layer4_name(const unsigned l4type) return "UNKNOWN PROTOCOL"; } +/** + * Flowopts: Transport-layer independent encapsulation of socket options. + * + * These collect individual socket options into a queue, which is disposed of + * directly after makesock(). The 'pre_conn_opt' structure is for internal use + * only and should not be visible elsewhere. + * + * \sa setsockopt(2), makesock() + */ +struct pre_conn_opt { + int sock_level; /**< Second argument to setsockopt() */ + int sock_option; /**< Third argument to setsockopt() */ + char *opt_name; /**< Stringified \a sock_option */ + void *opt_val; /**< Fourth argument to setsockopt() */ + socklen_t opt_len; /**< Fifth argument to setsockopt() */ + + struct list_head node; /**< FIFO, as sockopt order matters. */ +}; + +/** FIFO list of pre-connection socket options to be set */ +struct flowopts { + struct list_head sockopts; +}; + +struct flowopts *flowopt_new(void) +{ + struct flowopts *new = para_malloc(sizeof(*new)); + + INIT_LIST_HEAD(&new->sockopts); + return new; +} + +/** + * Append new socket option to flowopt queue. + * + * \param fo The flowopt queue to append to. + * \param lev Level at which \a opt resides. + * \param opt New option to add. + * \param name Stringified name of \a opt. + * \param val The value to set \a opt to. + * \param len Length of \a val. + * + * \sa setsockopt(2) + */ +void flowopt_add(struct flowopts *fo, int lev, int opt, + char *name, const void *val, int len) +{ + struct pre_conn_opt *new = para_malloc(sizeof(*new)); + + new->sock_option = opt; + new->sock_level = lev; + new->opt_name = para_strdup(name); + + if (val == NULL) { + new->opt_val = NULL; + new->opt_len = 0; + } else { + new->opt_val = para_malloc(len); + new->opt_len = len; + memcpy(new->opt_val, val, len); + } + + list_add_tail(&new->node, &fo->sockopts); +} + +void flowopt_add_bool(struct flowopts *fo, int lev, int opt, + char *optname, bool on_or_off) +{ + int on = on_or_off; /* kernel takes 'int' */ + + flowopt_add(fo, lev, opt, optname, &on, sizeof(on)); +} + +/** Set the entire bunch of pre-connection options at once. */ +static void flowopt_setopts(int sockfd, struct flowopts *fo) +{ + struct pre_conn_opt *pc; + + if (fo == NULL) + return; + + list_for_each_entry(pc, &fo->sockopts, node) + if (setsockopt(sockfd, pc->sock_level, pc->sock_option, + pc->opt_val, pc->opt_len) < 0) { + PARA_EMERG_LOG("Can not set %s socket option: %s", + pc->opt_name, strerror(errno)); + exit(EXIT_FAILURE); + } +} + +static void flowopt_cleanup(struct flowopts *fo) +{ + struct pre_conn_opt *cur, *next; + + if (fo == NULL) + return; + + list_for_each_entry_safe(cur, next, &fo->sockopts, node) { + free(cur->opt_name); + free(cur->opt_val); + free(cur); + } + free(fo); +} + /** * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket. * - * \param l3type The layer-3 type (\p AF_INET, \p AF_INET6, \p AF_UNSPEC). * \param l4type The layer-4 type (\p IPPROTO_xxx). * \param passive Whether this is a passive (1) or active (0) socket. * \param host Remote or local hostname or IPv/6 address string. * \param port_number Decimal port number. + * \param fo Socket options to be set before making the connection. * * This creates a ready-made IPv4/v6 socket structure after looking up the * necessary parameters. The interpretation of \a host depends on the value of @@ -293,18 +369,20 @@ static const char *layer4_name(const unsigned l4type) * * Furthermore, bind(2) is called on passive sockets, and connect(2) on active * sockets. The algorithm tries all possible address combinations until it - * succeeds. + * succeeds. If \a fo is supplied, options are set and cleanup is performed. * * \return This function returns 1 on success and \a -E_ADDRESS_LOOKUP when no * matching connection could be set up (with details in the error log). * * \sa ipv6(7), getaddrinfo(3), bind(2), connect(2). */ -int makesock(unsigned l3type, unsigned l4type, int passive, - const char *host, unsigned short port_number) +int makesock(unsigned l4type, bool passive, + const char *host, uint16_t port_number, + struct flowopts *fo) { struct addrinfo *local = NULL, *src, *remote = NULL, *dst, hints; + unsigned int l3type = AF_UNSPEC; int rc, on = 1, sockfd = -1, socktype = sock_type(l4type); char port[6]; /* port number has at most 5 digits */ @@ -351,16 +429,19 @@ int makesock(unsigned l3type, unsigned l4type, int passive, goto get_next_dst; /* - * Set those options that need to be set before establishing - * the connection. Reuse the address on passive (listening) - * sockets to avoid failure on restart. + * Reuse the address on passive sockets to avoid failure on + * restart (protocols using listen()) and when creating + * multiple listener instances (UDP multicast). */ if (passive && setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)) == -1) { + &on, sizeof(on)) == -1) { + rc = errno; + close(sockfd); PARA_ERROR_LOG("can not set SO_REUSEADDR: %s\n", - strerror(errno)); - return -ERRNO_TO_PARA_ERROR(errno); + strerror(rc)); + return -ERRNO_TO_PARA_ERROR(rc); } + flowopt_setopts(sockfd, fo); if (src) { if (bind(sockfd, src->ai_addr, src->ai_addrlen) < 0) { @@ -385,12 +466,14 @@ get_next_src: freeaddrinfo(local); if (remote) freeaddrinfo(remote); + flowopt_cleanup(fo); if (src == NULL && dst == NULL) { + rc = errno; PARA_ERROR_LOG("can not create %s socket %s#%s.\n", layer4_name(l4type), host? host : (passive? "[loopback]" : "[localhost]"), port); - return -ERRNO_TO_PARA_ERROR(errno); + return -ERRNO_TO_PARA_ERROR(rc); } return sockfd; } @@ -398,24 +481,25 @@ get_next_src: /** * Create a passive / listening socket. * - * \param l3type The network-layer type (\p AF_xxx). * \param l4type The transport-layer type (\p IPPROTO_xxx). * \param port The decimal port number to listen on. + * \param fo Flowopts (if any) to set before starting to listen. * * \return Positive integer (socket descriptor) on success, negative value * otherwise. * * \sa makesock(), ip(7), ipv6(7), bind(2), listen(2). */ -int para_listen(unsigned l3type, unsigned l4type, unsigned short port) +int para_listen(unsigned l4type, uint16_t port, struct flowopts *fo) { - int ret, fd = makesock(l3type, l4type, 1, NULL, port); + int ret, fd = makesock(l4type, 1, NULL, port, fo); if (fd > 0) { ret = listen(fd, BACKLOG); if (ret < 0) { + ret = errno; close(fd); - return -ERRNO_TO_PARA_ERROR(errno); + return -ERRNO_TO_PARA_ERROR(ret); } PARA_INFO_LOG("listening on %s port %u, fd %d\n", layer4_name(l4type), port, fd); @@ -423,28 +507,70 @@ int para_listen(unsigned l3type, unsigned l4type, unsigned short port) return fd; } +/** + * Determine IPv4/v6 socket address length. + * \param sa Container of IPv4 or IPv6 address. + * \return Address-family dependent address length. + */ +static socklen_t salen(const struct sockaddr *sa) +{ + assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); + + return sa->sa_family == AF_INET6 + ? sizeof(struct sockaddr_in6) + : sizeof(struct sockaddr_in); +} + +/** + * Process IPv4/v6 address, turn v6-mapped-v4 address into normal IPv4 address. + * \param ss Container of IPv4/6 address. + * \return Pointer to normalized address (may be static storage). + * + * \sa RFC 3493 + */ +static const struct sockaddr * +normalize_ip_address(const struct sockaddr_storage *ss) +{ + const struct sockaddr_in6 *ia6 = (const struct sockaddr_in6 *)ss; + + assert(ss->ss_family == AF_INET || ss->ss_family == AF_INET6); + + if (ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&ia6->sin6_addr)) { + static struct sockaddr_in ia; + + ia.sin_family = AF_INET; + ia.sin_port = ia6->sin6_port; + memcpy(&ia.sin_addr.s_addr, &(ia6->sin6_addr.s6_addr[12]), 4); + return (const struct sockaddr *)&ia; + } + return (const struct sockaddr *)ss; +} + /** * Print numeric host and port number (beware - uses static char). * * \param sa The IPv4/IPv6 socket address to use. - * \param len The length of \p sa. * - * \sa getnameinfo(3). + * \sa getnameinfo(3), services(5), nsswitch.conf(5) */ -static char *host_and_port(struct sockaddr *sa, socklen_t len) +static char *host_and_port(const struct sockaddr_storage *ss) { - static char output[NI_MAXHOST + NI_MAXSERV + 2]; + const struct sockaddr *sa = normalize_ip_address(ss); char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + static char output[sizeof(hbuf) + sizeof(sbuf) + 2]; int ret; - ret = getnameinfo(sa, len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), - NI_NUMERICHOST | NI_NUMERICSERV); - if (ret) { + ret = getnameinfo(sa, salen(sa), + hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), + NI_NUMERICHOST); + if (ret == 0) { + snprintf(output, sizeof(output), "%s#%s", hbuf, sbuf); + } else { + snprintf(output, sizeof(output), "(unknown)"); PARA_WARNING_LOG("hostname lookup error (%s).\n", - gai_strerror(ret)); - sprintf(output, "(unknown)"); - } else - sprintf(output, "%s#%s", hbuf, sbuf); + gai_strerror(ret)); + } return output; } @@ -472,7 +598,7 @@ static char *__get_sock_name(int fd, int (*getname)(int, struct sockaddr*, fd, strerror(errno)); return dont_know; } - return host_and_port((struct sockaddr *)&ss, sslen); + return host_and_port(&ss); } /** @@ -515,54 +641,34 @@ char *remote_name(int sockfd) struct in_addr extract_v4_addr(const struct sockaddr_storage *ss) { struct in_addr ia = {.s_addr = 0}; + const struct sockaddr *sa = normalize_ip_address(ss); - if (ss->ss_family == AF_INET) - ia.s_addr = ((struct sockaddr_in *)ss)->sin_addr.s_addr; - if (ss->ss_family == AF_INET6) { - const struct in6_addr v6_addr = ((struct sockaddr_in6 *)ss)->sin6_addr; - - if (IN6_IS_ADDR_V4MAPPED(&v6_addr)) - memcpy(&ia.s_addr, &(v6_addr.s6_addr[12]), 4); - } + if (sa->sa_family == AF_INET) + ia = ((struct sockaddr_in *)sa)->sin_addr; return ia; } /** - * Encrypt and send a binary buffer. + * Send a binary buffer. * * \param fd The file descriptor. - * \param buf The buffer to be encrypted and sent. + * \param buf The buffer to be sent. * \param len The length of \a buf. * - * Check if encryption is available. If yes, encrypt the given buffer. Send - * out the buffer, encrypted or not, and try to resend the remaining part in - * case of short writes. + * Send out the buffer and try to resend the remaining part in case of short + * writes. * * \return Standard. */ int send_bin_buffer(int fd, const char *buf, size_t len) { - int ret; - crypt_function *cf = NULL; - if (!len) PARA_CRIT_LOG("len == 0\n"); - if (fd + 1 <= cda_size) - cf = crypt_data_array[fd].send; - if (cf) { - void *private = crypt_data_array[fd].private_data; - /* RC4 may write more than len to the output buffer */ - unsigned char *outbuf = para_malloc(ROUND_UP(len, 8)); - (*cf)(len, (unsigned char *)buf, outbuf, private); - ret = write_all(fd, (char *)outbuf, &len); - free(outbuf); - } else - ret = write_all(fd, buf, &len); - return ret; + return write_all(fd, buf, &len); } /** - * Encrypt and send null terminated buffer. + * Send a \p NULL-terminated buffer. * * \param fd The file descriptor. * \param buf The null-terminated buffer to be send. @@ -576,9 +682,8 @@ int send_buffer(int fd, const char *buf) return send_bin_buffer(fd, buf, strlen(buf)); } - /** - * Send and encrypt a buffer given by a format string. + * Send a buffer given by a format string. * * \param fd The file descriptor. * \param fmt A format string. @@ -597,51 +702,37 @@ __printf_2_3 int send_va_buffer(int fd, const char *fmt, ...) } /** - * Receive and decrypt. + * Receive data from a file descriptor. * * \param fd The file descriptor. - * \param buf The buffer to write the decrypted data to. + * \param buf The buffer to write the data to. * \param size The size of \a buf. * - * Receive at most \a size bytes from file descriptor \a fd. If encryption is - * available, decrypt the received buffer. + * Receive at most \a size bytes from file descriptor \a fd. * - * \return The number of bytes received on success, negative on errors. + * \return The number of bytes received on success, negative on errors, zero if + * the peer has performed an orderly shutdown. * - * \sa recv(2) + * \sa recv(2). */ __must_check int recv_bin_buffer(int fd, char *buf, size_t size) { ssize_t n; - crypt_function *cf = NULL; - - if (fd + 1 <= cda_size) - cf = crypt_data_array[fd].recv; - if (cf) { - unsigned char *tmp = para_malloc(size); - void *private = crypt_data_array[fd].private_data; - n = recv(fd, tmp, size, 0); - if (n > 0) { - size_t numbytes = n; - unsigned char *b = (unsigned char *)buf; - (*cf)(numbytes, tmp, b, private); - } - free(tmp); - } else - n = recv(fd, buf, size, 0); + + n = recv(fd, buf, size, 0); if (n == -1) return -ERRNO_TO_PARA_ERROR(errno); return n; } /** - * Receive, decrypt and write terminating NULL byte. + * Receive and write terminating NULL byte. * * \param fd The file descriptor. - * \param buf The buffer to write the decrypted data to. + * \param buf The buffer to write the data to. * \param size The size of \a buf. * - * Read and decrypt at most \a size - 1 bytes from file descriptor \a fd and + * Read at most \a size - 1 bytes from file descriptor \a fd and * write a NULL byte at the end of the received data. * * \return The return value of the underlying call to \a recv_bin_buffer(). @@ -684,6 +775,38 @@ int para_accept(int fd, void *addr, socklen_t size) return new_fd < 0? -ERRNO_TO_PARA_ERROR(errno) : new_fd; } +/** + * Probe the list of DCCP CCIDs configured on this host. + * \param ccid_array Pointer to return statically allocated array in. + * \return Number of elements returned in \a ccid_array or error. + * + * NB: This feature is only available on Linux > 2.6.30; on older kernels + * ENOPROTOOPT ("Protocol not available") will be returned. + */ +int dccp_available_ccids(uint8_t **ccid_array) +{ + static uint8_t ccids[DCCP_MAX_HOST_CCIDS]; + socklen_t nccids = sizeof(ccids); + int ret, fd; + + ret = fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL); + if (ret < 0) + return ret; + + if (getsockopt(fd, SOL_DCCP, DCCP_SOCKOPT_AVAILABLE_CCIDS, + ccids, &nccids) < 0) { + ret = errno; + close(fd); + PARA_ERROR_LOG("No DCCP_SOCKOPT_AVAILABLE_CCIDS: %s\n", + strerror(ret)); + return -ERRNO_TO_PARA_ERROR(ret); + } + + close(fd); + *ccid_array = ccids; + return nccids; +} + /** * Prepare a structure for \p AF_UNIX socket addresses. * @@ -923,10 +1046,11 @@ int recv_pattern(int fd, const char *pattern, size_t bufsize) ret = 1; out: if (ret < 0) { - PARA_NOTICE_LOG("n = %d, did not receive pattern '%s'\n", n, - pattern); + PARA_NOTICE_LOG("did not receive pattern '%s'\n", pattern); if (n > 0) - PARA_NOTICE_LOG("recvd: %s\n", buf); + PARA_NOTICE_LOG("recvd %d bytes: %s\n", n, buf); + else if (n < 0) + PARA_NOTICE_LOG("%s\n", para_strerror(-n)); } free(buf); return ret; diff --git a/net.h b/net.h index 8b706178..93c0c5e8 100644 --- a/net.h +++ b/net.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -17,14 +17,52 @@ #define UNIX_PATH_MAX 108 #endif -/** \cond Userland defines for Linux DCCP support. */ +/* Userland defines for Linux DCCP support. */ + #ifndef IPPROTO_DCCP -#define IPPROTO_DCCP 33 /**< IANA assigned value */ -#define SOCK_DCCP 6 /**< Linux socket type */ -#define SOL_DCCP 269 /**< Linux socket level */ +#define IPPROTO_DCCP 33 /**< IANA assigned value. */ #endif -/** \endcond */ +#ifndef SOCK_DCCP +#define SOCK_DCCP 6 /**< Linux socket type. */ +#endif + +#ifndef DCCP_SOCKOPT_RX_CCID +/** Per-connection CCID support (set/get the RX CCID, since v2.6.30-rc1). */ +#define DCCP_SOCKOPT_RX_CCID 15 +#endif + +#ifndef SOL_DCCP +#define SOL_DCCP 269 /**< Linux socket level. */ +#endif + +#ifndef DCCP_SOCKOPT_AVAILABLE_CCIDS +#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 /**< List of supported CCIDs. */ +#endif + +#ifndef DCCP_SOCKOPT_CCID +#define DCCP_SOCKOPT_CCID 13 /**< Sets both TX/RX CCID. */ +#endif + +#ifndef DCCP_SOCKOPT_TX_CCID +#define DCCP_SOCKOPT_TX_CCID 14 /**< Set/get the TX CCID. */ +#endif + +/** + * Flowopts: Transport-layer independent encapsulation of socket options + * that need to be registered prior to setting up a connection. + */ +struct flowopts; + +extern struct flowopts *flowopt_new(void); +extern void flowopt_add(struct flowopts *fo, int level, int opt, + char *name, const void *val, int len); +extern void flowopt_add_bool(struct flowopts *fo, int lev, int opt, + char *optname, bool on_or_off); +/** Flowopt shortcut macros */ +#define OPT_ADD(fo, lev, opt, val, len) flowopt_add(fo, lev, opt, #opt, val, len) +#define OPT_ENABLE(fo, lev, opt) flowopt_add_bool(fo, lev, opt, #opt, 1) +#define OPT_DISABLE(fo, lev, opt) flowopt_add_bool(fo, lev, opt, #opt, 0) /** * Functions to parse and validate (parts of) URLs. @@ -33,6 +71,7 @@ extern char *parse_cidr(const char *cidr, char *addr, ssize_t addrlen, int32_t *netmask); extern char *parse_url(const char *url, char *host, ssize_t hostlen, int32_t *port); +extern const char *stringify_port(int port, const char *transport); /** * Ensure that string conforms to the IPv4 address format. * @@ -65,8 +104,16 @@ _static_inline_ bool is_valid_ipv6_address(const char *address) /** * Generic socket creation (passive and active sockets). */ -extern int makesock(unsigned l3type, unsigned l4type, int passive, - const char *host, unsigned short port_number); +extern int makesock(unsigned l4type, bool passive, + const char *host, uint16_t port_number, + struct flowopts *fo); + +static inline int para_connect_simple(unsigned l4type, + const char *host, uint16_t port) +{ + return makesock(l4type, 0, host, port, NULL); +} + extern struct in_addr extract_v4_addr(const struct sockaddr_storage *ss); /** @@ -74,21 +121,24 @@ extern struct in_addr extract_v4_addr(const struct sockaddr_storage *ss); */ /** How many pending connections queue of a listening server will hold. */ #define BACKLOG 10 -extern int para_listen(unsigned l3type, unsigned l4type, unsigned short port); +extern int para_listen(unsigned l4type, uint16_t port, struct flowopts *fo); + +static inline int para_listen_simple(unsigned l4type, uint16_t port) +{ + return para_listen(l4type, port, NULL); +} /** Pretty-printing of IPv4/6 socket addresses */ extern char *local_name(int sockfd); extern char *remote_name(int sockfd); -/** used to crypt the communication between para_server and para_client */ -typedef void crypt_function(unsigned long len, - const unsigned char *indata, unsigned char *outdata, void *private_data); - -int send_buffer(int, const char *); int send_bin_buffer(int, const char *, size_t); +int send_buffer(int, const char *); __printf_2_3 int send_va_buffer(int fd, const char *fmt, ...); -int recv_buffer(int fd, char *buf, size_t size); + int recv_bin_buffer(int fd, char *buf, size_t size); +int recv_buffer(int fd, char *buf, size_t size); + int para_accept(int, void *addr, socklen_t size); int create_local_socket(const char *name, struct sockaddr_un *unix_addr, mode_t mode); @@ -96,6 +146,10 @@ int create_remote_socket(const char *name); int recv_cred_buffer(int, char *, size_t); ssize_t send_cred_buffer(int, char*); int recv_pattern(int fd, const char *pattern, size_t bufsize); -void enable_crypt(int fd, crypt_function *recv_f, crypt_function *send_f, - void *private_data); -void disable_crypt(int fd); + +/** + * Functions and definitions to support \p IPPROTO_DCCP + */ +/** Hardcoded maximum number of separate CCID modules compiled into a host */ +#define DCCP_MAX_HOST_CCIDS 20 +extern int dccp_available_ccids(uint8_t **ccid_array); diff --git a/ogg_afh.c b/ogg_afh.c index 11214c89..4b44ea16 100644 --- a/ogg_afh.c +++ b/ogg_afh.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009 Andre Noll + * Copyright (C) 2004-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -9,6 +9,7 @@ #include #include #include +#include #include "para.h" #include "afh.h" @@ -60,7 +61,6 @@ static int read_vorbis_info(ogg_sync_state *oss, struct afh_info *afhi) ogg_stream_state stream; ogg_page page; int ret; - char *taginfo; vorbis_info_init(&vi); vorbis_comment_init(&vc); @@ -93,17 +93,11 @@ static int read_vorbis_info(ogg_sync_state *oss, struct afh_info *afhi) ret = read_vorbis_comment(oss, &stream, &vi, &vc); if (ret < 0) goto out; - taginfo = make_taginfo( - vorbis_comment_query(&vc, "title", 0), - vorbis_comment_query(&vc, "artist", 0), - vorbis_comment_query(&vc, "album", 0), - vorbis_comment_query(&vc, "year", 0), - vorbis_comment_query(&vc, "comment", 0) - ); - PARA_DEBUG_LOG("tag info: %s\n", taginfo); - afhi->info_string = make_message("%s:\n%s", - status_item_list[SI_AUDIO_FILE_INFO], taginfo); - free(taginfo); + afhi->tags.artist = para_strdup(vorbis_comment_query(&vc, "artist", 0)); + afhi->tags.title = para_strdup(vorbis_comment_query(&vc, "title", 0)); + afhi->tags.album = para_strdup(vorbis_comment_query(&vc, "album", 0)); + afhi->tags.year = para_strdup(vorbis_comment_query(&vc, "year", 0)); + afhi->tags.comment = para_strdup(vorbis_comment_query(&vc, "comment", 0)); afhi->header_offset = 0; afhi->header_len = oss->returned; @@ -111,7 +105,7 @@ static int read_vorbis_info(ogg_sync_state *oss, struct afh_info *afhi) out: vorbis_info_clear(&vi); vorbis_comment_clear(&vc); - ogg_stream_clear(&stream); + //ogg_stream_clear(&stream); return ret; } @@ -138,7 +132,6 @@ static int ogg_get_file_info(char *map, size_t numbytes, __a_unused int fd, int ret, i, j, frames_per_chunk, ct_size; long long unsigned num_frames = 0; - afhi->info_string = NULL; ogg_sync_init(&oss); ret = -E_OGG_SYNC; buf = ogg_sync_buffer(&oss, len); @@ -187,7 +180,6 @@ static int ogg_get_file_info(char *map, size_t numbytes, __a_unused int fd, } afhi->chunks_total = j; set_chunk_tv(num_frames, j, afhi->frequency, &afhi->chunk_tv); - tv_scale(3, &afhi->chunk_tv, &afhi->eof_tv); ret = 0; out: ogg_sync_clear(&oss); diff --git a/oggdec_filter.c b/oggdec_filter.c index 7155a54f..edd33c1d 100644 --- a/oggdec_filter.c +++ b/oggdec_filter.c @@ -1,23 +1,25 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file oggdec_filter.c Paraslash's ogg vorbis decoder. */ -#include "para.h" +#include +#include +#include +#include "para.h" #include "oggdec_filter.cmdline.h" #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "error.h" #include "string.h" -#include - /** Determine byte sex. */ #ifdef WORDS_BIGENDIAN #define ENDIAN 1 @@ -29,35 +31,36 @@ struct private_oggdec_data { /** Describes an ogg vorbis file. */ OggVorbis_File *vf; - /** The input buffer. */ - char *inbuf; - /** The length of \a inbuf. */ - size_t inbuf_len; /** The number of bytes consumed from the input buffer. */ size_t converted; - /** When to start producing output. */ - struct timeval stream_start; + /** The number of channels of the current stream. */ + unsigned int channels; + /** Current sample rate in Hz. */ + unsigned int samplerate; }; static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource) { struct filter_node *fn = datasource; struct private_oggdec_data *pod = fn->private_data; - size_t ret, have = pod->inbuf_len - pod->converted; - char *p = pod->inbuf + pod->converted; - -// PARA_DEBUG_LOG("pod = %p\n", pod); -// PARA_DEBUG_LOG("vorbis requests %d bytes, have %d\n", size * nmemb, have); - if (pod->inbuf_len < size) { - if (*fn->fc->input_error) - return 0; - errno = EAGAIN; - return (size_t)-1; - } - ret = PARA_MIN(nmemb, have / size) * size; - memcpy(buf, p, ret); - pod->converted += ret; - return ret; + struct btr_node *btrn = fn->btrn; + char *btr_buf; + size_t nbytes = btr_next_buffer(btrn, &btr_buf), tmp; + + /** + * oggvorbis always uses size == 1. Other sizes would complicate the code + * for no real gain. So we simply don't support size != 1. + */ + assert(size == 1); + assert(pod->converted <= nbytes); + tmp = nbytes - pod->converted; + PARA_DEBUG_LOG("vorbis requests %zu bytes have %zu\n", nmemb, tmp); + tmp = PARA_MIN(tmp, nmemb); + if (tmp == 0) + return 0; + memcpy(buf, btr_buf + pod->converted, tmp); + pod->converted += tmp; + return tmp; } /* @@ -92,11 +95,9 @@ static void ogg_open(struct filter_node *fn) { struct private_oggdec_data *pod = para_calloc( sizeof(struct private_oggdec_data)); - struct oggdec_filter_args_info *conf = fn->conf; fn->private_data = pod; - fn->bufsize = conf->bufsize_arg * 1024; - fn->buf = para_malloc(fn->bufsize); + fn->min_iqs = 8000; } static void ogg_close(struct filter_node *fn) @@ -108,83 +109,143 @@ static void ogg_close(struct filter_node *fn) free(pod->vf); pod->vf = NULL; } else - PARA_DEBUG_LOG("nothing to close in fc %p, pod = %p\n", pod->vf, pod); - free(fn->buf); - fn->buf = NULL; + PARA_DEBUG_LOG("nothing to close\n"); free(fn->private_data); fn->private_data = NULL; } -static ssize_t ogg_convert(char *inbuffer, size_t len, struct filter_node *fn) +#define OGGDEC_OUTPUT_CHUNK_SIZE (64 * 1024) + +static int oggdec_execute(struct btr_node *btrn, const char *cmd, char **result) { - ssize_t ret; + struct filter_node *fn = btr_context(btrn); struct private_oggdec_data *pod = fn->private_data; - struct oggdec_filter_args_info *conf = fn->conf; - /* make the buffer known to the read callback cb_read() */ - pod->inbuf = inbuffer; - pod->inbuf_len = len; - pod->converted = 0; - if (!pod->vf) { - if (*fn->fc->input_error < 0) - return *fn->fc->input_error; - if (!len) - return 0; - pod->vf = para_malloc(sizeof(struct OggVorbis_File)); - PARA_NOTICE_LOG("input buffer: %zd, opening ov callbacks\n", len); - ret = ov_open_callbacks(fn, pod->vf, - NULL, /* no initial buffer */ - 0, /* no initial bytes */ - ovc); /* the ov_open_callbacks */ - if (ret == OV_ENOTVORBIS || ret == OV_EBADHEADER) { - /* this might be due to the input buffer being too small */ - int ib = 1024 * conf->initial_buffer_arg; /* initial buffer */ - if (len < ib) { - PARA_INFO_LOG("initial input buffer %zd/%d, " - "waiting for more data\n", len, ib); - free(pod->vf); - pod->vf = NULL; - return 0; - } - return ret == OV_ENOTVORBIS? - -E_OGGDEC_NOTVORBIS : -E_OGGDEC_BADHEADER; + if (!strcmp(cmd, "samplerate")) { + if (pod->samplerate == 0) + return -E_BTR_NAVAIL; + *result = make_message("%u", pod->samplerate); + return 1; + } + if (!strcmp(cmd, "channels")) { + if (pod->channels == 0) + return -E_BTR_NAVAIL; + *result = make_message("%u", pod->channels); + return 1; + } + return -ERRNO_TO_PARA_ERROR(ENOTSUP); +} + +static int ogg_init(struct filter_node *fn) +{ + struct private_oggdec_data *pod = fn->private_data; + struct btr_node *btrn = fn->btrn; + int ret, oret; + size_t iqs; + + pod->vf = para_malloc(sizeof(struct OggVorbis_File)); + PARA_NOTICE_LOG("iqs: %zu, min_iqs: %zu, opening ov callbacks\n", + btr_get_input_queue_size(btrn), fn->min_iqs); +open: + oret = ov_open_callbacks(fn, pod->vf, + NULL, /* no initial buffer */ + 0, /* no initial bytes */ + ovc); /* the ov_open_callbacks */ + if (oret == OV_ENOTVORBIS || oret == OV_EBADHEADER) { + /* this might be due to the input buffer being too small */ + if (!btr_no_parent(btrn)) { + fn->min_iqs += 1000; + iqs = btr_get_input_queue_size(btrn); + ret = 0; + if (iqs < fn->min_iqs) + goto out; + PARA_CRIT_LOG("iqs: %zu\n", iqs); + btr_merge(btrn, fn->min_iqs); + pod->converted = 0; + goto open; } - if (ret == OV_EREAD) - return -E_OGGDEC_READ; - if (ret == OV_EVERSION) - return -E_OGGDEC_VERSION; - if (ret < 0) - return -E_OGGDEC_FAULT; - fn->fc->channels = ov_info(pod->vf, 0)->channels; - fn->fc->samplerate = ov_info(pod->vf, 0)->rate; - PARA_NOTICE_LOG("%d channels, %d Hz\n", fn->fc->channels, - fn->fc->samplerate); - /* wait a bit to avoid buffer underruns */ - tv_add(now, &(struct timeval){0, 500 * 1000}, &pod->stream_start); - return pod->converted; + ret = (oret == OV_ENOTVORBIS)? + -E_OGGDEC_NOTVORBIS : -E_OGGDEC_BADHEADER; + goto out; } - if (tv_diff(now, &pod->stream_start, NULL) < 0) { - PARA_DEBUG_LOG("initial delay..\n"); - return 0; + ret = -E_OGGDEC_READ; + if (oret == OV_EREAD) + goto out; + ret = -E_OGGDEC_VERSION; + if (oret == OV_EVERSION) + goto out; + ret = -E_OGGDEC_FAULT; + if (oret < 0) + goto out; + pod->channels = ov_info(pod->vf, 0)->channels; + pod->samplerate = ov_info(pod->vf, 0)->rate; + PARA_NOTICE_LOG("%d channels, %d Hz\n", pod->channels, + pod->samplerate); + ret = 1; +out: + if (ret <= 0) { + free(pod->vf); + pod->vf = NULL; + } else { + btr_consume(btrn, pod->converted); + pod->converted = 0; + fn->min_iqs = 0; + } + return ret; +} + +static void ogg_post_select(__a_unused struct sched *s, struct task *t) +{ + struct filter_node *fn = container_of(t, struct filter_node, task); + struct private_oggdec_data *pod = fn->private_data; + struct btr_node *btrn = fn->btrn; + size_t iqs, len; + int ret; + char *in; + + pod->converted = 0; + t->error = 0; + ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); + if (ret <= 0) + goto out; + btr_merge(btrn, fn->min_iqs); + len = btr_next_buffer(btrn, &in); + iqs = btr_get_input_queue_size(btrn); + if (!pod->vf) { + ret = ogg_init(fn); + if (ret <= 0) + goto out; } - while (fn->loaded < fn->bufsize) { - int length = fn->bufsize - fn->loaded; - long read_ret = ov_read(pod->vf, fn->buf + fn->loaded, length, + for (;;) { + char *out = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE); + ssize_t read_ret = ov_read(pod->vf, out, OGGDEC_OUTPUT_CHUNK_SIZE, ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL); - if (read_ret == 0) - return pod->converted; - if (read_ret == OV_HOLE) { - if (!fn->loaded) { - PARA_INFO_LOG("hole, delaying playback\n"); - tv_add(now, &(struct timeval){0, 500 * 1000}, &pod->stream_start); - } - return pod->converted; + btr_consume(btrn, pod->converted); + pod->converted = 0; + if (read_ret <= 0) + free(out); + if (read_ret == 0) { + if (btr_no_parent(btrn)) + ret = -E_OGGDEC_EOF; + else + ret = 0; + goto out; } + ret = 0; + if (read_ret == OV_HOLE) + goto out; + ret = -E_OGGDEC_BADLINK; if (read_ret < 0) - return -E_OGGDEC_BADLINK; - fn->loaded += read_ret; + goto out; + btr_add_output(out, read_ret, btrn); + if (btr_get_output_queue_size(btrn) > 640 * 1024) + return; /* enough data for the moment */ + } +out: + if (ret < 0) { + t->error = ret; + btr_remove_node(btrn); } - return pod->converted; } static int oggdec_parse_config(int argc, char **argv, void **config) @@ -212,6 +273,11 @@ err: return ret; } +static void oggdec_free_config(void *conf) +{ + oggdec_cmdline_parser_free(conf); +} + /** * The init function of the ogg vorbis decoder. * @@ -224,8 +290,11 @@ void oggdec_filter_init(struct filter *f) oggdec_cmdline_parser_init(&dummy); f->open = ogg_open; f->close = ogg_close; - f->convert = ogg_convert; + f->pre_select = generic_filter_pre_select; + f->post_select = ogg_post_select; f->parse_config = oggdec_parse_config; + f->free_config = oggdec_free_config; + f->execute = oggdec_execute; f->help = (struct ggo_help) { .short_help = oggdec_filter_args_info_help, .detailed_help = oggdec_filter_args_info_detailed_help diff --git a/osl.c b/osl.c deleted file mode 100644 index e2c1ef46..00000000 --- a/osl.c +++ /dev/null @@ -1,2082 +0,0 @@ -/* - * Copyright (C) 2007-2009 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file osl.c Object storage layer functions. */ -#include /* readdir() */ -#include - - -#include "para.h" -#include "error.h" -#include "fd.h" -#include "osl_core.h" -/** - * A wrapper for lseek(2). - * - * \param fd The file descriptor whose offset is to be to repositioned. - * \param offset A value-result parameter. - * \param whence Usual repositioning directive. - * - * Reposition the offset of the file descriptor \a fd to the argument \a offset - * according to the directive \a whence. Upon successful return, \a offset - * contains the resulting offset location as measured in bytes from the - * beginning of the file. - * - * \return Positive on success. Otherwise, the function returns \p -E_LSEEK. - * - * \sa lseek(2). - */ -int para_lseek(int fd, off_t *offset, int whence) -{ - int ret = -E_LSEEK; - - *offset = lseek(fd, *offset, whence); - if (*offset == -1) - return ret; - return 1; -} - -/** - * Wrapper for the write system call. - * - * \param fd The file descriptor to write to. - * \param buf The buffer to write. - * \param size The length of \a buf in bytes. - * - * This function writes out the given buffer and retries if an interrupt - * occurred during the write. - * - * \return On success, the number of bytes written is returned, otherwise, the - * function returns \p -E_WRITE. - * - * \sa write(2). - */ -ssize_t para_write(int fd, const void *buf, size_t size) -{ - ssize_t ret; - - for (;;) { - ret = write(fd, buf, size); - if ((ret < 0) && (errno == EAGAIN || errno == EINTR)) - continue; - return ret >= 0? ret : -E_WRITE; - } -} - -/** - * Write the whole buffer to a file descriptor. - * - * \param fd The file descriptor to write to. - * \param buf The buffer to write. - * \param size The length of \a buf in bytes. - * - * This function writes the given buffer and continues on short writes and - * when interrupted by a signal. - * - * \return Positive on success, negative on errors. Possible errors: any - * errors returned by para_write(). - * - * \sa para_write(). - */ -ssize_t para_write_all(int fd, const void *buf, size_t size) -{ -// PARA_DEBUG_LOG("writing %zu bytes\n", size); - const char *b = buf; - while (size) { - ssize_t ret = para_write(fd, b, size); -// PARA_DEBUG_LOG("ret: %zd\n", ret); - if (ret < 0) - return ret; - b += ret; - size -= ret; - } - return 1; -} -/** - * Open a file, write the given buffer and close the file. - * - * \param filename Full path to the file to open. - * \param buf The buffer to write to the file. - * \param size The size of \a buf. - * - * \return Positive on success, negative on errors. Possible errors include: - * any errors from para_open() or para_write(). - * - * \sa para_open(), para_write(). - */ -int para_write_file(const char *filename, const void *buf, size_t size) -{ - int ret, fd; - - ret = para_open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644); - if (ret < 0) - return ret; - fd = ret; - ret = para_write_all(fd, buf, size); - if (ret < 0) - goto out; - ret = 1; -out: - close(fd); - return ret; -} - -static int append_file(const char *filename, char *header, size_t header_size, - char *data, size_t data_size, uint32_t *new_pos) -{ - int ret, fd; - -// PARA_DEBUG_LOG("appending %zu + %zu bytes\n", header_size, data_size); - ret = para_open(filename, O_WRONLY | O_CREAT | O_APPEND, 0644); - if (ret < 0) - return ret; - fd = ret; - if (header && header_size) { - ret = para_write_all(fd, header, header_size); - if (ret < 0) - goto out; - } - ret = para_write_all(fd, data, data_size); - if (ret < 0) - goto out; - if (new_pos) { - off_t offset = 0; - ret = para_lseek(fd, &offset, SEEK_END); - if (ret < 0) - goto out; -// PARA_DEBUG_LOG("new file size: " FMT_OFF_T "\n", offset); - *new_pos = offset; - } - ret = 1; -out: - close(fd); - return ret; -} - -/** - * Traverse the given directory recursively. - * - * \param dirname The directory to traverse. - * \param func The function to call for each entry. - * \param private_data Pointer to an arbitrary data structure. - * - * For each regular file under \a dirname, the supplied function \a func is - * called. The full path of the regular file and the \a private_data pointer - * are passed to \a func. Directories for which the calling process has no - * permissions to change to are silently ignored. - * - * \return Standard. - */ -int for_each_file_in_dir(const char *dirname, - int (*func)(const char *, void *), void *private_data) -{ - DIR *dir; - struct dirent *entry; - int cwd_fd, ret2, ret = para_opendir(dirname, &dir, &cwd_fd); - - if (ret < 0) - return ret == -ERRNO_TO_PARA_ERROR(EACCES)? 1 : ret; - /* scan cwd recursively */ - while ((entry = readdir(dir))) { - mode_t m; - char *tmp; - struct stat s; - - if (!strcmp(entry->d_name, ".")) - continue; - if (!strcmp(entry->d_name, "..")) - continue; - if (lstat(entry->d_name, &s) == -1) - continue; - m = s.st_mode; - if (!S_ISREG(m) && !S_ISDIR(m)) - continue; - tmp = make_message("%s/%s", dirname, entry->d_name); - if (!S_ISDIR(m)) { - ret = func(tmp, private_data); - free(tmp); - if (ret < 0) - goto out; - continue; - } - /* directory */ - ret = for_each_file_in_dir(tmp, func, private_data); - free(tmp); - if (ret < 0) - goto out; - } - ret = 1; -out: - closedir(dir); - ret2 = para_fchdir(cwd_fd); - if (ret2 < 0 && ret >= 0) - ret = ret2; - close(cwd_fd); - return ret; -} - -static int verify_name(const char *name) -{ - if (!name) - return -E_BAD_NAME; - if (!*name) - return -E_BAD_NAME; - if (strchr(name, '/')) - return -E_BAD_NAME; - if (!strcmp(name, "..")) - return -E_BAD_NAME; - if (!strcmp(name, ".")) - return -E_BAD_NAME; - return 1; -} - -/** - * Compare two osl objects pointing to unsigned integers of 32 bit size. - * - * \param obj1 Pointer to the first integer. - * \param obj2 Pointer to the second integer. - * - * \return The values required for an osl compare function. - * - * \sa osl_compare_func, osl_hash_compare(). - */ -int uint32_compare(const struct osl_object *obj1, const struct osl_object *obj2) -{ - uint32_t d1 = read_u32((const char *)obj1->data); - uint32_t d2 = read_u32((const char *)obj2->data); - - if (d1 < d2) - return 1; - if (d1 > d2) - return -1; - return 0; -} - -/** - * Compare two osl objects pointing to hash values. - * - * \param obj1 Pointer to the first hash object. - * \param obj2 Pointer to the second hash object. - * - * \return The values required for an osl compare function. - * - * \sa osl_compare_func, uint32_compare(). - */ -int osl_hash_compare(const struct osl_object *obj1, const struct osl_object *obj2) -{ - return hash_compare((HASH_TYPE *)obj1->data, (HASH_TYPE *)obj2->data); -} - -static char *disk_storage_dirname(const struct osl_table *t, unsigned col_num, - const char *ds_name) -{ - char *dirname, *column_name = column_filename(t, col_num); - - if (!(t->desc->flags & OSL_LARGE_TABLE)) - return column_name; - dirname = make_message("%s/%.2s", column_name, ds_name); - free(column_name); - return dirname; -} - -static char *disk_storage_name_of_object(const struct osl_table *t, - const struct osl_object *obj) -{ - HASH_TYPE hash[HASH_SIZE]; - hash_object(obj, hash); - return disk_storage_name_of_hash(t, hash); -} - -static int disk_storage_name_of_row(const struct osl_table *t, - const struct osl_row *row, char **name) -{ - struct osl_object obj; - int ret = osl_get_object(t, row, t->disk_storage_name_column, &obj); - - if (ret < 0) - return ret; - *name = disk_storage_name_of_object(t, &obj); - return 1; -} - -static void column_name_hash(const char *col_name, HASH_TYPE *hash) -{ - hash_function(col_name, strlen(col_name), hash); -} - -static int init_column_descriptions(struct osl_table *t) -{ - int i, j, ret; - const struct osl_column_description *cd; - - ret = verify_name(t->desc->name); - if (ret < 0) - goto err; - ret = -E_BAD_DB_DIR; - if (!t->desc->dir && (t->num_disk_storage_columns || t->num_mapped_columns)) - goto err; - /* the size of the index header without column descriptions */ - t->index_header_size = IDX_COLUMN_DESCRIPTIONS; - FOR_EACH_COLUMN(i, t->desc, cd) { - struct osl_column *col = t->columns + i; - if (cd->storage_flags & OSL_RBTREE) { - if (!cd->compare_function) - return -E_NO_COMPARE_FUNC; - } - if (cd->storage_type == OSL_NO_STORAGE) - continue; - ret = -E_NO_COLUMN_NAME; - if (!cd->name || !cd->name[0]) - goto err; - ret = verify_name(cd->name); - if (ret < 0) - goto err; - t->index_header_size += index_column_description_size(cd->name); - column_name_hash(cd->name, col->name_hash); - ret = -E_DUPLICATE_COL_NAME; - for (j = i + 1; j < t->desc->num_columns; j++) { - const char *name2 = get_column_description(t->desc, - j)->name; - if (cd->name && name2 && !strcmp(cd->name, name2)) - goto err; - } - } - return 1; -err: - return ret; -} - -/** - * Initialize a struct table from given table description. - * - * \param desc The description of the osl table. - * \param table_ptr Result is returned here. - * - * This function performs several sanity checks on \p desc and returns if any - * of these tests fail. On success, a struct \p osl_table is allocated and - * initialized with data derived from \p desc. - * - * \return Positive on success, negative on errors. Possible errors include: \p - * E_BAD_TABLE_DESC, \p E_NO_COLUMN_DESC, \p E_NO_COLUMNS, \p - * E_BAD_STORAGE_TYPE, \p E_BAD_STORAGE_FLAGS, \p E_BAD_STORAGE_SIZE, \p - * E_NO_UNIQUE_RBTREE_COLUMN, \p E_NO_RBTREE_COL. - * - * \sa struct osl_table. - */ -int init_table_structure(const struct osl_table_description *desc, - struct osl_table **table_ptr) -{ - const struct osl_column_description *cd; - struct osl_table *t = para_calloc(sizeof(*t)); - int i, ret = -E_BAD_TABLE_DESC, have_disk_storage_name_column = 0; - - if (!desc) - goto err; - PARA_DEBUG_LOG("creating table structure for '%s' from table " - "description\n", desc->name); - ret = -E_NO_COLUMN_DESC; - if (!desc->column_descriptions) - goto err; - ret = -E_NO_COLUMNS; - if (!desc->num_columns) - goto err; - t->columns = para_calloc(desc->num_columns * sizeof(struct osl_column)); - t->desc = desc; - FOR_EACH_COLUMN(i, t->desc, cd) { - enum osl_storage_type st = cd->storage_type; - enum osl_storage_flags sf = cd->storage_flags; - struct osl_column *col = &t->columns[i]; - - ret = -E_BAD_STORAGE_TYPE; - if (st != OSL_MAPPED_STORAGE && st != OSL_DISK_STORAGE - && st != OSL_NO_STORAGE) - goto err; - ret = -E_BAD_STORAGE_FLAGS; - if (st == OSL_DISK_STORAGE && sf & OSL_RBTREE) - goto err; - ret = -E_BAD_STORAGE_SIZE; - if (sf & OSL_FIXED_SIZE && !cd->data_size) - goto err; - switch (st) { - case OSL_DISK_STORAGE: - t->num_disk_storage_columns++; - break; - case OSL_MAPPED_STORAGE: - t->num_mapped_columns++; - col->index_offset = t->row_index_size; - t->row_index_size += 8; - break; - case OSL_NO_STORAGE: - col->volatile_num = t->num_volatile_columns; - t->num_volatile_columns++; - break; - } - if (sf & OSL_RBTREE) { - col->rbtree_num = t->num_rbtrees; - t->num_rbtrees++; - if ((sf & OSL_UNIQUE) && (st == OSL_MAPPED_STORAGE)) { - if (!have_disk_storage_name_column) - t->disk_storage_name_column = i; - have_disk_storage_name_column = 1; - } - } - } - ret = -E_NO_UNIQUE_RBTREE_COLUMN; - if (t->num_disk_storage_columns && !have_disk_storage_name_column) - goto err; - ret = -E_NO_RBTREE_COL; - if (!t->num_rbtrees) - goto err; - /* success */ - PARA_DEBUG_LOG("OK. Index entry size: %u\n", t->row_index_size); - ret = init_column_descriptions(t); - if (ret < 0) - goto err; - *table_ptr = t; - return 1; -err: - free(t->columns); - free(t); - return ret; -} - -/** - * Read the table description from index header. - * - * \param map The memory mapping of the index file. - * \param desc The values found in the index header are returned here. - * - * Read the index header, check for the paraslash magic string and the table version number. - * Read all information stored in the index header into \a desc. - * - * \return Positive on success, negative on errors. - * - * \sa struct osl_table_description, osl_create_table. - */ -int read_table_desc(struct osl_object *map, struct osl_table_description *desc) -{ - char *buf = map->data; - uint8_t version; - uint16_t header_size; - int ret, i; - unsigned offset; - struct osl_column_description *cd; - - if (map->size < MIN_INDEX_HEADER_SIZE(1)) - return -E_SHORT_TABLE; - if (strncmp(buf + IDX_PARA_MAGIC, PARA_MAGIC, strlen(PARA_MAGIC))) - return -E_NO_MAGIC; - version = read_u8(buf + IDX_VERSION); - if (version < MIN_TABLE_VERSION || version > MAX_TABLE_VERSION) - return -E_VERSION_MISMATCH; - desc->num_columns = read_u8(buf + IDX_TABLE_FLAGS); - desc->flags = read_u8(buf + IDX_TABLE_FLAGS); - desc->num_columns = read_u16(buf + IDX_NUM_COLUMNS); - PARA_DEBUG_LOG("%u columns\n", desc->num_columns); - if (!desc->num_columns) - return -E_NO_COLUMNS; - header_size = read_u16(buf + IDX_HEADER_SIZE); - if (map->size < header_size) - return -E_BAD_SIZE; - desc->column_descriptions = para_calloc(desc->num_columns - * sizeof(struct osl_column_description)); - offset = IDX_COLUMN_DESCRIPTIONS; - FOR_EACH_COLUMN(i, desc, cd) { - char *null_byte; - - ret = -E_SHORT_TABLE; - if (map->size < offset + MIN_IDX_COLUMN_DESCRIPTION_SIZE) { - PARA_ERROR_LOG("map size = %zu < %u = offset + min desc size\n", - map->size, offset + MIN_IDX_COLUMN_DESCRIPTION_SIZE); - goto err; - } - cd->storage_type = read_u16(buf + offset + IDX_CD_STORAGE_TYPE); - cd->storage_flags = read_u16(buf + offset + - IDX_CD_STORAGE_FLAGS); - cd->data_size = read_u32(buf + offset + IDX_CD_DATA_SIZE); - null_byte = memchr(buf + offset + IDX_CD_NAME, '\0', - map->size - offset - IDX_CD_NAME); - ret = -E_INDEX_CORRUPTION; - if (!null_byte) - goto err; - cd->name = para_strdup(buf + offset + IDX_CD_NAME); - offset += index_column_description_size(cd->name); - } - if (offset != header_size) { - ret = -E_INDEX_CORRUPTION; - PARA_ERROR_LOG("real header size = %u != %u = stored header size\n", - offset, header_size); - goto err; - } - return 1; -err: - FOR_EACH_COLUMN(i, desc, cd) - free(cd->name); - return ret; -} - -/* - * check whether the table description given by \p t->desc matches the on-disk - * table structure stored in the index of \a t. - */ -static int compare_table_descriptions(struct osl_table *t) -{ - int i, ret; - struct osl_table_description desc; - const struct osl_column_description *cd1, *cd2; - - /* read the on-disk structure into desc */ - ret = read_table_desc(&t->index_map, &desc); - if (ret < 0) - return ret; - ret = -E_BAD_TABLE_FLAGS; - if (desc.flags != t->desc->flags) - goto out; - ret = -E_BAD_COLUMN_NUM; - if (desc.num_columns != t->desc->num_columns) - goto out; - FOR_EACH_COLUMN(i, t->desc, cd1) { - cd2 = get_column_description(&desc, i); - ret = -E_BAD_STORAGE_TYPE; - if (cd1->storage_type != cd2->storage_type) - goto out; - ret = -E_BAD_STORAGE_FLAGS; - if (cd1->storage_flags != cd2->storage_flags) { - PARA_ERROR_LOG("sf1 = %u != %u = sf2\n", - cd1->storage_flags, cd2->storage_flags); - goto out; - } - ret = -E_BAD_DATA_SIZE; - if (cd1->storage_flags & OSL_FIXED_SIZE) - if (cd1->data_size != cd2->data_size) - goto out; - ret = -E_BAD_COLUMN_NAME; - if (strcmp(cd1->name, cd2->name)) - goto out; - } - PARA_DEBUG_LOG("table description of '%s' matches on-disk data, good\n", - t->desc->name); - ret = 1; -out: - FOR_EACH_COLUMN(i, &desc, cd1) - free(cd1->name); - free(desc.column_descriptions); - return ret; -} - -static int create_table_index(struct osl_table *t) -{ - char *buf, *filename; - int i, ret; - size_t size = t->index_header_size; - const struct osl_column_description *cd; - unsigned offset; - - PARA_INFO_LOG("creating %zu byte index for table %s\n", size, - t->desc->name); - buf = para_calloc(size); - sprintf(buf + IDX_PARA_MAGIC, "%s", PARA_MAGIC); - write_u8(buf + IDX_TABLE_FLAGS, t->desc->flags); - write_u8(buf + IDX_DIRTY_FLAG, 0); - write_u8(buf + IDX_VERSION, CURRENT_TABLE_VERSION); - write_u16(buf + IDX_NUM_COLUMNS, t->desc->num_columns); - write_u16(buf + IDX_HEADER_SIZE, t->index_header_size); - offset = IDX_COLUMN_DESCRIPTIONS; - FOR_EACH_COLUMN(i, t->desc, cd) { - write_u16(buf + offset + IDX_CD_STORAGE_TYPE, - cd->storage_type); - write_u16(buf + offset + IDX_CD_STORAGE_FLAGS, - cd->storage_flags); - if (cd->storage_flags & OSL_FIXED_SIZE) - write_u32(buf + offset + IDX_CD_DATA_SIZE, - cd->data_size); - strcpy(buf + offset + IDX_CD_NAME, cd->name); - offset += index_column_description_size(cd->name); - } - assert(offset = size); - filename = index_filename(t->desc); - ret = para_write_file(filename, buf, size); - free(buf); - free(filename); - return ret; -} - -/** - * Create a new osl table. - * - * \param desc Pointer to the table description. - * - * \return Standard. - */ -int osl_create_table(const struct osl_table_description *desc) -{ - const struct osl_column_description *cd; - char *table_dir = NULL, *filename; - struct osl_table *t; - int i, ret = init_table_structure(desc, &t); - - if (ret < 0) - return ret; - PARA_INFO_LOG("creating %s\n", desc->name); - FOR_EACH_COLUMN(i, t->desc, cd) { - if (cd->storage_type == OSL_NO_STORAGE) - continue; - if (!table_dir) { - ret = para_mkdir(desc->dir, 0777); - if (ret < 0 && !is_errno(-ret, EEXIST)) - goto out; - table_dir = make_message("%s/%s", desc->dir, - desc->name); - ret = para_mkdir(table_dir, 0777); - if (ret < 0) - goto out; - } - filename = column_filename(t, i); - PARA_INFO_LOG("filename: %s\n", filename); - if (cd->storage_type == OSL_MAPPED_STORAGE) { - ret = para_open(filename, O_RDWR | O_CREAT | O_EXCL, - 0644); - free(filename); - if (ret < 0) - goto out; - close(ret); - continue; - } - /* DISK STORAGE */ - ret = para_mkdir(filename, 0777); - free(filename); - if (ret < 0) - goto out; - } - if (t->num_mapped_columns) { - ret = create_table_index(t); - if (ret < 0) - goto out; - } - ret = 1; -out: - free(table_dir); - free(t->columns); - free(t); - return ret; -} - -static int table_is_dirty(struct osl_table *t) -{ - char *buf = (char *)t->index_map.data + IDX_DIRTY_FLAG; - uint8_t dirty = read_u8(buf) & 0x1; - return !!dirty; -} - -static void mark_table_dirty(struct osl_table *t) -{ - char *buf = (char *)t->index_map.data + IDX_DIRTY_FLAG; - write_u8(buf, read_u8(buf) | 1); -} - -static void mark_table_clean(struct osl_table *t) -{ - char *buf = (char *)t->index_map.data + IDX_DIRTY_FLAG; - write_u8(buf, read_u8(buf) & 0xfe); -} - -static void unmap_column(struct osl_table *t, unsigned col_num) -{ - struct osl_object map = t->columns[col_num].data_map; - int ret; - if (!map.data) - return; - ret = para_munmap(map.data, map.size); - assert(ret > 0); - map.data = NULL; -} - -/** - * Unmap all mapped files of an osl table. - * - * \param t Pointer to a mapped table. - * \param flags Options for unmapping. - * - * \return Positive on success, negative on errors. - * - * \sa map_table(), enum osl_close_flags, para_munmap(). - */ -int unmap_table(struct osl_table *t, enum osl_close_flags flags) -{ - unsigned i; - const struct osl_column_description *cd; - int ret; - - if (!t->num_mapped_columns) /* can this ever happen? */ - return 1; - PARA_DEBUG_LOG("unmapping table '%s'\n", t->desc->name); - if (!t->index_map.data) - return -E_NOT_MAPPED; - if (flags & OSL_MARK_CLEAN) - mark_table_clean(t); - ret = para_munmap(t->index_map.data, t->index_map.size); - if (ret < 0) - return ret; - t->index_map.data = NULL; - if (!t->num_rows) - return 1; - FOR_EACH_MAPPED_COLUMN(i, t, cd) - unmap_column(t, i); - return 1; -} - -static int map_column(struct osl_table *t, unsigned col_num) -{ - struct stat statbuf; - char *filename = column_filename(t, col_num); - int ret = -E_STAT; - if (stat(filename, &statbuf) < 0) { - free(filename); - return ret; - } - if (!(S_IFREG & statbuf.st_mode)) { - free(filename); - return ret; - } - ret = mmap_full_file(filename, O_RDWR, - &t->columns[col_num].data_map.data, - &t->columns[col_num].data_map.size, - NULL); - free(filename); - return ret; -} - -/** - * Map the index file and all columns of type \p OSL_MAPPED_STORAGE into memory. - * - * \param t Pointer to an initialized table structure. - * \param flags Mapping options. - * - * \return Negative return value on errors; on success the number of rows - * (including invalid rows) is returned. - * - * \sa unmap_table(), enum map_table_flags, osl_open_table(), mmap(2). - */ -int map_table(struct osl_table *t, enum map_table_flags flags) -{ - char *filename; - const struct osl_column_description *cd; - int i = 0, ret, num_rows = 0; - - if (!t->num_mapped_columns) - return 0; - if (t->index_map.data) - return -E_ALREADY_MAPPED; - filename = index_filename(t->desc); - PARA_DEBUG_LOG("mapping table '%s' (index: %s)\n", t->desc->name, filename); - ret = mmap_full_file(filename, flags & MAP_TBL_FL_MAP_RDONLY? - O_RDONLY : O_RDWR, &t->index_map.data, &t->index_map.size, NULL); - free(filename); - if (ret < 0) - return ret; - if (flags & MAP_TBL_FL_VERIFY_INDEX) { - ret = compare_table_descriptions(t); - if (ret < 0) - goto err; - } - ret = -E_BUSY; - if (!(flags & MAP_TBL_FL_IGNORE_DIRTY)) { - if (table_is_dirty(t)) { - PARA_ERROR_LOG("%s is dirty\n", t->desc->name); - goto err; - } - } - mark_table_dirty(t); - num_rows = table_num_rows(t); - if (!num_rows) - return num_rows; - /* map data files */ - FOR_EACH_MAPPED_COLUMN(i, t, cd) { - ret = map_column(t, i); - if (ret < 0) - goto err; - } - return num_rows; -err: /* unmap what is already mapped */ - for (i--; i >= 0; i--) { - struct osl_object map = t->columns[i].data_map; - para_munmap(map.data, map.size); - map.data = NULL; - } - para_munmap(t->index_map.data, t->index_map.size); - t->index_map.data = NULL; - return ret; -} - -/** - * Retrieve a mapped object by row and column number. - * - * \param t Pointer to an open osl table. - * \param col_num Number of the mapped column containing the object to retrieve. - * \param row_num Number of the row containing the object to retrieve. - * \param obj The result is returned here. - * - * It is considered an error if \a col_num does not refer to a column - * of storage type \p OSL_MAPPED_STORAGE. - * - * \return Positive on success, negative on errors. Possible errors include: - * \p E_BAD_ROW_NUM, \p E_INVALID_OBJECT. - * - * \sa osl_storage_type. - */ -int get_mapped_object(const struct osl_table *t, unsigned col_num, - uint32_t row_num, struct osl_object *obj) -{ - struct osl_column *col = &t->columns[col_num]; - uint32_t offset; - char *header; - char *cell_index; - int ret; - - if (t->num_rows <= row_num) - return -E_BAD_ROW_NUM; - ret = get_cell_index(t, row_num, col_num, &cell_index); - if (ret < 0) - return ret; - offset = read_u32(cell_index); - obj->size = read_u32(cell_index + 4) - 1; - header = col->data_map.data + offset; - obj->data = header + 1; - if (read_u8(header) == 0xff) { - PARA_ERROR_LOG("col %u, size %zu, offset %u\n", col_num, - obj->size, offset); - return -E_INVALID_OBJECT; - } - return 1; -} - -static int search_rbtree(const struct osl_object *obj, - const struct osl_table *t, unsigned col_num, - struct rb_node **result, struct rb_node ***rb_link) -{ - struct osl_column *col = &t->columns[col_num]; - struct rb_node **new = &col->rbtree.rb_node, *parent = NULL; - const struct osl_column_description *cd = - get_column_description(t->desc, col_num); - enum osl_storage_type st = cd->storage_type; - while (*new) { - struct osl_row *this_row = get_row_pointer(*new, - col->rbtree_num); - int ret; - struct osl_object this_obj; - parent = *new; - if (st == OSL_MAPPED_STORAGE) { - ret = get_mapped_object(t, col_num, this_row->num, - &this_obj); - if (ret < 0) - return ret; - } else - this_obj = this_row->volatile_objects[col->volatile_num]; - ret = cd->compare_function(obj, &this_obj); - if (!ret) { - if (result) - *result = get_rb_node_pointer(this_row, - col->rbtree_num); - return 1; - } - if (ret < 0) - new = &((*new)->rb_left); - else - new = &((*new)->rb_right); - } - if (result) - *result = parent; - if (rb_link) - *rb_link = new; - return -E_RB_KEY_NOT_FOUND; -} - -static int insert_rbtree(struct osl_table *t, unsigned col_num, - const struct osl_row *row, const struct osl_object *obj) -{ - struct rb_node *parent, **rb_link; - unsigned rbtree_num; - struct rb_node *n; - int ret = search_rbtree(obj, t, col_num, &parent, &rb_link); - - if (ret > 0) - return -E_RB_KEY_EXISTS; - rbtree_num = t->columns[col_num].rbtree_num; - n = get_rb_node_pointer(row, rbtree_num); - rb_link_node(n, parent, rb_link); - rb_insert_color(n, &t->columns[col_num].rbtree); - return 1; -} - -static void remove_rb_node(struct osl_table *t, unsigned col_num, - const struct osl_row *row) -{ - struct osl_column *col = &t->columns[col_num]; - const struct osl_column_description *cd = - get_column_description(t->desc, col_num); - enum osl_storage_flags sf = cd->storage_flags; - struct rb_node *victim, *splice_out_node, *tmp; - if (!(sf & OSL_RBTREE)) - return; - /* - * Which node is removed/spliced out actually depends on how many - * children the victim node has: If it has no children, it gets - * deleted. If it has one child, it gets spliced out. If it has two - * children, its successor (which has at most a right child) gets - * spliced out. - */ - victim = get_rb_node_pointer(row, col->rbtree_num); - if (victim->rb_left && victim->rb_right) - splice_out_node = rb_next(victim); - else - splice_out_node = victim; - /* Go up to the root and decrement the size of each node in the path. */ - for (tmp = splice_out_node; tmp; tmp = rb_parent(tmp)) - tmp->size--; - rb_erase(victim, &col->rbtree); -} - -static int add_row_to_rbtrees(struct osl_table *t, uint32_t row_num, - struct osl_object *volatile_objs, struct osl_row **row_ptr) -{ - unsigned i; - int ret; - struct osl_row *row = allocate_row(t->num_rbtrees); - const struct osl_column_description *cd; - - row->num = row_num; - row->volatile_objects = volatile_objs; - FOR_EACH_RBTREE_COLUMN(i, t, cd) { - if (cd->storage_type == OSL_MAPPED_STORAGE) { - struct osl_object obj; - ret = get_mapped_object(t, i, row_num, &obj); - if (ret < 0) - goto err; - ret = insert_rbtree(t, i, row, &obj); - } else { /* volatile */ - const struct osl_object *obj - = volatile_objs + t->columns[i].volatile_num; - ret = insert_rbtree(t, i, row, obj); - } - if (ret < 0) - goto err; - } - if (row_ptr) - *row_ptr = row; - return 1; -err: /* rollback changes, i.e. remove added entries from rbtrees */ - while (i) - remove_rb_node(t, i--, row); - free(row); - return ret; -} - -static void free_volatile_objects(const struct osl_table *t, - enum osl_close_flags flags) -{ - int i, j; - struct rb_node *n; - struct osl_column *rb_col; - const struct osl_column_description *cd; - - if (!t->num_volatile_columns) - return; - /* find the first rbtree column (any will do) */ - FOR_EACH_RBTREE_COLUMN(i, t, cd) - break; - rb_col = t->columns + i; - /* walk that rbtree and free all volatile objects */ - for (n = rb_first(&rb_col->rbtree); n; n = rb_next(n)) { - struct osl_row *r = get_row_pointer(n, rb_col->rbtree_num); - if (flags & OSL_FREE_VOLATILE) - FOR_EACH_VOLATILE_COLUMN(j, t, cd) { - if (cd->storage_flags & OSL_DONT_FREE) - continue; - free(r->volatile_objects[ - t->columns[j].volatile_num].data); - } -// for (j = 0; j < t->num_volatile_columns; j++) -// free(r->volatile_objects[j].data); - free(r->volatile_objects); - } -} - -/** - * Erase all rbtree nodes and free resources. - * - * \param t Pointer to an open osl table. - * - * This function is called by osl_close_table(). - */ -void clear_rbtrees(struct osl_table *t) -{ - const struct osl_column_description *cd; - unsigned i, rbtrees_cleared = 0; - - FOR_EACH_RBTREE_COLUMN(i, t, cd) { - struct osl_column *col = &t->columns[i]; - struct rb_node *n; - rbtrees_cleared++; - for (n = rb_first(&col->rbtree); n;) { - struct osl_row *r; - rb_erase(n, &col->rbtree); - if (rbtrees_cleared == t->num_rbtrees) { - r = get_row_pointer(n, col->rbtree_num); - n = rb_next(n); - free(r); - } else - n = rb_next(n); - } - } - -} - -/** - * Close an osl table. - * - * \param t Pointer to the table to be closed. - * \param flags Options for what should be cleaned up. - * - * If osl_open_table() succeeds, the resulting table pointer must later be - * passed to this function in order to flush all changes to the file system and - * to free the resources that were allocated by osl_open_table(). - * - * \return Positive on success, negative on errors. Possible errors: \p E_BAD_TABLE, - * errors returned by unmap_table(). - * - * \sa osl_open_table(), unmap_table(). - */ -int osl_close_table(struct osl_table *t, enum osl_close_flags flags) -{ - int ret; - - if (!t) - return -E_BAD_TABLE; - free_volatile_objects(t, flags); - clear_rbtrees(t); - ret = unmap_table(t, flags); - if (ret < 0) - PARA_ERROR_LOG("unmap_table failed: %d\n", ret); - free(t->columns); - free(t); - return ret; -} - -/** - * Find out whether the given row number corresponds to an invalid row. - * - * \param t Pointer to the osl table. - * \param row_num The number of the row in question. - * - * By definition, a row is considered invalid if all its index entries - * are invalid. - * - * \return Positive if \a row_num corresponds to an invalid row, - * zero if it corresponds to a valid row, negative on errors. - */ -int row_is_invalid(struct osl_table *t, uint32_t row_num) -{ - char *row_index; - int i, ret = get_row_index(t, row_num, &row_index); - - if (ret < 0) - return ret; - for (i = 0; i < t->row_index_size; i++) { - if ((unsigned char)row_index[i] != 0xff) - return 0; - } - PARA_INFO_LOG("row %d is invalid\n", row_num); - return 1; -} - -/** - * Invalidate a row of an osl table. - * - * \param t Pointer to an open osl table. - * \param row_num Number of the row to mark as invalid. - * - * This function marks each mapped object in the index entry of \a row as - * invalid. - * - * \return Positive on success, negative on errors. - */ -int mark_row_invalid(struct osl_table *t, uint32_t row_num) -{ - char *row_index; - int ret = get_row_index(t, row_num, &row_index); - - if (ret < 0) - return ret; - PARA_INFO_LOG("marking row %d as invalid\n", row_num); - memset(row_index, 0xff, t->row_index_size); - return 1; -} - -/** - * Initialize all rbtrees and compute number of invalid rows. - * - * \param t The table containing the rbtrees to be initialized. - * - * \return Positive on success, negative on errors. - */ -int init_rbtrees(struct osl_table *t) -{ - int i, ret; - const struct osl_column_description *cd; - - /* create rbtrees */ - FOR_EACH_RBTREE_COLUMN(i, t, cd) - t->columns[i].rbtree = RB_ROOT; - /* add valid rows to rbtrees */ - t->num_invalid_rows = 0; - for (i = 0; i < t->num_rows; i++) { - ret = row_is_invalid(t, i); - if (ret < 0) - return ret; - if (ret) { - t->num_invalid_rows++; - continue; - } - ret = add_row_to_rbtrees(t, i, NULL, NULL); - if (ret < 0) - return ret; - } - return 1; -} - -/** - * Open an osl table. - * - * Each osl table must be opened before its data can be accessed. - * - * \param table_desc Describes the table to be opened. - * \param result Contains a pointer to the open table on success. - * - * The table description given by \a desc should coincide with the - * description used at creation time. - * - * \return Standard. - */ -int osl_open_table(const struct osl_table_description *table_desc, - struct osl_table **result) -{ - int i, ret; - struct osl_table *t; - const struct osl_column_description *cd; - - PARA_INFO_LOG("opening table %s\n", table_desc->name); - ret = init_table_structure(table_desc, &t); - if (ret < 0) - return ret; - FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) { - /* check if directory exists */ - char *dirname = column_filename(t, i); - struct stat statbuf; - ret = stat(dirname, &statbuf); - free(dirname); - if (ret < 0) { - ret = -ERRNO_TO_PARA_ERROR(errno); - goto err; - } - ret = -ERRNO_TO_PARA_ERROR(ENOTDIR); - if (!S_ISDIR(statbuf.st_mode)) - goto err; - } - ret = map_table(t, MAP_TBL_FL_VERIFY_INDEX); - if (ret < 0) - goto err; - t->num_rows = ret; - PARA_DEBUG_LOG("num rows: %d\n", t->num_rows); - ret = init_rbtrees(t); - if (ret < 0) { - osl_close_table(t, OSL_MARK_CLEAN); /* ignore further errors */ - return ret; - } - *result = t; - return 1; -err: - free(t->columns); - free(t); - return ret; -} - -static int create_disk_storage_object_dir(const struct osl_table *t, - unsigned col_num, const char *ds_name) -{ - char *dirname; - int ret; - - if (!(t->desc->flags & OSL_LARGE_TABLE)) - return 1; - dirname = disk_storage_dirname(t, col_num, ds_name); - ret = para_mkdir(dirname, 0777); - free(dirname); - if (ret < 0 && !is_errno(-ret, EEXIST)) - return ret; - return 1; -} - -static int write_disk_storage_file(const struct osl_table *t, unsigned col_num, - const struct osl_object *obj, const char *ds_name) -{ - int ret; - char *filename; - - ret = create_disk_storage_object_dir(t, col_num, ds_name); - if (ret < 0) - return ret; - filename = disk_storage_path(t, col_num, ds_name); - ret = para_write_file(filename, obj->data, obj->size); - free(filename); - return ret; -} - -static int append_map_file(const struct osl_table *t, unsigned col_num, - const struct osl_object *obj, uint32_t *new_size) -{ - char *filename = column_filename(t, col_num); - int ret; - char header = 0; /* zero means valid object */ - -// PARA_DEBUG_LOG("appending %zu + 1 byte\n", obj->size); - ret = append_file(filename, &header, 1, obj->data, obj->size, - new_size); - free(filename); - return ret; -} - -static int append_row_index(const struct osl_table *t, char *row_index) -{ - char *filename; - int ret; - - if (!t->num_mapped_columns) - return 1; - filename = index_filename(t->desc); - ret = append_file(filename, NULL, 0, row_index, - t->row_index_size, NULL); - free(filename); - return ret; -} - -/** - * A wrapper for truncate(2) - * - * \param path Name of the regular file to truncate - * \param size Number of bytes to \b shave \b off - * - * Truncate the regular file named by \a path by \a size bytes. - * - * \return Positive on success, negative on errors. Possible errors include: \p - * E_STAT, \p E_BAD_SIZE, \p E_TRUNC. - * - * \sa truncate(2) - */ -int para_truncate(const char *path, off_t size) -{ - int ret; - struct stat statbuf; - - ret = -E_STAT; - if (stat(path, &statbuf) < 0) - goto out; - ret = -E_BAD_SIZE; - if (statbuf.st_size < size) - goto out; - ret = -E_TRUNC; - if (truncate(path, statbuf.st_size - size) < 0) - goto out; - ret = 1; -out: - return ret; -} - -static int truncate_mapped_file(const struct osl_table *t, unsigned col_num, - off_t size) -{ - char *filename = column_filename(t, col_num); - int ret = para_truncate(filename, size); - free(filename); - return ret; -} - -static int delete_disk_storage_file(const struct osl_table *t, unsigned col_num, - const char *ds_name) -{ - char *dirname, *filename = disk_storage_path(t, col_num, ds_name); - int ret = unlink(filename), err = errno; - - free(filename); - if (ret < 0) - return -ERRNO_TO_PARA_ERROR(err); - if (!(t->desc->flags & OSL_LARGE_TABLE)) - return 1; - dirname = disk_storage_dirname(t, col_num, ds_name); - rmdir(dirname); - free(dirname); - return 1; -} - -/** - * Add a new row to an osl table and retrieve this row. - * - * \param t Pointer to an open osl table. - * \param objects Array of objects to be added. - * \param row Result pointer. - * - * The \a objects parameter must point to an array containing one object per - * column. The order of the objects in the array is given by the table - * description of \a table. Several sanity checks are performed during object - * insertion and the function returns without modifying the table if any of - * these tests fail. In fact, it is atomic in the sense that it either - * succeeds or leaves the table unchanged (i.e. either all or none of the - * objects are added to the table). - * - * It is considered an error if an object is added to a column with associated - * rbtree if this object is equal to an object already contained in that column - * (i.e. the compare function for the column's rbtree returns zero). - * - * Possible errors include: \p E_RB_KEY_EXISTS, \p E_BAD_DATA_SIZE. - * - * \return Positive on success, negative on errors. - * - * \sa struct osl_table_description, osl_compare_func, osl_add_row(). - */ -int osl_add_and_get_row(struct osl_table *t, struct osl_object *objects, - struct osl_row **row) -{ - int i, ret; - char *ds_name = NULL; - struct rb_node **rb_parents = NULL, ***rb_links = NULL; - char *new_row_index = NULL; - struct osl_object *volatile_objs = NULL; - const struct osl_column_description *cd; - - if (!t) - return -E_BAD_TABLE; - rb_parents = para_malloc(t->num_rbtrees * sizeof(struct rn_node*)); - rb_links = para_malloc(t->num_rbtrees * sizeof(struct rn_node**)); - if (t->num_mapped_columns) - new_row_index = para_malloc(t->row_index_size); - /* pass 1: sanity checks */ -// PARA_DEBUG_LOG("sanity tests: %p:%p\n", objects[0].data, -// objects[1].data); - FOR_EACH_COLUMN(i, t->desc, cd) { - enum osl_storage_type st = cd->storage_type; - enum osl_storage_flags sf = cd->storage_flags; - -// ret = -E_NULL_OBJECT; -// if (!objects[i]) -// goto out; - if (st == OSL_DISK_STORAGE) - continue; - if (sf & OSL_RBTREE) { - unsigned rbtree_num = t->columns[i].rbtree_num; - ret = -E_RB_KEY_EXISTS; -// PARA_DEBUG_LOG("checking whether %p exists\n", -// objects[i].data); - if (search_rbtree(objects + i, t, i, - &rb_parents[rbtree_num], - &rb_links[rbtree_num]) > 0) - goto out; - } - if (sf & OSL_FIXED_SIZE) { -// PARA_DEBUG_LOG("fixed size. need: %zu, have: %d\n", -// objects[i].size, cd->data_size); - ret = -E_BAD_DATA_SIZE; - if (objects[i].size != cd->data_size) - goto out; - } - } - if (t->num_disk_storage_columns) - ds_name = disk_storage_name_of_object(t, - &objects[t->disk_storage_name_column]); - ret = unmap_table(t, OSL_MARK_CLEAN); - if (ret < 0) - goto out; -// PARA_DEBUG_LOG("sanity tests passed%s\n", ""); - /* pass 2: create data files, append map data */ - FOR_EACH_COLUMN(i, t->desc, cd) { - enum osl_storage_type st = cd->storage_type; - if (st == OSL_NO_STORAGE) - continue; - if (st == OSL_MAPPED_STORAGE) { - uint32_t new_size; - struct osl_column *col = &t->columns[i]; -// PARA_DEBUG_LOG("appending object of size %zu\n", -// objects[i].size); - ret = append_map_file(t, i, objects + i, &new_size); - if (ret < 0) - goto rollback; - update_cell_index(new_row_index, col, new_size, - objects[i].size); - continue; - } - /* DISK_STORAGE */ - ret = write_disk_storage_file(t, i, objects + i, ds_name); - if (ret < 0) - goto rollback; - } - ret = append_row_index(t, new_row_index); - if (ret < 0) - goto rollback; - ret = map_table(t, MAP_TBL_FL_VERIFY_INDEX); - if (ret < 0) { /* truncate index and rollback changes */ - char *filename = index_filename(t->desc); - para_truncate(filename, t->row_index_size); - free(filename); - goto rollback; - } - /* pass 3: add entry to rbtrees */ - if (t->num_volatile_columns) { - volatile_objs = para_calloc(t->num_volatile_columns - * sizeof(struct osl_object)); - FOR_EACH_VOLATILE_COLUMN(i, t, cd) - volatile_objs[t->columns[i].volatile_num] = objects[i]; - } - t->num_rows++; -// PARA_DEBUG_LOG("adding new entry as row #%d\n", t->num_rows - 1); - ret = add_row_to_rbtrees(t, t->num_rows - 1, volatile_objs, row); - if (ret < 0) - goto out; -// PARA_DEBUG_LOG("added new entry as row #%d\n", t->num_rows - 1); - ret = 1; - goto out; -rollback: /* rollback all changes made, ignore further errors */ - for (i--; i >= 0; i--) { - enum osl_storage_type st; - - cd = get_column_description(t->desc, i); - st = cd->storage_type; - if (st == OSL_NO_STORAGE) - continue; - - if (st == OSL_MAPPED_STORAGE) - truncate_mapped_file(t, i, objects[i].size); - else /* disk storage */ - delete_disk_storage_file(t, i, ds_name); - } - /* ignore error and return previous error value */ - map_table(t, MAP_TBL_FL_VERIFY_INDEX); -out: - free(new_row_index); - free(ds_name); - free(rb_parents); - free(rb_links); - return ret; -} - -/** - * Add a new row to an osl table. - * - * \param t Same meaning as osl_add_and_get_row(). - * \param objects Same meaning as osl_add_and_get_row(). - * - * \return The return value of the underlying call to osl_add_and_get_row(). - * - * This is equivalent to osl_add_and_get_row(t, objects, NULL). - */ -int osl_add_row(struct osl_table *t, struct osl_object *objects) -{ - return osl_add_and_get_row(t, objects, NULL); -} - -/** - * Retrieve an object identified by row and column - * - * \param t Pointer to an open osl table. - * \param r Pointer to the row. - * \param col_num The column number. - * \param object The result pointer. - * - * The column determined by \a col_num must be of type \p OSL_MAPPED_STORAGE - * or \p OSL_NO_STORAGE, i.e. no disk storage objects may be retrieved by this - * function. - * - * \return Positive if object was found, negative on errors. Possible errors - * include: \p E_BAD_TABLE, \p E_BAD_STORAGE_TYPE. - * - * \sa osl_storage_type, osl_open_disk_object(). - */ -int osl_get_object(const struct osl_table *t, const struct osl_row *r, - unsigned col_num, struct osl_object *object) -{ - const struct osl_column_description *cd; - - if (!t) - return -E_BAD_TABLE; - cd = get_column_description(t->desc, col_num); - /* col must not be disk storage */ - if (cd->storage_type == OSL_DISK_STORAGE) - return -E_BAD_STORAGE_TYPE; - if (cd->storage_type == OSL_MAPPED_STORAGE) - return get_mapped_object(t, col_num, r->num, object); - /* volatile */ - *object = r->volatile_objects[t->columns[col_num].volatile_num]; - return 1; -} - -static int mark_mapped_object_invalid(const struct osl_table *t, - uint32_t row_num, unsigned col_num) -{ - struct osl_object obj; - char *p; - int ret = get_mapped_object(t, col_num, row_num, &obj); - - if (ret < 0) - return ret; - p = obj.data; - p--; - *p = 0xff; - return 1; -} - -/** - * Delete a row from an osl table. - * - * \param t Pointer to an open osl table. - * \param row Pointer to the row to delete. - * - * This removes all disk storage objects, removes all rbtree nodes, and frees - * all volatile objects belonging to the given row. For mapped columns, the - * data is merely marked invalid and may be pruned from time to time by - * para_fsck. - * - * \return Positive on success, negative on errors. Possible errors include: - * \p E_BAD_TABLE, errors returned by osl_get_object(). - */ -int osl_del_row(struct osl_table *t, struct osl_row *row) -{ - struct osl_row *r = row; - int i, ret; - const struct osl_column_description *cd; - - if (!t) - return -E_BAD_TABLE; - PARA_INFO_LOG("deleting row %p\n", row); - - if (t->num_disk_storage_columns) { - char *ds_name; - ret = disk_storage_name_of_row(t, r, &ds_name); - if (ret < 0) - goto out; - FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) - delete_disk_storage_file(t, i, ds_name); - free(ds_name); - } - FOR_EACH_COLUMN(i, t->desc, cd) { - struct osl_column *col = t->columns + i; - enum osl_storage_type st = cd->storage_type; - remove_rb_node(t, i, r); - if (st == OSL_MAPPED_STORAGE) { - mark_mapped_object_invalid(t, r->num, i); - continue; - } - if (st == OSL_NO_STORAGE && !(cd->storage_flags & OSL_DONT_FREE)) - free(r->volatile_objects[col->volatile_num].data); - } - if (t->num_mapped_columns) { - ret = mark_row_invalid(t, r->num); - if (ret < 0) - goto out; - t->num_invalid_rows++; - } else - t->num_rows--; - ret = 1; -out: - free(r->volatile_objects); - free(r); - return ret; -} - -/* test if column has an rbtree */ -static int check_rbtree_col(const struct osl_table *t, unsigned col_num, - struct osl_column **col) -{ - if (!t) - return -E_BAD_TABLE; - if (!(get_column_description(t->desc, col_num)->storage_flags & OSL_RBTREE)) - return -E_BAD_STORAGE_FLAGS; - *col = t->columns + col_num; - return 1; -} - -/** - * Get the row that contains the given object. - * - * \param t Pointer to an open osl table. - * \param col_num The number of the column to be searched. - * \param obj The object to be looked up. - * \param result Points to the row containing \a obj. - * - * Lookup \a obj in \a t and return the row containing \a obj. The column - * specified by \a col_num must have an associated rbtree. - * - * \return Positive on success, negative on errors. If an error occurred, \a - * result is set to \p NULL. Possible errors include: \p E_BAD_TABLE, \p - * E_BAD_STORAGE_FLAGS, errors returned by get_mapped_object(), \p - * E_RB_KEY_NOT_FOUND. - * - * \sa osl_storage_flags - */ -int osl_get_row(const struct osl_table *t, unsigned col_num, - const struct osl_object *obj, struct osl_row **result) -{ - int ret; - struct rb_node *node; - struct osl_row *row; - struct osl_column *col; - - *result = NULL; - ret = check_rbtree_col(t, col_num, &col); - if (ret < 0) - return ret; - ret = search_rbtree(obj, t, col_num, &node, NULL); - if (ret < 0) - return ret; - row = get_row_pointer(node, t->columns[col_num].rbtree_num); - *result = row; - return 1; -} - -static int rbtree_loop(struct osl_column *col, void *private_data, - osl_rbtree_loop_func *func) -{ - struct rb_node *n, *tmp; - - /* this for-loop is safe against removal of an entry */ - for (n = rb_first(&col->rbtree), tmp = n? rb_next(n) : NULL; - n; - n = tmp, tmp = tmp? rb_next(tmp) : NULL) { - struct osl_row *r = get_row_pointer(n, col->rbtree_num); - int ret = func(r, private_data); - if (ret < 0) - return ret; - } - return 1; -} - -static int rbtree_loop_reverse(struct osl_column *col, void *private_data, - osl_rbtree_loop_func *func) -{ - struct rb_node *n, *tmp; - - /* safe against removal of an entry */ - for (n = rb_last(&col->rbtree), tmp = n? rb_prev(n) : NULL; - n; - n = tmp, tmp = tmp? rb_prev(tmp) : NULL) { - struct osl_row *r = get_row_pointer(n, col->rbtree_num); - int ret = func(r, private_data); - if (ret < 0) - return ret; - } - return 1; -} - -/** - * Loop over all nodes in an rbtree. - * - * \param t Pointer to an open osl table. - * \param col_num The column to use for iterating over the elements. - * \param private_data Pointer that gets passed to \a func. - * \param func The function to be called for each node in the rbtree. - * - * This function does an in-order walk of the rbtree associated with \a - * col_num. It is an error if the \p OSL_RBTREE flag is not set for this - * column. For each node in the rbtree, the given function \a func is called - * with two pointers as arguments: The first osl_row* argument points to the - * row that contains the object corresponding to the rbtree node currently - * traversed, and the \a private_data pointer is passed verbatim to \a func as the - * second argument. The loop terminates either if \a func returns a negative - * value, or if all nodes of the tree have been visited. - * - * - * \return Positive on success, negative on errors. If the termination of the - * loop was caused by \a func returning a negative value, this value is - * returned. - * - * \sa osl_storage_flags, osl_rbtree_loop_reverse(), osl_compare_func. - */ -int osl_rbtree_loop(const struct osl_table *t, unsigned col_num, - void *private_data, osl_rbtree_loop_func *func) -{ - struct osl_column *col; - - int ret = check_rbtree_col(t, col_num, &col); - if (ret < 0) - return ret; - return rbtree_loop(col, private_data, func); -} - -/** - * Loop over all nodes in an rbtree in reverse order. - * - * \param t Identical meaning as in \p osl_rbtree_loop(). - * \param col_num Identical meaning as in \p osl_rbtree_loop(). - * \param private_data Identical meaning as in \p osl_rbtree_loop(). - * \param func Identical meaning as in \p osl_rbtree_loop(). - * - * This function is identical to \p osl_rbtree_loop(), the only difference - * is that the tree is walked in reverse order. - * - * \return The same return value as \p osl_rbtree_loop(). - * - * \sa osl_rbtree_loop(). - */ -int osl_rbtree_loop_reverse(const struct osl_table *t, unsigned col_num, - void *private_data, osl_rbtree_loop_func *func) -{ - struct osl_column *col; - - int ret = check_rbtree_col(t, col_num, &col); - if (ret < 0) - return ret; - return rbtree_loop_reverse(col, private_data, func); -} - -/* TODO: Rollback changes on errors */ -static int rename_disk_storage_objects(struct osl_table *t, - struct osl_object *old_obj, struct osl_object *new_obj) -{ - int i, ret; - const struct osl_column_description *cd; - char *old_ds_name, *new_ds_name; - - if (!t->num_disk_storage_columns) - return 1; /* nothing to do */ - if (old_obj->size == new_obj->size && !memcmp(new_obj->data, - old_obj->data, new_obj->size)) - return 1; /* object did not change */ - old_ds_name = disk_storage_name_of_object(t, old_obj); - new_ds_name = disk_storage_name_of_object(t, new_obj); - FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) { - char *old_filename, *new_filename; - ret = create_disk_storage_object_dir(t, i, new_ds_name); - if (ret < 0) - goto out; - old_filename = disk_storage_path(t, i, old_ds_name); - new_filename = disk_storage_path(t, i, new_ds_name); - ret = para_rename(old_filename, new_filename); - free(old_filename); - free(new_filename); - if (ret < 0) - goto out; - } - ret = 1; -out: - free(old_ds_name); - free(new_ds_name); - return ret; - -} - -/** - * Change an object in an osl table. - * - * \param t Pointer to an open osl table. - * \param r Pointer to the row containing the object to be updated. - * \param col_num Number of the column containing the object to be updated. - * \param obj Pointer to the replacement object. - * - * This function gets rid of all references to the old object. This includes - * removal of the rbtree node in case there is an rbtree associated with \a - * col_num. It then inserts \a obj into the table and the rbtree if necessary. - * - * If the \p OSL_RBTREE flag is set for \a col_num, you \b MUST call this - * function in order to change the contents of an object, even for volatile or - * mapped columns of constant size (which may be updated directly if \p - * OSL_RBTREE is not set). Otherwise the rbtree might become corrupted. - * - * \return Standard - */ -int osl_update_object(struct osl_table *t, const struct osl_row *r, - unsigned col_num, struct osl_object *obj) -{ - struct osl_column *col; - const struct osl_column_description *cd; - int ret; - - if (!t) - return -E_BAD_TABLE; - col = &t->columns[col_num]; - cd = get_column_description(t->desc, col_num); - PARA_DEBUG_LOG("updating column %u of %s\n", col_num, t->desc->name); - if (cd->storage_flags & OSL_RBTREE) { - if (search_rbtree(obj, t, col_num, NULL, NULL) > 0) - return -E_RB_KEY_EXISTS; - } - if (cd->storage_flags & OSL_FIXED_SIZE) { - if (obj->size != cd->data_size) - return -E_BAD_DATA_SIZE; - } - remove_rb_node(t, col_num, r); - if (cd->storage_type == OSL_NO_STORAGE) { /* TODO: If fixed size, reuse object? */ - free(r->volatile_objects[col->volatile_num].data); - r->volatile_objects[col->volatile_num] = *obj; - } else if (cd->storage_type == OSL_DISK_STORAGE) { - char *ds_name; - ret = disk_storage_name_of_row(t, r, &ds_name); - if (ret < 0) - return ret; - ret = delete_disk_storage_file(t, col_num, ds_name); - if (ret < 0 && !is_errno(-ret, ENOENT)) { - free(ds_name); - return ret; - } - ret = write_disk_storage_file(t, col_num, obj, ds_name); - free(ds_name); - if (ret < 0) - return ret; - } else { /* mapped storage */ - struct osl_object old_obj; - ret = get_mapped_object(t, col_num, r->num, &old_obj); - if (ret < 0) - return ret; - /* - * If the updated column is the disk storage name column, the - * disk storage name changes, so we have to rename all disk - * storage objects accordingly. - */ - if (col_num == t->disk_storage_name_column) { - ret = rename_disk_storage_objects(t, &old_obj, obj); - if (ret < 0) - return ret; - } - if (cd->storage_flags & OSL_FIXED_SIZE) - memcpy(old_obj.data, obj->data, cd->data_size); - else { /* TODO: if the size doesn't change, use old space */ - uint32_t new_data_map_size; - char *row_index; - ret = get_row_index(t, r->num, &row_index); - if (ret < 0) - return ret; - ret = mark_mapped_object_invalid(t, r->num, col_num); - if (ret < 0) - return ret; - unmap_column(t, col_num); - ret = append_map_file(t, col_num, obj, - &new_data_map_size); - if (ret < 0) - return ret; - ret = map_column(t, col_num); - if (ret < 0) - return ret; - update_cell_index(row_index, col, new_data_map_size, - obj->size); - } - } - if (cd->storage_flags & OSL_RBTREE) { - ret = insert_rbtree(t, col_num, r, obj); - if (ret < 0) - return ret; - } - return 1; -} - -/** - * Retrieve an object of type \p OSL_DISK_STORAGE by row and column. - * - * \param t Pointer to an open osl table. - * \param r Pointer to the row containing the object. - * \param col_num The column number. - * \param obj Points to the result upon successful return. - * - * For columns of type \p OSL_DISK_STORAGE, this function must be used to - * retrieve one of its containing objects. Afterwards, osl_close_disk_object() - * must be called in order to deallocate the resources. - * - * \return Positive on success, negative on errors. Possible errors include: - * \p E_BAD_TABLE, \p E_BAD_STORAGE_TYPE, errors returned by osl_get_object(). - * - * \sa osl_get_object(), osl_storage_type, osl_close_disk_object(). - */ -int osl_open_disk_object(const struct osl_table *t, const struct osl_row *r, - unsigned col_num, struct osl_object *obj) -{ - const struct osl_column_description *cd; - char *ds_name, *filename; - int ret; - - if (!t) - return -E_BAD_TABLE; - cd = get_column_description(t->desc, col_num); - if (cd->storage_type != OSL_DISK_STORAGE) - return -E_BAD_STORAGE_TYPE; - - ret = disk_storage_name_of_row(t, r, &ds_name); - if (ret < 0) - return ret; - filename = disk_storage_path(t, col_num, ds_name); - free(ds_name); - PARA_DEBUG_LOG("filename: %s\n", filename); - ret = mmap_full_file(filename, O_RDONLY, &obj->data, &obj->size, NULL); - free(filename); - return ret; -} - -/** - * Free resources that were allocated during osl_open_disk_object(). - * - * \param obj Pointer to the object previously returned by open_disk_object(). - * - * \return The return value of the underlying call to para_munmap(). - * - * \sa para_munmap(). - */ -int osl_close_disk_object(struct osl_object *obj) -{ - return para_munmap(obj->data, obj->size); -} - -/** - * Get the number of rows of the given table. - * - * \param t Pointer to an open osl table. - * \param num_rows Result is returned here. - * - * The number of rows returned via \a num_rows excluding any invalid rows. - * - * \return Positive on success, \p -E_BAD_TABLE if \a t is \p NULL. - */ -int osl_get_num_rows(const struct osl_table *t, unsigned *num_rows) -{ - if (!t) - return -E_BAD_TABLE; - assert(t->num_rows >= t->num_invalid_rows); - *num_rows = t->num_rows - t->num_invalid_rows; - return 1; -} - -/** - * Get the rank of a row. - * - * \param t An open osl table. - * \param r The row to get the rank of. - * \param col_num The number of an rbtree column. - * \param rank Result pointer. - * - * The rank is, by definition, the position of the row in the linear order - * determined by an in-order tree walk of the rbtree associated with column - * number \a col_num of \a table. - * - * \return Positive on success, negative on errors. - * - * \sa osl_get_nth_row(). - */ -int osl_get_rank(const struct osl_table *t, struct osl_row *r, - unsigned col_num, unsigned *rank) -{ - struct osl_object obj; - struct osl_column *col; - struct rb_node *node; - int ret = check_rbtree_col(t, col_num, &col); - - if (ret < 0) - return ret; - ret = osl_get_object(t, r, col_num, &obj); - if (ret < 0) - return ret; - ret = search_rbtree(&obj, t, col_num, &node, NULL); - if (ret < 0) - return ret; - ret = rb_rank(node, rank); - if (ret < 0) - return -E_BAD_ROW; - return 1; -} - -/** - * Get the row with n-th greatest value. - * - * \param t Pointer to an open osl table. - * \param col_num The column number. - * \param n The rank of the desired row. - * \param result Row is returned here. - * - * Retrieve the n-th order statistic with respect to the compare function - * of the rbtree column \a col_num. In other words, get that row with - * \a n th greatest value in column \a col_num. It's an error if - * \a col_num is not a rbtree column, or if \a n is larger than the - * number of rows in the table. - * - * \return Positive on success, negative on errors. Possible errors: - * \p E_BAD_TABLE, \p E_BAD_STORAGE_FLAGS, \p E_RB_KEY_NOT_FOUND. - * - * \sa osl_storage_flags, osl_compare_func, osl_get_row(), - * osl_rbtree_last_row(), osl_rbtree_first_row(), osl_get_rank(). - */ -int osl_get_nth_row(const struct osl_table *t, unsigned col_num, - unsigned n, struct osl_row **result) -{ - struct osl_column *col; - struct rb_node *node; - unsigned num_rows; - int ret; - - if (n == 0) - return -E_RB_KEY_NOT_FOUND; - ret = osl_get_num_rows(t, &num_rows); - if (ret < 0) - return ret; - if (n > num_rows) - return -E_RB_KEY_NOT_FOUND; - ret = check_rbtree_col(t, col_num, &col); - if (ret < 0) - return ret; - node = rb_nth(col->rbtree.rb_node, n); - if (!node) - return -E_RB_KEY_NOT_FOUND; - *result = get_row_pointer(node, col->rbtree_num); - return 1; -} - -/** - * Get the row corresponding to the smallest rbtree node of a column. - * - * \param t An open rbtree table. - * \param col_num The number of the rbtree column. - * \param result A pointer to the first row is returned here. - * - * The rbtree node of the smallest object (with respect to the corresponding - * compare function) is selected and the row containing this object is - * returned. It is an error if \a col_num refers to a column without an - * associated rbtree. - * - * \return Positive on success, negative on errors. - * - * \sa osl_get_nth_row(), osl_rbtree_last_row(). - */ -int osl_rbtree_first_row(const struct osl_table *t, unsigned col_num, - struct osl_row **result) -{ - return osl_get_nth_row(t, col_num, 1, result); -} - -/** - * Get the row corresponding to the greatest rbtree node of a column. - * - * \param t The same meaning as in \p osl_rbtree_first_row(). - * \param col_num The same meaning as in \p osl_rbtree_first_row(). - * \param result The same meaning as in \p osl_rbtree_first_row(). - * - * This function works just like osl_rbtree_first_row(), the only difference - * is that the row containing the greatest rather than the smallest object is - * returned. - * - * \return Positive on success, negative on errors. - * - * \sa osl_get_nth_row(), osl_rbtree_first_row(). - */ -int osl_rbtree_last_row(const struct osl_table *t, unsigned col_num, - struct osl_row **result) -{ - unsigned num_rows; - int ret = osl_get_num_rows(t, &num_rows); - - if (ret < 0) - return ret; - return osl_get_nth_row(t, col_num, num_rows, result); -} diff --git a/osl.h b/osl.h deleted file mode 100644 index 3c35fd4e..00000000 --- a/osl.h +++ /dev/null @@ -1,187 +0,0 @@ -#include -/* - * Copyright (C) 2007-2009 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file osl.h User interface for the object storage layer. */ - -/** describes an object of the object storage layer (osl) */ -struct osl_object { - /** Pointer to the data of the object. */ - void *data; - /** The object's size. */ - size_t size; -}; - -/** Flags that change the internal handling of osl tables. */ -enum osl_table_flags { - /** This table will have many rows. */ - OSL_LARGE_TABLE = 1 -}; - -/** The three different types of storage for an osl column */ -enum osl_storage_type { - /** - * All data for this column is stored in one file which gets mmapped by - * osl_open_table(). This is suitable for columns that do not hold much - * data. - */ - OSL_MAPPED_STORAGE, - /** - * Each entry is stored on disk and is loaded on demand by - * open_disk_object(). This is the preferable storage type for large - * objects that need not be in memory all the time. - */ - OSL_DISK_STORAGE, - /** - * Objects for columns of this type are volatile: They are only stored - * in memory and are discarded once the table gets closed. - */ - OSL_NO_STORAGE -}; - -/** - * Additional per-column flags - */ -enum osl_storage_flags { - /** - * Build an rbtree for this column. This is only possible if the - * storage type of the column is either \a OSL_MAPPED_STORAGE or \a - * OSL_NO_STORAGE. In order to lookup objects in the table by using \a - * osl_get_row(), the lookup column must have an associated rbtree. - * - * \sa osl_storage_type, osl_get_row() - */ - OSL_RBTREE = 1, - /** The data for this column will have constant size. */ - OSL_FIXED_SIZE = 2, - /** All values of this column will be different. */ - OSL_UNIQUE = 4, - /** Do not free the data for this column (\p OSL_NO_STORAGE). */ - OSL_DONT_FREE = 8 -}; - -struct osl_table; -struct osl_row; - -/** - * In order to build up an rbtree a compare function for the objects must be - * specified. Such a function always takes pointers to the two objects to be - * compared. It must return -1, zero, or 1, if the first argument is considered - * to be respectively less than, equal to, or greater than the second. If two - * members compare as equal, their order in the sorted array is undefined. - */ -typedef int osl_compare_func(const struct osl_object *obj1, - const struct osl_object *obj2); -typedef int osl_rbtree_loop_func(struct osl_row *row, void *data); - -osl_compare_func osl_hash_compare, uint32_compare; - -/** - * Describes one column of a osl table. - */ -struct osl_column_description { - /** One of zje tree possible types of storage */ - enum osl_storage_type storage_type; - /** Specifies further properties of the column */ - enum osl_storage_flags storage_flags; - /** - * The column name determines the name of the directory where all data - * for this column will be stored. Its hash is stored in the table - * header. This field is ignored if the storage type is \a NO_STORAGE - */ - char *name; - /** - * For columns with an associated rbtree, this must point to a function - * that compares the values of two objects, either a built-in function - * or a function defined by the application may be supplied. This - * field is ignored if the column does not have an associated rbtree. - * - * \sa osl_storage_flags, osl_compare_func - */ - osl_compare_func *compare_function; - /** - * If the \a OSL_FIXED_SIZE flag is set for this column, this value - * determines the fixed size of all objects of this column. It is - * ignored, if \a OSL_FIXED_SIZE is not set. - */ - uint32_t data_size; -}; - -/** - * Describes one osl table. - */ -struct osl_table_description { - /** The directory which contains all files of this table. */ - const char *dir; - /** - * The table name. A subdirectory of \a dir called \a name is created - * at table creation time. It must be a valid name for a subdirectory. - * In particular, no slashes are allowed for \a name. - */ - const char *name; - /** The number of columns of this table. */ - uint16_t num_columns; - /** Further table-wide information. */ - enum osl_table_flags flags; - /** The array describing the individual columns of the table. */ - struct osl_column_description *column_descriptions; -}; - -/** Flags to be passed to \a osl_close_table(). */ -enum osl_close_flags { - /** - * The table header contains a "dirty" flag which specifies whether - * the table is currently open by another process. This flag specifies - * that the dirty flag should be cleared before closing the table. - */ - OSL_MARK_CLEAN = 1, - /** - * If the table contains columns of type \a OSL_NO_STORAGE and this - * flag is passed to osl_close_table(), free(3) is called for each - * object of each column of type \a OSL_NO_STORAGE. - */ - OSL_FREE_VOLATILE = 2 -}; - - - -int osl_create_table(const struct osl_table_description *desc); -int osl_open_table(const struct osl_table_description *desc, - struct osl_table **result); -int osl_close_table(struct osl_table *t, enum osl_close_flags flags); -int osl_get_row(const struct osl_table *t, unsigned col_num, - const struct osl_object *obj, struct osl_row **result); -int osl_get_object(const struct osl_table *t, const struct osl_row *row, - unsigned col_num, struct osl_object *object); -int osl_open_disk_object(const struct osl_table *t, - const struct osl_row *r, unsigned col_num, struct osl_object *obj); -int osl_close_disk_object(struct osl_object *obj); -int osl_add_and_get_row(struct osl_table *t, struct osl_object *objects, - struct osl_row **row); -int osl_add_row(struct osl_table *t, struct osl_object *objects); -int osl_del_row(struct osl_table *t, struct osl_row *row); -int osl_rbtree_loop(const struct osl_table *t, unsigned col_num, - void *private_data, osl_rbtree_loop_func *func); -int osl_rbtree_loop_reverse(const struct osl_table *t, unsigned col_num, - void *private_data, osl_rbtree_loop_func *func); -int osl_update_object(struct osl_table *t, const struct osl_row *r, - unsigned col_num, struct osl_object *obj); -int osl_get_num_rows(const struct osl_table *t, unsigned *num_rows); -int osl_rbtree_first_row(const struct osl_table *t, unsigned col_num, - struct osl_row **result); -int osl_rbtree_last_row(const struct osl_table *t, unsigned col_num, - struct osl_row **result); -int osl_get_nth_row(const struct osl_table *t, unsigned col_num, - unsigned n, struct osl_row **result); -int osl_get_rank(const struct osl_table *t, struct osl_row *r, - unsigned col_num, unsigned *rank); - -int for_each_file_in_dir(const char *dirname, - int (*func)(const char *, void *), void *private_data); -ssize_t para_write_all(int fd, const void *buf, size_t size); -int para_lseek(int fd, off_t *offset, int whence); -int para_write_file(const char *filename, const void *buf, size_t size); - diff --git a/osl_core.h b/osl_core.h deleted file mode 100644 index de53cdd1..00000000 --- a/osl_core.h +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright (C) 2007-2009 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file osl_core.h Object storage layer details, not visible to users. */ - -#include "rbtree.h" -#include "osl.h" -#include "string.h" -#include "portable_io.h" -#include "hash.h" - -/** Internal representation of a column of an osl table. */ -struct osl_column { - /** The memory mapping of this comumn (only used for mapped columns). */ - struct osl_object data_map; - /** The root of the rbtree (only used for columns with rbtrees). */ - struct rb_root rbtree; - /** The index in the array of rb nodes (only used for columns with rbtrees). */ - unsigned rbtree_num; - /** Index for volatile_objects of struct osl_row. */ - unsigned volatile_num; - /** - * Starting point of the data for this column within the index - * (only used for mapped columns). - */ - uint16_t index_offset; - /** - * The hash value of the name of this column. - * - * This is only used for mapped and disk storage columns). - */ - HASH_TYPE name_hash[HASH_SIZE]; -}; - -/** Internal representation of an osl table */ -struct osl_table { - /** Pointer to the table description */ - const struct osl_table_description *desc; - /** The size of the index header of this table. */ - uint16_t index_header_size; - /** Contains the mapping of the table's index file */ - struct osl_object index_map; - /** Total number of rows, including invalid rows. */ - unsigned num_rows; - /** Keeps track of the number of invalid rows in the table. */ - uint32_t num_invalid_rows; - /** Number of columns of type \p OSL_MAPPED_STORAGE. */ - unsigned num_mapped_columns; - /** Number of columns of type \p OSL_NO_STORAGE. */ - unsigned num_volatile_columns; - /** Number of columns of type \p OSL_DISK_STORAGE. */ - unsigned num_disk_storage_columns; - /** Number of columns with associated rbtree. */ - unsigned num_rbtrees; - /** - * The number of the column that determines the name of the disk - * storage objects. - */ - unsigned disk_storage_name_column; - /** The number of bytes of an index entry of a row. */ - unsigned row_index_size; - /** Pointer to the internal representation of the columns. */ - struct osl_column *columns; -}; - -/** Internal representation of a row of an osl table */ -struct osl_row { - /** - * The row number. - * - * It is only used if there is at least one mapped column. - */ - off_t num; - /** Array of size \a num_volatile_columns. */ - struct osl_object *volatile_objects; -}; - -int read_table_desc(struct osl_object *map, struct osl_table_description *desc); -int init_table_structure(const struct osl_table_description *desc, - struct osl_table **table_ptr); -int row_is_invalid(struct osl_table *t, uint32_t row_num); -int get_mapped_object(const struct osl_table *t, unsigned col_num, - uint32_t row_num, struct osl_object *obj); -int para_truncate(const char *filename, off_t size); -int unmap_table(struct osl_table *t, enum osl_close_flags flags); -int init_rbtrees(struct osl_table *t); - -/** - * Flags to be specified for map_table(). - * - * \sa map_table(). - */ -enum map_table_flags { - /** - * Check whether the entries in the table index match the entries in - * the table description. - */ - MAP_TBL_FL_VERIFY_INDEX = 1, - /** Do not complain even if the dirty flag is set. */ - MAP_TBL_FL_IGNORE_DIRTY = 2, - /** Use read-only mappings. */ - MAP_TBL_FL_MAP_RDONLY = 4 -}; - -int map_table(struct osl_table *t, enum map_table_flags flags); -void clear_rbtrees(struct osl_table *t); -int mark_row_invalid(struct osl_table *t, uint32_t row_num); - - -/** - * Get the description of a column by column number - * - * \param d Pointer to the table description. - * \param col_num The number of the column to get the description for. - * - * \return The table description. - * - * \sa struct osl_column_description. - */ -_static_inline_ struct osl_column_description *get_column_description( - const struct osl_table_description *d, unsigned col_num) -{ - return &d->column_descriptions[col_num]; -} - -/** - * Format of the header of the index file of an osl table. - * - * Bytes 16-31 are currently unused. - * - * \sa enum index_column_desc_offsets, HASH_SIZE, osl_table_flags. - */ -enum index_header_offsets { - /** Bytes 0-8: PARASLASH. */ - IDX_PARA_MAGIC = 0, - /** Byte 9: Dirty flag (nonzero if table is mapped). */ - IDX_DIRTY_FLAG = 9, - /** Byte 10: osl table version number. */ - IDX_VERSION = 10, - /** Byte 11: Table flags.*/ - IDX_TABLE_FLAGS = 11, - /** Byte 12,13: Number of columns. */ - IDX_NUM_COLUMNS, - /** Byte 14,15 Size of the index header. */ - IDX_HEADER_SIZE = 14, - /** Column descriptions start here. */ - IDX_COLUMN_DESCRIPTIONS = 32 -}; - -/** - * Format of the column description in the index header. - * - * \sa index_header_offsets. - */ -enum index_column_desc_offsets { - /** Bytes 0,1: Storage_type. */ - IDX_CD_STORAGE_TYPE = 0, - /** Bytes 2,3: Storage_flags. */ - IDX_CD_STORAGE_FLAGS = 2, - /** Bytes 4 - 7: Data_size (only used for fixed size columns). */ - IDX_CD_DATA_SIZE = 4, - /** Bytes 8 - ... Name of the column. */ - IDX_CD_NAME = 8, -}; - -/** Magic string contained in the header of the index file of each osl table. */ -#define PARA_MAGIC "PARASLASH" - -/** - * The minimal number of bytes for a column in the index header. - * - * The column name starts at byte IDX_CD_NAME and must at least contain one - * character, plus the terminating NULL byte. - */ -#define MIN_IDX_COLUMN_DESCRIPTION_SIZE (IDX_CD_NAME + 2) - -/** - * Get the number of bytes used for a column in the index header. - * - * \param name The name of the column. - * - * The amount of space used for a column in the index header of a table depends - * on the (length of the) name of the column. - * - * \return The total number of bytes needed to store one column of this name. - */ -_static_inline_ size_t index_column_description_size(const char *name) -{ - return MIN_IDX_COLUMN_DESCRIPTION_SIZE + strlen(name) - 1; -} - -#define CURRENT_TABLE_VERSION 1 -#define MIN_TABLE_VERSION 1 -#define MAX_TABLE_VERSION 1 -/** An index header must be at least that many bytes long. */ -#define MIN_INDEX_HEADER_SIZE(num_cols) (MIN_IDX_COLUMN_DESCRIPTION_SIZE \ - * num_cols + IDX_COLUMN_DESCRIPTIONS) - -/** - * Get the number of rows from the size of the memory mapping. - * - * \param t Pointer to an open table. - * - * \return The number of rows, including invalid rows. - */ -_static_inline_ unsigned table_num_rows(const struct osl_table *t) -{ - return (t->index_map.size - t->index_header_size) - / t->row_index_size; -} - -/** - * Get the path of the index file from a table description. - * - * \param d The table description. - * - * \return The full path of the index file. Result must be freed by - * the caller. - */ -_static_inline_ char *index_filename(const struct osl_table_description *d) -{ - return make_message("%s/%s/index", d->dir, d->name); -} - -/** - * Get the path of storage of a column. - * - * \param t Pointer to an initialized table. - * \param col_num The number of the column to get the path for. - * - * \return The full path of the mapped data file (mapped storage) or the - * data directory (disk storage). Result must be freed by the caller. - * - * \sa osl_storage_type. - */ -_static_inline_ char *column_filename(const struct osl_table *t, unsigned col_num) -{ - char asc[2 * HASH_SIZE + 1]; - hash_to_asc(t->columns[col_num].name_hash, asc); - return make_message("%s/%s/%s", t->desc->dir, t->desc->name, asc); -} - -/** - * Get the start of an index entry - * - * \param t Pointer to a table which has been mapped. - * \param row_num The number of the row whose index entry should be retrieved. - * \param row_index Result pointer. - * - * \return Positive on success, \p -E_INDEX_CORRUPTION otherwise. - * - * \sa get_cell_index(). - */ -_static_inline_ int get_row_index(const struct osl_table *t, uint32_t row_num, - char **row_index) -{ - uint32_t index_offset; - index_offset = t->index_header_size + t->row_index_size * row_num; - if (index_offset + 8 > t->index_map.size) { - *row_index = NULL; - return -E_INDEX_CORRUPTION; - } - *row_index = (char *)(t->index_map.data) + index_offset; - return 1; -} - -/** - * Get the index entry of a row/column pair. - * - * \param t Pointer to a table which has been mapped. - * \param row_num The number of the row whose index entry should be retrieved. - * \param col_num The number of the column whose index entry should be retrieved. - * \param cell_index Result pointer. - * - * \return Positive on success, \p -E_INDEX_CORRUPTION otherwise. - * - * \sa get_row_index(). - */ -_static_inline_ int get_cell_index(const struct osl_table *t, uint32_t row_num, - uint32_t col_num, char **cell_index) -{ - int ret = get_row_index(t, row_num, cell_index); - if (ret < 0) - return ret; - *cell_index += t->columns[col_num].index_offset; - return ret; -} - -/** - * Change an index entry of a column after object was added. - * - * \param row_index Pointer to the index of the row to update. - * \param col Pointer to the column. - * \param map_size The new size of the data file. - * \param object_size The size of the object just appended to the data file. - * - * This is called right after an object was appended to the data file for a - * mapped column. - * - * \sa get_row_index(). - */ -_static_inline_ void update_cell_index(char *row_index, struct osl_column *col, - uint32_t map_size, uint32_t object_size) -{ - write_u32(row_index + col->index_offset, map_size - object_size - 1); - write_u32(row_index + col->index_offset + 4, object_size + 1); -} - -/** - * Get the full path of a disk storage object - * - * \param t Pointer to an initialized table. - * \param col_num The number of the column the disk storage object belongs to. - * \param ds_name The disk storage name of the object. - * - * \return Pointer to the full path which must be freed by the caller. - * - * \sa column_filename(), disk_storage_name_of_row(). - */ -_static_inline_ char *disk_storage_path(const struct osl_table *t, - unsigned col_num, const char *ds_name) -{ - char *dirname = column_filename(t, col_num); - char *filename = make_message("%s/%s", dirname, ds_name); - free(dirname); - return filename; -} - -/** - * Get the column description of the next column of a given type. - * - * \param type the desired storage type. - * \param col_num the column to start the search. - * \param t the osl table. - * \param cd result is returned here. - * - * \return On success, \a cd contains the column description of the next column - * of type \a type, and the number of that column is returned. Otherwise, \a - * cd is set to \p NULL and the function returns t->num_columns + 1. - * - * \sa FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, FOR_EACH_RBTREE_COLUMN, - * FOR_EACH_DISK_STORAGE_COLUMN, FOR_EACH_VOLATILE_COLUMN, osl_storage_type. - */ -_static_inline_ int next_column_of_type(enum osl_storage_type type, int col_num, - const struct osl_table *t, - const struct osl_column_description **cd) -{ - *cd = NULL; - while (col_num < t->desc->num_columns) { - *cd = get_column_description(t->desc, col_num); - if ((*cd)->storage_type == type) - break; - col_num++; - } - return col_num; -} - -/** - * Find the next column with an associated rbtree. - * - * \param col_num The column to start the search. - * \param t The osl table. - * \param cd Result is returned here. - - * \return On success, \a cd contains the column description of the next column - * with associated rbtree, and the number of that column is returned. - * Otherwise, \a cd is set to \p NULL and the function returns t->num_columns + - * 1. - * - * \sa FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, FOR_EACH_RBTREE_COLUMN, - * FOR_EACH_DISK_STORAGE_COLUMN, FOR_EACH_VOLATILE_COLUMN, osl_storage_flags. - */ -_static_inline_ int next_rbtree_column(int col_num, const struct osl_table *t, - const struct osl_column_description **cd) -{ - *cd = NULL; - while (col_num < t->desc->num_columns) { - *cd = get_column_description(t->desc, col_num); - if ((*cd)->storage_flags & OSL_RBTREE) - break; - col_num++; - } - return col_num; -} - - -/* quite some dirty hacks */ - -/** Round up size of struct osl_row to multiple of 8 */ -#define RB_NODES_OFFSET ((sizeof(struct osl_row) + 7) / 8 * 8) - -/** - * Allocate a new osl row. - * - * \param num_rbtrees The number of rbtrees for this row. - * - * \return A pointer to a zeroed-out area suitable for holding an osl row - * with \a num_rbtrees rbtree columns. - */ -_static_inline_ struct osl_row *allocate_row(unsigned num_rbtrees) -{ - size_t s = RB_NODES_OFFSET + num_rbtrees * sizeof(struct rb_node); - return para_calloc(s); -} - -/** - * Compute the pointer to a rbtree node embedded in a osl row. - * - * \param row The row to extract the rbtree pointer from. - * \param rbtree_num The number of the rbtree node to extract. - * - * \return A pointer to the \a rbtree_num th node contained in \a row. - */ -_static_inline_ struct rb_node *get_rb_node_pointer(const struct osl_row *row, uint32_t rbtree_num) -{ - return ((struct rb_node *)(((char *)row) + RB_NODES_OFFSET)) + rbtree_num; -} - -/** - * Get a pointer to the struct containing the given rbtree node. - * - * \param node Pointer to the rbtree node. - * \param rbtree_num Number of \a node in the array of rbtree nodes. - * - * \return A pointer to the row containing \a node. - */ -_static_inline_ struct osl_row *get_row_pointer(const struct rb_node *node, - unsigned rbtree_num) -{ - node -= rbtree_num; - return (struct osl_row *)(((char *)node) - RB_NODES_OFFSET); -} - -/** - * Compute a cryptographic hash of an osl object. - * - * \param obj the Object to compute the hash value from. - * \param hash Result is returned here. - */ -_static_inline_ void hash_object(const struct osl_object *obj, HASH_TYPE *hash) -{ - hash_function(obj->data, obj->size, hash); -} - -/** - * Get the relative path of an object, given the hash value. - * - * \param t Pointer to an initialized osl table. - * \param hash An arbitrary hash value. - * - * This function is typically called with \a hash being the hash of the object - * stored in the disk storage name column of a row. If the OSL_LARGE_TABLE - * flag is set, the first two hex digits get separated with a slash from the - * remaining digits. - * - * \return The relative path for all disk storage objects corresponding to \a - * hash. - * - * \sa struct osl_table:disk_storage_name_column. - */ -_static_inline_ char *disk_storage_name_of_hash(const struct osl_table *t, HASH_TYPE *hash) -{ - char asc[2 * HASH_SIZE + 2]; - - hash_to_asc(hash, asc); - if (t->desc->flags & OSL_LARGE_TABLE) - return make_message("%.2s/%s", asc, asc + 2); - return para_strdup(asc); -} - -/** - * A wrapper for rename(2). - * - * \param old_path The source path. - * \param new_path The destination path. - * - * \return Standard. - * - * \sa rename(2). - */ -_static_inline_ int para_rename(const char *old_path, const char *new_path) -{ - if (rename(old_path, new_path) < 0) - return -ERRNO_TO_PARA_ERROR(errno); - return 1; -} - -/** - * Iterate over each column of an initialized table. - * - * \param col A pointer to a struct osl_column. - * \param desc Pointer to the table description. - * \param cd Pointer to struct osl_column_description. - * - * On each iteration, \a col will point to the next column of the table and \a - * cd will point to the column description of this column. - * - * \sa struct osl_column FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, - * FOR_EACH_MAPPED_COLUMN, FOR_EACH_DISK_STORAGE_COLUMN, - * FOR_EACH_VOLATILE_COLUMN. - */ -#define FOR_EACH_COLUMN(col, desc, cd) \ - for (col = 0; col < (desc)->num_columns && \ - (cd = get_column_description(desc, col)); col++) - -/** - * Iterate over each column with associated rbtree. - * - * \param col Same meaning as in FOR_EACH_COLUMN(). - * \param table Same meaning as in FOR_EACH_COLUMN(). - * \param cd Same meaning as in FOR_EACH_COLUMN(). - * - * \sa osl_storage_flags::OSL_RBTREE, FOR_EACH_COLUMN, FOR_EACH_COLUMN_OF_TYPE, - * FOR_EACH_MAPPED_COLUMN, FOR_EACH_DISK_STORAGE_COLUMN, - * FOR_EACH_VOLATILE_COLUMN. - */ -#define FOR_EACH_RBTREE_COLUMN(col, table, cd) \ - for (col = next_rbtree_column(0, table, &cd); \ - col < table->desc->num_columns; \ - col = next_rbtree_column(++col, table, &cd)) - -/** - * Iterate over each column of given storage type. - * - * \param type The osl_storage_type to iterate over. - * \param col Same meaning as in FOR_EACH_COLUMN(). - * \param table Same meaning as in FOR_EACH_COLUMN(). - * \param cd Same meaning as in FOR_EACH_COLUMN(). - * - * \sa osl_storage_type, FOR_EACH_COLUMN, FOR_EACH_RBTREE_COLUMN, - * FOR_EACH_MAPPED_COLUMN, FOR_EACH_DISK_STORAGE_COLUMN, - * FOR_EACH_VOLATILE_COLUMN. - */ -#define FOR_EACH_COLUMN_OF_TYPE(type, col, table, cd) \ - for (col = next_column_of_type(type, 0, table, &cd); \ - col < table->desc->num_columns; \ - col = next_column_of_type(type, ++col, table, &cd)) - -/** - * Iterate over each mapped column. - * - * \param col Same meaning as in FOR_EACH_COLUMN(). - * \param table Same meaning as in FOR_EACH_COLUMN(). - * \param cd Same meaning as in FOR_EACH_COLUMN(). - * - * Just like FOR_EACH_COLUMN(), but skip columns which are - * not of type \p OSL_MAPPED_STORAGE. - * - * \sa osl_storage_flags::OSL_MAPPED_STORAGE, FOR_EACH_COLUMN, - * FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, - * FOR_EACH_DISK_STORAGE_COLUMN, FOR_EACH_VOLATILE_COLUMN. - */ -#define FOR_EACH_MAPPED_COLUMN(col, table, cd) \ - FOR_EACH_COLUMN_OF_TYPE(OSL_MAPPED_STORAGE, col, table, cd) - -/** - * Iterate over each disk storage column. - * - * \param col Same meaning as in FOR_EACH_COLUMN(). - * \param table Same meaning as in FOR_EACH_COLUMN(). - * \param cd Same meaning as in FOR_EACH_COLUMN(). - * - * Just like FOR_EACH_COLUMN(), but skip columns which are - * not of type \p OSL_DISK_STORAGE. - * - * \sa osl_storage_flags::OSL_DISK_STORAGE, FOR_EACH_COLUMN, - * FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, - * FOR_EACH_VOLATILE_COLUMN. - */ -#define FOR_EACH_DISK_STORAGE_COLUMN(col, table, cd) \ - FOR_EACH_COLUMN_OF_TYPE(OSL_DISK_STORAGE, col, table, cd) - -/** - * Iterate over each volatile column. - * - * \param col Same meaning as in FOR_EACH_COLUMN(). - * \param table Same meaning as in FOR_EACH_COLUMN(). - * \param cd Same meaning as in FOR_EACH_COLUMN(). - * - * Just like FOR_EACH_COLUMN(), but skip columns which are - * not of type \p OSL_NO_STORAGE. - * - * \sa osl_storage_flags::OSL_NO_STORAGE, FOR_EACH_COLUMN, - * FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, - * FOR_EACH_DISK_STORAGE_COLUMN. - */ -#define FOR_EACH_VOLATILE_COLUMN(col, table, cd) \ - FOR_EACH_COLUMN_OF_TYPE(OSL_NO_STORAGE, col, table, cd) diff --git a/oss_write.c b/oss_write.c index 3ef0d9e8..10a64497 100644 --- a/oss_write.c +++ b/oss_write.c @@ -1,15 +1,17 @@ /* - * Copyright (C) 2009 Andre Noll + * Copyright (C) 2009-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file oss_write.c Paraslash's oss output plugin. */ +#include #include #include #include #include +#include #include "para.h" #include "fd.h" @@ -17,7 +19,9 @@ #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "write.h" +#include "write_common.h" #include "oss_write.cmdline.h" #include "error.h" @@ -28,54 +32,21 @@ struct private_oss_write_data { /** The file handle of the device. */ int fd; - /** - * The samplerate given by command line option or the decoder - * of the writer node group. - */ - int samplerate; - /** - * The number of channels, given by command line option or the - * decoder of the writer node group. - */ - int channels; /** Four bytes for stereo streams, two bytes for mono streams. */ int bytes_per_frame; }; -static int oss_pre_select(struct sched *s, struct writer_node *wn) -{ - struct private_oss_write_data *powd = wn->private_data; - struct writer_node_group *wng = wn->wng; - - if (*wng->loaded - wn->written < powd->bytes_per_frame) - return 0; - para_fd_set(powd->fd, &s->wfds, &s->max_fileno); - return 1; -} - -static int oss_post_select(struct sched *s, struct writer_node *wn) +static void oss_pre_select(struct sched *s, struct task *t) { - int ret; + struct writer_node *wn = container_of(t, struct writer_node, task); struct private_oss_write_data *powd = wn->private_data; - struct writer_node_group *wng = wn->wng; - size_t frames, bytes = *wng->loaded - wn->written; - char *data = *wng->bufp + wn->written; + int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); - if (*wng->input_error < 0 && bytes < powd->bytes_per_frame) { - wn->written = *wng->loaded; - return *wng->input_error; - } - frames = bytes / powd->bytes_per_frame; - if (!frames) /* less than a single frame available */ - goto out; - if (!FD_ISSET(powd->fd, &s->wfds)) - goto out; - ret = write_nonblock(powd->fd, data, frames * powd->bytes_per_frame, 0); + t->error = 0; if (ret < 0) - return ret; - wn->written += ret; -out: - return 1; + sched_min_delay(s); + else if (ret > 0) + para_fd_set(powd->fd, &s->wfds, &s->max_fileno); } static void oss_close(struct writer_node *wn) @@ -97,19 +68,17 @@ static void oss_close(struct writer_node *wn) * incorrectly believe that the device is still in 44.1 kHz mode when actually * the speed is decreased to 22.05 kHz. */ -static int oss_open(struct writer_node *wn) +static int oss_init(struct writer_node *wn, unsigned samplerate, unsigned channels) { - int ret, format = FORMAT, channels, samplerate; + int ret, format = FORMAT; + unsigned ch, rate; struct oss_write_args_info *conf = wn->conf; - struct writer_node_group *wng = wn->wng; - struct private_oss_write_data *powd; + struct private_oss_write_data *powd = wn->private_data; PARA_INFO_LOG("opening %s\n", conf->device_arg); ret = para_open(conf->device_arg, O_WRONLY, 0); if (ret < 0) return ret; - powd = para_calloc(sizeof(*powd)); - wn->private_data = powd; powd->fd = ret; ret = mark_fd_nonblocking(powd->fd); if (ret < 0) @@ -124,22 +93,19 @@ static int oss_open(struct writer_node *wn) if (format != FORMAT) goto err; /* set number of channels */ - if (!conf->channels_given && wng->channels) - channels = *wng->channels; - else - channels = conf->channels_arg; ret = -E_BAD_CHANNEL_COUNT; if (channels == 0) goto err; - powd->channels = channels; - ret = ioctl(powd->fd, SNDCTL_DSP_CHANNELS, &channels); + ch = channels; + ret = ioctl(powd->fd, SNDCTL_DSP_CHANNELS, &ch); if (ret < 0) { ret = -ERRNO_TO_PARA_ERROR(errno); goto err; } - if (powd->channels != channels) + ret = -E_BAD_CHANNEL_COUNT; + if (ch != channels) goto err; - powd->bytes_per_frame = channels * 2; + powd->bytes_per_frame = ch * 2; /* * Set sampling rate @@ -148,19 +114,15 @@ static int oss_open(struct writer_node *wn) * device, the the highest possible speed is automatically used. The * value actually used is returned as the new value of the argument. */ - if (!conf->samplerate_given && wng->samplerate) - samplerate = *wng->samplerate; - else - samplerate = conf->samplerate_arg; - powd->samplerate = samplerate; - ret = ioctl(powd->fd, SNDCTL_DSP_SPEED, &samplerate); + rate = samplerate; + ret = ioctl(powd->fd, SNDCTL_DSP_SPEED, &rate); if (ret < 0) { ret = -ERRNO_TO_PARA_ERROR(errno); goto err; } - if (samplerate != powd->samplerate) { - int min = PARA_MIN(samplerate, powd->samplerate), - max = PARA_MAX(samplerate, powd->samplerate); + if (rate != samplerate) { + unsigned min = PARA_MIN(rate, samplerate), + max = PARA_MAX(rate, samplerate); /* * Check whether the returned sample rate differs significantly * from the requested one. @@ -168,10 +130,10 @@ static int oss_open(struct writer_node *wn) ret = -E_BAD_SAMPLERATE; if (100 * max > 110 * min) /* more than 10% deviation */ goto err; - PARA_NOTICE_LOG("using %dHz rather than %dHz\n", samplerate, - powd->samplerate); + PARA_NOTICE_LOG("using %dHz rather than %dHz\n", rate, + samplerate); } - + wn->min_iqs = powd->bytes_per_frame; return 1; err: close(powd->fd); @@ -179,12 +141,73 @@ err: return ret; } +static void oss_post_select(__a_unused struct sched *s, + struct task *t) +{ + struct writer_node *wn = container_of(t, struct writer_node, task); + struct oss_write_args_info *conf = wn->conf; + struct private_oss_write_data *powd = wn->private_data; + struct btr_node *btrn = wn->btrn; + size_t frames, bytes; + int ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF); + char *data; + + if (ret < 0) + goto out; + if (ret == 0) + return; + if (powd->fd < 0) { + int32_t rate, ch; + ret = -1; + if (!conf->samplerate_given) /* config option trumps btr_exec */ + ret = get_btr_samplerate(wn->btrn, &rate); + if (ret < 0) + rate = conf->samplerate_arg; + ret = -1; + if (!conf->channels_given) + ret = get_btr_channels(wn->btrn, &ch); + if (ret < 0) + ch = conf->channels_arg; + ret = oss_init(wn, rate, ch); + if (ret < 0) + goto out; + return; + } + bytes = btr_next_buffer(btrn, &data); + frames = bytes / powd->bytes_per_frame; + if (!frames) { /* eof and less than a single frame available */ + ret = -E_OSS_EOF; + goto out; + } + ret = 0; + if (!FD_ISSET(powd->fd, &s->wfds)) + goto out; + ret = write_nonblock(powd->fd, data, frames * powd->bytes_per_frame, 0); + if (ret < 0) + goto out; + btr_consume(btrn, ret); + ret = 0; +out: + t->error = ret; + if (ret < 0) + btr_remove_node(btrn); +} + +static int oss_open(struct writer_node *wn) +{ + struct private_oss_write_data *powd; + + powd = para_calloc(sizeof(*powd)); + wn->private_data = powd; + powd->fd = -1; + return 1; +} + __malloc static void *oss_parse_config(const char *options) { int ret; struct oss_write_args_info *conf = para_calloc(sizeof(*conf)); - PARA_INFO_LOG("options: %s, %zd\n", options, strcspn(options, " \t")); ret = oss_cmdline_parser_string(options, conf, "oss_write"); if (ret) goto err_out; @@ -194,6 +217,11 @@ err_out: return NULL; } +static void oss_free_config(void *conf) +{ + oss_cmdline_parser_free(conf); +} + /** * The init function of the oss writer. * @@ -211,6 +239,7 @@ void oss_write_init(struct writer *w) w->pre_select = oss_pre_select; w->post_select = oss_post_select; w->parse_config = oss_parse_config; + w->free_config = oss_free_config; w->shutdown = NULL; w->help = (struct ggo_help) { .short_help = oss_write_args_info_help, diff --git a/osx_write.c b/osx_write.c index c4042889..f3917f64 100644 --- a/osx_write.c +++ b/osx_write.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -11,8 +11,10 @@ * */ +#include #include #include +#include #include "para.h" #include "fd.h" @@ -20,7 +22,9 @@ #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "write.h" +#include "write_common.h" #include "osx_write.cmdline.h" #include "error.h" @@ -163,7 +167,7 @@ static int osx_write_open(struct writer_node *wn) AURenderCallbackStruct inputCallback = {osx_callback, powd}; AudioStreamBasicDescription format; int ret; - struct writer_node_group *wng = wn->wng; + struct btr_node *btrn = wn->btrn; struct osx_write_args_info *conf = wn->conf; wn->private_data = powd; @@ -185,27 +189,32 @@ static int osx_write_open(struct writer_node *wn) if (AudioUnitInitialize(powd->audio_unit)) goto e1; powd->play = 0; - /* Hmmm, let's choose PCM format */ - /* We tell the Output Unit what format we're going to supply data to it. - * This is necessary if you're providing data through an input callback - * AND you want the DefaultOutputUnit to do any format conversions - * necessary from your format to the device's format. + powd->samplerate = conf->samplerate_arg; + powd->channels = conf->channels_arg; + if (!conf->samplerate_given) { + int32_t rate; + if (get_btr_samplerate(btrn, &rate) >= 0) + powd->samplerate = rate; + } + if (!conf->channels_given) { + int32_t ch; + if (get_btr_channels(btrn, &ch) >= 0) + powd->channels = ch; + } + /* + * Choose PCM format. We tell the Output Unit what format we're going + * to supply data to it. This is necessary if you're providing data + * through an input callback AND you want the DefaultOutputUnit to do + * any format conversions necessary from your format to the device's + * format. */ - if (!conf->samplerate_given && wng->samplerate) - powd->samplerate = *wng->samplerate; - else - powd->samplerate = conf->samplerate_arg; format.mSampleRate = powd->samplerate; - /* The specific encoding type of audio stream*/ + /* The specific encoding type of audio stream */ format.mFormatID = kAudioFormatLinearPCM; /* flags specific to each format */ format.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsPacked | ENDIAN_FLAGS; - if (!conf->channels_given && wng->channels) - powd->channels = *wng->channels; - else - powd->channels = conf->channels_arg; format.mChannelsPerFrame = powd->channels; format.mFramesPerPacket = 1; format.mBytesPerPacket = format.mChannelsPerFrame * sizeof(float); @@ -223,6 +232,7 @@ static int osx_write_open(struct writer_node *wn) kAudioUnitScope_Input, 0, &inputCallback, sizeof(inputCallback)) < 0) goto e3; + wn->min_iqs = powd->channels * 2; return 1; e3: destroy_buffers(powd); @@ -249,6 +259,11 @@ err_out: } +static void osx_free_config(void *conf) +{ + osx_cmdline_parser_free(conf); +} + static void osx_write_close(struct writer_node *wn) { struct private_osx_write_data *powd = wn->private_data; @@ -263,70 +278,81 @@ static void osx_write_close(struct writer_node *wn) static int need_new_buffer(struct writer_node *wn) { - struct writer_node_group *wng = wn->wng; struct private_osx_write_data *powd = wn->private_data; - if (*wng->loaded < sizeof(short)) + if (wn->min_iqs > btr_get_input_queue_size(wn->btrn)) return 0; if (powd->to->remaining) /* Non empty buffer, must still be playing */ return 0; return 1; } -static int osx_write_post_select(__a_unused struct sched *s, - struct writer_node *wn) +static void osx_write_post_select(__a_unused struct sched *s, struct task *t) { + struct writer_node *wn = container_of(t, struct writer_node, task); struct private_osx_write_data *powd = wn->private_data; - struct writer_node_group *wng = wn->wng; - short *data = (short*)*wng->bufp; + struct btr_node *btrn = wn->btrn; + char *data; + size_t bytes; + int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); + if (ret <= 0) + goto out; if (!need_new_buffer(wn)) - return 1; - fill_buffer(powd->to, data, *wng->loaded / sizeof(short)); + goto out; + btr_merge(btrn, wn->min_iqs); + bytes = btr_next_buffer(btrn, &data); + fill_buffer(powd->to, (short *)data, bytes / sizeof(short)); + btr_consume(btrn, bytes); powd->to = powd->to->next; - wn->written = *wng->loaded; if (!powd->play) { + ret = -E_UNIT_START; if (AudioOutputUnitStart(powd->audio_unit)) - return -E_UNIT_START; + goto out; powd->play = 1; } - return 1; + ret = 1; +out: + if (ret < 0) + btr_remove_node(btrn); + t->error = ret; } -static int osx_write_pre_select(struct sched *s, __a_unused struct writer_node *wn) +static void osx_write_pre_select(struct sched *s, struct task *t) { + struct writer_node *wn = container_of(t, struct writer_node, task); struct private_osx_write_data *powd = wn->private_data; - struct writer_node_group *wng = wn->wng; - size_t numbytes = powd->to->remaining * sizeof(short); struct timeval tmp = {.tv_sec = 1, .tv_usec = 0}, delay = tmp; unsigned long divisor; + size_t numbytes = powd->to->remaining * sizeof(short); + int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); - if (!numbytes && *wng->loaded >= sizeof(short)) - goto min_delay; /* there's a buffer to fill */ - if (!numbytes) - return 1; - divisor = powd->samplerate * powd->channels * 2 / numbytes; + if (ret < 0) + sched_min_delay(s); + if (ret <= 0 || numbytes < wn->min_iqs) + return; + divisor = powd->samplerate * wn->min_iqs / numbytes; if (divisor) tv_divide(divisor, &tmp, &delay); - if (tv_diff(&s->timeout, &delay, NULL) > 0) - s->timeout = delay; -// PARA_DEBUG_LOG("delay: %lu:%lu\n", (long unsigned) s->timeout.tv_sec, -// (long unsigned) s->timeout.tv_usec); - return 1; -min_delay: - PARA_DEBUG_LOG("%s\n", "minimal delay"); - s->timeout.tv_sec = 0; - s->timeout.tv_usec = 1; - return 1; + sched_request_timeout(&delay, s); } /** the init function of the osx writer */ void osx_write_init(struct writer *w) { + struct osx_write_args_info dummy; + + osx_cmdline_parser_init(&dummy); w->open = osx_write_open; w->close = osx_write_close; w->pre_select = osx_write_pre_select; w->post_select = osx_write_post_select; w->parse_config = osx_write_parse_config; + w->free_config = osx_free_config; w->shutdown = NULL; /* nothing to do */ + w->help = (struct ggo_help) { + .short_help = osx_write_args_info_help, + .detailed_help = osx_write_args_info_detailed_help + }; + osx_cmdline_parser_free(&dummy); } diff --git a/para.h b/para.h index e4e9a53d..ae3b5b2b 100644 --- a/para.h +++ b/para.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2009 Andre Noll + * Copyright (C) 1997-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -131,9 +131,10 @@ va_end(argp); \ } -/** version text used by various commands if -V switch was given */ -#define VERSION_TEXT(prefix) "para_" prefix " " PACKAGE_VERSION " (" CODENAME ")" "\n" \ - "Copyright (C) 2009 Andre Noll\n" \ +/** Version text used by various commands if -V switch was given. */ +#define VERSION_TEXT(prefix) "para_" prefix " " PACKAGE_VERSION \ + " (" GIT_VERSION ": " CODENAME ")" "\n" \ + "Copyright (C) 2010 Andre Noll\n" \ "This is free software with ABSOLUTELY NO WARRANTY." \ " See COPYING for details.\n" \ "Written by Andre Noll.\n" \ @@ -145,16 +146,17 @@ printf("%s", VERSION_TEXT(_prefix)); \ exit(EXIT_SUCCESS); \ } + +/* Sent by para_client to initiate the authentication procedure. */ +#define AUTH_REQUEST_MSG "auth rsa " /** sent by para_server for commands that expect a data file */ #define AWAITING_DATA_MSG "\nAwaiting Data." /** sent by para_server if authentication was successful */ -#define PROCEED_MSG "\nProceed.\n" +#define PROCEED_MSG "Proceed." /** length of the \p PROCEED_MSG string */ #define PROCEED_MSG_LEN strlen(PROCEED_MSG) /** sent by para_client to indicate the end of the command line */ #define EOC_MSG "\nEnd of Command." -/** sent by para_client, followed by the decrypted challenge number */ -#define CHALLENGE_RESPONSE_MSG "challenge_response:" /* exec */ int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds); @@ -180,10 +182,8 @@ enum status_items {STATUS_ITEM_ENUM NUM_STAT_ITEMS}; extern const char *status_item_list[]; /** Loop over each status item. */ #define FOR_EACH_STATUS_ITEM(i) for (i = 0; i < NUM_STAT_ITEMS; i++) -int stat_item_valid(const char *item); -int stat_line_valid(const char *); -void stat_client_write(const char *msg, int itemnum); -int stat_client_add(int fd, uint64_t mask); +int for_each_stat_item(char *item_buf, size_t num_bytes, + int (*item_handler)(int, char *)); __printf_2_3 void para_log(int, const char*, ...); @@ -224,7 +224,7 @@ __printf_2_3 void para_log(int, const char*, ...); * * \return An integer between zero and \p max - 1, inclusively. */ -static inline long int para_random(unsigned max) +_static_inline_ long int para_random(unsigned max) { return ((max + 0.0) * (random() / (RAND_MAX + 1.0))); } @@ -255,3 +255,7 @@ static inline long int para_random(unsigned max) #define FEC_EOF_PACKET "\xec\x0d\xcc\xfe\0\0\0\0" \ "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0" #define FEC_EOF_PACKET_LEN 32 + +/** Used to avoid a shortcoming in vim's syntax highlighting. */ +#define EMBRACE(...) { __VA_ARGS__} + diff --git a/pics/paraslash/default.jpg b/pics/paraslash/default.jpg deleted file mode 100644 index 451b9164..00000000 Binary files a/pics/paraslash/default.jpg and /dev/null differ diff --git a/pics/screenshots/audiod.log b/pics/screenshots/audiod.log deleted file mode 100644 index c9e43663..00000000 --- a/pics/screenshots/audiod.log +++ /dev/null @@ -1,549 +0,0 @@ -Dec 08 19:23:27 meins 2 log_welcome: welcome to para_audiod git (Sat Dec 8 13:42:52 MET 2007) -Dec 08 19:23:27 meins 2 init_writers: maximal number of writers: 3 -Dec 08 19:23:27 meins 2 check_writer_arg: checking alsa -d plug:swmix -Dec 08 19:23:27 meins 2 alsa_parse_config: options: -d plug:swmix, 2 -Dec 08 19:23:27 meins 2 alsa_parse_config: help given: 0 -Dec 08 19:23:27 meins 2 init_writers: mp3 writer #0: alsa -Dec 08 19:23:27 meins 2 check_writer_arg: checking alsa -d plug:swmix -Dec 08 19:23:27 meins 2 alsa_parse_config: options: -d plug:swmix, 2 -Dec 08 19:23:27 meins 2 alsa_parse_config: help given: 0 -Dec 08 19:23:27 meins 2 init_writers: ogg writer #0: alsa -Dec 08 19:23:27 meins 2 check_writer_arg: checking alsa -d plug:swmix -Dec 08 19:23:27 meins 2 alsa_parse_config: options: -d plug:swmix, 2 -Dec 08 19:23:27 meins 2 alsa_parse_config: help given: 0 -Dec 08 19:23:27 meins 2 init_writers: aac writer #0: alsa -Dec 08 19:23:27 meins 2 init_receivers: initializing http receiver -Dec 08 19:23:27 meins 2 init_receivers: initializing dccp receiver -Dec 08 19:23:27 meins 2 init_receivers: initializing ortp receiver -Dec 08 19:23:27 meins 2 init_filters: maximal number of filters: 6 -Dec 08 19:23:27 meins 2 add_filter: mp3 filter 1: mp3dec -Dec 08 19:23:27 meins 2 add_filter: mp3 filter 2: compress -Dec 08 19:23:27 meins 2 add_filter: ogg filter 1: oggdec -Dec 08 19:23:27 meins 2 add_filter: ogg filter 2: compress -Dec 08 19:23:27 meins 2 add_filter: aac filter 1: aacdec -Dec 08 19:23:27 meins 2 add_filter: aac filter 2: compress -Dec 08 19:23:27 meins 2 clear_slot: clearing slot 0 -Dec 08 19:23:27 meins 2 clear_slot: clearing slot 1 -Dec 08 19:23:27 meins 2 clear_slot: clearing slot 2 -Dec 08 19:23:27 meins 2 clear_slot: clearing slot 3 -Dec 08 19:23:27 meins 2 clear_slot: clearing slot 4 -Dec 08 19:23:27 meins 2 init_grabbing: grab init -Dec 08 19:23:27 meins 2 setup_signal_handling: signal pipe: fd 4 -Dec 08 19:23:27 meins 3 audiod_get_socket: local socket: /var/paraslash/audiod_socket.meins -Dec 08 19:23:27 meins 2 daemon_init: daemonizing -Dec 08 19:23:27 meins 2 init_sched: initializing scheduler -Dec 08 19:23:27 meins 2 register_task: registering signal task (0x80631bc) -Dec 08 19:23:27 meins 2 register_task: registering command task (0xbfec3ec4) -Dec 08 19:23:27 meins 2 register_task: registering status task (0x80632e8) -Dec 08 19:23:27 meins 2 register_task: registering audiod task (0xbfec3fe8) -Dec 08 19:23:27 meins 2 status_pre_select: clock diff count: 5 -Dec 08 19:23:27 meins 2 client_open: loglevel: 5 -Dec 08 19:23:27 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:23:27 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:23:27 meins 3 client_open: connecting localhost:2990 -Dec 08 19:23:27 meins 2 register_task: registering client (0x8067878) -Dec 08 19:23:27 meins 2 client_post_select: --> auth rc4 maan -Dec 08 19:23:27 meins 2 client_post_select: <-- [challenge] -Dec 08 19:23:27 meins 2 client_post_select: --> 1499570993 -Dec 08 19:23:27 meins 2 client_post_select: decrypting session key -Dec 08 19:23:27 meins 2 enable_crypt: rc4 encryption activated for fd 7 -Dec 08 19:23:27 meins 2 unregister_task: unregistering client (0x8067878) -Dec 08 19:23:28 meins 2 handle_connect: connection from user 409, buf: stat -Dec 08 19:23:28 meins 2 handle_connect: argv[0]: stat, argc= 1 -Dec 08 19:23:28 meins 2 com_stat: mask: 0xffffffff -Dec 08 19:23:28 meins 2 stat_client_add: adding client on fd 7 -Dec 08 19:23:28 meins 2 dump_stat_client_list: stat client on fd 7 -Dec 08 19:23:28 meins 2 status_pre_select: clock diff count: 4 -Dec 08 19:23:28 meins 2 client_open: loglevel: 5 -Dec 08 19:23:28 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:23:28 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:23:28 meins 3 client_open: connecting localhost:2990 -Dec 08 19:23:28 meins 2 register_task: registering client (0x8067878) -Dec 08 19:23:28 meins 2 client_post_select: --> auth rc4 maan -Dec 08 19:23:28 meins 2 client_post_select: <-- [challenge] -Dec 08 19:23:28 meins 2 client_post_select: --> 1400991892 -Dec 08 19:23:28 meins 2 client_post_select: decrypting session key -Dec 08 19:23:28 meins 2 enable_crypt: rc4 encryption activated for fd 8 -Dec 08 19:23:28 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms -Dec 08 19:23:28 meins 2 unregister_task: unregistering client (0x8067878) -Dec 08 19:23:29 meins 2 status_pre_select: clock diff count: 3 -Dec 08 19:23:29 meins 2 client_open: loglevel: 5 -Dec 08 19:23:29 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:23:29 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:23:29 meins 3 client_open: connecting localhost:2990 -Dec 08 19:23:29 meins 2 register_task: registering client (0x8067878) -Dec 08 19:23:29 meins 2 client_post_select: --> auth rc4 maan -Dec 08 19:23:29 meins 2 client_post_select: <-- [challenge] -Dec 08 19:23:29 meins 2 client_post_select: --> 1772412478 -Dec 08 19:23:29 meins 2 client_post_select: decrypting session key -Dec 08 19:23:29 meins 2 enable_crypt: rc4 encryption activated for fd 8 -Dec 08 19:23:29 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms -Dec 08 19:23:29 meins 2 unregister_task: unregistering client (0x8067878) -Dec 08 19:23:30 meins 2 status_pre_select: clock diff count: 2 -Dec 08 19:23:30 meins 2 client_open: loglevel: 5 -Dec 08 19:23:30 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:23:30 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:23:30 meins 3 client_open: connecting localhost:2990 -Dec 08 19:23:30 meins 2 register_task: registering client (0x8067878) -Dec 08 19:23:30 meins 2 client_post_select: --> auth rc4 maan -Dec 08 19:23:30 meins 2 client_post_select: <-- [challenge] -Dec 08 19:23:30 meins 2 client_post_select: --> 1882631692 -Dec 08 19:23:30 meins 2 client_post_select: decrypting session key -Dec 08 19:23:30 meins 2 enable_crypt: rc4 encryption activated for fd 8 -Dec 08 19:23:30 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms -Dec 08 19:23:30 meins 2 unregister_task: unregistering client (0x8067878) -Dec 08 19:23:31 meins 2 status_pre_select: clock diff count: 1 -Dec 08 19:23:31 meins 2 client_open: loglevel: 5 -Dec 08 19:23:31 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:23:31 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:23:31 meins 3 client_open: connecting localhost:2990 -Dec 08 19:23:31 meins 2 register_task: registering client (0x8067878) -Dec 08 19:23:31 meins 2 client_post_select: --> auth rc4 maan -Dec 08 19:23:31 meins 2 client_post_select: <-- [challenge] -Dec 08 19:23:31 meins 2 client_post_select: --> 1630455651 -Dec 08 19:23:31 meins 2 client_post_select: decrypting session key -Dec 08 19:23:31 meins 2 enable_crypt: rc4 encryption activated for fd 8 -Dec 08 19:23:31 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms -Dec 08 19:23:31 meins 2 unregister_task: unregistering client (0x8067878) -Dec 08 19:23:36 meins 2 client_open: loglevel: 5 -Dec 08 19:23:36 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:23:36 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:23:36 meins 3 client_open: connecting localhost:2990 -Dec 08 19:23:36 meins 2 register_task: registering client (0x8067878) -Dec 08 19:23:36 meins 2 client_post_select: --> auth rc4 maan -Dec 08 19:23:36 meins 2 client_post_select: <-- [challenge] -Dec 08 19:23:36 meins 2 client_post_select: --> 759215314 -Dec 08 19:23:36 meins 2 client_post_select: decrypting session key -Dec 08 19:23:36 meins 2 enable_crypt: rc4 encryption activated for fd 8 -Dec 08 19:23:36 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:23:36 meins 2 clear_slot: clearing slot 0 -Dec 08 19:23:36 meins 3 open_receiver: started ogg: dccp receiver in slot 0 -Dec 08 19:23:36 meins 2 register_task: registering dccp receiver node (0x806a324) -Dec 08 19:23:37 meins 2 open_filters: opening ogg filters -Dec 08 19:23:37 meins 3 open_filters: ogg filter 1/2 (oggdec) started in slot 0 -Dec 08 19:23:37 meins 3 open_filters: ogg filter 2/2 (compress) started in slot 0 -Dec 08 19:23:37 meins 2 register_task: registering filter chain (0x807460c) -Dec 08 19:23:37 meins 3 ogg_convert: input buffer: 17032, opening ov callbacks -Dec 08 19:23:37 meins 3 ogg_convert: 2 channels, 44100 Hz -Dec 08 19:23:37 meins 2 open_writers: opening ogg writers -Dec 08 19:23:37 meins 2 open_writers: samplerate: 44100 -Dec 08 19:23:37 meins 3 wng_open: opening wng 0x80a5660 with 1 writer(s) -Dec 08 19:23:37 meins 2 register_task: registering (0x80a5684) -Dec 08 19:23:37 meins 2 alsa_open: 2 channel(s), 44100Hz -Dec 08 19:23:37 meins 2 alsa_open: buffer time: 170658 -Dec 08 19:23:37 meins 2 alsa_open: buffer size: 7526, period_size: 940 -Dec 08 19:23:43 meins 3 rn_event_handler: dccp_recv: end of file -Dec 08 19:23:43 meins 2 unregister_task: unregistering dccp receiver node (0x806a324) -Dec 08 19:23:43 meins 2 unregister_task: unregistering client (0x8067878) -Dec 08 19:23:43 meins 2 kill_all_decoders: unregistering writer node group in slot 0 -Dec 08 19:23:43 meins 2 unregister_task: unregistering writer node group (0x80a5684) -Dec 08 19:23:43 meins 2 kill_all_decoders: unregistering filter chain in slot 0 -Dec 08 19:23:43 meins 2 unregister_task: unregistering filter chain (0x807460c) -Dec 08 19:23:43 meins 2 try_to_close_slot: closing slot 0 -Dec 08 19:23:43 meins 3 wng_close: closing wng with 1 writer(s) -Dec 08 19:23:43 meins 2 alsa_close: closing writer node 0x80a57b0 -Dec 08 19:23:43 meins 3 close_filters: closing filter chain 0x80745e0 -Dec 08 19:23:43 meins 2 close_filters: closing oggdec filter -Dec 08 19:23:43 meins 2 close_filters: closing compress filter -Dec 08 19:23:43 meins 3 close_receiver: closing ogg receiver in slot 0 (eof = 1) -Dec 08 19:23:43 meins 2 clear_slot: clearing slot 0 -Dec 08 19:23:48 meins 2 client_open: loglevel: 5 -Dec 08 19:23:48 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:23:48 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:23:48 meins 3 client_open: connecting localhost:2990 -Dec 08 19:23:48 meins 2 register_task: registering client (0x8067878) -Dec 08 19:23:48 meins 2 client_post_select: --> auth rc4 maan -Dec 08 19:23:48 meins 2 client_post_select: <-- [challenge] -Dec 08 19:23:48 meins 2 client_post_select: --> 960122352 -Dec 08 19:23:48 meins 2 client_post_select: decrypting session key -Dec 08 19:23:48 meins 2 enable_crypt: rc4 encryption activated for fd 8 -Dec 08 19:23:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:23:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:23:58 meins 2 clear_slot: clearing slot 0 -Dec 08 19:23:58 meins 3 open_receiver: started mp3: http receiver in slot 0 -Dec 08 19:23:58 meins 2 register_task: registering http receiver node (0x806a204) -Dec 08 19:23:58 meins 2 http_recv_post_select: sending http request -Dec 08 19:23:58 meins 2 http_recv_post_select: received ok msg, streaming -Dec 08 19:23:59 meins 2 open_filters: opening mp3 filters -Dec 08 19:23:59 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0 -Dec 08 19:23:59 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0 -Dec 08 19:23:59 meins 2 register_task: registering filter chain (0x807260c) -Dec 08 19:23:59 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 173 ms left -Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 147 ms left -Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 121 ms left -Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 95 ms left -Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 69 ms left -Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 43 ms left -Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 17 ms left -Dec 08 19:23:59 meins 2 open_writers: opening mp3 writers -Dec 08 19:23:59 meins 2 open_writers: samplerate: 44100 -Dec 08 19:23:59 meins 3 wng_open: opening wng 0x80a3c18 with 1 writer(s) -Dec 08 19:23:59 meins 2 register_task: registering (0x80a3c3c) -Dec 08 19:23:59 meins 2 alsa_open: 2 channel(s), 44100Hz -Dec 08 19:23:59 meins 2 alsa_open: buffer time: 170658 -Dec 08 19:23:59 meins 2 alsa_open: buffer size: 7526, period_size: 940 -Dec 08 19:24:43 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:24:57 meins 4 alsa_write_post_select: EAGAIN -Dec 08 19:24:57 meins 4 alsa_write_post_select: EAGAIN -Dec 08 19:25:33 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:25:43 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:25:51 meins 3 rn_event_handler: http_recv: end of file -Dec 08 19:25:51 meins 2 unregister_task: unregistering http receiver node (0x806a204) -Dec 08 19:25:51 meins 2 unregister_task: unregistering client (0x8067878) -Dec 08 19:25:51 meins 2 kill_all_decoders: unregistering writer node group in slot 0 -Dec 08 19:25:51 meins 2 unregister_task: unregistering writer node group (0x80a3c3c) -Dec 08 19:25:51 meins 2 kill_all_decoders: unregistering filter chain in slot 0 -Dec 08 19:25:51 meins 2 unregister_task: unregistering filter chain (0x807260c) -Dec 08 19:25:51 meins 2 try_to_close_slot: closing slot 0 -Dec 08 19:25:51 meins 3 wng_close: closing wng with 1 writer(s) -Dec 08 19:25:51 meins 2 alsa_close: closing writer node 0x806a440 -Dec 08 19:25:51 meins 3 close_filters: closing filter chain 0x80725e0 -Dec 08 19:25:51 meins 2 close_filters: closing mp3dec filter -Dec 08 19:25:51 meins 2 close_filters: closing compress filter -Dec 08 19:25:51 meins 3 close_receiver: closing mp3 receiver in slot 0 (eof = 1) -Dec 08 19:25:51 meins 2 clear_slot: clearing slot 0 -Dec 08 19:25:56 meins 2 client_open: loglevel: 5 -Dec 08 19:25:56 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:25:56 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:25:56 meins 3 client_open: connecting localhost:2990 -Dec 08 19:25:56 meins 2 register_task: registering client (0x8067878) -Dec 08 19:25:56 meins 2 client_post_select: --> auth rc4 maan -Dec 08 19:25:56 meins 2 client_post_select: <-- [challenge] -Dec 08 19:25:56 meins 2 client_post_select: --> 563263924 -Dec 08 19:25:56 meins 2 client_post_select: decrypting session key -Dec 08 19:25:56 meins 2 enable_crypt: rc4 encryption activated for fd 8 -Dec 08 19:25:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:26:06 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:26:06 meins 2 clear_slot: clearing slot 0 -Dec 08 19:26:06 meins 3 open_receiver: started mp3: http receiver in slot 0 -Dec 08 19:26:06 meins 2 register_task: registering http receiver node (0x806a29c) -Dec 08 19:26:06 meins 2 http_recv_post_select: sending http request -Dec 08 19:26:06 meins 2 http_recv_post_select: received ok msg, streaming -Dec 08 19:26:06 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:26:06 meins 2 open_filters: opening mp3 filters -Dec 08 19:26:06 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0 -Dec 08 19:26:06 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0 -Dec 08 19:26:06 meins 2 register_task: registering filter chain (0x80726dc) -Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 173 ms left -Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 147 ms left -Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 121 ms left -Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 95 ms left -Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 69 ms left -Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 43 ms left -Dec 08 19:26:07 meins 2 audiod_pre_select: initial delay: 17 ms left -Dec 08 19:26:07 meins 2 open_writers: opening mp3 writers -Dec 08 19:26:07 meins 2 open_writers: samplerate: 44100 -Dec 08 19:26:07 meins 3 wng_open: opening wng 0x80a3cb8 with 1 writer(s) -Dec 08 19:26:07 meins 2 register_task: registering (0x80a3cdc) -Dec 08 19:26:07 meins 2 alsa_open: 2 channel(s), 44100Hz -Dec 08 19:26:07 meins 2 alsa_open: buffer time: 170658 -Dec 08 19:26:07 meins 2 alsa_open: buffer size: 7526, period_size: 940 -Dec 08 19:26:51 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:26:58 meins 2 unregister_task: unregistering client (0x8067878) -Dec 08 19:26:58 meins 2 kill_all_decoders: unregistering writer node group in slot 0 -Dec 08 19:26:58 meins 2 unregister_task: unregistering writer node group (0x80a3cdc) -Dec 08 19:26:58 meins 2 kill_all_decoders: unregistering filter chain in slot 0 -Dec 08 19:26:58 meins 2 unregister_task: unregistering filter chain (0x80726dc) -Dec 08 19:26:58 meins 2 kill_all_decoders: unregistering receiver_node in slot 0 -Dec 08 19:26:58 meins 2 unregister_task: unregistering http receiver node (0x806a29c) -Dec 08 19:26:58 meins 2 try_to_close_slot: closing slot 0 -Dec 08 19:26:58 meins 3 wng_close: closing wng with 1 writer(s) -Dec 08 19:26:58 meins 2 alsa_close: closing writer node 0x80a3e08 -Dec 08 19:26:58 meins 3 close_filters: closing filter chain 0x80726b0 -Dec 08 19:26:58 meins 2 close_filters: closing mp3dec filter -Dec 08 19:26:58 meins 2 close_filters: closing compress filter -Dec 08 19:26:58 meins 3 close_receiver: closing mp3 receiver in slot 0 (eof = 1) -Dec 08 19:26:58 meins 2 clear_slot: clearing slot 0 -Dec 08 19:27:04 meins 2 client_open: loglevel: 5 -Dec 08 19:27:04 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:27:04 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:27:04 meins 3 client_open: connecting localhost:2990 -Dec 08 19:27:04 meins 5 makesock: can not create TCP socket localhost#2990. -Dec 08 19:27:04 meins 5 client_open: Connection refused -Dec 08 19:27:09 meins 2 client_open: loglevel: 5 -Dec 08 19:27:09 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:27:09 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:27:09 meins 3 client_open: connecting localhost:2990 -Dec 08 19:27:09 meins 5 makesock: can not create TCP socket localhost#2990. -Dec 08 19:27:09 meins 5 client_open: Connection refused -Dec 08 19:27:14 meins 2 client_open: loglevel: 5 -Dec 08 19:27:14 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:27:14 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:27:14 meins 3 client_open: connecting localhost:2990 -Dec 08 19:27:14 meins 5 makesock: can not create TCP socket localhost#2990. -Dec 08 19:27:14 meins 5 client_open: Connection refused -Dec 08 19:27:19 meins 2 client_open: loglevel: 5 -Dec 08 19:27:19 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:27:19 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:27:19 meins 3 client_open: connecting localhost:2990 -Dec 08 19:27:19 meins 5 makesock: can not create TCP socket localhost#2990. -Dec 08 19:27:19 meins 5 client_open: Connection refused -Dec 08 19:27:24 meins 2 client_open: loglevel: 5 -Dec 08 19:27:24 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:27:24 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:27:24 meins 3 client_open: connecting localhost:2990 -Dec 08 19:27:24 meins 5 makesock: can not create TCP socket localhost#2990. -Dec 08 19:27:24 meins 5 client_open: Connection refused -Dec 08 19:27:29 meins 2 client_open: loglevel: 5 -Dec 08 19:27:29 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:27:29 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:27:29 meins 3 client_open: connecting localhost:2990 -Dec 08 19:27:29 meins 5 makesock: can not create TCP socket localhost#2990. -Dec 08 19:27:29 meins 5 client_open: Connection refused -Dec 08 19:27:34 meins 2 client_open: loglevel: 5 -Dec 08 19:27:34 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:27:34 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:27:34 meins 3 client_open: connecting localhost:2990 -Dec 08 19:27:34 meins 2 register_task: registering client (0x8067878) -Dec 08 19:27:34 meins 2 client_post_select: --> auth rc4 maan -Dec 08 19:27:34 meins 2 client_post_select: <-- [challenge] -Dec 08 19:27:34 meins 2 client_post_select: --> 1725057215 -Dec 08 19:27:34 meins 2 client_post_select: decrypting session key -Dec 08 19:27:34 meins 2 enable_crypt: rc4 encryption activated for fd 8 -Dec 08 19:27:34 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:27:45 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:27:45 meins 2 clear_slot: clearing slot 0 -Dec 08 19:27:45 meins 3 open_receiver: started mp3: http receiver in slot 0 -Dec 08 19:27:45 meins 2 register_task: registering http receiver node (0x806a7b4) -Dec 08 19:27:45 meins 2 http_recv_post_select: sending http request -Dec 08 19:27:45 meins 2 http_recv_post_select: received ok msg, streaming -Dec 08 19:27:46 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:27:46 meins 2 open_filters: opening mp3 filters -Dec 08 19:27:46 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0 -Dec 08 19:27:46 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0 -Dec 08 19:27:46 meins 2 register_task: registering filter chain (0x80729f4) -Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 173 ms left -Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 147 ms left -Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 121 ms left -Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 95 ms left -Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 69 ms left -Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 43 ms left -Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 17 ms left -Dec 08 19:27:46 meins 2 open_writers: opening mp3 writers -Dec 08 19:27:46 meins 2 open_writers: samplerate: 44100 -Dec 08 19:27:46 meins 3 wng_open: opening wng 0x80a4000 with 1 writer(s) -Dec 08 19:27:46 meins 2 register_task: registering (0x80a4024) -Dec 08 19:27:46 meins 2 alsa_open: 2 channel(s), 44100Hz -Dec 08 19:27:46 meins 2 alsa_open: buffer time: 170658 -Dec 08 19:27:46 meins 2 alsa_open: buffer size: 7526, period_size: 940 -Dec 08 19:28:30 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:28:58 meins 3 rn_event_handler: http_recv: end of file -Dec 08 19:28:58 meins 2 unregister_task: unregistering http receiver node (0x806a7b4) -Dec 08 19:28:58 meins 2 unregister_task: unregistering client (0x8067878) -Dec 08 19:28:58 meins 2 kill_all_decoders: unregistering writer node group in slot 0 -Dec 08 19:28:58 meins 2 unregister_task: unregistering writer node group (0x80a4024) -Dec 08 19:28:58 meins 2 kill_all_decoders: unregistering filter chain in slot 0 -Dec 08 19:28:58 meins 2 unregister_task: unregistering filter chain (0x80729f4) -Dec 08 19:28:58 meins 2 try_to_close_slot: closing slot 0 -Dec 08 19:28:58 meins 3 wng_close: closing wng with 1 writer(s) -Dec 08 19:28:58 meins 2 alsa_close: closing writer node 0x80a4150 -Dec 08 19:28:58 meins 3 close_filters: closing filter chain 0x80729c8 -Dec 08 19:28:58 meins 2 close_filters: closing mp3dec filter -Dec 08 19:28:58 meins 2 close_filters: closing compress filter -Dec 08 19:28:58 meins 3 close_receiver: closing mp3 receiver in slot 0 (eof = 1) -Dec 08 19:28:58 meins 2 clear_slot: clearing slot 0 -Dec 08 19:29:03 meins 2 client_open: loglevel: 5 -Dec 08 19:29:03 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:29:03 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:29:03 meins 3 client_open: connecting localhost:2990 -Dec 08 19:29:03 meins 2 register_task: registering client (0x8067878) -Dec 08 19:29:03 meins 2 client_post_select: --> auth rc4 maan -Dec 08 19:29:03 meins 2 client_post_select: <-- [challenge] -Dec 08 19:29:03 meins 2 client_post_select: --> 705434988 -Dec 08 19:29:03 meins 2 client_post_select: decrypting session key -Dec 08 19:29:03 meins 2 enable_crypt: rc4 encryption activated for fd 8 -Dec 08 19:29:03 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:29:13 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:29:13 meins 2 clear_slot: clearing slot 0 -Dec 08 19:29:13 meins 3 open_receiver: started ogg: dccp receiver in slot 0 -Dec 08 19:29:13 meins 2 register_task: registering dccp receiver node (0x806a804) -Dec 08 19:29:13 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:29:13 meins 2 open_filters: opening ogg filters -Dec 08 19:29:13 meins 3 open_filters: ogg filter 1/2 (oggdec) started in slot 0 -Dec 08 19:29:13 meins 3 open_filters: ogg filter 2/2 (compress) started in slot 0 -Dec 08 19:29:13 meins 2 register_task: registering filter chain (0x8074a44) -Dec 08 19:29:14 meins 3 ogg_convert: input buffer: 17167, opening ov callbacks -Dec 08 19:29:14 meins 3 ogg_convert: 2 channels, 44100 Hz -Dec 08 19:29:14 meins 2 open_writers: opening ogg writers -Dec 08 19:29:14 meins 2 open_writers: samplerate: 44100 -Dec 08 19:29:14 meins 3 wng_open: opening wng 0x80c8d20 with 1 writer(s) -Dec 08 19:29:14 meins 2 register_task: registering (0x80c8d44) -Dec 08 19:29:14 meins 2 alsa_open: 2 channel(s), 44100Hz -Dec 08 19:29:14 meins 2 alsa_open: buffer time: 170658 -Dec 08 19:29:14 meins 2 alsa_open: buffer size: 7526, period_size: 940 -Dec 08 19:29:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:30:48 meins 2 compute_time_diff: time diff (cur/avg): -3ms/+1ms -Dec 08 19:30:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:31:48 meins 2 compute_time_diff: time diff (cur/avg): -2ms/+1ms -Dec 08 19:31:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:32:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:32:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:33:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:33:56 meins 3 rn_event_handler: dccp_recv: end of file -Dec 08 19:33:56 meins 2 unregister_task: unregistering dccp receiver node (0x806a804) -Dec 08 19:33:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:33:56 meins 3 filter_event_handler: filter chain: eof -Dec 08 19:33:56 meins 2 unregister_task: unregistering filter chain (0x8074a44) -Dec 08 19:33:56 meins 2 wng_event_handler: wng: end of file -Dec 08 19:33:56 meins 2 unregister_task: unregistering writer node group (0x80c8d44) -Dec 08 19:33:56 meins 2 try_to_close_slot: closing slot 0 -Dec 08 19:33:56 meins 3 wng_close: closing wng with 1 writer(s) -Dec 08 19:33:56 meins 2 alsa_close: closing writer node 0x80c8e70 -Dec 08 19:33:56 meins 3 close_filters: closing filter chain 0x8074a18 -Dec 08 19:33:56 meins 2 close_filters: closing oggdec filter -Dec 08 19:33:56 meins 2 close_filters: closing compress filter -Dec 08 19:33:56 meins 3 close_receiver: closing ogg receiver in slot 0 (eof = 1) -Dec 08 19:33:56 meins 2 clear_slot: clearing slot 0 -Dec 08 19:33:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:33:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:33:58 meins 2 clear_slot: clearing slot 0 -Dec 08 19:33:58 meins 3 open_receiver: started ogg: dccp receiver in slot 0 -Dec 08 19:33:58 meins 2 register_task: registering dccp receiver node (0x80d0dc4) -Dec 08 19:33:58 meins 2 open_filters: opening ogg filters -Dec 08 19:33:58 meins 3 open_filters: ogg filter 1/2 (oggdec) started in slot 0 -Dec 08 19:33:58 meins 3 open_filters: ogg filter 2/2 (compress) started in slot 0 -Dec 08 19:33:58 meins 2 register_task: registering filter chain (0x807481c) -Dec 08 19:33:58 meins 2 compute_time_diff: time diff (cur/avg): -3ms/+1ms -Dec 08 19:33:58 meins 3 ogg_convert: input buffer: 16947, opening ov callbacks -Dec 08 19:33:58 meins 3 ogg_convert: 2 channels, 44100 Hz -Dec 08 19:33:58 meins 2 open_writers: opening ogg writers -Dec 08 19:33:58 meins 2 open_writers: samplerate: 44100 -Dec 08 19:33:58 meins 3 wng_open: opening wng 0x809d860 with 1 writer(s) -Dec 08 19:33:58 meins 2 register_task: registering (0x809d884) -Dec 08 19:33:58 meins 2 alsa_open: 2 channel(s), 44100Hz -Dec 08 19:33:58 meins 2 alsa_open: buffer time: 170658 -Dec 08 19:33:58 meins 2 alsa_open: buffer size: 7526, period_size: 940 -Dec 08 19:34:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:34:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:35:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:35:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:36:48 meins 2 compute_time_diff: time diff (cur/avg): -3ms/+1ms -Dec 08 19:36:54 meins 3 rn_event_handler: dccp_recv: end of file -Dec 08 19:36:54 meins 2 unregister_task: unregistering dccp receiver node (0x80d0dc4) -Dec 08 19:36:54 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:36:54 meins 3 filter_event_handler: filter chain: eof -Dec 08 19:36:54 meins 2 unregister_task: unregistering filter chain (0x807481c) -Dec 08 19:36:54 meins 2 wng_event_handler: wng: end of file -Dec 08 19:36:54 meins 2 unregister_task: unregistering writer node group (0x809d884) -Dec 08 19:36:54 meins 2 try_to_close_slot: closing slot 0 -Dec 08 19:36:54 meins 3 wng_close: closing wng with 1 writer(s) -Dec 08 19:36:54 meins 2 alsa_close: closing writer node 0x809d9b0 -Dec 08 19:36:54 meins 3 close_filters: closing filter chain 0x80747f0 -Dec 08 19:36:54 meins 2 close_filters: closing oggdec filter -Dec 08 19:36:54 meins 2 close_filters: closing compress filter -Dec 08 19:36:54 meins 3 close_receiver: closing ogg receiver in slot 0 (eof = 1) -Dec 08 19:36:54 meins 2 clear_slot: clearing slot 0 -Dec 08 19:36:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:36:56 meins 2 clear_slot: clearing slot 0 -Dec 08 19:36:56 meins 3 open_receiver: started mp3: http receiver in slot 0 -Dec 08 19:36:56 meins 2 register_task: registering http receiver node (0x80d14ec) -Dec 08 19:36:56 meins 2 http_recv_post_select: sending http request -Dec 08 19:36:56 meins 2 http_recv_post_select: received ok msg, streaming -Dec 08 19:36:56 meins 2 open_filters: opening mp3 filters -Dec 08 19:36:56 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0 -Dec 08 19:36:56 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0 -Dec 08 19:36:56 meins 2 register_task: registering filter chain (0x80d16ac) -Dec 08 19:36:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 174 ms left -Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 148 ms left -Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 122 ms left -Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 96 ms left -Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 70 ms left -Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 44 ms left -Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 18 ms left -Dec 08 19:36:56 meins 2 open_writers: opening mp3 writers -Dec 08 19:36:56 meins 2 open_writers: samplerate: 44100 -Dec 08 19:36:56 meins 3 wng_open: opening wng 0x80743a0 with 1 writer(s) -Dec 08 19:36:56 meins 2 register_task: registering (0x80743c4) -Dec 08 19:36:56 meins 2 alsa_open: 2 channel(s), 44100Hz -Dec 08 19:36:56 meins 2 alsa_open: buffer time: 170658 -Dec 08 19:36:56 meins 2 alsa_open: buffer size: 7526, period_size: 940 -Dec 08 19:36:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:37:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms -Dec 08 19:37:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:38:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:38:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms -Dec 08 19:39:06 meins 7 signal_event_handler: terminating on signal 15 -Dec 08 19:39:06 meins 7 clean_exit: caught deadly signal -Dec 08 19:39:06 meins 2 log_welcome: welcome to para_audiod git (Sat Dec 8 13:42:52 MET 2007) -Dec 08 19:39:06 meins 1 log_welcome: using loglevel 1 -Dec 08 19:39:06 meins 2 init_writers: maximal number of writers: 3 -Dec 08 19:39:06 meins 2 check_writer_arg: checking alsa -d plug:swmix -Dec 08 19:39:06 meins 2 alsa_parse_config: options: -d plug:swmix, 2 -Dec 08 19:39:06 meins 2 alsa_parse_config: help given: 0 -Dec 08 19:39:06 meins 2 init_writers: mp3 writer #0: alsa -Dec 08 19:39:06 meins 2 check_writer_arg: checking alsa -d plug:swmix -Dec 08 19:39:06 meins 2 alsa_parse_config: options: -d plug:swmix, 2 -Dec 08 19:39:06 meins 2 alsa_parse_config: help given: 0 -Dec 08 19:39:06 meins 2 init_writers: ogg writer #0: alsa -Dec 08 19:39:06 meins 2 check_writer_arg: checking alsa -d plug:swmix -Dec 08 19:39:06 meins 2 alsa_parse_config: options: -d plug:swmix, 2 -Dec 08 19:39:06 meins 2 alsa_parse_config: help given: 0 -Dec 08 19:39:06 meins 2 init_writers: aac writer #0: alsa -Dec 08 19:39:06 meins 2 init_receivers: initializing http receiver -Dec 08 19:39:06 meins 2 init_receivers: initializing dccp receiver -Dec 08 19:39:06 meins 2 init_receivers: initializing ortp receiver -Dec 08 19:39:06 meins 1 check_receiver_arg: checking http -i 127.0.0.1 -Dec 08 19:39:06 meins 1 parse_receiver_args: options: -i 127.0.0.1 -Dec 08 19:39:06 meins 1 parse_receiver_args: argc = 3, argv[0]: http -Dec 08 19:39:06 meins 1 check_receiver_arg: checking dccp -i localhost -Dec 08 19:39:06 meins 1 parse_receiver_args: options: -i localhost -Dec 08 19:39:06 meins 1 parse_receiver_args: argc = 3, argv[0]: dccp -Dec 08 19:39:06 meins 1 check_receiver_arg: checking dccp -i localhost -Dec 08 19:39:06 meins 1 parse_receiver_args: options: -i localhost -Dec 08 19:39:06 meins 1 parse_receiver_args: argc = 3, argv[0]: dccp -Dec 08 19:39:06 meins 2 init_filters: maximal number of filters: 6 -Dec 08 19:39:06 meins 2 add_filter: mp3 filter 1: mp3dec -Dec 08 19:39:06 meins 2 add_filter: mp3 filter 2: compress -Dec 08 19:39:06 meins 2 add_filter: ogg filter 1: oggdec -Dec 08 19:39:06 meins 2 add_filter: ogg filter 2: compress -Dec 08 19:39:06 meins 2 add_filter: aac filter 1: aacdec -Dec 08 19:39:06 meins 2 add_filter: aac filter 2: compress -Dec 08 19:39:06 meins 2 clear_slot: clearing slot 0 -Dec 08 19:39:06 meins 2 clear_slot: clearing slot 1 -Dec 08 19:39:06 meins 2 clear_slot: clearing slot 2 -Dec 08 19:39:06 meins 2 clear_slot: clearing slot 3 -Dec 08 19:39:06 meins 2 clear_slot: clearing slot 4 -Dec 08 19:39:06 meins 2 init_grabbing: grab init -Dec 08 19:39:06 meins 2 setup_signal_handling: signal pipe: fd 4 -Dec 08 19:39:06 meins 1 para_install_sighandler: catching signal 2 -Dec 08 19:39:06 meins 1 para_install_sighandler: catching signal 15 -Dec 08 19:39:06 meins 1 para_install_sighandler: catching signal 1 -Dec 08 19:39:06 meins 3 audiod_get_socket: local socket: /var/paraslash/audiod_socket.meins -Dec 08 19:39:06 meins 2 daemon_init: daemonizing -Dec 08 19:39:06 meins 2 init_sched: initializing scheduler -Dec 08 19:39:06 meins 2 register_task: registering signal task (0x80631bc) -Dec 08 19:39:06 meins 1 register_task: pre_select: 0x80631c0 -Dec 08 19:39:06 meins 1 register_task: post_select: 0x80631c0 -Dec 08 19:39:06 meins 2 register_task: registering command task (0xbfa7e274) -Dec 08 19:39:06 meins 1 register_task: pre_select: 0xbfa7e278 -Dec 08 19:39:06 meins 1 register_task: post_select: 0xbfa7e278 -Dec 08 19:39:06 meins 2 register_task: registering status task (0x80632e8) -Dec 08 19:39:06 meins 1 register_task: pre_select: 0x80632ec -Dec 08 19:39:06 meins 1 register_task: post_select: 0x80632ec -Dec 08 19:39:06 meins 2 register_task: registering audiod task (0xbfa7e398) -Dec 08 19:39:06 meins 1 register_task: pre_select: 0xbfa7e39c -Dec 08 19:39:06 meins 1 register_task: post_select: 0xbfa7e39c -Dec 08 19:39:06 meins 2 status_pre_select: clock diff count: 5 -Dec 08 19:39:06 meins 2 client_open: loglevel: 5 -Dec 08 19:39:06 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf -Dec 08 19:39:06 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan -Dec 08 19:39:06 meins 3 client_open: connecting localhost:2990 -Dec 08 19:39:06 meins 2 register_task: registering client (0x8067878) -Dec 08 19:39:06 meins 1 register_task: pre_select: 0x806787c -Dec 08 19:39:06 meins 1 register_task: post_select: 0x806787c -Dec 08 19:39:06 meins 2 client_post_select: --> auth rc4 maan -Dec 08 19:39:06 meins 2 client_post_select: <-- [challenge] -Dec 08 19:39:06 meins 2 client_post_select: --> 153889019 -Dec 08 19:39:06 meins 1 client_post_select: ++++ server info ++++ - -Proceed. - -++++ end of server info ++++ -Dec 08 19:39:06 meins 2 client_post_select: decrypting session key -Dec 08 19:39:06 meins 2 enable_crypt: rc4 encryption activated for fd 7 -Dec 08 19:39:06 meins 1 client_post_select: --> -stat -1 -End of Command. diff --git a/pics/screenshots/gui.png b/pics/screenshots/gui.png deleted file mode 100644 index 2a6c7443..00000000 Binary files a/pics/screenshots/gui.png and /dev/null differ diff --git a/pics/screenshots/server.log b/pics/screenshots/server.log deleted file mode 100644 index f84ede81..00000000 --- a/pics/screenshots/server.log +++ /dev/null @@ -1,192 +0,0 @@ -Dec 08 19:28:58 2: (21285) log_welcome: welcome to para_server git (Sat Dec 8 13:42:52 MET 2007) -Dec 08 19:28:58 1: (21285) log_welcome: using loglevel 1 -Dec 08 19:28:58 1: (21285) populate_user_list: found entry for maan -Dec 08 19:28:58 1: (21285) populate_user_list: found 4 perm entries -Dec 08 19:28:58 1: (21285) populate_user_list: found entry for install -Dec 08 19:28:58 1: (21285) populate_user_list: found 4 perm entries -Dec 08 19:28:58 1: (21285) populate_user_list: found entry for www -Dec 08 19:28:58 1: (21285) populate_user_list: found 4 perm entries -Dec 08 19:28:58 2: (21285) daemon_init: daemonizing -Dec 08 19:28:58 3: (21286) server_init: initializing audio format handlers -Dec 08 19:28:58 2: (21286) afh_init: supported audio formats: mp3 ogg aac -Dec 08 19:28:58 3: (21286) afh_init: initializing mp3 handler -Dec 08 19:28:58 3: (21286) afh_init: initializing ogg handler -Dec 08 19:28:58 3: (21286) afh_init: initializing aac handler -Dec 08 19:28:58 3: (21286) server_init: initializing virtual streaming system -Dec 08 19:28:58 2: (21286) vss_init: announce timeval: 300ms -Dec 08 19:28:58 3: (21286) vss_init: initializing http sender -Dec 08 19:28:58 2: (21286) para_listen: listening on TCP port 8000, fd 4 -Dec 08 19:28:58 1: (21286) http_send_init: http sender init complete -Dec 08 19:28:58 3: (21286) vss_init: initializing dccp sender -Dec 08 19:28:58 2: (21286) para_listen: listening on DCCP port 5001, fd 5 -Dec 08 19:28:58 3: (21286) vss_init: initializing ortp sender -Dec 08 19:28:58 1: (21286) ortp_send_init: ortp sender init complete -Dec 08 19:28:58 3: (21286) setup_signal_handling: setting up signal handlers -Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 2 -Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 15 -Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 1 -Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 17 -Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 10 -Dec 08 19:28:58 3: (21286) server_init: initializing the audio file selector -Dec 08 19:28:58 2: (21287) get_database_dir: afs_database dir /home/maan/.paraslash/afs_database -Dec 08 19:28:58 3: (21287) open_afs_tables: opening 7 osl tables in /home/maan/.paraslash/afs_database -Dec 08 19:28:58 2: (21287) osl_open_table: opening table audio_files -Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'audio_files' from table description -Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 32 -Dec 08 19:28:58 1: (21287) map_table: mapping table 'audio_files' (index: /home/maan/.paraslash/afs_database/audio_files/index) -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/index: size 218667 -Dec 08 19:28:58 1: (21287) read_table_desc: 5 columns -Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'audio_files' matches on-disk data, good -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/2346ad27d7568ba9896f1b7da6b5991251debdf2: size 143430 -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/3150ecd5e0294534a81ae047ddac559de481d774: size 436636 -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/94ea39e309f3f31357ab60b190b6b8c32f21620b: size 225390 -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/b063ad096f9d142a388ca2a10d46b56904e26cda: size 1174409 -Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 6830 -Dec 08 19:28:58 2: (21286) init_afs: afs_socket: 8, afs_socket_cookie: 1363241225 -Dec 08 19:28:58 3: (21286) server_init: initializing tcp command socket -Dec 08 19:28:58 2: (21286) para_listen: listening on TCP port 2990, fd 9 -Dec 08 19:28:58 3: (21286) server_init: server init complete -Dec 08 19:28:58 1: (21286) chk_barrier: autoplay_delay barrier: 14981ms left -Dec 08 19:28:58 1: (21286) status_refresh: 0 events, forcing status update -Dec 08 19:28:58 1: (21286) para_next_signal: next signal: 10 -Dec 08 19:28:58 1: (21286) chk_barrier: autoplay_delay barrier: 14981ms left -Dec 08 19:28:58 1: (21286) para_next_signal: next signal: 10 -Dec 08 19:28:58 1: (21286) chk_barrier: autoplay_delay barrier: 14981ms left -Dec 08 19:28:58 2: (21287) aft_open: audio file table contains 6830 files -Dec 08 19:28:58 2: (21287) osl_open_table: opening table attributes -Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'attributes' from table description -Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16 -Dec 08 19:28:58 1: (21287) map_table: mapping table 'attributes' (index: /home/maan/.paraslash/afs_database/attributes/index) -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/attributes/index: size 396 -Dec 08 19:28:58 1: (21287) read_table_desc: 2 columns -Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'attributes' matches on-disk data, good -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/attributes/fd1e48caeff7212c45fc08608b7187feb10a7a2d: size 42 -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/attributes/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 141 -Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 21 -Dec 08 19:28:58 2: (21287) osl_open_table: opening table score -Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'score' from table description -Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 0 -Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 0 -Dec 08 19:28:58 2: (21287) osl_open_table: opening table moods -Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'moods' from table description -Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16 -Dec 08 19:28:58 1: (21287) map_table: mapping table 'moods' (index: /home/maan/.paraslash/afs_database/moods/index) -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/index: size 235 -Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns -Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'moods' matches on-disk data, good -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/87ea5dfc8b8e384d848979496e706390b497e547: size 50 -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 67 -Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 10 -Dec 08 19:28:58 2: (21287) osl_open_table: opening table lyrics -Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'lyrics' from table description -Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16 -Dec 08 19:28:58 1: (21287) map_table: mapping table 'lyrics' (index: /home/maan/.paraslash/afs_database/lyrics/index) -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/lyrics/index: size 347 -Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns -Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'lyrics' matches on-disk data, good -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/lyrics/87ea5dfc8b8e384d848979496e706390b497e547: size 85 -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/lyrics/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 478 -Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 17 -Dec 08 19:28:58 2: (21287) osl_open_table: opening table images -Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'images' from table description -Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16 -Dec 08 19:28:58 1: (21287) map_table: mapping table 'images' (index: /home/maan/.paraslash/afs_database/images/index) -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/images/index: size 7227 -Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns -Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'images' matches on-disk data, good -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/images/87ea5dfc8b8e384d848979496e706390b497e547: size 2235 -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/images/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 12950 -Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 447 -Dec 08 19:28:58 2: (21287) osl_open_table: opening table playlists -Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'playlists' from table description -Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16 -Dec 08 19:28:58 1: (21287) map_table: mapping table 'playlists' (index: /home/maan/.paraslash/afs_database/playlists/index) -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/playlists/index: size 107 -Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns -Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'playlists' matches on-disk data, good -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/playlists/87ea5dfc8b8e384d848979496e706390b497e547: size 10 -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/playlists/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 7 -Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 2 -Dec 08 19:28:58 2: (21287) afs_init: server_socket: 9, afs_socket_cookie: 1363241225 -Dec 08 19:28:58 1: (21287) osl_open_disk_object: filename: /home/maan/.paraslash/afs_database/moods/f3f1dd33eb2a8b380b64a830e5fd90eab77d9ff3/9d/b063f3b5e0adfd0d29a03db0a1c207b3740a94 -Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/f3f1dd33eb2a8b380b64a830e5fd90eab77d9ff3/9d/b063f3b5e0adfd0d29a03db0a1c207b3740a94: size 31 -Dec 08 19:28:58 1: (21287) parse_mood_line: accept entry added, method: 0x806c0f4 -Dec 08 19:28:58 3: (21287) change_current_mood: computing statistics of admissible files -Dec 08 19:28:58 2: (21287) log_statistics: last_played mean: 1193159581, last_played sigma: 4161524 -Dec 08 19:28:58 2: (21287) log_statistics: num_played mean: 32, num_played sigma: 21 -Dec 08 19:28:58 2: (21287) change_current_mood: 26 admissible files -Dec 08 19:28:58 3: (21287) change_current_mood: loaded mood gulp -Dec 08 19:28:58 2: (21287) register_signal_task: signal pipe: fd 8 -Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 2 -Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 15 -Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 13 -Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 1 -Dec 08 19:28:58 2: (21287) init_sched: initializing scheduler -Dec 08 19:28:58 2: (21287) register_task: registering signal task (0x806f244) -Dec 08 19:28:58 1: (21287) register_task: pre_select: 0x806f248 -Dec 08 19:28:58 1: (21287) register_task: post_select: 0x806f248 -Dec 08 19:28:58 2: (21287) setup_command_socket_or_die: listening on socket /var/paraslash/afs_command_socket (fd 1) -Dec 08 19:28:58 2: (21287) register_task: registering command task (0x806f118) -Dec 08 19:28:58 1: (21287) register_task: pre_select: 0x806f11c -Dec 08 19:28:58 1: (21287) register_task: post_select: 0x806f11c -Dec 08 19:28:59 2: (21286) main: got connection from ::ffff:127.0.0.1#42344, forking -Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 9 -Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 8 -Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 6 -Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 5 -Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 4 -Dec 08 19:28:59 1: (21286) chk_barrier: autoplay_delay barrier: 13507ms left -Dec 08 19:28:59 1: (21356) handle_connect: received rc4 request for user maan -Dec 08 19:28:59 1: (21356) handle_connect: sending 64 byte challenge -Dec 08 19:28:59 2: (21356) handle_connect: good auth for maan (1081806111) -Dec 08 19:28:59 1: (21356) init_rc4_keys: rc4 keys initialized (84:105) -Dec 08 19:28:59 2: (21356) enable_crypt: rc4 encryption activated for fd 10 -Dec 08 19:28:59 3: (21356) handle_connect: invalid command -Dec 08 19:28:59 1: (21286) para_next_signal: next signal: 17 -Dec 08 19:28:59 1: (21286) para_reap_child: child 21356 exited. Exit status: 1 -Dec 08 19:28:59 1: (21286) chk_barrier: autoplay_delay barrier: 13498ms left -Dec 08 19:29:03 2: (21286) main: got connection from ::ffff:127.0.0.1#42345, forking -Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 9 -Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 8 -Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 6 -Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 5 -Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 4 -Dec 08 19:29:03 1: (21286) chk_barrier: autoplay_delay barrier: 9951ms left -Dec 08 19:29:03 1: (21362) handle_connect: received rc4 request for user maan -Dec 08 19:29:03 1: (21362) handle_connect: sending 64 byte challenge -Dec 08 19:29:03 2: (21362) handle_connect: good auth for maan (705434988) -Dec 08 19:29:03 1: (21362) init_rc4_keys: rc4 keys initialized (18:65) -Dec 08 19:29:03 2: (21362) enable_crypt: rc4 encryption activated for fd 10 -Dec 08 19:29:03 1: (21362) check_perms: checking permissions -Dec 08 19:29:03 3: (21362) handle_connect: calling com_stat() for maan@::ffff:127.0.0.1#42345 -Dec 08 19:29:13 1: (21286) chk_barrier: autoplay_delay barrier: 3ms left -Dec 08 19:29:13 1: (21286) vss_preselect: ready and playing, but no audio file -Dec 08 19:29:13 3: (21286) vss_post_select: requesting new fd from afs -Dec 08 19:29:13 1: (21287) execute_server_command: received: new -Dec 08 19:29:13 3: (21287) open_next_audio_file: getting next audio file -Dec 08 19:29:13 1: (21287) osl_open_disk_object: filename: /home/maan/.paraslash/afs_database/audio_files/7e078c9876ccabef154017c770e05195c85b5e4d/55/b5e38d467105bd88133cf5ded70e551e582593 -Dec 08 19:29:13 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/7e078c9876ccabef154017c770e05195c85b5e4d/55/b5e38d467105bd88133cf5ded70e551e582593: size 4532 -Dec 08 19:29:13 1: (21287) mmap_full_file: /home/mp3/checked/dvd_07/cd_46/The_G.U.L.P.__Scheiss_Krieg.ogg: size 5274482 -Dec 08 19:29:13 1: (21287) mood_update_audio_file: score: 6 -Dec 08 19:29:13 1: (21287) mood_update_audio_file: moving from rank 26 to 35% -Dec 08 19:29:13 1: (21287) score_update: new score: 0, rank 9/26 -Dec 08 19:29:13 1: (21287) osl_update_object: updating column 1 of score -Dec 08 19:29:13 1: (21287) score_update: new score: -65, rank 1/26 -Dec 08 19:29:13 1: (21287) osl_update_object: updating column 1 of score -Dec 08 19:29:13 1: (21287) save_afd: size: 8932 -Dec 08 19:29:13 1: (21287) pass_afd: passing 8 bytes and fd 12 -Dec 08 19:29:13 1: (21286) recv_afs_result: fd: 10, code: 0, shmid: 28442631 -Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left -Dec 08 19:29:13 1: (21286) status_refresh: 1 events, forcing status update -Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left -Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left -Dec 08 19:29:13 1: (21286) para_next_signal: next signal: 10 -Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left -Dec 08 19:29:13 3: (21286) dccp_post_select: connection from ::ffff:127.0.0.1#46539 -Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 287ms left -Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 287ms left -Dec 08 19:29:13 1: (21287) para_next_signal: next signal: 10 -Dec 08 19:29:13 1: (21286) status_refresh: 2 events, forcing status update -Dec 08 19:29:13 1: (21286) para_next_signal: next signal: 10 -Dec 08 19:29:13 1: (21287) para_next_signal: next signal: 10 -Dec 08 19:29:14 1: (21286) cq_enqueue: 4446 bytes queued for 0x8072eb0 diff --git a/pics/web/paraslash.ico b/pics/web/paraslash.ico deleted file mode 100644 index 85ce9be1..00000000 Binary files a/pics/web/paraslash.ico and /dev/null differ diff --git a/pics/web/paraslash.png b/pics/web/paraslash.png deleted file mode 100644 index 744ce0ac..00000000 Binary files a/pics/web/paraslash.png and /dev/null differ diff --git a/playlist.c b/playlist.c index a9925cd3..b04598b8 100644 --- a/playlist.c +++ b/playlist.c @@ -1,9 +1,12 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ +#include +#include + #include "para.h" #include "error.h" #include "string.h" @@ -174,7 +177,7 @@ int playlist_open(char *name) obj.data = name; obj.size = strlen(obj.data); - ret = osl_get_row(playlists_table, BLOBCOL_NAME, &obj, &row); + ret = osl(osl_get_row(playlists_table, BLOBCOL_NAME, &obj, &row)); if (ret < 0) { PARA_NOTICE_LOG("failed to load playlist %s\n", name); return ret; diff --git a/portable_io.h b/portable_io.h index c2ed8afc..2353c610 100644 --- a/portable_io.h +++ b/portable_io.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/prebuffer_filter.c b/prebuffer_filter.c index 1ad5f446..548a8177 100644 --- a/prebuffer_filter.c +++ b/prebuffer_filter.c @@ -1,16 +1,20 @@ /* - * Copyright (C) 2009 Andre Noll + * Copyright (C) 2009-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file prebuffer_filter.c Paraslash's prebuffering filter. */ +#include +#include + #include "para.h" #include "prebuffer_filter.cmdline.h" #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "string.h" #include "error.h" @@ -25,56 +29,52 @@ struct private_prebuffer_data { struct timeval barrier; }; -static ssize_t prebuffer_convert(char *inbuf, size_t inbuf_len, - struct filter_node *fn) +static void prebuffer_pre_select(struct sched *s, struct task *t) { + struct filter_node *fn = container_of(t, struct filter_node, task); + struct btr_node *btrn = fn->btrn; + size_t iqs = btr_get_input_queue_size(btrn); struct private_prebuffer_data *ppd = fn->private_data; struct prebuffer_filter_args_info *conf = ppd->conf; + struct timeval diff; - if (inbuf_len == 0) { - if (*fn->fc->input_error < 0 && ppd->prebuffered >= 0) - goto prebuffer_end; - return 0; - } - if (ppd->prebuffered < 0) { - size_t copy = PARA_MIN(inbuf_len, fn->bufsize - fn->loaded); - memcpy(fn->buf + fn->loaded, inbuf, copy); - fn->loaded += copy; - return copy; - } - if (ppd->prebuffered + inbuf_len > fn->bufsize) { - fn->bufsize = PARA_MAX(2 * fn->bufsize, - ppd->prebuffered + inbuf_len); - fn->buf = para_realloc(fn->buf, fn->bufsize); - } - memcpy(fn->buf + ppd->prebuffered, inbuf, inbuf_len); - if (ppd->prebuffered == 0) { + t->error = 0; + if (iqs == 0) + return; + if (ppd->barrier.tv_sec == 0) { struct timeval tv; PARA_INFO_LOG("prebuffer period %dms\n", conf->duration_arg); ms2tv(conf->duration_arg, &tv); tv_add(&tv, now, &ppd->barrier); } - ppd->prebuffered += inbuf_len; - PARA_DEBUG_LOG("%d bytes prebuffered\n", ppd->prebuffered); - if (*fn->fc->input_error >= 0) { - struct timeval diff; - if (tv_diff(now, &ppd->barrier, &diff) < 0) - goto out; - if (ppd->prebuffered < conf->size_arg) - goto out; - } -prebuffer_end: - fn->loaded = ppd->prebuffered; - ppd->prebuffered = -1; -out: - return inbuf_len; + if (tv_diff(&ppd->barrier, now, &diff) < 0) + return sched_min_delay(s); + sched_request_timeout(&diff, s); } static void prebuffer_close(struct filter_node *fn) { free(fn->private_data); - free(fn->buf); +} + +static void prebuffer_post_select(__a_unused struct sched *s, struct task *t) +{ + struct filter_node *fn = container_of(t, struct filter_node, task); + struct btr_node *btrn = fn->btrn; + size_t iqs = btr_get_input_queue_size(btrn); + struct private_prebuffer_data *ppd = fn->private_data; + struct prebuffer_filter_args_info *conf = ppd->conf; + + t->error = 0; + if (ppd->barrier.tv_sec == 0) + return; + if (tv_diff(now, &ppd->barrier, NULL) < 0) + return; + if (iqs < conf->size_arg) + return; + btr_splice_out_node(btrn); + t->error = -E_PREBUFFER_SUCCESS; } static int prebuffer_parse_config(int argc, char **argv, void **config) @@ -105,8 +105,11 @@ static void prebuffer_open(struct filter_node *fn) ppd->conf = fn->conf; fn->private_data = ppd; - fn->bufsize = 8192; /* gets increased on demand */ - fn->buf = para_malloc(fn->bufsize); +} + +static void prebuffer_free_config(void *conf) +{ + prebuffer_cmdline_parser_free(conf); } /** @@ -121,8 +124,10 @@ void prebuffer_filter_init(struct filter *f) prebuffer_cmdline_parser_init(&dummy); f->open = prebuffer_open; f->close = prebuffer_close; - f->convert = prebuffer_convert; f->parse_config = prebuffer_parse_config; + f->free_config = prebuffer_free_config; + f->pre_select = prebuffer_pre_select; + f->post_select = prebuffer_post_select; f->help = (struct ggo_help) { .short_help = prebuffer_filter_args_info_help, .detailed_help = prebuffer_filter_args_info_detailed_help diff --git a/rbtree.c b/rbtree.c deleted file mode 100644 index 7f200d1d..00000000 --- a/rbtree.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - Red Black Trees - (C) 1999 Andrea Arcangeli - (C) 2002 David Woodhouse - (C) 2007 Andre Noll - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - linux/lib/rbtree.c -*/ - -/** \file rbtree.c Red-black tree implementation. */ - -#include "stddef.h" -#include "rbtree.h" - -static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *right = node->rb_right; - struct rb_node *parent = rb_parent(node); - - if ((node->rb_right = right->rb_left)) - rb_set_parent(right->rb_left, node); - right->rb_left = node; - - rb_set_parent(right, parent); - - if (parent) - { - if (node == parent->rb_left) - parent->rb_left = right; - else - parent->rb_right = right; - } - else - root->rb_node = right; - rb_set_parent(node, right); - right->size = node->size; - node->size = 1; - if (node->rb_right) - node->size += node->rb_right->size; - if (node->rb_left) - node->size += node->rb_left->size; -} - -static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *left = node->rb_left; - struct rb_node *parent = rb_parent(node); - - if ((node->rb_left = left->rb_right)) - rb_set_parent(left->rb_right, node); - left->rb_right = node; - - rb_set_parent(left, parent); - - if (parent) - { - if (node == parent->rb_right) - parent->rb_right = left; - else - parent->rb_left = left; - } - else - root->rb_node = left; - rb_set_parent(node, left); - left->size = node->size; - node->size = 1; - if (node->rb_right) - node->size += node->rb_right->size; - if (node->rb_left) - node->size += node->rb_left->size; -} - -void rb_insert_color(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *parent, *gparent; - - while ((parent = rb_parent(node)) && rb_is_red(parent)) - { - gparent = rb_parent(parent); - - if (parent == gparent->rb_left) - { - { - register struct rb_node *uncle = gparent->rb_right; - if (uncle && rb_is_red(uncle)) - { - rb_set_black(uncle); - rb_set_black(parent); - rb_set_red(gparent); - node = gparent; - continue; - } - } - - if (parent->rb_right == node) - { - register struct rb_node *tmp; - __rb_rotate_left(parent, root); - tmp = parent; - parent = node; - node = tmp; - } - - rb_set_black(parent); - rb_set_red(gparent); - __rb_rotate_right(gparent, root); - } else { - { - register struct rb_node *uncle = gparent->rb_left; - if (uncle && rb_is_red(uncle)) - { - rb_set_black(uncle); - rb_set_black(parent); - rb_set_red(gparent); - node = gparent; - continue; - } - } - - if (parent->rb_left == node) - { - register struct rb_node *tmp; - __rb_rotate_right(parent, root); - tmp = parent; - parent = node; - node = tmp; - } - - rb_set_black(parent); - rb_set_red(gparent); - __rb_rotate_left(gparent, root); - } - } - - rb_set_black(root->rb_node); -} - -static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, - struct rb_root *root) -{ - struct rb_node *other; - - while ((!node || rb_is_black(node)) && node != root->rb_node) - { - if (parent->rb_left == node) - { - other = parent->rb_right; - if (rb_is_red(other)) - { - rb_set_black(other); - rb_set_red(parent); - __rb_rotate_left(parent, root); - other = parent->rb_right; - } - if ((!other->rb_left || rb_is_black(other->rb_left)) && - (!other->rb_right || rb_is_black(other->rb_right))) - { - rb_set_red(other); - node = parent; - parent = rb_parent(node); - } - else - { - if (!other->rb_right || rb_is_black(other->rb_right)) - { - struct rb_node *o_left; - if ((o_left = other->rb_left)) - rb_set_black(o_left); - rb_set_red(other); - __rb_rotate_right(other, root); - other = parent->rb_right; - } - rb_set_color(other, rb_color(parent)); - rb_set_black(parent); - if (other->rb_right) - rb_set_black(other->rb_right); - __rb_rotate_left(parent, root); - node = root->rb_node; - break; - } - } - else - { - other = parent->rb_left; - if (rb_is_red(other)) - { - rb_set_black(other); - rb_set_red(parent); - __rb_rotate_right(parent, root); - other = parent->rb_left; - } - if ((!other->rb_left || rb_is_black(other->rb_left)) && - (!other->rb_right || rb_is_black(other->rb_right))) - { - rb_set_red(other); - node = parent; - parent = rb_parent(node); - } - else - { - if (!other->rb_left || rb_is_black(other->rb_left)) - { - register struct rb_node *o_right; - if ((o_right = other->rb_right)) - rb_set_black(o_right); - rb_set_red(other); - __rb_rotate_left(other, root); - other = parent->rb_left; - } - rb_set_color(other, rb_color(parent)); - rb_set_black(parent); - if (other->rb_left) - rb_set_black(other->rb_left); - __rb_rotate_right(parent, root); - node = root->rb_node; - break; - } - } - } - if (node) - rb_set_black(node); -} - -void rb_erase(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *child, *parent; - int color; - - if (!node->rb_left) - child = node->rb_right; - else if (!node->rb_right) - child = node->rb_left; - else - { - struct rb_node *old = node, *left; - - node = node->rb_right; - while ((left = node->rb_left) != NULL) - node = left; - child = node->rb_right; - parent = rb_parent(node); - color = rb_color(node); - - if (child) - rb_set_parent(child, parent); - if (parent == old) { - parent->rb_right = child; - parent = node; - } else - parent->rb_left = child; - - node->rb_parent_color = old->rb_parent_color; - node->rb_right = old->rb_right; - node->rb_left = old->rb_left; - node->size = old->size; - - if (rb_parent(old)) - { - if (rb_parent(old)->rb_left == old) - rb_parent(old)->rb_left = node; - else - rb_parent(old)->rb_right = node; - } else - root->rb_node = node; - - rb_set_parent(old->rb_left, node); - if (old->rb_right) - rb_set_parent(old->rb_right, node); - goto color; - } - - parent = rb_parent(node); - color = rb_color(node); - - if (child) - rb_set_parent(child, parent); - if (parent) - { - if (parent->rb_left == node) - parent->rb_left = child; - else - parent->rb_right = child; - } - else - root->rb_node = child; - - color: - if (color == RB_BLACK) - __rb_erase_color(child, parent, root); -} - -/* - * This function returns the first node (in sort order) of the tree. - */ -struct rb_node *rb_first(const struct rb_root *root) -{ - struct rb_node *n; - - n = root->rb_node; - if (!n) - return NULL; - while (n->rb_left) - n = n->rb_left; - return n; -} - -struct rb_node *rb_last(const struct rb_root *root) -{ - struct rb_node *n; - - n = root->rb_node; - if (!n) - return NULL; - while (n->rb_right) - n = n->rb_right; - return n; -} - -struct rb_node *rb_next(const struct rb_node *node) -{ - struct rb_node *parent; - - if (rb_parent(node) == node) - return NULL; - - /* If we have a right-hand child, go down and then left as far - as we can. */ - if (node->rb_right) { - node = node->rb_right; - while (node->rb_left) - node=node->rb_left; - return (struct rb_node *)node; - } - - /* No right-hand children. Everything down and left is - smaller than us, so any 'next' node must be in the general - direction of our parent. Go up the tree; any time the - ancestor is a right-hand child of its parent, keep going - up. First time it's a left-hand child of its parent, said - parent is our 'next' node. */ - while ((parent = rb_parent(node)) && node == parent->rb_right) - node = parent; - - return parent; -} - -struct rb_node *rb_prev(const struct rb_node *node) -{ - struct rb_node *parent; - - if (rb_parent(node) == node) - return NULL; - - /* If we have a left-hand child, go down and then right as far - as we can. */ - if (node->rb_left) { - node = node->rb_left; - while (node->rb_right) - node=node->rb_right; - return (struct rb_node *)node; - } - - /* No left-hand children. Go up till we find an ancestor which - is a right-hand child of its parent */ - while ((parent = rb_parent(node)) && node == parent->rb_left) - node = parent; - - return parent; -} - -void rb_replace_node(struct rb_node *victim, struct rb_node *new, - struct rb_root *root) -{ - struct rb_node *parent = rb_parent(victim); - - /* Set the surrounding nodes to point to the replacement */ - if (parent) { - if (victim == parent->rb_left) - parent->rb_left = new; - else - parent->rb_right = new; - } else { - root->rb_node = new; - } - if (victim->rb_left) - rb_set_parent(victim->rb_left, new); - if (victim->rb_right) - rb_set_parent(victim->rb_right, new); - - /* Copy the pointers/colour from the victim to the replacement */ - *new = *victim; -} - -/** - * Get the n-th node (in sort order) of the tree. - * - * \param node The root of the subtree to consider. - * \param n The order statistic to compute. - * - * \return Pointer to the \a n th greatest node on success, \p NULL on errors. - */ -struct rb_node *rb_nth(const struct rb_node *node, unsigned n) -{ - unsigned size = 1; - - if (!node) - return NULL; - if (node->rb_left) - size += node->rb_left->size; - if (n == size) - return (struct rb_node *)node; - if (n < size) - return rb_nth(node->rb_left, n); - return rb_nth(node->rb_right, n - size); -} - -/** - * Get the rank of a node in O(log n) time. - * - * \param node The node to get the rank of. - * \param rank Result pointer. - * - * \return Positive on success, -1 on errors. - */ -int rb_rank(const struct rb_node *node, unsigned *rank) -{ - struct rb_node *parent; - - *rank = 1; - if (!node) - return -1; - if (node->rb_left) - *rank += node->rb_left->size; - while ((parent = rb_parent(node))) { - if (node == parent->rb_right) { - (*rank)++; - if (parent->rb_left) - *rank += parent->rb_left->size; - } - node = parent; - } - return 1; -} diff --git a/rbtree.h b/rbtree.h deleted file mode 100644 index 8295d2ad..00000000 --- a/rbtree.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - Red Black Trees - (C) 1999 Andrea Arcangeli - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - linux/include/linux/rbtree.h - - To use rbtrees you'll have to implement your own insert and search cores. - This will avoid us to use callbacks and to drop drammatically performances. - I know it's not the cleaner way, but in C (not in C++) to get - performances and genericity... - - Some example of insert and search follows here. The search is a plain - normal search over an ordered tree. The insert instead must be implemented - int two steps: as first thing the code must insert the element in - order as a red leaf in the tree, then the support library function - rb_insert_color() must be called. Such function will do the - not trivial work to rebalance the rbtree if necessary. - ------------------------------------------------------------------------ -static inline struct page * rb_search_page_cache(struct inode * inode, - unsigned long offset) -{ - struct rb_node * n = inode->i_rb_page_cache.rb_node; - struct page * page; - - while (n) - { - page = rb_entry(n, struct page, rb_page_cache); - - if (offset < page->offset) - n = n->rb_left; - else if (offset > page->offset) - n = n->rb_right; - else - return page; - } - return NULL; -} - -static inline struct page * __rb_insert_page_cache(struct inode * inode, - unsigned long offset, - struct rb_node * node) -{ - struct rb_node ** p = &inode->i_rb_page_cache.rb_node; - struct rb_node * parent = NULL; - struct page * page; - - while (*p) - { - parent = *p; - page = rb_entry(parent, struct page, rb_page_cache); - - if (offset < page->offset) - p = &(*p)->rb_left; - else if (offset > page->offset) - p = &(*p)->rb_right; - else - return page; - } - - rb_link_node(node, parent, p); - - return NULL; -} - -static inline struct page * rb_insert_page_cache(struct inode * inode, - unsigned long offset, - struct rb_node * node) -{ - struct page * ret; - if ((ret = __rb_insert_page_cache(inode, offset, node))) - goto out; - rb_insert_color(node, &inode->i_rb_page_cache); - out: - return ret; -} ------------------------------------------------------------------------ -*/ - -/** \file rbtree.h Exported symbols from rbtree.h */ - -#ifndef _LINUX_RBTREE_H -#define _LINUX_RBTREE_H - - -/** get the struct this entry is embedded in */ -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) - - -struct rb_node -{ - unsigned long rb_parent_color; -#define RB_RED 0 -#define RB_BLACK 1 - struct rb_node *rb_right; - struct rb_node *rb_left; - unsigned size; -} __attribute__((aligned(sizeof(long)))); - /* The alignment might seem pointless, but allegedly CRIS needs it */ - -struct rb_root -{ - struct rb_node *rb_node; -}; - - -#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) -#define rb_color(r) ((r)->rb_parent_color & 1) -#define rb_is_red(r) (!rb_color(r)) -#define rb_is_black(r) rb_color(r) -#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) -#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) - -static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) -{ - rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; -} -static inline void rb_set_color(struct rb_node *rb, int color) -{ - rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; -} - -#define RB_ROOT (struct rb_root) { NULL, } -#define rb_entry(ptr, type, member) container_of(ptr, type, member) - -#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) -#define RB_EMPTY_NODE(node) (rb_parent(node) == node) -#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) - -extern void rb_insert_color(struct rb_node *, struct rb_root *); -extern void rb_erase(struct rb_node *, struct rb_root *); - -/* Find logical next and previous nodes in a tree */ -extern struct rb_node *rb_next(const struct rb_node *); -extern struct rb_node *rb_prev(const struct rb_node *); -extern struct rb_node *rb_first(const struct rb_root *); -extern struct rb_node *rb_last(const struct rb_root *); - -extern struct rb_node *rb_nth(const struct rb_node *node, unsigned n); -extern int rb_rank(const struct rb_node *node, unsigned *rank); - -/* Fast replacement of a single node without remove/rebalance/add/rebalance */ -extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, - struct rb_root *root); - -static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, - struct rb_node ** rb_link) -{ - node->size = 1; - node->rb_parent_color = (unsigned long )parent; - node->rb_left = node->rb_right = NULL; - - *rb_link = node; - /* Fixup the size fields in the tree */ - while ((node = rb_parent(node))) - node->size++; -} - -#endif /* _LINUX_RBTREE_H */ diff --git a/rc4.h b/rc4.h index 9ddd8224..1815e3b8 100644 --- a/rc4.h +++ b/rc4.h @@ -1,4 +1,4 @@ /** \file rc4.h common symbols of command.c and client_common.c */ -/** number of bytes of the rc4 session key */ -#define RC4_KEY_LEN 16 +/** Number of bytes of the rc4 session key. */ +#define RC4_KEY_LEN 32 diff --git a/recv.c b/recv.c index fc34279a..99efefb7 100644 --- a/recv.c +++ b/recv.c @@ -1,13 +1,15 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file recv.c the stand-alone audio stream receiver */ +#include #include #include +#include #include "para.h" #include "list.h" @@ -19,9 +21,10 @@ #include "string.h" #include "error.h" #include "stdout.h" +#include "buffer_tree.h" -/** the gengetopt args info struct */ -struct recv_args_info conf; +/** The gengetopt args info struct. */ +static struct recv_args_info conf; static int loglevel; /** Always log to stderr. */ @@ -57,7 +60,7 @@ static void *parse_config(int argc, char *argv[], int *receiver_num) } /** - * the main function of para_recv + * The main function of para_recv. * * \param argc number of arguments * \param argv vector of arguments @@ -89,28 +92,31 @@ int main(int argc, char *argv[]) } r = &receivers[receiver_num]; rn.receiver = r; + rn.btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = r->name)); ret = r->open(&rn); if (ret < 0) goto out; r_opened = 1; + sot.btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.parent = rn.btrn, .name = "stdout")); stdout_set_defaults(&sot); - sot.bufp = &rn.buf; - sot.loaded = &rn.loaded; - sot.input_error = &rn.task.error; register_task(&sot.task); rn.task.pre_select = r->pre_select; rn.task.post_select = r->post_select; - sprintf(rn.task.status, "receiver node"); + sprintf(rn.task.status, "%s", r->name); register_task(&rn.task); ret = schedule(&s); out: if (r_opened) r->close(&rn); - if (r) - r->shutdown(); + btr_free_node(rn.btrn); + btr_free_node(sot.btrn); + if (rn.conf) + r->free_config(rn.conf); if (ret < 0) PARA_ERROR_LOG("%s\n", para_strerror(-ret)); return ret < 0? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/recv.h b/recv.h index dc49ef95..0da3fd69 100644 --- a/recv.h +++ b/recv.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -12,18 +12,14 @@ struct receiver_node { /** Points to the corresponding receiver. */ struct receiver *receiver; - /** The output buffer. */ - char *buf; - /** The amount of bytes in \a buf. */ - size_t loaded; /** Receiver-specific data. */ void *private_data; - /** Pointer to the error member of the consumer. */ - int *output_error; /** Pointer to the configuration data for this instance. */ void *conf; /** The task associated with this instance. */ struct task task; + /** The receiver node is always the root of the buffer tree. */ + struct btr_node *btrn; }; /** @@ -54,6 +50,7 @@ struct receiver { * \a argc and \a argv. */ void *(*parse_config)(int argc, char **argv); + void (*free_config)(void *conf); /** * Open one instance of the receiver. * @@ -75,12 +72,6 @@ struct receiver { * \sa receiver_node. */ void (*close)(struct receiver_node *rn); - /** - * Deactivate the receiver. - * - * Clean up what init has allocated. - */ - void (*shutdown)(void); /** * Add file descriptors to fd_sets and compute timeout for select(2). * @@ -134,3 +125,4 @@ extern struct receiver receivers[]; void recv_init(void); void *check_receiver_arg(char *ra, int *receiver_num); void print_receiver_helps(int detailed); +int generic_recv_pre_select(struct sched *s, struct task *t); diff --git a/recv_common.c b/recv_common.c index 00c1dd84..6f3fc575 100644 --- a/recv_common.c +++ b/recv_common.c @@ -1,18 +1,21 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file recv_common.c common functions of para_recv and para_audiod */ -#include "para.h" +#include +#include +#include "para.h" #include "list.h" #include "sched.h" #include "ggo.h" #include "recv.h" #include "string.h" +#include "buffer_tree.h" DEFINE_RECEIVER_ARRAY; @@ -35,14 +38,16 @@ static void *parse_receiver_args(int receiver_num, char *options) void *conf; -// PARA_DEBUG_LOG("%s, options: %s\n", r->name, -// options? options : "(none)"); + PARA_DEBUG_LOG("%s, options: %s\n", r->name, + options? options : "(none)"); if (options) { PARA_DEBUG_LOG("options: %s\n", options); - argc = split_args(options, &argv, " \t"); + argc = create_argv(options, " \t", &argv); + if (argc < 0) + return NULL; for (i = argc - 1; i >= 0; i--) argv[i + 1] = argv[i]; - argc += 1; + argc++; } else { argc = 1; argv = para_malloc(2 * sizeof(char*)); @@ -50,7 +55,8 @@ static void *parse_receiver_args(int receiver_num, char *options) } argv[0] = make_message("%s_recv", r->name); conf = r->parse_config(argc, argv); - free(argv[0]); + for (i = 0; i < argc; i++) + free(argv[i]); free(argv); return conf; } @@ -116,3 +122,16 @@ void print_receiver_helps(int detailed) ggo_print_help(&r->help, detailed); } } + +int generic_recv_pre_select(struct sched *s, struct task *t) +{ + struct receiver_node *rn = container_of(t, struct receiver_node, task); + int ret = btr_node_status(rn->btrn, 0, BTR_NT_ROOT); + + t->error = 0; + if (ret < 0) { + s->timeout.tv_sec = 0; + s->timeout.tv_usec = 1; + } + return ret; +} diff --git a/ringbuffer.c b/ringbuffer.c index b27fd8e8..1f0b4e46 100644 --- a/ringbuffer.c +++ b/ringbuffer.c @@ -1,11 +1,13 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file ringbuffer.c Simple ringbuffer implementation */ +#include + #include "para.h" #include "ringbuffer.h" #include "string.h" diff --git a/ringbuffer.h b/ringbuffer.h index a7488429..7ddfb1c0 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/sched.c b/sched.c index 6f3f1faf..62d45d52 100644 --- a/sched.c +++ b/sched.c @@ -1,11 +1,12 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file sched.c Paraslash's scheduling functions. */ +#include #include /* readdir() */ #include #include @@ -24,7 +25,7 @@ static int initialized; static struct timeval now_struct; struct timeval *now = &now_struct; -/** +/* * Remove a task from the scheduler. * * \param t The task to remove. @@ -107,18 +108,28 @@ int schedule(struct sched *s) return -E_NOT_INITIALIZED; if (!s->select_function) s->select_function = para_select; - gettimeofday(now, NULL); again: FD_ZERO(&s->rfds); FD_ZERO(&s->wfds); s->timeout = s->default_timeout; s->max_fileno = -1; + gettimeofday(now, NULL); sched_preselect(s); if (list_empty(&pre_select_list) && list_empty(&post_select_list)) return 0; ret = s->select_function(s->max_fileno + 1, &s->rfds, &s->wfds, &s->timeout); if (ret < 0) return ret; + if (ret == 0) { + /* + * APUE: Be careful not to check the descriptor sets on return + * unless the return value is greater than zero. The return + * state of the descriptor sets is implementation dependent if + * either a signal is caught or the timer expires. + */ + FD_ZERO(&s->rfds); + FD_ZERO(&s->wfds); + } gettimeofday(now, NULL); sched_post_select(s); if (list_empty(&pre_select_list) && list_empty(&post_select_list)) @@ -126,7 +137,7 @@ again: goto again; } -/** +/* * Initialize the paraslash scheduler. */ static void init_sched(void) @@ -188,7 +199,7 @@ void sched_shutdown(void) * * Each entry of the list contains an identifier which is simply a hex number * that may be used in \a kill_task() to terminate the task. - * The result ist dynamically allocated and must be freed by the caller. + * The result is dynamically allocated and must be freed by the caller. */ char *get_task_list(void) { @@ -249,3 +260,88 @@ int kill_task(char *id) } return -E_NO_SUCH_TASK; } + +/** + * Set the select timeout to the minimal possible value. + * + * \param s Pointer to the scheduler struct. + * + * This causes the next select() call to return immediately. + */ +void sched_min_delay(struct sched *s) +{ + s->timeout.tv_sec = 0; + s->timeout.tv_usec = 1; +} + +/** + * Impose an upper bound for the timeout of the next select() call. + * + * \param timeout Maximal allowed timeout. + * \param s Pointer to the scheduler struct. + * + * If the current scheduler timeout is already smaller than \a timeout, this + * function does nothing. Otherwise the timeout for the next select() call is + * set to the given value. + * + * \sa sched_request_timeout_ms(). + */ +void sched_request_timeout(struct timeval *timeout, struct sched *s) +{ + if (tv_diff(&s->timeout, timeout, NULL) > 0) + s->timeout = *timeout; +} + +/** + * Force the next select() call to return before the given amount of milliseconds. + * + * \param ms The maximal allowed timeout in milliseconds. + * \param s Pointer to the scheduler struct. + * + * Like sched_request_timeout() this imposes an upper bound on the timeout + * value for the next select() call. + */ +void sched_request_timeout_ms(long unsigned ms, struct sched *s) +{ + struct timeval tv; + ms2tv(ms, &tv); + sched_request_timeout(&tv, s); +} + +/** + * Force the next select() call to return before the given future time. + * + * \param barrier Absolute time before select() should return. + * \param s Pointer to the scheduler struct. + * + * If \a barrier is in the past, this function does nothing. + * + * \sa sched_request_barrier_or_min_delay(). + */ +void sched_request_barrier(struct timeval *barrier, struct sched *s) +{ + struct timeval diff; + + if (tv_diff(now, barrier, &diff) > 0) + return; + sched_request_timeout(&diff, s); +} + +/** + * Force the next select() call to return before the given time. + * + * \param barrier Absolute time before select() should return. + * \param s Pointer to the scheduler struct. + * + * If \a barrier is in the past, this function requests a minimal timeout. + * + * \sa sched_min_delay(), sched_request_barrier(). + */ +void sched_request_barrier_or_min_delay(struct timeval *barrier, struct sched *s) +{ + struct timeval diff; + + if (tv_diff(now, barrier, &diff) > 0) + return sched_min_delay(s); + sched_request_timeout(&diff, s); +} diff --git a/sched.h b/sched.h index 26aaabe9..7681567b 100644 --- a/sched.h +++ b/sched.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -63,7 +63,7 @@ struct task { /** Position of the task in the post_select list of the scheduler. */ struct list_head post_select_node; /** Descriptive text and current status of the task. */ - char status[MAXLINE]; + char status[255]; }; /** @@ -79,3 +79,8 @@ int schedule(struct sched *s); char *get_task_list(void); int kill_task(char *id); void sched_shutdown(void); +void sched_min_delay(struct sched *s); +void sched_request_timeout(struct timeval *timeout, struct sched *s); +void sched_request_timeout_ms(long unsigned ms, struct sched *s); +void sched_request_barrier(struct timeval *barrier, struct sched *s); +void sched_request_barrier_or_min_delay(struct timeval *barrier, struct sched *s); diff --git a/score.c b/score.c index 51ec1e4a..6b9d05b5 100644 --- a/score.c +++ b/score.c @@ -1,10 +1,13 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file score.c Scoring functions to determine the audio file streaming order. */ +#include +#include + #include "para.h" #include "error.h" #include "string.h" @@ -93,7 +96,7 @@ static struct osl_table_description score_table_desc = { */ int get_num_admissible_files(unsigned *num) { - return osl_get_num_rows(score_table, num); + return osl(osl_get_num_rows(score_table, num)); } /** @@ -109,7 +112,7 @@ int get_num_admissible_files(unsigned *num) static int get_score_of_row(void *score_row, long *score) { struct osl_object obj; - int ret = osl_get_object(score_table, score_row, SCORECOL_SCORE, &obj); + int ret = osl(osl_get_object(score_table, score_row, SCORECOL_SCORE, &obj)); if (ret >= 0) *score = *(long *)obj.data; @@ -141,7 +144,7 @@ int score_add(const struct osl_row *aft_row, long score) *(int *)(score_objs[SCORECOL_SCORE].data) = score; // PARA_DEBUG_LOG("adding %p\n", *(void **) (score_objs[SCORECOL_AFT_ROW].data)); - ret = osl_add_row(score_table, score_objs); + ret = osl(osl_add_row(score_table, score_objs)); if (ret < 0) { PARA_ERROR_LOG("%s\n", para_strerror(-ret)); free(score_objs[SCORECOL_SCORE].data); @@ -152,7 +155,7 @@ int score_add(const struct osl_row *aft_row, long score) static int get_nth_score(unsigned n, long *score) { struct osl_row *row; - int ret = osl_get_nth_row(score_table, SCORECOL_SCORE, n, &row); + int ret = osl(osl_get_nth_row(score_table, SCORECOL_SCORE, n, &row)); if (ret < 0) return ret; @@ -180,9 +183,9 @@ int score_update(const struct osl_row *aft_row, long percent) unsigned n, new_pos; struct osl_object obj = {.data = (struct osl_row *)aft_row, .size = sizeof(aft_row)}; - int ret = osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, &row); + int ret = osl(osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, &row)); - if (ret == -E_RB_KEY_NOT_FOUND) /* not an error */ + if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) /* not an error */ return 1; if (ret < 0) return ret; @@ -198,7 +201,7 @@ int score_update(const struct osl_row *aft_row, long percent) obj.data = para_malloc(obj.size); *(long *)obj.data = new_score; PARA_DEBUG_LOG("new score: %ld, rank %u/%u\n", new_score, new_pos, n); - return osl_update_object(score_table, row, SCORECOL_SCORE, &obj); + return osl(osl_update_object(score_table, row, SCORECOL_SCORE, &obj)); } /** @@ -219,7 +222,7 @@ int get_score_and_aft_row(struct osl_row *score_row, long *score, if (ret < 0) return ret; - ret = osl_get_object(score_table, score_row, SCORECOL_AFT_ROW, &obj); + ret = osl(osl_get_object(score_table, score_row, SCORECOL_AFT_ROW, &obj)); if (ret < 0) return ret; *aft_row = obj.data; @@ -231,8 +234,7 @@ static int get_score_row_from_aft_row(const struct osl_row *aft_row, { struct osl_object obj = {.data = (struct osl_row *)aft_row, .size = sizeof(aft_row)}; - return osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, score_row); - + return osl(osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, score_row)); } /** @@ -250,7 +252,7 @@ static int get_score_row_from_aft_row(const struct osl_row *aft_row, */ int admissible_file_loop(void *data, osl_rbtree_loop_func *func) { - return osl_rbtree_loop(score_table, SCORECOL_SCORE, data, func); + return osl(osl_rbtree_loop(score_table, SCORECOL_SCORE, data, func)); } /** @@ -265,7 +267,7 @@ int admissible_file_loop(void *data, osl_rbtree_loop_func *func) */ int admissible_file_loop_reverse(void *data, osl_rbtree_loop_func *func) { - return osl_rbtree_loop_reverse(score_table, SCORECOL_SCORE, data, func); + return osl(osl_rbtree_loop_reverse(score_table, SCORECOL_SCORE, data, func)); } /** @@ -281,11 +283,11 @@ int score_get_best(struct osl_row **aft_row, long *score) { struct osl_row *row; struct osl_object obj; - int ret = osl_rbtree_last_row(score_table, SCORECOL_SCORE, &row); + int ret = osl(osl_rbtree_last_row(score_table, SCORECOL_SCORE, &row)); if (ret < 0) return ret; - ret = osl_get_object(score_table, row, SCORECOL_AFT_ROW, &obj); + ret = osl(osl_get_object(score_table, row, SCORECOL_AFT_ROW, &obj)); if (ret < 0) return ret; *aft_row = obj.data; @@ -309,7 +311,7 @@ int score_delete(const struct osl_row *aft_row) if (ret < 0) return ret; - return osl_del_row(score_table, score_row); + return osl(osl_del_row(score_table, score_row)); } /** @@ -327,13 +329,13 @@ int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank) struct osl_row *score_row; int ret = get_score_row_from_aft_row(aft_row, &score_row); - if (ret == -E_RB_KEY_NOT_FOUND) + if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) return 0; if (ret < 0) return ret; if (!rank) return 1; - ret = osl_get_rank(score_table, score_row, SCORECOL_SCORE, rank); + ret = osl(osl_get_rank(score_table, score_row, SCORECOL_SCORE, rank)); if (ret < 0) return ret; return 1; @@ -356,7 +358,7 @@ static void score_close(void) static int score_open(__a_unused const char *dir) { score_table_desc.dir = NULL; /* this table has only volatile columns */ - return osl_open_table(&score_table_desc, &score_table); + return osl(osl_open_table(&score_table_desc, &score_table)); } /** diff --git a/send.h b/send.h index 7087c266..85e5ed1f 100644 --- a/send.h +++ b/send.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -131,7 +131,7 @@ void send_chunk(struct sender_client *sc, struct sender_status *ss, size_t header_len); void init_sender_status(struct sender_status *ss, char **access_arg, int num_access_args, int port, int max_clients, int default_deny); -char *get_sender_info(struct sender_status *ss, char *name); +char *get_sender_info(struct sender_status *ss, const char *name); void generic_com_allow(struct sender_command_data *scd, struct sender_status *ss); diff --git a/send_common.c b/send_common.c index 8653c330..92c9ab3b 100644 --- a/send_common.c +++ b/send_common.c @@ -1,12 +1,15 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file send_common.c Functions used by more than one paraslash sender. */ +#include #include +#include + #include "para.h" #include "error.h" #include "string.h" @@ -39,7 +42,7 @@ */ static int open_sender(unsigned l4type, int port) { - int fd, ret = para_listen(AF_UNSPEC, l4type, port); + int fd, ret = para_listen_simple(l4type, port); if (ret < 0) return ret; @@ -214,7 +217,7 @@ void init_sender_status(struct sender_status *ss, char **access_arg, * * \return The string printed in the "si" command. */ -char *get_sender_info(struct sender_status *ss, char *name) +char *get_sender_info(struct sender_status *ss, const char *name) { char *clnts = NULL, *ret; struct sender_client *sc, *tmp_sc; @@ -228,14 +231,14 @@ char *get_sender_info(struct sender_status *ss, char *name) ret = make_message( "%s sender:\n" "\tstatus: %s\n" - "\tport: %d\n" + "\tport: %s\n" "\tnumber of connected clients: %d\n" "\tmaximal number of clients: %d%s\n" "\tconnected clients: %s\n" "\taccess %s list: %s\n", name, (ss->listen_fd >= 0)? "on" : "off", - ss->port, + stringify_port(ss->port, strcmp(name, "http") ? "dccp" : "tcp"), ss->num_clients, ss->max_clients, ss->max_clients > 0? "" : " (unlimited)", diff --git a/server.c b/server.c index 95ff25cf..89a81372 100644 --- a/server.c +++ b/server.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2009 Andre Noll + * Copyright (C) 1997-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -14,18 +14,20 @@ * * * - The main programs: \ref server.c, \ref audiod.c, \ref client.c, - * \ref audioc.c, \ref fsck.c, \ref afh.c + * \ref audioc.c, \ref afh.c * - Server: \ref server_command, \ref sender, - * - Audio file selector: \ref audio_format_handler, \ref mood, \ref afs_table, + * - Audio file selector: \ref audio_format_handler, \ref afs_table, * - Client: \ref receiver, \ref receiver_node, \ref filter, \ref filter_node. * * * The gory details, listed by topic: * - * - Audio format handlers: \ref send_common.c \ref mp3_afh.c, \ref ogg_afh.c, \ref aac_afh.c, - * - Decoders: \ref mp3dec_filter.c, \ref oggdec_filter.c, \ref aacdec_filter.c, + * - Audio format handlers: \ref send_common.c \ref mp3_afh.c, + * \ref ogg_afh.c, \ref aac_afh.c, \ref wma_afh.c, + * - Decoders: \ref mp3dec_filter.c, \ref oggdec_filter.c, + * \ref aacdec_filter.c, \ref wmadec_filter.c, * - Volume normalizer: \ref compress_filter.c, - * - Output: \ref alsa_write.c, \ref osx_write.c, + * - Output: \ref alsa_write.c, \ref osx_write.c, \ref oss_write.c, * - http: \ref http_recv.c, \ref http_send.c, * - udp: \ref udp_recv.c, \ref udp_send.c, * - dccp: \ref dccp_recv.c, \ref dccp_send.c, @@ -47,7 +49,6 @@ * - Time: \ref time.c, * - Spawning processes: \ref exec.c, * - Inter process communication: \ref ipc.c, - * - The object storage layer: \ref osl.c, * - Blob tables: \ref blob.c, * - The error subssystem: \ref error.h. * - Access control for paraslash senders: \ref acl.c, \ref acl.h. @@ -55,19 +56,22 @@ * Low-level data structures: * * - Doubly linked lists: \ref list.h, - * - Red-black trees: \ref rbtree.h, \ref rbtree.c, * - Ring buffer: \ref ringbuffer.c, \ref ringbuffer.h, * - Hashing: \ref hash.h, \ref sha1.h, \ref sha1.c, * - Crypto: \ref crypt.c. - * - Forward error correction: \ref fec.c + * - Forward error correction: \ref fec.c. */ #include #include #include +#include +#include +#include #include "para.h" #include "error.h" +#include "crypt.h" #include "server.cmdline.h" #include "afh.h" #include "string.h" @@ -241,6 +245,8 @@ void parse_config_or_die(int override) daemon_set_flag(DF_LOG_PID); daemon_set_flag(DF_LOG_LL); daemon_set_flag(DF_LOG_TIME); + if (conf.log_timing_given) + daemon_set_flag(DF_LOG_TIMING); ret = 1; out: free(cf); @@ -318,7 +324,6 @@ static void signal_post_select(struct sched *s, struct task *t) waitpid(mmd->afs_pid, NULL, 0); cleanup: free(mmd->afd.afhi.chunk_table); - free(mmd->afd.afhi.info_string); close_listed_fds(); mutex_destroy(mmd_mutex); shm_detach(mmd); @@ -360,7 +365,6 @@ static void command_post_select(struct sched *s, struct task *t) char *peer_name; pid_t child_pid; uint32_t *chunk_table; - char *info_string; if (!FD_ISSET(sct->listen_fd, &s->rfds)) return; @@ -372,16 +376,15 @@ static void command_post_select(struct sched *s, struct task *t) PARA_INFO_LOG("got connection from %s, forking\n", peer_name); mmd->num_connects++; mmd->active_connections++; - random(); - /* The chunk table and the info_string are pointers located in the - * mmd struct that point to dynamically allocated memory that must be - * freed by the parent and the child. However, as the mmd struct is in - * a shared memory area, there's no guarantee that after the fork these - * pointers are still valid in child context. As these two pointers are - * not used in the child anyway, we save them to local variables and - * free the memory via that copy in the child. + /* + * The chunk table is a pointer located in the mmd struct that points + * to dynamically allocated memory, i.e. it must be freed by the parent + * and the child. However, as the mmd struct is in a shared memory + * area, there's no guarantee that after the fork this pointer is still + * valid in child context. As it is not used in the child anyway, we + * save it to a local variable before the fork and free the memory via + * that copy in the child directly after the fork. */ - info_string = mmd->afd.afhi.info_string; chunk_table = mmd->afd.afhi.chunk_table; child_pid = fork(); if (child_pid < 0) { @@ -394,7 +397,6 @@ static void command_post_select(struct sched *s, struct task *t) return; } /* mmd might already have changed at this point */ - free(info_string); free(chunk_table); alarm(ALARM_TIMEOUT); close_listed_fds(); @@ -423,7 +425,7 @@ static void init_server_command_task(int argc, char **argv) sct->task.post_select = command_post_select; sct->argc = argc; sct->argv = argv; - ret = para_listen(AF_UNSPEC, IPPROTO_TCP, conf.port_arg); + ret = para_listen_simple(IPPROTO_TCP, conf.port_arg); if (ret < 0) goto err; sct->listen_fd = ret; @@ -438,35 +440,6 @@ err: exit(EXIT_FAILURE); } -static void init_random_seed(void) -{ - unsigned int seed; - int fd, ret = para_open("/dev/urandom", O_RDONLY, 0); - - if (ret < 0) - goto err; - fd = ret; - ret = read(fd, &seed, sizeof(seed)); - if (ret < 0) { - ret = -ERRNO_TO_PARA_ERROR(errno); - goto out; - } - if (ret != sizeof(seed)) { - ret = -ERRNO_TO_PARA_ERROR(EIO); - goto out; - } - srandom(seed); - ret = 1; -out: - close(fd); - if (ret >= 0) - return; -err: - PARA_EMERG_LOG("can not seed pseudo random number generator: %s\n", - para_strerror(-ret)); - exit(EXIT_FAILURE); -} - static int init_afs(void) { int ret, afs_server_socket[2]; @@ -475,7 +448,8 @@ static int init_afs(void) ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, afs_server_socket); if (ret < 0) exit(EXIT_FAILURE); - afs_socket_cookie = para_random((uint32_t)-1); + get_random_bytes_or_die((unsigned char *)&afs_socket_cookie, + sizeof(afs_socket_cookie)); afs_pid = fork(); if (afs_pid < 0) exit(EXIT_FAILURE); @@ -512,7 +486,7 @@ static void server_init(int argc, char **argv) int afs_socket; valid_fd_012(); - init_random_seed(); + init_random_seed_or_die(); /* parse command line options */ server_cmdline_parser_ext(argc, argv, &conf, ¶ms); HANDLE_VERSION_FLAG("server", conf); diff --git a/server.cmd b/server.cmd index a6b512fe..73ee3417 100644 --- a/server.cmd +++ b/server.cmd @@ -3,7 +3,8 @@ SF: command.c HC: prototypes for the server command handlers CC: array of server commands AT: server_command -IN: para error string afh afs server list user_list +SI: openssl/rc4 osl regex +IN: para error crypt command string afh afs server list user_list SN: list of server commands --- N: ff @@ -92,11 +93,14 @@ H: Print server uptime and other information. --- N: stat P: VSS_READ -D: Print status info for current audio file. -U: stat [n] -H: Without any arguments, stat continuously prints status messages -H: about the audio file being streamed. Use the optional number n -H: to let stat exit after having displayed status n times. +D: Print status info for the current audio file. +U: stat [-n num] [-p] +H: If -n is given, the command exits after having displayed the status n +H: times. Otherwise, the command runs in an endless loop. +H: +H: The -p option activates parser-friendly output: Each status item is +H: prefixed with its size in bytes and the status items identifiers are +H: printed as numerical values. --- N: stop P: VSS_READ | VSS_WRITE diff --git a/server.h b/server.h index cf17c0ef..43ba841d 100644 --- a/server.h +++ b/server.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2009 Andre Noll + * Copyright (C) 1997-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -12,23 +12,6 @@ /** The maximum length of the host component in an URL */ #define MAX_HOSTLEN 256 -/** - * Defines one command of para_server. - */ -struct server_command { - /** The name of the command. */ - const char *name; - /** Pointer to the function that handles the command. */ - int (*handler)(int, int, char * const * const); - /** The privileges a user must have to execute this command. */ - unsigned int perms; - /** One-line description of the command. */ - const char *description; - /** Summary of the command line options. */ - const char *usage; - /** The long help text. */ - const char *help; -}; /** Holds the arguments for the para_server's sender command. */ struct sender_command_data{ diff --git a/sha1.c b/sha1.c index 65524bc5..70731ec2 100644 --- a/sha1.c +++ b/sha1.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/signal.c b/signal.c index 5d3e2c9d..bded532e 100644 --- a/signal.c +++ b/signal.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009 Andre Noll + * Copyright (C) 2004-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/signal.h b/signal.h index f55409df..7be960ef 100644 --- a/signal.h +++ b/signal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2009 Andre Noll + * Copyright (C) 2007-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/skencil/overview.sk b/skencil/overview.sk index d57c08f8..5ab648c8 100644 --- a/skencil/overview.sk +++ b/skencil/overview.sk @@ -79,17 +79,6 @@ Fn('Times-Roman') Fs(14) txt('cmd output',(-1.83691e-16,-1,1,-1.83691e-16,64.2028,142.553)) lw(0.992126) -la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) -b() -bs(497.249,328.901,0) -bs(497.249,212.112,0) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(14) -txt('pcm data',(-1.83691e-16,-1,1,-1.83691e-16,507.289,295.35)) -lw(0.992126) ld((0.10000000000000001, 1.0)) b() bs(24.7094,792.443,0) @@ -174,7 +163,7 @@ le() lw(1) Fn('Times-Bold') Fs(36) -txt('0.3.0',(437.217,712.433)) +txt('0.4.0',(437.217,712.433)) fp((0,0,0)) le() lw(1) @@ -290,17 +279,6 @@ lw(1) ld((4, 4)) r(156.432,0,0,-101.428,50.1221,646.762) G() -G() -lw(1) -r(41.883,0,0,-28.2587,55.6728,642.22) -fp((0,0,0)) -le() -lw(1) -Fn('Times-Roman') -Fs(24) -txt('osl',(61.2681,622.499)) -G_() -G() lw(1) r(41.883,0,0,-28.2587,160.634,643.23) fp((0,0,0)) @@ -317,7 +295,6 @@ Fn('Times-Italic') txt('afs process',(106.135,651.24)) lw(1) r(0,0,0,-0.504639,125.815,629.1) -G_() lw(1) ld((5, 5)) r(272.999,0,0,-67.6189,281.238,614.467) @@ -469,6 +446,29 @@ Fn('Times-Roman') Fs(24) txt('afs',(0.886791,0,0,1,115.93,576.074)) G_() +G() +lw(1) +r(75.1101,0,0,-28.2587,56.6797,643.23) +fp((0,0,0)) +le() +lw(1) +Fn('Times-Roman') +Fs(24) +txt('libosl',(67.5587,620.179)) +G_() +G() +lw(0.992126) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +b() +bs(466.036,335.949,0) +bs(466.036,203.05,0) +fp((0,0,0)) +le() +lw(1) +Fn('Times-Roman') +Fs(14) +txt('pcm data',(-1.83691e-16,-1,1,-1.83691e-16,476.076,294.966)) +G_() guidelayer('Guide Lines',1,0,0,1,(0,0,1)) guide(-307.905,0) grid((0,0,20,20),0,(0,0,1),'Grid') diff --git a/stat.c b/stat.c index 0306cba4..58e289f7 100644 --- a/stat.c +++ b/stat.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -9,174 +9,75 @@ * and para_audiod. */ - -#include -#include +#include #include "para.h" -#include "close_on_fork.h" -#include "list.h" #include "error.h" #include "string.h" -#include "fd.h" - -/** The maximal number of simultaneous connections. */ -#define MAX_STAT_CLIENTS 50 -/** - * Describes a status client of para_audiod. - * - * There's one such structure per audiod client that sent the 'stat' command. - * - * A status client is identified by its file descriptor. para_audiod - * keeps a list of connected status clients. - */ -struct stat_client { - /** The stat client's file descriptor. */ - int fd; - /** Bitmask of those status items the client is interested in. */ - uint64_t item_mask; - /** Its entry in the list of stat clients. */ - struct list_head node; -}; -static struct list_head client_list; -static int initialized; -static int num_clients; +/** The minimal length of a status item buffer. */ +#define MIN_STAT_ITEM_LEN 9 /* 5 + 2 + 2, e.g. '0005 00:\n' */ -/** The list of all status items used by para_{server,audiod,gui}. */ -const char *status_item_list[] = {STATUS_ITEM_ARRAY}; - -static void dump_stat_client_list(void) -{ - struct stat_client *sc; - - if (!initialized) - return; - list_for_each_entry(sc, &client_list, node) - PARA_INFO_LOG("stat client on fd %d\n", sc->fd); -} /** - * Add a status client to the list. + * Call a function for each complete status item of a buffer. * - * \param fd The file descriptor of the client. - * \param mask Bitfield of status items for this client. + * \param item_buf The source buffer. + * \param num_bytes The length of \a buf. + * \param item_handler Function to call for each complete item. * - * Only those status items having the bit set in \a mask will be - * sent to the client. + * \return Negative on errors, the number of bytes _not_ passed to \a + * item_handler on success. * - * \return Positive value on success, or -E_TOO_MANY_CLIENTS if - * the number of connected clients exceeds #MAX_STAT_CLIENTS. + * Status items are expected in the format used by parser-friendly output mode + * of the stat command of para_client/para_audioc. */ -int stat_client_add(int fd, uint64_t mask) +int for_each_stat_item(char *item_buf, size_t num_bytes, + int (*item_handler)(int, char *)) { - struct stat_client *new_client; - - if (num_clients >= MAX_STAT_CLIENTS) { - PARA_ERROR_LOG("maximal number of stat clients (%d) exceeded\n", - MAX_STAT_CLIENTS); - return -E_TOO_MANY_CLIENTS; - } - if (!initialized) { - INIT_LIST_HEAD(&client_list); - initialized = 1; - } - PARA_INFO_LOG("adding client on fd %d\n", fd); - new_client = para_malloc(sizeof(struct stat_client)); - new_client->fd = fd; - new_client->item_mask = mask; - para_list_add(&new_client->node, &client_list); - dump_stat_client_list(); - num_clients++; - return 1; -} -/** - * Write a message to all connected status clients. - * - * \param msg A \p NULL terminated buffer. - * \param itemnum The number of the status item of \a msg. - * - * On write errors, remove the status client from the client list and close its - * file descriptor. - */ -void stat_client_write(const char *msg, int itemnum) -{ - struct stat_client *sc, *tmp; - size_t len = strlen(msg); - const uint64_t one = 1; - - if (!initialized || !len) - return; - list_for_each_entry_safe(sc, tmp, &client_list, node) { - int fd = sc->fd, ret; - - if (!((one << itemnum) & sc->item_mask)) - continue; - if (write_ok(fd) > 0) { - ret = write(fd, msg, len); - // PARA_DEBUG_LOG("dumped %s to fd %d, ret = %d\n", msg, fd, ret); - if (ret == len) + char *buf = item_buf; + int len = num_bytes; + + for (;;) { + int i, ret, item_len, item_num = 0; + if (len < MIN_STAT_ITEM_LEN) + break; + ret = read_size_header(buf); + if (ret < 0) + return ret; + item_len = ret; + if (item_len > len - 5) /* item not complete */ + break; + for (i = 0; i < 2; i++) { + unsigned char c = buf[5 + i]; + item_num <<= 4; + if (c >= '0' && c <= '9') { + item_num += c - '0'; continue; + } + if (c >= 'a' && c <= 'f') { + item_num += c - 'a' + 10; + continue; + } + return -E_STAT_ITEM_PARSE; } - /* write error or fd not ready for writing */ - close(fd); - num_clients--; - PARA_INFO_LOG("deleting client on fd %d\n", fd); - list_del(&sc->node); - free(sc); - dump_stat_client_list(); - } -// if (num_clients) -// PARA_DEBUG_LOG("%d client(s)\n", num_clients); -} - -/** - * Check if string is a known status item. - * - * \param item Buffer containing the text to check. - * - * \return If \a item is a valid status item, the number of that status item is - * returned. Otherwise, this function returns \p -E_UNKNOWN_STAT_ITEM. - */ -int stat_item_valid(const char *item) -{ - int i; - if (!item || !*item) { - PARA_ERROR_LOG("%s\n", "no item"); - return -E_UNKNOWN_STAT_ITEM; - } - FOR_EACH_STATUS_ITEM(i) - if (!strcmp(status_item_list[i], item)) - return i; - PARA_ERROR_LOG("invalid stat item: %s\n", item); - return -E_UNKNOWN_STAT_ITEM; -} - -/** - * Check if line starts with known status item. - * - * \param line Buffer containing the line. - * - * \return If the beginning of \a line matches any paraslash status item and is - * followed by a colon, the number of that status item is returned. Otherwise, - * this function returns \p -E_UNKNOWN_STAT_ITEM. - */ -int stat_line_valid(const char *line) -{ - int i; - size_t line_len; - - if (!line || !*line) - return -E_UNKNOWN_STAT_ITEM; - line_len = strlen(line); - FOR_EACH_STATUS_ITEM(i) { - const char *s = status_item_list[i]; - size_t item_len = strlen(s); - - if (line_len > item_len && line[item_len] == ':' && - !strncmp(line, s, item_len)) - return i; + if (buf[7] != ':' || buf[5 + item_len - 1] != '\n') + return -E_STAT_ITEM_PARSE; + buf[5 + item_len - 1] = '\0'; + if (item_num >= NUM_STAT_ITEMS) + PARA_WARNING_LOG("unknown status item %d: %s\n", + item_num, buf + 8); + else { + ret = item_handler(item_num, buf + 8); + if (ret < 0) + return ret; + } + buf += 5 + item_len; + len -= 5 + item_len; + assert(len >= 0 && buf <= item_buf + num_bytes); } - return -E_UNKNOWN_STAT_ITEM; + assert(len >= 0); + if (len && len != num_bytes) + memmove(item_buf, item_buf + num_bytes - len, len); + return len; } - diff --git a/stdin.c b/stdin.c index 14201a9a..5fc91c9d 100644 --- a/stdin.c +++ b/stdin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -8,15 +8,17 @@ #include /* readdir() */ #include - +#include +#include #include "para.h" -#include "string.h" #include "list.h" #include "sched.h" #include "fd.h" #include "error.h" #include "stdin.h" +#include "buffer_tree.h" +#include "string.h" /** * The pre_select function of the stdin task. @@ -31,17 +33,17 @@ static void stdin_pre_select(struct sched *s, struct task *t) { struct stdin_task *sit = container_of(t, struct stdin_task, task); + int ret; - if (sit->output_error && *sit->output_error < 0) { - t->error = *sit->output_error; - return; - } t->error = 0; - sit->check_fd = 0; - if (sit->loaded >= sit->bufsize) + ret = btr_node_status(sit->btrn, 0, BTR_NT_ROOT); + if (ret < 0) + sched_min_delay(s); + if (ret <= 0) return; - sit->check_fd = 1; - para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno); + if (btr_pool_unused(sit->btrp) > 0) + return para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno); + sched_request_timeout_ms(100, s); } /** @@ -52,30 +54,46 @@ static void stdin_pre_select(struct sched *s, struct task *t) * * This function checks if \p STDIN_FILENO was included by in the read fd set * of \a s during the previous pre_select call. If yes, and \p STDIN_FILENO - * appears to be readable, data is read from stdin into the buffer of the - * stdin task. + * appears to be readable, data is read from stdin and fed into the buffer + * tree. */ static void stdin_post_select(struct sched *s, struct task *t) { struct stdin_task *sit = container_of(t, struct stdin_task, task); ssize_t ret; + size_t sz; + char *buf = NULL; - if (sit->output_error && *sit->output_error < 0) { - t->error = *sit->output_error; - return; - } t->error = 0; - if (!sit->check_fd) + ret = btr_node_status(sit->btrn, 0, BTR_NT_ROOT); + if (ret < 0) + goto err; + if (ret == 0) return; if (!FD_ISSET(STDIN_FILENO, &s->rfds)) return; - ret = read(STDIN_FILENO, sit->buf + sit->loaded, sit->bufsize - sit->loaded); + sz = btr_pool_get_buffer(sit->btrp, &buf); + if (sz == 0) + return; + /* + * Do not use the maximal size to avoid having only a single buffer + * reference for the whole pool. This is bad because if that single + * reference can not be freed, we're stuck. + */ + sz = PARA_MIN(sz, btr_pool_size(sit->btrp) / 2); + ret = read(STDIN_FILENO, buf, sz); + if (ret < 0) + ret = -ERRNO_TO_PARA_ERROR(errno); + if (ret == 0) + ret = -E_STDIN_EOF; if (ret < 0) - t->error = -ERRNO_TO_PARA_ERROR(errno); - else if (ret > 0) - sit->loaded += ret; - else - t->error = -E_STDIN_EOF; + goto err; + btr_add_output_pool(sit->btrp, ret, sit->btrn); + return; +err: + btr_remove_node(sit->btrn); + //btr_pool_free(sit->btrp); + t->error = ret; } /** @@ -85,20 +103,19 @@ static void stdin_post_select(struct sched *s, struct task *t) * * This fills in the pre/post select function pointers of the task structure * given by \a sit. Moreover, the stdin file desctiptor is set to nonblocking - * mode and \a bufsize is initialized to 16 KB (but no buffer is allocated). + * mode, and a buffer tree is created. */ void stdin_set_defaults(struct stdin_task *sit) { int ret; - sit->bufsize = 32 * 1024, sit->task.pre_select = stdin_pre_select; sit->task.post_select = stdin_post_select; + sit->btrp = btr_pool_new("stdin", 64 * 1024); sprintf(sit->task.status, "stdin reader"); ret = mark_fd_nonblocking(STDIN_FILENO); if (ret >= 0) return; - sit->output_error = NULL; PARA_EMERG_LOG("%s\n", para_strerror(-ret)); exit(EXIT_FAILURE); } diff --git a/stdin.h b/stdin.h index edb72cf8..a1983dd5 100644 --- a/stdin.h +++ b/stdin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -8,18 +8,12 @@ /** The task structure used for reading from stdin. */ struct stdin_task { - /** Input buffer. */ - char *buf; - /** The size of \a buf. */ - size_t bufsize; - /** Number of bytes currently loaded in \a buf. */ - size_t loaded; - /** Pointer to the error member of the consumer. */ - int *output_error; - /** Whether \p STDIN_FILENO was included in the read fd set. */ - int check_fd; /** The task structure. */ struct task task; + /** Stdin is always the root of a buffer tree. */ + struct btr_node *btrn; + /* Use a buffer pool to minimize memcpy due to alignment problems. */ + struct btr_pool *btrp; }; void stdin_set_defaults(struct stdin_task *sit); diff --git a/stdout.c b/stdout.c index e178fe0e..eaf0f99f 100644 --- a/stdout.c +++ b/stdout.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -8,14 +8,15 @@ #include /* readdir() */ #include +#include #include "para.h" -#include "string.h" #include "list.h" #include "sched.h" #include "fd.h" #include "error.h" #include "stdout.h" +#include "buffer_tree.h" /** * The pre_select function of the stdout task. @@ -23,25 +24,20 @@ * \param s The scheduler this task was registered to. * \param t The task structure of the stdout task. * - * This function is always successful. If there is data available in the input - * buffer, it adds \p STDOUT_FILENO to the write fd set of \a s. + * This function is always successful. If there is input data available, it + * adds \p STDOUT_FILENO to the write fd set of \a s. */ static void stdout_pre_select(struct sched *s, struct task *t) { struct stdout_task *sot = container_of(t, struct stdout_task, task); + int ret; t->error = 0; - sot->check_fd = 0; - if (!*sot->loaded) { - if (*sot->input_error < 0) { - t->error = *sot->input_error; - s->timeout.tv_sec = 0; - s->timeout.tv_usec = 1; - } - return; - } - sot->check_fd = 1; - para_fd_set(STDOUT_FILENO, &s->wfds, &s->max_fileno); + ret = btr_node_status(sot->btrn, 0, BTR_NT_LEAF); + if (ret > 0) + para_fd_set(STDOUT_FILENO, &s->wfds, &s->max_fileno); + else if (ret < 0) + sched_min_delay(s); } /** @@ -50,41 +46,47 @@ static void stdout_pre_select(struct sched *s, struct task *t) * \param s The scheduler this task was registered to. * \param t The task structure of the stdout task. * - * This function checks if \p STDOUT_FILENO was included by in the write fd set - * of \a s during the previous pre_select call. If yes, and \p STDOUT_FILENO - * appeears to be writable, the data loaded in the input buffer is written to - * stdout. + * This function writes input data from the buffer tree to stdout if \p + * STDOUT_FILENO is writable. */ static void stdout_post_select(struct sched *s, struct task *t) { struct stdout_task *sot = container_of(t, struct stdout_task, task); - ssize_t ret; + struct btr_node *btrn = sot->btrn; + int ret; + char *buf; + size_t sz; t->error = 0; - if (!sot->check_fd) { - if (!*sot->loaded && *sot->input_error < 0) - t->error = *sot->input_error; + ret = btr_node_status(btrn, 0, BTR_NT_LEAF); + if (ret < 0) + goto out; + if (ret == 0) return; - } if (!FD_ISSET(STDOUT_FILENO, &s->wfds)) return; - ret = write(STDOUT_FILENO, *sot->bufp, *sot->loaded); - if (ret < 0) { - t->error = -ERRNO_TO_PARA_ERROR(errno); - return; + + for (;;) { + sz = btr_next_buffer(btrn, &buf); + if (sz == 0) + break; + ret = write_nonblock(STDOUT_FILENO, buf, sz, 0); + if (ret <= 0) + break; + btr_consume(btrn, ret); } - *sot->loaded -= ret; - if (*sot->loaded) - memmove(*sot->bufp, *sot->bufp + ret, *sot->loaded); +out: + if (ret < 0) + btr_remove_node(btrn); + t->error = ret; } - /** * Initialize a stdout task structure with default values. * * \param sot The stdout task structure. * - * This fills in the pre/post select function poinzters of the task structure - * given by \a sot. + * This fills in the pre/post select function pointers of the task structure + * given by \a sot and sets the stdout file descriptor to nonblocking mode. */ void stdout_set_defaults(struct stdout_task *sot) { @@ -92,7 +94,7 @@ void stdout_set_defaults(struct stdout_task *sot) sot->task.pre_select = stdout_pre_select; sot->task.post_select = stdout_post_select; - sprintf(sot->task.status, "stdout writer"); + sprintf(sot->task.status, "stdout"); ret = mark_fd_nonblocking(STDOUT_FILENO); if (ret >= 0) return; diff --git a/stdout.h b/stdout.h index cca12e5f..3783796c 100644 --- a/stdout.h +++ b/stdout.h @@ -1,25 +1,21 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ -/** \file stdout.h The standard out task structure. */ +/** \file stdout.h Writing to stdout via buffer trees. */ /** * The task structure used for writing to stdout. + * + * This is used by para_recv, para_filter and para_client. */ struct stdout_task { - /** Pointer to the data buffer pointer. */ - char **bufp; - /** Number of bytes loaded in \a buf. */ - size_t *loaded; - /** Pointer to the error variable of the feeding task. */ - int *input_error; - /** The task structure. */ + /** The task structure used by the scheduler. */ struct task task; - /** Whether \p STDOUT_FILENO was included in the write fd set. */ - int check_fd; + /** Stdout is always a leaf node in the buffer tree. */ + struct btr_node *btrn; }; void stdout_set_defaults(struct stdout_task *sot); diff --git a/string.c b/string.c index f51f3574..05febebf 100644 --- a/string.c +++ b/string.c @@ -1,19 +1,19 @@ /* - * Copyright (C) 2004-2009 Andre Noll + * Copyright (C) 2004-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file string.c Memory allocation and string handling functions. */ -#include "para.h" -#include "string.h" - #include /* gettimeofday */ #include #include /* uname() */ #include +#include +#include "para.h" +#include "string.h" #include "error.h" /** @@ -135,6 +135,20 @@ __must_check __printf_1_2 __malloc char *make_message(const char *fmt, ...) return msg; } +/** + * Free the content of a pointer and set it to \p NULL. + * + * This is equivalent to "free(*arg); *arg = NULL;". + * + * \param arg The pointer whose content should be freed. + */ +void freep(void *arg) +{ + void **ptr = (void **)arg; + free(*ptr); + *ptr = NULL; +} + /** * Paraslash's version of strcat(). * @@ -200,15 +214,15 @@ __must_check __malloc char *para_dirname(const char *name) * ends with a slash. Otherwise, a pointer within \a name is returned. Caller * must not free the result. */ -__must_check const char *para_basename(const char *name) +__must_check char *para_basename(const char *name) { - const char *ret; + char *ret; if (!name || !*name) return NULL; ret = strrchr(name, '/'); if (!ret) - return name; + return (char *)name; ret++; return ret; } @@ -231,27 +245,6 @@ void chop(char *buf) buf[n - 1] = '\0'; } -/** - * Get a random filename. - * - * This is by no means a secure way to create temporary files in a hostile - * directory like \p /tmp. However, it is OK to use for temp files, fifos, - * sockets that are created in ~/.paraslash. Result must be freed by the - * caller. - * - * \return A pointer to a random filename. - */ -__must_check __malloc char *para_tmpname(void) -{ - struct timeval now; - unsigned int seed; - - gettimeofday(&now, NULL); - seed = now.tv_usec; - srand(seed); - return make_message("%08i", rand()); -} - /** * Get the logname of the current user. * @@ -279,56 +272,6 @@ __must_check __malloc char *para_homedir(void) return para_strdup(pw? pw->pw_dir : "/tmp"); } -/** - * Split string and return pointers to its parts. - * - * \param args The string to be split. - * \param argv_ptr Pointer to the list of substrings. - * \param delim Delimiter. - * - * This function modifies \a args by replacing each occurrence of \a delim by - * zero. A \p NULL-terminated array of pointers to char* is allocated dynamically - * and these pointers are initialized to point to the broken-up substrings - * within \a args. A pointer to this array is returned via \a argv_ptr. - * - * \return The number of substrings found in \a args. - */ -unsigned split_args(char *args, char *** const argv_ptr, const char *delim) -{ - char *p; - char **argv; - size_t n = 0, i, j; - - p = args + strspn(args, delim); - for (;;) { - i = strcspn(p, delim); - if (!i) - break; - p += i; - n++; - p += strspn(p, delim); - } - *argv_ptr = para_malloc((n + 1) * sizeof(char *)); - argv = *argv_ptr; - i = 0; - p = args + strspn(args, delim); - while (p) { - argv[i] = p; - j = strcspn(p, delim); - if (!j) - break; - p += strcspn(p, delim); - if (*p) { - *p = '\0'; - p++; - p += strspn(p, delim); - } - i++; - } - argv[n] = NULL; - return n; -} - /** * Get the own hostname. * @@ -457,6 +400,53 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, private_data); } +/** Return the hex characters of the lower 4 bits. */ +#define hex(a) (hexchar[(a) & 15]) + +static void write_size_header(char *buf, int n) +{ + static char hexchar[] = "0123456789abcdef"; + + buf[0] = hex(n >> 12); + buf[1] = hex(n >> 8); + buf[2] = hex(n >> 4); + buf[3] = hex(n); + buf[4] = ' '; +} + +/** + * Read a four-byte hex-number and return its value. + * + * Each status item sent by para_server is prefixed with such a hex number in + * ASCII which describes the size of the status item. + * + * \param buf The buffer which must be at least four bytes long. + * + * \return The value of the hex number on success, \p -E_SIZE_PREFIX if the + * buffer did not contain only hex digits. + */ +int read_size_header(const char *buf) +{ + int i, len = 0; + + for (i = 0; i < 4; i++) { + unsigned char c = buf[i]; + len <<= 4; + if (c >= '0' && c <= '9') { + len += c - '0'; + continue; + } + if (c >= 'a' && c <= 'f') { + len += c - 'a' + 10; + continue; + } + return -E_SIZE_PREFIX; + } + if (buf[4] != ' ') + return -E_SIZE_PREFIX; + return len; +} + /** * Safely print into a buffer at a given offset. * @@ -472,7 +462,8 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, * private_data pointer of \a b are passed to the \a max_size_handler of \a b. * If this function succeeds, i.e. returns a non-negative value, the offset of * \a b is reset to zero and the given data is written to the beginning of the - * buffer. + * buffer. If \a max_size_handler() returns a negative value, this value is + * returned by \a para_printf(). * * Upon return, the offset of \a b is adjusted accordingly so that subsequent * calls to this function append data to what is already contained in the @@ -482,13 +473,15 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, * initial buffer is allocated. * * \return The number of bytes printed into the buffer (not including the - * terminating \p NULL byte). + * terminating \p NULL byte) on success, negative on errors. If there is no + * size-bound on \a b, i.e. if \p b->max_size is zero, this function never + * fails. * * \sa make_message(), vsnprintf(3). */ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...) { - int ret; + int ret, sz_off = (b->flags & PBF_SIZE_PREFIX)? 5 : 0; if (!b->buf) { b->buf = para_malloc(128); @@ -499,13 +492,16 @@ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...) char *p = b->buf + b->offset; size_t size = b->size - b->offset; va_list ap; - if (size) { + + if (size > sz_off) { va_start(ap, fmt); - ret = vsnprintf(p, size, fmt, ap); + ret = vsnprintf(p + sz_off, size - sz_off, fmt, ap); va_end(ap); - if (ret > -1 && ret < size) { /* success */ - b->offset += ret; - return ret; + if (ret > -1 && ret < size - sz_off) { /* success */ + b->offset += ret + sz_off; + if (sz_off) + write_size_header(p, ret); + return ret + sz_off; } } /* check if we may grow the buffer */ @@ -618,3 +614,178 @@ int get_loglevel_by_name(const char *txt) return LL_EMERG; return -1; } + +static int get_next_word(const char *buf, const char *delim, char **word) +{ + enum line_state_flags {LSF_HAVE_WORD = 1, LSF_BACKSLASH = 2, + LSF_SINGLE_QUOTE = 4, LSF_DOUBLE_QUOTE = 8}; + const char *in; + char *out; + int ret, state = 0; + + out = para_malloc(strlen(buf) + 1); + *out = '\0'; + *word = out; + for (in = buf; *in; in++) { + const char *p; + + switch (*in) { + case '\\': + if (state & LSF_BACKSLASH) /* \\ */ + goto copy_char; + state |= LSF_BACKSLASH; + state |= LSF_HAVE_WORD; + continue; + case 'n': + case 't': + if (state & LSF_BACKSLASH) { /* \n or \t */ + *out++ = (*in == 'n')? '\n' : '\t'; + state &= ~LSF_BACKSLASH; + continue; + } + goto copy_char; + case '"': + if (state & LSF_BACKSLASH) /* \" */ + goto copy_char; + if (state & LSF_SINGLE_QUOTE) /* '" */ + goto copy_char; + if (state & LSF_DOUBLE_QUOTE) { + state &= ~LSF_DOUBLE_QUOTE; + continue; + } + state |= LSF_HAVE_WORD; + state |= LSF_DOUBLE_QUOTE; + continue; + case '\'': + if (state & LSF_BACKSLASH) /* \' */ + goto copy_char; + if (state & LSF_DOUBLE_QUOTE) /* "' */ + goto copy_char; + if (state & LSF_SINGLE_QUOTE) { + state &= ~LSF_SINGLE_QUOTE; + continue; + } + state |= LSF_HAVE_WORD; + state |= LSF_SINGLE_QUOTE; + continue; + } + for (p = delim; *p; p++) { + if (*in != *p) + continue; + if (state & LSF_BACKSLASH) + goto copy_char; + if (state & LSF_SINGLE_QUOTE) + goto copy_char; + if (state & LSF_DOUBLE_QUOTE) + goto copy_char; + if (state & LSF_HAVE_WORD) + goto success; + break; + } + if (*p) /* ignore delimiter at the beginning */ + continue; +copy_char: + state |= LSF_HAVE_WORD; + *out++ = *in; + state &= ~LSF_BACKSLASH; + } + ret = 0; + if (!(state & LSF_HAVE_WORD)) + goto out; + ret = -ERRNO_TO_PARA_ERROR(EINVAL); + if (state & LSF_BACKSLASH) { + PARA_ERROR_LOG("trailing backslash\n"); + goto out; + } + if ((state & LSF_SINGLE_QUOTE) || (state & LSF_DOUBLE_QUOTE)) { + PARA_ERROR_LOG("unmatched quote character\n"); + goto out; + } +success: + *out = '\0'; + return in - buf; +out: + free(*word); + *word = NULL; + return ret; +} + +/** + * Free an array of words created by create_argv(). + * + * \param argv A pointer previously obtained by \ref create_argv(). + */ +void free_argv(char **argv) +{ + int i; + + for (i = 0; argv[i]; i++) + free(argv[i]); + free(argv); +} + +/** + * Split a buffer into words. + * + * This parser honors single and double quotes, backslash-escaped characters + * and special characters like \p \\n. The result contains pointers to copies + * of the words contained in \a buf and has to be freed by using \ref + * free_argv(). + * + * \param buf The buffer to be split. + * \param delim Each character in this string is treated as a separator. + * \param result The array of words is returned here. + * + * \return Number of words in \a buf, negative on errors. + */ +int create_argv(const char *buf, const char *delim, char ***result) +{ + char *word, **argv = para_malloc(2 * sizeof(char *)); + const char *p; + int ret, num_words; + + for (p = buf, num_words = 0; ; p += ret, num_words++) { + ret = get_next_word(p, delim, &word); + if (ret < 0) + goto err; + if (!ret) + break; + argv = para_realloc(argv, (num_words + 2) * sizeof(char*)); + argv[num_words] = word; + } + argv[num_words] = NULL; + *result = argv; + return num_words; +err: + while (num_words > 0) + free(argv[--num_words]); + free(argv); + return ret; +} + +/** + * Compile a regular expression. + * + * This simple wrapper calls regcomp() and logs a message on errors. + * + * \param preg See regcomp(3). + * \param regex See regcomp(3). + * \param cflags See regcomp(3). + * + * \return Standard. + */ +int para_regcomp(regex_t *preg, const char *regex, int cflags) +{ + char *buf; + size_t size; + int ret = regcomp(preg, regex, cflags); + + if (ret == 0) + return 1; + size = regerror(ret, preg, NULL, 0); + buf = para_malloc(size); + regerror(ret, preg, buf, size); + PARA_ERROR_LOG("%s\n", buf); + free(buf); + return -E_REGEX; +} diff --git a/string.h b/string.h index 272f107d..7b5887e1 100644 --- a/string.h +++ b/string.h @@ -1,11 +1,17 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file string.h exported sybmols from string.c */ +/** Flags that change how content is printed into the buffer. */ +enum para_buffer_flags { + /** Prefix each buffer with its length. */ + PBF_SIZE_PREFIX = 1, +}; + /** A string buffer used for para_printf(). */ struct para_buffer { /** The buffer. May be \p NULL. */ @@ -14,6 +20,8 @@ struct para_buffer { size_t size; /** The maximal size this buffer may grow. Zero means unlimited. */ size_t max_size; + /** \sa para_buffer_flags. */ + unsigned flags; /** The next para_printf() will write at this offset. */ size_t offset; /** @@ -26,6 +34,28 @@ struct para_buffer { void *private_data; }; +/** + * Write the contents of a status item to a para_buffer. + * + * \param b The para_buffer. + * \param n The number of the status item. + * \param f A format string. + * + * \return The return value of the underlying call to para_printf(). + */ +#define WRITE_STATUS_ITEM(b, n, f, ...) (\ +{ \ + int _ret; \ + if ((b)->flags & PBF_SIZE_PREFIX) { \ + _ret = para_printf((b), "%02x:" f, n, ## __VA_ARGS__); \ + } else { \ + _ret = para_printf((b), "%s: " f, status_item_list[(n)], \ + ## __VA_ARGS__); \ + } \ + _ret; \ +} \ +) + __must_check __malloc void *para_realloc(void *p, size_t size); __must_check __malloc void *para_malloc(size_t size); __must_check __malloc void *para_calloc(size_t size); @@ -33,12 +63,10 @@ __must_check __malloc char *para_strdup(const char *s); __must_check __malloc __printf_1_2 char *make_message(const char *fmt, ...); __must_check __malloc char *para_strcat(char *a, const char *b); __must_check __malloc char *para_dirname(const char *name); -__must_check const char *para_basename(const char *name); +__must_check char *para_basename(const char *name); void chop(char *buf); -__must_check __malloc char *para_tmpname(void); __must_check __malloc char *para_logname(void); __must_check __malloc char *para_homedir(void); -unsigned split_args(char *args, char *** const argv_ptr, const char *delim); __malloc char *para_hostname(void); __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...); /** Used for for_each_line() and for_each_line_ro(). */ @@ -50,3 +78,8 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, int para_atoi64(const char *str, int64_t *result); int para_atoi32(const char *str, int32_t *value); int get_loglevel_by_name(const char *txt); +int read_size_header(const char *buf); +int create_argv(const char *buf, const char *delim, char ***result); +void free_argv(char **argv); +int para_regcomp(regex_t *preg, const char *regex, int cflags); +void freep(void *arg); diff --git a/time.c b/time.c index 0cb9babe..8faefc05 100644 --- a/time.c +++ b/time.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -131,43 +131,44 @@ void tv_divide(const unsigned long divisor, const struct timeval *tv, /** * Compute a convex combination of two time values. * - * \param a The first coefiicent. + * \param a The first coefficient. * \param tv1 The first time value. - * \param b The second coefiicent. + * \param b The second coefficient. * \param tv2 The second time value. * \param result Contains the convex combination upon return. * - * compute x := (a * tv1 + b * tv2) / (|a| + |b|) and store |x| in \a result. + * Compute x := (a * tv1 + b * tv2) / (|a| + |b|) and store |x| in \a result. * Both \a a and \a b may be negative. * - * \return One if \a x is positive, -1 otherwise. + * \return Zero, 1 or -1, if \a x is zero, positive or negative, respectively. */ int tv_convex_combination(const long a, const struct timeval *tv1, const long b, const struct timeval *tv2, struct timeval *result) { struct timeval tmp1, tmp2, tmp3; - int ret = 1, subtract = ((a > 0 && b < 0) || (a < 0 && b > 0)); - unsigned long a1 = PARA_ABS(a), b1 = PARA_ABS(b); + int ret = 1; + unsigned long a1, b1; + if (a == 0 && b == 0) { + result->tv_sec = 0; + result->tv_usec = 0; + return 0; + } + a1 = PARA_ABS(a); + b1 = PARA_ABS(b); tv_scale(a1, tv1, &tmp1); tv_scale(b1, tv2, &tmp2); - if (subtract) + if ((a > 0 && b < 0) || (a < 0 && b > 0)) /* subtract */ ret = tv_diff(&tmp1, &tmp2, &tmp3); else tv_add(&tmp1, &tmp2, &tmp3); - if (a1 + b1) - tv_divide(a1 + b1, &tmp3, result); - else { - result->tv_sec = 0; - result->tv_usec = 0; - } + tv_divide(a1 + b1, &tmp3, result); if (!a || !b) { if (a + b < 0) ret = -1; - } else - if (a < 0) - ret = -ret; + } else if (a < 0) + ret = -ret; return ret; } diff --git a/udp_recv.c b/udp_recv.c index e4d23404..00ad3e27 100644 --- a/udp_recv.c +++ b/udp_recv.c @@ -1,10 +1,11 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file udp_recv.c Paraslash's udp receiver */ +#include #include #include #include @@ -17,13 +18,11 @@ #include "ggo.h" #include "recv.h" #include "udp_recv.cmdline.h" -#include "audiod.h" #include "string.h" #include "net.h" #include "fd.h" +#include "buffer_tree.h" -/** The size of the receiver node buffer. */ -#define UDP_RECV_CHUNK_SIZE (128 * 1024) /** * Data specific to the udp receiver. * @@ -32,6 +31,7 @@ struct private_udp_recv_data { /** The socket file descriptor. */ int fd; + struct btr_pool *btrp; }; static void udp_recv_pre_select(struct sched *s, struct task *t) @@ -39,63 +39,75 @@ static void udp_recv_pre_select(struct sched *s, struct task *t) struct receiver_node *rn = container_of(t, struct receiver_node, task); struct private_udp_recv_data *purd = rn->private_data; + if (generic_recv_pre_select(s, t) <= 0) + return; para_fd_set(purd->fd, &s->rfds, &s->max_fileno); } -static int enough_space(size_t nbytes, size_t loaded) +static int udp_check_eof(size_t sz, struct iovec iov[2]) { - return nbytes + loaded < UDP_RECV_CHUNK_SIZE; -} - -static int add_rn_output(struct receiver_node *rn, char *buf, size_t len) -{ - if (!len) - return 1; - if (!enough_space(len, rn->loaded)) - return -E_UDP_OVERRUN; - memcpy(rn->buf + rn->loaded, buf, len); - rn->loaded += len; - return 1; + if (sz < FEC_EOF_PACKET_LEN) + return 0; + if (iov[0].iov_len >= FEC_EOF_PACKET_LEN) { + if (memcmp(iov[0].iov_base, FEC_EOF_PACKET, + FEC_EOF_PACKET_LEN) != 0) + return 0; + return -E_RECV_EOF; + } + if (memcmp(iov[0].iov_base, FEC_EOF_PACKET, iov[0].iov_len) != 0) + return 0; + if (memcmp(iov[1].iov_base, FEC_EOF_PACKET + iov[0].iov_len, + FEC_EOF_PACKET_LEN - iov[0].iov_len) != 0) + return 0; + return -E_RECV_EOF; } static void udp_recv_post_select(__a_unused struct sched *s, struct task *t) { struct receiver_node *rn = container_of(t, struct receiver_node, task); struct private_udp_recv_data *purd = rn->private_data; - int ret; - char tmpbuf[UDP_RECV_CHUNK_SIZE]; + struct btr_node *btrn = rn->btrn; size_t packet_size; + struct iovec iov[2]; + int ret, iovcnt; - if (rn->output_error && *rn->output_error < 0) { - t->error = *rn->output_error; + t->error = 0; + ret = btr_node_status(btrn, 0, BTR_NT_ROOT); + if (ret < 0) + goto err; + if (ret == 0) return; - } if (!FD_ISSET(purd->fd, &s->rfds)) return; - ret = recv_bin_buffer(purd->fd, tmpbuf, UDP_RECV_CHUNK_SIZE); - if (ret < 0) { - if (is_errno(ret, EINTR) || is_errno(ret, EAGAIN)) - goto success; - t->error = ret; - return; - } - t->error = -E_RECV_EOF; - if (!ret) + iovcnt = btr_pool_get_buffers(purd->btrp, iov); + ret = -E_UDP_OVERRUN; + if (iovcnt == 0) + goto err; + ret = para_readv(purd->fd, iov, iovcnt); + /* EAGAIN is possible even if FD_ISSET */ + if (ret < 0 && is_errno(-ret, EAGAIN)) return; + if (ret == 0) + ret = -E_RECV_EOF; + if (ret < 0) + goto err; packet_size = ret; - if (packet_size >= FEC_EOF_PACKET_LEN) - if (!memcmp(tmpbuf, FEC_EOF_PACKET, FEC_EOF_PACKET_LEN)) - return; - t->error = add_rn_output(rn, tmpbuf, packet_size); - if (t->error < 0) - return; -success: - t->error = 1; -} - -static void udp_shutdown(void) -{ + ret = udp_check_eof(packet_size, iov); + if (ret < 0) + goto err; + if (iov[0].iov_len >= packet_size) + btr_add_output_pool(purd->btrp, packet_size, btrn); + else { /* both buffers contain data */ + btr_add_output_pool(purd->btrp, iov[0].iov_len, btrn); + btr_add_output_pool(purd->btrp, packet_size - iov[0].iov_len, + btrn); + } return; +err: + btr_remove_node(btrn); + t->error = ret; + close(purd->fd); + purd->fd = -1; } static void udp_recv_close(struct receiver_node *rn) @@ -104,8 +116,8 @@ static void udp_recv_close(struct receiver_node *rn) if (purd->fd >= 0) close(purd->fd); + btr_pool_free(purd->btrp); free(rn->private_data); - free(rn->buf); } static void *udp_recv_parse_config(int argc, char **argv) @@ -189,11 +201,10 @@ static int udp_recv_open(struct receiver_node *rn) char *iface = c->iface_given ? c->iface_arg : NULL; int ret; - rn->buf = para_calloc(UDP_RECV_CHUNK_SIZE); rn->private_data = para_calloc(sizeof(struct private_udp_recv_data)); purd = rn->private_data; - ret = makesock(AF_UNSPEC, IPPROTO_UDP, 1, c->host_arg, c->port_arg); + ret = makesock(IPPROTO_UDP, 1, c->host_arg, c->port_arg, NULL); if (ret < 0) goto err; purd->fd = ret; @@ -201,21 +212,29 @@ static int udp_recv_open(struct receiver_node *rn) ret = mcast_receiver_setup(purd->fd, iface); if (ret < 0) { close(purd->fd); - return ret; + goto err; } ret = mark_fd_nonblocking(purd->fd); - if (ret < 0) + if (ret < 0) { + close(purd->fd); goto err; - PARA_NOTICE_LOG("receiving from %s:%d, fd=%d\n", c->host_arg, + } + PARA_INFO_LOG("receiving from %s:%d, fd=%d\n", c->host_arg, c->port_arg, purd->fd); + purd->btrp = btr_pool_new("udp_recv", 320 * 1024); return purd->fd; err: free(rn->private_data); - free(rn->buf); return ret; } +static void udp_recv_free_config(void *conf) +{ + udp_recv_cmdline_parser_free(conf); + free(conf); +} + /** * The init function of the udp receiver. * @@ -228,12 +247,12 @@ void udp_recv_init(struct receiver *r) struct udp_recv_args_info dummy; udp_recv_cmdline_parser_init(&dummy); - r->shutdown = udp_shutdown; r->open = udp_recv_open; r->close = udp_recv_close; r->pre_select = udp_recv_pre_select; r->post_select = udp_recv_post_select; r->parse_config = udp_recv_parse_config; + r->free_config = udp_recv_free_config; r->help = (struct ggo_help) { .short_help = udp_recv_args_info_help, .detailed_help = udp_recv_args_info_detailed_help diff --git a/udp_send.c b/udp_send.c index 8677ec5e..3f9059bb 100644 --- a/udp_send.c +++ b/udp_send.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -7,10 +7,12 @@ /** \file udp_send.c Para_server's udp sender. */ +#include #include #include #include #include +#include #include "server.cmdline.h" #include "para.h" @@ -158,7 +160,7 @@ static int udp_init_session(struct udp_target *ut) if (ut->fd >= 0) /* nothing to do */ return 0; - ret = makesock(AF_UNSPEC, IPPROTO_UDP, 0, ut->host, ut->port); + ret = para_connect_simple(IPPROTO_UDP, ut->host, ut->port); if (ret < 0) return ret; ut->fd = ret; @@ -304,10 +306,10 @@ static char *udp_info(void) ret = make_message( "udp sender:\n" "\tstatus: %s\n" - "\tport: udp %d\n" + "\tport: %s\n" "\ttargets: %s\n", (sender_status == SENDER_ON)? "on" : "off", - conf.udp_default_port_arg, + stringify_port(conf.udp_default_port_arg, "udp"), tgts? tgts : "(none)" ); free(tgts); diff --git a/user_list.c b/user_list.c index f5aabc09..a1f1557c 100644 --- a/user_list.c +++ b/user_list.c @@ -1,20 +1,24 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file user_list.c User handling for para_server. */ +#include #include #include +#include #include "para.h" #include "error.h" +#include "crypt.h" #include "fd.h" #include "string.h" #include "list.h" #include "user_list.h" +#include "rc4.h" static struct list_head user_list; @@ -53,6 +57,18 @@ static void populate_user_list(char *user_list_file) para_strerror(-ret)); continue; } + /* + * In order to encrypt len := CHALLENGE_SIZE + 2 * RC4_KEY_LEN + * bytes using RSA_public_encrypt() with EME-OAEP padding mode, + * RSA_size(rsa) must be greater than len + 41. So ignore keys + * which are too short. For details see RSA_public_encrypt(3). + */ + if (ret <= CHALLENGE_SIZE + 2 * RC4_KEY_LEN + 41) { + PARA_WARNING_LOG("rsa key %s too short (%d)\n", + k, ret); + rsa_free(rsa); + continue; + } u = para_malloc(sizeof(*u)); u->name = para_strdup(n); u->rsa = rsa; diff --git a/user_list.h b/user_list.h index 5baceacc..5d3b27fb 100644 --- a/user_list.h +++ b/user_list.h @@ -1,13 +1,11 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file user_list.h exported functions from user_list.c */ -#include "crypt.h" - /** * permission flags that can be set individually for any server command * diff --git a/versions/paraslash-0.4.0.tar.bz2 b/versions/paraslash-0.4.0.tar.bz2 new file mode 100644 index 00000000..b4bd7fd9 Binary files /dev/null and b/versions/paraslash-0.4.0.tar.bz2 differ diff --git a/versions/paraslash-0.4.0.tar.bz2.asc b/versions/paraslash-0.4.0.tar.bz2.asc new file mode 100644 index 00000000..c97337db --- /dev/null +++ b/versions/paraslash-0.4.0.tar.bz2.asc @@ -0,0 +1,7 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.6 (GNU/Linux) + +iD8DBQBK+ZoEWto1QDEAkw8RAnNWAJ918+nDTCYHpYNyDiYiOg42QyfPIACZAWAI +a3yaR7ISQ9Fl5RGNkcnU3ms= +=1J9B +-----END PGP SIGNATURE----- diff --git a/versions/paraslash-0.4.1.tar.bz2 b/versions/paraslash-0.4.1.tar.bz2 new file mode 100644 index 00000000..a6c815ee Binary files /dev/null and b/versions/paraslash-0.4.1.tar.bz2 differ diff --git a/versions/paraslash-0.4.1.tar.bz2.asc b/versions/paraslash-0.4.1.tar.bz2.asc new file mode 100644 index 00000000..6a8296e9 --- /dev/null +++ b/versions/paraslash-0.4.1.tar.bz2.asc @@ -0,0 +1,7 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.6 (GNU/Linux) + +iD8DBQBLMMIsWto1QDEAkw8RAobAAJ98LeeEymj+MuUsNe8fse8Ty3mzxgCgj+RP +uuiX7zu0xTzuuKchgesqxAU= +=lTsJ +-----END PGP SIGNATURE----- diff --git a/vss.c b/vss.c index b63a1fcb..bfb0f0a1 100644 --- a/vss.c +++ b/vss.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1997-2009 Andre Noll + * Copyright (C) 1997-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -11,7 +11,9 @@ * senders. */ +#include #include +#include #include "para.h" #include "error.h" @@ -499,8 +501,7 @@ static void compute_slice_timeout(struct timeval *timeout) static void set_eof_barrier(struct vss_task *vsst) { struct fec_client *fc; - struct timeval timeout = mmd->afd.afhi.eof_tv, - *chunk_tv = vss_chunk_time(); + struct timeval timeout = {1, 0}, *chunk_tv = vss_chunk_time(); if (!chunk_tv) goto out; @@ -636,16 +637,12 @@ static void vss_eof(struct vss_task *vsst) para_munmap(vsst->map, mmd->size); vsst->map = NULL; mmd->chunks_sent = 0; - mmd->offset = 0; + //mmd->offset = 0; mmd->afd.afhi.seconds_total = 0; mmd->afd.afhi.chunk_tv.tv_sec = 0; mmd->afd.afhi.chunk_tv.tv_usec = 0; free(mmd->afd.afhi.chunk_table); mmd->afd.afhi.chunk_table = NULL; - free(mmd->afd.afhi.info_string); - mmd->afd.afhi.info_string = make_message("%s:\n%s:\n%s:\n", status_item_list[SI_AUDIO_FILE_INFO], - status_item_list[SI_TAGINFO1], status_item_list[SI_TAGINFO2]); - make_empty_status_items(mmd->afd.verbose_ls_output); mmd->mtime = 0; mmd->size = 0; mmd->events++; @@ -681,6 +678,13 @@ static int need_to_request_new_audio_file(struct vss_task *vsst) return 1; } +static void set_mmd_offset(void) +{ + struct timeval offset; + tv_scale(mmd->current_chunk, &mmd->afd.afhi.chunk_tv, &offset); + mmd->offset = tv2ms(&offset); +} + /** * Compute the timeout for the main select-loop of the scheduler. * @@ -725,6 +729,7 @@ static void vss_pre_select(struct sched *s, struct task *t) mmd->chunks_sent = 0; mmd->current_chunk = mmd->repos_request; mmd->new_vss_status_flags &= ~VSS_REPOS; + set_mmd_offset(); } if (need_to_request_new_audio_file(vsst)) { PARA_DEBUG_LOG("ready and playing, but no audio file\n"); @@ -794,7 +799,6 @@ static void recv_afs_result(struct vss_task *vsst) if (passed_fd < 0) goto err; shmid = afs_data; - free(mmd->afd.afhi.info_string); ret = load_afd(shmid, &mmd->afd); if (ret < 0) goto err; @@ -880,11 +884,9 @@ static void vss_send(struct vss_task *vsst) size_t len; if (!mmd->chunks_sent) { - struct timeval tmp; mmd->stream_start = *now; - tv_scale(mmd->current_chunk, &mmd->afd.afhi.chunk_tv, &tmp); - mmd->offset = tv2ms(&tmp); mmd->events++; + set_mmd_offset(); } /* * We call the send function also in case of empty chunks as @@ -971,7 +973,6 @@ void init_vss_task(int afs_socket) free(hn); free(home); mmd->sender_cmd_data.cmd_num = -1; - make_empty_status_items(mmd->afd.verbose_ls_output); if (conf.autoplay_given) { struct timeval tmp; mmd->vss_status_flags |= VSS_PLAYING; diff --git a/vss.h b/vss.h index 9bdc449c..ed2cd785 100644 --- a/vss.h +++ b/vss.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ diff --git a/wav_filter.c b/wav_filter.c index 1c0da181..abd850b2 100644 --- a/wav_filter.c +++ b/wav_filter.c @@ -1,33 +1,33 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file wav_filter.c A filter that inserts a wave header. */ +#include +#include + #include "para.h" #include "error.h" - #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "string.h" #include "portable_io.h" -/** size of the output buffer */ -#define WAV_OUTBUF_SIZE 81920 -/** a wav header is always 44 bytes */ +/** A wav header is always 44 bytes. */ #define WAV_HEADER_LEN 44 -/** always write 16 bit header */ +/** Always write 16 bit header. */ #define BITS 16 static void make_wav_header(unsigned int channels, unsigned int samplerate, - struct filter_node *fn) + char *headbuf) { - char *headbuf = fn->buf; unsigned int size = 0x7fffffff; int bytespersec = channels * samplerate * BITS / 8; int align = channels * BITS / 8; @@ -49,34 +49,8 @@ static void make_wav_header(unsigned int channels, unsigned int samplerate, write_u32(headbuf + 40, size - 44); /* chunk size */ } -static ssize_t wav_convert(char *inbuf, size_t len, struct filter_node *fn) -{ - size_t copy; - int *bof = fn->private_data; - - if (*bof) { - if (!len) - return 0; - if (!fn->fc->channels || !fn->fc->samplerate) { - PARA_ERROR_LOG("%s\n", para_strerror(E_WAV_BAD_FC)); - return -E_WAV_BAD_FC; - } - make_wav_header(fn->fc->channels, fn->fc->samplerate, fn); - fn->loaded = WAV_HEADER_LEN; - *bof = 0; -// return 0; - } - copy = PARA_MIN(len, fn->bufsize - fn->loaded); - memmove(fn->buf + fn->loaded, inbuf, copy); - fn->loaded += copy; -// PARA_DEBUG_LOG("len = %d, copy = %d\n", len, copy); - return copy; -} - static void wav_close(struct filter_node *fn) { - free(fn->buf); - fn->buf = NULL; free(fn->private_data); fn->private_data = NULL; } @@ -85,24 +59,80 @@ static void wav_open(struct filter_node *fn) { int *bof; - fn->bufsize = WAV_OUTBUF_SIZE; - fn->buf = para_malloc(fn->bufsize); fn->private_data = para_malloc(sizeof(int)); bof = fn->private_data; - fn->loaded = 0; *bof = 1; - PARA_INFO_LOG("wav filter node: %p, output buffer: %p, loaded: %zd\n", - fn, fn->buf, fn->loaded); +} + +static void wav_pre_select(struct sched *s, struct task *t) +{ + struct filter_node *fn = container_of(t, struct filter_node, task); + size_t iqs = btr_get_input_queue_size(fn->btrn); + + t->error = 0; + if (iqs == 0) + return; + s->timeout.tv_sec = 0; + s->timeout.tv_usec = 1; +} + +static void wav_post_select(__a_unused struct sched *s, struct task *t) +{ + struct filter_node *fn = container_of(t, struct filter_node, task); + struct btr_node *btrn = fn->btrn; + size_t iqs = btr_get_input_queue_size(btrn); + int ret; + char *header, *buf; + int32_t rate, ch; + + t->error = 0; + if (iqs == 0) { + ret = -E_WAV_EOF; + if (btr_no_parent(btrn)) + goto err; + return; + } + ret = btr_exec_up(btrn, "samplerate", &buf); + if (ret < 0) { + ret = -E_WAV_BAD_FC; + goto err; + } + ret = para_atoi32(buf, &rate); + free(buf); + if (ret < 0) + goto err; + ret = btr_exec_up(btrn, "channels", &buf); + if (ret < 0) { + ret = -E_WAV_BAD_FC; + goto err; + } + ret = para_atoi32(buf, &ch); + free(buf); + if (ret < 0) + goto err; + header = para_malloc(WAV_HEADER_LEN); + make_wav_header(ch, rate, header); + btr_add_output(header, WAV_HEADER_LEN, btrn); + ret = -E_WAV_SUCCESS; +err: + t->error = ret; + if (ret == -E_WAV_SUCCESS) + btr_splice_out_node(btrn); + else { + btr_remove_node(btrn); + PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + } } /** - * the init function of the wav filter + * The init function of the wav filter. * - * \param f struct to initialize + * \param f Structure to initialize. */ void wav_filter_init(struct filter *f) { - f->convert = wav_convert; f->close = wav_close; f->open = wav_open; + f->pre_select = wav_pre_select; + f->post_select = wav_post_select; } diff --git a/web/images/paraslash.ico b/web/images/paraslash.ico new file mode 100644 index 00000000..f8b78169 Binary files /dev/null and b/web/images/paraslash.ico differ diff --git a/web/images/paraslash.png b/web/images/paraslash.png new file mode 100644 index 00000000..d4a1bd9a Binary files /dev/null and b/web/images/paraslash.png differ diff --git a/web/index.in.html b/web/index.in.html index 65b7f0ed..5d34c507 100644 --- a/web/index.in.html +++ b/web/index.in.html @@ -1,10 +1,18 @@

Events


    +
  • 2009-12-22: paraslash-0.4.1 + (sig) + "concurrent horizon" +
  • 2009-12-07: paraslash-0.3.6 (sig) "cubic continuity"
  • +
  • 2009-11-10: paraslash-0.4.0 + (sig) + "simultaneous independence" +
  • 2009-09-21: paraslash-0.3.5 (sig) "symplectic separability" diff --git a/web/para.css b/web/para.css index 157cce96..f103abd3 100644 --- a/web/para.css +++ b/web/para.css @@ -93,8 +93,8 @@ A.qindexHL:visited { A.el { text-decoration: none; font-weight: bold } A.elRef { font-weight: bold } -A.code:link { text-decoration: none; font-weight: normal; color: ##BA3708} -A.code:visited { text-decoration: none; font-weight: normal; color: ##BA3708} +A.code:link { text-decoration: none; font-weight: normal; color: #BA3708} +A.code:visited { text-decoration: none; font-weight: normal; color: #BA3708} A.codeRef:link { font-weight: normal; color: #BA3708} A.codeRef:visited { font-weight: normal; color: #BA3708} A:hover { text-decoration: none; background-color: #ffff00 } diff --git a/web/screenshots/audiod.log b/web/screenshots/audiod.log new file mode 100644 index 00000000..c9e43663 --- /dev/null +++ b/web/screenshots/audiod.log @@ -0,0 +1,549 @@ +Dec 08 19:23:27 meins 2 log_welcome: welcome to para_audiod git (Sat Dec 8 13:42:52 MET 2007) +Dec 08 19:23:27 meins 2 init_writers: maximal number of writers: 3 +Dec 08 19:23:27 meins 2 check_writer_arg: checking alsa -d plug:swmix +Dec 08 19:23:27 meins 2 alsa_parse_config: options: -d plug:swmix, 2 +Dec 08 19:23:27 meins 2 alsa_parse_config: help given: 0 +Dec 08 19:23:27 meins 2 init_writers: mp3 writer #0: alsa +Dec 08 19:23:27 meins 2 check_writer_arg: checking alsa -d plug:swmix +Dec 08 19:23:27 meins 2 alsa_parse_config: options: -d plug:swmix, 2 +Dec 08 19:23:27 meins 2 alsa_parse_config: help given: 0 +Dec 08 19:23:27 meins 2 init_writers: ogg writer #0: alsa +Dec 08 19:23:27 meins 2 check_writer_arg: checking alsa -d plug:swmix +Dec 08 19:23:27 meins 2 alsa_parse_config: options: -d plug:swmix, 2 +Dec 08 19:23:27 meins 2 alsa_parse_config: help given: 0 +Dec 08 19:23:27 meins 2 init_writers: aac writer #0: alsa +Dec 08 19:23:27 meins 2 init_receivers: initializing http receiver +Dec 08 19:23:27 meins 2 init_receivers: initializing dccp receiver +Dec 08 19:23:27 meins 2 init_receivers: initializing ortp receiver +Dec 08 19:23:27 meins 2 init_filters: maximal number of filters: 6 +Dec 08 19:23:27 meins 2 add_filter: mp3 filter 1: mp3dec +Dec 08 19:23:27 meins 2 add_filter: mp3 filter 2: compress +Dec 08 19:23:27 meins 2 add_filter: ogg filter 1: oggdec +Dec 08 19:23:27 meins 2 add_filter: ogg filter 2: compress +Dec 08 19:23:27 meins 2 add_filter: aac filter 1: aacdec +Dec 08 19:23:27 meins 2 add_filter: aac filter 2: compress +Dec 08 19:23:27 meins 2 clear_slot: clearing slot 0 +Dec 08 19:23:27 meins 2 clear_slot: clearing slot 1 +Dec 08 19:23:27 meins 2 clear_slot: clearing slot 2 +Dec 08 19:23:27 meins 2 clear_slot: clearing slot 3 +Dec 08 19:23:27 meins 2 clear_slot: clearing slot 4 +Dec 08 19:23:27 meins 2 init_grabbing: grab init +Dec 08 19:23:27 meins 2 setup_signal_handling: signal pipe: fd 4 +Dec 08 19:23:27 meins 3 audiod_get_socket: local socket: /var/paraslash/audiod_socket.meins +Dec 08 19:23:27 meins 2 daemon_init: daemonizing +Dec 08 19:23:27 meins 2 init_sched: initializing scheduler +Dec 08 19:23:27 meins 2 register_task: registering signal task (0x80631bc) +Dec 08 19:23:27 meins 2 register_task: registering command task (0xbfec3ec4) +Dec 08 19:23:27 meins 2 register_task: registering status task (0x80632e8) +Dec 08 19:23:27 meins 2 register_task: registering audiod task (0xbfec3fe8) +Dec 08 19:23:27 meins 2 status_pre_select: clock diff count: 5 +Dec 08 19:23:27 meins 2 client_open: loglevel: 5 +Dec 08 19:23:27 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:23:27 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:23:27 meins 3 client_open: connecting localhost:2990 +Dec 08 19:23:27 meins 2 register_task: registering client (0x8067878) +Dec 08 19:23:27 meins 2 client_post_select: --> auth rc4 maan +Dec 08 19:23:27 meins 2 client_post_select: <-- [challenge] +Dec 08 19:23:27 meins 2 client_post_select: --> 1499570993 +Dec 08 19:23:27 meins 2 client_post_select: decrypting session key +Dec 08 19:23:27 meins 2 enable_crypt: rc4 encryption activated for fd 7 +Dec 08 19:23:27 meins 2 unregister_task: unregistering client (0x8067878) +Dec 08 19:23:28 meins 2 handle_connect: connection from user 409, buf: stat +Dec 08 19:23:28 meins 2 handle_connect: argv[0]: stat, argc= 1 +Dec 08 19:23:28 meins 2 com_stat: mask: 0xffffffff +Dec 08 19:23:28 meins 2 stat_client_add: adding client on fd 7 +Dec 08 19:23:28 meins 2 dump_stat_client_list: stat client on fd 7 +Dec 08 19:23:28 meins 2 status_pre_select: clock diff count: 4 +Dec 08 19:23:28 meins 2 client_open: loglevel: 5 +Dec 08 19:23:28 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:23:28 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:23:28 meins 3 client_open: connecting localhost:2990 +Dec 08 19:23:28 meins 2 register_task: registering client (0x8067878) +Dec 08 19:23:28 meins 2 client_post_select: --> auth rc4 maan +Dec 08 19:23:28 meins 2 client_post_select: <-- [challenge] +Dec 08 19:23:28 meins 2 client_post_select: --> 1400991892 +Dec 08 19:23:28 meins 2 client_post_select: decrypting session key +Dec 08 19:23:28 meins 2 enable_crypt: rc4 encryption activated for fd 8 +Dec 08 19:23:28 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms +Dec 08 19:23:28 meins 2 unregister_task: unregistering client (0x8067878) +Dec 08 19:23:29 meins 2 status_pre_select: clock diff count: 3 +Dec 08 19:23:29 meins 2 client_open: loglevel: 5 +Dec 08 19:23:29 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:23:29 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:23:29 meins 3 client_open: connecting localhost:2990 +Dec 08 19:23:29 meins 2 register_task: registering client (0x8067878) +Dec 08 19:23:29 meins 2 client_post_select: --> auth rc4 maan +Dec 08 19:23:29 meins 2 client_post_select: <-- [challenge] +Dec 08 19:23:29 meins 2 client_post_select: --> 1772412478 +Dec 08 19:23:29 meins 2 client_post_select: decrypting session key +Dec 08 19:23:29 meins 2 enable_crypt: rc4 encryption activated for fd 8 +Dec 08 19:23:29 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms +Dec 08 19:23:29 meins 2 unregister_task: unregistering client (0x8067878) +Dec 08 19:23:30 meins 2 status_pre_select: clock diff count: 2 +Dec 08 19:23:30 meins 2 client_open: loglevel: 5 +Dec 08 19:23:30 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:23:30 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:23:30 meins 3 client_open: connecting localhost:2990 +Dec 08 19:23:30 meins 2 register_task: registering client (0x8067878) +Dec 08 19:23:30 meins 2 client_post_select: --> auth rc4 maan +Dec 08 19:23:30 meins 2 client_post_select: <-- [challenge] +Dec 08 19:23:30 meins 2 client_post_select: --> 1882631692 +Dec 08 19:23:30 meins 2 client_post_select: decrypting session key +Dec 08 19:23:30 meins 2 enable_crypt: rc4 encryption activated for fd 8 +Dec 08 19:23:30 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms +Dec 08 19:23:30 meins 2 unregister_task: unregistering client (0x8067878) +Dec 08 19:23:31 meins 2 status_pre_select: clock diff count: 1 +Dec 08 19:23:31 meins 2 client_open: loglevel: 5 +Dec 08 19:23:31 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:23:31 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:23:31 meins 3 client_open: connecting localhost:2990 +Dec 08 19:23:31 meins 2 register_task: registering client (0x8067878) +Dec 08 19:23:31 meins 2 client_post_select: --> auth rc4 maan +Dec 08 19:23:31 meins 2 client_post_select: <-- [challenge] +Dec 08 19:23:31 meins 2 client_post_select: --> 1630455651 +Dec 08 19:23:31 meins 2 client_post_select: decrypting session key +Dec 08 19:23:31 meins 2 enable_crypt: rc4 encryption activated for fd 8 +Dec 08 19:23:31 meins 2 compute_time_diff: time diff (cur/avg): -1ms/+1ms +Dec 08 19:23:31 meins 2 unregister_task: unregistering client (0x8067878) +Dec 08 19:23:36 meins 2 client_open: loglevel: 5 +Dec 08 19:23:36 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:23:36 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:23:36 meins 3 client_open: connecting localhost:2990 +Dec 08 19:23:36 meins 2 register_task: registering client (0x8067878) +Dec 08 19:23:36 meins 2 client_post_select: --> auth rc4 maan +Dec 08 19:23:36 meins 2 client_post_select: <-- [challenge] +Dec 08 19:23:36 meins 2 client_post_select: --> 759215314 +Dec 08 19:23:36 meins 2 client_post_select: decrypting session key +Dec 08 19:23:36 meins 2 enable_crypt: rc4 encryption activated for fd 8 +Dec 08 19:23:36 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:23:36 meins 2 clear_slot: clearing slot 0 +Dec 08 19:23:36 meins 3 open_receiver: started ogg: dccp receiver in slot 0 +Dec 08 19:23:36 meins 2 register_task: registering dccp receiver node (0x806a324) +Dec 08 19:23:37 meins 2 open_filters: opening ogg filters +Dec 08 19:23:37 meins 3 open_filters: ogg filter 1/2 (oggdec) started in slot 0 +Dec 08 19:23:37 meins 3 open_filters: ogg filter 2/2 (compress) started in slot 0 +Dec 08 19:23:37 meins 2 register_task: registering filter chain (0x807460c) +Dec 08 19:23:37 meins 3 ogg_convert: input buffer: 17032, opening ov callbacks +Dec 08 19:23:37 meins 3 ogg_convert: 2 channels, 44100 Hz +Dec 08 19:23:37 meins 2 open_writers: opening ogg writers +Dec 08 19:23:37 meins 2 open_writers: samplerate: 44100 +Dec 08 19:23:37 meins 3 wng_open: opening wng 0x80a5660 with 1 writer(s) +Dec 08 19:23:37 meins 2 register_task: registering (0x80a5684) +Dec 08 19:23:37 meins 2 alsa_open: 2 channel(s), 44100Hz +Dec 08 19:23:37 meins 2 alsa_open: buffer time: 170658 +Dec 08 19:23:37 meins 2 alsa_open: buffer size: 7526, period_size: 940 +Dec 08 19:23:43 meins 3 rn_event_handler: dccp_recv: end of file +Dec 08 19:23:43 meins 2 unregister_task: unregistering dccp receiver node (0x806a324) +Dec 08 19:23:43 meins 2 unregister_task: unregistering client (0x8067878) +Dec 08 19:23:43 meins 2 kill_all_decoders: unregistering writer node group in slot 0 +Dec 08 19:23:43 meins 2 unregister_task: unregistering writer node group (0x80a5684) +Dec 08 19:23:43 meins 2 kill_all_decoders: unregistering filter chain in slot 0 +Dec 08 19:23:43 meins 2 unregister_task: unregistering filter chain (0x807460c) +Dec 08 19:23:43 meins 2 try_to_close_slot: closing slot 0 +Dec 08 19:23:43 meins 3 wng_close: closing wng with 1 writer(s) +Dec 08 19:23:43 meins 2 alsa_close: closing writer node 0x80a57b0 +Dec 08 19:23:43 meins 3 close_filters: closing filter chain 0x80745e0 +Dec 08 19:23:43 meins 2 close_filters: closing oggdec filter +Dec 08 19:23:43 meins 2 close_filters: closing compress filter +Dec 08 19:23:43 meins 3 close_receiver: closing ogg receiver in slot 0 (eof = 1) +Dec 08 19:23:43 meins 2 clear_slot: clearing slot 0 +Dec 08 19:23:48 meins 2 client_open: loglevel: 5 +Dec 08 19:23:48 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:23:48 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:23:48 meins 3 client_open: connecting localhost:2990 +Dec 08 19:23:48 meins 2 register_task: registering client (0x8067878) +Dec 08 19:23:48 meins 2 client_post_select: --> auth rc4 maan +Dec 08 19:23:48 meins 2 client_post_select: <-- [challenge] +Dec 08 19:23:48 meins 2 client_post_select: --> 960122352 +Dec 08 19:23:48 meins 2 client_post_select: decrypting session key +Dec 08 19:23:48 meins 2 enable_crypt: rc4 encryption activated for fd 8 +Dec 08 19:23:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:23:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:23:58 meins 2 clear_slot: clearing slot 0 +Dec 08 19:23:58 meins 3 open_receiver: started mp3: http receiver in slot 0 +Dec 08 19:23:58 meins 2 register_task: registering http receiver node (0x806a204) +Dec 08 19:23:58 meins 2 http_recv_post_select: sending http request +Dec 08 19:23:58 meins 2 http_recv_post_select: received ok msg, streaming +Dec 08 19:23:59 meins 2 open_filters: opening mp3 filters +Dec 08 19:23:59 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0 +Dec 08 19:23:59 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0 +Dec 08 19:23:59 meins 2 register_task: registering filter chain (0x807260c) +Dec 08 19:23:59 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 173 ms left +Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 147 ms left +Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 121 ms left +Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 95 ms left +Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 69 ms left +Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 43 ms left +Dec 08 19:23:59 meins 2 audiod_pre_select: initial delay: 17 ms left +Dec 08 19:23:59 meins 2 open_writers: opening mp3 writers +Dec 08 19:23:59 meins 2 open_writers: samplerate: 44100 +Dec 08 19:23:59 meins 3 wng_open: opening wng 0x80a3c18 with 1 writer(s) +Dec 08 19:23:59 meins 2 register_task: registering (0x80a3c3c) +Dec 08 19:23:59 meins 2 alsa_open: 2 channel(s), 44100Hz +Dec 08 19:23:59 meins 2 alsa_open: buffer time: 170658 +Dec 08 19:23:59 meins 2 alsa_open: buffer size: 7526, period_size: 940 +Dec 08 19:24:43 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:24:57 meins 4 alsa_write_post_select: EAGAIN +Dec 08 19:24:57 meins 4 alsa_write_post_select: EAGAIN +Dec 08 19:25:33 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:25:43 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:25:51 meins 3 rn_event_handler: http_recv: end of file +Dec 08 19:25:51 meins 2 unregister_task: unregistering http receiver node (0x806a204) +Dec 08 19:25:51 meins 2 unregister_task: unregistering client (0x8067878) +Dec 08 19:25:51 meins 2 kill_all_decoders: unregistering writer node group in slot 0 +Dec 08 19:25:51 meins 2 unregister_task: unregistering writer node group (0x80a3c3c) +Dec 08 19:25:51 meins 2 kill_all_decoders: unregistering filter chain in slot 0 +Dec 08 19:25:51 meins 2 unregister_task: unregistering filter chain (0x807260c) +Dec 08 19:25:51 meins 2 try_to_close_slot: closing slot 0 +Dec 08 19:25:51 meins 3 wng_close: closing wng with 1 writer(s) +Dec 08 19:25:51 meins 2 alsa_close: closing writer node 0x806a440 +Dec 08 19:25:51 meins 3 close_filters: closing filter chain 0x80725e0 +Dec 08 19:25:51 meins 2 close_filters: closing mp3dec filter +Dec 08 19:25:51 meins 2 close_filters: closing compress filter +Dec 08 19:25:51 meins 3 close_receiver: closing mp3 receiver in slot 0 (eof = 1) +Dec 08 19:25:51 meins 2 clear_slot: clearing slot 0 +Dec 08 19:25:56 meins 2 client_open: loglevel: 5 +Dec 08 19:25:56 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:25:56 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:25:56 meins 3 client_open: connecting localhost:2990 +Dec 08 19:25:56 meins 2 register_task: registering client (0x8067878) +Dec 08 19:25:56 meins 2 client_post_select: --> auth rc4 maan +Dec 08 19:25:56 meins 2 client_post_select: <-- [challenge] +Dec 08 19:25:56 meins 2 client_post_select: --> 563263924 +Dec 08 19:25:56 meins 2 client_post_select: decrypting session key +Dec 08 19:25:56 meins 2 enable_crypt: rc4 encryption activated for fd 8 +Dec 08 19:25:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:26:06 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:26:06 meins 2 clear_slot: clearing slot 0 +Dec 08 19:26:06 meins 3 open_receiver: started mp3: http receiver in slot 0 +Dec 08 19:26:06 meins 2 register_task: registering http receiver node (0x806a29c) +Dec 08 19:26:06 meins 2 http_recv_post_select: sending http request +Dec 08 19:26:06 meins 2 http_recv_post_select: received ok msg, streaming +Dec 08 19:26:06 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:26:06 meins 2 open_filters: opening mp3 filters +Dec 08 19:26:06 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0 +Dec 08 19:26:06 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0 +Dec 08 19:26:06 meins 2 register_task: registering filter chain (0x80726dc) +Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 173 ms left +Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 147 ms left +Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 121 ms left +Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 95 ms left +Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 69 ms left +Dec 08 19:26:06 meins 2 audiod_pre_select: initial delay: 43 ms left +Dec 08 19:26:07 meins 2 audiod_pre_select: initial delay: 17 ms left +Dec 08 19:26:07 meins 2 open_writers: opening mp3 writers +Dec 08 19:26:07 meins 2 open_writers: samplerate: 44100 +Dec 08 19:26:07 meins 3 wng_open: opening wng 0x80a3cb8 with 1 writer(s) +Dec 08 19:26:07 meins 2 register_task: registering (0x80a3cdc) +Dec 08 19:26:07 meins 2 alsa_open: 2 channel(s), 44100Hz +Dec 08 19:26:07 meins 2 alsa_open: buffer time: 170658 +Dec 08 19:26:07 meins 2 alsa_open: buffer size: 7526, period_size: 940 +Dec 08 19:26:51 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:26:58 meins 2 unregister_task: unregistering client (0x8067878) +Dec 08 19:26:58 meins 2 kill_all_decoders: unregistering writer node group in slot 0 +Dec 08 19:26:58 meins 2 unregister_task: unregistering writer node group (0x80a3cdc) +Dec 08 19:26:58 meins 2 kill_all_decoders: unregistering filter chain in slot 0 +Dec 08 19:26:58 meins 2 unregister_task: unregistering filter chain (0x80726dc) +Dec 08 19:26:58 meins 2 kill_all_decoders: unregistering receiver_node in slot 0 +Dec 08 19:26:58 meins 2 unregister_task: unregistering http receiver node (0x806a29c) +Dec 08 19:26:58 meins 2 try_to_close_slot: closing slot 0 +Dec 08 19:26:58 meins 3 wng_close: closing wng with 1 writer(s) +Dec 08 19:26:58 meins 2 alsa_close: closing writer node 0x80a3e08 +Dec 08 19:26:58 meins 3 close_filters: closing filter chain 0x80726b0 +Dec 08 19:26:58 meins 2 close_filters: closing mp3dec filter +Dec 08 19:26:58 meins 2 close_filters: closing compress filter +Dec 08 19:26:58 meins 3 close_receiver: closing mp3 receiver in slot 0 (eof = 1) +Dec 08 19:26:58 meins 2 clear_slot: clearing slot 0 +Dec 08 19:27:04 meins 2 client_open: loglevel: 5 +Dec 08 19:27:04 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:27:04 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:27:04 meins 3 client_open: connecting localhost:2990 +Dec 08 19:27:04 meins 5 makesock: can not create TCP socket localhost#2990. +Dec 08 19:27:04 meins 5 client_open: Connection refused +Dec 08 19:27:09 meins 2 client_open: loglevel: 5 +Dec 08 19:27:09 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:27:09 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:27:09 meins 3 client_open: connecting localhost:2990 +Dec 08 19:27:09 meins 5 makesock: can not create TCP socket localhost#2990. +Dec 08 19:27:09 meins 5 client_open: Connection refused +Dec 08 19:27:14 meins 2 client_open: loglevel: 5 +Dec 08 19:27:14 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:27:14 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:27:14 meins 3 client_open: connecting localhost:2990 +Dec 08 19:27:14 meins 5 makesock: can not create TCP socket localhost#2990. +Dec 08 19:27:14 meins 5 client_open: Connection refused +Dec 08 19:27:19 meins 2 client_open: loglevel: 5 +Dec 08 19:27:19 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:27:19 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:27:19 meins 3 client_open: connecting localhost:2990 +Dec 08 19:27:19 meins 5 makesock: can not create TCP socket localhost#2990. +Dec 08 19:27:19 meins 5 client_open: Connection refused +Dec 08 19:27:24 meins 2 client_open: loglevel: 5 +Dec 08 19:27:24 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:27:24 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:27:24 meins 3 client_open: connecting localhost:2990 +Dec 08 19:27:24 meins 5 makesock: can not create TCP socket localhost#2990. +Dec 08 19:27:24 meins 5 client_open: Connection refused +Dec 08 19:27:29 meins 2 client_open: loglevel: 5 +Dec 08 19:27:29 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:27:29 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:27:29 meins 3 client_open: connecting localhost:2990 +Dec 08 19:27:29 meins 5 makesock: can not create TCP socket localhost#2990. +Dec 08 19:27:29 meins 5 client_open: Connection refused +Dec 08 19:27:34 meins 2 client_open: loglevel: 5 +Dec 08 19:27:34 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:27:34 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:27:34 meins 3 client_open: connecting localhost:2990 +Dec 08 19:27:34 meins 2 register_task: registering client (0x8067878) +Dec 08 19:27:34 meins 2 client_post_select: --> auth rc4 maan +Dec 08 19:27:34 meins 2 client_post_select: <-- [challenge] +Dec 08 19:27:34 meins 2 client_post_select: --> 1725057215 +Dec 08 19:27:34 meins 2 client_post_select: decrypting session key +Dec 08 19:27:34 meins 2 enable_crypt: rc4 encryption activated for fd 8 +Dec 08 19:27:34 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:27:45 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:27:45 meins 2 clear_slot: clearing slot 0 +Dec 08 19:27:45 meins 3 open_receiver: started mp3: http receiver in slot 0 +Dec 08 19:27:45 meins 2 register_task: registering http receiver node (0x806a7b4) +Dec 08 19:27:45 meins 2 http_recv_post_select: sending http request +Dec 08 19:27:45 meins 2 http_recv_post_select: received ok msg, streaming +Dec 08 19:27:46 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:27:46 meins 2 open_filters: opening mp3 filters +Dec 08 19:27:46 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0 +Dec 08 19:27:46 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0 +Dec 08 19:27:46 meins 2 register_task: registering filter chain (0x80729f4) +Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 173 ms left +Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 147 ms left +Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 121 ms left +Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 95 ms left +Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 69 ms left +Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 43 ms left +Dec 08 19:27:46 meins 2 audiod_pre_select: initial delay: 17 ms left +Dec 08 19:27:46 meins 2 open_writers: opening mp3 writers +Dec 08 19:27:46 meins 2 open_writers: samplerate: 44100 +Dec 08 19:27:46 meins 3 wng_open: opening wng 0x80a4000 with 1 writer(s) +Dec 08 19:27:46 meins 2 register_task: registering (0x80a4024) +Dec 08 19:27:46 meins 2 alsa_open: 2 channel(s), 44100Hz +Dec 08 19:27:46 meins 2 alsa_open: buffer time: 170658 +Dec 08 19:27:46 meins 2 alsa_open: buffer size: 7526, period_size: 940 +Dec 08 19:28:30 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:28:58 meins 3 rn_event_handler: http_recv: end of file +Dec 08 19:28:58 meins 2 unregister_task: unregistering http receiver node (0x806a7b4) +Dec 08 19:28:58 meins 2 unregister_task: unregistering client (0x8067878) +Dec 08 19:28:58 meins 2 kill_all_decoders: unregistering writer node group in slot 0 +Dec 08 19:28:58 meins 2 unregister_task: unregistering writer node group (0x80a4024) +Dec 08 19:28:58 meins 2 kill_all_decoders: unregistering filter chain in slot 0 +Dec 08 19:28:58 meins 2 unregister_task: unregistering filter chain (0x80729f4) +Dec 08 19:28:58 meins 2 try_to_close_slot: closing slot 0 +Dec 08 19:28:58 meins 3 wng_close: closing wng with 1 writer(s) +Dec 08 19:28:58 meins 2 alsa_close: closing writer node 0x80a4150 +Dec 08 19:28:58 meins 3 close_filters: closing filter chain 0x80729c8 +Dec 08 19:28:58 meins 2 close_filters: closing mp3dec filter +Dec 08 19:28:58 meins 2 close_filters: closing compress filter +Dec 08 19:28:58 meins 3 close_receiver: closing mp3 receiver in slot 0 (eof = 1) +Dec 08 19:28:58 meins 2 clear_slot: clearing slot 0 +Dec 08 19:29:03 meins 2 client_open: loglevel: 5 +Dec 08 19:29:03 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:29:03 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:29:03 meins 3 client_open: connecting localhost:2990 +Dec 08 19:29:03 meins 2 register_task: registering client (0x8067878) +Dec 08 19:29:03 meins 2 client_post_select: --> auth rc4 maan +Dec 08 19:29:03 meins 2 client_post_select: <-- [challenge] +Dec 08 19:29:03 meins 2 client_post_select: --> 705434988 +Dec 08 19:29:03 meins 2 client_post_select: decrypting session key +Dec 08 19:29:03 meins 2 enable_crypt: rc4 encryption activated for fd 8 +Dec 08 19:29:03 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:29:13 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:29:13 meins 2 clear_slot: clearing slot 0 +Dec 08 19:29:13 meins 3 open_receiver: started ogg: dccp receiver in slot 0 +Dec 08 19:29:13 meins 2 register_task: registering dccp receiver node (0x806a804) +Dec 08 19:29:13 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:29:13 meins 2 open_filters: opening ogg filters +Dec 08 19:29:13 meins 3 open_filters: ogg filter 1/2 (oggdec) started in slot 0 +Dec 08 19:29:13 meins 3 open_filters: ogg filter 2/2 (compress) started in slot 0 +Dec 08 19:29:13 meins 2 register_task: registering filter chain (0x8074a44) +Dec 08 19:29:14 meins 3 ogg_convert: input buffer: 17167, opening ov callbacks +Dec 08 19:29:14 meins 3 ogg_convert: 2 channels, 44100 Hz +Dec 08 19:29:14 meins 2 open_writers: opening ogg writers +Dec 08 19:29:14 meins 2 open_writers: samplerate: 44100 +Dec 08 19:29:14 meins 3 wng_open: opening wng 0x80c8d20 with 1 writer(s) +Dec 08 19:29:14 meins 2 register_task: registering (0x80c8d44) +Dec 08 19:29:14 meins 2 alsa_open: 2 channel(s), 44100Hz +Dec 08 19:29:14 meins 2 alsa_open: buffer time: 170658 +Dec 08 19:29:14 meins 2 alsa_open: buffer size: 7526, period_size: 940 +Dec 08 19:29:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:30:48 meins 2 compute_time_diff: time diff (cur/avg): -3ms/+1ms +Dec 08 19:30:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:31:48 meins 2 compute_time_diff: time diff (cur/avg): -2ms/+1ms +Dec 08 19:31:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:32:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:32:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:33:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:33:56 meins 3 rn_event_handler: dccp_recv: end of file +Dec 08 19:33:56 meins 2 unregister_task: unregistering dccp receiver node (0x806a804) +Dec 08 19:33:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:33:56 meins 3 filter_event_handler: filter chain: eof +Dec 08 19:33:56 meins 2 unregister_task: unregistering filter chain (0x8074a44) +Dec 08 19:33:56 meins 2 wng_event_handler: wng: end of file +Dec 08 19:33:56 meins 2 unregister_task: unregistering writer node group (0x80c8d44) +Dec 08 19:33:56 meins 2 try_to_close_slot: closing slot 0 +Dec 08 19:33:56 meins 3 wng_close: closing wng with 1 writer(s) +Dec 08 19:33:56 meins 2 alsa_close: closing writer node 0x80c8e70 +Dec 08 19:33:56 meins 3 close_filters: closing filter chain 0x8074a18 +Dec 08 19:33:56 meins 2 close_filters: closing oggdec filter +Dec 08 19:33:56 meins 2 close_filters: closing compress filter +Dec 08 19:33:56 meins 3 close_receiver: closing ogg receiver in slot 0 (eof = 1) +Dec 08 19:33:56 meins 2 clear_slot: clearing slot 0 +Dec 08 19:33:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:33:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:33:58 meins 2 clear_slot: clearing slot 0 +Dec 08 19:33:58 meins 3 open_receiver: started ogg: dccp receiver in slot 0 +Dec 08 19:33:58 meins 2 register_task: registering dccp receiver node (0x80d0dc4) +Dec 08 19:33:58 meins 2 open_filters: opening ogg filters +Dec 08 19:33:58 meins 3 open_filters: ogg filter 1/2 (oggdec) started in slot 0 +Dec 08 19:33:58 meins 3 open_filters: ogg filter 2/2 (compress) started in slot 0 +Dec 08 19:33:58 meins 2 register_task: registering filter chain (0x807481c) +Dec 08 19:33:58 meins 2 compute_time_diff: time diff (cur/avg): -3ms/+1ms +Dec 08 19:33:58 meins 3 ogg_convert: input buffer: 16947, opening ov callbacks +Dec 08 19:33:58 meins 3 ogg_convert: 2 channels, 44100 Hz +Dec 08 19:33:58 meins 2 open_writers: opening ogg writers +Dec 08 19:33:58 meins 2 open_writers: samplerate: 44100 +Dec 08 19:33:58 meins 3 wng_open: opening wng 0x809d860 with 1 writer(s) +Dec 08 19:33:58 meins 2 register_task: registering (0x809d884) +Dec 08 19:33:58 meins 2 alsa_open: 2 channel(s), 44100Hz +Dec 08 19:33:58 meins 2 alsa_open: buffer time: 170658 +Dec 08 19:33:58 meins 2 alsa_open: buffer size: 7526, period_size: 940 +Dec 08 19:34:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:34:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:35:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:35:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:36:48 meins 2 compute_time_diff: time diff (cur/avg): -3ms/+1ms +Dec 08 19:36:54 meins 3 rn_event_handler: dccp_recv: end of file +Dec 08 19:36:54 meins 2 unregister_task: unregistering dccp receiver node (0x80d0dc4) +Dec 08 19:36:54 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:36:54 meins 3 filter_event_handler: filter chain: eof +Dec 08 19:36:54 meins 2 unregister_task: unregistering filter chain (0x807481c) +Dec 08 19:36:54 meins 2 wng_event_handler: wng: end of file +Dec 08 19:36:54 meins 2 unregister_task: unregistering writer node group (0x809d884) +Dec 08 19:36:54 meins 2 try_to_close_slot: closing slot 0 +Dec 08 19:36:54 meins 3 wng_close: closing wng with 1 writer(s) +Dec 08 19:36:54 meins 2 alsa_close: closing writer node 0x809d9b0 +Dec 08 19:36:54 meins 3 close_filters: closing filter chain 0x80747f0 +Dec 08 19:36:54 meins 2 close_filters: closing oggdec filter +Dec 08 19:36:54 meins 2 close_filters: closing compress filter +Dec 08 19:36:54 meins 3 close_receiver: closing ogg receiver in slot 0 (eof = 1) +Dec 08 19:36:54 meins 2 clear_slot: clearing slot 0 +Dec 08 19:36:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:36:56 meins 2 clear_slot: clearing slot 0 +Dec 08 19:36:56 meins 3 open_receiver: started mp3: http receiver in slot 0 +Dec 08 19:36:56 meins 2 register_task: registering http receiver node (0x80d14ec) +Dec 08 19:36:56 meins 2 http_recv_post_select: sending http request +Dec 08 19:36:56 meins 2 http_recv_post_select: received ok msg, streaming +Dec 08 19:36:56 meins 2 open_filters: opening mp3 filters +Dec 08 19:36:56 meins 3 open_filters: mp3 filter 1/2 (mp3dec) started in slot 0 +Dec 08 19:36:56 meins 3 open_filters: mp3 filter 2/2 (compress) started in slot 0 +Dec 08 19:36:56 meins 2 register_task: registering filter chain (0x80d16ac) +Dec 08 19:36:56 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 174 ms left +Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 148 ms left +Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 122 ms left +Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 96 ms left +Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 70 ms left +Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 44 ms left +Dec 08 19:36:56 meins 2 audiod_pre_select: initial delay: 18 ms left +Dec 08 19:36:56 meins 2 open_writers: opening mp3 writers +Dec 08 19:36:56 meins 2 open_writers: samplerate: 44100 +Dec 08 19:36:56 meins 3 wng_open: opening wng 0x80743a0 with 1 writer(s) +Dec 08 19:36:56 meins 2 register_task: registering (0x80743c4) +Dec 08 19:36:56 meins 2 alsa_open: 2 channel(s), 44100Hz +Dec 08 19:36:56 meins 2 alsa_open: buffer time: 170658 +Dec 08 19:36:56 meins 2 alsa_open: buffer size: 7526, period_size: 940 +Dec 08 19:36:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:37:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+1ms +Dec 08 19:37:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:38:48 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:38:58 meins 2 compute_time_diff: time diff (cur/avg): -0ms/+0ms +Dec 08 19:39:06 meins 7 signal_event_handler: terminating on signal 15 +Dec 08 19:39:06 meins 7 clean_exit: caught deadly signal +Dec 08 19:39:06 meins 2 log_welcome: welcome to para_audiod git (Sat Dec 8 13:42:52 MET 2007) +Dec 08 19:39:06 meins 1 log_welcome: using loglevel 1 +Dec 08 19:39:06 meins 2 init_writers: maximal number of writers: 3 +Dec 08 19:39:06 meins 2 check_writer_arg: checking alsa -d plug:swmix +Dec 08 19:39:06 meins 2 alsa_parse_config: options: -d plug:swmix, 2 +Dec 08 19:39:06 meins 2 alsa_parse_config: help given: 0 +Dec 08 19:39:06 meins 2 init_writers: mp3 writer #0: alsa +Dec 08 19:39:06 meins 2 check_writer_arg: checking alsa -d plug:swmix +Dec 08 19:39:06 meins 2 alsa_parse_config: options: -d plug:swmix, 2 +Dec 08 19:39:06 meins 2 alsa_parse_config: help given: 0 +Dec 08 19:39:06 meins 2 init_writers: ogg writer #0: alsa +Dec 08 19:39:06 meins 2 check_writer_arg: checking alsa -d plug:swmix +Dec 08 19:39:06 meins 2 alsa_parse_config: options: -d plug:swmix, 2 +Dec 08 19:39:06 meins 2 alsa_parse_config: help given: 0 +Dec 08 19:39:06 meins 2 init_writers: aac writer #0: alsa +Dec 08 19:39:06 meins 2 init_receivers: initializing http receiver +Dec 08 19:39:06 meins 2 init_receivers: initializing dccp receiver +Dec 08 19:39:06 meins 2 init_receivers: initializing ortp receiver +Dec 08 19:39:06 meins 1 check_receiver_arg: checking http -i 127.0.0.1 +Dec 08 19:39:06 meins 1 parse_receiver_args: options: -i 127.0.0.1 +Dec 08 19:39:06 meins 1 parse_receiver_args: argc = 3, argv[0]: http +Dec 08 19:39:06 meins 1 check_receiver_arg: checking dccp -i localhost +Dec 08 19:39:06 meins 1 parse_receiver_args: options: -i localhost +Dec 08 19:39:06 meins 1 parse_receiver_args: argc = 3, argv[0]: dccp +Dec 08 19:39:06 meins 1 check_receiver_arg: checking dccp -i localhost +Dec 08 19:39:06 meins 1 parse_receiver_args: options: -i localhost +Dec 08 19:39:06 meins 1 parse_receiver_args: argc = 3, argv[0]: dccp +Dec 08 19:39:06 meins 2 init_filters: maximal number of filters: 6 +Dec 08 19:39:06 meins 2 add_filter: mp3 filter 1: mp3dec +Dec 08 19:39:06 meins 2 add_filter: mp3 filter 2: compress +Dec 08 19:39:06 meins 2 add_filter: ogg filter 1: oggdec +Dec 08 19:39:06 meins 2 add_filter: ogg filter 2: compress +Dec 08 19:39:06 meins 2 add_filter: aac filter 1: aacdec +Dec 08 19:39:06 meins 2 add_filter: aac filter 2: compress +Dec 08 19:39:06 meins 2 clear_slot: clearing slot 0 +Dec 08 19:39:06 meins 2 clear_slot: clearing slot 1 +Dec 08 19:39:06 meins 2 clear_slot: clearing slot 2 +Dec 08 19:39:06 meins 2 clear_slot: clearing slot 3 +Dec 08 19:39:06 meins 2 clear_slot: clearing slot 4 +Dec 08 19:39:06 meins 2 init_grabbing: grab init +Dec 08 19:39:06 meins 2 setup_signal_handling: signal pipe: fd 4 +Dec 08 19:39:06 meins 1 para_install_sighandler: catching signal 2 +Dec 08 19:39:06 meins 1 para_install_sighandler: catching signal 15 +Dec 08 19:39:06 meins 1 para_install_sighandler: catching signal 1 +Dec 08 19:39:06 meins 3 audiod_get_socket: local socket: /var/paraslash/audiod_socket.meins +Dec 08 19:39:06 meins 2 daemon_init: daemonizing +Dec 08 19:39:06 meins 2 init_sched: initializing scheduler +Dec 08 19:39:06 meins 2 register_task: registering signal task (0x80631bc) +Dec 08 19:39:06 meins 1 register_task: pre_select: 0x80631c0 +Dec 08 19:39:06 meins 1 register_task: post_select: 0x80631c0 +Dec 08 19:39:06 meins 2 register_task: registering command task (0xbfa7e274) +Dec 08 19:39:06 meins 1 register_task: pre_select: 0xbfa7e278 +Dec 08 19:39:06 meins 1 register_task: post_select: 0xbfa7e278 +Dec 08 19:39:06 meins 2 register_task: registering status task (0x80632e8) +Dec 08 19:39:06 meins 1 register_task: pre_select: 0x80632ec +Dec 08 19:39:06 meins 1 register_task: post_select: 0x80632ec +Dec 08 19:39:06 meins 2 register_task: registering audiod task (0xbfa7e398) +Dec 08 19:39:06 meins 1 register_task: pre_select: 0xbfa7e39c +Dec 08 19:39:06 meins 1 register_task: post_select: 0xbfa7e39c +Dec 08 19:39:06 meins 2 status_pre_select: clock diff count: 5 +Dec 08 19:39:06 meins 2 client_open: loglevel: 5 +Dec 08 19:39:06 meins 2 client_open: config_file: /home/maan/.paraslash/client.conf +Dec 08 19:39:06 meins 2 client_open: key_file: /home/maan/.paraslash/key.maan +Dec 08 19:39:06 meins 3 client_open: connecting localhost:2990 +Dec 08 19:39:06 meins 2 register_task: registering client (0x8067878) +Dec 08 19:39:06 meins 1 register_task: pre_select: 0x806787c +Dec 08 19:39:06 meins 1 register_task: post_select: 0x806787c +Dec 08 19:39:06 meins 2 client_post_select: --> auth rc4 maan +Dec 08 19:39:06 meins 2 client_post_select: <-- [challenge] +Dec 08 19:39:06 meins 2 client_post_select: --> 153889019 +Dec 08 19:39:06 meins 1 client_post_select: ++++ server info ++++ + +Proceed. + +++++ end of server info ++++ +Dec 08 19:39:06 meins 2 client_post_select: decrypting session key +Dec 08 19:39:06 meins 2 enable_crypt: rc4 encryption activated for fd 7 +Dec 08 19:39:06 meins 1 client_post_select: --> +stat +1 +End of Command. diff --git a/web/screenshots/gui.png b/web/screenshots/gui.png new file mode 100644 index 00000000..2a6c7443 Binary files /dev/null and b/web/screenshots/gui.png differ diff --git a/web/screenshots/server.log b/web/screenshots/server.log new file mode 100644 index 00000000..f84ede81 --- /dev/null +++ b/web/screenshots/server.log @@ -0,0 +1,192 @@ +Dec 08 19:28:58 2: (21285) log_welcome: welcome to para_server git (Sat Dec 8 13:42:52 MET 2007) +Dec 08 19:28:58 1: (21285) log_welcome: using loglevel 1 +Dec 08 19:28:58 1: (21285) populate_user_list: found entry for maan +Dec 08 19:28:58 1: (21285) populate_user_list: found 4 perm entries +Dec 08 19:28:58 1: (21285) populate_user_list: found entry for install +Dec 08 19:28:58 1: (21285) populate_user_list: found 4 perm entries +Dec 08 19:28:58 1: (21285) populate_user_list: found entry for www +Dec 08 19:28:58 1: (21285) populate_user_list: found 4 perm entries +Dec 08 19:28:58 2: (21285) daemon_init: daemonizing +Dec 08 19:28:58 3: (21286) server_init: initializing audio format handlers +Dec 08 19:28:58 2: (21286) afh_init: supported audio formats: mp3 ogg aac +Dec 08 19:28:58 3: (21286) afh_init: initializing mp3 handler +Dec 08 19:28:58 3: (21286) afh_init: initializing ogg handler +Dec 08 19:28:58 3: (21286) afh_init: initializing aac handler +Dec 08 19:28:58 3: (21286) server_init: initializing virtual streaming system +Dec 08 19:28:58 2: (21286) vss_init: announce timeval: 300ms +Dec 08 19:28:58 3: (21286) vss_init: initializing http sender +Dec 08 19:28:58 2: (21286) para_listen: listening on TCP port 8000, fd 4 +Dec 08 19:28:58 1: (21286) http_send_init: http sender init complete +Dec 08 19:28:58 3: (21286) vss_init: initializing dccp sender +Dec 08 19:28:58 2: (21286) para_listen: listening on DCCP port 5001, fd 5 +Dec 08 19:28:58 3: (21286) vss_init: initializing ortp sender +Dec 08 19:28:58 1: (21286) ortp_send_init: ortp sender init complete +Dec 08 19:28:58 3: (21286) setup_signal_handling: setting up signal handlers +Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 2 +Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 15 +Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 1 +Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 17 +Dec 08 19:28:58 1: (21286) para_install_sighandler: catching signal 10 +Dec 08 19:28:58 3: (21286) server_init: initializing the audio file selector +Dec 08 19:28:58 2: (21287) get_database_dir: afs_database dir /home/maan/.paraslash/afs_database +Dec 08 19:28:58 3: (21287) open_afs_tables: opening 7 osl tables in /home/maan/.paraslash/afs_database +Dec 08 19:28:58 2: (21287) osl_open_table: opening table audio_files +Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'audio_files' from table description +Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 32 +Dec 08 19:28:58 1: (21287) map_table: mapping table 'audio_files' (index: /home/maan/.paraslash/afs_database/audio_files/index) +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/index: size 218667 +Dec 08 19:28:58 1: (21287) read_table_desc: 5 columns +Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'audio_files' matches on-disk data, good +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/2346ad27d7568ba9896f1b7da6b5991251debdf2: size 143430 +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/3150ecd5e0294534a81ae047ddac559de481d774: size 436636 +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/94ea39e309f3f31357ab60b190b6b8c32f21620b: size 225390 +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/b063ad096f9d142a388ca2a10d46b56904e26cda: size 1174409 +Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 6830 +Dec 08 19:28:58 2: (21286) init_afs: afs_socket: 8, afs_socket_cookie: 1363241225 +Dec 08 19:28:58 3: (21286) server_init: initializing tcp command socket +Dec 08 19:28:58 2: (21286) para_listen: listening on TCP port 2990, fd 9 +Dec 08 19:28:58 3: (21286) server_init: server init complete +Dec 08 19:28:58 1: (21286) chk_barrier: autoplay_delay barrier: 14981ms left +Dec 08 19:28:58 1: (21286) status_refresh: 0 events, forcing status update +Dec 08 19:28:58 1: (21286) para_next_signal: next signal: 10 +Dec 08 19:28:58 1: (21286) chk_barrier: autoplay_delay barrier: 14981ms left +Dec 08 19:28:58 1: (21286) para_next_signal: next signal: 10 +Dec 08 19:28:58 1: (21286) chk_barrier: autoplay_delay barrier: 14981ms left +Dec 08 19:28:58 2: (21287) aft_open: audio file table contains 6830 files +Dec 08 19:28:58 2: (21287) osl_open_table: opening table attributes +Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'attributes' from table description +Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16 +Dec 08 19:28:58 1: (21287) map_table: mapping table 'attributes' (index: /home/maan/.paraslash/afs_database/attributes/index) +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/attributes/index: size 396 +Dec 08 19:28:58 1: (21287) read_table_desc: 2 columns +Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'attributes' matches on-disk data, good +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/attributes/fd1e48caeff7212c45fc08608b7187feb10a7a2d: size 42 +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/attributes/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 141 +Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 21 +Dec 08 19:28:58 2: (21287) osl_open_table: opening table score +Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'score' from table description +Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 0 +Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 0 +Dec 08 19:28:58 2: (21287) osl_open_table: opening table moods +Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'moods' from table description +Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16 +Dec 08 19:28:58 1: (21287) map_table: mapping table 'moods' (index: /home/maan/.paraslash/afs_database/moods/index) +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/index: size 235 +Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns +Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'moods' matches on-disk data, good +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/87ea5dfc8b8e384d848979496e706390b497e547: size 50 +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 67 +Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 10 +Dec 08 19:28:58 2: (21287) osl_open_table: opening table lyrics +Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'lyrics' from table description +Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16 +Dec 08 19:28:58 1: (21287) map_table: mapping table 'lyrics' (index: /home/maan/.paraslash/afs_database/lyrics/index) +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/lyrics/index: size 347 +Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns +Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'lyrics' matches on-disk data, good +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/lyrics/87ea5dfc8b8e384d848979496e706390b497e547: size 85 +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/lyrics/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 478 +Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 17 +Dec 08 19:28:58 2: (21287) osl_open_table: opening table images +Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'images' from table description +Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16 +Dec 08 19:28:58 1: (21287) map_table: mapping table 'images' (index: /home/maan/.paraslash/afs_database/images/index) +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/images/index: size 7227 +Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns +Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'images' matches on-disk data, good +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/images/87ea5dfc8b8e384d848979496e706390b497e547: size 2235 +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/images/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 12950 +Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 447 +Dec 08 19:28:58 2: (21287) osl_open_table: opening table playlists +Dec 08 19:28:58 1: (21287) init_table_structure: creating table structure for 'playlists' from table description +Dec 08 19:28:58 1: (21287) init_table_structure: OK. Index entry size: 16 +Dec 08 19:28:58 1: (21287) map_table: mapping table 'playlists' (index: /home/maan/.paraslash/afs_database/playlists/index) +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/playlists/index: size 107 +Dec 08 19:28:58 1: (21287) read_table_desc: 3 columns +Dec 08 19:28:58 1: (21287) compare_table_descriptions: table description of 'playlists' matches on-disk data, good +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/playlists/87ea5dfc8b8e384d848979496e706390b497e547: size 10 +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/playlists/6ae999552a0d2dca14d62e2bc8b764d377b1dd6c: size 7 +Dec 08 19:28:58 1: (21287) osl_open_table: num rows: 2 +Dec 08 19:28:58 2: (21287) afs_init: server_socket: 9, afs_socket_cookie: 1363241225 +Dec 08 19:28:58 1: (21287) osl_open_disk_object: filename: /home/maan/.paraslash/afs_database/moods/f3f1dd33eb2a8b380b64a830e5fd90eab77d9ff3/9d/b063f3b5e0adfd0d29a03db0a1c207b3740a94 +Dec 08 19:28:58 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/moods/f3f1dd33eb2a8b380b64a830e5fd90eab77d9ff3/9d/b063f3b5e0adfd0d29a03db0a1c207b3740a94: size 31 +Dec 08 19:28:58 1: (21287) parse_mood_line: accept entry added, method: 0x806c0f4 +Dec 08 19:28:58 3: (21287) change_current_mood: computing statistics of admissible files +Dec 08 19:28:58 2: (21287) log_statistics: last_played mean: 1193159581, last_played sigma: 4161524 +Dec 08 19:28:58 2: (21287) log_statistics: num_played mean: 32, num_played sigma: 21 +Dec 08 19:28:58 2: (21287) change_current_mood: 26 admissible files +Dec 08 19:28:58 3: (21287) change_current_mood: loaded mood gulp +Dec 08 19:28:58 2: (21287) register_signal_task: signal pipe: fd 8 +Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 2 +Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 15 +Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 13 +Dec 08 19:28:58 1: (21287) para_install_sighandler: catching signal 1 +Dec 08 19:28:58 2: (21287) init_sched: initializing scheduler +Dec 08 19:28:58 2: (21287) register_task: registering signal task (0x806f244) +Dec 08 19:28:58 1: (21287) register_task: pre_select: 0x806f248 +Dec 08 19:28:58 1: (21287) register_task: post_select: 0x806f248 +Dec 08 19:28:58 2: (21287) setup_command_socket_or_die: listening on socket /var/paraslash/afs_command_socket (fd 1) +Dec 08 19:28:58 2: (21287) register_task: registering command task (0x806f118) +Dec 08 19:28:58 1: (21287) register_task: pre_select: 0x806f11c +Dec 08 19:28:58 1: (21287) register_task: post_select: 0x806f11c +Dec 08 19:28:59 2: (21286) main: got connection from ::ffff:127.0.0.1#42344, forking +Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 9 +Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 8 +Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 6 +Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 5 +Dec 08 19:28:59 1: (21356) close_listed_fds: closing fd 4 +Dec 08 19:28:59 1: (21286) chk_barrier: autoplay_delay barrier: 13507ms left +Dec 08 19:28:59 1: (21356) handle_connect: received rc4 request for user maan +Dec 08 19:28:59 1: (21356) handle_connect: sending 64 byte challenge +Dec 08 19:28:59 2: (21356) handle_connect: good auth for maan (1081806111) +Dec 08 19:28:59 1: (21356) init_rc4_keys: rc4 keys initialized (84:105) +Dec 08 19:28:59 2: (21356) enable_crypt: rc4 encryption activated for fd 10 +Dec 08 19:28:59 3: (21356) handle_connect: invalid command +Dec 08 19:28:59 1: (21286) para_next_signal: next signal: 17 +Dec 08 19:28:59 1: (21286) para_reap_child: child 21356 exited. Exit status: 1 +Dec 08 19:28:59 1: (21286) chk_barrier: autoplay_delay barrier: 13498ms left +Dec 08 19:29:03 2: (21286) main: got connection from ::ffff:127.0.0.1#42345, forking +Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 9 +Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 8 +Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 6 +Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 5 +Dec 08 19:29:03 1: (21362) close_listed_fds: closing fd 4 +Dec 08 19:29:03 1: (21286) chk_barrier: autoplay_delay barrier: 9951ms left +Dec 08 19:29:03 1: (21362) handle_connect: received rc4 request for user maan +Dec 08 19:29:03 1: (21362) handle_connect: sending 64 byte challenge +Dec 08 19:29:03 2: (21362) handle_connect: good auth for maan (705434988) +Dec 08 19:29:03 1: (21362) init_rc4_keys: rc4 keys initialized (18:65) +Dec 08 19:29:03 2: (21362) enable_crypt: rc4 encryption activated for fd 10 +Dec 08 19:29:03 1: (21362) check_perms: checking permissions +Dec 08 19:29:03 3: (21362) handle_connect: calling com_stat() for maan@::ffff:127.0.0.1#42345 +Dec 08 19:29:13 1: (21286) chk_barrier: autoplay_delay barrier: 3ms left +Dec 08 19:29:13 1: (21286) vss_preselect: ready and playing, but no audio file +Dec 08 19:29:13 3: (21286) vss_post_select: requesting new fd from afs +Dec 08 19:29:13 1: (21287) execute_server_command: received: new +Dec 08 19:29:13 3: (21287) open_next_audio_file: getting next audio file +Dec 08 19:29:13 1: (21287) osl_open_disk_object: filename: /home/maan/.paraslash/afs_database/audio_files/7e078c9876ccabef154017c770e05195c85b5e4d/55/b5e38d467105bd88133cf5ded70e551e582593 +Dec 08 19:29:13 1: (21287) mmap_full_file: /home/maan/.paraslash/afs_database/audio_files/7e078c9876ccabef154017c770e05195c85b5e4d/55/b5e38d467105bd88133cf5ded70e551e582593: size 4532 +Dec 08 19:29:13 1: (21287) mmap_full_file: /home/mp3/checked/dvd_07/cd_46/The_G.U.L.P.__Scheiss_Krieg.ogg: size 5274482 +Dec 08 19:29:13 1: (21287) mood_update_audio_file: score: 6 +Dec 08 19:29:13 1: (21287) mood_update_audio_file: moving from rank 26 to 35% +Dec 08 19:29:13 1: (21287) score_update: new score: 0, rank 9/26 +Dec 08 19:29:13 1: (21287) osl_update_object: updating column 1 of score +Dec 08 19:29:13 1: (21287) score_update: new score: -65, rank 1/26 +Dec 08 19:29:13 1: (21287) osl_update_object: updating column 1 of score +Dec 08 19:29:13 1: (21287) save_afd: size: 8932 +Dec 08 19:29:13 1: (21287) pass_afd: passing 8 bytes and fd 12 +Dec 08 19:29:13 1: (21286) recv_afs_result: fd: 10, code: 0, shmid: 28442631 +Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left +Dec 08 19:29:13 1: (21286) status_refresh: 1 events, forcing status update +Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left +Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left +Dec 08 19:29:13 1: (21286) para_next_signal: next signal: 10 +Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 300ms left +Dec 08 19:29:13 3: (21286) dccp_post_select: connection from ::ffff:127.0.0.1#46539 +Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 287ms left +Dec 08 19:29:13 1: (21286) chk_barrier: data send barrier: 287ms left +Dec 08 19:29:13 1: (21287) para_next_signal: next signal: 10 +Dec 08 19:29:13 1: (21286) status_refresh: 2 events, forcing status update +Dec 08 19:29:13 1: (21286) para_next_signal: next signal: 10 +Dec 08 19:29:13 1: (21287) para_next_signal: next signal: 10 +Dec 08 19:29:14 1: (21286) cq_enqueue: 4446 bytes queued for 0x8072eb0 diff --git a/wma.h b/wma.h new file mode 100644 index 00000000..33e34a35 --- /dev/null +++ b/wma.h @@ -0,0 +1,33 @@ +/** \file wma.h The asf_header structure and public wma function declarations. */ + +/** + * Information contained in an asf audio file header. + * + * Both para_filter and para_afh need to read the header. + */ +struct asf_header_info { + /** The size of the audio file header. */ + int header_len; + /** Offset of the audio stream info header. */ + int audio_stream_info_start; + /** Number of channels. */ + uint8_t channels; + /** One of the 5 possible sample rates. */ + uint16_t sample_rate; + /** Size of one data block. */ + uint16_t block_align; + /** Bits per second. */ + uint32_t bit_rate; + /** Further decoding information (ignored). */ + uint32_t flags1; + /** Whether to use exp_vlc, bit reservoir, variable block len. */ + uint16_t flags2; +}; + +/* wma_common.c */ +int wma_log2(unsigned int v); +const char *search_pattern(const char *pattern, int pattern_len, + const char *buf, int buf_size); +int read_asf_header(const char *buf, int loaded, struct asf_header_info *ahi); + +#define WMA_FRAME_SKIP 31 diff --git a/wma_afh.c b/wma_afh.c new file mode 100644 index 00000000..d9a83483 --- /dev/null +++ b/wma_afh.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2009-2010 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file wma_afh.c The audio format handler for WMA files. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "para.h" +#include "error.h" +#include "afh.h" +#include "portable_io.h" +#include "string.h" +#include "wma.h" + +#define FOR_EACH_FRAME(_f, _buf, _size, _ba) for (_f = (_buf); \ + _f + (_ba) + WMA_FRAME_SKIP < (_buf) + (_size); \ + _f += (_ba) + WMA_FRAME_SKIP) + +/* + * Must be called on a frame boundary, e.g. start + header_len. + * \return Frame count, superframe count via *num_superframes. + */ +static int count_frames(const char *buf, int buf_size, int block_align, + int *num_superframes) +{ + int fc = 0, sfc = 0; /* frame count, superframe count */ + const uint8_t *p; + + + FOR_EACH_FRAME(p, (uint8_t *)buf, buf_size, block_align) { + fc += p[WMA_FRAME_SKIP] & 0x0f; + sfc++; + } + PARA_INFO_LOG("%d frames, %d superframes\n", fc, sfc); + if (num_superframes) + *num_superframes = sfc; + return fc; +} + +/* + * put_utf8() and get_str16() below are based on macros in libavutil/common.h + * of the mplayer source code, copyright (c) 2006 Michael Niedermayer + * . + */ + +/* + * Convert a 32-bit Unicode character to its UTF-8 encoded form. + * + * Writes up to 4 bytes for values in the valid UTF-8 range and up to 7 bytes + * in the general case, depending on the length of the converted Unicode + * character. + * + * \param result Where the converted UTF-8 bytes are written. + */ +static int put_utf8(uint32_t val, char *result) +{ + char *out = result; + int bytes, shift; + uint32_t in = val; + + if (in < 0x80) { + *out++ = in; + return 1; + } + bytes = (wma_log2(in) + 4) / 5; + shift = (bytes - 1) * 6; + *out++ = (256 - (256 >> bytes)) | (in >> shift); + while (shift >= 6) { + shift -= 6; + *out++ = 0x80 | ((in >> shift) & 0x3f); + } + return out - result; +} + +static char *get_str16(const char *in, int len) +{ + const char *p = in; + int out_size = 0, out_len = 0; + char *out = NULL; + + len /= 2; + while (len--) { + uint32_t x; + if (out_len + 7 + 1 >= out_size) { + out_size = 2 * out_size + 50; + out = para_realloc(out, out_size); + } + x = read_u16(p); + p += 2; + out_len += put_utf8(x, out + out_len); + if (x == 0) + return out; + } + if (out) + out[out_len] = '\0'; + return out; +} + +static const char comment_header[] = { + 0x33, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11, + 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c +}; + +static const char extended_content_header[] = { + 0x40, 0xA4, 0xD0, 0xD2, 0x07, 0xE3, 0xD2, 0x11, + 0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50 +}; + +static const char year_tag_header[] = { /* WM/Year */ + 0x57, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x59, 0x00, + 0x65, 0x00, 0x61, 0x00, 0x72, 0x00 +}; + +static const char album_tag_header[] = { /* WM/AlbumTitle */ + 0x57, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x41, 0x00, + 0x6c, 0x00, 0x62, 0x00, 0x75, 0x00, 0x6d, 0x00, + 0x54, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, + 0x65, 0x00 +}; + +static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti) +{ + const char *p, *end = buf + buf_size, *q; + uint16_t len1, len2, len3, len4, len5; + + p = search_pattern(comment_header, sizeof(comment_header), + buf, buf_size); + if (!p || p + 34 >= end) { + PARA_NOTICE_LOG("comment header not found\n"); + goto next; + } + p += 24; + len1 = read_u16(p); + p += 2; + len2 = read_u16(p); + p += 2; + len3 = read_u16(p); + p += 2; + len4 = read_u16(p); + p += 2; + len5 = read_u16(p); + p += 2; + if (p + len1 >= end) + goto next; + ti->title = get_str16(p, len1); + p += len1; + if (p + len2 >= end) + goto next; + ti->artist = get_str16(p, len2); + p += len2 + len3 + len4; + if (p + len5 >= end) + goto next; + ti->comment = get_str16(p, len5); +next: + p = search_pattern(extended_content_header, sizeof(extended_content_header), + buf, buf_size); + if (!p) { + PARA_NOTICE_LOG("extended content header not found\n"); + return; + } + q = search_pattern(year_tag_header, sizeof(year_tag_header), + p, end - p); + if (q) { + const char *r = q + sizeof(year_tag_header) + 6; + if (r < end) + ti->year = get_str16(r, end - r); + } + q = search_pattern(album_tag_header, sizeof(album_tag_header), + p, end - p); + if (q) { + const char *r = q + sizeof(album_tag_header) + 6; + if (r < end) + ti->album = get_str16(r, end - r); + } +} + +static void set_chunk_tv(int frames_per_chunk, int frequency, + struct timeval *result) +{ + uint64_t x = (uint64_t)frames_per_chunk * 2048 * 1000 * 1000 + / frequency; + + result->tv_sec = x / 1000 / 1000; + result->tv_usec = x % (1000 * 1000); + PARA_INFO_LOG("chunk time: %lums\n", tv2ms(result)); +} + +/* Must be called on a frame boundary. */ +static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align, + struct afh_info *afhi) +{ + const uint8_t *f, *start = (uint8_t *)buf; + int j, frames_per_chunk; + size_t ct_size = 250; + int ret, count = 0, num_frames, num_superframes; + + afhi->chunk_table = para_malloc(ct_size * sizeof(uint32_t)); + afhi->chunk_table[0] = 0; + afhi->chunk_table[1] = afhi->header_len; + + num_frames = count_frames(buf, buf_size, block_align, + &num_superframes); + ret = -E_NO_WMA; + if (num_frames == 0 || num_superframes == 0) + goto fail; + afhi->seconds_total = num_frames * 2048 /* FIXME */ + / afhi->frequency; + frames_per_chunk = num_frames / num_superframes / 2; + PARA_INFO_LOG("%d frames per chunk\n", frames_per_chunk); + j = 1; + FOR_EACH_FRAME(f, start, buf_size, block_align) { + count += f[WMA_FRAME_SKIP] & 0x0f; + while (count > j * frames_per_chunk) { + j++; + if (j >= ct_size) { + ct_size *= 2; + afhi->chunk_table = para_realloc( + afhi->chunk_table, + ct_size * sizeof(uint32_t)); + } + afhi->chunk_table[j] = f - start + afhi->header_len + block_align + WMA_FRAME_SKIP; + } + } + afhi->chunks_total = j; + set_chunk_tv(frames_per_chunk, afhi->frequency, &afhi->chunk_tv); + return 1; +fail: + free(afhi->chunk_table); + return ret; +} + +static int wma_get_file_info(char *map, size_t numbytes, __a_unused int fd, + struct afh_info *afhi) +{ + int ret; + struct asf_header_info ahi; + + ret = read_asf_header(map, numbytes, &ahi); + if (ret < 0) + return ret; + if (ret == 0) + return -E_NO_WMA; + afhi->bitrate = ahi.bit_rate / 1000; + if (ahi.sample_rate == 0) + return -E_NO_WMA; + afhi->frequency = ahi.sample_rate; + afhi->channels = ahi.channels; + afhi->header_len = ahi.header_len; + afhi->header_offset = 0; + wma_make_chunk_table(map + ahi.header_len, numbytes - ahi.header_len, + ahi.block_align, afhi); + read_asf_tags(map, ahi.header_len, &afhi->tags); + return 0; +} + +static const char* wma_suffixes[] = {"wma", NULL}; + +/** + * The init function of the wma audio format handler. + * + * \param afh Pointer to the struct to initialize. + */ +void wma_afh_init(struct audio_format_handler *afh) +{ + afh->get_file_info = wma_get_file_info; + afh->suffixes = wma_suffixes; +} diff --git a/wma_common.c b/wma_common.c new file mode 100644 index 00000000..0cb881e5 --- /dev/null +++ b/wma_common.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2009-2010 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file wma_common.c Functions used by both the WMA afh and decoder. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "para.h" +#include "error.h" +#include "afh.h" +#include "portable_io.h" +#include "imdct.h" +#include "wma.h" + +/** + * Find the first occurrence of the given pattern. + * + * \param pattern The pattern to search for. + * \param pattern_len The length of the pattern in bytes. + * \param buf The buffer to search for the pattern. + * \param buf_size The number of bytes in \a buf. + */ +const char *search_pattern(const char *pattern, int pattern_len, + const char *buf, int buf_size) +{ + const char *p, *end = buf + buf_size; + + /* TODO: Use suffix arrays to speed up the search. */ + for (p = buf; p + pattern_len < end; p++) { + if (memcmp(p, pattern, pattern_len)) + continue; + PARA_DEBUG_LOG("found %d byte pattern@%d\n", + pattern_len, (int)(p - buf)); + return p; + } + PARA_NOTICE_LOG("%d byte pattern not found\n", pattern_len); + return NULL; +} + +/* + 40 9e 69 f8 4d 5b cf 11 a8 fd 00 80 5f 5c 44 2b + */ +static int find_audio_stream_info(const char *buf, int len) +{ + const char pattern[] = {0x40, 0x9e, 0x69, 0xf8}; + const char *p = search_pattern(pattern, sizeof(pattern), buf, len); + + if (!p) + return -E_WMA_NO_GUID; + PARA_DEBUG_LOG("found audio stream guid@%0x\n", (int)(p - buf)); + return p - buf + 16; +} + +static int read_header_len(const char *buf, int len) +{ + uint16_t header_len; + + if (len < 18) + return 0; + header_len = read_u16(buf + 16) + 46; + PARA_DEBUG_LOG("header_len: %d\n", header_len); + return header_len; +} + +/** + * Read an asf audio file header. + * + * \param buf The input buffer. + * \param loaded Number of bytes in \a buf. + * \param ahi Result pointer. + * + * \return Negative on errors, zero if more data is needed in order to read the + * full header, 1 on success. + */ +int read_asf_header(const char *buf, int loaded, struct asf_header_info *ahi) +{ + int ret; + const char *start; + + ahi->header_len = read_header_len(buf, loaded); + if (ahi->header_len == 0) /* too short to read header len */ + return 0; + if (ahi->header_len > loaded) /* too short to read header */ + return 0; + ret = find_audio_stream_info(buf, ahi->header_len); + if (ret < 0) + return ret; + if (ret + 62 > loaded) + return 0; + ahi->audio_stream_info_start = ret; + start = buf + ahi->audio_stream_info_start; + ahi->channels = ((uint8_t *)start)[40]; + ahi->sample_rate = read_u16(start + 42); + PARA_NOTICE_LOG("%d channels, sample rate: %d\n", ahi->channels, + ahi->sample_rate); + + ahi->bit_rate = 8 * read_u16(start + 46); + PARA_INFO_LOG("bit rate: %d\n", ahi->bit_rate); + + ahi->block_align = read_u16(start + 50); + PARA_INFO_LOG("block_align: %d\n", ahi->block_align); + + ahi->flags1 = read_u32(start + 56); + ahi->flags2 = read_u16(start + 60); + PARA_INFO_LOG("read_asf_header: flags1: %d, flag2: %d\n", + ahi->flags1, ahi->flags2); + return 1; +} + +const uint8_t log2_tab[256] = { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 +}; + +/** + * Compute the base-2 logarithm. + * + * \param v The value to compute the logarithm of. + * + * \return An integer approximation of log2(v). + */ +int wma_log2(unsigned int v) +{ + int n = 0; + if (v & 0xffff0000) { + v >>= 16; + n += 16; + } + if (v & 0xff00) { + v >>= 8; + n += 8; + } + n += log2_tab[v]; + + return n; +} diff --git a/wmadata.h b/wmadata.h new file mode 100644 index 00000000..b5dad0f5 --- /dev/null +++ b/wmadata.h @@ -0,0 +1,1453 @@ +/* + * WMA compatible decoder + * copyright (c) 2002 The FFmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** \file wmadata.h Various WMA tables. */ + +/** Contains huffcodes, huffbits and run/level tables. */ +struct coef_vlc_table { + /** Total number of codes. */ + int n; + /** VLC bit values. */ + const uint32_t *huffcodes; + /** VLC bit size. */ + const uint8_t *huffbits; + /* Table to build run/level tables. */ + const uint16_t *levels; +}; + +static const uint16_t wma_critical_freqs[25] = { + 100, 200, 300, 400, 510, 630, 770, 920, + 1080, 1270, 1480, 1720, 2000, 2320, 2700, 3150, + 3700, 4400, 5300, 6400, 7700, 9500, 12000, 15500, + 24500, +}; + +/* first value is number of bands */ +static const uint8_t exponent_band_22050[3][25] = { + {10, 4, 8, 4, 8, 8, 12, 20, 24, 24, 16,}, + {14, 4, 8, 8, 4, 12, 12, 16, 24, 16, 20, 24, 32, 40, 36,}, + {23, 4, 4, 4, 8, 4, 4, 8, 8, 8, 8, 8, 12, 12, 16, 16, 24, 24, 32, 44, + 48, 60, 84, 72,}, +}; + +static const uint8_t exponent_band_32000[3][25] = { + {11, 4, 4, 8, 4, 4, 12, 16, 24, 20, 28, 4,}, + {15, 4, 8, 4, 4, 8, 8, 16, 20, 12, 20, 20, 28, 40, 56, 8,}, + {16, 8, 4, 8, 8, 12, 16, 20, 24, 40, 32, 32, 44, 56, 80, 112, 16,}, +}; + +static const uint8_t exponent_band_44100[3][25] = { + {12, 4, 4, 4, 4, 4, 8, 8, 8, 12, 16, 20, 36,}, + {15, 4, 8, 4, 8, 8, 4, 8, 8, 12, 12, 12, 24, 28, 40, 76,}, + {17, 4, 8, 8, 4, 12, 12, 8, 8, 24, 16, 20, 24, 32, 40, 60, 80, 152,}, +}; + +static const uint16_t wma_hgain_huffcodes[37] = { + 0x00003, 0x002e7, 0x00001, 0x005cd, 0x0005d, 0x005c9, 0x0005e, 0x00003, + 0x00016, 0x0000b, 0x00001, 0x00006, 0x00001, 0x00006, 0x00004, 0x00005, + 0x00004, 0x00007, 0x00003, 0x00007, 0x00004, 0x0000a, 0x0000a, 0x00002, + 0x00003, 0x00000, 0x00005, 0x00002, 0x0005f, 0x00004, 0x00003, 0x00002, + 0x005c8, 0x000b8, 0x005ca, 0x005cb, 0x005cc, +}; + +static const uint8_t wma_hgain_huffbits[37] = { + 10, 12, 10, 13, 9, 13, 9, 8, + 7, 5, 5, 4, 4, 3, 3, 3, + 4, 3, 4, 4, 5, 5, 6, 8, + 7, 10, 8, 10, 9, 8, 9, 9, + 13, 10, 13, 13, 13, +}; + +#define NB_LSP_COEFS 10 + +static const float wma_lsp_codebook[NB_LSP_COEFS][16] = { + {1.98732877, 1.97944528, 1.97179088, 1.96260549, 1.95038374, 1.93336114, + 1.90719232, 1.86191415,}, + {1.97260000, 1.96083160, 1.94982586, 1.93806164, 1.92516608, 1.91010199, + 1.89232331, 1.87149812, + 1.84564818, 1.81358067, 1.77620070, 1.73265264, 1.67907855, 1.60959081, + 1.50829650, 1.33120330,}, + {1.90109110, 1.86482426, 1.83419671, 1.80168452, 1.76650116, 1.72816320, + 1.68502700, 1.63738256, + 1.58501580, 1.51795181, 1.43679906, 1.33950585, 1.24176208, 1.12260729, + 0.96749668, 0.74048265,}, + {1.76943864, 1.67822463, 1.59946365, 1.53560582, 1.47470796, 1.41210167, + 1.34509536, 1.27339507, + 1.19303814, 1.09765169, 0.98818722, 0.87239446, 0.74369172, 0.59768184, + 0.43168630, 0.17977021,}, + {1.43428349, 1.32038354, 1.21074086, 1.10577988, 1.00561746, 0.90335924, + 0.80437489, 0.70709671, + 0.60427395, 0.49814048, 0.38509539, 0.27106800, 0.14407416, 0.00219910, + -0.16725141, -0.36936085,}, + {0.99895687, 0.84188166, 0.70753739, 0.57906595, 0.47055563, 0.36966965, + 0.26826648, 0.17163380, + 0.07208392, -0.03062936, -1.40037388, -0.25128968, -0.37213937, + -0.51075646, -0.64887512, -0.80308031,}, + {0.26515280, 0.06313551, -0.08872080, -0.21103548, -0.31069678, + -0.39680323, -0.47223474, -0.54167135, + -0.61444740, -0.68943343, -0.76580211, -0.85170082, -0.95289061, + -1.06514703, -1.20510707, -1.37617746,}, + {-0.53940301, -0.73770929, -0.88424876, -1.01117930, -1.13389091, + -1.26830073, -1.42041987, -1.62033919, + -1.10158808, -1.16512566, -1.23337128, -1.30414401, -1.37663312, + -1.46853845, -1.57625798, -1.66893638,}, + {-0.38601997, -0.56009350, -0.66978483, -0.76028471, -0.83846064, + -0.90868087, -0.97408881, -1.03694962,}, + {-1.56144989, -1.65944032, -1.72689685, -1.77857740, -1.82203011, + -1.86220079, -1.90283983, -1.94820479,}, +}; + +static const uint32_t wma_scale_huffcodes[121] = { + 0x3ffe8, 0x3ffe6, 0x3ffe7, 0x3ffe5, 0x7fff5, 0x7fff1, 0x7ffed, 0x7fff6, + 0x7ffee, 0x7ffef, 0x7fff0, 0x7fffc, 0x7fffd, 0x7ffff, 0x7fffe, 0x7fff7, + 0x7fff8, 0x7fffb, 0x7fff9, 0x3ffe4, 0x7fffa, 0x3ffe3, 0x1ffef, 0x1fff0, + 0x0fff5, 0x1ffee, 0x0fff2, 0x0fff3, 0x0fff4, 0x0fff1, 0x07ff6, 0x07ff7, + 0x03ff9, 0x03ff5, 0x03ff7, 0x03ff3, 0x03ff6, 0x03ff2, 0x01ff7, 0x01ff5, + 0x00ff9, 0x00ff7, 0x00ff6, 0x007f9, 0x00ff4, 0x007f8, 0x003f9, 0x003f7, + 0x003f5, 0x001f8, 0x001f7, 0x000fa, 0x000f8, 0x000f6, 0x00079, 0x0003a, + 0x00038, 0x0001a, 0x0000b, 0x00004, 0x00000, 0x0000a, 0x0000c, 0x0001b, + 0x00039, 0x0003b, 0x00078, 0x0007a, 0x000f7, 0x000f9, 0x001f6, 0x001f9, + 0x003f4, 0x003f6, 0x003f8, 0x007f5, 0x007f4, 0x007f6, 0x007f7, 0x00ff5, + 0x00ff8, 0x01ff4, 0x01ff6, 0x01ff8, 0x03ff8, 0x03ff4, 0x0fff0, 0x07ff4, + 0x0fff6, 0x07ff5, 0x3ffe2, 0x7ffd9, 0x7ffda, 0x7ffdb, 0x7ffdc, 0x7ffdd, + 0x7ffde, 0x7ffd8, 0x7ffd2, 0x7ffd3, 0x7ffd4, 0x7ffd5, 0x7ffd6, 0x7fff2, + 0x7ffdf, 0x7ffe7, 0x7ffe8, 0x7ffe9, 0x7ffea, 0x7ffeb, 0x7ffe6, 0x7ffe0, + 0x7ffe1, 0x7ffe2, 0x7ffe3, 0x7ffe4, 0x7ffe5, 0x7ffd7, 0x7ffec, 0x7fff4, + 0x7fff3, +}; + +static const uint8_t wma_scale_huffbits[121] = { + 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 18, 19, 18, 17, 17, + 16, 17, 16, 16, 16, 16, 15, 15, + 14, 14, 14, 14, 14, 14, 13, 13, + 12, 12, 12, 11, 12, 11, 10, 10, + 10, 9, 9, 8, 8, 8, 7, 6, + 6, 5, 4, 3, 1, 4, 4, 5, + 6, 6, 7, 7, 8, 8, 9, 9, + 10, 10, 10, 11, 11, 11, 11, 12, + 12, 13, 13, 13, 14, 14, 16, 15, + 16, 15, 18, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, +}; + +static const uint32_t coef0_huffcodes[666] = { + 0x00258, 0x0003d, 0x00000, 0x00005, 0x00008, 0x00008, 0x0000c, 0x0001b, + 0x0001f, 0x00015, 0x00024, 0x00032, 0x0003a, 0x00026, 0x0002c, 0x0002f, + 0x0004a, 0x0004d, 0x00061, 0x00070, 0x00073, 0x00048, 0x00052, 0x0005a, + 0x0005d, 0x0006e, 0x00099, 0x0009e, 0x000c1, 0x000ce, 0x000e4, 0x000f0, + 0x00093, 0x0009e, 0x000a2, 0x000a1, 0x000b8, 0x000d2, 0x000d3, 0x0012e, + 0x00130, 0x000de, 0x0012d, 0x0019b, 0x001e4, 0x00139, 0x0013a, 0x0013f, + 0x0014f, 0x0016d, 0x001a2, 0x0027c, 0x0027e, 0x00332, 0x0033c, 0x0033f, + 0x0038b, 0x00396, 0x003c5, 0x00270, 0x0027c, 0x0025a, 0x00395, 0x00248, + 0x004bd, 0x004fb, 0x00662, 0x00661, 0x0071b, 0x004e6, 0x004ff, 0x00666, + 0x0071c, 0x0071a, 0x0071f, 0x00794, 0x00536, 0x004e2, 0x0078e, 0x004ee, + 0x00518, 0x00535, 0x004fb, 0x0078d, 0x00530, 0x00680, 0x0068f, 0x005cb, + 0x00965, 0x006a6, 0x00967, 0x0097f, 0x00682, 0x006ae, 0x00cd0, 0x00e28, + 0x00f13, 0x00f1f, 0x009f5, 0x00cd3, 0x00f11, 0x00926, 0x00964, 0x00f32, + 0x00f12, 0x00f30, 0x00966, 0x00d0b, 0x00a68, 0x00b91, 0x009c7, 0x00b73, + 0x012fa, 0x0131d, 0x013f9, 0x01ca0, 0x0199c, 0x01c7a, 0x0198c, 0x01248, + 0x01c74, 0x01c64, 0x0139e, 0x012fd, 0x00a77, 0x012fc, 0x01c7b, 0x012ca, + 0x014cc, 0x014d2, 0x014e3, 0x014dc, 0x012dc, 0x03344, 0x02598, 0x0263c, + 0x0333b, 0x025e6, 0x01a1c, 0x01e3c, 0x014e2, 0x033d4, 0x01a11, 0x03349, + 0x03cce, 0x014e1, 0x01a34, 0x0273e, 0x02627, 0x0273f, 0x038ee, 0x03971, + 0x03c67, 0x03c61, 0x0333d, 0x038c2, 0x0263f, 0x038cd, 0x02638, 0x02e41, + 0x0351f, 0x03348, 0x03c66, 0x03562, 0x02989, 0x027d5, 0x0333c, 0x02e4f, + 0x0343b, 0x02ddf, 0x04bc8, 0x029c0, 0x02e57, 0x04c72, 0x025b7, 0x03547, + 0x03540, 0x029d3, 0x04c45, 0x025bb, 0x06600, 0x04c73, 0x04bce, 0x0357b, + 0x029a6, 0x029d2, 0x0263e, 0x0298a, 0x07183, 0x06602, 0x07958, 0x04b66, + 0x0537d, 0x05375, 0x04fe9, 0x04b67, 0x0799f, 0x04bc9, 0x051fe, 0x06a3b, + 0x05bb6, 0x04fa8, 0x0728f, 0x05376, 0x0492c, 0x0537e, 0x0795a, 0x06a3c, + 0x0e515, 0x07887, 0x0683a, 0x051f9, 0x051fd, 0x0cc6a, 0x06a8a, 0x0cc6d, + 0x05bb3, 0x0683b, 0x051fc, 0x05378, 0x0728e, 0x07886, 0x05bb7, 0x0f2a4, + 0x0795b, 0x0683c, 0x09fc1, 0x0683d, 0x0b752, 0x09678, 0x0a3e8, 0x06ac7, + 0x051f0, 0x0b759, 0x06af3, 0x04b6b, 0x0f2a0, 0x0f2ad, 0x096c3, 0x0e518, + 0x0b75c, 0x0d458, 0x0cc6b, 0x0537c, 0x067aa, 0x04fea, 0x0343a, 0x0cc71, + 0x0967f, 0x09fc4, 0x096c2, 0x0e516, 0x0f2a1, 0x0d45c, 0x0d45d, 0x0d45e, + 0x12fb9, 0x0967e, 0x1982f, 0x09883, 0x096c4, 0x0b753, 0x12fb8, 0x0f2a8, + 0x1ca21, 0x096c5, 0x0e51a, 0x1ca27, 0x12f3c, 0x0d471, 0x0f2aa, 0x0b75b, + 0x12fbb, 0x0f2a9, 0x0f2ac, 0x0d45a, 0x0b74f, 0x096c8, 0x16e91, 0x096ca, + 0x12fbf, 0x0d0a7, 0x13103, 0x0d516, 0x16e99, 0x12cbd, 0x0a3ea, 0x19829, + 0x0b755, 0x29ba7, 0x1ca28, 0x29ba5, 0x16e93, 0x1982c, 0x19828, 0x25994, + 0x0a3eb, 0x1ca29, 0x16e90, 0x1ca25, 0x1982d, 0x1ca26, 0x16e9b, 0x0b756, + 0x0967c, 0x25997, 0x0b75f, 0x198d3, 0x0b757, 0x19a2a, 0x0d45b, 0x0e517, + 0x1ca24, 0x1ca23, 0x1ca22, 0x0b758, 0x16e97, 0x0cd14, 0x13100, 0x00007, + 0x0003b, 0x0006b, 0x00097, 0x00138, 0x00125, 0x00173, 0x00258, 0x00335, + 0x0028e, 0x004c6, 0x00715, 0x00729, 0x004ef, 0x00519, 0x004ed, 0x00532, + 0x0068c, 0x00686, 0x00978, 0x00e5d, 0x00e31, 0x009f4, 0x00b92, 0x012f8, + 0x00d06, 0x00a67, 0x00d44, 0x00a76, 0x00d59, 0x012cd, 0x01c78, 0x01c75, + 0x0199f, 0x0198f, 0x01c67, 0x014c6, 0x01c79, 0x01c76, 0x00b94, 0x00d1b, + 0x01e32, 0x01e31, 0x01ab0, 0x01a05, 0x01aa1, 0x0333a, 0x025e5, 0x02626, + 0x03541, 0x03544, 0x03421, 0x03546, 0x02e55, 0x02e56, 0x0492d, 0x02dde, + 0x0299b, 0x02ddc, 0x0357a, 0x0249c, 0x0668b, 0x1c77f, 0x1ca20, 0x0d45f, + 0x09886, 0x16e9a, 0x0f2a7, 0x0b751, 0x0a3ee, 0x0cf59, 0x0cf57, 0x0b754, + 0x0d0a6, 0x16e98, 0x0b760, 0x06ac6, 0x0a3f0, 0x12fbe, 0x13104, 0x0f2a5, + 0x0a3ef, 0x0d472, 0x12cba, 0x1982e, 0x16e9c, 0x1c77e, 0x198d0, 0x13105, + 0x16e92, 0x0b75d, 0x0d459, 0x0001a, 0x000c0, 0x0016c, 0x003cd, 0x00350, + 0x0067b, 0x0051e, 0x006a9, 0x009f4, 0x00b72, 0x00d09, 0x01249, 0x01e3d, + 0x01ca1, 0x01a1f, 0x01721, 0x01a8a, 0x016e8, 0x03347, 0x01a35, 0x0249d, + 0x0299a, 0x02596, 0x02e4e, 0x0298b, 0x07182, 0x04c46, 0x025ba, 0x02e40, + 0x027d6, 0x04fe8, 0x06607, 0x05310, 0x09884, 0x072e1, 0x06a3d, 0x04b6a, + 0x04c7a, 0x06603, 0x04c7b, 0x03428, 0x06605, 0x09664, 0x09fc0, 0x071de, + 0x06601, 0x05bb2, 0x09885, 0x0a3e2, 0x1c61f, 0x12cbb, 0x0b750, 0x0cf58, + 0x0967d, 0x25995, 0x668ad, 0x0b75a, 0x09fc2, 0x0537f, 0x0b75e, 0x13fae, + 0x12fbc, 0x00031, 0x001c4, 0x004c5, 0x005b8, 0x00cf4, 0x0096f, 0x00d46, + 0x01e57, 0x01a04, 0x02625, 0x03346, 0x028f9, 0x04c47, 0x072e0, 0x04b69, + 0x03420, 0x07957, 0x06639, 0x0799e, 0x07959, 0x07881, 0x04b68, 0x09fc3, + 0x09fd6, 0x0cc70, 0x0a3f1, 0x12cbe, 0x0e30e, 0x0e51b, 0x06af2, 0x12cbc, + 0x1c77d, 0x0f2ab, 0x12fbd, 0x1aa2f, 0x0a3ec, 0x0d473, 0x05377, 0x0a3e9, + 0x1982b, 0x0e300, 0x12f3f, 0x0cf5f, 0x096c0, 0x38c3c, 0x16e94, 0x16e95, + 0x12f3d, 0x29ba4, 0x29ba6, 0x1c77c, 0x6a8ba, 0x3545c, 0x33457, 0x668ac, + 0x6a8bb, 0x16e9d, 0x0e519, 0x25996, 0x12f3e, 0x00036, 0x0033e, 0x006ad, + 0x00d03, 0x012c8, 0x0124a, 0x03c42, 0x03ccd, 0x06606, 0x07880, 0x06852, + 0x06a3a, 0x05bb4, 0x0f2a2, 0x09fc7, 0x12cb9, 0x0cc6c, 0x0a6e8, 0x096c1, + 0x0004a, 0x00355, 0x012f9, 0x014e8, 0x01abe, 0x025b6, 0x0492e, 0x09fc6, + 0x051ff, 0x0cc6f, 0x096cb, 0x0d071, 0x198d1, 0x12cb8, 0x38c3d, 0x13faf, + 0x096c9, 0x0009d, 0x00539, 0x012ce, 0x0341f, 0x029c1, 0x04b33, 0x0a3e3, + 0x0d070, 0x16e96, 0x0b763, 0x000a0, 0x009ce, 0x038cc, 0x0343d, 0x051fa, + 0x09888, 0x12fba, 0x000df, 0x00a75, 0x029a7, 0x09fc5, 0x0e301, 0x0967b, + 0x001e7, 0x012c9, 0x051fb, 0x09889, 0x0f2a6, 0x0016f, 0x01cb9, 0x0cf5a, + 0x12cbf, 0x09679, 0x00272, 0x01a15, 0x0967a, 0x003cb, 0x025f6, 0x0b762, + 0x0028d, 0x03c60, 0x0cf5e, 0x00352, 0x03ccc, 0x0072f, 0x07186, 0x004ec, + 0x05379, 0x0068e, 0x09887, 0x006a7, 0x06af1, 0x00e29, 0x0cf5b, 0x00f31, + 0x0d470, 0x009c6, 0x013fb, 0x13102, 0x019a5, 0x13101, 0x01983, 0x01c65, + 0x0124f, 0x014c7, 0x01726, 0x01abf, 0x03304, 0x02624, 0x03c41, 0x027d7, + 0x02ddd, 0x02e54, 0x0343c, 0x06604, 0x07181, 0x0663a, 0x04fa9, 0x0663b, + 0x05311, 0x0537a, 0x06839, 0x05bb5, 0x0492f, 0x06af0, 0x096c7, 0x0cc6e, + 0x0537b, 0x0cf5c, 0x0cf56, 0x198d2, 0x0cf5d, 0x0a3ed, 0x0f2a3, 0x1982a, + 0x0b761, 0x096c6, +}; + +static const uint8_t coef0_huffbits[666] = { + 11, 6, 2, 3, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 11, 11, 11, 10, 11, + 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 11, 12, + 12, 12, 12, 11, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 13, 13, 12, + 12, 12, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 14, + 13, 13, 13, 13, 13, 13, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 13, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 15, + 15, 14, 14, 15, 15, 15, 14, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 14, 15, 15, 15, 15, 16, + 16, 16, 15, 16, 15, 15, 16, 16, + 16, 16, 15, 16, 16, 16, 15, 16, + 16, 15, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 15, 15, 16, 16, + 15, 16, 16, 16, 17, 17, 17, 16, + 16, 17, 16, 16, 16, 16, 17, 16, + 17, 17, 16, 16, 15, 15, 15, 16, + 17, 16, 17, 16, 16, 17, 17, 17, + 17, 17, 17, 16, 17, 17, 17, 16, + 17, 17, 16, 17, 17, 17, 16, 17, + 17, 16, 16, 17, 17, 17, 18, 17, + 17, 17, 17, 17, 18, 18, 17, 17, + 17, 19, 17, 19, 18, 17, 17, 18, + 17, 17, 18, 17, 17, 17, 18, 17, + 17, 18, 17, 17, 17, 17, 17, 16, + 17, 17, 17, 17, 18, 16, 17, 4, + 6, 8, 9, 9, 10, 10, 10, 10, + 11, 11, 11, 11, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 14, 13, 13, 13, 13, + 13, 13, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 16, 15, + 15, 15, 15, 15, 15, 17, 17, 17, + 16, 18, 16, 17, 17, 16, 16, 17, + 17, 18, 17, 16, 17, 17, 17, 16, + 17, 17, 18, 17, 18, 17, 17, 17, + 18, 17, 17, 5, 8, 10, 10, 11, + 11, 12, 12, 12, 13, 13, 14, 13, + 13, 14, 14, 14, 14, 14, 14, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 16, 16, 15, 16, 16, + 15, 15, 15, 15, 15, 16, 16, 15, + 15, 16, 16, 17, 17, 18, 17, 16, + 17, 18, 19, 17, 16, 16, 17, 17, + 17, 6, 9, 11, 12, 12, 13, 13, + 13, 14, 14, 14, 15, 15, 15, 16, + 15, 15, 15, 15, 15, 15, 16, 16, + 16, 16, 17, 18, 16, 16, 16, 18, + 17, 16, 17, 18, 17, 17, 16, 17, + 17, 16, 17, 16, 17, 18, 18, 18, + 17, 19, 19, 17, 20, 19, 18, 19, + 20, 18, 16, 18, 17, 7, 10, 12, + 13, 13, 14, 14, 14, 15, 15, 16, + 16, 16, 16, 16, 18, 16, 17, 17, + 8, 11, 13, 14, 14, 15, 16, 16, + 16, 16, 17, 17, 17, 18, 18, 17, + 17, 8, 12, 14, 15, 15, 15, 17, + 17, 18, 17, 9, 12, 14, 15, 16, + 16, 17, 9, 13, 15, 16, 16, 17, + 9, 13, 16, 16, 16, 10, 13, 16, + 18, 17, 10, 14, 17, 10, 14, 17, + 11, 14, 16, 11, 14, 11, 15, 12, + 16, 12, 16, 12, 16, 12, 16, 12, + 17, 13, 13, 17, 13, 17, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 15, + 15, 15, 15, 15, 15, 15, 16, 15, + 16, 16, 16, 16, 16, 16, 17, 16, + 16, 16, 16, 17, 16, 17, 16, 17, + 17, 17, +}; + +static const uint32_t coef1_huffcodes[555] = { + 0x00115, 0x00002, 0x00001, 0x00000, 0x0000d, 0x00007, 0x00013, 0x0001d, + 0x00008, 0x0000c, 0x00023, 0x0002b, 0x0003f, 0x00017, 0x0001b, 0x00043, + 0x00049, 0x00050, 0x00055, 0x00054, 0x00067, 0x00064, 0x0007b, 0x0002d, + 0x00028, 0x0002a, 0x00085, 0x00089, 0x0002b, 0x00035, 0x00090, 0x00091, + 0x00094, 0x00088, 0x000c1, 0x000c6, 0x000f2, 0x000e3, 0x000c5, 0x000e2, + 0x00036, 0x000f0, 0x000a7, 0x000cd, 0x000fb, 0x00059, 0x00116, 0x00103, + 0x00108, 0x0012b, 0x0012d, 0x00188, 0x0012e, 0x0014c, 0x001c3, 0x00187, + 0x001e7, 0x0006f, 0x00094, 0x00069, 0x001e6, 0x001ca, 0x00147, 0x00195, + 0x000a7, 0x00213, 0x00209, 0x00303, 0x00295, 0x00289, 0x0028c, 0x0028d, + 0x00312, 0x00330, 0x0029b, 0x00308, 0x00328, 0x0029a, 0x0025e, 0x003c5, + 0x00384, 0x0039f, 0x00397, 0x00296, 0x0032e, 0x00332, 0x003c6, 0x003e6, + 0x0012d, 0x000d1, 0x00402, 0x000dd, 0x00161, 0x0012b, 0x00127, 0x0045d, + 0x00601, 0x004ab, 0x0045f, 0x00410, 0x004bf, 0x00528, 0x0045c, 0x00424, + 0x00400, 0x00511, 0x00618, 0x0073d, 0x0063a, 0x00614, 0x0073c, 0x007c0, + 0x007cf, 0x00802, 0x00966, 0x00964, 0x00951, 0x008a0, 0x00346, 0x00803, + 0x00a52, 0x0024a, 0x007c1, 0x0063f, 0x00126, 0x00406, 0x00789, 0x008a2, + 0x00960, 0x00967, 0x00c05, 0x00c70, 0x00c79, 0x00a5d, 0x00c26, 0x00c4d, + 0x00372, 0x008a5, 0x00c08, 0x002c5, 0x00f11, 0x00cc4, 0x00f8e, 0x00e16, + 0x00496, 0x00e77, 0x00f9c, 0x00c25, 0x00f1e, 0x00c27, 0x00f1f, 0x00e17, + 0x00ccd, 0x00355, 0x00c09, 0x00c78, 0x00f90, 0x00521, 0x00357, 0x00356, + 0x0068e, 0x00f9d, 0x00c04, 0x00e58, 0x00a20, 0x00a2c, 0x00c4c, 0x0052f, + 0x00f8d, 0x01178, 0x01053, 0x01097, 0x0180f, 0x0180d, 0x012fb, 0x012aa, + 0x0202a, 0x00a40, 0x018ed, 0x01ceb, 0x01455, 0x018e3, 0x012a1, 0x00354, + 0x00353, 0x00f1c, 0x00c7b, 0x00c37, 0x0101d, 0x012cb, 0x01142, 0x0197d, + 0x01095, 0x01e3b, 0x0186b, 0x00588, 0x01c2a, 0x014b8, 0x01e3a, 0x018ec, + 0x01f46, 0x012fa, 0x00a53, 0x01ce8, 0x00a55, 0x01c29, 0x0117b, 0x01052, + 0x012a0, 0x00589, 0x00950, 0x01c2b, 0x00a50, 0x0208b, 0x0180e, 0x02027, + 0x02556, 0x01e20, 0x006e7, 0x01c28, 0x0197a, 0x00684, 0x020a2, 0x01f22, + 0x03018, 0x039cf, 0x03e25, 0x02557, 0x0294c, 0x028a6, 0x00d11, 0x028a9, + 0x02979, 0x00d46, 0x00a56, 0x039ce, 0x030cc, 0x0329a, 0x0149d, 0x0510f, + 0x0451c, 0x02028, 0x03299, 0x01ced, 0x014b9, 0x00f85, 0x00c7a, 0x01800, + 0x00341, 0x012ca, 0x039c8, 0x0329d, 0x00d0d, 0x03e20, 0x05144, 0x00d45, + 0x030d0, 0x0186d, 0x030d5, 0x00d0f, 0x00d40, 0x04114, 0x020a1, 0x0297f, + 0x03e24, 0x032f1, 0x04047, 0x030d4, 0x028a8, 0x00d0e, 0x0451d, 0x04044, + 0x0297e, 0x04042, 0x030d2, 0x030cf, 0x03e21, 0x03e26, 0x028a5, 0x0451a, + 0x00d48, 0x01a16, 0x00d44, 0x04518, 0x0149b, 0x039ca, 0x01498, 0x0403d, + 0x0451b, 0x0149c, 0x032f3, 0x030cb, 0x08073, 0x03e22, 0x0529a, 0x020aa, + 0x039cc, 0x0738a, 0x06530, 0x07389, 0x06193, 0x08071, 0x04043, 0x030ce, + 0x05147, 0x07388, 0x05145, 0x08072, 0x04521, 0x00d47, 0x0297c, 0x030cd, + 0x030ca, 0x0000b, 0x0000c, 0x00083, 0x000e4, 0x00048, 0x00102, 0x001cc, + 0x001f5, 0x00097, 0x0020b, 0x00124, 0x00453, 0x00627, 0x00639, 0x00605, + 0x00517, 0x001b8, 0x00663, 0x00667, 0x007c3, 0x00823, 0x00961, 0x00963, + 0x00e5a, 0x00e59, 0x00a2b, 0x00cbf, 0x00292, 0x00a2d, 0x007d0, 0x00953, + 0x00cc5, 0x00f84, 0x004ab, 0x014a7, 0x0068a, 0x0117a, 0x0052e, 0x01442, + 0x0052c, 0x00c77, 0x00f8f, 0x004aa, 0x01094, 0x01801, 0x012c4, 0x0297b, + 0x00952, 0x01f19, 0x006a5, 0x01149, 0x012c5, 0x01803, 0x022f2, 0x0329b, + 0x04520, 0x0149e, 0x00d13, 0x01f16, 0x01ce9, 0x0101c, 0x006e6, 0x039c9, + 0x06191, 0x07c8e, 0x06192, 0x0ca63, 0x039cd, 0x06190, 0x06884, 0x06885, + 0x07382, 0x00d49, 0x00d41, 0x0450c, 0x0149a, 0x030d1, 0x08077, 0x03e23, + 0x01a15, 0x0e701, 0x0e702, 0x08079, 0x0822a, 0x0a218, 0x07887, 0x0403f, + 0x0520b, 0x0529b, 0x0e700, 0x04519, 0x00007, 0x000e0, 0x000d0, 0x0039b, + 0x003e5, 0x00163, 0x0063e, 0x007c9, 0x00806, 0x00954, 0x01044, 0x01f44, + 0x0197c, 0x01f45, 0x00a51, 0x01f47, 0x00951, 0x0052d, 0x02291, 0x0092f, + 0x00a54, 0x00d12, 0x0297d, 0x00d0c, 0x01499, 0x0329e, 0x032f0, 0x02025, + 0x039c6, 0x00a57, 0x03e46, 0x00d42, 0x0738b, 0x05146, 0x04046, 0x08078, + 0x0510e, 0x07886, 0x02904, 0x04156, 0x04157, 0x06032, 0x030d3, 0x08bce, + 0x04040, 0x0403e, 0x0a414, 0x10457, 0x08075, 0x06887, 0x07c8f, 0x039c7, + 0x07387, 0x08070, 0x08bcf, 0x1482a, 0x10456, 0x1482b, 0x01a17, 0x06886, + 0x0450d, 0x00013, 0x0006b, 0x00615, 0x0080b, 0x0082b, 0x00952, 0x00e5b, + 0x018e2, 0x0186c, 0x01f18, 0x0329f, 0x00d43, 0x03e29, 0x05140, 0x05141, + 0x0ca62, 0x06033, 0x03c42, 0x03e28, 0x0450f, 0x0a21a, 0x07384, 0x0a219, + 0x0e703, 0x0a21b, 0x01a14, 0x07383, 0x045e6, 0x0007a, 0x0012c, 0x00ccc, + 0x0068f, 0x01802, 0x00a52, 0x00953, 0x04045, 0x01a20, 0x0451f, 0x000a4, + 0x00735, 0x01cec, 0x02029, 0x020a3, 0x0451e, 0x00069, 0x00c24, 0x02024, + 0x032f2, 0x05142, 0x00196, 0x00523, 0x000a6, 0x0197b, 0x0030b, 0x0092e, + 0x003e9, 0x03e27, 0x00160, 0x05143, 0x00652, 0x04041, 0x00734, 0x028a7, + 0x0080f, 0x01483, 0x0097c, 0x00340, 0x0068b, 0x00522, 0x01054, 0x01096, + 0x01f17, 0x0202b, 0x01cea, 0x020a0, 0x02978, 0x02026, 0x0297a, 0x039cb, + 0x03e2b, 0x0149f, 0x0329c, 0x07385, 0x08074, 0x0450e, 0x03e2a, 0x05149, + 0x08076, 0x07386, 0x05148, +}; + +static const uint8_t coef1_huffbits[555] = { + 9, 5, 2, 4, 4, 5, 5, 5, + 6, 6, 6, 6, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 9, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 10, 10, 10, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, + 11, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 11, 11, 11, 11, 11, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 13, 12, 12, 12, 12, 12, 12, 12, + 13, 12, 12, 12, 12, 12, 12, 12, + 12, 13, 12, 12, 12, 13, 13, 13, + 13, 12, 12, 12, 12, 12, 12, 13, + 12, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 13, 13, 13, 13, 13, 13, + 13, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 14, 13, 14, 13, 13, 13, + 13, 13, 14, 13, 14, 14, 13, 14, + 14, 13, 14, 13, 13, 14, 14, 13, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 15, 14, 14, 14, 14, 15, 15, + 15, 14, 14, 13, 13, 12, 12, 13, + 13, 13, 14, 14, 15, 14, 15, 15, + 14, 13, 14, 15, 15, 15, 14, 14, + 14, 14, 15, 14, 14, 15, 15, 15, + 14, 15, 14, 14, 14, 14, 14, 15, + 15, 16, 15, 15, 15, 14, 15, 15, + 15, 15, 14, 14, 16, 14, 15, 14, + 14, 15, 15, 15, 15, 16, 15, 14, + 15, 15, 15, 16, 15, 15, 14, 14, + 14, 4, 7, 8, 8, 9, 9, 9, + 9, 10, 10, 11, 11, 11, 11, 11, + 11, 12, 11, 11, 11, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 11, 12, + 12, 12, 13, 13, 13, 13, 13, 13, + 13, 12, 12, 13, 13, 13, 13, 14, + 14, 13, 14, 13, 13, 13, 14, 14, + 15, 15, 14, 13, 13, 13, 14, 14, + 15, 15, 15, 16, 14, 15, 17, 17, + 15, 15, 15, 15, 15, 14, 16, 14, + 16, 16, 16, 16, 16, 16, 15, 15, + 17, 15, 16, 15, 6, 8, 10, 10, + 10, 11, 11, 11, 12, 12, 13, 13, + 13, 13, 14, 13, 14, 13, 14, 14, + 14, 14, 14, 15, 15, 14, 14, 14, + 14, 14, 14, 15, 15, 15, 15, 16, + 15, 15, 16, 15, 15, 15, 14, 16, + 15, 15, 18, 17, 16, 17, 15, 14, + 15, 16, 16, 19, 17, 19, 16, 17, + 15, 7, 10, 11, 12, 12, 12, 12, + 13, 13, 13, 14, 15, 14, 15, 15, + 16, 15, 14, 14, 15, 16, 15, 16, + 16, 16, 16, 15, 15, 7, 11, 12, + 13, 13, 14, 14, 15, 15, 15, 8, + 11, 13, 14, 14, 15, 9, 12, 14, + 14, 15, 9, 13, 10, 13, 10, 14, + 10, 14, 11, 15, 11, 15, 11, 14, + 12, 15, 12, 13, 13, 13, 13, 13, + 13, 14, 13, 14, 14, 14, 14, 14, + 14, 15, 14, 15, 16, 15, 14, 15, + 16, 15, 15, +}; + +static const uint32_t coef2_huffcodes[1336] = { + 0x003e6, 0x000f6, 0x00000, 0x00002, 0x00006, 0x0000f, 0x0001b, 0x00028, + 0x00039, 0x0003f, 0x0006b, 0x00076, 0x000b7, 0x000e8, 0x000ef, 0x00169, + 0x001a7, 0x001d4, 0x001dc, 0x002c4, 0x00349, 0x00355, 0x00391, 0x003dc, + 0x00581, 0x005b2, 0x00698, 0x0070c, 0x00755, 0x0073a, 0x00774, 0x007cf, + 0x00b0a, 0x00b66, 0x00d2e, 0x00d5e, 0x00e1b, 0x00eac, 0x00e5a, 0x00f7e, + 0x00fa1, 0x0163e, 0x01a37, 0x01a52, 0x01c39, 0x01ab3, 0x01d5f, 0x01cb6, + 0x01f52, 0x01dd9, 0x02c04, 0x02c2e, 0x02c2d, 0x02c23, 0x03467, 0x034a3, + 0x0351b, 0x03501, 0x03a5d, 0x0351c, 0x03875, 0x03dea, 0x0397b, 0x039db, + 0x03df1, 0x039d8, 0x03bb4, 0x0580a, 0x0584d, 0x05842, 0x05b13, 0x058ea, + 0x0697d, 0x06a06, 0x068cc, 0x06ac7, 0x06a96, 0x072f4, 0x07543, 0x072b4, + 0x07d20, 0x0b003, 0x073b5, 0x07be6, 0x0d180, 0x07bd1, 0x07cb8, 0x07d06, + 0x07d25, 0x0d2f2, 0x0d19a, 0x0d334, 0x0e1dc, 0x0d529, 0x0d584, 0x0e1d2, + 0x0e5e3, 0x0eec4, 0x0e564, 0x0fa49, 0x16001, 0x0eedc, 0x0f7fa, 0x1a32c, + 0x16131, 0x16003, 0x0f9c8, 0x1ef80, 0x1d2a0, 0x1aa4b, 0x0f7ce, 0x1abfe, + 0x1aa50, 0x1a458, 0x1a816, 0x1cae4, 0x1d2fe, 0x1d52e, 0x1aa4c, 0x2c245, + 0x1d2a1, 0x1a35d, 0x1ca1b, 0x1d5d8, 0x1f531, 0x1ca1c, 0x1f389, 0x1f4af, + 0x3a5e7, 0x351fb, 0x2c24b, 0x34bce, 0x2c24d, 0x2c249, 0x2c24a, 0x72dfc, + 0x357ef, 0x35002, 0x3a5e6, 0x39431, 0x5843b, 0x34a77, 0x58431, 0x3a5f3, + 0x3a5dd, 0x3e5e5, 0x356bd, 0x3976e, 0x6a3d2, 0x3500d, 0x694c4, 0x580bd, + 0x3e5e8, 0x74b95, 0x34a6e, 0x3977c, 0x39432, 0x5b0d2, 0x6a3d8, 0x580b8, + 0x5b0cb, 0x5b0d7, 0x72dee, 0x72ded, 0x72dec, 0x74b9c, 0x3977f, 0x72dea, + 0x74b9e, 0x7be7d, 0x580bf, 0x5b0d5, 0x7cba8, 0x74b91, 0x3e5dd, 0xb6171, + 0xd46b3, 0xd46b9, 0x7cba1, 0x74b9f, 0x72de1, 0xe59f5, 0x3e5eb, 0x00004, + 0x00015, 0x00038, 0x00075, 0x000e8, 0x001d3, 0x00347, 0x0039c, 0x00690, + 0x0074a, 0x00b60, 0x00e93, 0x00f74, 0x0163d, 0x01a5a, 0x01d24, 0x01cbe, + 0x01f4b, 0x03468, 0x03562, 0x03947, 0x03e82, 0x05804, 0x05b12, 0x05803, + 0x0696d, 0x06a9e, 0x0697c, 0x06978, 0x06afb, 0x074b2, 0x072f5, 0x073c0, + 0x07541, 0x06944, 0x074b7, 0x070d3, 0x07ba9, 0x0b0b1, 0x0d1af, 0x0e1dd, + 0x0e5e2, 0x0e1a3, 0x0eec3, 0x1612f, 0x0e961, 0x0eeda, 0x0e78e, 0x0fa48, + 0x1612c, 0x0e511, 0x0e565, 0x0e953, 0x1aa4a, 0x0e59d, 0x1d52c, 0x1a811, + 0x1cae7, 0x1abfc, 0x1d52d, 0x1cacf, 0x1cf05, 0x2c254, 0x34a72, 0x1f4ac, + 0x3976b, 0x34a71, 0x2c6d9, 0x2d873, 0x34a6a, 0x357e7, 0x3464c, 0x3e5f5, + 0x58433, 0x1f53a, 0x3500a, 0x357ea, 0x34a73, 0x3942f, 0x357e5, 0x39775, + 0x694cd, 0x39772, 0x7cba5, 0x6a3ef, 0x35483, 0x74b98, 0x5b0c1, 0x39770, + 0x3a5d7, 0x39433, 0x39434, 0x694ce, 0x580be, 0x3e5ff, 0x6a3ec, 0xb616f, + 0xd46b1, 0x6a3d1, 0x72de5, 0x74b6e, 0x72de9, 0x3e700, 0xd46b6, 0x6a3e9, + 0x74b69, 0xe5675, 0xd46b8, 0x7cbaa, 0x3a5d1, 0x0000c, 0x0003c, 0x000eb, + 0x001f1, 0x003a4, 0x006a8, 0x007d5, 0x00d43, 0x00e77, 0x016c5, 0x01cb1, + 0x02c5d, 0x03a55, 0x03a56, 0x03e51, 0x03bb5, 0x05b0a, 0x06a9f, 0x074b8, + 0x07d28, 0x0d187, 0x0d40e, 0x0d52e, 0x0d425, 0x0eae3, 0x0e1d3, 0x1612e, + 0x0e59e, 0x0eec2, 0x0e578, 0x0e51a, 0x0e579, 0x0e515, 0x0e960, 0x0d183, + 0x0d220, 0x0d2cb, 0x0e512, 0x16c3e, 0x16002, 0x16c42, 0x1cae9, 0x3461a, + 0x1d2fa, 0x1a308, 0x1a849, 0x1cf07, 0x1f38f, 0x34b65, 0x2c253, 0x1ef9e, + 0x1cbc3, 0x1cbc1, 0x2c255, 0x1f384, 0x58435, 0x2c5cd, 0x3a5f7, 0x2c252, + 0x3959c, 0x2c6d8, 0x3a5d3, 0x6ad78, 0x6a3f2, 0x7cba9, 0xb6176, 0x72deb, + 0x39764, 0x3e5f6, 0x3a5d8, 0x74a8c, 0x6a3e6, 0x694d1, 0x6ad79, 0x1a4592, + 0xe59fb, 0x7cbb3, 0x5b0cd, 0x00017, 0x000b5, 0x002c3, 0x005b7, 0x00b1c, + 0x00e5c, 0x0163f, 0x01ab2, 0x01efa, 0x0348a, 0x0396e, 0x058da, 0x06963, + 0x06a30, 0x072cd, 0x073cf, 0x07ce7, 0x0d2ca, 0x0d2d8, 0x0e764, 0x0e794, + 0x16008, 0x16167, 0x1617e, 0x1aa49, 0x1a30b, 0x1a813, 0x2c6da, 0x1a580, + 0x1cbc2, 0x0f9ca, 0x1617f, 0x1d2fe, 0x0f7fc, 0x16c40, 0x0e513, 0x0eec5, + 0x0f7c3, 0x1d508, 0x1a81e, 0x1d2fd, 0x39430, 0x35486, 0x3e5fd, 0x2c24c, + 0x2c75a, 0x34a74, 0x3a5f4, 0x3464d, 0x694ca, 0x3a5f1, 0x1d509, 0x1d5c0, + 0x34648, 0x3464e, 0x6a3d5, 0x6a3e8, 0x6a3e7, 0x5b0c3, 0x2c248, 0x1f38a, + 0x3a5f2, 0x6a3e5, 0x00029, 0x00168, 0x0058c, 0x00b67, 0x00f9d, 0x01c3d, + 0x01cbf, 0x02c20, 0x0351d, 0x03df6, 0x06af9, 0x072b5, 0x0b1d7, 0x0b0b2, + 0x0d40a, 0x0d52b, 0x0e952, 0x0e797, 0x163c3, 0x1c3a0, 0x1f386, 0x1ca21, + 0x34655, 0x2c247, 0x1f53b, 0x2c250, 0x2c24f, 0x1f385, 0x1ef5d, 0x1cf15, + 0x1caea, 0x1ab0a, 0x1cf19, 0x1f53d, 0x1d5c2, 0x1d2fb, 0x1ef58, 0x34a78, + 0x357ec, 0x1f533, 0x3a5e1, 0x694d2, 0x58482, 0x3a5ee, 0x2c6dc, 0x357eb, + 0x5b0c4, 0x39778, 0x6a3e1, 0x7cbb4, 0x3a5e1, 0x74b68, 0x3a5ef, 0x3a5d2, + 0x39424, 0x72de2, 0xe59f6, 0xe59f7, 0x3e702, 0x3e5ec, 0x1f38b, 0x0003b, + 0x001f0, 0x00777, 0x00fa8, 0x01cb2, 0x02d84, 0x03a57, 0x03dd6, 0x06917, + 0x06a11, 0x07d07, 0x0eae2, 0x0e796, 0x0f9c9, 0x0f7fb, 0x16166, 0x16160, + 0x1ab1b, 0x1abfa, 0x2d87b, 0x1d2f7, 0x39768, 0x1f38c, 0x34653, 0x34651, + 0x6a3d9, 0x35001, 0x3abbd, 0x38742, 0x39426, 0x34a76, 0x3a5ec, 0x34a75, + 0x35000, 0x35488, 0x1cf10, 0x2c6db, 0x357ed, 0x357e8, 0x357e9, 0x3a5f0, + 0x694c2, 0xb6178, 0x72df5, 0x39425, 0x3942b, 0x74b6d, 0x74b6f, 0xb6177, + 0xb6179, 0x74b6a, 0xb6172, 0x58487, 0x3e5ee, 0x3e5ed, 0x72df2, 0x72df4, + 0x7cbae, 0x6a3ca, 0x70e86, 0x34bcf, 0x6a3c8, 0x00059, 0x00384, 0x00d5b, + 0x01c38, 0x03560, 0x0395b, 0x0584e, 0x06964, 0x073cd, 0x0b1e7, 0x0e798, + 0x0e78d, 0x0fa43, 0x1a848, 0x1a32f, 0x1aa4e, 0x3464a, 0x1f4ab, 0x1f38d, + 0x3a5eb, 0x3a5d4, 0x3548a, 0x6a3c7, 0x5b0d0, 0x6a3c5, 0x7cbb0, 0x694cb, + 0x3a5e5, 0x3e5e2, 0x3942c, 0x2d872, 0x1f4ae, 0x3a5d5, 0x694d3, 0x58481, + 0x35009, 0x39774, 0x58432, 0xb616c, 0x5b0db, 0x3548b, 0xb6174, 0x1d5d95, + 0xb004c, 0x7cbb2, 0x3a5e5, 0x74a8f, 0xe59f9, 0x72df6, 0xe59fd, 0x7cbad, + 0xd427d, 0x72cff, 0x3977a, 0x5b0d9, 0xb616d, 0xb616b, 0x1a4593, 0x7cbaf, + 0x5b0da, 0x00071, 0x003eb, 0x01603, 0x02c6c, 0x03961, 0x068c8, 0x06a31, + 0x072bd, 0x0d2c2, 0x0e51b, 0x0e5e6, 0x1abfb, 0x1d2ff, 0x1cae5, 0x1ef5c, + 0x1ef5e, 0x1cf13, 0x34a6d, 0x3976d, 0xb616a, 0x3e5f2, 0x6a3c4, 0xb6169, + 0x3e5dc, 0x580b9, 0x74b99, 0x75764, 0x58434, 0x3a5d9, 0x6945a, 0x69459, + 0x3548c, 0x3a5e9, 0x69457, 0x72df1, 0x6945e, 0x6a35e, 0x3e701, 0xb6168, + 0x5b0dd, 0x3a5de, 0x6a3c2, 0xd4278, 0x6a3cc, 0x72dfd, 0xb6165, 0x16009a, + 0x7cbb1, 0xd427c, 0xb6162, 0xe765e, 0x1cecbe, 0x7cbb6, 0x69454, 0xb6160, + 0xd427a, 0x1d5d96, 0xb1d6d, 0xe59f4, 0x72de8, 0x3a5db, 0x0007a, 0x006ae, + 0x01c3c, 0x03aba, 0x058e9, 0x072cc, 0x0d2dd, 0x0d22d, 0x0eec1, 0x0eedb, + 0x1d2a2, 0x1ef5b, 0x357e2, 0x3abbf, 0x1d2f9, 0x35004, 0x3a5dc, 0x351fc, + 0x3976c, 0x6a3c6, 0x6a3cb, 0x3e5ea, 0xe59f3, 0x6a3ce, 0x69452, 0xe59f0, + 0x74b90, 0xd4279, 0xd427b, 0x7cbb5, 0x5b0c5, 0x3a5e3, 0x3a5e2, 0x000d0, + 0x00775, 0x01efe, 0x03dd5, 0x0728c, 0x07cb9, 0x0e1a2, 0x0ea85, 0x0eed8, + 0x1a30a, 0x1aa4f, 0x3a5df, 0x35008, 0x3a5e0, 0x3e5f4, 0x3e5f7, 0xb1d6c, + 0x5843e, 0x34a70, 0x72df8, 0x74b6b, 0xd427f, 0x72df0, 0x5b0bf, 0x5b0c0, + 0xd46b0, 0x72def, 0xe59f8, 0x162e64, 0xb1d6f, 0x3a5e0, 0x39427, 0x69166, + 0x6a3e2, 0x6a3e3, 0x74a8d, 0xd427e, 0x1d5d97, 0xd46b4, 0x5b0d8, 0x6a3d3, + 0x000e0, 0x00b63, 0x034cc, 0x06a33, 0x073c9, 0x0e1a0, 0x0f7fd, 0x0f9cc, + 0x1617d, 0x1caeb, 0x1f4a9, 0x3abb3, 0x69450, 0x39420, 0x39777, 0x3e5e0, + 0x6a3d4, 0x6a3ed, 0xb6166, 0xe59f1, 0xb1d6e, 0xe5676, 0x6a3ea, 0xe5674, + 0xb6163, 0xd46b7, 0x7cba6, 0xd46ba, 0x1d5d94, 0xb6164, 0x6a3f1, 0x7cba2, + 0x69451, 0x72dfa, 0xd46bb, 0x72df7, 0x74b94, 0x1cecbf, 0xe59fa, + 0x16009b, + 0x6a3e4, 0x000e6, 0x00e94, 0x03876, 0x070ef, 0x0d52a, 0x16015, 0x16014, + 0x1abf9, 0x1cf17, 0x34a79, 0x34650, 0x3e705, 0x6a3d0, 0x58430, 0x74b9d, + 0x7be7e, 0x5b0be, 0x39773, 0x6a3de, 0x000fb, 0x00f7b, 0x03dd7, 0x07bd0, + 0x0e59c, 0x0f9cd, 0x1cf18, 0x1d2ff, 0x34a7a, 0x39429, 0x3500c, 0x72de0, + 0x69456, 0x7be7c, 0xd46b5, 0xd46b2, 0x6a3dd, 0x001a2, 0x0163b, 0x06913, + 0x0b016, 0x0fa42, 0x1a32d, 0x1cf06, 0x34a7c, 0x34a7d, 0xb6161, 0x35481, + 0x3e5fa, 0x7cba0, 0x7be7f, 0x7cba3, 0x7cba7, 0x5b0d3, 0x72de6, 0x6a3dc, + 0x001a9, 0x01ab4, 0x06a34, 0x0d46a, 0x16130, 0x1ef5f, 0x1f532, 0x1f536, + 0x3942e, 0x58436, 0x6a3db, 0x6945b, 0x001c9, 0x01ca0, 0x0728b, 0x0eed9, + 0x1f539, 0x1ca1d, 0x39765, 0x39766, 0x58439, 0x6945d, 0x39767, 0x001d3, + 0x01f2c, 0x07bfc, 0x16161, 0x34652, 0x3a5ed, 0x3548d, 0x58438, 0x6a3da, + 0x002c1, 0x02c5e, 0x0d335, 0x1ab1a, 0x2d874, 0x35006, 0x35484, 0x5b0cc, + 0x74b9a, 0x72df3, 0x6a3d6, 0x002da, 0x034b3, 0x0d5ae, 0x1caee, 0x2d871, + 0x357e3, 0x74b97, 0x72df9, 0x580ba, 0x5b0d4, 0x0034d, 0x0354e, 0x0f750, + 0x1cbc0, 0x3a5e7, 0x3a5e4, 0x00385, 0x03a58, 0x16c41, 0x2c5cf, 0x3e5e1, + 0x74b6c, 0xe5677, 0x6a3df, 0x00390, 0x03e50, 0x163c2, 0x2d876, 0x35482, + 0x5b0d6, 0x5843a, 0x0039f, 0x0585e, 0x1a583, 0x3500f, 0x74b93, 0x39771, + 0x003e4, 0x06912, 0x16c43, 0x357e1, 0x0058a, 0x0696f, 0x1f538, 0x5b0c9, + 0x6a3cf, 0x005b6, 0x06af8, 0x1f534, 0x58483, 0x6a3e0, 0x00695, 0x07d02, + 0x1cae8, 0x58485, 0x006a2, 0x0754a, 0x357ee, 0x3977b, 0x00748, 0x074b2, + 0x34a7b, 0x00729, 0x0b1e0, 0x34649, 0x3e5e3, 0x0073d, 0x0d2c4, 0x3e5e6, + 0x007bb, 0x0b099, 0x39762, 0x5b0ce, 0x6945f, 0x007d1, 0x0d5ab, 0x39779, + 0x007d3, 0x0d52f, 0x39763, 0x6945c, 0x00b1a, 0x0d2c5, 0x35489, 0x00d23, + 0x0eaed, 0x3e5f8, 0x00d32, 0x16016, 0x3e5fb, 0x00d41, 0x0e768, 0x3a5ed, + 0x00e1f, 0x16017, 0x58027, 0x00ead, 0x0fa07, 0x69455, 0x00e54, 0x1612b, + 0x00e55, 0x1a581, 0x00f78, 0x1a32b, 0x580bc, 0x6a3ee, 0x00f79, 0x1abfd, + 0x00f95, 0x1ab18, 0x6a3f0, 0x01637, 0x1aa4d, 0x0162d, 0x1f53c, 0x6a3f3, + 0x01a31, 0x1a810, 0x39769, 0x01a50, 0x1caef, 0x01a36, 0x1a32e, 0x01a67, + 0x1f38e, 0x01a85, 0x1ef59, 0x01aa6, 0x1ef83, 0x01d51, 0x2c012, 0x01d53, + 0x2d879, 0x01d5e, 0x35005, 0x01cba, 0x1cf04, 0x69453, 0x01d2d, 0x351ff, + 0x01f2d, 0x2d86f, 0x01f29, 0x35007, 0x02c22, 0x351fa, 0x02c03, 0x3a5ec, + 0x02c5f, 0x3a5eb, 0x02c58, 0x34a6b, 0x03469, 0x356be, 0x02c59, 0x34a6c, + 0x0346a, 0x3a5ea, 0x034bd, 0x034bf, 0x356bf, 0x0386a, 0x03ab9, 0x5843f, + 0x0386b, 0x3a5f5, 0x03a4b, 0x39421, 0x03aa4, 0x3a5e9, 0x03a5a, 0x03960, + 0x3977e, 0x03de9, 0x03958, 0x03df7, 0x039e1, 0x3e5e4, 0x0395f, 0x69458, + 0x03e91, 0x03df2, 0x39428, 0x058f2, 0x03e80, 0x6a3c3, 0x03e93, 0x694c0, + 0x058b8, 0x5b0ca, 0x0584f, 0x694c1, 0x058f1, 0x068d6, 0x06a10, 0x06ac3, + 0x06a32, 0x070d2, 0x06911, 0x074b1, 0x07494, 0x06ad4, 0x06ad6, 0x072b8, + 0x06afa, 0x074b3, 0x07540, 0x073ce, 0x0b005, 0x074b3, 0x07495, 0x074b9, + 0x0d336, 0x07bff, 0x07763, 0x073c8, 0x07d29, 0x0b622, 0x0d221, 0x0d181, + 0x0b1d1, 0x074b8, 0x0b1d0, 0x0d19b, 0x0d2c3, 0x0b172, 0x0d2dc, 0x0b623, + 0x0d5aa, 0x0d426, 0x0d182, 0x0e795, 0x0e1d1, 0x0d337, 0x0e96c, 0x0e5e4, + 0x0e514, 0x0eaee, 0x16000, 0x0e767, 0x0e1a1, 0x0e78f, 0x16004, 0x0f7c2, + 0x0e799, 0x0e5e7, 0x0e566, 0x0e769, 0x0f751, 0x0eede, 0x0fa06, 0x16005, + 0x0fa9f, 0x1a5e6, 0x0e766, 0x1636f, 0x0eedd, 0x0eec0, 0x1a309, 0x1ceca, + 0x163cd, 0x0f9cb, 0x0eedf, 0x1a582, 0x1612d, 0x0e5e5, 0x1abf8, 0x1a30c, + 0x1ca1f, 0x163cc, 0x1a35c, 0x1ca1e, 0x1aa51, 0x163ac, 0x1a84e, 0x1a53f, + 0x1cf16, 0x1d2fc, 0x1a5b3, 0x1ab19, 0x1a81f, 0x1d5c3, 0x16c3f, 0x1d5c1, + 0x1d2fc, 0x1f4aa, 0x1a812, 0x1f535, 0x1cf12, 0x1a817, 0x1617c, 0x1ab0b, + 0x1d2f8, 0x1ef82, 0x2d87a, 0x1d52f, 0x1f530, 0x1aa48, 0x35487, 0x1d2fd, + 0x1f4ad, 0x1cf11, 0x3461b, 0x35485, 0x1ca20, 0x1caed, 0x1cae6, 0x1abff, + 0x3464f, 0x34a6f, 0x1ef81, 0x3464b, 0x39d96, 0x1f383, 0x1f537, 0x1cf14, + 0x2c5ce, 0x3500e, 0x2c251, 0x1caec, 0x1f387, 0x34654, 0x357e4, 0x2d878, + 0x3500b, 0x35480, 0x3a5e8, 0x3548e, 0x34b64, 0x1f4a8, 0x35003, 0x3e5df, + 0x2d870, 0x357e6, 0x3e5f0, 0x1ef5a, 0x3a5ea, 0x1f388, 0x3e703, 0x2c24e, + 0x3a5e2, 0x351fd, 0x2c6dd, 0x3e704, 0x351fe, 0x2d875, 0x5b0c7, 0x3976a, + 0x3a5e6, 0x39423, 0x58480, 0x2c246, 0x3a5e3, 0x2d877, 0x3e5f1, 0x3abbe, + 0x58489, 0x3e5f9, 0x357e0, 0x3abbc, 0x5b0c6, 0x69167, 0x69165, 0x3e5e9, + 0x39422, 0x3976f, 0x3977d, 0x3e5de, 0x6a3c9, 0x58b98, 0x3a5f6, 0x3a5d0, + 0x58486, 0x6a3c1, 0x3e5fc, 0x5b0dc, 0x3548f, 0x3942d, 0x694c9, 0x58484, + 0x3a5e8, 0x74b9b, 0x74b96, 0x694d0, 0x58488, 0x3a5e4, 0x3942a, 0x72ec2, + 0x39776, 0x5b0d1, 0x5b0cf, 0x3a5d6, 0xe59fc, 0x5b0c8, 0x3e5e7, 0x7cbb7, + 0x70e87, 0x7cbab, 0x5b0c2, 0x694c3, 0x74a8e, 0x3e5f3, 0x6a3cd, 0x72dfe, + 0x73b2e, 0x72ec0, 0x694c5, 0x58437, 0x694c8, 0x72dff, 0x39435, 0x5843d, + 0x6a3d7, 0x72ec1, 0xd22c8, 0x694cf, 0xb6173, 0x3e5fe, 0x580bb, 0xe59f2, + 0xb616e, 0xb6175, 0x3a5da, 0x5b0bd, 0x694cc, 0x5843c, 0x694c7, 0x74b92, + 0x72ec3, 0x694c6, 0xb6170, 0x7cbac, 0xb1733, 0x7cba4, 0xb6167, 0x72de7, + 0x72de4, 0x6a3c0, 0x3e5ef, 0x162e65, 0x72de3, 0x72dfb, 0x6a35f, 0x6a3eb, +}; + +static const uint8_t coef2_huffbits[1336] = { + 11, 9, 2, 3, 4, 4, 5, 6, + 6, 7, 7, 8, 8, 8, 9, 9, + 9, 9, 10, 10, 10, 10, 11, 11, + 11, 11, 11, 11, 11, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 16, 15, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 18, 17, 17, 17, 17, + 17, 17, 17, 18, 18, 17, 17, 18, + 17, 17, 18, 17, 18, 18, 18, 18, + 19, 18, 18, 18, 18, 18, 18, 20, + 18, 18, 18, 19, 19, 18, 19, 18, + 19, 19, 18, 19, 19, 18, 19, 19, + 19, 19, 18, 19, 19, 19, 19, 19, + 19, 19, 20, 20, 20, 19, 19, 20, + 19, 20, 19, 19, 20, 19, 19, 20, + 20, 20, 20, 19, 20, 21, 19, 3, + 5, 7, 8, 9, 9, 10, 11, 11, + 12, 12, 12, 13, 13, 13, 13, 14, + 14, 14, 14, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 16, 16, + 15, 15, 15, 15, 16, 16, 16, 16, + 17, 16, 17, 17, 16, 17, 17, 17, + 17, 17, 17, 16, 17, 17, 17, 17, + 18, 17, 17, 18, 18, 18, 18, 18, + 19, 18, 18, 18, 18, 18, 18, 19, + 19, 18, 18, 18, 18, 19, 18, 19, + 19, 19, 20, 19, 18, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 20, + 20, 19, 20, 19, 20, 19, 20, 19, + 19, 21, 20, 20, 19, 4, 7, 8, + 10, 11, 11, 12, 12, 13, 13, 14, + 14, 14, 14, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 16, 16, + 16, 16, 17, 17, 17, 17, 18, 18, + 18, 17, 17, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 19, 18, 18, 18, + 19, 18, 19, 19, 19, 20, 20, 20, + 19, 19, 19, 19, 19, 19, 19, 21, + 21, 20, 19, 5, 8, 10, 11, 12, + 13, 13, 13, 14, 14, 15, 15, 15, + 15, 16, 16, 16, 16, 16, 17, 17, + 17, 17, 17, 17, 17, 17, 18, 17, + 18, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 19, 18, 19, 18, + 18, 18, 18, 18, 19, 18, 17, 17, + 18, 18, 19, 19, 19, 19, 18, 18, + 18, 19, 6, 9, 11, 12, 13, 13, + 14, 14, 14, 15, 15, 16, 16, 16, + 16, 16, 16, 17, 17, 17, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, + 18, 17, 18, 18, 17, 18, 18, 18, + 18, 18, 18, 19, 19, 18, 18, 18, + 19, 19, 19, 20, 19, 19, 18, 19, + 19, 20, 21, 21, 19, 19, 18, 6, + 10, 12, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 17, 17, 17, 17, 17, + 17, 17, 18, 18, 19, 18, 18, 18, + 19, 18, 18, 18, 19, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, + 19, 20, 20, 19, 19, 19, 19, 20, + 20, 19, 20, 19, 19, 19, 20, 20, + 20, 19, 19, 18, 19, 7, 10, 12, + 13, 14, 15, 15, 15, 16, 16, 17, + 17, 17, 17, 17, 17, 18, 18, 18, + 18, 19, 18, 19, 19, 19, 20, 19, + 18, 19, 19, 18, 18, 19, 19, 19, + 18, 19, 19, 20, 19, 18, 20, 21, + 20, 20, 19, 19, 21, 20, 21, 20, + 20, 20, 19, 19, 20, 20, 21, 20, + 19, 7, 11, 13, 14, 15, 15, 15, + 16, 16, 17, 17, 17, 17, 18, 18, + 18, 18, 18, 19, 20, 19, 19, 20, + 19, 19, 19, 19, 19, 19, 19, 19, + 18, 18, 19, 20, 19, 19, 19, 20, + 19, 19, 19, 20, 19, 20, 20, 21, + 20, 20, 20, 21, 22, 20, 19, 20, + 20, 21, 20, 21, 20, 19, 8, 11, + 13, 14, 15, 16, 16, 16, 17, 17, + 17, 18, 18, 18, 18, 18, 19, 18, + 19, 19, 19, 19, 21, 19, 19, 21, + 19, 20, 20, 20, 19, 18, 18, 8, + 12, 14, 15, 16, 16, 16, 16, 17, + 17, 17, 19, 18, 18, 19, 19, 20, + 19, 18, 20, 19, 20, 20, 19, 19, + 20, 20, 21, 21, 20, 19, 19, 19, + 19, 19, 19, 20, 21, 20, 19, 19, + 8, 12, 14, 15, 16, 16, 17, 17, + 17, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 20, 21, 20, 21, 19, 21, + 20, 20, 20, 20, 21, 20, 19, 20, + 19, 20, 20, 20, 19, 22, 21, 21, + 19, 9, 12, 14, 15, 16, 17, 17, + 17, 18, 18, 18, 19, 19, 19, 19, + 20, 19, 19, 19, 9, 13, 15, 16, + 17, 17, 18, 18, 18, 19, 18, 20, + 19, 20, 20, 20, 19, 9, 13, 15, + 16, 17, 17, 18, 18, 18, 20, 18, + 19, 20, 20, 20, 20, 19, 20, 19, + 9, 13, 15, 16, 17, 18, 18, 18, + 19, 19, 19, 19, 10, 14, 16, 17, + 18, 18, 19, 19, 19, 19, 19, 10, + 14, 16, 17, 18, 18, 18, 19, 19, + 10, 14, 16, 17, 18, 18, 18, 19, + 19, 20, 19, 10, 14, 16, 18, 18, + 18, 19, 20, 19, 19, 10, 14, 17, + 18, 18, 18, 10, 15, 17, 18, 19, + 19, 21, 19, 11, 15, 17, 18, 18, + 19, 19, 11, 15, 17, 18, 19, 19, + 11, 15, 17, 18, 11, 15, 18, 19, + 19, 11, 15, 18, 19, 19, 11, 16, + 18, 19, 11, 15, 18, 19, 11, 16, + 18, 12, 16, 18, 19, 12, 16, 19, + 12, 16, 19, 19, 19, 12, 16, 19, + 12, 16, 19, 19, 12, 16, 18, 12, + 16, 19, 12, 17, 19, 12, 17, 19, + 12, 17, 19, 12, 17, 19, 13, 17, + 13, 17, 13, 17, 19, 19, 13, 17, + 13, 17, 19, 13, 17, 13, 18, 19, + 13, 17, 19, 13, 18, 13, 17, 13, + 18, 13, 18, 13, 18, 13, 18, 13, + 18, 13, 18, 14, 18, 19, 14, 18, + 14, 18, 14, 18, 14, 18, 14, 19, + 14, 19, 14, 18, 14, 18, 14, 18, + 14, 19, 14, 14, 18, 14, 14, 19, + 14, 18, 14, 19, 14, 19, 14, 15, + 19, 15, 15, 15, 15, 19, 15, 19, + 15, 15, 19, 15, 15, 19, 15, 19, + 15, 19, 15, 19, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 16, + 15, 15, 15, 16, 16, 16, 15, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 17, 16, 16, 16, 17, + 17, 16, 17, 17, 16, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 18, + 17, 17, 17, 17, 17, 17, 17, 17, + 18, 17, 17, 18, 17, 17, 17, 17, + 18, 18, 17, 17, 17, 17, 17, 17, + 17, 18, 17, 18, 18, 17, 17, 17, + 18, 18, 18, 17, 18, 17, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 17, + 18, 18, 18, 18, 19, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 19, + 18, 18, 19, 18, 18, 18, 19, 18, + 19, 18, 18, 19, 18, 18, 19, 19, + 19, 19, 19, 18, 19, 18, 19, 18, + 19, 19, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 18, 19, + 19, 19, 19, 19, 18, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 20, + 19, 19, 19, 19, 21, 19, 19, 20, + 19, 20, 19, 19, 19, 19, 19, 20, + 20, 20, 19, 19, 19, 20, 19, 19, + 19, 20, 20, 19, 20, 19, 19, 21, + 20, 20, 19, 19, 19, 19, 19, 19, + 20, 19, 20, 20, 20, 20, 20, 20, + 20, 19, 19, 21, 20, 20, 19, 19, +}; + +static const uint32_t coef3_huffcodes[1072] = { + 0x001b2, 0x00069, 0x00000, 0x00004, 0x00006, 0x0000e, 0x00014, 0x00019, + 0x00016, 0x0002b, 0x00030, 0x0003d, 0x0003c, 0x0005a, 0x0005f, 0x0006d, + 0x0007e, 0x0005f, 0x0007f, 0x000b6, 0x000bc, 0x000d8, 0x000f2, 0x000fe, + 0x000bc, 0x000fc, 0x00161, 0x0016e, 0x00174, 0x00176, 0x001a2, 0x001e3, + 0x001f3, 0x00174, 0x0017a, 0x001ea, 0x002a8, 0x002c4, 0x002e6, 0x00314, + 0x00346, 0x00367, 0x003e9, 0x002e5, 0x002ee, 0x003d6, 0x00555, 0x00554, + 0x00557, 0x005c3, 0x005d6, 0x006e0, 0x0062f, 0x006e2, 0x00799, 0x00789, + 0x007fa, 0x005ce, 0x007fe, 0x005ec, 0x007cc, 0x007af, 0x00aa7, 0x00b19, + 0x00b94, 0x00b85, 0x00b9f, 0x00c48, 0x00c45, 0x00dd8, 0x00c4c, 0x00c4b, + 0x00d99, 0x00d1f, 0x00dc2, 0x00f95, 0x00fa2, 0x00bb5, 0x00b9f, 0x00f5d, + 0x00bbf, 0x00f47, 0x0154a, 0x00fd5, 0x00f45, 0x00f7f, 0x0160d, 0x01889, + 0x01757, 0x01722, 0x018b3, 0x0172d, 0x01a39, 0x01a18, 0x01bb3, 0x01b30, + 0x01e63, 0x0173c, 0x01b35, 0x01723, 0x01e80, 0x01fee, 0x01761, 0x01ffc, + 0x01f7f, 0x02c7c, 0x01fa1, 0x0177b, 0x01755, 0x0175a, 0x01fa6, 0x02eab, + 0x0310a, 0x02c69, 0x03669, 0x03127, 0x03103, 0x02e43, 0x03662, 0x03165, + 0x03124, 0x0313b, 0x03111, 0x03668, 0x0343b, 0x03c52, 0x03efc, 0x02e6c, + 0x03fda, 0x03ef8, 0x02e7b, 0x03ee2, 0x03cc5, 0x03d72, 0x058c0, 0x03df8, + 0x02ea9, 0x03e7e, 0x0556d, 0x05c82, 0x03d71, 0x03e7b, 0x03c42, 0x058d7, + 0x03f4e, 0x06200, 0x03d70, 0x05cb2, 0x05c96, 0x05cb0, 0x03f45, 0x05cb1, + 0x02e6d, 0x03110, 0x02f68, 0x05c90, 0x07ca6, 0x07c88, 0x06204, 0x062c8, + 0x078a6, 0x07986, 0x079d5, 0x0b1ad, 0x07989, 0x0b079, 0x05cdd, 0x0aad4, + 0x05de8, 0x07dcd, 0x07987, 0x05d67, 0x05d99, 0x0b91d, 0x07cf1, 0x05d9b, + 0x079d7, 0x0b07b, 0x05c85, 0x05d9a, 0x07dcc, 0x07ebf, 0x07dce, 0x07dfb, + 0x07ec0, 0x07d1a, 0x07a07, 0x05c84, 0x0c471, 0x07cf2, 0x0baef, 0x0b9d2, + 0x05deb, 0x07bd6, 0x0b845, 0x05d98, 0x0b91a, 0x0bae8, 0x0c4e0, 0x0dc31, + 0x0f93d, 0x0bbce, 0x0d1d2, 0x0f7a9, 0x0d9b9, 0x0bbcb, 0x0b900, 0x0aad7, + 0x0babd, 0x0c4e1, 0x0f46f, 0x0c588, 0x0c58b, 0x160e6, 0x0bbcf, 0x0bac3, + 0x0f945, 0x0f7a3, 0x0d1c1, 0x0fb8e, 0x0f7a4, 0x0fb8c, 0x0f40c, 0x0c473, + 0x0fd72, 0x0bbcd, 0x0fffa, 0x0f940, 0x0bbc9, 0x0f7a8, 0x1a1ed, 0x0bbc5, + 0x1f26f, 0x163fd, 0x160c7, 0x1a1f5, 0x0f947, 0x163fc, 0x154b3, 0x0fff6, + 0x163f6, 0x160e9, 0x1a1f0, 0x0bab9, 0x0baba, 0x17086, 0x0b903, 0x0fd75, + 0x0f308, 0x176f3, 0x163ff, 0x0fd7d, 0x1bb78, 0x163fb, 0x188db, 0x1a1f7, + 0x154b2, 0x172fd, 0x163f4, 0x1bb73, 0x172ff, 0x0babc, 0x0f97d, 0x1a1f3, + 0x1bb6d, 0x1ffd5, 0x1a1f4, 0x1f272, 0x17380, 0x17382, 0x1ffe7, 0x0bac8, + 0x0bbc4, 0x188d3, 0x160e0, 0x0fd7b, 0x1725f, 0x172f5, 0x1bb79, 0x1fad9, + 0x1f269, 0x188d0, 0x0bac4, 0x0bac5, 0x31185, 0x188d2, 0x188cc, 0x31187, + 0x3e7fe, 0x188d1, 0x1bb6c, 0x1f268, 0x1fad2, 0x1ffd9, 0x1a1ea, 0x1bb68, + 0x1facb, 0x3fdb2, 0x1e81a, 0x188ce, 0x172fb, 0x1a1ef, 0x1face, 0x1bb70, + 0x0bac1, 0x1bb6b, 0x172f8, 0x1bb66, 0x1ffdf, 0x1bb6a, 0x1ffd7, 0x1f266, + 0x176f8, 0x37653, 0x1fa7e, 0x31182, 0x1fac8, 0x2c7e3, 0x370ee, 0x176ec, + 0x176e9, 0x2e4bc, 0x160c5, 0x3765a, 0x3ce9c, 0x17373, 0x176e8, 0x188d4, + 0x176f1, 0x176ef, 0x37659, 0x1bb7c, 0x1ffde, 0x176f2, 0x3118b, 0x2c7d4, + 0x37651, 0x5ce9f, 0x37650, 0x31191, 0x3f4f6, 0x3f4f5, 0x7a06c, 0x1fac1, + 0x5c97b, 0x2c7e0, 0x79d3a, 0x3e7fd, 0x2c7df, 0x3f4f0, 0x7a06d, 0x376c1, + 0x79d3b, 0x00004, 0x00014, 0x00059, 0x000ab, 0x000b8, 0x00177, 0x001f5, + 0x001f2, 0x00315, 0x003fc, 0x005bd, 0x0062d, 0x006e8, 0x007dd, 0x00b04, + 0x007cd, 0x00b1e, 0x00d1e, 0x00f15, 0x00f3b, 0x00f41, 0x01548, 0x018b0, + 0x0173b, 0x01884, 0x01a1c, 0x01bb4, 0x01f25, 0x017b5, 0x0176d, 0x01ef8, + 0x02e73, 0x03107, 0x03125, 0x03105, 0x02e49, 0x03ce8, 0x03ef9, 0x03e5e, + 0x02e72, 0x03471, 0x03fd9, 0x0623f, 0x078a0, 0x06867, 0x05cb3, 0x06272, + 0x068ec, 0x06e9a, 0x079d4, 0x06e98, 0x0b1aa, 0x06e1a, 0x07985, 0x068ee, + 0x06e9b, 0x05c88, 0x0b1ac, 0x07dfa, 0x05d65, 0x07cf0, 0x07cbf, 0x0c475, + 0x160eb, 0x1bb7e, 0x0f7a6, 0x1fedd, 0x160e3, 0x0fffb, 0x0fb8d, 0x0fff9, + 0x0d1c0, 0x0c58c, 0x1a1e9, 0x0bab8, 0x0f5cf, 0x0fff5, 0x376c5, 0x1a1ec, + 0x160ed, 0x1fede, 0x1fac9, 0x1a1eb, 0x1f224, 0x176ee, 0x0fd79, 0x17080, + 0x17387, 0x1bb7a, 0x1ffe9, 0x176f7, 0x17385, 0x17781, 0x2c7d5, 0x17785, + 0x1ffe3, 0x163f5, 0x1fac2, 0x3e7f9, 0x3118d, 0x3fdb1, 0x1ffe2, 0x1f226, + 0x3118a, 0x2c7d9, 0x31190, 0x3118c, 0x3f4f3, 0x1bb7f, 0x1bb72, 0x31184, + 0xb92f4, 0x3e7fb, 0x6e1d9, 0x1faca, 0x62300, 0x3fdb8, 0x3d037, 0x3e7fc, + 0x62301, 0x3f4f2, 0x1f26a, 0x0000e, 0x00063, 0x000f8, 0x001ee, 0x00377, + 0x003f7, 0x006e3, 0x005cc, 0x00b05, 0x00dd2, 0x00fd4, 0x0172e, 0x0172a, + 0x01e23, 0x01f2d, 0x01763, 0x01769, 0x0176c, 0x02e75, 0x03104, 0x02ec1, + 0x03e58, 0x0583f, 0x03f62, 0x03f44, 0x058c5, 0x0623c, 0x05cf4, 0x07bd7, + 0x05d9d, 0x0aad2, 0x05d66, 0x0b1a9, 0x0b078, 0x07cfe, 0x0b918, 0x0c46f, + 0x0b919, 0x0b847, 0x06e1b, 0x0b84b, 0x0aad8, 0x0fd74, 0x172f4, 0x17081, + 0x0f97c, 0x1f273, 0x0f7a0, 0x0fd7c, 0x172f7, 0x0fd7a, 0x1bb77, 0x172fe, + 0x1f270, 0x0fd73, 0x1bb7b, 0x1a1bc, 0x1bb7d, 0x0bbc3, 0x172f6, 0x0baeb, + 0x0fb8f, 0x3f4f4, 0x3fdb4, 0x376c8, 0x3e7fa, 0x1ffd0, 0x62303, 0xb92f5, + 0x1f261, 0x31189, 0x3fdb5, 0x2c7db, 0x376c9, 0x1fad6, 0x1fad1, 0x00015, + 0x000f0, 0x002e0, 0x0058e, 0x005d7, 0x00c4d, 0x00fa1, 0x00bdb, 0x01756, + 0x01f70, 0x02c19, 0x0313c, 0x0370f, 0x03cc0, 0x02ea8, 0x058c6, 0x058c7, + 0x02eb7, 0x058d0, 0x07d18, 0x0aa58, 0x0b848, 0x05d9e, 0x05d6c, 0x0b84c, + 0x0c589, 0x0b901, 0x163f8, 0x0bac9, 0x0b9c5, 0x0f93c, 0x188d8, 0x0bbc7, + 0x160ec, 0x0fd6f, 0x188d9, 0x160ea, 0x0f7a7, 0x0f944, 0x0baab, 0x0dc3a, + 0x188cf, 0x176fb, 0x2c7d8, 0x2c7d7, 0x1bb75, 0x5ce9e, 0x62302, 0x370ed, + 0x176f4, 0x1ffd1, 0x370ef, 0x3f4f8, 0x376c7, 0x1ffe1, 0x376c6, 0x176ff, + 0x6e1d8, 0x176f6, 0x17087, 0x0f5cd, 0x00035, 0x001a0, 0x0058b, 0x00aac, + 0x00b9a, 0x0175f, 0x01e22, 0x01e8c, 0x01fb2, 0x0310b, 0x058d1, 0x0552e, + 0x05c27, 0x0686e, 0x07ca7, 0x0c474, 0x0dc33, 0x07bf2, 0x05de9, 0x07a35, + 0x0baaa, 0x0b9eb, 0x0fb95, 0x0b9b8, 0x17381, 0x1f262, 0x188cd, 0x17088, + 0x172fa, 0x0f7a2, 0x1fad3, 0x0bac0, 0x3765c, 0x1fedf, 0x1f225, 0x1fad4, + 0x2c7da, 0x5ce9d, 0x3e7f8, 0x1e203, 0x188d7, 0x00054, 0x002c0, 0x007a1, + 0x00f78, 0x01b36, 0x01fa3, 0x0313a, 0x03436, 0x0343a, 0x07d1d, 0x07bd8, + 0x05cdf, 0x0b846, 0x0b189, 0x0d9b8, 0x0fff8, 0x0d9be, 0x0c58a, 0x05dea, + 0x0d1d3, 0x160e4, 0x1f26b, 0x188da, 0x1e202, 0x2c7d2, 0x163fe, 0x31193, + 0x17782, 0x376c2, 0x2c7d1, 0x3fdb0, 0x3765d, 0x2c7d0, 0x1fad0, 0x1e201, + 0x188dd, 0x2c7e2, 0x37657, 0x37655, 0x376c4, 0x376c0, 0x176ea, 0x0006f, + 0x003cf, 0x00dd5, 0x01f23, 0x02c61, 0x02ed0, 0x05d54, 0x0552d, 0x07883, + 0x0b1a8, 0x0b91c, 0x0babf, 0x0b902, 0x0f7aa, 0x0f7a5, 0x1a1e8, 0x1ffd6, + 0x0babe, 0x1a1bf, 0x163f3, 0x1ffd8, 0x1fad7, 0x1f275, 0x1ffdc, 0x0007d, + 0x005bc, 0x01549, 0x02a99, 0x03def, 0x06273, 0x079d6, 0x07d1b, 0x0aad3, + 0x0d0fc, 0x2c7dd, 0x188d6, 0x0bac2, 0x2c7e1, 0x1bb76, 0x1a1bd, 0x31186, + 0x0fd78, 0x1a1be, 0x31183, 0x3fdb6, 0x3f4f1, 0x37652, 0x1fad5, 0x3f4f9, + 0x3e7ff, 0x5ce9c, 0x3765b, 0x31188, 0x17372, 0x000bd, 0x0078b, 0x01f21, + 0x03c43, 0x03ded, 0x0aad6, 0x07ec1, 0x0f942, 0x05c86, 0x17089, 0x0babb, + 0x1ffe8, 0x2c7de, 0x1f26e, 0x1fac4, 0x3f4f7, 0x37656, 0x1fa7d, 0x376c3, + 0x3fdb3, 0x3118f, 0x1fac6, 0x000f8, 0x007ed, 0x01efd, 0x03e7a, 0x05c91, + 0x0aad9, 0x0baec, 0x0dc32, 0x0f46e, 0x1e200, 0x176fa, 0x3765e, 0x3fdb7, + 0x2c7d6, 0x3fdb9, 0x37654, 0x37658, 0x3118e, 0x1ffdb, 0x000f6, 0x00c43, + 0x03106, 0x068ef, 0x0b84d, 0x0b188, 0x0bbcc, 0x1f264, 0x1bb69, 0x17386, + 0x1fac0, 0x00171, 0x00f39, 0x03e41, 0x068ed, 0x0d9bc, 0x0f7a1, 0x1bb67, + 0x1ffdd, 0x176f9, 0x001b9, 0x00f7d, 0x03f63, 0x0d0fd, 0x0b9ea, 0x188dc, + 0x1fac3, 0x1a1f2, 0x31192, 0x1ffe4, 0x001f6, 0x01754, 0x06865, 0x0f309, + 0x160e5, 0x176f5, 0x3765f, 0x1facc, 0x001e9, 0x01a1a, 0x06201, 0x0f105, + 0x176f0, 0x002df, 0x01756, 0x05d6d, 0x163fa, 0x176ed, 0x00342, 0x02e40, + 0x0d0ff, 0x17082, 0x003cd, 0x02a98, 0x0fffc, 0x2c7dc, 0x1fa7f, 0x003fe, + 0x03764, 0x0fffd, 0x176fc, 0x1fac5, 0x002f7, 0x02ed1, 0x0fb97, 0x0058a, + 0x02edc, 0x0bbc8, 0x005d4, 0x0623d, 0x160e8, 0x0062e, 0x05830, 0x163f9, + 0x006eb, 0x06205, 0x1f274, 0x007de, 0x062c9, 0x1f265, 0x005c9, 0x05cde, + 0x1ffd3, 0x005d4, 0x07988, 0x007ce, 0x0b849, 0x00b1b, 0x05c89, 0x1fac7, + 0x00b93, 0x05c83, 0x00b9e, 0x0f14f, 0x00c4a, 0x0b9c7, 0x00dd4, 0x0c470, + 0x1f271, 0x00f38, 0x0fb96, 0x176eb, 0x00fa0, 0x163f7, 0x00bb2, 0x0b91b, + 0x00bbe, 0x0f102, 0x00f44, 0x0f946, 0x1facd, 0x00f79, 0x0d9bd, 0x0154d, + 0x0bbc6, 0x00fd2, 0x160e7, 0x0172b, 0x188cb, 0x0175e, 0x0fd76, 0x0175c, + 0x1bb71, 0x0189f, 0x1a1ee, 0x01f24, 0x1a1f6, 0x01ba7, 0x0bbca, 0x01f7d, + 0x0ffff, 0x01f2e, 0x1bb65, 0x01bb5, 0x172f9, 0x01fef, 0x1f26c, 0x01f3e, + 0x0fd77, 0x01762, 0x1bb6e, 0x01ef9, 0x172fc, 0x01fa0, 0x02ab7, 0x02e4a, + 0x1f267, 0x01fb3, 0x1ffda, 0x02e42, 0x03101, 0x17780, 0x0313d, 0x03475, + 0x17784, 0x03126, 0x1facf, 0x03c51, 0x17783, 0x03e40, 0x1ffe5, 0x03663, + 0x1ffe0, 0x03e8f, 0x1f26d, 0x0343c, 0x03cc1, 0x176fd, 0x03e45, 0x02ec0, + 0x03f61, 0x03dee, 0x03fd8, 0x0583e, 0x02e45, 0x03e59, 0x03d02, 0x05ce8, + 0x05568, 0x176fe, 0x02f69, 0x1fad8, 0x058c1, 0x05c83, 0x1ffe6, 0x06271, + 0x06e1c, 0x062c7, 0x068e1, 0x0552f, 0x06864, 0x06866, 0x06e99, 0x05cbc, + 0x07ca5, 0x078a1, 0x05c82, 0x07dcf, 0x0623b, 0x0623e, 0x068e8, 0x07a36, + 0x05d9c, 0x0b077, 0x07cf3, 0x07a34, 0x07ca4, 0x07d19, 0x079d2, 0x07d1c, + 0x07bd9, 0x0b84a, 0x0fb94, 0x0aad5, 0x0dc30, 0x07bf3, 0x0baee, 0x0b07a, + 0x0c472, 0x0b91e, 0x0d9ba, 0x05d9f, 0x0d0fe, 0x0b9c6, 0x05c87, 0x0f14e, + 0x0baed, 0x0b92e, 0x0f103, 0x0b9c4, 0x0fb91, 0x0d9bb, 0x0b1ab, 0x0c58d, + 0x0fffe, 0x0f93b, 0x0f941, 0x0baea, 0x0b91f, 0x0f5cc, 0x0d9bf, 0x0f943, + 0x0f104, 0x1f260, 0x0fb92, 0x0f93f, 0x0f3a6, 0x0bac7, 0x0f7ab, 0x0bac6, + 0x17383, 0x0fd6d, 0x0bae9, 0x0fd6e, 0x1e74f, 0x188ca, 0x1f227, 0x0fb93, + 0x0fb90, 0x0fff7, 0x17085, 0x17083, 0x160e1, 0x17084, 0x0f93e, 0x160e2, + 0x160c6, 0x1a1f1, 0x1bb6f, 0x17384, 0x0fd70, 0x1f263, 0x188d5, 0x173a6, + 0x0f5ce, 0x163f2, 0x0fd71, 0x1ffd2, 0x160c4, 0x1ffd4, 0x2c7d3, 0x1bb74, +}; + +static const uint8_t coef3_huffbits[1072] = { + 9, 7, 2, 3, 4, 4, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, + 11, 12, 11, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, + 13, 14, 13, 14, 14, 13, 14, 13, + 13, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 15, + 14, 14, 15, 14, 14, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 14, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 14, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 16, 15, 16, 16, 16, + 16, 15, 15, 16, 16, 16, 16, 16, + 15, 16, 16, 16, 15, 16, 15, 15, + 16, 15, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 17, 16, 17, 16, 17, 17, 16, + 17, 16, 17, 16, 16, 17, 17, 17, + 16, 17, 16, 16, 17, 16, 17, 16, + 17, 17, 16, 16, 17, 17, 17, 17, + 17, 17, 17, 17, 16, 17, 17, 16, + 17, 17, 17, 17, 17, 17, 17, 17, + 16, 18, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 16, 17, + 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 18, + 17, 17, 17, 17, 18, 17, 17, 18, + 19, 17, 17, 17, 18, 17, 17, 17, + 18, 18, 18, 17, 17, 17, 18, 17, + 17, 17, 17, 17, 17, 17, 17, 17, + 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 17, 18, 18, 18, 18, 17, + 18, 18, 18, 17, 17, 18, 18, 18, + 18, 19, 18, 18, 19, 19, 20, 18, + 19, 18, 19, 19, 18, 19, 20, 18, + 19, 4, 6, 7, 8, 9, 9, 9, + 10, 10, 10, 11, 11, 11, 11, 12, + 12, 12, 12, 12, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 16, 15, 15, 15, + 15, 16, 16, 15, 16, 16, 15, 16, + 17, 17, 17, 17, 17, 16, 16, 16, + 16, 16, 17, 17, 17, 16, 18, 17, + 17, 17, 18, 17, 17, 18, 17, 17, + 17, 17, 17, 18, 17, 18, 18, 18, + 17, 17, 18, 19, 18, 18, 17, 17, + 18, 18, 18, 18, 19, 17, 17, 18, + 20, 19, 19, 18, 19, 18, 19, 19, + 19, 19, 17, 5, 7, 9, 10, 10, + 11, 11, 12, 12, 12, 13, 13, 13, + 13, 13, 14, 14, 14, 14, 14, 15, + 14, 15, 15, 15, 15, 15, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 15, 16, 16, 17, 17, 17, + 16, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 16, + 16, 19, 18, 18, 19, 17, 19, 20, + 17, 18, 18, 18, 18, 18, 18, 6, + 8, 10, 11, 12, 12, 12, 13, 13, + 13, 14, 14, 14, 14, 15, 15, 15, + 15, 15, 15, 16, 16, 16, 16, 16, + 16, 17, 17, 17, 16, 16, 17, 17, + 17, 17, 17, 17, 17, 16, 16, 16, + 17, 18, 18, 18, 17, 19, 19, 18, + 18, 17, 18, 19, 18, 17, 18, 18, + 19, 18, 17, 17, 6, 9, 11, 12, + 13, 13, 13, 14, 14, 14, 15, 15, + 15, 15, 15, 16, 16, 16, 16, 16, + 16, 17, 16, 17, 17, 17, 17, 17, + 17, 17, 18, 17, 18, 17, 17, 18, + 18, 19, 19, 17, 17, 7, 10, 12, + 13, 13, 14, 14, 14, 14, 15, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 17, 17, 17, 17, 18, 17, 18, + 18, 18, 18, 18, 18, 18, 18, 17, + 17, 18, 18, 18, 18, 18, 18, 7, + 10, 12, 13, 14, 15, 15, 15, 15, + 16, 16, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 18, 17, 17, 8, + 11, 13, 14, 15, 15, 15, 15, 16, + 16, 18, 17, 17, 18, 17, 17, 18, + 17, 17, 18, 18, 19, 18, 18, 19, + 19, 19, 18, 18, 18, 8, 11, 13, + 14, 15, 16, 16, 16, 16, 17, 17, + 17, 18, 17, 18, 19, 18, 18, 18, + 18, 18, 18, 8, 12, 14, 15, 15, + 16, 16, 16, 17, 17, 18, 18, 18, + 18, 18, 18, 18, 18, 17, 9, 12, + 14, 15, 16, 16, 17, 17, 17, 17, + 18, 9, 12, 14, 15, 16, 17, 17, + 17, 18, 9, 13, 15, 16, 17, 17, + 18, 17, 18, 17, 9, 13, 15, 16, + 17, 18, 18, 18, 10, 13, 15, 16, + 18, 10, 14, 16, 17, 18, 10, 14, + 16, 17, 10, 14, 16, 18, 18, 10, + 14, 16, 18, 18, 11, 15, 16, 11, + 15, 17, 11, 15, 17, 11, 15, 17, + 11, 15, 17, 11, 15, 17, 12, 16, + 17, 12, 15, 12, 16, 12, 16, 18, + 12, 16, 12, 16, 12, 16, 12, 16, + 17, 12, 16, 18, 12, 17, 13, 16, + 13, 16, 13, 16, 18, 13, 16, 13, + 17, 13, 17, 13, 17, 13, 17, 13, + 17, 13, 17, 13, 17, 13, 17, 13, + 16, 13, 17, 13, 17, 13, 17, 14, + 17, 14, 17, 14, 17, 14, 14, 14, + 17, 14, 17, 14, 14, 18, 14, 14, + 18, 14, 18, 14, 18, 14, 17, 14, + 17, 14, 17, 14, 14, 18, 14, 15, + 15, 15, 14, 15, 15, 14, 15, 15, + 15, 18, 15, 18, 15, 15, 17, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 16, 15, 15, 15, 15, 16, + 16, 16, 16, 16, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 16, 16, + 16, 17, 16, 16, 16, 17, 17, 17, + 17, 17, 16, 17, 17, 17, 17, 16, + 16, 16, 17, 17, 17, 17, 16, 17, + 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 18, 17, +}; + +static const uint32_t coef4_huffcodes[476] = { + 0x00f01, 0x0001e, 0x00000, 0x00004, 0x00006, 0x0000d, 0x0000a, 0x00017, + 0x0001d, 0x00017, 0x0002c, 0x00031, 0x00039, 0x0003e, 0x00039, 0x0005a, + 0x00066, 0x00070, 0x0007b, 0x00070, 0x00077, 0x000af, 0x000c9, 0x000f2, + 0x000f4, 0x000b2, 0x000e3, 0x0015b, 0x0015d, 0x00181, 0x0019d, 0x001e3, + 0x001c5, 0x002b5, 0x002db, 0x00338, 0x003c3, 0x003cc, 0x003f0, 0x002cd, + 0x003fa, 0x003a1, 0x005b4, 0x00657, 0x007ab, 0x0074d, 0x0074c, 0x00ac1, + 0x00ac5, 0x0076b, 0x00ca8, 0x00f04, 0x00f00, 0x00fe3, 0x00f3c, 0x00f10, + 0x00f39, 0x00fe6, 0x00e26, 0x00e90, 0x016c5, 0x01827, 0x01954, 0x015c5, + 0x01958, 0x01f8a, 0x01c4a, 0x02b0f, 0x02b41, 0x02b0e, 0x033c6, 0x03050, + 0x01c4f, 0x02d88, 0x0305c, 0x03c18, 0x02b4f, 0x02cc2, 0x03a47, 0x05680, + 0x0569d, 0x06442, 0x06443, 0x06446, 0x0656e, 0x06444, 0x07120, 0x0748a, + 0x0c1ba, 0x07e22, 0x07aa6, 0x07f25, 0x07aa7, 0x07e20, 0x0c11b, 0x0c118, + 0x07aa5, 0x0ad0a, 0x0f389, 0x19ebb, 0x0caad, 0x0fe42, 0x0fe40, 0x16c34, + 0x2b4e5, 0x33d65, 0x16c30, 0x1e7ae, 0x1e25c, 0x18370, 0x1e703, 0x19eba, + 0x16c37, 0x0e234, 0x16c6e, 0x00004, 0x0002a, 0x00061, 0x00075, 0x000cb, + 0x000ff, 0x00190, 0x001eb, 0x001d1, 0x002b9, 0x00307, 0x00339, 0x0033f, + 0x003fb, 0x003b4, 0x0060c, 0x00679, 0x00645, 0x0067d, 0x0078a, 0x007e3, + 0x00749, 0x00ac4, 0x00ad2, 0x00ae3, 0x00c10, 0x00c16, 0x00ad1, 0x00cf4, + 0x00fe2, 0x01586, 0x00e9d, 0x019f1, 0x01664, 0x01e26, 0x01d38, 0x02b4d, + 0x033c5, 0x01fc2, 0x01fc3, 0x01d28, 0x03c1d, 0x0598e, 0x0f094, 0x07aa4, + 0x0ad38, 0x0ac0c, 0x0c11a, 0x079ea, 0x0c881, 0x0fe44, 0x0b635, 0x0ac0d, + 0x0b61e, 0x05987, 0x07121, 0x0f382, 0x0f387, 0x0e237, 0x0fe47, 0x0f383, + 0x0f091, 0x0f385, 0x0e233, 0x182ee, 0x19eb8, 0x1663e, 0x0f093, 0x00014, + 0x00058, 0x00159, 0x00167, 0x00300, 0x003d4, 0x005b5, 0x0079d, 0x0076a, + 0x00b67, 0x00b60, 0x00f05, 0x00cf0, 0x00f17, 0x00e95, 0x01822, 0x01913, + 0x016c2, 0x0182f, 0x01959, 0x01fcb, 0x01e27, 0x01c40, 0x033c7, 0x01e7b, + 0x01c49, 0x02d89, 0x01e23, 0x01660, 0x03f12, 0x02cc6, 0x033e1, 0x05b34, + 0x0609a, 0x06569, 0x07488, 0x07e21, 0x0cf5f, 0x0712c, 0x0389d, 0x067cf, + 0x07f28, 0x1663f, 0x33d67, 0x1663d, 0x1e25d, 0x3c1ab, 0x15c44, 0x16c36, + 0x0001f, 0x000ec, 0x00323, 0x005b2, 0x0079f, 0x00ac2, 0x00f16, 0x00e9e, + 0x01956, 0x01e0f, 0x019ea, 0x01666, 0x02b89, 0x02b02, 0x02d8c, 0x03c1b, + 0x03c19, 0x032b5, 0x03f9c, 0x02ccf, 0x03897, 0x05b35, 0x0ad02, 0x07f29, + 0x06441, 0x03884, 0x07888, 0x0784e, 0x06568, 0x0c1bb, 0x05986, 0x067cc, + 0x0fe49, 0x0fe48, 0x0c1bc, 0x0fe41, 0x18371, 0x1663c, 0x0e231, 0x0711e, + 0x0ad09, 0x0f092, 0x0002d, 0x001db, 0x00781, 0x00c1a, 0x00f55, 0x01580, + 0x01ea8, 0x02d9b, 0x032af, 0x03f16, 0x03c1c, 0x07834, 0x03c45, 0x0389c, + 0x067ce, 0x06445, 0x0c1b9, 0x07889, 0x07f3a, 0x0784f, 0x07f2b, 0x0ad0b, + 0x0f090, 0x0c11d, 0x0e94e, 0x0711f, 0x0e9f1, 0x0f38e, 0x079e9, 0x0ad03, + 0x0f09b, 0x0caae, 0x0fe46, 0x2b4e6, 0x0e9f0, 0x19eb6, 0x67ac1, 0x67ac0, + 0x33d66, 0x0f388, 0x00071, 0x003a0, 0x00ca9, 0x01829, 0x01d39, 0x02b43, + 0x02cc4, 0x06554, 0x0f09a, 0x0b61f, 0x067cd, 0x0711c, 0x0b636, 0x07f2a, + 0x0b634, 0x0c11f, 0x0cf5e, 0x0b61d, 0x0f06b, 0x0caab, 0x0c1be, 0x0e94c, + 0x0f099, 0x182ed, 0x0e94f, 0x0c119, 0x0e232, 0x2b4e4, 0x0f38a, 0x19eb4, + 0x1e25f, 0x0e94d, 0x000b7, 0x00785, 0x016cc, 0x03051, 0x033c4, 0x0656f, + 0x03891, 0x0711d, 0x0caaf, 0x0f097, 0x07489, 0x0f098, 0x0c880, 0x0caaa, + 0x0f386, 0x19eb7, 0x16c6f, 0x0f384, 0x182e8, 0x182e9, 0x0e230, 0x1e700, + 0x33d62, 0x33d63, 0x33d64, 0x16c33, 0x0e216, 0x000fd, 0x00c15, 0x01665, + 0x03c4a, 0x07f3b, 0x07896, 0x0c11c, 0x0e215, 0x16c32, 0x0f38b, 0x0f38d, + 0x182ea, 0x1e701, 0x712df, 0x15c46, 0x00194, 0x00fe0, 0x03f13, 0x0748b, + 0x0f096, 0x0cf80, 0x1e25e, 0xe25bd, 0x33d61, 0x16c31, 0x001f9, 0x01912, + 0x05710, 0x0f3d0, 0x0c1bf, 0x00301, 0x01e24, 0x0ad08, 0x003cd, 0x01c41, + 0x0c1bd, 0x00563, 0x03a52, 0x0f3d1, 0x00570, 0x02cce, 0x0e217, 0x0067b, + 0x0655d, 0x0074b, 0x06447, 0x00c12, 0x074fb, 0x00f08, 0x0b61c, 0x00e22, + 0x0fe43, 0x016c7, 0x01836, 0x019f2, 0x01c43, 0x01d3f, 0x01fcf, 0x02b4c, + 0x0304c, 0x032b6, 0x03a46, 0x05607, 0x03f17, 0x02cc5, 0x0609b, 0x0655c, + 0x07e23, 0x067c1, 0x07f26, 0x07f27, 0x0f095, 0x0e9f3, 0x0cf81, 0x0c11e, + 0x0caac, 0x0f38f, 0x0e9f2, 0x074fa, 0x0e236, 0x0fe45, 0x1c428, 0x0e235, + 0x182ef, 0x19eb5, 0x0f3d6, 0x182ec, 0x16c35, 0x0f38c, 0x2b4e7, 0x15c47, + 0xe25bc, 0x1e702, 0x1c4b6, 0x0e25a, 0x3c1aa, 0x15c45, 0x1c429, 0x19eb9, + 0x1e7af, 0x182eb, 0x1e0d4, 0x3896e, +}; + +static const uint8_t coef4_huffbits[476] = { + 12, 6, 2, 3, 4, 4, 5, 5, + 5, 6, 6, 6, 6, 6, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8, + 8, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 11, + 10, 11, 11, 11, 11, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 13, 13, 13, 13, 13, 13, + 13, 13, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 16, 16, + 16, 15, 15, 15, 15, 15, 16, 16, + 15, 16, 16, 17, 16, 16, 16, 17, + 18, 18, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 4, 6, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 13, 13, 13, 14, 13, 14, 14, + 14, 13, 13, 14, 14, 16, 16, 15, + 16, 16, 16, 15, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 16, 16, + 16, 16, 17, 17, 17, 18, 16, 5, + 8, 9, 10, 10, 10, 11, 11, 12, + 12, 12, 12, 12, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 14, 14, 13, + 14, 14, 13, 14, 14, 15, 14, 15, + 15, 15, 16, 15, 16, 16, 15, 15, + 15, 18, 18, 18, 17, 18, 17, 17, + 6, 9, 10, 11, 11, 12, 12, 13, + 13, 13, 13, 14, 14, 14, 14, 14, + 14, 14, 14, 15, 15, 15, 16, 15, + 15, 15, 15, 15, 15, 16, 16, 15, + 16, 16, 16, 16, 17, 18, 17, 16, + 16, 16, 7, 10, 11, 12, 12, 13, + 13, 14, 14, 14, 14, 15, 14, 15, + 15, 15, 16, 15, 15, 15, 15, 16, + 16, 16, 17, 16, 17, 16, 15, 16, + 16, 16, 16, 18, 17, 17, 19, 19, + 18, 16, 7, 11, 12, 13, 14, 14, + 15, 15, 16, 16, 15, 16, 16, 15, + 16, 16, 16, 16, 16, 16, 16, 17, + 16, 17, 17, 16, 17, 18, 16, 17, + 17, 17, 8, 11, 13, 14, 14, 15, + 15, 16, 16, 16, 16, 16, 16, 16, + 16, 17, 17, 16, 17, 17, 17, 17, + 18, 18, 18, 17, 17, 8, 12, 14, + 14, 15, 15, 16, 17, 17, 16, 16, + 17, 17, 20, 17, 9, 12, 14, 16, + 16, 16, 17, 21, 18, 17, 9, 13, + 15, 16, 16, 10, 13, 16, 10, 14, + 16, 11, 15, 16, 11, 15, 17, 11, + 15, 12, 15, 12, 16, 12, 16, 13, + 16, 13, 13, 13, 14, 14, 13, 14, + 14, 14, 15, 15, 14, 15, 15, 15, + 15, 15, 15, 15, 16, 17, 16, 16, + 16, 16, 17, 16, 17, 16, 18, 17, + 17, 17, 16, 17, 17, 16, 18, 17, + 21, 17, 18, 17, 18, 17, 18, 17, + 17, 17, 17, 19, +}; + +static const uint32_t coef5_huffcodes[435] = { + 0x00347, 0x0000b, 0x00001, 0x00001, 0x0000c, 0x00004, 0x00010, 0x00015, + 0x0001f, 0x0000b, 0x00023, 0x00026, 0x00029, 0x00035, 0x00037, 0x00001, + 0x00015, 0x0001a, 0x0001d, 0x0001c, 0x0001e, 0x0004e, 0x00049, 0x00051, + 0x00078, 0x00004, 0x00000, 0x00008, 0x0000d, 0x0007b, 0x00005, 0x00032, + 0x00095, 0x00091, 0x00096, 0x000a1, 0x000d9, 0x00003, 0x00019, 0x00061, + 0x00066, 0x00060, 0x00017, 0x0000e, 0x00063, 0x001a0, 0x001b7, 0x001e6, + 0x001e7, 0x001b6, 0x00018, 0x001e8, 0x00038, 0x00031, 0x00005, 0x0003d, + 0x00027, 0x001ea, 0x0001a, 0x000c5, 0x000f9, 0x000ff, 0x000db, 0x00250, + 0x000fc, 0x0025c, 0x00008, 0x00075, 0x003d7, 0x003d3, 0x001b0, 0x0007c, + 0x003ca, 0x00036, 0x00189, 0x004a6, 0x004a2, 0x004fb, 0x000c0, 0x0007f, + 0x0009a, 0x00311, 0x0006e, 0x0009b, 0x0068c, 0x006c0, 0x00484, 0x00012, + 0x000c3, 0x0094f, 0x00979, 0x009f9, 0x00d09, 0x00da6, 0x00da8, 0x00901, + 0x000c1, 0x00373, 0x00d08, 0x009fa, 0x00d8b, 0x00d85, 0x00d86, 0x000df, + 0x006e2, 0x000ce, 0x00f24, 0x009fe, 0x001f7, 0x007c1, 0x000cf, 0x009fc, + 0x009ff, 0x00d89, 0x00da9, 0x009fd, 0x001f8, 0x01a36, 0x0128c, 0x0129d, + 0x01a37, 0x00196, 0x003ea, 0x00f8b, 0x00d93, 0x01e45, 0x01e58, 0x01e4b, + 0x01e59, 0x013f1, 0x00309, 0x00265, 0x00308, 0x0243a, 0x027e1, 0x00f89, + 0x00324, 0x03cbc, 0x03c86, 0x03695, 0x0243c, 0x0243b, 0x0243e, 0x01e4a, + 0x003a5, 0x03468, 0x03428, 0x03c84, 0x027e0, 0x025e2, 0x01880, 0x00197, + 0x00325, 0x03cb7, 0x0791e, 0x007ec, 0x06c75, 0x004c8, 0x04bc7, 0x004c6, + 0x00983, 0x0481e, 0x01b53, 0x0251b, 0x01b58, 0x00984, 0x04fa8, 0x03cbb, + 0x00f8a, 0x00322, 0x0346a, 0x0243d, 0x00326, 0x03469, 0x0481f, 0x0481d, + 0x00746, 0x09032, 0x01b50, 0x01d13, 0x0d8e4, 0x0481b, 0x06c74, 0x0796b, + 0x07969, 0x00985, 0x0d8e3, 0x00986, 0x00fa2, 0x01301, 0x06c7c, 0x00987, + 0x03cb8, 0x0f4af, 0x00e88, 0x1b1c0, 0x00fce, 0x033eb, 0x03f6a, 0x03f69, + 0x00fcf, 0x0791f, 0x004c9, 0x04871, 0x00fcd, 0x00982, 0x00fcc, 0x00fa3, + 0x01d12, 0x0796c, 0x01b47, 0x00321, 0x0796a, 0x0d8e2, 0x04872, 0x04873, + 0x0000e, 0x00014, 0x0000a, 0x000a0, 0x00012, 0x0007d, 0x001a2, 0x0003b, + 0x0025f, 0x000dd, 0x0027c, 0x00343, 0x00368, 0x0036b, 0x0003e, 0x001fa, + 0x00485, 0x001b3, 0x0007f, 0x001b1, 0x0019e, 0x004ba, 0x007ad, 0x00339, + 0x00066, 0x007a4, 0x00793, 0x006c6, 0x0007e, 0x000f1, 0x00372, 0x009fb, + 0x00d83, 0x00d8a, 0x00947, 0x009f4, 0x001d0, 0x01b09, 0x01b4b, 0x007ec, + 0x003e1, 0x000ca, 0x003ec, 0x02539, 0x04fa9, 0x01b57, 0x03429, 0x03d2a, + 0x00d97, 0x003a7, 0x00dc0, 0x00d96, 0x00dc1, 0x007eb, 0x03cba, 0x00c43, + 0x00c41, 0x01b52, 0x007ef, 0x00323, 0x03cb9, 0x03c83, 0x007d0, 0x007ed, + 0x06c7f, 0x09033, 0x03f6c, 0x36383, 0x1e95d, 0x06c78, 0x00747, 0x01b51, + 0x00022, 0x00016, 0x00039, 0x00252, 0x00079, 0x00486, 0x00338, 0x00369, + 0x00d88, 0x00026, 0x00d87, 0x00f4b, 0x00d82, 0x00027, 0x001e1, 0x01a15, + 0x007c7, 0x012f0, 0x001e0, 0x006d0, 0x01a16, 0x01e44, 0x01e5f, 0x03690, + 0x00d90, 0x00c42, 0x00daf, 0x00d92, 0x00f80, 0x00cfb, 0x0342f, 0x0487f, + 0x01b46, 0x07968, 0x00d95, 0x00d91, 0x01b55, 0x03f68, 0x04bc6, 0x03cbd, + 0x00f81, 0x00320, 0x00069, 0x000fe, 0x006d5, 0x0033f, 0x000de, 0x007c6, + 0x01e40, 0x00d94, 0x00f88, 0x03c8e, 0x03694, 0x00dae, 0x00dad, 0x00267, + 0x003a6, 0x00327, 0x0487e, 0x007ee, 0x00749, 0x004c7, 0x03692, 0x01b56, + 0x00fd1, 0x07a56, 0x06c77, 0x09031, 0x00748, 0x06c7a, 0x0796d, 0x033ea, + 0x06c76, 0x00fd0, 0x36382, 0x1e417, 0x00745, 0x04faf, 0x0d8e1, 0x03f6b, + 0x1e95c, 0x04fad, 0x0009e, 0x004bd, 0x0067c, 0x01b08, 0x003eb, 0x01b45, + 0x03691, 0x0d8e5, 0x07904, 0x00981, 0x007ea, 0x019f4, 0x06c7d, 0x04fab, + 0x04fac, 0x06c7e, 0x01300, 0x06c7b, 0x0006f, 0x003f7, 0x03c85, 0x004c4, + 0x0001e, 0x006e1, 0x03693, 0x01b44, 0x00241, 0x01e46, 0x0019d, 0x00266, + 0x004bb, 0x02538, 0x007ac, 0x01b54, 0x00902, 0x04870, 0x00da7, 0x00900, + 0x00185, 0x06c79, 0x006e3, 0x003e9, 0x01e94, 0x003ed, 0x003f2, 0x0342e, + 0x0346b, 0x0251a, 0x004c5, 0x01881, 0x0481c, 0x01b59, 0x03c87, 0x04fae, + 0x007e9, 0x03f6d, 0x0f20a, 0x09030, 0x04faa, 0x0d8e6, 0x03f6f, 0x0481a, + 0x03f6e, 0x1e416, 0x0d8e7, +}; + +static const uint8_t coef5_huffbits[435] = { + 10, 4, 2, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 6, 6, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 7, 8, 8, + 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 10, 9, 10, 10, 10, 10, + 10, 9, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 11, 10, 10, 11, 11, + 10, 11, 11, 11, 11, 11, 12, 12, + 12, 12, 12, 12, 11, 11, 11, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 12, 12, 13, 13, 13, 12, + 12, 12, 12, 12, 13, 13, 13, 13, + 13, 14, 14, 14, 14, 13, 13, 13, + 13, 13, 14, 14, 14, 14, 14, 14, + 15, 14, 14, 14, 14, 14, 14, 13, + 14, 14, 14, 14, 14, 14, 15, 14, + 15, 14, 15, 15, 15, 15, 15, 15, + 16, 15, 15, 14, 15, 16, 15, 14, + 14, 15, 14, 14, 15, 14, 15, 15, + 15, 16, 15, 17, 16, 15, 15, 15, + 15, 16, 16, 16, 16, 17, 15, 16, + 14, 16, 16, 17, 16, 16, 16, 16, + 16, 15, 15, 15, 16, 16, 16, 16, + 17, 15, 15, 15, 15, 16, 15, 15, + 4, 7, 8, 8, 9, 9, 9, 10, + 10, 10, 10, 10, 10, 10, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 12, + 12, 11, 11, 11, 12, 12, 12, 12, + 12, 12, 12, 12, 13, 13, 13, 13, + 12, 13, 14, 14, 15, 15, 14, 14, + 14, 14, 14, 14, 14, 15, 14, 14, + 14, 15, 15, 15, 14, 14, 15, 15, + 15, 16, 16, 18, 17, 15, 15, 15, + 6, 9, 10, 10, 11, 11, 12, 12, + 12, 13, 12, 12, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 15, + 15, 15, 14, 14, 15, 16, 15, 14, + 14, 15, 7, 10, 11, 12, 13, 13, + 13, 14, 14, 14, 14, 14, 14, 14, + 14, 15, 15, 15, 15, 15, 14, 15, + 16, 15, 15, 16, 15, 15, 15, 16, + 15, 16, 18, 17, 15, 15, 16, 16, + 17, 15, 8, 11, 13, 13, 14, 15, + 14, 16, 15, 16, 15, 15, 15, 15, + 15, 15, 17, 15, 9, 12, 14, 15, + 10, 13, 14, 15, 10, 13, 11, 14, + 11, 14, 11, 15, 12, 15, 12, 12, + 13, 15, 13, 14, 13, 14, 14, 14, + 14, 14, 15, 15, 15, 15, 14, 15, + 15, 16, 16, 16, 15, 16, 16, 15, + 16, 17, 16, +}; + +static const uint16_t levels0[60] = { + 317, 92, 62, 60, 19, 17, 10, 7, + 6, 5, 5, 3, 3, 3, 2, 2, + 2, 2, 2, 2, 2, 1, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, +}; + +static const uint16_t levels1[40] = { + 311, 91, 61, 28, 10, 6, 5, 2, + 2, 2, 2, 2, 2, 2, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static const uint16_t levels2[340] = { + 181, 110, 78, 63, 61, 62, 60, 61, + 33, 41, 41, 19, 17, 19, 12, 11, + 9, 11, 10, 6, 8, 7, 6, 4, + 5, 5, 4, 4, 3, 4, 3, 5, + 3, 4, 3, 3, 3, 3, 3, 3, + 2, 2, 4, 2, 3, 2, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 3, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 1, 2, 1, 2, 2, + 2, 2, 1, 2, 1, 1, 1, 2, + 2, 1, 2, 1, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, +}; + +static const uint16_t levels3[180] = { + 351, 122, 76, 61, 41, 42, 24, 30, + 22, 19, 11, 9, 10, 8, 5, 5, + 4, 5, 5, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 3, 2, 2, 2, + 3, 3, 2, 2, 2, 3, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 1, + 2, 2, 1, 2, 1, 2, 2, 2, + 2, 2, 2, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 1, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, +}; + +static const uint16_t levels4[70] = { + 113, 68, 49, 42, 40, 32, 27, 15, + 10, 5, 3, 3, 3, 3, 2, 2, + 2, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, +}; + +static const uint16_t levels5[40] = { + 214, 72, 42, 40, 18, 4, 4, 2, + 2, 2, 2, 2, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, +}; + +#define DEF_COEF_TABLE(_x) { \ + .n = sizeof(coef ## _x ## _huffbits), \ + .huffcodes = coef ## _x ## _huffcodes, \ + .huffbits = coef ## _x ##_huffbits, \ + .levels = levels ## _x} + +static const struct coef_vlc_table coef_vlcs[6] = { + DEF_COEF_TABLE(0), DEF_COEF_TABLE(1), DEF_COEF_TABLE(2), + DEF_COEF_TABLE(3), DEF_COEF_TABLE(4), DEF_COEF_TABLE(5) +}; diff --git a/wmadec_filter.c b/wmadec_filter.c new file mode 100644 index 00000000..96fb5008 --- /dev/null +++ b/wmadec_filter.c @@ -0,0 +1,1307 @@ +/* + * WMA compatible decoder + * + * Extracted 2009 from the mplayer source code 2009-02-10. + * + * Copyright (c) 2002 The FFmpeg Project + * + * Licensed under the GNU Lesser General Public License. + * For licencing details see COPYING.LIB. + */ + +/** \file wmadec_filter.c paraslash's WMA decoder. */ + +/* + * This decoder handles Microsoft Windows Media Audio data version 2. + */ + +#define _XOPEN_SOURCE 600 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "para.h" +#include "error.h" +#include "list.h" +#include "ggo.h" +#include "string.h" +#include "sched.h" +#include "buffer_tree.h" +#include "filter.h" +#include "bitstream.h" +#include "imdct.h" +#include "wma.h" +#include "wmadata.h" + + +/* size of blocks */ +#define BLOCK_MIN_BITS 7 +#define BLOCK_MAX_BITS 11 +#define BLOCK_MAX_SIZE (1 << BLOCK_MAX_BITS) + +#define BLOCK_NB_SIZES (BLOCK_MAX_BITS - BLOCK_MIN_BITS + 1) + +/* XXX: find exact max size */ +#define HIGH_BAND_MAX_SIZE 16 + +/* XXX: is it a suitable value ? */ +#define MAX_CODED_SUPERFRAME_SIZE 16384 + +#define MAX_CHANNELS 2 + +#define NOISE_TAB_SIZE 8192 + +#define LSP_POW_BITS 7 + +struct private_wmadec_data { + /** Information contained in the audio file header. */ + struct asf_header_info ahi; + struct getbit_context gb; + /** Whether to use the bit reservoir. */ + int use_bit_reservoir; + /** Whether to use variable block length. */ + int use_variable_block_len; + /** Whether to use exponent coding. */ + int use_exp_vlc; + /** Whether perceptual noise is added. */ + int use_noise_coding; + int byte_offset_bits; + struct vlc exp_vlc; + int exponent_sizes[BLOCK_NB_SIZES]; + uint16_t exponent_bands[BLOCK_NB_SIZES][25]; + /** The index of the first coef in high band. */ + int high_band_start[BLOCK_NB_SIZES]; + /** Maximal number of coded coefficients. */ + int coefs_end[BLOCK_NB_SIZES]; + int exponent_high_sizes[BLOCK_NB_SIZES]; + int exponent_high_bands[BLOCK_NB_SIZES][HIGH_BAND_MAX_SIZE]; + struct vlc hgain_vlc; + + /* coded values in high bands */ + int high_band_coded[MAX_CHANNELS][HIGH_BAND_MAX_SIZE]; + int high_band_values[MAX_CHANNELS][HIGH_BAND_MAX_SIZE]; + + /* there are two possible tables for spectral coefficients */ + struct vlc coef_vlc[2]; + uint16_t *run_table[2]; + uint16_t *level_table[2]; + const struct coef_vlc_table *coef_vlcs[2]; + /** Frame length in samples. */ + int frame_len; + /** log2 of frame_len. */ + int frame_len_bits; + /** Number of block sizes. */ + int nb_block_sizes; + /* block info */ + int reset_block_lengths; + /** log2 of current block length. */ + int block_len_bits; + /** log2 of next block length. */ + int next_block_len_bits; + /** log2 of previous block length. */ + int prev_block_len_bits; + /** Block length in samples. */ + int block_len; + /** Current position in frame. */ + int block_pos; + /** True if mid/side stereo mode. */ + uint8_t ms_stereo; + /** True if channel is coded. */ + uint8_t channel_coded[MAX_CHANNELS]; + /** log2 ratio frame/exp. length. */ + int exponents_bsize[MAX_CHANNELS]; + + float exponents[MAX_CHANNELS][BLOCK_MAX_SIZE]; + float max_exponent[MAX_CHANNELS]; + int16_t coefs1[MAX_CHANNELS][BLOCK_MAX_SIZE]; + float coefs[MAX_CHANNELS][BLOCK_MAX_SIZE]; + float output[BLOCK_MAX_SIZE * 2]; + struct mdct_context *mdct_ctx[BLOCK_NB_SIZES]; + float *windows[BLOCK_NB_SIZES]; + /** Output buffer for one frame and the last for IMDCT windowing. */ + float frame_out[MAX_CHANNELS][BLOCK_MAX_SIZE * 2]; + /** Last frame info. */ + uint8_t last_superframe[MAX_CODED_SUPERFRAME_SIZE + 4]; /* padding added */ + int last_bitoffset; + int last_superframe_len; + float noise_table[NOISE_TAB_SIZE]; + int noise_index; + float noise_mult; /* XXX: suppress that and integrate it in the noise array */ + /* lsp_to_curve tables */ + float lsp_cos_table[BLOCK_MAX_SIZE]; + float lsp_pow_e_table[256]; + float lsp_pow_m_table1[(1 << LSP_POW_BITS)]; + float lsp_pow_m_table2[(1 << LSP_POW_BITS)]; +}; + +#define EXPVLCBITS 8 +#define EXPMAX ((19 + EXPVLCBITS - 1) / EXPVLCBITS) + +#define HGAINVLCBITS 9 +#define HGAINMAX ((13 + HGAINVLCBITS - 1) / HGAINVLCBITS) + +#define VLCBITS 9 +#define VLCMAX ((22 + VLCBITS - 1) / VLCBITS) + +#define SINE_WINDOW(x) float sine_ ## x[x] __a_aligned(16) + +SINE_WINDOW(128); +SINE_WINDOW(256); +SINE_WINDOW(512); +SINE_WINDOW(1024); +SINE_WINDOW(2048); +SINE_WINDOW(4096); + +static float *sine_windows[6] = { + sine_128, sine_256, sine_512, sine_1024, sine_2048, sine_4096 +}; + +/* Generate a sine window. */ +static void sine_window_init(float *window, int n) +{ + int i; + + for (i = 0; i < n; i++) + window[i] = sinf((i + 0.5) * (M_PI / (2.0 * n))); +} + +static void wmadec_cleanup(struct private_wmadec_data *pwd) +{ + int i; + + for (i = 0; i < pwd->nb_block_sizes; i++) + imdct_end(pwd->mdct_ctx[i]); + if (pwd->use_exp_vlc) + free_vlc(&pwd->exp_vlc); + if (pwd->use_noise_coding) + free_vlc(&pwd->hgain_vlc); + for (i = 0; i < 2; i++) { + free_vlc(&pwd->coef_vlc[i]); + free(pwd->run_table[i]); + free(pwd->level_table[i]); + } +} + +static void init_coef_vlc(struct vlc *vlc, uint16_t **prun_table, + uint16_t **plevel_table, const struct coef_vlc_table *vlc_table) +{ + int n = vlc_table->n; + const uint8_t *table_bits = vlc_table->huffbits; + const uint32_t *table_codes = vlc_table->huffcodes; + const uint16_t *levels_table = vlc_table->levels; + uint16_t *run_table, *level_table; + int i, l, j, k, level; + + init_vlc(vlc, VLCBITS, n, table_bits, table_codes, 4); + + run_table = para_malloc(n * sizeof(uint16_t)); + level_table = para_malloc(n * sizeof(uint16_t)); + i = 2; + level = 1; + k = 0; + while (i < n) { + l = levels_table[k++]; + for (j = 0; j < l; j++) { + run_table[i] = j; + level_table[i] = level; + i++; + } + level++; + } + *prun_table = run_table; + *plevel_table = level_table; +} + +/* compute the scale factor band sizes for each MDCT block size */ +static void compute_scale_factor_band_sizes(struct private_wmadec_data *pwd, + float high_freq) +{ + struct asf_header_info *ahi = &pwd->ahi; + int a, b, pos, lpos, k, block_len, i, j, n; + const uint8_t *table; + + for (k = 0; k < pwd->nb_block_sizes; k++) { + block_len = pwd->frame_len >> k; + + table = NULL; + a = pwd->frame_len_bits - BLOCK_MIN_BITS - k; + if (a < 3) { + if (ahi->sample_rate >= 44100) + table = exponent_band_44100[a]; + else if (ahi->sample_rate >= 32000) + table = exponent_band_32000[a]; + else if (ahi->sample_rate >= 22050) + table = exponent_band_22050[a]; + } + if (table) { + n = *table++; + for (i = 0; i < n; i++) + pwd->exponent_bands[k][i] = table[i]; + pwd->exponent_sizes[k] = n; + } else { + j = 0; + lpos = 0; + for (i = 0; i < 25; i++) { + a = wma_critical_freqs[i]; + b = ahi->sample_rate; + pos = ((block_len * 2 * a) + (b << 1)) / (4 * b); + pos <<= 2; + if (pos > block_len) + pos = block_len; + if (pos > lpos) + pwd->exponent_bands[k][j++] = pos - lpos; + if (pos >= block_len) + break; + lpos = pos; + } + pwd->exponent_sizes[k] = j; + } + + /* max number of coefs */ + pwd->coefs_end[k] = (pwd->frame_len - ((pwd->frame_len * 9) / 100)) >> k; + /* high freq computation */ + pwd->high_band_start[k] = (int) ((block_len * 2 * high_freq) + / ahi->sample_rate + 0.5); + n = pwd->exponent_sizes[k]; + j = 0; + pos = 0; + for (i = 0; i < n; i++) { + int start, end; + start = pos; + pos += pwd->exponent_bands[k][i]; + end = pos; + if (start < pwd->high_band_start[k]) + start = pwd->high_band_start[k]; + if (end > pwd->coefs_end[k]) + end = pwd->coefs_end[k]; + if (end > start) + pwd->exponent_high_bands[k][j++] = end - start; + } + pwd->exponent_high_sizes[k] = j; + } +} + +static int wma_init(struct private_wmadec_data *pwd) +{ + int i; + float bps1, high_freq; + volatile float bps; + int sample_rate1; + int coef_vlc_table; + struct asf_header_info *ahi = &pwd->ahi; + int flags2 = ahi->flags2; + + if (ahi->sample_rate <= 0 || ahi->sample_rate > 50000 + || ahi->channels <= 0 || ahi->channels > 8 + || ahi->bit_rate <= 0) + return -E_WMA_BAD_PARAMS; + + /* compute MDCT block size */ + if (ahi->sample_rate <= 16000) + pwd->frame_len_bits = 9; + else if (ahi->sample_rate <= 22050) + pwd->frame_len_bits = 10; + else + pwd->frame_len_bits = 11; + pwd->frame_len = 1 << pwd->frame_len_bits; + if (pwd->use_variable_block_len) { + int nb_max, nb; + nb = ((flags2 >> 3) & 3) + 1; + if ((ahi->bit_rate / ahi->channels) >= 32000) + nb += 2; + nb_max = pwd->frame_len_bits - BLOCK_MIN_BITS; + if (nb > nb_max) + nb = nb_max; + pwd->nb_block_sizes = nb + 1; + } else + pwd->nb_block_sizes = 1; + + /* init rate dependent parameters */ + pwd->use_noise_coding = 1; + high_freq = ahi->sample_rate * 0.5; + + /* wma2 rates are normalized */ + sample_rate1 = ahi->sample_rate; + if (sample_rate1 >= 44100) + sample_rate1 = 44100; + else if (sample_rate1 >= 22050) + sample_rate1 = 22050; + else if (sample_rate1 >= 16000) + sample_rate1 = 16000; + else if (sample_rate1 >= 11025) + sample_rate1 = 11025; + else if (sample_rate1 >= 8000) + sample_rate1 = 8000; + + bps = (float) ahi->bit_rate / (float) (ahi->channels * ahi->sample_rate); + pwd->byte_offset_bits = wma_log2((int) (bps * pwd->frame_len / 8.0 + 0.5)) + 2; + /* + * Compute high frequency value and choose if noise coding should be + * activated. + */ + bps1 = bps; + if (ahi->channels == 2) + bps1 = bps * 1.6; + if (sample_rate1 == 44100) { + if (bps1 >= 0.61) + pwd->use_noise_coding = 0; + else + high_freq = high_freq * 0.4; + } else if (sample_rate1 == 22050) { + if (bps1 >= 1.16) + pwd->use_noise_coding = 0; + else if (bps1 >= 0.72) + high_freq = high_freq * 0.7; + else + high_freq = high_freq * 0.6; + } else if (sample_rate1 == 16000) { + if (bps > 0.5) + high_freq = high_freq * 0.5; + else + high_freq = high_freq * 0.3; + } else if (sample_rate1 == 11025) + high_freq = high_freq * 0.7; + else if (sample_rate1 == 8000) { + if (bps <= 0.625) + high_freq = high_freq * 0.5; + else if (bps > 0.75) + pwd->use_noise_coding = 0; + else + high_freq = high_freq * 0.65; + } else { + if (bps >= 0.8) + high_freq = high_freq * 0.75; + else if (bps >= 0.6) + high_freq = high_freq * 0.6; + else + high_freq = high_freq * 0.5; + } + PARA_INFO_LOG("channels=%d sample_rate=%d " + "bitrate=%d block_align=%d\n", + ahi->channels, ahi->sample_rate, + ahi->bit_rate, ahi->block_align); + PARA_INFO_LOG("frame_len=%d, bps=%f bps1=%f " + "high_freq=%f bitoffset=%d\n", + pwd->frame_len, bps, bps1, + high_freq, pwd->byte_offset_bits); + PARA_INFO_LOG("use_noise_coding=%d use_exp_vlc=%d nb_block_sizes=%d\n", + pwd->use_noise_coding, pwd->use_exp_vlc, pwd->nb_block_sizes); + + compute_scale_factor_band_sizes(pwd, high_freq); + /* init MDCT windows : simple sinus window */ + for (i = 0; i < pwd->nb_block_sizes; i++) { + int n; + n = 1 << (pwd->frame_len_bits - i); + sine_window_init(sine_windows[pwd->frame_len_bits - i - 7], n); + pwd->windows[i] = sine_windows[pwd->frame_len_bits - i - 7]; + } + + pwd->reset_block_lengths = 1; + + if (pwd->use_noise_coding) { + /* init the noise generator */ + if (pwd->use_exp_vlc) + pwd->noise_mult = 0.02; + else + pwd->noise_mult = 0.04; + + { + unsigned int seed; + float norm; + seed = 1; + norm = (1.0 / (float) (1LL << 31)) * sqrt(3) * pwd->noise_mult; + for (i = 0; i < NOISE_TAB_SIZE; i++) { + seed = seed * 314159 + 1; + pwd->noise_table[i] = (float) ((int) seed) * norm; + } + } + } + + /* choose the VLC tables for the coefficients */ + coef_vlc_table = 2; + if (ahi->sample_rate >= 32000) { + if (bps1 < 0.72) + coef_vlc_table = 0; + else if (bps1 < 1.16) + coef_vlc_table = 1; + } + pwd->coef_vlcs[0] = &coef_vlcs[coef_vlc_table * 2]; + pwd->coef_vlcs[1] = &coef_vlcs[coef_vlc_table * 2 + 1]; + init_coef_vlc(&pwd->coef_vlc[0], &pwd->run_table[0], &pwd->level_table[0], + pwd->coef_vlcs[0]); + init_coef_vlc(&pwd->coef_vlc[1], &pwd->run_table[1], &pwd->level_table[1], + pwd->coef_vlcs[1]); + return 0; +} + +static void wma_lsp_to_curve_init(struct private_wmadec_data *pwd, int frame_len) +{ + float wdel, a, b; + int i, e, m; + + wdel = M_PI / frame_len; + for (i = 0; i < frame_len; i++) + pwd->lsp_cos_table[i] = 2.0f * cos(wdel * i); + + /* tables for x^-0.25 computation */ + for (i = 0; i < 256; i++) { + e = i - 126; + pwd->lsp_pow_e_table[i] = pow(2.0, e * -0.25); + } + + /* These two tables are needed to avoid two operations in pow_m1_4. */ + b = 1.0; + for (i = (1 << LSP_POW_BITS) - 1; i >= 0; i--) { + m = (1 << LSP_POW_BITS) + i; + a = (float) m *(0.5 / (1 << LSP_POW_BITS)); + a = pow(a, -0.25); + pwd->lsp_pow_m_table1[i] = 2 * a - b; + pwd->lsp_pow_m_table2[i] = b - a; + b = a; + } +} + +static int wma_decode_init(char *initial_buf, int len, struct private_wmadec_data **result) +{ + struct private_wmadec_data *pwd; + int ret, i; + + PARA_NOTICE_LOG("initial buf: %d bytes\n", len); + pwd = para_calloc(sizeof(*pwd)); + ret = read_asf_header(initial_buf, len, &pwd->ahi); + if (ret <= 0) { + free(pwd); + return ret; + } + + pwd->use_exp_vlc = pwd->ahi.flags2 & 0x0001; + pwd->use_bit_reservoir = pwd->ahi.flags2 & 0x0002; + pwd->use_variable_block_len = pwd->ahi.flags2 & 0x0004; + + ret = wma_init(pwd); + if (ret < 0) + return ret; + /* init MDCT */ + for (i = 0; i < pwd->nb_block_sizes; i++) { + ret = imdct_init(pwd->frame_len_bits - i + 1, &pwd->mdct_ctx[i]); + if (ret < 0) + return ret; + } + if (pwd->use_noise_coding) { + PARA_INFO_LOG("using noise coding\n"); + init_vlc(&pwd->hgain_vlc, HGAINVLCBITS, + sizeof(wma_hgain_huffbits), wma_hgain_huffbits, + wma_hgain_huffcodes, 2); + } + + if (pwd->use_exp_vlc) { + PARA_INFO_LOG("using exp_vlc\n"); + init_vlc(&pwd->exp_vlc, EXPVLCBITS, + sizeof(wma_scale_huffbits), wma_scale_huffbits, + wma_scale_huffcodes, 4); + } else { + PARA_INFO_LOG("using curve\n"); + wma_lsp_to_curve_init(pwd, pwd->frame_len); + } + *result = pwd; + return pwd->ahi.header_len; +} + +/** + * compute x^-0.25 with an exponent and mantissa table. We use linear + * interpolation to reduce the mantissa table size at a small speed + * expense (linear interpolation approximately doubles the number of + * bits of precision). + */ +static inline float pow_m1_4(struct private_wmadec_data *pwd, float x) +{ + union { + float f; + unsigned int v; + } u, t; + unsigned int e, m; + float a, b; + + u.f = x; + e = u.v >> 23; + m = (u.v >> (23 - LSP_POW_BITS)) & ((1 << LSP_POW_BITS) - 1); + /* build interpolation scale: 1 <= t < 2. */ + t.v = ((u.v << LSP_POW_BITS) & ((1 << 23) - 1)) | (127 << 23); + a = pwd->lsp_pow_m_table1[m]; + b = pwd->lsp_pow_m_table2[m]; + return pwd->lsp_pow_e_table[e] * (a + b * t.f); +} + +static void wma_lsp_to_curve(struct private_wmadec_data *pwd, + float *out, float *val_max_ptr, int n, float *lsp) +{ + int i, j; + float p, q, w, v, val_max; + + val_max = 0; + for (i = 0; i < n; i++) { + p = 0.5f; + q = 0.5f; + w = pwd->lsp_cos_table[i]; + for (j = 1; j < NB_LSP_COEFS; j += 2) { + q *= w - lsp[j - 1]; + p *= w - lsp[j]; + } + p *= p * (2.0f - w); + q *= q * (2.0f + w); + v = p + q; + v = pow_m1_4(pwd, v); + if (v > val_max) + val_max = v; + out[i] = v; + } + *val_max_ptr = val_max; +} + +/* Decode exponents coded with LSP coefficients (same idea as Vorbis). */ +static void decode_exp_lsp(struct private_wmadec_data *pwd, int ch) +{ + float lsp_coefs[NB_LSP_COEFS]; + int val, i; + + for (i = 0; i < NB_LSP_COEFS; i++) { + if (i == 0 || i >= 8) + val = get_bits(&pwd->gb, 3); + else + val = get_bits(&pwd->gb, 4); + lsp_coefs[i] = wma_lsp_codebook[i][val]; + } + + wma_lsp_to_curve(pwd, pwd->exponents[ch], &pwd->max_exponent[ch], + pwd->block_len, lsp_coefs); +} + +/* Decode exponents coded with VLC codes. */ +static int decode_exp_vlc(struct private_wmadec_data *pwd, int ch) +{ + int last_exp, n, code; + const uint16_t *ptr, *band_ptr; + float v, *q, max_scale, *q_end; + + band_ptr = pwd->exponent_bands[pwd->frame_len_bits - pwd->block_len_bits]; + ptr = band_ptr; + q = pwd->exponents[ch]; + q_end = q + pwd->block_len; + max_scale = 0; + last_exp = 36; + + while (q < q_end) { + code = get_vlc(&pwd->gb, pwd->exp_vlc.table, EXPVLCBITS, EXPMAX); + if (code < 0) + return code; + /* NOTE: this offset is the same as MPEG4 AAC ! */ + last_exp += code - 60; + /* XXX: use a table */ + v = pow(10, last_exp * (1.0 / 16.0)); + if (v > max_scale) + max_scale = v; + n = *ptr++; + do { + *q++ = v; + } while (--n); + } + pwd->max_exponent[ch] = max_scale; + return 0; +} + +/* compute src0 * src1 + src2 */ +static inline void vector_mult_add(float *dst, const float *src0, const float *src1, + const float *src2, int len) +{ + int i; + + for (i = 0; i < len; i++) + dst[i] = src0[i] * src1[i] + src2[i]; +} + +static inline void vector_mult_reverse(float *dst, const float *src0, + const float *src1, int len) +{ + int i; + + src1 += len - 1; + for (i = 0; i < len; i++) + dst[i] = src0[i] * src1[-i]; +} + +/** + * Apply MDCT window and add into output. + * + * We ensure that when the windows overlap their squared sum + * is always 1 (MDCT reconstruction rule). + */ +static void wma_window(struct private_wmadec_data *pwd, float *out) +{ + float *in = pwd->output; + int block_len, bsize, n; + + /* left part */ + if (pwd->block_len_bits <= pwd->prev_block_len_bits) { + block_len = pwd->block_len; + bsize = pwd->frame_len_bits - pwd->block_len_bits; + vector_mult_add(out, in, pwd->windows[bsize], out, block_len); + } else { + block_len = 1 << pwd->prev_block_len_bits; + n = (pwd->block_len - block_len) / 2; + bsize = pwd->frame_len_bits - pwd->prev_block_len_bits; + vector_mult_add(out + n, in + n, pwd->windows[bsize], out + n, + block_len); + memcpy(out + n + block_len, in + n + block_len, + n * sizeof(float)); + } + out += pwd->block_len; + in += pwd->block_len; + /* right part */ + if (pwd->block_len_bits <= pwd->next_block_len_bits) { + block_len = pwd->block_len; + bsize = pwd->frame_len_bits - pwd->block_len_bits; + vector_mult_reverse(out, in, pwd->windows[bsize], block_len); + } else { + block_len = 1 << pwd->next_block_len_bits; + n = (pwd->block_len - block_len) / 2; + bsize = pwd->frame_len_bits - pwd->next_block_len_bits; + memcpy(out, in, n * sizeof(float)); + vector_mult_reverse(out + n, in + n, pwd->windows[bsize], + block_len); + memset(out + n + block_len, 0, n * sizeof(float)); + } +} + +static int wma_total_gain_to_bits(int total_gain) +{ + if (total_gain < 15) + return 13; + else if (total_gain < 32) + return 12; + else if (total_gain < 40) + return 11; + else if (total_gain < 45) + return 10; + else + return 9; +} + +static int compute_high_band_values(struct private_wmadec_data *pwd, + int bsize, int nb_coefs[MAX_CHANNELS]) +{ + int ch; + + if (!pwd->use_noise_coding) + return 0; + for (ch = 0; ch < pwd->ahi.channels; ch++) { + int i, m, a; + if (!pwd->channel_coded[ch]) + continue; + m = pwd->exponent_high_sizes[bsize]; + for (i = 0; i < m; i++) { + a = get_bit(&pwd->gb); + pwd->high_band_coded[ch][i] = a; + if (!a) + continue; + nb_coefs[ch] -= pwd->exponent_high_bands[bsize][i]; + } + } + for (ch = 0; ch < pwd->ahi.channels; ch++) { + int i, n, val; + if (!pwd->channel_coded[ch]) + continue; + n = pwd->exponent_high_sizes[bsize]; + val = (int)0x80000000; + for (i = 0; i < n; i++) { + if (!pwd->high_band_coded[ch][i]) + continue; + if (val == (int)0x80000000) + val = get_bits(&pwd->gb, 7) - 19; + else { + int code = get_vlc(&pwd->gb, + pwd->hgain_vlc.table, HGAINVLCBITS, + HGAINMAX); + if (code < 0) + return code; + val += code - 18; + } + pwd->high_band_values[ch][i] = val; + } + } + return 1; +} + +static void compute_mdct_coefficients(struct private_wmadec_data *pwd, + int bsize, int total_gain, int nb_coefs[MAX_CHANNELS]) +{ + int ch; + float mdct_norm = 1.0 / (pwd->block_len / 2); + + for (ch = 0; ch < pwd->ahi.channels; ch++) { + int16_t *coefs1; + float *coefs, *exponents, mult, mult1, noise; + int i, j, n, n1, last_high_band, esize; + float exp_power[HIGH_BAND_MAX_SIZE]; + + if (!pwd->channel_coded[ch]) + continue; + coefs1 = pwd->coefs1[ch]; + exponents = pwd->exponents[ch]; + esize = pwd->exponents_bsize[ch]; + mult = pow(10, total_gain * 0.05) / pwd->max_exponent[ch]; + mult *= mdct_norm; + coefs = pwd->coefs[ch]; + if (!pwd->use_noise_coding) { + /* XXX: optimize more */ + n = nb_coefs[ch]; + for (i = 0; i < n; i++) + *coefs++ = coefs1[i] * + exponents[i << bsize >> esize] * mult; + n = pwd->block_len - pwd->coefs_end[bsize]; + for (i = 0; i < n; i++) + *coefs++ = 0.0; + continue; + } + mult1 = mult; + n1 = pwd->exponent_high_sizes[bsize]; + /* compute power of high bands */ + exponents = pwd->exponents[ch] + + (pwd->high_band_start[bsize] << bsize); + last_high_band = 0; /* avoid warning */ + for (j = 0; j < n1; j++) { + n = pwd->exponent_high_bands[ + pwd->frame_len_bits - pwd->block_len_bits][j]; + if (pwd->high_band_coded[ch][j]) { + float e2, val; + e2 = 0; + for (i = 0; i < n; i++) { + val = exponents[i << bsize >> esize]; + e2 += val * val; + } + exp_power[j] = e2 / n; + last_high_band = j; + } + exponents += n << bsize; + } + /* main freqs and high freqs */ + exponents = pwd->exponents[ch]; + for (j = -1; j < n1; j++) { + if (j < 0) + n = pwd->high_band_start[bsize]; + else + n = pwd->exponent_high_bands[pwd->frame_len_bits + - pwd->block_len_bits][j]; + if (j >= 0 && pwd->high_band_coded[ch][j]) { + /* use noise with specified power */ + mult1 = sqrt(exp_power[j] + / exp_power[last_high_band]); + /* XXX: use a table */ + mult1 = mult1 * pow(10, + pwd->high_band_values[ch][j] * 0.05); + mult1 /= (pwd->max_exponent[ch] * pwd->noise_mult); + mult1 *= mdct_norm; + for (i = 0; i < n; i++) { + noise = pwd->noise_table[pwd->noise_index]; + pwd->noise_index = (pwd->noise_index + 1) + & (NOISE_TAB_SIZE - 1); + *coefs++ = noise * exponents[ + i << bsize >> esize] * mult1; + } + exponents += n << bsize; + } else { + /* coded values + small noise */ + for (i = 0; i < n; i++) { + noise = pwd->noise_table[pwd->noise_index]; + pwd->noise_index = (pwd->noise_index + 1) + & (NOISE_TAB_SIZE - 1); + *coefs++ = ((*coefs1++) + noise) * + exponents[i << bsize >> esize] + * mult; + } + exponents += n << bsize; + } + } + /* very high freqs: noise */ + n = pwd->block_len - pwd->coefs_end[bsize]; + mult1 = mult * exponents[((-1 << bsize)) >> esize]; + for (i = 0; i < n; i++) { + *coefs++ = pwd->noise_table[pwd->noise_index] * mult1; + pwd->noise_index = (pwd->noise_index + 1) + & (NOISE_TAB_SIZE - 1); + } + } +} + +/** + * Returns 0 if OK, 1 if last block of frame, negative on uncorrectable + * errors. + */ +static int wma_decode_block(struct private_wmadec_data *pwd) +{ + int ret, n, v, ch, code, bsize; + int coef_nb_bits, total_gain; + int nb_coefs[MAX_CHANNELS]; + + /* compute current block length */ + if (pwd->use_variable_block_len) { + n = wma_log2(pwd->nb_block_sizes - 1) + 1; + + if (pwd->reset_block_lengths) { + pwd->reset_block_lengths = 0; + v = get_bits(&pwd->gb, n); + if (v >= pwd->nb_block_sizes) + return -E_WMA_BLOCK_SIZE; + pwd->prev_block_len_bits = pwd->frame_len_bits - v; + v = get_bits(&pwd->gb, n); + if (v >= pwd->nb_block_sizes) + return -E_WMA_BLOCK_SIZE; + pwd->block_len_bits = pwd->frame_len_bits - v; + } else { + /* update block lengths */ + pwd->prev_block_len_bits = pwd->block_len_bits; + pwd->block_len_bits = pwd->next_block_len_bits; + } + v = get_bits(&pwd->gb, n); + if (v >= pwd->nb_block_sizes) + return -E_WMA_BLOCK_SIZE; + pwd->next_block_len_bits = pwd->frame_len_bits - v; + } else { + /* fixed block len */ + pwd->next_block_len_bits = pwd->frame_len_bits; + pwd->prev_block_len_bits = pwd->frame_len_bits; + pwd->block_len_bits = pwd->frame_len_bits; + } + + /* now check if the block length is coherent with the frame length */ + pwd->block_len = 1 << pwd->block_len_bits; + if ((pwd->block_pos + pwd->block_len) > pwd->frame_len) + return -E_INCOHERENT_BLOCK_LEN; + + if (pwd->ahi.channels == 2) + pwd->ms_stereo = get_bit(&pwd->gb); + v = 0; + for (ch = 0; ch < pwd->ahi.channels; ch++) { + int a = get_bit(&pwd->gb); + pwd->channel_coded[ch] = a; + v |= a; + } + + bsize = pwd->frame_len_bits - pwd->block_len_bits; + + /* if no channel coded, no need to go further */ + /* XXX: fix potential framing problems */ + if (!v) + goto next; + + /* + * Read total gain and extract corresponding number of bits for coef + * escape coding. + */ + total_gain = 1; + for (;;) { + int a = get_bits(&pwd->gb, 7); + total_gain += a; + if (a != 127) + break; + } + + coef_nb_bits = wma_total_gain_to_bits(total_gain); + + /* compute number of coefficients */ + n = pwd->coefs_end[bsize]; + for (ch = 0; ch < pwd->ahi.channels; ch++) + nb_coefs[ch] = n; + + ret = compute_high_band_values(pwd, bsize, nb_coefs); + if (ret < 0) + return ret; + + /* exponents can be reused in short blocks. */ + if ((pwd->block_len_bits == pwd->frame_len_bits) || get_bit(&pwd->gb)) { + for (ch = 0; ch < pwd->ahi.channels; ch++) { + if (pwd->channel_coded[ch]) { + if (pwd->use_exp_vlc) { + ret = decode_exp_vlc(pwd, ch); + if (ret < 0) + return ret; + } else + decode_exp_lsp(pwd, ch); + pwd->exponents_bsize[ch] = bsize; + } + } + } + + /* parse spectral coefficients : just RLE encoding */ + for (ch = 0; ch < pwd->ahi.channels; ch++) { + struct vlc *coef_vlc; + int level, run, tindex; + int16_t *ptr, *eptr; + const uint16_t *level_table, *run_table; + + if (!pwd->channel_coded[ch]) + continue; + /* + * special VLC tables are used for ms stereo because there is + * potentially less energy there + */ + tindex = (ch == 1 && pwd->ms_stereo); + coef_vlc = &pwd->coef_vlc[tindex]; + run_table = pwd->run_table[tindex]; + level_table = pwd->level_table[tindex]; + /* XXX: optimize */ + ptr = &pwd->coefs1[ch][0]; + eptr = ptr + nb_coefs[ch]; + memset(ptr, 0, pwd->block_len * sizeof(int16_t)); + for (;;) { + code = get_vlc(&pwd->gb, coef_vlc->table, + VLCBITS, VLCMAX); + if (code < 0) + return code; + if (code == 1) /* EOB */ + break; + if (code == 0) { /* escape */ + level = get_bits(&pwd->gb, coef_nb_bits); + /* reading block_len_bits would be better */ + run = get_bits(&pwd->gb, pwd->frame_len_bits); + } else { /* normal code */ + run = run_table[code]; + level = level_table[code]; + } + if (!get_bit(&pwd->gb)) + level = -level; + ptr += run; + if (ptr >= eptr) { + PARA_ERROR_LOG("overflow in spectral RLE, ignoring\n"); + break; + } + *ptr++ = level; + if (ptr >= eptr) /* EOB can be omitted */ + break; + } + } + compute_mdct_coefficients(pwd, bsize, total_gain, nb_coefs); + if (pwd->ms_stereo && pwd->channel_coded[1]) { + float a, b; + int i; + /* + * Nominal case for ms stereo: we do it before mdct. + * + * No need to optimize this case because it should almost never + * happen. + */ + if (!pwd->channel_coded[0]) { + PARA_NOTICE_LOG("rare ms-stereo\n"); + memset(pwd->coefs[0], 0, sizeof(float) * pwd->block_len); + pwd->channel_coded[0] = 1; + } + for (i = 0; i < pwd->block_len; i++) { + a = pwd->coefs[0][i]; + b = pwd->coefs[1][i]; + pwd->coefs[0][i] = a + b; + pwd->coefs[1][i] = a - b; + } + } +next: + for (ch = 0; ch < pwd->ahi.channels; ch++) { + int n4, index; + + n = pwd->block_len; + n4 = pwd->block_len / 2; + if (pwd->channel_coded[ch]) + imdct(pwd->mdct_ctx[bsize], pwd->output, pwd->coefs[ch]); + else if (!(pwd->ms_stereo && ch == 1)) + memset(pwd->output, 0, sizeof(pwd->output)); + + /* multiply by the window and add in the frame */ + index = (pwd->frame_len / 2) + pwd->block_pos - n4; + wma_window(pwd, &pwd->frame_out[ch][index]); + } + + /* update block number */ + pwd->block_pos += pwd->block_len; + if (pwd->block_pos >= pwd->frame_len) + return 1; + else + return 0; +} + +/* + * Clip a signed integer value into the -32768,32767 range. + * + * \param a The value to clip. + * + * \return The clipped value. + */ +static inline int16_t av_clip_int16(int a) +{ + if ((a + 32768) & ~65535) + return (a >> 31) ^ 32767; + else + return a; +} + +/* Decode a frame of frame_len samples. */ +static int wma_decode_frame(struct private_wmadec_data *pwd, int16_t *samples) +{ + int ret, i, n, ch, incr; + int16_t *ptr; + float *iptr; + + /* read each block */ + pwd->block_pos = 0; + for (;;) { + ret = wma_decode_block(pwd); + if (ret < 0) + return ret; + if (ret) + break; + } + + /* convert frame to integer */ + n = pwd->frame_len; + incr = pwd->ahi.channels; + for (ch = 0; ch < pwd->ahi.channels; ch++) { + ptr = samples + ch; + iptr = pwd->frame_out[ch]; + + for (i = 0; i < n; i++) { + *ptr = av_clip_int16(lrintf(*iptr++)); + ptr += incr; + } + /* prepare for next block */ + memmove(&pwd->frame_out[ch][0], &pwd->frame_out[ch][pwd->frame_len], + pwd->frame_len * sizeof(float)); + } + return 0; +} + +static int wma_decode_superframe(struct private_wmadec_data *pwd, void *data, + int *data_size, const uint8_t *buf, int buf_size) +{ + int ret; + int16_t *samples; + + if (buf_size == 0) { + pwd->last_superframe_len = 0; + return 0; + } + if (buf_size < pwd->ahi.block_align) + return 0; + buf_size = pwd->ahi.block_align; + samples = data; + init_get_bits(&pwd->gb, buf, buf_size); + if (pwd->use_bit_reservoir) { + int i, nb_frames, bit_offset, pos, len; + uint8_t *q; + + /* read super frame header */ + skip_bits(&pwd->gb, 4); /* super frame index */ + nb_frames = get_bits(&pwd->gb, 4) - 1; + // PARA_DEBUG_LOG("have %d frames\n", nb_frames); + ret = -E_WMA_OUTPUT_SPACE; + if ((nb_frames + 1) * pwd->ahi.channels * pwd->frame_len + * sizeof(int16_t) > *data_size) + goto fail; + + bit_offset = get_bits(&pwd->gb, pwd->byte_offset_bits + 3); + + if (pwd->last_superframe_len > 0) { + /* add bit_offset bits to last frame */ + ret = -E_WMA_BAD_SUPERFRAME; + if ((pwd->last_superframe_len + ((bit_offset + 7) >> 3)) > + MAX_CODED_SUPERFRAME_SIZE) + goto fail; + q = pwd->last_superframe + pwd->last_superframe_len; + len = bit_offset; + while (len > 7) { + *q++ = get_bits(&pwd->gb, 8); + len -= 8; + } + if (len > 0) + *q++ = get_bits(&pwd->gb, len) << (8 - len); + + /* XXX: bit_offset bits into last frame */ + init_get_bits(&pwd->gb, pwd->last_superframe, + MAX_CODED_SUPERFRAME_SIZE); + /* skip unused bits */ + if (pwd->last_bitoffset > 0) + skip_bits(&pwd->gb, pwd->last_bitoffset); + /* + * This frame is stored in the last superframe and in + * the current one. + */ + ret = wma_decode_frame(pwd, samples); + if (ret < 0) + goto fail; + samples += pwd->ahi.channels * pwd->frame_len; + } + + /* read each frame starting from bit_offset */ + pos = bit_offset + 4 + 4 + pwd->byte_offset_bits + 3; + init_get_bits(&pwd->gb, buf + (pos >> 3), + (MAX_CODED_SUPERFRAME_SIZE - (pos >> 3))); + len = pos & 7; + if (len > 0) + skip_bits(&pwd->gb, len); + + pwd->reset_block_lengths = 1; + for (i = 0; i < nb_frames; i++) { + ret = wma_decode_frame(pwd, samples); + if (ret < 0) + goto fail; + samples += pwd->ahi.channels * pwd->frame_len; + } + + /* we copy the end of the frame in the last frame buffer */ + pos = get_bits_count(&pwd->gb) + + ((bit_offset + 4 + 4 + pwd->byte_offset_bits + 3) & ~7); + pwd->last_bitoffset = pos & 7; + pos >>= 3; + len = buf_size - pos; + ret = -E_WMA_BAD_SUPERFRAME; + if (len > MAX_CODED_SUPERFRAME_SIZE || len < 0) + goto fail; + pwd->last_superframe_len = len; + memcpy(pwd->last_superframe, buf + pos, len); + } else { + PARA_DEBUG_LOG("not using bit reservoir\n"); + ret = -E_WMA_OUTPUT_SPACE; + if (pwd->ahi.channels * pwd->frame_len * sizeof(int16_t) > *data_size) + goto fail; + /* single frame decode */ + ret = wma_decode_frame(pwd, samples); + if (ret < 0) + goto fail; + samples += pwd->ahi.channels * pwd->frame_len; + } + PARA_DEBUG_LOG("frame_len: %d, block_len: %d, outbytes: %d, eaten: %d\n", + pwd->frame_len, pwd->block_len, + (int)((int8_t *)samples - (int8_t *)data), pwd->ahi.block_align); + *data_size = (int8_t *)samples - (int8_t *)data; + return pwd->ahi.block_align; +fail: + /* reset the bit reservoir on errors */ + pwd->last_superframe_len = 0; + return ret; +} + +static void wmadec_close(struct filter_node *fn) +{ + struct private_wmadec_data *pwd = fn->private_data; + + if (!pwd) + return; + wmadec_cleanup(pwd); + free(fn->private_data); + fn->private_data = NULL; +} + +static int wmadec_execute(struct btr_node *btrn, const char *cmd, char **result) +{ + struct filter_node *fn = btr_context(btrn); + struct private_wmadec_data *pwd = fn->private_data; + + if (!strcmp(cmd, "samplerate")) { + if (pwd->ahi.sample_rate == 0) + return -E_BTR_NAVAIL; + *result = make_message("%u", pwd->ahi.sample_rate); + return 1; + } + if (!strcmp(cmd, "channels")) { + if (pwd->ahi.channels == 0) + return -E_BTR_NAVAIL; + *result = make_message("%u", pwd->ahi.channels); + return 1; + } + return -ERRNO_TO_PARA_ERROR(ENOTSUP); +} + +#define WMA_OUTPUT_BUFFER_SIZE (128 * 1024) + +static void wmadec_post_select(__a_unused struct sched *s, struct task *t) +{ + struct filter_node *fn = container_of(t, struct filter_node, task); + int ret, converted; + struct private_wmadec_data *pwd = fn->private_data; + struct btr_node *btrn = fn->btrn; + size_t len; + char *in; + +next_buffer: + converted = 0; + t->error = 0; + ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); + if (ret < 0) + goto err; + if (ret == 0) + return; + btr_merge(btrn, fn->min_iqs); + len = btr_next_buffer(btrn, (char **)&in); + ret = -E_WMADEC_EOF; + if (len < fn->min_iqs) + goto err; + if (!pwd) { + ret = wma_decode_init(in, len, &pwd); + if (ret < 0) + goto err; + if (ret == 0) { + fn->min_iqs += 4096; + goto next_buffer; + } + fn->min_iqs = 2 * (WMA_FRAME_SKIP + pwd->ahi.block_align); + fn->private_data = pwd; + converted = pwd->ahi.header_len; + goto success; + } + fn->min_iqs = WMA_FRAME_SKIP + pwd->ahi.block_align; + for (;;) { + char *out; + int out_size = WMA_OUTPUT_BUFFER_SIZE; + if (converted + fn->min_iqs > len) + break; + out = para_malloc(WMA_OUTPUT_BUFFER_SIZE); + ret = wma_decode_superframe(pwd, out, + &out_size, (uint8_t *)in + converted + WMA_FRAME_SKIP, + len - WMA_FRAME_SKIP); + if (ret < 0) { + free(out); + goto err; + } + btr_add_output(out, out_size, btrn); + converted += ret + WMA_FRAME_SKIP; + } +success: + btr_consume(btrn, converted); + goto next_buffer; +err: + assert(ret < 0); + t->error = ret; + btr_remove_node(btrn); +} + +static void wmadec_open(struct filter_node *fn) +{ + fn->private_data = NULL; + fn->min_iqs = 4096; +} + +/** + * The init function of the wma decoder. + * + * \param f Its fields are filled in by the function. + */ +void wmadec_filter_init(struct filter *f) +{ + f->open = wmadec_open; + f->close = wmadec_close; + f->execute = wmadec_execute; + f->pre_select = generic_filter_pre_select; + f->post_select = wmadec_post_select; +} diff --git a/write.c b/write.c index c12736a6..416408a1 100644 --- a/write.c +++ b/write.c @@ -1,13 +1,15 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file write.c Paraslash's standalone wav/raw player. */ +#include #include #include +#include #include "para.h" #include "string.h" @@ -16,6 +18,7 @@ #include "sched.h" #include "ggo.h" #include "stdin.h" +#include "buffer_tree.h" #include "write.h" #include "write_common.h" #include "fd.h" @@ -23,39 +26,28 @@ INIT_WRITE_ERRLISTS; -/** Check if given buffer contains a valid wave header. */ +enum check_wav_state { + CWS_NEED_HEADER, + CWS_HAVE_HEADER, + CWS_NO_HEADER, +}; + struct check_wav_task { - /** The buffer to check. */ - char *buf; - /** Number of bytes loaded in \a buf. */ - size_t *loaded; - /** Non-zero if an error occurred or end of file was reached. */ - int *input_error; + int state; /** Number of channels specified in wav header given by \a buf. */ unsigned channels; /** Sample rate specified in wav header given by \a buf. */ unsigned samplerate; /** The task structure used by the scheduler. */ struct task task; -}; - -/** Delay writing until given time. */ -struct initial_delay_task { - /** The time the first data should be written out. */ - struct timeval start_time; - /** The task structure for this task. */ - struct task task; + struct btr_node *btrn; + size_t min_iqs; }; static struct write_args_info conf; static struct stdin_task sit; -static struct check_wav_task the_check_wav_task; -static struct initial_delay_task the_initial_delay_task; - -static struct writer_node_group *wng; - /** Length of a standard wav header. */ #define WAV_HEADER_LEN 44 @@ -65,104 +57,81 @@ static struct writer_node_group *wng; * \return If not, return -E_NO_WAV_HEADER, otherwise, return zero. If * there is less than WAV_HEADER_LEN bytes available, return one. */ -static void check_wav_pre_select(__a_unused struct sched *s, struct task *t) +static void check_wav_pre_select(struct sched *s, struct task *t) { struct check_wav_task *cwt = container_of(t, struct check_wav_task, task); - unsigned char *a; int ret; - if (*cwt->loaded < WAV_HEADER_LEN) { - if (*cwt->input_error < 0) - t->error = *cwt->input_error; - return; + ret = btr_node_status(cwt->btrn, cwt->min_iqs, BTR_NT_INTERNAL); + if (ret != 0) + sched_min_delay(s); +} + +static int check_wav_exec(struct btr_node *btrn, const char *cmd, char **result) +{ + struct check_wav_task *cwt = btr_context(btrn); + + + if (!strcmp(cmd, "samplerate")) { + if (cwt->state != CWS_HAVE_HEADER) + return -E_BTR_NAVAIL; + *result = make_message("%d", cwt->samplerate); + return 1; + } + if (!strcmp(cmd, "channels")) { + if (cwt->state != CWS_HAVE_HEADER) + return -E_BTR_NAVAIL; + *result = make_message("%d", cwt->channels); + return 1; } + return -ERRNO_TO_PARA_ERROR(ENOTSUP); +} + +static void check_wav_post_select(__a_unused struct sched *s, struct task *t) +{ + struct check_wav_task *cwt = container_of(t, struct check_wav_task, task); + struct btr_node *btrn = cwt->btrn; + unsigned char *a; + size_t sz; + int ret; + + t->error = 0; + ret = btr_node_status(btrn, cwt->min_iqs, BTR_NT_INTERNAL); + if (ret <= 0) + goto out; + if (cwt->state != CWS_NEED_HEADER) + goto pushdown; + btr_merge(btrn, cwt->min_iqs); + sz = btr_next_buffer(btrn, (char **)&a); + if (sz < cwt->min_iqs) /* file size less than WAV_HEADER_SIZE */ + goto pushdown; + cwt->min_iqs = 0; cwt->channels = 2; cwt->samplerate = 44100; - a = (unsigned char*)cwt->buf; if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' || a[3] != 'F') { PARA_NOTICE_LOG("wav header not found\n"); - t->error = -E_NO_WAV_HEADER; + cwt->state = CWS_NO_HEADER; + sprintf(t->status, "check wav: no header"); goto out; } + PARA_INFO_LOG("found wav header\n"); + cwt->state = CWS_HAVE_HEADER; + sprintf(t->status, "check wav: have header"); cwt->channels = (unsigned) a[22]; cwt->samplerate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24); - *cwt->loaded -= WAV_HEADER_LEN; - memmove(cwt->buf, cwt->buf + WAV_HEADER_LEN, *cwt->loaded); - t->error = -E_WAV_HEADER_SUCCESS; PARA_INFO_LOG("channels: %d, sample rate: %d\n", cwt->channels, cwt->samplerate); + btr_consume(btrn, WAV_HEADER_LEN); +pushdown: + btr_pushdown(btrn); out: - wng->channels = &cwt->channels; - wng->samplerate = &cwt->samplerate; - ret = wng_open(wng); + t->error = ret; if (ret < 0) - t->error = ret; - s->timeout.tv_sec = 0; - s->timeout.tv_usec = 1; -} - -static void initial_delay_pre_select(struct sched *s, struct task *t) -{ - struct initial_delay_task *idt = container_of(t, struct initial_delay_task, task); - struct timeval diff; - - if (!idt->start_time.tv_sec && !idt->start_time.tv_usec) { - t->error = -E_NO_DELAY; - goto register_check_wav; - } - if (tv_diff(now, &idt->start_time, &diff) > 0) { - t->error = -E_DELAY_TIMEOUT; - goto register_check_wav; - } - if (tv_diff(&s->timeout , &diff, NULL) > 0) - s->timeout = diff; - return; -register_check_wav: - register_task(&the_check_wav_task.task); - s->timeout.tv_sec = 0; - s->timeout.tv_usec = 1; + btr_remove_node(btrn); } static int loglevel; INIT_STDERR_LOGGING(loglevel) -static struct writer_node_group *check_args(void) -{ - int i, ret = -E_WRITE_SYNTAX; - struct writer_node_group *g = NULL; - struct initial_delay_task *idt = &the_initial_delay_task; - - loglevel = get_loglevel_by_name(conf.loglevel_arg); - if (conf.start_time_given) { - long unsigned sec, usec; - if (sscanf(conf.start_time_arg, "%lu:%lu", - &sec, &usec) != 2) - goto out; - idt->start_time.tv_sec = sec; - idt->start_time.tv_usec = usec; - } - if (!conf.writer_given) { - g = setup_default_wng(); - ret = 1; - goto out; - } - g = wng_new(conf.writer_given); - ret = -E_WRITE_SYNTAX; - for (i = 0; i < conf.writer_given; i++) { - int writer_num; - g->writer_nodes[i].conf = check_writer_arg( - conf.writer_arg[i], &writer_num); - if (!g->writer_nodes[i].conf) - goto out; - g->writer_nodes[i].writer_num = writer_num; - } - ret = 1; -out: - if (ret > 0) - return g; - free(g); - return NULL; -} - __noreturn static void print_help_and_die(void) { int d = conf.detailed_help_given; @@ -178,14 +147,72 @@ __noreturn static void print_help_and_die(void) exit(0); } +static int main_btr(struct sched *s) +{ + int i, ret; + struct check_wav_task _cwt, *cwt = &_cwt; + struct writer_node *wns; + + loglevel = get_loglevel_by_name(conf.loglevel_arg); + sit.btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "stdin")); + stdin_set_defaults(&sit); + register_task(&sit.task); + + cwt->state = CWS_NEED_HEADER; + cwt->min_iqs = WAV_HEADER_LEN; + cwt->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "check_wav", .parent = sit.btrn, + .handler = check_wav_exec, .context = cwt)); + sprintf(cwt->task.status, "check_wav"); + cwt->task.pre_select = check_wav_pre_select; + cwt->task.post_select = check_wav_post_select; + cwt->task.error = 0; + register_task(&cwt->task); + + ret = -E_WRITE_SYNTAX; + if (!conf.writer_given) { + i = 0; + wns = para_calloc(sizeof(*wns)); + ret = setup_writer_node(NULL, cwt->btrn, wns); + if (ret < 0) + goto out; + i = 1; + } else { + wns = para_calloc(conf.writer_given * sizeof(*wns)); + for (i = 0; i < conf.writer_given; i++) { + ret = setup_writer_node(conf.writer_arg[i], + cwt->btrn, wns + i); + if (ret < 0) + goto out; + } + } + + s->default_timeout.tv_sec = 10; + s->default_timeout.tv_usec = 50000; + ret = schedule(s); +out: + for (i--; i >= 0; i--) { + struct writer_node *wn = wns + i; + struct writer *w = writers + wn->writer_num; + + w->close(wn); + btr_free_node(wn->btrn); + free(wn->conf); + } + free(wns); + btr_free_node(cwt->btrn); + return ret; +} + /** * Para_write's main function. * * \param argc The usual argument counter. * \param argv The usual argument vector. * - * It registers the stdin task, the check_wav_task, the task for initial delay - * and all tasks for actually writing out the stream. + * It sets up and starts the tasks and the buffer tree nodes determined by + * command line options. * * \return \p EXIT_SUCCESS or EXIT_FAILURE */ @@ -193,8 +220,6 @@ int main(int argc, char *argv[]) { int ret = -E_WRITE_SYNTAX; static struct sched s; - struct check_wav_task *cwt = &the_check_wav_task; - struct initial_delay_task *idt = &the_initial_delay_task; writer_init(); write_cmdline_parser(argc, argv, &conf); @@ -202,39 +227,7 @@ int main(int argc, char *argv[]) if (conf.help_given || conf.detailed_help_given) print_help_and_die(); - wng = check_args(); - if (!wng) - goto out; - stdin_set_defaults(&sit); - ret = -ERRNO_TO_PARA_ERROR(EINVAL); - if (conf.bufsize_arg < 0) - goto out; - if (conf.bufsize_arg >= INT_MAX / 1024) - goto out; - sit.bufsize = conf.bufsize_arg * 1024; - sit.buf = para_malloc(sit.bufsize); - - wng->bufp = &sit.buf; - wng->loaded = &sit.loaded; - wng->input_error = &sit.task.error; - - register_task(&sit.task); - - cwt->buf = sit.buf; - cwt->loaded = &sit.loaded; - cwt->input_error = &sit.task.error; - sprintf(cwt->task.status, "check wav"); - cwt->task.pre_select = check_wav_pre_select; - - idt->task.pre_select = initial_delay_pre_select; - sprintf(idt->task.status, "initial_delay"); - register_task(&idt->task); - - s.default_timeout.tv_sec = 10; - s.default_timeout.tv_usec = 0; - ret = schedule(&s); - wng_close(wng); -out: + ret = main_btr(&s); if (ret < 0) { PARA_ERROR_LOG("%s\n", para_strerror(-ret)); exit(EXIT_FAILURE); diff --git a/write.h b/write.h index 8816be73..c7196dc5 100644 --- a/write.h +++ b/write.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -17,12 +17,11 @@ struct writer_node { int writer_num; /** Writer-specific data. */ void *private_data; - /** Pointer to the group this node belongs to. */ - struct writer_node_group *wng; /** The writer-specific configuration of this node. */ void *conf; - /** How much of the wng's buffer is already written. */ - size_t written; + struct btr_node *btrn; + struct task task; + size_t min_iqs; }; /** Describes one supported writer. */ @@ -43,6 +42,7 @@ struct writer { * more than once with different values of \a options. */ void *(*parse_config)(const char *options); + void (*free_config)(void *conf); /** * Open one instance of this writer. * @@ -54,22 +54,16 @@ struct writer { /** * Prepare the fd sets for select. * - * This is called from the writer node group task's pre_select(). It - * may use the sched pointer to add any file descriptors or to decrease - * the select timeout. It must return positive on success and negative - * on errors. + * This is called from scheduler. It may use the sched pointer to add + * any file descriptors or to decrease the select timeout. */ - int (*pre_select)(struct sched *s, struct writer_node *wn); + void (*pre_select)(struct sched *s, struct task *t); /** * Write audio data. * - * Called from the post_select function of the wng task. It must keep - * track of the number of bytes consumed from the wng's buffer via - * the \p wn->written variable (which may be modified by the wng handling - * functions). This function must return positive on success and - * negative on errors. + * Called from the post_select function of the writer node's task. */ - int (*post_select)(struct sched *s, struct writer_node *wn); + void (*post_select)(struct sched *s, struct task *t); /** * Close one instance of the writer. * @@ -83,36 +77,9 @@ struct writer { */ void (*shutdown)(struct writer_node *); struct ggo_help help; + btr_command_handler execute; }; -/** - * Describes a set of writer nodes that all write the same stream. - */ -struct writer_node_group { - /** Number of nodes belonging to this group. */ - unsigned num_writers; - /** Array of pointers to the corresponding writer nodes. */ - struct writer_node *writer_nodes; - /** Non-zero if an error or end of file was encountered by the feeding task. */ - int *input_error; - /** Current output buffer. */ - char **bufp; - /** Number of bytes loaded in the output buffer. */ - size_t *loaded; - /** Number of audio channels of the current stream. */ - unsigned int *channels; - /** Sample rate of the current stream. */ - unsigned int *samplerate; - /** The task associated to this group. */ - struct task task; - /** Whether the group is open, i.e. wng_open() was called. */ - int open; - /** Max number of bytes written in the previous post_select() call. */ - int last_written; -}; - -/** Loop over each writer node in a writer group. */ -#define FOR_EACH_WRITER_NODE(i, wng) for (i = 0; i < (wng)->num_writers; i++) /** Loop over each supported writer. */ #define FOR_EACH_WRITER(i) for (i = 0; i < NUM_SUPPORTED_WRITERS; i++) diff --git a/write_common.c b/write_common.c index 2dca309c..1da78b51 100644 --- a/write_common.c +++ b/write_common.c @@ -1,16 +1,20 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file write_common.c common functions of para_audiod and para_write */ +#include +#include + #include "para.h" #include "string.h" #include "list.h" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "write.h" #include "error.h" @@ -20,150 +24,6 @@ const char *writer_names[] ={WRITER_NAMES}; /** the array of supported writers */ struct writer writers[NUM_SUPPORTED_WRITERS] = {WRITER_ARRAY}; -static void wng_pre_select(struct sched *s, struct task *t) -{ - struct writer_node_group *g = container_of(t, struct writer_node_group, task); - int i; - - FOR_EACH_WRITER_NODE(i, g) { - struct writer_node *wn = &g->writer_nodes[i]; - struct writer *w = writers + wn->writer_num; - if (!w->pre_select) - continue; - t->error = w->pre_select(s, wn); - if (t->error < 0) - return; - } - /* - * Force a minimal delay if something was written during the previous - * call to wng_post_select(). This is necessary because the filter - * chain might still have data for us which it couldn't convert during - * the previous run due to its buffer size constraints. In this case we - * do not want to wait until the next input data arrives as this could - * lead to buffer underruns. - */ - if (g->last_written == 0) - return; - s->timeout.tv_sec = 0; - s->timeout.tv_usec = 1; -} - -static void wng_post_select(struct sched *s, struct task *t) -{ - struct writer_node_group *g = container_of(t, struct writer_node_group, task); - int i; - size_t min_written = 0, max_written = 0; - - FOR_EACH_WRITER_NODE(i, g) { - struct writer_node *wn = &g->writer_nodes[i]; - struct writer *w = writers + wn->writer_num; - t->error = w->post_select(s, wn); - if (t->error < 0) - return; - if (!i) - min_written = wn->written; - else - min_written = PARA_MIN(min_written, wn->written); - max_written = PARA_MAX(max_written, wn->written); - } - g->last_written = max_written; - //PARA_INFO_LOG("loaded: %zd, min_written: %zd bytes\n", *g->loaded, min_written); - if (min_written) { - *g->loaded -= min_written; - FOR_EACH_WRITER_NODE(i, g) - g->writer_nodes[i].written -= min_written; - } - if (!*g->loaded && *g->input_error) { - t->error = *g->input_error; - return; - } - if (*g->loaded && min_written) { -// PARA_INFO_LOG("moving %zd bytes\n", *g->loaded); - memmove(*g->bufp, *g->bufp + min_written, *g->loaded); - } -} - -/** - * call the open function of each writer in the group - * - * \param g the writer node group - * - * \return If at least one open function returned an error, all successful - * writer notes get closed and this error value is returned. Upon success, a - * task associated with \a g is registered to the scheduler and the function - * returns a positive value. - * */ -int wng_open(struct writer_node_group *g) -{ - int i, ret = 1; - - PARA_NOTICE_LOG("opening wng %p with %d writer(s)\n", g, g->num_writers); - FOR_EACH_WRITER_NODE(i, g) { - struct writer_node *wn = &g->writer_nodes[i]; - struct writer *w = writers + wn->writer_num; - wn->wng = g; - ret = w->open(wn); - if (ret < 0) - goto err_out; - } - sprintf(g->task.status, "%s", "writer node group"); - register_task(&g->task); - g->open = 1; - return 1; -err_out: - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - while (i > 0) { - struct writer_node *wn = &g->writer_nodes[--i]; - struct writer *w = writers + wn->writer_num; - w->close(wn); - } - free(g->writer_nodes); - g->num_writers = 0; - g->task.error = -E_TASK_UNREGISTERED; - return ret; -} - -/** - * call the close function of each writer in the given group - * - * \param g the writer node group to close - * - * This function also frees all resources of the given group. - */ -void wng_close(struct writer_node_group *g) -{ - int i; - - if (!g || !g->open) - return; - PARA_NOTICE_LOG("closing wng with %d writer(s)\n", g->num_writers); - FOR_EACH_WRITER_NODE(i, g) { - struct writer_node *wn = &g->writer_nodes[i]; - struct writer *w = writers + wn->writer_num; - w->close(wn); - } - free(g->writer_nodes); - free(g); -} - -/** - * allocate and initialize a new writer_node_group struct - * - * \param num_writers the number of writer nodes for the new group - * - * \return Pointer to the new writer node group - */ -struct writer_node_group *wng_new(unsigned num_writers) -{ - struct writer_node_group *g = para_calloc(sizeof(struct writer_node_group)); - g->num_writers = num_writers; - g->writer_nodes = para_calloc(num_writers - * sizeof(struct writer_node)); - g->task.post_select = wng_post_select; - g->task.pre_select = wng_pre_select; - return g; -} - /** * Call the init function of each supported paraslash writer. */ @@ -215,23 +75,55 @@ void *check_writer_arg(const char *wa, int *writer_num) return NULL; } +void register_writer_node(struct writer_node *wn, struct btr_node *parent) +{ + struct writer *w = writers + wn->writer_num; + char *name = make_message("%s writer", writer_names[wn->writer_num]); + int ret; + + wn->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = name, .parent = parent, + .handler = w->execute, .context = wn)); + strcpy(wn->task.status, name); + free(name); + ret = w->open(wn); + wn->task.post_select = w->post_select; + wn->task.pre_select = w->pre_select; + register_task(&wn->task); +} + /** - * setup a writer node group with only one writer, the default writer + * Setup a writer node with the default writer. * - * The writer which is set up depends on the OS. It defaults to alsa for Linux, - * osx_write for OS X, file writer if neither of these is supported. + * If arg is \p NULL, the OS-dependent default writer is used with an empty + * configuration string. It defaults to alsa for Linux, osx for OS X, oss for + * *BSD and the file writer if neither of these is supported. * - * \return pointer to the allocated writer node group + * Once the writer configuration has been retrieved, a writer node is created, + * its buffer tree node is added to the buffer tree as a child of the given + * parent. + * + * Finally, the new writer node's taks structure is initialized and registered + * to the paraslash scheduler. + * + * \return A pointer to the allocated writer node group. */ -struct writer_node_group *setup_default_wng(void) +int setup_writer_node(const char *arg, struct btr_node *parent, + struct writer_node *wn) { - struct writer_node_group *wng = wng_new(1); - wng->writer_nodes[0].writer_num = DEFAULT_WRITER; - PARA_INFO_LOG("using default writer: %s %p\n", - writer_names[DEFAULT_WRITER], writers[DEFAULT_WRITER].parse_config); - wng->writer_nodes[0].conf = writers[DEFAULT_WRITER].parse_config(""); - return wng; + if (arg) + wn->conf = check_writer_arg(arg, &wn->writer_num); + else { + wn->writer_num = DEFAULT_WRITER; + wn->conf = writers[DEFAULT_WRITER].parse_config(""); + } + if (!wn->conf) + return -E_WRITE_COMMON_SYNTAX; + register_writer_node(wn, parent); + return 1; } + + /** * Print the help text of all writers to stdout. * @@ -254,3 +146,31 @@ void print_writer_helps(int detailed) ggo_print_help(&w->help, detailed); } } + +static int get_btr_value(struct btr_node *btrn, const char *key, int32_t *result) +{ + char *buf = NULL; + int ret = btr_exec_up(btrn, key, &buf); + + if (ret < 0) + return ret; + ret = para_atoi32(buf, result); + free(buf); + return ret; +} + +/* + * Ask parent btr nodes for the samplerate of the current stream. + */ +int get_btr_samplerate(struct btr_node *btrn, int32_t *result) +{ + return get_btr_value(btrn, "samplerate", result); +} + +/* + * Ask parent btr nodes for the channel count of the current stream. + */ +int get_btr_channels(struct btr_node *btrn, int32_t *result) +{ + return get_btr_value(btrn, "channels", result); +} diff --git a/write_common.h b/write_common.h index 5edebe11..b6f66fa6 100644 --- a/write_common.h +++ b/write_common.h @@ -1,15 +1,16 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file write_common.h exported symbols from write_common.c */ -int wng_open(struct writer_node_group *g); -void wng_close(struct writer_node_group *g); -struct writer_node_group *wng_new(unsigned num_writers); void writer_init(void); void *check_writer_arg(const char *wa, int *writer_num); -struct writer_node_group *setup_default_wng(void); void print_writer_helps(int detailed); +void register_writer_node(struct writer_node *wn, struct btr_node *parent); +int setup_writer_node(const char *arg, struct btr_node *parent, + struct writer_node *wn); +int get_btr_samplerate(struct btr_node *btrn, int32_t *result); +int get_btr_channels(struct btr_node *btrn, int32_t *result);