Initial revision.

This commit is contained in:
Rolf Ahrenberg 2014-03-08 13:07:47 +02:00
commit 476f8be52b
29 changed files with 4996 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.dependencies
*.o
*.so
*~
po/*.pot
po/*.mo

340
COPYING Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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