diff --git a/common.h b/common.h index 4124a18..e84884c 100644 --- a/common.h +++ b/common.h @@ -84,6 +84,44 @@ #define ELEMENTS(x) (sizeof(x) / sizeof(x[0])) +class cSatipMemoryBuffer { +private: + 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) { + dataM = (char *)AllocBuffer(dataM, sizeM + sizeP + 1); + if (dataM) { + memcpy(&(dataM[sizeM]), dataP, sizeP); + sizeM += sizeP; + dataM[sizeM] = 0; + return sizeP; + } + else + esyslog("[%s,%d]: Failed to allocate memory", __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); diff --git a/discover.c b/discover.c index 7a8a0f9..dcd508c 100644 --- a/discover.c +++ b/discover.c @@ -47,16 +47,14 @@ void cSatipDiscover::Destroy(void) instanceS->Deactivate(); } -size_t cSatipDiscover::WriteCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP) +size_t cSatipDiscover::DataCallback(char *ptrP, size_t sizeP, size_t nmembP, void *dataP) { cSatipDiscover *obj = reinterpret_cast(dataP); size_t len = sizeP * nmembP; debug16("%s len=%zu", __PRETTY_FUNCTION__, len); - if (obj) { - char *s = strndup(ptrP, len); - obj->deviceInfoM = cString::sprintf("%s%s", *obj->deviceInfoM, s ? s : ""); - } + if (obj && (len > 0)) + obj->dataBufferM.Add(ptrP, len); return len; } @@ -93,7 +91,7 @@ int cSatipDiscover::DebugCallback(CURL *handleP, curl_infotype typeP, char *data cSatipDiscover::cSatipDiscover() : cThread("SATIP discover"), mutexM(), - deviceInfoM(""), + dataBufferM(), msearchM(*this), probeUrlListM(), handleM(curl_easy_init()), @@ -179,7 +177,7 @@ void cSatipDiscover::Fetch(const char *urlP) SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_DEBUGDATA, this); // Set callback - SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipDiscover::WriteCallback); + SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipDiscover::DataCallback); SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this); // No progress meter and no signaling @@ -197,12 +195,13 @@ void cSatipDiscover::Fetch(const char *urlP) SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_URL, urlP); // Fetch the data - deviceInfoM = ""; SATIP_CURL_EASY_PERFORM(handleM); SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_RESPONSE_CODE, &rc); SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_PRIMARY_IP, &addr); - if (rc == 200) + if (rc == 200) { ParseDeviceInfo(addr); + dataBufferM.Reset(); + } else error("Discovery detected invalid status code: %ld", rc); } @@ -214,7 +213,7 @@ void cSatipDiscover::ParseDeviceInfo(const char *addrP) const char *desc = NULL, *model = NULL; #ifdef USE_TINYXML TiXmlDocument doc; - doc.Parse(*deviceInfoM); + doc.Parse(dataBufferM.Data()); TiXmlHandle docHandle(&doc); TiXmlElement *descElement = docHandle.FirstChild("root").FirstChild("device").FirstChild("friendlyName").ToElement(); if (descElement) @@ -224,7 +223,7 @@ void cSatipDiscover::ParseDeviceInfo(const char *addrP) model = modelElement->GetText() ? modelElement->GetText() : "DVBS2-1"; #else pugi::xml_document doc; - if (doc.load_buffer(*deviceInfoM, strlen(*deviceInfoM))) { + 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"); diff --git a/discover.h b/discover.h index 0e6bcd7..619a102 100644 --- a/discover.h +++ b/discover.h @@ -13,6 +13,7 @@ #include #include +#include "common.h" #include "discoverif.h" #include "msearch.h" #include "server.h" @@ -45,10 +46,10 @@ private: eProbeIntervalMs = 60000 // in milliseconds }; static cSatipDiscover *instanceS; - static size_t WriteCallback(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; - cString deviceInfoM; + cSatipMemoryBuffer dataBufferM; cSatipMsearch msearchM; cStringList probeUrlListM; CURL *handleM; diff --git a/rtsp.c b/rtsp.c index 55580fa..562626e 100644 --- a/rtsp.c +++ b/rtsp.c @@ -15,12 +15,14 @@ cSatipRtsp::cSatipRtsp(cSatipTunerIf &tunerP) : tunerM(tunerP), + headerBufferM(), + dataBufferM(), modeM(cmUnicast), handleM(NULL), headerListM(NULL), - errorNoMore(""), - errorOutOfRange(""), - errorCheckSyntax("") + errorNoMoreM(""), + errorOutOfRangeM(""), + errorCheckSyntaxM("") { debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId()); Create(); @@ -32,82 +34,26 @@ cSatipRtsp::~cSatipRtsp() Destroy(); } -size_t cSatipRtsp::HeaderCallback(void *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); - - 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::DescribeCallback(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(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::SetupPlayCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP) +size_t cSatipRtsp::DataCallback(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); - 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, "No-More:")) { - char *tmp = NULL; - if (sscanf(r, "No-More:%m[^;]", &tmp) == 1) - obj->SetErrorNoMore(skipspace(tmp)); - FREE_POINTER(tmp); - } - else if (strstr(r, "Out-of-Range:")) { - char *tmp = NULL; - if (sscanf(r, "Out-of-Range:%m[^;]", &tmp) == 1) - obj->SetErrorOutOfRange(skipspace(tmp)); - FREE_POINTER(tmp); - } - else if (strstr(r, "Check-Syntax:")) { - char *tmp = NULL; - if (sscanf(r, "Check-Syntax:%m[^;]", &tmp) == 1) - obj->SetErrorCheckSyntax(skipspace(tmp)); - FREE_POINTER(tmp); - } - r = strtok_r(NULL, "\r\n", &s); - } + if (obj) + obj->dataBufferM.Add(ptrP, len); return len; } @@ -255,7 +201,7 @@ 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::SetupPlayCallback); + SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEFUNCTION, cSatipRtsp::DataCallback); SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_WRITEDATA, this); SATIP_CURL_EASY_PERFORM(handleM); // Session id is now known - disable header parsing @@ -263,6 +209,14 @@ bool cSatipRtsp::Setup(const char *uriP, int rtpPortP, int rtcpPortP) 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()); @@ -296,11 +250,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::DescribeCallback); + 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()); @@ -321,11 +279,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::SetupPlayCallback); + 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()); @@ -346,11 +308,15 @@ 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::SetupPlayCallback); + 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(); + } SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_CLIENT_CSEQ, 1L); SATIP_CURL_EASY_SETOPT(handleM, CURLOPT_RTSP_SESSION_ID, NULL); @@ -362,22 +328,68 @@ bool cSatipRtsp::Teardown(const char *uriP) return result; } -void cSatipRtsp::SetErrorNoMore(const char *strP) +void cSatipRtsp::ParseHeader(void) { - debug1("%s (%s) [device %d]", __PRETTY_FUNCTION__, strP, tunerM.GetId()); - errorNoMore = strP; + 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); + } + r = strtok_r(NULL, "\r\n", &s); + } } -void cSatipRtsp::SetErrorOutOfRange(const char *strP) +void cSatipRtsp::ParseData(void) { - debug1("%s (%s) [device %d]", __PRETTY_FUNCTION__, strP, tunerM.GetId()); - errorOutOfRange = strP; -} + debug1("%s [device %d]", __PRETTY_FUNCTION__, tunerM.GetId()); + char *s, *p = dataBufferM.Data(); + char *r = strtok_r(p, "\r\n", &s); -void cSatipRtsp::SetErrorCheckSyntax(const char *strP) -{ - debug1("%s (%s) [device %d]", __PRETTY_FUNCTION__, strP, tunerM.GetId()); - errorCheckSyntax = strP; + 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) @@ -397,9 +409,9 @@ bool cSatipRtsp::ValidateLatestResponse(long *rcP) // SETUP PLAY TEARDOWN // The message body of the response may contain the "Check-Syntax:" parameter followed // by the malformed syntax - if (!isempty(*errorCheckSyntax)) { + if (!isempty(*errorCheckSyntaxM)) { SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url); - error("Check syntax: %s (error code %ld: %s) [device %d]", *errorCheckSyntax, rc, url, tunerM.GetId()); + error("Check syntax: %s (error code %ld: %s) [device %d]", *errorCheckSyntaxM, rc, url, tunerM.GetId()); break; } case 403: @@ -407,18 +419,18 @@ bool cSatipRtsp::ValidateLatestResponse(long *rcP) // 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(*errorOutOfRange)) { + if (!isempty(*errorOutOfRangeM)) { SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url); - error("Out of range: %s (error code %ld: %s) [device %d]", *errorOutOfRange, rc, url, tunerM.GetId()); + error("Out of range: %s (error code %ld: %s) [device %d]", *errorOutOfRangeM, rc, url, tunerM.GetId()); 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(*errorNoMore)) { + if (!isempty(*errorNoMoreM)) { SATIP_CURL_EASY_GETINFO(handleM, CURLINFO_EFFECTIVE_URL, &url); - error("No more: %s (error code %ld: %s) [device %d]", *errorNoMore, rc, url, tunerM.GetId()); + error("No more: %s (error code %ld: %s) [device %d]", *errorNoMoreM, rc, url, tunerM.GetId()); break; } default: @@ -429,9 +441,9 @@ bool cSatipRtsp::ValidateLatestResponse(long *rcP) if (rcP) *rcP = rc; } - errorNoMore = ""; - errorOutOfRange = ""; - errorCheckSyntax = ""; + errorNoMoreM = ""; + errorOutOfRangeM = ""; + errorCheckSyntaxM = ""; debug1("%s result=%s [device %d]", __PRETTY_FUNCTION__, result ? "ok" : "failed", tunerM.GetId()); return result; diff --git a/rtsp.h b/rtsp.h index 1b4a9e9..3949524 100644 --- a/rtsp.h +++ b/rtsp.h @@ -15,13 +15,13 @@ #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 DescribeCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP); - static size_t SetupPlayCallback(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 int DebugCallback(CURL *handleP, curl_infotype typeP, char *dataP, size_t sizeP, void *userPtrP); enum { @@ -30,18 +30,19 @@ private: enum eCommunicationMode { cmUnicast, cmMulticast }; cSatipTunerIf &tunerM; + cSatipMemoryBuffer headerBufferM; + cSatipMemoryBuffer dataBufferM; eCommunicationMode modeM; CURL *handleM; struct curl_slist *headerListM; - cString errorNoMore; - cString errorOutOfRange; - cString errorCheckSyntax; + cString errorNoMoreM; + cString errorOutOfRangeM; + cString errorCheckSyntaxM; void Create(void); void Destroy(void); - void SetErrorNoMore(const char *strP); - void SetErrorOutOfRange(const char *strP); - void SetErrorCheckSyntax(const char *strP); + void ParseHeader(void); + void ParseData(void); bool ValidateLatestResponse(long *rcP); // to prevent copy constructor and assignment