mirror of
https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
synced 2023-10-10 19:16:51 +02:00
Snapshot 2007-03-20
This commit is contained in:
commit
5e30711bfd
21
CONTRIBUTORS
Normal file
21
CONTRIBUTORS
Normal file
@ -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
|
340
COPYING
Normal file
340
COPYING
Normal file
@ -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.
|
146
HISTORY
Normal file
146
HISTORY
Normal file
@ -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.
|
135
Makefile
Normal file
135
Makefile
Normal file
@ -0,0 +1,135 @@
|
||||
#
|
||||
# Makefile for a Video Disk Recorder plugin
|
||||
#
|
||||
# $Id: Makefile,v 1.7 2006/09/14 10:30:16 schmirl 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 ?= -fPIC -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"):
|
||||
|
||||
APIVERSION = $(shell grep 'define APIVERSION ' $(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/source.o tools/select.o tools/socket.o tools/tools.o
|
||||
|
||||
CLIENTOBJS = $(PLUGIN)-client.o \
|
||||
\
|
||||
client/socket.o client/device.o client/setup.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 remux/extern.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)/$@.$(APIVERSION)
|
||||
|
||||
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
|
139
PROTOCOL
Normal file
139
PROTOCOL
Normal file
@ -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.
|
299
README
Normal file
299
README
Normal file
@ -0,0 +1,299 @@
|
||||
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 :-(.
|
||||
|
||||
|
125
client/assembler.c
Normal file
125
client/assembler.c
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* $Id: assembler.c,v 1.2 2005/01/25 14:14:43 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();
|
||||
}
|
32
client/assembler.h
Normal file
32
client/assembler.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* $Id: assembler.h,v 1.1.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
|
||||
|
188
client/device.c
Normal file
188
client/device.c
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* $Id: device.c,v 1.8 2007/01/15 12:15:12 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include "client/device.h"
|
||||
#include "client/setup.h"
|
||||
#include "client/assembler.h"
|
||||
#include "client/filter.h"
|
||||
|
||||
#include "tools/select.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::ProvidesTransponder(const cChannel *Channel) const
|
||||
{
|
||||
Dprintf("ProvidesTransponder\n");
|
||||
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;
|
||||
|
||||
#if VDRVERSNUM < 10338
|
||||
DetachAll(pidHandles[ptAudio].pid);
|
||||
DetachAll(pidHandles[ptVideo].pid);
|
||||
DetachAll(pidHandles[ptPcr].pid);
|
||||
DetachAll(pidHandles[ptTeletext].pid);
|
||||
DelPid(pidHandles[ptAudio].pid);
|
||||
DelPid(pidHandles[ptVideo].pid);
|
||||
DelPid(pidHandles[ptPcr].pid, ptPcr);
|
||||
DelPid(pidHandles[ptTeletext].pid);
|
||||
DelPid(pidHandles[ptDolby].pid);
|
||||
#else
|
||||
DetachAllReceivers();
|
||||
#endif
|
||||
m_Channel = Channel;
|
||||
bool r = ClientSocket.SetChannelDevice(m_Channel);
|
||||
Dprintf("setchanneldevice r=%d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
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");
|
||||
CloseDvr();
|
||||
if (ClientSocket.CreateDataConnection(siLive)) {
|
||||
//m_Assembler = new cStreamdevAssembler(ClientSocket.DataSocket(siLive));
|
||||
//m_TSBuffer = new cTSBuffer(m_Assembler->ReadPipe(), MEGABYTE(2), CardIndex() + 1);
|
||||
m_TSBuffer = new cTSBuffer(*ClientSocket.DataSocket(siLive), MEGABYTE(2), CardIndex() + 1);
|
||||
Dprintf("waiting\n");
|
||||
//m_Assembler->WaitForFill();
|
||||
Dprintf("resuming\n");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cStreamdevDevice::CloseDvr(void) {
|
||||
Dprintf("CloseDvr\n");
|
||||
|
||||
//DELETENULL(m_Assembler);
|
||||
DELETENULL(m_TSBuffer);
|
||||
ClientSocket.CloseDvr();
|
||||
}
|
||||
|
||||
bool cStreamdevDevice::GetTSPacket(uchar *&Data) {
|
||||
if (m_TSBuffer) {
|
||||
Data = m_TSBuffer->Get();
|
||||
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;
|
||||
}
|
||||
|
64
client/device.h
Normal file
64
client/device.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* $Id: device.h,v 1.3 2005/02/08 15:21:19 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(int TimeoutMs)
|
||||
{
|
||||
//printf("HasLock is %d\n", (ClientSocket.DataSocket(siLive) != NULL));
|
||||
//return ClientSocket.DataSocket(siLive) != NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
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 ProvidesTransponder(const cChannel *Channel) 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
|
119
client/filter.c
Normal file
119
client/filter.c
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* $Id: filter.c,v 1.3 2005/11/06 16:43:58 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "client/filter.h"
|
||||
#include "client/socket.h"
|
||||
#include "tools/select.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <vdr/ringbuffer.h>
|
||||
#include <vdr/device.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) {
|
||||
int p = m_RingBuffer->Put(Data, TS_SIZE);
|
||||
if (p != TS_SIZE)
|
||||
m_RingBuffer->ReportOverflow(TS_SIZE - p);
|
||||
}
|
||||
|
||||
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
|
64
client/filter.h
Normal file
64
client/filter.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* $Id: filter.h,v 1.1.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
|
1045
client/menu.c
Normal file
1045
client/menu.c
Normal file
File diff suppressed because it is too large
Load Diff
144
client/menu.h
Normal file
144
client/menu.h
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* $Id: menu.h,v 1.1.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
|
||||
|
476
client/remote.c
Normal file
476
client/remote.c
Normal file
@ -0,0 +1,476 @@
|
||||
/*
|
||||
* $Id: remote.c,v 1.4 2005/04/24 16:26:14 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#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", m_StartTime.c_str());
|
||||
if (*ptr == 0) return;
|
||||
if (isspace(*ptr)) ++ptr;
|
||||
if (*ptr == 0) return;
|
||||
m_Name = ptr;
|
||||
Dprintf("file: %s\n", m_Name.c_str());
|
||||
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.c_str(), '~')) != NULL)
|
||||
t++;
|
||||
else
|
||||
t = m_Name.c_str();
|
||||
|
||||
asprintf(&m_TitleBuffer, "%s%c%c%s", m_StartTime.c_str(), 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.c_str();
|
||||
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.c_str();
|
||||
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", m_Summary.c_str());
|
||||
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 != "")
|
||||
summary = strreplace(strdup(m_Summary.c_str()), ':', '|');
|
||||
|
||||
asprintf(&m_Buffer, "%d:%s:%s:%04d:%04d:%d:%d:%s:%s", m_Active,
|
||||
(const char*)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);
|
||||
}
|
||||
|
131
client/remote.h
Normal file
131
client/remote.h
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* $Id: remote.h,v 1.2 2005/02/08 17:22:35 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_REMOTE_H
|
||||
#define VDR_STREAMDEV_REMOTE_H
|
||||
|
||||
#include <vdr/config.h>
|
||||
#include <string>
|
||||
|
||||
#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;
|
||||
std::string m_StartTime;
|
||||
std::string m_Name;
|
||||
std::string 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.c_str(); }
|
||||
bool IsNew(void) const { return m_IsNew; }
|
||||
const char *Name(void) const { return m_Name.c_str(); }
|
||||
const char *Summary(void) const { return m_Summary.c_str(); }
|
||||
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;
|
||||
std::string 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 std::string &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
|
79
client/setup.c
Normal file
79
client/setup.c
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* $Id: setup.c,v 1.2 2005/02/08 15:34:38 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;
|
||||
#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);
|
||||
#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);
|
||||
#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);
|
||||
#if VDRVERSNUM >= 10300
|
||||
SetupStore("StreamFilters", m_NewSetup.StreamFilters);
|
||||
#endif
|
||||
SetupStore("SyncEPG", m_NewSetup.SyncEPG);
|
||||
|
||||
StreamdevClientSetup = m_NewSetup;
|
||||
|
||||
cStreamdevDevice::ReInit();
|
||||
}
|
||||
|
38
client/setup.h
Normal file
38
client/setup.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* $Id: setup.h,v 1.2 2005/02/08 15:34:38 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;
|
||||
#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
|
587
client/socket.c
Normal file
587
client/socket.c
Normal file
@ -0,0 +1,587 @@
|
||||
/*
|
||||
* $Id: socket.c,v 1.7 2007/01/15 11:45:48 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <tools/select.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#define MINLOGREPEAT 10 //don't log connect failures too often (seconds)
|
||||
|
||||
#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)
|
||||
{
|
||||
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 std::string &Command, uint Expected, uint TimeoutMs)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
std::string pkt = Command + "\015\012";
|
||||
Dprintf("OUT: |%s|\n", Command.c_str());
|
||||
|
||||
cTimeMs starttime;
|
||||
if (!TimedWrite(pkt.c_str(), pkt.size(), TimeoutMs)) {
|
||||
esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
|
||||
strerror(errno));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t elapsed = starttime.Elapsed();
|
||||
if (Expected != 0) { // XXX+ What if elapsed > TimeoutMs?
|
||||
TimeoutMs -= elapsed;
|
||||
return Expect(Expected, NULL, TimeoutMs);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::Expect(uint Expected, std::string *Result, uint TimeoutMs) {
|
||||
char *endptr;
|
||||
int bufcount;
|
||||
bool res;
|
||||
|
||||
errno = 0;
|
||||
|
||||
if ((bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs)) == -1) {
|
||||
esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
|
||||
strerror(errno));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (m_Buffer[bufcount - 1] == '\015')
|
||||
--bufcount;
|
||||
m_Buffer[bufcount] = '\0';
|
||||
Dprintf("IN: |%s|\n", m_Buffer);
|
||||
|
||||
if (Result != NULL)
|
||||
*Result = m_Buffer;
|
||||
|
||||
res = strtoul(m_Buffer, &endptr, 10) == Expected;
|
||||
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)){
|
||||
static time_t lastTime = 0;
|
||||
if (time(NULL) - lastTime > MINLOGREPEAT) {
|
||||
esyslog("ERROR: Streamdev: Couldn't connect to %s:%d: %s",
|
||||
(const char*)StreamdevClientSetup.RemoteIp,
|
||||
StreamdevClientSetup.RemotePort, strerror(errno));
|
||||
lastTime = time(NULL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Expect(220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Didn't receive greeting from %s:%d",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Command("CAPS TSPIDS", 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't negotiate capabilities on %s:%d",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::ProvidesChannel(const cChannel *Channel, int Priority) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
std::string command = (std::string)"PROV " + (const char*)itoa(Priority) + " "
|
||||
+ (const char*)Channel->GetChannelID().ToString();
|
||||
if (!Command(command))
|
||||
return false;
|
||||
|
||||
std::string buffer;
|
||||
if (!Expect(220, &buffer)) {
|
||||
if (buffer.substr(0, 3) != "560" && errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't check if %s:%d provides channel %s",
|
||||
RemoteIp().c_str(), RemotePort(), Channel->Name());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
||||
cTBSocket listen(SOCK_STREAM);
|
||||
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
if (m_DataSockets[Id] != NULL)
|
||||
DELETENULL(m_DataSockets[Id]);
|
||||
|
||||
if (!listen.Listen(LocalIp(), 0, 1)) {
|
||||
esyslog("ERROR: Streamdev: Couldn't create data connection: %s",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string command = (std::string)"PORT " + (const char*)itoa(Id) + " "
|
||||
+ LocalIp().c_str() + ","
|
||||
+ (const char*)itoa((listen.LocalPort() >> 8) & 0xff) + ","
|
||||
+ (const char*)itoa(listen.LocalPort() & 0xff);
|
||||
size_t idx = 4;
|
||||
while ((idx = command.find('.', idx + 1)) != (size_t)-1)
|
||||
command[idx] = ',';
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command(command, 220)) {
|
||||
Dprintf("error: %m\n");
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d",
|
||||
RemoteIp().c_str(), 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",
|
||||
RemoteIp().c_str(), 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;
|
||||
|
||||
std::string command = (std::string)"TUNE "
|
||||
+ (const char*)Channel->GetChannelID().ToString();
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s",
|
||||
RemoteIp().c_str(), RemotePort(), Channel->Name());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::SetPid(int Pid, bool On) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid);
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("Streamdev: Pid %d not available from %s:%d", Pid, LocalIp().c_str(),
|
||||
LocalPort());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if VDRVERSNUM >= 10300
|
||||
bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
std::string command = (std::string)(On ? "ADDF " : "DELF ") + (const char*)itoa(Pid)
|
||||
+ " " + (const char*)itoa(Tid) + " " + (const char*)itoa(Mask);
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d",
|
||||
Pid, Tid, Mask, LocalIp().c_str(), LocalPort());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool cClientSocket::CloseDvr(void) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (m_DataSockets[siLive] != NULL) {
|
||||
std::string command = (std::string)"ABRT " + (const char*)itoa(siLive);
|
||||
if (!Command(command, 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) {
|
||||
std::string buffer;
|
||||
bool result;
|
||||
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 ((result = Expect(215, &buffer))) {
|
||||
if (buffer[3] == ' ') break;
|
||||
fputs(buffer.c_str() + 4, epgfd);
|
||||
fputc('\n', epgfd);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't fetch EPG data from %s:%d",
|
||||
RemoteIp().c_str(), 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",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
}
|
||||
Close();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool cClientSocket::LoadRecordings(cRemoteRecordings &Recordings) {
|
||||
bool res;
|
||||
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command("LSTR"))
|
||||
return false;
|
||||
|
||||
std::string buffer;
|
||||
while ((res = Expect(250, &buffer))) {
|
||||
cRemoteRecording *rec = new cRemoteRecording(buffer.c_str() + 4);
|
||||
Dprintf("recording valid: %d\n", rec->IsValid());
|
||||
if (rec->IsValid())
|
||||
Recordings.Add(rec);
|
||||
else
|
||||
delete rec;
|
||||
if (buffer[3] == ' ') break;
|
||||
}
|
||||
|
||||
if (!res && buffer.substr(0, 3) != "550") {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (cRemoteRecording *r = Recordings.First(); r; r = Recordings.Next(r)) {
|
||||
std::string command = (std::string)"LSTR " + (const char*)itoa(r->Index());
|
||||
if (!Command(command))
|
||||
return false;
|
||||
|
||||
if (Expect(250, &buffer))
|
||||
r->ParseInfo(buffer.c_str() + 4);
|
||||
else if (buffer.substr(0, 3) != "550") {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't fetch details for recording from %s:%d",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
Dprintf("recording complete: %d\n", r->Index());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool cClientSocket::StartReplay(const char *Filename) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
std::string command = (std::string)"PLAY " + Filename;
|
||||
if (!Command(command, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't replay \"%s\" from %s:%d",
|
||||
Filename, RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::AbortReplay(void) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (m_DataSockets[siReplay] != NULL) {
|
||||
std::string command = (std::string)"ABRT " + (const char*)itoa(siReplay);
|
||||
if (!Command(command, 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;
|
||||
cRemoteRecording *rec = NULL;
|
||||
|
||||
if (!CheckConnection())
|
||||
return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command("LSTR"))
|
||||
return false;
|
||||
|
||||
std::string buffer;
|
||||
while ((res = Expect(250, &buffer))) {
|
||||
if (rec == NULL) {
|
||||
rec = new cRemoteRecording(buffer.c_str() + 4);
|
||||
if (!rec->IsValid() || rec->Index() != Recording->Index())
|
||||
DELETENULL(rec);
|
||||
}
|
||||
if (buffer[3] == ' ') break;
|
||||
}
|
||||
|
||||
if (!res && buffer.substr(0, 3) != "550") {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
if (rec != NULL) delete rec;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rec == NULL || *rec != *Recording) {
|
||||
ERROR(tr("Recordings not in sync! Try again..."));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string command = (std::string)"DELR " + (const char*)itoa(Recording->Index());
|
||||
if (!Command(command, 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) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command("LSTT"))
|
||||
return false;
|
||||
|
||||
bool res;
|
||||
std::string buffer;
|
||||
while ((res = Expect(250, &buffer))) {
|
||||
cRemoteTimer *timer = new cRemoteTimer(buffer.c_str() + 4);
|
||||
Dprintf("timer valid: %d\n", timer->IsValid());
|
||||
if (timer->IsValid())
|
||||
Timers.Add(timer);
|
||||
if (buffer[3] == ' ') break;
|
||||
}
|
||||
|
||||
if (!res && buffer.substr(0, 3) != "550") {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool cClientSocket::SaveTimer(cRemoteTimer *Old, cRemoteTimer &New) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (New.Index() == -1) { // New timer
|
||||
std::string command = (std::string)"NEWT " + (const char*)New.ToText();
|
||||
if (!Command(command, 250)) {
|
||||
ERROR(tr("Couldn't save timer! Try again..."));
|
||||
return false;
|
||||
}
|
||||
} else { // Modified timer
|
||||
std::string command = (std::string)"LSTT " + (const char*)itoa(New.Index());
|
||||
if (!Command(command))
|
||||
return false;
|
||||
|
||||
std::string buffer;
|
||||
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(buffer.c_str() + 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;
|
||||
}
|
||||
|
||||
|
||||
command = (std::string)"MODT " + (const char*)itoa(New.Index()) + " "
|
||||
+ (const char*)New.ToText();
|
||||
if (!Command(command, 250)) {
|
||||
ERROR(tr("Couldn't save timer! Try again..."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::DeleteTimer(cRemoteTimer *Timer) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
std::string command = (std::string)"LSTT " + (const char*)itoa(Timer->Index());
|
||||
if (!Command(command))
|
||||
return false;
|
||||
|
||||
std::string buffer;
|
||||
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(buffer.c_str() + 4);
|
||||
if (oldstate != *Timer) {
|
||||
ERROR(tr("Timers not in sync! Try again..."));
|
||||
return false;
|
||||
}
|
||||
|
||||
command = (std::string)"DELT " + (const char*)itoa(Timer->Index());
|
||||
if (!Command(command, 250)) {
|
||||
ERROR(tr("Couldn't delete timer! Try again..."));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
72
client/socket.h
Normal file
72
client/socket.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* $Id: socket.h,v 1.3 2005/02/08 17:22:35 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_CLIENT_CONNECTION_H
|
||||
#define VDR_STREAMDEV_CLIENT_CONNECTION_H
|
||||
|
||||
#include <tools/socket.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#define CMD_LOCK cMutexLock CmdLock((cMutex*)&m_Mutex)
|
||||
|
||||
class cRemoteRecordings;
|
||||
class cRemoteRecording;
|
||||
class cRemoteTimers;
|
||||
class cRemoteTimer;
|
||||
class cPES2TSRemux;
|
||||
|
||||
class cClientSocket: public cTBSocket {
|
||||
private:
|
||||
cTBSocket *m_DataSockets[si_Count];
|
||||
cMutex m_Mutex;
|
||||
char m_Buffer[BUFSIZ + 1]; // various uses
|
||||
|
||||
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 std::string &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, std::string *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
|
295
common.c
Normal file
295
common.c
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* $Id: common.c,v 1.4 2005/02/11 16:44:14 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-20070320";
|
||||
|
||||
const char *StreamTypes[st_Count] = {
|
||||
"TS",
|
||||
"PES",
|
||||
"PS",
|
||||
"ES",
|
||||
"Extern",
|
||||
"", // used internally only
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const cChannel *ChannelFromString(const char *String, int *Apid) {
|
||||
const cChannel *channel = NULL;
|
||||
char *string = strdup(String);
|
||||
char *ptr, *end;
|
||||
int apididx = 0;
|
||||
|
||||
if ((ptr = strrchr(string, '+')) != NULL) {
|
||||
*(ptr++) = '\0';
|
||||
apididx = strtoul(ptr, &end, 10);
|
||||
Dprintf("found apididx: %d\n", apididx);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channel != NULL && apididx > 0) {
|
||||
int apid = 0, index = 1;
|
||||
|
||||
for (int i = 0; channel->Apid(i) != 0; ++i, ++index) {
|
||||
if (index == apididx) {
|
||||
apid = channel->Apid(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (apid == 0) {
|
||||
for (int i = 0; channel->Dpid(i) != 0; ++i, ++index) {
|
||||
if (index == apididx) {
|
||||
apid = channel->Dpid(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Apid != NULL)
|
||||
*Apid = apid;
|
||||
}
|
||||
|
||||
free(string);
|
||||
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_CountSetup, 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;
|
||||
}
|
||||
|
124
common.h
Normal file
124
common.h
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* $Id: common.h,v 1.7 2005/11/06 16:43:58 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
|
||||
|
||||
#if VDRVERSNUM >= 10336
|
||||
# define MAXPARSEBUFFER KILOBYTE(16)
|
||||
#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);
|
||||
|
||||
const cChannel *ChannelFromString(const char *String, int *Apid = NULL);
|
||||
|
||||
/* 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 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,
|
||||
stExtern,
|
||||
stTSPIDS,
|
||||
|
||||
#define st_CountSetup (stExtern+1)
|
||||
#define st_Count (stTSPIDS+1)
|
||||
};
|
||||
|
||||
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
|
831
i18n.c
Normal file
831
i18n.c
Normal file
@ -0,0 +1,831 @@
|
||||
/*
|
||||
* $Id: i18n.c,v 1.5 2006/08/17 09:26:00 thomas 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
|
||||
},
|
||||
{ "Bind to IP", // English
|
||||
"",// Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Sido osoitteeseen", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
#if VDRVERSNUM >= 10300
|
||||
"" // Russian
|
||||
#endif
|
||||
},
|
||||
{ "Remote Schedule", // English
|
||||
"",// Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Etäkoneen ohjelmaopas", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
#if VDRVERSNUM >= 10300
|
||||
"" // Russian
|
||||
#endif
|
||||
},
|
||||
{ "Filter Streaming", // English
|
||||
"",// Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Suodatetun tiedon suoratoisto", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
#if VDRVERSNUM >= 10300
|
||||
"" // Russian
|
||||
#endif
|
||||
},
|
||||
{ "Streaming active", // English
|
||||
"Streamen im Gange",// Deutsch
|
||||
"", // Slovenski
|
||||
"", // Italiano
|
||||
"", // Nederlands
|
||||
"", // Português
|
||||
"", // Français
|
||||
"", // Norsk
|
||||
"Suoratoistopalvelin aktiivinen", // suomi
|
||||
"", // Polski
|
||||
"", // Español
|
||||
"", // Ellinika
|
||||
"", // Svenska
|
||||
"", // Romaneste
|
||||
"", // Magyar
|
||||
"", // Catala
|
||||
#if VDRVERSNUM >= 10300
|
||||
"" // Russian
|
||||
#endif
|
||||
},
|
||||
{ NULL }
|
||||
};
|
16
i18n.h
Normal file
16
i18n.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* $Id: i18n.h,v 1.1.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
|
1
libdvbmpeg/.cvsignore
Normal file
1
libdvbmpeg/.cvsignore
Normal file
@ -0,0 +1 @@
|
||||
.depend
|
446
libdvbmpeg/DVB.hh
Normal file
446
libdvbmpeg/DVB.hh
Normal file
@ -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
|
33
libdvbmpeg/Makefile
Normal file
33
libdvbmpeg/Makefile
Normal file
@ -0,0 +1,33 @@
|
||||
INCS = -I.
|
||||
CFLAGS = -g -Wall -O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -fPIC
|
||||
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
|
30
libdvbmpeg/OSD.h
Normal file
30
libdvbmpeg/OSD.h
Normal file
@ -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
|
58
libdvbmpeg/channel.h
Normal file
58
libdvbmpeg/channel.h
Normal file
@ -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
|
167
libdvbmpeg/ci.hh
Normal file
167
libdvbmpeg/ci.hh
Normal file
@ -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
|
946
libdvbmpeg/cpptools.cc
Normal file
946
libdvbmpeg/cpptools.cc
Normal file
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
330
libdvbmpeg/cpptools.hh
Normal file
330
libdvbmpeg/cpptools.hh
Normal file
@ -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_
|
||||
|
2379
libdvbmpeg/ctools.c
Normal file
2379
libdvbmpeg/ctools.c
Normal file
File diff suppressed because it is too large
Load Diff
404
libdvbmpeg/ctools.h
Normal file
404
libdvbmpeg/ctools.h
Normal file
@ -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_*/
|
310
libdvbmpeg/devices.hh
Normal file
310
libdvbmpeg/devices.hh
Normal file
@ -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*/
|
84
libdvbmpeg/osd.hh
Normal file
84
libdvbmpeg/osd.hh
Normal file
@ -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
|
1215
libdvbmpeg/remux.c
Normal file
1215
libdvbmpeg/remux.c
Normal file
File diff suppressed because it is too large
Load Diff
149
libdvbmpeg/remux.h
Normal file
149
libdvbmpeg/remux.h
Normal file
@ -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_*/
|
201
libdvbmpeg/ringbuffy.c
Normal file
201
libdvbmpeg/ringbuffy.c
Normal file
@ -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;
|
||||
}
|
52
libdvbmpeg/ringbuffy.h
Normal file
52
libdvbmpeg/ringbuffy.h
Normal file
@ -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 */
|
2681
libdvbmpeg/transform.c
Normal file
2681
libdvbmpeg/transform.c
Normal file
File diff suppressed because it is too large
Load Diff
250
libdvbmpeg/transform.h
Normal file
250
libdvbmpeg/transform.h
Normal file
@ -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_*/
|
||||
|
||||
|
||||
|
43
patches/respect_ca.diff
Normal file
43
patches/respect_ca.diff
Normal file
@ -0,0 +1,43 @@
|
||||
# The cannels.conf ca field can be used to bind a channel to a specific
|
||||
# device. The streamdev-client does not consider this information, so
|
||||
# there's no way to keep VDR from using streamdev for a specific
|
||||
# channel. Apply this patch if you need this feature.
|
||||
#
|
||||
# This fix should probably become part of streamdev. However as it
|
||||
# changes the behaviour of streamdev, I decided to keep it as a separate
|
||||
# patch until there is something like a new official streamdev release.
|
||||
#
|
||||
--- client/device.h.bak 2006-11-09 12:25:21.000000000 +0100
|
||||
+++ client/device.h 2006-11-09 12:26:57.000000000 +0100
|
||||
@@ -50,6 +50,7 @@
|
||||
cStreamdevDevice(void);
|
||||
virtual ~cStreamdevDevice();
|
||||
|
||||
+ virtual int ProvidesCa(const cChannel *Channel) const;
|
||||
virtual bool ProvidesSource(int Source) const;
|
||||
virtual bool ProvidesTransponder(const cChannel *Channel) const;
|
||||
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1,
|
||||
--- client/device.c.bak 2006-11-09 12:23:24.000000000 +0100
|
||||
+++ client/device.c 2006-11-09 12:35:48.000000000 +0100
|
||||
@@ -57,6 +57,12 @@
|
||||
#endif
|
||||
}
|
||||
|
||||
+int cStreamdevDevice::ProvidesCa(const cChannel *Channel) const
|
||||
+{
|
||||
+ // Encrypted is acceptable for now. Will ask the server later.
|
||||
+ return Channel->Ca() <= CA_DVB_MAX ? cDevice::ProvidesCa(Channel) : 1;
|
||||
+}
|
||||
+
|
||||
bool cStreamdevDevice::ProvidesSource(int Source) const {
|
||||
Dprintf("ProvidesSource, Source=%d\n", Source);
|
||||
return false;
|
||||
@@ -78,7 +84,7 @@
|
||||
if (ClientSocket.DataSocket(siLive) != NULL
|
||||
&& TRANSPONDER(Channel, m_Channel))
|
||||
res = true;
|
||||
- else {
|
||||
+ else if (ProvidesCa(Channel)) {
|
||||
res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
|
||||
ndr = true;
|
||||
}
|
29
patches/thread.c.diff
Normal file
29
patches/thread.c.diff
Normal file
@ -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;
|
61
patches/vdr-1.3.11-localchannelprovide.diff
Normal file
61
patches/vdr-1.3.11-localchannelprovide.diff
Normal file
@ -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)
|
93
patches/vdr-1.3.24.LocalChannelProvide.diff
Normal file
93
patches/vdr-1.3.24.LocalChannelProvide.diff
Normal file
@ -0,0 +1,93 @@
|
||||
diff -Nu vdr-1.3.24/config.c vdr-1.3.24.LocalChannelProvide/config.c
|
||||
--- vdr-1.3.24/config.c 2005-02-20 13:52:59.000000000 +0100
|
||||
+++ vdr-1.3.24.LocalChannelProvide/config.c 2005-05-12 19:23:58.000000000 +0200
|
||||
@@ -301,6 +301,7 @@
|
||||
CurrentChannel = -1;
|
||||
CurrentVolume = MAXVOLUME;
|
||||
CurrentDolby = 0;
|
||||
+ LocalChannelProvide = 1;
|
||||
}
|
||||
|
||||
cSetup& cSetup::operator= (const cSetup &s)
|
||||
@@ -458,6 +459,7 @@
|
||||
else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
|
||||
else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value);
|
||||
else if (!strcasecmp(Name, "CurrentDolby")) CurrentDolby = atoi(Value);
|
||||
+ else if (!strcasecmp(Name, "LocalChannelProvide")) LocalChannelProvide = atoi(Value);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
@@ -522,6 +524,7 @@
|
||||
Store("CurrentChannel", CurrentChannel);
|
||||
Store("CurrentVolume", CurrentVolume);
|
||||
Store("CurrentDolby", CurrentDolby);
|
||||
+ Store("LocalChannelProvide",LocalChannelProvide);
|
||||
|
||||
Sort();
|
||||
|
||||
diff -Nu vdr-1.3.24/config.h vdr-1.3.24.LocalChannelProvide/config.h
|
||||
--- vdr-1.3.24/config.h 2005-05-05 13:04:18.000000000 +0200
|
||||
+++ vdr-1.3.24.LocalChannelProvide/config.h 2005-05-12 19:24:31.000000000 +0200
|
||||
@@ -255,6 +255,7 @@
|
||||
int CurrentChannel;
|
||||
int CurrentVolume;
|
||||
int CurrentDolby;
|
||||
+ int LocalChannelProvide;
|
||||
int __EndData__;
|
||||
cSetup(void);
|
||||
cSetup& operator= (const cSetup &s);
|
||||
diff -Nu vdr-1.3.24/dvbdevice.c vdr-1.3.24.LocalChannelProvide/dvbdevice.c
|
||||
--- vdr-1.3.24/dvbdevice.c 2005-03-20 11:10:38.000000000 +0100
|
||||
+++ vdr-1.3.24.LocalChannelProvide/dvbdevice.c 2005-05-12 19:19:29.000000000 +0200
|
||||
@@ -746,6 +746,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 -Nu vdr-1.3.24/i18n.c vdr-1.3.24.LocalChannelProvide/i18n.c
|
||||
--- vdr-1.3.24/i18n.c 2005-05-05 15:12:54.000000000 +0200
|
||||
+++ vdr-1.3.24.LocalChannelProvide/i18n.c 2005-05-12 19:30:50.000000000 +0200
|
||||
@@ -5325,6 +5325,27 @@
|
||||
"ST:TNG konsool",
|
||||
"ST:TNG konsol",
|
||||
},
|
||||
+ { "Channels available locally",
|
||||
+ "Kanäle lokal beziehen",
|
||||
+ "",// TODO
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",// TODO
|
||||
+ "",// TODO
|
||||
+ "",// TODO
|
||||
+ "",
|
||||
+ "",// TODO
|
||||
+ "",// TODO
|
||||
+ "",// TODO
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",// TODO
|
||||
+ "",// TODO
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
diff -Nu vdr-1.3.24/menu.c vdr-1.3.24.LocalChannelProvide/menu.c
|
||||
--- vdr-1.3.24/menu.c 2005-03-20 16:14:51.000000000 +0100
|
||||
+++ vdr-1.3.24.LocalChannelProvide/menu.c 2005-05-12 19:26:57.000000000 +0200
|
||||
@@ -1968,7 +1968,7 @@
|
||||
Add(new cMenuEditIntItem( tr("Setup.DVB$Audio languages"), &numAudioLanguages, 0, I18nNumLanguages));
|
||||
for (int i = 0; i < numAudioLanguages; i++)
|
||||
Add(new cMenuEditStraItem(tr("Setup.DVB$Audio language"), &data.AudioLanguages[i], I18nNumLanguages, I18nLanguages()));
|
||||
-
|
||||
+ Add(new cMenuEditBoolItem(tr("Channels available locally"), &data.LocalChannelProvide));
|
||||
SetCurrent(Get(current));
|
||||
Display();
|
||||
}
|
22
patches/vdr-1.3.6-incompletesections.diff
Normal file
22
patches/vdr-1.3.6-incompletesections.diff
Normal file
@ -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;
|
85
patches/vdr-1.4.3-recursion.diff
Normal file
85
patches/vdr-1.4.3-recursion.diff
Normal file
@ -0,0 +1,85 @@
|
||||
# If you have two or more VDRs and you like them to mutually share
|
||||
# there DVB cards you might need to apply this patch first.
|
||||
#
|
||||
# IMPORTANT: As this patch does not only modify streamdev-server but
|
||||
# also an exported method of VDR, you will need to
|
||||
#
|
||||
# !!!!! RECOMPILE VDR AND ALL PLUGINS !!!!!
|
||||
#
|
||||
# Why do I need the patch?
|
||||
# --------------------------
|
||||
# Before switching channels VDR will consider all of its devices to
|
||||
# find the one with the least impact. This includes the device provided
|
||||
# by the streamdev-client plugin. Streamdev-client will forward the
|
||||
# request to its server which in turn checks all of its devices. Now if
|
||||
# the server is running streamdev-client, too, the request will again
|
||||
# be forwarded to its server and finally you will endup in a loop.
|
||||
#
|
||||
# What does the patch do?
|
||||
# -----------------------
|
||||
# The patch adds the additional parameter "bool DVBCardsOnly" to VDR's
|
||||
# device selection method cDevice::GetDevice(...). The parameter
|
||||
# defaults to false which gives you the standard behaviour of GetDevice.
|
||||
# When set to true, GetDevice will use only those devices with a card
|
||||
# index < MAXDVBDEVICES, so only real DVB cards will be considered.
|
||||
# Other devices like streamdev-client or DVB cards provided by plugin
|
||||
# (Hauppauge PVR) won't be used.
|
||||
#
|
||||
# Author: Frank Schmirler (http://vdr.schmirler.de)
|
||||
#
|
||||
--- device.h.orig 2006-11-15 12:01:34.000000000 +0100
|
||||
+++ device.h 2006-11-15 12:02:15.000000000 +0100
|
||||
@@ -128,7 +128,7 @@
|
||||
///< Gets the device with the given Index.
|
||||
///< \param Index must be in the range 0..numDevices-1.
|
||||
///< \return A pointer to the device, or NULL if the Index was invalid.
|
||||
- static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
|
||||
+ static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL, bool DVBCardsOnly = false);
|
||||
///< Returns a device that is able to receive the given Channel at the
|
||||
///< given Priority, with the least impact on active recordings and
|
||||
///< live viewing.
|
||||
--- device.c.orig 2006-11-15 12:01:30.000000000 +0100
|
||||
+++ device.c 2006-11-22 12:28:05.000000000 +0100
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "device.h"
|
||||
+#include "dvbdevice.h"
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -278,11 +279,13 @@
|
||||
return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
|
||||
}
|
||||
|
||||
-cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
|
||||
+cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers, bool DVBCardsOnly)
|
||||
{
|
||||
cDevice *d = NULL;
|
||||
uint Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
+ if (DVBCardsOnly && device[i]->CardIndex() >= MAXDVBDEVICES)
|
||||
+ continue;
|
||||
bool ndr;
|
||||
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
|
||||
// Put together an integer number that reflects the "impact" using
|
||||
--- PLUGINS/src/streamdev/server/connection.c.orig 2006-11-15 12:10:11.000000000 +0100
|
||||
+++ PLUGINS/src/streamdev/server/connection.c 2006-11-15 12:10:59.000000000 +0100
|
||||
@@ -132,7 +132,7 @@
|
||||
Dprintf(" * GetDevice(const cChannel*, int)\n");
|
||||
Dprintf(" * -------------------------------\n");
|
||||
|
||||
- device = cDevice::GetDevice(Channel, Priority);
|
||||
+ device = cDevice::GetDevice(Channel, Priority, NULL, true);
|
||||
|
||||
Dprintf(" * Found following device: %p (%d)\n", device,
|
||||
device ? device->CardIndex() + 1 : 0);
|
||||
@@ -150,7 +150,7 @@
|
||||
const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
|
||||
isyslog("streamdev-server: Detaching current receiver");
|
||||
Detach();
|
||||
- device = cDevice::GetDevice(Channel, Priority);
|
||||
+ device = cDevice::GetDevice(Channel, Priority, NULL, true);
|
||||
Attach();
|
||||
Dprintf(" * Found following device: %p (%d)\n", device,
|
||||
device ? device->CardIndex() + 1 : 0);
|
88
patches/vdr-1.4.3-recursion_bigpatch.diff
Normal file
88
patches/vdr-1.4.3-recursion_bigpatch.diff
Normal file
@ -0,0 +1,88 @@
|
||||
# If you have two or more VDRs and you like them to mutually share
|
||||
# there DVB cards you might need to apply this patch first.
|
||||
#
|
||||
# This is a modified version of the patch for VDRs with BIGPATCH.
|
||||
# Thanks to p_body@vdrportal.
|
||||
#
|
||||
# IMPORTANT: As this patch does not only modify streamdev-server but
|
||||
# also an exported method of VDR, you will need to
|
||||
#
|
||||
# !!!!! RECOMPILE VDR AND ALL PLUGINS !!!!!
|
||||
#
|
||||
# Why do I need the patch?
|
||||
# --------------------------
|
||||
# Before switching channels VDR will consider all of its devices to
|
||||
# find the one with the least impact. This includes the device provided
|
||||
# by the streamdev-client plugin. Streamdev-client will forward the
|
||||
# request to its server which in turn checks all of its devices. Now if
|
||||
# the server is running streamdev-client, too, the request will again
|
||||
# be forwarded to its server and finally you will endup in a loop.
|
||||
#
|
||||
# What does the patch do?
|
||||
# -----------------------
|
||||
# The patch adds the additional parameter "bool DVBCardsOnly" to VDR's
|
||||
# device selection method cDevice::GetDevice(...). The parameter
|
||||
# defaults to false which gives you the standard behaviour of GetDevice.
|
||||
# When set to true, GetDevice will use only those devices with a card
|
||||
# index < MAXDVBDEVICES, so only real DVB cards will be considered.
|
||||
# Other devices like streamdev-client or DVB cards provided by plugin
|
||||
# (Hauppauge PVR) won't be used.
|
||||
#
|
||||
# Author: Frank Schmirler (http://vdr.schmirler.de)
|
||||
#
|
||||
--- device.h.orig 2006-11-15 12:01:34.000000000 +0100
|
||||
+++ device.h 2006-11-15 12:02:15.000000000 +0100
|
||||
@@ -128,7 +128,7 @@
|
||||
///< Gets the device with the given Index.
|
||||
///< \param Index must be in the range 0..numDevices-1.
|
||||
///< \return A pointer to the device, or NULL if the Index was invalid.
|
||||
- static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL, bool LiveView = false);
|
||||
+ static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL, bool LiveView = false, bool DVBCardsOnly = false);
|
||||
///< Returns a device that is able to receive the given Channel at the
|
||||
///< given Priority, with the least impact on active recordings and
|
||||
///< live viewing.
|
||||
--- device.c.orig 2006-11-15 12:01:30.000000000 +0100
|
||||
+++ device.c 2006-11-22 12:28:05.000000000 +0100
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "device.h"
|
||||
+#include "dvbdevice.h"
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -278,11 +279,13 @@
|
||||
return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
|
||||
}
|
||||
|
||||
-cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers, bool LiveView)
|
||||
+cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers, bool LiveView, bool DVBCardsOnly)
|
||||
{
|
||||
cDevice *d = NULL;
|
||||
uint Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
+ if (DVBCardsOnly && device[i]->CardIndex() >= MAXDVBDEVICES)
|
||||
+ continue;
|
||||
bool ndr;
|
||||
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
|
||||
// Put together an integer number that reflects the "impact" using
|
||||
--- PLUGINS/src/streamdev/server/connection.c.orig 2006-11-15 12:10:11.000000000 +0100
|
||||
+++ PLUGINS/src/streamdev/server/connection.c 2006-11-15 12:10:59.000000000 +0100
|
||||
@@ -132,7 +132,7 @@
|
||||
Dprintf(" * GetDevice(const cChannel*, int)\n");
|
||||
Dprintf(" * -------------------------------\n");
|
||||
|
||||
- device = cDevice::GetDevice(Channel, Priority);
|
||||
+ device = cDevice::GetDevice(Channel, Priority, NULL, NULL, true);
|
||||
|
||||
Dprintf(" * Found following device: %p (%d)\n", device,
|
||||
device ? device->CardIndex() + 1 : 0);
|
||||
@@ -150,7 +150,7 @@
|
||||
const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
|
||||
isyslog("streamdev-server: Detaching current receiver");
|
||||
Detach();
|
||||
- device = cDevice::GetDevice(Channel, Priority);
|
||||
+ device = cDevice::GetDevice(Channel, Priority, NULL, NULL, true);
|
||||
Attach();
|
||||
Dprintf(" * Found following device: %p (%d)\n", device,
|
||||
device ? device->CardIndex() + 1 : 0);
|
102
patches/vdr-1.4.x-localchannelprovide.diff
Normal file
102
patches/vdr-1.4.x-localchannelprovide.diff
Normal file
@ -0,0 +1,102 @@
|
||||
# Apply this patch to VDR if you want to use a fullfeatured DVB card
|
||||
# as pure output device. Infact the patch will keep VDR from using the
|
||||
# tuner of any local DVB card (also budget cards). It will not affect
|
||||
# other input devices like e.g. streamdev-client or DVB cards provided
|
||||
# by plugins (e.g. Hauppauge PVR).
|
||||
#
|
||||
# By default the patch is DISABLED. There will be a new OSD menu entry
|
||||
# in Setup->DVB which allows you to enable or disable the patch at any
|
||||
# time.
|
||||
diff -ru vdr-1.4.3.orig/config.c vdr-1.4.3/config.c
|
||||
--- vdr-1.4.3.orig/config.c 2006-07-22 13:57:51.000000000 +0200
|
||||
+++ vdr-1.4.3/config.c 2006-11-16 08:16:37.000000000 +0100
|
||||
@@ -273,6 +273,7 @@
|
||||
CurrentChannel = -1;
|
||||
CurrentVolume = MAXVOLUME;
|
||||
CurrentDolby = 0;
|
||||
+ LocalChannelProvide = 1;
|
||||
InitialChannel = 0;
|
||||
InitialVolume = -1;
|
||||
}
|
||||
@@ -434,6 +435,7 @@
|
||||
else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
|
||||
else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value);
|
||||
else if (!strcasecmp(Name, "CurrentDolby")) CurrentDolby = atoi(Value);
|
||||
+ else if (!strcasecmp(Name, "LocalChannelProvide")) LocalChannelProvide = atoi(Value);
|
||||
else if (!strcasecmp(Name, "InitialChannel")) InitialChannel = atoi(Value);
|
||||
else if (!strcasecmp(Name, "InitialVolume")) InitialVolume = atoi(Value);
|
||||
else
|
||||
@@ -502,6 +504,7 @@
|
||||
Store("CurrentChannel", CurrentChannel);
|
||||
Store("CurrentVolume", CurrentVolume);
|
||||
Store("CurrentDolby", CurrentDolby);
|
||||
+ Store("LocalChannelProvide",LocalChannelProvide);
|
||||
Store("InitialChannel", InitialChannel);
|
||||
Store("InitialVolume", InitialVolume);
|
||||
|
||||
diff -ru vdr-1.4.3.orig/config.h vdr-1.4.3/config.h
|
||||
--- vdr-1.4.3.orig/config.h 2006-09-23 15:56:08.000000000 +0200
|
||||
+++ vdr-1.4.3/config.h 2006-11-16 08:16:57.000000000 +0100
|
||||
@@ -250,6 +250,7 @@
|
||||
int CurrentChannel;
|
||||
int CurrentVolume;
|
||||
int CurrentDolby;
|
||||
+ int LocalChannelProvide;
|
||||
int InitialChannel;
|
||||
int InitialVolume;
|
||||
int __EndData__;
|
||||
diff -ru vdr-1.4.3.orig/dvbdevice.c vdr-1.4.3/dvbdevice.c
|
||||
--- vdr-1.4.3.orig/dvbdevice.c 2006-08-14 11:38:32.000000000 +0200
|
||||
+++ vdr-1.4.3/dvbdevice.c 2006-11-16 08:17:58.000000000 +0100
|
||||
@@ -766,6 +766,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 -ru vdr-1.4.3.orig/i18n.c vdr-1.4.3/i18n.c
|
||||
--- vdr-1.4.3.orig/i18n.c 2006-09-16 11:08:30.000000000 +0200
|
||||
+++ vdr-1.4.3/i18n.c 2006-11-16 08:36:53.000000000 +0100
|
||||
@@ -3546,6 +3546,28 @@
|
||||
"Foretrukket sprog",
|
||||
"Preferovaný jazyk",
|
||||
},
|
||||
+ { "Setup.DVB$Use DVB receivers",
|
||||
+ "DVB Empfangsteile benutzen",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ "",
|
||||
+ },
|
||||
{ "Setup.DVB$Primary DVB interface",
|
||||
"Primäres DVB-Interface",
|
||||
"Primarna naprava",
|
||||
diff -ru vdr-1.4.3.orig/menu.c vdr-1.4.3/menu.c
|
||||
--- vdr-1.4.3.orig/menu.c 2006-07-23 11:23:11.000000000 +0200
|
||||
+++ vdr-1.4.3/menu.c 2006-11-16 08:37:27.000000000 +0100
|
||||
@@ -2354,6 +2354,7 @@
|
||||
|
||||
Clear();
|
||||
|
||||
+ Add(new cMenuEditBoolItem(tr("Setup.DVB$Use DVB receivers"), &data.LocalChannelProvide));
|
||||
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"));
|
||||
if (data.VideoFormat == 0)
|
113
patches/vdr-pluginactivity.diff
Normal file
113
patches/vdr-pluginactivity.diff
Normal file
@ -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:
|
157
remux/extern.c
Normal file
157
remux/extern.c
Normal file
@ -0,0 +1,157 @@
|
||||
#include "remux/extern.h"
|
||||
#include "server/streamer.h"
|
||||
#include <vdr/tools.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
const char *g_ExternRemux = "/root/externremux.sh";
|
||||
|
||||
class cTSExt: public cThread {
|
||||
private:
|
||||
cRingBufferLinear *m_ResultBuffer;
|
||||
bool m_Active;
|
||||
int m_Process;
|
||||
int m_Inpipe, m_Outpipe;
|
||||
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
|
||||
public:
|
||||
cTSExt(cRingBufferLinear *ResultBuffer);
|
||||
virtual ~cTSExt();
|
||||
|
||||
void Put(const uchar *Data, int Count);
|
||||
};
|
||||
|
||||
cTSExt::cTSExt(cRingBufferLinear *ResultBuffer):
|
||||
m_ResultBuffer(ResultBuffer),
|
||||
m_Active(false),
|
||||
m_Process(0),
|
||||
m_Inpipe(0),
|
||||
m_Outpipe(0)
|
||||
{
|
||||
int inpipe[2];
|
||||
int outpipe[2];
|
||||
|
||||
if (pipe(inpipe) == -1) {
|
||||
LOG_ERROR_STR("pipe failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pipe(outpipe) == -1) {
|
||||
LOG_ERROR_STR("pipe failed");
|
||||
close(inpipe[0]);
|
||||
close(inpipe[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_Process = fork()) == -1) {
|
||||
LOG_ERROR_STR("fork failed");
|
||||
close(inpipe[0]);
|
||||
close(inpipe[1]);
|
||||
close(outpipe[0]);
|
||||
close(outpipe[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Process == 0) {
|
||||
// child process
|
||||
dup2(inpipe[0], STDIN_FILENO);
|
||||
close(inpipe[1]);
|
||||
dup2(outpipe[1], STDOUT_FILENO);
|
||||
close(outpipe[0]);
|
||||
|
||||
int MaxPossibleFileDescriptors = getdtablesize();
|
||||
for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
|
||||
close(i); //close all dup'ed filedescriptors
|
||||
|
||||
//printf("starting externremux.sh\n");
|
||||
execl("/bin/sh", "sh", "-c", g_ExternRemux, NULL);
|
||||
//printf("failed externremux.sh\n");
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
close(inpipe[0]);
|
||||
close(outpipe[1]);
|
||||
m_Inpipe = inpipe[1];
|
||||
m_Outpipe = outpipe[0];
|
||||
Start();
|
||||
}
|
||||
|
||||
cTSExt::~cTSExt()
|
||||
{
|
||||
m_Active = false;
|
||||
Cancel(3);
|
||||
if (m_Process > 0) {
|
||||
close(m_Outpipe);
|
||||
close(m_Inpipe);
|
||||
kill(m_Process, SIGTERM);
|
||||
for (int i = 0; waitpid(m_Process, NULL, WNOHANG) == 0; i++) {
|
||||
if (i == 20) {
|
||||
esyslog("streamdev-server: externremux process won't stop - killing it");
|
||||
kill(m_Process, SIGKILL);
|
||||
}
|
||||
cCondWait::SleepMs(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cTSExt::Action(void)
|
||||
{
|
||||
m_Active = true;
|
||||
while (m_Active) {
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(m_Outpipe, &rfds);
|
||||
|
||||
while (FD_ISSET(m_Outpipe, &rfds)) {
|
||||
tv.tv_sec = 2;
|
||||
tv.tv_usec = 0;
|
||||
if (select(m_Outpipe + 1, &rfds, NULL, NULL, &tv) == -1) {
|
||||
LOG_ERROR_STR("poll failed");
|
||||
break;;
|
||||
}
|
||||
|
||||
if (FD_ISSET(m_Outpipe, &rfds)) {
|
||||
int result;
|
||||
if ((result = m_ResultBuffer->Read(m_Outpipe)) == -1) {
|
||||
LOG_ERROR_STR("read failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Active = false;
|
||||
}
|
||||
|
||||
|
||||
void cTSExt::Put(const uchar *Data, int Count)
|
||||
{
|
||||
if (safe_write(m_Inpipe, Data, Count) == -1) {
|
||||
LOG_ERROR_STR("write failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cExternRemux::cExternRemux(int VPid, const int *APids, const int *Dpids, const int *SPids):
|
||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2)),
|
||||
m_Remux(new cTSExt(m_ResultBuffer))
|
||||
{
|
||||
m_ResultBuffer->SetTimeouts(0, 100);
|
||||
}
|
||||
|
||||
cExternRemux::~cExternRemux()
|
||||
{
|
||||
delete m_Remux;
|
||||
delete m_ResultBuffer;
|
||||
}
|
||||
|
||||
int cExternRemux::Put(const uchar *Data, int Count)
|
||||
{
|
||||
m_Remux->Put(Data, Count);
|
||||
return Count;
|
||||
}
|
25
remux/extern.h
Normal file
25
remux/extern.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef VDR_STREAMDEV_EXTERNREMUX_H
|
||||
#define VDR_STREAMDEV_EXTERNREMUX_H
|
||||
|
||||
#include "remux/tsremux.h"
|
||||
#include <vdr/ringbuffer.h>
|
||||
|
||||
extern const char *g_ExternRemux;
|
||||
|
||||
class cTSExt;
|
||||
|
||||
class cExternRemux: public cTSRemux {
|
||||
private:
|
||||
cRingBufferLinear *m_ResultBuffer;
|
||||
cTSExt *m_Remux;
|
||||
|
||||
public:
|
||||
cExternRemux(int VPid, const int *APids, const int *Dpids, const int *SPids);
|
||||
virtual ~cExternRemux();
|
||||
|
||||
int Put(const uchar *Data, int Count);
|
||||
uchar *Get(int &Count) { return m_ResultBuffer->Get(Count); }
|
||||
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_EXTERNREMUX_H
|
140
remux/ts2es.c
Normal file
140
remux/ts2es.c
Normal file
@ -0,0 +1,140 @@
|
||||
#include "remux/ts2es.h"
|
||||
#include "server/streamer.h"
|
||||
#include "libdvbmpeg/transform.h"
|
||||
#include "common.h"
|
||||
#include <vdr/device.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:
|
||||
cRingBufferLinear *m_ResultBuffer;
|
||||
|
||||
public:
|
||||
cTS2ES(cRingBufferLinear *ResultBuffer);
|
||||
~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;
|
||||
|
||||
int n = This->m_ResultBuffer->Put(Buffer + payl, count);
|
||||
if (n != count)
|
||||
esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", count - n, count);
|
||||
This->start = 1;
|
||||
}
|
||||
|
||||
cTS2ES::cTS2ES(cRingBufferLinear *ResultBuffer)
|
||||
{
|
||||
m_ResultBuffer = ResultBuffer;
|
||||
|
||||
init_ipack(this, IPACKS, PutES, 0);
|
||||
data = (void*)this;
|
||||
}
|
||||
|
||||
cTS2ES::~cTS2ES()
|
||||
{
|
||||
free_ipack(this);
|
||||
}
|
||||
|
||||
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):
|
||||
m_Pid(Pid),
|
||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, IPACKS)),
|
||||
m_Remux(new cTS2ES(m_ResultBuffer))
|
||||
{
|
||||
m_ResultBuffer->SetTimeouts(0, 100);
|
||||
}
|
||||
|
||||
cTS2ESRemux::~cTS2ESRemux()
|
||||
{
|
||||
delete m_Remux;
|
||||
delete m_ResultBuffer;
|
||||
}
|
||||
|
||||
int cTS2ESRemux::Put(const uchar *Data, int Count)
|
||||
{
|
||||
int used = 0;
|
||||
|
||||
// Make sure we are looking at a TS packet:
|
||||
|
||||
while (Count > TS_SIZE) {
|
||||
if (Data[0] == TS_SYNC_BYTE && Data[TS_SIZE] == TS_SYNC_BYTE)
|
||||
break;
|
||||
Data++;
|
||||
Count--;
|
||||
used++;
|
||||
}
|
||||
|
||||
if (used)
|
||||
esyslog("ERROR: skipped %d byte to sync on TS packet", used);
|
||||
|
||||
// Convert incoming TS data into ES:
|
||||
|
||||
for (int i = 0; i < Count; i += TS_SIZE) {
|
||||
if (Count - i < TS_SIZE)
|
||||
break;
|
||||
if (Data[i] != TS_SYNC_BYTE)
|
||||
break;
|
||||
if (m_ResultBuffer->Free() < 2 * IPACKS)
|
||||
break; // A cTS2ES might write one full packet and also a small rest
|
||||
int pid = cTSRemux::GetPid(Data + i + 1);
|
||||
if (Data[i + 3] & 0x10) { // got payload
|
||||
if (m_Pid == pid)
|
||||
m_Remux->PutTSPacket(Data + i);
|
||||
}
|
||||
used += TS_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
// Check if we're getting anywhere here:
|
||||
if (!synced && skipped >= 0) {
|
||||
if (skipped > MAXNONUSEFULDATA) {
|
||||
esyslog("ERROR: no useful data seen within %d byte of video stream", skipped);
|
||||
skipped = -1;
|
||||
if (exitOnFailure)
|
||||
cThread::EmergencyExit(true);
|
||||
}
|
||||
else
|
||||
skipped += used;
|
||||
}
|
||||
*/
|
||||
|
||||
return used;
|
||||
}
|
||||
|
25
remux/ts2es.h
Normal file
25
remux/ts2es.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef VDR_STREAMDEV_TS2ESREMUX_H
|
||||
#define VDR_STREAMDEV_TS2ESREMUX_H
|
||||
|
||||
#include "remux/tsremux.h"
|
||||
#include <vdr/ringbuffer.h>
|
||||
|
||||
class cTS2ES;
|
||||
class cRingBufferLinear;
|
||||
|
||||
class cTS2ESRemux: public cTSRemux {
|
||||
private:
|
||||
int m_Pid;
|
||||
cRingBufferLinear *m_ResultBuffer;
|
||||
cTS2ES *m_Remux;
|
||||
|
||||
public:
|
||||
cTS2ESRemux(int Pid);
|
||||
virtual ~cTS2ESRemux();
|
||||
|
||||
int Put(const uchar *Data, int Count);
|
||||
uchar *Get(int &Count) { return m_ResultBuffer->Get(Count); }
|
||||
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_TS2ESREMUX_H
|
211
remux/ts2ps.c
Normal file
211
remux/ts2ps.c
Normal file
@ -0,0 +1,211 @@
|
||||
#include "remux/ts2ps.h"
|
||||
#include "server/streamer.h"
|
||||
#include <vdr/channels.h>
|
||||
#include <vdr/device.h>
|
||||
|
||||
class cTS2PS {
|
||||
friend void PutPES(uint8_t *Buffer, int Size, void *Data);
|
||||
|
||||
private:
|
||||
ipack m_Ipack;
|
||||
int m_Pid;
|
||||
cRingBufferLinear *m_ResultBuffer;
|
||||
|
||||
public:
|
||||
cTS2PS(cRingBufferLinear *ResultBuffer, int Pid, uint8_t AudioCid = 0x00);
|
||||
~cTS2PS();
|
||||
|
||||
void PutTSPacket(const uint8_t *Buffer);
|
||||
|
||||
int Pid(void) const { return m_Pid; }
|
||||
};
|
||||
|
||||
void PutPES(uint8_t *Buffer, int Size, void *Data)
|
||||
{
|
||||
cTS2PS *This = (cTS2PS*)Data;
|
||||
int n = This->m_ResultBuffer->Put(Buffer, Size);
|
||||
if (n != Size)
|
||||
esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Size - n, Size);
|
||||
}
|
||||
|
||||
cTS2PS::cTS2PS(cRingBufferLinear *ResultBuffer, int Pid, uint8_t AudioCid)
|
||||
{
|
||||
m_ResultBuffer = ResultBuffer;
|
||||
m_Pid = Pid;
|
||||
|
||||
init_ipack(&m_Ipack, IPACKS, PutPES, false);
|
||||
m_Ipack.cid = AudioCid;
|
||||
m_Ipack.data = (void*)this;
|
||||
}
|
||||
|
||||
cTS2PS::~cTS2PS()
|
||||
{
|
||||
free_ipack(&m_Ipack);
|
||||
}
|
||||
|
||||
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, const int *APids, const int *DPids, const int *SPids):
|
||||
m_NumTracks(0),
|
||||
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, IPACKS)),
|
||||
m_ResultSkipped(0),
|
||||
m_Skipped(0),
|
||||
m_Synced(false),
|
||||
m_IsRadio(VPid == 0 || VPid == 1 || VPid == 0x1FFF)
|
||||
{
|
||||
m_ResultBuffer->SetTimeouts(0, 100);
|
||||
|
||||
if (VPid)
|
||||
m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, VPid);
|
||||
if (APids) {
|
||||
int n = 0;
|
||||
while (*APids && m_NumTracks < MAXTRACKS && n < MAXAPIDS)
|
||||
m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, *APids++, 0xC0 + n++);
|
||||
}
|
||||
if (DPids) {
|
||||
int n = 0;
|
||||
while (*DPids && m_NumTracks < MAXTRACKS && n < MAXDPIDS)
|
||||
m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, *DPids++, 0x80 + n++);
|
||||
}
|
||||
}
|
||||
|
||||
cTS2PSRemux::~cTS2PSRemux() {
|
||||
for (int i = 0; i < m_NumTracks; ++i)
|
||||
delete m_Remux[i];
|
||||
delete m_ResultBuffer;
|
||||
}
|
||||
|
||||
int cTS2PSRemux::Put(const uchar *Data, int Count)
|
||||
{
|
||||
int used = 0;
|
||||
|
||||
// Make sure we are looking at a TS packet:
|
||||
while (Count > TS_SIZE) {
|
||||
if (Data[0] == TS_SYNC_BYTE && Data[TS_SIZE] == TS_SYNC_BYTE)
|
||||
break;
|
||||
Data++;
|
||||
Count--;
|
||||
used++;
|
||||
}
|
||||
if (used)
|
||||
esyslog("ERROR: m_Skipped %d byte to sync on TS packet", used);
|
||||
|
||||
// Convert incoming TS data into multiplexed PS:
|
||||
|
||||
for (int i = 0; i < Count; i += TS_SIZE) {
|
||||
if (Count - i < TS_SIZE)
|
||||
break;
|
||||
if (Data[i] != TS_SYNC_BYTE)
|
||||
break;
|
||||
if (m_ResultBuffer->Free() < 2 * IPACKS)
|
||||
break; // A cTS2PS might write one full packet and also a small rest
|
||||
int pid = GetPid(Data + i + 1);
|
||||
if (Data[i + 3] & 0x10) { // got payload
|
||||
for (int t = 0; t < m_NumTracks; t++) {
|
||||
if (m_Remux[t]->Pid() == pid) {
|
||||
m_Remux[t]->PutTSPacket(Data + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
used += TS_SIZE;
|
||||
}
|
||||
|
||||
// Check if we're getting anywhere here:
|
||||
if (!m_Synced && m_Skipped >= 0)
|
||||
m_Skipped += used;
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
uchar *cTS2PSRemux::Get(int &Count)
|
||||
{
|
||||
// Remove any previously skipped data from the result buffer:
|
||||
|
||||
if (m_ResultSkipped > 0) {
|
||||
m_ResultBuffer->Del(m_ResultSkipped);
|
||||
m_ResultSkipped = 0;
|
||||
}
|
||||
|
||||
// Special VPID case to enable recording radio channels:
|
||||
if (m_IsRadio) {
|
||||
// Force syncing of radio channels to avoid "no useful data" error
|
||||
m_Synced = true;
|
||||
return m_ResultBuffer->Get(Count);
|
||||
}
|
||||
|
||||
// Check for frame borders:
|
||||
Count = 0;
|
||||
uchar *resultData = NULL;
|
||||
int resultCount = 0;
|
||||
uchar *data = m_ResultBuffer->Get(resultCount);
|
||||
if (data) {
|
||||
for (int i = 0; i < resultCount - 3; i++) {
|
||||
if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
|
||||
int l = 0;
|
||||
uchar StreamType = data[i + 3];
|
||||
if (VIDEO_STREAM_S <= StreamType && StreamType <= VIDEO_STREAM_E) {
|
||||
uchar pt = NO_PICTURE;
|
||||
l = ScanVideoPacket(data, resultCount, i, pt);
|
||||
if (l < 0)
|
||||
return resultData;
|
||||
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_ResultSkipped = i; // will drop everything before this position
|
||||
SetBrokenLink(data + i, l);
|
||||
m_Synced = true;
|
||||
}
|
||||
}
|
||||
else if (Count)
|
||||
return resultData;
|
||||
}
|
||||
} else {
|
||||
l = GetPacketLength(data, resultCount, i);
|
||||
if (l < 0)
|
||||
return resultData;
|
||||
}
|
||||
if (m_Synced) {
|
||||
if (!Count)
|
||||
resultData = data + i;
|
||||
Count += l;
|
||||
} else
|
||||
m_ResultSkipped = i + l;
|
||||
if (l > 0)
|
||||
i += l - 1; // the loop increments, too
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultData;
|
||||
}
|
||||
|
29
remux/ts2ps.h
Normal file
29
remux/ts2ps.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef VDR_STREAMDEV_TS2PESREMUX_H
|
||||
#define VDR_STREAMDEV_TS2PESREMUX_H
|
||||
|
||||
#include "remux/tsremux.h"
|
||||
#include <vdr/remux.h>
|
||||
#include <vdr/ringbuffer.h>
|
||||
|
||||
class cTS2PS;
|
||||
|
||||
class cTS2PSRemux: public cTSRemux {
|
||||
private:
|
||||
int m_NumTracks;
|
||||
cTS2PS *m_Remux[MAXTRACKS];
|
||||
cRingBufferLinear *m_ResultBuffer;
|
||||
int m_ResultSkipped;
|
||||
int m_Skipped;
|
||||
bool m_Synced;
|
||||
bool m_IsRadio;
|
||||
|
||||
public:
|
||||
cTS2PSRemux(int VPid, const int *Apids, const int *Dpids, const int *Spids);
|
||||
virtual ~cTS2PSRemux();
|
||||
|
||||
int Put(const uchar *Data, int Count);
|
||||
uchar *Get(int &Count);
|
||||
void Del(int Count) { m_ResultBuffer->Del(Count); }
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_TS2PESREMUX_H
|
59
remux/tsremux.c
Normal file
59
remux/tsremux.c
Normal file
@ -0,0 +1,59 @@
|
||||
#include "remux/tsremux.h"
|
||||
|
||||
#define SC_PICTURE 0x00 // "picture header"
|
||||
|
||||
void cTSRemux::SetBrokenLink(uchar *Data, int Length)
|
||||
{
|
||||
if (Length > 9 && Data[0] == 0 && Data[1] == 0 && Data[2] == 1 && (Data[3] & 0xF0) == 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");
|
||||
}
|
||||
|
||||
int cTSRemux::GetPid(const uchar *Data)
|
||||
{
|
||||
return (((uint16_t)Data[0] & PID_MASK_HI) << 8) | (Data[1] & 0xFF);
|
||||
}
|
||||
|
||||
int cTSRemux::GetPacketLength(const uchar *Data, int Count, int Offset)
|
||||
{
|
||||
// Returns the length of the packet starting at Offset, or -1 if Count is
|
||||
// too small to contain the entire packet.
|
||||
int Length = (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
|
||||
if (Length > 0 && Offset + Length <= Count)
|
||||
return Length;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (Length >= 8) {
|
||||
int i = Offset + 8; // the minimum length of the video packet header
|
||||
i += Data[i] + 1; // possible additional header bytes
|
||||
for (; i < Offset + Length - 5; 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;
|
||||
}
|
||||
|
33
remux/tsremux.h
Normal file
33
remux/tsremux.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef VDR_STREAMDEV_TSREMUX_H
|
||||
#define VDR_STREAMDEV_TSREMUX_H
|
||||
|
||||
#include "libdvbmpeg/transform.h"
|
||||
#include <vdr/remux.h>
|
||||
|
||||
#define RESULTBUFFERSIZE KILOBYTE(256)
|
||||
|
||||
class cTSRemux {
|
||||
protected:
|
||||
/*uchar m_ResultBuffer[RESULTBUFFERSIZE];
|
||||
int m_ResultCount;
|
||||
int m_ResultDelivered;
|
||||
int m_Synced;
|
||||
int m_Skipped;
|
||||
int m_Sync;
|
||||
|
||||
|
||||
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);
|
||||
static int GetPid(const uchar *Data);
|
||||
static int GetPacketLength(const uchar *Data, int Count, int Offset);
|
||||
static int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_TSREMUX_H
|
48
server/component.c
Normal file
48
server/component.c
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* $Id: component.c,v 1.3 2005/05/09 20:22:29 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/component.h"
|
||||
#include "server/connection.h"
|
||||
|
||||
cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp,
|
||||
uint ListenPort):
|
||||
m_Protocol(Protocol),
|
||||
m_ListenIp(ListenIp),
|
||||
m_ListenPort(ListenPort)
|
||||
{
|
||||
}
|
||||
|
||||
cServerComponent::~cServerComponent()
|
||||
{
|
||||
}
|
||||
|
||||
bool cServerComponent::Initialize(void)
|
||||
{
|
||||
if (!m_Listen.Listen(m_ListenIp, m_ListenPort, 5)) {
|
||||
esyslog("Streamdev: Couldn't listen (%s) %s:%d: %m",
|
||||
m_Protocol, m_ListenIp, m_ListenPort);
|
||||
return false;
|
||||
}
|
||||
isyslog("Streamdev: Listening (%s) on port %d", m_Protocol, m_ListenPort);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cServerComponent::Destruct(void)
|
||||
{
|
||||
m_Listen.Close();
|
||||
}
|
||||
|
||||
cServerConnection *cServerComponent::Accept(void)
|
||||
{
|
||||
cServerConnection *client = NewClient();
|
||||
if (client->Accept(m_Listen)) {
|
||||
isyslog("Streamdev: Accepted new client (%s) %s:%d", m_Protocol,
|
||||
client->RemoteIp().c_str(), client->RemotePort());
|
||||
return client;
|
||||
} else {
|
||||
esyslog("Streamdev: Couldn't accept (%s): %m", m_Protocol);
|
||||
delete client;
|
||||
}
|
||||
return NULL;
|
||||
}
|
51
server/component.h
Normal file
51
server/component.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* $Id: component.h,v 1.2 2005/05/09 20:22:29 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;
|
||||
|
||||
protected:
|
||||
/* Returns a new connection object for Accept() */
|
||||
virtual cServerConnection *NewClient(void) = 0;
|
||||
|
||||
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 Initialize(void);
|
||||
|
||||
/* Stops listening, override if you want to do things different */
|
||||
virtual void Destruct(void);
|
||||
|
||||
/* Get the listening socket's file number */
|
||||
virtual int Socket(void) const { return (int)m_Listen; }
|
||||
|
||||
/* Adds the listening socket to the Select object */
|
||||
virtual void Add(cTBSelect &Select) const { Select.Add(m_Listen); }
|
||||
|
||||
/* Accepts the connection on a NewClient() object and calls the
|
||||
Welcome() on it, override if you want to do things different */
|
||||
virtual cServerConnection *Accept(void);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_COMPONENT_H
|
18
server/componentHTTP.c
Normal file
18
server/componentHTTP.c
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* $Id: componentHTTP.c,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/componentHTTP.h"
|
||||
#include "server/connectionHTTP.h"
|
||||
#include "server/setup.h"
|
||||
|
||||
cComponentHTTP::cComponentHTTP(void):
|
||||
cServerComponent("HTTP", StreamdevServerSetup.HTTPBindIP,
|
||||
StreamdevServerSetup.HTTPServerPort)
|
||||
{
|
||||
}
|
||||
|
||||
cServerConnection *cComponentHTTP::NewClient(void)
|
||||
{
|
||||
return new cConnectionHTTP;
|
||||
}
|
18
server/componentHTTP.h
Normal file
18
server/componentHTTP.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* $Id: componentHTTP.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_HTTPSERVER_H
|
||||
#define VDR_STREAMDEV_HTTPSERVER_H
|
||||
|
||||
#include "server/component.h"
|
||||
|
||||
class cComponentHTTP: public cServerComponent {
|
||||
protected:
|
||||
virtual cServerConnection *NewClient(void);
|
||||
|
||||
public:
|
||||
cComponentHTTP(void);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_HTTPSERVER_H
|
18
server/componentVTP.c
Normal file
18
server/componentVTP.c
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* $Id: componentVTP.c,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/componentVTP.h"
|
||||
#include "server/connectionVTP.h"
|
||||
#include "server/setup.h"
|
||||
|
||||
cComponentVTP::cComponentVTP(void):
|
||||
cServerComponent("VTP", StreamdevServerSetup.VTPBindIP,
|
||||
StreamdevServerSetup.VTPServerPort)
|
||||
{
|
||||
}
|
||||
|
||||
cServerConnection *cComponentVTP::NewClient(void)
|
||||
{
|
||||
return new cConnectionVTP;
|
||||
}
|
18
server/componentVTP.h
Normal file
18
server/componentVTP.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* $Id: componentVTP.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVERS_SERVERVTP_H
|
||||
#define VDR_STREAMDEV_SERVERS_SERVERVTP_H
|
||||
|
||||
#include "server/component.h"
|
||||
|
||||
class cComponentVTP: public cServerComponent {
|
||||
protected:
|
||||
virtual cServerConnection *NewClient(void);
|
||||
|
||||
public:
|
||||
cComponentVTP(void);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_SERVERVTP_H
|
198
server/connection.c
Normal file
198
server/connection.c
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* $Id: connection.c,v 1.8 2007/01/15 12:00:19 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include "server/connection.h"
|
||||
#include "server/setup.h"
|
||||
#include "server/suspend.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
cServerConnection::cServerConnection(const char *Protocol):
|
||||
m_Protocol(Protocol),
|
||||
m_DeferClose(false),
|
||||
m_Pending(false),
|
||||
m_ReadBytes(0),
|
||||
m_WriteBytes(0),
|
||||
m_WriteIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
cServerConnection::~cServerConnection()
|
||||
{
|
||||
}
|
||||
|
||||
bool cServerConnection::Read(void)
|
||||
{
|
||||
int b;
|
||||
if ((b = cTBSocket::Read(m_ReadBuffer + m_ReadBytes,
|
||||
sizeof(m_ReadBuffer) - m_ReadBytes - 1)) < 0) {
|
||||
esyslog("ERROR: read from client (%s) %s:%d failed: %m",
|
||||
m_Protocol, RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (b == 0) {
|
||||
isyslog("client (%s) %s:%d has closed connection",
|
||||
m_Protocol, RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ReadBytes += b;
|
||||
m_ReadBuffer[m_ReadBytes] = '\0';
|
||||
|
||||
char *end;
|
||||
bool result = true;
|
||||
while ((end = strchr(m_ReadBuffer, '\012')) != NULL) {
|
||||
*end = '\0';
|
||||
if (end > m_ReadBuffer && *(end - 1) == '\015')
|
||||
*(end - 1) = '\0';
|
||||
|
||||
if (!Command(m_ReadBuffer))
|
||||
return false;
|
||||
|
||||
m_ReadBytes -= ++end - m_ReadBuffer;
|
||||
if (m_ReadBytes > 0)
|
||||
memmove(m_ReadBuffer, end, m_ReadBytes);
|
||||
}
|
||||
|
||||
if (m_ReadBytes == sizeof(m_ReadBuffer) - 1) {
|
||||
esyslog("ERROR: streamdev: input buffer overflow (%s) for %s:%d",
|
||||
m_Protocol, RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool cServerConnection::Write(void)
|
||||
{
|
||||
int b;
|
||||
if ((b = cTBSocket::Write(m_WriteBuffer + m_WriteIndex,
|
||||
m_WriteBytes - m_WriteIndex)) < 0) {
|
||||
esyslog("ERROR: streamdev: write to client (%s) %s:%d failed: %m",
|
||||
m_Protocol, RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_WriteIndex += b;
|
||||
if (m_WriteIndex == m_WriteBytes) {
|
||||
m_WriteIndex = 0;
|
||||
m_WriteBytes = 0;
|
||||
if (m_Pending)
|
||||
Command(NULL);
|
||||
if (m_DeferClose)
|
||||
return false;
|
||||
Flushed();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cServerConnection::Respond(const char *Message, bool Last, ...)
|
||||
{
|
||||
char *buffer;
|
||||
int length;
|
||||
va_list ap;
|
||||
va_start(ap, Last);
|
||||
length = vasprintf(&buffer, Message, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (m_WriteBytes + length + 2 > sizeof(m_WriteBuffer)) {
|
||||
esyslog("ERROR: streamdev: output buffer overflow (%s) for %s:%d",
|
||||
m_Protocol, RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
Dprintf("OUT: |%s|\n", buffer);
|
||||
memcpy(m_WriteBuffer + m_WriteBytes, buffer, length);
|
||||
free(buffer);
|
||||
|
||||
m_WriteBytes += length;
|
||||
m_WriteBuffer[m_WriteBytes++] = '\015';
|
||||
m_WriteBuffer[m_WriteBytes++] = '\012';
|
||||
m_Pending = !Last;
|
||||
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");
|
||||
|
||||
#if VDRVERSNUM < 10500
|
||||
device = cDevice::GetDevice(Channel, Priority);
|
||||
#else
|
||||
device = cDevice::GetDevice(Channel, Priority, false);
|
||||
#endif
|
||||
|
||||
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();
|
||||
#if VDRVERSNUM < 10500
|
||||
device = cDevice::GetDevice(Channel, Priority);
|
||||
#else
|
||||
device = cDevice::GetDevice(Channel, Priority, false);
|
||||
#endif
|
||||
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;
|
||||
}
|
90
server/connection.h
Normal file
90
server/connection.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* $Id: connection.h,v 1.3 2005/05/09 20:22:29 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVER_CONNECTION_H
|
||||
#define VDR_STREAMDEV_SERVER_CONNECTION_H
|
||||
|
||||
#include "tools/socket.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:
|
||||
const char *m_Protocol;
|
||||
bool m_DeferClose;
|
||||
bool m_Pending;
|
||||
|
||||
char m_ReadBuffer[MAXPARSEBUFFER];
|
||||
uint m_ReadBytes;
|
||||
|
||||
char m_WriteBuffer[MAXPARSEBUFFER];
|
||||
uint m_WriteBytes;
|
||||
uint m_WriteIndex;
|
||||
|
||||
protected:
|
||||
/* 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.
|
||||
Only one line at a time may be sent. If there are lines to follow, set
|
||||
Last to false. Command(NULL) will be called in the next cycle, so you can
|
||||
post the next line. */
|
||||
virtual bool Respond(const char *Message, bool Last = true, ...)
|
||||
__attribute__ ((format (printf, 2, 4)));
|
||||
|
||||
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(); }
|
||||
|
||||
/* Get the client socket's file number */
|
||||
virtual int Socket(void) const { return (int)*this; }
|
||||
|
||||
/* Determine if there is data to send or any command pending */
|
||||
virtual bool HasData(void) const;
|
||||
|
||||
/* Gets called by server when the socket can accept more data. Writes
|
||||
the buffer filled up by Respond(). Calls Command(NULL) if there is a
|
||||
command pending. Returns false in case of an error */
|
||||
virtual bool Write(void);
|
||||
|
||||
/* Gets called by server when there is incoming data to read. Calls
|
||||
Command() for each line. Returns false in case of an error, or if
|
||||
the connection shall be closed and removed by the server */
|
||||
virtual bool Read(void);
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
inline bool cServerConnection::HasData(void) const
|
||||
{
|
||||
return m_WriteBytes > 0 || m_Pending || m_DeferClose;
|
||||
}
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVER_CONNECTION_H
|
200
server/connectionHTTP.c
Normal file
200
server/connectionHTTP.c
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* $Id: connectionHTTP.c,v 1.10 2006/01/26 19:40:18 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "server/connectionHTTP.h"
|
||||
#include "server/setup.h"
|
||||
|
||||
cConnectionHTTP::cConnectionHTTP(void):
|
||||
cServerConnection("HTTP"),
|
||||
m_Status(hsRequest),
|
||||
m_LiveStreamer(NULL),
|
||||
m_Channel(NULL),
|
||||
m_Apid(0),
|
||||
m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
|
||||
m_ListChannel(NULL)
|
||||
{
|
||||
Dprintf("constructor hsRequest\n");
|
||||
}
|
||||
|
||||
cConnectionHTTP::~cConnectionHTTP()
|
||||
{
|
||||
delete m_LiveStreamer;
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::Command(char *Cmd)
|
||||
{
|
||||
Dprintf("command %s\n", Cmd);
|
||||
switch (m_Status) {
|
||||
case hsRequest:
|
||||
Dprintf("Request\n");
|
||||
m_Request = Cmd;
|
||||
m_Status = hsHeaders;
|
||||
return true;
|
||||
|
||||
case hsHeaders:
|
||||
if (*Cmd == '\0') {
|
||||
m_Status = hsBody;
|
||||
return ProcessRequest();
|
||||
}
|
||||
Dprintf("header\n");
|
||||
return true;
|
||||
}
|
||||
return false; // ??? shouldn't happen
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::ProcessRequest(void)
|
||||
{
|
||||
Dprintf("process\n");
|
||||
if (m_Request.substr(0, 4) == "GET " && CmdGET(m_Request.substr(4))) {
|
||||
switch (m_Job) {
|
||||
case hjListing:
|
||||
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>");
|
||||
|
||||
case hjTransfer:
|
||||
if (m_Channel == NULL) {
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 404 not found");
|
||||
}
|
||||
|
||||
m_LiveStreamer = new cStreamdevLiveStreamer(0);
|
||||
cDevice *device = GetDevice(m_Channel, 0);
|
||||
if (device != NULL) {
|
||||
device->SwitchChannel(m_Channel, false);
|
||||
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid)) {
|
||||
m_LiveStreamer->SetDevice(device);
|
||||
if (m_StreamType == stES && (m_Apid != 0 || ISRADIO(m_Channel))) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond("icy-name: %s", true, 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")
|
||||
&& Respond("");
|
||||
}
|
||||
}
|
||||
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 400 Bad Request")
|
||||
&& Respond("");
|
||||
}
|
||||
|
||||
void cConnectionHTTP::Flushed(void)
|
||||
{
|
||||
std::string line;
|
||||
|
||||
if (m_Status != hsBody)
|
||||
return;
|
||||
|
||||
switch (m_Job) {
|
||||
case hjListing:
|
||||
if (m_ListChannel == NULL) {
|
||||
Respond("</ul></body></html>");
|
||||
DeferClose();
|
||||
m_Status = hsFinished;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_ListChannel->GroupSep())
|
||||
line = (std::string)"<li>--- " + m_ListChannel->Name() + "---</li>";
|
||||
else {
|
||||
int index = 1;
|
||||
line = (std::string)"<li><a href=\"http://" + LocalIp() + ":"
|
||||
+ (const char*)itoa(StreamdevServerSetup.HTTPServerPort) + "/"
|
||||
+ StreamTypes[m_StreamType] + "/"
|
||||
+ (const char*)m_ListChannel->GetChannelID().ToString() + "\">"
|
||||
+ m_ListChannel->Name() + "</a> ";
|
||||
for (int i = 0; m_ListChannel->Apid(i) != 0; ++i, ++index) {
|
||||
line += "<a href=\"http://" + LocalIp() + ":"
|
||||
+ (const char*)itoa(StreamdevServerSetup.HTTPServerPort) + "/"
|
||||
+ StreamTypes[m_StreamType] + "/"
|
||||
+ (const char*)m_ListChannel->GetChannelID().ToString() + "+"
|
||||
+ (const char*)itoa(index) + "\">("
|
||||
+ m_ListChannel->Alang(i) + ")</a> ";
|
||||
}
|
||||
for (int i = 0; m_ListChannel->Dpid(i) != 0; ++i, ++index) {
|
||||
line += "<a href=\"http://" + LocalIp() + ":"
|
||||
+ (const char*)itoa(StreamdevServerSetup.HTTPServerPort) + "/"
|
||||
+ StreamTypes[m_StreamType] + "/"
|
||||
+ (const char*)m_ListChannel->GetChannelID().ToString() + "+"
|
||||
+ (const char*)itoa(index) + "\">("
|
||||
+ m_ListChannel->Dlang(i) + ")</a> ";
|
||||
}
|
||||
line += "</li>";
|
||||
}
|
||||
if (!Respond(line.c_str()))
|
||||
DeferClose();
|
||||
m_ListChannel = Channels.Next(m_ListChannel);
|
||||
break;
|
||||
|
||||
case hjTransfer:
|
||||
Dprintf("streamer start\n");
|
||||
m_LiveStreamer->Start(this);
|
||||
m_Status = hsFinished;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::CmdGET(const std::string &Opts)
|
||||
{
|
||||
const char *sp = Opts.c_str(), *ptr = sp, *ep;
|
||||
const cChannel *chan;
|
||||
int apid = 0, pos;
|
||||
|
||||
ptr = skipspace(ptr);
|
||||
while (*ptr == '/')
|
||||
++ptr;
|
||||
|
||||
if (strncasecmp(ptr, "PS/", 3) == 0) {
|
||||
m_StreamType = stPS;
|
||||
ptr += 3;
|
||||
} else if (strncasecmp(ptr, "PES/", 4) == 0) {
|
||||
m_StreamType = stPES;
|
||||
ptr += 4;
|
||||
} else if (strncasecmp(ptr, "TS/", 3) == 0) {
|
||||
m_StreamType = stTS;
|
||||
ptr += 3;
|
||||
} else if (strncasecmp(ptr, "ES/", 3) == 0) {
|
||||
m_StreamType = stES;
|
||||
ptr += 3;
|
||||
} else if (strncasecmp(ptr, "Extern/", 3) == 0) {
|
||||
m_StreamType = stExtern;
|
||||
ptr += 7;
|
||||
}
|
||||
|
||||
while (*ptr == '/')
|
||||
++ptr;
|
||||
for (ep = ptr + strlen(ptr); ep >= ptr && !isspace(*ep); --ep)
|
||||
;
|
||||
|
||||
std::string filespec = Opts.substr(ptr - sp, ep - ptr);
|
||||
Dprintf("substr: %s\n", filespec.c_str());
|
||||
|
||||
Dprintf("before channelfromstring\n");
|
||||
if (filespec == "" || filespec.substr(0, 12) == "channels.htm") {
|
||||
m_ListChannel = Channels.First();
|
||||
m_Job = hjListing;
|
||||
} else if ((chan = ChannelFromString(filespec.c_str(), &apid)) != NULL) {
|
||||
m_Channel = chan;
|
||||
m_Apid = apid;
|
||||
Dprintf("Apid is %d\n", apid);
|
||||
m_Job = hjTransfer;
|
||||
}
|
||||
Dprintf("after channelfromstring\n");
|
||||
return true;
|
||||
}
|
||||
|
58
server/connectionHTTP.h
Normal file
58
server/connectionHTTP.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* $Id: connectionHTTP.h,v 1.3 2005/02/11 16:44:15 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
|
||||
#define VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
|
||||
|
||||
#include "connection.h"
|
||||
#include "server/livestreamer.h"
|
||||
|
||||
#include <tools/select.h>
|
||||
|
||||
class cChannel;
|
||||
class cStreamdevLiveStreamer;
|
||||
|
||||
class cConnectionHTTP: public cServerConnection {
|
||||
private:
|
||||
enum eHTTPStatus {
|
||||
hsRequest,
|
||||
hsHeaders,
|
||||
hsBody,
|
||||
hsFinished,
|
||||
};
|
||||
|
||||
enum eHTTPJob {
|
||||
hjTransfer,
|
||||
hjListing,
|
||||
};
|
||||
|
||||
std::string m_Request;
|
||||
//std::map<std::string,std::string> m_Headers; TODO: later?
|
||||
eHTTPStatus m_Status;
|
||||
eHTTPJob m_Job;
|
||||
// job: transfer
|
||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||
const cChannel *m_Channel;
|
||||
int m_Apid;
|
||||
eStreamType m_StreamType;
|
||||
// job: listing
|
||||
const cChannel *m_ListChannel;
|
||||
|
||||
protected:
|
||||
bool ProcessRequest(void);
|
||||
|
||||
public:
|
||||
cConnectionHTTP(void);
|
||||
virtual ~cConnectionHTTP();
|
||||
|
||||
virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
|
||||
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
|
||||
|
||||
virtual bool Command(char *Cmd);
|
||||
bool CmdGET(const std::string &Opts);
|
||||
|
||||
virtual void Flushed(void);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
990
server/connectionVTP.c
Normal file
990
server/connectionVTP.c
Normal file
@ -0,0 +1,990 @@
|
||||
/*
|
||||
* $Id: connectionVTP.c,v 1.8 2007/03/02 15:27:07 schmirl 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 <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* VTP Response codes:
|
||||
220: Service ready
|
||||
221: Service closing connection
|
||||
451: Requested action aborted: try again
|
||||
500: Syntax error or Command unrecognized
|
||||
501: Wrong parameters or missing parameters
|
||||
550: Requested action not taken
|
||||
551: Data connection not accepted
|
||||
560: Channel not available currently
|
||||
561: Capability not known
|
||||
562: Pid not available currently
|
||||
563: Recording not available (currently?)
|
||||
*/
|
||||
|
||||
// --- cLSTEHandler -----------------------------------------------------------
|
||||
|
||||
class cLSTEHandler
|
||||
{
|
||||
private:
|
||||
enum eStates { Channel, Event, Title, Subtitle, Description, Vps,
|
||||
EndEvent, EndChannel, EndEPG };
|
||||
cConnectionVTP *m_Client;
|
||||
cSchedulesLock *m_SchedulesLock;
|
||||
const cSchedules *m_Schedules;
|
||||
const cSchedule *m_Schedule;
|
||||
const cEvent *m_Event;
|
||||
int m_Errno;
|
||||
char *m_Error;
|
||||
eStates m_State;
|
||||
bool m_Traverse;
|
||||
public:
|
||||
cLSTEHandler(cConnectionVTP *Client, const char *Option);
|
||||
~cLSTEHandler();
|
||||
bool Next(bool &Last);
|
||||
};
|
||||
|
||||
cLSTEHandler::cLSTEHandler(cConnectionVTP *Client, const char *Option):
|
||||
m_Client(Client),
|
||||
m_SchedulesLock(new cSchedulesLock(false, 500)),
|
||||
m_Schedules(cSchedules::Schedules(*m_SchedulesLock)),
|
||||
m_Schedule(NULL),
|
||||
m_Event(NULL),
|
||||
m_Errno(0),
|
||||
m_Error(NULL),
|
||||
m_State(Channel),
|
||||
m_Traverse(false)
|
||||
{
|
||||
eDumpMode dumpmode = dmAll;
|
||||
time_t attime = 0;
|
||||
|
||||
if (m_Schedules != NULL && *Option) {
|
||||
char buf[strlen(Option) + 1];
|
||||
strcpy(buf, Option);
|
||||
const char *delim = " \t";
|
||||
char *strtok_next;
|
||||
char *p = strtok_r(buf, delim, &strtok_next);
|
||||
while (p && dumpmode == dmAll) {
|
||||
if (strcasecmp(p, "NOW") == 0)
|
||||
dumpmode = dmPresent;
|
||||
else if (strcasecmp(p, "NEXT") == 0)
|
||||
dumpmode = dmFollowing;
|
||||
else if (strcasecmp(p, "AT") == 0) {
|
||||
dumpmode = dmAtTime;
|
||||
if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
|
||||
if (isnumber(p))
|
||||
attime = strtol(p, NULL, 10);
|
||||
else {
|
||||
m_Errno = 501;
|
||||
m_Error = strdup("Invalid time");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
m_Errno = 501;
|
||||
m_Error = strdup("Missing time");
|
||||
break;
|
||||
}
|
||||
} else if (!m_Schedule) {
|
||||
cChannel* Channel = NULL;
|
||||
if (isnumber(p))
|
||||
Channel = Channels.GetByNumber(strtol(Option, NULL, 10));
|
||||
else
|
||||
Channel = Channels.GetByChannelID(tChannelID::FromString(
|
||||
Option));
|
||||
if (Channel) {
|
||||
m_Schedule = m_Schedules->GetSchedule(Channel->GetChannelID());
|
||||
if (!m_Schedule) {
|
||||
m_Errno = 550;
|
||||
m_Error = strdup("No schedule found");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
m_Errno = 550;
|
||||
asprintf(&m_Error, "Channel \"%s\" not defined", p);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
m_Errno = 501;
|
||||
asprintf(&m_Error, "Unknown option: \"%s\"", p);
|
||||
break;
|
||||
}
|
||||
p = strtok_r(NULL, delim, &strtok_next);
|
||||
}
|
||||
} else if (m_Schedules == NULL) {
|
||||
m_Errno = 451;
|
||||
m_Error = strdup("EPG data is being modified, try again");
|
||||
}
|
||||
|
||||
if (m_Error == NULL) {
|
||||
if (m_Schedule != NULL)
|
||||
m_Schedules = NULL;
|
||||
else if (m_Schedules != NULL)
|
||||
m_Schedule = m_Schedules->First();
|
||||
|
||||
if (m_Schedule != NULL && m_Schedule->Events() != NULL) {
|
||||
switch (dumpmode) {
|
||||
case dmAll: m_Event = m_Schedule->Events()->First();
|
||||
m_Traverse = true;
|
||||
break;
|
||||
case dmPresent: m_Event = m_Schedule->GetPresentEvent();
|
||||
break;
|
||||
case dmFollowing: m_Event = m_Schedule->GetFollowingEvent();
|
||||
break;
|
||||
case dmAtTime: m_Event = m_Schedule->GetEventAround(attime);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cLSTEHandler::~cLSTEHandler()
|
||||
{
|
||||
delete m_SchedulesLock;
|
||||
if (m_Error != NULL)
|
||||
free(m_Error);
|
||||
}
|
||||
|
||||
bool cLSTEHandler::Next(bool &Last)
|
||||
{
|
||||
char *buffer;
|
||||
|
||||
if (m_Error != NULL) {
|
||||
Last = true;
|
||||
cString str(m_Error, true);
|
||||
m_Error = NULL;
|
||||
return m_Client->Respond(m_Errno, *str);
|
||||
}
|
||||
|
||||
Last = false;
|
||||
switch (m_State) {
|
||||
case Channel:
|
||||
if (m_Schedule != NULL) {
|
||||
cChannel *channel = Channels.GetByChannelID(m_Schedule->ChannelID(),
|
||||
true);
|
||||
if (channel != NULL) {
|
||||
m_State = Event;
|
||||
return m_Client->Respond(-215, "C %s %s",
|
||||
*channel->GetChannelID().ToString(),
|
||||
channel->Name());
|
||||
} else {
|
||||
esyslog("ERROR: vdr streamdev: unable to find channel %s by ID",
|
||||
*m_Schedule->ChannelID().ToString());
|
||||
m_State = EndChannel;
|
||||
return Next(Last);
|
||||
}
|
||||
} else {
|
||||
m_State = EndEPG;
|
||||
return Next(Last);
|
||||
}
|
||||
break;
|
||||
|
||||
case Event:
|
||||
if (m_Event != NULL) {
|
||||
m_State = Title;
|
||||
return m_Client->Respond(-215, "E %u %ld %d %X", m_Event->EventID(),
|
||||
m_Event->StartTime(), m_Event->Duration(),
|
||||
m_Event->TableID());
|
||||
} else {
|
||||
m_State = EndChannel;
|
||||
return Next(Last);
|
||||
}
|
||||
break;
|
||||
|
||||
case Title:
|
||||
m_State = Subtitle;
|
||||
if (!isempty(m_Event->Title()))
|
||||
return m_Client->Respond(-215, "T %s", m_Event->Title());
|
||||
else
|
||||
return Next(Last);
|
||||
break;
|
||||
|
||||
case Subtitle:
|
||||
m_State = Description;
|
||||
if (!isempty(m_Event->ShortText()))
|
||||
return m_Client->Respond(-215, "S %s", m_Event->ShortText());
|
||||
else
|
||||
return Next(Last);
|
||||
break;
|
||||
|
||||
case Description:
|
||||
m_State = Vps;
|
||||
if (!isempty(m_Event->Description())) {
|
||||
char *copy = strdup(m_Event->Description());
|
||||
cString cpy(copy, true);
|
||||
strreplace(copy, '\n', '|');
|
||||
return m_Client->Respond(-215, "D %s", copy);
|
||||
} else
|
||||
return Next(Last);
|
||||
break;
|
||||
|
||||
case Vps:
|
||||
m_State = EndEvent;
|
||||
if (m_Event->Vps())
|
||||
return m_Client->Respond(-215, "V %ld", m_Event->Vps());
|
||||
else
|
||||
return Next(Last);
|
||||
break;
|
||||
|
||||
case EndEvent:
|
||||
if (m_Traverse)
|
||||
m_Event = m_Schedule->Events()->Next(m_Event);
|
||||
else
|
||||
m_Event = NULL;
|
||||
|
||||
if (m_Event != NULL)
|
||||
m_State = Event;
|
||||
else
|
||||
m_State = EndChannel;
|
||||
|
||||
return m_Client->Respond(-215, "e");
|
||||
|
||||
case EndChannel:
|
||||
if (m_Schedules != NULL) {
|
||||
m_Schedule = m_Schedules->Next(m_Schedule);
|
||||
if (m_Schedule != NULL) {
|
||||
if (m_Schedule->Events() != NULL)
|
||||
m_Event = m_Schedule->Events()->First();
|
||||
m_State = Channel;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Schedules == NULL || m_Schedule == NULL)
|
||||
m_State = EndEPG;
|
||||
|
||||
return m_Client->Respond(-215, "c");
|
||||
|
||||
case EndEPG:
|
||||
Last = true;
|
||||
return m_Client->Respond(215, "End of EPG data");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- cLSTCHandler -----------------------------------------------------------
|
||||
|
||||
class cLSTCHandler
|
||||
{
|
||||
private:
|
||||
cConnectionVTP *m_Client;
|
||||
const cChannel *m_Channel;
|
||||
char *m_Option;
|
||||
int m_Errno;
|
||||
char *m_Error;
|
||||
bool m_Traverse;
|
||||
public:
|
||||
cLSTCHandler(cConnectionVTP *Client, const char *Option);
|
||||
~cLSTCHandler();
|
||||
bool Next(bool &Last);
|
||||
};
|
||||
|
||||
cLSTCHandler::cLSTCHandler(cConnectionVTP *Client, const char *Option):
|
||||
m_Client(Client),
|
||||
m_Channel(NULL),
|
||||
m_Option(NULL),
|
||||
m_Errno(0),
|
||||
m_Error(NULL),
|
||||
m_Traverse(false)
|
||||
{
|
||||
if (!Channels.Lock(false, 500)) {
|
||||
m_Errno = 451;
|
||||
m_Error = strdup("Channels are being modified - try again");
|
||||
} else if (*Option) {
|
||||
if (isnumber(Option)) {
|
||||
m_Channel = Channels.GetByNumber(strtol(Option, NULL, 10));
|
||||
if (m_Channel == NULL) {
|
||||
m_Errno = 501;
|
||||
asprintf(&m_Error, "Channel \"%s\" not defined", Option);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
int i = 1;
|
||||
m_Traverse = true;
|
||||
m_Option = strdup(Option);
|
||||
while (i <= Channels.MaxNumber()) {
|
||||
m_Channel = Channels.GetByNumber(i, 1);
|
||||
if (strcasestr(m_Channel->Name(), Option) != NULL)
|
||||
break;
|
||||
i = m_Channel->Number() + 1;
|
||||
}
|
||||
|
||||
if (i > Channels.MaxNumber()) {
|
||||
m_Errno = 501;
|
||||
asprintf(&m_Error, "Channel \"%s\" not defined", Option);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (Channels.MaxNumber() >= 1) {
|
||||
m_Channel = Channels.GetByNumber(1, 1);
|
||||
m_Traverse = true;
|
||||
} else {
|
||||
m_Errno = 550;
|
||||
m_Error = strdup("No channels defined");
|
||||
}
|
||||
}
|
||||
|
||||
cLSTCHandler::~cLSTCHandler()
|
||||
{
|
||||
Channels.Unlock();
|
||||
if (m_Error != NULL)
|
||||
free(m_Error);
|
||||
if (m_Option != NULL)
|
||||
free(m_Option);
|
||||
}
|
||||
|
||||
bool cLSTCHandler::Next(bool &Last)
|
||||
{
|
||||
if (m_Error != NULL) {
|
||||
Last = true;
|
||||
cString str(m_Error, true);
|
||||
m_Error = NULL;
|
||||
return m_Client->Respond(m_Errno, *str);
|
||||
}
|
||||
|
||||
int number;
|
||||
char *buffer;
|
||||
|
||||
number = m_Channel->Number();
|
||||
buffer = strdup(*m_Channel->ToText());
|
||||
buffer[strlen(buffer) - 1] = '\0'; // remove \n
|
||||
cString str(buffer, true);
|
||||
|
||||
Last = true;
|
||||
if (m_Traverse) {
|
||||
int i = m_Channel->Number() + 1;
|
||||
while (i <= Channels.MaxNumber()) {
|
||||
m_Channel = Channels.GetByNumber(i, 1);
|
||||
if (m_Channel != NULL) {
|
||||
if (m_Option == NULL || strcasestr(m_Channel->Name(),
|
||||
m_Option) != NULL)
|
||||
break;
|
||||
i = m_Channel->Number() + 1;
|
||||
} else {
|
||||
m_Errno = 501;
|
||||
asprintf(&m_Error, "Channel \"%d\" not found", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (i < Channels.MaxNumber())
|
||||
Last = false;
|
||||
}
|
||||
|
||||
return m_Client->Respond(Last ? 250 : -250, "%d %s", number, buffer);
|
||||
}
|
||||
|
||||
// --- cLSTTHandler -----------------------------------------------------------
|
||||
|
||||
class cLSTTHandler
|
||||
{
|
||||
private:
|
||||
cConnectionVTP *m_Client;
|
||||
cTimer *m_Timer;
|
||||
int m_Index;
|
||||
int m_Errno;
|
||||
char *m_Error;
|
||||
bool m_Traverse;
|
||||
public:
|
||||
cLSTTHandler(cConnectionVTP *Client, const char *Option);
|
||||
~cLSTTHandler();
|
||||
bool Next(bool &Last);
|
||||
};
|
||||
|
||||
cLSTTHandler::cLSTTHandler(cConnectionVTP *Client, const char *Option):
|
||||
m_Client(Client),
|
||||
m_Timer(NULL),
|
||||
m_Index(0),
|
||||
m_Errno(0),
|
||||
m_Error(NULL),
|
||||
m_Traverse(false)
|
||||
{
|
||||
if (*Option) {
|
||||
if (isnumber(Option)) {
|
||||
m_Timer = Timers.Get(strtol(Option, NULL, 10) - 1);
|
||||
if (m_Timer == NULL) {
|
||||
m_Errno = 501;
|
||||
asprintf(&m_Error, "Timer \"%s\" not defined", Option);
|
||||
}
|
||||
} else {
|
||||
m_Errno = 501;
|
||||
asprintf(&m_Error, "Error in timer number \"%s\"", Option);
|
||||
}
|
||||
} else if (Timers.Count()) {
|
||||
m_Traverse = true;
|
||||
m_Index = 0;
|
||||
m_Timer = Timers.Get(m_Index);
|
||||
if (m_Timer == NULL) {
|
||||
m_Errno = 501;
|
||||
asprintf(&m_Error, "Timer \"%d\" not found", m_Index + 1);
|
||||
}
|
||||
} else {
|
||||
m_Errno = 550;
|
||||
m_Error = strdup("No timers defined");
|
||||
}
|
||||
}
|
||||
|
||||
cLSTTHandler::~cLSTTHandler()
|
||||
{
|
||||
if (m_Error != NULL)
|
||||
free(m_Error);
|
||||
}
|
||||
|
||||
bool cLSTTHandler::Next(bool &Last)
|
||||
{
|
||||
if (m_Error != NULL) {
|
||||
Last = true;
|
||||
cString str(m_Error, true);
|
||||
m_Error = NULL;
|
||||
return m_Client->Respond(m_Errno, *str);
|
||||
}
|
||||
|
||||
bool result;
|
||||
char *buffer;
|
||||
Last = !m_Traverse || m_Index >= Timers.Count() - 1;
|
||||
buffer = strdup(*m_Timer->ToText());
|
||||
buffer[strlen(buffer) - 1] = '\0'; // strip \n
|
||||
result = m_Client->Respond(Last ? 250 : -250, "%d %s", m_Timer->Index() + 1,
|
||||
buffer);
|
||||
free(buffer);
|
||||
|
||||
if (m_Traverse && !Last) {
|
||||
m_Timer = Timers.Get(++m_Index);
|
||||
if (m_Timer == NULL) {
|
||||
m_Errno = 501;
|
||||
asprintf(&m_Error, "Timer \"%d\" not found", m_Index + 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// --- cConnectionVTP ---------------------------------------------------------
|
||||
|
||||
cConnectionVTP::cConnectionVTP(void):
|
||||
cServerConnection("VTP"),
|
||||
m_LiveSocket(NULL),
|
||||
m_LiveStreamer(NULL),
|
||||
m_LastCommand(NULL),
|
||||
m_NoTSPIDS(false),
|
||||
m_LSTEHandler(NULL),
|
||||
m_LSTCHandler(NULL),
|
||||
m_LSTTHandler(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
cConnectionVTP::~cConnectionVTP()
|
||||
{
|
||||
if (m_LastCommand != NULL)
|
||||
free(m_LastCommand);
|
||||
delete m_LiveStreamer;
|
||||
delete m_LiveSocket;
|
||||
delete m_LSTTHandler;
|
||||
delete m_LSTCHandler;
|
||||
delete m_LSTEHandler;
|
||||
}
|
||||
|
||||
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 *param = NULL;
|
||||
|
||||
if (Cmd != NULL) {
|
||||
if (m_LastCommand != NULL) {
|
||||
esyslog("ERROR: streamdev: protocol violation (VTP) from %s:%d",
|
||||
RemoteIp().c_str(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((param = strchr(Cmd, ' ')) != NULL)
|
||||
*(param++) = '\0';
|
||||
else
|
||||
param = Cmd + strlen(Cmd);
|
||||
m_LastCommand = strdup(Cmd);
|
||||
} else {
|
||||
Cmd = m_LastCommand;
|
||||
param = NULL;
|
||||
}
|
||||
|
||||
if (strcasecmp(Cmd, "LSTE") == 0) return CmdLSTE(param);
|
||||
//else if (strcasecmp(Cmd, "LSTR") == 0) return CmdLSTR(param);
|
||||
else if (strcasecmp(Cmd, "LSTT") == 0) return CmdLSTT(param);
|
||||
else if (strcasecmp(Cmd, "LSTC") == 0) return CmdLSTC(param);
|
||||
|
||||
if (param == NULL) {
|
||||
esyslog("ERROR: streamdev: this seriously shouldn't happen at %s:%d",
|
||||
__FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcasecmp(Cmd, "CAPS") == 0) return CmdCAPS(param);
|
||||
else if (strcasecmp(Cmd, "PROV") == 0) return CmdPROV(param);
|
||||
else if (strcasecmp(Cmd, "PORT") == 0) return CmdPORT(param);
|
||||
else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(param);
|
||||
else if (strcasecmp(Cmd, "ADDP") == 0) return CmdADDP(param);
|
||||
else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(param);
|
||||
else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(param);
|
||||
else if (strcasecmp(Cmd, "DELF") == 0) return CmdDELF(param);
|
||||
else if (strcasecmp(Cmd, "ABRT") == 0) return CmdABRT(param);
|
||||
else if (strcasecmp(Cmd, "QUIT") == 0) return CmdQUIT(param);
|
||||
else if (strcasecmp(Cmd, "SUSP") == 0) return CmdSUSP(param);
|
||||
// Commands adopted from SVDRP
|
||||
//else if (strcasecmp(Cmd, "DELR") == 0) return CmdDELR(param);
|
||||
else if (strcasecmp(Cmd, "MODT") == 0) return CmdMODT(param);
|
||||
else if (strcasecmp(Cmd, "NEWT") == 0) return CmdNEWT(param);
|
||||
else if (strcasecmp(Cmd, "DELT") == 0) return CmdDELT(param);
|
||||
else
|
||||
return Respond(500, "Unknown Command \"%s\"", Cmd);
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdCAPS(char *Opts)
|
||||
{
|
||||
char *buffer;
|
||||
|
||||
if (strcasecmp(Opts, "TS") == 0) {
|
||||
m_NoTSPIDS = true;
|
||||
return Respond(220, "Ignored, capability \"%s\" accepted for "
|
||||
"compatibility", Opts);
|
||||
}
|
||||
|
||||
if (strcasecmp(Opts, "TSPIDS") == 0) {
|
||||
m_NoTSPIDS = false;
|
||||
return Respond(220, "Capability \"%s\" accepted", Opts);
|
||||
}
|
||||
|
||||
return Respond(561, "Capability \"%s\" not known", Opts);
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdPROV(char *Opts)
|
||||
{
|
||||
const 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, "Undefined channel \"%s\"", 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 != 0)
|
||||
return Respond(501, "Wrong connection id %d", 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_LiveSocket = new cTBSocket(SOCK_STREAM);
|
||||
if (!m_LiveSocket->Connect(dataip, dataport)) {
|
||||
esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s",
|
||||
dataip, dataport, strerror(errno));
|
||||
DELETENULL(m_LiveSocket);
|
||||
return Respond(551, "Couldn't open data connection");
|
||||
}
|
||||
|
||||
if (id == siLive)
|
||||
m_LiveStreamer->Start(m_LiveSocket);
|
||||
|
||||
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, "Undefined channel \"%s\"", 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_NoTSPIDS ? stTS : stTSPIDS);
|
||||
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 %d available", pid)
|
||||
: Respond(560, "Pid %d not available", pid);
|
||||
}
|
||||
|
||||
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 %d stopped", pid)
|
||||
: Respond(560, "Pid %d not transferring", pid);
|
||||
}
|
||||
|
||||
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 %d transferring", pid)
|
||||
: Respond(560, "Filter %d not available", pid);
|
||||
#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 %d stopped", pid)
|
||||
: Respond(560, "Filter %d not transferring", pid);
|
||||
#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");
|
||||
|
||||
switch (id) {
|
||||
case 0: DELETENULL(m_LiveStreamer); break;
|
||||
}
|
||||
|
||||
DELETENULL(m_LiveSocket);
|
||||
return Respond(220, "Data connection closed");
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdQUIT(char *Opts)
|
||||
{
|
||||
DeferClose();
|
||||
return Respond(221, "Video Disk Recorder closing connection");
|
||||
}
|
||||
|
||||
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 extended from SVDRP
|
||||
|
||||
template<class cHandler>
|
||||
bool cConnectionVTP::CmdLSTX(cHandler *&Handler, char *Option)
|
||||
{
|
||||
if (Option != NULL) {
|
||||
delete Handler;
|
||||
Handler = new cHandler(this, Option);
|
||||
}
|
||||
|
||||
bool last, result = false;
|
||||
if (Handler != NULL)
|
||||
result = Handler->Next(last);
|
||||
else
|
||||
esyslog("ERROR: vdr streamdev: Handler in LSTX command is NULL");
|
||||
if (!result || last)
|
||||
DELETENULL(Handler);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdLSTE(char *Option)
|
||||
{
|
||||
return CmdLSTX(m_LSTEHandler, Option);
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdLSTC(char *Option)
|
||||
{
|
||||
return CmdLSTX(m_LSTCHandler, Option);
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdLSTT(char *Option)
|
||||
{
|
||||
return CmdLSTX(m_LSTTHandler, Option);
|
||||
}
|
||||
|
||||
// Functions adopted from SVDRP
|
||||
#define INIT_WRAPPER() bool _res
|
||||
#define Reply(c,m...) _res = Respond(c,m)
|
||||
#define EXIT_WRAPPER() return _res
|
||||
|
||||
bool cConnectionVTP::CmdMODT(const 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)
|
||||
t.SetFlags(tfActive);
|
||||
else if (strcasecmp(tail, "OFF") == 0)
|
||||
t.ClrFlags(tfActive);
|
||||
else if (!t.Parse(tail)) {
|
||||
Reply(501, "Error in timer settings");
|
||||
EXIT_WRAPPER();
|
||||
}
|
||||
*timer = t;
|
||||
Timers.SetModified();
|
||||
isyslog("timer %s modified (%s)", *timer->ToDescr(),
|
||||
timer->HasFlags(tfActive) ? "active" : "inactive");
|
||||
Reply(250, "%d %s", timer->Index() + 1, *timer->ToText());
|
||||
} 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(const 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.SetModified();
|
||||
isyslog("timer %s added", *timer->ToDescr());
|
||||
Reply(250, "%d %s", timer->Index() + 1, *timer->ToText());
|
||||
EXIT_WRAPPER();
|
||||
} else
|
||||
Reply(550, "Timer already defined: %d %s", t->Index() + 1,
|
||||
*t->ToText());
|
||||
} else
|
||||
Reply(501, "Error in timer settings");
|
||||
delete timer;
|
||||
} else
|
||||
Reply(501, "Missing timer settings");
|
||||
EXIT_WRAPPER();
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdDELT(const char *Option)
|
||||
{
|
||||
INIT_WRAPPER();
|
||||
if (*Option) {
|
||||
if (isnumber(Option)) {
|
||||
cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
|
||||
if (timer) {
|
||||
if (!timer->Recording()) {
|
||||
isyslog("deleting timer %s", *timer->ToDescr());
|
||||
Timers.Del(timer);
|
||||
Timers.SetModified();
|
||||
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::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::Respond(int Code, const char *Message, ...)
|
||||
{
|
||||
char *buffer;
|
||||
va_list ap;
|
||||
va_start(ap, Message);
|
||||
vasprintf(&buffer, Message, ap);
|
||||
va_end(ap);
|
||||
cString str(buffer, true);
|
||||
|
||||
if (Code >= 0 && m_LastCommand != NULL) {
|
||||
free(m_LastCommand);
|
||||
m_LastCommand = NULL;
|
||||
}
|
||||
|
||||
return cServerConnection::Respond("%03d%c%s", Code >= 0,
|
||||
Code < 0 ? -Code : Code,
|
||||
Code < 0 ? '-' : ' ', buffer);
|
||||
}
|
75
server/connectionVTP.h
Normal file
75
server/connectionVTP.h
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
||||
#define VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
||||
|
||||
#include "server/connection.h"
|
||||
|
||||
class cTBSocket;
|
||||
class cStreamdevLiveStreamer;
|
||||
class cLSTEHandler;
|
||||
class cLSTCHandler;
|
||||
class cLSTTHandler;
|
||||
|
||||
class cConnectionVTP: public cServerConnection {
|
||||
friend class cLSTEHandler;
|
||||
// if your compiler doesn't understand the following statement
|
||||
// (e.g. gcc 2.x), simply remove it and try again ;-)
|
||||
using cServerConnection::Respond;
|
||||
|
||||
private:
|
||||
cTBSocket *m_LiveSocket;
|
||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||
|
||||
char *m_LastCommand;
|
||||
bool m_NoTSPIDS;
|
||||
|
||||
// Members adopted for SVDRP
|
||||
cRecordings Recordings;
|
||||
cLSTEHandler *m_LSTEHandler;
|
||||
cLSTCHandler *m_LSTCHandler;
|
||||
cLSTTHandler *m_LSTTHandler;
|
||||
|
||||
protected:
|
||||
template<class cHandler>
|
||||
bool CmdLSTX(cHandler *&Handler, char *Option);
|
||||
|
||||
public:
|
||||
cConnectionVTP(void);
|
||||
virtual ~cConnectionVTP();
|
||||
|
||||
virtual void Welcome(void);
|
||||
virtual void Reject(void);
|
||||
|
||||
virtual void Detach(void);
|
||||
virtual void Attach(void);
|
||||
|
||||
virtual 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);
|
||||
|
||||
// Thread-safe implementations of SVDRP commands
|
||||
bool CmdLSTE(char *Opts);
|
||||
bool CmdLSTC(char *Opts);
|
||||
bool CmdLSTT(char *Opts);
|
||||
|
||||
// Commands adopted from SVDRP
|
||||
bool CmdMODT(const char *Option);
|
||||
bool CmdNEWT(const char *Option);
|
||||
bool CmdDELT(const char *Option);
|
||||
|
||||
//bool CmdLSTR(char *Opts);
|
||||
//bool CmdDELR(char *Opts);
|
||||
|
||||
bool Respond(int Code, const char *Message, ...)
|
||||
__attribute__ ((format (printf, 3, 4)));
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
41
server/livefilter.c
Normal file
41
server/livefilter.c
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* $Id: livefilter.c,v 1.2 2005/02/08 13:59:16 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)
|
||||
{
|
||||
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)
|
||||
m_Streamer->ReportOverflow(TS_SIZE - p);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // VDRVERSNUM >= 10300
|
31
server/livefilter.h
Normal file
31
server/livefilter.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* $Id: livefilter.h,v 1.2 2005/11/07 19:28:41 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMEV_LIVEFILTER_H
|
||||
#define VDR_STREAMEV_LIVEFILTER_H
|
||||
|
||||
#include <vdr/config.h>
|
||||
|
||||
# if VDRVERSNUM >= 10300
|
||||
|
||||
#include <vdr/filter.h>
|
||||
|
||||
class cStreamdevLiveStreamer;
|
||||
|
||||
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
|
298
server/livestreamer.c
Normal file
298
server/livestreamer.c
Normal file
@ -0,0 +1,298 @@
|
||||
#include <vdr/ringbuffer.h>
|
||||
|
||||
#include "server/livestreamer.h"
|
||||
#include "remux/ts2ps.h"
|
||||
#include "remux/ts2es.h"
|
||||
#include "remux/extern.h"
|
||||
#include "common.h"
|
||||
|
||||
// --- cStreamdevLiveReceiver -------------------------------------------------
|
||||
|
||||
#if VDRVERSNUM < 10500
|
||||
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, int Ca,
|
||||
int Priority, const int *Pids):
|
||||
cReceiver(Ca, Priority, 0, Pids),
|
||||
#else
|
||||
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, tChannelID ChannelID,
|
||||
int Priority, const int *Pids):
|
||||
cReceiver(ChannelID, Priority, 0, Pids),
|
||||
#endif
|
||||
m_Streamer(Streamer)
|
||||
{
|
||||
}
|
||||
|
||||
cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
|
||||
{
|
||||
Dprintf("Killing live receiver\n");
|
||||
Detach();
|
||||
}
|
||||
|
||||
void cStreamdevLiveReceiver::Receive(uchar *Data, int Length) {
|
||||
int p = m_Streamer->Receive(Data, Length);
|
||||
if (p != Length)
|
||||
m_Streamer->ReportOverflow(Length - p);
|
||||
}
|
||||
|
||||
// --- cStreamdevLiveStreamer -------------------------------------------------
|
||||
|
||||
cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority):
|
||||
cStreamdevStreamer("streamdev-livestreaming"),
|
||||
m_Priority(Priority),
|
||||
m_NumPids(0),
|
||||
m_StreamType(stTSPIDS),
|
||||
m_Channel(NULL),
|
||||
m_Device(NULL),
|
||||
m_Receiver(NULL),
|
||||
m_PESRemux(NULL),
|
||||
m_ESRemux(NULL),
|
||||
m_PSRemux(NULL),
|
||||
m_ExtRemux(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
cStreamdevLiveStreamer::~cStreamdevLiveStreamer()
|
||||
{
|
||||
Dprintf("Desctructing Live streamer\n");
|
||||
Stop();
|
||||
delete m_Receiver;
|
||||
delete m_PESRemux;
|
||||
delete m_ESRemux;
|
||||
delete m_PSRemux;
|
||||
delete m_ExtRemux;
|
||||
#if VDRVERSNUM >= 10300
|
||||
//delete m_Filter; TODO
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cStreamdevLiveStreamer::SetPid(int Pid, bool On)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (Pid == 0)
|
||||
return true;
|
||||
|
||||
if (On) {
|
||||
for (idx = 0; idx < m_NumPids; ++idx) {
|
||||
if (m_Pids[idx] == Pid)
|
||||
return true; // No change needed
|
||||
}
|
||||
|
||||
if (m_NumPids == MAXRECEIVEPIDS) {
|
||||
esyslog("ERROR: Streamdev: No free slot to receive pid %d\n", Pid);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Pids[m_NumPids++] = Pid;
|
||||
m_Pids[m_NumPids] = 0;
|
||||
} else {
|
||||
for (idx = 0; idx < m_NumPids; ++idx) {
|
||||
if (m_Pids[idx] == Pid) {
|
||||
--m_NumPids;
|
||||
memmove(&m_Pids[idx], &m_Pids[idx + 1], sizeof(int) * (m_NumPids - idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DELETENULL(m_Receiver);
|
||||
if (m_NumPids > 0) {
|
||||
Dprintf("Creating Receiver to respect changed pids\n");
|
||||
#if VDRVERSNUM < 10500
|
||||
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->Ca(), m_Priority, m_Pids);
|
||||
#else
|
||||
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->GetChannelID(), m_Priority, m_Pids);
|
||||
#endif
|
||||
if (IsRunning() && m_Device != NULL) {
|
||||
Dprintf("Attaching new receiver\n");
|
||||
Attach();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, int Apid)
|
||||
{
|
||||
Dprintf("Initializing Remuxer for full channel transfer\n");
|
||||
printf("ca pid: %d\n", Channel->Ca());
|
||||
m_Channel = Channel;
|
||||
m_StreamType = StreamType;
|
||||
switch (m_StreamType) {
|
||||
case stES:
|
||||
{
|
||||
int pid = ISRADIO(m_Channel) ? m_Channel->Apid(0) : m_Channel->Vpid();
|
||||
if (Apid != 0)
|
||||
pid = Apid;
|
||||
m_ESRemux = new cTS2ESRemux(pid);
|
||||
return SetPid(pid, true);
|
||||
}
|
||||
|
||||
case stPES:
|
||||
Dprintf("PES\n");
|
||||
m_PESRemux = new cRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
||||
m_Channel->Spids(), false);
|
||||
if (Apid != 0)
|
||||
return SetPid(m_Channel->Vpid(), true)
|
||||
&& SetPid(Apid, true);
|
||||
else
|
||||
return SetPid(m_Channel->Vpid(), true)
|
||||
&& SetPid(m_Channel->Apid(0), true)
|
||||
&& SetPid(m_Channel->Dpid(0), true);
|
||||
|
||||
case stPS:
|
||||
m_PSRemux = new cTS2PSRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
||||
m_Channel->Spids());
|
||||
if (Apid != 0)
|
||||
return SetPid(m_Channel->Vpid(), true)
|
||||
&& SetPid(Apid, true);
|
||||
else
|
||||
return SetPid(m_Channel->Vpid(), true)
|
||||
&& SetPid(m_Channel->Apid(0), true)
|
||||
&& SetPid(m_Channel->Dpid(0), true);
|
||||
|
||||
case stTS:
|
||||
if (Apid != 0)
|
||||
return SetPid(m_Channel->Vpid(), true)
|
||||
&& SetPid(Apid, true);
|
||||
else
|
||||
return SetPid(m_Channel->Vpid(), true)
|
||||
&& SetPid(m_Channel->Apid(0), true)
|
||||
&& SetPid(m_Channel->Dpid(0), true);
|
||||
|
||||
case stExtern:
|
||||
m_ExtRemux = new cExternRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
|
||||
m_Channel->Spids());
|
||||
if (Apid != 0)
|
||||
return SetPid(m_Channel->Vpid(), true)
|
||||
&& SetPid(Apid, true);
|
||||
else
|
||||
return SetPid(m_Channel->Vpid(), true)
|
||||
&& SetPid(m_Channel->Apid(0), true)
|
||||
&& SetPid(m_Channel->Dpid(0), true);
|
||||
|
||||
case stTSPIDS:
|
||||
Dprintf("pid streaming mode\n");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cStreamdevLiveStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On)
|
||||
{
|
||||
#if 0
|
||||
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
|
||||
}
|
||||
|
||||
int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
|
||||
{
|
||||
switch (m_StreamType) {
|
||||
case stTS:
|
||||
case stTSPIDS:
|
||||
return cStreamdevStreamer::Put(Data, Count);
|
||||
|
||||
case stPES:
|
||||
return m_PESRemux->Put(Data, Count);
|
||||
|
||||
case stES:
|
||||
return m_ESRemux->Put(Data, Count);
|
||||
|
||||
case stPS:
|
||||
return m_PSRemux->Put(Data, Count);
|
||||
|
||||
case stExtern:
|
||||
return m_ExtRemux->Put(Data, Count);
|
||||
|
||||
default: // shouldn't happen???
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uchar *cStreamdevLiveStreamer::Get(int &Count)
|
||||
{
|
||||
switch (m_StreamType) {
|
||||
case stTS:
|
||||
case stTSPIDS:
|
||||
return cStreamdevStreamer::Get(Count);
|
||||
|
||||
case stPES:
|
||||
return m_PESRemux->Get(Count);
|
||||
|
||||
case stES:
|
||||
return m_ESRemux->Get(Count);
|
||||
|
||||
case stPS:
|
||||
return m_PSRemux->Get(Count);
|
||||
|
||||
case stExtern:
|
||||
return m_ExtRemux->Get(Count);
|
||||
|
||||
default: // shouldn't happen???
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::Del(int Count)
|
||||
{
|
||||
switch (m_StreamType) {
|
||||
case stTS:
|
||||
case stTSPIDS:
|
||||
cStreamdevStreamer::Del(Count);
|
||||
break;
|
||||
|
||||
case stPES:
|
||||
m_PESRemux->Del(Count);
|
||||
break;
|
||||
|
||||
case stES:
|
||||
m_ESRemux->Del(Count);
|
||||
break;
|
||||
|
||||
case stPS:
|
||||
m_PSRemux->Del(Count);
|
||||
break;
|
||||
|
||||
case stExtern:
|
||||
m_ExtRemux->Del(Count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::Attach(void)
|
||||
{
|
||||
printf("RIGHT ATTACH\n");
|
||||
m_Device->AttachReceiver(m_Receiver);
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::Detach(void)
|
||||
{
|
||||
printf("RIGHT DETACH\n");
|
||||
m_Device->Detach(m_Receiver);
|
||||
}
|
||||
|
||||
std::string cStreamdevLiveStreamer::Report(void)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (m_Device != NULL)
|
||||
result += (std::string)"+- Device is " + (const char*)itoa(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 += (std::string)(const char*)itoa(m_Pids[i]) + ", ";
|
||||
result += "\n";
|
||||
return result;
|
||||
}
|
81
server/livestreamer.h
Normal file
81
server/livestreamer.h
Normal file
@ -0,0 +1,81 @@
|
||||
#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"
|
||||
|
||||
class cTS2PSRemux;
|
||||
class cTS2ESRemux;
|
||||
class cExternRemux;
|
||||
class cRemux;
|
||||
|
||||
// --- cStreamdevLiveReceiver -------------------------------------------------
|
||||
|
||||
class cStreamdevLiveReceiver: public cReceiver {
|
||||
friend class cStreamdevLiveStreamer;
|
||||
|
||||
private:
|
||||
cStreamdevLiveStreamer *m_Streamer;
|
||||
|
||||
protected:
|
||||
virtual void Activate(bool On);
|
||||
virtual void Receive(uchar *Data, int Length);
|
||||
|
||||
public:
|
||||
#if VDRVERSNUM < 10500
|
||||
cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, int Ca, int Priority, const int *Pids);
|
||||
#else
|
||||
cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, tChannelID ChannelID, int Priority, const int *Pids);
|
||||
#endif
|
||||
virtual ~cStreamdevLiveReceiver();
|
||||
};
|
||||
|
||||
// --- cStreamdevLiveStreamer -------------------------------------------------
|
||||
|
||||
class cStreamdevLiveStreamer: public cStreamdevStreamer {
|
||||
private:
|
||||
int m_Priority;
|
||||
int m_Pids[MAXRECEIVEPIDS + 1];
|
||||
int m_NumPids;
|
||||
eStreamType m_StreamType;
|
||||
const cChannel *m_Channel;
|
||||
cDevice *m_Device;
|
||||
cStreamdevLiveReceiver *m_Receiver;
|
||||
cRemux *m_PESRemux;
|
||||
cTS2ESRemux *m_ESRemux;
|
||||
cTS2PSRemux *m_PSRemux;
|
||||
cExternRemux *m_ExtRemux;
|
||||
|
||||
public:
|
||||
cStreamdevLiveStreamer(int Priority);
|
||||
virtual ~cStreamdevLiveStreamer();
|
||||
|
||||
void SetDevice(cDevice *Device) { m_Device = Device; }
|
||||
bool SetPid(int Pid, bool On);
|
||||
bool SetChannel(const cChannel *Channel, eStreamType StreamType, int Apid = 0);
|
||||
bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On);
|
||||
|
||||
virtual int Put(const uchar *Data, int Count);
|
||||
virtual uchar *Get(int &Count);
|
||||
virtual void Del(int Count);
|
||||
|
||||
virtual void Attach(void);
|
||||
virtual void Detach(void);
|
||||
|
||||
// Statistical purposes:
|
||||
virtual std::string Report(void);
|
||||
};
|
||||
|
||||
// --- cStreamdevLiveReceiver reverse inlines ---------------------------------
|
||||
|
||||
inline void cStreamdevLiveReceiver::Activate(bool On)
|
||||
{
|
||||
Dprintf("LiveReceiver->Activate(%d)\n", On);
|
||||
m_Streamer->Activate(On);
|
||||
}
|
||||
|
||||
#endif // VDR_STREAMDEV_LIVESTREAMER_H
|
158
server/server.c
Normal file
158
server/server.c
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* $Id: server.c,v 1.4 2006/11/10 11:52:41 schmirl 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;
|
||||
cList<cServerComponent> cStreamdevServer::m_Servers;
|
||||
cList<cServerConnection> cStreamdevServer::m_Clients;
|
||||
|
||||
cStreamdevServer::cStreamdevServer(void):
|
||||
cThread("streamdev server"),
|
||||
m_Active(false)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
|
||||
cStreamdevServer::~cStreamdevServer()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void cStreamdevServer::Initialize(void)
|
||||
{
|
||||
if (m_Instance == NULL) {
|
||||
if (StreamdevServerSetup.StartVTPServer) Register(new cComponentVTP);
|
||||
if (StreamdevServerSetup.StartHTTPServer) Register(new cComponentHTTP);
|
||||
|
||||
m_Instance = new cStreamdevServer;
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevServer::Destruct(void)
|
||||
{
|
||||
DELETENULL(m_Instance);
|
||||
}
|
||||
|
||||
void cStreamdevServer::Stop(void)
|
||||
{
|
||||
if (m_Active) {
|
||||
m_Active = false;
|
||||
Cancel(3);
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevServer::Register(cServerComponent *Server)
|
||||
{
|
||||
m_Servers.Add(Server);
|
||||
}
|
||||
|
||||
void cStreamdevServer::Action(void)
|
||||
{
|
||||
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->Initialize())
|
||||
m_Servers.Del(c);
|
||||
c = next;
|
||||
}
|
||||
|
||||
if (m_Servers.Count() == 0) {
|
||||
esyslog("ERROR: no streamdev server activated, exiting");
|
||||
m_Active = false;
|
||||
}
|
||||
|
||||
cTBSelect select;
|
||||
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))
|
||||
select.Add(c->Socket(), false);
|
||||
|
||||
/* Ask all Client connections to register to the selector */
|
||||
for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s))
|
||||
{
|
||||
select.Add(s->Socket(), false);
|
||||
if (s->HasData())
|
||||
select.Add(s->Socket(), true);
|
||||
}
|
||||
|
||||
int result;
|
||||
while ((result = select.Select(100)) < 0 && errno == ETIMEDOUT) {
|
||||
if (!m_Active) break;
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
if (m_Active) // no exit was requested while polling
|
||||
esyslog("fatal error, server exiting: %m");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Ask all Server components to act on signalled sockets */
|
||||
for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)){
|
||||
if (select.CanRead(c->Socket())) {
|
||||
cServerConnection *client = c->Accept();
|
||||
m_Clients.Add(client);
|
||||
|
||||
if (m_Clients.Count() > StreamdevServerSetup.MaxClients) {
|
||||
esyslog("streamdev: too many clients, rejecting %s:%d",
|
||||
client->RemoteIp().c_str(), client->RemotePort());
|
||||
client->Reject();
|
||||
} else if (!StreamdevHosts.Acceptable(client->RemoteIpAddr())) {
|
||||
esyslog("streamdev: client %s:%d not allowed to connect",
|
||||
client->RemoteIp().c_str(), client->RemotePort());
|
||||
client->Reject();
|
||||
} else
|
||||
client->Welcome();
|
||||
}
|
||||
}
|
||||
|
||||
/* Ask all Client connections to act on signalled sockets */
|
||||
for (cServerConnection *s = m_Clients.First(); s;) {
|
||||
bool result = true;
|
||||
|
||||
if (select.CanWrite(s->Socket()))
|
||||
result = s->Write();
|
||||
|
||||
if (result && select.CanRead(s->Socket()))
|
||||
result = s->Read();
|
||||
|
||||
cServerConnection *next = m_Clients.Next(s);
|
||||
if (!result) {
|
||||
isyslog("streamdev: closing streamdev connection to %s:%d",
|
||||
s->RemoteIp().c_str(), s->RemotePort());
|
||||
s->Close();
|
||||
m_Clients.Del(s);
|
||||
}
|
||||
s = next;
|
||||
}
|
||||
}
|
||||
|
||||
while (m_Clients.Count() > 0) {
|
||||
cServerConnection *s = m_Clients.First();
|
||||
s->Close();
|
||||
m_Clients.Del(s);
|
||||
}
|
||||
|
||||
while (m_Servers.Count() > 0) {
|
||||
cServerComponent *c = m_Servers.First();
|
||||
c->Destruct();
|
||||
m_Servers.Del(c);
|
||||
}
|
||||
|
||||
m_Active = false;
|
||||
}
|
47
server/server.h
Normal file
47
server/server.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* $Id: server.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVER_H
|
||||
#define VDR_STREAMDEV_SERVER_H
|
||||
|
||||
#include <vdr/thread.h>
|
||||
|
||||
#include "server/component.h"
|
||||
#include "server/connection.h"
|
||||
|
||||
#define STREAMDEVHOSTSPATH (*AddDirectory(cPlugin::ConfigDirectory(), "streamdevhosts.conf"))
|
||||
|
||||
class cStreamdevServer: public cThread {
|
||||
private:
|
||||
bool m_Active;
|
||||
|
||||
static cStreamdevServer *m_Instance;
|
||||
static cList<cServerComponent> m_Servers;
|
||||
static cList<cServerConnection> m_Clients;
|
||||
|
||||
protected:
|
||||
void Stop(void);
|
||||
|
||||
virtual void Action(void);
|
||||
|
||||
static void Register(cServerComponent *Server);
|
||||
|
||||
public:
|
||||
cStreamdevServer(void);
|
||||
virtual ~cStreamdevServer();
|
||||
|
||||
static void Initialize(void);
|
||||
static void Destruct(void);
|
||||
static bool Active(void);
|
||||
};
|
||||
|
||||
inline bool cStreamdevServer::Active(void)
|
||||
{
|
||||
return m_Instance != NULL
|
||||
&& m_Instance->m_Clients.Count() > 0;
|
||||
}
|
||||
|
||||
extern cSVDRPhosts StreamdevHosts;
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVER_H
|
94
server/setup.c
Normal file
94
server/setup.c
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* $Id: setup.c,v 1.2 2005/05/09 20:22:29 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::Destruct();
|
||||
}
|
||||
|
||||
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::Initialize();
|
||||
}
|
||||
|
41
server/setup.h
Normal file
41
server/setup.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* $Id: setup.h,v 1.1.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
|
146
server/streamer.c
Normal file
146
server/streamer.c
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* $Id: streamer.c,v 1.14 2005/05/09 20:22:29 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 "tools/select.h"
|
||||
#include "common.h"
|
||||
|
||||
// --- cStreamdevWriter -------------------------------------------------------
|
||||
|
||||
cStreamdevWriter::cStreamdevWriter(cTBSocket *Socket,
|
||||
cStreamdevStreamer *Streamer):
|
||||
cThread("streamdev-writer"),
|
||||
m_Streamer(Streamer),
|
||||
m_Socket(Socket),
|
||||
m_Active(false)
|
||||
{
|
||||
}
|
||||
|
||||
cStreamdevWriter::~cStreamdevWriter()
|
||||
{
|
||||
Dprintf("destructing writer\n");
|
||||
m_Active = false;
|
||||
Cancel(3);
|
||||
}
|
||||
|
||||
void cStreamdevWriter::Action(void)
|
||||
{
|
||||
cTBSelect sel;
|
||||
Dprintf("Writer start\n");
|
||||
int max = 0;
|
||||
uchar *block = NULL;
|
||||
int count, offset = 0;
|
||||
m_Active = true;
|
||||
while (m_Active) {
|
||||
if (block == NULL) {
|
||||
block = m_Streamer->Get(count);
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if (block != NULL) {
|
||||
sel.Clear();
|
||||
sel.Add(*m_Socket, true);
|
||||
if (sel.Select(500) == -1) {
|
||||
esyslog("ERROR: streamdev-server: couldn't send data: %m");
|
||||
break;
|
||||
}
|
||||
|
||||
if (sel.CanWrite(*m_Socket)) {
|
||||
int written;
|
||||
if ((written = m_Socket->Write(block + offset, count)) == -1) {
|
||||
esyslog("ERROR: streamdev-server: couldn't send data: %m");
|
||||
break;
|
||||
}
|
||||
if (count > max)
|
||||
max = count;
|
||||
|
||||
offset += written;
|
||||
count -= written;
|
||||
if (count == 0) {
|
||||
m_Streamer->Del(offset);
|
||||
block = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Active = false;
|
||||
Dprintf("Max. Transmit Blocksize was: %d\n", max);
|
||||
}
|
||||
|
||||
// --- cStreamdevStreamer -----------------------------------------------------
|
||||
|
||||
cStreamdevStreamer::cStreamdevStreamer(const char *Name):
|
||||
cThread(Name),
|
||||
m_Active(false),
|
||||
m_Running(false),
|
||||
m_Writer(NULL),
|
||||
m_RingBuffer(new cRingBufferLinear(STREAMERBUFSIZE, TS_SIZE * 2,
|
||||
true, "streamdev-streamer")),
|
||||
m_SendBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2))
|
||||
{
|
||||
m_RingBuffer->SetTimeouts(0, 100);
|
||||
m_SendBuffer->SetTimeouts(0, 100);
|
||||
}
|
||||
|
||||
cStreamdevStreamer::~cStreamdevStreamer()
|
||||
{
|
||||
Dprintf("Desctructing streamer\n");
|
||||
delete m_RingBuffer;
|
||||
delete m_SendBuffer;
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Start(cTBSocket *Socket)
|
||||
{
|
||||
Dprintf("start streamer\n");
|
||||
m_Writer = new cStreamdevWriter(Socket, this);
|
||||
m_Running = true;
|
||||
Attach();
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Activate(bool On)
|
||||
{
|
||||
if (On && !m_Active) {
|
||||
Dprintf("activate streamer\n");
|
||||
m_Writer->Start();
|
||||
cThread::Start();
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Stop(void)
|
||||
{
|
||||
if (m_Active) {
|
||||
Dprintf("stopping streamer\n");
|
||||
m_Active = false;
|
||||
Cancel(3);
|
||||
}
|
||||
if (m_Running) {
|
||||
Detach();
|
||||
m_Running = false;
|
||||
DELETENULL(m_Writer);
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Action(void)
|
||||
{
|
||||
m_Active = true;
|
||||
while (m_Active) {
|
||||
int got;
|
||||
uchar *block = m_RingBuffer->Get(got);
|
||||
|
||||
if (block) {
|
||||
int count = Put(block, got);
|
||||
if (count)
|
||||
m_RingBuffer->Del(count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
69
server/streamer.h
Normal file
69
server/streamer.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* $Id: streamer.h,v 1.7 2005/03/12 12:54:19 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;
|
||||
|
||||
#define STREAMERBUFSIZE MEGABYTE(4)
|
||||
#define WRITERBUFSIZE KILOBYTE(256)
|
||||
|
||||
// --- cStreamdevWriter -------------------------------------------------------
|
||||
|
||||
class cStreamdevWriter: public cThread {
|
||||
private:
|
||||
cStreamdevStreamer *m_Streamer;
|
||||
cTBSocket *m_Socket;
|
||||
bool m_Active;
|
||||
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
|
||||
public:
|
||||
cStreamdevWriter(cTBSocket *Socket, cStreamdevStreamer *Streamer);
|
||||
virtual ~cStreamdevWriter();
|
||||
};
|
||||
|
||||
// --- cStreamdevStreamer -----------------------------------------------------
|
||||
|
||||
class cStreamdevStreamer: public cThread {
|
||||
private:
|
||||
bool m_Active;
|
||||
bool m_Running;
|
||||
cStreamdevWriter *m_Writer;
|
||||
cRingBufferLinear *m_RingBuffer;
|
||||
cRingBufferLinear *m_SendBuffer;
|
||||
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
|
||||
bool IsRunning(void) const { return m_Running; }
|
||||
|
||||
public:
|
||||
cStreamdevStreamer(const char *Name);
|
||||
virtual ~cStreamdevStreamer();
|
||||
|
||||
virtual void Start(cTBSocket *Socket);
|
||||
virtual void Stop(void);
|
||||
|
||||
void Activate(bool On);
|
||||
int Receive(uchar *Data, int Length) { return m_RingBuffer->Put(Data, Length); }
|
||||
void ReportOverflow(int Bytes) { m_RingBuffer->ReportOverflow(Bytes); }
|
||||
|
||||
virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->Put(Data, Count); }
|
||||
virtual uchar *Get(int &Count) { return m_SendBuffer->Get(Count); }
|
||||
virtual void Del(int Count) { m_SendBuffer->Del(Count); }
|
||||
|
||||
virtual void Detach(void) {}
|
||||
virtual void Attach(void) {}
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_STREAMER_H
|
||||
|
69
server/suspend.c
Normal file
69
server/suspend.c
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* $Id: suspend.c,v 1.1.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;
|
||||
}
|
1206
server/suspend.dat
Normal file
1206
server/suspend.dat
Normal file
File diff suppressed because it is too large
Load Diff
41
server/suspend.h
Normal file
41
server/suspend.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* $Id: suspend.h,v 1.1.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
|
59
streamdev-client.c
Normal file
59
streamdev-client.c
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.2 2005/04/24 16:19:44 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 NULL;
|
||||
//return StreamdevClientSetup.StartClient ? tr("Streaming Control") : NULL;
|
||||
}
|
||||
|
||||
cOsdObject *cPluginStreamdevClient::MainMenuAction(void) {
|
||||
return NULL;
|
||||
//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!
|
29
streamdev-client.h
Normal file
29
streamdev-client.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* $Id: streamdev-client.h,v 1.1.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
|
121
streamdev-server.c
Normal file
121
streamdev-server.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.5 2007/02/19 12:08:16 schmirl Exp $
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include "streamdev-server.h"
|
||||
#include "server/setup.h"
|
||||
#include "server/server.h"
|
||||
#include "server/suspend.h"
|
||||
#include "remux/extern.h"
|
||||
#include "i18n.h"
|
||||
|
||||
const char *cPluginStreamdevServer::DESCRIPTION = "VDR Streaming Server";
|
||||
|
||||
cPluginStreamdevServer::cPluginStreamdevServer(void)
|
||||
{
|
||||
}
|
||||
|
||||
cPluginStreamdevServer::~cPluginStreamdevServer()
|
||||
{
|
||||
}
|
||||
|
||||
const char *cPluginStreamdevServer::Description(void)
|
||||
{
|
||||
return tr(DESCRIPTION);
|
||||
}
|
||||
|
||||
const char *cPluginStreamdevServer::CommandLineHelp(void)
|
||||
{
|
||||
// return a string that describes all known command line options.
|
||||
return " -r <CMD>, --remux=<CMD> Define an external command for remuxing.\n";
|
||||
}
|
||||
|
||||
bool cPluginStreamdevServer::ProcessArgs(int argc, char *argv[])
|
||||
{
|
||||
// implement command line argument processing here if applicable.
|
||||
static const struct option long_options[] = {
|
||||
{ "remux", required_argument, NULL, 'r' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int c;
|
||||
while((c = getopt_long(argc, argv, "r:", long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'r':
|
||||
g_ExternRemux = optarg;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPluginStreamdevServer::Start(void)
|
||||
{
|
||||
i18n_name = Name();
|
||||
RegisterI18n(Phrases);
|
||||
|
||||
if (!StreamdevHosts.Load(STREAMDEVHOSTSPATH, true, true)) {
|
||||
esyslog("streamdev-server: error while loading %s", STREAMDEVHOSTSPATH);
|
||||
fprintf(stderr, "streamdev-server: error while loading %s\n");
|
||||
if (access(STREAMDEVHOSTSPATH, 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::Initialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cPluginStreamdevServer::Stop(void)
|
||||
{
|
||||
cStreamdevServer::Destruct();
|
||||
}
|
||||
|
||||
cString cPluginStreamdevServer::Active(void)
|
||||
{
|
||||
if (cStreamdevServer::Active())
|
||||
{
|
||||
static const char *Message = NULL;
|
||||
if (!Message) Message = tr("Streaming active");
|
||||
return Message;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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!
|
33
streamdev-server.h
Normal file
33
streamdev-server.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* $Id: streamdev-server.h,v 1.4 2007/02/19 12:08:16 schmirl 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 const char *CommandLineHelp(void);
|
||||
virtual bool ProcessArgs(int argc, char *argv[]);
|
||||
virtual bool Start(void);
|
||||
virtual void Stop(void);
|
||||
virtual cString 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
|
13
streamdevhosts.conf.example
Normal file
13
streamdevhosts.conf.example
Normal file
@ -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!)
|
49
tools/select.c
Normal file
49
tools/select.c
Normal file
@ -0,0 +1,49 @@
|
||||
#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;
|
||||
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);
|
||||
|
||||
cTimeMs starttime;
|
||||
ms = TimeoutMs;
|
||||
while (ms > 0 && (res = ::select(m_MaxFiled + 1, &m_Rfds, &m_Wfds, NULL,
|
||||
&tv)) == -1 && errno == EINTR) {
|
||||
ms = TimeoutMs - starttime.Elapsed();
|
||||
tv.tv_usec = (ms % 1000) * 1000;
|
||||
tv.tv_sec = ms / 1000;
|
||||
}
|
||||
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;
|
||||
}
|
75
tools/select.h
Normal file
75
tools/select.h
Normal file
@ -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
|
143
tools/socket.c
Normal file
143
tools/socket.c
Normal file
@ -0,0 +1,143 @@
|
||||
#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 std::string &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) {
|
||||
::close(socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_RemoteAddr.sin_family = AF_INET;
|
||||
m_RemoteAddr.sin_port = htons(Port);
|
||||
m_RemoteAddr.sin_addr.s_addr = inet_addr(Host.c_str());
|
||||
if (::connect(socket, (struct sockaddr*)&m_RemoteAddr,
|
||||
sizeof(m_RemoteAddr)) == -1) {
|
||||
::close(socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
len = sizeof(struct sockaddr_in);
|
||||
if (::getpeername(socket, (struct sockaddr*)&m_RemoteAddr, &len) == -1) {
|
||||
::close(socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
len = sizeof(struct sockaddr_in);
|
||||
if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &len) == -1) {
|
||||
::close(socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
return cTBSource::Open(socket);
|
||||
}
|
||||
|
||||
bool cTBSocket::Listen(const std::string &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.c_str());
|
||||
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;
|
||||
}
|
108
tools/socket.h
Normal file
108
tools/socket.h
Normal file
@ -0,0 +1,108 @@
|
||||
#ifndef TOOLBOX_SOCKET_H
|
||||
#define TOOLBOX_SOCKET_H
|
||||
|
||||
#include "tools/tools.h"
|
||||
#include "tools/source.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string>
|
||||
|
||||
/* 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 std::string &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 std::string &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. */
|
||||
std::string 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. */
|
||||
std::string 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
|
169
tools/source.c
Normal file
169
tools/source.c
Normal file
@ -0,0 +1,169 @@
|
||||
#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;
|
||||
int ms, offs;
|
||||
|
||||
cTimeMs starttime;
|
||||
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 = TimeoutMs - starttime.Elapsed();
|
||||
if (ms <= 0) {
|
||||
errno = ETIMEDOUT;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cTBSource::SafeWrite(const void *Buffer, size_t Length) {
|
||||
cTBSelect sel;
|
||||
int offs;
|
||||
|
||||
offs = 0;
|
||||
while (Length > 0) {
|
||||
int b;
|
||||
|
||||
sel.Clear();
|
||||
sel.Add(m_Filed, true);
|
||||
if (sel.Select() == -1)
|
||||
return false;
|
||||
|
||||
if (sel.CanWrite(m_Filed)) {
|
||||
if ((b = Write((char*)Buffer + offs, Length)) == -1)
|
||||
return false;
|
||||
offs += b;
|
||||
Length -= b;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t cTBSource::ReadUntil(void *Buffer, size_t Length, const char *Seq,
|
||||
uint TimeoutMs) {
|
||||
int seqlen, ms;
|
||||
size_t len;
|
||||
cTBSelect sel;
|
||||
|
||||
if ((len = m_LineBuffer.find(Seq)) != (size_t)-1) {
|
||||
if (len > Length) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
memcpy(Buffer, m_LineBuffer.data(), len);
|
||||
m_LineBuffer.erase(0, len + strlen(Seq));
|
||||
Dprintf("ReadUntil: Served from Linebuffer: %d, |%.*s|\n", len, len - 1,
|
||||
(char*)Buffer);
|
||||
return len;
|
||||
}
|
||||
|
||||
cTimeMs starttime;
|
||||
ms = TimeoutMs;
|
||||
while (m_LineBuffer.size() < BUFSIZ) {
|
||||
sel.Clear();
|
||||
sel.Add(m_Filed, false);
|
||||
|
||||
if (sel.Select(ms) == -1)
|
||||
return -1;
|
||||
|
||||
if (sel.CanRead(m_Filed)) {
|
||||
int b;
|
||||
|
||||
len = m_LineBuffer.size();
|
||||
m_LineBuffer.resize(BUFSIZ);
|
||||
if ((b = Read((char*)m_LineBuffer.data() + len, BUFSIZ - len)) == -1)
|
||||
return -1;
|
||||
m_LineBuffer.resize(len + b);
|
||||
|
||||
if ((len = m_LineBuffer.find(Seq)) != (size_t)-1) {
|
||||
if (len > Length) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
memcpy(Buffer, m_LineBuffer.data(), len);
|
||||
m_LineBuffer.erase(0, len + strlen(Seq));
|
||||
Dprintf("ReadUntil: Served from Linebuffer: %d, |%.*s|\n", len, len - 1,
|
||||
(char*)Buffer);
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
ms = TimeoutMs - starttime.Elapsed();
|
||||
if (ms <= 0) {
|
||||
errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
|
109
tools/source.h
Normal file
109
tools/source.h
Normal file
@ -0,0 +1,109 @@
|
||||
#ifndef TOOLBOX_SOURCE_H
|
||||
#define TOOLBOX_SOURCE_H
|
||||
|
||||
#include "tools/tools.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string>
|
||||
|
||||
/* 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;
|
||||
|
||||
std::string 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);
|
||||
|
||||
bool SafeWrite(const void *Buffer, size_t Length);
|
||||
|
||||
/* 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
|
12
tools/tools.c
Normal file
12
tools/tools.c
Normal file
@ -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;
|
||||
}
|
||||
|
67
tools/tools.h
Normal file
67
tools/tools.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user