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;