mirror of
https://github.com/rofafor/vdr-plugin-satip.git
synced 2023-10-10 13:37:42 +02:00
Initial revision.
This commit is contained in:
commit
476f8be52b
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.dependencies
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
*~
|
||||||
|
po/*.pot
|
||||||
|
po/*.mo
|
340
COPYING
Normal file
340
COPYING
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General
|
||||||
|
Public License instead of this License.
|
8
HISTORY
Normal file
8
HISTORY
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
===================================
|
||||||
|
VDR Plugin 'satip' Revision History
|
||||||
|
===================================
|
||||||
|
|
||||||
|
2014-03-08: Version 0.0.1
|
||||||
|
|
||||||
|
- Initial revision.
|
||||||
|
- Added German translation (Thanks to Frank Neumann).
|
157
Makefile
Normal file
157
Makefile
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#
|
||||||
|
# Makefile for SAT>IP plugin
|
||||||
|
#
|
||||||
|
|
||||||
|
# Debugging on/off
|
||||||
|
|
||||||
|
#SATIP_DEBUG = 1
|
||||||
|
|
||||||
|
# Strip debug symbols? Set eg. to /bin/true if not
|
||||||
|
|
||||||
|
STRIP = strip
|
||||||
|
|
||||||
|
# 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 = satip
|
||||||
|
|
||||||
|
### The version number of this plugin (taken from the main source file):
|
||||||
|
|
||||||
|
VERSION = $(shell grep 'const char VERSION\[\] *=' $(PLUGIN).c | awk '{ print $$5 }' | sed -e 's/[";]//g')
|
||||||
|
GITTAG = $(shell git describe --always 2>/dev/null)
|
||||||
|
|
||||||
|
### The directory environment:
|
||||||
|
|
||||||
|
# Use package data if installed...otherwise assume we're under the VDR source directory:
|
||||||
|
PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
|
||||||
|
LIBDIR = $(call PKGCFG,libdir)
|
||||||
|
LOCDIR = $(call PKGCFG,locdir)
|
||||||
|
PLGCFG = $(call PKGCFG,plgcfg)
|
||||||
|
CFGDIR = $(call PKGCFG,configdir)
|
||||||
|
#
|
||||||
|
TMPDIR ?= /tmp
|
||||||
|
|
||||||
|
### The compiler options:
|
||||||
|
|
||||||
|
export CFLAGS = $(call PKGCFG,cflags)
|
||||||
|
export CXXFLAGS = $(call PKGCFG,cxxflags)
|
||||||
|
|
||||||
|
### The version number of VDR's plugin API:
|
||||||
|
|
||||||
|
APIVERSION = $(call PKGCFG,apiversion)
|
||||||
|
|
||||||
|
### Allow user defined options to overwrite defaults:
|
||||||
|
|
||||||
|
-include $(PLGCFG)
|
||||||
|
|
||||||
|
### The name of the distribution archive:
|
||||||
|
|
||||||
|
ARCHIVE = $(PLUGIN)-$(VERSION)
|
||||||
|
PACKAGE = vdr-$(ARCHIVE)
|
||||||
|
|
||||||
|
### The name of the shared object file:
|
||||||
|
|
||||||
|
SOFILE = libvdr-$(PLUGIN).so
|
||||||
|
|
||||||
|
### Libraries
|
||||||
|
|
||||||
|
LIBS = $(shell curl-config --libs)
|
||||||
|
|
||||||
|
### Includes and Defines (add further entries here):
|
||||||
|
|
||||||
|
INCLUDES +=
|
||||||
|
|
||||||
|
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
|
||||||
|
|
||||||
|
ifdef SATIP_DEBUG
|
||||||
|
DEFINES += -DDEBUG
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(strip $(GITTAG)),)
|
||||||
|
DEFINES += -DGITVERSION='"-GIT-$(GITTAG)"'
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: all all-redirect
|
||||||
|
all-redirect: all
|
||||||
|
|
||||||
|
### The object files (add further files here):
|
||||||
|
|
||||||
|
OBJS = $(PLUGIN).o common.o config.o device.o discover.o \
|
||||||
|
sectionfilter.o setup.o socket.o source.o statistics.o tuner.o
|
||||||
|
|
||||||
|
### The main target:
|
||||||
|
|
||||||
|
all: $(SOFILE) i18n
|
||||||
|
|
||||||
|
### Implicit rules:
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
|
||||||
|
|
||||||
|
### Dependencies:
|
||||||
|
|
||||||
|
MAKEDEP = $(CXX) -MM -MG
|
||||||
|
DEPFILE = .dependencies
|
||||||
|
$(DEPFILE): Makefile
|
||||||
|
@$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
|
||||||
|
|
||||||
|
-include $(DEPFILE)
|
||||||
|
|
||||||
|
### Internationalization (I18N):
|
||||||
|
|
||||||
|
PODIR = po
|
||||||
|
I18Npo = $(wildcard $(PODIR)/*.po)
|
||||||
|
I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
|
||||||
|
I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
|
||||||
|
I18Npot = $(PODIR)/$(PLUGIN).pot
|
||||||
|
|
||||||
|
%.mo: %.po
|
||||||
|
msgfmt -c -o $@ $<
|
||||||
|
|
||||||
|
$(I18Npot): $(wildcard *.c)
|
||||||
|
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ `ls $^`
|
||||||
|
|
||||||
|
%.po: $(I18Npot)
|
||||||
|
msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
|
||||||
|
@touch $@
|
||||||
|
|
||||||
|
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
|
||||||
|
install -D -m644 $< $@
|
||||||
|
|
||||||
|
.PHONY: i18n
|
||||||
|
i18n: $(I18Nmo) $(I18Npot)
|
||||||
|
|
||||||
|
install-i18n: $(I18Nmsgs)
|
||||||
|
|
||||||
|
### Targets:
|
||||||
|
|
||||||
|
$(SOFILE): $(OBJS)
|
||||||
|
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@
|
||||||
|
ifndef SATIP_DEBUG
|
||||||
|
@$(STRIP) $@
|
||||||
|
endif
|
||||||
|
|
||||||
|
install-lib: $(SOFILE)
|
||||||
|
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
|
||||||
|
|
||||||
|
install-conf:
|
||||||
|
@mkdir -p $(DESTDIR)$(CFGDIR)/plugins/$(PLUGIN)
|
||||||
|
|
||||||
|
install: install-lib install-i18n install-conf
|
||||||
|
|
||||||
|
dist: $(I18Npo) clean
|
||||||
|
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
||||||
|
@mkdir $(TMPDIR)/$(ARCHIVE)
|
||||||
|
@cp -a * $(TMPDIR)/$(ARCHIVE)
|
||||||
|
@tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
|
||||||
|
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
||||||
|
@echo Distribution package created as $(PACKAGE).tgz
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
|
||||||
|
@-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~
|
||||||
|
|
||||||
|
.PHONY: cppcheck
|
||||||
|
cppcheck:
|
||||||
|
@cppcheck --language=c++ --enable=all -v -f $(OBJS:%.o=%.c)
|
175
README
Normal file
175
README
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
This is an SAT>IP plugin for the Video Disk Recorder (VDR).
|
||||||
|
|
||||||
|
Written by: Rolf Ahrenberg
|
||||||
|
< R o l f . A h r e n b e r g @ s c i . f i >
|
||||||
|
|
||||||
|
Project's homepage: http://www.saunalahti.fi/~rahrenbe/vdr/satip/
|
||||||
|
|
||||||
|
Latest version available at: http://www.saunalahti.fi/~rahrenbe/vdr/satip/
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License version 2 as
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
See the file COPYING for more information.
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
- Libcurl - the multiprotocol file transfer library with RTSP support
|
||||||
|
http://curl.haxx.se/libcurl/
|
||||||
|
|
||||||
|
- VDR-2.1.4+ for scrambled channels
|
||||||
|
|
||||||
|
Description:
|
||||||
|
|
||||||
|
This plugin integrates SAT>IP network devices seamlessly into VDR.
|
||||||
|
You can use any SAT>IP channel like any other normal DVB channel for
|
||||||
|
live viewing, recording, etc. The plugin also features full section
|
||||||
|
filtering capabilities which allow for example EIT information to be
|
||||||
|
extracted from the incoming stream.
|
||||||
|
|
||||||
|
Installation:
|
||||||
|
|
||||||
|
tar -xzf /put/your/path/here/vdr-satip-X.Y.Z.tgz
|
||||||
|
make -C satip-X.Y.Z install
|
||||||
|
|
||||||
|
Setup menu:
|
||||||
|
|
||||||
|
- Enable EPG scanning = yes If you want exclude all SAT>IP devices
|
||||||
|
from VDR's EIT background scanning, set
|
||||||
|
this option to "no".
|
||||||
|
- Disabled filters = none Certain section filters might cause some
|
||||||
|
unwanted behaviour to VDR such as time
|
||||||
|
being falsely synchronized etc. This option
|
||||||
|
allows creation of blacklists of ill-behaving
|
||||||
|
filters. If this option is set to a non-zero
|
||||||
|
value, the menu page will contain that many
|
||||||
|
"Disable filter" options which allow you
|
||||||
|
to disable the individual section filters.
|
||||||
|
Valid range: "none" = 0 ... 7
|
||||||
|
- [Blue:Info] Opens SAT>IP information/statistics menu.
|
||||||
|
- [Ok] Opens information menu of selected SAT>IP
|
||||||
|
device.
|
||||||
|
|
||||||
|
Information menu:
|
||||||
|
|
||||||
|
- [Red:General] Opens the general information page.
|
||||||
|
- [Green:Pids] Opens the pid statistics page.
|
||||||
|
- [Yellow:Filters] Opens the section filter statistics page.
|
||||||
|
- [Blue:Bits/bytes] Toggles between bits and bytes mode.
|
||||||
|
|
||||||
|
SAT>IP device menu:
|
||||||
|
|
||||||
|
- [Red:Scan] Starts the channel scanning sometime in
|
||||||
|
the future.
|
||||||
|
|
||||||
|
Configuration:
|
||||||
|
|
||||||
|
- Parameters
|
||||||
|
|
||||||
|
Various parameters, depending on whether this is a DVB-S, DVB-S2, DVB-T,
|
||||||
|
or DVB-T2 channel. Each parameter consist of a key character, followed by
|
||||||
|
an integer number that represents the actual setting of that parameter.
|
||||||
|
The valid key characters, their meaning, and allowed values are:
|
||||||
|
|
||||||
|
B Bandwidth (1712, 5, 6, 7, 8, 10)
|
||||||
|
C Code rate high priority (12, 23, 34, 35, 45, 56, 67, 78, 89, 910)
|
||||||
|
G Guard interval (4, 8, 16, 32, 128, 19128, 19256)
|
||||||
|
H Horizontal polarization
|
||||||
|
L Left circular polarization
|
||||||
|
M Modulation (2, 5, 16, 64, 256)
|
||||||
|
N pilot toNes (0, 1)
|
||||||
|
O rollOff (20, 25, 35)
|
||||||
|
P stream id (0-255)
|
||||||
|
Q t2 system id (0-65535)
|
||||||
|
R Right circular polarization
|
||||||
|
S delivery System (0, 1)
|
||||||
|
T Transmission mode (1, 2, 4, 8, 16, 32)
|
||||||
|
V Vertical polarization
|
||||||
|
X siso/miso mode (0, 1)
|
||||||
|
Z signal source position (1-255)
|
||||||
|
|
||||||
|
Bandwidth: The bandwidth of the channel in MHz (1712 in kHz)
|
||||||
|
(DVB-T/DVB-T2)
|
||||||
|
|
||||||
|
Code rate high priority: Forward Error Correction (FEC) of the high
|
||||||
|
priority stream (DVB-T/DVB-T2) or the the inner FEC scheme (DVB-S/DVB-S2)
|
||||||
|
12 = 1/2, 23 = 2/3, 34 = 3/4, 35 = 3/5, 45 = 4/5, 56 = 5/6,
|
||||||
|
67 = 6/7, 78 = 7/8, 89 = 8/9, 910 = 9/10
|
||||||
|
(DVB-T/DVB-T2/DVB-S/DVB-S2)
|
||||||
|
|
||||||
|
Guard interval: The guard interval value
|
||||||
|
4 = 1/4, 8 = 1/8, 16 = 1/16, 32 = 1/32, 128 = 1/128,
|
||||||
|
19128 = 19/128, 19256 = 19/256
|
||||||
|
(DVB-T/DVB-T2)
|
||||||
|
|
||||||
|
Modulation: Specifies the modulation/constellation of the channel
|
||||||
|
2 = QPSK (DVB-S, DVB-S2, DVB-T, DVB-T2)
|
||||||
|
5 = 8PSK (DVB-S, DVB-S2)
|
||||||
|
16 = QAM16 (DVB-T, DVB-T2)
|
||||||
|
64 = QAM64 (DVB-T, DVB-T2)
|
||||||
|
256 = QAM256 (DVB-T2)
|
||||||
|
|
||||||
|
Pilot tones: Specifies pilot tones usage
|
||||||
|
0 = off, 1 = on
|
||||||
|
(DVB-S2)
|
||||||
|
|
||||||
|
Rolloff: The Nyquist filter rolloff factor
|
||||||
|
35 = 0.35, 25 = 0.25, 20 = 0.20
|
||||||
|
(DVB-S/DVB-S2)
|
||||||
|
|
||||||
|
Stream id: Physical Layer Pipe (PLP) id (0-255) for DVB-T2 multiplex
|
||||||
|
(DVB-T2)
|
||||||
|
|
||||||
|
T2 system id: Unique identifier (0-65535) of the T2 system
|
||||||
|
(DVB-T2)
|
||||||
|
|
||||||
|
Transmission mode: Number of DVB-T OFDM carriers
|
||||||
|
32 = 32k, 16 = 16k, 8 = 8k, 4 = 4k, 2 = 2k, 1 = 1k
|
||||||
|
(DVB-T/DVB-T2)
|
||||||
|
|
||||||
|
Delivery System: The delivery system
|
||||||
|
0 = "first generation" (DVB-S/DVB-T)
|
||||||
|
1 = "second generation" (DVB-S2/DVB-T2)
|
||||||
|
|
||||||
|
Polarization: Satellite antenna polarization
|
||||||
|
H = horizontal, V = vertical
|
||||||
|
R = circular right, L = circular left
|
||||||
|
(DVB-S/DVB-S2)
|
||||||
|
|
||||||
|
Signal source: Specifies the signal source (satellite) position
|
||||||
|
(DVB-S/DVB-S2)
|
||||||
|
|
||||||
|
SISO/MISO: Specifies the Single-Input/Multiple-Input Single-Output mode
|
||||||
|
0 = SISO, 1 = MISO
|
||||||
|
(DVB-T2)
|
||||||
|
|
||||||
|
- Source
|
||||||
|
|
||||||
|
There are two separate signal sources for SAT>IP channels:
|
||||||
|
Z = SAT>IP Satellite (DVB-S/DVB-S2)
|
||||||
|
Y = SAT>IP Terrestrial (DVB-T/DVB-T2)
|
||||||
|
|
||||||
|
- Examples
|
||||||
|
|
||||||
|
MTV3;DVB-T:658000000:B8C23D23G8M64S0T8Y0:T:0:305=2:561=fin@4,562=dut@4:5010;1073=fin,1074=dut:0:49:8438:8193:0
|
||||||
|
=>
|
||||||
|
MTV3;SAT>IP:658000000:B8C23G8M64S0T8:Y:0:305=2:561=fin@4,562=dut@4:5010;1073=fin,1074=dut:0:49:8438:8193:0
|
||||||
|
|
||||||
|
Das Erste HD;DVB-S2:11494:HC23M5O35S1:S19.2E:22000:5101=27:5102=deu@3,5103=mis@3;5106=deu@106:5104;5105=deu:0:10301:1:1019:0
|
||||||
|
=>
|
||||||
|
Das Erste HD;SAT>IP:11494:HC23M5O35S1:Z:22000:5101=27:5102=deu@3,5103=mis@3;5106=deu@106:5104;5105=deu:0:10301:1:1019:0
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- The stream id "-1" states about unsuccessful tuning. This might be a
|
||||||
|
result of invalid channel parameters or lack of free SAT>IP tuners.
|
||||||
|
|
||||||
|
- SAT>IP specification 1.2 doesn't support DVB-C/DVB-C2 receivers yet.
|
||||||
|
|
||||||
|
- If the plugin doesn't detect your SAT>IP network device, make sure
|
||||||
|
your setup doesn't have firewalled the UDP port 1900.
|
||||||
|
|
||||||
|
Acknowledgements:
|
||||||
|
|
||||||
|
- Big thanks to Digital Devices GmbH for providing the Octopus Net
|
||||||
|
hardware for development!
|
81
common.c
Normal file
81
common.c
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* common.c: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <vdr/tools.h>
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
uint16_t ts_pid(const uint8_t *bufP)
|
||||||
|
{
|
||||||
|
return (uint16_t)(((bufP[1] & 0x1f) << 8) + bufP[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t payload(const uint8_t *bufP)
|
||||||
|
{
|
||||||
|
if (!(bufP[3] & 0x10)) // no payload?
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (bufP[3] & 0x20) { // adaptation field?
|
||||||
|
if (bufP[4] > 183) // corrupted data?
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return (uint8_t)((184 - 1) - bufP[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 184;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *id_pid(const u_short pidP)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
|
||||||
|
if (pidP == section_filter_table[i].pid)
|
||||||
|
return section_filter_table[i].tag;
|
||||||
|
}
|
||||||
|
return "---";
|
||||||
|
}
|
||||||
|
|
||||||
|
char *StripTags(char *strP)
|
||||||
|
{
|
||||||
|
if (strP) {
|
||||||
|
char *c = strP, *r = strP, t = 0;
|
||||||
|
while (*strP) {
|
||||||
|
if (*strP == '<')
|
||||||
|
++t;
|
||||||
|
else if (*strP == '>')
|
||||||
|
--t;
|
||||||
|
else if (t < 1)
|
||||||
|
*(c++) = *strP;
|
||||||
|
++strP;
|
||||||
|
}
|
||||||
|
*c = 0;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cString ChangeCase(const cString &strP, bool upperP)
|
||||||
|
{
|
||||||
|
cString res(strP);
|
||||||
|
char *p = (char *)*res;
|
||||||
|
while (p && *p) {
|
||||||
|
*p = upperP ? toupper(*p) : tolower(*p);
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const section_filter_table_type section_filter_table[SECTION_FILTER_TABLE_SIZE] =
|
||||||
|
{
|
||||||
|
/* description tag pid tid mask */
|
||||||
|
{trNOOP("PAT (0x00)"), "PAT", 0x00, 0x00, 0xFF},
|
||||||
|
{trNOOP("NIT (0x40)"), "NIT", 0x10, 0x40, 0xFF},
|
||||||
|
{trNOOP("SDT (0x42)"), "SDT", 0x11, 0x42, 0xFF},
|
||||||
|
{trNOOP("EIT (0x4E/0x4F)"), "EIT", 0x12, 0x4E, 0xFE},
|
||||||
|
{trNOOP("EIT (0x5X)"), "EIT", 0x12, 0x50, 0xF0},
|
||||||
|
{trNOOP("EIT (0x6X)"), "EIT", 0x12, 0x60, 0xF0},
|
||||||
|
{trNOOP("TDT (0x70)"), "TDT", 0x14, 0x70, 0xFF},
|
||||||
|
};
|
102
common.h
Normal file
102
common.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* common.h: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SATIP_COMMON_H
|
||||||
|
#define __SATIP_COMMON_H
|
||||||
|
|
||||||
|
#include <vdr/tools.h>
|
||||||
|
#include <vdr/config.h>
|
||||||
|
#include <vdr/i18n.h>
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define debug(x...) dsyslog("SATIP: " x);
|
||||||
|
#define info(x...) isyslog("SATIP: " x);
|
||||||
|
#define error(x...) esyslog("ERROR: " x);
|
||||||
|
#else
|
||||||
|
#define debug(x...) ;
|
||||||
|
#define info(x...) isyslog("SATIP: " x);
|
||||||
|
#define error(x...) esyslog("ERROR: " x);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ELEMENTS(x) (sizeof(x) / sizeof(x[0]))
|
||||||
|
|
||||||
|
#define SATIP_BUFFER_SIZE MEGABYTE(1)
|
||||||
|
|
||||||
|
#define SATIP_DEVICE_INFO_ALL 0
|
||||||
|
#define SATIP_DEVICE_INFO_GENERAL 1
|
||||||
|
#define SATIP_DEVICE_INFO_PIDS 2
|
||||||
|
#define SATIP_DEVICE_INFO_FILTERS 3
|
||||||
|
#define SATIP_DEVICE_INFO_PROTOCOL 4
|
||||||
|
#define SATIP_DEVICE_INFO_BITRATE 5
|
||||||
|
|
||||||
|
#define SATIP_STATS_ACTIVE_PIDS_COUNT 10
|
||||||
|
#define SATIP_STATS_ACTIVE_FILTERS_COUNT 10
|
||||||
|
|
||||||
|
#define SECTION_FILTER_TABLE_SIZE 7
|
||||||
|
|
||||||
|
#define SATIP_CURL_EASY_GETINFO(X, Y, Z) \
|
||||||
|
if ((res = curl_easy_getinfo((X), (Y), (Z))) != CURLE_OK) { \
|
||||||
|
error("curl_easy_getinfo(%s) [%s,%d] failed: %s (%d)", #Y, __FILE__, __LINE__, curl_easy_strerror(res), res); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SATIP_CURL_EASY_SETOPT(X, Y, Z) \
|
||||||
|
if ((res = curl_easy_setopt((X), (Y), (Z))) != CURLE_OK) { \
|
||||||
|
error("curl_easy_setopt(%s, %s) [%s,%d] failed: %s (%d)", #Y, #Z, __FILE__, __LINE__, curl_easy_strerror(res), res); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SATIP_CURL_EASY_PERFORM(X) \
|
||||||
|
if ((res = curl_easy_perform((X))) != CURLE_OK) { \
|
||||||
|
error("curl_easy_perform() [%s,%d] failed: %s (%d)", __FILE__, __LINE__, curl_easy_strerror(res), res); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ERROR_IF_FUNC(exp, errstr, func, ret) \
|
||||||
|
do { \
|
||||||
|
if (exp) { \
|
||||||
|
char tmp[64]; \
|
||||||
|
error("[%s,%d]: "errstr": %s", __FILE__, __LINE__, \
|
||||||
|
strerror_r(errno, tmp, sizeof(tmp))); \
|
||||||
|
func; \
|
||||||
|
ret; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
#define ERROR_IF_RET(exp, errstr, ret) ERROR_IF_FUNC(exp, errstr, ,ret);
|
||||||
|
|
||||||
|
#define ERROR_IF(exp, errstr) ERROR_IF_FUNC(exp, errstr, , );
|
||||||
|
|
||||||
|
#define DELETE_POINTER(ptr) \
|
||||||
|
do { \
|
||||||
|
if (ptr) { \
|
||||||
|
typeof(*ptr) *tmp = ptr; \
|
||||||
|
ptr = NULL; \
|
||||||
|
delete(tmp); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||||
|
|
||||||
|
uint16_t ts_pid(const uint8_t *bufP);
|
||||||
|
uint8_t payload(const uint8_t *bufP);
|
||||||
|
const char *id_pid(const u_short pidP);
|
||||||
|
char *StripTags(char *strP);
|
||||||
|
cString ChangeCase(const cString &strP, bool upperP);
|
||||||
|
|
||||||
|
struct section_filter_table_type {
|
||||||
|
const char *description;
|
||||||
|
const char *tag;
|
||||||
|
u_short pid;
|
||||||
|
u_char tid;
|
||||||
|
u_char mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const section_filter_table_type section_filter_table[SECTION_FILTER_TABLE_SIZE];
|
||||||
|
|
||||||
|
extern const char VERSION[];
|
||||||
|
|
||||||
|
#endif // __SATIP_COMMON_H
|
||||||
|
|
45
config.c
Normal file
45
config.c
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* config.c: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "discover.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
cSatipConfig SatipConfig;
|
||||||
|
|
||||||
|
cSatipConfig::cSatipConfig(void)
|
||||||
|
: eitScanM(1),
|
||||||
|
useBytesM(1)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < ARRAY_SIZE(disabledFiltersM); ++i)
|
||||||
|
disabledFiltersM[i] = -1;
|
||||||
|
memset(configDirectoryM, 0, sizeof(configDirectoryM));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int cSatipConfig::GetDisabledFiltersCount(void) const
|
||||||
|
{
|
||||||
|
unsigned int n = 0;
|
||||||
|
while ((n < ARRAY_SIZE(disabledFiltersM) && (disabledFiltersM[n] != -1)))
|
||||||
|
n++;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipConfig::GetDisabledFilters(unsigned int indexP) const
|
||||||
|
{
|
||||||
|
return (indexP < ARRAY_SIZE(disabledFiltersM)) ? disabledFiltersM[indexP] : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipConfig::SetDisabledFilters(unsigned int indexP, int numberP)
|
||||||
|
{
|
||||||
|
if (indexP < ARRAY_SIZE(disabledFiltersM))
|
||||||
|
disabledFiltersM[indexP] = numberP;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipConfig::SetConfigDirectory(const char *directoryP)
|
||||||
|
{
|
||||||
|
debug("cSatipConfig::%s(%s)", __FUNCTION__, directoryP);
|
||||||
|
ERROR_IF(!realpath(directoryP, configDirectoryM), "Cannot canonicalize configuration directory");
|
||||||
|
}
|
38
config.h
Normal file
38
config.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* config.h: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SATIP_CONFIG_H
|
||||||
|
#define __SATIP_CONFIG_H
|
||||||
|
|
||||||
|
#include <vdr/menuitems.h>
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class cSatipConfig
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
unsigned int eitScanM;
|
||||||
|
unsigned int useBytesM;
|
||||||
|
int disabledFiltersM[SECTION_FILTER_TABLE_SIZE];
|
||||||
|
char configDirectoryM[PATH_MAX];
|
||||||
|
|
||||||
|
public:
|
||||||
|
cSatipConfig();
|
||||||
|
unsigned int GetEITScan(void) const { return eitScanM; }
|
||||||
|
unsigned int GetUseBytes(void) const { return useBytesM; }
|
||||||
|
const char *GetConfigDirectory(void) const { return configDirectoryM; }
|
||||||
|
unsigned int GetDisabledFiltersCount(void) const;
|
||||||
|
int GetDisabledFilters(unsigned int indexP) const;
|
||||||
|
|
||||||
|
void SetEITScan(unsigned int onOffP) { eitScanM = onOffP; }
|
||||||
|
void SetUseBytes(unsigned int onOffP) { useBytesM = onOffP; }
|
||||||
|
void SetConfigDirectory(const char *directoryP);
|
||||||
|
void SetDisabledFilters(unsigned int indexP, int numberP);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern cSatipConfig SatipConfig;
|
||||||
|
|
||||||
|
#endif // __SATIP_CONFIG_H
|
442
device.c
Normal file
442
device.c
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
/*
|
||||||
|
* device.c: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "discover.h"
|
||||||
|
#include "source.h"
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
#define SATIP_MAX_DEVICES MAXDEVICES
|
||||||
|
|
||||||
|
static cSatipDevice * SatipDevicesS[SATIP_MAX_DEVICES] = { NULL };
|
||||||
|
|
||||||
|
cSatipDevice::cSatipDevice(unsigned int indexP)
|
||||||
|
: deviceIndexM(indexP),
|
||||||
|
isPacketDeliveredM(false),
|
||||||
|
isOpenDvrM(false),
|
||||||
|
deviceNameM(*cString::sprintf("%s %d", *DeviceType(), deviceIndexM)),
|
||||||
|
channelM()
|
||||||
|
{
|
||||||
|
unsigned int bufsize = (unsigned int)SATIP_BUFFER_SIZE;
|
||||||
|
bufsize -= (bufsize % TS_SIZE);
|
||||||
|
isyslog("creating SAT>IP device %d (CardIndex=%d)", deviceIndexM, CardIndex());
|
||||||
|
tsBufferM = new cRingBufferLinear(bufsize + 1, TS_SIZE, false,
|
||||||
|
*cString::sprintf("SAT>IP TS %d", deviceIndexM));
|
||||||
|
if (tsBufferM) {
|
||||||
|
tsBufferM->SetTimeouts(100, 100);
|
||||||
|
tsBufferM->SetIoThrottle();
|
||||||
|
pTunerM = new cSatipTuner(*this, tsBufferM->Free());
|
||||||
|
}
|
||||||
|
// Start section handler
|
||||||
|
pSectionFilterHandlerM = new cSatipSectionFilterHandler(deviceIndexM, bufsize + 1);
|
||||||
|
StartSectionHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipDevice::~cSatipDevice()
|
||||||
|
{
|
||||||
|
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
// Stop section handler
|
||||||
|
StopSectionHandler();
|
||||||
|
DELETE_POINTER(pSectionFilterHandlerM);
|
||||||
|
DELETE_POINTER(pTunerM);
|
||||||
|
DELETE_POINTER(tsBufferM);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::Initialize(unsigned int deviceCountP)
|
||||||
|
{
|
||||||
|
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceCountP);
|
||||||
|
new cSatipSourceParam('Y', "DVB-T (SAT>IP)");
|
||||||
|
new cSatipSourceParam('Z', "DVB-S (SAT>IP)");
|
||||||
|
if (deviceCountP > SATIP_MAX_DEVICES)
|
||||||
|
deviceCountP = SATIP_MAX_DEVICES;
|
||||||
|
for (unsigned int i = 0; i < deviceCountP; ++i)
|
||||||
|
SatipDevicesS[i] = new cSatipDevice(i);
|
||||||
|
for (unsigned int i = deviceCountP; i < SATIP_MAX_DEVICES; ++i)
|
||||||
|
SatipDevicesS[i] = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipDevice::Shutdown(void)
|
||||||
|
{
|
||||||
|
debug("cSatipDevice::%s()", __FUNCTION__);
|
||||||
|
for (int i = 0; i < SATIP_MAX_DEVICES; ++i) {
|
||||||
|
if (SatipDevicesS[i])
|
||||||
|
SatipDevicesS[i]->CloseDvr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int cSatipDevice::Count(void)
|
||||||
|
{
|
||||||
|
unsigned int count = 0;
|
||||||
|
debug("cSatipDevice::%s()", __FUNCTION__);
|
||||||
|
for (unsigned int i = 0; i < SATIP_MAX_DEVICES; ++i) {
|
||||||
|
if (SatipDevicesS[i] != NULL)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipDevice *cSatipDevice::GetSatipDevice(int cardIndexP)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%d)", __FUNCTION__, cardIndexP);
|
||||||
|
for (unsigned int i = 0; i < SATIP_MAX_DEVICES; ++i) {
|
||||||
|
if (SatipDevicesS[i] && (SatipDevicesS[i]->CardIndex() == cardIndexP)) {
|
||||||
|
//debug("cSatipDevice::%s(%d): found!", __FUNCTION__, cardIndexP);
|
||||||
|
return SatipDevicesS[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipDevice::GetGeneralInformation(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
return cString::sprintf("SAT>IP device: %d\nCardIndex: %d\nStream: %s\nSignal: %s\nStream bitrate: %s\n%sChannel: %s",
|
||||||
|
deviceIndexM, CardIndex(),
|
||||||
|
pTunerM ? *pTunerM->GetInformation() : "",
|
||||||
|
pTunerM ? *pTunerM->GetSignalStatus() : "",
|
||||||
|
pTunerM ? *pTunerM->GetTunerStatistic() : "",
|
||||||
|
*GetBufferStatistic(),
|
||||||
|
*Channels.GetByNumber(cDevice::CurrentChannel())->ToText());
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipDevice::GetPidsInformation(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
return GetPidStatistic();
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipDevice::GetFiltersInformation(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
return cString::sprintf("Active section filters:\n%s", pSectionFilterHandlerM ? *pSectionFilterHandlerM->GetInformation() : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipDevice::GetInformation(unsigned int pageP)
|
||||||
|
{
|
||||||
|
// generate information string
|
||||||
|
cString s;
|
||||||
|
switch (pageP) {
|
||||||
|
case SATIP_DEVICE_INFO_GENERAL:
|
||||||
|
s = GetGeneralInformation();
|
||||||
|
break;
|
||||||
|
case SATIP_DEVICE_INFO_PIDS:
|
||||||
|
s = GetPidsInformation();
|
||||||
|
break;
|
||||||
|
case SATIP_DEVICE_INFO_FILTERS:
|
||||||
|
s = GetFiltersInformation();
|
||||||
|
break;
|
||||||
|
case SATIP_DEVICE_INFO_PROTOCOL:
|
||||||
|
s = pTunerM ? *pTunerM->GetInformation() : "";
|
||||||
|
break;
|
||||||
|
case SATIP_DEVICE_INFO_BITRATE:
|
||||||
|
s = pTunerM ? *pTunerM->GetTunerStatistic() : "";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
s = cString::sprintf("%s%s%s",
|
||||||
|
*GetGeneralInformation(),
|
||||||
|
*GetPidsInformation(),
|
||||||
|
*GetFiltersInformation());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipDevice::DeviceType(void) const
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
return "SAT>IP";
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipDevice::DeviceName(void) const
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
return deviceNameM;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipDevice::SignalStrength(void) const
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
return (pTunerM ? pTunerM->SignalStrength() : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipDevice::SignalQuality(void) const
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
return (pTunerM ? pTunerM->SignalQuality() : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::ProvidesSource(int sourceP) const
|
||||||
|
{
|
||||||
|
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
int model = 0;
|
||||||
|
if (cSource::IsType(sourceP, 'Z'))
|
||||||
|
model |= cSatipServer::eSatipModelTypeDVBS2;
|
||||||
|
if (cSource::IsType(sourceP, 'Y'))
|
||||||
|
model |= cSatipServer::eSatipModelTypeDVBT2 | cSatipServer::eSatipModelTypeDVBT;
|
||||||
|
return !!cSatipDiscover::GetInstance()->GetServer(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::ProvidesTransponder(const cChannel *channelP) const
|
||||||
|
{
|
||||||
|
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
return (ProvidesSource(channelP->Source()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::ProvidesChannel(const cChannel *channelP, int priorityP, bool *needsDetachReceiversP) const
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
bool hasPriority = (priorityP == IDLEPRIORITY) || (priorityP > this->Priority());
|
||||||
|
bool needsDetachReceivers = false;
|
||||||
|
|
||||||
|
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
|
||||||
|
if (channelP && ProvidesTransponder(channelP)) {
|
||||||
|
result = hasPriority;
|
||||||
|
if (priorityP > IDLEPRIORITY) {
|
||||||
|
if (Receiving()) {
|
||||||
|
if (IsTunedToTransponder(channelP)) {
|
||||||
|
if (channelP->Vpid() && !HasPid(channelP->Vpid()) || channelP->Apid(0) && !HasPid(channelP->Apid(0)) || channelP->Dpid(0) && !HasPid(channelP->Dpid(0))) {
|
||||||
|
if (CamSlot() && channelP->Ca() >= CA_ENCRYPTED_MIN) {
|
||||||
|
if (CamSlot()->CanDecrypt(channelP))
|
||||||
|
result = true;
|
||||||
|
else
|
||||||
|
needsDetachReceivers = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
needsDetachReceivers = Receiving();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (needsDetachReceiversP)
|
||||||
|
*needsDetachReceiversP = needsDetachReceivers;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::ProvidesEIT(void) const
|
||||||
|
{
|
||||||
|
return (SatipConfig.GetEITScan() && pTunerM && pTunerM->IsTuned());
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipDevice::NumProvidedSystems(void) const
|
||||||
|
{
|
||||||
|
return cSatipDiscover::GetInstance()->NumProvidedSystems();
|
||||||
|
}
|
||||||
|
|
||||||
|
const cChannel *cSatipDevice::GetCurrentlyTunedTransponder(void) const
|
||||||
|
{
|
||||||
|
return &channelM;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::IsTunedToTransponder(const cChannel *channelP) const
|
||||||
|
{
|
||||||
|
if (pTunerM && !pTunerM->IsTuned())
|
||||||
|
return false;
|
||||||
|
if ((channelM.Source() != channelP->Source()) || (channelM.Transponder() != channelP->Transponder()))
|
||||||
|
return false;
|
||||||
|
return (strcmp(channelM.Parameters(), channelP->Parameters()) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::MaySwitchTransponder(const cChannel *channelP) const
|
||||||
|
{
|
||||||
|
return cDevice::MaySwitchTransponder(channelP);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::SetChannelDevice(const cChannel *channelP, bool liveViewP)
|
||||||
|
{
|
||||||
|
if (channelP) {
|
||||||
|
cSatipTransponderParameters stp(channelP->Parameters());
|
||||||
|
cString params = stp.UrlParameters(cSource::ToChar(channelP->Source()));
|
||||||
|
cString address;
|
||||||
|
int mode = 0;
|
||||||
|
if (cSource::IsType(channelP->Source(), 'Z'))
|
||||||
|
mode |= cSatipServer::eSatipModelTypeDVBS2;
|
||||||
|
if (cSource::IsType(channelP->Source(), 'Y'))
|
||||||
|
mode |= stp.System() ? cSatipServer::eSatipModelTypeDVBT2 : cSatipServer::eSatipModelTypeDVBT;
|
||||||
|
cSatipServer *server = cSatipDiscover::GetInstance()->GetServer(mode);
|
||||||
|
if (!server) {
|
||||||
|
debug("cSatipDevice::%s(%u): no suitable server found", __FUNCTION__, deviceIndexM);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
address = server->Address();
|
||||||
|
float freq = channelP->Frequency();
|
||||||
|
if (isempty(params)) {
|
||||||
|
error("Unrecognized SAT>IP channel parameters: %s", channelP->Parameters());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Scale down frequencies to MHz
|
||||||
|
while (freq > 20000L)
|
||||||
|
freq /= 1000L;
|
||||||
|
params = cString::sprintf("freq=%s%s", *dtoa(freq, "%.3f"), *params);
|
||||||
|
if (cSource::IsType(channelP->Source(), 'Z'))
|
||||||
|
params = cString::sprintf("%s&sr=%d", *params, channelP->Srate());
|
||||||
|
if (pTunerM && pTunerM->SetSource(*address, *params, deviceIndexM)) {
|
||||||
|
deviceNameM = cString::sprintf("%s %d %s:%s:%s", *DeviceType(), deviceIndexM, server->Address(), server->Model(), server->Description());
|
||||||
|
channelM = *channelP;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::SetPid(cPidHandle *handleP, int typeP, bool onP)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u): pid=%d type=%d on=%d", __FUNCTION__, deviceIndexM, handleP->pid, typeP, onP);
|
||||||
|
if (pTunerM && handleP && handleP->pid >= 0) {
|
||||||
|
if (onP)
|
||||||
|
return pTunerM->SetPid(handleP->pid, typeP, true);
|
||||||
|
else if (!handleP->used)
|
||||||
|
return pTunerM->SetPid(handleP->pid, typeP, false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipDevice::OpenFilter(u_short pidP, u_char tidP, u_char maskP)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u): pid=%d tid=%d mask=%d", __FUNCTION__, deviceIndexM, pidP, tidP, maskP);
|
||||||
|
if (pSectionFilterHandlerM) {
|
||||||
|
if (pTunerM)
|
||||||
|
pTunerM->SetPid(pidP, ptOther, true);
|
||||||
|
return pSectionFilterHandlerM->Open(pidP, tidP, maskP);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipDevice::CloseFilter(int handleP)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u): handle=%d", __FUNCTION__, deviceIndexM, handleP);
|
||||||
|
if (pSectionFilterHandlerM) {
|
||||||
|
if (pTunerM)
|
||||||
|
pTunerM->SetPid(pSectionFilterHandlerM->GetPid(handleP), ptOther, false);
|
||||||
|
pSectionFilterHandlerM->Close(handleP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::OpenDvr(void)
|
||||||
|
{
|
||||||
|
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
isPacketDeliveredM = false;
|
||||||
|
tsBufferM->Clear();
|
||||||
|
if (pTunerM)
|
||||||
|
pTunerM->Open();
|
||||||
|
isOpenDvrM = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipDevice::CloseDvr(void)
|
||||||
|
{
|
||||||
|
debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
if (pTunerM)
|
||||||
|
pTunerM->Close();
|
||||||
|
isOpenDvrM = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::HasLock(int timeoutMsP) const
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u): timeoutMs=%d", __FUNCTION__, deviceIndexM, timeoutMsP);
|
||||||
|
return (pTunerM && pTunerM->HasLock());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::HasInternalCam(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipDevice::WriteData(uchar *bufferP, int lengthP)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
// Fill up TS buffer
|
||||||
|
if (tsBufferM) {
|
||||||
|
int len = tsBufferM->Put(bufferP, lengthP);
|
||||||
|
if (len != lengthP)
|
||||||
|
tsBufferM->ReportOverflow(lengthP - len);
|
||||||
|
}
|
||||||
|
// Filter the sections
|
||||||
|
if (pSectionFilterHandlerM)
|
||||||
|
pSectionFilterHandlerM->Write(bufferP, lengthP);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int cSatipDevice::CheckData(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
if (tsBufferM)
|
||||||
|
return (unsigned int)tsBufferM->Free();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uchar *cSatipDevice::GetData(int *availableP)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
if (isOpenDvrM && tsBufferM) {
|
||||||
|
int count = 0;
|
||||||
|
if (isPacketDeliveredM)
|
||||||
|
SkipData(TS_SIZE);
|
||||||
|
uchar *p = tsBufferM->Get(count);
|
||||||
|
if (p && count >= TS_SIZE) {
|
||||||
|
if (*p != TS_SYNC_BYTE) {
|
||||||
|
for (int i = 1; i < count; i++) {
|
||||||
|
if (p[i] == TS_SYNC_BYTE) {
|
||||||
|
count = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tsBufferM->Del(count);
|
||||||
|
info("Skipped %d bytes to sync on TS packet", count);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
isPacketDeliveredM = true;
|
||||||
|
if (availableP)
|
||||||
|
*availableP = count;
|
||||||
|
// Update pid statistics
|
||||||
|
AddPidStatistic(ts_pid(p), payload(p));
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipDevice::SkipData(int countP)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
tsBufferM->Del(countP);
|
||||||
|
isPacketDeliveredM = false;
|
||||||
|
// Update buffer statistics
|
||||||
|
AddBufferStatistic(countP, tsBufferM->Available());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDevice::GetTSPacket(uchar *&dataP)
|
||||||
|
{
|
||||||
|
//debug("cSatipDevice::%s(%u)", __FUNCTION__, deviceIndexM);
|
||||||
|
if (tsBufferM) {
|
||||||
|
#if defined(APIVERSNUM) && APIVERSNUM >= 20104
|
||||||
|
if (cCamSlot *cs = CamSlot()) {
|
||||||
|
if (cs->WantsTsData()) {
|
||||||
|
int available;
|
||||||
|
dataP = GetData(&available);
|
||||||
|
if (dataP) {
|
||||||
|
dataP = cs->Decrypt(dataP, available);
|
||||||
|
SkipData(available);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
dataP = GetData();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Reduce cpu load by preventing busylooping
|
||||||
|
cCondWait::SleepMs(10);
|
||||||
|
dataP = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
106
device.h
Normal file
106
device.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* device.h: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SATIP_DEVICE_H
|
||||||
|
#define __SATIP_DEVICE_H
|
||||||
|
|
||||||
|
#include <vdr/device.h>
|
||||||
|
#include "common.h"
|
||||||
|
#include "deviceif.h"
|
||||||
|
#include "tuner.h"
|
||||||
|
#include "sectionfilter.h"
|
||||||
|
#include "statistics.h"
|
||||||
|
|
||||||
|
class cSatipDevice : public cDevice, public cSatipPidStatistics, public cSatipBufferStatistics, public cSatipDeviceIf {
|
||||||
|
// static ones
|
||||||
|
public:
|
||||||
|
static unsigned int deviceCount;
|
||||||
|
static bool Initialize(unsigned int DeviceCount);
|
||||||
|
static void Shutdown(void);
|
||||||
|
static unsigned int Count(void);
|
||||||
|
static cSatipDevice *GetSatipDevice(int CardIndex);
|
||||||
|
|
||||||
|
// private parts
|
||||||
|
private:
|
||||||
|
unsigned int deviceIndexM;
|
||||||
|
bool isPacketDeliveredM;
|
||||||
|
bool isOpenDvrM;
|
||||||
|
cString deviceNameM;
|
||||||
|
cChannel channelM;
|
||||||
|
cRingBufferLinear *tsBufferM;
|
||||||
|
cSatipTuner *pTunerM;
|
||||||
|
cSatipSectionFilterHandler *pSectionFilterHandlerM;
|
||||||
|
cMutex mutexM;
|
||||||
|
|
||||||
|
// constructor & destructor
|
||||||
|
public:
|
||||||
|
cSatipDevice(unsigned int deviceIndexP);
|
||||||
|
virtual ~cSatipDevice();
|
||||||
|
cString GetInformation(unsigned int pageP = SATIP_DEVICE_INFO_ALL);
|
||||||
|
|
||||||
|
// copy and assignment constructors
|
||||||
|
private:
|
||||||
|
cSatipDevice(const cSatipDevice&);
|
||||||
|
cSatipDevice& operator=(const cSatipDevice&);
|
||||||
|
|
||||||
|
// for statistics and general information
|
||||||
|
cString GetGeneralInformation(void);
|
||||||
|
cString GetPidsInformation(void);
|
||||||
|
cString GetFiltersInformation(void);
|
||||||
|
|
||||||
|
// for channel info
|
||||||
|
public:
|
||||||
|
virtual cString DeviceType(void) const;
|
||||||
|
virtual cString DeviceName(void) const;
|
||||||
|
virtual int SignalStrength(void) const;
|
||||||
|
virtual int SignalQuality(void) const;
|
||||||
|
|
||||||
|
// for channel selection
|
||||||
|
public:
|
||||||
|
virtual bool ProvidesSource(int sourceP) const;
|
||||||
|
virtual bool ProvidesTransponder(const cChannel *channelP) const;
|
||||||
|
virtual bool ProvidesChannel(const cChannel *channelP, int priorityP = -1, bool *needsDetachReceiversP = NULL) const;
|
||||||
|
virtual bool ProvidesEIT(void) const;
|
||||||
|
virtual int NumProvidedSystems(void) const;
|
||||||
|
virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
|
||||||
|
virtual bool IsTunedToTransponder(const cChannel *channelP) const;
|
||||||
|
virtual bool MaySwitchTransponder(const cChannel *channelP) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool SetChannelDevice(const cChannel *channelP, bool liveViewP);
|
||||||
|
|
||||||
|
// for recording
|
||||||
|
private:
|
||||||
|
uchar *GetData(int *availableP = NULL);
|
||||||
|
void SkipData(int countP);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool SetPid(cPidHandle *handleP, int typeP, bool onP);
|
||||||
|
virtual bool OpenDvr(void);
|
||||||
|
virtual void CloseDvr(void);
|
||||||
|
virtual bool GetTSPacket(uchar *&dataP);
|
||||||
|
|
||||||
|
// for section filtering
|
||||||
|
public:
|
||||||
|
virtual int OpenFilter(u_short pidP, u_char tidP, u_char maskP);
|
||||||
|
virtual void CloseFilter(int handleP);
|
||||||
|
|
||||||
|
// for transponder lock
|
||||||
|
public:
|
||||||
|
virtual bool HasLock(int timeoutMsP) const;
|
||||||
|
|
||||||
|
// for common interface
|
||||||
|
public:
|
||||||
|
virtual bool HasInternalCam(void);
|
||||||
|
|
||||||
|
// for internal device interface
|
||||||
|
public:
|
||||||
|
virtual void WriteData(u_char *bufferP, int lengthP);
|
||||||
|
virtual unsigned int CheckData(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SATIP_DEVICE_H
|
23
deviceif.h
Normal file
23
deviceif.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* deviceif.h: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SATIP_DEVICEIF_H
|
||||||
|
#define __SATIP_DEVICEIF_H
|
||||||
|
|
||||||
|
class cSatipDeviceIf {
|
||||||
|
public:
|
||||||
|
cSatipDeviceIf() {}
|
||||||
|
virtual ~cSatipDeviceIf() {}
|
||||||
|
virtual void WriteData(u_char *bufferP, int lengthP) = 0;
|
||||||
|
virtual unsigned int CheckData(void) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
cSatipDeviceIf(const cSatipDeviceIf&);
|
||||||
|
cSatipDeviceIf& operator=(const cSatipDeviceIf&);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SATIP_DEVICEIF_H
|
283
discover.c
Normal file
283
discover.c
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
/*
|
||||||
|
* discover.c: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "discover.h"
|
||||||
|
|
||||||
|
cSatipDiscover *cSatipDiscover::instanceS = NULL;
|
||||||
|
|
||||||
|
const char *cSatipDiscover::bcastAddressS = "239.255.255.250";
|
||||||
|
const char *cSatipDiscover::bcastMessageS = "M-SEARCH * HTTP/1.1\r\n" \
|
||||||
|
"HOST: 239.255.255.250:1900\r\n" \
|
||||||
|
"MAN: \"ssdp:discover\"\r\n" \
|
||||||
|
"ST: urn:ses-com:device:SatIPServer:1\r\n" \
|
||||||
|
"MX: 2\r\n\r\n";
|
||||||
|
|
||||||
|
cSatipDiscover *cSatipDiscover::GetInstance(void)
|
||||||
|
{
|
||||||
|
if (!instanceS)
|
||||||
|
instanceS = new cSatipDiscover();
|
||||||
|
return instanceS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDiscover::Initialize(void)
|
||||||
|
{
|
||||||
|
debug("cSatipDiscover::%s()", __FUNCTION__);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipDiscover::Destroy(void)
|
||||||
|
{
|
||||||
|
debug("cSatipDiscover::%s()", __FUNCTION__);
|
||||||
|
DELETE_POINTER(instanceS);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cSatipDiscover::WriteCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
|
||||||
|
{
|
||||||
|
cSatipDiscover *obj = reinterpret_cast<cSatipDiscover *>(dataP);
|
||||||
|
size_t len = sizeP * nmembP;
|
||||||
|
//debug("cSatipDiscover::%s(%zu)", __FUNCTION__, len);
|
||||||
|
|
||||||
|
char *s, *p = (char *)ptrP;
|
||||||
|
char *r = strtok_r(p, "\r\n", &s);
|
||||||
|
char *desc = NULL, *model = NULL, *addr = NULL;
|
||||||
|
while (r) {
|
||||||
|
//debug("cSatipDiscover::%s(%zu): %s", __FUNCTION__, len, r);
|
||||||
|
// <friendlyName>OctopusNet</friendlyName>
|
||||||
|
if (startswith(r, "<friendlyName"))
|
||||||
|
desc = StripTags(r);
|
||||||
|
// <satip:X_SATIPCAP xmlns:satip="urn:ses-com:satip">DVBT-2</satip:X_SATIPCAP>
|
||||||
|
if (startswith(r, "<satip:X_SATIPCAP"))
|
||||||
|
model = StripTags(r);
|
||||||
|
r = strtok_r(NULL, "\r\n", &s);
|
||||||
|
}
|
||||||
|
if (obj) {
|
||||||
|
CURLcode res = CURLE_OK;
|
||||||
|
SATIP_CURL_EASY_GETINFO(obj->handleM, CURLINFO_PRIMARY_IP, &addr);
|
||||||
|
obj->AddServer(addr, desc, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipDiscover::cSatipDiscover()
|
||||||
|
: cThread("SAT>IP discover"),
|
||||||
|
mutexM(),
|
||||||
|
handleM(curl_easy_init()),
|
||||||
|
socketM(new cSatipSocket()),
|
||||||
|
sleepM(),
|
||||||
|
probeIntervalM(0),
|
||||||
|
serversM(new cSatipServers())
|
||||||
|
{
|
||||||
|
debug("cSatipDiscover::%s()", __FUNCTION__);
|
||||||
|
// Start the thread
|
||||||
|
Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipDiscover::~cSatipDiscover()
|
||||||
|
{
|
||||||
|
debug("cSatipDiscover::%s()", __FUNCTION__);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
sleepM.Signal();
|
||||||
|
if (Running())
|
||||||
|
Cancel(3);
|
||||||
|
// Free allocated memory
|
||||||
|
DELETENULL(socketM);
|
||||||
|
DELETENULL(serversM);
|
||||||
|
if (handleM)
|
||||||
|
curl_easy_cleanup(handleM);
|
||||||
|
handleM = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipDiscover::Action(void)
|
||||||
|
{
|
||||||
|
debug("cSatipDiscover::%s(): entering", __FUNCTION__);
|
||||||
|
// Do the thread loop
|
||||||
|
while (Running()) {
|
||||||
|
if (probeIntervalM.TimedOut()) {
|
||||||
|
probeIntervalM.Set(eProbeIntervalMs);
|
||||||
|
Probe();
|
||||||
|
Janitor();
|
||||||
|
}
|
||||||
|
// to avoid busy loop and reduce cpu load
|
||||||
|
sleepM.Wait(10);
|
||||||
|
}
|
||||||
|
debug("cSatipDiscover::%s(): exiting", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipDiscover::Janitor(void)
|
||||||
|
{
|
||||||
|
debug("cSatipDiscover::%s()", __FUNCTION__);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
for (cSatipServer *srv = serversM->First(); srv; srv = serversM->Next(srv)) {
|
||||||
|
if (srv->LastSeen() > eProbeIntervalMs * 2) {
|
||||||
|
info("Removing device %s (%s %s)", srv->Description(), srv->Address(), srv->Model());
|
||||||
|
serversM->Del(srv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipDiscover::Probe(void)
|
||||||
|
{
|
||||||
|
debug("cSatipDiscover::%s()", __FUNCTION__);
|
||||||
|
if (socketM && socketM->Open(eDiscoveryPort)) {
|
||||||
|
cTimeMs timeout(eProbeTimeoutMs);
|
||||||
|
socketM->Write(bcastAddressS, reinterpret_cast<const unsigned char *>(bcastMessageS), strlen(bcastMessageS));
|
||||||
|
while (Running() && !timeout.TimedOut()) {
|
||||||
|
Read();
|
||||||
|
// to avoid busy loop and reduce cpu load
|
||||||
|
sleepM.Wait(100);
|
||||||
|
}
|
||||||
|
socketM->Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipDiscover::Read(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipDiscover::%s()", __FUNCTION__);
|
||||||
|
if (socketM) {
|
||||||
|
unsigned char *buf = MALLOC(unsigned char, eProbeBufferSize + 1);
|
||||||
|
if (buf) {
|
||||||
|
memset(buf, 0, eProbeBufferSize + 1);
|
||||||
|
int len = socketM->Read(buf, eProbeBufferSize);
|
||||||
|
if (len > 0) {
|
||||||
|
//debug("cSatipDiscover::%s(): len=%d", __FUNCTION__, len);
|
||||||
|
bool status = false;
|
||||||
|
char *s, *p = reinterpret_cast<char *>(buf), *location = NULL;
|
||||||
|
char *r = strtok_r(p, "\r\n", &s);
|
||||||
|
while (r) {
|
||||||
|
//debug("cSatipDiscover::%s(): %s", __FUNCTION__, r);
|
||||||
|
// Check the status code
|
||||||
|
// HTTP/1.1 200 OK
|
||||||
|
if (!status && startswith(r, "HTTP/1.1 200 OK")) {
|
||||||
|
status = true;
|
||||||
|
}
|
||||||
|
// Check the location data
|
||||||
|
// LOCATION: http://192.168.0.115:8888/octonet.xml
|
||||||
|
if (status && startswith(r, "LOCATION:")) {
|
||||||
|
location = compactspace(r + 9);
|
||||||
|
debug("cSatipDiscover::%s(): location='%s'", __FUNCTION__, location);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
r = strtok_r(NULL, "\r\n", &s);
|
||||||
|
}
|
||||||
|
if (handleM && !isempty(location)) {
|
||||||
|
long rc = 0;
|
||||||
|
CURLcode res = CURLE_OK;
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Verbose output
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_VERBOSE, 1L);
|
||||||
|
#endif
|
||||||
|
// Set callback
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipDiscover::WriteCallback);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
|
||||||
|
|
||||||
|
// No progress meter and no signaling
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOPROGRESS, 1L);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOSIGNAL, 1L);
|
||||||
|
|
||||||
|
// Set timeouts
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_TIMEOUT_MS, (long)eConnectTimeoutMs);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_CONNECTTIMEOUT_MS, (long)eConnectTimeoutMs);
|
||||||
|
|
||||||
|
// Set user-agent
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s", PLUGIN_NAME_I18N, VERSION));
|
||||||
|
|
||||||
|
// Set URL
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_URL, location);
|
||||||
|
|
||||||
|
// Fetch the data
|
||||||
|
SATIP_CURL_EASY_PERFORM(handleM);
|
||||||
|
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_RESPONSE_CODE, &rc);
|
||||||
|
if (rc != 200)
|
||||||
|
error("Discovery detected invalid status code: %ld", rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipDiscover::AddServer(const char *addrP, const char *descP, const char * modelP)
|
||||||
|
{
|
||||||
|
debug("cSatipDiscover::%s(%s, %s, %s)", __FUNCTION__, addrP, descP, modelP);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
if (serversM) {
|
||||||
|
cSatipServer *tmp = new cSatipServer(addrP, descP, modelP);
|
||||||
|
// Validate against existing servers
|
||||||
|
bool found = false;
|
||||||
|
for (cSatipServer *s = serversM->First(); s; s = serversM->Next(s)) {
|
||||||
|
if (s->Compare(*tmp) == 0) {
|
||||||
|
found = true;
|
||||||
|
s->Update();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
info("Adding device %s (%s %s)", tmp->Description(), tmp->Address(), tmp->Model());
|
||||||
|
serversM->Add(tmp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DELETENULL(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipDiscover::IsValidServer(cSatipServer *serverP)
|
||||||
|
{
|
||||||
|
//debug("cSatipDiscover::%s(%d)", __FUNCTION__);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
for (cSatipServer *srv = serversM->First(); srv; srv = serversM->Next(srv)) {
|
||||||
|
if (srv == serverP)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipServer *cSatipDiscover::GetServer(int modelP)
|
||||||
|
{
|
||||||
|
//debug("cSatipDiscover::%s(%d)", __FUNCTION__, modelP);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
for (cSatipServer *srv = serversM->First(); srv; srv = serversM->Next(srv)) {
|
||||||
|
if (srv->Match(modelP))
|
||||||
|
return srv;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipServers *cSatipDiscover::GetServers(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipDiscover::%s()", __FUNCTION__);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
return serversM;
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipDiscover::GetServerList(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipDiscover::%s(%d)", __FUNCTION__, modelP);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
cString list = "";
|
||||||
|
for (cSatipServer *srv = serversM->First(); srv; srv = serversM->Next(srv))
|
||||||
|
list = cString::sprintf("%s%s:%s:%s\n", *list, srv->Address(), srv->Model(), srv->Description());
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipDiscover::NumProvidedSystems(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipDiscover::%s(%d)", __FUNCTION__, modelP);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
int count = 0;
|
||||||
|
for (cSatipServer *srv = serversM->First(); srv; srv = serversM->Next(srv)) {
|
||||||
|
count += srv->Satellite();
|
||||||
|
count += srv->Terrestrial();
|
||||||
|
count += srv->Terrestrial2();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
129
discover.h
Normal file
129
discover.h
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* discover.h: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SATIP_DISCOVER_H
|
||||||
|
#define __SATIP_DISCOVER_H
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <curl/easy.h>
|
||||||
|
|
||||||
|
#include <vdr/thread.h>
|
||||||
|
#include <vdr/tools.h>
|
||||||
|
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
class cSatipServer : public cListObject {
|
||||||
|
private:
|
||||||
|
enum eSatipServer {
|
||||||
|
eSatipServerDVBS2 = 0,
|
||||||
|
eSatipServerDVBT,
|
||||||
|
eSatipServerDVBT2,
|
||||||
|
eSatipServerCount
|
||||||
|
};
|
||||||
|
cString addressM;
|
||||||
|
cString descriptionM;
|
||||||
|
cString modelM;
|
||||||
|
int modelCountM[eSatipServerCount];
|
||||||
|
int modelTypeM;
|
||||||
|
cTimeMs lastSeenM;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum eSatipModelType {
|
||||||
|
eSatipModelTypeDVBS2 = 0x01,
|
||||||
|
eSatipModelTypeDVBT = 0x02,
|
||||||
|
eSatipModelTypeDVBT2 = 0x04,
|
||||||
|
eSatipModelTypeMask = 0x0F
|
||||||
|
};
|
||||||
|
cSatipServer(const char *addressP, const char *descriptionP, const char *modelP) : addressM(addressP), descriptionM(descriptionP), modelM(modelP), modelTypeM(eSatipModelTypeMask), lastSeenM(0)
|
||||||
|
{
|
||||||
|
memset(modelCountM, 0, sizeof(modelCountM));
|
||||||
|
if (isempty(*modelM))
|
||||||
|
modelM = "DVBS-1,DVBT2-1";
|
||||||
|
char *s, *p = strdup(*modelM);
|
||||||
|
char *r = strtok_r(p, ",", &s);
|
||||||
|
while (r) {
|
||||||
|
if (strstr(r, "DVBS2")) {
|
||||||
|
modelTypeM |= cSatipServer::eSatipModelTypeDVBS2;
|
||||||
|
if (char *c = strstr(r, "-"))
|
||||||
|
modelCountM[eSatipServerDVBS2] = atoi(++c);
|
||||||
|
}
|
||||||
|
if (strstr(r, "DVBT2")) {
|
||||||
|
modelTypeM |= cSatipServer::eSatipModelTypeDVBT | cSatipServer::eSatipModelTypeDVBT2;
|
||||||
|
if (char *c = strstr(r, "-"))
|
||||||
|
modelCountM[eSatipServerDVBT2] = atoi(++c);
|
||||||
|
}
|
||||||
|
if (strstr(r, "DVBT")) {
|
||||||
|
modelTypeM |= cSatipServer::eSatipModelTypeDVBT;
|
||||||
|
if (char *c = strstr(r, "-"))
|
||||||
|
modelCountM[eSatipServerDVBT] = atoi(++c);
|
||||||
|
}
|
||||||
|
r = strtok_r(NULL, ",", &s);
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
virtual int Compare(const cListObject &listObjectP) const { const cSatipServer *s = (const cSatipServer *)&listObjectP; return strcasecmp(*addressM, *s->addressM); }
|
||||||
|
const char *Description() { return *descriptionM; }
|
||||||
|
const char *Address() { return *addressM; }
|
||||||
|
const char *Model(void) { return modelM; }
|
||||||
|
int ModelType(void) { return modelTypeM; }
|
||||||
|
bool Match(int modelP) { return ((modelP & eSatipModelTypeMask) & modelTypeM); }
|
||||||
|
int Satellite() { return (modelTypeM & eSatipModelTypeDVBS2) ? modelCountM[eSatipServerDVBS2] : 0; }
|
||||||
|
int Terrestrial() { return (modelTypeM & eSatipModelTypeDVBT) ? modelCountM[eSatipServerDVBT] : 0; }
|
||||||
|
int Terrestrial2() { return (modelTypeM & eSatipModelTypeDVBT2) ? modelCountM[eSatipServerDVBT2] : 0; }
|
||||||
|
int LastSeen(void) { return lastSeenM.Elapsed(); }
|
||||||
|
void Update(void) { lastSeenM.Set(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class cSatipServers : public cList<cSatipServer> {
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class cSatipDiscover : public cThread {
|
||||||
|
private:
|
||||||
|
enum {
|
||||||
|
eConnectTimeoutMs = 1500, // in milliseconds
|
||||||
|
eDiscoveryPort = 1900,
|
||||||
|
eProbeBufferSize = 1024, // in bytes
|
||||||
|
eProbeTimeoutMs = 2000, // in milliseconds
|
||||||
|
eProbeIntervalMs = 60000 // in milliseconds
|
||||||
|
};
|
||||||
|
static cSatipDiscover *instanceS;
|
||||||
|
static const char *bcastAddressS;
|
||||||
|
static const char *bcastMessageS;
|
||||||
|
static size_t WriteCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP);
|
||||||
|
cMutex mutexM;
|
||||||
|
CURL *handleM;
|
||||||
|
cSatipSocket *socketM;
|
||||||
|
cCondWait sleepM;
|
||||||
|
cTimeMs probeIntervalM;
|
||||||
|
cSatipServers *serversM;
|
||||||
|
void Janitor(void);
|
||||||
|
void Probe(void);
|
||||||
|
void Read(void);
|
||||||
|
void AddServer(const char *addrP, const char *descP, const char *modelP);
|
||||||
|
// constructor
|
||||||
|
cSatipDiscover();
|
||||||
|
// to prevent copy constructor and assignment
|
||||||
|
cSatipDiscover(const cSatipDiscover&);
|
||||||
|
cSatipDiscover& operator=(const cSatipDiscover&);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void Action(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static cSatipDiscover *GetInstance(void);
|
||||||
|
static bool Initialize(void);
|
||||||
|
static void Destroy(void);
|
||||||
|
virtual ~cSatipDiscover();
|
||||||
|
bool IsValidServer(cSatipServer *serverP);
|
||||||
|
cSatipServer *GetServer(int modelP);
|
||||||
|
cSatipServers *GetServers(void);
|
||||||
|
cString GetServerList(void);
|
||||||
|
int NumProvidedSystems(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SATIP_DISCOVER_H
|
141
po/de_DE.po
Normal file
141
po/de_DE.po
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# VDR plugin language source file.
|
||||||
|
# Copyright (C) 2007-2014 Rolf Ahrenberg & Antti Seppala
|
||||||
|
# This file is distributed under the same license as the iptv package.
|
||||||
|
# Frank Neumann, 2014
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: vdr-satip 0.0.1\n"
|
||||||
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
|
"POT-Creation-Date: 2014-03-08 03:08+0200\n"
|
||||||
|
"PO-Revision-Date: 2014-03-08 03:08+0200\n"
|
||||||
|
"Last-Translator: Frank Neumann <fnu@yavdr.org>\n"
|
||||||
|
"Language-Team: German <vdr@linuxtv.org>\n"
|
||||||
|
"Language: de\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "PAT (0x00)"
|
||||||
|
msgstr "PAT (0x00)"
|
||||||
|
|
||||||
|
msgid "NIT (0x40)"
|
||||||
|
msgstr "NIT (0x40)"
|
||||||
|
|
||||||
|
msgid "SDT (0x42)"
|
||||||
|
msgstr "SDT (0x42)"
|
||||||
|
|
||||||
|
msgid "EIT (0x4E/0x4F)"
|
||||||
|
msgstr "EIT (0x4E/0x4F)"
|
||||||
|
|
||||||
|
msgid "EIT (0x5X)"
|
||||||
|
msgstr "EIT (0x5X)"
|
||||||
|
|
||||||
|
msgid "EIT (0x6X)"
|
||||||
|
msgstr "EIT (0x6X)"
|
||||||
|
|
||||||
|
msgid "TDT (0x70)"
|
||||||
|
msgstr "TDT (0x70)"
|
||||||
|
|
||||||
|
msgid "SAT>IP Devices"
|
||||||
|
msgstr "SAT>IP Geräte"
|
||||||
|
|
||||||
|
msgid "SAT>IP Device"
|
||||||
|
msgstr "SAT>IP Gerät"
|
||||||
|
|
||||||
|
msgid "Button$Scan"
|
||||||
|
msgstr "Starte Kanalsuche"
|
||||||
|
|
||||||
|
msgid "SAT>IP Information"
|
||||||
|
msgstr "SAT>IP Informationen"
|
||||||
|
|
||||||
|
msgid "General"
|
||||||
|
msgstr "Allgemein"
|
||||||
|
|
||||||
|
msgid "Pids"
|
||||||
|
msgstr "Pids"
|
||||||
|
|
||||||
|
msgid "Filters"
|
||||||
|
msgstr "Filter"
|
||||||
|
|
||||||
|
msgid "Bits/bytes"
|
||||||
|
msgstr "Bits/Bytes"
|
||||||
|
|
||||||
|
msgid "SAT>IP information not available!"
|
||||||
|
msgstr "Keine SAT>IP Informationen verfügbar!"
|
||||||
|
|
||||||
|
msgid "Enable EPG scanning"
|
||||||
|
msgstr "Aktiviere EPG Aktualisierung"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Define whether the EPG background scanning shall be used.\n"
|
||||||
|
"\n"
|
||||||
|
"This setting disables the automatic EIT scanning functionality for all SAT>IP devices."
|
||||||
|
msgstr ""
|
||||||
|
"Definiert ob EPG im Hintergrund aktualisiert werden soll oder nicht.\n"
|
||||||
|
"\n"
|
||||||
|
"Diese Einstellung schaltet die automatische EIT Aktualisierung für alle SAT>IP Geräte."
|
||||||
|
|
||||||
|
|
||||||
|
msgid "Disabled filters"
|
||||||
|
msgstr "Deaktivierte Filter"
|
||||||
|
|
||||||
|
msgid "none"
|
||||||
|
msgstr "keine"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Define number of section filters to be disabled.\n"
|
||||||
|
"\n"
|
||||||
|
"Certain section filters might cause some unwanted behaviour to VDR such as time being falsely synchronized. By black-listing the filters here useful section data can be left intact for VDR to process."
|
||||||
|
msgstr ""
|
||||||
|
"Bestimme die Anzahl der Abschnittsfilter die deaktiviert werden sollen.\n"
|
||||||
|
"\n"
|
||||||
|
"Bestimmte Abschnittsfilter können unerwünschtes Verhalten mit VDR, z.B. falsche Zeit-Synchronisation, verursachen. Durch das Ausblenden einzelner Filter können nützliche Daten dieser Abschnitte für den VDR erhalten werden."
|
||||||
|
|
||||||
|
msgid "Filter"
|
||||||
|
msgstr "Filter"
|
||||||
|
|
||||||
|
msgid "Define an ill-behaving filter to be blacklisted."
|
||||||
|
msgstr "Bestimme einen fehlerhaften Filter der ausgeblendet werden soll."
|
||||||
|
|
||||||
|
msgid "Active SAT>IP devices:"
|
||||||
|
msgstr "Aktive SAT>IP Geräte:"
|
||||||
|
|
||||||
|
msgid "Help"
|
||||||
|
msgstr "Hilfe"
|
||||||
|
|
||||||
|
msgid "off"
|
||||||
|
msgstr "aus"
|
||||||
|
|
||||||
|
msgid "on"
|
||||||
|
msgstr "ein"
|
||||||
|
|
||||||
|
msgid "auto"
|
||||||
|
msgstr "auto"
|
||||||
|
|
||||||
|
msgid "SISO"
|
||||||
|
msgstr "SISO"
|
||||||
|
|
||||||
|
msgid "MISO"
|
||||||
|
msgstr "MISO"
|
||||||
|
|
||||||
|
msgid "Nid"
|
||||||
|
msgstr "Nid"
|
||||||
|
|
||||||
|
msgid "Tid"
|
||||||
|
msgstr "Tid"
|
||||||
|
|
||||||
|
msgid "Rid"
|
||||||
|
msgstr "Rid"
|
||||||
|
|
||||||
|
msgid "SignalSource"
|
||||||
|
msgstr "Signalquelle"
|
||||||
|
|
||||||
|
msgid "PilotTones"
|
||||||
|
msgstr "Pilottöne"
|
||||||
|
|
||||||
|
msgid "T2SystemId"
|
||||||
|
msgstr "T2-Systemkennung"
|
||||||
|
|
||||||
|
msgid "SISO/MISO"
|
||||||
|
msgstr "SISO/MISO"
|
140
po/fi_FI.po
Normal file
140
po/fi_FI.po
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
# VDR plugin language source file.
|
||||||
|
# Copyright (C) 2007-2014 Rolf Ahrenberg & Antti Seppala
|
||||||
|
# This file is distributed under the same license as the iptv package.
|
||||||
|
# Rolf Ahrenberg, 2014
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: vdr-satip 0.0.1\n"
|
||||||
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
|
"POT-Creation-Date: 2014-03-08 03:08+0200\n"
|
||||||
|
"PO-Revision-Date: 2014-03-08 03:08+0200\n"
|
||||||
|
"Last-Translator: Rolf Ahrenberg\n"
|
||||||
|
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
||||||
|
"Language: fi\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "PAT (0x00)"
|
||||||
|
msgstr "PAT (0x00)"
|
||||||
|
|
||||||
|
msgid "NIT (0x40)"
|
||||||
|
msgstr "NIT (0x40)"
|
||||||
|
|
||||||
|
msgid "SDT (0x42)"
|
||||||
|
msgstr "SDT (0x42)"
|
||||||
|
|
||||||
|
msgid "EIT (0x4E/0x4F)"
|
||||||
|
msgstr "EIT (0x4E/0x4F)"
|
||||||
|
|
||||||
|
msgid "EIT (0x5X)"
|
||||||
|
msgstr "EIT (0x5X)"
|
||||||
|
|
||||||
|
msgid "EIT (0x6X)"
|
||||||
|
msgstr "EIT (0x6X)"
|
||||||
|
|
||||||
|
msgid "TDT (0x70)"
|
||||||
|
msgstr "TDT (0x70)"
|
||||||
|
|
||||||
|
msgid "SAT>IP Devices"
|
||||||
|
msgstr "SAT>IP-laitteet"
|
||||||
|
|
||||||
|
msgid "SAT>IP Device"
|
||||||
|
msgstr "SAT>IP-laite"
|
||||||
|
|
||||||
|
msgid "Button$Scan"
|
||||||
|
msgstr "Kanavahaku"
|
||||||
|
|
||||||
|
msgid "SAT>IP Information"
|
||||||
|
msgstr "SAT>IP-tiedot"
|
||||||
|
|
||||||
|
msgid "General"
|
||||||
|
msgstr "Yleiset"
|
||||||
|
|
||||||
|
msgid "Pids"
|
||||||
|
msgstr "Pidit"
|
||||||
|
|
||||||
|
msgid "Filters"
|
||||||
|
msgstr "Suodattimet"
|
||||||
|
|
||||||
|
msgid "Bits/bytes"
|
||||||
|
msgstr "Bitit/tavut"
|
||||||
|
|
||||||
|
msgid "SAT>IP information not available!"
|
||||||
|
msgstr "SAT>IP-tietoja ei saatavilla!"
|
||||||
|
|
||||||
|
msgid "Enable EPG scanning"
|
||||||
|
msgstr "Käytä ohjelmaoppaan taustapäivitystä"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Define whether the EPG background scanning shall be used.\n"
|
||||||
|
"\n"
|
||||||
|
"This setting disables the automatic EIT scanning functionality for all SAT>IP devices."
|
||||||
|
msgstr ""
|
||||||
|
"Määrittele ohjelmaoppaan taustapäivityksen olemassaolo.\n"
|
||||||
|
"\n"
|
||||||
|
"Tällä asetuksella saadaan otettua automaattinen EIT-datan päivitys pois päältä kaikilta SAT>IP-laitteilta."
|
||||||
|
|
||||||
|
msgid "Disabled filters"
|
||||||
|
msgstr "Käytöstä poistetut suodattimet"
|
||||||
|
|
||||||
|
msgid "none"
|
||||||
|
msgstr "tyhjä"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Define number of section filters to be disabled.\n"
|
||||||
|
"\n"
|
||||||
|
"Certain section filters might cause some unwanted behaviour to VDR such as time being falsely synchronized. By black-listing the filters here useful section data can be left intact for VDR to process."
|
||||||
|
msgstr ""
|
||||||
|
"Määrittele käytöstä poistettavien suodattimien lukumäärä sektioille.\n"
|
||||||
|
"\n"
|
||||||
|
"Tietyt sektiot saattavat aiheuttaa virheellistä toimintaa VDR:ssä, esimerkiksi asettavat väärän kellonajan, ja näiden poistaminen auttaa VDR:ää toimimaan kunnolla jäljelle jäävien sektioiden kanssa."
|
||||||
|
|
||||||
|
msgid "Filter"
|
||||||
|
msgstr "Suodatin"
|
||||||
|
|
||||||
|
msgid "Define an ill-behaving filter to be blacklisted."
|
||||||
|
msgstr "Määrittele käytöstä poistettava suodatin, joka lisätään mustalle listalle."
|
||||||
|
|
||||||
|
msgid "Active SAT>IP devices:"
|
||||||
|
msgstr "Aktiiviset SAT>IP-laitteet:"
|
||||||
|
|
||||||
|
msgid "Help"
|
||||||
|
msgstr "Opaste"
|
||||||
|
|
||||||
|
msgid "off"
|
||||||
|
msgstr "pois"
|
||||||
|
|
||||||
|
msgid "on"
|
||||||
|
msgstr "päällä"
|
||||||
|
|
||||||
|
msgid "auto"
|
||||||
|
msgstr "auto"
|
||||||
|
|
||||||
|
msgid "SISO"
|
||||||
|
msgstr "SISO"
|
||||||
|
|
||||||
|
msgid "MISO"
|
||||||
|
msgstr "MISO"
|
||||||
|
|
||||||
|
msgid "Nid"
|
||||||
|
msgstr "Verkko-ID"
|
||||||
|
|
||||||
|
msgid "Tid"
|
||||||
|
msgstr "Lähete-ID"
|
||||||
|
|
||||||
|
msgid "Rid"
|
||||||
|
msgstr "Radio-ID"
|
||||||
|
|
||||||
|
msgid "SignalSource"
|
||||||
|
msgstr "Signaalin lähde"
|
||||||
|
|
||||||
|
msgid "PilotTones"
|
||||||
|
msgstr "Pilottiäänet"
|
||||||
|
|
||||||
|
msgid "T2SystemId"
|
||||||
|
msgstr "T2-järjestelmä"
|
||||||
|
|
||||||
|
msgid "SISO/MISO"
|
||||||
|
msgstr "SISO/MISO"
|
291
satip.c
Normal file
291
satip.c
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
/*
|
||||||
|
* satip.c: A plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <vdr/plugin.h>
|
||||||
|
#include "common.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "discover.h"
|
||||||
|
#include "setup.h"
|
||||||
|
|
||||||
|
#if defined(APIVERSNUM) && APIVERSNUM < 20000
|
||||||
|
#error "VDR-2.0.0 API version or greater is required!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef GITVERSION
|
||||||
|
#define GITVERSION ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char VERSION[] = "0.0.1" GITVERSION;
|
||||||
|
static const char DESCRIPTION[] = trNOOP("SAT>IP Devices");
|
||||||
|
|
||||||
|
class cPluginSatip : public cPlugin {
|
||||||
|
private:
|
||||||
|
unsigned int deviceCountM;
|
||||||
|
cSatipDiscover *discoverM;
|
||||||
|
int ParseFilters(const char *valueP, int *filtersP);
|
||||||
|
public:
|
||||||
|
cPluginSatip(void);
|
||||||
|
virtual ~cPluginSatip();
|
||||||
|
virtual const char *Version(void) { return VERSION; }
|
||||||
|
virtual const char *Description(void) { return tr(DESCRIPTION); }
|
||||||
|
virtual const char *CommandLineHelp(void);
|
||||||
|
virtual bool ProcessArgs(int argc, char *argv[]);
|
||||||
|
virtual bool Initialize(void);
|
||||||
|
virtual bool Start(void);
|
||||||
|
virtual void Stop(void);
|
||||||
|
virtual void Housekeeping(void);
|
||||||
|
virtual void MainThreadHook(void);
|
||||||
|
virtual cString Active(void);
|
||||||
|
virtual time_t WakeupTime(void);
|
||||||
|
virtual const char *MainMenuEntry(void) { return NULL; }
|
||||||
|
virtual cOsdObject *MainMenuAction(void);
|
||||||
|
virtual cMenuSetupPage *SetupMenu(void);
|
||||||
|
virtual bool SetupParse(const char *Name, const char *Value);
|
||||||
|
virtual bool Service(const char *Id, void *Data = NULL);
|
||||||
|
virtual const char **SVDRPHelpPages(void);
|
||||||
|
virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
cPluginSatip::cPluginSatip(void)
|
||||||
|
: deviceCountM(1),
|
||||||
|
discoverM(NULL)
|
||||||
|
{
|
||||||
|
//debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Initialize any member variables here.
|
||||||
|
// DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
|
||||||
|
// VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
|
||||||
|
}
|
||||||
|
|
||||||
|
cPluginSatip::~cPluginSatip()
|
||||||
|
{
|
||||||
|
//debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Clean up after yourself!
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cPluginSatip::CommandLineHelp(void)
|
||||||
|
{
|
||||||
|
debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Return a string that describes all known command line options.
|
||||||
|
return " -d <num>, --devices=<number> number of devices to be created\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPluginSatip::ProcessArgs(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Implement command line argument processing here if applicable.
|
||||||
|
static const struct option long_options[] = {
|
||||||
|
{ "devices", required_argument, NULL, 'd' },
|
||||||
|
{ NULL, no_argument, NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
int c;
|
||||||
|
while ((c = getopt_long(argc, argv, "d:", long_options, NULL)) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'd':
|
||||||
|
deviceCountM = atoi(optarg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPluginSatip::Initialize(void)
|
||||||
|
{
|
||||||
|
debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Initialize any background activities the plugin shall perform.
|
||||||
|
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
|
||||||
|
error("Unable to initialize CURL");
|
||||||
|
SatipConfig.SetConfigDirectory(cPlugin::ResourceDirectory(PLUGIN_NAME_I18N));
|
||||||
|
cSatipDiscover::GetInstance()->Initialize();
|
||||||
|
return cSatipDevice::Initialize(deviceCountM);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPluginSatip::Start(void)
|
||||||
|
{
|
||||||
|
debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Start any background activities the plugin shall perform.
|
||||||
|
curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
|
||||||
|
cString info = cString::sprintf("Using CURL %s", data->version);
|
||||||
|
for (int i = 0; data->protocols[i]; ++i) {
|
||||||
|
// Supported protocols: HTTP(S), RTSP, FILE
|
||||||
|
if (startswith(data->protocols[i], "rtsp"))
|
||||||
|
info = cString::sprintf("%s %s", *info, data->protocols[i]);
|
||||||
|
}
|
||||||
|
info("%s", *info);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPluginSatip::Stop(void)
|
||||||
|
{
|
||||||
|
debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Stop any background activities the plugin is performing.
|
||||||
|
cSatipDevice::Shutdown();
|
||||||
|
cSatipDiscover::GetInstance()->Destroy();
|
||||||
|
curl_global_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPluginSatip::Housekeeping(void)
|
||||||
|
{
|
||||||
|
//debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Perform any cleanup or other regular tasks.
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPluginSatip::MainThreadHook(void)
|
||||||
|
{
|
||||||
|
//debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Perform actions in the context of the main program thread.
|
||||||
|
// WARNING: Use with great care - see PLUGINS.html!
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cPluginSatip::Active(void)
|
||||||
|
{
|
||||||
|
//debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Return a message string if shutdown should be postponed
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t cPluginSatip::WakeupTime(void)
|
||||||
|
{
|
||||||
|
//debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Return custom wakeup time for shutdown script
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cOsdObject *cPluginSatip::MainMenuAction(void)
|
||||||
|
{
|
||||||
|
//debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Perform the action when selected from the main VDR menu.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cMenuSetupPage *cPluginSatip::SetupMenu(void)
|
||||||
|
{
|
||||||
|
debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Return a setup menu in case the plugin supports one.
|
||||||
|
return new cSatipPluginSetup();
|
||||||
|
}
|
||||||
|
|
||||||
|
int cPluginSatip::ParseFilters(const char *valueP, int *filtersP)
|
||||||
|
{
|
||||||
|
debug("cPluginSatip::%s(%s)", __FUNCTION__, valueP);
|
||||||
|
char buffer[256];
|
||||||
|
int n = 0;
|
||||||
|
while (valueP && *valueP && (n < SECTION_FILTER_TABLE_SIZE)) {
|
||||||
|
strn0cpy(buffer, valueP, sizeof(buffer));
|
||||||
|
int i = atoi(buffer);
|
||||||
|
//debug("cPluginSatip::%s(): filters[%d]=%d", __FUNCTION__, n, i);
|
||||||
|
if (i >= 0)
|
||||||
|
filtersP[n++] = i;
|
||||||
|
if ((valueP = strchr(valueP, ' ')) != NULL)
|
||||||
|
valueP++;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPluginSatip::SetupParse(const char *nameP, const char *valueP)
|
||||||
|
{
|
||||||
|
debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
// Parse your own setup parameters and store their values.
|
||||||
|
if (!strcasecmp(nameP, "EnableEITScan"))
|
||||||
|
SatipConfig.SetEITScan(atoi(valueP));
|
||||||
|
else if (!strcasecmp(nameP, "DisabledFilters")) {
|
||||||
|
int DisabledFilters[SECTION_FILTER_TABLE_SIZE];
|
||||||
|
for (unsigned int i = 0; i < ARRAY_SIZE(DisabledFilters); ++i)
|
||||||
|
DisabledFilters[i] = -1;
|
||||||
|
unsigned int DisabledFiltersCount = ParseFilters(valueP, DisabledFilters);
|
||||||
|
for (unsigned int i = 0; i < DisabledFiltersCount; ++i)
|
||||||
|
SatipConfig.SetDisabledFilters(i, DisabledFilters[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPluginSatip::Service(const char *idP, void *dataP)
|
||||||
|
{
|
||||||
|
debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char **cPluginSatip::SVDRPHelpPages(void)
|
||||||
|
{
|
||||||
|
debug("cPluginSatip::%s()", __FUNCTION__);
|
||||||
|
static const char *HelpPages[] = {
|
||||||
|
"INFO [ <page> ] [ <card index> ]\n"
|
||||||
|
" Prints SAT>IP device information and statistics.\n"
|
||||||
|
" The output can be narrowed using optional \"page\""
|
||||||
|
" option: 1=general 2=pids 3=section filters.\n",
|
||||||
|
"MODE\n"
|
||||||
|
" Toggles between bit or byte information mode.\n",
|
||||||
|
"LIST\n"
|
||||||
|
" Lists active SAT>IP servers.\n",
|
||||||
|
"CONT\n"
|
||||||
|
" Shows SAT>IP device count.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
return HelpPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cPluginSatip::SVDRPCommand(const char *commandP, const char *optionP, int &replyCodeP)
|
||||||
|
{
|
||||||
|
debug("cPluginSatip::%s(%s, %s)", __FUNCTION__, commandP, optionP);
|
||||||
|
if (strcasecmp(commandP, "INFO") == 0) {
|
||||||
|
int index = cDevice::ActualDevice()->CardIndex();
|
||||||
|
int page = SATIP_DEVICE_INFO_ALL;
|
||||||
|
char *opt = strdup(optionP);
|
||||||
|
char *num = skipspace(opt);
|
||||||
|
char *option = num;
|
||||||
|
while (*option && !isspace(*option))
|
||||||
|
++option;
|
||||||
|
if (*option) {
|
||||||
|
*option = 0;
|
||||||
|
option = skipspace(++option);
|
||||||
|
if (isnumber(option))
|
||||||
|
index = atoi(option);
|
||||||
|
}
|
||||||
|
if (isnumber(num)) {
|
||||||
|
page = atoi(num);
|
||||||
|
if ((page < SATIP_DEVICE_INFO_ALL) || (page > SATIP_DEVICE_INFO_FILTERS))
|
||||||
|
page = SATIP_DEVICE_INFO_ALL;
|
||||||
|
}
|
||||||
|
free(opt);
|
||||||
|
cSatipDevice *device = cSatipDevice::GetSatipDevice(index);
|
||||||
|
if (device) {
|
||||||
|
return device->GetInformation(page);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
replyCodeP = 550; // Requested action not taken
|
||||||
|
return cString("SAT>IP information not available!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcasecmp(commandP, "MODE") == 0) {
|
||||||
|
unsigned int mode = !SatipConfig.GetUseBytes();
|
||||||
|
SatipConfig.SetUseBytes(mode);
|
||||||
|
return cString::sprintf("SAT>IP information mode: %s\n", mode ? "bytes" : "bits");
|
||||||
|
}
|
||||||
|
else if (strcasecmp(commandP, "LIST") == 0) {
|
||||||
|
cString list = cSatipDiscover::GetInstance()->GetServerList();
|
||||||
|
if (!isempty(list)) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
replyCodeP = 550; // Requested action not taken
|
||||||
|
return cString("No SAT>IP devices detected!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcasecmp(commandP, "CONT") == 0) {
|
||||||
|
return cString::sprintf("SAT>IP device count: %u", cSatipDevice::Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
VDRPLUGINCREATOR(cPluginSatip); // Don't touch this!
|
406
sectionfilter.c
Normal file
406
sectionfilter.c
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
/*
|
||||||
|
* sectionfilter.c: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "sectionfilter.h"
|
||||||
|
|
||||||
|
cSatipSectionFilter::cSatipSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t tidP, uint8_t maskP)
|
||||||
|
: pusiSeenM(0),
|
||||||
|
feedCcM(0),
|
||||||
|
doneqM(0),
|
||||||
|
secBufM(NULL),
|
||||||
|
secBufpM(0),
|
||||||
|
secLenM(0),
|
||||||
|
tsFeedpM(0),
|
||||||
|
pidM(pidP),
|
||||||
|
deviceIndexM(deviceIndexP)
|
||||||
|
{
|
||||||
|
//debug("cSatipSectionFilter::%s(%d, %d)", __FUNCTION__, deviceIndexM, pidM);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(secBufBaseM, 0, sizeof(secBufBaseM));
|
||||||
|
memset(filterValueM, 0, sizeof(filterValueM));
|
||||||
|
memset(filterMaskM, 0, sizeof(filterMaskM));
|
||||||
|
memset(filterModeM, 0, sizeof(filterModeM));
|
||||||
|
memset(maskAndModeM, 0, sizeof(maskAndModeM));
|
||||||
|
memset(maskAndNotModeM, 0, sizeof(maskAndNotModeM));
|
||||||
|
|
||||||
|
filterValueM[0] = tidP;
|
||||||
|
filterMaskM[0] = maskP;
|
||||||
|
|
||||||
|
// Invert the filter
|
||||||
|
for (i = 0; i < DMX_MAX_FILTER_SIZE; ++i)
|
||||||
|
filterValueM[i] ^= 0xFF;
|
||||||
|
|
||||||
|
uint8_t mask, mode, doneq = 0;
|
||||||
|
for (i = 0; i < DMX_MAX_FILTER_SIZE; ++i) {
|
||||||
|
mode = filterModeM[i];
|
||||||
|
mask = filterMaskM[i];
|
||||||
|
maskAndModeM[i] = (uint8_t)(mask & mode);
|
||||||
|
maskAndNotModeM[i] = (uint8_t)(mask & ~mode);
|
||||||
|
doneq |= maskAndNotModeM[i];
|
||||||
|
}
|
||||||
|
doneqM = doneq ? 1 : 0;
|
||||||
|
|
||||||
|
// Create sockets
|
||||||
|
socketM[0] = socketM[1] = -1;
|
||||||
|
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socketM) != 0) {
|
||||||
|
char tmp[64];
|
||||||
|
error("Opening section filter sockets failed (device=%d pid=%d): %s", deviceIndexM, pidM, strerror_r(errno, tmp, sizeof(tmp)));
|
||||||
|
}
|
||||||
|
else if ((fcntl(socketM[0], F_SETFL, O_NONBLOCK) != 0) || (fcntl(socketM[1], F_SETFL, O_NONBLOCK) != 0)) {
|
||||||
|
char tmp[64];
|
||||||
|
error("Setting section filter socket to non-blocking mode failed (device=%d pid=%d): %s", deviceIndexM, pidM, strerror_r(errno, tmp, sizeof(tmp)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipSectionFilter::~cSatipSectionFilter()
|
||||||
|
{
|
||||||
|
//debug("cSatipSectionFilter::%s(%d, %d)", __FUNCTION__, deviceIndexM, pidM);
|
||||||
|
int tmp = socketM[1];
|
||||||
|
socketM[1] = -1;
|
||||||
|
if (tmp >= 0)
|
||||||
|
close(tmp);
|
||||||
|
tmp = socketM[0];
|
||||||
|
socketM[0] = -1;
|
||||||
|
if (tmp >= 0)
|
||||||
|
close(tmp);
|
||||||
|
secBufM = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t cSatipSectionFilter::GetLength(const uint8_t *dataP)
|
||||||
|
{
|
||||||
|
return (uint16_t)(3 + ((dataP[1] & 0x0f) << 8) + dataP[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipSectionFilter::New(void)
|
||||||
|
{
|
||||||
|
tsFeedpM = secBufpM = secLenM = 0;
|
||||||
|
secBufM = secBufBaseM;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipSectionFilter::Filter(void)
|
||||||
|
{
|
||||||
|
if (secBufM) {
|
||||||
|
int i;
|
||||||
|
uint8_t neq = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < DMX_MAX_FILTER_SIZE; ++i) {
|
||||||
|
uint8_t calcxor = (uint8_t)(filterValueM[i] ^ secBufM[i]);
|
||||||
|
if (maskAndModeM[i] & calcxor)
|
||||||
|
return 0;
|
||||||
|
neq |= (maskAndNotModeM[i] & calcxor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doneqM && !neq)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// There is no data in the read socket, more can be written
|
||||||
|
if ((socketM[0] >= 0) && (socketM[1] >= 0) /*&& !select_single_desc(socketM[0], 0, false)*/) {
|
||||||
|
ssize_t len = write(socketM[1], secBufM, secLenM);
|
||||||
|
ERROR_IF(len < 0, "write()");
|
||||||
|
// Update statistics
|
||||||
|
AddSectionStatistic(len, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int cSatipSectionFilter::Feed(void)
|
||||||
|
{
|
||||||
|
if (Filter() < 0)
|
||||||
|
return -1;
|
||||||
|
secLenM = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipSectionFilter::CopyDump(const uint8_t *bufP, uint8_t lenP)
|
||||||
|
{
|
||||||
|
uint16_t limit, seclen, n;
|
||||||
|
|
||||||
|
if (tsFeedpM >= DMX_MAX_SECFEED_SIZE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (tsFeedpM + lenP > DMX_MAX_SECFEED_SIZE)
|
||||||
|
lenP = (uint8_t)(DMX_MAX_SECFEED_SIZE - tsFeedpM);
|
||||||
|
|
||||||
|
if (lenP <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memcpy(secBufBaseM + tsFeedpM, bufP, lenP);
|
||||||
|
tsFeedpM = uint16_t(tsFeedpM + lenP);
|
||||||
|
|
||||||
|
limit = tsFeedpM;
|
||||||
|
if (limit > DMX_MAX_SECFEED_SIZE)
|
||||||
|
return -1; // internal error should never happen
|
||||||
|
|
||||||
|
// Always set secbuf
|
||||||
|
secBufM = secBufBaseM + secBufpM;
|
||||||
|
|
||||||
|
for (n = 0; secBufpM + 2 < limit; ++n) {
|
||||||
|
seclen = GetLength(secBufM);
|
||||||
|
if ((seclen <= 0) || (seclen > DMX_MAX_SECTION_SIZE) || ((seclen + secBufpM) > limit))
|
||||||
|
return 0;
|
||||||
|
secLenM = seclen;
|
||||||
|
if (pusiSeenM)
|
||||||
|
Feed();
|
||||||
|
secBufpM = uint16_t(secBufpM + seclen);
|
||||||
|
secBufM += seclen;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipSectionFilter::Process(const uint8_t* dataP)
|
||||||
|
{
|
||||||
|
if (dataP[0] != TS_SYNC_BYTE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Stop if not the PID this filter is looking for
|
||||||
|
if (ts_pid(dataP) != pidM)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8_t count = payload(dataP);
|
||||||
|
|
||||||
|
// Check if no payload or out of range
|
||||||
|
if (count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Payload start
|
||||||
|
uint8_t p = (uint8_t)(TS_SIZE - count);
|
||||||
|
|
||||||
|
uint8_t cc = (uint8_t)(dataP[3] & 0x0f);
|
||||||
|
int ccok = ((feedCcM + 1) & 0x0f) == cc;
|
||||||
|
feedCcM = cc;
|
||||||
|
|
||||||
|
int dc_i = 0;
|
||||||
|
if (dataP[3] & 0x20) {
|
||||||
|
// Adaption field present, check for discontinuity_indicator
|
||||||
|
if ((dataP[4] > 0) && (dataP[5] & 0x80))
|
||||||
|
dc_i = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ccok || dc_i) {
|
||||||
|
// Discontinuity detected. Reset pusiSeenM = 0 to
|
||||||
|
// stop feeding of suspicious data until next PUSI=1 arrives
|
||||||
|
pusiSeenM = 0;
|
||||||
|
New();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataP[1] & 0x40) {
|
||||||
|
// PUSI=1 (is set), section boundary is here
|
||||||
|
if (count > 1 && dataP[p] < count) {
|
||||||
|
const uint8_t *before = &dataP[p + 1];
|
||||||
|
uint8_t before_len = dataP[p];
|
||||||
|
const uint8_t *after = &before[before_len];
|
||||||
|
uint8_t after_len = (uint8_t)(count - 1 - before_len);
|
||||||
|
CopyDump(before, before_len);
|
||||||
|
|
||||||
|
// Before start of new section, set pusiSeenM = 1
|
||||||
|
pusiSeenM = 1;
|
||||||
|
New();
|
||||||
|
CopyDump(after, after_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// PUSI=0 (is not set), no section boundary
|
||||||
|
CopyDump(&dataP[p], count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cSatipSectionFilterHandler::cSatipSectionFilterHandler(int deviceIndexP, unsigned int bufferLenP)
|
||||||
|
: cThread("SAT>IP section handler", true),
|
||||||
|
mutexM(),
|
||||||
|
deviceIndexM(deviceIndexP),
|
||||||
|
processedM(false),
|
||||||
|
ringBufferM(new cRingBufferLinear(bufferLenP, TS_SIZE, false, *cString::sprintf("SAT>IP SECTION HANDLER %d", deviceIndexP)))
|
||||||
|
{
|
||||||
|
debug("cSatipSectionFilterHandler::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
|
|
||||||
|
// Initialize filter pointers
|
||||||
|
memset(filtersM, 0, sizeof(filtersM));
|
||||||
|
|
||||||
|
// Create input buffer
|
||||||
|
if (ringBufferM) {
|
||||||
|
ringBufferM->SetTimeouts(100, 100);
|
||||||
|
ringBufferM->SetIoThrottle();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error("Failed to allocate buffer for section filter handler (device=%d): ", deviceIndexM);
|
||||||
|
|
||||||
|
Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipSectionFilterHandler::~cSatipSectionFilterHandler()
|
||||||
|
{
|
||||||
|
debug("cSatipSectionFilterHandler::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
|
Stop();
|
||||||
|
|
||||||
|
DELETE_POINTER(ringBufferM);
|
||||||
|
|
||||||
|
// Destroy all filters
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
for (int i = 0; i < eMaxSecFilterCount; ++i)
|
||||||
|
Delete(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipSectionFilterHandler::Stop(void)
|
||||||
|
{
|
||||||
|
debug("cSatipSectionFilterHandler::%s(%d): entering", __FUNCTION__, deviceIndexM);
|
||||||
|
// Stop thread
|
||||||
|
if (Running())
|
||||||
|
Cancel(3);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipSectionFilterHandler::Action(void)
|
||||||
|
{
|
||||||
|
debug("cSatipSectionFilterHandler::%s(%d): entering", __FUNCTION__, deviceIndexM);
|
||||||
|
// Do the thread loop
|
||||||
|
while (Running()) {
|
||||||
|
// Read one TS packet
|
||||||
|
if (ringBufferM) {
|
||||||
|
int len = 0;
|
||||||
|
if (processedM) {
|
||||||
|
ringBufferM->Del(TS_SIZE);
|
||||||
|
processedM = false;
|
||||||
|
}
|
||||||
|
uchar *p = ringBufferM->Get(len);
|
||||||
|
if (p && (len >= TS_SIZE)) {
|
||||||
|
if (*p != TS_SYNC_BYTE) {
|
||||||
|
for (int i = 1; i < len; ++i) {
|
||||||
|
if (p[i] == TS_SYNC_BYTE) {
|
||||||
|
len = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ringBufferM->Del(len);
|
||||||
|
debug("cSatipSectionFilterHandler::%s(%d): Skipped %d bytes to sync on TS packet", __FUNCTION__, deviceIndexM, len);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Process TS packet through all filters
|
||||||
|
mutexM.Lock();
|
||||||
|
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
||||||
|
if (filtersM[i])
|
||||||
|
filtersM[i]->Process(p);
|
||||||
|
}
|
||||||
|
mutexM.Unlock();
|
||||||
|
processedM = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cCondWait::SleepMs(10); // to avoid busy loop and reduce cpu load
|
||||||
|
}
|
||||||
|
debug("cSatipSectionFilterHandler::%s(%d): exiting", __FUNCTION__, deviceIndexM);
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipSectionFilterHandler::GetInformation(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipSectionFilterHandler::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
|
// loop through active section filters
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
cString s = "";
|
||||||
|
unsigned int count = 0;
|
||||||
|
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
||||||
|
if (filtersM[i]) {
|
||||||
|
s = cString::sprintf("%sFilter %d: %s Pid=0x%02X (%s)\n", *s, i,
|
||||||
|
*filtersM[i]->GetSectionStatistic(), filtersM[i]->GetPid(),
|
||||||
|
id_pid(filtersM[i]->GetPid()));
|
||||||
|
if (++count > SATIP_STATS_ACTIVE_FILTERS_COUNT)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipSectionFilterHandler::Delete(unsigned int indexP)
|
||||||
|
{
|
||||||
|
//debug("cSatipSectionFilterHandler::%s(%d): index=%d", __FUNCTION__, deviceIndexM, indexP);
|
||||||
|
if ((indexP < eMaxSecFilterCount) && filtersM[indexP]) {
|
||||||
|
//debug("cSatipSectionFilterHandler::%s(%d): found %d", __FUNCTION__, deviceIndexM, indexP);
|
||||||
|
cSatipSectionFilter *tmp = filtersM[indexP];
|
||||||
|
filtersM[indexP] = NULL;
|
||||||
|
delete tmp;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipSectionFilterHandler::IsBlackListed(u_short pidP, u_char tidP, u_char maskP) const
|
||||||
|
{
|
||||||
|
//debug("cSatipSectionFilterHandler::%s(%d): pid=%d tid=%02X mask=%02X", __FUNCTION__, deviceIndexM, pidP, tidP, maskP);
|
||||||
|
// loop through section filter table
|
||||||
|
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
|
||||||
|
int index = SatipConfig.GetDisabledFilters(i);
|
||||||
|
// Check if matches
|
||||||
|
if ((index >= 0) && (index < SECTION_FILTER_TABLE_SIZE) &&
|
||||||
|
(section_filter_table[index].pid == pidP) && (section_filter_table[index].tid == tidP) &&
|
||||||
|
(section_filter_table[index].mask == maskP)) {
|
||||||
|
//debug("cSatipSectionFilterHandler::%s(%d): found %s", __FUNCTION__, deviceIndexM, section_filter_table[index].description);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipSectionFilterHandler::Open(u_short pidP, u_char tidP, u_char maskP)
|
||||||
|
{
|
||||||
|
// Lock
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
// Blacklist check, refuse certain filters
|
||||||
|
if (IsBlackListed(pidP, tidP, maskP))
|
||||||
|
return -1;
|
||||||
|
// Search the next free filter slot
|
||||||
|
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
||||||
|
if (!filtersM[i]) {
|
||||||
|
filtersM[i] = new cSatipSectionFilter(deviceIndexM, pidP, tidP, maskP);
|
||||||
|
//debug("cSatipSectionFilterHandler::%s(%d): pid=%d tid=%02X mask=%02X handle=%d index=%u", __FUNCTION__, deviceIndexM, pidP, tidP, maskP, filtersM[i]->GetFd(), i);
|
||||||
|
return filtersM[i]->GetFd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No free filter slot found
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipSectionFilterHandler::Close(int handleP)
|
||||||
|
{
|
||||||
|
// Lock
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
// Search the filter for deletion
|
||||||
|
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
||||||
|
if (filtersM[i] && (handleP == filtersM[i]->GetFd())) {
|
||||||
|
//debug("cSatipSectionFilterHandler::%s(%d): pid=%d handle=%d index=%u", __FUNCTION__, deviceIndexM, filtersM[i]->GetPid(), filtersM[i]->GetFd(), i);
|
||||||
|
Delete(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipSectionFilterHandler::GetPid(int handleP)
|
||||||
|
{
|
||||||
|
// Lock
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
// Search the filter for data
|
||||||
|
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
||||||
|
if (filtersM[i] && (handleP == filtersM[i]->GetFd())) {
|
||||||
|
//debug("cSatipSectionFilterHandler::%s(%d): pid=%d handle=%d index=%u", __FUNCTION__, deviceIndexM, filtersM[i]->GetPid(), filtersM[i]->GetFd(), i);
|
||||||
|
return filtersM[i]->GetPid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipSectionFilterHandler::Write(uchar *bufferP, int lengthP)
|
||||||
|
{
|
||||||
|
//debug("cSatipSectionFilterHandler::%s(%d): length=%d", __FUNCTION__, deviceIndexM, lengthP);
|
||||||
|
// Fill up the buffer
|
||||||
|
if (ringBufferM) {
|
||||||
|
int len = ringBufferM->Put(bufferP, lengthP);
|
||||||
|
if (len != lengthP)
|
||||||
|
ringBufferM->ReportOverflow(lengthP - len);
|
||||||
|
}
|
||||||
|
}
|
91
sectionfilter.h
Normal file
91
sectionfilter.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* sectionfilter.h: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SATIP_SECTIONFILTER_H
|
||||||
|
#define __SATIP_SECTIONFILTER_H
|
||||||
|
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif // __FreeBSD__
|
||||||
|
#include <vdr/device.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "statistics.h"
|
||||||
|
|
||||||
|
class cSatipSectionFilter : public cSatipSectionStatistics {
|
||||||
|
private:
|
||||||
|
enum dmx_limits {
|
||||||
|
DMX_MAX_FILTER_SIZE = 18,
|
||||||
|
DMX_MAX_SECTION_SIZE = 4096,
|
||||||
|
DMX_MAX_SECFEED_SIZE = (DMX_MAX_SECTION_SIZE + TS_SIZE)
|
||||||
|
};
|
||||||
|
|
||||||
|
int pusiSeenM;
|
||||||
|
int feedCcM;
|
||||||
|
int doneqM;
|
||||||
|
|
||||||
|
uint8_t *secBufM;
|
||||||
|
uint8_t secBufBaseM[DMX_MAX_SECFEED_SIZE];
|
||||||
|
uint16_t secBufpM;
|
||||||
|
uint16_t secLenM;
|
||||||
|
uint16_t tsFeedpM;
|
||||||
|
uint16_t pidM;
|
||||||
|
|
||||||
|
int deviceIndexM;
|
||||||
|
int socketM[2];
|
||||||
|
|
||||||
|
uint8_t filterValueM[DMX_MAX_FILTER_SIZE];
|
||||||
|
uint8_t filterMaskM[DMX_MAX_FILTER_SIZE];
|
||||||
|
uint8_t filterModeM[DMX_MAX_FILTER_SIZE];
|
||||||
|
|
||||||
|
uint8_t maskAndModeM[DMX_MAX_FILTER_SIZE];
|
||||||
|
uint8_t maskAndNotModeM[DMX_MAX_FILTER_SIZE];
|
||||||
|
|
||||||
|
inline uint16_t GetLength(const uint8_t *dataP);
|
||||||
|
void New(void);
|
||||||
|
int Filter(void);
|
||||||
|
inline int Feed(void);
|
||||||
|
int CopyDump(const uint8_t *bufP, uint8_t lenP);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// constructor & destructor
|
||||||
|
cSatipSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t tidP, uint8_t maskP);
|
||||||
|
virtual ~cSatipSectionFilter();
|
||||||
|
void Process(const uint8_t* dataP);
|
||||||
|
int GetFd(void) { return socketM[0]; }
|
||||||
|
uint16_t GetPid(void) const { return pidM; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class cSatipSectionFilterHandler : public cThread {
|
||||||
|
private:
|
||||||
|
enum {
|
||||||
|
eMaxSecFilterCount = 32
|
||||||
|
};
|
||||||
|
cMutex mutexM;
|
||||||
|
int deviceIndexM;
|
||||||
|
bool processedM;
|
||||||
|
cRingBufferLinear *ringBufferM;
|
||||||
|
cSatipSectionFilter *filtersM[eMaxSecFilterCount];
|
||||||
|
|
||||||
|
bool Delete(unsigned int indexP);
|
||||||
|
bool IsBlackListed(u_short pidP, u_char tidP, u_char maskP) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void Action(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
cSatipSectionFilterHandler(int deviceIndexP, unsigned int bufferLenP);
|
||||||
|
virtual ~cSatipSectionFilterHandler();
|
||||||
|
bool Stop(void);
|
||||||
|
cString GetInformation(void);
|
||||||
|
int Open(u_short pidP, u_char tidP, u_char maskP);
|
||||||
|
void Close(int handleP);
|
||||||
|
int GetPid(int handleP);
|
||||||
|
void Write(u_char *bufferP, int lengthP);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SATIP_SECTIONFILTER_H
|
335
setup.c
Normal file
335
setup.c
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
/*
|
||||||
|
* setup.c: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vdr/status.h>
|
||||||
|
#include <vdr/menu.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "discover.h"
|
||||||
|
#include "setup.h"
|
||||||
|
|
||||||
|
// --- cSatipMenuScan ---------------------------------------------------------
|
||||||
|
|
||||||
|
class cSatipMenuScan : public cOsdMenu
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
enum {
|
||||||
|
INFO_TIMEOUT_MS = 2000
|
||||||
|
};
|
||||||
|
cString textM;
|
||||||
|
cTimeMs timeoutM;
|
||||||
|
|
||||||
|
public:
|
||||||
|
cSatipMenuScan(cSatipServer *serverP);
|
||||||
|
virtual ~cSatipMenuScan();
|
||||||
|
virtual void Display(void);
|
||||||
|
virtual eOSState ProcessKey(eKeys keyP);
|
||||||
|
};
|
||||||
|
|
||||||
|
cSatipMenuScan::cSatipMenuScan(cSatipServer *serverP)
|
||||||
|
: cOsdMenu(tr("SAT>IP Device")),
|
||||||
|
textM("")
|
||||||
|
{
|
||||||
|
SetMenuCategory(mcText);
|
||||||
|
if (serverP) {
|
||||||
|
if (serverP->Model())
|
||||||
|
textM = cString::sprintf("%s\nModel:\t%s", *textM, serverP->Model());
|
||||||
|
if (serverP->Address())
|
||||||
|
textM = cString::sprintf("%s\nAddress:\t%s", *textM, serverP->Address());
|
||||||
|
if (serverP->Description())
|
||||||
|
textM = cString::sprintf("%s\nDescription:\t%s", *textM, serverP->Description());
|
||||||
|
}
|
||||||
|
SetHelp(tr("Button$Scan"), NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipMenuScan::~cSatipMenuScan()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipMenuScan::Display(void)
|
||||||
|
{
|
||||||
|
cOsdMenu::Display();
|
||||||
|
DisplayMenu()->SetText(textM, true);
|
||||||
|
if (*textM)
|
||||||
|
cStatus::MsgOsdTextItem(textM);
|
||||||
|
}
|
||||||
|
|
||||||
|
eOSState cSatipMenuScan::ProcessKey(eKeys keyP)
|
||||||
|
{
|
||||||
|
eOSState state = cOsdMenu::ProcessKey(keyP);
|
||||||
|
|
||||||
|
if (state == osUnknown) {
|
||||||
|
switch (keyP) {
|
||||||
|
case kOk: return osBack;
|
||||||
|
case kRed:
|
||||||
|
default: state = osContinue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cSatipMenuInfo ---------------------------------------------------------
|
||||||
|
|
||||||
|
class cSatipServerItem : public cOsdItem {
|
||||||
|
private:
|
||||||
|
cSatipServer *serverM;
|
||||||
|
|
||||||
|
public:
|
||||||
|
cSatipServerItem(cSatipServer *serverP);
|
||||||
|
cSatipServer *Server(void) { return serverM; }
|
||||||
|
virtual void SetMenuItem(cSkinDisplayMenu *displayMenuP, int indexP, bool currentP, bool selectableP);
|
||||||
|
};
|
||||||
|
|
||||||
|
cSatipServerItem::cSatipServerItem(cSatipServer *serverP)
|
||||||
|
: serverM(serverP)
|
||||||
|
{
|
||||||
|
SetSelectable(true);
|
||||||
|
// Must begin with a '#' character!
|
||||||
|
SetText(*cString::sprintf("# %s (%s)\t%s", serverM->Address(), serverM->Model(), serverM->Description()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipServerItem::SetMenuItem(cSkinDisplayMenu *displayMenuP, int indexP, bool currentP, bool selectableP)
|
||||||
|
{
|
||||||
|
if (displayMenuP)
|
||||||
|
displayMenuP->SetItem(Text(), indexP, currentP, selectableP);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cSatipMenuInfo ---------------------------------------------------------
|
||||||
|
|
||||||
|
class cSatipMenuInfo : public cOsdMenu
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
enum {
|
||||||
|
INFO_TIMEOUT_MS = 2000
|
||||||
|
};
|
||||||
|
cString textM;
|
||||||
|
cTimeMs timeoutM;
|
||||||
|
unsigned int pageM;
|
||||||
|
void UpdateInfo();
|
||||||
|
|
||||||
|
public:
|
||||||
|
cSatipMenuInfo();
|
||||||
|
virtual ~cSatipMenuInfo();
|
||||||
|
virtual void Display(void);
|
||||||
|
virtual eOSState ProcessKey(eKeys keyP);
|
||||||
|
};
|
||||||
|
|
||||||
|
cSatipMenuInfo::cSatipMenuInfo()
|
||||||
|
: cOsdMenu(tr("SAT>IP Information")),
|
||||||
|
textM(""),
|
||||||
|
timeoutM(),
|
||||||
|
pageM(SATIP_DEVICE_INFO_GENERAL)
|
||||||
|
{
|
||||||
|
SetMenuCategory(mcText);
|
||||||
|
timeoutM.Set(INFO_TIMEOUT_MS);
|
||||||
|
UpdateInfo();
|
||||||
|
SetHelp(tr("General"), tr("Pids"), tr("Filters"), tr("Bits/bytes"));
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipMenuInfo::~cSatipMenuInfo()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipMenuInfo::UpdateInfo()
|
||||||
|
{
|
||||||
|
cSatipDevice *device = cSatipDevice::GetSatipDevice(cDevice::ActualDevice()->CardIndex());
|
||||||
|
if (device)
|
||||||
|
textM = device->GetInformation(pageM);
|
||||||
|
else
|
||||||
|
textM = cString(tr("SAT>IP information not available!"));
|
||||||
|
Display();
|
||||||
|
timeoutM.Set(INFO_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipMenuInfo::Display(void)
|
||||||
|
{
|
||||||
|
cOsdMenu::Display();
|
||||||
|
DisplayMenu()->SetText(textM, true);
|
||||||
|
if (*textM)
|
||||||
|
cStatus::MsgOsdTextItem(textM);
|
||||||
|
}
|
||||||
|
|
||||||
|
eOSState cSatipMenuInfo::ProcessKey(eKeys keyP)
|
||||||
|
{
|
||||||
|
switch (int(keyP)) {
|
||||||
|
case kUp|k_Repeat:
|
||||||
|
case kUp:
|
||||||
|
case kDown|k_Repeat:
|
||||||
|
case kDown:
|
||||||
|
case kLeft|k_Repeat:
|
||||||
|
case kLeft:
|
||||||
|
case kRight|k_Repeat:
|
||||||
|
case kRight:
|
||||||
|
DisplayMenu()->Scroll(NORMALKEY(keyP) == kUp || NORMALKEY(keyP) == kLeft, NORMALKEY(keyP) == kLeft || NORMALKEY(keyP) == kRight);
|
||||||
|
cStatus::MsgOsdTextItem(NULL, NORMALKEY(keyP) == kUp || NORMALKEY(keyP) == kLeft);
|
||||||
|
return osContinue;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
eOSState state = cOsdMenu::ProcessKey(keyP);
|
||||||
|
|
||||||
|
if (state == osUnknown) {
|
||||||
|
switch (keyP) {
|
||||||
|
case kOk: return osBack;
|
||||||
|
case kRed: pageM = SATIP_DEVICE_INFO_GENERAL;
|
||||||
|
UpdateInfo();
|
||||||
|
break;
|
||||||
|
case kGreen: pageM = SATIP_DEVICE_INFO_PIDS;
|
||||||
|
UpdateInfo();
|
||||||
|
break;
|
||||||
|
case kYellow: pageM = SATIP_DEVICE_INFO_FILTERS;
|
||||||
|
UpdateInfo();
|
||||||
|
break;
|
||||||
|
case kBlue: SatipConfig.SetUseBytes(SatipConfig.GetUseBytes() ? 0 : 1);
|
||||||
|
UpdateInfo();
|
||||||
|
break;
|
||||||
|
default: if (timeoutM.TimedOut())
|
||||||
|
UpdateInfo();
|
||||||
|
state = osContinue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cSatipPluginSetup ------------------------------------------------------
|
||||||
|
|
||||||
|
cSatipPluginSetup::cSatipPluginSetup()
|
||||||
|
: deviceCountM(0),
|
||||||
|
eitScanM(SatipConfig.GetEITScan()),
|
||||||
|
numDisabledFiltersM(SatipConfig.GetDisabledFiltersCount())
|
||||||
|
{
|
||||||
|
debug("cSatipPluginSetup::%s()", __FUNCTION__);
|
||||||
|
if (numDisabledFiltersM > SECTION_FILTER_TABLE_SIZE)
|
||||||
|
numDisabledFiltersM = SECTION_FILTER_TABLE_SIZE;
|
||||||
|
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
|
||||||
|
disabledFilterIndexesM[i] = SatipConfig.GetDisabledFilters(i);
|
||||||
|
disabledFilterNamesM[i] = tr(section_filter_table[i].description);
|
||||||
|
}
|
||||||
|
SetMenuCategory(mcSetupPlugins);
|
||||||
|
Setup();
|
||||||
|
SetHelp(NULL, NULL, NULL, trVDR("Button$Info"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipPluginSetup::Setup(void)
|
||||||
|
{
|
||||||
|
int current = Current();
|
||||||
|
|
||||||
|
Clear();
|
||||||
|
helpM.Clear();
|
||||||
|
|
||||||
|
Add(new cMenuEditBoolItem(tr("Enable EPG scanning"), &eitScanM));
|
||||||
|
helpM.Append(tr("Define whether the EPG background scanning shall be used.\n\nThis setting disables the automatic EIT scanning functionality for all SAT>IP devices."));
|
||||||
|
|
||||||
|
Add(new cMenuEditIntItem(tr("Disabled filters"), &numDisabledFiltersM, 0, SECTION_FILTER_TABLE_SIZE, tr("none")));
|
||||||
|
helpM.Append(tr("Define number of section filters to be disabled.\n\nCertain section filters might cause some unwanted behaviour to VDR such as time being falsely synchronized. By black-listing the filters here useful section data can be left intact for VDR to process."));
|
||||||
|
|
||||||
|
for (int i = 0; i < numDisabledFiltersM; ++i) {
|
||||||
|
Add(new cMenuEditStraItem(*cString::sprintf(" %s %d", tr("Filter"), i + 1), &disabledFilterIndexesM[i], SECTION_FILTER_TABLE_SIZE, disabledFilterNamesM));
|
||||||
|
helpM.Append(tr("Define an ill-behaving filter to be blacklisted."));
|
||||||
|
}
|
||||||
|
|
||||||
|
Add(new cOsdItem(tr("Active SAT>IP devices:"), osUnknown, false));
|
||||||
|
helpM.Append("");
|
||||||
|
|
||||||
|
cSatipServers *servers = cSatipDiscover::GetInstance()->GetServers();
|
||||||
|
deviceCountM = servers->Count();
|
||||||
|
for (cSatipServer *s = servers->First(); s; s = servers->Next(s)) {
|
||||||
|
Add(new cSatipServerItem(s));
|
||||||
|
helpM.Append("");
|
||||||
|
}
|
||||||
|
|
||||||
|
SetCurrent(Get(current));
|
||||||
|
Display();
|
||||||
|
}
|
||||||
|
|
||||||
|
eOSState cSatipPluginSetup::ChannelScan(void)
|
||||||
|
{
|
||||||
|
debug("cSatipPluginSetup::%s()", __FUNCTION__);
|
||||||
|
if (HasSubMenu() || Count() == 0)
|
||||||
|
return osContinue;
|
||||||
|
|
||||||
|
cSatipServerItem *item = reinterpret_cast<cSatipServerItem *>(Get(Current()));
|
||||||
|
if (item && cSatipDiscover::GetInstance()->IsValidServer(item->Server()))
|
||||||
|
return AddSubMenu(new cSatipMenuScan(item->Server()));
|
||||||
|
|
||||||
|
return osContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
eOSState cSatipPluginSetup::ShowInfo(void)
|
||||||
|
{
|
||||||
|
debug("cSatipPluginSetup::%s()", __FUNCTION__);
|
||||||
|
if (HasSubMenu() || Count() == 0)
|
||||||
|
return osContinue;
|
||||||
|
|
||||||
|
return AddSubMenu(new cSatipMenuInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
eOSState cSatipPluginSetup::ProcessKey(eKeys keyP)
|
||||||
|
{
|
||||||
|
bool hadSubMenu = HasSubMenu();
|
||||||
|
int oldNumDisabledFilters = numDisabledFiltersM;
|
||||||
|
eOSState state = cMenuSetupPage::ProcessKey(keyP);
|
||||||
|
|
||||||
|
// Ugly hack with hardcoded '#' character :(
|
||||||
|
const char *p = Get(Current())->Text();
|
||||||
|
if (!hadSubMenu && !HasSubMenu() && (*p == '#') && (keyP == kOk))
|
||||||
|
return ChannelScan();
|
||||||
|
|
||||||
|
if (state == osUnknown) {
|
||||||
|
switch (keyP) {
|
||||||
|
case kBlue: return ShowInfo();
|
||||||
|
case kInfo: if (Current() < helpM.Size())
|
||||||
|
return AddSubMenu(new cMenuText(cString::sprintf("%s - %s '%s'", tr("Help"), trVDR("Plugin"), PLUGIN_NAME_I18N), helpM[Current()]));
|
||||||
|
default: state = osContinue; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((keyP == kNone) && (cSatipDiscover::GetInstance()->GetServers()->Count() != deviceCountM))
|
||||||
|
Setup();
|
||||||
|
|
||||||
|
if ((keyP != kNone) && (numDisabledFiltersM != oldNumDisabledFilters)) {
|
||||||
|
while ((numDisabledFiltersM < oldNumDisabledFilters) && (oldNumDisabledFilters > 0))
|
||||||
|
disabledFilterIndexesM[--oldNumDisabledFilters] = -1;
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipPluginSetup::StoreFilters(const char *nameP, int *valuesP)
|
||||||
|
{
|
||||||
|
char buffer[SECTION_FILTER_TABLE_SIZE * 4];
|
||||||
|
char *q = buffer;
|
||||||
|
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
|
||||||
|
char s[3];
|
||||||
|
if (valuesP[i] < 0)
|
||||||
|
break;
|
||||||
|
if (q > buffer)
|
||||||
|
*q++ = ' ';
|
||||||
|
snprintf(s, sizeof(s), "%d", valuesP[i]);
|
||||||
|
strncpy(q, s, strlen(s));
|
||||||
|
q += strlen(s);
|
||||||
|
}
|
||||||
|
*q = 0;
|
||||||
|
debug("cSatipPluginSetup::%s(%s, %s)", __FUNCTION__, nameP, buffer);
|
||||||
|
SetupStore(nameP, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipPluginSetup::Store(void)
|
||||||
|
{
|
||||||
|
// Store values into setup.conf
|
||||||
|
SetupStore("EnableEITScan", eitScanM);
|
||||||
|
StoreFilters("DisabledFilters", disabledFilterIndexesM);
|
||||||
|
// Update global config
|
||||||
|
SatipConfig.SetEITScan(eitScanM);
|
||||||
|
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i)
|
||||||
|
SatipConfig.SetDisabledFilters(i, disabledFilterIndexesM[i]);
|
||||||
|
}
|
38
setup.h
Normal file
38
setup.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* setup.h: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SATIP_SETUP_H
|
||||||
|
#define __SATIP_SETUP_H
|
||||||
|
|
||||||
|
#include <vdr/menuitems.h>
|
||||||
|
#include <vdr/sourceparams.h>
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class cSatipPluginSetup : public cMenuSetupPage
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int deviceCountM;
|
||||||
|
int eitScanM;
|
||||||
|
int numDisabledFiltersM;
|
||||||
|
int disabledFilterIndexesM[SECTION_FILTER_TABLE_SIZE];
|
||||||
|
const char *disabledFilterNamesM[SECTION_FILTER_TABLE_SIZE];
|
||||||
|
cVector<const char*> helpM;
|
||||||
|
|
||||||
|
eOSState ChannelScan(void);
|
||||||
|
eOSState ShowInfo(void);
|
||||||
|
void Setup(void);
|
||||||
|
void StoreFilters(const char *nameP, int *valuesP);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual eOSState ProcessKey(eKeys keyP);
|
||||||
|
virtual void Store(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
cSatipPluginSetup();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SATIP_SETUP_H
|
233
socket.c
Normal file
233
socket.c
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* socket.c: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <vdr/device.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
cSatipSocket::cSatipSocket()
|
||||||
|
: socketPortM(0),
|
||||||
|
socketDescM(-1)
|
||||||
|
{
|
||||||
|
debug("cSatipSocket::%s()", __FUNCTION__);
|
||||||
|
memset(&sockAddrM, 0, sizeof(sockAddrM));
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipSocket::~cSatipSocket()
|
||||||
|
{
|
||||||
|
debug("cSatipSocket::%s()", __FUNCTION__);
|
||||||
|
// Close the socket
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipSocket::Open(const int portP)
|
||||||
|
{
|
||||||
|
debug("cSatipSocket::%s(%d)", __FUNCTION__, portP);
|
||||||
|
// Bind to the socket if it is not active already
|
||||||
|
if (socketDescM < 0) {
|
||||||
|
socklen_t len = sizeof(sockAddrM);
|
||||||
|
// Create socket
|
||||||
|
socketDescM = socket(PF_INET, SOCK_DGRAM, 0);
|
||||||
|
ERROR_IF_RET(socketDescM < 0, "socket()", return false);
|
||||||
|
// Make it use non-blocking I/O to avoid stuck read calls
|
||||||
|
ERROR_IF_FUNC(fcntl(socketDescM, F_SETFL, O_NONBLOCK), "fcntl(O_NONBLOCK)",
|
||||||
|
Close(), return false);
|
||||||
|
// Allow multiple sockets to use the same PORT number
|
||||||
|
int yes = 1;
|
||||||
|
ERROR_IF_FUNC(setsockopt(socketDescM, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0,
|
||||||
|
"setsockopt(SO_REUSEADDR)", Close(), return false);
|
||||||
|
// Bind socket
|
||||||
|
memset(&sockAddrM, 0, sizeof(sockAddrM));
|
||||||
|
sockAddrM.sin_family = AF_INET;
|
||||||
|
sockAddrM.sin_port = htons((uint16_t)(portP & 0xFFFF));
|
||||||
|
sockAddrM.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
ERROR_IF_FUNC(bind(socketDescM, (struct sockaddr *)&sockAddrM, sizeof(sockAddrM)) < 0,
|
||||||
|
"bind()", Close(), return false);
|
||||||
|
// Update socket port
|
||||||
|
ERROR_IF_FUNC(getsockname(socketDescM,(struct sockaddr*)&sockAddrM, &len) < 0,
|
||||||
|
"getsockname()", Close(), return false);
|
||||||
|
socketPortM = ntohs(sockAddrM.sin_port);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipSocket::Close(void)
|
||||||
|
{
|
||||||
|
debug("cSatipSocket::%s()", __FUNCTION__);
|
||||||
|
// Check if socket exists
|
||||||
|
if (socketDescM >= 0) {
|
||||||
|
close(socketDescM);
|
||||||
|
socketDescM = -1;
|
||||||
|
socketPortM = 0;
|
||||||
|
memset(&sockAddrM, 0, sizeof(sockAddrM));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipSocket::Flush(void)
|
||||||
|
{
|
||||||
|
debug("cSatipSocket::%s()", __FUNCTION__);
|
||||||
|
if (socketDescM < 0) {
|
||||||
|
const unsigned int len = 65535;
|
||||||
|
unsigned char *buf = MALLOC(unsigned char, len);
|
||||||
|
if (buf) {
|
||||||
|
int i = 0;
|
||||||
|
do {
|
||||||
|
// Sanity check
|
||||||
|
if (++i > 10)
|
||||||
|
break;
|
||||||
|
} while (Read(buf, len));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
|
||||||
|
{
|
||||||
|
//debug("cSatipSocket::%s()", __FUNCTION__);
|
||||||
|
// Error out if socket not initialized
|
||||||
|
if (socketDescM <= 0) {
|
||||||
|
error("Invalid socket in cSatipUdpSocket::%s()", __FUNCTION__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int len = 0;
|
||||||
|
// Read data from socket in a loop
|
||||||
|
do {
|
||||||
|
socklen_t addrlen = sizeof(sockAddrM);
|
||||||
|
struct msghdr msgh;
|
||||||
|
struct iovec iov;
|
||||||
|
char cbuf[256];
|
||||||
|
len = 0;
|
||||||
|
// Initialize iov and msgh structures
|
||||||
|
memset(&msgh, 0, sizeof(struct msghdr));
|
||||||
|
iov.iov_base = bufferAddrP;
|
||||||
|
iov.iov_len = bufferLenP;
|
||||||
|
msgh.msg_control = cbuf;
|
||||||
|
msgh.msg_controllen = sizeof(cbuf);
|
||||||
|
msgh.msg_name = &sockAddrM;
|
||||||
|
msgh.msg_namelen = addrlen;
|
||||||
|
msgh.msg_iov = &iov;
|
||||||
|
msgh.msg_iovlen = 1;
|
||||||
|
msgh.msg_flags = 0;
|
||||||
|
|
||||||
|
if (socketDescM && bufferAddrP && (bufferLenP > 0))
|
||||||
|
len = (int)recvmsg(socketDescM, &msgh, MSG_DONTWAIT);
|
||||||
|
if (len > 0)
|
||||||
|
return len;
|
||||||
|
} while (len > 0);
|
||||||
|
ERROR_IF_RET(len < 0 && errno != EAGAIN, "recvmsg()", return -1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipSocket::ReadVideo(unsigned char *bufferAddrP, unsigned int bufferLenP)
|
||||||
|
{
|
||||||
|
//debug("cSatipSocket::%s()", __FUNCTION__);
|
||||||
|
int len = Read(bufferAddrP, bufferLenP);
|
||||||
|
if (len > 0) {
|
||||||
|
if (bufferAddrP[0] == TS_SYNC_BYTE)
|
||||||
|
return len;
|
||||||
|
else if (len > 3) {
|
||||||
|
// http://tools.ietf.org/html/rfc3550
|
||||||
|
// http://tools.ietf.org/html/rfc2250
|
||||||
|
// Version
|
||||||
|
unsigned int v = (bufferAddrP[0] >> 6) & 0x03;
|
||||||
|
// Extension bit
|
||||||
|
unsigned int x = (bufferAddrP[0] >> 4) & 0x01;
|
||||||
|
// CSCR count
|
||||||
|
unsigned int cc = bufferAddrP[0] & 0x0F;
|
||||||
|
// Payload type: MPEG2 TS = 33
|
||||||
|
//unsigned int pt = bufferAddrP[1] & 0x7F;
|
||||||
|
// Header lenght
|
||||||
|
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
|
||||||
|
// Check if extension
|
||||||
|
if (x) {
|
||||||
|
// Extension header length
|
||||||
|
unsigned int ehl = (((bufferAddrP[headerlen + 2] & 0xFF) << 8) |
|
||||||
|
(bufferAddrP[headerlen + 3] & 0xFF));
|
||||||
|
// Update header length
|
||||||
|
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
// Check that rtp is version 2 and payload contains multiple of TS packet data
|
||||||
|
if ((v == 2) && (((len - headerlen) % TS_SIZE) == 0) &&
|
||||||
|
(bufferAddrP[headerlen] == TS_SYNC_BYTE)) {
|
||||||
|
// Set argument point to payload in read buffer
|
||||||
|
memmove(bufferAddrP, &bufferAddrP[headerlen], (len - headerlen));
|
||||||
|
return (len - headerlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipSocket::ReadApplication(unsigned char *bufferAddrP, unsigned int bufferLenP)
|
||||||
|
{
|
||||||
|
//debug("cSatipSocket::%s()", __FUNCTION__);
|
||||||
|
int len = Read(bufferAddrP, bufferLenP);
|
||||||
|
int offset = 0;
|
||||||
|
while (len > 0) {
|
||||||
|
// Version
|
||||||
|
unsigned int v = (bufferAddrP[offset] >> 6) & 0x03;
|
||||||
|
// Padding
|
||||||
|
//unsigned int p = (bufferAddrP[offset] >> 5) & 0x01;
|
||||||
|
// Subtype
|
||||||
|
//unsigned int st = bufferAddrP[offset] & 0x1F;
|
||||||
|
// Payload type
|
||||||
|
unsigned int pt = bufferAddrP[offset + 1] & 0xFF;
|
||||||
|
// Lenght
|
||||||
|
unsigned int length = ((bufferAddrP[offset + 2] & 0xFF) << 8) | (bufferAddrP[offset + 3] & 0xFF);
|
||||||
|
// Convert it to bytes
|
||||||
|
length = (length + 1) * 4;
|
||||||
|
// V=2, APP = 204
|
||||||
|
if ((v == 2) && (pt == 204)) {
|
||||||
|
// SSCR/CSCR
|
||||||
|
//unsigned int ssrc = ((bufferAddrP[offset + 4] & 0xFF) << 24) | ((bufferAddrP[offset + 5] & 0xFF) << 16) |
|
||||||
|
// ((bufferAddrP[offset + 6] & 0xFF) << 8) | (bufferAddrP[offset + 7] & 0xFF);
|
||||||
|
// Name
|
||||||
|
if ((bufferAddrP[offset + 8] == 'S') && (bufferAddrP[offset + 9] == 'E') &&
|
||||||
|
(bufferAddrP[offset + 10] == 'S') && (bufferAddrP[offset + 11] == '1')) {
|
||||||
|
// Identifier
|
||||||
|
//unsigned int id = ((bufferAddrP[offset + 12] & 0xFF) << 8) | (bufferAddrP[offset + 13] & 0xFF);
|
||||||
|
// String length
|
||||||
|
int string_length = ((bufferAddrP[offset + 14] & 0xFF) << 8) | (bufferAddrP[offset + 15] & 0xFF);
|
||||||
|
if (string_length > 0) {
|
||||||
|
// Set argument point to payload in read buffer
|
||||||
|
memmove(bufferAddrP, &bufferAddrP[offset + 16], string_length);
|
||||||
|
return string_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset += length;
|
||||||
|
len -= length;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipSocket::Write(const char *addrP, const unsigned char *bufferAddrP, unsigned int bufferLenP)
|
||||||
|
{
|
||||||
|
debug("cSatipSocket::%s(%s)", __FUNCTION__, addrP);
|
||||||
|
// Error out if socket not initialized
|
||||||
|
if (socketDescM <= 0) {
|
||||||
|
error("cSatipSocket::%s(): Invalid socket", __FUNCTION__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct sockaddr_in sockAddr;
|
||||||
|
memset(&sockAddr, 0, sizeof(sockAddr));
|
||||||
|
sockAddr.sin_family = AF_INET;
|
||||||
|
sockAddr.sin_port = htons((uint16_t)(socketPortM & 0xFFFF));
|
||||||
|
sockAddr.sin_addr.s_addr = inet_addr(addrP);
|
||||||
|
ERROR_IF_RET(sendto(socketDescM, bufferAddrP, bufferLenP, 0, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) < 0, "sendto()", return false);
|
||||||
|
return true;
|
||||||
|
}
|
34
socket.h
Normal file
34
socket.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* socket.h: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SATIP_SOCKET_H
|
||||||
|
#define __SATIP_SOCKET_H
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
class cSatipSocket {
|
||||||
|
private:
|
||||||
|
int socketPortM;
|
||||||
|
int socketDescM;
|
||||||
|
struct sockaddr_in sockAddrM;
|
||||||
|
|
||||||
|
public:
|
||||||
|
cSatipSocket();
|
||||||
|
~cSatipSocket();
|
||||||
|
bool Open(const int portP = 0);
|
||||||
|
void Close(void);
|
||||||
|
int Port(void) { return socketPortM; }
|
||||||
|
bool IsOpen(void) { return (socketDescM >= 0); }
|
||||||
|
bool Flush(void);
|
||||||
|
int Read(unsigned char *bufferAddrP, unsigned int bufferLenP);
|
||||||
|
int ReadVideo(unsigned char *bufferAddrP, unsigned int bufferLenP);
|
||||||
|
int ReadApplication(unsigned char *bufferAddrP, unsigned int bufferLenP);
|
||||||
|
bool Write(const char *addrP, const unsigned char *bufferAddrP, unsigned int bufferLenP);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SATIP_SOCKET_H
|
||||||
|
|
384
source.c
Normal file
384
source.c
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
/*
|
||||||
|
* source.c: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "common.h"
|
||||||
|
#include "source.h"
|
||||||
|
|
||||||
|
// --- cSatipParameterMaps ----------------------------------------------------
|
||||||
|
|
||||||
|
static const tSatipParameterMap SatipBandwidthValues[] = {
|
||||||
|
{ 5, "5 MHz", "bw=5" },
|
||||||
|
{ 6, "6 MHz", "bw=6" },
|
||||||
|
{ 7, "7 MHz", "bw=7" },
|
||||||
|
{ 8, "8 MHz", "bw=8" },
|
||||||
|
{ 10, "10 MHz", "bw=10" },
|
||||||
|
{ 1712, "1.712 MHz", "bw=1.712" },
|
||||||
|
{ -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const tSatipParameterMap SatipPilotTonesValues[] = {
|
||||||
|
{ 0, trNOOP("off"), "plts=off" },
|
||||||
|
{ 1, trNOOP("on"), "plts=on" },
|
||||||
|
{ 999, trNOOP("auto"), "" },
|
||||||
|
{ -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const tSatipParameterMap SatipSisoMisoValues[] = {
|
||||||
|
{ 0, trNOOP("SISO"), "sm=0" },
|
||||||
|
{ 1, trNOOP("MISO"), "sm=1" },
|
||||||
|
{ 999, trNOOP("auto"), "" },
|
||||||
|
{ -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const tSatipParameterMap SatipCodeRateValues[] = {
|
||||||
|
{ 0, trNOOP("none"), "" },
|
||||||
|
{ 12, "1/2", "fec=12" },
|
||||||
|
{ 23, "2/3", "fec=23" },
|
||||||
|
{ 34, "3/4", "fec=34" },
|
||||||
|
{ 35, "3/5", "fec=35" },
|
||||||
|
{ 45, "4/5", "fec=45" },
|
||||||
|
{ 56, "5/6", "fec=56" },
|
||||||
|
{ 78, "7/8", "fec=78" },
|
||||||
|
{ 89, "8/9", "fec=89" },
|
||||||
|
{ 910, "9/10", "fec=910" },
|
||||||
|
{ 999, trNOOP("auto"), "" },
|
||||||
|
{ -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const tSatipParameterMap SatipModulationValues[] = {
|
||||||
|
{ 2, "QPSK", "mtype=qpsk" },
|
||||||
|
{ 5, "8PSK", "mtype=8psk" },
|
||||||
|
{ 16, "QAM16", "mtype=16qam" },
|
||||||
|
{ 64, "QAM64", "mtype=64qam" },
|
||||||
|
{ 256, "QAM256", "mtype=256qam" },
|
||||||
|
{ 999, trNOOP("auto"), "" },
|
||||||
|
{ -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const tSatipParameterMap SatipSystemValuesSat[] = {
|
||||||
|
{ 0, "DVB-S", "msys=dvbs" },
|
||||||
|
{ 1, "DVB-S2", "msys=dvbs2" },
|
||||||
|
{ -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const tSatipParameterMap SatipSystemValuesTerr[] = {
|
||||||
|
{ 0, "DVB-T", "msys=dvbt" },
|
||||||
|
{ 1, "DVB-T2", "msys=dvbt2" },
|
||||||
|
{ -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const tSatipParameterMap SatipTransmissionValues[] = {
|
||||||
|
{ 1, "1K", "tmode=1k" },
|
||||||
|
{ 2, "2K", "tmode=2k" },
|
||||||
|
{ 4, "4K", "tmode=4k" },
|
||||||
|
{ 8, "8K", "tmode=8k" },
|
||||||
|
{ 16, "16K", "tmode=16k" },
|
||||||
|
{ 32, "32K", "tmode=32k" },
|
||||||
|
{ 999, trNOOP("auto"), "" },
|
||||||
|
{ -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const tSatipParameterMap SatipGuardValues[] = {
|
||||||
|
{ 4, "1/4", "gi=14" },
|
||||||
|
{ 8, "1/8", "gi=18" },
|
||||||
|
{ 16, "1/16", "gi=116" },
|
||||||
|
{ 32, "1/32", "gi=132" },
|
||||||
|
{ 128, "1/128", "gi=1128" },
|
||||||
|
{ 19128, "19/128", "gi=19128" },
|
||||||
|
{ 19256, "19/256", "gi=19256" },
|
||||||
|
{ 999, trNOOP("auto"), "" },
|
||||||
|
{ -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const tSatipParameterMap SatipRollOffValues[] = {
|
||||||
|
{ 0, trNOOP("auto"), "" },
|
||||||
|
{ 20, "0.20", "ro=0.20" },
|
||||||
|
{ 25, "0.25", "ro=0.25" },
|
||||||
|
{ 35, "0.35", "ro=0.35" },
|
||||||
|
{ -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static int SatipUserIndex(int valueP, const tSatipParameterMap *mapP)
|
||||||
|
{
|
||||||
|
const tSatipParameterMap *map = mapP;
|
||||||
|
while (map && map->userValue != -1) {
|
||||||
|
if (map->userValue == valueP)
|
||||||
|
return map - mapP;
|
||||||
|
map++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int SatipMapToUser(int valueP, const tSatipParameterMap *mapP, const char **strP)
|
||||||
|
{
|
||||||
|
int n = SatipUserIndex(valueP, mapP);
|
||||||
|
if (n >= 0) {
|
||||||
|
if (strP)
|
||||||
|
*strP = tr(mapP[n].userString);
|
||||||
|
return mapP[n].userValue;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cMenuEditSatipItem -----------------------------------------------------
|
||||||
|
|
||||||
|
class cMenuEditSatipItem : public cMenuEditItem {
|
||||||
|
protected:
|
||||||
|
int *valueM;
|
||||||
|
const tSatipParameterMap *mapM;
|
||||||
|
const char *zeroStringM;
|
||||||
|
virtual void Set(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
cMenuEditSatipItem(const char *nameP, int *valueP, const tSatipParameterMap *mapP, const char *zeroStringP = NULL);
|
||||||
|
virtual eOSState ProcessKey(eKeys keyP);
|
||||||
|
};
|
||||||
|
|
||||||
|
cMenuEditSatipItem::cMenuEditSatipItem(const char *nameP, int *valueP, const tSatipParameterMap *mapP, const char *zeroStringP)
|
||||||
|
: cMenuEditItem(nameP)
|
||||||
|
{
|
||||||
|
valueM = valueP;
|
||||||
|
mapM = mapP;
|
||||||
|
zeroStringM = zeroStringP;
|
||||||
|
Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cMenuEditSatipItem::Set(void)
|
||||||
|
{
|
||||||
|
const char *s = NULL;
|
||||||
|
int n = SatipMapToUser(*valueM, mapM, &s);
|
||||||
|
if (n == 0 && zeroStringM)
|
||||||
|
SetValue(zeroStringM);
|
||||||
|
else if (n >= 0) {
|
||||||
|
if (s)
|
||||||
|
SetValue(s);
|
||||||
|
else {
|
||||||
|
char buf[16];
|
||||||
|
snprintf(buf, sizeof(buf), "%d", n);
|
||||||
|
SetValue(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SetValue("???");
|
||||||
|
}
|
||||||
|
|
||||||
|
eOSState cMenuEditSatipItem::ProcessKey(eKeys keyP)
|
||||||
|
{
|
||||||
|
eOSState state = cMenuEditItem::ProcessKey(keyP);
|
||||||
|
|
||||||
|
if (state == osUnknown) {
|
||||||
|
int newValue = *valueM;
|
||||||
|
int n = SatipUserIndex(*valueM, mapM);
|
||||||
|
if (NORMALKEY(keyP) == kLeft) { // TODO might want to increase the delta if repeated quickly?
|
||||||
|
if (n-- > 0)
|
||||||
|
newValue = mapM[n].userValue;
|
||||||
|
}
|
||||||
|
else if (NORMALKEY(keyP) == kRight) {
|
||||||
|
if (mapM[++n].userValue >= 0)
|
||||||
|
newValue = mapM[n].userValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return state;
|
||||||
|
if (newValue != *valueM) {
|
||||||
|
*valueM = newValue;
|
||||||
|
Set();
|
||||||
|
}
|
||||||
|
state = osContinue;
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cSatipTransponderParameters --------------------------------------------
|
||||||
|
|
||||||
|
cSatipTransponderParameters::cSatipTransponderParameters(const char *parametersP)
|
||||||
|
: polarizationM('H'),
|
||||||
|
bandwidthM(8),
|
||||||
|
coderateHM(0),
|
||||||
|
systemM(0),
|
||||||
|
modulationM(999),
|
||||||
|
transmissionM(8),
|
||||||
|
guardM(999),
|
||||||
|
rollOffM(0),
|
||||||
|
streamIdM(0),
|
||||||
|
t2SystemIdM(0),
|
||||||
|
sisoMisoM(999),
|
||||||
|
pilotTonesM(0),
|
||||||
|
signalSourceM(1)
|
||||||
|
{
|
||||||
|
Parse(parametersP);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipTransponderParameters::PrintParameter(char *ptrP, char nameP, int valueP) const
|
||||||
|
{
|
||||||
|
return (valueP >= 0 && valueP != 999) ? sprintf(ptrP, "%c%d", nameP, valueP) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipTransponderParameters::PrintString(char *ptrP, int valueP, const tSatipParameterMap *mapP)
|
||||||
|
{
|
||||||
|
int n = SatipUserIndex(valueP, mapP);
|
||||||
|
return (n >= 0) ? sprintf(ptrP, "&%s", tr(mapP[n].satipString)) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipTransponderParameters::UrlParameters(char typeP)
|
||||||
|
{
|
||||||
|
char buffer[255];
|
||||||
|
char *q = buffer;
|
||||||
|
*q = 0;
|
||||||
|
#define ST(s) if (strchr(s, typeP) && (strchr(s, '0' + systemM + 1) || strchr(s, '*')))
|
||||||
|
ST("Z *") q += sprintf(q, "&src=%d", signalSourceM);
|
||||||
|
ST("Z *") q += sprintf(q, "&pol=%c", tolower(polarizationM));
|
||||||
|
ST(" Y2") q += sprintf(q, "&plp=%d", streamIdM);
|
||||||
|
ST(" Y2") q += sprintf(q, "&t2id=%d", t2SystemIdM);
|
||||||
|
ST(" Y*") q += PrintString(q, bandwidthM, SatipBandwidthValues);
|
||||||
|
ST(" Y*") q += PrintString(q, guardM, SatipGuardValues);
|
||||||
|
ST("ZY*") q += PrintString(q, coderateHM, SatipCodeRateValues);
|
||||||
|
ST("Z 2") q += PrintString(q, pilotTonesM, SatipPilotTonesValues);
|
||||||
|
ST("Z 2") q += PrintString(q, modulationM, SatipModulationValues);
|
||||||
|
ST(" Y*") q += PrintString(q, modulationM, SatipModulationValues);
|
||||||
|
ST("Z 2") q += PrintString(q, rollOffM, SatipRollOffValues);
|
||||||
|
ST("Z *") q += PrintString(q, systemM, SatipSystemValuesSat);
|
||||||
|
ST(" Y*") q += PrintString(q, systemM, SatipSystemValuesTerr);
|
||||||
|
ST(" Y*") q += PrintString(q, transmissionM, SatipTransmissionValues);
|
||||||
|
ST(" Y2") q += PrintString(q, sisoMisoM, SatipSisoMisoValues);
|
||||||
|
#undef ST
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipTransponderParameters::ToString(char typeP) const
|
||||||
|
{
|
||||||
|
char buffer[64];
|
||||||
|
char *q = buffer;
|
||||||
|
*q = 0;
|
||||||
|
#define ST(s) if (strchr(s, typeP) && (strchr(s, '0' + systemM + 1) || strchr(s, '*')))
|
||||||
|
ST("Z *") q += sprintf(q, "%c", polarizationM);
|
||||||
|
ST(" Y*") q += PrintParameter(q, 'B', bandwidthM);
|
||||||
|
ST("ZY*") q += PrintParameter(q, 'C', coderateHM);
|
||||||
|
ST(" Y*") q += PrintParameter(q, 'G', guardM);
|
||||||
|
ST("Z 2") q += PrintParameter(q, 'M', modulationM);
|
||||||
|
ST(" Y*") q += PrintParameter(q, 'M', modulationM);
|
||||||
|
ST("Z 2") q += PrintParameter(q, 'N', pilotTonesM);
|
||||||
|
ST("Z 2") q += PrintParameter(q, 'O', rollOffM);
|
||||||
|
ST(" Y2") q += PrintParameter(q, 'P', streamIdM);
|
||||||
|
ST(" Y2") q += PrintParameter(q, 'Q', t2SystemIdM);
|
||||||
|
ST("ZY*") q += PrintParameter(q, 'S', systemM);
|
||||||
|
ST(" Y*") q += PrintParameter(q, 'T', transmissionM);
|
||||||
|
ST(" Y2") q += PrintParameter(q, 'X', sisoMisoM);
|
||||||
|
ST("Z *") q += PrintParameter(q, 'Z', signalSourceM);
|
||||||
|
#undef ST
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cSatipTransponderParameters::ParseParameter(const char *strP, int &valueP)
|
||||||
|
{
|
||||||
|
if (*++strP) {
|
||||||
|
char *p = NULL;
|
||||||
|
errno = 0;
|
||||||
|
int n = strtol(strP, &p, 10);
|
||||||
|
if (!errno && p != strP) {
|
||||||
|
valueP = n;
|
||||||
|
if (valueP >= 0)
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error("invalid value for parameter '%c'", *(strP - 1));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipTransponderParameters::Parse(const char *strP)
|
||||||
|
{
|
||||||
|
while (strP && *strP) {
|
||||||
|
int ignoreThis;
|
||||||
|
switch (toupper(*strP)) {
|
||||||
|
case 'B': strP = ParseParameter(strP, bandwidthM); break;
|
||||||
|
case 'C': strP = ParseParameter(strP, coderateHM); break;
|
||||||
|
case 'G': strP = ParseParameter(strP, guardM); break;
|
||||||
|
case 'H': polarizationM = 'H'; strP++; break;
|
||||||
|
case 'L': polarizationM = 'L'; strP++; break;
|
||||||
|
case 'M': strP = ParseParameter(strP, modulationM); break;
|
||||||
|
case 'N': strP = ParseParameter(strP, pilotTonesM); break;
|
||||||
|
case 'O': strP = ParseParameter(strP, rollOffM); break;
|
||||||
|
case 'P': strP = ParseParameter(strP, streamIdM); break;
|
||||||
|
case 'Q': strP = ParseParameter(strP, t2SystemIdM); break;
|
||||||
|
case 'R': polarizationM = 'R'; strP++; break;
|
||||||
|
case 'S': strP = ParseParameter(strP, systemM); break;
|
||||||
|
case 'T': strP = ParseParameter(strP, transmissionM); break;
|
||||||
|
case 'V': polarizationM = 'V'; strP++; break;
|
||||||
|
case 'X': strP = ParseParameter(strP, sisoMisoM); break;
|
||||||
|
case 'Z': strP = ParseParameter(strP, signalSourceM); break;
|
||||||
|
case 'D': strP = ParseParameter(strP, ignoreThis); break; /* silently ignore coderate low priority */
|
||||||
|
case 'I': strP = ParseParameter(strP, ignoreThis); break; /* silently ignore inversion */
|
||||||
|
case 'Y': strP = ParseParameter(strP, ignoreThis); break; /* silently ignore hierarchy */
|
||||||
|
default: esyslog("ERROR: unknown parameter key '%c'", *strP);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cSatipSourceParam ------------------------------------------------------
|
||||||
|
|
||||||
|
cSatipSourceParam::cSatipSourceParam(char sourceP, const char *descriptionP)
|
||||||
|
: cSourceParam(sourceP, descriptionP),
|
||||||
|
paramM(0),
|
||||||
|
nidM(0),
|
||||||
|
tidM(0),
|
||||||
|
ridM(0),
|
||||||
|
srateM(0),
|
||||||
|
dataM(),
|
||||||
|
stpM()
|
||||||
|
{
|
||||||
|
debug("cSatipSourceParam::%s(%c, %s)", __FUNCTION__, sourceP, descriptionP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipSourceParam::SetData(cChannel *channelP)
|
||||||
|
{
|
||||||
|
debug("cSatipSourceParam::%s(%s)", __FUNCTION__, channelP->Parameters());
|
||||||
|
dataM = *channelP;
|
||||||
|
nidM = dataM.Nid();
|
||||||
|
tidM = dataM.Tid();
|
||||||
|
ridM = dataM.Rid();
|
||||||
|
srateM = dataM.Srate();
|
||||||
|
stpM.Parse(dataM.Parameters());
|
||||||
|
paramM = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipSourceParam::GetData(cChannel *channelP)
|
||||||
|
{
|
||||||
|
debug("cSatipSourceParam::%s(%s)", __FUNCTION__, channelP->Parameters());
|
||||||
|
channelP->SetTransponderData(channelP->Source(), channelP->Frequency(), srateM, stpM.ToString(Source()), true);
|
||||||
|
channelP->SetId(nidM, tidM, channelP->Sid(), ridM);
|
||||||
|
}
|
||||||
|
|
||||||
|
cOsdItem *cSatipSourceParam::GetOsdItem(void)
|
||||||
|
{
|
||||||
|
char type = Source();
|
||||||
|
const tSatipParameterMap *SatipSystemValues = type == 'Z' ? SatipSystemValuesSat : SatipSystemValuesTerr;
|
||||||
|
#define ST(s) if (strchr(s, type))
|
||||||
|
switch (paramM++) {
|
||||||
|
case 0: return new cMenuEditIntItem( tr("Nid"), &nidM, 0);
|
||||||
|
case 1: return new cMenuEditIntItem( tr("Tid"), &tidM, 0);
|
||||||
|
case 2: return new cMenuEditIntItem( tr("Rid"), &ridM, 0);
|
||||||
|
case 3: ST("Z ") return new cMenuEditIntItem( trVDR("Srate"), &srateM); else return GetOsdItem();
|
||||||
|
case 4: ST("Z ") return new cMenuEditIntItem( tr("SignalSource"), &stpM.signalSourceM, 1, 255); else return GetOsdItem();
|
||||||
|
case 5: ST("Z ") return new cMenuEditChrItem( trVDR("Polarization"), &stpM.polarizationM, "HVLR"); else return GetOsdItem();
|
||||||
|
case 6: ST("Z ") return new cMenuEditSatipItem(trVDR("Rolloff"), &stpM.rollOffM, SatipRollOffValues); else return GetOsdItem();
|
||||||
|
case 7: ST("Z ") return new cMenuEditSatipItem( tr("PilotTones"), &stpM.pilotTonesM, SatipPilotTonesValues); else return GetOsdItem();
|
||||||
|
case 8: ST("ZY") return new cMenuEditSatipItem(trVDR("System"), &stpM.systemM, SatipSystemValues); else return GetOsdItem();
|
||||||
|
case 9: ST("ZY") return new cMenuEditSatipItem(trVDR("Modulation"), &stpM.modulationM, SatipModulationValues); else return GetOsdItem();
|
||||||
|
case 10: ST("ZY") return new cMenuEditSatipItem(trVDR("CoderateH"), &stpM.coderateHM, SatipCodeRateValues); else return GetOsdItem();
|
||||||
|
case 11: ST(" Y") return new cMenuEditSatipItem(trVDR("Bandwidth"), &stpM.bandwidthM, SatipBandwidthValues); else return GetOsdItem();
|
||||||
|
case 12: ST(" Y") return new cMenuEditSatipItem(trVDR("Transmission"), &stpM.transmissionM, SatipTransmissionValues); else return GetOsdItem();
|
||||||
|
case 13: ST(" Y") return new cMenuEditSatipItem(trVDR("Guard"), &stpM.guardM, SatipGuardValues); else return GetOsdItem();
|
||||||
|
case 14: ST(" Y") return new cMenuEditIntItem( trVDR("StreamId"), &stpM.streamIdM, 0, 255); else return GetOsdItem();
|
||||||
|
case 15: ST(" Y") return new cMenuEditIntItem( tr("T2SystemId"), &stpM.t2SystemIdM, 0, 65535); else return GetOsdItem();
|
||||||
|
case 16: ST(" Y") return new cMenuEditSatipItem( tr("SISO/MISO"), &stpM.sisoMisoM, SatipSisoMisoValues); else return GetOsdItem();
|
||||||
|
default: return NULL;
|
||||||
|
}
|
||||||
|
#undef ST
|
||||||
|
return NULL;
|
||||||
|
}
|
93
source.h
Normal file
93
source.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* source.h: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SATIP_SOURCE_H
|
||||||
|
#define __SATIP_SOURCE_H
|
||||||
|
|
||||||
|
#include <vdr/menuitems.h>
|
||||||
|
#include <vdr/sourceparams.h>
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
struct tSatipParameterMap {
|
||||||
|
int userValue;
|
||||||
|
const char *userString;
|
||||||
|
const char *satipString;
|
||||||
|
};
|
||||||
|
|
||||||
|
class cSatipTransponderParameters {
|
||||||
|
friend class cSatipSourceParam;
|
||||||
|
|
||||||
|
private:
|
||||||
|
char polarizationM;
|
||||||
|
int bandwidthM;
|
||||||
|
int coderateHM;
|
||||||
|
int systemM;
|
||||||
|
int modulationM;
|
||||||
|
int transmissionM;
|
||||||
|
int guardM;
|
||||||
|
int rollOffM;
|
||||||
|
int streamIdM;
|
||||||
|
int t2SystemIdM;
|
||||||
|
int sisoMisoM;
|
||||||
|
int pilotTonesM;
|
||||||
|
int signalSourceM;
|
||||||
|
int PrintParameter(char *ptrP, char nameP, int valueP) const;
|
||||||
|
int PrintString(char *ptrP, int valueP, const tSatipParameterMap *mapP);
|
||||||
|
const char *ParseParameter(const char *strP, int &valueP);
|
||||||
|
|
||||||
|
public:
|
||||||
|
cSatipTransponderParameters(const char *parametersP = NULL);
|
||||||
|
char Polarization(void) const { return polarizationM; }
|
||||||
|
int Bandwidth(void) const { return bandwidthM; }
|
||||||
|
int CoderateH(void) const { return coderateHM; }
|
||||||
|
int System(void) const { return systemM; }
|
||||||
|
int Modulation(void) const { return modulationM; }
|
||||||
|
int Transmission(void) const { return transmissionM; }
|
||||||
|
int Guard(void) const { return guardM; }
|
||||||
|
int RollOff(void) const { return rollOffM; }
|
||||||
|
int StreamId(void) const { return streamIdM; }
|
||||||
|
int T2SystemId(void) const { return t2SystemIdM; }
|
||||||
|
int SisoMiso(void) const { return sisoMisoM; }
|
||||||
|
int PilotTones(void) const { return pilotTonesM; }
|
||||||
|
int SignalSource(void) const { return signalSourceM; }
|
||||||
|
void SetPolarization(char polarizationP) { polarizationM = polarizationP; }
|
||||||
|
void SetBandwidth(int bandwidthP) { bandwidthM = bandwidthP; }
|
||||||
|
void SetCoderateH(int coderateHP) { coderateHM = coderateHP; }
|
||||||
|
void SetSystem(int systemP) { systemM = systemP; }
|
||||||
|
void SetModulation(int modulationP) { modulationM = modulationP; }
|
||||||
|
void SetTransmission(int transmissionP) { transmissionM = transmissionP; }
|
||||||
|
void SetGuard(int guardP) { guardM = guardP; }
|
||||||
|
void SetRollOff(int rollOffP) { rollOffM = rollOffP; }
|
||||||
|
void SetStreamId(int streamIdP) { streamIdM = streamIdP; }
|
||||||
|
void SetT2SystemId(int t2SystemIdP) { t2SystemIdM = t2SystemIdP; }
|
||||||
|
void SetSisoMiso(int sisoMisoP) { sisoMisoM = sisoMisoP; }
|
||||||
|
void SetPilotTones(int pilotTonesP) { pilotTonesM = pilotTonesP; }
|
||||||
|
void SetSignalSource(int signalSourceP) { signalSourceM = signalSourceP; }
|
||||||
|
cString UrlParameters(char typeP);
|
||||||
|
cString ToString(char typeP) const;
|
||||||
|
bool Parse(const char *strP);
|
||||||
|
};
|
||||||
|
|
||||||
|
class cSatipSourceParam : public cSourceParam
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int paramM;
|
||||||
|
int nidM;
|
||||||
|
int tidM;
|
||||||
|
int ridM;
|
||||||
|
int srateM;
|
||||||
|
cChannel dataM;
|
||||||
|
cSatipTransponderParameters stpM;
|
||||||
|
|
||||||
|
public:
|
||||||
|
cSatipSourceParam(char sourceP, const char *descriptionP);
|
||||||
|
virtual void SetData(cChannel *channelP);
|
||||||
|
virtual void GetData(cChannel *channelP);
|
||||||
|
virtual cOsdItem *GetOsdItem(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SATIP_SOURCE_H
|
219
statistics.c
Normal file
219
statistics.c
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* statistics.c: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "statistics.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
// Section statistics class
|
||||||
|
cSatipSectionStatistics::cSatipSectionStatistics()
|
||||||
|
: filteredDataM(0),
|
||||||
|
numberOfCallsM(0),
|
||||||
|
timerM(),
|
||||||
|
mutexM()
|
||||||
|
{
|
||||||
|
//debug("cSatipSectionStatistics::%s()", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipSectionStatistics::~cSatipSectionStatistics()
|
||||||
|
{
|
||||||
|
//debug("cSatipSectionStatistics::%s()", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipSectionStatistics::GetSectionStatistic()
|
||||||
|
{
|
||||||
|
//debug("cSatipSectionStatistics::%s()", __FUNCTION__);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
uint64_t elapsed = timerM.Elapsed(); /* in milliseconds */
|
||||||
|
timerM.Set();
|
||||||
|
long bitrate = elapsed ? (long)(1000.0L * filteredDataM / KILOBYTE(1) / elapsed) : 0L;
|
||||||
|
if (!SatipConfig.GetUseBytes())
|
||||||
|
bitrate *= 8;
|
||||||
|
// no trailing linefeed here!
|
||||||
|
cString s = cString::sprintf("%4ld (%4ld k%s/s)", numberOfCallsM, bitrate,
|
||||||
|
SatipConfig.GetUseBytes() ? "B" : "bit");
|
||||||
|
filteredDataM = numberOfCallsM = 0;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipSectionStatistics::AddSectionStatistic(long bytesP, long callsP)
|
||||||
|
{
|
||||||
|
//debug("cSatipSectionStatistics::%s(%ld, %ld)", __FUNCTION__, bytesP, callsP);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
filteredDataM += bytesP;
|
||||||
|
numberOfCallsM += callsP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cSatipPidStatistics ----------------------------------------------------
|
||||||
|
|
||||||
|
// Device statistics class
|
||||||
|
cSatipPidStatistics::cSatipPidStatistics()
|
||||||
|
: timerM(),
|
||||||
|
mutexM()
|
||||||
|
{
|
||||||
|
debug("cSatipPidStatistics::%s()", __FUNCTION__);
|
||||||
|
const int numberOfElements = sizeof(mostActivePidsM) / sizeof(pidStruct);
|
||||||
|
for (int i = 0; i < numberOfElements; ++i) {
|
||||||
|
mostActivePidsM[i].pid = -1;
|
||||||
|
mostActivePidsM[i].dataAmount = 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipPidStatistics::~cSatipPidStatistics()
|
||||||
|
{
|
||||||
|
debug("cSatipPidStatistics::%s()", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipPidStatistics::GetPidStatistic()
|
||||||
|
{
|
||||||
|
//debug("cSatipPidStatistics::%s()", __FUNCTION__);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
const int numberOfElements = sizeof(mostActivePidsM) / sizeof(pidStruct);
|
||||||
|
uint64_t elapsed = timerM.Elapsed(); /* in milliseconds */
|
||||||
|
timerM.Set();
|
||||||
|
cString s("Active pids:\n");
|
||||||
|
for (int i = 0; i < numberOfElements; ++i) {
|
||||||
|
if (mostActivePidsM[i].pid >= 0) {
|
||||||
|
long bitrate = elapsed ? (long)(1000.0L * mostActivePidsM[i].dataAmount / KILOBYTE(1) / elapsed) : 0L;
|
||||||
|
if (!SatipConfig.GetUseBytes())
|
||||||
|
bitrate *= 8;
|
||||||
|
s = cString::sprintf("%sPid %d: %4d (%4ld k%s/s)\n", *s, i,
|
||||||
|
mostActivePidsM[i].pid, bitrate,
|
||||||
|
SatipConfig.GetUseBytes() ? "B" : "bit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < numberOfElements; ++i) {
|
||||||
|
mostActivePidsM[i].pid = -1;
|
||||||
|
mostActivePidsM[i].dataAmount = 0L;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipPidStatistics::SortPids(const void* data1P, const void* data2P)
|
||||||
|
{
|
||||||
|
//debug("cSatipPidStatistics::%s()", __FUNCTION__);
|
||||||
|
const pidStruct *comp1 = reinterpret_cast<const pidStruct*>(data1P);
|
||||||
|
const pidStruct *comp2 = reinterpret_cast<const pidStruct*>(data2P);
|
||||||
|
if (comp1->dataAmount > comp2->dataAmount)
|
||||||
|
return -1;
|
||||||
|
if (comp1->dataAmount < comp2->dataAmount)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipPidStatistics::AddPidStatistic(int pidP, long payloadP)
|
||||||
|
{
|
||||||
|
//debug("cSatipPidStatistics::%s(%ld, %ld)", __FUNCTION__, pidP, payloadP);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
const int numberOfElements = sizeof(mostActivePidsM) / sizeof(pidStruct);
|
||||||
|
// If our statistic already is in the array, update it and quit
|
||||||
|
for (int i = 0; i < numberOfElements; ++i) {
|
||||||
|
if (mostActivePidsM[i].pid == pidP) {
|
||||||
|
mostActivePidsM[i].dataAmount += payloadP;
|
||||||
|
// Now re-sort the array and quit
|
||||||
|
qsort(mostActivePidsM, numberOfElements, sizeof(pidStruct), SortPids);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Apparently our pid isn't in the array. Replace the last element with this
|
||||||
|
// one if new payload is greater
|
||||||
|
if (mostActivePidsM[numberOfElements - 1].dataAmount < payloadP) {
|
||||||
|
mostActivePidsM[numberOfElements - 1].pid = pidP;
|
||||||
|
mostActivePidsM[numberOfElements - 1].dataAmount = payloadP;
|
||||||
|
// Re-sort
|
||||||
|
qsort(mostActivePidsM, numberOfElements, sizeof(pidStruct), SortPids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cSatipTunerStatistics --------------------------------------------------
|
||||||
|
|
||||||
|
// Tuner statistics class
|
||||||
|
cSatipTunerStatistics::cSatipTunerStatistics()
|
||||||
|
: dataBytesM(0),
|
||||||
|
timerM(),
|
||||||
|
mutexM()
|
||||||
|
{
|
||||||
|
debug("cSatipTunerStatistics::%s()", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipTunerStatistics::~cSatipTunerStatistics()
|
||||||
|
{
|
||||||
|
debug("cSatipTunerStatistics::%s()", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipTunerStatistics::GetTunerStatistic()
|
||||||
|
{
|
||||||
|
//debug("cSatipTunerStatistics::%s()", __FUNCTION__);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
uint64_t elapsed = timerM.Elapsed(); /* in milliseconds */
|
||||||
|
timerM.Set();
|
||||||
|
long bitrate = elapsed ? (long)(1000.0L * dataBytesM / KILOBYTE(1) / elapsed) : 0L;
|
||||||
|
if (!SatipConfig.GetUseBytes())
|
||||||
|
bitrate *= 8;
|
||||||
|
cString s = cString::sprintf("%ld k%s/s", bitrate, SatipConfig.GetUseBytes() ? "B" : "bit");
|
||||||
|
dataBytesM = 0;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipTunerStatistics::AddTunerStatistic(long bytesP)
|
||||||
|
{
|
||||||
|
//debug("cSatipTunerStatistics::%s(%ld)", __FUNCTION__, bytesP);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
dataBytesM += bytesP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Buffer statistics class
|
||||||
|
cSatipBufferStatistics::cSatipBufferStatistics()
|
||||||
|
: dataBytesM(0),
|
||||||
|
freeSpaceM(0),
|
||||||
|
usedSpaceM(0),
|
||||||
|
timerM(),
|
||||||
|
mutexM()
|
||||||
|
{
|
||||||
|
debug("cSatipBufferStatistics::%s()", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipBufferStatistics::~cSatipBufferStatistics()
|
||||||
|
{
|
||||||
|
debug("cSatipBufferStatistics::%s()", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipBufferStatistics::GetBufferStatistic()
|
||||||
|
{
|
||||||
|
//debug("cSatipBufferStatistics::%s()", __FUNCTION__);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
uint64_t elapsed = timerM.Elapsed(); /* in milliseconds */
|
||||||
|
timerM.Set();
|
||||||
|
long bitrate = elapsed ? (long)(1000.0L * dataBytesM / KILOBYTE(1) / elapsed) : 0L;
|
||||||
|
long totalSpace = SATIP_BUFFER_SIZE;
|
||||||
|
float percentage = (float)((float)usedSpaceM / (float)totalSpace * 100.0);
|
||||||
|
long totalKilos = totalSpace / KILOBYTE(1);
|
||||||
|
long usedKilos = usedSpaceM / KILOBYTE(1);
|
||||||
|
if (!SatipConfig.GetUseBytes()) {
|
||||||
|
bitrate *= 8;
|
||||||
|
totalKilos *= 8;
|
||||||
|
usedKilos *= 8;
|
||||||
|
}
|
||||||
|
cString s = cString::sprintf("Buffer bitrate: %ld k%s/s\nBuffer usage: %ld/%ld k%s (%2.1f%%)\n", bitrate,
|
||||||
|
SatipConfig.GetUseBytes() ? "B" : "bit", usedKilos, totalKilos,
|
||||||
|
SatipConfig.GetUseBytes() ? "B" : "bit", percentage);
|
||||||
|
dataBytesM = 0;
|
||||||
|
usedSpaceM = 0;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipBufferStatistics::AddBufferStatistic(long bytesP, long usedP)
|
||||||
|
{
|
||||||
|
//debug("cSatipBufferStatistics::%s(%ld, %ld)", __FUNCTION__, bytesP, usedP);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
dataBytesM += bytesP;
|
||||||
|
if (usedP > usedSpaceM)
|
||||||
|
usedSpaceM = usedP;
|
||||||
|
}
|
87
statistics.h
Normal file
87
statistics.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* statistics.h: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SATIP_STATISTICS_H
|
||||||
|
#define __SATIP_STATISTICS_H
|
||||||
|
|
||||||
|
#include <vdr/thread.h>
|
||||||
|
|
||||||
|
// Section statistics
|
||||||
|
class cSatipSectionStatistics {
|
||||||
|
public:
|
||||||
|
cSatipSectionStatistics();
|
||||||
|
virtual ~cSatipSectionStatistics();
|
||||||
|
cString GetSectionStatistic();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void AddSectionStatistic(long bytesP, long callsP);
|
||||||
|
|
||||||
|
private:
|
||||||
|
long filteredDataM;
|
||||||
|
long numberOfCallsM;
|
||||||
|
cTimeMs timerM;
|
||||||
|
cMutex mutexM;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pid statistics
|
||||||
|
class cSatipPidStatistics {
|
||||||
|
public:
|
||||||
|
cSatipPidStatistics();
|
||||||
|
virtual ~cSatipPidStatistics();
|
||||||
|
cString GetPidStatistic();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void AddPidStatistic(int pidP, long payloadP);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct pidStruct {
|
||||||
|
int pid;
|
||||||
|
long dataAmount;
|
||||||
|
};
|
||||||
|
pidStruct mostActivePidsM[SATIP_STATS_ACTIVE_PIDS_COUNT];
|
||||||
|
cTimeMs timerM;
|
||||||
|
cMutex mutexM;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int SortPids(const void* data1P, const void* data2P);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tuner statistics
|
||||||
|
class cSatipTunerStatistics {
|
||||||
|
public:
|
||||||
|
cSatipTunerStatistics();
|
||||||
|
virtual ~cSatipTunerStatistics();
|
||||||
|
cString GetTunerStatistic();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void AddTunerStatistic(long bytesP);
|
||||||
|
|
||||||
|
private:
|
||||||
|
long dataBytesM;
|
||||||
|
cTimeMs timerM;
|
||||||
|
cMutex mutexM;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Buffer statistics
|
||||||
|
class cSatipBufferStatistics {
|
||||||
|
public:
|
||||||
|
cSatipBufferStatistics();
|
||||||
|
virtual ~cSatipBufferStatistics();
|
||||||
|
cString GetBufferStatistic();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void AddBufferStatistic(long bytesP, long usedP);
|
||||||
|
|
||||||
|
private:
|
||||||
|
long dataBytesM;
|
||||||
|
long freeSpaceM;
|
||||||
|
long usedSpaceM;
|
||||||
|
cTimeMs timerM;
|
||||||
|
cMutex mutexM;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SATIP_STATISTICS_H
|
482
tuner.c
Normal file
482
tuner.c
Normal file
@ -0,0 +1,482 @@
|
|||||||
|
/*
|
||||||
|
* tuner.c: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "tuner.h"
|
||||||
|
|
||||||
|
cSatipTuner::cSatipTuner(cSatipDeviceIf &deviceP, unsigned int packetLenP)
|
||||||
|
: cThread("SAT>IP tuner"),
|
||||||
|
sleepM(),
|
||||||
|
deviceM(&deviceP),
|
||||||
|
packetBufferLenM(packetLenP),
|
||||||
|
rtpSocketM(new cSatipSocket()),
|
||||||
|
rtcpSocketM(new cSatipSocket()),
|
||||||
|
streamAddrM(""),
|
||||||
|
streamParamM(""),
|
||||||
|
mutexM(),
|
||||||
|
handleM(NULL),
|
||||||
|
headerListM(NULL),
|
||||||
|
keepAliveM(),
|
||||||
|
pidUpdateCacheM(),
|
||||||
|
timeoutM(eKeepAliveIntervalMs),
|
||||||
|
openedM(false),
|
||||||
|
tunedM(false),
|
||||||
|
hasLockM(false),
|
||||||
|
signalStrengthM(-1),
|
||||||
|
signalQualityM(-1),
|
||||||
|
streamIdM(-1),
|
||||||
|
pidUpdatedM(false),
|
||||||
|
pidsM()
|
||||||
|
{
|
||||||
|
debug("cSatipTuner::%s(%d)", __FUNCTION__, packetBufferLenM);
|
||||||
|
// Allocate packet buffer
|
||||||
|
packetBufferM = MALLOC(unsigned char, packetBufferLenM);
|
||||||
|
if (packetBufferM)
|
||||||
|
memset(packetBufferM, 0, packetBufferLenM);
|
||||||
|
else
|
||||||
|
error("MALLOC() failed for packet buffer");
|
||||||
|
// Start thread
|
||||||
|
Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
cSatipTuner::~cSatipTuner()
|
||||||
|
{
|
||||||
|
debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
// Stop thread
|
||||||
|
sleepM.Signal();
|
||||||
|
if (Running())
|
||||||
|
Cancel(3);
|
||||||
|
Close();
|
||||||
|
// Free allocated memory
|
||||||
|
free(packetBufferM);
|
||||||
|
DELETENULL(rtcpSocketM);
|
||||||
|
DELETENULL(rtpSocketM);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cSatipTuner::HeaderCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
|
||||||
|
{
|
||||||
|
cSatipTuner *obj = reinterpret_cast<cSatipTuner *>(dataP);
|
||||||
|
size_t len = sizeP * nmembP;
|
||||||
|
//debug("cSatipTuner::%s(%zu)", __FUNCTION__, len);
|
||||||
|
|
||||||
|
int id = -1, timeout = -1;
|
||||||
|
char *s, *p = (char *)ptrP;
|
||||||
|
char *r = strtok_r(p, "\r\n", &s);
|
||||||
|
|
||||||
|
while (r) {
|
||||||
|
//debug("cSatipTuner::%s(%zu): %s", __FUNCTION__, len, r);
|
||||||
|
if (strstr(r, "com.ses.streamID")) {
|
||||||
|
if (sscanf(r, "com.ses.streamID: %11d", &id) != 1)
|
||||||
|
id = -1;
|
||||||
|
}
|
||||||
|
else if (strstr(r, "Session:")) {
|
||||||
|
int session = -1;
|
||||||
|
if (sscanf(r, "Session: %11d;timeout=%11d", &session, &timeout) != 2)
|
||||||
|
timeout = -1;
|
||||||
|
}
|
||||||
|
r = strtok_r(NULL, "\r\n", &s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= 0 && obj)
|
||||||
|
obj->SetStreamInfo(id, timeout);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipTuner::Action(void)
|
||||||
|
{
|
||||||
|
debug("cSatipTuner::%s(): entering", __FUNCTION__);
|
||||||
|
cTimeMs timeout(0);
|
||||||
|
// Increase priority
|
||||||
|
SetPriority(-1);
|
||||||
|
// Do the thread loop
|
||||||
|
while (packetBufferM && Running()) {
|
||||||
|
int length = -1;
|
||||||
|
unsigned int size = min(deviceM->CheckData(), packetBufferLenM);
|
||||||
|
if (tunedM && (size > 0)) {
|
||||||
|
// Update pids
|
||||||
|
UpdatePids();
|
||||||
|
// Remember the heart beat
|
||||||
|
KeepAlive();
|
||||||
|
// Read reception statistics
|
||||||
|
if (rtcpSocketM && rtcpSocketM->IsOpen()) {
|
||||||
|
unsigned char buf[1450];
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
if (rtcpSocketM->ReadApplication(buf, sizeof(buf)) > 0) {
|
||||||
|
ParseReceptionParameters((const char *)buf);
|
||||||
|
timeout.Set(eReConnectTimeoutMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Read data
|
||||||
|
if (rtpSocketM && rtpSocketM->IsOpen())
|
||||||
|
length = rtpSocketM->ReadVideo(packetBufferM, size);
|
||||||
|
}
|
||||||
|
if (length > 0) {
|
||||||
|
AddTunerStatistic(length);
|
||||||
|
deviceM->WriteData(packetBufferM, length);
|
||||||
|
timeout.Set(eReConnectTimeoutMs);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Reconnect if necessary
|
||||||
|
if (openedM && !tunedM && timeout.TimedOut()) {
|
||||||
|
Disconnect();
|
||||||
|
Connect();
|
||||||
|
timeout.Set(eReConnectTimeoutMs);
|
||||||
|
}
|
||||||
|
sleepM.Wait(10); // to avoid busy loop and reduce cpu load
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug("cSatipTuner::%s(): exiting", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipTuner::Open(void)
|
||||||
|
{
|
||||||
|
debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
if (Connect()) {
|
||||||
|
openedM = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipTuner::Close(void)
|
||||||
|
{
|
||||||
|
debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
openedM = false;
|
||||||
|
Disconnect();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipTuner::Connect(void)
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
|
||||||
|
// Initialize the curl session
|
||||||
|
if (!handleM)
|
||||||
|
handleM = curl_easy_init();
|
||||||
|
|
||||||
|
if (handleM && !isempty(*streamAddrM)) {
|
||||||
|
cString uri, control, transport, range;
|
||||||
|
CURLcode res = CURLE_OK;
|
||||||
|
|
||||||
|
// Just retune
|
||||||
|
if (tunedM && (streamIdM >= 0)) {
|
||||||
|
debug("cSatipTuner::%s(): retune", __FUNCTION__);
|
||||||
|
uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
|
||||||
|
SATIP_CURL_EASY_PERFORM(handleM);
|
||||||
|
if (!ValidateLatestResponse())
|
||||||
|
return false;
|
||||||
|
// Flush any old content
|
||||||
|
if (rtpSocketM)
|
||||||
|
rtpSocketM->Flush();
|
||||||
|
keepAliveM.Set(eKeepAliveIntervalMs);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Verbose output
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_VERBOSE, 1L);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Set callback
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_HEADERFUNCTION, cSatipTuner::HeaderCallback);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEHEADER, this);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
|
||||||
|
|
||||||
|
// No progress meter and no signaling
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOPROGRESS, 1L);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_NOSIGNAL, 1L);
|
||||||
|
|
||||||
|
// Set timeouts
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_TIMEOUT_MS, (long)eConnectTimeoutMs);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_CONNECTTIMEOUT_MS, (long)eConnectTimeoutMs);
|
||||||
|
|
||||||
|
// Set user-agent
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s", PLUGIN_NAME_I18N, VERSION));
|
||||||
|
|
||||||
|
// Set URL
|
||||||
|
char *p = curl_easy_unescape(handleM, *streamAddrM, 0, NULL);
|
||||||
|
streamAddrM = p;
|
||||||
|
curl_free(p);
|
||||||
|
uri = cString::sprintf("rtsp://%s/", *streamAddrM);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_URL, *uri);
|
||||||
|
|
||||||
|
// Open sockets
|
||||||
|
int i = 100;
|
||||||
|
while (i-- > 0) {
|
||||||
|
if (rtpSocketM->Open() && rtcpSocketM->Open(rtpSocketM->Port() + 1))
|
||||||
|
break;
|
||||||
|
rtpSocketM->Close();
|
||||||
|
rtcpSocketM->Close();
|
||||||
|
}
|
||||||
|
if ((rtpSocketM->Port() <= 0) || (rtcpSocketM->Port() <= 0)) {
|
||||||
|
error("Cannot open required ports!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request server options: "&pids=all" for the whole mux
|
||||||
|
uri = cString::sprintf("rtsp://%s/?%s&pids=0", *streamAddrM, *streamParamM);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
|
||||||
|
SATIP_CURL_EASY_PERFORM(handleM);
|
||||||
|
if (!ValidateLatestResponse())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Setup media stream
|
||||||
|
transport = cString::sprintf("RTP/AVP;unicast;client_port=%d-%d", rtpSocketM->Port(), rtcpSocketM->Port());
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_TRANSPORT, *transport);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
|
||||||
|
SATIP_CURL_EASY_PERFORM(handleM);
|
||||||
|
if (!ValidateLatestResponse())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Start playing
|
||||||
|
uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
|
||||||
|
SATIP_CURL_EASY_PERFORM(handleM);
|
||||||
|
if (!ValidateLatestResponse())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
keepAliveM.Set(eKeepAliveIntervalMs);
|
||||||
|
tunedM = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipTuner::Disconnect(void)
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
|
||||||
|
// Terminate curl session
|
||||||
|
if (handleM) {
|
||||||
|
// Teardown rtsp session
|
||||||
|
if (!isempty(*streamAddrM) && streamIdM >= 0) {
|
||||||
|
CURLcode res = CURLE_OK;
|
||||||
|
|
||||||
|
cString uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
|
||||||
|
SATIP_CURL_EASY_PERFORM(handleM);
|
||||||
|
ValidateLatestResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup curl stuff
|
||||||
|
if (headerListM) {
|
||||||
|
curl_slist_free_all(headerListM);
|
||||||
|
headerListM = NULL;
|
||||||
|
}
|
||||||
|
curl_easy_cleanup(handleM);
|
||||||
|
handleM = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the listening sockets
|
||||||
|
rtpSocketM->Close();
|
||||||
|
rtcpSocketM->Close();
|
||||||
|
|
||||||
|
// Reset signal parameters
|
||||||
|
hasLockM = false;
|
||||||
|
signalStrengthM = -1;
|
||||||
|
signalQualityM = -1;
|
||||||
|
|
||||||
|
tunedM = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipTuner::ValidateLatestResponse(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
if (handleM) {
|
||||||
|
long rc = 0;
|
||||||
|
CURLcode res = CURLE_OK;
|
||||||
|
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_RESPONSE_CODE, &rc);
|
||||||
|
if (rc == 200)
|
||||||
|
return true;
|
||||||
|
else if (rc != 0)
|
||||||
|
error("Tuner detected invalid status code: %ld", rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipTuner::ParseReceptionParameters(const char *paramP)
|
||||||
|
{
|
||||||
|
debug("cSatipTuner::%s(%s)", __FUNCTION__, paramP);
|
||||||
|
// DVB-S2:
|
||||||
|
// ver=<major>.<minor>;src=<srcID>;tuner=<feID>,<level>,<lock>,<quality>,<frequency>,<polarisation>,<system>,<type>,<pilots>,<roll_off>,<symbol_rate>,<fec_inner>;pids=<pid0>,...,<pidn>
|
||||||
|
// DVB-T2:
|
||||||
|
// ver=1.1;tuner=<feID>,<level>,<lock>,<quality>,<freq>,<bw>,<msys>,<tmode>,<mtype>,<gi>,<fec>,<plp>,<t2id>,<sm>;pids=<pid0>,...,<pidn>
|
||||||
|
if (!isempty(paramP)) {
|
||||||
|
char *s = strdup(paramP);
|
||||||
|
char *c = strstr(s, ";tuner=");
|
||||||
|
if (c) {
|
||||||
|
int value;
|
||||||
|
|
||||||
|
// level:
|
||||||
|
// Numerical value between 0 and 255
|
||||||
|
// An incoming L-band satellite signal of
|
||||||
|
// -25dBm corresponds to 224
|
||||||
|
// -65dBm corresponds to 32
|
||||||
|
// No signal corresponds to 0
|
||||||
|
c = strstr(c, ",");
|
||||||
|
value = atoi(++c);
|
||||||
|
// Scale value to 0-100
|
||||||
|
signalStrengthM = (value >= 0) ? (value * 100 / 255) : -1;
|
||||||
|
|
||||||
|
// lock:
|
||||||
|
// lock Set to one of the following values:
|
||||||
|
// "0" the frontend is not locked
|
||||||
|
// "1" the frontend is locked
|
||||||
|
c = strstr(c, ",");
|
||||||
|
hasLockM = atoi(++c);
|
||||||
|
|
||||||
|
// quality:
|
||||||
|
// Numerical value between 0 and 15
|
||||||
|
// Lowest value corresponds to highest error rate
|
||||||
|
// The value 15 shall correspond to
|
||||||
|
// -a BER lower than 2x10-4 after Viterbi for DVB-S
|
||||||
|
// -a PER lower than 10-7 for DVB-S2
|
||||||
|
c = strstr(c, ",");
|
||||||
|
value = atoi(++c);
|
||||||
|
// Scale value to 0-100
|
||||||
|
signalQualityM = (hasLockM && (value >= 0)) ? (value * 100 / 15) : 0;
|
||||||
|
}
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSatipTuner::SetStreamInfo(int idP, int timeoutP)
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
debug("cSatipTuner::%s(%d, %d)", __FUNCTION__, idP, timeoutP);
|
||||||
|
streamIdM = idP;
|
||||||
|
timeoutM = timeoutP > 0 ? timeoutP * 1000L : eKeepAliveIntervalMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipTuner::SetSource(const char* addressP, const char *parameterP, const int indexP)
|
||||||
|
{
|
||||||
|
debug("cSatipTuner::%s(%s, %s, %d)", __FUNCTION__, addressP, parameterP, indexP);
|
||||||
|
if (!isempty(addressP) && !isempty(parameterP)) {
|
||||||
|
// Update stream address and parameter
|
||||||
|
streamAddrM = addressP;
|
||||||
|
streamParamM = parameterP;
|
||||||
|
// Reconnect
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipTuner::SetPid(int pidP, int typeP, bool onP)
|
||||||
|
{
|
||||||
|
debug("cSatipTuner::%s(%d, %d, %d)", __FUNCTION__, pidP, typeP, onP);
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; i < pidsM.Size(); ++i) {
|
||||||
|
if (pidsM[i] == pidP) {
|
||||||
|
found = true;
|
||||||
|
if (!onP) {
|
||||||
|
pidsM.Remove(i);
|
||||||
|
pidUpdatedM = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (onP && !found) {
|
||||||
|
pidsM.Append(pidP);
|
||||||
|
pidUpdatedM = true;
|
||||||
|
}
|
||||||
|
pidUpdateCacheM.Set(ePidUpdateIntervalMs);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipTuner::UpdatePids(void)
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
if (pidUpdateCacheM.TimedOut() && pidUpdatedM && pidsM.Size() && tunedM && handleM && !isempty(*streamAddrM) && (streamIdM > 0)) {
|
||||||
|
debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
CURLcode res = CURLE_OK;
|
||||||
|
//cString uri = cString::sprintf("rtsp://%s/stream=%d?%spids=%d", *streamAddrM, streamIdM, onP ? "add" : "del", pidP);
|
||||||
|
cString uri = cString::sprintf("rtsp://%s/stream=%d?pids=", *streamAddrM, streamIdM);
|
||||||
|
|
||||||
|
for (int i = 0; i < pidsM.Size(); ++i)
|
||||||
|
uri = cString::sprintf("%s%d%s", *uri, pidsM[i], (i == (pidsM.Size() - 1)) ? "" : ",");
|
||||||
|
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
|
||||||
|
SATIP_CURL_EASY_PERFORM(handleM);
|
||||||
|
if (ValidateLatestResponse()) {
|
||||||
|
keepAliveM.Set(eKeepAliveIntervalMs);
|
||||||
|
pidUpdatedM = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Disconnect();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipTuner::KeepAlive(void)
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
if (tunedM && handleM && keepAliveM.TimedOut()) {
|
||||||
|
debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
CURLcode res = CURLE_OK;
|
||||||
|
cString uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
|
||||||
|
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
|
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
|
||||||
|
SATIP_CURL_EASY_PERFORM(handleM);
|
||||||
|
if (ValidateLatestResponse())
|
||||||
|
keepAliveM.Set(eKeepAliveIntervalMs);
|
||||||
|
else
|
||||||
|
Disconnect();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipTuner::SignalStrength(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
return signalStrengthM;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cSatipTuner::SignalQuality(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
return signalQualityM;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSatipTuner::HasLock(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
return tunedM && hasLockM;
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipTuner::GetSignalStatus(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
return cString::sprintf("lock=%d strength=%d quality=%d", HasLock(), SignalStrength(), SignalQuality());
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cSatipTuner::GetInformation(void)
|
||||||
|
{
|
||||||
|
//debug("cSatipTuner::%s()", __FUNCTION__);
|
||||||
|
return tunedM ? cString::sprintf("rtsp://%s/?%s [stream=%d]", *streamAddrM, *streamParamM, streamIdM) : "connection failed";
|
||||||
|
}
|
87
tuner.h
Normal file
87
tuner.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* tuner.h: SAT>IP plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SATIP_TUNER_H
|
||||||
|
#define __SATIP_TUNER_H
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <curl/easy.h>
|
||||||
|
|
||||||
|
#ifndef CURLOPT_RTSPHEADER
|
||||||
|
#error "libcurl is missing required RTSP support"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <vdr/thread.h>
|
||||||
|
#include <vdr/tools.h>
|
||||||
|
|
||||||
|
#include "deviceif.h"
|
||||||
|
#include "statistics.h"
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
class cSatipTuner : public cThread, public cSatipTunerStatistics {
|
||||||
|
private:
|
||||||
|
enum {
|
||||||
|
eConnectTimeoutMs = 1500, // in milliseconds
|
||||||
|
ePidUpdateIntervalMs = 100, // in milliseconds
|
||||||
|
eReConnectTimeoutMs = 5000, // in milliseconds
|
||||||
|
eKeepAliveIntervalMs = 600000 // in milliseconds
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t HeaderCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP);
|
||||||
|
|
||||||
|
cCondWait sleepM;
|
||||||
|
cSatipDeviceIf* deviceM;
|
||||||
|
unsigned char* packetBufferM;
|
||||||
|
unsigned int packetBufferLenM;
|
||||||
|
cSatipSocket *rtpSocketM;
|
||||||
|
cSatipSocket *rtcpSocketM;
|
||||||
|
cString streamAddrM;
|
||||||
|
cString streamParamM;
|
||||||
|
cMutex mutexM;
|
||||||
|
CURL *handleM;
|
||||||
|
struct curl_slist *headerListM;
|
||||||
|
cTimeMs keepAliveM;
|
||||||
|
cTimeMs signalInfoCacheM;
|
||||||
|
cTimeMs pidUpdateCacheM;
|
||||||
|
int timeoutM;
|
||||||
|
bool openedM;
|
||||||
|
bool tunedM;
|
||||||
|
bool hasLockM;
|
||||||
|
int signalStrengthM;
|
||||||
|
int signalQualityM;
|
||||||
|
int streamIdM;
|
||||||
|
bool pidUpdatedM;
|
||||||
|
cVector<int> pidsM;
|
||||||
|
|
||||||
|
bool Connect(void);
|
||||||
|
bool Disconnect(void);
|
||||||
|
bool ValidateLatestResponse(void);
|
||||||
|
void ParseReceptionParameters(const char *paramP);
|
||||||
|
void SetStreamInfo(int idP, int timeoutP);
|
||||||
|
bool KeepAlive(void);
|
||||||
|
bool UpdateSignalInfoCache(void);
|
||||||
|
bool UpdatePids(void);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void Action(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
cSatipTuner(cSatipDeviceIf &deviceP, unsigned int packetLenP);
|
||||||
|
virtual ~cSatipTuner();
|
||||||
|
bool IsTuned(void) const { return tunedM; }
|
||||||
|
bool SetSource(const char* addressP, const char *parameterP, const int indexP);
|
||||||
|
bool SetPid(int pidP, int typeP, bool onP);
|
||||||
|
bool Open(void);
|
||||||
|
bool Close(void);
|
||||||
|
int SignalStrength(void);
|
||||||
|
int SignalQuality(void);
|
||||||
|
bool HasLock(void);
|
||||||
|
cString GetSignalStatus(void);
|
||||||
|
cString GetInformation(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SATIP_TUNER_H
|
Loading…
Reference in New Issue
Block a user