Snapshot 2007-03-20

This commit is contained in:
Frank Schmirler 2010-12-02 08:53:01 +01:00
commit 5e30711bfd
97 changed files with 21652 additions and 0 deletions

21
CONTRIBUTORS Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

144
client/menu.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1 @@
.depend

446
libdvbmpeg/DVB.hh Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

404
libdvbmpeg/ctools.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

149
libdvbmpeg/remux.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

250
libdvbmpeg/transform.h Normal file
View 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
View 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
View 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;

View 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)

View 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();
}

View 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;

View 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);

View 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);

View 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)

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

File diff suppressed because it is too large Load Diff

41
server/suspend.h Normal file
View 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
View 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
View 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
View 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
View 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

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