release 0.10.0 trunk 0.10.0
authornathan
Sat, 29 Dec 2007 14:47:40 +0100
branchtrunk
changeset 0474a1293c3c0
child 1 a6fedb9b8528
release 0.10.0
.exclude
COPYING
HISTORY
MANUAL
Makefile
README
common.h
config.h
data-mp3-image.c
data-mp3.c
data-mp3.h
data-src.h
data.c
data.h
decoder-core.h
decoder-mp3-stream.c
decoder-mp3-stream.h
decoder-mp3.c
decoder-mp3.h
decoder-ogg.c
decoder-ogg.h
decoder-snd.c
decoder-snd.h
decoder.c
decoder.h
examples/image_convert.sh.example
examples/mount.sh.example
examples/mp3sources.conf.example
examples/mplayer.sh.example
examples/network.sh.example
i18n.c
i18n.h
i18ntest.c
menu-async.h
menu.c
menu.h
mp3.c
mplayer.c
network.c
network.h
patches/cdfs-0.5c-discid-1.diff
patches/cdfs-0.5c-discid-2.diff
patches/mplayer-0.90rc1-head.diff
patches/mplayer-0.90rc1-slavemode.diff
patches/mplayer-0.90rc5-slavemode.diff
patches/mplayer-1.0cvs20070302-slavemode.diff
patches/mplayer-1.0pre2-slavemode.diff
patches/vdr-1.3.10-nostop.diff
player-mp3-sample.c
player-mp3.c
player-mp3.h
player-mplayer.c
player-mplayer.h
service.h
setup-mp3.c
setup-mp3.h
setup-mplayer.c
setup-mplayer.h
setup.h
stream.c
stream.h
version.h
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/.exclude	Sat Dec 29 14:47:40 2007 +0100
     1.3 @@ -0,0 +1,3 @@
     1.4 +.dependencies
     1.5 +*.o
     1.6 +libvdr-*.so
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/COPYING	Sat Dec 29 14:47:40 2007 +0100
     2.3 @@ -0,0 +1,340 @@
     2.4 +		    GNU GENERAL PUBLIC LICENSE
     2.5 +		       Version 2, June 1991
     2.6 +
     2.7 + Copyright (C) 1989, 1991 Free Software Foundation, Inc.
     2.8 +                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     2.9 + Everyone is permitted to copy and distribute verbatim copies
    2.10 + of this license document, but changing it is not allowed.
    2.11 +
    2.12 +			    Preamble
    2.13 +
    2.14 +  The licenses for most software are designed to take away your
    2.15 +freedom to share and change it.  By contrast, the GNU General Public
    2.16 +License is intended to guarantee your freedom to share and change free
    2.17 +software--to make sure the software is free for all its users.  This
    2.18 +General Public License applies to most of the Free Software
    2.19 +Foundation's software and to any other program whose authors commit to
    2.20 +using it.  (Some other Free Software Foundation software is covered by
    2.21 +the GNU Library General Public License instead.)  You can apply it to
    2.22 +your programs, too.
    2.23 +
    2.24 +  When we speak of free software, we are referring to freedom, not
    2.25 +price.  Our General Public Licenses are designed to make sure that you
    2.26 +have the freedom to distribute copies of free software (and charge for
    2.27 +this service if you wish), that you receive source code or can get it
    2.28 +if you want it, that you can change the software or use pieces of it
    2.29 +in new free programs; and that you know you can do these things.
    2.30 +
    2.31 +  To protect your rights, we need to make restrictions that forbid
    2.32 +anyone to deny you these rights or to ask you to surrender the rights.
    2.33 +These restrictions translate to certain responsibilities for you if you
    2.34 +distribute copies of the software, or if you modify it.
    2.35 +
    2.36 +  For example, if you distribute copies of such a program, whether
    2.37 +gratis or for a fee, you must give the recipients all the rights that
    2.38 +you have.  You must make sure that they, too, receive or can get the
    2.39 +source code.  And you must show them these terms so they know their
    2.40 +rights.
    2.41 +
    2.42 +  We protect your rights with two steps: (1) copyright the software, and
    2.43 +(2) offer you this license which gives you legal permission to copy,
    2.44 +distribute and/or modify the software.
    2.45 +
    2.46 +  Also, for each author's protection and ours, we want to make certain
    2.47 +that everyone understands that there is no warranty for this free
    2.48 +software.  If the software is modified by someone else and passed on, we
    2.49 +want its recipients to know that what they have is not the original, so
    2.50 +that any problems introduced by others will not reflect on the original
    2.51 +authors' reputations.
    2.52 +
    2.53 +  Finally, any free program is threatened constantly by software
    2.54 +patents.  We wish to avoid the danger that redistributors of a free
    2.55 +program will individually obtain patent licenses, in effect making the
    2.56 +program proprietary.  To prevent this, we have made it clear that any
    2.57 +patent must be licensed for everyone's free use or not licensed at all.
    2.58 +
    2.59 +  The precise terms and conditions for copying, distribution and
    2.60 +modification follow.
    2.61 +
    2.62 +		    GNU GENERAL PUBLIC LICENSE
    2.63 +   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
    2.64 +
    2.65 +  0. This License applies to any program or other work which contains
    2.66 +a notice placed by the copyright holder saying it may be distributed
    2.67 +under the terms of this General Public License.  The "Program", below,
    2.68 +refers to any such program or work, and a "work based on the Program"
    2.69 +means either the Program or any derivative work under copyright law:
    2.70 +that is to say, a work containing the Program or a portion of it,
    2.71 +either verbatim or with modifications and/or translated into another
    2.72 +language.  (Hereinafter, translation is included without limitation in
    2.73 +the term "modification".)  Each licensee is addressed as "you".
    2.74 +
    2.75 +Activities other than copying, distribution and modification are not
    2.76 +covered by this License; they are outside its scope.  The act of
    2.77 +running the Program is not restricted, and the output from the Program
    2.78 +is covered only if its contents constitute a work based on the
    2.79 +Program (independent of having been made by running the Program).
    2.80 +Whether that is true depends on what the Program does.
    2.81 +
    2.82 +  1. You may copy and distribute verbatim copies of the Program's
    2.83 +source code as you receive it, in any medium, provided that you
    2.84 +conspicuously and appropriately publish on each copy an appropriate
    2.85 +copyright notice and disclaimer of warranty; keep intact all the
    2.86 +notices that refer to this License and to the absence of any warranty;
    2.87 +and give any other recipients of the Program a copy of this License
    2.88 +along with the Program.
    2.89 +
    2.90 +You may charge a fee for the physical act of transferring a copy, and
    2.91 +you may at your option offer warranty protection in exchange for a fee.
    2.92 +
    2.93 +  2. You may modify your copy or copies of the Program or any portion
    2.94 +of it, thus forming a work based on the Program, and copy and
    2.95 +distribute such modifications or work under the terms of Section 1
    2.96 +above, provided that you also meet all of these conditions:
    2.97 +
    2.98 +    a) You must cause the modified files to carry prominent notices
    2.99 +    stating that you changed the files and the date of any change.
   2.100 +
   2.101 +    b) You must cause any work that you distribute or publish, that in
   2.102 +    whole or in part contains or is derived from the Program or any
   2.103 +    part thereof, to be licensed as a whole at no charge to all third
   2.104 +    parties under the terms of this License.
   2.105 +
   2.106 +    c) If the modified program normally reads commands interactively
   2.107 +    when run, you must cause it, when started running for such
   2.108 +    interactive use in the most ordinary way, to print or display an
   2.109 +    announcement including an appropriate copyright notice and a
   2.110 +    notice that there is no warranty (or else, saying that you provide
   2.111 +    a warranty) and that users may redistribute the program under
   2.112 +    these conditions, and telling the user how to view a copy of this
   2.113 +    License.  (Exception: if the Program itself is interactive but
   2.114 +    does not normally print such an announcement, your work based on
   2.115 +    the Program is not required to print an announcement.)
   2.116 +
   2.117 +These requirements apply to the modified work as a whole.  If
   2.118 +identifiable sections of that work are not derived from the Program,
   2.119 +and can be reasonably considered independent and separate works in
   2.120 +themselves, then this License, and its terms, do not apply to those
   2.121 +sections when you distribute them as separate works.  But when you
   2.122 +distribute the same sections as part of a whole which is a work based
   2.123 +on the Program, the distribution of the whole must be on the terms of
   2.124 +this License, whose permissions for other licensees extend to the
   2.125 +entire whole, and thus to each and every part regardless of who wrote it.
   2.126 +
   2.127 +Thus, it is not the intent of this section to claim rights or contest
   2.128 +your rights to work written entirely by you; rather, the intent is to
   2.129 +exercise the right to control the distribution of derivative or
   2.130 +collective works based on the Program.
   2.131 +
   2.132 +In addition, mere aggregation of another work not based on the Program
   2.133 +with the Program (or with a work based on the Program) on a volume of
   2.134 +a storage or distribution medium does not bring the other work under
   2.135 +the scope of this License.
   2.136 +
   2.137 +  3. You may copy and distribute the Program (or a work based on it,
   2.138 +under Section 2) in object code or executable form under the terms of
   2.139 +Sections 1 and 2 above provided that you also do one of the following:
   2.140 +
   2.141 +    a) Accompany it with the complete corresponding machine-readable
   2.142 +    source code, which must be distributed under the terms of Sections
   2.143 +    1 and 2 above on a medium customarily used for software interchange; or,
   2.144 +
   2.145 +    b) Accompany it with a written offer, valid for at least three
   2.146 +    years, to give any third party, for a charge no more than your
   2.147 +    cost of physically performing source distribution, a complete
   2.148 +    machine-readable copy of the corresponding source code, to be
   2.149 +    distributed under the terms of Sections 1 and 2 above on a medium
   2.150 +    customarily used for software interchange; or,
   2.151 +
   2.152 +    c) Accompany it with the information you received as to the offer
   2.153 +    to distribute corresponding source code.  (This alternative is
   2.154 +    allowed only for noncommercial distribution and only if you
   2.155 +    received the program in object code or executable form with such
   2.156 +    an offer, in accord with Subsection b above.)
   2.157 +
   2.158 +The source code for a work means the preferred form of the work for
   2.159 +making modifications to it.  For an executable work, complete source
   2.160 +code means all the source code for all modules it contains, plus any
   2.161 +associated interface definition files, plus the scripts used to
   2.162 +control compilation and installation of the executable.  However, as a
   2.163 +special exception, the source code distributed need not include
   2.164 +anything that is normally distributed (in either source or binary
   2.165 +form) with the major components (compiler, kernel, and so on) of the
   2.166 +operating system on which the executable runs, unless that component
   2.167 +itself accompanies the executable.
   2.168 +
   2.169 +If distribution of executable or object code is made by offering
   2.170 +access to copy from a designated place, then offering equivalent
   2.171 +access to copy the source code from the same place counts as
   2.172 +distribution of the source code, even though third parties are not
   2.173 +compelled to copy the source along with the object code.
   2.174 +
   2.175 +  4. You may not copy, modify, sublicense, or distribute the Program
   2.176 +except as expressly provided under this License.  Any attempt
   2.177 +otherwise to copy, modify, sublicense or distribute the Program is
   2.178 +void, and will automatically terminate your rights under this License.
   2.179 +However, parties who have received copies, or rights, from you under
   2.180 +this License will not have their licenses terminated so long as such
   2.181 +parties remain in full compliance.
   2.182 +
   2.183 +  5. You are not required to accept this License, since you have not
   2.184 +signed it.  However, nothing else grants you permission to modify or
   2.185 +distribute the Program or its derivative works.  These actions are
   2.186 +prohibited by law if you do not accept this License.  Therefore, by
   2.187 +modifying or distributing the Program (or any work based on the
   2.188 +Program), you indicate your acceptance of this License to do so, and
   2.189 +all its terms and conditions for copying, distributing or modifying
   2.190 +the Program or works based on it.
   2.191 +
   2.192 +  6. Each time you redistribute the Program (or any work based on the
   2.193 +Program), the recipient automatically receives a license from the
   2.194 +original licensor to copy, distribute or modify the Program subject to
   2.195 +these terms and conditions.  You may not impose any further
   2.196 +restrictions on the recipients' exercise of the rights granted herein.
   2.197 +You are not responsible for enforcing compliance by third parties to
   2.198 +this License.
   2.199 +
   2.200 +  7. If, as a consequence of a court judgment or allegation of patent
   2.201 +infringement or for any other reason (not limited to patent issues),
   2.202 +conditions are imposed on you (whether by court order, agreement or
   2.203 +otherwise) that contradict the conditions of this License, they do not
   2.204 +excuse you from the conditions of this License.  If you cannot
   2.205 +distribute so as to satisfy simultaneously your obligations under this
   2.206 +License and any other pertinent obligations, then as a consequence you
   2.207 +may not distribute the Program at all.  For example, if a patent
   2.208 +license would not permit royalty-free redistribution of the Program by
   2.209 +all those who receive copies directly or indirectly through you, then
   2.210 +the only way you could satisfy both it and this License would be to
   2.211 +refrain entirely from distribution of the Program.
   2.212 +
   2.213 +If any portion of this section is held invalid or unenforceable under
   2.214 +any particular circumstance, the balance of the section is intended to
   2.215 +apply and the section as a whole is intended to apply in other
   2.216 +circumstances.
   2.217 +
   2.218 +It is not the purpose of this section to induce you to infringe any
   2.219 +patents or other property right claims or to contest validity of any
   2.220 +such claims; this section has the sole purpose of protecting the
   2.221 +integrity of the free software distribution system, which is
   2.222 +implemented by public license practices.  Many people have made
   2.223 +generous contributions to the wide range of software distributed
   2.224 +through that system in reliance on consistent application of that
   2.225 +system; it is up to the author/donor to decide if he or she is willing
   2.226 +to distribute software through any other system and a licensee cannot
   2.227 +impose that choice.
   2.228 +
   2.229 +This section is intended to make thoroughly clear what is believed to
   2.230 +be a consequence of the rest of this License.
   2.231 +
   2.232 +  8. If the distribution and/or use of the Program is restricted in
   2.233 +certain countries either by patents or by copyrighted interfaces, the
   2.234 +original copyright holder who places the Program under this License
   2.235 +may add an explicit geographical distribution limitation excluding
   2.236 +those countries, so that distribution is permitted only in or among
   2.237 +countries not thus excluded.  In such case, this License incorporates
   2.238 +the limitation as if written in the body of this License.
   2.239 +
   2.240 +  9. The Free Software Foundation may publish revised and/or new versions
   2.241 +of the General Public License from time to time.  Such new versions will
   2.242 +be similar in spirit to the present version, but may differ in detail to
   2.243 +address new problems or concerns.
   2.244 +
   2.245 +Each version is given a distinguishing version number.  If the Program
   2.246 +specifies a version number of this License which applies to it and "any
   2.247 +later version", you have the option of following the terms and conditions
   2.248 +either of that version or of any later version published by the Free
   2.249 +Software Foundation.  If the Program does not specify a version number of
   2.250 +this License, you may choose any version ever published by the Free Software
   2.251 +Foundation.
   2.252 +
   2.253 +  10. If you wish to incorporate parts of the Program into other free
   2.254 +programs whose distribution conditions are different, write to the author
   2.255 +to ask for permission.  For software which is copyrighted by the Free
   2.256 +Software Foundation, write to the Free Software Foundation; we sometimes
   2.257 +make exceptions for this.  Our decision will be guided by the two goals
   2.258 +of preserving the free status of all derivatives of our free software and
   2.259 +of promoting the sharing and reuse of software generally.
   2.260 +
   2.261 +			    NO WARRANTY
   2.262 +
   2.263 +  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
   2.264 +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
   2.265 +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
   2.266 +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
   2.267 +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   2.268 +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
   2.269 +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
   2.270 +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
   2.271 +REPAIR OR CORRECTION.
   2.272 +
   2.273 +  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
   2.274 +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
   2.275 +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
   2.276 +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
   2.277 +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
   2.278 +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
   2.279 +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
   2.280 +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
   2.281 +POSSIBILITY OF SUCH DAMAGES.
   2.282 +
   2.283 +		     END OF TERMS AND CONDITIONS
   2.284 +
   2.285 +	    How to Apply These Terms to Your New Programs
   2.286 +
   2.287 +  If you develop a new program, and you want it to be of the greatest
   2.288 +possible use to the public, the best way to achieve this is to make it
   2.289 +free software which everyone can redistribute and change under these terms.
   2.290 +
   2.291 +  To do so, attach the following notices to the program.  It is safest
   2.292 +to attach them to the start of each source file to most effectively
   2.293 +convey the exclusion of warranty; and each file should have at least
   2.294 +the "copyright" line and a pointer to where the full notice is found.
   2.295 +
   2.296 +    <one line to give the program's name and a brief idea of what it does.>
   2.297 +    Copyright (C) <year>  <name of author>
   2.298 +
   2.299 +    This program is free software; you can redistribute it and/or modify
   2.300 +    it under the terms of the GNU General Public License as published by
   2.301 +    the Free Software Foundation; either version 2 of the License, or
   2.302 +    (at your option) any later version.
   2.303 +
   2.304 +    This program is distributed in the hope that it will be useful,
   2.305 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
   2.306 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   2.307 +    GNU General Public License for more details.
   2.308 +
   2.309 +    You should have received a copy of the GNU General Public License
   2.310 +    along with this program; if not, write to the Free Software
   2.311 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   2.312 +
   2.313 +
   2.314 +Also add information on how to contact you by electronic and paper mail.
   2.315 +
   2.316 +If the program is interactive, make it output a short notice like this
   2.317 +when it starts in an interactive mode:
   2.318 +
   2.319 +    Gnomovision version 69, Copyright (C) year name of author
   2.320 +    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
   2.321 +    This is free software, and you are welcome to redistribute it
   2.322 +    under certain conditions; type `show c' for details.
   2.323 +
   2.324 +The hypothetical commands `show w' and `show c' should show the appropriate
   2.325 +parts of the General Public License.  Of course, the commands you use may
   2.326 +be called something other than `show w' and `show c'; they could even be
   2.327 +mouse-clicks or menu items--whatever suits your program.
   2.328 +
   2.329 +You should also get your employer (if you work as a programmer) or your
   2.330 +school, if any, to sign a "copyright disclaimer" for the program, if
   2.331 +necessary.  Here is a sample; alter the names:
   2.332 +
   2.333 +  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
   2.334 +  `Gnomovision' (which makes passes at compilers) written by James Hacker.
   2.335 +
   2.336 +  <signature of Ty Coon>, 1 April 1989
   2.337 +  Ty Coon, President of Vice
   2.338 +
   2.339 +This General Public License does not permit incorporating your program into
   2.340 +proprietary programs.  If your program is a subroutine library, you may
   2.341 +consider it more useful to permit linking proprietary applications with the
   2.342 +library.  If this is what you want to do, use the GNU Library General
   2.343 +Public License instead of this License.
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/HISTORY	Sat Dec 29 14:47:40 2007 +0100
     3.3 @@ -0,0 +1,443 @@
     3.4 +VDR Plugin Revision History
     3.5 +---------------------------
     3.6 +
     3.7 +17.06.2007: Version 0.10.0 (vdr 1.4.7 / 1.5.2)
     3.8 +- Fixed directory scaning in file browser if remembered directory has been
     3.9 +  removed meanwhile. Reported by Halim Sahin.
    3.10 +- Added processing of kNext/kPrev to skip songs. Patch provided by Peter Pinnau.
    3.11 +- Made subsampling mode selection for ppmtoy4m call in example image convert
    3.12 +  script backward compatible. Suggested by C.Y.M.
    3.13 +- Fixed a missing include in mplayer.c. Reported by Dirk Vornheder.
    3.14 +- Now closing all unused filedescriptors in MPlayer child thread. Suggested by
    3.15 +  Anssi Hannula.
    3.16 +- Now defaults to slavemode enabled in MPlayer plugin.
    3.17 +- Adapted to the API changes in vdr 1.5.0+.
    3.18 +- Documentation updates.
    3.19 +
    3.20 +----------------------------------------------------------------------
    3.21 +
    3.22 +21.09.2006: Version 0.9.15 development (vdr 1.4.1-4)
    3.23 +- NOTE: This version has been tested with VDR 1.4.x only. It might be still
    3.24 +  compatible with VDR 1.2.x or 1.3.x, but this wasn't verified.
    3.25 +- In MPlayer filebrowser you can display a summary text file with key "0".
    3.26 +  Filename convention: video filename extended with ".summary", e.g.
    3.27 +  "somefile.avi" -> "somefile.avi.summary". Summary files are not shown in the
    3.28 +  browsers filelist. (was in 0.9.14 already, but was missing in history)
    3.29 +- Using ".txt" and ".nfo" as alternative extention for MPlayer summary file
    3.30 +  display. Suggested by Soeren Sonnenburg.
    3.31 +- Additionaly searching for a MPlayer summary file with the video filename
    3.32 +  extention stripped of (e.g. "somefile.summary").
    3.33 +- Added SVDR and service support to MP3 and MPlayer plugin. Based on a patches
    3.34 +  provided by Olivier Jacques and Holger Brunn.
    3.35 +- Added setup option to reverse title/artist display in MP3 plugin.
    3.36 +- Added support for basic HTTP authorization in MP3 streaming.
    3.37 +- Now passing correct name & path to VDR's status class during MPlayer playback.
    3.38 +- Added commandline option to specify a subdirectory to load sources file from.
    3.39 +  Suggested by Ronny Kornexl.
    3.40 +- Added commandline option to specify a directory for the global MPlayer resume
    3.41 +  file. Suggested by Ronny Kornexl.
    3.42 +- Added -S option to ppmtoy4m call in example image convert script. Suggested by
    3.43 +  Thorsten Gehrig.
    3.44 +- Replaced leftover usleep() calls. Thanks to Malte Schröder.
    3.45 +- Moved cleanup of ID3 cache to a seperate thread to prevent watchdog timeout.
    3.46 +  Suggested by Peter Holik.
    3.47 +- Fixed browser excludes taking effect only after the first directory change.
    3.48 +  Reported by Soeren Sonnenburg.
    3.49 +- Fixed parsing the filename from MPlayer output. Based on a fix from TomG.
    3.50 +- Fixed MPlayer volume changes. Take non-linear changes in VDR into account.
    3.51 +  Reported by Daniel Karsubka.
    3.52 +- Fixed delay when MPlayer process exits at EOF. Reported by Daniel Karsubka.
    3.53 +- Fixed MPlayer volumen handling for VDR 1.4.1-2. Note that the current code
    3.54 +  doesn't work with VDR 1.4.1 and 1.4.1-1.
    3.55 +- Fixed button translations with vdr 1.3.38+. Thanks to Ville Skyttä.
    3.56 +- Fixed some gcc 4.1 issues. Thanks to Ville Skyttä.
    3.57 +- Removed explizit libz dependency from Makefile. Suggested by Ville Skyttä.
    3.58 +- Updated Makefile according to changes in VDR 1.3.47 & 1.4.1.
    3.59 +- Updated finish translations. Provided by Rolf Ahrenberg.
    3.60 +
    3.61 +08.01.2006: Version 0.9.14 development (vdr 1.2.6/1.3.38)
    3.62 +- Fixed VDR hanging for some seconds when skipping songs and fixed pause mode 
    3.63 +  freezing live background when using OSS output. Reported by Andy Grobb.
    3.64 +- Now muting DVB audio while using OSS output.
    3.65 +- Finaly restored the function of the back key (jumps directly to plugin main
    3.66 +  menu) (vdr >= 1.3.32 only). Thanks to Sascha Volkenandt for the hint.
    3.67 +- During MPlayer playback, the name of the current videofile is now parsed from
    3.68 +  the MPlayer output. If you're using some kind of playlist hack, the MPlayer
    3.69 +  plugin will now report the correct filename. Based on suggestions from Ronny
    3.70 +  Kornexl.
    3.71 +- Extended the example mplayer.sh file to show how a playlist can be passed to
    3.72 +  MPLayer.
    3.73 +- Fixed segfault in progress display with skincurses. Thanks to Pasi Juppo for
    3.74 +  reporting.
    3.75 +- Adapted to the API changes in vdr 1.3.38.
    3.76 +
    3.77 +31.07.2005: Version 0.9.13 development (vdr 1.2.6/1.3.24)
    3.78 +- Moved OSD position selection to plugin setup menu (as before this works for
    3.79 +  non-skined vdr <1.3.7 only).
    3.80 +- Removed DVD navigation from MPlayer slavemode.
    3.81 +- Added support for configurable keys (0-9) in MPlayer slavemode. The slave
    3.82 +  command can be entered in the plugin setup menu and is sent to MPlayer during
    3.83 +  playback if the key is pressed.
    3.84 +- Added support to display a summary file (plain ascii) in the MPlayer browser.
    3.85 +  If you are on a video file, press '0' to display the summary. The summary file
    3.86 +  has to be named like the video file but with ".summary" appended (e.g.
    3.87 +  video.mpeg -> video.mpeg.summary).
    3.88 +- New MP3 plugin setup option to keep the selection menu opened after selection.
    3.89 +  Suggested by Mikko Mäkinen.
    3.90 +- Fixed ringbuffer timeouts in network code.
    3.91 +- Fixed ID3 V2 tag handling. Unusual big tags caused the ID3 scanner to mark the
    3.92 +  file as bad. Now correctly skipping ID3 tags during playback too. Reported by
    3.93 +  Olaf Henkel.
    3.94 +- Prevent writing to a broken pipe in mplayer slave control. Reported by Gerhard
    3.95 +  Steiner.
    3.96 +- Fixed passing AID 0 to mplayer.sh. To allow MPlayer autoselect the audio
    3.97 +  stream, set AID to -1 (this can only be achieved by entering 0 and than
    3.98 +  pressing left).
    3.99 +- Improved creation of LPCM frames. Based on the changes in muggle 0.1.8.
   3.100 +- Updated french translations. Provided by Jerome Rousset.
   3.101 +
   3.102 +17.03.2005: Version 0.9.12 development (vdr 1.2.6/1.3.22)
   3.103 +- Fixed background image convert hanging a long time and some other oddities.
   3.104 +- Now filtering image files in the song browser display.
   3.105 +- Extended include field in mp3sources.conf to allow multiple patterns seperated
   3.106 +  by '/' e.g. *.mp3/*.ogg/*.txt
   3.107 +- Enhanced image convert script for NTSC format. Thanks to Cym.
   3.108 +- Fixed list sorting for vdr versions before 1.3.15.
   3.109 +- Fixed MPlayer slavemode progress display reappearing on user requested close.
   3.110 +- Now explicitly setting C locale in example image convert script. Thanks to
   3.111 +  Tobias Grimm.
   3.112 +- Changed the location where cStatus::MsgReplaying is called. Helps to prevent a
   3.113 +  crash in graphlcd. In my opinion cControl API should be called from the
   3.114 +  foreground thread only. Reported by Wolfgang Fritz.
   3.115 +- Fixed a huge bunch of typos in the documentation. Thanks to Ville Skyttä.
   3.116 +- Updated finish translations. Provided by Rolf Ahrenberg & Ville Skyttä.
   3.117 +
   3.118 +20.02.2005: Version 0.9.11 development (vdr 1.2.6/1.3.21)
   3.119 +- Now using propper readdir() to scan directory contents rather than the
   3.120 +  external find & sort tools.
   3.121 +- Added cover image display during MP3 replay. See the README on how this has
   3.122 +  to be configured. If you was using the old image patch, be aware that there
   3.123 +  are now new commandline options and that the layout of the cache directory
   3.124 +  has changed. Initial patch made by Eloy, currently maintained by Tobias Grimm
   3.125 +  for ctvdr-debian package.
   3.126 +
   3.127 +06.02.2005: Version 0.9.10 development (vdr 1.2.6/1.3.20)
   3.128 +- Fixed MPlayer resume for filenames/directories containing whitespace
   3.129 +  characters.
   3.130 +- Fixed vdr 1.3.18+ compatibility with BROKEN_PCM=1.
   3.131 +- Fixed MP3 replay with vdr 1.3.19+. Thanks to Tobias Grimm.
   3.132 +- Fixed huge memory leak in song/decoder handling (approx 22kB/song). Thanks to
   3.133 +  the great valgrind tool.
   3.134 +- Fixed some mismatched new[]/delete calls.
   3.135 +- Reduced MP3 decoder memory footprint (stopped state, from 22k to 308).
   3.136 +- Fixed crashes on status display from background threads (e.g. connect to
   3.137 +  streamserver). An asyncronous status display was added.
   3.138 +- Updated finnish translations. Provided by Rolf Ahrenberg.
   3.139 +- Updated recommended library versions to libmad/libid3tag 0.15.1b and
   3.140 +  libsndfile 1.0.11.
   3.141 +- Updated patch for cdfs 0.5c. Correct tracksize for last audio track if a data
   3.142 +  track follows. Thanks to Merten Falk.
   3.143 +
   3.144 +10.01.2005: Version 0.9.9 development (vdr 1.2.6/1.3.18)
   3.145 +- Added global MPlayer resume file. This is used if the directory of the video
   3.146 +  file is not writeable. The global resume file is located in the video
   3.147 +  directory.
   3.148 +- Added DVD navigation to MPlayer slavemode. Due to this some other functions
   3.149 +  have been moved to new keys. See MANUAL file.
   3.150 +- Added some glue for vdr 1.3.18.
   3.151 +- Fixed saving of MPlayer setup option "ResumeMode". You can select if you want
   3.152 +  "local/global" or "global only" resume files too.
   3.153 +- Fixed thread deadlock in playmanager and non-blocking libsndfile reader
   3.154 +  (appearently only with some pthread versions) and high cpu usage of
   3.155 +  background scan thread when using libsndfile decoder. Thanks to Tobias Grimm
   3.156 +  for debugging.
   3.157 +- Removed workarounds for broken PCM handling in firmware (PCM pause &
   3.158 +  samplerate changes). This requires DVB firmware 261d-rc6 or newer. If you
   3.159 +  prefer to use an older firmware, you can use the make commandline option
   3.160 +  BROKEN_PCM=1 to enable old behaviour.
   3.161 +- Fixed handling of sym-links in source base path and playlists.
   3.162 +- Fixed path generation for "wide" sym-links.
   3.163 +- Replaced non-reentrant glibc functions with reentrant versions.
   3.164 +- Updated finnish translations. Provided by Rolf Ahrenberg.
   3.165 +
   3.166 +28.11.2004: Version 0.9.8 development (vdr 1.2.6/1.3.14)
   3.167 +- Added MPlayer audiostream selection. Audiostream is selected with the blue
   3.168 +  key in the MPlayer menu. The selected stream is passed to the mplayer.sh
   3.169 +  script as "AID x" where x is the stream number. For compatibility reasons the
   3.170 +  AID parameter is passed after the SLAVE parameter. Due to this, parameter
   3.171 +  positions aren't fixed any more. You have to implement a flexible parameter
   3.172 +  parsing in mplayer.sh. See the example file how this can be done. Based on a
   3.173 +  patch from VDR-Portal.
   3.174 +- Fixed segfault in shuffle code when adding a single song to the current
   3.175 +  playlist. Reported by Malte Schröder.
   3.176 +- Fixed segfault in network code (pthread_cancel). Reported by Malte Schröder.
   3.177 +- Fixed mounting/unmounting sources in MPlayer plugin. Reported by Guy Roussin.
   3.178 +- Fixed status display with open (classic) progress display for vdr 1.3.7+.
   3.179 +- Added russian translations (vdr >= 1.3.2). Provided by Vyacheslav Dikonov.
   3.180 +
   3.181 +24.09.2004: Version 0.9.7 development (vdr 1.2.6/1.3.12)
   3.182 +- Added non-blocking reader thread to libsndfile decoder.
   3.183 +- Fixed backward skip and progress display. Reported by Burkhardt Petermann.
   3.184 +- Fixed some locking problems in play manager.
   3.185 +- Fixed handling of samplerate changes for DVB & OSS output.
   3.186 +
   3.187 +13.09.2004: Version 0.9.6 development (vdr 1.2.6/1.3.12)
   3.188 +- Changed the way how the player maintains the ringbuffer to prevent audio
   3.189 +  dropouts while background scan is active.
   3.190 +- Increased player ringbuffer size and take care of ringbuffer contents when
   3.191 +  skipping forward.
   3.192 +- Prevent full file scan on MP3 decoder check.
   3.193 +- Flushing playlist on playback abort to unblock removable sources. This also
   3.194 +  prevents the background scanner to continue after playback was aborted.
   3.195 +- Removed a buffer-to-buffer copy in DVB output.
   3.196 +- Fixed several race conditions where the play manager could discard the
   3.197 +  currently played or scanned song.
   3.198 +- Added a patch for cdfs 0.5c to correct discid calculation (applies to cdfs
   3.199 +  2.4.20 as well).
   3.200 +
   3.201 +07.09.2004: Version 0.9.5 development (vdr 1.2.6/1.3.12)
   3.202 +- Resolved several oddities in progress display if the player is idle.
   3.203 +- Now removing trailing '/' from source definitions in *sources.conf and warn
   3.204 +  the user about this.
   3.205 +- Outdated entries in the MPlayer resume file are removed now.
   3.206 +- Fixed playback start delay in case the background scanner is working on the
   3.207 +  current song.
   3.208 +- Fixed progress display for net streams. Reported by Wolfgang Fritz.
   3.209 +- Fixed MPlayer rewind button (broken by sources fix in 0.9.4). Reported by
   3.210 +  Sebastian Kemper.
   3.211 +- Updated finnish translations. Provided by Rolf Ahrenberg.
   3.212 +- Added a patch that prevents the VDR core from aborting the MP3 player in
   3.213 +  black background mode (see patches subdirectory).
   3.214 +
   3.215 +03.09.2004: Version 0.9.4 development (vdr 1.2.6/1.3.12)
   3.216 +- Added a queue manager. During playback you can add new songs to the playlist
   3.217 +  by selecting them from the MP3 menu/browser. To flush the playlist you have
   3.218 +  to stop the player for now. You can add new songs even if the player is kept
   3.219 +  idle (see below).
   3.220 +- Replaced setup option "mute at end of list" with "abort player at end of
   3.221 +  list". If you set this option to "no" and the end of playlist is reached, the
   3.222 +  player is kept idle. To restart playback select a song to restart from there
   3.223 +  or "up" to restart from the begining.
   3.224 +- Background scan (formerly known as ID3 prescan) now also can determine the
   3.225 +  song level. Note that level scan requires to decode the complete song. This is
   3.226 +  done on a seperate thread with nice 5 but nevertheless it needs CPU cycles. If
   3.227 +  your system crawls, you can set background scan to "ID3 only" in the plugin
   3.228 +  setup menu.
   3.229 +- Added commanline option -C to specify the directory to place the id3cache
   3.230 +  file. The default is to place the file into the video directory.
   3.231 +- MPlayer resume feature can now be disabled in the plugin setup menu. No resume
   3.232 +  file is created in this case.
   3.233 +- Another fix to the OSS output. Now using little-endian samples only (which
   3.234 +  should be supported by all soundcard drivers).
   3.235 +- Fixed mounting/unmounting of MPlayer sources (Bug introduced in 0.9.2). Thanks
   3.236 +  to Bill Blough for spotting this one.
   3.237 +- Updated finnish translations. Provided by Rolf Ahrenberg.
   3.238 +
   3.239 +03.07.2004: Version 0.9.3 development (vdr 1.2.6/1.3.11)
   3.240 +- If a (single) selected file in the MP3 browser is actualy a playlist (*.m3u),
   3.241 +  load the playlist rather than trying to play the file. This allows to have
   3.242 +  playlists in subdirectories (by now only selectable from the browser not from
   3.243 +  the main menu). Note that song paths must be either absolute or relative to
   3.244 +  the location of the playlist file. Suggested by Helmut Auer.
   3.245 +- Now returning replay mode information in MP3 replay. Suggested by Sascha
   3.246 +  Volkenandt.
   3.247 +- Now paying attention to values returned by OSS ioctl's. This fixes playback
   3.248 +  for sound drivers which doesn't support big-endian samples. Thanks to
   3.249 +  Antti-Pekka Liedes for reporting and testing the fix.
   3.250 +- Added setup option to hide the mainmenu entry.
   3.251 +- Updated finnish translations. Provided by Rolf Ahrenberg.
   3.252 +- Fixed VDR 1.2.x compile issues.
   3.253 +- Fixed crash on status message display while the classic replay display is
   3.254 +  open.
   3.255 +
   3.256 +24.06.2004: Version 0.9.2 development (vdr 1.2.6/1.3.11)
   3.257 +- Added a setup option to toggle MP3 replay display between classic and skin
   3.258 +  version. Note that the big playlist display isn't available in skin mode.
   3.259 +- Added rewind button to MPlayer menu. Suggested by Sebastian Kemper.
   3.260 +- Updated finnish translations. Provided by Rolf Ahrenberg.
   3.261 +- Fixed current play time calculation for MPlayer without slave-patch.
   3.262 +  Reported by Uwe Scheffler.
   3.263 +- Fixed status messages which were displayed for ever. Reported by Sascha
   3.264 +  Volkenandt.
   3.265 +
   3.266 +05.06.2004: Version 0.9.1 development (vdr 1.2.6/1.3.9)
   3.267 +- Added finnish translations. Provided by Rolf Ahrenberg.
   3.268 +- Adapted to the changes in VDR 1.3.7+. Based on patches provided by Sascha
   3.269 +  Volkenandt & Sven Goethel.
   3.270 +- Fixed long standing bug in ID3 tag parsing, when the file contains an
   3.271 +  incomplete ID3 tag at the end of file. Thanks to Matt Tovey for debugging.
   3.272 +- Better german translations. Suggested by Andreas Brachold.
   3.273 +- Now ignoring dot-files in file browser. Suggested by Patrick Cernko.
   3.274 +- Now showing total play time in MPlayer progress display. The value may be
   3.275 +  inaccurate. Suggested by Patrick Cernko.
   3.276 +- Added resume capability for MPlayer. The resume position is stored in a file
   3.277 +  called ".mplayer.resume" in the directory of the MPlayer file. Based on a
   3.278 +  patch provided by Patrick Cernko.
   3.279 +- Added a setup option to mute audio at end of playlist. Based on a patch
   3.280 +  provided by Christoph Gohle.
   3.281 +- Now accepting absolute paths in playlists, even though the path must point to
   3.282 +  somewhere inside the defined source directory. Suggested by Niklaus Stutz.
   3.283 +
   3.284 +08.05.2004: Version 0.9.0 development (vdr 1.2.6/1.3.1)
   3.285 +- Added OSS soundcard output. Use make option WITH_OSS_OUTPUT=1 to compile
   3.286 +  support, commandline option -D to change DSP device and plugin setup menu to
   3.287 +  select output mode. Thanks to Gunnar Roth.
   3.288 +- Added shoutcast/icecast metadata parsing. Thanks to Andreas Brachold.
   3.289 +- Now restoring locale settings in MPlayer plugin.
   3.290 +- Fixed MPlayer exit problem while paused.
   3.291 +- Fixed showing outdated song name shortly after start of a new song.
   3.292 +
   3.293 +----------------------------------------------------------------------
   3.294 +
   3.295 +16.01.2004: Version 0.8.3 (vdr 1.2.6/1.3.1)
   3.296 +- Fixed shutting down network connection (race condition).
   3.297 +- Added a pointer to Juri's mplayer.sh in the README.
   3.298 +- Fixed compilation problem with VDR 1.3.x and Beauty-Patch.
   3.299 +
   3.300 +16.11.2003: Version 0.8.2 (vdr 1.2.6)
   3.301 +- Fixed quoting special shell characters when calling MPlayer.
   3.302 +- Now sending a "quit" to MPlayer when in slave mode rather than killing the
   3.303 +  process.
   3.304 +- Updated recommended library versions to libmad/libid3tag 0.15.0b and
   3.305 +  libsndfile 1.0.5.
   3.306 +
   3.307 +08.08.2003: Version 0.8.1 (vdr 1.2.2)
   3.308 +- Fixed network code for DOS-style \r\n (Thanks to Roland Praml).
   3.309 +
   3.310 +01.06.2003: Version 0.8.0 (vdr 1.2.0)
   3.311 +- Fixed thread cancelation in network code (valgrind hits).
   3.312 +- Stable release 0.8.0.
   3.313 +
   3.314 +----------------------------------------------------------------------
   3.315 +
   3.316 +25.05.2003: Version 0.7.15 plugin development (vdr 1.1.33)
   3.317 +- Adapted to the changes in vdr 1.1.33.
   3.318 +- Corrected the README about the fact that the plugins require vdr 1.1.29+.
   3.319 +- Added Spanish & Catalan translations. Thanks to Ramon Roca.
   3.320 +
   3.321 +18.05.2003: Version 0.7.14 plugin development (vdr 1.1.32)
   3.322 +- Fixed ringbuffer handling for changes in vdr 1.1.31/32.
   3.323 +- MPlayer progressbar can be moved up/down now. Use keys 6/9 while the
   3.324 +  progressbar is displayed.
   3.325 +- Added Swedish translations. Thanks to Jan Ekholm.
   3.326 +
   3.327 +27.04.2003: Version 0.7.13 plugin development (vdr 1.1.29)
   3.328 +- Fixed compile error due to changes in vdr 1.1.29.
   3.329 +- Fixed VDR version check.
   3.330 +
   3.331 +18.04.2003: Version 0.7.12 plugin development (vdr 1.1.27)
   3.332 +- Added support for MPlayers slave commands "get_percent_pos" &
   3.333 +  "get_time_length". Gives better results for the progressbar. The slavemode
   3.334 +  patch for MPlayer is still needed as not all video formats provide
   3.335 +  information through these slave commands.
   3.336 +- Fixed s/ms mixup in remote CDDB connect.
   3.337 +- Fixed possible deadlock-race in mp3 & snd decoder.
   3.338 +- Added OggVorbis decoder. libvorbis & libvorbisfile needed.
   3.339 +- Updated French translations. Thanks to Pierre-Henri Beguin.
   3.340 +- Added VDR version check.
   3.341 +
   3.342 +27.03.2003: Version 0.7.11 plugin development (vdr 1.1.26)
   3.343 +- Fixed MPlayer AudioDelay(). Thanks to Sven Goethel.
   3.344 +- Fixed MPlayer key repeat. Thanks to Reinhard Walter Buchner.
   3.345 +- Added yellow/green key to MPlayer control to skip back/forward a minute.
   3.346 +  Key assignment may change in future for these keys.
   3.347 +- Fixed Makefile for DVB includes.
   3.348 +- Fixed creation of cMPlayerStatus according to the rules.
   3.349 +- Fixed pipe reading and parse code. Caused all kind of weird behaviour.
   3.350 +- Some changes to the MPlayer pipe handling.
   3.351 +- Changed the network ringbuffer handling. You should use VDR version 1.1.25+
   3.352 +  or you may suffer from high cpu load during network streaming.
   3.353 +- Added a console message if a plugin fails to start due to a missing
   3.354 +  sources.conf.
   3.355 +
   3.356 +03.02.2003: Version 0.7.10 plugin development (vdr 1.1.23)
   3.357 +- Added patches for MPlayer 0.90rc1 (contributed by Beppe on the VDR ML).
   3.358 +- Adapted to the API changes in vdr 1.1.22.
   3.359 +- Removed speed drift detection. Makes not much sense with HEAD driver, as this
   3.360 +  supports non-48kHz modes anyways.
   3.361 +
   3.362 +18.12.2002: Version 0.7.9 plugin development (vdr 1.1.20)
   3.363 +- Added commandline option to specify a script which is called before & after
   3.364 +  network access (e.g. for dial-up networking) (suggested by Matthias Raus).
   3.365 +- Fixed playback speed check for (partly) corrupted files.
   3.366 +- Fixed blocking frontend thread during possibly slow playback startup.
   3.367 +- Added support for shoutcast/icecast streams. See MANUAL for URL file format
   3.368 +  and setup menu options.
   3.369 +
   3.370 +24.11.2002: Version 0.7.8 plugin development (vdr 1.1.16)
   3.371 +- Speed up local CDDB database search with huge databases. Due to this, only
   3.372 +  one subdirectory level is allowed in the database directory (usually the
   3.373 +  category name). This follows the common xmcd database layout.
   3.374 +- Added code for remote CDDB lookups. cddb.sh script is no longer needed. Please
   3.375 +  note, that the commandline options have changed.
   3.376 +- Added some status output while scanning/loading playlists to inform the user.
   3.377 +
   3.378 +13.11.2002: Version 0.7.7 plugin development (vdr 1.1.16)
   3.379 +- Updated Makefile to support Make.config.
   3.380 +- Removed a deadlock together with the rt-patch.
   3.381 +- Added support for the new remote keys (play,pause,fastfwd/rwd).
   3.382 +
   3.383 +06.10.2002: Version 0.7.6 plugin development (vdr 1.1.12)
   3.384 +- Removed a class name conflict (cSource) introduced with the changes in
   3.385 +  vdr 1.1.12.
   3.386 +- Added a field to the sources.conf files to specify which kind of files should
   3.387 +  be used on a source (see mp3sources.conf.example).
   3.388 +
   3.389 +06.09.2002: Version 0.7.5 plugin development (vdr 1.1.8)
   3.390 +- Fixed bug in new status string code, which crashed VDR if a song without
   3.391 +  artist tag was played.
   3.392 +- Fixed parsing error of MPlayer output in slavemode if "C" is not the current
   3.393 +  locale.
   3.394 +- Fixed jump to minute in MPlayer slavemode.
   3.395 +- If you have installed the "replay mode beauty patch" the new symbols are used
   3.396 +  for MPlayer progressbar too. This feature is auto-detected in the Makefile.
   3.397 +
   3.398 +01.09.2002: Version 0.7.4 plugin development (vdr 1.1.8)
   3.399 +- Changed the status string to show the loop/shuffle state too.
   3.400 +- Fixed replay problem with background mode "black". With vdr 1.1.8 you must
   3.401 +  apply the included patch to the base source.
   3.402 +- Changed the way mplayer.sh is called. Now it shouldn't be necessary to give
   3.403 +  the full path to the script if the script is in the current PATH.
   3.404 +- Added MANUAL a file.
   3.405 +- New setup option SlaveMode for MPlayer. With SlaveMode enabled you can
   3.406 +  control MPlayer via VDR. A progress display is available too. See MANUAL for
   3.407 +  details (thanks to Dariush Forouher).
   3.408 +
   3.409 +23.08.2002: Version 0.7.3 plugin development (vdr 1.1.7)
   3.410 +- Now using libsndfile 1.x.x. You must update you system. The plugin won't
   3.411 +  compile with version 0.0.x.
   3.412 +- Fixed detection if MPlayer is still running.
   3.413 +- Fixed GetIndex() to return frames rather than seconds (thanks to Martin
   3.414 +  Hammerschmidt).
   3.415 +- The last second of the last song in a playlist wasn't played. Now waiting
   3.416 +  until the ringbuffer is really empty.
   3.417 +
   3.418 +16.08.2002: Version 0.7.2 plugin development (vdr 1.1.7)
   3.419 +- Fixed gcc 2.96 compiling error due to variable name "not".
   3.420 +- Fixed a undefined symbol error when compiling without libsndfile.
   3.421 +- Fixed a deadlock in the MP3 player.
   3.422 +- Removed old stale LCD code parts.
   3.423 +- Now using free() on all strings.
   3.424 +- Adapted to the API changes in vdr 1.1.7.
   3.425 +
   3.426 +11.08.2002: Version 0.7.1 plugin development (vdr 1.1.6)
   3.427 +- Added some calls to the status monitor.
   3.428 +- Fixed internationalization (thanks to David Spiller).
   3.429 +- Fixed jump to the next song at the end of current song (thanks to David
   3.430 +  Spiller).
   3.431 +- Replaced all busy usleep wait loops with proper condition variables.
   3.432 +- Removed one thread level in the MPlayer plugin.
   3.433 +
   3.434 +08.08.2002: Version 0.7.0 plugin development (vdr 1.1.6)
   3.435 +- Updated the source base to release version 0.6.2.
   3.436 +- Added MPlayer plugin.
   3.437 +- MP3 playback now functionaly.
   3.438 +
   3.439 +13.05.2002: Version 0.0.2 plugin development (vdr 1.1.2)
   3.440 +- Added setup menu page and saving of setup options.
   3.441 +- Adding to playlist and playlist rename now functional. Beside playback all
   3.442 +  menus are (better should be) working.
   3.443 +- Now using confdir/plugins/mp3sources.conf for sources config.
   3.444 +
   3.445 +10.05.2002: Version 0.0.1 plugin development (vdr 1.1.1)
   3.446 +- Initial revision.
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/MANUAL	Sat Dec 29 14:47:40 2007 +0100
     4.3 @@ -0,0 +1,480 @@
     4.4 +
     4.5 +This is a dual-plugin for VDR.
     4.6 +The "MP3-Plugin" allows playback of MP3 and other audio files.
     4.7 +The "MPlayer-Plugin" is used to call MPlayer for playback of video
     4.8 +files (e.g. DivX)
     4.9 +
    4.10 +Written by:                  Stefan Hülswitt <s.huelswitt@gmx.de>
    4.11 +Project's homepage:          http://www.muempf.de/
    4.12 +Latest version available at: http://www.muempf.de/down/
    4.13 +
    4.14 +See the file COPYING for license information.
    4.15 +
    4.16 +----------------------------------------------------------------------
    4.17 +
    4.18 +For install instructions see the README file.
    4.19 +For MPlayer plugin documentation skip to the end this file.
    4.20 + 
    4.21 +****
    4.22 +****
    4.23 +**** MP3 plugin
    4.24 +****
    4.25 +****
    4.26 +
    4.27 +The basic concept of the MP3 plugin is to use playlists for the songs you want
    4.28 +to play. So most things designed toward playlists, but you can play directories
    4.29 +and single files as well.
    4.30 +
    4.31 +Available audio codecs:
    4.32 +  libmad     Supports MPEG-1/2/2.5 with layers 1/2/3.
    4.33 +  libsndfile Supports MS WAV/A-law/u-law; Apple/SGI AIFF/AIFC; Sun/NeXT AU/SND;
    4.34 +             Amiga IFF/8SVX/16SV and more.
    4.35 +  libvorbis  Supports OGG.
    4.36 +
    4.37 +The starting point for all MP3 actions (or more generally: all song actions)
    4.38 +is the MP3 menu. Select the MP3 entry from VDR's main menu to enter
    4.39 +this menu.
    4.40 +
    4.41 +The MP3 menu:
    4.42 +-------------
    4.43 +
    4.44 +A list of available playlists is displayed (only playlists in your base
    4.45 +directory of the selected source are displayed, there is no recursive scanning
    4.46 +for playlists). Select the a playlist with "up" and "down". Press "OK" to start
    4.47 +playback with the selected playlist.
    4.48 +
    4.49 +Press the "blue" key to see a second level of buttons. If you are on the second
    4.50 +level, press the "red" button to return to the first level.
    4.51 +
    4.52 +If you are on the first level, press "red" to enter the playlist editor with
    4.53 +the selected playlist (see Playlist editor). Press "green" to enter the source
    4.54 +selector (see Sources). Press "yellow" to enter the file browser (see Browsing
    4.55 +and instant playlists).
    4.56 +
    4.57 +On the second level, press "green" to create a new, empty playlist and enter
    4.58 +the playlist editor. The new playlist will be named "unnamed" followed by a
    4.59 +number. Press "yellow" to delete a playlist. There is a double confirmation
    4.60 +needed to really delete the playlist. Press "blue" to rename a playlist. You
    4.61 +are prompted for a new name. Press "back" to abort renaming.
    4.62 +
    4.63 +During playback:
    4.64 +----------------
    4.65 +
    4.66 +During playback you will see the channel which was tuned last or a black screen
    4.67 +depending on what you choose in setup (see Setup options). You can use some
    4.68 +keys to control playback:
    4.69 +
    4.70 +Down        skips back to the start of current song or to previous song if you
    4.71 +            are at the beginning of current song.
    4.72 +
    4.73 +Up          skips forward to the start of next song.
    4.74 +
    4.75 +Right/Left  skips back/forward 3 seconds in current song. Hold key to scan
    4.76 +            through song.
    4.77 +
    4.78 +Red         enters jump mode. Enter the number of minutes/seconds you want to
    4.79 +            jump with the number keys. Press "left" to jump backwards, "right"
    4.80 +            to jump forward and "up" to jump to the absolute position. Press
    4.81 +            "blue" to toggle the jump unit between minute (m) and seconds (s).
    4.82 +            Any other key cancels jump mode.
    4.83 +            Note: jumping forward requires to scan all frame headers until
    4.84 +            the new position. On slow media or with long jumps this may be
    4.85 +            visible in the progress display.
    4.86 +
    4.87 +Green       toggles loop and shuffle mode. Press once to enable loop, twice for
    4.88 +            loop and shuffle and three times for shuffle only. To disable
    4.89 +            shuffle, wait >4 seconds and press again.
    4.90 +
    4.91 +Yellow      is pause/unpause.
    4.92 +
    4.93 +Blue        aborts playback.
    4.94 +
    4.95 +Back        aborts playback and returns to MP3 menu.
    4.96 +
    4.97 +Ok          toggles progress display. If "ok" is pressed again within 4
    4.98 +            seconds, the playlist window is opened (playlist window is
    4.99 +            available with classic progress display mode only). The color bar
   4.100 +            marks the current song. If the playlist window is open, you can
   4.101 +            page through the playlist with "left" and "right". If available
   4.102 +            title/artist is displayed. This is true for songs already played or
   4.103 +            which have been scanned in background (see Setup options).
   4.104 +
   4.105 +Menu        enters VDR's main menu.
   4.106 +
   4.107 +0-9         direct song selection. Selection timeout is 1 second.
   4.108 +
   4.109 +The progress display shows various information bits which are flipped every few
   4.110 +seconds. You can adjust which information is shown (see Setup options).
   4.111 +
   4.112 +The playlist editor:
   4.113 +--------------------
   4.114 +
   4.115 +In the playlist editor you can add and remove files and shuffle them around.
   4.116 +
   4.117 +Press "red" to add songs to the playlist. A directory browser is started. You
   4.118 +will see the directories and files you created beneath you the base directory
   4.119 +of the current source. The entries surrounded by [ ] are directories. If you
   4.120 +press "ok" on a directory, you will decent to this directory. Press "red" to
   4.121 +add the current file/directory to the playlist. Selecting a directory adds
   4.122 +recursively all files from the directory and subdirectories. If you have
   4.123 +selected more than one file, you have to confirm the action. The new file(s)
   4.124 +will be inserted below the currently selected file in the playlist.
   4.125 +
   4.126 +Press "green" to toggle between display of filenames and titles/artists (if
   4.127 +available). The initial display of title/artist may take some time as all the
   4.128 +files have to be scanned. You can configure if the editor is started with
   4.129 +title/artist or filename display (see Setup options). Press "ok" over an entry
   4.130 +to display a information page for this file.
   4.131 +
   4.132 +Song information which have been scanned in the playlist editor or during
   4.133 +playback are saved to a file called "id3info.cache" located in the video
   4.134 +directory. This file is used to speed up display of title/artist for files you
   4.135 +already touched. The file is loaded on startup and saved regularly while VDR is
   4.136 +running.
   4.137 +
   4.138 +Press "yellow" to remove a song from the playlist (the file IS NOT removed from
   4.139 +disk, of course). Press "blue" to reorder the songs in the playlist.
   4.140 +
   4.141 +Browsing and instant playlists:
   4.142 +-------------------------------
   4.143 +
   4.144 +If you enter the browser you can browser through your song files. You can
   4.145 +start playback for individual files, playlists and whole directories from here
   4.146 +too.
   4.147 +
   4.148 +Position on a file or directory and select "red" to start playback. A "instant"
   4.149 +playlist is created which contains either the selected file or all files from
   4.150 +the selected directory and its subdirectories. When scanning directories, all
   4.151 +files matching "*.m3u" (playlists) are ignored. The "instant" playlist is
   4.152 +deleted if you stop playback.
   4.153 +
   4.154 +In the basedir you can press "yellow" to play all files in all directories.
   4.155 +Press "blue" over an entry to display the song information page.
   4.156 +
   4.157 +Sources and playlists:
   4.158 +----------------------
   4.159 +
   4.160 +It's fine to have all your songs on harddisk, but may be you have some CDROM's
   4.161 +with song files on them and you want to play them directly from CDROM? Then
   4.162 +this is what you are looking for!
   4.163 +
   4.164 +You can define multiple sources from which your songs could be played. At
   4.165 +runtime you can select which source to use, you can mount, unmount and eject
   4.166 +the source at runtime, too. This is done through a config file and a simple
   4.167 +shell script.
   4.168 +
   4.169 +First you have to create a config file named "mp3sources.conf" located in the
   4.170 +"plugins" subdirectory of the directory where you keep the other config files
   4.171 +for VDR (e.g. if your VDR configfiles are in "/video" you must create the files
   4.172 +as "/video/plugins/mp3sources.conf").
   4.173 +
   4.174 +Every line defines a source (see the example config file which comes with the
   4.175 +archive). You need three information for a source: the base directory, a
   4.176 +description and a flag which determines if a mount/unmount/eject command is
   4.177 +applicable to this source. Optionally you can give a fourth information to
   4.178 +specify which kind of files should be used on this source. The fields must be
   4.179 +separated by a semicolon. The basedir must be a real directory. Using a symlink
   4.180 +to a directory will not work.
   4.181 +
   4.182 +So a valid line could be:
   4.183 +  /mp3;Local files;0
   4.184 +This means that the base directory is /mp3, the description say that these are
   4.185 +local files and mount/unmount/eject commands can not be applied here.
   4.186 +
   4.187 +If you want to ignore all files without the ".mp3" extension, you could use:
   4.188 +  /mp3;Local files;0;*.mp3
   4.189 +You can give multiple patterns separated with a slash:
   4.190 +  /mp3;Local files;0;*.mp3/*.ogg/*.wav
   4.191 +
   4.192 +Another useful one:
   4.193 +  /cdrom;CDROM;1
   4.194 +This means that the base directory is /cdrom, which is obviously a CDROM drive
   4.195 +and mount/unmount/eject commands can be applied here.
   4.196 +
   4.197 +Note some important things for using mount/unmount/eject commands here:
   4.198 + - You must have defined an entry in your /etc/fstab for the base directory.
   4.199 + - The user running VDR must have permission to mount/unmount the device (e.g.
   4.200 +   add "users" to the options in /etc/fstab).
   4.201 + - You must have a mount script which can be called from VDR (see below).
   4.202 +
   4.203 +The actual mount/unmount/eject action is done with a script. See the README
   4.204 +file and the example "mount.sh" which comes with the archive.
   4.205 +
   4.206 +You can create arbitrary directories below the base directory to group your
   4.207 +songs. BUT all playlists have to be located in the base directory (the tree is
   4.208 +not scanned recursively for playlists).
   4.209 +
   4.210 +A playlist is a simple text file which contains the paths of the songs to play.
   4.211 +One path/filename on every line. The paths must be relative to the base
   4.212 +directory (e.g. if you have a MP3 file /mp3/rock/bon_jovi/sample.mp3 a proper
   4.213 +line in a playlist would be rock/bon_jovi/sample.mp3). All playlist must have
   4.214 +the extension ".m3u".
   4.215 +
   4.216 +You also can load WinAmp-style playlists, this means that comment lines
   4.217 +starting with "#" are ignored and if a line "#EXTM3U" is found, the pathnames
   4.218 +are converted from DOS-style to UNIX-style (changing "\" to "/"). The DOS-style
   4.219 +pathnames must not contain "/" for proper conversion.
   4.220 +
   4.221 +The sources menu:
   4.222 +-----------------
   4.223 +
   4.224 +If you enter this menu, you will get the list of the sources which you have
   4.225 +defined in "mp3sources.conf". Entries marked with ">" can be mounted/unmounted.
   4.226 +An entry marked with "*" is currently mounted.
   4.227 +
   4.228 +Use the "red" key to select a source. All playback and editing functions will
   4.229 +refer only to the selected source. Press "green" to mount the source, "yellow"
   4.230 +to unmount and "blue" to eject the media.
   4.231 +
   4.232 +Setup options:
   4.233 +--------------
   4.234 +
   4.235 +There are various configuration options which can be changed from the plugin
   4.236 +setup menu. Select VDR's setup menu, select "Plugins" and select "mp3" to enter
   4.237 +the setup menu.
   4.238 +
   4.239 +Audio output mode:    The MP3 plugin supports alternative sound output modes
   4.240 +                      (if activated at compile time). Use this option to
   4.241 +                      select the desired output mode.
   4.242 +
   4.243 +Audio mode:           The MP3 decoder of libmad delivers 24bit data which must
   4.244 +                      be scaled to 16bit for output. You can select how this is
   4.245 +                      done. "round" simply cuts of the LSB bits, while "dither"
   4.246 +                      implements a error diffusion strategy. "dither" takes
   4.247 +                      slightly more CPU power (about 1% on my 400Mhz Celeron).
   4.248 +
   4.249 +Use 48kHz mode only:  Forces the plugin to use the default DVB samplerate of
   4.250 +                      48kHz only. All other are resampled to this value.
   4.251 +
   4.252 +Display mode:         Choose which information is shown in the progress
   4.253 +                      display:
   4.254 +                        1 - shows only title and artist.
   4.255 +                        2 - additionally shows album and year.
   4.256 +                        3 - additionally shows samplerate, bitrate and number
   4.257 +                            of channels.
   4.258 +
   4.259 +Background mode:      Choose what you want to see during playback:
   4.260 +                      Black  - a black screen
   4.261 +                      Live   - live video from the last tuned channel
   4.262 +                      Images - display cover images (if available)
   4.263 +
   4.264 +Initial loop mode:    Choose if loop mode should be enabled by default.
   4.265 +
   4.266 +Initial shuffle mode: Choose if shuffle mode should be enabled by default.
   4.267 +
   4.268 +Abort player at end of list: If you set this option to "no" and the end of
   4.269 +                      playlist is reached, the player is kept idle. To restart
   4.270 +                      playback select a song to restart from there or "up" to
   4.271 +                      restart from the beginning.
   4.272 +
   4.273 +Background mode:      Choose if the background scanner is enabled during
   4.274 +                      playback. In the playlist window, title/artist is shown
   4.275 +                      only for songs already played or which have been scanned
   4.276 +                      in background. There are two scan modes: "ID3 only"
   4.277 +                      gathers information from ID3 tags only, while "ID3 &
   4.278 +                      Level" pre-calculates the level for the normalizer as
   4.279 +                      well. Note that level scan requires to decode the
   4.280 +                      complete song. This is done on a separate thread with
   4.281 +                      nice 5 but nevertheless it needs CPU cycles. If you have
   4.282 +                      a slow CPU your system may crawl.
   4.283 +
   4.284 +Editor display mode:  Choose if the playlist editor shows title/artist or
   4.285 +                      filenames by default. Be warned: the initial display of
   4.286 +                      the title/artist may take some time, as all the files in
   4.287 +                      the playlist have to be scanned. This is specially true
   4.288 +                      for slower storage media.
   4.289 +
   4.290 +Main menu mode:       Choose if you want to see your playlists or if you want
   4.291 +                      to jump to the directory browser when entering the MP3
   4.292 +                      menu.
   4.293 +
   4.294 +Normalizer level:     The volume level for the normalizer. Allowed range is
   4.295 +                      0-50. If set to zero the normalizer is disabled (see The
   4.296 +                      normalizer).
   4.297 +
   4.298 +Limiter level:        The volume level for the limiter. Samples above this
   4.299 +                      level are limited. Allowed range is 25-100. If set to 100
   4.300 +                      the limiter is disabled (see The normalizer).
   4.301 +
   4.302 +Use HTTP proxy:       Enables use of a HTTP proxy server when playing
   4.303 +                      Shoutcast/Icecast streams.
   4.304 +
   4.305 +HTTP proxy host:      The hostname of the HTTP proxy (used only if you have
   4.306 +                      enabled HTTP proxy option above).
   4.307 +
   4.308 +HTTP proxy port:      The port number to use on the HTTP proxy server (used
   4.309 +                      only if you have enabled HTTP proxy option above).
   4.310 +
   4.311 +CDDB for CD-Audio:    Enables lookups to the CDDB database if cd-audio is
   4.312 +                      played via cdfs. You can choose between local only and
   4.313 +                      local&remote lookups.
   4.314 +
   4.315 +CDDB server:          The hostname of the CDDB server (used only if you have
   4.316 +                      enabled remote lookups above).
   4.317 +
   4.318 +CDDB port:            The port number to use on the CDDB server (used only if
   4.319 +                      you have enabled remote lookups above).
   4.320 +
   4.321 +Playing Shoutcast/Icecast streams:
   4.322 +----------------------------------
   4.323 +
   4.324 +The plugin is able to play Shoutcast and Icecast streams (which in fact are
   4.325 +just streamed MP3's). This feature needs some special setup:
   4.326 +
   4.327 +First, your VDR machine must have a connection to the internet (either directly
   4.328 +or through a proxy).
   4.329 +
   4.330 +Second, you have to create a simple text file in any of your MP3 source
   4.331 +directories for every stream you want to play. The file must contain a single
   4.332 +line of text with the complete URL of the stream. E.g. the file could contain
   4.333 +(no guarantee that this link still works):
   4.334 +http://152.163.134.164:80/stream/1012
   4.335 +The link must point to the stream itself and not to any kind of playlist.
   4.336 +
   4.337 +To play the stream, add the text file to a playlist or select the file from the
   4.338 +browser. You cannot pause, FWD or REW a stream.
   4.339 +
   4.340 +Note:
   4.341 +- If you internet connection doesn't provides the bandwidth the stream
   4.342 +  requires, playback will be distorted.
   4.343 +- Any network operation has a timeout of 30 seconds (in case the stream server
   4.344 +  stalls). You should not set VDR's watchdog timer below this value.
   4.345 +- If the stream server provides special Icecast headers or metadata, these
   4.346 +  values are displayed in the progress display.
   4.347 +
   4.348 +The normalizer:
   4.349 +---------------
   4.350 +
   4.351 +Very often songs from different albums are recorded at different volume levels.
   4.352 +If you have playlists with songs from different albums, it's very likely that
   4.353 +you keep adjusting the volume at you amplifier all the time. This is why the
   4.354 +MP3 plugin has a function to normalize the volume level of all songs to a
   4.355 +common level.
   4.356 +
   4.357 +The algorithm to calculate and to adjust the volume level was taken from the 
   4.358 +normalize project <http://www.cs.columbia.edu/~cvaill/normalize/> (version 0.7)
   4.359 +from Chris Vaill. Basically the song is divided into chunks, for which the peak
   4.360 +level is calculated. From the peak level a moving average value is calculated
   4.361 +and the maximum of this is considered a measure for the perceived volume.
   4.362 +Please refer to the normalize homepage for more details.
   4.363 +
   4.364 +This approach has one drawback: you must first decode the complete file to
   4.365 +calculate the volume level. For this reason, the normalize function can not be
   4.366 +applied if you are listening a song for the very first time. In this case the
   4.367 +volume level is calculated and stored to the song cache file. If you are
   4.368 +listening to the same songs again, the volume level is read back and the
   4.369 +normalize function is applied.
   4.370 +
   4.371 +There are two parameters for the normalize function which can be changed via
   4.372 +setup menu:
   4.373 +
   4.374 +The "target level": this is the volume level to which all songs are normalized.
   4.375 +The allowed range is 0-50, while useful values are between 25 and 30. Setting
   4.376 +the target level to zero disables the normalize function. You shouldn't use
   4.377 +this parameter as a volume adjustment (volume adjustment should be done at your
   4.378 +amplifier). You should set this parameter slightly above the average volume
   4.379 +level of your songs (in my case this is 27). You can use the normalize tool
   4.380 +from <http://www.cs.columbia.edu/~cvaill/normalize/> to calculate the volume
   4.381 +level for all your songs (if invoked with --fractions, normalize returns a
   4.382 +volume level which must be multiplied with 100 to be comparable).
   4.383 +
   4.384 +The "limiter level": this is the volume level for the limiter function. When
   4.385 +boosting up the volume of a songs, it may very well happen that individual
   4.386 +samples exceed the allowed range. In this case, one could simply clip the
   4.387 +sample to the allowed range, but this would remove too much information from
   4.388 +the audio data. So a kind of dynamic compression is applied to the exceeding
   4.389 +samples to bring them back to the allowed range without loosing to much. The
   4.390 +dynamic compression is applied to all sample above the limiter level. The
   4.391 +allowed range is 25-100. Setting the limiter level to 100 disables the dynamic
   4.392 +compression and returns to clipping. I'm not sure about the useful range of
   4.393 +this parameter. While normalize uses 25, I prefer 70 as this leaves much more
   4.394 +audio data untouched.
   4.395 +
   4.396 +****
   4.397 +****
   4.398 +**** MPlayer plugin
   4.399 +**** 
   4.400 +****
   4.401 +
   4.402 +The MPlayer plugin is basically a front-end to MPlayer
   4.403 +<http://www.MPlayerHQ.hu/>. You can select a video file from a browser which
   4.404 +then is replayed with MPlayer.
   4.405 +
   4.406 +The MPlayer menu:
   4.407 +-----------------
   4.408 +
   4.409 +The MPlayer menu is very similar to the the directory browser of the MP3
   4.410 +plugin. You will see the directories and files you created beneath you the base
   4.411 +directory of the current source (how to define sources is explained in "Sources
   4.412 +and playlists" in the MP3 plugin section, but the config file is named
   4.413 +"mplayersources.conf"). The entries surrounded by [ ] are directories. If you
   4.414 +press "ok" on a directory, you will descend to this directory. Press "green" to
   4.415 +return to the parent directory. Use "yellow" to select a different source (see
   4.416 +"The sources menu" in the MP3 plugin section).
   4.417 +
   4.418 +Press "red" to start replay of the current file. MPlayer will be started with
   4.419 +this file through a shell script (see README file).
   4.420 +
   4.421 +There are two different ways to control MPlayer during replay. The control mode
   4.422 +is selected from the MPlayer plugin setup menu.
   4.423 +
   4.424 +The traditional mode:
   4.425 +---------------------
   4.426 +
   4.427 +In traditional mode, only "blue" from VDR's remote is active to abort the
   4.428 +replay. No other action is passed to MPlayer. It's up to you to configure
   4.429 +MPlayer to use whatever control device you want (e.g. LIRC, keyboard).
   4.430 +
   4.431 +The slave mode:
   4.432 +---------------
   4.433 +
   4.434 +In slave mode the MPlayer plugin acts as a control frontend to MPlayer. All
   4.435 +important actions are passed from VDR's remote to MPlayer (see README file on
   4.436 +how to setup your mplayer.sh script for this mode). You can use the following
   4.437 +keys to control playback:
   4.438 +
   4.439 +Down        is pause/unpause.
   4.440 +
   4.441 +Up          returns to normal replay.
   4.442 +
   4.443 +Right/Left  skips back/forward 10 seconds.
   4.444 +
   4.445 +Red         enters jump mode. Enter the number of minutes/percent you want to
   4.446 +            jump with the number keys. Press "left" to jump backwards, "right"
   4.447 +            to jump forward and "up" to jump to the absolute position. Press
   4.448 +            "blue" to toggle the jump unit between minute (m) and percent (%).
   4.449 +            Any other key cancels jump mode.
   4.450 +
   4.451 +Green       skips back 60 seconds.
   4.452 +
   4.453 +Yellow      skips forward 60 seconds.
   4.454 +
   4.455 +Blue/Back   aborts playback.
   4.456 +
   4.457 +Ok          toggles progress display.
   4.458 +
   4.459 +0-9         send MPlayer slave command (configurable in plugin setup menu).
   4.460 +
   4.461 +In addition the "Volume+","Volume-" and "Mute" keys are routed to MPlayer.
   4.462 +
   4.463 +Setup options:
   4.464 +--------------
   4.465 +
   4.466 +There are some configuration options which can be changed from the plugin setup
   4.467 +menu. Select VDR's setup menu, select "Plugins" and select "mplayer" to enter
   4.468 +the setup menu.
   4.469 +
   4.470 +Control mode:         The MPlayer plugin supports two control modes during
   4.471 +                      replay: traditional and slave (see description above).
   4.472 +
   4.473 +Resume mode:          Selects the mode for resuming playback. "local first"
   4.474 +                      means that the plugin first tries to use a resume file in
   4.475 +                      the directory of the video file. Only if this directory is
   4.476 +                      non-writeable the global resume file is used. "global
   4.477 +                      only" will use the global file only and "disabled"
   4.478 +                      disables resume completely.
   4.479 +
   4.480 +Slave command key:    Customize the slave commands which are send to MPlayer
   4.481 +                      when the remote keys "0" to "9" are pressed.
   4.482 +
   4.483 +Hide main menu entry: Hides the MPlayer menu entry from the main menu.
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/Makefile	Sat Dec 29 14:47:40 2007 +0100
     5.3 @@ -0,0 +1,178 @@
     5.4 +#
     5.5 +# MP3/MPlayer plugin to VDR
     5.6 +#
     5.7 +# (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
     5.8 +#
     5.9 +# This code is free software; you can redistribute it and/or
    5.10 +# modify it under the terms of the GNU General Public License
    5.11 +# as published by the Free Software Foundation; either version 2
    5.12 +# of the License, or (at your option) any later version.
    5.13 +#
    5.14 +# This code is distributed in the hope that it will be useful,
    5.15 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.16 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.17 +# GNU General Public License for more details.
    5.18 +#
    5.19 +# You should have received a copy of the GNU General Public License
    5.20 +# along with this program; if not, write to the Free Software
    5.21 +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    5.22 +# Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    5.23 +
    5.24 +# You can change the compile options here or create a Make.config
    5.25 +# in the VDR directory an set them there.
    5.26 +
    5.27 +### uncomment one of these lines, if you don't want one of the plugins
    5.28 +#WITHOUT_MP3=1
    5.29 +#WITHOUT_MPLAYER=1
    5.30 +
    5.31 +### uncomment the following line, if you don't have libsndfile installed
    5.32 +#WITHOUT_LIBSNDFILE=1
    5.33 +
    5.34 +### uncomment the following line, if you don't have libvorbisfile installed
    5.35 +#WITHOUT_LIBVORBISFILE=1
    5.36 +
    5.37 +### uncomment the following line, if you want OSS sound output
    5.38 +#WITH_OSS_OUTPUT=1
    5.39 +
    5.40 +### uncomment the following line, if you want to include debug symbols
    5.41 +#DBG=1
    5.42 +
    5.43 +### The C++ compiler and options:
    5.44 +CXX      ?= g++
    5.45 +CXXFLAGS ?= -O2 -fPIC -Wall -Woverloaded-virtual
    5.46 +
    5.47 +###############################################
    5.48 +###############################################
    5.49 +#
    5.50 +# no user configurable options below this point
    5.51 +#
    5.52 +###############################################
    5.53 +###############################################
    5.54 +
    5.55 +### The directory environment:
    5.56 +
    5.57 +VDRDIR = ../../..
    5.58 +LIBDIR = ../../lib
    5.59 +TMPDIR = /tmp
    5.60 +
    5.61 +# The official name of this plugin.
    5.62 +# This name will be used in the '-P...' option of VDR to load the plugin.
    5.63 +# By default the main source file also carries this name.
    5.64 +#
    5.65 +PLUGIN  = mp3
    5.66 +PLUGIN2 = mplayer
    5.67 +
    5.68 +### Allow user defined options to overwrite defaults:
    5.69 +
    5.70 +-include $(VDRDIR)/Make.config
    5.71 +
    5.72 +### The version number of this plugin (taken from the main source file):
    5.73 +
    5.74 +VERSION = $(shell grep 'define PLUGIN_VERSION' version.h | awk '{ print $$3 }' | sed -e 's/[";]//g')
    5.75 +
    5.76 +### The version number of VDR (taken from VDR's "config.h"):
    5.77 +
    5.78 +VDRVERSION = $(shell sed -ne '/define VDRVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
    5.79 +APIVERSION = $(shell sed -ne '/define APIVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
    5.80 +ifeq ($(strip $(APIVERSION)),)
    5.81 +   APIVERSION = $(VDRVERSION)
    5.82 +endif
    5.83 +VDRVERSNUM = $(shell sed -ne '/define VDRVERSNUM/ s/^.[a-zA-Z ]*\([0-9]*\) .*$$/\1/p' $(VDRDIR)/config.h)
    5.84 +APIVERSNUM = $(shell sed -ne '/define APIVERSNUM/ s/^.[a-zA-Z ]*\([0-9]*\) .*$$/\1/p' $(VDRDIR)/config.h)
    5.85 +ifeq ($(strip $(APIVERSNUM)),)
    5.86 +   APIVERSNUM = $(VDRVERSNUM)
    5.87 +endif
    5.88 +
    5.89 +### The name of the distribution archive:
    5.90 +
    5.91 +ARCHIVE = $(PLUGIN)-$(VERSION)
    5.92 +PACKAGE = vdr-$(ARCHIVE)
    5.93 +
    5.94 +### Includes and Defines (add further entries here):
    5.95 +
    5.96 +INCLUDES += -I$(VDRDIR)/include
    5.97 +DEFINES  += -D_GNU_SOURCE -DAPIVERSNUM=$(APIVERSNUM)
    5.98 +
    5.99 +### The object files (add further files here):
   5.100 +
   5.101 +ifndef WITHOUT_MP3
   5.102 +  ALL += libvdr-$(PLUGIN).so
   5.103 +endif
   5.104 +ifndef WITHOUT_MPLAYER
   5.105 +  ALL += libvdr-$(PLUGIN2).so
   5.106 +endif
   5.107 +
   5.108 +COM_OBJS = i18n.o data.o menu.o
   5.109 +
   5.110 +OBJS     = $(PLUGIN).o $(COM_OBJS)\
   5.111 +            data-mp3.o setup-mp3.o player-mp3.o stream.o network.o\
   5.112 +            decoder.o decoder-mp3.o decoder-mp3-stream.o decoder-snd.o \
   5.113 +            decoder-ogg.o
   5.114 +LIBS     = -lmad -lid3tag
   5.115 +
   5.116 +ifndef WITHOUT_LIBSNDFILE
   5.117 +  LIBS    += -lsndfile
   5.118 +  DEFINES += -DHAVE_SNDFILE
   5.119 +endif
   5.120 +ifndef WITHOUT_LIBVORBISFILE
   5.121 +  LIBS    += -lvorbisfile -lvorbis
   5.122 +  DEFINES += -DHAVE_VORBISFILE
   5.123 +endif
   5.124 +ifdef WITH_OSS_OUTPUT
   5.125 +  DEFINES += -DWITH_OSS
   5.126 +endif
   5.127 +ifdef BROKEN_PCM
   5.128 +  DEFINES += -DBROKEN_PCM
   5.129 +endif
   5.130 +
   5.131 +OBJS2    = $(PLUGIN2).o $(COM_OBJS)\
   5.132 +            setup-mplayer.o player-mplayer.o
   5.133 +LIBS2    = 
   5.134 +
   5.135 +ifeq ($(shell test -f $(VDRDIR)/fontsym.h ; echo $$?),0)
   5.136 +  DEFINES += -DHAVE_BEAUTYPATCH
   5.137 +endif  
   5.138 +
   5.139 +ifdef DBG
   5.140 +CXXFLAGS += -g
   5.141 +endif
   5.142 +
   5.143 +### Implicit rules:
   5.144 +
   5.145 +%.o: %.c
   5.146 +	$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
   5.147 +
   5.148 +# Dependencies:
   5.149 +
   5.150 +MAKEDEP = g++ -MM -MG
   5.151 +DEPFILE = .dependencies
   5.152 +$(DEPFILE): Makefile
   5.153 +	@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) $(OBJS2:%.o=%.c) > $@
   5.154 +
   5.155 +-include $(DEPFILE)
   5.156 +
   5.157 +### Targets:
   5.158 +
   5.159 +all: $(ALL)
   5.160 +
   5.161 +libvdr-$(PLUGIN).so: $(OBJS)
   5.162 +	$(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@
   5.163 +	@cp $@ $(LIBDIR)/$@.$(APIVERSION)
   5.164 +
   5.165 +libvdr-$(PLUGIN2).so: $(OBJS2)
   5.166 +	$(CXX) $(CXXFLAGS) -shared $(OBJS2) $(LIBS2) -o $@
   5.167 +	@cp $@ $(LIBDIR)/$@.$(APIVERSION)
   5.168 +
   5.169 +i18ntest: i18ntest.c i18n.c i18n.h
   5.170 +	$(CXX) $(CXXFLAGS) $(INCLUDES) $< -o $@
   5.171 +
   5.172 +dist: clean
   5.173 +	@-rm -rf $(TMPDIR)/$(ARCHIVE)
   5.174 +	@mkdir $(TMPDIR)/$(ARCHIVE)
   5.175 +	@cp -a * $(TMPDIR)/$(ARCHIVE)
   5.176 +	@tar czf $(PACKAGE).tar.gz -C $(TMPDIR) $(ARCHIVE)
   5.177 +	@-rm -rf $(TMPDIR)/$(ARCHIVE)
   5.178 +	@echo Distribution package created as $(PACKAGE).tar.gz
   5.179 +
   5.180 +clean:
   5.181 +	@-rm -f $(OBJS) $(OBJS2) $(DEPFILE) i18ntest libvdr-*.so $(PACKAGE).tar.gz core* *~
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/README	Sat Dec 29 14:47:40 2007 +0100
     6.3 @@ -0,0 +1,213 @@
     6.4 +
     6.5 +This is a dual-plugin for VDR.
     6.6 +The "MP3-Plugin" allows playback of MP3 and other audio files.
     6.7 +The "MPlayer-Plugin" is used to call MPlayer for playback of video
     6.8 +files (e.g. DivX)
     6.9 +
    6.10 +Written by:                  Stefan Hülswitt <s.huelswitt@gmx.de>
    6.11 +Project's homepage:          http://www.muempf.de/
    6.12 +Latest version available at: http://www.muempf.de/down/
    6.13 +
    6.14 +See the file COPYING for license information.
    6.15 +
    6.16 +----------------------------------------------------------------------
    6.17 +
    6.18 +For the user manual see the MANUAL file.
    6.19 +
    6.20 +
    6.21 +Installing:
    6.22 +-----------
    6.23 +
    6.24 +By default the Makefile builds both plugins. If you want to use only one of
    6.25 +them, you can add one of "WITHOUT_MP3=1" or "WITHOUT_MPLAYER=1" to your
    6.26 +Make.config.
    6.27 +
    6.28 +In the past month the plugins only have been tested with VDR 1.4.x and 1.5.x.
    6.29 +While the plugins probably can be compiled with VDR down to version 1.1.29, this
    6.30 +is not tested nor supported.
    6.31 +
    6.32 +By default any mentioned plugin config file should be located in the "plugins"
    6.33 +subdirectory of your VDR config directory (see commandline option -S to specify
    6.34 +an plugin specific subdirectory). For a complete description of the config
    6.35 +files, see the MANUAL file.
    6.36 +
    6.37 +The MP3 plugin needs some external libraries (some optionally):
    6.38 +
    6.39 +You must have installed libmad and libid3tag from
    6.40 +<http://www.underbit.com/products/mad/> for decoding MP3 and ID3 support.
    6.41 +Recommended version is 0.15.1b. To compile and install I suggest the following
    6.42 +sequence from the source directory (libmad and libid3tag are installed
    6.43 +separately, so you must execute this once for each directory):
    6.44 +
    6.45 +> configure
    6.46 +> make
    6.47 +> make install
    6.48 +> ldconfig
    6.49 +
    6.50 +For playback of WAV and other sound files you must have installed libsndfile
    6.51 +<http://www.mega-nerd.com/libsndfile/> on your system. Recommended version is
    6.52 +1.0.11 or newer. The old 0.0.x series doesn't work due to some API changes. To
    6.53 +compile and install libsndfile I suggest the following sequence from the
    6.54 +libsndfile source directory:
    6.55 +
    6.56 +> configure
    6.57 +> make
    6.58 +> make install
    6.59 +> ldconfig
    6.60 +
    6.61 +If you don't want to install libsndfile, you must add "WITHOUT_LIBSNDFILE=1" to
    6.62 +your Make.config.
    6.63 +
    6.64 +If you want to listen to CD audio, you can use cdfs
    6.65 +<http://www.elis.rug.ac.be/~ronsse/cdfs/> (version 0.5c suggested). I suggest
    6.66 +that you create a new entry in /etc/fstab and mp3sources.conf for cdfs. The
    6.67 +/etc/fstab entry could look like:
    6.68 +  /dev/hdc	/mnt/cdfs	cdfs	ro,noauto,user 0 0
    6.69 +The entry for mp3sources.conf could look like:
    6.70 +  /mnt/cdfs;CD-Audio;1
    6.71 +Or if you want only WAV files to be displayed:
    6.72 +  /mnt/cdfs;CD-Audio;1;*.wav
    6.73 +If you don't like cdfs, you could use any other filesystems which allows to
    6.74 +access the CD audio data as WAV files.
    6.75 +
    6.76 +For playback of OGG files you must have installed libvorbis and libvorbisfile
    6.77 +<http://www.xiph.org/ogg/vorbis/> on your system. Most Linux distributions
    6.78 +include packages for this.
    6.79 +If you don't want to install the vorbis libraries, you must add
    6.80 +"WITHOUT_LIBVORBISFILE=1" to your Make.config.
    6.81 +
    6.82 +The MP3 plugin can output the sound to an OSS soundcard. To compile this
    6.83 +support add "WITH_OSS_OUTPUT=1" to your Make.config. The output device defaults
    6.84 +to "/dev/dsp" (see commandline option -D too). Don't forget to enable OSS
    6.85 +output in the plugin setup menu.
    6.86 +
    6.87 +Before compiling the plugin, you could have a look at "mp3-config.h". This file
    6.88 +includes a number of defines to set options and values at compile time. You
    6.89 +should only change defines, if you have understand the source code parts which
    6.90 +deal with the define. Improper settings can make the MP3 plugin to fail or to
    6.91 +operate unsmoothly. So you should know what you are doing.
    6.92 +
    6.93 +To build the plugin(s) type:
    6.94 +"make plugins"
    6.95 +from the VDR source directory.
    6.96 +
    6.97 +To make the progressbar work with the MPlayer plugin and slave mode you need to
    6.98 +apply a small patch to mplayer. Select an appropriate patch file for your
    6.99 +MPlayer version from the patches subdirectory. Apply the patch and recompile
   6.100 +MPlayer.
   6.101 +
   6.102 +Early 0.90rc versions of MPlayer need a patch to enabled HEAD driver support
   6.103 +too. You will find one for 0.90rc1 in the patches subdirectory. Probably you'll
   6.104 +have to point configure to the new DVB include files with
   6.105 +--with-extraincdir=/usr/local/src/DVB/include or where ever you have stored the
   6.106 +files. MPlayer versions later as 0.90rc5 allow to select HEAD support with a
   6.107 +configure option or even detect this automatically.
   6.108 +
   6.109 +Commandline options:
   6.110 +--------------------
   6.111 +
   6.112 +For both plugins it's possible to give an additional configuration subdirectory
   6.113 +with commandline option -S/--sources, i.e. the given directory name is appended
   6.114 +to the default plugin config directory path.
   6.115 +
   6.116 +Both plugins have a commandline option -m/--mount to define the name and
   6.117 +location of the mount script. The default mount script is "mount.sh". This
   6.118 +script is called from the plugin with 2 options on the commandline. The first
   6.119 +is one of mount/unmount/eject/status and gives the action to perform. The
   6.120 +second one is the base directory as defined in "mp3sources.conf" or
   6.121 +"mplayersources.conf". The script must return the exit code 0 if the action was
   6.122 +successful and 1 if the action failed (see the comments in the example
   6.123 +"mount.sh" script which comes with the archive).
   6.124 +
   6.125 +The MP3 plugin maintains a cache for information scanned from song files (e.g.
   6.126 +ID3 tags). This information is saved to the id3info.cache file by default
   6.127 +located in the video directory. You can use commandline option -C/--cache to
   6.128 +specify a different directory for this file.
   6.129 +
   6.130 +If you are using cdfs, the MP3 plugin is able to query a CDDB database for the
   6.131 +song information (like title, artist). Local CDDB lookups can be enable from
   6.132 +the setup menu and you must give the path to your local CDDB files with
   6.133 +commandline option -B/--cddb. The database layout follows the xmcd standard
   6.134 +(one subdirectory level for the categories, individual files for every disc).
   6.135 +The settings for remote CDDB lookups can be found in the setup menu, too. Any
   6.136 +information retrieved from a remote host is stored to your local CDDB database,
   6.137 +so the user running VDR needs write access to this directory.
   6.138 +
   6.139 +If you are using any of the networking capabilities and you have a dial-up
   6.140 +network, you can use the commandline option -n/--network to give a script name.
   6.141 +This script is called before and after any network access and receives one
   6.142 +option on commandline. This can either be "up" or "down", depending on if it's
   6.143 +before or after the network access. In the "up" case the script should not
   6.144 +return before the network is connected and useable. By default the plugin
   6.145 +assumes that network access can be done at any time without prior action.
   6.146 +
   6.147 +If you want to use the OSS output capability but your soundcard device is not
   6.148 +"/dev/dsp" (which is the default) you can use the commandline option -D/--dsp
   6.149 +to give an alternative soundcard device.
   6.150 +
   6.151 +If you want to use the cover image display, you have to provide an image
   6.152 +converting script. Usually your covers are in a common image format like e.g.
   6.153 +JPEG or PNG, but the MP3 plugin can only display MPEG (still) frames. The
   6.154 +conversion is done by the script, which takes two arguments: first the filename
   6.155 +of the image file and second the filename or the resulting MPEG file. The
   6.156 +script also has to take care that any needed subdirectories in the cache are
   6.157 +created e.g. mkdir -p. You will find an example script in the examples
   6.158 +subdirectory. By default the script is called "image_convert.sh", but you can
   6.159 +use the commandline option -i/--iconv to change the name and location of the
   6.160 +script.
   6.161 +To speed up consecutive accesses to the same image, the converted frames are
   6.162 +cached on disk. The default cache directory is "/var/cache/images/mp3". You can
   6.163 +change this location with the commandline option -c/--icache.
   6.164 +Don't forget to enable the image display in the plugin setup menu.
   6.165 +The search order for images is:
   6.166 +- An image in the same directory as the song, named like the song but with the
   6.167 +  song extension replaced with the image format extension
   6.168 +  e.g. test.mp3 -> test.jpg
   6.169 +- An image named "cover" with the image format extension in the same directory
   6.170 +  as the song (album cover).
   6.171 +  e.g. cover.gif
   6.172 +- An image named "artist" with the image format extension in the parent
   6.173 +  directory of the song (artist image).
   6.174 +  e.g. artist.png
   6.175 +- An image named "background" with the image format extension in the base
   6.176 +  directory of the MP3 source.
   6.177 +For all locations the extensions "jpg", "png" & "gif" are checked (in that
   6.178 +order). If no image can be found, a full screen black image is displayed (this
   6.179 +is included statically in the plugin).
   6.180 +
   6.181 +MPlayer is called through a script called "mplayer.sh" with the filename to
   6.182 +play as first argument and the phrase SLAVE as second argument if slave mode is
   6.183 +enabled. You can use the commandline option -M/--mplayer to change the name and
   6.184 +location of the mplayer script. The script has to call MPlayer with all the
   6.185 +necessary options.
   6.186 +
   6.187 +The script should parse the SLAVE keyword too and give appropriate options to
   6.188 +MPlayer. To enable slave mode you must give at least the "-slave" option, while
   6.189 +I suggest "-slave -nolirc -quiet".
   6.190 +
   6.191 +You can use the mplayer.sh.example file, which comes with the archive, as a
   6.192 +starting point. Juri Haberland maintains a full featured mplayer.sh file which
   6.193 +is available at <http://batleth.sapienti-sat.org/projects/VDR/>.
   6.194 +
   6.195 +For the MPlayer plugin you can give a location for the global resume file with
   6.196 +commandline option -R/--resume.
   6.197 +
   6.198 +
   6.199 +Summary of commandline options:
   6.200 +-------------------------------
   6.201 +
   6.202 +MP3 Plugin:
   6.203 +-m CMD,  --mount=CMD    use CMD to mount/unmount/eject mp3 sources
   6.204 +-n CMD,  --network=CMD  execute CMD before & after network access
   6.205 +-B DIR,  --cddb=DIR     search CDDB files in DIR
   6.206 +-C DIR,  --cache=DIR    store ID3 cache file in DIR
   6.207 +-D DIR,  --dsp=DIR      device for OSS output
   6.208 +-i CMD,  --iconv=CMD    use CMD to convert background images
   6.209 +-c DIR,  --icache=DIR   cache converted images in DIR
   6.210 +-S SUB,  --sources=SUB  search sources config in SUB subdirectory
   6.211 + 
   6.212 +MPlayer plugin:
   6.213 +-m CMD,  --mount=CMD    use CMD to mount/unmount/eject mplayer sources
   6.214 +-M CMD,  --mplayer=CMD  use CMD when calling MPlayer
   6.215 +-S SUB,  --sources=SUB  search sources config in SUB subdirectory
   6.216 +-R DIR,  --resume=DIR   store global resume file in DIR
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/common.h	Sat Dec 29 14:47:40 2007 +0100
     7.3 @@ -0,0 +1,54 @@
     7.4 +/*
     7.5 + * MP3/MPlayer plugin to VDR (C++)
     7.6 + *
     7.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
     7.8 + *
     7.9 + * This code is free software; you can redistribute it and/or
    7.10 + * modify it under the terms of the GNU General Public License
    7.11 + * as published by the Free Software Foundation; either version 2
    7.12 + * of the License, or (at your option) any later version.
    7.13 + *
    7.14 + * This code is distributed in the hope that it will be useful,
    7.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    7.17 + * GNU General Public License for more details.
    7.18 + *
    7.19 + * You should have received a copy of the GNU General Public License
    7.20 + * along with this program; if not, write to the Free Software
    7.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    7.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    7.23 + */
    7.24 +
    7.25 +#ifndef ___COMMON_H
    7.26 +#define ___COMMON_H
    7.27 +
    7.28 +#ifndef APIVERSNUM
    7.29 +#include <vdr/config.h>
    7.30 +#endif
    7.31 +#include "config.h"
    7.32 +
    7.33 +#if APIVERSNUM >= 10313
    7.34 +#define SLEEP(x) cCondWait::SleepMs(x)
    7.35 +#else
    7.36 +#define SLEEP(x) usleep((x)*1000)
    7.37 +#endif
    7.38 +
    7.39 +#if APIVERSNUM >= 10318
    7.40 +#include <vdr/tools.h>
    7.41 +static cTimeMs __time;
    7.42 +#define time_ms() ((int)__time.Elapsed())
    7.43 +#endif
    7.44 +
    7.45 +#if APIVERSNUM >= 10338
    7.46 +#define BUTTON "Button$"
    7.47 +#else
    7.48 +#define BUTTON
    7.49 +#endif
    7.50 +
    7.51 +#if !defined(NO_DEBUG) && defined(DEBUG)
    7.52 +#define d(x) { (x); }
    7.53 +#else
    7.54 +#define d(x) ; 
    7.55 +#endif
    7.56 +
    7.57 +#endif //___COMMON_H
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/config.h	Sat Dec 29 14:47:40 2007 +0100
     8.3 @@ -0,0 +1,61 @@
     8.4 +/*
     8.5 + * MP3/MPlayer plugin to VDR (C++)
     8.6 + *
     8.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
     8.8 + *
     8.9 + * This code is free software; you can redistribute it and/or
    8.10 + * modify it under the terms of the GNU General Public License
    8.11 + * as published by the Free Software Foundation; either version 2
    8.12 + * of the License, or (at your option) any later version.
    8.13 + *
    8.14 + * This code is distributed in the hope that it will be useful,
    8.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    8.17 + * GNU General Public License for more details.
    8.18 + *
    8.19 + * You should have received a copy of the GNU General Public License
    8.20 + * along with this program; if not, write to the Free Software
    8.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    8.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    8.23 + */
    8.24 +
    8.25 +// This files contains some option switches/values for compile time
    8.26 +// configuration of the MP3/MPlayer plugin. You should only alter
    8.27 +// this file, if you have understand the source code parts which deal
    8.28 +// with the changed value.
    8.29 +
    8.30 +// After changing this file you should do a "make plugins-clean ; make plugins"
    8.31 +// to recompile vdr.
    8.32 +
    8.33 +#ifndef ___CONFIG_H
    8.34 +#define ___CONFIG_H
    8.35 +
    8.36 +// Uncomment to enable generic debugging messages to the console. This may slow
    8.37 +// down operation in some cases.
    8.38 +#define DEBUG
    8.39 +//#define NO_DEBUG
    8.40 +
    8.41 +// Defines the filename extention to use for playlist files.
    8.42 +#define PLAYLISTEXT ".m3u"
    8.43 +
    8.44 +// Defines the text to identify WinAmp-Style playlists.
    8.45 +#define WINAMPEXT "#EXTM3U"
    8.46 +
    8.47 +// Defines the timeout in seconds for functions which use a single key
    8.48 +// (e.g. openning the playlist window). If the key is repressed during
    8.49 +// the timeout, the secondary function is activated.
    8.50 +#define MULTI_TIMEOUT 3
    8.51 +
    8.52 +// Defines the timeout in ms for entering the single digits in direct song
    8.53 +// selection.
    8.54 +#define SELECT_TIMEOUT 1000
    8.55 +
    8.56 +// If the progress display is closed on direct song selection, the display
    8.57 +// is opend temporarily. This defines the time in seconds after the display
    8.58 +// is closed again.
    8.59 +#define SELECTHIDE_TIMEOUT 3
    8.60 +
    8.61 +// Defines the time in seconds to jump inside a song with left/right.
    8.62 +#define JUMPSIZE 3
    8.63 +
    8.64 +#endif //___CONFIG_H
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/data-mp3-image.c	Sat Dec 29 14:47:40 2007 +0100
     9.3 @@ -0,0 +1,541 @@
     9.4 +unsigned char defaultImage[]={
     9.5 +0x00,0x00,0x01,0xb3,0x2c,0x02,0x40,0x23,0x06,0x1a,0xa3,0x70,0x00,0x00,0x01,0xb5,
     9.6 +0x14,0x82,0x00,0x01,0x00,0x00,0x00,0x00,0x01,0xb5,0x23,0x05,0x05,0x05,0x0b,0x02,
     9.7 +0x12,0x00,0x00,0x00,0x01,0xb8,0x00,0x08,0x00,0x40,0x00,0x00,0x01,0x00,0x00,0x0f,
     9.8 +0xff,0xf8,0x00,0x00,0x01,0xb5,0x8f,0xff,0xf7,0xdd,0x80,0x00,0x00,0x01,0x01,0x33,
     9.9 +0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
    9.10 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
    9.11 +0x1a,0x96,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
    9.12 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
    9.13 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xa9,0x23,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
    9.14 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
    9.15 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
    9.16 +0x86,0xa3,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
    9.17 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
    9.18 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6a,0x28,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
    9.19 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
    9.20 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
    9.21 +0x61,0xa8,0x63,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
    9.22 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
    9.23 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0x00,0x00,0x01,0x02,
    9.24 +0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
    9.25 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
    9.26 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
    9.27 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
    9.28 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
    9.29 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
    9.30 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
    9.31 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
    9.32 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
    9.33 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
    9.34 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
    9.35 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
    9.36 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
    9.37 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
    9.38 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x03,0x0b,0xfc,
    9.39 +0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
    9.40 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
    9.41 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
    9.42 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
    9.43 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
    9.44 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
    9.45 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
    9.46 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
    9.47 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
    9.48 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
    9.49 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
    9.50 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
    9.51 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
    9.52 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
    9.53 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x04,0x0b,0xfc,0x3e,0xd1,
    9.54 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
    9.55 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
    9.56 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
    9.57 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
    9.58 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
    9.59 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
    9.60 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
    9.61 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
    9.62 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
    9.63 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
    9.64 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
    9.65 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
    9.66 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
    9.67 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
    9.68 +0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x05,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,
    9.69 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
    9.70 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
    9.71 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
    9.72 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
    9.73 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
    9.74 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
    9.75 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
    9.76 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
    9.77 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
    9.78 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
    9.79 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
    9.80 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
    9.81 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
    9.82 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
    9.83 +0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x06,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,
    9.84 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
    9.85 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
    9.86 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
    9.87 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
    9.88 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
    9.89 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
    9.90 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
    9.91 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
    9.92 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
    9.93 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
    9.94 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
    9.95 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
    9.96 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
    9.97 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
    9.98 +0x61,0x80,0x00,0x00,0x01,0x07,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
    9.99 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.100 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.101 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.102 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.103 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.104 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.105 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.106 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.107 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.108 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.109 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.110 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.111 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.112 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,
   9.113 +0x00,0x00,0x01,0x08,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.114 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.115 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.116 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.117 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.118 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.119 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.120 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.121 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.122 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.123 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.124 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.125 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.126 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.127 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,
   9.128 +0x01,0x09,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.129 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.130 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.131 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.132 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.133 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.134 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.135 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.136 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.137 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.138 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.139 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.140 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.141 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.142 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x0a,
   9.143 +0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.144 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.145 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.146 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.147 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.148 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.149 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.150 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.151 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.152 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.153 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.154 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.155 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.156 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.157 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x0b,0x0b,0xfc,
   9.158 +0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.159 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.160 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.161 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.162 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.163 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.164 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.165 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.166 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.167 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.168 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.169 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.170 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.171 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.172 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x0c,0x0b,0xfc,0x3e,0xd1,
   9.173 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.174 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.175 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.176 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.177 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.178 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.179 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.180 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.181 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.182 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.183 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.184 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.185 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.186 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.187 +0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x0d,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,
   9.188 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.189 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.190 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.191 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.192 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.193 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.194 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.195 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.196 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.197 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.198 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.199 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.200 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.201 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.202 +0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x0e,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,
   9.203 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.204 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.205 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.206 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.207 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.208 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.209 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.210 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.211 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.212 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.213 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.214 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.215 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.216 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.217 +0x61,0x80,0x00,0x00,0x01,0x0f,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.218 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.219 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.220 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.221 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.222 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.223 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.224 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.225 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.226 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.227 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.228 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.229 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.230 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.231 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,
   9.232 +0x00,0x00,0x01,0x10,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.233 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.234 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.235 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.236 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.237 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.238 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.239 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.240 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.241 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.242 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.243 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.244 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.245 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.246 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,
   9.247 +0x01,0x11,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.248 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.249 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.250 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.251 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.252 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.253 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.254 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.255 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.256 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.257 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.258 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.259 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.260 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.261 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x12,
   9.262 +0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.263 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.264 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.265 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.266 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.267 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.268 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.269 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.270 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.271 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.272 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.273 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.274 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.275 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.276 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x13,0x0b,0xfc,
   9.277 +0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.278 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.279 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.280 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.281 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.282 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.283 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.284 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.285 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.286 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.287 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.288 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.289 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.290 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.291 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x14,0x0b,0xfc,0x3e,0xd1,
   9.292 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.293 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.294 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.295 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.296 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.297 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.298 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.299 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.300 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.301 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.302 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.303 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.304 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.305 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.306 +0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x15,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,
   9.307 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.308 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.309 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.310 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.311 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.312 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.313 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.314 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.315 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.316 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.317 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.318 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.319 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.320 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.321 +0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x16,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,
   9.322 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.323 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.324 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.325 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.326 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.327 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.328 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.329 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.330 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.331 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.332 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.333 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.334 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.335 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.336 +0x61,0x80,0x00,0x00,0x01,0x17,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.337 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.338 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.339 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.340 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.341 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.342 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.343 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.344 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.345 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.346 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.347 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.348 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.349 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.350 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,
   9.351 +0x00,0x00,0x01,0x18,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.352 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.353 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.354 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.355 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.356 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.357 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.358 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.359 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.360 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.361 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.362 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.363 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.364 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.365 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,
   9.366 +0x01,0x19,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.367 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.368 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.369 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.370 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.371 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.372 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.373 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.374 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.375 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.376 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.377 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.378 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.379 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.380 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x1a,
   9.381 +0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.382 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.383 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.384 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.385 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.386 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.387 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.388 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.389 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.390 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.391 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.392 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.393 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.394 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.395 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x1b,0x0b,0xfc,
   9.396 +0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.397 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.398 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.399 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.400 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.401 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.402 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.403 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.404 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.405 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.406 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.407 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.408 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.409 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.410 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x1c,0x0b,0xfc,0x3e,0xd1,
   9.411 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.412 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.413 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.414 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.415 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.416 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.417 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.418 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.419 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.420 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.421 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.422 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.423 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.424 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.425 +0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x1d,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,
   9.426 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.427 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.428 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.429 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.430 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.431 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.432 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.433 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.434 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.435 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.436 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.437 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.438 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.439 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.440 +0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x1e,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,
   9.441 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.442 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.443 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.444 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.445 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.446 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.447 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.448 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.449 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.450 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.451 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.452 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.453 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.454 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.455 +0x61,0x80,0x00,0x00,0x01,0x1f,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.456 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.457 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.458 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.459 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.460 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.461 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.462 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.463 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.464 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.465 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.466 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.467 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.468 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.469 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,
   9.470 +0x00,0x00,0x01,0x20,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.471 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.472 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.473 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.474 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.475 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.476 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.477 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.478 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.479 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.480 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.481 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.482 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.483 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.484 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,
   9.485 +0x01,0x21,0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.486 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.487 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.488 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.489 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.490 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.491 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.492 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.493 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.494 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.495 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.496 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.497 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.498 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.499 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x22,
   9.500 +0x0b,0xfc,0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.501 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.502 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.503 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.504 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.505 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.506 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.507 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.508 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.509 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.510 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.511 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.512 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.513 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.514 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x23,0x0b,0xfc,
   9.515 +0x3e,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.516 +0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,
   9.517 +0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,
   9.518 +0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,
   9.519 +0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,
   9.520 +0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,
   9.521 +0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,
   9.522 +0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,
   9.523 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.524 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.525 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.526 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.527 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.528 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.529 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0x24,0x0b,0xfc,0x3e,0xd1,
   9.530 +0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,
   9.531 +0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,
   9.532 +0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,
   9.533 +0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,
   9.534 +0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,
   9.535 +0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,
   9.536 +0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,
   9.537 +0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,
   9.538 +0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,
   9.539 +0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,
   9.540 +0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,
   9.541 +0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,0xb8,0xd1,0xa3,0x46,0x18,
   9.542 +0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,0xe3,0x46,0x8d,0x18,0x61,
   9.543 +0xb8,0xd1,0xa3,0x46,0x18,0x6e,0x34,0x68,0xd1,0x86,0x1b,0x8d,0x1a,0x34,0x61,0x86,
   9.544 +0xe3,0x46,0x8d,0x18,0x61,0x80,0x00,0x00,0x01,0xb7};
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/data-mp3.c	Sat Dec 29 14:47:40 2007 +0100
    10.3 @@ -0,0 +1,577 @@
    10.4 +/*
    10.5 + * MP3/MPlayer plugin to VDR (C++)
    10.6 + *
    10.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    10.8 + *
    10.9 + * This code is free software; you can redistribute it and/or
   10.10 + * modify it under the terms of the GNU General Public License
   10.11 + * as published by the Free Software Foundation; either version 2
   10.12 + * of the License, or (at your option) any later version.
   10.13 + *
   10.14 + * This code is distributed in the hope that it will be useful,
   10.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10.17 + * GNU General Public License for more details.
   10.18 + *
   10.19 + * You should have received a copy of the GNU General Public License
   10.20 + * along with this program; if not, write to the Free Software
   10.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   10.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   10.23 + */
   10.24 +
   10.25 +#include <stdlib.h>
   10.26 +#include <stdio.h>
   10.27 +#include <unistd.h>
   10.28 +
   10.29 +#include "common.h"
   10.30 +#include "data-mp3.h"
   10.31 +#include "data.h"
   10.32 +#include "decoder.h"
   10.33 +
   10.34 +#define DEBUG_IMAGE
   10.35 +
   10.36 +#ifdef DEBUG_IMAGE
   10.37 +#define di(x) { (x); }
   10.38 +#else
   10.39 +#define di(x) ; 
   10.40 +#endif
   10.41 +
   10.42 +const char *imagecache = "/var/cache/images/mp3";
   10.43 +const char *imageconv  = "image_convert.sh";
   10.44 +
   10.45 +// image suffixes to search
   10.46 +const char *img_suff[] = { "jpg","png","gif",0 };
   10.47 +// exclude list for instant playlist creation
   10.48 +const char *excl_pl[] = { "*"PLAYLISTEXT,"*.jpg","*.gif","*.png",0 };
   10.49 +// exclude list for song browser
   10.50 +const char *excl_br[] = { ".*","*.jpg","*.gif","*.png",0 };
   10.51 +
   10.52 +// --- cImageConvert -----------------------------------------------------------
   10.53 +
   10.54 +class cImageConvert : private cThread {
   10.55 +private:
   10.56 +  char *image;
   10.57 +  enum eStatus { stNone, stRun, stFin };
   10.58 +  eStatus status;
   10.59 +protected:
   10.60 +  virtual void Action(void);
   10.61 +public:
   10.62 +  cImageConvert(void);
   10.63 +  ~cImageConvert();
   10.64 +  bool Convert(const char *Image);
   10.65 +  bool Status(void);
   10.66 +  };
   10.67 +
   10.68 +cImageConvert::cImageConvert(void)
   10.69 +{
   10.70 +  image=0; status=stNone;
   10.71 +}
   10.72 +
   10.73 +cImageConvert::~cImageConvert()
   10.74 +{
   10.75 +  if(status==stRun) Cancel(10);
   10.76 +  free(image);
   10.77 +}
   10.78 +
   10.79 +bool cImageConvert::Convert(const char *Image)
   10.80 +{
   10.81 +  if(status==stNone) {
   10.82 +    image=strdup(Image);
   10.83 +    status=stRun;
   10.84 +    Start();
   10.85 +    return true;
   10.86 +    }
   10.87 +  return false;
   10.88 +}
   10.89 +
   10.90 +bool cImageConvert::Status(void)
   10.91 +{
   10.92 +  if(status==stRun && !Active()) status=stFin;
   10.93 +  return status==stFin;
   10.94 +}
   10.95 +
   10.96 +void cImageConvert::Action(void)
   10.97 +{
   10.98 +  nice(3);
   10.99 +  char *m, *cmd, *qp, *qm;
  10.100 +  asprintf(&m,"%s%s.mpg",imagecache,image);
  10.101 +  di(printf("image: convert started %s -> %s\n",image,m))
  10.102 +  asprintf(&cmd,"%s \"%s\" \"%s\"",imageconv,qp=Quote(image),qm=Quote(m));
  10.103 +  int r=system(cmd);
  10.104 +  if(r!=0) di(printf("image: convert returned with code %d. Failed?\n",r))
  10.105 +  free(cmd); free(qp); free(qm); free(m);
  10.106 +  di(printf("image: convert finished\n"))
  10.107 +  status=stFin;
  10.108 +}
  10.109 +
  10.110 +// --- cSong -------------------------------------------------------------------
  10.111 +
  10.112 +cSong::cSong(cFileObj *Obj)
  10.113 +{
  10.114 +  obj=new cFileObj(Obj);
  10.115 +  Init();
  10.116 +}
  10.117 +
  10.118 +cSong::cSong(cFileSource *Source, const char *Subdir, const char *Name)
  10.119 +{
  10.120 +  obj=new cFileObj(Source,Subdir,Name,otFile);
  10.121 +  Init();
  10.122 +}
  10.123 +
  10.124 +cSong::cSong(cSong *Song)
  10.125 +{
  10.126 +  obj=new cFileObj(Song->obj);
  10.127 +  Init();
  10.128 +}
  10.129 +
  10.130 +cSong::~cSong()
  10.131 +{
  10.132 +  delete conv;
  10.133 +  delete decoder;
  10.134 +  obj->Source()->Unblock();
  10.135 +  delete obj;
  10.136 +  free((char *)image);
  10.137 +}
  10.138 +
  10.139 +void cSong::Init(void)
  10.140 +{
  10.141 +  decoder=0; user=0; image=0; conv=0; queueStat=0;
  10.142 +  fromDOS=decoderFailed=false;
  10.143 +  obj->Source()->Block();
  10.144 +}
  10.145 +
  10.146 +#if APIVERSNUM >= 10315
  10.147 +int cSong::Compare(const cListObject &ListObject) const
  10.148 +#else
  10.149 +bool cSong::operator<(const cListObject &ListObject)
  10.150 +#endif
  10.151 +{
  10.152 +  cSong *song=(cSong *)&ListObject;
  10.153 +#if APIVERSNUM >= 10315
  10.154 +  return strcasecmp(obj->Path(),song->obj->Path());
  10.155 +#else
  10.156 +  return strcasecmp(obj->Path(),song->obj->Path())<0;
  10.157 +#endif
  10.158 +}
  10.159 +
  10.160 +cSongInfo *cSong::Info(bool get)
  10.161 +{
  10.162 +  Decoder();
  10.163 +  cSongInfo *si=0;
  10.164 +  if(decoder) si=decoder->SongInfo(get);
  10.165 +  return si;
  10.166 +}
  10.167 +
  10.168 +cDecoder *cSong::Decoder(void)
  10.169 +{
  10.170 +  decLock.Lock();
  10.171 +  if(!decoder && !decoderFailed) {
  10.172 +    decoder=cDecoders::FindDecoder(obj);
  10.173 +    if(!decoder) decoderFailed=true;
  10.174 +    }
  10.175 +  decLock.Unlock();
  10.176 +  return decoder;
  10.177 +}
  10.178 +
  10.179 +void cSong::Convert(void)
  10.180 +{
  10.181 +  char *Name=Convert2Unix(obj->Name());
  10.182 +  obj->SetName(Name);
  10.183 +  fromDOS=true;
  10.184 +  free(Name);
  10.185 +}
  10.186 +
  10.187 +char *cSong::Convert2Unix(const char *name) const
  10.188 +{
  10.189 +  char *Name=strdup(name);
  10.190 +  char *p=Name;
  10.191 +  while(*p) {
  10.192 +    if(*p=='/') *p='?';
  10.193 +    if(*p=='\\') *p='/';
  10.194 +    p++;
  10.195 +    }
  10.196 +  return Name;
  10.197 +}
  10.198 +
  10.199 +/*
  10.200 +char *cSong::Convert2Dos(const char *name)
  10.201 +{
  10.202 +  char *Name=strdup(name);
  10.203 +  char *p=Name;
  10.204 +  while(*p) {
  10.205 +    if(*p=='\\') *p='?';
  10.206 +    if(*p=='/') *p='\\';
  10.207 +    p++;
  10.208 +    }
  10.209 +  return Name;
  10.210 +}
  10.211 +*/
  10.212 +
  10.213 +bool cSong::Parse(char *s, const char *reldir) const
  10.214 +{
  10.215 +  s=skipspace(stripspace(s));
  10.216 +  if(*s) {
  10.217 +    if(s[0]=='/' || !reldir)
  10.218 +      obj->SplitAndSet(s);
  10.219 +    else {
  10.220 +      s=AddPath(reldir,s);
  10.221 +      obj->SplitAndSet(s);
  10.222 +      free(s);
  10.223 +      }
  10.224 +    return true;
  10.225 +    }
  10.226 +  return false;
  10.227 +}
  10.228 +
  10.229 +bool cSong::Save(FILE *f, const char *reldir) const
  10.230 +{
  10.231 +  const char *path=obj->Path();
  10.232 +  if(reldir) {
  10.233 +    int l=strlen(reldir);
  10.234 +    if(!strncasecmp(path,reldir,l)) path+=l+1;
  10.235 +    }
  10.236 +  return fprintf(f,"%s\n",path)>0;
  10.237 +}
  10.238 +
  10.239 +bool cSong::FindImage(void)
  10.240 +{
  10.241 +  if(image) return true;
  10.242 +
  10.243 +  char base[strlen(obj->Path())+32];
  10.244 +  strcpy(base,obj->Path());
  10.245 +  di(printf("image: checking image for %s\n",obj->Path()))
  10.246 +
  10.247 +  // song specific image
  10.248 +  char *m=rindex(base,'.');
  10.249 +  if(m) *m=0;
  10.250 +  if((image=CheckImage(base))) return true;
  10.251 +
  10.252 +  // album specific image in song directory
  10.253 +  if(!(m=rindex(base,'/'))) m=base-1;
  10.254 +  strcpy(m+1,"cover");
  10.255 +  if((image=CheckImage(base))) return true;
  10.256 +
  10.257 +  // artist specific image in parent directory
  10.258 +  if((m=rindex(base,'/'))) {
  10.259 +    *m=0;
  10.260 +    if(!(m=rindex(base,'/'))) m=base-1;
  10.261 +    strcpy(m+1,"artist");
  10.262 +    if((image=CheckImage(base))) return true;
  10.263 +    }
  10.264 +
  10.265 +  // default image in source basedir
  10.266 +  if((image=CheckImage("background"))) return true;
  10.267 +
  10.268 +  di(printf("image: no image for %s\n",obj->Path()))
  10.269 +  return false;
  10.270 +}
  10.271 +
  10.272 +const char *cSong::CheckImage(const char *base) const
  10.273 +{
  10.274 +  char *p;
  10.275 +  int n;
  10.276 +  asprintf(&p,"%s/%s.%n     ",obj->Source()->BaseDir(),base,&n);
  10.277 +  for(const char **s=img_suff; *s; s++) {
  10.278 +#ifdef DEBUG
  10.279 +    if(strlen(*s)>5) printf("ERROR: buffer overflow in CheckImage ext=%s\n",*s);
  10.280 +#endif
  10.281 +    strcpy(&p[n],*s);
  10.282 +    di(printf("image: check %s\n",p))
  10.283 +    if(!access(p,R_OK)) {
  10.284 +      di(printf("image: found\n"))
  10.285 +      return p;
  10.286 +      }
  10.287 +    }
  10.288 +  free(p);
  10.289 +  return 0;
  10.290 +}
  10.291 +
  10.292 +#include "data-mp3-image.c"
  10.293 +extern void PropagateImage(const char *image);
  10.294 +
  10.295 +bool cSong::Image(unsigned char * &mem, int &len)
  10.296 +{
  10.297 +  mem=0;
  10.298 +  if(queueStat>0) {
  10.299 +    if(!conv->Status()) {
  10.300 +      di(printf("image: still queued\n"))
  10.301 +      return false;
  10.302 +      }
  10.303 +    queueStat=-1;
  10.304 +    delete conv; conv=0;
  10.305 +    }
  10.306 +
  10.307 +  int res=0;
  10.308 +  if(image || FindImage()) {
  10.309 +    di(printf("image: loading image %s\n",image))
  10.310 +    char *m;
  10.311 +    asprintf(&m,"%s%s.mpg",imagecache,image);
  10.312 +    if(access(m,R_OK)) {
  10.313 +      di(printf("image: not cached\n"))
  10.314 +      if(queueStat<0) {
  10.315 +        di(printf("image: obviously convert failed...\n"))
  10.316 +        }
  10.317 +      else {
  10.318 +        if(!conv) conv=new cImageConvert;
  10.319 +        if(conv && conv->Convert(image)) {
  10.320 +          di(printf("image: convert queued\n"))
  10.321 +          queueStat=1;
  10.322 +          res=-1;
  10.323 +          }
  10.324 +        else {
  10.325 +          di(printf("image: queueing failed\n"))
  10.326 +          queueStat=-1;
  10.327 +          }
  10.328 +        }
  10.329 +      }
  10.330 +    else {
  10.331 +      di(printf("image: cached\n"))
  10.332 +      int f=open(m,O_RDONLY);
  10.333 +      if(f>=0) {
  10.334 +        struct stat64 st;
  10.335 +        fstat64(f,&st);
  10.336 +        len=st.st_size;
  10.337 +        mem=MALLOC(unsigned char,len);
  10.338 +        if(mem) {
  10.339 +          if(read(f,mem,len)==len) res=1;
  10.340 +          else free(mem);
  10.341 +          }
  10.342 +        close(f);
  10.343 +        }
  10.344 +      }
  10.345 +    free(m);
  10.346 +    }
  10.347 +
  10.348 +  PropagateImage(res==1 ? image : 0);
  10.349 +
  10.350 +  if(res<=0) {
  10.351 +    di(printf("image: using static default image\n"))
  10.352 +    len=sizeof(defaultImage);
  10.353 +    mem=MALLOC(unsigned char,len);
  10.354 +    if(mem) {
  10.355 +      memcpy(mem,defaultImage,len);
  10.356 +      }
  10.357 +    }
  10.358 +  return res>=0;
  10.359 +}
  10.360 +
  10.361 +// -- cPlayList --------------------------------------------------------------
  10.362 +
  10.363 +cPlayList::cPlayList(cFileObj *Obj)
  10.364 +{
  10.365 +  obj=new cFileObj(Obj);
  10.366 +  Init();
  10.367 +}
  10.368 +
  10.369 +cPlayList::cPlayList(cFileSource *Source, const char *Subdir, const char *Name)
  10.370 +{
  10.371 +  obj=new cFileObj(Source,Subdir,Name,otFile);
  10.372 +  Init();
  10.373 +}
  10.374 +
  10.375 +cPlayList::cPlayList(cPlayList *List)
  10.376 +{
  10.377 +  obj=new cFileObj(List->obj);
  10.378 +  Init();
  10.379 +}
  10.380 +
  10.381 +cPlayList::~cPlayList()
  10.382 +{
  10.383 +  free(basename);
  10.384 +  free(extbuffer);
  10.385 +  obj->Source()->Unblock();
  10.386 +  delete obj;
  10.387 +}
  10.388 +
  10.389 +void cPlayList::Init(void)
  10.390 +{
  10.391 +  extbuffer=basename=0;
  10.392 +  isWinAmp=false;
  10.393 +  obj->Source()->Block();
  10.394 +  Set();
  10.395 +}
  10.396 +
  10.397 +void cPlayList::Set(void)
  10.398 +{
  10.399 +  free(basename); basename=0;
  10.400 +  if(obj->Name()) {
  10.401 +    basename=strdup(obj->Name());
  10.402 +    int l=strlen(basename)-strlen(PLAYLISTEXT);
  10.403 +    if(l>0 && !strcasecmp(basename+l,PLAYLISTEXT)) basename[l]=0;
  10.404 +    }
  10.405 +}
  10.406 +
  10.407 +#if APIVERSNUM >= 10315
  10.408 +int cPlayList::Compare(const cListObject &ListObject) const
  10.409 +#else
  10.410 +bool cPlayList::operator<(const cListObject &ListObject)
  10.411 +#endif
  10.412 +{
  10.413 +  cPlayList *list=(cPlayList *)&ListObject;
  10.414 +#if APIVERSNUM >= 10315
  10.415 +  return strcasecmp(obj->Name(),list->obj->Name());
  10.416 +#else
  10.417 +  return strcasecmp(obj->Name(),list->obj->Name())<0;
  10.418 +#endif
  10.419 +}
  10.420 +
  10.421 +bool cPlayList::Load(void)
  10.422 +{
  10.423 +  Clear();
  10.424 +  bool result=false;
  10.425 +  FILE *f=fopen(obj->FullPath(),"r");
  10.426 +  if(f) {
  10.427 +    char buffer[512];
  10.428 +    result=true;
  10.429 +    while(fgets(buffer,sizeof(buffer),f)>0) {
  10.430 +      if(buffer[0]=='#') {
  10.431 +        if(!strncmp(buffer,WINAMPEXT,strlen(WINAMPEXT))) {
  10.432 +          d(printf("mp3: detected WinAmp style playlist\n"))
  10.433 +          isWinAmp=true;
  10.434 +          }
  10.435 +        continue;
  10.436 +        }
  10.437 +      if(!isempty(buffer)) {
  10.438 +        cSong *song=new cSong(obj->Source(),0,0);
  10.439 +        if(song->Parse(buffer,obj->Subdir())) Add(song);
  10.440 +        else {
  10.441 +          esyslog("error loading playlist %s\n",obj->FullPath());
  10.442 +          delete song;
  10.443 +          result=false;
  10.444 +          break;
  10.445 +          }
  10.446 +        }
  10.447 +      }
  10.448 +    fclose(f);
  10.449 +    }
  10.450 +  else LOG_ERROR_STR(obj->FullPath());
  10.451 +
  10.452 +  if(result && isWinAmp) {
  10.453 +    cSong *song=First();
  10.454 +    while(song) {   // if this is a WinAmp playlist, convert \ to /
  10.455 +      song->Convert();
  10.456 +      song=cList<cSong>::Next(song);
  10.457 +      }
  10.458 +    }
  10.459 +  return result;
  10.460 +}
  10.461 +
  10.462 +bool cPlayList::Save(void)
  10.463 +{
  10.464 +  bool result=true;
  10.465 +  cSafeFile f(obj->FullPath());
  10.466 +  if(f.Open()) {
  10.467 +    cSong *song=First();
  10.468 +    while(song) {
  10.469 +      if(!song->Save(f,obj->Subdir())) {
  10.470 +         result=false;
  10.471 +         break;
  10.472 +         }
  10.473 +      song=cList<cSong>::Next(song);
  10.474 +      }
  10.475 +    if(!f.Close()) result=false;
  10.476 +    }
  10.477 +  else result=false;
  10.478 +  return result;
  10.479 +}
  10.480 + 
  10.481 +bool cPlayList::Exists(void)
  10.482 +{
  10.483 +  return obj->Exists();
  10.484 +}
  10.485 +
  10.486 +bool cPlayList::TestName(const char *newName)
  10.487 +{
  10.488 +  return obj->TestName(AddExt(newName,PLAYLISTEXT));
  10.489 +}
  10.490 +
  10.491 +bool cPlayList::Rename(const char *newName)
  10.492 +{
  10.493 +  bool r=obj->Rename(AddExt(newName,PLAYLISTEXT));
  10.494 +  if(r) Set();
  10.495 +  return r;
  10.496 +}
  10.497 +
  10.498 +bool cPlayList::Create(const char *newName)
  10.499 +{
  10.500 +  bool r=obj->Create(AddExt(newName,PLAYLISTEXT));
  10.501 +  if(r) {
  10.502 +    Set();
  10.503 +    r=Load();
  10.504 +    }
  10.505 +  return r;
  10.506 +}
  10.507 +
  10.508 +bool cPlayList::Delete(void)
  10.509 +{
  10.510 +  return obj->Delete();
  10.511 +}
  10.512 +
  10.513 +const char *cPlayList::AddExt(const char *FileName, const char *Ext)
  10.514 +{
  10.515 +  free(extbuffer); extbuffer=0;
  10.516 +  asprintf(&extbuffer,"%s%s",FileName,Ext);
  10.517 +  return extbuffer;
  10.518 +}
  10.519 +
  10.520 +// -- cInstantPlayList ------------------------------------------------------
  10.521 +
  10.522 +cInstantPlayList::cInstantPlayList(cFileObj *Obj)
  10.523 +:cPlayList(Obj)
  10.524 +{
  10.525 +  if(!Obj->Name()) Obj->SetName("instant");
  10.526 +}
  10.527 +
  10.528 +bool cInstantPlayList::Load(void)
  10.529 +{
  10.530 +  bool res=false;
  10.531 +  Clear();
  10.532 +  switch(obj->Type()) {
  10.533 +    case otFile:
  10.534 +      d(printf("instant: file %s\n",obj->Name()))
  10.535 +      if(strcasecmp(obj->Name(),basename)) {
  10.536 +        d(printf("instant: detected as playlist\n"))
  10.537 +        res=cPlayList::Load();
  10.538 +        }
  10.539 +      else {
  10.540 +        Add(new cSong(obj));
  10.541 +        res=true;
  10.542 +        }
  10.543 +      break;
  10.544 +    case otDir:
  10.545 +      {
  10.546 +      d(printf("instant: dir %s\n",obj->Name()))
  10.547 +      res=ScanDir(obj->Source(),obj->Path(),stFile,obj->Source()->Include(),excl_pl,true);
  10.548 +      Sort();
  10.549 +      break;
  10.550 +      }
  10.551 +    case otBase:
  10.552 +      d(printf("instant: base\n"))
  10.553 +      res=ScanDir(obj->Source(),0,stFile,obj->Source()->Include(),excl_pl,true);
  10.554 +      Sort();
  10.555 +      break;
  10.556 +    default: break;
  10.557 +    }
  10.558 +  return res;
  10.559 +}
  10.560 +
  10.561 +void cInstantPlayList::DoItem(cFileSource *src, const char *subdir, const char *name)
  10.562 +{
  10.563 +  Add(new cSong(src,subdir,name));
  10.564 +}
  10.565 +
  10.566 +// -- cPlayLists --------------------------------------------------------------
  10.567 +
  10.568 +bool cPlayLists::Load(cFileSource *Source)
  10.569 +{
  10.570 +  static const char *spec[] = { "*"PLAYLISTEXT,0 };
  10.571 +  Clear();
  10.572 +  bool res=ScanDir(Source,0,stFile,spec,0,false);
  10.573 +  Sort();
  10.574 +  return res;
  10.575 +}
  10.576 +
  10.577 +void cPlayLists::DoItem(cFileSource *src, const char *subdir, const char *name)
  10.578 +{
  10.579 +  Add(new cPlayList(src,subdir,name));
  10.580 +}
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/data-mp3.h	Sat Dec 29 14:47:40 2007 +0100
    11.3 @@ -0,0 +1,134 @@
    11.4 +/*
    11.5 + * MP3/MPlayer plugin to VDR (C++)
    11.6 + *
    11.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    11.8 + *
    11.9 + * This code is free software; you can redistribute it and/or
   11.10 + * modify it under the terms of the GNU General Public License
   11.11 + * as published by the Free Software Foundation; either version 2
   11.12 + * of the License, or (at your option) any later version.
   11.13 + *
   11.14 + * This code is distributed in the hope that it will be useful,
   11.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11.17 + * GNU General Public License for more details.
   11.18 + *
   11.19 + * You should have received a copy of the GNU General Public License
   11.20 + * along with this program; if not, write to the Free Software
   11.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   11.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   11.23 + */
   11.24 +
   11.25 +#ifndef ___DATA_MP3_H
   11.26 +#define ___DATA_MP3_H
   11.27 +
   11.28 +#include <vdr/thread.h>
   11.29 +#include <vdr/tools.h>
   11.30 +#include "data.h"
   11.31 +
   11.32 +// ----------------------------------------------------------------
   11.33 +
   11.34 +class cDecoder;
   11.35 +class cSongInfo;
   11.36 +class cImageConvert;
   11.37 +
   11.38 +extern const char *imagecache, *imageconv;
   11.39 +extern const char *img_suff[], *excl_pl[], *excl_br[];
   11.40 +
   11.41 +// ----------------------------------------------------------------
   11.42 +
   11.43 +class cSong : public cListObject {
   11.44 +public:
   11.45 +  int user;
   11.46 +private:
   11.47 +  cFileObj *obj;
   11.48 +  bool fromDOS, decoderFailed;
   11.49 +  cDecoder *decoder;
   11.50 +  cMutex decLock;
   11.51 +  //
   11.52 +  const char *image;
   11.53 +  cImageConvert *conv;
   11.54 +  int queueStat;
   11.55 +  //
   11.56 +  void Init(void);
   11.57 +  char *Convert2Unix(const char *name) const;
   11.58 +  bool FindImage(void);
   11.59 +  const char *CheckImage(const char *base) const;
   11.60 +public:
   11.61 +  cSong(cFileObj *Obj);
   11.62 +  cSong(cFileSource *Source, const char *Subdir, const char *Name);
   11.63 +  cSong(cSong *Song);
   11.64 +  ~cSong();
   11.65 +#if APIVERSNUM >= 10315
   11.66 +  virtual int Compare(const cListObject &ListObject) const;
   11.67 +#else
   11.68 +  virtual bool operator<(const cListObject &ListObject);
   11.69 +#endif
   11.70 +  bool Parse(char *s, const char *reldir) const;
   11.71 +  bool Save(FILE *f, const char *reldir) const;
   11.72 +  void Convert(void);
   11.73 +  cSongInfo *Info(bool get=true);
   11.74 +  cDecoder *Decoder(void);
   11.75 +  bool Image(unsigned char * &mem, int &len);
   11.76 +  inline const char *Name(void) const { return obj->Name(); }
   11.77 +  inline const char *FullPath(void) const { return obj->FullPath(); }
   11.78 +  };
   11.79 +
   11.80 +// ----------------------------------------------------------------
   11.81 +
   11.82 +class cPlayList : public cList<cSong>, public cListObject {
   11.83 +private:
   11.84 +  bool isWinAmp;
   11.85 +  char *extbuffer;
   11.86 +  //
   11.87 +  void Init(void);
   11.88 +  void Set(void);
   11.89 +  const char *AddExt(const char *Name, const char *Ext);
   11.90 +protected:
   11.91 +  cFileObj *obj;
   11.92 +  char *basename;
   11.93 +public:
   11.94 +  cPlayList(cFileObj *Obj);
   11.95 +  cPlayList(cFileSource *Source, const char *Subdir, const char *Name);
   11.96 +  cPlayList(cPlayList *List);
   11.97 +  ~cPlayList();
   11.98 +  virtual bool Load(void);
   11.99 +  virtual bool Save(void);
  11.100 +#if APIVERSNUM >= 10315
  11.101 +  virtual int Compare(const cListObject &ListObject) const;
  11.102 +#else
  11.103 +  virtual bool operator<(const cListObject &ListObject);
  11.104 +#endif
  11.105 +  //
  11.106 +  bool Rename(const char *newName);
  11.107 +  bool Delete(void);
  11.108 +  bool Create(const char *newName);
  11.109 +  bool Exists(void);
  11.110 +  bool TestName(const char *newName);
  11.111 +  //
  11.112 +  inline const char *Name(void) const { return obj->Name(); }
  11.113 +  inline const char *BaseName(void) const { return basename; }
  11.114 +  inline bool IsWinAmp(void) const { return isWinAmp; }
  11.115 +  };
  11.116 +
  11.117 +// ----------------------------------------------------------------
  11.118 +
  11.119 +class cInstantPlayList : public cScanDir, public cPlayList {
  11.120 +protected:
  11.121 +  virtual void DoItem(cFileSource *src, const char *subdir, const char *name);
  11.122 +public:
  11.123 +  cInstantPlayList(cFileObj *Obj);
  11.124 +  virtual bool Load(void);
  11.125 +  virtual bool Save(void) { return false; }
  11.126 +  };
  11.127 +
  11.128 +// ----------------------------------------------------------------
  11.129 +
  11.130 +class cPlayLists : public cScanDir, public cList<cPlayList> {
  11.131 +protected:
  11.132 +  virtual void DoItem(cFileSource *src, const char *subdir, const char *name);
  11.133 +public:
  11.134 +  bool Load(cFileSource *Source);
  11.135 +  };
  11.136 +
  11.137 +#endif //___DATA_MP3_H
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/data-src.h	Sat Dec 29 14:47:40 2007 +0100
    12.3 @@ -0,0 +1,40 @@
    12.4 +/*
    12.5 + * MP3/MPlayer plugin to VDR (C++)
    12.6 + *
    12.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    12.8 + *
    12.9 + * This code is free software; you can redistribute it and/or
   12.10 + * modify it under the terms of the GNU General Public License
   12.11 + * as published by the Free Software Foundation; either version 2
   12.12 + * of the License, or (at your option) any later version.
   12.13 + *
   12.14 + * This code is distributed in the hope that it will be useful,
   12.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12.17 + * GNU General Public License for more details.
   12.18 + *
   12.19 + * You should have received a copy of the GNU General Public License
   12.20 + * along with this program; if not, write to the Free Software
   12.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   12.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   12.23 + */
   12.24 +
   12.25 +#ifndef ___DATA_SRC_H
   12.26 +#define ___DATA_SRC_H
   12.27 +
   12.28 +#include <vdr/config.h>
   12.29 +#include "data.h"
   12.30 +
   12.31 +// ----------------------------------------------------------------
   12.32 +
   12.33 +class cFileSources : public cConfig<cFileSource> {
   12.34 +private:
   12.35 +  cFileSource *current;
   12.36 +public:
   12.37 +  virtual bool Load(const char *filename, bool dummy=false);
   12.38 +  void SetSource(cFileSource *source) { current=source; }
   12.39 +  cFileSource *GetSource(void) { return current; }
   12.40 +  cFileSource *FindSource(const char *filename);
   12.41 +  };
   12.42 +
   12.43 +#endif //___DATA_SRC_H
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/data.c	Sat Dec 29 14:47:40 2007 +0100
    13.3 @@ -0,0 +1,574 @@
    13.4 +/*
    13.5 + * MP3/MPlayer plugin to VDR (C++)
    13.6 + *
    13.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    13.8 + *
    13.9 + * This code is free software; you can redistribute it and/or
   13.10 + * modify it under the terms of the GNU General Public License
   13.11 + * as published by the Free Software Foundation; either version 2
   13.12 + * of the License, or (at your option) any later version.
   13.13 + *
   13.14 + * This code is distributed in the hope that it will be useful,
   13.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13.17 + * GNU General Public License for more details.
   13.18 + *
   13.19 + * You should have received a copy of the GNU General Public License
   13.20 + * along with this program; if not, write to the Free Software
   13.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   13.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   13.23 + */
   13.24 +
   13.25 +#include <ctype.h>
   13.26 +#include <dirent.h>
   13.27 +#include <stdlib.h>
   13.28 +#include <stdio.h>
   13.29 +#include <errno.h>
   13.30 +#include <sys/stat.h>
   13.31 +#include <sys/types.h>
   13.32 +#include <unistd.h>
   13.33 +#include <dirent.h>
   13.34 +#include <fnmatch.h>
   13.35 +
   13.36 +#include <vdr/tools.h>
   13.37 +
   13.38 +#include "common.h"
   13.39 +#include "data.h"
   13.40 +#include "data-src.h"
   13.41 +
   13.42 +// ----------------------------------------------------------------
   13.43 +
   13.44 +const char *mountscript = "mount.sh";
   13.45 +
   13.46 +char *Quote(const char *str)
   13.47 +{
   13.48 +  char *nstr=MALLOC(char,strlen(str)*2);
   13.49 +  char *p=nstr;
   13.50 +  while(*str) {
   13.51 +    switch(*str) {
   13.52 +      case '$':  // dollar
   13.53 +      case '\\': // backslash
   13.54 +      case '\"': // double quote
   13.55 +      case '`':  // back tick
   13.56 +                 *p++='\\'; break;
   13.57 +      }
   13.58 +    *p++=*str++;
   13.59 +    }
   13.60 +  *p=0;
   13.61 +  return nstr;
   13.62 +}
   13.63 +
   13.64 +char *AddPath(const char *dir, const char *filename)
   13.65 +{
   13.66 +  char *name=0;
   13.67 +  asprintf(&name,"%s/%s",dir,filename);
   13.68 +  return name;
   13.69 +}
   13.70 +
   13.71 +bool CheckVDRVersion(int Version, int Major, int Minor, const char *text)
   13.72 +{
   13.73 +  static char vv[] = VDRVERSION;
   13.74 +  int version, major, minor;
   13.75 +  if(sscanf(vv,"%d.%d.%d",&version,&major,&minor)==3) {
   13.76 +    if(version<Version ||
   13.77 +       (version==Version && major<Major) ||
   13.78 +       (version==Version && major==Major && minor<Minor)) {
   13.79 +      if(text) {
   13.80 +        esyslog("ERROR: %s plugin needs at least VDR version %d.%d.%d",text,Version,Major,Minor);
   13.81 +        fprintf(stderr,"%s plugin needs at least VDR version %d.%d.%d\n",text,Version,Major,Minor);
   13.82 +        }
   13.83 +      return false;
   13.84 +      }
   13.85 +    }
   13.86 +  else esyslog("ERROR: cannot parse VDR version string '%s'",vv);
   13.87 +  return true;
   13.88 +}
   13.89 +
   13.90 +// -- cScanDir --------------------------------------------------------------
   13.91 +
   13.92 +bool cScanDir::ScanDir(cFileSource *src, const char *subdir, eScanType type, const char * const *spec, const char * const *excl, bool recursiv)
   13.93 +{
   13.94 +  bool res=true;
   13.95 +  char *dir, *f=0;
   13.96 +  asprintf(&dir,subdir ? "%s/%s":"%s",src->BaseDir(),subdir);
   13.97 +  DIR *d=opendir(dir);
   13.98 +  if(d) {
   13.99 +    struct dirent64 *e;
  13.100 +    while((e=readdir64(d))) {
  13.101 +      if(!strcmp(e->d_name,".") || !strcmp(e->d_name,"..")) continue;
  13.102 +      free(f);
  13.103 +      if(!(f=AddPath(dir,e->d_name))) continue;
  13.104 +      struct stat64 st;
  13.105 +      if(stat64(f,&st)<0) {
  13.106 +        esyslog("ERROR: stat(1) %s: %s",f,strerror(errno));
  13.107 +        continue;
  13.108 +        }
  13.109 +      if(S_ISLNK(st.st_mode)) {
  13.110 +        char *of=f; f=ReadLink(of); free(of);
  13.111 +        if(!f) continue;
  13.112 +        if(stat64(f,&st)<0) {
  13.113 +          esyslog("ERROR: stat(2) %s: %s",f,strerror(errno));
  13.114 +          continue;
  13.115 +          }
  13.116 +        }
  13.117 +      if(S_ISDIR(st.st_mode)) {
  13.118 +        if(type==stFile && recursiv) {
  13.119 +          char *s;
  13.120 +          asprintf(&s,subdir ? "%2$s/%1$s":"%s",e->d_name,subdir);
  13.121 +          res=ScanDir(src,s,type,spec,excl,recursiv);
  13.122 +          free(s);
  13.123 +          if(!res) break;
  13.124 +          continue;
  13.125 +          }
  13.126 +        if(type!=stDir) continue;
  13.127 +        }
  13.128 +      if(S_ISREG(st.st_mode)) {
  13.129 +        if(type!=stFile) continue;
  13.130 +        if(spec) {
  13.131 +          bool ok=false;
  13.132 +          for(const char * const *m=spec; *m; m++) {
  13.133 +            int n=fnmatch(*m,e->d_name,FNM_CASEFOLD);
  13.134 +            if(n==0) { ok=true; break; }
  13.135 +            if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(1) %s: %s",*m,strerror(errno));
  13.136 +            }
  13.137 +          if(!ok) continue;
  13.138 +          }
  13.139 +        if(excl) {
  13.140 +          bool ok=true;
  13.141 +          for(const char * const *m=excl; *m; m++) {
  13.142 +            int n=fnmatch(*m,e->d_name,FNM_CASEFOLD);
  13.143 +            if(n==0) { ok=false; break; }
  13.144 +            if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(2) %s: %s",*m,strerror(errno));
  13.145 +            }
  13.146 +          if(!ok) continue;
  13.147 +          }
  13.148 +        }
  13.149 +      DoItem(src,subdir,e->d_name);
  13.150 +      }
  13.151 +    closedir(d);
  13.152 +    }
  13.153 +  else {
  13.154 +    esyslog("ERROR: opendir %s: %s",dir,strerror(errno));
  13.155 +    res=false;
  13.156 +    }
  13.157 +  free(dir); free(f);
  13.158 +  return res;
  13.159 +}
  13.160 +
  13.161 +// -- cFileObj --------------------------------------------------------------
  13.162 +
  13.163 +cFileObj::cFileObj(cFileSource *Source, const char *Subdir, const char *Name, const eObjType Type)
  13.164 +{
  13.165 +  path=fpath=0;
  13.166 +  source=Source;
  13.167 +  subdir=Subdir ? strdup(Subdir):0;
  13.168 +  name=Name ? strdup(Name):0;
  13.169 +  type=Type;
  13.170 +  Set();
  13.171 +}
  13.172 +
  13.173 +cFileObj::cFileObj(const cFileObj *obj)
  13.174 +{
  13.175 +  path=fpath=0;
  13.176 +  source=obj->source;
  13.177 +  subdir=obj->subdir ? strdup(obj->subdir):0;
  13.178 +  name=obj->name ? strdup(obj->name):0;
  13.179 +  type=obj->type;
  13.180 +  Set();
  13.181 +}
  13.182 +
  13.183 +cFileObj::~cFileObj()
  13.184 +{
  13.185 +  free(name);
  13.186 +  free(subdir);
  13.187 +  free(path);
  13.188 +  free(fpath);
  13.189 +}
  13.190 +
  13.191 +#if APIVERSNUM >= 10315
  13.192 +int cFileObj::Compare(const cListObject &ListObject) const
  13.193 +{
  13.194 +  cFileObj *obj=(cFileObj *)&ListObject;
  13.195 +  if(type==otParent) return obj->type==otParent ? 0:-1;
  13.196 +  if(obj->type==otParent) return 1;
  13.197 +  if(type==otBase) return obj->type==otBase ? 0:1;
  13.198 +  if(obj->type==otBase) return -1;
  13.199 +  if(type!=obj->type) {
  13.200 +    if(type==otFile) return 1;
  13.201 +    return -1;
  13.202 +    }
  13.203 +  return strcasecmp(path,obj->path);
  13.204 +}
  13.205 +#else
  13.206 +bool cFileObj::operator<(const cListObject &ListObject)
  13.207 +{
  13.208 +  cFileObj *obj=(cFileObj *)&ListObject;
  13.209 +  if(type==otParent) return obj->type==otParent ? false:true;
  13.210 +  if(obj->type==otParent) return false;
  13.211 +  if(type==otBase) return false;
  13.212 +  if(obj->type==otBase) return true;
  13.213 +  if(type!=obj->type) {
  13.214 +    if(type==otFile) return false;
  13.215 +    return true;
  13.216 +    }
  13.217 +  return strcasecmp(path,obj->path)<0;
  13.218 +}
  13.219 +#endif
  13.220 +
  13.221 +void cFileObj::SplitAndSet(const char *Path)
  13.222 +{
  13.223 +  free(subdir); subdir=0;
  13.224 +  const char *p=Path;
  13.225 +  if(Path[0]=='/') {
  13.226 +    int l=strlen(source->BaseDir());
  13.227 +    if(!strncasecmp(Path,source->BaseDir(),l)) p+=l+1;
  13.228 +    else {
  13.229 +      l=strlen(source->RealBaseDir());
  13.230 +      if(!strncasecmp(Path,source->RealBaseDir(),l)) p+=l+1;
  13.231 +      else {
  13.232 +        char buff[strlen(Path)+5];
  13.233 +        strcpy(buff,"/");
  13.234 +        p++;
  13.235 +        while(1) {
  13.236 +          char real[PATH_MAX+1];
  13.237 +          if(!realpath(buff,real)) {
  13.238 +            if(errno!=ENOENT && errno!=ENOTDIR)
  13.239 +              esyslog("ERROR: realpath: %s: %s",buff,strerror(errno));
  13.240 +            p=Path+1;
  13.241 +            break;
  13.242 +            }
  13.243 +          if(!strncasecmp(real,source->RealBaseDir(),l))
  13.244 +            break;
  13.245 +          const char *r=index(p,'/');
  13.246 +          if(!r) {
  13.247 +            esyslog("ERROR: can't find source basedir in '%s'. Outside source?",Path);
  13.248 +            p=Path+1;
  13.249 +            break;
  13.250 +            }
  13.251 +          strn0cpy(buff,Path,r-Path+1);
  13.252 +          p=r+1;
  13.253 +          }
  13.254 +        }
  13.255 +      }
  13.256 +    }
  13.257 +
  13.258 +  const char *s=rindex(p,'/');
  13.259 +  if(s) {
  13.260 +    const int l=s-p+1;
  13.261 +    subdir=MALLOC(char,l);
  13.262 +    if(subdir) strn0cpy(subdir,p,l);
  13.263 +    SetName(s+1);
  13.264 +    }
  13.265 +  else
  13.266 +    SetName(p);
  13.267 +}
  13.268 +
  13.269 +void cFileObj::SetName(const char *Name)
  13.270 +{
  13.271 +  free(name);
  13.272 +  name=Name ? strdup(Name):0;
  13.273 +  Set();
  13.274 +}
  13.275 +
  13.276 +void cFileObj::Set(void)
  13.277 +{
  13.278 +  free(path); path=0;
  13.279 +  asprintf(&path,subdir ? "%2$s/%1$s":"%s",name,subdir);
  13.280 +  free(fpath); fpath=0;
  13.281 +  MakeFullName(&fpath,name);
  13.282 +}
  13.283 +
  13.284 +void cFileObj::MakeFullName(char **fp, const char *Name)
  13.285 +{
  13.286 +  asprintf(fp,subdir ? "%1$s/%3$s/%2$s":"%s/%s",source->BaseDir(),Name,subdir);
  13.287 +}
  13.288 +
  13.289 +bool cFileObj::GuessType(void)
  13.290 +{
  13.291 +  struct stat64 ds;
  13.292 +  if(!stat64(fpath,&ds)) {
  13.293 +    if(S_ISREG(ds.st_mode))      type=otFile;
  13.294 +    else if(S_ISDIR(ds.st_mode)) type=subdir ? otDir:otBase;
  13.295 +    else return false;
  13.296 +    return true;
  13.297 +    }
  13.298 +  return false;
  13.299 +}
  13.300 +
  13.301 +bool cFileObj::Exists(void)
  13.302 +{
  13.303 +  if(type==otFile) {
  13.304 +    struct stat64 ds;
  13.305 +    if(!stat64(fpath,&ds) &&
  13.306 +       S_ISREG(ds.st_mode) &&
  13.307 +       !access(fpath,R_OK)) return true;
  13.308 +    }
  13.309 +  return false;
  13.310 +}
  13.311 +
  13.312 +bool cFileObj::TestName(const char *newName)
  13.313 +{
  13.314 +  bool r=false;
  13.315 +  if(type==otFile) {
  13.316 +    char *fname;
  13.317 +    MakeFullName(&fname,newName);
  13.318 +    if(access(fname,F_OK)==0) r=true;
  13.319 +    free(fname);
  13.320 +    }
  13.321 +  return r;
  13.322 +}
  13.323 +
  13.324 +bool cFileObj::Rename(const char *newName)
  13.325 +{
  13.326 +  bool r=false;
  13.327 +  if(type==otFile) {
  13.328 +    char *fname;
  13.329 +    MakeFullName(&fname,newName);
  13.330 +    if(access(fname,F_OK) && (!rename(fpath,fname))) {
  13.331 +      SetName(newName);
  13.332 +      r=true;
  13.333 +      }
  13.334 +    free(fname);
  13.335 +    }
  13.336 +  return r;
  13.337 +}
  13.338 +
  13.339 +bool cFileObj::Create(const char *newName)
  13.340 +{
  13.341 +  bool r=false;
  13.342 +  if(type==otFile) {
  13.343 +    char *fname;
  13.344 +    MakeFullName(&fname,newName);
  13.345 +    FILE *newf;
  13.346 +    if(access(fname,F_OK) && (newf=fopen(fname,"w"))) {
  13.347 +      fclose(newf);
  13.348 +      SetName(newName);
  13.349 +      r=true;
  13.350 +      }
  13.351 +    free(fname);
  13.352 +    }
  13.353 +  return r;
  13.354 +}
  13.355 +
  13.356 +bool cFileObj::Delete(void)
  13.357 +{
  13.358 +  if(type==otFile && !unlink(fpath)) return true;
  13.359 +  return false;
  13.360 +}
  13.361 +
  13.362 +// -- cDirList --------------------------------------------------------------
  13.363 +
  13.364 +bool cDirList::Load(cFileSource *src, const char *subdir, const char * const *excl)
  13.365 +{
  13.366 +  static const char *excl_s[] = { ".*",0 };
  13.367 +
  13.368 +  bool res=false;
  13.369 +  Clear();
  13.370 +  if(subdir) Add(new cFileObj(src,subdir,"..",otParent));
  13.371 +  otype=otDir;
  13.372 +  if(ScanDir(src,subdir,stDir,0,0,false)) {
  13.373 +    otype=otFile;
  13.374 +    if(!excl) excl=excl_s;
  13.375 +    if(ScanDir(src,subdir,stFile,src->Include(),excl,false)) res=true;
  13.376 +    }
  13.377 +  Sort();
  13.378 +  return res;
  13.379 +}
  13.380 +
  13.381 +void cDirList::DoItem(cFileSource *src, const char *subdir, const char *name)
  13.382 +{
  13.383 +  Add(new cFileObj(src,subdir,name,otype));
  13.384 +}
  13.385 +
  13.386 +// -- cFileSource --------------------------------------------------------------
  13.387 +
  13.388 +cFileSource::cFileSource(void)
  13.389 +{
  13.390 +  browsedir=browseparent=0;
  13.391 +  basedir=realbasedir=description=0; useCount=0;
  13.392 +  needsmount=false;
  13.393 +  include=0; incCount=0;
  13.394 +}
  13.395 +
  13.396 +cFileSource::cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
  13.397 +{
  13.398 +  browsedir=browseparent=0;
  13.399 +  basedir=realbasedir=description=0; useCount=0;
  13.400 +  include=0; incCount=0;
  13.401 +  Set(Basedir,Description,NeedsMount,Include);
  13.402 +}
  13.403 +
  13.404 +cFileSource::~cFileSource()
  13.405 +{
  13.406 +  ClearRemember();
  13.407 +  Clear();
  13.408 +}
  13.409 +
  13.410 +void cFileSource::Clear(void)
  13.411 +{
  13.412 +  free(basedir); basedir=0;
  13.413 +  free(realbasedir); realbasedir=0;
  13.414 +  free(description); description=0;
  13.415 +  for(int i=0; i<incCount; i++) free(include[i]);
  13.416 +  free(include); include=0; incCount=0;
  13.417 +}
  13.418 +
  13.419 +void cFileSource::Set(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
  13.420 +{
  13.421 +  Clear();
  13.422 +  basedir=strdup(Basedir);
  13.423 +  description=strdup(Description);
  13.424 +  if(Include) {
  13.425 +    do {
  13.426 +      char *s=index(Include,'/');
  13.427 +      int l=s ? s-Include : strlen(Include);
  13.428 +      if(l) {
  13.429 +        char **s=(char **)realloc(include,(incCount+2)*sizeof(char *));
  13.430 +        if(s) {
  13.431 +          include=s;
  13.432 +          include[incCount]=strndup(Include,l);
  13.433 +          incCount++;
  13.434 +          include[incCount]=0;
  13.435 +          }
  13.436 +        }
  13.437 +      Include+=l+(s ? 1:0);
  13.438 +      } while(*Include>0);
  13.439 +    }
  13.440 +#ifdef DEBUG
  13.441 +  if(include) {
  13.442 +    printf("sources: filesource %s includes (count=%d):",basedir,incCount);
  13.443 +    for(int i=0; i<incCount; i++) printf(" '%s'",include[i]);
  13.444 +    printf("\n");
  13.445 +    }
  13.446 +  else
  13.447 +    printf("sources: filesource %s has no includes set\n",basedir);
  13.448 +#endif
  13.449 +  needsmount=NeedsMount;
  13.450 +
  13.451 +  realbasedir=MALLOC(char,PATH_MAX+1);
  13.452 +  if(realpath(basedir,realbasedir)) {
  13.453 +    if(strcmp(basedir,realbasedir)) { esyslog("WARNING: source base %s expands to %s",basedir,realbasedir); }
  13.454 +    }
  13.455 +  else {
  13.456 +    switch(errno) {
  13.457 +      case EACCES:  esyslog("ERROR: source base %s permission denied",basedir); break;
  13.458 +      case ENOENT:  esyslog("ERROR: source base %s not found",basedir); break;
  13.459 +      case ENOTDIR: esyslog("ERROR: source base %s has invalid path",basedir); break;
  13.460 +      default:      esyslog("ERROR: source base %s realpath: %s",basedir,strerror(errno)); break;
  13.461 +      }
  13.462 +    strn0cpy(realbasedir,basedir,PATH_MAX);
  13.463 +    }
  13.464 +}
  13.465 +
  13.466 +void cFileSource::SetRemember(const char *dir, const char *parent)
  13.467 +{
  13.468 +  ClearRemember();
  13.469 +  if(dir) browsedir=strdup(dir);
  13.470 +  if(parent) browseparent=strdup(parent);
  13.471 +}
  13.472 +
  13.473 +void cFileSource::ClearRemember(void)
  13.474 +{
  13.475 +  free(browsedir); browsedir=0;
  13.476 +  free(browseparent); browseparent=0;
  13.477 +}
  13.478 +
  13.479 +bool cFileSource::GetRemember(char * &dir, char * &parent)
  13.480 +{
  13.481 +  dir=parent=0;
  13.482 +  if(browsedir) {
  13.483 +    if(browseparent) parent=strdup(browseparent);
  13.484 +    dir=strdup(browsedir);
  13.485 +    return true;
  13.486 +    }
  13.487 +  return false;
  13.488 +}
  13.489 +
  13.490 +bool cFileSource::Parse(char *s)
  13.491 +{
  13.492 +  char base[256], des[256], incl[256];
  13.493 +  int needsmount, n;
  13.494 +  if((n=sscanf(s,"%255[^;];%255[^;];%d;%255[^;]",base,des,&needsmount,incl))>=3) {
  13.495 +    char *base2=skipspace(stripspace(base));
  13.496 +    int l=strlen(base2);
  13.497 +    while(l>0 && base2[l-1]=='/') {
  13.498 +      esyslog("WARNING: removing trailing '/' from base %s",base2);
  13.499 +      base2[l-1]=0;
  13.500 +      l--;
  13.501 +      }
  13.502 +    Set(base2,skipspace(stripspace(des)),needsmount!=0,n>3?skipspace(stripspace(incl)):0);
  13.503 +
  13.504 +    // do some checking of the basedir and issue a warning if apropriate
  13.505 +    if(access(realbasedir,R_OK)) { esyslog("WARNING: source base %s not found/permission denied",realbasedir); }
  13.506 +    else {
  13.507 +      struct stat64 ds;
  13.508 +      if(lstat64(realbasedir,&ds)) { esyslog("WARNING: can't stat source base %s",realbasedir); }
  13.509 +      else if(!S_ISDIR(ds.st_mode)) { esyslog("WARNING: source base %s is not a directory",realbasedir); }
  13.510 +      }
  13.511 +    return true;
  13.512 +    }
  13.513 +  return false;
  13.514 +}
  13.515 +
  13.516 +bool cFileSource::Action(eAction act)
  13.517 +{
  13.518 +  static char *str[] = { "mount","unmount","eject","status" };
  13.519 +  
  13.520 +  char *cmd=0;
  13.521 +  asprintf(&cmd,"%s %s %s",mountscript,str[act],basedir);
  13.522 +  bool res=(system(cmd)==0);
  13.523 +  free(cmd);
  13.524 +  return res;
  13.525 +}
  13.526 +
  13.527 +bool cFileSource::Mount(void)
  13.528 +{
  13.529 +  bool res=false;
  13.530 +  if(needsmount && (res=Action(acMount))) ClearRemember();
  13.531 +  return res;
  13.532 +}
  13.533 +
  13.534 +bool cFileSource::Unmount(void)
  13.535 +{
  13.536 +  bool res=false;
  13.537 +  if(needsmount) {
  13.538 +    if(!useCount && (res=Action(acUnmount))) ClearRemember();
  13.539 +    }
  13.540 +  return res;
  13.541 +}
  13.542 +
  13.543 +bool cFileSource::Eject(void)
  13.544 +{
  13.545 +  bool res=false;
  13.546 +  if(needsmount) {
  13.547 +    if(!useCount && (res=Action(acEject))) ClearRemember();
  13.548 +    }
  13.549 +  return res;
  13.550 +}
  13.551 +
  13.552 +bool cFileSource::Status(void)
  13.553 +{
  13.554 +  if(needsmount) return Action(acStatus);
  13.555 +  return true;
  13.556 +}
  13.557 +
  13.558 +// -- cFileSources --------------------------------------------------------------
  13.559 +
  13.560 +bool cFileSources::Load(const char *filename, bool dummy)
  13.561 +{
  13.562 +  if(cConfig<cFileSource>::Load(filename,true)) {
  13.563 +    SetSource(First());
  13.564 +    return true;
  13.565 +    }
  13.566 +  return false;
  13.567 +}
  13.568 +
  13.569 +cFileSource *cFileSources::FindSource(const char *filename)
  13.570 +{
  13.571 +  cFileSource *src=First();
  13.572 +  while(src) {
  13.573 +    if(startswith(filename,src->RealBaseDir())) return src;
  13.574 +    src=Next(src);
  13.575 +    }
  13.576 +  return 0;
  13.577 +}
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/data.h	Sat Dec 29 14:47:40 2007 +0100
    14.3 @@ -0,0 +1,133 @@
    14.4 +/*
    14.5 + * MP3/MPlayer plugin to VDR (C++)
    14.6 + *
    14.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    14.8 + *
    14.9 + * This code is free software; you can redistribute it and/or
   14.10 + * modify it under the terms of the GNU General Public License
   14.11 + * as published by the Free Software Foundation; either version 2
   14.12 + * of the License, or (at your option) any later version.
   14.13 + *
   14.14 + * This code is distributed in the hope that it will be useful,
   14.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14.17 + * GNU General Public License for more details.
   14.18 + *
   14.19 + * You should have received a copy of the GNU General Public License
   14.20 + * along with this program; if not, write to the Free Software
   14.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   14.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   14.23 + */
   14.24 +
   14.25 +#ifndef ___DATA_H
   14.26 +#define ___DATA_H
   14.27 +
   14.28 +#include <vdr/tools.h>
   14.29 +
   14.30 +// ----------------------------------------------------------------
   14.31 +
   14.32 +class cFileSource;
   14.33 +
   14.34 +extern char *Quote(const char *str);
   14.35 +extern char *AddPath(const char *dir, const char *filename);
   14.36 +extern bool CheckVDRVersion(int Version, int Major, int Minor, const char *text=0);
   14.37 +
   14.38 +// ----------------------------------------------------------------
   14.39 +
   14.40 +class cScanDir {
   14.41 +protected:
   14.42 +  enum eScanType { stFile, stDir };
   14.43 +  virtual void DoItem(cFileSource *src, const char *subdir, const char *name)=0;
   14.44 +public:
   14.45 +  virtual ~cScanDir() {}
   14.46 +  bool ScanDir(cFileSource *src, const char *subdir, eScanType type, const char * const *spec, const char * const *excl, bool recursiv);
   14.47 +  };
   14.48 +
   14.49 +// ----------------------------------------------------------------
   14.50 +
   14.51 +enum eObjType { otDir, otParent, otFile, otBase };
   14.52 +
   14.53 +class cFileObj : public cListObject {
   14.54 +private:
   14.55 +  cFileSource *source;
   14.56 +  char *subdir, *name, *path, *fpath;
   14.57 +  eObjType type;
   14.58 +  //
   14.59 +  void Set(void);
   14.60 +  void MakeFullName(char **fp, const char *Name);
   14.61 +public:
   14.62 +  cFileObj(cFileSource *Source, const char *Subdir, const char *Name, const eObjType Type);
   14.63 +  cFileObj(const cFileObj *obj);
   14.64 +  virtual ~cFileObj();
   14.65 +#if APIVERSNUM >= 10315
   14.66 +  virtual int Compare(const cListObject &ListObject) const;
   14.67 +#else
   14.68 +  virtual bool operator<(const cListObject &ListObject);
   14.69 +#endif
   14.70 +  void SetName(const char *Name);
   14.71 +  void SplitAndSet(const char *Path);
   14.72 +  bool GuessType(void);
   14.73 +  //
   14.74 +  bool Exists(void);
   14.75 +  bool TestName(const char *newName);
   14.76 +  bool Rename(const char *newName);
   14.77 +  bool Create(const char *newName);
   14.78 +  bool Delete(void);
   14.79 +  //
   14.80 +  inline const char *Name(void) const { return name; }
   14.81 +  inline const char *Subdir(void) const { return subdir; }
   14.82 +  inline cFileSource *Source(void) const { return source; }
   14.83 +  inline eObjType Type(void) const { return type; }
   14.84 +  inline const char *Path(void) const { return path; }
   14.85 +  inline const char *FullPath(void) const { return fpath; }
   14.86 +  };
   14.87 +
   14.88 +// ----------------------------------------------------------------
   14.89 +
   14.90 +class cDirList : public cScanDir, public cList<cFileObj> {
   14.91 +private:
   14.92 +  eObjType otype;
   14.93 +protected:
   14.94 +  virtual void DoItem(cFileSource *src, const char *subdir, const char *name);
   14.95 +public:
   14.96 +  bool Load(cFileSource *src, const char *subdir, const char * const *excl=0);
   14.97 +  };
   14.98 +
   14.99 +// ----------------------------------------------------------------
  14.100 +
  14.101 +class cFileSource : public cListObject {
  14.102 +private:
  14.103 +  enum eAction { acMount, acUnmount, acEject, acStatus };
  14.104 +  char *basedir, *realbasedir;
  14.105 +  char *description;
  14.106 +  char **include;
  14.107 +  bool needsmount;
  14.108 +  int useCount, incCount;
  14.109 +  // remember last browse position
  14.110 +  char *browsedir, *browseparent;
  14.111 +  //
  14.112 +  void Set(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include);
  14.113 +  bool Action(eAction act);
  14.114 +  void ClearRemember(void);
  14.115 +  void Clear(void);
  14.116 +public:
  14.117 +  cFileSource(void);
  14.118 +  cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include=0);
  14.119 +  ~cFileSource();
  14.120 +  bool Parse(char *s);
  14.121 +  bool Mount(void);
  14.122 +  bool Unmount(void);
  14.123 +  bool Eject(void);
  14.124 +  bool Status(void);
  14.125 +  void Block(void) { useCount++; }
  14.126 +  void Unblock(void) { useCount--; }
  14.127 +  void SetRemember(const char *dir, const char *parent);
  14.128 +  bool GetRemember(char * &dir, char * &parent);
  14.129 +  inline const char *BaseDir(void) const { return basedir; }
  14.130 +  inline const char *RealBaseDir(void) const { return realbasedir; }
  14.131 +  inline const char *Description(void) const { return description; }
  14.132 +  inline const char * const * Include(void) const { return include; }
  14.133 +  inline bool NeedsMount(void) const { return needsmount; }
  14.134 +  };
  14.135 +
  14.136 +#endif //___DATA_H
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/decoder-core.h	Sat Dec 29 14:47:40 2007 +0100
    15.3 @@ -0,0 +1,64 @@
    15.4 +/*
    15.5 + * MP3/MPlayer plugin to VDR (C++)
    15.6 + *
    15.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    15.8 + *
    15.9 + * This code is free software; you can redistribute it and/or
   15.10 + * modify it under the terms of the GNU General Public License
   15.11 + * as published by the Free Software Foundation; either version 2
   15.12 + * of the License, or (at your option) any later version.
   15.13 + *
   15.14 + * This code is distributed in the hope that it will be useful,
   15.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15.17 + * GNU General Public License for more details.
   15.18 + *
   15.19 + * You should have received a copy of the GNU General Public License
   15.20 + * along with this program; if not, write to the Free Software
   15.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   15.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   15.23 + */
   15.24 +
   15.25 +#ifndef ___DECODER_CORE_H
   15.26 +#define ___DECODER_CORE_H
   15.27 +
   15.28 +#include "decoder.h"
   15.29 +
   15.30 +#define DEC_ID(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
   15.31 +
   15.32 +// ----------------------------------------------------------------
   15.33 +
   15.34 +enum eDecodeStatus { dsOK=0, dsPlay, dsSkip, dsEof, dsError, dsSoftError };
   15.35 +
   15.36 +struct Decode {
   15.37 +  eDecodeStatus status;
   15.38 +  int index;
   15.39 +  struct mad_pcm *pcm;
   15.40 +  };
   15.41 +
   15.42 +// ----------------------------------------------------------------
   15.43 +
   15.44 +#define CACHE_VERSION 7
   15.45 +
   15.46 +class cCacheData : public cSongInfo, public cFileInfo, public cListObject {
   15.47 +friend class cInfoCache;
   15.48 +private:
   15.49 +  int hash, version;
   15.50 +  time_t touch;
   15.51 +  cMutex lock;
   15.52 +protected:
   15.53 +  bool Save(FILE *f);
   15.54 +  bool Load(FILE *f);
   15.55 +  bool Upgrade(void);
   15.56 +  void Touch(void);
   15.57 +  bool Purge(void);
   15.58 +  void Create(cFileInfo *fi, cSongInfo *si);
   15.59 +public:
   15.60 +  cCacheData(void);
   15.61 +  void Lock(void) { lock.Lock(); }
   15.62 +  void Unlock(void) { lock.Unlock(); }
   15.63 +  };
   15.64 +
   15.65 +// ----------------------------------------------------------------
   15.66 +
   15.67 +#endif //___DECODER_CORE_H
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/decoder-mp3-stream.c	Sat Dec 29 14:47:40 2007 +0100
    16.3 @@ -0,0 +1,125 @@
    16.4 +/*
    16.5 + * MP3/MPlayer plugin to VDR (C++)
    16.6 + *
    16.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    16.8 + *
    16.9 + * This code is free software; you can redistribute it and/or
   16.10 + * modify it under the terms of the GNU General Public License
   16.11 + * as published by the Free Software Foundation; either version 2
   16.12 + * of the License, or (at your option) any later version.
   16.13 + *
   16.14 + * This code is distributed in the hope that it will be useful,
   16.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   16.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16.17 + * GNU General Public License for more details.
   16.18 + *
   16.19 + * You should have received a copy of the GNU General Public License
   16.20 + * along with this program; if not, write to the Free Software
   16.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   16.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   16.23 + */
   16.24 +
   16.25 +#include <stdlib.h>
   16.26 +#include <stdio.h>
   16.27 +#include <errno.h>
   16.28 +#include <sys/types.h>
   16.29 +
   16.30 +#include "common.h"
   16.31 +#include "decoder-mp3-stream.h"
   16.32 +#include "stream.h"
   16.33 +
   16.34 +// --- cNetScanID3 -------------------------------------------------------------
   16.35 +
   16.36 +class cNetScanID3 : public cScanID3 {
   16.37 +private:
   16.38 +  cNetStream *nstr;
   16.39 +  //
   16.40 +  void IcyInfo(void);
   16.41 +public:
   16.42 +  cNetScanID3(cNetStream *Str, bool *Urgent);
   16.43 +  virtual bool DoScan(bool KeepOpen=false);
   16.44 +  virtual void InfoHook(struct mad_header *header);
   16.45 +  };
   16.46 +
   16.47 +cNetScanID3::cNetScanID3(cNetStream *Str, bool *Urgent)
   16.48 +:cScanID3(Str,Urgent)
   16.49 +{
   16.50 +  nstr=Str;
   16.51 +}
   16.52 +
   16.53 +bool cNetScanID3::DoScan(bool KeepOpen)
   16.54 +{
   16.55 +  Clear();
   16.56 +  IcyInfo();
   16.57 +  if(!Title) FakeTitle(nstr->Filename);
   16.58 +  Total=0;
   16.59 +  ChMode=3;
   16.60 +  DecoderID=DEC_MP3S;
   16.61 +  InfoDone();
   16.62 +  return true;
   16.63 +}
   16.64 +
   16.65 +void cNetScanID3::InfoHook(struct mad_header *header)
   16.66 +{
   16.67 +  if(nstr->IcyChanged()) IcyInfo();
   16.68 +  SampleFreq=header->samplerate;
   16.69 +  Channels=MAD_NCHANNELS(header);
   16.70 +  ChMode=header->mode;
   16.71 + 
   16.72 +  int br=header->bitrate;
   16.73 +  if(Bitrate<0) Bitrate=br;
   16.74 +  else if(Bitrate!=br) {
   16.75 +    if(MaxBitrate<0) {
   16.76 +      if(Bitrate<br) MaxBitrate=br;
   16.77 +      else { MaxBitrate=Bitrate; Bitrate=br; }
   16.78 +      }
   16.79 +    else {
   16.80 +      if(br>MaxBitrate) MaxBitrate=br;
   16.81 +      if(br<Bitrate)    Bitrate=br;
   16.82 +      }
   16.83 +    }
   16.84 +}
   16.85 +
   16.86 +void cNetScanID3::IcyInfo(void)
   16.87 +{
   16.88 +  const char *t=nstr->IcyTitle();
   16.89 +  const char *a;
   16.90 +  if(t) {
   16.91 +    a=nstr->IcyName();
   16.92 +    if(!a) a=nstr->IcyUrl();
   16.93 +    }
   16.94 +  else {
   16.95 +    t=nstr->IcyName();
   16.96 +    a=nstr->IcyUrl();
   16.97 +    }
   16.98 +  if(t && (!Title || strcmp(t,Title))) {
   16.99 +    free(Title);
  16.100 +    Title=strdup(t);
  16.101 +    }
  16.102 +  if(a && (!Album || strcmp(a,Album))) {
  16.103 +    free(Album);
  16.104 +    Album=strdup(a);
  16.105 +    }
  16.106 +}
  16.107 +
  16.108 +// --- cMP3StreamDecoder -------------------------------------------------------
  16.109 +
  16.110 +cMP3StreamDecoder::cMP3StreamDecoder(const char *Filename)
  16.111 +:cMP3Decoder(Filename,false)
  16.112 +{
  16.113 +  nstr=new cNetStream(filename);
  16.114 +  str=nstr;
  16.115 +  nscan=new cNetScanID3(nstr,&urgentLock);
  16.116 +  scan=nscan;
  16.117 +  isStream=true;
  16.118 +}
  16.119 +
  16.120 +bool cMP3StreamDecoder::Valid(void)
  16.121 +{
  16.122 +  bool res=false;
  16.123 +  if(TryLock()) {
  16.124 +    if(nstr->Valid()) res=true;
  16.125 +    Unlock();
  16.126 +    }
  16.127 +  return res;
  16.128 +}
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/decoder-mp3-stream.h	Sat Dec 29 14:47:40 2007 +0100
    17.3 @@ -0,0 +1,45 @@
    17.4 +/*
    17.5 + * MP3/MPlayer plugin to VDR (C++)
    17.6 + *
    17.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    17.8 + *
    17.9 + * This code is free software; you can redistribute it and/or
   17.10 + * modify it under the terms of the GNU General Public License
   17.11 + * as published by the Free Software Foundation; either version 2
   17.12 + * of the License, or (at your option) any later version.
   17.13 + *
   17.14 + * This code is distributed in the hope that it will be useful,
   17.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   17.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17.17 + * GNU General Public License for more details.
   17.18 + *
   17.19 + * You should have received a copy of the GNU General Public License
   17.20 + * along with this program; if not, write to the Free Software
   17.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   17.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   17.23 + */
   17.24 +
   17.25 +#ifndef ___DECODER_MP3_STREAM_H
   17.26 +#define ___DECODER_MP3_STREAM_H
   17.27 +
   17.28 +#define DEC_MP3S     DEC_ID('M','P','3','S')
   17.29 +#define DEC_MP3S_STR "MP3S"
   17.30 +
   17.31 +#include "decoder-mp3.h"
   17.32 +
   17.33 +class cNetStream;
   17.34 +class cNetScanID3;
   17.35 +
   17.36 +// ----------------------------------------------------------------
   17.37 +
   17.38 +class cMP3StreamDecoder : public cMP3Decoder {
   17.39 +private:
   17.40 +  cNetStream *nstr;
   17.41 +  cNetScanID3 *nscan;
   17.42 +public:
   17.43 +  cMP3StreamDecoder(const char *Filename);
   17.44 +  virtual bool Valid(void);
   17.45 +  virtual bool IsStream(void) { return true; }
   17.46 +  };
   17.47 +
   17.48 +#endif //___DECODER_MP3_STREAM_H
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/decoder-mp3.c	Sat Dec 29 14:47:40 2007 +0100
    18.3 @@ -0,0 +1,641 @@
    18.4 +/*
    18.5 + * MP3/MPlayer plugin to VDR (C++)
    18.6 + *
    18.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    18.8 + *
    18.9 + * This code is free software; you can redistribute it and/or
   18.10 + * modify it under the terms of the GNU General Public License
   18.11 + * as published by the Free Software Foundation; either version 2
   18.12 + * of the License, or (at your option) any later version.
   18.13 + *
   18.14 + * This code is distributed in the hope that it will be useful,
   18.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   18.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18.17 + * GNU General Public License for more details.
   18.18 + *
   18.19 + * You should have received a copy of the GNU General Public License
   18.20 + * along with this program; if not, write to the Free Software
   18.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   18.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   18.23 + */
   18.24 +
   18.25 +#include <stdlib.h>
   18.26 +#include <stdio.h>
   18.27 +
   18.28 +#include "common.h"
   18.29 +#include "data-mp3.h"
   18.30 +#include "decoder-mp3.h"
   18.31 +#include "stream.h"
   18.32 +
   18.33 +#define MAX_FRAME_ERR 10
   18.34 +
   18.35 +// ----------------------------------------------------------------
   18.36 +
   18.37 +int MadStream(struct mad_stream *stream, cStream *str)
   18.38 +{
   18.39 +  unsigned char *data;
   18.40 +  unsigned long len;
   18.41 +  if(str->Stream(data,len,stream->next_frame)) {
   18.42 +    if(len>0) mad_stream_buffer(stream, data, len);
   18.43 +    return len;
   18.44 +    }
   18.45 +  return -1;
   18.46 +}
   18.47 +
   18.48 +// --- cMP3Decoder -------------------------------------------------------------
   18.49 +
   18.50 +cMP3Decoder::cMP3Decoder(const char *Filename, bool preinit)
   18.51 +:cDecoder(Filename)
   18.52 +{
   18.53 +  str=0; scan=0; isStream=false;
   18.54 +  if(preinit) {
   18.55 +    //d(printf("mp3: preinit\n"))
   18.56 +    str=new cStream(filename);
   18.57 +    scan=new cScanID3(str,&urgentLock);
   18.58 +    }
   18.59 +  fi=0; stream=0; frame=0; synth=0;
   18.60 +}
   18.61 +
   18.62 +cMP3Decoder::~cMP3Decoder()
   18.63 +{
   18.64 +  Clean();
   18.65 +  delete scan;
   18.66 +  delete str;
   18.67 +}
   18.68 +
   18.69 +bool cMP3Decoder::Valid(void)
   18.70 +{
   18.71 +  bool res=false;
   18.72 +  if(TryLock()) {
   18.73 +    struct mad_stream stream;
   18.74 +    struct mad_header header;
   18.75 +    mad_stream_init(&stream);
   18.76 +    mad_stream_options(&stream,MAD_OPTION_IGNORECRC);
   18.77 +    mad_header_init(&header);
   18.78 +    if(str->Open() && str->Seek()) {
   18.79 +      int count=10;
   18.80 +      do {
   18.81 +        if(mad_header_decode(&header,&stream)<0) {
   18.82 +          if(stream.error==MAD_ERROR_BUFLEN || stream.error==MAD_ERROR_BUFPTR) {
   18.83 +            if(MadStream(&stream,str)<=0) break;
   18.84 +            }
   18.85 +          else if(!MAD_RECOVERABLE(stream.error)) break;
   18.86 +          count++;
   18.87 +          }
   18.88 +        } while(--count);
   18.89 +      if(!count) res=true;
   18.90 +      }
   18.91 +    mad_header_finish(&header);
   18.92 +    mad_stream_finish(&stream);
   18.93 +    str->Close();
   18.94 +    Unlock();
   18.95 +    }
   18.96 +  return res;
   18.97 +}
   18.98 +
   18.99 +cFileInfo *cMP3Decoder::FileInfo(void)
  18.100 +{
  18.101 +  cFileInfo *fi=0;
  18.102 +  if(str->HasInfo()) fi=str;
  18.103 +  else if(TryLock()){
  18.104 +    if(str->Open()) { fi=str; str->Close(); }
  18.105 +    Unlock();
  18.106 +    }
  18.107 +  return fi;
  18.108 +}
  18.109 +
  18.110 +cSongInfo *cMP3Decoder::SongInfo(bool get)
  18.111 +{
  18.112 +  cSongInfo *si=0;
  18.113 +  if(scan->HasInfo()) si=scan;
  18.114 +  else if(get && TryLock()) {
  18.115 +    if(scan->DoScan()) si=scan;
  18.116 +    Unlock();
  18.117 +    }
  18.118 +  return si;
  18.119 +}
  18.120 +
  18.121 +cPlayInfo *cMP3Decoder::PlayInfo(void)
  18.122 +{
  18.123 +  if(playing) {
  18.124 +    pi.Index=mad_timer_count(playtime,MAD_UNITS_SECONDS);
  18.125 +    pi.Total=scan->Total;
  18.126 +    return &pi;
  18.127 +    }
  18.128 +  return 0;
  18.129 +}
  18.130 +
  18.131 +void cMP3Decoder::Init(void)
  18.132 +{
  18.133 +  Clean();
  18.134 +  stream=new struct mad_stream;
  18.135 +  mad_stream_init(stream);
  18.136 +  mad_stream_options(stream,MAD_OPTION_IGNORECRC);
  18.137 +  frame=new struct mad_frame;
  18.138 +  mad_frame_init(frame);
  18.139 +  synth=new struct mad_synth;
  18.140 +  mad_synth_init(synth);
  18.141 +  playtime=mad_timer_zero; skiptime=mad_timer_zero;
  18.142 +  framenum=framemax=0; mute=errcount=0;
  18.143 +}
  18.144 +
  18.145 +void cMP3Decoder::Clean(void)
  18.146 +{
  18.147 +  playing=false;
  18.148 +  if(synth) { mad_synth_finish(synth); delete synth; synth=0; }
  18.149 +  if(frame) { mad_frame_finish(frame); delete frame; frame=0; }
  18.150 +  if(stream) { mad_stream_finish(stream); delete stream; stream=0; }
  18.151 +  delete[] fi; fi=0;
  18.152 +}
  18.153 +
  18.154 +bool cMP3Decoder::Start(void)
  18.155 +{
  18.156 +  Lock(true);
  18.157 +  Init(); playing=true;
  18.158 +  if(str->Open() && scan->DoScan(true)) {
  18.159 +    if(!isStream) {
  18.160 +      str->Seek();
  18.161 +      framemax=scan->Frames+20;
  18.162 +      fi=new struct FrameInfo[framemax];
  18.163 +      if(!fi) esyslog("ERROR: no memory for frame index, rewinding disabled");
  18.164 +      }
  18.165 +    Unlock();
  18.166 +    return true;
  18.167 +    }
  18.168 +  str->Close();
  18.169 +  Clean();
  18.170 +  Unlock();
  18.171 +  return false;
  18.172 +}
  18.173 +
  18.174 +bool cMP3Decoder::Stop(void)
  18.175 +{
  18.176 +  Lock();
  18.177 +  if(playing) {
  18.178 +    str->Close();
  18.179 +    Clean();
  18.180 +    }
  18.181 +  Unlock();
  18.182 +  return true;
  18.183 +}
  18.184 +
  18.185 +struct Decode *cMP3Decoder::Done(eDecodeStatus status)
  18.186 +{
  18.187 +  ds.status=status;
  18.188 +  ds.index=mad_timer_count(playtime,MAD_UNITS_MILLISECONDS);
  18.189 +  ds.pcm=&synth->pcm;
  18.190 +  Unlock(); // release the lock from Decode()
  18.191 +  return &ds;
  18.192 +}
  18.193 +
  18.194 +eDecodeStatus cMP3Decoder::DecodeError(bool hdr)
  18.195 +{
  18.196 +  if(stream->error==MAD_ERROR_BUFLEN || stream->error==MAD_ERROR_BUFPTR) {
  18.197 +    int s=MadStream(stream,str);
  18.198 +    if(s<0) return dsError;
  18.199 +    if(s==0) return dsEof;
  18.200 +    }
  18.201 +  else if(!MAD_RECOVERABLE(stream->error)) {
  18.202 +    d(printf("mad: decode %sfailed, frame=%d: %s\n",hdr?"hdr ":"",framenum,mad_stream_errorstr(stream)))
  18.203 +    return dsError;
  18.204 +    }
  18.205 +  else { 
  18.206 +    if(stream->error==MAD_ERROR_LOSTSYNC) { // check for ID3 tags
  18.207 +#ifdef DEBUG
  18.208 +      char buf[10];
  18.209 +      int buf2[3];
  18.210 +      memcpy(buf,stream->this_frame,8); buf[8]=0;
  18.211 +      memcpy(buf2,stream->this_frame,8);
  18.212 +      printf("mad: lost sync %08x %08x %s\n",buf2[0],buf2[1],buf);
  18.213 +#endif
  18.214 +      id3_length_t count=stream->bufend-stream->this_frame;
  18.215 +      id3_length_t tagsize=id3_tag_query(stream->this_frame,count);
  18.216 +      if(tagsize>0) {
  18.217 +        d(printf("mad: skipping over ID3 tag\n"))
  18.218 +        if(count>tagsize) count=tagsize;
  18.219 +        mad_stream_skip(stream,count);
  18.220 +        while(count<tagsize) {
  18.221 +          unsigned char *sdata;
  18.222 +          unsigned long slen;
  18.223 +          if(!str->Stream(sdata,slen)) return dsError;
  18.224 +          if(slen<=0) return dsEof;
  18.225 +          unsigned long len=min(tagsize-count,slen);
  18.226 +          count+=len;
  18.227 +          sdata+=len; slen-=len;
  18.228 +          if(slen>0) mad_stream_buffer(stream,sdata,slen);
  18.229 +          }
  18.230 +        return dsOK;
  18.231 +        }
  18.232 +      }
  18.233 +    errcount+=hdr?1:100;
  18.234 +    d(printf("mad: decode %serror, frame=%d count=%d: %s\n",hdr?"hdr ":"",framenum,errcount,mad_stream_errorstr(stream)))
  18.235 +    }
  18.236 +  return dsOK;
  18.237 +}
  18.238 +
  18.239 +struct Decode *cMP3Decoder::Decode(void)
  18.240 +{
  18.241 +  Lock(); // this is released in Done()
  18.242 +  eDecodeStatus r;
  18.243 +  while(playing) {
  18.244 +    if(errcount>=MAX_FRAME_ERR*100) {
  18.245 +      esyslog("ERROR: excessive decoding errors, aborting file %s",filename);
  18.246 +      return Done(dsError);
  18.247 +      }
  18.248 +
  18.249 +    if(mad_header_decode(&frame->header,stream)<0) {
  18.250 +      if((r=DecodeError(true))) return Done(r);
  18.251 +      }
  18.252 +    else {
  18.253 +      if(!isStream) {
  18.254 +#ifdef DEBUG
  18.255 +        if(framenum>=framemax) printf("mp3: framenum >= framemax!!!!\n");
  18.256 +#endif
  18.257 +        if(fi && framenum<framemax) {
  18.258 +          fi[framenum].Pos=str->BufferPos() + (stream->this_frame-stream->buffer);
  18.259 +          fi[framenum].Time=playtime;
  18.260 +          }
  18.261 +        }
  18.262 +
  18.263 +      mad_timer_add(&playtime,frame->header.duration); framenum++;
  18.264 +
  18.265 +      if(mad_timer_compare(playtime,skiptime)>=0) skiptime=mad_timer_zero;
  18.266 +      else return Done(dsSkip);  // skipping, decode next header
  18.267 +
  18.268 +      if(mad_frame_decode(frame,stream)<0) {
  18.269 +        if((r=DecodeError(false))) return Done(r);
  18.270 +        }
  18.271 +      else {
  18.272 +        errcount=0;
  18.273 +        scan->InfoHook(&frame->header);
  18.274 +        mad_synth_frame(synth,frame);
  18.275 +        if(mute) { mute--; return Done(dsSkip); }
  18.276 +        return Done(dsPlay);
  18.277 +        }
  18.278 +      }
  18.279 +    }
  18.280 +  return Done(dsError);
  18.281 +}
  18.282 +
  18.283 +void cMP3Decoder::MakeSkipTime(mad_timer_t *skiptime, mad_timer_t playtime, int secs, float bsecs)
  18.284 +{
  18.285 +  mad_timer_t time;
  18.286 +  *skiptime=playtime;
  18.287 +  mad_timer_set(&time,abs(secs),0,0);
  18.288 +  if(secs<0) mad_timer_negate(&time);
  18.289 +  mad_timer_add(skiptime,time);
  18.290 +  int full=(int)bsecs; bsecs-=(float)full;
  18.291 +  mad_timer_set(&time,full,(int)(bsecs*1000.0),1000);
  18.292 +  mad_timer_negate(&time);
  18.293 +  mad_timer_add(skiptime,time);
  18.294 +  d(printf("mp3: skip: playtime=%ld secs=%d full=%d bsecs=%f skiptime=%ld\n",
  18.295 +           mad_timer_count(playtime,MAD_UNITS_MILLISECONDS),secs,full,bsecs,mad_timer_count(*skiptime,MAD_UNITS_MILLISECONDS)))
  18.296 +}
  18.297 +
  18.298 +bool cMP3Decoder::Skip(int Seconds, float bsecs)
  18.299 +{
  18.300 +  Lock();
  18.301 +  bool res=false;
  18.302 +  if(playing && !isStream) {
  18.303 +    if(!mad_timer_compare(skiptime,mad_timer_zero)) { // allow only one skip at any time
  18.304 +      mad_timer_t time;
  18.305 +      MakeSkipTime(&time,playtime,Seconds,bsecs);
  18.306 +
  18.307 +      if(mad_timer_compare(playtime,time)<=0) { // forward skip
  18.308 +#ifdef DEBUG
  18.309 +        int i=mad_timer_count(time,MAD_UNITS_SECONDS);
  18.310 +        printf("mp3: forward skipping to %02d:%02d\n",i/60,i%60);
  18.311 +#endif
  18.312 +        skiptime=time; mute=1;
  18.313 +        res=true;
  18.314 +        }
  18.315 +      else {                                    // backward skip
  18.316 +        if(fi) {
  18.317 +#ifdef DEBUG
  18.318 +          int i=mad_timer_count(time,MAD_UNITS_SECONDS);
  18.319 +          printf("mp3: rewinding to %02d:%02d\n",i/60,i%60);
  18.320 +#endif
  18.321 +          while(framenum && mad_timer_compare(time,fi[--framenum].Time)<0) ;
  18.322 +          mute=2; if(framenum>=2) framenum-=2;
  18.323 +          playtime=fi[framenum].Time;
  18.324 +          str->Seek(fi[framenum].Pos);
  18.325 +          mad_stream_finish(stream); // reset stream buffer
  18.326 +          mad_stream_init(stream);
  18.327 +#ifdef DEBUG
  18.328 +          i=mad_timer_count(playtime,MAD_UNITS_MILLISECONDS);
  18.329 +          printf("mp3: new playtime=%d framenum=%d filepos=%lld\n",i,framenum,fi[framenum].Pos);
  18.330 +#endif
  18.331 +          res=true;
  18.332 +          }
  18.333 +        }
  18.334 +      }
  18.335 +    }
  18.336 +  Unlock();
  18.337 +  return res;
  18.338 +}
  18.339 +
  18.340 +// --- cScanID3 ----------------------------------------------------------------
  18.341 +
  18.342 +// This function was adapted from mad_timer, from the 
  18.343 +// libmad distribution
  18.344 +
  18.345 +#define MIN_SCAN_FRAMES 200 // min. number of frames to scan
  18.346 +
  18.347 +cScanID3::cScanID3(cStream *Str, bool *Urgent)
  18.348 +{
  18.349 +  str=Str;
  18.350 +  urgent=Urgent;
  18.351 +}
  18.352 +
  18.353 +bool cScanID3::Abort(bool result)
  18.354 +{
  18.355 +  if(!keepOpen) str->Close();
  18.356 +  return result;
  18.357 +}
  18.358 +
  18.359 +bool cScanID3::DoScan(bool KeepOpen)
  18.360 +{
  18.361 +  mad_timer_t duration=mad_timer_zero;
  18.362 +  unsigned int bitrate=0, minrate=~0, maxrate=0;
  18.363 +  int xframes=0;
  18.364 +  unsigned int id3_vers=0;
  18.365 +  bool is_vbr=false, has_id3=false;
  18.366 +
  18.367 +  keepOpen=KeepOpen;
  18.368 +  if(!str->Open()) return Abort(false);
  18.369 +  if(HasInfo()) return Abort(true);
  18.370 +
  18.371 +  // check the infocache
  18.372 +  cCacheData *dat=InfoCache.Search(str);
  18.373 +  if(dat) {
  18.374 +    Set(dat); dat->Unlock();
  18.375 +    if(!DecoderID) {
  18.376 +      DecoderID=DEC_MP3;
  18.377 +      InfoCache.Cache(this,str);
  18.378 +      }
  18.379 +    return Abort(true);
  18.380 +    }
  18.381 +
  18.382 +  Clear();
  18.383 +
  18.384 +  // do a initial check for a ID3v1 tag at the end of the file
  18.385 +  // to speed up the following scan
  18.386 +  if(str->Filesize>=128 && str->Seek(str->Filesize-128)) {
  18.387 +    unsigned char *data;
  18.388 +    unsigned long len;
  18.389 +    if(str->Stream(data,len)) {
  18.390 +      struct id3_tag *tag=id3_tag_parse(data,len);
  18.391 +      if(tag) {
  18.392 +        d(printf("id3-scan: initialy found ID3 V1 tag at EOF\n"))
  18.393 +        ParseID3(tag);
  18.394 +        has_id3=true; id3_vers=tag->version;
  18.395 +        id3_tag_delete(tag);
  18.396 +        }
  18.397 +      }
  18.398 +    }
  18.399 +  if(!str->Seek()) return Abort(false);
  18.400 +
  18.401 +  // There are three ways of calculating the length of an mp3:
  18.402 +  // 1) Constant bitrate: One frame can provide the information
  18.403 +  //    needed: # of frames and duration. Just see how long it
  18.404 +  //    is and do the division.
  18.405 +  // 2) Variable bitrate: Xing tag. It provides the number of 
  18.406 +  //    frames. Each frame has the same number of samples, so
  18.407 +  //    just use that.
  18.408 +  // 3) All: Count up the frames and duration of each frames
  18.409 +  //    by decoding each one. We do this if we've no other
  18.410 +  //    choice, i.e. if it's a VBR file with no Xing tag.
  18.411 +
  18.412 +  struct mad_stream stream;
  18.413 +  struct mad_header header;
  18.414 +  mad_stream_init(&stream);
  18.415 +  mad_stream_options(&stream,MAD_OPTION_IGNORECRC);
  18.416 +  mad_header_init(&header);
  18.417 +  bool res=true;
  18.418 +  int errcount=0;
  18.419 +  while(1) {
  18.420 +    if(*urgent) {
  18.421 +      d(printf("id3-scan: urgent request, aborting!\n"))
  18.422 +      res=false; break; // abort scan if there is an urgent request for the decoder lock
  18.423 +      }
  18.424 +
  18.425 +    if(mad_header_decode(&header,&stream)<0) {
  18.426 +      if(stream.error==MAD_ERROR_BUFLEN || stream.error==MAD_ERROR_BUFPTR) {
  18.427 +        int s=MadStream(&stream,str);
  18.428 +        if(s>0) continue;
  18.429 +        if(s<0) res=false;
  18.430 +        break;
  18.431 +        }
  18.432 +      else if(stream.error==MAD_ERROR_LOSTSYNC) { // check for ID3 tags
  18.433 +#ifdef DEBUG
  18.434 +        char buf[10];
  18.435 +        int buf2[3];
  18.436 +        memcpy(buf,stream.this_frame,8); buf[8]=0;
  18.437 +        memcpy(buf2,stream.this_frame,8);
  18.438 +        printf("id3-scan: lost sync %08x %08x %s\n",buf2[0],buf2[1],buf);
  18.439 +#endif
  18.440 +        id3_length_t tagsize=id3_tag_query(stream.this_frame,stream.bufend-stream.this_frame);
  18.441 +        if(tagsize>0) {
  18.442 +	  struct id3_tag *tag=GetID3(&stream,tagsize);
  18.443 +	  if(tag) {
  18.444 +            unsigned int vers=id3_tag_version(tag);
  18.445 +            d(printf("id3-scan: found ID3 %s tag (%d.%d)\n",vers==0x100?"V1":"V2",ID3_TAG_VERSION_MAJOR(vers),ID3_TAG_VERSION_MINOR(vers)))
  18.446 +            if(!has_id3 || vers>id3_vers) {
  18.447 +              ParseID3(tag);
  18.448 +              has_id3=true; id3_vers=vers;
  18.449 +              }
  18.450 +	    id3_tag_delete(tag);
  18.451 +	    }
  18.452 +          }
  18.453 +        continue;
  18.454 +        }
  18.455 +      else {
  18.456 +        d(printf("id3-scan: decode header error (frame %d): %s\n",Frames,mad_stream_errorstr(&stream)))
  18.457 +        errcount++;
  18.458 +        if(errcount<MAX_FRAME_ERR*100 && MAD_RECOVERABLE(stream.error)) continue;
  18.459 +        res=false; break;
  18.460 +        }
  18.461 +      }
  18.462 +    errcount=0;
  18.463 +    if(header.bitrate>maxrate) maxrate=header.bitrate;
  18.464 +    if(header.bitrate<minrate) minrate=header.bitrate;
  18.465 +
  18.466 +    // Limit xing testing to the first frame header
  18.467 +    if(!Frames) {
  18.468 +      if((xframes=ParseXing(&stream.anc_ptr, stream.anc_bitlen))>=0) {
  18.469 +        is_vbr=true;
  18.470 +        }
  18.471 +      }                
  18.472 +    // Test the first n frames to see if this is a VBR file
  18.473 +    if(!is_vbr && Frames<MIN_SCAN_FRAMES) {
  18.474 +      if(bitrate && header.bitrate!=bitrate) is_vbr=true;
  18.475 +      else bitrate=header.bitrate;
  18.476 +      }
  18.477 +    // We have to assume it's not a VBR file if it hasn't already been
  18.478 +    // marked as one and we've checked n frames for different bitrates
  18.479 +    else if(!is_vbr && has_id3)
  18.480 +      {
  18.481 +      break;
  18.482 +      }
  18.483 +
  18.484 +    Frames++;
  18.485 +    mad_timer_add(&duration,header.duration);
  18.486 +    }
  18.487 +  mad_header_finish(&header);
  18.488 +  mad_stream_finish(&stream);
  18.489 +
  18.490 +  if(res) {
  18.491 +    d(printf("mad: scanned %d frames%s\n",Frames,Frames?"":"(is this really a mp3?)"))
  18.492 +    if(Frames) {
  18.493 +      SampleFreq=header.samplerate;
  18.494 +      Channels=MAD_NCHANNELS(&header);
  18.495 +      ChMode=header.mode;
  18.496 +      DecoderID=DEC_MP3;
  18.497 +      InfoDone();
  18.498 +
  18.499 +      if(!is_vbr) {
  18.500 +        d(printf("mad: constant birate\n"))
  18.501 +        double time = (str->Filesize * 8.0) / (header.bitrate);  // time in seconds
  18.502 +        long nsamples = 32 * MAD_NSBSAMPLES(&header);   // samples per frame
  18.503 +
  18.504 +        Frames = (long)(time * header.samplerate / nsamples);
  18.505 +        Total  = (long)time;
  18.506 +        Bitrate= (int)bitrate;
  18.507 +        }
  18.508 +      else if(xframes>0) {
  18.509 +        d(printf("mad: vbr, but has Xing frame\n"))
  18.510 +        mad_timer_multiply(&header.duration, xframes);
  18.511 +        Frames = xframes;
  18.512 +        Total  = mad_timer_count(header.duration,MAD_UNITS_SECONDS);
  18.513 +        }
  18.514 +      else {
  18.515 +        // the durations have been added up, and the number of frames counted. We do nothing here.
  18.516 +        d(printf("mad: vbr detected\n"))
  18.517 +        Total      = mad_timer_count(duration,MAD_UNITS_SECONDS);
  18.518 +        Bitrate    = (int)minrate;
  18.519 +        MaxBitrate = (int)maxrate;
  18.520 +        }
  18.521 +
  18.522 +      if(!has_id3 || !Title) FakeTitle(str->Filename,".mp3");
  18.523 +      InfoCache.Cache(this,str);
  18.524 +      }
  18.525 +    }
  18.526 +  return Abort(res);
  18.527 +}
  18.528 +
  18.529 +// This function was adapted from player.c, from the 
  18.530 +// libmad distribution
  18.531 +
  18.532 +struct id3_tag *cScanID3::GetID3(struct mad_stream *stream, id3_length_t tagsize) const
  18.533 +{
  18.534 +  struct id3_tag *tag=0;
  18.535 +  const id3_byte_t *data;
  18.536 +  id3_byte_t *allocated=0;
  18.537 +
  18.538 +  id3_length_t count=stream->bufend-stream->this_frame;
  18.539 +
  18.540 +  if(count>=tagsize) {
  18.541 +    data=stream->this_frame;
  18.542 +    mad_stream_skip(stream,tagsize);
  18.543 +    }
  18.544 +  else {
  18.545 +    if(!(allocated=(id3_byte_t *)malloc(tagsize))) {
  18.546 +      esyslog("ERROR: not enough memory for id3 tag buffer");
  18.547 +      return 0;
  18.548 +      }
  18.549 +    memcpy(allocated,stream->this_frame,count);
  18.550 +    mad_stream_skip(stream,count);
  18.551 +
  18.552 +    while(count<tagsize) {
  18.553 +      unsigned char *sdata;
  18.554 +      unsigned long len, slen;
  18.555 +
  18.556 +      if(!str->Stream(sdata,slen) || !slen) {
  18.557 +         d(printf("mad: error or eof on ID3 tag parse\n"))
  18.558 +         free(allocated);
  18.559 +         return 0;
  18.560 +         }
  18.561 +      len=tagsize-count; if(len>slen) len=slen;
  18.562 +      memcpy(allocated+count,sdata,len);
  18.563 +      count+=len;
  18.564 +      sdata+=len; slen-=len;
  18.565 +      if(slen) mad_stream_buffer(stream,sdata,slen);
  18.566 +      }
  18.567 +    data=allocated;
  18.568 +    }
  18.569 +
  18.570 +  tag=id3_tag_parse(data,tagsize);
  18.571 +  if(allocated) free(allocated);
  18.572 +  return tag;
  18.573 +}
  18.574 +
  18.575 +void cScanID3::ParseID3(const struct id3_tag *tag)
  18.576 +{
  18.577 +  d(printf("id3-scan: parsing ID3 tag\n"))
  18.578 +  ParseStr(tag,ID3_FRAME_TITLE,Title);
  18.579 +  ParseStr(tag,ID3_FRAME_ARTIST,Artist);
  18.580 +  ParseStr(tag,ID3_FRAME_ALBUM,Album);
  18.581 +  char *data=0;
  18.582 +  ParseStr(tag,ID3_FRAME_YEAR,data);
  18.583 +  if(data) Year=atol(data);
  18.584 +  free(data);
  18.585 +  //ParseStr(tag,ID3_FRAME_TRACK,Track);
  18.586 +  //ParseStr(tag,ID3_FRAME_GENRE,Genre);
  18.587 +}
  18.588 +
  18.589 +/*
  18.590 +void cScanID3::ParsePic(const struct id3_tag *tag, const char *id, char * &name)
  18.591 +{
  18.592 +  const struct id3_frame *frame=id3_tag_findframe(tag,id,0);
  18.593 +  if(frame) {
  18.594 +    id3_length_t len;
  18.595 +    const id3_byte_t *data=id3_field_getbinarydata(&frame->fields[1],&len);
  18.596 +    if(data && len>0) {
  18.597 +      static const char salt[] = { "$1$id3__pic$" };
  18.598 +      
  18.599 +      }
  18.600 +    }
  18.601 +}
  18.602 +*/
  18.603 +
  18.604 +// This function was adapted from player.c, from the 
  18.605 +// libmad distribution 
  18.606 +
  18.607 +void cScanID3::ParseStr(const struct id3_tag *tag, const char *id, char * &data)
  18.608 +{
  18.609 +  const struct id3_frame *frame=id3_tag_findframe(tag,id,0);
  18.610 +  if(!frame) return;
  18.611 +
  18.612 +  free(data); data=0;
  18.613 +  const union id3_field *field=&frame->fields[1];
  18.614 +  if(id3_field_getnstrings(field)>0) {
  18.615 +    const id3_ucs4_t *ucs4=id3_field_getstrings(field,0);
  18.616 +    if(!ucs4) return;
  18.617 +    if(!strcmp(id,ID3_FRAME_GENRE)) ucs4=id3_genre_name(ucs4);
  18.618 +
  18.619 +    id3_latin1_t *latin1=id3_ucs4_latin1duplicate(ucs4);
  18.620 +    if(!latin1) return;
  18.621 +
  18.622 +    data=strdup((char *)latin1);
  18.623 +    free(latin1);
  18.624 +    }
  18.625 +}
  18.626 +
  18.627 +// XING parsing was adapted from the MAD winamp input plugin,
  18.628 +// from the libmad distribution
  18.629 +
  18.630 +#define XING_MAGIC (('X'<<24) | ('i'<<16) | ('n'<<8) | 'g')
  18.631 +#define XING_FRAMES 0x0001
  18.632 +// #define XING_BYTES  0x0002
  18.633 +// #define XING_TOC    0x0004
  18.634 +// #define XING_SCALE  0x0008
  18.635 +
  18.636 +int cScanID3::ParseXing(struct mad_bitptr *ptr, unsigned int bitlen) const
  18.637 +{
  18.638 +  if(bitlen>=64 && mad_bit_read(ptr,32)==XING_MAGIC) {
  18.639 +    int flags=mad_bit_read(ptr, 32);
  18.640 +    bitlen-=64;
  18.641 +    return (bitlen>=32 && (flags & XING_FRAMES)) ? mad_bit_read(ptr,32) : 0;
  18.642 +    }
  18.643 +  return -1;
  18.644 +}
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/decoder-mp3.h	Sat Dec 29 14:47:40 2007 +0100
    19.3 @@ -0,0 +1,100 @@
    19.4 +/*
    19.5 + * MP3/MPlayer plugin to VDR (C++)
    19.6 + *
    19.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    19.8 + *
    19.9 + * This code is free software; you can redistribute it and/or
   19.10 + * modify it under the terms of the GNU General Public License
   19.11 + * as published by the Free Software Foundation; either version 2
   19.12 + * of the License, or (at your option) any later version.
   19.13 + *
   19.14 + * This code is distributed in the hope that it will be useful,
   19.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   19.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   19.17 + * GNU General Public License for more details.
   19.18 + *
   19.19 + * You should have received a copy of the GNU General Public License
   19.20 + * along with this program; if not, write to the Free Software
   19.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   19.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   19.23 + */
   19.24 +
   19.25 +#ifndef ___DECODER_MP3_H
   19.26 +#define ___DECODER_MP3_H
   19.27 +
   19.28 +#define DEC_MP3     DEC_ID('M','P','3',' ')
   19.29 +#define DEC_MP3_STR "MP3"
   19.30 +
   19.31 +#include <mad.h>
   19.32 +#include <id3tag.h>
   19.33 +
   19.34 +#include "decoder.h"
   19.35 +#include "decoder-core.h"
   19.36 +
   19.37 +#if MAD_F_FRACBITS != 28
   19.38 +#warning libmad with MAD_F_FRACBITS != 28 not tested
   19.39 +#endif
   19.40 +
   19.41 +class cStream;
   19.42 +
   19.43 +// ----------------------------------------------------------------
   19.44 +
   19.45 +class cScanID3 : public cSongInfo {
   19.46 +private:
   19.47 +  bool keepOpen, *urgent;
   19.48 +  //
   19.49 +  bool Abort(bool result);
   19.50 +  struct id3_tag *GetID3(struct mad_stream *stream, id3_length_t tagsize) const;
   19.51 +  void ParseID3(const struct id3_tag *tag);
   19.52 +  void ParseStr(const struct id3_tag *tag, const char *id, char * &data);
   19.53 +  void ParsePic(const struct id3_tag *tag, const char *id, char * &name);
   19.54 +  int ParseXing(struct mad_bitptr *ptr, unsigned int bitlen) const;
   19.55 +protected:
   19.56 +  cStream *str;
   19.57 +public:
   19.58 +  cScanID3(cStream *Str, bool *Urgent);
   19.59 +  virtual ~cScanID3() {}
   19.60 +  virtual bool DoScan(bool KeepOpen=false);
   19.61 +  virtual void InfoHook(struct mad_header *header) {}
   19.62 +  };
   19.63 +
   19.64 +// ----------------------------------------------------------------
   19.65 +
   19.66 +class cMP3Decoder : public cDecoder {
   19.67 +private:
   19.68 +  struct Decode ds;
   19.69 +  //
   19.70 +  struct mad_stream *stream;
   19.71 +  struct mad_frame *frame;
   19.72 +  struct mad_synth *synth;
   19.73 +  mad_timer_t playtime, skiptime;
   19.74 +  //
   19.75 +  struct FrameInfo {
   19.76 +    unsigned long long Pos;
   19.77 +    mad_timer_t Time;
   19.78 +    } *fi;
   19.79 +  int framenum, framemax, errcount, mute;
   19.80 +  //
   19.81 +  void Init(void);
   19.82 +  void Clean(void);
   19.83 +  struct Decode *Done(eDecodeStatus status);
   19.84 +  eDecodeStatus DecodeError(bool hdr);
   19.85 +  void MakeSkipTime(mad_timer_t *skiptime, mad_timer_t playtime, int secs, float bsecs);
   19.86 +protected:
   19.87 +  cStream *str;
   19.88 +  cScanID3 *scan;
   19.89 +  bool isStream;
   19.90 +public:
   19.91 +  cMP3Decoder(const char *Filename, bool preinit=true);
   19.92 +  virtual ~cMP3Decoder();
   19.93 +  virtual bool Valid(void);
   19.94 +  virtual cFileInfo *FileInfo(void);
   19.95 +  virtual cSongInfo *SongInfo(bool get);
   19.96 +  virtual cPlayInfo *PlayInfo(void);
   19.97 +  virtual bool Start(void);
   19.98 +  virtual bool Stop(void);
   19.99 +  virtual bool Skip(int Seconds, float bsecs);
  19.100 +  virtual struct Decode *Decode(void);
  19.101 +  };
  19.102 +
  19.103 +#endif //___DECODER_MP3_H
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/decoder-ogg.c	Sat Dec 29 14:47:40 2007 +0100
    20.3 @@ -0,0 +1,377 @@
    20.4 +/*
    20.5 + * MP3/MPlayer plugin to VDR (C++)
    20.6 + *
    20.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    20.8 + *
    20.9 + * This code is free software; you can redistribute it and/or
   20.10 + * modify it under the terms of the GNU General Public License
   20.11 + * as published by the Free Software Foundation; either version 2
   20.12 + * of the License, or (at your option) any later version.
   20.13 + *
   20.14 + * This code is distributed in the hope that it will be useful,
   20.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   20.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   20.17 + * GNU General Public License for more details.
   20.18 + *
   20.19 + * You should have received a copy of the GNU General Public License
   20.20 + * along with this program; if not, write to the Free Software
   20.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   20.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   20.23 + */
   20.24 +
   20.25 +#ifdef HAVE_VORBISFILE
   20.26 +
   20.27 +#include <stdlib.h>
   20.28 +#include <stdio.h>
   20.29 +#include <errno.h>
   20.30 +
   20.31 +#include "common.h"
   20.32 +#include "decoder-ogg.h"
   20.33 +
   20.34 +// --- cOggFile ----------------------------------------------------------------
   20.35 +
   20.36 +cOggFile::cOggFile(const char *Filename)
   20.37 +:cFileInfo(Filename)
   20.38 +{
   20.39 +  canSeek=opened=false;
   20.40 +}
   20.41 +
   20.42 +cOggFile::~cOggFile()
   20.43 +{
   20.44 +  Close();
   20.45 +}
   20.46 +
   20.47 +bool cOggFile::Open(bool log)
   20.48 +{
   20.49 +  if(opened) {
   20.50 +    if(canSeek) return (Seek()>=0);
   20.51 +    return true;
   20.52 +    }
   20.53 +
   20.54 +  if(FileInfo(log)) {
   20.55 +    FILE *f=fopen(Filename,"r");
   20.56 +    if(f) {
   20.57 +      int r=ov_open(f,&vf,0,0);
   20.58 +      if(!r) {
   20.59 +        canSeek=(ov_seekable(&vf)!=0);
   20.60 +        opened=true;
   20.61 +        }
   20.62 +      else {
   20.63 +        fclose(f);
   20.64 +        if(log) Error("open",r);
   20.65 +        }
   20.66 +      }
   20.67 +    else if(log) { esyslog("ERROR: failed to open file %s: %s",Filename,strerror(errno)); }
   20.68 +    }
   20.69 +  return opened;
   20.70 +}
   20.71 +
   20.72 +void cOggFile::Close(void)
   20.73 +{
   20.74 +  if(opened) { ov_clear(&vf); opened=false; }
   20.75 +}
   20.76 +
   20.77 +void cOggFile::Error(const char *action, const int err)
   20.78 +{
   20.79 +  char *errstr;
   20.80 +  switch(err) {
   20.81 +    case OV_FALSE:      errstr="false/no data available"; break;
   20.82 +    case OV_EOF:        errstr="EOF"; break;
   20.83 +    case OV_HOLE:       errstr="missing or corrupted data"; break;
   20.84 +    case OV_EREAD:      errstr="read error"; break;
   20.85 +    case OV_EFAULT:     errstr="internal error"; break;
   20.86 +    case OV_EIMPL:      errstr="unimplemented feature"; break;
   20.87 +    case OV_EINVAL:     errstr="invalid argument"; break;
   20.88 +    case OV_ENOTVORBIS: errstr="no Ogg Vorbis stream"; break;
   20.89 +    case OV_EBADHEADER: errstr="corrupted Ogg Vorbis stream"; break;
   20.90 +    case OV_EVERSION:   errstr="unsupported bitstream version"; break;
   20.91 +    case OV_ENOTAUDIO:  errstr="ENOTAUDIO"; break;
   20.92 +    case OV_EBADPACKET: errstr="EBADPACKET"; break;
   20.93 +    case OV_EBADLINK:   errstr="corrupted link"; break;
   20.94 +    case OV_ENOSEEK:    errstr="stream not seekable"; break;
   20.95 +    default:            errstr="unspecified error"; break;
   20.96 +    }
   20.97 +  esyslog("ERROR: vorbisfile %s failed on %s: %s",action,Filename,errstr);
   20.98 +}
   20.99 +
  20.100 +long long cOggFile::IndexMs(void)
  20.101 +{
  20.102 +  double p=ov_time_tell(&vf);
  20.103 +  if(p<0.0) p=0.0;
  20.104 +  return (long long)(p*1000.0);
  20.105 +}
  20.106 +
  20.107 +long long cOggFile::Seek(long long posMs, bool relativ)
  20.108 +{
  20.109 +  if(relativ) posMs+=IndexMs();
  20.110 +  int r=ov_time_seek(&vf,(double)posMs/1000.0);
  20.111 +  if(r) {
  20.112 +    Error("seek",r);
  20.113 +    return -1;
  20.114 +    }
  20.115 +  posMs=IndexMs();
  20.116 +  return posMs;
  20.117 +}
  20.118 +
  20.119 +int cOggFile::Stream(short *buffer, int samples)
  20.120 +{
  20.121 +  int n;
  20.122 +  do {
  20.123 +    int stream;
  20.124 +    n=ov_read(&vf,(char *)buffer,samples*2,0,2,1,&stream);
  20.125 +    } while(n==OV_HOLE);
  20.126 +  if(n<0) Error("read",n);
  20.127 +  return (n/2);
  20.128 +}
  20.129 +
  20.130 +// --- cOggInfo ----------------------------------------------------------------
  20.131 +
  20.132 +cOggInfo::cOggInfo(cOggFile *File)
  20.133 +{
  20.134 +  file=File;
  20.135 +}
  20.136 +
  20.137 +bool cOggInfo::Abort(bool result)
  20.138 +{
  20.139 +  if(!keepOpen) file->Close();
  20.140 +  return result;
  20.141 +}
  20.142 +
  20.143 +bool cOggInfo::DoScan(bool KeepOpen)
  20.144 +{
  20.145 +  keepOpen=KeepOpen;
  20.146 +  if(!file->Open()) return Abort(false);
  20.147 +  if(HasInfo()) return Abort(true);
  20.148 +
  20.149 +  // check the infocache
  20.150 +  cCacheData *dat=InfoCache.Search(file);
  20.151 +  if(dat) {
  20.152 +    Set(dat); dat->Unlock();
  20.153 +    if(!DecoderID) {
  20.154 +      DecoderID=DEC_OGG;
  20.155 +      InfoCache.Cache(this,file);
  20.156 +      }
  20.157 +    return Abort(true);
  20.158 +    }
  20.159 +
  20.160 +  Clear();
  20.161 +
  20.162 +  vorbis_comment *vc=ov_comment(&file->vf,-1);
  20.163 +  if(vc) {
  20.164 +    for(int i=0 ; i<vc->comments ; i++) {
  20.165 +      const char *cc=vc->user_comments[i];
  20.166 +      d(printf("ogg: comment%d='%s'\n",i,cc))
  20.167 +      char *p=strchr(cc,'=');
  20.168 +      if(p) {
  20.169 +        const int len=p-cc;
  20.170 +        p++;
  20.171 +        if(!strncasecmp(cc,"TITLE",len)) {
  20.172 +          if(!Title) Title=strdup(p);
  20.173 +          }
  20.174 +        else if(!strncasecmp(cc,"ARTIST",len)) {
  20.175 +          if(!Artist) Artist=strdup(p);
  20.176 +          }
  20.177 +        else if(!strncasecmp(cc,"ALBUM",len)) {
  20.178 +          if(!Album) Album=strdup(p);
  20.179 +          }
  20.180 +        else if(!strncasecmp(cc,"YEAR",len)) {
  20.181 +          if(Year<0) {
  20.182 +            Year=atoi(p);
  20.183 +            if(Year<1800 || Year>2100) Year=-1;
  20.184 +            }
  20.185 +          }
  20.186 +        }
  20.187 +      }
  20.188 +    }
  20.189 +  if(!Title) FakeTitle(file->Filename);
  20.190 +
  20.191 +  vorbis_info *vi=ov_info(&file->vf,-1);
  20.192 +  if(!vi) Abort(false);
  20.193 +  d(printf("ogg: info ch=%d srate=%ld brate_low=%ld brate_high=%ld brate_avg=%ld\n",
  20.194 +            vi->channels,vi->rate,vi->bitrate_lower,vi->bitrate_upper,vi->bitrate_nominal))
  20.195 +  Channels=vi->channels;
  20.196 +  ChMode=Channels>1 ? 3:0;
  20.197 +  SampleFreq=vi->rate;
  20.198 +  if(vi->bitrate_upper>0 && vi->bitrate_lower>0) {
  20.199 +    Bitrate=vi->bitrate_lower;
  20.200 +    MaxBitrate=vi->bitrate_upper;
  20.201 +    }
  20.202 +  else
  20.203 +    Bitrate=vi->bitrate_nominal;
  20.204 +
  20.205 +  Total=(int)ov_time_total(&file->vf,-1);
  20.206 +  Frames=-1;
  20.207 +  DecoderID=DEC_OGG;
  20.208 +
  20.209 +  InfoDone();
  20.210 +  InfoCache.Cache(this,file);
  20.211 +  return Abort(true);  
  20.212 +}
  20.213 +
  20.214 +// --- cOggDecoder -------------------------------------------------------------
  20.215 +
  20.216 +cOggDecoder::cOggDecoder(const char *Filename)
  20.217 +:cDecoder(Filename)
  20.218 +,file(Filename)
  20.219 +,info(&file)
  20.220 +{
  20.221 +  pcm=0;
  20.222 +}
  20.223 +
  20.224 +cOggDecoder::~cOggDecoder()
  20.225 +{
  20.226 +  Clean();
  20.227 +}
  20.228 +
  20.229 +bool cOggDecoder::Valid(void)
  20.230 +{
  20.231 +  bool res=false;
  20.232 +  if(TryLock()) {
  20.233 +    if(file.Open(false)) res=true;
  20.234 +    Unlock();
  20.235 +    }
  20.236 +  return res;
  20.237 +}
  20.238 +
  20.239 +cFileInfo *cOggDecoder::FileInfo(void)
  20.240 +{
  20.241 +  cFileInfo *fi=0;
  20.242 +  if(file.HasInfo()) fi=&file;
  20.243 +  else if(TryLock()){
  20.244 +    if(file.Open()) { fi=&file; file.Close(); }
  20.245 +    Unlock();
  20.246 +    }
  20.247 +  return fi;
  20.248 +}
  20.249 +
  20.250 +cSongInfo *cOggDecoder::SongInfo(bool get)
  20.251 +{
  20.252 +  cSongInfo *si=0;
  20.253 +  if(info.HasInfo()) si=&info;
  20.254 +  else if(get && TryLock()) {
  20.255 +    if(info.DoScan(false)) si=&info;
  20.256 +    Unlock();
  20.257 +    }
  20.258 +  return si;
  20.259 +}
  20.260 +
  20.261 +cPlayInfo *cOggDecoder::PlayInfo(void)
  20.262 +{
  20.263 +  if(playing) {
  20.264 +    pi.Index=index/1000;
  20.265 +    pi.Total=info.Total;
  20.266 +    return &pi;
  20.267 +    }
  20.268 +  return 0;
  20.269 +}
  20.270 +
  20.271 +void cOggDecoder::Init(void)
  20.272 +{
  20.273 +  Clean();
  20.274 +  pcm=new struct mad_pcm;
  20.275 +  index=0;
  20.276 +}
  20.277 +
  20.278 +bool cOggDecoder::Clean(void)
  20.279 +{
  20.280 +  playing=false;
  20.281 +  delete pcm; pcm=0;
  20.282 +  file.Close();
  20.283 +  return false;
  20.284 +}
  20.285 +
  20.286 +#define SF_SAMPLES (sizeof(pcm->samples[0])/sizeof(mad_fixed_t))
  20.287 +
  20.288 +bool cOggDecoder::Start(void)
  20.289 +{
  20.290 +  Lock(true);
  20.291 +  Init(); playing=true;
  20.292 +  if(file.Open() && info.DoScan(true)) {
  20.293 +    d(printf("ogg: open rate=%d channels=%d seek=%d\n",
  20.294 +             info.SampleFreq,info.Channels,file.CanSeek()))
  20.295 +    if(info.Channels<=2) {
  20.296 +      Unlock();
  20.297 +      return true;
  20.298 +      }
  20.299 +    else esyslog("ERROR: cannot play ogg file %s: more than 2 channels",filename);
  20.300 +    }
  20.301 +  Clean();
  20.302 +  Unlock();
  20.303 +  return false;
  20.304 +}
  20.305 +
  20.306 +bool cOggDecoder::Stop(void)
  20.307 +{
  20.308 +  Lock();
  20.309 +  if(playing) Clean();
  20.310 +  Unlock();
  20.311 +  return true;
  20.312 +}
  20.313 +
  20.314 +struct Decode *cOggDecoder::Done(eDecodeStatus status)
  20.315 +{
  20.316 +  ds.status=status;
  20.317 +  ds.index=index;
  20.318 +  ds.pcm=pcm;
  20.319 +  Unlock(); // release the lock from Decode()
  20.320 +  return &ds;
  20.321 +}
  20.322 +
  20.323 +struct Decode *cOggDecoder::Decode(void)
  20.324 +{
  20.325 +  Lock(); // this is released in Done()
  20.326 +  if(playing) {
  20.327 +    short framebuff[2*SF_SAMPLES];
  20.328 +    int n=file.Stream(framebuff,SF_SAMPLES);
  20.329 +    if(n<0) return Done(dsError);
  20.330 +    if(n==0) return Done(dsEof);
  20.331 +
  20.332 +    pcm->samplerate=info.SampleFreq;
  20.333 +    pcm->channels=info.Channels;
  20.334 +    n/=pcm->channels;
  20.335 +    pcm->length=n;
  20.336 +    index=file.IndexMs();
  20.337 +
  20.338 +    short *data=framebuff;
  20.339 +    mad_fixed_t *sam0=pcm->samples[0], *sam1=pcm->samples[1]; 
  20.340 +    const int s=MAD_F_FRACBITS+1-(sizeof(short)*8); // shift value for mad_fixed conversion
  20.341 +    if(pcm->channels>1) {
  20.342 +      for(; n>0 ; n--) {
  20.343 +        *sam0++=(*data++) << s;
  20.344 +        *sam1++=(*data++) << s;
  20.345 +        }
  20.346 +      }
  20.347 +    else {
  20.348 +      for(; n>0 ; n--)
  20.349 +        *sam0++=(*data++) << s;
  20.350 +      }
  20.351 +    return Done(dsPlay);
  20.352 +    }
  20.353 +  return Done(dsError);
  20.354 +}
  20.355 +
  20.356 +bool cOggDecoder::Skip(int Seconds, float bsecs)
  20.357 +{
  20.358 +  Lock();
  20.359 +  bool res=false;
  20.360 +  if(playing && file.CanSeek()) {
  20.361 +    float fsecs=(float)Seconds - bsecs;
  20.362 +    long long newpos=file.IndexMs()+(long long)(fsecs*1000.0);
  20.363 +    if(newpos<0) newpos=0;
  20.364 +    d(printf("ogg: skip: secs=%d fsecs=%f current=%lld new=%lld\n",Seconds,fsecs,file.IndexMs(),newpos))
  20.365 +
  20.366 +    newpos=file.Seek(newpos,false);
  20.367 +    if(newpos>=0) {
  20.368 +      index=file.IndexMs();
  20.369 +#ifdef DEBUG
  20.370 +      int i=index/1000;
  20.371 +      printf("ogg: skipping to %02d:%02d\n",i/60,i%60);
  20.372 +#endif
  20.373 +      res=true;
  20.374 +      }
  20.375 +    }
  20.376 +  Unlock();
  20.377 +  return res;
  20.378 +}
  20.379 +
  20.380 +#endif //HAVE_VORBISFILE
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/decoder-ogg.h	Sat Dec 29 14:47:40 2007 +0100
    21.3 @@ -0,0 +1,101 @@
    21.4 +/*
    21.5 + * MP3/MPlayer plugin to VDR (C++)
    21.6 + *
    21.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    21.8 + *
    21.9 + * This code is free software; you can redistribute it and/or
   21.10 + * modify it under the terms of the GNU General Public License
   21.11 + * as published by the Free Software Foundation; either version 2
   21.12 + * of the License, or (at your option) any later version.
   21.13 + *
   21.14 + * This code is distributed in the hope that it will be useful,
   21.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   21.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   21.17 + * GNU General Public License for more details.
   21.18 + *
   21.19 + * You should have received a copy of the GNU General Public License
   21.20 + * along with this program; if not, write to the Free Software
   21.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   21.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   21.23 + */
   21.24 +
   21.25 +#ifndef ___DECODER_OGG_H
   21.26 +#define ___DECODER_OGG_H
   21.27 +
   21.28 +#define DEC_OGG     DEC_ID('O','G','G',' ')
   21.29 +#define DEC_OGG_STR "OGG"
   21.30 +
   21.31 +#ifdef HAVE_VORBISFILE
   21.32 +
   21.33 +#include <mad.h>
   21.34 +#include <vorbis/vorbisfile.h>
   21.35 +
   21.36 +#include "decoder.h"
   21.37 +#include "decoder-core.h"
   21.38 +
   21.39 +// ----------------------------------------------------------------
   21.40 +
   21.41 +class cOggInfo;
   21.42 +
   21.43 +class cOggFile : public cFileInfo {
   21.44 +friend class cOggInfo;
   21.45 +private:
   21.46 +  bool opened, canSeek;
   21.47 +  OggVorbis_File vf;
   21.48 +  //
   21.49 +  void Error(const char *action, const int err);
   21.50 +public:
   21.51 +  cOggFile(const char *Filename);
   21.52 +  ~cOggFile();
   21.53 +  bool Open(bool log=true);
   21.54 +  void Close(void);
   21.55 +  long long Seek(long long posMs=0, bool relativ=false);
   21.56 +  int Stream(short *buffer, int samples);
   21.57 +  bool CanSeek(void) { return canSeek; }
   21.58 +  long long IndexMs(void);
   21.59 +  };
   21.60 +
   21.61 +// ----------------------------------------------------------------
   21.62 +
   21.63 +class cOggInfo : public cSongInfo {
   21.64 +private:
   21.65 +  cOggFile *file;
   21.66 +  bool keepOpen;
   21.67 +  //
   21.68 +  bool Abort(bool result);
   21.69 +public:
   21.70 +  cOggInfo(cOggFile *File);
   21.71 +  bool DoScan(bool KeepOpen=false);
   21.72 +  };
   21.73 +
   21.74 +// ----------------------------------------------------------------
   21.75 +
   21.76 +class cOggDecoder : public cDecoder {
   21.77 +private:
   21.78 +  cOggFile file;
   21.79 +  cOggInfo info;
   21.80 +  struct Decode ds;
   21.81 +  struct mad_pcm *pcm;
   21.82 +  unsigned long long index;
   21.83 +  //
   21.84 +  void Init(void);
   21.85 +  bool Clean(void);
   21.86 +  bool GetInfo(bool keepOpen);
   21.87 +  struct Decode *Done(eDecodeStatus status);
   21.88 +public:
   21.89 +  cOggDecoder(const char *Filename);
   21.90 +  ~cOggDecoder();
   21.91 +  virtual bool Valid(void);
   21.92 +  virtual cFileInfo *FileInfo(void);
   21.93 +  virtual cSongInfo *SongInfo(bool get);
   21.94 +  virtual cPlayInfo *PlayInfo(void);
   21.95 +  virtual bool Start(void);
   21.96 +  virtual bool Stop(void);
   21.97 +  virtual bool Skip(int Seconds, float bsecs);
   21.98 +  virtual struct Decode *Decode(void);
   21.99 +  };
  21.100 +
  21.101 +// ----------------------------------------------------------------
  21.102 +
  21.103 +#endif //HAVE_VORBISFILE
  21.104 +#endif //___DECODER_OGG_H
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/decoder-snd.c	Sat Dec 29 14:47:40 2007 +0100
    22.3 @@ -0,0 +1,945 @@
    22.4 +/*
    22.5 + * MP3/MPlayer plugin to VDR (C++)
    22.6 + *
    22.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    22.8 + *
    22.9 + * This code is free software; you can redistribute it and/or
   22.10 + * modify it under the terms of the GNU General Public License
   22.11 + * as published by the Free Software Foundation; either version 2
   22.12 + * of the License, or (at your option) any later version.
   22.13 + *
   22.14 + * This code is distributed in the hope that it will be useful,
   22.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   22.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   22.17 + * GNU General Public License for more details.
   22.18 + *
   22.19 + * You should have received a copy of the GNU General Public License
   22.20 + * along with this program; if not, write to the Free Software
   22.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   22.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   22.23 + */
   22.24 +
   22.25 +#ifdef TEST_MAIN
   22.26 +#define HAVE_SNDFILE
   22.27 +#define REMOTE_LIRC
   22.28 +#define _GNU_SOURCE
   22.29 +#define DEBUG
   22.30 +#endif
   22.31 +
   22.32 +#ifdef HAVE_SNDFILE
   22.33 +
   22.34 +#include <ctype.h>
   22.35 +#include <stdlib.h>
   22.36 +#include <stdio.h>
   22.37 +#include <stdarg.h>
   22.38 +#include <errno.h>
   22.39 +#include <sys/types.h>
   22.40 +#include <unistd.h>
   22.41 +#include <math.h>
   22.42 +
   22.43 +#include "common.h"
   22.44 +#include "setup-mp3.h"
   22.45 +#include "decoder-snd.h"
   22.46 +#include "data.h"
   22.47 +#include "network.h"
   22.48 +#include "menu-async.h"
   22.49 +#include "i18n.h"
   22.50 +#include "version.h"
   22.51 +
   22.52 +#ifndef SNDFILE_1
   22.53 +#error You must use libsndfile version 1.x.x
   22.54 +#endif
   22.55 +
   22.56 +#define CDFS_TRACK_OFF 150
   22.57 +#define CDFS_PROC      "/proc/cdfs"
   22.58 +#define CDFS_MARK_ID   "CD (discid=%x) contains %d tracks:"
   22.59 +#define CDFS_MARK_TR   "%*[^[][ %d - %d"
   22.60 +#define CDFS_TRACK     "track-"
   22.61 +
   22.62 +#define CDDB_PROTO 5                   // used protocol level
   22.63 +#define CDDB_TOUT  30*1000             // connection timeout (ms)
   22.64 +
   22.65 +const char *cddbpath="/var/lib/cddb";  // default local cddb path
   22.66 +
   22.67 +#define CDDB_DEBUG  // debug cddb queries
   22.68 +//#define DEBUG_CDFS  // debug cdfs parsing
   22.69 +//#define GUARD_DEBUG // enable framebuffer guard
   22.70 +
   22.71 +#if !defined(NO_DEBUG) && defined(DEBUG_CDFS)
   22.72 +#define dc(x) { (x); }
   22.73 +#else
   22.74 +#define dc(x) ; 
   22.75 +#endif
   22.76 +
   22.77 +#ifndef TEST_MAIN
   22.78 +
   22.79 +// --- cSndDecoder -------------------------------------------------------------
   22.80 +
   22.81 +#define SF_SAMPLES (sizeof(pcm->samples[0])/sizeof(mad_fixed_t))
   22.82 +
   22.83 +cSndDecoder::cSndDecoder(const char *Filename)
   22.84 +:cDecoder(Filename)
   22.85 +,file(Filename)
   22.86 +,info(&file)
   22.87 +{
   22.88 +  pcm=0; framebuff=0; playing=ready=false;
   22.89 +}
   22.90 +
   22.91 +cSndDecoder::~cSndDecoder()
   22.92 +{
   22.93 +  Clean();
   22.94 +}
   22.95 +
   22.96 +bool cSndDecoder::Valid(void)
   22.97 +{
   22.98 +  bool res=false;
   22.99 +  if(TryLock()) {
  22.100 +    if(file.Open(false)) res=true;
  22.101 +    cDecoder::Unlock();
  22.102 +    }
  22.103 +  return res;
  22.104 +}
  22.105 +
  22.106 +cFileInfo *cSndDecoder::FileInfo(void)
  22.107 +{
  22.108 +  cFileInfo *fi=0;
  22.109 +  if(file.HasInfo()) fi=&file;
  22.110 +  else if(TryLock()){
  22.111 +    if(file.Open()) { fi=&file; file.Close(); }
  22.112 +    cDecoder::Unlock();
  22.113 +    }
  22.114 +  return fi;
  22.115 +}
  22.116 +
  22.117 +cSongInfo *cSndDecoder::SongInfo(bool get)
  22.118 +{
  22.119 +  cSongInfo *si=0;
  22.120 +  if(info.HasInfo()) si=&info;
  22.121 +  else if(get && TryLock()) {
  22.122 +    if(info.DoScan(false)) si=&info;
  22.123 +    cDecoder::Unlock();
  22.124 +    }
  22.125 +  return si;
  22.126 +}
  22.127 +
  22.128 +cPlayInfo *cSndDecoder::PlayInfo(void)
  22.129 +{
  22.130 +  if(playing) {
  22.131 +    pi.Index=index/info.SampleFreq;
  22.132 +    pi.Total=info.Total;
  22.133 +    return &pi;
  22.134 +    }
  22.135 +  return 0;
  22.136 +}
  22.137 +
  22.138 +void cSndDecoder::Init(void)
  22.139 +{
  22.140 +  Clean();
  22.141 +  pcm=new struct mad_pcm;
  22.142 +  framebuff=MALLOC(int,2*SF_SAMPLES+8);
  22.143 +#ifdef GUARD_DEBUG
  22.144 +  for(int i=0; i<8; i++) framebuff[i+(SF_SAMPLES*2)-4]=0xdeadbeaf;
  22.145 +#endif
  22.146 +  index=0;
  22.147 +}
  22.148 +
  22.149 +bool cSndDecoder::Clean(void)
  22.150 +{
  22.151 +  playing=false;
  22.152 +
  22.153 +  buffMutex.Lock();
  22.154 +  run=false; bgCond.Broadcast();
  22.155 +  buffMutex.Unlock();
  22.156 +  cThread::Cancel(3);
  22.157 +
  22.158 +  buffMutex.Lock();
  22.159 +  if(!ready) { deferedN=-1; ready=true; }
  22.160 +  fgCond.Broadcast();
  22.161 +  buffMutex.Unlock();
  22.162 +
  22.163 +  delete pcm; pcm=0;
  22.164 +#ifdef GUARD_DEBUG
  22.165 +  if(framebuff) {
  22.166 +    printf("snd: bufferguard");
  22.167 +    for(int i=0; i<8; i++) printf(" %08x",framebuff[i+(SF_SAMPLES*2)-4]);
  22.168 +    printf("\n");
  22.169 +    }
  22.170 +#endif
  22.171 +  free(framebuff); framebuff=0;
  22.172 +  file.Close();
  22.173 +  return false;
  22.174 +}
  22.175 +
  22.176 +bool cSndDecoder::Start(void)
  22.177 +{
  22.178 +  cDecoder::Lock(true);
  22.179 +  Init(); playing=true;
  22.180 +  if(file.Open() && info.DoScan(true)) {
  22.181 +    d(printf("snd: open rate=%d frames=%lld channels=%d format=0x%x seek=%d\n",
  22.182 +             file.sfi.samplerate,file.sfi.frames,file.sfi.channels,file.sfi.format,file.sfi.seekable))
  22.183 +    if(file.sfi.channels<=2) {
  22.184 +      ready=false; run=true; softCount=0;
  22.185 +      cThread::Start();
  22.186 +      cDecoder::Unlock();
  22.187 +      return true;
  22.188 +      }
  22.189 +    else esyslog("ERROR: cannot play sound file %s: more than 2 channels",filename);
  22.190 +    }
  22.191 +  cDecoder::Unlock();
  22.192 +  return Clean();
  22.193 +}
  22.194 +
  22.195 +bool cSndDecoder::Stop(void)
  22.196 +{
  22.197 +  cDecoder::Lock();
  22.198 +  if(playing) Clean();
  22.199 +  cDecoder::Unlock();
  22.200 +  return true;
  22.201 +}
  22.202 +
  22.203 +void cSndDecoder::Action(void)
  22.204 +{
  22.205 +  buffMutex.Lock();
  22.206 +  while(run) {
  22.207 +    if(ready) bgCond.Wait(buffMutex);
  22.208 +    if(!ready) {
  22.209 +      buffMutex.Unlock();
  22.210 +      deferedN=file.Stream(framebuff,SF_SAMPLES);
  22.211 +      buffMutex.Lock();
  22.212 +      ready=true; fgCond.Broadcast();
  22.213 +      }
  22.214 +    }
  22.215 +  buffMutex.Unlock();
  22.216 +}
  22.217 +
  22.218 +struct Decode *cSndDecoder::Done(eDecodeStatus status)
  22.219 +{
  22.220 +  ds.status=status;
  22.221 +  ds.index=index*1000/info.SampleFreq;
  22.222 +  ds.pcm=pcm;
  22.223 +  cDecoder::Unlock(); // release the lock from Decode()
  22.224 +  return &ds;
  22.225 +}
  22.226 +
  22.227 +struct Decode *cSndDecoder::Decode(void)
  22.228 +{
  22.229 +  cDecoder::Lock(); // this is released in Done()
  22.230 +  if(playing) {
  22.231 +    cMutexLock lock(&buffMutex);
  22.232 +    while(!ready)
  22.233 +      if(!softCount || !fgCond.TimedWait(buffMutex,softCount*5)) {
  22.234 +        if(softCount<20) softCount++;
  22.235 +        return Done(dsSoftError);
  22.236 +        }
  22.237 +    softCount=0;
  22.238 +    ready=false; bgCond.Broadcast();
  22.239 +
  22.240 +    int n=deferedN;
  22.241 +    if(n<0) return Done(dsError);
  22.242 +    if(n==0) return Done(dsEof);
  22.243 +
  22.244 +    pcm->samplerate=file.sfi.samplerate;
  22.245 +    pcm->channels=file.sfi.channels;
  22.246 +    pcm->length=n;
  22.247 +    index+=n;
  22.248 +
  22.249 +    int *data=framebuff;
  22.250 +    mad_fixed_t *sam0=pcm->samples[0], *sam1=pcm->samples[1]; 
  22.251 +    const int s=(sizeof(int)*8)-1-MAD_F_FRACBITS; // shift value for mad_fixed conversion
  22.252 +    if(pcm->channels>1) {
  22.253 +      for(; n>0 ; n--) {
  22.254 +        *sam0++=(*data++) >> s;
  22.255 +        *sam1++=(*data++) >> s;
  22.256 +        }
  22.257 +      }
  22.258 +    else {
  22.259 +      for(; n>0 ; n--)
  22.260 +        *sam0++=(*data++) >> s;
  22.261 +      }
  22.262 +    return Done(dsPlay);
  22.263 +    }
  22.264 +  return Done(dsError);
  22.265 +}
  22.266 +
  22.267 +bool cSndDecoder::Skip(int Seconds, float bsecs)
  22.268 +{
  22.269 +  cDecoder::Lock();
  22.270 +  bool res=false;
  22.271 +  if(playing && file.sfi.seekable) {
  22.272 +    float fsecs=(float)Seconds-bsecs;
  22.273 +    sf_count_t frames=(sf_count_t)(fsecs*(float)file.sfi.samplerate);
  22.274 +    sf_count_t newpos=file.Seek(0,true)+frames;
  22.275 +    if(newpos>file.sfi.frames) newpos=file.sfi.frames-1;
  22.276 +    if(newpos<0) newpos=0;
  22.277 +    d(printf("snd: skip: secs=%d fsecs=%f frames=%lld current=%lld new=%lld\n",Seconds,fsecs,frames,file.Seek(0,true),newpos))
  22.278 +
  22.279 +    buffMutex.Lock();
  22.280 +    frames=file.Seek(newpos,false);
  22.281 +    ready=false; bgCond.Broadcast();
  22.282 +    buffMutex.Unlock();
  22.283 +    if(frames>=0) {
  22.284 +      index=frames;
  22.285 +#ifdef DEBUG
  22.286 +      int i=frames/file.sfi.samplerate;
  22.287 +      printf("snd: skipping to %02d:%02d (frame %lld)\n",i/60,i%60,frames);
  22.288 +#endif
  22.289 +      res=true;
  22.290 +      }
  22.291 +    }
  22.292 +  cDecoder::Unlock();
  22.293 +  return res;
  22.294 +}
  22.295 +
  22.296 +#endif //TEST_MAIN
  22.297 +
  22.298 +// --- cDiscID -------------------------------------------------------------------
  22.299 +
  22.300 +class cDiscID {
  22.301 +public:
  22.302 +  int discid, ntrks, nsecs;
  22.303 +  int *offsets;
  22.304 +  //
  22.305 +  cDiscID(void);
  22.306 +  ~cDiscID();
  22.307 +  bool Get(void);
  22.308 +  };
  22.309 +
  22.310 +cDiscID::cDiscID(void)
  22.311 +{
  22.312 +  offsets=0; discid=ntrks=0;
  22.313 +}
  22.314 +
  22.315 +cDiscID::~cDiscID()
  22.316 +{
  22.317 +  delete offsets;
  22.318 +}
  22.319 +
  22.320 +bool cDiscID::Get(void)
  22.321 +{
  22.322 +  bool res=false;
  22.323 +  FILE *f=fopen(CDFS_PROC,"r");
  22.324 +  if(f) {
  22.325 +    char line[256];
  22.326 +    bool state=false;
  22.327 +    int tr=0;
  22.328 +    while(fgets(line,sizeof(line),f)) {
  22.329 +      if(!state) {
  22.330 +        int id, n;
  22.331 +        if(sscanf(line,CDFS_MARK_ID,&id,&n)==2) {
  22.332 +          d(printf("discid: found id=%08x n=%d\n",id,n))
  22.333 +          if(discid==id && ntrks==n) {
  22.334 +            res=true;
  22.335 +            break;
  22.336 +            }
  22.337 +          else {
  22.338 +            discid=id; ntrks=n;
  22.339 +            delete offsets; offsets=new int[ntrks];
  22.340 +            state=true;
  22.341 +            }
  22.342 +          }
  22.343 +        }
  22.344 +      else {
  22.345 +        int off, end;
  22.346 +        if(sscanf(line,CDFS_MARK_TR,&off,&end)==2) {
  22.347 +          dc(printf("discid: found offset=%d end=%d for track %d\n",off,end,tr+1))
  22.348 +          offsets[tr]=off+CDFS_TRACK_OFF;
  22.349 +          if(++tr==ntrks) {
  22.350 +            nsecs=(end+1)/75;
  22.351 +            dc(printf("discid: nsecs=%d / 0x%x\n",nsecs,nsecs))
  22.352 +            res=true;
  22.353 +            break;
  22.354 +            }
  22.355 +          }
  22.356 +        }
  22.357 +      }
  22.358 +    fclose(f);
  22.359 +    }
  22.360 +  return res;
  22.361 +}
  22.362 +
  22.363 +// --- cCDDBSong ---------------------------------------------------------------
  22.364 +
  22.365 +// The CDDB code is loosely based on the implementation in mp3c 0.27 which is
  22.366 +// (C) 1999-2001 WSPse, Matthias Hensler, <matthias@wspse.de>
  22.367 +
  22.368 +class cCDDBSong : public cListObject {
  22.369 +public:
  22.370 +  cCDDBSong(void);
  22.371 +  ~cCDDBSong();
  22.372 +  //
  22.373 +  int Track;
  22.374 +  char *TTitle, *ExtT;
  22.375 +  char *Title, *Artist;
  22.376 +  };
  22.377 +
  22.378 +cCDDBSong::cCDDBSong(void)
  22.379 +{
  22.380 +  Title=Artist=0; TTitle=ExtT=0;
  22.381 +}
  22.382 +
  22.383 +cCDDBSong::~cCDDBSong()
  22.384 +{
  22.385 +  free(Title);
  22.386 +  free(Artist);
  22.387 +  free(TTitle);
  22.388 +  free(ExtT);
  22.389 +}
  22.390 +
  22.391 +// --- cCDDBDisc ---------------------------------------------------------------
  22.392 +
  22.393 +const char *sampler[] = { // some artist names to identify sampler discs
  22.394 +  "various",
  22.395 +  "varios",
  22.396 +  "variété",
  22.397 +  "compilation",
  22.398 +  "sampler",
  22.399 +  "mixed",
  22.400 +  "divers",
  22.401 +  "v.a.",
  22.402 +  "VA",
  22.403 +  "misc",
  22.404 +  "none",
  22.405 +  0 };
  22.406 +
  22.407 +class cCDDBDisc : public cList<cCDDBSong> {
  22.408 +private:
  22.409 +  int DiscID;
  22.410 +  //
  22.411 +  bool isSampler;
  22.412 +  char *DTitle, *ExtD;
  22.413 +  //
  22.414 +  cCDDBSong *GetTrack(const char *name, unsigned int pos);
  22.415 +  cCDDBSong *FindTrack(int tr);
  22.416 +  void Strcat(char * &store, char *value);
  22.417 +  bool Split(const char *source, char div, char * &first, char * &second, bool only3=false);
  22.418 +  void Put(const char *from, char * &to);
  22.419 +  void Clean(void);
  22.420 +public:
  22.421 +  cCDDBDisc(void);
  22.422 +  ~cCDDBDisc();
  22.423 +  bool Load(cDiscID *id, const char *filename);
  22.424 +  bool Cached(cDiscID *id) { return DiscID==id->discid; }
  22.425 +  bool TrackInfo(int tr, cSongInfo *si);
  22.426 +  //
  22.427 +  char *Album, *Artist;
  22.428 +  int Year;
  22.429 +  };
  22.430 +
  22.431 +cCDDBDisc::cCDDBDisc(void)
  22.432 +{
  22.433 +  Album=Artist=0; DTitle=ExtD=0; DiscID=0;
  22.434 +}
  22.435 +
  22.436 +cCDDBDisc::~cCDDBDisc()
  22.437 +{
  22.438 +  Clean();
  22.439 +}
  22.440 +
  22.441 +void cCDDBDisc::Clean(void)
  22.442 +{
  22.443 +  free(DTitle); DTitle=0;
  22.444 +  free(ExtD); ExtD=0;
  22.445 +  free(Artist); Artist=0;
  22.446 +  free(Album); Album=0;
  22.447 +  Year=-1; DiscID=0; isSampler=false;
  22.448 +}
  22.449 +
  22.450 +bool cCDDBDisc::TrackInfo(int tr, cSongInfo *si)
  22.451 +{
  22.452 +  cCDDBSong *s=FindTrack(tr);
  22.453 +  if(s) {
  22.454 +    Put(s->Title,si->Title);
  22.455 +    if(s->Artist) Put(s->Artist,si->Artist); else Put(Artist,si->Artist);
  22.456 +    Put(Album,si->Album);
  22.457 +    if(Year>0) si->Year=Year;
  22.458 +    return true;
  22.459 +    }
  22.460 +  return false;
  22.461 +}
  22.462 +
  22.463 +void cCDDBDisc::Put(const char *from, char * &to)
  22.464 +{
  22.465 +  free(to);
  22.466 +  to=from ? strdup(from):0;
  22.467 +}
  22.468 +
  22.469 +bool cCDDBDisc::Load(cDiscID *id, const char *filename)
  22.470 +{
  22.471 +  char *p;
  22.472 +  Clean(); Clear();
  22.473 +
  22.474 +  d(printf("cddb: loading discid %08x from %s\n",id->discid,filename))
  22.475 +  DiscID=id->discid;
  22.476 +  FILE *f=fopen(filename,"r");
  22.477 +  if(f) {
  22.478 +    char buff[1024];
  22.479 +    while(fgets(buff,sizeof(buff),f)) {
  22.480 +      int i=strlen(buff);
  22.481 +      while(i && (buff[i-1]=='\n' || buff[i-1]=='\r')) buff[--i]=0;
  22.482 +
  22.483 +      if(buff[0]=='#') { // special comment line handling
  22.484 +        }
  22.485 +      else {
  22.486 +        p=strchr(buff,'=');
  22.487 +        if(p) {
  22.488 +           *p=0;
  22.489 +           char *name =compactspace(buff);
  22.490 +           char *value=compactspace(p+1);
  22.491 +           if(*name && *value) {
  22.492 +             if(!strcasecmp(name,"DTITLE")) Strcat(DTitle,value);
  22.493 +             else if(!strcasecmp(name,"EXTD")) Strcat(ExtD,value);
  22.494 +             else if(!strcasecmp(name,"DYEAR")) Year=atoi(value);
  22.495 +             else if(!strncasecmp(name,"TTITLE",6)) {
  22.496 +               cCDDBSong *s=GetTrack(name,6);
  22.497 +               if(s) Strcat(s->TTitle,value);
  22.498 +               }
  22.499 +             else if(!strncasecmp(name,"EXTT",4)) {
  22.500 +               cCDDBSong *s=GetTrack(name,4);
  22.501 +               if(s) Strcat(s->ExtT,value);
  22.502 +               }
  22.503 +             }
  22.504 +           }
  22.505 +        }
  22.506 +      }
  22.507 +    fclose(f);
  22.508 +
  22.509 +    // read all data, now post-processing
  22.510 +    if(Count()>0) {
  22.511 +      if(DTitle) {
  22.512 +        if(Split(DTitle,'/',Artist,Album)) {
  22.513 +          for(int n=0 ; sampler[n] ; n++)
  22.514 +            if(!strncasecmp(Artist,sampler[n],strlen(sampler[n]))) {
  22.515 +              isSampler=true;
  22.516 +              break;
  22.517 +              }
  22.518 +          }
  22.519 +        else {
  22.520 +          Album=strdup(DTitle);
  22.521 +          isSampler=true;
  22.522 +          }
  22.523 +        }
  22.524 +      d(printf("cddb: found artist='%s' album='%s' isSampler=%d\n",Artist,Album,isSampler))
  22.525 +      free(DTitle); DTitle=0;
  22.526 +
  22.527 +      if(!isSampler && Artist && Album && !strncmp(Album,Artist,strlen(Artist))) {
  22.528 +        d(printf("cddb: detecting sampler from Artist==Album\n"))
  22.529 +        isSampler=true;
  22.530 +        }
  22.531 +
  22.532 +      if(!isSampler) {
  22.533 +        int nofail1=0, nofail2=0;
  22.534 +        cCDDBSong *s=First();
  22.535 +        while(s) {
  22.536 +          if(s->TTitle) {
  22.537 +            if(strstr(s->TTitle," / ")) nofail1++;
  22.538 +            //if(strstr(s->TTitle," - ")) nofail2++;
  22.539 +            }
  22.540 +          s=Next(s);
  22.541 +          }
  22.542 +        if(nofail1==Count() || nofail2==Count()) {
  22.543 +          d(printf("cddb: detecting sampler from nofail\n"))
  22.544 +          isSampler=true;
  22.545 +          }
  22.546 +        }
  22.547 +
  22.548 +      if(Year<0 && ExtD && (p=strstr(ExtD,"YEAR:"))) Year=atoi(p+5);
  22.549 +      free(ExtD); ExtD=0;
  22.550 +      d(printf("cddb: found year=%d\n",Year))
  22.551 +
  22.552 +      cCDDBSong *s=First();
  22.553 +      while(s) {
  22.554 +        if(s->TTitle) {
  22.555 +          if(isSampler) {
  22.556 +            if(!Split(s->TTitle,'/',s->Artist,s->Title) && 
  22.557 +               !Split(s->TTitle,'-',s->Artist,s->Title,true)) {
  22.558 +              s->Title=compactspace(strdup(s->TTitle));
  22.559 +              if(s->ExtT) s->Artist=compactspace(strdup(s->ExtT));
  22.560 +              }
  22.561 +            }
  22.562 +          else {
  22.563 +            s->Title=compactspace(strdup(s->TTitle));
  22.564 +            if(Artist) s->Artist=strdup(Artist);
  22.565 +            }
  22.566 +          }
  22.567 +        else s->Title=strdup(tr("unknown"));
  22.568 +
  22.569 +        free(s->TTitle); s->TTitle=0;
  22.570 +        free(s->ExtT); s->ExtT=0;
  22.571 +        d(printf("cddb: found track %d title='%s' artist='%s'\n",s->Track,s->Title,s->Artist))
  22.572 +        s=Next(s);
  22.573 +        }
  22.574 +      return true;
  22.575 +      }
  22.576 +    }
  22.577 +  return false;
  22.578 +}
  22.579 +
  22.580 +bool cCDDBDisc::Split(const char *source, char div, char * &first, char * &second, bool only3)
  22.581 +{
  22.582 +  int pos=-1, n=0;
  22.583 +  char *p, l[4]={ ' ',div,' ',0 };
  22.584 +  if ((p=strstr(source,l))) { pos=p-source; n=3; }
  22.585 +  else if(!only3 && (p=strchr(source,div)))  { pos=p-source; n=1; }
  22.586 +  if(pos>=0) {
  22.587 +    free(first); first=strdup(source); first[pos]=0; compactspace(first);
  22.588 +    free(second); second=strdup(source+pos+n); compactspace(second);
  22.589 +    return true;
  22.590 +    }
  22.591 +  return false;
  22.592 +}
  22.593 +
  22.594 +void cCDDBDisc::Strcat(char * &store, char *value)
  22.595 +{
  22.596 +  if(store) {
  22.597 +    char *n=MALLOC(char,strlen(store)+strlen(value)+1);
  22.598 +    if(n) {
  22.599 +      strcpy(n,store);
  22.600 +      strcat(n,value);
  22.601 +      free(store); store=n;
  22.602 +      }
  22.603 +    }
  22.604 +  else store=strdup(value);
  22.605 +}
  22.606 +
  22.607 +cCDDBSong *cCDDBDisc::GetTrack(const char *name, unsigned int pos)
  22.608 +{
  22.609 +  cCDDBSong *s=0;
  22.610 +  if(strlen(name)>pos) {
  22.611 +    int tr=atoi(&name[pos]);
  22.612 +    s=FindTrack(tr);
  22.613 +    if(!s) {
  22.614 +      s=new cCDDBSong;
  22.615 +      Add(s);
  22.616 +      s->Track=tr;
  22.617 +      }
  22.618 +    }
  22.619 +  return s;
  22.620 +}
  22.621 +
  22.622 +cCDDBSong *cCDDBDisc::FindTrack(int tr)
  22.623 +{
  22.624 +  cCDDBSong *s=First();
  22.625 +  while(s) {
  22.626 +    if(s->Track==tr) break;
  22.627 +    s=Next(s);
  22.628 +    }
  22.629 +  return s;
  22.630 +}
  22.631 +
  22.632 +#ifndef TEST_MAIN
  22.633 +
  22.634 +// --- cCDDB -------------------------------------------------------------------
  22.635 +
  22.636 +class cCDDB : public cScanDir, cMutex {
  22.637 +private:
  22.638 +  cCDDBDisc cache;
  22.639 +  cFileSource *src;
  22.640 +  cFileObj *file;
  22.641 +  cNet *net;
  22.642 +  char searchID[10], cddbstr[256];
  22.643 +  //
  22.644 +  virtual void DoItem(cFileSource *src, const char *subdir, const char *name);
  22.645 +  bool LocalQuery(cDiscID *id);
  22.646 +  bool RemoteGet(cDiscID *id);
  22.647 +  bool GetLine(char *buff, int size, bool log=true);
  22.648 +  int GetCddbResponse(void);
  22.649 +  int DoCddbCmd(char *format, ...);
  22.650 +public:
  22.651 +  cCDDB(void);
  22.652 +  virtual ~cCDDB();
  22.653 +  bool Lookup(cDiscID *id, int track, cSongInfo *si);
  22.654 +  };
  22.655 +
  22.656 +cCDDB cddb;
  22.657 +
  22.658 +cCDDB::cCDDB(void)
  22.659 +{
  22.660 +  src=0; file=0; net=0;
  22.661 +}
  22.662 +
  22.663 +cCDDB::~cCDDB()
  22.664 +{
  22.665 +  delete file;
  22.666 +  delete src;
  22.667 +  delete net;
  22.668 +}
  22.669 +
  22.670 +bool cCDDB::Lookup(cDiscID *id, int track, cSongInfo *si)
  22.671 +{
  22.672 +  bool res=false;
  22.673 +  Lock();
  22.674 +  if(!cache.Cached(id)) {
  22.675 +    if(LocalQuery(id) || (MP3Setup.UseCddb>1 && RemoteGet(id) && LocalQuery(id)))
  22.676 +      cache.Load(id,file->FullPath());
  22.677 +    }
  22.678 +  if(cache.Cached(id) && cache.TrackInfo(track,si)) res=true;
  22.679 +  Unlock();
  22.680 +  return res;
  22.681 +}
  22.682 +
  22.683 +bool cCDDB::LocalQuery(cDiscID *id)
  22.684 +{
  22.685 +  bool res=false;
  22.686 +  delete file; file=0;
  22.687 +  if(!src) src=new cFileSource(cddbpath,"CDDB database",false);
  22.688 +  if(src) {
  22.689 +    snprintf(searchID,sizeof(searchID),"%08x",id->discid);
  22.690 +    if(ScanDir(src,0,stDir,0,0,false) && file) res=true;
  22.691 +    }
  22.692 +  return res;
  22.693 +}
  22.694 +
  22.695 +void cCDDB::DoItem(cFileSource *src, const char *subdir, const char *name)
  22.696 +{
  22.697 +  if(!file) {
  22.698 +    file=new cFileObj(src,name,searchID,otFile);
  22.699 +    if(access(file->FullPath(),R_OK)) { delete file; file=0; }
  22.700 +    }
  22.701 +}
  22.702 +
  22.703 +bool cCDDB::RemoteGet(cDiscID *id)
  22.704 +{
  22.705 +  bool res=false;
  22.706 +  asyncStatus.Set(tr("Remote CDDB lookup..."));
  22.707 +
  22.708 +  delete net; net=new cNet(16*1024,CDDB_TOUT,CDDB_TOUT);
  22.709 +  if(net->Connect(MP3Setup.CddbHost,MP3Setup.CddbPort)) {
  22.710 +    int code=GetCddbResponse();
  22.711 +    if(code/100==2) {
  22.712 +      char *host=getenv("HOSTNAME"); if(!host) host="unknown";
  22.713 +      char *user=getenv("USER"); if(!user) user="nobody";
  22.714 +      code=DoCddbCmd("cddb hello %s %s %s %s\n",user,host,PLUGIN_NAME,PLUGIN_VERSION);
  22.715 +      if(code/100==2) {
  22.716 +        code=DoCddbCmd("proto %d\n",CDDB_PROTO);
  22.717 +        if(code>0) {
  22.718 +          char off[1024];
  22.719 +          off[0]=0; for(int i=0 ; i<id->ntrks ; i++) sprintf(&off[strlen(off)]," %d",id->offsets[i]);
  22.720 +
  22.721 +          code=DoCddbCmd("cddb query %08x %d %s %d\n",id->discid,id->ntrks,off,id->nsecs);
  22.722 +          if(code/100==2) {
  22.723 +            char *cat=0;
  22.724 +            if(code==200) cat=strdup(cddbstr);
  22.725 +            else if(code==210) {
  22.726 +              if(GetLine(off,sizeof(off))) {
  22.727 +                cat=strdup(off);
  22.728 +                while(GetLine(off,sizeof(off)) && off[0]!='.');
  22.729 +                }
  22.730 +              }
  22.731 +
  22.732 +            if(cat) {
  22.733 +              char *s=index(cat,' '); if(s) *s=0;
  22.734 +              code=DoCddbCmd("cddb read %s %08x\n",cat,id->discid);
  22.735 +              if(code==210) {
  22.736 +                char *name=0;
  22.737 +                asprintf(&name,"%s/%s/%08x",cddbpath,cat,id->discid);
  22.738 +                if(MakeDirs(name,false)) {
  22.739 +                  FILE *out=fopen(name,"w");
  22.740 +                  if(out) {
  22.741 +                    while(GetLine(off,sizeof(off),false) && off[0]!='.') fputs(off,out);
  22.742 +                    fclose(out);
  22.743 +                    res=true;
  22.744 +                    }
  22.745 +                  else esyslog("fopen() failed: %s",strerror(errno));
  22.746 +                  }
  22.747 +                free(name);
  22.748 +                }
  22.749 +              else if(code>0) esyslog("server read error: %d %s",code,cddbstr);
  22.750 +              free(cat);
  22.751 +              }
  22.752 +            }
  22.753 +          else if(code>0) esyslog("server query error: %d %s",code,cddbstr);
  22.754 +          }
  22.755 +        else esyslog("server proto error: %d %s",code,cddbstr);
  22.756 +        }
  22.757 +      else if(code>0) esyslog("server hello error: %d %s",code,cddbstr);
  22.758 +      }
  22.759 +    else if(code>0) esyslog("server sign-on error: %d %s",code,cddbstr);
  22.760 +    }
  22.761 +
  22.762 +  delete net; net=0;
  22.763 +  asyncStatus.Set(0);
  22.764 +  return res;
  22.765 +}
  22.766 +
  22.767 +bool cCDDB::GetLine(char *buff, int size, bool log)
  22.768 +{
  22.769 +  if(net->Gets(buff,size)>0) {
  22.770 +#ifdef CDDB_DEBUG
  22.771 +    if(log) printf("cddb: <- %s",buff);
  22.772 +#endif
  22.773 +    return true;
  22.774 +    }
  22.775 +  return false;
  22.776 +}
  22.777 +
  22.778 +int cCDDB::GetCddbResponse(void)
  22.779 +{
  22.780 +  char buf[1024];
  22.781 +  if(GetLine(buf,sizeof(buf))) {
  22.782 +    int code;
  22.783 +    if(sscanf(buf,"%d %255[^\n]",&code,cddbstr)==2) return code;
  22.784 +    else esyslog("Unexpected server response: %s",buf);
  22.785 +    }
  22.786 +  return -1;
  22.787 +}
  22.788 +
  22.789 +int cCDDB::DoCddbCmd(char *format, ...)
  22.790 +{
  22.791 +  va_list ap;
  22.792 +  va_start(ap,format);
  22.793 +  char *buff=0;
  22.794 +  vasprintf(&buff,format,ap);
  22.795 +  va_end(ap);
  22.796 +#ifdef CDDB_DEBUG
  22.797 +  printf("cddb: -> %s",buff);
  22.798 +#endif
  22.799 +  int r=net->Puts(buff);
  22.800 +  free(buff);
  22.801 +  if(r<0) return -1;
  22.802 +  return GetCddbResponse();
  22.803 +}
  22.804 +
  22.805 +// --- cSndInfo ----------------------------------------------------------------
  22.806 +
  22.807 +cSndInfo::cSndInfo(cSndFile *File)
  22.808 +{
  22.809 +  file=File;
  22.810 +  id=new cDiscID;
  22.811 +}
  22.812 +
  22.813 +cSndInfo::~cSndInfo()
  22.814 +{
  22.815 +  delete id;
  22.816 +}
  22.817 +
  22.818 +bool cSndInfo::Abort(bool result)
  22.819 +{
  22.820 +  if(!keepOpen) file->Close();
  22.821 +  return result;
  22.822 +}
  22.823 +
  22.824 +bool cSndInfo::DoScan(bool KeepOpen)
  22.825 +{
  22.826 +  keepOpen=KeepOpen;
  22.827 +  if(!file->Open()) return Abort(false);
  22.828 +  if(HasInfo()) return Abort(true);
  22.829 +
  22.830 +  // check the infocache
  22.831 +  cCacheData *dat=InfoCache.Search(file);
  22.832 +  if(dat) {
  22.833 +    Set(dat); dat->Unlock();
  22.834 +    if(!DecoderID) {
  22.835 +      DecoderID=DEC_SND;
  22.836 +      InfoCache.Cache(this,file);
  22.837 +      }
  22.838 +    return Abort(true);
  22.839 +    }
  22.840 +
  22.841 +  Clear();
  22.842 +
  22.843 +  if(file->FsType!=CDFS_MAGIC || !MP3Setup.UseCddb || !CDDBLookup(file->Filename))
  22.844 +    FakeTitle(file->Filename);
  22.845 +
  22.846 +  Frames=file->sfi.frames;
  22.847 +  SampleFreq=file->sfi.samplerate;
  22.848 +  Channels=file->sfi.channels;
  22.849 +  ChMode=Channels>1 ? 3:0;
  22.850 +  Total=Frames/SampleFreq;
  22.851 +  Bitrate=file->Filesize*8/Total; //XXX SampleFreq*Channels*file->sfi.pcmbitwidth;
  22.852 +  DecoderID=DEC_SND;
  22.853 +
  22.854 +  InfoDone();
  22.855 +  InfoCache.Cache(this,file);
  22.856 +  return Abort(true);  
  22.857 +}
  22.858 +
  22.859 +bool cSndInfo::CDDBLookup(const char *filename)
  22.860 +{
  22.861 +  if(id->Get()) {
  22.862 +    int tr;
  22.863 +    char *s=strstr(filename,CDFS_TRACK);
  22.864 +    if(s && sscanf(s+strlen(CDFS_TRACK),"%d",&tr)==1) {
  22.865 +      d(printf("snd: looking up disc id %08x track %d\n",id->discid,tr))
  22.866 +      return cddb.Lookup(id,tr-1,this);
  22.867 +      }
  22.868 +    }
  22.869 +  return false;
  22.870 +}
  22.871 +
  22.872 +// --- cSndFile ----------------------------------------------------------------
  22.873 +
  22.874 +cSndFile::cSndFile(const char *Filename)
  22.875 +:cFileInfo(Filename)
  22.876 +{
  22.877 +  sf=0;
  22.878 +}
  22.879 +
  22.880 +cSndFile::~cSndFile()
  22.881 +{
  22.882 +  Close();
  22.883 +}
  22.884 +
  22.885 +bool cSndFile::Open(bool log)
  22.886 +{
  22.887 +  if(sf) return (Seek()>=0);
  22.888 +
  22.889 +  if(FileInfo(log)) {
  22.890 +    sf=sf_open(Filename,SFM_READ,&sfi);
  22.891 +    if(!sf && log) Error("open");
  22.892 +    }
  22.893 +  return (sf!=0);
  22.894 +}
  22.895 +
  22.896 +void cSndFile::Close(void)
  22.897 +{
  22.898 +  if(sf) { sf_close(sf); sf=0; } 
  22.899 +}
  22.900 +
  22.901 +void cSndFile::Error(const char *action)
  22.902 +{
  22.903 +  char buff[128];
  22.904 +  sf_error_str(sf,buff,sizeof(buff));
  22.905 +  esyslog("ERROR: sndfile %s failed on %s: %s",action,Filename,buff);
  22.906 +}
  22.907 +
  22.908 +sf_count_t cSndFile::Seek(sf_count_t frames, bool relativ)
  22.909 +{
  22.910 +  int dir=SEEK_CUR;
  22.911 +  if(!relativ) dir=SEEK_SET;
  22.912 +  int n=sf_seek(sf,frames,dir);
  22.913 +  if(n<0) Error("seek");
  22.914 +  return n;
  22.915 +}
  22.916 +
  22.917 +sf_count_t cSndFile::Stream(int *buffer, sf_count_t frames)
  22.918 +{
  22.919 +  sf_count_t n=sf_readf_int(sf,buffer,frames);
  22.920 +  if(n<0) Error("read");
  22.921 +  return n;
  22.922 +}
  22.923 +
  22.924 +#endif //TEST_MAIN
  22.925 +#endif //HAVE_SNDFILE
  22.926 +
  22.927 +#ifdef TEST_MAIN
  22.928 +//
  22.929 +// to compile:
  22.930 +// g++ -g -DTEST_MAIN -o test mp3-decoder-snd.c tools.o thread.o -lpthread
  22.931 +//
  22.932 +// calling:
  22.933 +// test <cddb-file>
  22.934 +//
  22.935 +
  22.936 +extern const char *tr(const char *test)
  22.937 +{
  22.938 +  return test;
  22.939 +}
  22.940 +
  22.941 +int main (int argc, char *argv[])
  22.942 +{
  22.943 +  cCDDBDisc cddb;
  22.944 +  
  22.945 +  cddb.Load(1,argv[1]);
  22.946 +  return 0;
  22.947 +}
  22.948 +#endif
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/decoder-snd.h	Sat Dec 29 14:47:40 2007 +0100
    23.3 @@ -0,0 +1,114 @@
    23.4 +/*
    23.5 + * MP3/MPlayer plugin to VDR (C++)
    23.6 + *
    23.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    23.8 + *
    23.9 + * This code is free software; you can redistribute it and/or
   23.10 + * modify it under the terms of the GNU General Public License
   23.11 + * as published by the Free Software Foundation; either version 2
   23.12 + * of the License, or (at your option) any later version.
   23.13 + *
   23.14 + * This code is distributed in the hope that it will be useful,
   23.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   23.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   23.17 + * GNU General Public License for more details.
   23.18 + *
   23.19 + * You should have received a copy of the GNU General Public License
   23.20 + * along with this program; if not, write to the Free Software
   23.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   23.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   23.23 + */
   23.24 +
   23.25 +#ifndef ___DECODER_SND_H
   23.26 +#define ___DECODER_SND_H
   23.27 +
   23.28 +#define DEC_SND     DEC_ID('S','N','D',' ')
   23.29 +#define DEC_SND_STR "SND"
   23.30 +
   23.31 +#ifdef HAVE_SNDFILE
   23.32 +
   23.33 +#include <time.h>
   23.34 +#include <mad.h>
   23.35 +#include <sndfile.h>
   23.36 +
   23.37 +#include <vdr/thread.h>
   23.38 +
   23.39 +#include "decoder.h"
   23.40 +#include "decoder-core.h"
   23.41 +
   23.42 +#define CDFS_MAGIC 0xCDDA // cdfs filesystem-ID
   23.43 +
   23.44 +class cDiscID;
   23.45 +
   23.46 +// ----------------------------------------------------------------
   23.47 +
   23.48 +class cSndFile : public cFileInfo {
   23.49 +private:
   23.50 +  SNDFILE *sf;
   23.51 +  //
   23.52 +  void Error(const char *action);
   23.53 +public:
   23.54 +  SF_INFO sfi;
   23.55 +  //
   23.56 +  cSndFile(const char *Filename);
   23.57 +  ~cSndFile();
   23.58 +  bool Open(bool log=true);
   23.59 +  void Close(void);
   23.60 +  sf_count_t Seek(sf_count_t frames=0, bool relativ=false);
   23.61 +  sf_count_t Stream(int *buffer, sf_count_t frames);
   23.62 +  };
   23.63 +
   23.64 +// ----------------------------------------------------------------
   23.65 +
   23.66 +class cSndInfo : public cSongInfo {
   23.67 +private:
   23.68 +  cSndFile *file;
   23.69 +  cDiscID *id;
   23.70 +  bool keepOpen;
   23.71 +  //
   23.72 +  bool Abort(bool result);
   23.73 +  bool CDDBLookup(const char *filename);
   23.74 +public:
   23.75 +  cSndInfo(cSndFile *File);
   23.76 +  ~cSndInfo();
   23.77 +  bool DoScan(bool KeepOpen=false);
   23.78 +  };
   23.79 +
   23.80 +// ----------------------------------------------------------------
   23.81 +
   23.82 +class cSndDecoder : public cDecoder, public cThread {
   23.83 +private:
   23.84 +  cSndFile file;
   23.85 +  cSndInfo info;
   23.86 +  struct Decode ds;
   23.87 +  struct mad_pcm *pcm;
   23.88 +  unsigned long long index;
   23.89 +  //
   23.90 +  cMutex buffMutex;
   23.91 +  cCondVar fgCond, bgCond;
   23.92 +  bool run, ready;
   23.93 +  int *framebuff, deferedN, softCount;
   23.94 +  //
   23.95 +  void Init(void);
   23.96 +  bool Clean(void);
   23.97 +  bool GetInfo(bool keepOpen);
   23.98 +  struct Decode *Done(eDecodeStatus status);
   23.99 +protected:
  23.100 +  virtual void Action(void);
  23.101 +public:
  23.102 +  cSndDecoder(const char *Filename);
  23.103 +  ~cSndDecoder();
  23.104 +  virtual bool Valid(void);
  23.105 +  virtual cFileInfo *FileInfo(void);
  23.106 +  virtual cSongInfo *SongInfo(bool get);
  23.107 +  virtual cPlayInfo *PlayInfo(void);
  23.108 +  virtual bool Start(void);
  23.109 +  virtual bool Stop(void);
  23.110 +  virtual bool Skip(int Seconds, float bsecs);
  23.111 +  virtual struct Decode *Decode(void);
  23.112 +  };
  23.113 +
  23.114 +// ----------------------------------------------------------------
  23.115 +
  23.116 +#endif //HAVE_SNDFILE
  23.117 +#endif //___DECODER_SND_H
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/decoder.c	Sat Dec 29 14:47:40 2007 +0100
    24.3 @@ -0,0 +1,632 @@
    24.4 +/*
    24.5 + * MP3/MPlayer plugin to VDR (C++)
    24.6 + *
    24.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    24.8 + *
    24.9 + * This code is free software; you can redistribute it and/or
   24.10 + * modify it under the terms of the GNU General Public License
   24.11 + * as published by the Free Software Foundation; either version 2
   24.12 + * of the License, or (at your option) any later version.
   24.13 + *
   24.14 + * This code is distributed in the hope that it will be useful,
   24.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   24.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   24.17 + * GNU General Public License for more details.
   24.18 + *
   24.19 + * You should have received a copy of the GNU General Public License
   24.20 + * along with this program; if not, write to the Free Software
   24.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   24.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   24.23 + */
   24.24 +
   24.25 +#include <stdlib.h>
   24.26 +#include <stdio.h>
   24.27 +#include <errno.h>
   24.28 +#include <sys/stat.h>
   24.29 +#include <sys/vfs.h>
   24.30 +
   24.31 +#include <vdr/videodir.h>
   24.32 +
   24.33 +#include "common.h"
   24.34 +#include "data-mp3.h"
   24.35 +#include "data-src.h"
   24.36 +#include "decoder.h"
   24.37 +#include "decoder-core.h"
   24.38 +#include "decoder-mp3.h"
   24.39 +#include "decoder-mp3-stream.h"
   24.40 +#include "decoder-snd.h"
   24.41 +#include "decoder-ogg.h"
   24.42 +
   24.43 +#define CACHEFILENAME     "id3info.cache"
   24.44 +#define CACHESAVETIMEOUT  120 // secs
   24.45 +#define CACHEPURGETIMEOUT 120 // days
   24.46 +
   24.47 +extern cFileSources MP3Sources;
   24.48 +
   24.49 +cInfoCache InfoCache;
   24.50 +char *cachedir=0;
   24.51 +
   24.52 +int MakeHashBuff(const char *buff, int len)
   24.53 +{
   24.54 +  int h=len;
   24.55 +  while(len--) h=(h*13 + *buff++) & 0x7ff;
   24.56 +  return h;
   24.57 +}
   24.58 +
   24.59 +// --- cSongInfo ---------------------------------------------------------------
   24.60 +
   24.61 +cSongInfo::cSongInfo(void)
   24.62 +{
   24.63 +  Title=Artist=Album=0;
   24.64 +  Clear();
   24.65 +}
   24.66 +
   24.67 +cSongInfo::~cSongInfo()
   24.68 +{
   24.69 +  Clear();
   24.70 +}
   24.71 +
   24.72 +void cSongInfo::Clear(void)
   24.73 +{
   24.74 +  Frames=0; Total=-1; DecoderID=0;
   24.75 +  SampleFreq=Channels=Bitrate=MaxBitrate=ChMode=-1;
   24.76 +  free(Title); Title=0;
   24.77 +  free(Artist); Artist=0;
   24.78 +  free(Album); Album=0;
   24.79 +  Year=-1;
   24.80 +  Level=Peak=0.0;
   24.81 +  infoDone=false;
   24.82 +}
   24.83 +
   24.84 +void cSongInfo::Set(cSongInfo *si)
   24.85 +{
   24.86 +  Clear(); InfoDone();
   24.87 +  Frames=si->Frames;
   24.88 +  Total=si->Total;
   24.89 +  SampleFreq=si->SampleFreq;
   24.90 +  Channels=si->Channels;
   24.91 +  Bitrate=si->Bitrate;
   24.92 +  MaxBitrate=si->MaxBitrate;
   24.93 +  ChMode=si->ChMode;
   24.94 +  Year=si->Year;
   24.95 +  Title=si->Title ? strdup(si->Title):0;
   24.96 +  Artist=si->Artist ? strdup(si->Artist):0;
   24.97 +  Album=si->Album ? strdup(si->Album):0;
   24.98 +  if(si->Level>0.0) { // preserve old level
   24.99 +    Level=si->Level;
  24.100 +    Peak=si->Peak;
  24.101 +    }
  24.102 +  DecoderID=si->DecoderID;
  24.103 +}
  24.104 +
  24.105 +void cSongInfo::FakeTitle(const char *filename, const char *extention)
  24.106 +{
  24.107 +  // if no title, try to build a reasonable from the filename
  24.108 +  if(!Title && filename)  {
  24.109 +    char *s=rindex(filename,'/');
  24.110 +    if(s && *s=='/') {
  24.111 +      s++;
  24.112 +      Title=strdup(s);
  24.113 +      strreplace(Title,'_',' ');
  24.114 +      if(extention) {                            // strip given extention
  24.115 +        int l=strlen(Title)-strlen(extention);
  24.116 +        if(l>0 && !strcasecmp(Title+l,extention)) Title[l]=0;
  24.117 +        }
  24.118 +      else {                                     // strip any extention
  24.119 +        s=rindex(Title,'.');
  24.120 +        if(s && *s=='.' && strlen(s)<=5) *s=0;
  24.121 +        }
  24.122 +      d(printf("mp3: faking title '%s' from filename '%s'\n",Title,filename))
  24.123 +      }
  24.124 +    }
  24.125 +}
  24.126 +
  24.127 +// --- cFileInfo ---------------------------------------------------------------
  24.128 +
  24.129 +cFileInfo::cFileInfo(void)
  24.130 +{
  24.131 +  Filename=FsID=0; Clear();
  24.132 +}
  24.133 +
  24.134 +cFileInfo::cFileInfo(const char *Name)
  24.135 +{
  24.136 +  Filename=FsID=0; Clear();
  24.137 +  Filename=strdup(Name);
  24.138 +}
  24.139 +
  24.140 +cFileInfo::~cFileInfo()
  24.141 +{
  24.142 +  Clear();
  24.143 +}
  24.144 +
  24.145 +void cFileInfo::Clear(void)
  24.146 +{
  24.147 +  free(Filename); Filename=0;
  24.148 +  free(FsID); FsID=0;
  24.149 +  Filesize=0; CTime=0; FsType=0; removable=-1;
  24.150 +  infoDone=false;
  24.151 +}
  24.152 +
  24.153 +bool cFileInfo::Removable(void)
  24.154 +{
  24.155 +  if(removable<0 && Filename) {
  24.156 +    cFileSource *src=MP3Sources.FindSource(Filename);
  24.157 +    if(src) removable=src->NeedsMount();
  24.158 +    else removable=1;
  24.159 +    }
  24.160 +  return (removable!=0);
  24.161 +}
  24.162 +
  24.163 +void cFileInfo::Set(cFileInfo *fi)
  24.164 +{
  24.165 +  Clear(); InfoDone();
  24.166 +  Filename=fi->Filename ? strdup(fi->Filename):0;
  24.167 +  FsID=fi->FsID ? strdup(fi->FsID):0;
  24.168 +  Filesize=fi->Filesize;
  24.169 +  CTime=fi->CTime;
  24.170 +}
  24.171 +
  24.172 +
  24.173 +bool cFileInfo::FileInfo(bool log)
  24.174 +{
  24.175 +  if(Filename) {
  24.176 +    struct stat64 ds;
  24.177 +    if(!stat64(Filename,&ds)) {
  24.178 +      if(S_ISREG(ds.st_mode)) {
  24.179 +        free(FsID); FsID=0;
  24.180 +        FsType=0;
  24.181 +        struct statfs64 sfs;
  24.182 +        if(!statfs64(Filename,&sfs)) {
  24.183 +          if(Removable()) asprintf(&FsID,"%llx:%llx",sfs.f_blocks,sfs.f_files);
  24.184 +          FsType=sfs.f_type;
  24.185 +          }
  24.186 +        else if(errno!=ENOSYS && log) { esyslog("ERROR: can't statfs %s: %s",Filename,strerror(errno)); }
  24.187 +        Filesize=ds.st_size;
  24.188 +        CTime=ds.st_ctime;
  24.189 +#ifdef CDFS_MAGIC
  24.190 +        if(FsType==CDFS_MAGIC) CTime=0; // CDFS returns mount time as ctime
  24.191 +#endif
  24.192 +        InfoDone();
  24.193 +        return true;
  24.194 +        }
  24.195 +      else if(log) { esyslog("ERROR: %s is not a regular file",Filename); }
  24.196 +      }
  24.197 +    else if(log) { esyslog("ERROR: can't stat %s: %s",Filename,strerror(errno)); }
  24.198 +    }
  24.199 +  return false;
  24.200 +}
  24.201 +
  24.202 +// --- cDecoders ---------------------------------------------------------------
  24.203 +
  24.204 +cDecoder *cDecoders::FindDecoder(cFileObj *Obj)
  24.205 +{
  24.206 +  const char *full=Obj->FullPath();
  24.207 +  cFileInfo fi(full);
  24.208 +  cCacheData *dat;
  24.209 +  cDecoder *decoder=0;
  24.210 +  if(fi.FileInfo(false) && (dat=InfoCache.Search(&fi))) {
  24.211 +    if(dat->DecoderID) {
  24.212 +      //d(printf("mp3: found DecoderID '%s' for %s from cache\n",cDecoders::ID2Str(dat->DecoderID),Filename))
  24.213 +      switch(dat->DecoderID) {
  24.214 +        case DEC_MP3:  decoder=new cMP3Decoder(full); break;
  24.215 +        case DEC_MP3S: decoder=new cMP3StreamDecoder(full); break;
  24.216 +#ifdef HAVE_SNDFILE
  24.217 +        case DEC_SND:  decoder=new cSndDecoder(full); break;
  24.218 +#endif
  24.219 +#ifdef HAVE_VORBISFILE
  24.220 +        case DEC_OGG:  decoder=new cOggDecoder(full); break;
  24.221 +#endif
  24.222 +        default:       esyslog("ERROR: bad DecoderID '%s' from info cache: %s",cDecoders::ID2Str(dat->DecoderID),full); break;
  24.223 +        }
  24.224 +      }
  24.225 +    dat->Unlock();
  24.226 +    }
  24.227 +
  24.228 +  if(!decoder || !decoder->Valid()) {
  24.229 +    // no decoder in cache or cached decoder doesn't matches.
  24.230 +    // try to detect a decoder
  24.231 +
  24.232 +    delete decoder; decoder=0;
  24.233 +#ifdef HAVE_SNDFILE
  24.234 +    if(!decoder) {
  24.235 +      decoder=new cSndDecoder(full);
  24.236 +      if(!decoder || !decoder->Valid()) { delete decoder; decoder=0; }
  24.237 +      }
  24.238 +#endif
  24.239 +#ifdef HAVE_VORBISFILE
  24.240 +    if(!decoder) {
  24.241 +      decoder=new cOggDecoder(full);
  24.242 +      if(!decoder || !decoder->Valid()) { delete decoder; decoder=0; }
  24.243 +      }
  24.244 +#endif
  24.245 +    if(!decoder) {
  24.246 +      decoder=new cMP3StreamDecoder(full);
  24.247 +      if(!decoder || !decoder->Valid()) { delete decoder; decoder=0; }
  24.248 +      }
  24.249 +    if(!decoder) {
  24.250 +      decoder=new cMP3Decoder(full);
  24.251 +      if(!decoder || !decoder->Valid()) { delete decoder; decoder=0; }
  24.252 +      }
  24.253 +    if(!decoder) esyslog("ERROR: no decoder found for %s",Obj->Name());
  24.254 +    }
  24.255 +  return decoder;
  24.256 +}
  24.257 +
  24.258 +const char *cDecoders::ID2Str(int id)
  24.259 +{
  24.260 +  switch(id) {
  24.261 +    case DEC_MP3:  return DEC_MP3_STR;
  24.262 +    case DEC_MP3S: return DEC_MP3S_STR;
  24.263 +    case DEC_SND:  return DEC_SND_STR;
  24.264 +    case DEC_OGG:  return DEC_OGG_STR;
  24.265 +    }
  24.266 +  return 0;
  24.267 +}
  24.268 +
  24.269 +int cDecoders::Str2ID(const char *str)
  24.270 +{
  24.271 +  if     (!strcmp(str,DEC_MP3_STR )) return DEC_MP3;
  24.272 +  else if(!strcmp(str,DEC_MP3S_STR)) return DEC_MP3S;
  24.273 +  else if(!strcmp(str,DEC_SND_STR )) return DEC_SND;
  24.274 +  else if(!strcmp(str,DEC_OGG_STR )) return DEC_OGG;
  24.275 +  return 0;
  24.276 +}
  24.277 +
  24.278 +// --- cDecoder ----------------------------------------------------------------
  24.279 +
  24.280 +cDecoder::cDecoder(const char *Filename)
  24.281 +{
  24.282 +  filename=strdup(Filename);
  24.283 +  locked=0; urgentLock=playing=false;
  24.284 +}
  24.285 +
  24.286 +cDecoder::~cDecoder()
  24.287 +{
  24.288 +  free(filename);
  24.289 +}
  24.290 +
  24.291 +void cDecoder::Lock(bool urgent)
  24.292 +{
  24.293 +  locklock.Lock();
  24.294 +  if(urgent && locked) urgentLock=true; // signal other locks to release quickly
  24.295 +  locked++;
  24.296 +  locklock.Unlock(); // don't hold the "locklock" when locking "lock", may cause a deadlock
  24.297 +  lock.Lock();
  24.298 +  urgentLock=false;
  24.299 +}
  24.300 +
  24.301 +void cDecoder::Unlock(void)
  24.302 +{
  24.303 +  locklock.Lock();
  24.304 +  locked--;
  24.305 +  lock.Unlock();
  24.306 +  locklock.Unlock();
  24.307 +}
  24.308 +
  24.309 +bool cDecoder::TryLock(void)
  24.310 +{
  24.311 +  bool res=false;
  24.312 +  locklock.Lock();
  24.313 +  if(!locked && !playing) {
  24.314 +    Lock();
  24.315 +    res=true;
  24.316 +    }
  24.317 +  locklock.Unlock();
  24.318 +  return res;
  24.319 +}
  24.320 +
  24.321 +// --- cCacheData -----------------------------------------------------
  24.322 +
  24.323 +cCacheData::cCacheData(void)
  24.324 +{
  24.325 +  touch=0; version=0;
  24.326 +}
  24.327 +
  24.328 +void cCacheData::Touch(void)
  24.329 +{
  24.330 +  touch=time(0);
  24.331 +}
  24.332 +
  24.333 +#define SECS_PER_DAY (24*60*60)
  24.334 +
  24.335 +bool cCacheData::Purge(void)
  24.336 +{
  24.337 +  time_t now=time(0);
  24.338 +  //XXX does this realy made sense?
  24.339 +  //if(touch+CACHEPURGETIMEOUT*SECS_PER_DAY < now) {
  24.340 +  //  d(printf("cache: purged: timeout %s\n",Filename))
  24.341 +  //  return true;
  24.342 +  //  }
  24.343 +  if(touch+CACHEPURGETIMEOUT*SECS_PER_DAY/10 < now) {
  24.344 +    if(!Removable()) {                            // is this a permant source?
  24.345 +      struct stat64 ds;                           // does the file exists? if not, purge
  24.346 +      if(stat64(Filename,&ds) || !S_ISREG(ds.st_mode) || access(Filename,R_OK)) {
  24.347 +        d(printf("cache: purged: file not found %s\n",Filename))
  24.348 +        return true;
  24.349 +        }
  24.350 +      }
  24.351 +    }
  24.352 +  return false;
  24.353 +}
  24.354 +
  24.355 +bool cCacheData::Upgrade(void)
  24.356 +{
  24.357 +  if(version<7) {
  24.358 +    if(DecoderID==DEC_SND || (Title && startswith(Title,"track-")))
  24.359 +      return false;              // Trash older SND entries (incomplete)
  24.360 +
  24.361 +    if(Removable()) {
  24.362 +      if(!FsID) FsID=strdup("old"); // Dummy entry, will be replaced in InfoCache::Search()
  24.363 +      }
  24.364 +    else { free(FsID); FsID=0; }
  24.365 +    }
  24.366 +  if(version<4) {
  24.367 +    Touch();                     // Touch entry
  24.368 +    }
  24.369 +  if(version<3 && !Title) {
  24.370 +    FakeTitle(Filename,".mp3");  // Fake title
  24.371 +    }
  24.372 +  if(version<2 && Bitrate<=0) {
  24.373 +    return false;                // Trash entry without bitrate
  24.374 +    }
  24.375 +  return true;
  24.376 +}
  24.377 +
  24.378 +void cCacheData::Create(cFileInfo *fi, cSongInfo *si)
  24.379 +{
  24.380 +  cFileInfo::Set(fi);
  24.381 +  cSongInfo::Set(si);
  24.382 +  hash=MakeHash(Filename);
  24.383 +  Touch();
  24.384 +}
  24.385 +
  24.386 +bool cCacheData::Save(FILE *f)
  24.387 +{
  24.388 +  fprintf(f,"##BEGIN\n"
  24.389 +            "Filename=%s\n"
  24.390 +            "Filesize=%lld\n"
  24.391 +            "Timestamp=%ld\n"
  24.392 +            "Touch=%ld\n"
  24.393 +            "Version=%d\n"
  24.394 +            "Frames=%d\n"
  24.395 +            "Total=%d\n"
  24.396 +            "SampleFreq=%d\n"
  24.397 +            "Channels=%d\n"
  24.398 +            "Bitrate=%d\n"
  24.399 +            "MaxBitrate=%d\n"
  24.400 +            "ChMode=%d\n"
  24.401 +            "Year=%d\n"
  24.402 +            "Level=%.4f\n"
  24.403 +            "Peak=%.4f\n",
  24.404 +            Filename,Filesize,CTime,touch,CACHE_VERSION,Frames,Total,SampleFreq,Channels,Bitrate,MaxBitrate,ChMode,Year,Level,Peak);
  24.405 +  if(Title)     fprintf(f,"Title=%s\n"    ,Title);
  24.406 +  if(Artist)    fprintf(f,"Artist=%s\n"   ,Artist);
  24.407 +  if(Album)     fprintf(f,"Album=%s\n"    ,Album);
  24.408 +  if(DecoderID) fprintf(f,"DecoderID=%s\n",cDecoders::ID2Str(DecoderID));
  24.409 +  if(FsID)      fprintf(f,"FsID=%s\n"     ,FsID);
  24.410 +  fprintf(f,"##END\n");
  24.411 +  return ferror(f)==0;
  24.412 +}
  24.413 +
  24.414 +bool cCacheData::Load(FILE *f)
  24.415 +{
  24.416 +  char buf[1024], *delimiters="=\n";
  24.417 +
  24.418 +  cFileInfo::Clear();
  24.419 +  cSongInfo::Clear();
  24.420 +  while(fgets(buf,sizeof(buf),f)) {
  24.421 +    char *ptrptr;
  24.422 +    char *name =strtok_r(buf ,delimiters,&ptrptr);
  24.423 +    char *value=strtok_r(0,delimiters,&ptrptr);
  24.424 +    if(name) {
  24.425 +      if(!strcasecmp(name,"##END")) break;
  24.426 +      if(value) {
  24.427 +        if     (!strcasecmp(name,"Filename"))   Filename  =strdup(value);
  24.428 +        else if(!strcasecmp(name,"Filesize") ||
  24.429 +                !strcasecmp(name,"Size"))       Filesize  =atoll(value);
  24.430 +        else if(!strcasecmp(name,"FsID"))       FsID      =strdup(value);
  24.431 +        else if(!strcasecmp(name,"Timestamp"))  CTime     =atol(value);
  24.432 +        else if(!strcasecmp(name,"Touch"))      touch     =atol(value);
  24.433 +        else if(!strcasecmp(name,"Version"))    version   =atoi(value);
  24.434 +        else if(!strcasecmp(name,"DecoderID"))  DecoderID =cDecoders::Str2ID(value);
  24.435 +        else if(!strcasecmp(name,"Frames"))     Frames    =atoi(value);
  24.436 +        else if(!strcasecmp(name,"Total"))      Total     =atoi(value);
  24.437 +        else if(!strcasecmp(name,"SampleFreq")) SampleFreq=atoi(value);
  24.438 +        else if(!strcasecmp(name,"Channels"))   Channels  =atoi(value);
  24.439 +        else if(!strcasecmp(name,"Bitrate"))    Bitrate   =atoi(value);
  24.440 +        else if(!strcasecmp(name,"MaxBitrate")) MaxBitrate=atoi(value);
  24.441 +        else if(!strcasecmp(name,"ChMode"))     ChMode    =atoi(value);
  24.442 +        else if(!strcasecmp(name,"Year"))       Year      =atoi(value);
  24.443 +        else if(!strcasecmp(name,"Title"))      Title     =strdup(value);
  24.444 +        else if(!strcasecmp(name,"Artist"))     Artist    =strdup(value);
  24.445 +        else if(!strcasecmp(name,"Album"))      Album     =strdup(value);
  24.446 +        else if(!strcasecmp(name,"Level"))      Level     =atof(value);
  24.447 +        else if(!strcasecmp(name,"Peak"))       Peak      =atof(value);
  24.448 +        else d(printf("cache: ignoring bad token '%s' from cache file\n",name))
  24.449 +        }
  24.450 +      }
  24.451 +    }
  24.452 +
  24.453 +  if(ferror(f) || !Filename) return false;
  24.454 +  hash=MakeHash(Filename);
  24.455 +  return true;
  24.456 +}
  24.457 +
  24.458 +// --- cInfoCache ----------------------------------------------------
  24.459 +
  24.460 +cInfoCache::cInfoCache(void)
  24.461 +{
  24.462 +  lasttime=0; modified=false;
  24.463 +  lastpurge=time(0)-(50*60);
  24.464 +}
  24.465 +
  24.466 +void cInfoCache::Cache(cSongInfo *info, cFileInfo *file)
  24.467 +{
  24.468 +  lock.Lock();
  24.469 +  cCacheData *dat=Search(file);
  24.470 +  if(dat) {
  24.471 +    dat->Create(file,info);
  24.472 +    Modified();
  24.473 +    dat->Unlock();
  24.474 +    d(printf("cache: updating infos for %s\n",file->Filename))
  24.475 +    }
  24.476 +  else {
  24.477 +    dat=new cCacheData;
  24.478 +    dat->Create(file,info);
  24.479 +    AddEntry(dat);
  24.480 +    d(printf("cache: caching infos for %s\n",file->Filename))
  24.481 +    }
  24.482 +  lock.Unlock();
  24.483 +}
  24.484 +
  24.485 +cCacheData *cInfoCache::Search(cFileInfo *file)
  24.486 +{
  24.487 +  int hash=MakeHash(file->Filename);
  24.488 +  lock.Lock();
  24.489 +  cCacheData *dat=FirstEntry(hash);
  24.490 +  while(dat) {
  24.491 +    if(dat->hash==hash && !strcmp(dat->Filename,file->Filename) && dat->Filesize==file->Filesize) {
  24.492 +      dat->Lock();
  24.493 +      if(file->FsID && dat->FsID && !strcmp(dat->FsID,"old")) { // duplicate FsID for old entries
  24.494 +        dat->FsID=strdup(file->FsID);
  24.495 +        dat->Touch(); Modified();
  24.496 +        //d(printf("adding FsID for %s\n",dat->Filename))
  24.497 +        }
  24.498 +
  24.499 +      if((!file->FsID && !dat->FsID) || (file->FsID && dat->FsID && !strcmp(dat->FsID,file->FsID))) {
  24.500 +        //d(printf("cache: found cache entry for %s\n",dat->Filename))
  24.501 +        dat->Touch(); Modified();
  24.502 +        if(dat->CTime!=file->CTime) {
  24.503 +          d(printf("cache: ctime differs, removing from cache: %s\n",dat->Filename))
  24.504 +          DelEntry(dat); dat=0;
  24.505 +          }
  24.506 +        break;
  24.507 +        }
  24.508 +      dat->Unlock();
  24.509 +      }
  24.510 +    dat=(cCacheData *)dat->Next();
  24.511 +    }
  24.512 +  lock.Unlock();
  24.513 +  return dat;
  24.514 +}
  24.515 +
  24.516 +void cInfoCache::AddEntry(cCacheData *dat)
  24.517 +{
  24.518 +  lists[dat->hash%CACHELINES].Add(dat);
  24.519 +  Modified();
  24.520 +}
  24.521 +
  24.522 +void cInfoCache::DelEntry(cCacheData *dat)
  24.523 +{
  24.524 +  dat->Lock();
  24.525 +  lists[dat->hash%CACHELINES].Del(dat);
  24.526 +  Modified();
  24.527 +}
  24.528 +
  24.529 +cCacheData *cInfoCache::FirstEntry(int hash)
  24.530 +{
  24.531 +  return lists[hash%CACHELINES].First();
  24.532 +}
  24.533 +
  24.534 +bool cInfoCache::Purge(void)
  24.535 +{
  24.536 +  time_t now=time(0);
  24.537 +  if(now-lastpurge>(60*60)) {
  24.538 +    isyslog("cleaning up id3 cache");
  24.539 +    Start();
  24.540 +    lastpurge=now;
  24.541 +    }
  24.542 +  return Active();
  24.543 +}
  24.544 +
  24.545 +void cInfoCache::Action(void)
  24.546 +{
  24.547 +  d(printf("cache: id3 cache purge thread started (pid=%d)\n",getpid()))
  24.548 +  nice(3);
  24.549 +  lock.Lock();
  24.550 +  for(int i=0,n=0 ; i<CACHELINES && Running(); i++) {
  24.551 +    cCacheData *dat=FirstEntry(i);
  24.552 +    while(dat && Running()) {
  24.553 +      cCacheData *ndat=(cCacheData *)dat->Next();
  24.554 +      if(dat->Purge()) DelEntry(dat);
  24.555 +      dat=ndat;
  24.556 +
  24.557 +      if(++n>30) {
  24.558 +        lastmod=false; n=0;
  24.559 +        lock.Unlock(); lock.Lock();    // give concurrent thread an access chance
  24.560 +        if(lastmod) dat=FirstEntry(i); // restart line, if cache changed meanwhile
  24.561 +        }
  24.562 +      }
  24.563 +    }
  24.564 +  lock.Unlock();
  24.565 +  d(printf("cache: id3 cache purge thread ended (pid=%d)\n",getpid()))
  24.566 +}
  24.567 +
  24.568 +char *cInfoCache::CacheFile(void)
  24.569 +{
  24.570 +  return AddPath(cachedir?cachedir:VideoDirectory,CACHEFILENAME);
  24.571 +}
  24.572 +
  24.573 +void cInfoCache::Save(bool force)
  24.574 +{
  24.575 +  if(!Purge() && modified && (force || time(0)>lasttime)) {
  24.576 +    char *name=CacheFile();
  24.577 +    cSafeFile f(name);
  24.578 +    free(name);
  24.579 +    if(f.Open()) {
  24.580 +      lock.Lock();
  24.581 +      fprintf(f,"## This is a generated file. DO NOT EDIT!!\n"
  24.582 +                "## This file will be OVERWRITTEN WITHOUT WARNING!!\n");
  24.583 +      for(int i=0 ; i<CACHELINES ; i++) {
  24.584 +        cCacheData *dat=FirstEntry(i);
  24.585 +        while(dat) {
  24.586 +          if(!dat->Save(f)) { i=CACHELINES+1; break; }
  24.587 +          dat=(cCacheData *)dat->Next();
  24.588 +          }
  24.589 +        }
  24.590 +      lock.Unlock();
  24.591 +      f.Close();
  24.592 +      modified=false; lasttime=time(0)+CACHESAVETIMEOUT;
  24.593 +      d(printf("cache: saved cache to file\n"))
  24.594 +      }
  24.595 +    }
  24.596 +}
  24.597 +
  24.598 +void cInfoCache::Load(void)
  24.599 +{
  24.600 +  char *name=CacheFile();
  24.601 +  if(access(name,F_OK)==0) {
  24.602 +    isyslog("loading id3 cache from %s",name);
  24.603 +    FILE *f=fopen(name,"r");
  24.604 +    if(f) {
  24.605 +      char buf[256];
  24.606 +      bool mod=false;
  24.607 +      lock.Lock();
  24.608 +      for(int i=0 ; i<CACHELINES ; i++) lists[i].Clear();
  24.609 +      while(fgets(buf,sizeof(buf),f)) {
  24.610 +        if(!strcasecmp(buf,"##BEGIN\n")) {
  24.611 +          cCacheData *dat=new cCacheData;
  24.612 +          if(dat->Load(f)) {
  24.613 +            if(dat->version!=CACHE_VERSION) {
  24.614 +              if(dat->Upgrade()) mod=true;
  24.615 +              else { delete dat; continue; }
  24.616 +              }
  24.617 +            AddEntry(dat);
  24.618 +            }
  24.619 +          else {
  24.620 +            delete dat;
  24.621 +            if(ferror(f)) {
  24.622 +              esyslog("ERROR: failed to load id3 cache");
  24.623 +              break;
  24.624 +              }
  24.625 +            }
  24.626 +          }
  24.627 +        }
  24.628 +      lock.Unlock();
  24.629 +      fclose(f);
  24.630 +      modified=false; if(mod) Modified();
  24.631 +      }
  24.632 +    else LOG_ERROR_STR(name);
  24.633 +    }
  24.634 +  free(name);
  24.635 +}
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/decoder.h	Sat Dec 29 14:47:40 2007 +0100
    25.3 @@ -0,0 +1,161 @@
    25.4 +/*
    25.5 + * MP3/MPlayer plugin to VDR (C++)
    25.6 + *
    25.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    25.8 + *
    25.9 + * This code is free software; you can redistribute it and/or
   25.10 + * modify it under the terms of the GNU General Public License
   25.11 + * as published by the Free Software Foundation; either version 2
   25.12 + * of the License, or (at your option) any later version.
   25.13 + *
   25.14 + * This code is distributed in the hope that it will be useful,
   25.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   25.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   25.17 + * GNU General Public License for more details.
   25.18 + *
   25.19 + * You should have received a copy of the GNU General Public License
   25.20 + * along with this program; if not, write to the Free Software
   25.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   25.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   25.23 + */
   25.24 +
   25.25 +#ifndef ___DECODER_H
   25.26 +#define ___DECODER_H
   25.27 +
   25.28 +#include <vdr/thread.h>
   25.29 +#include <vdr/tools.h>
   25.30 +
   25.31 +// ----------------------------------------------------------------
   25.32 +
   25.33 +class cCacheData;
   25.34 +class cFileObj;
   25.35 +
   25.36 +extern int MakeHashBuff(const char *buff, int len);
   25.37 +#define MakeHash(str) MakeHashBuff((str),strlen(str))
   25.38 +
   25.39 +// ----------------------------------------------------------------
   25.40 +
   25.41 +class cSongInfo {
   25.42 +private:
   25.43 +  bool infoDone;
   25.44 +protected:
   25.45 +  void Clear(void);
   25.46 +  void Set(cSongInfo *si);
   25.47 +  void FakeTitle(const char *filename, const char *extention=0);
   25.48 +  void InfoDone(void) { infoDone=true; }
   25.49 +public:
   25.50 +  cSongInfo(void);
   25.51 +  ~cSongInfo();
   25.52 +  bool HasInfo(void) { return infoDone; }
   25.53 +  // Song
   25.54 +  char *Title, *Artist, *Album;
   25.55 +  int Year, Frames, Total;
   25.56 +  int SampleFreq, Channels, Bitrate, MaxBitrate, ChMode;
   25.57 +  // Normalize
   25.58 +  double Level, Peak;
   25.59 +  // Decoder
   25.60 +  int DecoderID;
   25.61 +  };
   25.62 +
   25.63 +// ----------------------------------------------------------------
   25.64 +
   25.65 +class cFileInfo {
   25.66 +private:
   25.67 +  bool infoDone;
   25.68 +  int removable;
   25.69 +protected:
   25.70 +  void Clear(void);
   25.71 +  void Set(cFileInfo *fi);
   25.72 +  void InfoDone(void) { infoDone=true; }
   25.73 +public:
   25.74 +  cFileInfo(void);
   25.75 +  cFileInfo(const char *Name);
   25.76 +  ~cFileInfo();
   25.77 +  bool FileInfo(bool log=true);
   25.78 +  bool HasInfo(void) { return infoDone; }
   25.79 +  bool Removable(void);
   25.80 +  //
   25.81 +  char *Filename, *FsID;
   25.82 +  unsigned long long Filesize;
   25.83 +  time_t CTime;
   25.84 +  long FsType;
   25.85 +  };
   25.86 +
   25.87 +// ----------------------------------------------------------------
   25.88 +
   25.89 +class cPlayInfo {
   25.90 +public:
   25.91 +  int Index, Total;
   25.92 +  };
   25.93 +
   25.94 +// ----------------------------------------------------------------
   25.95 +
   25.96 +class cDecoder {
   25.97 +protected:
   25.98 +  char *filename;
   25.99 +  cMutex lock, locklock;
  25.100 +  int locked;
  25.101 +  bool urgentLock;
  25.102 +  //
  25.103 +  cPlayInfo pi;
  25.104 +  bool playing;
  25.105 +  //
  25.106 +  void Lock(bool urgent=false);
  25.107 +  void Unlock(void);
  25.108 +  bool TryLock(void);
  25.109 +public:
  25.110 +  cDecoder(const char *Filename);
  25.111 +  virtual ~cDecoder();
  25.112 +  //
  25.113 +  virtual cFileInfo *FileInfo(void) { return 0; }
  25.114 +  virtual cSongInfo *SongInfo(bool get) { return 0; }
  25.115 +  virtual cPlayInfo *PlayInfo(void) { return 0; }
  25.116 +  //
  25.117 +  virtual bool Valid(void)=0;
  25.118 +  virtual bool IsStream(void) { return false; }
  25.119 +  virtual bool Start(void)=0;
  25.120 +  virtual bool Stop(void)=0;
  25.121 +  virtual bool Skip(int Seconds, float bsecs) { return false; }
  25.122 +  virtual struct Decode *Decode(void)=0;
  25.123 +  };
  25.124 +  
  25.125 +// ----------------------------------------------------------------
  25.126 +
  25.127 +class cDecoders {
  25.128 +public:
  25.129 +  static cDecoder *FindDecoder(cFileObj *Obj);
  25.130 +  static const char *ID2Str(int id);
  25.131 +  static int Str2ID(const char *str);
  25.132 +  };
  25.133 +
  25.134 +// ----------------------------------------------------------------
  25.135 +
  25.136 +#define CACHELINES 32
  25.137 +
  25.138 +class cInfoCache : public cThread {
  25.139 +private:
  25.140 +  cMutex lock;
  25.141 +  cList<cCacheData> lists[CACHELINES];
  25.142 +  time_t lasttime, lastpurge;
  25.143 +  bool modified, lastmod;
  25.144 +  //
  25.145 +  char *CacheFile(void);
  25.146 +  void AddEntry(cCacheData *dat);
  25.147 +  void DelEntry(cCacheData *dat);
  25.148 +  cCacheData *FirstEntry(int hash);
  25.149 +  void Modified(void) { modified=lastmod=true; }
  25.150 +protected:
  25.151 +  virtual void Action(void);
  25.152 +public:
  25.153 +  cInfoCache(void);
  25.154 +  void Save(bool force=false);
  25.155 +  void Load(void);
  25.156 +  bool Purge(void);
  25.157 +  void Cache(cSongInfo *info, cFileInfo *file);
  25.158 +  cCacheData *Search(cFileInfo *file);
  25.159 +  };
  25.160 +
  25.161 +extern cInfoCache InfoCache;
  25.162 +extern char *cachedir;
  25.163 +
  25.164 +#endif //___DECODER_H
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/examples/image_convert.sh.example	Sat Dec 29 14:47:40 2007 +0100
    26.3 @@ -0,0 +1,87 @@
    26.4 +#!/bin/bash
    26.5 +#
    26.6 +# requires: ...topnm, pnmscale, pnmcomp, ppmntsc, ppmtoy4m, mpeg2enc
    26.7 +#
    26.8 +
    26.9 +# video format. pal or ntsc
   26.10 +FORMAT=pal
   26.11 +
   26.12 +# target image width/height (taking into account visible screen area)
   26.13 +if [ "$FORMAT" = "ntsc" ]; then
   26.14 +  TW=600
   26.15 +  TH=420
   26.16 +else
   26.17 +  TW=632
   26.18 +  TH=512
   26.19 +fi
   26.20 +
   26.21 +TMP=/tmp/image_convert.$$.pnm
   26.22 +IMG=$1
   26.23 +MPG=$2
   26.24 +
   26.25 +DIR=`dirname "$MPG"`
   26.26 +if [ ! -d "$DIR" ]; then
   26.27 +  mkdir -p "$DIR"
   26.28 +fi
   26.29 +#
   26.30 +# get the file type and set the according converter to PNM
   26.31 +#
   26.32 +FILE_TYPE=`file -i -L -b "$IMG" 2>/dev/null | cut -f2 -d/`
   26.33 +case "$FILE_TYPE" in
   26.34 +  jpg | jpeg)
   26.35 +  TO_PNM=jpegtopnm
   26.36 +  ;;
   26.37 +  tiff)
   26.38 +  TO_PNM=tifftopnm
   26.39 +  ;;
   26.40 +  bmp | x-bmp)
   26.41 +  TO_PNM=bmptoppm
   26.42 +  ;;
   26.43 +  png | x-png)
   26.44 +  TO_PNM=pngtopnm
   26.45 +  ;;
   26.46 +  Netpbm | pnm | x-portable-pixmap)
   26.47 +  TO_PNM=cat
   26.48 +  ;;
   26.49 +  gif)
   26.50 +  TO_PNM=giftopnm
   26.51 +  ;;
   26.52 +  *)
   26.53 +  echo "filetype '$FILE_TYPE' is not supported"
   26.54 +  exit 1
   26.55 +  ;;
   26.56 +esac
   26.57 +#
   26.58 +# 'chroma subsampling mode' mjpegtools >= 1.8.0
   26.59 +#
   26.60 +SUBSAMPLINGMODE=""
   26.61 +if ppmtoy4m -h | egrep -q "'420mpeg2'"; then
   26.62 +    SUBSAMPLINGMODE="-S 420mpeg2"
   26.63 +fi
   26.64 +#
   26.65 +# extract the image size & compute scale value
   26.66 +#
   26.67 +LANG=C # get the decimal point right
   26.68 +$TO_PNM "$IMG" >$TMP 2>/dev/null
   26.69 +S=`pnmfile $TMP | awk '{ printf "%d %d ",$4,$6 }'`
   26.70 +S=`echo $S $TW $TH | awk '{ sw=$3/$1; sh=$4/$2; s=(sw<sh)?sw:sh; printf "%.4f\n",(s>1)?1.0:s; }'`
   26.71 +#
   26.72 +# now run the conversion
   26.73 +#
   26.74 +if [ "$FORMAT" = "ntsc" ]; then
   26.75 +  pnmscale $S $TMP | \
   26.76 +    pnmpad -black -width 704 -height 480 | \
   26.77 +    ppmntsc | \
   26.78 +    ppmtoy4m -v 0 -n 1 -r -F 30000:1001 $SUBSAMPLINGMODE | \
   26.79 +    mpeg2enc -f 7 -T 90 -F 4 -nn -a 2 -v 0 -o "$MPG"
   26.80 +else
   26.81 +  pnmscale $S $TMP | \
   26.82 +    pnmpad -black -width 704 -height 576 | \
   26.83 +    ppmntsc --pal | \
   26.84 +    ppmtoy4m -v 0 -n 1 -r -F 25:1 $SUBSAMPLINGMODE | \
   26.85 +    mpeg2enc -f 7 -T 90 -F 3 -np -a 2 -v 0 -o "$MPG"
   26.86 +fi
   26.87 +#
   26.88 +# cleanup
   26.89 +#
   26.90 +rm $TMP
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/examples/mount.sh.example	Sat Dec 29 14:47:40 2007 +0100
    27.3 @@ -0,0 +1,34 @@
    27.4 +#!/bin/bash
    27.5 +#
    27.6 +# This script is called from VDR to mount/unmount/eject
    27.7 +# the sources for MP3 play.
    27.8 +#
    27.9 +# argument 1: wanted action, one of mount,unmount,eject,status
   27.10 +# argument 2: mountpoint to act on
   27.11 +#
   27.12 +# mount,unmount,eject must return 0 if succeeded, 1 if failed
   27.13 +# status must return 0 if device is mounted, 1 if not
   27.14 +#
   27.15 +
   27.16 +action="$1"
   27.17 +path="$2"
   27.18 +
   27.19 +case "$action" in
   27.20 +mount)
   27.21 +  eject -t "$path" || exit 1         # close the tray
   27.22 +  mount "$path" || exit 1            # mount it
   27.23 +  ;;
   27.24 +unmount)
   27.25 +  umount "$path" || exit 1           # unmount it
   27.26 +  ;;
   27.27 +eject)
   27.28 +  eject "$path" || exit 1            # eject disk
   27.29 +  ;;
   27.30 +status)
   27.31 +  cat /proc/mounts | grep -q "$path" # check if mounted
   27.32 +  if [ $? -ne 0 ]; then              # not mounted ...
   27.33 +    exit 1
   27.34 +  fi
   27.35 +esac
   27.36 +
   27.37 +exit 0
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/examples/mp3sources.conf.example	Sat Dec 29 14:47:40 2007 +0100
    28.3 @@ -0,0 +1,4 @@
    28.4 +/mp3;Locale Platte;0;*.mp3/*.ogg/*.wav
    28.5 +/mnt/nfs/mp3;Remote NFS;0
    28.6 +/mnt/cdrom;CDROM;1
    28.7 +/mnt/cdfs;CD-Audio;1;*.wav
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/examples/mplayer.sh.example	Sat Dec 29 14:47:40 2007 +0100
    29.3 @@ -0,0 +1,57 @@
    29.4 +#!/bin/bash
    29.5 +#
    29.6 +# This script is called from VDR to start MPlayer
    29.7 +#
    29.8 +# argument 1: the file to play
    29.9 +# argument 2: (optional) the phrase SLAVE if SlaveMode is enabled
   29.10 +# argument 3: (optional) the phrase AID x to select audio stream x
   29.11 +
   29.12 +# where to find mplayer
   29.13 +MPLAYER="mplayer"
   29.14 +
   29.15 +# mplayer options, -vc will be added below
   29.16 +# add "-lircconf <lircrc>" to enable LIRC support
   29.17 +OPTS="-vo mpegpes"
   29.18 +
   29.19 +# mplayer options for SlaveMode
   29.20 +SLAVE="-slave -quiet -nolirc"
   29.21 +
   29.22 +#####################
   29.23 +
   29.24 +FILE=$1
   29.25 +case "$FILE" in
   29.26 +*.pls | *.m3u)
   29.27 +  popt="-playlist"
   29.28 +  first=`grep -v -m1 "^#" $FILE`
   29.29 +  type=`file "$first"`
   29.30 +  ;;
   29.31 +*)
   29.32 +  type=`file "$FILE"`
   29.33 +  ;;
   29.34 +esac
   29.35 +
   29.36 +while shift; do
   29.37 +  if [ "$1" = "SLAVE" ]; then
   29.38 +    sopt=$SLAVE
   29.39 +  elif [ "$1" = "AID" ]; then
   29.40 +    aopt="-aid $2"
   29.41 +    shift
   29.42 +  fi
   29.43 +done
   29.44 +
   29.45 +case "$type" in
   29.46 +*AVI*)
   29.47 +  VC="ffdivx"
   29.48 +  ;;
   29.49 +*MPEG*)
   29.50 +  VC="mpegpes"
   29.51 +  ;;
   29.52 +*)
   29.53 +  echo "Unknown video file format $type"
   29.54 +  echo "Edit mplayer.sh to support this file type"
   29.55 +  exit 1
   29.56 +  ;;
   29.57 +esac
   29.58 +
   29.59 +exec $MPLAYER $OPTS -vc $VC $sopt $aopt $popt "$FILE"
   29.60 +
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/examples/network.sh.example	Sat Dec 29 14:47:40 2007 +0100
    30.3 @@ -0,0 +1,19 @@
    30.4 +#!/bin/bash
    30.5 +#
    30.6 +# This script is called from VDR before & after network access
    30.7 +#
    30.8 +# argument 1: wanted action, one of up,down
    30.9 +#
   30.10 +
   30.11 +action="$1"
   30.12 +
   30.13 +case "$action" in
   30.14 +up)
   30.15 +  echo "starting dialin"
   30.16 +  ;;
   30.17 +down)
   30.18 +  echo "hangup"
   30.19 +  ;;
   30.20 +esac
   30.21 +
   30.22 +exit 0
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/i18n.c	Sat Dec 29 14:47:40 2007 +0100
    31.3 @@ -0,0 +1,2309 @@
    31.4 +/*
    31.5 + * MP3/MPlayer plugin to VDR (C++)
    31.6 + *
    31.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    31.8 + *
    31.9 + * This code is free software; you can redistribute it and/or
   31.10 + * modify it under the terms of the GNU General Public License
   31.11 + * as published by the Free Software Foundation; either version 2
   31.12 + * of the License, or (at your option) any later version.
   31.13 + *
   31.14 + * This code is distributed in the hope that it will be useful,
   31.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   31.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   31.17 + * GNU General Public License for more details.
   31.18 + *
   31.19 + * You should have received a copy of the GNU General Public License
   31.20 + * along with this program; if not, write to the Free Software
   31.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   31.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   31.23 + */
   31.24 +
   31.25 +#include "common.h"
   31.26 +#include "i18n.h"
   31.27 +
   31.28 +const char *i18n_name = 0;
   31.29 +
   31.30 +const tI18nPhrase Phrases[] = {
   31.31 +/*
   31.32 + * French     translations provided by Maxime Guilbot <maxime.guilbot@orange.fr>
   31.33 + *               additions provided by Pierre-Henri Beguin <ph.beguin@free.fr>
   31.34 + *               additions provided by Jerome Rousset <zekje@hotmail.com>
   31.35 + * Slovenian  translations provided by Matjaz Thaler <matjaz.thaler@guest.arnes.si>
   31.36 + * Greek      translations provided by Dimitrios Dimitrakos <mail@dimitrios.de>
   31.37 + * Swedish    translations provided by Jan Ekholm <chakie@infa.abo.fi>
   31.38 + * Spanish & 
   31.39 + * Catalan    translations provided by Ramon Roca <ramon.roca@xcombo.com>
   31.40 + * Finnish    translations provided by Rolf Ahrenberg <rahrenbe@cc.hut.fi>
   31.41 + *               additions provided by Ville Skyttä <ville.skytta@iki.fi>
   31.42 + * Russian    translations provided by Vyacheslav Dikonov <sdiconov@mail.ru>
   31.43 +*/
   31.44 +  { "Parent",
   31.45 +    "Zurück",
   31.46 +    "Nazaj",
   31.47 +    "", // TODO
   31.48 +    "", // TODO
   31.49 +    "", // TODO
   31.50 +    "Parent",
   31.51 +    "", // TODO
   31.52 +    "Takaisin",
   31.53 +    "", // TODO
   31.54 +    "Predecesor", 
   31.55 +    "Piso",
   31.56 +    "Tillbaka",
   31.57 +    "", // TODO
   31.58 +    "", // TODO
   31.59 +    "Anterior",
   31.60 +#if APIVERSNUM >= 10302
   31.61 +    "²ÒÕàå",
   31.62 +#endif
   31.63 +  },
   31.64 +  { "Select",
   31.65 +    "Auswählen",
   31.66 +    "Izberi",
   31.67 +    "", // TODO
   31.68 +    "", // TODO
   31.69 +    "", // TODO
   31.70 +    "Sélectionner",
   31.71 +    "", // TODO
   31.72 +    "Valitse",
   31.73 +    "", // TODO
   31.74 +    "Seleccionar", 
   31.75 +    "Epilogi",
   31.76 +    "Välj",
   31.77 +    "", // TODO
   31.78 +    "", // TODO
   31.79 +    "Escollir",
   31.80 +#if APIVERSNUM >= 10302
   31.81 +    "²ëÑàÐâì",
   31.82 +#endif
   31.83 +  },
   31.84 +  { "Error scanning directory!",
   31.85 +    "Fehler beim Lesen des Verzeichnisses!",
   31.86 +    "Napaka pri pregledovanju direktorija!",
   31.87 +    "", // TODO
   31.88 +    "", // TODO
   31.89 +    "", // TODO
   31.90 +    "Erreur de parcours du répertoire!",
   31.91 +    "", // TODO
   31.92 +    "Hakemiston avaaminen epäonnistui!",
   31.93 +    "", // TODO
   31.94 +    "¡Error al leer una carpeta!", 
   31.95 +    "Lathos stin sarosi tou fakelou!",
   31.96 +    "Kunde inte läsa katalogen!",
   31.97 +    "", // TODO
   31.98 +    "", // TODO
   31.99 +    "Error al llegir una carpeta!",
  31.100 +#if APIVERSNUM >= 10302
  31.101 +    "¾èØÑÚÐ áÚÐÝØàÞÒÐÝØï ÚÐâÐÛÞÓÐ!",
  31.102 +#endif
  31.103 +  },
  31.104 +  { "Source",
  31.105 +    "Datenträger",
  31.106 +    "Izvor",
  31.107 +    "", // TODO
  31.108 +    "", // TODO
  31.109 +    "", // TODO
  31.110 +    "Source",
  31.111 +    "", // TODO
  31.112 +    "Lähde",
  31.113 +    "", // TODO
  31.114 +    "Origen", 
  31.115 +    "Pigi",
  31.116 +    "Källa",
  31.117 +    "", // TODO
  31.118 +    "", // TODO
  31.119 +    "Orígen",
  31.120 +#if APIVERSNUM >= 10302
  31.121 +    "´ØáÚ",
  31.122 +#endif
  31.123 +  },
  31.124 +  { "Mount",
  31.125 +    "Einbinden",
  31.126 +    "Priklopi",
  31.127 +    "", // TODO
  31.128 +    "", // TODO
  31.129 +    "", // TODO
  31.130 +    "Monter",
  31.131 +    "", // TODO
  31.132 +    "Kiinnitä",
  31.133 +    "", // TODO
  31.134 +    "Mount", 
  31.135 +    "Sindesi",
  31.136 +    "Montera",
  31.137 +    "", // TODO
  31.138 +    "", // TODO
  31.139 +    "Mount",
  31.140 +#if APIVERSNUM >= 10302
  31.141 +    "¿ÞÔÚÛîçØâì",
  31.142 +#endif
  31.143 +  },
  31.144 +  { "Unmount",
  31.145 +    "Aushängen",
  31.146 +    "Izklopi",
  31.147 +    "", // TODO
  31.148 +    "", // TODO
  31.149 +    "", // TODO
  31.150 +    "Démonter",
  31.151 +    "", // TODO
  31.152 +    "Irrota",
  31.153 +    "", // TODO
  31.154 +    "Unmount",
  31.155 +    "Aposindesi",
  31.156 +    "Avmontera",
  31.157 +    "", // TODO
  31.158 +    "", // TODO
  31.159 +    "Unmount",
  31.160 +#if APIVERSNUM >= 10302
  31.161 +    "¾âÚÛîçØâì",
  31.162 +#endif
  31.163 +  },
  31.164 +  { "Eject",
  31.165 +    "Auswerfen",
  31.166 +    "", // TODO
  31.167 +    "", // TODO
  31.168 +    "", // TODO
  31.169 +    "", // TODO
  31.170 +    "", // TODO
  31.171 +    "", // TODO
  31.172 +    "Avaa",
  31.173 +    "", // TODO
  31.174 +    "", // TODO
  31.175 +    "", // TODO
  31.176 +    "", // TODO
  31.177 +    "", // TODO
  31.178 +    "", // TODO
  31.179 +    "", // TODO
  31.180 +#if APIVERSNUM >= 10302
  31.181 +    "", // TODO
  31.182 +#endif
  31.183 +  },
  31.184 +  { "Selected source is not mounted!",
  31.185 +    "Ausgewählte Datenträger ist nicht eingebunden!",
  31.186 +    "Izbran izvor ni prikljucen!",
  31.187 +    "", // TODO
  31.188 +    "", // TODO
  31.189 +    "", // TODO
  31.190 +    "Source sélectionnée non montée!",
  31.191 +    "", // TODO
  31.192 +    "Valittu lähde ei ole kiinnitetty!",
  31.193 +    "", // TODO
  31.194 +    "¡El origen deseado no està montado!", 
  31.195 +    "Epilegmeni Pigi den ine sindemeni!",
  31.196 +    "Den valda källan är inte monterad!",
  31.197 +    "", // TODO
  31.198 +    "", // TODO
  31.199 +    "l'Origen sel.leccionat no està muntat!",
  31.200 +#if APIVERSNUM >= 10302
  31.201 +    "²ëÑàÐÝÝëÙ ÔØáÚ ÝÕ ßÞÔÚÛîçÕÝ!",
  31.202 +#endif
  31.203 +  },
  31.204 +  { "Mount succeeded",
  31.205 +    "Einbinden erfolgreich",
  31.206 +    "Priklop izveden",
  31.207 +    "", // TODO
  31.208 +    "", // TODO
  31.209 +    "", // TODO
  31.210 +    "Montage réussi",
  31.211 +    "", // TODO
  31.212 +    "Kiinnittäminen onnistui",
  31.213 +    "", // TODO
  31.214 +    "mount correcto", 
  31.215 +    "I sindesi petixe",
  31.216 +    "Monteringen lyckades",
  31.217 +    "", // TODO
  31.218 +    "", // TODO
  31.219 +    "mount correcte",
  31.220 +#if APIVERSNUM >= 10302
  31.221 +    "´ØáÚ ßÞÔÚÛîçÕÝ",
  31.222 +#endif
  31.223 +  },
  31.224 +  { "Mount failed!",
  31.225 +    "Einbinden fehlgeschlagen!",
  31.226 +    "Napaka pri priklopu!",
  31.227 +    "", // TODO
  31.228 +    "", // TODO
  31.229 +    "", // TODO
  31.230 +    "Echec du montage!",
  31.231 +    "", // TODO
  31.232 +    "Kiinnittäminen epäonnistui!",
  31.233 +    "", // TODO
  31.234 +    "¡No he podido montar!", 
  31.235 +    "I sindesi apetixe!",
  31.236 +    "Monteringen misslyckades!",
  31.237 +    "", // TODO
  31.238 +    "", // TODO
  31.239 +    "No he pogut muntar!",
  31.240 +#if APIVERSNUM >= 10302
  31.241 +    "¾èØÑÚÐ ßÞÔÚÛîçÕÝØï ÔØáÚÐ!",
  31.242 +#endif
  31.243 +  },
  31.244 +  { "Unmount succeeded",
  31.245 +    "Aushängen erfolgreich",
  31.246 +    "Izklop izveden",
  31.247 +    "", // TODO
  31.248 +    "", // TODO
  31.249 +    "", // TODO
  31.250 +    "Démontage réussi",
  31.251 +    "", // TODO
  31.252 +    "Irrottaminen onnistui",
  31.253 +    "", // TODO
  31.254 +    "Éxito al unmount", 
  31.255 +    "I aposindesi itan epitixisi",
  31.256 +    "Avmonteringen lyckades",
  31.257 +    "", // TODO
  31.258 +    "", // TODO
  31.259 +    "unmount amb èxit",
  31.260 +#if APIVERSNUM >= 10302
  31.261 +    "´ØáÚ ÞâÚÛîçÕÝ",
  31.262 +#endif
  31.263 +  },
  31.264 +  { "Unmount failed!",
  31.265 +    "Aushängen fehlgeschlagen!",
  31.266 +    "Napaka pri izklopu!",
  31.267 +    "", // TODO
  31.268 +    "", // TODO
  31.269 +    "", // TODO
  31.270 +    "Echec du démontage!",
  31.271 +    "", // TODO
  31.272 +    "Irrottaminen epäonnistui!",
  31.273 +    "", // TODO
  31.274 +    "¡No puedo desmontar!", 
  31.275 +    "I aposindesi den itan epitixis!",
  31.276 +    "Avmonteringen misslyckades!",
  31.277 +    "", // TODO
  31.278 +    "", // TODO
  31.279 +    "No puc desmontar!",
  31.280 +#if APIVERSNUM >= 10302
  31.281 +    "¾èØÑÚÐ ÞâÚÛîçÕÝØï ÔØáÚÐ!",
  31.282 +#endif
  31.283 +  },
  31.284 +  { "Eject failed!",
  31.285 +    "Auswerfen fehlgeschlagen!",
  31.286 +    "Napaka pri izmetu!",
  31.287 +    "", // TODO
  31.288 +    "", // TODO
  31.289 +    "", // TODO
  31.290 +    "Echec de l'éjection!",
  31.291 +    "", // TODO
  31.292 +    "Aseman avaaminen epäonnistui!",
  31.293 +    "", // TODO
  31.294 +    "¡No puedo expulsar!", 
  31.295 +    "I apovoli apetixe!",
  31.296 +    "Mata ut!",
  31.297 +    "", // TODO
  31.298 +    "", // TODO
  31.299 +    "No puc expulsar!",
  31.300 +#if APIVERSNUM >= 10302
  31.301 +    "¾èØÑÚÐ Ø×ÒÛÕçÕÝØï ÔØáÚÐ!",
  31.302 +#endif
  31.303 +  },
  31.304 +  { "Hide mainmenu entry",
  31.305 +    "Hauptmenüeintrag verstecken",
  31.306 +    "", // TODO
  31.307 +    "", // TODO
  31.308 +    "", // TODO
  31.309 +    "", // TODO
  31.310 +    "Cacher l entrée du menu",
  31.311 +    "", // TODO
  31.312 +    "Piilota valinta päävalikosta",
  31.313 +    "", // TODO
  31.314 +    "", // TODO
  31.315 +    "", // TODO
  31.316 +    "", // TODO
  31.317 +    "", // TODO
  31.318 +    "", // TODO
  31.319 +    "", // TODO
  31.320 +#if APIVERSNUM >= 10302
  31.321 +    "ÁÚàëâì ÚÞÜÐÝÔã Ò ÜÕÝî",
  31.322 +#endif
  31.323 +  },
  31.324 +
  31.325 +// start of MP3 specific phrases
  31.326 +
  31.327 +  { "MP3",
  31.328 +    "MP3",
  31.329 +    "MP3",
  31.330 +    "MP3",
  31.331 +    "MP3",
  31.332 +    "MP3",
  31.333 +    "MP3",
  31.334 +    "MP3",
  31.335 +    "MP3-soitin",
  31.336 +    "MP3",
  31.337 +    "MP3",
  31.338 +    "MP3",
  31.339 +    "MP3",
  31.340 +    "MP3",
  31.341 +    "MP3",
  31.342 +    "MP3",
  31.343 +#if APIVERSNUM >= 10302
  31.344 +    "¿àÞØÓàëÒÐâÕÛì MP3",
  31.345 +#endif
  31.346 +  },
  31.347 +  { "A versatile audio player",
  31.348 +    "Ein vielseitiger Audio-Player",
  31.349 +    "", // TODO
  31.350 +    "", // TODO
  31.351 +    "", // TODO
  31.352 +    "", // TODO
  31.353 +    "Lecteur multiformats",
  31.354 +    "", // TODO
  31.355 +    "Monipuolinen audiosoitin",
  31.356 +    "", // TODO
  31.357 +    "", // TODO
  31.358 +    "", // TODO
  31.359 +    "", // TODO
  31.360 +    "", // TODO
  31.361 +    "", // TODO
  31.362 +    "", // TODO
  31.363 +#if APIVERSNUM >= 10302
  31.364 +    "¿àÞØÓàëÒÐâÕÛì ÐãÔØÞäÐÙÛÞÒ",
  31.365 +#endif
  31.366 +  },
  31.367 +  { "Browse",
  31.368 +    "Blättern",
  31.369 +    "Isci",
  31.370 +    "", // TODO
  31.371 +    "", // TODO
  31.372 +    "", // TODO
  31.373 +    "Parcourir",
  31.374 +    "", // TODO
  31.375 +    "Selaa",
  31.376 +    "", // TODO
  31.377 +    "Explorar",
  31.378 +    "Selida",
  31.379 +    "Bläddra",
  31.380 +    "", // TODO
  31.381 +    "", // TODO
  31.382 +    "Explorar",
  31.383 +#if APIVERSNUM >= 10302
  31.384 +    "ºÐâÐÛÞÓØ",
  31.385 +#endif
  31.386 +  },
  31.387 +  { "MP3 source",
  31.388 +    "MP3 Datenträger",
  31.389 +    "MP3 izvor",
  31.390 +    "", // TODO
  31.391 +    "", // TODO
  31.392 +    "", // TODO
  31.393 +    "Source MP3",
  31.394 +    "", // TODO
  31.395 +    "MP3-lähde",
  31.396 +    "", // TODO
  31.397 +    "Origen MP3", 
  31.398 +    "Pigi MP3",
  31.399 +    "MP3 källa",
  31.400 +    "", // TODO
  31.401 +    "", // TODO
  31.402 +    "Orígen MP3",
  31.403 +#if APIVERSNUM >= 10302
  31.404 +    "´ØáÚ MP3",
  31.405 +#endif
  31.406 +  },
  31.407 +  { "Rename",
  31.408 +    "Umbenennen",
  31.409 +    "Preimenuj",
  31.410 +    "", // TODO
  31.411 +    "", // TODO
  31.412 +    "", // TODO
  31.413 +    "Renommer",
  31.414 +    "", // TODO
  31.415 +    "Nimeä",
  31.416 +    "", // TODO
  31.417 +    "Renombrar", 
  31.418 +    "Alagi Onomatos",
  31.419 +    "Döp om",
  31.420 +    "", // TODO
  31.421 +    "", // TODO
  31.422 +    "Reanomenar",
  31.423 +#if APIVERSNUM >= 10302
  31.424 +    "¿ÕàÕØÜÕÝÞÒÐâì",
  31.425 +#endif
  31.426 +  },
  31.427 +  { "Add",
  31.428 +    "Hinzufügen",
  31.429 +    "Dodaj",
  31.430 +    "", // TODO
  31.431 +    "", // TODO
  31.432 +    "", // TODO
  31.433 +    "Ajouter",
  31.434 +    "", // TODO
  31.435 +    "Lisää",
  31.436 +    "", // TODO
  31.437 +    "Añadir", 
  31.438 +    "Prosthesi",
  31.439 +    "Lägg till",
  31.440 +    "", // TODO
  31.441 +    "", // TODO
  31.442 +    "Afegir",
  31.443 +#if APIVERSNUM >= 10302
  31.444 +    "´ÞÑÐÒØâì",
  31.445 +#endif
  31.446 +  },
  31.447 +  { "Remove",
  31.448 +    "Entfernen",
  31.449 +    "Odstrani",
  31.450 +    "", // TODO
  31.451 +    "", // TODO
  31.452 +    "", // TODO
  31.453 +    "Enlever",
  31.454 +    "", // TODO
  31.455 +    "Poista",
  31.456 +    "", // TODO
  31.457 +    "Borrar", 
  31.458 +    "Aferesi",
  31.459 +    "Radera",
  31.460 +    "", // TODO
  31.461 +    "", // TODO
  31.462 +    "Esborrar",
  31.463 +#if APIVERSNUM >= 10302
  31.464 +    "ÃÔÐÛØâì",
  31.465 +#endif
  31.466 +  },
  31.467 +  { "Play all",
  31.468 +    "Alle abspielen",
  31.469 +    "Predvajaj vse",
  31.470 +    "", // TODO
  31.471 +    "", // TODO
  31.472 +    "", // TODO
  31.473 +    "Jouer tous",
  31.474 +    "", // TODO
  31.475 +    "Soita kaikki",
  31.476 +    "", // TODO
  31.477 +    "Reproducirlo todo", 
  31.478 +    "Peksimo olon",
  31.479 +    "Spela alla",
  31.480 +    "", // TODO
  31.481 +    "", // TODO
  31.482 +    "Escoltar-ho tot",
  31.483 +#if APIVERSNUM >= 10302
  31.484 +    "²ÞáßàÞØ×ÒÕáâØ Òáñ",
  31.485 +#endif
  31.486 +  },
  31.487 +  { "Add all",
  31.488 +    "Alle hinzufügen",
  31.489 +    "Dodaj vse",
  31.490 +    "", // TODO
  31.491 +    "", // TODO
  31.492 +    "", // TODO
  31.493 +    "Ajouter tous",
  31.494 +    "", // TODO
  31.495 +    "Lisää kaikki",
  31.496 +    "", // TODO
  31.497 +    "Añadir todo", 
  31.498 +    "Prosthesi olon",
  31.499 +    "Lägg till alla",
  31.500 +    "", // TODO
  31.501 +    "", // TODO
  31.502 +    "Afegir-ho tot",
  31.503 +#if APIVERSNUM >= 10302
  31.504 +    "´ÞÑÐÒØâì Òáñ",
  31.505 +#endif
  31.506 +  },
  31.507 +  { "Empty directory!",
  31.508 +    "Leeres Verzeichnis!",
  31.509 +    "Direktorij prazen!",
  31.510 +    "", // TODO
  31.511 +    "", // TODO
  31.512 +    "", // TODO
  31.513 +    "Répertoire vide!",
  31.514 +    "", // TODO
  31.515 +    "Hakemisto on tyhjä!",
  31.516 +    "", // TODO
  31.517 +    "¡Carpeta vacía!", 
  31.518 +    "Adios fakelos!",
  31.519 +    "Tom katalog!",
  31.520 +    "", // TODO
  31.521 +    "", // TODO
  31.522 +    "Carpeta buida!",
  31.523 +#if APIVERSNUM >= 10302
  31.524 +    "¿ãáâÞÙ ÚÐâÐÛÞÓ!",
  31.525 +#endif
  31.526 +  },
  31.527 +  { "Add recursivly?",
  31.528 +    "Recursiv hinzufügen?",
  31.529 +    "Rekurzivno dodaj?",
  31.530 +    "", // TODO
  31.531 +    "", // TODO
  31.532 +    "", // TODO
  31.533 +    "Ajouter récursivement?",
  31.534 +    "", // TODO
  31.535 +    "Lisää rekursiivisesti?",
  31.536 +    "", // TODO
  31.537 +    "¿Añadir recursivamente?", 
  31.538 +    "Prosthesi recursiv?",
  31.539 +    "Lägg till rekursivt?",
  31.540 +    "", // TODO
  31.541 +    "", // TODO
  31.542 +    "Afegir recursivament?",
  31.543 +#if APIVERSNUM >= 10302
  31.544 +    "´ÞÑÐÒØâì àÕÚãàáØÒÝÞ?",
  31.545 +#endif
  31.546 +  },
  31.547 +  { "Remove entry?",
  31.548 +    "Eintrag entfernen?",
  31.549 +    "Odstrani izbrano?",
  31.550 +    "", // TODO
  31.551 +    "", // TODO
  31.552 +    "", // TODO
  31.553 +    "Enlever titre?",
  31.554 +    "", // TODO
  31.555 +    "Poistetaanko merkintä?",
  31.556 +    "", // TODO
  31.557 +    "¿Borrar la entrada?", 
  31.558 +    "Aferesi simiou?",
  31.559 +    "Radera post?",
  31.560 +    "", // TODO
  31.561 +    "", // TODO
  31.562 +    "Esborrar la entrada?",
  31.563 +#if APIVERSNUM >= 10302
  31.564 +    "ÃÔÐÛØâì ×ÐßØáì?",
  31.565 +#endif
  31.566 +  },
  31.567 +  { "Playlist editor",
  31.568 +    "Abspielliste bearbeiten",
  31.569 +    "Urejevalnik liste",
  31.570 +    "", // TODO
  31.571 +    "", // TODO
  31.572 +    "", // TODO
  31.573 +    "Editeur de liste de lecture",
  31.574 +    "", // TODO
  31.575 +    "Soittolistan muokkaus",
  31.576 +    "", // TODO
  31.577 +    "Editor de listas de reproducción", 
  31.578 +    "Metatropes stin Playlista",
  31.579 +    "Spellisteditor",
  31.580 +    "", // TODO
  31.581 +    "", // TODO
  31.582 +    "Editor de llistes de reproducció",
  31.583 +#if APIVERSNUM >= 10302
  31.584 +    "ÀÕÔÐÚâÞà áßØáÚÐ ßÕáÕÝ",
  31.585 +#endif
  31.586 +  },
  31.587 +  { "Directory browser",
  31.588 +    "Verzeichnisanzeige",
  31.589 +    "Navigator",
  31.590 +    "", // TODO
  31.591 +    "", // TODO
  31.592 +    "", // TODO
  31.593 +    "Navigateur",
  31.594 +    "", // TODO
  31.595 +    "Hakemistoselain",
  31.596 +    "", // TODO
  31.597 +    "Explorar carpetes", 
  31.598 +    "Endiski fakelou",
  31.599 +    "Katalogbläddrare",
  31.600 +    "", // TODO
  31.601 +    "", // TODO
  31.602 +    "Navegar per les carpetes",
  31.603 +#if APIVERSNUM >= 10302
  31.604 +    "¿àÞáÜÞâà ÚÐâÐÛÞÓÞÒ",
  31.605 +#endif
  31.606 +  },
  31.607 +  { "Delete playlist?",
  31.608 +    "Abspielliste löschen?",
  31.609 +    "Odstrani listo?",
  31.610 +    "", // TODO
  31.611 +    "", // TODO
  31.612 +    "", // TODO
  31.613 +    "Effacer liste?",
  31.614 +    "", // TODO
  31.615 +    "Poistetaanko soittolista?",
  31.616 +    "", // TODO
  31.617 +    "¿Borrar la lista?", 
  31.618 +    "Svisimo listas?",
  31.619 +    "Radera spellista?",
  31.620 +    "", // TODO
  31.621 +    "", // TODO
  31.622 +    "Esborrar la Llista?",
  31.623 +#if APIVERSNUM >= 10302
  31.624 +    "ÃÔÐÛØâì áßØáÞÚ",
  31.625 +#endif
  31.626 +  },
  31.627 +  { "Are you sure?",
  31.628 +    "Wirklich löschen?",
  31.629 +    "Ste prepricani?",
  31.630 +    "", // TODO
  31.631 +    "", // TODO
  31.632 +    "", // TODO
  31.633 +    "Etes-vous sûr?",
  31.634 +    "", // TODO
  31.635 +    "Oletko varma?",
  31.636 +    "", // TODO
  31.637 +    "¿Está seguro?", 
  31.638 +    "Ise sigouros?",
  31.639 +    "Är du säker?",
  31.640 +    "", // TODO
  31.641 +    "", // TODO
  31.642 +    "N'esteu segur?",
  31.643 +#if APIVERSNUM >= 10302
  31.644 +    "²ë ãÒÕàÕÝë?",
  31.645 +#endif
  31.646 +  },
  31.647 +  { "unnamed",
  31.648 +    "unbenannt",
  31.649 +    "neimenovan",
  31.650 +    "", // TODO
  31.651 +    "", // TODO
  31.652 +    "", // TODO
  31.653 +    "sansnom",
  31.654 +    "", // TODO
  31.655 +    "nimetön",
  31.656 +    "", // TODO
  31.657 +    "sin nombre", 
  31.658 +    "xoris onoma",
  31.659 +    "namnlös",
  31.660 +    "", // TODO
  31.661 +    "", // TODO
  31.662 +    "sense nom",
  31.663 +#if APIVERSNUM >= 10302
  31.664 +    "ÑÕ×ëÜïÝÝëÙ",
  31.665 +#endif
  31.666 +  },
  31.667 +  { "unknown",
  31.668 +    "unbekannt",
  31.669 +    "", // TODO
  31.670 +    "", // TODO
  31.671 +    "", // TODO
  31.672 +    "", // TODO
  31.673 +    "inconnu",
  31.674 +    "", // TODO
  31.675 +    "tuntematon",
  31.676 +    "", // TODO
  31.677 +    "desconocido", 
  31.678 +    "", // TODO
  31.679 +    "okänd",
  31.680 +    "", // TODO
  31.681 +    "", // TODO
  31.682 +    "desconegut",
  31.683 +#if APIVERSNUM >= 10302
  31.684 +    "ÝÕØ×ÒÕáâÝëÙ",
  31.685 +#endif
  31.686 +  },
  31.687 +  { "Error scanning playlists!",
  31.688 +    "Fehler beim Einlesen der Abspiellisten!",
  31.689 +    "Napaka pri pregledovanju liste!",
  31.690 +    "", // TODO
  31.691 +    "", // TODO
  31.692 +    "", // TODO
  31.693 +    "Erreur de parcours de la liste!",
  31.694 +    "", // TODO
  31.695 +    "Soittolistojen lukeminen epäonnistui!",
  31.696 +    "", // TODO
  31.697 +    "¡Error al leer las listas!", 
  31.698 +    "Lathos stin sarosi tis Playlistas!",
  31.699 +    "Fel uppstod då spellistorna lästes!",
  31.700 +    "", // TODO
  31.701 +    "", // TODO
  31.702 +    "Error al llegir les llistes!",
  31.703 +#if APIVERSNUM >= 10302
  31.704 +    "¾èØÑÚÐ ßÞØáÚÐ áßØáÚÞÒ ßÕáÕÝ!",
  31.705 +#endif
  31.706 +  },
  31.707 +  { "Error deleting playlist!",
  31.708 +    "Fehler beim Löschen der Abspielliste!",
  31.709 +    "Napaka pri odstranjevanju liste!",
  31.710 +    "", // TODO
  31.711 +    "", // TODO
  31.712 +    "", // TODO
  31.713 +    "Erreur d'effacement de la liste!",
  31.714 +    "", // TODO
  31.715 +    "Soittolistan poistaminen epäonnistui!",
  31.716 +    "", // TODO
  31.717 +    "¡Error al borrar una lista!",
  31.718 +    "Lathos stin akirosi tis Playlistas!",
  31.719 +    "Fel uppstod då spellistan raderades!",
  31.720 +    "", // TODO
  31.721 +    "", // TODO
  31.722 +    "Error al esborrar una llista!",
  31.723 +#if APIVERSNUM >= 10302
  31.724 +    "¾èØÑÚÐ ãÔÐÛÕÝØï áßØáÚÐ ßÕáÕÝ!",
  31.725 +#endif
  31.726 +  },
  31.727 +  { "Error creating playlist!",
  31.728 +    "Fehler beim Erstellen der Abspielliste!",
  31.729 +    "Napaka pri kreiranju liste!",
  31.730 +    "", // TODO
  31.731 +    "", // TODO
  31.732 +    "", // TODO
  31.733 +    "Erreur de création de la liste!",
  31.734 +    "", // TODO
  31.735 +    "Soittolistan luominen epäonnistui!",
  31.736 +    "", // TODO
  31.737 +    "¡Error al crear una lista!",
  31.738 +    "Lathos stin dimiourgia tis Playlistas!",
  31.739 +    "Fel uppstod då spellistan skapades!",
  31.740 +    "", // TODO
  31.741 +    "", // TODO
  31.742 +    "Error al crear una llista!",
  31.743 +#if APIVERSNUM >= 10302
  31.744 +    "¾èØÑÚÐ áÞ×ÔÐÝØï áßØáÚÐ ßÕáÕÝ!",
  31.745 +#endif
  31.746 +  },
  31.747 +  { "Can't edit a WinAmp playlist!",
  31.748 +    "Editieren von WinAmp Abspiellisten nicht möglich!",
  31.749 +    "Ne morem vnesti liste od WinAmp-a!",
  31.750 +    "", // TODO
  31.751 +    "", // TODO
  31.752 +    "", // TODO
  31.753 +    "Ne peut pas éditer une liste WinAmp!",
  31.754 +    "", // TODO
  31.755 +    "WinAmp-soittolistaa ei voida muokata!",
  31.756 +    "", // TODO
  31.757 +    "¡No puedo editar una lista del WinAmp!", 
  31.758 +    "Den mporo na kano metatropes se WinAmp Playlista!",
  31.759 +    "Kan inte redigera en WinAmp-spellista!",
  31.760 +    "", // TODO
  31.761 +    "", // TODO
  31.762 +    "No puc editar una llista del WinAmp!",
  31.763 +#if APIVERSNUM >= 10302
  31.764 +    "½ÕÒÞ×ÜÞÖÝÞ àÕÔÐÚâØàÞÒÐâì áßØáÞÚ WinAmp!",
  31.765 +#endif
  31.766 +  },
  31.767 +  { "Error renaming playlist!",
  31.768 +    "Fehler beim Umbenennen der Abspielliste!",
  31.769 +    "Napaka pri preimenovanju liste!",
  31.770 +    "", // TODO
  31.771 +    "", // TODO
  31.772 +    "", // TODO
  31.773 +    "Erreur pour renommer la liste!",
  31.774 +    "", // TODO
  31.775 +    "Soittolistan uudelleennimeäminen epäonnistui!",
  31.776 +    "", // TODO
  31.777 +    "¡Errir al renombrar la lista!", 
  31.778 +    "Latsos stin metonomasi tis Playlistas!",
  31.779 +    "Fel uppstod då spellistan döptes om!",
  31.780 +    "", // TODO
  31.781 +    "", // TODO
  31.782 +    "Error al reanomenar la llista!",
  31.783 +#if APIVERSNUM >= 10302
  31.784 +    "¾èØÑÚÐ ßÕàÕØÜÕÝÞÒÐÝØï áßØáÚÐ!",
  31.785 +#endif
  31.786 +  },
  31.787 +  { "Loading playlist...",
  31.788 +    "Lade Abspielliste...",
  31.789 +    "", // TODO
  31.790 +    "", // TODO
  31.791 +    "", // TODO
  31.792 +    "", // TODO
  31.793 +    "Chargement playlist...",
  31.794 +    "", // TODO
  31.795 +    "Avataan soittolistaa...",
  31.796 +    "", // TODO
  31.797 +    "Cargando la lista de reproducción...", 
  31.798 +    "", // TODO
  31.799 +    "Läser in spellista...",
  31.800 +    "", // TODO
  31.801 +    "", // TODO
  31.802 +    "Carregant la llista de reproducció...",
  31.803 +#if APIVERSNUM >= 10302
  31.804 +    "ÇâÕÝØÕ áßØáÚÐ ßÕáÕÝ...",
  31.805 +#endif
  31.806 +  },
  31.807 +  { "Error loading playlist!",
  31.808 +    "Fehler beim Lesen der Abspielliste!",
  31.809 +    "Napaka pri nalaganju liste!",
  31.810 +    "", // TODO
  31.811 +    "", // TODO
  31.812 +    "", // TODO
  31.813 +    "Erreur de chargement de la playlist!",
  31.814 +    "", // TODO
  31.815 +    "Soittolistan avaaminen epäonnistui!",
  31.816 +    "", // TODO
  31.817 +    "¡Error al cargar la lista de reproducción!", 
  31.818 +    "Lathos sto fortoma tis Playlistas!",
  31.819 +    "Fel uppstod då spellistan lästes in!",
  31.820 +    "", // TODO
  31.821 +    "", // TODO
  31.822 +    "Error al carregar la llista de reproducció!",
  31.823 +#if APIVERSNUM >= 10302
  31.824 +    "¾èØÑÚÐ çâÕÝØï áßØáÚÐ ßÕáÕÝ!",
  31.825 +#endif
  31.826 +  },
  31.827 +  { "Scanning playlists...",
  31.828 +    "Durchsuche Abspiellisten...",
  31.829 +    "", // TODO
  31.830 +    "", // TODO
  31.831 +    "", // TODO
  31.832 +    "", // TODO
  31.833 +    "Scanne playlists...",
  31.834 +    "", // TODO
  31.835 +    "Selataan soittolistoja...", 
  31.836 +    "", // TODO
  31.837 +    "Leyendo las listas de reproducción...",
  31.838 +    "", // TODO
  31.839 +    "Söker spellistor...",
  31.840 +    "", // TODO
  31.841 +    "", // TODO
  31.842 +    "Llegint les llistes de reproducció...",
  31.843 +#if APIVERSNUM >= 10302
  31.844 +    "¿ÞØáÚ áßØáÚÞÒ ßÕáÕÝ...",
  31.845 +#endif
  31.846 +  },
  31.847 +  { "Scanning directory...",
  31.848 +    "Durchsuche Verzeichnis...",
  31.849 +    "", // TODO
  31.850 +    "", // TODO
  31.851 +    "", // TODO
  31.852 +    "", // TODO
  31.853 +    "Scanne le repertoire...",
  31.854 +    "", // TODO
  31.855 +    "Selataan hakemistoa...",
  31.856 +    "", // TODO
  31.857 +    "Leyendo las carpetas..",
  31.858 +    "", // TODO
  31.859 +    "Söker igenom katalog...",
  31.860 +    "", // TODO
  31.861 +    "", // TODO
  31.862 +    "Revisant les carpetes..",
  31.863 +#if APIVERSNUM >= 10302
  31.864 +    "ÁÚÐÝØàÞÒÐÝØÕ ÚÐâÐÛÞÓÞÒ...",
  31.865 +#endif
  31.866 +  },
  31.867 +  { "Building playlist...",
  31.868 +    "Baue Abspielliste auf...",
  31.869 +    "", // TODO
  31.870 +    "", // TODO
  31.871 +    "", // TODO
  31.872 +    "", // TODO
  31.873 +    "Construit la playlist...",
  31.874 +    "", // TODO
  31.875 +    "Muodostetaan soittolistaa...",
  31.876 +    "", // TODO
  31.877 +    "Construyendo la lista de reproducción...",
  31.878 +    "", // TODO
  31.879 +    "Skapar en spellista...",
  31.880 +    "", // TODO
  31.881 +    "", // TODO
  31.882 +    "Construïnt la llista de reproducció...",
  31.883 +#if APIVERSNUM >= 10302
  31.884 +    "¿ÞáâàÞÕÝØÕ áßØáÚÐ ßÕáÕÝ...",
  31.885 +#endif
  31.886 +  },
  31.887 +  { "Error building playlist!",
  31.888 +    "Fehler beim Aufbau der Abspielliste!",
  31.889 +    "", // TODO
  31.890 +    "", // TODO
  31.891 +    "", // TODO
  31.892 +    "", // TODO
  31.893 +    "Erreur construction playlist!",
  31.894 +    "", // TODO
  31.895 +    "Soittolistan muodostaminen epäonnistui!",
  31.896 +    "", // TODO
  31.897 +    "¡Error al construir la lista de reproducción!",
  31.898 +    "", // TODO
  31.899 +    "Fel uppstod då spellistan skapades!",
  31.900 +    "", // TODO
  31.901 +    "", // TODO
  31.902 +    "Error al consstruir la llista de reproducció!",
  31.903 +#if APIVERSNUM >= 10302
  31.904 +    "¾èØÑÚÐ ßÞáâàÞÕÝØï áßØáÚÐ ßÕáÕÝ!",
  31.905 +#endif
  31.906 +  },
  31.907 +  { "Remote CDDB lookup...",
  31.908 +    "Remote CDDB Abfrage...",
  31.909 +    "", // TODO
  31.910 +    "", // TODO
  31.911 +    "", // TODO
  31.912 +    "", // TODO
  31.913 +    "Consultation CDDB...",
  31.914 +    "", // TODO
  31.915 +    "Kysytään CDDB-tietoja...",
  31.916 +    "", // TODO
  31.917 +    "Consultando CDDB...",
  31.918 +    "", // TODO
  31.919 +    "Fjärrförfrågan till CDDB...",
  31.920 +    "", // TODO
  31.921 +    "", // TODO
  31.922 +    "Consultant CDDB...",
  31.923 +#if APIVERSNUM >= 10302
  31.924 +    "·ÐßàÞá Ú ãÔÐÛñÝÝÞÙ ÑÐ×Õ CDDB...",
  31.925 +#endif
  31.926 +  },
  31.927 +  { "Connecting to stream server ...",
  31.928 +    "Verbinde mit Stream Server...",
  31.929 +    "", // TODO
  31.930 +    "", // TODO
  31.931 +    "", // TODO
  31.932 +    "", // TODO
  31.933 +    "Connexion au Serveur Stream...",
  31.934 +    "", // TODO
  31.935 +    "Yhdistetään palvelimeen...",
  31.936 +    "", // TODO
  31.937 +    "Conectando con el servidor...", 
  31.938 +    "", // TODO
  31.939 +    "Kontaktar stream-server...",
  31.940 +    "", // TODO
  31.941 +    "", // TODO
  31.942 +    "Connectant al servidor...",
  31.943 +#if APIVERSNUM >= 10302
  31.944 +    "¿ÞÔÚÛîçÕÝØÕ Ú áÕàÒÕàã ßÞâÞÚÞÒ...",
  31.945 +#endif
  31.946 +  },
  31.947 +  { "Rename playlist",
  31.948 +    "Abspielliste umbenennen",
  31.949 +    "Preimenuj listo",
  31.950 +    "", // TODO
  31.951 +    "", // TODO
  31.952 +    "", // TODO
  31.953 +    "Renommer la liste",
  31.954 +    "", // TODO
  31.955 +    "Nimeä soittolista",
  31.956 +    "", // TODO
  31.957 +    "Renombrar la lista de reproducción",
  31.958 +    "Metonomasi tis Playlistas",
  31.959 +    "Döp om spellistan",
  31.960 +    "", // TODO
  31.961 +    "", // TODO
  31.962 +    "Reanomenar la llista de repr.",
  31.963 +#if APIVERSNUM >= 10302
  31.964 +    "¿ÕàÕØÜÕÝÞÒÐâì áßØáÞÚ ßÕáÕÝ",
  31.965 +#endif
  31.966 +  },
  31.967 +  { "Old name:",
  31.968 +    "Alter Name:",
  31.969 +    "Staro ime:",
  31.970 +    "", // TODO
  31.971 +    "", // TODO
  31.972 +    "", // TODO
  31.973 +    "Ancien nom:",
  31.974 +    "", // TODO
  31.975 +    "Vanha nimi",
  31.976 +    "", // TODO
  31.977 +    "Nombre anterior:", 
  31.978 +    "Palio onoma:",
  31.979 +    "Gammalt namn:",
  31.980 +    "", // TODO
  31.981 +    "", // TODO
  31.982 +    "Nom anterior:",
  31.983 +#if APIVERSNUM >= 10302
  31.984 +    "ÁâÐàÞÕ ØÜï:",
  31.985 +#endif
  31.986 +  },
  31.987 +  { "New name",
  31.988 +    "Neuer Name",
  31.989 +    "Novo ime",
  31.990 +    "", // TODO
  31.991 +    "", // TODO
  31.992 +    "", // TODO
  31.993 +    "Nouveau nom",
  31.994 +    "", // TODO
  31.995 +    "Uusi nimi",
  31.996 +    "", // TODO
  31.997 +    "Nombre nuevo", 
  31.998 +    "Neo onoma",
  31.999 +    "Nytt namn",
 31.1000 +    "", // TODO
 31.1001 +    "", // TODO
 31.1002 +    "Nou nom", 
 31.1003 +#if APIVERSNUM >= 10302
 31.1004 +    "½ÞÒÞÕ ØÜï:",
 31.1005 +#endif
 31.1006 +  },
 31.1007 +  { "Filenames",
 31.1008 +    "Dateinamen",
 31.1009 +    "Imena datotek",
 31.1010 +    "", // TODO
 31.1011 +    "", // TODO
 31.1012 +    "", // TODO
 31.1013 +    "Noms de fichiers",
 31.1014 +    "", // TODO
 31.1015 +    "tiedostonimet",
 31.1016 +    "", // TODO
 31.1017 +    "Nombre de los archivos", 
 31.1018 +    "Onomata arxeion",
 31.1019 +    "Filnamn",
 31.1020 +    "", // TODO
 31.1021 +    "", 
 31.1022 +    "Nom dels arxius",
 31.1023 +#if APIVERSNUM >= 10302
 31.1024 +    "ØÜÕÝÐ äÐÙÛÞÒ",
 31.1025 +#endif
 31.1026 +  },
 31.1027 +  { "ID3 names",
 31.1028 +    "ID3 Namen",
 31.1029 +    "ID3 imena",
 31.1030 +    "", // TODO
 31.1031 +    "", // TODO
 31.1032 +    "", // TODO
 31.1033 +    "Nom ID3",
 31.1034 +    "", // TODO
 31.1035 +    "ID3-nimet",
 31.1036 +    "", // TODO
 31.1037 +    "Nombre ID3", 
 31.1038 +    "ID3 onomata",
 31.1039 +    "ID3 namn",
 31.1040 +    "", // TODO
 31.1041 +    "", // TODO
 31.1042 +    "Nom ID3",
 31.1043 +#if APIVERSNUM >= 10302
 31.1044 +    "ID3-ØÜÕÝÐ",
 31.1045 +#endif
 31.1046 +  },
 31.1047 +  { "ID3 information",
 31.1048 +    "ID3 Information",
 31.1049 +    "ID3 informacija",
 31.1050 +    "", // TODO
 31.1051 +    "", // TODO
 31.1052 +    "", // TODO
 31.1053 +    "Information ID3",
 31.1054 +    "", // TODO
 31.1055 +    "ID3-informaatio",
 31.1056 +    "", // TODO
 31.1057 +    "Información ID3",
 31.1058 +    "Plirofories ID3",
 31.1059 +    "ID3 information",
 31.1060 +    "", // TODO
 31.1061 +    "", // TODO
 31.1062 +    "Informació ID3",
 31.1063 +#if APIVERSNUM >= 10302
 31.1064 +    "ID3-ÜÕâÚØ",
 31.1065 +#endif
 31.1066 +  },
 31.1067 +  { "ID3 info",
 31.1068 +    "ID3 Info",
 31.1069 +    "ID3 info",
 31.1070 +    "", // TODO
 31.1071 +    "", // TODO
 31.1072 +    "", // TODO
 31.1073 +    "Info ID3",
 31.1074 +    "", // TODO
 31.1075 +    "ID3-info",
 31.1076 +    "", // TODO
 31.1077 +    "ID3 info",
 31.1078 +    "ID3 pliroforia",
 31.1079 +    "ID3 info",
 31.1080 +    "", // TODO
 31.1081 +    "", // TODO
 31.1082 +    "ID3 info",
 31.1083 +#if APIVERSNUM >= 10302
 31.1084 +    "ÔÐÝÝëÕ ID3",
 31.1085 +#endif
 31.1086 +  },
 31.1087 +  { "Filename",
 31.1088 +    "Dateiname",
 31.1089 +    "Ime datoteke",
 31.1090 +    "", // TODO
 31.1091 +    "", // TODO
 31.1092 +    "", // TODO
 31.1093 +    "Nom du fichier",
 31.1094 +    "", // TODO
 31.1095 +    "Tiedostonimi",
 31.1096 +    "", // TODO
 31.1097 +    "Nombre del archivo", 
 31.1098 +    "Onoma arxeiou",
 31.1099 +    "Filnamn",
 31.1100 +    "", // TODO
 31.1101 +    "", // TODO
 31.1102 +    "Nom de l'arxiu",
 31.1103 +#if APIVERSNUM >= 10302
 31.1104 +    "¸Üï äÐÙÛÐ",
 31.1105 +#endif
 31.1106 +  },
 31.1107 +  { "Length",
 31.1108 +    "Länge",
 31.1109 +    "Dolzina",
 31.1110 +    "", // TODO
 31.1111 +    "", // TODO
 31.1112 +    "", // TODO
 31.1113 +    "Longueur",
 31.1114 +    "", // TODO
 31.1115 +    "Pituus",
 31.1116 +    "", // TODO
 31.1117 +    "Duración", 
 31.1118 +    "Megethos",
 31.1119 +    "Längd",
 31.1120 +    "", // TODO
 31.1121 +    "", // TODO
 31.1122 +    "Durada",
 31.1123 +#if APIVERSNUM >= 10302
 31.1124 +    "´ÛØÝÐ",
 31.1125 +#endif
 31.1126 +  },
 31.1127 +  { "Title",
 31.1128 +    "Titel",
 31.1129 +    "Naslov",
 31.1130 +    "", // TODO
 31.1131 +    "", // TODO
 31.1132 +    "", // TODO
 31.1133 +    "Titre",
 31.1134 +    "", // TODO
 31.1135 +    "Kappale",
 31.1136 +    "", // TODO
 31.1137 +    "Título", 
 31.1138 +    "Titlos",
 31.1139 +    "Tittel",
 31.1140 +    "", // TODO
 31.1141 +    "", // TODO
 31.1142 +    "Títol",
 31.1143 +#if APIVERSNUM >= 10302
 31.1144 +    "½Ð×ÒÐÝØÕ",
 31.1145 +#endif
 31.1146 +  },
 31.1147 +  { "Artist",
 31.1148 +    "Interpret",
 31.1149 +    "Avtor",
 31.1150 +    "", // TODO
 31.1151 +    "", // TODO
 31.1152 +    "", // TODO
 31.1153 +    "Artiste",
 31.1154 +    "", // TODO
 31.1155 +    "Esittäjä",
 31.1156 +    "", // TODO
 31.1157 +    "Artista",
 31.1158 +    "Ermineftis",
 31.1159 +    "Artist",
 31.1160 +    "", // TODO
 31.1161 +    "", // TODO
 31.1162 +    "Intèrpret",
 31.1163 +#if APIVERSNUM >= 10302
 31.1164 +    "¸áßÞÛÝØâÕÛì",
 31.1165 +#endif
 31.1166 +  },
 31.1167 +  { "Album",
 31.1168 +    "Album",
 31.1169 +    "Album",
 31.1170 +    "", // TODO
 31.1171 +    "", // TODO
 31.1172 +    "", // TODO
 31.1173 +    "Album",
 31.1174 +    "", // TODO
 31.1175 +    "Albumi",
 31.1176 +    "", // TODO
 31.1177 +    "Album",
 31.1178 +    "Album",
 31.1179 +    "Album",
 31.1180 +    "", // TODO
 31.1181 +    "", // TODO
 31.1182 +    "Àlbum",
 31.1183 +#if APIVERSNUM >= 10302
 31.1184 +    "°ÛìÑÞÜ",
 31.1185 +#endif
 31.1186 +  },
 31.1187 +  { "Year",
 31.1188 +    "Jahr",
 31.1189 +    "Leto",
 31.1190 +    "", // TODO
 31.1191 +    "", // TODO
 31.1192 +    "", // TODO
 31.1193 +    "Année",
 31.1194 +    "", // TODO
 31.1195 +    "Vuosi",
 31.1196 +    "", // TODO
 31.1197 +    "", // TODO
 31.1198 +    "Etos",
 31.1199 +    "År",
 31.1200 +    "", // TODO
 31.1201 +    "", // TODO
 31.1202 +    "Any",
 31.1203 +#if APIVERSNUM >= 10302
 31.1204 +    "³ÞÔ",
 31.1205 +#endif
 31.1206 +  },
 31.1207 +  { "Samplerate",
 31.1208 +    "Sample Rate",
 31.1209 +    "Vzorcenje",
 31.1210 +    "", // TODO
 31.1211 +    "", // TODO
 31.1212 +    "", // TODO
 31.1213 +    "Fréquence",
 31.1214 +    "", // TODO
 31.1215 +    "Näytteenottotaajuus",
 31.1216 +    "", // TODO
 31.1217 +    "Frecuencia", 
 31.1218 +    "Sixnotita",
 31.1219 +    "Samplingshastighet",
 31.1220 +    "", // TODO
 31.1221 +    "", // TODO
 31.1222 +    "Freqüència",
 31.1223 +#if APIVERSNUM >= 10302
 31.1224 +    "ÇÐáâÞâÐ",
 31.1225 +#endif
 31.1226 +  },
 31.1227 +  { "Bitrate",
 31.1228 +    "Bit Rate",
 31.1229 +    "Bitna hitrost",
 31.1230 +    "", // TODO
 31.1231 +    "", // TODO
 31.1232 +    "", // TODO
 31.1233 +    "Bitrate",
 31.1234 +    "", // TODO
 31.1235 +    "Bittinopeus",
 31.1236 +    "", // TODO
 31.1237 +    "Bitrate",
 31.1238 +    "Bitrate",
 31.1239 +    "Bithastighet",
 31.1240 +    "", // TODO
 31.1241 +    "", // TODO
 31.1242 +    "Bitrate",
 31.1243 +#if APIVERSNUM >= 10302
 31.1244 +    "¿ÞâÞÚ",
 31.1245 +#endif
 31.1246 +  },
 31.1247 +  { "Setup.MP3$Initial loop mode",
 31.1248 +    "Default Loop Modus",
 31.1249 +    "Osnovni nacin ponavljanje",
 31.1250 +    "", // TODO
 31.1251 +    "", // TODO
 31.1252 +    "", // TODO
 31.1253 +    "Mode de répétition initial",
 31.1254 +    "", // TODO
 31.1255 +    "Jatkuva soitto oletuksena",
 31.1256 +    "", // TODO
 31.1257 +    "Modo de repetición inicial", 
 31.1258 +    "", // TODO
 31.1259 +    "Normalt upprepningsläge",
 31.1260 +    "", // TODO
 31.1261 +    "", // TODO
 31.1262 +    "Mode de repetició inicial",
 31.1263 +#if APIVERSNUM >= 10302
 31.1264 +    "ÀÕÖØÜ ßÞÒâÞàÐ ßÞ ãÜÞÛçÐÝØî",
 31.1265 +#endif
 31.1266 +  },
 31.1267 +  { "Setup.MP3$Initial shuffle mode",
 31.1268 +    "Default Shuffle Modus",
 31.1269 +    "Osnovni nacin mesano",
 31.1270 +    "", // TODO
 31.1271 +    "", // TODO
 31.1272 +    "", // TODO
 31.1273 +    "Mode de lecture aléatoire initial",
 31.1274 +    "", // TODO
 31.1275 +    "Satunnaissoitto oletuksena",
 31.1276 +    "", // TODO
 31.1277 +    "Lectura aleatória inicial", 
 31.1278 +    "", // TODO
 31.1279 +    "Normalt blandläge",
 31.1280 +    "", // TODO
 31.1281 +    "", // TODO
 31.1282 +    "Lectura aleatòria inicial",
 31.1283 +#if APIVERSNUM >= 10302
 31.1284 +    "ÀÕÖØÜ áÛãçÐÙÝ. ÒëÑÞàÐ ßÞ ãÜÞÛçÐÝØî",
 31.1285 +#endif
 31.1286 +  },
 31.1287 +  { "Setup.MP3$Title/Artist order",
 31.1288 +    "Title/Artist Reihenfolge",
 31.1289 +    "", // TODO
 31.1290 +    "", // TODO
 31.1291 +    "", // TODO
 31.1292 +    "", // TODO
 31.1293 +    "",
 31.1294 +    "", // TODO
 31.1295 +    "Näytä kappale ja esittäjä",
 31.1296 +    "", // TODO
 31.1297 +    "", // TODO
 31.1298 +    "", // TODO
 31.1299 +    "", // TODO
 31.1300 +    "", // TODO
 31.1301 +    "", // TODO
 31.1302 +    "", // TODO
 31.1303 +#if APIVERSNUM >= 10302
 31.1304 +    "",
 31.1305 +#endif
 31.1306 +  },
 31.1307 +  { "Normal",
 31.1308 +    "Normal",
 31.1309 +    "", // TODO
 31.1310 +    "", // TODO
 31.1311 +    "", // TODO
 31.1312 +    "", // TODO
 31.1313 +    "",
 31.1314 +    "", // TODO
 31.1315 +    "normaalisti",
 31.1316 +    "", // TODO
 31.1317 +    "", // TODO
 31.1318 +    "", // TODO
 31.1319 +    "", // TODO
 31.1320 +    "", // TODO
 31.1321 +    "", // TODO
 31.1322 +    "", // TODO
 31.1323 +#if APIVERSNUM >= 10302
 31.1324 +    "",
 31.1325 +#endif
 31.1326 +  },
 31.1327 +  { "Reversed",
 31.1328 +    "Umgedreht",
 31.1329 +    "", // TODO
 31.1330 +    "", // TODO
 31.1331 +    "", // TODO
 31.1332 +    "", // TODO
 31.1333 +    "",
 31.1334 +    "", // TODO
 31.1335 +    "käännettynä",
 31.1336 +    "", // TODO
 31.1337 +    "", // TODO
 31.1338 +    "", // TODO
 31.1339 +    "", // TODO
 31.1340 +    "", // TODO
 31.1341 +    "", // TODO
 31.1342 +    "", // TODO
 31.1343 +#if APIVERSNUM >= 10302
 31.1344 +    "",
 31.1345 +#endif
 31.1346 +  },
 31.1347 +  { "Setup.MP3$Audio output mode",
 31.1348 +    "Audio Ausgabe Modus",
 31.1349 +    "", // TODO
 31.1350 +    "", // TODO
 31.1351 +    "", // TODO
 31.1352 +    "", // TODO
 31.1353 +    "Mode de sortie audio",
 31.1354 +    "", // TODO
 31.1355 +    "Äänen ulostulomoodi",
 31.1356 +    "", // TODO
 31.1357 +    "", // TODO
 31.1358 +    "", // TODO
 31.1359 +    "", // TODO
 31.1360 +    "", // TODO
 31.1361 +    "", // TODO
 31.1362 +    "", // TODO
 31.1363 +#if APIVERSNUM >= 10302
 31.1364 +    "ÀÕÖØÜ ÒëÒÞÔÐ ×ÒãÚÐ",
 31.1365 +#endif
 31.1366 +  },
 31.1367 +  { "OSS",
 31.1368 +    "OSS",
 31.1369 +    "OSS",
 31.1370 +    "OSS",
 31.1371 +    "OSS",
 31.1372 +    "OSS",
 31.1373 +    "OSS",
 31.1374 +    "OSS",
 31.1375 +    "OSS",
 31.1376 +    "OSS",
 31.1377 +    "OSS",
 31.1378 +    "OSS",
 31.1379 +    "OSS",
 31.1380 +    "OSS",
 31.1381 +    "OSS",
 31.1382 +    "OSS",
 31.1383 +#if APIVERSNUM >= 10302
 31.1384 +    "OSS",
 31.1385 +#endif
 31.1386 +  },
 31.1387 +  { "Setup.MP3$Audio mode",
 31.1388 +    "Audio Modus",
 31.1389 +    "Avdio nacin",
 31.1390 +    "", // TODO
 31.1391 +    "", // TODO
 31.1392 +    "", // TODO
 31.1393 +    "Mode audio",
 31.1394 +    "", // TODO
 31.1395 +    "Äänimoodi",
 31.1396 +    "", // TODO
 31.1397 +    "Modo de audio", 
 31.1398 +    "Katastasi audio",
 31.1399 +    "Audioläge",
 31.1400 +    "", // TODO
 31.1401 +    "", // TODO
 31.1402 +    "Mode d'audio",
 31.1403 +#if APIVERSNUM >= 10302
 31.1404 +    "ÀÕÖØÜ ßÕàÕÞæØäàÞÒÚØ ×ÒãÚÐ",
 31.1405 +#endif
 31.1406 +  },
 31.1407 +  { "Round",
 31.1408 +    "Runden",
 31.1409 +    "Zaokrozeni",
 31.1410 +    "", // TODO
 31.1411 +    "", // TODO
 31.1412 +    "", // TODO
 31.1413 +    "Arrondir",
 31.1414 +    "", // TODO
 31.1415 +    "pyöristetty",
 31.1416 +    "", // TODO
 31.1417 +    "Redondear", 
 31.1418 +    "Kikli",
 31.1419 +    "Avrunda",
 31.1420 +    "", // TODO
 31.1421 +    "", // TODO
 31.1422 +    "Arrodonir",
 31.1423 +#if APIVERSNUM >= 10302
 31.1424 +    "ßàØÑÛØÖÕÝØÕ",
 31.1425 +#endif
 31.1426 +  },
 31.1427 +  { "Dither",
 31.1428 +    "Streuen",
 31.1429 +    "Raztrosni",
 31.1430 +    "", // TODO
 31.1431 +    "", // TODO
 31.1432 +    "", // TODO
 31.1433 +    "Lisser",
 31.1434 +    "", // TODO
 31.1435 +    "ditteroitu",
 31.1436 +    "", // TODO
 31.1437 +    "Compactar",
 31.1438 +    "", // TODO
 31.1439 +    "", // TODO
 31.1440 +    "", // TODO
 31.1441 +    "", // TODO
 31.1442 +    "Compactar",
 31.1443 +#if APIVERSNUM >= 10302
 31.1444 +    "áÓÛÐÖØÒÐÝØÕ",
 31.1445 +#endif
 31.1446 +  },
 31.1447 +  { "Setup.MP3$Background scan",
 31.1448 +    "Hintergrund Scan",
 31.1449 +    "",
 31.1450 +    "", // TODO
 31.1451 +    "", // TODO
 31.1452 +    "", // TODO
 31.1453 +    "Scan en tache de fond",
 31.1454 +    "", // TODO
 31.1455 +    "Taustaskannaus",
 31.1456 +    "", // TODO
 31.1457 +    "", 
 31.1458 +    "", // TODO
 31.1459 +    "",
 31.1460 +    "", // TODO
 31.1461 +    "", // TODO
 31.1462 +    "",
 31.1463 +#if APIVERSNUM >= 10302
 31.1464 +    "ÄÞÝÞÒÞÕ áÚÐÝØàÞÒÐÝØÕ",
 31.1465 +#endif
 31.1466 +  },
 31.1467 +  { "ID3 only",
 31.1468 +    "nur ID3",
 31.1469 +    "",
 31.1470 +    "", // TODO
 31.1471 +    "", // TODO
 31.1472 +    "", // TODO
 31.1473 +    "Seulement ID3",
 31.1474 +    "", // TODO
 31.1475 +    "vain ID3",
 31.1476 +    "", // TODO
 31.1477 +    "", 
 31.1478 +    "",
 31.1479 +    "",
 31.1480 +    "", // TODO
 31.1481 +    "", // TODO
 31.1482 +    "",
 31.1483 +#if APIVERSNUM >= 10302
 31.1484 +    "ÂÞÛìÚÞ ID3",
 31.1485 +#endif
 31.1486 +  },
 31.1487 +  { "ID3 & Level",
 31.1488 +    "ID3 & Pegel",
 31.1489 +    "",
 31.1490 +    "", // TODO
 31.1491 +    "", // TODO
 31.1492 +    "", // TODO
 31.1493 +    "ID3 et niveau",
 31.1494 +    "", // TODO
 31.1495 +    "ID3 & taso",
 31.1496 +    "", // TODO
 31.1497 +    "", 
 31.1498 +    "",
 31.1499 +    "",
 31.1500 +    "", // TODO
 31.1501 +    "", // TODO
 31.1502 +    "",
 31.1503 +#if APIVERSNUM >= 10302
 31.1504 +    "ID3 Ø ãàÞÒÕÝì",
 31.1505 +#endif
 31.1506 +  },
 31.1507 +  { "Setup.MP3$Editor display mode",
 31.1508 +    "Editor Anzeige Modus",
 31.1509 +    "Nacin prikaza urejevalnika",
 31.1510 +    "", // TODO
 31.1511 +    "", // TODO
 31.1512 +    "", // TODO
 31.1513 +    "Mode d'affichage de l'éditeur",
 31.1514 +    "", // TODO
 31.1515 +    "Muokkaustilan näyttömoodi",
 31.1516 +    "", // TODO
 31.1517 +    "Modo de visualización del editor",
 31.1518 +    "", // TODO
 31.1519 +    "Redigerarens visuella läge",
 31.1520 +    "", // TODO
 31.1521 +    "", // TODO
 31.1522 +    "Mode de visualització del editor",
 31.1523 +#if APIVERSNUM >= 10302
 31.1524 +    "ÀÕÖØÜ àÕÔÐÚâÞàÐ áßØáÚÞÒ",
 31.1525 +#endif
 31.1526 +  },
 31.1527 +  { "Setup.MP3$Display mode",
 31.1528 +    "Anzeige Modus",
 31.1529 +    "Nacin prikaza",
 31.1530 +    "", // TODO
 31.1531 +    "", // TODO
 31.1532 +    "", // TODO
 31.1533 +    "Mode d'affichage",
 31.1534 +    "", // TODO
 31.1535 +    "Näyttömoodi",
 31.1536 +    "", // TODO
 31.1537 +    "Modo de Visualización", 
 31.1538 +    "", // TODO
 31.1539 +    "Visuellt läge",
 31.1540 +    "", // TODO
 31.1541 +    "", // TODO
 31.1542 +    "Mode de visualització",
 31.1543 +#if APIVERSNUM >= 10302
 31.1544 +    "ÀÕÖØÜ ØÝâÕàäÕÙáÐ",
 31.1545 +#endif
 31.1546 +  },
 31.1547 +  { "Setup.MP3$Background mode",
 31.1548 +    "Hintergrund Modus",
 31.1549 +    "Ozadje",
 31.1550 +    "", // TODO
 31.1551 +    "", // TODO
 31.1552 +    "", // TODO
 31.1553 +    "Arrière plan",
 31.1554 +    "", // TODO
 31.1555 +    "Taustamoodi",
 31.1556 +    "", // TODO
 31.1557 +    "Ejecutar al fondo", 
 31.1558 +    "", // TODO
 31.1559 +    "Bakgrundsläge",
 31.1560 +    "", // TODO
 31.1561 +    "", // TODO
 31.1562 +    "Execució de fons",
 31.1563 +#if APIVERSNUM >= 10302
 31.1564 +    "ÀÕÖØÜ äÞÝÐ",
 31.1565 +#endif
 31.1566 +  },
 31.1567 +  { "Setup.MP3$CDDB for CD-Audio",
 31.1568 +    "CDDB für CD-Audio",
 31.1569 +    "", // TODO
 31.1570 +    "", // TODO
 31.1571 +    "", // TODO
 31.1572 +    "", // TODO
 31.1573 +    "CDDB pour CD-Audio",
 31.1574 +    "", // TODO
 31.1575 +    "CDDB-tietokanta",
 31.1576 +    "", // TODO
 31.1577 +    "CDDB para el Aidio-CD", 
 31.1578 +    "", // TODO
 31.1579 +    "CDDB för CD-audio",
 31.1580 +    "", // TODO
 31.1581 +    "", // TODO
 31.1582 +    "CDDB per al Audio-CD",
 31.1583 +#if APIVERSNUM >= 10302
 31.1584 +    "CDDB ÔÛï ÐãÔØÞ-CD",
 31.1585 +#endif
 31.1586 +  },
 31.1587 +  { "disabled",
 31.1588 +    "aus",
 31.1589 +    "", // TODO
 31.1590 +    "", // TODO
 31.1591 +    "", // TODO
 31.1592 +    "", // TODO
 31.1593 +    "désactiver",
 31.1594 +    "", // TODO
 31.1595 +    "pois",
 31.1596 +    "", // TODO
 31.1597 +    "desactivado", 
 31.1598 +    "", // TODO
 31.1599 +    "avstängd",
 31.1600 +    "", // TODO
 31.1601 +    "", // TODO
 31.1602 +    "desactivat",
 31.1603 +#if APIVERSNUM >= 10302
 31.1604 +    "ÒëÚÛ",
 31.1605 +#endif
 31.1606 +  },
 31.1607 +  { "local only",
 31.1608 +    "nur lokal",
 31.1609 +    "", // TODO
 31.1610 +    "", // TODO
 31.1611 +    "", // TODO
 31.1612 +    "", // TODO
 31.1613 +    "local uniquement",
 31.1614 +    "", // TODO
 31.1615 +    "vain paikallinen",
 31.1616 +    "", // TODO
 31.1617 +    "solo local", 
 31.1618 +    "", // TODO
 31.1619 +    "", // TODO
 31.1620 +    "", // TODO
 31.1621 +    "", // TODO
 31.1622 +    "solsament local",
 31.1623 +#if APIVERSNUM >= 10302
 31.1624 +    "âÞÛìÚÞ ÛÞÚÐÛìÝ.",
 31.1625 +#endif
 31.1626 +  },
 31.1627 +  { "local&remote",
 31.1628 +    "lokal und entfernt",
 31.1629 +    "", // TODO
 31.1630 +    "", // TODO
 31.1631 +    "", // TODO
 31.1632 +    "", // TODO
 31.1633 +    "local et distant",
 31.1634 +    "", // TODO
 31.1635 +    "paikallinen & etä",
 31.1636 +    "", // TODO
 31.1637 +    "local y remoto",
 31.1638 +    "", // TODO
 31.1639 +    "", // TODO
 31.1640 +    "", // TODO
 31.1641 +    "", // TODO
 31.1642 +    "local i remot",
 31.1643 +#if APIVERSNUM >= 10302
 31.1644 +    "ÛÞÚÐÛìÝ. Ø ãÔÐÛñÝ.",
 31.1645 +#endif
 31.1646 +  },
 31.1647 +  { "Setup.MP3$Use 48kHz mode only",
 31.1648 +    "Nur 48kHz Modus benutzen",
 31.1649 +    "", // TODO
 31.1650 +    "", // TODO
 31.1651 +    "", // TODO
 31.1652 +    "", // TODO
 31.1653 +    "Mode 48kHz uniquement",
 31.1654 +    "", // TODO
 31.1655 +    "Käytä vain 48kHz moodia",
 31.1656 +    "", // TODO
 31.1657 +    "Solo modo 48kHz",
 31.1658 +    "", // TODO
 31.1659 +    "Använd endast 48Khz-läge",
 31.1660 +    "", // TODO
 31.1661 +    "", // TODO
 31.1662 +    "Només mode 48kHz",
 31.1663 +#if APIVERSNUM >= 10302
 31.1664 +    "ÂÞÛìÚÞ 48 Ú³æ",
 31.1665 +#endif
 31.1666 +  },
 31.1667 +  { "Setup.MP3$Use HTTP proxy",
 31.1668 +    "HTTP Proxy benutzen",
 31.1669 +    "", // TODO
 31.1670 +    "", // TODO
 31.1671 +    "", // TODO
 31.1672 +    "", // TODO
 31.1673 +    "Utiliser un Proxy HTTP",
 31.1674 +    "", // TODO
 31.1675 +    "Käytä HTTP-välityspalvelinta",
 31.1676 +    "", // TODO
 31.1677 +    "Utilizar proxy HTTP", 
 31.1678 +    "", // TODO
 31.1679 +    "Använd en HTTP-proxy",
 31.1680 +    "", // TODO
 31.1681 +    "", // TODO
 31.1682 +    "Util·litzar proxy HTTP",
 31.1683 +#if APIVERSNUM >= 10302
 31.1684 +    "¸áßÞÛì×ÞÒÐâì HTTP ßàÞÚáØ",
 31.1685 +#endif
 31.1686 +  },
 31.1687 +  { "Setup.MP3$HTTP proxy host",
 31.1688 +    "HTTP Proxy Name",
 31.1689 +    "", // TODO
 31.1690 +    "", // TODO
 31.1691 +    "", // TODO
 31.1692 +    "", // TODO
 31.1693 +    "Nom du Proxy HTTP",
 31.1694 +    "", // TODO
 31.1695 +    "HTTP-välityspalvelimen osoite",
 31.1696 +    "", // TODO
 31.1697 +    "Nombre del puerto HTTP", 
 31.1698 +    "", // TODO
 31.1699 +    "HTTP-proxyns namn",
 31.1700 +    "", // TODO
 31.1701 +    "", // TODO
 31.1702 +    "Nom del proxy HTTP",
 31.1703 +#if APIVERSNUM >= 10302
 31.1704 +    "ÁÕàÒÕà HTTP ßàÞÚáØ",
 31.1705 +#endif
 31.1706 +  },
 31.1707 +  { "Setup.MP3$HTTP proxy port",
 31.1708 +    "HTTP Proxy Port",
 31.1709 +    "", // TODO
 31.1710 +    "", // TODO
 31.1711 +    "", // TODO
 31.1712 +    "", // TODO
 31.1713 +    "Port du proxy HTTP",
 31.1714 +    "", // TODO
 31.1715 +    "HTTP-välityspalvelimen portti",
 31.1716 +    "", // TODO
 31.1717 +    "Puerto del Proxy HTTP", 
 31.1718 +    "", // TODO
 31.1719 +    "HTTP-proxyns port",
 31.1720 +    "", // TODO
 31.1721 +    "", // TODO
 31.1722 +    "Port del proxy HTTP",
 31.1723 +#if APIVERSNUM >= 10302
 31.1724 +    "¿Þàâ HTTP ßàÞÚáØ",
 31.1725 +#endif
 31.1726 +  },
 31.1727 +  { "Setup.MP3$CDDB server",
 31.1728 +    "CDDB Server",
 31.1729 +    "", // TODO
 31.1730 +    "", // TODO
 31.1731 +    "", // TODO
 31.1732 +    "", // TODO
 31.1733 +    "Serveur CDDB",
 31.1734 +    "", // TODO
 31.1735 +    "CDDB-palvelin",
 31.1736 +    "", // TODO
 31.1737 +    "Servidor CDDB", 
 31.1738 +    "", // TODO
 31.1739 +    "CDDB server",
 31.1740 +    "", // TODO
 31.1741 +    "", // TODO
 31.1742 +    "Servidor CDDB",
 31.1743 +#if APIVERSNUM >= 10302
 31.1744 +    "ÁÕàÒÕà CDDB",
 31.1745 +#endif
 31.1746 +  },
 31.1747 +  { "Setup.MP3$CDDB port",
 31.1748 +    "CDDB Port",
 31.1749 +    "", // TODO
 31.1750 +    "", // TODO
 31.1751 +    "", // TODO
 31.1752 +    "", // TODO
 31.1753 +    "Port CDDB",
 31.1754 +    "", // TODO
 31.1755 +    "CDDB-palvelimen portti",
 31.1756 +    "", // TODO
 31.1757 +    "Puerto CDDB",
 31.1758 +    "", // TODO
 31.1759 +    "CDDB port",
 31.1760 +    "", // TODO
 31.1761 +    "", // TODO
 31.1762 +    "Port CDDB",
 31.1763 +#if APIVERSNUM >= 10302
 31.1764 +    "¿Þàâ CDDB",
 31.1765 +#endif
 31.1766 +  },
 31.1767 +  { "Black",
 31.1768 +    "Schwarz",
 31.1769 +    "Crnina",
 31.1770 +    "", // TODO
 31.1771 +    "", // TODO
 31.1772 +    "", // TODO
 31.1773 +    "Noir",
 31.1774 +    "", // TODO
 31.1775 +    "musta",
 31.1776 +    "", // TODO
 31.1777 +    "Negra", 
 31.1778 +    "Mavro",
 31.1779 +    "Svart",
 31.1780 +    "", // TODO
 31.1781 +    "", // TODO
 31.1782 +    "Negre",
 31.1783 +#if APIVERSNUM >= 10302
 31.1784 +    "çñàÝëÙ íÚàÐÝ",
 31.1785 +#endif
 31.1786 +  },
 31.1787 +  { "Live",
 31.1788 +    "Live",
 31.1789 +    "Slika",
 31.1790 +    "", // TODO
 31.1791 +    "", // TODO
 31.1792 +    "", // TODO
 31.1793 +    "Live",
 31.1794 +    "", // TODO
 31.1795 +    "live",
 31.1796 +    "", // TODO
 31.1797 +    "En vivo", 
 31.1798 +    "Zontana",
 31.1799 +    "Live",
 31.1800 +    "", // TODO
 31.1801 +    "", // TODO
 31.1802 +    "Imatge",
 31.1803 +#if APIVERSNUM >= 10302
 31.1804 +    "âÕÛÕÒØÔÕÝØÕ",
 31.1805 +#endif
 31.1806 +  },
 31.1807 +  { "Images",
 31.1808 +    "Bilder",
 31.1809 +    "",
 31.1810 +    "", // TODO
 31.1811 +    "", // TODO
 31.1812 +    "", // TODO
 31.1813 +    "Images",
 31.1814 +    "", // TODO
 31.1815 +    "kansikuva",
 31.1816 +    "", // TODO
 31.1817 +    "", 
 31.1818 +    "",
 31.1819 +    "",
 31.1820 +    "", // TODO
 31.1821 +    "", // TODO
 31.1822 +    "",
 31.1823 +#if APIVERSNUM >= 10302
 31.1824 +    "",
 31.1825 +#endif
 31.1826 +  },
 31.1827 +  { "Setup.MP3$Mainmenu mode",
 31.1828 +    "Hauptmenu Modus",
 31.1829 +    "Nacin glavnega menija",
 31.1830 +    "", // TODO
 31.1831 +    "", // TODO
 31.1832 +    "", // TODO
 31.1833 +    "Menu principal",
 31.1834 +    "", // TODO
 31.1835 +    "Päävalikon tila",
 31.1836 +    "", // TODO
 31.1837 +    "Menú Principal", 
 31.1838 +    "", // TODO
 31.1839 +    "Huvudmenyläge",
 31.1840 +    "", // TODO
 31.1841 +    "", // TODO
 31.1842 +    "Menú Principal",
 31.1843 +#if APIVERSNUM >= 10302
 31.1844 +    "ÀÕÖØÜ ÓÛÐÒÝÞÓÞ ÜÕÝî",
 31.1845 +#endif
 31.1846 +  },
 31.1847 +  { "Playlists",
 31.1848 +    "Abspiellisten",
 31.1849 +    "Lista",
 31.1850 +    "", // TODO
 31.1851 +    "", // TODO
 31.1852 +    "", // TODO
 31.1853 +    "Playlists",
 31.1854 +    "", // TODO
 31.1855 +    "soittolistat",
 31.1856 +    "", // TODO
 31.1857 +    "Lista de reproducción", 
 31.1858 +    "Listes peksimatos",
 31.1859 +    "Spellistor",
 31.1860 +    "", // TODO
 31.1861 +    "", // TODO
 31.1862 +    "Llista a reproduïr",
 31.1863 +#if APIVERSNUM >= 10302
 31.1864 +    "áßØáÚØ ßÕáÕÝ",
 31.1865 +#endif
 31.1866 +  },
 31.1867 +  { "Browser",
 31.1868 +    "Verz.anzeige",
 31.1869 +    "Navigator",
 31.1870 +    "", // TODO
 31.1871 +    "", // TODO
 31.1872 +    "", // TODO
 31.1873 +    "Navigateur",
 31.1874 +    "", // TODO
 31.1875 +    "selain",
 31.1876 +    "", // TODO
 31.1877 +    "Navegador",
 31.1878 +    "", // TODO
 31.1879 +    "Bläddra",
 31.1880 +    "", // TODO
 31.1881 +    "", // TODO
 31.1882 +    "Navegador",
 31.1883 +#if APIVERSNUM >= 10302
 31.1884 +    "ÚÐâÐÛÞÓØ",
 31.1885 +#endif
 31.1886 +  },
 31.1887 +  { "Setup.MP3$Normalizer level",
 31.1888 +    "Normalisierer Pegel",
 31.1889 +    "Normalni nivo",
 31.1890 +    "", // TODO
 31.1891 +    "", // TODO
 31.1892 +    "", // TODO
 31.1893 +    "Niveau de normalisation",
 31.1894 +    "", // TODO
 31.1895 +    "Normalisoinnin taso",
 31.1896 +    "", // TODO
 31.1897 +    "Nivel de normalización", 
 31.1898 +    "", // TODO
 31.1899 +    "Normaliseringsnivå",
 31.1900 +    "", // TODO
 31.1901 +    "", // TODO
 31.1902 +    "Nivell de normalització",
 31.1903 +#if APIVERSNUM >= 10302
 31.1904 +    "ÃàÞÒÕÝì ÝÞàÜÐÛØ×ÐæØØ",
 31.1905 +#endif
 31.1906 +  },
 31.1907 +  { "Setup.MP3$Limiter level",
 31.1908 +    "Begrenzer Pegel",
 31.1909 +    "Limitni nivo",
 31.1910 +    "", // TODO
 31.1911 +    "", // TODO
 31.1912 +    "", // TODO
 31.1913 +    "Niveau limite",
 31.1914 +    "", // TODO
 31.1915 +    "Rajoittimen taso",
 31.1916 +    "", // TODO
 31.1917 +    "Limitador del nivel", 
 31.1918 +    "", // TODO
 31.1919 +    "Begränsningsnivå",
 31.1920 +    "", // TODO
 31.1921 +    "", // TODO
 31.1922 +    "Limitador del nivell",
 31.1923 +#if APIVERSNUM >= 10302
 31.1924 +    "¿ÞàÞÓ ÞÓàÐÝØçØâÕÛï ßØÚÞÒ",
 31.1925 +#endif
 31.1926 +  },
 31.1927 +  { "Setup.MP3$Abort player at end of list",
 31.1928 +    "Abspieler am Listenende beenden",
 31.1929 +    "", // TODO
 31.1930 +    "", // TODO
 31.1931 +    "", // TODO
 31.1932 +    "", // TODO
 31.1933 +    "Stopper le lecteur à la fin de la liste",
 31.1934 +    "", // TODO
 31.1935 +    "Keskeytä soittolistan loputtua",
 31.1936 +    "", // TODO
 31.1937 +    "", // TODO
 31.1938 +    "", // TODO
 31.1939 +    "", // TODO
 31.1940 +    "", // TODO
 31.1941 +    "", // TODO
 31.1942 +    "", // TODO
 31.1943 +#if APIVERSNUM >= 10302
 31.1944 +    "²ëåÞÔ Ò ÚÞÝæÕ áßØáÚÐ ßÕáÕÝ",
 31.1945 +#endif
 31.1946 +  },
 31.1947 +  { "Setup.MP3$Replay display",
 31.1948 +    "Wiedergabe Anzeige",
 31.1949 +    "", // TODO
 31.1950 +    "", // TODO
 31.1951 +    "", // TODO
 31.1952 +    "", // TODO
 31.1953 +    "Affichage relecture",
 31.1954 +    "", // TODO
 31.1955 +    "Toistotilan näyttömoodi",
 31.1956 +    "", // TODO
 31.1957 +    "", // TODO
 31.1958 +    "", // TODO
 31.1959 +    "", // TODO
 31.1960 +    "", // TODO
 31.1961 +    "", // TODO
 31.1962 +    "", // TODO
 31.1963 +#if APIVERSNUM >= 10302
 31.1964 +    "¸ÝâÕàäÕÙá Ò àÕÖØÜÕ ÒÞáßàÞØ×ÒÕÔÕÝØï",
 31.1965 +#endif
 31.1966 +  },
 31.1967 +  { "classic",
 31.1968 +    "klassisch",
 31.1969 +    "", // TODO
 31.1970 +    "", // TODO
 31.1971 +    "", // TODO
 31.1972 +    "", // TODO
 31.1973 +    "Classique",
 31.1974 +    "", // TODO
 31.1975 +    "klassinen",
 31.1976 +    "", // TODO
 31.1977 +    "", // TODO
 31.1978 +    "", // TODO
 31.1979 +    "", // TODO
 31.1980 +    "", // TODO
 31.1981 +    "", // TODO
 31.1982 +    "", // TODO
 31.1983 +#if APIVERSNUM >= 10302
 31.1984 +    "ÚÛÐááØçÕáÚØÙ",
 31.1985 +#endif
 31.1986 +  },
 31.1987 +  { "via skin",
 31.1988 +    "über Skin",
 31.1989 +    "", // TODO
 31.1990 +    "", // TODO
 31.1991 +    "", // TODO
 31.1992 +    "", // TODO
 31.1993 +    "Par skin",
 31.1994 +    "", // TODO
 31.1995 +    "ulkoasun mukaan",
 31.1996 +    "", // TODO
 31.1997 +    "", // TODO
 31.1998 +    "", // TODO
 31.1999 +    "", // TODO
 31.2000 +    "", // TODO
 31.2001 +    "", // TODO
 31.2002 +    "", // TODO
 31.2003 +#if APIVERSNUM >= 10302
 31.2004 +    "áÞÓÛÐáÝÞ âÕÜÕ",
 31.2005 +#endif
 31.2006 +  },
 31.2007 +  { "Setup.MP3$Keep selection menu",
 31.2008 +    "Auswahlmenu geöffnet lassen",
 31.2009 +    "", // TODO
 31.2010 +    "", // TODO
 31.2011 +    "", // TODO
 31.2012 +    "", // TODO
 31.2013 +    "",
 31.2014 +    "", // TODO
 31.2015 +    "Pidä valintaikkunaa auki",
 31.2016 +    "", // TODO
 31.2017 +    "", // TODO
 31.2018 +    "", // TODO
 31.2019 +    "", // TODO
 31.2020 +    "", // TODO
 31.2021 +    "", // TODO
 31.2022 +    "", // TODO
 31.2023 +#if APIVERSNUM >= 10302
 31.2024 +    "",
 31.2025 +#endif
 31.2026 +  },
 31.2027 +
 31.2028 +// start of MPlayer specific phrases
 31.2029 +
 31.2030 +  { "MPlayer",
 31.2031 +    "MPlayer",
 31.2032 +    "MPlayer",
 31.2033 +    "MPlayer",
 31.2034 +    "MPlayer",
 31.2035 +    "MPlayer",
 31.2036 +    "MPlayer",
 31.2037 +    "MPlayer",
 31.2038 +    "MPlayer-mediasoitin",
 31.2039 +    "MPlayer",
 31.2040 +    "MPlayer",
 31.2041 +    "MPlayer",
 31.2042 +    "MPlayer",
 31.2043 +    "MPlayer",
 31.2044 +    "MPlayer",
 31.2045 +    "MPlayer",
 31.2046 +#if APIVERSNUM >= 10302
 31.2047 +    "¿àÞØÓàëÒÐâÕÛì ÒØÔÕÞ",
 31.2048 +#endif
 31.2049 +  },
 31.2050 +  { "Media replay via MPlayer",
 31.2051 +    "Medien Wiedergabe über MPlayer",
 31.2052 +    "", // TODO
 31.2053 +    "", // TODO
 31.2054 +    "", // TODO
 31.2055 +    "", // TODO
 31.2056 +    "Lecture par MPlayer",
 31.2057 +    "", // TODO
 31.2058 +    "MPlayeriin perustuva mediasoitin",
 31.2059 +    "", // TODO
 31.2060 +    "", // TODO
 31.2061 +    "", // TODO
 31.2062 +    "", // TODO
 31.2063 +    "", // TODO
 31.2064 +    "", // TODO
 31.2065 +    "", // TODO
 31.2066 +#if APIVERSNUM >= 10302
 31.2067 +    "¿àÞØÓàëÒÐâÕÛì ÒØÔÕÞäÐÙÛÞÒ",
 31.2068 +#endif
 31.2069 +  },
 31.2070 +  { "MPlayer browser",
 31.2071 +    "MPlayer Verzeichnisanzeige",
 31.2072 +    "MPlayer navigator",
 31.2073 +    "", // TODO
 31.2074 +    "", // TODO
 31.2075 +    "", // TODO
 31.2076 +    "Navigateur MPlayer",
 31.2077 +    "", // TODO
 31.2078 +    "MPlayer-hakemistoselain",
 31.2079 +    "", // TODO
 31.2080 +    "Navegador del MPlayer",
 31.2081 +    "Mplayer endiksi fakelon",
 31.2082 +    "MPlayer-bläddrare",
 31.2083 +    "", // TODO
 31.2084 +    "", // TODO
 31.2085 +    "Navegador del MPlayer",
 31.2086 +#if APIVERSNUM >= 10302
 31.2087 +    "¿àÞáÜÞâà ÚÐâÐÛÞÓÞÒ MPlayer",
 31.2088 +#endif
 31.2089 +  },
 31.2090 +  { "MPlayer source",
 31.2091 +    "MPlayer Datenträger",
 31.2092 +    "MPlayer izvor",
 31.2093 +    "", // TODO
 31.2094 +    "", // TODO
 31.2095 +    "", // TODO
 31.2096 +    "Source MPlayer",
 31.2097 +    "", // TODO
 31.2098 +    "MPlayer-lähde",
 31.2099 +    "Orígen del Mplayer", 
 31.2100 +    "", // TODO
 31.2101 +    "Pigi MPlayer",
 31.2102 +    "MPlayer källa",
 31.2103 +    "", // TODO
 31.2104 +    "", // TODO
 31.2105 +    "Orígen del MPlayer",
 31.2106 +#if APIVERSNUM >= 10302
 31.2107 +    "¸áâÞçÝØÚ ÒØÔÕÞ MPlayer",
 31.2108 +#endif
 31.2109 +  },
 31.2110 +  { "Setup.MPlayer$Control mode",
 31.2111 +    "Kontroll Modus",
 31.2112 +    "", // TODO
 31.2113 +    "", // TODO
 31.2114 +    "", // TODO
 31.2115 +    "", // TODO
 31.2116 +    "Mode de controle",
 31.2117 +    "", // TODO
 31.2118 +    "Komentotila",
 31.2119 +    "", // TODO
 31.2120 +    "Modo de control", 
 31.2121 +    "", // TODO
 31.2122 +    "Kontrolläge",
 31.2123 +    "", // TODO
 31.2124 +    "", // TODO
 31.2125 +    "Mode de control",
 31.2126 +#if APIVERSNUM >= 10302
 31.2127 +    "ÀÕÖØÜ ãßàÐÒÛÕÝØï",
 31.2128 +#endif
 31.2129 +  },
 31.2130 +  { "Traditional",
 31.2131 +    "Traditionell",
 31.2132 +    "", // TODO
 31.2133 +    "", // TODO
 31.2134 +    "", // TODO
 31.2135 +    "", // TODO
 31.2136 +    "Traditionnel",
 31.2137 +    "", // TODO
 31.2138 +    "perinteinen",
 31.2139 +    "", // TODO
 31.2140 +    "Tradicional", 
 31.2141 +    "", // TODO
 31.2142 +    "Traditionell",
 31.2143 +    "", // TODO
 31.2144 +    "", // TODO
 31.2145 +    "Tradicional",
 31.2146 +#if APIVERSNUM >= 10302
 31.2147 +    "ÞÑëçÝëÙ",
 31.2148 +#endif
 31.2149 +  },
 31.2150 +  { "Slave",
 31.2151 +    "Slave",
 31.2152 +    "", // TODO
 31.2153 +    "", // TODO
 31.2154 +    "", // TODO
 31.2155 +    "", // TODO
 31.2156 +    "Esclave",
 31.2157 +    "", // TODO
 31.2158 +    "orja",
 31.2159 +    "", // TODO
 31.2160 +    "Esclavo", 
 31.2161 +    "", // TODO
 31.2162 +    "Slav",
 31.2163 +    "", // TODO
 31.2164 +    "", // TODO
 31.2165 +    "Esclau",
 31.2166 +#if APIVERSNUM >= 10302
 31.2167 +    "àÐáèØàÕÝÝëÙ",
 31.2168 +#endif
 31.2169 +  },
 31.2170 +  { "Setup.MPlayer$OSD position",
 31.2171 +    "OSD Position",
 31.2172 +    "", // TODO
 31.2173 +    "", // TODO
 31.2174 +    "", // TODO
 31.2175 +    "", // TODO
 31.2176 +    "",
 31.2177 +    "", // TODO
 31.2178 +    "Kuvaruutunäytön sijainti",
 31.2179 +    "", // TODO
 31.2180 +    "", 
 31.2181 +    "", // TODO
 31.2182 +    "",
 31.2183 +    "", // TODO
 31.2184 +    "", // TODO
 31.2185 +    "",
 31.2186 +#if APIVERSNUM >= 10302
 31.2187 +    "",
 31.2188 +#endif
 31.2189 +  },
 31.2190 +  { "Setup.MPlayer$Resume mode",
 31.2191 +    "Modus für Wiederaufnahme",
 31.2192 +    "", // TODO
 31.2193 +    "", // TODO
 31.2194 +    "", // TODO
 31.2195 +    "", // TODO
 31.2196 +    "Reprise",
 31.2197 +    "", // TODO
 31.2198 +    "Resume-toiminto",
 31.2199 +    "", // TODO
 31.2200 +    "", 
 31.2201 +    "", // TODO
 31.2202 +    "",
 31.2203 +    "", // TODO
 31.2204 +    "", // TODO
 31.2205 +    "",
 31.2206 +#if APIVERSNUM >= 10302
 31.2207 +    "",
 31.2208 +#endif
 31.2209 +  },
 31.2210 +  { "local first",
 31.2211 +    "zuerst local",
 31.2212 +    "", // TODO
 31.2213 +    "", // TODO
 31.2214 +    "", // TODO
 31.2215 +    "", // TODO
 31.2216 +    "Local en premier",
 31.2217 +    "", // TODO
 31.2218 +    "paikallinen",
 31.2219 +    "", // TODO
 31.2220 +    "", 
 31.2221 +    "", // TODO
 31.2222 +    "",
 31.2223 +    "", // TODO
 31.2224 +    "", // TODO
 31.2225 +    "",
 31.2226 +#if APIVERSNUM >= 10302
 31.2227 +    "",
 31.2228 +#endif
 31.2229 +  },
 31.2230 +  { "global only",
 31.2231 +    "nur global",
 31.2232 +    "", // TODO
 31.2233 +    "", // TODO
 31.2234 +    "", // TODO
 31.2235 +    "", // TODO
 31.2236 +    "Global seulement",
 31.2237 +    "", // TODO
 31.2238 +    "globaali",
 31.2239 +    "", // TODO
 31.2240 +    "", 
 31.2241 +    "", // TODO
 31.2242 +    "",
 31.2243 +    "", // TODO
 31.2244 +    "", // TODO
 31.2245 +    "",
 31.2246 +#if APIVERSNUM >= 10302
 31.2247 +    "",
 31.2248 +#endif
 31.2249 +  },
 31.2250 +  { "Setup.MPlayer$Slave command key",
 31.2251 +    "Slave Kommando Taste",
 31.2252 +    "", // TODO
 31.2253 +    "", // TODO
 31.2254 +    "", // TODO
 31.2255 +    "", // TODO
 31.2256 +    "",
 31.2257 +    "", // TODO
 31.2258 +    "Orjakomento näppäimelle",
 31.2259 +    "", // TODO
 31.2260 +    "", 
 31.2261 +    "", // TODO
 31.2262 +    "",
 31.2263 +    "", // TODO
 31.2264 +    "", // TODO
 31.2265 +    "",
 31.2266 +#if APIVERSNUM >= 10302
 31.2267 +    "",
 31.2268 +#endif
 31.2269 +  },
 31.2270 +  { "MPlayer Audio ID",
 31.2271 +    "MPlayer Audio ID",
 31.2272 +    "", // TODO
 31.2273 +    "", // TODO
 31.2274 +    "", // TODO
 31.2275 +    "", // TODO
 31.2276 +    "Mplayer piste audio",
 31.2277 +    "", // TODO
 31.2278 +    "MPlayerin ääniraidan valinta",
 31.2279 +    "", // TODO
 31.2280 +    "", 
 31.2281 +    "", // TODO
 31.2282 +    "",
 31.2283 +    "", // TODO
 31.2284 +    "", // TODO
 31.2285 +    "",
 31.2286 +#if APIVERSNUM >= 10302
 31.2287 +    "",
 31.2288 +#endif
 31.2289 +  },
 31.2290 +  { "Audiostream ID",
 31.2291 +    "Tonspur ID",
 31.2292 +    "", // TODO
 31.2293 +    "", // TODO
 31.2294 +    "", // TODO
 31.2295 +    "", // TODO
 31.2296 +    "Canal audio",
 31.2297 +    "", // TODO
 31.2298 +    "Ääniraidan ID",
 31.2299 +    "", // TODO
 31.2300 +    "", 
 31.2301 +    "", // TODO
 31.2302 +    "",
 31.2303 +    "", // TODO
 31.2304 +    "", // TODO
 31.2305 +    "",
 31.2306 +#if APIVERSNUM >= 10302
 31.2307 +    "",
 31.2308 +#endif
 31.2309 +  },
 31.2310 +
 31.2311 +  { NULL }
 31.2312 +  };
    32.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.2 +++ b/i18n.h	Sat Dec 29 14:47:40 2007 +0100
    32.3 @@ -0,0 +1,33 @@
    32.4 +/*
    32.5 + * MP3/MPlayer plugin to VDR (C++)
    32.6 + *
    32.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    32.8 + *
    32.9 + * This code is free software; you can redistribute it and/or
   32.10 + * modify it under the terms of the GNU General Public License
   32.11 + * as published by the Free Software Foundation; either version 2
   32.12 + * of the License, or (at your option) any later version.
   32.13 + *
   32.14 + * This code is distributed in the hope that it will be useful,
   32.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   32.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   32.17 + * GNU General Public License for more details.
   32.18 + *
   32.19 + * You should have received a copy of the GNU General Public License
   32.20 + * along with this program; if not, write to the Free Software
   32.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   32.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   32.23 + */
   32.24 +
   32.25 +#ifndef ___I18N_H
   32.26 +#define ___I18N_H
   32.27 +
   32.28 +#include <vdr/i18n.h>
   32.29 +
   32.30 +extern const char *i18n_name;
   32.31 +extern const tI18nPhrase Phrases[];
   32.32 +
   32.33 +#undef tr
   32.34 +#define tr(s)  I18nTranslate(s, i18n_name)
   32.35 +
   32.36 +#endif //___I18N_H
    33.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.2 +++ b/i18ntest.c	Sat Dec 29 14:47:40 2007 +0100
    33.3 @@ -0,0 +1,24 @@
    33.4 +
    33.5 +#include <stdio.h>
    33.6 +#include <stdlib.h>
    33.7 +
    33.8 +#undef APIVERSNUM
    33.9 +#define APIVERSNUM 10307 // to keep common.h happy
   33.10 +
   33.11 +#include "i18n.h"
   33.12 +#include "i18n.c"
   33.13 +
   33.14 +int main(int argc, char *argv[])
   33.15 +{
   33.16 +  if(argc<2) return 1;
   33.17 +  int num=atoi(argv[1]);
   33.18 +  if(num<1 || num>I18nNumLanguages) return 1;
   33.19 +
   33.20 +  const tI18nPhrase *p=Phrases;
   33.21 +  while(*p[0]) {
   33.22 +    if((*p)[num-1]==0 || *((*p)[num-1])==0)
   33.23 +      printf("missing translation for '%s'\n",*p[0]);
   33.24 +    p++;
   33.25 +    }
   33.26 +}
   33.27 +
    34.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.2 +++ b/menu-async.h	Sat Dec 29 14:47:40 2007 +0100
    34.3 @@ -0,0 +1,44 @@
    34.4 +/*
    34.5 + * MP3/MPlayer plugin to VDR (C++)
    34.6 + *
    34.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    34.8 + *
    34.9 + * This code is free software; you can redistribute it and/or
   34.10 + * modify it under the terms of the GNU General Public License
   34.11 + * as published by the Free Software Foundation; either version 2
   34.12 + * of the License, or (at your option) any later version.
   34.13 + *
   34.14 + * This code is distributed in the hope that it will be useful,
   34.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   34.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   34.17 + * GNU General Public License for more details.
   34.18 + *
   34.19 + * You should have received a copy of the GNU General Public License
   34.20 + * along with this program; if not, write to the Free Software
   34.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   34.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   34.23 + */
   34.24 +
   34.25 +#ifndef ___MENU_ASYNC_H
   34.26 +#define ___MENU_ASYNC_H
   34.27 +
   34.28 +#include <vdr/thread.h>
   34.29 +
   34.30 +// ----------------------------------------------------------------
   34.31 +
   34.32 +class cAsyncStatus : private cMutex {
   34.33 +private:
   34.34 +  const char *text;
   34.35 +  bool changed;
   34.36 +public:
   34.37 +  cAsyncStatus(void);
   34.38 +  ~cAsyncStatus();
   34.39 +  void Set(const char *Text);
   34.40 +  bool Changed(void) { return changed; }
   34.41 +  const char *Begin(void);
   34.42 +  void Finish(void);
   34.43 +  };
   34.44 +
   34.45 +extern cAsyncStatus asyncStatus;
   34.46 +
   34.47 +#endif //___MENU_ASYNC_H
    35.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.2 +++ b/menu.c	Sat Dec 29 14:47:40 2007 +0100
    35.3 @@ -0,0 +1,402 @@
    35.4 +/*
    35.5 + * MP3/MPlayer plugin to VDR (C++)
    35.6 + *
    35.7 + * (C) 2001-2007 Stefan Huelswitt <s.huelswitt@gmx.de>
    35.8 + *
    35.9 + * This code is free software; you can redistribute it and/or
   35.10 + * modify it under the terms of the GNU General Public License
   35.11 + * as published by the Free Software Foundation; either version 2
   35.12 + * of the License, or (at your option) any later version.
   35.13 + *
   35.14 + * This code is distributed in the hope that it will be useful,
   35.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   35.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   35.17 + * GNU General Public License for more details.
   35.18 + *
   35.19 + * You should have received a copy of the GNU General Public License
   35.20 + * along with this program; if not, write to the Free Software
   35.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   35.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   35.23 + */
   35.24 +
   35.25 +#include <ctype.h>
   35.26 +#include <dirent.h>
   35.27 +#include <stdlib.h>
   35.28 +#include <stdio.h>
   35.29 +#include <errno.h>
   35.30 +#include <sys/stat.h>
   35.31 +#include <sys/ioctl.h>
   35.32 +#include <sys/types.h>
   35.33 +#include <unistd.h>
   35.34 +
   35.35 +#include "common.h"
   35.36 +#include "menu.h"
   35.37 +#include "data.h"
   35.38 +#include "data-src.h"
   35.39 +#include "i18n.h"
   35.40 +
   35.41 +// -----------------------------------------------------------------------------
   35.42 +
   35.43 +void Status(const char *text)
   35.44 +{
   35.45 +#if APIVERSNUM >= 10307
   35.46 +  Skins.Message(mtStatus,text);
   35.47 +#else
   35.48 +  if(text) {
   35.49 +    Interface->Status(text);
   35.50 +    Interface->Flush();
   35.51 +    }
   35.52 +#endif
   35.53 +}
   35.54 +
   35.55 +void Error(const char *text)
   35.56 +{
   35.57 +#if APIVERSNUM >= 10307
   35.58 +  Skins.Message(mtError,text);
   35.59 +#else
   35.60 +  Interface->Error(text);
   35.61 +#endif
   35.62 +}
   35.63 +
   35.64 +void Info(const char *text)
   35.65 +{
   35.66 +#if APIVERSNUM >= 10307
   35.67 +  Skins.Message(mtInfo,text);
   35.68 +#else
   35.69 +  Interface->Info(text);
   35.70 +#endif
   35.71 +}
   35.72 +
   35.73 +// --- cMenuBrowseItem ---------------------------------------------------------
   35.74 +
   35.75 +class cMenuBrowseItem : public cOsdItem {
   35.76 +  private:
   35.77 +  cFileObj *item;
   35.78 +  virtual void Set(void);
   35.79 +public:
   35.80 +  cMenuBrowseItem(cFileObj *Item);
   35.81 +  cFileObj *Item(void) { return item; }
   35.82 +  };
   35.83 +
   35.84 +cMenuBrowseItem::cMenuBrowseItem(cFileObj *Item)
   35.85 +{
   35.86 +  item = Item;
   35.87 +  Set();
   35.88 +}
   35.89 +
   35.90 +void cMenuBrowseItem::Set(void)
   35.91 +{
   35.92 +  char *buffer=0;
   35.93 +  asprintf(&buffer,item->Type()==otFile?"%s":"[%s]",item->Name());
   35.94 +  SetText(buffer,false);
   35.95 +}
   35.96 +
   35.97 +// --- cMenuBrowse ------------------------------------------------------
   35.98 +
   35.99 +cFileObj *cMenuBrowse::lastselect=0;
  35.100 +
  35.101 +cMenuBrowse::cMenuBrowse(cFileSource *Source, bool Dirselect, bool WithID3, const char *title, const char * const *Excl)
  35.102 +:cOsdMenu(title)
  35.103 +{
  35.104 +  currentdir=parent=0;
  35.105 +  delete lastselect; lastselect=0;
  35.106 +  list=new cDirList;
  35.107 +
  35.108 +  dirselectable=Dirselect;
  35.109 +  withID3=WithID3;
  35.110 +  excl=Excl;
  35.111 +
  35.112 +  SetSource(Source); 
  35.113 +  NewDir(currentdir);
  35.114 +}
  35.115 +
  35.116 +cMenuBrowse::~cMenuBrowse()
  35.117 +{
  35.118 +  free(parent);
  35.119 +  free(currentdir);
  35.120 +  delete list;
  35.121 +}
  35.122 +
  35.123 +cFileObj *cMenuBrowse::CurrentItem(void)
  35.124 +{
  35.125 +  cMenuBrowseItem *item = (cMenuBrowseItem *)Get(Current());
  35.126 +  return item ? item->Item():0;
  35.127 +}
  35.128 +
  35.129 +void cMenuBrowse::SetButtons(void)
  35.130 +{
  35.131 +  SetHelp(tr("Select"), currentdir?tr("Parent"):0, 0, withID3?tr("ID3 info"):0);
  35.132 +  Display();
  35.133 +}
  35.134 +
  35.135 +void cMenuBrowse::SetSource(cFileSource *Source)
  35.136 +{
  35.137 +  source=Source;
  35.138 +  free(currentdir); currentdir=0;
  35.139 +  free(parent); parent=0;
  35.140 +  source->GetRemember(currentdir,parent);
  35.141 +}
  35.142 +
  35.143 +bool cMenuBrowse::LoadDir(const char *dir)
  35.144 +{
  35.145 +  Clear();
  35.146 +  Status(tr("Scanning directory..."));
  35.147 +  bool res=list->Load(source,dir,excl);
  35.148 +  if(res) {
  35.149 +    cFileObj *item=list->First();
  35.150 +    while(item) {
  35.151 +      Add(new cMenuBrowseItem(item),(parent && !strcmp(item->Name(),parent)));
  35.152 +      item=list->Next(item);
  35.153 +      }
  35.154 +    }
  35.155 +  Status(0);
  35.156 +  return res;
  35.157 +}
  35.158 +
  35.159 +bool cMenuBrowse::NewDir(const char *dir)
  35.160 +{
  35.161 +  char *ncur=dir ? strdup(dir):0;
  35.162 +  bool r=LoadDir(ncur);
  35.163 +  if(!r && ncur) {
  35.164 +    free(ncur); ncur=0;
  35.165 +    r=LoadDir(ncur);
  35.166 +    }
  35.167 +  if(r) {
  35.168 +    free(currentdir); currentdir=ncur;
  35.169 +
  35.170 +    cFileObj *item=CurrentItem();
  35.171 +    source->SetRemember(currentdir,item?item->Name():0);
  35.172 +
  35.173 +    SetButtons();
  35.174 +    return true;
  35.175 +    }
  35.176 +  free(ncur);
  35.177 +  Error(tr("Error scanning directory!"));
  35.178 +  return false;
  35.179 +}
  35.180 +
  35.181 +eOSState cMenuBrowse::Parent(void)
  35.182 +{
  35.183 +  eOSState res=osContinue;
  35.184 +
  35.185 +  if(currentdir) {
  35.186 +    free(parent);
  35.187 +    char *ss=strrchr(currentdir,'/');
  35.188 +    if(ss) {
  35.189 +      *ss++=0;
  35.190 +      parent=strdup(ss);
  35.191 +      ss=currentdir;
  35.192 +      }
  35.193 +    else parent=strdup(currentdir);
  35.194 +
  35.195 +    if(!NewDir(ss)) res=osEnd;
  35.196 +    }
  35.197 +  return res;
  35.198 +}
  35.199 +
  35.200 +eOSState cMenuBrowse::Select(bool isred)
  35.201 +{
  35.202 +  eOSState res=osContinue;
  35.203 +  cFileObj *item;
  35.204 +
  35.205 +  if((item=CurrentItem())) {
  35.206 +    switch(item->Type()) {
  35.207 +      case otParent:
  35.208 +        if(!isred || !dirselectable)
  35.209 +          res=Parent();
  35.210 +        break;
  35.211 +      case otDir:
  35.212 +        if(!isred || !dirselectable) {
  35.213 +          if(!NewDir(item->Path())) res=osEnd;
  35.214 +          break;
  35.215 +          }
  35.216 +        // fall through to otFile
  35.217 +      case otFile:
  35.218 +        lastselect=new cFileObj(item);
  35.219 +        res=osBack;
  35.220 +        break;
  35.221 +      default:
  35.222 +        break;
  35.223 +      }
  35.224 +    } 
  35.225 +  return res;
  35.226 +}
  35.227 +
  35.228 +eOSState cMenuBrowse::ID3Info(void)
  35.229 +{
  35.230 +  return osContinue;
  35.231 +}
  35.232 +
  35.233 +eOSState cMenuBrowse::ProcessStdKey(eKeys Key, eOSState state)
  35.234 +{
  35.235 +  if(state==osUnknown) {
  35.236 +    switch (Key) {
  35.237 +      case kOk:     state=Select(false); break;
  35.238 +      case kRed:    state=Select(true); break;
  35.239 +      case kGreen:  state=Parent(); break;
  35.240 +      case kBlue:   if(withID3) state=ID3Info();
  35.241 +                    break;
  35.242 +      //case kMenu:   state=osEnd; break;
  35.243 +      default: break;
  35.244 +      }
  35.245 +    }
  35.246 +  if(state==osEnd || state==osBack) {
  35.247 +    cFileObj *item=CurrentItem();
  35.248 +    if(item) source->SetRemember(currentdir,item->Name());
  35.249 +    }
  35.250 +  return state;
  35.251 +}
  35.252 +
  35.253 +// --- cMenuSourceItem ----------------------------------------------------------
  35.254 +
  35.255 +class cMenuSourceItem : public cOsdItem {
  35.256 +  private:
  35.257 +  cFileSource *source;
  35.258 +  virtual void Set(void);
  35.259 +public:
  35.260 +  cMenuSourceItem(cFileSource *Source);
  35.261 +  cFileSource *Source(void) { return source; }
  35.262 +  };
  35.263 +
  35.264 +cMenuSourceItem::cMenuSourceItem(cFileSource *Source)
  35.265 +{
  35.266 +  source=Source;
  35.267 +  Set();
  35.268 +}
  35.269 +
  35.270 +void cMenuSourceItem::Set(void)
  35.271 +{
  35.272 +  char *buffer=0;
  35.273 +  asprintf(&buffer, "%s\t%s\t%s", source->NeedsMount()?(source->Status()?"*":">"):"", source->Description(), source->BaseDir());
  35.274 +  SetText(buffer,false);
  35.275 +}
  35.276 +
  35.277 +// --- cMenuSource --------------------------------------------------
  35.278 +
  35.279 +cFileSource *cMenuSource::selected=0;
  35.280 +
  35.281 +cMenuSource::cMenuSource(cFileSources *Sources, const char *title)
  35.282 +:cOsdMenu(title,2,20)
  35.283 +{
  35.284 +  selected=0;
  35.285 +  current=Sources->GetSource();
  35.286 +  cFileSource *source=Sources->First();
  35.287 +  while(source) {
  35.288 +    cOsdMenu::Add(new cMenuSourceItem(source),source==current);
  35.289 +    source=Sources->Next(source);
  35.290 +    }
  35.291 +
  35.292 +  SetHelp(tr("Select"), tr("Mount"), tr("Unmount"), tr("Eject"));
  35.293 +  Display();
  35.294 +}
  35.295 +
  35.296 +bool cMenuSource::DoMount(cFileSource *src)
  35.297 +{
  35.298 +  bool res=src->Mount();
  35.299 +  RefreshCurrent();
  35.300 +  DisplayCurrent(true);
  35.301 +  return res;
  35.302 +}
  35.303 +
  35.304 +bool cMenuSource::CheckMount(void)
  35.305 +{
  35.306 +  cFileSource *src=selected ? selected:current;
  35.307 +  if(src->NeedsMount() && !src->Status()) {
  35.308 +    Error(tr("Selected source is not mounted!"));
  35.309 +    return false;
  35.310 +    }
  35.311 +  return true;
  35.312 +}
  35.313 +
  35.314 +eOSState cMenuSource::Select(void)
  35.315 +{
  35.316 +  if(HasSubMenu() || Count() == 0) return osContinue;
  35.317 +
  35.318 +  cFileSource *src = ((cMenuSourceItem *)Get(Current()))->Source();
  35.319 +  if(src->NeedsMount() && !src->Status()) {
  35.320 +    if(!DoMount(src)) Error(tr("Mount failed!"));
  35.321 +    }
  35.322 +  if(!src->NeedsMount() || src->Status()) {
  35.323 +    selected=src;
  35.324 +    return osBack;
  35.325 +    }
  35.326 +  return osContinue;
  35.327 +}
  35.328 +
  35.329 +eOSState cMenuSource::Mount(void)
  35.330 +{
  35.331 +  if(HasSubMenu() || Count() == 0) return osContinue;
  35.332 +
  35.333 +  cFileSource *src = ((cMenuSourceItem *)Get(Current()))->Source();
  35.334 +  if(src->NeedsMount() && !src->Status()) {
  35.335 +    if(DoMount(src)) Info(tr("Mount succeeded"));
  35.336 +    else Error(tr("Mount failed!"));
  35.337 +    }
  35.338 +  return osContinue;
  35.339 +}
  35.340 +
  35.341 +eOSState cMenuSource::Unmount(void)
  35.342 +{
  35.343 +  if(HasSubMenu() || Count() == 0) return osContinue;
  35.344 +
  35.345 +  cFileSource *src = ((cMenuSourceItem *)Get(Current()))->Source();
  35.346 +  if(src->NeedsMount() && src->Status()) {
  35.347 +    bool res=src->Unmount();
  35.348 +    RefreshCurrent();
  35.349 +    DisplayCurrent(true);
  35.350 +    if(res) Info(tr("Unmount succeeded"));
  35.351 +    else Error(tr("Unmount failed!"));
  35.352 +    }
  35.353 +  return osContinue;
  35.354 +}
  35.355 +
  35.356 +eOSState cMenuSource::Eject(void)
  35.357 +{
  35.358 +  if(HasSubMenu() || Count() == 0) return osContinue;
  35.359 +
  35.360 +  cFileSource *src = ((cMenuSourceItem *)Get(Current()))->Source();
  35.361 +  if(src->NeedsMount()) {
  35.362 +    bool res=src->Eject();
  35.363 +    RefreshCurrent();
  35.364 +    DisplayCurrent(true);
  35.365 +    if(!res) Error(tr("Eject failed!"));
  35.366 +    }
  35.367 +  return osContinue;
  35.368 +}
  35.369 +
  35.370 +eOSState cMenuSource::ProcessKey(eKeys Key)
  35.371 +{
  35.372 +  eOSState state = cOsdMenu::ProcessKey(Key);
  35.373 +
  35.374 +  if(state==osBack && !CheckMount()) state=osContinue;
  35.375 +  if(state==osUnknown) {
  35.376 +     switch(Key) {
  35.377 +       case kOk:     
  35.378 +       case kRed:    return Select();
  35.379 +       case kGreen:  return Mount();
  35.380 +       case kYellow: return Unmount();
  35.381 +       case kBlue:   return Eject();
  35.382 +       case kMenu:   CheckMount(); return osEnd;
  35.383 +       default: break;
  35.384 +       }
  35.385 +     }
  35.386 +  return state;
  35.387 +}
  35.388 +
  35.389 +// --- cProgressBar ------------------------------------------------------------
  35.390 +
  35.391 +cProgressBar::cProgressBar(int Width, int Height, int Current, int Total)
  35.392 +:cBitmap(Width, Height, 2)
  35.393 +{
  35.394 +  if(Total > 0) {
  35.395 +    int p = Current * Width / Total;;
  35.396 +#if APIVERSNUM >= 10307
  35.397 +    DrawRectangle(0, 0, p, Height - 1, clrGreen);
  35.398 +    DrawRectangle(p + 1, 0, Width - 1, Height - 1, clrWhite);
  35.399 +#else
  35.400 +    Fill(0, 0, p, Height - 1, clrGreen);
  35.401 +    Fill(p + 1, 0, Width - 1, Height - 1, clrWhite);
  35.402 +#endif
  35.403 +    }
  35.404 +}
  35.405 +
    36.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.2 +++ b/menu.h	Sat Dec 29 14:47:40 2007 +0100
    36.3 @@ -0,0 +1,99 @@
    36.4 +/*
    36.5 + * MP3/MPlayer plugin to VDR (C++)
    36.6 + *
    36.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    36.8 + *
    36.9 + * This code is free software; you can redistribute it and/or
   36.10 + * modify it under the terms of the GNU General Public License
   36.11 + * as published by the Free Software Foundation; either version 2
   36.12 + * of the License, or (at your option) any later version.
   36.13 + *
   36.14 + * This code is distributed in the hope that it will be useful,
   36.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   36.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   36.17 + * GNU General Public License for more details.
   36.18 + *
   36.19 + * You should have received a copy of the GNU General Public License
   36.20 + * along with this program; if not, write to the Free Software
   36.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   36.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   36.23 + */
   36.24 +
   36.25 +#ifndef ___MENU_H
   36.26 +#define ___MENU_H
   36.27 +
   36.28 +#include "common.h"
   36.29 +#if APIVERSNUM >= 10307
   36.30 +#include <vdr/osdbase.h>
   36.31 +#else
   36.32 +#include <vdr/osd.h>
   36.33 +#endif
   36.34 +
   36.35 +// ----------------------------------------------------------------
   36.36 +
   36.37 +void Status(const char *text);
   36.38 +void Error(const char *text);
   36.39 +void Info(const char *text);
   36.40 +
   36.41 +// ----------------------------------------------------------------
   36.42 +
   36.43 +class cFileSources;
   36.44 +class cFileSource;
   36.45 +class cFileObj;
   36.46 +class cDirList;
   36.47 +
   36.48 +// ----------------------------------------------------------------
   36.49 +
   36.50 +class cMenuBrowse : public cOsdMenu {
   36.51 +private:
   36.52 +  eOSState Select(bool isred);
   36.53 +  eOSState Parent(void);
   36.54 +  bool LoadDir(const char *dir);
   36.55 +protected:
   36.56 +  static cFileObj *lastselect;
   36.57 +  //
   36.58 +  cDirList *list;
   36.59 +  cFileSource *source;
   36.60 +  bool dirselectable, withID3;
   36.61 +  char *currentdir, *parent;
   36.62 +  const char * const *excl;
   36.63 +//
   36.64 +  bool NewDir(const char *dir);
   36.65 +  void SetSource(cFileSource *Source);
   36.66 +  cFileObj *CurrentItem(void);
   36.67 +  virtual void SetButtons(void);
   36.68 +  virtual eOSState ID3Info(void);
   36.69 +  virtual eOSState ProcessStdKey(eKeys Key, eOSState state);
   36.70 +public:
   36.71 +  cMenuBrowse(cFileSource *Source, bool Dirselect, bool WithID3, const char *title, const char * const *Excl);
   36.72 +  ~cMenuBrowse();
   36.73 +  static cFileObj *GetSelected(void) { return lastselect; }
   36.74 +  };
   36.75 +
   36.76 +// ----------------------------------------------------------------
   36.77 +
   36.78 +class cMenuSource : public cOsdMenu {
   36.79 +private:
   36.80 +  static cFileSource *selected;
   36.81 +  cFileSource *current;
   36.82 +  //
   36.83 +  eOSState Mount(void);
   36.84 +  eOSState Unmount(void);
   36.85 +  eOSState Eject(void);
   36.86 +  eOSState Select(void);
   36.87 +  bool DoMount(cFileSource *src);
   36.88 +  bool CheckMount(void);
   36.89 +public:
   36.90 +  cMenuSource(cFileSources *Sources, const char *title);
   36.91 +  virtual eOSState ProcessKey(eKeys Key);
   36.92 +  static cFileSource *GetSelected(void) { return selected; }
   36.93 +  };
   36.94 +
   36.95 +// ----------------------------------------------------------------
   36.96 +
   36.97 +class cProgressBar : public cBitmap {
   36.98 +public:
   36.99 +  cProgressBar(int Width, int Height, int Current, int Total);
  36.100 +  };
  36.101 +
  36.102 +#endif //___MENU_H
    37.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    37.2 +++ b/mp3.c	Sat Dec 29 14:47:40 2007 +0100
    37.3 @@ -0,0 +1,1857 @@
    37.4 +/*
    37.5 + * MP3/MPlayer plugin to VDR (C++)
    37.6 + *
    37.7 + * (C) 2001-2007 Stefan Huelswitt <s.huelswitt@gmx.de>
    37.8 + *
    37.9 + * This code is free software; you can redistribute it and/or
   37.10 + * modify it under the terms of the GNU General Public License
   37.11 + * as published by the Free Software Foundation; either version 2
   37.12 + * of the License, or (at your option) any later version.
   37.13 + *
   37.14 + * This code is distributed in the hope that it will be useful,
   37.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   37.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   37.17 + * GNU General Public License for more details.
   37.18 + *
   37.19 + * You should have received a copy of the GNU General Public License
   37.20 + * along with this program; if not, write to the Free Software
   37.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   37.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   37.23 + */
   37.24 +
   37.25 +#include <stdlib.h>
   37.26 +#include <getopt.h>
   37.27 +#include <strings.h>
   37.28 +#include <typeinfo>
   37.29 +
   37.30 +#include "common.h"
   37.31 +
   37.32 +#include <vdr/menuitems.h>
   37.33 +#include <vdr/status.h>
   37.34 +#include <vdr/plugin.h>
   37.35 +#if APIVERSNUM >= 10307
   37.36 +#include <vdr/interface.h>
   37.37 +#include <vdr/skins.h>
   37.38 +#endif
   37.39 +
   37.40 +#include "setup.h"
   37.41 +#include "setup-mp3.h"
   37.42 +#include "data-mp3.h"
   37.43 +#include "data-src.h"
   37.44 +#include "player-mp3.h"
   37.45 +#include "menu.h"
   37.46 +#include "menu-async.h"
   37.47 +#include "decoder.h"
   37.48 +#include "i18n.h"
   37.49 +#include "version.h"
   37.50 +#include "service.h"
   37.51 +
   37.52 +#ifdef DEBUG
   37.53 +#include <mad.h>
   37.54 +#endif
   37.55 +
   37.56 +const char *sourcesSub=0;
   37.57 +cFileSources MP3Sources;
   37.58 +
   37.59 +// --- cMenuSetupMP3 --------------------------------------------------------
   37.60 +
   37.61 +class cMenuSetupMP3 : public cMenuSetupPage {
   37.62 +private:
   37.63 +  cMP3Setup data;
   37.64 +  //
   37.65 +  const char *cddb[3], *disp[2], *scan[3], *bgr[3];
   37.66 +  const char *aout[AUDIOOUTMODES];
   37.67 +  int amode, amodes[AUDIOOUTMODES];
   37.68 +protected:
   37.69 +  virtual void Store(void);
   37.70 +public:
   37.71 +  cMenuSetupMP3(void);
   37.72 +  };
   37.73 +
   37.74 +cMenuSetupMP3::cMenuSetupMP3(void)
   37.75 +{
   37.76 +  static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789-_" };
   37.77 +  int numModes=0;
   37.78 +  aout[numModes]=tr("DVB"); amodes[numModes]=AUDIOOUTMODE_DVB; numModes++;
   37.79 +#ifdef WITH_OSS
   37.80 +  aout[numModes]=tr("OSS"); amodes[numModes]=AUDIOOUTMODE_OSS; numModes++;
   37.81 +#endif
   37.82 +  data=MP3Setup;
   37.83 +  amode=0;
   37.84 +  for(int i=0; i<numModes; i++)
   37.85 +    if(amodes[i]==data.AudioOutMode) { amode=i; break; }
   37.86 +
   37.87 +  SetSection(tr("MP3"));
   37.88 +  Add(new cMenuEditStraItem(tr("Setup.MP3$Audio output mode"),     &amode,numModes,aout));
   37.89 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Audio mode"),            &data.AudioMode, tr("Round"), tr("Dither")));
   37.90 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Use 48kHz mode only"),   &data.Only48kHz));
   37.91 +#if APIVERSNUM >= 10307
   37.92 +  disp[0]=tr("classic");
   37.93 +  disp[1]=tr("via skin");
   37.94 +  Add(new cMenuEditStraItem(tr("Setup.MP3$Replay display"),        &data.ReplayDisplay, 2, disp));
   37.95 +#endif
   37.96 +  Add(new cMenuEditIntItem( tr("Setup.MP3$Display mode"),          &data.DisplayMode, 1, 3));
   37.97 +  bgr[0]=tr("Black");
   37.98 +  bgr[1]=tr("Live");
   37.99 +  bgr[2]=tr("Images");
  37.100 +  Add(new cMenuEditStraItem(tr("Setup.MP3$Background mode"),       &data.BackgrMode, 3, bgr));
  37.101 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Initial loop mode"),     &data.InitLoopMode));
  37.102 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Initial shuffle mode"),  &data.InitShuffleMode));
  37.103 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Abort player at end of list"),&data.AbortAtEOL));
  37.104 +  scan[0]=tr("disabled");
  37.105 +  scan[1]=tr("ID3 only");
  37.106 +  scan[2]=tr("ID3 & Level");
  37.107 +  Add(new cMenuEditStraItem(tr("Setup.MP3$Background scan"),       &data.BgrScan, 3, scan));
  37.108 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Editor display mode"),   &data.EditorMode, tr("Filenames"), tr("ID3 names")));
  37.109 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Mainmenu mode"),         &data.MenuMode, tr("Playlists"), tr("Browser")));
  37.110 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Keep selection menu"),   &data.KeepSelect));
  37.111 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Title/Artist order"),    &data.TitleArtistOrder, tr("Normal"), tr("Reversed")));
  37.112 +  Add(new cMenuEditBoolItem(tr("Hide mainmenu entry"),             &data.HideMainMenu));
  37.113 +  Add(new cMenuEditIntItem( tr("Setup.MP3$Normalizer level"),      &data.TargetLevel, 0, MAX_TARGET_LEVEL));
  37.114 +  Add(new cMenuEditIntItem( tr("Setup.MP3$Limiter level"),         &data.LimiterLevel, MIN_LIMITER_LEVEL, 100));
  37.115 +  Add(new cMenuEditBoolItem(tr("Setup.MP3$Use HTTP proxy"),        &data.UseProxy));
  37.116 +  Add(new cMenuEditStrItem( tr("Setup.MP3$HTTP proxy host"),       data.ProxyHost,MAX_HOSTNAME,allowed));
  37.117 +  Add(new cMenuEditIntItem( tr("Setup.MP3$HTTP proxy port"),       &data.ProxyPort,1,65535));
  37.118 +  cddb[0]=tr("disabled");
  37.119 +  cddb[1]=tr("local only");
  37.120 +  cddb[2]=tr("local&remote");
  37.121 +  Add(new cMenuEditStraItem(tr("Setup.MP3$CDDB for CD-Audio"),     &data.UseCddb,3,cddb));
  37.122 +  Add(new cMenuEditStrItem( tr("Setup.MP3$CDDB server"),           data.CddbHost,MAX_HOSTNAME,allowed));
  37.123 +  Add(new cMenuEditIntItem( tr("Setup.MP3$CDDB port"),             &data.CddbPort,1,65535));
  37.124 +}
  37.125 +
  37.126 +void cMenuSetupMP3::Store(void)
  37.127 +{
  37.128 +  data.AudioOutMode=amodes[amode];
  37.129 +
  37.130 +  MP3Setup=data;
  37.131 +  SetupStore("InitLoopMode",     MP3Setup.InitLoopMode   );
  37.132 +  SetupStore("InitShuffleMode",  MP3Setup.InitShuffleMode);
  37.133 +  SetupStore("AudioMode",        MP3Setup.AudioMode      );
  37.134 +  SetupStore("AudioOutMode",     MP3Setup.AudioOutMode   );
  37.135 +  SetupStore("BgrScan",          MP3Setup.BgrScan        );
  37.136 +  SetupStore("EditorMode",       MP3Setup.EditorMode     );
  37.137 +  SetupStore("DisplayMode",      MP3Setup.DisplayMode    );
  37.138 +  SetupStore("BackgrMode",       MP3Setup.BackgrMode     );
  37.139 +  SetupStore("MenuMode",         MP3Setup.MenuMode       );
  37.140 +  SetupStore("TargetLevel",      MP3Setup.TargetLevel    );
  37.141 +  SetupStore("LimiterLevel",     MP3Setup.LimiterLevel   );
  37.142 +  SetupStore("Only48kHz",        MP3Setup.Only48kHz      );
  37.143 +  SetupStore("UseProxy",         MP3Setup.UseProxy       );
  37.144 +  SetupStore("ProxyHost",        MP3Setup.ProxyHost      );
  37.145 +  SetupStore("ProxyPort",        MP3Setup.ProxyPort      );
  37.146 +  SetupStore("UseCddb",          MP3Setup.UseCddb        );
  37.147 +  SetupStore("CddbHost",         MP3Setup.CddbHost       );
  37.148 +  SetupStore("CddbPort",         MP3Setup.CddbPort       );
  37.149 +  SetupStore("AbortAtEOL",       MP3Setup.AbortAtEOL     );
  37.150 +#if APIVERSNUM >= 10307
  37.151 +  SetupStore("ReplayDisplay",    MP3Setup.ReplayDisplay  );
  37.152 +#endif
  37.153 +  SetupStore("HideMainMenu",     MP3Setup.HideMainMenu   );
  37.154 +  SetupStore("KeepSelect",       MP3Setup.KeepSelect     );
  37.155 +  SetupStore("TitleArtistOrder", MP3Setup.TitleArtistOrder);
  37.156 +}
  37.157 +
  37.158 +// --- cAsyncStatus ------------------------------------------------------------
  37.159 +
  37.160 +cAsyncStatus asyncStatus;
  37.161 +
  37.162 +cAsyncStatus::cAsyncStatus(void)
  37.163 +{
  37.164 +  text=0;
  37.165 +  changed=false;
  37.166 +}
  37.167 +
  37.168 +cAsyncStatus::~cAsyncStatus()
  37.169 +{
  37.170 +  free((void *)text);
  37.171 +}
  37.172 +
  37.173 +void cAsyncStatus::Set(const char *Text)
  37.174 +{
  37.175 +  Lock();
  37.176 +  free((void *)text);
  37.177 +  text=Text ? strdup(Text) : 0;
  37.178 +  changed=true;
  37.179 +  Unlock();
  37.180 +}
  37.181 +
  37.182 +const char *cAsyncStatus::Begin(void)
  37.183 +{
  37.184 +  Lock();
  37.185 +  return text;
  37.186 +}
  37.187 +
  37.188 +void cAsyncStatus::Finish(void)
  37.189 +{
  37.190 +  changed=false;
  37.191 +  Unlock();
  37.192 +}
  37.193 +
  37.194 +// --- --------------------------------------------------------------------
  37.195 +
  37.196 +static const char *TitleArtist(const char *title, const char *artist)
  37.197 +{
  37.198 +  static char buf[256]; // clearly not multi-thread save!
  37.199 +  char *fmt;
  37.200 +  if(artist && artist[0]) {
  37.201 +    if(MP3Setup.TitleArtistOrder) fmt="%2$s - %1$s";
  37.202 +    else  fmt="%s - %s";
  37.203 +    }
  37.204 +  else fmt="%s";
  37.205 +  snprintf(buf,sizeof(buf),fmt,title,artist);
  37.206 +  return buf;
  37.207 +}
  37.208 +
  37.209 +// --- cMP3Control --------------------------------------------------------
  37.210 +
  37.211 +#if APIVERSNUM >= 10307
  37.212 +#define clrBackground clrGray50
  37.213 +#define eDvbColor int
  37.214 +#define MAXROWS 120
  37.215 +#define INLINE
  37.216 +#else
  37.217 +#define MAXROWS MAXOSDHEIGHT
  37.218 +#define INLINE inline
  37.219 +#endif
  37.220 +
  37.221 +class cMP3Control : public cControl {
  37.222 +private:
  37.223 +#if APIVERSNUM >= 10307
  37.224 +  cOsd *osd;
  37.225 +  const cFont *font;
  37.226 +  cSkinDisplayReplay *disp;
  37.227 +#else
  37.228 +  bool statusInterfaceOpen;
  37.229 +#endif
  37.230 +  int bw, bh, bwc, fw, fh;
  37.231 +  //
  37.232 +  cMP3Player *player;
  37.233 +  bool visible, shown, bigwin, statusActive;
  37.234 +  time_t timeoutShow, greentime, oktime;
  37.235 +  int lastkeytime, number;
  37.236 +  bool selecting, selecthide;
  37.237 +  //
  37.238 +  cMP3PlayInfo *lastMode;
  37.239 +  time_t fliptime, listtime;
  37.240 +  int hashlist[MAXROWS];
  37.241 +  int flip, flipint, top, rows;
  37.242 +  int lastIndex, lastTotal, lastTop;
  37.243 +  int framesPerSecond;
  37.244 +  //
  37.245 +  bool jumpactive, jumphide, jumpsecs;
  37.246 +  int jumpmm;
  37.247 +  //
  37.248 +  void ShowTimed(int Seconds=0);
  37.249 +  void ShowProgress(bool open=false, bool bigWin=false);
  37.250 +  void ShowStatus(bool force);
  37.251 +  void HideStatus(void);
  37.252 +  void DisplayInfo(const char *s=0);
  37.253 +  void JumpDisplay(void);
  37.254 +  void JumpProcess(eKeys Key);
  37.255 +  void Jump(void);
  37.256 +  void Stop(void);
  37.257 +  INLINE void Write(int x, int y, int w, const char *text, eDvbColor fg=clrWhite, eDvbColor bg=clrBackground);
  37.258 +  INLINE void Fill(int x, int y, int w, int h, eDvbColor fg);
  37.259 +  inline void Flush(void);
  37.260 +public:
  37.261 +  cMP3Control(void);
  37.262 +  virtual ~cMP3Control();
  37.263 +  virtual eOSState ProcessKey(eKeys Key);
  37.264 +  virtual void Show(void) { ShowTimed(); }
  37.265 +  virtual void Hide(void);
  37.266 +  bool Visible(void) { return visible; }
  37.267 +  static bool SetPlayList(cPlayList *plist);
  37.268 +  };
  37.269 +
  37.270 +cMP3Control::cMP3Control(void)
  37.271 +:cControl(player=new cMP3Player)
  37.272 +{
  37.273 +  visible=shown=bigwin=selecting=selecthide=jumpactive=jumphide=statusActive=false;
  37.274 +  timeoutShow=greentime=oktime=0;
  37.275 +  lastkeytime=number=0;
  37.276 +  lastMode=0;
  37.277 +  framesPerSecond=SecondsToFrames(1);
  37.278 +#if APIVERSNUM >= 10307
  37.279 +  osd=0; disp=0;
  37.280 +  font=cFont::GetFont(fontOsd);
  37.281 +#else
  37.282 +  statusInterfaceOpen=false;
  37.283 +#endif
  37.284 +#if APIVERSNUM >= 10338
  37.285 +  cStatus::MsgReplaying(this,"MP3",0,true);
  37.286 +#else
  37.287 +  cStatus::MsgReplaying(this,"MP3");
  37.288 +#endif
  37.289 +}
  37.290 +
  37.291 +cMP3Control::~cMP3Control()
  37.292 +{
  37.293 +  delete lastMode;
  37.294 +  Hide();
  37.295 +  Stop();
  37.296 +}
  37.297 +
  37.298 +void cMP3Control::Stop(void)
  37.299 +{
  37.300 +#if APIVERSNUM >= 10338
  37.301 +  cStatus::MsgReplaying(this,0,0,false);
  37.302 +#else
  37.303 +  cStatus::MsgReplaying(this,0);
  37.304 +#endif
  37.305 +  delete player; player=0;
  37.306 +  mgr->Halt();
  37.307 +  mgr->Flush(); //XXX remove later
  37.308 +}
  37.309 +
  37.310 +bool cMP3Control::SetPlayList(cPlayList *plist)
  37.311 +{
  37.312 +  bool res;
  37.313 +  cControl *control=cControl::Control();
  37.314 +  // is there a running MP3 player?
  37.315 +  if(control && typeid(*control)==typeid(cMP3Control)) {
  37.316 +    // add songs to running playlist
  37.317 +    mgr->Add(plist);
  37.318 +    res=true;
  37.319 +    }
  37.320 +  else {
  37.321 +    mgr->Flush();
  37.322 +    mgr->Add(plist);
  37.323 +    cControl::Launch(new cMP3Control);
  37.324 +    res=false;
  37.325 +    }
  37.326 +  delete plist;
  37.327 +  return res;
  37.328 +}
  37.329 +
  37.330 +void cMP3Control::ShowTimed(int Seconds)
  37.331 +{
  37.332 +  if(!visible) {
  37.333 +    ShowProgress(true);
  37.334 +    if(Seconds>0) timeoutShow=time(0)+Seconds;
  37.335 +    }
  37.336 +}
  37.337 +
  37.338 +void cMP3Control::Hide(void)
  37.339 +{
  37.340 +  HideStatus();
  37.341 +  if(visible) {
  37.342 +#if APIVERSNUM >= 10307
  37.343 +    delete osd; osd=0;
  37.344 +    delete disp; disp=0;
  37.345 +#else
  37.346 +    Interface->Close();
  37.347 +#endif
  37.348 +    visible=bigwin=false;
  37.349 +#if APIVERSNUM >= 10500
  37.350 +    SetNeedsFastResponse(false);
  37.351 +#else
  37.352 +    needsFastResponse=false;
  37.353 +#endif
  37.354 +    }
  37.355 +}
  37.356 +
  37.357 +void cMP3Control::ShowStatus(bool force)
  37.358 +{
  37.359 +  if((asyncStatus.Changed() || (force && !statusActive)) && !jumpactive) {
  37.360 +    const char *text=asyncStatus.Begin();
  37.361 +    if(text) {
  37.362 +#if APIVERSNUM >= 10307
  37.363 +      if(MP3Setup.ReplayDisplay || !osd) {
  37.364 +        if(statusActive) Skins.Message(mtStatus,0);
  37.365 +        Skins.Message(mtStatus,text);
  37.366 +        }
  37.367 +      else {
  37.368 +        if(!statusActive) osd->SaveRegion(0,bh-2*fh,bw-1,bh-fh-1);
  37.369 +        osd->DrawText(0,bh-2*fh,text,clrBlack,clrCyan,font,bw,fh,taCenter);
  37.370 +        osd->Flush();
  37.371 +        }
  37.372 +#else
  37.373 +      if(!Interface->IsOpen()) {
  37.374 +        Interface->Open(0,-1);
  37.375 +        statusInterfaceOpen=true;
  37.376 +        }
  37.377 +      Interface->Status(text);
  37.378 +      Interface->Flush();
  37.379 +#endif
  37.380 +      statusActive=true;
  37.381 +      }
  37.382 +    else
  37.383 +      HideStatus();
  37.384 +    asyncStatus.Finish();
  37.385 +    }
  37.386 +}
  37.387 +
  37.388 +void cMP3Control::HideStatus(void)
  37.389 +{
  37.390 +  if(statusActive) {
  37.391 +#if APIVERSNUM >= 10307
  37.392 +    if(MP3Setup.ReplayDisplay || !osd)
  37.393 +      Skins.Message(mtStatus,0);
  37.394 +    else {
  37.395 +      osd->RestoreRegion();
  37.396 +      osd->Flush();
  37.397 +      }
  37.398 +#else
  37.399 +    if(statusInterfaceOpen) {
  37.400 +      Interface->Close();
  37.401 +      statusInterfaceOpen=false;
  37.402 +      }
  37.403 +    else {
  37.404 +      Interface->Status(0);
  37.405 +      Interface->Flush();
  37.406 +      }
  37.407 +#endif
  37.408 +    }
  37.409 +  statusActive=false;
  37.410 +}
  37.411 +
  37.412 +#define CTAB    11 // some tabbing values for the progress display
  37.413 +#define CTAB2   5
  37.414 +
  37.415 +void cMP3Control::Write(int x, int y, int w, const char *text, eDvbColor fg, eDvbColor bg)
  37.416 +{
  37.417 +#if APIVERSNUM >= 10307
  37.418 +  if(osd) {
  37.419 +    //d(printf("write x=%d y=%d w=%d ->",x,y,w))
  37.420 +    x*=fw; if(x<0) x+=bw;
  37.421 +    y*=fh; if(y<0) y+=bh;
  37.422 +    osd->DrawText(x,y,text,fg,bg,font,w*fw);
  37.423 +    //d(printf(" x=%d y=%d w=%d\n",x,y,w*fw))
  37.424 +    }
  37.425 +#else
  37.426 +  if(w>0) Fill(x,y,w,1,bg);
  37.427 +  Interface->Write(x,y,text,fg,bg);
  37.428 +#endif
  37.429 +}
  37.430 +
  37.431 +void cMP3Control::Fill(int x, int y, int w, int h, eDvbColor fg)
  37.432 +{
  37.433 +#if APIVERSNUM >= 10307
  37.434 +  if(osd) {
  37.435 +    //d(printf("fill x=%d y=%d w=%d h=%d ->",x,y,w,h))
  37.436 +    x*=fw; if(x<0) x+=bw;
  37.437 +    y*=fh; if(y<0) y+=bh;
  37.438 +    osd->DrawRectangle(x,y,x+w*fw-1,y+h*fh-1,fg);
  37.439 +    //d(printf(" x=%d y=%d x2=%d y2=%d\n",x,y,x+h*fh-1,y+w*fw-1))
  37.440 +    }
  37.441 +#else
  37.442 +  Interface->Fill(x,y,w,h,fg);
  37.443 +#endif
  37.444 +}
  37.445 +
  37.446 +void cMP3Control::Flush(void)
  37.447 +{
  37.448 +#if APIVERSNUM >= 10307
  37.449 +  if(MP3Setup.ReplayDisplay) Skins.Flush();
  37.450 +  else if(osd) osd->Flush();
  37.451 +#else
  37.452 +  Interface->Flush();
  37.453 +#endif
  37.454 +}
  37.455 +
  37.456 +void cMP3Control::ShowProgress(bool open, bool bigWin)
  37.457 +{
  37.458 +  int index, total;
  37.459 +
  37.460 +  if(player->GetIndex(index,total) && total>=0) {
  37.461 +    if(!visible && open) {
  37.462 +      HideStatus();
  37.463 +#if APIVERSNUM >= 10307
  37.464 +      if(MP3Setup.ReplayDisplay) {
  37.465 +        disp=Skins.Current()->DisplayReplay(false);
  37.466 +        if(!disp) return;
  37.467 +        bigWin=false;
  37.468 +        }
  37.469 +      else {
  37.470 +        int bt, bp;
  37.471 +        fw=font->Width(' ')*2;
  37.472 +        fh=font->Height();
  37.473 +        if(bigWin) {
  37.474 +          bw=Setup.OSDWidth;
  37.475 +          bh=Setup.OSDHeight;
  37.476 +          bt=0;
  37.477 +          bp=2*fh;
  37.478 +          rows=(bh-bp-fh/3)/fh;
  37.479 +          }
  37.480 +        else {
  37.481 +          bw=Setup.OSDWidth;
  37.482 +          bh=bp=2*fh;
  37.483 +          bt=Setup.OSDHeight-bh;
  37.484 +          rows=0;
  37.485 +          }
  37.486 +        bwc=bw/fw+1;
  37.487 +        //d(printf("mp3: bw=%d bh=%d bt=%d bp=%d bwc=%d rows=%d fw=%d fh=%d\n",
  37.488 +        //  bw,bh,bt,bp,bwc,rows,fw,fh))
  37.489 +        osd=cOsdProvider::NewOsd(Setup.OSDLeft,Setup.OSDTop+bt);
  37.490 +        if(!osd) return;
  37.491 +        if(bigWin) {
  37.492 +          tArea Areas[] = { { 0,0,bw-1,bh-bp-1,2 }, { 0,bh-bp,bw-1,bh-1,4 } };
  37.493 +          osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea));
  37.494 +          }
  37.495 +        else {
  37.496 +          tArea Areas[] = { { 0,0,bw-1,bh-1,4 } };
  37.497 +          osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea));
  37.498 +          }
  37.499 +        osd->DrawRectangle(0,0,bw-1,bh-1,clrGray50);
  37.500 +        osd->Flush();
  37.501 +        }
  37.502 +#else
  37.503 +      fw=cOsd::CellWidth();
  37.504 +      fh=cOsd::LineHeight();
  37.505 +      bh=bigWin ? Setup.OSDheight : -2;
  37.506 +      bw=bwc=Setup.OSDwidth;
  37.507 +      rows=Setup.OSDheight-3;
  37.508 +      Interface->Open(bw,bh);
  37.509 +      Interface->Clear();
  37.510 +#endif
  37.511 +      ShowStatus(true);
  37.512 +      bigwin=bigWin;
  37.513 +      visible=true;
  37.514 +#if APIVERSNUM >= 10500
  37.515 +      SetNeedsFastResponse(true);
  37.516 +#else
  37.517 +      needsFastResponse=true;
  37.518 +#endif
  37.519 +      fliptime=listtime=0; flipint=0; flip=-1; top=lastTop=-1; lastIndex=lastTotal=-1;
  37.520 +      delete lastMode; lastMode=0;
  37.521 +      }
  37.522 +
  37.523 +    cMP3PlayInfo *mode=new cMP3PlayInfo;
  37.524 +    bool valid=mgr->Info(-1,mode);
  37.525 +    bool changed=(!lastMode || mode->Hash!=lastMode->Hash);
  37.526 +    char buf[256];
  37.527 +    if(changed) { d(printf("mp3-ctrl: mode change detected\n")) }
  37.528 +
  37.529 +    if(valid) { // send progress to status monitor
  37.530 +      if(changed || mode->Loop!=lastMode->Loop || mode->Shuffle!=lastMode->Shuffle) {
  37.531 +        snprintf(buf,sizeof(buf),"[%c%c] (%d/%d) %s",
  37.532 +                  mode->Loop?'L':'.',mode->Shuffle?'S':'.',mode->Num,mode->MaxNum,TitleArtist(mode->Title,mode->Artist));
  37.533 +#if APIVERSNUM >= 10338
  37.534 +        cStatus::MsgReplaying(this,buf,mode->Filename[0]?mode->Filename:0,true);
  37.535 +#else
  37.536 +        cStatus::MsgReplaying(this,buf);
  37.537 +#endif
  37.538 +        }
  37.539 +      }
  37.540 +
  37.541 +    if(visible) { // refresh the OSD progress display
  37.542 +      bool flush=false;
  37.543 +
  37.544 +#if APIVERSNUM >= 10307
  37.545 +      if(MP3Setup.ReplayDisplay) {
  37.546 +        if(!statusActive) {
  37.547 +          if(total>0) disp->SetProgress(index,total);
  37.548 +          disp->SetCurrent(IndexToHMSF(index));
  37.549 +          disp->SetTotal(IndexToHMSF(total));
  37.550 +          bool Play, Forward;
  37.551 +          int Speed;
  37.552 +          if(GetReplayMode(Play,Forward,Speed)) 
  37.553 +            disp->SetMode(Play, Forward, Speed);
  37.554 +          flush=true;
  37.555 +          }
  37.556 +        }
  37.557 +      else {
  37.558 +#endif
  37.559 +        if(!selecting && changed && !statusActive) {
  37.560 +          snprintf(buf,sizeof(buf),"(%d/%d)",mode->Num,mode->MaxNum);
  37.561 +          Write(0,-2,CTAB,buf);
  37.562 +          flush=true;
  37.563 +          }
  37.564 +
  37.565 +        if(!lastMode || mode->Loop!=lastMode->Loop) {
  37.566 +          if(mode->Loop) Write(-4,-1,0,"L",clrBlack,clrYellow);
  37.567 +          else Fill(-4,-1,2,1,clrBackground);
  37.568 +          flush=true;
  37.569 +          }
  37.570 +        if(!lastMode || mode->Shuffle!=lastMode->Shuffle) {
  37.571 +          if(mode->Shuffle) Write(-2,-1,0,"S",clrWhite,clrRed);
  37.572 +          else Fill(-2,-1,2,1,clrBackground);
  37.573 +          flush=true;
  37.574 +          }
  37.575 +
  37.576 +        index/=framesPerSecond; total/=framesPerSecond;
  37.577 +        if(index!=lastIndex || total!=lastTotal) {
  37.578 +          if(total>0) {
  37.579 +#if APIVERSNUM >= 10307
  37.580 +            cProgressBar ProgressBar(bw-(CTAB+CTAB2)*fw,fh,index,total);
  37.581 +            osd->DrawBitmap(CTAB*fw,bh-fh,ProgressBar);
  37.582 +#else
  37.583 +            cProgressBar ProgressBar((bw-CTAB-CTAB2)*fw,fh,index,total);
  37.584 +            Interface->SetBitmap(CTAB*fw,(abs(bh)-1)*fh,ProgressBar);
  37.585 +#endif
  37.586 +            }
  37.587 +          snprintf(buf,sizeof(buf),total?"%02d:%02d/%02d:%02d":"%02d:%02d",index/60,index%60,total/60,total%60);
  37.588 +          Write(0,-1,11,buf);
  37.589 +          flush=true;
  37.590 +          }
  37.591 +#if APIVERSNUM >= 10307
  37.592 +        }
  37.593 +#endif
  37.594 +
  37.595 +      if(!jumpactive) {
  37.596 +        bool doflip=false;
  37.597 +        if(MP3Setup.ReplayDisplay && (!lastMode || mode->Loop!=lastMode->Loop || mode->Shuffle!=lastMode->Shuffle))
  37.598 +          doflip=true;
  37.599 +        if(!valid || changed) {
  37.600 +          fliptime=time(0); flip=0;
  37.601 +	  doflip=true;
  37.602 +	  }
  37.603 +        else if(time(0)>fliptime+flipint) {
  37.604 +	  fliptime=time(0);
  37.605 +	  flip++; if(flip>=MP3Setup.DisplayMode) flip=0;
  37.606 +          doflip=true;
  37.607 +	  }
  37.608 +        if(doflip) {
  37.609 +          buf[0]=0;
  37.610 +          switch(flip) {
  37.611 +	    default:
  37.612 +	      flip=0;
  37.613 +	      // fall through
  37.614 +	    case 0:
  37.615 +	      snprintf(buf,sizeof(buf),"%s",TitleArtist(mode->Title,mode->Artist));
  37.616 +	      flipint=6;
  37.617 +	      break;
  37.618 +	    case 1:
  37.619 +              if(mode->Album[0]) {
  37.620 +      	        snprintf(buf,sizeof(buf),mode->Year>0?"from: %s (%d)":"from: %s",mode->Album,mode->Year);
  37.621 +	        flipint=4;
  37.622 +	        }
  37.623 +              else fliptime=0;
  37.624 +              break;
  37.625 +	    case 2:
  37.626 +              if(mode->MaxBitrate>0)
  37.627 +                snprintf(buf,sizeof(buf),"%.1f kHz, %d-%d kbps, %s",mode->SampleFreq/1000.0,mode->Bitrate/1000,mode->MaxBitrate/1000,mode->SMode);
  37.628 +              else
  37.629 +                snprintf(buf,sizeof(buf),"%.1f kHz, %d kbps, %s",mode->SampleFreq/1000.0,mode->Bitrate/1000,mode->SMode);
  37.630 +	      flipint=3;
  37.631 +	      break;
  37.632 +	    }
  37.633 +          if(buf[0]) {
  37.634 +#if APIVERSNUM >= 10307
  37.635 +            if(MP3Setup.ReplayDisplay) {
  37.636 +              char buf2[256];
  37.637 +              snprintf(buf2,sizeof(buf2),"[%c%c] (%d/%d) %s",
  37.638 +                       mode->Loop?'L':'.',mode->Shuffle?'S':'.',mode->Num,mode->MaxNum,buf);
  37.639 +              disp->SetTitle(buf2);
  37.640 +              flush=true;
  37.641 +              }
  37.642 +            else {
  37.643 +#endif
  37.644 +              if(!statusActive) {
  37.645 +                DisplayInfo(buf);
  37.646 +                flush=true;
  37.647 +                }
  37.648 +              else { d(printf("mp3-ctrl: display info skip due to status active\n")) }
  37.649 +#if APIVERSNUM >= 10307
  37.650 +              }
  37.651 +#endif
  37.652 +            }
  37.653 +          }
  37.654 +        }
  37.655 +
  37.656 +      if(bigwin) {
  37.657 +        bool all=(top!=lastTop || changed);
  37.658 +        if(all || time(0)>listtime+2) {
  37.659 +          int num=(top>0 && mode->Num==lastMode->Num) ? top : mode->Num - rows/2;
  37.660 +          if(num+rows>mode->MaxNum) num=mode->MaxNum-rows+1;
  37.661 +          if(num<1) num=1;
  37.662 +          top=num;
  37.663 +          for(int i=0 ; i<rows && i<MAXROWS && num<=mode->MaxNum ; i++,num++) {
  37.664 +            cMP3PlayInfo pi;
  37.665 +            mgr->Info(num,&pi); if(!pi.Title[0]) break;
  37.666 +            snprintf(buf,sizeof(buf),"%d.\t%s",num,TitleArtist(pi.Title,pi.Artist));
  37.667 +            eDvbColor fg=clrWhite, bg=clrBackground;
  37.668 +            int hash=MakeHash(buf);
  37.669 +            if(num==mode->Num) { fg=clrBlack; bg=clrCyan; hash=(hash^77) + 23; }
  37.670 +            if(all || hash!=hashlist[i]) {
  37.671 +              char *s=rindex(buf,'\t');
  37.672 +              if(s) {
  37.673 +                *s++=0;
  37.674 +                Write(0,i,5,buf,fg,bg);
  37.675 +                Write(5,i,bwc-5,s,fg,bg);
  37.676 +                }
  37.677 +              else
  37.678 +                Write(0,i,bwc,buf,fg,bg);
  37.679 +              flush=true;
  37.680 +              hashlist[i]=hash;
  37.681 +              }
  37.682 +            }
  37.683 +          listtime=time(0); lastTop=top;
  37.684 +          }
  37.685 +        }
  37.686 +
  37.687 +      if(flush) Flush();
  37.688 +      }
  37.689 +
  37.690 +    lastIndex=index; lastTotal=total;
  37.691 +    delete lastMode; lastMode=mode;
  37.692 +    }
  37.693 +}
  37.694 +
  37.695 +void cMP3Control::DisplayInfo(const char *s)
  37.696 +{
  37.697 +  if(s) Write(CTAB,-2,bwc-CTAB,s);
  37.698 +  else Fill(CTAB,-2,bwc-CTAB,1,clrBackground);
  37.699 +}
  37.700 +
  37.701 +void cMP3Control::JumpDisplay(void)
  37.702 +{
  37.703 +  char buf[64];
  37.704 +  const char *j=tr("Jump: "), u=jumpsecs?'s':'m';
  37.705 +  if(!jumpmm) sprintf(buf,"%s- %c",  j,u);
  37.706 +  else        sprintf(buf,"%s%d- %c",j,jumpmm,u);
  37.707 +#if APIVERSNUM >= 10307
  37.708 +  if(MP3Setup.ReplayDisplay) {
  37.709 +    disp->SetJump(buf);
  37.710 +    }
  37.711 +  else {
  37.712 +#endif
  37.713 +    DisplayInfo(buf);
  37.714 +#if APIVERSNUM >= 10307
  37.715 +    }
  37.716 +#endif
  37.717 +}
  37.718 +
  37.719 +void cMP3Control::JumpProcess(eKeys Key)
  37.720 +{
  37.721 + int n=Key-k0, d=jumpsecs?1:60;
  37.722 +  switch (Key) {
  37.723 +    case k0 ... k9:
  37.724 +      if(jumpmm*10+n <= lastTotal/d) jumpmm=jumpmm*10+n;
  37.725 +      JumpDisplay();
  37.726 +      break;
  37.727 +    case kBlue:
  37.728 +      jumpsecs=!jumpsecs;
  37.729 +      JumpDisplay();
  37.730 +      break;
  37.731 +    case kPlay:
  37.732 +    case kUp:
  37.733 +      jumpmm-=lastIndex/d;
  37.734 +      // fall through
  37.735 +    case kFastRew:
  37.736 +    case kFastFwd:
  37.737 +    case kLeft:
  37.738 +    case kRight:
  37.739 +      player->SkipSeconds(jumpmm*d * ((Key==kLeft || Key==kFastRew) ? -1:1));
  37.740 +      // fall through
  37.741 +    default:
  37.742 +      jumpactive=false;
  37.743 +      break;
  37.744 +    }
  37.745 +
  37.746 +  if(!jumpactive) {
  37.747 +    if(jumphide) Hide();
  37.748 +#if APIVERSNUM >= 10307
  37.749 +    else if(MP3Setup.ReplayDisplay) disp->SetJump(0);
  37.750 +#endif
  37.751 +    }
  37.752 +}
  37.753 +
  37.754 +void cMP3Control::Jump(void)
  37.755 +{
  37.756 +  jumpmm=0; jumphide=jumpsecs=false;
  37.757 +  if(!visible) {
  37.758 +    ShowTimed(); if(!visible) return;
  37.759 +    jumphide=true;
  37.760 +    }
  37.761 +  JumpDisplay();
  37.762 +  jumpactive=true; fliptime=0; flip=-1;
  37.763 +}
  37.764 +
  37.765 +eOSState cMP3Control::ProcessKey(eKeys Key)
  37.766 +{
  37.767 +  if(!player->Active()) return osEnd;
  37.768 +
  37.769 +  if(visible && timeoutShow && time(0)>timeoutShow) { Hide(); timeoutShow=0; }
  37.770 +  ShowProgress();
  37.771 +#if APIVERSNUM >= 10307
  37.772 +  ShowStatus(Key==kNone && !Skins.IsOpen());
  37.773 +#else
  37.774 +  ShowStatus(Key==kNone && !Interface->IsOpen());
  37.775 +#endif
  37.776 +
  37.777 +  if(jumpactive && Key!=kNone) { JumpProcess(Key); return osContinue; }
  37.778 +
  37.779 +  switch(Key) {
  37.780 +    case kUp:
  37.781 +    case kUp|k_Repeat:
  37.782 +#if APIVERSNUM >= 10347
  37.783 +    case kNext:
  37.784 +    case kNext|k_Repeat:    
  37.785 +#endif
  37.786 +      mgr->Next(); player->Play();
  37.787 +      break;
  37.788 +    case kDown:
  37.789 +    case kDown|k_Repeat:
  37.790 +#if APIVERSNUM >= 10347
  37.791 +    case kPrev:
  37.792 +    case kPrev|k_Repeat:
  37.793 +#endif
  37.794 +      if(!player->PrevCheck()) mgr->Prev();
  37.795 +      player->Play();
  37.796 +      break;
  37.797 +    case kLeft:
  37.798 +    case kLeft|k_Repeat:
  37.799 +      if(bigwin) {
  37.800 +        if(top>0) { top-=rows; if(top<1) top=1; }
  37.801 +        break;
  37.802 +        }
  37.803 +      // fall through
  37.804 +    case kFastRew:
  37.805 +    case kFastRew|k_Repeat:
  37.806 +      if(!player->IsStream()) player->SkipSeconds(-JUMPSIZE);
  37.807 +      break;
  37.808 +    case kRight:
  37.809 +    case kRight|k_Repeat:
  37.810 +      if(bigwin) {
  37.811 +        if(top>0) top+=rows;
  37.812 +        break;
  37.813 +        }
  37.814 +      // fall through
  37.815 +    case kFastFwd:
  37.816 +    case kFastFwd|k_Repeat:
  37.817 +      if(!player->IsStream()) player->SkipSeconds(JUMPSIZE);
  37.818 +      break;
  37.819 +    case kRed:
  37.820 +      if(!player->IsStream()) Jump();
  37.821 +      break;
  37.822 +    case kGreen:
  37.823 +      if(lastMode) {
  37.824 +        if(time(0)>greentime) {
  37.825 +          if(lastMode->Loop || (!lastMode->Loop && !lastMode->Shuffle)) mgr->ToggleLoop();
  37.826 +          if(lastMode->Shuffle) mgr->ToggleShuffle();
  37.827 +          }
  37.828 +        else {
  37.829 +          if(!lastMode->Loop) mgr->ToggleLoop();
  37.830 +          else if(!lastMode->Shuffle) mgr->ToggleShuffle();
  37.831 +          else mgr->ToggleLoop();
  37.832 +          }
  37.833 +        greentime=time(0)+MULTI_TIMEOUT;
  37.834 +        }
  37.835 +      break;
  37.836 +    case kPlay:
  37.837 +      player->Play();
  37.838 +      break;
  37.839 +    case kPause:
  37.840 +    case kYellow:
  37.841 +      if(!player->IsStream()) player->Pause();
  37.842 +      break;
  37.843 +    case kStop:
  37.844 +    case kBlue:
  37.845 +      Hide();
  37.846 +      Stop();
  37.847 +      return osEnd;
  37.848 +    case kBack:
  37.849 +      Hide();
  37.850 +#if APIVERSNUM >= 10332
  37.851 +      cRemote::CallPlugin(i18n_name);
  37.852 +      return osBack;
  37.853 +#else
  37.854 +      return osEnd;
  37.855 +#endif
  37.856 +
  37.857 +    case k0 ... k9:
  37.858 +      number=number*10+Key-k0;
  37.859 +      if(lastMode && number>0 && number<=lastMode->MaxNum) {
  37.860 +        if(!visible) { ShowTimed(); selecthide=true; }
  37.861 +        selecting=true; lastkeytime=time_ms();
  37.862 +        char buf[32];
  37.863 +#if APIVERSNUM >= 10307
  37.864 +        if(MP3Setup.ReplayDisplay) {
  37.865 +          snprintf(buf,sizeof(buf),"%s%d-/%d",tr("Jump: "),number,lastMode->MaxNum);
  37.866 +          disp->SetJump(buf);
  37.867 +          }
  37.868 +        else {
  37.869 +#endif
  37.870 +          snprintf(buf,sizeof(buf),"(%d-/%d)",number,lastMode->MaxNum);
  37.871 +          Write(0,-2,CTAB,buf);
  37.872 +#if APIVERSNUM >= 10307
  37.873 +          }
  37.874 +#endif
  37.875 +        Flush();
  37.876 +        break;
  37.877 +        }
  37.878 +      number=0; lastkeytime=0;
  37.879 +      // fall through
  37.880 +    case kNone:
  37.881 +      if(selecting && time_ms()-lastkeytime>SELECT_TIMEOUT) {
  37.882 +        if(number>0) { mgr->Goto(number); player->Play();  }
  37.883 +        if(selecthide) timeoutShow=time(0)+SELECTHIDE_TIMEOUT;
  37.884 +        if(lastMode) lastMode->Hash=-1;
  37.885 +        number=0; selecting=selecthide=false;
  37.886 +#if APIVERSNUM >= 10307
  37.887 +        if(MP3Setup.ReplayDisplay) disp->SetJump(0);
  37.888 +#endif
  37.889 +        }
  37.890 +      break;
  37.891 +    case kOk:
  37.892 +      if(time(0)>oktime || MP3Setup.ReplayDisplay) {
  37.893 +        visible ? Hide() : ShowTimed();
  37.894 +        }
  37.895 +      else {
  37.896 +        if(visible && !bigwin) { Hide(); ShowProgress(true,true); }
  37.897 +        else { Hide(); ShowTimed(); }
  37.898 +        }
  37.899 +      oktime=time(0)+MULTI_TIMEOUT;
  37.900 +      ShowStatus(true);
  37.901 +      break;
  37.902 +    default:
  37.903 +      return osUnknown;
  37.904 +    }
  37.905 +  return osContinue;
  37.906 +}
  37.907 +
  37.908 +// --- cMenuID3Info ------------------------------------------------------------
  37.909 +
  37.910 +class cMenuID3Info : public cOsdMenu {
  37.911 +private:
  37.912 +  cOsdItem *Item(const char *name, const char *text);
  37.913 +  cOsdItem *Item(const char *name, const char *format, const float num);
  37.914 +  void Build(cSongInfo *info, const char *name);
  37.915 +public:
  37.916 +  cMenuID3Info(cSong *song);
  37.917 +  cMenuID3Info(cSongInfo *si, const char *name);
  37.918 +  virtual eOSState ProcessKey(eKeys Key);
  37.919 +  };
  37.920 +
  37.921 +cMenuID3Info::cMenuID3Info(cSong *song)
  37.922 +:cOsdMenu(tr("ID3 information"),12)
  37.923 +{
  37.924 +  Build(song->Info(),song->Name());
  37.925 +}
  37.926 +
  37.927 +cMenuID3Info::cMenuID3Info(cSongInfo *si, const char *name)
  37.928 +:cOsdMenu(tr("ID3 information"),12)
  37.929 +{
  37.930 +  Build(si,name);
  37.931 +}
  37.932 +
  37.933 +void cMenuID3Info::Build(cSongInfo *si, const char *name)
  37.934 +{
  37.935 +  if(si) {
  37.936 +    Item(tr("Filename"),name);
  37.937 +    if(si->HasInfo() && si->Total>0) {
  37.938 +      char *buf=0;
  37.939 +      asprintf(&buf,"%02d:%02d",si->Total/60,si->Total%60);
  37.940 +      Item(tr("Length"),buf);
  37.941 +      free(buf);
  37.942 +      Item(tr("Title"),si->Title);
  37.943 +      Item(tr("Artist"),si->Artist);
  37.944 +      Item(tr("Album"),si->Album);
  37.945 +      Item(tr("Year"),0,(float)si->Year);
  37.946 +      Item(tr("Samplerate"),"%.1f kHz",si->SampleFreq/1000.0);
  37.947 +      Item(tr("Bitrate"),"%.f kbit/s",si->Bitrate/1000.0);
  37.948 +      Item(tr("Channels"),0,(float)si->Channels);
  37.949 +      }
  37.950 +    Display();
  37.951 +    }
  37.952 +}
  37.953 +
  37.954 +cOsdItem *cMenuID3Info::Item(const char *name, const char *format, const float num)
  37.955 +{
  37.956 +  cOsdItem *item;
  37.957 +  if(num>=0.0) {
  37.958 +    char *buf=0;
  37.959 +    asprintf(&buf,format?format:"%.f",num);
  37.960 +    item=Item(name,buf);
  37.961 +    free(buf);
  37.962 +    }
  37.963 +  else item=Item(name,"");
  37.964 +  return item;
  37.965 +}
  37.966 +
  37.967 +cOsdItem *cMenuID3Info::Item(const char *name, const char *text)
  37.968 +{
  37.969 +  char *buf=0;
  37.970 +  asprintf(&buf,"%s:\t%s",name,text?text:"");
  37.971 +  cOsdItem *item = new cOsdItem(buf,osBack);
  37.972 +#if APIVERSNUM >= 10307
  37.973 +  item->SetSelectable(false);
  37.974 +#else
  37.975 +  item->SetColor(clrWhite, clrBackground);
  37.976 +#endif
  37.977 +  free(buf);
  37.978 +  Add(item); return item;
  37.979 +}
  37.980 +
  37.981 +eOSState cMenuID3Info::ProcessKey(eKeys Key)
  37.982 +{
  37.983 +  eOSState state = cOsdMenu::ProcessKey(Key);
  37.984 +
  37.985 +  if(state==osUnknown) {
  37.986 +     switch(Key) {
  37.987 +       case kRed:
  37.988 +       case kGreen:
  37.989 +       case kYellow:
  37.990 +       case kBlue:   return osContinue;
  37.991 +       case kMenu:   return osEnd;
  37.992 +       default: break;
  37.993 +       }
  37.994 +     }
  37.995 +  return state;
  37.996 +}
  37.997 +
  37.998 +// --- cMenuInstantBrowse -------------------------------------------------------
  37.999 +
 37.1000 +class cMenuInstantBrowse : public cMenuBrowse {
 37.1001 +private:
 37.1002 +  const char *selecttext, *alltext;
 37.1003 +  virtual void SetButtons(void);
 37.1004 +  virtual eOSState ID3Info(void);
 37.1005 +public:
 37.1006 +  cMenuInstantBrowse(cFileSource *Source, const char *Selecttext, const char *Alltext);
 37.1007 +  virtual eOSState ProcessKey(eKeys Key);
 37.1008 +  };
 37.1009 +
 37.1010 +cMenuInstantBrowse::cMenuInstantBrowse(cFileSource *Source, const char *Selecttext, const char *Alltext)
 37.1011 +:cMenuBrowse(Source,true,true,tr("Directory browser"),excl_br)
 37.1012 +{
 37.1013 +  selecttext=Selecttext; alltext=Alltext;
 37.1014 +  SetButtons();
 37.1015 +}
 37.1016 +
 37.1017 +void cMenuInstantBrowse::SetButtons(void)
 37.1018 +{
 37.1019 +  SetHelp(selecttext, currentdir?tr("Parent"):0, currentdir?0:alltext, tr("ID3 info"));
 37.1020 +  Display();
 37.1021 +}
 37.1022 +
 37.1023 +eOSState cMenuInstantBrowse::ID3Info(void)
 37.1024 +{
 37.1025 +  cFileObj *item=CurrentItem();
 37.1026 +  if(item && item->Type()==otFile) {
 37.1027 +    cSong *song=new cSong(item);
 37.1028 +    cSongInfo *si;
 37.1029 +    if(song && (si=song->Info())) {
 37.1030 +      AddSubMenu(new cMenuID3Info(si,item->Path()));
 37.1031 +      }
 37.1032 +    delete song;
 37.1033 +    }
 37.1034 +  return osContinue;
 37.1035 +}
 37.1036 +
 37.1037 +eOSState cMenuInstantBrowse::ProcessKey(eKeys Key)
 37.1038 +{
 37.1039 +  eOSState state=cOsdMenu::ProcessKey(Key);
 37.1040 +  if(state==osUnknown) {
 37.1041 +     switch (Key) {
 37.1042 +       case kYellow: lastselect=new cFileObj(source,0,0,otBase);
 37.1043 +                     return osBack;
 37.1044 +       default: break;
 37.1045 +       }
 37.1046 +     }
 37.1047 +  if(state==osUnknown) state=cMenuBrowse::ProcessStdKey(Key,state);
 37.1048 +  return state;
 37.1049 +}
 37.1050 +
 37.1051 +// --- cMenuPlayListItem -------------------------------------------------------
 37.1052 +
 37.1053 +class cMenuPlayListItem : public cOsdItem {
 37.1054 +  private:
 37.1055 +  bool showID3;
 37.1056 +  cSong *song;
 37.1057 +public:
 37.1058 +  cMenuPlayListItem(cSong *Song, bool showid3);
 37.1059 +  cSong *Song(void) { return song; }
 37.1060 +  virtual void Set(void);
 37.1061 +  void Set(bool showid3);
 37.1062 +  };
 37.1063 +
 37.1064 +cMenuPlayListItem::cMenuPlayListItem(cSong *Song, bool showid3)
 37.1065 +{
 37.1066 +  song=Song;
 37.1067 +  Set(showid3);
 37.1068 +}
 37.1069 +
 37.1070 +void cMenuPlayListItem::Set(bool showid3)
 37.1071 +{
 37.1072 +  showID3=showid3;
 37.1073 +  Set();
 37.1074 +}
 37.1075 +
 37.1076 +void cMenuPlayListItem::Set(void)
 37.1077 +{
 37.1078 +  char *buffer=0;
 37.1079 +  cSongInfo *si=song->Info(false);
 37.1080 +  if(showID3 && !si) si=song->Info();
 37.1081 +  if(showID3 && si && si->Title)
 37.1082 +    asprintf(&buffer, "%d.\t%s",song->Index()+1,TitleArtist(si->Title,si->Artist));
 37.1083 +  else
 37.1084 +    asprintf(&buffer, "%d.\t<%s>",song->Index()+1,song->Name());
 37.1085 +  SetText(buffer,false);
 37.1086 +}
 37.1087 +
 37.1088 +// --- cMenuPlayList ------------------------------------------------------
 37.1089 +
 37.1090 +class cMenuPlayList : public cOsdMenu {
 37.1091 +private:
 37.1092 +  cPlayList *playlist;
 37.1093 +  bool browsing, showid3;
 37.1094 +  void Buttons(void);
 37.1095 +  void Refresh(bool all = false);
 37.1096 +  void Add(void);
 37.1097 +  virtual void Move(int From, int To);
 37.1098 +  eOSState Remove(void);
 37.1099 +  eOSState ShowID3(void);
 37.1100 +  eOSState ID3Info(void);
 37.1101 +public:
 37.1102 +  cMenuPlayList(cPlayList *Playlist);
 37.1103 +  virtual eOSState ProcessKey(eKeys Key);
 37.1104 +  };
 37.1105 +
 37.1106 +cMenuPlayList::cMenuPlayList(cPlayList *Playlist)
 37.1107 +:cOsdMenu(tr("Playlist editor"),4)
 37.1108 +{
 37.1109 +  browsing=showid3=false;
 37.1110 +  playlist=Playlist;
 37.1111 +  if(MP3Setup.EditorMode) showid3=true;
 37.1112 +
 37.1113 +  cSong *mp3 = playlist->First();
 37.1114 +  while(mp3) {
 37.1115 +    cOsdMenu::Add(new cMenuPlayListItem(mp3,showid3));
 37.1116 +    mp3 = playlist->cList<cSong>::Next(mp3);
 37.1117 +    }
 37.1118 +  Buttons(); Display();
 37.1119 +}
 37.1120 +
 37.1121 +void cMenuPlayList::Buttons(void)
 37.1122 +{
 37.1123 +  SetHelp(tr("Add"), showid3?tr("Filenames"):tr("ID3 names"), tr("Remove"), tr(BUTTON"Mark"));
 37.1124 +}
 37.1125 +
 37.1126 +void cMenuPlayList::Refresh(bool all)
 37.1127 +{
 37.1128 +  cMenuPlayListItem *cur=(cMenuPlayListItem *)((all || Count()<2) ? First() : Get(Current()));
 37.1129 +  while(cur) {
 37.1130 +    cur->Set(showid3);
 37.1131 +    cur=(cMenuPlayListItem *)Next(cur);
 37.1132 +    }
 37.1133 +}
 37.1134 +
 37.1135 +void cMenuPlayList::Add(void)
 37.1136 +{
 37.1137 +  cFileObj *item=cMenuInstantBrowse::GetSelected();
 37.1138 +  if(item) {
 37.1139 +    Status(tr("Scanning directory..."));
 37.1140 +    cInstantPlayList *newpl=new cInstantPlayList(item);
 37.1141 +    if(newpl->Load()) {
 37.1142 +      if(newpl->Count()) {
 37.1143 +        if(newpl->Count()==1 || Interface->Confirm(tr("Add recursivly?"))) {
 37.1144 +          cSong *mp3=newpl->First();
 37.1145 +          while(mp3) {
 37.1146 +            cSong *n=new cSong(mp3);
 37.1147 +            if(Count()>0) {
 37.1148 +              cMenuPlayListItem *current=(cMenuPlayListItem *)Get(Current());
 37.1149 +              playlist->Add(n,current->Song());
 37.1150 +              cOsdMenu::Add(new cMenuPlayListItem(n,showid3),true,current);
 37.1151 +              }
 37.1152 +            else {
 37.1153 +              playlist->Add(n);
 37.1154 +              cOsdMenu::Add(new cMenuPlayListItem(n,showid3),true);
 37.1155 +              }
 37.1156 +            mp3=newpl->cList<cSong>::Next(mp3);
 37.1157 +            }
 37.1158 +          playlist->Save();
 37.1159 +          Refresh(); Display();
 37.1160 +          }
 37.1161 +        }
 37.1162 +      else Error(tr("Empty directory!"));
 37.1163 +      }
 37.1164 +    else Error(tr("Error scanning directory!"));
 37.1165 +    delete newpl;
 37.1166 +    Status(0);
 37.1167 +    }
 37.1168 +}
 37.1169 +
 37.1170 +void cMenuPlayList::Move(int From, int To)
 37.1171 +{
 37.1172 +  playlist->Move(From,To); playlist->Save();
 37.1173 +  cOsdMenu::Move(From,To);
 37.1174 +  Refresh(true); Display();
 37.1175 +}
 37.1176 +
 37.1177 +eOSState cMenuPlayList::ShowID3(void)
 37.1178 +{
 37.1179 +  showid3=!showid3;
 37.1180 +  Buttons(); Refresh(true); Display();
 37.1181 +  return osContinue;
 37.1182 +}
 37.1183 +
 37.1184 +eOSState cMenuPlayList::ID3Info(void)
 37.1185 +{
 37.1186 +  if(Count()>0) {
 37.1187 +    cMenuPlayListItem *current = (cMenuPlayListItem *)Get(Current());
 37.1188 +    AddSubMenu(new cMenuID3Info(current->Song()));
 37.1189 +    }
 37.1190 +  return osContinue;
 37.1191 +}
 37.1192 +
 37.1193 +eOSState cMenuPlayList::Remove(void)
 37.1194 +{
 37.1195 +  if(Count()>0) {
 37.1196 +    cMenuPlayListItem *current = (cMenuPlayListItem *)Get(Current());
 37.1197 +    if(Interface->Confirm(tr("Remove entry?"))) {
 37.1198 +      playlist->Del(current->Song()); playlist->Save();
 37.1199 +      cOsdMenu::Del(Current());
 37.1200 +      Refresh(); Display();
 37.1201 +      }
 37.1202 +    }
 37.1203 +  return osContinue;
 37.1204 +}
 37.1205 +
 37.1206 +eOSState cMenuPlayList::ProcessKey(eKeys Key)
 37.1207 +{
 37.1208 +  eOSState state = cOsdMenu::ProcessKey(Key);
 37.1209 +
 37.1210 +  if(browsing && !HasSubMenu() && state==osContinue) { Add(); browsing=false; }
 37.1211 +
 37.1212 +  if(state==osUnknown) {
 37.1213 +     switch(Key) {
 37.1214 +       case kOk:     return ID3Info();
 37.1215 +       case kRed:    browsing=true;
 37.1216 +                     return AddSubMenu(new cMenuInstantBrowse(MP3Sources.GetSource(),tr("Add"),tr("Add all")));
 37.1217 +       case kGreen:  return ShowID3();
 37.1218 +       case kYellow: return Remove();
 37.1219 +       case kBlue:   Mark(); return osContinue;
 37.1220 +       case kMenu:   return osEnd;
 37.1221 +       default: break;
 37.1222 +       }
 37.1223 +     }
 37.1224 +  return state;
 37.1225 +}
 37.1226 +
 37.1227 +// --- cPlaylistRename --------------------------------------------------------
 37.1228 +
 37.1229 +class cPlaylistRename : public cOsdMenu {
 37.1230 +private:
 37.1231 +  static char *newname;
 37.1232 +  const char *oldname;
 37.1233 +  char data[64];
 37.1234 +public:
 37.1235 +  cPlaylistRename(const char *Oldname);
 37.1236 +  virtual eOSState ProcessKey(eKeys Key);
 37.1237 +  static const char *GetNewname(void) { return newname; }
 37.1238 +  };
 37.1239 +
 37.1240 +char *cPlaylistRename::newname = NULL;
 37.1241 +
 37.1242 +cPlaylistRename::cPlaylistRename(const char *Oldname)
 37.1243 +:cOsdMenu(tr("Rename playlist"), 15)
 37.1244 +{
 37.1245 +  free(newname); newname=0;
 37.1246 +
 37.1247 +  oldname=Oldname;
 37.1248 +  char *buf=NULL;
 37.1249 +  asprintf(&buf,"%s\t%s",tr("Old name:"),oldname);
 37.1250 +  cOsdItem *old = new cOsdItem(buf,osContinue);
 37.1251 +#if APIVERSNUM >= 10307
 37.1252 +  old->SetSelectable(false);
 37.1253 +#else
 37.1254 +  old->SetColor(clrWhite, clrBackground);
 37.1255 +#endif
 37.1256 +  Add(old);
 37.1257 +  free(buf);
 37.1258 +
 37.1259 +  data[0]=0;
 37.1260 +  Add(new cMenuEditStrItem( tr("New name"), data, sizeof(data)-1, tr(FileNameChars)),true);
 37.1261 +}
 37.1262 +
 37.1263 +eOSState cPlaylistRename::ProcessKey(eKeys Key)
 37.1264 +{
 37.1265 +  eOSState state = cOsdMenu::ProcessKey(Key);
 37.1266 +
 37.1267 +  if (state == osUnknown) {
 37.1268 +     switch (Key) {
 37.1269 +       case kOk:     if(data[0] && strcmp(data,oldname)) newname=strdup(data);
 37.1270 +                     return osBack;
 37.1271 +       case kRed:
 37.1272 +       case kGreen:
 37.1273 +       case kYellow:
 37.1274 +       case kBlue:   return osContinue;
 37.1275 +       default: break;
 37.1276 +       }
 37.1277 +     }
 37.1278 +  return state;
 37.1279 +}
 37.1280 +
 37.1281 +// --- cMenuMP3Item -----------------------------------------------------
 37.1282 +
 37.1283 +class cMenuMP3Item : public cOsdItem {
 37.1284 +  private:
 37.1285 +  cPlayList *playlist;
 37.1286 +  virtual void Set(void);
 37.1287 +public:
 37.1288 +  cMenuMP3Item(cPlayList *PlayList);
 37.1289 +  cPlayList *List(void) { return playlist; }
 37.1290 +  };
 37.1291 +
 37.1292 +cMenuMP3Item::cMenuMP3Item(cPlayList *PlayList)
 37.1293 +{
 37.1294 +  playlist=PlayList;
 37.1295 +  Set();
 37.1296 +}
 37.1297 +
 37.1298 +void cMenuMP3Item::Set(void)
 37.1299 +{
 37.1300 +  char *buffer=0;
 37.1301 +  asprintf(&buffer," %s",playlist->BaseName());
 37.1302 +  SetText(buffer,false);
 37.1303 +}
 37.1304 +
 37.1305 +// --- cMenuMP3 --------------------------------------------------------
 37.1306 +
 37.1307 +class cMenuMP3 : public cOsdMenu {
 37.1308 +private:
 37.1309 +  cPlayLists *lists;
 37.1310 +  bool renaming, sourcing, instanting;
 37.1311 +  int buttonnum;
 37.1312 +  eOSState Play(void);
 37.1313 +  eOSState Edit(void);
 37.1314 +  eOSState New(void);
 37.1315 +  eOSState Delete(void);
 37.1316 +  eOSState Rename(bool second);
 37.1317 +  eOSState Source(bool second);
 37.1318 +  eOSState Instant(bool second);
 37.1319 +  void ScanLists(void);
 37.1320 +  eOSState SetButtons(int num);
 37.1321 +public:
 37.1322 +  cMenuMP3(void);
 37.1323 +  ~cMenuMP3(void);
 37.1324 +  virtual eOSState ProcessKey(eKeys Key);
 37.1325 +  };
 37.1326 +
 37.1327 +cMenuMP3::cMenuMP3(void)
 37.1328 +:cOsdMenu(tr("MP3"))
 37.1329 +{
 37.1330 +  renaming=sourcing=instanting=false;
 37.1331 +  lists=new cPlayLists;
 37.1332 +  ScanLists(); SetButtons(1);
 37.1333 +  if(MP3Setup.MenuMode) Instant(false);
 37.1334 +}
 37.1335 +
 37.1336 +cMenuMP3::~cMenuMP3(void)
 37.1337 +{
 37.1338 +  delete lists;
 37.1339 +}
 37.1340 +
 37.1341 +eOSState cMenuMP3::SetButtons(int num)
 37.1342 +{
 37.1343 +  switch(num) {
 37.1344 +    case 1:
 37.1345 +      SetHelp(tr(BUTTON"Edit"), tr("Source"), tr("Browse"), ">>");
 37.1346 +      break;
 37.1347 +    case 2:
 37.1348 +      SetHelp("<<", tr(BUTTON"New"), tr(BUTTON"Delete"), tr("Rename"));
 37.1349 +      break;
 37.1350 +    }
 37.1351 +  buttonnum=num; Display();
 37.1352 +  return osContinue;
 37.1353 +}
 37.1354 +
 37.1355 +void cMenuMP3::ScanLists(void)
 37.1356 +{
 37.1357 +  Clear();
 37.1358 +  Status(tr("Scanning playlists..."));
 37.1359 +  bool res=lists->Load(MP3Sources.GetSource());
 37.1360 +  Status(0);
 37.1361 +  if(res) {
 37.1362 +    cPlayList *plist=lists->First();
 37.1363 +    while(plist) {
 37.1364 +      Add(new cMenuMP3Item(plist));
 37.1365 +      plist=lists->Next(plist);
 37.1366 +      }
 37.1367 +    }
 37.1368 +  else Error(tr("Error scanning playlists!"));
 37.1369 +}
 37.1370 +
 37.1371 +eOSState cMenuMP3::Delete(void)
 37.1372 +{
 37.1373 +  if(Count()>0) {
 37.1374 +    if(Interface->Confirm(tr("Delete playlist?")) &&
 37.1375 +       Interface->Confirm(tr("Are you sure?")) ) {
 37.1376 +      cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
 37.1377 +      if(plist->Delete()) {
 37.1378 +        lists->Del(plist);
 37.1379 +        cOsdMenu::Del(Current());
 37.1380 +        Display();
 37.1381 +        }
 37.1382 +      else Error(tr("Error deleting playlist!"));
 37.1383 +      }
 37.1384 +    }
 37.1385 +  return osContinue;
 37.1386 +}
 37.1387 +
 37.1388 +eOSState cMenuMP3::New(void)
 37.1389 +{
 37.1390 +  cPlayList *plist=new cPlayList(MP3Sources.GetSource(),0,0);
 37.1391 +  char name[32];
 37.1392 +  int i=0;
 37.1393 +  do {
 37.1394 +    if(i) sprintf(name,"%s%d",tr("unnamed"),i++);
 37.1395 +    else { strcpy(name,tr("unnamed")); i++; }
 37.1396 +    } while(plist->TestName(name));
 37.1397 +
 37.1398 +  if(plist->Create(name)) {
 37.1399 +    lists->Add(plist);
 37.1400 +    Add(new cMenuMP3Item(plist), true);
 37.1401 +
 37.1402 +    isyslog("MP3: playlist %s added", plist->Name());
 37.1403 +    return AddSubMenu(new cMenuPlayList(plist));
 37.1404 +    }
 37.1405 +  Error(tr("Error creating playlist!"));
 37.1406 +  delete plist;
 37.1407 +  return osContinue;
 37.1408 +}
 37.1409 +
 37.1410 +eOSState cMenuMP3::Rename(bool second)
 37.1411 +{
 37.1412 +  if(HasSubMenu() || Count() == 0) return osContinue;
 37.1413 +
 37.1414 +  cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
 37.1415 +  if(!second) {
 37.1416 +    renaming=true;
 37.1417 +    return AddSubMenu(new cPlaylistRename(plist->BaseName()));
 37.1418 +    }
 37.1419 +  renaming=false;
 37.1420 +  const char *newname=cPlaylistRename::GetNewname();
 37.1421 +  if(newname) {
 37.1422 +    if(plist->Rename(newname)) {
 37.1423 +      RefreshCurrent();
 37.1424 +      DisplayCurrent(true);
 37.1425 +      }
 37.1426 +    else Error(tr("Error renaming playlist!"));
 37.1427 +    }
 37.1428 +  return osContinue;
 37.1429 +}
 37.1430 +
 37.1431 +eOSState cMenuMP3::Edit(void)
 37.1432 +{
 37.1433 +  if(HasSubMenu() || Count() == 0) return osContinue;
 37.1434 +
 37.1435 +  cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
 37.1436 +  if(!plist->Load()) Error(tr("Error loading playlist!"));
 37.1437 +  else if(!plist->IsWinAmp()) {
 37.1438 +    isyslog("MP3: editing playlist %s", plist->Name());
 37.1439 +    return AddSubMenu(new cMenuPlayList(plist));
 37.1440 +    }
 37.1441 +  else Error(tr("Can't edit a WinAmp playlist!"));
 37.1442 +  return osContinue;
 37.1443 +}
 37.1444 +
 37.1445 +eOSState cMenuMP3::Play(void)
 37.1446 +{
 37.1447 +  if(HasSubMenu() || Count() == 0) return osContinue;
 37.1448 +
 37.1449 +  Status(tr("Loading playlist..."));
 37.1450 +  cPlayList *newpl=new cPlayList(((cMenuMP3Item *)Get(Current()))->List());
 37.1451 +  if(newpl->Load() && newpl->Count()) {
 37.1452 +    isyslog("mp3: playback started with playlist %s", newpl->Name());
 37.1453 +    cMP3Control::SetPlayList(newpl);
 37.1454 +    if(MP3Setup.KeepSelect) { Status(0); return osContinue; }
 37.1455 +    return osEnd;
 37.1456 +    }
 37.1457 +  Status(0);
 37.1458 +  delete newpl;
 37.1459 +  Error(tr("Error loading playlist!"));
 37.1460 +  return osContinue;
 37.1461 +}
 37.1462 +
 37.1463 +eOSState cMenuMP3::Source(bool second)
 37.1464 +{
 37.1465 +  if(HasSubMenu()) return osContinue;
 37.1466 +
 37.1467 +  if(!second) {
 37.1468 +    sourcing=true;
 37.1469 +    return AddSubMenu(new cMenuSource(&MP3Sources,tr("MP3 source")));
 37.1470 +    }
 37.1471 +  sourcing=false;
 37.1472 +  cFileSource *src=cMenuSource::GetSelected();
 37.1473 +  if(src) {
 37.1474 +    MP3Sources.SetSource(src);
 37.1475 +    ScanLists();
 37.1476 +    Display();
 37.1477 +    }
 37.1478 +  return osContinue;
 37.1479 +}
 37.1480 +
 37.1481 +eOSState cMenuMP3::Instant(bool second)
 37.1482 +{
 37.1483 +  if(HasSubMenu()) return osContinue;
 37.1484 +
 37.1485 +  if(!second) {
 37.1486 +    instanting=true;
 37.1487 +    return AddSubMenu(new cMenuInstantBrowse(MP3Sources.GetSource(),tr(BUTTON"Play"),tr("Play all")));
 37.1488 +    }
 37.1489 +  instanting=false;
 37.1490 +  cFileObj *item=cMenuInstantBrowse::GetSelected();
 37.1491 +  if(item) {
 37.1492 +    Status(tr("Building playlist..."));
 37.1493 +    cInstantPlayList *newpl = new cInstantPlayList(item);
 37.1494 +    if(newpl->Load() && newpl->Count()) {
 37.1495 +      isyslog("mp3: playback started with instant playlist %s", newpl->Name());
 37.1496 +      cMP3Control::SetPlayList(newpl);
 37.1497 +      if(MP3Setup.KeepSelect) { Status(0); return Instant(false); }
 37.1498 +      return osEnd;
 37.1499 +      }
 37.1500 +    Status(0);
 37.1501 +    delete newpl;
 37.1502 +    Error(tr("Error building playlist!"));
 37.1503 +    }
 37.1504 +  return osContinue;
 37.1505 +}
 37.1506 +
 37.1507 +eOSState cMenuMP3::ProcessKey(eKeys Key)
 37.1508 +{
 37.1509 +  eOSState state = cOsdMenu::ProcessKey(Key);
 37.1510 +
 37.1511 +  if(!HasSubMenu() && state==osContinue) { // eval the return value from submenus
 37.1512 +    if(renaming) return Rename(true);
 37.1513 +    if(sourcing) return Source(true);
 37.1514 +    if(instanting) return Instant(true);
 37.1515 +    }
 37.1516 +
 37.1517 +  if(state == osUnknown) {
 37.1518 +    switch(Key) {
 37.1519 +      case kOk:     return Play();
 37.1520 +      case kRed:    return (buttonnum==1 ? Edit() : SetButtons(1)); 
 37.1521 +      case kGreen:  return (buttonnum==1 ? Source(false) : New());
 37.1522 +      case kYellow: return (buttonnum==1 ? Instant(false) : Delete());
 37.1523 +      case kBlue:   return (buttonnum==1 ? SetButtons(2) : Rename(false));
 37.1524 +      case kMenu:   return osEnd;
 37.1525 +      default:      break;
 37.1526 +      }
 37.1527 +    }
 37.1528 +  return state;
 37.1529 +}
 37.1530 +
 37.1531 +// --- PropagateImage ----------------------------------------------------------
 37.1532 +
 37.1533 +void PropagateImage(const char *image)
 37.1534 +{
 37.1535 +  cPlugin *graphtft=cPluginManager::GetPlugin("graphtft");
 37.1536 +  if(graphtft) graphtft->SetupParse("CoverImage",image ? image:"");
 37.1537 +}
 37.1538 +
 37.1539 +// --- cPluginMP3 --------------------------------------------------------------
 37.1540 +
 37.1541 +static const char *VERSION        = PLUGIN_VERSION;
 37.1542 +static const char *DESCRIPTION    = "A versatile audio player";
 37.1543 +static const char *MAINMENUENTRY  = "MP3";
 37.1544 +
 37.1545 +class cPluginMp3 : public cPlugin {
 37.1546 +private:
 37.1547 +#if APIVERSNUM >= 10330
 37.1548 +  bool ExternalPlay(const char *path, bool test);
 37.1549 +#endif
 37.1550 +public:
 37.1551 +  cPluginMp3(void);
 37.1552 +  virtual ~cPluginMp3();
 37.1553 +  virtual const char *Version(void) { return VERSION; }
 37.1554 +  virtual const char *Description(void) { return tr(DESCRIPTION); }
 37.1555 +  virtual const char *CommandLineHelp(void);
 37.1556 +  virtual bool ProcessArgs(int argc, char *argv[]);
 37.1557 +#if APIVERSNUM >= 10131
 37.1558 +  virtual bool Initialize(void);
 37.1559 +#else
 37.1560 +  virtual bool Start(void);
 37.1561 +#endif
 37.1562 +  virtual void Housekeeping(void);
 37.1563 +  virtual const char *MainMenuEntry(void);
 37.1564 +  virtual cOsdObject *MainMenuAction(void);
 37.1565 +  virtual cMenuSetupPage *SetupMenu(void);
 37.1566 +  virtual bool SetupParse(const char *Name, const char *Value);
 37.1567 +#if APIVERSNUM >= 10330
 37.1568 +  virtual bool Service(const char *Id, void *Data);
 37.1569 +#if APIVERSNUM >= 10331
 37.1570 +  virtual const char **SVDRPHelpPages(void);
 37.1571 +  virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
 37.1572 +#endif
 37.1573 +#endif
 37.1574 +  };
 37.1575 +
 37.1576 +cPluginMp3::cPluginMp3(void)
 37.1577 +{
 37.1578 +  // Initialize any member varaiables here.
 37.1579 +  // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
 37.1580 +  // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
 37.1581 +}
 37.1582 +
 37.1583 +cPluginMp3::~cPluginMp3()
 37.1584 +{
 37.1585 +  InfoCache.Save();
 37.1586 +  delete mgr;
 37.1587 +}
 37.1588 +
 37.1589 +const char *cPluginMp3::CommandLineHelp(void)
 37.1590 +{
 37.1591 +  static char *help_str=0;
 37.1592 +  
 37.1593 +  free(help_str);    //                                     for easier orientation, this is column 80|
 37.1594 +  asprintf(&help_str,"  -m CMD,   --mount=CMD    use CMD to mount/unmount/eject mp3 sources\n"
 37.1595 +                     "                           (default: %s)\n"
 37.1596 +                     "  -n CMD,   --network=CMD  execute CMD before & after network access\n"
 37.1597 +                     "                           (default: %s)\n"
 37.1598 +                     "  -C DIR,   --cache=DIR    store ID3 cache file in DIR\n"
 37.1599 +                     "                           (default: %s)\n"
 37.1600 +                     "  -B DIR,   --cddb=DIR     search CDDB files in DIR\n"
 37.1601 +                     "                           (default: %s)\n"
 37.1602 +                     "  -D DEV,   --dsp=DEV      device for OSS output\n"
 37.1603 +                     "                           (default: %s)\n"
 37.1604 +                     "  -i CMD,   --iconv=CMD    use CMD to convert background images\n"
 37.1605 +                     "                           (default: %s)\n"
 37.1606 +                     "  -c DIR,   --icache=DIR   cache converted images in DIR\n"
 37.1607 +                     "                           (default: %s)\n"
 37.1608 +                     "  -S SUB,   --sources=SUB  search sources config in SUB subdirectory\n"
 37.1609 +                     "                           (default: %s)\n",
 37.1610 +                     
 37.1611 +                     mountscript,
 37.1612 +                     netscript ? netscript:"none",
 37.1613 +                     cachedir ? cachedir:"video dir",
 37.1614 +#ifdef HAVE_SNDFILE
 37.1615 +                     cddbpath,
 37.1616 +#else
 37.1617 +                     "none",
 37.1618 +#endif
 37.1619 +#ifdef WITH_OSS
 37.1620 +                     dspdevice,
 37.1621 +#else
 37.1622 +                     "none",
 37.1623 +#endif
 37.1624 +                     imageconv,
 37.1625 +                     imagecache,
 37.1626 +                     sourcesSub ? sourcesSub:"empty"
 37.1627 +                     );
 37.1628 +  return help_str;
 37.1629 +}
 37.1630 +
 37.1631 +bool cPluginMp3::ProcessArgs(int argc, char *argv[])
 37.1632 +{
 37.1633 +  static struct option long_options[] = {
 37.1634 +      { "mount",    required_argument, NULL, 'm' },
 37.1635 +      { "network",  required_argument, NULL, 'n' },
 37.1636 +      { "cddb",     required_argument, NULL, 'B' },
 37.1637 +      { "dsp",      required_argument, NULL, 'D' },
 37.1638 +      { "cache",    required_argument, NULL, 'C' },
 37.1639 +      { "icache",   required_argument, NULL, 'c' },
 37.1640 +      { "iconv",    required_argument, NULL, 'i' },
 37.1641 +      { "sources",  required_argument, NULL, 'S' },
 37.1642 +      { NULL }
 37.1643 +    };
 37.1644 +
 37.1645 +  int c, option_index = 0;
 37.1646 +  while((c=getopt_long(argc,argv,"c:i:m:n:B:C:D:S:",long_options,&option_index))!=-1) {
 37.1647 +    switch (c) {
 37.1648 +      case 'i': imageconv=optarg; break;
 37.1649 +      case 'c': imagecache=optarg; break;
 37.1650 +      case 'm': mountscript=optarg; break;
 37.1651 +      case 'n': netscript=optarg; break;
 37.1652 +      case 'C': cachedir=optarg; break;
 37.1653 +      case 'S': sourcesSub=optarg; break;
 37.1654 +      case 'B':
 37.1655 +#ifdef HAVE_SNDFILE
 37.1656 +                cddbpath=optarg; break;
 37.1657 +#else
 37.1658 +                fprintf(stderr, "mp3: libsndfile support has not been compiled in!\n"); return false;
 37.1659 +#endif
 37.1660 +      case 'D':
 37.1661 +#ifdef WITH_OSS
 37.1662 +                dspdevice=optarg; break;
 37.1663 +#else
 37.1664 +                fprintf(stderr, "mp3: OSS output has not been compiled in!\n"); return false;
 37.1665 +#endif
 37.1666 +      default:  return false;
 37.1667 +      }
 37.1668 +    }
 37.1669 +  return true;
 37.1670 +}
 37.1671 +
 37.1672 +#if APIVERSNUM >= 10131
 37.1673 +bool cPluginMp3::Initialize(void)
 37.1674 +#else
 37.1675 +bool cPluginMp3::Start(void)
 37.1676 +#endif
 37.1677 +{
 37.1678 +  if(!CheckVDRVersion(1,1,29,"mp3")) return false;
 37.1679 +  i18n_name=Name();
 37.1680 +  MP3Sources.Load(AddDirectory(ConfigDirectory(sourcesSub),"mp3sources.conf"));
 37.1681 +  if(MP3Sources.Count()<1) {
 37.1682 +     esyslog("ERROR: you should have defined at least one source in mp3sources.conf");
 37.1683 +     fprintf(stderr,"No source(s) defined in mp3sources.conf\n");
 37.1684 +     return false;
 37.1685 +     }
 37.1686 +  InfoCache.Load();
 37.1687 +  RegisterI18n(Phrases);
 37.1688 +  mgr=new cPlayManager;
 37.1689 +  if(!mgr) {
 37.1690 +    esyslog("ERROR: creating playmanager failed");
 37.1691 +    fprintf(stderr,"Creating playmanager failed\n");
 37.1692 +    return false;
 37.1693 +    }
 37.1694 +  d(printf("mp3: using %s\n",mad_version))
 37.1695 +  d(printf("mp3: compiled with %s\n",MAD_VERSION))
 37.1696 +  return true;
 37.1697 +}
 37.1698 +
 37.1699 +void cPluginMp3::Housekeeping(void)
 37.1700 +{
 37.1701 +  InfoCache.Save();
 37.1702 +}
 37.1703 +
 37.1704 +const char *cPluginMp3::MainMenuEntry(void)
 37.1705 +{
 37.1706 +  return MP3Setup.HideMainMenu ? 0 : tr(MAINMENUENTRY);
 37.1707 +}
 37.1708 +
 37.1709 +cOsdObject *cPluginMp3::MainMenuAction(void)
 37.1710 +{
 37.1711 +  return new cMenuMP3;
 37.1712 +}
 37.1713 +
 37.1714 +cMenuSetupPage *cPluginMp3::SetupMenu(void)
 37.1715 +{
 37.1716 +  return new cMenuSetupMP3;
 37.1717 +}
 37.1718 +
 37.1719 +bool cPluginMp3::SetupParse(const char *Name, const char *Value)
 37.1720 +{
 37.1721 +  if      (!strcasecmp(Name, "InitLoopMode"))     MP3Setup.InitLoopMode    = atoi(Value);
 37.1722 +  else if (!strcasecmp(Name, "InitShuffleMode"))  MP3Setup.InitShuffleMode = atoi(Value);
 37.1723 +  else if (!strcasecmp(Name, "AudioMode"))        MP3Setup.AudioMode       = atoi(Value);
 37.1724 +  else if (!strcasecmp(Name, "BgrScan"))          MP3Setup.BgrScan         = atoi(Value);
 37.1725 +  else if (!strcasecmp(Name, "EditorMode"))       MP3Setup.EditorMode      = atoi(Value);
 37.1726 +  else if (!strcasecmp(Name, "DisplayMode"))      MP3Setup.DisplayMode     = atoi(Value);
 37.1727 +  else if (!strcasecmp(Name, "BackgrMode"))       MP3Setup.BackgrMode      = atoi(Value);
 37.1728 +  else if (!strcasecmp(Name, "MenuMode"))         MP3Setup.MenuMode        = atoi(Value);
 37.1729 +  else if (!strcasecmp(Name, "TargetLevel"))      MP3Setup.TargetLevel     = atoi(Value);
 37.1730 +  else if (!strcasecmp(Name, "LimiterLevel"))     MP3Setup.LimiterLevel    = atoi(Value);
 37.1731 +  else if (!strcasecmp(Name, "Only48kHz"))        MP3Setup.Only48kHz       = atoi(Value);
 37.1732 +  else if (!strcasecmp(Name, "UseProxy"))         MP3Setup.UseProxy        = atoi(Value);
 37.1733 +  else if (!strcasecmp(Name, "ProxyHost"))        strn0cpy(MP3Setup.ProxyHost,Value,MAX_HOSTNAME);
 37.1734 +  else if (!strcasecmp(Name, "ProxyPort"))        MP3Setup.ProxyPort       = atoi(Value);
 37.1735 +  else if (!strcasecmp(Name, "UseCddb"))          MP3Setup.UseCddb         = atoi(Value);
 37.1736 +  else if (!strcasecmp(Name, "CddbHost"))         strn0cpy(MP3Setup.CddbHost,Value,MAX_HOSTNAME);
 37.1737 +  else if (!strcasecmp(Name, "CddbPort"))         MP3Setup.CddbPort        = atoi(Value);
 37.1738 +  else if (!strcasecmp(Name, "AbortAtEOL"))       MP3Setup.AbortAtEOL      = atoi(Value);
 37.1739 +  else if (!strcasecmp(Name, "AudioOutMode")) {
 37.1740 +    MP3Setup.AudioOutMode = atoi(Value);
 37.1741 +#ifndef WITH_OSS
 37.1742 +    if(MP3Setup.AudioOutMode==AUDIOOUTMODE_OSS) {
 37.1743 +      esyslog("WARNING: AudioOutMode OSS not supported, falling back to DVB");
 37.1744 +      MP3Setup.AudioOutMode=AUDIOOUTMODE_DVB;
 37.1745 +      }
 37.1746 +#endif
 37.1747 +    }
 37.1748 +#if APIVERSNUM >= 10307
 37.1749 +  else if (!strcasecmp(Name, "ReplayDisplay"))      MP3Setup.ReplayDisplay = atoi(Value);
 37.1750 +#endif
 37.1751 +  else if (!strcasecmp(Name, "HideMainMenu"))       MP3Setup.HideMainMenu  = atoi(Value);
 37.1752 +  else if (!strcasecmp(Name, "KeepSelect"))         MP3Setup.KeepSelect    = atoi(Value);
 37.1753 +  else if (!strcasecmp(Name, "TitleArtistOrder"))   MP3Setup.TitleArtistOrder = atoi(Value);
 37.1754 +  else return false;
 37.1755 +  return true;
 37.1756 +}
 37.1757 +
 37.1758 +#if APIVERSNUM >= 10330
 37.1759 +
 37.1760 +bool cPluginMp3::ExternalPlay(const char *path, bool test)
 37.1761 +{
 37.1762 +  char real[PATH_MAX+1];
 37.1763 +  if(realpath(path,real)) {
 37.1764 +    cFileSource *src=MP3Sources.FindSource(real);
 37.1765 +    if(src) {
 37.1766 +      cFileObj *item=new cFileObj(src,0,0,otFile);
 37.1767 +      if(item) {
 37.1768 +        item->SplitAndSet(real);
 37.1769 +        if(item->GuessType()) {
 37.1770 +          if(item->Exists()) {
 37.1771 +            cInstantPlayList *pl=new cInstantPlayList(item);
 37.1772 +            if(pl && pl->Load() && pl->Count()) {
 37.1773 +              if(!test) cMP3Control::SetPlayList(pl);
 37.1774 +              else delete pl;
 37.1775 +              delete item;
 37.1776 +              return true;
 37.1777 +              }
 37.1778 +            else dsyslog("MP3 service: error building playlist");
 37.1779 +            delete pl;
 37.1780 +            }
 37.1781 +          else dsyslog("MP3 service: cannot play '%s'",path);
 37.1782 +          }
 37.1783 +        else dsyslog("MP3 service: GuessType() failed for '%s'",path);
 37.1784 +        delete item;
 37.1785 +        }
 37.1786 +      }
 37.1787 +    else dsyslog("MP3 service: cannot find source for '%s', real '%s'",path,real);
 37.1788 +    }
 37.1789 +  else if(errno!=ENOENT && errno!=ENOTDIR)
 37.1790 +    esyslog("ERROR: realpath: %s: %s",path,strerror(errno));
 37.1791 +  return false;
 37.1792 +}
 37.1793 +
 37.1794 +bool cPluginMp3::Service(const char *Id, void *Data)
 37.1795 +{
 37.1796 +  if(!strcasecmp(Id,"MP3-Play-v1")) {
 37.1797 +    if(Data) {
 37.1798 +      struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
 37.1799 +      msd->result=ExternalPlay(msd->data.filename,false);
 37.1800 +      }
 37.1801 +    return true;
 37.1802 +    }
 37.1803 +  else if(!strcasecmp(Id,"MP3-Test-v1")) {
 37.1804 +    if(Data) {
 37.1805 +      struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
 37.1806 +      msd->result=ExternalPlay(msd->data.filename,true);
 37.1807 +      }
 37.1808 +    return true;
 37.1809 +    }
 37.1810 +  return false;
 37.1811 +}
 37.1812 +
 37.1813 +#if APIVERSNUM >= 10331
 37.1814 +
 37.1815 +const char **cPluginMp3::SVDRPHelpPages(void)
 37.1816 +{
 37.1817 +  static const char *HelpPages[] = {
 37.1818 +    "PLAY <filename>\n"
 37.1819 +    "    Triggers playback of file 'filename'.",
 37.1820 +    "TEST <filename>\n"
 37.1821 +    "    Tests is playback of file 'filename' is possible.",
 37.1822 +    "CURR\n"
 37.1823 +    "    Returns filename of song currently being replayed.",
 37.1824 +    NULL
 37.1825 +    };
 37.1826 +  return HelpPages;
 37.1827 +}
 37.1828 +
 37.1829 +cString cPluginMp3::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
 37.1830 +{
 37.1831 +  if(!strcasecmp(Command,"PLAY")) {
 37.1832 +    if(*Option) {
 37.1833 +      if(ExternalPlay(Option,false)) return "Playback triggered";
 37.1834 +      else { ReplyCode=550; return "Playback failed"; }
 37.1835 +      }
 37.1836 +    else { ReplyCode=501; return "Missing filename"; }
 37.1837 +    }
 37.1838 +  else if(!strcasecmp(Command,"TEST")) {
 37.1839 +    if(*Option) {
 37.1840 +      if(ExternalPlay(Option,true)) return "Playback possible";
 37.1841 +      else { ReplyCode=550; return "Playback not possible"; }
 37.1842 +      }
 37.1843 +    else { ReplyCode=501; return "Missing filename"; }
 37.1844 +    }
 37.1845 +  else if(!strcasecmp(Command,"CURR")) {
 37.1846 +    cControl *control=cControl::Control();
 37.1847 +    if(control && typeid(*control)==typeid(cMP3Control)) {
 37.1848 +      cMP3PlayInfo mode;
 37.1849 +      if(mgr->Info(-1,&mode)) return mode.Filename;
 37.1850 +      else return "<unknown>";
 37.1851 +      }
 37.1852 +    else { ReplyCode=550; return "No running playback"; }
 37.1853 +    }
 37.1854 +  return NULL;
 37.1855 +}
 37.1856 +
 37.1857 +#endif // 1.3.31
 37.1858 +#endif // 1.3.30
 37.1859 +
 37.1860 +VDRPLUGINCREATOR(cPluginMp3); // Don't touch this!
    38.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    38.2 +++ b/mplayer.c	Sat Dec 29 14:47:40 2007 +0100
    38.3 @@ -0,0 +1,955 @@
    38.4 +/*
    38.5 + * MP3/MPlayer plugin to VDR (C++)
    38.6 + *
    38.7 + * (C) 2001-2007 Stefan Huelswitt <s.huelswitt@gmx.de>
    38.8 + *
    38.9 + * This code is free software; you can redistribute it and/or
   38.10 + * modify it under the terms of the GNU General Public License
   38.11 + * as published by the Free Software Foundation; either version 2
   38.12 + * of the License, or (at your option) any later version.
   38.13 + *
   38.14 + * This code is distributed in the hope that it will be useful,
   38.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   38.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   38.17 + * GNU General Public License for more details.
   38.18 + *
   38.19 + * You should have received a copy of the GNU General Public License
   38.20 + * along with this program; if not, write to the Free Software
   38.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   38.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   38.23 + */
   38.24 +
   38.25 +#include <getopt.h>
   38.26 +#include <malloc.h>
   38.27 +#include <stdlib.h>
   38.28 +#include <ctype.h>
   38.29 +
   38.30 +#include "common.h"
   38.31 +
   38.32 +#include <vdr/plugin.h>
   38.33 +#include <vdr/player.h>
   38.34 +#include <vdr/status.h>
   38.35 +#include <vdr/font.h>
   38.36 +#include <vdr/osdbase.h>
   38.37 +#include <vdr/menuitems.h>
   38.38 +#ifdef HAVE_BEAUTYPATCH
   38.39 +#include <vdr/fontsym.h>
   38.40 +#endif
   38.41 +#if APIVERSNUM >= 10307
   38.42 +#include <vdr/skins.h>
   38.43 +#endif
   38.44 +#if APIVERSNUM >= 10332
   38.45 +#include <vdr/remote.h>
   38.46 +#endif
   38.47 +
   38.48 +#if APIVERSNUM > 10307
   38.49 +#include <vdr/menu.h>
   38.50 +#elif APIVERSNUM == 10307
   38.51 +class cMenuText : public cOsdMenu {
   38.52 +private:
   38.53 +  char *text;
   38.54 +public:
   38.55 +  cMenuText(const char *Title, const char *Text, eDvbFont Font = fontOsd);
   38.56 +  virtual ~cMenuText();
   38.57 +  void SetText(const char *Text);
   38.58 +  virtual void Display(void);
   38.59 +  virtual eOSState ProcessKey(eKeys Key);
   38.60 +  };
   38.61 +#else
   38.62 +class cMenuText : public cOsdMenu {
   38.63 +public:
   38.64 +  cMenuText(const char *Title, const char *Text, eDvbFont Font = fontOsd);
   38.65 +  virtual eOSState ProcessKey(eKeys Key);
   38.66 +  };
   38.67 +#endif
   38.68 +
   38.69 +#include "setup.h"
   38.70 +#include "setup-mplayer.h"
   38.71 +#include "menu.h"
   38.72 +#include "player-mplayer.h"
   38.73 +#include "data.h"
   38.74 +#include "data-src.h"
   38.75 +#include "i18n.h"
   38.76 +#include "version.h"
   38.77 +#include "service.h"
   38.78 +
   38.79 +const char *sourcesSub=0;
   38.80 +cFileSources MPlaySources;
   38.81 +
   38.82 +// --- cMenuSetupMPlayer --------------------------------------------------------
   38.83 +
   38.84 +class cMenuSetupMPlayer : public cMenuSetupPage {
   38.85 +private:
   38.86 +  cMPlayerSetup data;
   38.87 +  const char *res[3];
   38.88 +protected:
   38.89 +  virtual void Store(void);
   38.90 +public:
   38.91 +  cMenuSetupMPlayer(void);
   38.92 +  };
   38.93 +
   38.94 +cMenuSetupMPlayer::cMenuSetupMPlayer(void)
   38.95 +{
   38.96 +  data=MPlayerSetup;
   38.97 +  SetSection(tr("MPlayer"));
   38.98 +  Add(new cMenuEditBoolItem(tr("Setup.MPlayer$Control mode"),  &data.SlaveMode, tr("Traditional"), tr("Slave")));
   38.99 +#if APIVERSNUM < 10307
  38.100 +  Add(new cMenuEditIntItem( tr("Setup.MPlayer$OSD position"),  &data.OsdPos, 0, 6));
  38.101 +#endif
  38.102 +  res[0]=tr("disabled");
  38.103 +  res[1]=tr("global only");
  38.104 +  res[2]=tr("local first");
  38.105 +  Add(new cMenuEditStraItem(tr("Setup.MPlayer$Resume mode"),   &data.ResumeMode, 3, res));
  38.106 +  Add(new cMenuEditBoolItem(tr("Hide mainmenu entry"),         &data.HideMainMenu));
  38.107 +  for(int i=0; i<10; i++) {
  38.108 +    char name[32];
  38.109 +    snprintf(name,sizeof(name),"%s %d",tr("Setup.MPlayer$Slave command key"),i);
  38.110 +    static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789!\"§$%&/()=?{}[]\\+*~#',;.:-_<>|@´`^°" };
  38.111 +    Add(new cMenuEditStrItem(name, data.KeyCmd[i],MAX_KEYCMD,allowed));
  38.112 +    }
  38.113 +}
  38.114 +
  38.115 +void cMenuSetupMPlayer::Store(void)
  38.116 +{
  38.117 +  MPlayerSetup=data;
  38.118 +  SetupStore("ControlMode", MPlayerSetup.SlaveMode);
  38.119 +  SetupStore("HideMainMenu",MPlayerSetup.HideMainMenu);
  38.120 +  SetupStore("ResumeMode",  MPlayerSetup.ResumeMode);
  38.121 +#if APIVERSNUM < 10307
  38.122 +  SetupStore("OsdPos",      MPlayerSetup.OsdPos);
  38.123 +#endif
  38.124 +  for(int i=0; i<10; i++) {
  38.125 +    char name[16];
  38.126 +    snprintf(name,sizeof(name),"KeyCmd%d",i);
  38.127 +    SetupStore(name,MPlayerSetup.KeyCmd[i]);
  38.128 +    }
  38.129 +}
  38.130 +
  38.131 +// --- cMPlayerControl ---------------------------------------------------------
  38.132 +
  38.133 +class cMPlayerControl : public cControl {
  38.134 +private:
  38.135 +  static cFileObj *file;
  38.136 +  static bool rewind;
  38.137 +  cMPlayerPlayer *player;
  38.138 +#if APIVERSNUM >= 10307
  38.139 +  cSkinDisplayReplay *display;
  38.140 +#endif
  38.141 +  bool visible, modeOnly, haveBeauty;
  38.142 +  time_t timeoutShow;
  38.143 +  int lastCurrent, lastTotal;
  38.144 +  char *lastReplayMsg;
  38.145 +  //
  38.146 +  bool jumpactive, jumphide, jumpmode;
  38.147 +  int jumpval;
  38.148 +  //
  38.149 +  void Stop(void);
  38.150 +  void ShowTimed(int Seconds=0);
  38.151 +  void DisplayAtBottom(const char *s);
  38.152 +  void ShowProgress(void);
  38.153 +  void ShowMode(void);
  38.154 +  void ShowTitle(void);
  38.155 +  void Jump(void);
  38.156 +  void JumpProcess(eKeys Key);
  38.157 +  void JumpDisplay(void);
  38.158 +public:
  38.159 +  cMPlayerControl(void);
  38.160 +  virtual ~cMPlayerControl();
  38.161 +  virtual eOSState ProcessKey(eKeys Key);
  38.162 +  virtual void Show(void) { ShowTimed(); }
  38.163 +  virtual void Hide(void);
  38.164 +  static void SetFile(const cFileObj *File, bool Rewind);
  38.165 +  };
  38.166 +
  38.167 +cFileObj *cMPlayerControl::file=0;
  38.168 +bool cMPlayerControl::rewind=false;
  38.169 +
  38.170 +cMPlayerControl::cMPlayerControl(void)
  38.171 +:cControl(player=new cMPlayerPlayer(file,rewind))
  38.172 +{
  38.173 +  visible=modeOnly=jumpactive=haveBeauty=false;
  38.174 +  lastReplayMsg=0;
  38.175 +#if APIVERSNUM >= 10307
  38.176 +  display=0;
  38.177 +#else
  38.178 +#ifdef HAVE_BEAUTYPATCH
  38.179 +#if APIVERSNUM >= 10300
  38.180 +  const cFont *sym=cFont::GetFont(fontSym);
  38.181 +  const cFont *osd=cFont::GetFont(fontOsd);
  38.182 +  const cFont::tCharData *symD=sym->CharData(32);
  38.183 +  const cFont::tCharData *osdD=osd->CharData(32);
  38.184 +#else //APIVERSNUM >= 10300
  38.185 +  cFont *sym=new cFont(fontSym);
  38.186 +  cFont *osd=new cFont(fontOsd);
  38.187 +  const cFont::tCharData *symD=sym->CharData(32);
  38.188 +  const cFont::tCharData *osdD=osd->CharData(32);
  38.189 +  delete sym;
  38.190 +  delete osd;
  38.191 +#endif //APIVERSNUM >= 10300
  38.192 +  if(symD != osdD) haveBeauty=true;
  38.193 +  d(printf("mplayer: beauty patch %sdetected\n",haveBeauty?"":"NOT "))
  38.194 +#endif //HAVE_BEAUTYPATCH
  38.195 +#endif //APIVERSNUM >= 10307
  38.196 +  ShowTitle();
  38.197 +}
  38.198 +
  38.199 +cMPlayerControl::~cMPlayerControl()
  38.200 +{
  38.201 +  Stop();
  38.202 +#if APIVERSNUM >= 10338
  38.203 +  cStatus::MsgReplaying(this,0,0,false);
  38.204 +#else
  38.205 +  cStatus::MsgReplaying(this, NULL);
  38.206 +#endif
  38.207 +  free(lastReplayMsg);
  38.208 +}
  38.209 +
  38.210 +void cMPlayerControl::SetFile(const cFileObj *File, bool Rewind)
  38.211 +{
  38.212 +  delete file;
  38.213 +  file=File ? new cFileObj(File) : 0;
  38.214 +  rewind=Rewind;
  38.215 +}
  38.216 +
  38.217 +void cMPlayerControl::Stop(void)
  38.218 +{
  38.219 +  delete player; player=0;
  38.220 +}
  38.221 +
  38.222 +void cMPlayerControl::ShowTimed(int Seconds)
  38.223 +{
  38.224 +  if(modeOnly) Hide();
  38.225 +  if(!visible) {
  38.226 +    ShowProgress();
  38.227 +    timeoutShow = Seconds>0 ? time(0)+Seconds : 0;
  38.228 +    }
  38.229 +}
  38.230 +
  38.231 +void cMPlayerControl::Hide(void)
  38.232 +{
  38.233 +  if(visible) {
  38.234 +#if APIVERSNUM >= 10307
  38.235 +    delete display; display=0;
  38.236 +#else
  38.237 +    Interface->Close();
  38.238 +#endif
  38.239 +    visible=modeOnly=false;
  38.240 +#if APIVERSNUM >= 10500
  38.241 +    SetNeedsFastResponse(false);
  38.242 +#else
  38.243 +    needsFastResponse=false;
  38.244 +#endif
  38.245 +    }
  38.246 +}
  38.247 +
  38.248 +void cMPlayerControl::DisplayAtBottom(const char *s)
  38.249 +{
  38.250 +#if APIVERSNUM < 10307
  38.251 +  const int p=modeOnly ? 0 : 2;
  38.252 +  if(s) {
  38.253 +    const int d=max(Width()-cOsd::WidthInCells(s),0) / 2;
  38.254 +    if(modeOnly) Interface->Fill(0, p, Interface->Width(), 1, clrTransparent);
  38.255 +    Interface->Write(d, p, s);
  38.256 +    }
  38.257 +  else
  38.258 +    Interface->Fill(12, p, Width() - 22, 1, clrBackground);
  38.259 +#endif
  38.260 +}
  38.261 +
  38.262 +void cMPlayerControl::ShowTitle(void)
  38.263 +{
  38.264 +  const char *path=0;
  38.265 +  bool release=true;
  38.266 +  if(player) path=player->GetCurrentName();
  38.267 +  if(!path) {
  38.268 +    path=file->FullPath();
  38.269 +    release=false;
  38.270 +    }
  38.271 +  if(path) {
  38.272 +    const char *name=rindex(path,'/');
  38.273 +    if(name) name++; else name=path;
  38.274 +    if(!lastReplayMsg || strcmp(lastReplayMsg,path)) {
  38.275 +#if APIVERSNUM >= 10338
  38.276 +      cStatus::MsgReplaying(this,name,path,true);
  38.277 +#else
  38.278 +      cStatus::MsgReplaying(this,path);
  38.279 +#endif
  38.280 +      free(lastReplayMsg);
  38.281 +      lastReplayMsg=strdup(path);
  38.282 +      }
  38.283 +    if(visible) {
  38.284 +#if APIVERSNUM >= 10307
  38.285 +      if(display) display->SetTitle(name);
  38.286 +#else
  38.287 +      int n=strlen(name);
  38.288 +      if(n>Width()) {
  38.289 +        n=n-Width()+4; if(n<0) n=0;
  38.290 +        char str[72];
  38.291 +        snprintf(str,sizeof(str),"... %s",name+n);
  38.292 +        Interface->Write(0,0,str);
  38.293 +        }
  38.294 +      else Interface->Write(0,0,name);
  38.295 +#endif
  38.296 +      }
  38.297 +    }
  38.298 +  if(release) free((void *)path);
  38.299 +}
  38.300 +
  38.301 +void cMPlayerControl::ShowProgress(void)
  38.302 +{
  38.303 +  int Current, Total;
  38.304 +
  38.305 +  if(GetIndex(Current,Total) && Total>0) {
  38.306 +    bool flush=false;
  38.307 +    if(!visible) {
  38.308 +#if APIVERSNUM >= 10307
  38.309 +      display=Skins.Current()->DisplayReplay(false);
  38.310 +#else
  38.311 +      Interface->Open(Setup.OSDwidth,-MPlayerSetup.OsdPos-3);
  38.312 +      Interface->Clear();
  38.313 +      if(MPlayerSetup.OsdPos>0) Interface->Fill(0,3,Interface->Width(),MPlayerSetup.OsdPos,clrTransparent);
  38.314 +#endif
  38.315 +      visible=true; modeOnly=false;
  38.316 +#if APIVERSNUM >= 10500
  38.317 +      SetNeedsFastResponse(true);
  38.318 +#else
  38.319 +      needsFastResponse=true;
  38.320 +#endif
  38.321 +      lastCurrent=lastTotal=-1;
  38.322 +      flush=true;
  38.323 +      }
  38.324 +
  38.325 +    if(abs(Current-lastCurrent)>12) {
  38.326 +#if APIVERSNUM >= 10307
  38.327 +      if(Total>0) display->SetProgress(Current, Total);
  38.328 +      display->SetCurrent(IndexToHMSF(Current));
  38.329 +      display->SetTotal(IndexToHMSF(Total));
  38.330 +      bool Play, Forward;
  38.331 +      int Speed;
  38.332 +      if(GetReplayMode(Play,Forward,Speed)) 
  38.333 +        display->SetMode(Play, Forward, Speed);
  38.334 +#else
  38.335 +      cProgressBar ProgressBar(Width() * cOsd::CellWidth(), cOsd::LineHeight(), Current, Total);
  38.336 +      Interface->SetBitmap(0, cOsd::LineHeight(), ProgressBar);
  38.337 +      Interface->Write(0,2,IndexToHMSF(Current));
  38.338 +      Interface->Write(-7,2,IndexToHMSF(Total));
  38.339 +#endif
  38.340 +      ShowTitle();
  38.341 +      flush=true;
  38.342 +      lastCurrent=Current; lastTotal=Total;
  38.343 +      }
  38.344 +    if(flush) 
  38.345 +#if APIVERSNUM >= 10307
  38.346 +      Skins.Flush();
  38.347 +#else
  38.348 +      Interface->Flush();
  38.349 +#endif
  38.350 +    ShowMode();
  38.351 +    }
  38.352 +}
  38.353 +
  38.354 +#if APIVERSNUM < 10307
  38.355 +#ifdef HAVE_BEAUTYPATCH
  38.356 +int forwSym[] = { FSYM_FORW,FSYM_FORW1,FSYM_FORW2,FSYM_FORW3 };
  38.357 +int backSym[] = { FSYM_BACK,FSYM_BACK1,FSYM_BACK2,FSYM_BACK3 };
  38.358 +#endif
  38.359 +#endif
  38.360 +
  38.361 +void cMPlayerControl::ShowMode(void)
  38.362 +{
  38.363 +  if(Setup.ShowReplayMode && !jumpactive) {
  38.364 +    bool Play, Forward;
  38.365 +    int Speed;
  38.366 +    if(GetReplayMode(Play, Forward, Speed)) {
  38.367 +       bool NormalPlay = (Play && Speed == -1);
  38.368 +
  38.369 +       if(!visible) {
  38.370 +         if(NormalPlay) return;
  38.371 +#if APIVERSNUM >= 10307
  38.372 +         display = Skins.Current()->DisplayReplay(true);
  38.373 +#else
  38.374 +         Interface->Open(0,-MPlayerSetup.OsdPos-1);
  38.375 +#endif
  38.376 +         visible=modeOnly=true;
  38.377 +         }
  38.378 +
  38.379 +       if(modeOnly && !timeoutShow && NormalPlay) timeoutShow=time(0)+SELECTHIDE_TIMEOUT;
  38.380 +
  38.381 +#if APIVERSNUM >= 10307
  38.382 +       display->SetMode(Play, Forward, Speed);
  38.383 +#else
  38.384 +       char buf[16];
  38.385 +       eDvbFont OldFont;
  38.386 +#ifdef HAVE_BEAUTYPATCH
  38.387 +       if(haveBeauty) {
  38.388 +         int i=0;
  38.389 +         if(!(Width()&1)) buf[i++]=' ';
  38.390 +         buf[i]=FSYM_EMPTY; if(Speed>=0 && !Forward) buf[i]=backSym[Speed];
  38.391 +         i++;
  38.392 +         buf[i++]=Play?(Speed==-1?FSYM_PLAY:FSYM_EMPTY):FSYM_PAUSE;
  38.393 +         buf[i]=FSYM_EMPTY; if(Speed>=0 && Forward) buf[i]=forwSym[Speed];
  38.394 +         i++;
  38.395 +         if(!(Width()&1)) buf[i++]=' ';
  38.396 +         buf[i]=0;
  38.397 +         OldFont = Interface->SetFont(fontSym);
  38.398 +         }
  38.399 +       else {
  38.400 +#endif //HAVE_BEAUTYPATCH
  38.401 +         const char *Mode;
  38.402 +         if (Speed == -1) Mode = Play    ? "  >  " : " ||  ";
  38.403 +         else if (Play)   Mode = Forward ? " X>> " : " <<X ";
  38.404 +         else             Mode = Forward ? " X|> " : " <|X ";
  38.405 +         strn0cpy(buf, Mode, sizeof(buf));
  38.406 +         char *p = strchr(buf, 'X');
  38.407 +         if(p) *p = Speed > 0 ? '1' + Speed - 1 : ' ';
  38.408 +         OldFont = Interface->SetFont(fontFix);
  38.409 +#ifdef HAVE_BEAUTYPATCH
  38.410 +         }
  38.411 +#endif //HAVE_BEAUTYPATCH
  38.412 +       DisplayAtBottom(buf);
  38.413 +       Interface->SetFont(OldFont);
  38.414 +#endif //APIVERSNUM >= 10307
  38.415 +       }
  38.416 +    }
  38.417 +}
  38.418 +
  38.419 +void cMPlayerControl::JumpDisplay(void)
  38.420 +{
  38.421 +  char buf[64];
  38.422 +  const char *j=tr("Jump: "), u=jumpmode?'%':'m';
  38.423 +  if(!jumpval) sprintf(buf,"%s- %c",  j,u);
  38.424 +  else         sprintf(buf,"%s%d- %c",j,jumpval,u);
  38.425 +#if APIVERSNUM >= 10307
  38.426 +  display->SetJump(buf);
  38.427 +#else
  38.428 +  DisplayAtBottom(buf);
  38.429 +#endif
  38.430 +}
  38.431 +
  38.432 +void cMPlayerControl::JumpProcess(eKeys Key)
  38.433 +{
  38.434 +  const int n=Key-k0;
  38.435 +  switch (Key) {
  38.436 +    case k0 ... k9:
  38.437 +      {
  38.438 +      const int max=jumpmode?100:lastTotal;
  38.439 +      if(jumpval*10+n <= max) jumpval=jumpval*10+n;
  38.440 +      JumpDisplay();
  38.441 +      }
  38.442 +      break;
  38.443 +    case kBlue:
  38.444 +      jumpmode=!jumpmode; jumpval=0;
  38.445 +      DisplayAtBottom(0); JumpDisplay();
  38.446 +      break;
  38.447 +    case kPlay:
  38.448 +    case kUp:
  38.449 +      player->Goto(jumpval*(jumpmode?1:60),jumpmode,false);
  38.450 +      jumpactive=false;
  38.451 +      break;
  38.452 +    case kFastRew:
  38.453 +    case kFastFwd:
  38.454 +    case kLeft:
  38.455 +    case kRight:
  38.456 +      if(!jumpmode) {
  38.457 +        player->SkipSeconds(jumpval*60 * ((Key==kLeft || Key==kFastRew) ? -1:1));
  38.458 +        jumpactive=false;
  38.459 +        }
  38.460 +      break;
  38.461 +    default:
  38.462 +      jumpactive=false;
  38.463 +      break;
  38.464 +    }
  38.465 +
  38.466 +  if(!jumpactive) {
  38.467 +    if(jumphide) Hide();
  38.468 +    else 
  38.469 +#if APIVERSNUM >= 10307
  38.470 +      display->SetJump(0);
  38.471 +#else
  38.472 +      DisplayAtBottom(0);
  38.473 +#endif
  38.474 +    }
  38.475 +}
  38.476 +
  38.477 +void cMPlayerControl::Jump(void)
  38.478 +{
  38.479 +  jumpval=0; jumphide=jumpmode=false;
  38.480 +  if(!visible) {
  38.481 +    ShowTimed(); if(!visible) return;
  38.482 +    jumphide=true;
  38.483 +    }
  38.484 +  JumpDisplay();
  38.485 +  jumpactive=true;
  38.486 +}
  38.487 +
  38.488 +eOSState cMPlayerControl::ProcessKey(eKeys Key)
  38.489 +{
  38.490 +  if(!player->Active()) { Hide(); Stop(); return osEnd; }
  38.491 +
  38.492 +  if(!player->SlaveMode()) {
  38.493 +    if(Key==kBlue) { Hide(); Stop(); return osEnd; }
  38.494 +    }
  38.495 +  else {
  38.496 +    if(visible) {
  38.497 +      if(timeoutShow && time(0)>timeoutShow) {
  38.498 +        Hide(); ShowMode();
  38.499 +        timeoutShow = 0;
  38.500 +        }
  38.501 +      else {
  38.502 +        if(modeOnly) ShowMode();
  38.503 +        else ShowProgress();
  38.504 +        }
  38.505 +      }
  38.506 +    else ShowTitle();
  38.507 +
  38.508 +    if(jumpactive && Key != kNone) {
  38.509 +      JumpProcess(Key);
  38.510 +      return osContinue;
  38.511 +      }
  38.512 +
  38.513 +    bool DoShowMode = true;
  38.514 +    switch (Key) {
  38.515 +      case kPlay:
  38.516 +      case kUp:      player->Play(); break;
  38.517 +
  38.518 +      case kPause:
  38.519 +      case kDown:    player->Pause(); break;
  38.520 +
  38.521 +      case kFastRew|k_Repeat:
  38.522 +      case kFastRew:
  38.523 +      case kLeft|k_Repeat:
  38.524 +      case kLeft:    player->SkipSeconds(-10); break;
  38.525 +
  38.526 +      case kFastFwd|k_Repeat:
  38.527 +      case kFastFwd:
  38.528 +      case kRight|k_Repeat:
  38.529 +      case kRight:   player->SkipSeconds(10); break;
  38.530 +
  38.531 +      case kRed:     Jump(); break;
  38.532 +
  38.533 +      case kGreen|k_Repeat:                      // temporary use
  38.534 +      case kGreen:   player->SkipSeconds(-60); break;
  38.535 +      case kYellow|k_Repeat:
  38.536 +      case kYellow:  player->SkipSeconds(60); break;
  38.537 +  //    case kGreen|k_Repeat:                      // reserved for future use
  38.538 +  //    case kGreen:   player->SkipPrev(); break;
  38.539 +  //    case kYellow|k_Repeat:
  38.540 +  //    case kYellow:  player->SkipNext(); break;
  38.541 +
  38.542 +      case kBack:
  38.543 +#if APIVERSNUM >= 10332
  38.544 +                     Hide();
  38.545 +                     cRemote::CallPlugin(i18n_name);
  38.546 +                     return osBack;
  38.547 +#endif
  38.548 +      case kStop:
  38.549 +      case kBlue:    Hide(); Stop(); return osEnd;
  38.550 +
  38.551 +      default:
  38.552 +        DoShowMode = false;
  38.553 +        switch(Key) {
  38.554 +          case kOk: if(visible && !modeOnly) { Hide(); DoShowMode=true; }
  38.555 +                    else ShowTimed();
  38.556 +                    break;
  38.557 +          case k0:
  38.558 +          case k1:
  38.559 +          case k2:
  38.560 +          case k3:
  38.561 +          case k4:
  38.562 +          case k5:
  38.563 +          case k6:
  38.564 +          case k7:
  38.565 +          case k8:
  38.566 +          case k9:  {
  38.567 +                    const char *cmd=MPlayerSetup.KeyCmd[Key-k0];
  38.568 +                    if(cmd[0]) player->KeyCmd(cmd);
  38.569 +                    }
  38.570 +                    break;
  38.571 +          default:  break;
  38.572 +          }
  38.573 +        break;
  38.574 +      }
  38.575 +
  38.576 +    if(DoShowMode) ShowMode();
  38.577 +    }
  38.578 +  return osContinue;
  38.579 +}
  38.580 +
  38.581 +// --- cMenuMPlayAid -----------------------------------------------------------
  38.582 +
  38.583 +class cMenuMPlayAid : public cOsdMenu {
  38.584 +public:
  38.585 +  cMenuMPlayAid(void);
  38.586 +  virtual eOSState ProcessKey(eKeys Key);
  38.587 +  };
  38.588 +
  38.589 +cMenuMPlayAid::cMenuMPlayAid(void)
  38.590 +:cOsdMenu(tr("MPlayer Audio ID"),20)
  38.591 +{
  38.592 +  Add(new cMenuEditIntItem(tr("Audiostream ID"),&MPlayerAid,-1,255));
  38.593 +  Display();
  38.594 +}
  38.595 +
  38.596 +eOSState cMenuMPlayAid::ProcessKey(eKeys Key)
  38.597 +{
  38.598 +  eOSState state=cOsdMenu::ProcessKey(Key);
  38.599 +  if(state==osUnknown) {
  38.600 +    switch(Key) {
  38.601 +      case kOk: state=osBack; break;
  38.602 +      default:  break;
  38.603 +      }
  38.604 +    }
  38.605 +  return state;
  38.606 +}
  38.607 +
  38.608 +// --- cMenuMPlayBrowse ---------------------------------------------------------
  38.609 +
  38.610 +class cMenuMPlayBrowse : public cMenuBrowse {
  38.611 +private:
  38.612 +  bool sourcing, aidedit;
  38.613 +  eOSState Source(bool second);
  38.614 +  eOSState Summary(void);
  38.615 +protected:
  38.616 +  virtual void SetButtons(void);
  38.617 +public:
  38.618 +  cMenuMPlayBrowse(void);
  38.619 +  virtual eOSState ProcessKey(eKeys Key);
  38.620 +  };
  38.621 +
  38.622 +static const char *excl_sum[] = { ".*","*.summary","*.txt","*.nfo",0 };
  38.623 +
  38.624 +cMenuMPlayBrowse::cMenuMPlayBrowse(void)
  38.625 +:cMenuBrowse(MPlaySources.GetSource(),false,false,tr("MPlayer browser"),excl_sum)
  38.626 +{
  38.627 +  sourcing=aidedit=false;
  38.628 +  SetButtons();
  38.629 +}
  38.630 +
  38.631 +void cMenuMPlayBrowse::SetButtons(void)
  38.632 +{
  38.633 +  static char blue[12];
  38.634 +  snprintf(blue,sizeof(blue),MPlayerAid>=0 ? "AID:%d" : "AID:def",MPlayerAid);
  38.635 +  SetHelp(tr(BUTTON"Play"), MPlayerSetup.ResumeMode ? tr(BUTTON"Rewind"):0, tr("Source"), blue);
  38.636 +  Display();
  38.637 +}
  38.638 +
  38.639 +eOSState cMenuMPlayBrowse::Source(bool second)
  38.640 +{
  38.641 +  if(HasSubMenu()) return osContinue;
  38.642 +
  38.643 +  if(!second) {
  38.644 +    sourcing=true;
  38.645 +    return AddSubMenu(new cMenuSource(&MPlaySources,tr("MPlayer source")));
  38.646 +    }
  38.647 +  sourcing=false;
  38.648 +  cFileSource *src=cMenuSource::GetSelected();
  38.649 +  if(src) {
  38.650 +    MPlaySources.SetSource(src);
  38.651 +    SetSource(src);
  38.652 +    NewDir(0);
  38.653 +    }
  38.654 +  return osContinue;
  38.655 +}
  38.656 +
  38.657 +eOSState cMenuMPlayBrowse::Summary(void)
  38.658 +{
  38.659 +  cFileObj *item=CurrentItem();
  38.660 +  if(item && item->Type()==otFile) {
  38.661 +    static const char *exts[] = { ".summary",".txt",".nfo",0 };
  38.662 +    for(int i=0; exts[i]; i++) {
  38.663 +      char buff[4096];
  38.664 +      strn0cpy(buff,item->FullPath(),sizeof(buff)-20);
  38.665 +      char *e=&buff[strlen(buff)];
  38.666 +      strn0cpy(e,exts[i],20);
  38.667 +      int fd=open(buff,O_RDONLY);
  38.668 +      *e=0;
  38.669 +      if(fd<0 && (e=rindex(buff,'.'))) {
  38.670 +        strn0cpy(e,exts[i],20);
  38.671 +        fd=open(buff,O_RDONLY);
  38.672 +        }
  38.673 +      if(fd>=0) {
  38.674 +        int r=read(fd,buff,sizeof(buff)-1);
  38.675 +        close(fd);
  38.676 +        if(r>0) {
  38.677 +          buff[r]=0;
  38.678 +          return AddSubMenu(new cMenuText(tr("Summary"),buff));
  38.679 +          }
  38.680 +        }
  38.681 +      }
  38.682 +    }
  38.683 +  return osContinue;
  38.684 +}
  38.685 +
  38.686 +eOSState cMenuMPlayBrowse::ProcessKey(eKeys Key)
  38.687 +{
  38.688 +  eOSState state=cOsdMenu::ProcessKey(Key);
  38.689 +  if(state==osContinue && !HasSubMenu()) {
  38.690 +    if(sourcing) return Source(true);
  38.691 +    if(aidedit) { aidedit=false; SetButtons(); }
  38.692 +    }
  38.693 +  bool rew=false;
  38.694 +  if(state==osUnknown) {
  38.695 +    switch(Key) {
  38.696 +      case kGreen:
  38.697 +        {
  38.698 +        cFileObj *item=CurrentItem();
  38.699 +        if(item && item->Type()==otFile) {
  38.700 +          lastselect=new cFileObj(item);
  38.701 +          state=osBack;
  38.702 +          rew=true;
  38.703 +          } 
  38.704 +        else state=osContinue;
  38.705 +        break;
  38.706 +        }
  38.707 +      case kYellow:
  38.708 +        state=Source(false);
  38.709 +        break;
  38.710 +      case kBlue:
  38.711 +        aidedit=true;
  38.712 +        state=AddSubMenu(new cMenuMPlayAid);
  38.713 +        break;
  38.714 +      case k0:
  38.715 +        state=Summary();
  38.716 +        break;
  38.717 +      default:
  38.718 +        break;
  38.719 +      }
  38.720 +    }
  38.721 +  if(state==osUnknown) state=cMenuBrowse::ProcessStdKey(Key,state);
  38.722 +  if(state==osBack && lastselect) {
  38.723 +    cMPlayerControl::SetFile(lastselect,rew);
  38.724 +    cControl::Launch(new cMPlayerControl);
  38.725 +    return osEnd;
  38.726 +    }
  38.727 +  return state;
  38.728 +}
  38.729 +
  38.730 +// --- cPluginMPlayer ----------------------------------------------------------
  38.731 +
  38.732 +static const char *VERSION        = PLUGIN_VERSION;
  38.733 +static const char *DESCRIPTION    = "Media replay via MPlayer";
  38.734 +static const char *MAINMENUENTRY  = "MPlayer";
  38.735 +
  38.736 +class cPluginMPlayer : public cPlugin {
  38.737 +private:
  38.738 +#if APIVERSNUM >= 10330
  38.739 +  bool ExternalPlay(const char *path, bool test);
  38.740 +#endif
  38.741 +public:
  38.742 +  cPluginMPlayer(void);
  38.743 +  virtual ~cPluginMPlayer();
  38.744 +  virtual const char *Version(void) { return VERSION; }
  38.745 +  virtual const char *Description(void) { return tr(DESCRIPTION); }
  38.746 +  virtual const char *CommandLineHelp(void);
  38.747 +  virtual bool ProcessArgs(int argc, char *argv[]);
  38.748 +#if APIVERSNUM >= 10131
  38.749 +  virtual bool Initialize(void);
  38.750 +#else
  38.751 +  virtual bool Start(void);
  38.752 +#endif
  38.753 +  virtual const char *MainMenuEntry(void);
  38.754 +  virtual cOsdMenu *MainMenuAction(void);
  38.755 +  virtual cMenuSetupPage *SetupMenu(void);
  38.756 +  virtual bool SetupParse(const char *Name, const char *Value);
  38.757 +#if APIVERSNUM >= 10330
  38.758 +  virtual bool Service(const char *Id, void *Data);
  38.759 +#if APIVERSNUM >= 10331
  38.760 +  virtual const char **SVDRPHelpPages(void);
  38.761 +  virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
  38.762 +#endif
  38.763 +#endif
  38.764 +  };
  38.765 +
  38.766 +cPluginMPlayer::cPluginMPlayer(void)
  38.767 +{
  38.768 +  // Initialize any member variables here.
  38.769 +  // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
  38.770 +  // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
  38.771 +  status=0;
  38.772 +}
  38.773 +
  38.774 +cPluginMPlayer::~cPluginMPlayer()
  38.775 +{
  38.776 +  delete status;
  38.777 +}
  38.778 +
  38.779 +const char *cPluginMPlayer::CommandLineHelp(void)
  38.780 +{
  38.781 +  static char *help_str=0;
  38.782 +  
  38.783 +  free(help_str);    //                                     for easier orientation, this is column 80|
  38.784 +  asprintf(&help_str,"  -m CMD,   --mount=CMD    use CMD to mount/unmount/eject mp3 sources\n"
  38.785 +                     "                           (default: %s)\n"
  38.786 +                     "  -M CMD,   --mplayer=CMD  use CMD when calling MPlayer\n"
  38.787 +                     "                           (default: %s)\n"
  38.788 +                     "  -S SUB,   --sources=SUB  search sources config in SUB subdirectory\n"
  38.789 +                     "                           (default: %s)\n"
  38.790 +                     "  -R DIR,   --resume=DIR   store global resume file in DIR\n"
  38.791 +                     "                           (default: %s)\n",
  38.792 +                     mountscript,
  38.793 +                     MPlayerCmd,
  38.794 +                     sourcesSub ? sourcesSub:"none",
  38.795 +                     globalResumeDir ? globalResumeDir:"video dir"
  38.796 +                     );
  38.797 +  return help_str;
  38.798 +}
  38.799 +
  38.800 +bool cPluginMPlayer::ProcessArgs(int argc, char *argv[])
  38.801 +{
  38.802 +  static struct option long_options[] = {
  38.803 +      { "mount",    required_argument, NULL, 'm' },
  38.804 +      { "mplayer",  required_argument, NULL, 'M' },
  38.805 +      { "sources",  required_argument, NULL, 'S' },
  38.806 +      { "resume",   required_argument, NULL, 'R' },
  38.807 +      { NULL }
  38.808 +    };
  38.809 +
  38.810 +  int c, option_index = 0;
  38.811 +  while((c=getopt_long(argc,argv,"m:M:S:R:",long_options,&option_index))!=-1) {
  38.812 +    switch (c) {
  38.813 +      case 'm': mountscript=optarg; break;
  38.814 +      case 'M': MPlayerCmd=optarg; break;
  38.815 +      case 'S': sourcesSub=optarg; break;
  38.816 +      case 'R': globalResumeDir=optarg; break;
  38.817 +      default:  return false;
  38.818 +      }
  38.819 +    }
  38.820 +  return true;
  38.821 +}
  38.822 +
  38.823 +#if APIVERSNUM >= 10131
  38.824 +bool cPluginMPlayer::Initialize(void)
  38.825 +#else
  38.826 +bool cPluginMPlayer::Start(void)
  38.827 +#endif
  38.828 +{
  38.829 +  if(!CheckVDRVersion(1,1,16,"mplayer")) return false;
  38.830 +  i18n_name=Name();
  38.831 +  MPlaySources.Load(AddDirectory(ConfigDirectory(sourcesSub),"mplayersources.conf"));
  38.832 +  if(MPlaySources.Count()<1) {
  38.833 +    esyslog("ERROR: you must have defined at least one source in mplayersources.conf");
  38.834 +    fprintf(stderr,"No source(s) defined in mplayersources.conf\n");
  38.835 +    return false;
  38.836 +    }
  38.837 +  RegisterI18n(Phrases);
  38.838 +  if(!(status=new cMPlayerStatus)) return false;
  38.839 +  return true;
  38.840 +}
  38.841 +
  38.842 +const char *cPluginMPlayer::MainMenuEntry(void)
  38.843 +{
  38.844 +  return MPlayerSetup.HideMainMenu ? 0 : tr(MAINMENUENTRY);
  38.845 +}
  38.846 +
  38.847 +cOsdMenu *cPluginMPlayer::MainMenuAction(void)
  38.848 +{
  38.849 +  return new cMenuMPlayBrowse;
  38.850 +}
  38.851 +
  38.852 +cMenuSetupPage *cPluginMPlayer::SetupMenu(void)
  38.853 +{
  38.854 +  return new cMenuSetupMPlayer;
  38.855 +}
  38.856 +
  38.857 +bool cPluginMPlayer::SetupParse(const char *Name, const char *Value)
  38.858 +{
  38.859 +  if(      !strcasecmp(Name, "ControlMode"))  MPlayerSetup.SlaveMode    = atoi(Value);
  38.860 +  else if (!strcasecmp(Name, "HideMainMenu")) MPlayerSetup.HideMainMenu = atoi(Value);
  38.861 +  else if (!strcasecmp(Name, "ResumeMode"))   MPlayerSetup.ResumeMode   = atoi(Value);
  38.862 +  else if (!strcasecmp(Name, "OsdPos"))       MPlayerSetup.OsdPos       = atoi(Value);
  38.863 +  else if (!strncasecmp(Name,"KeyCmd", 6) && strlen(Name)==7 && isdigit(Name[6]))
  38.864 +    strn0cpy(MPlayerSetup.KeyCmd[Name[6]-'0'],Value,sizeof(MPlayerSetup.KeyCmd[0]));
  38.865 +  else return false;
  38.866 +  return true;
  38.867 +}
  38.868 +
  38.869 +#if APIVERSNUM >= 10330
  38.870 +
  38.871 +bool cPluginMPlayer::ExternalPlay(const char *path, bool test)
  38.872 +{
  38.873 +  char real[PATH_MAX+1];
  38.874 +  if(realpath(path,real)) {
  38.875 +    cFileSource *src=MPlaySources.FindSource(real);
  38.876 +    if(src) {
  38.877 +      cFileObj *item=new cFileObj(src,0,0,otFile);
  38.878 +      if(item) {
  38.879 +        item->SplitAndSet(real);
  38.880 +        if(item->GuessType()) {
  38.881 +          if(item->Exists()) {
  38.882 +            if(!test) {
  38.883 +              cMPlayerControl::SetFile(item,true);
  38.884 +              cControl::Launch(new cMPlayerControl);
  38.885 +              cControl::Attach();
  38.886 +              }
  38.887 +            delete item;
  38.888 +            return true;
  38.889 +            }
  38.890 +          else dsyslog("MPlayer service: cannot play '%s'",path);
  38.891 +          }
  38.892 +        else dsyslog("MPlayer service: GuessType() failed for '%s'",path);
  38.893 +        delete item;
  38.894 +        }
  38.895 +      }
  38.896 +    else dsyslog("MPlayer service: cannot find source for '%s', real '%s'",path,real);
  38.897 +    }
  38.898 +  else if(errno!=ENOENT && errno!=ENOTDIR)
  38.899 +    esyslog("ERROR: realpath: %s: %s",path,strerror(errno));
  38.900 +  return false;
  38.901 +}
  38.902 +
  38.903 +bool cPluginMPlayer::Service(const char *Id, void *Data)
  38.904 +{
  38.905 +  if(!strcasecmp(Id,"MPlayer-Play-v1")) {
  38.906 +    if(Data) {
  38.907 +      struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
  38.908 +      msd->result=ExternalPlay(msd->data.filename,false);
  38.909 +      }
  38.910 +    return true;
  38.911 +    }
  38.912 +  else if(!strcasecmp(Id,"MPlayer-Test-v1")) {
  38.913 +    if(Data) {
  38.914 +      struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
  38.915 +      msd->result=ExternalPlay(msd->data.filename,true);
  38.916 +      }
  38.917 +    return true;
  38.918 +    }
  38.919 +  return false;
  38.920 +}
  38.921 +
  38.922 +#if APIVERSNUM >= 10331
  38.923 +
  38.924 +const char **cPluginMPlayer::SVDRPHelpPages(void)
  38.925 +{
  38.926 +  static const char *HelpPages[] = {
  38.927 +    "PLAY <filename>\n"
  38.928 +    "    Triggers playback of file 'filename'.",
  38.929 +    "TEST <filename>\n"
  38.930 +    "    Tests is playback of file 'filename' is possible.",
  38.931 +    NULL
  38.932 +    };
  38.933 +  return HelpPages;
  38.934 +}
  38.935 +
  38.936 +cString cPluginMPlayer::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
  38.937 +{
  38.938 +  if(!strcasecmp(Command,"PLAY")) {
  38.939 +    if(*Option) {
  38.940 +      if(ExternalPlay(Option,false)) return "Playback triggered";
  38.941 +      else { ReplyCode=550; return "Playback failed"; }
  38.942 +      }
  38.943 +    else { ReplyCode=501; return "Missing filename"; }
  38.944 +    }
  38.945 +  else if(!strcasecmp(Command,"TEST")) {
  38.946 +    if(*Option) {
  38.947 +      if(ExternalPlay(Option,true)) return "Playback possible";
  38.948 +      else { ReplyCode=550; return "Playback not possible"; }
  38.949 +      }
  38.950 +    else { ReplyCode=501; return "Missing filename"; }
  38.951 +    }
  38.952 +  return NULL;
  38.953 +}
  38.954 +
  38.955 +#endif // 1.3.31
  38.956 +#endif // 1.3.30
  38.957 +
  38.958 +VDRPLUGINCREATOR(cPluginMPlayer); // Don't touch this!
    39.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    39.2 +++ b/network.c	Sat Dec 29 14:47:40 2007 +0100
    39.3 @@ -0,0 +1,379 @@
    39.4 +/*
    39.5 + * MP3/MPlayer plugin to VDR (C++)
    39.6 + *
    39.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    39.8 + *
    39.9 + * This code is free software; you can redistribute it and/or
   39.10 + * modify it under the terms of the GNU General Public License
   39.11 + * as published by the Free Software Foundation; either version 2
   39.12 + * of the License, or (at your option) any later version.
   39.13 + *
   39.14 + * This code is distributed in the hope that it will be useful,
   39.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   39.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   39.17 + * GNU General Public License for more details.
   39.18 + *
   39.19 + * You should have received a copy of the GNU General Public License
   39.20 + * along with this program; if not, write to the Free Software
   39.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   39.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   39.23 + */
   39.24 +
   39.25 +#include <ctype.h>
   39.26 +#include <stdlib.h>
   39.27 +#include <stdio.h>
   39.28 +#include <stdarg.h>
   39.29 +#include <sys/types.h>
   39.30 +#include <unistd.h>
   39.31 +#include <string.h>
   39.32 +
   39.33 +#include <fcntl.h>
   39.34 +#include <sys/socket.h>
   39.35 +#include <netinet/in.h>
   39.36 +#include <arpa/inet.h>
   39.37 +#include <netdb.h>
   39.38 +
   39.39 +#include <vdr/tools.h>
   39.40 +
   39.41 +#include "common.h"
   39.42 +#include "setup-mp3.h"
   39.43 +#include "network.h"
   39.44 +
   39.45 +#define CON_TIMEOUT      30*1000   // default timeout (ms) for connect operation
   39.46 +#define RW_TIMEOUT       30*1000   // default timeout (ms) for read/write operations
   39.47 +#define BUFFERSIZE       128*1024  // default ringbuffer size (bytes) for async read
   39.48 +
   39.49 +#define NETDOWN_TIMEOUT  30        // timeout (s) for shutting down network
   39.50 +
   39.51 +const char *netscript=0;
   39.52 +
   39.53 +#if APIVERSNUM == 10131
   39.54 +#error Using this plugin with vdr 1.1.31 is not recommended (may cause high cpu load during streaming)
   39.55 +#endif
   39.56 +
   39.57 +// -----------------------------------------------------------------------------
   39.58 +
   39.59 +int RunCommand(const char *cmd, const char *State, const char *Name=0)
   39.60 +{
   39.61 +  int res=-1;
   39.62 +  if(cmd) {
   39.63 +    char *tmp=0;
   39.64 +    if(Name)
   39.65 +#if APIVERSNUM < 10318
   39.66 +      asprintf(&tmp,"%s %s \"%s\"",cmd,State,strescape(Name,"\"$"));
   39.67 +#else
   39.68 +      asprintf(&tmp,"%s %s \"%s\"",cmd,State,*strescape(Name,"\"$"));
   39.69 +#endif
   39.70 +    else asprintf(&tmp,"%s %s",cmd,State);
   39.71 +
   39.72 +    d(printf("run: executing '%s'\n",tmp))
   39.73 +    res=SystemExec(tmp);
   39.74 +    free(tmp);
   39.75 +    }
   39.76 +  return res;
   39.77 +}
   39.78 +
   39.79 +// -- cNetScript ---------------------------------------------------------------
   39.80 +
   39.81 +class cNetScript : public cThread {
   39.82 +private:
   39.83 +  int count;
   39.84 +  bool pending;
   39.85 +protected:
   39.86 +  virtual void Action(void);
   39.87 +public:
   39.88 +  cNetScript(void);
   39.89 +  ~cNetScript();
   39.90 +  void Up(void);
   39.91 +  void Down(void);
   39.92 +  };
   39.93 +
   39.94 +cNetScript ns;
   39.95 +
   39.96 +cNetScript::cNetScript(void)
   39.97 +{
   39.98 +  count=0; pending=false;
   39.99 +}
  39.100 +
  39.101 +cNetScript::~cNetScript()
  39.102 +{
  39.103 +  if(pending) Cancel(0);
  39.104 +}
  39.105 +
  39.106 +void cNetScript::Up(void)
  39.107 +{
  39.108 +  Lock();
  39.109 +  if(netscript) {
  39.110 +    if(pending) { Cancel(0); pending=false; }
  39.111 +    RunCommand(netscript,"up");
  39.112 +    count++;
  39.113 +    }
  39.114 +  Unlock();
  39.115 +}
  39.116 +
  39.117 +void cNetScript::Down(void)
  39.118 +{
  39.119 +  Lock();
  39.120 +  if(netscript) {
  39.121 +    if(--count==0) { Start(); pending=true; }
  39.122 +    }
  39.123 +  Unlock();
  39.124 +}
  39.125 +
  39.126 +void cNetScript::Action(void)
  39.127 +{
  39.128 +  d(printf("net: netscript down delay\n"))
  39.129 +  sleep(NETDOWN_TIMEOUT);
  39.130 +  Lock();
  39.131 +  RunCommand(netscript,"down");
  39.132 +  Unlock();
  39.133 +}
  39.134 +
  39.135 +// -- cNetConnect --------------------------------------------------------------
  39.136 +
  39.137 +class cNetConnect : public cThread {
  39.138 +private:
  39.139 +  int fd;
  39.140 +  const char *hostname;
  39.141 +  int port;
  39.142 +  cMutex conMutex;
  39.143 +  cCondVar conCond;
  39.144 +  int result;
  39.145 +protected:
  39.146 +  virtual void Action(void);
  39.147 +  void Done(int res);
  39.148 +public:
  39.149 +  cNetConnect(int Fd, const char *Hostname, int Port);
  39.150 +  ~cNetConnect();
  39.151 +  int Wait(int timeoutMs);
  39.152 +  };
  39.153 +
  39.154 +cNetConnect::cNetConnect(int Fd, const char *Hostname, int Port)
  39.155 +{
  39.156 +  fd=Fd;
  39.157 +  hostname=Hostname;
  39.158 +  port=Port;
  39.159 +  result=0;
  39.160 +  Start();
  39.161 +}
  39.162 +
  39.163 +cNetConnect::~cNetConnect()
  39.164 +{
  39.165 +  Cancel(1);
  39.166 +}
  39.167 +
  39.168 +int cNetConnect::Wait(int timeoutMs)
  39.169 +{
  39.170 +  conMutex.Lock();
  39.171 +  if(!result) conCond.TimedWait(conMutex,timeoutMs);
  39.172 +  conMutex.Unlock();
  39.173 +  return result;
  39.174 +}
  39.175 +
  39.176 +void cNetConnect::Done(int res)
  39.177 +{
  39.178 +  conMutex.Lock();
  39.179 +  result=res;
  39.180 +  conCond.Broadcast();
  39.181 +  conMutex.Unlock();
  39.182 +}
  39.183 +
  39.184 +void cNetConnect::Action(void)
  39.185 +{
  39.186 +  d(printf("net: name lookup %s\n",hostname))
  39.187 +  struct hostent *hp=gethostbyname(hostname);
  39.188 +  if(hp) {
  39.189 +    struct sockaddr_in sin;
  39.190 +    sin.sin_port=htons(port);
  39.191 +    sin.sin_family=AF_INET;
  39.192 +    memcpy((char *)&sin.sin_addr,hp->h_addr,hp->h_length);
  39.193 +    d(printf("net: connecting to %s:%d\n",hostname,port))
  39.194 +    if(connect(fd,(struct sockaddr *)&sin,sizeof(sin))==0) {
  39.195 +      d(printf("net: connected\n"))
  39.196 +      Done(1);
  39.197 +      }
  39.198 +    else { esyslog("connect() failed: %s",strerror(errno)); Done(-1); }
  39.199 +    }
  39.200 +  else { esyslog("Unknown host '%s'",hostname); Done(-1); }
  39.201 +}
  39.202 +
  39.203 +// -- cNet ---------------------------------------------------------------------
  39.204 +
  39.205 +cNet::cNet(int size, int ConTimeoutMs, int RwTimeoutMs)
  39.206 +:cRingBufferLinear(size>0?size:BUFFERSIZE,1,false)
  39.207 +{
  39.208 +  fd=-1; deferedErrno=0; count=0;
  39.209 +  connected=netup=false;
  39.210 +  rwTimeout =RwTimeoutMs  ? RwTimeoutMs :RW_TIMEOUT;
  39.211 +  conTimeout=ConTimeoutMs ? ConTimeoutMs:CON_TIMEOUT;
  39.212 +#if APIVERSNUM >= 10132
  39.213 +  SetTimeouts(50,50);
  39.214 +#endif
  39.215 +}
  39.216 +
  39.217 +cNet::~cNet()
  39.218 +{
  39.219 +  Disconnect();
  39.220 +}
  39.221 +
  39.222 +void cNet::Close(void)
  39.223 +{
  39.224 +  if(connected) {
  39.225 +    connected=false;
  39.226 +    Cancel(2);
  39.227 +    deferedErrno=0;
  39.228 +    }
  39.229 +  if(fd>=0) { close(fd); fd=-1; }
  39.230 +  Clear(); count=0;
  39.231 +}
  39.232 +
  39.233 +void cNet::Disconnect(void)
  39.234 +{
  39.235 +  Close();
  39.236 +  if(netup) { ns.Down(); netup=false; }
  39.237 +}
  39.238 +
  39.239 +bool cNet::Connect(const char *hostname, const int port)
  39.240 +{
  39.241 +  Close();
  39.242 +  fd=socket(AF_INET,SOCK_STREAM,0);
  39.243 +  if(fd>=0) {
  39.244 +    ns.Up(); netup=true;
  39.245 +    cNetConnect *con=new cNetConnect(fd,hostname,port);
  39.246 +    int res=con->Wait(conTimeout);
  39.247 +    delete con;
  39.248 +    if(res>0) {
  39.249 +      if(fcntl(fd,F_SETFL,O_NONBLOCK)>=0) {
  39.250 +        deferedErrno=0; connected=true;
  39.251 +        Start();
  39.252 +        return(true);
  39.253 +        }
  39.254 +      else esyslog("fnctl() failed: %s",strerror(errno)); 
  39.255 +      }
  39.256 +    else if(res==0) esyslog("Connection timed out"); 
  39.257 +    }
  39.258 +  else esyslog("socket() failed: %s",strerror(errno)); 
  39.259 +  Disconnect();
  39.260 +  return false;
  39.261 +}
  39.262 +
  39.263 +void cNet::CopyFromBuff(unsigned char *dest, int n)
  39.264 +{
  39.265 +  memcpy(dest,lineBuff,n);
  39.266 +  count-=n;
  39.267 +  if(count>0) memmove(lineBuff,lineBuff+n,count);
  39.268 +}
  39.269 +
  39.270 +int cNet::Gets(char *dest, int len)
  39.271 +{
  39.272 +  len--; // let room for trailing zero
  39.273 +  int c=0;
  39.274 +  while(c<len) {
  39.275 +    if(count<=0) {
  39.276 +      int r=RingRead(lineBuff,sizeof(lineBuff));
  39.277 +      if(r<0) {
  39.278 +        if(c==0) return -1;
  39.279 +        break;
  39.280 +        }
  39.281 +      count=r;
  39.282 +      }
  39.283 +    int n=0;
  39.284 +    while(n<count && n+c<len) {
  39.285 +      if(lineBuff[n]=='\n') len=0;
  39.286 +      n++;
  39.287 +      }
  39.288 +    CopyFromBuff((unsigned char *)dest,n);
  39.289 +    dest+=n; c+=n;
  39.290 +    }
  39.291 +  *dest=0;
  39.292 +  return c;
  39.293 +}
  39.294 +
  39.295 +int cNet::Read(unsigned char *dest, int len)
  39.296 +{
  39.297 +  int c=0;
  39.298 +  if(count>0) {
  39.299 +    c=count; if(c>len) c=len;
  39.300 +    CopyFromBuff(dest,c);
  39.301 +    }
  39.302 +  else {
  39.303 +    c=RingRead(dest,len);
  39.304 +    }
  39.305 +  return c;
  39.306 +}
  39.307 +
  39.308 +int cNet::Write(unsigned char *dest, int len)
  39.309 +{
  39.310 +  int t=0, r;
  39.311 +  cPoller poll(fd,true);
  39.312 +  do {
  39.313 +    if(poll.Poll(rwTimeout)) {
  39.314 +      r=write(fd,dest,len);
  39.315 +      if(r<0 && errno!=EAGAIN) {
  39.316 +        esyslog("write() failed: %s",strerror(errno));
  39.317 +        break;
  39.318 +        }
  39.319 +      dest+=r; len-=r; t+=r;
  39.320 +      }
  39.321 +    else { esyslog("Write timed out"); break; }
  39.322 +    } while(len>0);
  39.323 +  return t;
  39.324 +}
  39.325 +
  39.326 +int cNet::Puts(char *dest)
  39.327 +{
  39.328 +  return Write((unsigned char *)dest,strlen(dest));
  39.329 +}
  39.330 +
  39.331 +int cNet::RingRead(unsigned char *dest, int len)
  39.332 +{
  39.333 +  int r=0;
  39.334 +  const uchar *rd;
  39.335 +  for(;;) {
  39.336 +    if(!Available() && deferedErrno) {
  39.337 +      d(printf("net: ringbuffer empty, async read bailed out\n"))
  39.338 +      return -1;
  39.339 +      }
  39.340 +    rd=Get(r);
  39.341 +    if(rd && r>0) {
  39.342 +      if(r>len) r=len;
  39.343 +      memcpy(dest,rd,r);
  39.344 +      Del(r);
  39.345 +      return r;
  39.346 +      }
  39.347 +    }
  39.348 +}
  39.349 +
  39.350 +void cNet::Action(void)
  39.351 +{
  39.352 +  d(printf("net: async read started\n"))
  39.353 +
  39.354 +  cPoller poll(fd,false);
  39.355 +  while(connected) {
  39.356 +    if(poll.Poll(rwTimeout)) {
  39.357 +      unsigned char buff[8192];
  39.358 +      int r=read(fd,buff,sizeof(buff));
  39.359 +      if(r>0) {
  39.360 +        int d=0;
  39.361 +        do { d+=Put(buff+d,r-d); } while(d<r && connected);
  39.362 +        }
  39.363 +      else if(r<0 && errno!=EAGAIN) {
  39.364 +        deferedErrno=errno;
  39.365 +        esyslog("read() failed: %s",strerror(errno));
  39.366 +        break;
  39.367 +        }
  39.368 +      else if(r==0) {
  39.369 +        deferedErrno=-1;
  39.370 +        d(printf("EOF from read()\n"))
  39.371 +        break;
  39.372 +        }
  39.373 +      }
  39.374 +    else {
  39.375 +      deferedErrno=-1;
  39.376 +      esyslog("Read timed out");
  39.377 +      break;
  39.378 +      }
  39.379 +    }
  39.380 +  EnableGet();
  39.381 +  connected=false;
  39.382 +}
    40.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    40.2 +++ b/network.h	Sat Dec 29 14:47:40 2007 +0100
    40.3 @@ -0,0 +1,58 @@
    40.4 +/*
    40.5 + * MP3/MPlayer plugin to VDR (C++)
    40.6 + *
    40.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    40.8 + *
    40.9 + * This code is free software; you can redistribute it and/or
   40.10 + * modify it under the terms of the GNU General Public License
   40.11 + * as published by the Free Software Foundation; either version 2
   40.12 + * of the License, or (at your option) any later version.
   40.13 + *
   40.14 + * This code is distributed in the hope that it will be useful,
   40.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   40.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   40.17 + * GNU General Public License for more details.
   40.18 + *
   40.19 + * You should have received a copy of the GNU General Public License
   40.20 + * along with this program; if not, write to the Free Software
   40.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   40.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   40.23 + */
   40.24 +
   40.25 +#ifndef ___NETWORK_H
   40.26 +#define ___NETWORK_H
   40.27 +
   40.28 +#include <vdr/thread.h>
   40.29 +#include <vdr/ringbuffer.h>
   40.30 +
   40.31 +class cRingBufferLinear;
   40.32 +
   40.33 +// ----------------------------------------------------------------
   40.34 +
   40.35 +class cNet : public cRingBufferLinear, cThread {
   40.36 +private:
   40.37 +  int fd;
   40.38 +  bool connected, netup;
   40.39 +  int deferedErrno;
   40.40 +  int rwTimeout, conTimeout;
   40.41 +  unsigned char lineBuff[4096];
   40.42 +  int count;
   40.43 +  //
   40.44 +  void Close(void);
   40.45 +  int RingRead(unsigned char *dest, int len);
   40.46 +  void CopyFromBuff(unsigned char *dest, int n);
   40.47 +protected:
   40.48 +  virtual void Action(void);
   40.49 +public:
   40.50 +  cNet(int size, int ConTimeoutMs, int RwTimeoutMs);
   40.51 +  ~cNet();
   40.52 +  bool Connect(const char *hostname, const int port);
   40.53 +  void Disconnect(void);
   40.54 +  bool Connected(void) { return connected; }
   40.55 +  int Gets(char *dest, int len);
   40.56 +  int Puts(char *dest);
   40.57 +  int Read(unsigned char *dest, int len);
   40.58 +  int Write(unsigned char *dest, int len);
   40.59 +  };
   40.60 +
   40.61 +#endif //___NETWORK_H
    41.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    41.2 +++ b/patches/cdfs-0.5c-discid-1.diff	Sat Dec 29 14:47:40 2007 +0100
    41.3 @@ -0,0 +1,25 @@
    41.4 +--- discid.c.orig	Thu Dec 27 19:48:57 2001
    41.5 ++++ discid.c	Mon Feb 23 20:18:44 2004
    41.6 +@@ -5,14 +5,18 @@
    41.7 + 
    41.8 + 
    41.9 + unsigned long discid(cd * this_cd) {
   41.10 +-  unsigned int i=0, t, n = 0;
   41.11 ++  unsigned int i=0, t, n = 0, trks=0, l=0;
   41.12 + 
   41.13 +   for (i=0; i< this_cd->tracks; i++)
   41.14 +-    n += cddb_sum((this_cd->track[T2I(i)].start_lba+CD_MSF_OFFSET)/CD_FRAMES);
   41.15 ++    if(this_cd->track[T2I(i)].type==AUDIO) {
   41.16 ++      n += cddb_sum((this_cd->track[T2I(i)].start_lba+CD_MSF_OFFSET)/CD_FRAMES);
   41.17 ++      trks++; l=i;
   41.18 ++      }
   41.19 + 
   41.20 +-  t = this_cd->track[T2I(this_cd->tracks-1)].stop_lba/CD_FRAMES;
   41.21 ++  t = (this_cd->track[T2I(l+1)].start_lba-
   41.22 ++       this_cd->track[T2I(0)].start_lba)/CD_FRAMES;
   41.23 + 
   41.24 +-  return (((n % 0xFF) << 24) | (t << 8) | this_cd->tracks);
   41.25 ++  return (((n % 0xFF) << 24) | (t << 8) | trks);
   41.26 + }
   41.27 + 
   41.28 + 
    42.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    42.2 +++ b/patches/cdfs-0.5c-discid-2.diff	Sat Dec 29 14:47:40 2007 +0100
    42.3 @@ -0,0 +1,38 @@
    42.4 +--- discid.c.orig	2001-12-27 19:48:57.000000000 +0100
    42.5 ++++ discid.c	2004-02-23 20:18:45.000000000 +0100
    42.6 +@@ -5,14 +5,18 @@
    42.7 + 
    42.8 + 
    42.9 + unsigned long discid(cd * this_cd) {
   42.10 +-  unsigned int i=0, t, n = 0;
   42.11 ++  unsigned int i=0, t, n = 0, trks=0, l=0;
   42.12 + 
   42.13 +   for (i=0; i< this_cd->tracks; i++)
   42.14 +-    n += cddb_sum((this_cd->track[T2I(i)].start_lba+CD_MSF_OFFSET)/CD_FRAMES);
   42.15 ++    if(this_cd->track[T2I(i)].type==AUDIO) {
   42.16 ++      n += cddb_sum((this_cd->track[T2I(i)].start_lba+CD_MSF_OFFSET)/CD_FRAMES);
   42.17 ++      trks++; l=i;
   42.18 ++      }
   42.19 + 
   42.20 +-  t = this_cd->track[T2I(this_cd->tracks-1)].stop_lba/CD_FRAMES;
   42.21 ++  t = (this_cd->track[T2I(l+1)].start_lba-
   42.22 ++       this_cd->track[T2I(0)].start_lba)/CD_FRAMES;
   42.23 + 
   42.24 +-  return (((n % 0xFF) << 24) | (t << 8) | this_cd->tracks);
   42.25 ++  return (((n % 0xFF) << 24) | (t << 8) | trks);
   42.26 + }
   42.27 + 
   42.28 + 
   42.29 +--- root.c.orig	2002-04-07 18:31:39.000000000 +0200
   42.30 ++++ root.c	2005-02-05 17:27:48.000000000 +0100
   42.31 +@@ -146,6 +146,10 @@
   42.32 +         this_cd->track[i].type        = AUDIO;
   42.33 +         this_cd->track[i].time        = CURRENT_TIME;
   42.34 +         this_cd->track[i].iso_size    = 0;
   42.35 ++        if ((t!=(this_cd->tracks - 1)) && t > 0 && (this_cd->track[i + 1].type!=AUDIO)) {
   42.36 ++          this_cd->track[i].track_size = this_cd->track[i].track_size - 11400;
   42.37 ++          this_cd->track[i].stop_lba = this_cd->track[i].stop_lba -11400;
   42.38 ++        }
   42.39 +         this_cd->track[i].track_size  = this_cd->track[i].track_size * CD_FRAMESIZE_RAW + ((this_cd->raw_audio==0)?WAV_HEADER_SIZE:0);
   42.40 +         this_cd->track[i].size        = this_cd->track[i].track_size;
   42.41 + 	this_cd->track[i].avi         = 0;
    43.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    43.2 +++ b/patches/mplayer-0.90rc1-head.diff	Sat Dec 29 14:47:40 2007 +0100
    43.3 @@ -0,0 +1,51 @@
    43.4 +diff -ruN ./libao2/ao_mpegpes.c /usr/src/MPlayer-0.90rc1/libao2/ao_mpegpes.c
    43.5 +--- ./libao2/ao_mpegpes.c	Sun Oct  6 03:08:04 2002
    43.6 ++++ /usr/src/MPlayer-0.90rc1/libao2/ao_mpegpes.c	Wed Dec 11 15:47:20 2002
    43.7 +@@ -15,7 +15,7 @@
    43.8 + #include "../mp_msg.h"
    43.9 + 
   43.10 + #ifdef HAVE_DVB
   43.11 +-#include <ost/audio.h>
   43.12 +-audioMixer_t dvb_mixer={255,255};
   43.13 ++#include <linux/dvb/audio.h>
   43.14 ++audio_mixer_t dvb_mixer={255,255};
   43.15 + #endif
   43.16 + extern int vo_mpegpes_fd;
   43.17 +diff -ruN ./libvo/vo_mpegpes.c /usr/src/MPlayer-0.90rc1/libvo/vo_mpegpes.c
   43.18 +--- ./libvo/vo_mpegpes.c	Mon Nov 11 16:20:26 2002
   43.19 ++++ /usr/src/MPlayer-0.90rc1/libvo/vo_mpegpes.c	Wed Dec 11 15:54:49 2002
   43.20 +@@ -35,12 +35,14 @@
   43.21 + #include <stdio.h>
   43.22 + #include <time.h>
   43.23 + 
   43.24 +-#include <ost/dmx.h>
   43.25 +-#include <ost/frontend.h>
   43.26 +-#include <ost/sec.h>
   43.27 +-#include <ost/video.h>
   43.28 +-#include <ost/audio.h>
   43.29 +-
   43.30 ++#include <linux/dvb/video.h>
   43.31 ++#include <linux/dvb/audio.h>
   43.32 ++#ifndef true
   43.33 ++#define true 1
   43.34 ++#endif
   43.35 ++#ifndef false
   43.36 ++#define false 0
   43.37 ++#endif
   43.38 + #endif
   43.39 + 
   43.40 + #include "config.h"
   43.41 +@@ -86,11 +88,11 @@
   43.42 + #ifdef HAVE_DVB
   43.43 +     if(!arg){
   43.44 +     //|O_NONBLOCK
   43.45 +-	if((vo_mpegpes_fd = open("/dev/ost/video",O_RDWR)) < 0){
   43.46 ++	if((vo_mpegpes_fd = open("/dev/dvb/adapter0/video0",O_RDWR)) < 0){
   43.47 + 		perror("DVB VIDEO DEVICE: ");
   43.48 + 		return -1;
   43.49 + 	}
   43.50 +-	if((vo_mpegpes_fd2 = open("/dev/ost/audio",O_RDWR|O_NONBLOCK)) < 0){
   43.51 ++	if((vo_mpegpes_fd2 = open("/dev/dvb/adapter0/audio0",O_RDWR|O_NONBLOCK)) < 0){
   43.52 + 		perror("DVB AUDIO DEVICE: ");
   43.53 + 		return -1;
   43.54 + 	}
    44.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    44.2 +++ b/patches/mplayer-0.90rc1-slavemode.diff	Sat Dec 29 14:47:40 2007 +0100
    44.3 @@ -0,0 +1,29 @@
    44.4 +diff -ruN ./mplayer.c /usr/src/MPlayer-0.90rc1/mplayer.c
    44.5 +--- ./mplayer.c	Thu Dec  5 01:18:56 2002
    44.6 ++++ /usr/src/MPlayer-0.90rc1/mplayer.c	Wed Dec 11 15:49:50 2002
    44.7 +@@ -2682,6 +2682,25 @@
    44.8 +   current_module=NULL;
    44.9 + }
   44.10 + 
   44.11 ++#if 1
   44.12 ++  if(slave_mode){
   44.13 ++    float position=0.0;
   44.14 ++    float time=0.0;
   44.15 ++    if(demuxer->file_format==DEMUXER_TYPE_AVI && sh_video->video.dwLength>2) {
   44.16 ++      // get pos from frame number / total frames
   44.17 ++      position=(float)d_video->pack_no*100.0/(float)sh_video->video.dwLength;
   44.18 ++      }
   44.19 ++    else {
   44.20 ++      off_t len = ( demuxer->movi_end - demuxer->movi_start );
   44.21 ++      off_t pos = ( demuxer->file_format == DEMUXER_TYPE_AUDIO?stream->pos:demuxer->filepos );
   44.22 ++      if(len>0) position=( pos - demuxer->movi_start ) * 100.0 / len;
   44.23 ++      }
   44.24 ++    if(sh_video) time=d_video->pts;
   44.25 ++    else if(sh_audio) time=sh_audio->delay;
   44.26 ++    mp_msg(MSGT_OSD,MSGL_ERR,"SLAVE: time=%.2f position=%.2f\r",time,position);
   44.27 ++    }
   44.28 ++#endif
   44.29 ++
   44.30 + #ifdef HAVE_NEW_GUI
   44.31 +       if(use_gui){
   44.32 +         guiEventHandling();
    45.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    45.2 +++ b/patches/mplayer-0.90rc5-slavemode.diff	Sat Dec 29 14:47:40 2007 +0100
    45.3 @@ -0,0 +1,28 @@
    45.4 +--- mplayer.c.orig	2003-03-10 15:02:18.000000000 +0100
    45.5 ++++ mplayer.c	2003-03-29 14:58:17.000000000 +0100
    45.6 +@@ -3114,6 +3114,25 @@
    45.7 +   loop_seek=0;
    45.8 + }
    45.9 + 
   45.10 ++#if 1
   45.11 ++  if(slave_mode){
   45.12 ++    float position=0.0;
   45.13 ++    float time=0.0;
   45.14 ++    if(demuxer->file_format==DEMUXER_TYPE_AVI && sh_video->video.dwLength>2) {
   45.15 ++      // get pos from frame number / total frames
   45.16 ++      position=(float)d_video->pack_no*100.0/(float)sh_video->video.dwLength;
   45.17 ++      }
   45.18 ++    else {
   45.19 ++      off_t len = ( demuxer->movi_end - demuxer->movi_start );
   45.20 ++      off_t pos = ( demuxer->file_format == DEMUXER_TYPE_AUDIO?stream->pos:demuxer->filepos );
   45.21 ++      if(len>0) position=( pos - demuxer->movi_start ) * 100.0 / len;
   45.22 ++      }
   45.23 ++    if(sh_video) time=d_video->pts;
   45.24 ++    else if(sh_audio) time=sh_audio->delay;
   45.25 ++    mp_msg(MSGT_OSD,MSGL_ERR,"SLAVE: time=%.2f position=%.2f\r",time,position);
   45.26 ++    }
   45.27 ++#endif
   45.28 ++
   45.29 + #ifdef HAVE_NEW_GUI
   45.30 +       if(use_gui){
   45.31 +         guiEventHandling();
    46.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    46.2 +++ b/patches/mplayer-1.0cvs20070302-slavemode.diff	Sat Dec 29 14:47:40 2007 +0100
    46.3 @@ -0,0 +1,28 @@
    46.4 +--- mplayer/mplayer.c.orig	2007-03-02 13:20:05.000000000 -0800
    46.5 ++++ mplayer/mplayer.c	2007-03-02 13:20:39.000000000 -0800
    46.6 +@@ -3549,6 +3549,25 @@
    46.7 +   edl_decision = 0;
    46.8 + }
    46.9 + 
   46.10 ++#if 1
   46.11 ++  if(slave_mode){
   46.12 ++    float position=0.0;
   46.13 ++    float time=0.0;
   46.14 ++    if(mpctx->demuxer->file_format==DEMUXER_TYPE_AVI && mpctx->sh_video->video.dwLength>2) {
   46.15 ++      // get pos from frame number / total frames
   46.16 ++      position=(float)mpctx->d_video->pack_no*100.0/(float)mpctx->sh_video->video.dwLength;
   46.17 ++      }
   46.18 ++    else {
   46.19 ++      off_t len = ( mpctx->demuxer->movi_end - mpctx->demuxer->movi_start );
   46.20 ++      off_t pos = ( mpctx->demuxer->file_format == DEMUXER_TYPE_AUDIO?mpctx->stream->pos:mpctx->demuxer->filepos );
   46.21 ++      if(len>0) position=( pos - mpctx->demuxer->movi_start ) * 100.0 / len;
   46.22 ++      }
   46.23 ++    if(mpctx->sh_video) time=mpctx->d_video->pts;
   46.24 ++    else if(mpctx->sh_audio) time=mpctx->sh_audio->delay;
   46.25 ++    mp_msg(MSGT_OSD,MSGL_ERR,"SLAVE: time=%.2f position=%.2f\r",time,position);
   46.26 ++    }
   46.27 ++#endif
   46.28 ++
   46.29 + #ifdef HAVE_NEW_GUI
   46.30 +       if(use_gui){
   46.31 +         guiEventHandling();
    47.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    47.2 +++ b/patches/mplayer-1.0pre2-slavemode.diff	Sat Dec 29 14:47:40 2007 +0100
    47.3 @@ -0,0 +1,28 @@
    47.4 +--- mplayer.c.orig	2003-10-04 03:24:50.000000000 +0200
    47.5 ++++ mplayer.c	2003-11-01 16:17:37.000000000 +0100
    47.6 +@@ -3444,6 +3444,25 @@
    47.7 +   loop_seek=0;
    47.8 + }
    47.9 + 
   47.10 ++#if 1
   47.11 ++  if(slave_mode){
   47.12 ++    float position=0.0;
   47.13 ++    float time=0.0;
   47.14 ++    if(demuxer->file_format==DEMUXER_TYPE_AVI && sh_video->video.dwLength>2) {
   47.15 ++      // get pos from frame number / total frames
   47.16 ++      position=(float)d_video->pack_no*100.0/(float)sh_video->video.dwLength;
   47.17 ++      }
   47.18 ++    else {
   47.19 ++      off_t len = ( demuxer->movi_end - demuxer->movi_start );
   47.20 ++      off_t pos = ( demuxer->file_format == DEMUXER_TYPE_AUDIO?stream->pos:demuxer->filepos );
   47.21 ++      if(len>0) position=( pos - demuxer->movi_start ) * 100.0 / len;
   47.22 ++      }
   47.23 ++    if(sh_video) time=d_video->pts;
   47.24 ++    else if(sh_audio) time=sh_audio->delay;
   47.25 ++    mp_msg(MSGT_OSD,MSGL_ERR,"SLAVE: time=%.2f position=%.2f\r",time,position);
   47.26 ++    }
   47.27 ++#endif
   47.28 ++
   47.29 + #ifdef HAVE_NEW_GUI
   47.30 +       if(use_gui){
   47.31 +         guiEventHandling();
    48.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    48.2 +++ b/patches/vdr-1.3.10-nostop.diff	Sat Dec 29 14:47:40 2007 +0100
    48.3 @@ -0,0 +1,14 @@
    48.4 +--- vdr-1.3.10-orig/dvbdevice.c	2004-06-06 13:28:28.000000000 +0200
    48.5 ++++ vdr-1.3.10/dvbdevice.c	2004-09-06 17:32:09.000000000 +0200
    48.6 +@@ -856,10 +890,9 @@
    48.7 +          CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
    48.8 +          break;
    48.9 +     case pmAudioVideo:
   48.10 ++    case pmAudioOnlyBlack:
   48.11 +          if (playMode == pmNone)
   48.12 +             TurnOffLiveMode();
   48.13 +-         // continue with next...
   48.14 +-    case pmAudioOnlyBlack:
   48.15 +          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
   48.16 +          CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
   48.17 +          CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, PlayMode == pmAudioVideo));
    49.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    49.2 +++ b/player-mp3-sample.c	Sat Dec 29 14:47:40 2007 +0100
    49.3 @@ -0,0 +1,151 @@
    49.4 +/*
    49.5 + * MP3/MPlayer plugin to VDR (C++)
    49.6 + *
    49.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    49.8 + *
    49.9 + * This code is free software; you can redistribute it and/or
   49.10 + * modify it under the terms of the GNU General Public License
   49.11 + * as published by the Free Software Foundation; either version 2
   49.12 + * of the License, or (at your option) any later version.
   49.13 + *
   49.14 + * This code is distributed in the hope that it will be useful,
   49.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   49.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   49.17 + * GNU General Public License for more details.
   49.18 + *
   49.19 + * You should have received a copy of the GNU General Public License
   49.20 + * along with this program; if not, write to the Free Software
   49.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   49.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   49.23 + */
   49.24 +
   49.25 +static const unsigned char testAudio[] = {
   49.26 +  0x00,0x00,0x01,0xc0,0x07,0xfa,0x87,0x80,0x05,0x2f,0x46,0x05,0x80,0xcf,0xff,0xfc,
   49.27 +  0xa4,0x00,0xd2,0x6c,0x36,0x22,0x12,0x11,0x11,0x21,0x11,0x11,0x11,0x43,0x33,0xd5,
   49.28 +  0xa4,0x6b,0xac,0x92,0x49,0x24,0x90,0x00,0x00,0x00,0x12,0x32,0x23,0x23,0x22,0x22,
   49.29 +  0x22,0x22,0x32,0x23,0x35,0x51,0x54,0xe3,0x1c,0x71,0xa6,0x1c,0x75,0xc7,0x99,0x75,
   49.30 +  0xf7,0xda,0x71,0xd7,0x5c,0x65,0xd6,0xd9,0x71,0x97,0x1c,0x75,0xc7,0x5b,0x65,0xc6,
   49.31 +  0xdc,0x6d,0xb5,0xd9,0x5d,0x95,0xd7,0x61,0x84,0x13,0x51,0x36,0x1b,0x69,0xb6,0x5b,
   49.32 +  0x6d,0xd4,0xd3,0x5d,0x75,0x55,0x4d,0x76,0x9c,0x75,0xc7,0xdf,0x71,0xe6,0xdb,0x75,
   49.33 +  0xd7,0x5c,0x79,0xc6,0xdd,0x75,0xd7,0xdd,0x82,0xf2,0xf4,0x60,0xcc,0xdb,0x95,0x96,
   49.34 +  0x16,0xd5,0x4d,0xb5,0xac,0xd3,0x49,0x8e,0xbd,0xb1,0x6e,0x65,0x34,0xaa,0x69,0x40,
   49.35 +  0x42,0x9a,0x5c,0x54,0x69,0xde,0x36,0x52,0xd1,0x88,0x89,0xdb,0x08,0x31,0xb5,0xac,
   49.36 +  0x5e,0x6e,0xe7,0xb5,0x05,0x53,0x82,0x45,0xcb,0x54,0xeb,0xbd,0xb5,0x0b,0x98,0xc9,
   49.37 +  0xaa,0xdb,0x12,0x65,0x41,0x1a,0x87,0xda,0xd4,0xa4,0xac,0x4d,0x45,0x51,0xe9,0x38,
   49.38 +  0x88,0xe6,0x41,0xaa,0x6b,0x46,0xfc,0xed,0xcb,0x7a,0xda,0x0c,0xb4,0x94,0x42,0x44,
   49.39 +  0x45,0x2f,0x69,0x1a,0xf6,0x49,0x86,0xb4,0x49,0x88,0x93,0x68,0xf8,0x85,0xd0,0x21,
   49.40 +  0x8d,0x14,0xb2,0xea,0xa5,0xc3,0x71,0x34,0xc4,0x6e,0x2d,0x08,0x24,0xd0,0xcd,0x21,
   49.41 +  0xe3,0x92,0x19,0x6d,0x54,0xb2,0x12,0xcd,0x64,0xc4,0xb2,0x6d,0x20,0x06,0x62,0x12,
   49.42 +  0x11,0x16,0x96,0xd5,0x22,0x6e,0x8b,0xdc,0xba,0x95,0xe3,0x70,0xf1,0x23,0xcd,0x67,
   49.43 +  0x06,0x85,0xb2,0x71,0xd9,0x55,0x8a,0x54,0xbb,0x37,0x98,0x63,0x60,0x6a,0x9a,0xc6,
   49.44 +  0x00,0x5d,0x5f,0x0a,0x8a,0xc8,0x90,0xd2,0x4c,0x4c,0x81,0x44,0x00,0x90,0x01,0x82,
   49.45 +  0x45,0x25,0x9d,0xe5,0xb6,0xa5,0x66,0x0d,0x65,0x6b,0x02,0x98,0x82,0x95,0x09,0x2e,
   49.46 +  0x59,0xc8,0x46,0xa6,0xd6,0x32,0x29,0xdb,0x0a,0x3a,0xca,0x17,0x09,0x4a,0x34,0xd1,
   49.47 +  0xa3,0x04,0x78,0xb2,0x57,0xa3,0x84,0x85,0x31,0x05,0x51,0x92,0x0d,0x83,0x5c,0x18,
   49.48 +  0xb9,0xce,0x02,0x53,0x08,0xd7,0xb6,0x5c,0x4d,0x71,0x3d,0x8d,0x6d,0xc6,0xb5,0xa5,
   49.49 +  0x99,0xda,0x8c,0xa6,0xe3,0xb5,0xb2,0x37,0xac,0xf5,0x65,0xae,0xe1,0x52,0x3b,0x05,
   49.50 +  0x63,0xc4,0xa9,0x86,0xd6,0xb6,0x09,0x52,0x9a,0xac,0xa9,0x0f,0x1d,0x2d,0x43,0x20,
   49.51 +  0xa5,0x6b,0x54,0xbb,0x96,0xbc,0x58,0xf9,0x4d,0x88,0x71,0xbd,0x1b,0xb0,0x93,0x1d,
   49.52 +  0x9c,0xd0,0xc1,0x70,0xd2,0xc5,0x42,0x89,0x88,0xdb,0x66,0x0d,0x93,0x42,0xaa,0xd8,
   49.53 +  0x85,0x13,0xa0,0x34,0x96,0x4d,0x6a,0xd4,0xe7,0x1b,0x22,0x16,0x45,0x34,0xe4,0x6b,
   49.54 +  0x8f,0x1c,0xdb,0x4c,0xcf,0xaa,0xb5,0x36,0x0e,0xeb,0xc1,0xc8,0xeb,0x51,0x1e,0x0c,
   49.55 +  0x99,0x51,0x2c,0xd6,0xa9,0x56,0xba,0xba,0x94,0x20,0x2c,0x4a,0xdd,0x53,0x12,0x2c,
   49.56 +  0x6b,0x55,0x41,0xa3,0x6a,0xb2,0xd8,0x86,0xcb,0x2c,0x6e,0x26,0x8e,0x7a,0x6a,0x1b,
   49.57 +  0x35,0x35,0xa0,0xa8,0xa3,0xa6,0x35,0x16,0x67,0x35,0xb0,0x5d,0x33,0x15,0x11,0x1a,
   49.58 +  0x38,0xa3,0x95,0xa7,0x06,0x51,0x61,0x6a,0x88,0x20,0x54,0xa5,0xce,0xd3,0xed,0xb2,
   49.59 +  0xa7,0x1b,0x63,0x4c,0xee,0xa4,0x66,0xa8,0x5a,0x4e,0x49,0x91,0xd2,0x38,0x69,0xef,
   49.60 +  0x68,0x48,0xea,0x34,0x40,0x28,0x70,0xf1,0xf6,0x9c,0x80,0x05,0x28,0x5a,0xad,0x0c,
   49.61 +  0x4b,0x3a,0x0f,0x4c,0xe3,0x49,0x6a,0x69,0x47,0x7e,0x12,0xa1,0x73,0x91,0x5e,0xb7,
   49.62 +  0x4e,0xeb,0x93,0x29,0x59,0x08,0xac,0xd8,0x35,0xaa,0x6b,0x80,0x00,0x00,0xff,0xfc,
   49.63 +  0xa4,0x00,0x17,0xc7,0x46,0x22,0x12,0x11,0x11,0x11,0x11,0x11,0x21,0x43,0x42,0xad,
   49.64 +  0xa4,0x63,0xac,0x92,0x49,0x24,0x90,0x00,0x00,0x00,0xbb,0xbb,0x88,0x88,0x88,0x00,
   49.65 +  0x00,0x00,0x00,0x00,0x04,0x50,0x29,0xb6,0x5a,0x75,0xd6,0x9e,0x79,0xb7,0x5b,0x7d,
   49.66 +  0xc6,0xdc,0x71,0xd7,0x5e,0x7a,0x07,0x5c,0x71,0xd6,0x9d,0x71,0xc5,0xd9,0x65,0xa5,
   49.67 +  0x9b,0x61,0xa7,0x1a,0x55,0x45,0x97,0x61,0x86,0x5b,0x5d,0xd6,0xdb,0x6d,0xc7,0x1e,
   49.68 +  0x79,0xd5,0x54,0x61,0x86,0x5c,0x55,0x55,0x58,0x69,0xa7,0x9d,0x7e,0x08,0x1f,0x8e,
   49.69 +  0x08,0xa0,0x86,0x07,0xe1,0x89,0xf8,0xe3,0x79,0xf8,0x1f,0x86,0x18,0xde,0x86,0x18,
   49.70 +  0x60,0x74,0x22,0x14,0x61,0x16,0xcb,0x4d,0xb9,0x4a,0xd6,0xd6,0x6b,0x8a,0xe2,0x35,
   49.71 +  0xc4,0x9d,0x0b,0x18,0x28,0x33,0x60,0x71,0x4d,0xa7,0x18,0x0c,0xa9,0xc2,0x9e,0xc6,
   49.72 +  0x42,0xf8,0x11,0xa5,0x70,0x1b,0xa8,0x5b,0x7a,0x5d,0x3e,0xf3,0x26,0xa7,0x67,0x33,
   49.73 +  0x40,0x74,0x11,0xae,0xce,0x6c,0xfa,0x6b,0x63,0x6d,0x58,0x3c,0xab,0xd5,0x32,0x93,
   49.74 +  0x2b,0xb8,0x45,0x2a,0x9c,0x59,0x8d,0x0a,0xa6,0xdc,0x32,0x11,0x81,0x37,0xbb,0x68,
   49.75 +  0xb7,0x0f,0x37,0xa2,0xb3,0x26,0xb6,0x33,0x08,0x62,0x29,0xad,0xaf,0x34,0x6d,0xb4,
   49.76 +  0x5e,0x9e,0x44,0xa5,0x25,0x52,0x19,0xf9,0xa8,0x13,0x21,0xb6,0x31,0xe6,0xa0,0xc3,
   49.77 +  0x54,0xfc,0xb2,0xd4,0x9c,0xa4,0x54,0x9b,0x9b,0x6e,0xb6,0x32,0xa2,0x3e,0x0c,0x7b,
   49.78 +  0x5a,0x52,0x8e,0xb0,0xb4,0x01,0x44,0x5c,0xd0,0xb2,0x4f,0x37,0x65,0xb2,0xd6,0x96,
   49.79 +  0xeb,0x3c,0xb6,0xdb,0x78,0x65,0x8e,0x64,0xc4,0xe8,0xd2,0x70,0x23,0x4d,0x4a,0x24,
   49.80 +  0x94,0xe2,0xc3,0x5a,0x66,0xca,0xe4,0x1a,0x26,0xc8,0xac,0x41,0x2b,0x3b,0x6e,0x2d,
   49.81 +  0xdd,0x03,0x73,0x9e,0x6b,0xaa,0xac,0x78,0xce,0x36,0x74,0xd7,0x68,0x34,0xad,0x54,
   49.82 +  0x21,0x0a,0x76,0xb1,0x96,0x9d,0x4d,0x05,0x11,0x4f,0x1d,0xe1,0x55,0x40,0x2f,0xd8,
   49.83 +  0x5f,0xa4,0xaa,0x03,0xd1,0x00,0x91,0x6d,0xbe,0x72,0xa6,0xae,0x0b,0x43,0xd4,0x65,
   49.84 +  0xae,0xad,0x69,0xd2,0x9b,0x5a,0x55,0xc6,0xbe,0x1c,0x95,0x2e,0x46,0xc9,0x64,0xc5,
   49.85 +  0x28,0x2c,0x56,0x86,0x47,0x53,0x14,0x89,0x33,0x35,0xca,0xc5,0xcc,0xbd,0x24,0xb3,
   49.86 +  0x4a,0x58,0xaa,0x29,0x64,0x62,0x23,0x77,0x5a,0xb9,0x83,0x9d,0x8b,0xdb,0x9f,0x6e,
   49.87 +  0x72,0xc8,0xd9,0xda,0x8e,0x75,0x5e,0xf6,0xc1,0xae,0x1b,0xb9,0xaa,0xe9,0xb7,0x97,
   49.88 +  0x0c,0x9d,0xc5,0x81,0x27,0x86,0x01,0x55,0xc0,0xca,0x3b,0x31,0xa5,0x58,0x42,0x1b,
   49.89 +  0x15,0x41,0xac,0xa1,0xa7,0x15,0x97,0x22,0x9b,0x32,0xb0,0x35,0x11,0xd6,0x5d,0x0d,
   49.90 +  0x65,0x39,0x10,0x84,0xdc,0x1f,0x76,0xee,0xdc,0x60,0x90,0x04,0x40,0x77,0xba,0xa1,
   49.91 +  0x1d,0x28,0xd4,0xad,0x69,0x2b,0x40,0x17,0x31,0x4b,0x9d,0x1a,0x24,0x11,0x1b,0x60,
   49.92 +  0xa4,0x21,0x68,0x26,0x4a,0x3b,0xf2,0x81,0x30,0xb4,0xb9,0xc4,0x84,0x25,0x86,0x37,
   49.93 +  0x24,0x48,0x2f,0x48,0x93,0x70,0xd2,0x5b,0xca,0x84,0xe8,0xd6,0x56,0x8e,0x54,0x83,
   49.94 +  0xc4,0x7c,0xfe,0x29,0x2b,0xb3,0x4c,0xa5,0x1b,0x60,0x72,0x4f,0x4d,0x56,0x47,0x6e,
   49.95 +  0xc8,0x36,0x9f,0x4b,0xa5,0x23,0xad,0x72,0x56,0xa2,0x2d,0x25,0xa2,0x51,0x46,0x13,
   49.96 +  0x9d,0xb2,0xd6,0x55,0x86,0x31,0x5e,0x39,0x76,0xd6,0x2f,0x16,0x33,0x69,0x90,0x8e,
   49.97 +  0x69,0x02,0x23,0x91,0x63,0x8f,0x13,0x1b,0x11,0xec,0xae,0x6d,0x46,0xf6,0x5b,0x1e,
   49.98 +  0xdc,0x6f,0x0d,0x51,0x2d,0x68,0x36,0x2d,0x21,0x2e,0xd0,0x00,0x00,0x00,0xff,0xfc,
   49.99 +  0xa4,0x00,0x61,0x12,0x47,0x22,0x12,0x12,0x11,0x11,0x11,0x22,0x23,0x42,0x31,0x8d,
  49.100 +  0x92,0x5a,0x88,0x92,0x49,0x24,0x90,0x00,0x00,0x00,0xa5,0xab,0x00,0x00,0x00,0x00,
  49.101 +  0x00,0x00,0x00,0x00,0x04,0x4a,0x71,0xa7,0x19,0x6d,0x97,0x9a,0x72,0x07,0xdb,0x71,
  49.102 +  0xf7,0x1c,0x7d,0xd6,0xde,0x76,0x08,0x1a,0x7a,0x07,0xdd,0x69,0xa6,0xdc,0x69,0xf7,
  49.103 +  0x18,0x6d,0xc5,0xd7,0x65,0x86,0x9b,0x6d,0x96,0x5a,0x71,0xc7,0x57,0x55,0x76,0x9a,
  49.104 +  0x65,0xa6,0x19,0x6d,0xc7,0x1b,0x79,0xa7,0x5f,0x71,0x76,0x18,0x65,0xa7,0x19,0x5d,
  49.105 +  0x86,0xdd,0x6d,0xf8,0x60,0x86,0x18,0x62,0x91,0xe8,0xa2,0x86,0x18,0xde,0x8a,0x38,
  49.106 +  0x61,0x91,0xa8,0xa5,0x76,0x28,0xdd,0x92,0x67,0xd0,0x74,0xbe,0xbe,0x3e,0xa6,0xee,
  49.107 +  0xad,0x52,0x2a,0xb5,0x22,0xe6,0xd5,0xf1,0x15,0x23,0x12,0x18,0x2e,0x4a,0x6a,0x5b,
  49.108 +  0x49,0xc5,0x20,0x56,0xab,0x05,0xcd,0xdd,0x26,0x86,0xaa,0x8a,0x1a,0xa5,0x5d,0xcf,
  49.109 +  0x93,0xe3,0xc3,0x69,0x8a,0x46,0xd2,0x96,0x2e,0x1c,0x84,0x6d,0x2d,0xa5,0xec,0x95,
  49.110 +  0x18,0xcc,0x95,0xd6,0xc6,0x45,0x37,0x1a,0x6b,0x5b,0x0b,0x43,0x27,0x52,0x63,0x1f,
  49.111 +  0x64,0x92,0x64,0x12,0xe9,0x38,0xb3,0x2b,0x28,0xb8,0xa4,0x0d,0xb1,0x28,0xe3,0xa1,
  49.112 +  0x26,0x29,0x29,0xa5,0x39,0x04,0x14,0x4c,0x9c,0x64,0x3d,0x02,0x8e,0x26,0x17,0x36,
  49.113 +  0xb8,0x90,0xa2,0xd2,0x95,0x8c,0xd3,0x94,0x44,0x86,0x2c,0x3a,0x61,0xe1,0x6d,0x82,
  49.114 +  0x41,0x17,0x15,0x9e,0x59,0x79,0xb3,0x72,0x99,0xca,0x88,0xdb,0x8d,0x5a,0xac,0x93,
  49.115 +  0x56,0x43,0x00,0x2b,0x5d,0x0c,0x41,0x52,0xbd,0xad,0x5a,0xd0,0x05,0xae,0x73,0x4a,
  49.116 +  0x60,0x8d,0x88,0x86,0x54,0xad,0x2d,0x49,0x10,0x61,0x43,0x08,0x69,0x11,0x22,0x3d,
  49.117 +  0x10,0xb5,0x94,0x9e,0xcd,0x5e,0x8d,0x5a,0xa8,0xd9,0x8e,0x48,0x69,0x91,0x01,0x06,
  49.118 +  0x2d,0x65,0x40,0xd4,0xb4,0x44,0x28,0xb0,0xf1,0x13,0x49,0x36,0xd4,0x53,0x5c,0xb7,
  49.119 +  0x57,0x6b,0x9a,0xfd,0x35,0x72,0x9a,0x8e,0x6a,0xa8,0xa4,0xd1,0x48,0xc9,0x39,0x27,
  49.120 +  0x50,0x1c,0xda,0x53,0xd5,0xac,0xc8,0xe6,0x6a,0x6b,0x93,0x18,0x91,0x16,0x1b,0xc7,
  49.121 +  0x52,0x6c,0x8d,0x9d,0x30,0x8a,0x52,0x0e,0x0f,0x92,0xb0,0x6a,0xd8,0x57,0x23,0x70,
  49.122 +  0x5b,0x23,0xb1,0xb6,0xae,0x72,0x15,0xe9,0x23,0x68,0x3d,0xcc,0x6b,0x54,0xd5,0x96,
  49.123 +  0x31,0x8a,0x22,0x7a,0xd9,0x55,0x9a,0x47,0x49,0xcf,0x46,0x35,0x9e,0xa6,0x68,0xd7,
  49.124 +  0x6b,0x24,0x47,0x85,0x52,0xa6,0xd4,0xdd,0x0a,0x0b,0x1e,0xea,0x57,0x02,0xeb,0x6b,
  49.125 +  0x9c,0x46,0xa0,0x87,0x63,0xe5,0x6a,0xb3,0x3a,0x3e,0x26,0xcb,0x89,0x51,0x2a,0x9b,
  49.126 +  0x94,0xe6,0x30,0xd8,0xae,0xd3,0x6a,0x3c,0x52,0x31,0xad,0x54,0x98,0x56,0xb2,0xb0,
  49.127 +  0x0a,0x98,0x29,0x94,0x1c,0xe5,0xb5,0x6c,0x1a,0xd6,0xb6,0x73,0xc4,0x14,0x6c,0xb9,
  49.128 +  0x91,0x98,0xd6,0xda,0xa7,0xb8,0xa8,0x2b,0x83,0x35,0xad,0x88,0xea,0x4c,0x95,0xc0,
  49.129 +  0x77,0xf5,0xe4,0xe2,0x43,0x5c,0xa5,0xf0,0xba,0x56,0x21,0x1a,0xcb,0x35,0xad,0x6b,
  49.130 +  0x71,0xac,0xcf,0xd5,0x55,0xbc,0x9e,0x36,0x42,0xcb,0x23,0x48,0xa5,0x44,0xb8,0xf1,
  49.131 +  0xbe,0x8e,0x23,0x50,0xd1,0x04,0x4b,0x63,0x0d,0x36,0xb5,0xa1,0xba,0xa4,0xd1,0x4c,
  49.132 +  0xb4,0x8c,0x6b,0x5a,0xd6,0xb7,0x17,0xc5,0x55,0x9d,0xac,0x90,0x9a,0x44,0xc8,0xd5,
  49.133 +  0x65,0xd4,0xbe,0x70,0xf9,0x37,0x78,0xf2,0x0e,0x06,0x24,0xea,0xba,0x6a,0x83,0x52,
  49.134 +  0x94,0x92,0x4a,0x6d,0xa0,0xbe,0xb1,0x82,0xa2,0xb8,0x42,0x00,0x00,0x00,0xff,0xfc,
  49.135 +  0xa4,0x00,0x63,0x29,0x45,0x23,0x22,0x34,0x33,0x33,0x23,0x23,0x33,0x23,0x23,0x6d,
  49.136 +  0x22,0x51,0x45,0x22,0x49,0x24,0x90,0x00,0x00,0x00,0xaa,0x7e,0xbb,0x6a,0xaa,0xea,
  49.137 +  0xaa,0xeb,0xaa,0xae,0x23,0x4a,0x4d,0x25,0x18,0x49,0x44,0x95,0x4d,0x44,0x95,0x55,
  49.138 +  0x25,0xd5,0x69,0x55,0x55,0x59,0x45,0x93,0x61,0x55,0x94,0x55,0x45,0x99,0x61,0xb5,
  49.139 +  0x57,0x5d,0x76,0x56,0x5d,0x75,0xd9,0x61,0xa5,0xd9,0x69,0x66,0xd7,0x73,0xeb,0x6d,
  49.140 +  0xfa,0x55,0x25,0xa0,0xb6,0xdb,0x61,0xab,0x54,0x06,0x99,0x49,0xb6,0x9a,0x36,0x65,
  49.141 +  0x25,0x6c,0x8d,0xd2,0x6d,0xb8,0x69,0x44,0x5b,0x70,0xdf,0x39,0xa2,0x5a,0xa9,0xb5,
  49.142 +  0xeb,0xdd,0xa4,0x23,0x2a,0x72,0x63,0x32,0xc7,0x4e,0xdc,0x56,0xb2,0xb4,0x46,0x39,
  49.143 +  0x29,0xaa,0x9d,0xc1,0x8c,0xdd,0xd9,0x32,0x73,0x26,0x70,0x35,0x24,0x3a,0x83,0x36,
  49.144 +  0x9d,0x89,0xc7,0xaf,0x9c,0xd9,0x56,0xcf,0x33,0xe2,0xdb,0x77,0x38,0xed,0x53,0x4e,
  49.145 +  0x41,0x71,0xde,0x0a,0x15,0x83,0x4c,0x09,0x59,0x86,0xea,0x99,0xb7,0x35,0x7a,0x43,
  49.146 +  0x68,0xb9,0x32,0x16,0xe4,0xc8,0xd9,0x5c,0x1d,0xa5,0x24,0x53,0xfa,0xed,0x65,0x96,
  49.147 +  0xba,0x2b,0xea,0x2e,0x2a,0x96,0xb7,0x22,0x70,0xa6,0x62,0x8e,0x28,0x1a,0xaa,0xd0,
  49.148 +  0x9b,0x6e,0x24,0x43,0x08,0x9b,0x8d,0xc7,0x27,0xb3,0x61,0xbb,0x8c,0x4e,0x29,0x89,
  49.149 +  0x24,0xf9,0x39,0x95,0xac,0x58,0xc5,0x39,0xfb,0x52,0xad,0x42,0xe6,0xba,0x0f,0x6a,
  49.150 +  0xda,0xec,0x30,0xd6,0x67,0xb0,0x71,0x99,0xed,0xd2,0xc2,0xa2,0x6c,0xe2,0x75,0x02,
  49.151 +  0xcb,0x48,0x1d,0x37,0x1d,0x82,0x26,0xd5,0x87,0x1d,0xb2,0xe9,0x8d,0xd7,0x73,0xc8,
  49.152 +  0x48,0x99,0xd3,0x1b,0x12,0x24,0x20,0xe7,0x46,0x2c,0x69,0x92,0x7e,0xf7,0xb5,0xd8,
  49.153 +  0xb3,0x29,0x36,0x6c,0xcc,0xcc,0x65,0xb7,0x0c,0x2f,0x14,0x72,0x28,0x4e,0x12,0xe2,
  49.154 +  };
    50.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    50.2 +++ b/player-mp3.c	Sat Dec 29 14:47:40 2007 +0100
    50.3 @@ -0,0 +1,1979 @@
    50.4 +/*
    50.5 + * MP3/MPlayer plugin to VDR (C++)
    50.6 + *
    50.7 + * (C) 2001-2007 Stefan Huelswitt <s.huelswitt@gmx.de>
    50.8 + *
    50.9 + * This code is free software; you can redistribute it and/or
   50.10 + * modify it under the terms of the GNU General Public License
   50.11 + * as published by the Free Software Foundation; either version 2
   50.12 + * of the License, or (at your option) any later version.
   50.13 + *
   50.14 + * This code is distributed in the hope that it will be useful,
   50.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   50.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   50.17 + * GNU General Public License for more details.
   50.18 + *
   50.19 + * You should have received a copy of the GNU General Public License
   50.20 + * along with this program; if not, write to the Free Software
   50.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   50.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   50.23 + */
   50.24 +
   50.25 +#include <stdlib.h>
   50.26 +#include <stdio.h>
   50.27 +#include <sys/ioctl.h>
   50.28 +#include <math.h>
   50.29 +#ifdef WITH_OSS
   50.30 +#include <sys/soundcard.h>
   50.31 +#endif
   50.32 +
   50.33 +#include <mad.h>
   50.34 +#include <id3tag.h>
   50.35 +
   50.36 +#include <vdr/player.h>
   50.37 +#include <vdr/ringbuffer.h>
   50.38 +#include <vdr/thread.h>
   50.39 +#include <vdr/tools.h>
   50.40 +
   50.41 +#include "common.h"
   50.42 +#include "setup-mp3.h"
   50.43 +#include "player-mp3.h"
   50.44 +#include "data-mp3.h"
   50.45 +#include "decoder.h"
   50.46 +#include "decoder-core.h"
   50.47 +
   50.48 +#ifndef NO_DEBUG
   50.49 +//#define DEBUG_MODE      // debug playmode changes
   50.50 +#define DEBUG_BGR       // debug backround scan thread
   50.51 +#define DEBUG_DELAY 300 // debug write/decode delays
   50.52 +//#define ACC_DUMP        // dump limiter lookup table to /tmp/limiter
   50.53 +#endif
   50.54 +
   50.55 +#if !defined(NO_DEBUG) && defined(DEBUG_MODE)
   50.56 +#define dm(x) { (x); }
   50.57 +#else
   50.58 +#define dm(x) ; 
   50.59 +#endif
   50.60 +
   50.61 +#if !defined(NO_DEBUG) && defined(DEBUG_BGR)
   50.62 +#define db(x) { (x); }
   50.63 +#else
   50.64 +#define db(x) ; 
   50.65 +#endif
   50.66 +
   50.67 +// ----------------------------------------------------------------
   50.68 +
   50.69 +#define MP3BUFSIZE (1024*1024)                               // output ringbuffer size
   50.70 +#define OUT_BITS 16                                          // output 16 bit samples to DVB driver
   50.71 +#define OUT_FACT (OUT_BITS/8*2)                              // output factor is 16 bit & 2 channels -> 4 bytes
   50.72 +// cResample
   50.73 +#define MAX_NSAMPLES (1152*7)                                // max. buffer for resampled frame
   50.74 +// cNormalize
   50.75 +#define MIN_GAIN   0.03                                      // min. gain required to launch the normalizer
   50.76 +#define MAX_GAIN   3.0                                       // max. allowed gain
   50.77 +#define USE_FAST_LIMITER
   50.78 +#define LIM_ACC    12                                        // bit, accuracy for lookup table
   50.79 +#define F_LIM_MAX  (mad_fixed_t)((1<<(MAD_F_FRACBITS+2))-1)  // max. value covered by lookup table
   50.80 +#define LIM_SHIFT  (MAD_F_FRACBITS-LIM_ACC)                  // shift value for table lookup
   50.81 +#define F_LIM_JMP  (mad_fixed_t)(1<<LIM_SHIFT)               // lookup table jump between values
   50.82 +// cLevel
   50.83 +#define POW_WIN 100                                          // window width for smoothing power values
   50.84 +#define EPSILON 0.00000000001                                // anything less than EPSILON is considered zero
   50.85 +
   50.86 +// --- cResample ------------------------------------------------------------
   50.87 +
   50.88 +// The resample code has been adapted from the madplay project
   50.89 +// (resample.c) found in the libmad distribution
   50.90 +   
   50.91 +class cResample {
   50.92 +private:
   50.93 +  mad_fixed_t ratio;
   50.94 +  mad_fixed_t step;
   50.95 +  mad_fixed_t last;
   50.96 +  mad_fixed_t resampled[MAX_NSAMPLES];
   50.97 +public:
   50.98 +  bool SetInputRate(unsigned int oldrate, unsigned int newrate);
   50.99 +  unsigned int ResampleBlock(unsigned int nsamples, const mad_fixed_t *old);
  50.100 +  const mad_fixed_t *Resampled(void) { return resampled; }
  50.101 +  };
  50.102 +
  50.103 +bool cResample::SetInputRate(unsigned int oldrate, unsigned int newrate)
  50.104 +{
  50.105 +  if(oldrate<8000 || oldrate>newrate*6) { // out of range
  50.106 +    esyslog("WARNING: samplerate %d out of range 8000-%d\n",oldrate,newrate*6);
  50.107 +    return 0;
  50.108 +    }
  50.109 +  ratio=mad_f_tofixed((double)oldrate/(double)newrate);
  50.110 +  step=0; last=0;
  50.111 +#ifdef DEBUG
  50.112 +  static mad_fixed_t oldratio=0;
  50.113 +  if(oldratio!=ratio) {
  50.114 +    printf("mad: new resample ratio %f (from %d kHz to %d kHz)\n",mad_f_todouble(ratio),oldrate,newrate);
  50.115 +    oldratio=ratio;
  50.116 +    }
  50.117 +#endif
  50.118 +  return ratio!=MAD_F_ONE;
  50.119 +}
  50.120 +
  50.121 +unsigned int cResample::ResampleBlock(unsigned int nsamples, const mad_fixed_t *old)
  50.122 +{
  50.123 +  // This resampling algorithm is based on a linear interpolation, which is
  50.124 +  // not at all the best sounding but is relatively fast and efficient.
  50.125 +  //
  50.126 +  // A better algorithm would be one that implements a bandlimited
  50.127 +  // interpolation.
  50.128 +
  50.129 +  mad_fixed_t *nsam=resampled;
  50.130 +  const mad_fixed_t *end=old+nsamples;
  50.131 +  const mad_fixed_t *begin=nsam;
  50.132 +
  50.133 +  if(step < 0) {
  50.134 +    step = mad_f_fracpart(-step);
  50.135 +
  50.136 +    while (step < MAD_F_ONE) {
  50.137 +      *nsam++ = step ? last+mad_f_mul(*old-last,step) : last;
  50.138 +      step += ratio;
  50.139 +      if(((step + 0x00000080L) & 0x0fffff00L) == 0)
  50.140 +	step = (step + 0x00000080L) & ~0x0fffffffL;
  50.141 +      }
  50.142 +    step -= MAD_F_ONE;
  50.143 +    }
  50.144 +
  50.145 +  while (end - old > 1 + mad_f_intpart(step)) {
  50.146 +    old += mad_f_intpart(step);
  50.147 +    step = mad_f_fracpart(step);
  50.148 +    *nsam++ = step ? *old + mad_f_mul(old[1] - old[0], step) : *old;
  50.149 +    step += ratio;
  50.150 +    if (((step + 0x00000080L) & 0x0fffff00L) == 0)
  50.151 +      step = (step + 0x00000080L) & ~0x0fffffffL;
  50.152 +    }
  50.153 +
  50.154 +  if (end - old == 1 + mad_f_intpart(step)) {
  50.155 +    last = end[-1];
  50.156 +    step = -step;
  50.157 +    }
  50.158 +  else step -= mad_f_fromint(end - old);
  50.159 +
  50.160 +  return nsam-begin;
  50.161 +}
  50.162 +
  50.163 +// --- cLevel ----------------------------------------------------------------
  50.164 +
  50.165 +// The normalize algorithm and parts of the code has been adapted from the
  50.166 +// Normalize 0.7 project. (C) 1999-2002, Chris Vaill <cvaill@cs.columbia.edu>
  50.167 +
  50.168 +// A little background on how normalize computes the volume
  50.169 +// of a wav file, in case you want to know just how your
  50.170 +// files are being munged:
  50.171 +//
  50.172 +// The volumes calculated are RMS amplitudes, which corre­
  50.173 +// spond (roughly) to perceived volume. Taking the RMS ampli­
  50.174 +// tude of an entire file would not give us quite the measure
  50.175 +// we want, though, because a quiet song punctuated by short
  50.176 +// loud parts would average out to a quiet song, and the
  50.177 +// adjustment we would compute would make the loud parts
  50.178 +// excessively loud.
  50.179 +//
  50.180 +// What we want is to consider the maximum volume of the
  50.181 +// file, and normalize according to that. We break up the
  50.182 +// signal into 100 chunks per second, and get the signal
  50.183 +// power of each chunk, in order to get an estimation of
  50.184 +// "instantaneous power" over time. This "instantaneous
  50.185 +// power" signal varies too much to get a good measure of the
  50.186 +// original signal's maximum sustained power, so we run a
  50.187 +// smoothing algorithm over the power signal (specifically, a
  50.188 +// mean filter with a window width of 100 elements). The max­
  50.189 +// imum point of the smoothed power signal turns out to be a
  50.190 +// good measure of the maximum sustained power of the file.
  50.191 +// We can then take the square root of the power to get maxi­
  50.192 +// mum sustained RMS amplitude.
  50.193 +
  50.194 +class cLevel {
  50.195 +private:
  50.196 +  double maxpow;
  50.197 +  mad_fixed_t peak;
  50.198 +  struct Power {
  50.199 +    // smooth
  50.200 +    int npow, wpow;
  50.201 +    double powsum, pows[POW_WIN];
  50.202 +    // sum
  50.203 +    unsigned int nsum;
  50.204 +    double sum;
  50.205 +    } power[2];
  50.206 +  //
  50.207 +  inline void AddPower(struct Power *p, double pow);
  50.208 +public:
  50.209 +  void Init(void);
  50.210 +  void GetPower(struct mad_pcm *pcm);
  50.211 +  double GetLevel(void);
  50.212 +  double GetPeak(void);
  50.213 +  };
  50.214 +
  50.215 +void cLevel::Init(void)
  50.216 +{
  50.217 +  for(int l=0 ; l<2 ; l++) {
  50.218 +    struct Power *p=&power[l];
  50.219 +    p->sum=p->powsum=0.0; p->wpow=p->npow=p->nsum=0;
  50.220 +    for(int i=POW_WIN-1 ; i>=0 ; i--) p->pows[i]=0.0;
  50.221 +    }
  50.222 +  maxpow=0.0; peak=0;
  50.223 +}
  50.224 +
  50.225 +void cLevel::GetPower(struct mad_pcm *pcm)
  50.226 +{
  50.227 +  for(int i=0 ; i<pcm->channels ; i++) {
  50.228 +    struct Power *p=&power[i];
  50.229 +    mad_fixed_t *data=pcm->samples[i];
  50.230 +    for(int n=pcm->length ; n>0 ; n--) {
  50.231 +      if(*data < -peak) peak = -*data;
  50.232 +      if(*data >  peak) peak =  *data;
  50.233 +      double s=mad_f_todouble(*data++);
  50.234 +      p->sum+=(s*s);
  50.235 +      if(++(p->nsum)>=pcm->samplerate/100) {
  50.236 +        AddPower(p,p->sum/(double)p->nsum);
  50.237 +        p->sum=0.0; p->nsum=0;
  50.238 +        }
  50.239 +      }
  50.240 +    }
  50.241 +}
  50.242 +
  50.243 +void cLevel::AddPower(struct Power *p, double pow)
  50.244 +{
  50.245 +  p->powsum+=pow;
  50.246 +  if(p->npow>=POW_WIN) {
  50.247 +    if(p->powsum>maxpow) maxpow=p->powsum;
  50.248 +    p->powsum-=p->pows[p->wpow];
  50.249 +    }
  50.250 +  else p->npow++;
  50.251 +  p->pows[p->wpow]=pow;
  50.252 +  p->wpow=(p->wpow+1) % POW_WIN;
  50.253 +}
  50.254 +
  50.255 +double cLevel::GetLevel(void)
  50.256 +{
  50.257 +  if(maxpow<EPSILON) {
  50.258 +    // Either this whole file has zero power, or was too short to ever
  50.259 +    // fill the smoothing buffer.  In the latter case, we need to just
  50.260 +    // get maxpow from whatever data we did collect.
  50.261 +
  50.262 +    if(power[0].powsum>maxpow) maxpow=power[0].powsum;
  50.263 +    if(power[1].powsum>maxpow) maxpow=power[1].powsum;
  50.264 +    }
  50.265 +  double level=sqrt(maxpow/(double)POW_WIN);     // adjust for the smoothing window size and root
  50.266 +  d(printf("norm: new volumen level=%f peak=%f\n",level,mad_f_todouble(peak)))
  50.267 +  return level;
  50.268 +}
  50.269 +
  50.270 +double cLevel::GetPeak(void)
  50.271 +{
  50.272 +  return mad_f_todouble(peak);
  50.273 +}
  50.274 +
  50.275 +// --- cNormalize ------------------------------------------------------------
  50.276 +
  50.277 +class cNormalize {
  50.278 +private:
  50.279 +  mad_fixed_t gain;
  50.280 +  double d_limlvl, one_limlvl;
  50.281 +  mad_fixed_t limlvl;
  50.282 +  bool dogain, dolimit;
  50.283 +#ifdef DEBUG
  50.284 +  // stats
  50.285 +  unsigned long limited, clipped, total;
  50.286 +  mad_fixed_t peak;
  50.287 +#endif
  50.288 +  // limiter
  50.289 +#ifdef USE_FAST_LIMITER
  50.290 +  mad_fixed_t *table, tablestart;
  50.291 +  int tablesize;
  50.292 +  inline mad_fixed_t FastLimiter(mad_fixed_t x);
  50.293 +#endif
  50.294 +  inline mad_fixed_t Limiter(mad_fixed_t x);
  50.295 +public:
  50.296 +  cNormalize(void);
  50.297 +  ~cNormalize();
  50.298 +  void Init(double Level, double Peak);
  50.299 +  void Stats(void);
  50.300 +  void AddGain(struct mad_pcm *pcm);
  50.301 +  };
  50.302 +
  50.303 +cNormalize::cNormalize(void)
  50.304 +{
  50.305 +  d_limlvl=(double)MP3Setup.LimiterLevel/100.0;
  50.306 +  one_limlvl=1-d_limlvl;
  50.307 +  limlvl=mad_f_tofixed(d_limlvl);
  50.308 +  d(printf("norm: lim_lev=%f lim_acc=%d\n",d_limlvl,LIM_ACC))
  50.309 +
  50.310 +#ifdef USE_FAST_LIMITER
  50.311 +  mad_fixed_t start=limlvl & ~(F_LIM_JMP-1);
  50.312 +  tablestart=start;
  50.313 +  tablesize=(unsigned int)(F_LIM_MAX-start)/F_LIM_JMP + 2;
  50.314 +  table=new mad_fixed_t[tablesize];
  50.315 +  if(table) {
  50.316 +    d(printf("norm: table size=%d start=%08x jump=%08x\n",tablesize,start,F_LIM_JMP))
  50.317 +    for(int i=0 ; i<tablesize ; i++) {
  50.318 +      table[i]=Limiter(start);
  50.319 +      start+=F_LIM_JMP;
  50.320 +      }
  50.321 +    tablesize--; // avoid a -1 in FastLimiter()
  50.322 +
  50.323 +    // do a quick accuracy check, just to be sure that FastLimiter() is working
  50.324 +    // as expected :-)
  50.325 +#ifdef ACC_DUMP
  50.326 +    FILE *out=fopen("/tmp/limiter","w");
  50.327 +#endif
  50.328 +    mad_fixed_t maxdiff=0;
  50.329 +    for(mad_fixed_t x=F_LIM_MAX ; x>=limlvl ; x-=mad_f_tofixed(1e-4)) {
  50.330 +      mad_fixed_t diff=mad_f_abs(Limiter(x)-FastLimiter(x));
  50.331 +      if(diff>maxdiff) maxdiff=diff;
  50.332 +#ifdef ACC_DUMP
  50.333 +      fprintf(out,"%0.10f\t%0.10f\t%0.10f\t%0.10f\t%0.10f\n",
  50.334 +        mad_f_todouble(x),mad_f_todouble(Limiter(x)),mad_f_todouble(FastLimiter(x)),mad_f_todouble(diff),mad_f_todouble(maxdiff));
  50.335 +      if(ferror(out)) break;
  50.336 +#endif
  50.337 +      }
  50.338 +#ifdef ACC_DUMP
  50.339 +    fclose(out);
  50.340 +#endif
  50.341 +    d(printf("norm: accuracy %.12f\n",mad_f_todouble(maxdiff)))
  50.342 +    if(mad_f_todouble(maxdiff)>1e-6) {
  50.343 +      esyslog("ERROR: accuracy check failed, normalizer disabled");
  50.344 +      delete table; table=0;
  50.345 +      }
  50.346 +    }
  50.347 +  else esyslog("ERROR: no memory for lookup table, normalizer disabled");
  50.348 +#endif // USE_FAST_LIMITER
  50.349 +}
  50.350 +
  50.351 +cNormalize::~cNormalize()
  50.352 +{
  50.353 +#ifdef USE_FAST_LIMITER
  50.354 +  delete[] table;
  50.355 +#endif
  50.356 +}
  50.357 +
  50.358 +void cNormalize::Init(double Level, double Peak)
  50.359 +{
  50.360 +  double Target=(double)MP3Setup.TargetLevel/100.0;
  50.361 +  double dgain=Target/Level;
  50.362 +  if(dgain>MAX_GAIN) dgain=MAX_GAIN;
  50.363 +  gain=mad_f_tofixed(dgain);
  50.364 +  // Check if we actually need to apply a gain
  50.365 +  dogain=(Target>0.0 && fabs(1-dgain)>MIN_GAIN);
  50.366 +#ifdef USE_FAST_LIMITER
  50.367 +  if(!table) dogain=false;
  50.368 +#endif
  50.369 +  // Check if we actually need to do limiting:
  50.370 +  // we have to if limiter is enabled, if gain>1 and if the peaks will clip.
  50.371 +  dolimit=(d_limlvl<1.0 && dgain>1.0 && Peak*dgain>1.0);
  50.372 +#ifdef DEBUG
  50.373 +  printf("norm: gain=%f dogain=%d dolimit=%d (target=%f level=%f peak=%f)\n",dgain,dogain,dolimit,Target,Level,Peak);
  50.374 +  limited=clipped=total=0; peak=0;
  50.375 +#endif
  50.376 +}
  50.377 +
  50.378 +void cNormalize::Stats(void)
  50.379 +{
  50.380 +#ifdef DEBUG
  50.381 +  if(total)
  50.382 +    printf("norm: stats tot=%ld lim=%ld/%.3f%% clip=%ld/%.3f%% peak=%.3f\n",
  50.383 +           total,limited,(double)limited/total*100.0,clipped,(double)clipped/total*100.0,mad_f_todouble(peak));
  50.384 +#endif
  50.385 +}
  50.386 +
  50.387 +mad_fixed_t cNormalize::Limiter(mad_fixed_t x)
  50.388 +{
  50.389 +// Limiter function:
  50.390 +//
  50.391 +//        / x                                                (for x <= lev)
  50.392 +//   x' = |
  50.393 +//        \ tanh((x - lev) / (1-lev)) * (1-lev) + lev        (for x > lev)
  50.394 +//
  50.395 +// call only with x>=0. For negative samples, preserve sign outside this function
  50.396 +//
  50.397 +// With limiter level = 0, this is equivalent to a tanh() function;
  50.398 +// with limiter level = 1, this is equivalent to clipping.
  50.399 +
  50.400 +  if(x>limlvl) {
  50.401 +#ifdef DEBUG
  50.402 +    if(x>MAD_F_ONE) clipped++;
  50.403 +    limited++;
  50.404 +#endif
  50.405 +    x=mad_f_tofixed(tanh((mad_f_todouble(x)-d_limlvl) / one_limlvl) * one_limlvl + d_limlvl);
  50.406 +    }
  50.407 +  return x;
  50.408 +}
  50.409 +
  50.410 +#ifdef USE_FAST_LIMITER
  50.411 +mad_fixed_t cNormalize::FastLimiter(mad_fixed_t x)
  50.412 +{
  50.413 +// The fast algorithm is based on a linear interpolation between the
  50.414 +// the values in the lookup table. Relays heavly on libmads fixed point format.
  50.415 +
  50.416 +  if(x>limlvl) {
  50.417 +    int i=(unsigned int)(x-tablestart)/F_LIM_JMP;
  50.418 +#ifdef DEBUG
  50.419 +    if(x>MAD_F_ONE) clipped++;
  50.420 +    limited++;
  50.421 +    if(i>=tablesize) printf("norm: overflow x=%f x-ts=%f i=%d tsize=%d\n",
  50.422 +                            mad_f_todouble(x),mad_f_todouble(x-tablestart),i,tablesize);
  50.423 +#endif
  50.424 +    mad_fixed_t r=x & (F_LIM_JMP-1);
  50.425 +    x=MAD_F_ONE;
  50.426 +    if(i<tablesize) {
  50.427 +      mad_fixed_t *ptr=&table[i];
  50.428 +      x=*ptr;
  50.429 +      mad_fixed_t d=*(ptr+1)-x;
  50.430 +      //x+=mad_f_mul(d,r)<<LIM_ACC;                // this is not accurate as mad_f_mul() does >>MAD_F_FRACBITS
  50.431 +                                                   // which is senseless in the case of following <<LIM_ACC.
  50.432 +      x+=((long long)d*(long long)r)>>LIM_SHIFT;   // better, don't know if works on all machines
  50.433 +      }
  50.434 +    }
  50.435 +  return x;
  50.436 +}
  50.437 +#endif
  50.438 +
  50.439 +#ifdef USE_FAST_LIMITER
  50.440 +#define LIMITER_FUNC FastLimiter
  50.441 +#else
  50.442 +#define LIMITER_FUNC Limiter
  50.443 +#endif
  50.444 +
  50.445 +void cNormalize::AddGain(struct mad_pcm *pcm)
  50.446 +{
  50.447 +  if(dogain) {
  50.448 +    for(int i=0 ; i<pcm->channels ; i++) {
  50.449 +      mad_fixed_t *data=pcm->samples[i];
  50.450 +#ifdef DEBUG
  50.451 +      total+=pcm->length;
  50.452 +#endif
  50.453 +      if(dolimit) {
  50.454 +        for(int n=pcm->length ; n>0 ; n--) {
  50.455 +          mad_fixed_t s=mad_f_mul(*data,gain);
  50.456 +          if(s<0) {
  50.457 +            s=-s;
  50.458 +#ifdef DEBUG
  50.459 +            if(s>peak) peak=s;
  50.460 +#endif
  50.461 +            s=LIMITER_FUNC(s);
  50.462 +            s=-s;
  50.463 +            }
  50.464 +          else {
  50.465 +#ifdef DEBUG
  50.466 +            if(s>peak) peak=s;
  50.467 +#endif
  50.468 +            s=LIMITER_FUNC(s);
  50.469 +            }
  50.470 +          *data++=s;
  50.471 +          }
  50.472 +        }
  50.473 +      else {
  50.474 +        for(int n=pcm->length ; n>0 ; n--) {
  50.475 +          mad_fixed_t s=mad_f_mul(*data,gain);
  50.476 +#ifdef DEBUG
  50.477 +          if(s>peak) peak=s;
  50.478 +          else if(-s>peak) peak=-s;
  50.479 +#endif
  50.480 +          if(s>MAD_F_ONE) s=MAD_F_ONE;   // do clipping
  50.481 +          if(s<-MAD_F_ONE) s=-MAD_F_ONE;
  50.482 +          *data++=s;
  50.483 +          }
  50.484 +        }
  50.485 +      }
  50.486 +    }
  50.487 +}
  50.488 +
  50.489 +// --- cScale ----------------------------------------------------------------
  50.490 +
  50.491 +// The dither code has been adapted from the madplay project
  50.492 +// (audio.c) found in the libmad distribution
  50.493 +
  50.494 +enum eAudioMode { amRoundBE, amDitherBE, amRoundLE, amDitherLE };
  50.495 +
  50.496 +class cScale {
  50.497 +private:
  50.498 +  enum { MIN=-MAD_F_ONE, MAX=MAD_F_ONE - 1 };
  50.499 +#ifdef DEBUG
  50.500 +  // audio stats
  50.501 +  unsigned long clipped_samples;
  50.502 +  mad_fixed_t peak_clipping;
  50.503 +  mad_fixed_t peak_sample;
  50.504 +#endif
  50.505 +  // dither
  50.506 +  struct dither {
  50.507 +    mad_fixed_t error[3];
  50.508 +    mad_fixed_t random;
  50.509 +    } leftD, rightD;
  50.510 +  //
  50.511 +  inline mad_fixed_t Clip(mad_fixed_t sample, bool stats=true);
  50.512 +  inline unsigned long Prng(unsigned long state);
  50.513 +  signed long LinearRound(mad_fixed_t sample);
  50.514 +  signed long LinearDither(mad_fixed_t sample, struct dither *dither);
  50.515 +public:
  50.516 +  void Init(void);
  50.517 +  void Stats(void);
  50.518 +  unsigned int ScaleBlock(unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode);
  50.519 +  };
  50.520 +
  50.521 +void cScale::Init(void)
  50.522 +{
  50.523 +#ifdef DEBUG
  50.524 +  clipped_samples=0; peak_clipping=peak_sample=0;
  50.525 +#endif
  50.526 +  memset(&leftD,0,sizeof(leftD));
  50.527 +  memset(&rightD,0,sizeof(rightD));
  50.528 +}
  50.529 +
  50.530 +void cScale::Stats(void)
  50.531 +{
  50.532 +#ifdef DEBUG
  50.533 +  printf("mp3: scale stats clipped=%ld peak_clip=%f peak=%f\n",
  50.534 +         clipped_samples,mad_f_todouble(peak_clipping),mad_f_todouble(peak_sample));
  50.535 +#endif
  50.536 +}
  50.537 +
  50.538 +// gather signal statistics while clipping
  50.539 +mad_fixed_t cScale::Clip(mad_fixed_t sample, bool stats)
  50.540 +{
  50.541 +#ifndef DEBUG
  50.542 +  if (sample > MAX) sample = MAX;
  50.543 +  if (sample < MIN) sample = MIN;
  50.544 +#else
  50.545 +  if(!stats) {
  50.546 +    if (sample > MAX) sample = MAX;
  50.547 +    if (sample < MIN) sample = MIN;
  50.548 +    }
  50.549 +  else {
  50.550 +    if (sample >= peak_sample) {
  50.551 +      if (sample > MAX) {
  50.552 +        ++clipped_samples;
  50.553 +        if (sample - MAX > peak_clipping)
  50.554 +	  peak_clipping = sample - MAX;
  50.555 +        sample = MAX;
  50.556 +        }
  50.557 +      peak_sample = sample;
  50.558 +      }
  50.559 +    else if (sample < -peak_sample) {
  50.560 +      if (sample < MIN) {
  50.561 +        ++clipped_samples;
  50.562 +        if (MIN - sample > peak_clipping)
  50.563 +	  peak_clipping = MIN - sample;
  50.564 +        sample = MIN;
  50.565 +        }
  50.566 +      peak_sample = -sample;
  50.567 +      }
  50.568 +    }
  50.569 +#endif
  50.570 +  return sample;
  50.571 +}
  50.572 +
  50.573 +// generic linear sample quantize routine
  50.574 +signed long cScale::LinearRound(mad_fixed_t sample)
  50.575 +{
  50.576 +  // round
  50.577 +  sample += (1L << (MAD_F_FRACBITS - OUT_BITS));
  50.578 +  // clip
  50.579 +  sample=Clip(sample);
  50.580 +  // quantize and scale
  50.581 +  return sample >> (MAD_F_FRACBITS + 1 - OUT_BITS);
  50.582 +}
  50.583 +
  50.584 +// 32-bit pseudo-random number generator
  50.585 +unsigned long cScale::Prng(unsigned long state)
  50.586 +{
  50.587 +  return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
  50.588 +}
  50.589 +
  50.590 +// generic linear sample quantize and dither routine
  50.591 +signed long cScale::LinearDither(mad_fixed_t sample, struct dither *dither)
  50.592 +{
  50.593 +  // noise shape
  50.594 +  sample += dither->error[0] - dither->error[1] + dither->error[2];
  50.595 +  dither->error[2] = dither->error[1];
  50.596 +  dither->error[1] = dither->error[0] / 2;
  50.597 +  // bias
  50.598 +  mad_fixed_t output = sample + (1L << (MAD_F_FRACBITS + 1 - OUT_BITS - 1));
  50.599 +  const int scalebits = MAD_F_FRACBITS + 1 - OUT_BITS;
  50.600 +  const mad_fixed_t mask = (1L << scalebits) - 1;
  50.601 +  // dither
  50.602 +  const mad_fixed_t random = Prng(dither->random);
  50.603 +  output += (random & mask) - (dither->random & mask);
  50.604 +  dither->random = random;
  50.605 +  // clip
  50.606 +  output=Clip(output);
  50.607 +  sample=Clip(sample,false);
  50.608 +  // quantize
  50.609 +  output &= ~mask;
  50.610 +  // error feedback
  50.611 +  dither->error[0] = sample - output;
  50.612 +  // scale
  50.613 +  return output >> scalebits;
  50.614 +}
  50.615 +
  50.616 +#define PUT_BE(data,sample) { *data++=(sample)>>8; *data++=(sample)>>0; }
  50.617 +#define PUT_LE(data,sample) { *data++=(sample)>>0; *data++=(sample)>>8; }
  50.618 +
  50.619 +// write a block of signed 16-bit PCM samples
  50.620 +unsigned int cScale::ScaleBlock(unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode)
  50.621 +{
  50.622 +  unsigned int len=size/OUT_FACT;
  50.623 +  if(len>nsamples) { len=nsamples; size=len*OUT_FACT; }
  50.624 +  nsamples-=len;
  50.625 +  switch(mode) {
  50.626 +    case amRoundBE:
  50.627 +      while(len--) {
  50.628 +        signed int sample=LinearRound(*left++);
  50.629 +        PUT_BE(data,sample);
  50.630 +        if(right) sample=LinearRound(*right++);
  50.631 +        PUT_BE(data,sample);
  50.632 +        }
  50.633 +      break;
  50.634 +    case amDitherBE:
  50.635 +      while(len--) {
  50.636 +	signed int sample=LinearDither(*left++,&leftD);
  50.637 +        PUT_BE(data,sample);
  50.638 +	if(right) sample=LinearDither(*right++,&rightD);
  50.639 +        PUT_BE(data,sample);
  50.640 +        }
  50.641 +      break;
  50.642 +    case amRoundLE:
  50.643 +      while(len--) {
  50.644 +        signed int sample=LinearRound(*left++);
  50.645 +        PUT_LE(data,sample);
  50.646 +        if(right) sample=LinearRound(*right++);
  50.647 +        PUT_LE(data,sample);
  50.648 +        }
  50.649 +      break;
  50.650 +    case amDitherLE:
  50.651 +      while(len--) {
  50.652 +	signed int sample=LinearDither(*left++,&leftD);
  50.653 +        PUT_LE(data,sample);
  50.654 +	if(right) sample=LinearDither(*right++,&rightD);
  50.655 +        PUT_LE(data,sample);
  50.656 +        }
  50.657 +      break;
  50.658 +    }
  50.659 + return size;
  50.660 +}
  50.661 +
  50.662 +// --- cShuffle ----------------------------------------------------------------
  50.663 +
  50.664 +class cShuffle {
  50.665 +private:
  50.666 +  int *shuffle, max;
  50.667 +  unsigned int seed;
  50.668 +  //
  50.669 +  int Index(int pos);
  50.670 +public:
  50.671 +  cShuffle(void);
  50.672 +  ~cShuffle();
  50.673 +  void Shuffle(int num, int curr);
  50.674 +  void Del(int pos);
  50.675 +  void Flush(void);
  50.676 +  int First(void);
  50.677 +  int Next(int curr);
  50.678 +  int Prev(int curr);
  50.679 +  int Goto(int pos, int curr);
  50.680 +  };
  50.681 +
  50.682 +cShuffle::cShuffle(void)
  50.683 +{
  50.684 +  shuffle=0; max=0;
  50.685 +  seed=time(0);
  50.686 +}
  50.687 +
  50.688 +cShuffle::~cShuffle(void)
  50.689 +{
  50.690 +  Flush();
  50.691 +}
  50.692 +
  50.693 +void cShuffle::Flush(void)
  50.694 +{
  50.695 +  delete shuffle; shuffle=0;
  50.696 +  max=0;
  50.697 +}
  50.698 +
  50.699 +int cShuffle::Index(int pos)
  50.700 +{
  50.701 +  if(pos>=0)
  50.702 +    for(int i=0; i<max; i++) if(shuffle[i]==pos) return i;
  50.703 +  return -1;
  50.704 +}
  50.705 +
  50.706 +void cShuffle::Shuffle(int num, int curr)
  50.707 +{
  50.708 +  int oldmax=0;
  50.709 +  if(num!=max) {
  50.710 +    int *ns=new int[num];
  50.711 +    if(shuffle) {
  50.712 +      if(num>max) {
  50.713 +        memcpy(ns,shuffle,max*sizeof(int));
  50.714 +        oldmax=max;
  50.715 +        }
  50.716 +      delete shuffle;
  50.717 +      }
  50.718 +    shuffle=ns; max=num;
  50.719 +    }
  50.720 +  if(!oldmax) curr=-1;
  50.721 +  for(int i=oldmax ; i<max ; i++) shuffle[i]=i;
  50.722 +
  50.723 +  int in=Index(curr)+1; if(in<0) in=0;
  50.724 +  if((max-in)>=2) {
  50.725 +    for(int i=in ; i<max ; i++) {
  50.726 +      int ran=(rand_r(&seed) % ((max-in)*4-4))/4; ran+=((ran+in) >= i);
  50.727 +      int t=shuffle[i];
  50.728 +      shuffle[i]=shuffle[ran+in];
  50.729 +      shuffle[ran+in]=t;
  50.730 +      }
  50.731 +    }
  50.732 +#ifdef DEBUG
  50.733 +  printf("shuffle: order (%d , %d -> %d) ",num,curr,in);
  50.734 +  for(int i=0 ; i<max ; i++) printf("%d ",shuffle[i]);
  50.735 +  printf("\n");
  50.736 +#endif
  50.737 +}
  50.738 +
  50.739 +void cShuffle::Del(int pos)
  50.740 +{
  50.741 +  int i=Index(pos);
  50.742 +  if(i>=0) {
  50.743 +    if(i+1<max) memmove(&shuffle[i],&shuffle[i+1],(max-i-1)*sizeof(int));
  50.744 +    max--;
  50.745 +    }
  50.746 +}
  50.747 +
  50.748 +int cShuffle::First(void)
  50.749 +{
  50.750 +  return shuffle[0];
  50.751 +}
  50.752 +
  50.753 +int cShuffle::Next(int curr)
  50.754 +{
  50.755 +  int i=Index(curr);
  50.756 +  return (i>=0 && i+1<max) ? shuffle[i+1] : -1;
  50.757 +}
  50.758 +
  50.759 +int cShuffle::Prev(int curr)
  50.760 +{
  50.761 +  int i=Index(curr);
  50.762 +  return (i>0) ? shuffle[i-1] : -1;
  50.763 +}
  50.764 +
  50.765 +int cShuffle::Goto(int pos, int curr)
  50.766 +{
  50.767 +  int i=Index(curr);
  50.768 +  int g=Index(pos);
  50.769 +  if(g>=0) {
  50.770 +    if(g<i) {
  50.771 +      for(int l=g; l<i; l++) shuffle[l]=shuffle[l+1];
  50.772 +      shuffle[i]=pos;
  50.773 +      }
  50.774 +    else if(g>i) {
  50.775 +      for(int l=g; l>i+1; l--) shuffle[l]=shuffle[l-1];
  50.776 +      shuffle[i+1]=pos;
  50.777 +      }
  50.778 +#ifdef DEBUG
  50.779 +    printf("shuffle: goto order (%d -> %d , %d -> %d) ",pos,g,curr,i);
  50.780 +    for(int i=0 ; i<max ; i++) printf("%d ",shuffle[i]);
  50.781 +    printf("\n");
  50.782 +#endif
  50.783 +    return pos;
  50.784 +    }
  50.785 +  return -1;
  50.786 +}
  50.787 +
  50.788 +// --- cPlayManager ------------------------------------------------------------
  50.789 +
  50.790 +#define SCANNED_ID3 1
  50.791 +#define SCANNED_LVL 2
  50.792 +
  50.793 +cPlayManager *mgr=0;
  50.794 +
  50.795 +cPlayManager::cPlayManager(void)
  50.796 +{
  50.797 +  curr=0; currIndex=-1;
  50.798 +  scan=0; stopscan=throttle=pass2=release=false;
  50.799 +  play=0; playNew=eol=false;
  50.800 +  shuffle=new cShuffle;
  50.801 +  loopMode=(MP3Setup.InitLoopMode>0);
  50.802 +  shuffleMode=(MP3Setup.InitShuffleMode>0);
  50.803 +}
  50.804 +
  50.805 +cPlayManager::~cPlayManager()
  50.806 +{
  50.807 +  Flush();
  50.808 +  Release();
  50.809 +  listMutex.Lock();
  50.810 +  stopscan=true; bgCond.Broadcast();
  50.811 +  listMutex.Unlock();
  50.812 +  Cancel(2);
  50.813 +  delete shuffle;
  50.814 +}
  50.815 +
  50.816 +void cPlayManager::ThrottleWait(void)
  50.817 +{
  50.818 +  while(!stopscan && !release && throttle) {
  50.819 +    db(printf("mgr: background scan throttled\n"))
  50.820 +    bgCond.Wait(listMutex);
  50.821 +    db(printf("mgr: background scan throttle wakeup\n"))
  50.822 +    }
  50.823 +}
  50.824 +
  50.825 +void cPlayManager::Action(void)
  50.826 +{
  50.827 +  db(printf("mgr: background scan thread started (pid=%d)\n", getpid()))
  50.828 +  nice(5);
  50.829 +  listMutex.Lock();
  50.830 +  while(!stopscan) {
  50.831 +    for(scan=list.First(); !stopscan && !release && scan; scan=list.Next(scan)) {
  50.832 +      ThrottleWait();
  50.833 +      listMutex.Unlock();
  50.834 +      if(!(scan->user & SCANNED_ID3)) {
  50.835 +        db(printf("mgr: scanning (id3) %s\n",scan->Name()))
  50.836 +        cSongInfo *si=scan->Info(true);
  50.837 +        if(si && si->Level>0.0) scan->user|=SCANNED_LVL;
  50.838 +        scan->user|=SCANNED_ID3;
  50.839 +        }
  50.840 +      listMutex.Lock();
  50.841 +      }
  50.842 +    if(MP3Setup.BgrScan>1) {
  50.843 +      pass2=true;
  50.844 +      for(scan=list.First(); !stopscan && !release && scan; scan=list.Next(scan)) {
  50.845 +        if(scan==curr) continue;
  50.846 +        ThrottleWait();
  50.847 +        listMutex.Unlock();
  50.848 +        if(!(scan->user & SCANNED_LVL)) {
  50.849 +          cDecoder *dec=scan->Decoder();
  50.850 +          if(dec) {
  50.851 +            cSongInfo *si=scan->Info(false);
  50.852 +            if(!dec->IsStream() && (!si || si->Level<=0.0) && dec->Start()) {
  50.853 +              db(printf("mgr: scanning (lvl) %s\n",scan->Name()))
  50.854 +              cLevel level;
  50.855 +              level.Init();
  50.856 +              bool go=true;
  50.857 +              while(go && !release) {
  50.858 +                if(throttle) {
  50.859 +                  listMutex.Lock(); ThrottleWait(); listMutex.Unlock();
  50.860 +                  continue;
  50.861 +                  }
  50.862 +                struct Decode *ds=dec->Decode();
  50.863 +                switch(ds->status) {
  50.864 +                  case dsPlay:
  50.865 +                    level.GetPower(ds->pcm);
  50.866 +                    break;
  50.867 +                  case dsSkip:
  50.868 +                  case dsSoftError:
  50.869 +                    break;
  50.870 +                  case dsEof:
  50.871 +                    {
  50.872 +                    double l=level.GetLevel();
  50.873 +                    if(l>0.0) {
  50.874 +                      cSongInfo *si=dec->SongInfo(false);
  50.875 +                      cFileInfo *fi=dec->FileInfo();
  50.876 +                      if(si && fi) {
  50.877 +                        si->Level=l;
  50.878 +                        si->Peak=level.GetPeak();
  50.879 +                        InfoCache.Cache(si,fi);
  50.880 +                        }
  50.881 +                      }
  50.882 +                    }
  50.883 +                    //fall through
  50.884 +                  case dsOK:
  50.885 +                  case dsError:
  50.886 +                    scan->user|=SCANNED_LVL;
  50.887 +                    go=false;
  50.888 +                    break;
  50.889 +                  }
  50.890 +                }
  50.891 +              }
  50.892 +            else scan->user|=SCANNED_LVL;
  50.893 +            dec->Stop();
  50.894 +            }
  50.895 +          }
  50.896 +        listMutex.Lock();
  50.897 +        }
  50.898 +      pass2=false;
  50.899 +      }
  50.900 +    do {
  50.901 +      scan=0; release=false; fgCond.Broadcast();
  50.902 +      db(printf("mgr: background scan idle\n"))
  50.903 +      bgCond.Wait(listMutex);
  50.904 +      db(printf("mgr: background scan idle wakeup\n"))
  50.905 +      } while(!stopscan && (release || throttle));
  50.906 +    }
  50.907 +  listMutex.Unlock();
  50.908 +  db(printf("mgr: background scan thread ended (pid=%d)\n", getpid()))
  50.909 +}
  50.910 +
  50.911 +void cPlayManager::Throttle(bool thr)
  50.912 +{
  50.913 +  if(MP3Setup.BgrScan) {
  50.914 +    if(!thr && throttle) {
  50.915 +      db(printf("mgr: bgr-scan -> run (%d)\n",time_ms()))
  50.916 +      listMutex.Lock();
  50.917 +      throttle=false; bgCond.Broadcast();
  50.918 +      listMutex.Unlock();
  50.919 +      }
  50.920 +    if(thr && !throttle) {
  50.921 +      db(printf("mgr: bgr-scan -> throttle (%d)\n",time_ms()))
  50.922 +      throttle=true;
  50.923 +      }
  50.924 +    }
  50.925 +}
  50.926 +
  50.927 +void cPlayManager::ToggleShuffle(void)
  50.928 +{
  50.929 +  shuffleMode=!shuffleMode;
  50.930 +  d(printf("mgr: shuffle mode toggled : %d\n",shuffleMode))
  50.931 +  if(shuffleMode && !eol) {
  50.932 +    curr=0; currIndex=-1;
  50.933 +    shuffle->Shuffle(maxIndex+1,-1);
  50.934 +    Next();
  50.935 +    }
  50.936 +}
  50.937 +
  50.938 +void cPlayManager::ToggleLoop(void)
  50.939 +{
  50.940 +  loopMode=!loopMode;
  50.941 +  d(printf("mgr: loop mode toggled : %d\n",loopMode))
  50.942 +}
  50.943 +
  50.944 +bool cPlayManager::Info(int num, cMP3PlayInfo *pi)
  50.945 +{
  50.946 +  cSong *s;
  50.947 +  int idx=num-1;
  50.948 +  if(idx<0) { idx=currIndex; s=curr; }
  50.949 +  else      { s=list.Get(idx); }
  50.950 +  memset(pi,0,sizeof(*pi));
  50.951 +  pi->Num=idx+1;
  50.952 +  pi->MaxNum=maxIndex+1;
  50.953 +  pi->Loop=loopMode;
  50.954 +  pi->Shuffle=shuffleMode;
  50.955 +  bool res=false;
  50.956 +  if(s) {
  50.957 +    strn0cpy(pi->Title,s->Name(),sizeof(pi->Title));
  50.958 +    strn0cpy(pi->Filename,s->FullPath(),sizeof(pi->Filename));
  50.959 +    cSongInfo *si=s->Info(false);
  50.960 +    if(si && si->HasInfo()) {
  50.961 +      static char *modestr[] = { "Mono","Dual","Joint-Stereo","Stereo" };
  50.962 +
  50.963 +      if(si->Title)  strn0cpy(pi->Title,si->Title,sizeof(pi->Title));
  50.964 +      if(si->Artist) strn0cpy(pi->Artist,si->Artist,sizeof(pi->Artist));
  50.965 +      if(si->Album)  strn0cpy(pi->Album,si->Album,sizeof(pi->Album));
  50.966 +      strn0cpy(pi->SMode,modestr[si->ChMode],sizeof(pi->SMode));
  50.967 +      pi->Year=si->Year;
  50.968 +      pi->SampleFreq=si->SampleFreq;
  50.969 +      pi->Bitrate=si->Bitrate;
  50.970 +      pi->MaxBitrate=si->MaxBitrate;
  50.971 +      res=true;
  50.972 +      }
  50.973 +    }
  50.974 +  pi->Hash=MakeHashBuff((char *)pi,(char *)&pi->Loop-(char *)pi);
  50.975 +  return res;
  50.976 +}
  50.977 +
  50.978 +void cPlayManager::Add(cPlayList *pl)
  50.979 +{
  50.980 +  cMutexLock lock(&listMutex);
  50.981 +  bool real=false;
  50.982 +  for(cSong *song=pl->First(); song; song=pl->cList<cSong>::Next(song)) {
  50.983 +    cSong *ns=new cSong(song);
  50.984 +    list.Add(ns);
  50.985 +    real=true;
  50.986 +    }
  50.987 +  if(real) {
  50.988 +    if(MP3Setup.BgrScan) { stopscan=false; if(!Active()) Start(); }
  50.989 +    else stopscan=true;
  50.990 +    bgCond.Broadcast();
  50.991 +    maxIndex=list.Count()-1;
  50.992 +    if(shuffleMode) shuffle->Shuffle(maxIndex+1,currIndex);
  50.993 +    if(!curr) Next();
  50.994 +    }
  50.995 +}
  50.996 +
  50.997 +void cPlayManager::Flush(void)
  50.998 +{
  50.999 +  cMutexLock lock(&listMutex);
 50.1000 +  Halt();
 50.1001 +  list.Clear();
 50.1002 +  shuffle->Flush();
 50.1003 +}
 50.1004 +
 50.1005 +void cPlayManager::Halt(void)
 50.1006 +{
 50.1007 +  cMutexLock lock(&listMutex);
 50.1008 +  curr=0; currIndex=-1;
 50.1009 +  playNew=true;
 50.1010 +  stopscan=true; bgCond.Broadcast();
 50.1011 +  NoScan(0);
 50.1012 +  NoPlay(0);
 50.1013 +}
 50.1014 +
 50.1015 +void cPlayManager::NoScan(cSong *nono)
 50.1016 +{
 50.1017 +  // call with listMutex locked!!
 50.1018 +  while((nono && pass2 && scan==nono) || (!nono && scan)) {
 50.1019 +    release=true; bgCond.Broadcast();
 50.1020 +    d(printf("mgr: waiting for bgr release ... (pass2=%d nono=%p scan=%p)\n",pass2,nono,scan))
 50.1021 +    fgCond.Wait(listMutex);
 50.1022 +    }
 50.1023 +}
 50.1024 +
 50.1025 +void cPlayManager::NoPlay(cSong *nono)
 50.1026 +{
 50.1027 +  // call with listMutex locked!!
 50.1028 +  while((nono && play==nono) || (!nono && play)) {
 50.1029 +    playNew=true;
 50.1030 +    fgCond.Wait(listMutex);
 50.1031 +    }
 50.1032 +}
 50.1033 +
 50.1034 +bool cPlayManager::Next(void)
 50.1035 +{
 50.1036 +  cMutexLock lock(&listMutex);
 50.1037 +  int ni;
 50.1038 +  cSong *n;
 50.1039 +  if(shuffleMode) {
 50.1040 +    if(curr) {
 50.1041 +      ni=shuffle->Next(currIndex);
 50.1042 +      if(ni<0) {
 50.1043 +        if(loopMode || eol) {
 50.1044 +          shuffle->Shuffle(maxIndex+1,-1);
 50.1045 +          ni=shuffle->First();
 50.1046 +          }
 50.1047 +        else eol=true;
 50.1048 +        }
 50.1049 +      }
 50.1050 +    else
 50.1051 +      ni=shuffle->First();
 50.1052 +    n=(ni>=0) ? list.Get(ni) : 0;
 50.1053 +    }
 50.1054 +  else {
 50.1055 +    if(curr) {
 50.1056 +      n=list.cList<cSong>::Next(curr);
 50.1057 +      if(!n) {
 50.1058 +        if(loopMode || eol) n=list.First();
 50.1059 +        else eol=true;
 50.1060 +        }
 50.1061 +      }
 50.1062 +    else
 50.1063 +      n=list.First();
 50.1064 +    ni=n ? n->Index() : -1;
 50.1065 +    }
 50.1066 +  if(n) {
 50.1067 +    curr=n; currIndex=ni;
 50.1068 +    playNew=true; eol=false;
 50.1069 +    d(printf("mgr: next -> %d\n",currIndex))
 50.1070 +    return true;
 50.1071 +    }
 50.1072 +  return false;
 50.1073 +}
 50.1074 +
 50.1075 +bool cPlayManager::Prev(void)
 50.1076 +{
 50.1077 +  cMutexLock lock(&listMutex);
 50.1078 +  int ni;
 50.1079 +  cSong *n;
 50.1080 +  if(shuffleMode) {
 50.1081 +    ni=shuffle->Prev(currIndex);
 50.1082 +    n=(ni>=0) ? list.Get(ni) : 0;
 50.1083 +    }
 50.1084 +  else {
 50.1085 +    n=list.cList<cSong>::Prev(curr);
 50.1086 +    ni=n ? n->Index() : -1;
 50.1087 +    }
 50.1088 +  if(n) {
 50.1089 +    curr=n; currIndex=ni;
 50.1090 +    playNew=true; eol=false;
 50.1091 +    d(printf("mgr: prev -> %d\n",currIndex))
 50.1092 +    return true;
 50.1093 +    }
 50.1094 +  return false;
 50.1095 +}
 50.1096 +
 50.1097 +void cPlayManager::Goto(int num)
 50.1098 +{
 50.1099 +  cMutexLock lock(&listMutex);
 50.1100 +  if(num>0 && num<=maxIndex+1) {
 50.1101 +    int idx=num-1;
 50.1102 +    if(shuffleMode) {
 50.1103 +      if(eol) {
 50.1104 +        shuffle->Shuffle(maxIndex+1,-1);
 50.1105 +        currIndex=shuffle->Goto(idx,-1);
 50.1106 +        }
 50.1107 +      else
 50.1108 +        currIndex=shuffle->Goto(idx,currIndex);
 50.1109 +      }
 50.1110 +    else
 50.1111 +      currIndex=idx;
 50.1112 +    curr=(currIndex>=0) ? list.Get(currIndex) : 0;
 50.1113 +    playNew=true; eol=false;
 50.1114 +    d(printf("mgr: goto -> %d\n",currIndex))
 50.1115 +    }
 50.1116 +}
 50.1117 +
 50.1118 +cSong *cPlayManager::Current(void)
 50.1119 +{
 50.1120 +  cMutexLock lock(&listMutex);
 50.1121 +  if(!play) {
 50.1122 +    NoScan(curr);
 50.1123 +    play=curr;
 50.1124 +    playNew=false;
 50.1125 +    if(play) d(printf("mgr: playing %s\n",play->Name()))
 50.1126 +    else d(printf("mgr: nothing to play\n"))
 50.1127 +    fgCond.Broadcast();
 50.1128 +    }
 50.1129 +  return play;
 50.1130 +}
 50.1131 +
 50.1132 +bool cPlayManager::NextCurrent(void)
 50.1133 +{
 50.1134 +  cMutexLock lock(&listMutex);
 50.1135 +  return (!eol && (playNew || Next()));
 50.1136 +}
 50.1137 +
 50.1138 +bool cPlayManager::NewCurrent(void)
 50.1139 +{
 50.1140 +  return playNew;
 50.1141 +}
 50.1142 +
 50.1143 +void cPlayManager::Release(void)
 50.1144 +{
 50.1145 +  cMutexLock lock(&listMutex);
 50.1146 +  play=0;
 50.1147 +  fgCond.Broadcast();
 50.1148 +}
 50.1149 +
 50.1150 +// --- cOutput -----------------------------------------------------------------
 50.1151 +
 50.1152 +struct FrameHeader {
 50.1153 +  unsigned int samplerate;
 50.1154 +  };
 50.1155 +#define FHS sizeof(struct FrameHeader)
 50.1156 +
 50.1157 +class cOutput {
 50.1158 +protected:
 50.1159 +  cMP3Player *player;
 50.1160 +  cScale scale;
 50.1161 +public:
 50.1162 +  cOutput(cMP3Player *Player);
 50.1163 +  virtual ~cOutput() {}
 50.1164 +  virtual void Init(void);
 50.1165 +  virtual unsigned int SampleRate(unsigned int PcmSampleRate)=0;
 50.1166 +  virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr)=0;
 50.1167 +  virtual int Output(const unsigned char *Data, int Len, bool SOF)=0;
 50.1168 +  virtual bool Poll(void)=0;
 50.1169 +  virtual void Play(void)=0;
 50.1170 +  virtual void Pause(void)=0;
 50.1171 +#ifdef DEBUG
 50.1172 +  virtual void Stats(void);
 50.1173 +#endif
 50.1174 +  };
 50.1175 +
 50.1176 +cOutput::cOutput(cMP3Player *Player)
 50.1177 +{
 50.1178 +  player=Player;
 50.1179 +}
 50.1180 +
 50.1181 +void cOutput::Init(void)
 50.1182 +{
 50.1183 +  scale.Init();
 50.1184 +}
 50.1185 +
 50.1186 +#ifdef DEBUG
 50.1187 +void cOutput::Stats(void)
 50.1188 +{
 50.1189 +  scale.Stats();
 50.1190 +}
 50.1191 +#endif
 50.1192 +
 50.1193 +// --- cOutputDvb --------------------------------------------------------------
 50.1194 +
 50.1195 +/*
 50.1196 +struct LPCMHeader { int id:8;              // id
 50.1197 +                    int frame_count:8;     // number of frames
 50.1198 +                    int access_ptr:16;     // first acces unit pointer, i.e. start of audio frame
 50.1199 +                    bool emphasis:1;       // audio emphasis on-off
 50.1200 +                    bool mute:1;           // audio mute on-off
 50.1201 +                    bool reserved:1;       // reserved
 50.1202 +                    int frame_number:5;    // audio frame number
 50.1203 +                    int quant_wlen:2;      // quantization word length
 50.1204 +                    int sample_freq:2;     // audio sampling frequency (48khz=0, 96khz=1, 44,1khz=2, 32khz=3)
 50.1205 +                    bool reserved2:1;      // reserved
 50.1206 +                    int chan_count:3;      // number of audio channels - 1 (e.g. stereo = 1)
 50.1207 +                    int dyn_range_ctrl:8;  // dynamic range control (0x80 if off)
 50.1208 +                    };
 50.1209 +*/
 50.1210 +
 50.1211 +#define FRAMESIZE 2048 // max. frame size allowed for DVB driver
 50.1212 +
 50.1213 +class cOutputDvb : public cOutput {
 50.1214 +private:
 50.1215 +  cPoller poll;
 50.1216 +  unsigned int outSr;
 50.1217 +  bool only48khz;
 50.1218 +public:
 50.1219 +  cOutputDvb(cMP3Player *Player);
 50.1220 +  virtual unsigned int SampleRate(unsigned int PcmSampleRate);
 50.1221 +  virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr);
 50.1222 +  virtual int Output(const unsigned char *Data, int Len, bool SOF);
 50.1223 +  virtual bool Poll(void);
 50.1224 +  virtual void Play(void);
 50.1225 +  virtual void Pause(void);
 50.1226 +  };
 50.1227 +
 50.1228 +cOutputDvb::cOutputDvb(cMP3Player *Player)
 50.1229 +:cOutput(Player)
 50.1230 +{
 50.1231 +  only48khz=MP3Setup.Only48kHz;
 50.1232 +  outSr=0;
 50.1233 +#if APIVERSNUM == 10318
 50.1234 +  cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttDolbyFirst);
 50.1235 +#elif APIVERSNUM >= 10319
 50.1236 +  cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttAudio);
 50.1237 +#endif
 50.1238 +  d(printf("mp3-dvb: using DVB output\n"))
 50.1239 +}
 50.1240 +
 50.1241 +unsigned int cOutputDvb::SampleRate(unsigned int PcmSampleRate)
 50.1242 +{
 50.1243 +  unsigned int samplerate=48000;
 50.1244 +  if(!only48khz) {
 50.1245 +    switch(PcmSampleRate) { // If one of the supported frequencies, do it without resampling.
 50.1246 +      case 96000:           // Select a "even" upsampling frequency if possible, too.
 50.1247 +        samplerate=96000;
 50.1248 +        break;
 50.1249 +      //case 48000: // this is already the default ...
 50.1250 +      //  samplerate=48000;
 50.1251 +      //  break;
 50.1252 +      case 11025:
 50.1253 +      case 22050:
 50.1254 +      case 44100:
 50.1255 +        samplerate=44100;
 50.1256 +        break;
 50.1257 +      case 8000:
 50.1258 +      case 16000:
 50.1259 +      case 32000:
 50.1260 +        samplerate=32000;
 50.1261 +        break;
 50.1262 +      }
 50.1263 +    }
 50.1264 +  return samplerate;
 50.1265 +}
 50.1266 +
 50.1267 +cFrame *cOutputDvb::MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr)
 50.1268 +{
 50.1269 +  static const unsigned char header[] = {
 50.1270 +    0x00, // PES header
 50.1271 +    0x00,
 50.1272 +    0x01,
 50.1273 +    0xBD, // private stream
 50.1274 +    0x00,
 50.1275 +    0x00,
 50.1276 +    0x87, // mpeg2, aligned, copyright, original
 50.1277 +    0x00, // no pts/dts
 50.1278 +    0x00, // PES header data len
 50.1279 +    0xA0, // aLPCM header
 50.1280 +    0xFF,
 50.1281 +    0x00,
 50.1282 +    0x04,
 50.1283 +    0x00,
 50.1284 +    0x01, // 2-channel stereo (n-1)
 50.1285 +    0x80  // neutral dynamic range
 50.1286 +    };
 50.1287 +  cFrame *f=0;
 50.1288 +  unsigned char *buff=MALLOC(uchar,FRAMESIZE);
 50.1289 +  if(buff) {
 50.1290 +    struct FrameHeader *fh=(struct FrameHeader *)buff;
 50.1291 +    fh->samplerate=sr;
 50.1292 +    memcpy(buff+FHS,header,sizeof(header));
 50.1293 +    int srMode;
 50.1294 +    switch(sr) {
 50.1295 +      default:
 50.1296 +      case 48000: srMode=0<<4; break;
 50.1297 +      case 96000: srMode=1<<4; break;
 50.1298 +      case 44100: srMode=2<<4; break;
 50.1299 +      case 32000: srMode=3<<4; break;
 50.1300 +      }
 50.1301 +    buff[14+FHS]|=srMode;
 50.1302 +    unsigned int outlen=scale.ScaleBlock(buff+sizeof(header)+FHS,FRAMESIZE-sizeof(header)-FHS,Samples,Data[0],Data[1],MP3Setup.AudioMode?amDitherBE:amRoundBE);
 50.1303 +    if(outlen) { 
 50.1304 +      // lPCM has 600 fps which is 80 samples at 48kHz per channel
 50.1305 +      // Frame size = (sample_rate * quantization * channels)/4800
 50.1306 +      buff[10+FHS]=outlen*(4800/16/2)/sr;
 50.1307 +      outlen+=(sizeof(header)-6);
 50.1308 +      buff[4+FHS]=outlen>>8;
 50.1309 +      buff[5+FHS]=outlen;
 50.1310 +      f=new cFrame(buff,-(outlen+6+FHS),ftUnknown,index);
 50.1311 +      }
 50.1312 +    if(!f) free(buff);
 50.1313 +    }
 50.1314 +  return f;
 50.1315 +}
 50.1316 +
 50.1317 +#ifdef BROKEN_PCM
 50.1318 +#include "player-mp3-sample.c"
 50.1319 +#endif
 50.1320 +
 50.1321 +int cOutputDvb::Output(const unsigned char *Data, int Len, bool SOF)
 50.1322 +{
 50.1323 +  int n=0;
 50.1324 +  if(SOF) {
 50.1325 +#ifdef BROKEN_PCM
 50.1326 +    struct FrameHeader *fh=(struct FrameHeader *)Data;
 50.1327 +    if(fh->samplerate!=outSr) {
 50.1328 +      if(outSr) {
 50.1329 +        // at this point we would need access to AUDIO_STOP/AUDIO_PLAY
 50.1330 +        // ioctl, but unfortunaly VDR's API doesn't provides this.
 50.1331 +        // So we have to do magic to make the driver switch samplerate.
 50.1332 +        const unsigned char *p=testAudio;
 50.1333 +        int pc=sizeof(testAudio);
 50.1334 +        int r;
 50.1335 +        do {
 50.1336 +#if APIVERSNUM < 10318
 50.1337 +          r=player->PlayVideo(p,pc);
 50.1338 +#else
 50.1339 +          r=player->PlayPes(p,pc);
 50.1340 +#endif
 50.1341 +          if(r>0) { p+=r; pc-=r; }
 50.1342 +          if(r==0) Poll();
 50.1343 +          } while(r>=0 && pc>0);
 50.1344 +        }
 50.1345 +      outSr=fh->samplerate;
 50.1346 +      d(printf("mp3-dvb: output samplerate now %d\n",outSr))
 50.1347 +      }
 50.1348 +#endif
 50.1349 +    n=FHS;
 50.1350 +    Data+=n; Len-=n;
 50.1351 +    }
 50.1352 +#if APIVERSNUM < 10318
 50.1353 +  int r=player->PlayVideo(Data,Len);
 50.1354 +#else
 50.1355 +  int r=player->PlayPes(Data,Len);
 50.1356 +#endif
 50.1357 +  return (r>=0 ? r+n : -1);
 50.1358 +}
 50.1359 +
 50.1360 +bool cOutputDvb::Poll(void)
 50.1361 +{
 50.1362 +  return player->DevicePoll(poll,500);
 50.1363 +}
 50.1364 +
 50.1365 +void cOutputDvb::Play(void)
 50.1366 +{
 50.1367 +#ifndef BROKEN_PCM
 50.1368 +  player->DevicePlay();
 50.1369 +#endif
 50.1370 +}
 50.1371 +
 50.1372 +void cOutputDvb::Pause(void)
 50.1373 +{
 50.1374 +#ifndef BROKEN_PCM
 50.1375 +  player->DeviceFreeze();
 50.1376 +#endif
 50.1377 +}
 50.1378 +
 50.1379 +// --- cOutputOss --------------------------------------------------------------
 50.1380 +
 50.1381 +#ifdef WITH_OSS
 50.1382 +
 50.1383 +const char *dspdevice="/dev/dsp";
 50.1384 +
 50.1385 +class cOutputOss : public cOutput {
 50.1386 +private:
 50.1387 +  int fd;
 50.1388 +  cPoller poll;
 50.1389 +  unsigned int outSr;
 50.1390 +  unsigned char buff[8192];
 50.1391 +  //
 50.1392 +  bool Reset(unsigned int sr);
 50.1393 +public:
 50.1394 +  cOutputOss(cMP3Player *Player);
 50.1395 +  virtual ~cOutputOss();
 50.1396 +  virtual void Init(void);
 50.1397 +  virtual unsigned int SampleRate(unsigned int PcmSampleRate);
 50.1398 +  virtual cFrame *MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr);
 50.1399 +  virtual int Output(const unsigned char *Data, int Len, bool SOF);
 50.1400 +  virtual bool Poll(void);
 50.1401 +  virtual void Play(void);
 50.1402 +  virtual void Pause(void);
 50.1403 +  };
 50.1404 +
 50.1405 +cOutputOss::cOutputOss(cMP3Player *Player)
 50.1406 +:cOutput(Player)
 50.1407 +{
 50.1408 +  fd=-1; outSr=0;
 50.1409 +  d(printf("mp3-oss: using OSS output\n"))
 50.1410 +}
 50.1411 +
 50.1412 +cOutputOss::~cOutputOss()
 50.1413 +{
 50.1414 +  close(fd);
 50.1415 +}
 50.1416 +
 50.1417 +void cOutputOss::Init(void)
 50.1418 +{
 50.1419 +  if(fd<0) {
 50.1420 +    fd=open(dspdevice,O_WRONLY|O_NONBLOCK);
 50.1421 +    if(fd>=0) poll.Add(fd,true);
 50.1422 +    else esyslog("ERROR: Cannot open dsp device '%s': %s!",dspdevice,strerror(errno));
 50.1423 +    }
 50.1424 +  cOutput::Init();
 50.1425 +}
 50.1426 +
 50.1427 +bool cOutputOss::Reset(unsigned int sr)
 50.1428 +{
 50.1429 +  if(fd>=0) {
 50.1430 +    CHECK(ioctl(fd,SNDCTL_DSP_SYNC,0));
 50.1431 +    int format=AFMT_S16_LE;
 50.1432 +    CHECK(ioctl(fd,SNDCTL_DSP_SETFMT,&format));
 50.1433 +    if(format==AFMT_S16_LE) {
 50.1434 +      int channels=2;
 50.1435 +      CHECK(ioctl(fd,SNDCTL_DSP_CHANNELS,&channels));
 50.1436 +      if(channels==2) {
 50.1437 +        int real=sr;
 50.1438 +        CHECK(ioctl(fd,SNDCTL_DSP_SPEED,&real));
 50.1439 +        d(printf("oss: DSP samplerate now %d\n",real))
 50.1440 +        if(abs(real-sr)<sr/50) {
 50.1441 +          outSr=sr;
 50.1442 +          d(printf("mp3-oss: DSP reset done\n"))
 50.1443 +          return true;
 50.1444 +          }
 50.1445 +        else {
 50.1446 +          d(printf("mp3-oss: driver can't handle samplerate %d, got %d\n",sr,real))
 50.1447 +          esyslog("ERROR: OSS driver can't handle samplerate %d, got %d\n",sr,real);
 50.1448 +          }
 50.1449 +        }
 50.1450 +      else {
 50.1451 +        d(printf("mp3-oss: 2-channel stereo not supported\n"))
 50.1452 +        esyslog("ERROR: OSS driver doesn't support 2-channel stereo.");
 50.1453 +        }
 50.1454 +      }
 50.1455 +    else {
 50.1456 +      d(printf("mp3-oss: little-endian samples not supported\n"))
 50.1457 +      esyslog("ERROR: OSS driver doesn't support 16-bit little-endian samples.");
 50.1458 +      }
 50.1459 +    close(fd); fd=-1;
 50.1460 +    }
 50.1461 +  return false;
 50.1462 +}
 50.1463 +
 50.1464 +unsigned int cOutputOss::SampleRate(unsigned int PcmSampleRate)
 50.1465 +{
 50.1466 +  return PcmSampleRate;
 50.1467 +}
 50.1468 +
 50.1469 +cFrame *cOutputOss::MakeFrame(unsigned int & Samples, const mad_fixed_t **Data, int index, int sr)
 50.1470 +{
 50.1471 +  struct FrameHeader *fh=(struct FrameHeader *)buff;
 50.1472 +  fh->samplerate=sr;
 50.1473 +  cFrame *f=0;
 50.1474 +  unsigned int outlen=scale.ScaleBlock(buff+FHS,sizeof(buff)-FHS,Samples,Data[0],Data[1],MP3Setup.AudioMode?amDitherLE:amRoundLE);
 50.1475 +  if(outlen) 
 50.1476 +    f=new cFrame(buff,outlen+FHS,ftUnknown,index);
 50.1477 +  return f;
 50.1478 +}
 50.1479 +
 50.1480 +int cOutputOss::Output(const unsigned char *Data, int Len, bool SOF)
 50.1481 +{
 50.1482 +  if(fd>=0) {
 50.1483 +    int n=0;
 50.1484 +    if(SOF) {
 50.1485 +      struct FrameHeader *fh=(struct FrameHeader *)Data;
 50.1486 +      if(fh->samplerate!=outSr) Reset(fh->samplerate);
 50.1487 +      n=FHS;
 50.1488 +      Data+=n; Len-=n;
 50.1489 +      }
 50.1490 +    int r=write(fd,Data,Len);
 50.1491 +    if(r<0 && !FATALERRNO) r=0;
 50.1492 +    if(r>=0) return n+r;
 50.1493 +    }
 50.1494 +  return -1;
 50.1495 +}
 50.1496 +
 50.1497 +bool cOutputOss::Poll(void)
 50.1498 +{
 50.1499 +  return fd>=0 ? poll.Poll(500) : false;
 50.1500 +}
 50.1501 +
 50.1502 +void cOutputOss::Play(void)
 50.1503 +{
 50.1504 +}
 50.1505 +
 50.1506 +void cOutputOss::Pause(void)
 50.1507 +{
 50.1508 +  CHECK(ioctl(fd,SNDCTL_DSP_POST,0));
 50.1509 +}
 50.1510 +
 50.1511 +#endif
 50.1512 +
 50.1513 +// --- cMP3Player --------------------------------------------------------------
 50.1514 +
 50.1515 +cMP3Player::cMP3Player()
 50.1516 +:cPlayer(MP3Setup.BackgrMode==1 ? pmAudioOnly : pmAudioOnlyBlack)
 50.1517 +{
 50.1518 +  active=true; started=false; isStream=false;
 50.1519 +  ringBuffer=new cRingBufferFrame(MP3BUFSIZE);
 50.1520 +  rframe=0; pframe=0; decoder=0; output=0;
 50.1521 +  playMode=pmStartup; state=msStop;
 50.1522 +  playindex=total=0;
 50.1523 +}
 50.1524 +
 50.1525 +cMP3Player::~cMP3Player()
 50.1526 +{
 50.1527 +  Detach();
 50.1528 +  delete ringBuffer;
 50.1529 +}
 50.1530 +
 50.1531 +void cMP3Player::Activate(bool On)
 50.1532 +{
 50.1533 +  if(On) {
 50.1534 +    d(printf("mp3: player active true requested...\n"))
 50.1535 +    if(!started) {
 50.1536 +      playMode=pmStartup; Start(); started=true;
 50.1537 +      playModeMutex.Lock();
 50.1538 +      WaitPlayMode(pmStartup,true); // wait for the decoder to become ready
 50.1539 +      playModeMutex.Unlock();
 50.1540 +      Lock();
 50.1541 +      Play();
 50.1542 +      Unlock();
 50.1543 +      }
 50.1544 +    d(printf("mp3: player active true done\n"))
 50.1545 +    }
 50.1546 +  else if(started && active) {
 50.1547 +    d(printf("mp3: player active false requested...\n"))
 50.1548 +    Lock(); StopPlay(); Unlock();
 50.1549 +    active=false;
 50.1550 +    SetPlayMode(pmStartup);
 50.1551 +    Cancel(2);
 50.1552 +    d(printf("mp3: player active false done\n"))
 50.1553 +    }
 50.1554 +}
 50.1555 +
 50.1556 +void cMP3Player::SetPlayMode(ePlayMode mode)
 50.1557 +{
 50.1558 +  playModeMutex.Lock();
 50.1559 +  if(mode!=playMode) {
 50.1560 +    playMode=mode;
 50.1561 +    dm(printf("mp3: setting mode=%d (pid=%d)\n",mode,getpid()))
 50.1562 +    playModeCond.Broadcast();
 50.1563 +    }
 50.1564 +  playModeMutex.Unlock();
 50.1565 +}
 50.1566 +
 50.1567 +void cMP3Player::WaitPlayMode(ePlayMode mode, bool inv)
 50.1568 +{
 50.1569 +  // must be called with playModeMutex LOCKED !!!
 50.1570 +
 50.1571 +  while(active && ((!inv && mode!=playMode) || (inv && mode==playMode))) {
 50.1572 +    dm(printf("mp3: entering wait for mode%s%d with mode=%d (pid=%d)\n",inv?"!=":"==",mode,playMode,getpid()))
 50.1573 +    playModeCond.Wait(playModeMutex);
 50.1574 +    dm(printf("mp3: returning from wait with mode=%d (pid=%d)\n",playMode,getpid()))
 50.1575 +    }
 50.1576 +}
 50.1577 +
 50.1578 +void cMP3Player::Action(void)
 50.1579 +{
 50.1580 +  cSong *playing=0;
 50.1581 +  struct mad_pcm *pcm=0;
 50.1582 +  cResample resample[2];
 50.1583 +  unsigned int nsamples[2];
 50.1584 +  const mad_fixed_t *data[2];
 50.1585 +  cLevel level;
 50.1586 +  cNormalize norm;
 50.1587 +  bool haslevel=false;
 50.1588 +  const unsigned char *p=0;
 50.1589 +  int pc=0, readindex=0;
 50.1590 +  bool imageValid=true;
 50.1591 +  int imageCheck=0;
 50.1592 +#ifdef DEBUG
 50.1593 +  int beat=0;
 50.1594 +#endif
 50.1595 +#ifdef DEBUG_DELAY
 50.1596 +  int lastwrite=0;
 50.1597 +#endif
 50.1598 +
 50.1599 +  dsyslog("mp3: player thread started (pid=%d)", getpid());
 50.1600 +  state=msStop;
 50.1601 +  SetPlayMode(pmStopped);
 50.1602 +
 50.1603 +  delete output; output=0;
 50.1604 +#ifdef WITH_OSS
 50.1605 +  if(MP3Setup.AudioOutMode==AUDIOOUTMODE_OSS) output=new cOutputOss(this);
 50.1606 +#endif
 50.1607 +  if(MP3Setup.AudioOutMode==AUDIOOUTMODE_DVB) output=new cOutputDvb(this);
 50.1608 +  if(!output) {
 50.1609 +    d(printf("mp3: audiooutmode mismatch or no output driver\n"))
 50.1610 +    esyslog("ERROR: no audio output driver. balling out");
 50.1611 +    goto abort;
 50.1612 +    }
 50.1613 +
 50.1614 +  while(active) {
 50.1615 +#ifdef DEBUG
 50.1616 +    {
 50.1617 +    int now=time(0);
 50.1618 +    if(now>=beat) {
 50.1619 +      int avail=ringBuffer->Available();
 50.1620 +      printf("mp3: heartbeat buffer=%d now=%d\n",avail,now&4095);
 50.1621 +      //output->Stats(); if(haslevel) norm.Stats();
 50.1622 +      beat=now+(avail>(MP3BUFSIZE*10/100) ? (avail<(MP3BUFSIZE*50/100) ? 2 : 20) : 1);
 50.1623 +      }
 50.1624 +    }
 50.1625 +#endif
 50.1626 +
 50.1627 +    Lock();
 50.1628 +
 50.1629 +next:
 50.1630 +    if(!pframe && playing && !imageValid && imageCheck<time_ms()) {
 50.1631 +      unsigned char *mem;
 50.1632 +      int len;
 50.1633 +      imageCheck=time_ms()+250;
 50.1634 +      imageValid=playing->Image(mem,len);
 50.1635 +      if(mem) {
 50.1636 +        if(playindex) SLEEP(80); // stillpicture ioctl freezes without this
 50.1637 +        DeviceStillPicture(mem,len);
 50.1638 +        free(mem);
 50.1639 +        }
 50.1640 +      }
 50.1641 +
 50.1642 +    bool SOF=false;
 50.1643 +    if(!pframe && playMode==pmPlay) {
 50.1644 +      pframe=ringBuffer->Get();
 50.1645 +      if(pframe) {
 50.1646 +        playindex=pframe->Index();
 50.1647 +        p=pframe->Data();
 50.1648 +        pc=pframe->Count();
 50.1649 +        SOF=true;
 50.1650 +        }
 50.1651 +      }
 50.1652 +
 50.1653 +    if(pframe) {
 50.1654 +#ifdef DEBUG_DELAY
 50.1655 +      {
 50.1656 +      int now=time_ms();
 50.1657 +      if(lastwrite && lastwrite<now-(DEBUG_DELAY+50))
 50.1658 +        printf("mp3: write delayed %d ms\n",now-lastwrite);
 50.1659 +      lastwrite=now;
 50.1660 +      }
 50.1661 +#endif
 50.1662 +      int w=output->Output(p,pc,SOF);
 50.1663 +      if(w>0) {
 50.1664 +        p+=w; pc-=w;
 50.1665 +        if(pc<=0) {
 50.1666 +          ringBuffer->Drop(pframe);
 50.1667 +          pframe=0;
 50.1668 +          goto next;
 50.1669 +          }
 50.1670 +        }
 50.1671 +      else if(w<0 && FATALERRNO) {
 50.1672 +        LOG_ERROR;
 50.1673 +        d(printf("mp3: output failed: %s\n",strerror(errno)))
 50.1674 +        Unlock();
 50.1675 +        goto abort;
 50.1676 +        }
 50.1677 +      }
 50.1678 +
 50.1679 +    if(mgr->NewCurrent() && playMode==pmPlay && state!=msStart) {
 50.1680 +      Empty();
 50.1681 +      state=msRestart;
 50.1682 +      d(printf("mp3: stale song change, restart.\n"))
 50.1683 +      }
 50.1684 +
 50.1685 +    if(!rframe && playMode==pmPlay) {
 50.1686 +      switch(state) {
 50.1687 +        case msStart:
 50.1688 +          d(printf("mp3: starting play\n"))
 50.1689 +          mgr->Throttle(true);
 50.1690 +          playindex=readindex=total=0;
 50.1691 +          playing=mgr->Current();
 50.1692 +          if(playing) {
 50.1693 +            if((decoder=playing->Decoder()) && decoder->Start()) {
 50.1694 +              isStream=decoder->IsStream(); levelgood=!isStream; haslevel=false;
 50.1695 +              cSongInfo *si=playing->Info(true);
 50.1696 +              if(si) {
 50.1697 +                if(si->Level>0.0) {
 50.1698 +                  d(printf("mp3: found song level=%f peak=%f\n",si->Level,si->Peak))
 50.1699 +                  haslevel=true;
 50.1700 +                  norm.Init(si->Level,si->Peak);
 50.1701 +                  }
 50.1702 +                if(si->HasInfo())
 50.1703 +                  total=SecondsToFrames(si->Total);
 50.1704 +                }
 50.1705 +              d(printf("mp3: isStream=%d levelgood=%d haslevel=%d\n",isStream,levelgood,haslevel))
 50.1706 +              output->Init();
 50.1707 +              level.Init();
 50.1708 +              if(MP3Setup.BackgrMode==2) imageValid=false;
 50.1709 +              state=msDecode;
 50.1710 +              break;
 50.1711 +              }
 50.1712 +            else
 50.1713 +              esyslog("ERROR: playlist entry %s is not a valid file",playing->Name());
 50.1714 +            }
 50.1715 +          else
 50.1716 +            d(printf("mp3: no current on start play\n"))
 50.1717 +          state=msEof;
 50.1718 +          break;
 50.1719 +        case msDecode:
 50.1720 +          {
 50.1721 +#ifdef DEBUG_DELAY
 50.1722 +          int now=time_ms();
 50.1723 +#endif
 50.1724 +          struct Decode *ds=decoder->Decode();
 50.1725 +#ifdef DEBUG_DELAY
 50.1726 +          now=time_ms()-now;
 50.1727 +          if(now>DEBUG_DELAY) printf("mp3: decode delayed %d ms\n",now);
 50.1728 +#endif
 50.1729 +          switch(ds->status) {
 50.1730 +            case dsPlay:
 50.1731 +              pcm=ds->pcm;
 50.1732 +              readindex=ds->index;
 50.1733 +              state=msNormalize;
 50.1734 +              break;
 50.1735 +            case dsSkip:
 50.1736 +            case dsSoftError:
 50.1737 +              // skipping, state unchanged, next decode
 50.1738 +              break;
 50.1739 +            case dsEof:
 50.1740 +              if(!haslevel && levelgood) { // save level & peak to infocache on eof
 50.1741 +                double l=level.GetLevel();
 50.1742 +                if(l>0.0) {
 50.1743 +                  cSongInfo *si=decoder->SongInfo(false);
 50.1744 +                  cFileInfo *fi=decoder->FileInfo();
 50.1745 +                  if(si && fi) {
 50.1746 +                    si->Level=l;
 50.1747 +                    si->Peak=level.GetPeak();
 50.1748 +                    InfoCache.Cache(si,fi);
 50.1749 +                    }
 50.1750 +                  }
 50.1751 +                }
 50.1752 +              state=msEof;
 50.1753 +              break;
 50.1754 +            case dsOK:
 50.1755 +            case dsError:
 50.1756 +              state=msError;
 50.1757 +              break;
 50.1758 +            }
 50.1759 +          break;
 50.1760 +          }
 50.1761 +        case msNormalize:
 50.1762 +          if(!haslevel) { if(levelgood) level.GetPower(pcm); }
 50.1763 +          else norm.AddGain(pcm);
 50.1764 +          state=msResample;
 50.1765 +          break;
 50.1766 +        case msResample:
 50.1767 +#ifdef DEBUG
 50.1768 +          {
 50.1769 +          static unsigned int oldrate=0;
 50.1770 +          if(oldrate!=pcm->samplerate) {
 50.1771 +            printf("mp3: new input sample rate %d\n",pcm->samplerate);
 50.1772 +            oldrate=pcm->samplerate;
 50.1773 +            }
 50.1774 +          }
 50.1775 +#endif
 50.1776 +          nsamples[0]=nsamples[1]=pcm->length;
 50.1777 +          data[0]=pcm->samples[0];
 50.1778 +          data[1]=pcm->channels>1 ? pcm->samples[1]:0;
 50.1779 +
 50.1780 +          dvbSampleRate=output->SampleRate(pcm->samplerate);
 50.1781 +          if(dvbSampleRate!=pcm->samplerate) {
 50.1782 +            if(resample[0].SetInputRate(pcm->samplerate,dvbSampleRate)) {
 50.1783 +              nsamples[0]=resample[0].ResampleBlock(nsamples[0],data[0]);
 50.1784 +              data[0]    =resample[0].Resampled();
 50.1785 +              }
 50.1786 +            if(data[1] && resample[1].SetInputRate(pcm->samplerate,dvbSampleRate)) {
 50.1787 +              nsamples[1]=resample[1].ResampleBlock(nsamples[1],data[1]);
 50.1788 +              data[1]    =resample[1].Resampled();
 50.1789 +              }
 50.1790 +            }
 50.1791 +          state=msOutput;
 50.1792 +          break;
 50.1793 +        case msOutput:
 50.1794 +          if(nsamples[0]>0) rframe=output->MakeFrame(nsamples[0],data,readindex,dvbSampleRate);
 50.1795 +          else state=msDecode;
 50.1796 +          break;
 50.1797 +        case msError:
 50.1798 +        case msEof:
 50.1799 +          d(printf("mp3: eof or error\n"))
 50.1800 +          state=msWait;
 50.1801 +          // fall through
 50.1802 +        case msRestart:
 50.1803 +        case msStop:
 50.1804 +          d(printf("mp3: stopping play\n"))
 50.1805 +          if(decoder) { decoder->Stop(); decoder=0; }
 50.1806 +          mgr->Release(); playing=0; imageValid=true;
 50.1807 +          levelgood=false;
 50.1808 +#ifdef DEBUG
 50.1809 +          output->Stats(); if(haslevel) norm.Stats();
 50.1810 +#endif
 50.1811 +          if(state==msStop) SetPlayMode(pmStopped);
 50.1812 +          if(state==msRestart) state=msStart;
 50.1813 +          break;
 50.1814 +        case msWait:
 50.1815 +          if(ringBuffer->Available()==0) {
 50.1816 +            if(mgr->NextCurrent()) {
 50.1817 +              d(printf("mp3: playing next\n"))
 50.1818 +              state=msStart;
 50.1819 +              }
 50.1820 +            else {
 50.1821 +              d(printf("mp3: end of playlist\n"))
 50.1822 +              if(MP3Setup.AbortAtEOL) {
 50.1823 +                active=false;
 50.1824 +                d(printf("mp3: aborting player...\n"))
 50.1825 +                }
 50.1826 +              else d(printf("mp3: player idle...\n"))
 50.1827 +              SetPlayMode(pmStopped);
 50.1828 +              }
 50.1829 +            }
 50.1830 +          break;
 50.1831 +        }
 50.1832 +      }
 50.1833 +
 50.1834 +    if(rframe && ringBuffer->Put(rframe)) rframe=0;
 50.1835 +
 50.1836 +    Unlock();
 50.1837 +
 50.1838 +    if((rframe || state==msWait) && pframe) {
 50.1839 +      mgr->Throttle(false);
 50.1840 +      output->Poll();
 50.1841 +      }
 50.1842 +    else if(playMode!=pmPlay) {
 50.1843 +      mgr->Throttle(false);
 50.1844 +      if(!imageValid)
 50.1845 +        SLEEP(100);
 50.1846 +      else {
 50.1847 +        playModeMutex.Lock();
 50.1848 +        if(playMode!=pmPlay) WaitPlayMode(playMode,true);
 50.1849 +        playModeMutex.Unlock();
 50.1850 +        }
 50.1851 +#ifdef DEBUG_DELAY
 50.1852 +      lastwrite=0;
 50.1853 +#endif
 50.1854 +      }
 50.1855 +    else if(state!=msWait && ringBuffer->Available()<(MP3BUFSIZE*50/100)) {
 50.1856 +      mgr->Throttle(true);
 50.1857 +      }
 50.1858 +    }
 50.1859 +
 50.1860 +abort:
 50.1861 +  Lock();
 50.1862 +  delete rframe;
 50.1863 +  delete output; output=0;
 50.1864 +  if(decoder) { decoder->Stop(); decoder=0; }
 50.1865 +  mgr->Release(); playing=0;
 50.1866 +  SetPlayMode(pmStopped);
 50.1867 +  Unlock();
 50.1868 +  active=false;
 50.1869 +
 50.1870 +  dsyslog("mp3: player thread ended (pid=%d)", getpid());
 50.1871 +}
 50.1872 +
 50.1873 +void cMP3Player::Empty(void)
 50.1874 +{
 50.1875 +  Lock();
 50.1876 +  delete rframe; rframe=0; pframe=0;
 50.1877 +  ringBuffer->Clear();
 50.1878 +  DeviceClear();
 50.1879 +  Unlock();
 50.1880 +}
 50.1881 +
 50.1882 +void cMP3Player::StopPlay(void) // StopPlay() must be called in locked state!!!
 50.1883 +{
 50.1884 +  if(playMode!=pmStopped) {
 50.1885 +    Empty();
 50.1886 +    state=msStop;
 50.1887 +    SetPlayMode(pmPlay);
 50.1888 +    Unlock();                 // let the decode thread process the stop signal
 50.1889 +    playModeMutex.Lock();
 50.1890 +    WaitPlayMode(pmStopped,false);
 50.1891 +    playModeMutex.Unlock();
 50.1892 +    Lock();
 50.1893 +    }
 50.1894 +}
 50.1895 +
 50.1896 +void cMP3Player::Pause(void)
 50.1897 +{
 50.1898 +  Lock();
 50.1899 +  if(playMode==pmPaused) Play();
 50.1900 +  else if(playMode==pmPlay && !isStream) {
 50.1901 +    d(printf("mp3: pause\n"))
 50.1902 +    if(output) output->Pause();
 50.1903 +    SetPlayMode(pmPaused);
 50.1904 +    }
 50.1905 +  Unlock();
 50.1906 +}
 50.1907 +
 50.1908 +void cMP3Player::Play(void)
 50.1909 +{
 50.1910 +  Lock();
 50.1911 +  if(playMode!=pmPlay) {
 50.1912 +    d(printf("mp3: play\n"))
 50.1913 +    if(playMode==pmStopped) state=msStart;
 50.1914 +    if(output) output->Play();
 50.1915 +    SetPlayMode(pmPlay);
 50.1916 +    }
 50.1917 +  Unlock();
 50.1918 +}
 50.1919 +
 50.1920 +bool cMP3Player::PrevCheck(void)
 50.1921 +{
 50.1922 +  bool res=false;
 50.1923 +  Lock();
 50.1924 +  if(playindex>=2000 && !isStream) {
 50.1925 +    state=msRestart; res=true;
 50.1926 +    Empty();
 50.1927 +    d(printf("mp3: skip to start of song\n"))
 50.1928 +    }
 50.1929 +  Unlock();
 50.1930 +  return res;
 50.1931 +}
 50.1932 +
 50.1933 +void cMP3Player::SkipSeconds(int secs)
 50.1934 +{
 50.1935 +  if(playMode!=pmStopped && !isStream) {
 50.1936 +    Lock();
 50.1937 +    d(printf("mp3: skip secs %d\n",secs))
 50.1938 +    if(playMode==pmPaused) SetPlayMode(pmPlay);
 50.1939 +    float bufsecs=(float)ringBuffer->Available() / (float)(dvbSampleRate*OUT_FACT);
 50.1940 +    d(printf("mp3: ringbuffer available %f secs\n",bufsecs))
 50.1941 +    if(secs>0 && bufsecs>=(float)secs) {
 50.1942 +      // clear intermediate queue
 50.1943 +      if(pframe) {
 50.1944 +        ringBuffer->Drop(pframe);
 50.1945 +        pframe=0;
 50.1946 +        }
 50.1947 +      DeviceClear();
 50.1948 +      // skip inside ringbuffer
 50.1949 +      int skipindex=playindex+secs*1000;
 50.1950 +      d(printf("mp3: skipping play=%d skip=%d ...",playindex,skipindex))
 50.1951 +      cFrame *f;
 50.1952 +      do {
 50.1953 +        f=ringBuffer->Get();
 50.1954 +        if(f) {
 50.1955 +          playindex=f->Index();
 50.1956 +          ringBuffer->Drop(f);
 50.1957 +          d(printf("*"))
 50.1958 +          }
 50.1959 +        } while(f && playindex<skipindex);
 50.1960 +      d(printf("\nmp3: skipped play=%d skip=%d\n",playindex,skipindex))
 50.1961 +      }
 50.1962 +    else {
 50.1963 +      if(decoder && decoder->Skip(secs,bufsecs)) levelgood=false;
 50.1964 +      Empty();
 50.1965 +      }
 50.1966 +    Unlock();
 50.1967 +    }
 50.1968 +}
 50.1969 +
 50.1970 +bool cMP3Player::GetIndex(int &Current, int &Total, bool SnapToIFrame)
 50.1971 +{
 50.1972 +  Current=SecondsToFrames(playindex/1000); Total=total;
 50.1973 +  return total>=0;
 50.1974 +}
 50.1975 +
 50.1976 +bool cMP3Player::GetReplayMode(bool &Play, bool &Forward, int &Speed)
 50.1977 +{
 50.1978 +  Play=(playMode==pmPlay);
 50.1979 +  Forward=true;
 50.1980 +  Speed=-1;
 50.1981 +  return true;
 50.1982 +}
    51.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    51.2 +++ b/player-mp3.h	Sat Dec 29 14:47:40 2007 +0100
    51.3 @@ -0,0 +1,141 @@
    51.4 +/*
    51.5 + * MP3/MPlayer plugin to VDR (C++)
    51.6 + *
    51.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    51.8 + *
    51.9 + * This code is free software; you can redistribute it and/or
   51.10 + * modify it under the terms of the GNU General Public License
   51.11 + * as published by the Free Software Foundation; either version 2
   51.12 + * of the License, or (at your option) any later version.
   51.13 + *
   51.14 + * This code is distributed in the hope that it will be useful,
   51.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   51.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   51.17 + * GNU General Public License for more details.
   51.18 + *
   51.19 + * You should have received a copy of the GNU General Public License
   51.20 + * along with this program; if not, write to the Free Software
   51.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   51.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   51.23 + */
   51.24 +
   51.25 +#ifndef ___DVB_MP3_H
   51.26 +#define ___DVB_MP3_H
   51.27 +
   51.28 +#include <vdr/thread.h>
   51.29 +#include <vdr/player.h>
   51.30 +
   51.31 +// -------------------------------------------------------------------
   51.32 +
   51.33 +class cRingBufferFrame;
   51.34 +class cFrame;
   51.35 +
   51.36 +class cPlayList;
   51.37 +class cSong;
   51.38 +class cSongInfo;
   51.39 +class cBackgroundScan;
   51.40 +class cDecoder;
   51.41 +class cOutput;
   51.42 +class cOutputDvb;
   51.43 +class cShuffle;
   51.44 +
   51.45 +// -------------------------------------------------------------------
   51.46 +
   51.47 +class cMP3PlayInfo {
   51.48 +public:
   51.49 +  char Title[64], Artist[64], Album[64], SMode[32], Filename[256];
   51.50 +  int Year, SampleFreq, Bitrate, MaxBitrate;
   51.51 +  int Num, MaxNum;
   51.52 +  // not in hash
   51.53 +  bool Loop, Shuffle;
   51.54 +  int Hash;
   51.55 +  };
   51.56 +
   51.57 +// -------------------------------------------------------------------
   51.58 +
   51.59 +class cPlayManager : public cThread {
   51.60 +private:
   51.61 +  cMutex listMutex;
   51.62 +  cCondVar fgCond, bgCond;
   51.63 +  cList<cSong> list;
   51.64 +  cSong *curr;
   51.65 +  int currIndex, maxIndex;
   51.66 +  //
   51.67 +  cSong *play;
   51.68 +  bool playNew, eol;
   51.69 +  //
   51.70 +  cShuffle *shuffle;
   51.71 +  bool shuffleMode, loopMode;
   51.72 +  //
   51.73 +  cSong *scan;
   51.74 +  bool stopscan, throttle, pass2, release;
   51.75 +  //
   51.76 +  virtual void Action(void);
   51.77 +  void NoScan(cSong *nono);
   51.78 +  void NoPlay(cSong *nono);
   51.79 +  void ThrottleWait(void);
   51.80 +public:
   51.81 +  cPlayManager(void);
   51.82 +  ~cPlayManager();
   51.83 +  // Control interface (to be called from frontend thread only!)
   51.84 +  void Flush(void);
   51.85 +  void Add(cPlayList *pl);
   51.86 +  bool Next(void);
   51.87 +  bool Prev(void);
   51.88 +  void Goto(int num);
   51.89 +  void ToggleShuffle(void);
   51.90 +  void ToggleLoop(void);
   51.91 +  bool Info(int num, cMP3PlayInfo *info);
   51.92 +  void Halt(void);
   51.93 +  // Player interface (to be called from player thread only!)
   51.94 +  cSong *Current(void);
   51.95 +  bool NewCurrent(void);
   51.96 +  bool NextCurrent(void);
   51.97 +  void Release(void);
   51.98 +  void Throttle(bool thr);
   51.99 +  };
  51.100 +
  51.101 +extern cPlayManager *mgr;
  51.102 +
  51.103 +// -------------------------------------------------------------------
  51.104 +
  51.105 +class cMP3Player : public cPlayer, cThread {
  51.106 +friend class cOutputDvb;
  51.107 +private:
  51.108 +  bool active, started;
  51.109 +  cRingBufferFrame *ringBuffer;
  51.110 +  cMutex playModeMutex;
  51.111 +  cCondVar playModeCond;
  51.112 +//
  51.113 +  int playindex, total;
  51.114 +  cDecoder *decoder;
  51.115 +  cOutput *output;
  51.116 +  cFrame *rframe, *pframe;
  51.117 +  enum ePlayMode { pmPlay, pmStopped, pmPaused, pmStartup };
  51.118 +  ePlayMode playMode;
  51.119 +  enum eState { msStart, msStop, msDecode, msNormalize, msResample, msOutput, msError, msEof, msWait, msRestart };
  51.120 +  eState state;
  51.121 +  bool levelgood, isStream;
  51.122 +  unsigned int dvbSampleRate;
  51.123 +//
  51.124 +  void Empty(void);
  51.125 +  void StopPlay(void);
  51.126 +  void SetPlayMode(ePlayMode mode);
  51.127 +  void WaitPlayMode(ePlayMode mode, bool inv);
  51.128 +protected:
  51.129 +  virtual void Activate(bool On);
  51.130 +  virtual void Action(void);
  51.131 +public:
  51.132 +  cMP3Player(void);
  51.133 +  virtual ~cMP3Player();
  51.134 +  void Pause(void);
  51.135 +  void Play(void);
  51.136 +  bool PrevCheck(void);
  51.137 +  void SkipSeconds(int secs);
  51.138 +  virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false);
  51.139 +  virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
  51.140 +  bool Active(void) { return active; }
  51.141 +  bool IsStream(void) { return isStream; }
  51.142 +  };
  51.143 +
  51.144 +#endif //___DVB_MP3_H
    52.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    52.2 +++ b/player-mplayer.c	Sat Dec 29 14:47:40 2007 +0100
    52.3 @@ -0,0 +1,694 @@
    52.4 +/*
    52.5 + * MP3/MPlayer plugin to VDR (C++)
    52.6 + *
    52.7 + * (C) 2001-2007 Stefan Huelswitt <s.huelswitt@gmx.de>
    52.8 + *
    52.9 + * This code is free software; you can redistribute it and/or
   52.10 + * modify it under the terms of the GNU General Public License
   52.11 + * as published by the Free Software Foundation; either version 2
   52.12 + * of the License, or (at your option) any later version.
   52.13 + *
   52.14 + * This code is distributed in the hope that it will be useful,
   52.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   52.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   52.17 + * GNU General Public License for more details.
   52.18 + *
   52.19 + * You should have received a copy of the GNU General Public License
   52.20 + * along with this program; if not, write to the Free Software
   52.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   52.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   52.23 + */
   52.24 +
   52.25 +#include <ctype.h>
   52.26 +#include <stdlib.h>
   52.27 +#include <stdio.h>
   52.28 +#include <stdarg.h>
   52.29 +#include <string.h>
   52.30 +#include <fcntl.h>
   52.31 +#include <errno.h>
   52.32 +#include <sys/ioctl.h>
   52.33 +#include <sys/types.h>
   52.34 +#include <sys/poll.h>
   52.35 +#include <unistd.h>
   52.36 +#include <signal.h>
   52.37 +#include <wait.h>
   52.38 +#include <math.h>
   52.39 +#include <locale.h>
   52.40 +
   52.41 +#include <vdr/device.h>
   52.42 +#include <vdr/videodir.h>
   52.43 +
   52.44 +#include "common.h"
   52.45 +#include "data.h"
   52.46 +#include "player-mplayer.h"
   52.47 +#include "setup-mplayer.h"
   52.48 +
   52.49 +//#define DEBUG_SLAVE
   52.50 +
   52.51 +#define MPLAYER_VOL_STEP 3.0
   52.52 +
   52.53 +const char *MPlayerCmd = "mplayer.sh";
   52.54 +int MPlayerAid=-1;
   52.55 +const char *globalResumeDir = 0;
   52.56 +
   52.57 +// -- cMPlayerStatus -----------------------------------------------------------
   52.58 +
   52.59 +cMPlayerStatus *status;
   52.60 +
   52.61 +cMPlayerStatus::cMPlayerStatus(void)
   52.62 +{
   52.63 +  mute=changed=false;
   52.64 +  volume=0;
   52.65 +}
   52.66 +
   52.67 +bool cMPlayerStatus::GetVolume(int &Volume, bool &Mute)
   52.68 +{
   52.69 +  Lock();
   52.70 +  bool r=changed;
   52.71 +  // convert according cDvbDevice::SetVolumeDevice();
   52.72 +  // take into account that VDR does non-linear changes, while
   52.73 +  // MPlayer does linear volume changes.
   52.74 +  Volume=((2*volume-volume*volume/255)*100+128)/256;
   52.75 +  Mute=mute;
   52.76 +  changed=false;
   52.77 +  Unlock();
   52.78 +  return r;
   52.79 +}
   52.80 +
   52.81 +void cMPlayerStatus::SetVolume(int Volume, bool Absolute)
   52.82 +{
   52.83 +  Lock();
   52.84 +  if(Absolute && Volume==0) mute=true;
   52.85 +  else {
   52.86 +#if APIVERSNUM>=10401
   52.87 +#if APIVERSNUM==10401
   52.88 +#warning Caution! This code does not work with VDR 1.4.1 and 1.4.1-1. You can ignore this warning if you are using VDR 1.4.1-2 or later.
   52.89 +#endif
   52.90 +    if(!Absolute)
   52.91 +      volume+=Volume;
   52.92 +    else
   52.93 +#endif
   52.94 +      volume=Volume;
   52.95 +    if(volume>0) mute=false;
   52.96 +    }
   52.97 +  d(printf("status: volume=%d mute=%d\n",volume,mute))
   52.98 +  changed=true;
   52.99 +  Unlock();
  52.100 +}
  52.101 +
  52.102 +// --- cResumeEntry ------------------------------------------------------------
  52.103 +
  52.104 +class cResumeEntry : public cListObject {
  52.105 +public:
  52.106 +  char *name;
  52.107 +  float pos;
  52.108 +  //
  52.109 +  cResumeEntry(void);
  52.110 +  ~cResumeEntry();
  52.111 +  };
  52.112 +
  52.113 +cResumeEntry::cResumeEntry(void)
  52.114 +{
  52.115 +  name=0;
  52.116 +}
  52.117 +
  52.118 +cResumeEntry::~cResumeEntry()
  52.119 +{
  52.120 +  free(name);
  52.121 +}
  52.122 +
  52.123 +// --- cMPlayerResume ----------------------------------------------------------
  52.124 +
  52.125 +#define RESUME_FILE ".mplayer.resume"
  52.126 +#define GLOBAL_RESUME_FILE "global.mplayer.resume"
  52.127 +
  52.128 +class cMPlayerResume : public cList<cResumeEntry> {
  52.129 +private:
  52.130 +  char *resfile;
  52.131 +  bool modified, global;
  52.132 +  cFileObj *resobj;
  52.133 +  //
  52.134 +  bool OpenResume(const cFileObj *file);
  52.135 +  bool SaveResume(void);
  52.136 +  void Purge(void);
  52.137 +  cResumeEntry *FindResume(const cFileObj *file);
  52.138 +public:
  52.139 +  cMPlayerResume(void);
  52.140 +  ~cMPlayerResume();
  52.141 +  void SetResume(const cFileObj *file, float pos);
  52.142 +  bool GetResume(const cFileObj *file, float &pos);
  52.143 +  };
  52.144 +
  52.145 +cMPlayerResume::cMPlayerResume(void)
  52.146 +{
  52.147 +  resfile=0; resobj=0;
  52.148 +}
  52.149 +
  52.150 +cMPlayerResume::~cMPlayerResume()
  52.151 +{
  52.152 +  SaveResume();
  52.153 +  free(resfile);
  52.154 +  delete resobj;
  52.155 +}
  52.156 +
  52.157 +void cMPlayerResume::SetResume(const cFileObj *file, float pos)
  52.158 +{
  52.159 +  if(pos<0.001) pos=0.0;
  52.160 +  else if(pos>99.0) pos=99.0;
  52.161 +  cResumeEntry *re;
  52.162 +  if(OpenResume(file) && (re=FindResume(file))) {
  52.163 +    d(printf("resume: setting resume %f (update)\n",pos))
  52.164 +    }
  52.165 +  else {
  52.166 +    re=new cResumeEntry;
  52.167 +    re->name=strdup(global ? file->FullPath() : file->Name());
  52.168 +    Add(re);
  52.169 +    d(printf("resume: setting resume %f (new)\n",pos))
  52.170 +    }
  52.171 +  re->pos=pos;
  52.172 +  modified=true;
  52.173 +}
  52.174 +
  52.175 +bool cMPlayerResume::GetResume(const cFileObj *file, float &pos)
  52.176 +{
  52.177 +  cResumeEntry *re;
  52.178 +  if(OpenResume(file) && (re=FindResume(file))) {
  52.179 +    pos=re->pos;
  52.180 +    return true;
  52.181 +    }
  52.182 +  return false;
  52.183 +}
  52.184 +
  52.185 +bool cMPlayerResume::OpenResume(const cFileObj *file)
  52.186 +{
  52.187 +  if(!resfile) {
  52.188 +    Clear();
  52.189 +    modified=global=false;
  52.190 +    free(resfile); resfile=0;
  52.191 +    delete resobj; resobj=new cFileObj(file);
  52.192 +    char *s;
  52.193 +    asprintf(&s,file->Subdir() ? "%s/%s":"%s",file->Source()->BaseDir(),file->Subdir());
  52.194 +    if(MPlayerSetup.ResumeMode==1 || 
  52.195 +       (access(s,W_OK) && (errno==EACCES || errno==EROFS))) {
  52.196 +      global=true;
  52.197 +      resfile=AddPath(globalResumeDir?globalResumeDir:VideoDirectory,GLOBAL_RESUME_FILE);
  52.198 +      d(printf("resume: using global file\n"))
  52.199 +      }
  52.200 +    else {
  52.201 +      resfile=AddPath(s,RESUME_FILE);
  52.202 +      }
  52.203 +    free(s);
  52.204 +    d(printf("resume: resume file is '%s'\n",resfile))
  52.205 +    FILE *f=fopen(resfile,"r");
  52.206 +    if(f) {
  52.207 +      d(printf("resume: successfully opened resume file\n"))
  52.208 +      char line[768];
  52.209 +      while(fgets(line,sizeof(line),f)) {
  52.210 +        char name[512];
  52.211 +        float p;
  52.212 +        if(sscanf(line,"%f:%511[^\n]",&p,name)==2) {
  52.213 +          cResumeEntry *re=new cResumeEntry;
  52.214 +          re->name=strdup(name);
  52.215 +          re->pos=p;
  52.216 +          Add(re);
  52.217 +          }
  52.218 +        }
  52.219 +      fclose(f);
  52.220 +      return true;
  52.221 +      }
  52.222 +    else {
  52.223 +      d(printf("resume: assuming empty resume file\n"))
  52.224 +      return false;
  52.225 +      }
  52.226 +    }
  52.227 +  return true;
  52.228 +}
  52.229 +
  52.230 +bool cMPlayerResume::SaveResume(void)
  52.231 +{
  52.232 +  if(resfile && modified) {
  52.233 +    Purge();
  52.234 +    d(printf("resume: saving resume file\n"))
  52.235 +    cSafeFile f(resfile);
  52.236 +    if(f.Open()) {
  52.237 +      for(cResumeEntry *re=First(); re; re=Next(re))
  52.238 +        fprintf(f,"%06.2f:%s\n",re->pos,re->name);
  52.239 +      f.Close();
  52.240 +      return true;
  52.241 +      }
  52.242 +    else
  52.243 +      d(printf("resume: failed to save resume file\n"))
  52.244 +    }
  52.245 +  return false;
  52.246 +}
  52.247 +
  52.248 +void cMPlayerResume::Purge(void)
  52.249 +{
  52.250 +  d(printf("resume: purging from resume file\n"))
  52.251 +  for(cResumeEntry *re=First(); re;) {
  52.252 +    bool del=false;
  52.253 +    if(re->pos<1.0 || re->pos>99.0) {
  52.254 +      del=true;
  52.255 +      d(printf("resume: purging due to position: %s\n",re->name))
  52.256 +      }
  52.257 +    else if(!global) {
  52.258 +      resobj->SetName(re->name);
  52.259 +      if(access(resobj->FullPath(),F_OK)<0) {
  52.260 +        del=true;
  52.261 +        d(printf("resume: purging due to access: %s\n",re->name))
  52.262 +        }
  52.263 +      }
  52.264 +    if(del) {
  52.265 +      cResumeEntry *n=Next(re);
  52.266 +      Del(re);
  52.267 +      modified=true;
  52.268 +      re=n;
  52.269 +      }
  52.270 +    else
  52.271 +      re=Next(re);
  52.272 +    }
  52.273 +}
  52.274 +
  52.275 +cResumeEntry *cMPlayerResume::FindResume(const cFileObj *file)
  52.276 +{
  52.277 + if(resfile) {
  52.278 +   d(printf("resume: searching resume  position for '%s'\n",file->Name()))
  52.279 +   const char *s=global ? file->FullPath() : file->Name();
  52.280 +   for(cResumeEntry *re=First(); re; re=Next(re))
  52.281 +     if(!strcasecmp(re->name,s)) {
  52.282 +       d(printf("resume: found resume position %.1f%%\n",re->pos))
  52.283 +       return re;
  52.284 +       }
  52.285 +   }
  52.286 + d(printf("resume: no resume position found\n"))
  52.287 + return 0;
  52.288 +}
  52.289 +
  52.290 +// --- cMPlayerPlayer ----------------------------------------------------------
  52.291 +
  52.292 +cMPlayerPlayer::cMPlayerPlayer(const cFileObj *File, bool Rewind)
  52.293 +:cPlayer(pmExtern_THIS_SHOULD_BE_AVOIDED)
  52.294 +{
  52.295 +  started=slave=brokenPipe=false; run=true; pid=-1; pipefl=0;
  52.296 +  playMode=pmPlay; index=saveIndex=total=-1; nextTime=nextPos=0;
  52.297 +  currentName=0;
  52.298 +  file=new cFileObj(File);
  52.299 +  rewind=Rewind;
  52.300 +  resume=MPlayerSetup.ResumeMode ? new cMPlayerResume : 0;
  52.301 +}
  52.302 +
  52.303 +cMPlayerPlayer::~cMPlayerPlayer()
  52.304 +{
  52.305 +  Detach();
  52.306 +  ClosePipe();
  52.307 +  delete file;
  52.308 +  delete resume;
  52.309 +  free(currentName);
  52.310 +}
  52.311 +
  52.312 +void cMPlayerPlayer::ClosePipe(void)
  52.313 +{
  52.314 +  if(pipefl&1) close(inpipe[0]);
  52.315 +  if(pipefl&2) close(inpipe[1]);
  52.316 +  if(pipefl&4) close(outpipe[0]);
  52.317 +  if(pipefl&8) close(outpipe[1]);
  52.318 +  pipefl=0;
  52.319 +}
  52.320 +
  52.321 +void cMPlayerPlayer::Activate(bool On)
  52.322 +{
  52.323 +  if(On) {
  52.324 +    if(file && !started) {
  52.325 +      if(Fork()) started=true;
  52.326 +      }
  52.327 +    }
  52.328 +  else if(started) {
  52.329 +    run=false;
  52.330 +    if(Active()) {
  52.331 +      if(slave) {
  52.332 +        Play(); // MPlayer ignores "quit" while paused
  52.333 +        MPlayerControl("quit");
  52.334 +        int until=time_ms()+3000; // wait some time until MPlayer is gone
  52.335 +        d(printf("mplayer: waiting for child exit"))
  52.336 +        while(Active()) {
  52.337 +          if(time_ms()>until) {
  52.338 +            kill(pid,SIGKILL); // kill it anyways
  52.339 +            d(printf(" SIGKILL"))
  52.340 +            break;
  52.341 +            }
  52.342 +          SLEEP(250);
  52.343 +          d(printf(".")) d(fflush(stdout))
  52.344 +          }
  52.345 +        d(printf("\n"))
  52.346 +        }
  52.347 +      else {
  52.348 +        kill(pid,SIGTERM);
  52.349 +        d(printf("mplayer: waiting for child exit (non-slave)\n"))
  52.350 +        }
  52.351 +      waitpid(pid,0,0);
  52.352 +      }
  52.353 +    ClosePipe();
  52.354 +    Cancel(2);
  52.355 +    started=slave=false;
  52.356 +    }
  52.357 +}
  52.358 +
  52.359 +bool cMPlayerPlayer::Active(void)
  52.360 +{
  52.361 +  return waitpid(pid,0,WNOHANG)==0;
  52.362 +}
  52.363 +
  52.364 +bool cMPlayerPlayer::Fork(void)
  52.365 +{
  52.366 +  if(MPlayerSetup.SlaveMode) {
  52.367 +    if(pipe(inpipe)==-1) {
  52.368 +      esyslog("ERROR: pipe failed for inpipe: (%d) %s",errno,strerror(errno));
  52.369 +      return false;
  52.370 +      }
  52.371 +    pipefl|=1+2;
  52.372 +    if(pipe(outpipe)==-1) {
  52.373 +      esyslog("ERROR: pipe failed for outpipe: (%d) %s",errno,strerror(errno));
  52.374 +      return false;
  52.375 +      }
  52.376 +    pipefl|=4+8;
  52.377 +    brokenPipe=false;
  52.378 +    }
  52.379 +
  52.380 +  pid=fork();
  52.381 +  if(pid==-1) {
  52.382 +    esyslog("ERROR: fork failed: (%d) %s",errno,strerror(errno));
  52.383 +    return false;
  52.384 +    }
  52.385 +  if(pid==0) { // child
  52.386 +    dsyslog("mplayer: mplayer child started (pid=%d)", getpid());
  52.387 +
  52.388 +    if(MPlayerSetup.SlaveMode) {
  52.389 +      if(dup2(inpipe[0],STDIN_FILENO)<0 ||
  52.390 +         dup2(outpipe[1],STDOUT_FILENO)<0 ||
  52.391 +         dup2(outpipe[1],STDERR_FILENO)<0) {
  52.392 +        esyslog("ERROR: dup2() failed in MPlayer child: (%d) %s",errno,strerror(errno));
  52.393 +        exit(127);
  52.394 +        }
  52.395 +      }
  52.396 +    else {
  52.397 +      int nfd=open("/dev/null",O_RDONLY);
  52.398 +      if(nfd<0 || dup2(nfd,STDIN_FILENO)<0)
  52.399 +        esyslog("ERROR: redirect of STDIN failed in MPlayer child: (%d) %s",errno,strerror(errno));
  52.400 +      }
  52.401 +    for(int i=getdtablesize()-1; i>STDERR_FILENO; i--) close(i);
  52.402 +
  52.403 +    char cmd[64+PATH_MAX*2], aid[20];
  52.404 +    char *fname=Quote(file->FullPath());
  52.405 +    if(MPlayerAid>=0) snprintf(aid,sizeof(aid)," AID %d",MPlayerAid);
  52.406 +    else aid[0]=0;
  52.407 +    snprintf(cmd,sizeof(cmd),"%s \"%s\" %s%s",MPlayerCmd,fname,MPlayerSetup.SlaveMode?"SLAVE":"",aid);
  52.408 +    free(fname);
  52.409 +    execle("/bin/sh","sh","-c",cmd,(char *)0,environ);
  52.410 +    esyslog("ERROR: exec failed for %s: (%d) %s",cmd,errno,strerror(errno));
  52.411 +    exit(127);
  52.412 +    }
  52.413 +
  52.414 +  if(MPlayerSetup.SlaveMode) {
  52.415 +    close(inpipe[0]); pipefl&=~1;
  52.416 +    close(outpipe[1]); pipefl&=~8;
  52.417 +    fcntl(outpipe[0],F_SETFL,O_NONBLOCK);
  52.418 +    run=slave=true;
  52.419 +    mpVolume=100; // MPlayer startup defaults
  52.420 +    mpMute=false;
  52.421 +    Start();
  52.422 +    }
  52.423 +  return true;
  52.424 +}
  52.425 +
  52.426 +#define BSIZE    1024
  52.427 +#define TIME_INT 20
  52.428 +#define POS_INT  1
  52.429 +
  52.430 +void cMPlayerPlayer::Action(void)
  52.431 +{
  52.432 +  dsyslog("mplayer: player thread started (pid=%d)", getpid());
  52.433 +
  52.434 +  // set locale for correct parsing of MPlayer output.
  52.435 +  // I don't know if this affects other parts of VDR.
  52.436 +  const char * const oldLocale=setlocale(LC_NUMERIC,"C");
  52.437 +
  52.438 +  pollfd pfd[1];
  52.439 +  pfd[0].fd=outpipe[0];
  52.440 +  pfd[0].events=POLLIN;
  52.441 +
  52.442 +  float curPos=-1.0, resPos=-1.0;
  52.443 +  if(resume && !rewind) resume->GetResume(file,resPos);
  52.444 +
  52.445 +  char buff[BSIZE+2]; // additional space for fake newline
  52.446 +  int c=0;
  52.447 +  bool force=true, slavePatch=false, trustedTotal=false, playBack=false;
  52.448 +  while(run) {
  52.449 +    if(playMode==pmPlay && playBack) {
  52.450 +      int t=time(0);
  52.451 +      if(t>=nextTime) {
  52.452 +        MPlayerControl("get_time_length");
  52.453 +        nextTime=t+(total>0 ? TIME_INT : POS_INT);
  52.454 +        }
  52.455 +      if(t>=nextPos) {
  52.456 +        if(!slavePatch) MPlayerControl("get_percent_pos");
  52.457 +        nextPos=t+POS_INT;
  52.458 +        }
  52.459 +      }
  52.460 +
  52.461 +    poll(pfd,1,300);
  52.462 +    int r=read(outpipe[0],buff+c,BSIZE-c);
  52.463 +    if(r>0) c+=r;
  52.464 +    if(c>0) {
  52.465 +      buff[c]=0; // make sure buffer is NULL terminated
  52.466 +      char *p;
  52.467 +      do {
  52.468 +        p=strpbrk(buff,"\n\r");
  52.469 +        if(!p && c==BSIZE) { // Full buffer, but no newline found.
  52.470 +          p=&buff[c];        // Have to fake one.
  52.471 +          buff[c]='\n'; c++; buff[c]=0;
  52.472 +          }
  52.473 +        if(p) {
  52.474 +#ifdef DEBUG
  52.475 +          char cc=*p;
  52.476 +#endif
  52.477 +          *p++=0;
  52.478 +          float ftime=-1.0, fpos=-1.0;
  52.479 +          int itime;
  52.480 +          if(strncmp(buff,"Starting playback",17)==0 ||
  52.481 +             strncmp(buff,"Starte Wiedergabe",17)==0) {
  52.482 +            if(!playBack) {
  52.483 +              playBack=true;
  52.484 +              nextTime=nextPos=0;
  52.485 +              d(printf("PLAYBACK STARTED\n"))
  52.486 +              if(resPos>=0.0) {
  52.487 +                if(!currentName ||
  52.488 +                   !strcmp(currentName,file->FullPath()) ||
  52.489 +                   !strcmp(currentName,file->Path()))
  52.490 +                  MPlayerControl("seek %.1f 1",resPos);
  52.491 +                else
  52.492 +                  d(printf("mplayer: no resume, seems to be playlist\n"))
  52.493 +                }
  52.494 +              }
  52.495 +            }
  52.496 +          else if(strncmp(buff,"Playing ",8)==0 ||
  52.497 +                  strncmp(buff,"Spiele ",7)==0) {
  52.498 +            nextTime=nextPos=0;
  52.499 +            index=saveIndex=total=-1;
  52.500 +            trustedTotal=false;
  52.501 +            LOCK_THREAD;
  52.502 +            free(currentName);
  52.503 +            currentName=strdup(::index(buff,' ')+1);
  52.504 +            if(currentName[0]) {
  52.505 +              int l=strlen(currentName);
  52.506 +              if(currentName[l-1]=='.') currentName[l-1]=0; // skip trailing dot
  52.507 +              }
  52.508 +            d(printf("PLAYING %s\n",currentName))
  52.509 +            }
  52.510 +          else if(sscanf(buff,"ANS_LENGTH=%d",&itime)==1) {
  52.511 +            if(itime>0) {
  52.512 +              total=SecondsToFrames(itime);
  52.513 +              trustedTotal=true;
  52.514 +#ifdef DEBUG_SLAVE
  52.515 +              printf("sl: ANS_LENGTH=%s (%s)\n",IndexToHMSF(total),buff);
  52.516 +#endif
  52.517 +              }
  52.518 +            }
  52.519 +          else if(sscanf(buff,"ANS_PERCENT_POSITION=%d",&itime)==1) {
  52.520 +            if(itime>0) {
  52.521 +              curPos=itime;
  52.522 +              if(total>=0) {
  52.523 +                index=total*itime/100;
  52.524 +#ifdef DEBUG_SLAVE
  52.525 +                printf("sl: ANS_PERCENT_POS=%s (%s)\n",IndexToHMSF(index),buff);
  52.526 +#endif
  52.527 +                }
  52.528 +              }
  52.529 +            }
  52.530 +          else if(sscanf(buff,"SLAVE: time=%f position=%f",&ftime,&fpos)==2) {
  52.531 +            curPos=fpos;
  52.532 +            const float fr=(float)SecondsToFrames(1);
  52.533 +            itime=(int)(ftime*fr);
  52.534 +            if(saveIndex<0 || itime>saveIndex) { // prevent index jump-back
  52.535 +              saveIndex=index=itime;
  52.536 +              if(!trustedTotal) total=(int)(ftime*fr*100.0/fpos);
  52.537 +#ifdef DEBUG_SLAVE
  52.538 +              printf("sl: SLAVE=%s/%s [%d] (%s)\n",IndexToHMSF(index),IndexToHMSF(total),trustedTotal,buff);
  52.539 +#endif
  52.540 +              }
  52.541 +            slavePatch=playBack=true;
  52.542 +            }
  52.543 +#ifdef DEBUG
  52.544 +          else printf("%s%c",buff,cc);
  52.545 +#endif
  52.546 +          c-=(p-buff);
  52.547 +          memmove(buff,p,c+1);
  52.548 +          }
  52.549 +        } while(c>0 && p);
  52.550 +      }
  52.551 +    if(playBack) {
  52.552 +      SetMPlayerVolume(force);
  52.553 +      force=false;
  52.554 +      }
  52.555 +    }
  52.556 +
  52.557 +  if(resume && curPos>=0.0) resume->SetResume(file,curPos);
  52.558 +
  52.559 +  // restore old locale
  52.560 +  if(oldLocale) setlocale(LC_NUMERIC,oldLocale);
  52.561 +
  52.562 +  dsyslog("mplayer: player thread ended (pid=%d)", getpid());
  52.563 +}
  52.564 +
  52.565 +void cMPlayerPlayer::SetMPlayerVolume(bool force)
  52.566 +{
  52.567 +  int volume;
  52.568 +  bool mute;
  52.569 +  Lock();
  52.570 +  if(status->GetVolume(volume,mute) || force) {
  52.571 +    if(mute) {
  52.572 +      if(!mpMute) { MPlayerControl("mute"); mpMute=true; }
  52.573 +      }
  52.574 +    else {
  52.575 +      if(mpMute) { MPlayerControl("mute"); mpMute=false; }
  52.576 +      if(volume!=mpVolume) {
  52.577 +        MPlayerControl("volume %d 1",volume);
  52.578 +        mpVolume=volume;
  52.579 +        }
  52.580 +      }
  52.581 +    d(printf("mplayer: volume=%d mpVolume=%d mpMute=%d\n",volume,mpVolume,mpMute))
  52.582 +    }
  52.583 +  Unlock();
  52.584 +}
  52.585 +
  52.586 +void cMPlayerPlayer::MPlayerControl(const char *format, ...)
  52.587 +{
  52.588 +  if(slave) {
  52.589 +    va_list ap;
  52.590 +    va_start(ap,format);
  52.591 +    char *buff=0;
  52.592 +    vasprintf(&buff,format,ap);
  52.593 +    Lock();
  52.594 +    // check for writeable pipe i.e. prevent broken pipe signal
  52.595 +    if(!brokenPipe) {
  52.596 +      struct pollfd pfd;
  52.597 +      pfd.fd=inpipe[1]; pfd.events=POLLOUT; pfd.revents=0;
  52.598 +      int r=poll(&pfd,1,50);
  52.599 +      if(r>0) {
  52.600 +        if(pfd.revents & ~POLLOUT) {
  52.601 +          d(printf("mplayer: %s%s%s%sin MPlayerControl\n",pfd.revents&POLLOUT?"POLLOUT ":"",pfd.revents&POLLERR?"POLLERR ":"",pfd.revents&POLLHUP?"POLLHUP ":"",pfd.revents&POLLNVAL?"POLLNVAL ":""))
  52.602 +          brokenPipe=true;
  52.603 +          }
  52.604 +        else if(pfd.revents & POLLOUT) {
  52.605 +          r=write(inpipe[1],buff,strlen(buff));
  52.606 +          if(r<0) {
  52.607 +            d(printf("mplayer: pipe write(1) failed: %s\n",strerror(errno)))
  52.608 +            brokenPipe=true;
  52.609 +            }
  52.610 +          else {
  52.611 +            r=write(inpipe[1],"\n",1);
  52.612 +            if(r<0) {
  52.613 +              d(printf("mplayer: pipe write(2) failed: %s\n",strerror(errno)))
  52.614 +              brokenPipe=true;
  52.615 +              }
  52.616 +            }
  52.617 +          }
  52.618 +        }
  52.619 +      else if(r==0) d(printf("mplayer: poll timed out in MPlayerControl (hugh?)\n"))
  52.620 +      else d(printf("mplayer: poll failed in MPlayerControl: %s\n",strerror(errno)))
  52.621 +      }
  52.622 +    else d(printf("mplayer: cmd pipe is broken\n"))
  52.623 +    Unlock();
  52.624 +    d(printf("mplayer: slave cmd: %s\n",buff))
  52.625 +    free(buff);
  52.626 +    va_end(ap);
  52.627 +    }
  52.628 +}
  52.629 +
  52.630 +void cMPlayerPlayer::Pause(void)
  52.631 +{
  52.632 +  if(slave) {
  52.633 +    if(playMode==pmPaused) Play();
  52.634 +    else if(playMode==pmPlay) {
  52.635 +      playMode=pmPaused;
  52.636 +      MPlayerControl("pause");
  52.637 +      }
  52.638 +    }
  52.639 +}
  52.640 +
  52.641 +void cMPlayerPlayer::Play(void)
  52.642 +{
  52.643 +  if(slave) {
  52.644 +    if(playMode==pmPaused) {
  52.645 +      playMode=pmPlay;
  52.646 +      MPlayerControl("pause");
  52.647 +      }
  52.648 +    }
  52.649 +}
  52.650 +
  52.651 +void cMPlayerPlayer::Goto(int Index, bool percent, bool still)
  52.652 +{
  52.653 +  if(slave) {
  52.654 +    if(playMode==pmPaused) Play();
  52.655 +    if(percent) MPlayerControl("seek %d 1",Index);
  52.656 +    else        MPlayerControl("seek %+d 0",Index-(index/SecondsToFrames(1)));
  52.657 +    if(still) Pause();
  52.658 +    saveIndex=-1;
  52.659 +    }
  52.660 +}
  52.661 +
  52.662 +void cMPlayerPlayer::SkipSeconds(int secs)
  52.663 +{
  52.664 +  if(slave) {
  52.665 +    bool p=false;
  52.666 +    if(playMode==pmPaused) { Play(); p=true; }
  52.667 +    MPlayerControl("seek %+d 0",secs);
  52.668 +    if(p) Pause();
  52.669 +    saveIndex=-1;
  52.670 +    }
  52.671 +}
  52.672 +
  52.673 +void cMPlayerPlayer::KeyCmd(const char *cmd)
  52.674 +{
  52.675 +  if(slave) MPlayerControl(cmd);
  52.676 +}
  52.677 +
  52.678 +bool cMPlayerPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
  52.679 +{
  52.680 +  Current=index; Total=total;
  52.681 +  return true;
  52.682 +}
  52.683 +
  52.684 +bool cMPlayerPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
  52.685 +{
  52.686 +  Play=(playMode==pmPlay);
  52.687 +  Forward=true;
  52.688 +  Speed=-1;
  52.689 +  return true;
  52.690 +}
  52.691 +
  52.692 +char *cMPlayerPlayer::GetCurrentName(void)
  52.693 +{
  52.694 +  LOCK_THREAD;
  52.695 +  return currentName ? strdup(currentName) : 0;
  52.696 +}
  52.697 +
    53.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    53.2 +++ b/player-mplayer.h	Sat Dec 29 14:47:40 2007 +0100
    53.3 @@ -0,0 +1,92 @@
    53.4 +/*
    53.5 + * MP3/MPlayer plugin to VDR (C++)
    53.6 + *
    53.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    53.8 + *
    53.9 + * This code is free software; you can redistribute it and/or
   53.10 + * modify it under the terms of the GNU General Public License
   53.11 + * as published by the Free Software Foundation; either version 2
   53.12 + * of the License, or (at your option) any later version.
   53.13 + *
   53.14 + * This code is distributed in the hope that it will be useful,
   53.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   53.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   53.17 + * GNU General Public License for more details.
   53.18 + *
   53.19 + * You should have received a copy of the GNU General Public License
   53.20 + * along with this program; if not, write to the Free Software
   53.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   53.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   53.23 + */
   53.24 +
   53.25 +#ifndef ___DVB_MPLAYER_H
   53.26 +#define ___DVB_MPLAYER_H
   53.27 +
   53.28 +#include <vdr/player.h>
   53.29 +#include <vdr/thread.h>
   53.30 +#include <vdr/status.h>
   53.31 +
   53.32 +// ----------------------------------------------------------------
   53.33 +
   53.34 +class cFileObj;
   53.35 +class cMPlayerResume;
   53.36 +
   53.37 +// ----------------------------------------------------------------
   53.38 +
   53.39 +class cMPlayerStatus : public cStatus, cMutex {
   53.40 +private:
   53.41 +  int volume;
   53.42 +  bool mute, changed;
   53.43 +protected:
   53.44 +  virtual void SetVolume(int Volume, bool Absolute);
   53.45 +public:
   53.46 +  cMPlayerStatus(void);
   53.47 +  bool GetVolume(int &Volume, bool &Mute);
   53.48 +  };
   53.49 +
   53.50 +extern cMPlayerStatus *status;
   53.51 +
   53.52 +// ----------------------------------------------------------------
   53.53 +
   53.54 +class cMPlayerPlayer : public cPlayer, cThread {
   53.55 +private:
   53.56 +  cFileObj *file;
   53.57 +  bool rewind;
   53.58 +  cMPlayerResume *resume;
   53.59 +  bool started, slave, run;
   53.60 +  int pid, inpipe[2], outpipe[2], pipefl;
   53.61 +  bool brokenPipe;
   53.62 +  enum ePlayMode { pmPlay, pmPaused };
   53.63 +  ePlayMode playMode;
   53.64 +  int index, total, saveIndex;
   53.65 +  int nextTime, nextPos;
   53.66 +  int mpVolume;
   53.67 +  bool mpMute;
   53.68 +  char *currentName;
   53.69 +  //
   53.70 +  bool Fork(void);
   53.71 +  void ClosePipe(void);
   53.72 +  void MPlayerControl(const char *format, ...);
   53.73 +  void SetMPlayerVolume(bool force);
   53.74 +protected:
   53.75 +  virtual void Activate(bool On);
   53.76 +  virtual void Action(void);
   53.77 +public:
   53.78 +  cMPlayerPlayer(const cFileObj *File, bool Rewind);
   53.79 +  virtual ~cMPlayerPlayer();
   53.80 +  bool Active(void);
   53.81 +  bool SlaveMode(void) const { return slave; }
   53.82 +  void Pause(void);
   53.83 +  void Play(void);
   53.84 +  void Goto(int Index, bool percent, bool still);
   53.85 +  void SkipSeconds(int secs);
   53.86 +  void KeyCmd(const char *cmd);
   53.87 +  char *GetCurrentName(void);
   53.88 +  virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame);
   53.89 +  virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
   53.90 +  };
   53.91 +
   53.92 +extern int MPlayerAid;
   53.93 +extern const char *globalResumeDir;
   53.94 +
   53.95 +#endif //___DVB_MPLAYER_H
    54.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    54.2 +++ b/service.h	Sat Dec 29 14:47:40 2007 +0100
    54.3 @@ -0,0 +1,39 @@
    54.4 +/*
    54.5 + * MP3/MPlayer plugin to VDR (C++)
    54.6 + *
    54.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    54.8 + *
    54.9 + * This code is free software; you can redistribute it and/or
   54.10 + * modify it under the terms of the GNU General Public License
   54.11 + * as published by the Free Software Foundation; either version 2
   54.12 + * of the License, or (at your option) any later version.
   54.13 + *
   54.14 + * This code is distributed in the hope that it will be useful,
   54.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   54.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   54.17 + * GNU General Public License for more details.
   54.18 + *
   54.19 + * You should have received a copy of the GNU General Public License
   54.20 + * along with this program; if not, write to the Free Software
   54.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   54.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   54.23 + */
   54.24 +
   54.25 +#ifndef ___SERVICE_H
   54.26 +#define ___SERVICE_H
   54.27 +
   54.28 +struct MP3ServiceData {
   54.29 +  int result;
   54.30 +  union {
   54.31 +    const char *filename;
   54.32 +    } data;
   54.33 +  };
   54.34 +
   54.35 +struct MPlayerServiceData {
   54.36 +  int result;
   54.37 +  union {
   54.38 +    const char *filename;
   54.39 +    } data;
   54.40 +  };
   54.41 +
   54.42 +#endif //___SERVICE_H
    55.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    55.2 +++ b/setup-mp3.c	Sat Dec 29 14:47:40 2007 +0100
    55.3 @@ -0,0 +1,56 @@
    55.4 +/*
    55.5 + * MP3/MPlayer plugin to VDR (C++)
    55.6 + *
    55.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    55.8 + *
    55.9 + * This code is free software; you can redistribute it and/or
   55.10 + * modify it under the terms of the GNU General Public License
   55.11 + * as published by the Free Software Foundation; either version 2
   55.12 + * of the License, or (at your option) any later version.
   55.13 + *
   55.14 + * This code is distributed in the hope that it will be useful,
   55.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   55.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   55.17 + * GNU General Public License for more details.
   55.18 + *
   55.19 + * You should have received a copy of the GNU General Public License
   55.20 + * along with this program; if not, write to the Free Software
   55.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   55.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   55.23 + */
   55.24 +
   55.25 +#include <string.h>
   55.26 +
   55.27 +#include "common.h"
   55.28 +#include "setup-mp3.h"
   55.29 +
   55.30 +cMP3Setup MP3Setup;
   55.31 +
   55.32 +// --- cMP3Setup ---------------------------------------------------------------
   55.33 +
   55.34 +cMP3Setup::cMP3Setup(void)
   55.35 +{
   55.36 +  InitLoopMode = 0;
   55.37 +  InitShuffleMode = 0;
   55.38 +  AudioMode = 1;
   55.39 +  BgrScan = 2;
   55.40 +  EditorMode = 1;
   55.41 +  DisplayMode = 3;
   55.42 +  BackgrMode = 1;
   55.43 +  MenuMode = 0;
   55.44 +  TargetLevel = DEFAULT_TARGET_LEVEL;
   55.45 +  LimiterLevel = DEFAULT_LIMITER_LEVEL;
   55.46 +  Only48kHz = 0;
   55.47 +  UseProxy = 0;
   55.48 +  strcpy(ProxyHost,"localhost");
   55.49 +  ProxyPort = 8080;
   55.50 +  UseCddb = 1;
   55.51 +  strcpy(CddbHost,"freedb.freedb.org");
   55.52 +  CddbPort = 888;
   55.53 +  AudioOutMode = 0;
   55.54 +  AbortAtEOL = 1;
   55.55 +  ReplayDisplay = 0;
   55.56 +  HideMainMenu = 0;
   55.57 +  KeepSelect = 0;
   55.58 +  TitleArtistOrder = 0;
   55.59 +}
    56.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    56.2 +++ b/setup-mp3.h	Sat Dec 29 14:47:40 2007 +0100
    56.3 @@ -0,0 +1,75 @@
    56.4 +/*
    56.5 + * MP3/MPlayer plugin to VDR (C++)
    56.6 + *
    56.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    56.8 + *
    56.9 + * This code is free software; you can redistribute it and/or
   56.10 + * modify it under the terms of the GNU General Public License
   56.11 + * as published by the Free Software Foundation; either version 2
   56.12 + * of the License, or (at your option) any later version.
   56.13 + *
   56.14 + * This code is distributed in the hope that it will be useful,
   56.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   56.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   56.17 + * GNU General Public License for more details.
   56.18 + *
   56.19 + * You should have received a copy of the GNU General Public License
   56.20 + * along with this program; if not, write to the Free Software
   56.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   56.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   56.23 + */
   56.24 +
   56.25 +#ifndef ___SETUP_MP3_H
   56.26 +#define ___SETUP_MP3_H
   56.27 +
   56.28 +extern const char *cddbpath;
   56.29 +extern const char *netscript;
   56.30 +#ifdef WITH_OSS
   56.31 +extern const char *dspdevice;
   56.32 +#endif
   56.33 +
   56.34 +// ----------------------------------------------------------------
   56.35 +
   56.36 +#define DEFAULT_TARGET_LEVEL  25
   56.37 +#define MAX_TARGET_LEVEL      50
   56.38 +#define DEFAULT_LIMITER_LEVEL 70
   56.39 +#define MIN_LIMITER_LEVEL     25
   56.40 +
   56.41 +#define MAX_HOSTNAME 128
   56.42 +
   56.43 +#define AUDIOOUTMODES    2
   56.44 +#define AUDIOOUTMODE_DVB 0
   56.45 +#define AUDIOOUTMODE_OSS 1
   56.46 +
   56.47 +class cMP3Setup {
   56.48 +public:
   56.49 +  int InitLoopMode;
   56.50 +  int InitShuffleMode;
   56.51 +  int AudioMode;
   56.52 +  int BgrScan;
   56.53 +  int EditorMode;
   56.54 +  int DisplayMode;
   56.55 +  int BackgrMode;
   56.56 +  int MenuMode;
   56.57 +  int TargetLevel;
   56.58 +  int LimiterLevel;
   56.59 +  int Only48kHz;
   56.60 +  int UseProxy;
   56.61 +  char ProxyHost[MAX_HOSTNAME];
   56.62 +  int ProxyPort;
   56.63 +  int UseCddb;
   56.64 +  char CddbHost[MAX_HOSTNAME];
   56.65 +  int CddbPort;
   56.66 +  int AudioOutMode;
   56.67 +  int AbortAtEOL;
   56.68 +  int ReplayDisplay;
   56.69 +  int HideMainMenu;
   56.70 +  int KeepSelect;
   56.71 +  int TitleArtistOrder;
   56.72 +public:
   56.73 +  cMP3Setup(void);
   56.74 +  };
   56.75 +
   56.76 +extern cMP3Setup MP3Setup;
   56.77 +
   56.78 +#endif //___SETUP_MP3_H
    57.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    57.2 +++ b/setup-mplayer.c	Sat Dec 29 14:47:40 2007 +0100
    57.3 @@ -0,0 +1,41 @@
    57.4 +/*
    57.5 + * MP3/MPlayer plugin to VDR (C++)
    57.6 + *
    57.7 + * (C) 2001-2007 Stefan Huelswitt <s.huelswitt@gmx.de>
    57.8 + *
    57.9 + * This code is free software; you can redistribute it and/or
   57.10 + * modify it under the terms of the GNU General Public License
   57.11 + * as published by the Free Software Foundation; either version 2
   57.12 + * of the License, or (at your option) any later version.
   57.13 + *
   57.14 + * This code is distributed in the hope that it will be useful,
   57.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   57.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   57.17 + * GNU General Public License for more details.
   57.18 + *
   57.19 + * You should have received a copy of the GNU General Public License
   57.20 + * along with this program; if not, write to the Free Software
   57.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   57.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   57.23 + */
   57.24 +
   57.25 +#include <string.h>
   57.26 +
   57.27 +#include "common.h"
   57.28 +#include "setup-mplayer.h"
   57.29 +
   57.30 +cMPlayerSetup MPlayerSetup;
   57.31 +
   57.32 +// --- cMPlayerSetup -----------------------------------------------------------
   57.33 +
   57.34 +cMPlayerSetup::cMPlayerSetup(void)
   57.35 +{
   57.36 +  SlaveMode = 1;
   57.37 +  ResumeMode = 2;
   57.38 +  HideMainMenu = 0;
   57.39 +  OsdPos = 0;
   57.40 +  memset(KeyCmd,0,sizeof(KeyCmd));
   57.41 +  strcpy(KeyCmd[1],"audio_delay +0.1");
   57.42 +  strcpy(KeyCmd[7],"audio_delay -0.1");
   57.43 +  strcpy(KeyCmd[4],"switch_audio");
   57.44 +}
    58.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    58.2 +++ b/setup-mplayer.h	Sat Dec 29 14:47:40 2007 +0100
    58.3 @@ -0,0 +1,44 @@
    58.4 +/*
    58.5 + * MP3/MPlayer plugin to VDR (C++)
    58.6 + *
    58.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    58.8 + *
    58.9 + * This code is free software; you can redistribute it and/or
   58.10 + * modify it under the terms of the GNU General Public License
   58.11 + * as published by the Free Software Foundation; either version 2
   58.12 + * of the License, or (at your option) any later version.
   58.13 + *
   58.14 + * This code is distributed in the hope that it will be useful,
   58.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   58.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   58.17 + * GNU General Public License for more details.
   58.18 + *
   58.19 + * You should have received a copy of the GNU General Public License
   58.20 + * along with this program; if not, write to the Free Software
   58.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   58.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   58.23 + */
   58.24 +
   58.25 +#ifndef ___SETUP_MPLAYER_H
   58.26 +#define ___SETUP_MPLAYER_H
   58.27 +
   58.28 +extern const char *MPlayerCmd;
   58.29 +
   58.30 +// ----------------------------------------------------------------
   58.31 +
   58.32 +#define MAX_KEYCMD 32
   58.33 +
   58.34 +class cMPlayerSetup {
   58.35 +public:
   58.36 +  int SlaveMode;
   58.37 +  int ResumeMode;
   58.38 +  int HideMainMenu;
   58.39 +  int OsdPos;
   58.40 +  char KeyCmd[10][MAX_KEYCMD];
   58.41 +public:
   58.42 +  cMPlayerSetup(void);
   58.43 +  };
   58.44 +
   58.45 +extern cMPlayerSetup MPlayerSetup;
   58.46 +
   58.47 +#endif //___SETUP_MPLAYER_H
    59.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    59.2 +++ b/setup.h	Sat Dec 29 14:47:40 2007 +0100
    59.3 @@ -0,0 +1,27 @@
    59.4 +/*
    59.5 + * MP3/MPlayer plugin to VDR (C++)
    59.6 + *
    59.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
    59.8 + *
    59.9 + * This code is free software; you can redistribute it and/or
   59.10 + * modify it under the terms of the GNU General Public License
   59.11 + * as published by the Free Software Foundation; either version 2
   59.12 + * of the License, or (at your option) any later version.
   59.13 + *
   59.14 + * This code is distributed in the hope that it will be useful,
   59.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   59.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   59.17 + * GNU General Public License for more details.
   59.18 + *
   59.19 + * You should have received a copy of the GNU General Public License
   59.20 + * along with this program; if not, write to the Free Software
   59.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   59.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   59.23 + */
   59.24 +
   59.25 +#ifndef ___SETUP_H
   59.26 +#define ___SETUP_H
   59.27 +
   59.28 +extern const char *mountscript;
   59.29 +
   59.30 +#endif //___SETUP_H
    60.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    60.2 +++ b/stream.c	Sat Dec 29 14:47:40 2007 +0100
    60.3 @@ -0,0 +1,589 @@
    60.4 +/*
    60.5 + * MP3/MPlayer plugin to VDR (C++)
    60.6 + *
    60.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    60.8 + *
    60.9 + * This code is free software; you can redistribute it and/or
   60.10 + * modify it under the terms of the GNU General Public License
   60.11 + * as published by the Free Software Foundation; either version 2
   60.12 + * of the License, or (at your option) any later version.
   60.13 + *
   60.14 + * This code is distributed in the hope that it will be useful,
   60.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   60.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   60.17 + * GNU General Public License for more details.
   60.18 + *
   60.19 + * You should have received a copy of the GNU General Public License
   60.20 + * along with this program; if not, write to the Free Software
   60.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   60.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   60.23 + */
   60.24 +
   60.25 +#include <stdlib.h>
   60.26 +#include <stdio.h>
   60.27 +#include <unistd.h>
   60.28 +#include <errno.h>
   60.29 +
   60.30 +#include "common.h"
   60.31 +#include "setup-mp3.h"
   60.32 +#include "stream.h"
   60.33 +#include "network.h"
   60.34 +#include "menu-async.h"
   60.35 +#include "i18n.h"
   60.36 +#include "version.h"
   60.37 +
   60.38 +#define USE_MMAP
   60.39 +#define MAX_MMAP_SIZE   (32*1024*1024)
   60.40 +#define MP3FILE_BUFSIZE (32*1024)
   60.41 +
   60.42 +#ifdef USE_MMAP
   60.43 +#include <sys/mman.h>
   60.44 +#endif
   60.45 +
   60.46 +#define DEFAULT_PORT 80 // default port for streaming (HTTP)
   60.47 +
   60.48 +//#define DUMP_HEAD "/var/tmp/headers"
   60.49 +
   60.50 +// --- cIO ---------------------------------------------------------------------
   60.51 +
   60.52 +#if 0
   60.53 +cIO::cIO(const char *Filename, bool Log)
   60.54 +:cFileInfo(Filename)
   60.55 +{
   60.56 +  log=Log;
   60.57 +  readpos=0;
   60.58 +}
   60.59 +
   60.60 +cIO::~cIO()
   60.61 +{
   60.62 +}
   60.63 +
   60.64 +// --- cStreamIO ---------------------------------------------------------------
   60.65 +
   60.66 +cStreamIO::cStreamIO(void)
   60.67 +{
   60.68 +  data=0;
   60.69 +}
   60.70 +
   60.71 +cStreamIO::~cStreamIO()
   60.72 +{
   60.73 +  StreamClear(true);
   60.74 +}
   60.75 +
   60.76 +void cStreamIO::StreamClear(bool all)
   60.77 +{
   60.78 +  if(all) { free(data); data=0; }
   60.79 +  fill=0;
   60.80 +}
   60.81 +
   60.82 +unsigned char *cStreamIO::StreamInit(int Size)
   60.83 +{
   60.84 +  StreamClear(true);
   60.85 +  size=Size; data=(unsigned char *)malloc(size);
   60.86 +  return data;
   60.87 +}
   60.88 +
   60.89 +int cStreamIO::Stream(const unsigned char *rest)
   60.90 +{
   60.91 +  if(rest && fill) { // copy remaining data to start of buffer
   60.92 +    fill-=(rest-data);
   60.93 +    memmove(data,rest,fill);
   60.94 +    }
   60.95 +  else fill=0;
   60.96 +
   60.97 +  unsigned long r=Read(data+fill,size-fill);
   60.98 +  if(r>=0) { 
   60.99 +    fill+=r;
  60.100 +    return fill;
  60.101 +    }
  60.102 +  return -1;    
  60.103 +}
  60.104 +
  60.105 +// --- cFileIO -----------------------------------------------------------------
  60.106 +
  60.107 +cFileIO::cFileIO(const char *Filename, bool Log)
  60.108 +:cIO(Filename,Log)
  60.109 +{
  60.110 +  fd=-1;
  60.111 +}
  60.112 +
  60.113 +cFileIO::~cFileIO()
  60.114 +{
  60.115 +  Close();
  60.116 +}
  60.117 +
  60.118 +bool cFileIO::Open(void)
  60.119 +{
  60.120 +  if(fd>=0) return Seek(0,SEEK_SET);
  60.121 +  if(FileInfo(log)) {
  60.122 +    if((fd=open(Filename,O_RDONLY))>=0) {
  60.123 +      StreamClear(false);
  60.124 +      readpos=0;
  60.125 +      return true;
  60.126 +      }
  60.127 +    else if(log) { esyslog("ERROR: open failed on %s: %s",Filename,strerror(errno)); }
  60.128 +    }
  60.129 +  Close();
  60.130 +  return false;
  60.131 +}
  60.132 +
  60.133 +void cFileIO::Close(void)
  60.134 +{
  60.135 +  if(fd>=0) { close(fd); fd=-1; }
  60.136 +}
  60.137 +
  60.138 +int cFileIO::Read(unsigned char *Data, int Size)
  60.139 +{
  60.140 +  unsigned long r=read(fd,Data,Size);
  60.141 +  if(r>=0) readpos+=r;
  60.142 +  else if(log) { esyslog("ERROR: read failed in %s: %s",Filename,strerror(errno)); }
  60.143 +  return r;
  60.144 +}
  60.145 +
  60.146 +bool cFileIO::Seek(unsigned long long pos, int whence)
  60.147 +{
  60.148 +  if(pos>=0 && pos<=Filesize) {
  60.149 +    StreamClear(false);
  60.150 +    if((readpos=lseek64(fd,pos,whence))>=0) {
  60.151 +      if(readpos!=pos) { dsyslog("seek mismatch in %s, wanted %lld, got %lld",Filename,pos,readpos); }
  60.152 +      return true;
  60.153 +      }
  60.154 +    else if(log) { esyslog("ERROR: seek failed in %s: %s",Filename,strerror(errno)); }
  60.155 +    }
  60.156 +  else d(printf("mp3: bad seek call fd=%d pos=%lld name=%s\n",fd,pos,Filename))
  60.157 +  return false;
  60.158 +}
  60.159 +#endif
  60.160 +
  60.161 +// --- cStream -----------------------------------------------------------------
  60.162 +
  60.163 +cStream::cStream(const char *Filename)
  60.164 +:cFileInfo(Filename)
  60.165 +{
  60.166 +  fd=-1; ismmap=false; buffer=0;
  60.167 +}
  60.168 +
  60.169 +cStream::~cStream()
  60.170 +{
  60.171 +  Close();
  60.172 +}
  60.173 +
  60.174 +bool cStream::Open(bool log)
  60.175 +{
  60.176 +  if(fd>=0) return Seek();
  60.177 +
  60.178 +  if(FileInfo(log)) {
  60.179 +    if((fd=open(Filename,O_RDONLY))>=0) {
  60.180 +      buffpos=readpos=0; fill=0;
  60.181 +
  60.182 +#ifdef USE_MMAP
  60.183 +      if(Filesize<=MAX_MMAP_SIZE) {
  60.184 +        buffer=(unsigned char *)mmap(0,Filesize,PROT_READ,MAP_SHARED,fd,0);
  60.185 +        if(buffer!=MAP_FAILED) {
  60.186 +          ismmap=true;
  60.187 +          return true;
  60.188 +          }
  60.189 +        else dsyslog("mmap() failed for %s: %s",Filename,strerror(errno));
  60.190 +        }
  60.191 +#endif
  60.192 +
  60.193 +        buffer = new unsigned char[MP3FILE_BUFSIZE];
  60.194 +        if(buffer) return true;
  60.195 +        else { esyslog("ERROR: not enough memory for buffer: %s",Filename); }
  60.196 +      }
  60.197 +    else if(log) { esyslog("ERROR: failed to open file %s: %s",Filename,strerror(errno)); }
  60.198 +    }
  60.199 +
  60.200 +  Close();
  60.201 +  return false;
  60.202 +}
  60.203 +
  60.204 +void cStream::Close(void)
  60.205 +{
  60.206 +#ifdef USE_MMAP
  60.207 +  if(ismmap) { 
  60.208 +    munmap(buffer,Filesize); buffer=0; ismmap=false;
  60.209 +    }
  60.210 +  else {
  60.211 +#endif
  60.212 +    delete buffer; buffer=0;
  60.213 +#ifdef USE_MMAP
  60.214 +    }
  60.215 +#endif
  60.216 +  if(fd>=0) { close(fd); fd=-1; }
  60.217 +}
  60.218 +
  60.219 +bool cStream::Seek(unsigned long long pos)
  60.220 +{
  60.221 +  if(fd>=0 && pos>=0 && pos<=Filesize) {
  60.222 +    buffpos=0; fill=0;
  60.223 +    if(ismmap) {
  60.224 +      readpos=pos;
  60.225 +      return true;
  60.226 +      }
  60.227 +    else {
  60.228 +      if((readpos=lseek64(fd,pos,SEEK_SET))>=0) {
  60.229 +        if(readpos!=pos) { dsyslog("seek mismatch in %s, wanted %lld, got %lld",Filename,pos,readpos); }
  60.230 +        return true;
  60.231 +        }
  60.232 +      else { esyslog("ERROR: seeking failed in %s: %d,%s",Filename,errno,strerror(errno)); }
  60.233 +      }
  60.234 +    }
  60.235 +  else d(printf("mp3: bad seek call fd=%d pos=%lld name=%s\n",fd,pos,Filename))
  60.236 +  return false;
  60.237 +}
  60.238 +
  60.239 +bool cStream::Stream(unsigned char * &data, unsigned long &len, const unsigned char *rest)
  60.240 +{
  60.241 +  if(fd>=0) {
  60.242 +    if(readpos<Filesize) {
  60.243 +      if(ismmap) {
  60.244 +        if(rest && fill) readpos=(rest-buffer);   // take care of remaining data
  60.245 +        fill=Filesize-readpos;
  60.246 +        data=buffer+readpos; len=fill;
  60.247 +        buffpos=readpos; readpos+=fill;
  60.248 +        return true;
  60.249 +        }
  60.250 +      else {
  60.251 +        if(rest && fill) {       // copy remaining data to start of buffer
  60.252 +          fill-=(rest-buffer);   // remaing bytes
  60.253 +          memmove(buffer,rest,fill);
  60.254 +          }
  60.255 +        else fill=0;
  60.256 +
  60.257 +        int r;
  60.258 +        do { 
  60.259 +          r=read(fd,buffer+fill,MP3FILE_BUFSIZE-fill);
  60.260 +          } while(r==-1 && errno==EINTR);
  60.261 +
  60.262 +        if(r>=0) {
  60.263 +          buffpos=readpos-fill; readpos+=r; fill+=r;
  60.264 +          data=buffer; len=fill;
  60.265 +          return true;
  60.266 +          }
  60.267 +        else { esyslog("ERROR: read failed in %s: %d,%s",Filename,errno,strerror(errno)); }
  60.268 +        }
  60.269 +      }
  60.270 +    else {
  60.271 +      len=0;
  60.272 +      return true;
  60.273 +      }
  60.274 +    }
  60.275 +  return false;
  60.276 +}
  60.277 +
  60.278 +// -----------------------------------------------------------------------------
  60.279 +
  60.280 +#ifdef DUMP_HEAD
  60.281 +void Dump(const char *name, char *buffer)
  60.282 +{
  60.283 +  FILE *f=fopen(name,"a");
  60.284 +  if(f) {
  60.285 +    fprintf(f,"<<<< %s\n",buffer);
  60.286 +/*
  60.287 +    int n=strlen(buffer);
  60.288 +    for(int i=0 ; i<n ; i+=8) {
  60.289 +      fprintf(f,"%04x: ",i);
  60.290 +      for(int l=0 ; l<8 && i+l<n ; l++) fprintf(f,"%02x ",buffer[i+l]);
  60.291 +      fprintf(f,"\n");
  60.292 +      }
  60.293 +*/
  60.294 +    fclose(f);
  60.295 +    }
  60.296 +}
  60.297 +#endif
  60.298 +
  60.299 +// --- cNetStream -----------------------------------------------------------------
  60.300 +
  60.301 +cNetStream::cNetStream(const char *Filename)
  60.302 +:cStream(Filename)
  60.303 +{
  60.304 +  net=0; host=path=auth=0; cc=0;
  60.305 +  icyName=icyUrl=icyTitle=0; icyChanged=false;
  60.306 +  metaInt=0;
  60.307 +  InfoDone();
  60.308 +}
  60.309 +
  60.310 +cNetStream::~cNetStream()
  60.311 +{
  60.312 +  free(host); free(path); free(auth);
  60.313 +  free(icyName); free(icyUrl); free(icyTitle);
  60.314 +}
  60.315 +
  60.316 +bool cNetStream::ParseURL(const char *line, bool log)
  60.317 +{
  60.318 +  char pr[32], h[512], p[512], a[512];
  60.319 +  int r=sscanf(line," %31[^:]://%511[^/]%511[^\r\n]",pr,h,p);
  60.320 +  if(r==2) {
  60.321 +    d(printf("netstream: adding default path '/'\n"))
  60.322 +    strcpy(p,"/");
  60.323 +    r++;
  60.324 +    }
  60.325 +  if(r==3) {
  60.326 +    a[0]=0;
  60.327 +    char *s=index(h,'@');
  60.328 +    if(s) {
  60.329 +      *s=0;
  60.330 +      strcpy(a,h);
  60.331 +      strcpy(h,s+1);
  60.332 +      }
  60.333 +    d(printf("netstream: parsed proto='%s' host='%s' path='%s' auth='%s'\n",pr,h,p,a))
  60.334 +    if(!strcasecmp(pr,"http")) {
  60.335 +      int pp=DEFAULT_PORT;
  60.336 +      s=rindex(h,':');
  60.337 +      if(s) { *s++=0; pp=atoi(s); }
  60.338 +
  60.339 +      free(host); host=strdup(h);
  60.340 +      free(path); path=strdup(p);
  60.341 +      free(auth); auth=a[0] ? strdup(a) : 0;
  60.342 +      port=pp;
  60.343 +      return true;
  60.344 +      }
  60.345 +    else if(log) esyslog("Unsupported protocol %s in: %s",pr,line);
  60.346 +    }
  60.347 +  else if(log) esyslog("Bad URL line: %s",line);
  60.348 +  return false;    
  60.349 +}
  60.350 +
  60.351 +bool cNetStream::ParseURLFile(const char *name, bool log)
  60.352 +{
  60.353 +  bool res=false;
  60.354 +  FILE *f=fopen(name,"r");
  60.355 +  if(f) {
  60.356 +    char line[2048];
  60.357 +    if(fgets(line,sizeof(line),f)) {
  60.358 +      res=ParseURL(line,log);
  60.359 +      }
  60.360 +    else if(log) esyslog("Nothing to read from URL file %s. File empty?",name);
  60.361 +    fclose(f);
  60.362 +    }
  60.363 +  else if(log) esyslog("fopen() failed on URL file %s: %s",name,strerror(errno));
  60.364 +  return res;
  60.365 +}
  60.366 +
  60.367 +bool cNetStream::SendRequest(void)
  60.368 +{
  60.369 +  bool res=false;
  60.370 +  char buff[2048];
  60.371 +
  60.372 +  char *h, *p;
  60.373 +  asprintf(&h,port!=DEFAULT_PORT ? "%s:%d":"%s",host,port);
  60.374 +  if(MP3Setup.UseProxy) asprintf(&p,"http://%s%s",h,path);
  60.375 +  else asprintf(&p,"%s",path);
  60.376 +
  60.377 +  char a[1024];
  60.378 +  a[0]=0;
  60.379 +  if(auth) {
  60.380 +    cBase64Encoder b64((uchar *)auth,strlen(auth),76);
  60.381 +    int q=0;
  60.382 +    const char *l;
  60.383 +    while((l=b64.NextLine())) {
  60.384 +      q+=snprintf(&a[q],sizeof(a)-q,"%s%s\r\n",q==0?"Authorization: Basic ":" ",l);
  60.385 +      }
  60.386 +    }
  60.387 +
  60.388 +  snprintf(buff,sizeof(buff),
  60.389 +           "GET %s HTTP/1.0\r\n"
  60.390 +           "User-Agent: %s/%s\r\n"
  60.391 +           "Host: %s\r\n"
  60.392 +           "Accept: audio/mpeg\r\n"   //XXX audio/x-mpegurl, */*
  60.393 +           "Icy-MetaData: 1\r\n"
  60.394 +           "%s\r\n",
  60.395 +           p,PLUGIN_NAME,PLUGIN_VERSION,h,a);
  60.396 +  free(p); free(h);  
  60.397 +
  60.398 +  if(++cc==1) asyncStatus.Set(tr("Connecting to stream server ..."));
  60.399 +
  60.400 +  if(net->Connect(MP3Setup.UseProxy ? MP3Setup.ProxyHost:host , MP3Setup.UseProxy ? MP3Setup.ProxyPort:port)) {
  60.401 +    d(printf("netstream: -> %s",buff))
  60.402 +    if(net->Puts(buff)>0) res=GetHTTPResponse();
  60.403 +    }
  60.404 +
  60.405 +  if(cc--==1) asyncStatus.Set(0);
  60.406 +  return res;
  60.407 +}
  60.408 +
  60.409 +bool cNetStream::ParseHeader(const char *buff, const char *name, char **value)
  60.410 +{
  60.411 +  char *s=index(buff,':');
  60.412 +  if(s && !strncasecmp(buff,name,s-buff)) {
  60.413 +    s=skipspace(s+1);
  60.414 +    d(printf("netstream: found header '%s' contents '%s'\n",name,s))
  60.415 +    free(*value); *value=strdup(s);
  60.416 +    return true;
  60.417 +    }
  60.418 +  return false;
  60.419 +}
  60.420 +
  60.421 +bool cNetStream::GetHTTPResponse(void)
  60.422 +{
  60.423 +  bool res=false;
  60.424 +  char buff[1024], text[128], *newurl=0;
  60.425 +  int code=-1, hcount=0;
  60.426 +  while(net->Gets(buff,sizeof(buff))>0) {
  60.427 +    stripspace(buff);
  60.428 +#ifdef DUMP_HEAD
  60.429 +    Dump(DUMP_HEAD,buff);
  60.430 +#endif
  60.431 +    d(printf("netstream: <- %s\n",buff))
  60.432 +    hcount++;
  60.433 +    if(hcount==1) {   // parse status line
  60.434 +      if(sscanf(buff,"%*[^ ] %d %128s",&code,text)!=2) {
  60.435 +        esyslog("Bad HTTP response '%s' from %s:%d",buff,host,port);
  60.436 +        goto out;
  60.437 +        }
  60.438 +      }
  60.439 +    else {            // parse header lines
  60.440 +      if(buff[0]==0) { // headers finish if we receive a empty line
  60.441 +        switch(code) {
  60.442 +          case 200: // OK
  60.443 +             res=true;
  60.444 +             goto out;
  60.445 +          case 300: // MULTIPLE_CHOICES
  60.446 +          case 301: // MOVED_PERMANENTLY
  60.447 +          case 302: // MOVED_TEMPORARILY
  60.448 +             if(newurl) {
  60.449 +               if(ParseURL(newurl,true)) res=SendRequest();
  60.450 +               }
  60.451 +             else esyslog("No location header for redirection from %s:%d",host,port);
  60.452 +             goto out;
  60.453 +          default:
  60.454 +             esyslog("Unhandled HTTP response '%d %s' from %s:%d",code,text,host,port);
  60.455 +             goto out;
  60.456 +          }
  60.457 +        }
  60.458 +
  60.459 +      ParseHeader(buff,"Location",&newurl);
  60.460 +      ParseHeader(buff,"icy-name",&icyName);
  60.461 +      ParseHeader(buff,"icy-url",&icyUrl);
  60.462 +      char *meta=0;
  60.463 +      if(ParseHeader(buff,"icy-metaint",&meta)) {
  60.464 +        metaInt=metaCnt=atol(meta);
  60.465 +        d(printf("netstream: meta interval set to %d\n",metaInt));
  60.466 +        }
  60.467 +      free(meta);
  60.468 +      }
  60.469 +    }
  60.470 +out:
  60.471 +  free(newurl);
  60.472 +  return res;
  60.473 +}
  60.474 +
  60.475 +bool cNetStream::Open(bool log)
  60.476 +{
  60.477 +  if(net && net->Connected()) return true;
  60.478 +
  60.479 +  if(!net) net=new cNet(0,0,0);
  60.480 +  net->Disconnect();
  60.481 +  
  60.482 +  if(ParseURLFile(Filename,log)) {
  60.483 +    buffpos=readpos=0; fill=0;
  60.484 +    buffer = new unsigned char[MP3FILE_BUFSIZE];
  60.485 +    if(buffer) {
  60.486 +      if(SendRequest()) {
  60.487 +        return true;
  60.488 +        }
  60.489 +      }
  60.490 +    else esyslog("Not enough memory for buffer");
  60.491 +    }
  60.492 +
  60.493 +  Close();
  60.494 +  return false;
  60.495 +}
  60.496 +
  60.497 +void cNetStream::Close(void)
  60.498 +{
  60.499 +  delete buffer; buffer=0;
  60.500 +  delete net; net=0;
  60.501 +}
  60.502 +
  60.503 +bool cNetStream::Seek(unsigned long long pos)
  60.504 +{
  60.505 +  return false;
  60.506 +}
  60.507 +
  60.508 +bool cNetStream::Stream(unsigned char * &data, unsigned long &len, const unsigned char *rest)
  60.509 +{
  60.510 +  if(net && net->Connected()) {
  60.511 +    if(rest && fill) {       // copy remaining data to start of buffer
  60.512 +      fill-=(rest-buffer);   // remaing bytes
  60.513 +      memmove(buffer,rest,fill);
  60.514 +      }
  60.515 +    else fill=0;
  60.516 +
  60.517 +    int r=MP3FILE_BUFSIZE-fill;
  60.518 +    if(metaInt && r>metaCnt) r=metaCnt;
  60.519 +    r=net->Read(buffer+fill,r);
  60.520 +    if(r>=0) {
  60.521 +      fill+=r; data=buffer; len=fill;
  60.522 +      metaCnt-=r;
  60.523 +      if(metaInt && metaCnt<=0) {
  60.524 +        ParseMetaData();
  60.525 +        metaCnt=metaInt;
  60.526 +        }
  60.527 +      return true;
  60.528 +      }
  60.529 +    }
  60.530 +  return false;
  60.531 +}
  60.532 +
  60.533 +char *cNetStream::ParseMetaString(const char *buff, const char *name, char **value)
  60.534 +{
  60.535 +  char *s=index(buff,'=');
  60.536 +  if(s && !strncasecmp(buff,name,s-buff)) {
  60.537 +    char *end=index(s+2,'\'');
  60.538 +    if(s[1]=='\'' && end) {
  60.539 +      *end=0;
  60.540 +      s=stripspace(skipspace(s+2));
  60.541 +      if(strlen(s)>0) {
  60.542 +        d(printf("netstream: found metadata '%s' contents '%s'\n",name,s))
  60.543 +        free(*value); *value=strdup(s);
  60.544 +        }
  60.545 +      //else d(printf("netstream: found empty metadata '%s'\n",name))
  60.546 +      return end+1;
  60.547 +      }
  60.548 +    else d(printf("netstream: bad metadata format\n"))
  60.549 +    }
  60.550 +  return 0;
  60.551 +}
  60.552 +
  60.553 +bool cNetStream::ParseMetaData(void)
  60.554 +{
  60.555 +  unsigned char byte;
  60.556 +  int r=net->Read(&byte,1);
  60.557 +  if(r<=0) return false;
  60.558 +  int metalen=byte*16;
  60.559 +  if(metalen>0) {
  60.560 +    char data[metalen+1];
  60.561 +    data[metalen]=0;
  60.562 +    int cnt=0;
  60.563 +    do {
  60.564 +      r=net->Read((unsigned char *)data+cnt,metalen-cnt);
  60.565 +      if(r<=0) return false;
  60.566 +      cnt+=r;
  60.567 +      } while(cnt<metalen);
  60.568 +
  60.569 +#ifdef DUMP_HEAD
  60.570 +    Dump(DUMP_HEAD,data);
  60.571 +#endif
  60.572 +
  60.573 +    char *p=data;
  60.574 +    while(*p && p-data<metalen) {
  60.575 +      char *n;
  60.576 +      if((n=ParseMetaString(p,"StreamTitle",&icyTitle)) ||
  60.577 +         (n=ParseMetaString(p,"StreamUrl",&icyUrl))) {
  60.578 +        p=n;
  60.579 +        icyChanged=true;
  60.580 +        }
  60.581 +      else  p++;
  60.582 +      }
  60.583 +    }
  60.584 +  return true;
  60.585 +}
  60.586 +
  60.587 +bool cNetStream::IcyChanged(void)
  60.588 +{
  60.589 +  bool c=icyChanged;
  60.590 +  icyChanged=false;
  60.591 +  return c;
  60.592 +}
    61.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    61.2 +++ b/stream.h	Sat Dec 29 14:47:40 2007 +0100
    61.3 @@ -0,0 +1,132 @@
    61.4 +/*
    61.5 + * MP3/MPlayer plugin to VDR (C++)
    61.6 + *
    61.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    61.8 + *
    61.9 + * This code is free software; you can redistribute it and/or
   61.10 + * modify it under the terms of the GNU General Public License
   61.11 + * as published by the Free Software Foundation; either version 2
   61.12 + * of the License, or (at your option) any later version.
   61.13 + *
   61.14 + * This code is distributed in the hope that it will be useful,
   61.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   61.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   61.17 + * GNU General Public License for more details.
   61.18 + *
   61.19 + * You should have received a copy of the GNU General Public License
   61.20 + * along with this program; if not, write to the Free Software
   61.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   61.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   61.23 + */
   61.24 +
   61.25 +#ifndef ___STREAM_H
   61.26 +#define ___STREAM_H
   61.27 +
   61.28 +#include "decoder.h"
   61.29 +
   61.30 +class cNet;
   61.31 +
   61.32 +// ----------------------------------------------------------------
   61.33 +
   61.34 +#if 0
   61.35 +class cIO : public cFileInfo {
   61.36 +protected:
   61.37 +  bool log;
   61.38 +  unsigned long long readpos;
   61.39 +public:
   61.40 +  cIO(const char *Filename, bool Log);
   61.41 +  virtual ~cIO();
   61.42 +  virtual bool Open(void)=0;
   61.43 +  virtual void Close(void)=0;
   61.44 +  virtual int Read(unsigned char *Data, int Size)=0;
   61.45 +  virtual unsigned char *StreamInit(int Size) { return 0; }
   61.46 +  virtual int Stream(const unsigned char *rest) { return -1; }
   61.47 +  virtual bool Seek(unsigned long long pos, int whence) { return -1; }
   61.48 +  virtual unsigned long long Tell(void) { return readpos; }
   61.49 +  };
   61.50 +
   61.51 +// ----------------------------------------------------------------
   61.52 +
   61.53 +class cStreamIO {
   61.54 +private:
   61.55 +  int size, fill;
   61.56 +  unsigned char *data;
   61.57 +protected:
   61.58 +  void StreamClear(bool all);
   61.59 +public:
   61.60 +  cStreamIO(void);
   61.61 +  virtual ~cStreamIO();
   61.62 +  virtual unsigned char *StreamInit(int Size);
   61.63 +  virtual int Stream(const unsigned char *rest);
   61.64 +  virtual int Read(unsigned char *Data, int Size)=0;
   61.65 +  };
   61.66 +
   61.67 +// ----------------------------------------------------------------
   61.68 +
   61.69 +class cFileIO : public cIO, public cStreamIO {
   61.70 +protected:
   61.71 +  int fd;
   61.72 +public:
   61.73 +  cFileIO(const char *Filename, bool Log);
   61.74 +  virtual ~cFileIO();
   61.75 +  virtual bool Open(void);
   61.76 +  virtual void Close(void);
   61.77 +  virtual int Read(unsigned char *Data, int Size);
   61.78 +  virtual bool Seek(unsigned long long pos, int whence);
   61.79 +  };
   61.80 +#endif
   61.81 +
   61.82 +// ----------------------------------------------------------------
   61.83 +
   61.84 +class cStream : public cFileInfo {
   61.85 +private:
   61.86 +  int fd;
   61.87 +  bool ismmap;
   61.88 +protected:
   61.89 +  unsigned char *buffer;
   61.90 +  unsigned long long readpos, buffpos;
   61.91 +  unsigned long fill;
   61.92 +public:
   61.93 +  cStream(const char *Filename);
   61.94 +  virtual ~cStream();
   61.95 +  virtual bool Open(bool log=true);
   61.96 +  virtual void Close(void);
   61.97 +  virtual bool Stream(unsigned char *&data, unsigned long &len, const unsigned char *rest=NULL);
   61.98 +  virtual bool Seek(unsigned long long pos=0);
   61.99 +  virtual unsigned long long BufferPos(void) { return buffpos; }
  61.100 +  };
  61.101 +
  61.102 +// ----------------------------------------------------------------
  61.103 +
  61.104 +class cNetStream : public cStream {
  61.105 +private:
  61.106 +  cNet *net;
  61.107 +  char *host, *path, *auth;
  61.108 +  int port, cc;
  61.109 +  //
  61.110 +  char *icyName, *icyUrl, *icyTitle;
  61.111 +  int metaInt, metaCnt;
  61.112 +  bool icyChanged;
  61.113 +  //
  61.114 +  bool ParseURL(const char *line, bool log);
  61.115 +  bool ParseURLFile(const char *name, bool log);
  61.116 +  bool SendRequest(void);
  61.117 +  bool GetHTTPResponse(void);
  61.118 +  bool ParseHeader(const char *buff, const char *name, char **value);
  61.119 +  bool ParseMetaData(void);
  61.120 +  char *ParseMetaString(const char *buff, const char *name, char **value);
  61.121 +public:
  61.122 +  cNetStream(const char *Filename);
  61.123 +  virtual ~cNetStream();
  61.124 +  virtual bool Open(bool log=true);
  61.125 +  virtual void Close(void);
  61.126 +  virtual bool Stream(unsigned char *&data, unsigned long &len, const unsigned char *rest=NULL);
  61.127 +  virtual bool Seek(unsigned long long pos=0);
  61.128 +  bool Valid(void) { return ParseURLFile(Filename,false); }
  61.129 +  const char *IcyName(void) const { return icyName; }
  61.130 +  const char *IcyUrl(void) const { return icyUrl; }
  61.131 +  const char *IcyTitle(void) const { return icyTitle; }
  61.132 +  bool IcyChanged(void);
  61.133 +  };
  61.134 +
  61.135 +#endif //___STREAM_H
    62.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    62.2 +++ b/version.h	Sat Dec 29 14:47:40 2007 +0100
    62.3 @@ -0,0 +1,28 @@
    62.4 +/*
    62.5 + * MP3/MPlayer plugin to VDR (C++)
    62.6 + *
    62.7 + * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
    62.8 + *
    62.9 + * This code is free software; you can redistribute it and/or
   62.10 + * modify it under the terms of the GNU General Public License
   62.11 + * as published by the Free Software Foundation; either version 2
   62.12 + * of the License, or (at your option) any later version.
   62.13 + *
   62.14 + * This code is distributed in the hope that it will be useful,
   62.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   62.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   62.17 + * GNU General Public License for more details.
   62.18 + *
   62.19 + * You should have received a copy of the GNU General Public License
   62.20 + * along with this program; if not, write to the Free Software
   62.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   62.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
   62.23 + */
   62.24 +
   62.25 +#ifndef ___VERSION_H
   62.26 +#define ___VERSION_H
   62.27 +
   62.28 +#define PLUGIN_NAME    "VDR-MP3"
   62.29 +#define PLUGIN_VERSION "0.10.0"
   62.30 +
   62.31 +#endif //___VERSION_H