diff -Nru kodi-pvr-nextpvr-3.3.15/appveyor.yml kodi-pvr-nextpvr-3.3.17/appveyor.yml --- kodi-pvr-nextpvr-3.3.15/appveyor.yml 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/appveyor.yml 2013-05-31 22:59:22.000000000 +0000 @@ -0,0 +1,33 @@ +version: BuildNr.{build} + +image: Visual Studio 2015 + +shallow_clone: true + +clone_folder: c:\projects\pvr.nextpvr + +environment: + app_id: pvr.nextpvr + + matrix: + - GENERATOR: "Visual Studio 14" + CONFIG: Release + - GENERATOR: "Visual Studio 14 Win64" + CONFIG: Release + - GENERATOR: "Visual Studio 14 Win64" + CONFIG: Release + WINSTORE: -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0.16299.0" + - GENERATOR: "Visual Studio 14 ARM" + CONFIG: Release + WINSTORE: -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0.16299.0" + +build_script: + - cd .. + - git clone --branch Leia --depth=1 https://github.com/xbmc/xbmc.git + - cd %app_id% + - mkdir build + - cd build + - mkdir -p definition\%app_id% + - echo %app_id% %APPVEYOR_BUILD_FOLDER% %APPVEYOR_REPO_COMMIT% > definition\%app_id%\%app_id%.txt + - cmake -T host=x64 -G "%GENERATOR%" %WINSTORE% -DADDONS_TO_BUILD=%app_id% -DCMAKE_BUILD_TYPE=%CONFIG% -DADDONS_DEFINITION_DIR=%APPVEYOR_BUILD_FOLDER%/build/definition -DADDON_SRC_PREFIX=../.. -DCMAKE_INSTALL_PREFIX=../../xbmc/addons -DPACKAGE_ZIP=1 ../../xbmc/cmake/addons + - cmake --build . --config %CONFIG% --target %app_id% diff -Nru kodi-pvr-nextpvr-3.3.15/CMakeLists.txt kodi-pvr-nextpvr-3.3.17/CMakeLists.txt --- kodi-pvr-nextpvr-3.3.15/CMakeLists.txt 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/CMakeLists.txt 2013-05-31 22:59:22.000000000 +0000 @@ -1,11 +1,8 @@ +cmake_minimum_required(VERSION 3.5) project(pvr.nextpvr) -cmake_minimum_required(VERSION 2.6) - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}) -enable_language(CXX) - find_package(Kodi REQUIRED) find_package(kodiplatform REQUIRED) find_package(p8-platform REQUIRED) @@ -14,7 +11,7 @@ include_directories(${kodiplatform_INCLUDE_DIRS} ${p8-platform_INCLUDE_DIRS} ${TINYXML_INCLUDE_DIR} - ${KODI_INCLUDE_DIR}) + ${KODI_INCLUDE_DIR}/..) # Hack way with "/..", need bigger Kodi cmake rework to match right include ways set(NEXTPVR_SOURCES src/client.cpp src/md5.cpp @@ -24,6 +21,7 @@ src/BackendRequest.cpp src/buffers/Buffer.cpp src/buffers/DummyBuffer.cpp + src/buffers/ClientTimeshift.cpp src/buffers/TimeshiftBuffer.cpp src/buffers/RecordingBuffer.cpp src/buffers/CircularBuffer.cpp @@ -40,6 +38,7 @@ src/BackendRequest.h src/buffers/Buffer.h src/buffers/DummyBuffer.h + src/buffers/ClientTimeshift.h src/buffers/TimeshiftBuffer.h src/buffers/RecordingBuffer.h src/buffers/CircularBuffer.h @@ -47,6 +46,7 @@ src/buffers/Seeker.h) SET(DEPLIBS ${p8-platform_LIBRARIES} + ${kodiplatform_LIBRARIES} ${TINYXML_LIBRARIES}) if(WIN32) list(APPEND DEPLIBS ws2_32) diff -Nru kodi-pvr-nextpvr-3.3.15/debian/changelog kodi-pvr-nextpvr-3.3.17/debian/changelog --- kodi-pvr-nextpvr-3.3.15/debian/changelog 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/debian/changelog 2013-05-31 22:59:22.000000000 +0000 @@ -1,4 +1,4 @@ -kodi-pvr-nextpvr (3.3.15-1~disco) disco; urgency=low +kodi-pvr-nextpvr (3.3.17-1~disco) disco; urgency=low [ kodi ] * autogenerated dummy changelog diff -Nru kodi-pvr-nextpvr-3.3.15/FindTinyXML.cmake kodi-pvr-nextpvr-3.3.17/FindTinyXML.cmake --- kodi-pvr-nextpvr-3.3.15/FindTinyXML.cmake 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/FindTinyXML.cmake 2013-05-31 22:59:22.000000000 +0000 @@ -6,22 +6,19 @@ # TINYXML_LIBRARIES - List of libraries when using TinyXML. # +find_package(PkgConfig) if(PKG_CONFIG_FOUND) - pkg_check_modules (TINYXML tinyxml) - list(APPEND TINYXML_INCLUDE_DIRS ${TINYXML_INCLUDEDIR}) + pkg_check_modules(PC_TINYXML tinyxml QUIET) endif() -if(NOT TINYXML_FOUND) - find_path( TINYXML_INCLUDE_DIRS "tinyxml.h" - PATH_SUFFIXES "tinyxml" ) - find_library( TINYXML_LIBRARIES - NAMES "tinyxml" - PATH_SUFFIXES "tinyxml" ) -endif() +find_path(TINYXML_INCLUDE_DIRS NAMES tinyxml.h + PATHS ${PC_TINYXML_INCLUDEDIR} + PATH_SUFFIXES tinyxml) +find_library(TINYXML_LIBRARIES NAMES tinyxml + PATHS ${PC_TINYXML_LIBDIR} + PATH_SUFFIXES tinyxml) -# handle the QUIETLY and REQUIRED arguments and set TINYXML_FOUND to TRUE if -# all listed variables are TRUE -include( "FindPackageHandleStandardArgs" ) -find_package_handle_standard_args(TinyXML DEFAULT_MSG TINYXML_INCLUDE_DIRS TINYXML_LIBRARIES ) +include("FindPackageHandleStandardArgs") +find_package_handle_standard_args(TinyXML REQUIRED_VARS TINYXML_INCLUDE_DIRS TINYXML_LIBRARIES) mark_as_advanced(TINYXML_INCLUDE_DIRS TINYXML_LIBRARIES) diff -Nru kodi-pvr-nextpvr-3.3.15/Jenkinsfile kodi-pvr-nextpvr-3.3.17/Jenkinsfile --- kodi-pvr-nextpvr-3.3.15/Jenkinsfile 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/Jenkinsfile 2013-05-31 22:59:22.000000000 +0000 @@ -1 +1 @@ -buildPlugin() +buildPlugin(version: "Leia") diff -Nru kodi-pvr-nextpvr-3.3.15/pvr.nextpvr/addon.xml.in kodi-pvr-nextpvr-3.3.17/pvr.nextpvr/addon.xml.in --- kodi-pvr-nextpvr-3.3.15/pvr.nextpvr/addon.xml.in 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/pvr.nextpvr/addon.xml.in 2013-05-31 22:59:22.000000000 +0000 @@ -1,7 +1,7 @@ @ADDON_DEPENDS@ diff -Nru kodi-pvr-nextpvr-3.3.15/pvr.nextpvr/changelog.txt kodi-pvr-nextpvr-3.3.17/pvr.nextpvr/changelog.txt --- kodi-pvr-nextpvr-3.3.15/pvr.nextpvr/changelog.txt 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/pvr.nextpvr/changelog.txt 2013-05-31 22:59:22.000000000 +0000 @@ -1,3 +1,15 @@ +v3.3.17 +- implement v5 timeshifting +- improve playback of in progress recordings +- added the sid to recordings +- reintroduce XMLUtils makes the parsing code easier to read. +- fixed bug passing empty group titles to Kodi (kodi bug) + +v3.3.16 +- Update build system version and cleanup +- Fix package check for TinyXML +- Added AppVeyor for Windows related build tests + v3.3.15 - CreateThread() change - Move LiveStreams.xml to only load once diff -Nru kodi-pvr-nextpvr-3.3.15/pvr.nextpvr/resources/settings.xml kodi-pvr-nextpvr-3.3.17/pvr.nextpvr/resources/settings.xml --- kodi-pvr-nextpvr-3.3.15/pvr.nextpvr/resources/settings.xml 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/pvr.nextpvr/resources/settings.xml 2013-05-31 22:59:22.000000000 +0000 @@ -13,7 +13,7 @@ - + diff -Nru kodi-pvr-nextpvr-3.3.15/README.md kodi-pvr-nextpvr-3.3.17/README.md --- kodi-pvr-nextpvr-3.3.15/README.md 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/README.md 2013-05-31 22:59:22.000000000 +0000 @@ -1,14 +1,15 @@ -[![Build Status](https://travis-ci.org/kodi-pvr/pvr.nextpvr.svg?branch=master)](https://travis-ci.org/kodi-pvr/pvr.nextpvr) +[![Build Status](https://travis-ci.org/kodi-pvr/pvr.nextpvr.svg?branch=Leia)](https://travis-ci.org/kodi-pvr/pvr.nextpvr/branches) +[![Build Status](https://ci.appveyor.com/api/projects/status/github/kodi-pvr/pvr.nextpvr?branch=Leia&svg=true)](https://ci.appveyor.com/project/kodi-pvr/pvr-nextpvr?branch=Leia) [![Coverity Scan Build Status](https://scan.coverity.com/projects/5120/badge.svg)](https://scan.coverity.com/projects/5120) # NextPVR PVR -NextPVR PVR client addon for [Kodi] (http://kodi.tv) +NextPVR PVR client addon for [Kodi] (https://kodi.tv) ## Build instructions ### Linux -1. `git clone https://github.com/xbmc/xbmc.git` +1. `git clone --branch Leia https://github.com/xbmc/xbmc.git` 2. `git clone https://github.com/kodi-pvr/pvr.nextpvr.git` 3. `cd pvr.nextpvr && mkdir build && cd build` 4. `cmake -DADDONS_TO_BUILD=pvr.nextpvr -DADDON_SRC_PREFIX=../.. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=../../xbmc/addons -DPACKAGE_ZIP=1 ../../xbmc/cmake/addons` @@ -16,5 +17,5 @@ ##### Useful links -* [Kodi's PVR user support] (http://forum.kodi.tv/forumdisplay.php?fid=167) -* [Kodi's PVR development support] (http://forum.kodi.tv/forumdisplay.php?fid=136) +* [Kodi's PVR user support] (https://forum.kodi.tv/forumdisplay.php?fid=167) +* [Kodi's PVR development support] (https://forum.kodi.tv/forumdisplay.php?fid=136) diff -Nru kodi-pvr-nextpvr-3.3.15/src/BackendRequest.cpp kodi-pvr-nextpvr-3.3.17/src/BackendRequest.cpp --- kodi-pvr-nextpvr-3.3.15/src/BackendRequest.cpp 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/BackendRequest.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -20,11 +20,6 @@ #include "BackendRequest.h" -#include "Filesystem.h" - -#define HTTP_OK 200 -#define HTTP_NOTFOUND 404 -#define HTTP_BADREQUEST 400 using namespace ADDON; diff -Nru kodi-pvr-nextpvr-3.3.15/src/BackendRequest.h kodi-pvr-nextpvr-3.3.17/src/BackendRequest.h --- kodi-pvr-nextpvr-3.3.15/src/BackendRequest.h 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/BackendRequest.h 2013-05-31 22:59:22.000000000 +0000 @@ -26,6 +26,10 @@ #include "client.h" #include "p8-platform/threads/mutex.h" +#define HTTP_OK 200 +#define HTTP_NOTFOUND 404 +#define HTTP_BADREQUEST 400 + using namespace ADDON; namespace NextPVR diff -Nru kodi-pvr-nextpvr-3.3.15/src/buffers/Buffer.cpp kodi-pvr-nextpvr-3.3.17/src/buffers/Buffer.cpp --- kodi-pvr-nextpvr-3.3.15/src/buffers/Buffer.cpp 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/buffers/Buffer.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -20,7 +20,7 @@ */ #include "Buffer.h" -#include "Filesystem.h" + #include using namespace timeshift; @@ -77,3 +77,44 @@ handle = nullptr; } } + +void Buffer::LeaseWorker(void) +{ + while (m_isLeaseRunning == true) + { + time_t now = time(nullptr); + bool complete = false; + if ( m_nextLease <= now && m_complete == false) + { + std::this_thread::yield(); + std::unique_lock lock(m_mutex); + int retval = Lease(); + if ( retval == HTTP_OK) + { + m_nextLease = now + 7; + } + else if (retval == HTTP_BADREQUEST) + { + complete = true; + XBMC->QueueNotification(QUEUE_INFO, "Tuner required for recording"); + } + else + { + XBMC->Log(LOG_ERROR, "channel.transcode.lease failed %lld", m_nextLease ); + m_nextLease = now + 1; + } + } + if (m_nextStreamInfo <= now || m_nextRoll <= now || complete == true) + { + GetStreamInfo(); + if (complete) m_complete = true; + } + SLEEP(1000); + } +} + +int Buffer::Lease() +{ + std::string response; + return NextPVR::m_backEnd->DoRequest("/service?method=channel.transcode.lease", response); +} diff -Nru kodi-pvr-nextpvr-3.3.15/src/buffers/Buffer.h kodi-pvr-nextpvr-3.3.17/src/buffers/Buffer.h --- kodi-pvr-nextpvr-3.3.15/src/buffers/Buffer.h 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/buffers/Buffer.h 2013-05-31 22:59:22.000000000 +0000 @@ -27,6 +27,16 @@ #include #include #include "../client.h" +#include +#include +#include "../BackendRequest.h" + + +#if defined(TARGET_WINDOWS) +#define SLEEP(ms) Sleep(ms) +#else +#define SLEEP(ms) usleep(ms*1000) +#endif using namespace ADDON; @@ -165,7 +175,25 @@ m_readTimeout = timeout; } + void Channel(int channel_id) + { + m_channel_id = channel_id; + } + protected: + + time_t m_nextRoll; + time_t m_nextLease; + time_t m_nextStreamInfo; + bool m_isLeaseRunning; + std::thread m_leaseThread; + int Lease(); + void LeaseWorker(); + virtual bool GetStreamInfo() {return true;} + bool m_complete; + mutable std::mutex m_mutex; + + const static int DEFAULT_READ_TIMEOUT; /** @@ -194,5 +222,8 @@ * The time the buffer was created */ time_t m_startTime; + + int m_channel_id; + }; } diff -Nru kodi-pvr-nextpvr-3.3.15/src/buffers/ClientTimeshift.cpp kodi-pvr-nextpvr-3.3.17/src/buffers/ClientTimeshift.cpp --- kodi-pvr-nextpvr-3.3.15/src/buffers/ClientTimeshift.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/buffers/ClientTimeshift.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -0,0 +1,218 @@ +/* +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This Program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301 USA +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +#include "ClientTimeshift.h" +#include "../BackendRequest.h" +#include "tinyxml.h" +#include "kodi/util/XMLUtils.h" + +//#define FMODE 1 + +using namespace timeshift; + +bool ClientTimeShift::Open(const std::string inputUrl) +{ + m_isPaused = false; + m_stream_length = 0; + m_stream_duration = 0; + m_nextLease = 0; + m_nextRoll = 0; + m_nextStreamInfo = 0; + m_isLive = true; + m_rollingStartSeconds = 0; + m_bytesPerSecond = 0; + m_complete = false; + + if (g_NowPlaying == TV) + { + m_chunkSize = m_liveChunkSize; + } + else + m_chunkSize = 4; + + XBMC->Log(LOG_DEBUG, "%s:%d: %d", __FUNCTION__, __LINE__, m_chunkSize); + + if (m_channel_id != 0) + { + std::string timeshift = "/services/service?method=channel.stream.start&channel_id=" + std::to_string(m_channel_id); + std::string response; + if (NextPVR::m_backEnd->DoRequest(timeshift.c_str(), response) != HTTP_OK) + { + return false; + } + } + else + { + XBMC->Log(LOG_ERROR, "Missing channel for ClientTImeShift"); + return false; + } + + time_t timeout = time(nullptr) + 20; + do { + SLEEP(1000); + if ( ClientTimeShift::GetStreamInfo()) + { + if ( m_stream_duration > m_prebuffer ) + { + break; + } + } + Lease(); + } while (!m_complete && (timeout > time(nullptr))); + + if (m_complete || m_stream_duration == 0) + { + XBMC->Log(LOG_ERROR,"Could not buffer stream"); + StreamStop(); + return false; + } + + if (Buffer::Open(inputUrl, 0 ) == false) + { + XBMC->Log(LOG_ERROR,"Could not open streaming file"); + StreamStop(); + return false; + } + m_sourceURL = inputUrl + "&seek="; + m_rollingStartSeconds = m_streamStart = time(nullptr); + m_isLeaseRunning = true; + m_leaseThread = std::thread([this]() + { + LeaseWorker(); + }); + + return true; +} + +void ClientTimeShift::Close() +{ + Buffer::Close(); + m_isLeaseRunning = false; + + if (m_leaseThread.joinable()) + m_leaseThread.join(); + + StreamStop(); + XBMC->Log(LOG_DEBUG, "%s:%d:", __FUNCTION__, __LINE__); + m_lastClose = time(nullptr); +} + +void ClientTimeShift::StreamStop() +{ + std::string response; + if (NextPVR::m_backEnd->DoRequest("/services/service?method=channel.stream.stop", response) != HTTP_OK) + { + XBMC->Log(LOG_ERROR, "%s:%d:", __FUNCTION__, __LINE__); + } +} + +int64_t ClientTimeShift::Seek(int64_t position, int whence) +{ + if (m_complete) return -1; + if (m_active) + Buffer::Close(); + ClientTimeShift::GetStreamInfo(); + + if (m_stream_duration > g_timeShiftBufferSeconds) + { + int64_t startSlipBuffer = m_stream_length - (g_timeShiftBufferSeconds * m_stream_length/m_stream_duration); + XBMC->Log(LOG_DEBUG, "%s:%d: %lld %lld %lld", __FUNCTION__, __LINE__, startSlipBuffer, position, m_stream_length.load()); + if (position < startSlipBuffer) + position = startSlipBuffer; + } + + XBMC->Log(LOG_DEBUG, "%s:%d: %lld %d %lld %d", __FUNCTION__, __LINE__, position, whence, m_stream_duration.load(), m_isPaused); + if ( m_isPaused == true) + { + // skip while paused + m_streamPosition = position; + } + else + { + std::string seekingInput = m_sourceURL + std::to_string(position ) + "-"; + if ( Buffer::Open(seekingInput.c_str(), 0) == false) + { + XBMC->Log(LOG_ERROR, "Could not open file on seek"); + return -1; + } + } + return position; +} + +bool ClientTimeShift::GetStreamInfo() +{ + enum infoReturns + { + OK, + XML_PARSE, + HTTP_ERROR + }; + int64_t stream_duration; + infoReturns infoReturn = HTTP_ERROR; + std::string response; + + if (m_complete) + { + XBMC->Log(LOG_ERROR, "NextPVR not updating completed rolling file"); + return ( m_stream_length != 0 ); + } + if (NextPVR::m_backEnd->DoRequest("/services/service?method=channel.stream.info", response) == HTTP_OK) + { + TiXmlDocument doc; + if (doc.Parse(response.c_str()) != NULL) + { + TiXmlElement* filesNode = doc.FirstChildElement("map"); + if (filesNode != NULL) + { + stream_duration = strtoll(filesNode->FirstChildElement("stream_duration")->GetText(),nullptr,0); + if (stream_duration != 0) + { + m_stream_length = strtoll(filesNode->FirstChildElement("stream_length")->GetText(),nullptr,0); + m_stream_duration = stream_duration/1000; + if (m_stream_duration > g_timeShiftBufferSeconds) + { + m_rollingStartSeconds = m_streamStart + m_stream_duration - g_timeShiftBufferSeconds; + } + XMLUtils::GetBoolean(filesNode,"complete",m_complete); + if (m_complete == false) + { + if (m_nextRoll < time(nullptr)) + { + m_nextRoll = time(nullptr) + g_timeShiftBufferSeconds/3 + g_ServerTimeOffset; + } + } + else + { + XBMC->QueueNotification(QUEUE_INFO, "Tuner required. Navigation disabled"); + } + } + XBMC->Log(LOG_DEBUG,"CT channel.stream.info %lld %lld %d %lld",m_stream_length.load(), stream_duration,m_complete, m_rollingStartSeconds.load()); + infoReturn = OK; + } + } + else + { + infoReturn = XML_PARSE; + } + + } + m_nextStreamInfo = time(nullptr) + 10; + return infoReturn == OK; +} diff -Nru kodi-pvr-nextpvr-3.3.15/src/buffers/ClientTimeshift.h kodi-pvr-nextpvr-3.3.17/src/buffers/ClientTimeshift.h --- kodi-pvr-nextpvr-3.3.15/src/buffers/ClientTimeshift.h 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/buffers/ClientTimeshift.h 2013-05-31 22:59:22.000000000 +0000 @@ -0,0 +1,99 @@ +#pragma once +/* +* Copyright (C) 2015 Sam Stenvall +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This Program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301 USA +* http://www.gnu.org/copyleft/gpl.html +* +*/ +#include "RollingFile.h" +#include +#include + +std::string UriEncode(const std::string sSrc); + +using namespace ADDON; +namespace timeshift { + + class ClientTimeShift : public RollingFile + { + private: + bool m_isPaused = false; + int64_t m_streamPosition; + + /** + * The current live stream url with &seek= + */ + std::string m_sourceURL; + + public: + ClientTimeShift() : RollingFile() + { + if (!XBMC->GetSetting("prebuffer", &m_prebuffer)) + { + m_prebuffer = 0; + } + if (!XBMC->GetSetting("chunklivetv", &m_liveChunkSize)) + { + m_liveChunkSize = 64; + } + m_lastClose = 0; + m_channel_id = 0; + XBMC->Log(LOG_NOTICE, "ClientTimeShift Buffer created!"); + } + + virtual void PauseStream(bool bPause) override + { + if ((m_isPaused = bPause)) + { + // pause + m_streamPosition = XBMC->GetFilePosition(m_inputHandle); + if (!m_complete) + Buffer::Close(); + } + else + { + Seek(m_streamPosition,0); + } + } + + virtual ~ClientTimeShift() {} + + virtual bool Open(const std::string inputUrl) override; + virtual void Close() override; + + virtual bool GetStreamInfo(); + + virtual int64_t Position() const override + { + return XBMC->GetFilePosition(m_inputHandle); + } + virtual int Read(byte *buffer, size_t length) override + { + int64_t dataLen = XBMC->ReadFile(m_inputHandle, buffer, length); + if (m_complete && dataLen == 0) + { + XBMC->Log(LOG_DEBUG, "%s:%d: %lld %lld %lld %lld", __FUNCTION__, __LINE__, dataLen, length, XBMC->GetFileLength(m_inputHandle) ,XBMC->GetFilePosition(m_inputHandle)); + } + return dataLen; + } + + int64_t Seek(int64_t position, int whence) override; + + void StreamStop(void); + + }; +} diff -Nru kodi-pvr-nextpvr-3.3.15/src/buffers/RecordingBuffer.cpp kodi-pvr-nextpvr-3.3.17/src/buffers/RecordingBuffer.cpp --- kodi-pvr-nextpvr-3.3.15/src/buffers/RecordingBuffer.cpp 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/buffers/RecordingBuffer.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -34,16 +34,27 @@ int RecordingBuffer::Duration(void) { - if (m_isRecording.load()) + if (m_recordingTime) { + std::unique_lock lock(m_mutex); time_t endTime = time(nullptr); - int diff = (int) (endTime - m_recordingTime); + int diff = (int) (endTime - m_recordingTime -10); if (diff > 0) - { + { + int64_t bps = XBMC->GetFileLength(m_inputHandle) / diff; + if ((XBMC->GetFileLength(m_inputHandle) - XBMC->GetFilePosition(m_inputHandle)) * bps < 10) + { + m_isLive = false; + } + else + { + m_isLive = true; + } return diff; } else { + m_isLive = false; return 0; } } @@ -60,16 +71,19 @@ { m_chunkSize = 32; } + XBMC->Log(LOG_DEBUG, "RecordingBuffer::Open In Progress %d %lld", recording.iDuration, recording.recordingTime); if (recording.iDuration + recording.recordingTime > time(nullptr)) { - m_recordingTime = recording.recordingTime; + m_recordingTime = recording.recordingTime + g_ServerTimeOffset; XBMC->Log(LOG_DEBUG, "RecordingBuffer::Open In Progress %d %lld", recording.iDuration, recording.recordingTime); - m_isRecording.store(true); + m_isLive = true; } else { - m_isRecording.store(false); + m_recordingTime = 0; + m_isLive = false; } + m_recordingURL = inputUrl; if (recording.strDirectory[0] != 0) { char strDirectory [PVR_ADDON_URL_STRING_LENGTH]; @@ -98,29 +112,26 @@ } if ( XBMC->FileExists(strDirectory,false)) { - XBMC->Log(LOG_DEBUG, "Native playback %s", strDirectory); - return Buffer::Open(std::string(strDirectory),0); + //m_recordingURL = strDirectory; } } - return Buffer::Open(inputUrl,0); + return Buffer::Open(m_recordingURL,0); } int RecordingBuffer::Read(byte *buffer, size_t length) { + if (m_recordingTime) + std::unique_lock lock(m_mutex); int dataRead = (int) XBMC->ReadFile(m_inputHandle, buffer, length); - if (dataRead==0 && m_isRecording.load()) + if (dataRead == 0 && m_isLive) { XBMC->Log(LOG_DEBUG, "%s:%d: %lld %lld", __FUNCTION__, __LINE__, XBMC->GetFileLength(m_inputHandle) ,XBMC->GetFilePosition(m_inputHandle)); - if (XBMC->GetFileLength(m_inputHandle) == XBMC->GetFilePosition(m_inputHandle)) - { - int64_t where = XBMC->GetFileLength(m_inputHandle); - Seek(where - length,SEEK_SET); - Seek(where,SEEK_SET); - if (where != Length()) - { - XBMC->Log(LOG_INFO, "%s:%d: Before %lld After %lld", __FUNCTION__, __LINE__, where, Length()); - } - } + int64_t position = XBMC->GetFilePosition(m_inputHandle); + Buffer::Close(); + Buffer::Open(m_recordingURL,0); + Seek(position,0); + dataRead = (int) XBMC->ReadFile(m_inputHandle, buffer, length); + XBMC->Log(LOG_DEBUG, "%s:%d: %lld %lld", __FUNCTION__, __LINE__, XBMC->GetFileLength(m_inputHandle) ,XBMC->GetFilePosition(m_inputHandle)); } return dataRead; } diff -Nru kodi-pvr-nextpvr-3.3.15/src/buffers/RecordingBuffer.h kodi-pvr-nextpvr-3.3.17/src/buffers/RecordingBuffer.h --- kodi-pvr-nextpvr-3.3.15/src/buffers/RecordingBuffer.h 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/buffers/RecordingBuffer.h 2013-05-31 22:59:22.000000000 +0000 @@ -33,6 +33,8 @@ { private: int m_Duration; + bool m_buffering = false; + std::string m_recordingURL; public: RecordingBuffer() : Buffer() { m_Duration = 0; XBMC->Log(LOG_NOTICE, "RecordingBuffer created!"); } @@ -58,7 +60,7 @@ virtual bool IsRealTimeStream() const override { - return m_isRecording.load(); + return false; } @@ -79,7 +81,9 @@ bool Open(const std::string inputUrl,const PVR_RECORDING &recording); - std::atomic m_isRecording; + std::atomic m_isLive; + + // recording start time time_t m_recordingTime; }; } diff -Nru kodi-pvr-nextpvr-3.3.15/src/buffers/RollingFile.cpp kodi-pvr-nextpvr-3.3.17/src/buffers/RollingFile.cpp --- kodi-pvr-nextpvr-3.3.15/src/buffers/RollingFile.cpp 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/buffers/RollingFile.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -20,19 +20,13 @@ #include "RollingFile.h" #include "../BackendRequest.h" -#include "Filesystem.h" + #include #include #include "tinyxml.h" +#include "util/XMLUtils.h" - -#define HTTP_OK 200 - -#if defined(TARGET_WINDOWS) -#define SLEEP(ms) Sleep(ms) -#else -#define SLEEP(ms) usleep(ms*1000) -#endif +//#define TESTURL "d:/downloads/abc.ts" using namespace timeshift; @@ -40,15 +34,18 @@ bool RollingFile::Open(const std::string inputUrl) { - m_sd.isPaused = false; - m_sd.lastPauseAdjust = 0; - m_sd.lastBufferTime = 0; - m_sd.lastKnownLength.store(0); + m_isPaused = false; + m_nextLease = 0; + m_nextStreamInfo = 0; + m_nextRoll = 0; + + m_stream_duration = 0; + m_bytesPerSecond = 0; m_activeFilename.clear(); - m_isRecording.store(true); + m_isLive = true; + slipFiles.clear(); std::stringstream ss; - m_nextRoll = 0; if (g_NowPlaying == TV) { @@ -59,7 +56,7 @@ XBMC->Log(LOG_DEBUG, "%s:%d: %d", __FUNCTION__, __LINE__, m_chunkSize); - ss << inputUrl << "|connection-timeout=" << 15; + ss << inputUrl ;//<< "|connection-timeout=" << 15; if (ss.str().find("&epgmode=true") != std::string::npos) { m_isEpgBased = true; @@ -71,7 +68,7 @@ m_slipHandle = XBMC->OpenFile(ss.str().c_str(), READ_NO_CACHE ); if (m_slipHandle == nullptr) { - XBMC->Log(LOG_ERROR,"Could not open slip file"); + XBMC->Log(LOG_ERROR,"Could not open slipHandle file"); return false; } int waitTime = 0; @@ -92,19 +89,20 @@ if ( !RollingFile::GetStreamInfo()) { - XBMC->Log(LOG_ERROR,"Could not read slip file"); + XBMC->Log(LOG_ERROR,"Could not read rolling file"); return false; } - m_rollingBegin = m_slipStart = time(nullptr); + m_rollingStartSeconds = m_streamStart = time(nullptr); XBMC->Log(LOG_DEBUG, "RollingFile::Open in Rolling File Mode: %d", m_isEpgBased); m_activeFilename = slipFiles.back().filename; m_activeLength = -1; - m_tsbThread = std::thread([this]() + m_isLeaseRunning = true; + m_leaseThread = std::thread([this]() { - TSBTimerProc(); + LeaseWorker(); }); - while (m_sd.tsbStart.load() < waitTime) + while (m_stream_length < waitTime) { SLEEP(500); RollingFile::GetStreamInfo(); @@ -126,7 +124,7 @@ #if defined(TESTURL) strcpy(strURL,TESTURL); #else - snprintf(strURL,sizeof(strURL),"http://%s:%d/stream?f=%s&sid=%s", g_szHostname.c_str(), g_iPort, UriEncode(m_activeFilename).c_str(), NextPVR::m_backEnd->getSID()); + snprintf(strURL,sizeof(strURL),"http://%s:%d/stream?f=%s&mode=http&sid=%s", g_szHostname.c_str(), g_iPort, UriEncode(m_activeFilename).c_str(), NextPVR::m_backEnd->getSID()); if (g_NowPlaying == Radio && m_activeLength == -1) { // reduce buffer for radio when playing in-progess slip file @@ -136,37 +134,6 @@ return RecordingBuffer::Open(strURL,recording); } -void RollingFile::TSBTimerProc(void) -{ - while (m_slipHandle != nullptr) - { - time_t now = time(nullptr); - //XBMC->Log(LOG_DEBUG,"TSB %lld %lld %lld %lld",now,m_nextRoll, m_sd.lastPauseAdjust, m_sd.lastBufferTime ); - if ( m_sd.lastPauseAdjust <= now ) - { - std::this_thread::yield(); - std::unique_lock lock(m_mutex); - std::string response; - if (NextPVR::m_backEnd->DoRequest("/service?method=channel.transcode.lease", response) == HTTP_OK) - { - m_sd.lastPauseAdjust = now + 7; - } - else - { - XBMC->Log(LOG_ERROR, "channel.transcode.lease failed %lld", m_sd.lastPauseAdjust ); - m_sd.lastPauseAdjust = now + 1; - } - } - if (m_sd.lastBufferTime <= now || m_nextRoll <= now) - { - std::this_thread::yield(); - std::unique_lock lock(m_mutex); - RollingFile::GetStreamInfo(); - } - SLEEP(1000); - } -} - bool RollingFile::GetStreamInfo() { enum infoReturns @@ -175,16 +142,17 @@ XML_PARSE, HTTP_ERROR }; - int64_t length; + int64_t stream_length; int64_t duration; - int complete; + bool complete; infoReturns infoReturn; infoReturn = HTTP_ERROR; std::string response; + if (m_nextRoll == LLONG_MAX) { XBMC->Log(LOG_ERROR, "NextPVR not updating completed rolling file"); - return true; + return ( m_stream_length != 0 ); } if (NextPVR::m_backEnd->DoRequest("/services/service?method=channel.stream.info", response) == HTTP_OK) { @@ -194,33 +162,34 @@ TiXmlElement* filesNode = doc.FirstChildElement("Files"); if (filesNode != NULL) { - length = strtoll(filesNode->FirstChildElement("Length")->GetText(),nullptr,0); + stream_length = strtoll(filesNode->FirstChildElement("Length")->GetText(),nullptr,0); duration = strtoll(filesNode->FirstChildElement("Duration")->GetText(),nullptr,0); - m_sd.tsbStart.store(duration/1000); - complete = atoi(filesNode->FirstChildElement("Complete")->GetText()); - XBMC->Log(LOG_DEBUG,"channel.stream.info %lld %lld %d %d",length, duration,complete, m_sd.iBytesPerSecond); - if (complete == 1) + XMLUtils::GetBoolean(filesNode,"Complete",complete); + XBMC->Log(LOG_DEBUG,"channel.stream.info %lld %lld %d %d",stream_length, duration,complete, m_bytesPerSecond.load()); + if (complete == true) { if ( slipFiles.empty() ) { return false; } - m_sd.lastKnownLength.store(length); - slipFiles.back().length = length - slipFiles.back().offset; - m_sd.lastBufferTime = m_nextRoll = LLONG_MAX; + m_stream_length = stream_length-500000; + slipFiles.back().length = stream_length - slipFiles.back().offset; + m_nextStreamInfo = m_nextRoll = LLONG_MAX; return true; } infoReturn = OK; if (duration!=0) { - m_sd.iBytesPerSecond = length/duration * 1000; + m_bytesPerSecond = stream_length/duration * 1000; } - m_sd.lastKnownLength.store(length); + m_stream_length = stream_length; + m_stream_duration = duration/1000; TiXmlElement* pFileNode; for( pFileNode = filesNode->FirstChildElement("File"); pFileNode; pFileNode=pFileNode->NextSiblingElement("File")) { int64_t offset = strtoll(pFileNode->Attribute("offset"),nullptr,0); + if (!slipFiles.empty()) { if ( slipFiles.back().offset == offset) @@ -231,6 +200,15 @@ { m_nextRoll = now + 1; } + if (slipFiles.size() == 4) + { + slipFiles.front().offset = slipFiles.front().offset + stream_length - offset; + if (!m_isEpgBased) + { + duration = slipFiles.front().seconds - duration; + m_rollingStartSeconds = now - g_timeShiftBufferSeconds; + } + } break; } slipFiles.back().length = offset - slipFiles.back().offset; @@ -247,6 +225,7 @@ newFile.filename = pFileNode->GetText(); newFile.offset = offset; newFile.length = -1; + newFile.seconds = time(nullptr); slipFiles.push_back(newFile); if (m_isEpgBased) { @@ -282,11 +261,21 @@ if (!m_isEpgBased) { m_nextRoll = time(nullptr) + g_timeShiftBufferSeconds/3 - 3 + g_ServerTimeOffset; - if (slipFiles.size() == 5) + } + if (slipFiles.size() == 5) + { + time_t slipDuration = slipFiles.front().seconds; + slipFiles.pop_front(); + if (m_isEpgBased) { - slipFiles.pop_front(); - m_rollingBegin += g_timeShiftBufferSeconds/3; + slipDuration = slipFiles.front().seconds - slipDuration; + m_rollingStartSeconds += slipDuration; } + else + { + m_rollingStartSeconds = time(nullptr) - g_timeShiftBufferSeconds; + } + } for (auto File : slipFiles ) { @@ -301,21 +290,21 @@ if (infoReturn != OK) { XBMC->Log(LOG_ERROR, "NextPVR not updating rolling file %d", infoReturn ); - m_sd.lastBufferTime = time(nullptr) + 1; + m_nextStreamInfo = time(nullptr) + 1; return false; } - m_sd.lastBufferTime = time(nullptr) + 10; + m_nextStreamInfo = time(nullptr) + 10; return true; } PVR_ERROR RollingFile::GetStreamTimes(PVR_STREAM_TIMES *stimes) { - if (m_isRecording.load()==false) + if (m_isLive == false) return RecordingBuffer::GetStreamTimes(stimes); - stimes->startTime = m_slipStart; + stimes->startTime = m_streamStart; stimes->ptsStart = 0; - stimes->ptsBegin = (m_rollingBegin - m_slipStart) * DVD_TIME_BASE; - stimes->ptsEnd = (time(nullptr) - m_slipStart) * DVD_TIME_BASE; + stimes->ptsBegin = (m_rollingStartSeconds - m_streamStart) * DVD_TIME_BASE; + stimes->ptsEnd = (time(nullptr) - m_streamStart) * DVD_TIME_BASE; return PVR_ERROR_NO_ERROR; } @@ -329,15 +318,17 @@ XBMC->Log(LOG_DEBUG, "%s:%d:", __FUNCTION__, __LINE__); m_slipHandle = nullptr; } - if (m_tsbThread.joinable()) - m_tsbThread.join(); + m_isLeaseRunning = false; + if (m_leaseThread.joinable()) + m_leaseThread.join(); m_lastClose = time(nullptr); } int RollingFile::Read(byte *buffer, size_t length) { - int dataRead = (int) XBMC->ReadFile(m_inputHandle,buffer, length); + std::unique_lock lock(m_mutex); bool foundFile = false; + int dataRead = (int) XBMC->ReadFile(m_inputHandle,buffer, length); if (dataRead == 0) { RollingFile::GetStreamInfo(); @@ -374,7 +365,7 @@ } else { - while( XBMC->GetFilePosition(m_inputHandle) == Length()) + while( XBMC->GetFilePosition(m_inputHandle) == XBMC->GetFileLength(m_inputHandle)) { RollingFile::GetStreamInfo(); if (m_nextRoll == LLONG_MAX) @@ -382,6 +373,7 @@ XBMC->Log(LOG_DEBUG, "should exit %s:%d: %lld %lld %lld", __FUNCTION__, __LINE__,Length(), XBMC->GetFileLength(m_inputHandle) ,XBMC->GetFilePosition(m_inputHandle)); return 0; } + XBMC->Log(LOG_DEBUG, "should exit %s:%d: %lld %lld %lld", __FUNCTION__, __LINE__,Length(), XBMC->GetFileLength(m_inputHandle) ,XBMC->GetFilePosition(m_inputHandle)); SLEEP(200); } } @@ -399,11 +391,7 @@ slipFile prevFile; int64_t adjust; RollingFile::GetStreamInfo(); - if (!m_isEpgBased) - { - // catch deleted files - prevFile = slipFiles.front(); - } + prevFile = slipFiles.front(); if (slipFiles.back().offset <= position) { // seek on head @@ -444,6 +432,7 @@ { adjust = position; } - XBMC->Log(LOG_DEBUG, "%s:%d: %lld %d", __FUNCTION__, __LINE__, position, adjust); - return RecordingBuffer::Seek(position - adjust,whence); + int64_t seekval = RecordingBuffer::Seek(position - adjust,whence); + XBMC->Log(LOG_DEBUG, "%s:%d: %lld %d %lld", __FUNCTION__, __LINE__, position, adjust, seekval); + return seekval; } diff -Nru kodi-pvr-nextpvr-3.3.15/src/buffers/RollingFile.h kodi-pvr-nextpvr-3.3.17/src/buffers/RollingFile.h --- kodi-pvr-nextpvr-3.3.15/src/buffers/RollingFile.h 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/buffers/RollingFile.h 2013-05-31 22:59:22.000000000 +0000 @@ -23,7 +23,6 @@ #include #include #include -#include "session.h" std::string UriEncode(const std::string sSrc); @@ -37,33 +36,35 @@ class RollingFile : public RecordingBuffer { private: - mutable std::mutex m_mutex; - session_data_t m_sd; std::string m_activeFilename; int64_t m_activeLength; + + protected: void *m_slipHandle = nullptr; - time_t m_slipStart; - time_t m_rollingBegin; - time_t m_nextRoll; + time_t m_streamStart; + + std::atomic m_rollingStartSeconds; + + std::atomic m_stream_length; + std::atomic m_stream_duration; + std::atomic m_bytesPerSecond; + bool m_isEpgBased; int m_prebuffer; int m_liveChunkSize; - int m_lastClose; + time_t m_lastClose; + + bool m_isPaused; struct slipFile{ std::string filename; int64_t offset; int64_t length; + int seconds; }; std::list slipFiles; - /** - * The thread that keeps track of the size of the current tsb, and - * drags the starting time forward when slip seconds is exceeded - */ - std::thread m_tsbThread; - public: RollingFile() : RecordingBuffer() { @@ -86,19 +87,13 @@ virtual void PauseStream(bool bPause) override { - if ((m_sd.isPaused = bPause)) - m_sd.lastBufferTime = 0; - } - - virtual bool IsTimeshifting() const override - { - XBMC->Log(LOG_DEBUG, "%s:%d: %lld %lld", __FUNCTION__, __LINE__, XBMC->GetFileLength(m_inputHandle) ,XBMC->GetFilePosition(m_inputHandle)); - return true; + if ((m_isPaused = bPause)) + m_nextLease = 20; } virtual int64_t Length() const override { - return m_sd.lastKnownLength.load(); + return m_stream_length; } virtual int64_t Position() const override @@ -110,10 +105,11 @@ int64_t Seek(int64_t position, int whence) override; - void TSBTimerProc(); + bool RollingFileOpen(); - bool GetStreamInfo(); + virtual bool GetStreamInfo(); + virtual PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES *) override; }; } diff -Nru kodi-pvr-nextpvr-3.3.15/src/buffers/TimeshiftBuffer.h kodi-pvr-nextpvr-3.3.17/src/buffers/TimeshiftBuffer.h --- kodi-pvr-nextpvr-3.3.15/src/buffers/TimeshiftBuffer.h 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/buffers/TimeshiftBuffer.h 2013-05-31 22:59:22.000000000 +0000 @@ -64,7 +64,7 @@ else m_sd.lastPauseAdjust = m_sd.pauseStart = 0; } - + virtual bool CanSeekStream() const override { return true; @@ -79,7 +79,7 @@ { return m_sd.lastKnownLength.load(); } - + virtual bool IsTimeshifting() const override { if (m_active) @@ -95,7 +95,7 @@ const static int INPUT_READ_LENGTH; const static int WINDOW_SIZE; const static int BUFFER_BLOCKS; - + NextPVR::Socket *m_streamingclient; /** @@ -103,10 +103,10 @@ * handle and writes it to the output handle */ void ConsumeInput(); - + void TSBTimerProc(); - + bool WriteData(const byte *, unsigned int, uint64_t); /** @@ -118,13 +118,13 @@ * Sends requests for blocks to backend. */ void RequestBlocks(void); // Acquires lock, calls internalRequestBlocks(); - void internalRequestBlocks(void); // Call when already holding lock. + void internalRequestBlocks(void); // Call when already holding lock. /** * Pull in incoming blocks. */ uint32_t WatchForBlock(byte *, uint64_t *); - + /** * The thread that reads from m_inputHandle and writes to the output * handles @@ -140,7 +140,7 @@ /** * Protects m_output*Handle */ - mutable std::mutex m_mutex; + // mutable std::mutex m_mutex moved to base class /** * Protects seek completion @@ -161,7 +161,7 @@ * Signaled whenever seek processing is complete. */ mutable std::condition_variable m_seeker; - + /** * The current write position in the buffer file */ diff -Nru kodi-pvr-nextpvr-3.3.15/src/client.cpp kodi-pvr-nextpvr-3.3.17/src/client.cpp --- kodi-pvr-nextpvr-3.3.15/src/client.cpp 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/client.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -18,7 +18,7 @@ */ #include "client.h" -#include "xbmc_pvr_dll.h" +#include "kodi/xbmc_pvr_dll.h" #include "pvrclient-nextpvr.h" #include "uri.h" @@ -309,12 +309,23 @@ } else if (str == "livestreamingmethod") { - eStreamingMethod tmp_livestreamingmethod = g_livestreamingmethod; - g_livestreamingmethod = *(eStreamingMethod*) settingValue; - if (g_livestreamingmethod != tmp_livestreamingmethod) + eStreamingMethod setting_livestreamingmethod = *(eStreamingMethod*) settingValue; + if (g_livestreamingmethod == ClientTimeshift) + { + if (setting_livestreamingmethod == RealTime) + { + g_livestreamingmethod = RealTime; + return ADDON_STATUS_NEED_RESTART; + } + } + else + { + if (g_livestreamingmethod != setting_livestreamingmethod) { + g_livestreamingmethod = setting_livestreamingmethod; return ADDON_STATUS_NEED_RESTART; } + } } else if (str == "host_mac") { diff -Nru kodi-pvr-nextpvr-3.3.15/src/client.h kodi-pvr-nextpvr-3.3.17/src/client.h --- kodi-pvr-nextpvr-3.3.15/src/client.h 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/client.h 2013-05-31 22:59:22.000000000 +0000 @@ -23,15 +23,16 @@ #ifndef CLIENT_H #define CLIENT_H -#include "libXBMC_addon.h" -#include "libXBMC_pvr.h" -#include "libKODI_guilib.h" +#include "kodi/libXBMC_addon.h" +#include "kodi/libXBMC_pvr.h" +#include "kodi/libKODI_guilib.h" enum eStreamingMethod { Timeshift = 0, RollingFile = 1, - RealTime = 2 + RealTime = 2, + ClientTimeshift = 3 }; enum eNowPlaying @@ -76,6 +77,8 @@ typedef unsigned char byte; +#define READ_NO_CACHE 0 + /*! * @brief PVR macros for string exchange */ diff -Nru kodi-pvr-nextpvr-3.3.15/src/pvrclient-nextpvr.cpp kodi-pvr-nextpvr-3.3.17/src/pvrclient-nextpvr.cpp --- kodi-pvr-nextpvr-3.3.15/src/pvrclient-nextpvr.cpp 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/pvrclient-nextpvr.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -24,6 +24,7 @@ #include #include +#include "kodi/util/XMLUtils.h" #include "client.h" #include "pvrclient-nextpvr.h" @@ -57,10 +58,6 @@ #define PVRCLIENT_NEXTPVR_VERSION_STRING "1.0.0.0" #define NEXTPVRC_MIN_VERSION_STRING "3.6.0" -#define HTTP_OK 200 -#define HTTP_NOTFOUND 404 -#define HTTP_BADREQUEST 400 - #define DEBUGGING_XML 0 #if DEBUGGING_XML void dump_to_log( TiXmlNode* pParent, unsigned int indent); @@ -281,15 +278,22 @@ } else if (version >= 50000) { - if ( g_livestreamingmethod != RealTime) - { - g_livestreamingmethod = RealTime; - XBMC->QueueNotification(QUEUE_ERROR,"v5 timeshifting disabled"); - } + if ( g_livestreamingmethod != RealTime) + { + if (version >= 50001) + { + g_livestreamingmethod = ClientTimeshift; + } + else + { + g_livestreamingmethod = RealTime; + XBMC->QueueNotification(QUEUE_ERROR,"NextPVR v5.0.1+ required for timeshift"); + } + } } } TiXmlElement* liveTimeshiftNode = settingsDoc.RootElement()->FirstChildElement("LiveTimeshift"); - if (liveTimeshiftNode != NULL && g_livestreamingmethod != RealTime) + if ( (liveTimeshiftNode != NULL && g_livestreamingmethod != RealTime) || g_livestreamingmethod == ClientTimeshift) { m_supportsLiveTimeshift = true; g_timeShiftBufferSeconds = atoi(settingsDoc.RootElement()->FirstChildElement("SlipSeconds")->FirstChild()->Value()); @@ -300,11 +304,19 @@ Sleep(2000); g_livestreamingmethod = Timeshift; } - if (g_livestreamingmethod == RollingFile ) + if (g_livestreamingmethod != Timeshift ) { - XBMC->Log(LOG_NOTICE, "Rolling File Based Buffering"); delete m_timeshiftBuffer; - m_timeshiftBuffer = new timeshift::RollingFile(); + if (version < 50000 ) + { + XBMC->Log(LOG_NOTICE, "Rolling File Based Buffering"); + m_timeshiftBuffer = new timeshift::RollingFile(); + } + else + { + XBMC->Log(LOG_NOTICE, "Client Timeshift Based Buffering"); + m_timeshiftBuffer = new timeshift::ClientTimeShift(); + } } else { @@ -921,15 +933,23 @@ memset(&tag, 0, sizeof(PVR_CHANNEL_GROUP)); tag.bIsRadio = false; tag.iPosition = 0; // groups default order, unused - strncpy(tag.strGroupName, pGroupNode->FirstChildElement("name")->FirstChild()->Value(), sizeof tag.strGroupName); - - // tell XBMC about channel, ignoring "All Channels" since xbmc has an built in group with effectively the same function - if (strcmp(tag.strGroupName, "All Channels") != 0) + std::string group; + if ( XMLUtils::GetString(pGroupNode,"name",group) ) { - PVR->TransferChannelGroup(handle, &tag); + // tell XBMC about channel, ignoring "All Channels" since xbmc has an built in group with effectively the same function + strcpy(tag.strGroupName,group.c_str()); + if (strcmp(tag.strGroupName, "All Channels") != 0 && tag.strGroupName[0]!=0) + { + PVR->TransferChannelGroup(handle, &tag); + } } } } + else + { + XBMC->Log(LOG_DEBUG, "GetChannelGroupsAmount"); + } + } return PVR_ERROR_NO_ERROR; } @@ -2119,6 +2139,12 @@ sprintf(line, "http://%s:%d/live?channeloid=%d&client=XBMC-%s&epgmode=true", g_szHostname.c_str(), g_iPort, channelinfo.iUniqueId, m_sid); m_livePlayer = m_timeshiftBuffer; } + else if (g_livestreamingmethod == ClientTimeshift) + { + sprintf(line, "http://%s:%d/live?channeloid=%d&client=%s&sid=%s", g_szHostname.c_str(), g_iPort, channelinfo.iUniqueId, m_sid,m_sid); + m_livePlayer = m_timeshiftBuffer; + m_livePlayer->Channel(channelinfo.iUniqueId); + } else { sprintf(line, "http://%s:%d/live?channeloid=%d&client=XBMC-%s", g_szHostname.c_str(), g_iPort, channelinfo.iUniqueId, m_sid); @@ -2225,7 +2251,7 @@ char line[1024]; g_NowPlaying = Recording; strcpy(copyRecording.strDirectory,m_hostFilenames[recording.strRecordingId].c_str()); - snprintf(line, sizeof(line), "http://%s:%d/live?recording=%s&client=XBMC", g_szHostname.c_str(), g_iPort, recording.strRecordingId); + snprintf(line, sizeof(line), "http://%s:%d/live?recording=%s&client=XBMC-%s", g_szHostname.c_str(), g_iPort, recording.strRecordingId, m_sid); return m_recordingBuffer->Open(line,copyRecording); } @@ -2278,10 +2304,16 @@ bool cPVRClientNextPVR::IsRealTimeStream() { LOG_API_CALL(__FUNCTION__); + bool retval; if (g_NowPlaying == Recording ) - return false; + { + retval = m_recordingBuffer->IsRealTimeStream(); + } else - return m_livePlayer->IsRealTimeStream(); + { + retval = m_livePlayer->IsRealTimeStream(); + } + return retval; } PVR_ERROR cPVRClientNextPVR::GetStreamTimes(PVR_STREAM_TIMES *stimes) diff -Nru kodi-pvr-nextpvr-3.3.15/src/pvrclient-nextpvr.h kodi-pvr-nextpvr-3.3.17/src/pvrclient-nextpvr.h --- kodi-pvr-nextpvr-3.3.15/src/pvrclient-nextpvr.h 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/pvrclient-nextpvr.h 2013-05-31 22:59:22.000000000 +0000 @@ -21,7 +21,7 @@ #include /* Master defines for client control */ -#include "xbmc_pvr_types.h" +#include "kodi/xbmc_pvr_types.h" /* Local includes */ #include "Socket.h" @@ -32,6 +32,7 @@ #include "buffers/TimeshiftBuffer.h" #include "buffers/RecordingBuffer.h" #include "buffers/RollingFile.h" +#include "buffers/ClientTimeshift.h" #include #define SAFE_DELETE(p) do { delete (p); (p)=NULL; } while (0) diff -Nru kodi-pvr-nextpvr-3.3.15/src/Socket.cpp kodi-pvr-nextpvr-3.3.17/src/Socket.cpp --- kodi-pvr-nextpvr-3.3.15/src/Socket.cpp 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/src/Socket.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -16,7 +16,7 @@ * along with this program. If not, see . * */ -#include "libXBMC_addon.h" +#include "kodi/libXBMC_addon.h" #include #include "p8-platform/os.h" #include "p8-platform/util/timeutils.h" diff -Nru kodi-pvr-nextpvr-3.3.15/.travis.yml kodi-pvr-nextpvr-3.3.17/.travis.yml --- kodi-pvr-nextpvr-3.3.15/.travis.yml 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-nextpvr-3.3.17/.travis.yml 2013-05-31 22:59:22.000000000 +0000 @@ -32,7 +32,7 @@ # before_script: - cd $TRAVIS_BUILD_DIR/.. - - git clone --depth=1 https://github.com/xbmc/xbmc.git + - git clone --branch Leia --depth=1 https://github.com/xbmc/xbmc.git - cd ${app_id} && mkdir build && cd build - mkdir -p definition/${app_id} - echo ${app_id} $TRAVIS_BUILD_DIR $TRAVIS_COMMIT > definition/${app_id}/${app_id}.txt