summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlordjaxom <lordjaxom>2004-12-30 22:43:55 +0000
committerlordjaxom <lordjaxom>2004-12-30 22:43:55 +0000
commit302fa2e67276bd0674e81e2a9a01b9e91dd45d8c (patch)
treea454884a16e0ffa48b5ce3e4ce1a66eb874a9de0
downloadvdr-plugin-streamdev-302fa2e67276bd0674e81e2a9a01b9e91dd45d8c.tar.gz
vdr-plugin-streamdev-302fa2e67276bd0674e81e2a9a01b9e91dd45d8c.tar.bz2
Initial revision
-rw-r--r--CONTRIBUTORS21
-rw-r--r--COPYING340
-rw-r--r--HISTORY146
-rw-r--r--Makefile136
-rw-r--r--PROTOCOL139
-rw-r--r--README298
-rw-r--r--client/assembler.c125
-rw-r--r--client/assembler.h32
-rw-r--r--client/device.c185
-rw-r--r--client/device.h58
-rw-r--r--client/filter.c141
-rw-r--r--client/filter.h64
-rw-r--r--client/menu.c1047
-rw-r--r--client/menu.h144
-rw-r--r--client/remote.c475
-rw-r--r--client/remote.h132
-rw-r--r--client/setup.c83
-rw-r--r--client/setup.h39
-rw-r--r--client/socket.c591
-rw-r--r--client/socket.h71
-rw-r--r--common.c259
-rw-r--r--common.h119
-rw-r--r--i18n.c751
-rw-r--r--i18n.h16
-rw-r--r--libdvbmpeg/.cvsignore1
-rw-r--r--libdvbmpeg/DVB.hh446
-rw-r--r--libdvbmpeg/Makefile33
-rw-r--r--libdvbmpeg/OSD.h30
-rw-r--r--libdvbmpeg/channel.h58
-rw-r--r--libdvbmpeg/ci.hh167
-rw-r--r--libdvbmpeg/cpptools.cc946
-rw-r--r--libdvbmpeg/cpptools.hh330
-rw-r--r--libdvbmpeg/ctools.c2379
-rw-r--r--libdvbmpeg/ctools.h404
-rw-r--r--libdvbmpeg/devices.hh310
-rw-r--r--libdvbmpeg/osd.hh84
-rw-r--r--libdvbmpeg/remux.c1215
-rw-r--r--libdvbmpeg/remux.h149
-rw-r--r--libdvbmpeg/ringbuffy.c201
-rw-r--r--libdvbmpeg/ringbuffy.h52
-rw-r--r--libdvbmpeg/transform.c2681
-rw-r--r--libdvbmpeg/transform.h250
-rw-r--r--patches/thread.c.diff29
-rw-r--r--patches/vdr-1.3.11-localchannelprovide.diff61
-rw-r--r--patches/vdr-1.3.6-incompletesections.diff22
-rw-r--r--patches/vdr-pluginactivity.diff113
-rw-r--r--remux/ts2es.c88
-rw-r--r--remux/ts2es.h21
-rw-r--r--remux/ts2ps.c104
-rw-r--r--remux/ts2ps.h22
-rw-r--r--remux/tsremux.c185
-rw-r--r--remux/tsremux.h30
-rw-r--r--server/component.c50
-rw-r--r--server/component.h50
-rw-r--r--server/componentHTTP.c15
-rw-r--r--server/componentHTTP.h26
-rw-r--r--server/componentVTP.c15
-rw-r--r--server/componentVTP.h29
-rw-r--r--server/connection.c172
-rw-r--r--server/connection.h87
-rw-r--r--server/connectionHTTP.c180
-rw-r--r--server/connectionHTTP.h44
-rw-r--r--server/connectionVTP.c553
-rw-r--r--server/connectionVTP.h57
-rw-r--r--server/livefilter.c64
-rw-r--r--server/livefilter.h29
-rw-r--r--server/livestreamer.c242
-rw-r--r--server/livestreamer.h70
-rw-r--r--server/server.c148
-rw-r--r--server/server.h44
-rw-r--r--server/setup.c94
-rw-r--r--server/setup.h41
-rw-r--r--server/streamer.c99
-rw-r--r--server/streamer.h43
-rw-r--r--server/suspend.c69
-rw-r--r--server/suspend.dat1206
-rw-r--r--server/suspend.h41
-rw-r--r--streamdev-client.c57
-rw-r--r--streamdev-client.h29
-rw-r--r--streamdev-server.c72
-rw-r--r--streamdev-server.h29
-rw-r--r--streamdevhosts.conf.example13
-rw-r--r--tools/file.c95
-rw-r--r--tools/file.h100
-rw-r--r--tools/select.c52
-rw-r--r--tools/select.h75
-rw-r--r--tools/shared.c90
-rw-r--r--tools/shared.h65
-rw-r--r--tools/socket.c135
-rw-r--r--tools/socket.h108
-rw-r--r--tools/source.c195
-rw-r--r--tools/source.h107
-rw-r--r--tools/string.c454
-rw-r--r--tools/string.h353
-rw-r--r--tools/tools.c12
-rw-r--r--tools/tools.h67
96 files changed, 21399 insertions, 0 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644
index 0000000..f872f9a
--- /dev/null
+++ b/CONTRIBUTORS
@@ -0,0 +1,21 @@
+Special thanks go to the following persons (if you think your name is missing
+here, please send an email to sascha@akv-soft.de):
+
+The Metzler Brothers
+ because I took a whole lot of code from their libdvbmpeg package
+
+Angelus (DOm)
+ for providing italian language texts
+ for reporting problems with the Elchi-Patch
+
+Michal
+ for sending a patch to select the HTTP streamtype via remote
+
+Rolf Ahrenberg
+ for providing finnish language texts
+
+Rantanen Teemu
+ for providing vdr-incompletesections.diff
+
+Thomas Keil
+ for providing vdr-localchannelprovide.diff
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..5018176
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,146 @@
+VDR Plugin 'streamdev' Revision History
+---------------------------------------
+
+2004-??-??: Version 0.3.3
+
+- dropped support for non-ts streaming in vdr-to-vdr clients
+- implemented packet buffer that seems to improve distortions
+- greatly re-worked device selection on server and client
+ (vdr-to-vdr clients should behave exactly like clients with one card,
+ can't test conditional access, though)
+- now printing an error and exiting if streamdevhosts.conf is not existing
+- increased client stream priority to 1
+- implemented remote schedule to program remote timers directly from schedule
+- the servers are turned on by default now
+- new setup parameters "Bind to IP" for both servers for binding to a specific
+ interface
+- re-implemented section streaming (turned off by default, see setup menu)
+- implemented a possibility to prevent a shutdown when clients are connected
+ (patch VDR with patches/vdr-pluginactivity.diff if you want this feature)
+- implemented channel listing through channels.htm(l) URI
+
+????-??-??: Version 0.3.2
+
+... has myteriously disappeared :-)
+
+2004-02-16: Version 0.3.1 (unstable)
+
+- Added finnish language texts (thanks to Rolf Ahrenberg)
+- Increased all ringbuffer sizes to 3 MB
+- Autodetecting VDR 1.2.x, 1.2.x with AutoPID and 1.3.x on compilation
+- Server is only restarted if necessary after confirming setup
+- Implemented PID-based streaming (only needed PIDs are transferred instead of
+ all PIDs of the requested channel) (configurable)
+- Implemented an editor for remote timers
+- Implemented manual EPG synchronization from client
+- Implemented Server Suspend remotely from client (configurable)
+- Implemented an IP-Editor for the setup menu
+- Separated Client and Server into two PlugIns
+- Increased initial number of clients to five
+- Implemented host-based authorization (syntax is equal to svdrphosts.conf)
+- Removed two irritating messages that appeared sometimes while exiting VDR
+- Implemented "Choose, Always, Never" for Suspend Mode, so it can be configured
+ to behave like 0.2.0 (Always), 0.3.0 (Choose) or completely different (Never)
+- Added missing translation entries
+- Added PlugIn description to translation table
+- Fully upgraded to VDR 1.3.X regarding threading (but still works with 1.2.6)
+- Reworked manual (almost everything)
+
+2003-10-10: Version 0.3.0 (unstable)
+
+- Implemented "Suspend Live TV" in the VDR server (configurable)
+- Reimplemented choice of device for live streaming (better for switching on
+ client, and server doesn't loose live-tv)
+- Added missing translation entries
+- Increased client's streaming buffer size from 1 to 3 MB
+- Updated installation instructions (including a patch to VDR that is
+ recommended currently)
+- Updated manual
+
+2003-10-04: Version 0.2.0
+
+- Removed those silly warnings in the toolbox-headers
+- Implemented intelligent buffer overflow logging (doesn't flood syslog)
+- Implemented EPG synchronization in the VDR client (configurable)
+- Station name is transmitted in radio streaming now (Shoutcast-format).
+
+2003-09-24: Version 0.1.1beta1
+
+- Restructured remuxer code
+- Added an ES-remuxer for radio channels (currently only manually)
+
+2003-09-20: Version 0.1.0
+
+- Fixed thread-abortion timeout in server thread
+
+2003-08-31: Version 0.1.0beta4
+
+- Added italian language texts (thanks to Angelus (DOm))
+- Added a missing i18n translation (thanks to DOm)
+- Added an #ifdef so the setup menu is displayed correctly with ElchiAIO
+ (thanks to DOm for reporting this one)
+- It's possible to select the HTTP streamtype remotely, specified in the
+ URL in addition to the old behaviour (thanks to Michal Novotny)
+- Fixed creation ob remuxer objects in the server
+- Fixed handling of timeout in cTBSelect
+
+2003-06-08: Version 0.1.0beta3
+
+- Fixed setup menu - now the cursor starts at the first visible entry
+- Added PS streaming for HTTP (should work with most players now)
+- Debugging symbols are only compiled with DEBUG=1 set
+
+2003-06-06: Version 0.1.0beta2
+
+- Added an #ifdef so this PlugIn will compile cleanly with the next
+ AUTOPID-patches
+- Added categories to the menu
+- Fixed segfault when closing the menu with OK
+- Added an AnalogTV section to the README
+- Added some missing i18n entries
+- Corrected client reinitialization code (when changing client settings)
+- Added PS streaming for HTTP (should work with most players now)
+- Added -D_GNU_SOURCE to the Makefile (.......)
+
+2003-06-03: Version 0.1.0beta1
+
+- Replaced the toolbox with a current version
+- Rewrote the server core from scratch
+- Rewrote the client core from scratch
+- Reduced the size of blocks processed in a transceiver turn to 10 TS packets
+- Added TS transmission for HTTP (configurable via setup)
+- Most client settings can be done on-the-fly now
+- MIME type for radio channels now "audio/mpeg" instead of "video/mpeg"
+ (still doesn't work really)
+
+2003-05-08: Version 0.0.3beta1
+
+- Server stops correctly on VDR exit
+- Fixed a race condition when several threads access the client device
+- Made server code more modular
+- Structured the directories
+- Fixed a bug in informational log-message
+- Added Apid2, Dpid1 and Ppid in TS mode (silly me;) )
+
+2003-05-03: Version 0.0.2
+
+- Device is not deactivated anymore, since VDR does that itself
+- Server is correctly deactivated, so it can be faultlessly reactivated
+- Did some major code cleanup
+- Added new command to the PROTOCOL (to negotiate stream types)
+- Added the possibility to stream TS between two VDR's (which adds the
+ possibility of having AC3, Teletext etc. on the client) - this is
+ autonegotiated
+- Streamtype can be changed in the setup menu, if TS works too unreliable
+- Fixed a bug in multi-threaded device operation
+- Sharing an epg.data with a server will be possible even if there is no
+ DVB-Device present
+- Added a basic HTTP daemon to the server code
+
+2003-03-17: Version 0.0.1a
+
+- Corrected some bugs in the README and on the homepage *g*
+
+2003-03-17: Version 0.0.1
+
+- Initial revision.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..405a200
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,136 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id: Makefile,v 1.1 2004/12/30 22:43:58 lordjaxom Exp $
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+#
+PLUGIN = streamdev
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'const char \*VERSION *=' common.c | awk '{ print $$5 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -W -Woverloaded-virtual
+
+### The directory environment:
+
+DVBDIR = ../../../../DVB
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR (taken from VDR's "config.h"):
+
+VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include -I.
+
+DEFINES += -D_GNU_SOURCE
+
+### The object files (add further files here):
+
+COMMONOBJS = common.o i18n.o \
+ \
+ tools/file.o tools/source.o tools/select.o tools/shared.o tools/socket.o \
+ tools/string.o tools/tools.o
+
+CLIENTOBJS = $(PLUGIN)-client.o \
+ \
+ client/socket.o client/device.o client/setup.o client/menu.o \
+ client/remote.o client/assembler.o client/filter.o
+
+
+SERVEROBJS = $(PLUGIN)-server.o \
+ \
+ server/server.o server/connectionVTP.o server/connectionHTTP.o \
+ server/componentHTTP.o server/componentVTP.o server/connection.o \
+ server/component.o server/suspend.o server/setup.o server/streamer.o \
+ server/livestreamer.o server/livefilter.o \
+ \
+ remux/tsremux.o remux/ts2ps.o remux/ts2es.o
+
+ifdef DEBUG
+ DEFINES += -DDEBUG
+ CXXFLAGS += -g
+else
+ CXXFLAGS += -O2
+endif
+
+ifeq ($(shell test -f $(VDRDIR)/fontsym.h ; echo $$?),0)
+ DEFINES += -DHAVE_BEAUTYPATCH
+endif
+
+ifeq ($(shell test -f $(VDRDIR)/fontsym.c ; echo $$?),0)
+ DEFINES += -DHAVE_BEAUTYPATCH
+endif
+
+# HAVE_AUTOPID only applies if VDRVERSNUM < 10300
+ifeq ($(shell test -f $(VDRDIR)/sections.c ; echo $$?),0)
+ DEFINES += -DHAVE_AUTOPID
+endif
+
+libdvbmpeg/libdvbmpegtools.a: libdvbmpeg/*.c libdvbmpeg/*.cc libdvbmpeg/*.h libdvbmpeg/*.hh
+ make -C ./libdvbmpeg libdvbmpegtools.a
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
+
+# Dependencies:
+
+MAKEDEP = g++ -MM -MG
+DEPFILE = .dependencies
+ifdef GCC3
+$(DEPFILE): Makefile
+ @rm -f $@
+ @for i in $(CLIENTOBJS:%.o=%.c) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) ; do \
+ $(MAKEDEP) $(DEFINES) $(INCLUDES) -MT "`dirname $$i`/`basename $$i .c`.o" $$i >>$@ ; \
+ done
+else
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(SERVEROBJS:%.o=%.c) \
+ $(COMMONOBJS:%.o=%.c) > $@
+endif
+
+-include $(DEPFILE)
+
+### Targets:
+
+all: libvdr-$(PLUGIN)-client.so libvdr-$(PLUGIN)-server.so
+
+libvdr-$(PLUGIN)-client.so: $(CLIENTOBJS) $(COMMONOBJS) libdvbmpeg/libdvbmpegtools.a
+libvdr-$(PLUGIN)-server.so: $(SERVEROBJS) $(COMMONOBJS) libdvbmpeg/libdvbmpegtools.a
+
+%.so:
+ $(CXX) $(CXXFLAGS) -shared $^ -o $@
+ @cp $@ $(LIBDIR)/$@.$(VDRVERSION)
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz --exclude SCCS -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(SERVEROBJS) $(DEPFILE) *.so *.tgz core* *~
+ make -C ./libdvbmpeg clean
diff --git a/PROTOCOL b/PROTOCOL
new file mode 100644
index 0000000..a0c999e
--- /dev/null
+++ b/PROTOCOL
@@ -0,0 +1,139 @@
+Written by: Sascha Volkenandt <sascha@akv-soft.de>
+
+Project's homepage: http://www.magoa.net/linux/
+
+Version: 0.0.3
+
+Description:
+------------
+
+I call this protocol "VTP", the Video Transfer Protocol. I hope that's not
+already claimed by someone ;).
+
+This Protocol was created for Video Transfers over a Network. It is a text-
+based protocol like the FTP, and is used by a client to communicate with a
+server providing different types of video data, such as live streams,
+recordings or disc media. The basic communication consists of short text
+commands sent by the client, answered by numerical codes accompanied by
+human-readable messages. All messages should be finished by a full CR/LF
+line-ending, which should preferably written as "\015\012", as this is fully
+platform-independent. Nevertheless, a client or (especially) a server should
+also act on "\n" line-endings. The MPEG data is being transmitted over a
+separate data connection.
+
+TODO:
+- PORT behaviour changed
+- TUNE syntax changed
+- connection IDs
+- new command PLAY
+
+
+Response Code Summary
+
+Code Meaning
+220 Last command ok / connection ready
+221 Service is closing the connection afterwards
+500 The command was not recognized
+501 The parameters couldn't be interpreted correctly
+550 Action not taken, for various reason
+551 Action not taken, a subsequent connection was unsuccessful
+560 Live-Stream not available currently [changed in 0.0.3]
+561 Capability not known [new in 0.0.2]
+562 Pid currently not available [new in 0.0.3]
+563 Stream not available currently [new in 0.0.3]
+
+
+Command Reference
+
+Command: Connect to VTP Server
+Responses: 220 - The server is ready
+Description: Upon connection to the server (which usually listens at port
+ 2004), the first thing the client has to expect is a welcome message with
+ the "220" response code. The client may now send a CAPS command, to tell
+ the server it's capabilities.
+
+Command: CAPS <Capability>
+Responses: 220 - This capability is known and will be used from now on.
+ 561 - This capability is unknown, try anotherone
+Description: This command tells the server to serve media data in a specific
+ format, like "PES" (for MPEG2-PES) or "TS" (for MPEG2-TS). A client can
+ do several CAPS commands until the server accepts one. So a client should
+ try all formats it can handle, descending from the most preffered one. If
+ no such command is sent, streaming is defaulted to PES.
+ Capabilities currently used:
+ TS Transport Stream (all PIDs belonging to a channel)
+ TSPIDS Only in conjunction with TS: Stream PIDs separately upon request
+ (this enables the ADDP/DELP commands)
+ PES Program Elementary stream (Video and primary Audio stream)
+[new in 0.0.2,updated in 0.0.3]
+
+Command: PROV <Priority> <Media>
+Responses: 220 - Media available for receive
+ 501 - The parameters were incorrect
+ 550 - The media couldn't be identified
+ 560 - This server can currently not serve that media
+Description: With this command, the server is asked if the given media can
+ be received. The Priority is a number between 0 and 100 (in case a media
+ can not be received by an unlimited number of clients, the server shall
+ grant higher priorities before lower ones, and it shall also quit streams
+ with lower permissions if a higher one is requested), or -1 to ask the
+ server if this media is available at all.
+ The Media is a string defining the wanted media type. This is currently for
+ free use, it could for example carry a VDR unique channel id, to specify
+ a TV channel.
+
+Command: PORT <Id> <Address and Port>
+Responses: 220 - The data connection was opened
+ 501 - The parameter list was wrong
+ 551 - The data connection was refused by the client or timed out
+Description: The PORT command tells the server the target of a following
+ media transmission. The parameter Id specifies an index which is used to
+ establish multiple data connections over one control connection. It is used
+ later in the ABRT command to close a specific data connection. The second
+ parameter argument has six comma-separated fields, of which the first four
+ represent the target IP address, in the byte-order as the dot-notation
+ would be printed. The last two fields represent the target port, with the
+ high-byte first. To calculate the actual values, you could use the
+ following:
+ Field(5) = (RealPort & 0xFF00) shr 8
+ Field(6) = RealPort & 0xFF
+ Reversed:
+ RealPort = (Field(5) shl 8) + Field(6)
+ After receiving the port command, the data connection is opened but no data
+ is transferred, yet.
+ Id's currently used:
+ 0 Data connection for live streams
+ 1 Data connection for saved streams
+[changed in 0.0.3]
+
+Command: TUNE <Priority> <Media>
+Responses: 220 - Data connection was opened successfully
+ 550 - The media couldn't be identified
+ 560 - The live stream is unavailable
+Description: This command tells the media server to start the transfer over a
+ connection to a remote target established by the PORT command before.
+ Please look at the PROV command for the meaning of the parameters. The
+ server begins to send MPEG data. After the transfer has been started, the
+ response code "220" is sent.
+
+Command: ADDP <Pid>
+Responses: 220 - The requested Pid is transferring
+ 560 - The requested Pid is not available currently
+Description: This tells the server to start the transfer of a specific Pid
+ over a data connection established by the PORT command before.
+
+Command: DELP <Pid>
+Responses: 220 - The requested Pid is transferring
+ 560 - The requested Pid was not transferring
+Description: This tells the server to stop the transfer of a specific Pid
+ enabled by DELP before.
+
+Command: ABRT <Id>
+Responses: 220 - Data connection closed
+Description: This one should be sent before requesting another media or when
+ a media isn't needed anymore. It terminates the data connection previously
+ opened by PORT.
+
+Command: QUIT
+Responses: 221 - Connection is being closed afterwards
+Description: This commands terminates the client connection.
diff --git a/README b/README
new file mode 100644
index 0000000..0a8ec0d
--- /dev/null
+++ b/README
@@ -0,0 +1,298 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Sascha Volkenandt <sascha@akv-soft.de>
+
+Project's homepage: http://www.magoa.net/linux/
+
+Latest version available at: http://www.magoa.net/linux/index.php?view=streamdev
+
+See the file COPYING for license information.
+
+Contents:
+---------
+
+1. Description
+2. Installation
+2.1 VDR 1.2.X
+2.2 VDR 1.3.X
+3. Usage
+3.1 Usage VDR-to-VDR server
+3.2 Usage HTTP server
+3.3 Usage VDR-to-VDR client
+3.4 General Usage Notes
+4. VDR-to-VDR client notes (PLEASE READ IF YOU HAVE ONE)
+4.1 EPG data [OUTDATED]
+4.2 Teletext / OSD Teletext
+4.3 AnalogTV [OUTDATED]
+5. Known Problems
+
+
+1. Description:
+---------------
+
+This PlugIn is a VDR implementation of the VTP (Video Transfer Protocol)
+Version 0.0.3 (see file PROTOCOL) and a basic HTTP Streaming Protocol.
+
+It consists of a server and a client part, but both parts are compiled together
+with the PlugIn source, but appear as separate PlugIns to VDR.
+
+The client part acts as a full Input Device, so it can be used in conjunction
+with a DXR3-Card, XINE, SoftDevice or others to act as a working VDR
+installation without any DVB-Hardware including EPG-Handling.
+
+The server part acts as a Receiver-Device and works transparently in the
+background within your running VDR. It can serve multiple clients and it can
+distribute multiple input streams (i.e. from multiple DVB-cards) to multiple
+clients using the native VTP protocol (for VDR-clients), or using the HTTP
+protocol supporting clients such as XINE, MPlayer and so on. With XMMS or
+WinAMP, you can also listen to radio channels over a HTTP connection.
+
+It is possible to attach as many clients as the bus and network can handle, as
+long as there is a device which can receive a specific channel. Multiple
+channels homed on the same transponder (which is determined by it's frequency)
+can be broadcasted with a single device.
+
+Additional clients can be programmed using the Protocol Instructions inside
+the PROTOCOL file.
+
+
+2. Installation:
+----------------
+
+Let's say streamdev's version is 0.3.1 and vdr's version is 1.X.X. If you
+use anything else please exchange the version numbers appropriately (this
+way I don't have to update this section all the times;) ).
+
+After compiling the PlugIn as stated below, start either (or both) parts of it
+by specifying "-P streamdev-client" and/or "-P streamdev-server" on the VDR
+command line.
+
+What's important is that the client requests a channel using its Unique Channel
+ID. So, in order to find the channel at the server, it must have the same ID
+that is used on the client. You can achieve this by putting the server's
+channels.conf on the client, preferably after scanning (in case you use 1.2.X
+with AutoPID or 1.3.X).
+
+If you want to drive additional Input-Devices (with different sources) on the
+client, you can merge the channels.conf files. VDR will detect if the local
+device or the network device can receive the channels.
+
+Last, but not least you have to put the provided streamdevhosts.conf.example
+into the "plugins" subfolder of your config-directory (which is equal to your
+video-directory if not specified otherwise), rename it to streamdevhosts.conf
+and adjust it to your needs. The syntax is the same as for svdrphosts.conf, so
+please consult VDR's documentation on how to fill that file, if you can't do
+it on-the-fly. For example, if you didn't specify a separate config-directory,
+and specified your video directory as "/video0", the file has to be put to
+/video0/plugins/streamdevhosts.conf.
+
+
+2.1 VDR 1.2.X:
+--------------
+
+It is recommended that you apply a patch to VDR that improves thread
+cancellation. You can work without it, but you _might_ have delays in switching
+(especially when using VDR-to-VDR streaming) that are around three seconds.
+
+cd vdr-1.X.X/PLUGINS/src
+tar xvfz vdr-streamdev-0.3.1.tgz
+ln -s streamdev-0.3.1 streamdev
+cd ../..
+patch -p1 <PLUGINS/src/streamdev/patches/thread.c.diff
+make [options, if necessary] vdr
+make [options, if necessary] plugins
+
+2.2 VDR 1.3.X:
+--------------
+
+cd vdr-1.X.X/PLUGINS/src
+tar xvfz vdr-streamdev-0.3.1.tgz
+ln -s streamdev-0.3.1 streamdev
+cd ../..
+make [options, if necessary] vdr
+make [options, if necessary] plugins
+
+
+3. Usage:
+---------
+
+Start the server core itself by specifying -Pstreamdev-server on your VDR
+commandline. To use the client core, specify -Pstreamdev-client. Both parts
+can run in one VDR instance, if necessary.
+
+The parameter "Suspend behaviour" allows you to specify how the server should
+react in case the client requests a channel that would require switching the
+primary device (i.e. disrupt live-tv). If set to "Offer suspend mode" (the
+default), you will have a new entry in the main menu. Activating that will put
+the server into "Suspend Mode" (a picture is displayed on TV). Then, a client
+may switch the primary card to wherever it likes to. While watching TV (Suspend
+deactivated), the client may not switch the transponder on the primary device.
+If you set the behaviour to "Always suspended", there will be normal live-tv
+on the server, but whenever a client decides to switch the transponder, the
+server will lose it's live-tv. Set to "Never suspended", the server always
+prevents the client from switching transponders. If you set "Client may
+suspend" to yes, the client can suspend the server remotely (this only applies
+if "Offer suspend mode" is selected).
+
+
+3.1 Usage VDR-to-VDR server:
+----------------------------
+
+You can activate the VDR-to-VDR server part in the PlugIn's Setup Menu. It is
+deactivated by default. The Parameter "VDR-to-VDR Server Port" specifies the
+port where you want the server to listen for incoming connections. The server
+will be activated when you push the OK button inside the setup menu, so there's
+no need to restart VDR.
+
+NOTE: This mainly applies to One-Card-Systems, since with multiple cards there
+is no need to switch transponders on the primary interface, if the secondary
+can stream a given channel (i.e. if it is not blocked by a recording). If both
+cards are in use (i.e. when something is recorded, or by multiple clients),
+this applies to Multiple-Card-Systems as well.
+
+3.2 Usage HTTP server:
+----------------------
+
+You can use the HTTP part by accessing the server with a HTTP-capable media
+player (such as XINE, MPlayer, and so on, if you have appropriate MPEG2-codecs
+installed). In the PlugIn's Setup, you can specify the port the server will
+listen to with the parameter "HTTP Server Port". The parameter "HTTP Streamtype"
+allows you to specify a default stream type, which is used if no specific type
+has been requested in the URL (see below). The supported stream types are:
+
+TS Transport Stream (i.e. a dump from the device)
+PES Packetized Elemetary Stream (VDR's native recording format)
+PS Program Stream (SVCD, DVD like stream)
+ES Elementary Stream (only Video, if available, otherwise only Audio)
+
+If you leave the default port (3000), you can access the streams like this:
+
+http://hostname:3000/3
+http://hostname:3000/S19.2E-0-12480-898
+
+The first one will deliver a channel by number on the server, the second one
+will request the channel by unique channel id. In addition, you can specify
+the desired stream type as a path to the channel.
+
+http://hostname:3000/TS/3
+http://hostname:3000/PES/S19.2E-0-12480-898
+
+The first one would deliver the stream in TS, the second one in PES format.
+Possible values are 'PES', 'TS', 'PS' and 'ES'. You need to specify the ES
+format explicitly if you want to listen to radio channels. Play them pack i.e.
+with mpg123.
+
+mpg123 http://hostname:3000/ES/200
+
+3.3 Usage VDR-to-VDR client:
+----------------------------
+
+The parameter "Remote IP" uses an IP-Adress-Editor, where you can just enter
+the IP number with the number keys on your remote. After three digits (or if
+the next digit would result in an invalid IP adress, or if the first digit is
+0), the current position jumps to the next one. You can change positions with
+the left and right buttons, and you can cycle the current position using up
+and down. To confirm the entered address, press OK. So, if you want to enter
+the IP address "127.0.0.1", just mark the corresponding entry as active and
+type "127001<OK>" on your remote. If you want to enter "192.168.1.12", type
+"1921681<Right>12<OK>".
+
+The parameters "Remote IP" and "Remote Port" in the client's setup specify the
+address of the remote VDR-to-VDR server to connect to. Activate the client by
+setting "Start Client" to yes. It is disabled by default, because it wouldn't
+make much sense to start the client without specifying a server anyway. The
+client is activated after you push the OK button, so there's no need to restart
+VDR. Deactivation on-the-fly is not possible, so in order to deactivate the
+client, you will have to restart VDR. All other settings can be changed without
+restarting VDR.
+
+The client will try to connect to the server (in case it isn't yet) whenever
+a remote channel is requested. Just activate the client and switch to a
+channel that's not available by local devices. If anything goes wrong with the
+connection between the two, you will see it in the logfile instantly. If you
+now switch the client to a channel which isn't covered by it's own local
+devices, it will ask the server for it. If the server can (currently) receive
+that channel, the client will show it until you switch again, or until the
+server needs that card (if no other is free) for a recording on a different
+transponder.
+
+You can choose a remote streamtype in the setup. I'd suggest TS streaming as
+it has a much shorter delay than PES streaming (compared to live-view of the
+same channel on the server), and transmits more information such as AC3 and
+teletext data.
+
+When setting the parameter "MultiPID streaming" to yes (the default) (only
+applies if the streamtype is TS), only the needed PIDs are transferred, and
+additional PIDs can be turned on during an active transfer. This makes it
+possible to switch languages, receive additional channels (for recording on
+the client) and use plugins that use receivers themselves (like osdteletext).
+
+The last parameter, "Synchronize EPG", will have the client synchronize it's
+program table with the server every now and then, but not regularly. This
+happens when starting the client, and everytime VDR does its housekeeping
+tasks. The only thing that's guaranteed is, that there will be a minimum
+interval of ten seconds between each EPG synchronization.
+
+The client has a Main Menu entry called "Streaming Control". This is used to
+control various aspects of the remote server VDR. Inside, you will find
+"Remote Timers", "Remote Recordings", "Suspend server" and "Synchronize EPG".
+
+The "Remote Timers" entry gives you the possibility to edit, create and delete
+the server's timers remotely. Every timer is synchronized before the requested
+action actually takes place. This only leaves a very short time-span (of a few
+milliseconds) in which a race-condition could happen.
+
+"Remote Recordings" shows up all recordings that the server can access. Only
+deleting recordings is implemented, yet.
+
+With "Suspend Server", you can send the server into suspend mode remotely, if
+the server is set to "Offer suspend mode" and allows the client to suspend.
+
+Last but not least, "Synchronize EPG" starts a synchronization in case you
+don't want to do it regularly, or in case you just activated it and can't wait
+for the first synchronization to happen by itself.
+
+3.4 General Usage Notes:
+------------------------
+
+If there's still some debug output on stdout, please ignore it ;)
+
+
+4. VDR-to-VDR client notes:
+---------------------------
+
+4.1 EPG data:
+--------------
+
+[ OUTDATED, see "Synchronize EPG" in 3.2 ]
+
+4.2 Teletext / OSD Teletext:
+-----------------------------
+
+Usual teletext will probably not work on the client, if it has no DVB hardware.
+I never tried, and probably I never will, so don't ask about it please ;)
+
+Osdteletext-0.3.1 (and later) definitely work when used in MultiPID Streaming
+mode.
+
+
+4.3 AnalogTV
+------------
+
+Works with ivtv and analogue cards according to Andreas Kool.
+
+
+5. Known Problems:
+------------------
+
+- Recordings & Timers on the client side could endanger Timers & Recordings on
+ the server, as they will have the same priority (by default). Set the
+ default priority to i.e. 40 if you want the server to supersede the client.
+
+- Sometimes, if you reload VDR too often (for example while recompiling), the
+ driver can get "stuck" in some situations. Try a driver restart if anything
+ you think should work doesn't before sending a bug-report :-).
+ [ ADDITION ]
+ In the meantime I have discovered that this error is caused by the all-
+ mysterical UPT (unknown picture type) error :-(.
+
diff --git a/client/assembler.c b/client/assembler.c
new file mode 100644
index 0000000..9e36204
--- /dev/null
+++ b/client/assembler.c
@@ -0,0 +1,125 @@
+/*
+ * $Id: assembler.c,v 1.1 2004/12/30 22:44:04 lordjaxom Exp $
+ */
+
+#include "client/assembler.h"
+#include "common.h"
+
+#include "tools/socket.h"
+#include "tools/select.h"
+
+#include <vdr/tools.h>
+#include <vdr/device.h>
+#include <vdr/ringbuffer.h>
+
+#include <unistd.h>
+
+cStreamdevAssembler::cStreamdevAssembler(cTBSocket *Socket)
+#if VDRVERSNUM >= 10300
+ :cThread("Streamdev: UDP-TS Assembler")
+#endif
+{
+ m_Socket = Socket;
+ if (pipe(m_Pipe) != 0) {
+ esyslog("streamdev-client: Couldn't open assembler pipe: %m");
+ return;
+ }
+ fcntl(m_Pipe[0], F_SETFL, O_NONBLOCK);
+ fcntl(m_Pipe[1], F_SETFL, O_NONBLOCK);
+ m_Mutex.Lock();
+ Start();
+}
+
+cStreamdevAssembler::~cStreamdevAssembler() {
+ if (m_Active) {
+ m_Active = false;
+ WakeUp();
+ Cancel(3);
+ }
+ close(m_Pipe[0]);
+ close(m_Pipe[1]);
+}
+
+void cStreamdevAssembler::Action(void) {
+ cTBSelect sel;
+ uchar buffer[2048];
+ bool fillup = true;
+
+ const int rbsize = TS_SIZE * 5600;
+ const int rbmargin = TS_SIZE * 2;
+ const int rbminfill = rbmargin * 50;
+ cRingBufferLinear ringbuf(rbsize, rbmargin, true);
+
+#if VDRVERSNUM < 10300
+ isyslog("streamdev-client: UDP-TS Assembler thread started (pid=%d)",
+ getpid());
+#endif
+
+ m_Mutex.Lock();
+
+ m_Active = true;
+ while (m_Active) {
+ sel.Clear();
+
+ if (ringbuf.Available() < rbsize * 80 / 100)
+ sel.Add(*m_Socket, false);
+ if (ringbuf.Available() > rbminfill) {
+ if (fillup) {
+ Dprintf("giving signal\n");
+ m_WaitFill.Broadcast();
+ m_Mutex.Unlock();
+ fillup = false;
+ }
+ sel.Add(m_Pipe[1], true);
+ }
+
+ if (sel.Select(1500) < 0) {
+ if (!m_Active) // Exit was requested
+ break;
+ esyslog("streamdev-client: Fatal error: %m");
+ Dprintf("streamdev-client: select failed (%m)\n");
+ m_Active = false;
+ break;
+ }
+
+ if (sel.CanRead(*m_Socket)) {
+ int b;
+ if ((b = m_Socket->Read(buffer, sizeof(buffer))) < 0) {
+ esyslog("streamdev-client: Couldn't read from server: %m");
+ Dprintf("streamdev-client: read failed (%m)\n");
+ m_Active = false;
+ break;
+ }
+ if (b == 0)
+ m_Active = false;
+ else
+ ringbuf.Put(buffer, b);
+ }
+
+ if (sel.CanWrite(m_Pipe[1])) {
+ int recvd;
+ const uchar *block = ringbuf.Get(recvd);
+ if (block && recvd > 0) {
+ int result;
+ if (recvd > ringbuf.Available() - rbminfill)
+ recvd = ringbuf.Available() - rbminfill;
+ if ((result = write(m_Pipe[1], block, recvd)) == -1) {
+ esyslog("streamdev-client: Couldn't write to VDR: %m"); // TODO
+ Dprintf("streamdev-client: write failed (%m)\n");
+ m_Active = false;
+ break;
+ }
+ ringbuf.Del(result);
+ }
+ }
+ }
+
+#if VDRVERSNUM < 10300
+ isyslog("streamdev-client: UDP-TS Assembler thread stopped", getpid());
+#endif
+}
+
+void cStreamdevAssembler::WaitForFill(void) {
+ m_WaitFill.Wait(m_Mutex);
+ m_Mutex.Unlock();
+}
diff --git a/client/assembler.h b/client/assembler.h
new file mode 100644
index 0000000..a4b0747
--- /dev/null
+++ b/client/assembler.h
@@ -0,0 +1,32 @@
+/*
+ * $Id: assembler.h,v 1.1 2004/12/30 22:44:04 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_ASSEMBLER_H
+#define VDR_STREAMDEV_ASSEMBLER_H
+
+#include <vdr/config.h>
+#include <vdr/thread.h>
+
+class cTBSocket;
+
+class cStreamdevAssembler: public cThread {
+private:
+ cTBSocket *m_Socket;
+ cMutex m_Mutex;
+ cCondVar m_WaitFill;
+ int m_Pipe[2];
+ bool m_Active;
+protected:
+ virtual void Action(void);
+
+public:
+ cStreamdevAssembler(cTBSocket *Socket);
+ virtual ~cStreamdevAssembler();
+
+ int ReadPipe(void) const { return m_Pipe[0]; }
+ void WaitForFill(void);
+};
+
+#endif // VDR_STREAMDEV_ASSEMBLER_H
+
diff --git a/client/device.c b/client/device.c
new file mode 100644
index 0000000..50510e2
--- /dev/null
+++ b/client/device.c
@@ -0,0 +1,185 @@
+/*
+ * $Id: device.c,v 1.1 2004/12/30 22:44:00 lordjaxom Exp $
+ */
+
+#include "client/device.h"
+#include "client/setup.h"
+#include "client/assembler.h"
+#include "client/filter.h"
+
+#include "tools/select.h"
+#include "tools/string.h"
+
+#include <vdr/channels.h>
+#include <vdr/ringbuffer.h>
+#include <vdr/eit.h>
+#include <vdr/timers.h>
+
+#include <time.h>
+#include <iostream>
+
+using namespace std;
+
+#define VIDEOBUFSIZE MEGABYTE(3)
+
+cStreamdevDevice *cStreamdevDevice::m_Device = NULL;
+
+cStreamdevDevice::cStreamdevDevice(void) {
+ m_Channel = NULL;
+ m_TSBuffer = NULL;
+ m_Assembler = NULL;
+
+#if VDRVERSNUM < 10300
+# if defined(HAVE_AUTOPID)
+ (void)new cSIProcessor(new cSectionsScanner(""));
+# else
+ (void)new cSIProcessor("");
+# endif
+ cSIProcessor::Read();
+#else
+ m_Filters = new cStreamdevFilters;
+ StartSectionHandler();
+ cSchedules::Read();
+#endif
+
+ m_Device = this;
+
+ if (StreamdevClientSetup.SyncEPG)
+ ClientSocket.SynchronizeEPG();
+}
+
+cStreamdevDevice::~cStreamdevDevice() {
+ Dprintf("Device gets destructed\n");
+ m_Device = NULL;
+ delete m_TSBuffer;
+ delete m_Assembler;
+#if VDRVERSNUM >= 10300
+ delete m_Filters;
+#endif
+}
+
+bool cStreamdevDevice::ProvidesSource(int Source) const {
+ Dprintf("ProvidesSource, Source=%d\n", Source);
+ return false;
+}
+
+bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
+ bool *NeedsDetachReceivers) const {
+ bool res = false;
+ bool prio = Priority < 0 || Priority > this->Priority();
+ bool ndr = false;
+ Dprintf("ProvidesChannel, Channel=%s, Prio=%d\n", Channel->Name(), Priority);
+
+ if (ClientSocket.DataSocket(siLive) != NULL
+ && TRANSPONDER(Channel, m_Channel))
+ res = true;
+ else {
+ res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
+ ndr = true;
+ }
+
+ if (NeedsDetachReceivers)
+ *NeedsDetachReceivers = ndr;
+ Dprintf("prov res = %d, ndr = %d\n", res, ndr);
+ return res;
+}
+
+bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
+ bool LiveView) {
+ Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(),
+ LiveView ? "true" : "false");
+
+ if (LiveView)
+ return false;
+
+ if (ClientSocket.DataSocket(siLive) != NULL
+ && TRANSPONDER(Channel, m_Channel))
+ return true;
+
+ m_Channel = Channel;
+ return ClientSocket.SetChannelDevice(m_Channel);
+}
+
+bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) {
+ Dprintf("SetPid, Pid=%d, Type=%d, On=%d, used=%d\n", Handle->pid, Type, On,
+ Handle->used);
+ if (Handle->pid && (On || !Handle->used))
+ return ClientSocket.SetPid(Handle->pid, On);
+ return true;
+}
+
+bool cStreamdevDevice::OpenDvr(void) {
+ Dprintf("OpenDvr\n");
+ if (ClientSocket.CreateDataConnection(siLive)) {
+ m_Assembler = new cStreamdevAssembler(ClientSocket.DataSocket(siLive));
+ m_TSBuffer = new cTSBuffer(m_Assembler->ReadPipe(), MEGABYTE(2),
+ CardIndex() + 1);
+ Dprintf("waiting\n");
+ m_Assembler->WaitForFill();
+ Dprintf("resuming\n");
+ return true;
+ }
+ return false;
+}
+
+void cStreamdevDevice::CloseDvr(void) {
+ Dprintf("CloseDvr\n");
+
+ ClientSocket.CloseDvr();
+ DELETENULL(m_TSBuffer);
+ DELETENULL(m_Assembler);
+}
+
+bool cStreamdevDevice::GetTSPacket(uchar *&Data) {
+ if (m_TSBuffer) {
+ int r;
+ while ((r = m_TSBuffer->Read()) >= 0) {
+ Data = m_TSBuffer->Get();
+#if VDRVERSNUM >= 10300
+ if (Data != NULL) {
+ u_short pid = (((u_char)Data[1] & PID_MASK_HI) << 8) | Data[2];
+ u_char tid = Data[3];
+ if (m_Filters->Matches(pid, tid)) {
+ m_Filters->Put(Data);
+ continue;
+ }
+ }
+#endif
+ return true;
+ }
+ if (FATALERRNO) {
+ LOG_ERROR;
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+#if VDRVERSNUM >= 10300
+int cStreamdevDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
+ Dprintf("OpenFilter\n");
+ if (StreamdevClientSetup.StreamFilters
+ && ClientSocket.SetFilter(Pid, Tid, Mask, true)) {
+ return m_Filters->OpenFilter(Pid, Tid, Mask);
+ } else
+ return -1;
+}
+#endif
+
+bool cStreamdevDevice::Init(void) {
+ if (m_Device == NULL && StreamdevClientSetup.StartClient)
+ new cStreamdevDevice;
+ return true;
+}
+
+bool cStreamdevDevice::ReInit(void) {
+ ClientSocket.Quit();
+ ClientSocket.Reset();
+ if (m_Device != NULL) {
+ DELETENULL(m_Device->m_TSBuffer);
+ DELETENULL(m_Device->m_Assembler);
+ }
+ return StreamdevClientSetup.StartClient ? Init() : true;
+}
+
diff --git a/client/device.h b/client/device.h
new file mode 100644
index 0000000..b35dabe
--- /dev/null
+++ b/client/device.h
@@ -0,0 +1,58 @@
+/*
+ * $Id: device.h,v 1.1 2004/12/30 22:44:00 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_DEVICE_H
+#define VDR_STREAMDEV_DEVICE_H
+
+#include <vdr/device.h>
+
+#include "client/socket.h"
+#include "client/assembler.h"
+#include "client/filter.h"
+
+class cTBString;
+
+#define CMD_LOCK_OBJ(x) cMutexLock CmdLock((cMutex*)&(x)->m_Mutex)
+
+class cStreamdevDevice: public cDevice {
+ friend class cRemoteRecordings;
+
+private:
+ const cChannel *m_Channel;
+ cTSBuffer *m_TSBuffer;
+ cStreamdevAssembler *m_Assembler;
+#if VDRVERSNUM >= 10307
+ cStreamdevFilters *m_Filters;
+#endif
+
+ static cStreamdevDevice *m_Device;
+
+protected:
+ virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+ virtual bool HasLock(void) { return m_TSBuffer != NULL; }
+
+ virtual bool SetPid(cPidHandle *Handle, int Type, bool On);
+ virtual bool OpenDvr(void);
+ virtual void CloseDvr(void);
+ virtual bool GetTSPacket(uchar *&Data);
+
+#if VDRVERSNUM >= 10300
+ virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
+#endif
+
+public:
+ cStreamdevDevice(void);
+ virtual ~cStreamdevDevice();
+
+ virtual bool ProvidesSource(int Source) const;
+ virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1,
+ bool *NeedsDetachReceivers = NULL) const;
+
+ static bool Init(void);
+ static bool ReInit(void);
+
+ static cStreamdevDevice *GetDevice(void) { return m_Device; }
+};
+
+#endif // VDR_STREAMDEV_DEVICE_H
diff --git a/client/filter.c b/client/filter.c
new file mode 100644
index 0000000..dad86f3
--- /dev/null
+++ b/client/filter.c
@@ -0,0 +1,141 @@
+/*
+ * $Id: filter.c,v 1.1 2004/12/30 22:44:04 lordjaxom Exp $
+ */
+
+#include "client/filter.h"
+#include "client/socket.h"
+#include "tools/select.h"
+#include "common.h"
+
+#include <vdr/ringbuffer.h>
+
+#if VDRVERSNUM >= 10300
+
+cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
+ m_Used = 0;
+ m_Pid = Pid;
+ m_Tid = Tid;
+ m_Mask = Mask;
+
+ if (pipe(m_Pipe) != 0 || fcntl(m_Pipe[0], F_SETFL, O_NONBLOCK) != 0) {
+ esyslog("streamev-client: coudln't open section filter pipe: %m");
+ m_Pipe[0] = m_Pipe[1] = -1;
+ }
+}
+
+cStreamdevFilter::~cStreamdevFilter() {
+ Dprintf("~cStreamdevFilter %p\n", this);
+ if (m_Pipe[0] >= 0)
+ close(m_Pipe[0]);
+ if (m_Pipe[1] >= 0)
+ close(m_Pipe[1]);
+}
+
+bool cStreamdevFilter::PutSection(const uchar *Data, int Length) {
+ if (m_Used + Length >= (int)sizeof(m_Buffer)) {
+ esyslog("ERROR: Streamdev: Section handler buffer overflow (%d bytes lost)",
+ Length);
+ m_Used = 0;
+ return true;
+ }
+ memcpy(m_Buffer + m_Used, Data, Length);
+ m_Used += Length;
+
+ if (m_Used > 3) {
+ int length = (((m_Buffer[1] & 0x0F) << 8) | m_Buffer[2]) + 3;
+ if (m_Used == length) {
+ if (write(m_Pipe[1], m_Buffer, length) < 0)
+ return false;
+ m_Used = 0;
+ }
+ }
+ return true;
+}
+
+cStreamdevFilters::cStreamdevFilters(void):
+ cThread("streamdev-client: sections assembler") {
+ m_Active = false;
+ m_RingBuffer = new cRingBufferLinear(MEGABYTE(1), TS_SIZE * 2, true);
+ Start();
+}
+
+cStreamdevFilters::~cStreamdevFilters() {
+ if (m_Active) {
+ m_Active = false;
+ Cancel(3);
+ }
+ delete m_RingBuffer;
+}
+
+int cStreamdevFilters::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
+ cStreamdevFilter *f = new cStreamdevFilter(Pid, Tid, Mask);
+ Add(f);
+ return f->ReadPipe();
+}
+
+cStreamdevFilter *cStreamdevFilters::Matches(u_short Pid, u_char Tid) {
+ for (cStreamdevFilter *f = First(); f; f = Next(f)) {
+ if (f->Matches(Pid, Tid))
+ return f;
+ }
+ return NULL;
+}
+
+void cStreamdevFilters::Put(const uchar *Data) {
+ static time_t firsterr = 0;
+ static int errcnt = 0;
+ static bool showerr = true;
+
+ int p = m_RingBuffer->Put(Data, TS_SIZE);
+ if (p != TS_SIZE) {
+ ++errcnt;
+ if (showerr) {
+ if (firsterr == 0)
+ firsterr = time_ms();
+ else if (firsterr + BUFOVERTIME > time_ms() && errcnt > BUFOVERCOUNT) {
+ esyslog("ERROR: too many buffer overflows, logging stopped");
+ showerr = false;
+ firsterr = time_ms();
+ }
+ } else if (firsterr + BUFOVERTIME < time_ms()) {
+ showerr = true;
+ firsterr = 0;
+ errcnt = 0;
+ }
+
+ if (showerr)
+ esyslog("ERROR: ring buffer overflow (%d bytes dropped)", TS_SIZE - p);
+ else
+ firsterr = time_ms();
+ }
+}
+
+void cStreamdevFilters::Action(void) {
+ m_Active = true;
+ while (m_Active) {
+ int recvd;
+ const uchar *block = m_RingBuffer->Get(recvd);
+
+ if (block && recvd > 0) {
+ cStreamdevFilter *f;
+ u_short pid = (((u_short)block[1] & PID_MASK_HI) << 8) | block[2];
+ u_char tid = block[3];
+
+ if ((f = Matches(pid, tid)) != NULL) {
+ int len = block[4];
+ if (!f->PutSection(block + 5, len)) {
+ if (errno != EPIPE) {
+ esyslog("streamdev-client: couldn't send section packet: %m");
+ Dprintf("FATAL ERROR: %m\n");
+ }
+ ClientSocket.SetFilter(f->Pid(), f->Tid(), f->Mask(), false);
+ Del(f);
+ }
+ }
+ m_RingBuffer->Del(TS_SIZE);
+ } else
+ usleep(1);
+ }
+}
+
+#endif // VDRVERSNUM >= 10300
diff --git a/client/filter.h b/client/filter.h
new file mode 100644
index 0000000..679b2b5
--- /dev/null
+++ b/client/filter.h
@@ -0,0 +1,64 @@
+/*
+ * $Id: filter.h,v 1.1 2004/12/30 22:44:04 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_FILTER_H
+#define VDR_STREAMDEV_FILTER_H
+
+#include <vdr/config.h>
+
+# if VDRVERSNUM >= 10300
+
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+
+class cRingBufferFrame;
+class cRingBufferLinear;
+
+class cStreamdevFilter: public cListObject {
+private:
+ uchar m_Buffer[4096];
+ int m_Used;
+ int m_Pipe[2];
+ u_short m_Pid;
+ u_char m_Tid;
+ u_char m_Mask;
+ cRingBufferFrame *m_RingBuffer;
+
+public:
+ cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask);
+ virtual ~cStreamdevFilter();
+
+ bool Matches(u_short Pid, u_char Tid);
+ bool PutSection(const uchar *Data, int Length);
+ int ReadPipe(void) const { return m_Pipe[0]; }
+
+ u_short Pid(void) const { return m_Pid; }
+ u_char Tid(void) const { return m_Tid; }
+ u_char Mask(void) const { return m_Mask; }
+
+};
+
+inline bool cStreamdevFilter::Matches(u_short Pid, u_char Tid) {
+ return m_Pid == Pid && m_Tid == (Tid & m_Mask);
+}
+
+class cStreamdevFilters: public cList<cStreamdevFilter>, public cThread {
+private:
+ bool m_Active;
+ cRingBufferLinear *m_RingBuffer;
+
+protected:
+ virtual void Action(void);
+
+public:
+ cStreamdevFilters(void);
+ virtual ~cStreamdevFilters();
+
+ int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
+ cStreamdevFilter *Matches(u_short Pid, u_char Tid);
+ void Put(const uchar *Data);
+};
+
+# endif // VDRVERSNUM >= 10300
+#endif // VDR_STREAMDEV_FILTER_H
diff --git a/client/menu.c b/client/menu.c
new file mode 100644
index 0000000..4499268
--- /dev/null
+++ b/client/menu.c
@@ -0,0 +1,1047 @@
+/*
+ * $Id: menu.c,v 1.1 2004/12/30 22:44:01 lordjaxom Exp $
+ */
+
+#include <vdr/menuitems.h>
+#include <vdr/interface.h>
+
+#include "client/menu.h"
+#include "client/socket.h"
+#include "i18n.h"
+
+#define CHNUMWIDTH (numdigits(Channels.MaxNumber()) + 1)
+
+// --- cMenuText -------------------------------------------------------------
+
+class cMenuText : public cOsdMenu {
+public:
+ cMenuText(const char *Title, const char *Text, eDvbFont Font = fontOsd);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+// --- cStreamdevMenu --------------------------------------------------------
+
+cStreamdevMenu::cStreamdevMenu(void):
+ cOsdMenu(tr("Streaming Control")) {
+ SetHasHotkeys();
+ Add(new cOsdItem(hk(tr("Remote Schedule")), (eOSState)subSchedule));
+ Add(new cOsdItem(hk(tr("Remote Timers")), (eOSState)subTimers));
+ Add(new cOsdItem(hk(tr("Remote Recordings")), (eOSState)subRecordings));
+ Add(new cOsdItem(hk(tr("Suspend Server")), (eOSState)subSuspend));
+ Add(new cOsdItem(hk(tr("Synchronize EPG")), (eOSState)subSyncEPG));
+}
+
+cStreamdevMenu::~cStreamdevMenu() {
+}
+
+eOSState cStreamdevMenu::ProcessKey(eKeys Key) {
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ switch (state) {
+ case subSchedule: return AddSubMenu(new cStreamdevMenuSchedule);
+ case subTimers: return AddSubMenu(new cStreamdevMenuTimers);
+ case subRecordings: return AddSubMenu(new cStreamdevMenuRecordings);
+ case subSuspend: SuspendServer(); return osEnd;
+ case subSyncEPG: ClientSocket.SynchronizeEPG(); return osEnd;
+ default: return state;
+ }
+}
+
+void cStreamdevMenu::SuspendServer(void) {
+ cTBString buffer;
+
+ if (ClientSocket.SuspendServer())
+ INFO(tr("Server is suspended"));
+ else
+ ERROR(tr("Couldn't suspend Server!"));
+}
+
+#if VDRVERSNUM < 10307
+// --- cMenuEditChanItem -----------------------------------------------------
+
+class cMenuEditChanItem : public cMenuEditIntItem {
+protected:
+ virtual void Set(void);
+public:
+ cMenuEditChanItem(const char *Name, int *Value);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+// --- cMenuEditDateItem -----------------------------------------------------
+
+class cMenuEditDateItem : public cMenuEditItem {
+protected:
+ time_t *value;
+ virtual void Set(void);
+public:
+ cMenuEditDateItem(const char *Name, time_t *Value);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+// --- cMenuEditDayItem ------------------------------------------------------
+
+class cMenuEditDayItem : public cMenuEditIntItem {
+protected:
+ static int days[];
+ int d;
+ virtual void Set(void);
+public:
+ cMenuEditDayItem(const char *Name, int *Value);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+// --- cMenuEditTimeItem -----------------------------------------------------
+
+class cMenuEditTimeItem : public cMenuEditItem {
+protected:
+ int *value;
+ int hh, mm;
+ int pos;
+ virtual void Set(void);
+public:
+ cMenuEditTimeItem(const char *Name, int *Value);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+#endif // VDRVERSNUM < 10307
+
+// --- cStreamdevMenuEditTimer -----------------------------------------------
+
+class cStreamdevMenuEditTimer : public cOsdMenu {
+private:
+ int m_Channel;
+ bool m_AddIfConfirmed;
+ cRemoteTimer *m_Timer;
+ cRemoteTimer m_Data;
+ cMenuEditDateItem *m_FirstDay;
+
+protected:
+ void SetFirstDayItem(void);
+
+public:
+ cStreamdevMenuEditTimer(cRemoteTimer *Timer, bool New = false);
+ virtual ~cStreamdevMenuEditTimer();
+
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+cStreamdevMenuEditTimer::cStreamdevMenuEditTimer(cRemoteTimer *Timer, bool New):
+ cOsdMenu(tr("Edit remote timer"), 12) {
+ m_FirstDay = NULL;
+ m_Timer = Timer;
+ m_AddIfConfirmed = New;
+
+ if (m_Timer) {
+ m_Data = *m_Timer;
+ if (New)
+ m_Data.m_Active = 1;
+ m_Channel = m_Data.Channel()->Number();
+#if VDRVERSNUM < 10300
+ Add(new cMenuEditBoolItem(tr("Active"), &m_Data.m_Active));
+#else
+ Add(new cMenuEditBitItem( tr("Active"), &m_Data.m_Active, tfActive));
+#endif
+ Add(new cMenuEditChanItem(tr("Channel"), &m_Channel));
+ Add(new cMenuEditDayItem( tr("Day"), &m_Data.m_Day));
+ Add(new cMenuEditTimeItem(tr("Start"), &m_Data.m_Start));
+ Add(new cMenuEditTimeItem(tr("Stop"), &m_Data.m_Stop));
+#if VDRVERSNUM >= 10300
+ Add(new cMenuEditBitItem( tr("VPS"), &m_Data.m_Active, tfVps));
+#endif
+ Add(new cMenuEditIntItem( tr("Priority"), &m_Data.m_Priority, 0,
+ MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &m_Data.m_Lifetime, 0,
+ MAXLIFETIME));
+ Add(new cMenuEditStrItem( tr("File"), m_Data.m_File,
+ sizeof(m_Data.m_File), tr(FileNameChars)));
+ SetFirstDayItem();
+ }
+}
+
+cStreamdevMenuEditTimer::~cStreamdevMenuEditTimer() {
+ if (m_Timer && m_AddIfConfirmed) {
+ Dprintf("SOMETHING GETS DELETED\n");
+ delete m_Timer; // apparently it wasn't confirmed
+ }
+}
+
+void cStreamdevMenuEditTimer::SetFirstDayItem(void) {
+ if (!m_FirstDay && !m_Data.IsSingleEvent()) {
+ Add(m_FirstDay = new cMenuEditDateItem(tr("First day"),&m_Data.m_FirstDay));
+ Display();
+ } else if (m_FirstDay && m_Data.IsSingleEvent()) {
+ Del(m_FirstDay->Index());
+ m_FirstDay = NULL;
+ m_Data.m_FirstDay = 0;
+ Display();
+ }
+}
+
+eOSState cStreamdevMenuEditTimer::ProcessKey(eKeys Key) {
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk:
+ {
+ cChannel *ch = Channels.GetByNumber(m_Channel);
+ if (ch)
+ m_Data.m_Channel = ch;
+ else {
+ ERROR(tr("*** Invalid Channel ***"));
+ break;
+ }
+ if (!*m_Data.m_File)
+ strcpy(m_Data.m_File, m_Data.Channel()->Name());
+ if (m_Timer) {
+ bool success = true;
+ if (m_Data != *m_Timer) {
+ // Timer has changed
+ if ((success = ClientSocket.SaveTimer(m_Timer, m_Data))) {
+ *m_Timer = m_Data;
+ if (m_Timer->m_Active)
+ m_Timer->m_Active = 1;
+ // allows external programs to mark active timers with
+ // values > 1 and recognize if the user has modified them
+ }
+ }
+ if (success) {
+ if (m_AddIfConfirmed)
+ RemoteTimers.Add(m_Timer);
+ isyslog("timer %d %s (%s)", m_Timer->Index() + 1,
+ m_AddIfConfirmed ? "added" : "modified",
+ m_Timer->m_Active ? "active" : "inactive");
+ m_AddIfConfirmed = false;
+ }
+ }
+ }
+ return osBack;
+
+ case kRed:
+ case kGreen:
+ case kYellow:
+ case kBlue: return osContinue;
+ default: break;
+ }
+ }
+ if (Key != kNone)
+ SetFirstDayItem();
+ return state;
+}
+
+// --- cMenuWhatsOnItem ------------------------------------------------------
+
+#if VDRVERSNUM < 10300
+class cMenuWhatsOnItem : public cOsdItem {
+public:
+ const cEventInfo *eventInfo;
+# ifdef HAVE_BEAUTYPATCH
+ cMenuWhatsOnItem(const cEventInfo *EventInfo, bool ShowProgressBar);
+ ~cMenuWhatsOnItem();
+ virtual void Display(int Offset= -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
+protected:
+ cBitmap *progressBar;
+ bool showProgressBar;
+ float percent;
+private:
+ void DrawProgressBar(eDvbColor FgColor, eDvbColor BgColor);
+# else
+ cMenuWhatsOnItem(const cEventInfo *EventInfo);
+# endif
+};
+#else
+class cMenuWhatsOnItem : public cOsdItem {
+public:
+ const cEvent *event;
+ const cChannel *channel;
+ cMenuWhatsOnItem(const cEvent *Event, cChannel *Channel);
+};
+#endif
+
+// --- cMenuEvent ------------------------------------------------------------
+
+#if VDRVERSNUM < 10300
+class cMenuEvent : public cOsdMenu {
+private:
+ const cEventInfo *eventInfo;
+public:
+ cMenuEvent(const cEventInfo *EventInfo, bool CanSwitch = false);
+ cMenuEvent(bool Now);
+ virtual eOSState ProcessKey(eKeys Key);
+};
+#elif VDRVERSNUM < 10307
+class cMenuEvent : public cOsdMenu {
+private:
+ const cEvent *event;
+public:
+ cMenuEvent(const cEvent *Event, bool CanSwitch = false);
+ cMenuEvent(bool Now);
+ virtual eOSState ProcessKey(eKeys Key);
+};
+#else
+class cMenuEvent : public cOsdMenu {
+private:
+ const cEvent *event;
+public:
+ cMenuEvent(const cEvent *Event, bool CanSwitch = false);
+ virtual void Display(void);
+ virtual eOSState ProcessKey(eKeys Key);
+};
+#endif
+
+// --- cStreamdevMenuWhatsOn -------------------------------------------------
+
+int cStreamdevMenuWhatsOn::m_CurrentChannel = 0;
+#if VDRVERSNUM < 10300
+const cEventInfo *cStreamdevMenuWhatsOn::m_ScheduleEventInfo = NULL;
+#else
+const cEvent *cStreamdevMenuWhatsOn::m_ScheduleEventInfo = NULL;
+#endif
+
+#if VDRVERSNUM < 10300
+static int CompareEventChannel(const void *p1, const void *p2) {
+ return (int)((*(const cEventInfo**)p1)->GetChannelNumber()
+ - (*(const cEventInfo**)p2)->GetChannelNumber());
+}
+#endif
+
+cStreamdevMenuWhatsOn::cStreamdevMenuWhatsOn(const cSchedules *Schedules,
+ bool Now, int CurrentChannel):
+ cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH,
+ 7, 6) {
+#if VDRVERSNUM < 10300
+ const cSchedule *Schedule = Schedules->First();
+ const cEventInfo **pArray = NULL;
+ int num = 0;
+
+ while (Schedule) {
+ pArray=(const cEventInfo**)realloc(pArray, (num + 1) * sizeof(cEventInfo*));
+ pArray[num] = Now ? Schedule->GetPresentEvent()
+ : Schedule->GetFollowingEvent();
+ if (pArray[num]) {
+ cChannel *channel
+ = Channels.GetByChannelID(pArray[num]->GetChannelID(), true);
+ if (channel)
+ pArray[num++]->SetChannelNumber(channel->Number());
+ }
+ Schedule = Schedules->Next(Schedule);
+ }
+
+ qsort(pArray, num, sizeof(cEventInfo*), CompareEventChannel);
+ for (int a = 0; a < num; ++a) {
+ int channelnr = pArray[a]->GetChannelNumber();
+# ifdef HAVE_BEAUTYPATCH
+ Add(new cMenuWhatsOnItem(pArray[a],Now), channelnr == CurrentChannel);
+# else
+ Add(new cMenuWhatsOnItem(pArray[a]), channelnr == CurrentChannel);
+# endif
+ }
+
+ free(pArray);
+#else
+ for (cChannel *Channel = Channels.First(); Channel;
+ Channel = Channels.Next(Channel)) {
+ if (!Channel->GroupSep()) {
+ const cSchedule *Schedule
+ = Schedules->GetSchedule(Channel->GetChannelID());
+ if (Schedule) {
+ const cEvent *Event = Now ? Schedule->GetPresentEvent()
+ : Schedule->GetFollowingEvent();
+ if (Event)
+ Add(new cMenuWhatsOnItem(Event, Channel),
+ Channel->Number() == CurrentChannel);
+ }
+ }
+ }
+#endif
+ m_CurrentChannel = CurrentChannel;
+ SetHelp(Count() ? tr("Record") : NULL, Now ? tr("Next") : tr("Now"),
+ tr("Schedule"), tr("Switch"));
+}
+
+#if VDRVERSNUM < 10300
+const cEventInfo *cStreamdevMenuWhatsOn::ScheduleEventInfo(void) {
+ const cEventInfo *ei = m_ScheduleEventInfo;
+ m_ScheduleEventInfo = NULL;
+ return ei;
+}
+#else
+const cEvent *cStreamdevMenuWhatsOn::ScheduleEventInfo(void) {
+ const cEvent *ei = m_ScheduleEventInfo;
+ m_ScheduleEventInfo = NULL;
+ return ei;
+}
+#endif
+
+eOSState cStreamdevMenuWhatsOn::Switch(void) {
+ cMenuWhatsOnItem *item = (cMenuWhatsOnItem*)Get(Current());
+ if (item) {
+ cChannel *channel
+#if VDRVERSNUM < 10300
+ = Channels.GetByChannelID(item->eventInfo->GetChannelID(), true);
+#else
+ = Channels.GetByChannelID(item->event->ChannelID(), true);
+#endif
+ if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true))
+ return osEnd;
+ }
+ ERROR(tr("Can't switch channel!"));
+ return osContinue;
+}
+
+eOSState cStreamdevMenuWhatsOn::Record(void) {
+ cMenuWhatsOnItem *item = (cMenuWhatsOnItem*)Get(Current());
+ if (item) {
+ cRemoteTimer *timer
+#if VDRVERSNUM < 10300
+ = new cRemoteTimer(item->eventInfo);
+#else
+ = new cRemoteTimer(item->event);
+#endif
+ return AddSubMenu(new cStreamdevMenuEditTimer(timer));
+ // Load remote timers and see if timer exists before editing
+ }
+ return osContinue;
+}
+
+eOSState cStreamdevMenuWhatsOn::ProcessKey(eKeys Key) {
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ if (state == osUnknown) {
+ switch (Key) {
+ case kRecord:
+ case kRed:
+ return Record();
+
+ case kYellow:
+ state = osBack;
+ case kGreen:
+ {
+ cMenuWhatsOnItem *mi = (cMenuWhatsOnItem*)Get(Current());
+ if (mi) {
+#if VDRVERSNUM < 10300
+ m_ScheduleEventInfo = mi->eventInfo;
+ m_CurrentChannel = mi->eventInfo->GetChannelNumber();
+#else
+ m_ScheduleEventInfo = mi->event;
+ m_CurrentChannel = mi->channel->Number();
+#endif
+ }
+ }
+ break;
+
+ case kBlue:
+ return Switch();
+
+ case kOk:
+ if (Count())
+#if VDRVERSNUM < 10300
+ return AddSubMenu(new cMenuEvent(
+ ((cMenuWhatsOnItem*)Get(Current()))->eventInfo, true));
+#else
+ return AddSubMenu(new cMenuEvent(
+ ((cMenuWhatsOnItem*)Get(Current()))->event, true));
+#endif
+ break;
+
+ default:
+ break;
+ }
+ }
+ return state;
+}
+
+// --- cMenuScheduleItem -----------------------------------------------------
+
+#if VDRVERSNUM < 10300
+class cMenuScheduleItem : public cOsdItem {
+public:
+ const cEventInfo *eventInfo;
+ cMenuScheduleItem(const cEventInfo *EventInfo);
+};
+#else
+class cMenuScheduleItem : public cOsdItem {
+public:
+ const cEvent *event;
+ cMenuScheduleItem(const cEvent *Event);
+};
+#endif
+
+// --- cStreamdevMenuSchedule ------------------------------------------------
+
+cStreamdevMenuSchedule::cStreamdevMenuSchedule(void):
+#if VDRVERSNUM < 10300
+ cOsdMenu("", 6, 6)
+#else
+ cOsdMenu("", 7, 6, 4)
+#endif
+{
+ m_Now = false;
+ m_Next = false;
+ m_OtherChannel = -1;
+ m_Schedules = NULL;
+
+ cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
+ if (channel) {
+#if VDRVERSNUM < 10300
+ m_Schedules = cSIProcessor::Schedules(m_Lock);
+#else
+ m_Schedules = cSchedules::Schedules(m_Lock);
+#endif
+ PrepareSchedule(channel);
+ SetHelp(Count() ? tr("Record") : NULL, tr("Now"), tr("Next"));
+ }
+}
+
+cStreamdevMenuSchedule::~cStreamdevMenuSchedule() {
+}
+
+#if VDRVERSNUM < 10307
+static int CompareEventTime(const void *p1, const void *p2) {
+#if VDRVERSNUM < 10300
+ return (int)((*(cEventInfo **)p1)->GetTime()
+ - (*(cEventInfo **)p2)->GetTime());
+#else
+ return (int)((*(cEvent**)p1)->StartTime()
+ - (*(cEvent**)p2)->StartTime());
+#endif
+}
+#endif
+
+void cStreamdevMenuSchedule::PrepareSchedule(cChannel *Channel) {
+#if VDRVERSNUM < 10300
+ cTBString buffer;
+ Clear();
+ buffer.Format(tr("Schedule - %s"), Channel->Name());
+ SetTitle(buffer);
+ if (m_Schedules) {
+ const cSchedule *Schedule=m_Schedules->GetSchedule(Channel->GetChannelID());
+ if (Schedule) {
+ int num = Schedule->NumEvents();
+ const cEventInfo **pArray = MALLOC(const cEventInfo*, num);
+ if (pArray) {
+ time_t now = time(NULL);
+ int numreal = 0;
+ for (int a = 0; a < num; ++a) {
+ const cEventInfo *EventInfo = Schedule->GetEventNumber(a);
+ if (EventInfo->GetTime() + EventInfo->GetDuration() > now)
+ pArray[numreal++] = EventInfo;
+ }
+
+ qsort(pArray, numreal, sizeof(cEventInfo*), CompareEventTime);
+ for (int a = 0; a < numreal; ++a)
+ Add(new cMenuScheduleItem(pArray[a]));
+ free(pArray);
+ }
+ }
+ }
+#else
+ Clear();
+ char *buffer = NULL;
+ asprintf(&buffer, tr("Schedule - %s"), Channel->Name());
+ SetTitle(buffer);
+ free(buffer);
+ if (m_Schedules) {
+ const cSchedule *Schedule = m_Schedules->GetSchedule(Channel->GetChannelID());
+ if (Schedule) {
+ const cEvent *PresentEvent = Schedule->GetPresentEvent(Channel->Number() == cDevice::CurrentChannel());
+ time_t now = time(NULL) - Setup.EPGLinger * 60;
+ for (const cEvent *Event = Schedule->Events()->First(); Event; Event = Schedule->Events()->Next(Event)) {
+ if (Event->EndTime() > now || Event == PresentEvent)
+ Add(new cMenuScheduleItem(Event), Event == PresentEvent);
+ }
+ }
+ }
+#endif
+}
+
+eOSState cStreamdevMenuSchedule::Switch(void) {
+ if (m_OtherChannel) {
+ if (Channels.SwitchTo(m_OtherChannel))
+ return osEnd;
+ }
+ ERROR(tr("Can't switch channel!"));
+ return osContinue;
+}
+
+eOSState cStreamdevMenuSchedule::Record(void) {
+ cMenuScheduleItem *item = (cMenuScheduleItem*)Get(Current());
+ if (item) {
+ cRemoteTimer *timer
+#if VDRVERSNUM < 10300
+ = new cRemoteTimer(item->eventInfo);
+#else
+ = new cRemoteTimer(item->event);
+#endif
+ return AddSubMenu(new cStreamdevMenuEditTimer(timer));
+ // Load remote timers and see if timer exists before editing
+ }
+ return osContinue;
+}
+
+eOSState cStreamdevMenuSchedule::ProcessKey(eKeys Key) {
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ if (state == osUnknown) {
+ switch (Key) {
+ case kRecord:
+ case kRed:
+ return Record();
+
+ case kGreen:
+ if (m_Schedules) {
+ if (!m_Now && !m_Next) {
+ int channelnr = 0;
+ if (Count()) {
+ cChannel *channel
+#if VDRVERSNUM < 10300
+ = Channels.GetByChannelID(
+ ((cMenuScheduleItem*)Get(Current()))->eventInfo->GetChannelID(),
+ true);
+#else
+ = Channels.GetByChannelID(
+ ((cMenuScheduleItem*)Get(Current()))->event->ChannelID(), true);
+#endif
+ if (channel)
+ channelnr = channel->Number();
+ }
+ m_Now = true;
+ return AddSubMenu(new cStreamdevMenuWhatsOn(m_Schedules, true,
+ channelnr));
+ }
+ m_Now = !m_Now;
+ m_Next = !m_Next;
+ return AddSubMenu(new cStreamdevMenuWhatsOn(m_Schedules, m_Now,
+ cStreamdevMenuWhatsOn::CurrentChannel()));
+ }
+
+ case kYellow:
+ if (m_Schedules)
+ return AddSubMenu(new cStreamdevMenuWhatsOn(m_Schedules, false,
+ cStreamdevMenuWhatsOn::CurrentChannel()));
+ break;
+
+ case kBlue:
+ if (Count())
+ return Switch();
+ break;
+
+ case kOk:
+ if (Count())
+#if VDRVERSNUM < 10300
+ return AddSubMenu(new cMenuEvent(
+ ((cMenuScheduleItem*)Get(Current()))->eventInfo, m_OtherChannel));
+#else
+ return AddSubMenu(new cMenuEvent(
+ ((cMenuScheduleItem*)Get(Current()))->event, m_OtherChannel));
+#endif
+ break;
+
+ default:
+ break;
+ }
+ } else if (!HasSubMenu()) {
+ m_Now = false;
+ m_Next = false;
+#if VDRVERSNUM < 10300
+ const cEventInfo *ei
+#else
+ const cEvent *ei
+#endif
+ = cStreamdevMenuWhatsOn::ScheduleEventInfo();
+ if (ei) {
+ cChannel *channel
+#if VDRVERSNUM < 10300
+ = Channels.GetByChannelID(ei->GetChannelID(), true);
+#else
+ = Channels.GetByChannelID(ei->ChannelID(), true);
+#endif
+ if (channel) {
+ PrepareSchedule(channel);
+ if (channel->Number() != cDevice::CurrentChannel()) {
+ m_OtherChannel = channel->Number();
+ SetHelp(Count() ? tr("Record") : NULL, tr("Now"), tr("Next"),
+ tr("Switch"));
+ }
+ Display();
+ }
+ }
+ }
+ return state;
+}
+
+// --- cStreamdevMenuRecordingItem -------------------------------------------
+
+class cStreamdevMenuRecordingItem: public cOsdItem {
+private:
+ int m_Total;
+ int m_New;
+ char *m_FileName;
+ char *m_Name;
+
+public:
+ cStreamdevMenuRecordingItem(cRemoteRecording *Recording, int Level);
+ virtual ~cStreamdevMenuRecordingItem();
+
+ void IncrementCounter(bool New);
+ const char *Name(void) const { return m_Name; }
+ const char *FileName(void) const { return m_FileName; }
+ bool IsDirectory(void) const { return m_Name != NULL; }
+};
+
+cStreamdevMenuRecordingItem::cStreamdevMenuRecordingItem(
+ cRemoteRecording *Recording, int Level) {
+ m_FileName = strdup(Recording->Name());
+ m_Name = NULL;
+ m_Total = m_New = 0;
+ SetText(Recording->Title('\t', true, Level));
+ if (*Text() == '\t')
+ m_Name = strdup(Text() + 2);
+}
+
+cStreamdevMenuRecordingItem::~cStreamdevMenuRecordingItem() {
+}
+
+void cStreamdevMenuRecordingItem::IncrementCounter(bool New) {
+ ++m_Total;
+ if (New) ++m_New;
+ char *buffer = NULL;
+ asprintf(&buffer, "%d\t%d\t%s", m_Total, m_New, m_Name);
+ SetText(buffer, false);
+}
+
+// --- cStreamdevMenuRecordings ----------------------------------------------
+
+cRemoteRecordings cStreamdevMenuRecordings::Recordings;
+int cStreamdevMenuRecordings::HelpKeys = -1;
+
+cStreamdevMenuRecordings::cStreamdevMenuRecordings(const char *Base, int Level,
+ bool OpenSubMenus):
+ cOsdMenu(Base ? Base : tr("Remote Recordings"), 6, 6) {
+ m_Base = Base ? strdup(Base) : NULL;
+ m_Level = Setup.RecordingDirs ? Level : -1;
+
+ Display(); // this keeps the higher level menus from showing up briefly when
+ // pressing 'Back' during replay
+
+ if (!Base) {
+ STATUS(tr("Fetching recordings..."));
+ FLUSH();
+ }
+
+ if (Base || Recordings.Load()) {
+ cStreamdevMenuRecordingItem *LastItem = NULL;
+ char *LastItemText = NULL;
+ for (cRemoteRecording *r = Recordings.First(); r; r = Recordings.Next(r)) {
+ if (!Base || (strstr(r->Name(), Base) == r->Name()
+ && r->Name()[strlen(Base)] == '~')) {
+ cStreamdevMenuRecordingItem *Item = new cStreamdevMenuRecordingItem(r,
+ m_Level);
+ if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText)
+ != 0)) {
+ Add(Item);
+ LastItem = Item;
+ free(LastItemText);
+ LastItemText = strdup(LastItem->Text());
+ } else
+ delete Item;
+
+ if (LastItem) {
+ if (LastItem->IsDirectory())
+ LastItem->IncrementCounter(r->IsNew());
+ }
+ }
+ }
+ free(LastItemText);
+ if (Current() < 0)
+ SetCurrent(First());
+ else if (OpenSubMenus && Open(true))
+ return;
+ }
+
+#if VDRVERSNUM >= 10307
+ STATUS(NULL);
+ FLUSH();
+#endif
+
+ SetHelpKeys();
+}
+
+cStreamdevMenuRecordings::~cStreamdevMenuRecordings() {
+ if (m_Base != NULL) free(m_Base);
+ HelpKeys = -1;
+}
+
+void cStreamdevMenuRecordings::SetHelpKeys(void) {
+ cStreamdevMenuRecordingItem *ri =(cStreamdevMenuRecordingItem*)Get(Current());
+ int NewHelpKeys = HelpKeys;
+ if (ri) {
+ if (ri->IsDirectory())
+ NewHelpKeys = 1;
+ else {
+ NewHelpKeys = 2;
+ cRemoteRecording *recording = GetRecording(ri);
+ if (recording && recording->Summary())
+ NewHelpKeys = 3;
+ }
+ }
+ if (NewHelpKeys != HelpKeys) {
+ switch (NewHelpKeys) {
+ case 0: SetHelp(NULL); break;
+ case 1: SetHelp(tr("Open")); break;
+ case 2:
+ case 3: SetHelp(NULL, NULL, tr("Delete"), NewHelpKeys == 3 ? tr("Summary") : NULL);
+ //SetHelp(tr("Play"), tr("Rewind"), tr("Delete"), NewHelpKeys == 3 ? tr("Summary") : NULL); XXX
+ }
+ HelpKeys = NewHelpKeys;
+ }
+}
+
+cRemoteRecording *cStreamdevMenuRecordings::GetRecording(
+ cStreamdevMenuRecordingItem *Item) {
+ Dprintf("looking for %s\n", Item->FileName());
+ cRemoteRecording *recording = Recordings.GetByName(Item->FileName());
+ if (!recording)
+ ERROR(tr("Error while accessing recording!"));
+ return recording;
+}
+
+bool cStreamdevMenuRecordings::Open(bool OpenSubMenus) {
+ cStreamdevMenuRecordingItem *ri
+ = (cStreamdevMenuRecordingItem*)Get(Current());
+
+ if (ri && ri->IsDirectory()) {
+ const char *t = ri->Name();
+ char *buffer = NULL;
+ if (m_Base) {
+ asprintf(&buffer, "%s~%s", m_Base, t);
+ t = buffer;
+ }
+ AddSubMenu(new cStreamdevMenuRecordings(t, m_Level + 1, OpenSubMenus));
+ if (buffer != NULL) free(buffer);
+ return true;
+ }
+ return false;
+}
+
+eOSState cStreamdevMenuRecordings::Select(void) {
+ cStreamdevMenuRecordingItem *ri
+ = (cStreamdevMenuRecordingItem*)Get(Current());
+
+ if (ri) {
+ if (ri->IsDirectory())
+ Open();
+ /*else {
+ cControl::Launch(new cStreamdevPlayerControl(ri->FileName()));
+ return osEnd;
+ } XXX */
+ }
+ return osContinue;
+}
+
+eOSState cStreamdevMenuRecordings::Delete(void) {
+ if (HasSubMenu() || Count() == 0)
+ return osContinue;
+ cStreamdevMenuRecordingItem *ri
+ = (cStreamdevMenuRecordingItem*)Get(Current());
+ if (ri && !ri->IsDirectory()) {
+ if (Interface->Confirm(tr("Delete recording?"))) {
+ cRemoteRecording *recording = GetRecording(ri);
+ if (recording) {
+ if (ClientSocket.DeleteRecording(recording)) {
+ cOsdMenu::Del(Current());
+ Recordings.Del(recording);
+ Display();
+ if (!Count())
+ return osBack;
+ }
+ }
+ }
+ }
+ return osContinue;
+}
+
+eOSState cStreamdevMenuRecordings::Summary(void) {
+ if (HasSubMenu() || Count() == 0)
+ return osContinue;
+ cStreamdevMenuRecordingItem *ri=(cStreamdevMenuRecordingItem *)Get(Current());
+ if (ri && !ri->IsDirectory()) {
+ cRemoteRecording *recording = GetRecording(ri);
+ if (recording && recording->Summary() && *recording->Summary())
+ return AddSubMenu(new cMenuText(tr("Summary"), recording->Summary()));
+ }
+ return osContinue;
+}
+
+eOSState cStreamdevMenuRecordings::ProcessKey(eKeys Key) {
+ bool HadSubMenu = HasSubMenu();
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk:
+ case kRed: return Select();
+ case kYellow: return Delete();
+ case kBlue: return Summary();
+ default: break;
+ }
+ }
+
+ if (Key == kYellow && HadSubMenu && !HasSubMenu()) {
+ cOsdMenu::Del(Current());
+ if (!Count())
+ return osBack;
+ Display();
+ }
+
+ if (!HasSubMenu() && Key != kNone)
+ SetHelpKeys();
+ return state;
+}
+
+// --- cStreamdevMenuTimerItem -----------------------------------------------
+
+class cStreamdevMenuTimerItem: public cOsdItem {
+private:
+ cRemoteTimer *m_Timer;
+
+public:
+ cStreamdevMenuTimerItem(cRemoteTimer *Timer);
+ virtual ~cStreamdevMenuTimerItem();
+
+ virtual void Set(void);
+
+ cRemoteTimer *Timer(void) const { return m_Timer; }
+};
+
+cStreamdevMenuTimerItem::cStreamdevMenuTimerItem(cRemoteTimer *Timer) {
+ m_Timer = Timer;
+ Set();
+}
+
+cStreamdevMenuTimerItem::~cStreamdevMenuTimerItem() {
+}
+
+void cStreamdevMenuTimerItem::Set(void) {
+ char *buffer = NULL;
+ asprintf(&buffer, "%c\t%d\t%s\t%02d:%02d\t%02d:%02d\t%s",
+ !m_Timer->Active() ? ' ' :
+ m_Timer->FirstDay() ? '!' :
+ /*m_Timer->Recording() ? '#' :*/ '>',
+ m_Timer->Channel()->Number(),
+ m_Timer->PrintDay(m_Timer->Day()),
+ m_Timer->Start() / 100,
+ m_Timer->Start() % 100,
+ m_Timer->Stop() / 100,
+ m_Timer->Stop() % 100,
+ m_Timer->File());
+ SetText(buffer, false);
+}
+
+// --- cStreamdevMenuTimers --------------------------------------------------
+
+cStreamdevMenuTimers::cStreamdevMenuTimers(void):
+ cOsdMenu(tr("Remote Timers"), 2, CHNUMWIDTH, 10, 6, 6) {
+ Refresh();
+ SetHelp(tr("Edit"), tr("New"), tr("Delete"), tr("On/Off"));
+}
+
+cStreamdevMenuTimers::~cStreamdevMenuTimers() {
+}
+
+eOSState cStreamdevMenuTimers::ProcessKey(eKeys Key) {
+ int timerNum = HasSubMenu() ? Count() : -1;
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk: return Summary();
+ case kRed: return Edit();
+ case kGreen: return New();
+ case kYellow: return Delete();
+ case kBlue: OnOff(); break;
+ default: break;
+ }
+ }
+
+ if (timerNum >= 0 && !HasSubMenu()) {
+ Refresh();
+ Display();
+ }
+ return state;
+}
+
+eOSState cStreamdevMenuTimers::Edit(void) {
+ if (HasSubMenu() || Count() == 0)
+ return osContinue;
+ isyslog("Streamdev: Editing remote timer %d", CurrentTimer()->Index() + 1);
+ return AddSubMenu(new cStreamdevMenuEditTimer(CurrentTimer()));
+}
+
+eOSState cStreamdevMenuTimers::New(void) {
+ if (HasSubMenu())
+ return osContinue;
+ return AddSubMenu(new cStreamdevMenuEditTimer(new cRemoteTimer, true));
+}
+
+eOSState cStreamdevMenuTimers::Delete(void) {
+ cRemoteTimer *ti = CurrentTimer();
+ if (ti) {
+ if (Interface->Confirm(tr("Delete timer?"))) {
+ int idx = ti->Index();
+ if (ClientSocket.DeleteTimer(ti)) {
+ RemoteTimers.Del(ti);
+ cOsdMenu::Del(Current());
+ isyslog("Streamdev: Remote timer %d deleted", idx + 1);
+ }
+ Refresh();
+ Display();
+ }
+ }
+ return osContinue;
+}
+
+eOSState cStreamdevMenuTimers::OnOff(void) {
+ cRemoteTimer *timer = CurrentTimer();
+ if (timer) {
+ cRemoteTimer data = *timer;
+ data.OnOff();
+ if (data.FirstDay())
+ isyslog("Streamdev: Remote timer %d first day set to %s",
+ data.Index() + 1, data.PrintFirstDay());
+ else
+ isyslog("Streamdev: Remote timer %d %sactivated", data.Index() + 1,
+ data.Active() ? "" : "de");
+
+ if (ClientSocket.SaveTimer(timer, data)) {
+ *timer = data;
+ RefreshCurrent();
+ DisplayCurrent(true);
+ } else {
+ Refresh();
+ Display();
+ }
+ }
+ return osContinue;
+}
+
+eOSState cStreamdevMenuTimers::Summary(void) {
+ if (HasSubMenu() || Count() == 0)
+ return osContinue;
+
+ cRemoteTimer *ti = CurrentTimer();
+ if (ti && !ti->Summary().IsNull())
+ return AddSubMenu(new cMenuText(tr("Summary"), ti->Summary()));
+
+ return osContinue;
+}
+
+cRemoteTimer *cStreamdevMenuTimers::CurrentTimer(void) {
+ cStreamdevMenuTimerItem *item = (cStreamdevMenuTimerItem*)Get(Current());
+ return item ? item->Timer() : NULL;
+}
+
+void cStreamdevMenuTimers::Refresh(void) {
+ Clear();
+ if (RemoteTimers.Load()) {
+ for (cRemoteTimer *t = RemoteTimers.First(); t; t = RemoteTimers.Next(t)) {
+ Add(new cStreamdevMenuTimerItem(t));
+ }
+ }
+}
diff --git a/client/menu.h b/client/menu.h
new file mode 100644
index 0000000..17d91ae
--- /dev/null
+++ b/client/menu.h
@@ -0,0 +1,144 @@
+/*
+ * $Id: menu.h,v 1.1 2004/12/30 22:44:02 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_MENU_H
+#define VDR_STREAMDEV_MENU_H
+
+#include <vdr/osd.h>
+
+#include "client/remote.h"
+
+class cStreamdevMenuRecordingItem;
+
+// --- cStreamdevMenu --------------------------------------------------------
+
+class cStreamdevMenu: public cOsdMenu {
+private:
+ enum eSubmenus {
+ sub_Start = os_User,
+ subSchedule,
+ subTimers,
+ subRecordings,
+ subSuspend,
+ subSyncEPG
+ };
+
+protected:
+ void SuspendServer(void);
+
+public:
+ cStreamdevMenu(void);
+ virtual ~cStreamdevMenu(void);
+
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+// --- cStreamdevMenuSchedule ------------------------------------------------
+
+class cStreamdevMenuSchedule: public cOsdMenu {
+private:
+ bool m_Now;
+ bool m_Next;
+ int m_OtherChannel;
+ const cSchedules *m_Schedules;
+#if VDRVERSNUM < 10300
+ cMutexLock m_Lock;
+#else
+ cSchedulesLock m_Lock;
+#endif
+
+protected:
+ void PrepareSchedule(cChannel *Channel);
+
+ eOSState Switch(void);
+ eOSState Record(void);
+
+public:
+ cStreamdevMenuSchedule(void);
+ virtual ~cStreamdevMenuSchedule(void);
+
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+// --- cStreamdevMenuWhatsOn -------------------------------------------------
+
+class cStreamdevMenuWhatsOn: public cOsdMenu {
+private:
+ static int m_CurrentChannel;
+#if VDRVERSNUM < 10300
+ static const cEventInfo *m_ScheduleEventInfo;
+#else
+ static const cEvent *m_ScheduleEventInfo;
+#endif
+
+protected:
+ eOSState Switch(void);
+ eOSState Record(void);
+
+public:
+ cStreamdevMenuWhatsOn(const cSchedules *Schedules, bool Now,
+ int CurrentChannel);
+
+ static int CurrentChannel(void) { return m_CurrentChannel; }
+ static void SetCurrentChannel(int Channel) { m_CurrentChannel = Channel; }
+#if VDRVERSNUM < 10300
+ static const cEventInfo *ScheduleEventInfo(void);
+#else
+ static const cEvent *ScheduleEventInfo(void);
+#endif
+
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+// --- cStreamdevMenuRecordings ----------------------------------------------
+
+class cStreamdevMenuRecordings: public cOsdMenu {
+private:
+ char *m_Base;
+ int m_Level;
+
+ static int HelpKeys;
+ static cRemoteRecordings Recordings;
+
+protected:
+ bool Open(bool OpenSubMenus = false);
+ void SetHelpKeys();
+ cRemoteRecording *cStreamdevMenuRecordings::GetRecording(
+ cStreamdevMenuRecordingItem *Item);
+
+ eOSState Select(void);
+ eOSState Delete(void);
+ eOSState Summary(void);
+
+public:
+ cStreamdevMenuRecordings(const char *Base = NULL, int Level = 0,
+ bool OpenSubMenus = false);
+ virtual ~cStreamdevMenuRecordings();
+
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+// --- cStreamdevMenuTimers --------------------------------------------------
+
+class cStreamdevMenuTimers: public cOsdMenu {
+protected:
+ eOSState Edit(void);
+ eOSState New(void);
+ eOSState Delete(void);
+ eOSState OnOff(void);
+ eOSState Summary(void);
+
+ cRemoteTimer *CurrentTimer(void);
+
+ void Refresh(void);
+
+public:
+ cStreamdevMenuTimers(void);
+ virtual ~cStreamdevMenuTimers();
+
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+#endif // VDR_STREAMDEV_MENU_H
+
diff --git a/client/remote.c b/client/remote.c
new file mode 100644
index 0000000..4cc7cfe
--- /dev/null
+++ b/client/remote.c
@@ -0,0 +1,475 @@
+/*
+ * $Id: remote.c,v 1.1 2004/12/30 22:44:02 lordjaxom Exp $
+ */
+
+#include "client/remote.h"
+#include "client/device.h"
+#include "common.h"
+
+cRemoteTimers RemoteTimers;
+
+// --- cRemoteRecording ------------------------------------------------------
+
+cRemoteRecording::cRemoteRecording(const char *Text) {
+ m_IsValid = false;
+ m_Index = -1;
+ m_IsNew = false;
+ m_TitleBuffer = NULL;
+
+ char *ptr;
+ char *timestr;
+ int idx;
+
+ Dprintf("text: %s\n", Text);
+
+ m_Index = strtoul(Text, &ptr, 10);
+ Dprintf("index: %d\n", m_Index);
+ if (*ptr == '\0' || *++ptr == '\0' ) return;
+ timestr = ptr;
+ while (*ptr != '\0' && !isspace(*ptr)) ++ptr;
+ if (*ptr == '\0' || *++ptr == '\0') return;
+ while (*ptr != '\0' && *ptr != '*' && !isspace(*ptr)) ++ptr;
+ if (*ptr == '*') m_IsNew = true;
+ Dprintf("new: %d\n", m_IsNew);
+ *(ptr++) = '\0';
+ m_StartTime = timestr;
+ idx = -1;
+ while ((idx = m_StartTime.Find(' ', idx + 1)) != -1) m_StartTime[idx] = '\t';
+ Dprintf("m_Start: %s\n", (const char*)m_StartTime);
+ if (*ptr == 0) return;
+ if (isspace(*ptr)) ++ptr;
+ if (*ptr == 0) return;
+ m_Name = ptr;
+ Dprintf("file: %s\n", (const char*)m_Name);
+ m_IsValid = true;
+}
+
+cRemoteRecording::~cRemoteRecording(void) {
+}
+
+bool cRemoteRecording::operator==(const cRemoteRecording &Recording) {
+ return m_IsValid == Recording.m_IsValid
+ && m_Index == Recording.m_Index
+ && m_StartTime == Recording.m_StartTime
+ && m_Name == Recording.m_Name;
+}
+
+void cRemoteRecording::ParseInfo(const char *Text) {
+ m_Summary = strreplace(strdup(Text), '|', '\n');
+}
+
+const char *cRemoteRecording::Title(char Delimiter, bool NewIndicator,
+ int Level) {
+ char New = NewIndicator && IsNew() ? '*' : ' ';
+
+ if (m_TitleBuffer != NULL) {
+ free(m_TitleBuffer);
+ m_TitleBuffer = NULL;
+ }
+
+ if (Level < 0 || Level == HierarchyLevels()) {
+ char *s;
+ const char *t;
+ if (Level > 0 && (t = strrchr(m_Name, '~')) != NULL)
+ t++;
+ else
+ t = (const char*)m_Name;
+
+ asprintf(&m_TitleBuffer, "%s%c%c%s", (const char*)m_StartTime, New,
+ Delimiter, t);
+ // let's not display a trailing '~':
+ stripspace(m_TitleBuffer);
+ s = &m_TitleBuffer[strlen(m_TitleBuffer) - 1];
+ if (*s == '~')
+ *s = 0;
+ } else if (Level < HierarchyLevels()) {
+ const char *s = m_Name;
+ const char *p = s;
+ while (*++s) {
+ if (*s == '~') {
+ if (Level--)
+ p = s + 1;
+ else
+ break;
+ }
+ }
+ m_TitleBuffer = MALLOC(char, s - p + 3);
+ *m_TitleBuffer = Delimiter;
+ *(m_TitleBuffer + 1) = Delimiter;
+ strn0cpy(m_TitleBuffer + 2, p, s - p + 1);
+ } else
+ return "";
+ return m_TitleBuffer;
+}
+
+int cRemoteRecording::HierarchyLevels(void)
+{
+ const char *s = m_Name;
+ int level = 0;
+ while (*++s) {
+ if (*s == '~') ++level;
+ }
+ return level;
+}
+
+// --- cRemoteRecordings -----------------------------------------------------
+
+bool cRemoteRecordings::Load(void) {
+ Clear();
+ return ClientSocket.LoadRecordings(*this);
+}
+
+cRemoteRecording *cRemoteRecordings::GetByName(const char *Name) {
+ for (cRemoteRecording *r = First(); r; r = Next(r))
+ if (strcmp(r->Name(), Name) == 0)
+ return r;
+ return NULL;
+}
+
+// --- cRemoteTimer ----------------------------------------------------------
+
+cRemoteTimer::cRemoteTimer(const char *Text) {
+ m_IsValid = false;
+ m_Index = -1;
+ m_Active = -1;
+ m_Day = -1;
+ m_Start = -1;
+ m_Stop = -1;
+ m_StartTime = 0;
+ m_StopTime = 0;
+ m_Priority = -1;
+ m_Lifetime = -1;
+ m_File[0] = '\0';
+ m_FirstDay = 0;
+ m_Buffer = NULL;
+ m_Channel = NULL;
+
+ char *tmpbuf;
+ char *ptr;
+
+ Dprintf("text: %s\n", Text);
+
+ m_Index = strtoul(Text, &ptr, 10);
+ Dprintf("index: %d\n", m_Index);
+ if (*ptr == '\0' || *++ptr == '\0') return;
+ m_Active = strtoul(ptr, &ptr, 10);
+ Dprintf("m_Active: %d\n", m_Active);
+ if (*ptr == '\0' || *++ptr == '\0') return;
+
+ tmpbuf = ptr;
+ while (*ptr != '\0' && *ptr != ':') ++ptr;
+ if (*ptr == '\0') return;
+ *(ptr++)= '\0';
+ if (isnumber(tmpbuf))
+ m_Channel = Channels.GetByNumber(strtoul(tmpbuf, NULL, 10));
+ else
+ m_Channel = Channels.GetByChannelID(tChannelID::FromString(tmpbuf));
+ Dprintf("channel no.: %d\n", m_Channel->Number());
+
+ tmpbuf = ptr;
+ while (*ptr != '\0' && *ptr != ':') ++ptr;
+ if (*ptr == '\0') return;
+ *(ptr++) = '\0';
+ m_Day = ParseDay(tmpbuf, &m_FirstDay);
+ Dprintf("Day: %d\n", m_Day);
+ m_Start = strtoul(ptr, &ptr, 10);
+ Dprintf("Start: %d\n", m_Start);
+ if (*ptr == '\0' || *++ptr == '\0') return;
+ m_Stop = strtoul(ptr, &ptr, 10);
+ Dprintf("Stop: %d\n", m_Stop);
+ if (*ptr == '\0' || *++ptr == '\0') return;
+ m_Priority = strtoul(ptr, &ptr, 10);
+ Dprintf("Prio: %d\n", m_Priority);
+ if (*ptr == '\0' || *++ptr == '\0') return;
+ m_Lifetime = strtoul(ptr, &ptr, 10);
+ Dprintf("Lifetime: %d\n", m_Lifetime);
+ if (*ptr == '\0' || *++ptr == '\0') return;
+ tmpbuf = ptr;
+ while (*ptr != '\0' && *ptr != ':') ++ptr;
+ if (*ptr == '\0') return;
+ *(ptr++) = '\0';
+ strncpy(m_File, tmpbuf, MaxFileName);
+ Dprintf("file: %s\n", m_File);
+ if (*ptr != '\0') m_Summary = ptr;
+ Dprintf("summary: %s\n", (const char*)m_Summary);
+ m_IsValid = true;
+}
+
+#if VDRVERSNUM < 10300
+cRemoteTimer::cRemoteTimer(const cEventInfo *EventInfo) {
+ time_t tstart = EventInfo->GetTime();
+ time_t tstop = tstart + EventInfo->GetDuration() + Setup.MarginStop * 60;
+ tstart -= Setup.MarginStart * 60;
+ struct tm tm_r;
+ struct tm *time = localtime_r(&tstart, &tm_r);
+ const char *title = EventInfo->GetTitle();
+ cChannel *channel = Channels.GetByChannelID(EventInfo->GetChannelID(), true);
+#else
+cRemoteTimer::cRemoteTimer(const cEvent *Event) {
+ time_t tstart = Event->StartTime();
+ time_t tstop = tstart + Event->Duration() + Setup.MarginStop * 60;
+ tstart -= Setup.MarginStart * 60;
+ struct tm tm_r;
+ struct tm *time = localtime_r(&tstart, &tm_r);
+ const char *title = Event->Title();
+ cChannel *channel = Channels.GetByChannelID(Event->ChannelID(), true);
+#endif
+
+ m_IsValid = true;
+ m_Index = -1;
+ m_Active = true;
+ m_Day = time->tm_mday;
+ m_Start = time->tm_hour * 100 + time->tm_min;
+ time = localtime_r(&tstop, &tm_r);
+ m_Stop = time->tm_hour * 100 + time->tm_min;
+ m_StartTime = 0;
+ m_StopTime = 0;
+ if (m_Stop >= 2400) m_Stop -= 2400;
+ m_Priority = Setup.DefaultPriority;
+ m_Lifetime = Setup.DefaultLifetime;
+ m_File[0] = '\0';
+ if (!isempty(title))
+ strn0cpy(m_File, title, sizeof(m_File));
+ m_FirstDay = 0;
+ m_Channel = channel;
+}
+
+cRemoteTimer::cRemoteTimer(void) {
+ time_t t = time(NULL);
+ struct tm tm_r;
+ struct tm *now = localtime_r(&t, &tm_r);
+
+ m_IsValid = true;
+ m_Index = -1;
+ m_Active = -1;
+ m_Day = now->tm_mday;
+ m_Start = now->tm_hour * 100 + now->tm_min;
+ m_Stop = now->tm_hour * 60 + now->tm_min + Setup.InstantRecordTime;
+ m_Stop = (m_Stop / 60) * 100 + (m_Stop % 60);
+ if (m_Stop >= 2400) m_Stop -= 2400;
+ m_StartTime = 0;
+ m_StopTime = 0;
+ m_Priority = Setup.DefaultPriority;
+ m_Lifetime = Setup.DefaultLifetime;
+ m_File[0] = '\0';
+ m_FirstDay = 0;
+ m_Buffer = NULL;
+ m_Channel = Channels.GetByNumber(cDevice::CurrentChannel());
+}
+
+cRemoteTimer::~cRemoteTimer() {
+ if (m_Buffer != NULL) free(m_Buffer);
+}
+
+cRemoteTimer &cRemoteTimer::operator=(const cRemoteTimer &Timer) {
+ Dprintf("\n\n\n\nOPÜERATHVBDÖLJVG\n\n\n");
+ m_IsValid = Timer.m_IsValid;
+ m_Index = Timer.m_Index;
+ m_Active = Timer.m_Active;
+ m_Day = Timer.m_Day;
+ m_Start = Timer.m_Start;
+ m_Stop = Timer.m_Stop;
+ m_Priority = Timer.m_Priority;
+ m_Lifetime = Timer.m_Lifetime;
+ m_FirstDay = Timer.m_FirstDay;
+ m_Channel = Timer.m_Channel;
+ m_Summary = Timer.m_Summary;
+ return *this;
+}
+
+bool cRemoteTimer::operator==(const cRemoteTimer &Timer) {
+ return m_IsValid == Timer.m_IsValid
+ && m_Index == Timer.m_Index
+ && m_Active == Timer.m_Active
+ && m_Day == Timer.m_Day
+ && m_Start == Timer.m_Start
+ && m_Stop == Timer.m_Stop
+ && m_Priority == Timer.m_Priority
+ && m_Lifetime == Timer.m_Lifetime
+ && m_FirstDay == Timer.m_FirstDay
+ && m_Channel == Timer.m_Channel
+ && strcmp(m_File, Timer.m_File) == 0
+ && m_Summary == Timer.m_Summary;
+}
+
+int cRemoteTimer::ParseDay(const char *s, time_t *FirstDay) {
+ char *tail;
+ int d = strtol(s, &tail, 10);
+ if (FirstDay)
+ *FirstDay = 0;
+ if (tail && *tail) {
+ d = 0;
+ if (tail == s) {
+ const char *first = strchr(s, '@');
+ int l = first ? first - s : strlen(s);
+ if (l == 7) {
+ for (const char *p = s + 6; p >= s; p--) {
+ d <<= 1;
+ d |= (*p != '-');
+ }
+ d |= 0x80000000;
+ }
+ if (FirstDay && first) {
+ ++first;
+ if (strlen(first) == 10) {
+ struct tm tm_r;
+ if (3 == sscanf(first, "%d-%d-%d", &tm_r.tm_year, &tm_r.tm_mon, &tm_r.tm_mday)) {
+ tm_r.tm_year -= 1900;
+ tm_r.tm_mon--;
+ tm_r.tm_hour = tm_r.tm_min = tm_r.tm_sec = 0;
+ tm_r.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
+ *FirstDay = mktime(&tm_r);
+ }
+ }
+ else
+ d = 0;
+ }
+ }
+ }
+ else if (d < 1 || d > 31)
+ d = 0;
+ return d;
+}
+
+const char *cRemoteTimer::PrintDay(int d, time_t FirstDay) {
+#define DAYBUFFERSIZE 32
+ static char buffer[DAYBUFFERSIZE];
+ if ((d & 0x80000000) != 0) {
+ char *b = buffer;
+ const char *w = tr("MTWTFSS");
+ while (*w) {
+ *b++ = (d & 1) ? *w : '-';
+ d >>= 1;
+ w++;
+ }
+ if (FirstDay) {
+ struct tm tm_r;
+ localtime_r(&FirstDay, &tm_r);
+ b += strftime(b, DAYBUFFERSIZE - (b - buffer), "@%Y-%m-%d", &tm_r);
+ }
+ *b = 0;
+ }
+ else
+ sprintf(buffer, "%d", d);
+ return buffer;
+}
+
+const char *cRemoteTimer::PrintFirstDay(void) const {
+ if (m_FirstDay) {
+ const char *s = PrintDay(m_Day, m_FirstDay);
+ if (strlen(s) == 18)
+ return s + 8;
+ }
+ return ""; // not NULL, so the caller can always use the result
+}
+
+void cRemoteTimer::OnOff(void) {
+ if (IsSingleEvent())
+ m_Active = !m_Active;
+ else if (m_FirstDay) {
+ m_FirstDay = 0;
+ m_Active = false;
+ }
+ else if (m_Active)
+ Skip();
+ else
+ m_Active = true;
+ Matches(); // refresh m_Start and end time
+}
+
+time_t cRemoteTimer::SetTime(time_t t, int SecondsFromMidnight) {
+ struct tm tm_r;
+ tm tm = *localtime_r(&t, &tm_r);
+ tm.tm_hour = SecondsFromMidnight / 3600;
+ tm.tm_min = (SecondsFromMidnight % 3600) / 60;
+ tm.tm_sec = SecondsFromMidnight % 60;
+ tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
+ return mktime(&tm);
+}
+
+bool cRemoteTimer::Matches(time_t t) {
+ m_StartTime = m_StopTime = 0;
+ if (t == 0)
+ t = time(NULL);
+
+ int begin = TimeToInt(m_Start); // seconds from midnight
+ int length = TimeToInt(m_Stop) - begin;
+ if (length < 0)
+ length += SECSINDAY;
+
+ int DaysToCheck = IsSingleEvent() ? 61 : 7; // 61 to handle months with 31/30/31
+ for (int i = -1; i <= DaysToCheck; i++) {
+ time_t t0 = IncDay(t, i);
+ if (DayMatches(t0)) {
+ time_t a = SetTime(t0, begin);
+ time_t b = a + length;
+ if ((!m_FirstDay || a >= m_FirstDay) && t <= b) {
+ m_StartTime = a;
+ m_StopTime = b;
+ break;
+ }
+ }
+ }
+ if (!m_StartTime)
+ m_StartTime = m_FirstDay; // just to have something that's more than a week in the future
+ else if (t > m_StartTime || t > m_FirstDay + SECSINDAY + 3600) // +3600 in case of DST change
+ m_FirstDay = 0;
+ return m_Active && m_StartTime <= t && t < m_StopTime; // must m_Stop *before* m_StopTime to allow adjacent timers
+}
+
+bool cRemoteTimer::DayMatches(time_t t) {
+ return IsSingleEvent()
+ ? GetMDay(t) == m_Day
+ : (m_Day & (1 << GetWDay(t))) != 0;
+}
+
+int cRemoteTimer::GetMDay(time_t t)
+{
+ struct tm tm_r;
+ return localtime_r(&t, &tm_r)->tm_mday;
+}
+
+int cRemoteTimer::GetWDay(time_t t)
+{
+ struct tm tm_r;
+ int weekday = localtime_r(&t, &tm_r)->tm_wday;
+ return weekday == 0 ? 6 : weekday - 1; // we start with monday==0!
+}
+
+time_t cRemoteTimer::IncDay(time_t t, int Days) {
+ struct tm tm_r;
+ tm tm = *localtime_r(&t, &tm_r);
+ tm.tm_mday += Days; // now tm_mday may be out of its valid range
+ int h = tm.tm_hour; // save original hour to compensate for DST change
+ tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
+ t = mktime(&tm); // normalize all values
+ tm.tm_hour = h; // compensate for DST change
+ return mktime(&tm); // calculate final result
+}
+
+const char *cRemoteTimer::ToText(void) {
+ char *summary = NULL;
+
+ if (m_Buffer != NULL) free(m_Buffer);
+
+ strreplace(m_File, ':', '|');
+ if (!m_Summary.IsNull())
+ summary = strreplace(strdup(m_Summary), ':', '|');
+
+ asprintf(&m_Buffer, "%d:%s:%s:%04d:%04d:%d:%d:%s:%s", m_Active,
+ Channel()->GetChannelID().ToString(), PrintDay(m_Day, m_FirstDay),
+ m_Start, m_Stop, m_Priority, m_Lifetime, m_File, summary ? summary : "");
+
+ if (summary != NULL)
+ free(summary);
+ strreplace(m_File, '|', ':');
+ return m_Buffer;
+}
+
+// --- cRemoteTimers ---------------------------------------------------------
+
+bool cRemoteTimers::Load(void) {
+ Clear();
+ return ClientSocket.LoadTimers(*this);
+}
+
diff --git a/client/remote.h b/client/remote.h
new file mode 100644
index 0000000..24a7067
--- /dev/null
+++ b/client/remote.h
@@ -0,0 +1,132 @@
+/*
+ * $Id: remote.h,v 1.1 2004/12/30 22:44:03 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_REMOTE_H
+#define VDR_STREAMDEV_REMOTE_H
+
+#include <vdr/config.h>
+
+#include "tools/string.h"
+
+#if VDRVERSNUM < 10300
+class cEventInfo;
+#else
+class cEvent;
+#endif
+class cChannel;
+
+class cRemoteRecording: public cListObject {
+private:
+ bool m_IsValid;
+ int m_Index;
+ bool m_IsNew;
+ char *m_TitleBuffer;
+ cTBString m_StartTime;
+ cTBString m_Name;
+ cTBString m_Summary;
+
+public:
+ cRemoteRecording(const char *Text);
+ ~cRemoteRecording();
+
+ bool operator==(const cRemoteRecording &Recording);
+ bool operator!=(const cRemoteRecording &Recording);
+
+ void ParseInfo(const char *Text);
+
+ bool IsValid(void) const { return m_IsValid; }
+ int Index(void) const { return m_Index; }
+ const char *StartTime(void) const { return m_StartTime; }
+ bool IsNew(void) const { return m_IsNew; }
+ const char *Name(void) const { return m_Name; }
+ const char *Summary(void) const { return m_Summary; }
+ const char *Title(char Delimiter, bool NewIndicator, int Level);
+ int HierarchyLevels(void);
+};
+
+inline bool cRemoteRecording::operator!=(const cRemoteRecording &Recording) {
+ return !operator==(Recording);
+}
+
+class cRemoteRecordings: public cList<cRemoteRecording> {
+public:
+ bool Load(void);
+ cRemoteRecording *GetByName(const char *Name);
+};
+
+class cRemoteTimer: public cListObject {
+ friend class cStreamdevMenuEditTimer;
+
+private:
+ bool m_IsValid;
+ int m_Index;
+ int m_Active;
+ int m_Day;
+ int m_Start;
+ int m_Stop;
+ time_t m_StartTime;
+ time_t m_StopTime;
+ int m_Priority;
+ int m_Lifetime;
+ char m_File[MaxFileName];
+ time_t m_FirstDay;
+ cTBString m_Summary;
+ char *m_Buffer;
+ const cChannel *m_Channel;
+
+public:
+ cRemoteTimer(const char *Text);
+#if VDRVERSNUM < 10300
+ cRemoteTimer(const cEventInfo *EventInfo);
+#else
+ cRemoteTimer(const cEvent *Event);
+#endif
+ cRemoteTimer(void);
+ ~cRemoteTimer();
+
+ cRemoteTimer &operator=(const cRemoteTimer &Timer);
+ bool operator==(const cRemoteTimer &Timer);
+ bool operator!=(const cRemoteTimer &Timer) { return !operator==(Timer); }
+
+ static int ParseDay(const char *s, time_t *FirstDay);
+ static const char *PrintDay(int d, time_t FirstDay = 0);
+ static time_t SetTime(time_t t, int SecondsFromMidnight);
+ static time_t IncDay(time_t t, int Days);
+ static int TimeToInt(int t) { return (t / 100 * 60 + t % 100) * 60; }
+
+ const char *PrintFirstDay(void) const;
+ void OnOff(void);
+ bool IsSingleEvent(void) const { return (m_Day & 0x80000000) == 0; }
+ void Skip(void) { m_FirstDay = IncDay(SetTime(StartTime(), 0), 1); }
+ bool Matches(time_t t = 0);
+ bool DayMatches(time_t t = 0);
+ int GetMDay(time_t t);
+ int GetWDay(time_t t);
+
+ bool IsValid(void) const { return m_IsValid; }
+ int Index(void) const { return m_Index; }
+ int Active(void) const { return m_Active; }
+ int Day(void) const { return m_Day; }
+ int Start(void) const { return m_Start; }
+ int Stop(void) const { return m_Stop; }
+ time_t StartTime(void) { if (!m_StartTime) Matches(); return m_StartTime; }
+ time_t StopTime(void) { if (!m_StopTime) Matches(); return m_StopTime; }
+ int Priority(void) const { return m_Priority; }
+ int Lifetime(void) const { return m_Lifetime; }
+ const char *File(void) const { return m_File; }
+ time_t FirstDay(void) const { return m_FirstDay; }
+ const cTBString &Summary(void) const { return m_Summary; }
+ const cChannel *Channel(void) const { return m_Channel; }
+
+ const char *ToText(void);
+};
+
+class cRemoteTimers: public cList<cRemoteTimer> {
+public:
+ bool Load(void);
+};
+
+extern cRemoteTimers RemoteTimers;
+
+#endif // VDR_STREAMDEV_REMOTE_H
diff --git a/client/setup.c b/client/setup.c
new file mode 100644
index 0000000..f8f53db
--- /dev/null
+++ b/client/setup.c
@@ -0,0 +1,83 @@
+/*
+ * $Id: setup.c,v 1.1 2004/12/30 22:44:03 lordjaxom Exp $
+ */
+
+#include <vdr/menuitems.h>
+
+#include "client/setup.h"
+#include "client/device.h"
+#include "i18n.h"
+
+cStreamdevClientSetup StreamdevClientSetup;
+
+cStreamdevClientSetup::cStreamdevClientSetup(void) {
+ StartClient = false;
+ RemotePort = 2004;
+ StreamPIDS = true;
+#if VDRVERSNUM >= 10300
+ StreamFilters = false;
+#endif
+ SyncEPG = false;
+ strcpy(RemoteIp, "");
+}
+
+bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
+ if (strcmp(Name, "StartClient") == 0) StartClient = atoi(Value);
+ else if (strcmp(Name, "RemoteIp") == 0) {
+ if (strcmp(Value, "-none-") == 0)
+ strcpy(RemoteIp, "");
+ else
+ strcpy(RemoteIp, Value);
+ }
+ else if (strcmp(Name, "RemotePort") == 0) RemotePort = atoi(Value);
+ else if (strcmp(Name, "StreamPIDS") == 0) StreamPIDS = atoi(Value);
+#if VDRVERSNUM >= 10300
+ else if (strcmp(Name, "StreamFilters") == 0) StreamFilters = atoi(Value);
+#endif
+ else if (strcmp(Name, "SyncEPG") == 0) SyncEPG = atoi(Value);
+ else return false;
+ return true;
+}
+
+cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(void) {
+ m_NewSetup = StreamdevClientSetup;
+
+ AddBoolEdit (tr("Start Client"), m_NewSetup.StartClient);
+ AddIpEdit (tr("Remote IP"), m_NewSetup.RemoteIp);
+ AddShortEdit(tr("Remote Port"), m_NewSetup.RemotePort);
+ AddBoolEdit (tr("MultiPID Streaming"), m_NewSetup.StreamPIDS);
+#if VDRVERSNUM >= 10300
+ AddBoolEdit (tr("Filter Streaming"), m_NewSetup.StreamFilters);
+#endif
+ AddBoolEdit (tr("Synchronize EPG"), m_NewSetup.SyncEPG);
+ SetCurrent(Get(0));
+}
+
+cStreamdevClientMenuSetupPage::~cStreamdevClientMenuSetupPage() {
+}
+
+void cStreamdevClientMenuSetupPage::Store(void) {
+ if (m_NewSetup.StartClient != StreamdevClientSetup.StartClient) {
+ if (m_NewSetup.StartClient)
+ cStreamdevDevice::Init();
+ else
+ INFO(tr("Please restart VDR to activate changes"));
+ }
+
+ SetupStore("StartClient", m_NewSetup.StartClient);
+ if (strcmp(m_NewSetup.RemoteIp, "") == 0)
+ SetupStore("RemoteIp", "-none-");
+ else
+ SetupStore("RemoteIp", m_NewSetup.RemoteIp);
+ SetupStore("RemotePort", m_NewSetup.RemotePort);
+ SetupStore("StreamPIDS", m_NewSetup.StreamPIDS);
+#if VDRVERSNUM >= 10300
+ SetupStore("StreamFilters", m_NewSetup.StreamFilters);
+#endif
+ SetupStore("SyncEPG", m_NewSetup.SyncEPG);
+
+ StreamdevClientSetup = m_NewSetup;
+
+ cStreamdevDevice::ReInit();
+}
+
diff --git a/client/setup.h b/client/setup.h
new file mode 100644
index 0000000..fe8e975
--- /dev/null
+++ b/client/setup.h
@@ -0,0 +1,39 @@
+/*
+ * $Id: setup.h,v 1.1 2004/12/30 22:44:03 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SETUPCLIENT_H
+#define VDR_STREAMDEV_SETUPCLIENT_H
+
+#include "common.h"
+
+struct cStreamdevClientSetup {
+ cStreamdevClientSetup(void);
+
+ bool SetupParse(const char *Name, const char *Value);
+
+ int StartClient;
+ char RemoteIp[20];
+ int RemotePort;
+ int StreamPIDS;
+#if VDRVERSNUM >= 10300
+ int StreamFilters;
+#endif
+ int SyncEPG;
+};
+
+extern cStreamdevClientSetup StreamdevClientSetup;
+
+class cStreamdevClientMenuSetupPage: public cStreamdevMenuSetupPage {
+private:
+ cStreamdevClientSetup m_NewSetup;
+
+protected:
+ virtual void Store(void);
+
+public:
+ cStreamdevClientMenuSetupPage(void);
+ virtual ~cStreamdevClientMenuSetupPage();
+};
+
+#endif // VDR_STREAMDEV_SETUPCLIENT_H
diff --git a/client/socket.c b/client/socket.c
new file mode 100644
index 0000000..44af8db
--- /dev/null
+++ b/client/socket.c
@@ -0,0 +1,591 @@
+/*
+ * $Id: socket.c,v 1.1 2004/12/30 22:44:04 lordjaxom Exp $
+ */
+
+#include <tools/select.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "client/socket.h"
+#include "client/setup.h"
+#include "client/remote.h"
+#include "common.h"
+#include "i18n.h"
+
+cClientSocket ClientSocket;
+
+cClientSocket::cClientSocket(void) {
+ memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
+ Reset();
+}
+
+cClientSocket::~cClientSocket() {
+ Reset();
+ if (IsOpen()) Quit();
+}
+
+void cClientSocket::Reset(void) {
+ m_StreamPIDS = false;
+
+ for (int it = 0; it < si_Count; ++it)
+ if (m_DataSockets[it] != NULL)
+ DELETENULL(m_DataSockets[it]);
+}
+
+cTBSocket *cClientSocket::DataSocket(eSocketId Id) const {
+ return m_DataSockets[Id];
+}
+
+bool cClientSocket::Command(const cTBString &Command, uint Expected,
+ uint TimeoutMs) {
+ cTBString pkt;
+ time_t st;
+
+ errno = 0;
+
+ pkt = Command + "\015\012";
+ Dprintf("OUT: |%s|\n", (const char*)Command);
+
+ st = time_ms();
+ if (!TimedWrite((const char*)pkt, pkt.Length(), TimeoutMs)) {
+ esyslog("Streamdev: Lost connection to %s:%d: %s",
+ (const char*)RemoteIp(), RemotePort(), strerror(errno));
+ Close();
+ return false;
+ }
+
+ if (Expected != 0) {
+ TimeoutMs -= time_ms() - st;
+ return Expect(Expected, NULL, TimeoutMs);
+ }
+
+ return true;
+}
+
+bool cClientSocket::Expect(uint Expected, cTBString *Result, uint TimeoutMs) {
+ char *buffer;
+ char *endptr;
+ int bufcount;
+ bool res;
+
+ errno = 0;
+
+ buffer = new char[BUFSIZ + 1];
+
+ if ((bufcount = ReadUntil(buffer, BUFSIZ, "\012", TimeoutMs))
+ == -1) {
+ esyslog("Streamdev: Lost connection to %s:%d: %s",
+ (const char*)RemoteIp(), RemotePort(), strerror(errno));
+ Close();
+ delete[] buffer;
+ return false;
+ }
+ if (buffer[bufcount - 1] == '\015')
+ --bufcount;
+ buffer[bufcount] = '\0';
+ Dprintf("IN: |%s|\n", buffer);
+
+ if (Result != NULL)
+ *Result = buffer;
+
+ res = strtoul(buffer, &endptr, 10) == Expected;
+ delete[] buffer;
+ return res;
+}
+
+bool cClientSocket::CheckConnection(void) {
+ CMD_LOCK;
+
+ if (IsOpen()) {
+ cTBSelect select;
+
+ Dprintf("connection open\n");
+
+ // XXX+ check if connection is still alive (is there a better way?)
+ // There REALLY shouldn't be anything readable according to PROTOCOL here
+ // If there is, assume it's an eof signal (subseq. read would return 0)
+ select.Add(*this, false);
+ int res;
+ if ((res = select.Select(0)) == 0) {
+ Dprintf("select said nothing happened\n");
+ return true;
+ }
+ Dprintf("closing connection (res was %d)", res);
+ Close();
+ }
+
+ if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort)){
+ esyslog("ERROR: Streamdev: Couldn't connect to %s:%d: %s",
+ (const char*)StreamdevClientSetup.RemoteIp,
+ StreamdevClientSetup.RemotePort, strerror(errno));
+ return false;
+ }
+
+ if (!Expect(220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Didn't receive greeting from %s:%d",
+ (const char*)RemoteIp(), RemotePort());
+ Close();
+ return false;
+ }
+
+ if (!Command((cTBString)"CAPS TS", 220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't negotiate capabilities on %s:%d",
+ (const char*)RemoteIp(), RemotePort());
+ Close();
+ return false;
+ }
+
+ if (StreamdevClientSetup.StreamPIDS) {
+ if (!Command("CAPS TSPIDS", 220)) {
+ if (errno != 0) {
+ Close();
+ return false;
+ }
+
+ esyslog("ERROR: Streamdev: Server %s:%d isn't capable of PID streaming",
+ (const char*)RemoteIp(), RemotePort());
+ } else
+ m_StreamPIDS = true;
+ }
+
+ isyslog("Streamdev: Connected to server %s:%d using capabilities TS%s",
+ (const char*)RemoteIp(), RemotePort(), m_StreamPIDS ? ", TSPIDS" : "");
+ return true;
+}
+
+bool cClientSocket::ProvidesChannel(const cChannel *Channel, int Priority) {
+ cTBString buffer;
+
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if (!Command("PROV " + cTBString::Number(Priority) + " "
+ + Channel->GetChannelID().ToString()))
+ return false;
+
+ if (!Expect(220, &buffer)) {
+ if (buffer.Left(3) != "560" && errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't check if %s:%d provides channel %s",
+ (const char*)RemoteIp(), RemotePort(), Channel->Name());
+ return false;
+ }
+ return true;
+}
+
+bool cClientSocket::CreateDataConnection(eSocketId Id) {
+ int idx;
+ cTBSocket listen(SOCK_STREAM);
+ cTBString buffer;
+
+ if (!CheckConnection()) return false;
+
+ if (m_DataSockets[Id] != NULL)
+ DELETENULL(m_DataSockets[Id]);
+
+ if (!listen.Listen((const char*)LocalIp(), 0, 1)) {
+ esyslog("ERROR: Streamdev: Couldn't create data connection: %s",
+ strerror(errno));
+ return false;
+ }
+
+ buffer.Format("PORT %d %s,%d,%d", Id, (const char*)LocalIp(),
+ (listen.LocalPort() >> 8) & 0xff, listen.LocalPort() & 0xff);
+ idx = 5;
+ while ((idx = buffer.Find('.', idx + 1)) != -1)
+ buffer[idx] = ',';
+
+ CMD_LOCK;
+
+ if (!Command(buffer, 220)) {
+ Dprintf("error: %m\n");
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d",
+ (const char*)RemoteIp(), RemotePort());
+ return false;
+ }
+
+ /* The server SHOULD do the following:
+ * - get PORT command
+ * - connect to socket
+ * - return 220
+ */
+
+ m_DataSockets[Id] = new cTBSocket;
+ if (!m_DataSockets[Id]->Accept(listen)) {
+ esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d%s%s",
+ (const char*)RemoteIp(), RemotePort(), errno == 0 ? "" : ": ",
+ errno == 0 ? "" : strerror(errno));
+ DELETENULL(m_DataSockets[Id]);
+ return false;
+ }
+
+ return true;
+}
+
+bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if (!Command((cTBString)"TUNE " + Channel->GetChannelID().ToString(), 220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s",
+ (const char*)RemoteIp(), RemotePort(), Channel->Name());
+ return false;
+ }
+ return true;
+}
+
+bool cClientSocket::SetPid(int Pid, bool On) {
+ if (!CheckConnection()) return false;
+
+ if (m_StreamPIDS) {
+ Dprintf("m_StreamPIDS is ON\n");
+ CMD_LOCK;
+
+ if (!Command((On ? "ADDP " : "DELP ") + cTBString::Number(Pid), 220)) {
+ if (errno == 0)
+ esyslog("Streamdev: Pid %d not available from %s:%d", Pid,
+ (const char*)LocalIp(), LocalPort());
+ return false;
+ }
+ }
+ return true;
+}
+
+#if VDRVERSNUM >= 10300
+bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
+ cTBString cmd;
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+ cmd.Format("%s %hu %hhu %hhu", On ? "ADDF" : "DELF", Pid, Tid, Mask);
+ if (!Command(cmd, 220)) {
+ if (errno == 0)
+ esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d",
+ Pid, Tid, Mask, (const char*)LocalIp(), LocalPort());
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool cClientSocket::CloseDvr(void) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if (m_DataSockets[siLive] != NULL) {
+ if (!Command("ABRT " + cTBString::Number(siLive), 220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
+ return false;
+ }
+
+ DELETENULL(m_DataSockets[siLive]);
+ }
+ return true;
+}
+
+bool cClientSocket::SynchronizeEPG(void) {
+ cTBString buffer;
+ bool res;
+ FILE *epgfd;
+
+ if (!CheckConnection()) return false;
+
+ isyslog("Streamdev: Synchronizing EPG from server\n");
+
+ CMD_LOCK;
+
+ if (!Command("LSTE"))
+ return false;
+
+ if ((epgfd = tmpfile()) == NULL) {
+ esyslog("ERROR: Streamdev: Error while processing EPG data: %s",
+ strerror(errno));
+ return false;
+ }
+
+ while ((res = Expect(215, &buffer))) {
+ if (buffer[3] == ' ') break;
+ fputs((const char*)buffer + 4, epgfd);
+ fputc('\n', epgfd);
+ }
+
+ if (!res) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't fetch EPG data from %s:%d",
+ (const char*)RemoteIp(), RemotePort());
+ fclose(epgfd);
+ return false;
+ }
+
+ rewind(epgfd);
+ if (cSchedules::Read(epgfd))
+#if VDRVERSNUM < 10300
+ cSIProcessor::TriggerDump();
+#else
+ cSchedules::Cleanup(true);
+#endif
+ else {
+ esyslog("ERROR: Streamdev: Parsing EPG data failed");
+ fclose(epgfd);
+ return false;
+ }
+ fclose(epgfd);
+ return true;
+}
+
+bool cClientSocket::Quit(void) {
+ bool res;
+
+ if (!CheckConnection()) return false;
+
+ if (!(res = Command("QUIT", 221))) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't quit command connection to %s:%d",
+ (const char*)RemoteIp(), RemotePort());
+ }
+ Close();
+ return res;
+}
+
+bool cClientSocket::LoadRecordings(cRemoteRecordings &Recordings) {
+ cTBString buffer;
+ bool res;
+
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if (!Command("LSTR"))
+ return false;
+
+ while ((res = Expect(250, &buffer))) {
+ cRemoteRecording *rec = new cRemoteRecording((const char*)buffer + 4);
+ Dprintf("recording valid: %d\n", rec->IsValid());
+ if (rec->IsValid())
+ Recordings.Add(rec);
+ else
+ delete rec;
+ if (buffer[3] == ' ') break;
+ }
+
+ if (!res && buffer.Left(3) != "550") {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
+ (const char*)RemoteIp(), RemotePort());
+ return false;
+ }
+
+ for (cRemoteRecording *r = Recordings.First(); r; r = Recordings.Next(r)) {
+ if (!Command("LSTR " + cTBString::Number(r->Index())))
+ return false;
+
+ if (Expect(250, &buffer))
+ r->ParseInfo((const char*)buffer + 4);
+ else if (buffer.Left(3) != "550") {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't fetch details for recording from "
+ "%s:%d", (const char*)RemoteIp(), RemotePort());
+ return false;
+ }
+ Dprintf("recording complete: %d\n", r->Index());
+ }
+ return res;
+}
+
+bool cClientSocket::StartReplay(const char *Filename) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if (!Command((cTBString)"PLAY " + Filename, 220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't replay \"%s\" from %s:%d",
+ Filename, (const char*)RemoteIp(), RemotePort());
+ return false;
+ }
+ return true;
+}
+
+bool cClientSocket::AbortReplay(void) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if (m_DataSockets[siReplay] != NULL) {
+ if (!Command("ABRT " + cTBString::Number(siReplay), 220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
+ return false;
+ }
+
+ DELETENULL(m_DataSockets[siReplay]);
+ }
+ return true;
+}
+
+bool cClientSocket::DeleteRecording(cRemoteRecording *Recording) {
+ bool res;
+ cTBString buffer;
+ cRemoteRecording *rec = NULL;
+
+ if (!CheckConnection())
+ return false;
+
+ CMD_LOCK;
+
+ if (!Command("LSTR"))
+ return false;
+
+ while ((res = Expect(250, &buffer))) {
+ if (rec == NULL) {
+ rec = new cRemoteRecording((const char*)buffer + 4);
+ if (!rec->IsValid() || rec->Index() != Recording->Index())
+ DELETENULL(rec);
+ }
+ if (buffer[3] == ' ') break;
+ }
+
+ if (!res && buffer.Left(3) != "550") {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
+ (const char*)RemoteIp(), RemotePort());
+ if (rec != NULL) delete rec;
+ return false;
+ }
+
+ if (rec == NULL || *rec != *Recording) {
+ ERROR(tr("Recordings not in sync! Try again..."));
+ return false;
+ }
+
+ if (!Command("DELR " + cTBString::Number(Recording->Index()), 250)) {
+ ERROR(tr("Couldn't delete recording! Try again..."));
+ return false;
+ }
+ return true;
+}
+
+bool cClientSocket::SuspendServer(void) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if (!Command("SUSP", 220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't suspend server");
+ return false;
+ }
+ return true;
+}
+
+bool cClientSocket::LoadTimers(cRemoteTimers &Timers) {
+ cTBString buffer;
+ bool res;
+
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if (!Command("LSTT"))
+ return false;
+
+ while ((res = Expect(250, &buffer))) {
+ cRemoteTimer *timer = new cRemoteTimer((const char*)buffer + 4);
+ Dprintf("timer valid: %d\n", timer->IsValid());
+ if (timer->IsValid())
+ Timers.Add(timer);
+ if (buffer[3] == ' ') break;
+ }
+
+ if (!res && buffer.Left(3) != "550") {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
+ (const char*)RemoteIp(), RemotePort());
+ return false;
+ }
+ return res;
+}
+
+bool cClientSocket::SaveTimer(cRemoteTimer *Old, cRemoteTimer &New) {
+ cTBString buffer;
+
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if (New.Index() == -1) { // New timer
+ if (!Command((cTBString)"NEWT " + New.ToText(), 250)) {
+ ERROR(tr("Couldn't save timer! Try again..."));
+ return false;
+ }
+ } else { // Modified timer
+ if (!Command("LSTT " + cTBString::Number(New.Index())))
+ return false;
+
+ if (!Expect(250, &buffer)) {
+ if (errno == 0)
+ ERROR(tr("Timers not in sync! Try again..."));
+ else
+ ERROR(tr("Server error! Try again..."));
+ return false;
+ }
+
+ cRemoteTimer oldstate((const char*)buffer + 4);
+ if (oldstate != *Old) {
+ /*Dprintf("old timer: %d,%d,%d,%d,%d,%d,%s,%d,%s,%d\n", oldstate.m_Index,
+ oldstate.m_Active,oldstate.m_Day,oldstate.m_Start,oldstate.m_StartTime,oldstate.m_Priority,oldstate.m_File,oldstate.m_FirstDay,(const char*)oldstate.m_Summary,oldstate.m_Channel->Number());
+ Dprintf("new timer: %d,%d,%d,%d,%d,%d,%s,%d,%s,%d\n", Old->m_Index,
+ Old->m_Active,Old->m_Day,Old->m_Start,Old->m_StartTime,Old->m_Priority,Old->m_File,Old->m_FirstDay,(const char*)Old->m_Summary,Old->m_Channel->Number());*/
+ ERROR(tr("Timers not in sync! Try again..."));
+ return false;
+ }
+
+ if (!Command("MODT " + cTBString::Number(New.Index()) + " "
+ + New.ToText(), 250)) {
+ ERROR(tr("Couldn't save timer! Try again..."));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cClientSocket::DeleteTimer(cRemoteTimer *Timer) {
+ cTBString buffer;
+
+ if (!CheckConnection())
+ return false;
+
+ CMD_LOCK;
+
+ if (!Command("LSTT " + cTBString::Number(Timer->Index())))
+ return false;
+
+ if (!Expect(250, &buffer)) {
+ if (errno == 0)
+ ERROR(tr("Timers not in sync! Try again..."));
+ else
+ ERROR(tr("Server error! Try again..."));
+ return false;
+ }
+
+ cRemoteTimer oldstate((const char*)buffer + 4);
+
+ if (oldstate != *Timer) {
+ ERROR(tr("Timers not in sync! Try again..."));
+ return false;
+ }
+
+ if (!Command("DELT " + cTBString::Number(Timer->Index()), 250)) {
+ ERROR(tr("Couldn't delete timer! Try again..."));
+ return false;
+ }
+ return true;
+}
diff --git a/client/socket.h b/client/socket.h
new file mode 100644
index 0000000..d4f360a
--- /dev/null
+++ b/client/socket.h
@@ -0,0 +1,71 @@
+/*
+ * $Id: socket.h,v 1.1 2004/12/30 22:44:04 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_CLIENT_CONNECTION_H
+#define VDR_STREAMDEV_CLIENT_CONNECTION_H
+
+#include <tools/socket.h>
+
+#include "common.h"
+
+#define CMD_LOCK cMutexLock CmdLock((cMutex*)&m_Mutex)
+
+class cRemoteRecordings;
+class cRemoteRecording;
+class cRemoteTimers;
+class cRemoteTimer;
+class cPES2TSRemux;
+
+class cClientSocket: public cTBSocket {
+private:
+ bool m_StreamPIDS;
+ cTBSocket *m_DataSockets[si_Count];
+ cMutex m_Mutex;
+
+protected:
+ /* Send Command, and return true if the command results in Expected.
+ Returns false on failure, setting errno appropriately if it has been
+ a system failure. If Expected is zero, returns immediately after
+ sending the command. */
+ bool Command(const cTBString &Command, uint Expected = 0,
+ uint TimeoutMs = 1500);
+
+ /* Fetch results from an ongoing Command called with Expected == 0. Returns
+ true if the response has the code Expected, returning an internal buffer
+ in the array pointer pointed to by Result. Returns false on failure,
+ setting errno appropriately if it has been a system failure. */
+ bool Expect(uint Expected, cTBString *Result = NULL, uint TimeoutMs = 1500);
+
+public:
+ cClientSocket(void);
+ virtual ~cClientSocket();
+
+ void Reset(void);
+
+ bool CheckConnection(void);
+ bool ProvidesChannel(const cChannel *Channel, int Priority);
+ bool CreateDataConnection(eSocketId Id);
+ bool SetChannelDevice(const cChannel *Channel);
+ bool SetPid(int Pid, bool On);
+#if VDRVERSNUM >= 10300
+ bool SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On);
+#endif
+ bool CloseDvr(void);
+ bool SynchronizeEPG(void);
+ bool LoadRecordings(cRemoteRecordings &Recordings);
+ bool StartReplay(const char *Filename);
+ bool AbortReplay(void);
+ bool DeleteRecording(cRemoteRecording *Recording);
+ bool LoadTimers(cRemoteTimers &Timers);
+ bool SaveTimer(cRemoteTimer *Old, cRemoteTimer &New);
+ bool DeleteTimer(cRemoteTimer *Timer);
+ bool SuspendServer(void);
+ bool Quit(void);
+
+ cTBSocket *DataSocket(eSocketId Id) const;
+};
+
+extern class cClientSocket ClientSocket;
+
+#endif // VDR_STREAMDEV_CLIENT_CONNECTION_H
diff --git a/common.c b/common.c
new file mode 100644
index 0000000..af80c6e
--- /dev/null
+++ b/common.c
@@ -0,0 +1,259 @@
+/*
+ * $Id: common.c,v 1.1 2004/12/30 22:43:55 lordjaxom Exp $
+ */
+
+#include <vdr/channels.h>
+#include <iostream>
+
+#include "common.h"
+#include "tools/select.h"
+#include "i18n.h"
+
+using namespace std;
+
+const char *VERSION = "0.3.3-pre3-geni";
+
+const char *StreamTypes[st_Count] = {
+ "TS",
+ "PES",
+ "PS",
+ "ES",
+};
+
+const char *SuspendModes[sm_Count] = {
+ "Offer suspend mode",
+ "Always suspended",
+ "Never suspended"
+};
+
+const char IpCharacters[] = "0123456789.";
+
+char *GetNextLine(char *String, uint Length, uint &Offset) {
+ char *last, *first;
+
+ first = String + Offset;
+ for (last = first; last < String + Length; ++last) {
+ if (*last == '\012') {
+ if (*(last - 1) == '\015')
+ *(last - 1) = '\0';
+
+ *last++ = '\0';
+ Dprintf("IN: |%s|\n", first);
+ Offset = last - String;
+ return first;
+ }
+ }
+ return NULL;
+}
+
+cChannel *ChannelFromString(char *String) {
+ cChannel *channel = NULL;
+
+ if (isnumber(String)) {
+ int temp = strtol(String, NULL, 10);
+ if (temp >= 1 && temp <= Channels.MaxNumber())
+ channel = Channels.GetByNumber(temp);
+ } else {
+ channel = Channels.GetByChannelID(tChannelID::FromString(String));
+
+ if (channel == NULL) {
+ int i = 1;
+ while ((channel = Channels.GetByNumber(i, 1)) != NULL) {
+ if (String == channel->Name())
+ break;
+
+ i = channel->Number() + 1;
+ }
+ }
+ }
+ return channel;
+}
+
+void cStreamdevMenuSetupPage::AddCategory(const char *Title) {
+ char *buffer = NULL;
+
+ asprintf(&buffer, "--- %s -------------------------------------------------"
+ "---------------", Title );
+
+ cOsdItem *item = new cOsdItem(buffer);
+ free(buffer);
+
+#if VDRVERSNUM < 10307
+# ifdef HAVE_BEAUTYPATCH
+ item->SetColor(clrScrolLine, clrBackground);
+# else
+ item->SetColor(clrCyan, clrBackground);
+# endif
+#else
+ item->SetSelectable(false);
+#endif
+ Add(item);
+}
+
+void cStreamdevMenuSetupPage::AddBoolEdit(const char *Title, int &Value) {
+ Add(new cMenuEditBoolItem(Title, &Value));
+}
+
+void cStreamdevMenuSetupPage::AddIpEdit(const char *Title, char *Value) {
+ Add(new cMenuEditIpItem(Title, Value));
+}
+
+void cStreamdevMenuSetupPage::AddShortEdit(const char *Title, int &Value) {
+ AddRangeEdit(Title, Value, 0, 65535);
+}
+
+void cStreamdevMenuSetupPage::AddRangeEdit(const char *Title, int &Value,
+ int Min, int Max) {
+ Add(new cMenuEditIntItem(Title, &Value, Min, Max));
+}
+
+void cStreamdevMenuSetupPage::AddSuspEdit(const char *Title, int &Value) {
+ static const char *SuspendModesTR[sm_Count] = { NULL };
+
+ if (SuspendModesTR[0] == NULL) {
+ for (int i = 0; i < sm_Count; ++i)
+ SuspendModesTR[i] = tr(SuspendModes[i]);
+ }
+
+ Add(new cMenuEditStraItem(Title, &Value, sm_Count, SuspendModesTR));
+}
+void cStreamdevMenuSetupPage::AddTypeEdit(const char *Title, int &Value) {
+ Add(new cMenuEditStraItem(Title, &Value, st_Count, StreamTypes));
+}
+
+cMenuEditIpItem::cMenuEditIpItem(const char *Name, char *Value):
+ cMenuEditItem(Name) {
+ value = Value;
+ curNum = -1;
+ pos = -1;
+ step = false;
+ Set();
+}
+
+cMenuEditIpItem::~cMenuEditIpItem() {
+}
+
+void cMenuEditIpItem::Set(void) {
+ char buf[1000];
+ if (pos >= 0) {
+ in_addr_t addr = inet_addr(value);
+ if ((int)addr == -1)
+ addr = 0;
+ int p = 0;
+ for (int i = 0; i < 4; ++i) {
+ p += snprintf(buf + p, sizeof(buf) - p, pos == i ? "[%d]" : "%d",
+ pos == i ? curNum : (addr >> (i * 8)) & 0xff);
+ if (i < 3)
+ buf[p++] = '.';
+ }
+ SetValue(buf);
+ } else
+ SetValue(value);
+}
+
+eOSState cMenuEditIpItem::ProcessKey(eKeys Key) {
+ in_addr addr;
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+
+ switch (Key) {
+ case kUp:
+ if (pos >= 0) {
+ if (curNum < 255) ++curNum;
+ } else
+ return cMenuEditItem::ProcessKey(Key);
+ break;
+
+ case kDown:
+ if (pos >= 0) {
+ if (curNum > 0) --curNum;
+ } else
+ return cMenuEditItem::ProcessKey(Key);
+ break;
+
+ case kOk:
+ if (pos >= 0) {
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ } else
+ return cMenuEditItem::ProcessKey(Key);
+ curNum = -1;
+ pos = -1;
+ break;
+
+ case kRight:
+ if (pos >= 0) {
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ }
+
+ if (pos == -1 || pos == 3)
+ pos = 0;
+ else
+ ++pos;
+
+ curNum = (addr.s_addr >> (pos * 8)) & 0xff;
+ step = true;
+ break;
+
+ case kLeft:
+ if (pos >= 0) {
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ }
+
+ if (pos <= 0)
+ pos = 3;
+ else
+ --pos;
+
+ curNum = (addr.s_addr >> (pos * 8)) & 0xff;
+ step = true;
+ break;
+
+ case k0 ... k9:
+ if (pos == -1)
+ pos = 0;
+
+ if (curNum == -1 || step) {
+ curNum = Key - k0;
+ step = false;
+ } else
+ curNum = curNum * 10 + (Key - k0);
+
+ if (!step && (curNum * 10 > 255) || (curNum == 0)) {
+ in_addr addr;
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ if (++pos == 4)
+ pos = 0;
+ curNum = (addr.s_addr >> (pos * 8)) & 0xff;
+ step = true;
+ }
+ break;
+
+ default:
+ return cMenuEditItem::ProcessKey(Key);
+ }
+
+ Set();
+ return osContinue;
+}
+
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..252a655
--- /dev/null
+++ b/common.h
@@ -0,0 +1,119 @@
+/*
+ * $Id: common.h,v 1.1 2004/12/30 22:43:55 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_COMMON_H
+#define VDR_STREAMDEV_COMMON_H
+
+#include <vdr/tools.h>
+#include <vdr/plugin.h>
+
+#include "tools/socket.h"
+
+#ifdef DEBUG
+# include <stdio.h>
+# define Dprintf(x...) fprintf(stderr, x)
+#else
+# define Dprintf(x...)
+#endif
+
+#if VDRVERSNUM < 10300
+# define TRANSPONDER(c1, c2) (ISTRANSPONDER(c1->Frequency(), c2->Frequency()))
+#else
+# define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder())
+#endif
+
+#if VDRVERSNUM < 10307
+# define INFO(s) Interface->Info(s)
+# define STATUS(s) Interface->Status(s)
+# define ERROR(s) Interface->Status(s)
+# define FLUSH() Interface->Flush()
+#else
+# define INFO(s) Skins.Message(mtInfo, s)
+# define STATUS(s) Skins.Message(mtInfo, s)
+# define ERROR(s) Skins.Message(mtStatus, s)
+# define FLUSH() Skins.Flush()
+#endif
+
+/* Check if a channel is a radio station. */
+#define ISRADIO(x) ((x)->Vpid()==0||(x)->Vpid()==1||(x)->Vpid()==0x1fff)
+
+class cChannel;
+
+char *GetNextLine(char *String, uint Length, uint &Offset);
+
+cChannel *ChannelFromString(char *String);
+
+/* Disable logging if BUFCOUNT buffer overflows occur within BUFOVERTIME
+ milliseconds. Enable logging again if there is no error within BUFOVERTIME
+ milliseconds. */
+#define BUFOVERTIME 5000
+#define BUFOVERCOUNT 100
+
+#define STREAMDEVHOSTS AddDirectory(cPlugin::ConfigDirectory(), \
+ "streamdevhosts.conf")
+
+#define POLLFAIL esyslog("Streamdev: Polling failed: %s", strerror(errno))
+#define WRITEFAIL esyslog("Streamdev: Writing failed: %s", strerror(errno))
+#define READFAIL esyslog("Streamdev: Reading failed: %s", strerror(errno))
+#define CHECKPOLL(x) if ((x)<0){POLLFAIL; return false;}
+#define CHECKWRITE(x) if ((x)<0) { WRITEFAIL; return false; }
+#define CHECKREAD(x) if ((x)<0) { READFAIL; return false; }
+
+enum eStreamType {
+ stTS,
+ stPES,
+ stPS,
+ stES,
+ st_Count
+};
+
+enum eSuspendMode {
+ smOffer,
+ smAlways,
+ smNever,
+ sm_Count
+};
+
+enum eSocketId {
+ siLive,
+ siReplay,
+ si_Count
+};
+
+extern const char *VERSION;
+extern const char *StreamTypes[st_Count];
+extern const char *SuspendModes[sm_Count];
+extern const char IpCharacters[];
+
+class cStreamdevMenuSetupPage: public cMenuSetupPage {
+protected:
+ void AddCategory(const char *Title);
+ virtual void Store(void) = 0;
+
+ void AddBoolEdit(const char *Title, int &Value);
+ void AddIpEdit(const char *Title, char *Value);
+ void AddShortEdit(const char *Title, int &Value);
+ void AddRangeEdit(const char *Title, int &Value, int Min, int Max);
+ void AddSuspEdit(const char *Title, int &Value);
+ void AddTypeEdit(const char *Title, int &Value);
+};
+
+class cMenuEditIpItem: public cMenuEditItem {
+private:
+ char *value;
+ int curNum;
+ int pos;
+ bool step;
+
+protected:
+ virtual void Set(void);
+
+public:
+ cMenuEditIpItem(const char *Name, char *Value); // Value must be 16 bytes
+ ~cMenuEditIpItem();
+
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+#endif // VDR_STREAMDEV_COMMON_H
diff --git a/i18n.c b/i18n.c
new file mode 100644
index 0000000..61ec329
--- /dev/null
+++ b/i18n.c
@@ -0,0 +1,751 @@
+/*
+ * $Id: i18n.c,v 1.1 2004/12/30 22:43:58 lordjaxom Exp $
+ */
+
+#include "i18n.h"
+
+const char *i18n_name = NULL;
+
+const tI18nPhrase Phrases[] = {
+ { "VDR Streaming Server", // English
+ "VDR Streaming Server", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "VDR-suoratoistopalvelin", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika / Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "VTP Streaming Client", // English
+ "VTP Streaming Client", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "VTP-suoratoistoasiakas ", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika / Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Start VDR-to-VDR Server",// English
+ "VDR-zu-VDR Server starten",// Deutsch
+ "", // Slovenski
+ "Avvia il Server VDR-toVDR",// Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Käynnistä VDR-palvelin", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika / Greek
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Start HTTP Server", // English
+ "HTTP Server starten", // Deutsch
+ "", // Slovenski
+ "Avvia il Server HTTP", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Käynnistä HTTP-palvelin", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "HTTP Streamtype", // English
+ "HTTP Streamtyp", // Deutsch
+ "", // Slovenski
+ "Tipo di Stream HTTP", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "HTTP-lähetysmuoto", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Start Client", // English
+ "Client starten", // Deutsch
+ "", // Slovenski
+ "Avvia il Client", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Käynnistä VDR-asiakas", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "VDR-to-VDR Server Port",// English
+ "Port des VDR-zu-VDR Servers",// Deutsch
+ "", // Slovenski
+ "Porta del Server VDR-to-VDR",// Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "VDR-palvelimen portti", // Suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "HTTP Server Port", // English
+ "Port des HTTP Servers",// Deutsch
+ "", // Slovenski
+ "Porta del Server HTTP",// Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "HTTP-palvelimen portti", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Maximum Number of Clients",// English
+ "Maximalanzahl an Clients",// Deutsch
+ "", // Slovenski
+ "Numero Massimo di Client",// Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Suurin sallittu asiakkaiden määrä", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Remote IP", // English
+ "IP der Gegenseite", // Deutsch
+ "", // Slovenski
+ "Indirizzo IP del Server",// Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Etäkoneen IP-osoite", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Remote Port", // English
+ "Port der Gegenseite", // Deutsch
+ "", // Slovenski
+ "Porta del Server Remoto",// Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Etäkoneen portti", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Remote Streamtype", // English
+ "Streamtyp von Gegenseite",// Deutsch
+ "", // Slovenski
+ "Tipo di Stream", // Italiano (oppure Flusso ?)
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Etäkoneen lähetysmuoto", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Common Settings", // English
+ "Allgemeines", // Deutsch
+ "", // Slovenski
+ "Settaggi Comuni", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Yleiset asetukset", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "VDR-to-VDR Server", // English
+ "VDR-zu-VDR Server", // Deutsch
+ "", // Slovenski
+ "Server VDR-to-VDR", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "VDR-palvelin", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "HTTP Server", // English
+ "HTTP Server", // Deutsch
+ "", // Slovenski
+ "Server HTTP", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "HTTP-palvelin", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "VDR-to-VDR Client", // English
+ "VDR-zu-VDR Client", // Deutsch
+ "", // Slovenski
+ "Client VDR-to-VDR", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "VDR-asiakas", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Please restart VDR to activate changes",// English
+ "Bitte starten Sie für die Änderungen VDR neu",// Deutsch
+ "", // Slovenski
+ "Riavviare VDR per attivare i cambiamenti",// Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Aktivoi muutokset käynnistämällä VDR uudelleen", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Synchronize EPG", // English
+ "EPG synchronisieren", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Päivitä ohjelmaopas", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Suspend Live TV", // English
+ "Live-TV pausieren", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Pysäytä suora TV-lähetys", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Suspend behaviour", // English
+ "Pausierverhalten", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Pysäytystoiminto", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Offer suspend mode", // English
+ "Pausieren anbieten", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "tyrkytä", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Always suspended", // English
+ "Immer pausiert", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "aina", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Never suspended", // English
+ "Nie pausiert", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "ei koskaan", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Streaming Control", // English
+ "Streamkontrolle", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Suoratoiston hallinta", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Fetching recordings...",// English
+ "Hole Aufnahmen...", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Haetaan tallenteita...", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Remote Recordings", // English
+ "Entfernte Aufnahmen", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Etätallenteet", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Remote Timers", // English
+ "Entfernte Timer", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Etäajastimet", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Suspend Server", // English
+ "Server pausieren", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Pysäytä palvelin", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Server is suspended", // English
+ "Server ist pausiert", // Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Palvelin on pysäytetty", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Couldn't suspend Server!",// English
+ "Konnte Server nicht pausieren!",// Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Palvelinta ei onnistuttu pysäyttämään!", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Edit remote timer", // English
+ "Entfernten Timer editieren",// Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Muokkaa etäajastinta", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Timers not in sync! Try again...",// Englisch
+ "Timer nicht synchron! Bitte wiederholen...",//Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Ajastimet eivät täsmää! Yritä uudelleen...", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Couldn't save timer! Try again...",// English
+ "Konnte Timer nicht speichern! Bitte wiederholen...",// Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Ajastimen tallennus epäonnistui! Yritä uudelleen...", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Couldn't delete timer! Try again...",// English
+ "Konnte Timer nicht löschen! Bitte wiederholen...",// Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Ajastimen poistaminen epäonnistui! Yritä uudelleen...", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Server error! Try again...",// English
+ "Serverfehler! Bitte wiederholen...",// Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Palvelimessa virhe! Yritä uudelleen...", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "MultiPID Streaming", // English
+ "Multiple PIDs streamen",// Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Usean PID:in suoratoisto", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { "Client may suspend", // English
+ "Client darf pausieren",// Deutsch
+ "", // Slovenski
+ "", // Italiano
+ "", // Nederlands
+ "", // Português
+ "", // Français
+ "", // Norsk
+ "Asiakas saa pysäyttää palvelimen", // suomi
+ "", // Polski
+ "", // Español
+ "", // Ellinika
+ "", // Svenska
+ "", // Romaneste
+ "", // Magyar
+ "", // Catala
+#if VDRVERSNUM >= 10300
+ "" // Russian
+#endif
+ },
+ { NULL }
+};
diff --git a/i18n.h b/i18n.h
new file mode 100644
index 0000000..c2eeceb
--- /dev/null
+++ b/i18n.h
@@ -0,0 +1,16 @@
+/*
+ * $Id: i18n.h,v 1.1 2004/12/30 22:43:58 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_I18N_H
+#define VDR_STREAMDEV_I18N_H
+
+#include <vdr/i18n.h>
+
+extern const char *i18n_name;
+extern const tI18nPhrase Phrases[];
+
+#undef tr
+#define tr(s) I18nTranslate(s, i18n_name)
+
+#endif // VDR_STREAMDEV_I18N_H
diff --git a/libdvbmpeg/.cvsignore b/libdvbmpeg/.cvsignore
new file mode 100644
index 0000000..4671378
--- /dev/null
+++ b/libdvbmpeg/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/libdvbmpeg/DVB.hh b/libdvbmpeg/DVB.hh
new file mode 100644
index 0000000..e713bee
--- /dev/null
+++ b/libdvbmpeg/DVB.hh
@@ -0,0 +1,446 @@
+#ifndef _DVB_DEV_HH_
+#define _DVB_DEV_HH_
+
+extern "C" {
+#include <asm/errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#define NEWSTRUCT
+#include <channel.h>
+}
+
+#include <sstream>
+#include <iostream>
+#include <iomanip>
+using namespace std;
+
+#include <osd.hh>
+#include <devices.hh>
+
+#ifndef MAXNAM
+#define MAXNAM 80
+#endif
+
+
+
+#define FRONT_DVBS 1
+#define FRONT_DVBC 2
+#define FRONT_DVBT 3
+
+#define VTXDIR "/var/vtx"
+
+#define DEC(N) dec << setw(N) << setfill('0')
+#define HEX(N) hex << setw(N) << setfill('0')
+
+#define MAXSECSIZE 4096
+
+#define NK 10
+enum {LNB=0,DIS,ROTOR,TRANS,CHAN,BOU,SAT,PICS,SWI,NTW};
+static const int nums[]={LNB,DIS,ROTOR,TRANS,CHAN,BOU,SAT,PICS,SWI,NTW};
+static const int maxs[]={ 32, 32, 32, 512,16384,512,100, 50, 10, 100};
+
+#define MAX_TRANS_CHAN 1024
+
+enum{DVB_ORIG=0, DVB_NOKIA, DVB_XML, DVB_SATCO};
+
+typedef struct frontend_stat_s{
+ fe_status_t status;
+ uint16_t snr;
+ uint16_t strength;
+ uint32_t ber;
+ uint32_t u_blocks;
+} frontend_stat;
+
+
+extern uint8_t hamtab[256];
+extern uint8_t invtab[256];
+
+#define MAX_MAG 8
+typedef struct mag_struct_ {
+ int valid;
+ int magn;
+ uint8_t flags;
+ uint8_t lang;
+ int pnum,sub;
+ uint8_t pagebuf[25*40];
+} magazin_t;
+
+
+class DVB {
+public:
+ int no_open;
+ int fd_frontend;
+ int fd_demuxa;
+ int fd_demuxv;
+ int fd_demuxpcr;
+ int fd_demuxtt;
+ int fdvb;
+
+ int minor;
+ int adapter;
+ int max_tpid;
+ int max_satid;
+ int max_chanid;
+
+ frontend_stat festat;
+
+ struct dvb_diseqc_master_cmd dcmd;
+ fe_sec_tone_mode_t tone;
+ fe_sec_voltage_t voltage;
+ int burst;
+ struct dmx_pes_filter_params pesFilterParamsV;
+ struct dmx_pes_filter_params pesFilterParamsA;
+ struct dmx_pes_filter_params pesFilterParamsP;
+ struct dmx_pes_filter_params pesFilterParamsTT;
+ struct dvb_frontend_parameters front_param;
+ int front_type;
+ int dvr_enabled;
+ OSD osd;
+ uint32_t transponder_freq;
+ char transponder_pol;
+ uint32_t transponder_srate;
+
+
+
+ fe_status_t status;
+ uint32_t ber, uncorrected_blocks;
+ uint16_t snr, signal;
+
+
+ struct Lnb *lnbs;
+ struct DiSEqC *diseqcs;
+ struct Rotor *rotors;
+ struct Transponder *tps;
+ struct Channel *chans;
+ struct Bouquet *bouqs;
+ struct Sat *sats;
+ struct Picture *pics;
+ struct Switch *swis;
+ struct Network *ntws;
+ int num[NK];
+ int oldsec;
+ int tryit;
+ int oldpol;
+
+ char *vtxdir;
+ magazin_t magazin[MAX_MAG];
+
+ DVB(){
+ no_open = 0;
+ max_tpid = 0;
+ max_satid = 0;
+ max_chanid = 0;
+ minor = 0;
+
+ fd_frontend = -1;
+ fd_demuxa = -1;
+ fd_demuxpcr = -1;
+ fd_demuxv = -1;
+ fd_demuxtt = -1;
+ fdvb = -1;
+ vtxdir = NULL;
+ transponder_freq=0;
+ transponder_pol=0;
+ transponder_srate=0;
+ }
+
+ DVB(int i){
+ if (i >= 0)
+ no_open = 0;
+ else
+ no_open = 1;
+ max_tpid = 0;
+ max_satid = 0;
+ max_chanid = 0;
+
+ fd_frontend = -1;
+ fd_demuxa = -1;
+ fd_demuxpcr = -1;
+ fd_demuxv = -1;
+ fd_demuxtt = -1;
+ fdvb = -1;
+ vtxdir = NULL;
+ transponder_freq=0;
+ transponder_pol=0;
+ transponder_srate=0;
+
+ init("","",i);
+ }
+
+ DVB(char *a, char *b) {
+ max_tpid = 0;
+ max_satid = 0;
+ max_chanid = 0;
+
+ fd_frontend = -1;
+ fd_demuxa = -1;
+ fd_demuxpcr = -1;
+ fd_demuxv = -1;
+ fd_demuxtt = -1;
+
+ fdvb = -1;
+ vtxdir = NULL;
+ init(a,b,0);
+ }
+
+ ~DVB();
+
+ void use_osd(int fd = -1){
+ char dvn[32];
+ if (no_open) return;
+ if (fd < 0) fd = 0;
+ sprintf(dvn,OSD_DEV,adapter,fd);
+ fdvb = open(dvn, O_RDWR);
+
+ if (fdvb >= 0){
+ cerr << dvn << " for OSD" << endl;
+ osd.init(fdvb);
+ } else perror("osd");
+ osd.Open(80, 500, 640, 540, 2, 0, 2);
+ osd.SetColor(0, 0, 0, 0, 255);
+ osd.SetColor(1, 240, 240, 240, 255);
+ osd.Show();
+ }
+
+ void set_vtxdir(char *newname){
+ if (!newname) return;
+ if (vtxdir) free(vtxdir);
+ vtxdir = (char *) malloc(sizeof(char)*(strlen(newname)+1));
+ if (vtxdir)
+ strncpy(vtxdir, newname, strlen(newname));
+ }
+
+ void close_osd(){
+ osd.Close(fdvb);
+ close(fdvb);
+ }
+
+ int DVR_enabled(){
+ if (no_open) return -1;
+ return dvr_enabled;
+ }
+
+ void enable_DVR(){
+ if (no_open) return;
+ dvr_enabled = 1;
+ }
+
+ void enable_DVR_other(){
+ if (no_open) return;
+ dvr_enabled = 2;
+ }
+
+ void disable_DVR(){
+ if (no_open) return;
+ dvr_enabled = 0;
+ }
+
+ void init(char *a="/dev/video0", char *b="/dev/vbi0",int adapt=0,
+ int minor = 0);
+
+
+ inline void init(char *a, char *b){
+ if (no_open) return;
+ init(a,b,0,0);
+ }
+
+ int check_frontend();
+
+ void set_apid(ushort apid);
+ void set_vpid(ushort vpid);
+ void set_pcrpid(ushort vpid);
+ void set_ttpid(ushort ttpid);
+ int set_apid_fd(ushort apid, int fd);
+ int set_vpid_fd(ushort vpid, int fd);
+ int set_ttpid_fd(ushort ttpid, int fd);
+ int set_pcrpid_fd(ushort pcrpid, int fd);
+ int set_otherpid_fd(ushort otherpid, int fd);
+
+
+ int set_lnb(int dis);
+ void set_diseqc_nb(int nr);
+ int set_front(void);
+ void get_front(void);
+
+ void scan_pf_eit(int chnr,
+ int (*callback)(uint8_t *data, int l, int pnr,
+ int c_n, uint8_t *t));
+
+ void scan_pf_eit(Channel *chan,
+ int (*callback)(uint8_t *data, int l, int pnr,
+ int c_n, uint8_t *t));
+ void scan_pf_eit(int chnr);
+
+
+ int search_in_TP(Transponder &tp, int show=1, int verbose=0);
+ int search_in_TP(uint16_t tpid, uint16_t satid, int show=1,
+ int verbose=0);
+ int scan_TP(uint16_t tpid, uint16_t satid, int timeout=-1, int verbose=0);
+
+ int GetSection(uint8_t *buf,
+ uint16_t PID, uint8_t TID, uint16_t TIDExt,
+ uint16_t FilterTIDExt,
+ uint8_t secnum, uint8_t &msecnum);
+ int GetSection(uint8_t *buf,
+ uint16_t PID, uint8_t *filter, uint8_t *mask,
+ uint8_t secnum, uint8_t &msecnum);
+ int GetSection(uint8_t *buf, ushort PID, uint8_t sec,
+ uint8_t secnum, uint8_t &msecnum);
+ int SetFilter(uint16_t pid, uint8_t *filter,
+ uint8_t *mask,
+ uint32_t timeout, uint32_t flags);
+ uint16_t SetFilter(uint16_t pid, uint16_t section, uint16_t mode);
+ int CloseFilter(int h);
+
+
+ void bar2(int x, int y, int w, int h, int val, int col1, int col2);
+
+ int SetTP(unsigned int, unsigned int);
+ int scan(void);
+ int scan_all_tps(void);
+ int scan_lnb(struct Lnb &);
+ int scan_cable(Sat &sat);
+ int scan_sat(struct Sat &);
+ int scan_tp(struct Transponder &);
+
+ int AddLNB(int id, int t, uint l1, uint l2, uint sl,
+ int dnr, int dis, int sw);
+ int AddSat(Sat &sat);
+ int AddSat(int satid, unsigned int lnbid, char *name, uint fmin, uint fmax);
+ int AddTP(Transponder &tp);
+ int AddChannel(Channel &chan);
+ int parse_descriptor(Channel *chan, uint8_t *data, int length);
+ int parse_pmt(Channel *chan, uint8_t *data);
+ int parse_pat(Channel *chan, uint8_t *data);
+
+ int check_pids(Channel *chan);
+ void check_all_pids();
+ void scan_sdt(Channel *chan);
+ int scan_sdts(int *chs, int n);
+
+ int channel_num(void) {
+ return num[CHAN];
+ };
+
+ int channel_change(int n) {
+ return 0;
+ };
+ int SetChannel(uint16_t, uint16_t, uint16_t, uint16_t);
+ int SetChannel(Channel *chan, char* apref=NULL, uint16_t *apidp=NULL,
+ uint16_t *vpidp=NULL) ;
+ int SetChannel(int chnr, char *apref=NULL, uint16_t *apidp=NULL,
+ uint16_t *vpidp=NULL);
+ int GetChannel(int chnr, struct channel *);
+ int NumChannel(void) {
+ return num[CHAN];
+ }
+ int tune_it(struct dvb_frontend_parameters *qpsk);
+ void find_satid(Channel &chan);
+ int check_input_format(istream &ins);
+ void read_original(istream &ins);
+ int get_all_progs(uint16_t *progbuf, uint16_t *pnrbuf, int length);
+ uint16_t find_pnr(uint16_t vpid, uint16_t apid);
+ int get_pids(uint16_t prog_pid, uint16_t *vpid, uint16_t *apids,
+ uint16_t *ttpid, uint8_t *apids_name=NULL);
+ void AddECM(Channel *chan, uint8_t *data, int length);
+ int check_ecm(Channel *chan);
+ void add_vtx_line(magazin_t *mag, int line, uint8_t *data, int pnr);
+
+ friend ostream &operator<<(ostream &stream, DVB &x);
+ friend istream &operator>>(istream &stream, DVB &x);
+
+};
+
+#define NOKIA_MAX_SAT 4
+class nokiaconv{
+public:
+ DVB *dvb;
+ struct lnb_sat_l{
+ int n;
+ int diseqc[NOKIA_MAX_SAT];
+ char sat_names[NOKIA_MAX_SAT][MAXNAM+1];
+ int satid[NOKIA_MAX_SAT];
+ } lnb_sat;
+
+ nokiaconv(DVB *d){
+ dvb = d;
+ }
+
+ friend istream &operator>>(istream &stream, nokiaconv &x);
+};
+
+#define XML_MAX_SAT 4
+class xmlconv{
+public:
+ DVB *dvb;
+ struct lnb_sat_l{
+ int n;
+ int diseqc[XML_MAX_SAT];
+ char sat_names[XML_MAX_SAT][MAXNAM+1];
+ int satid[XML_MAX_SAT];
+ } lnb_sat;
+
+ xmlconv(DVB *d){
+ dvb = d;
+ }
+ int read_stream(istream &ins, int nchan);
+ int read_desc(istream &ins, int nchan);
+ int read_serv(istream &ins, int ctp, int csat);
+ int read_trans(istream &ins, int satid);
+ int read_sat(istream &ins, int satid = -1);
+ int skip_tag(istream &ins, char *tag);
+ int read_iso639(istream &ins, int nchan, int apids);
+
+ friend istream &operator>>(istream &stream, xmlconv &x);
+};
+
+
+
+#define SATCO_MAX_SAT 10
+class satcoconv{
+public:
+ DVB *dvb;
+ int nlnb;
+
+ satcoconv(DVB *d){
+ dvb = d;
+ }
+
+ friend istream &operator>>(istream &stream, satcoconv &x);
+};
+
+void hdump(uint8_t *buf, int n);
+int get_dvbrc(char *path, DVB &dv, int dev, int len);
+int set_dvbrc(char *path, DVB &dv, int dev, int len);
+void dvb2txt(char *out, char *in, int len);
+int set_sfront(int fdf, uint32_t freq, uint32_t pol, uint32_t sr , int snum, fe_code_rate_t fec);
+void set_pes_filt(int fd,uint16_t pes_pid);
+void set_diseqc(int fdf, int snum, fe_sec_voltage_t v, fe_sec_tone_mode_t t);
+int tune(int fdf, uint32_t freq, uint32_t sr, fe_code_rate_t fec);
+int set_sfront(int fdf, uint32_t freq, uint32_t pol, uint32_t sr , int snum,
+ fe_code_rate_t fec);
+
+
+struct in_addr getaddress (const char *name);
+int tcp_client_connect(const char *hostname, int sckt);
+int udp_client_connect(const char *filename);
+void client_send_msg(int fd, uint8_t *msg, int size);
+int chck_frontend (int fefd, frontend_stat *festat);
+
+uint8_t deham(uint8_t x, uint8_t y);
+
+#endif
diff --git a/libdvbmpeg/Makefile b/libdvbmpeg/Makefile
new file mode 100644
index 0000000..a56cb6b
--- /dev/null
+++ b/libdvbmpeg/Makefile
@@ -0,0 +1,33 @@
+INCS = -I.
+CFLAGS = -g -Wall -O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
+MFLAG = -M
+OBJS = ctools.o ringbuffy.o remux.o transform.o cpptools.o
+SRC = $(wildcard *.c)
+CPPSRC = $(wildcard *.cpp)
+CSRC = $(wildcard *.cc)
+
+DESTDIR = /usr/local
+
+.PHONY: depend clean install uninstall
+
+clean:
+ - rm -f *.o *~ *.a .depend
+
+libdvbmpegtools.a: $(OBJS)
+ ar -rcs libdvbmpegtools.a $(OBJS)
+
+%.o: %.cc
+ $(CXX) -c $(CFLAGS) $(INCS) $(DEFINES) $<
+
+%.o: %.cpp
+ $(CXX) -c $(CFLAGS) $(INCS) $(DEFINES) $<
+
+%.o: %.c
+ $(CC) -c $(CFLAGS) $(INCS) $(DEFINES) $<
+
+.depend:
+ $(CXX) $(DEFINES) $(MFLAG) $(SRC) $(CSRC) $(CPPSRC) $(INCS)> .depend
+
+
+
+-include .depend
diff --git a/libdvbmpeg/OSD.h b/libdvbmpeg/OSD.h
new file mode 100644
index 0000000..385ac78
--- /dev/null
+++ b/libdvbmpeg/OSD.h
@@ -0,0 +1,30 @@
+#ifndef _OSD_H_
+#define _OSD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+int OSDClose(int dev);
+int OSDOpen(int dev, int x0, int y0, int x1, int y1, int BitPerPixel, int mix);
+int OSDShow(int dev);
+int OSDHide(int dev);
+int OSDClear(int dev);
+int OSDFill(int dev, int color);
+int OSDSetColor(int dev, int color, int r, int g, int b, int op);
+int OSDText(int dev, int x, int y, int size, int color, const char *text);
+int OSDSetPalette(int dev, int first, int last, unsigned char *data);
+int OSDSetTrans(int dev, int trans);
+int OSDSetPixel(int dev, int x, int y, unsigned int color);
+int OSDGetPixel(int dev, int x, int y);
+int OSDSetRow(int dev, int x, int y, int x1, unsigned char *data);
+int OSDSetBlock(int dev, int x, int y, int x1, int y1, int inc, unsigned char *data);
+int OSDFillRow(int dev, int x, int y, int x1, int color);
+int OSDFillBlock(int dev, int x, int y, int x1, int y1, int color);
+int OSDLine(int dev, int x, int y, int x1, int y1, int color);
+int OSDQuery(int dev);
+int OSDSetWindow(int dev, int win);
+int OSDMoveWindow(int dev, int x, int y);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/libdvbmpeg/channel.h b/libdvbmpeg/channel.h
new file mode 100644
index 0000000..c4f62b4
--- /dev/null
+++ b/libdvbmpeg/channel.h
@@ -0,0 +1,58 @@
+#ifndef _CHANNEL_H
+#define _CHANNEL_H
+
+#include <sys/types.h>
+
+struct channel {
+ int id;
+ char name[81];
+ int type;
+ ushort pnr;
+ ushort vpid;
+ ushort apids[8];
+ ushort apidnum;
+ ushort ac3pid;
+ ushort pcrpid;
+
+ uint freq;
+ int pol;
+ int qam;
+ uint srate;
+ int fec;
+};
+
+#ifdef NEWSTRUCT
+
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+
+#define DVR_DEV "/dev/dvb/adapter%d/dvr%d"
+#define VIDEO_DEV "/dev/dvb/adapter%d/video%d"
+#define AUDIO_DEV "/dev/dvb/adapter%d/audio%d"
+#define DEMUX_DEV "/dev/dvb/adapter%d/demux%d"
+#define FRONT_DEV "/dev/dvb/adapter%d/frontend%d"
+#define OSD_DEV "/dev/dvb/adapter%d/osd%d"
+#define CA_DEV "/dev/dvb/adapter%d/ca%d"
+
+#else
+
+#include <ost/dmx.h>
+#include <ost/frontend.h>
+#include <ost/sec.h>
+#include <ost/video.h>
+#include <ost/audio.h>
+
+#define DVR_DEV "/dev/ost/dvr%d"
+#define VIDEO_DEV "/dev/ost/video%d"
+#define AUDIO_DEV "/dev/ost/audio%d"
+#define DEMUX_DEV "/dev/ost/demux%d"
+#define FRONT_DEV "/dev/ost/frontend%d"
+#define OSD_DEV "/dev/ost/osd%d"
+#define CA_DEV "/dev/ost/ca%d"
+
+#endif
+
+
+#endif
diff --git a/libdvbmpeg/ci.hh b/libdvbmpeg/ci.hh
new file mode 100644
index 0000000..77e7684
--- /dev/null
+++ b/libdvbmpeg/ci.hh
@@ -0,0 +1,167 @@
+/*
+ * ci.hh: Common Interface
+ *
+ * Copyright (C) 2000 Klaus Schmidinger
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ * The author can be reached at kls@cadsoft.de
+ *
+ * The project's page is at http://www.cadsoft.de/people/kls/vdr
+ *
+ */
+
+#ifndef __CI_H
+#define __CI_H
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#define MAXCASYSTEMIDS 16
+
+class cMutex {
+ friend class cCondVar;
+private:
+ pthread_mutex_t mutex;
+ pid_t lockingPid;
+ int locked;
+public:
+ cMutex(void);
+ ~cMutex();
+ void Lock(void);
+ void Unlock(void);
+ };
+
+class cMutexLock {
+private:
+ cMutex *mutex;
+ bool locked;
+public:
+ cMutexLock(cMutex *Mutex = NULL);
+ ~cMutexLock();
+ bool Lock(cMutex *Mutex);
+ };
+
+
+class cCiMMI;
+
+class cCiMenu {
+ friend class cCiMMI;
+private:
+ enum { MAX_CIMENU_ENTRIES = 64 }; ///< XXX is there a specified maximum?
+ cCiMMI *mmi;
+ bool selectable;
+ char *titleText;
+ char *subTitleText;
+ char *bottomText;
+ char *entries[MAX_CIMENU_ENTRIES];
+ int numEntries;
+ bool AddEntry(char *s);
+ cCiMenu(cCiMMI *MMI, bool Selectable);
+public:
+ ~cCiMenu();
+ const char *TitleText(void) { return titleText; }
+ const char *SubTitleText(void) { return subTitleText; }
+ const char *BottomText(void) { return bottomText; }
+ const char *Entry(int n) { return n < numEntries ? entries[n] : NULL; }
+ int NumEntries(void) { return numEntries; }
+ bool Selectable(void) { return selectable; }
+ bool Select(int Index);
+ bool Cancel(void);
+ };
+
+class cCiEnquiry {
+ friend class cCiMMI;
+private:
+ cCiMMI *mmi;
+ char *text;
+ bool blind;
+ int expectedLength;
+ cCiEnquiry(cCiMMI *MMI);
+public:
+ ~cCiEnquiry();
+ const char *Text(void) { return text; }
+ bool Blind(void) { return blind; }
+ int ExpectedLength(void) { return expectedLength; }
+ bool Reply(const char *s);
+ bool Cancel(void);
+ };
+
+class cCiCaPmt {
+ friend class cCiConditionalAccessSupport;
+private:
+ int length;
+ int esInfoLengthPos;
+ uint8_t capmt[2048]; ///< XXX is there a specified maximum?
+public:
+ cCiCaPmt(int ProgramNumber);
+ void AddPid(int Pid);
+ void AddCaDescriptor(int Length, uint8_t *Data);
+ };
+
+#define MAX_CI_SESSION 16 //XXX
+
+class cCiSession;
+class cCiTransportLayer;
+class cCiTransportConnection;
+
+class cCiHandler {
+private:
+ cMutex mutex;
+ int numSlots;
+ bool newCaSupport;
+ bool hasUserIO;
+ cCiSession *sessions[MAX_CI_SESSION];
+ cCiTransportLayer *tpl;
+ cCiTransportConnection *tc;
+ int ResourceIdToInt(const uint8_t *Data);
+ bool Send(uint8_t Tag, int SessionId, int ResourceId = 0, int Status = -1);
+ cCiSession *GetSessionBySessionId(int SessionId);
+ cCiSession *GetSessionByResourceId(int ResourceId, int Slot);
+ cCiSession *CreateSession(int ResourceId);
+ bool OpenSession(int Length, const uint8_t *Data);
+ bool CloseSession(int SessionId);
+ int CloseAllSessions(int Slot);
+ cCiHandler(int Fd, int NumSlots);
+public:
+ ~cCiHandler();
+ static cCiHandler *CreateCiHandler(const char *FileName);
+ int NumSlots(void) { return numSlots; }
+ bool Process(void);
+ bool HasUserIO(void) { return hasUserIO; }
+ bool EnterMenu(int Slot);
+ cCiMenu *GetMenu(void);
+ cCiEnquiry *GetEnquiry(void);
+ bool SetCaPmt(cCiCaPmt &CaPmt);
+ const unsigned short *GetCaSystemIds(int Slot);
+ bool SetCaPmt(cCiCaPmt &CaPmt, int Slot);
+ bool Reset(int Slot);
+ };
+
+int tcp_listen(struct sockaddr_in *name,int sckt,unsigned long address=INADDR_ANY);
+int accept_tcp(int ip_sock,struct sockaddr_in *ip_name);
+int udp_listen(struct sockaddr_un *name,char const * const filename);
+int accept_udp(int ip_sock,struct sockaddr_un *ip_name);
+
+#endif //__CI_H
diff --git a/libdvbmpeg/cpptools.cc b/libdvbmpeg/cpptools.cc
new file mode 100644
index 0000000..91808cc
--- /dev/null
+++ b/libdvbmpeg/cpptools.cc
@@ -0,0 +1,946 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de
+ */
+
+#include "cpptools.hh"
+
+#define HEX(N) "0x" << hex << setw(2) << setfill('0') \
+<< int(N) << " " << dec
+#define HHEX(N,M) "0x" << hex << setw(M) << setfill('0') \
+<< int(N) << " " << dec
+#define LHEX(N,M) "0x" << hex << setw(M) << setfill('0') \
+<< long(N) << " " << dec
+
+#define MAX_SEARCH 1024 * 1024
+
+ostream & operator << (ostream & stream, PES_Packet & x){
+
+ if (x.info){
+ cerr << "PES Packet: " ;
+ switch ( x.p.stream_id ) {
+
+ case PROG_STREAM_MAP:
+ cerr << "Program Stream Map";
+ break;
+ case PRIVATE_STREAM2:
+ cerr << "Private Stream 2";
+ break;
+ case PROG_STREAM_DIR:
+ cerr << "Program Stream Directory";
+ break;
+ case ECM_STREAM :
+ cerr << "ECM Stream";
+ break;
+ case EMM_STREAM :
+ cerr << "EMM Stream";
+ break;
+ case PADDING_STREAM :
+ cerr << "Padding Stream";
+ break;
+ case DSM_CC_STREAM :
+ cerr << "DSM Stream";
+ break;
+ case ISO13522_STREAM:
+ cerr << "ISO13522 Stream";
+ break;
+ case PRIVATE_STREAM1:
+ cerr << "Private Stream 1";
+ break;
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ cerr << "Audio Stream " << HEX(x.p.stream_id);
+ break;
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ cerr << "Video Stream " << HEX(x.p.stream_id);
+ break;
+
+ }
+ cerr << " MPEG" << x.p.mpeg << endl;
+ if ( x.p.mpeg == 2 ){
+ cerr << " FLAGS: ";
+
+ if (x.p.flags1 & SCRAMBLE_FLAGS){
+ cerr << " SCRAMBLE(";
+ cerr << ((x.p.flags1 & SCRAMBLE_FLAGS)>>4);
+ cerr << ")";
+ }
+ if (x.p.flags1 & PRIORITY_FLAG)
+ cerr << " PRIORITY";
+ if (x.p.flags1 & DATA_ALIGN_FLAG)
+ cerr << " DATA_ALIGN";
+ if (x.p.flags1 & COPYRIGHT_FLAG)
+ cerr << " COPYRIGHT";
+ if (x.p.flags1 & ORIGINAL_FLAG)
+ cerr << " ORIGINAL";
+
+ if (x.p.flags2 & PTS_DTS_FLAGS){
+ cerr << " PTS_DTS(";
+ cerr << ((x.p.flags2 & PTS_DTS_FLAGS)>>6);
+ cerr << ")";
+ }
+ if (x.p.flags2 & ESCR_FLAG)
+ cerr << " ESCR";
+ if (x.p.flags2 & ES_RATE_FLAG)
+ cerr << " ES_RATE";
+ if (x.p.flags2 & DSM_TRICK_FLAG)
+ cerr << " DSM_TRICK";
+ if (x.p.flags2 & ADD_CPY_FLAG)
+ cerr << " ADD_CPY";
+ if (x.p.flags2 & PES_CRC_FLAG)
+ cerr << " CRC";
+ if (x.p.flags2 & PES_EXT_FLAG)
+ cerr << " EXT";
+
+ cerr << endl;
+
+ if ((x.p.flags2 & PTS_DTS_FLAGS) == PTS_ONLY)
+ cerr << " PTS: "
+ << LHEX(ntohl(x.WPTS()),8)
+ << "(h" << int(x.high_pts()) << ")"
+ << endl;
+ else if ((x.p.flags2 & PTS_DTS_FLAGS) == PTS_DTS){
+ cerr << " PTS: "
+ << LHEX(ntohl(x.WPTS()),8)
+ << "(h" << int(x.high_pts()) << ")";
+ cerr << " DTS: "
+ << LHEX(ntohl(x.WDTS()),8)
+ << "(h" << int(x.high_dts()) << ")"
+ << endl;
+ }
+/*
+ if (x.p.flags2 & ESCR_FLAG)
+
+
+ if (x.p.flags2 & ES_RATE_FLAG)
+
+
+ if (x.p.flags2 & DSM_TRICK_FLAG)
+
+
+ if (x.p.flags2 & ADD_CPY_FLAG)
+
+
+ if (x.p.flags2 & PES_CRC_FLAG)
+
+
+ if (x.p.flags2 & PES_EXT_FLAG){
+
+ if (x.p.priv_flags & PRIVATE_DATA)
+ stream.write(x.p.pes_priv_data,16);
+
+ if (x.p.priv_flags & HEADER_FIELD){
+ stream.write(&x.p.pack_field_length,1);
+ x.p.pack_header = new
+ uint8_t[x.p.pack_field_length];
+ stream.write(x.p.pack_header,
+ x.p.pack_field_length);
+ }
+
+ if ( x.p.priv_flags & PACK_SEQ_CTR){
+ stream.write(&x.p.pck_sqnc_cntr,1);
+ stream.write(&x.p.org_stuff_length,1);
+ }
+
+ if ( x.p.priv_flags & P_STD_BUFFER)
+ stream.write(x.p.p_std,2);
+
+ if ( x.p.priv_flags & PES_EXT_FLAG2){
+ stream.write(&x.p.pes_ext_lngth,1);
+ x.p.pes_ext = new
+ uint8_t[x.p.pes_ext_lngth];
+ stream.write(x.p.pes_ext,
+ x.p.pes_ext_lngth);
+ }
+ }
+ } else {
+ if ((x.p.flags2 & PTS_DTS_FLAGS) == PTS_ONLY)
+ stream.write(x.p.pts,5);
+ else if ((x.p.flags2 & PTS_DTS_FLAGS) ==
+ PTS_DTS){
+ stream.write(x.p.pts,5);
+ stream.write(x.p.dts,5);
+ }
+*/
+ }
+ cerr << endl << endl;
+ return stream;
+ }
+
+ int l = x.p.length+x.p.pes_hlength+9;
+ uint8_t buf[l];
+ int length = cwrite_pes(buf,&(x.p),l);
+ stream.write((char *)buf,length);
+
+ return stream;
+}
+
+static unsigned int find_length(istream & stream){
+ streampos p = 0;
+ streampos start = 0;
+ streampos q = 0;
+ int found = 0;
+ uint8_t sync4[4];
+
+ start = stream.tellg();
+ start -=2;
+ stream.seekg(start);
+ while ( !stream.eof() && !found ){
+ p = stream.tellg();
+ stream.read((char *)&sync4,4);
+ if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
+ switch ( sync4[3] ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ found = 1;
+ break;
+ default:
+ q = stream.tellg();
+ break;
+ }
+ }
+ }
+ q = stream.tellg();
+ stream.seekg(streampos(2)+start);
+ if (found) return (unsigned int)(q-start)-4-2;
+ else return (unsigned int)(q-start)-2;
+
+}
+
+istream & operator >> (istream & stream, PES_Packet & x){
+
+ uint8_t sync4[4];
+ int found=0;
+ int done=0;
+ streampos p = 0;
+
+ while (!stream.eof() && !found) {
+ p = stream.tellg();
+ stream.read((char *)&sync4,4);
+ if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
+ x.p.stream_id = sync4[3];
+
+ switch ( sync4[3] ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ found = 1;
+ stream.read((char *)x.p.llength,2);
+ x.setlength();
+ if (!x.p.length){
+ x.p.length = find_length(stream);
+ x.Nlength();
+ }
+ stream.read((char *)x.p.pes_pckt_data,x.p.length);
+ done = 1;
+ break;
+ case PADDING_STREAM :
+ found = 1;
+ stream.read((char *)x.p.llength,2);
+ x.setlength();
+ if (!x.p.length){
+ x.p.length = find_length(stream);
+ x.Nlength();
+ }
+ x.p.padding = x.p.length;
+ stream.read((char *)x.p.pes_pckt_data,x.p.length);
+ done = 1;
+ break;
+
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ stream.read((char *)x.p.llength,2);
+ x.setlength();
+ if (!x.p.length){
+ x.p.length = find_length(stream);
+ x.Nlength();
+ }
+ found = 1;
+ break;
+
+ default:
+ stream.seekg(p+streampos(1));
+ break;
+ }
+ } else stream.seekg(p+streampos(1));
+ }
+
+ if ( found && !done) {
+ p = stream.tellg();
+ stream.read((char *)&x.p.flags1,1);
+ if ( (x.p.flags1 & 0xC0) == 0x80 )
+ x.p.mpeg = 2;
+ else
+ x.p.mpeg = 1;
+ if ( x.p.mpeg == 2 ){
+ stream.read((char *)&x.p.flags2,1);
+ stream.read((char *)&x.p.pes_hlength,1);
+
+ if ((int)x.p.length > x.p.pes_hlength+3)
+ x.p.length -=x.p.pes_hlength+3;
+ else
+ return stream;
+
+ uint8_t count = x.p.pes_hlength;
+
+ if ((x.p.flags2 & PTS_DTS_FLAGS) == PTS_ONLY){
+ stream.read((char *)x.p.pts,5);
+ count -=5;
+ } else
+ if ((x.p.flags2 & PTS_DTS_FLAGS) == PTS_DTS){
+ stream.read((char *)x.p.pts,5);
+ stream.read((char *)x.p.dts,5);
+ count -= 10;
+ }
+
+ if (x.p.flags2 & ESCR_FLAG){
+ stream.read((char *)x.p.escr,6);
+ count -= 6;
+ }
+
+ if (x.p.flags2 & ES_RATE_FLAG){
+ stream.read((char *)x.p.es_rate,3);
+ count -= 6;
+ }
+
+ if (x.p.flags2 & DSM_TRICK_FLAG){
+ stream.read((char *)&x.p.trick,1);
+ count -= 1;
+ }
+
+ if (x.p.flags2 & ADD_CPY_FLAG){
+ stream.read((char *)&x.p.add_cpy,1);
+ count -= 1;
+ }
+
+ if (x.p.flags2 & PES_CRC_FLAG){
+ stream.read((char *)x.p.prev_pes_crc,2);
+ count -= 2;
+ }
+
+ if (x.p.flags2 & PES_EXT_FLAG){
+ stream.read((char *)&x.p.priv_flags,1);
+ count -= 1;
+
+ if (x.p.priv_flags & PRIVATE_DATA){
+ stream.read((char *)x.p.pes_priv_data,16);
+ count -= 16;
+ }
+
+ if (x.p.priv_flags & HEADER_FIELD){
+ stream.read((char *)&x.p.pack_field_length,1);
+ x.p.pack_header = new
+ uint8_t[x.p.pack_field_length];
+ stream.read((char *)x.p.pack_header,
+ x.p.pack_field_length);
+ count -= 1+x.p.pack_field_length;
+ }
+
+ if ( x.p.priv_flags & PACK_SEQ_CTR){
+ stream.read((char *)&x.p.pck_sqnc_cntr,1);
+ stream.read((char *)&x.p.org_stuff_length,1);
+ count -= 2;
+ }
+
+ if ( x.p.priv_flags & P_STD_BUFFER){
+ stream.read((char *)x.p.p_std,2);
+ count -= 2;
+ }
+
+ if ( x.p.priv_flags & PES_EXT_FLAG2){
+ stream.read((char *)&x.p.pes_ext_lngth,1);
+ x.p.pes_ext = new
+ uint8_t[x.p.pes_ext_lngth];
+ stream.read((char *)x.p.pes_ext,
+ x.p.pes_ext_lngth);
+ count -= 1+x.p.pes_ext_lngth;
+ }
+ }
+ x.p.stuffing = count;
+ uint8_t dummy;
+ for(int i = 0; i< count ;i++)
+ stream.read((char *)&dummy,1);
+
+ } else {
+ uint8_t check;
+ x.p.mpeg1_pad = 1;
+ check = x.p.flags1;
+ while (check == 0xFF){
+ stream.read((char *)&check,1);
+ x.p.mpeg1_pad++;
+ }
+
+ if ( (check & 0xC0) == 0x40){
+ stream.read((char *)&check,1);
+ x.p.mpeg1_pad++;
+ stream.read((char *)&check,1);
+ x.p.mpeg1_pad++;
+ }
+ x.p.flags2 = 0;
+ x.p.length -= x.p.mpeg1_pad;
+
+ stream.seekg(p);
+ if ( (check & 0x30)){
+ x.p.length ++;
+ x.p.mpeg1_pad --;
+
+ if (check == x.p.flags1){
+ x.p.pes_hlength = 0;
+ } else {
+ x.p.mpeg1_headr =
+ new uint8_t[x.p.mpeg1_pad];
+ x.p.pes_hlength = x.p.mpeg1_pad;
+ stream.read((char *)x.p.mpeg1_headr,
+ x.p.mpeg1_pad);
+ }
+
+ x.p.flags2 = (check & 0xF0) << 2;
+ if ((x.p.flags2 & PTS_DTS_FLAGS) == PTS_ONLY){
+ stream.read((char *)x.p.pts,5);
+ x.p.length -= 5;
+ x.p.pes_hlength += 5;
+ }
+ else if ((x.p.flags2 & PTS_DTS_FLAGS) ==
+ PTS_DTS){
+ stream.read((char *)x.p.pts,5);
+ stream.read((char *)x.p.dts,5);
+ x.p.length -= 10;
+ x.p.pes_hlength += 10;
+ }
+ } else {
+ x.p.mpeg1_headr = new uint8_t[x.p.mpeg1_pad];
+ x.p.pes_hlength = x.p.mpeg1_pad;
+ stream.read((char *)x.p.mpeg1_headr,x.p.mpeg1_pad);
+ }
+ }
+ stream.read((char *)x.p.pes_pckt_data,x.p.length);
+ }
+ return stream;
+}
+
+ostream & operator << (ostream & stream, TS_Packet & x){
+
+ uint8_t buf[TS_SIZE];
+ int length = cwrite_ts(buf,&(x.p),TS_SIZE);
+ stream.write((char *)buf,length);
+
+ return stream;
+}
+
+istream & operator >> (istream & stream, TS_Packet & x){
+ uint8_t sync;
+ int found=0;
+ streampos p,q;
+
+ sync=0;
+ while (!stream.eof() && !found) {
+ stream.read((char *)&sync,1);
+ if (sync == 0x47)
+ found = 1;
+ }
+ stream.read((char *)x.p.pid,2);
+ stream.read((char *)&x.p.flags,1);
+ x.p.count = x.p.flags & COUNT_MASK;
+
+ if (!(x.p.flags & ADAPT_FIELD) && (x.p.flags & PAYLOAD)){
+ //no adapt. field only payload
+ stream.read((char *)x.p.data,184);
+ x.p.rest = 184;
+ return stream;
+ }
+
+ if ( x.p.flags & ADAPT_FIELD ) {
+ // adaption field
+ stream.read((char *)&x.p.adapt_length,1);
+ if (x.p.adapt_length){
+ p = stream.tellg();
+ stream.read((char *)&x.p.adapt_flags,1);
+
+ if ( x.p.adapt_flags & PCR_FLAG )
+ stream.read((char *) x.p.pcr,6);
+
+ if ( x.p.adapt_flags & OPCR_FLAG )
+ stream.read((char *) x.p.opcr,6);
+
+ if ( x.p.adapt_flags & SPLICE_FLAG )
+ stream.read((char *) &x.p.splice_count,1);
+
+ if( x.p.adapt_flags & TRANS_PRIV){
+ stream.read((char *)&x.p.priv_dat_len,1);
+ x.p.priv_dat = new uint8_t[x.p.priv_dat_len];
+ stream.read((char *)x.p.priv_dat,x.p.priv_dat_len);
+ }
+
+ if( x.p.adapt_flags & ADAP_EXT_FLAG){
+ stream.read((char *)&x.p.adapt_ext_len,1);
+ stream.read((char *)&x.p.adapt_eflags,1);
+ if( x.p.adapt_eflags & LTW_FLAG)
+ stream.read((char *)x.p.ltw,2);
+
+ if( x.p.adapt_eflags & PIECE_RATE)
+ stream.read((char *)x.p.piece_rate,3);
+
+ if( x.p.adapt_eflags & SEAM_SPLICE)
+ stream.read((char *)x.p.dts,5);
+ }
+ q = stream.tellg();
+ x.p.stuffing = x.p.adapt_length -(q-p);
+ x.p.rest = 183-x.p.adapt_length;
+ stream.seekg(q+streampos(x.p.stuffing));
+ if (x.p.flags & PAYLOAD) // payload
+ stream.read((char *)x.p.data,x.p.rest);
+ else
+ stream.seekg(q+streampos(x.p.rest));
+ } else {
+ x.p.rest = 182;
+ stream.read((char *)x.p.data, 183);
+ return stream;
+ }
+
+ }
+ return stream;
+}
+
+
+ostream & operator << (ostream & stream, PS_Packet & x){
+
+ uint8_t buf[PS_MAX];
+ int length = cwrite_ps(buf,&(x.p),PS_MAX);
+ stream.write((char *)buf,length);
+
+ return stream;
+}
+
+istream & operator >> (istream & stream, PS_Packet & x){
+ uint8_t headr[4];
+ int found=0;
+ streampos p = 0;
+ streampos q = 0;
+ int count = 0;
+
+ p = stream.tellg();
+ while (!stream.eof() && !found && count < MAX_SEARCH) {
+ stream.read((char *)&headr,4);
+ if (headr[0] == 0x00 && headr[1] == 0x00 && headr[2] == 0x01)
+ if ( headr[3] == 0xBA )
+ found = 1;
+ else if ( headr[3] == 0xB9 ) break;
+ else stream.seekg(p+streampos(1));
+ count++;
+ }
+
+ if (found){
+ stream.read((char *)x.p.scr,6);
+ if (x.p.scr[0] & 0x40)
+ x.p.mpeg = 2;
+ else
+ x.p.mpeg = 1;
+
+ if (x.p.mpeg == 2){
+ stream.read((char *)x.p.mux_rate,3);
+ stream.read((char *)&x.p.stuff_length,1);
+ p = stream.tellg();
+ stream.seekg(p+streampos(x.p.stuff_length & 3));
+ } else {
+ x.p.mux_rate[0] = x.p.scr[5]; //mpeg1 scr is only 5 bytes
+ stream.read((char *)x.p.mux_rate+1,2);
+ }
+
+ p=stream.tellg();
+ stream.read((char *)headr,4);
+ if (headr[0] == 0x00 && headr[1] == 0x00 &&
+ headr[2] == 0x01 && headr[3] == 0xBB ) {
+ stream.read((char *)x.p.sheader_llength,2);
+ x.setlength();
+ if (x.p.mpeg == 2){
+ stream.read((char *)x.p.rate_bound,3);
+ stream.read((char *)&x.p.audio_bound,1);
+ stream.read((char *)&x.p.video_bound,1);
+ stream.read((char *)&x.p.reserved,1);
+ }
+ stream.read((char *)x.p.data,x.p.sheader_length);
+ } else {
+ stream.seekg(p);
+ x.p.sheader_length = 0;
+ }
+
+ int i = 0;
+ int done = 0;
+ q = stream.tellg();
+ PES_Packet pes;
+ do {
+ p=stream.tellg();
+ stream.read((char *)headr,4);
+ stream.seekg(p);
+ if ( headr[0] == 0x00 && headr[1] == 0x00
+ && headr[2] == 0x01 && headr[3] != 0xBA){
+ pes.init();
+ stream >> pes;
+ i++;
+ } else done = 1;
+ } while (!stream.eof() && !done);
+ x.p.npes = i;
+ stream.seekg(q);
+ }
+ return stream;
+}
+
+void extract_audio_from_PES(istream &in, ostream &out){
+ PES_Packet pes;
+
+ while(!in.eof()){
+ pes.init();
+ in >> pes ;
+ if (pes.Stream_ID() == 0xC0)
+ out << pes;
+ }
+}
+
+void extract_video_from_PES(istream &in, ostream &out){
+ PES_Packet pes;
+
+ while(!in.eof()){
+ pes.init();
+ in >> pes ;
+ if (pes.Stream_ID() == 0xE0)
+ out << pes;
+ }
+}
+
+void extract_es_audio_from_PES(istream &in, ostream &out){
+ PES_Packet pes;
+
+ while(!in.eof()){
+ pes.init();
+ in >> pes ;
+ if (pes.Stream_ID() == 0xC0)
+ out.write((char *)pes.Data(),pes.Length());
+ }
+}
+
+void extract_es_video_from_PES(istream &in, ostream &out){
+ PES_Packet pes;
+
+ while(!in.eof()){
+ pes.init();
+ in >> pes ;
+ if (pes.Stream_ID() == 0xE0)
+ out.write((char *)pes.Data(),pes.Length());
+ }
+}
+
+
+
+#define MAX_PID 20
+int TS_PIDS(istream &in, ostream &out){
+ int pid[MAX_PID];
+ TS_Packet ts;
+ int npid=0;
+
+ for (int i=0 ; i<MAX_PID; i++)
+ pid[i] = -1;
+ while (!in.eof()) {
+ ts.init();
+ in >> ts;
+ int j;
+ int found = 0;
+ for (j=0;j<npid;j++){
+ if ( ts.PID() == pid[j] )
+ found = 1;
+ }
+ if (! found){
+ out << ts.PID() << " ";
+ pid[npid] = ts.PID();
+ npid++;
+ if (npid == MAX_PID) return -1;
+ }
+ }
+ out << endl;
+ return 0;
+}
+
+int tv_norm(istream &stream){
+ uint8_t headr[4];
+ int found=0;
+ streampos p = 0;
+ streampos q = 0;
+ int hsize,vsize;
+ int form= 0;
+
+ q = stream.tellg();
+ while (!stream.eof() && !found) {
+ p = stream.tellg();
+ stream.read((char *)headr,4);
+ if (headr[0] == 0x00 && headr[1] == 0x00 && headr[2] == 0x01)
+ if ( headr[3] == 0xB3 ){
+ found = 1;
+ }
+ if (! found) stream.seekg(p+streampos(1));
+ }
+ stream.read((char *)headr,4);
+
+ hsize = (headr[1] &0xF0) >> 4 | headr[0] << 4;
+ vsize = (headr[1] &0x0F) << 8 | headr[2];
+ cerr << "SIZE: " << hsize << "x" << vsize << endl;
+
+ switch(((headr[3]&0xF0) >>4)){
+ case 1:
+ cerr << "ASPECT: 1:1" << endl;
+ break;
+ case 2:
+ cerr << "ASPECT: 4:3" << endl;
+ break;
+ case 3:
+ cerr << "ASPECT: 16:9" << endl;
+ break;
+ case 4:
+ cerr << "ASPECT: 2.21:1" << endl;
+ break;
+ }
+
+ switch (int(headr[3]&0x0F)){
+ case 1:
+ cerr << "FRAMERATE: 23.976" << endl;
+ form = pDUNNO;
+ break;
+ case 2:
+ cerr << "FRAMERATE: 24" << endl;
+ form = pDUNNO;
+ break;
+ case 3:
+ cerr << "FRAMERATE: 25" << endl;
+ form = pPAL;
+ break;
+ case 4:
+ cerr << "FRAMERATE: 29.97" << endl;
+ form = pNTSC;
+ break;
+ case 5:
+ cerr << "FRAMERATE: 30" << endl;
+ form = pNTSC;
+ break;
+ case 6:
+ cerr << "FRAMERATE: 50" << endl;
+ form = pPAL;
+ break;
+ case 7:
+ cerr << "FRAMERATE: 59.94" << endl;
+ form = pNTSC;
+ break;
+ case 8:
+ cerr << "FRAMERATE: 60" << endl;
+ form = pNTSC;
+ break;
+ }
+
+ int mpeg = 0;
+ found = 0;
+ while (!stream.eof() && !found) {
+ p = stream.tellg();
+ stream.read((char *)headr,4);
+ if (headr[0] == 0x00 && headr[1] == 0x00 && headr[2] == 0x01)
+ if ( headr[3] == 0xB5 ){
+ char *profile[] = {"reserved", "High", "Spatially Scalable",
+ "SNR Scalable", "Main", "Simple", "undef",
+ "undef"};
+ char *level[] = {"res", "res", "res", "res",
+ "High","res", "High 1440", "res",
+ "Main","res", "Low", "res",
+ "res", "res", "res", "res"};
+ char *chroma[] = {"res", "4:2:0", "4:2:2", "4:4:4:"};
+ mpeg = 2;
+ stream.read((char *)headr,4);
+ cerr << "PROFILE: " << profile[headr[0] & 0x7] << endl;
+ cerr << "LEVEL: " << level[headr[1]>>4 & 0xF] << endl;
+ cerr << "CHROMA: " << chroma[headr[1]>>1 & 0x3] << endl;
+ found = 1;
+ } else {
+ mpeg = 1;
+ found = 1;
+ }
+ if (! found) stream.seekg(p+streampos(1));
+ }
+
+ stream.seekg(q);
+ return (form | mpeg << 4);
+}
+
+
+
+int stream_type(istream &fin){
+ uint8_t headr[4];
+ streampos p=fin.tellg();
+
+ TS_Packet ts;
+ fin >> ts;
+ fin.read((char *)headr,1);
+ fin.seekg(p);
+ if(fin && headr[0] == 0x47){
+ return TS_STREAM;
+ }
+
+ PS_Packet ps;
+ fin >> ps;
+ PES_Packet pes;
+ for(int j=0;j < ps.NPES();j++){
+ fin >> pes;
+ }
+ fin.read((char *)headr,4);
+ fin.seekg(p);
+ if (fin && headr[0] == 0x00 && headr[1] == 0x00
+ && headr[2] == 0x01 && headr[3] == 0xBA){
+ return PS_STREAM;
+ }
+
+ fin >> pes;
+ fin.read((char *)!headr,4);
+ fin.seekg(p);
+ if (fin && headr[0] == 0x00 && headr[1] == 0x00
+ && headr[2] == 0x01 ){
+ int found = 0;
+ switch ( headr[3] ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ found = 1;
+ break;
+ }
+ if (found){
+ return PES_STREAM;
+ }
+ }
+
+
+
+ return -1;
+}
+
+
+void analyze(istream &fin)
+{
+ PS_Packet ps;
+ PES_Packet pes;
+ int fc = 0;
+ char *frames[3] = {"I-Frame","P-Frame","B-Frame"};
+
+ while(fin){
+ uint32_t pts;
+ fin >> ps;
+ cerr << "SCR base: " << hex << setw(5)
+ << setfill('0') \
+ << ps.SCR_base() << " " << dec
+ << "ext : " << ps.SCR_ext();
+
+ cerr << " MUX rate: " << ps.MUX()*50*8/1000000.0
+ << " Mbit/s ";
+ cerr << "RATE bound: " << ps.Rate()*50*8/1000000.0
+ << " Mbit/s" << endl;
+ cerr << " Audio bound: "
+ << hex << "0x"
+ << int(ps.P()->audio_bound);
+ cerr << " Video bound: " << hex << "0x"
+ << int(ps.P()->video_bound)
+ << dec
+ << endl;
+ cerr << endl;
+
+ for (int i=0; i < ps.NPES(); i++){
+ pes.init();
+ fin >> pes;
+ pts2pts((uint8_t *)&pts,pes.PTS());
+ pes.Info() = 1;
+ cerr << pes;
+
+ uint8_t *buf = pes.P()->pes_pckt_data;
+ int c = 0;
+ int l;
+ switch (pes.P()->stream_id){
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ l=pes.P()->length;
+ break;
+ default:
+ l = 0;
+ break;
+ }
+ while ( c < l - 6){
+ if (buf[c] == 0x00 &&
+ buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01 &&
+ buf[c+3] == 0xB8) {
+ c += 4;
+ cerr << "TIME hours: "
+ << int((buf[c]>>2)& 0x1F)
+ << " minutes: "
+ << int(((buf[c]<<4)& 0x30)|
+ ((buf[c+1]>>4)& 0x0F))
+ << " seconds: "
+ << int(((buf[c+1]<<3)& 0x38)|
+ ((buf[c+2]>>5)& 0x0F))
+ << endl;
+ }
+
+ if ( buf[c] == 0x00 &&
+ buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01 &&
+ buf[c+3] == 0x00) {
+ fc++;
+ c += 4;
+ cerr << "picture: "
+ << fc
+ << " ("
+ << frames[((buf[c+1]&0x38) >>3)-1]
+ << ")" << endl << endl;
+ } else c++;
+ }
+ }
+ }
+}
+
+
diff --git a/libdvbmpeg/cpptools.hh b/libdvbmpeg/cpptools.hh
new file mode 100644
index 0000000..49ea5de
--- /dev/null
+++ b/libdvbmpeg/cpptools.hh
@@ -0,0 +1,330 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de,
+ */
+
+#include <fstream>
+#include <sstream>
+#include <iostream>
+#include <iomanip>
+using namespace std;
+
+
+#ifndef _CPPTOOLS_HH_
+#define _CPPTOOLS_HH_
+
+#include "ctools.h"
+
+
+class PES_Packet{
+ int info;
+ pes_packet p;
+public:
+ PES_Packet(){
+ info = 0;
+ init_pes(&p);
+ }
+
+ ~PES_Packet(){
+ if (p.pack_header)
+ delete [] p.pack_header;
+ if (p.pes_ext)
+ delete [] p.pes_ext;
+ if (p.pes_pckt_data)
+ delete [] p.pes_pckt_data;
+ if (p.mpeg1_headr)
+ delete [] p.mpeg1_headr;
+ }
+
+ inline void init(){
+ if (p.pack_header)
+ delete [] p.pack_header;
+ if (p.pes_ext)
+ delete [] p.pes_ext;
+ if (p.pes_pckt_data)
+ delete [] p.pes_pckt_data;
+ if (p.mpeg1_headr)
+ delete [] p.mpeg1_headr;
+
+ info = 0;
+ init_pes(&p);
+ }
+
+ inline pes_packet *P(){
+ return &p;
+ }
+
+ inline void setlength(){
+ setlength_pes(&p);
+ if (p.length)
+ p.pes_pckt_data = new uint8_t[p.length];
+ }
+
+ inline void Nlength(){
+ nlength_pes(&p);
+ p.pes_pckt_data = new uint8_t[p.length];
+ }
+
+
+ inline uint8_t &Stream_ID(){
+ return p.stream_id;
+ }
+
+ inline uint8_t &Flags1(){
+ return p.flags1;
+ }
+
+ inline uint8_t &Flags2(){
+ return p.flags2;
+ }
+
+ inline uint32_t &Length(){
+ return p.length;
+ }
+
+ inline uint8_t &HLength(){
+ return p.pes_hlength;
+ }
+
+ inline uint8_t &Stuffing(){
+ return p.stuffing;
+ }
+
+ inline uint8_t *Data(){
+ return p.pes_pckt_data;
+ }
+
+ inline int has_pts(){
+ return (p.flags2 & PTS_DTS);
+ }
+
+ inline int &MPEG(){
+ return p.mpeg;
+ }
+ inline uint8_t *PTS(){
+ return p.pts;
+ }
+
+ inline uint8_t *DTS(){
+ return p.dts;
+ }
+
+ inline int &Info(){
+ return info;
+ }
+
+
+
+ inline uint8_t high_pts(){
+ if (has_pts())
+ return ((p.pts[0] & 0x08)>>3);
+ else
+ return 0;
+ }
+
+ inline uint8_t high_dts(){
+ return ((p.dts[0] & 0x08)>>3);
+ }
+
+ inline int WDTS(){
+ int w_dts;
+ w_dts = (int)trans_pts_dts(p.dts);
+ return w_dts;
+ }
+
+ inline int WPTS(){
+ int w_dts;
+ w_dts = (int)trans_pts_dts(p.pts);
+ return w_dts;
+ }
+
+ friend ostream & operator << (ostream & stream, PES_Packet & x);
+ friend istream & operator >> (istream & stream, PES_Packet & x);
+
+};
+
+
+class TS_Packet{
+ ts_packet p;
+ int info;
+
+public:
+ TS_Packet(){
+ init_ts(&p);
+ info = 0;
+ }
+
+ ~TS_Packet(){
+ if (p.priv_dat)
+ delete [] p.priv_dat;
+ }
+
+ inline void init(){
+ if (p.priv_dat)
+ delete [] p.priv_dat;
+
+ init_ts(&p);
+ info = 0;
+ }
+
+ inline ts_packet *P(){
+ return &p;
+ }
+
+ inline int &Rest(){
+ return p.rest;
+ }
+
+ inline uint8_t *Data(){
+ return p.data;
+ }
+
+ inline short PID(){
+ return pid_ts(&p);
+ }
+
+ inline uint8_t FLAG1(){
+ return (p.pid[0] & ~PID_MASK_HI);
+ }
+
+ inline int &Info(){
+ return info;
+ }
+
+ friend ostream & operator << (ostream & stream, TS_Packet & x);
+ friend istream & operator >> (istream & stream, TS_Packet & x);
+};
+
+
+class PS_Packet{
+ int info;
+ ps_packet p;
+public:
+
+ PS_Packet(){
+ init_ps(&p);
+ info = 0;
+ }
+
+ ~PS_Packet(){
+ if (p.data)
+ delete [] p.data;
+ }
+
+ inline void init(){
+ if (p.data)
+ delete [] p.data;
+
+ init_ps(&p);
+ info = 0;
+ }
+
+ inline ps_packet *P(){
+ return &p;
+ }
+
+ inline int MUX(){
+ return mux_ps(&p);
+ }
+
+ inline int Rate(){
+ return rate_ps(&p);
+ }
+
+ inline void setlength(){
+ setlength_ps(&p);
+ p.data = new uint8_t[p.sheader_length];
+ }
+
+ inline int Stuffing(){
+ return p.stuff_length & PACK_STUFF_MASK;
+ }
+
+ inline int NPES(){
+ return p.npes;
+ }
+
+ inline int &MPEG(){
+ return p.mpeg;
+ }
+
+ inline uint8_t &operator()(int l){
+ return p.data[l];
+ }
+
+ inline char * Data() {
+ return (char *)p.data+p.stuff_length;
+ }
+
+ inline int &SLENGTH(){
+ return p.sheader_length;
+ }
+
+ inline int &Info(){
+ return info;
+ }
+
+ uint32_t SCR_base(){
+ return scr_base_ps(&p);
+ }
+
+ uint16_t SCR_ext(){
+ return scr_ext_ps(&p);
+ }
+
+ friend ostream & operator << (ostream & stream, PS_Packet & x);
+ friend istream & operator >> (istream & stream, PS_Packet & x);
+};
+
+
+typedef void (* FILTER)(istream &in, ostream &out);
+
+typedef struct thread_args_{
+ FILTER function;
+ int *fd;
+ int in;
+ int out;
+} thread_args;
+
+
+void extract_audio_from_PES(istream &in, ostream &out);
+void extract_video_from_PES(istream &in, ostream &out);
+void extract_es_audio_from_PES(istream &in, ostream &out);
+void extract_es_video_from_PES(istream &in, ostream &out);
+int TS_PIDS(istream &in, ostream &out);
+int ifilter (istream &in, FILTER function);
+int ofilter (istream &in, FILTER function);
+int itfilter (int in, FILTER function);
+int otfilter (istream &in, FILTER function);
+int stream_type(int fd);
+int stream_type(istream &stream);
+int tv_norm(istream &fin);
+
+void analyze(istream &fin);
+
+
+#endif //_CPPTOOLS_HH_
+
diff --git a/libdvbmpeg/ctools.c b/libdvbmpeg/ctools.c
new file mode 100644
index 0000000..dfd1751
--- /dev/null
+++ b/libdvbmpeg/ctools.c
@@ -0,0 +1,2379 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de,
+ */
+
+#include "ctools.h"
+
+#define MAX_SEARCH 1024 * 1024
+
+
+/*
+
+ PES
+
+*/
+
+ssize_t save_read(int fd, void *buf, size_t count)
+{
+ ssize_t neof = 1;
+ size_t re = 0;
+
+ while(neof >= 0 && re < count){
+ neof = read(fd, buf+re, count - re);
+ if (neof > 0) re += neof;
+ else break;
+ }
+
+ if (neof < 0 && re == 0) return neof;
+ else return re;
+}
+
+void init_pes(pes_packet *p){
+ p->stream_id = 0;
+ p->llength[0] = 0;
+ p->llength[1] = 0;
+ p->length = 0;
+ p->flags1 = 0x80;
+ p->flags2 = 0;
+ p->pes_hlength = 0;
+ p->trick = 0;
+ p->add_cpy = 0;
+ p->priv_flags = 0;
+ p->pack_field_length = 0;
+ p->pack_header = (uint8_t *) NULL;
+ p->pck_sqnc_cntr = 0;
+ p->org_stuff_length = 0;
+ p->pes_ext_lngth = 0;
+ p->pes_ext = (uint8_t *) NULL;
+ p->pes_pckt_data = (uint8_t *) NULL;
+ p->padding = 0;
+ p->mpeg = 2; // DEFAULT MPEG2
+ p->mpeg1_pad = 0;
+ p->mpeg1_headr = NULL;
+ p->stuffing = 0;
+}
+
+void kill_pes(pes_packet *p){
+ if (p->pack_header)
+ free(p->pack_header);
+ if (p->pes_ext)
+ free(p->pes_ext);
+ if (p->pes_pckt_data)
+ free(p->pes_pckt_data);
+ if (p->mpeg1_headr)
+ free(p->mpeg1_headr);
+ init_pes(p);
+}
+
+void setlength_pes(pes_packet *p){
+ short *ll;
+ ll = (short *) p->llength;
+ p->length = ntohs(*ll);
+}
+
+static void setl_pes(pes_packet *p){
+ setlength_pes(p);
+ if (p->length)
+ p->pes_pckt_data = (uint8_t *)malloc(p->length);
+}
+
+void nlength_pes(pes_packet *p){
+ if (p->length <= 0xFFFF){
+ short *ll = (short *) p->llength;
+ short l = p->length;
+ *ll = htons(l);
+ } else {
+ p->llength[0] =0x00;
+ p->llength[1] =0x00;
+ }
+}
+
+static void nl_pes(pes_packet *p)
+{
+ nlength_pes(p);
+ p->pes_pckt_data = (uint8_t *) malloc(p->length);
+}
+
+void pts2pts(uint8_t *av_pts, uint8_t *pts)
+{
+
+ av_pts[0] = ((pts[0] & 0x06) << 5) |
+ ((pts[1] & 0xFC) >> 2);
+ av_pts[1] = ((pts[1] & 0x03) << 6) |
+ ((pts[2] & 0xFC) >> 2);
+ av_pts[2] = ((pts[2] & 0x02) << 6) |
+ ((pts[3] & 0xFE) >> 1);
+ av_pts[3] = ((pts[3] & 0x01) << 7) |
+ ((pts[4] & 0xFE) >> 1);
+
+}
+
+
+int cwrite_pes(uint8_t *buf, pes_packet *p, long length){
+ int count,i;
+ uint8_t dummy;
+ int more = 0;
+ uint8_t headr[3] = { 0x00, 0x00 , 0x01};
+
+ if (length < p->length+p->pes_hlength){
+ fprintf(stderr,"Wrong buffer size in cwrite_pes\n");
+ exit(1);
+ }
+
+
+ memcpy(buf,headr,3);
+ count = 3;
+ buf[count] = p->stream_id;
+ count++;
+
+ switch ( p->stream_id ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ buf[count] = p->llength[0];
+ count++;
+ buf[count] = p->llength[1];
+ count++;
+ memcpy(buf+count,p->pes_pckt_data,p->length);
+ count += p->length;
+ break;
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ buf[count] = p->llength[0];
+ count++;
+ buf[count] = p->llength[1];
+ count++;
+ more = 1;
+ break;
+ }
+
+
+ if ( more ) {
+ if ( p->mpeg == 2 ){
+ memcpy(buf+count,&p->flags1,1);
+ count++;
+ memcpy(buf+count,&p->flags2,1);
+ count++;
+ memcpy(buf+count,&p->pes_hlength,1);
+ count++;
+
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_ONLY){
+ memcpy(buf+count,p->pts,5);
+ count += 5;
+ } else
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_DTS){
+ memcpy(buf+count,p->pts,5);
+ count += 5;
+ memcpy(buf+count,p->dts,5);
+ count += 5;
+ }
+ if (p->flags2 & ESCR_FLAG){
+ memcpy(buf+count,p->escr,6);
+ count += 6;
+ }
+ if (p->flags2 & ES_RATE_FLAG){
+ memcpy(buf+count,p->es_rate,3);
+ count += 3;
+ }
+ if (p->flags2 & DSM_TRICK_FLAG){
+ memcpy(buf+count,&p->trick,1);
+ count++;
+ }
+ if (p->flags2 & ADD_CPY_FLAG){
+ memcpy(buf+count,&p->add_cpy,1);
+ count++;
+ }
+ if (p->flags2 & PES_CRC_FLAG){
+ memcpy(buf+count,p->prev_pes_crc,2);
+ count += 2;
+ }
+ if (p->flags2 & PES_EXT_FLAG){
+ memcpy(buf+count,&p->priv_flags,1);
+ count++;
+
+ if (p->priv_flags & PRIVATE_DATA){
+ memcpy(buf+count,p->pes_priv_data,16);
+ count += 16;
+ }
+ if (p->priv_flags & HEADER_FIELD){
+ memcpy(buf+count,&p->pack_field_length,
+ 1);
+ count++;
+ memcpy(buf+count,p->pack_header,
+ p->pack_field_length);
+ count += p->pack_field_length;
+
+ }
+
+ if ( p->priv_flags & PACK_SEQ_CTR){
+ memcpy(buf+count,&p->pck_sqnc_cntr,1);
+ count++;
+ memcpy(buf+count,&p->org_stuff_length,
+ 1);
+ count++;
+ }
+
+ if ( p->priv_flags & P_STD_BUFFER){
+ memcpy(buf+count,p->p_std,2);
+ count += 2;
+ }
+ if ( p->priv_flags & PES_EXT_FLAG2){
+ memcpy(buf+count,&p->pes_ext_lngth,1);
+ count++;
+ memcpy(buf+count,p->pes_ext,
+ p->pes_ext_lngth);
+ count += p->pes_ext_lngth;
+ }
+ }
+ dummy = 0xFF;
+ for (i=0;i<p->stuffing;i++) {
+ memcpy(buf+count,&dummy,1);
+ count++;
+ }
+ } else {
+ if (p->mpeg1_pad){
+ memcpy(buf+count,p->mpeg1_headr,p->mpeg1_pad);
+ count += p->mpeg1_pad;
+ }
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_ONLY){
+ memcpy(buf+count,p->pts,5);
+ count += 5;
+ }
+ else if ((p->flags2 & PTS_DTS_FLAGS) ==
+ PTS_DTS){
+ memcpy(buf+count,p->pts,5);
+ count += 5;
+ memcpy(buf+count,p->dts,5);
+ count += 5;
+ }
+ }
+ memcpy(buf+count,p->pes_pckt_data,p->length);
+ count += p->length;
+ }
+
+ return count;
+
+}
+
+void write_pes(int fd, pes_packet *p){
+ long length;
+ uint8_t *buf;
+ int l = p->length+p->pes_hlength;
+
+ buf = (uint8_t *) malloc(l);
+ length = cwrite_pes(buf,p,l);
+ write(fd,buf,length);
+ free(buf);
+}
+
+static unsigned int find_length(int f){
+ uint64_t p = 0;
+ uint64_t start = 0;
+ uint64_t q = 0;
+ int found = 0;
+ uint8_t sync4[4];
+ int neof = 1;
+
+ start = lseek(f,0,SEEK_CUR);
+ start -=2;
+ lseek(f,start,SEEK_SET);
+ while ( neof > 0 && !found ){
+ p = lseek(f,0,SEEK_CUR);
+ neof = save_read(f,&sync4,4);
+ if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
+ switch ( sync4[3] ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ found = 1;
+ break;
+ default:
+ q = lseek(f,0,SEEK_CUR);
+ break;
+ }
+ }
+ }
+ q = lseek(f,0,SEEK_CUR);
+ lseek(f,start+2,SEEK_SET);
+ if (found) return (unsigned int)(q-start)-4-2;
+ else return (unsigned int)(q-start-2);
+
+}
+
+
+void cread_pes(char *buf, pes_packet *p){
+
+ uint8_t count, dummy, check;
+ int i;
+ uint64_t po = 0;
+ int c=0;
+
+ switch ( p->stream_id ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ memcpy(p->pes_pckt_data,buf+c,p->length);
+ return;
+ break;
+ case PADDING_STREAM :
+ p->padding = p->length;
+ memcpy(p->pes_pckt_data,buf+c,p->length);
+ return;
+ break;
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ break;
+ default:
+ return;
+ break;
+ }
+
+ po = c;
+ memcpy(&p->flags1,buf+c,1);
+ c++;
+ if ( (p->flags1 & 0xC0) == 0x80 ) p->mpeg = 2;
+ else p->mpeg = 1;
+
+ if ( p->mpeg == 2 ){
+ memcpy(&p->flags2,buf+c,1);
+ c++;
+ memcpy(&p->pes_hlength,buf+c,1);
+ c++;
+
+ p->length -=p->pes_hlength+3;
+ count = p->pes_hlength;
+
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_ONLY){
+ memcpy(p->pts,buf+c,5);
+ c += 5;
+ count -=5;
+ } else
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_DTS){
+ memcpy(p->pts,buf+c,5);
+ c += 5;
+ memcpy(p->dts,buf+c,5);
+ c += 5;
+ count -= 10;
+ }
+
+ if (p->flags2 & ESCR_FLAG){
+ memcpy(p->escr,buf+c,6);
+ c += 6;
+ count -= 6;
+ }
+
+ if (p->flags2 & ES_RATE_FLAG){
+ memcpy(p->es_rate,buf+c,3);
+ c += 3;
+ count -= 3;
+ }
+
+ if (p->flags2 & DSM_TRICK_FLAG){
+ memcpy(&p->trick,buf+c,1);
+ c += 1;
+ count -= 1;
+ }
+
+ if (p->flags2 & ADD_CPY_FLAG){
+ memcpy(&p->add_cpy,buf+c,1);
+ c++;
+ count -= 1;
+ }
+
+ if (p->flags2 & PES_CRC_FLAG){
+ memcpy(p->prev_pes_crc,buf+c,2);
+ c += 2;
+ count -= 2;
+ }
+
+ if (p->flags2 & PES_EXT_FLAG){
+ memcpy(&p->priv_flags,buf+c,1);
+ c++;
+ count -= 1;
+
+ if (p->priv_flags & PRIVATE_DATA){
+ memcpy(p->pes_priv_data,buf+c,16);
+ c += 16;
+ count -= 16;
+ }
+
+ if (p->priv_flags & HEADER_FIELD){
+ memcpy(&p->pack_field_length,buf+c,1);
+ c++;
+ p->pack_header = (uint8_t *)
+ malloc(p->pack_field_length);
+ memcpy(p->pack_header,buf+c,
+ p->pack_field_length);
+ c += p->pack_field_length;
+ count -= 1+p->pack_field_length;
+ }
+
+ if ( p->priv_flags & PACK_SEQ_CTR){
+ memcpy(&p->pck_sqnc_cntr,buf+c,1);
+ c++;
+ memcpy(&p->org_stuff_length,buf+c,1);
+ c++;
+ count -= 2;
+ }
+
+ if ( p->priv_flags & P_STD_BUFFER){
+ memcpy(p->p_std,buf+c,2);
+ c += 2;
+ count -= 2;
+ }
+
+ if ( p->priv_flags & PES_EXT_FLAG2){
+ memcpy(&p->pes_ext_lngth,buf+c,1);
+ c++;
+ p->pes_ext = (uint8_t *)
+ malloc(p->pes_ext_lngth);
+ memcpy(p->pes_ext,buf+c,
+ p->pes_ext_lngth);
+ c += p->pes_ext_lngth;
+ count -= 1+p->pes_ext_lngth;
+ }
+ }
+ p->stuffing = count;
+ for(i = 0; i< count ;i++){
+ memcpy(&dummy,buf+c,1);
+ c++;
+ }
+ } else {
+ p->mpeg1_pad = 1;
+ check = p->flags1;
+ while (check == 0xFF){
+ memcpy(&check,buf+c,1);
+ c++;
+ p->mpeg1_pad++;
+ }
+
+ if ( (check & 0xC0) == 0x40){
+ memcpy(&check,buf+c,1);
+ c++;
+ p->mpeg1_pad++;
+ memcpy(&check,buf+c,1);
+ c++;
+ p->mpeg1_pad++;
+ }
+ p->flags2 = 0;
+ p->length -= p->mpeg1_pad;
+
+ c = po;
+ if ( (check & 0x30)){
+ p->length ++;
+ p->mpeg1_pad --;
+
+ if (check == p->flags1){
+ p->pes_hlength = 0;
+ } else {
+ p->mpeg1_headr = (uint8_t *)
+ malloc(p->mpeg1_pad);
+ p->pes_hlength = p->mpeg1_pad;
+ memcpy(p->mpeg1_headr,buf+c,
+ p->mpeg1_pad);
+ c += p->mpeg1_pad;
+ }
+
+ p->flags2 = (check & 0xF0) << 2;
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_ONLY){
+ memcpy(p->pts,buf+c,5);
+ c += 5;
+ p->length -= 5;
+ p->pes_hlength += 5;
+ }
+ else if ((p->flags2 & PTS_DTS_FLAGS) ==
+ PTS_DTS){
+ memcpy(p->pts,buf+c,5);
+ c += 5;
+ memcpy(p->dts,buf+c,5);
+ c += 5;
+ p->length -= 10;
+ p->pes_hlength += 10;
+ }
+ } else {
+ p->mpeg1_headr = (uint8_t *) malloc(p->mpeg1_pad);
+ p->pes_hlength = p->mpeg1_pad;
+ memcpy(p->mpeg1_headr,buf+c,
+ p->mpeg1_pad);
+ c += p->mpeg1_pad;
+ }
+ }
+ memcpy(p->pes_pckt_data,buf+c,p->length);
+}
+
+
+int read_pes(int f, pes_packet *p){
+
+ uint8_t sync4[4];
+ int found=0;
+ uint64_t po = 0;
+ int neof = 1;
+ uint8_t *buf;
+
+ while (neof > 0 && !found) {
+ po = lseek(f,0,SEEK_CUR);
+ if (po < 0) return -1;
+ if ((neof = save_read(f,&sync4,4)) < 4) return -1;
+ if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
+ p->stream_id = sync4[3];
+ switch ( sync4[3] ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ if((neof = save_read(f,p->llength,2)) < 2)
+ return -1;
+ setl_pes(p);
+ if (!p->length){
+ p->length = find_length(f);
+ nl_pes(p);
+ }
+ found = 1;
+ break;
+
+ default:
+ if (lseek(f,po+1,SEEK_SET) < po+1) return -1;
+ break;
+ }
+ } else if(lseek(f,po+1,SEEK_SET) < po+1) return -1;
+ }
+
+ if (!found || !p->length) return 0;
+
+ if (p->length >0){
+ buf = (uint8_t *) malloc(p->length);
+ if((neof = save_read(f,buf,p->length))< p->length) return -1;
+ cread_pes((char *)buf,p);
+ free(buf);
+ } else return 0;
+
+ return neof;
+}
+
+/*
+
+ Transport Stream
+
+*/
+
+void init_ts(ts_packet *p){
+ p->pid[0] = 0;
+ p->pid[1] = 0;
+ p->flags = 0;
+ p->count = 0;
+ p->adapt_length = 0;
+ p->adapt_flags = 0;
+ p->splice_count = 0;
+ p->priv_dat_len = 0;
+ p->priv_dat = NULL;
+ p->adapt_ext_len = 0;
+ p->adapt_eflags = 0;
+ p->rest = 0;
+ p->stuffing = 0;
+}
+
+void kill_ts(ts_packet *p){
+ if (p->priv_dat)
+ free(p->priv_dat);
+ init_ts(p);
+}
+
+
+
+unsigned short pid_ts(ts_packet *p)
+{
+ return get_pid(p->pid);
+}
+
+int cwrite_ts(uint8_t *buf, ts_packet *p, long length){
+ long count,i;
+ uint8_t sync,dummy;
+
+ sync = 0x47;
+ memcpy(buf,&sync,1);
+ count = 1;
+ memcpy(buf+count,p->pid,2);
+ count += 2;
+ memcpy(buf+count,&p->flags,1);
+ count++;
+
+
+ if (! (p->flags & ADAPT_FIELD) && (p->flags & PAYLOAD)){
+ memcpy(buf+count,p->data,184);
+ count += 184;
+ } else {
+ memcpy(buf+count,&p->adapt_length,1);
+ count++;
+ memcpy(buf+count,&p->adapt_flags,1);
+ count++;
+
+ if ( p->adapt_flags & PCR_FLAG ){
+ memcpy(buf+count, p->pcr,6);
+ count += 6;
+ }
+ if ( p->adapt_flags & OPCR_FLAG ){
+ memcpy(buf+count, p->opcr,6);
+ count += 6;
+ }
+ if ( p->adapt_flags & SPLICE_FLAG ){
+ memcpy(buf+count, &p->splice_count,1);
+ count++;
+ }
+ if( p->adapt_flags & TRANS_PRIV){
+ memcpy(buf+count,&p->priv_dat_len,1);
+ count++;
+ memcpy(buf+count,p->priv_dat,p->priv_dat_len);
+ count += p->priv_dat_len;
+ }
+
+ if( p->adapt_flags & ADAP_EXT_FLAG){
+ memcpy(buf+count,&p->adapt_ext_len,1);
+ count++;
+ memcpy(buf+count,&p->adapt_eflags,1);
+ count++;
+
+ if( p->adapt_eflags & LTW_FLAG){
+ memcpy(buf+count,p->ltw,2);
+ count += 2;
+ }
+ if( p->adapt_eflags & PIECE_RATE){
+ memcpy(buf+count,p->piece_rate,3);
+ count += 3;
+ }
+ if( p->adapt_eflags & SEAM_SPLICE){
+ memcpy(buf+count,p->dts,5);
+ count += 5;
+ }
+ }
+ dummy = 0xFF;
+ for(i=0; i < p->stuffing ; i++){
+ memcpy(buf+count,&dummy,1);
+ count++;
+ }
+ if (p->flags & PAYLOAD){
+ memcpy(buf+count,p->data,p->rest);
+ count += p->rest;
+ }
+ }
+
+
+ return count;
+}
+
+void write_ts(int fd, ts_packet *p){
+ long length;
+ uint8_t buf[TS_SIZE];
+
+ length = cwrite_ts(buf,p,TS_SIZE);
+ write(fd,buf,length);
+}
+
+int read_ts (int f, ts_packet *p){
+ uint8_t sync;
+ int found=0;
+ uint64_t po,q;
+ int neof = 1;
+
+ sync=0;
+ while (neof > 0 && !found) {
+ neof = save_read(f,&sync,1);
+ if (sync == 0x47)
+ found = 1;
+ }
+ neof = save_read(f,p->pid,2);
+ neof = save_read(f,&p->flags,1);
+ p->count = p->flags & COUNT_MASK;
+
+ if (!(p->flags & ADAPT_FIELD) && (p->flags & PAYLOAD)){
+ //no adapt. field only payload
+ neof = save_read(f,p->data,184);
+ p->rest = 184;
+ return neof;
+ }
+
+ if ( p->flags & ADAPT_FIELD ) {
+ // adaption field
+ neof = save_read(f,&p->adapt_length,1);
+ po = lseek(f,0,SEEK_CUR);
+ neof = save_read(f,&p->adapt_flags,1);
+
+ if ( p->adapt_flags & PCR_FLAG )
+ neof = save_read(f, p->pcr,6);
+
+ if ( p->adapt_flags & OPCR_FLAG )
+ neof = save_read(f, p->opcr,6);
+
+ if ( p->adapt_flags & SPLICE_FLAG )
+ neof = save_read(f, &p->splice_count,1);
+
+ if( p->adapt_flags & TRANS_PRIV){
+ neof = save_read(f,&p->priv_dat_len,1);
+ p->priv_dat = (uint8_t *) malloc(p->priv_dat_len);
+ neof = save_read(f,p->priv_dat,p->priv_dat_len);
+ }
+
+ if( p->adapt_flags & ADAP_EXT_FLAG){
+ neof = save_read(f,&p->adapt_ext_len,1);
+ neof = save_read(f,&p->adapt_eflags,1);
+ if( p->adapt_eflags & LTW_FLAG)
+ neof = save_read(f,p->ltw,2);
+
+ if( p->adapt_eflags & PIECE_RATE)
+ neof = save_read(f,p->piece_rate,3);
+
+ if( p->adapt_eflags & SEAM_SPLICE)
+ neof = save_read(f,p->dts,5);
+ }
+ q = lseek(f,0,SEEK_CUR);
+ p->stuffing = p->adapt_length -(q-po);
+ p->rest = 183-p->adapt_length;
+ lseek(f,q+p->stuffing,SEEK_SET);
+ if (p->flags & PAYLOAD) // payload
+ neof = save_read(f,p->data,p->rest);
+ else
+ lseek(f,q+p->rest,SEEK_SET);
+ }
+ return neof;
+}
+
+void cread_ts (char *buf, ts_packet *p, long length){
+ uint8_t sync;
+ int found=0;
+ uint64_t po,q;
+ long count=0;
+
+ sync=0;
+ while (count < length && !found) {
+ sync=buf[count];
+ count++;
+ if (sync == 0x47)
+ found = 1;
+ }
+ memcpy(p->pid,buf+count,2);
+ count += 2;
+ p->flags = buf[count];
+ count++;
+ p->count = p->flags & COUNT_MASK;
+
+ if (!(p->flags & ADAPT_FIELD) && (p->flags & PAYLOAD)){
+ //no adapt. field only payload
+ memcpy(p->data,buf+count,184);
+ p->rest = 184;
+ return;
+ }
+
+ if ( p->flags & ADAPT_FIELD ) {
+ // adaption field
+ p->adapt_length = buf[count];
+ count++;
+ po = count;
+ memcpy(&p->adapt_flags,buf+count,1);
+ count++;
+
+ if ( p->adapt_flags & PCR_FLAG ){
+ memcpy( p->pcr,buf+count,6);
+ count += 6;
+ }
+ if ( p->adapt_flags & OPCR_FLAG ){
+ memcpy( p->opcr,buf+count,6);
+ count += 6;
+ }
+ if ( p->adapt_flags & SPLICE_FLAG ){
+ memcpy( &p->splice_count,buf+count,1);
+ count++;
+ }
+ if( p->adapt_flags & TRANS_PRIV){
+ memcpy(&p->priv_dat_len,buf+count,1);
+ count++;
+ p->priv_dat = (uint8_t *) malloc(p->priv_dat_len);
+ memcpy(p->priv_dat,buf+count,p->priv_dat_len);
+ count += p->priv_dat_len;
+ }
+
+ if( p->adapt_flags & ADAP_EXT_FLAG){
+ memcpy(&p->adapt_ext_len,buf+count,1);
+ count++;
+ memcpy(&p->adapt_eflags,buf+count,1);
+ count++;
+ if( p->adapt_eflags & LTW_FLAG){
+ memcpy(p->ltw,buf+count,2);
+ count += 2;
+ }
+ if( p->adapt_eflags & PIECE_RATE){
+ memcpy(p->piece_rate,buf+count,3);
+ count += 3;
+ }
+ if( p->adapt_eflags & SEAM_SPLICE){
+ memcpy(p->dts,buf+count,5);
+ count += 5;
+ }
+ }
+ q = count;
+ p->stuffing = p->adapt_length -(q-po);
+ p->rest = 183-p->adapt_length;
+ count = q+p->stuffing;
+ if (p->flags & PAYLOAD){ // payload
+ memcpy(p->data,buf+count,p->rest);
+ count += p->rest;
+ } else
+ count = q+p->rest;
+ }
+}
+
+
+/*
+
+ Program Stream
+
+*/
+
+
+void init_ps(ps_packet *p)
+{
+ p->stuff_length=0xF8;
+ p->data = NULL;
+ p->sheader_length = 0;
+ p->audio_bound = 0;
+ p->video_bound = 0;
+ p->npes = 0;
+ p->mpeg = 2;
+}
+
+void kill_ps(ps_packet *p)
+{
+ if (p->data)
+ free(p->data);
+ init_ps(p);
+}
+
+void setlength_ps(ps_packet *p)
+{
+ short *ll;
+ ll = (short *) p->sheader_llength;
+ if (p->mpeg == 2)
+ p->sheader_length = ntohs(*ll) - 6;
+ else
+ p->sheader_length = ntohs(*ll);
+}
+
+static void setl_ps(ps_packet *p)
+{
+ setlength_ps(p);
+ p->data = (uint8_t *) malloc(p->sheader_length);
+}
+
+int mux_ps(ps_packet *p)
+{
+ uint32_t mux = 0;
+ uint8_t *i = (uint8_t *)&mux;
+
+ i[1] = p->mux_rate[0];
+ i[2] = p->mux_rate[1];
+ i[3] = p->mux_rate[2];
+ mux = ntohl(mux);
+ mux = (mux >>2);
+ return mux;
+}
+
+int rate_ps(ps_packet *p)
+{
+ uint32_t rate=0;
+ uint8_t *i= (uint8_t *) &rate;
+
+ i[1] = p->rate_bound[0] & 0x7F;
+ i[2] = p->rate_bound[1];
+ i[3] = p->rate_bound[2];
+
+ rate = ntohl(rate);
+ rate = (rate >> 1);
+ return rate;
+}
+
+
+uint32_t scr_base_ps(ps_packet *p) // only 32 bit!!
+{
+ uint32_t base = 0;
+ uint8_t *buf = (uint8_t *)&base;
+
+ buf[0] |= (long int)((p->scr[0] & 0x18) << 3);
+ buf[0] |= (long int)((p->scr[0] & 0x03) << 4);
+ buf[0] |= (long int)((p->scr[1] & 0xF0) >> 4);
+
+ buf[1] |= (long int)((p->scr[1] & 0x0F) << 4);
+ buf[1] |= (long int)((p->scr[2] & 0xF0) >> 4);
+
+ buf[2] |= (long int)((p->scr[2] & 0x08) << 4);
+ buf[2] |= (long int)((p->scr[2] & 0x03) << 5);
+ buf[2] |= (long int)((p->scr[3] & 0xF8) >> 3);
+
+ buf[3] |= (long int)((p->scr[3] & 0x07) << 5);
+ buf[3] |= (long int)((p->scr[4] & 0xF8) >> 3);
+
+ base = ntohl(base);
+ return base;
+}
+
+uint16_t scr_ext_ps(ps_packet *p)
+{
+ short ext = 0;
+
+ ext = (short)(p->scr[5] >> 1);
+ ext += (short) (p->scr[4] & 0x03) * 128;
+
+ return ext;
+}
+
+int cwrite_ps(uint8_t *buf, ps_packet *p, long length)
+{
+ long count,i;
+ uint8_t headr1[4] = {0x00, 0x00, 0x01, 0xBA };
+ uint8_t headr2[4] = {0x00, 0x00, 0x01, 0xBB };
+ uint8_t buffy = 0xFF;
+
+
+ memcpy(buf,headr1,4);
+ count = 4;
+ if (p->mpeg == 2){
+ memcpy(buf+count,p->scr,6);
+ count += 6;
+ memcpy(buf+count,p->mux_rate,3);
+ count += 3;
+ memcpy(buf+count,&p->stuff_length,1);
+ count++;
+ for(i=0; i< (p->stuff_length & 3); i++){
+ memcpy(buf+count,&buffy,1);
+ count++;
+ }
+ } else {
+ memcpy(buf+count,p->scr,5);
+ count += 5;
+ memcpy(buf+count,p->mux_rate,3);
+ count += 3;
+ }
+ if (p->sheader_length){
+ memcpy(buf+count,headr2,4);
+ count += 4;
+ memcpy(buf+count,p->sheader_llength,2);
+ count += 2;
+ if ( p->mpeg == 2){
+ memcpy(buf+count,p->rate_bound,3);
+ count += 3;
+ memcpy(buf+count,&p->audio_bound,1);
+ count++;
+ memcpy(buf+count,&p->video_bound,1);
+ count++;
+ memcpy(buf+count,&p->reserved,1);
+ count++;
+ }
+ memcpy(buf+count,p->data,p->sheader_length);
+ count += p->sheader_length;
+ }
+
+ return count;
+}
+
+void write_ps(int fd, ps_packet *p){
+ long length;
+ uint8_t buf[PS_MAX];
+
+ length = cwrite_ps(buf,p,PS_MAX);
+ write(fd,buf,length);
+}
+
+int read_ps (int f, ps_packet *p){
+ uint8_t headr[4];
+ pes_packet pes;
+ int i,done;
+ int found=0;
+ uint64_t po = 0;
+ uint64_t q = 0;
+ long count = 0;
+ int neof = 1;
+
+ po = lseek(f,0,SEEK_CUR);
+ while (neof > 0 && !found && count < MAX_SEARCH) {
+ neof = save_read(f,&headr,4);
+ if (headr[0] == 0x00 && headr[1] == 0x00 && headr[2] == 0x01){
+ if ( headr[3] == 0xBA )
+ found = 1;
+ else
+ if ( headr[3] == 0xB9 ) break;
+ else lseek(f,po+1,SEEK_SET);
+ }
+ count++;
+ }
+
+ if (found){
+ neof = save_read(f,p->scr,6);
+ if (p->scr[0] & 0x40)
+ p->mpeg = 2;
+ else
+ p->mpeg = 1;
+
+ if (p->mpeg == 2){
+ neof = save_read(f,p->mux_rate,3);
+ neof = save_read(f,&p->stuff_length,1);
+ po = lseek(f,0,SEEK_CUR);
+ lseek(f,po+(p->stuff_length & 3),SEEK_SET);
+ } else {
+ p->mux_rate[0] = p->scr[5]; //mpeg1 scr is only 5 bytes
+ neof = save_read(f,p->mux_rate+1,2);
+ }
+
+ po = lseek(f,0,SEEK_CUR);
+ neof = save_read(f,headr,4);
+ if (headr[0] == 0x00 && headr[1] == 0x00 &&
+ headr[2] == 0x01 && headr[3] == 0xBB ) {
+ neof = save_read(f,p->sheader_llength,2);
+ setl_ps(p);
+ if (p->mpeg == 2){
+ neof = save_read(f,p->rate_bound,3);
+ neof = save_read(f,&p->audio_bound,1);
+ neof = save_read(f,&p->video_bound,1);
+ neof = save_read(f,&p->reserved,1);
+ }
+ neof = save_read(f,p->data,p->sheader_length);
+ } else {
+ lseek(f,po,SEEK_SET);
+ p->sheader_length = 0;
+ }
+
+ i = 0;
+ done = 0;
+ q = lseek(f,0,SEEK_CUR);
+ do {
+ po = lseek(f,0,SEEK_CUR);
+ neof = save_read(f,headr,4);
+ lseek(f,po,SEEK_SET);
+ if ( headr[0] == 0x00 && headr[1] == 0x00
+ && headr[2] == 0x01 && headr[3] != 0xBA){
+ init_pes(&pes);
+ neof = read_pes(f,&pes);
+ i++;
+ } else done = 1;
+ kill_pes(&pes);
+ } while ( neof > 0 && !done);
+ p->npes = i;
+ lseek(f,q,SEEK_SET);
+ }
+ return neof;
+}
+
+void cread_ps (char *buf, ps_packet *p, long length){
+ uint8_t *headr;
+ pes_packet pes;
+ int i,done;
+ int found=0;
+ uint64_t po = 0;
+ uint64_t q = 0;
+ long count = 0;
+ long c = 0;
+
+ po = c;
+ while ( count < length && !found && count < MAX_SEARCH) {
+ headr = (uint8_t *)buf+c;
+ c += 4;
+ if (headr[0] == 0x00 && headr[1] == 0x00 && headr[2] == 0x01){
+ if ( headr[3] == 0xBA )
+ found = 1;
+ else
+ if ( headr[3] == 0xB9 ) break;
+ else c = po+1;
+ }
+ count++;
+ }
+
+ if (found){
+ memcpy(p->scr,buf+c,6);
+ c += 6;
+ if (p->scr[0] & 0x40)
+ p->mpeg = 2;
+ else
+ p->mpeg = 1;
+
+ if (p->mpeg == 2){
+ memcpy(p->mux_rate,buf+c,3);
+ c += 3;
+ memcpy(&p->stuff_length,buf+c,1);
+ c++;
+ po = c;
+ c = po+(p->stuff_length & 3);
+ } else {
+ p->mux_rate[0] = p->scr[5]; //mpeg1 scr is only 5 bytes
+ memcpy(p->mux_rate+1,buf+c,2);
+ c += 2;
+ }
+
+ po = c;
+ headr = (uint8_t *)buf+c;
+ c += 4;
+ if (headr[0] == 0x00 && headr[1] == 0x00 &&
+ headr[2] == 0x01 && headr[3] == 0xBB ) {
+ memcpy(p->sheader_llength,buf+c,2);
+ c += 2;
+ setl_ps(p);
+ if (p->mpeg == 2){
+ memcpy(p->rate_bound,buf+c,3);
+ c += 3;
+ memcpy(&p->audio_bound,buf+c,1);
+ c++;
+ memcpy(&p->video_bound,buf+c,1);
+ c++;
+ memcpy(&p->reserved,buf+c,1);
+ c++;
+ }
+ memcpy(p->data,buf+c,p->sheader_length);
+ c += p->sheader_length;
+ } else {
+ c = po;
+ p->sheader_length = 0;
+ }
+
+ i = 0;
+ done = 0;
+ q = c;
+ do {
+ headr = (uint8_t *)buf+c;
+ if ( headr[0] == 0x00 && headr[1] == 0x00
+ && headr[2] == 0x01 && headr[3] != 0xBA){
+ init_pes(&pes);
+ // cread_pes(buf+c,&pes);
+ i++;
+ } else done = 1;
+ kill_pes(&pes);
+ } while (c < length && !done);
+ p->npes = i;
+ c = q;
+ }
+}
+
+
+
+
+
+
+
+/*
+ conversion
+*/
+
+void init_trans(trans *p)
+{
+ int i;
+
+ p->found = 0;
+ p->pes = 0;
+ p->is_full = 0;
+ p->pes_start = 0;
+ p->pes_started = 0;
+ p->set = 0;
+
+ for (i = 0; i < MASKL*MAXFILT ; i++){
+ p->mask[i] = 0;
+ p->filt[i] = 0;
+ }
+ for (i = 0; i < MAXFILT ; i++){
+ p->sec[i].found = 0;
+ p->sec[i].length = 0;
+ }
+}
+
+int set_trans_filt(trans *p, int filtn, uint16_t pid, uint8_t *mask, uint8_t *filt, int pes)
+{
+ int i;
+ int off;
+
+ if ( filtn > MAXFILT-1 || filtn<0 ) return -1;
+ p->pid[filtn] = pid;
+ if (pes) p->pes |= (tflags)(1 << filtn);
+ else {
+ off = MASKL*filtn;
+ p->pes &= ~((tflags) (1 << filtn) );
+ for (i = 0; i < MASKL ; i++){
+ p->mask[off+i] = mask[i];
+ p->filt[off+i] = filt[i];
+ }
+ }
+ p->set |= (tflags) (1 << filtn);
+ return 0;
+}
+
+void clear_trans_filt(trans *p,int filtn)
+{
+ int i;
+
+ p->set &= ~((tflags) (1 << filtn) );
+ p->pes &= ~((tflags) (1 << filtn) );
+ p->is_full &= ~((tflags) (1 << filtn) );
+ p->pes_start &= ~((tflags) (1 << filtn) );
+ p->pes_started &= ~((tflags) (1 << filtn) );
+
+ for (i = MASKL*filtn; i < MASKL*(filtn+1) ; i++){
+ p->mask[i] = 0;
+ p->filt[i] = 0;
+ }
+ p->sec[filtn].found = 0;
+ p->sec[filtn].length = 0;
+}
+
+int filt_is_set(trans *p, int filtn)
+{
+ if (p->set & ((tflags)(1 << filtn))) return 1;
+ return 0;
+}
+
+int pes_is_set(trans *p, int filtn)
+{
+ if (p->pes & ((tflags)(1 << filtn))) return 1;
+ return 0;
+}
+
+int pes_is_started(trans *p, int filtn)
+{
+ if (p->pes_started & ((tflags)(1 << filtn))) return 1;
+ return 0;
+}
+
+int pes_is_start(trans *p, int filtn)
+{
+ if (p->pes_start & ((tflags)(1 << filtn))) return 1;
+ return 0;
+}
+
+int filt_is_ready(trans *p,int filtn)
+{
+ if (p->is_full & ((tflags)(1 << filtn))) return 1;
+ return 0;
+}
+
+void trans_filt(uint8_t *buf, int count, trans *p)
+{
+ int c=0;
+ //fprintf(stderr,"trans_filt\n");
+
+
+ while (c < count && p->found <1 ){
+ if ( buf[c] == 0x47) p->found = 1;
+ c++;
+ p->packet[0] = 0x47;
+ }
+ if (c == count) return;
+
+ while( c < count && p->found < 188 && p->found > 0 ){
+ p->packet[p->found] = buf[c];
+ c++;
+ p->found++;
+ }
+ if (p->found == 188){
+ p->found = 0;
+ tfilter(p);
+ }
+
+ if (c < count) trans_filt(buf+c,count-c,p);
+}
+
+
+void tfilter(trans *p)
+{
+ int l,c;
+ int tpid;
+ uint8_t flag,flags;
+ uint8_t adapt_length = 0;
+ uint8_t cpid[2];
+
+
+ // fprintf(stderr,"tfilter\n");
+
+ cpid[0] = p->packet[1];
+ cpid[1] = p->packet[2];
+ tpid = get_pid(cpid);
+
+ if ( p->packet[1]&0x80){
+ fprintf(stderr,"Error in TS for PID: %d\n",
+ tpid);
+ }
+
+ flag = cpid[0];
+ flags = p->packet[3];
+
+ if ( flags & ADAPT_FIELD ) {
+ // adaption field
+ adapt_length = p->packet[4];
+ }
+
+ c = 5 + adapt_length - (int)(!(flags & ADAPT_FIELD));
+ if (flags & PAYLOAD){
+ for ( l = 0; l < MAXFILT ; l++){
+ if ( filt_is_set(p,l) ) {
+ if ( p->pid[l] == tpid) {
+ if ( pes_is_set(p,l) ){
+ if (cpid[0] & PAY_START){
+ p->pes_started |=
+ (tflags)
+ (1 << l);
+ p->pes_start |=
+ (tflags)
+ (1 << l);
+ } else {
+ p->pes_start &= ~
+ ((tflags)
+ (1 << l));
+ }
+ pes_filter(p,l,c);
+ } else {
+ sec_filter(p,l,c);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void pes_filter(trans *p, int filtn, int off)
+{
+ int count,c;
+ uint8_t *buf;
+
+ if (filtn < 0 || filtn >= MAXFILT) return;
+
+ count = 188 - off;
+ c = 188*filtn;
+ buf = p->packet+off;
+ if (pes_is_started(p,filtn)){
+ p->is_full |= (tflags) (1 << filtn);
+ memcpy(p->transbuf+c,buf,count);
+ p->transcount[filtn] = count;
+ }
+}
+
+section *get_filt_sec(trans *p, int filtn)
+{
+ section *sec;
+
+ sec = &p->sec[filtn];
+ p->is_full &= ~((tflags) (1 << filtn) );
+ return sec;
+}
+
+int get_filt_buf(trans *p, int filtn,uint8_t **buf)
+{
+ *buf = p->transbuf+188*filtn;
+ p->is_full &= ~((tflags) (1 << filtn) );
+ return p->transcount[filtn];
+}
+
+
+
+
+void sec_filter(trans *p, int filtn, int off)
+{
+ int i,j;
+ int error;
+ int count,c;
+ uint8_t *buf, *secbuf;
+ section *sec;
+
+ // fprintf(stderr,"sec_filter\n");
+
+ if (filtn < 0 || filtn >= MAXFILT) return;
+
+ count = 188 - off;
+ c = 0;
+ buf = p->packet+off;
+ sec = &p->sec[filtn];
+ secbuf = sec->payload;
+ if(!filt_is_ready(p,filtn)){
+ p->is_full &= ~((tflags) (1 << filtn) );
+ sec->found = 0;
+ sec->length = 0;
+ }
+
+ if ( !sec->found ){
+ c = buf[c]+1;
+ if (c >= count) return;
+ sec->id = buf[c];
+ secbuf[0] = buf[c];
+ c++;
+ sec->found++;
+ sec->length = 0;
+ }
+
+ while ( c < count && sec->found < 3){
+ secbuf[sec->found] = buf[c];
+ c++;
+ sec->found++;
+ }
+ if (c == count) return;
+
+ if (!sec->length && sec->found == 3){
+ sec->length |= ((secbuf[1] & 0x0F) << 8);
+ sec->length |= (secbuf[2] & 0xFF);
+ }
+
+ while ( c < count && sec->found < sec->length+3){
+ secbuf[sec->found] = buf[c];
+ c++;
+ sec->found++;
+ }
+
+ if ( sec->length && sec->found == sec->length+3 ){
+ error=0;
+ for ( i = 0; i < MASKL; i++){
+ if (i > 0 ) j=2+i;
+ else j = 0;
+ error += (sec->payload[j]&p->mask[MASKL*filtn+i])^
+ (p->filt[MASKL*filtn+i]&
+ p->mask[MASKL*filtn+i]);
+ }
+ if (!error){
+ p->is_full |= (tflags) (1 << filtn);
+ }
+ if (buf[0]+1 < c ) c=count;
+ }
+
+ if ( c < count ) sec_filter(p, filtn, off);
+
+}
+
+#define MULT 1024
+
+
+void write_ps_headr( ps_packet *p, uint8_t *pts,int fd)
+{
+ long muxr = 37500;
+ uint8_t audio_bound = 1;
+ uint8_t fixed = 0;
+ uint8_t CSPS = 0;
+ uint8_t audio_lock = 1;
+ uint8_t video_lock = 1;
+ uint8_t video_bound = 1;
+ uint8_t stream1 = 0XC0;
+ uint8_t buffer1_scale = 1;
+ uint32_t buffer1_size = 32;
+ uint8_t stream2 = 0xE0;
+ uint8_t buffer2_scale = 1;
+ uint32_t buffer2_size = 230;
+
+ init_ps(p);
+
+ p->mpeg = 2;
+// SCR = 0
+ p->scr[0] = 0x44;
+ p->scr[1] = 0x00;
+ p->scr[2] = 0x04;
+ p->scr[3] = 0x00;
+ p->scr[4] = 0x04;
+ p->scr[5] = 0x01;
+
+// SCR = PTS
+ p->scr[0] = 0x44 | ((pts[0] >> 3)&0x18) | ((pts[0] >> 4)&0x03);
+ p->scr[1] = 0x00 | ((pts[0] << 4)&0xF0) | ((pts[1] >> 4)&0x0F);
+ p->scr[2] = 0x04 | ((pts[1] << 4)&0xF0) | ((pts[2] >> 4)&0x08)
+ | ((pts[2] >> 5)&0x03);
+ p->scr[3] = 0x00 | ((pts[2] << 3)&0xF8) | ((pts[3] >> 5)&0x07);
+ p->scr[4] = 0x04 | ((pts[3] << 3)&0xF8);
+ p->scr[5] = 0x01;
+
+ p->mux_rate[0] = (uint8_t)(muxr >> 14);
+ p->mux_rate[1] = (uint8_t)(0xff & (muxr >> 6));
+ p->mux_rate[2] = (uint8_t)(0x03 | ((muxr & 0x3f) << 2));
+
+ p->stuff_length = 0xF8;
+
+ p->sheader_llength[0] = 0x00;
+ p->sheader_llength[1] = 0x0c;
+
+ setl_ps(p);
+
+ p->rate_bound[0] = (uint8_t)(0x80 | (muxr >>15));
+ p->rate_bound[1] = (uint8_t)(0xff & (muxr >> 7));
+ p->rate_bound[2] = (uint8_t)(0x01 | ((muxr & 0x7f)<<1));
+
+
+ p->audio_bound = (uint8_t)((audio_bound << 2)|(fixed << 1)|CSPS);
+ p->video_bound = (uint8_t)((audio_lock << 7)|
+ (video_lock << 6)|0x20|video_bound);
+ p->reserved = (uint8_t)(0xFF);
+
+ p->data[0] = stream2;
+ p->data[1] = (uint8_t) (0xc0 | (buffer2_scale << 5) |
+ (buffer2_size >> 8));
+ p->data[2] = (uint8_t) (buffer2_size & 0xff);
+ p->data[3] = stream1;
+ p->data[4] = (uint8_t) (0xc0 | (buffer1_scale << 5) |
+ (buffer1_size >> 8));
+ p->data[5] = (uint8_t) (buffer1_size & 0xff);
+
+ write_ps(fd, p);
+ kill_ps(p);
+}
+
+
+
+void twrite(uint8_t const *buf)
+{
+ int l = TS_SIZE;
+ int c = 0;
+ int w;
+
+
+ while (l){
+ w = write(STDOUT_FILENO,buf+c,l);
+ if (w>=0){
+ l-=w;
+ c+=w;
+ }
+ }
+}
+
+void init_p2t(p2t_t *p, void (*fkt)(uint8_t const *buf))
+{
+ memset(p->pes,0,TS_SIZE);
+ p->counter = 0;
+ p->pos = 0;
+ p->frags = 0;
+ if (fkt) p->t_out = fkt;
+ else p->t_out = twrite;
+}
+
+void clear_p2t(p2t_t *p)
+{
+ memset(p->pes,0,TS_SIZE);
+ p->counter = 0;
+ p->pos = 0;
+ p->frags = 0;
+}
+
+
+long int find_pes_header(uint8_t const *buf, long int length, int *frags)
+{
+ int c = 0;
+ int found = 0;
+
+ *frags = 0;
+
+ while (c < length-3 && !found) {
+ if (buf[c] == 0x00 && buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01) {
+ switch ( buf[c+3] ) {
+ case 0xBA:
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ found = 1;
+ break;
+
+ default:
+ c++;
+ break;
+ }
+ } else c++;
+ }
+ if (c == length-3 && !found){
+ if (buf[length-1] == 0x00) *frags = 1;
+ if (buf[length-2] == 0x00 &&
+ buf[length-1] == 0x00) *frags = 2;
+ if (buf[length-3] == 0x00 &&
+ buf[length-2] == 0x00 &&
+ buf[length-1] == 0x01) *frags = 3;
+ return -1;
+ }
+
+ return c;
+}
+
+void pes_to_ts( uint8_t const *buf, long int length, uint16_t pid, p2t_t *p)
+{
+ int c,c2,l,add;
+ int check,rest;
+
+ c = 0;
+ c2 = 0;
+ if (p->frags){
+ check = 0;
+ switch(p->frags){
+ case 1:
+ if ( buf[c] == 0x00 && buf[c+1] == 0x01 ){
+ check = 1;
+ c += 2;
+ }
+ break;
+ case 2:
+ if ( buf[c] == 0x01 ){
+ check = 1;
+ c++;
+ }
+ break;
+ case 3:
+ check = 1;
+ }
+ if(check){
+ switch ( buf[c] ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ p->pes[0] = 0x00;
+ p->pes[1] = 0x00;
+ p->pes[2] = 0x01;
+ p->pes[3] = buf[c];
+ p->pos=4;
+ memcpy(p->pes+p->pos,buf+c,TS_SIZE-4-p->pos);
+ c += TS_SIZE-4-p->pos;
+ p_to_t(p->pes,TS_SIZE-4,pid,&p->counter,
+ p->t_out);
+ clear_p2t(p);
+ break;
+
+ default:
+ c=0;
+ break;
+ }
+ }
+ p->frags = 0;
+ }
+
+ if (p->pos){
+ c2 = find_pes_header(buf+c,length-c,&p->frags);
+ if (c2 >= 0 && c2 < TS_SIZE-4-p->pos){
+ l = c2+c;
+ } else l = TS_SIZE-4-p->pos;
+ memcpy(p->pes+p->pos,buf,l);
+ c += l;
+ p->pos += l;
+ p_to_t(p->pes,p->pos,pid,&p->counter,
+ p->t_out);
+ clear_p2t(p);
+ }
+
+ add = 0;
+ while (c < length){
+ c2 = find_pes_header(buf+c+add,length-c-add,&p->frags);
+ if (c2 >= 0) {
+ c2 += c+add;
+ if (c2 > c){
+ p_to_t(buf+c,c2-c,pid,&p->counter,
+ p->t_out);
+ c = c2;
+ clear_p2t(p);
+ add = 0;
+ } else add = 1;
+ } else {
+ l = length-c;
+ rest = l % (TS_SIZE-4);
+ l -= rest;
+ p_to_t(buf+c,l,pid,&p->counter,
+ p->t_out);
+ memcpy(p->pes,buf+c+l,rest);
+ p->pos = rest;
+ c = length;
+ }
+ }
+}
+
+
+
+void p_to_t( uint8_t const *buf, long int length, uint16_t pid, uint8_t *counter,
+ void (*ts_write)(uint8_t const *))
+{
+
+ int l, pes_start;
+ uint8_t obuf[TS_SIZE];
+ long int c = 0;
+ pes_start = 0;
+ if ( length > 3 &&
+ buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01 )
+ switch (buf[3]){
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ pes_start = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ while ( c < length ){
+ memset(obuf,0,TS_SIZE);
+ if (length - c >= TS_SIZE-4){
+ l = write_ts_header(pid, counter, pes_start
+ , obuf, TS_SIZE-4);
+ memcpy(obuf+l, buf+c, TS_SIZE-l);
+ c += TS_SIZE-l;
+ } else {
+ l = write_ts_header(pid, counter, pes_start
+ , obuf, length-c);
+ memcpy(obuf+l, buf+c, TS_SIZE-l);
+ c = length;
+ }
+ ts_write(obuf);
+ pes_start = 0;
+ }
+}
+
+
+int write_ps_header(uint8_t *buf,
+ uint32_t SCR,
+ long muxr,
+ uint8_t audio_bound,
+ uint8_t fixed,
+ uint8_t CSPS,
+ uint8_t audio_lock,
+ uint8_t video_lock,
+ uint8_t video_bound,
+ uint8_t stream1,
+ uint8_t buffer1_scale,
+ uint32_t buffer1_size,
+ uint8_t stream2,
+ uint8_t buffer2_scale,
+ uint32_t buffer2_size)
+{
+ ps_packet p;
+ uint8_t *pts;
+ long lpts;
+ init_ps(&p);
+
+ lpts = htonl(SCR);
+ pts = (uint8_t *) &lpts;
+
+
+ p.mpeg = 2;
+// SCR = 0
+ p.scr[0] = 0x44;
+ p.scr[1] = 0x00;
+ p.scr[2] = 0x04;
+ p.scr[3] = 0x00;
+ p.scr[4] = 0x04;
+ p.scr[5] = 0x01;
+
+// SCR = PTS
+ p.scr[0] = 0x44 | ((pts[0] >> 3)&0x18) | ((pts[0] >> 4)&0x03);
+ p.scr[1] = 0x00 | ((pts[0] << 4)&0xF0) | ((pts[1] >> 4)&0x0F);
+ p.scr[2] = 0x04 | ((pts[1] << 4)&0xF0) | ((pts[2] >> 4)&0x08)
+ | ((pts[2] >> 5)&0x03);
+ p.scr[3] = 0x00 | ((pts[2] << 3)&0xF8) | ((pts[3] >> 5)&0x07);
+ p.scr[4] = 0x04 | ((pts[3] << 3)&0xF8);
+ p.scr[5] = 0x01;
+
+ p.mux_rate[0] = (uint8_t)(muxr >> 14);
+ p.mux_rate[1] = (uint8_t)(0xff & (muxr >> 6));
+ p.mux_rate[2] = (uint8_t)(0x03 | ((muxr & 0x3f) << 2));
+
+ p.stuff_length = 0xF8;
+
+ if (stream1 && stream2){
+ p.sheader_llength[0] = 0x00;
+ p.sheader_llength[1] = 0x0c;
+
+ setl_ps(&p);
+
+ p.rate_bound[0] = (uint8_t)(0x80 | (muxr >>15));
+ p.rate_bound[1] = (uint8_t)(0xff & (muxr >> 7));
+ p.rate_bound[2] = (uint8_t)(0x01 | ((muxr & 0x7f)<<1));
+
+
+ p.audio_bound = (uint8_t)((audio_bound << 2)|(fixed << 1)|CSPS);
+ p.video_bound = (uint8_t)((audio_lock << 7)|
+ (video_lock << 6)|0x20|video_bound);
+ p.reserved = (uint8_t)(0xFF >> 1);
+
+ p.data[0] = stream2;
+ p.data[1] = (uint8_t) (0xc0 | (buffer2_scale << 5) |
+ (buffer2_size >> 8));
+ p.data[2] = (uint8_t) (buffer2_size & 0xff);
+ p.data[3] = stream1;
+ p.data[4] = (uint8_t) (0xc0 | (buffer1_scale << 5) |
+ (buffer1_size >> 8));
+ p.data[5] = (uint8_t) (buffer1_size & 0xff);
+
+ cwrite_ps(buf, &p, PS_HEADER_L2);
+ kill_ps(&p);
+ return PS_HEADER_L2;
+ } else {
+ cwrite_ps(buf, &p, PS_HEADER_L1);
+ kill_ps(&p);
+ return PS_HEADER_L1;
+ }
+}
+
+
+
+#define MAX_BASE 80
+#define MAX_PATH 256
+#define MAX_EXT 10
+
+int break_up_filename(char *name, char *base_name, char *path, char *ext)
+{
+ int l,i,sstop,sstart;
+
+ l = strlen(name);
+ sstop = l;
+ sstart = -1;
+ for( i= l-1; i >= 0; i--){
+ if (sstop == l && name[i] == '.') sstop = i;
+ if (sstart<0 && name[i] == '/') sstart = i+1;
+ }
+ if (sstart < 0) sstart = 0;
+ if (sstop-sstart < MAX_BASE){
+ strncpy(base_name, name+sstart, sstop-sstart);
+ base_name[sstop-sstart]=0;
+ if(sstart > 0){
+ if( l - sstop + sstart < MAX_PATH){
+ strncpy(path, name, sstart);
+ path[sstart] = 0;
+ } else {
+ fprintf(stderr,"PATH too long\n");
+ return -1;
+ }
+
+ } else {
+ strcpy(path, "./");
+ }
+
+ if(sstop < l){
+ if( l - sstop -1 < MAX_EXT){
+ strncpy(ext, name+sstop+1, l-sstop-1);
+ ext[l-sstop-1]=0;
+ } else {
+ fprintf(stderr,"Extension too long\n");
+ return -1;
+ }
+
+ } else {
+ strcpy(ext, "");
+ }
+
+ } else {
+ fprintf(stderr,"Name too long\n");
+ return -1;
+ }
+/*
+ printf("%d %d\n",sstart, sstop);
+ printf("%s %d\n",name, strlen(name));
+ printf("%s %d\n",base_name, strlen(base_name));
+ printf("%s %d\n",path,strlen(path));
+ printf("%s %d\n",ext,strlen(ext));
+*/
+ return 0;
+}
+
+
+int seek_mpg_start(uint8_t *buf, int size)
+{
+ int found = 0;
+ int c=0;
+ int seq = 0;
+ int mpeg = 0;
+ int mark = 0;
+
+ while ( !seq ){
+ while (found != 4){
+ switch (found) {
+ case 0:
+ if ( buf[c] == 0x00 ) found++;
+ c++;
+ break;
+ case 1:
+ if ( buf[c] == 0x00 ) found++;
+ else found = 0;
+ c++;
+ break;
+ case 2:
+ if ( buf[c] == 0x01 ) found++;
+ else found = 0;
+ if ( buf[c] == 0x00 ) found = 2;
+ c++;
+ break;
+
+ case 3:
+ if ( (buf[c] & 0xe0) == 0xe0 ) found++;
+ else found = 0;
+ c++;
+ break;
+ }
+ if (c >= size) return -1;
+ }
+
+ if (found == 4){
+ mark = c-4;
+ c+=2;
+ if (c >= size) return -1;
+
+ if ( (buf[c] & 0xC0) == 0x80 ){
+ mpeg = 2;
+ c += 2;
+ if (c >= size) return -1;
+ c += buf[c]+1;
+ if (c >= size) return -1;
+ } else {
+ mpeg = 1;
+ while( buf[c] == 0xFF ) {
+ c++;
+ if (c >= size) return -1;
+ }
+ if ( (buf[c] & 0xC0) == 0x40) c+=2;
+ if (c >= size) return -1;
+ if ( (buf[c] & 0x30) ){
+ if ( (buf[c] & 0x30) == 0x20) c+=5;
+ else c+=10;
+ } else c++;
+ if (c >= size) return -1;
+ }
+
+ if ( buf[c] == 0x00 &&
+ buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01 &&
+ buf[c+3] == 0xB3 )
+ seq = 1;
+ }
+ found = 0;
+ }
+
+ return size-mark;
+}
+
+
+void write_mpg(int fstart, uint64_t length, int fdin, int fdout)
+{
+// uint8_t mpeg_end[4] = { 0x00, 0x00, 0x01, 0xB9 };
+ uint8_t *buf;
+ uint64_t l=0;
+ uint64_t count = 0;
+ struct stat sb;
+ int buf_size;
+
+ fstat (fdout, &sb);
+ buf_size = sb.st_blksize;
+
+ buf = (char *) alloca (buf_size + sizeof (int));
+
+ lseek(fdin, fstart, SEEK_SET);
+
+ while ( count < length && (l = read(fdin,buf,buf_size)) >= 0){
+ if (l > 0) count+=l;
+ write(fdout,buf,l);
+ printf("written %02.2f%%\r",(100.*count)/length);
+ }
+ printf("\n");
+
+ //write( fdout, mpeg_end, 4);
+}
+
+
+#define CHECKBUF (1024*1024)
+#define ONE_GIG (1024UL*1024UL*1024UL)
+void split_mpg(char *name, uint64_t size)
+{
+ char base_name[MAX_BASE];
+ char path[MAX_PATH];
+ char ext[MAX_EXT];
+ char new_name[256];
+ uint8_t buf[CHECKBUF];
+ int fdin;
+ int fdout;
+ uint64_t length = 0;
+ uint64_t last;
+ int i;
+ int mark, csize;
+ struct stat sb;
+
+ if (break_up_filename(name,base_name,path,ext) < 0) exit(1);
+
+
+ if ( (fdin = open(name, O_RDONLY|O_LARGEFILE)) < 0){
+ fprintf(stderr,"Can't open %s\n",name);
+ exit(1);
+ }
+
+ fstat (fdin, &sb);
+
+ length = sb.st_size;
+ if ( length < ONE_GIG )
+ printf("Filelength = %2.2f MB\n", length/1024./1024.);
+ else
+ printf("Filelength = %2.2f GB\n", length/1024./1024./1024.);
+
+ if ( length < size ) length = size;
+
+ printf("Splitting %s into Files with size <= %2.2f MB\n",name,
+ size/1024./1024.);
+
+ csize = CHECKBUF;
+ read(fdin, buf, csize);
+ if ( (mark = seek_mpg_start(buf,csize)) < 0){
+ fprintf(stderr,"Couldn't find sequence header\n");
+ exit(1);
+ }
+
+ last = csize-mark;
+
+ for ( i = 0 ; i < length/size; i++){
+ csize = CHECKBUF;
+
+ if (csize > length-last) csize = length-last;
+ lseek(fdin, last+size-csize, SEEK_SET);
+ read(fdin, buf, csize);
+ if ( (mark = seek_mpg_start(buf,csize)) < 0){
+ fprintf(stderr,"Couldn't find sequence header\n");
+ exit(1);
+ }
+
+ sprintf(new_name,"%s-%03d.%s",base_name,i,ext);
+ printf("writing %s\n",new_name);
+
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
+ |O_LARGEFILE,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH)) < 0){
+ fprintf(stderr,"Can't open %s\n",new_name);
+ exit(1);
+ }
+ write_mpg(last, size-mark, fdin, fdout);
+ last = last + size - mark;
+ }
+ sprintf(new_name,"%s-%03d.%s",base_name,i,ext);
+ printf("writing %s\n",new_name);
+
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
+ |O_LARGEFILE,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH)) < 0){
+ fprintf(stderr,"Can't open %s\n",new_name);
+ exit(1);
+ }
+ write_mpg(last, length-last, fdin, fdout);
+}
+
+
+
+
+void cut_mpg(char *name, uint64_t size)
+{
+ char base_name[MAX_BASE];
+ char path[MAX_PATH];
+ char ext[MAX_EXT];
+ char new_name[256];
+ uint8_t buf[CHECKBUF];
+ int fdin;
+ int fdout;
+ uint64_t length = 0;
+ uint64_t last;
+ int mark, csize;
+ struct stat sb;
+
+ if (break_up_filename(name,base_name,path,ext) < 0) exit(1);
+
+
+ if ( (fdin = open(name, O_RDONLY|O_LARGEFILE)) < 0){
+ fprintf(stderr,"Can't open %s\n",name);
+ exit(1);
+ }
+
+ fstat (fdin, &sb);
+
+ length = sb.st_size;
+ if ( length < ONE_GIG )
+ printf("Filelength = %2.2f MB\n", length/1024./1024.);
+ else
+ printf("Filelength = %2.2f GB\n", length/1024./1024./1024.);
+
+ if ( length < size ) length = size;
+
+ printf("Splitting %s into 2 Files with length %.2f MB and %.2f MB\n",
+ name, size/1024./1024., (length-size)/1024./1024.);
+
+ csize = CHECKBUF;
+ read(fdin, buf, csize);
+ if ( (mark = seek_mpg_start(buf,csize)) < 0){
+ fprintf(stderr,"Couldn't find sequence header\n");
+ exit(1);
+ }
+
+ last = csize-mark;
+
+ if (csize > length-last) csize = length-last;
+ lseek(fdin, last+size-csize, SEEK_SET);
+ read(fdin, buf, csize);
+ if ( (mark = seek_mpg_start(buf,csize)) < 0){
+ fprintf(stderr,"Couldn't find sequence header\n");
+ exit(1);
+ }
+
+ sprintf(new_name,"%s-1.%s",base_name,ext);
+ printf("writing %s\n",new_name);
+
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
+ |O_LARGEFILE,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH)) < 0){
+ fprintf(stderr,"Can't open %s\n",new_name);
+ exit(1);
+ }
+ write_mpg(last, size-mark, fdin, fdout);
+ last = last + size - mark;
+
+ sprintf(new_name,"%s-2.%s",base_name,ext);
+ printf("writing %s\n",new_name);
+
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
+ |O_LARGEFILE,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH)) < 0){
+ fprintf(stderr,"Can't open %s\n",new_name);
+ exit(1);
+ }
+ write_mpg(last, length-last, fdin, fdout);
+}
+
+
+
+
+void write_all (int fd, uint8_t *data, int length)
+{
+ int r;
+
+ while (length) {
+ if ((r = write(fd, data, length)) > 0) {
+ data += r;
+ length -= r;
+ }
+ }
+}
+
+
+
+void read_all (int fd, uint8_t *data, int length)
+{
+ int c = 0;
+
+ while(1) {
+ if( read(fd, data+c, 1) == 1) {
+ c++;
+ if(data[c-1] == '\n') {
+ data[c] = 0;
+ break;
+ }
+ }
+ else {
+ fprintf (stderr, "Error reading socket\n");
+ exit(1);
+ }
+ }
+}
+
+
+
+char *url2host (uint8_t *url, char **name, uint32_t *ip, uint32_t *port)
+{
+ uint8_t *murl;
+ struct hostent *hoste;
+ struct in_addr haddr;
+ int found_ip = 1;
+
+ if (!(strncmp(url, "http://", 7)))
+ url += 7;
+
+ *name = strdup(url);
+ if (!(*name)) {
+ *name = NULL;
+ return (NULL);
+ }
+
+ murl = url;
+ while (*murl && *murl != ':' && *murl != '/') {
+ if ((*murl < '0' || *murl > '9') && *murl != '.')
+ found_ip = 0;
+ murl++;
+ }
+
+ (*name)[murl - url] = 0;
+ if (found_ip) {
+ if ((*ip = inet_addr(*name)) == INADDR_NONE)
+ return (NULL);
+ } else {
+ if (!(hoste = gethostbyname(*name)))
+ return (NULL);
+ memcpy (&haddr, hoste->h_addr, sizeof(haddr));
+ *ip = haddr.s_addr;
+ }
+
+ if (!*murl || *murl == '/') {
+ *port = 80;
+ return (murl);
+ }
+ *port = atoi(++murl);
+
+ while (*murl && *murl != '/')
+ murl++;
+ return (murl);
+}
+
+#define ACCEPT "Accept: video/mpeg, video/x-mpegurl, */*\r\n"
+
+int http_open (char *url)
+{
+ char purl[1024], *host, req[1024], *sptr;
+ uint32_t ip;
+ uint32_t port;
+ int sock;
+ int reloc, relocnum = 0;
+ struct sockaddr_in server;
+ int mfd;
+
+ strncpy (purl, url, 1023);
+ purl[1023] = '\0';
+
+ do {
+ host = NULL;
+ strcpy (req, "GET ");
+ if (!(sptr = url2host(purl, &host, &ip, &port))) {
+ fprintf (stderr, "Unknown host\n");
+ exit (1);
+ }
+ strcat (req, sptr);
+ sprintf (req + strlen(req),
+ " HTTP/1.0\r\nUser-Agent: %s/%s\r\n",
+ "whatever", "you want");
+ if (host) {
+ sprintf(req + strlen(req),
+ "Host: %s:%u\r\n", host, port);
+ free (host);
+ }
+
+ strcat (req, ACCEPT);
+ strcat (req, "\r\n");
+
+ server.sin_port = htons(port);
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = ip;
+
+ if ((sock = socket(PF_INET, SOCK_STREAM, 6)) < 0) {
+ perror ("socket");
+ exit (1);
+ }
+
+ if (connect(sock, (struct sockaddr *)&server,
+ sizeof(server))) {
+ perror ("connect");
+ exit (1);
+ }
+
+ write_all (sock, req, strlen(req));
+ if (!(mfd = fileno(fdopen(sock, "rb")))) {
+ perror ("open");
+ exit (1);
+ }
+ reloc = 0;
+ purl[0] = '\0';
+ read_all (mfd, req, 1023);
+ if ((sptr = strchr(req, ' '))) {
+ switch (sptr[1]) {
+ case '2':
+ break;
+ case '3':
+ reloc = 1;
+ default:
+ fprintf (stderr, "HTTP req failed:%s",
+ sptr+1);
+ exit (1);
+ }
+ }
+ do {
+ read_all (mfd,req, 1023);
+ if (!strncmp(req, "Location:", 9))
+ strncpy (purl, req+10, 1023);
+ } while (req[0] != '\r' && req[0] != '\n');
+ } while (reloc && purl[0] && relocnum++ < 3);
+ if (reloc) {
+ fprintf (stderr, "Too many HTTP relocations.\n");
+ exit (1);
+ }
+
+ return sock;
+}
+
+extern int errno;
+const char * strerrno (void)
+{
+ return strerror(errno);
+}
diff --git a/libdvbmpeg/ctools.h b/libdvbmpeg/ctools.h
new file mode 100644
index 0000000..a7b0271
--- /dev/null
+++ b/libdvbmpeg/ctools.h
@@ -0,0 +1,404 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002, 2003 Marcus Metzler
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+#include "ringbuffy.h"
+#include "transform.h"
+
+#ifndef _CTOOLS_H_
+#define _CTOOLS_H_
+
+#define VIDEO_MODE_PAL 0
+#define VIDEO_MODE_NTSC 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ enum {PS_STREAM, TS_STREAM, PES_STREAM};
+ enum {pDUNNO, pPAL, pNTSC};
+
+ uint64_t trans_pts_dts(uint8_t *pts);
+
+/*
+ PES
+*/
+
+#define PROG_STREAM_MAP 0xBC
+#ifndef PRIVATE_STREAM1
+#define PRIVATE_STREAM1 0xBD
+#endif
+#define PADDING_STREAM 0xBE
+#ifndef PRIVATE_STREAM2
+#define PRIVATE_STREAM2 0xBF
+#endif
+#define AUDIO_STREAM_S 0xC0
+#define AUDIO_STREAM_E 0xDF
+#define VIDEO_STREAM_S 0xE0
+#define VIDEO_STREAM_E 0xEF
+#define ECM_STREAM 0xF0
+#define EMM_STREAM 0xF1
+#define DSM_CC_STREAM 0xF2
+#define ISO13522_STREAM 0xF3
+#define PROG_STREAM_DIR 0xFF
+
+#define BUFFYSIZE 10*MAX_PLENGTH
+#define MAX_PTS 8192
+#define MAX_FRAME 8192
+#define MAX_PACK_L 4096
+#define PS_HEADER_L1 14
+#define PS_HEADER_L2 (PS_HEADER_L1+18)
+#define MAX_H_SIZE (PES_H_MIN + PS_HEADER_L1 + 5)
+#define PES_MIN 7
+#define PES_H_MIN 9
+
+//flags1
+#define FLAGS 0x40
+#define SCRAMBLE_FLAGS 0x30
+#define PRIORITY_FLAG 0x08
+#define DATA_ALIGN_FLAG 0x04
+#define COPYRIGHT_FLAG 0x02
+#define ORIGINAL_FLAG 0x01
+
+//flags2
+#define PTS_DTS_FLAGS 0xC0
+#define ESCR_FLAG 0x20
+#define ES_RATE_FLAG 0x10
+#define DSM_TRICK_FLAG 0x08
+#define ADD_CPY_FLAG 0x04
+#define PES_CRC_FLAG 0x02
+#define PES_EXT_FLAG 0x01
+
+//pts_dts flags
+#define PTS_ONLY 0x80
+#define PTS_DTS 0xC0
+
+//private flags
+#define PRIVATE_DATA 0x80
+#define HEADER_FIELD 0x40
+#define PACK_SEQ_CTR 0x20
+#define P_STD_BUFFER 0x10
+#define PES_EXT_FLAG2 0x01
+
+#define MPEG1_2_ID 0x40
+#define STFF_LNGTH_MASK 0x3F
+
+
+ typedef struct pes_packet_{
+ uint8_t stream_id;
+ uint8_t llength[2];
+ uint32_t length;
+ uint8_t flags1;
+ uint8_t flags2;
+ uint8_t pes_hlength;
+ uint8_t pts[5];
+ uint8_t dts[5];
+ uint8_t escr[6];
+ uint8_t es_rate[3];
+ uint8_t trick;
+ uint8_t add_cpy;
+ uint8_t prev_pes_crc[2];
+ uint8_t priv_flags;
+ uint8_t pes_priv_data[16];
+ uint8_t pack_field_length;
+ uint8_t *pack_header;
+ uint8_t pck_sqnc_cntr;
+ uint8_t org_stuff_length;
+ uint8_t p_std[2];
+ uint8_t pes_ext_lngth;
+ uint8_t *pes_ext;
+ uint8_t *pes_pckt_data;
+ int padding;
+ int mpeg;
+ int mpeg1_pad;
+ uint8_t *mpeg1_headr;
+ uint8_t stuffing;
+ } pes_packet;
+
+ void init_pes(pes_packet *p);
+ void kill_pes(pes_packet *p);
+ void setlength_pes(pes_packet *p);
+ void nlength_pes(pes_packet *p);
+ int cwrite_pes(uint8_t *buf, pes_packet *p, long length);
+ void write_pes(int fd, pes_packet *p);
+ int read_pes(int f, pes_packet *p);
+ void cread_pes(char *buf, pes_packet *p);
+
+/*
+ Transport Stream
+*/
+
+#define TS_SIZE 188
+#define TRANS_ERROR 0x80
+#define PAY_START 0x40
+#define TRANS_PRIO 0x20
+#define PID_MASK_HI 0x1F
+//flags
+#define TRANS_SCRMBL1 0x80
+#define TRANS_SCRMBL2 0x40
+#define ADAPT_FIELD 0x20
+#define PAYLOAD 0x10
+#define COUNT_MASK 0x0F
+
+// adaptation flags
+#define DISCON_IND 0x80
+#define RAND_ACC_IND 0x40
+#define ES_PRI_IND 0x20
+#define PCR_FLAG 0x10
+#define OPCR_FLAG 0x08
+#define SPLICE_FLAG 0x04
+#define TRANS_PRIV 0x02
+#define ADAP_EXT_FLAG 0x01
+
+// adaptation extension flags
+#define LTW_FLAG 0x80
+#define PIECE_RATE 0x40
+#define SEAM_SPLICE 0x20
+
+ typedef struct ts_packet_{
+ uint8_t pid[2];
+ uint8_t flags;
+ uint8_t count;
+ uint8_t data[184];
+ uint8_t adapt_length;
+ uint8_t adapt_flags;
+ uint8_t pcr[6];
+ uint8_t opcr[6];
+ uint8_t splice_count;
+ uint8_t priv_dat_len;
+ uint8_t *priv_dat;
+ uint8_t adapt_ext_len;
+ uint8_t adapt_eflags;
+ uint8_t ltw[2];
+ uint8_t piece_rate[3];
+ uint8_t dts[5];
+ int rest;
+ uint8_t stuffing;
+ } ts_packet;
+
+ void init_ts(ts_packet *p);
+ void kill_ts(ts_packet *p);
+ unsigned short pid_ts(ts_packet *p);
+ int cwrite_ts(uint8_t *buf, ts_packet *p, long length);
+ void write_ts(int fd, ts_packet *p);
+ int read_ts(int f, ts_packet *p);
+ void cread_ts (char *buf, ts_packet *p, long length);
+
+
+/*
+ Program Stream
+*/
+
+#define PACK_STUFF_MASK 0x07
+
+#define FIXED_FLAG 0x02
+#define CSPS_FLAG 0x01
+#define SAUDIO_LOCK_FLAG 0x80
+#define SVIDEO_LOCK_FLAG 0x40
+
+#define PS_MAX 200
+
+ typedef struct ps_packet_{
+ uint8_t scr[6];
+ uint8_t mux_rate[3];
+ uint8_t stuff_length;
+ uint8_t *data;
+ uint8_t sheader_llength[2];
+ int sheader_length;
+ uint8_t rate_bound[3];
+ uint8_t audio_bound;
+ uint8_t video_bound;
+ uint8_t reserved;
+ int npes;
+ int mpeg;
+ } ps_packet;
+
+ void init_ps(ps_packet *p);
+ void kill_ps(ps_packet *p);
+ void setlength_ps(ps_packet *p);
+ uint32_t scr_base_ps(ps_packet *p);
+ uint16_t scr_ext_ps(ps_packet *p);
+ int mux_ps(ps_packet *p);
+ int rate_ps(ps_packet *p);
+ int cwrite_ps(uint8_t *buf, ps_packet *p, long length);
+ void write_ps(int fd, ps_packet *p);
+ int read_ps (int f, ps_packet *p);
+ void cread_ps (char *buf, ps_packet *p, long length);
+
+
+
+#define MAX_PLENGTH 0xFFFF
+
+ typedef struct sectionstruct {
+ int id;
+ int length;
+ int found;
+ uint8_t payload[4096+3];
+ } section;
+
+
+ typedef uint32_t tflags;
+#define MAXFILT 32
+#define MASKL 16
+ typedef struct trans_struct {
+ int found;
+ uint8_t packet[188];
+ uint16_t pid[MAXFILT];
+ uint8_t mask[MAXFILT*MASKL];
+ uint8_t filt[MAXFILT*MASKL];
+ uint8_t transbuf[MAXFILT*188];
+ int transcount[MAXFILT];
+ section sec[MAXFILT];
+ tflags is_full;
+ tflags pes_start;
+ tflags pes_started;
+ tflags pes;
+ tflags set;
+ } trans;
+
+
+ void init_trans(trans *p);
+ int set_trans_filt(trans *p, int filtn, uint16_t pid, uint8_t *mask,
+ uint8_t *filt, int pes);
+
+ void clear_trans_filt(trans *p,int filtn);
+ int filt_is_set(trans *p, int filtn);
+ int pes_is_set(trans *p, int filtn);
+ int pes_is_started(trans *p, int filtn);
+ int pes_is_start(trans *p, int filtn);
+ int filt_is_ready(trans *p,int filtn);
+
+ void trans_filt(uint8_t *buf, int count, trans *p);
+ void tfilter(trans *p);
+ void pes_filter(trans *p, int filtn, int off);
+ void sec_filter(trans *p, int filtn, int off);
+ int get_filt_buf(trans *p, int filtn,uint8_t **buf);
+ section *get_filt_sec(trans *p, int filtn);
+
+
+ typedef struct a2pstruct{
+ int type;
+ int fd;
+ int found;
+ int length;
+ int headr;
+ int plength;
+ uint8_t cid;
+ uint8_t flags;
+ uint8_t abuf[MAX_PLENGTH];
+ int alength;
+ uint8_t vbuf[MAX_PLENGTH];
+ int vlength;
+ uint8_t last_av_pts[4];
+ uint8_t av_pts[4];
+ uint8_t scr[4];
+ uint8_t pid0;
+ uint8_t pid1;
+ uint8_t pidv;
+ uint8_t pida;
+ } a2p;
+
+
+
+ void get_pespts(uint8_t *av_pts,uint8_t *pts);
+ void init_a2p(a2p *p);
+ void av_pes_to_pes(uint8_t *buf,int count, a2p *p);
+ int w_pesh(uint8_t id,int length ,uint8_t *pts, uint8_t *obuf);
+ int w_tsh(uint8_t id,int length ,uint8_t *pts, uint8_t *obuf,a2p *p,int startpes);
+ void pts2pts(uint8_t *av_pts, uint8_t *pts);
+ void write_ps_headr(ps_packet *p,uint8_t *pts,int fd);
+
+ typedef struct p2t_s{
+ uint8_t pes[TS_SIZE];
+ uint8_t counter;
+ long int pos;
+ int frags;
+ void (*t_out)(uint8_t const *buf);
+ } p2t_t;
+
+ void twrite(uint8_t const *buf);
+ void init_p2t(p2t_t *p, void (*fkt)(uint8_t const *buf));
+ long int find_pes_header(uint8_t const *buf, long int length, int *frags);
+ void pes_to_ts( uint8_t const *buf, long int length, uint16_t pid, p2t_t *p);
+ void p_to_t( uint8_t const *buf, long int length, uint16_t pid,
+ uint8_t *counter, void (*ts_write)(uint8_t const *));
+
+
+ int write_pes_header(uint8_t id,int length , long PTS,
+ uint8_t *obuf, int stuffing);
+
+ int write_ps_header(uint8_t *buf,
+ uint32_t SCR,
+ long muxr,
+ uint8_t audio_bound,
+ uint8_t fixed,
+ uint8_t CSPS,
+ uint8_t audio_lock,
+ uint8_t video_lock,
+ uint8_t video_bound,
+ uint8_t stream1,
+ uint8_t buffer1_scale,
+ uint32_t buffer1_size,
+ uint8_t stream2,
+ uint8_t buffer2_scale,
+ uint32_t buffer2_size);
+
+
+ int seek_mpg_start(uint8_t *buf, int size);
+
+
+ void split_mpg(char *name, uint64_t size);
+ void cut_mpg(char *name, uint64_t size);
+ int http_open (char *url);
+ ssize_t save_read(int fd, void *buf, size_t count);
+
+ const char * strerrno(void);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /*_CTOOLS_H_*/
diff --git a/libdvbmpeg/devices.hh b/libdvbmpeg/devices.hh
new file mode 100644
index 0000000..02c62cd
--- /dev/null
+++ b/libdvbmpeg/devices.hh
@@ -0,0 +1,310 @@
+#ifndef _channel_hh
+#define _channel_hh
+
+using namespace std;
+#include <stdint.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include "DVB.hh"
+
+#define MAXNAM 80
+#define MAXKEY 15
+
+const int maxname=80;
+const int MAXAPIDS=32;
+const uint32_t UNSET=0xffffffff;
+const uint16_t NOID=0xffff;
+const uint16_t NOPID=0xffff;
+
+class Transponder {
+public:
+ uint16_t id;
+ uint16_t onid;
+ uint16_t satid;
+ int type;
+ char name[maxname+1];
+ uint32_t freq;
+ int pol;
+ int qam;
+ uint32_t srate;
+ int fec;
+ int band;
+ int hp_rate;
+ int lp_rate;
+ int mod;
+ int transmode;
+ int guard;
+ int hierarchy;
+
+ struct Sat *sat;
+
+ Transponder() {
+ name[0]='\0';
+ id = NOID;
+ onid = NOID;
+ satid = NOID;
+ type = 0;
+ }
+
+ friend ostream &operator<<(ostream &stream, Transponder &x);
+ friend istream &operator>>(istream &stream, Transponder &x);
+};
+
+class Sat {
+public:
+ uint16_t id;
+ char name[maxname+1];
+ unsigned int lnbid;
+ struct Lnb *lnb;
+ unsigned int rotorid;
+ unsigned int fmin;
+ unsigned int fmax;
+
+ Sat() {
+ id=NOID;
+ name[0]='\0';
+ lnb=NULL;
+ rotorid=NOID;
+ lnbid=NOID;
+ fmin=fmax=0;
+ };
+ int set(int sid, char *sname, int slnbid, int srotorid) {
+ return 0;
+ };
+
+ friend ostream &operator<<(ostream &stream, Sat &x);
+ friend istream &operator>>(istream &stream, Sat &x);
+};
+
+
+class Lnb {
+public:
+ Sat *sat;
+ uint16_t id;
+ struct DVB *dvbd;
+ char name[maxname+1];
+ int type;
+ unsigned int lof1;
+ unsigned int lof2;
+ unsigned int slof;
+ int diseqcnr;
+ uint16_t diseqcid;
+ uint16_t swiid;
+
+
+ void cpy (const Lnb &olnb){
+ this->id=olnb.id;
+ this->type=olnb.type;
+ this->lof1=olnb.lof1;
+ this->lof2=olnb.lof2;
+ this->slof=olnb.slof;
+ this->diseqcnr=olnb.diseqcnr;
+ this->diseqcid=olnb.diseqcid;
+ this->swiid=olnb.swiid;
+ strncpy(this->name,olnb.name,maxname);
+ }
+
+ void init(int t, uint l1, uint l2, uint sl,
+ int dnr, int disid, int sw) {
+ type=t;
+ lof1=l1;
+ lof2=l2;
+ slof=sl;
+ diseqcnr=dnr;
+ diseqcid=disid;
+ swiid=sw;
+ dvbd=0;
+ name[0]='\0';
+ }
+
+ Lnb () {
+ lof1=lof2=slof=0;
+ swiid=NOID;
+ diseqcid=NOID;
+ diseqcnr=-1;
+ name[0]='\0';
+ }
+
+ Lnb (const Lnb &olnb){
+ cpy(olnb);
+ }
+
+
+
+ friend ostream &operator<<(ostream &stream, Lnb &x);
+ friend istream &operator>>(istream &stream, Lnb &x);
+};
+
+struct diseqcmsg {
+ int burst;
+ int len;
+ unsigned char msg[8];
+};
+
+class DiSEqC {
+public:
+ uint16_t id;
+ char name[maxname+1];
+ diseqcmsg msgs[4];
+
+ friend ostream &operator<<(ostream &stream, DiSEqC &x);
+ friend istream &operator>>(istream &stream, DiSEqC &x);
+};
+
+class Rotor {
+public:
+ uint16_t id;
+ char name[maxname+1];
+ diseqcmsg msgs[4];
+
+ friend ostream &operator<<(ostream &stream, Rotor &x);
+ friend istream &operator>>(istream &stream, Rotor &x);
+};
+
+class Switch {
+public:
+ uint16_t id;
+ int switchid;
+ char name[maxname+1];
+ diseqcmsg msg;
+
+ friend ostream &operator<<(ostream &stream, Switch &x);
+ friend istream &operator>>(istream &stream, Switch &x);
+};
+
+class Network {
+public:
+ uint16_t id;
+ char name[maxname+1];
+
+ friend ostream &operator<<(ostream &stream, Network &x);
+ friend istream &operator>>(istream &stream, Network &x);
+};
+
+class Bouquet {
+public:
+ uint16_t id;
+ char name[maxname+1];
+
+ friend ostream &operator<<(ostream &stream, Bouquet &x);
+ friend istream &operator>>(istream &stream, Bouquet &x);
+};
+
+
+#define MAX_ECM 16
+#define MAX_ECM_DESC 256
+typedef struct ecm_struct {
+ int num;
+ uint16_t sysid[MAX_ECM];
+ uint16_t pid[MAX_ECM];
+ uint16_t length[MAX_ECM];
+ uint8_t data[MAX_ECM*MAX_ECM_DESC];
+} ecm_t;
+
+
+
+class Channel{
+public:
+ Channel *next;
+ uint32_t id;
+ char name[maxname+1];
+ int32_t type;
+ int checked;
+
+ uint16_t pnr;
+ uint16_t vpid;
+ uint16_t apids[MAXAPIDS];
+ char apids_name[MAXAPIDS*4];
+ int32_t apidnum;
+ int last_apidn;
+ uint16_t ac3pid;
+ uint16_t ttpid;
+ uint16_t pmtpid;
+ uint16_t pcrpid;
+ uint16_t casystem;
+ uint16_t capid;
+
+ ecm_t ecm;
+ int (*ecm_callback)(Channel *chan);
+
+ int has_eit;
+ int pres_follow;
+
+ uint16_t satid;
+ uint16_t tpid;
+ uint16_t onid;
+ uint16_t bid;
+ int8_t eit_ver_n;
+ int8_t eit_ver_c;
+
+ void clearall(void) {
+ id=UNSET;
+ name[0]='\0';
+ type=0;
+ checked = 0;
+ has_eit = -1;
+ pres_follow = -1;
+ eit_ver_c = -1;
+ eit_ver_n = -1;
+
+ pnr=NOPID;
+ vpid=NOPID;
+ memset(apids, 0, sizeof(uint16_t)*MAXAPIDS);
+ memset(apids_name, 0, sizeof(char)*MAXAPIDS*4);
+ apidnum=0;
+ last_apidn=-1;
+ ac3pid=NOPID;
+ ttpid=NOPID;
+ pmtpid=NOPID;
+ pcrpid=NOPID;
+ capid=NOPID;
+
+ satid=NOID;
+ tpid=NOID;
+ onid=NOID;
+ bid=NOID;
+ ecm_callback = NULL;
+ memset(&ecm,0, sizeof(ecm_t));
+ };
+
+ Channel() {
+ clearall();
+ }
+
+ Channel(int cid, char *nam, int ty, int prognr,
+ int vid, int aid, int tid) {
+ int l=strlen(nam);
+
+ clearall();
+ if (l>maxname){
+ cerr << "" << endl;
+ l=maxname;
+ }
+ strncpy(name, nam, l);
+ name[l]='\0';
+ type=ty;
+ pnr=prognr;
+ vpid=vid;
+ apids[0]=aid;
+ }
+
+#ifdef DEBUG
+ ~Channel(){
+ cerr <<"Channel " << name << " destroyed" << endl;
+ }
+#endif
+
+ friend ostream &operator<<(ostream &stream, Channel &x);
+ friend istream &operator>>(istream &stream, Channel &x);
+};
+
+int findkey(char *name, char *keys[]);
+void getname(char *name,istream &ins);
+#endif /*channel.h*/
diff --git a/libdvbmpeg/osd.hh b/libdvbmpeg/osd.hh
new file mode 100644
index 0000000..9c6b530
--- /dev/null
+++ b/libdvbmpeg/osd.hh
@@ -0,0 +1,84 @@
+#ifndef _OSD_HH_
+#define _OSD_HH_
+
+extern "C" {
+#include "OSD.h"
+}
+struct OSD {
+ int dev;
+
+ void init(int d) {
+ dev=d;
+ }
+ int Open(int x0, int y0, int x1, int y1, int BitPerPixel, int mix, int win) {
+ if (OSDSetWindow(dev, win))
+ return -1;
+ return OSDOpen(dev, x0, y0, x1, y1, BitPerPixel, mix);
+ }
+ int Open(int x0, int y0, int x1, int y1, int BitPerPixel, int mix) {
+ return OSDOpen(dev, x0, y0, x1, y1, BitPerPixel, mix);
+ }
+ int Close(int win) {
+ if (OSDSetWindow(dev, win))
+ return -1;
+ return OSDClose(dev);
+ }
+ int Close(void) {
+ return OSDClose(dev);
+ }
+ int Show(void) {
+ return OSDShow(dev);
+ }
+ int Hide(void) {
+ return OSDHide(dev);
+ }
+ int Clear(void) {
+ return OSDClear(dev);
+ }
+ int Fill(int color) {
+ return OSDFill(dev, color);
+ }
+ int SetColor(int color, int r, int g, int b, int op) {
+ return OSDSetColor(dev, color, r, g, b, op);
+ }
+ int Text(int x, int y, int size, int color, const char *text) {
+ return OSDText(dev, x, y, size, color, text);
+ }
+ int SetPalette(int first, int last, unsigned char *data) {
+ return OSDSetPalette(dev, first, last, data);
+
+ }
+ int SetTrans(int trans) {
+ return OSDSetTrans(dev, trans);
+
+ }
+ int SetPixel(int dev, int x, int y, unsigned int color) {
+ return OSDSetPixel(dev, x, y, color);
+ }
+ int GetPixel(int dev, int x, int y) {
+ return OSDGetPixel(dev, x, y);
+ }
+ int SetRow(int x, int y, int x1, unsigned char *data) {
+ return OSDSetRow(dev, x, y, x1, data);
+ }
+ int SetBlock(int x, int y, int x1, int y1, int inc, unsigned char *data) {
+ return OSDSetBlock(dev, x, y, x1, y1, inc, data);
+ }
+ int FillRow(int x, int y, int x1, int color) {
+ return OSDFillRow(dev, x, y, x1, color);
+ }
+ int FillBlock(int x, int y, int x1, int y1, int color) {
+ return OSDFillBlock(dev, x, y, x1, y1, color);
+ }
+ int Line(int x, int y, int x1, int y1, int color) {
+ return OSDLine(dev, x, y, x1, y1, color);
+ }
+ int Query() {
+ return OSDQuery(dev);
+ }
+ int SetWindow(int win) {
+ return OSDSetWindow(dev, win);
+ }
+};
+
+#endif
diff --git a/libdvbmpeg/remux.c b/libdvbmpeg/remux.c
new file mode 100644
index 0000000..6f8a44f
--- /dev/null
+++ b/libdvbmpeg/remux.c
@@ -0,0 +1,1215 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de,
+ */
+
+#include "remux.h"
+
+unsigned int bitrates[3][16] =
+{{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},
+ {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0},
+ {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}};
+
+uint32_t freq[4] = {441, 480, 320, 0};
+static uint32_t samples[4] = { 384, 1152, 0, 0};
+char *frames[3] = {"I-Frame","P-Frame","B-Frame"};
+
+
+void copy_ptslm(PTS_List *a, PTS_List *b)
+{
+ a->pos = b->pos;
+ a->PTS = b->PTS;
+ a->dts = b->dts;
+ a->spos = b->spos;
+}
+
+void clear_ptslm(PTS_List *a)
+{
+ a->pos = 0;
+ a->PTS = 0;
+ a->dts = 0;
+ a->spos = 0;
+}
+
+void init_ptsl(PTS_List *ptsl)
+{
+ int i;
+ for (i=0;i< MAX_PTS;i++){
+ clear_ptslm(&ptsl[i]);
+ }
+}
+
+int del_pts(PTS_List *ptsl, int pos, int nr)
+{
+ int i;
+ int del = 0;
+
+ for( i = 0; i < nr-1; i++){
+ if(pos > ptsl[i].pos && pos >= ptsl[i+1].pos) del++;
+ }
+
+ if(del)
+ for( i = 0; i < nr-del; i++){
+ copy_ptslm(&ptsl[i], &ptsl[i+del]);
+ }
+
+ return nr-del;
+}
+
+int del_ptss(PTS_List *ptsl, int pts, int *nb)
+{
+ int i;
+ int del = 0;
+ int sum = 0;
+ int nr = *nb;
+
+ for( i = 0; i < nr; i++){
+ if(pts > ptsl[i].PTS){
+ del++;
+ sum += ptsl[i].pos;
+ }
+ }
+
+ if(del)
+ for( i = 0; i < nr-del; i++){
+ copy_ptslm(&ptsl[i], &ptsl[i+del]);
+ }
+
+ *nb = nr-del;
+ return sum;
+}
+
+int add_pts(PTS_List *ptsl, uint32_t pts, int pos, int spos, int nr, uint32_t dts)
+{
+ int i;
+
+ for ( i=0;i < nr; i++) if (spos && ptsl[i].pos == pos) return nr;
+ if (nr == MAX_PTS) {
+ nr = del_pts(ptsl, ptsl[1].pos+1, nr);
+ } else nr++;
+ i = nr-1;
+
+ ptsl[i].pos = pos;
+ ptsl[i].spos = spos;
+ ptsl[i].PTS = pts;
+ ptsl[i].dts = dts;
+ return nr;
+}
+
+void add_vpts(Remux *rem, uint8_t *pts)
+{
+ uint32_t PTS = trans_pts_dts(pts);
+ rem->vptsn = add_pts(rem->vpts_list, PTS, rem->vwrite, rem->awrite,
+ rem->vptsn, PTS);
+}
+
+void add_apts(Remux *rem, uint8_t *pts)
+{
+ uint32_t PTS = trans_pts_dts(pts);
+ rem->aptsn = add_pts(rem->apts_list, PTS, rem->awrite, rem->vwrite,
+ rem->aptsn, PTS);
+}
+
+void del_vpts(Remux *rem)
+{
+ rem->vptsn = del_pts(rem->vpts_list, rem->vread, rem->vptsn);
+}
+
+void del_apts(Remux *rem)
+{
+ rem->aptsn = del_pts(rem->apts_list, rem->aread, rem->aptsn);
+}
+
+
+void copy_framelm(FRAME_List *a, FRAME_List *b)
+{
+ a->type = b->type;
+ a->pos = b->pos;
+ a->FRAME = b->FRAME;
+ a->time = b->time;
+ a->pts = b->pts;
+ a->dts = b->dts;
+}
+
+void clear_framelm(FRAME_List *a)
+{
+ a->type = 0;
+ a->pos = 0;
+ a->FRAME = 0;
+ a->time = 0;
+ a->pts = 0;
+ a->dts = 0;
+}
+
+void init_framel(FRAME_List *framel)
+{
+ int i;
+ for (i=0;i< MAX_FRAME;i++){
+ clear_framelm(&framel[i]);
+ }
+}
+
+int del_frame(FRAME_List *framel, int pos, int nr)
+{
+ int i;
+ int del = 0;
+
+ for( i = 0; i < nr-1; i++){
+ if(pos > framel[i].pos && pos >= framel[i+1].pos) del++;
+ }
+
+ if(del)
+ for( i = 0; i < nr-del; i++){
+ copy_framelm(&framel[i], &framel[i+del]);
+ }
+
+ return nr-del;
+}
+
+int add_frame(FRAME_List *framel, uint32_t frame, int pos, int type, int nr,
+ uint32_t time, uint32_t pts, uint32_t dts)
+{
+ int i;
+
+ if (nr == MAX_FRAME) {
+ nr = del_frame(framel, framel[1].pos+1, nr);
+ } else nr++;
+ i = nr-1;
+
+ framel[i].type = type;
+ framel[i].pos = pos;
+ framel[i].FRAME = frame;
+ framel[i].time = time;
+ framel[i].pts = pts;
+ framel[i].dts = dts;
+ return nr;
+}
+
+void add_vframe(Remux *rem, uint32_t frame, long int pos, int type, int time,
+ uint32_t pts, uint32_t dts)
+{
+ rem->vframen = add_frame(rem->vframe_list, frame, pos, type,
+ rem->vframen, time, pts, dts);
+}
+
+void add_aframe(Remux *rem, uint32_t frame, long int pos, uint32_t pts)
+{
+ rem->aframen = add_frame(rem->aframe_list, frame, pos, 0,
+ rem->aframen, 0, pts, pts);
+}
+
+void del_vframe(Remux *rem)
+{
+ rem->vframen = del_frame(rem->vframe_list, rem->vread, rem->vframen);
+}
+
+void del_aframe(Remux *rem)
+{
+ rem->aframen = del_frame(rem->aframe_list, rem->aread, rem->aframen);
+}
+
+
+void printpts(uint32_t pts)
+{
+ fprintf(stderr,"%2d:%02d:%02d.%03d",
+ (int)(pts/90000.)/3600,
+ ((int)(pts/90000.)%3600)/60,
+ ((int)(pts/90000.)%3600)%60,
+ (((int)(pts/90.)%3600000)%60000)%1000
+ );
+}
+
+
+void find_vframes( Remux *rem, uint8_t *buf, int l)
+{
+ int c = 0;
+ int type;
+ uint32_t time = 0;
+ int hour;
+ int min;
+ int sec;
+ uint64_t pts=0;
+ uint64_t dts=0;
+ uint32_t tempref = 0;
+
+ while ( c < l - 6){
+ if (buf[c] == 0x00 &&
+ buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01 &&
+ buf[c+3] == 0xB8) {
+ c += 4;
+ hour = (int)((buf[c]>>2)& 0x1F);
+ min = (int)(((buf[c]<<4)& 0x30)|
+ ((buf[c+1]>>4)& 0x0F));
+ sec = (int)(((buf[c+1]<<3)& 0x38)|
+ ((buf[c+2]>>5)& 0x07));
+
+ time = 3600*hour + 60*min + sec;
+ if ( rem->time_off){
+ time = (uint32_t)((uint64_t)time - rem->time_off);
+ hour = time/3600;
+ min = (time%3600)/60;
+ sec = (time%3600)%60;
+ /*
+ buf[c] |= (hour & 0x1F) << 2;
+ buf[c] |= (min & 0x30) >> 4;
+ buf[c+1] |= (min & 0x0F) << 4;
+ buf[c+1] |= (sec & 0x38) >> 3;
+ buf[c+2] |= (sec & 0x07) >> 5;*/
+ }
+ rem->group++;
+ rem->groupframe = 0;
+ }
+ if ( buf[c] == 0x00 &&
+ buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01 &&
+ buf[c+3] == 0x00) {
+ c += 4;
+ tempref = (buf[c+1]>>6) & 0x03;
+ tempref |= buf[c] << 2;
+
+ type = ((buf[c+1]&0x38) >>3);
+ if ( rem->video_info.framerate){
+ pts = ((uint64_t)rem->vframe + tempref + 1
+ - rem->groupframe ) * 90000ULL
+ /rem->video_info.framerate
+ + rem->vpts_off;
+ dts = (uint64_t)rem->vframe * 90000ULL/
+ rem->video_info.framerate
+ + rem->vpts_off;
+
+
+fprintf(stderr,"MYPTS:");
+printpts((uint32_t)pts-rem->vpts_off);
+ fprintf(stderr," REALPTS:");
+ printpts(rem->vpts_list[rem->vptsn-1].PTS-rem->vpts_off);
+ fprintf(stderr," DIFF:");
+ printpts(pts-(uint64_t)rem->vpts_list[rem->vptsn-1].PTS);
+// fprintf(stderr," DIST: %4d",-rem->vpts_list[rem->vptsn-1].pos+(rem->vwrite+c-4));
+ //fprintf(stderr," ERR: %3f",(double)(-rem->vpts_list[rem->vptsn-1].PTS+pts)/(rem->vframe+1));
+ fprintf(stderr,"\r");
+
+
+
+ rem->vptsn = add_pts(rem->vpts_list,(uint32_t)pts
+ ,rem->vwrite+c-4,
+ rem->awrite,
+ rem->vptsn,
+ (uint32_t)dts);
+
+
+
+ }
+ rem->vframe++;
+ rem->groupframe++;
+ add_vframe( rem, rem->vframe, rem->vwrite+c, type,
+ time, pts, dts);
+ } else c++;
+ }
+}
+
+void find_aframes( Remux *rem, uint8_t *buf, int l)
+{
+ int c = 0;
+ uint64_t pts = 0;
+ int sam;
+ uint32_t fr;
+
+
+ while ( c < l - 2){
+ if ( buf[c] == 0xFF &&
+ (buf[c+1] & 0xF8) == 0xF8) {
+ c += 2;
+ if ( rem->audio_info.layer >= 0){
+ sam = samples[3-rem->audio_info.layer];
+ fr = freq[rem->audio_info.frequency] ;
+
+ pts = ( (uint64_t)rem->aframe * sam * 900ULL)/fr
+ + rem->apts_off;
+
+
+fprintf(stderr,"MYPTS:");
+printpts((uint32_t)pts-rem->apts_off);
+ fprintf(stderr," REALPTS:");
+ printpts(rem->apts_list[rem->aptsn-1].PTS-rem->apts_off);
+ fprintf(stderr," DIFF:");
+ printpts((uint32_t)((uint64_t)rem->apts_list[rem->aptsn-1].PTS-pts));
+// fprintf(stderr," DIST: %4d",-rem->apts_list[rem->aptsn-1].pos+(rem->awrite+c-2));
+ fprintf(stderr,"\r");
+
+ rem->aptsn = add_pts(rem->apts_list,(uint32_t)pts
+ ,rem->awrite+c-2,
+ rem->vwrite,
+ rem->aptsn,
+ (uint32_t)pts);
+ }
+
+ rem->aframe++;
+ add_aframe( rem, rem->aframe, rem->awrite+c, pts);
+
+ } else c++;
+ }
+}
+
+int refill_buffy(Remux *rem)
+{
+ pes_packet pes;
+ int count = 0;
+ int acount, vcount;
+ ringbuffy *vbuf = &rem->vid_buffy;
+ ringbuffy *abuf = &rem->aud_buffy;
+ int fin = rem->fin;
+
+ acount = abuf->size-ring_rest(abuf);
+ vcount = vbuf->size-ring_rest(vbuf);
+
+
+ while ( acount > MAX_PLENGTH && vcount > MAX_PLENGTH && count < 10){
+ int neof;
+ count++;
+ init_pes(&pes);
+ if ((neof = read_pes(fin,&pes)) <= 0) return -1;
+ switch(pes.stream_id){
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ rem->apes++;
+ if( rem->audio_info.layer < 0 &&
+ (pes.flags2 & PTS_DTS) )
+ add_apts(rem, pes.pts);
+ find_aframes( rem, pes.pes_pckt_data, pes.length);
+ ring_write(abuf,(char *)pes.pes_pckt_data,pes.length);
+ rem->awrite += pes.length;
+ break;
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ rem->vpes++;
+ if( !rem->video_info.framerate &&
+ (pes.flags2 & PTS_DTS) )
+ add_vpts(rem, pes.pts);
+
+ find_vframes( rem, pes.pes_pckt_data, pes.length);
+
+ ring_write(vbuf,(char *)pes.pes_pckt_data,pes.length);
+ rem->vwrite += pes.length;
+ break;
+ }
+ acount = abuf->size-ring_rest(abuf);
+ vcount = vbuf->size-ring_rest(vbuf);
+ kill_pes(&pes);
+ }
+ if (count < 10) return 0;
+ return 1;
+}
+
+int vring_read( Remux *rem, uint8_t *buf, int l)
+{
+ int c = 0;
+ int r = 0;
+
+ if (ring_rest(&rem->vid_buffy) <= l)
+ r = refill_buffy(rem);
+ if (r) return -1;
+
+ c = ring_read(&rem->vid_buffy, (char *) buf, l);
+ rem->vread += c;
+ del_vpts(rem);
+ del_vframe(rem);
+ return c;
+}
+
+int aring_read( Remux *rem, uint8_t *buf, int l)
+{
+ int c = 0;
+ int r = 0;
+
+ if (ring_rest(&rem->aud_buffy) <= l)
+ r = refill_buffy(rem);
+ if (r) return -1;
+
+ c = ring_read(&rem->aud_buffy, (char *)buf, l);
+ rem->aread += c;
+ del_apts(rem);
+ del_aframe(rem);
+ return c;
+}
+
+int vring_peek( Remux *rem, uint8_t *buf, int l, long off)
+{
+ int c = 0;
+
+ if (ring_rest(&rem->vid_buffy) <= l)
+ refill_buffy(rem);
+
+ c = ring_peek(&rem->vid_buffy, (char *) buf, l, off);
+ return c;
+}
+
+int aring_peek( Remux *rem, uint8_t *buf, int l, long off)
+{
+ int c = 0;
+
+ if (ring_rest(&rem->aud_buffy) <= l)
+ refill_buffy(rem);
+
+ c = ring_peek(&rem->aud_buffy, (char *)buf, l, off);
+ return c;
+}
+
+
+int get_video_info(Remux *rem)
+{
+ uint8_t buf[12];
+ uint8_t *headr;
+ int found = 0;
+ int sw;
+ long off = 0;
+ int form = -1;
+ ringbuffy *vid_buffy = &rem->vid_buffy;
+ VideoInfo *vi = &rem->video_info;
+
+ while (found < 4 && ring_rest(vid_buffy)){
+ uint8_t b[3];
+
+ vring_peek( rem, b, 4, 0);
+ if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01
+ && b[3] == 0xb3) found = 4;
+ else {
+ off++;
+ vring_read( rem, b, 1);
+ }
+ }
+ rem->vframe = rem->vframen-1;
+
+ if (! found) return -1;
+ buf[0] = 0x00; buf[1] = 0x00; buf[2] = 0x01; buf[3] = 0xb3;
+ headr = buf+4;
+ if(vring_peek(rem, buf, 12, 0) < 12) return -1;
+
+ vi->horizontal_size = ((headr[1] &0xF0) >> 4) | (headr[0] << 4);
+ vi->vertical_size = ((headr[1] &0x0F) << 8) | (headr[2]);
+
+ sw = (int)((headr[3]&0xF0) >> 4) ;
+
+ switch( sw ){
+ case 1:
+ fprintf(stderr,"Videostream: ASPECT: 1:1");
+ vi->aspect_ratio = 100;
+ break;
+ case 2:
+ fprintf(stderr,"Videostream: ASPECT: 4:3");
+ vi->aspect_ratio = 133;
+ break;
+ case 3:
+ fprintf(stderr,"Videostream: ASPECT: 16:9");
+ vi->aspect_ratio = 177;
+ break;
+ case 4:
+ fprintf(stderr,"Videostream: ASPECT: 2.21:1");
+ vi->aspect_ratio = 221;
+ break;
+
+ case 5 ... 15:
+ fprintf(stderr,"Videostream: ASPECT: reserved");
+ vi->aspect_ratio = 0;
+ break;
+
+ default:
+ vi->aspect_ratio = 0;
+ return -1;
+ }
+
+ fprintf(stderr," Size = %dx%d",vi->horizontal_size,vi->vertical_size);
+
+ sw = (int)(headr[3]&0x0F);
+
+ switch ( sw ) {
+ case 1:
+ fprintf(stderr," FRate: 23.976 fps");
+ vi->framerate = 24000/1001.;
+ form = -1;
+ break;
+ case 2:
+ fprintf(stderr," FRate: 24 fps");
+ vi->framerate = 24;
+ form = -1;
+ break;
+ case 3:
+ fprintf(stderr," FRate: 25 fps");
+ vi->framerate = 25;
+ form = VIDEO_MODE_PAL;
+ break;
+ case 4:
+ fprintf(stderr," FRate: 29.97 fps");
+ vi->framerate = 30000/1001.;
+ form = VIDEO_MODE_NTSC;
+ break;
+ case 5:
+ fprintf(stderr," FRate: 30 fps");
+ vi->framerate = 30;
+ form = VIDEO_MODE_NTSC;
+ break;
+ case 6:
+ fprintf(stderr," FRate: 50 fps");
+ vi->framerate = 50;
+ form = VIDEO_MODE_PAL;
+ break;
+ case 7:
+ fprintf(stderr," FRate: 60 fps");
+ vi->framerate = 60;
+ form = VIDEO_MODE_NTSC;
+ break;
+ }
+
+ rem->dts_delay = (int)(7.0/vi->framerate/2.0*90000);
+
+ vi->bit_rate = 400*(((headr[4] << 10) & 0x0003FC00UL)
+ | ((headr[5] << 2) & 0x000003FCUL) |
+ (((headr[6] & 0xC0) >> 6) & 0x00000003UL));
+
+ fprintf(stderr," BRate: %.2f Mbit/s",(vi->bit_rate)/1000000.);
+
+ fprintf(stderr,"\n");
+ vi->video_format = form;
+
+ /*
+ marker_bit (&video_bs, 1);
+ vi->vbv_buffer_size = getbits (&video_bs, 10);
+ vi->CSPF = get1bit (&video_bs);
+ */
+ return form;
+}
+
+
+int get_audio_info( Remux *rem)
+{
+ uint8_t *headr;
+ uint8_t buf[3];
+ long off = 0;
+ int found = 0;
+ ringbuffy *aud_buffy = &rem->aud_buffy;
+ AudioInfo *ai = &rem->audio_info;
+
+ while(!ring_rest(aud_buffy) && !refill_buffy(rem));
+ while (found < 2 && ring_rest(aud_buffy)){
+ uint8_t b[2];
+ refill_buffy(rem);
+ aring_peek( rem, b, 2, 0);
+
+ if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8)
+ found = 2;
+ else {
+ off++;
+ aring_read( rem, b, 1);
+ }
+ }
+
+ if (!found) return -1;
+ rem->aframe = rem->aframen-1;
+
+ if (aring_peek(rem, buf, 3, 0) < 1) return -1;
+ headr = buf+2;
+
+ ai->layer = (buf[1] & 0x06) >> 1;
+
+ fprintf(stderr,"Audiostream: Layer: %d", 4-ai->layer);
+
+
+ ai->bit_rate = bitrates[(3-ai->layer)][(headr[0] >> 4 )]*1000;
+
+ if (ai->bit_rate == 0)
+ fprintf (stderr," Bit rate: free");
+ else if (ai->bit_rate == 0xf)
+ fprintf (stderr," BRate: reserved");
+ else
+ fprintf (stderr," BRate: %d kb/s", ai->bit_rate/1000);
+
+
+ ai->frequency = (headr[0] & 0x0c ) >> 2;
+ if (ai->frequency == 3)
+ fprintf (stderr, " Freq: reserved\n");
+ else
+ fprintf (stderr," Freq: %2.1f kHz\n",
+ freq[ai->frequency]/10.);
+
+ return 0;
+}
+
+
+
+void init_remux(Remux *rem, int fin, int fout, int mult)
+{
+ rem->video_info.framerate = 0;
+ rem->audio_info.layer = -1;
+ rem->fin = fin;
+ rem->fout = fout;
+ ring_init(&rem->vid_buffy, 40*BUFFYSIZE*mult);
+ ring_init(&rem->aud_buffy, BUFFYSIZE*mult);
+ init_ptsl(rem->vpts_list);
+ init_ptsl(rem->apts_list);
+ init_framel(rem->vframe_list);
+ init_framel(rem->aframe_list);
+
+ rem->vptsn = 0;
+ rem->aptsn = 0;
+ rem->vframen = 0;
+ rem->aframen = 0;
+ rem->vframe = 0;
+ rem->aframe = 0;
+ rem->vcframe = 0;
+ rem->acframe = 0;
+ rem->vpts = 0;
+ rem->vdts = 0;
+ rem->apts_off = 0;
+ rem->vpts_off = 0;
+ rem->apts_delay= 0;
+ rem->vpts_delay= 0;
+ rem->dts_delay = 0;
+ rem->apts = 0;
+ rem->vpes = 0;
+ rem->apes = 0;
+ rem->vpts_old = 0;
+ rem->apts_old = 0;
+ rem->SCR = 0;
+ rem->vwrite = 0;
+ rem->awrite = 0;
+ rem->vread = 0;
+ rem->aread = 0;
+ rem->group = 0;
+ rem->groupframe= 0;
+ rem->pack_size = 0;
+ rem->muxr = 0;
+ rem->time_off = 0;
+}
+
+uint32_t bytes2pts(int bytes, int rate)
+{
+ if (bytes < 0xFFFFFFFFUL/720000UL)
+ return (uint32_t)(bytes*720000UL/rate);
+ else
+ return (uint32_t)(bytes/rate*720000UL);
+}
+
+long pts2bytes( uint32_t pts, int rate)
+{
+ if (pts < 0xEFFFFFFFUL/rate)
+ return (pts*rate/720000);
+ else
+ return (pts* (rate/720000));
+}
+
+int write_audio_pes( Remux *rem, uint8_t *buf, int *alength)
+{
+ int add;
+ int pos = 0;
+ int p = 0;
+ uint32_t pts = 0;
+ int stuff = 0;
+ int length = *alength;
+
+ if (!length) return 0;
+ p = PS_HEADER_L1+PES_H_MIN;
+
+ if (rem->apts_old != rem->apts){
+ pts = (uint32_t)((uint64_t)rem->apts + rem->apts_delay - rem->apts_off);
+ p += 5;
+ }
+ if ( length+p >= rem->pack_size){
+ length = rem->pack_size;
+ } else {
+ if (rem->pack_size-length-p <= PES_MIN){
+ stuff = rem->pack_size - length;
+ length = rem->pack_size;
+ } else
+ length = length+p;
+ }
+ pos = write_ps_header(buf,rem->SCR,rem->muxr, 1, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0);
+
+ pos += write_pes_header( 0xC0, length-pos, pts, buf+pos, stuff);
+ add = aring_read( rem, buf+pos, length-pos);
+ *alength = add;
+ if (add < 0) return -1;
+ pos += add;
+ rem->apts_old = rem->apts;
+ rem->apts = rem->apts_list[0].PTS;
+
+ if (pos+PES_MIN < rem->pack_size){
+ pos += write_pes_header( PADDING_STREAM, rem->pack_size-pos, 0,
+ buf+pos, 0);
+ pos = rem->pack_size;
+ }
+ if (pos != rem->pack_size) {
+ fprintf(stderr,"apos: %d\n",pos);
+ exit(1);
+ }
+
+ return pos;
+}
+
+int write_video_pes( Remux *rem, uint8_t *buf, int *vlength)
+{
+ int add;
+ int pos = 0;
+ int p = 0;
+ uint32_t pts = 0;
+ uint32_t dts = 0;
+ int stuff = 0;
+ int length = *vlength;
+ long diff = 0;
+
+ if (! length) return 0;
+ p = PS_HEADER_L1+PES_H_MIN;
+
+ if (rem->vpts_old != rem->vpts){
+ pts = (uint32_t)((uint64_t)rem->vpts + rem->vpts_delay - rem->vpts_off);
+ p += 5;
+ }
+ if ( length+p >= rem->pack_size){
+ length = rem->pack_size;
+ } else {
+ if (rem->pack_size - length - p <= PES_MIN){
+ stuff = rem->pack_size - length;
+ length = rem->pack_size;
+ } else
+ length = length+p;
+ }
+
+ pos = write_ps_header(buf,rem->SCR,rem->muxr, 1, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0);
+
+ pos += write_pes_header( 0xE0, length-pos, pts, buf+pos, stuff);
+ add = vring_read( rem, buf+pos, length-pos);
+ *vlength = add;
+ if (add < 0) return -1;
+ pos += add;
+ rem->vpts_old = rem->vpts;
+ dts = rem->vdts;
+ rem->vpts = rem->vpts_list[0].PTS;
+ rem->vdts = rem->vpts_list[0].dts;
+ if ( diff > 0) rem->SCR += diff;
+ if (pos+PES_MIN < rem->pack_size){
+ // fprintf(stderr,"vstuffing: %d \n",rem->pack_size-pos);
+ pos += write_pes_header( PADDING_STREAM, rem->pack_size-pos, 0,
+ buf+pos, 0);
+ pos = rem->pack_size;
+ }
+ return pos;
+}
+
+void print_info( Remux *rem , int ret)
+{
+ int newtime = 0;
+ static int time = 0;
+ int i = 0;
+
+ while(! newtime && i < rem->vframen) {
+ if( (newtime = rem->vframe_list[i].time)) break;
+ i++;
+ }
+ if (newtime) time = newtime;
+
+ fprintf(stderr,"SCR:");
+ printpts(rem->SCR);
+ fprintf(stderr," VDTS:");
+ printpts((uint32_t)((uint64_t)rem->vdts - rem->vpts_off + rem->vpts_delay));
+ fprintf(stderr," APTS:");
+ printpts((uint32_t)((uint64_t)rem->apts - rem->apts_off + rem->apts_delay));
+ fprintf(stderr," TIME:%2d:", time/3600);
+ fprintf(stderr,"%02d:", (time%3600)/60);
+ fprintf(stderr,"%02d", (time%3600)%60);
+ if (ret) fprintf(stderr,"\n");
+ else fprintf(stderr,"\r");
+}
+
+void remux(int fin, int fout, int pack_size, int mult)
+{
+ Remux rem;
+ long ptsdiff;
+ uint8_t buf[MAX_PACK_L];
+ long pos = 0;
+ int r = 0;
+ int i, r1, r2;
+ long packets = 0;
+ uint8_t mpeg_end[4] = { 0x00, 0x00, 0x01, 0xB9 };
+ uint32_t SCR_inc = 0;
+ int data_size;
+ long vbuf, abuf;
+ long vbuf_max, abuf_max;
+ PTS_List abufl[MAX_PTS];
+ PTS_List vbufl[MAX_PTS];
+ int abufn = 0;
+ int vbufn = 0;
+ uint64_t pts_d = 0;
+ int ok_audio;
+ int ok_video;
+ uint32_t apos = 0;
+ uint32_t vpos = 0;
+ int vpack_size = 0;
+ int apack_size = 0;
+
+ init_ptsl(abufl);
+ init_ptsl(vbufl);
+
+ if (mult < 0 || mult >1000){
+ fprintf(stderr,"Multipler too large\n");
+ exit(1);
+ }
+ init_remux(&rem, fin, fout, mult);
+ rem.pack_size = pack_size;
+ data_size = pack_size - MAX_H_SIZE;
+ fprintf(stderr,"pack_size: %d header_size: %d data size: %d\n",
+ pack_size, MAX_H_SIZE, data_size);
+ refill_buffy(&rem);
+ fprintf(stderr,"Package size: %d\n",pack_size);
+
+ if ( get_video_info(&rem) < 0 ){
+ fprintf(stderr,"ERROR: Can't find valid video stream\n");
+ exit(1);
+ }
+
+ i = 0;
+ while(! rem.time_off && i < rem.vframen) {
+ if( (rem.time_off = rem.vframe_list[i].time)) break;
+ i++;
+ }
+
+ if ( get_audio_info(&rem) < 0 ){
+ fprintf(stderr,"ERROR: Can't find valid audio stream\n");
+ exit(1);
+ }
+
+ rem.vpts = rem.vpts_list[0].PTS;
+ rem.vdts = rem.vpts;
+ rem.vpts_off = rem.vpts;
+ fprintf(stderr,"Video start PTS = %fs \n",rem.vpts_off/90000.);
+ rem.apts = rem.apts_list[0].PTS;
+ rem.apts_off = rem.apts;
+ ptsdiff = rem.vpts - rem.apts;
+ if (ptsdiff > 0) rem.vpts_off -= ptsdiff;
+ else rem.apts_off -= -ptsdiff;
+ fprintf(stderr,"Audio start PTS = %fs\n",rem.apts_off/90000.);
+ fprintf(stderr,"Difference Video - Audio = %fs\n",ptsdiff/90000.);
+ fprintf(stderr,"Time offset = %ds\n",rem.time_off);
+
+ rem.muxr = (rem.video_info.bit_rate +
+ rem.audio_info.bit_rate)/400;
+ fprintf(stderr,"MUXRATE: %.2f Mb/sec\n",rem.muxr/2500.);
+ SCR_inc = 1800 * pack_size / rem.muxr;
+
+ r = 0;
+ while ( rem.vptsn < 2 && !r) r = refill_buffy(&rem);
+ r = 0;
+ while ( rem.aptsn < 2 && !r) r = refill_buffy(&rem);
+
+ //rem.vpts_delay = (uint32_t)(2*90000ULL* (uint64_t)pack_size/rem.muxr);
+ rem.vpts_delay = rem.dts_delay;
+ rem.apts_delay = rem.vpts_delay;
+
+ vbuf_max = 29440;
+ abuf_max = 4096;
+ vbuf = 0;
+ abuf = 0;
+ pos = write_ps_header(buf,rem.SCR,rem.muxr, 1, 0, 0, 1, 1, 1,
+ 0xC0, 0, 32, 0xE0, 1, 230);
+ pos += write_pes_header( PADDING_STREAM, pack_size-pos, 0, buf+pos,0);
+ pos = rem.pack_size;
+ write( fout, buf, pos);
+
+ apos = rem.aread;
+ vpos = rem.vread;
+ print_info( &rem, 1 );
+
+ while( ring_rest(&rem.aud_buffy) && ring_rest(&rem.vid_buffy) ){
+ uint32_t next_apts;
+ uint32_t next_vdts;
+ int asize, vsize;
+
+ r1 = 0;
+ r2 = 0;
+ while ( rem.aframen < 2 && !r1)
+ r1 = refill_buffy(&rem);
+ while ( rem.vframen < 2 && !r2)
+ r2 = refill_buffy(&rem);
+ if (r1 && r2) break;
+
+ if ( !r1 && apos <= rem.aread)
+ apos = rem.aframe_list[1].pos;
+ if ( !r2 && vpos <= rem.vread)
+ vpos = rem.vframe_list[1].pos;
+ apack_size = apos - rem.aread;
+ vpack_size = vpos - rem.vread;
+
+
+ next_vdts = (uint32_t)((uint64_t)rem.vdts + rem.vpts_delay
+ - rem.vpts_off) ;
+ ok_video = ( rem.SCR < next_vdts);
+
+ next_apts = (uint32_t)((uint64_t)rem.apts + rem.apts_delay
+ - rem.apts_off) ;
+ ok_audio = ( rem.SCR < next_apts);
+
+ asize = (apack_size > data_size ? data_size: apack_size);
+ vsize = (vpack_size > data_size ? data_size: vpack_size);
+
+ fprintf(stderr,"vframen: %d aframen: %d v_ok: %d a_ok: %d v_buf: %d a_buf: %d vpacks: %d apacks: %d\n",rem.vframen,rem.aframen, ok_video, ok_audio, (int)vbuf,(int)abuf,vsize, asize);
+
+
+ if( vbuf+vsize < vbuf_max && vsize && ok_audio ){
+ fprintf(stderr,"1 ");
+ pos = write_video_pes( &rem, buf, &vpack_size);
+ write( fout, buf, pos);
+ vbuf += vpack_size;
+ vbufn = add_pts( vbufl, rem.vdts, vpack_size,
+ 0, vbufn, 0);
+ packets++;
+ } else if ( abuf+asize < abuf_max && asize &&
+ ok_video ){
+ fprintf(stderr,"2 ");
+ pos = write_audio_pes( &rem, buf, &apack_size);
+ write( fout, buf, pos);
+ abuf += apack_size;
+ abufn = add_pts( abufl, rem.apts, apack_size,
+ 0, abufn, 0);
+ packets++;
+ } else if ( abuf+asize < abuf_max && asize &&
+ !ok_audio){
+ fprintf(stderr,"3 ");
+ pos = write_audio_pes( &rem, buf, &apack_size);
+ write( fout, buf, pos);
+ abuf += apack_size;
+ abufn = add_pts( abufl, rem.apts, apack_size,
+ 0, abufn, 0);
+ packets++;
+ } else if (vbuf+vsize < vbuf_max && vsize &&
+ !ok_video){
+ fprintf(stderr,"4 ");
+ pos = write_video_pes( &rem, buf, &vpack_size);
+ write( fout, buf, pos);
+ vbuf += vpack_size;
+ vbufn = add_pts( vbufl, rem.vdts, vpack_size,
+ 0, vbufn, 0);
+ packets++;
+ } else {
+ fprintf(stderr,"5 ");
+ pos = write_ps_header(buf,rem.SCR,rem.muxr, 1, 0, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0, 0);
+
+ pos += write_pes_header( PADDING_STREAM, pack_size-pos,
+ 0, buf+pos, 0);
+ write( fout, buf, pos);
+ }
+
+
+ //fprintf(stderr,"vbufn: %d abufn: %d ", vbufn,abufn);
+ //fprintf(stderr,"vbuf: %5d abuf: %4d\n", vbuf,abuf);
+
+ if (rem.SCR > rem.vdts+rem.vpts_off -rem.vpts_delay)
+ rem.SCR = rem.vdts-rem.vpts_off;
+ rem.SCR = (uint32_t)((uint64_t) rem.SCR + SCR_inc);
+
+ if ( rem.apts_off + rem.SCR < rem.apts_delay ) pts_d = 0;
+ else pts_d = (uint64_t) rem.SCR + rem.apts_off - rem.apts_delay;
+ abuf -= del_ptss( abufl, (uint32_t) pts_d, &abufn);
+
+ if ( rem.vpts_off + rem.SCR < rem.vpts_delay ) pts_d = 0;
+ else pts_d = (uint64_t) rem.SCR + rem.vpts_off - rem.vpts_delay;
+ vbuf -= del_ptss( vbufl, (uint32_t) pts_d, &vbufn);
+
+ print_info( &rem, 1);
+ //fprintf(stderr,"vbufn: %d abufn: %d ", vbufn,abufn);
+ //fprintf(stderr,"vbuf: %5d abuf: %4d\n\n", vbuf,abuf);
+
+
+ }
+ pos = write_ps_header(buf,rem.SCR,rem.muxr, 1, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0);
+
+ pos += write_pes_header( PADDING_STREAM, pack_size-pos-4, 0,
+ buf+pos, 0);
+ pos = rem.pack_size-4;
+ write( fout, buf, pos);
+
+ write( fout, mpeg_end, 4);
+ fprintf(stderr,"\ndone\n");
+}
+
+
+typedef
+struct pes_buffer_s{
+ ringbuffy pes_buffy;
+ uint8_t type;
+ PTS_List pts_list[MAX_PTS];
+ FRAME_List frame_list[MAX_FRAME];
+ int pes_size;
+ uint64_t written;
+ uint64_t read;
+} PESBuffer;
+
+
+void init_PESBuffer(PESBuffer *pbuf, int pes_size, int buf_size, uint8_t type)
+{
+ init_framel( pbuf->frame_list);
+ init_ptsl( pbuf->pts_list);
+ ring_init( &pbuf->pes_buffy, buf_size);
+ pbuf->pes_size = pes_size;
+ pbuf->type = type;
+ pbuf->written = 0;
+ pbuf->read = 0;
+}
+
+
+#define MAX_PBUF 4
+
+typedef
+struct remux_s{
+ PESBuffer pbuf_list[MAX_PBUF];
+ int num_pbuf;
+} REMUX;
+
+
+void init_REMUX(REMUX *rem)
+{
+ rem->num_pbuf = 0;
+}
+
+
+
+#define REPACK 2048
+#define ABUF_SIZE REPACK*1024
+#define VBUF_SIZE REPACK*10240
+
+void remux_main(uint8_t *buf, int count, void *pr)
+{
+ int i, b;
+ int bufsize = 0;
+ p2p *p = (p2p *) pr;
+ PESBuffer *pbuf;
+ REMUX *rem = (REMUX *) p->data;
+ uint8_t type = buf[3];
+ int *npbuf = &(rem->num_pbuf);
+
+ switch ( type ){
+ case PRIVATE_STREAM1:
+ bufsize = ABUF_SIZE;
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ if (!bufsize) bufsize = VBUF_SIZE;
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ if (!bufsize) bufsize = ABUF_SIZE;
+ b = -1;
+ for ( i = 0; i < *npbuf; i++){
+ if ( type == rem->pbuf_list[i].type ){
+ b = i;
+ break;
+ }
+ }
+ if (b < 0){
+ if ( *npbuf < MAX_PBUF ){
+ init_PESBuffer(&rem->pbuf_list[*npbuf],
+ p->repack+6, bufsize, type);
+ b = *npbuf;
+ (*npbuf)++;
+ } else {
+ fprintf(stderr,"Not enough PES buffers\n");
+ exit(1);
+ }
+ }
+ break;
+ default:
+ return;
+ }
+
+ pbuf = &(rem->pbuf_list[b]);
+ if (ring_write(&(pbuf->pes_buffy),(char *)buf,count) != count){
+ fprintf(stderr,"buffer overflow type 0x%2x\n",type);
+ exit(1);
+ } else {
+ pbuf->written += count;
+ if ((p->flag2 & PTS_DTS_FLAGS)){
+ uint32_t PTS = trans_pts_dts(p->pts);
+ add_pts(pbuf->pts_list, PTS, pbuf->written,
+ pbuf->written, 0, 0);
+ }
+ p->flag2 = 0;
+ }
+
+}
+
+void output_mux(p2p *p)
+{
+ int i, filling;
+ PESBuffer *pbuf;
+ ringbuffy *pes_buffy;
+ REMUX *rem = (REMUX *) p->data;
+ int repack = p->repack;
+ int npbuf = rem->num_pbuf;
+
+ for ( i = 0; i < npbuf; i++){
+ pbuf = &(rem->pbuf_list[i]);
+ pes_buffy = &pbuf->pes_buffy;
+ filling = pes_buffy->size - ring_rest(pes_buffy);
+ if (filling/(2 *repack)){
+ pbuf->read += ring_read_file(pes_buffy, p->fd1,
+ (filling/repack)*repack);
+ }
+ }
+}
+
+
+
+#define SIZE 32768
+
+void remux2(int fdin, int fdout)
+{
+ p2p p;
+ int count = 1;
+ uint8_t buf[SIZE];
+ uint64_t length = 0;
+ uint64_t l = 0;
+ int verb = 0;
+ REMUX rem;
+
+ init_p2p(&p, remux_main, REPACK);
+ p.fd1 = fdout;
+ p.data = (void *) &rem;
+
+
+ if (fdin != STDIN_FILENO) verb = 1;
+
+ if (verb) {
+ length = lseek(fdin, 0, SEEK_END);
+ lseek(fdin,0,SEEK_SET);
+ }
+
+ while (count > 0){
+ count = read(fdin,buf,SIZE);
+ l += count;
+ if (verb)
+ fprintf(stderr,"Writing %2.2f %%\r",
+ 100.*l/length);
+
+ get_pes(buf,count,&p,pes_repack);
+ output_mux(&p);
+ }
+
+}
diff --git a/libdvbmpeg/remux.h b/libdvbmpeg/remux.h
new file mode 100644
index 0000000..76c128b
--- /dev/null
+++ b/libdvbmpeg/remux.h
@@ -0,0 +1,149 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de,
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+//#include <libgen.h>
+#include <stdint.h>
+
+#include "ringbuffy.h"
+#include "ctools.h"
+
+#ifndef _REMUX_H_
+#define _REMUX_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ typedef struct video_i{
+ uint32_t horizontal_size;
+ uint32_t vertical_size ;
+ uint32_t aspect_ratio ;
+ double framerate ;
+ uint32_t video_format;
+ uint32_t bit_rate ;
+ uint32_t comp_bit_rate ;
+ uint32_t vbv_buffer_size;
+ uint32_t CSPF ;
+ uint32_t off;
+ } VideoInfo;
+
+ typedef struct audio_i{
+ int layer;
+ uint32_t bit_rate;
+ uint32_t frequency;
+ uint32_t mode;
+ uint32_t mode_extension;
+ uint32_t emphasis;
+ uint32_t framesize;
+ uint32_t off;
+ } AudioInfo;
+
+
+
+ typedef
+ struct PTS_list_struct{
+ uint32_t PTS;
+ int pos;
+ uint32_t dts;
+ int spos;
+ } PTS_List;
+
+ typedef
+ struct frame_list_struct{
+ int type;
+ int pos;
+ uint32_t FRAME;
+ uint32_t time;
+ uint32_t pts;
+ uint32_t dts;
+ } FRAME_List;
+
+ typedef
+ struct remux_struct{
+ ringbuffy vid_buffy;
+ ringbuffy aud_buffy;
+ PTS_List vpts_list[MAX_PTS];
+ PTS_List apts_list[MAX_PTS];
+ FRAME_List vframe_list[MAX_FRAME];
+ FRAME_List aframe_list[MAX_FRAME];
+ int vptsn;
+ int aptsn;
+ int vframen;
+ int aframen;
+ long apes;
+ long vpes;
+ uint32_t vframe;
+ uint32_t aframe;
+ uint32_t vcframe;
+ uint32_t acframe;
+ uint32_t vpts;
+ uint32_t vdts;
+ uint32_t apts;
+ uint32_t vpts_old;
+ uint32_t apts_old;
+ uint32_t SCR;
+ uint32_t apts_off;
+ uint32_t vpts_off;
+ uint32_t apts_delay;
+ uint32_t vpts_delay;
+ uint32_t dts_delay;
+ AudioInfo audio_info;
+ VideoInfo video_info;
+ int fin;
+ int fout;
+ long int awrite;
+ long int vwrite;
+ long int aread;
+ long int vread;
+ uint32_t group;
+ uint32_t groupframe;
+ uint32_t muxr;
+ int pack_size;
+ uint32_t time_off;
+ } Remux;
+
+ enum { NONE, I_FRAME, P_FRAME, B_FRAME, D_FRAME };
+
+ void remux(int fin, int fout, int pack_size, int mult);
+ void remux2(int fdin, int fdout);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /*_REMUX_H_*/
diff --git a/libdvbmpeg/ringbuffy.c b/libdvbmpeg/ringbuffy.c
new file mode 100644
index 0000000..965e49a
--- /dev/null
+++ b/libdvbmpeg/ringbuffy.c
@@ -0,0 +1,201 @@
+/*
+ Ringbuffer Implementation for gtvscreen
+
+ Copyright (C) 2000 Marcus Metzler (mocm@metzlerbros.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "ringbuffy.h"
+#include <string.h>
+
+int ring_init (ringbuffy *rbuf, int size)
+{
+ if (size > 0){
+ rbuf->size = size;
+ if( !(rbuf->buffy = (char *) malloc(sizeof(char)*size)) ){
+ fprintf(stderr,"Not enough memory for ringbuffy\n");
+ return -1;
+ }
+ } else {
+ fprintf(stderr,"Wrong size for ringbuffy\n");
+ return -1;
+ }
+ rbuf->read_pos = 0;
+ rbuf->write_pos = 0;
+ return 0;
+}
+
+
+void ring_destroy(ringbuffy *rbuf)
+{
+ free(rbuf->buffy);
+}
+
+
+int ring_write(ringbuffy *rbuf, char *data, int count)
+{
+
+ int diff, free, pos, rest;
+
+ if (count <=0 ) return 0;
+ pos = rbuf->write_pos;
+ rest = rbuf->size - pos;
+ diff = rbuf->read_pos - pos;
+ free = (diff > 0) ? diff-1 : rbuf->size+diff-1;
+
+ if ( free <= 0 ) return FULL_BUFFER;
+ if ( free < count ) count = free;
+
+ if (count >= rest){
+ memcpy (rbuf->buffy+pos, data, rest);
+ if (count - rest)
+ memcpy (rbuf->buffy, data+rest, count - rest);
+ rbuf->write_pos = count - rest;
+ } else {
+ memcpy (rbuf->buffy+pos, data, count);
+ rbuf->write_pos += count;
+ }
+
+ return count;
+}
+
+
+
+
+int ring_peek(ringbuffy *rbuf, char *data, int count, long off)
+{
+
+ int diff, free, pos, rest;
+
+ if (count <=0 ) return 0;
+ pos = rbuf->read_pos+off;
+ rest = rbuf->size - pos ;
+ diff = rbuf->write_pos - pos;
+ free = (diff >= 0) ? diff : rbuf->size+diff;
+
+ if ( free <= 0 ) return FULL_BUFFER;
+ if ( free < count ) count = free;
+
+ if ( count < rest ){
+ memcpy(data, rbuf->buffy+pos, count);
+ } else {
+ memcpy(data, rbuf->buffy+pos, rest);
+ if ( count - rest)
+ memcpy(data+rest, rbuf->buffy, count - rest);
+ }
+
+ return count;
+}
+
+int ring_read(ringbuffy *rbuf, char *data, int count)
+{
+
+ int diff, free, pos, rest;
+
+ if (count <=0 ) return 0;
+ pos = rbuf->read_pos;
+ rest = rbuf->size - pos;
+ diff = rbuf->write_pos - pos;
+ free = (diff >= 0) ? diff : rbuf->size+diff;
+
+ if ( rest <= 0 ) return 0;
+ if ( free < count ) count = free;
+
+ if ( count < rest ){
+ memcpy(data, rbuf->buffy+pos, count);
+ rbuf->read_pos += count;
+ } else {
+ memcpy(data, rbuf->buffy+pos, rest);
+ if ( count - rest)
+ memcpy(data+rest, rbuf->buffy, count - rest);
+ rbuf->read_pos = count - rest;
+ }
+
+ return count;
+}
+
+
+
+int ring_write_file(ringbuffy *rbuf, int fd, int count)
+{
+
+ int diff, free, pos, rest, rr;
+
+ if (count <=0 ) return 0;
+ pos = rbuf->write_pos;
+ rest = rbuf->size - pos;
+ diff = rbuf->read_pos - pos;
+ free = (diff > 0) ? diff-1 : rbuf->size+diff-1;
+
+ if ( rest <= 0 ) return 0;
+ if ( free < count ) count = free;
+
+ if (count >= rest){
+ rr = read (fd, rbuf->buffy+pos, rest);
+ if (rr == rest && count - rest)
+ rr += read (fd, rbuf->buffy, count - rest);
+ if (rr >=0)
+ rbuf->write_pos = (pos + rr) % rbuf->size;
+ } else {
+ rr = read (fd, rbuf->buffy+pos, count);
+ if (rr >=0)
+ rbuf->write_pos += rr;
+ }
+
+ return rr;
+}
+
+
+
+int ring_read_file(ringbuffy *rbuf, int fd, int count)
+{
+
+ int diff, free, pos, rest, rr;
+
+ if (count <=0 ) return 0;
+ pos = rbuf->read_pos;
+ rest = rbuf->size - pos;
+ diff = rbuf->write_pos - pos;
+ free = (diff >= 0) ? diff : rbuf->size+diff;
+
+ if ( free <= 0 ) return FULL_BUFFER;
+ if ( free < count ) count = free;
+
+ if (count >= rest){
+ rr = write (fd, rbuf->buffy+pos, rest);
+ if (rr == rest && count - rest)
+ rr += write (fd, rbuf->buffy, count - rest);
+ if (rr >=0)
+ rbuf->read_pos = (pos + rr) % rbuf->size;
+ } else {
+ rr = write (fd, rbuf->buffy+pos, count);
+ if (rr >=0)
+ rbuf->read_pos += rr;
+ }
+
+
+ return rr;
+}
+
+int ring_rest(ringbuffy *rbuf){
+ int diff, free, pos, rest;
+ pos = rbuf->read_pos;
+ rest = rbuf->size - pos;
+ diff = rbuf->write_pos - pos;
+ free = (diff >= 0) ? diff : rbuf->size+diff;
+
+ return free;
+}
diff --git a/libdvbmpeg/ringbuffy.h b/libdvbmpeg/ringbuffy.h
new file mode 100644
index 0000000..16011d7
--- /dev/null
+++ b/libdvbmpeg/ringbuffy.h
@@ -0,0 +1,52 @@
+/*
+ Ringbuffer Implementation for gtvscreen
+
+ Copyright (C) 2000 Marcus Metzler (mocm@metzlerbros.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef RINGBUFFY_H
+#define RINGBUFFY_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define FULL_BUFFER -1000
+typedef struct ringbuffy{
+ int read_pos;
+ int write_pos;
+ int size;
+ char *buffy;
+} ringbuffy;
+
+int ring_init (ringbuffy *rbuf, int size);
+void ring_destroy(ringbuffy *rbuf);
+int ring_write(ringbuffy *rbuf, char *data, int count);
+int ring_read(ringbuffy *rbuf, char *data, int count);
+int ring_write_file(ringbuffy *rbuf, int fd, int count);
+int ring_read_file(ringbuffy *rbuf, int fd, int count);
+int ring_rest(ringbuffy *rbuf);
+int ring_peek(ringbuffy *rbuf, char *data, int count, long off);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* RINGBUFFY_H */
diff --git a/libdvbmpeg/transform.c b/libdvbmpeg/transform.c
new file mode 100644
index 0000000..c53f1fb
--- /dev/null
+++ b/libdvbmpeg/transform.c
@@ -0,0 +1,2681 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at marcus@convergence.de,
+
+ * the project's page is at http://linuxtv.org/dvb/
+ */
+
+
+#include "transform.h"
+#include <stdlib.h>
+#include <string.h>
+#include "ctools.h"
+
+static uint8_t tspid0[TS_SIZE] = {
+ 0x47, 0x40, 0x00, 0x10, 0x00, 0x00, 0xb0, 0x11,
+ 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x00, 0xe0,
+ 0x10, 0x00, 0x01, 0xe4, 0x00, 0x2a, 0xd6, 0x1a,
+ 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+
+static uint8_t tspid1[TS_SIZE] = {
+ 0x47, 0x44, 0x00, 0x10, 0x00, 0x02, 0xb0, 0x1c,
+ 0x00, 0x01, 0xcb, 0x00, 0x00, 0xe0, 0xa0, 0xf0,
+ 0x05, 0x48, 0x03, 0x01, 0x00, 0x00, 0x02, 0xe0,
+ 0xa0, 0xf0, 0x00, 0x03, 0xe0, 0x50, 0xf0, 0x00,
+ 0xae, 0xea, 0x4e, 0x48, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+
+// CRC32 lookup table for polynomial 0x04c11db7
+static const uint32_t crc_table[256] = {
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+ 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
+ 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
+ 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
+ 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
+ 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+ 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
+ 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+ 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
+ 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
+ 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+ 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
+ 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+ 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
+ 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
+ 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
+ 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
+ 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+ 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
+ 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+ 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
+ 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
+ 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+ 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
+ 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+ 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+static uint32_t
+calc_crc32 (const uint8_t *sec, uint8_t len)
+{
+ int i;
+ uint32_t crc = 0xffffffff;
+
+ for (i = 0; i < len; i++)
+ crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *sec++) & 0xff];
+
+ return crc;
+}
+
+
+uint64_t trans_pts_dts(uint8_t *pts)
+{
+ uint64_t wts;
+
+ wts = ((uint64_t)((pts[0] & 0x0E) << 5) |
+ ((pts[1] & 0xFC) >> 2)) << 24;
+ wts |= (((pts[1] & 0x03) << 6) |
+ ((pts[2] & 0xFC) >> 2)) << 16;
+ wts |= (((pts[2] & 0x02) << 6) |
+ ((pts[3] & 0xFE) >> 1)) << 8;
+ wts |= (((pts[3] & 0x01) << 7) |
+ ((pts[4] & 0xFE) >> 1));
+ return wts;
+}
+
+
+void get_pespts(uint8_t *av_pts,uint8_t *pts)
+{
+
+ pts[0] = 0x21 |
+ ((av_pts[0] & 0xC0) >>5);
+ pts[1] = ((av_pts[0] & 0x3F) << 2) |
+ ((av_pts[1] & 0xC0) >> 6);
+ pts[2] = 0x01 | ((av_pts[1] & 0x3F) << 2) |
+ ((av_pts[2] & 0x80) >> 6);
+ pts[3] = ((av_pts[2] & 0x7F) << 1) |
+ ((av_pts[3] & 0x80) >> 7);
+ pts[4] = 0x01 | ((av_pts[3] & 0x7F) << 1);
+}
+
+uint16_t get_pid(uint8_t *pid)
+{
+ uint16_t pp = 0;
+
+ pp = (pid[0] & PID_MASK_HI)<<8;
+ pp |= pid[1];
+
+ return pp;
+}
+
+int write_ts_header(uint16_t pid, uint8_t *counter, int pes_start,
+ uint8_t *buf, uint8_t length)
+{
+ int i;
+ int c = 0;
+ int fill;
+ uint8_t tshead[4] = { 0x47, 0x00, 0x00, 0x10};
+
+
+ fill = TS_SIZE-4-length;
+ if (pes_start) tshead[1] = 0x40;
+ if (fill) tshead[3] = 0x30;
+ tshead[1] |= (uint8_t)((pid & 0x1F00) >> 8);
+ tshead[2] |= (uint8_t)(pid & 0x00FF);
+ tshead[3] |= ((*counter)++ & 0x0F) ;
+ memcpy(buf,tshead,4);
+ c+=4;
+
+
+ if (fill){
+ buf[4] = fill-1;
+ c++;
+ if (fill >1){
+ buf[5] = 0x00;
+ c++;
+ }
+ for ( i = 6; i < fill+4; i++){
+ buf[i] = 0xFF;
+ c++;
+ }
+ }
+
+ return c;
+}
+
+
+int write_pes_header(uint8_t id,int length , long PTS, uint8_t *obuf,
+ int stuffing)
+{
+ uint8_t le[2];
+ uint8_t dummy[3];
+ uint8_t *pts;
+ uint8_t ppts[5];
+ long lpts;
+ int c;
+ uint8_t headr[3] = {0x00, 0x00, 0x01};
+
+ lpts = htonl(PTS);
+ pts = (uint8_t *) &lpts;
+
+ get_pespts(pts,ppts);
+
+ c = 0;
+ memcpy(obuf+c,headr,3);
+ c += 3;
+ memcpy(obuf+c,&id,1);
+ c++;
+
+ le[0] = 0;
+ le[1] = 0;
+ length -= 6+stuffing;
+
+ le[0] |= ((uint8_t)(length >> 8) & 0xFF);
+ le[1] |= ((uint8_t)(length) & 0xFF);
+ memcpy(obuf+c,le,2);
+ c += 2;
+
+ if (id == PADDING_STREAM){
+ memset(obuf+c,0xff,length);
+ c+= length;
+ return c;
+ }
+
+ dummy[0] = 0x80;
+ dummy[1] = 0;
+ dummy[2] = 0;
+ if (PTS){
+ dummy[1] |= PTS_ONLY;
+ dummy[2] = 5+stuffing;
+ }
+ memcpy(obuf+c,dummy,3);
+ c += 3;
+ memset(obuf+c,0xFF,stuffing);
+
+ if (PTS){
+ memcpy(obuf+c,ppts,5);
+ c += 5;
+ }
+
+ return c;
+}
+
+
+void init_p2p(p2p *p, void (*func)(uint8_t *buf, int count, void *p),
+ int repack){
+ p->found = 0;
+ p->cid = 0;
+ p->mpeg = 0;
+ memset(p->buf,0,MMAX_PLENGTH);
+ p->done = 0;
+ p->fd1 = -1;
+ p->func = func;
+ p->bigend_repack = 0;
+ p->repack = 0;
+ if ( repack < MAX_PLENGTH && repack > 265 ){
+ p->repack = repack-6;
+ p->bigend_repack = (uint16_t)htons((short)
+ ((repack-6) & 0xFFFF));
+ } else {
+ fprintf(stderr, "Repack size %d is out of range\n",repack);
+ exit(1);
+ }
+}
+
+
+
+void pes_repack(p2p *p)
+{
+ int count = 0;
+ int repack = p->repack;
+ int rest = p->plength;
+ uint8_t buf[MAX_PLENGTH];
+ int bfill = 0;
+ int diff;
+ uint16_t length;
+
+ if (rest < 0) {
+ fprintf(stderr,"Error in repack\n");
+ return;
+ }
+
+ if (!repack){
+ fprintf(stderr,"forgot to set repack size\n");
+ return;
+ }
+
+ if (p->plength == repack){
+ memcpy(p->buf+4,(char *)&p->bigend_repack,2);
+ p->func(p->buf, repack+6, p);
+ return;
+ }
+
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ buf[2] = 0x01;
+ buf[3] = p->cid;
+ memcpy(buf+4,(char *)&p->bigend_repack,2);
+ memset(buf+6,0,MAX_PLENGTH-6);
+
+ if (p->mpeg == 2){
+
+ if ( rest > repack){
+ memcpy(p->buf+4,(char *)&p->bigend_repack,2);
+ p->func(p->buf, repack+6, p);
+ count += repack+6;
+ rest -= repack;
+ } else {
+ memcpy(buf,p->buf,9+p->hlength);
+ bfill = p->hlength;
+ count += 9+p->hlength;
+ rest -= p->hlength+3;
+ }
+
+ while (rest >= repack-3){
+ memset(buf+6,0,MAX_PLENGTH-6);
+ buf[6] = 0x80;
+ buf[7] = 0x00;
+ buf[8] = 0x00;
+ memcpy(buf+9,p->buf+count,repack-3);
+ rest -= repack-3;
+ count += repack-3;
+ p->func(buf, repack+6, p);
+ }
+
+ if (rest){
+ diff = repack - 3 - rest - bfill;
+ if (!bfill){
+ buf[6] = 0x80;
+ buf[7] = 0x00;
+ buf[8] = 0x00;
+ }
+
+ if ( diff < PES_MIN){
+ length = rest+ diff + bfill+3;
+ buf[4] = (uint8_t)((length & 0xFF00) >> 8);
+ buf[5] = (uint8_t)(length & 0x00FF);
+ buf[8] = (uint8_t)(bfill+diff);
+ memset(buf+9+bfill,0xFF,diff);
+ memcpy(buf+9+bfill+diff,p->buf+count,rest);
+ } else {
+ length = rest+ bfill+3;
+ buf[4] = (uint8_t)((length & 0xFF00) >> 8);
+ buf[5] = (uint8_t)(length & 0x00FF);
+ memcpy(buf+9+bfill,p->buf+count,rest);
+ bfill += rest+9;
+ write_pes_header( PADDING_STREAM, diff, 0,
+ buf+bfill, 0);
+ }
+ p->func(buf, repack+6, p);
+ }
+ }
+
+ if (p->mpeg == 1){
+
+ if ( rest > repack){
+ memcpy(p->buf+4,(char *)&p->bigend_repack,2);
+ p->func(p->buf, repack+6, p);
+ count += repack+6;
+ rest -= repack;
+ } else {
+ memcpy(buf,p->buf,6+p->hlength);
+ bfill = p->hlength;
+ count += 6;
+ rest -= p->hlength;
+ }
+
+ while (rest >= repack-1){
+ memset(buf+6,0,MAX_PLENGTH-6);
+ buf[6] = 0x0F;
+ memcpy(buf+7,p->buf+count,repack-1);
+ rest -= repack-1;
+ count += repack-1;
+ p->func(buf, repack+6, p);
+ }
+
+
+ if (rest){
+ diff = repack - 1 - rest - bfill;
+
+ if ( diff < PES_MIN){
+ length = rest+ diff + bfill+1;
+ buf[4] = (uint8_t)((length & 0xFF00) >> 8);
+ buf[5] = (uint8_t)(length & 0x00FF);
+ memset(buf+6,0xFF,diff);
+ if (!bfill){
+ buf[6+diff] = 0x0F;
+ }
+ memcpy(buf+7+diff,p->buf+count,rest+bfill);
+ } else {
+ length = rest+ bfill+1;
+ buf[4] = (uint8_t)((length & 0xFF00) >> 8);
+ buf[5] = (uint8_t)(length & 0x00FF);
+ if (!bfill){
+ buf[6] = 0x0F;
+ memcpy(buf+7,p->buf+count,rest);
+ bfill = rest+7;
+ } else {
+ memcpy(buf+6,p->buf+count,rest+bfill);
+ bfill += rest+6;
+ }
+ write_pes_header( PADDING_STREAM, diff, 0,
+ buf+bfill, 0);
+ }
+ p->func(buf, repack+6, p);
+ }
+ }
+}
+
+
+
+
+
+
+
+
+int filter_pes (uint8_t *buf, int count, p2p *p, int (*func)(p2p *p))
+{
+
+ int l;
+ unsigned short *pl;
+ int c=0;
+ int ret = 1;
+
+ uint8_t headr[3] = { 0x00, 0x00, 0x01} ;
+
+ while (c < count && (p->mpeg == 0 ||
+ (p->mpeg == 1 && p->found < 7) ||
+ (p->mpeg == 2 && p->found < 9))
+ && (p->found < 5 || !p->done)){
+ switch ( p->found ){
+ case 0:
+ case 1:
+ if (buf[c] == 0x00) p->found++;
+ else {
+ if (p->fd1 >= 0)
+ write(p->fd1,buf+c,1);
+ p->found = 0;
+ }
+ c++;
+ break;
+ case 2:
+ if (buf[c] == 0x01) p->found++;
+ else if (buf[c] == 0){
+ p->found = 2;
+ } else {
+ if (p->fd1 >= 0)
+ write(p->fd1,buf+c,1);
+ p->found = 0;
+ }
+ c++;
+ break;
+ case 3:
+ p->cid = 0;
+ switch (buf[c]){
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ if (p->fd1 >= 0)
+ write(p->fd1,buf+c,1);
+ p->done = 1;
+ case PRIVATE_STREAM1:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ p->found++;
+ p->cid = buf[c];
+ c++;
+ break;
+ default:
+ if (p->fd1 >= 0)
+ write(p->fd1,buf+c,1);
+ p->found = 0;
+ break;
+ }
+ break;
+
+
+ case 4:
+ if (count-c > 1){
+ pl = (unsigned short *) (buf+c);
+ p->plength = ntohs(*pl);
+ p->plen[0] = buf[c];
+ c++;
+ p->plen[1] = buf[c];
+ c++;
+ p->found+=2;
+ } else {
+ p->plen[0] = buf[c];
+ p->found++;
+ return 1;
+ }
+ break;
+ case 5:
+ p->plen[1] = buf[c];
+ c++;
+ pl = (unsigned short *) p->plen;
+ p->plength = ntohs(*pl);
+ p->found++;
+ break;
+
+
+ case 6:
+ if (!p->done){
+ p->flag1 = buf[c];
+ c++;
+ p->found++;
+ if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2;
+ else {
+ p->hlength = 0;
+ p->which = 0;
+ p->mpeg = 1;
+ p->flag2 = 0;
+ }
+ }
+ break;
+
+ case 7:
+ if ( !p->done && p->mpeg == 2){
+ p->flag2 = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ case 8:
+ if ( !p->done && p->mpeg == 2){
+ p->hlength = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ default:
+
+ break;
+ }
+ }
+
+ if (!p->plength) p->plength = MMAX_PLENGTH-6;
+
+
+ if ( p->done || ((p->mpeg == 2 && p->found >= 9) ||
+ (p->mpeg == 1 && p->found >= 7)) ){
+ switch (p->cid){
+
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case PRIVATE_STREAM1:
+
+ memcpy(p->buf, headr, 3);
+ p->buf[3] = p->cid;
+ memcpy(p->buf+4,p->plen,2);
+
+ if (p->mpeg == 2 && p->found == 9){
+ p->buf[6] = p->flag1;
+ p->buf[7] = p->flag2;
+ p->buf[8] = p->hlength;
+ }
+
+ if (p->mpeg == 1 && p->found == 7){
+ p->buf[6] = p->flag1;
+ }
+
+
+ if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
+ p->found < 14){
+ while (c < count && p->found < 14){
+ p->pts[p->found-9] = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ }
+ if (c == count) return 1;
+ }
+
+ if (p->mpeg == 1 && p->which < 2000){
+
+ if (p->found == 7) {
+ p->check = p->flag1;
+ p->hlength = 1;
+ }
+
+ while (!p->which && c < count &&
+ p->check == 0xFF){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ }
+
+ if ( c == count) return 1;
+
+ if ( (p->check & 0xC0) == 0x40 && !p->which){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+
+ p->which = 1;
+ if ( c == count) return 1;
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return 1;
+ }
+
+ if (p->which == 1){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return 1;
+ }
+
+ if ( (p->check & 0x30) && p->check != 0xFF){
+ p->flag2 = (p->check & 0xF0) << 2;
+ p->pts[0] = p->check;
+ p->which = 3;
+ }
+
+ if ( c == count) return 1;
+ if (p->which > 2){
+ if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_ONLY){
+ while (c < count &&
+ p->which < 7){
+ p->pts[p->which-2] =
+ buf[c];
+ p->buf[p->found] =
+ buf[c];
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return 1;
+ } else if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_DTS){
+ while (c < count &&
+ p->which< 12){
+ if (p->which< 7)
+ p->pts[p->which
+ -2] =
+ buf[c];
+ p->buf[p->found] =
+ buf[c];
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return 1;
+ }
+ p->which = 2000;
+ }
+
+ }
+
+ while (c < count && p->found < p->plength+6){
+ l = count -c;
+ if (l+p->found > p->plength+6)
+ l = p->plength+6-p->found;
+ memcpy(p->buf+p->found, buf+c, l);
+ p->found += l;
+ c += l;
+ }
+ if(p->found == p->plength+6){
+ if (func(p)){
+ if (p->fd1 >= 0){
+ write(p->fd1,p->buf,
+ p->plength+6);
+ }
+ } else ret = 0;
+ }
+ break;
+ }
+
+
+ if ( p->done ){
+ if( p->found + count - c < p->plength+6){
+ p->found += count-c;
+ c = count;
+ } else {
+ c += p->plength+6 - p->found;
+ p->found = p->plength+6;
+ }
+ }
+
+ if (p->plength && p->found == p->plength+6) {
+ p->found = 0;
+ p->done = 0;
+ p->plength = 0;
+ memset(p->buf, 0, MAX_PLENGTH);
+ if (c < count)
+ return filter_pes(buf+c, count-c, p, func);
+ }
+ }
+ return ret;
+}
+
+
+#define SIZE 4096
+
+
+int audio_pes_filt(p2p *p)
+{
+ uint8_t off;
+
+ switch(p->cid){
+ case PRIVATE_STREAM1:
+ if ( p->cid == p->filter) {
+ off = 9+p->buf[8];
+ if (p->buf[off] == p->subid){
+ return 1;
+ }
+ }
+ break;
+
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ if ( p->cid == p->filter)
+ return 1;
+ break;
+
+ default:
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+
+void filter_audio_from_pes(int fdin, int fdout, uint8_t id, uint8_t subid)
+{
+ p2p p;
+ int count = 1;
+ uint8_t buf[2048];
+
+ init_p2p(&p, NULL, 2048);
+ p.fd1 = -1;
+ p.filter = id;
+ p.subid = subid;
+
+ while (count > 0){
+ count = read(fdin,buf,2048);
+ if(filter_pes(buf,count,&p,audio_pes_filt))
+ write(fdout,buf,2048);
+ }
+}
+
+
+void pes_filt(p2p *p)
+{
+ int factor = p->mpeg-1;
+
+ if ( p->cid == p->filter) {
+ if (p->es)
+ write(p->fd1,p->buf+p->hlength+6+3*factor,
+ p->plength-p->hlength-3*factor);
+ else
+ write(p->fd1,p->buf,p->plength+6);
+ }
+}
+
+void extract_from_pes(int fdin, int fdout, uint8_t id, int es)
+{
+ p2p p;
+ int count = 1;
+ uint8_t buf[SIZE];
+
+ init_p2p(&p, NULL, 2048);
+ p.fd1 = fdout;
+ p.filter = id;
+ p.es = es;
+
+ while (count > 0){
+ count = read(fdin,buf,SIZE);
+ get_pes(buf,count,&p,pes_filt);
+ }
+}
+
+
+void pes_dfilt(p2p *p)
+{
+ int factor = p->mpeg-1;
+ int fd =0;
+ int head=0;
+ int type = NOPES;
+ int streamid;
+ int c = 6+p->hlength+3*factor;
+
+
+ switch ( p->cid ) {
+ case PRIVATE_STREAM1:
+ streamid = p->buf[c];
+ head = 4;
+ if ((streamid & 0xF8) == 0x80+p->es-1){
+ fd = p->fd1;
+ type = AC3;
+ }
+ break;
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ fd = p->fd1;
+ type = AUDIO;
+ break;
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ fd = p->fd2;
+ type = VIDEO;
+ break;
+ }
+
+ if (p->es && !p->startv && type == VIDEO){
+ int found = 0;
+
+ if ( p->flag2 & PTS_DTS )
+ p->vpts = trans_pts_dts(p->pts);
+ else return;
+
+ while ( !found && c+3 < p->plength+6 ){
+ if ( p->buf[c] == 0x00 &&
+ p->buf[c+1] == 0x00 &&
+ p->buf[c+2] == 0x01 &&
+ p->buf[c+3] == 0xb3)
+ found = 1;
+ else c++;
+ }
+ if (found){
+ p->startv = 1;
+ write(fd, p->buf+c, p->plength+6-c);
+ }
+ fd = 0;
+ }
+
+
+ if ( p->es && !p->starta && type == AUDIO){
+ int found = 0;
+ if ( p->flag2 & PTS_DTS )
+ p->apts = trans_pts_dts(p->pts);
+ else return;
+
+ if (p->startv)
+ while ( !found && c+1 < p->plength+6){
+ if ( p->buf[c] == 0xFF &&
+ (p->buf[c+1] & 0xF8) == 0xF8)
+ found = 1;
+ else c++;
+ }
+ if (found){
+ p->starta = 1;
+ write(fd, p->buf+c, p->plength+6-c);
+ }
+ fd = 0;
+ }
+
+ if ( p->es && !p->starta && type == AC3){
+ if ( p->flag2 & PTS_DTS )
+ p->apts = trans_pts_dts(p->pts);
+ else return;
+
+ if (p->startv){
+ c+= ((p->buf[c+2] << 8)| p->buf[c+3]);
+ p->starta = 1;
+ write(fd, p->buf+c, p->plength+6-c);
+ }
+ fd = 0;
+ }
+
+
+ if (fd){
+ if (p->es)
+ write(fd,p->buf+p->hlength+6+3*factor+head,
+ p->plength-p->hlength-3*factor-head);
+ else
+ write(fd,p->buf,p->plength+6);
+ }
+}
+
+int64_t pes_dmx( int fdin, int fdouta, int fdoutv, int es)
+{
+ p2p p;
+ int count = 1;
+ uint8_t buf[SIZE];
+ uint64_t length = 0;
+ uint64_t l = 0;
+ int verb = 0;
+ int percent, oldPercent = -1;
+
+ init_p2p(&p, NULL, 2048);
+ p.fd1 = fdouta;
+ p.fd2 = fdoutv;
+ p.es = es;
+ p.startv = 0;
+ p.starta = 0;
+ p.apts=-1;
+ p.vpts=-1;
+
+ if (fdin != STDIN_FILENO) verb = 1;
+
+ if (verb) {
+ length = lseek(fdin, 0, SEEK_END);
+ lseek(fdin,0,SEEK_SET);
+ }
+
+ while (count > 0){
+ count = read(fdin,buf,SIZE);
+ l += count;
+ if (verb){
+ percent = 100 * l / length;
+
+ if (percent != oldPercent) {
+ fprintf(stderr, "Demuxing %d %%\r", percent);
+ oldPercent = percent;
+ }
+ }
+ get_pes(buf,count,&p,pes_dfilt);
+ }
+
+ return (int64_t)p.vpts - (int64_t)p.apts;
+
+}
+
+
+/* SV: made non-static */
+void pes_in_ts(p2p *p)
+{
+ int l, pes_start;
+ uint8_t obuf[TS_SIZE];
+ long int c = 0;
+ int length = p->plength+6;
+ uint16_t pid;
+ uint8_t *counter;
+ pes_start = 1;
+ switch ( p->cid ) {
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ pid = p->pida;
+ counter = &p->acounter;
+ break;
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ pid = p->pidv;
+ counter = &p->acounter;
+
+ tspid0[3] |= (p->count0++)
+ & 0x0F ;
+ tspid1[3] |= (p->count1++)
+ & 0x0F ;
+
+ tspid1[24] = p->pidv;
+ tspid1[23] |= (p->pidv >> 8) & 0x3F;
+ tspid1[29] = p->pida;
+ tspid1[28] |= (p->pida >> 8) & 0x3F;
+
+ p->func(tspid0,188,p);
+ p->func(tspid1,188,p);
+ break;
+ default:
+ return;
+ }
+
+ while ( c < length ){
+ memset(obuf,0,TS_SIZE);
+ if (length - c >= TS_SIZE-4){
+ l = write_ts_header(pid, counter, pes_start
+ , obuf, TS_SIZE-4);
+ memcpy(obuf+l, p->buf+c, TS_SIZE-l);
+ c += TS_SIZE-l;
+ } else {
+ l = write_ts_header(pid, counter, pes_start
+ , obuf, length-c);
+ memcpy(obuf+l, p->buf+c, TS_SIZE-l);
+ c = length;
+ }
+ p->func(obuf,188,p);
+ pes_start = 0;
+ }
+}
+
+static
+void write_out(uint8_t *buf, int count,void *p)
+{
+ write(STDOUT_FILENO, buf, count);
+}
+
+
+void pes_to_ts2( int fdin, int fdout, uint16_t pida, uint16_t pidv)
+{
+ p2p p;
+ int count = 1;
+ uint8_t buf[SIZE];
+ uint64_t length = 0;
+ uint64_t l = 0;
+ int verb = 0;
+
+ init_p2p(&p, NULL, 2048);
+ p.fd1 = fdout;
+ p.pida = pida;
+ p.pidv = pidv;
+ p.acounter = 0;
+ p.vcounter = 0;
+ p.count1 = 0;
+ p.count0 = 0;
+ p.func = write_out;
+
+ if (fdin != STDIN_FILENO) verb = 1;
+
+ if (verb) {
+ length = lseek(fdin, 0, SEEK_END);
+ lseek(fdin,0,SEEK_SET);
+ }
+
+ while (count > 0){
+ count = read(fdin,buf,SIZE);
+ l += count;
+ if (verb)
+ fprintf(stderr,"Writing TS %2.2f %%\r",
+ 100.*l/length);
+
+ get_pes(buf,count,&p,pes_in_ts);
+ }
+
+}
+
+
+#define IN_SIZE TS_SIZE*10
+void find_avpids(int fd, uint16_t *vpid, uint16_t *apid)
+{
+ uint8_t buf[IN_SIZE];
+ int count;
+ int i;
+ int off =0;
+
+ while ( *apid == 0 || *vpid == 0){
+ count = read(fd, buf, IN_SIZE);
+ for (i = 0; i < count-7; i++){
+ if (buf[i] == 0x47){
+ if (buf[i+1] & 0x40){
+ off = 0;
+ if ( buf[3+i] & 0x20)//adapt field?
+ off = buf[4+i] + 1;
+ switch(buf[i+7+off]){
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ *vpid = get_pid(buf+i+1);
+ break;
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ *apid = get_pid(buf+i+1);
+ break;
+ }
+ }
+ i += 187;
+ }
+ if (*apid != 0 && *vpid != 0) break;
+ }
+ }
+}
+
+void find_bavpids(uint8_t *buf, int count, uint16_t *vpid, uint16_t *apid)
+{
+ int i;
+ int founda = 0;
+ int foundb = 0;
+ int off = 0;
+
+ *vpid = 0;
+ *apid = 0;
+ for (i = 0; i < count-7; i++){
+ if (buf[i] == 0x47){
+ if ((buf[i+1] & 0xF0) == 0x40){
+ off = 0;
+ if ( buf[3+i] & 0x20) // adaptation field?
+ off = buf[4+i] + 1;
+
+ if (buf[off+i+4] == 0x00 &&
+ buf[off+i+5] == 0x00 &&
+ buf[off+i+6] == 0x01){
+ switch(buf[off+i+7]){
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ *vpid = get_pid(buf+i+1);
+ foundb=1;
+ break;
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ *apid = get_pid(buf+i+1);
+ founda=1;
+ break;
+ }
+ }
+ }
+ i += 187;
+ }
+ if (founda && foundb) break;
+ }
+}
+
+
+void ts_to_pes( int fdin, uint16_t pida, uint16_t pidv, int ps)
+{
+
+ uint8_t buf[IN_SIZE];
+ uint8_t mbuf[TS_SIZE];
+ int i;
+ int count = 1;
+ uint16_t pid;
+ uint16_t dummy;
+ ipack pa, pv;
+ ipack *p;
+
+ if (fdin != STDIN_FILENO && (!pida || !pidv))
+ find_avpids(fdin, &pidv, &pida);
+
+ init_ipack(&pa, IPACKS,write_out, ps);
+ init_ipack(&pv, IPACKS,write_out, ps);
+
+ if ((count = save_read(fdin,mbuf,TS_SIZE))<0)
+ perror("reading");
+
+ for ( i = 0; i < 188 ; i++){
+ if ( mbuf[i] == 0x47 ) break;
+ }
+ if ( i == 188){
+ fprintf(stderr,"Not a TS\n");
+ return;
+ } else {
+ memcpy(buf,mbuf+i,TS_SIZE-i);
+ if ((count = save_read(fdin,mbuf,i))<0)
+ perror("reading");
+ memcpy(buf+TS_SIZE-i,mbuf,i);
+ i = 188;
+ }
+ count = 1;
+ while (count > 0){
+ if ((count = save_read(fdin,buf+i,IN_SIZE-i)+i)<0)
+ perror("reading");
+
+
+ if (!pidv){
+ find_bavpids(buf+i, IN_SIZE-i, &pidv, &dummy);
+ if (pidv) fprintf(stderr, "vpid %d (0x%02x)\n",
+ pidv,pidv);
+ }
+
+ if (!pida){
+ find_bavpids(buf+i, IN_SIZE-i, &dummy, &pida);
+ if (pida) fprintf(stderr, "apid %d (0x%02x)\n",
+ pida,pida);
+ }
+
+
+ for( i = 0; i < count; i+= TS_SIZE){
+ uint8_t off = 0;
+
+ if ( count - i < TS_SIZE) break;
+
+ pid = get_pid(buf+i+1);
+ if (!(buf[3+i]&0x10)) // no payload?
+ continue;
+ if ( buf[1+i]&0x80){
+ fprintf(stderr,"Error in TS for PID: %d\n",
+ pid);
+ }
+ if (pid == pidv){
+ p = &pv;
+ } else {
+ if (pid == pida){
+ p = &pa;
+ } else continue;
+ }
+
+ if ( buf[1+i]&0x40) {
+ if (p->plength == MMAX_PLENGTH-6){
+ p->plength = p->found-6;
+ p->found = 0;
+ send_ipack(p);
+ reset_ipack(p);
+ }
+ }
+
+ if ( buf[3+i] & 0x20) { // adaptation field?
+ off = buf[4+i] + 1;
+ }
+
+ instant_repack(buf+4+off+i, TS_SIZE-4-off, p);
+ }
+ i = 0;
+
+ }
+
+}
+
+
+#define INN_SIZE 2*IN_SIZE
+void insert_pat_pmt( int fdin, int fdout)
+{
+
+ uint8_t buf[INN_SIZE];
+ uint8_t mbuf[TS_SIZE];
+ int i;
+ int count = 1;
+ uint16_t pida = 0;
+ uint16_t pidv = 0;
+ int written,c;
+ uint8_t c0 = 0;
+ uint8_t c1 = 0;
+ uint8_t pmt_len;
+ uint32_t crc32;
+
+
+ find_avpids(fdin, &pidv, &pida);
+
+ count = save_read(fdin,mbuf,TS_SIZE);
+ for ( i = 0; i < 188 ; i++){
+ if ( mbuf[i] == 0x47 ) break;
+ }
+ if ( i == 188){
+ fprintf(stderr,"Not a TS\n");
+ return;
+ } else {
+ memcpy(buf,mbuf+i,TS_SIZE-i);
+ count = save_read(fdin,mbuf,i);
+ memcpy(buf+TS_SIZE-i,mbuf,i);
+ i = 188;
+ }
+
+ count = 1;
+ /* length is not correct, but we only create a very small
+ * PMT, so it doesn't matter :-)
+ */
+ pmt_len = tspid1[7] + 3;
+ while (count > 0){
+ tspid1[24] = pidv;
+ tspid1[23] |= (pidv >> 8) & 0x3F;
+ tspid1[29] = pida;
+ tspid1[28] |= (pida >> 8) & 0x3F;
+ crc32 = calc_crc32 (&tspid1[5], pmt_len - 4);
+ tspid1[5 + pmt_len - 4] = (crc32 & 0xff000000) >> 24;
+ tspid1[5 + pmt_len - 3] = (crc32 & 0x00ff0000) >> 16;
+ tspid1[5 + pmt_len - 2] = (crc32 & 0x0000ff00) >> 8;
+ tspid1[5 + pmt_len - 1] = (crc32 & 0x000000ff) >> 0;
+
+ write(fdout,tspid0,188);
+ write(fdout,tspid1,188);
+
+ count = save_read(fdin,buf+i,INN_SIZE-i);
+
+ written = 0;
+ while (written < IN_SIZE){
+ c = write(fdout,buf,INN_SIZE);
+ if (c>0) written += c;
+ }
+ tspid0[3] &= 0xF0 ;
+ tspid0[3] |= (c0++)& 0x0F ;
+
+ tspid1[3] &= 0xF0 ;
+ tspid1[3] |= (c1++)& 0x0F ;
+
+ i=0;
+ }
+
+}
+
+void get_pes (uint8_t *buf, int count, p2p *p, void (*func)(p2p *p))
+{
+
+ int l;
+ unsigned short *pl;
+ int c=0;
+
+ uint8_t headr[3] = { 0x00, 0x00, 0x01} ;
+
+ while (c < count && (p->mpeg == 0 ||
+ (p->mpeg == 1 && p->found < 7) ||
+ (p->mpeg == 2 && p->found < 9))
+ && (p->found < 5 || !p->done)){
+ switch ( p->found ){
+ case 0:
+ case 1:
+ if (buf[c] == 0x00) p->found++;
+ else p->found = 0;
+ c++;
+ break;
+ case 2:
+ if (buf[c] == 0x01) p->found++;
+ else if (buf[c] == 0){
+ p->found = 2;
+ } else p->found = 0;
+ c++;
+ break;
+ case 3:
+ p->cid = 0;
+ switch (buf[c]){
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ p->done = 1;
+ case PRIVATE_STREAM1:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ p->found++;
+ p->cid = buf[c];
+ c++;
+ break;
+ default:
+ p->found = 0;
+ break;
+ }
+ break;
+
+
+ case 4:
+ if (count-c > 1){
+ pl = (unsigned short *) (buf+c);
+ p->plength = ntohs(*pl);
+ p->plen[0] = buf[c];
+ c++;
+ p->plen[1] = buf[c];
+ c++;
+ p->found+=2;
+ } else {
+ p->plen[0] = buf[c];
+ p->found++;
+ return;
+ }
+ break;
+ case 5:
+ p->plen[1] = buf[c];
+ c++;
+ pl = (unsigned short *) p->plen;
+ p->plength = ntohs(*pl);
+ p->found++;
+ break;
+
+
+ case 6:
+ if (!p->done){
+ p->flag1 = buf[c];
+ c++;
+ p->found++;
+ if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2;
+ else {
+ p->hlength = 0;
+ p->which = 0;
+ p->mpeg = 1;
+ p->flag2 = 0;
+ }
+ }
+ break;
+
+ case 7:
+ if ( !p->done && p->mpeg == 2){
+ p->flag2 = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ case 8:
+ if ( !p->done && p->mpeg == 2){
+ p->hlength = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ default:
+
+ break;
+ }
+ }
+
+ if (!p->plength) p->plength = MMAX_PLENGTH-6;
+
+
+ if ( p->done || ((p->mpeg == 2 && p->found >= 9) ||
+ (p->mpeg == 1 && p->found >= 7)) ){
+ switch (p->cid){
+
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case PRIVATE_STREAM1:
+
+ memcpy(p->buf, headr, 3);
+ p->buf[3] = p->cid;
+ memcpy(p->buf+4,p->plen,2);
+
+ if (p->mpeg == 2 && p->found == 9){
+ p->buf[6] = p->flag1;
+ p->buf[7] = p->flag2;
+ p->buf[8] = p->hlength;
+ }
+
+ if (p->mpeg == 1 && p->found == 7){
+ p->buf[6] = p->flag1;
+ }
+
+
+ if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
+ p->found < 14){
+ while (c < count && p->found < 14){
+ p->pts[p->found-9] = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ }
+ if (c == count) return;
+ }
+
+ if (p->mpeg == 1 && p->which < 2000){
+
+ if (p->found == 7) {
+ p->check = p->flag1;
+ p->hlength = 1;
+ }
+
+ while (!p->which && c < count &&
+ p->check == 0xFF){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ }
+
+ if ( c == count) return;
+
+ if ( (p->check & 0xC0) == 0x40 && !p->which){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+
+ p->which = 1;
+ if ( c == count) return;
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return;
+ }
+
+ if (p->which == 1){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return;
+ }
+
+ if ( (p->check & 0x30) && p->check != 0xFF){
+ p->flag2 = (p->check & 0xF0) << 2;
+ p->pts[0] = p->check;
+ p->which = 3;
+ }
+
+ if ( c == count) return;
+ if (p->which > 2){
+ if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_ONLY){
+ while (c < count &&
+ p->which < 7){
+ p->pts[p->which-2] =
+ buf[c];
+ p->buf[p->found] =
+ buf[c];
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return;
+ } else if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_DTS){
+ while (c < count &&
+ p->which< 12){
+ if (p->which< 7)
+ p->pts[p->which
+ -2] =
+ buf[c];
+ p->buf[p->found] =
+ buf[c];
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return;
+ }
+ p->which = 2000;
+ }
+
+ }
+
+ while (c < count && p->found < p->plength+6){
+ l = count -c;
+ if (l+p->found > p->plength+6)
+ l = p->plength+6-p->found;
+ memcpy(p->buf+p->found, buf+c, l);
+ p->found += l;
+ c += l;
+ }
+ if(p->found == p->plength+6)
+ func(p);
+
+ break;
+ }
+
+
+ if ( p->done ){
+ if( p->found + count - c < p->plength+6){
+ p->found += count-c;
+ c = count;
+ } else {
+ c += p->plength+6 - p->found;
+ p->found = p->plength+6;
+ }
+ }
+
+ if (p->plength && p->found == p->plength+6) {
+ p->found = 0;
+ p->done = 0;
+ p->plength = 0;
+ memset(p->buf, 0, MAX_PLENGTH);
+ if (c < count)
+ get_pes(buf+c, count-c, p, func);
+ }
+ }
+ return;
+}
+
+
+
+
+void setup_pes2ts( p2p *p, uint32_t pida, uint32_t pidv,
+ void (*ts_write)(uint8_t *buf, int count, void *p))
+{
+ init_p2p( p, ts_write, 2048);
+ p->pida = pida;
+ p->pidv = pidv;
+ p->acounter = 0;
+ p->vcounter = 0;
+ p->count1 = 0;
+ p->count0 = 0;
+}
+
+void kpes_to_ts( p2p *p,uint8_t *buf ,int count )
+{
+ get_pes(buf,count, p,pes_in_ts);
+}
+
+
+void setup_ts2pes( p2p *pa, p2p *pv, uint32_t pida, uint32_t pidv,
+ void (*pes_write)(uint8_t *buf, int count, void *p))
+{
+ init_p2p( pa, pes_write, 2048);
+ init_p2p( pv, pes_write, 2048);
+ pa->pid = pida;
+ pv->pid = pidv;
+}
+
+void kts_to_pes( p2p *p, uint8_t *buf) // don't need count (=188)
+{
+ uint8_t off = 0;
+ uint16_t pid = 0;
+
+ if (!(buf[3]&PAYLOAD)) // no payload?
+ return;
+
+ pid = get_pid(buf+1);
+
+ if (pid != p->pid) return;
+ if ( buf[1]&0x80){
+ fprintf(stderr,"Error in TS for PID: %d\n",
+ pid);
+ }
+
+ if ( buf[1]&PAY_START) {
+ if (p->plength == MMAX_PLENGTH-6){
+ p->plength = p->found-6;
+ p->found = 0;
+ pes_repack(p);
+ }
+ }
+
+ if ( buf[3] & ADAPT_FIELD) { // adaptation field?
+ off = buf[4] + 1;
+ if (off+4 > 187) return;
+ }
+
+ get_pes(buf+4+off, TS_SIZE-4-off, p , pes_repack);
+}
+
+
+
+
+// instant repack
+
+
+void reset_ipack(ipack *p)
+{
+ p->found = 0;
+ p->cid = 0;
+ p->plength = 0;
+ p->flag1 = 0;
+ p->flag2 = 0;
+ p->hlength = 0;
+ p->mpeg = 0;
+ p->check = 0;
+ p->which = 0;
+ p->done = 0;
+ p->count = 0;
+ p->size = p->size_orig;
+}
+
+void init_ipack(ipack *p, int size,
+ void (*func)(uint8_t *buf, int size, void *priv), int ps)
+{
+ if ( !(p->buf = malloc(size)) ){
+ fprintf(stderr,"Couldn't allocate memory for ipack\n");
+ exit(1);
+ }
+ p->ps = ps;
+ p->size_orig = size;
+ p->func = func;
+ reset_ipack(p);
+ p->has_ai = 0;
+ p->has_vi = 0;
+ p->start = 0;
+}
+
+void free_ipack(ipack * p)
+{
+ if (p->buf) free(p->buf);
+}
+
+
+
+int get_vinfo(uint8_t *mbuf, int count, VideoInfo *vi, int pr)
+{
+ uint8_t *headr;
+ int found = 0;
+ int sw;
+ int form = -1;
+ int c = 0;
+
+ while (found < 4 && c+4 < count){
+ uint8_t *b;
+
+ b = mbuf+c;
+ if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01
+ && b[3] == 0xb3) found = 4;
+ else {
+ c++;
+ }
+ }
+
+ if (! found) return -1;
+ c += 4;
+ if (c+12 >= count) return -1;
+ headr = mbuf+c;
+
+ vi->horizontal_size = ((headr[1] &0xF0) >> 4) | (headr[0] << 4);
+ vi->vertical_size = ((headr[1] &0x0F) << 8) | (headr[2]);
+
+ sw = (int)((headr[3]&0xF0) >> 4) ;
+
+ switch( sw ){
+ case 1:
+ if (pr)
+ fprintf(stderr,"Videostream: ASPECT: 1:1");
+ vi->aspect_ratio = 100;
+ break;
+ case 2:
+ if (pr)
+ fprintf(stderr,"Videostream: ASPECT: 4:3");
+ vi->aspect_ratio = 133;
+ break;
+ case 3:
+ if (pr)
+ fprintf(stderr,"Videostream: ASPECT: 16:9");
+ vi->aspect_ratio = 177;
+ break;
+ case 4:
+ if (pr)
+ fprintf(stderr,"Videostream: ASPECT: 2.21:1");
+ vi->aspect_ratio = 221;
+ break;
+
+ case 5 ... 15:
+ if (pr)
+ fprintf(stderr,"Videostream: ASPECT: reserved");
+ vi->aspect_ratio = 0;
+ break;
+
+ default:
+ vi->aspect_ratio = 0;
+ return -1;
+ }
+
+ if (pr)
+ fprintf(stderr," Size = %dx%d",vi->horizontal_size,
+ vi->vertical_size);
+
+ sw = (int)(headr[3]&0x0F);
+
+ switch ( sw ) {
+ case 1:
+ if (pr)
+ fprintf(stderr," FRate: 23.976 fps");
+ vi->framerate = 24000/1001.;
+ form = -1;
+ break;
+ case 2:
+ if (pr)
+ fprintf(stderr," FRate: 24 fps");
+ vi->framerate = 24;
+ form = -1;
+ break;
+ case 3:
+ if (pr)
+ fprintf(stderr," FRate: 25 fps");
+ vi->framerate = 25;
+ form = VIDEO_MODE_PAL;
+ break;
+ case 4:
+ if (pr)
+ fprintf(stderr," FRate: 29.97 fps");
+ vi->framerate = 30000/1001.;
+ form = VIDEO_MODE_NTSC;
+ break;
+ case 5:
+ if (pr)
+ fprintf(stderr," FRate: 30 fps");
+ vi->framerate = 30;
+ form = VIDEO_MODE_NTSC;
+ break;
+ case 6:
+ if (pr)
+ fprintf(stderr," FRate: 50 fps");
+ vi->framerate = 50;
+ form = VIDEO_MODE_PAL;
+ break;
+ case 7:
+ if (pr)
+ fprintf(stderr," FRate: 60 fps");
+ vi->framerate = 60;
+ form = VIDEO_MODE_NTSC;
+ break;
+ }
+
+ vi->bit_rate = 400*(((headr[4] << 10) & 0x0003FC00UL)
+ | ((headr[5] << 2) & 0x000003FCUL) |
+ (((headr[6] & 0xC0) >> 6) & 0x00000003UL));
+
+ if (pr){
+ fprintf(stderr," BRate: %.2f Mbit/s",(vi->bit_rate)/1000000.);
+ fprintf(stderr,"\n");
+ }
+ vi->video_format = form;
+
+ vi->off = c-4;
+ return c-4;
+}
+
+extern unsigned int bitrates[3][16];
+extern uint32_t freq[4];
+
+int get_ainfo(uint8_t *mbuf, int count, AudioInfo *ai, int pr)
+{
+ uint8_t *headr;
+ int found = 0;
+ int c = 0;
+ int fr =0;
+
+ while (!found && c < count){
+ uint8_t *b = mbuf+c;
+
+ if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8)
+ found = 1;
+ else {
+ c++;
+ }
+ }
+
+ if (!found) return -1;
+
+ if (c+3 >= count) return -1;
+ headr = mbuf+c;
+
+ ai->layer = (headr[1] & 0x06) >> 1;
+
+ if (pr)
+ fprintf(stderr,"Audiostream: Layer: %d", 4-ai->layer);
+
+
+ ai->bit_rate = bitrates[(3-ai->layer)][(headr[2] >> 4 )]*1000;
+
+ if (pr){
+ if (ai->bit_rate == 0)
+ fprintf (stderr," Bit rate: free");
+ else if (ai->bit_rate == 0xf)
+ fprintf (stderr," BRate: reserved");
+ else
+ fprintf (stderr," BRate: %d kb/s", ai->bit_rate/1000);
+ }
+
+ fr = (headr[2] & 0x0c ) >> 2;
+ ai->frequency = freq[fr]*100;
+
+ if (pr){
+ if (ai->frequency == 3)
+ fprintf (stderr, " Freq: reserved\n");
+ else
+ fprintf (stderr," Freq: %2.1f kHz\n",
+ ai->frequency/1000.);
+ }
+ ai->off = c;
+ return c;
+}
+
+unsigned int ac3_bitrates[32] =
+ {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+uint32_t ac3_freq[4] = {480, 441, 320, 0};
+uint32_t ac3_frames[3][32] =
+ {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024,
+ 1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114,
+ 1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ {96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344,
+ 1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+
+int get_ac3info(uint8_t *mbuf, int count, AudioInfo *ai, int pr)
+{
+ uint8_t *headr;
+ int found = 0;
+ int c = 0;
+ uint8_t frame;
+ int fr = 0;
+
+ while ( !found && c < count){
+ uint8_t *b = mbuf+c;
+ if ( b[0] == 0x0b && b[1] == 0x77 )
+ found = 1;
+ else {
+ c++;
+ }
+ }
+
+
+ if (!found){
+ return -1;
+ }
+ ai->off = c;
+
+ if (c+5 >= count) return -1;
+
+ ai->layer = 0; // 0 for AC3
+ headr = mbuf+c+2;
+
+ frame = (headr[2]&0x3f);
+ ai->bit_rate = ac3_bitrates[frame>>1]*1000;
+
+ if (pr) fprintf (stderr," BRate: %d kb/s", ai->bit_rate/1000);
+
+ fr = (headr[2] & 0xc0 ) >> 6;
+ ai->frequency = freq[fr]*100;
+ if (pr) fprintf (stderr," Freq: %d Hz\n", ai->frequency);
+
+ ai->framesize = ac3_frames[fr][frame >> 1];
+ if ((frame & 1) && (fr == 1)) ai->framesize++;
+ ai->framesize = ai->framesize << 1;
+ if (pr) fprintf (stderr," Framesize %d\n", ai->framesize);
+
+ return c;
+}
+
+
+void ps_pes(ipack *p)
+{
+ int check;
+ uint8_t pbuf[PS_HEADER_L2];
+ static int muxr = 0;
+ static int ai = 0;
+ static int vi = 0;
+ static int start = 0;
+ static uint32_t SCR = 0;
+
+ if (p->mpeg == 2){
+ switch(p->buf[3]){
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ if (!p->has_vi){
+ if(get_vinfo(p->buf, p->count, &p->vi,1) >=0) {
+ p->has_vi = 1;
+ vi = p->vi.bit_rate;
+ }
+ }
+ break;
+
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ if (!p->has_ai){
+ if(get_ainfo(p->buf, p->count, &p->ai,1) >=0) {
+ p->has_ai = 1;
+ ai = p->ai.bit_rate;
+ }
+ }
+ break;
+ }
+
+ if (p->has_vi && vi && !muxr){
+ muxr = (vi+ai)/400;
+ }
+
+ if ( start && muxr && (p->buf[7] & PTS_ONLY) && (p->has_ai ||
+ p->buf[9+p->buf[8]+4] == 0xb3)){
+ SCR = trans_pts_dts(p->pts)-3600;
+
+ check = write_ps_header(pbuf,
+ SCR,
+ muxr, 1, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0);
+
+ p->func(pbuf, check , p->data);
+ }
+
+ if (muxr && !start && vi){
+ SCR = trans_pts_dts(p->pts)-3600;
+ check = write_ps_header(pbuf,
+ SCR,
+ muxr, 1, 0, 0, 1, 1, 1,
+ 0xC0, 0, 64, 0xE0, 1, 460);
+ start = 1;
+ p->func(pbuf, check , p->data);
+ }
+
+ if (start)
+ p->func(p->buf, p->count, p->data);
+ }
+}
+
+void send_ipack(ipack *p)
+{
+ int streamid=0;
+ int off;
+ int ac3_off = 0;
+ AudioInfo ai;
+ int nframes= 0;
+ int f=0;
+
+ if (p->count < 10) return;
+ p->buf[3] = p->cid;
+ p->buf[4] = (uint8_t)(((p->count-6) & 0xFF00) >> 8);
+ p->buf[5] = (uint8_t)((p->count-6) & 0x00FF);
+
+
+ if (p->cid == PRIVATE_STREAM1){
+
+ off = 9+p->buf[8];
+ streamid = p->buf[off];
+ if ((streamid & 0xF8) == 0x80){
+ ai.off = 0;
+ ac3_off = ((p->buf[off+2] << 8)| p->buf[off+3]);
+ if (ac3_off < p->count)
+ f=get_ac3info(p->buf+off+3+ac3_off,
+ p->count-ac3_off, &ai,0);
+ if ( !f ){
+ nframes = (p->count-off-3-ac3_off)/
+ ai.framesize + 1;
+ p->buf[off+1] = nframes;
+ p->buf[off+2] = (ac3_off >> 8)& 0xFF;
+ p->buf[off+3] = (ac3_off)& 0xFF;
+
+ ac3_off += nframes * ai.framesize - p->count;
+ }
+ }
+ }
+
+ if (p->ps) ps_pes(p);
+ else p->func(p->buf, p->count, p->data);
+
+ switch ( p->mpeg ){
+ case 2:
+
+ p->buf[6] = 0x80;
+ p->buf[7] = 0x00;
+ p->buf[8] = 0x00;
+ p->count = 9;
+
+ if (p->cid == PRIVATE_STREAM1 && (streamid & 0xF8)==0x80 ){
+ p->count += 4;
+ p->buf[9] = streamid;
+ p->buf[10] = 0;
+ p->buf[11] = (ac3_off >> 8)& 0xFF;
+ p->buf[12] = (ac3_off)& 0xFF;
+ }
+
+ break;
+ case 1:
+ p->buf[6] = 0x0F;
+ p->count = 7;
+ break;
+ }
+
+}
+
+
+static void write_ipack(ipack *p, uint8_t *data, int count)
+{
+ AudioInfo ai;
+ uint8_t headr[3] = { 0x00, 0x00, 0x01} ;
+ int diff =0;
+
+ if (p->count < 6){
+ if (trans_pts_dts(p->pts) > trans_pts_dts(p->last_pts))
+ memcpy(p->last_pts, p->pts, 5);
+ p->count = 0;
+ memcpy(p->buf+p->count, headr, 3);
+ p->count += 6;
+ }
+ if ( p->size == p->size_orig && p->plength &&
+ (diff = 6+p->plength - p->found + p->count +count) > p->size &&
+ diff < 3*p->size/2){
+
+ p->size = diff/2;
+// fprintf(stderr,"size: %d \n",p->size);
+ }
+
+ if (p->cid == PRIVATE_STREAM1 && p->count == p->hlength+9){
+ if ((data[0] & 0xF8) != 0x80){
+ int ac3_off;
+
+ ac3_off = get_ac3info(data, count, &ai,0);
+ if (ac3_off>=0 && ai.framesize){
+ p->buf[p->count] = 0x80;
+ p->buf[p->count+1] = (p->size - p->count
+ - 4 - ac3_off)/
+ ai.framesize + 1;
+ p->buf[p->count+2] = (ac3_off >> 8)& 0xFF;
+ p->buf[p->count+3] = (ac3_off)& 0xFF;
+ p->count+=4;
+
+ }
+ }
+ }
+
+ if (p->count + count < p->size){
+ memcpy(p->buf+p->count, data, count);
+ p->count += count;
+ } else {
+ int rest = p->size - p->count;
+ if (rest < 0) rest = 0;
+ memcpy(p->buf+p->count, data, rest);
+ p->count += rest;
+// fprintf(stderr,"count: %d \n",p->count);
+ send_ipack(p);
+ if (count - rest > 0)
+ write_ipack(p, data+rest, count-rest);
+ }
+}
+
+void instant_repack (uint8_t *buf, int count, ipack *p)
+{
+
+ int l;
+ unsigned short *pl;
+ int c=0;
+
+ while (c < count && (p->mpeg == 0 ||
+ (p->mpeg == 1 && p->found < 7) ||
+ (p->mpeg == 2 && p->found < 9))
+ && (p->found < 5 || !p->done)){
+ switch ( p->found ){
+ case 0:
+ case 1:
+ if (buf[c] == 0x00) p->found++;
+ else p->found = 0;
+ c++;
+ break;
+ case 2:
+ if (buf[c] == 0x01) p->found++;
+ else if (buf[c] == 0){
+ p->found = 2;
+ } else p->found = 0;
+ c++;
+ break;
+ case 3:
+ p->cid = 0;
+ switch (buf[c]){
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ p->done = 1;
+ case PRIVATE_STREAM1:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ p->found++;
+ p->cid = buf[c];
+ c++;
+ break;
+ default:
+ p->found = 0;
+ break;
+ }
+ break;
+
+
+ case 4:
+ if (count-c > 1){
+ pl = (unsigned short *) (buf+c);
+ p->plength = ntohs(*pl);
+ p->plen[0] = buf[c];
+ c++;
+ p->plen[1] = buf[c];
+ c++;
+ p->found+=2;
+ } else {
+ p->plen[0] = buf[c];
+ p->found++;
+ return;
+ }
+ break;
+ case 5:
+ p->plen[1] = buf[c];
+ c++;
+ pl = (unsigned short *) p->plen;
+ p->plength = ntohs(*pl);
+ p->found++;
+ break;
+
+
+ case 6:
+ if (!p->done){
+ p->flag1 = buf[c];
+ c++;
+ p->found++;
+ if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2;
+ else {
+ p->hlength = 0;
+ p->which = 0;
+ p->mpeg = 1;
+ p->flag2 = 0;
+ }
+ }
+ break;
+
+ case 7:
+ if ( !p->done && p->mpeg == 2){
+ p->flag2 = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ case 8:
+ if ( !p->done && p->mpeg == 2){
+ p->hlength = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ default:
+
+ break;
+ }
+ }
+
+
+ if (c == count) return;
+
+ if (!p->plength) p->plength = MMAX_PLENGTH-6;
+
+
+ if ( p->done || ((p->mpeg == 2 && p->found >= 9) ||
+ (p->mpeg == 1 && p->found >= 7)) ){
+ switch (p->cid){
+
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case PRIVATE_STREAM1:
+
+ if (p->mpeg == 2 && p->found == 9){
+ write_ipack(p, &p->flag1, 1);
+ write_ipack(p, &p->flag2, 1);
+ write_ipack(p, &p->hlength, 1);
+ }
+
+ if (p->mpeg == 1 && p->found == 7){
+ write_ipack(p, &p->flag1, 1);
+ }
+
+
+ if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
+ p->found < 14){
+ while (c < count && p->found < 14){
+ p->pts[p->found-9] = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ }
+ if (c == count) return;
+ }
+
+ if (p->mpeg == 1 && p->which < 2000){
+
+ if (p->found == 7) {
+ p->check = p->flag1;
+ p->hlength = 1;
+ }
+
+ while (!p->which && c < count &&
+ p->check == 0xFF){
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+ }
+
+ if ( c == count) return;
+
+ if ( (p->check & 0xC0) == 0x40 && !p->which){
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+
+ p->which = 1;
+ if ( c == count) return;
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return;
+ }
+
+ if (p->which == 1){
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return;
+ }
+
+ if ( (p->check & 0x30) && p->check != 0xFF){
+ p->flag2 = (p->check & 0xF0) << 2;
+ p->pts[0] = p->check;
+ p->which = 3;
+ }
+
+ if ( c == count) return;
+ if (p->which > 2){
+ if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_ONLY){
+ while (c < count &&
+ p->which < 7){
+ p->pts[p->which-2] =
+ buf[c];
+ write_ipack(p,buf+c,1);
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return;
+ } else if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_DTS){
+ while (c < count &&
+ p->which< 12){
+ if (p->which< 7)
+ p->pts[p->which
+ -2] =
+ buf[c];
+ write_ipack(p,buf+c,1);
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return;
+ }
+ p->which = 2000;
+ }
+
+ }
+
+ while (c < count && p->found < p->plength+6){
+ l = count -c;
+ if (l+p->found > p->plength+6)
+ l = p->plength+6-p->found;
+ write_ipack(p, buf+c, l);
+ p->found += l;
+ c += l;
+ }
+
+ break;
+ }
+
+
+ if ( p->done ){
+ if( p->found + count - c < p->plength+6){
+ p->found += count-c;
+ c = count;
+ } else {
+ c += p->plength+6 - p->found;
+ p->found = p->plength+6;
+ }
+ }
+
+ if (p->plength && p->found == p->plength+6) {
+ send_ipack(p);
+ reset_ipack(p);
+ if (c < count)
+ instant_repack(buf+c, count-c, p);
+ }
+ }
+ return;
+}
+
+void write_out_es(uint8_t *buf, int count,void *priv)
+{
+ ipack *p = (ipack *) priv;
+ uint8_t payl = buf[8]+9+p->start-1;
+
+ write(p->fd, buf+payl, count-payl);
+ p->start = 1;
+}
+
+void write_out_pes(uint8_t *buf, int count,void *priv)
+{
+ ipack *p = (ipack *) priv;
+ write(p->fd, buf, count);
+}
+
+
+
+int64_t ts_demux(int fdin, int fdv_out,int fda_out,uint16_t pida,
+ uint16_t pidv, int es)
+{
+ uint8_t buf[IN_SIZE];
+ uint8_t mbuf[TS_SIZE];
+ int i;
+ int count = 1;
+ uint16_t pid;
+ ipack pa, pv;
+ ipack *p;
+ uint8_t *sb;
+ int64_t apts=0;
+ int64_t vpts=0;
+ int verb = 0;
+ uint64_t length =0;
+ uint64_t l=0;
+ int perc =0;
+ int last_perc =0;
+
+ if (fdin != STDIN_FILENO) verb = 1;
+
+ if (verb) {
+ length = lseek(fdin, 0, SEEK_END);
+ lseek(fdin,0,SEEK_SET);
+ }
+
+ if (!pida || !pidv)
+ find_avpids(fdin, &pidv, &pida);
+
+ if (es){
+ init_ipack(&pa, IPACKS,write_out_es, 0);
+ init_ipack(&pv, IPACKS,write_out_es, 0);
+ } else {
+ init_ipack(&pa, IPACKS,write_out_pes, 0);
+ init_ipack(&pv, IPACKS,write_out_pes, 0);
+ }
+ pa.fd = fda_out;
+ pv.fd = fdv_out;
+ pa.data = (void *)&pa;
+ pv.data = (void *)&pv;
+
+ count = save_read(fdin,mbuf,TS_SIZE);
+ if (count) l+=count;
+ for ( i = 0; i < 188 ; i++){
+ if ( mbuf[i] == 0x47 ) break;
+ }
+ if ( i == 188){
+ fprintf(stderr,"Not a TS\n");
+ return 0;
+ } else {
+ memcpy(buf,mbuf+i,TS_SIZE-i);
+ count = save_read(fdin,mbuf,i);
+ if (count) l+=count;
+ memcpy(buf+TS_SIZE-i,mbuf,i);
+ i = 188;
+ }
+
+ count = 1;
+ while (count > 0){
+ count = save_read(fdin,buf+i,IN_SIZE-i)+i;
+ if (count) l+=count;
+ if (verb && perc >last_perc){
+ perc = (100*l)/length;
+ fprintf(stderr,"Reading TS %d %%\r",perc);
+ last_perc = perc;
+ }
+
+ for( i = 0; i < count; i+= TS_SIZE){
+ uint8_t off = 0;
+
+ if ( count - i < TS_SIZE) break;
+
+ pid = get_pid(buf+i+1);
+ if (!(buf[3+i]&0x10)) // no payload?
+ continue;
+ if ( buf[1+i]&0x80){
+ fprintf(stderr,"Error in TS for PID: %d\n",
+ pid);
+ }
+ if (pid == pidv){
+ p = &pv;
+ } else {
+ if (pid == pida){
+ p = &pa;
+ } else continue;
+ }
+
+ if ( buf[3+i] & 0x20) { // adaptation field?
+ off = buf[4+i] + 1;
+ }
+
+ if ( buf[1+i]&0x40) {
+ if (p->plength == MMAX_PLENGTH-6){
+ p->plength = p->found-6;
+ p->found = 0;
+ send_ipack(p);
+ reset_ipack(p);
+ }
+ sb = buf+4+off+i;
+ if( es &&
+ !p->start && (sb[7] & PTS_DTS_FLAGS)){
+ uint8_t *pay = sb+sb[8]+9;
+ int l = TS_SIZE - 13 - off - sb[8];
+ if ( pid == pidv &&
+ (p->start =
+ get_vinfo( pay, l,&p->vi,1)+1) >0
+ ){
+ vpts = trans_pts_dts(sb+9);
+ printf("vpts : %fs\n",
+ vpts/90000.);
+ }
+ if ( pid == pida && es==1 &&
+ (p->start =
+ get_ainfo( pay, l,&p->ai,1)+1) >0
+ ){
+ apts = trans_pts_dts(sb+9);
+ printf("apts : %fs\n",
+ apts/90000.);
+ }
+ if ( pid == pida && es==2 &&
+ (p->start =
+ get_ac3info( pay, l,&p->ai,1)+1) >0
+ ){
+ apts = trans_pts_dts(sb+9);
+ printf("apts : %fs\n",
+ apts/90000.);
+ }
+ }
+ }
+
+ if (p->start)
+ instant_repack(buf+4+off+i, TS_SIZE-4-off, p);
+ }
+ i = 0;
+
+ }
+
+ return (vpts-apts);
+}
+
+void ts2es_opt(int fdin, uint16_t pidv, ipack *p, int verb)
+{
+ uint8_t buf[IN_SIZE];
+ uint8_t mbuf[TS_SIZE];
+ int i;
+ int count = 1;
+ uint64_t length =0;
+ uint64_t l=0;
+ int perc =0;
+ int last_perc =0;
+ uint16_t pid;
+
+ if (verb) {
+ length = lseek(fdin, 0, SEEK_END);
+ lseek(fdin,0,SEEK_SET);
+ }
+
+ count = save_read(fdin,mbuf,TS_SIZE);
+ if (count) l+=count;
+ for ( i = 0; i < 188 ; i++){
+ if ( mbuf[i] == 0x47 ) break;
+ }
+ if ( i == 188){
+ fprintf(stderr,"Not a TS\n");
+ return;
+ } else {
+ memcpy(buf,mbuf+i,TS_SIZE-i);
+ count = save_read(fdin,mbuf,i);
+ if (count) l+=count;
+ memcpy(buf+TS_SIZE-i,mbuf,i);
+ i = 188;
+ }
+
+ count = 1;
+ while (count > 0){
+ count = save_read(fdin,buf+i,IN_SIZE-i)+i;
+ if (count) l+=count;
+ if (verb && perc >last_perc){
+ perc = (100*l)/length;
+ fprintf(stderr,"Reading TS %d %%\r",perc);
+ last_perc = perc;
+ }
+
+ for( i = 0; i < count; i+= TS_SIZE){
+ uint8_t off = 0;
+
+ if ( count - i < TS_SIZE) break;
+
+ pid = get_pid(buf+i+1);
+ if (!(buf[3+i]&0x10)) // no payload?
+ continue;
+ if ( buf[1+i]&0x80){
+ fprintf(stderr,"Error in TS for PID: %d\n",
+ pid);
+ }
+ if (pid != pidv){
+ continue;
+ }
+
+ if ( buf[3+i] & 0x20) { // adaptation field?
+ off = buf[4+i] + 1;
+ }
+
+ if ( buf[1+i]&0x40) {
+ if (p->plength == MMAX_PLENGTH-6){
+ p->plength = p->found-6;
+ p->found = 0;
+ send_ipack(p);
+ reset_ipack(p);
+ }
+ }
+
+ instant_repack(buf+4+off+i, TS_SIZE-4-off, p);
+ }
+ i = 0;
+
+ }
+}
+
+void ts2es(int fdin, uint16_t pidv)
+{
+ ipack p;
+ int verb = 0;
+
+ init_ipack(&p, IPACKS,write_out_es, 0);
+ p.fd = STDOUT_FILENO;
+ p.data = (void *)&p;
+
+ if (fdin != STDIN_FILENO) verb = 1;
+
+ ts2es_opt(fdin, pidv, &p, verb);
+}
+
+
+void change_aspect(int fdin, int fdout, int aspect)
+{
+ ps_packet ps;
+ pes_packet pes;
+ int neof,i;
+
+ do {
+ init_ps(&ps);
+ neof = read_ps(fdin,&ps);
+ write_ps(fdout,&ps);
+ for (i = 0; i < ps.npes; i++){
+ uint8_t *buf;
+ int c = 0;
+ int l;
+
+ init_pes(&pes);
+ read_pes(fdin, &pes);
+
+ buf = pes.pes_pckt_data;
+
+ switch (pes.stream_id){
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ l=pes.length;
+ break;
+ default:
+ l = 0;
+ break;
+ }
+ while ( c < l - 6){
+ if (buf[c] == 0x00 &&
+ buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01 &&
+ buf[c+3] == 0xB3) {
+ c += 4;
+ buf[c+3] &= 0x0f;
+ buf[c+3] |= aspect;
+ }
+ else c++;
+ }
+ write_pes(fdout,&pes);
+ }
+ } while( neof > 0 );
+}
diff --git a/libdvbmpeg/transform.h b/libdvbmpeg/transform.h
new file mode 100644
index 0000000..ad32706
--- /dev/null
+++ b/libdvbmpeg/transform.h
@@ -0,0 +1,250 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de,
+
+ */
+
+#ifndef _TS_TRANSFORM_H_
+#define _TS_TRANSFORM_H_
+
+#include <stdint.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "remux.h"
+
+#define PROG_STREAM_MAP 0xBC
+#ifndef PRIVATE_STREAM1
+#define PRIVATE_STREAM1 0xBD
+#endif
+#define PADDING_STREAM 0xBE
+#ifndef PRIVATE_STREAM2
+#define PRIVATE_STREAM2 0xBF
+#endif
+#define AUDIO_STREAM_S 0xC0
+#define AUDIO_STREAM_E 0xDF
+#define VIDEO_STREAM_S 0xE0
+#define VIDEO_STREAM_E 0xEF
+#define ECM_STREAM 0xF0
+#define EMM_STREAM 0xF1
+#define DSM_CC_STREAM 0xF2
+#define ISO13522_STREAM 0xF3
+#define PROG_STREAM_DIR 0xFF
+
+#define BUFFYSIZE 10*MAX_PLENGTH
+#define MAX_PTS 8192
+#define MAX_FRAME 8192
+#define MAX_PACK_L 4096
+#define PS_HEADER_L1 14
+#define PS_HEADER_L2 (PS_HEADER_L1+18)
+#define MAX_H_SIZE (PES_H_MIN + PS_HEADER_L1 + 5)
+#define PES_MIN 7
+#define PES_H_MIN 9
+
+//flags2
+#define PTS_DTS_FLAGS 0xC0
+#define ESCR_FLAG 0x20
+#define ES_RATE_FLAG 0x10
+#define DSM_TRICK_FLAG 0x08
+#define ADD_CPY_FLAG 0x04
+#define PES_CRC_FLAG 0x02
+#define PES_EXT_FLAG 0x01
+
+//pts_dts flags
+#define PTS_ONLY 0x80
+#define PTS_DTS 0xC0
+
+#define TS_SIZE 188
+#define TRANS_ERROR 0x80
+#define PAY_START 0x40
+#define TRANS_PRIO 0x20
+#define PID_MASK_HI 0x1F
+//flags
+#define TRANS_SCRMBL1 0x80
+#define TRANS_SCRMBL2 0x40
+#define ADAPT_FIELD 0x20
+#define PAYLOAD 0x10
+#define COUNT_MASK 0x0F
+
+// adaptation flags
+#define DISCON_IND 0x80
+#define RAND_ACC_IND 0x40
+#define ES_PRI_IND 0x20
+#define PCR_FLAG 0x10
+#define OPCR_FLAG 0x08
+#define SPLICE_FLAG 0x04
+#define TRANS_PRIV 0x02
+#define ADAP_EXT_FLAG 0x01
+
+// adaptation extension flags
+#define LTW_FLAG 0x80
+#define PIECE_RATE 0x40
+#define SEAM_SPLICE 0x20
+
+
+#define MAX_PLENGTH 0xFFFF
+#define MMAX_PLENGTH (8*MAX_PLENGTH)
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define P2P_LENGTH 2048
+
+ enum{NOPES, AUDIO, VIDEO, AC3};
+
+ typedef struct p2pstruct {
+ int found;
+ uint8_t buf[MMAX_PLENGTH];
+ uint8_t cid;
+ uint8_t subid;
+ uint32_t plength;
+ uint8_t plen[2];
+ uint8_t flag1;
+ uint8_t flag2;
+ uint8_t hlength;
+ uint8_t pts[5];
+ int mpeg;
+ uint8_t check;
+ int fd1;
+ int fd2;
+ int es;
+ int filter;
+ int which;
+ int done;
+ int repack;
+ uint16_t bigend_repack;
+ void (*func)(uint8_t *buf, int count, void *p);
+ int startv;
+ int starta;
+ int64_t apts;
+ int64_t vpts;
+ uint16_t pid;
+ uint16_t pida;
+ uint16_t pidv;
+ uint8_t acounter;
+ uint8_t vcounter;
+ uint8_t count0;
+ uint8_t count1;
+ void *data;
+ } p2p;
+
+
+ uint64_t trans_pts_dts(uint8_t *pts);
+ int write_ts_header(uint16_t pid, uint8_t *counter, int pes_start,
+ uint8_t *buf, uint8_t length);
+ uint16_t get_pid(uint8_t *pid);
+ void init_p2p(p2p *p, void (*func)(uint8_t *buf, int count, void *p),
+ int repack);
+ void get_pes (uint8_t *buf, int count, p2p *p, void (*func)(p2p *p));
+ void get_pes (uint8_t *buf, int count, p2p *p, void (*func)(p2p *p));
+ void pes_repack(p2p *p);
+ void setup_pes2ts( p2p *p, uint32_t pida, uint32_t pidv,
+ void (*ts_write)(uint8_t *buf, int count, void *p));
+ void kpes_to_ts( p2p *p,uint8_t *buf ,int count );
+ void setup_ts2pes( p2p *pa, p2p *pv, uint32_t pida, uint32_t pidv,
+ void (*pes_write)(uint8_t *buf, int count, void *p));
+ void kts_to_pes( p2p *p, uint8_t *buf);
+ void pes_repack(p2p *p);
+ void extract_from_pes(int fdin, int fdout, uint8_t id, int es);
+ int64_t pes_dmx(int fdin, int fdouta, int fdoutv, int es);
+ void pes_to_ts2( int fdin, int fdout, uint16_t pida, uint16_t pidv);
+ void ts_to_pes( int fdin, uint16_t pida, uint16_t pidv, int pad);
+ int get_ainfo(uint8_t *mbuf, int count, AudioInfo *ai, int pr);
+ int get_vinfo(uint8_t *mbuf, int count, VideoInfo *vi, int pr);
+ int get_ac3info(uint8_t *mbuf, int count, AudioInfo *ai, int pr);
+ void filter_audio_from_pes(int fdin, int fdout, uint8_t id,
+ uint8_t subid);
+
+
+//instant repack
+
+ typedef struct ipack_s {
+ int size;
+ int size_orig;
+ int found;
+ int ps;
+ int has_ai;
+ int has_vi;
+ AudioInfo ai;
+ VideoInfo vi;
+ uint8_t *buf;
+ uint8_t cid;
+ uint32_t plength;
+ uint8_t plen[2];
+ uint8_t flag1;
+ uint8_t flag2;
+ uint8_t hlength;
+ uint8_t pts[5];
+ uint8_t last_pts[5];
+ int mpeg;
+ uint8_t check;
+ int which;
+ int done;
+ void *data;
+ void *data2;
+ void (*func)(uint8_t *buf, int size, void *priv);
+ int count;
+ int start;
+ int fd;
+ int fd1;
+ int fd2;
+ int ffd;
+ int playing;
+ } ipack;
+
+ void instant_repack (uint8_t *buf, int count, ipack *p);
+ void init_ipack(ipack *p, int size,
+ void (*func)(uint8_t *buf, int size, void *priv),
+ int pad);
+ void free_ipack(ipack * p);
+ void send_ipack(ipack *p);
+ void reset_ipack(ipack *p);
+ void ps_pes(ipack *p);
+ // use with ipack structure, repack size and callback func
+
+ int64_t ts_demux(int fd_in, int fdv_out,int fda_out,uint16_t pida,
+ uint16_t pidv, int es);
+
+ void ts2es(int fdin, uint16_t pidv);
+ void ts2es_opt(int fdin, uint16_t pidv, ipack *p, int verb);
+ void insert_pat_pmt( int fdin, int fdout);
+ void change_aspect(int fdin, int fdout, int aspect);
+
+// SV: all made non-static:
+ void pes_in_ts(p2p *p);
+
+// SV: moved from .c file:
+#define IPACKS 2048
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _TS_TRANSFORM_H_*/
+
+
+
diff --git a/patches/thread.c.diff b/patches/thread.c.diff
new file mode 100644
index 0000000..a03ffac
--- /dev/null
+++ b/patches/thread.c.diff
@@ -0,0 +1,29 @@
+--- vdr-vanilla/thread.c 2003-10-10 18:19:15.000000000 +0200
++++ vdr-vanilla-thread/thread.c 2003-10-10 18:43:36.000000000 +0200
+@@ -158,12 +158,21 @@
+
+ bool cThread::Active(void)
+ {
+- if (threadPid) {
+- if (kill(threadPid, SIGIO) < 0) { // couldn't find another way of checking whether the thread is still running - any ideas?
+- if (errno == ESRCH)
+- threadPid = 0;
+- else
++ if (thread) {
++ /*
++ * Single UNIX Spec v2 says:
++ *
++ * The pthread_kill() function is used to request
++ * that a signal be delivered to the specified thread.
++ *
++ * As in kill(), if sig is zero, error checking is
++ * performed but no signal is actually sent.
++ */
++ int err;
++ if ((err = pthread_kill(thread, 0)) != 0) {
++ if (err != ESRCH)
+ LOG_ERROR;
++ thread = 0;
+ }
+ else
+ return true;
diff --git a/patches/vdr-1.3.11-localchannelprovide.diff b/patches/vdr-1.3.11-localchannelprovide.diff
new file mode 100644
index 0000000..448d7fc
--- /dev/null
+++ b/patches/vdr-1.3.11-localchannelprovide.diff
@@ -0,0 +1,61 @@
+diff -u vdr-1.3.11/config.c vdr-1.3.11.LocalChannelProvide/config.c
+--- vdr-1.3.11/config.c 2004-05-16 14:43:55.000000000 +0200
++++ vdr-1.3.11.LocalChannelProvide/config.c 2004-08-29 17:55:59.000000000 +0200
+@@ -297,6 +297,7 @@
+ ResumeID = 0;
+ CurrentChannel = -1;
+ CurrentVolume = MAXVOLUME;
++ LocalChannelProvide = 1;
+ }
+
+ cSetup& cSetup::operator= (const cSetup &s)
+@@ -450,6 +451,7 @@
+ else if (!strcasecmp(Name, "ResumeID")) ResumeID = atoi(Value);
+ else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
+ else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value);
++ else if (!strcasecmp(Name, "LocalChannelProvide")) LocalChannelProvide = atoi(Value);
+ else
+ return false;
+ return true;
+@@ -510,6 +512,7 @@
+ Store("ResumeID", ResumeID);
+ Store("CurrentChannel", CurrentChannel);
+ Store("CurrentVolume", CurrentVolume);
++ Store("LocalChannelProvide",LocalChannelProvide);
+
+ Sort();
+
+diff -u vdr-1.3.11/config.h vdr-1.3.11.LocalChannelProvide/config.h
+--- vdr-1.3.11/config.h 2004-06-10 15:18:50.000000000 +0200
++++ vdr-1.3.11.LocalChannelProvide/config.h 2004-08-29 17:47:32.000000000 +0200
+@@ -251,6 +251,7 @@
+ int ResumeID;
+ int CurrentChannel;
+ int CurrentVolume;
++ int LocalChannelProvide;
+ int __EndData__;
+ cSetup(void);
+ cSetup& operator= (const cSetup &s);
+diff -u vdr-1.3.11/dvbdevice.c vdr-1.3.11.LocalChannelProvide/dvbdevice.c
+--- vdr-1.3.11/dvbdevice.c 2004-06-19 11:33:42.000000000 +0200
++++ vdr-1.3.11.LocalChannelProvide/dvbdevice.c 2004-08-29 18:00:37.000000000 +0200
+@@ -674,6 +674,8 @@
+
+ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
+ {
++ if (Setup.LocalChannelProvide != 1)
++ return false;
+ bool result = false;
+ bool hasPriority = Priority < 0 || Priority > this->Priority();
+ bool needsDetachReceivers = false;
+diff -u vdr-1.3.11/menu.c vdr-1.3.11.LocalChannelProvide/menu.c
+--- vdr-1.3.11/menu.c 2004-06-13 22:26:51.000000000 +0200
++++ vdr-1.3.11.LocalChannelProvide/menu.c 2004-08-29 17:52:31.000000000 +0200
+@@ -1878,6 +1878,7 @@
+ Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
+ Add(new cMenuEditBoolItem(tr("Setup.DVB$Video format"), &data.VideoFormat, "4:3", "16:9"));
+ Add(new cMenuEditStraItem(tr("Setup.DVB$Update channels"), &data.UpdateChannels, 5, updateChannelsTexts));
++ Add(new cMenuEditBoolItem(tr("Channels available locally"), &data.LocalChannelProvide));
+ }
+
+ eOSState cMenuSetupDVB::ProcessKey(eKeys Key)
diff --git a/patches/vdr-1.3.6-incompletesections.diff b/patches/vdr-1.3.6-incompletesections.diff
new file mode 100644
index 0000000..a58a121
--- /dev/null
+++ b/patches/vdr-1.3.6-incompletesections.diff
@@ -0,0 +1,22 @@
+--- vdr-1.3.6/sections.c 2004-02-07 17:51:57.000000000 +0200
++++ sections.c 2004-03-21 18:34:47.000000000 +0200
+@@ -185,11 +185,17 @@
+ if (fh) {
+ // Read section data:
+ unsigned char buf[4096]; // max. allowed size for any EIT section
+- int r = safe_read(fh->handle, buf, sizeof(buf));
++ struct stat statbuf;
++ int st = fstat(fh->handle, &statbuf);
++ int ispipe = (st == 0 && !S_ISCHR(statbuf.st_mode));
++ /*printf("ispipe %d\n", ispipe);*/
++ int r = safe_read(fh->handle, buf, ispipe ? 3 : sizeof(buf));
+ if (!DeviceHasLock)
+ continue; // we do the read anyway, to flush any data that might have come from a different transponder
+- if (r > 3) { // minimum number of bytes necessary to get section length
++ if (r >= 3) { // minimum number of bytes necessary to get section length
+ int len = (((buf[1] & 0x0F) << 8) | (buf[2] & 0xFF)) + 3;
++ if (ispipe)
++ r += safe_read(fh->handle, buf+3, len-3);
+ if (len == r) {
+ // Distribute data to all attached filters:
+ int pid = fh->filterData.pid;
diff --git a/patches/vdr-pluginactivity.diff b/patches/vdr-pluginactivity.diff
new file mode 100644
index 0000000..0b173ae
--- /dev/null
+++ b/patches/vdr-pluginactivity.diff
@@ -0,0 +1,113 @@
+diff -Nru -x PLUGINS vdr-1.3.12-orig/i18n.c vdr-1.3.12/i18n.c
+--- vdr-1.3.12-orig/i18n.c 2004-05-28 15:19:29.000000000 +0200
++++ vdr-1.3.12/i18n.c 2004-08-17 16:01:07.000000000 +0200
+@@ -1033,8 +1033,8 @@
+ "´ÕÙáâÒØâÕÛìÝÞ ßÕàÕ×ÐßãáâØâì?",
+ "Zaista ponovo pokrenuti?",
+ },
+- { "Recording - restart anyway?",
+- "Aufnahme läuft - trotzdem neu starten?",
++ { "Busy - restart anyway?",
++ "Beschäftigt - trotzdem neu starten?",
+ "Snemanje - zares ponoven zagon?",
+ "In registrazione - restart comunque?",
+ "Opname loopt - toch opnieuw starten?",
+@@ -1052,8 +1052,8 @@
+ "¸Ôñâ ×ÐßØáì - ÔÕÙáâÒØâÕÛìÝÞ ßÕàÕ×ÐßãáâØâì?",
+ "Snimanje traje - svejedno restart sistema?",
+ },
+- { "Recording - shut down anyway?",
+- "Aufnahme läuft - trotzdem ausschalten?",
++ { "Busy - shut down anyway?",
++ "Beschäftigt - trotzdem ausschalten?",
+ "Snemanje - zares izklopi?",
+ "In registrazione - spengo comunque?",
+ "Opname loopt - toch uitschakelen?",
+diff -Nru -x PLUGINS vdr-1.3.12-orig/menu.c vdr-1.3.12/menu.c
+--- vdr-1.3.12-orig/menu.c 2004-06-13 22:26:51.000000000 +0200
++++ vdr-1.3.12/menu.c 2004-08-17 16:00:07.000000000 +0200
+@@ -2201,7 +2201,7 @@
+
+ eOSState cMenuSetup::Restart(void)
+ {
+- if (Interface->Confirm(cRecordControls::Active() ? tr("Recording - restart anyway?") : tr("Really restart?"))) {
++ if (Interface->Confirm((cRecordControls::Active() || cPluginManager::Active()) ? tr("Busy - restart anyway?") : tr("Really restart?"))) {
+ cThread::EmergencyExit(true);
+ return osEnd;
+ }
+diff -Nru -x PLUGINS vdr-1.3.12-orig/plugin.c vdr-1.3.12/plugin.c
+--- vdr-1.3.12-orig/plugin.c 2004-05-22 13:25:22.000000000 +0200
++++ vdr-1.3.12/plugin.c 2004-08-17 15:57:52.000000000 +0200
+@@ -64,6 +64,11 @@
+ {
+ }
+
++bool cPlugin::Active(void)
++{
++ return false;
++}
++
+ const char *cPlugin::MainMenuEntry(void)
+ {
+ return NULL;
+@@ -369,6 +374,18 @@
+ return NULL;
+ }
+
++bool cPluginManager::Active(void)
++{
++ if (pluginManager) {
++ for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
++ cPlugin *p = dll->Plugin();
++ if (p && p->Active())
++ return true;
++ }
++ }
++ return false;
++}
++
+ void cPluginManager::Shutdown(bool Log)
+ {
+ cDll *dll;
+diff -Nru -x PLUGINS vdr-1.3.12-orig/plugin.h vdr-1.3.12/plugin.h
+--- vdr-1.3.12-orig/plugin.h 2004-04-30 15:46:21.000000000 +0200
++++ vdr-1.3.12/plugin.h 2004-08-17 15:56:51.000000000 +0200
+@@ -36,6 +36,7 @@
+ virtual bool Initialize(void);
+ virtual bool Start(void);
+ virtual void Housekeeping(void);
++ virtual bool Active(void);
+
+ virtual const char *MainMenuEntry(void);
+ virtual cOsdObject *MainMenuAction(void);
+@@ -85,6 +86,7 @@
+ static bool HasPlugins(void);
+ static cPlugin *GetPlugin(int Index);
+ static cPlugin *GetPlugin(const char *Name);
++ static bool Active(void);
+ void Shutdown(bool Log = false);
+ };
+
+diff -Nru -x PLUGINS vdr-1.3.12-orig/vdr.c vdr-1.3.12/vdr.c
+--- vdr-1.3.12-orig/vdr.c 2004-06-13 15:52:09.000000000 +0200
++++ vdr-1.3.12/vdr.c 2004-08-17 15:59:18.000000000 +0200
+@@ -707,8 +707,8 @@
+ Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!"));
+ break;
+ }
+- if (cRecordControls::Active()) {
+- if (Interface->Confirm(tr("Recording - shut down anyway?")))
++ if (cRecordControls::Active() || cPluginManager::Active()) {
++ if (Interface->Confirm(tr("Busy - shut down anyway?")))
+ ForceShutdown = true;
+ }
+ LastActivity = 1; // not 0, see below!
+@@ -821,7 +821,7 @@
+ Skins.Message(mtInfo, tr("Editing process finished"));
+ }
+ }
+- if (!Interact && ((!cRecordControls::Active() && !cCutter::Active() && (!Interface->HasSVDRPConnection() || UserShutdown)) || ForceShutdown)) {
++ if (!Interact && ((!cRecordControls::Active() && !cCutter::Active() && !cPluginManager::Active() && (!Interface->HasSVDRPConnection() || UserShutdown)) || ForceShutdown)) {
+ time_t Now = time(NULL);
+ if (Now - LastActivity > ACTIVITYTIMEOUT) {
+ // Shutdown:
diff --git a/remux/ts2es.c b/remux/ts2es.c
new file mode 100644
index 0000000..3c3138c
--- /dev/null
+++ b/remux/ts2es.c
@@ -0,0 +1,88 @@
+#include "remux/ts2es.h"
+#include "common.h"
+
+// from VDR's remux.c
+#define MAXNONUSEFULDATA (10*1024*1024)
+
+class cTS2ES: public ipack {
+ friend void PutES(uint8_t *Buffer, int Size, void *Data);
+
+private:
+ uint8_t *m_ResultBuffer;
+ int *m_ResultCount;
+
+public:
+ cTS2ES(uint8_t *ResultBuffer, int *ResultCount);
+ ~cTS2ES();
+
+ void PutTSPacket(const uint8_t *Buffer);
+};
+
+void PutES(uint8_t *Buffer, int Size, void *Data) {
+ cTS2ES *This = (cTS2ES*)Data;
+ uint8_t payl = Buffer[8] + 9 + This->start - 1;
+ int count = Size - payl;
+
+ if (*This->m_ResultCount + count > RESULTBUFFERSIZE) {
+ esyslog("ERROR: result buffer overflow (%d + %d > %d)",
+ *This->m_ResultCount, count, RESULTBUFFERSIZE);
+ count = RESULTBUFFERSIZE - *This->m_ResultCount;
+ }
+ memcpy(This->m_ResultBuffer + *This->m_ResultCount, Buffer + payl, count);
+ *This->m_ResultCount += count;
+ This->start = 1;
+}
+
+cTS2ES::cTS2ES(uint8_t *ResultBuffer, int *ResultCount) {
+ m_ResultBuffer = ResultBuffer;
+ m_ResultCount = ResultCount;
+
+ init_ipack(this, IPACKS, PutES, 0);
+ data = (void*)this;
+}
+
+cTS2ES::~cTS2ES() {
+}
+
+void cTS2ES::PutTSPacket(const uint8_t *Buffer) {
+ if (!Buffer)
+ return;
+
+ if (Buffer[1] & 0x80) { // ts error
+ // TODO
+ }
+
+ if (Buffer[1] & 0x40) { // payload start
+ if (plength == MMAX_PLENGTH - 6) {
+ plength = found - 6;
+ found = 0;
+ send_ipack(this);
+ reset_ipack(this);
+ }
+ }
+
+ uint8_t off = 0;
+
+ if (Buffer[3] & 0x20) { // adaptation field?
+ off = Buffer[4] + 1;
+ if (off + 4 > TS_SIZE - 1)
+ return;
+ }
+
+ instant_repack((uint8_t*)(Buffer + 4 + off), TS_SIZE - 4 - off, this);
+}
+
+cTS2ESRemux::cTS2ESRemux(int Pid):
+ cTSRemux(false) {
+ m_Pid = Pid;
+ m_Remux = new cTS2ES(m_ResultBuffer, &m_ResultCount);
+}
+
+cTS2ESRemux::~cTS2ESRemux() {
+ delete m_Remux;
+}
+
+void cTS2ESRemux::PutTSPacket(int Pid, const uint8_t *Data) {
+ if (Pid == m_Pid) m_Remux->PutTSPacket(Data);
+}
+
diff --git a/remux/ts2es.h b/remux/ts2es.h
new file mode 100644
index 0000000..8026a1b
--- /dev/null
+++ b/remux/ts2es.h
@@ -0,0 +1,21 @@
+#ifndef VDR_STREAMDEV_TS2ESREMUX_H
+#define VDR_STREAMDEV_TS2ESREMUX_H
+
+#include "remux/tsremux.h"
+
+class cTS2ES;
+
+class cTS2ESRemux: public cTSRemux {
+private:
+ int m_Pid;
+ cTS2ES *m_Remux;
+
+protected:
+ virtual void PutTSPacket(int Pid, const uint8_t *Data);
+
+public:
+ cTS2ESRemux(int Pid);
+ virtual ~cTS2ESRemux();
+};
+
+#endif // VDR_STREAMDEV_TS2ESREMUX_H
diff --git a/remux/ts2ps.c b/remux/ts2ps.c
new file mode 100644
index 0000000..222c39a
--- /dev/null
+++ b/remux/ts2ps.c
@@ -0,0 +1,104 @@
+#include "remux/ts2ps.h"
+
+class cTS2PS {
+ friend void PutPES(uint8_t *Buffer, int Size, void *Data);
+
+private:
+ ipack m_Ipack;
+ uint8_t *m_ResultBuffer;
+ int *m_ResultCount;
+
+public:
+ cTS2PS(uint8_t *ResultBuffer, int *ResultCount, uint8_t AudioCid = 0x00,
+ bool PS = false);
+ ~cTS2PS();
+
+ void PutTSPacket(const uint8_t *Buffer);
+};
+
+void PutPES(uint8_t *Buffer, int Size, void *Data) {
+ cTS2PS *This = (cTS2PS*)Data;
+ if (*This->m_ResultCount + Size > RESULTBUFFERSIZE) {
+ esyslog("ERROR: result buffer overflow (%d + %d > %d)",
+ *This->m_ResultCount, Size, RESULTBUFFERSIZE);
+ Size = RESULTBUFFERSIZE - *This->m_ResultCount;
+ }
+ memcpy(This->m_ResultBuffer + *This->m_ResultCount, Buffer, Size);
+ *This->m_ResultCount += Size;
+}
+
+cTS2PS::cTS2PS(uint8_t *ResultBuffer, int *ResultCount, uint8_t AudioCid,
+ bool PS) {
+ m_ResultBuffer = ResultBuffer;
+ m_ResultCount = ResultCount;
+
+ init_ipack(&m_Ipack, IPACKS, PutPES, PS);
+ m_Ipack.cid = AudioCid;
+ m_Ipack.data = (void*)this;
+}
+
+cTS2PS::~cTS2PS() {
+}
+
+void cTS2PS::PutTSPacket(const uint8_t *Buffer) {
+ if (!Buffer)
+ return;
+
+ if (Buffer[1] & 0x80) { // ts error
+ // TODO
+ }
+
+ if (Buffer[1] & 0x40) { // payload start
+ if (m_Ipack.plength == MMAX_PLENGTH - 6 && m_Ipack.found > 6) {
+ m_Ipack.plength = m_Ipack.found - 6;
+ m_Ipack.found = 0;
+ send_ipack(&m_Ipack);
+ reset_ipack(&m_Ipack);
+ }
+ }
+
+ uint8_t off = 0;
+
+ if (Buffer[3] & 0x20) { // adaptation field?
+ off = Buffer[4] + 1;
+ if (off + 4 > TS_SIZE - 1)
+ return;
+ }
+
+ instant_repack((uint8_t*)(Buffer + 4 + off), TS_SIZE - 4 - off, &m_Ipack);
+}
+
+cTS2PSRemux::cTS2PSRemux(int VPid, int APid1, int APid2, int DPid1,
+ int DPid2, bool PS) {
+ m_VPid = VPid;
+ m_APid1 = APid1;
+ m_APid2 = APid2;
+ m_DPid1 = DPid1;
+ m_DPid2 = DPid2;
+ m_VRemux = new cTS2PS(m_ResultBuffer, &m_ResultCount, 0x00, PS);
+ m_ARemux1 = new cTS2PS(m_ResultBuffer, &m_ResultCount, 0xC0, PS);
+ m_ARemux2 = APid2 ? new cTS2PS(m_ResultBuffer, &m_ResultCount, 0xC1, PS)
+ : NULL;
+ m_DRemux1 = DPid1 ? new cTS2PS(m_ResultBuffer, &m_ResultCount, 0x00, PS)
+ : NULL;
+ //XXX don't yet know how to tell apart primary and secondary DD data...
+ m_DRemux2 = /*XXX m_DPid2 ? new cTS2PS(m_ResultBuffer, &m_ResultCount,
+ 0x00, PS) : XXX*/ NULL;
+}
+
+cTS2PSRemux::~cTS2PSRemux() {
+ if (m_DRemux2) delete m_DRemux2;
+ if (m_DRemux1) delete m_DRemux1;
+ if (m_ARemux2) delete m_ARemux2;
+ delete m_ARemux1;
+ delete m_VRemux;
+}
+
+void cTS2PSRemux::PutTSPacket(int Pid, const uint8_t *Data) {
+ if (Pid == m_VPid) m_VRemux->PutTSPacket(Data);
+ else if (Pid == m_APid1) m_ARemux1->PutTSPacket(Data);
+ else if (Pid == m_APid2 && m_ARemux2) m_ARemux2->PutTSPacket(Data);
+ else if (Pid == m_DPid1 && m_DRemux1) m_DRemux1->PutTSPacket(Data);
+ else if (Pid == m_DPid2 && m_DRemux2) m_DRemux2->PutTSPacket(Data);
+}
+
diff --git a/remux/ts2ps.h b/remux/ts2ps.h
new file mode 100644
index 0000000..4e43ed2
--- /dev/null
+++ b/remux/ts2ps.h
@@ -0,0 +1,22 @@
+#ifndef VDR_STREAMDEV_TS2PESREMUX_H
+#define VDR_STREAMDEV_TS2PESREMUX_H
+
+#include "remux/tsremux.h"
+
+class cTS2PS;
+
+class cTS2PSRemux: public cTSRemux {
+private:
+ int m_VPid, m_APid1, m_APid2, m_DPid1, m_DPid2;
+ cTS2PS *m_VRemux, *m_ARemux1, *m_ARemux2, *m_DRemux1, *m_DRemux2;
+
+protected:
+ virtual void PutTSPacket(int Pid, const uint8_t *Data);
+
+public:
+ cTS2PSRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2,
+ bool PS = false);
+ virtual ~cTS2PSRemux();
+};
+
+#endif // VDR_STREAMDEV_TS2PESREMUX_H
diff --git a/remux/tsremux.c b/remux/tsremux.c
new file mode 100644
index 0000000..93f513b
--- /dev/null
+++ b/remux/tsremux.c
@@ -0,0 +1,185 @@
+#include "remux/tsremux.h"
+
+// from VDR's remux.c
+#define MAXNONUSEFULDATA (10*1024*1024)
+#define SC_PICTURE 0x00 // "picture header"
+#define VIDEO_STREAM_S 0xE0
+
+cTSRemux::cTSRemux(bool Sync) {
+ m_ResultCount = 0;
+ m_ResultDelivered = 0;
+ m_Synced = false;
+ m_Skipped = 0;
+ m_Sync = Sync;
+}
+
+cTSRemux::~cTSRemux(void) {
+}
+
+uchar *cTSRemux::Process(const uchar *Data, int &Count, int &Result) {
+ // Remove any previously delivered data from the result buffer:
+ if (m_ResultDelivered) {
+ if (m_ResultDelivered < m_ResultCount)
+ memmove(m_ResultBuffer, m_ResultBuffer + m_ResultDelivered, m_ResultCount
+ - m_ResultDelivered);
+ m_ResultCount -= m_ResultDelivered;
+ m_ResultDelivered = 0;
+ }
+
+ int used = 0;
+
+ // Make sure we are looking at a TS packet:
+ while (Count > TS_SIZE) {
+ if (Data[0] == 0x47 && Data[TS_SIZE] == 0x47)
+ break;
+ Data++;
+ Count--;
+ used++;
+ }
+ if (used)
+ esyslog("ERROR: skipped %d byte to sync on TS packet", used);
+
+ // Convert incoming TS data
+ for (int i = 0; i < Count; i += TS_SIZE) {
+ if (Count - i < TS_SIZE)
+ break;
+ if (Data[i] != 0x47)
+ break;
+ int pid = get_pid((uint8_t*)(Data + i + 1));
+ if (Data[i + 3] & 0x10) // got payload
+ PutTSPacket(pid, Data + i);
+ /*if (pid == m_VPid) m_VRemux->ConvertTSPacket(Data + i);
+ else if (pid == m_APid1) m_ARemux1->ConvertTSPacket(Data + i);
+ else if (pid == m_APid2 && m_ARemux2) m_ARemux2->ConvertTSPacket(Data + i);
+ else if (pid == m_DPid1 && m_DRemux1) m_DRemux1->ConvertTSPacket(Data + i);
+ else if (pid == m_DPid2 && m_DRemux2) m_DRemux2->ConvertTSPacket(Data + i);*/
+ used += TS_SIZE;
+ if (m_ResultCount > (int)sizeof(m_ResultBuffer) / 2)
+ break;
+ }
+ Count = used;
+
+ // When we don't need to sync, we don't need to sync :-)
+ if (!m_Sync) {
+ Result = m_ResultDelivered = m_ResultCount;
+ return Result ? m_ResultBuffer : NULL;
+ }
+
+ // Check if we're getting anywhere here:
+
+ if (!m_Synced && m_Skipped >= 0) {
+ if (m_Skipped > MAXNONUSEFULDATA) {
+ esyslog("ERROR: no useful data seen within %d byte of video stream", m_Skipped);
+ m_Skipped = -1;
+ //if (exitOnFailure)
+ //cThread::EmergencyExit(true);
+ }
+ else
+ m_Skipped += Count;
+ }
+
+ // Check for frame borders:
+
+ if (m_ResultCount >= MINVIDEODATA) {
+ for (int i = 0; i < m_ResultCount; i++) {
+ if (m_ResultBuffer[i] == 0 && m_ResultBuffer[i + 1] == 0 && m_ResultBuffer[i + 2] == 1) {
+ switch (m_ResultBuffer[i + 3]) {
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ {
+ uchar pt = NO_PICTURE;
+ int l = ScanVideoPacket(m_ResultBuffer, m_ResultCount, i, pt);
+ if (l < 0)
+ return NULL; // no useful data found, wait for more
+ if (pt != NO_PICTURE) {
+ if (pt < I_FRAME || B_FRAME < pt)
+ esyslog("ERROR: unknown picture type '%d'", pt);
+ else if (!m_Synced) {
+ if (pt == I_FRAME) {
+ m_ResultDelivered = i; // will drop everything before this position
+ SetBrokenLink(m_ResultBuffer + i, l);
+ m_Synced = true;
+ }
+ else {
+ m_ResultDelivered = i + l; // will drop everything before and including this packet
+ return NULL;
+ }
+ }
+ }
+ if (m_Synced) {
+ Result = l;
+ uchar *p = m_ResultBuffer + m_ResultDelivered;
+ m_ResultDelivered += l;
+ return p;
+ }
+ else {
+ m_ResultDelivered = i + l; // will drop everything before and including this packet
+ return NULL;
+ }
+ }
+ break;
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ {
+ int l = GetPacketLength(m_ResultBuffer, m_ResultCount, i);
+ if (l < 0)
+ return NULL; // no useful data found, wait for more
+ if (m_Synced) {
+ Result = l;
+ uchar *p = m_ResultBuffer + m_ResultDelivered;
+ m_ResultDelivered += l;
+ return p;
+ }
+ else {
+ m_ResultDelivered = i + l; // will drop everything before and including this packet
+ return NULL;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ return NULL; // no useful data found, wait for more
+}
+
+int cTSRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType) {
+ // Scans the video packet starting at Offset and returns its length.
+ // If the return value is -1 the packet was not completely in the buffer.
+
+ int Length = GetPacketLength(Data, Count, Offset);
+ if (Length > 0 && Offset + Length <= Count) {
+ int i = Offset + 8; // the minimum length of the video packet header
+ i += Data[i] + 1; // possible additional header bytes
+ for (; i < Offset + Length; i++) {
+ if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
+ switch (Data[i + 3]) {
+ case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
+ return Length;
+ }
+ }
+ }
+ PictureType = NO_PICTURE;
+ return Length;
+ }
+ return -1;
+}
+
+int cTSRemux::GetPacketLength(const uchar *Data, int Count, int Offset) {
+ // Returns the entire length of the packet starting at offset, or -1 in case of error.
+ return (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
+}
+
+void cTSRemux::SetBrokenLink(uchar *Data, int Length) {
+ if (Length > 9 && Data[0] == 0 && Data[1] == 0 && Data[2] == 1 && (Data[3] & VIDEO_STREAM_S) == VIDEO_STREAM_S) {
+ for (int i = Data[8] + 9; i < Length - 7; i++) { // +9 to skip video packet header
+ if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) {
+ if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed
+ Data[i + 7] |= 0x20;
+ return;
+ }
+ }
+ dsyslog("SetBrokenLink: no GOP header found in video packet");
+ }
+ else
+ dsyslog("SetBrokenLink: no video packet in frame");
+}
diff --git a/remux/tsremux.h b/remux/tsremux.h
new file mode 100644
index 0000000..3e83c73
--- /dev/null
+++ b/remux/tsremux.h
@@ -0,0 +1,30 @@
+#ifndef VDR_STREAMDEV_TSREMUX_H
+#define VDR_STREAMDEV_TSREMUX_H
+
+#include "libdvbmpeg/transform.h"
+#include <vdr/remux.h>
+
+class cTSRemux {
+protected:
+ uchar m_ResultBuffer[RESULTBUFFERSIZE];
+ int m_ResultCount;
+ int m_ResultDelivered;
+ int m_Synced;
+ int m_Skipped;
+ int m_Sync;
+
+ int GetPacketLength(const uchar *Data, int Count, int Offset);
+ int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
+
+ virtual void PutTSPacket(int Pid, const uint8_t *Data) = 0;
+
+public:
+ cTSRemux(bool Sync = true);
+ virtual ~cTSRemux();
+
+ virtual uchar *Process(const uchar *Data, int &Count, int &Result);
+
+ static void SetBrokenLink(uchar *Data, int Length);
+};
+
+#endif // VDR_STREAMDEV_TSREMUX_H
diff --git a/server/component.c b/server/component.c
new file mode 100644
index 0000000..6410de3
--- /dev/null
+++ b/server/component.c
@@ -0,0 +1,50 @@
+/*
+ * $Id: component.c,v 1.1 2004/12/30 22:44:18 lordjaxom Exp $
+ */
+
+#include "server/component.h"
+#include "server/connection.h"
+
+#include <vdr/tools.h>
+#include <string.h>
+#include <errno.h>
+
+cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp,
+ uint ListenPort) {
+ m_Protocol = Protocol;
+ m_ListenIp = ListenIp;
+ m_ListenPort = ListenPort;
+}
+
+cServerComponent::~cServerComponent() {
+}
+
+bool cServerComponent::Init(void) {
+ if (!m_Listen.Listen(m_ListenIp, m_ListenPort, 5)) {
+ esyslog("Streamdev: Couldn't listen (%s) %s:%d: %s", m_Protocol, m_ListenIp,
+ m_ListenPort, strerror(errno));
+ return false;
+ }
+ isyslog("Streamdev: Listening (%s) on port %d", m_Protocol, m_ListenPort);
+ return true;
+}
+
+void cServerComponent::Exit(void) {
+ m_Listen.Close();
+}
+
+cServerConnection *cServerComponent::CanAct(const cTBSelect &Select) {
+ if (Select.CanRead(m_Listen)) {
+ cServerConnection *client = NewConnection();
+ if (client->Accept(m_Listen)) {
+ isyslog("Streamdev: Accepted new client (%s) %s:%d", m_Protocol,
+ (const char*)client->RemoteIp(), client->RemotePort());
+ return client;
+ } else {
+ esyslog("Streamdev: Couldn't accept (%s): %s", m_Protocol,
+ strerror(errno));
+ delete client;
+ }
+ }
+ return NULL;
+}
diff --git a/server/component.h b/server/component.h
new file mode 100644
index 0000000..2f8e605
--- /dev/null
+++ b/server/component.h
@@ -0,0 +1,50 @@
+/*
+ * $Id: component.h,v 1.1 2004/12/30 22:44:18 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SERVERS_COMPONENT_H
+#define VDR_STREAMDEV_SERVERS_COMPONENT_H
+
+#include "tools/socket.h"
+#include "tools/select.h"
+
+#include <vdr/tools.h>
+
+class cServerConnection;
+
+/* Basic TCP listen server, all functions virtual if a derivation wants to do
+ things different */
+
+class cServerComponent: public cListObject {
+private:
+ cTBSocket m_Listen;
+ const char *m_Protocol;
+ const char *m_ListenIp;
+ uint m_ListenPort;
+
+public:
+ cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort);
+ virtual ~cServerComponent();
+
+ /* Starts listening on the specified Port, override if you want to do things
+ different */
+ virtual bool Init(void);
+
+ /* Stops listening, override if you want to do things different */
+ virtual void Exit(void);
+
+ /* Adds the listening socket to the Select object */
+ virtual void AddSelect(cTBSelect &Select) const { Select.Add(m_Listen); }
+
+ /* Accepts the connection on a NewConnection() object and calls the
+ Welcome() on it, override if you want to do things different */
+ virtual cServerConnection *CanAct(const cTBSelect &Select);
+
+ /* Returns a new connection object for CanAct */
+ virtual cServerConnection *NewConnection(void) const = 0;
+};
+
+class cServerComponents: public cList<cServerComponent> {
+};
+
+#endif // VDR_STREAMDEV_SERVERS_COMPONENT_H
diff --git a/server/componentHTTP.c b/server/componentHTTP.c
new file mode 100644
index 0000000..035cb01
--- /dev/null
+++ b/server/componentHTTP.c
@@ -0,0 +1,15 @@
+/*
+ * $Id: componentHTTP.c,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
+ */
+
+#include "server/componentHTTP.h"
+#include "server/connectionHTTP.h"
+#include "server/setup.h"
+
+cComponentHTTP::cComponentHTTP(void):
+ cServerComponent("HTTP", StreamdevServerSetup.HTTPBindIP,
+ StreamdevServerSetup.HTTPServerPort) {
+}
+
+cComponentHTTP::~cComponentHTTP() {
+}
diff --git a/server/componentHTTP.h b/server/componentHTTP.h
new file mode 100644
index 0000000..46174d9
--- /dev/null
+++ b/server/componentHTTP.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: componentHTTP.h,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_HTTPSERVER_H
+#define VDR_STREAMDEV_HTTPSERVER_H
+
+#include "server/component.h"
+#include "server/connectionHTTP.h"
+
+#include <tools/socket.h>
+#include <tools/select.h>
+
+class cComponentHTTP: public cServerComponent {
+public:
+ cComponentHTTP(void);
+ ~cComponentHTTP(void);
+
+ virtual cServerConnection *NewConnection(void) const;
+};
+
+inline cServerConnection *cComponentHTTP::NewConnection(void) const {
+ return new cConnectionHTTP;
+}
+
+#endif // VDR_STREAMDEV_HTTPSERVER_H
diff --git a/server/componentVTP.c b/server/componentVTP.c
new file mode 100644
index 0000000..5fb9dcd
--- /dev/null
+++ b/server/componentVTP.c
@@ -0,0 +1,15 @@
+/*
+ * $Id: componentVTP.c,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
+ */
+
+#include "server/componentVTP.h"
+#include "server/connectionVTP.h"
+#include "server/setup.h"
+
+cComponentVTP::cComponentVTP(void):
+ cServerComponent("VTP", StreamdevServerSetup.VTPBindIP,
+ StreamdevServerSetup.VTPServerPort) {
+}
+
+cComponentVTP::~cComponentVTP() {
+}
diff --git a/server/componentVTP.h b/server/componentVTP.h
new file mode 100644
index 0000000..fe411ff
--- /dev/null
+++ b/server/componentVTP.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: componentVTP.h,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SERVERS_SERVERVTP_H
+#define VDR_STREAMDEV_SERVERS_SERVERVTP_H
+
+#include "server/component.h"
+#include "server/connectionVTP.h"
+
+#include <tools/socket.h>
+#include <tools/select.h>
+
+class cComponentVTP: public cServerComponent {
+private:
+ cTBSocket m_Listen;
+
+public:
+ cComponentVTP(void);
+ virtual ~cComponentVTP();
+
+ virtual cServerConnection *NewConnection(void) const;
+};
+
+inline cServerConnection *cComponentVTP::NewConnection(void) const {
+ return new cConnectionVTP;
+}
+
+#endif // VDR_STREAMDEV_SERVERS_SERVERVTP_H
diff --git a/server/connection.c b/server/connection.c
new file mode 100644
index 0000000..b4dd091
--- /dev/null
+++ b/server/connection.c
@@ -0,0 +1,172 @@
+/*
+ * $Id: connection.c,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
+ */
+
+#include "server/connection.h"
+#include "server/setup.h"
+#include "server/suspend.h"
+#include "common.h"
+
+#include <vdr/tools.h>
+#include <string.h>
+#include <errno.h>
+
+cServerConnection::cServerConnection(const char *Protocol) {
+ m_RdBytes = 0;
+ m_WrBytes = 0;
+ m_WrOffs = 0;
+ m_DeferClose = false;
+ m_Protocol = Protocol;
+}
+
+cServerConnection::~cServerConnection() {
+}
+
+bool cServerConnection::CanAct(const cTBSelect &Select) {
+ if (Select.CanRead(*this)) {
+ int b;
+ if ((b = Read(m_RdBuf + m_RdBytes, sizeof(m_RdBuf) - m_RdBytes - 1)) < 0) {
+ esyslog("Streamdev: Read from client (%s) %s:%d failed: %s", m_Protocol,
+ (const char*)RemoteIp(), RemotePort(), strerror(errno));
+ return false;
+ }
+
+ if (b == 0) {
+ isyslog("Streamdev: Client (%s) %s:%d closed connection", m_Protocol,
+ (const char*)RemoteIp(), RemotePort());
+ return false;
+ }
+
+ m_RdBytes += b;
+ m_RdBuf[m_RdBytes] = '\0';
+ return ParseBuffer();
+ }
+
+ if (Select.CanWrite(*this)) {
+ int b;
+ if ((b = Write(m_WrBuf + m_WrOffs, m_WrBytes - m_WrOffs)) < 0) {
+ esyslog("Streamdev: Write to client (%s) %s:%d failed: %s", m_Protocol,
+ (const char*)RemoteIp(), RemotePort(), strerror(errno));
+ return false;
+ }
+
+ m_WrOffs += b;
+ if (m_WrOffs == m_WrBytes) {
+ m_WrBytes = 0;
+ m_WrOffs = 0;
+ }
+ }
+
+ if (m_WrBytes == 0) {
+ if (m_DeferClose)
+ return false;
+ Flushed();
+ }
+ return true;
+}
+
+bool cServerConnection::ParseBuffer(void) {
+ char *ep;
+ bool res;
+
+ while ((ep = strchr(m_RdBuf, '\012')) != NULL) {
+ *ep = '\0';
+ if (ep > m_RdBuf && *(ep-1) == '\015')
+ *(ep-1) = '\0';
+
+ Dprintf("IN: |%s|\n", m_RdBuf);
+ res = Command(m_RdBuf);
+ m_RdBytes -= ++ep - m_RdBuf;
+ if (m_RdBytes > 0)
+ memmove(m_RdBuf, ep, m_RdBytes);
+ if (res == false)
+ return false;
+ }
+ return true;
+}
+
+bool cServerConnection::Respond(const char *Message) {
+ uint len = strlen(Message);
+ if (m_WrBytes + len + 2 > sizeof(m_WrBuf)) {
+ esyslog("Streamdev: Output buffer overflow (%s) for %s:%d", m_Protocol,
+ (const char*)RemoteIp(), RemotePort());
+ return false;
+ }
+ Dprintf("OUT: |%s|\n", Message);
+ memcpy(m_WrBuf + m_WrBytes, Message, len);
+ m_WrBuf[m_WrBytes + len] = '\015';
+ m_WrBuf[m_WrBytes + len + 1] = '\012';
+ m_WrBytes += len + 2;
+ return true;
+}
+
+cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority) {
+ cDevice *device = NULL;
+
+ /*Dprintf("+ Statistics:\n");
+ Dprintf("+ Current Channel: %d\n", cDevice::CurrentChannel());
+ Dprintf("+ Current Device: %d\n", cDevice::ActualDevice()->CardIndex());
+ Dprintf("+ Transfer Mode: %s\n", cDevice::ActualDevice()
+ == cDevice::PrimaryDevice() ? "false" : "true");
+ Dprintf("+ Replaying: %s\n", cDevice::PrimaryDevice()->Replaying() ? "true"
+ : "false");*/
+
+ Dprintf(" * GetDevice(const cChannel*, int)\n");
+ Dprintf(" * -------------------------------\n");
+
+ device = cDevice::GetDevice(Channel, Priority);
+
+ Dprintf(" * Found following device: %p (%d)\n", device,
+ device ? device->CardIndex() + 1 : 0);
+ if (device == cDevice::ActualDevice())
+ Dprintf(" * is actual device\n");
+ if (!cSuspendCtl::IsActive() && StreamdevServerSetup.SuspendMode != smAlways)
+ Dprintf(" * NOT suspended\n");
+
+ if (!device || (device == cDevice::ActualDevice()
+ && !cSuspendCtl::IsActive()
+ && StreamdevServerSetup.SuspendMode != smAlways)) {
+ // mustn't switch actual device
+ // maybe a device would be free if THIS connection did turn off its streams?
+ Dprintf(" * trying again...\n");
+ const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
+ isyslog("streamdev-server: Detaching current receiver");
+ Detach();
+ device = cDevice::GetDevice(Channel, Priority);
+ Attach();
+ Dprintf(" * Found following device: %p (%d)\n", device,
+ device ? device->CardIndex() + 1 : 0);
+ if (device == cDevice::ActualDevice())
+ Dprintf(" * is actual device\n");
+ if (!cSuspendCtl::IsActive()
+ && StreamdevServerSetup.SuspendMode != smAlways)
+ Dprintf(" * NOT suspended\n");
+ if (current && !TRANSPONDER(Channel, current))
+ Dprintf(" * NOT same transponder\n");
+ if (device && (device == cDevice::ActualDevice()
+ && !cSuspendCtl::IsActive()
+ && StreamdevServerSetup.SuspendMode != smAlways
+ && current != NULL
+ && !TRANSPONDER(Channel, current))) {
+ // now we would have to switch away live tv...let's see if live tv
+ // can be handled by another device
+ cDevice *newdev = NULL;
+ for (int i = 0; i < cDevice::NumDevices(); ++i) {
+ cDevice *dev = cDevice::GetDevice(i);
+ if (dev->ProvidesChannel(current, 0) && dev != device) {
+ newdev = dev;
+ break;
+ }
+ }
+ Dprintf(" * Found device for live tv: %p (%d)\n", newdev,
+ newdev ? newdev->CardIndex() + 1 : 0);
+ if (newdev == NULL || newdev == device)
+ // no suitable device to continue live TV, giving up...
+ device = NULL;
+ else
+ newdev->SwitchChannel(current, true);
+ }
+ }
+
+ return device;
+}
diff --git a/server/connection.h b/server/connection.h
new file mode 100644
index 0000000..7fc83a7
--- /dev/null
+++ b/server/connection.h
@@ -0,0 +1,87 @@
+/*
+ * $Id: connection.h,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SERVER_CONNECTION_H
+#define VDR_STREAMDEV_SERVER_CONNECTION_H
+
+#include "tools/socket.h"
+#include "tools/select.h"
+
+#include "common.h"
+
+class cChannel;
+class cDevice;
+
+/* Basic capabilities of a straight text-based protocol, most functions
+ virtual to support more complicated protocols */
+
+class cServerConnection: public cListObject, public cTBSocket {
+private:
+ char m_RdBuf[8192];
+ uint m_RdBytes;
+
+ char m_WrBuf[8192];
+ uint m_WrBytes;
+ uint m_WrOffs;
+
+ const char *m_Protocol;
+
+ bool m_DeferClose;
+
+public:
+ /* If you derive, specify a short string such as HTTP for Protocol, which
+ will be displayed in error messages */
+ cServerConnection(const char *Protocol);
+ virtual ~cServerConnection();
+
+ /* Gets called if the client has been accepted by the core */
+ virtual void Welcome(void) { }
+
+ /* Gets called if the client has been rejected by the core */
+ virtual void Reject(void) { DeferClose(); }
+
+ /* Adds itself to the Select object, if data can be received or if data is
+ to be sent. Override if necessary */
+ virtual void AddSelect(cTBSelect &Select) const;
+
+ /* Receives incoming data and calls ParseBuffer on it. Also writes queued
+ output data if possible. Override if necessary */
+ virtual bool CanAct(const cTBSelect &Select);
+
+ /* Called by CanAct(), parses the input buffer for full lines (terminated
+ either by '\012' or '\015\012') and calls Command on them, if any */
+ virtual bool ParseBuffer(void);
+
+ /* Will be called when a command terminated by a newline has been received */
+ virtual bool Command(char *Cmd) = 0;
+
+ /* Will put Message into the response queue, which will be sent in the next
+ server cycle. Note that Message will be line-terminated by Respond */
+ bool Respond(const char *Message);
+
+ /* Will make the socket close after sending all queued output data */
+ void DeferClose(void) { m_DeferClose = true; }
+
+ /* Will retrieve an unused device for transmitting data. Use the returned
+ cDevice in a following call to StartTransfer */
+ cDevice *GetDevice(const cChannel *Channel, int Priority);
+
+ virtual void Flushed(void) {}
+
+ virtual void Detach(void) = 0;
+ virtual void Attach(void) = 0;
+};
+
+class cServerConnections: public cList<cServerConnection> {
+};
+
+inline void cServerConnection::AddSelect(cTBSelect &Select) const {
+ if (m_WrBytes > 0)
+ Select.Add(*this, true);
+
+ if (m_WrBytes == 0 && m_RdBytes < sizeof(m_RdBuf) - 1)
+ Select.Add(*this, false);
+}
+
+#endif // VDR_STREAMDEV_SERVER_CONNECTION_H
diff --git a/server/connectionHTTP.c b/server/connectionHTTP.c
new file mode 100644
index 0000000..7d20f80
--- /dev/null
+++ b/server/connectionHTTP.c
@@ -0,0 +1,180 @@
+/*
+ * $Id: connectionHTTP.c,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
+ */
+
+#include "server/connectionHTTP.h"
+#include "server/livestreamer.h"
+#include "server/setup.h"
+
+cConnectionHTTP::cConnectionHTTP(void): cServerConnection("HTTP") {
+ m_Channel = NULL;
+ m_ListChannel = NULL;
+ m_LiveStreamer = NULL;
+ m_Status = hsRequest;
+ m_StreamType = (eStreamType)StreamdevServerSetup.HTTPStreamType;
+ m_Startup = false;
+}
+
+cConnectionHTTP::~cConnectionHTTP() {
+ if (m_LiveStreamer != NULL) delete m_LiveStreamer;
+}
+
+void cConnectionHTTP::Detach(void) {
+ if (m_LiveStreamer != NULL) m_LiveStreamer->Detach();
+}
+
+void cConnectionHTTP::Attach(void) {
+ if (m_LiveStreamer != NULL) m_LiveStreamer->Attach();
+}
+
+bool cConnectionHTTP::Command(char *Cmd) {
+ switch (m_Status) {
+ case hsRequest:
+ if (strncmp(Cmd, "GET ", 4) == 0) return CmdGET(Cmd + 4);
+ else {
+ DeferClose();
+ m_Status = hsTransfer; // Ignore following lines
+ return Respond("HTTP/1.0 400 Bad Request");
+ }
+ break;
+
+ case hsHeaders:
+ if (*Cmd == '\0') {
+ if (m_ListChannel != NULL) {
+ m_Status = hsListing;
+ return Respond("HTTP/1.0 200 OK")
+ && Respond("Content-Type: text/html")
+ && Respond("")
+ && Respond("<html><head><title>VDR Channel Listing</title></head>")
+ && Respond("<body><ul>");
+ } else if (m_Channel == NULL) {
+ DeferClose();
+ return Respond("HTTP/1.0 404 not found");
+ }
+ m_Status = hsTransfer;
+ m_LiveStreamer = new cStreamdevLiveStreamer(0);
+ cDevice *device = GetDevice(m_Channel, 0);
+ if (device != NULL) {
+ device->SwitchChannel(m_Channel, false);
+ m_LiveStreamer->SetDevice(device);
+ if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, false)) {
+ m_Startup = true;
+ if (m_StreamType == stES && (m_Channel->Vpid() == 0
+ || m_Channel->Vpid() == 1 || m_Channel->Vpid() == 0x1FFF)) {
+ return Respond("HTTP/1.0 200 OK")
+ && Respond("Content-Type: audio/mpeg")
+ && Respond((cTBString)"icy-name: " + m_Channel->Name())
+ && Respond("");
+ } else {
+ return Respond("HTTP/1.0 200 OK")
+ && Respond("Content-Type: video/mpeg")
+ && Respond("");
+ }
+ }
+ }
+ DELETENULL(m_LiveStreamer);
+ DeferClose();
+ return Respond("HTTP/1.0 409 Channel not available");
+ }
+ break;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+void cConnectionHTTP::Flushed(void) {
+ if (m_Status == hsListing) {
+ cTBString line;
+
+ if (m_ListChannel == NULL) {
+ Respond("</ul></body></html>");
+ DeferClose();
+ return;
+ }
+
+ if (m_ListChannel->GroupSep())
+ line.Format("<li>--- %s ---</li>", m_ListChannel->Name());
+ else
+ line.Format("<li><a href=\"http://%s:%d/%s\">%s</a></li>",
+ (const char*)LocalIp(), StreamdevServerSetup.HTTPServerPort,
+ m_ListChannel->GetChannelID().ToString(), m_ListChannel->Name());
+ if (!Respond(line))
+ DeferClose();
+ m_ListChannel = Channels.Next(m_ListChannel);
+ } else if (m_Startup) {
+ Dprintf("streamer start\n");
+ m_LiveStreamer->Start(this);
+ m_Startup = false;
+ }
+}
+
+bool cConnectionHTTP::CmdGET(char *Opts) {
+ cChannel *chan;
+ char *ep;
+
+ Opts = skipspace(Opts);
+ while (*Opts == '/')
+ ++Opts;
+
+ if (strncasecmp(Opts, "PS/", 3) == 0) {
+ m_StreamType = stPS;
+ Opts+=3;
+ } else if (strncasecmp(Opts, "PES/", 4) == 0) {
+ m_StreamType = stPES;
+ Opts+=4;
+ } else if (strncasecmp(Opts, "TS/", 3) == 0) {
+ m_StreamType = stTS;
+ Opts+=3;
+ } else if (strncasecmp(Opts, "ES/", 3) == 0) {
+ m_StreamType = stES;
+ Opts+=3;
+ }
+
+ while (*Opts == '/')
+ ++Opts;
+ for (ep = Opts + strlen(Opts); ep >= Opts && !isspace(*ep); --ep)
+ ;
+ *ep = '\0';
+
+ if (strncmp(Opts, "channels.htm", 12) == 0) {
+ m_ListChannel = Channels.First();
+ m_Status = hsHeaders;
+ } else if ((chan = ChannelFromString(Opts)) != NULL) {
+ m_Channel = chan;
+ m_Status = hsHeaders;
+ }
+ return true;
+}
+
+#if 0
+bool cHTTPConnection::Listing(void) {
+ cChannel *chan;
+ cTBString line;
+
+ Respond(200, "OK");
+ Respond("Content-Type: text/html");
+ Respond("");
+ Respond("<html><head><title>VDR Channel Listing</title></head>");
+ Respond("<body><ul>");
+
+ for (chan = Channels.First(); chan != NULL; chan = Channels.Next(chan)) {
+ if (chan->GroupSep() && !*chan->Name())
+ continue;
+
+ if (chan->GroupSep())
+ line.Format("<li>--- %s ---</li>", chan->Name());
+ else
+ line.Format("<li><a href=\"http://%s:%d/%s\">%s</a></li>",
+ (const char*)m_Socket.LocalIp(), StreamdevServerSetup.HTTPServerPort,
+ chan->GetChannelID().ToString(), chan->Name());
+ Respond(line);
+ }
+
+ Respond("</ul></body></html>");
+
+ m_DeferClose = true;
+ return true;
+}
+#endif
diff --git a/server/connectionHTTP.h b/server/connectionHTTP.h
new file mode 100644
index 0000000..5b8abbe
--- /dev/null
+++ b/server/connectionHTTP.h
@@ -0,0 +1,44 @@
+/*
+ * $Id: connectionHTTP.h,v 1.1 2004/12/30 22:44:18 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
+#define VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
+
+#include "connection.h"
+
+#include <tools/select.h>
+
+class cChannel;
+class cStreamdevLiveStreamer;
+
+class cConnectionHTTP: public cServerConnection {
+private:
+ enum eHTTPStatus {
+ hsRequest,
+ hsHeaders,
+ hsTransfer,
+ hsListing,
+ };
+
+ cChannel *m_Channel;
+ cChannel *m_ListChannel;
+ cStreamdevLiveStreamer *m_LiveStreamer;
+ eStreamType m_StreamType;
+ eHTTPStatus m_Status;
+ bool m_Startup;
+
+public:
+ cConnectionHTTP(void);
+ virtual ~cConnectionHTTP();
+
+ virtual void Detach(void);
+ virtual void Attach(void);
+
+ virtual bool Command(char *Cmd);
+ bool CmdGET(char *Opts);
+
+ virtual void Flushed(void);
+};
+
+#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
diff --git a/server/connectionVTP.c b/server/connectionVTP.c
new file mode 100644
index 0000000..2177905
--- /dev/null
+++ b/server/connectionVTP.c
@@ -0,0 +1,553 @@
+/*
+ * $Id: connectionVTP.c,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
+ */
+
+#include "server/connectionVTP.h"
+#include "server/livestreamer.h"
+#include "server/suspend.h"
+#include "setup.h"
+
+#include <vdr/tools.h>
+#include <tools/select.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+/* VTP Response codes:
+ 220: Service ready
+ 221: Service closing connection
+ 500: Syntax error or Command unrecognized
+ 501: Wrong parameters or missing parameters
+ 550: Action not done
+ 551: Data connection not accepted
+ 560: Channel not available currently
+ 561: Capability not known
+ 562: Pid not available currently
+ 563: Recording not available (currently?)
+*/
+
+cConnectionVTP::cConnectionVTP(void): cServerConnection("VTP") {
+ m_StreamPIDS = false;
+ m_LiveStreamer = NULL;
+ m_ClientCaps = stTS;
+
+ memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
+}
+
+cConnectionVTP::~cConnectionVTP() {
+ if (m_LiveStreamer != NULL) delete m_LiveStreamer;
+
+ for (int idx = 0; idx < si_Count; ++idx)
+ if (m_DataSockets[idx] != NULL) delete m_DataSockets[idx];
+}
+
+void cConnectionVTP::Welcome(void) {
+ Respond(220, "Welcome to Video Disk Recorder (VTP)");
+}
+
+void cConnectionVTP::Reject(void) {
+ Respond(221, "Too many clients or client not allowed to connect");
+ cServerConnection::Reject();
+}
+
+void cConnectionVTP::Detach(void) {
+ if (m_LiveStreamer != NULL) m_LiveStreamer->Detach();
+}
+
+void cConnectionVTP::Attach(void) {
+ if (m_LiveStreamer != NULL) m_LiveStreamer->Attach();
+}
+
+bool cConnectionVTP::Command(char *Cmd) {
+ char *ep;
+ if ((ep = strchr(Cmd, ' ')) != NULL)
+ *(ep++) = '\0';
+ else
+ ep = Cmd + strlen(Cmd);
+
+ if (strcasecmp(Cmd, "CAPS") == 0) return CmdCAPS(ep);
+ else if (strcasecmp(Cmd, "PROV") == 0) return CmdPROV(ep);
+ else if (strcasecmp(Cmd, "PORT") == 0) return CmdPORT(ep);
+ else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(ep);
+ else if (strcasecmp(Cmd, "ADDP") == 0) return CmdADDP(ep);
+ else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(ep);
+ else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(ep);
+ else if (strcasecmp(Cmd, "DELF") == 0) return CmdDELF(ep);
+ else if (strcasecmp(Cmd, "ABRT") == 0) return CmdABRT(ep);
+ else if (strcasecmp(Cmd, "QUIT") == 0) return CmdQUIT(ep);
+ else if (strcasecmp(Cmd, "SUSP") == 0) return CmdSUSP(ep);
+ // Commands adopted from SVDRP
+ else if (strcasecmp(Cmd, "LSTE") == 0) return CmdLSTE(ep);
+ else if (strcasecmp(Cmd, "LSTR") == 0) return CmdLSTR(ep);
+ else if (strcasecmp(Cmd, "DELR") == 0) return CmdDELR(ep);
+ else if (strcasecmp(Cmd, "LSTT") == 0) return CmdLSTT(ep);
+ else if (strcasecmp(Cmd, "MODT") == 0) return CmdMODT(ep);
+ else if (strcasecmp(Cmd, "NEWT") == 0) return CmdNEWT(ep);
+ else if (strcasecmp(Cmd, "DELT") == 0) return CmdDELT(ep);
+ else
+ return Respond(500, (cTBString)"Unknown Command '" + Cmd + "'");
+}
+
+bool cConnectionVTP::CmdCAPS(char *Opts) {
+ if (strcasecmp(Opts, "TSPIDS") == 0) m_StreamPIDS = true;
+ else {
+ int idx = 0;
+ while (idx < st_Count && strcasecmp(Opts, StreamTypes[idx]) != 0)
+ ++idx;
+
+ if (idx == st_Count)
+ return Respond(561, (cTBString)"Capability \"" + Opts + "\" not known");
+
+ m_ClientCaps = (eStreamType)idx;
+ }
+ return Respond(220, (cTBString)"Capability \"" + Opts + "\" accepted");
+}
+
+bool cConnectionVTP::CmdPROV(char *Opts) {
+ cChannel *chan;
+ int prio;
+ char *ep;
+
+ prio = strtol(Opts, &ep, 10);
+ if (ep == Opts || !isspace(*ep))
+ return Respond(501, "Use: PROV Priority Channel");
+
+ Opts = skipspace(ep);
+ if ((chan = ChannelFromString(Opts)) == NULL)
+ return Respond(550, (cTBString)"Undefined channel \"" + Opts + "\"");
+
+ return GetDevice(chan, prio) != NULL
+ ? Respond(220, "Channel available")
+ : Respond(560, "Channel not available");
+}
+
+bool cConnectionVTP::CmdPORT(char *Opts) {
+ uint id, dataport = 0;
+ char dataip[20];
+ char *ep, *ipoffs;
+ int n;
+
+ id = strtoul(Opts, &ep, 10);
+ if (ep == Opts || !isspace(*ep))
+ return Respond(500, "Use: PORT Id Destination");
+
+ if (id >= si_Count)
+ return Respond(501, "Wrong connection id " + cTBString::Number(id));
+
+ Opts = skipspace(ep);
+ n = 0;
+ ipoffs = dataip;
+ while ((ep = strchr(Opts, ',')) != NULL) {
+ if (n < 4) {
+ memcpy(ipoffs, Opts, ep - Opts);
+ ipoffs += ep - Opts;
+ if (n < 3) *(ipoffs++) = '.';
+ } else if (n == 4) {
+ *ep = 0;
+ dataport = strtoul(Opts, NULL, 10) << 8;
+ } else
+ break;
+ Opts = ep + 1;
+ ++n;
+ }
+ *ipoffs = '\0';
+
+ if (n != 5)
+ return Respond(501, "Argument count invalid (must be 6 values)");
+
+ dataport |= strtoul(Opts, NULL, 10);
+
+ isyslog("Streamdev: Setting data connection to %s:%d", dataip, dataport);
+
+ m_DataSockets[id] = new cTBSocket(SOCK_STREAM);
+ if (!m_DataSockets[id]->Connect(dataip, dataport)) {
+ esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s",
+ dataip, dataport, strerror(errno));
+ DELETENULL(m_DataSockets[id]);
+ return Respond(551, "Couldn't open data connection");
+ }
+
+ if (id == siLive)
+ m_LiveStreamer->Start(m_DataSockets[id]);
+
+ return Respond(220, "Port command ok, data connection opened");
+}
+
+bool cConnectionVTP::CmdTUNE(char *Opts) {
+ const cChannel *chan;
+ cDevice *dev;
+
+ if ((chan = ChannelFromString(Opts)) == NULL)
+ return Respond(550, (cTBString)"Undefined channel \"" + Opts + "\"");
+
+ if ((dev = GetDevice(chan, 0)) == NULL)
+ return Respond(560, "Channel not available");
+
+ if (!dev->SwitchChannel(chan, false))
+ return Respond(560, "Channel not available");
+
+ delete m_LiveStreamer;
+ m_LiveStreamer = new cStreamdevLiveStreamer(1);
+ m_LiveStreamer->SetChannel(chan, m_ClientCaps, m_StreamPIDS);
+ m_LiveStreamer->SetDevice(dev);
+
+ return Respond(220, "Channel tuned");
+}
+
+bool cConnectionVTP::CmdADDP(char *Opts) {
+ int pid;
+ char *end;
+
+ pid = strtoul(Opts, &end, 10);
+ if (end == Opts || (*end != '\0' && *end != ' '))
+ return Respond(500, "Use: ADDP Pid");
+
+ return m_LiveStreamer && m_LiveStreamer->SetPid(pid, true)
+ ? Respond(220, "Pid " + cTBString::Number(pid) + " available")
+ : Respond(560, "Pid " + cTBString::Number(pid) + " not available");
+}
+
+bool cConnectionVTP::CmdDELP(char *Opts) {
+ int pid;
+ char *end;
+
+ pid = strtoul(Opts, &end, 10);
+ if (end == Opts || (*end != '\0' && *end != ' '))
+ return Respond(500, "Use: DELP Pid");
+
+ return m_LiveStreamer && m_LiveStreamer->SetPid(pid, false)
+ ? Respond(220, "Pid " + cTBString::Number(pid) + " stopped")
+ : Respond(560, "Pid " + cTBString::Number(pid) + " not transferring");
+}
+
+bool cConnectionVTP::CmdADDF(char *Opts) {
+#if VDRVERSNUM >= 10300
+ int pid, tid, mask;
+ char *ep;
+
+ if (m_LiveStreamer == NULL)
+ return Respond(560, "Can't set filters without a stream");
+
+ pid = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != ' '))
+ return Respond(500, "Use: ADDF Pid Tid Mask");
+ Opts = skipspace(ep);
+ tid = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != ' '))
+ return Respond(500, "Use: ADDF Pid Tid Mask");
+ Opts = skipspace(ep);
+ mask = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != '\0' && *ep != ' '))
+ return Respond(500, "Use: ADDF Pid Tid Mask");
+
+ return m_LiveStreamer->SetFilter(pid, tid, mask, true)
+ ? Respond(220, "Filter " + cTBString::Number(pid) + " transferring")
+ : Respond(560, "Filter " + cTBString::Number(pid) + " not available");
+#else
+ return Respond(500, "ADDF known but unimplemented with VDR < 1.3.0");
+#endif
+}
+
+bool cConnectionVTP::CmdDELF(char *Opts) {
+#if VDRVERSNUM >= 10307
+ int pid, tid, mask;
+ char *ep;
+
+ if (m_LiveStreamer == NULL)
+ return Respond(560, "Can't delete filters without a stream");
+
+ pid = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != ' '))
+ return Respond(500, "Use: DELF Pid Tid Mask");
+ Opts = skipspace(ep);
+ tid = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != ' '))
+ return Respond(500, "Use: DELF Pid Tid Mask");
+ Opts = skipspace(ep);
+ mask = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != '\0' && *ep != ' '))
+ return Respond(500, "Use: DELF Pid Tid Mask");
+
+ return m_LiveStreamer->SetFilter(pid, tid, mask, false)
+ ? Respond(220, "Filter " + cTBString::Number(pid) + " stopped")
+ : Respond(560, "Filter " + cTBString::Number(pid) + " not transferring");
+#else
+ return Respond(500, "DELF known but unimplemented with VDR < 1.3.0");
+#endif
+}
+
+bool cConnectionVTP::CmdABRT(char *Opts) {
+ uint id;
+ char *ep;
+
+ id = strtoul(Opts, &ep, 10);
+ if (ep == Opts || (*ep != '\0' && *ep != ' '))
+ return Respond(500, "Use: ABRT Id");
+
+ time_t st = time_ms();
+ if (id == siLive)
+ DELETENULL(m_LiveStreamer);
+
+ Dprintf("ABRT took %ld ms\n", time_ms() - st);
+ DELETENULL(m_DataSockets[id]);
+ return Respond(220, "Data connection closed");
+}
+
+bool cConnectionVTP::CmdQUIT(char *Opts) {
+ if (!Respond(221, "Video Disk Recorder closing connection"))
+ return false;
+ DeferClose();
+ return true;
+}
+
+bool cConnectionVTP::CmdSUSP(char *Opts) {
+ if (StreamdevServerSetup.SuspendMode == smAlways || cSuspendCtl::IsActive())
+ return Respond(220, "Server is suspended");
+ else if (StreamdevServerSetup.SuspendMode == smOffer
+ && StreamdevServerSetup.AllowSuspend) {
+ cControl::Launch(new cSuspendCtl);
+ return Respond(220, "Server is suspended");
+ } else
+ return Respond(550, "Client may not suspend server");
+}
+
+// Functions adopted from SVDRP
+#define INIT_WRAPPER() bool _res = true
+#define Reply(x...) _res &= ReplyWrapper(x)
+#define EXIT_WRAPPER() return _res
+
+bool cConnectionVTP::ReplyWrapper(int Code, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ char *buffer;
+ vasprintf(&buffer, fmt, ap);
+ int npos;
+ if (buffer[npos = strlen(buffer)-1] == '\n')
+ buffer[npos] = '\0';
+ bool res = Respond(Code, buffer);
+ free(buffer);
+ return res;
+}
+
+bool cConnectionVTP::CmdLSTE(char *Option) {
+#if VDRVERSNUM < 10300
+ cMutexLock MutexLock;
+#else
+ cSchedulesLock MutexLock;
+#endif
+ INIT_WRAPPER();
+ /* we need to create a blocking copy of the socket here */
+ int dupfd = dup(*this);
+ fcntl(dupfd, F_SETFL, fcntl(dupfd, F_GETFL) & ~O_NONBLOCK);
+#if VDRVERSNUM < 10300
+ const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
+#else
+ const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
+#endif
+ if (Schedules) {
+ FILE *f = fdopen(dupfd, "w");
+ if (f) {
+ Schedules->Dump(f, "215-");
+ fflush(f);
+ Reply(215, "End of EPG data");
+ fclose(f);
+ }
+ else
+ Reply(451, "Can't open file connection");
+ }
+ else
+ Reply(451, "Can't get EPG data");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdLSTR(char *Option) {
+ INIT_WRAPPER();
+ bool recordings = Recordings.Load();
+ Recordings.Sort();
+ if (*Option) {
+ if (isnumber(Option)) {
+ cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
+ if (recording) {
+ if (recording->Summary()) {
+ char *summary = strdup(recording->Summary());
+ Reply(250, "%s", strreplace(summary,'\n','|'));
+ free(summary);
+ }
+ else
+ Reply(550, "No summary availabe");
+ }
+ else
+ Reply(550, "Recording \"%s\" not found", Option);
+ }
+ else
+ Reply(501, "Error in recording number \"%s\"", Option);
+ }
+ else if (recordings) {
+ cRecording *recording = Recordings.First();
+ while (recording) {
+ Reply(recording == Recordings.Last() ? 250 : -250, "%d %s", recording->Index() + 1, recording->Title(' ', true));
+ recording = Recordings.Next(recording);
+ }
+ }
+ else
+ Reply(550, "No recordings available");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdDELR(char *Option) {
+ INIT_WRAPPER();
+ if (*Option) {
+ if (isnumber(Option)) {
+ cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
+ if (recording) {
+ if (recording->Delete())
+ Reply(250, "Recording \"%s\" deleted", Option);
+ else
+ Reply(554, "Error while deleting recording!");
+ }
+ else
+ Reply(550, "Recording \"%s\" not found%s", Option, Recordings.Count() ? "" : " (use LSTR before deleting)");
+ }
+ else
+ Reply(501, "Error in recording number \"%s\"", Option);
+ }
+ else
+ Reply(501, "Missing recording number");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdLSTT(char *Option) {
+ INIT_WRAPPER();
+ if (*Option) {
+ if (isnumber(Option)) {
+ cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
+ if (timer)
+ Reply(250, "%d %s", timer->Index() + 1, timer->ToText(true));
+ else
+ Reply(501, "Timer \"%s\" not defined", Option);
+ }
+ else
+ Reply(501, "Error in timer number \"%s\"", Option);
+ }
+ else if (Timers.Count()) {
+ for (int i = 0; i < Timers.Count(); i++) {
+ cTimer *timer = Timers.Get(i);
+ if (timer)
+ Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText(true));
+ else
+ Reply(501, "Timer \"%d\" not found", i + 1);
+ }
+ }
+ else
+ Reply(550, "No timers defined");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdMODT(char *Option) {
+ INIT_WRAPPER();
+ if (*Option) {
+ char *tail;
+ int n = strtol(Option, &tail, 10);
+ if (tail && tail != Option) {
+ tail = skipspace(tail);
+ cTimer *timer = Timers.Get(n - 1);
+ if (timer) {
+ cTimer t = *timer;
+ if (strcasecmp(tail, "ON") == 0)
+#if VDRVERSNUM < 10300
+ t.SetActive(taActive);
+#else
+ t.SetFlags(tfActive);
+#endif
+ else if (strcasecmp(tail, "OFF") == 0)
+#if VDRVERSNUM < 10300
+ t.SetActive(taInactive);
+#else
+ t.ClrFlags(tfActive);
+#endif
+ else if (!t.Parse(tail)) {
+ Reply(501, "Error in timer settings");
+ EXIT_WRAPPER();
+ }
+ *timer = t;
+ Timers.Save();
+#if VDRVERSNUM < 10300
+ isyslog("timer %d modified (%s)", timer->Index() + 1,
+ timer->Active() ? "active" : "inactive");
+#else
+ isyslog("timer %d modified (%s)", timer->Index() + 1,
+ timer->HasFlags(tfActive) ? "active" : "inactive");
+#endif
+ Reply(250, "%d %s", timer->Index() + 1, timer->ToText(true));
+ }
+ else
+ Reply(501, "Timer \"%d\" not defined", n);
+ }
+ else
+ Reply(501, "Error in timer number");
+ }
+ else
+ Reply(501, "Missing timer settings");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdNEWT(char *Option) {
+ INIT_WRAPPER();
+ if (*Option) {
+ cTimer *timer = new cTimer;
+ if (timer->Parse(Option)) {
+ cTimer *t = Timers.GetTimer(timer);
+ if (!t) {
+ Timers.Add(timer);
+ Timers.Save();
+ isyslog("timer %d added", timer->Index() + 1);
+ Reply(250, "%d %s", timer->Index() + 1, timer->ToText(true));
+ EXIT_WRAPPER();
+ }
+ else
+ Reply(550, "Timer already defined: %d %s", t->Index() + 1, t->ToText(true));
+ }
+ else
+ Reply(501, "Error in timer settings");
+ delete timer;
+ }
+ else
+ Reply(501, "Missing timer settings");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdDELT(char *Option) {
+ INIT_WRAPPER();
+ if (*Option) {
+ if (isnumber(Option)) {
+ cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
+ if (timer) {
+ if (!timer->Recording()) {
+ Timers.Del(timer);
+ Timers.Save();
+ isyslog("timer %s deleted", Option);
+ Reply(250, "Timer \"%s\" deleted", Option);
+ }
+ else
+ Reply(550, "Timer \"%s\" is recording", Option);
+ }
+ else
+ Reply(501, "Timer \"%s\" not defined", Option);
+ }
+ else
+ Reply(501, "Error in timer number \"%s\"", Option);
+ }
+ else
+ Reply(501, "Missing timer number");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::Respond(int Code, const char *Message) {
+ cTBString pkt;
+ if (Code < 0)
+ pkt.Format("%03d-%s", -Code, Message);
+ else
+ pkt.Format("%03d %s", Code, Message);
+ return cServerConnection::Respond((const char*)pkt);
+}
+
diff --git a/server/connectionVTP.h b/server/connectionVTP.h
new file mode 100644
index 0000000..ec42d23
--- /dev/null
+++ b/server/connectionVTP.h
@@ -0,0 +1,57 @@
+#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
+#define VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
+
+#include "server/connection.h"
+
+class cDevice;
+class cTBSocket;
+class cStreamdevLiveStreamer;
+
+class cConnectionVTP: public cServerConnection {
+private:
+ cTBSocket *m_DataSockets[si_Count];
+ eStreamType m_ClientCaps;
+ bool m_StreamPIDS;
+
+ cStreamdevLiveStreamer *m_LiveStreamer;
+
+ // Members adopted from SVDRP
+ cRecordings Recordings;
+
+public:
+ cConnectionVTP(void);
+ virtual ~cConnectionVTP();
+
+ virtual void Welcome(void);
+ virtual void Reject(void);
+
+ virtual void Detach(void);
+ virtual void Attach(void);
+
+ bool Command(char *Cmd);
+ bool CmdCAPS(char *Opts);
+ bool CmdPROV(char *Opts);
+ bool CmdPORT(char *Opts);
+ bool CmdTUNE(char *Opts);
+ bool CmdADDP(char *Opts);
+ bool CmdDELP(char *Opts);
+ bool CmdADDF(char *Opts);
+ bool CmdDELF(char *Opts);
+ bool CmdABRT(char *Opts);
+ bool CmdQUIT(char *Opts);
+ bool CmdSUSP(char *Opts);
+
+ // Commands adopted from SVDRP
+ bool ReplyWrapper(int Code, const char *fmt, ...);
+ bool CmdLSTE(char *Opts);
+ bool CmdLSTR(char *Opts);
+ bool CmdDELR(char *Opts);
+ bool CmdLSTT(char *Opts);
+ bool CmdMODT(char *Opts);
+ bool CmdNEWT(char *Opts);
+ bool CmdDELT(char *Opts);
+
+ bool Respond(int Code, const char *Message);
+};
+
+#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
diff --git a/server/livefilter.c b/server/livefilter.c
new file mode 100644
index 0000000..14f20fa
--- /dev/null
+++ b/server/livefilter.c
@@ -0,0 +1,64 @@
+/*
+ * $Id: livefilter.c,v 1.1 2004/12/30 22:44:27 lordjaxom Exp $
+ */
+
+#include "server/livefilter.h"
+#include "server/livestreamer.h"
+#include "common.h"
+
+#if VDRVERSNUM >= 10300
+
+cStreamdevLiveFilter::cStreamdevLiveFilter(cStreamdevLiveStreamer *Streamer) {
+ m_Streamer = Streamer;
+}
+
+cStreamdevLiveFilter::~cStreamdevLiveFilter() {
+}
+
+void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data,
+ int Length) {
+ static time_t firsterr = 0;
+ static int errcnt = 0;
+ static bool showerr = true;
+
+ uchar buffer[TS_SIZE];
+ int length = Length;
+ int pos = 0;
+
+ while (length > 0) {
+ int chunk = min(length, TS_SIZE - 5);
+ buffer[0] = TS_SYNC_BYTE;
+ buffer[1] = (Pid >> 8) & 0xff;
+ buffer[2] = Pid & 0xff;
+ buffer[3] = Tid;
+ buffer[4] = (uchar)chunk;
+ memcpy(buffer + 5, Data + pos, chunk);
+ length -= chunk;
+ pos += chunk;
+
+ int p = m_Streamer->Put(buffer, TS_SIZE);
+ if (p != TS_SIZE) {
+ ++errcnt;
+ if (showerr) {
+ if (firsterr == 0)
+ firsterr = time_ms();
+ else if (firsterr + BUFOVERTIME > time_ms() && errcnt > BUFOVERCOUNT) {
+ esyslog("ERROR: too many buffer overflows, logging stopped");
+ showerr = false;
+ firsterr = time_ms();
+ }
+ } else if (firsterr + BUFOVERTIME < time_ms()) {
+ showerr = true;
+ firsterr = 0;
+ errcnt = 0;
+ }
+
+ if (showerr)
+ esyslog("ERROR: ring buffer overflow (%d bytes dropped)", TS_SIZE - p);
+ else
+ firsterr = time_ms();
+ }
+ }
+}
+
+#endif // VDRVERSNUM >= 10300
diff --git a/server/livefilter.h b/server/livefilter.h
new file mode 100644
index 0000000..6c480b8
--- /dev/null
+++ b/server/livefilter.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: livefilter.h,v 1.1 2004/12/30 22:44:27 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMEV_LIVEFILTER_H
+#define VDR_STREAMEV_LIVEFILTER_H
+
+#include <vdr/config.h>
+
+# if VDRVERSNUM >= 10300
+
+#include <vdr/filter.h>
+
+class cStreamdevLiveFilter: public cFilter {
+ friend class cStreamdevLiveStreamer;
+
+private:
+ cStreamdevLiveStreamer *m_Streamer;
+
+protected:
+ virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
+
+public:
+ cStreamdevLiveFilter(cStreamdevLiveStreamer *Streamer);
+ virtual ~cStreamdevLiveFilter();
+};
+
+# endif // VDRVERSNUM >= 10300
+#endif // VDR_STREAMEV_LIVEFILTER_H
diff --git a/server/livestreamer.c b/server/livestreamer.c
new file mode 100644
index 0000000..4204ea3
--- /dev/null
+++ b/server/livestreamer.c
@@ -0,0 +1,242 @@
+#include <vdr/ringbuffer.h>
+
+#include "server/livestreamer.h"
+#include "remux/ts2ps.h"
+#include "remux/ts2es.h"
+#include "common.h"
+
+cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer,
+ int Ca, int Priority,
+ int Pid1, int Pid2, int Pid3, int Pid4,
+ int Pid5, int Pid6, int Pid7, int Pid8,
+ int Pid9, int Pid10, int Pid11, int Pid12,
+ int Pid13, int Pid14, int Pid15, int Pid16):
+ cReceiver(Ca, Priority, 16,
+ Pid1, Pid2, Pid3, Pid4, Pid5, Pid6, Pid7, Pid8,
+ Pid9, Pid10, Pid11, Pid12, Pid13, Pid14, Pid15, Pid16) {
+ m_Streamer = Streamer;
+}
+
+cStreamdevLiveReceiver::~cStreamdevLiveReceiver() {
+ Dprintf("Killing live receiver\n");
+ Detach();
+}
+
+void cStreamdevLiveReceiver::Receive(uchar *Data, int Length) {
+ static time_t firsterr = 0;
+ static int errcnt = 0;
+ static bool showerr = true;
+
+ int p = m_Streamer->Put(Data, Length);
+ if (p != Length) {
+ ++errcnt;
+ if (showerr) {
+ if (firsterr == 0)
+ firsterr = time_ms();
+ else if (firsterr + BUFOVERTIME > time_ms() && errcnt > BUFOVERCOUNT) {
+ esyslog("ERROR: too many buffer overflows, logging stopped");
+ showerr = false;
+ firsterr = time_ms();
+ }
+ } else if (firsterr + BUFOVERTIME < time_ms()) {
+ showerr = true;
+ firsterr = 0;
+ errcnt = 0;
+ }
+
+ if (showerr)
+ esyslog("ERROR: ring buffer overflow (%d bytes dropped)", Length - p);
+ else
+ firsterr = time_ms();
+ }
+}
+
+cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority):
+ cStreamdevStreamer("Live streamer") {
+ m_Priority = Priority;
+ m_Channel = NULL;
+ m_Device = NULL;
+ m_Receiver = NULL;
+ m_Remux = NULL;
+ m_Buffer = NULL;
+ m_Sequence = 0;
+#if VDRVERSNUM >= 10300
+ m_Filter = NULL;
+#endif
+ memset(m_Pids, 0, sizeof(m_Pids));
+}
+
+cStreamdevLiveStreamer::~cStreamdevLiveStreamer() {
+ Dprintf("Desctructing Live streamer\n");
+ delete m_Receiver;
+ delete m_Remux;
+#if VDRVERSNUM >= 10300
+ delete m_Filter;
+#endif
+ free(m_Buffer);
+}
+
+void cStreamdevLiveStreamer::Detach(void) {
+ m_Device->Detach(m_Receiver);
+}
+
+void cStreamdevLiveStreamer::Attach(void) {
+ m_Device->AttachReceiver(m_Receiver);
+}
+
+void cStreamdevLiveStreamer::Start(cTBSocket *Socket) {
+ Dprintf("LIVESTREAMER START\n");
+ cStreamdevStreamer::Start(Socket);
+}
+
+bool cStreamdevLiveStreamer::SetPid(int Pid, bool On) {
+ int idx;
+ bool haspids = false;
+
+ if (On) {
+ for (idx = 0; idx < MAXRECEIVEPIDS; ++idx) {
+ if (m_Pids[idx] == Pid)
+ return true; // No change needed
+ else if (m_Pids[idx] == 0) {
+ m_Pids[idx] = Pid;
+ haspids = true;
+ break;
+ }
+ }
+
+ if (idx == MAXRECEIVEPIDS) {
+ esyslog("ERROR: Streamdev: No free slot to receive pid %d\n", Pid);
+ return false;
+ }
+ } else {
+ for (idx = 0; idx < MAXRECEIVEPIDS; ++idx) {
+ if (m_Pids[idx] == Pid)
+ m_Pids[idx] = 0;
+ else if (m_Pids[idx] != 0)
+ haspids = true;
+ }
+ }
+
+ DELETENULL(m_Receiver);
+ if (haspids) {
+ Dprintf("Creating Receiver to respect changed pids\n");
+ m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->Ca(), m_Priority,
+ m_Pids[0], m_Pids[1], m_Pids[2], m_Pids[3],
+ m_Pids[4], m_Pids[5], m_Pids[6], m_Pids[7],
+ m_Pids[8], m_Pids[9], m_Pids[10], m_Pids[11],
+ m_Pids[12], m_Pids[13], m_Pids[14], m_Pids[15]);
+ if (m_Device != NULL) {
+ Dprintf("Attaching new receiver\n");
+ m_Device->AttachReceiver(m_Receiver);
+ }
+ }
+ return true;
+}
+
+bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, int StreamType,
+ bool StreamPIDS) {
+ Dprintf("Initializing Remuxer for full channel transfer\n");
+ printf("ca pid: %d\n", Channel->Ca());
+ m_Channel = Channel;
+ switch (StreamType) {
+ case stES:
+ {
+ int pid = ISRADIO(Channel) ? Channel->Apid1() : Channel->Vpid();
+ m_Remux = new cTS2ESRemux(pid);
+ return SetPid(pid, true);
+ }
+
+ case stPES:
+ m_Remux = new cTS2PSRemux(Channel->Vpid(), Channel->Apid1(),
+ Channel->Apid2(), Channel->Dpid1(), 0, false);
+ return SetPid(Channel->Vpid(), true)
+ && SetPid(Channel->Apid1(), true)
+ && SetPid(Channel->Apid2(), true)
+ && SetPid(Channel->Dpid1(), true);
+ break;
+
+ case stPS:
+ m_Remux = new cTS2PSRemux(Channel->Vpid(), Channel->Apid1(), 0, 0, 0, true);
+ return SetPid(Channel->Vpid(), true)
+ && SetPid(Channel->Apid1(), true);
+ break;
+
+ case stTS:
+ if (!StreamPIDS) {
+ return SetPid(Channel->Vpid(), true)
+ && SetPid(Channel->Apid1(), true)
+ && SetPid(Channel->Apid2(), true)
+ && SetPid(Channel->Dpid1(), true);
+ }
+ Dprintf("pid streaming mode\n");
+ return true;
+ break;
+ }
+ return false;
+}
+
+bool cStreamdevLiveStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask,
+ bool On) {
+#if VDRVERSNUM >= 10300
+ Dprintf("setting filter\n");
+ if (On) {
+ if (m_Filter == NULL) {
+ m_Filter = new cStreamdevLiveFilter(this);
+ Dprintf("attaching filter to device\n");
+ m_Device->AttachFilter(m_Filter);
+ }
+ m_Filter->Set(Pid, Tid, Mask);
+ } else if (m_Filter != NULL)
+ m_Filter->Del(Pid, Tid, Mask);
+ return true;
+#else
+ return false;
+#endif
+}
+
+uchar *cStreamdevLiveStreamer::Process(const uchar *Data, int &Count,
+ int &Result) {
+ uchar *remuxed = m_Remux != NULL ? m_Remux->Process(Data, Count, Result)
+ : cStreamdevStreamer::Process(Data, Count, Result);
+ if (remuxed) {
+ /*if (Socket()->Type() == SOCK_DGRAM) {
+ free(m_Buffer);
+ Result += 12;
+ m_Buffer = MALLOC(uchar, Result);
+ m_Buffer[0] = 0x01;
+ m_Buffer[1] = 0x02;
+ m_Buffer[2] = 0x03;
+ m_Buffer[3] = 0x04;
+ m_Buffer[4] = (Result & 0xff000000) >> 24;
+ m_Buffer[5] = (Result & 0xff0000) >> 16;
+ m_Buffer[6] = (Result & 0xff00) >> 8;
+ m_Buffer[7] = (Result & 0xff);
+ m_Buffer[8] = (m_Sequence & 0xff000000) >> 24;
+ m_Buffer[9] = (m_Sequence & 0xff0000) >> 16;
+ m_Buffer[10] = (m_Sequence & 0xff00) >> 8;
+ m_Buffer[11] = (m_Sequence & 0xff);
+ memcpy(m_Buffer + 12, Data, Result - 12);
+ if (m_Sequence++ == 0x7fffffff)
+ m_Sequence = 0;
+ return m_Buffer;
+ }*/
+ return remuxed;
+ }
+ return NULL;
+}
+
+cTBString cStreamdevLiveStreamer::Report(void) {
+ cTBString result;
+
+ if (m_Device != NULL)
+ result += "+- Device is " + cTBString::Number(m_Device->CardIndex()) + "\n";
+ if (m_Receiver != NULL)
+ result += "+- Receiver is allocated\n";
+
+ result += "+- Pids are ";
+ for (int i = 0; i < MAXRECEIVEPIDS; ++i)
+ if (m_Pids[i] != 0)
+ result += cTBString::Number(m_Pids[i]) + ", ";
+ result += "\n";
+ return result;
+}
diff --git a/server/livestreamer.h b/server/livestreamer.h
new file mode 100644
index 0000000..7682206
--- /dev/null
+++ b/server/livestreamer.h
@@ -0,0 +1,70 @@
+#ifndef VDR_STREAMDEV_LIVESTREAMER_H
+#define VDR_STREAMDEV_LIVESTREAMER_H
+
+#include <vdr/config.h>
+#include <vdr/receiver.h>
+
+#include "server/streamer.h"
+#include "server/livefilter.h"
+#include "common.h"
+
+#if MAXRECEIVEPIDS < 16
+# error Too few receiver pids allowed! Please contact sascha@akv-soft.de!
+#endif
+
+class cTSRemux;
+
+class cStreamdevLiveReceiver: public cReceiver {
+ friend class cStreamdevLiveStreamer;
+
+private:
+ cStreamdevLiveStreamer *m_Streamer;
+
+protected:
+ virtual void Receive(uchar *Data, int Length);
+
+public:
+ cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, int Priority, int Ca,
+ int Pid1 = 0, int Pid2 = 0, int Pid3 = 0, int Pid4 = 0,
+ int Pid5 = 0, int Pid6 = 0, int Pid7 = 0, int Pid8 = 0,
+ int Pid9 = 0, int Pid10 = 0, int Pid11 = 0, int Pid12 = 0,
+ int Pid13 = 0, int Pid14 = 0, int Pid15 = 0, int Pid16 = 0);
+ virtual ~cStreamdevLiveReceiver();
+};
+
+class cStreamdevLiveStreamer: public cStreamdevStreamer {
+private:
+ int m_Priority;
+ int m_Pids[MAXRECEIVEPIDS];
+ const cChannel *m_Channel;
+ cDevice *m_Device;
+ cStreamdevLiveReceiver *m_Receiver;
+ cTSRemux *m_Remux;
+ uchar *m_Buffer;
+ int m_Sequence;
+#if VDRVERSNUM >= 10300
+ cStreamdevLiveFilter *m_Filter;
+#endif
+
+protected:
+ virtual uchar *Process(const uchar *Data, int &Count, int &Result);
+
+public:
+ cStreamdevLiveStreamer(int Priority);
+ virtual ~cStreamdevLiveStreamer();
+
+ void SetDevice(cDevice *Device) { m_Device = Device; }
+ bool SetPid(int Pid, bool On);
+ bool SetChannel(const cChannel *Channel, int StreamType, bool StreamPIDS);
+ bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On);
+
+ virtual void Detach(void);
+ virtual void Attach(void);
+
+ virtual void Start(cTBSocket *Socket);
+
+ // Statistical purposes:
+ virtual cTBString Report(void);
+};
+
+#endif // VDR_STREAMDEV_LIVESTREAMER_H
diff --git a/server/server.c b/server/server.c
new file mode 100644
index 0000000..9b4de9d
--- /dev/null
+++ b/server/server.c
@@ -0,0 +1,148 @@
+/*
+ * $Id: server.c,v 1.1 2004/12/30 22:44:20 lordjaxom Exp $
+ */
+
+#include "server/server.h"
+#include "server/componentVTP.h"
+#include "server/componentHTTP.h"
+#include "server/setup.h"
+
+#include <vdr/tools.h>
+#include <tools/select.h>
+#include <string.h>
+#include <errno.h>
+
+cSVDRPhosts StreamdevHosts;
+
+cStreamdevServer *cStreamdevServer::m_Instance = NULL;
+
+cStreamdevServer::cStreamdevServer(void)
+#if VDRVERSNUM >= 10300
+ : cThread("Streamdev: server")
+#endif
+{
+ m_Active = false;
+
+ StreamdevHosts.Load(AddDirectory(cPlugin::ConfigDirectory(),
+ "streamdevhosts.conf"), true);
+}
+
+cStreamdevServer::~cStreamdevServer() {
+ if (m_Active) Stop();
+}
+
+void cStreamdevServer::Init(void) {
+ if (m_Instance == NULL) {
+ m_Instance = new cStreamdevServer;
+ if (StreamdevServerSetup.StartVTPServer)
+ m_Instance->Register(new cComponentVTP);
+ if (StreamdevServerSetup.StartHTTPServer)
+ m_Instance->Register(new cComponentHTTP);
+ m_Instance->Start();
+ }
+}
+
+void cStreamdevServer::Exit(void) {
+ if (m_Instance != NULL) {
+ m_Instance->Stop();
+ DELETENULL(m_Instance);
+ }
+}
+
+void cStreamdevServer::Stop(void) {
+ m_Active = false;
+ Cancel(3);
+}
+
+void cStreamdevServer::Register(cServerComponent *Server) {
+ m_Servers.Add(Server);
+}
+
+void cStreamdevServer::Action(void) {
+ cTBSelect select;
+
+#if VDRVERSNUM < 10300
+ isyslog("Streamdev: Server thread started (pid=%d)", getpid());
+#endif
+
+ m_Active = true;
+
+ /* Initialize Server components, deleting those that failed */
+ for (cServerComponent *c = m_Servers.First(); c;) {
+ cServerComponent *next = m_Servers.Next(c);
+ if (!c->Init())
+ m_Servers.Del(c);
+ c = next;
+ }
+
+ if (!m_Servers.Count()) {
+ esyslog("Streamdev: No server components registered, exiting");
+ m_Active = false;
+ }
+
+ while (m_Active) {
+ select.Clear();
+
+ /* Ask all Server components to register to the selector */
+ for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c))
+ c->AddSelect(select);
+
+ /* Ask all Client connections to register to the selector */
+ for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s))
+ s->AddSelect(select);
+
+ if (select.Select(1000) < 0) {
+ if (!m_Active) // Exit was requested while polling
+ continue;
+ esyslog("Streamdev: Fatal error, server exiting: %s", strerror(errno));
+ m_Active = false;
+ }
+
+ /* Ask all Server components to act on signalled sockets */
+ for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)) {
+ cServerConnection *client;
+ if ((client = c->CanAct(select)) != NULL) {
+ m_Clients.Add(client);
+
+ if (m_Clients.Count() > StreamdevServerSetup.MaxClients) {
+ esyslog("Streamdev: Too many clients, rejecting %s:%d",
+ (const char*)client->RemoteIp(), client->RemotePort());
+ client->Reject();
+ } else if (!StreamdevHosts.Acceptable(client->RemoteIpAddr())) {
+ esyslog("Streamdev: Client from %s:%d not allowed to connect",
+ (const char*)client->RemoteIp(), client->RemotePort());
+ client->Reject();
+ } else
+ client->Welcome();
+ }
+ }
+
+ /* Ask all Client connections to act on signalled sockets */
+ for (cServerConnection *s = m_Clients.First(); s;) {
+ cServerConnection *next = m_Clients.Next(s);
+ if (!s->CanAct(select)) {
+ isyslog("Streamdev: Closing connection to %s:%d",
+ (const char*)s->RemoteIp(), s->RemotePort());
+ s->Close();
+ m_Clients.Del(s);
+ }
+ s = next;
+ }
+ }
+
+ while (m_Clients.Count()) {
+ cServerConnection *client = m_Clients.First();
+ client->Close();
+ m_Clients.Del(client);
+ }
+
+ while (m_Servers.Count()) {
+ cServerComponent *server = m_Servers.First();
+ server->Exit();
+ m_Servers.Del(server);
+ }
+
+#if VDRVERSNUM < 10300
+ isyslog("Streamdev: Server thread stopped");
+#endif
+}
diff --git a/server/server.h b/server/server.h
new file mode 100644
index 0000000..bf40f37
--- /dev/null
+++ b/server/server.h
@@ -0,0 +1,44 @@
+/*
+ * $Id: server.h,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SERVER_H
+#define VDR_STREAMDEV_SERVER_H
+
+#include <vdr/thread.h>
+
+#include "server/component.h"
+#include "server/connection.h"
+
+class cStreamdevServer: public cThread {
+private:
+ bool m_Active;
+
+ cServerComponents m_Servers;
+ cServerConnections m_Clients;
+
+ static cStreamdevServer *m_Instance;
+
+protected:
+ virtual void Action(void);
+
+ void Stop(void);
+
+public:
+ cStreamdevServer(void);
+ virtual ~cStreamdevServer();
+
+ void Register(cServerComponent *Server);
+
+ static void Init(void);
+ static void Exit(void);
+ static bool Active(void);
+};
+
+inline bool cStreamdevServer::Active(void) {
+ return m_Instance && m_Instance->m_Clients.Count() > 0;
+}
+
+extern cSVDRPhosts StreamdevHosts;
+
+#endif // VDR_STREAMDEV_SERVER_H
diff --git a/server/setup.c b/server/setup.c
new file mode 100644
index 0000000..bd8fb67
--- /dev/null
+++ b/server/setup.c
@@ -0,0 +1,94 @@
+/*
+ * $Id: setup.c,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
+ */
+
+#include <vdr/menuitems.h>
+
+#include "server/setup.h"
+#include "server/server.h"
+#include "i18n.h"
+
+cStreamdevServerSetup StreamdevServerSetup;
+
+cStreamdevServerSetup::cStreamdevServerSetup(void) {
+ MaxClients = 5;
+ StartVTPServer = true;
+ VTPServerPort = 2004;
+ StartHTTPServer = true;
+ HTTPServerPort = 3000;
+ HTTPStreamType = stPES;
+ SuspendMode = smOffer;
+ AllowSuspend = false;
+ strcpy(VTPBindIP, "0.0.0.0");
+ strcpy(HTTPBindIP, "0.0.0.0");
+}
+
+bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) {
+ if (strcmp(Name, "MaxClients") == 0) MaxClients = atoi(Value);
+ else if (strcmp(Name, "StartServer") == 0) StartVTPServer = atoi(Value);
+ else if (strcmp(Name, "ServerPort") == 0) VTPServerPort = atoi(Value);
+ else if (strcmp(Name, "VTPBindIP") == 0) strcpy(VTPBindIP, Value);
+ else if (strcmp(Name, "StartHTTPServer") == 0) StartHTTPServer = atoi(Value);
+ else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value);
+ else if (strcmp(Name, "HTTPStreamType") == 0) HTTPStreamType = atoi(Value);
+ else if (strcmp(Name, "HTTPBindIP") == 0) strcpy(HTTPBindIP, Value);
+ else if (strcmp(Name, "SuspendMode") == 0) SuspendMode = atoi(Value);
+ else if (strcmp(Name, "AllowSuspend") == 0) AllowSuspend = atoi(Value);
+ else return false;
+ return true;
+}
+
+cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
+ m_NewSetup = StreamdevServerSetup;
+
+ AddCategory (tr("Common Settings"));
+ AddRangeEdit(tr("Maximum Number of Clients"), m_NewSetup.MaxClients, 0, 100);
+ AddSuspEdit (tr("Suspend behaviour"), m_NewSetup.SuspendMode);
+ AddBoolEdit (tr("Client may suspend"), m_NewSetup.AllowSuspend);
+
+ AddCategory (tr("VDR-to-VDR Server"));
+ AddBoolEdit (tr("Start VDR-to-VDR Server"), m_NewSetup.StartVTPServer);
+ AddShortEdit(tr("VDR-to-VDR Server Port"), m_NewSetup.VTPServerPort);
+ AddIpEdit (tr("Bind to IP"), m_NewSetup.VTPBindIP);
+
+ AddCategory (tr("HTTP Server"));
+ AddBoolEdit (tr("Start HTTP Server"), m_NewSetup.StartHTTPServer);
+ AddShortEdit(tr("HTTP Server Port"), m_NewSetup.HTTPServerPort);
+ AddTypeEdit (tr("HTTP Streamtype"), m_NewSetup.HTTPStreamType);
+ AddIpEdit (tr("Bind to IP"), m_NewSetup.HTTPBindIP);
+
+ SetCurrent(Get(1));
+}
+
+cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() {
+}
+
+void cStreamdevServerMenuSetupPage::Store(void) {
+ bool restart = false;
+ if (m_NewSetup.StartVTPServer != StreamdevServerSetup.StartVTPServer
+ || m_NewSetup.VTPServerPort != StreamdevServerSetup.VTPServerPort
+ || strcmp(m_NewSetup.VTPBindIP, StreamdevServerSetup.VTPBindIP) != 0
+ || m_NewSetup.StartHTTPServer != StreamdevServerSetup.StartHTTPServer
+ || m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort
+ || strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0) {
+ restart = true;
+ cStreamdevServer::Exit();
+ }
+
+ SetupStore("MaxClients", m_NewSetup.MaxClients);
+ SetupStore("StartServer", m_NewSetup.StartVTPServer);
+ SetupStore("ServerPort", m_NewSetup.VTPServerPort);
+ SetupStore("VTPBindIP", m_NewSetup.VTPBindIP);
+ SetupStore("StartHTTPServer", m_NewSetup.StartHTTPServer);
+ SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort);
+ SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType);
+ SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
+ SetupStore("SuspendMode", m_NewSetup.SuspendMode);
+ SetupStore("AllowSuspend", m_NewSetup.AllowSuspend);
+
+ StreamdevServerSetup = m_NewSetup;
+
+ if (restart)
+ cStreamdevServer::Init();
+}
+
diff --git a/server/setup.h b/server/setup.h
new file mode 100644
index 0000000..ff27618
--- /dev/null
+++ b/server/setup.h
@@ -0,0 +1,41 @@
+/*
+ * $Id: setup.h,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SETUPSERVER_H
+#define VDR_STREAMDEV_SETUPSERVER_H
+
+#include "common.h"
+
+struct cStreamdevServerSetup {
+ cStreamdevServerSetup(void);
+
+ bool SetupParse(const char *Name, const char *Value);
+
+ int MaxClients;
+ int StartVTPServer;
+ int VTPServerPort;
+ char VTPBindIP[20];
+ int StartHTTPServer;
+ int HTTPServerPort;
+ int HTTPStreamType;
+ char HTTPBindIP[20];
+ int SuspendMode;
+ int AllowSuspend;
+};
+
+extern cStreamdevServerSetup StreamdevServerSetup;
+
+class cStreamdevServerMenuSetupPage: public cStreamdevMenuSetupPage {
+private:
+ cStreamdevServerSetup m_NewSetup;
+
+protected:
+ virtual void Store(void);
+
+public:
+ cStreamdevServerMenuSetupPage(void);
+ virtual ~cStreamdevServerMenuSetupPage();
+};
+
+#endif // VDR_STREAMDEV_SETUPSERVER_H
diff --git a/server/streamer.c b/server/streamer.c
new file mode 100644
index 0000000..2ffd1cd
--- /dev/null
+++ b/server/streamer.c
@@ -0,0 +1,99 @@
+/*
+ * $Id: streamer.c,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
+ */
+
+#include <vdr/ringbuffer.h>
+#include <vdr/device.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "server/streamer.h"
+#include "server/suspend.h"
+#include "server/setup.h"
+#include "tools/socket.h"
+#include "common.h"
+
+#define VIDEOBUFSIZE MEGABYTE(3)
+#define MAXBLOCKSIZE TS_SIZE*10
+
+cStreamdevStreamer::cStreamdevStreamer(const char *Name)
+#if VDRVERSNUM >= 10300
+ :cThread("Streamdev: " + (cTBString)Name)
+#endif
+{
+ m_Active = false;
+ m_Receivers = 0;
+ m_Buffer = NULL;
+ m_Name = Name;
+ m_Socket = NULL;
+ m_RingBuffer = new cRingBufferLinear(VIDEOBUFSIZE, TS_SIZE * 2, true);
+}
+
+cStreamdevStreamer::~cStreamdevStreamer() {
+ Stop();
+ if (m_Buffer != NULL) delete[] m_Buffer;
+ delete m_RingBuffer;
+}
+
+void cStreamdevStreamer::Start(cTBSocket *Socket) {
+ m_Socket = Socket;
+ Attach();
+ if (!m_Active)
+ cThread::Start();
+}
+
+void cStreamdevStreamer::Stop(void) {
+ if (m_Active) {
+ Dprintf("stopping live streamer\n");
+ m_Active = false;
+ Cancel(3);
+ }
+}
+
+uchar *cStreamdevStreamer::Process(const uchar *Data, int &Count, int &Result) {
+ if (m_Buffer == NULL)
+ m_Buffer = new uchar[MAXBLOCKSIZE];
+
+ if (Count > MAXBLOCKSIZE)
+ Count = MAXBLOCKSIZE;
+ memcpy(m_Buffer, Data, Count);
+ Result = Count;
+ return m_Buffer;
+}
+
+void cStreamdevStreamer::Action(void) {
+ int max = 0;
+
+#if VDRVERSNUM < 10300
+ isyslog("Streamdev: %s thread started (pid=%d)", m_Name, getpid());
+#endif
+
+ m_Active = true;
+ while (m_Active) {
+ int recvd;
+ const uchar *block = m_RingBuffer->Get(recvd);
+
+ if (block && recvd > 0) {
+ int result = 0;
+ uchar *sendBlock = Process(block, recvd, result);
+
+ m_RingBuffer->Del(recvd);
+ if (result > max) max = result;
+
+ if (!m_Socket->TimedWrite(sendBlock, result, 150)) {
+ if (errno != ETIMEDOUT) {
+ esyslog("ERROR: Streamdev: Couldn't write data: %s", strerror(errno));
+ m_Active = false;
+ }
+ }
+ } else
+ usleep(1); // this keeps the CPU load low (XXX: waiting buffers)
+ }
+
+ Dprintf("Max. Transmit Blocksize was: %d\n", max);
+
+#if VDRVERSNUM < 10300
+ isyslog("Streamdev: %s thread stopped", m_Name);
+#endif
+}
+
diff --git a/server/streamer.h b/server/streamer.h
new file mode 100644
index 0000000..1bfcc47
--- /dev/null
+++ b/server/streamer.h
@@ -0,0 +1,43 @@
+/*
+ * $Id: streamer.h,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_STREAMER_H
+#define VDR_STREAMDEV_STREAMER_H
+
+#include <vdr/thread.h>
+#include <vdr/ringbuffer.h>
+#include <vdr/tools.h>
+
+class cTBSocket;
+
+class cStreamdevStreamer: public cThread {
+private:
+ bool m_Active;
+ int m_Receivers;
+ uchar *m_Buffer;
+ const char *m_Name;
+ cTBSocket *m_Socket;
+ cRingBufferLinear *m_RingBuffer;
+
+protected:
+ virtual uchar *Process(const uchar *Data, int &Count, int &Result);
+ virtual void Action(void);
+
+ const cTBSocket *Socket(void) const { return m_Socket; }
+
+public:
+ cStreamdevStreamer(const char *Name);
+ virtual ~cStreamdevStreamer();
+
+ virtual void Start(cTBSocket *Socket);
+ virtual void Stop(void);
+
+ int Put(uchar *Data, int Length) { return m_RingBuffer->Put(Data, Length); }
+
+ virtual void Detach(void) = 0;
+ virtual void Attach(void) = 0;
+};
+
+#endif // VDR_STREAMDEV_STREAMER_H
+
diff --git a/server/suspend.c b/server/suspend.c
new file mode 100644
index 0000000..8f6da38
--- /dev/null
+++ b/server/suspend.c
@@ -0,0 +1,69 @@
+/*
+ * $Id: suspend.c,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
+ */
+
+#include "server/suspend.h"
+#include "server/suspend.dat"
+#include "common.h"
+
+cSuspendLive::cSuspendLive(void)
+#if VDRVERSNUM >= 10300
+ : cThread("Streamdev: server suspend")
+#endif
+{
+}
+
+cSuspendLive::~cSuspendLive() {
+ Detach();
+}
+
+void cSuspendLive::Activate(bool On) {
+ Dprintf("Activate cSuspendLive %d\n", On);
+ if (On)
+ Start();
+ else
+ Stop();
+}
+
+void cSuspendLive::Stop(void) {
+ if (m_Active) {
+ m_Active = false;
+ Cancel(3);
+ }
+}
+
+void cSuspendLive::Action(void) {
+#if VDRVERSNUM < 10300
+ isyslog("Streamdev: Suspend Live thread started (pid = %d)", getpid());
+#endif
+
+ m_Active = true;
+ while (m_Active) {
+ DeviceStillPicture(suspend_mpg, sizeof(suspend_mpg));
+ usleep(100000);
+ }
+
+#if VDRVERSNUM < 10300
+ isyslog("Streamdev: Suspend Live thread stopped");
+#endif
+}
+
+bool cSuspendCtl::m_Active = false;
+
+cSuspendCtl::cSuspendCtl(void):
+ cControl(m_Suspend = new cSuspendLive) {
+ m_Active = true;
+}
+
+cSuspendCtl::~cSuspendCtl() {
+ m_Active = false;
+ DELETENULL(m_Suspend);
+}
+
+eOSState cSuspendCtl::ProcessKey(eKeys Key) {
+ if (!m_Suspend->IsActive() || Key == kBack) {
+ DELETENULL(m_Suspend);
+ return osEnd;
+ }
+ return osContinue;
+}
diff --git a/server/suspend.dat b/server/suspend.dat
new file mode 100644
index 0000000..7b1b890
--- /dev/null
+++ b/server/suspend.dat
@@ -0,0 +1,1206 @@
+const unsigned char suspend_mpg[] = {
+ 0x00, 0x00, 0x01, 0xb3, 0x2d, 0x02, 0x40, 0x83, 0x02, 0xd0, 0x20, 0xa0,
+ 0x00, 0x00, 0x01, 0xb2, 0x4d, 0x50, 0x45, 0x47, 0x0a, 0x00, 0x00, 0x01,
+ 0xb8, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0a, 0xbb,
+ 0x58, 0x00, 0x00, 0x01, 0x01, 0x52, 0x97, 0xe6, 0x54, 0xa5, 0x2f, 0xdc,
+ 0xaf, 0x9a, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x45, 0xe2,
+ 0x2c, 0x52, 0x67, 0x98, 0x6c, 0x9a, 0x2e, 0xb9, 0x9e, 0xb4, 0x6c, 0xdd,
+ 0x34, 0x8c, 0x8b, 0xab, 0xa6, 0x61, 0xb5, 0xbd, 0x33, 0x8d, 0xa7, 0x22,
+ 0x6f, 0x75, 0x74, 0xcc, 0x36, 0xb4, 0x6c, 0xfd, 0x34, 0x64, 0x4d, 0xee,
+ 0x91, 0xb3, 0xf4, 0xd6, 0xf4, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b,
+ 0x37, 0x4d, 0x77, 0x4c, 0xdd, 0x34, 0xe4, 0x43, 0xdd, 0x43, 0x66, 0xe9,
+ 0xad, 0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x4f, 0x4c, 0xe3, 0x6b, 0x46,
+ 0xce, 0x36, 0x8c, 0x89, 0xbd, 0xd3, 0xd3, 0x3f, 0x4d, 0x68, 0xd9, 0x86,
+ 0xd3, 0x91, 0x37, 0xba, 0x86, 0xcc, 0x36, 0xbb, 0xa6, 0x6e, 0x9a, 0x32,
+ 0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xbd, 0x33, 0xf4, 0xd1, 0x91, 0x37,
+ 0xba, 0x46, 0xcf, 0xd3, 0x5a, 0x36, 0x7e, 0x9a, 0x72, 0x26, 0xf7, 0x4f,
+ 0x4c, 0xfd, 0x35, 0xa3, 0x66, 0x1b, 0x46, 0x44, 0xde, 0xea, 0xe9, 0x9c,
+ 0x6d, 0x67, 0x4c, 0xfd, 0x34, 0x64, 0x43, 0xdd, 0x43, 0x66, 0xe9, 0xad,
+ 0xe9, 0x9c, 0x6d, 0x39, 0x13, 0x7b, 0xa7, 0xa6, 0x7e, 0x9a, 0xd1, 0xb3,
+ 0x0d, 0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcd, 0xd3, 0x5c, 0x36, 0x61, 0xb4,
+ 0x0c, 0x9b, 0xdd, 0x43, 0x66, 0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x72, 0x26,
+ 0xf7, 0x50, 0xd9, 0xba, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea,
+ 0x1b, 0x30, 0xda, 0xd1, 0xb3, 0x8d, 0xa3, 0x22, 0x6f, 0x74, 0x8d, 0x9c,
+ 0x6d, 0x68, 0xd9, 0xba, 0x69, 0xc8, 0x87, 0xba, 0xba, 0x66, 0x1b, 0x5c,
+ 0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xdd, 0x35, 0xdd, 0x33,
+ 0x74, 0xd1, 0x91, 0x37, 0xba, 0x9e, 0x6e, 0x9a, 0xde, 0x99, 0xfa, 0x68,
+ 0xc8, 0x9b, 0xdd, 0x23, 0x67, 0xe9, 0xad, 0x79, 0xc6, 0xd3, 0x91, 0x37,
+ 0xba, 0x7a, 0x67, 0xe9, 0xad, 0x1b, 0x30, 0xda, 0x32, 0x26, 0xf7, 0x50,
+ 0xd9, 0xba, 0x6b, 0x7a, 0x67, 0x1b, 0x46, 0x44, 0x3d, 0xd4, 0x36, 0x61,
+ 0xb5, 0xbd, 0x33, 0xbd, 0x39, 0x13, 0x7b, 0xa7, 0xa6, 0x71, 0xb5, 0xbf,
+ 0xcc, 0x36, 0x8c, 0x89, 0xbd, 0xd4, 0xf3, 0x0d, 0xae, 0xe9, 0x9b, 0xa6,
+ 0x9c, 0x89, 0xbd, 0xd4, 0xf3, 0x74, 0xd7, 0x74, 0xcc, 0x36, 0x8c, 0x89,
+ 0xbd, 0xd2, 0x36, 0x7e, 0x9a, 0xd1, 0xb3, 0x0d, 0xa3, 0x22, 0x6f, 0x75,
+ 0x74, 0xcf, 0xd3, 0x5b, 0xd3, 0x37, 0x4d, 0x19, 0x10, 0xf7, 0x53, 0xcc,
+ 0x36, 0xb7, 0xa6, 0x71, 0xb4, 0xe4, 0x4d, 0xee, 0x91, 0xb3, 0xf4, 0xd6,
+ 0x8d, 0x98, 0x6d, 0x03, 0x26, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x2b, 0xf9,
+ 0x95, 0x29, 0x4b, 0xf7, 0x2b, 0xe6, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x45, 0xe2, 0x2c, 0x52, 0x71, 0xb3, 0x74, 0xc9, 0xa2,
+ 0xeb, 0x79, 0x86, 0xd6, 0xf4, 0xcc, 0x36, 0x9c, 0x88, 0xba, 0xba, 0x66,
+ 0xe9, 0xad, 0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x50, 0xd9, 0xba, 0x6b,
+ 0x7a, 0x67, 0xe9, 0xa7, 0x22, 0x6f, 0x74, 0x8d, 0x9f, 0xa6, 0xb4, 0x6c,
+ 0xc3, 0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x0d, 0xae, 0x1b, 0x37, 0x4d,
+ 0x19, 0x13, 0x7b, 0xab, 0xa6, 0x61, 0xb5, 0xc3, 0x66, 0xe9, 0xa3, 0x22,
+ 0x1e, 0xea, 0x1b, 0x30, 0xda, 0xde, 0x99, 0xfa, 0x69, 0xc8, 0x9b, 0xdd,
+ 0x23, 0x67, 0x1b, 0x5b, 0xd3, 0x37, 0x4d, 0x19, 0x13, 0x7b, 0xa8, 0x6c,
+ 0xc3, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0xe9, 0x9b, 0xa6,
+ 0xb7, 0xa6, 0x7e, 0x9a, 0x72, 0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xa3,
+ 0x67, 0xe9, 0xa3, 0x22, 0x6f, 0x74, 0xf4, 0xce, 0x36, 0xb7, 0xa6, 0x67,
+ 0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0xf5, 0xc3, 0x66, 0x1b, 0x4e, 0x44,
+ 0xde, 0xea, 0xe9, 0x9b, 0xa6, 0xb4, 0x6c, 0xfd, 0x34, 0x0c, 0x87, 0xba,
+ 0x46, 0xcf, 0xd3, 0x5a, 0x36, 0x61, 0xb4, 0x64, 0x4d, 0xee, 0xa1, 0xb3,
+ 0xf4, 0xd6, 0x8d, 0x9b, 0xa6, 0x9c, 0x89, 0xbd, 0xd5, 0xd3, 0x30, 0xda,
+ 0xee, 0x99, 0x86, 0xd1, 0x91, 0x37, 0xba, 0x86, 0xcc, 0x36, 0xb8, 0x6c,
+ 0xc3, 0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x74, 0xd6, 0x8d, 0x9c, 0x6d,
+ 0x39, 0x13, 0x7b, 0xa4, 0x6c, 0xe3, 0x6b, 0x7a, 0x66, 0xe9, 0xa3, 0x22,
+ 0x6f, 0x75, 0x3c, 0xdd, 0x35, 0xdd, 0x33, 0x0d, 0xa3, 0x22, 0x1e, 0xea,
+ 0xe9, 0x9b, 0xa6, 0xb8, 0x6c, 0xdd, 0x34, 0xe4, 0x4d, 0xee, 0xa1, 0xb3,
+ 0x0d, 0xad, 0x1b, 0x3f, 0x4d, 0x19, 0x13, 0x7b, 0xa4, 0x6c, 0xe3, 0x6b,
+ 0x7a, 0x67, 0x1b, 0x46, 0x44, 0xde, 0xe9, 0x1b, 0x38, 0xda, 0xd1, 0xb3,
+ 0x3d, 0x39, 0x13, 0x7b, 0xab, 0xa6, 0x61, 0xb5, 0xdd, 0x33, 0x74, 0xd1,
+ 0x91, 0x37, 0xba, 0x5e, 0x7e, 0x9a, 0xee, 0x99, 0x9e, 0x8c, 0x88, 0x7b,
+ 0xa4, 0x6c, 0xfd, 0x35, 0xbf, 0xcc, 0x36, 0x9c, 0x89, 0xbd, 0xd5, 0xd3,
+ 0x33, 0xd7, 0x0d, 0x9b, 0xa6, 0x8c, 0x89, 0xbd, 0xd5, 0xd3, 0x37, 0x4d,
+ 0x68, 0xd9, 0xc6, 0xd1, 0x91, 0x37, 0xba, 0xba, 0x66, 0x1b, 0x5b, 0xd3,
+ 0x33, 0xd3, 0x91, 0x37, 0xba, 0x86, 0xce, 0x36, 0xb7, 0xa6, 0x61, 0xb4,
+ 0x64, 0x4d, 0xee, 0xa1, 0xb3, 0x74, 0xd7, 0x0d, 0x98, 0x6d, 0x19, 0x13,
+ 0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xd1, 0xb3, 0xf4, 0xd3, 0x91, 0x0f, 0x74,
+ 0x8d, 0x9f, 0xa6, 0xb4, 0x6c, 0xe3, 0x68, 0xc8, 0x9b, 0xdd, 0x23, 0x66,
+ 0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xc3, 0x6b,
+ 0xba, 0x66, 0x1b, 0x4e, 0x44, 0xde, 0x00, 0x00, 0x01, 0x03, 0x2b, 0xf9,
+ 0x95, 0x29, 0x4b, 0xf7, 0x2b, 0xe6, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x28, 0xf3, 0x3c, 0x9b, 0xf8, 0xb5, 0xa5, 0x67, 0x4c,
+ 0xe3, 0x64, 0xd1, 0x75, 0x3c, 0xc3, 0x6b, 0x7a, 0x66, 0x1b, 0x4e, 0x44,
+ 0x3d, 0xd5, 0xd3, 0x30, 0xda, 0xe1, 0xb3, 0x74, 0xd1, 0x91, 0x37, 0xba,
+ 0x86, 0xcc, 0x36, 0xb4, 0x6c, 0xfd, 0x34, 0x64, 0x4d, 0xee, 0x9e, 0x99,
+ 0xc6, 0xd6, 0x8d, 0x9c, 0x6d, 0x38, 0xa9, 0xbd, 0xd2, 0x36, 0x71, 0xb5,
+ 0xbd, 0x33, 0x0d, 0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0x36, 0xbb, 0xa6,
+ 0x6e, 0x9a, 0x31, 0x53, 0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xd1, 0xb3, 0x8d,
+ 0xa7, 0x15, 0x37, 0xba, 0xba, 0x66, 0x1b, 0x5a, 0x36, 0x6e, 0x9a, 0x06,
+ 0x43, 0xdd, 0x5d, 0x33, 0x0d, 0xae, 0x1b, 0x30, 0xda, 0x72, 0x26, 0xf7,
+ 0x57, 0x4c, 0xc3, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b,
+ 0x37, 0x4d, 0x6f, 0x4c, 0xe3, 0x68, 0x19, 0x37, 0xba, 0x86, 0xcd, 0xd3,
+ 0x5a, 0x36, 0x61, 0xb4, 0xe4, 0x4d, 0xee, 0xae, 0x99, 0x86, 0xd7, 0x74,
+ 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, 0x30, 0xda, 0xde, 0x99, 0xfa,
+ 0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x0d, 0xad, 0x1b, 0x3f, 0x4d, 0x23,
+ 0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xbd, 0x33, 0x74, 0xd1, 0x91, 0x0f,
+ 0x75, 0x0d, 0x9f, 0xa6, 0xb7, 0xa6, 0x71, 0xb4, 0x64, 0x4d, 0xee, 0x9e,
+ 0x99, 0xba, 0x6b, 0x86, 0xcd, 0xd3, 0x4e, 0x44, 0xde, 0xea, 0x1b, 0x37,
+ 0x4d, 0x6f, 0x4c, 0xfd, 0x34, 0x64, 0x4d, 0xee, 0xa1, 0xb3, 0x0d, 0xad,
+ 0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x4f, 0x4c, 0xe3, 0x6b, 0x46, 0xcd,
+ 0xd3, 0x4e, 0x44, 0xde, 0xea, 0xe9, 0x98, 0x6d, 0x70, 0xd9, 0xba, 0x68,
+ 0xc8, 0x87, 0xba, 0x86, 0xcd, 0xd3, 0x5d, 0xd3, 0x30, 0xda, 0x32, 0x26,
+ 0xf7, 0x57, 0x4c, 0xdd, 0x35, 0xa3, 0x67, 0x1b, 0x4e, 0x44, 0xde, 0xe9,
+ 0x1b, 0x38, 0xda, 0xde, 0x99, 0x86, 0xd1, 0x91, 0x37, 0xba, 0xba, 0x66,
+ 0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xdd, 0x35,
+ 0xc3, 0x66, 0xe9, 0xa7, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0x36, 0xb5, 0xe6,
+ 0xe9, 0xa3, 0x22, 0x1e, 0xea, 0xe9, 0x99, 0xeb, 0xba, 0x66, 0xe9, 0xa3,
+ 0x22, 0x6f, 0x75, 0x0d, 0x98, 0x6d, 0x70, 0xd9, 0x86, 0xd3, 0x91, 0x37,
+ 0xba, 0x86, 0xcd, 0xd3, 0x5a, 0x36, 0x61, 0xb4, 0x64, 0x4e, 0xea, 0xe9,
+ 0x9b, 0xa6, 0xbb, 0xa6, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x53, 0xcc, 0x36,
+ 0xbb, 0xa6, 0x6e, 0x9a, 0x72, 0x26, 0xf7, 0x57, 0x4c, 0xc3, 0x6b, 0x46,
+ 0xcf, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, 0x37, 0x4d, 0x6f, 0x4c, 0xfd,
+ 0x34, 0x64, 0x43, 0xdd, 0x23, 0x67, 0xe9, 0xad, 0xe9, 0x98, 0x6d, 0x39,
+ 0x13, 0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xe1, 0xb3, 0x74, 0xd1, 0x91, 0x37,
+ 0xba, 0xba, 0x66, 0xe9, 0xad, 0x1b, 0x3f, 0x4d, 0x19, 0x13, 0x7b, 0xa4,
+ 0x6c, 0xfd, 0x35, 0xa3, 0x67, 0x1b, 0x4e, 0x44, 0xde, 0xe9, 0xe9, 0x9c,
+ 0x6d, 0x6f, 0x4c, 0xdd, 0x34, 0x64, 0x4d, 0xe0, 0x00, 0x00, 0x01, 0x04,
+ 0x2b, 0xf9, 0x95, 0x22, 0xf3, 0x74, 0xdf, 0xb8, 0xb5, 0xf3, 0x56, 0x91,
+ 0x7a, 0xce, 0x99, 0xc6, 0xc8, 0x64, 0x5d, 0x23, 0x67, 0xe9, 0xad, 0xe9,
+ 0x9b, 0xa6, 0x9c, 0x89, 0xdd, 0xe0, 0x19, 0xf4, 0x80, 0x54, 0x5f, 0x41,
+ 0x2c, 0x20, 0x61, 0x7f, 0x14, 0x4b, 0x25, 0xa5, 0x05, 0x27, 0x25, 0x25,
+ 0x20, 0x95, 0xd0, 0x58, 0xd2, 0xd1, 0xba, 0x33, 0x73, 0x4d, 0xb1, 0xe0,
+ 0x0c, 0x00, 0x60, 0x1a, 0x8d, 0xf2, 0x40, 0xaf, 0xc9, 0xcf, 0xbe, 0x01,
+ 0x88, 0x0e, 0xce, 0xe6, 0xe0, 0x0a, 0x6d, 0x1d, 0x32, 0x00, 0x36, 0xf8,
+ 0xa2, 0x62, 0x40, 0x74, 0xb0, 0x42, 0x08, 0x49, 0x29, 0x0f, 0xbe, 0x00,
+ 0xa3, 0xe1, 0xa1, 0x8c, 0xff, 0xff, 0x90, 0x8e, 0x06, 0x06, 0xba, 0xd2,
+ 0x57, 0xbe, 0x6c, 0x01, 0xa2, 0x51, 0xd0, 0x58, 0x67, 0xdb, 0xfd, 0xc9,
+ 0x79, 0xf6, 0xea, 0x3b, 0x91, 0x2f, 0x54, 0x03, 0x1c, 0x9d, 0x90, 0x5a,
+ 0x3f, 0xc6, 0x75, 0xde, 0xc0, 0x13, 0x10, 0xd2, 0x94, 0x16, 0x50, 0x6f,
+ 0x1a, 0x1a, 0x05, 0x0b, 0x49, 0x30, 0x37, 0xb3, 0x3f, 0x43, 0x74, 0x24,
+ 0x90, 0x84, 0x01, 0x72, 0xba, 0x0b, 0xe3, 0x30, 0xcd, 0xd2, 0xc6, 0x59,
+ 0x7a, 0x6d, 0x20, 0x21, 0xc0, 0x07, 0xc0, 0x19, 0xe4, 0xac, 0x60, 0x09,
+ 0xca, 0x18, 0x49, 0xed, 0x89, 0xa4, 0x27, 0xea, 0x43, 0x30, 0xd2, 0xc3,
+ 0x3f, 0x7e, 0xa5, 0x13, 0x31, 0xf0, 0x7b, 0xbc, 0x03, 0x10, 0x29, 0x88,
+ 0x40, 0x55, 0x39, 0x19, 0x90, 0x90, 0x94, 0x08, 0xe4, 0x0f, 0x72, 0x00,
+ 0xe8, 0x34, 0x0c, 0x86, 0x23, 0x9a, 0x77, 0xba, 0xba, 0x66, 0xe9, 0xad,
+ 0xe9, 0x9f, 0xa6, 0xec, 0xc8, 0xb9, 0xde, 0xe9, 0xe9, 0xb4, 0x13, 0x12,
+ 0x1a, 0x02, 0x14, 0x96, 0x80, 0x28, 0xdb, 0x06, 0x27, 0x20, 0xb2, 0x19,
+ 0x78, 0x68, 0x66, 0xfb, 0xf2, 0x59, 0x45, 0x6c, 0x94, 0x24, 0xb2, 0xd0,
+ 0xcc, 0x96, 0xf9, 0x08, 0xfd, 0x69, 0xe9, 0x47, 0x45, 0x79, 0x0d, 0x25,
+ 0xfc, 0x82, 0x83, 0x3e, 0xc1, 0xa4, 0xd2, 0xd0, 0x18, 0x57, 0x7d, 0xbe,
+ 0xe9, 0xeb, 0x2d, 0x19, 0x3b, 0xe4, 0xf0, 0xc2, 0x86, 0x86, 0x8c, 0x6e,
+ 0xf9, 0x1e, 0xf2, 0xc8, 0x41, 0x40, 0x3a, 0x0c, 0xdb, 0x29, 0x05, 0x63,
+ 0x7b, 0x87, 0xff, 0xd7, 0xff, 0x37, 0xf5, 0xfe, 0xbf, 0x78, 0x80, 0x0f,
+ 0x80, 0x34, 0x40, 0x6e, 0x03, 0x25, 0xa0, 0x6e, 0x76, 0x46, 0x1f, 0xf1,
+ 0xbc, 0x7e, 0xbd, 0x10, 0x10, 0x80, 0xef, 0x86, 0x32, 0x10, 0x7f, 0x27,
+ 0x55, 0x3d, 0x0c, 0x02, 0x02, 0x94, 0x35, 0x21, 0xa5, 0xf6, 0x67, 0xe5,
+ 0xa5, 0x0e, 0x02, 0x20, 0xce, 0x52, 0x1c, 0x5b, 0x55, 0x80, 0x1d, 0x13,
+ 0x07, 0x16, 0x09, 0x3f, 0x81, 0xa0, 0x9b, 0xfb, 0x64, 0x09, 0x4a, 0xdc,
+ 0x65, 0xeb, 0x50, 0x03, 0xb0, 0x1d, 0x97, 0x9c, 0x96, 0x56, 0x5a, 0x73,
+ 0x12, 0xbf, 0x5a, 0x3f, 0xdd, 0x60, 0x65, 0x6b, 0xc7, 0xaa, 0xf0, 0x53,
+ 0x8a, 0x2b, 0x3a, 0x7a, 0x36, 0x38, 0xe2, 0x05, 0xe9, 0xa0, 0x96, 0x5f,
+ 0x57, 0xe1, 0x75, 0x9f, 0x9d, 0x01, 0xb2, 0x05, 0x80, 0x46, 0x92, 0x0d,
+ 0x6f, 0x21, 0x15, 0xc0, 0x98, 0x24, 0xfe, 0x96, 0x00, 0x31, 0xbb, 0x24,
+ 0xb0, 0xd0, 0x26, 0x80, 0x08, 0xb8, 0x00, 0xc6, 0x80, 0x64, 0x37, 0x1a,
+ 0x8e, 0x1d, 0xf1, 0x17, 0xdb, 0x0d, 0x28, 0x7e, 0x0a, 0xb1, 0x18, 0x9b,
+ 0xce, 0x1a, 0x05, 0x06, 0xaf, 0x06, 0x96, 0x80, 0xa6, 0xaf, 0x46, 0x42,
+ 0x3e, 0x41, 0x7b, 0xef, 0xce, 0xe3, 0x52, 0xdc, 0x9e, 0x78, 0xab, 0x22,
+ 0x08, 0x60, 0x12, 0xe6, 0x17, 0xbb, 0xe2, 0x25, 0xc5, 0x2c, 0xac, 0x5a,
+ 0x3e, 0xeb, 0xde, 0xe6, 0xcc, 0x29, 0xae, 0x8e, 0xf8, 0xfb, 0xd2, 0x03,
+ 0x62, 0x85, 0x80, 0x46, 0x92, 0x0d, 0x00, 0x6c, 0x50, 0xee, 0x01, 0x1a,
+ 0x48, 0x37, 0xad, 0x1a, 0x43, 0x43, 0xa0, 0x97, 0x94, 0x3d, 0x0b, 0xf8,
+ 0xce, 0x49, 0x02, 0x34, 0x25, 0x93, 0x40, 0x25, 0x40, 0x04, 0x5c, 0x00,
+ 0x63, 0x69, 0xc6, 0x63, 0x70, 0x55, 0x8b, 0x26, 0xfc, 0x98, 0x0d, 0x8a,
+ 0x1d, 0xc0, 0x23, 0x49, 0x06, 0x80, 0x36, 0x28, 0x77, 0x00, 0x8d, 0x24,
+ 0x1b, 0xd7, 0x92, 0xc8, 0x60, 0x12, 0xa0, 0x02, 0x2e, 0x00, 0x31, 0x89,
+ 0x2c, 0x86, 0x01, 0x2a, 0x00, 0x22, 0x00, 0x17, 0xd4, 0xe5, 0x0f, 0xc1,
+ 0x54, 0x8c, 0x4d, 0xf9, 0x40, 0x0b, 0x0a, 0x30, 0x6e, 0x01, 0x1f, 0xe4,
+ 0x7f, 0xf5, 0xd9, 0x2c, 0x31, 0x25, 0x2f, 0x04, 0xe0, 0x08, 0x44, 0xa9,
+ 0x5c, 0x39, 0xab, 0xb1, 0x0c, 0x35, 0x07, 0xb2, 0x44, 0x2b, 0x91, 0xfd,
+ 0xe3, 0xd0, 0x9c, 0x5e, 0xdf, 0x1b, 0x0c, 0x8e, 0x3b, 0xd6, 0x12, 0x8a,
+ 0x9f, 0xdd, 0xe2, 0xf2, 0x76, 0xfb, 0xef, 0xbe, 0x7d, 0xf2, 0xf7, 0xdc,
+ 0xdd, 0xe2, 0xf5, 0x00, 0x1a, 0x86, 0x00, 0x1f, 0x00, 0xef, 0xa3, 0x12,
+ 0x03, 0x77, 0x0d, 0x28, 0x53, 0x7d, 0x8b, 0x46, 0x51, 0xdb, 0xfd, 0xf3,
+ 0xf0, 0x3e, 0xd9, 0x5a, 0xdc, 0xf7, 0xb6, 0x1a, 0x18, 0x06, 0x13, 0xf0,
+ 0x50, 0x8b, 0xc9, 0x28, 0xb2, 0x5a, 0x37, 0xba, 0x9e, 0x77, 0xad, 0x79,
+ 0x9e, 0xd1, 0xa8, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4,
+ 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x77,
+ 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x2d, 0x17, 0x4b, 0xce,
+ 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86,
+ 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb,
+ 0x5e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b,
+ 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc,
+ 0xef, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7,
+ 0x99, 0xeb, 0x9e, 0x67, 0xa5, 0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d,
+ 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x33,
+ 0xd7, 0x3c, 0xcf, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a, 0x1a,
+ 0x2e, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a,
+ 0xf3, 0x3d, 0x2d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd,
+ 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7,
+ 0x7a, 0x5a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd1, 0x75, 0x3c,
+ 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9,
+ 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9, 0x79, 0xde,
+ 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3,
+ 0xba, 0x9e, 0x67, 0xac, 0x8d, 0x2d, 0x16, 0xaf, 0x17, 0x8d, 0x29, 0x9c,
+ 0x00, 0x00, 0x01, 0x05, 0x3b, 0xf9, 0xd3, 0xce, 0xf5, 0xaf, 0x33, 0xdf,
+ 0xb5, 0x35, 0xf3, 0x97, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd1, 0x75,
+ 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa9, 0xe7, 0x28, 0xb0, 0x1b,
+ 0x00, 0xec, 0x31, 0x26, 0x06, 0xb9, 0x31, 0x21, 0x06, 0x9e, 0x57, 0xe9,
+ 0x1d, 0xbb, 0xa3, 0x61, 0xe1, 0xb5, 0x8f, 0x68, 0x26, 0xee, 0x59, 0x49,
+ 0xdb, 0xed, 0xc0, 0xf7, 0xf9, 0xc6, 0x27, 0x2d, 0x09, 0x61, 0xe8, 0xed,
+ 0xf0, 0xbd, 0x71, 0x01, 0x44, 0x81, 0x80, 0xd4, 0x24, 0xc3, 0xb8, 0x8f,
+ 0x6e, 0x1a, 0x82, 0x5a, 0x3e, 0xab, 0x78, 0x80, 0x3b, 0x47, 0x48, 0x67,
+ 0xf9, 0x7d, 0x01, 0x38, 0xc7, 0x63, 0x03, 0xf3, 0xe0, 0xfb, 0xab, 0xbe,
+ 0xfb, 0x7d, 0xdf, 0x73, 0x6f, 0x3a, 0x52, 0x49, 0x68, 0xdc, 0x7e, 0x5e,
+ 0x1d, 0xcd, 0xc1, 0xdd, 0x57, 0xae, 0xc0, 0x5b, 0xa4, 0xa0, 0x25, 0xc5,
+ 0x12, 0x3a, 0xd0, 0x1c, 0xca, 0x0e, 0xf7, 0x8a, 0x43, 0x43, 0xa3, 0x71,
+ 0xc4, 0x6b, 0xd2, 0x26, 0x76, 0x7d, 0x53, 0xe3, 0x79, 0x9e, 0xb9, 0xeb,
+ 0x12, 0x9c, 0x8d, 0xff, 0xfb, 0x6c, 0xbf, 0xfa, 0xb6, 0xc3, 0xfd, 0xef,
+ 0x64, 0x3d, 0xe4, 0xbd, 0x6d, 0xce, 0x2b, 0x3a, 0x5c, 0x09, 0x62, 0x7f,
+ 0x20, 0x62, 0x3c, 0x84, 0x34, 0xb3, 0x6c, 0x69, 0x1b, 0x00, 0xfa, 0xe6,
+ 0x86, 0x66, 0x46, 0x73, 0xfa, 0xf8, 0x07, 0xc7, 0x5e, 0xb5, 0x3c, 0x6a,
+ 0x53, 0xc7, 0x2b, 0x11, 0x40, 0x3e, 0xbe, 0x47, 0x04, 0x6b, 0xdd, 0x3f,
+ 0xd4, 0xfe, 0x02, 0xfa, 0x09, 0x33, 0xa8, 0xb0, 0x47, 0xfb, 0x21, 0x82,
+ 0x5f, 0xd9, 0x77, 0xcf, 0xaf, 0xa3, 0x06, 0x70, 0x1c, 0x82, 0x3f, 0xd9,
+ 0x0c, 0x12, 0xfe, 0xd3, 0x74, 0x80, 0x9c, 0x10, 0xc1, 0x81, 0x23, 0xf7,
+ 0x11, 0xbd, 0x00, 0x30, 0x24, 0xf1, 0x17, 0x8d, 0xee, 0x70, 0x60, 0x05,
+ 0xc0, 0x21, 0x26, 0x95, 0x83, 0x71, 0x41, 0x85, 0x15, 0xf3, 0x80, 0x52,
+ 0x5a, 0x50, 0x5b, 0x0f, 0x19, 0x83, 0x0b, 0x65, 0x2f, 0x64, 0xf4, 0x73,
+ 0x3e, 0xd7, 0x7c, 0x9a, 0x9d, 0x8a, 0x2d, 0xd7, 0x86, 0x77, 0x10, 0xd7,
+ 0x9b, 0x21, 0x80, 0x27, 0x2c, 0xa4, 0x96, 0x05, 0x39, 0x34, 0x07, 0x41,
+ 0x84, 0xde, 0x4d, 0x6c, 0x94, 0xe2, 0xf9, 0x41, 0x85, 0x37, 0x7c, 0x5e,
+ 0x35, 0x39, 0x38, 0x61, 0xed, 0xdd, 0xec, 0x00, 0x1e, 0x00, 0x60, 0x80,
+ 0x18, 0xe2, 0xfa, 0x51, 0xd2, 0x30, 0xcc, 0x47, 0xb9, 0x08, 0x64, 0xd2,
+ 0x53, 0x2d, 0xab, 0x4e, 0xe7, 0x37, 0x7c, 0x1d, 0xc7, 0x72, 0x27, 0xb8,
+ 0x84, 0x24, 0x9e, 0x81, 0xc7, 0x08, 0x1c, 0x1c, 0x40, 0xaf, 0x21, 0xfe,
+ 0x94, 0x10, 0x8b, 0x3b, 0xa7, 0x37, 0x0a, 0x7b, 0xa0, 0x19, 0xd8, 0x35,
+ 0x02, 0xdf, 0x13, 0x9c, 0x89, 0x57, 0x41, 0x29, 0x2c, 0x67, 0xb5, 0xeb,
+ 0xd5, 0x47, 0x28, 0x01, 0xe9, 0x4d, 0x83, 0x09, 0xb8, 0x0f, 0x24, 0x94,
+ 0x93, 0xc9, 0x05, 0xf6, 0xc1, 0x45, 0xb1, 0xe7, 0x6f, 0xee, 0xfb, 0xde,
+ 0x34, 0x03, 0x32, 0x80, 0xc8, 0x0c, 0x08, 0x68, 0xca, 0xe4, 0xa4, 0xb7,
+ 0xc7, 0xef, 0xb3, 0xef, 0x83, 0xef, 0x6f, 0xb2, 0x53, 0xb7, 0xc8, 0x4f,
+ 0xdf, 0x3e, 0x57, 0x36, 0xe9, 0x02, 0xa5, 0x38, 0xc4, 0xe1, 0x1c, 0x3a,
+ 0x60, 0x51, 0x2a, 0xfb, 0xda, 0x3c, 0xef, 0x02, 0x51, 0x60, 0x49, 0x21,
+ 0x5e, 0x7e, 0x50, 0x6a, 0xc0, 0xb1, 0x7d, 0x87, 0x21, 0x3f, 0x85, 0x6d,
+ 0xff, 0xbb, 0xc9, 0x49, 0x1d, 0xee, 0x21, 0xb8, 0x7e, 0xb8, 0xf7, 0x97,
+ 0x00, 0x70, 0xe0, 0x60, 0x9a, 0xee, 0x08, 0x7f, 0x6a, 0x70, 0x47, 0x04,
+ 0x28, 0xe1, 0x44, 0x57, 0xbd, 0x08, 0x60, 0x09, 0x80, 0x76, 0x43, 0x61,
+ 0x85, 0x72, 0xb7, 0x3d, 0x21, 0xbb, 0xa1, 0x81, 0x20, 0x0f, 0xb1, 0x7f,
+ 0x8a, 0x6d, 0x85, 0xeb, 0xc2, 0x10, 0x88, 0x48, 0x02, 0x81, 0x88, 0x0c,
+ 0x41, 0xd8, 0xd1, 0xff, 0xf7, 0xff, 0xb3, 0x67, 0xfd, 0x7e, 0xf3, 0x08,
+ 0x41, 0x9d, 0x0f, 0xfa, 0x9f, 0x33, 0x5e, 0x81, 0x0c, 0xbd, 0xf3, 0x65,
+ 0xdf, 0xa4, 0x00, 0x66, 0xa0, 0x03, 0xee, 0x00, 0x29, 0x4e, 0x13, 0xb9,
+ 0x49, 0xec, 0x17, 0xb7, 0x0e, 0xbd, 0x2b, 0xdc, 0xa0, 0x07, 0x64, 0xdc,
+ 0x1a, 0x43, 0x28, 0xac, 0xbc, 0x4c, 0xdd, 0x3c, 0xf2, 0x4a, 0x73, 0x61,
+ 0x44, 0x87, 0x71, 0x3a, 0xee, 0xbc, 0xc0, 0x77, 0x82, 0x76, 0x1c, 0x85,
+ 0xd8, 0x10, 0x94, 0x55, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf7, 0x6b, 0x5c,
+ 0xd7, 0x4b, 0xce, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad,
+ 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0x5d,
+ 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe7,
+ 0x7a, 0x1a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e,
+ 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4,
+ 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa5, 0xa2, 0xea, 0x79, 0x9e,
+ 0xb9, 0xe7, 0x7a, 0x1a, 0x77, 0x4b, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3,
+ 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d, 0xd2, 0xf3, 0x3d, 0x73,
+ 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x9d, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75,
+ 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x17, 0x4b, 0xce, 0xf5, 0xaf, 0x3b,
+ 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3,
+ 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1,
+ 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe7, 0x7a,
+ 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x45,
+ 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e,
+ 0x77, 0xa5, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9,
+ 0xe6, 0x7a, 0xca, 0x52, 0xd3, 0xb5, 0x79, 0xa9, 0x49, 0x45, 0xca, 0x52,
+ 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x00, 0x00, 0x00,
+ 0x01, 0x06, 0x43, 0xf1, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x7e, 0xc4, 0xd7,
+ 0xcf, 0x5d, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0xde,
+ 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd2, 0xd3,
+ 0xad, 0xf9, 0x04, 0xce, 0xdf, 0xe0, 0xfc, 0x45, 0x27, 0xac, 0x3e, 0xb4,
+ 0x9b, 0x99, 0x3b, 0x8f, 0x57, 0x23, 0xe2, 0x2d, 0xd3, 0x71, 0x9f, 0x71,
+ 0xc4, 0x63, 0x48, 0x21, 0xd7, 0x94, 0x0d, 0x40, 0xd4, 0x7c, 0x69, 0x82,
+ 0x71, 0xd7, 0xcd, 0x60, 0x9c, 0x4e, 0xbd, 0x9e, 0xaf, 0x54, 0x7a, 0x97,
+ 0xa1, 0x48, 0x00, 0xb4, 0x9a, 0x18, 0x18, 0x3f, 0x8d, 0x4a, 0xd8, 0x20,
+ 0xa6, 0xfc, 0x4e, 0xfd, 0x91, 0xf0, 0xe4, 0xda, 0xd0, 0x58, 0xd4, 0x64,
+ 0xe7, 0x5e, 0xdd, 0x67, 0xae, 0xf3, 0xe0, 0x0f, 0x8a, 0x72, 0x59, 0x34,
+ 0x7f, 0x25, 0x21, 0x6a, 0x64, 0x3b, 0xfd, 0x8c, 0xfd, 0x2f, 0xb8, 0xf1,
+ 0x9a, 0xf4, 0x40, 0xa0, 0x60, 0x17, 0x58, 0x1e, 0x58, 0x7d, 0xc4, 0x4d,
+ 0x2d, 0x8a, 0x13, 0x7c, 0xa0, 0x13, 0x93, 0x40, 0x2c, 0x21, 0x16, 0x9c,
+ 0x7b, 0x32, 0x30, 0xf4, 0xf5, 0x6c, 0xb1, 0xfe, 0xf7, 0x28, 0xc4, 0xa0,
+ 0xcc, 0x83, 0xfe, 0xcb, 0xec, 0xcf, 0xcd, 0xec, 0x2f, 0xaf, 0xdc, 0x80,
+ 0x07, 0xa1, 0xa4, 0xb2, 0x68, 0x61, 0x33, 0x1c, 0x80, 0x14, 0xa5, 0x4e,
+ 0x29, 0x18, 0x52, 0xc2, 0x77, 0x01, 0xe5, 0xd8, 0xfe, 0xf3, 0xc0, 0xaf,
+ 0x59, 0x45, 0x00, 0x8f, 0x89, 0xf7, 0x41, 0x31, 0x0c, 0x57, 0xe3, 0xef,
+ 0xe6, 0x97, 0xd3, 0x11, 0xc0, 0x72, 0x08, 0xff, 0x84, 0xd0, 0x4b, 0xfc,
+ 0x4d, 0xf3, 0x4b, 0xea, 0x3b, 0xac, 0xb0, 0x47, 0xfc, 0x26, 0x82, 0x5f,
+ 0xe2, 0x68, 0x02, 0xab, 0x0c, 0x01, 0x1e, 0x23, 0x7b, 0x60, 0x1d, 0x12,
+ 0x78, 0x8b, 0xcf, 0xca, 0x01, 0xbe, 0x4a, 0x3b, 0xe4, 0x0c, 0x4f, 0x0d,
+ 0x4e, 0x01, 0x21, 0x5b, 0x71, 0xa9, 0x16, 0xd8, 0xec, 0xcb, 0xff, 0x6b,
+ 0xe9, 0xc0, 0x54, 0x01, 0xb9, 0x41, 0xa9, 0x0d, 0x0d, 0xc4, 0xc0, 0x2e,
+ 0x92, 0xd3, 0xf3, 0x99, 0xbf, 0xff, 0x28, 0xe7, 0x61, 0xd7, 0x89, 0x00,
+ 0x6e, 0x05, 0x4b, 0x00, 0xd0, 0x01, 0xd0, 0x40, 0x42, 0x40, 0x35, 0x4b,
+ 0x3e, 0x3c, 0x02, 0xc2, 0x97, 0x97, 0xbf, 0x24, 0xfe, 0xfb, 0x81, 0x24,
+ 0xdf, 0x6b, 0xbe, 0x60, 0x03, 0x04, 0x24, 0x06, 0x01, 0x8a, 0xff, 0x74,
+ 0xaf, 0xc1, 0x35, 0x8b, 0xeb, 0x32, 0xbc, 0xfc, 0xd6, 0x4c, 0xe0, 0x16,
+ 0x90, 0x80, 0x7a, 0x82, 0x21, 0x07, 0x5b, 0xca, 0x01, 0xca, 0x04, 0xf2,
+ 0x37, 0xbd, 0x80, 0x06, 0x45, 0xf0, 0x32, 0x30, 0x7e, 0x23, 0x5f, 0x1c,
+ 0x49, 0x4c, 0x1a, 0xe6, 0x33, 0x87, 0x9c, 0x46, 0xba, 0x13, 0x9d, 0x70,
+ 0x65, 0x63, 0x8d, 0x24, 0x8e, 0x23, 0xda, 0x03, 0x3e, 0xe5, 0x21, 0xbe,
+ 0x3b, 0x4c, 0x43, 0x48, 0xc0, 0xc2, 0xf8, 0xed, 0xef, 0xaa, 0x64, 0x6d,
+ 0x99, 0x6a, 0x1d, 0xfe, 0x22, 0xde, 0xab, 0xed, 0x96, 0xf6, 0xe8, 0x43,
+ 0xd5, 0x3f, 0x1c, 0x7c, 0x3f, 0x49, 0x7d, 0x98, 0x95, 0xb0, 0x1f, 0x0d,
+ 0x04, 0x9f, 0xb0, 0xd0, 0x01, 0x8d, 0xc2, 0x01, 0xd0, 0x05, 0xe5, 0x80,
+ 0xe9, 0x3b, 0x06, 0xfe, 0x59, 0x33, 0x9e, 0x9e, 0x49, 0x0c, 0x16, 0x2d,
+ 0x03, 0xcd, 0xc7, 0xae, 0xac, 0x92, 0x92, 0xad, 0xfe, 0xe2, 0xec, 0xd2,
+ 0x4c, 0xfb, 0x56, 0xfe, 0x09, 0x25, 0xa1, 0x07, 0xf6, 0xbd, 0x59, 0x60,
+ 0x55, 0xd4, 0x80, 0xbe, 0x03, 0x5b, 0xc9, 0xa5, 0x18, 0xbc, 0xde, 0xfa,
+ 0x71, 0x68, 0xe5, 0x76, 0xe1, 0xdc, 0x78, 0x0b, 0xe8, 0x76, 0xab, 0x35,
+ 0x53, 0xab, 0x29, 0xcd, 0xff, 0xbf, 0xfc, 0xff, 0xc7, 0xfa, 0x3d, 0x6b,
+ 0xcc, 0xf7, 0xab, 0xdf, 0xf7, 0xf7, 0x93, 0x75, 0x3c, 0xef, 0x5a, 0xf3,
+ 0x3c, 0x9a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd1, 0x75, 0x3c,
+ 0xcf, 0x5c, 0xf3, 0x3d, 0x2d, 0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x9d, 0xe8,
+ 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0xde,
+ 0xb1, 0xe7, 0x7a, 0x5a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3,
+ 0xba, 0x5e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x8b, 0xa9, 0xe7, 0x7a, 0xd7,
+ 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9,
+ 0x79, 0xde, 0xb5, 0xe7, 0x7a, 0x1a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33,
+ 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86, 0x9d, 0xd2, 0xf3,
+ 0xbd, 0x6b, 0xce, 0xf4, 0x34, 0x5d, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x4b,
+ 0x4e, 0xea, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5,
+ 0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d,
+ 0xd2, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x9e,
+ 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x2d, 0x17, 0x53,
+ 0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x78, 0xd2,
+ 0xd3, 0xba, 0xde, 0x67, 0x9e, 0x94, 0xce, 0xc4, 0x69, 0x49, 0x45, 0xca,
+ 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94,
+ 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x40, 0x00, 0x00,
+ 0x01, 0x07, 0x4b, 0xf3, 0x2f, 0x33, 0xd6, 0xbc, 0xef, 0x7e, 0xb2, 0xd7,
+ 0x83, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa5, 0xe7, 0x7a,
+ 0xd7, 0x9d, 0xe9, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e,
+ 0xd3, 0xb7, 0xdf, 0x76, 0xfb, 0xab, 0xee, 0x77, 0xbc, 0x80, 0x06, 0x41,
+ 0xa0, 0x64, 0x85, 0xb7, 0x3c, 0xae, 0x4a, 0xef, 0xf7, 0xea, 0x3b, 0xf6,
+ 0x35, 0x28, 0x6e, 0x1f, 0x7b, 0x47, 0xb9, 0x40, 0x19, 0x06, 0x92, 0x88,
+ 0x63, 0x3f, 0x50, 0x6e, 0x6d, 0x80, 0x7a, 0x87, 0xea, 0x12, 0x87, 0x71,
+ 0x1a, 0xf3, 0x40, 0xa7, 0x6f, 0xf0, 0xf2, 0x3e, 0xbb, 0x48, 0x68, 0x74,
+ 0x38, 0xea, 0xe3, 0x7a, 0x70, 0xc4, 0xfd, 0x87, 0xbe, 0x0f, 0x03, 0xb7,
+ 0x83, 0xd8, 0x62, 0x36, 0x1d, 0xad, 0xa9, 0x28, 0x69, 0x68, 0x48, 0xe7,
+ 0x0f, 0xb4, 0x90, 0xd2, 0xc8, 0x17, 0xd5, 0x79, 0x3b, 0x5e, 0x8b, 0xbb,
+ 0x55, 0x3e, 0xc3, 0x86, 0x80, 0xdc, 0xb2, 0xc3, 0x40, 0x25, 0x4f, 0x65,
+ 0x01, 0xec, 0xac, 0x27, 0x75, 0xa7, 0x09, 0x8b, 0xd7, 0xab, 0x66, 0xca,
+ 0xd8, 0xc8, 0x1f, 0x7a, 0xff, 0x92, 0xcb, 0x71, 0xfe, 0xf2, 0x19, 0x90,
+ 0xc2, 0xaf, 0xe4, 0xf7, 0xde, 0x40, 0x4e, 0x43, 0x0d, 0x03, 0x21, 0xa1,
+ 0x85, 0x27, 0x14, 0x12, 0x5f, 0x4a, 0x59, 0xd2, 0xb0, 0xd2, 0xd4, 0x83,
+ 0x31, 0xc7, 0xf1, 0xea, 0xbe, 0x0f, 0x7d, 0x30, 0x10, 0x46, 0x8b, 0x48,
+ 0x15, 0x26, 0x96, 0x94, 0x95, 0x90, 0x1b, 0xdd, 0x5d, 0x3d, 0x2e, 0x37,
+ 0xf5, 0xee, 0x8d, 0xb2, 0xbb, 0x2b, 0x99, 0x7c, 0xb0, 0x09, 0x80, 0x2c,
+ 0xe1, 0xb8, 0x02, 0xd2, 0x46, 0xef, 0x7b, 0xa3, 0x7a, 0x32, 0x58, 0xfa,
+ 0xf3, 0xd6, 0xb0, 0x0d, 0xd9, 0x09, 0xe0, 0x2a, 0xe3, 0x80, 0xe5, 0xf4,
+ 0x94, 0xa3, 0xa3, 0xfd, 0xcc, 0x55, 0xf0, 0xa0, 0x03, 0xce, 0x3f, 0x5c,
+ 0x20, 0x03, 0xc2, 0x11, 0xfd, 0x0f, 0xc4, 0xf6, 0xbe, 0x5c, 0xbe, 0x51,
+ 0x9e, 0xd3, 0x57, 0xff, 0x58, 0x01, 0xd7, 0x25, 0x7c, 0x5f, 0x17, 0xaf,
+ 0x90, 0xa0, 0x87, 0x8b, 0x4f, 0xc7, 0x0e, 0x89, 0x34, 0x98, 0x35, 0x86,
+ 0x66, 0xd8, 0xca, 0xdc, 0x4c, 0x28, 0x31, 0x69, 0x5f, 0xc1, 0x5a, 0xcc,
+ 0x0b, 0x74, 0x0b, 0xad, 0x6f, 0x54, 0xa0, 0x03, 0x72, 0x68, 0xc2, 0x90,
+ 0x19, 0xba, 0xcb, 0x4e, 0x70, 0x15, 0x21, 0x7c, 0x2c, 0xe0, 0xfb, 0xdb,
+ 0xbc, 0xa4, 0x32, 0x1f, 0x01, 0xd9, 0x37, 0x93, 0x7b, 0xe7, 0x52, 0x37,
+ 0x03, 0xc8, 0x20, 0xf3, 0xf8, 0x76, 0x32, 0xd2, 0xf7, 0xe8, 0xe4, 0xdf,
+ 0xbf, 0x6c, 0x72, 0x15, 0x78, 0x41, 0x88, 0x19, 0xae, 0xa7, 0x99, 0xeb,
+ 0x9e, 0x67, 0xba, 0x5a, 0xc2, 0xb4, 0xf1, 0x00, 0x3a, 0x0d, 0x01, 0x27,
+ 0x01, 0x11, 0x00, 0x81, 0x79, 0xc2, 0x68, 0x60, 0x19, 0x43, 0x1a, 0x67,
+ 0x17, 0x7d, 0x75, 0xcd, 0xc7, 0xe5, 0xe1, 0x37, 0xc8, 0x09, 0x81, 0xb8,
+ 0x35, 0x0e, 0x1d, 0xc4, 0xde, 0x7d, 0xea, 0x27, 0x35, 0x55, 0xf5, 0xbe,
+ 0xf9, 0xf7, 0xdf, 0x7c, 0xaf, 0xb9, 0xfb, 0xe3, 0xf4, 0xef, 0x78, 0xd0,
+ 0x07, 0x20, 0x3a, 0x19, 0xc0, 0x2d, 0x46, 0x3d, 0xb1, 0x6d, 0x8d, 0x3b,
+ 0x24, 0x89, 0xc3, 0x85, 0xde, 0xe9, 0xed, 0xfa, 0x1a, 0xe1, 0x42, 0x46,
+ 0xeb, 0xa5, 0xe7, 0x7a, 0xd7, 0x9d, 0xe6, 0xd6, 0x77, 0x4b, 0xce, 0xf5,
+ 0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d,
+ 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e,
+ 0x77, 0xa5, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x17, 0x53,
+ 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e,
+ 0x96, 0x9d, 0xd4, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99,
+ 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d,
+ 0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6,
+ 0xbc, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee,
+ 0xa7, 0x99, 0xeb, 0x9e, 0x67, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3,
+ 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f,
+ 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a,
+ 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xb1, 0xa5, 0xa2, 0xeb, 0x79, 0x9e, 0x6a,
+ 0x4a, 0x77, 0x29, 0x4a, 0x4a, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0x80, 0x00, 0x00, 0x01, 0x08, 0x53, 0xe4, 0x3c, 0xef,
+ 0x5a, 0xf3, 0x3d, 0xfa, 0x83, 0x5e, 0x25, 0xd4, 0xf3, 0x3d, 0x6b, 0xce,
+ 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc,
+ 0xef, 0x69, 0x49, 0xc1, 0x41, 0xf9, 0xd7, 0x8c, 0x67, 0xca, 0x73, 0x2d,
+ 0x3c, 0x6a, 0x0b, 0x42, 0x3a, 0x73, 0xaf, 0xf0, 0x1e, 0xa0, 0xed, 0xc7,
+ 0x39, 0xc9, 0x53, 0x29, 0x2c, 0x65, 0xc4, 0x51, 0x30, 0x6a, 0x3f, 0x7c,
+ 0xa3, 0xed, 0x7a, 0x5b, 0xb5, 0xf3, 0xde, 0x61, 0xbd, 0x1f, 0x7c, 0x7e,
+ 0xfb, 0xe3, 0x17, 0x96, 0xb1, 0x43, 0xed, 0x43, 0x06, 0x97, 0xca, 0x61,
+ 0xa4, 0x95, 0x30, 0xd7, 0x00, 0xa3, 0xa4, 0xef, 0xbe, 0xed, 0xce, 0x54,
+ 0xe4, 0x81, 0x9b, 0xab, 0x8c, 0xef, 0x8e, 0x61, 0x9d, 0x95, 0x9b, 0x33,
+ 0xee, 0x3b, 0x65, 0x3e, 0xcb, 0xb9, 0x30, 0x14, 0xf9, 0x6e, 0xbd, 0xed,
+ 0x49, 0x6e, 0xaa, 0xf7, 0xe7, 0x00, 0x37, 0x28, 0x7e, 0xbd, 0x70, 0x6a,
+ 0x72, 0x52, 0xa5, 0xec, 0x47, 0xba, 0xe9, 0xcc, 0x93, 0x42, 0xb6, 0x23,
+ 0x4e, 0xc4, 0x63, 0xc3, 0x88, 0x26, 0x5e, 0x5b, 0x5d, 0x95, 0x55, 0x2f,
+ 0x11, 0xac, 0xbb, 0x5a, 0x12, 0x35, 0x1b, 0x8f, 0xdb, 0x85, 0xd4, 0x96,
+ 0x8e, 0x82, 0x5f, 0xe8, 0x6c, 0x4a, 0x42, 0x50, 0x96, 0xc8, 0x74, 0xed,
+ 0x94, 0xf8, 0xa0, 0x97, 0xcf, 0x97, 0xff, 0x37, 0x1d, 0x7a, 0xa1, 0x84,
+ 0xce, 0x97, 0x15, 0x71, 0x25, 0x2c, 0x1f, 0x5c, 0x7c, 0xd8, 0x02, 0xd0,
+ 0xc0, 0x27, 0x82, 0xf4, 0xa0, 0x37, 0x0c, 0x02, 0x78, 0x2f, 0x5f, 0x54,
+ 0xea, 0xce, 0x24, 0x89, 0x64, 0x4e, 0x6c, 0xe7, 0x2c, 0xdc, 0xc0, 0x6c,
+ 0x4d, 0xe5, 0x30, 0xab, 0xd0, 0x6a, 0xbf, 0xe6, 0xc0, 0x07, 0xa4, 0x20,
+ 0x45, 0xfc, 0x40, 0x5e, 0xad, 0xe0, 0x27, 0x21, 0x60, 0x12, 0xa3, 0x88,
+ 0xee, 0x46, 0xbe, 0x9b, 0xca, 0x01, 0x42, 0x3e, 0x4a, 0xfa, 0x0a, 0xe9,
+ 0x14, 0xfb, 0x2f, 0x61, 0xce, 0x78, 0x1b, 0xb5, 0xa7, 0xab, 0x88, 0xab,
+ 0xb2, 0x19, 0x44, 0xfa, 0x72, 0x37, 0xaf, 0xbe, 0x7b, 0xc0, 0xae, 0x33,
+ 0x7e, 0x4e, 0xbd, 0x8a, 0x53, 0xd2, 0x78, 0x55, 0x52, 0x76, 0xce, 0xc6,
+ 0x71, 0xd9, 0x41, 0x56, 0xaf, 0x82, 0x1d, 0x66, 0x99, 0x87, 0x8f, 0x3e,
+ 0xcd, 0x74, 0x55, 0x57, 0xbc, 0xcf, 0x56, 0x82, 0x93, 0xd0, 0x35, 0x08,
+ 0x25, 0x81, 0x76, 0xfb, 0xed, 0xce, 0xdb, 0xe3, 0xd1, 0xd8, 0xd5, 0xe6,
+ 0xe2, 0xd5, 0x5e, 0x19, 0x89, 0x68, 0x2d, 0xb3, 0xfd, 0xbb, 0x65, 0x25,
+ 0xf0, 0xd6, 0x01, 0xee, 0xea, 0x1e, 0xea, 0xff, 0xb2, 0xae, 0xc0, 0x0b,
+ 0x0e, 0x59, 0xf7, 0x38, 0xd3, 0xee, 0x3c, 0xcf, 0x63, 0x42, 0x30, 0x0d,
+ 0xc0, 0xa2, 0x3a, 0x18, 0x97, 0xf0, 0xd2, 0x94, 0xa6, 0x2f, 0x20, 0xe3,
+ 0xb6, 0xe8, 0x6e, 0x31, 0xa6, 0x26, 0x94, 0x18, 0x51, 0x48, 0x25, 0x7d,
+ 0xc0, 0xba, 0x50, 0xf9, 0x05, 0x21, 0x94, 0x48, 0xf8, 0xa5, 0x3a, 0x9f,
+ 0x7e, 0x22, 0x90, 0x0b, 0x0f, 0x15, 0x50, 0xd3, 0xaf, 0x63, 0xdc, 0x80,
+ 0x10, 0x00, 0x9c, 0x00, 0xf0, 0x35, 0xff, 0x02, 0xc9, 0x51, 0x62, 0x87,
+ 0x24, 0x38, 0xd1, 0x42, 0x2b, 0x80, 0xc0, 0x46, 0x74, 0x36, 0xe4, 0x20,
+ 0xc4, 0xe4, 0xb6, 0x40, 0x17, 0x25, 0x6e, 0x78, 0xc7, 0x4b, 0x9d, 0xdf,
+ 0xf3, 0xff, 0x32, 0x25, 0x13, 0x06, 0x90, 0x89, 0x79, 0x09, 0xfc, 0x6a,
+ 0x70, 0x40, 0x61, 0x49, 0x03, 0xc8, 0x35, 0x39, 0xf0, 0x75, 0x8e, 0x26,
+ 0x32, 0x4f, 0xaf, 0xfd, 0xf5, 0x77, 0xf3, 0x2e, 0x1a, 0x19, 0xf3, 0x0d,
+ 0xc1, 0x7a, 0xfa, 0xef, 0x6d, 0xfe, 0xdd, 0xf0, 0xfa, 0xb2, 0xb6, 0xfb,
+ 0xbf, 0x02, 0x4b, 0x6d, 0xcf, 0x71, 0xf6, 0x84, 0xa9, 0x3e, 0x8f, 0x95,
+ 0x5e, 0xaa, 0xae, 0x79, 0x9e, 0xb9, 0xe6, 0x7b, 0xb5, 0xae, 0x5b, 0xa5,
+ 0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6, 0xbc, 0xcf,
+ 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a, 0x5a, 0x2e, 0xa7, 0x99,
+ 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0x3d, 0x0d,
+ 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6,
+ 0xbc, 0xef, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x5a, 0x77,
+ 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79,
+ 0x9e, 0x86, 0x8b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f,
+ 0x3b, 0xd6, 0xbc, 0xef, 0x4b, 0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a,
+ 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67,
+ 0xad, 0x79, 0xde, 0x96, 0x9d, 0xd4, 0xf3, 0x3d, 0x64, 0x69, 0x68, 0xb4,
+ 0x79, 0xa3, 0x4a, 0x67, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17,
+ 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca,
+ 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94,
+ 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29,
+ 0x11, 0x72, 0x94, 0xa4, 0x44, 0x00, 0x00, 0x01, 0x09, 0x53, 0xa1, 0xe6,
+ 0x7a, 0xd7, 0x9d, 0xef, 0xd2, 0x1a, 0xf1, 0xee, 0x97, 0x9d, 0xeb, 0x5e,
+ 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9,
+ 0xed, 0x08, 0x46, 0x41, 0xc1, 0x46, 0x6c, 0x3b, 0x0a, 0xa3, 0xcc, 0xf6,
+ 0x59, 0xf0, 0x7c, 0xed, 0xf1, 0x5b, 0x66, 0xca, 0x5b, 0x88, 0x1c, 0xc6,
+ 0x28, 0xea, 0xc4, 0x14, 0x84, 0x66, 0x3c, 0xf3, 0xf6, 0x34, 0xfc, 0xdb,
+ 0x6c, 0xb3, 0x54, 0xba, 0xa7, 0x9d, 0xec, 0x46, 0xa5, 0x99, 0xfa, 0xeb,
+ 0x6d, 0x76, 0xe2, 0x99, 0x23, 0x46, 0xff, 0xc4, 0x21, 0x62, 0x7e, 0x46,
+ 0xe4, 0xf8, 0xf2, 0xb7, 0xee, 0xfc, 0x42, 0x8d, 0x3d, 0x6c, 0xf8, 0x3b,
+ 0xea, 0xb7, 0x9d, 0xec, 0xc9, 0x69, 0x77, 0x7e, 0xe7, 0x4d, 0xb5, 0xd4,
+ 0xf6, 0xb2, 0x56, 0x0e, 0x66, 0x66, 0x43, 0x30, 0xe3, 0x9d, 0x85, 0x3d,
+ 0x5b, 0xcc, 0xf1, 0xbf, 0x41, 0xdd, 0x9e, 0x47, 0xd8, 0x65, 0xbd, 0x68,
+ 0xcc, 0xe3, 0xce, 0xcc, 0xa3, 0x0e, 0x5b, 0x3f, 0x61, 0x7e, 0x0f, 0x33,
+ 0xd1, 0x92, 0x9c, 0x6f, 0xa9, 0xb5, 0xbf, 0x2b, 0x2f, 0xaf, 0x7e, 0xa7,
+ 0x14, 0xb5, 0x08, 0x5f, 0xa9, 0x7a, 0xe7, 0x99, 0xe4, 0xff, 0x9f, 0xe2,
+ 0xd4, 0x3f, 0x81, 0x17, 0x62, 0x75, 0x6a, 0x50, 0xea, 0x75, 0x2d, 0x4c,
+ 0x8e, 0x79, 0xe7, 0xa3, 0xc1, 0xe6, 0x7b, 0x2c, 0x94, 0xe3, 0x66, 0xda,
+ 0xd7, 0x81, 0xb3, 0xbb, 0x89, 0x7d, 0xe2, 0x1f, 0xc4, 0x89, 0xac, 0x79,
+ 0x9e, 0x4f, 0xc7, 0x9d, 0x3b, 0x53, 0x18, 0x0e, 0x11, 0x9b, 0x4c, 0x1f,
+ 0xcf, 0x7e, 0x2f, 0xaf, 0xac, 0x3f, 0xce, 0xf3, 0x3d, 0x0f, 0xc7, 0xf9,
+ 0xdd, 0x7f, 0xf1, 0xe2, 0x8e, 0xc7, 0x0e, 0x76, 0x72, 0x7c, 0xcf, 0xce,
+ 0x75, 0x0e, 0x64, 0x75, 0x8f, 0x5e, 0xf5, 0x8f, 0x3b, 0xd0, 0xe9, 0x1f,
+ 0x36, 0xd6, 0x86, 0x35, 0x1e, 0xb5, 0xe6, 0x79, 0x2e, 0x2e, 0xa7, 0x99,
+ 0xeb, 0x9e, 0x67, 0x86, 0x8b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe9, 0x69,
+ 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73,
+ 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x9e, 0x67, 0xa5, 0xa7, 0x75,
+ 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x99,
+ 0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79,
+ 0x9e, 0xb9, 0xe6, 0x7a, 0x5a, 0x2e, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1,
+ 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa5, 0xe7, 0x7a,
+ 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0x68, 0x69, 0xdd,
+ 0x6f, 0x33, 0xd6, 0x52, 0x99, 0xda, 0xbc, 0xd4, 0xa4, 0xa2, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00,
+ 0x01, 0x0a, 0x53, 0xd0, 0xf3, 0xbd, 0x6b, 0xcc, 0xf7, 0xdf, 0xb5, 0xe5,
+ 0x5d, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x45, 0xd2, 0xf3, 0xbd, 0x6b,
+ 0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75,
+ 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99,
+ 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79,
+ 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2,
+ 0xd1, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xad, 0xe6, 0x7a,
+ 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x4b, 0x4e,
+ 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf,
+ 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x77, 0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9,
+ 0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef,
+ 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x5a, 0x77, 0x4b, 0xce,
+ 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86,
+ 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb,
+ 0x5e, 0x77, 0xa1, 0xa2, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77,
+ 0x53, 0xcc, 0xf5, 0xcf, 0x1a, 0x5a, 0x77, 0x5b, 0xcc, 0xf3, 0xd2, 0x99,
+ 0xd8, 0x8d, 0x29, 0x28, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x20, 0x00, 0x00, 0x01, 0x0b, 0x53, 0xeb, 0xbc, 0xef,
+ 0x5a, 0xf3, 0xbd, 0xf6, 0xcd, 0x79, 0x97, 0x4b, 0xce, 0xf5, 0xaf, 0x33,
+ 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3,
+ 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa5,
+ 0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a,
+ 0xd7, 0x9d, 0xe8, 0x68, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d,
+ 0xd2, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e,
+ 0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x3b, 0xa5,
+ 0xe7, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf,
+ 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99,
+ 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d,
+ 0x3b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6,
+ 0xbc, 0xef, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x1a, 0x77,
+ 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x77, 0xac, 0x9a,
+ 0x86, 0x8b, 0xad, 0xe6, 0x79, 0xa9, 0x29, 0xd8, 0x8d, 0x29, 0x28, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00,
+ 0x01, 0x0c, 0x53, 0xf4, 0xef, 0x33, 0xd6, 0xbc, 0xef, 0x7d, 0x73, 0x5e,
+ 0x7d, 0xd2, 0xf3, 0xbd, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x99, 0xeb,
+ 0x9e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x58, 0xf3, 0xbd, 0x2d, 0x3b,
+ 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x68, 0xba, 0x5e, 0x77, 0xad, 0x79,
+ 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0xb4, 0xee, 0xa7,
+ 0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd,
+ 0x0d, 0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33,
+ 0xd7, 0x3c, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34,
+ 0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x67, 0xa5, 0xa7, 0x75, 0x3c, 0xef, 0x5a,
+ 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd,
+ 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe6,
+ 0x7a, 0x1a, 0x77, 0x53, 0xce, 0xf5, 0x91, 0xa5, 0xa2, 0xd1, 0xe6, 0x8d,
+ 0x29, 0x9d, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29,
+ 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44,
+ 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17,
+ 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca,
+ 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94,
+ 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29,
+ 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44,
+ 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17,
+ 0x29, 0x4a, 0x44, 0x40, 0x00, 0x00, 0x01, 0x0d, 0x53, 0xf6, 0xcf, 0x3b,
+ 0xd6, 0x3c, 0xef, 0x7d, 0x2b, 0x5c, 0x57, 0x53, 0xcc, 0xf5, 0xaf, 0x3b,
+ 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9, 0xe7,
+ 0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43,
+ 0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe7, 0x7a, 0x5a, 0x77, 0x4b, 0xce, 0xf5,
+ 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d,
+ 0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e,
+ 0x77, 0xa1, 0xa2, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53,
+ 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e,
+ 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x9d,
+ 0xeb, 0x29, 0x4c, 0xed, 0x5e, 0x31, 0xa4, 0xa2, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00, 0x01,
+ 0x0e, 0x53, 0xfa, 0x2b, 0xce, 0xf5, 0xaf, 0x33, 0xde, 0xe3, 0x5c, 0xb7,
+ 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd1, 0x75, 0x3c, 0xcf, 0x5a, 0xf3,
+ 0xbd, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f,
+ 0x3b, 0xd6, 0xbc, 0xcf, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a,
+ 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77,
+ 0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69,
+ 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5,
+ 0xe6, 0xa5, 0xa7, 0x75, 0x3c, 0xef, 0x3d, 0x29, 0x9d, 0x88, 0xd2, 0x92,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x88, 0x00, 0x00, 0x01, 0x0f, 0x53, 0xfa, 0xab, 0xcc, 0xf5, 0xaf, 0x3b,
+ 0xde, 0xa3, 0x5c, 0xf7, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba,
+ 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce,
+ 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc,
+ 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe9,
+ 0x68, 0xba, 0x9e, 0x67, 0xad, 0x78, 0xd0, 0xd3, 0xb4, 0x79, 0x9e, 0x6a,
+ 0x4a, 0x76, 0x23, 0x4a, 0x4a, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x97, 0xcf,
+ 0xca, 0x00, 0x58, 0x90, 0xd0, 0x1d, 0x00, 0x1f, 0xa0, 0x0a, 0x86, 0x01,
+ 0x81, 0x89, 0x4b, 0xb1, 0x6e, 0xc5, 0x3b, 0xa9, 0x9c, 0xcf, 0x6e, 0x58,
+ 0x6a, 0x03, 0x18, 0x6f, 0x67, 0xed, 0x84, 0xd3, 0xb7, 0xf7, 0xf4, 0x0a,
+ 0x5c, 0x22, 0x10, 0x0c, 0x0a, 0x21, 0x93, 0x40, 0x6c, 0x82, 0x1a, 0x1f,
+ 0xf4, 0x21, 0x1c, 0x61, 0x6c, 0x9e, 0x90, 0xe5, 0x31, 0xaf, 0x54, 0x01,
+ 0xa8, 0x06, 0x98, 0x03, 0x44, 0x92, 0xb9, 0x34, 0xa2, 0xcb, 0xcc, 0xe8,
+ 0x40, 0xd5, 0x38, 0xff, 0xf3, 0x07, 0x1e, 0xb9, 0x12, 0xd0, 0x34, 0x61,
+ 0xac, 0xec, 0xdc, 0xe8, 0x6e, 0x7d, 0xfa, 0x2a, 0x52, 0x96, 0x35, 0x5c,
+ 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0xf1, 0xa0, 0x18,
+ 0x00, 0x38, 0x48, 0x05, 0xe8, 0x4f, 0x28, 0x9a, 0x56, 0xe8, 0x61, 0xa8,
+ 0xf9, 0x2a, 0x77, 0x65, 0xb3, 0x73, 0xad, 0x8b, 0x2d, 0x01, 0x98, 0x6a,
+ 0x7b, 0x61, 0xf4, 0xec, 0xf7, 0xec, 0xa9, 0x72, 0x80, 0x62, 0x05, 0x10,
+ 0x03, 0xa2, 0xf9, 0x40, 0x67, 0x16, 0x94, 0x25, 0x28, 0x4b, 0x64, 0xe6,
+ 0xe8, 0xec, 0x6e, 0xce, 0x1d, 0x71, 0x09, 0x80, 0x0f, 0x31, 0x34, 0x07,
+ 0x5c, 0x07, 0x60, 0x15, 0x86, 0x20, 0xa4, 0x29, 0x23, 0x72, 0x5f, 0x25,
+ 0xb7, 0x18, 0x72, 0xfa, 0xcc, 0x3e, 0x7c, 0xb4, 0x6d, 0xbf, 0xea, 0xe7,
+ 0x2b, 0x9b, 0x26, 0xf7, 0xec, 0x29, 0x78, 0x00, 0x13, 0x00, 0x2e, 0x48,
+ 0x06, 0xa1, 0x9c, 0x34, 0x98, 0x43, 0x0c, 0xfb, 0x71, 0x9d, 0xf1, 0x79,
+ 0x6d, 0xb8, 0xe1, 0x76, 0xde, 0x00, 0xe9, 0xdb, 0x13, 0x00, 0x62, 0x03,
+ 0xa2, 0x6a, 0x7f, 0x24, 0x62, 0xdb, 0x9d, 0xfb, 0x84, 0xb6, 0xb5, 0x00,
+ 0xb4, 0x31, 0xfe, 0x1a, 0xbc, 0x7a, 0xba, 0x84, 0xaa, 0x43, 0x3d, 0xfb,
+ 0x2a, 0x5c, 0xd0, 0x1d, 0x80, 0xc0, 0x9a, 0x02, 0x62, 0xc9, 0x44, 0xd2,
+ 0xba, 0x39, 0x8e, 0x77, 0x5b, 0x3e, 0xe6, 0xeb, 0x20, 0x06, 0x80, 0x26,
+ 0x00, 0x37, 0x0d, 0xc4, 0xcc, 0x5a, 0x0a, 0x4f, 0x24, 0x6f, 0xba, 0xdb,
+ 0x7f, 0xd7, 0xb2, 0x9b, 0x63, 0x6c, 0x39, 0x68, 0xc8, 0x1a, 0xea, 0x67,
+ 0x73, 0xd5, 0x53, 0x2d, 0xaf, 0xbb, 0x4b, 0xe7, 0x25, 0x80, 0x2a, 0xe1,
+ 0x81, 0xa8, 0x0c, 0x48, 0x0c, 0x40, 0xa2, 0x0b, 0x4b, 0x0c, 0x47, 0x1a,
+ 0xfd, 0xfb, 0x66, 0x51, 0xbe, 0xf5, 0x60, 0x27, 0x00, 0xc8, 0x0a, 0x80,
+ 0x65, 0xd1, 0x89, 0xa8, 0x61, 0xbb, 0xfd, 0xf7, 0xdf, 0x76, 0x3d, 0x78,
+ 0xdd, 0x6c, 0x82, 0x18, 0x61, 0x7f, 0x6c, 0x9d, 0xdd, 0x78, 0x58, 0x81,
+ 0xf2, 0xe8, 0xfa, 0xfb, 0x74, 0xa5, 0xe4, 0x00, 0x60, 0x00, 0xd8, 0x0a,
+ 0x80, 0x9c, 0x0a, 0x16, 0x43, 0x43, 0x64, 0x23, 0x76, 0xdc, 0xf2, 0xc6,
+ 0x65, 0xec, 0xc1, 0x4b, 0xb1, 0x49, 0x68, 0x2b, 0x3a, 0x7b, 0x63, 0xaa,
+ 0xd9, 0xef, 0xd7, 0xd2, 0x94, 0xb2, 0x9d, 0xca, 0x52, 0x91, 0x17, 0x29,
+ 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52,
+ 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xb8,
+ 0x40, 0x1a, 0x00, 0x98, 0x00, 0xdc, 0x37, 0x13, 0x31, 0x68, 0x29, 0x3c,
+ 0x91, 0xbe, 0xeb, 0x6d, 0xff, 0x5e, 0xca, 0x6d, 0x8d, 0xad, 0x29, 0xc8,
+ 0xdf, 0xf5, 0x6c, 0x7c, 0x8f, 0xbe, 0xed, 0x2f, 0x22, 0x00, 0xec, 0x03,
+ 0x04, 0x00, 0x5c, 0x90, 0xc2, 0x80, 0xcf, 0x2c, 0xb4, 0xa0, 0x6a, 0x73,
+ 0x36, 0xc6, 0x66, 0xe6, 0xbf, 0xdc, 0x2a, 0xf5, 0xd5, 0x14, 0x5e, 0x2d,
+ 0x19, 0x28, 0x5e, 0xe7, 0x4b, 0xe6, 0xb9, 0x4a, 0x52, 0xca, 0xab, 0x94,
+ 0xbe, 0x6a, 0x03, 0xa0, 0x05, 0x88, 0x26, 0x90, 0xb8, 0x0e, 0xd2, 0x05,
+ 0x70, 0x0d, 0x86, 0x25, 0x2e, 0x84, 0x24, 0x68, 0x16, 0xfd, 0xd4, 0x96,
+ 0x1d, 0xef, 0xa5, 0x5d, 0x25, 0x06, 0xa0, 0xbe, 0x9c, 0x8e, 0xe3, 0x3b,
+ 0x71, 0x23, 0xa8, 0xfb, 0xeb, 0x94, 0xa5, 0x2e, 0x6a, 0xee, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88,
+ 0x00, 0x00, 0x01, 0x10, 0x53, 0xfb, 0x23, 0xcc, 0xf5, 0xcf, 0x33, 0xdd,
+ 0x6d, 0x65, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x3b, 0xa5, 0xe7,
+ 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43,
+ 0x4e, 0xea, 0x79, 0x9e, 0xb6, 0x34, 0xb4, 0x5a, 0x3c, 0xd1, 0xa5, 0x33,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x7c, 0x24, 0x00, 0xb8, 0x37,
+ 0x06, 0x80, 0xe8, 0x9a, 0x4c, 0xf9, 0xc6, 0x25, 0xd6, 0xbc, 0x30, 0xbc,
+ 0xcc, 0xc0, 0x22, 0xee, 0x26, 0xfb, 0xb5, 0xf2, 0xb0, 0x02, 0xac, 0x90,
+ 0x03, 0xf2, 0x6f, 0x58, 0xc0, 0x30, 0xdc, 0x73, 0xf4, 0xe6, 0x71, 0x6c,
+ 0x35, 0x83, 0xef, 0x64, 0x03, 0x54, 0x38, 0x14, 0x2c, 0x53, 0x7c, 0x27,
+ 0x7b, 0x00, 0xce, 0xc9, 0xd5, 0xff, 0x41, 0x00, 0x84, 0x0c, 0x8e, 0x9e,
+ 0x49, 0xef, 0x91, 0x8d, 0xc3, 0xfd, 0xea, 0x4a, 0x29, 0x21, 0xa9, 0x6c,
+ 0x1f, 0xee, 0x70, 0x15, 0x4f, 0x48, 0x6a, 0x45, 0x76, 0x0a, 0xbe, 0x58,
+ 0x03, 0xad, 0x8a, 0x26, 0x72, 0xc7, 0xbf, 0xe1, 0xd6, 0xbf, 0x8c, 0xce,
+ 0x6f, 0xa2, 0xfd, 0xa0, 0x05, 0x48, 0xdf, 0x23, 0x96, 0x13, 0xcc, 0x65,
+ 0x0a, 0xbd, 0x29, 0x64, 0xce, 0x18, 0x96, 0x4f, 0x0f, 0xb9, 0x64, 0x2d,
+ 0xc3, 0x49, 0xbb, 0xf7, 0xeb, 0x7f, 0xc0, 0x3f, 0xbc, 0x88, 0x0e, 0xd8,
+ 0xb0, 0xd2, 0x8a, 0x0b, 0x0f, 0x6a, 0x9f, 0xe4, 0xec, 0xee, 0xa7, 0xe2,
+ 0xa2, 0xfd, 0x29, 0x60, 0x50, 0x94, 0x50, 0xde, 0x91, 0xcc, 0x2e, 0x72,
+ 0x60, 0x14, 0xc1, 0xa8, 0xe9, 0xfd, 0xb6, 0xbc, 0x58, 0x09, 0xfb, 0x92,
+ 0x8a, 0xc6, 0xf7, 0x20, 0x11, 0x6e, 0x71, 0x60, 0x28, 0x02, 0x8d, 0xbb,
+ 0xbe, 0x61, 0x2e, 0x7b, 0x13, 0xee, 0xd1, 0xbc, 0x66, 0x1f, 0xf9, 0x3a,
+ 0xcd, 0xaf, 0xec, 0xa1, 0x85, 0x01, 0x94, 0xb3, 0xb5, 0xe3, 0x80, 0x0f,
+ 0x00, 0xbb, 0x6e, 0xac, 0x7f, 0xe4, 0x6c, 0x1d, 0x56, 0x02, 0x62, 0x80,
+ 0xbf, 0xff, 0x12, 0x09, 0xdc, 0x07, 0xf7, 0x64, 0x0a, 0x86, 0xfe, 0x1b,
+ 0xdb, 0x92, 0x15, 0x88, 0xb7, 0x07, 0xff, 0xa5, 0xb7, 0x32, 0xd0, 0xca,
+ 0xb3, 0xd4, 0x00, 0xc4, 0x99, 0x80, 0xa1, 0x0c, 0xb0, 0xcc, 0x9c, 0x5e,
+ 0x47, 0x61, 0xb8, 0xd0, 0x3b, 0x8e, 0xe2, 0xef, 0x00, 0x00, 0x91, 0x3c,
+ 0x02, 0xb0, 0xd4, 0x3a, 0xb8, 0x63, 0xfc, 0x2f, 0x27, 0x31, 0x1f, 0xdf,
+ 0x53, 0xbe, 0x7e, 0x00, 0xb0, 0x34, 0x94, 0x02, 0x1c, 0x86, 0x01, 0xca,
+ 0x12, 0x81, 0x0d, 0x86, 0x80, 0x0c, 0xae, 0x50, 0x2b, 0x9c, 0x84, 0x5e,
+ 0x3c, 0x23, 0x93, 0xad, 0x10, 0x49, 0xd5, 0xc6, 0xf0, 0x1d, 0x94, 0x08,
+ 0x5f, 0x29, 0xd8, 0xdf, 0x82, 0xfa, 0xc8, 0xfe, 0xa4, 0x0a, 0xa7, 0x23,
+ 0x20, 0x99, 0x8e, 0x28, 0xfe, 0x3d, 0x26, 0x85, 0x1d, 0x78, 0x52, 0x1a,
+ 0x00, 0xc0, 0x69, 0x64, 0x3e, 0x3b, 0x0c, 0x01, 0x5f, 0xcc, 0x4e, 0x67,
+ 0xbe, 0xd8, 0x7f, 0xb4, 0x28, 0x27, 0x61, 0x5b, 0x05, 0xd3, 0xea, 0xdf,
+ 0x81, 0x00, 0x98, 0x10, 0x82, 0x40, 0x1d, 0x72, 0xc0, 0x70, 0x9e, 0x51,
+ 0x69, 0x70, 0x1e, 0xf7, 0xc8, 0xe1, 0xe1, 0x0d, 0x7e, 0x81, 0xbe, 0x00,
+ 0x00, 0xec, 0x10, 0x81, 0xc0, 0x30, 0x41, 0x30, 0xf7, 0x25, 0x06, 0x39,
+ 0xac, 0x8c, 0xee, 0xc0, 0x22, 0x27, 0xdf, 0x68, 0x01, 0x80, 0x14, 0xe0,
+ 0x3b, 0x26, 0x94, 0x19, 0x80, 0xff, 0x5f, 0x1d, 0x8d, 0xe1, 0x57, 0x50,
+ 0x0e, 0xbe, 0x18, 0x1a, 0x1b, 0xc0, 0xfa, 0x7f, 0x71, 0x5a, 0x84, 0x9f,
+ 0xb5, 0x7f, 0xf2, 0x32, 0x18, 0x49, 0x09, 0x1f, 0x9e, 0x11, 0xc2, 0xf5,
+ 0xe9, 0xc0, 0x40, 0x87, 0x2f, 0xf6, 0x3b, 0x13, 0xc0, 0x7f, 0x79, 0x80,
+ 0x13, 0xa7, 0x64, 0x64, 0x63, 0x56, 0x1d, 0xa0, 0x02, 0x0c, 0x10, 0x9e,
+ 0x3f, 0xb0, 0x50, 0x1b, 0xbd, 0x27, 0x01, 0x47, 0x16, 0x70, 0x7d, 0x91,
+ 0xb7, 0xeb, 0xc8, 0x63, 0x4b, 0x21, 0xfc, 0x85, 0xb3, 0x6c, 0x28, 0x08,
+ 0xde, 0x4c, 0x04, 0x29, 0xc3, 0x36, 0x35, 0xdc, 0x3a, 0x50, 0x28, 0x4d,
+ 0xe1, 0xa5, 0xf5, 0x6c, 0xaf, 0xd5, 0x96, 0x7e, 0x36, 0xfa, 0x81, 0x0d,
+ 0x03, 0x03, 0x53, 0x9f, 0xf1, 0x22, 0x2e, 0x3d, 0x82, 0x76, 0x38, 0x66,
+ 0xae, 0xfb, 0xbb, 0x3f, 0x1d, 0x7c, 0x9f, 0x86, 0x24, 0x30, 0x99, 0xf9,
+ 0x2d, 0x5c, 0xcc, 0x77, 0x7b, 0xea, 0x48, 0x40, 0xd4, 0xec, 0x3f, 0xb5,
+ 0xf1, 0x90, 0x13, 0xf5, 0x21, 0x1b, 0x0b, 0x11, 0xb6, 0xb8, 0x53, 0xd6,
+ 0x8d, 0x6b, 0xaf, 0xe8, 0x85, 0x13, 0x1d, 0x04, 0xb4, 0xf6, 0x45, 0x9c,
+ 0xb2, 0x16, 0x42, 0x31, 0x68, 0x48, 0xdd, 0x79, 0x50, 0x28, 0x5e, 0xd8,
+ 0x0c, 0x17, 0x8f, 0x39, 0xc5, 0x99, 0x7a, 0xa0, 0x2a, 0x18, 0x03, 0x6e,
+ 0xdb, 0xf6, 0xeb, 0xad, 0xf8, 0xd6, 0x0a, 0x1d, 0xf4, 0xaf, 0xd9, 0x6c,
+ 0xfd, 0xb5, 0xf3, 0xc0, 0x0a, 0x91, 0xbe, 0x41, 0x69, 0x64, 0xf1, 0x4a,
+ 0x0b, 0xbd, 0xda, 0x1d, 0xd4, 0x7e, 0x58, 0x55, 0xf0, 0xa0, 0x2c, 0x03,
+ 0x14, 0x0e, 0x4e, 0x0b, 0xc0, 0x72, 0x82, 0xfb, 0x08, 0x34, 0x54, 0xf5,
+ 0xfd, 0x40, 0x02, 0xc6, 0x2c, 0x95, 0xfe, 0xdc, 0xc3, 0xd7, 0x12, 0x19,
+ 0x33, 0x13, 0x06, 0x67, 0x7f, 0xcf, 0xb8, 0x60, 0x3a, 0x47, 0xe4, 0xd5,
+ 0xbb, 0xa3, 0x72, 0x05, 0x8c, 0x37, 0x00, 0xdc, 0x86, 0xcf, 0x9b, 0x36,
+ 0x03, 0x82, 0x5f, 0xda, 0x3f, 0x1a, 0x7a, 0xb6, 0x7f, 0x4d, 0xf6, 0x52,
+ 0x05, 0x32, 0x03, 0x31, 0x7d, 0x2b, 0xec, 0xa1, 0x20, 0x72, 0xe8, 0x94,
+ 0x4c, 0x74, 0x12, 0xd3, 0xd9, 0x17, 0xcf, 0x00, 0x4f, 0xff, 0x18, 0x8c,
+ 0x2f, 0xf0, 0xe0, 0x23, 0x74, 0x40, 0xa1, 0x7b, 0x60, 0x30, 0x5e, 0x3c,
+ 0xe7, 0x16, 0x65, 0xd1, 0x92, 0x91, 0x8e, 0x2c, 0x9d, 0x49, 0xb7, 0xea,
+ 0x8b, 0x21, 0x64, 0x23, 0x16, 0x84, 0x8d, 0xd7, 0x19, 0x00, 0x3a, 0xdc,
+ 0xa1, 0xbd, 0x2a, 0x5f, 0x56, 0x33, 0x5d, 0x90, 0x2a, 0x18, 0x03, 0x6f,
+ 0xb6, 0xfd, 0xba, 0xef, 0x98, 0x80, 0x4c, 0x03, 0xac, 0x23, 0x70, 0xff,
+ 0xdc, 0x0d, 0x8a, 0x8e, 0x9e, 0xd9, 0xb9, 0xfe, 0x2f, 0xe8, 0xc1, 0x80,
+ 0x50, 0x96, 0x7f, 0xdb, 0xb3, 0xec, 0x68, 0x1a, 0xbb, 0xc0, 0x36, 0x49,
+ 0x2f, 0xa3, 0x67, 0xf8, 0x07, 0xf7, 0xce, 0x00, 0x4d, 0x9f, 0x96, 0x5f,
+ 0x0b, 0x27, 0xfb, 0xc6, 0x06, 0x77, 0xc9, 0x2d, 0x41, 0x3f, 0xf7, 0x15,
+ 0x95, 0x9e, 0xd3, 0x97, 0xf7, 0xe6, 0x9c, 0x17, 0xe4, 0xd7, 0xf5, 0xa0,
+ 0x0a, 0xb0, 0xd2, 0x51, 0x2f, 0x0d, 0x30, 0x70, 0x7d, 0xaf, 0x10, 0x80,
+ 0xb9, 0x2b, 0xa7, 0x6e, 0xe4, 0xe6, 0x7b, 0x86, 0x18, 0x92, 0x8a, 0xef,
+ 0x9c, 0xb4, 0xfb, 0x92, 0x03, 0xa7, 0x2c, 0x34, 0xb4, 0xa4, 0xfc, 0xa0,
+ 0x0e, 0x2c, 0x3f, 0xfb, 0x7e, 0x28, 0x9f, 0x55, 0xfa, 0xa2, 0xc0, 0xa0,
+ 0xc4, 0xb6, 0x49, 0x8f, 0xff, 0x56, 0xcb, 0x69, 0x49, 0xa0, 0x50, 0x06,
+ 0xc8, 0xdd, 0xdc, 0xcf, 0x72, 0x12, 0x02, 0x90, 0x2a, 0x30, 0xc7, 0x58,
+ 0xb6, 0x01, 0xf5, 0x84, 0x06, 0x2e, 0x5a, 0x72, 0x31, 0x87, 0x89, 0xfd,
+ 0x96, 0x02, 0xf9, 0xef, 0xdd, 0x19, 0xcc, 0x7f, 0x2b, 0xf7, 0x01, 0x84,
+ 0x24, 0x86, 0xbf, 0x3b, 0x63, 0x7d, 0xe1, 0x48, 0x41, 0x85, 0x12, 0xdd,
+ 0x09, 0xfc, 0x63, 0xba, 0x83, 0xae, 0x78, 0x0e, 0xf8, 0x18, 0x0c, 0xc5,
+ 0x63, 0x54, 0x1d, 0xae, 0x68, 0x0e, 0x91, 0x8a, 0x2b, 0xf3, 0x4f, 0xe2,
+ 0x80, 0x82, 0xee, 0xd4, 0xa7, 0xf4, 0x7e, 0x3d, 0x5b, 0x50, 0xd7, 0xf4,
+ 0xd2, 0x1b, 0x86, 0x93, 0x7e, 0xcf, 0xd9, 0x41, 0x47, 0x93, 0xe4, 0x00,
+ 0xab, 0x23, 0xec, 0x94, 0xed, 0xf9, 0xdb, 0x2d, 0x62, 0xef, 0x0c, 0x03,
+ 0x04, 0x70, 0x92, 0xf9, 0xe9, 0x02, 0x37, 0xa4, 0xc9, 0x0c, 0x41, 0x7c,
+ 0xbf, 0xfa, 0x52, 0xe7, 0x63, 0x8d, 0x1e, 0x7d, 0xc6, 0xe6, 0x63, 0x35,
+ 0x77, 0xec, 0x0b, 0x26, 0x24, 0xa4, 0x8c, 0x3f, 0xf3, 0x9a, 0xf3, 0xdc,
+ 0x0a, 0x62, 0xb0, 0x17, 0x4b, 0x39, 0x99, 0x77, 0xcf, 0x88, 0x43, 0x4b,
+ 0x2d, 0x25, 0xa7, 0x37, 0xc1, 0x1b, 0x0e, 0x36, 0xfa, 0x2e, 0x04, 0x2f,
+ 0xa2, 0x6b, 0xf3, 0x9c, 0x9c, 0x66, 0x0f, 0xa4, 0x6f, 0xe1, 0xd9, 0x73,
+ 0xab, 0x7c, 0x28, 0x0e, 0xf0, 0x01, 0xb0, 0x03, 0xac, 0x18, 0x37, 0xbf,
+ 0xd9, 0x0e, 0xcc, 0x03, 0x9e, 0x35, 0x39, 0x8c, 0x53, 0xeb, 0xef, 0x77,
+ 0xc4, 0x40, 0x16, 0xf4, 0xa4, 0x06, 0xc1, 0x88, 0x5a, 0x8b, 0x2d, 0x28,
+ 0xc3, 0xb6, 0x52, 0x78, 0x70, 0xdb, 0xec, 0x25, 0x93, 0x03, 0x43, 0x18,
+ 0x6a, 0x3a, 0x1f, 0x6f, 0x9f, 0x1f, 0xbd, 0x90, 0x09, 0x80, 0x70, 0x37,
+ 0x9e, 0x5f, 0xe9, 0xa8, 0xb3, 0xc4, 0x5e, 0xda, 0x58, 0xf9, 0x30, 0x35,
+ 0x08, 0xc5, 0xf4, 0x27, 0x21, 0x27, 0x76, 0x57, 0x68, 0x00, 0xd8, 0x34,
+ 0x94, 0x4b, 0x19, 0xc6, 0x3b, 0x0d, 0x65, 0x3d, 0xe2, 0x3e, 0x1f, 0x74,
+ 0x5e, 0xda, 0x52, 0x94, 0x4e, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x20, 0x00, 0x00, 0x01, 0x11, 0x6b, 0xfb, 0x9b, 0xce,
+ 0xf5, 0xaf, 0x35, 0x4d, 0x4d, 0xd4, 0xf3, 0x3d, 0x64, 0x65, 0x16, 0xaf,
+ 0x18, 0xd2, 0x99, 0xdc, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca,
+ 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94,
+ 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29,
+ 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x2f, 0x99, 0x00,
+ 0x64, 0x01, 0x98, 0x0e, 0x80, 0x34, 0xc5, 0xb2, 0x09, 0xa9, 0xe5, 0xfc,
+ 0xff, 0x8c, 0xc7, 0x1c, 0x68, 0xbf, 0x7d, 0x36, 0x97, 0x49, 0x65, 0x60,
+ 0xdc, 0xdc, 0xea, 0x37, 0x6b, 0xc4, 0x1b, 0xd1, 0xd2, 0xe8, 0xe9, 0xee,
+ 0x7f, 0x4b, 0x3b, 0x87, 0x7f, 0x79, 0x60, 0x13, 0x00, 0x5f, 0xc0, 0xa1,
+ 0x31, 0x24, 0x24, 0xa0, 0x96, 0x51, 0x6e, 0xeb, 0x6f, 0xb2, 0x73, 0x25,
+ 0x8c, 0x10, 0x2e, 0xf6, 0xd4, 0xa5, 0x25, 0x64, 0xe6, 0xeb, 0x9b, 0xb5,
+ 0xfc, 0xd4, 0x30, 0x01, 0xf9, 0x08, 0x04, 0xc8, 0x4e, 0x01, 0xd1, 0x31,
+ 0x2e, 0x93, 0xf9, 0x7d, 0xce, 0x38, 0xe1, 0xcb, 0xbd, 0x38, 0x14, 0x01,
+ 0x3a, 0x49, 0x80, 0x31, 0x40, 0x15, 0x2d, 0xcb, 0xef, 0xdc, 0x7f, 0x7d,
+ 0xf1, 0x15, 0x57, 0xa3, 0xa5, 0xae, 0x26, 0x23, 0x0d, 0x51, 0x25, 0x5f,
+ 0xf3, 0xf8, 0x7d, 0x2f, 0xc2, 0xaf, 0xc0, 0x80, 0x6a, 0x00, 0xff, 0x00,
+ 0xef, 0xf2, 0x50, 0x66, 0x43, 0xa0, 0x60, 0xde, 0x49, 0x18, 0xee, 0xc3,
+ 0xb2, 0xdc, 0x4e, 0xf7, 0x2c, 0x86, 0x00, 0xcc, 0x0c, 0x86, 0x21, 0x08,
+ 0x26, 0x64, 0x27, 0x2f, 0x73, 0xf1, 0x5c, 0xed, 0xd1, 0x87, 0xb7, 0xe7,
+ 0x9d, 0xef, 0xa9, 0x52, 0xd0, 0x02, 0xc2, 0x99, 0x19, 0x89, 0x07, 0x2f,
+ 0xc8, 0x6f, 0xbf, 0x9c, 0x80, 0x64, 0x00, 0xfc, 0xa0, 0x13, 0x06, 0x06,
+ 0xb9, 0x49, 0x48, 0x69, 0x7d, 0xc6, 0xb9, 0xea, 0x56, 0xe1, 0xee, 0x17,
+ 0x7d, 0x01, 0x29, 0xe5, 0xa7, 0xa5, 0x3d, 0xfa, 0x7f, 0xff, 0x8f, 0xfe,
+ 0xba, 0x96, 0x18, 0xa4, 0x23, 0xa5, 0x3b, 0x2d, 0x27, 0x87, 0x54, 0xf7,
+ 0x2e, 0x00, 0x06, 0x80, 0x57, 0x00, 0xc0, 0x0a, 0x94, 0x1a, 0x9d, 0xfe,
+ 0x40, 0xd5, 0x63, 0xd6, 0x8f, 0x88, 0xb7, 0x7e, 0x96, 0x5f, 0x64, 0xef,
+ 0xb9, 0xdd, 0x55, 0x1d, 0x7e, 0x18, 0x01, 0xf8, 0x06, 0x20, 0x06, 0xfc,
+ 0xac, 0x8d, 0xd0, 0x84, 0x63, 0xb8, 0x13, 0xe6, 0xa8, 0x51, 0xc1, 0xf7,
+ 0x7c, 0x06, 0x05, 0x86, 0x06, 0x06, 0xa0, 0xb2, 0xc6, 0x81, 0x64, 0xa1,
+ 0x3b, 0x23, 0x65, 0xb7, 0x75, 0xbf, 0xd7, 0x6a, 0x96, 0xa8, 0x29, 0x08,
+ 0x40, 0xcc, 0xad, 0x4b, 0xbf, 0x7b, 0xf9, 0x28, 0x0c, 0x40, 0x1f, 0x80,
+ 0x80, 0x04, 0xf9, 0x18, 0x34, 0x98, 0x4d, 0x08, 0xfd, 0xbe, 0xfb, 0x75,
+ 0x9f, 0x76, 0x4a, 0x00, 0x7e, 0x50, 0x0c, 0x32, 0x70, 0x0e, 0x88, 0x59,
+ 0x0e, 0xc0, 0x5c, 0xbe, 0xe9, 0x1e, 0xb3, 0x02, 0xaf, 0xa0, 0xd2, 0xe9,
+ 0xc0, 0x50, 0xad, 0x86, 0x2d, 0x87, 0x3f, 0x6a, 0x06, 0xfb, 0xe7, 0x16,
+ 0x4d, 0x49, 0x0c, 0xb1, 0xa4, 0xd4, 0xf4, 0x9c, 0xfc, 0xd6, 0x14, 0x65,
+ 0xf3, 0xa2, 0x10, 0x03, 0xf0, 0x28, 0x42, 0x01, 0xd7, 0x21, 0x21, 0x01,
+ 0xab, 0xc9, 0xc4, 0xb0, 0x8d, 0xdd, 0x2e, 0xb3, 0xcf, 0xc4, 0x4b, 0xea,
+ 0xd4, 0xb1, 0x48, 0x62, 0x0b, 0xdb, 0x73, 0xb2, 0xf6, 0xca, 0x3a, 0xad,
+ 0xfe, 0xbf, 0x2e, 0x02, 0x00, 0x13, 0x80, 0x1c, 0x10, 0xc9, 0x7b, 0x0d,
+ 0x47, 0x74, 0x7d, 0xcf, 0x19, 0xf9, 0x1d, 0x55, 0x60, 0x19, 0x81, 0x52,
+ 0x60, 0x0c, 0x43, 0x78, 0x69, 0x58, 0xb4, 0x27, 0xb3, 0xee, 0xcc, 0xac,
+ 0xcf, 0x9b, 0x88, 0xbd, 0x65, 0x28, 0x25, 0x20, 0x69, 0x2c, 0xd6, 0x60,
+ 0xe9, 0xba, 0x9a, 0xf8, 0x80, 0x20, 0x02, 0xa0, 0x07, 0x80, 0x54, 0xa0,
+ 0xd6, 0x71, 0x8e, 0xf8, 0x57, 0xdf, 0x11, 0x6f, 0x2e, 0x02, 0x10, 0x07,
+ 0x7c, 0x86, 0x90, 0x14, 0x20, 0x94, 0x76, 0xe9, 0xeb, 0x4a, 0x76, 0x58,
+ 0xce, 0x63, 0x3b, 0xa8, 0xd3, 0x6f, 0xa1, 0xd2, 0xae, 0x18, 0x8e, 0x9c,
+ 0x9f, 0x97, 0xb3, 0x49, 0xfb, 0x35, 0xfc, 0xcc, 0x34, 0x02, 0xf0, 0x28,
+ 0x02, 0x74, 0x20, 0x00, 0xfc, 0x98, 0x92, 0x5a, 0x00, 0xf9, 0x5f, 0x65,
+ 0x30, 0xb1, 0x66, 0x5e, 0x98, 0x03, 0x50, 0x0d, 0x4b, 0x00, 0xd5, 0x3b,
+ 0xa7, 0x64, 0x64, 0x12, 0x11, 0x8c, 0x73, 0xd6, 0x2e, 0xf4, 0xd4, 0xa9,
+ 0x04, 0xc4, 0x23, 0xef, 0xb1, 0xce, 0xc2, 0x9c, 0xf6, 0x8f, 0xff, 0xbf,
+ 0x36, 0x01, 0xa0, 0x08, 0x52, 0x01, 0xa1, 0x7d, 0x28, 0xd9, 0x3b, 0xe6,
+ 0xfb, 0x90, 0x4f, 0xd7, 0x97, 0x00, 0x13, 0x81, 0x42, 0x11, 0x65, 0x93,
+ 0x40, 0xa1, 0x31, 0x28, 0x2c, 0xbd, 0xf0, 0x4f, 0xc4, 0xac, 0xeb, 0x7d,
+ 0xb0, 0xbd, 0x95, 0x7d, 0x32, 0x92, 0x49, 0x31, 0x09, 0xef, 0xf1, 0xf9,
+ 0xba, 0xcf, 0xc2, 0xe1, 0xfd, 0xf8, 0x00, 0x0c, 0x00, 0x1f, 0x80, 0x1e,
+ 0x16, 0x4d, 0x48, 0x6e, 0xe5, 0x81, 0x96, 0xff, 0xe6, 0x77, 0xdf, 0x91,
+ 0xdf, 0xda, 0x80, 0x04, 0xe0, 0x85, 0xf3, 0x89, 0xb8, 0x98, 0x56, 0xd9,
+ 0x29, 0x2c, 0x62, 0xd6, 0x94, 0xe6, 0x7f, 0xcd, 0x6e, 0xbc, 0xa5, 0x5e,
+ 0xd6, 0x93, 0x01, 0xb1, 0x4f, 0xd2, 0xcb, 0x4e, 0x6e, 0x22, 0x06, 0xfb,
+ 0xf9, 0xe0, 0x08, 0x40, 0x1d, 0xf2, 0x1a, 0x40, 0x50, 0x82, 0x51, 0xdb,
+ 0xa7, 0xad, 0x29, 0xd9, 0x63, 0x39, 0x8c, 0xee, 0xa3, 0x4d, 0xb3, 0x06,
+ 0x80, 0x5e, 0x05, 0x00, 0x4e, 0x84, 0x00, 0x1f, 0x93, 0x12, 0x4b, 0x40,
+ 0x1f, 0x2b, 0xec, 0xa6, 0x16, 0x2c, 0xcb, 0xe9, 0x94, 0xac, 0x02, 0xc2,
+ 0xb6, 0xcd, 0x98, 0x61, 0xdf, 0x7e, 0xe1, 0x72, 0x1b, 0xeb, 0xff, 0x1a,
+ 0x58, 0x6a, 0x40, 0xa9, 0x68, 0xc7, 0x19, 0x83, 0xef, 0x97, 0x00, 0xc5,
+ 0x28, 0xc3, 0x32, 0x77, 0xfb, 0xbf, 0x51, 0xea, 0x03, 0x77, 0xdb, 0x8a,
+ 0x49, 0x29, 0x39, 0x2c, 0xec, 0x65, 0xcd, 0x26, 0x86, 0x80, 0xdd, 0x2e,
+ 0xcc, 0xa6, 0xbb, 0x3a, 0x17, 0x87, 0x67, 0xb3, 0x7a, 0xbb, 0x92, 0x1a,
+ 0x01, 0x7b, 0x13, 0x00, 0x2c, 0x02, 0x85, 0x01, 0x82, 0x99, 0xbb, 0xad,
+ 0x3d, 0xcf, 0x71, 0x2b, 0x0e, 0xaa, 0x01, 0x00, 0x05, 0xe0, 0x16, 0x06,
+ 0x24, 0x98, 0x1a, 0x8e, 0x59, 0x5f, 0x76, 0xd8, 0xfc, 0xe7, 0xe1, 0x1e,
+ 0xfa, 0x15, 0x2e, 0x20, 0x1b, 0x15, 0xf3, 0x31, 0xae, 0x2d, 0xc7, 0xb8,
+ 0xfb, 0x47, 0xf7, 0xe5, 0xb8, 0x15, 0x0c, 0x0c, 0xe9, 0x00, 0xb0, 0x33,
+ 0xa3, 0xf5, 0x64, 0xa5, 0x2e, 0x9d, 0xba, 0x3e, 0xec, 0xcd, 0xb7, 0x59,
+ 0xeb, 0xbc, 0xf0, 0x06, 0x80, 0x17, 0x86, 0x00, 0x80, 0x99, 0xd0, 0x4a,
+ 0x4a, 0x53, 0xff, 0xee, 0xad, 0x8e, 0x56, 0x73, 0xd8, 0x83, 0x7d, 0x2a,
+ 0x94, 0x24, 0x31, 0x09, 0xcd, 0xd6, 0x1e, 0x3a, 0x6f, 0xf5, 0xf9, 0x20,
+ 0x0c, 0x80, 0x4e, 0x00, 0x78, 0x43, 0xe5, 0xa7, 0x74, 0x8d, 0xdf, 0x71,
+ 0xeb, 0xfb, 0xba, 0xc5, 0xeb, 0x30, 0x0c, 0x40, 0x2f, 0x49, 0x34, 0xb7,
+ 0x2b, 0x01, 0x8c, 0x37, 0x25, 0x47, 0xa7, 0xa0, 0x91, 0xd2, 0x23, 0x6c,
+ 0xd8, 0xeb, 0xdb, 0x52, 0x40, 0x61, 0x03, 0x46, 0xb2, 0xd9, 0xa1, 0xfb,
+ 0x35, 0xfc, 0xbc, 0x07, 0x60, 0x0e, 0xca, 0x21, 0xe2, 0x81, 0x08, 0x1b,
+ 0x74, 0xa1, 0x6e, 0x95, 0xed, 0xd4, 0x76, 0x34, 0xe3, 0x6b, 0xc0, 0xa0,
+ 0x03, 0xdc, 0x4c, 0x02, 0x9c, 0x98, 0x90, 0xc2, 0x5f, 0xee, 0xb7, 0x24,
+ 0x8d, 0xfd, 0xc6, 0x9a, 0x72, 0xaf, 0xa4, 0xd2, 0xdc, 0x02, 0xc2, 0x86,
+ 0x0c, 0x65, 0x3b, 0x9f, 0xe9, 0x1b, 0xef, 0xe7, 0x00, 0x30, 0x00, 0x72,
+ 0x1a, 0x4c, 0x2d, 0x3c, 0xb0, 0x94, 0x24, 0xcc, 0xa1, 0x8a, 0x50, 0xc1,
+ 0x46, 0x35, 0xd6, 0xe0, 0x3b, 0x02, 0x85, 0x16, 0x4b, 0x21, 0x13, 0x3e,
+ 0xe7, 0xe2, 0x5f, 0x7c, 0x8e, 0xee, 0xad, 0x89, 0xcf, 0xef, 0x63, 0x4b,
+ 0x20, 0x30, 0x8d, 0xff, 0xd9, 0x9c, 0xf6, 0x36, 0xa7, 0xf7, 0xf3, 0xe0,
+ 0x0c, 0x00, 0x2f, 0x00, 0x3c, 0x25, 0xe2, 0xf2, 0x4b, 0xc9, 0xe3, 0x45,
+ 0x8d, 0x37, 0xac, 0x79, 0x12, 0xe8, 0xa0, 0x03, 0x5c, 0x51, 0x34, 0xa2,
+ 0xd0, 0x4c, 0x29, 0x08, 0x47, 0xfc, 0x95, 0x9d, 0x7f, 0xa1, 0x9f, 0xfe,
+ 0x7b, 0x1f, 0x87, 0x5e, 0xc2, 0x90, 0x06, 0x10, 0xff, 0xb1, 0x24, 0xf8,
+ 0x7f, 0x7f, 0x36, 0x01, 0xd8, 0x03, 0xb0, 0x32, 0x5e, 0xe5, 0x15, 0xb6,
+ 0xc3, 0x06, 0xb9, 0xe5, 0xa3, 0x77, 0x19, 0xc7, 0x67, 0x77, 0x1f, 0x6a,
+ 0x00, 0xcc, 0x01, 0xf8, 0x01, 0xd9, 0x45, 0x15, 0x90, 0x33, 0x66, 0x4f,
+ 0x01, 0x5b, 0x32, 0x85, 0x2e, 0xf7, 0xf4, 0xab, 0x13, 0x0a, 0xc3, 0x1b,
+ 0x12, 0x0e, 0x90, 0xdf, 0x7f, 0x36, 0x01, 0x88, 0x03, 0xb0, 0x0a, 0xc0,
+ 0xa6, 0x26, 0x62, 0xb1, 0x4e, 0x49, 0x1c, 0xdb, 0xbe, 0xc8, 0xa0, 0x00,
+ 0x84, 0x10, 0x82, 0x80, 0x2c, 0xc5, 0x6d, 0xc6, 0x12, 0x9c, 0xc1, 0x8c,
+ 0xdf, 0x89, 0xf7, 0xd2, 0x69, 0x30, 0x0b, 0x0a, 0xdb, 0x66, 0x3b, 0x8d,
+ 0x34, 0xf8, 0x1b, 0xee, 0x52, 0x94, 0xbc, 0xfb, 0x5b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0x80, 0x00, 0x00, 0x01, 0x12,
+ 0x73, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4b, 0x31, 0x5b, 0x25, 0x3b, 0x7f, 0xf3, 0x8e, 0xce,
+ 0x45, 0xd7, 0x9c, 0x0d, 0x01, 0x32, 0x03, 0x08, 0x60, 0x16, 0x01, 0x90,
+ 0x97, 0xd9, 0xfa, 0x19, 0x0b, 0xdb, 0x9f, 0xbf, 0xe6, 0x81, 0x2b, 0x5e,
+ 0x9d, 0xbb, 0x07, 0xd2, 0xd7, 0xeb, 0x29, 0x63, 0x0d, 0x0c, 0x49, 0x30,
+ 0xbe, 0x52, 0x73, 0x74, 0x76, 0xf6, 0xcb, 0x33, 0xe7, 0x72, 0x94, 0xa4,
+ 0xa7, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x5e, 0x5c, 0x06, 0x00, 0x3a, 0x28,
+ 0x9a, 0x51, 0x34, 0xb4, 0xa5, 0x38, 0x0c, 0x21, 0xdb, 0x6c, 0xb1, 0xcb,
+ 0xfc, 0x2d, 0x57, 0xa8, 0xb4, 0xdf, 0x7f, 0xf0, 0xea, 0x6e, 0x52, 0x94,
+ 0xb3, 0xaa, 0xe5, 0x29, 0x79, 0x70, 0x1d, 0x00, 0xe8, 0xa2, 0x69, 0x44,
+ 0xd2, 0xd2, 0x94, 0xe0, 0x30, 0x87, 0x6d, 0xb2, 0xc7, 0x2f, 0xf0, 0xbb,
+ 0x4f, 0xf2, 0x33, 0xfa, 0x4f, 0xbf, 0x51, 0x4a, 0x52, 0xce, 0xab, 0x94,
+ 0xa9, 0x4a, 0x3a, 0x0f, 0xcc, 0xa7, 0x0e, 0xf5, 0xd1, 0x17, 0x29, 0x4a,
+ 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91,
+ 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x97, 0x9a, 0x00,
+ 0x3e, 0x40, 0x01, 0xe7, 0x21, 0x8d, 0xe4, 0x9c, 0x8d, 0x98, 0xfe, 0x69,
+ 0xbc, 0xc1, 0x16, 0xe0, 0x1b, 0x60, 0x0b, 0x30, 0x69, 0x2d, 0x0e, 0x34,
+ 0x61, 0x28, 0x0f, 0x89, 0xb6, 0x1b, 0x89, 0xec, 0x1f, 0x4b, 0x5f, 0x5e,
+ 0x94, 0xa5, 0x8d, 0x57, 0x29, 0x4b, 0x20, 0x61, 0x5c, 0x34, 0xb4, 0x23,
+ 0x01, 0xff, 0xb4, 0xe2, 0xf5, 0xd2, 0xf2, 0x21, 0x80, 0x26, 0x28, 0x9b,
+ 0xcb, 0x18, 0x1b, 0xd8, 0x33, 0x7f, 0xd2, 0x4a, 0xdb, 0x36, 0x1c, 0x05,
+ 0xdd, 0x66, 0x2a, 0xb0, 0x98, 0x03, 0xa0, 0xc2, 0x19, 0xc4, 0xdf, 0x82,
+ 0x50, 0x53, 0x72, 0x8d, 0x49, 0xc7, 0x27, 0x8e, 0xba, 0x09, 0x79, 0x62,
+ 0x85, 0x7a, 0x1a, 0xfd, 0x55, 0x2e, 0x50, 0x15, 0x26, 0x62, 0x10, 0x6a,
+ 0x4a, 0x1a, 0x37, 0xa3, 0xb7, 0xec, 0xcb, 0x14, 0xbf, 0x64, 0x26, 0x81,
+ 0x44, 0x13, 0x0a, 0x42, 0x49, 0x7d, 0xcb, 0xcf, 0x91, 0xd0, 0xdd, 0x9d,
+ 0x9f, 0x84, 0xf6, 0x39, 0x47, 0xd9, 0x8d, 0xca, 0x16, 0x74, 0xda, 0xfd,
+ 0x05, 0x29, 0x4b, 0x2a, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x20, 0x00,
+ 0x00, 0x01, 0x13, 0x73, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xad, 0xfc, 0xc8, 0x0e, 0x90, 0x34, 0x9a,
+ 0x50, 0xae, 0xe1, 0x4b, 0xb8, 0x5c, 0xaf, 0xc8, 0x7c, 0xb4, 0x84, 0x20,
+ 0x6b, 0xbf, 0x22, 0x0f, 0xb7, 0x80, 0xeb, 0xa0, 0xa1, 0xa9, 0xff, 0x6e,
+ 0x33, 0xe4, 0x09, 0xbd, 0x87, 0xbd, 0x13, 0xd3, 0xc4, 0x35, 0x8d, 0x7f,
+ 0xf3, 0x07, 0x41, 0x35, 0x25, 0xa7, 0xec, 0xdb, 0x1f, 0xdc, 0x89, 0xee,
+ 0xea, 0x32, 0x0b, 0x3b, 0x36, 0x46, 0x74, 0xf3, 0x78, 0x1d, 0xb4, 0xe4,
+ 0xec, 0x4c, 0xf9, 0xd6, 0x94, 0x93, 0xb5, 0x90, 0x84, 0x5e, 0xd8, 0xb4,
+ 0x7e, 0x30, 0x6e, 0xcd, 0xc5, 0xeb, 0xa1, 0x98, 0x89, 0x45, 0xb8, 0xcc,
+ 0x4a, 0xfd, 0x38, 0x60, 0x12, 0x1e, 0x06, 0xaa, 0xba, 0x10, 0x8c, 0x8f,
+ 0xf0, 0xd4, 0x7c, 0x79, 0xaf, 0x76, 0xc2, 0x1f, 0x3f, 0x75, 0xaf, 0x90,
+ 0x2e, 0x08, 0xd4, 0x21, 0x05, 0xa1, 0xc6, 0xee, 0x71, 0x06, 0xca, 0xaa,
+ 0xdf, 0x6a, 0x0a, 0xc0, 0x60, 0x97, 0xb0, 0xd6, 0xfc, 0x97, 0xb9, 0x98,
+ 0xc0, 0x37, 0x7a, 0x42, 0xc6, 0x74, 0xf1, 0x9b, 0xec, 0x66, 0xf7, 0x88,
+ 0x28, 0xb0, 0xc2, 0x62, 0x72, 0x50, 0x97, 0x47, 0x40, 0x4a, 0x39, 0xee,
+ 0xc7, 0x01, 0xdb, 0xe8, 0x1e, 0xc3, 0x1f, 0xea, 0xaf, 0xfe, 0x7e, 0x8c,
+ 0xc4, 0x2f, 0xf8, 0x1f, 0xe4, 0xec, 0x38, 0x3e, 0xd6, 0x33, 0x24, 0x98,
+ 0x37, 0x6d, 0xb8, 0xd6, 0x53, 0xac, 0x2f, 0xd4, 0x27, 0xa1, 0x21, 0x85,
+ 0x23, 0x76, 0x18, 0x7b, 0x8b, 0x97, 0x06, 0xa3, 0x24, 0xac, 0x07, 0x90,
+ 0x4f, 0xe3, 0xda, 0xec, 0x59, 0x3e, 0xce, 0xf7, 0x96, 0x8c, 0x19, 0xc6,
+ 0x6c, 0x35, 0x26, 0x89, 0x1d, 0xae, 0x70, 0x68, 0x66, 0x03, 0x38, 0xa4,
+ 0x36, 0x52, 0xc8, 0xff, 0xda, 0xb7, 0x42, 0x03, 0x77, 0x1a, 0xeb, 0x3c,
+ 0x70, 0x59, 0xf7, 0x3f, 0x06, 0xa3, 0x13, 0x7f, 0x77, 0xdd, 0xdf, 0xb0,
+ 0xf9, 0xac, 0x9c, 0x4f, 0x8b, 0xf4, 0x19, 0x25, 0x6f, 0xf0, 0xc7, 0xc6,
+ 0x0f, 0x12, 0x7d, 0xe6, 0x83, 0x0a, 0xfc, 0x85, 0xc0, 0x7a, 0x95, 0x88,
+ 0xcb, 0x70, 0xb5, 0x5e, 0xcd, 0x39, 0x08, 0x3b, 0x8d, 0xbc, 0x40, 0x0d,
+ 0xf1, 0x40, 0x63, 0x65, 0xed, 0x82, 0x43, 0xcf, 0xa3, 0x8a, 0x01, 0xf4,
+ 0xef, 0xda, 0xa5, 0x25, 0x33, 0x21, 0x01, 0xff, 0xac, 0x81, 0x02, 0x66,
+ 0x1a, 0x5e, 0xc3, 0xfc, 0x4b, 0xc5, 0x23, 0x84, 0x71, 0xa2, 0xee, 0x58,
+ 0x14, 0x4e, 0x42, 0x7f, 0xfd, 0x95, 0xd8, 0x4d, 0xcc, 0x65, 0x77, 0xcd,
+ 0x04, 0x3f, 0x90, 0xde, 0x73, 0x1e, 0xe2, 0xb5, 0x68, 0x62, 0x10, 0x4d,
+ 0x4f, 0x46, 0xcd, 0x9b, 0xac, 0xd1, 0x35, 0xa1, 0x85, 0xa1, 0x1d, 0x38,
+ 0xc4, 0xa3, 0xa0, 0x69, 0x15, 0xa7, 0x42, 0x50, 0x18, 0x84, 0x9a, 0x94,
+ 0xa8, 0x46, 0xba, 0x96, 0x46, 0x6b, 0x3a, 0xc3, 0xda, 0xde, 0x19, 0x08,
+ 0x0c, 0x40, 0x0c, 0x30, 0x6a, 0x4a, 0x43, 0xf2, 0xdb, 0x12, 0x5d, 0x3b,
+ 0x61, 0xef, 0xf6, 0x16, 0x4e, 0xbd, 0xd5, 0xe4, 0xc0, 0x62, 0x1a, 0x84,
+ 0xa0, 0x84, 0x52, 0x3b, 0xe2, 0x8a, 0x58, 0x0a, 0xf9, 0xf7, 0x12, 0x5f,
+ 0xb6, 0x16, 0xbb, 0x5d, 0x5f, 0xe3, 0x19, 0xbe, 0xe5, 0xe4, 0x1f, 0xd9,
+ 0x08, 0x23, 0xd1, 0x28, 0x46, 0x25, 0x6e, 0xaf, 0xc9, 0x2c, 0x66, 0x20,
+ 0xd9, 0x72, 0x72, 0x49, 0x99, 0x03, 0x46, 0x74, 0x7e, 0x3d, 0x7b, 0x88,
+ 0xbb, 0xc8, 0xc5, 0x7e, 0xdf, 0xfc, 0xa5, 0x71, 0x63, 0xfd, 0xd6, 0x16,
+ 0xab, 0x3b, 0xca, 0x5e, 0xc5, 0x3a, 0x12, 0x86, 0xe6, 0x30, 0xe2, 0x2c,
+ 0x03, 0x72, 0x32, 0x0a, 0x53, 0x7f, 0x9f, 0x91, 0x0f, 0x90, 0xb2, 0xf7,
+ 0x41, 0x48, 0xeb, 0xf8, 0xd1, 0x43, 0xae, 0x68, 0x6a, 0x4a, 0xe5, 0x8d,
+ 0x1a, 0xe7, 0x32, 0x8e, 0x85, 0xa8, 0x3e, 0x2f, 0xa2, 0x43, 0x46, 0x28,
+ 0x66, 0x65, 0xb3, 0xfb, 0x0a, 0x50, 0x8c, 0x18, 0x57, 0x66, 0xfd, 0x64,
+ 0x03, 0x42, 0xed, 0xc4, 0x34, 0xa0, 0xb2, 0xdf, 0x77, 0x16, 0x2a, 0x62,
+ 0x6a, 0x50, 0x92, 0xf3, 0x70, 0x8c, 0xda, 0x79, 0xd4, 0x17, 0xe2, 0xf9,
+ 0xa1, 0xa8, 0x42, 0x32, 0x3b, 0x33, 0xa0, 0xe7, 0x34, 0x8f, 0x10, 0xd0,
+ 0xcd, 0xd2, 0x52, 0x11, 0x9b, 0xfd, 0x9c, 0x73, 0xdb, 0x8b, 0x2f, 0x16,
+ 0x4d, 0x70, 0x3d, 0x94, 0x1d, 0xae, 0x20, 0x19, 0x42, 0x08, 0x63, 0x5d,
+ 0xf2, 0xed, 0x16, 0x4e, 0xf4, 0xd6, 0x1b, 0x0a, 0x26, 0x06, 0x0d, 0x4f,
+ 0xe8, 0x62, 0x12, 0x1f, 0x72, 0xfb, 0x8d, 0x4a, 0x94, 0xc2, 0xdd, 0xba,
+ 0xb3, 0x5f, 0x42, 0xbc, 0xd1, 0x34, 0x94, 0x8f, 0xc0, 0x52, 0x03, 0x7e,
+ 0xb6, 0x3c, 0xd3, 0x7f, 0xe7, 0xf1, 0x43, 0xef, 0x47, 0x8f, 0x5d, 0xe7,
+ 0xa0, 0x7a, 0xed, 0xbd, 0x7f, 0xe1, 0xcb, 0xe3, 0x03, 0x03, 0x10, 0x02,
+ 0x3f, 0x69, 0x0d, 0xc8, 0x42, 0x0a, 0x6c, 0xdf, 0xe7, 0xe1, 0xe7, 0x59,
+ 0x88, 0x69, 0x29, 0x29, 0xcd, 0xdf, 0x2b, 0x08, 0xd3, 0x90, 0xd2, 0x84,
+ 0x96, 0xfb, 0xb9, 0x19, 0xae, 0xcc, 0x61, 0xe6, 0xfb, 0x2b, 0xb8, 0x9b,
+ 0xb6, 0xc5, 0x6d, 0xb6, 0xec, 0xfc, 0xfb, 0x9b, 0xc3, 0x10, 0xe2, 0x90,
+ 0x8c, 0x34, 0x48, 0xea, 0xe2, 0x1a, 0x51, 0xcb, 0x1b, 0xbb, 0x9c, 0xd8,
+ 0x74, 0xc4, 0xc2, 0xf0, 0xc2, 0xd1, 0xd2, 0x33, 0xa5, 0x97, 0x52, 0xdb,
+ 0x45, 0x69, 0xa5, 0x25, 0x23, 0x13, 0x3f, 0x2d, 0x19, 0x09, 0xff, 0x12,
+ 0xb6, 0x3c, 0x5b, 0x9e, 0xa6, 0x1f, 0x7b, 0x17, 0x6b, 0x9c, 0x58, 0xcc,
+ 0xad, 0xc6, 0x80, 0xf4, 0xe0, 0xec, 0x41, 0xba, 0x37, 0x9b, 0xad, 0xaf,
+ 0xc0, 0x93, 0x00, 0xb0, 0x0c, 0x08, 0x69, 0x02, 0x7f, 0x23, 0x11, 0x49,
+ 0xd7, 0xad, 0x26, 0x24, 0x61, 0x68, 0xe3, 0x93, 0xb8, 0x57, 0xbc, 0xa8,
+ 0x6f, 0x46, 0x02, 0x85, 0x14, 0x82, 0xd1, 0xfb, 0xf4, 0xa7, 0x0b, 0x22,
+ 0xde, 0xef, 0x2a, 0xe9, 0x42, 0xc6, 0x8b, 0xfc, 0x81, 0x47, 0x32, 0xb0,
+ 0xf0, 0x00, 0x3b, 0x26, 0x20, 0x00, 0xe4, 0x34, 0x6f, 0x08, 0xc0, 0x5d,
+ 0x22, 0x3e, 0xdd, 0x82, 0xfb, 0x5e, 0xc4, 0x9a, 0x1b, 0xc9, 0xa5, 0xf4,
+ 0xf3, 0x49, 0xc2, 0xaf, 0x1a, 0x01, 0x98, 0x15, 0x28, 0x37, 0xb1, 0x4d,
+ 0xc6, 0xe4, 0x72, 0xd2, 0x6f, 0xfb, 0x33, 0x08, 0x22, 0xdf, 0x42, 0x90,
+ 0x63, 0x8c, 0xdf, 0x8e, 0x69, 0xfb, 0x94, 0xa5, 0x2e, 0x0b, 0x5b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00, 0x01, 0x14, 0x83, 0xfb, 0xfd,
+ 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0x00, 0x00, 0x01, 0x15, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x16, 0x83,
+ 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0x00, 0x00, 0x01, 0x17, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01,
+ 0x18, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x19, 0x83, 0xfb, 0xfd, 0x29,
+ 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00,
+ 0x00, 0x01, 0x1a, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1b, 0x83, 0xfb,
+ 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0x00, 0x00, 0x01, 0x1c, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1d,
+ 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1e, 0x83, 0xfb, 0xfd, 0x29, 0x49,
+ 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00,
+ 0x01, 0x1f, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x20, 0x83, 0xfb, 0xfd,
+ 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0x00, 0x00, 0x01, 0x21, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x22, 0x83,
+ 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0x00, 0x00, 0x01, 0x23, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01,
+ 0x24, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0xb7,
+};
diff --git a/server/suspend.h b/server/suspend.h
new file mode 100644
index 0000000..f04ad42
--- /dev/null
+++ b/server/suspend.h
@@ -0,0 +1,41 @@
+/*
+ * $Id: suspend.h,v 1.1 2004/12/30 22:44:26 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SUSPEND_H
+#define VDR_STREAMDEV_SUSPEND_H
+
+#include <vdr/player.h>
+
+class cSuspendLive: public cPlayer, cThread {
+private:
+ bool m_Active;
+
+protected:
+ virtual void Activate(bool On);
+ virtual void Action(void);
+
+ void Stop(void);
+
+public:
+ cSuspendLive(void);
+ virtual ~cSuspendLive();
+
+ bool IsActive(void) const { return m_Active; }
+};
+
+class cSuspendCtl: public cControl {
+private:
+ cSuspendLive *m_Suspend;
+ static bool m_Active;
+
+public:
+ cSuspendCtl(void);
+ virtual ~cSuspendCtl();
+ virtual void Hide(void) {}
+ virtual eOSState ProcessKey(eKeys Key);
+
+ static bool IsActive(void) { return m_Active; }
+};
+
+#endif // VDR_STREAMDEV_SUSPEND_H
diff --git a/streamdev-client.c b/streamdev-client.c
new file mode 100644
index 0000000..093a166
--- /dev/null
+++ b/streamdev-client.c
@@ -0,0 +1,57 @@
+/*
+ * streamdev.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id: streamdev-client.c,v 1.1 2004/12/30 22:43:59 lordjaxom Exp $
+ */
+
+#include "streamdev-client.h"
+#include "client/device.h"
+#include "client/setup.h"
+#include "client/menu.h"
+#include "i18n.h"
+
+const char *cPluginStreamdevClient::DESCRIPTION = "VTP Streaming Client";
+
+cPluginStreamdevClient::cPluginStreamdevClient(void) {
+}
+
+cPluginStreamdevClient::~cPluginStreamdevClient() {
+}
+
+const char *cPluginStreamdevClient::Description(void) {
+ return tr(DESCRIPTION);
+}
+
+bool cPluginStreamdevClient::Start(void) {
+ i18n_name = Name();
+ RegisterI18n(Phrases);
+
+ cStreamdevDevice::Init();
+
+ return true;
+}
+
+void cPluginStreamdevClient::Housekeeping(void) {
+ if (StreamdevClientSetup.StartClient && StreamdevClientSetup.SyncEPG)
+ ClientSocket.SynchronizeEPG();
+}
+
+const char *cPluginStreamdevClient::MainMenuEntry(void) {
+ return StreamdevClientSetup.StartClient ? tr("Streaming Control") : NULL;
+}
+
+cOsdObject *cPluginStreamdevClient::MainMenuAction(void) {
+ return StreamdevClientSetup.StartClient ? new cStreamdevMenu : NULL;
+}
+
+cMenuSetupPage *cPluginStreamdevClient::SetupMenu(void) {
+ return new cStreamdevClientMenuSetupPage;
+}
+
+bool cPluginStreamdevClient::SetupParse(const char *Name, const char *Value) {
+ return StreamdevClientSetup.SetupParse(Name, Value);
+}
+
+VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this!
diff --git a/streamdev-client.h b/streamdev-client.h
new file mode 100644
index 0000000..ecf4579
--- /dev/null
+++ b/streamdev-client.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: streamdev-client.h,v 1.1 2004/12/30 22:43:59 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEVCLIENT_H
+#define VDR_STREAMDEVCLIENT_H
+
+#include "common.h"
+
+#include <vdr/plugin.h>
+
+class cPluginStreamdevClient : public cPlugin {
+private:
+ static const char *DESCRIPTION;
+
+public:
+ cPluginStreamdevClient(void);
+ virtual ~cPluginStreamdevClient();
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void);
+ virtual bool Start(void);
+ virtual void Housekeeping(void);
+ virtual const char *MainMenuEntry(void);
+ virtual cOsdObject *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+};
+
+#endif // VDR_STREAMDEVCLIENT_H
diff --git a/streamdev-server.c b/streamdev-server.c
new file mode 100644
index 0000000..b09a85d
--- /dev/null
+++ b/streamdev-server.c
@@ -0,0 +1,72 @@
+/*
+ * streamdev.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id: streamdev-server.c,v 1.1 2004/12/30 22:43:59 lordjaxom Exp $
+ */
+
+#include "streamdev-server.h"
+#include "server/setup.h"
+#include "server/server.h"
+#include "server/suspend.h"
+#include "i18n.h"
+
+const char *cPluginStreamdevServer::DESCRIPTION = "VDR Streaming Server";
+
+cPluginStreamdevServer::cPluginStreamdevServer(void) {
+}
+
+cPluginStreamdevServer::~cPluginStreamdevServer() {
+ cStreamdevServer::Exit();
+}
+
+const char *cPluginStreamdevServer::Description(void) {
+ return tr(DESCRIPTION);
+}
+
+bool cPluginStreamdevServer::Start(void) {
+ i18n_name = Name();
+ RegisterI18n(Phrases);
+
+ if (!StreamdevHosts.Load(STREAMDEVHOSTS, true, true)) {
+ esyslog("streamdev-server: error while loading %s", STREAMDEVHOSTS);
+ fprintf(stderr, "streamdev-server: error while loading %s\n");
+ if (access(STREAMDEVHOSTS, F_OK) != 0)
+ fprintf(stderr, " Please install streamdevhosts.conf into the path "
+ "printed above. Without it\n"
+ " no client will be able to access your streaming-"
+ "server. An example can be\n"
+ " found together with this plugin's sources.\n");
+ return false;
+ }
+
+ cStreamdevServer::Init();
+
+ return true;
+}
+
+bool cPluginStreamdevServer::Active(void) {
+ return cStreamdevServer::Active();
+}
+
+const char *cPluginStreamdevServer::MainMenuEntry(void) {
+ if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive())
+ return tr("Suspend Live TV");
+ return NULL;
+}
+
+cOsdObject *cPluginStreamdevServer::MainMenuAction(void) {
+ cControl::Launch(new cSuspendCtl);
+ return NULL;
+}
+
+cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void) {
+ return new cStreamdevServerMenuSetupPage;
+}
+
+bool cPluginStreamdevServer::SetupParse(const char *Name, const char *Value) {
+ return StreamdevServerSetup.SetupParse(Name, Value);
+}
+
+VDRPLUGINCREATOR(cPluginStreamdevServer); // Don't touch this!
diff --git a/streamdev-server.h b/streamdev-server.h
new file mode 100644
index 0000000..9362aac
--- /dev/null
+++ b/streamdev-server.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: streamdev-server.h,v 1.1 2004/12/30 22:43:59 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEVSERVER_H
+#define VDR_STREAMDEVSERVER_H
+
+#include "common.h"
+
+#include <vdr/plugin.h>
+
+class cPluginStreamdevServer : public cPlugin {
+private:
+ static const char *DESCRIPTION;
+
+public:
+ cPluginStreamdevServer(void);
+ virtual ~cPluginStreamdevServer();
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void);
+ virtual bool Start(void);
+ virtual bool Active(void);
+ virtual const char *MainMenuEntry(void);
+ virtual cOsdObject *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+};
+
+#endif // VDR_STREAMDEVSERVER_H
diff --git a/streamdevhosts.conf.example b/streamdevhosts.conf.example
new file mode 100644
index 0000000..6c13598
--- /dev/null
+++ b/streamdevhosts.conf.example
@@ -0,0 +1,13 @@
+#
+# streamdevhosts This file describes a number of host addresses that
+# are allowed to connect to the streamdev server running
+# with the Video Disk Recorder (VDR) on this system.
+# Syntax:
+#
+# IP-Address[/Netmask]
+#
+
+127.0.0.1 # always accept localhost
+#192.168.100.0/24 # any host on the local net
+#204.152.189.113 # a specific host
+#0.0.0.0/0 # any host on any net (USE THIS WITH CARE!)
diff --git a/tools/file.c b/tools/file.c
new file mode 100644
index 0000000..2283500
--- /dev/null
+++ b/tools/file.c
@@ -0,0 +1,95 @@
+#include "tools/file.h"
+
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+cTBFile::cTBFile(void) {
+}
+
+cTBFile::~cTBFile() {
+ Close();
+}
+
+bool cTBFile::Open(const cTBString &Filename, int Mode, mode_t Attribs) {
+ int filed;
+
+ if (IsOpen()) Close();
+
+ if ((filed = ::open(Filename, Mode, Attribs)) == -1)
+ return false;
+
+ if (!cTBSource::Open(filed))
+ return false;
+
+ m_Filename = Filename;
+ m_Anonymous = false;
+ return true;
+}
+
+bool cTBFile::Open(uint Fileno) {
+ if (IsOpen()) Close();
+
+ if (!cTBSource::Open(Fileno))
+ return false;
+
+ m_Filename.Format("<&%d>", Fileno);
+ m_Anonymous = true;
+ return true;
+}
+
+bool cTBFile::Close(void) {
+ bool ret = true;
+
+ if (!IsOpen())
+ ERRNUL(EBADF);
+
+ if (::close(*this) == -1)
+ ret = false;
+
+ if (!cTBSource::Close())
+ ret = false;
+
+ m_Filename.Clear();
+ return ret;
+}
+
+bool cTBFile::Unlink(void) const {
+ if (m_Filename.IsNull())
+ ERRNUL(ENOENT);
+
+ if (!IsOpen())
+ ERRNUL(EBADF);
+
+ if (m_Anonymous)
+ ERRNUL(EINVAL);
+
+ return cTBFile::Unlink(m_Filename);
+}
+
+bool cTBFile::Unlink(const cTBString &Filename) {
+ return (::unlink(Filename) != -1);
+}
+
+ssize_t cTBFile::Size(void) const {
+ struct stat buf;
+
+ if (!IsOpen())
+ ERRSYS(EBADF);
+
+ if (fstat(*this, &buf) == -1)
+ return -1;
+
+ return buf.st_size;
+}
+
+ssize_t cTBFile::Size(const cTBString &Filename) {
+ struct stat buf;
+
+ if (stat(Filename, &buf) == -1)
+ return -1;
+
+ return buf.st_size;
+}
diff --git a/tools/file.h b/tools/file.h
new file mode 100644
index 0000000..de63e74
--- /dev/null
+++ b/tools/file.h
@@ -0,0 +1,100 @@
+#ifndef TOOLBOX_FILE_H
+#define TOOLBOX_FILE_H
+
+#include "tools/tools.h"
+#include "tools/source.h"
+#include "tools/string.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* cTBFile provides a cTBSource-derived interface for input and output on UNIX
+ files. */
+
+class cTBFile: public cTBSource {
+private:
+ bool m_Anonymous;
+ cTBString m_Filename;
+
+ /* Unhide and forbid baseclass method */
+ virtual bool Open(int Fd, bool IsUnixFd = false) { return false; }
+
+public:
+ cTBFile(void);
+ virtual ~cTBFile();
+
+ /* enum eFileType represents the modes a file can be opened with. The full
+ open mode is one of the first three, maybe or'ed with one of the others.
+ */
+ enum eFileType {
+ ReadOnly = O_RDONLY,
+ WriteOnly = O_WRONLY,
+ ReadWrite = O_RDWR,
+
+ Create = O_CREAT,
+ Exclude = O_EXCL,
+ Truncate = O_TRUNC,
+ Append = O_APPEND
+ };
+
+ /* See cTBSource::SysRead()
+ Reimplemented for UNIX files. */
+ virtual ssize_t SysRead(void *Buffer, size_t Length) const;
+
+ /* See cTBSource::SysWrite()
+ Reimplemented for UNIX files. */
+ virtual ssize_t SysWrite(const void *Buffer, size_t Length) const;
+
+ /* Open() opens the file referred to by Filename according to the given
+ Mode. If the file is created, it receives the attributes given by
+ Attribs, defaulting to rw-------. Returns true on success and false on
+ error, setting errno appropriately. */
+ virtual bool Open(const cTBString &Filename, int Mode,
+ mode_t Attribs = S_IRUSR + S_IWUSR);
+
+ /* Open() associates this file object with Fileno. Fileno must refer to a
+ previously opened file descriptor, which will be set non-blocking by
+ this call. If successful, true is returned, false otherwise and errno
+ is set appropriately. */
+ virtual bool Open(uint Fileno);
+
+ /* Close() closes the associated file descriptor and releases all
+ structures. Returns true on success and false otherwise, setting errno
+ appropriately. The object is in the closed state afterwards, even if
+ an error occured. */
+ virtual bool Close(void);
+
+ /* Unlink() unlinks (deletes) the associated file from the underlying
+ filesystem. Returns true on success and false otherwise, setting errno
+ appropriately. The file must be opened by filename to use this. */
+ virtual bool Unlink(void) const;
+
+ /* Unlink() unlinks (deletes) the file referred to by Filename from the
+ underlying filesystem. Returns true on success and false otherwise,
+ setting errno appropriately. */
+ static bool Unlink(const cTBString &Filename);
+
+ /* Size() returns the current size of the associated file. Returns the
+ exact size of the file in bytes. Returns -1 on error, setting errno to
+ an appropriate value. */
+ virtual ssize_t Size(void) const;
+
+ /* Size() returns the current size of the file referred to by Filename.
+ Symbolic links are followed (the size of the link-target is returned).
+ Returns the exact size of the file in bytes. Returns -1 on error,
+ setting errno to an appropriate value. */
+ static ssize_t Size(const cTBString &Filename);
+};
+
+inline ssize_t cTBFile::SysRead(void *Buffer, size_t Length) const {
+ return ::read(*this, Buffer, Length);
+}
+
+inline ssize_t cTBFile::SysWrite(const void *Buffer, size_t Length) const {
+ return ::write(*this, Buffer, Length);
+}
+
+
+#endif // TOOLBOX_FILE_H
diff --git a/tools/select.c b/tools/select.c
new file mode 100644
index 0000000..77229c8
--- /dev/null
+++ b/tools/select.c
@@ -0,0 +1,52 @@
+#include "tools/select.h"
+
+#include <vdr/tools.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+cTBSelect::cTBSelect(void) {
+ Clear();
+}
+
+cTBSelect::~cTBSelect() {
+}
+
+int cTBSelect::Select(uint TimeoutMs) {
+ struct timeval tv;
+ time_t st, et;
+ ssize_t res;
+ int ms;
+
+ tv.tv_usec = (TimeoutMs % 1000) * 1000;
+ tv.tv_sec = TimeoutMs / 1000;
+
+ if (TimeoutMs == 0)
+ return ::select(m_MaxFiled + 1, &m_Rfds, &m_Wfds, NULL, &tv);
+
+ st = time_ms();
+ ms = TimeoutMs;
+ while (ms > 0 && (res = ::select(m_MaxFiled + 1, &m_Rfds, &m_Wfds, NULL,
+ &tv)) == -1 && errno == EINTR) {
+ et = time_ms();
+ ms -= et - st;
+ tv.tv_usec = (ms % 1000) * 1000;
+ tv.tv_sec = ms / 1000;
+ st = et;
+ }
+ if (ms <= 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ return res;
+}
+
+int cTBSelect::Select(void) {
+ ssize_t res;
+ while ((res = ::select(m_MaxFiled + 1, &m_Rfds, &m_Wfds, NULL, NULL)) == -1
+ && errno == EINTR)
+ ;
+ return res;
+}
diff --git a/tools/select.h b/tools/select.h
new file mode 100644
index 0000000..7e873e2
--- /dev/null
+++ b/tools/select.h
@@ -0,0 +1,75 @@
+#ifndef TOOLBOX_SELECT_H
+#define TOOLBOX_SELECT_H
+
+#include "tools/tools.h"
+
+#include <sys/types.h>
+
+/* cTBSelect provides an interface for polling UNIX-like file descriptors. */
+
+class cTBSelect {
+private:
+ int m_MaxFiled;
+
+ fd_set m_Rfds;
+ fd_set m_Wfds;
+
+public:
+ cTBSelect(void);
+ virtual ~cTBSelect();
+
+ /* Clear() resets the object for use in a new Select() call. All file
+ descriptors and their previous states are invalidated. */
+ virtual void Clear(void);
+
+ /* Add() adds a file descriptor to be polled in the next Select() call.
+ That call polls if the file is readable if Output is set to false,
+ writeable otherwise. */
+ virtual bool Add(int Filed, bool Output = false);
+
+ /* Select() polls all descriptors added by Add() and returns as soon as
+ one of those changes state (gets readable/writeable), or after
+ TimeoutMs milliseconds, whichever happens first. It returns the number
+ of filedescriptors that have changed state. On error, -1 is returned
+ and errno is set appropriately. */
+ virtual int Select(uint TimeoutMs);
+
+ /* Select() polls all descriptors added by Add() and returns as soon as
+ one of those changes state (gets readable/writeable). It returns the
+ number of filedescriptors that have changed state. On error, -1 is
+ returned and errno is set appropriately. */
+ virtual int Select(void);
+
+ /* CanRead() returns true if the descriptor has changed to readable during
+ the last Select() call. Otherwise false is returned. */
+ virtual bool CanRead(int FileNo) const;
+
+ /* CanWrite() returns true if the descriptor has changed to writeable
+ during the last Select() call. Otherwise false is returned. */
+ virtual bool CanWrite(int FileNo) const;
+};
+
+inline void cTBSelect::Clear(void) {
+ FD_ZERO(&m_Rfds);
+ FD_ZERO(&m_Wfds);
+ m_MaxFiled = -1;
+}
+
+inline bool cTBSelect::Add(int Filed, bool Output /* = false */) {
+ if (Filed < 0) return false;
+ FD_SET(Filed, Output ? &m_Wfds : &m_Rfds);
+ if (Filed > m_MaxFiled) m_MaxFiled = Filed;
+ return true;
+}
+
+inline bool cTBSelect::CanRead(int FileNo) const {
+ if (FileNo < 0) return false;
+ return FD_ISSET(FileNo, &m_Rfds);
+}
+
+inline bool cTBSelect::CanWrite(int FileNo) const {
+ if (FileNo < 0) return false;
+ return FD_ISSET(FileNo, &m_Wfds);
+}
+
+#endif // TOOLBOX_SELECT_H
diff --git a/tools/shared.c b/tools/shared.c
new file mode 100644
index 0000000..ef20969
--- /dev/null
+++ b/tools/shared.c
@@ -0,0 +1,90 @@
+#include "tools/shared.h"
+
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+
+cSharedData *cSharedData::Construct (size_t Length) {
+ size_t reallength = sizeof(cSharedData) + Length;
+ cSharedData *ret = (cSharedData*)new char[reallength];
+
+ ret->m_Length = Length;
+ ret->m_NumRefs = 0;
+ return ret;
+}
+
+cTBShared::cTBShared(void) {
+ m_Buffer = NULL;
+}
+
+cTBShared::cTBShared (const cTBShared &src) {
+ m_Buffer = src.m_Buffer;
+ if (m_Buffer)
+ ++*m_Buffer;
+}
+
+cTBShared::~cTBShared () {
+ if (m_Buffer)
+ Release();
+}
+
+void cTBShared::Clear () {
+ if (m_Buffer)
+ Release();
+ m_Buffer = 0;
+}
+
+void cTBShared::Set (const cTBShared &src) {
+ if (m_Buffer)
+ Release();
+
+ m_Buffer = src.m_Buffer;
+ if (m_Buffer)
+ ++*m_Buffer;
+}
+
+void cTBShared::Release () {
+ CHECK_PTR(m_Buffer);
+
+ if (--*m_Buffer == 0)
+ delete[] (char*)m_Buffer;
+
+ m_Buffer = 0;
+}
+
+void cTBShared::Release(uint newsize) {
+ CHECK_PTR(m_Buffer);
+
+ Allocate(newsize, true);
+}
+
+void cTBShared::Exclusive () {
+ CHECK_PTR(m_Buffer);
+
+ if (m_Buffer->Refs() == 1)
+ return;
+
+ cSharedData *copy = cSharedData::Construct(m_Buffer->Size());
+ memcpy(*copy, *m_Buffer, m_Buffer->Size());
+
+ Release();
+
+ m_Buffer = copy;
+ ++*m_Buffer;
+}
+
+void cTBShared::Allocate (size_t len, bool keep /* = false */) {
+ if (m_Buffer && (m_Buffer->Refs() == 1) && (m_Buffer->Size() == len))
+ return;
+
+ cSharedData *newBuffer = cSharedData::Construct(len);
+ if (m_Buffer) {
+ if (keep)
+ memcpy(*newBuffer, *m_Buffer, len < m_Buffer->Size() ? len : m_Buffer->Size());
+
+ Release();
+ }
+ m_Buffer = newBuffer;
+ ++*m_Buffer;
+}
+
diff --git a/tools/shared.h b/tools/shared.h
new file mode 100644
index 0000000..bacb708
--- /dev/null
+++ b/tools/shared.h
@@ -0,0 +1,65 @@
+#ifndef TOOLBOX_SHARED_H
+#define TOOLBOX_SHARED_H
+
+#include "tools/tools.h"
+
+struct cSharedData {
+private:
+ uint m_Length;
+ uint m_NumRefs;
+
+public:
+ static cSharedData *Construct (size_t Length);
+
+ operator char * () { return this ? (char*)(this+1) : 0; }
+
+ uint operator++ () { return ++m_NumRefs; }
+ uint operator-- () { return --m_NumRefs; }
+
+ size_t Size() const { return m_Length; }
+
+ uint Refs () const { return m_NumRefs; }
+};
+
+class cTBShared {
+private:
+ cSharedData *m_Buffer;
+
+protected:
+ void Release();
+ void Exclusive();
+ void Allocate(size_t len, bool keep = false);
+
+ char *Buffer() const { return m_Buffer ? (char*)*m_Buffer : (char*)0; }
+
+public:
+ cTBShared (void);
+ cTBShared (const cTBShared &src);
+ virtual ~cTBShared ();
+
+ virtual void Clear ();
+ virtual void Set (const cTBShared &src);
+
+ virtual char *Buffer (uint size);
+ virtual void Release (uint newsize);
+
+ cTBShared &operator= (const cTBShared &src) { Set(src); return *this; }
+
+ operator const void * () const { return m_Buffer ? (const void*)*m_Buffer : (const void*)0; }
+ operator void * () const { return m_Buffer ? (void*)*m_Buffer : (void*)0; }
+
+ operator const char * () const { return m_Buffer ? (const char*)*m_Buffer : (const char*)0; }
+
+ size_t Size() const { return m_Buffer ? m_Buffer->Size() : 0; }
+ size_t Length() const { return m_Buffer ? m_Buffer->Size() : 0; }
+
+ // friend cSource &operator>> (cSource &dest, cTBShared &str);
+};
+
+inline char *cTBShared::Buffer(uint size) {
+ if ((!m_Buffer) || (m_Buffer->Refs() > 1) || (size > m_Buffer->Size()))
+ Allocate(size, true);
+ return Buffer();
+}
+
+#endif // TOOLBOX_SHARED_H
diff --git a/tools/socket.c b/tools/socket.c
new file mode 100644
index 0000000..46d7b9a
--- /dev/null
+++ b/tools/socket.c
@@ -0,0 +1,135 @@
+#include "tools/socket.h"
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+cTBSocket::cTBSocket(int Type) {
+ memset(&m_LocalAddr, 0, sizeof(m_LocalAddr));
+ memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr));
+ m_Type = Type;
+}
+
+cTBSocket::~cTBSocket() {
+ if (IsOpen()) Close();
+}
+
+bool cTBSocket::Connect(const cTBString &Host, unsigned int Port) {
+ socklen_t len;
+ int socket;
+
+ if (IsOpen()) Close();
+
+ if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1)
+ return false;
+
+ m_LocalAddr.sin_family = AF_INET;
+ m_LocalAddr.sin_port = 0;
+ m_LocalAddr.sin_addr.s_addr = INADDR_ANY;
+ if (::bind(socket, (struct sockaddr*)&m_LocalAddr, sizeof(m_LocalAddr))
+ == -1)
+ return false;
+
+ m_RemoteAddr.sin_family = AF_INET;
+ m_RemoteAddr.sin_port = htons(Port);
+ m_RemoteAddr.sin_addr.s_addr = inet_addr(Host);
+ if (::connect(socket, (struct sockaddr*)&m_RemoteAddr,
+ sizeof(m_RemoteAddr)) == -1)
+ return false;
+
+ len = sizeof(struct sockaddr_in);
+ if (::getpeername(socket, (struct sockaddr*)&m_RemoteAddr, &len) == -1)
+ return false;
+
+ len = sizeof(struct sockaddr_in);
+ if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &len) == -1)
+ return false;
+
+ return cTBSource::Open(socket);
+}
+
+bool cTBSocket::Listen(const char *Ip, unsigned int Port, int BackLog) {
+ int val;
+ socklen_t len;
+ int socket;
+
+ if (IsOpen()) Close();
+
+ if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1)
+ return false;
+
+ val = 1;
+ if (::setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) == -1)
+ return false;
+
+ m_LocalAddr.sin_family = AF_INET;
+ m_LocalAddr.sin_port = htons(Port);
+ m_LocalAddr.sin_addr.s_addr = inet_addr(Ip);
+ if (::bind(socket, (struct sockaddr*)&m_LocalAddr, sizeof(m_LocalAddr))
+ == -1)
+ return false;
+
+ len = sizeof(struct sockaddr_in);
+ if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &len) == -1)
+ return false;
+
+ if (m_Type == SOCK_STREAM && ::listen(socket, BackLog) == -1)
+ return false;
+
+ if (!cTBSource::Open(socket))
+ return false;
+
+ return true;
+}
+
+bool cTBSocket::Accept(const cTBSocket &Listener) {
+ socklen_t addrlen;
+ int socket;
+
+ if (IsOpen()) Close();
+
+ addrlen = sizeof(struct sockaddr_in);
+ if ((socket = ::accept(Listener, (struct sockaddr*)&m_RemoteAddr,
+ &addrlen)) == -1)
+ return false;
+
+ addrlen = sizeof(struct sockaddr_in);
+ if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &addrlen) == -1)
+ return false;
+
+ if (!cTBSource::Open(socket))
+ return false;
+
+ return true;
+}
+
+RETURNS(cTBSocket, cTBSocket::Accept(void) const, ret)
+ ret.Accept(*this);
+RETURN(ret)
+
+bool cTBSocket::Close(void) {
+ bool ret = true;
+
+ if (!IsOpen())
+ ERRNUL(EBADF);
+
+ if (::close(*this) == -1)
+ ret = false;
+
+ if (!cTBSource::Close())
+ ret = false;
+
+ memset(&m_LocalAddr, 0, sizeof(m_LocalAddr));
+ memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr));
+
+ return ret;
+}
+
+bool cTBSocket::Shutdown(int how) {
+ if (!IsOpen())
+ ERRNUL(EBADF);
+
+ return ::shutdown(*this, how) != -1;
+}
diff --git a/tools/socket.h b/tools/socket.h
new file mode 100644
index 0000000..a4b06ab
--- /dev/null
+++ b/tools/socket.h
@@ -0,0 +1,108 @@
+#ifndef TOOLBOX_SOCKET_H
+#define TOOLBOX_SOCKET_H
+
+#include "tools/tools.h"
+#include "tools/source.h"
+#include "tools/string.h"
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/* cTBSocket provides a cTBSource-derived interface for input and output on
+ TCP/IPv4-sockets. */
+
+class cTBSocket: public cTBSource {
+private:
+ struct sockaddr_in m_LocalAddr;
+ struct sockaddr_in m_RemoteAddr;
+
+ int m_Type;
+
+public:
+ cTBSocket(int Type = SOCK_STREAM);
+ virtual ~cTBSocket();
+
+ /* See cTBSource::SysRead()
+ Reimplemented for TCP/IPv4 sockets. */
+ virtual ssize_t SysRead(void *Buffer, size_t Length) const;
+
+ /* See cTBSource::SysWrite()
+ Reimplemented for TCP/IPv4 sockets. */
+ virtual ssize_t SysWrite(const void *Buffer, size_t Length) const;
+
+ /* Connect() tries to connect an available local socket to the port given
+ by Port of the target host given by Host in numbers-and-dots notation
+ (i.e. "212.43.45.21"). Returns true if the connection attempt was
+ successful and false otherwise, setting errno appropriately. */
+ virtual bool Connect(const cTBString &Host, uint Port);
+
+ /* Shutdown() shuts down one or both ends of a socket. If called with How
+ set to SHUT_RD, further reads on this socket will be denied. If called
+ with SHUT_WR, all writes are denied. Called with SHUT_RDWR, all firther
+ action on this socket will be denied. Returns true on success and false
+ otherwise, setting errno appropriately. */
+ virtual bool Shutdown(int How);
+
+ /* Close() closes the associated socket and releases all structures.
+ Returns true on success and false otherwise, setting errno
+ appropriately. The object is in the closed state afterwards, regardless
+ of any errors. */
+ virtual bool Close(void);
+
+ /* Listen() listens on the local port Port for incoming connections. The
+ BackLog parameter defines the maximum length the queue of pending
+ connections may grow to. Returns true if the object is listening on
+ the specified port and false otherwise, setting errno appropriately. */
+ virtual bool Listen(const char *Ip, uint Port, int BackLog);
+
+ /* Accept() returns a newly created cTBSocket, which is connected to the
+ first connection request on the queue of pending connections of a
+ listening socket. If no connection request was pending, or if any other
+ error occured, the resulting cTBSocket is closed. */
+ virtual cTBSocket Accept(void) const;
+
+ /* Accept() extracts the first connection request on the queue of pending
+ connections of the listening socket Listener and connects it to this
+ object. Returns true on success and false otherwise, setting errno to
+ an appropriate value. */
+ virtual bool Accept(const cTBSocket &Listener);
+
+ /* LocalPort() returns the port number this socket is connected to locally.
+ The result is undefined for a non-open socket. */
+ int LocalPort(void) const { return ntohs(m_LocalAddr.sin_port); }
+
+ /* RemotePort() returns the port number this socket is connected to on the
+ remote side. The result is undefined for a non-open socket. */
+ int RemotePort(void) const { return ntohs(m_RemoteAddr.sin_port); }
+
+ /* LocalIp() returns the internet address in numbers-and-dots notation of
+ the interface this socket is connected to locally. This can be
+ "0.0.0.0" for a listening socket listening to all interfaces. If the
+ socket is in its closed state, the result is undefined. */
+ cTBString LocalIp(void) const { return inet_ntoa(m_LocalAddr.sin_addr); }
+
+ /* RemoteIp() returns the internet address in numbers-and-dots notation of
+ the interface this socket is connected to on the remote side. If the
+ socket is in its closed state, the result is undefined. */
+ cTBString RemoteIp(void) const { return inet_ntoa(m_RemoteAddr.sin_addr); }
+
+ in_addr_t LocalIpAddr(void) const { return m_LocalAddr.sin_addr.s_addr; }
+ in_addr_t RemoteIpAddr(void) const { return m_RemoteAddr.sin_addr.s_addr; }
+
+ int Type(void) const { return m_Type; }
+};
+
+inline ssize_t cTBSocket::SysRead(void *Buffer, size_t Length) const {
+ if (m_Type == SOCK_DGRAM) {
+ socklen_t len = sizeof(m_RemoteAddr);
+ return ::recvfrom(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, &len);
+ } else
+ return ::recv(*this, Buffer, Length, 0);
+}
+
+inline ssize_t cTBSocket::SysWrite(const void *Buffer, size_t Length) const {
+ return ::send(*this, Buffer, Length, 0);
+}
+
+#endif // TOOLBOX_SOCKET_H
diff --git a/tools/source.c b/tools/source.c
new file mode 100644
index 0000000..91c36c8
--- /dev/null
+++ b/tools/source.c
@@ -0,0 +1,195 @@
+#include "tools/source.h"
+#include "tools/select.h"
+#include "common.h"
+
+#include <vdr/tools.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+cTBSource::cTBSource(void) {
+ m_BytesRead = 0;
+ m_BytesWritten = 0;
+ m_Filed = -1;
+}
+
+bool cTBSource::Open(int Filed, bool IsUnixFd) {
+ if (IsOpen())
+ Close();
+
+ m_Filed = Filed;
+ if (IsUnixFd && ::fcntl(m_Filed, F_SETFL, O_NONBLOCK) == -1)
+ return false;
+
+ return true;
+}
+
+cTBSource::~cTBSource() {
+}
+
+bool cTBSource::Close(void) {
+ if (!IsOpen()) {
+ errno = EBADF;
+ return false;
+ }
+
+ m_Filed = -1;
+ return true;
+}
+
+ssize_t cTBSource::Read(void *Buffer, size_t Length) {
+ ssize_t res;
+ while ((res = SysRead(Buffer, Length)) < 0 && errno == EINTR)
+ errno = 0;
+ if (res > 0) m_BytesRead += res;
+ return res;
+}
+
+ssize_t cTBSource::Write(const void *Buffer, size_t Length) {
+ ssize_t res;
+ while ((res = SysWrite(Buffer, Length)) < 0 && errno == EINTR)
+ errno = 0;
+ if (res > 0) m_BytesWritten += res;
+ return res;
+}
+
+bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) {
+ cTBSelect sel;
+ time_t st;
+ int ms, offs;
+
+ st = time_ms();
+ ms = TimeoutMs;
+ offs = 0;
+ while (Length > 0) {
+ int b;
+
+ sel.Clear();
+ sel.Add(m_Filed, true);
+ if (sel.Select(ms) == -1)
+ return false;
+
+ if (sel.CanWrite(m_Filed)) {
+ if ((b = Write((char*)Buffer + offs, Length)) == -1)
+ return false;
+ offs += b;
+ Length -= b;
+ }
+
+ ms -= time_ms() - st;
+ if (ms <= 0) {
+ errno = ETIMEDOUT;
+ return false;
+ }
+ }
+ return true;
+}
+
+ssize_t cTBSource::ReadUntil(void *Buffer, size_t Length, const char *Seq,
+ uint TimeoutMs) {
+ char *offs;
+ time_t st;
+ int seqlen, ms;
+ size_t olen;
+ cTBSelect sel;
+
+ seqlen = strlen(Seq);
+ if ((offs = (char*)memmem(m_LineBuffer, m_LineBuffer.Length(), Seq, seqlen))){
+ olen = offs - m_LineBuffer;
+ if (olen >= Length) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(Buffer, m_LineBuffer, olen);
+ m_LineBuffer = m_LineBuffer.Mid(olen + seqlen);
+ Dprintf("ReadUntil: Served from Linebuffer: %d, |%.*s|\n", olen, olen - 1,
+ (char*)Buffer);
+ return olen;
+ }
+
+ st = time_ms();
+ ms = TimeoutMs;
+ while (m_LineBuffer.Length() < BUFSIZ) {
+ int b;
+
+ sel.Clear();
+ sel.Add(m_Filed, false);
+
+ if (sel.Select(ms) == -1)
+ return -1;
+
+ if (sel.CanRead(m_Filed)) {
+ offs = m_LineBuffer.Buffer(BUFSIZ);
+ if ((b = Read(offs + m_LineBuffer.Length(), BUFSIZ
+ - m_LineBuffer.Length())) == -1)
+ return -1;
+
+ m_LineBuffer.Release(m_LineBuffer.Length() + b);
+ if ((offs = (char*)memmem(m_LineBuffer, m_LineBuffer.Length(), Seq,
+ seqlen))) {
+ olen = offs - m_LineBuffer;
+ if (olen >= Length) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(Buffer, m_LineBuffer, olen);
+ m_LineBuffer = m_LineBuffer.Mid(olen + seqlen, m_LineBuffer.Length()
+ - olen - seqlen);
+ Dprintf("ReadUntil: Served after Read: %d, |%.*s|\n", olen, olen-1,
+ (char*)Buffer);
+ return olen;
+ }
+ }
+
+ ms -= time_ms() - st;
+ if (ms <= 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+ errno = ENOBUFS;
+ return -1;
+
+
+
+/*
+ cTBSelect sel;
+ time_t st, et;
+ int ms, seqlen, offs;
+
+ seqlen = strlen(Seq);
+ st = time_ms();
+ ms = TimeoutMs;
+ offs = 0;
+ while (Length > 0) {
+ int b;
+
+ sel.Clear();
+ sel.Add(m_Filed, false);
+ if (sel.Select(ms) == -1)
+ return -1;
+
+ if (sel.CanRead(m_Filed)) {
+ if ((b = Read((char*)Buffer + offs, Length)) == -1)
+ return -1;
+
+ offs += b;
+ Length -= b;
+
+ if (memmem(Buffer, offs, Seq, seqlen) != NULL)
+ return offs;
+ }
+
+ et = time_ms();
+ ms -= et - st;
+ if (ms <= 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+ errno = ENOBUFS;
+ return -1;
+*/
+}
+
diff --git a/tools/source.h b/tools/source.h
new file mode 100644
index 0000000..09536bc
--- /dev/null
+++ b/tools/source.h
@@ -0,0 +1,107 @@
+#ifndef TOOLBOX_SOURCE_H
+#define TOOLBOX_SOURCE_H
+
+#include "tools/tools.h"
+#include "tools/string.h"
+
+#include <sys/types.h>
+
+/* cTBSource provides an abstract interface for input and output. It can
+ be used to have common access to different types of UNIX-files. */
+
+class cTBSource {
+private:
+ int m_Filed;
+
+ size_t m_BytesRead;
+ size_t m_BytesWritten;
+
+ cTBString m_LineBuffer;
+
+public:
+ cTBSource(void);
+ virtual ~cTBSource();
+
+ /* SysRead() implements the low-level read on the source. It will store
+ data into the area pointed to by Buffer, which is at least Length
+ bytes in size. It will return the exact number of bytes read (which
+ can be fewer than requested). On error, -1 is returned, and errno
+ is set to an appropriate value. */
+ virtual ssize_t SysRead(void *Buffer, size_t Length) const = 0;
+
+ /* SysWrite() implements the low-level write on the source. It will write
+ at most Length bytes of the data pointed to by Buffer. It will return
+ the exact number of bytes written (which can be fewer than requested).
+ On error, -1 is returned, and errno is set to an appropriate value. */
+ virtual ssize_t SysWrite(const void *Buffer, size_t Length) const = 0;
+
+ /* IsOpen() returns true, if this source refers to a valid descriptor.
+ It is not checked whether this source is really open, so only if
+ opened by the appropriate Methods this function will return the
+ correct value */
+ virtual bool IsOpen(void) const { return m_Filed != -1; }
+
+ /* Open() associates this source with the descriptor Filed, setting it
+ to non-blocking mode if IsUnixFd in true. Returns true on success,
+ and false on error, setting errno to appropriately.
+ If you want to implement sources that can't be represented by UNIX
+ filedescriptors, you can use Filed to store any useful information
+ about the source.
+ This must be called by any derivations in an appropriate Method (like
+ open for files, connect for sockets). */
+ virtual bool Open(int Filed, bool IsUnixFd = true);
+
+ /* Close() resets the source to the uninitialized state (IsOpen() == false)
+ and must be called by any derivations after really closing the source.
+ Returns true on success and false on error, setting errno appropriately.
+ The object is in closed state afterwards, even if an error occured. */
+ virtual bool Close(void);
+
+ /* Read() reads at most Length bytes into the storage pointed to by Buffer,
+ which must be at least Length bytes in size, using the SysRead()-
+ Interface. It retries if an EINTR occurs (i.e. the low-level call was
+ interrupted). It returns the exact number of bytes read (which can be
+ fewer than requested). On error, -1 is returned, and errno is set
+ appropriately. */
+ ssize_t Read(void *Buffer, size_t Length);
+
+ /* Write() writes at most Length bytes from the storage pointed to by
+ Buffer, using the SysWrite()-Interface. It retries if EINTR occurs
+ (i.e. the low-level call was interrupted). It returns the exact number
+ of bytes written (which can be fewer than requested). On error, -1 is
+ returned and errno is set appropriately. */
+ ssize_t Write(const void *Buffer, size_t Length);
+
+ /* TimedWrite() tries to write Length bytes from the storage pointed to by
+ Buffer within the time specified by TimeoutMs, using the Write()-
+ Interface. On success, true is returned. On error, false is returned
+ and errno is set appropriately. TimedRead only works on UNIX file
+ descriptor sources. */
+ bool TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs);
+
+ /* ReadUntil() tries to read at most Length bytes into the storage pointed
+ to by Buffer, which must be at least Length bytes in size, within the
+ time specified by TimeoutMs, using the Read()-Interface. Reading stops
+ after the character sequence Seq has been read and on end-of-file.
+ Returns the number of bytes read (if that is equal to Length, you have
+ to check if the buffer ends with Seq), or -1 on error, in which case
+ errno is set appropriately. */
+ ssize_t ReadUntil(void *Buffer, size_t Length, const char *Seq,
+ uint TimeoutMs);
+
+ /* BytesRead() returns the exact number of bytes read through the Read()
+ method since Close() has been called on this source (or since its
+ creation). */
+ size_t BytesRead(void) const { return m_BytesRead; }
+
+ /* BytesWritten() returns the exact number of bytes written through the
+ Write() method since Close() has been called on this source (or since
+ its creation). */
+ size_t BytesWritten(void) const { return m_BytesWritten; }
+
+ /* operator int() returns the descriptor (or informative number) associated
+ with this source. */
+ operator int() const { return m_Filed; }
+};
+
+#endif // TOOLBOX_SOURCE_H
diff --git a/tools/string.c b/tools/string.c
new file mode 100644
index 0000000..0897ece
--- /dev/null
+++ b/tools/string.c
@@ -0,0 +1,454 @@
+#include "tools/string.h"
+#ifdef TOOLBOX_REGEX
+# include "tools/regex.h"
+#endif
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <limits.h>
+#include <math.h>
+#include <errno.h>
+
+const cTBString cTBString::Null;
+
+cTBString::cTBString():
+ cTBShared(),
+ m_StringLen(0) {
+}
+
+cTBString::cTBString(const cTBString &src):
+ cTBShared(src),
+ m_StringLen(src.m_StringLen) {
+}
+
+cTBString::cTBString(const char *src) {
+ Set(src);
+}
+
+cTBString::cTBString(const uchar *src) {
+ Set(src);
+}
+
+cTBString::cTBString(char src) {
+ Set(src);
+}
+
+cTBString::~cTBString () {
+}
+
+void cTBString::Release(uint newsize) {
+ m_StringLen = newsize;
+ cTBShared::Release(m_StringLen + 1);
+ Buffer()[m_StringLen] = 0;
+}
+
+void cTBString::Clear() {
+ cTBShared::Clear();
+ m_StringLen = 0;
+}
+
+void cTBString::Set(const cTBString &String) {
+ cTBShared::Set(String);
+ m_StringLen = String.m_StringLen;
+}
+
+void cTBString::Set (const char *String) {
+ m_StringLen = strlen(String);
+ Allocate(m_StringLen + 1);
+
+ memcpy(Buffer(), String, m_StringLen);
+ Buffer()[m_StringLen] = 0;
+}
+
+void cTBString::Set (const uchar *String) {
+ Set((const char*)String);
+}
+
+void cTBString::Set (char Character) {
+ m_StringLen = 1;
+ Allocate(m_StringLen + 1);
+
+ Buffer()[0] = Character;
+ Buffer()[1] = 0;
+}
+
+void cTBString::Fill(char Character, int Length) {
+ if (Length != -1) {
+ m_StringLen = Length;
+ Allocate(m_StringLen + 1);
+ }
+ memset(Buffer(), Character, m_StringLen);
+ Buffer()[m_StringLen] = 0;
+}
+
+void cTBString::Append(const cTBString &src) {
+ Allocate(m_StringLen + src.m_StringLen + 1, true);
+
+ memcpy(Buffer() + m_StringLen, src.Buffer(), src.m_StringLen);
+ m_StringLen += src.m_StringLen;
+ Buffer()[m_StringLen] = 0;
+}
+
+void cTBString::Append(const char *src) {
+ uint len = strlen(src);
+ Allocate(m_StringLen + len + 1, true);
+
+ memcpy(Buffer() + m_StringLen, src, len);
+ m_StringLen += len;
+ Buffer()[m_StringLen] = 0;
+}
+
+void cTBString::Append(char src) {
+ Allocate(m_StringLen + 2, true);
+
+ Buffer()[m_StringLen] = src;
+ ++m_StringLen;
+ Buffer()[m_StringLen] = 0;
+}
+
+void cTBString::Prepend(const cTBString &src) {
+ Allocate(m_StringLen + src.m_StringLen + 1, true);
+
+ memmove(Buffer() + src.m_StringLen, Buffer(), m_StringLen);
+ memcpy(Buffer(), src.Buffer(), src.m_StringLen);
+ m_StringLen += src.m_StringLen;
+ Buffer()[m_StringLen] = 0;
+}
+
+void cTBString::Prepend(const char *src) {
+ uint len = strlen(src);
+ Allocate(m_StringLen + len + 1, true);
+
+ memmove(Buffer() + len, Buffer(), m_StringLen);
+ memcpy(Buffer(), src, len);
+ m_StringLen += len;
+ Buffer()[m_StringLen] = 0;
+}
+
+void cTBString::Prepend(char src) {
+ Allocate(m_StringLen + 2, true);
+
+ memmove(Buffer() + 1, Buffer(), m_StringLen);
+ Buffer()[0] = src;
+ Buffer()[++m_StringLen] = 0;
+}
+
+void cTBString::Insert(uint Index, const cTBString &String) {
+ Allocate(m_StringLen + String.m_StringLen + 1, true);
+
+ memmove(Buffer() + Index + String.m_StringLen, Buffer() + Index, m_StringLen - Index);
+ memcpy(Buffer() + Index, String.Buffer(), String.m_StringLen);
+ m_StringLen += String.m_StringLen;
+ Buffer()[m_StringLen] = 0;
+}
+
+void cTBString::Insert(uint Index, const char *String) {
+ uint len = strlen(String);
+ Allocate(m_StringLen + len + 1, true);
+
+ memmove(Buffer() + Index + len, Buffer() + Index, m_StringLen - Index);
+ memcpy(Buffer() + Index, String, len);
+ m_StringLen += len;
+ Buffer()[m_StringLen] = 0;
+}
+
+void cTBString::Insert(uint Index, char Character) {
+ Allocate(m_StringLen + 2, true);
+
+ memmove(Buffer() + Index + 1, Buffer() + Index, m_StringLen - Index);
+ Buffer()[Index] = Character;
+ Buffer()[++m_StringLen] = 0;
+}
+
+RETURNS(cTBString, cTBString::Left(uint count) const, ret)
+ if (count > m_StringLen)
+ count = m_StringLen;
+
+ ret.Allocate(count + 1);
+ memcpy(ret.Buffer(), Buffer(), count);
+ ret.Buffer()[count] = 0;
+ ret.m_StringLen = count;
+RETURN(ret)
+
+RETURNS(cTBString, cTBString::Right(uint count) const, ret)
+ if (count > m_StringLen)
+ count = m_StringLen;
+
+ ret.Allocate(count + 1);
+ memcpy(ret.Buffer(), Buffer() + m_StringLen - count, count);
+ ret.Buffer()[count] = 0;
+ ret.m_StringLen = count;
+RETURN(ret)
+
+RETURNS(cTBString, cTBString::Mid(int idx, int count) const, ret)
+ if (idx < 0)
+ idx = m_StringLen + idx;
+
+ if ((count < 0) || (count > (int)m_StringLen - idx))
+ count = m_StringLen - idx;
+
+ ret.Allocate(count + 1);
+ memcpy(ret.Buffer(), Buffer() + idx, count);
+ ret.Buffer()[count] = 0;
+ ret.m_StringLen = count;
+RETURN(ret)
+
+int cTBString::Find (const cTBString &String, uint Offset) const {
+ if (Offset >= m_StringLen)
+ return -1;
+
+ char *pos = strstr(Buffer() + Offset, String.Buffer());
+ if (pos) return (pos - Buffer());
+ else return -1;
+}
+
+int cTBString::Find (const char *String, uint Offset) const {
+ if (Offset >= m_StringLen)
+ return -1;
+
+ char *pos = strstr(Buffer() + Offset, String);
+ if (pos) return (pos - Buffer());
+ else return -1;
+}
+
+int cTBString::Find (char Character, uint Offset) const {
+ if (Offset >= m_StringLen)
+ return -1;
+
+ char *pos = strchr(Buffer() + Offset, Character);
+ if (pos) return (pos - Buffer());
+ else return -1;
+}
+
+#ifdef TOOLBOX_REGEX
+bool cTBString::Find (cTBRegEx &Regex, uint Offset) const {
+ return Regex.Match(Buffer(), Offset);
+}
+#endif
+
+void cTBString::Format (const char *fmt, ...) {
+ int n, size = 128;
+ va_list ap;
+
+ char *buf = Buffer(size);
+
+ while (1) {
+ va_start(ap, fmt);
+ n = vsnprintf(buf, size, fmt, ap);
+ va_end(ap);
+
+ if ((n > -1) && (n < size))
+ break;
+
+ if (n > -1)
+ size = n + 1;
+ else
+ size *= 2;
+
+ buf = Buffer(size);
+ }
+ Release(n);
+}
+
+void cTBString::Format(const cTBString &fmt, ...) {
+ int n, size = 128;
+ va_list ap;
+
+ char *buf = Buffer(size);
+
+ while (1) {
+ va_start(ap, &fmt);
+ n = vsnprintf(buf, size, fmt, ap);
+ va_end(ap);
+
+ if ((n > -1) && (n < size))
+ break;
+
+ if (n > -1)
+ size = n + 1;
+ else
+ size *= 2;
+
+ buf = Buffer(size);
+ }
+ Release(n);
+}
+
+template<cTBString::TOFUNC F>
+cTBString cTBString::ToAnything(void) const {
+ const char *src;
+ char *dest;
+ cTBString ret;
+
+ src = Buffer();
+ dest = ret.Buffer(m_StringLen + 1);
+
+ for (; src < Buffer() + m_StringLen; ++src, ++dest)
+ *dest = F(*src);
+
+ *dest = '\0';
+
+ ret.Release(m_StringLen);
+ return ret;
+}
+
+template<cTBString::ISFUNC F>
+bool cTBString::IsAnything(void) const {
+ const char *ptr = Buffer();
+
+ for (; ptr < Buffer() + m_StringLen; ++ptr)
+ if (!F(*ptr)) return false;
+
+ return true;
+}
+
+short cTBString::ToShort(bool *Ok) const {
+ long ret;
+ char *endptr;
+ bool res = false;
+
+ ret = strtol(Buffer(), &endptr, 0);
+
+ if (!IsEmpty() && *endptr == '\0' && ret >= SHRT_MIN && ret <= SHRT_MAX)
+ res = true;
+
+ if (Ok) *Ok = res;
+ return (short)ret;
+}
+
+ushort cTBString::ToUShort(bool *Ok) const {
+ ulong ret;
+ char *endptr;
+ bool res = false;
+
+ ret = strtoul(Buffer(), &endptr, 0);
+
+ if (!IsEmpty() && *endptr == '\0' && ret <= USHRT_MAX)
+ res = true;
+
+ if (Ok) *Ok = res;
+ return (ushort)ret;
+}
+
+int cTBString::ToInt(bool *Ok) const {
+ long ret;
+ char *endptr;
+ bool res = false;
+
+ ret = strtol(Buffer(), &endptr, 0);
+
+ if (!IsEmpty() && *endptr == '\0' && ret >= INT_MIN && ret <= INT_MAX)
+ res = true;
+
+ if (Ok) *Ok = res;
+ return (int)ret;
+}
+
+uint cTBString::ToUInt(bool *Ok) const {
+ ulong ret;
+ char *endptr;
+ bool res = false;
+
+ ret = strtoul(Buffer(), &endptr, 0);
+
+ if (!IsEmpty() && *endptr == '\0' && ret <= UINT_MAX)
+ res = true;
+
+ if (Ok) *Ok = res;
+ return (uint)ret;
+}
+
+long cTBString::ToLong(bool *Ok) const {
+ long ret;
+ char *endptr;
+ bool res = false;
+
+ errno = 0;
+ ret = strtol(Buffer(), &endptr, 0);
+
+ if (!IsEmpty() && *endptr == '\0' && errno != ERANGE)
+ res = true;
+
+ if (Ok) *Ok = res;
+ return (long)ret;
+}
+
+ulong cTBString::ToULong(bool *Ok) const {
+ ulong ret;
+ char *endptr;
+ bool res = false;
+
+ errno = 0;
+ ret = strtoul(Buffer(), &endptr, 0);
+
+ if (!IsEmpty() && *endptr == '\0' && errno != ERANGE)
+ res = true;
+
+ if (Ok) *Ok = res;
+ return (ulong)ret;
+}
+
+float cTBString::ToFloat(bool *Ok) const {
+ double ret;
+ char *endptr;
+ bool res = false;
+
+ ret = strtod(Buffer(), &endptr);
+
+ if (!IsEmpty() && *endptr == '\0' && errno != ERANGE)
+ res = true;
+
+ if (Ok) *Ok = res;
+ return (float)ret;
+}
+
+double cTBString::ToDouble(bool *Ok) const {
+ double ret;
+ char *endptr;
+ bool res = false;
+
+ errno = 0;
+ ret = strtol(Buffer(), &endptr, 0);
+
+ if (!IsEmpty() && *endptr == '\0' && errno != ERANGE)
+ res = true;
+
+ if (Ok) *Ok = res;
+ return (double)ret;
+}
+
+RETURNS(cTBString, cTBString::Number(short Num), ret)
+ ret.Format("%hd", Num);
+RETURN(ret)
+
+RETURNS(cTBString, cTBString::Number(ushort Num), ret)
+ ret.Format("%hu", Num);
+RETURN(ret)
+
+RETURNS(cTBString, cTBString::Number(int Num), ret)
+ ret.Format("%d", Num);
+RETURN(ret)
+
+RETURNS(cTBString, cTBString::Number(uint Num), ret)
+ ret.Format("%u", Num);
+RETURN(ret)
+
+RETURNS(cTBString, cTBString::Number(long Num), ret)
+ ret.Format("%ld", Num);
+RETURN(ret)
+
+RETURNS(cTBString, cTBString::Number(ulong Num), ret)
+ ret.Format("%lu", Num);
+RETURN(ret)
+
+RETURNS(cTBString, cTBString::Number(float Num), ret)
+ ret.Format("%f", Num);
+RETURN(ret)
+
+RETURNS(cTBString, cTBString::Number(double Num), ret)
+ ret.Format("%f", Num);
+RETURN(ret)
+
diff --git a/tools/string.h b/tools/string.h
new file mode 100644
index 0000000..2e81929
--- /dev/null
+++ b/tools/string.h
@@ -0,0 +1,353 @@
+#ifndef TOOLBOX_STRING_H
+#define TOOLBOX_STRING_H
+
+#include "tools/tools.h"
+#include "tools/shared.h"
+//#include "tools/source.h"
+
+#include <ctype.h>
+#include <stddef.h>
+#include <string.h>
+
+#ifdef TOOLBOX_REGEX
+class cTBRegEx;
+#endif
+
+class cTBString: public cTBShared {
+private:
+ uint m_StringLen;
+
+ /* Unhide and forbid baseclass method */
+ virtual void Set (const cTBShared &src) {}
+
+public:
+ cTBString ();
+ cTBString (const cTBString &src);
+ cTBString (const uchar *src);
+ cTBString (const char *src);
+ cTBString (char src);
+ virtual ~cTBString ();
+
+ static const cTBString Null;
+
+ void Clear ();
+ void Set (const cTBString &String);
+ void Set (const uchar *String);
+ void Set (const char *String);
+ void Set (char Character);
+
+ void Fill (char Character, int Length = -1);
+
+ void Release (uint newsize);
+
+ cTBString &operator= (const cTBString &src) { Set(src); return *this; }
+ cTBString &operator= (const char *src) { Set(src); return *this; }
+ cTBString &operator= (char src) { Set(src); return *this; }
+
+ void Append (const cTBString &src);
+ void Append (const char *src);
+ void Append (char src);
+
+ friend cTBString operator+ (const cTBString &a, const cTBString &b);
+ friend cTBString operator+ (const cTBString &a, const char *b);
+ friend cTBString operator+ (const char *a, const cTBString &b);
+ friend cTBString operator+ (const cTBString &a, char b);
+ friend cTBString operator+ (char a, const cTBString &b);
+
+ friend cTBString &operator+= (cTBString &a, const cTBString &b);
+ friend cTBString &operator+= (cTBString &a, const char *b);
+ friend cTBString &operator+= (cTBString &a, char b);
+
+ void Prepend (const cTBString &src);
+ void Prepend (const char *src);
+ void Prepend (char src);
+
+ void Insert (uint Index, const cTBString &src);
+ void Insert (uint Index, const char *src);
+ void Insert (uint Index, char src);
+
+ char At (uint i) const;
+ char operator[] (int i) const { return At((uint)i); }
+
+ char &At (uint i);
+ char &operator[] (int i) { return At(i); }
+
+ cTBString Left (uint Count) const;
+ cTBString Right (uint Count) const;
+ cTBString Mid (int idx, int Count = -1) const;
+
+ int Find (const cTBString &String, uint Offset = 0) const;
+ int Find (const char *String, uint Offset = 0) const;
+ int Find (char Character, uint Offset = 0) const;
+#ifdef TOOLBOX_REGEX
+ bool Find (cTBRegEx &Regex, uint Offset = 0) const;
+#endif
+
+ void Format (const char *fmt, ...)
+#if defined(__GNUC__)
+ __attribute__ ((format (printf, 2, 3)))
+#endif
+ ;
+ void Format (const cTBString &fmt, ...);
+
+ typedef int(*TOFUNC)(int);
+ template<TOFUNC F> cTBString ToAnything(void) const;
+
+ cTBString ToUpper (void) const { return ToAnything<toupper>(); }
+ cTBString ToLower (void) const { return ToAnything<tolower>(); }
+
+ typedef int(*ISFUNC)(int);
+ template<ISFUNC F> bool IsAnything(void) const;
+
+ bool IsAlnum(void) const { return IsAnything<isalnum>(); }
+ bool IsAlpha(void) const { return IsAnything<isalpha>(); }
+ bool IsAscii(void) const { return IsAnything<isascii>(); }
+ bool IsCntrl(void) const { return IsAnything<iscntrl>(); }
+ bool IsDigit(void) const { return IsAnything<isdigit>(); }
+ bool IsGraph(void) const { return IsAnything<isgraph>(); }
+ bool IsLower(void) const { return IsAnything<islower>(); }
+ bool IsPrint(void) const { return IsAnything<isprint>(); }
+ bool IsPunct(void) const { return IsAnything<ispunct>(); }
+ bool IsSpace(void) const { return IsAnything<isspace>(); }
+ bool IsUpper(void) const { return IsAnything<isupper>(); }
+ bool IsXdigit(void) const { return IsAnything<isxdigit>(); }
+
+#if defined(_GNU_SOURCE)
+ bool IsBlank(void) const { return IsAnything<isblank>(); }
+#endif
+
+ uint Length (void) const { return m_StringLen; }
+ bool IsEmpty (void) const { return m_StringLen == 0; }
+ bool IsNull (void) const { return Buffer() == 0; }
+
+ short ToShort(bool *Ok = NULL) const;
+ ushort ToUShort(bool *Ok = NULL) const;
+ int ToInt(bool *Ok = NULL) const;
+ uint ToUInt(bool *Ok = NULL) const;
+ long ToLong(bool *Ok = NULL) const;
+ ulong ToULong(bool *Ok = NULL) const;
+ float ToFloat(bool *Ok = NULL) const;
+ double ToDouble(bool *Ok = NULL) const;
+
+ static cTBString Number(short Num);
+ static cTBString Number(ushort Num);
+ static cTBString Number(int Num);
+ static cTBString Number(uint Num);
+ static cTBString Number(long Num);
+ static cTBString Number(ulong Num);
+ static cTBString Number(float Num);
+ static cTBString Number(double Num);
+
+ friend bool operator== (const cTBString &str1, const cTBString &str2);
+ friend bool operator== (const cTBString &str1, const char *str2);
+ friend bool operator== (const char *str1, const cTBString &str2);
+
+ friend bool operator!= (const cTBString &str1, const cTBString &str2);
+ friend bool operator!= (const cTBString &str1, const char *str2);
+ friend bool operator!= (const char *str1, const cTBString &str2);
+
+ friend bool operator< (const cTBString &str1, const cTBString &str2);
+ friend bool operator< (const cTBString &str1, const char *str2);
+ friend bool operator< (const char *str1, const cTBString &str2);
+
+ friend bool operator> (const cTBString &str1, const cTBString &str2);
+ friend bool operator> (const cTBString &str1, const char *str2);
+ friend bool operator> (const char *str1, const cTBString &str2);
+
+ friend bool operator<= (const cTBString &str1, const cTBString &str2);
+ friend bool operator<= (const cTBString &str1, const char *str2);
+ friend bool operator<= (const char *str1, const cTBString &str2);
+
+ friend bool operator>= (const cTBString &str1, const cTBString &str2);
+ friend bool operator>= (const cTBString &str1, const char *str2);
+ friend bool operator>= (const char *str1, const cTBString &str2);
+};
+
+inline char cTBString::At(uint idx) const {
+ ASSERT(idx >= m_StringLen);
+ return Buffer() ? Buffer()[idx] : 0;
+}
+
+inline char &cTBString::At(uint idx) {
+ static char null = 0;
+ ASSERT(idx >= m_StringLen);
+ if (Buffer()) {
+ Exclusive();
+ return Buffer()[idx];
+ } else
+ return (null = 0);
+}
+
+inline
+RETURNS(cTBString, operator+(const cTBString &a, const cTBString &b), ret(a))
+ ret.Append(b);
+RETURN(ret)
+
+inline
+RETURNS(cTBString, operator+ (const cTBString &a, const char *b), ret(a))
+ ret.Append(b);
+RETURN(ret)
+
+inline
+RETURNS(cTBString, operator+ (const char *a, const cTBString &b), ret(a))
+ ret.Append(b);
+RETURN(ret)
+
+inline
+RETURNS(cTBString, operator+ (const cTBString &a, char b), ret(a))
+ ret.Append(b);
+RETURN(ret)
+
+inline
+RETURNS(cTBString, operator+ (char a, const cTBString &b), ret(a))
+ ret.Append(b);
+RETURN(ret)
+
+inline cTBString &operator+= (cTBString &a, const cTBString &b) {
+ a.Append(b);
+ return a;
+}
+
+inline cTBString &operator+= (cTBString &a, const char *b) {
+ a.Append(b);
+ return a;
+}
+
+inline cTBString &operator+= (cTBString &a, char b) {
+ a.Append(b);
+ return a;
+}
+
+inline bool operator== (const cTBString &str1, const cTBString &str2) {
+ if (str1.Length() != str2.Length())
+ return false;
+ return memcmp(str1.Buffer(), str2.Buffer(), str1.Length()) == 0;
+}
+
+inline bool operator== (const cTBString &str1, const char *str2) {
+ uint len = strlen(str2);
+ if (str1.Length() != len)
+ return false;
+ return memcmp(str1.Buffer(), str2, len) == 0;
+}
+
+inline bool operator== (const char *str1, const cTBString &str2) {
+ uint len = strlen(str1);
+ if (len != str2.Length())
+ return false;
+ return memcmp(str1, str2.Buffer(), len) == 0;
+}
+
+inline bool operator!= (const cTBString &str1, const cTBString &str2) {
+ if (str1.Length() != str2.Length())
+ return true;
+ return memcmp(str1.Buffer(), str2.Buffer(), str1.Length()) != 0;
+}
+
+inline bool operator!= (const cTBString &str1, const char *str2) {
+ uint len = strlen(str2);
+ if (str1.Length() != len)
+ return true;
+ return memcmp(str1.Buffer(), str2, len) != 0;
+}
+
+inline bool operator!= (const char *str1, const cTBString &str2) {
+ uint len = strlen(str1);
+ if (len != str2.Length())
+ return true;
+ return memcmp(str1, str2.Buffer(), len) != 0;
+}
+
+inline bool operator< (const cTBString &str1, const cTBString &str2) {
+ int ret = memcmp(str1.Buffer(), str2.Buffer(), str1.Length() < str2.Length() ? str1.Length() : str2.Length());
+ if ((ret < 0) || ((ret == 0) && (str1.Length() < str2.Length())))
+ return true;
+ return false;
+}
+
+inline bool operator< (const cTBString &str1, const char *str2) {
+ uint len = strlen(str2);
+ int ret = memcmp(str1.Buffer(), str2, str1.Length() < len ? str1.Length() : len);
+ if ((ret < 0) || ((ret == 0) && (str1.Length() < len)))
+ return true;
+ return false;
+}
+
+inline bool operator< (const char *str1, const cTBString &str2) {
+ uint len = strlen(str1);
+ int ret = memcmp(str1, str2.Buffer(), len < str2.Length() ? len : str2.Length());
+ if ((ret < 0) || ((ret == 0) && (len < str2.Length())))
+ return true;
+ return false;
+}
+
+inline bool operator> (const cTBString &str1, const cTBString &str2) {
+ int ret = memcmp(str1.Buffer(), str2.Buffer(), str1.Length() < str2.Length() ? str1.Length() : str2.Length());
+ if ((ret > 0) || ((ret == 0) && (str1.Length() < str2.Length())))
+ return true;
+ return false;
+}
+
+inline bool operator> (const cTBString &str1, const char *str2) {
+ uint len = strlen(str2);
+ int ret = memcmp(str1.Buffer(), str2, str1.Length() < len ? str1.Length() : len);
+ if ((ret > 0) || ((ret == 0) && (str1.Length() < len)))
+ return true;
+ return false;
+}
+
+inline bool operator> (const char *str1, const cTBString &str2) {
+ uint len = strlen(str1);
+ int ret = memcmp(str1, str2.Buffer(), len < str2.Length() ? len : str2.Length());
+ if ((ret > 0) || ((ret == 0) && (len < str2.Length())))
+ return true;
+ return false;
+}
+
+inline bool operator<= (const cTBString &str1, const cTBString &str2) {
+ int ret = memcmp(str1.Buffer(), str2.Buffer(), str1.Length() < str2.Length() ? str1.Length() : str2.Length());
+ if ((ret < 0) || ((ret == 0) && (str1.Length() <= str2.Length())))
+ return true;
+ return false;
+}
+
+inline bool operator<= (const cTBString &str1, const char *str2) {
+ uint len = strlen(str2);
+ int ret = memcmp(str1.Buffer(), str2, str1.Length() < len ? str1.Length() : len);
+ if ((ret < 0) || ((ret == 0) && (str1.Length() <= len)))
+ return true;
+ return false;
+}
+
+inline bool operator<= (const char *str1, const cTBString &str2) {
+ uint len = strlen(str1);
+ int ret = memcmp(str1, str2.Buffer(), len < str2.Length() ? len : str2.Length());
+ if ((ret < 0) || ((ret == 0) && (len <= str2.Length())))
+ return true;
+ return false;
+}
+
+inline bool operator>= (const cTBString &str1, const cTBString &str2) {
+ int ret = memcmp(str1.Buffer(), str2.Buffer(), str1.Length() < str2.Length() ? str1.Length() : str2.Length());
+ if ((ret > 0) || ((ret == 0) && (str1.Length() >= str2.Length())))
+ return true;
+ return false;
+}
+
+inline bool operator>= (const cTBString &str1, const char *str2) {
+ uint len = strlen(str2);
+ int ret = memcmp(str1.Buffer(), str2, str1.Length() < len ? str1.Length() : len);
+ if ((ret > 0) || ((ret == 0) && (str1.Length() >= len)))
+ return true;
+ return false;
+}
+
+inline bool operator>= (const char *str1, const cTBString &str2) {
+ uint len = strlen(str1);
+ int ret = memcmp(str1, str2.Buffer(), len < str2.Length() ? len : str2.Length());
+ if ((ret > 0) || ((ret == 0) && (len >= str2.Length())))
+ return true;
+ return false;
+}
+
+#endif // TOOLBOX_STRING_H
diff --git a/tools/tools.c b/tools/tools.c
new file mode 100644
index 0000000..fa813fa
--- /dev/null
+++ b/tools/tools.c
@@ -0,0 +1,12 @@
+#include "tools/tools.h"
+
+#include <sys/time.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+void *operator new(size_t nSize, void *p) throw () {
+ return p;
+}
+
diff --git a/tools/tools.h b/tools/tools.h
new file mode 100644
index 0000000..ab00c60
--- /dev/null
+++ b/tools/tools.h
@@ -0,0 +1,67 @@
+#ifndef TOOLBOX_TOOLS_H
+#define TOOLBOX_TOOLS_H
+
+//#include <stdio.h>
+//#include <iostream>
+#include <sys/types.h>
+
+//#define KILOBYTE(x) ((x)*1024)
+//#define MEGABYTE(x) (KILOBYTE(x)*1024)
+
+//typedef unsigned int uint;
+//typedef unsigned long ulong;
+typedef unsigned char uchar;
+//typedef unsigned short ushort;
+
+// Special constructor for CreateElements
+void *operator new(size_t, void*) throw ();
+
+#ifdef TOOLBOX_DEBUG
+# define ASSERT(x) if ((x)) cerr << "Warning: ASSERT failed At " << __FILE__ << ":" << __LINE__ << " ["#x"]" << endl
+# define CHECK_PTR(x) if (!(x)) cerr << "Warning: Pointer is NULL At " << __FILE__ << ":" << __LINE__ << endl;
+# define CHECK_NEXT_ALLOC() _checkNextAlloc()
+# define DPRINT(x...) LOGi(x)
+#else
+# define ASSERT(x)
+# define CHECK_PTR(x)
+# define CHECK_NEXT_ALLOC()
+# define DPRINT(x...)
+#endif
+
+#define ERRNUL(e) {errno=e;return 0;}
+#define ERRSYS(e) {errno=e;return -1;}
+
+/* RETURNS() and RETURN() are macros that can be used if a class object is
+ being returned. They make use of the GNU C-Compiler's named return value
+ feature, if available. In this case, the class object isn't returned and
+ copied, but the result itself is filled.
+
+ RETURNS(ReturnType, FunctionDeclaration, Result)
+ ... function-body working on Result ...
+ RETURN(Result)
+
+ A function like this (cXYZ is a class type):
+
+ cXYZ myfunction(int a, char *b) {
+ cXYZ result;
+ ... something happens with result ...
+ return result;
+ }
+
+ can be written like this:
+
+ RETURNS(cXYZ, myfunction(int a, char *b), result)
+ ... something happens with result ...
+ RETURN(result)
+
+ DISABLED SINCE GCC 3.x
+*/
+//#ifdef __GNUC__
+//# define RETURNS(t,x,r) t x return r {
+//# define RETURN(x) }
+//#else
+# define RETURNS(t,x,r) t x { t r;
+# define RETURN(x) return x; }
+//#endif
+
+#endif // TOOLBOX_TOOLS_H