diff --git a/HISTORY b/HISTORY index 0ec442f..c6f3de4 100644 --- a/HISTORY +++ b/HISTORY @@ -193,3 +193,8 @@ VDR Plugin 'iptv' Revision History - Updated for vdr-1.7.38. - Added a new CURL protocol for HTTP/HTTPS. + +2013-xx-xx: Version 1.2.1 + +- Fixed bugs found in the CURL implementation (Thanks + to Jeremy Hall). diff --git a/README b/README index 89f03ea..7f4101e 100644 --- a/README +++ b/README @@ -89,11 +89,11 @@ Configuration: - channels.conf - TV4;IPTV:40:S=1|P=0|F=EXT|U=iptvstream.sh|A=0:I:0:0:680:0:0:4:0:0:0 - TV3;IPTV:30:S=0|P=1|F=FILE|U=/video/stream.ts|A=5:I:0:514:670:2321:0:3:0:0:0 - TV2;IPTV:20:S=0|P=1|F=HTTP|U=127.0.0.1/TS/2|A=3000:I:0:513:660:2321:0:2:0:0:0 - TV1;IPTV:10:S=1|P=0|F=CURL|U=http%3A//foo%3Abar@127.0.0.1%3A3000/TS/2|A=0:I:0:512:650:2321:0:1:0:0:0 - TV1;IPTV:10:S=1|P=0|F=UDP|U=127.0.0.1@127.0.0.1|A=1234:I:0:512:650:2321:0:1:0:0:0 + TV4;IPTV:60:S=1|P=0|F=EXT|U=iptvstream.sh|A=0:I:0:0:680:0:0:4:0:0:0 + TV3;IPTV:50:S=0|P=1|F=FILE|U=/video/stream.ts|A=5:I:0:514:670:2321:0:3:0:0:0 + TV2;IPTV:40:S=0|P=1|F=HTTP|U=127.0.0.1/TS/2|A=3000:I:0:513:660:2321:0:2:0:0:0 + TV1;IPTV:30:S=1|P=0|F=CURL|U=http%3A//foo%3Abar@127.0.0.1%3A3000/TS/2|A=0:I:0:512:650:2321:0:1:0:0:0 + TV1;IPTV:20:S=1|P=0|F=UDP|U=127.0.0.1@127.0.0.1|A=1234:I:0:512:650:2321:0:1:0:0:0 TV1;IPTV:10:S=1|P=0|F=UDP|U=127.0.0.1|A=1234:I:0:512:650:2321:0:1:0:0:0 ^ ^ ^ ^ ^ ^ ^ | | | | | | Source type ("I") diff --git a/device.c b/device.c index 8dda082..7bc58ae 100644 --- a/device.c +++ b/device.c @@ -74,6 +74,7 @@ cIptvDevice::~cIptvDevice() DELETE_POINTER(pSidScannerM); } // Destroy all filters + cMutexLock MutexLock(&mutexM); for (int i = 0; i < eMaxSecFilterCount; ++i) DeleteFilter(i); // Close dvr fifo @@ -143,6 +144,7 @@ cString cIptvDevice::GetFiltersInformation(void) unsigned int count = 0; cString s("Active section filters:\n"); // loop through active section filters + cMutexLock MutexLock(&mutexM); for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) { if (secFiltersM[i]) { s = cString::sprintf("%sFilter %d: %s Pid=0x%02X (%s)\n", *s, i, @@ -321,7 +323,7 @@ bool cIptvDevice::IsBlackListed(u_short pidP, u_char tidP, u_char maskP) const // loop through section filter table for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) { int index = IptvConfig.GetDisabledFilters(i); - // check if matches + // Check if matches if ((index >= 0) && (index < SECTION_FILTER_TABLE_SIZE) && (section_filter_table[index].pid == pidP) && (section_filter_table[index].tid == tidP) && (section_filter_table[index].mask == maskP)) { @@ -418,7 +420,7 @@ bool cIptvDevice::HasInternalCam(void) void cIptvDevice::ResetBuffering(void) { debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM); - // pad prefill to multiple of TS_SIZE + // Pad prefill to multiple of TS_SIZE tsBufferPrefillM = (unsigned int)MEGABYTE(IptvConfig.GetTsBufferSize()) * IptvConfig.GetTsBufferPrefillRatio() / 100; tsBufferPrefillM -= (tsBufferPrefillM % TS_SIZE); diff --git a/iptv.c b/iptv.c index e69b2e4..9826e43 100644 --- a/iptv.c +++ b/iptv.c @@ -21,7 +21,7 @@ #define GITVERSION "" #endif - const char VERSION[] = "1.2.0" GITVERSION; + const char VERSION[] = "1.2.1" GITVERSION; static const char DESCRIPTION[] = trNOOP("Experience the IPTV"); class cPluginIptv : public cPlugin { diff --git a/protocolcurl.c b/protocolcurl.c index 51de280..e106ecc 100644 --- a/protocolcurl.c +++ b/protocolcurl.c @@ -10,10 +10,14 @@ #include "protocolcurl.h" #define iptv_curl_easy_setopt(X, Y, Z) \ - if ((res = curl_easy_setopt((X), (Y), (Z))) != CURLE_OK) { error("curl_easy_setopt(%s, %s, %s) failed: %d\n", #X, #Y, #Z, res); } + if ((res = curl_easy_setopt((X), (Y), (Z))) != CURLE_OK) { \ + error("curl_easy_setopt(%s, %s, %s) failed: %d\n", #X, #Y, #Z, res); \ + } #define iptv_curl_easy_perform(X) \ - if ((res = curl_easy_perform((X))) != CURLE_OK) { error("curl_easy_perform(%s) failed: %d\n", #X, res); } + if ((res = curl_easy_perform((X))) != CURLE_OK) { \ + error("curl_easy_perform(%s) failed: %d\n", #X, res); \ + } cIptvProtocolCurl::cIptvProtocolCurl() : streamUrlM(""), @@ -22,7 +26,8 @@ cIptvProtocolCurl::cIptvProtocolCurl() handleM(NULL), multiM(NULL), headerListM(NULL), - ringBufferM(new cRingBufferLinear(MEGABYTE(IptvConfig.GetTsBufferSize()), 7 * TS_SIZE)), + ringBufferM(new cRingBufferLinear(MEGABYTE(IptvConfig.GetTsBufferSize()), 7 * TS_SIZE, + false, *cString::sprintf("IPTV CURL"))), rtspControlM(), modeM(eModeUnknown), connectedM(false), @@ -61,29 +66,29 @@ size_t cIptvProtocolCurl::WriteRtspCallback(void *ptrP, size_t sizeP, size_t nme unsigned char *p = (unsigned char *)ptrP; //debug("cIptvProtocolCurl::%s(%zu)", __FUNCTION__, len); - // validate packet header ('$') and channel (0) + // Validate packet header ('$') and channel (0) if (obj && (p[0] == 0x24 ) && (p[1] == 0)) { int length = (p[2] << 8) | p[3]; if (length > 3) { - // skip interleave header + // Skip interleave header p += 4; // http://tools.ietf.org/html/rfc3550 // http://tools.ietf.org/html/rfc2250 - // version + // Version unsigned int v = (p[0] >> 6) & 0x03; - // extension bit + // Extension bit unsigned int x = (p[0] >> 4) & 0x01; - // cscr count + // CSCR count unsigned int cc = p[0] & 0x0F; - // payload type: MPEG2 TS = 33 + // Payload type: MPEG2 TS = 33 //unsigned int pt = p[1] & 0x7F; - // header lenght + // Header lenght unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t); - // check if extension + // Check if extension if (x) { - // extension header length + // Extension header length unsigned int ehl = (((p[headerlen + 2] & 0xFF) << 8) |(p[headerlen + 3] & 0xFF)); - // update header length + // Update header length headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t); } // Check that rtp is version 2 and payload contains multiple of TS packet data @@ -155,9 +160,10 @@ bool cIptvProtocolCurl::PutData(unsigned char *dataP, int lenP) if (pausedM) return false; if (ringBufferM && (lenP >= 0)) { - // should be pause the transfer? + // Should we pause the transfer ? if (ringBufferM->Free() < (2 * CURL_MAX_WRITE_SIZE)) { - debug("cIptvProtocolCurl::%s(pause): free=%d available=%d len=%d", __FUNCTION__, ringBufferM->Free(), ringBufferM->Available(), lenP); + debug("cIptvProtocolCurl::%s(pause): free=%d available=%d len=%d", __FUNCTION__, + ringBufferM->Free(), ringBufferM->Available(), lenP); pausedM = true; return false; } @@ -223,7 +229,7 @@ bool cIptvProtocolCurl::Connect() if (connectedM) return true; - // initialize the curl session + // Initialize the curl session if (!handleM) handleM = curl_easy_init(); if (!multiM) @@ -234,49 +240,49 @@ bool cIptvProtocolCurl::Connect() cString netrc = cString::sprintf("%s/netrc", IptvConfig.GetConfigDirectory()); #ifdef DEBUG - // verbose output + // Verbose output iptv_curl_easy_setopt(handleM, CURLOPT_VERBOSE, 1L); #endif - // set callbacks + // Set callbacks iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, cIptvProtocolCurl::WriteCallback); iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, this); iptv_curl_easy_setopt(handleM, CURLOPT_HEADERFUNCTION, cIptvProtocolCurl::HeaderCallback); iptv_curl_easy_setopt(handleM, CURLOPT_WRITEHEADER, this); - // no progress meter and no signaling + // No progress meter and no signaling iptv_curl_easy_setopt(handleM, CURLOPT_NOPROGRESS, 1L); iptv_curl_easy_setopt(handleM, CURLOPT_NOSIGNAL, 1L); - // support netrc + // Support netrc iptv_curl_easy_setopt(handleM, CURLOPT_NETRC, (long)CURL_NETRC_OPTIONAL); iptv_curl_easy_setopt(handleM, CURLOPT_NETRC_FILE, *netrc); - // set timeout + // Set timeout iptv_curl_easy_setopt(handleM, CURLOPT_CONNECTTIMEOUT, (long)eConnectTimeoutS); - // set user-agent + // Set user-agent iptv_curl_easy_setopt(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s", PLUGIN_NAME_I18N, VERSION)); - // set url + // Set URL //char *p = curl_easy_unescape(handleM, *streamUrlM, 0, NULL); //streamUrlM = p; //curl_free(p); iptv_curl_easy_setopt(handleM, CURLOPT_URL, *streamUrlM); - // protocol specific initializations + // Protocol specific initializations switch (modeM) { case eModeRtsp: { cString uri, control, transport, range; - // request server options + // Request server options uri = cString::sprintf("%s", *streamUrlM); iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri); iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); iptv_curl_easy_perform(handleM); - // request session description - SDP is delivered in message body and not in the header! + // Request session description - SDP is delivered in message body and not in the header! iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, cIptvProtocolCurl::DescribeCallback); iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, this); uri = cString::sprintf("%s", *streamUrlM); @@ -286,7 +292,7 @@ bool cIptvProtocolCurl::Connect() iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, NULL); iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, NULL); - // setup media stream + // Setup media stream uri = cString::sprintf("%s/%s", *streamUrlM, *rtspControlM); transport = "RTP/AVP/TCP;unicast;interleaved=0-1"; iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri); @@ -294,7 +300,7 @@ bool cIptvProtocolCurl::Connect() iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); iptv_curl_easy_perform(handleM); - // start playing + // Start playing uri = cString::sprintf("%s/", *streamUrlM); range = "0.000-"; iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri); @@ -302,7 +308,7 @@ bool cIptvProtocolCurl::Connect() iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); iptv_curl_easy_perform(handleM); - // start receiving + // Start receiving iptv_curl_easy_setopt(handleM, CURLOPT_INTERLEAVEFUNCTION, cIptvProtocolCurl::WriteRtspCallback); iptv_curl_easy_setopt(handleM, CURLOPT_INTERLEAVEDATA, this); iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE); @@ -313,16 +319,16 @@ bool cIptvProtocolCurl::Connect() case eModeHttp: case eModeHttps: { - // limit download speed (bytes/s) + // Limit download speed (bytes/s) iptv_curl_easy_setopt(handleM, CURLOPT_MAX_RECV_SPEED_LARGE, eMaxDownloadSpeedMBits * 131072L); - // follow location + // Follow location iptv_curl_easy_setopt(handleM, CURLOPT_FOLLOWLOCATION, 1L); - // fail if HTTP return code is >= 400 + // Fail if HTTP return code is >= 400 iptv_curl_easy_setopt(handleM, CURLOPT_FAILONERROR, 1L); - // set additional headers to prevent caching + // Set additional headers to prevent caching headerListM = curl_slist_append(headerListM, "Cache-Control: no-store, no-cache, must-revalidate"); headerListM = curl_slist_append(headerListM, "Cache-Control: post-check=0, pre-check=0"); headerListM = curl_slist_append(headerListM, "Pragma: no-cache"); @@ -337,7 +343,7 @@ bool cIptvProtocolCurl::Connect() break; } - // add handle into multi set + // Add handle into multi set curl_multi_add_handle(multiM, handleM); connectedM = true; @@ -351,13 +357,17 @@ bool cIptvProtocolCurl::Disconnect() { cMutexLock MutexLock(&mutexM); debug("cIptvProtocolCurl::%s()", __FUNCTION__); - if (handleM) { - // mode specific tricks + if (!connectedM) + return true; + + // Terminate curl session + if (handleM && multiM) { + // Mode specific tricks switch (modeM) { case eModeRtsp: { CURLcode res = CURLE_OK; - // teardown rtsp session + // Teardown rtsp session cString uri = cString::sprintf("%s/", *streamUrlM); iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri); iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN); @@ -374,16 +384,16 @@ bool cIptvProtocolCurl::Disconnect() break; } - // cleanup curl stuff + // Cleanup curl stuff if (headerListM) { curl_slist_free_all(headerListM); headerListM = NULL; } curl_multi_remove_handle(multiM, handleM); - curl_multi_cleanup(multiM); - multiM = NULL; curl_easy_cleanup(handleM); handleM = NULL; + curl_multi_cleanup(multiM); + multiM = NULL; } ClearData(); @@ -409,21 +419,19 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP) //debug("cIptvProtocolCurl::%s()", __FUNCTION__); int len = 0; if (ringBufferM) { - // fill up the buffer + // Fill up the buffer if (handleM && multiM) { switch (modeM) { case eModeRtsp: { - //CURLcode res = CURLE_OK; - //iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE); - //iptv_curl_easy_perform(handleM); - // @todo - how to detect eof? + CURLcode res = CURLE_OK; + iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE); + iptv_curl_easy_perform(handleM); + // @todo - How to detect eof? } break; case eModeFile: - break; - case eModeHttp: case eModeHttps: { @@ -434,21 +442,23 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP) res = curl_multi_perform(multiM, &running_handles); } while (res == CURLM_CALL_MULTI_PERFORM); - // shall be continue filling up the buffer? + // Shall we continue filling up the buffer? mutexM.Lock(); if (pausedM && (ringBufferM->Free() > ringBufferM->Available())) { - debug("cIptvProtocolCurl::%s(continue): free=%d available=%d", __FUNCTION__, ringBufferM->Free(), ringBufferM->Available()); + debug("cIptvProtocolCurl::%s(continue): free=%d available=%d", __FUNCTION__, + ringBufferM->Free(), ringBufferM->Available()); pausedM = false; curl_easy_pause(handleM, CURLPAUSE_CONT); } mutexM.Unlock(); - // check end of file + // Check if end of file if (running_handles == 0) { int msgcount; CURLMsg *msg = curl_multi_info_read(multiM, &msgcount); if (msg && (msg->msg == CURLMSG_DONE)) { - debug("cIptvProtocolCurl::%s(done): %s (%d)", __FUNCTION__, curl_easy_strerror(msg->data.result), msg->data.result); + debug("cIptvProtocolCurl::%s(done): %s (%d)", __FUNCTION__, + curl_easy_strerror(msg->data.result), msg->data.result); Disconnect(); Connect(); } @@ -456,6 +466,7 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP) } break; + case eModeUnknown: default: break; } diff --git a/protocolcurl.h b/protocolcurl.h index 956734d..9d6b691 100644 --- a/protocolcurl.h +++ b/protocolcurl.h @@ -29,7 +29,6 @@ private: }; enum { eConnectTimeoutS = 5, // in seconds - eSelectTimeoutMs = 10, // in milliseconds eMaxDownloadSpeedMBits = 20 // in megabits per second }; diff --git a/socket.c b/socket.c index 4cb1ceb..e0b74ef 100644 --- a/socket.c +++ b/socket.c @@ -264,22 +264,22 @@ int cIptvUdpSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP) else if (len > 3) { // http://tools.ietf.org/html/rfc3550 // http://tools.ietf.org/html/rfc2250 - // version + // Version unsigned int v = (bufferAddrP[0] >> 6) & 0x03; - // extension bit + // Extension bit unsigned int x = (bufferAddrP[0] >> 4) & 0x01; - // cscr count + // CSCR count unsigned int cc = bufferAddrP[0] & 0x0F; - // payload type: MPEG2 TS = 33 + // Payload type: MPEG2 TS = 33 //unsigned int pt = bufferAddrP[1] & 0x7F; - // header lenght + // Header lenght unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t); - // check if extension + // Check if extension if (x) { - // extension header length + // Extension header length unsigned int ehl = (((bufferAddrP[headerlen + 2] & 0xFF) << 8) | (bufferAddrP[headerlen + 3] & 0xFF)); - // update header length + // Update header length headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t); } // Check that rtp is version 2 and payload contains multiple of TS packet data diff --git a/streamer.c b/streamer.c index 60f50c3..0e2241b 100644 --- a/streamer.c +++ b/streamer.c @@ -14,6 +14,7 @@ cIptvStreamer::cIptvStreamer(cRingBufferLinear* ringBufferP, unsigned int packetLenP) : cThread("IPTV streamer"), ringBufferM(ringBufferP), + sleepM(), packetBufferLenM(packetLenP), protocolM(NULL) {