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 π
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 π
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 π
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