1
0
mirror of https://github.com/rofafor/vdr-plugin-satip.git synced 2023-10-10 11:37:42 +00:00

60 Commits

Author SHA1 Message Date
Rolf Ahrenberg
20e9dc99f9 Updated HISTORY. 2016-12-18 17:25:24 +02:00
Rolf Ahrenberg
d26658a22e Prefer section pids. 2016-12-16 08:17:41 +02:00
Rolf Ahrenberg
a5d57e9390 Add a preliminary RTP-over-TCP support. 2016-12-15 23:47:30 +02:00
Frank Neumann
af64cb3011 Update German translations. 2016-12-15 08:10:48 +02:00
Rolf Ahrenberg
48862f99d3 Add a preliminary multicast support. 2016-12-14 00:37:36 +02:00
Rolf Ahrenberg
6ed729c153 Add a new ForcePilot quirk. 2016-11-10 16:47:30 +02:00
Rolf Ahrenberg
0fc044a316 Update README. 2016-11-10 16:47:10 +02:00
Rolf Ahrenberg
94b7f1132f Add command-line support for setting server quirks. 2016-10-09 18:53:40 +03:00
Rolf Ahrenberg
e7c9b04ad2 Add preliminary support for DVBViewer CI. 2016-09-11 13:08:20 +03:00
Rolf Ahrenberg
9c91e01a87 Handle Out-of-Range responses as a normal operation. 2016-09-09 19:59:12 +03:00
Rolf Ahrenberg
7a84ba78c8 Check source validity also in server assign (Thanks to Patrick Boettcher). 2016-07-31 12:00:55 +03:00
Rolf Ahrenberg
c2fe2b748d Add support for source filtering. 2016-07-30 13:42:44 +03:00
Rolf Ahrenberg
13a6b5938f Fix command-line examples to match defined quirks. 2016-07-23 17:37:07 +03:00
Rolf Ahrenberg
5db9f93a11 Fix active device check. 2016-06-23 15:36:51 +03:00
Rolf Ahrenberg
cede4743cb Add support for activating/deactivating server on-the-fly. 2016-06-22 22:49:04 +03:00
Rolf Ahrenberg
4e9b6f11eb Add support for RTP-over-TCP. 2016-06-21 23:02:46 +03:00
Rolf Ahrenberg
4b1892d754 Change EINTR handling in poller. 2016-06-05 14:19:46 +03:00
Rolf Ahrenberg
473e016152 Use 2MB ringbuffer per device. 2016-05-12 23:46:27 +03:00
Rolf Ahrenberg
793aab17d7 Set SO_REUSEPORT only if it's defined. 2016-05-07 21:43:49 +03:00
Rolf Ahrenberg
8222d05f5d Re-enable to reuse address for msearch protocol. 2016-04-11 23:02:12 +03:00
Rolf Ahrenberg
9d7c745fe1 Reset device name when the device is idling. 2016-03-19 20:33:00 +02:00
Rolf Ahrenberg
c8a5245b6c Fix statistics output a bit more. 2016-02-28 18:23:31 +02:00
Rolf Ahrenberg
ff459f426e Fix statistics output. 2016-02-28 15:32:28 +02:00
Rolf Ahrenberg
4c216d81c8 Added support for X-SATIP-RTSP-Port header. 2016-02-27 17:28:13 +02:00
Rolf Ahrenberg
23e2b4d54d Merge pull request #18 from e-tobi/master
C++11 requires a mandatory space when concatenating string literals
2016-01-30 21:43:42 +02:00
Rolf Ahrenberg
954e1be6b3 Add a missing device name update. 2016-01-30 21:41:10 +02:00
Tobias Grimm
e46340f5f3 C++11 requires a mandatory space when concatenating string literals 2016-01-23 18:38:44 +01:00
Rolf Ahrenberg
d5e9b1050e Merge pull request #16 from chriszero/master
Make it possible to specify the rtp and rtcp ports
2016-01-08 20:22:51 +02:00
chriszero
6e9b5fc414 Make it possible to specify the rtp and rtcp ports
this makes it possible to use the satip through a
NAT (e.g. a docker bridged network)
2016-01-07 21:09:39 +01:00
Rolf Ahrenberg
6e9b43b0d8 Reorder also terrestrial and cable query parameters as introduced in the satip specification 1.2.2, although the ordering shouldn't matter according to it. 2015-12-05 20:23:32 +02:00
Rolf Ahrenberg
97aba6db0f Merge pull request #14 from e-tobi/master
Reorderd the transponder URL parameter in a way the Panasonic CXW804 …
2015-12-05 20:13:50 +02:00
Tobias Grimm
660c48a9f4 Reorderd the transponder URL parameter in a way the Panasonic CXW804 expects them
(src/freq/pol/ro/msys/mtype/plts/sr/fec)

This only applies to DVB-S. DVB-C and DVB-T might need further tweaking.
It's actually a bug of the Panasonic TV to expect the transponder parameters in
a specific order in the query string, but for now this seems to be the most
pragmatic workaround.
2015-12-05 17:11:44 +01:00
Rolf Ahrenberg
67b6c9f4f7 Removed support for older version than vdr-2.3.1. 2015-09-18 18:58:07 +03:00
Rolf Ahrenberg
37a0572a64 Updated HISTORY. 2015-09-18 18:49:44 +03:00
Rolf Ahrenberg
613c0aed7c Added a force lock quirk for Schwaiger MS41IP. 2015-09-18 18:31:53 +03:00
Rolf Ahrenberg
c3ad29eb27 Updated for vdr-2.3.1 (Thanks to Klaus Schmidinger). 2015-08-20 19:12:21 +03:00
Rolf Ahrenberg
e90b8651e6 Silenced some cppcheck warnings. 2015-08-16 01:38:03 +03:00
Rolf Ahrenberg
0f370aa36a Added a tuning timeout for better recovery. 2015-08-16 01:10:52 +03:00
Rolf Ahrenberg
49e2dd1fc1 Reset the RTSP connection after any failed connect. 2015-06-04 18:51:09 +03:00
Rolf Ahrenberg
ba0b808ec4 Enabled the X_PMT extension for minisatip servers. 2015-05-26 21:18:05 +03:00
Rolf Ahrenberg
4e2535a7e2 Added a double check for the idle release. 2015-05-23 15:28:37 +03:00
Rolf Ahrenberg
6384b8694e Added a timeout for releasing idling devices. 2015-05-22 22:43:08 +03:00
Rolf Ahrenberg
6c4c8a10b7 Set the default device count to two. 2015-04-26 12:49:40 +03:00
Rolf Ahrenberg
8b43cdc634 Added initial support for detaching and attaching SAT>IP servers. 2015-04-12 21:25:41 +03:00
Rolf Ahrenberg
fe010ab72c Added a more flexible OPER command in the SVDRP interface. 2015-04-09 21:36:13 +03:00
Rolf Ahrenberg
7bdc152f76 Prepared for a release. 2015-04-03 20:37:29 +03:00
Rolf Ahrenberg
bd6774ba28 Relaxed the server cleanup timeout. 2015-03-26 00:28:17 +02:00
Rolf Ahrenberg
fbf7977853 Added a memory guard for cSatipMemoryBuffer(). 2015-03-26 00:23:55 +02:00
Rolf Ahrenberg
19a6a4a5ee Simplified GetTransponderUrlParameters(). 2015-03-22 23:44:15 +02:00
Rolf Ahrenberg
7b683dba8d Implemented cSatipMemoryBuffer(). 2015-03-22 20:06:47 +02:00
Rolf Ahrenberg
942d3a936e Fixed CURL buffer size for discovery. 2015-03-22 17:33:08 +02:00
Rolf Ahrenberg
748ea15d1d Updated against SAT>IP protocol specification version 1.2.2. 2015-03-22 16:47:02 +02:00
Rolf Ahrenberg
1d75da403a Simplified GetTransponderUrlParameters(). 2015-03-22 16:33:48 +02:00
Rolf Ahrenberg
4d8263e8fd Improved RTSP error checking. 2015-03-18 22:59:38 +02:00
Rolf Ahrenberg
7019b719a5 Fixed a memory leak in TinyXML implementation (Thanks to Oliver Endriss). 2015-03-18 20:55:10 +02:00
Rolf Ahrenberg
39249ca2d5 Fixed generic error macros. 2015-03-08 15:53:54 +02:00
Rolf Ahrenberg
68e0d1474e Got rid of SATIP_DEBUG. 2015-03-07 17:50:48 +02:00
Rolf Ahrenberg
ad5c221e44 Fixed wrong indenting. 2015-03-04 19:43:01 +02:00
Rolf Ahrenberg
826e53e8ea Fixed the discovery tracing level. 2015-03-03 19:09:51 +02:00
Rolf Ahrenberg
f4dd02a9aa Robustify the server discovery. 2015-03-02 20:08:21 +02:00
41 changed files with 1720 additions and 339 deletions

54
HISTORY
View File

@@ -120,3 +120,57 @@ VDR Plugin 'satip' Revision History
- Fixed memory deallocation errors.
- Cleaned up all scan-build warnings.
- Refactored the frontend handling.
2015-04-04: Version 2.2.1
- Improved RTSP error checking.
- Got rid of SATIP_DEBUG.
- Robustify the server discovery.
- Fixed a memory leak in TinyXML implementation
(Thanks to Oliver Endriss).
- Updated against SAT>IP protocol specification
version 1.2.2.
2015-04-26: Version 2.2.2
- Added a more flexible OPER command in the SVDRP
interface.
- Added new ATTA and DETA SVDRP commands.
- Set the default device count to two.
2015-09-18: Version 2.2.3
- Added a timeout for releasing idling devices.
- Reset the RTSP connection after any failed connect.
- Added tweaks for minisatip and Schwaiger MS41IP.
- Updated for vdr-2.3.1 (Thanks to Klaus Schmidinger).
2016-12-18: Version 2.2.4
- Updated German translation (Thanks to Frank Neumann).
- Fixed Panasonic CXW804 support (Thanks to Tobias Grimm).
- Fixed C++11 support (Thanks to Tobias Grimm).
- Fixed server assigment with source validation (Thanks to Patrick Boettcher).
- Added configurable RTP/RTCP ports (Thanks to chriszero).
- Added support for X-SATIP-RTSP-Port header.
- Added multicast and RTP-over-TCP support.
- Added support for activating/deactivating server on-the-fly.
- Extended command-line parameters for setting server quirks.
===================================
VDR Plugin 'satip' Revision History
===================================
2016-12-18: Version 2.3.0
- Updated for vdr-2.3.1.
- Updated German translation (Thanks to Frank Neumann).
- Fixed Panasonic CXW804 support (Thanks to Tobias Grimm).
- Fixed C++11 support (Thanks to Tobias Grimm).
- Fixed server assigment with source validation (Thanks to Patrick Boettcher).
- Added configurable RTP/RTCP ports (Thanks to chriszero).
- Added support for X-SATIP-RTSP-Port header.
- Added multicast and RTP-over-TCP support.
- Added support for activating/deactivating server on-the-fly.
- Extended command-line parameters for setting server quirks.

View File

@@ -2,18 +2,10 @@
# Makefile for SAT>IP plugin
#
# Debugging on/off
#SATIP_DEBUG = 1
# Use TinyXML instead of PugiXML
#SATIP_USE_TINYXML = 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.
@@ -40,6 +32,7 @@ TMPDIR ?= /tmp
export CFLAGS = $(call PKGCFG,cflags)
export CXXFLAGS = $(call PKGCFG,cxxflags)
STRIP ?= /bin/true
### The version number of VDR's plugin API:
@@ -75,12 +68,6 @@ else
LIBS += -lpugixml
endif
ifdef SATIP_DEBUG
ifeq ($(SATIP_DEBUG),1)
DEFINES += -DDEBUG
endif
endif
ifneq ($(strip $(GITTAG)),)
DEFINES += -DGITVERSION='"-GIT-$(GITTAG)"'
endif
@@ -142,9 +129,7 @@ install-i18n: $(I18Nmsgs)
$(SOFILE): $(OBJS)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@
ifndef SATIP_DEBUG
@$(STRIP) $@
endif
install-lib: $(SOFILE)
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)

22
README
View File

@@ -42,7 +42,7 @@ make -C satip-X.Y.Z install
Configuration:
The plugin accepts a "--devices" (-d) command-line parameter defaulting
to one. This parameter defines how many simultaneous transponders can
to two. This parameter defines how many simultaneous transponders can
be received, if there are available SAT>IP tuners.
The plugin accepts also a "--server" (-s) command-line parameter, that
@@ -52,9 +52,15 @@ separated list of "<ipaddress>|<model>|<description>" entries. The model
consists of a DVB system (DVBS2,DVBT2,DVBT,DVBC) and number of available
frontends separated by a hyphen:
vdr -P 'satip -s <ipaddress>|<model>|<description>;...'
vdr -P 'satip -s 192.168.0.1|DVBS2-2,DVBT2-2|Octo1'
vdr -P 'satip -s 192.168.0.1|DVBS2-4|Octo1;192.168.0.2|DVBT2-4|Octo2'
vdr -P 'satip -s <ipaddress>[:<port>]|<model>[:<filter>]|<description>[:<quirk>];...'
vdr -P 'satip -s 192.168.0.1|DVBS2-2,DVBT2-2|OctopusNet'
vdr -P 'satip -s 192.168.0.1|DVBS2-4|OctopusNet;192.168.0.2|DVBT2-4|minisatip:0x18'
vdr -P 'satip -s 192.168.0.1:554|DVBS2-2:S19.2E|OctopusNet;192.168.0.2:8554|DVBS2-4:S19.2E,S1W|minisatip'
The plugin accepts a "--portrange" (-p) command-line parameter, that can
be used to manually specify the RTP & RTCP port range and therefore
enables using the plugin through a NAT (e.g. Docker bridged network).
A minimum of 2 ports per device is required.
SAT>IP satellite positions (aka. signal sources) shall be defined via
sources.conf. If the source description begins with a number, it's used
@@ -110,6 +116,11 @@ Setup menu:
"Disable filter" options which allow you
to disable the individual section filters.
Valid range: "none" = 0 ... 7
- Transport mode = unicast If you want to use the non-standard
multicast RTP-over-TCP transport mode, set this option
rtp-o-tcp accordingly. Otherwise, the transport
mode will be RTP-over-UDP via unicast or
multicast.
- [Red:Scan] Forces network scanning of SAT>IP hardware.
- [Yellow:Devices] Opens SAT>IP device status menu.
- [Blue:Info] Opens SAT>IP information/statistics menu.
@@ -125,6 +136,9 @@ Information menu:
Notes:
- If you are having problems receiving DVB-S2 channels, make sure your
channels.conf entry contains correct pilot tone setting.
- The stream id "-1" states about unsuccessful tuning. This might be a
result of invalid channel parameters or lack of free SAT>IP tuners.

View File

@@ -13,9 +13,11 @@
#include <vdr/config.h>
#include <vdr/i18n.h>
#define SATIP_DEFAULT_RTSP_PORT 554
#define SATIP_MAX_DEVICES MAXDEVICES
#define SATIP_BUFFER_SIZE KILOBYTE(1024)
#define SATIP_BUFFER_SIZE KILOBYTE(2048)
#define SATIP_DEVICE_INFO_ALL 0
#define SATIP_DEVICE_INFO_GENERAL 1
@@ -35,7 +37,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); \
esyslog("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) \
@@ -48,15 +50,15 @@
esyslog("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]; \
esyslog("[%s,%d]: "errstr": %s", __FILE__, __LINE__, \
strerror_r(errno, tmp, sizeof(tmp))); \
func; \
ret; \
} \
#define ERROR_IF_FUNC(exp, errstr, func, ret) \
do { \
if (exp) { \
char tmp[64]; \
esyslog("[%s,%d]: " errstr ": %s", __FILE__, __LINE__, \
strerror_r(errno, tmp, sizeof(tmp))); \
func; \
ret; \
} \
} while (0)
@@ -84,6 +86,52 @@
#define ELEMENTS(x) (sizeof(x) / sizeof(x[0]))
class cSatipMemoryBuffer {
private:
enum {
eMaxDataSize = MEGABYTE(2)
};
char *dataM;
size_t sizeM;
void *AllocBuffer(void *ptrP, size_t sizeP)
{
// There might be a realloc() out there that doesn't like reallocing NULL pointers, so we take care of it here
if (ptrP)
return realloc(ptrP, sizeP);
else
return malloc(sizeP);
}
// to prevent copy constructor and assignment
cSatipMemoryBuffer(const cSatipMemoryBuffer&);
cSatipMemoryBuffer& operator=(const cSatipMemoryBuffer&);
public:
cSatipMemoryBuffer() : dataM(NULL), sizeM(0) {}
~cSatipMemoryBuffer() { Reset(); }
size_t Add(char *dataP, size_t sizeP)
{
if (sizeP > 0) {
size_t len = sizeM + sizeP + 1;
if (len < eMaxDataSize) {
dataM = (char *)AllocBuffer(dataM, len);
if (dataM) {
memcpy(&(dataM[sizeM]), dataP, sizeP);
sizeM += sizeP;
dataM[sizeM] = 0;
return sizeP;
}
else
esyslog("[%s,%d]: Failed to allocate memory", __FILE__, __LINE__);
}
else
esyslog("[%s,%d]: Buffer overflow", __FILE__, __LINE__);
}
return 0;
};
char *Data(void) { return dataM; }
size_t Size(void) { return sizeM; }
void Reset(void) { FREE_POINTER(dataM); sizeM = 0; };
};
uint16_t ts_pid(const uint8_t *bufP);
uint8_t payload(const uint8_t *bufP);
const char *id_pid(const u_short pidP);

View File

@@ -17,6 +17,10 @@ cSatipConfig::cSatipConfig(void)
ciExtensionM(0),
eitScanM(1),
useBytesM(1),
portRangeStartM(0),
portRangeStopM(0),
transportModeM(eTransportModeUnicast),
detachedModeM(false),
disableServerQuirksM(false),
useSingleModelServersM(false)
{

View File

@@ -19,6 +19,10 @@ private:
unsigned int ciExtensionM;
unsigned int eitScanM;
unsigned int useBytesM;
unsigned int portRangeStartM;
unsigned int portRangeStopM;
unsigned int transportModeM;
bool detachedModeM;
bool disableServerQuirksM;
bool useSingleModelServersM;
int cicamsM[MAX_CICAM_COUNT];
@@ -33,25 +37,31 @@ public:
eOperatingModeHigh,
eOperatingModeCount
};
enum eTransportMode {
eTransportModeUnicast = 0,
eTransportModeMulticast,
eTransportModeRtpOverTcp,
eTransportModeCount
};
enum eTraceMode {
eTraceModeNormal = 0x0000,
eTraceModeDebug1 = 0x0001,
eTraceModeDebug2 = 0x0002,
eTraceModeDebug3 = 0x0004,
eTraceModeDebug4 = 0x0008,
eTraceModeDebug5 = 0x0010,
eTraceModeDebug6 = 0x0020,
eTraceModeDebug7 = 0x0040,
eTraceModeDebug8 = 0x0080,
eTraceModeDebug9 = 0x0100,
eTraceModeDebug10 = 0x0200,
eTraceModeDebug11 = 0x0400,
eTraceModeDebug12 = 0x0800,
eTraceModeDebug13 = 0x1000,
eTraceModeDebug14 = 0x2000,
eTraceModeDebug15 = 0x4000,
eTraceModeDebug16 = 0x8000,
eTraceModeMask = 0xFFFF
eTraceModeNormal = 0x0000,
eTraceModeDebug1 = 0x0001,
eTraceModeDebug2 = 0x0002,
eTraceModeDebug3 = 0x0004,
eTraceModeDebug4 = 0x0008,
eTraceModeDebug5 = 0x0010,
eTraceModeDebug6 = 0x0020,
eTraceModeDebug7 = 0x0040,
eTraceModeDebug8 = 0x0080,
eTraceModeDebug9 = 0x0100,
eTraceModeDebug10 = 0x0200,
eTraceModeDebug11 = 0x0400,
eTraceModeDebug12 = 0x0800,
eTraceModeDebug13 = 0x1000,
eTraceModeDebug14 = 0x2000,
eTraceModeDebug15 = 0x4000,
eTraceModeDebug16 = 0x8000,
eTraceModeMask = 0xFFFF
};
cSatipConfig();
unsigned int GetOperatingMode(void) const { return operatingModeM; }
@@ -66,12 +76,19 @@ public:
int GetCICAM(unsigned int indexP) const;
unsigned int GetEITScan(void) const { return eitScanM; }
unsigned int GetUseBytes(void) const { return useBytesM; }
unsigned int GetTransportMode(void) const { return transportModeM; }
bool IsTransportModeUnicast(void) const { return (transportModeM == eTransportModeUnicast); }
bool IsTransportModeRtpOverTcp(void) const { return (transportModeM == eTransportModeRtpOverTcp); }
bool IsTransportModeMulticast(void) const { return (transportModeM == eTransportModeMulticast); }
bool GetDetachedMode(void) const { return detachedModeM; }
bool GetDisableServerQuirks(void) const { return disableServerQuirksM; }
bool GetUseSingleModelServers(void) const { return useSingleModelServersM; }
unsigned int GetDisabledSourcesCount(void) const;
int GetDisabledSources(unsigned int indexP) const;
unsigned int GetDisabledFiltersCount(void) const;
int GetDisabledFilters(unsigned int indexP) const;
unsigned int GetPortRangeStart(void) const { return portRangeStartM; }
unsigned int GetPortRangeStop(void) const { return portRangeStopM; }
void SetOperatingMode(unsigned int operatingModeP) { operatingModeM = operatingModeP; }
void SetTraceMode(unsigned int modeP) { traceModeM = (modeP & eTraceModeMask); }
@@ -79,10 +96,14 @@ public:
void SetCICAM(unsigned int indexP, int cicamP);
void SetEITScan(unsigned int onOffP) { eitScanM = onOffP; }
void SetUseBytes(unsigned int onOffP) { useBytesM = onOffP; }
void SetTransportMode(unsigned int transportModeP) { transportModeM = transportModeP; }
void SetDetachedMode(bool onOffP) { detachedModeM = onOffP; }
void SetDisableServerQuirks(bool onOffP) { disableServerQuirksM = onOffP; }
void SetUseSingleModelServers(bool onOffP) { useSingleModelServersM = onOffP; }
void SetDisabledSources(unsigned int indexP, int sourceP);
void SetDisabledFilters(unsigned int indexP, int numberP);
void SetPortRangeStart(unsigned int rangeStartP) { portRangeStartM = rangeStartP; }
void SetPortRangeStop(unsigned int rangeStopP) { portRangeStopM = rangeStopP; }
};
extern cSatipConfig SatipConfig;

View File

@@ -103,7 +103,8 @@ cString cSatipDevice::GetSatipStatus(void)
bool live = (device == cDevice::ActualDevice());
bool lock = device->HasLock();
const cChannel *channel = device->GetCurrentlyTunedTransponder();
for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) {
LOCK_TIMERS_READ;
for (const cTimer *timer = Timers->First(); timer; timer = Timers->Next(timer)) {
if (timer->Recording()) {
cRecordControl *control = cRecordControls::GetRecordControl(timer);
if (control && control->Device() == device)
@@ -115,8 +116,12 @@ cString cSatipDevice::GetSatipStatus(void)
info = cString::sprintf("%sCardIndex: %d HasLock: yes Strength: %d Quality: %d%s\n", *info, device->CardIndex(), device->SignalStrength(), device->SignalQuality(), live ? " Live: yes" : "");
else
info = cString::sprintf("%sCardIndex: %d HasLock: no\n", *info, device->CardIndex());
if (channel && channel->Number() > 0)
info = cString::sprintf("%sTransponder: %d Channel: %s\n", *info, (channel && channel->Number() > 0) ? channel->Transponder() : 0, (channel && channel->Number() > 0) ? channel->Name() : "---");
if (channel) {
if (channel->Number() > 0 && device->Receiving())
info = cString::sprintf("%sTransponder: %d Channel: %s\n", *info, channel->Transponder(), channel->Name());
else
info = cString::sprintf("%sTransponder: %d\n", *info, channel->Transponder());
}
if (timers)
info = cString::sprintf("%sRecording: %d timer%s\n", *info, timers, (timers > 1) ? "s" : "");
info = cString::sprintf("%s\n", *info);
@@ -128,13 +133,14 @@ cString cSatipDevice::GetSatipStatus(void)
cString cSatipDevice::GetGeneralInformation(void)
{
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
return cString::sprintf("SAT>IP device: %d\nCardIndex: %d\nStream: %s\nSignal: %s\nStream bitrate: %s\n%sChannel: %s",
LOCK_CHANNELS_READ;
return cString::sprintf("SAT>IP device: %d\nCardIndex: %d\nStream: %s\nSignal: %s\nStream bitrate: %s\n%sChannel: %s\n",
deviceIndexM, CardIndex(),
pTunerM ? *pTunerM->GetInformation() : "",
pTunerM ? *pTunerM->GetSignalStatus() : "",
pTunerM ? *pTunerM->GetTunerStatistic() : "",
*GetBufferStatistic(),
*Channels.GetByNumber(cDevice::CurrentChannel())->ToText());
*Channels->GetByNumber(cDevice::CurrentChannel())->ToText());
}
cString cSatipDevice::GetPidsInformation(void)
@@ -194,6 +200,8 @@ cString cSatipDevice::DeviceType(void) const
cString cSatipDevice::DeviceName(void) const
{
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
if (!Receiving())
return cString::sprintf("%s %d", *DeviceType(), deviceIndexM);
return deviceNameM;
}
@@ -219,6 +227,8 @@ bool cSatipDevice::ProvidesSource(int sourceP) const
{
cSource *s = Sources.Get(sourceP);
debug9("%s (%c) desc='%s' [device %u]", __PRETTY_FUNCTION__, cSource::ToChar(sourceP), s ? s->Description() : "", deviceIndexM);
if (SatipConfig.GetDetachedMode())
return false;
// source descriptions starting with '0' are disabled
if (s && s->Description() && (*(s->Description()) == '0'))
return false;
@@ -341,6 +351,7 @@ bool cSatipDevice::SetChannelDevice(const cChannel *channelP, bool liveViewP)
}
else if (pTunerM) {
pTunerM->SetSource(NULL, 0, NULL, deviceIndexM);
deviceNameM = cString::sprintf("%s %d", *DeviceType(), deviceIndexM);
return true;
}
return false;
@@ -348,11 +359,11 @@ bool cSatipDevice::SetChannelDevice(const cChannel *channelP, bool liveViewP)
bool cSatipDevice::SetPid(cPidHandle *handleP, int typeP, bool onP)
{
debug12("%s (%d, %d, %d) [device %u]", __PRETTY_FUNCTION__, handleP->pid, typeP, onP, deviceIndexM);
debug12("%s (%d, %d, %d) [device %u]", __PRETTY_FUNCTION__, handleP ? handleP->pid : -1, typeP, onP, deviceIndexM);
if (pTunerM && handleP && handleP->pid >= 0) {
if (onP)
return pTunerM->SetPid(handleP->pid, typeP, true);
else if (!handleP->used)
else if (!handleP->used && pSectionFilterHandlerM && !pSectionFilterHandlerM->Exists(handleP->pid))
return pTunerM->SetPid(handleP->pid, typeP, false);
}
return true;
@@ -458,6 +469,18 @@ int cSatipDevice::GetCISlot(void)
return slot;
}
cString cSatipDevice::GetTnrParameterString(void)
{
if (channelM.Ca())
return GetTnrUrlParameters(&channelM);
return NULL;
}
bool cSatipDevice::IsIdle(void)
{
return !Receiving();
}
uchar *cSatipDevice::GetData(int *availableP)
{
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
@@ -501,6 +524,8 @@ void cSatipDevice::SkipData(int countP)
bool cSatipDevice::GetTSPacket(uchar *&dataP)
{
debug16("%s [device %u]", __PRETTY_FUNCTION__, deviceIndexM);
if (SatipConfig.GetDetachedMode())
return false;
if (tsBufferM) {
if (cCamSlot *cs = CamSlot()) {
if (cs->WantsTsData()) {

View File

@@ -43,7 +43,7 @@ private:
// constructor & destructor
public:
cSatipDevice(unsigned int deviceIndexP);
explicit cSatipDevice(unsigned int deviceIndexP);
virtual ~cSatipDevice();
cString GetInformation(unsigned int pageP = SATIP_DEVICE_INFO_ALL);
@@ -110,6 +110,8 @@ public:
virtual int GetId(void);
virtual int GetPmtPid(void);
virtual int GetCISlot(void);
virtual cString GetTnrParameterString(void);
virtual bool IsIdle(void);
};
#endif // __SATIP_DEVICE_H

View File

@@ -16,9 +16,11 @@ public:
virtual int GetId(void) = 0;
virtual int GetPmtPid(void) = 0;
virtual int GetCISlot(void) = 0;
virtual cString GetTnrParameterString(void) = 0;
virtual bool IsIdle(void) = 0;
private:
cSatipDeviceIf(const cSatipDeviceIf&);
explicit cSatipDeviceIf(const cSatipDeviceIf&);
cSatipDeviceIf& operator=(const cSatipDeviceIf&);
};

View File

@@ -32,7 +32,7 @@ bool cSatipDiscover::Initialize(cSatipDiscoverServers *serversP)
if (instanceS) {
if (serversP) {
for (cSatipDiscoverServer *s = serversP->First(); s; s = serversP->Next(s))
instanceS->AddServer(s->IpAddress(), s->Model(), s->Description());
instanceS->AddServer(s->IpAddress(), s->IpPort(), s->Model(), s->Filters(), s->Description(), s->Quirk());
}
else
instanceS->Activate();
@@ -47,43 +47,26 @@ void cSatipDiscover::Destroy(void)
instanceS->Deactivate();
}
size_t cSatipDiscover::WriteCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP)
size_t cSatipDiscover::HeaderCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP)
{
cSatipDiscover *obj = reinterpret_cast<cSatipDiscover *>(dataP);
size_t len = sizeP * nmembP;
debug16("%s len=%zu", __PRETTY_FUNCTION__, len);
if (obj) {
CURLcode res = CURLE_OK;
const char *desc = NULL, *model = NULL, *addr = NULL;
#ifdef USE_TINYXML
TiXmlDocument doc;
char *xml = MALLOC(char, len + 1);
memcpy(xml, ptrP, len);
*(xml + len + 1) = 0;
doc.Parse((const char *)xml);
TiXmlHandle docHandle(&doc);
TiXmlElement *descElement = docHandle.FirstChild("root").FirstChild("device").FirstChild("friendlyName").ToElement();
if (descElement)
desc = descElement->GetText() ? descElement->GetText() : "MyBrokenHardware";
TiXmlElement *modelElement = docHandle.FirstChild("root").FirstChild("device").FirstChild("satip:X_SATIPCAP").ToElement();
if (modelElement)
model = modelElement->GetText() ? modelElement->GetText() : "DVBS2-1";
#else
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_buffer(ptrP, len);
if (result) {
pugi::xml_node descNode = doc.first_element_by_path("root/device/friendlyName");
if (descNode)
desc = descNode.text().as_string("MyBrokenHardware");
pugi::xml_node modelNode = doc.first_element_by_path("root/device/satip:X_SATIPCAP");
if (modelNode)
model = modelNode.text().as_string("DVBS2-1");
}
#endif
SATIP_CURL_EASY_GETINFO(obj->handleM, CURLINFO_PRIMARY_IP, &addr);
obj->AddServer(addr, model, desc);
}
if (obj && (len > 0))
obj->headerBufferM.Add(ptrP, len);
return len;
}
size_t cSatipDiscover::DataCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP)
{
cSatipDiscover *obj = reinterpret_cast<cSatipDiscover *>(dataP);
size_t len = sizeP * nmembP;
debug16("%s len=%zu", __PRETTY_FUNCTION__, len);
if (obj && (len > 0))
obj->dataBufferM.Add(ptrP, len);
return len;
}
@@ -120,6 +103,8 @@ int cSatipDiscover::DebugCallback(CURL *handleP, curl_infotype typeP, char *data
cSatipDiscover::cSatipDiscover()
: cThread("SATIP discover"),
mutexM(),
headerBufferM(),
dataBufferM(),
msearchM(*this),
probeUrlListM(),
handleM(curl_easy_init()),
@@ -170,7 +155,7 @@ void cSatipDiscover::Action(void)
probeIntervalM.Set(eProbeIntervalMs);
msearchM.Probe();
mutexM.Lock();
serversM.Cleanup(eProbeIntervalMs * 2);
serversM.Cleanup(eCleanupTimeoutMs);
mutexM.Unlock();
}
mutexM.Lock();
@@ -195,6 +180,7 @@ void cSatipDiscover::Fetch(const char *urlP)
{
debug1("%s (%s)", __PRETTY_FUNCTION__, urlP);
if (handleM && !isempty(urlP)) {
const char *addr = NULL;
long rc = 0;
CURLcode res = CURLE_OK;
@@ -203,8 +189,10 @@ void cSatipDiscover::Fetch(const char *urlP)
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_DEBUGFUNCTION, cSatipDiscover::DebugCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_DEBUGDATA, this);
// Set callback
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipDiscover::WriteCallback);
// Set header and data callbacks
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_HEADERFUNCTION, cSatipDiscover::HeaderCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEHEADER, this);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipDiscover::DataCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
// No progress meter and no signaling
@@ -224,14 +212,71 @@ void cSatipDiscover::Fetch(const char *urlP)
// Fetch the data
SATIP_CURL_EASY_PERFORM(handleM);
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_RESPONSE_CODE, &rc);
if (rc != 200)
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_PRIMARY_IP, &addr);
if (rc == 200) {
ParseDeviceInfo(addr, ParseRtspPort());
headerBufferM.Reset();
dataBufferM.Reset();
}
else
error("Discovery detected invalid status code: %ld", rc);
}
}
void cSatipDiscover::AddServer(const char *addrP, const char *modelP, const char * descP)
int cSatipDiscover::ParseRtspPort(void)
{
debug1("%s (%s, %s, %s)", __PRETTY_FUNCTION__, addrP, modelP, descP);
debug1("%s", __PRETTY_FUNCTION__);
char *s, *p = headerBufferM.Data();
char *r = strtok_r(p, "\r\n", &s);
int port = SATIP_DEFAULT_RTSP_PORT;
while (r) {
debug16("%s (%zu): %s", __PRETTY_FUNCTION__, headerBufferM.Size(), r);
r = skipspace(r);
if (strstr(r, "X-SATIP-RTSP-Port")) {
int tmp = -1;
if (sscanf(r, "X-SATIP-RTSP-Port:%11d", &tmp) == 1) {
port = tmp;
break;
}
}
r = strtok_r(NULL, "\r\n", &s);
}
return port;
}
void cSatipDiscover::ParseDeviceInfo(const char *addrP, const int portP)
{
debug1("%s (%s, %d)", __PRETTY_FUNCTION__, addrP, portP);
const char *desc = NULL, *model = NULL;
#ifdef USE_TINYXML
TiXmlDocument doc;
doc.Parse(dataBufferM.Data());
TiXmlHandle docHandle(&doc);
TiXmlElement *descElement = docHandle.FirstChild("root").FirstChild("device").FirstChild("friendlyName").ToElement();
if (descElement)
desc = descElement->GetText() ? descElement->GetText() : "MyBrokenHardware";
TiXmlElement *modelElement = docHandle.FirstChild("root").FirstChild("device").FirstChild("satip:X_SATIPCAP").ToElement();
if (modelElement)
model = modelElement->GetText() ? modelElement->GetText() : "DVBS2-1";
#else
pugi::xml_document doc;
if (doc.load_buffer(dataBufferM.Data(), dataBufferM.Size())) {
pugi::xml_node descNode = doc.first_element_by_path("root/device/friendlyName");
if (descNode)
desc = descNode.text().as_string("MyBrokenHardware");
pugi::xml_node modelNode = doc.first_element_by_path("root/device/satip:X_SATIPCAP");
if (modelNode)
model = modelNode.text().as_string("DVBS2-1");
}
#endif
AddServer(addrP, portP, model, NULL, desc, cSatipServer::eSatipQuirkNone);
}
void cSatipDiscover::AddServer(const char *addrP, const int portP, const char *modelP, const char *filtersP, const char *descP, const int quirkP)
{
debug1("%s (%s, %d, %s, %s, %s, %d)", __PRETTY_FUNCTION__, addrP, portP, modelP, filtersP, descP, quirkP);
cMutexLock MutexLock(&mutexM);
if (SatipConfig.GetUseSingleModelServers() && modelP && !isempty(modelP)) {
int n = 0;
@@ -240,9 +285,9 @@ void cSatipDiscover::AddServer(const char *addrP, const char *modelP, const char
while (r) {
r = skipspace(r);
cString desc = cString::sprintf("%s #%d", !isempty(descP) ? descP : "MyBrokenHardware", n++);
cSatipServer *tmp = new cSatipServer(addrP, r, desc);
cSatipServer *tmp = new cSatipServer(addrP, portP, r, filtersP, desc, quirkP);
if (!serversM.Update(tmp)) {
info("Adding server '%s|%s|%s' CI: %s Quirks: %s", tmp->Address(), tmp->Model(), tmp->Description(), tmp->HasCI() ? "yes" : "no", tmp->HasQuirk() ? tmp->Quirks() : "none");
info("Adding server '%s|%s|%s' Filters: %s CI: %s Quirks: %s", tmp->Address(), tmp->Model(), tmp->Description(), !isempty(tmp->Filters()) ? tmp->Filters() : "none", tmp->HasCI() ? "yes" : "no", tmp->HasQuirk() ? tmp->Quirks() : "none");
serversM.Add(tmp);
}
else
@@ -252,9 +297,9 @@ void cSatipDiscover::AddServer(const char *addrP, const char *modelP, const char
FREE_POINTER(p);
}
else {
cSatipServer *tmp = new cSatipServer(addrP, modelP, descP);
cSatipServer *tmp = new cSatipServer(addrP, portP, modelP, filtersP, descP, quirkP);
if (!serversM.Update(tmp)) {
info("Adding server '%s|%s|%s' CI: %s Quirks: %s", tmp->Address(), tmp->Model(), tmp->Description(), tmp->HasCI() ? "yes" : "no", tmp->HasQuirk() ? tmp->Quirks() : "none");
info("Adding server '%s|%s|%s' Filters: %s CI: %s Quirks: %s", tmp->Address(), tmp->Model(), tmp->Description(), !isempty(tmp->Filters()) ? tmp->Filters() : "none", tmp->HasCI() ? "yes" : "no", tmp->HasQuirk() ? tmp->Quirks() : "none");
serversM.Add(tmp);
}
else
@@ -311,6 +356,13 @@ cString cSatipDiscover::GetServerList(void)
return serversM.List();
}
void cSatipDiscover::ActivateServer(cSatipServer *serverP, bool onOffP)
{
debug16("%s (, %d)", __PRETTY_FUNCTION__, onOffP);
cMutexLock MutexLock(&mutexM);
serversM.Activate(serverP, onOffP);
}
void cSatipDiscover::AttachServer(cSatipServer *serverP, int deviceIdP, int transponderP)
{
debug16("%s (, %d, %d)", __PRETTY_FUNCTION__, deviceIdP, transponderP);
@@ -346,6 +398,13 @@ cString cSatipDiscover::GetServerAddress(cSatipServer *serverP)
return serversM.GetAddress(serverP);
}
int cSatipDiscover::GetServerPort(cSatipServer *serverP)
{
debug16("%s", __PRETTY_FUNCTION__);
cMutexLock MutexLock(&mutexM);
return serversM.GetPort(serverP);
}
int cSatipDiscover::NumProvidedSystems(void)
{
debug16("%s", __PRETTY_FUNCTION__);

View File

@@ -13,6 +13,7 @@
#include <vdr/thread.h>
#include <vdr/tools.h>
#include "common.h"
#include "discoverif.h"
#include "msearch.h"
#include "server.h"
@@ -20,16 +21,22 @@
class cSatipDiscoverServer : public cListObject {
private:
int ipPortM;
int quirkM;
cString ipAddressM;
cString descriptionM;
cString modelM;
cString filtersM;
public:
cSatipDiscoverServer(const char *ipAddressP, const char *modelP, const char *descriptionP)
cSatipDiscoverServer(const char *ipAddressP, const int ipPortP, const char *modelP, const char *filtersP, const char *descriptionP, const int quirkP)
{
ipAddressM = ipAddressP; modelM = modelP; descriptionM = descriptionP;
ipAddressM = ipAddressP; ipPortM = ipPortP; modelM = modelP; filtersM = filtersP; descriptionM = descriptionP; quirkM = quirkP;
}
int IpPort(void) { return ipPortM; }
int Quirk(void) { return quirkM; }
const char *IpAddress(void) { return *ipAddressM; }
const char *Model(void) { return *modelM; }
const char *Filters(void) { return *filtersM; }
const char *Description(void) { return *descriptionM; }
};
@@ -39,15 +46,19 @@ class cSatipDiscoverServers : public cList<cSatipDiscoverServer> {
class cSatipDiscover : public cThread, public cSatipDiscoverIf {
private:
enum {
eSleepTimeoutMs = 500, // in milliseconds
eConnectTimeoutMs = 1500, // in milliseconds
eProbeTimeoutMs = 2000, // in milliseconds
eProbeIntervalMs = 60000 // in milliseconds
eSleepTimeoutMs = 500, // in milliseconds
eConnectTimeoutMs = 1500, // in milliseconds
eProbeTimeoutMs = 2000, // in milliseconds
eProbeIntervalMs = 60000, // in milliseconds
eCleanupTimeoutMs = 124000 // in milliseoonds
};
static cSatipDiscover *instanceS;
static size_t WriteCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static size_t HeaderCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static size_t DataCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static int DebugCallback(CURL *handleP, curl_infotype typeP, char *dataP, size_t sizeP, void *userPtrP);
cMutex mutexM;
cSatipMemoryBuffer headerBufferM;
cSatipMemoryBuffer dataBufferM;
cSatipMsearch msearchM;
cStringList probeUrlListM;
CURL *handleM;
@@ -56,7 +67,9 @@ private:
cSatipServers serversM;
void Activate(void);
void Deactivate(void);
void AddServer(const char *addrP, const char *modelP, const char *descP);
int ParseRtspPort(void);
void ParseDeviceInfo(const char *addrP, const int portP);
void AddServer(const char *addrP, const int portP, const char *modelP, const char *filtersP, const char *descP, const int quirkP);
void Fetch(const char *urlP);
// constructor
cSatipDiscover();
@@ -79,11 +92,13 @@ public:
cSatipServer *GetServer(cSatipServer *serverP);
cSatipServers *GetServers(void);
cString GetServerString(cSatipServer *serverP);
void ActivateServer(cSatipServer *serverP, bool onOffP);
void AttachServer(cSatipServer *serverP, int deviceIdP, int transponderP);
void DetachServer(cSatipServer *serverP, int deviceIdP, int transponderP);
bool IsServerQuirk(cSatipServer *serverP, int quirkP);
bool HasServerCI(cSatipServer *serverP);
cString GetServerAddress(cSatipServer *serverP);
int GetServerPort(cSatipServer *serverP);
cString GetServerList(void);
int NumProvidedSystems(void);

View File

@@ -15,7 +15,7 @@ public:
virtual void SetUrl(const char *urlP) = 0;
private:
cSatipDiscoverIf(const cSatipDiscoverIf&);
explicit cSatipDiscoverIf(const cSatipDiscoverIf&);
cSatipDiscoverIf& operator=(const cSatipDiscoverIf&);
};

5
log.h
View File

@@ -34,9 +34,9 @@
#define debug10(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug10) ? dsyslog("SATIP10: " x) : void() )
// 0x0400: CI
#define debug11(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug11) ? dsyslog("SATIP11: " x) : void() )
// 0x0800: Discovery
// 0x0800: Pids
#define debug12(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug12) ? dsyslog("SATIP12: " x) : void() )
// 0x1000: Pids
// 0x1000: Discovery
#define debug13(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug13) ? dsyslog("SATIP13: " x) : void() )
// 0x2000: TBD
#define debug14(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug14) ? dsyslog("SATIP14: " x) : void() )
@@ -46,4 +46,3 @@
#define debug16(x...) void( SatipConfig.IsTraceMode(cSatipConfig::eTraceModeDebug16) ? dsyslog("SATIP16: " x) : void() )
#endif // __SATIP_LOG_H

View File

@@ -29,7 +29,7 @@ cSatipMsearch::cSatipMsearch(cSatipDiscoverIf &discoverP)
memset(bufferM, 0, bufferLenM);
else
error("Cannot create Msearch buffer!");
if (!Open(eDiscoveryPort))
if (!Open(eDiscoveryPort, true))
error("Cannot open Msearch port!");
}
@@ -45,6 +45,9 @@ void cSatipMsearch::Probe(void)
cSatipPoller::GetInstance()->Register(*this);
registeredM = true;
}
// Send two queries with one second interval
Write(bcastAddressS, reinterpret_cast<const unsigned char *>(bcastMessageS), strlen(bcastMessageS));
cCondWait::SleepMs(1000);
Write(bcastAddressS, reinterpret_cast<const unsigned char *>(bcastMessageS), strlen(bcastMessageS));
}
@@ -60,12 +63,12 @@ void cSatipMsearch::Process(void)
int length;
while ((length = Read(bufferM, bufferLenM)) > 0) {
bufferM[min(length, int(bufferLenM - 1))] = 0;
debug12("%s len=%d buf=%s", __PRETTY_FUNCTION__, length, bufferM);
debug13("%s len=%d buf=%s", __PRETTY_FUNCTION__, length, bufferM);
bool status = false, valid = false;
char *s, *p = reinterpret_cast<char *>(bufferM), *location = NULL;
char *r = strtok_r(p, "\r\n", &s);
while (r) {
debug12("%s r=%s", __PRETTY_FUNCTION__, r);
debug13("%s r=%s", __PRETTY_FUNCTION__, r);
// Check the status code
// HTTP/1.1 200 OK
if (!status && startswith(r, "HTTP/1.1 200 OK"))
@@ -97,6 +100,11 @@ void cSatipMsearch::Process(void)
}
}
void cSatipMsearch::Process(unsigned char *dataP, int lengthP)
{
debug16("%s", __PRETTY_FUNCTION__);
}
cString cSatipMsearch::ToString(void) const
{
return "MSearch";

View File

@@ -26,7 +26,7 @@ private:
bool registeredM;
public:
cSatipMsearch(cSatipDiscoverIf &discoverP);
explicit cSatipMsearch(cSatipDiscoverIf &discoverP);
virtual ~cSatipMsearch();
void Probe(void);
@@ -34,6 +34,7 @@ public:
public:
virtual int GetFd(void);
virtual void Process(void);
virtual void Process(unsigned char *dataP, int lengthP);
virtual cString ToString(void) const;
};

352
param.c
View File

@@ -147,9 +147,6 @@ cString GetTransponderUrlParameters(const cChannel *channelP)
cDvbTransponderParameters dtp(channelP->Parameters());
int DataSlice = 0;
int C2TuningFrequencyType = 0;
int Pilot = dtp.Pilot();
int T2SystemId = dtp.T2SystemId();
int SisoMiso = dtp.SisoMiso();
float freq = channelP->Frequency();
char type = cSource::ToChar(channelP->Source());
cSource *source = Sources.Get(channelP->Source());
@@ -161,34 +158,349 @@ cString GetTransponderUrlParameters(const cChannel *channelP)
freq /= 1000L;
#define ST(s) if (strchr(s, type) && (strchr(s, '0' + dtp.System() + 1) || strchr(s, '*')))
#define STBUFLEFT (sizeof(buffer) - (q - buffer))
ST(" S 1") { // to comply with SAT>IP protocol specification 1.2.2
dtp.SetPilot(PILOT_OFF);
dtp.SetModulation(QPSK);
dtp.SetRollOff(ROLLOFF_35);
}
if ((channelP->Rid() % 100) > 0)
q += snprintf(q, STBUFLEFT, "&fe=%d", channelP->Rid() % 100);
ST(" S *") q += snprintf(q, STBUFLEFT, "src=%d&", ((src > 0) && (src <= 255)) ? src : 1);
q += snprintf(q, STBUFLEFT, "freq=%s", *dtoa(freq, "%lg"));
ST(" S *") q += snprintf(q, STBUFLEFT, "&src=%d", ((src > 0) && (src <= 255)) ? src : 1);
ST(" S *") q += snprintf(q, STBUFLEFT, "&sr=%d", channelP->Srate());
ST("C 1") q += snprintf(q, STBUFLEFT, "&sr=%d", channelP->Srate());
ST(" S *") q += snprintf(q, STBUFLEFT, "&pol=%c", tolower(dtp.Polarization()));
ST("C T2") q += snprintf(q, STBUFLEFT, "&plp=%d", dtp.StreamId());
ST(" T2") q += snprintf(q, STBUFLEFT, "&t2id=%d", T2SystemId);
ST(" S *") q += PrintUrlString(q, STBUFLEFT, dtp.RollOff(), SatipRollOffValues);
ST("C 2") q += snprintf(q, STBUFLEFT, "&c2tft=%d", C2TuningFrequencyType);
ST("C 2") q += snprintf(q, STBUFLEFT, "&ds=%d", DataSlice);
ST("C 1") q += PrintUrlString(q, STBUFLEFT, dtp.Inversion(), SatipInversionValues);
ST(" T2") q += PrintUrlString(q, STBUFLEFT, SisoMiso, SatipSisoMisoValues);
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.Bandwidth(), SatipBandwidthValues);
ST("C 2") q += PrintUrlString(q, STBUFLEFT, dtp.Bandwidth(), SatipBandwidthValues);
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.Guard(), SatipGuardValues);
ST("CST*") q += PrintUrlString(q, STBUFLEFT, dtp.CoderateH(), SatipCodeRateValues);
ST(" S 2") q += PrintUrlString(q, STBUFLEFT, Pilot, SatipPilotValues);
ST(" S 2") q += PrintUrlString(q, STBUFLEFT, dtp.Modulation(), SatipModulationValues);
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.Modulation(), SatipModulationValues);
ST("C 1") q += PrintUrlString(q, STBUFLEFT, dtp.Modulation(), SatipModulationValues);
ST(" S 2") q += PrintUrlString(q, STBUFLEFT, dtp.RollOff(), SatipRollOffValues);
ST(" S *") q += PrintUrlString(q, STBUFLEFT, dtp.System(), SatipSystemValuesSat);
ST("C *") q += PrintUrlString(q, STBUFLEFT, dtp.System(), SatipSystemValuesCable);
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.System(), SatipSystemValuesTerrestrial);
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.Transmission(), SatipTransmissionValues);
if ((channelP->Rid() % 100) > 0)
snprintf(q, STBUFLEFT, "&fe=%d", channelP->Rid() % 100);
ST(" S *") q += PrintUrlString(q, STBUFLEFT, dtp.Modulation(), SatipModulationValues);
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.Modulation(), SatipModulationValues);
ST("C 1") q += PrintUrlString(q, STBUFLEFT, dtp.Modulation(), SatipModulationValues);
ST(" S *") q += PrintUrlString(q, STBUFLEFT, dtp.Pilot(), SatipPilotValues);
ST(" S *") q += snprintf(q, STBUFLEFT, "&sr=%d", channelP->Srate());
ST("C 1") q += snprintf(q, STBUFLEFT, "&sr=%d", channelP->Srate());
ST(" T*") q += PrintUrlString(q, STBUFLEFT, dtp.Guard(), SatipGuardValues);
ST("CST*") q += PrintUrlString(q, STBUFLEFT, dtp.CoderateH(), SatipCodeRateValues);
ST("C 2") q += snprintf(q, STBUFLEFT, "&ds=%d", DataSlice);
ST("C T2") q += snprintf(q, STBUFLEFT, "&plp=%d", dtp.StreamId());
ST(" T2") q += snprintf(q, STBUFLEFT, "&t2id=%d", dtp.T2SystemId());
ST(" T2") q += PrintUrlString(q, STBUFLEFT, dtp.SisoMiso(), SatipSisoMisoValues);
ST("C 1") q += PrintUrlString(q, STBUFLEFT, dtp.Inversion(), SatipInversionValues);
#undef ST
return buffer;
}
return NULL;
}
cString GetTnrUrlParameters(const cChannel *channelP)
{
if (channelP) {
cDvbTransponderParameters dtp(channelP->Parameters());
eTrackType track = cDevice::PrimaryDevice()->GetCurrentAudioTrack();
// TunerType: Byte;
// 0 = cable, 1 = satellite, 2 = terrestrial, 3 = atsc, 4 = iptv, 5 = stream (URL, DVBViewer GE)
int TunerType = 0;
if (channelP->IsCable())
TunerType = 0;
else if (channelP->IsSat())
TunerType = 1;
else if (channelP->IsTerr())
TunerType = 2;
else if (channelP->IsAtsc())
TunerType = 3;
// Frequency: DWord;
// DVB-S: MHz if < 1000000, kHz if >= 1000000
// DVB-T/C, ATSC: kHz
// IPTV: IP address Byte3.Byte2.Byte1.Byte0
int Frequency = channelP->Frequency() / 1000;
// Symbolrate: DWord;
// DVB S/C: in kSym/s
// DVB-T, ATSC: 0
// IPTV: Port
int Symbolrate = (channelP->IsSat() || channelP->IsCable()) ? channelP->Srate() : 0;
// LNB_LOF: Word;
// DVB-S: Local oscillator frequency of the LNB
// DVB-T/C, ATSC: 0
// IPTV: Byte0 and Byte1 of Source IP
int LNB_LOF = channelP->IsSat() ? Setup.LnbSLOF : 0;
// Tone: Byte;
// 0 = off, 1 = 22 khz
int Tone = (channelP->Frequency() < Setup.LnbSLOF) ? 0 : 1;
// Polarity: Byte;
// DVB-S polarity: 0 = horizontal, 1 = vertical, 2 = circular left, 3 = circular right
// DVB-C modulation: 0 = Auto, 1 = 16QAM, 2 = 32QAM, 3 = 64QAM, 4 = 128QAM, 5 = 256 QAM
// DVB-T bandwidth: 0 = 6 MHz, 1 = 7 MHz, 2 = 8 MHz
// IPTV: Byte3 of SourceIP
int Polarity = 0;
if (channelP->IsSat()) {
switch (tolower(dtp.Polarization())) {
case 'h':
Polarity = 0;
break;
case 'v':
Polarity = 1;
break;
case 'l':
Polarity = 2;
break;
case 'r':
Polarity = 3;
break;
default:
break;
}
}
else if (channelP->IsCable()) {
switch (dtp.Modulation()) {
case 999:
Polarity = 0;
break;
case 16:
Polarity = 1;
break;
case 32:
Polarity = 2;
break;
case 64:
Polarity = 3;
break;
case 128:
Polarity = 4;
break;
case 256:
Polarity = 5;
break;
default:
break;
}
}
else if (channelP->IsTerr()) {
switch (dtp.Bandwidth()) {
case 6:
Polarity = 0;
break;
case 7:
Polarity = 1;
break;
case 8:
Polarity = 2;
break;
default:
break;
}
}
// DiSEqC: Byte;
// 0 = None
// 1 = Pos A (mostly translated to PosA/OptA)
// 2 = Pos B (mostly translated to PosB/OptA)
// 3 = PosA/OptA
// 4 = PosB/OptA
// 5 = PosA/OptB
// 6 = PosB/OptB
// 7 = Preset Position (DiSEqC 1.2, see DiSEqCExt)
// 8 = Angular Position (DiSEqC 1.2, see DiSEqCExt)
// 9 = DiSEqC Command Sequence (see DiSEqCExt)
int DiSEqC = 0;
// FEC: Byte;
// 0 = Auto
// 1 = 1/2
// 2 = 2/3
// 3 = 3/4
// 4 = 5/6
// 5 = 7/8
// 6 = 8/9
// 7 = 3/5
// 8 = 4/5
// 9 = 9/10
// IPTV: Byte2 of SourceIP
// DVB C/T, ATSC: 0
int FEC = 0;
if (channelP->IsSat()) {
switch (dtp.CoderateH()) {
case 999:
FEC = 0;
break;
case 12:
FEC = 1;
break;
case 23:
FEC = 2;
break;
case 34:
FEC = 3;
break;
case 56:
FEC = 4;
break;
case 78:
FEC = 5;
break;
case 89:
FEC = 6;
break;
case 35:
FEC = 7;
break;
case 45:
FEC = 8;
break;
case 910:
FEC = 9;
break;
default:
break;
}
}
// Audio_PID: Word;
int Audio_PID = channelP->Apid(0);
if (IS_AUDIO_TRACK(track))
Audio_PID = channelP->Apid(int(track - ttAudioFirst));
else if (IS_DOLBY_TRACK(track))
Audio_PID = channelP->Dpid(int(track - ttDolbyFirst));
// Video_PID: Word;
int Video_PID = channelP->Vpid();
// PMT_PID: Word;
int PMT_PID = channelP->Ppid();
// Service_ID: Word;
int Service_ID = channelP->Sid();
// SatModulation: Byte;
// Bit 0..1: satellite modulation. 0 = Auto, 1 = QPSK, 2 = 8PSK, 3 = 16QAM or APSK for DVB-S2
// Bit 2: modulation system. 0 = DVB-S/T/C, 1 = DVB-S2/T2/C2
// Bit 3..4: DVB-S2: roll-off. 0 = 0.35, 1 = 0.25, 2 = 0.20, 3 = reserved
// Bit 5..6: spectral inversion, 0 = undefined, 1 = auto, 2 = normal, 3 = inverted
// Bit 7: DVB-S2: pilot symbols, 0 = off, 1 = on
// DVB-T2: DVB-T2 Lite, 0 = off, 1 = on
int SatModulation = 0;
if (channelP->IsSat() && dtp.System()) {
switch (dtp.Modulation()) {
case 999:
SatModulation |= (0 & 0x3) << 0;
break;
case 2:
SatModulation |= (1 & 0x3) << 0;
break;
case 5:
SatModulation |= (2 & 0x3) << 0;
break;
case 6:
SatModulation |= (3 & 0x3) << 0;
break;
default:
break;
}
}
SatModulation |= (dtp.System() & 0x1) << 2;
if (channelP->IsSat() && dtp.System()) {
switch (dtp.RollOff()) {
case 35:
SatModulation |= (0 & 0x3) << 3;
break;
case 25:
SatModulation |= (1 & 0x3) << 3;
break;
case 20:
SatModulation |= (2 & 0x3) << 3;
break;
default:
break;
}
}
switch (dtp.Inversion()) {
case 999:
SatModulation |= (1 & 0x3) << 5;
break;
case 0:
SatModulation |= (2 & 0x3) << 5;
break;
case 1:
SatModulation |= (3 & 0x3) << 5;
break;
default:
break;
}
if (channelP->IsSat() && dtp.System()) {
switch (dtp.Pilot()) {
case 0:
SatModulation |= (0 & 0x1) << 7;
break;
case 1:
SatModulation |= (1 & 0x1) << 7;
break;
default:
break;
}
}
// DiSEqCExt: Word;
// DiSEqC Extension, meaning depends on DiSEqC
// DiSEqC = 0..6: 0
// DiSEqC = 7: Preset Position (DiSEqC 1.2)
// DiSEqC = 8: Orbital Position (DiSEqC 1.2, USALS, for calculating motor angle)
// Same format as OrbitalPos above
// DiSEQC = 9: Orbital Position referencing DiSEqC sequence defined in DiSEqC.xml/ini
// Same format as OrbitalPos above
int DiSEqCExt = 0;
// Flags: Byte;
// Bit 0: 1 = encrypted channel
// Bit 1: reserved, set to 0
// Bit 2: 1 = channel broadcasts RDS data
// Bit 3: 1 = channel is a video service (even if the Video PID is temporarily = 0)
// Bit 4: 1 = channel is an audio service (even if the Audio PID is temporarily = 0)
// Bit 5: 1 = audio has a different samplerate than 48 KHz
// Bit 6: 1 = bandstacking, internally polarisation is always set to H
// Bit 7: 1 = channel entry is an additional audio track of the preceding
// channel with bit 7 = 0
int Flags = (channelP->Ca() > 0xFF) ? 1 : 0;
// ChannelGroup: Byte;
// 0 = Group A, 1 = Group B, 2 = Group C etc.
int ChannelGroup = 0;
// TransportStream_ID: Word;
int TransportStream_ID = channelP->Tid();
// OriginalNetwork_ID: Word;
int OriginalNetwork_ID = channelP->Nid();
// Substream: Word;
// DVB-S/C/T, ATSC, IPTV: 0
// DVB-T2: 0 = PLP_ID not set, 1..256: PLP_ID + 1, 257... reserved
int Substream = (channelP->IsTerr() && dtp.System()) ? dtp.StreamId() - 1 : 0;
// OrbitalPos: Word;
// DVB-S: orbital position x 10, 0 = undefined, 1..1800 east, 1801..3599 west (1°W = 3599)
// DVB-C: 4000..4999
// DVB-T: 5000..5999
// ATSC: 6000..6999
// IPTV: 7000..7999
// Stream: 8000..8999
int OrbitalPos = 0;
if (channelP->IsSat()) {
OrbitalPos = cSource::Position(channelP->Source());
if (OrbitalPos != 3600)
OrbitalPos += 1800;
}
return cString::sprintf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
TunerType, Frequency, Symbolrate, LNB_LOF, Tone, Polarity, DiSEqC, FEC, Audio_PID, Video_PID, PMT_PID, Service_ID,
SatModulation, DiSEqCExt, Flags, ChannelGroup, TransportStream_ID, OriginalNetwork_ID, Substream, OrbitalPos);
}
return NULL;
}

View File

@@ -11,5 +11,6 @@
#include "common.h"
cString GetTransponderUrlParameters(const cChannel *channelP);
cString GetTnrUrlParameters(const cChannel *channelP);
#endif // __SATIP_PARAM_H

View File

@@ -1,14 +1,14 @@
# VDR plugin language source file.
# Copyright (C) 2007-2015 Rolf Ahrenberg
# Copyright (C) 2007-2016 Rolf Ahrenberg
# This file is distributed under the same license as the satip package.
# Gabriel Bonich, 2014-2015
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-satip 2.2.0\n"
"Project-Id-Version: vdr-satip 2.3.0\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2015-02-19 02:19+0200\n"
"PO-Revision-Date: 2015-02-19 02:19+0200\n"
"POT-Creation-Date: 2016-12-18 12:18+0200\n"
"PO-Revision-Date: 2016-12-18 12:18+0200\n"
"Last-Translator: Gabriel Bonich <gbonich@gmail.com>\n"
"Language-Team: Catalan <vdr@linuxtv.org>\n"
"Language: ca\n"
@@ -85,6 +85,15 @@ msgstr "Normal"
msgid "high"
msgstr "Alt"
msgid "Unicast"
msgstr ""
msgid "Multicast"
msgstr ""
msgid "RTP-over-TCP"
msgstr ""
msgid "Button$Devices"
msgstr "Dispositius"
@@ -178,6 +187,15 @@ msgstr "Filtra"
msgid "Define an ill-behaving filter to be blacklisted."
msgstr "Definir un filtre mal comportar a la llista negra."
msgid "Transport mode"
msgstr ""
msgid ""
"Define which transport mode shall be used.\n"
"\n"
"Unicast, Multicast, RTP-over-TCP"
msgstr ""
msgid "Active SAT>IP servers:"
msgstr "Activa SAT>IP servers:"

View File

@@ -1,14 +1,14 @@
# VDR plugin language source file.
# Copyright (C) 2007-2015 Rolf Ahrenberg
# Copyright (C) 2007-2016 Rolf Ahrenberg
# This file is distributed under the same license as the satip package.
# Frank Neumann, 2014-2015
# Frank Neumann, 2014-2016
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-satip 2.2.0\n"
"Project-Id-Version: vdr-satip 2.3.0\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2015-02-19 02:19+0200\n"
"PO-Revision-Date: 2015-02-19 02:19+0200\n"
"POT-Creation-Date: 2016-12-18 12:18+0200\n"
"PO-Revision-Date: 2016-12-18 12:18+0200\n"
"Last-Translator: Frank Neumann <fnu@yavdr.org>\n"
"Language-Team: German <vdr@linuxtv.org>\n"
"Language: de\n"
@@ -85,11 +85,20 @@ msgstr "normal"
msgid "high"
msgstr "hoch"
msgid "Unicast"
msgstr "Unicast"
msgid "Multicast"
msgstr "Multicast"
msgid "RTP-over-TCP"
msgstr "RTP-over-TCP"
msgid "Button$Devices"
msgstr "Geräte"
msgid "Operating mode"
msgstr "Betriebsmodus"
msgstr "Betriebsart"
msgid ""
"Define the used operating mode for all SAT>IP devices:\n"
@@ -99,7 +108,7 @@ msgid ""
"normal - devices are working within normal parameters\n"
"high - devices are working at the highest priority"
msgstr ""
"Bestimme den Betriebsmodus für alle SAT>IP Geräte:\n"
"Bestimme die Betriebsart für alle SAT>IP Geräte:\n"
"\n"
"aus - Geräte sind abgeschaltet\n"
"niedrig - Geräte arbeiten mit geringster Priorität\n"
@@ -170,13 +179,25 @@ msgid ""
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."
"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 bleiben."
msgid "Filter"
msgstr "Filter"
msgid "Define an ill-behaving filter to be blacklisted."
msgstr "Bestimme einen fehlerhaften Filter der ausgeblendet werden soll."
msgstr "Bestimme fehlerhafte Filter die ausgeblendet werden sollen."
msgid "Transport mode"
msgstr "Übertragungsart"
msgid ""
"Define which transport mode shall be used.\n"
"\n"
"Unicast, Multicast, RTP-over-TCP"
msgstr ""
"Lege die gewünschte Übertragungsart fest.\n"
"\n"
"Unicast, Multicast, RTP-over-TCP"
msgid "Active SAT>IP servers:"
msgstr "Aktive SAT>IP Server:"

View File

@@ -1,14 +1,14 @@
# VDR plugin language source file.
# Copyright (C) 2007-2015 Rolf Ahrenberg
# Copyright (C) 2007-2016 Rolf Ahrenberg
# This file is distributed under the same license as the satip package.
# Gabriel Bonich, 2014-2015
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-satip 2.2.0\n"
"Project-Id-Version: vdr-satip 2.3.0\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2015-02-19 02:19+0200\n"
"PO-Revision-Date: 2015-02-19 02:19+0200\n"
"POT-Creation-Date: 2016-12-18 12:18+0200\n"
"PO-Revision-Date: 2016-12-18 12:18+0200\n"
"Last-Translator: Gabriel Bonich <gbonich@gmail.com>\n"
"Language-Team: Spanish <vdr@linuxtv.org>\n"
"Language: es\n"
@@ -85,6 +85,15 @@ msgstr "Normal"
msgid "high"
msgstr "Alto"
msgid "Unicast"
msgstr ""
msgid "Multicast"
msgstr ""
msgid "RTP-over-TCP"
msgstr ""
msgid "Button$Devices"
msgstr "Dispositivos"
@@ -178,6 +187,15 @@ msgstr "Filtra"
msgid "Define an ill-behaving filter to be blacklisted."
msgstr "Define un filtro para poner en la lista negra."
msgid "Transport mode"
msgstr ""
msgid ""
"Define which transport mode shall be used.\n"
"\n"
"Unicast, Multicast, RTP-over-TCP"
msgstr ""
msgid "Active SAT>IP servers:"
msgstr "Activa SAT>IP servers:"

View File

@@ -1,14 +1,14 @@
# VDR plugin language source file.
# Copyright (C) 2007-2015 Rolf Ahrenberg
# Copyright (C) 2007-2016 Rolf Ahrenberg
# This file is distributed under the same license as the satip package.
# Rolf Ahrenberg, 2015
# Rolf Ahrenberg, 2015-2016
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-satip 2.2.0\n"
"Project-Id-Version: vdr-satip 2.3.0\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2015-02-19 02:19+0200\n"
"PO-Revision-Date: 2015-02-19 02:19+0200\n"
"POT-Creation-Date: 2016-12-18 12:18+0200\n"
"PO-Revision-Date: 2016-12-18 12:18+0200\n"
"Last-Translator: Rolf Ahrenberg\n"
"Language-Team: Finnish <vdr@linuxtv.org>\n"
"Language: fi\n"
@@ -85,6 +85,15 @@ msgstr "normaali"
msgid "high"
msgstr "korkea"
msgid "Unicast"
msgstr "Unicast"
msgid "Multicast"
msgstr "Multicast"
msgid "RTP-over-TCP"
msgstr "RTP-over-TCP"
msgid "Button$Devices"
msgstr "Laitteet"
@@ -177,6 +186,18 @@ 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 "Transport mode"
msgstr "Siirtoyhteystapa"
msgid ""
"Define which transport mode shall be used.\n"
"\n"
"Unicast, Multicast, RTP-over-TCP"
msgstr ""
"Määrittele käytettävä siirtoyhteystapa.\n"
"\n"
"Unicast, Multicast, RTP-over-TCP"
msgid "Active SAT>IP servers:"
msgstr "Aktiiviset SAT>IP-palvelimet:"

View File

@@ -79,7 +79,7 @@ void cSatipPoller::Action(void)
// Do the thread loop
while (Running()) {
int nfds = epoll_wait(fdM, events, eMaxFileDescriptors, -1);
ERROR_IF_FUNC((nfds == -1), "epoll_wait() failed", break, ;);
ERROR_IF_FUNC((nfds == -1 && errno != EINTR), "epoll_wait() failed", break, ;);
for (int i = 0; i < nfds; ++i) {
cSatipPollerIf* poll = reinterpret_cast<cSatipPollerIf *>(events[i].data.ptr);
if (poll) {

View File

@@ -14,10 +14,11 @@ public:
virtual ~cSatipPollerIf() {}
virtual int GetFd(void) = 0;
virtual void Process(void) = 0;
virtual void Process(unsigned char *dataP, int lengthP) = 0;
virtual cString ToString(void) const = 0;
private:
cSatipPollerIf(const cSatipPollerIf&);
explicit cSatipPollerIf(const cSatipPollerIf&);
cSatipPollerIf& operator=(const cSatipPollerIf&);
};

12
rtcp.c
View File

@@ -36,7 +36,7 @@ int cSatipRtcp::GetFd(void)
int cSatipRtcp::GetApplicationOffset(int *lengthP)
{
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, *lengthP, tunerM.GetId());
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, lengthP ? *lengthP : -1, tunerM.GetId());
if (!lengthP)
return -1;
int offset = 0;
@@ -92,6 +92,16 @@ void cSatipRtcp::Process(void)
}
}
void cSatipRtcp::Process(unsigned char *dataP, int lengthP)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
if (dataP && lengthP > 0) {
int offset = GetApplicationOffset(&lengthP);
if (offset >= 0)
tunerM.ProcessApplicationData(dataP + offset, lengthP);
}
}
cString cSatipRtcp::ToString(void) const
{
return cString::sprintf("RTCP [device %d]", tunerM.GetId());

3
rtcp.h
View File

@@ -23,13 +23,14 @@ private:
int GetApplicationOffset(int *lengthP);
public:
cSatipRtcp(cSatipTunerIf &tunerP);
explicit cSatipRtcp(cSatipTunerIf &tunerP);
virtual ~cSatipRtcp();
// for internal poller interface
public:
virtual int GetFd(void);
virtual void Process(void);
virtual void Process(unsigned char *dataP, int lengthP);
virtual cString ToString(void) const;
};

16
rtp.c
View File

@@ -142,6 +142,22 @@ void cSatipRtp::Process(void)
}
}
void cSatipRtp::Process(unsigned char *dataP, int lengthP)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
if (dataP && lengthP > 0) {
uint64_t elapsed;
cTimeMs processing(0);
int headerlen = GetHeaderLength(dataP, lengthP);
if ((headerlen >= 0) && (headerlen < lengthP))
tunerM.ProcessVideoData(dataP + headerlen, lengthP - headerlen);
elapsed = processing.Elapsed();
if (elapsed > 1)
debug6("%s %d read(s) took %" PRIu64 " ms [device %d]", __PRETTY_FUNCTION__, lengthP, elapsed, tunerM.GetId());
}
}
cString cSatipRtp::ToString(void) const
{
return cString::sprintf("RTP [device %d]", tunerM.GetId());

3
rtp.h
View File

@@ -28,7 +28,7 @@ private:
int GetHeaderLength(unsigned char *bufferP, unsigned int lengthP);
public:
cSatipRtp(cSatipTunerIf &tunerP);
explicit cSatipRtp(cSatipTunerIf &tunerP);
virtual ~cSatipRtp();
virtual void Close(void);
@@ -36,6 +36,7 @@ public:
public:
virtual int GetFd(void);
virtual void Process(void);
virtual void Process(unsigned char *dataP, int lengthP);
virtual cString ToString(void) const;
};

302
rtsp.c
View File

@@ -15,9 +15,16 @@
cSatipRtsp::cSatipRtsp(cSatipTunerIf &tunerP)
: tunerM(tunerP),
modeM(cmUnicast),
headerBufferM(),
dataBufferM(),
handleM(NULL),
headerListM(NULL)
headerListM(NULL),
errorNoMoreM(""),
errorOutOfRangeM(""),
errorCheckSyntaxM(""),
modeM(cSatipConfig::eTransportModeUnicast),
interleavedRtpIdM(0),
interleavedRtcpIdM(1)
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
Create();
@@ -29,46 +36,50 @@ cSatipRtsp::~cSatipRtsp()
Destroy();
}
size_t cSatipRtsp::HeaderCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
{
cSatipRtsp *obj = reinterpret_cast<cSatipRtsp *>(dataP);
size_t len = sizeP * nmembP;
debug16("%s len=%zu", __PRETTY_FUNCTION__, len);
char *s, *p = (char *)ptrP;
char *r = strtok_r(p, "\r\n", &s);
while (obj && r) {
debug16("%s (%zu): %s", __PRETTY_FUNCTION__, len, r);
r = skipspace(r);
if (strstr(r, "com.ses.streamID")) {
int streamid = -1;
if (sscanf(r, "com.ses.streamID:%11d", &streamid) == 1)
obj->tunerM.SetStreamId(streamid);
}
else if (strstr(r, "Session:")) {
int timeout = -1;
char *session = NULL;
if (sscanf(r, "Session:%m[^;];timeout=%11d", &session, &timeout) == 2)
obj->tunerM.SetSessionTimeout(skipspace(session), timeout * 1000);
else if (sscanf(r, "Session:%m[^;]", &session) == 1)
obj->tunerM.SetSessionTimeout(skipspace(session), -1);
FREE_POINTER(session);
}
r = strtok_r(NULL, "\r\n", &s);
}
return len;
}
size_t cSatipRtsp::WriteCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
size_t cSatipRtsp::HeaderCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP)
{
cSatipRtsp *obj = reinterpret_cast<cSatipRtsp *>(dataP);
size_t len = sizeP * nmembP;
debug16("%s len=%zu", __PRETTY_FUNCTION__, len);
if (obj && (len > 0))
obj->tunerM.ProcessApplicationData((u_char*)ptrP, len);
obj->headerBufferM.Add(ptrP, len);
return len;
}
size_t cSatipRtsp::DataCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP)
{
cSatipRtsp *obj = reinterpret_cast<cSatipRtsp *>(dataP);
size_t len = sizeP * nmembP;
debug16("%s len=%zu", __PRETTY_FUNCTION__, len);
if (obj)
obj->dataBufferM.Add(ptrP, len);
return len;
}
size_t cSatipRtsp::InterleaveCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP)
{
cSatipRtsp *obj = reinterpret_cast<cSatipRtsp *>(dataP);
size_t len = sizeP * nmembP;
debug16("%s len=%zu", __PRETTY_FUNCTION__, len);
if (obj && ptrP && len > 0) {
char tag = ptrP[0] & 0xFF;
if (tag == '$') {
int count = ((ptrP[2] & 0xFF) << 8) | (ptrP[3] & 0xFF);
if (count > 0) {
unsigned int channel = ptrP[1] & 0xFF;
u_char *data = (u_char *)&ptrP[4];
if (channel == obj->interleavedRtpIdM)
obj->tunerM.ProcessRtpData(data, count);
else if (channel == obj->interleavedRtcpIdM)
obj->tunerM.ProcessRtcpData(data, count);
}
}
}
return len;
}
@@ -102,6 +113,21 @@ int cSatipRtsp::DebugCallback(CURL *handleP, curl_infotype typeP, char *dataP, s
return 0;
}
cString cSatipRtsp::GetActiveMode(void)
{
switch (modeM) {
case cSatipConfig::eTransportModeUnicast:
return "Unicast";
case cSatipConfig::eTransportModeMulticast:
return "Multicast";
case cSatipConfig::eTransportModeRtpOverTcp:
return "RTP-over-TCP";
default:
break;
}
return "";
}
cString cSatipRtsp::RtspUnescapeString(const char *strP)
{
debug1("%s (%s) [device %d]", __PRETTY_FUNCTION__, strP, tunerM.GetId());
@@ -138,6 +164,9 @@ void cSatipRtsp::Create(void)
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_TIMEOUT_MS, (long)eConnectTimeoutMs);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_CONNECTTIMEOUT_MS, (long)eConnectTimeoutMs);
// Limit download speed (bytes/s)
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_MAX_RECV_SPEED_LARGE, eMaxDownloadSpeedMBits * 131072L);
// Set user-agent
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s (device %d)", PLUGIN_NAME_I18N, VERSION, tunerM.GetId()));
}
@@ -186,9 +215,9 @@ bool cSatipRtsp::Options(const char *uriP)
return result;
}
bool cSatipRtsp::Setup(const char *uriP, int rtpPortP, int rtcpPortP)
bool cSatipRtsp::Setup(const char *uriP, int rtpPortP, int rtcpPortP, bool useTcpP)
{
debug1("%s (%s, %d, %d) [device %d]", __PRETTY_FUNCTION__, uriP, rtpPortP, rtcpPortP, tunerM.GetId());
debug1("%s (%s, %d, %d, %d) [device %d]", __PRETTY_FUNCTION__, uriP, rtpPortP, rtcpPortP, useTcpP, tunerM.GetId());
bool result = false;
if (handleM && !isempty(uriP)) {
@@ -197,15 +226,18 @@ bool cSatipRtsp::Setup(const char *uriP, int rtpPortP, int rtcpPortP)
cTimeMs processing(0);
CURLcode res = CURLE_OK;
switch (modeM) {
case cmMulticast:
// RTP/AVP;multicast;destination=<IP multicast address>;port=<RTP port>-<RTCP port>;ttl=<ttl>
transport = cString::sprintf("RTP/AVP;multicast;port=%d-%d", rtpPortP, rtcpPortP);
switch (SatipConfig.GetTransportMode()) {
case cSatipConfig::eTransportModeMulticast:
// RTP/AVP;multicast;destination=<multicast group address>;port=<RTP port>-<RTCP port>;ttl=<ttl>[;source=<multicast source address>]
transport = cString::sprintf("RTP/AVP;multicast");
break;
default:
case cmUnicast:
// RTP/AVP;unicast;client_port=<client RTP port>-<client RTCP port>
transport = cString::sprintf("RTP/AVP;unicast;client_port=%d-%d", rtpPortP, rtcpPortP);
// RTP/AVP/TCP;unicast;client_port=<client RTP port>-<client RTCP port>
if (useTcpP)
transport = cString::sprintf("RTP/AVP/TCP;unicast;interleaved=%u-%u", interleavedRtpIdM, interleavedRtcpIdM);
else
transport = cString::sprintf("RTP/AVP;unicast;client_port=%d-%d", rtpPortP, rtcpPortP);
break;
}
@@ -216,10 +248,25 @@ bool cSatipRtsp::Setup(const char *uriP, int rtpPortP, int rtcpPortP)
// Set header callback for catching the session and timeout
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_HEADERFUNCTION, cSatipRtsp::HeaderCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEHEADER, this);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipRtsp::DataCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_INTERLEAVEFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_INTERLEAVEDATA, NULL);
SATIP_CURL_EASY_PERFORM(handleM);
// Session id is now known - disable header parsing
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_HEADERFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEHEADER, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, NULL);
if (headerBufferM.Size() > 0) {
ParseHeader();
headerBufferM.Reset();
}
if (dataBufferM.Size() > 0) {
ParseData();
dataBufferM.Reset();
}
result = ValidateLatestResponse(&rc);
debug5("%s (%s, %d, %d) Response %ld in %" PRIu64 " ms [device %d]", __PRETTY_FUNCTION__, uriP, rtpPortP, rtcpPortP, rc, processing.Elapsed(), tunerM.GetId());
@@ -253,11 +300,15 @@ bool cSatipRtsp::Describe(const char *uriP)
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, uriP);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipRtsp::WriteCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipRtsp::DataCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
SATIP_CURL_EASY_PERFORM(handleM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, NULL);
if (dataBufferM.Size() > 0) {
tunerM.ProcessApplicationData((u_char *)dataBufferM.Data(), dataBufferM.Size());
dataBufferM.Reset();
}
result = ValidateLatestResponse(&rc);
debug5("%s (%s) Response %ld in %" PRIu64 " ms [device %d]", __PRETTY_FUNCTION__, uriP, rc, processing.Elapsed(), tunerM.GetId());
@@ -278,7 +329,15 @@ bool cSatipRtsp::Play(const char *uriP)
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, uriP);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipRtsp::DataCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
SATIP_CURL_EASY_PERFORM(handleM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, NULL);
if (dataBufferM.Size() > 0) {
ParseData();
dataBufferM.Reset();
}
result = ValidateLatestResponse(&rc);
debug5("%s (%s) Response %ld in %" PRIu64 " ms [device %d]", __PRETTY_FUNCTION__, uriP, rc, processing.Elapsed(), tunerM.GetId());
@@ -299,7 +358,17 @@ bool cSatipRtsp::Teardown(const char *uriP)
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_STREAM_URI, uriP);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipRtsp::DataCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_INTERLEAVEFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_INTERLEAVEDATA, NULL);
SATIP_CURL_EASY_PERFORM(handleM);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, NULL);
if (dataBufferM.Size() > 0) {
ParseData();
dataBufferM.Reset();
}
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_CLIENT_CSEQ, 1L);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_SESSION_ID, NULL);
@@ -311,24 +380,153 @@ bool cSatipRtsp::Teardown(const char *uriP)
return result;
}
void cSatipRtsp::ParseHeader(void)
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
char *s, *p = headerBufferM.Data();
char *r = strtok_r(p, "\r\n", &s);
while (r) {
debug16("%s (%zu): %s", __PRETTY_FUNCTION__, headerBufferM.Size(), r);
r = skipspace(r);
if (strstr(r, "com.ses.streamID")) {
int streamid = -1;
if (sscanf(r, "com.ses.streamID:%11d", &streamid) == 1)
tunerM.SetStreamId(streamid);
}
else if (strstr(r, "Session:")) {
int timeout = -1;
char *session = NULL;
if (sscanf(r, "Session:%m[^;];timeout=%11d", &session, &timeout) == 2)
tunerM.SetSessionTimeout(skipspace(session), timeout * 1000);
else if (sscanf(r, "Session:%m[^;]", &session) == 1)
tunerM.SetSessionTimeout(skipspace(session), -1);
FREE_POINTER(session);
}
else if (strstr(r, "Transport:")) {
CURLcode res = CURLE_OK;
int rtp = -1, rtcp = -1, ttl = -1;
char *tmp = NULL, *destination = NULL, *source = NULL;
interleavedRtpIdM = 0;
interleavedRtcpIdM = 1;
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_INTERLEAVEFUNCTION, NULL);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_INTERLEAVEDATA, NULL);
if (sscanf(r, "Transport:%m[^;];unicast;client_port=%11d-%11d", &tmp, &rtp, &rtcp) == 3) {
modeM = cSatipConfig::eTransportModeUnicast;
tunerM.SetupTransport(rtp, rtcp, NULL, NULL);
}
else if (sscanf(r, "Transport:%m[^;];multicast;destination=%m[^;];port=%11d-%11d;ttl=%11d;source=%m[^;]", &tmp, &destination, &rtp, &rtcp, &ttl, &source) == 6 ||
sscanf(r, "Transport:%m[^;];multicast;destination=%m[^;];port=%11d-%11d;ttl=%11d", &tmp, &destination, &rtp, &rtcp, &ttl) == 5) {
modeM = cSatipConfig::eTransportModeMulticast;
tunerM.SetupTransport(rtp, rtcp, destination, source);
}
else if (sscanf(r, "Transport:%m[^;];interleaved=%11d-%11d", &tmp, &rtp, &rtcp) == 3) {
interleavedRtpIdM = rtp;
interleavedRtcpIdM = rtcp;
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_INTERLEAVEFUNCTION, cSatipRtsp::InterleaveCallback);
SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_INTERLEAVEDATA, this);
modeM = cSatipConfig::eTransportModeRtpOverTcp;
tunerM.SetupTransport(-1, -1, NULL, NULL);
}
FREE_POINTER(tmp);
FREE_POINTER(destination);
FREE_POINTER(source);
}
r = strtok_r(NULL, "\r\n", &s);
}
}
void cSatipRtsp::ParseData(void)
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId());
char *s, *p = dataBufferM.Data();
char *r = strtok_r(p, "\r\n", &s);
while (r) {
debug16("%s (%zu): %s", __PRETTY_FUNCTION__, dataBufferM.Size(), r);
r = skipspace(r);
if (strstr(r, "No-More:")) {
char *tmp = NULL;
if (sscanf(r, "No-More:%m[^;]", &tmp) == 1) {
errorNoMoreM = skipspace(tmp);
debug3("%s No-More: %s [device %d]", __PRETTY_FUNCTION__, *errorNoMoreM, tunerM.GetId());
}
FREE_POINTER(tmp);
}
else if (strstr(r, "Out-of-Range:")) {
char *tmp = NULL;
if (sscanf(r, "Out-of-Range:%m[^;]", &tmp) == 1) {
errorOutOfRangeM = skipspace(tmp);
debug3("%s Out-of-Range: %s [device %d]", __PRETTY_FUNCTION__, *errorOutOfRangeM, tunerM.GetId());
}
FREE_POINTER(tmp);
}
else if (strstr(r, "Check-Syntax:")) {
char *tmp = NULL;
if (sscanf(r, "Check-Syntax:%m[^;]", &tmp) == 1) {
errorCheckSyntaxM = skipspace(tmp);
debug3("%s Check-Syntax: %s [device %d]", __PRETTY_FUNCTION__, *errorCheckSyntaxM, tunerM.GetId());
}
FREE_POINTER(tmp);
}
r = strtok_r(NULL, "\r\n", &s);
}
}
bool cSatipRtsp::ValidateLatestResponse(long *rcP)
{
bool result = false;
if (handleM) {
char *url = NULL;
long rc = 0;
CURLcode res = CURLE_OK;
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_RESPONSE_CODE, &rc);
if (rc == 200)
result = true;
else if (rc != 0) {
char *url = NULL;
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url);
error("Detected invalid status code %ld: %s [device %d]", rc, url, tunerM.GetId());
}
switch (rc) {
case 200:
result = true;
break;
case 400:
// SETUP PLAY TEARDOWN
// The message body of the response may contain the "Check-Syntax:" parameter followed
// by the malformed syntax
if (!isempty(*errorCheckSyntaxM)) {
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url);
error("Check syntax: %s (error code %ld: %s) [device %d]", *errorCheckSyntaxM, rc, url, tunerM.GetId());
break;
}
case 403:
// SETUP PLAY TEARDOWN
// The message body of the response may contain the "Out-of-Range:" parameter followed
// by a space-separated list of the attribute names that are not understood:
// "src" "fe" "freq" "pol" "msys" "mtype" "plts" "ro" "sr" "fec" "pids" "addpids" "delpids" "mcast"
if (!isempty(*errorOutOfRangeM)) {
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url);
error("Out of range: %s (error code %ld: %s) [device %d]", *errorOutOfRangeM, rc, url, tunerM.GetId());
// Reseting the connection wouldn't help anything due to invalid channel configuration, so let it be successful
result = true;
break;
}
case 503:
// SETUP PLAY
// The message body of the response may contain the "No-More:" parameter followed
// by a space-separated list of the missing ressources: “sessions” "frontends" "pids
if (!isempty(*errorNoMoreM)) {
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url);
error("No more: %s (error code %ld: %s) [device %d]", *errorNoMoreM, rc, url, tunerM.GetId());
break;
}
default:
SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url);
error("Detected invalid status code %ld: %s [device %d]", rc, url, tunerM.GetId());
break;
}
if (rcP)
*rcP = rc;
}
errorNoMoreM = "";
errorOutOfRangeM = "";
errorCheckSyntaxM = "";
debug1("%s result=%s [device %d]", __PRETTY_FUNCTION__, result ? "ok" : "failed", tunerM.GetId());
return result;

26
rtsp.h
View File

@@ -15,26 +15,37 @@
#error "libcurl is missing required RTSP support"
#endif
#include "common.h"
#include "tunerif.h"
class cSatipRtsp {
private:
static size_t HeaderCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static size_t WriteCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static size_t HeaderCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static size_t DataCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static size_t InterleaveCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP);
static int DebugCallback(CURL *handleP, curl_infotype typeP, char *dataP, size_t sizeP, void *userPtrP);
enum {
eConnectTimeoutMs = 1500, // in milliseconds
eConnectTimeoutMs = 1500, // in milliseconds
eMaxDownloadSpeedMBits = 20, // in megabits per second
};
enum eCommunicationMode { cmUnicast, cmMulticast };
cSatipTunerIf &tunerM;
eCommunicationMode modeM;
cSatipMemoryBuffer headerBufferM;
cSatipMemoryBuffer dataBufferM;
CURL *handleM;
struct curl_slist *headerListM;
cString errorNoMoreM;
cString errorOutOfRangeM;
cString errorCheckSyntaxM;
int modeM;
unsigned int interleavedRtpIdM;
unsigned int interleavedRtcpIdM;
void Create(void);
void Destroy(void);
void ParseHeader(void);
void ParseData(void);
bool ValidateLatestResponse(long *rcP);
// to prevent copy constructor and assignment
@@ -42,13 +53,14 @@ private:
cSatipRtsp& operator=(const cSatipRtsp&);
public:
cSatipRtsp(cSatipTunerIf &tunerP);
explicit cSatipRtsp(cSatipTunerIf &tunerP);
virtual ~cSatipRtsp();
cString GetActiveMode(void);
cString RtspUnescapeString(const char *strP);
void Reset(void);
bool Options(const char *uriP);
bool Setup(const char *uriP, int rtpPortP, int rtcpPortP);
bool Setup(const char *uriP, int rtpPortP, int rtcpPortP, bool useTcpP);
bool SetSession(const char *sessionP);
bool Describe(const char *uriP);
bool Play(const char *uriP);

124
satip.c
View File

@@ -19,15 +19,15 @@
#warning "CURL version >= 7.36.0 is recommended"
#endif
#if defined(APIVERSNUM) && APIVERSNUM < 20200
#error "VDR-2.2.0 API version or greater is required!"
#if defined(APIVERSNUM) && APIVERSNUM < 20301
#error "VDR-2.3.1 API version or greater is required!"
#endif
#ifndef GITVERSION
#define GITVERSION ""
#endif
const char VERSION[] = "2.2.0" GITVERSION;
const char VERSION[] = "2.3.0" GITVERSION;
static const char DESCRIPTION[] = trNOOP("SAT>IP Devices");
class cPluginSatip : public cPlugin {
@@ -35,6 +35,7 @@ private:
unsigned int deviceCountM;
cSatipDiscoverServers *serversM;
void ParseServer(const char *paramP);
void ParsePortRange(const char *paramP);
int ParseCicams(const char *valueP, int *cicamsP);
int ParseSources(const char *valueP, int *sourcesP);
int ParseFilters(const char *valueP, int *filtersP);
@@ -62,7 +63,7 @@ public:
};
cPluginSatip::cPluginSatip(void)
: deviceCountM(1),
: deviceCountM(2),
serversM(NULL)
{
debug16("%s", __PRETTY_FUNCTION__);
@@ -83,10 +84,13 @@ const char *cPluginSatip::CommandLineHelp(void)
// Return a string that describes all known command line options.
return " -d <num>, --devices=<number> set number of devices to be created\n"
" -t <mode>, --trace=<mode> set the tracing mode\n"
" -s <ipaddr>|<model>|<desc>, --server=<ipaddr1>|<model1>|<desc1>;<ipaddr2>|<model2>|<desc2>\n"
" -s <ipaddr>|<model>|<desc>, --server=<ipaddr1>|<model1>|<desc1>;<ipaddr2>:<port>|<model2>:<filter>|<desc2>:<quirk>\n"
" define hard-coded SAT>IP server(s)\n"
" -D, --detach set the detached mode on\n"
" -S, --single set the single model server mode on\n"
" -n, --noquirks disable all the server quirks\n";
" -n, --noquirks disable autodetection of the server quirks\n"
" -p, --portrange=<start>-<end> set a range of ports used for the RT[C]P server\n"
" a minimum of 2 ports per device is required.\n";
}
bool cPluginSatip::ProcessArgs(int argc, char *argv[])
@@ -97,14 +101,17 @@ bool cPluginSatip::ProcessArgs(int argc, char *argv[])
{ "devices", required_argument, NULL, 'd' },
{ "trace", required_argument, NULL, 't' },
{ "server", required_argument, NULL, 's' },
{ "portrange",required_argument, NULL, 'p' },
{ "detach", no_argument, NULL, 'D' },
{ "single", no_argument, NULL, 'S' },
{ "noquirks", no_argument, NULL, 'n' },
{ NULL, no_argument, NULL, 0 }
};
cString server;
cString portrange;
int c;
while ((c = getopt_long(argc, argv, "d:t:s:Sn", long_options, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "d:t:s:p:DSn", long_options, NULL)) != -1) {
switch (c) {
case 'd':
deviceCountM = strtol(optarg, NULL, 0);
@@ -115,16 +122,24 @@ bool cPluginSatip::ProcessArgs(int argc, char *argv[])
case 's':
server = optarg;
break;
case 'D':
SatipConfig.SetDetachedMode(true);
break;
case 'S':
SatipConfig.SetUseSingleModelServers(true);
break;
case 'n':
SatipConfig.SetDisableServerQuirks(true);
break;
case 'p':
portrange = optarg;
break;
default:
return false;
}
}
if (!isempty(*portrange))
ParsePortRange(portrange);
// this must be done after all parameters are parsed
if (!isempty(*server))
ParseServer(*server);
@@ -217,7 +232,9 @@ void cPluginSatip::ParseServer(const char *paramP)
while (r) {
r = skipspace(r);
debug3("%s server[%d]=%s", __PRETTY_FUNCTION__, n, r);
cString serverAddr, serverModel, serverDescription;
cString serverAddr, serverModel, serverFilters, serverDescription;
int serverQuirk = cSatipServer::eSatipQuirkNone;
int serverPort = SATIP_DEFAULT_RTSP_PORT;
int n2 = 0;
char *s2, *p2 = r;
char *r2 = strtok_r(p2, "|", &s2);
@@ -225,13 +242,34 @@ void cPluginSatip::ParseServer(const char *paramP)
debug3("%s param[%d]=%s", __PRETTY_FUNCTION__, n2, r2);
switch (n2++) {
case 0:
{
serverAddr = r2;
char *r3 = strchr(r2, ':');
if (r3) {
serverPort = strtol(r3 + 1, NULL, 0);
serverAddr = serverAddr.Truncate(r3 - r2);
}
}
break;
case 1:
{
serverModel = r2;
char *r3 = strchr(r2, ':');
if (r3) {
serverFilters = r3 + 1;
serverModel = serverModel.Truncate(r3 - r2);
}
}
break;
case 2:
{
serverDescription = r2;
char *r3 = strchr(r2, ':');
if (r3) {
serverQuirk = strtol(r3 + 1, NULL, 0);
serverDescription = serverDescription.Truncate(r3 - r2);
}
}
break;
default:
break;
@@ -239,10 +277,10 @@ void cPluginSatip::ParseServer(const char *paramP)
r2 = strtok_r(NULL, "|", &s2);
}
if (*serverAddr && *serverModel && *serverDescription) {
debug1("%s ipaddr=%s model=%s desc=%s", __PRETTY_FUNCTION__, *serverAddr, *serverModel, *serverDescription);
debug1("%s ipaddr=%s port=%d model=%s (%s) desc=%s (%d)", __PRETTY_FUNCTION__, *serverAddr, serverPort, *serverModel, *serverFilters, *serverDescription, serverQuirk);
if (!serversM)
serversM = new cSatipDiscoverServers();
serversM->Add(new cSatipDiscoverServer(*serverAddr, *serverModel, *serverDescription));
serversM->Add(new cSatipDiscoverServer(*serverAddr, serverPort, *serverModel, *serverFilters, *serverDescription, serverQuirk));
}
++n;
r = strtok_r(NULL, ";", &s);
@@ -250,6 +288,37 @@ void cPluginSatip::ParseServer(const char *paramP)
FREE_POINTER(p);
}
void cPluginSatip::ParsePortRange(const char *paramP)
{
char *s, *p = skipspace(paramP);
char *r = strtok_r(p, "-", &s);
unsigned int rangeStart = 0;
unsigned int rangeStop = 0;
if (r) {
rangeStart = strtol(r, NULL, 0);
r = strtok_r(NULL, "-", &s);
}
if (r)
rangeStop = strtol(r, NULL, 0);
else {
error("Port range argument not valid '%s'", paramP);
rangeStart = 0;
rangeStop = 0;
}
if (rangeStart % 2) {
error("The given range start port must be even!");
rangeStart = 0;
rangeStop = 0;
}
else if (rangeStop - rangeStart + 1 < deviceCountM * 2) {
error("The given port range is to small: %d < %d!", rangeStop - rangeStart + 1, deviceCountM * 2);
rangeStart = 0;
rangeStop = 0;
}
SatipConfig.SetPortRangeStart(rangeStart);
SatipConfig.SetPortRangeStop(rangeStop);
}
int cPluginSatip::ParseCicams(const char *valueP, int *cicamsP)
{
debug1("%s (%s,)", __PRETTY_FUNCTION__, valueP);
@@ -337,6 +406,8 @@ bool cPluginSatip::SetupParse(const char *nameP, const char *valueP)
for (unsigned int i = 0; i < DisabledFiltersCount; ++i)
SatipConfig.SetDisabledFilters(i, DisabledFilters[i]);
}
else if (!strcasecmp(nameP, "TransportMode"))
SatipConfig.SetTransportMode(atoi(valueP));
else
return false;
return true;
@@ -366,8 +437,12 @@ const char **cPluginSatip::SVDRPHelpPages(void)
" Lists status information of SAT>IP devices.\n",
"CONT\n"
" Shows SAT>IP device count.\n",
"OPER\n"
" Toggles operating mode of SAT>IP devices.\n",
"OPER [ off | low | normal | high ]\n"
" Gets and(or sets operating mode of SAT>IP devices.\n",
"ATTA\n"
" Attach active SAT>IP servers.\n",
"DETA\n"
" Detachs active SAT>IP servers.\n",
"TRAC [ <mode> ]\n"
" Gets and/or sets used tracing mode.\n",
NULL
@@ -434,8 +509,19 @@ cString cPluginSatip::SVDRPCommand(const char *commandP, const char *optionP, in
}
else if (strcasecmp(commandP, "OPER") == 0) {
cString mode;
SatipConfig.ToggleOperatingMode();
switch (SatipConfig.GetOperatingMode()) {
unsigned int oper = SatipConfig.GetOperatingMode();
if (optionP && *optionP) {
if (strcasecmp(optionP, "off") == 0)
oper = cSatipConfig::eOperatingModeOff;
else if (strcasecmp(optionP, "low") == 0)
oper = cSatipConfig::eOperatingModeLow;
else if (strcasecmp(optionP, "normal") == 0)
oper = cSatipConfig::eOperatingModeNormal;
else if (strcasecmp(optionP, "high") == 0)
oper = cSatipConfig::eOperatingModeHigh;
SatipConfig.SetOperatingMode(oper);
}
switch (oper) {
case cSatipConfig::eOperatingModeOff:
mode = "off";
break;
@@ -454,6 +540,16 @@ cString cPluginSatip::SVDRPCommand(const char *commandP, const char *optionP, in
}
return cString::sprintf("SATIP operating mode: %s\n", *mode);
}
else if (strcasecmp(commandP, "ATTA") == 0) {
SatipConfig.SetDetachedMode(false);
info("SATIP servers attached");
return cString("SATIP servers attached");
}
else if (strcasecmp(commandP, "DETA") == 0) {
SatipConfig.SetDetachedMode(true);
info("SATIP servers detached");
return cString("SATIP servers detached");
}
else if (strcasecmp(commandP, "TRAC") == 0) {
if (optionP && *optionP)
SatipConfig.SetTraceMode(strtol(optionP, NULL, 0));

View File

@@ -338,6 +338,19 @@ cString cSatipSectionFilterHandler::GetInformation(void)
return s;
}
bool cSatipSectionFilterHandler::Exists(u_short pidP)
{
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, pidP, deviceIndexM);
cMutexLock MutexLock(&mutexM);
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (filtersM[i] && (pidP == filtersM[i]->GetPid())) {
debug12("%s (%d) Found [device %d]", __PRETTY_FUNCTION__, pidP, deviceIndexM);
return true;
}
}
return false;
}
bool cSatipSectionFilterHandler::Delete(unsigned int indexP)
{
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, indexP, deviceIndexM);

View File

@@ -80,6 +80,7 @@ public:
cSatipSectionFilterHandler(int deviceIndexP, unsigned int bufferLenP);
virtual ~cSatipSectionFilterHandler();
cString GetInformation(void);
bool Exists(u_short pidP);
int Open(u_short pidP, u_char tidP, u_char maskP);
void Close(int handleP);
int GetPid(int handleP);

202
server.c
View File

@@ -80,16 +80,38 @@ bool cSatipFrontends::Detach(int deviceIdP, int transponderP)
// --- cSatipServer -----------------------------------------------------------
cSatipServer::cSatipServer(const char *addressP, const char *modelP, const char *descriptionP)
cSatipServer::cSatipServer(const char *addressP, const int portP, const char *modelP, const char *filtersP, const char *descriptionP, const int quirkP)
: addressM((addressP && *addressP) ? addressP : "0.0.0.0"),
modelM((modelP && *modelP) ? modelP : "DVBS-1"),
filtersM((filtersP && *filtersP) ? filtersP : ""),
descriptionM(!isempty(descriptionP) ? descriptionP : "MyBrokenHardware"),
quirksM(""),
quirkM(eSatipQuirkNone),
portM(portP),
quirkM(quirkP),
hasCiM(false),
activeM(true),
createdM(time(NULL)),
lastSeenM(0)
{
memset(sourceFiltersM, 0, sizeof(sourceFiltersM));
if (!isempty(*filtersM)) {
char *s, *p = strdup(*filtersM);
char *r = strtok_r(p, ",", &s);
unsigned int i = 0;
while (r) {
int t = cSource::FromString(skipspace(r));
if (t && i < ELEMENTS(sourceFiltersM))
sourceFiltersM[i++] = t;
r = strtok_r(NULL, ",", &s);
}
if (i) {
filtersM = "";
for (unsigned int j = 0; j < i; ++j)
filtersM = cString::sprintf("%s%s%s", *filtersM, isempty(*filtersM) ? "" : ",", *cSource::ToString(sourceFiltersM[j]));
debug3("%s filters=%s", __PRETTY_FUNCTION__, *filtersM);
}
FREE_POINTER(p);
}
if (!SatipConfig.GetDisableServerQuirks()) {
// These devices contain a session id bug:
// Inverto Airscreen Server IDL 400 ?
@@ -97,27 +119,64 @@ cSatipServer::cSatipServer(const char *addressP, const char *modelP, const char
if (strstr(*descriptionM, "GSSBOX") || // Grundig Sat Systems GSS.box DSI 400
strstr(*descriptionM, "DIGIBIT") || // Telestar Digibit R1
strstr(*descriptionM, "Triax SatIP Converter") // Triax TSS 400
) {
)
quirkM |= eSatipQuirkSessionId;
quirksM = cString::sprintf("%s%sSessionId", *quirksM, isempty(*quirksM) ? "" : ",");
}
// These devices contain support for RTP over TCP:
if (strstr(*descriptionM, "minisatip") || // minisatip server
strstr(*descriptionM, "DVBViewer") || // DVBViewer Media Server
strstr(*descriptionM, "GSSBOX") || // Grundig Sat Systems GSS.box DSI 400
strstr(*descriptionM, "DIGIBIT") || // Telestar Digibit R1
strstr(*descriptionM, "Triax SatIP Converter") // Triax TSS 400
)
quirkM |= eSatipQuirkRtpOverTcp;
// These devices contain a play (add/delpids) parameter bug:
if (strstr(*descriptionM, "fritzdvbc") // Fritz!WLAN Repeater DVB-C
) {
)
quirkM |= eSatipQuirkPlayPids;
quirksM = cString::sprintf("%s%sPlayPids", *quirksM, isempty(*quirksM) ? "" : ",");
}
// These devices contain a frontend locking bug:
if (strstr(*descriptionM, "fritzdvbc") // Fritz!WLAN Repeater DVB-C
) {
if (strstr(*descriptionM, "fritzdvbc") || // Fritz!WLAN Repeater DVB-C
strstr(*descriptionM, "Schwaiger Sat>IP Server") // Schwaiger MS41IP
)
quirkM |= eSatipQuirkForceLock;
quirksM = cString::sprintf("%s%sForceLock", *quirksM, isempty(*quirksM) ? "" : ",");
}
debug3("%s description=%s quirks=%s", __PRETTY_FUNCTION__, *descriptionM, *quirksM);
// These devices support the X_PMT protocol extension
if (strstr(*descriptionM, "OctopusNet") || // Digital Devices OctopusNet
strstr(*descriptionM, "minisatip") // minisatip server
)
quirkM |= eSatipQuirkCiXpmt;
// These devices support the TNR protocol extension
if (strstr(*descriptionM, "DVBViewer") // DVBViewer Media Server
)
quirkM |= eSatipQuirkCiTnr;
// These devices don't support auto-detection of pilot tones
if (strstr(*descriptionM, "GSSBOX") || // Grundig Sat Systems GSS.box DSI 400
strstr(*descriptionM, "DIGIBIT") || // Telestar Digibit R1
strstr(*descriptionM, "Triax SatIP Converter") // Triax TSS 400
// Kathrein ExIP 414/E
)
quirkM |= eSatipQuirkForcePilot;
}
// These devices support the X_PMT protocol extension
if (strstr(*descriptionM, "OctopusNet")) // Digital Devices OctopusNet
if ((quirkM & eSatipQuirkMask) & eSatipQuirkSessionId)
quirksM = cString::sprintf("%s%sSessionId", *quirksM, isempty(*quirksM) ? "" : ",");
if ((quirkM & eSatipQuirkMask) & eSatipQuirkPlayPids)
quirksM = cString::sprintf("%s%sPlayPids", *quirksM, isempty(*quirksM) ? "" : ",");
if ((quirkM & eSatipQuirkMask) & eSatipQuirkForceLock)
quirksM = cString::sprintf("%s%sForceLock", *quirksM, isempty(*quirksM) ? "" : ",");
if ((quirkM & eSatipQuirkMask) & eSatipQuirkRtpOverTcp)
quirksM = cString::sprintf("%s%sRtpOverTcp", *quirksM, isempty(*quirksM) ? "" : ",");
if ((quirkM & eSatipQuirkMask) & eSatipQuirkCiXpmt)
quirksM = cString::sprintf("%s%sCiXpmt", *quirksM, isempty(*quirksM) ? "" : ",");
if ((quirkM & eSatipQuirkMask) & eSatipQuirkCiTnr)
quirksM = cString::sprintf("%s%sCiTnr", *quirksM, isempty(*quirksM) ? "" : ",");
if ((quirkM & eSatipQuirkMask) & eSatipQuirkForcePilot)
quirksM = cString::sprintf("%s%sForcePilot", *quirksM, isempty(*quirksM) ? "" : ",");
debug3("%s description=%s quirks=%s", __PRETTY_FUNCTION__, *descriptionM, *quirksM);
// These devices support external CI
if (strstr(*descriptionM, "OctopusNet") || // Digital Devices OctopusNet
strstr(*descriptionM, "minisatip") || // minisatip server
strstr(*descriptionM, "DVBViewer") // DVBViewer Media Server
) {
hasCiM = true;
}
char *s, *p = strdup(*modelM);
char *r = strtok_r(p, ",", &s);
while (r) {
@@ -149,7 +208,7 @@ cSatipServer::cSatipServer(const char *addressP, const char *modelP, const char
}
r = strtok_r(NULL, ",", &s);
}
free(p);
FREE_POINTER(p);
}
cSatipServer::~cSatipServer()
@@ -168,53 +227,72 @@ int cSatipServer::Compare(const cListObject &listObjectP) const
return result;
}
bool cSatipServer::IsValidSource(int sourceP)
{
if (sourceFiltersM[0]) {
for (unsigned int i = 0; i < ELEMENTS(sourceFiltersM); ++i) {
if (sourceP == sourceFiltersM[i]) {
return true;
}
}
return false;
}
return true;
}
bool cSatipServer::Assign(int deviceIdP, int sourceP, int systemP, int transponderP)
{
bool result = false;
if (cSource::IsType(sourceP, 'S'))
result = frontendsM[eSatipFrontendDVBS2].Assign(deviceIdP, transponderP);
else if (cSource::IsType(sourceP, 'T')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBT2].Assign(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBT].Assign(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBT2].Assign(deviceIdP, transponderP);
}
else if (cSource::IsType(sourceP, 'C')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBC2].Assign(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBC].Assign(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBC2].Assign(deviceIdP, transponderP);
if (IsValidSource(sourceP)) {
if (cSource::IsType(sourceP, 'S'))
result = frontendsM[eSatipFrontendDVBS2].Assign(deviceIdP, transponderP);
else if (cSource::IsType(sourceP, 'T')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBT2].Assign(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBT].Assign(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBT2].Assign(deviceIdP, transponderP);
}
else if (cSource::IsType(sourceP, 'C')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBC2].Assign(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBC].Assign(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBC2].Assign(deviceIdP, transponderP);
}
}
return result;
}
bool cSatipServer::Matches(int sourceP)
{
if (cSource::IsType(sourceP, 'S'))
return GetModulesDVBS2();
else if (cSource::IsType(sourceP, 'T'))
return GetModulesDVBT() || GetModulesDVBT2();
else if (cSource::IsType(sourceP, 'C'))
return GetModulesDVBC() || GetModulesDVBC2();
if (IsValidSource(sourceP)) {
if (cSource::IsType(sourceP, 'S'))
return GetModulesDVBS2();
else if (cSource::IsType(sourceP, 'T'))
return GetModulesDVBT() || GetModulesDVBT2();
else if (cSource::IsType(sourceP, 'C'))
return GetModulesDVBC() || GetModulesDVBC2();
}
return false;
}
bool cSatipServer::Matches(int deviceIdP, int sourceP, int systemP, int transponderP)
{
bool result = false;
if (cSource::IsType(sourceP, 'S'))
result = frontendsM[eSatipFrontendDVBS2].Matches(deviceIdP, transponderP);
else if (cSource::IsType(sourceP, 'T')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBT2].Matches(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBT].Matches(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBT2].Matches(deviceIdP, transponderP);
}
else if (cSource::IsType(sourceP, 'C')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBC2].Matches(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBC].Matches(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBC2].Matches(deviceIdP, transponderP);
if (IsValidSource(sourceP)) {
if (cSource::IsType(sourceP, 'S'))
result = frontendsM[eSatipFrontendDVBS2].Matches(deviceIdP, transponderP);
else if (cSource::IsType(sourceP, 'T')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBT2].Matches(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBT].Matches(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBT2].Matches(deviceIdP, transponderP);
}
else if (cSource::IsType(sourceP, 'C')) {
if (systemP)
result = frontendsM[eSatipFrontendDVBC2].Matches(deviceIdP, transponderP);
else
result = frontendsM[eSatipFrontendDVBC].Matches(deviceIdP, transponderP) || frontendsM[eSatipFrontendDVBC2].Matches(deviceIdP, transponderP);
}
}
return result;
}
@@ -283,11 +361,11 @@ cSatipServer *cSatipServers::Find(int sourceP)
cSatipServer *cSatipServers::Assign(int deviceIdP, int sourceP, int transponderP, int systemP)
{
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s->Matches(deviceIdP, sourceP, systemP, transponderP))
if (s->IsActive() && s->Matches(deviceIdP, sourceP, systemP, transponderP))
return s;
}
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s->Assign(deviceIdP, sourceP, systemP, transponderP))
if (s->IsActive() && s->Assign(deviceIdP, sourceP, systemP, transponderP))
return s;
}
return NULL;
@@ -304,6 +382,16 @@ cSatipServer *cSatipServers::Update(cSatipServer *serverP)
return NULL;
}
void cSatipServers::Activate(cSatipServer *serverP, bool onOffP)
{
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s == serverP) {
s->Activate(onOffP);
break;
}
}
}
void cSatipServers::Attach(cSatipServer *serverP, int deviceIdP, int transponderP)
{
for (cSatipServer *s = First(); s; s = Next(s)) {
@@ -370,6 +458,18 @@ cString cSatipServers::GetAddress(cSatipServer *serverP)
return address;
}
int cSatipServers::GetPort(cSatipServer *serverP)
{
int port = SATIP_DEFAULT_RTSP_PORT;
for (cSatipServer *s = First(); s; s = Next(s)) {
if (s == serverP) {
port = s->Port();
break;
}
}
return port;
}
cString cSatipServers::GetString(cSatipServer *serverP)
{
cString list = "";
@@ -386,7 +486,7 @@ cString cSatipServers::List(void)
{
cString list = "";
for (cSatipServer *s = First(); s; s = Next(s))
list = cString::sprintf("%s%s|%s|%s\n", *list, s->Address(), s->Model(), s->Description());
list = cString::sprintf("%s%c %s|%s|%s\n", *list, s->IsActive() ? '+' : '-', s->Address(), s->Model(), s->Description());
return list;
}

View File

@@ -54,25 +54,37 @@ private:
eSatipFrontendDVBC2,
eSatipFrontendCount
};
enum {
eSatipMaxSourceFilters = 16
};
cString addressM;
cString modelM;
cString filtersM;
cString descriptionM;
cString quirksM;
cSatipFrontends frontendsM[eSatipFrontendCount];
int sourceFiltersM[eSatipMaxSourceFilters];
int portM;
int quirkM;
bool hasCiM;
bool activeM;
time_t createdM;
cTimeMs lastSeenM;
bool IsValidSource(int sourceP);
public:
enum eSatipQuirk {
eSatipQuirkNone = 0x00,
eSatipQuirkSessionId = 0x01,
eSatipQuirkPlayPids = 0x02,
eSatipQuirkForceLock = 0x04,
eSatipQuirkMask = 0x0F
eSatipQuirkNone = 0x00,
eSatipQuirkSessionId = 0x01,
eSatipQuirkPlayPids = 0x02,
eSatipQuirkForceLock = 0x04,
eSatipQuirkRtpOverTcp = 0x08,
eSatipQuirkCiXpmt = 0x10,
eSatipQuirkCiTnr = 0x20,
eSatipQuirkForcePilot = 0x40,
eSatipQuirkMask = 0xFF
};
cSatipServer(const char *addressP, const char *modelP, const char *descriptionP);
cSatipServer(const char *addressP, const int portP, const char *modelP, const char *filtersP, const char *descriptionP, const int quirkP);
virtual ~cSatipServer();
virtual int Compare(const cListObject &listObjectP) const;
bool Assign(int deviceIdP, int sourceP, int systemP, int transponderP);
@@ -85,13 +97,17 @@ public:
int GetModulesDVBT2(void);
int GetModulesDVBC(void);
int GetModulesDVBC2(void);
void Activate(bool onOffP) { activeM = onOffP; }
const char *Address(void) { return *addressM; }
const char *Model(void) { return *modelM; }
const char *Filters(void) { return *filtersM; }
const char *Description(void) { return *descriptionM; }
const char *Quirks(void) { return *quirksM; }
int Port(void) { return portM; }
bool Quirk(int quirkP) { return ((quirkP & eSatipQuirkMask) & quirkM); }
bool HasQuirk(void) { return (quirkM != eSatipQuirkNone); }
bool HasCI(void) { return hasCiM; }
bool IsActive(void) { return activeM; }
void Update(void) { lastSeenM.Set(); }
uint64_t LastSeen(void) { return lastSeenM.Elapsed(); }
time_t Created(void) { return createdM; }
@@ -105,6 +121,7 @@ public:
cSatipServer *Find(int sourceP);
cSatipServer *Assign(int deviceIdP, int sourceP, int transponderP, int systemP);
cSatipServer *Update(cSatipServer *serverP);
void Activate(cSatipServer *serverP, bool onOffP);
void Attach(cSatipServer *serverP, int deviceIdP, int transponderP);
void Detach(cSatipServer *serverP, int deviceIdP, int transponderP);
bool IsQuirk(cSatipServer *serverP, int quirkP);
@@ -112,6 +129,7 @@ public:
void Cleanup(uint64_t intervalMsP = 0);
cString GetAddress(cSatipServer *serverP);
cString GetString(cSatipServer *serverP);
int GetPort(cSatipServer *serverP);
cString List(void);
int NumProvidedSystems(void);
};

52
setup.c
View File

@@ -86,6 +86,8 @@ eOSState cSatipEditSrcItem::ProcessKey(eKeys Key)
class cSatipServerInfo : public cOsdMenu
{
private:
cSatipServer *serverM;
int activeM;
cString addressM;
cString modelM;
cString descriptionM;
@@ -94,13 +96,15 @@ private:
void Setup(void);
public:
cSatipServerInfo(cSatipServer *serverP);
explicit cSatipServerInfo(cSatipServer *serverP);
virtual ~cSatipServerInfo();
virtual eOSState ProcessKey(eKeys keyP);
};
cSatipServerInfo::cSatipServerInfo(cSatipServer *serverP)
: cOsdMenu(tr("SAT>IP Server"), 20),
serverM(serverP),
activeM(serverP && serverP->IsActive()),
addressM(serverP ? serverP->Address() : "---"),
modelM(serverP ? serverP->Model() : "---"),
descriptionM(serverP ? serverP->Description() : "---"),
@@ -118,6 +122,7 @@ cSatipServerInfo::~cSatipServerInfo()
void cSatipServerInfo::Setup(void)
{
Add(new cMenuEditBoolItem(trVDR("Active"), &activeM));
Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Address"), *addressM), osUnknown, false));
Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Model"), *modelM), osUnknown, false));
Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Description"), *descriptionM), osUnknown, false));
@@ -127,6 +132,7 @@ void cSatipServerInfo::Setup(void)
eOSState cSatipServerInfo::ProcessKey(eKeys keyP)
{
int oldActive = activeM;
eOSState state = cOsdMenu::ProcessKey(keyP);
if (state == osUnknown) {
@@ -135,6 +141,12 @@ eOSState cSatipServerInfo::ProcessKey(eKeys keyP)
default: state = osContinue; break;
}
}
if (keyP != kNone && oldActive != activeM) {
cSatipDiscover::GetInstance()->ActivateServer(serverM, activeM);
Setup();
}
return state;
}
@@ -145,7 +157,7 @@ private:
cSatipServer *serverM;
public:
cSatipServerItem(cSatipServer *serverP);
explicit cSatipServerItem(cSatipServer *serverP);
cSatipServer *Server(void) { return serverM; }
virtual void SetMenuItem(cSkinDisplayMenu *displayMenuP, int indexP, bool currentP, bool selectableP);
};
@@ -155,7 +167,7 @@ cSatipServerItem::cSatipServerItem(cSatipServer *serverP)
{
SetSelectable(true);
// Must begin with a '#' character!
SetText(*cString::sprintf("# %s (%s)\t%s", serverM->Address(), serverM->Model(), serverM->Description()));
SetText(*cString::sprintf("%s %s (%s)\t%s", serverM->IsActive() ? "+" : "-", serverM->Address(), serverM->Model(), serverM->Description()));
}
void cSatipServerItem::SetMenuItem(cSkinDisplayMenu *displayMenuP, int indexP, bool currentP, bool selectableP)
@@ -330,8 +342,10 @@ eOSState cSatipMenuInfo::ProcessKey(eKeys keyP)
// --- cSatipPluginSetup ------------------------------------------------------
cSatipPluginSetup::cSatipPluginSetup()
: deviceCountM(0),
: detachedModeM(SatipConfig.GetDetachedMode()),
deviceCountM(0),
operatingModeM(SatipConfig.GetOperatingMode()),
transportModeM(SatipConfig.GetTransportMode()),
ciExtensionM(SatipConfig.GetCIExtension()),
eitScanM(SatipConfig.GetEITScan()),
numDisabledSourcesM(SatipConfig.GetDisabledSourcesCount()),
@@ -342,6 +356,9 @@ cSatipPluginSetup::cSatipPluginSetup()
operatingModeTextsM[cSatipConfig::eOperatingModeLow] = tr("low");
operatingModeTextsM[cSatipConfig::eOperatingModeNormal] = tr("normal");
operatingModeTextsM[cSatipConfig::eOperatingModeHigh] = tr("high");
transportModeTextsM[cSatipConfig::eTransportModeUnicast] = tr("Unicast");
transportModeTextsM[cSatipConfig::eTransportModeMulticast] = tr("Multicast");
transportModeTextsM[cSatipConfig::eTransportModeRtpOverTcp] = tr("RTP-over-TCP");
for (unsigned int i = 0; i < ELEMENTS(cicamsM); ++i)
cicamsM[i] = SatipConfig.GetCICAM(i);
for (unsigned int i = 0; i < ELEMENTS(ca_systems_table); ++i)
@@ -399,15 +416,20 @@ void cSatipPluginSetup::Setup(void)
helpM.Append(tr("Define an ill-behaving filter to be blacklisted."));
}
}
Add(new cMenuEditStraItem(tr("Transport mode"), &transportModeM, ELEMENTS(transportModeTextsM), transportModeTextsM));
helpM.Append(tr("Define which transport mode shall be used.\n\nUnicast, Multicast, RTP-over-TCP"));
Add(new cOsdItem(tr("Active SAT>IP servers:"), 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("");
}
detachedModeM = SatipConfig.GetDetachedMode();
if (!detachedModeM) {
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();
@@ -461,10 +483,12 @@ eOSState cSatipPluginSetup::ProcessKey(eKeys keyP)
int oldNumDisabledFilters = numDisabledFiltersM;
eOSState state = cMenuSetupPage::ProcessKey(keyP);
// Ugly hack with hardcoded '#' character :(
// Ugly hack with hardcoded '+/-' characters :(
const char *p = Get(Current())->Text();
if (!hadSubMenu && !HasSubMenu() && (*p == '#') && (keyP == kOk))
if (!hadSubMenu && !HasSubMenu() && p && (*p == '+' || *p == '-') && (keyP == kOk))
return DeviceInfo();
if (hadSubMenu && !HasSubMenu())
Setup();
if (state == osUnknown) {
switch (keyP) {
@@ -480,7 +504,7 @@ eOSState cSatipPluginSetup::ProcessKey(eKeys keyP)
if ((keyP == kNone) && (cSatipDiscover::GetInstance()->GetServers()->Count() != deviceCountM))
Setup();
if ((keyP != kNone) && ((numDisabledSourcesM != oldNumDisabledSources) || (numDisabledFiltersM != oldNumDisabledFilters) || (operatingModeM != oldOperatingMode) || (ciExtensionM != oldCiExtension))) {
if ((keyP != kNone) && ((numDisabledSourcesM != oldNumDisabledSources) || (numDisabledFiltersM != oldNumDisabledFilters) || (operatingModeM != oldOperatingMode) || (ciExtensionM != oldCiExtension) || (detachedModeM != SatipConfig.GetDetachedMode()))) {
while ((numDisabledSourcesM < oldNumDisabledSources) && (oldNumDisabledSources > 0))
disabledSourcesM[--oldNumDisabledSources] = cSource::stNone;
while ((numDisabledFiltersM < oldNumDisabledFilters) && (oldNumDisabledFilters > 0))
@@ -543,6 +567,7 @@ void cSatipPluginSetup::Store(void)
{
// Store values into setup.conf
SetupStore("OperatingMode", operatingModeM);
SetupStore("TransportMode", transportModeM);
SetupStore("EnableCIExtension", ciExtensionM);
SetupStore("EnableEITScan", eitScanM);
StoreCicams("CICAM", cicamsM);
@@ -550,6 +575,7 @@ void cSatipPluginSetup::Store(void)
StoreFilters("DisabledFilters", disabledFilterIndexesM);
// Update global config
SatipConfig.SetOperatingMode(operatingModeM);
SatipConfig.SetTransportMode(transportModeM);
SatipConfig.SetCIExtension(ciExtensionM);
SatipConfig.SetEITScan(eitScanM);
for (int i = 0; i < MAX_CICAM_COUNT; ++i)

View File

@@ -15,9 +15,12 @@
class cSatipPluginSetup : public cMenuSetupPage
{
private:
bool detachedModeM;
int deviceCountM;
int operatingModeM;
int transportModeM;
const char *operatingModeTextsM[cSatipConfig::eOperatingModeCount];
const char *transportModeTextsM[cSatipConfig::eTransportModeCount];
int ciExtensionM;
int cicamsM[MAX_CICAM_COUNT];
const char *cicamTextsM[CA_SYSTEMS_TABLE_SIZE];

155
socket.c
View File

@@ -21,7 +21,11 @@
cSatipSocket::cSatipSocket()
: socketPortM(0),
socketDescM(-1)
socketDescM(-1),
isMulticastM(false),
useSsmM(false),
streamAddrM(htonl(INADDR_ANY)),
sourceAddrM(htonl(INADDR_ANY))
{
debug1("%s", __PRETTY_FUNCTION__);
memset(&sockAddrM, 0, sizeof(sockAddrM));
@@ -34,10 +38,17 @@ cSatipSocket::~cSatipSocket()
Close();
}
bool cSatipSocket::Open(const int portP)
bool cSatipSocket::Open(const int portP, const bool reuseP)
{
// If socket is there already and it is bound to a different port, it must
// be closed first
if (portP != socketPortM) {
debug1("%s (%d, %d) Socket tear-down", __PRETTY_FUNCTION__, portP, reuseP);
Close();
}
// Bind to the socket if it is not active already
if (socketDescM < 0) {
int yes;
socklen_t len = sizeof(sockAddrM);
// Create socket
socketDescM = socket(PF_INET, SOCK_DGRAM, 0);
@@ -46,9 +57,19 @@ bool cSatipSocket::Open(const int portP)
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;
yes = reuseP;
ERROR_IF_FUNC(setsockopt(socketDescM, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0,
"setsockopt(SO_REUSEADDR)", Close(), return false);
yes = reuseP;
#ifdef SO_REUSEPORT
ERROR_IF_FUNC(setsockopt(socketDescM, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0 && errno != ENOPROTOOPT,
"setsockopt(SO_REUSEPORT)", Close(), return false);
#endif
#ifndef __FreeBSD__
// Allow packet information to be fetched
ERROR_IF_FUNC(setsockopt(socketDescM, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0,
"setsockopt(IP_PKTINFO)", Close(), return false);
#endif // __FreeBSD__
// Bind socket
memset(&sockAddrM, 0, sizeof(sockAddrM));
sockAddrM.sin_family = AF_INET;
@@ -57,23 +78,41 @@ bool cSatipSocket::Open(const int portP)
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,
ERROR_IF_FUNC(getsockname(socketDescM, (struct sockaddr*)&sockAddrM, &len) < 0,
"getsockname()", Close(), return false);
socketPortM = ntohs(sockAddrM.sin_port);
isMulticastM = false;
}
debug1("%s (%d) socketPort=%d", __PRETTY_FUNCTION__, portP, socketPortM);
return true;
}
bool cSatipSocket::OpenMulticast(const int portP, const char *streamAddrP, const char *sourceAddrP)
{
debug1("%s (%d, %s, %s)", __PRETTY_FUNCTION__, portP, streamAddrP, sourceAddrP);
if (Open(portP)) {
CheckAddress(streamAddrP, &streamAddrM);
if (!isempty(sourceAddrP))
useSsmM = CheckAddress(sourceAddrP, &sourceAddrM);
return Join();
}
return false;
}
void cSatipSocket::Close(void)
{
debug1("%s sockerPort=%d", __PRETTY_FUNCTION__, socketPortM);
// Check if socket exists
if (socketDescM >= 0) {
Leave();
close(socketDescM);
socketDescM = -1;
socketPortM = 0;
memset(&sockAddrM, 0, sizeof(sockAddrM));
streamAddrM = htonl(INADDR_ANY);
sourceAddrM = htonl(INADDR_ANY);
isMulticastM = false;
useSsmM = false;
}
}
@@ -96,6 +135,96 @@ bool cSatipSocket::Flush(void)
return false;
}
bool cSatipSocket::CheckAddress(const char *addrP, in_addr_t *inAddrP)
{
if (inAddrP) {
// First try only the IP address
*inAddrP = inet_addr(addrP);
if (*inAddrP == htonl(INADDR_NONE)) {
debug1("%s (%s, ) Cannot convert to address", __PRETTY_FUNCTION__, addrP);
// It may be a host name, get the name
struct hostent *host = gethostbyname(addrP);
if (!host) {
char tmp[64];
error("gethostbyname() failed: %s is not valid address: %s", addrP,
strerror_r(h_errno, tmp, sizeof(tmp)));
return false;
}
*inAddrP = inet_addr(*host->h_addr_list);
}
return true;
}
return false;
}
bool cSatipSocket::Join(void)
{
debug1("%s", __PRETTY_FUNCTION__);
// Check if socket exists
if (socketDescM >= 0 && !isMulticastM) {
// Join a new multicast group
if (useSsmM) {
// Source-specific multicast (SSM) is used
struct group_source_req gsr;
struct sockaddr_in *grp;
struct sockaddr_in *src;
gsr.gsr_interface = 0; // if_nametoindex("any") ?
grp = (struct sockaddr_in*)&gsr.gsr_group;
grp->sin_family = AF_INET;
grp->sin_addr.s_addr = streamAddrM;
grp->sin_port = 0;
src = (struct sockaddr_in*)&gsr.gsr_source;
src->sin_family = AF_INET;
src->sin_addr.s_addr = sourceAddrM;
src->sin_port = 0;
ERROR_IF_RET(setsockopt(socketDescM, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &gsr, sizeof(gsr)) < 0, "setsockopt(MCAST_JOIN_SOURCE_GROUP)", return false);
}
else {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = streamAddrM;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
ERROR_IF_RET(setsockopt(socketDescM, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0, "setsockopt(IP_ADD_MEMBERSHIP)", return false);
}
// Update multicasting flag
isMulticastM = true;
}
return true;
}
bool cSatipSocket::Leave(void)
{
debug1("%s", __PRETTY_FUNCTION__);
// Check if socket exists
if (socketDescM >= 0 && isMulticastM) {
// Leave the existing multicast group
if (useSsmM) {
// Source-specific multicast (SSM) is used
struct group_source_req gsr;
struct sockaddr_in *grp;
struct sockaddr_in *src;
gsr.gsr_interface = 0; // if_nametoindex("any") ?
grp = (struct sockaddr_in*)&gsr.gsr_group;
grp->sin_family = AF_INET;
grp->sin_addr.s_addr = streamAddrM;
grp->sin_port = 0;
src = (struct sockaddr_in*)&gsr.gsr_source;
src->sin_family = AF_INET;
src->sin_addr.s_addr = sourceAddrM;
src->sin_port = 0;
ERROR_IF_RET(setsockopt(socketDescM, SOL_IP, MCAST_LEAVE_SOURCE_GROUP, &gsr, sizeof(gsr)) < 0, "setsockopt(MCAST_LEAVE_SOURCE_GROUP)", return false);
}
else {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = streamAddrM;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
ERROR_IF_RET(setsockopt(socketDescM, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0, "setsockopt(IP_DROP_MEMBERSHIP)", return false);
}
// Update multicasting flag
isMulticastM = false;
}
return true;
}
int cSatipSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
{
debug16("%s (, %d)", __PRETTY_FUNCTION__, bufferLenP);
@@ -126,8 +255,22 @@ int cSatipSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
if (socketDescM && bufferAddrP && (bufferLenP > 0))
len = (int)recvmsg(socketDescM, &msgh, MSG_DONTWAIT);
if (len > 0)
return len;
if (len > 0) {
#ifndef __FreeBSD__
if (isMulticastM) {
// Process auxiliary received data and validate source address
for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
struct in_pktinfo *i = (struct in_pktinfo *)CMSG_DATA(cmsg);
if ((i->ipi_addr.s_addr == streamAddrM) || (htonl(INADDR_ANY) == streamAddrM))
return len;
}
}
}
else
#endif // __FreeBSD__
return len;
}
} while (len > 0);
ERROR_IF_RET(len < 0 && errno != EAGAIN && errno != EWOULDBLOCK, "recvmsg()", return -1);
return 0;

View File

@@ -15,14 +15,23 @@ private:
int socketPortM;
int socketDescM;
struct sockaddr_in sockAddrM;
bool isMulticastM;
bool useSsmM;
in_addr_t streamAddrM;
in_addr_t sourceAddrM;
bool CheckAddress(const char *addrP, in_addr_t *inAddrP);
bool Join(void);
bool Leave(void);
public:
cSatipSocket();
virtual ~cSatipSocket();
bool Open(const int portP = 0);
bool Open(const int portP = 0, const bool reuseP = false);
bool OpenMulticast(const int portP, const char *streamAddrP, const char *sourceAddrP);
virtual void Close(void);
int Fd(void) { return socketDescM; }
int Port(void) { return socketPortM; }
bool IsMulticast(void) { return isMulticastM; }
bool IsOpen(void) { return (socketDescM >= 0); }
bool Flush(void);
int Read(unsigned char *bufferAddrP, unsigned int bufferLenP);

141
tuner.c
View File

@@ -25,6 +25,8 @@ cSatipTuner::cSatipTuner(cSatipDeviceIf &deviceP, unsigned int packetLenP)
rtcpM(*this),
streamAddrM(""),
streamParamM(""),
tnrParamM(""),
streamPortM(SATIP_DEFAULT_RTSP_PORT),
currentServerM(NULL, deviceP.GetId(), 0),
nextServerM(NULL, deviceP.GetId(), 0),
mutexM(),
@@ -50,12 +52,16 @@ cSatipTuner::cSatipTuner(cSatipDeviceIf &deviceP, unsigned int packetLenP)
debug1("%s (, %d) [device %d]", __PRETTY_FUNCTION__, packetLenP, deviceIdM);
// Open sockets
int i = 100;
int i = SatipConfig.GetPortRangeStart() ? SatipConfig.GetPortRangeStop() - SatipConfig.GetPortRangeStart() - 1 : 100;
int port = SatipConfig.GetPortRangeStart();
while (i-- > 0) {
if (rtpM.Open(0) && rtcpM.Open(rtpM.Port() + 1))
// RTP must use an even port number
if (rtpM.Open(port) && (rtpM.Port() % 2 == 0) && rtcpM.Open(rtpM.Port() + 1))
break;
rtpM.Close();
rtcpM.Close();
if (SatipConfig.GetPortRangeStart())
port += 2;
}
if ((rtpM.Port() <= 0) || (rtcpM.Port() <= 0)) {
error("Cannot open required RTP/RTCP ports [device %d]", deviceIdM);
@@ -91,6 +97,10 @@ cSatipTuner::~cSatipTuner()
void cSatipTuner::Action(void)
{
debug1("%s Entering [device %d]", __PRETTY_FUNCTION__, deviceIdM);
bool lastIdleStatus = false;
cTimeMs idleCheck(eIdleCheckTimeoutMs);
cTimeMs tuning(eTuningTimeoutMs);
reConnectM.Set(eConnectTimeoutMs);
// Do the thread loop
while (Running()) {
@@ -107,6 +117,7 @@ void cSatipTuner::Action(void)
case tsSet:
debug4("%s: tsSet [device %d]", __PRETTY_FUNCTION__, deviceIdM);
if (Connect()) {
tuning.Set(eTuningTimeoutMs);
RequestState(tsTuned, smInternal);
UpdatePids(true);
}
@@ -116,6 +127,8 @@ void cSatipTuner::Action(void)
case tsTuned:
debug4("%s: tsTuned [device %d]", __PRETTY_FUNCTION__, deviceIdM);
reConnectM.Set(eConnectTimeoutMs);
idleCheck.Set(eIdleCheckTimeoutMs);
lastIdleStatus = false;
// Read reception statistics via DESCRIBE and RTCP
if (hasLockM || ReadReceptionStatus()) {
// Quirk for devices without valid reception data
@@ -127,6 +140,10 @@ void cSatipTuner::Action(void)
if (hasLockM)
RequestState(tsLocked, smInternal);
}
else if (tuning.TimedOut()) {
error("Tuning timeout - retuning [device %d]", deviceIdM);
RequestState(tsSet, smInternal);
}
break;
case tsLocked:
debug4("%s: tsLocked [device %d]", __PRETTY_FUNCTION__, deviceIdM);
@@ -145,6 +162,16 @@ void cSatipTuner::Action(void)
RequestState(tsSet, smInternal);
break;
}
if (idleCheck.TimedOut()) {
bool currentIdleStatus = deviceM->IsIdle();
if (lastIdleStatus && currentIdleStatus) {
info("Idle timeout - releasing [device %d]", deviceIdM);
RequestState(tsRelease, smInternal);
}
lastIdleStatus = currentIdleStatus;
idleCheck.Set(eIdleCheckTimeoutMs);
break;
}
break;
default:
error("Unknown tuner status %d [device %d]", currentStateM, deviceIdM);
@@ -184,7 +211,8 @@ bool cSatipTuner::Connect(void)
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
if (!isempty(*streamAddrM)) {
cString connectionUri = cString::sprintf("rtsp://%s/", *streamAddrM);
cString connectionUri = GetBaseUrl(*streamAddrM, streamPortM);
tnrParamM = "";
// Just retune
if (streamIdM >= 0) {
cString uri = cString::sprintf("%sstream=%d?%s", *connectionUri, streamIdM, *streamParamM);
@@ -196,10 +224,11 @@ bool cSatipTuner::Connect(void)
}
else if (rtspM.Options(*connectionUri)) {
cString uri = cString::sprintf("%s?%s", *connectionUri, *streamParamM);
bool useTcp = SatipConfig.IsTransportModeRtpOverTcp() && nextServerM.IsValid() && nextServerM.IsQuirk(cSatipServer::eSatipQuirkRtpOverTcp);
// Flush any old content
//rtpM.Flush();
//rtcpM.Flush();
if (rtspM.Setup(*uri, rtpM.Port(), rtcpM.Port())) {
if (rtspM.Setup(*uri, rtpM.Port(), rtcpM.Port(), useTcp)) {
keepAliveM.Set(timeoutM);
if (nextServerM.IsValid()) {
currentServerM = nextServerM;
@@ -209,8 +238,7 @@ bool cSatipTuner::Connect(void)
return true;
}
}
else
rtspM.Reset();
rtspM.Reset();
streamIdM = -1;
error("Connect failed [device %d]", deviceIdM);
}
@@ -224,7 +252,7 @@ bool cSatipTuner::Disconnect(void)
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
if (!isempty(*streamAddrM) && (streamIdM >= 0)) {
cString uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
cString uri = cString::sprintf("%sstream=%d", *GetBaseUrl(*streamAddrM, streamPortM), streamIdM);
rtspM.Teardown(*uri);
// some devices requires a teardown for TCP connection also
rtspM.Reset();
@@ -269,6 +297,11 @@ void cSatipTuner::ProcessVideoData(u_char *bufferP, int lengthP)
reConnectM.Set(eConnectTimeoutMs);
}
void cSatipTuner::ProcessRtpData(u_char *bufferP, int lengthP)
{
rtpM.Process(bufferP, lengthP);
}
void cSatipTuner::ProcessApplicationData(u_char *bufferP, int lengthP)
{
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, lengthP, deviceIdM);
@@ -322,6 +355,11 @@ void cSatipTuner::ProcessApplicationData(u_char *bufferP, int lengthP)
reConnectM.Set(eConnectTimeoutMs);
}
void cSatipTuner::ProcessRtcpData(u_char *bufferP, int lengthP)
{
rtcpM.Process(bufferP, lengthP);
}
void cSatipTuner::SetStreamId(int streamIdP)
{
cMutexLock MutexLock(&mutexM);
@@ -339,6 +377,47 @@ void cSatipTuner::SetSessionTimeout(const char *sessionP, int timeoutP)
timeoutM = (timeoutP > eMinKeepAliveIntervalMs) ? timeoutP : eMinKeepAliveIntervalMs;
}
void cSatipTuner::SetupTransport(int rtpPortP, int rtcpPortP, const char *streamAddrP, const char *sourceAddrP)
{
cMutexLock MutexLock(&mutexM);
debug1("%s (%d, %d, %s, %s) [device %d]", __PRETTY_FUNCTION__, rtpPortP, rtcpPortP, streamAddrP, sourceAddrP, deviceIdM);
bool multicast = !isempty(streamAddrP);
// Adapt RTP to any transport media change
if (multicast != rtpM.IsMulticast() || rtpPortP != rtpM.Port()) {
cSatipPoller::GetInstance()->Unregister(rtpM);
rtpM.Close();
if (rtpPortP >= 0) {
if (multicast)
rtpM.OpenMulticast(rtpPortP, streamAddrP, sourceAddrP);
else
rtpM.Open(rtpPortP);
cSatipPoller::GetInstance()->Register(rtpM);
}
}
// Adapt RTCP to any transport media change
if (multicast != rtcpM.IsMulticast() || rtcpPortP != rtcpM.Port()) {
cSatipPoller::GetInstance()->Unregister(rtcpM);
rtcpM.Close();
if (rtcpPortP >= 0) {
if (multicast)
rtcpM.OpenMulticast(rtpPortP, streamAddrP, sourceAddrP);
else
rtcpM.Open(rtpPortP);
cSatipPoller::GetInstance()->Register(rtcpM);
}
}
}
cString cSatipTuner::GetBaseUrl(const char *addressP, const int portP)
{
debug16("%s (%s, %d) [device %d]", __PRETTY_FUNCTION__, addressP, portP, deviceIdM);
if (portP != SATIP_DEFAULT_RTSP_PORT)
return cString::sprintf("rtsp://%s:%d/", addressP, portP);
return cString::sprintf("rtsp://%s/", addressP);
}
int cSatipTuner::GetId(void)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
@@ -355,6 +434,10 @@ bool cSatipTuner::SetSource(cSatipServer *serverP, const int transponderP, const
// Update stream address and parameter
streamAddrM = rtspM.RtspUnescapeString(*nextServerM.GetAddress());
streamParamM = rtspM.RtspUnescapeString(parameterP);
streamPortM = nextServerM.GetPort();
// Modify parameter if required
if (nextServerM.IsQuirk(cSatipServer::eSatipQuirkForcePilot) && strstr(parameterP, "msys=dvbs2") && !strstr(parameterP, "plts="))
streamParamM = rtspM.RtspUnescapeString(*cString::sprintf("%s&plts=on", parameterP));
// Reconnect
RequestState(tsSet, smExternal);
}
@@ -393,7 +476,7 @@ bool cSatipTuner::UpdatePids(bool forceP)
cMutexLock MutexLock(&mutexM);
if (((forceP && pidsM.Size()) || (pidUpdateCacheM.TimedOut() && (addPidsM.Size() || delPidsM.Size()))) &&
!isempty(*streamAddrM) && (streamIdM > 0)) {
cString uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
cString uri = cString::sprintf("%sstream=%d", *GetBaseUrl(*streamAddrM, streamPortM), streamIdM);
bool useci = (SatipConfig.GetCIExtension() && currentServerM.HasCI());
bool usedummy = currentServerM.IsQuirk(cSatipServer::eSatipQuirkPlayPids);
if (forceP || usedummy) {
@@ -409,20 +492,30 @@ bool cSatipTuner::UpdatePids(bool forceP)
uri = cString::sprintf("%s%sdelpids=%s", *uri, addPidsM.Size() ? "&" : "?", *delPidsM.ListPids());
}
if (useci) {
// CI extension parameters:
// - x_pmt : specifies the PMT of the service you want the CI to decode
// - x_ci : specfies which CI slot (1..n) to use
// value 0 releases the CI slot
// CI slot released automatically if the stream is released,
// but not when used retuning to another channel
int pid = deviceM->GetPmtPid();
if ((pid > 0) && (pid != pmtPidM)) {
int slot = deviceM->GetCISlot();
uri = cString::sprintf("%s&x_pmt=%d", *uri, pid);
if (slot > 0)
uri = cString::sprintf("%s&x_ci=%d", *uri, slot);
if (currentServerM.IsQuirk(cSatipServer::eSatipQuirkCiXpmt)) {
// CI extension parameters:
// - x_pmt : specifies the PMT of the service you want the CI to decode
// - x_ci : specfies which CI slot (1..n) to use
// value 0 releases the CI slot
// CI slot released automatically if the stream is released,
// but not when used retuning to another channel
int pid = deviceM->GetPmtPid();
if ((pid > 0) && (pid != pmtPidM)) {
int slot = deviceM->GetCISlot();
uri = cString::sprintf("%s&x_pmt=%d", *uri, pid);
if (slot > 0)
uri = cString::sprintf("%s&x_ci=%d", *uri, slot);
}
pmtPidM = pid;
}
else if (currentServerM.IsQuirk(cSatipServer::eSatipQuirkCiTnr)) {
// CI extension parameters:
// - tnr : specifies a channel config entry
cString param = deviceM->GetTnrParameterString();
if (!isempty(*param) && strcmp(*tnrParamM, *param) != 0)
uri = cString::sprintf("%s&tnr=%s", *uri, *param);
tnrParamM = param;
}
pmtPidM = pid;
}
pidUpdateCacheM.Set(ePidUpdateIntervalMs);
if (!rtspM.Play(*uri))
@@ -443,7 +536,7 @@ bool cSatipTuner::KeepAlive(bool forceP)
forceP = true;
}
if (forceP && !isempty(*streamAddrM)) {
cString uri = cString::sprintf("rtsp://%s/", *streamAddrM);
cString uri = GetBaseUrl(*streamAddrM, streamPortM);
if (!rtspM.Options(*uri))
return false;
}
@@ -460,7 +553,7 @@ bool cSatipTuner::ReadReceptionStatus(bool forceP)
forceP = true;
}
if (forceP && !isempty(*streamAddrM) && (streamIdM > 0)) {
cString uri = cString::sprintf("rtsp://%s/stream=%d", *streamAddrM, streamIdM);
cString uri = cString::sprintf("%sstream=%d", *GetBaseUrl(*streamAddrM, streamPortM), streamIdM);
if (rtspM.Describe(*uri))
return true;
}
@@ -595,5 +688,5 @@ cString cSatipTuner::GetSignalStatus(void)
cString cSatipTuner::GetInformation(void)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM);
return (currentStateM >= tsTuned) ? cString::sprintf("rtsp://%s/?%s [stream=%d]", *streamAddrM, *streamParamM, streamIdM) : "connection failed";
return (currentStateM >= tsTuned) ? cString::sprintf("%s?%s (%s) [stream=%d]", *GetBaseUrl(*streamAddrM, streamPortM), *streamParamM, *rtspM.GetActiveMode(), streamIdM) : "connection failed";
}

View File

@@ -69,6 +69,7 @@ public:
void Set(cSatipServer *serverP, const int transponderP) { serverM = serverP; transponderM = transponderP; }
void Reset(void) { serverM = NULL; transponderM = 0; }
cString GetAddress(void) { return serverM ? cSatipDiscover::GetInstance()->GetServerAddress(serverM) : ""; }
int GetPort(void) { return serverM ? cSatipDiscover::GetInstance()->GetServerPort(serverM) : SATIP_DEFAULT_RTSP_PORT; }
cString GetInfo(void) { return cString::sprintf("server=%s deviceid=%d transponder=%d", serverM ? "assigned" : "null", deviceIdM, transponderM); }
};
@@ -83,6 +84,8 @@ private:
eStatusUpdateTimeoutMs = 1000, // in milliseconds
ePidUpdateIntervalMs = 250, // in milliseconds
eConnectTimeoutMs = 5000, // in milliseconds
eIdleCheckTimeoutMs = 15000, // in milliseconds
eTuningTimeoutMs = 20000, // in milliseconds
eMinKeepAliveIntervalMs = 30000 // in milliseconds
};
enum eTunerState { tsIdle, tsRelease, tsSet, tsTuned, tsLocked };
@@ -96,6 +99,8 @@ private:
cSatipRtcp rtcpM;
cString streamAddrM;
cString streamParamM;
cString tnrParamM;
int streamPortM;
cSatipTunerServer currentServerM;
cSatipTunerServer nextServerM;
cMutex mutexM;
@@ -128,6 +133,7 @@ private:
bool RequestState(eTunerState stateP, eStateMode modeP);
const char *StateModeString(eStateMode modeP);
const char *TunerStateString(eTunerState stateP);
cString GetBaseUrl(const char *addressP, const int portP);
protected:
virtual void Action(void);
@@ -151,8 +157,11 @@ public:
public:
virtual void ProcessVideoData(u_char *bufferP, int lengthP);
virtual void ProcessApplicationData(u_char *bufferP, int lengthP);
virtual void ProcessRtpData(u_char *bufferP, int lengthP);
virtual void ProcessRtcpData(u_char *bufferP, int lengthP);
virtual void SetStreamId(int streamIdP);
virtual void SetSessionTimeout(const char *sessionP, int timeoutP);
virtual void SetupTransport(int rtpPortP, int rtcpPortP, const char *streamAddrP, const char *sourceAddrP);
virtual int GetId(void);
};

View File

@@ -14,12 +14,15 @@ public:
virtual ~cSatipTunerIf() {}
virtual void ProcessVideoData(u_char *bufferP, int lengthP) = 0;
virtual void ProcessApplicationData(u_char *bufferP, int lengthP) = 0;
virtual void ProcessRtpData(u_char *bufferP, int lengthP) = 0;
virtual void ProcessRtcpData(u_char *bufferP, int lengthP) = 0;
virtual void SetStreamId(int streamIdP) = 0;
virtual void SetSessionTimeout(const char *sessionP, int timeoutP) = 0;
virtual void SetupTransport(int rtpPortP, int rtcpPortP, const char *streamAddrP, const char *sourceAddrP) = 0;
virtual int GetId(void) = 0;
private:
cSatipTunerIf(const cSatipTunerIf&);
explicit cSatipTunerIf(const cSatipTunerIf&);
cSatipTunerIf& operator=(const cSatipTunerIf&);
};