From 7aef2a3dff007e5c1539ec73f4cdeed602effde2 Mon Sep 17 00:00:00 2001 From: Rolf Ahrenberg Date: Thu, 10 Nov 2016 16:47:10 +0200 Subject: [PATCH 1/6] Update README. --- README | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README b/README index 0bff7a9..5375598 100644 --- a/README +++ b/README @@ -131,6 +131,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. From e6c9776ec9cb5004cf150e7f916f973a69e3c897 Mon Sep 17 00:00:00 2001 From: Rolf Ahrenberg Date: Thu, 10 Nov 2016 16:47:30 +0200 Subject: [PATCH 2/6] Add a new ForcePilot quirk. --- device.c | 2 +- server.c | 9 +++++++++ server.h | 1 + tuner.c | 3 +++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/device.c b/device.c index da68834..fb58d7f 100644 --- a/device.c +++ b/device.c @@ -140,7 +140,7 @@ cString cSatipDevice::GetGeneralInformation(void) #if defined(APIVERSNUM) && APIVERSNUM >= 20301 LOCK_CHANNELS_READ; #endif - return cString::sprintf("SAT>IP device: %d\nCardIndex: %d\nStream: %s\nSignal: %s\nStream bitrate: %s\n%sChannel: %s", + 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() : "", diff --git a/server.c b/server.c index bfaeef4..01613a4 100644 --- a/server.c +++ b/server.c @@ -147,6 +147,13 @@ cSatipServer::cSatipServer(const char *addressP, const int portP, const char *mo 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; } if ((quirkM & eSatipQuirkMask) & eSatipQuirkSessionId) quirksM = cString::sprintf("%s%sSessionId", *quirksM, isempty(*quirksM) ? "" : ","); @@ -160,6 +167,8 @@ cSatipServer::cSatipServer(const char *addressP, const int portP, const char *mo 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 diff --git a/server.h b/server.h index 1ce46e3..ea9ae2d 100644 --- a/server.h +++ b/server.h @@ -81,6 +81,7 @@ public: eSatipQuirkRtpOverTcp = 0x08, eSatipQuirkCiXpmt = 0x10, eSatipQuirkCiTnr = 0x20, + eSatipQuirkForcePilot = 0x40, eSatipQuirkMask = 0xFF }; cSatipServer(const char *addressP, const int portP, const char *modelP, const char *filtersP, const char *descriptionP, const int quirkP); diff --git a/tuner.c b/tuner.c index bef87d4..d556da9 100644 --- a/tuner.c +++ b/tuner.c @@ -393,6 +393,9 @@ bool cSatipTuner::SetSource(cSatipServer *serverP, const int transponderP, const 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); } From ee6ac0d48a4e20754fe2f492ac4e625b4ca077b9 Mon Sep 17 00:00:00 2001 From: Rolf Ahrenberg Date: Sun, 11 Dec 2016 01:18:12 +0200 Subject: [PATCH 3/6] Add a preliminary multicast support. --- README | 5 ++ config.c | 2 +- config.h | 15 ++++-- po/ca_ES.po | 15 ++++-- po/de_DE.po | 15 ++++-- po/es_ES.po | 15 ++++-- po/fi_FI.po | 21 +++++--- rtsp.c | 30 ++++++++--- rtsp.h | 2 - satip.c | 11 ++-- setup.c | 13 +++-- setup.h | 3 +- socket.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++-- socket.h | 9 ++++ tuner.c | 34 ++++++++++-- tuner.h | 1 + tunerif.h | 1 + 17 files changed, 294 insertions(+), 43 deletions(-) diff --git a/README b/README index 5375598..0d414ab 100644 --- a/README +++ b/README @@ -116,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. diff --git a/config.c b/config.c index e077e5c..6743e9c 100644 --- a/config.c +++ b/config.c @@ -19,7 +19,7 @@ cSatipConfig::cSatipConfig(void) useBytesM(1), portRangeStartM(0), portRangeStopM(0), - useRtpOverTcpM(false), + transportModeM(eTransportModeUnicast), detachedModeM(false), disableServerQuirksM(false), useSingleModelServersM(false) diff --git a/config.h b/config.h index 87a469e..45503f6 100644 --- a/config.h +++ b/config.h @@ -21,7 +21,7 @@ private: unsigned int useBytesM; unsigned int portRangeStartM; unsigned int portRangeStopM; - bool useRtpOverTcpM; + unsigned int transportModeM; bool detachedModeM; bool disableServerQuirksM; bool useSingleModelServersM; @@ -37,6 +37,12 @@ public: eOperatingModeHigh, eOperatingModeCount }; + enum eTransportMode { + eTransportModeUnicast = 0, + eTransportModeMulticast, + eTransportModeRtpOverTcp, + eTransportModeCount + }; enum eTraceMode { eTraceModeNormal = 0x0000, eTraceModeDebug1 = 0x0001, @@ -70,7 +76,10 @@ public: int GetCICAM(unsigned int indexP) const; unsigned int GetEITScan(void) const { return eitScanM; } unsigned int GetUseBytes(void) const { return useBytesM; } - bool GetUseRtpOverTcp(void) const { return useRtpOverTcpM; } + 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; } @@ -87,7 +96,7 @@ public: void SetCICAM(unsigned int indexP, int cicamP); void SetEITScan(unsigned int onOffP) { eitScanM = onOffP; } void SetUseBytes(unsigned int onOffP) { useBytesM = onOffP; } - void SetUseRtpOverTcp(bool onOffP) { useRtpOverTcpM = 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; } diff --git a/po/ca_ES.po b/po/ca_ES.po index 27477dc..e399f4b 100644 --- a/po/ca_ES.po +++ b/po/ca_ES.po @@ -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,13 +187,13 @@ msgstr "Filtra" msgid "Define an ill-behaving filter to be blacklisted." msgstr "Definir un filtre mal comportar a la llista negra." -msgid "Use RTP-over-TCP mode" +msgid "Transport mode" msgstr "" msgid "" -"Define whether the RTP-over-TCP mode shall be used.\n" +"Define which transport mode shall be used.\n" "\n" -"This setting affects only SAT>IP devices supporting the feature." +"Unicast, Multicast, RTP-over-TCP" msgstr "" msgid "Active SAT>IP servers:" diff --git a/po/de_DE.po b/po/de_DE.po index 83e6a18..c10930a 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -85,6 +85,15 @@ msgstr "normal" msgid "high" msgstr "hoch" +msgid "Unicast" +msgstr "" + +msgid "Multicast" +msgstr "" + +msgid "RTP-over-TCP" +msgstr "" + msgid "Button$Devices" msgstr "Geräte" @@ -178,13 +187,13 @@ msgstr "Filter" msgid "Define an ill-behaving filter to be blacklisted." msgstr "Bestimme einen fehlerhaften Filter der ausgeblendet werden soll." -msgid "Use RTP-over-TCP mode" +msgid "Transport mode" msgstr "" msgid "" -"Define whether the RTP-over-TCP mode shall be used.\n" +"Define which transport mode shall be used.\n" "\n" -"This setting affects only SAT>IP devices supporting the feature." +"Unicast, Multicast, RTP-over-TCP" msgstr "" msgid "Active SAT>IP servers:" diff --git a/po/es_ES.po b/po/es_ES.po index a0c33f4..53c88b9 100644 --- a/po/es_ES.po +++ b/po/es_ES.po @@ -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,13 +187,13 @@ msgstr "Filtra" msgid "Define an ill-behaving filter to be blacklisted." msgstr "Define un filtro para poner en la lista negra." -msgid "Use RTP-over-TCP mode" +msgid "Transport mode" msgstr "" msgid "" -"Define whether the RTP-over-TCP mode shall be used.\n" +"Define which transport mode shall be used.\n" "\n" -"This setting affects only SAT>IP devices supporting the feature." +"Unicast, Multicast, RTP-over-TCP" msgstr "" msgid "Active SAT>IP servers:" diff --git a/po/fi_FI.po b/po/fi_FI.po index 18fc5e6..08abfa5 100644 --- a/po/fi_FI.po +++ b/po/fi_FI.po @@ -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,17 +186,17 @@ 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 "Use RTP-over-TCP mode" -msgstr "Käytä RTP-over-TCP -moodia" +msgid "Transport mode" +msgstr "Siirtoyhteystapa" msgid "" -"Define whether the RTP-over-TCP mode shall be used.\n" +"Define which transport mode shall be used.\n" "\n" -"This setting affects only SAT>IP devices supporting the feature." +"Unicast, Multicast, RTP-over-TCP" msgstr "" -"Määrittele RTP-over-TCP -moodin käyttöönotto.\n" +"Määrittele käytettävä siirtoyhteystapa.\n" "\n" -"Tämä asetus vaikuttaa vain SAT>IP-laitteisiin, jotka tukevat kyseistä ominaisuutta." +"Unicast, Multicast, RTP-over-TCP" msgid "Active SAT>IP servers:" msgstr "Aktiiviset SAT>IP-palvelimet:" diff --git a/rtsp.c b/rtsp.c index 0cbc460..5212f28 100644 --- a/rtsp.c +++ b/rtsp.c @@ -17,7 +17,6 @@ cSatipRtsp::cSatipRtsp(cSatipTunerIf &tunerP) : tunerM(tunerP), headerBufferM(), dataBufferM(), - modeM(cmUnicast), handleM(NULL), headerListM(NULL), errorNoMoreM(""), @@ -182,13 +181,12 @@ bool cSatipRtsp::Setup(const char *uriP, int rtpPortP, int rtcpPortP, bool useTc cTimeMs processing(0); CURLcode res = CURLE_OK; - switch (modeM) { - case cmMulticast: - // RTP/AVP;multicast;destination=;port=-;ttl= - transport = cString::sprintf("RTP/AVP;multicast;port=%d-%d", rtpPortP, rtcpPortP); + switch (SatipConfig.GetTransportMode()) { + case cSatipConfig::eTransportModeMulticast: + // RTP/AVP;multicast;destination=;port=-;ttl=[;source=] + transport = cString::sprintf("RTP/AVP;multicast"); break; default: - case cmUnicast: // RTP/AVP;unicast;client_port=- // RTP/AVP/TCP;unicast;client_port=- transport = cString::sprintf("RTP/AVP%s;unicast;client_port=%d-%d", useTcpP ? "/TCP" : "", rtpPortP, rtcpPortP); @@ -352,6 +350,26 @@ void cSatipRtsp::ParseHeader(void) tunerM.SetSessionTimeout(skipspace(session), -1); FREE_POINTER(session); } + else if (strstr(r, "Transport:")) { + int rtp = -1, rtcp = -1, ttl = -1; + char *tmp = NULL, *destination = NULL, *source = NULL; + if (sscanf(r, "Transport:%m[^;];unicast;client_port=%11d-%11d", &tmp, &rtp, &rtcp) == 3) + 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) + tunerM.SetupTransport(rtp, rtcp, destination, source); + // TODO: else if (sscanf(r, "Transport:%m[^;];interleaved=%11d-%11d", &tmp, &rtp, &rtcp) == 3) + // Stream data such as RTP packets is encapsulated by an ASCII dollar + // sign (24 hexadecimal), followed by a one-byte channel identifier, + // followed by the length of the encapsulated binary data as a binary, + // two-byte integer in network byte order. The stream data follows + // immediately afterwards, without a CRLF, but including the upper-layer + // protocol headers. Each $ block contains exactly one upper-layer + // protocol data unit, e.g., one RTP packet. + FREE_POINTER(tmp); + FREE_POINTER(destination); + FREE_POINTER(source); + } r = strtok_r(NULL, "\r\n", &s); } } diff --git a/rtsp.h b/rtsp.h index 4cefe6c..b6dc237 100644 --- a/rtsp.h +++ b/rtsp.h @@ -27,12 +27,10 @@ private: enum { eConnectTimeoutMs = 1500, // in milliseconds }; - enum eCommunicationMode { cmUnicast, cmMulticast }; cSatipTunerIf &tunerM; cSatipMemoryBuffer headerBufferM; cSatipMemoryBuffer dataBufferM; - eCommunicationMode modeM; CURL *handleM; struct curl_slist *headerListM; cString errorNoMoreM; diff --git a/satip.c b/satip.c index 78192b6..4def128 100644 --- a/satip.c +++ b/satip.c @@ -305,7 +305,12 @@ void cPluginSatip::ParsePortRange(const char *paramP) rangeStart = 0; rangeStop = 0; } - if (rangeStop - rangeStart + 1 < deviceCountM * 2) { + 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; @@ -401,8 +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, "UseRtpOverTcp")) - SatipConfig.SetUseRtpOverTcp(atoi(valueP)); + else if (!strcasecmp(nameP, "TransportMode")) + SatipConfig.SetTransportMode(atoi(valueP)); else return false; return true; diff --git a/setup.c b/setup.c index 1900263..1ed5530 100644 --- a/setup.c +++ b/setup.c @@ -344,8 +344,8 @@ eOSState cSatipMenuInfo::ProcessKey(eKeys keyP) cSatipPluginSetup::cSatipPluginSetup() : detachedModeM(SatipConfig.GetDetachedMode()), deviceCountM(0), - useRtpOverTcpM(SatipConfig.GetUseRtpOverTcp()), operatingModeM(SatipConfig.GetOperatingMode()), + transportModeM(SatipConfig.GetTransportMode()), ciExtensionM(SatipConfig.GetCIExtension()), eitScanM(SatipConfig.GetEITScan()), numDisabledSourcesM(SatipConfig.GetDisabledSourcesCount()), @@ -356,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) @@ -413,8 +416,8 @@ void cSatipPluginSetup::Setup(void) helpM.Append(tr("Define an ill-behaving filter to be blacklisted.")); } } - Add(new cMenuEditBoolItem(tr("Use RTP-over-TCP mode"), &useRtpOverTcpM)); - helpM.Append(tr("Define whether the RTP-over-TCP mode shall be used.\n\nThis setting affects only SAT>IP devices supporting the feature.")); + Add(new cMenuEditStraItem(tr("Transport mode"), &transportModeM, ELEMENTS(transportModeTextsM) - 1, transportModeTextsM)); // TODO: RTP-over-TCP + 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(""); @@ -563,16 +566,16 @@ void cSatipPluginSetup::StoreFilters(const char *nameP, int *valuesP) void cSatipPluginSetup::Store(void) { // Store values into setup.conf - SetupStore("UseRtpOverTcp", useRtpOverTcpM); SetupStore("OperatingMode", operatingModeM); + SetupStore("TransportMode", transportModeM); SetupStore("EnableCIExtension", ciExtensionM); SetupStore("EnableEITScan", eitScanM); StoreCicams("CICAM", cicamsM); StoreSources("DisabledSources", disabledSourcesM); StoreFilters("DisabledFilters", disabledFilterIndexesM); // Update global config - SatipConfig.SetUseRtpOverTcp(useRtpOverTcpM); SatipConfig.SetOperatingMode(operatingModeM); + SatipConfig.SetTransportMode(transportModeM); SatipConfig.SetCIExtension(ciExtensionM); SatipConfig.SetEITScan(eitScanM); for (int i = 0; i < MAX_CICAM_COUNT; ++i) diff --git a/setup.h b/setup.h index b00137a..e97ce2f 100644 --- a/setup.h +++ b/setup.h @@ -17,9 +17,10 @@ class cSatipPluginSetup : public cMenuSetupPage private: bool detachedModeM; int deviceCountM; - int useRtpOverTcpM; 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]; diff --git a/socket.c b/socket.c index 2e31b10..4cb19f3 100644 --- a/socket.c +++ b/socket.c @@ -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)); @@ -36,6 +40,12 @@ cSatipSocket::~cSatipSocket() 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; @@ -55,6 +65,11 @@ bool cSatipSocket::Open(const int portP, const bool reuseP) 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; @@ -63,23 +78,41 @@ bool cSatipSocket::Open(const int portP, const bool reuseP) 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; } } @@ -102,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); @@ -132,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; diff --git a/socket.h b/socket.h index a3228bf..12d1ac4 100644 --- a/socket.h +++ b/socket.h @@ -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, 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); diff --git a/tuner.c b/tuner.c index d556da9..19e558f 100644 --- a/tuner.c +++ b/tuner.c @@ -55,12 +55,13 @@ cSatipTuner::cSatipTuner(cSatipDeviceIf &deviceP, unsigned int packetLenP) int i = SatipConfig.GetPortRangeStart() ? SatipConfig.GetPortRangeStop() - SatipConfig.GetPortRangeStart() - 1 : 100; int port = SatipConfig.GetPortRangeStart(); while (i-- > 0) { - if (rtpM.Open(port) && 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; + port += 2; } if ((rtpM.Port() <= 0) || (rtcpM.Port() <= 0)) { error("Cannot open required RTP/RTCP ports [device %d]", deviceIdM); @@ -223,7 +224,7 @@ bool cSatipTuner::Connect(void) } else if (rtspM.Options(*connectionUri)) { cString uri = cString::sprintf("%s?%s", *connectionUri, *streamParamM); - bool useTcp = SatipConfig.GetUseRtpOverTcp() && nextServerM.IsValid() && nextServerM.IsQuirk(cSatipServer::eSatipQuirkRtpOverTcp); + bool useTcp = SatipConfig.IsTransportModeRtpOverTcp() && nextServerM.IsValid() && nextServerM.IsQuirk(cSatipServer::eSatipQuirkRtpOverTcp); // Flush any old content //rtpM.Flush(); //rtcpM.Flush(); @@ -366,6 +367,33 @@ 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 (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 (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); diff --git a/tuner.h b/tuner.h index b70b1ac..1ab3cf2 100644 --- a/tuner.h +++ b/tuner.h @@ -159,6 +159,7 @@ public: virtual void ProcessApplicationData(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); }; diff --git a/tunerif.h b/tunerif.h index f5ea6a6..f3e80ac 100644 --- a/tunerif.h +++ b/tunerif.h @@ -16,6 +16,7 @@ public: virtual void ProcessApplicationData(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: From 3b89dd4b014dd2ec5d093fd277126d92ce169cfa Mon Sep 17 00:00:00 2001 From: Frank Neumann Date: Wed, 14 Dec 2016 21:52:44 +0100 Subject: [PATCH 4/6] Update German translations. --- po/ca_ES.po | 2 +- po/de_DE.po | 23 +++++++++++++---------- po/es_ES.po | 2 +- po/fi_FI.po | 4 ++-- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/po/ca_ES.po b/po/ca_ES.po index e399f4b..f9ad8fd 100644 --- a/po/ca_ES.po +++ b/po/ca_ES.po @@ -1,5 +1,5 @@ # 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 # diff --git a/po/de_DE.po b/po/de_DE.po index c10930a..5b995fc 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -1,7 +1,7 @@ # 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 "" @@ -86,19 +86,19 @@ msgid "high" msgstr "hoch" msgid "Unicast" -msgstr "" +msgstr "Unicast" msgid "Multicast" -msgstr "" +msgstr "Multicast" msgid "RTP-over-TCP" -msgstr "" +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" @@ -108,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" @@ -179,22 +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 "" +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:" diff --git a/po/es_ES.po b/po/es_ES.po index 53c88b9..fecbeb4 100644 --- a/po/es_ES.po +++ b/po/es_ES.po @@ -1,5 +1,5 @@ # 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 # diff --git a/po/fi_FI.po b/po/fi_FI.po index 08abfa5..9ef3c73 100644 --- a/po/fi_FI.po +++ b/po/fi_FI.po @@ -1,7 +1,7 @@ # 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 "" From 99e366b261865903d1ceffa174dea1107257d9c7 Mon Sep 17 00:00:00 2001 From: Rolf Ahrenberg Date: Thu, 15 Dec 2016 08:49:47 +0200 Subject: [PATCH 5/6] Add a preliminary RTP-over-TCP support. --- msearch.c | 5 ++++ msearch.h | 1 + pollerif.h | 1 + rtcp.c | 10 +++++++ rtcp.h | 1 + rtp.c | 16 ++++++++++ rtp.h | 1 + rtsp.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++-------- rtsp.h | 8 ++++- setup.c | 2 +- tuner.c | 36 ++++++++++++++++------- tuner.h | 2 ++ tunerif.h | 2 ++ 13 files changed, 146 insertions(+), 25 deletions(-) diff --git a/msearch.c b/msearch.c index c7bf224..b137a05 100644 --- a/msearch.c +++ b/msearch.c @@ -100,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"; diff --git a/msearch.h b/msearch.h index db00c86..73bd375 100644 --- a/msearch.h +++ b/msearch.h @@ -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; }; diff --git a/pollerif.h b/pollerif.h index 3447871..67d33fb 100644 --- a/pollerif.h +++ b/pollerif.h @@ -14,6 +14,7 @@ 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: diff --git a/rtcp.c b/rtcp.c index c815287..a89640c 100644 --- a/rtcp.c +++ b/rtcp.c @@ -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()); diff --git a/rtcp.h b/rtcp.h index 59fb176..898b310 100644 --- a/rtcp.h +++ b/rtcp.h @@ -30,6 +30,7 @@ public: public: virtual int GetFd(void); virtual void Process(void); + virtual void Process(unsigned char *dataP, int lengthP); virtual cString ToString(void) const; }; diff --git a/rtp.c b/rtp.c index 0be033a..03ee267 100644 --- a/rtp.c +++ b/rtp.c @@ -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()); diff --git a/rtp.h b/rtp.h index 104a49c..56071f9 100644 --- a/rtp.h +++ b/rtp.h @@ -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; }; diff --git a/rtsp.c b/rtsp.c index 5212f28..87c194a 100644 --- a/rtsp.c +++ b/rtsp.c @@ -21,7 +21,10 @@ cSatipRtsp::cSatipRtsp(cSatipTunerIf &tunerP) headerListM(NULL), errorNoMoreM(""), errorOutOfRangeM(""), - errorCheckSyntaxM("") + errorCheckSyntaxM(""), + modeM(cSatipConfig::eTransportModeUnicast), + interleavedRtpIdM(0), + interleavedRtcpIdM(1) { debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId()); Create(); @@ -57,6 +60,30 @@ size_t cSatipRtsp::DataCallback(char *ptrP, size_t sizeP, size_t nmembP, void *d return len; } +size_t cSatipRtsp::InterleaveCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP) +{ + cSatipRtsp *obj = reinterpret_cast(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; +} + int cSatipRtsp::DebugCallback(CURL *handleP, curl_infotype typeP, char *dataP, size_t sizeP, void *userPtrP) { cSatipRtsp *obj = reinterpret_cast(userPtrP); @@ -86,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()); @@ -122,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())); } @@ -189,7 +234,10 @@ bool cSatipRtsp::Setup(const char *uriP, int rtpPortP, int rtcpPortP, bool useTc default: // RTP/AVP;unicast;client_port=- // RTP/AVP/TCP;unicast;client_port=- - transport = cString::sprintf("RTP/AVP%s;unicast;client_port=%d-%d", useTcpP ? "/TCP" : "", rtpPortP, rtcpPortP); + 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; } @@ -202,6 +250,9 @@ bool cSatipRtsp::Setup(const char *uriP, int rtpPortP, int rtcpPortP, bool useTc 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); @@ -309,6 +360,8 @@ bool cSatipRtsp::Teardown(const char *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); @@ -351,21 +404,30 @@ void cSatipRtsp::ParseHeader(void) 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; - if (sscanf(r, "Transport:%m[^;];unicast;client_port=%11d-%11d", &tmp, &rtp, &rtcp) == 3) + 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) + 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); - // TODO: else if (sscanf(r, "Transport:%m[^;];interleaved=%11d-%11d", &tmp, &rtp, &rtcp) == 3) - // Stream data such as RTP packets is encapsulated by an ASCII dollar - // sign (24 hexadecimal), followed by a one-byte channel identifier, - // followed by the length of the encapsulated binary data as a binary, - // two-byte integer in network byte order. The stream data follows - // immediately afterwards, without a CRLF, but including the upper-layer - // protocol headers. Each $ block contains exactly one upper-layer - // protocol data unit, e.g., one RTP packet. + } + 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); diff --git a/rtsp.h b/rtsp.h index b6dc237..fb09c27 100644 --- a/rtsp.h +++ b/rtsp.h @@ -22,10 +22,12 @@ class cSatipRtsp { private: 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 }; cSatipTunerIf &tunerM; @@ -36,6 +38,9 @@ private: cString errorNoMoreM; cString errorOutOfRangeM; cString errorCheckSyntaxM; + int modeM; + unsigned int interleavedRtpIdM; + unsigned int interleavedRtcpIdM; void Create(void); void Destroy(void); @@ -51,6 +56,7 @@ public: explicit cSatipRtsp(cSatipTunerIf &tunerP); virtual ~cSatipRtsp(); + cString GetActiveMode(void); cString RtspUnescapeString(const char *strP); void Reset(void); bool Options(const char *uriP); diff --git a/setup.c b/setup.c index 1ed5530..c178c61 100644 --- a/setup.c +++ b/setup.c @@ -416,7 +416,7 @@ void cSatipPluginSetup::Setup(void) helpM.Append(tr("Define an ill-behaving filter to be blacklisted.")); } } - Add(new cMenuEditStraItem(tr("Transport mode"), &transportModeM, ELEMENTS(transportModeTextsM) - 1, transportModeTextsM)); // TODO: RTP-over-TCP + 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(""); diff --git a/tuner.c b/tuner.c index 19e558f..0ff758f 100644 --- a/tuner.c +++ b/tuner.c @@ -297,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); @@ -350,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); @@ -376,21 +386,25 @@ void cSatipTuner::SetupTransport(int rtpPortP, int rtcpPortP, const char *stream if (multicast != rtpM.IsMulticast() || rtpPortP != rtpM.Port()) { cSatipPoller::GetInstance()->Unregister(rtpM); rtpM.Close(); - if (multicast) - rtpM.OpenMulticast(rtpPortP, streamAddrP, sourceAddrP); - else - rtpM.Open(rtpPortP); - cSatipPoller::GetInstance()->Register(rtpM); + 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 (multicast) - rtcpM.OpenMulticast(rtpPortP, streamAddrP, sourceAddrP); - else - rtcpM.Open(rtpPortP); - cSatipPoller::GetInstance()->Register(rtcpM); + if (rtcpPortP >= 0) { + if (multicast) + rtcpM.OpenMulticast(rtpPortP, streamAddrP, sourceAddrP); + else + rtcpM.Open(rtpPortP); + cSatipPoller::GetInstance()->Register(rtcpM); + } } } @@ -674,5 +688,5 @@ cString cSatipTuner::GetSignalStatus(void) cString cSatipTuner::GetInformation(void) { debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIdM); - return (currentStateM >= tsTuned) ? cString::sprintf("%s?%s [stream=%d]", *GetBaseUrl(*streamAddrM, streamPortM), *streamParamM, streamIdM) : "connection failed"; + return (currentStateM >= tsTuned) ? cString::sprintf("%s?%s (%s) [stream=%d]", *GetBaseUrl(*streamAddrM, streamPortM), *streamParamM, *rtspM.GetActiveMode(), streamIdM) : "connection failed"; } diff --git a/tuner.h b/tuner.h index 1ab3cf2..3d35cfd 100644 --- a/tuner.h +++ b/tuner.h @@ -157,6 +157,8 @@ 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); diff --git a/tunerif.h b/tunerif.h index f3e80ac..9eaac0c 100644 --- a/tunerif.h +++ b/tunerif.h @@ -14,6 +14,8 @@ 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; From 581ac4966db8bb6badb6bd3a12ece458663ba9f5 Mon Sep 17 00:00:00 2001 From: Rolf Ahrenberg Date: Fri, 16 Dec 2016 00:39:41 +0200 Subject: [PATCH 6/6] Prefer section pids. --- device.c | 2 +- sectionfilter.c | 13 +++++++++++++ sectionfilter.h | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/device.c b/device.c index fb58d7f..ee8c1c5 100644 --- a/device.c +++ b/device.c @@ -373,7 +373,7 @@ bool cSatipDevice::SetPid(cPidHandle *handleP, int typeP, bool onP) 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; diff --git a/sectionfilter.c b/sectionfilter.c index edf0382..10f655b 100644 --- a/sectionfilter.c +++ b/sectionfilter.c @@ -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); diff --git a/sectionfilter.h b/sectionfilter.h index e25c897..833511c 100644 --- a/sectionfilter.h +++ b/sectionfilter.h @@ -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);