summaryrefslogtreecommitdiff
path: root/hlsPlayer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'hlsPlayer.cpp')
-rw-r--r--hlsPlayer.cpp1393
1 files changed, 679 insertions, 714 deletions
diff --git a/hlsPlayer.cpp b/hlsPlayer.cpp
index 1ef8ada..fc0c7cb 100644
--- a/hlsPlayer.cpp
+++ b/hlsPlayer.cpp
@@ -21,726 +21,691 @@ const int BUFFERSIZE = 8192;
//--- cHlsSegmentLoader
-cHlsSegmentLoader::cHlsSegmentLoader(std::string startm3u8, plexclient::cVideo* pVideo)
-{
- m_pVideo = pVideo;
- m_newList = false;
- m_bufferFilled = false;
- m_lastLoadedSegment = 0;
- m_segmentsToBuffer = 2;
- m_streamlenght = 0;
- m_lastSegmentSize = 0;
- m_pBuffer = new uchar[BUFFERSIZE];
-
- // Initialize members
- m_pClientSession = NULL;
-
- m_ringBufferSize = MEGABYTE(1);
- m_pRingbuffer = NULL;
-
- m_startUri = Poco::URI(startm3u8);
- m_startParser = cM3u8Parser();
- m_indexParser = cM3u8Parser();
-}
-
-cHlsSegmentLoader::~cHlsSegmentLoader()
-{
- // Stop Thread
- Cancel(0);
-
- delete m_pClientSession;
- delete[] m_pBuffer;
- delete m_pRingbuffer;
-}
-
-void cHlsSegmentLoader::SkipEmptySegments(int segmentDuration)
-{
- pcrecpp::RE re("&offset=(\\d+)", pcrecpp::RE_Options(PCRE_CASELESS));
- int value;
- re.PartialMatch(m_startUri.getQuery(), &value);
- if (value > segmentDuration) {
- m_lastLoadedSegment = (value / segmentDuration) - 1;
- }
-}
-
-bool cHlsSegmentLoader::LoadM3u8(std::string uri)
-{
- //LOCK_THREAD;
- m_startUri = Poco::URI(uri);
- return (m_newList = true);
-}
-
-void cHlsSegmentLoader::Action(void)
-{
- if(!LoadLists()) return;
-
- m_ringBufferSize = MEGABYTE(Config::GetInstance().BufferSize);
-
- isyslog("[plex]%s Create Ringbuffer %d MB", __FUNCTION__, Config::GetInstance().BufferSize);
-
- m_pRingbuffer = new cRingBufferLinear(m_ringBufferSize, 2*TS_SIZE);
-
- while(Running()) {
- if(m_newList) {
- hlsMutex.Lock();
- m_bufferFilled = false;
- m_lastLoadedSegment = 0;
- LoadLists();
- m_newList = false;
- m_pRingbuffer->Clear();
- isyslog("[plex] Ringbuffer Cleared, loading new segments");
- hlsMutex.Unlock();
- }
- int count = 0;
- m_pRingbuffer->Get(count);
- if (!DoLoad() && count < TS_SIZE) {
- isyslog("[plex] No further segments to load and buffer empty, end of stream!");
- Cancel();
-
- }
- cCondWait::SleepMs(3);
- }
- StopLoader();
-}
-
-bool cHlsSegmentLoader::LoadLists(void)
-{
- if( LoadStartList() ) {
- if( false == LoadIndexList() ) {
- esyslog("[plex]LoadIndexList failed!");
- return false;
- }
- } else {
- esyslog("[plex]LoadStartList failed!");
- return false;
- }
- return true;
-}
-
-bool cHlsSegmentLoader::LoadIndexList(void)
-{
- bool res = false;
- try {
- if(m_startParser.MasterPlaylist && m_startParser.vPlaylistItems.size() > 0) {
- // Todo: make it universal, might only work for Plexmediaserver
- ConnectToServer();
-
- std::string startUri = m_startUri.toString();
- startUri = startUri.substr(0, startUri.find_last_of("/")+1);
-
- Poco::URI indexUri(startUri+m_startParser.vPlaylistItems[0].file);
-
- Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, indexUri.getPath());
- AddHeader(request);
- // same server
- m_pClientSession->sendRequest(request);
-
- Poco::Net::HTTPResponse responseStart;
- std::istream& indexFile = m_pClientSession->receiveResponse(responseStart);
-
- if(responseStart.getStatus() != 200) {
- esyslog("[plex]%s Response Not Valid", __FUNCTION__);
- Poco::StreamCopier::copyStream(indexFile, std::cout);
- return res;
- }
- m_indexParser = cM3u8Parser();
- res = m_indexParser.Parse(indexFile);
-
- if(res) {
- // Segment URI is relative to index.m3u8
- std::string path = indexUri.getPath();
- m_segmentUriPart = path.substr(0, path.find_last_of("/")+1);
- }
- if(m_indexParser.TargetDuration > 3) {
- m_segmentsToBuffer = 2;
- } else {
- m_segmentsToBuffer = 3;
- }
-
- SkipEmptySegments(m_indexParser.TargetDuration);
- m_lastSegmentSize = 0;
-
- // Get stream lenght
- m_streamlenght = 0;
- for(unsigned int i = 0; i < m_indexParser.vPlaylistItems.size(); i++) {
- m_streamlenght += m_indexParser.vPlaylistItems[i].length;
- }
- }
-
- } catch (Poco::Exception &exc) {
- esyslog("[plex] %s %s", __FUNCTION__, exc.displayText().c_str());
- res = false;
- }
- return res;
-}
-
-bool cHlsSegmentLoader::LoadStartList(void)
-{
- bool res = false;
- ConnectToServer();
- try {
- dsyslog("[plex]%s %s", __FUNCTION__, m_startUri.toString().c_str());
- Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, m_startUri.getPathAndQuery());
- AddHeader(request);
- m_pClientSession->sendRequest(request);
-
- Poco::Net::HTTPResponse responseStart;
- std::istream& startFile = m_pClientSession->receiveResponse(responseStart);
-
- if(responseStart.getStatus() != 200) {
- esyslog("[plex]%s Response Not Valid", __FUNCTION__);
- Poco::StreamCopier::copyStream(startFile, std::cout);
- return res;
- }
-
- m_startParser = cM3u8Parser();
- res = m_startParser.Parse(startFile);
- if(res) {
- // Get GUID
- pcrecpp::RE re("([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})", pcrecpp::RE_Options(PCRE_CASELESS));
- string value;
- re.PartialMatch(m_startParser.vPlaylistItems[0].file, &value);
- m_sessionCookie = value;
- }
- } catch (Poco::Exception &exc) {
- esyslog("[plex] %s %s", __FUNCTION__, exc.displayText().c_str());
- res = false;
- }
- return res;
-}
-
-int cHlsSegmentLoader::EstimateSegmentSize()
-{
- if(&m_startParser.vPlaylistItems[0] == NULL) {
- esyslog("[plex]%s first element NULL", __FUNCTION__);
- }
- double bandw = m_startParser.vPlaylistItems[0].bandwidth / 8.0 / 1000.0 / 1000.0;
-
- int len = m_indexParser.TargetDuration;
- double estSize = (bandw) * len;
- estSize = std::max(estSize, 1.0);
- // default
- if(estSize <= 1) {
- estSize = 32;
- }
- return ceil(estSize);
-}
-
-bool cHlsSegmentLoader::LoadSegment(std::string segmentUri)
-{
- try {
- Poco::Net::HTTPRequest segmentRequest(Poco::Net::HTTPRequest::HTTP_GET, segmentUri);
- AddHeader(segmentRequest);
- m_pClientSession->sendRequest(segmentRequest);
- } catch (Poco::Exception&) {
- esyslog("[plex] %s; %s failed.", __FUNCTION__, segmentUri.c_str());
- return false;
- }
- Poco::Net::HTTPResponse segmentResponse;
- std::istream& segmentFile = m_pClientSession->receiveResponse(segmentResponse);
-
- if(segmentResponse.getStatus() != 200) {
- // error
- esyslog("[plex] %s; %s failed.", __FUNCTION__, segmentUri.c_str());
- return false;
- }
-
- if(segmentResponse.getContentLength() <= TS_SIZE) {
- // Empty segment
- dsyslog("[plex] %s: %s <= 188 byte.", __FUNCTION__, segmentUri.c_str());
- return true;
- }
-
- dsyslog("[plex] %s: %s successfully.", __FUNCTION__, segmentUri.c_str());
- // copy response
- segmentFile.read(reinterpret_cast<char*>(m_pBuffer), BUFFERSIZE);
- std::streamsize n = segmentFile.gcount();
- while(n > 0) {
- if(m_pRingbuffer->Free() >= n) {
- m_pRingbuffer->Put(m_pBuffer, n);
-
- segmentFile.read(reinterpret_cast<char*>(m_pBuffer), BUFFERSIZE);
- n = segmentFile.gcount();
- m_bufferFilled = true;
- }
- else {
- cCondWait::SleepMs(1);
- }
- if(m_newList) {
- break;
- }
- }
- return true;
-}
-
-int cHlsSegmentLoader::GetSegmentSize(int segmentIndex)
-{
- //dsyslog("[plex]%s Segment %d", __FUNCTION__, segmentIndex);
- if(m_indexParser.vPlaylistItems[segmentIndex].size >= TS_SIZE) {
- return m_indexParser.vPlaylistItems[segmentIndex].size;
- }
- try {
- Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_HEAD, GetSegmentUri(segmentIndex));
- AddHeader(req);
- m_pClientSession->sendRequest(req);
- Poco::Net::HTTPResponse reqResponse;
- m_pClientSession->receiveResponse(reqResponse);
- return m_indexParser.vPlaylistItems[segmentIndex].size = reqResponse.getContentLength();
- } catch(Poco::IOException& exc) {
- esyslog("[plex]%s %s ", __FUNCTION__, exc.displayText().c_str());
- return 0;
- }
-}
-
-std::string cHlsSegmentLoader::GetSegmentUri(int segmentIndex) const
-{
- return m_segmentUriPart + m_indexParser.vPlaylistItems[segmentIndex].file;
-}
-
-void cHlsSegmentLoader::CloseConnection(void)
-{
- if(m_pClientSession)
- m_pClientSession->abort();
-
- delete m_pClientSession;
- m_pClientSession = NULL;
-}
-
-bool cHlsSegmentLoader::ConnectToServer(void)
-{
- dsyslog("[plex]%s", __FUNCTION__);
- if(!m_pClientSession) {
- if(m_startUri.getScheme().find("https") != std::string::npos) {
- m_pClientSession = new Poco::Net::HTTPSClientSession(m_startUri.getHost(), m_startUri.getPort());
- }
- else {
- m_pClientSession = new Poco::Net::HTTPClientSession(m_startUri.getHost(), m_startUri.getPort());
- }
- }
-
- return m_pClientSession->connected();
-}
-
-bool cHlsSegmentLoader::DoLoad(void)
-{
- LOCK_THREAD;
- bool result = true;
- bool recover = false;
- if(m_lastLoadedSegment < m_indexParser.vPlaylistItems.size()) {
-
- std::string segmentUri = GetSegmentUri(m_lastLoadedSegment);
- if((result = LoadSegment(segmentUri))) {
- m_lastLoadedSegment++;
- } else {
- // transcoder may be died, plex bug, restart transcode session
- esyslog("[plex] %s 404, Transcoder died, see logfile from PMS", __FUNCTION__);
- recover = true;
- std::string stopUri = "/video/:/transcode/universal/stop?session=" + m_sessionCookie;
- try {
-
- bool ok;
- auto cSession = m_pVideo->m_pServer->MakeRequest(ok, stopUri);
- int tmp = m_lastLoadedSegment;
- int tmp2 = m_lastSegmentSize;
- CloseConnection();
- LoadLists();
- m_lastLoadedSegment = tmp;
- m_lastSegmentSize = tmp2;
- } catch (Poco::Exception&) {
- return false;
- }
- }
-
- m_bufferFilled = result;
- } else {
- result = false;
- }
- return recover || result;
-}
-
-bool cHlsSegmentLoader::BufferFilled(void)
-{
- return m_bufferFilled;
-}
-
-bool cHlsSegmentLoader::StopLoader(void)
-{
- dsyslog("[plex]%s", __FUNCTION__);
- try {
- std::string stopUri = "/video/:/transcode/universal/stop?session=" + m_sessionCookie;
-
- bool ok;
- auto cSession = m_pVideo->m_pServer->MakeRequest(ok, stopUri);
-
- Cancel();
-
- return ok;
- } catch(Poco::Exception& exc) {
- esyslog("[plex]%s %s ", __FUNCTION__, exc.displayText().c_str());
- return false;
- }
-}
-void cHlsSegmentLoader::AddHeader(Poco::Net::HTTPRequest& req)
-{
- req.add("X-Plex-Client-Identifier", Config::GetInstance().GetUUID());
+cHlsSegmentLoader::cHlsSegmentLoader(std::string startm3u8, plexclient::cVideo *pVideo) {
+ m_pVideo = pVideo;
+ m_newList = false;
+ m_bufferFilled = false;
+ m_lastLoadedSegment = 0;
+ m_segmentsToBuffer = 2;
+ m_streamlenght = 0;
+ m_lastSegmentSize = 0;
+ m_pBuffer = new uchar[BUFFERSIZE];
+
+ // Initialize members
+ m_pClientSession = NULL;
+
+ m_ringBufferSize = MEGABYTE(1);
+ m_pRingbuffer = NULL;
+
+ m_startUri = Poco::URI(startm3u8);
+ m_startParser = cM3u8Parser();
+ m_indexParser = cM3u8Parser();
+}
+
+cHlsSegmentLoader::~cHlsSegmentLoader() {
+ // Stop Thread
+ Cancel(0);
+
+ delete m_pClientSession;
+ delete[] m_pBuffer;
+ delete m_pRingbuffer;
+}
+
+void cHlsSegmentLoader::SkipEmptySegments(int segmentDuration) {
+ pcrecpp::RE re("&offset=(\\d+)", pcrecpp::RE_Options(PCRE_CASELESS));
+ int value;
+ re.PartialMatch(m_startUri.getQuery(), &value);
+ if (value > segmentDuration) {
+ m_lastLoadedSegment = (value / segmentDuration) - 1;
+ }
+}
+
+bool cHlsSegmentLoader::LoadM3u8(std::string uri) {
+ //LOCK_THREAD;
+ m_startUri = Poco::URI(uri);
+ return (m_newList = true);
+}
+
+void cHlsSegmentLoader::Action(void) {
+ if (!LoadLists()) return;
+
+ m_ringBufferSize = MEGABYTE(Config::GetInstance().BufferSize);
+
+ isyslog("[plex]%s Create Ringbuffer %d MB", __FUNCTION__, Config::GetInstance().BufferSize);
+
+ m_pRingbuffer = new cRingBufferLinear(m_ringBufferSize, 2 * TS_SIZE);
+
+ while (Running()) {
+ if (m_newList) {
+ hlsMutex.Lock();
+ m_bufferFilled = false;
+ m_lastLoadedSegment = 0;
+ LoadLists();
+ m_newList = false;
+ m_pRingbuffer->Clear();
+ isyslog("[plex] Ringbuffer Cleared, loading new segments");
+ hlsMutex.Unlock();
+ }
+ int count = 0;
+ m_pRingbuffer->Get(count);
+ if (!DoLoad() && count < TS_SIZE) {
+ isyslog("[plex] No further segments to load and buffer empty, end of stream!");
+ Cancel();
+
+ }
+ cCondWait::SleepMs(3);
+ }
+ StopLoader();
+}
+
+bool cHlsSegmentLoader::LoadLists(void) {
+ if (LoadStartList()) {
+ if (false == LoadIndexList()) {
+ esyslog("[plex]LoadIndexList failed!");
+ return false;
+ }
+ } else {
+ esyslog("[plex]LoadStartList failed!");
+ return false;
+ }
+ return true;
+}
+
+bool cHlsSegmentLoader::LoadIndexList(void) {
+ bool res = false;
+ try {
+ if (m_startParser.MasterPlaylist && m_startParser.vPlaylistItems.size() > 0) {
+ // Todo: make it universal, might only work for Plexmediaserver
+ ConnectToServer();
+
+ std::string startUri = m_startUri.toString();
+ startUri = startUri.substr(0, startUri.find_last_of("/") + 1);
+
+ Poco::URI indexUri(startUri + m_startParser.vPlaylistItems[0].file);
+
+ Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, indexUri.getPath());
+ AddHeader(request);
+ // same server
+ m_pClientSession->sendRequest(request);
+
+ Poco::Net::HTTPResponse responseStart;
+ std::istream &indexFile = m_pClientSession->receiveResponse(responseStart);
+
+ if (responseStart.getStatus() != 200) {
+ esyslog("[plex]%s Response Not Valid", __FUNCTION__);
+ Poco::StreamCopier::copyStream(indexFile, std::cout);
+ return res;
+ }
+ m_indexParser = cM3u8Parser();
+ res = m_indexParser.Parse(indexFile);
+
+ if (res) {
+ // Segment URI is relative to index.m3u8
+ std::string path = indexUri.getPath();
+ m_segmentUriPart = path.substr(0, path.find_last_of("/") + 1);
+ }
+ if (m_indexParser.TargetDuration > 3) {
+ m_segmentsToBuffer = 2;
+ } else {
+ m_segmentsToBuffer = 3;
+ }
+
+ SkipEmptySegments(m_indexParser.TargetDuration);
+ m_lastSegmentSize = 0;
+
+ // Get stream lenght
+ m_streamlenght = 0;
+ for (unsigned int i = 0; i < m_indexParser.vPlaylistItems.size(); i++) {
+ m_streamlenght += m_indexParser.vPlaylistItems[i].length;
+ }
+ }
+
+ } catch (Poco::Exception &exc) {
+ esyslog("[plex] %s %s", __FUNCTION__, exc.displayText().c_str());
+ res = false;
+ }
+ return res;
+}
+
+bool cHlsSegmentLoader::LoadStartList(void) {
+ bool res = false;
+ ConnectToServer();
+ try {
+ dsyslog("[plex]%s %s", __FUNCTION__, m_startUri.toString().c_str());
+ Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, m_startUri.getPathAndQuery());
+ AddHeader(request);
+ m_pClientSession->sendRequest(request);
+
+ Poco::Net::HTTPResponse responseStart;
+ std::istream &startFile = m_pClientSession->receiveResponse(responseStart);
+
+ if (responseStart.getStatus() != 200) {
+ esyslog("[plex]%s Response Not Valid", __FUNCTION__);
+ Poco::StreamCopier::copyStream(startFile, std::cout);
+ return res;
+ }
+
+ m_startParser = cM3u8Parser();
+ res = m_startParser.Parse(startFile);
+ if (res) {
+ // Get GUID
+ pcrecpp::RE re("([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})",
+ pcrecpp::RE_Options(PCRE_CASELESS));
+ string value;
+ re.PartialMatch(m_startParser.vPlaylistItems[0].file, &value);
+ m_sessionCookie = value;
+ }
+ } catch (Poco::Exception &exc) {
+ esyslog("[plex] %s %s", __FUNCTION__, exc.displayText().c_str());
+ res = false;
+ }
+ return res;
+}
+
+int cHlsSegmentLoader::EstimateSegmentSize() {
+ if (&m_startParser.vPlaylistItems[0] == NULL) {
+ esyslog("[plex]%s first element NULL", __FUNCTION__);
+ }
+ double bandw = m_startParser.vPlaylistItems[0].bandwidth / 8.0 / 1000.0 / 1000.0;
+
+ int len = m_indexParser.TargetDuration;
+ double estSize = (bandw) * len;
+ estSize = std::max(estSize, 1.0);
+ // default
+ if (estSize <= 1) {
+ estSize = 32;
+ }
+ return ceil(estSize);
+}
+
+bool cHlsSegmentLoader::LoadSegment(std::string segmentUri) {
+ try {
+ Poco::Net::HTTPRequest segmentRequest(Poco::Net::HTTPRequest::HTTP_GET, segmentUri);
+ AddHeader(segmentRequest);
+ m_pClientSession->sendRequest(segmentRequest);
+ } catch (Poco::Exception &) {
+ esyslog("[plex] %s; %s failed.", __FUNCTION__, segmentUri.c_str());
+ return false;
+ }
+ Poco::Net::HTTPResponse segmentResponse;
+ std::istream &segmentFile = m_pClientSession->receiveResponse(segmentResponse);
+
+ if (segmentResponse.getStatus() != 200) {
+ // error
+ esyslog("[plex] %s; %s failed.", __FUNCTION__, segmentUri.c_str());
+ return false;
+ }
+
+ if (segmentResponse.getContentLength() <= TS_SIZE) {
+ // Empty segment
+ dsyslog("[plex] %s: %s <= 188 byte.", __FUNCTION__, segmentUri.c_str());
+ return true;
+ }
+
+ dsyslog("[plex] %s: %s successfully.", __FUNCTION__, segmentUri.c_str());
+ // copy response
+ segmentFile.read(reinterpret_cast<char *>(m_pBuffer), BUFFERSIZE);
+ std::streamsize n = segmentFile.gcount();
+ while (n > 0) {
+ if (m_pRingbuffer->Free() >= n) {
+ m_pRingbuffer->Put(m_pBuffer, n);
+
+ segmentFile.read(reinterpret_cast<char *>(m_pBuffer), BUFFERSIZE);
+ n = segmentFile.gcount();
+ m_bufferFilled = true;
+ }
+ else {
+ cCondWait::SleepMs(1);
+ }
+ if (m_newList) {
+ break;
+ }
+ }
+ return true;
+}
+
+int cHlsSegmentLoader::GetSegmentSize(int segmentIndex) {
+ //dsyslog("[plex]%s Segment %d", __FUNCTION__, segmentIndex);
+ if (m_indexParser.vPlaylistItems[segmentIndex].size >= TS_SIZE) {
+ return m_indexParser.vPlaylistItems[segmentIndex].size;
+ }
+ try {
+ Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_HEAD, GetSegmentUri(segmentIndex));
+ AddHeader(req);
+ m_pClientSession->sendRequest(req);
+ Poco::Net::HTTPResponse reqResponse;
+ m_pClientSession->receiveResponse(reqResponse);
+ return m_indexParser.vPlaylistItems[segmentIndex].size = reqResponse.getContentLength();
+ } catch (Poco::IOException &exc) {
+ esyslog("[plex]%s %s ", __FUNCTION__, exc.displayText().c_str());
+ return 0;
+ }
+}
+
+std::string cHlsSegmentLoader::GetSegmentUri(int segmentIndex) const {
+ return m_segmentUriPart + m_indexParser.vPlaylistItems[segmentIndex].file;
+}
+
+void cHlsSegmentLoader::CloseConnection(void) {
+ if (m_pClientSession)
+ m_pClientSession->abort();
+
+ delete m_pClientSession;
+ m_pClientSession = NULL;
+}
+
+bool cHlsSegmentLoader::ConnectToServer(void) {
+ dsyslog("[plex]%s", __FUNCTION__);
+ if (!m_pClientSession) {
+ if (m_startUri.getScheme().find("https") != std::string::npos) {
+ m_pClientSession = new Poco::Net::HTTPSClientSession(m_startUri.getHost(), m_startUri.getPort());
+ }
+ else {
+ m_pClientSession = new Poco::Net::HTTPClientSession(m_startUri.getHost(), m_startUri.getPort());
+ }
+ }
+
+ return m_pClientSession->connected();
+}
+
+bool cHlsSegmentLoader::DoLoad(void) {
+ LOCK_THREAD;
+ bool result = true;
+ bool recover = false;
+ if (m_lastLoadedSegment < m_indexParser.vPlaylistItems.size()) {
+
+ std::string segmentUri = GetSegmentUri(m_lastLoadedSegment);
+ if ((result = LoadSegment(segmentUri))) {
+ m_lastLoadedSegment++;
+ } else {
+ // transcoder may be died, plex bug, restart transcode session
+ esyslog("[plex] %s 404, Transcoder died, see logfile from PMS", __FUNCTION__);
+ recover = true;
+ std::string stopUri = "/video/:/transcode/universal/stop?session=" + m_sessionCookie;
+ try {
+
+ bool ok;
+ auto cSession = m_pVideo->m_pServer->MakeRequest(ok, stopUri);
+ int tmp = m_lastLoadedSegment;
+ int tmp2 = m_lastSegmentSize;
+ CloseConnection();
+ LoadLists();
+ m_lastLoadedSegment = tmp;
+ m_lastSegmentSize = tmp2;
+ } catch (Poco::Exception &) {
+ return false;
+ }
+ }
+
+ m_bufferFilled = result;
+ } else {
+ result = false;
+ }
+ return recover || result;
+}
+
+bool cHlsSegmentLoader::BufferFilled(void) {
+ return m_bufferFilled;
+}
+
+bool cHlsSegmentLoader::StopLoader(void) {
+ dsyslog("[plex]%s", __FUNCTION__);
+ try {
+ std::string stopUri = "/video/:/transcode/universal/stop?session=" + m_sessionCookie;
+
+ bool ok;
+ auto cSession = m_pVideo->m_pServer->MakeRequest(ok, stopUri);
+
+ Cancel();
+
+ return ok;
+ } catch (Poco::Exception &exc) {
+ esyslog("[plex]%s %s ", __FUNCTION__, exc.displayText().c_str());
+ return false;
+ }
+}
+
+void cHlsSegmentLoader::AddHeader(Poco::Net::HTTPRequest &req) {
+ req.add("X-Plex-Client-Identifier", Config::GetInstance().GetUUID());
// req.add("X-Plex-Device", "PC");
- req.add("X-Plex-Model", "Linux");
- if(Config::GetInstance().UseCustomTranscodeProfile) {
- req.add("X-Plex-Device", "VDR Plex Plugin");
- } else {
- req.add("X-Plex-Device", "Plex Home Theater");
- //req.add("X-Plex-Device", "tvOS");
- }
-
- if(Config::GetInstance().UsePlexAccount && !m_pVideo->m_pServer->GetAuthToken().empty()) {
- // Add PlexToken to Header
- req.add("X-Plex-Token", m_pVideo->m_pServer->GetAuthToken());
- }
-}
-
-bool cHlsSegmentLoader::Active(void)
-{
- return Running();
-}
-
-void cHlsSegmentLoader::Ping(void)
-{
- dsyslog("[plex]%s", __FUNCTION__);
- try {
- std::string uri = "/video/:/transcode/universal/ping?session=" + Config::GetInstance().GetUUID();
-
- bool ok;
- auto cSession = m_pVideo->m_pServer->MakeRequest(ok, uri);
-
- } catch(Poco::Exception& exc) {
- esyslog("[plex]%s %s ", __FUNCTION__, exc.displayText().c_str());
- }
-}
-
-void cHlsSegmentLoader::ResizeRingbuffer(int newsize)
-{
- hlsMutex.Lock();
- isyslog("[plex] %s, Oldsize: %d, Newsize: %d", __FUNCTION__, m_ringBufferSize, newsize);
- //Create new Ringbuffer
- cRingBufferLinear* newBuffer = new cRingBufferLinear(newsize, TS_SIZE);
- // Copy old data
- int count = 0;
- uchar* pData = m_pRingbuffer->Get(count);
- newBuffer->Put(pData, count);
- // delete old buffer
- delete m_pRingbuffer;
- m_pRingbuffer = NULL;
- // assing new buffer
- m_pRingbuffer = newBuffer;
- m_ringBufferSize = newsize;
- hlsMutex.Unlock();
-}
-
-int cHlsSegmentLoader::GetStreamLenght()
-{
- return m_streamlenght;
+ req.add("X-Plex-Model", "Linux");
+ if (Config::GetInstance().UseCustomTranscodeProfile) {
+ req.add("X-Plex-Device", "VDR Plex Plugin");
+ } else {
+ req.add("X-Plex-Device", "Plex Home Theater");
+ //req.add("X-Plex-Device", "tvOS");
+ }
+
+ if (Config::GetInstance().UsePlexAccount && !m_pVideo->m_pServer->GetAuthToken().empty()) {
+ // Add PlexToken to Header
+ req.add("X-Plex-Token", m_pVideo->m_pServer->GetAuthToken());
+ }
+}
+
+bool cHlsSegmentLoader::Active(void) {
+ return Running();
+}
+
+void cHlsSegmentLoader::Ping(void) {
+ dsyslog("[plex]%s", __FUNCTION__);
+ try {
+ std::string uri = "/video/:/transcode/universal/ping?session=" + Config::GetInstance().GetUUID();
+
+ bool ok;
+ auto cSession = m_pVideo->m_pServer->MakeRequest(ok, uri);
+
+ } catch (Poco::Exception &exc) {
+ esyslog("[plex]%s %s ", __FUNCTION__, exc.displayText().c_str());
+ }
+}
+
+void cHlsSegmentLoader::ResizeRingbuffer(int newsize) {
+ hlsMutex.Lock();
+ isyslog("[plex] %s, Oldsize: %d, Newsize: %d", __FUNCTION__, m_ringBufferSize, newsize);
+ //Create new Ringbuffer
+ cRingBufferLinear *newBuffer = new cRingBufferLinear(newsize, TS_SIZE);
+ // Copy old data
+ int count = 0;
+ uchar *pData = m_pRingbuffer->Get(count);
+ newBuffer->Put(pData, count);
+ // delete old buffer
+ delete m_pRingbuffer;
+ m_pRingbuffer = NULL;
+ // assing new buffer
+ m_pRingbuffer = newBuffer;
+ m_ringBufferSize = newsize;
+ hlsMutex.Unlock();
+}
+
+int cHlsSegmentLoader::GetStreamLenght() {
+ return m_streamlenght;
}
//--- cHlsPlayer
-cHlsPlayer::cHlsPlayer(std::string startm3u8, plexclient::cVideo Video, int offset)
-{
- dsyslog("[plex]: '%s'", __FUNCTION__);
- m_Video = Video;
- m_timeOffset = offset;
- m_jumpOffset = 0;
- m_tTimeSum = 0;
- m_doJump = false;
- m_isBuffering = false;
- AudioIndexOffset = 1000; // Just a magic number
- m_tTimer.Set(1);
- m_pSegmentLoader = new cHlsSegmentLoader(startm3u8, &m_Video);
-
- m_pDebugFile = NULL;
- //m_pDebugFile = new std::ofstream();
- //m_pDebugFile->open("debug.ts", std::ios::out);
-}
-
-cHlsPlayer::~cHlsPlayer()
-{
- dsyslog("[plex]: '%s'", __FUNCTION__);
- Cancel();
- if(m_pDebugFile) m_pDebugFile->close();
- delete m_pDebugFile;
- m_pDebugFile = NULL;
-
- delete m_pSegmentLoader;
- m_pSegmentLoader = NULL;
- Detach();
-}
-
-void cHlsPlayer::SetAudioAndSubtitleTracks(void)
-{
- //DeviceClrAvailableTracks();
- DeviceSetAvailableTrack(ttDolby, 0, 0, "Current");
- if(m_Video.m_Media.m_vStreams.size() > 0) {
- std::vector<plexclient::Stream> streams = m_Video.m_Media.m_vStreams;
- for(std::vector<plexclient::Stream>::iterator it = streams.begin(); it != streams.end(); ++it) {
- plexclient::Stream *pStream = &(*it);
- if(pStream->m_eStreamType == plexclient::StreamType::sAUDIO) {
- DeviceSetAvailableTrack(ttDolby, pStream->m_iIndex, pStream->m_iIndex + AudioIndexOffset, pStream->m_sLanguage.c_str(), pStream->m_sCodec.c_str());
- }
- }
- }
-}
-
-
-void cHlsPlayer::Action(void)
-{
- // Start SegmentLoader
- m_pSegmentLoader->Start();
-
- m_bFirstPlay = true;
-
- while (Running()) {
- if(m_doJump && m_pSegmentLoader && m_pSegmentLoader->BufferFilled()) {
- LOCK_THREAD;
-
- DeviceFreeze();
- m_doJump = false;
- m_bFirstPlay = true;
- std::string uri = plexclient::Plexservice::GetUniversalTranscodeUrl(&m_Video, m_jumpOffset);
- m_pSegmentLoader->LoadM3u8(uri);
- m_timeOffset = m_jumpOffset;
- DeviceClear();
- DevicePlay();
- playMode = pmPlay;
- } else if(m_pSegmentLoader && m_pSegmentLoader->BufferFilled()) {
- DoPlay();
- if(m_bFirstPlay) {
- SetAudioAndSubtitleTracks();
- ResetPlayedSeconds();
- playMode = pmPlay;
- m_bFirstPlay = false;
- }
- CountPlayedSeconds();
- } else {
- // Pause
- cCondWait::SleepMs(3);
- }
- // statusupdates to pms every 60s
- if(m_pSegmentLoader && m_tTimer.TimedOut()) {
- if(playMode == pmPause) {
- m_pSegmentLoader->Ping();
- }
- ReportProgress();
- m_tTimer.Set(60000);
- }
- }
- // set as watched if >= 90%
- int t = m_Video.m_Media.m_lDuration / 1000 * 0.9;
- if( GetPlayedSeconds() >= t) {
- SetWatched();
- }
- DeviceClear();
-}
-
-bool cHlsPlayer::DoPlay(void)
-{
- bool res = false;
- cPoller Poller;
- if(DevicePoll(Poller, 10)) {
- hlsMutex.Lock();
+cHlsPlayer::cHlsPlayer(std::string startm3u8, plexclient::cVideo Video, int offset) {
+ dsyslog("[plex]: '%s'", __FUNCTION__);
+ m_Video = Video;
+ m_timeOffset = offset;
+ m_jumpOffset = 0;
+ m_tTimeSum = 0;
+ m_doJump = false;
+ m_isBuffering = false;
+ AudioIndexOffset = 1000; // Just a magic number
+ m_tTimer.Set(1);
+ m_pSegmentLoader = new cHlsSegmentLoader(startm3u8, &m_Video);
+
+ m_pDebugFile = NULL;
+ //m_pDebugFile = new std::ofstream();
+ //m_pDebugFile->open("debug.ts", std::ios::out);
+}
+
+cHlsPlayer::~cHlsPlayer() {
+ dsyslog("[plex]: '%s'", __FUNCTION__);
+ Cancel();
+ if (m_pDebugFile) m_pDebugFile->close();
+ delete m_pDebugFile;
+ m_pDebugFile = NULL;
+
+ delete m_pSegmentLoader;
+ m_pSegmentLoader = NULL;
+ Detach();
+}
+
+void cHlsPlayer::SetAudioAndSubtitleTracks(void) {
+ //DeviceClrAvailableTracks();
+ DeviceSetAvailableTrack(ttDolby, 0, 0, "Current");
+ if (m_Video.m_Media.m_vStreams.size() > 0) {
+ std::vector<plexclient::Stream> streams = m_Video.m_Media.m_vStreams;
+ for (std::vector<plexclient::Stream>::iterator it = streams.begin(); it != streams.end(); ++it) {
+ plexclient::Stream *pStream = &(*it);
+ if (pStream->m_eStreamType == plexclient::StreamType::sAUDIO) {
+ DeviceSetAvailableTrack(ttDolby, pStream->m_iIndex, pStream->m_iIndex + AudioIndexOffset,
+ pStream->m_sLanguage.c_str(), pStream->m_sCodec.c_str());
+ }
+ }
+ }
+}
+
+
+void cHlsPlayer::Action(void) {
+ // Start SegmentLoader
+ m_pSegmentLoader->Start();
+
+ m_bFirstPlay = true;
+
+ while (Running()) {
+ if (m_doJump && m_pSegmentLoader && m_pSegmentLoader->BufferFilled()) {
+ LOCK_THREAD;
+
+ DeviceFreeze();
+ m_doJump = false;
+ m_bFirstPlay = true;
+ std::string uri = plexclient::Plexservice::GetUniversalTranscodeUrl(&m_Video, m_jumpOffset);
+ m_pSegmentLoader->LoadM3u8(uri);
+ m_timeOffset = m_jumpOffset;
+ DeviceClear();
+ DevicePlay();
+ playMode = pmPlay;
+ } else if (m_pSegmentLoader && m_pSegmentLoader->BufferFilled()) {
+ DoPlay();
+ if (m_bFirstPlay) {
+ SetAudioAndSubtitleTracks();
+ ResetPlayedSeconds();
+ playMode = pmPlay;
+ m_bFirstPlay = false;
+ }
+ CountPlayedSeconds();
+ } else {
+ // Pause
+ cCondWait::SleepMs(3);
+ }
+ // statusupdates to pms every 60s
+ if (m_pSegmentLoader && m_tTimer.TimedOut()) {
+ if (playMode == pmPause) {
+ m_pSegmentLoader->Ping();
+ }
+ ReportProgress();
+ m_tTimer.Set(60000);
+ }
+ }
+ // set as watched if >= 90%
+ int t = m_Video.m_Media.m_lDuration / 1000 * 0.9;
+ if (GetPlayedSeconds() >= t) {
+ SetWatched();
+ }
+ DeviceClear();
+}
+
+bool cHlsPlayer::DoPlay(void) {
+ bool res = false;
+ cPoller Poller;
+ if (DevicePoll(Poller, 10)) {
+ hlsMutex.Lock();
// Handle running out of packets. Buffer-> Play-> Pause-> Buffer-> Play
- for(int i = 0; i < 10; i++) {
- // Get a pointer to start of the data and the number of avaliable bytes
- int bytesAvaliable = 0;
- uchar* toPlay = m_pSegmentLoader->m_pRingbuffer->Get(bytesAvaliable);
- if(bytesAvaliable >= TS_SIZE) {
- int playedBytes = PlayTs(toPlay, TS_SIZE, false);
- // save stream to disk to debug data
- if(m_pDebugFile) {
- m_pDebugFile-> write((char*)toPlay, playedBytes);
- }
-
- m_pSegmentLoader->m_pRingbuffer->Del(playedBytes);
- res = true;
- } else {
- // Pause & Buffer
- break;
- }
- }
- hlsMutex.Unlock();
- }
- return res;
-}
-
-void cHlsPlayer::Activate(bool On)
-{
- if(On) {
- Start();
- } else {
- Cancel(1);
- }
-}
-
-bool cHlsPlayer::GetIndex(int& Current, int& Total, bool SnapToIFrame __attribute__((unused)))
-{
- if(m_Video.m_Media.m_lDuration > 0) {
- Total = m_Video.m_Media.m_lDuration / 1000 * FramesPerSecond(); // milliseconds
- } else {
- Total = m_pSegmentLoader->GetStreamLenght() * FramesPerSecond();
- }
- Current = GetPlayedSeconds() * FramesPerSecond();
- return true;
-}
-
-bool cHlsPlayer::GetReplayMode(bool& Play, bool& Forward, int& Speed)
-{
- Play = (playMode == pmPlay);
- Forward = true;
- Speed = -1;
- return true;
-}
-
-void cHlsPlayer::Pause(void)
-{
- LOCK_THREAD;
- dsyslog("[plex]%s", __FUNCTION__);
- if (playMode == pmPause) {
- Play();
- } else {
- DeviceFreeze();
- playMode = pmPause;
- }
-}
-
-void cHlsPlayer::Play(void)
-{
- LOCK_THREAD;
- dsyslog("[plex]%s", __FUNCTION__);
- if (playMode != pmPlay) {
- DevicePlay();
- playMode = pmPlay;
- }
-}
-
-bool cHlsPlayer::Active(void)
-{
- return Running() && m_pSegmentLoader && m_pSegmentLoader->Active();
-}
-
-void cHlsPlayer::Stop(void)
-{
- LOCK_THREAD;
- dsyslog("[plex]%s", __FUNCTION__);
- ReportProgress(true);
- if (m_pSegmentLoader)
- m_pSegmentLoader->StopLoader();
- Cancel(1);
-}
-
-double cHlsPlayer::FramesPerSecond(void)
-{
- return m_Video.m_Media.m_VideoFrameRate == "24p" ? 24 : DEFAULTFRAMESPERSECOND;
-}
-
-void cHlsPlayer::JumpRelative(int seconds)
-{
- JumpTo(GetPlayedSeconds() + seconds);
-}
-
-void cHlsPlayer::JumpTo(int seconds)
-{
- LOCK_THREAD;
- dsyslog("[plex]%s %d", __FUNCTION__, seconds);
- if(seconds < 0) seconds = 0;
- m_jumpOffset = seconds;
- m_doJump = true;
-}
-
-void cHlsPlayer::SetAudioTrack(eTrackType Type __attribute__((unused)), const tTrackId* TrackId)
-{
- LOCK_THREAD;
- dsyslog("[plex]%s %d %s", __FUNCTION__, TrackId->id, TrackId->language);
- // Check if stream availiable
- int streamId = 0;
- if(m_Video.m_Media.m_vStreams.size() > 0) {
- std::vector<plexclient::Stream> streams = m_Video.m_Media.m_vStreams;
- for(std::vector<plexclient::Stream>::iterator it = streams.begin(); it != streams.end(); ++it) {
- plexclient::Stream *pStream = &(*it);
- if(pStream->m_eStreamType == plexclient::StreamType::sAUDIO && pStream->m_iIndex + AudioIndexOffset == TrackId->id) {
- streamId = pStream->m_iID;
- break;
- }
- }
- }
- // Then do the request
- if(streamId > 0) {
- std::string uri = "/library/parts/" + std::string(itoa(m_Video.m_Media.m_iPartId)) + "?audioStreamID=" + std::string(itoa(streamId));
-
- bool ok;
- auto cSession = m_Video.m_pServer->MakeRequest(ok, uri);
- if(ok) {
- DeviceSetCurrentAudioTrack(eTrackType(ttDolby + 0)); // hacky
- DeviceSetAvailableTrack(ttDolby, 0, 0, TrackId->language);
- JumpRelative(0); // Reload Stream to get new Audio
- dsyslog("[plex]: Set AudioStream: %d\n", TrackId->id);
- }
- }
-}
-
-int cHlsPlayer::GetPlayedSeconds(void)
-{
- return m_timeOffset + (m_tTimeSum / 1000);
-}
-
-void cHlsPlayer::CountPlayedSeconds(void)
-{
- if (playMode == pmPlay) {
- unsigned long long tTmp = cTimeMs::Now();
- m_tTimeSum += (tTmp - m_tLastTime);
- m_tLastTime = tTmp;
- } else {
- m_tLastTime = cTimeMs::Now();
- }
-}
-
-void cHlsPlayer::ResetPlayedSeconds(void)
-{
- m_tTimeSum = 0;
- m_tLastTime = cTimeMs::Now();
-}
-
-void cHlsPlayer::ReportProgress(bool stopped)
-{
- std::string state;
- if(stopped) {
- state = "stopped";
- } else if (playMode == pmPlay) {
- state = "playing";
- } else {
- state = "paused";
- }
-
- try {
- std::string uri = "/:/progress?key=" + std::string(itoa(m_Video.m_iRatingKey)) + "&identifier=com.plexapp.plugins.library&time=" + std::string(itoa(GetPlayedSeconds()*1000)) + "&state=" + state;
-
- bool ok;
- auto cSession = m_Video.m_pServer->MakeRequest(ok, uri);
-
- if(ok) {
- dsyslog("[plex] %s", __FUNCTION__);
- }
- } catch (Poco::Exception&) {}
-
-}
-
-void cHlsPlayer::SetWatched(void)
-{
- std::string uri = "/:/scrobble?key=" + std::string(itoa(m_Video.m_iRatingKey)) + "&identifier=com.plexapp.plugins.library";
-
- bool ok;
- auto cSession = m_Video.m_pServer->MakeRequest(ok, uri);
-
- if(ok) {
- dsyslog("[plex] %s", __FUNCTION__);
- }
+ for (int i = 0; i < 10; i++) {
+ // Get a pointer to start of the data and the number of avaliable bytes
+ int bytesAvaliable = 0;
+ uchar *toPlay = m_pSegmentLoader->m_pRingbuffer->Get(bytesAvaliable);
+ if (bytesAvaliable >= TS_SIZE) {
+ int playedBytes = PlayTs(toPlay, TS_SIZE, false);
+ // save stream to disk to debug data
+ if (m_pDebugFile) {
+ m_pDebugFile->write((char *) toPlay, playedBytes);
+ }
+
+ m_pSegmentLoader->m_pRingbuffer->Del(playedBytes);
+ res = true;
+ } else {
+ // Pause & Buffer
+ break;
+ }
+ }
+ hlsMutex.Unlock();
+ }
+ return res;
+}
+
+void cHlsPlayer::Activate(bool On) {
+ if (On) {
+ Start();
+ } else {
+ Cancel(1);
+ }
+}
+
+bool cHlsPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame __attribute__((unused))) {
+ if (m_Video.m_Media.m_lDuration > 0) {
+ Total = m_Video.m_Media.m_lDuration / 1000 * FramesPerSecond(); // milliseconds
+ } else {
+ Total = m_pSegmentLoader->GetStreamLenght() * FramesPerSecond();
+ }
+ Current = GetPlayedSeconds() * FramesPerSecond();
+ return true;
+}
+
+bool cHlsPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed) {
+ Play = (playMode == pmPlay);
+ Forward = true;
+ Speed = -1;
+ return true;
+}
+
+void cHlsPlayer::Pause(void) {
+ LOCK_THREAD;
+ dsyslog("[plex]%s", __FUNCTION__);
+ if (playMode == pmPause) {
+ Play();
+ } else {
+ DeviceFreeze();
+ playMode = pmPause;
+ }
+}
+
+void cHlsPlayer::Play(void) {
+ LOCK_THREAD;
+ dsyslog("[plex]%s", __FUNCTION__);
+ if (playMode != pmPlay) {
+ DevicePlay();
+ playMode = pmPlay;
+ }
+}
+
+bool cHlsPlayer::Active(void) {
+ return Running() && m_pSegmentLoader && m_pSegmentLoader->Active();
+}
+
+void cHlsPlayer::Stop(void) {
+ LOCK_THREAD;
+ dsyslog("[plex]%s", __FUNCTION__);
+ ReportProgress(true);
+ if (m_pSegmentLoader)
+ m_pSegmentLoader->StopLoader();
+ Cancel(1);
+}
+
+double cHlsPlayer::FramesPerSecond(void) {
+ return m_Video.m_Media.m_VideoFrameRate == "24p" ? 24 : DEFAULTFRAMESPERSECOND;
+}
+
+void cHlsPlayer::JumpRelative(int seconds) {
+ JumpTo(GetPlayedSeconds() + seconds);
+}
+
+void cHlsPlayer::JumpTo(int seconds) {
+ LOCK_THREAD;
+ dsyslog("[plex]%s %d", __FUNCTION__, seconds);
+ if (seconds < 0) seconds = 0;
+ m_jumpOffset = seconds;
+ m_doJump = true;
+}
+
+void cHlsPlayer::SetAudioTrack(eTrackType Type __attribute__((unused)), const tTrackId *TrackId) {
+ LOCK_THREAD;
+ dsyslog("[plex]%s %d %s", __FUNCTION__, TrackId->id, TrackId->language);
+ // Check if stream availiable
+ int streamId = 0;
+ if (m_Video.m_Media.m_vStreams.size() > 0) {
+ std::vector<plexclient::Stream> streams = m_Video.m_Media.m_vStreams;
+ for (std::vector<plexclient::Stream>::iterator it = streams.begin(); it != streams.end(); ++it) {
+ plexclient::Stream *pStream = &(*it);
+ if (pStream->m_eStreamType == plexclient::StreamType::sAUDIO &&
+ pStream->m_iIndex + AudioIndexOffset == TrackId->id) {
+ streamId = pStream->m_iID;
+ break;
+ }
+ }
+ }
+ // Then do the request
+ if (streamId > 0) {
+ std::string uri = "/library/parts/" + std::string(itoa(m_Video.m_Media.m_iPartId)) + "?audioStreamID=" +
+ std::string(itoa(streamId));
+
+ bool ok;
+ auto cSession = m_Video.m_pServer->MakeRequest(ok, uri);
+ if (ok) {
+ DeviceSetCurrentAudioTrack(eTrackType(ttDolby + 0)); // hacky
+ DeviceSetAvailableTrack(ttDolby, 0, 0, TrackId->language);
+ JumpRelative(0); // Reload Stream to get new Audio
+ dsyslog("[plex]: Set AudioStream: %d\n", TrackId->id);
+ }
+ }
+}
+
+int cHlsPlayer::GetPlayedSeconds(void) {
+ return m_timeOffset + (m_tTimeSum / 1000);
+}
+
+void cHlsPlayer::CountPlayedSeconds(void) {
+ if (playMode == pmPlay) {
+ unsigned long long tTmp = cTimeMs::Now();
+ m_tTimeSum += (tTmp - m_tLastTime);
+ m_tLastTime = tTmp;
+ } else {
+ m_tLastTime = cTimeMs::Now();
+ }
+}
+
+void cHlsPlayer::ResetPlayedSeconds(void) {
+ m_tTimeSum = 0;
+ m_tLastTime = cTimeMs::Now();
+}
+
+void cHlsPlayer::ReportProgress(bool stopped) {
+ std::string state;
+ if (stopped) {
+ state = "stopped";
+ } else if (playMode == pmPlay) {
+ state = "playing";
+ } else {
+ state = "paused";
+ }
+
+ try {
+ std::string uri = "/:/progress?key=" + std::string(itoa(m_Video.m_iRatingKey)) +
+ "&identifier=com.plexapp.plugins.library&time=" +
+ std::string(itoa(GetPlayedSeconds() * 1000)) + "&state=" + state;
+
+ bool ok;
+ auto cSession = m_Video.m_pServer->MakeRequest(ok, uri);
+
+ if (ok) {
+ dsyslog("[plex] %s", __FUNCTION__);
+ }
+ } catch (Poco::Exception &) { }
+
+}
+
+void cHlsPlayer::SetWatched(void) {
+ std::string uri =
+ "/:/scrobble?key=" + std::string(itoa(m_Video.m_iRatingKey)) + "&identifier=com.plexapp.plugins.library";
+
+ bool ok;
+ auto cSession = m_Video.m_pServer->MakeRequest(ok, uri);
+
+ if (ok) {
+ dsyslog("[plex] %s", __FUNCTION__);
+ }
}