diff --git a/pvr.nextpvr/addon.xml b/pvr.nextpvr/addon.xml index 3ec5282d..dc63ae37 100644 --- a/pvr.nextpvr/addon.xml +++ b/pvr.nextpvr/addon.xml @@ -1,7 +1,7 @@ diff --git a/pvr.nextpvr/changelog.txt b/pvr.nextpvr/changelog.txt index e438afdb..6f4ae552 100644 --- a/pvr.nextpvr/changelog.txt +++ b/pvr.nextpvr/changelog.txt @@ -1,3 +1,7 @@ +v1.10.5 +- fixed issue where RPi user were unable to watch live tv due to subtle socket differences on that platform +- fixed issue where recording list would get out of date and not reflect reality of backend + v1.10.4 - Updated to use new libplatform-dev diff --git a/src/RingBuffer.cpp b/src/RingBuffer.cpp index 87a78799..e619fd6c 100644 --- a/src/RingBuffer.cpp +++ b/src/RingBuffer.cpp @@ -127,7 +127,7 @@ bool CRingBuffer::ReadData(CRingBuffer &rBuf, unsigned int size) /* Write data to ring buffer from buffer specified in 'buf'. Amount read in is * specified by 'size'. */ -bool CRingBuffer::WriteData(char *buf, unsigned int size) +bool CRingBuffer::WriteData(const char *buf, unsigned int size) { if (size > m_size - m_fillCount) { @@ -224,7 +224,7 @@ unsigned int CRingBuffer::getSize() return m_size; } -unsigned int CRingBuffer::getReadPtr() +unsigned int CRingBuffer::getReadPtr() const { return m_readPtr; } diff --git a/src/RingBuffer.h b/src/RingBuffer.h index 0b902f3a..bfc42297 100644 --- a/src/RingBuffer.h +++ b/src/RingBuffer.h @@ -36,14 +36,14 @@ class CRingBuffer void Clear(); bool ReadData(char *buf, unsigned int size); bool ReadData(CRingBuffer &rBuf, unsigned int size); - bool WriteData(char *buf, unsigned int size); + bool WriteData(const char *buf, unsigned int size); bool WriteData(CRingBuffer &rBuf, unsigned int size); bool SkipBytes(int skipSize); bool Append(CRingBuffer &rBuf); bool Copy(CRingBuffer &rBuf); char *getBuffer(); unsigned int getSize(); - unsigned int getReadPtr(); + unsigned int getReadPtr() const; unsigned int getWritePtr(); unsigned int getMaxReadSize(); unsigned int getMaxWriteSize(); diff --git a/src/Socket.cpp b/src/Socket.cpp index 71c0416d..5474e90f 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -19,6 +19,7 @@ #include "kodi/libXBMC_addon.h" #include #include "platform/os.h" +#include "platform/util/timeutils.h" #include "client.h" #include "Socket.h" @@ -83,18 +84,18 @@ bool Socket::setHostname ( const std::string& host ) bool Socket::read_ready() { - fd_set fdset; + fd_set fdset; - FD_ZERO(&fdset); - FD_SET(_sd, &fdset); + FD_ZERO(&fdset); + FD_SET(_sd, &fdset); - struct timeval tv; + struct timeval tv; tv.tv_sec = 1; - int retVal = select(_sd+1, &fdset, NULL, NULL, &tv); - if (retVal > 0) - return true; - return false; + int retVal = select(_sd+1, &fdset, NULL, NULL, &tv); + if (retVal > 0) + return true; + return false; } @@ -214,8 +215,15 @@ int Socket::send ( const std::string& data ) { return 0; } - - int status = Socket::send( (const char*) data.c_str(), (const unsigned int) data.size()); + int status = 0; + do + { + status = Socket::send( (const char*) data.c_str(), (const unsigned int) data.size()); +#if defined(TARGET_WINDOWS) + } while (status == SOCKET_ERROR && errno == WSAEWOULDBLOCK); +#else + } while (status == SOCKET_ERROR && errno == EAGAIN); +#endif return status; } @@ -249,10 +257,15 @@ int Socket::send ( const char* data, const unsigned int len ) _sd = INVALID_SOCKET; return 0; } - - int status = ::send(_sd, data, len, 0 ); - - if (status == -1) + int status = 0; + do { + status = ::send(_sd, data, len, 0 ); +#if defined(TARGET_WINDOWS) + } while (status == SOCKET_ERROR && errno == WSAEWOULDBLOCK); +#else + } while (status == SOCKET_ERROR && errno == EAGAIN); +#endif + if (status == SOCKET_ERROR) { errormessage( getLastError(), "Socket::send"); XBMC->Log(LOG_ERROR, "Socket::send - failed to send data"); @@ -436,18 +449,25 @@ int Socket::receive ( char* data, const unsigned int buffersize, const unsigned int lasterror = getLastError(); #if defined(TARGET_WINDOWS) if ( lasterror != WSAEWOULDBLOCK) - errormessage( lasterror, "Socket::receive" ); #else - if ( lasterror != EAGAIN && lasterror != EWOULDBLOCK ) - errormessage( lasterror, "Socket::receive" ); + if ( lasterror != EAGAIN ) #endif + { + errormessage( lasterror, "Socket::receive" ); + } + else + { + XBMC->Log(LOG_ERROR, "Socket::read EAGAIN"); + usleep(50000); + continue; + } return status; } receivedsize += status; - if (receivedsize >= minpacketsize) - break; + if (receivedsize >= minpacketsize) + break; } return receivedsize; diff --git a/src/liveshift.cpp b/src/liveshift.cpp index 0359ff82..2c36c395 100644 --- a/src/liveshift.cpp +++ b/src/liveshift.cpp @@ -163,7 +163,15 @@ unsigned int LiveShiftSource::Read(unsigned char *buffer, unsigned int length) memset(request, 0, sizeof(request)); snprintf(request, sizeof(request), "Range: bytes=%llu-%llu-%d", blockOffset, (blockOffset+length), m_requestNumber); LOG("sending request: %s\n", request); - int sent = m_pSocket->send(request, sizeof(request)); + int sent; + do + { + sent = m_pSocket->send(request, sizeof(request)); +#if defined(TARGET_WINDOWS) + } while (sent < 0 && errno == WSAEWOULDBLOCK); +#else + } while (sent < 0 && errno == EAGAIN); +#endif if (sent != sizeof(request)) { LOG("NOT ALL BYTES SENT! Only sent %d bytes\n", sent); @@ -195,7 +203,16 @@ unsigned int LiveShiftSource::Read(unsigned char *buffer, unsigned int length) { LOG("got: %s\n", response); } - +#if defined(TARGET_WINDOWS) + else if (responseByteCount < 0 && errno == WSAEWOULDBLOCK) +#else + else if (responseByteCount < 0 && errno == EAGAIN) +#endif + { + usleep(50000); + LOG("got: EAGAIN"); + continue; + } // drop out if response header looks incorrect if (responseByteCount != sizeof(response)) { @@ -211,8 +228,14 @@ unsigned int LiveShiftSource::Read(unsigned char *buffer, unsigned int length) m_lastKnownLength = fileSize; // read response payload - bytesRead = m_pSocket->receive((char *)buffer, length, payloadSize); - + do + { + bytesRead = m_pSocket->receive((char *)buffer, length, payloadSize); +#if defined(TARGET_WINDOWS) + } while (bytesRead < 0 && errno == WSAEWOULDBLOCK); +#else + } while (bytesRead < 0 && errno == EAGAIN); +#endif // if it's from the start of the file, then cache it if (m_startupCache != NULL && ((payloadOffset + payloadSize) < STARTUP_CACHE_SIZE)) { diff --git a/src/pvrclient-nextpvr.cpp b/src/pvrclient-nextpvr.cpp index fdc4b882..448474a4 100644 --- a/src/pvrclient-nextpvr.cpp +++ b/src/pvrclient-nextpvr.cpp @@ -34,6 +34,8 @@ #if defined(TARGET_WINDOWS) #define atoll(S) _atoi64(S) +#else + #define MAXINT64 ULONG_MAX #endif @@ -124,6 +126,7 @@ cPVRClientNextPVR::cPVRClientNextPVR() m_pLiveShiftSource = NULL; + m_lastRecordingUpdateTime = MAXINT64; // time of last recording check - force forever m_incomingStreamBuffer.Create(188*2000); } @@ -267,6 +270,44 @@ void cPVRClientNextPVR::Disconnect() */ bool cPVRClientNextPVR::IsUp() { + // check time since last time Recordings were updated, update if it has been awhile + if (m_bConnected == true && m_lastRecordingUpdateTime != MAXINT64 && time(0) > (m_lastRecordingUpdateTime + 60 )) + { + TiXmlDocument doc; + char request[512]; + sprintf(request, "/service?method=recording.lastupdated"); + CStdString response; + if (DoRequest(request, response) == HTTP_OK) + { + if (doc.Parse(response) != NULL) + { + TiXmlElement* last_update = doc.RootElement()->FirstChildElement("last_update"); + if (last_update != NULL) + { + int64_t update_time = atoll(last_update->GetText()); + if (update_time > m_lastRecordingUpdateTime) + { + m_lastRecordingUpdateTime = MAXINT64; + PVR->TriggerRecordingUpdate(); + PVR->TriggerTimerUpdate(); + } + else + { + m_lastRecordingUpdateTime = time(0); + } + } + else + { + m_lastRecordingUpdateTime = MAXINT64; + } + } + } + else + { + m_lastRecordingUpdateTime = MAXINT64; + XBMC->Log(LOG_NOTICE, "Disabling recording update. Update NextPVR to v3.4"); + } + } return m_bConnected; } @@ -769,14 +810,20 @@ PVR_ERROR cPVRClientNextPVR::GetRecordings(ADDON_HANDLE handle) { tag.iLastPlayedPosition = atoi(pRecordingNode->FirstChildElement("playback_position")->FirstChild()->Value()); } - - char artworkPath[512]; - snprintf(artworkPath, sizeof(artworkPath), "http://%s:%d/service?method=recording.artwork&sid=%s&recording_id=%s", g_szHostname.c_str(), g_iPort, m_sid, tag.strRecordingId); - PVR_STRCPY(tag.strIconPath, artworkPath); - PVR_STRCPY(tag.strThumbnailPath, artworkPath); - snprintf(artworkPath, sizeof(artworkPath), "http://%s:%d/service?method=recording.fanart&sid=%s&recording_id=%s", g_szHostname.c_str(), g_iPort, m_sid, tag.strRecordingId); - PVR_STRCPY(tag.strFanartPath, artworkPath); + if (pRecordingNode->FirstChildElement("epg_event_oid") != NULL && pRecordingNode->FirstChildElement("epg_event_oid")->FirstChild() != NULL) + { + tag.iEpgEventId = atoi(pRecordingNode->FirstChildElement("epg_event_oid")->FirstChild()->Value()); + XBMC->Log(LOG_DEBUG, "Setting epg id %s %d", tag.strRecordingId, tag.iEpgEventId); + } + + char artworkPath[512]; + snprintf(artworkPath, sizeof(artworkPath), "http://%s:%d/service?method=recording.artwork&sid=%s&recording_id=%s", g_szHostname.c_str(), g_iPort, m_sid, tag.strRecordingId); + PVR_STRCPY(tag.strIconPath, artworkPath); + PVR_STRCPY(tag.strThumbnailPath, artworkPath); + + snprintf(artworkPath, sizeof(artworkPath), "http://%s:%d/service?method=recording.fanart&sid=%s&recording_id=%s", g_szHostname.c_str(), g_iPort, m_sid, tag.strRecordingId); + PVR_STRCPY(tag.strFanartPath, artworkPath); CStdString strStream; strStream.Format("http://%s:%d/live?recording=%s", g_szHostname, g_iPort, tag.strRecordingId); @@ -786,6 +833,7 @@ PVR_ERROR cPVRClientNextPVR::GetRecordings(ADDON_HANDLE handle) PVR->TransferRecordingEntry(handle, &tag); } } + XBMC->Log(LOG_DEBUG, "Updated recordings %lld", m_lastRecordingUpdateTime); } // ...and any in-progress recordings @@ -825,7 +873,7 @@ PVR_ERROR cPVRClientNextPVR::GetRecordings(ADDON_HANDLE handle) } } } - + m_lastRecordingUpdateTime = time(0); return PVR_ERROR_NO_ERROR; } @@ -877,6 +925,7 @@ PVR_ERROR cPVRClientNextPVR::SetRecordingLastPlayedPosition(const PVR_RECORDING XBMC->Log(LOG_DEBUG, "SetRecordingLastPlayedPosition failed"); return PVR_ERROR_FAILED; } + PVR->TriggerRecordingUpdate(); } return PVR_ERROR_NO_ERROR; } @@ -1243,17 +1292,17 @@ bool cPVRClientNextPVR::OpenLiveStream(const PVR_CHANNEL &channelinfo) delete m_pLiveShiftSource; m_pLiveShiftSource = NULL; } - - char mode[32]; - memset(mode, 0, sizeof(mode)); - if (channelinfo.bIsRadio == false && m_supportsLiveTimeshift && g_bUseTimeshift) - strcpy(mode, "&mode=liveshift"); + + char mode[32]; + memset(mode, 0, sizeof(mode)); + if (channelinfo.bIsRadio == false && m_supportsLiveTimeshift && g_bUseTimeshift) + strcpy(mode, "&mode=liveshift"); char line[256]; - if (channelinfo.iSubChannelNumber == 0) - sprintf(line, "GET /live?channel=%d%s&client=XBMC-%s HTTP/1.0\r\n", channelinfo.iChannelNumber, mode, m_sid); - else - sprintf(line, "GET /live?channel=%d.%d%s&client=XBMC-%s HTTP/1.0\r\n", channelinfo.iChannelNumber, channelinfo.iSubChannelNumber, mode, m_sid); + if (channelinfo.iSubChannelNumber == 0) + sprintf(line, "GET /live?channel=%d%s&client=XBMC-%s HTTP/1.0\r\n", channelinfo.iChannelNumber, mode, m_sid); + else + sprintf(line, "GET /live?channel=%d.%d%s&client=XBMC-%s HTTP/1.0\r\n", channelinfo.iChannelNumber, channelinfo.iSubChannelNumber, mode, m_sid); m_streamingclient->send(line, strlen(line)); @@ -1479,7 +1528,7 @@ bool cPVRClientNextPVR::SwitchChannel(const PVR_CHANNEL &channel) return true; // open new stream - bool result = OpenLiveStream(channel); + bool result = OpenLiveStream(channel); return result; } diff --git a/src/pvrclient-nextpvr.h b/src/pvrclient-nextpvr.h index d52330cb..aa248e19 100644 --- a/src/pvrclient-nextpvr.h +++ b/src/pvrclient-nextpvr.h @@ -134,6 +134,8 @@ class cPVRClientNextPVR CStdString m_PlaybackURL; LiveShiftSource *m_pLiveShiftSource; + int64_t m_lastRecordingUpdateTime; + char m_sid[64]; int m_iChannelCount;