From 48862f99d36302fe050858bc5c9ad92e78ca853b Mon Sep 17 00:00:00 2001 From: Rolf Ahrenberg Date: Sun, 11 Dec 2016 01:18:12 +0200 Subject: [PATCH] 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 a22b176..bde6e61 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 b6881b8..4df1dcd 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 1986b1f..1f60625 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 d522ddc..e82d281 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 92350f3..ec2f8ab 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: