diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/channelfilter.c vdr-plugin-vnsiserver-1.5.2+git2017-02-25/channelfilter.c --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/channelfilter.c 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/channelfilter.c 2017-02-25 17:14:09.000000000 +0000 @@ -37,6 +37,11 @@ } +cVNSIProvider::cVNSIProvider(const cVNSIProvider &provider) + :m_name(provider.m_name), m_caid(provider.m_caid) +{ +} + cVNSIProvider::cVNSIProvider(std::string name, int caid) :m_name(name), m_caid(caid) { diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/channelfilter.h vdr-plugin-vnsiserver-1.5.2+git2017-02-25/channelfilter.h --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/channelfilter.h 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/channelfilter.h 2017-02-25 17:14:09.000000000 +0000 @@ -35,6 +35,7 @@ public: cVNSIProvider(); cVNSIProvider(std::string name, int caid); + cVNSIProvider(const cVNSIProvider &provider); bool operator==(const cVNSIProvider &rhs) const; std::string m_name; int m_caid; diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/debian/changelog vdr-plugin-vnsiserver-1.5.2+git2017-02-25/debian/changelog --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/debian/changelog 2016-08-02 09:13:16.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/debian/changelog 2017-03-04 11:54:42.000000000 +0000 @@ -1,3 +1,38 @@ +vdr-plugin-vnsiserver (2:1.5.2+git2017-02-25-0yavdr0~trusty) trusty; urgency=medium + + * new upstream snapshot + - make grouping of series recordings configurable + + -- Alexander Grothe Sat, 04 Mar 2017 12:54:16 +0100 + +vdr-plugin-vnsiserver (2:1.5.2-0yavdr0~trusty) trusty; urgency=medium + + * new upstream release + - group recordings + - fixes for epg updates + - fixes for searchtimers + - see github history + + -- Alexander Grothe Sun, 29 Jan 2017 13:13:49 +0100 + +vdr-plugin-vnsiserver (2:1.5.1+git20160927-9b19e98-0yavdr0~trusty) trusty; urgency=medium + + * new upstream snapshot + - version 1.5.1 + - multiple fixes and cleanup + + -- Alexander Grothe Tue, 04 Oct 2016 09:21:11 +0200 + +vdr-plugin-vnsiserver (2:1.5.0+git20160815-1b96c6a-0yavdr0~trusty) trusty; urgency=medium + + * new upstream snapshot + - avoid triggering an epg update if there is already one pending + - make sure not to set m_WaitIFrame on radio channels + - fix resetting cam + + + -- Alexander Grothe Wed, 17 Aug 2016 21:59:31 +0200 + vdr-plugin-vnsiserver (2:1.5.0+git20160801-5f956c0-0yavdr0~trusty) trusty; urgency=medium * rebuild for trusty diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/demuxer.c vdr-plugin-vnsiserver-1.5.2+git2017-02-25/demuxer.c --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/demuxer.c 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/demuxer.c 2017-02-25 17:14:09.000000000 +0000 @@ -31,6 +31,31 @@ #include #include +cStreamInfo::cStreamInfo() +{ + +} + +cStreamInfo::cStreamInfo(const cStreamInfo& info) +{ + pID = info.pID; + type = info.type; + content = info.content; + subtitlingType = info.subtitlingType; + compositionPageId = info.compositionPageId; + ancillaryPageId = info.ancillaryPageId; + handleRDS = info.handleRDS; + SetLanguage(info.language); +} + +void cStreamInfo::SetLanguage(const char* lang) +{ + language[0] = lang[0]; + language[1] = lang[1]; + language[2] = lang[2]; + language[3] = 0; +} + cVNSIDemuxer::cVNSIDemuxer(bool bAllowRDS) : m_bAllowRDS(bAllowRDS) { @@ -59,6 +84,7 @@ m_MuxPacketSerial = 0; m_Error = ERROR_DEMUX_NODATA; m_SetRefTime = true; + m_seenFirstPacket = false; } void cVNSIDemuxer::Close() @@ -82,6 +108,9 @@ cMutexLock lock(&m_Mutex); + if (!m_CurrentChannel.Vpid()) + m_WaitIFrame = false; + // clear packet if (!packet) return -1; @@ -118,7 +147,7 @@ { cChannel pmtChannel(m_CurrentChannel); SetChannelPids(&pmtChannel, &m_PatPmtParser); - SetChannelStreams(&pmtChannel); + SetChannelStreamInfos(&pmtChannel); m_PatPmtParser.Reset(); if (EnsureParsers()) { @@ -133,6 +162,7 @@ if (error == 0) { m_WaitIFrame = false; + m_seenFirstPacket = true; packet->serial = m_MuxPacketSerial; if (m_SetRefTime) @@ -148,7 +178,7 @@ m_Error |= abs(error); if (m_Error & ERROR_PES_SCRAMBLE) { - if (!m_WaitIFrame) + if (m_seenFirstPacket) { ResetParsers(); m_Error |= ERROR_CAM_ERROR; @@ -379,14 +409,10 @@ { i->ResetParser(); } + m_seenFirstPacket = false; } -void cVNSIDemuxer::AddStreamInfo(sStreamInfo &stream) -{ - m_StreamInfos.push_back(stream); -} - -static bool Contains(const std::list &list, int pID, eStreamType type) +static bool Contains(const std::list &list, int pID, eStreamType type) { for (const auto &i : list) if (i.pID == pID && i.type == type) @@ -483,11 +509,11 @@ return streamChange; } -void cVNSIDemuxer::SetChannelStreams(const cChannel *channel) +void cVNSIDemuxer::SetChannelStreamInfos(const cChannel *channel) { m_StreamInfos.clear(); - sStreamInfo newStream; + cStreamInfo newStream; bool containsVideo = false; int index = 0; if (channel->Vpid()) @@ -502,7 +528,7 @@ #endif newStream.type = stMPEG2VIDEO; - AddStreamInfo(newStream); + m_StreamInfos.push_back(newStream); containsVideo = true; } @@ -515,7 +541,7 @@ if (channel->Dtype(index) == SI::EnhancedAC3DescriptorTag) newStream.type = stEAC3; newStream.SetLanguage(channel->Dlang(index)); - AddStreamInfo(newStream); + m_StreamInfos.push_back(newStream); index++; } @@ -531,7 +557,7 @@ newStream.type = stAACLATM; newStream.handleRDS = m_bAllowRDS && newStream.type == stMPEG2AUDIO && !containsVideo ? true : false; // Relevant for RDS, if present only on mpeg 2 audio, use only if RDS is allowed newStream.SetLanguage(channel->Alang(index)); - AddStreamInfo(newStream); + m_StreamInfos.push_back(newStream); index++; } @@ -547,7 +573,7 @@ newStream.subtitlingType = channel->SubtitlingType(index); newStream.compositionPageId = channel->CompositionPageId(index); newStream.ancillaryPageId = channel->AncillaryPageId(index); - AddStreamInfo(newStream); + m_StreamInfos.push_back(newStream); } index++; } @@ -556,7 +582,7 @@ { newStream.pID = channel->Tpid(); newStream.type = stTELETEXT; - AddStreamInfo(newStream); + m_StreamInfos.push_back(newStream); } } @@ -567,9 +593,9 @@ int Dpids[MAXDPIDS + 1] = { 0 }; int Dtypes[MAXDPIDS + 1] = { 0 }; int Spids[MAXSPIDS + 1] = { 0 }; - char ALangs[MAXAPIDS][MAXLANGCODE2] = { "" }; - char DLangs[MAXDPIDS][MAXLANGCODE2] = { "" }; - char SLangs[MAXSPIDS][MAXLANGCODE2] = { "" }; + char ALangs[MAXAPIDS][MAXLANGCODE2] = { 0 }; + char DLangs[MAXDPIDS][MAXLANGCODE2] = { 0 }; + char SLangs[MAXSPIDS][MAXLANGCODE2] = { 0 }; int index = 0; const int *aPids = patPmtParser->Apids(); diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/demuxer.h vdr-plugin-vnsiserver-1.5.2+git2017-02-25/demuxer.h --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/demuxer.h 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/demuxer.h 2017-02-25 17:14:09.000000000 +0000 @@ -33,23 +33,22 @@ class cPatPmtParser; class cVideoBuffer; -struct sStreamInfo +class cStreamInfo { - int pID; - eStreamType type; +public: + cStreamInfo(); + cStreamInfo(const cStreamInfo& info); + cStreamInfo& operator=(const cStreamInfo& info) = delete; + void SetLanguage(const char* lang); + + int pID = 0; + eStreamType type = eStreamType::stNone; eStreamContent content; - char language[MAXLANGCODE2]; + char language[MAXLANGCODE2] = { 0 }; int subtitlingType; int compositionPageId; int ancillaryPageId; - bool handleRDS; - void SetLanguage(const char* lang) - { - language[0] = lang[0]; - language[1] = lang[1]; - language[2] = lang[2]; - language[3] = 0; - } + bool handleRDS = false; }; class cVNSIDemuxer @@ -75,14 +74,13 @@ protected: bool EnsureParsers(); void ResetParsers(); - void SetChannelStreams(const cChannel *channel); + void SetChannelStreamInfos(const cChannel *channel); void SetChannelPids(cChannel *channel, cPatPmtParser *patPmtParser); cTSStream *FindStream(int Pid); - void AddStreamInfo(sStreamInfo &stream); bool GetTimeAtPos(off_t *pos, int64_t *time); std::list m_Streams; std::list::iterator m_StreamsIterator; - std::list m_StreamInfos; + std::list m_StreamInfos; cChannel m_CurrentChannel; cPatPmtParser m_PatPmtParser; bool m_WaitIFrame; @@ -94,4 +92,5 @@ bool m_SetRefTime; time_t m_refTime, m_endTime, m_wrapTime; bool m_bAllowRDS; + bool m_seenFirstPacket; }; diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/HISTORY vdr-plugin-vnsiserver-1.5.2+git2017-02-25/HISTORY --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/HISTORY 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/HISTORY 2017-02-25 17:14:09.000000000 +0000 @@ -1,5 +1,18 @@ VDR Plugin 'vnsiserver' Revision History ---------------------------------------- +2017-01-14: Version 1.5.2 + +- group recordings +- fixes for epg updates +- fixes for searchtimers +- see github history + +2016-09-25: Version 1.5.1 + +- see github history for fixes +- added new setup parameter DisableCamBlacklist: + avoids a VDR hard-coded 15 seconds timeout if tuning failed due to hard-coded scramble timeout + 2016-08-01: Version 1.5.0 - support for vdr 2.3.1 diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/parser_AAC.c vdr-plugin-vnsiserver-1.5.2+git2017-02-25/parser_AAC.c --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/parser_AAC.c 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/parser_AAC.c 2017-02-25 17:14:09.000000000 +0000 @@ -154,6 +154,9 @@ m_FrameSize = bs.readBits(13); m_SampleRate = aac_sample_rates[SampleRateIndex & 0x0E]; + if (!m_SampleRate) + m_SampleRate = aac_sample_rates[4]; + m_FoundFrame = true; m_DTS = m_curPTS; m_PTS = m_curPTS; diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/po/de_DE.po vdr-plugin-vnsiserver-1.5.2+git2017-02-25/po/de_DE.po --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/po/de_DE.po 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/po/de_DE.po 2017-02-25 17:14:09.000000000 +0000 @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: VNSI-Server 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-04-23 09:08+0200\n" +"POT-Creation-Date: 2016-09-26 21:50+0300\n" "PO-Revision-Date: 2015-01-23 21:46+0100\n" "Last-Translator: Alwin Esch\n" "Language-Team: German\n" @@ -51,6 +51,9 @@ msgid "Disable scramble timeout" msgstr "" +msgid "Disable cam blacklist" +msgstr "" + msgid "Recording with the same name exists" msgstr "Aufnahme mit der selben größe existiert" diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/po/lt_LT.po vdr-plugin-vnsiserver-1.5.2+git2017-02-25/po/lt_LT.po --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/po/lt_LT.po 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/po/lt_LT.po 2017-02-25 17:14:09.000000000 +0000 @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: VNSI-Server 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-04-23 09:08+0200\n" +"POT-Creation-Date: 2016-09-26 21:50+0300\n" "PO-Revision-Date: 2015-02-11 22:30+0200\n" "Last-Translator: Valdemaras Pipiras\n" "Language-Team: Lithuanian\n" @@ -51,6 +51,9 @@ msgid "Disable scramble timeout" msgstr "" +msgid "Disable cam blacklist" +msgstr "" + msgid "Recording with the same name exists" msgstr "Jau yra įrašų tokiu pat pavadinimu" diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/setup.c vdr-plugin-vnsiserver-1.5.2+git2017-02-25/setup.c --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/setup.c 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/setup.c 2017-02-25 17:14:09.000000000 +0000 @@ -31,8 +31,10 @@ int TimeshiftBufferFileSize = 6; char TimeshiftBufferDir[PATH_MAX] = "\0"; int PlayRecording = 0; +int GroupRecordings = 1; int AvoidEPGScan = 1; int DisableScrambleTimeout = 0; +int DisableCamBlacklist = 0; cMenuSetupVNSI::cMenuSetupVNSI(void) { @@ -54,11 +56,17 @@ newPlayRecording = PlayRecording; Add(new cMenuEditBoolItem( tr("Play Recording instead of live"), &newPlayRecording)); + newGroupRecordings = GroupRecordings; + Add(new cMenuEditBoolItem( tr("Group series recordings"), &newGroupRecordings)); + newAvoidEPGScan = AvoidEPGScan; Add(new cMenuEditBoolItem( tr("Avoid EPG scan while streaming"), &newAvoidEPGScan)); newDisableScrambleTimeout = DisableScrambleTimeout; Add(new cMenuEditBoolItem( tr("Disable scramble timeout"), &newDisableScrambleTimeout)); + + newDisableCamBlacklist = DisableCamBlacklist; + Add(new cMenuEditBoolItem( tr("Disable cam blacklist"), &newDisableCamBlacklist)); } void cMenuSetupVNSI::Store(void) @@ -90,7 +98,11 @@ SetupStore(CONFNAME_PLAYRECORDING, PlayRecording = newPlayRecording); + SetupStore(CONFNAME_GROUPRECORDINGS, GroupRecordings = newGroupRecordings); + SetupStore(CONFNAME_AVOIDEPGSCAN, AvoidEPGScan = newAvoidEPGScan); SetupStore(CONFNAME_DISABLESCRAMBLETIMEOUT, DisableScrambleTimeout = newDisableScrambleTimeout); + + SetupStore(CONFNAME_DISABLECAMBLACKLIST, DisableCamBlacklist = newDisableCamBlacklist); } diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/setup.h vdr-plugin-vnsiserver-1.5.2+git2017-02-25/setup.h --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/setup.h 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/setup.h 2017-02-25 17:14:09.000000000 +0000 @@ -37,8 +37,10 @@ int newTimeshiftBufferFileSize; char newTimeshiftBufferDir[PATH_MAX]; int newPlayRecording; + int newGroupRecordings; int newAvoidEPGScan; int newDisableScrambleTimeout; + int newDisableCamBlacklist; protected: virtual void Store(void); public: diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/status.c vdr-plugin-vnsiserver-1.5.2+git2017-02-25/status.c --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/status.c 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/status.c 2017-02-25 17:14:09.000000000 +0000 @@ -247,19 +247,23 @@ if (epg->Lock(epgState)) { epgState.Remove(false); - INFOLOG("Requesting clients to load epg"); - bool callAgain = false; + DEBUGLOG("Requesting clients to load epg"); + int callAgain = 0; for (auto &i : m_clients) { callAgain |= i.EpgChange(); } - if (callAgain) + if (callAgain & VNSI_EPG_AGAIN) { epgTimer.Set(100); epgState.Reset(); } else { + if (callAgain & VNSI_EPG_PAUSE) + { + epgState.Reset(); + } epgTimer.Set(5000); m_vnsiTimers->Scan(); } diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/streamer.c vdr-plugin-vnsiserver-1.5.2+git2017-02-25/streamer.c --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/streamer.c 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/streamer.c 2017-02-25 17:14:09.000000000 +0000 @@ -46,7 +46,7 @@ , m_ClientID(clientID) , m_scanTimeout(timeout) , m_Demuxer(bAllowRDS) - , m_VideoInput(m_Event, m_Mutex, m_IsRetune) + , m_VideoInput(m_Event) { m_Timeshift = timeshift; @@ -142,7 +142,6 @@ if (m_Channel && ((m_Channel->Source() >> 24) == 'V')) m_IsMPEGPS = true; - m_IsRetune = false; if (!m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer)) { ERRORLOG("Can't switch to channel %i - %s", m_Channel->Number(), m_Channel->Name()); @@ -186,13 +185,17 @@ bool requestStreamChangeSideData = false; cTimeMs last_info(1000); cTimeMs bufferStatsTimer(1000); + int openFailCount = 0; while (Running()) { - if (m_IsRetune) + auto retune = m_VideoInput.ReceivingStatus(); + if (retune == cVideoInput::RETUNE) + // allow timeshift playback when retune == cVideoInput::CLOSE ret = -1; else ret = m_Demuxer.Read(&pkt_data, &pkt_side_data); + if (ret > 0) { if (pkt_data.pmtChange) @@ -250,39 +253,39 @@ else if (ret == -1) { // no data + if (retune == cVideoInput::CLOSE) { - bool retune = false; - { - cMutexLock lock(&m_Mutex); - retune = m_IsRetune; - if (!retune) - m_Event.TimedWait(m_Mutex, 10); - } - - if (m_Demuxer.GetError() & ERROR_CAM_ERROR) - { - INFOLOG("CAM error, try reset"); - cCamSlot *cs = m_Device->CamSlot(); - if (cs) - cs->StopDecrypting(); - retune = true; - } - - if (retune) + m_Socket->Shutdown(); + break; + } + if (m_Demuxer.GetError() & ERROR_CAM_ERROR) + { + INFOLOG("CAM error, try reset"); + cCamSlot *cs = m_Device->CamSlot(); + if (cs) + cs->StopDecrypting(); + retune = cVideoInput::RETUNE; + } + if (retune == cVideoInput::RETUNE) + { + INFOLOG("re-tuning..."); + m_VideoInput.Close(); + if (!m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer)) { - m_VideoInput.Close(); - if (m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer)) + if (++openFailCount == 3) { - cMutexLock lock(&m_Mutex); - m_IsRetune = false; + openFailCount = 0; + cCondWait::SleepMs(2000); } else - { - cMutexLock lock(&m_Mutex); - m_Event.TimedWait(m_Mutex, 100); - } + cCondWait::SleepMs(100); } + else + openFailCount = 0; } + else + m_Event.Wait(10); + if(m_last_tick.Elapsed() >= (uint64_t)(m_scanTimeout*1000)) { sendStreamStatus(); @@ -706,7 +709,5 @@ return; INFOLOG("re-tune to channel %s", m_Channel->Name()); - cMutexLock lock(&m_Mutex); - m_IsRetune = true; - m_Event.Broadcast(); + m_VideoInput.RequestRetune(); } diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/streamer.h vdr-plugin-vnsiserver-1.5.2+git2017-02-25/streamer.h --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/streamer.h 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/streamer.h 2017-02-25 17:14:09.000000000 +0000 @@ -32,7 +32,6 @@ #include #include #include -#include #include "parser.h" #include "responsepacket.h" @@ -49,8 +48,6 @@ class cLiveStreamer : public cThread { friend class cParser; - friend class cLivePatFilter; - friend class cLiveReceiver; void sendStreamPacket(sStreamPacket *pkt); void sendStreamChange(); @@ -80,9 +77,7 @@ cVideoInput m_VideoInput; int m_Priority; uint8_t m_Timeshift; - cCondVar m_Event; - cMutex m_Mutex; - bool m_IsRetune = false; + cCondWait m_Event; protected: virtual void Action(void); diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/videoinput.c vdr-plugin-vnsiserver-1.5.2+git2017-02-25/videoinput.c --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/videoinput.c 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/videoinput.c 2017-02-25 17:14:09.000000000 +0000 @@ -35,6 +35,9 @@ #include #include #include +#include +#include +#include // --- cLiveReceiver ------------------------------------------------- @@ -77,13 +80,11 @@ m_VideoInput->Receive(Data, Length); } -inline void cLiveReceiver::Activate(bool On) +void cLiveReceiver::Activate(bool On) { - INFOLOG("activate live receiver: %d, pmt change: %d", On, m_VideoInput->m_PmtChange); - if (!On && !m_VideoInput->m_PmtChange) - { - m_VideoInput->Retune(); - } + INFOLOG("activate live receiver: %d", On); + if (!On) + m_VideoInput->RequestRetune(); } // --- cLivePatFilter ---------------------------------------------------- @@ -407,24 +408,89 @@ pmtChannel->SetPids(Vpid, Ppid, Vtype, Apids, Atypes, ALangs, Dpids, Dtypes, DLangs, Spids, SLangs, Tpid); pmtChannel->SetSubtitlingDescriptors(SubtitlingTypes, CompositionPageIds, AncillaryPageIds); if (pmtChannel->Modification(CHANNELMOD_PIDS)) - m_VideoInput->Retune(); + m_VideoInput->RequestRetune(); } } #endif } +// --- cDummyReceiver ---------------------------------------------------- + +// This dummy receiver is used to detect if a recording/streaming task +// with a higher priority has acquired the device and detached *all* +// receivers from it. +class cDummyReceiver : public cReceiver +{ +public: + // Return a new or existing dummy receiver attached to the device. + static std::shared_ptr Create(cDevice *device); + virtual ~cDummyReceiver() {Detach();} + bool BeenDetached() {return m_BeenDetached;} +protected: +#if VDRVERSNUM >= 20301 + virtual void Receive(const uchar *Data, int Length) {} +#else + virtual void Receive(uchar *Data, int Length) {} +#endif + virtual void Activate(bool On); +private: + static std::vector> s_Pool; + static cMutex s_PoolMutex; + std::atomic m_BeenDetached; + cDummyReceiver() : cReceiver(NULL, MINPRIORITY), m_BeenDetached(false) {} +}; + +std::vector> cDummyReceiver::s_Pool; +cMutex cDummyReceiver::s_PoolMutex; + +void cDummyReceiver::Activate(bool On) +{ + INFOLOG("Dummy receiver (%p) %s", this, (On)? "activated" : "deactivated"); + if (!On) + m_BeenDetached = true; +} + +std::shared_ptr cDummyReceiver::Create(cDevice *device) +{ + if (!device) + return nullptr; + + cMutexLock MutexLock(&s_PoolMutex); + // cleanup + s_Pool.erase(std::remove_if(s_Pool.begin(), s_Pool.end(), + [](const std::weak_ptr &p) -> bool + {return !p.lock();}), s_Pool.end()); + + // find an active receiver for the device + for (auto p : s_Pool) + { + auto recv = p.lock(); + if (!recv->BeenDetached() && recv->Device() == device) + return recv; + } + auto recv = std::shared_ptr(new cDummyReceiver); + if (device->AttachReceiver(recv.get())) + { + s_Pool.push_back(recv); + return recv; + } + return nullptr; +} + // ---------------------------------------------------------------------------- -cVideoInput::cVideoInput(cCondVar &condVar, cMutex &mutex, bool &retune) : - m_Event(condVar), m_Mutex(mutex), m_IsRetune(retune) +cVideoInput::cVideoInput(cCondWait &event) + : m_Event(event) + , m_RetuneRequested(false) { - m_Device = NULL;; + m_Device = NULL; + m_camSlot = nullptr; m_PatFilter = NULL; - m_Receiver = NULL;; + m_Receiver = NULL; m_Channel = NULL; m_VideoBuffer = NULL; m_Priority = 0; - m_PmtChange = false; + m_DataSeen = false; } cVideoInput::~cVideoInput() @@ -437,11 +503,14 @@ m_VideoBuffer = videoBuffer; m_Channel = channel; m_Priority = priority; + m_RetuneRequested = false; + m_DataSeen = false; m_Device = cDevice::GetDevice(m_Channel, m_Priority, false); + m_camSlot = nullptr; if (m_Device != NULL) { - INFOLOG("Successfully found following device: %p (%d) for receiving", m_Device, m_Device ? m_Device->CardIndex() + 1 : 0); + INFOLOG("Successfully found following device: %p (%d) for receiving, priority=%d", m_Device, m_Device ? m_Device->CardIndex() + 1 : 0, m_Priority); if (m_Device->SwitchChannel(m_Channel, false)) { @@ -454,24 +523,36 @@ #endif m_PmtChannel = *m_Channel; - m_PmtChange = true; m_Receiver = new cLiveReceiver(this, m_Channel, m_Priority); m_Receiver->SetPids(NULL); m_Receiver->SetPids(&m_PmtChannel); m_Receiver->AddPid(m_PmtChannel.Tpid()); - m_Device->AttachReceiver(m_Receiver); + m_DummyReceiver = cDummyReceiver::Create(m_Device); + if (!m_DummyReceiver) + return false; + + m_camSlot = m_Device->CamSlot(); #if VDRVERSNUM >= 20107 - if (DisableScrambleTimeout) + if (DisableScrambleTimeout && m_camSlot) { - cCamSlot *cs = m_Device->CamSlot(); - if (cs) - cs->StartActivation(); + // HACK cDevice::AttachReceiver() doesn't start scrambling + // timer if priority == MINPRIORITY + m_Receiver->SetPriority(MINPRIORITY); + if (!m_Device->AttachReceiver(m_Receiver)) + return false; + if (m_camSlot) + m_camSlot->StartDecrypting(); + m_Receiver->SetPriority(m_Priority); } + else #endif - + { + if (!m_Device->AttachReceiver(m_Receiver)) + return false; + } m_VideoBuffer->AttachInput(true); return true; } @@ -485,15 +566,17 @@ if (m_Device) { - -#if VDRVERSNUM >= 20107 - if (DisableScrambleTimeout) + if (DisableCamBlacklist) { - cCamSlot *cs = m_Device->CamSlot(); - if (cs) - cs->CancelActivation(); + // HACK Undo ChannelCamRelations.SetChecked() - see cDevice::Action(). + // Note: m_Device->CamSlot() returns NULL after SetChecked() is called. + // Use m_camSlot here. + if (m_Receiver && m_camSlot) + { + ChannelCamRelations.ClrChecked(m_Receiver->ChannelID(), + m_camSlot->SlotNumber()); + } } -#endif if (m_Receiver) { @@ -515,6 +598,8 @@ DEBUGLOG("No live filter present"); } + m_DummyReceiver.reset(); + if (m_Receiver) { DEBUGLOG("Deleting Live Receiver"); @@ -551,7 +636,7 @@ inline void cVideoInput::Receive(const uchar *data, int length) { - if (m_PmtChange) + if (!m_DataSeen) { // generate pat/pmt so we can configure parsers later cPatPmtGenerator patPmtGenerator(&m_PmtChannel); @@ -559,14 +644,28 @@ int Index = 0; while (uchar *pmt = patPmtGenerator.GetPmt(Index)) m_VideoBuffer->Put(pmt, TS_SIZE); - m_PmtChange = false; + m_DataSeen = true; } m_VideoBuffer->Put(data, length); } -void cVideoInput::Retune() +void cVideoInput::RequestRetune() +{ + m_RetuneRequested = true; + m_Event.Signal(); +} + +cVideoInput::eReceivingStatus cVideoInput::ReceivingStatus() { - cMutexLock lock(&m_Mutex); - m_IsRetune = true; - m_Event.Broadcast(); + if (!m_Device || !m_DummyReceiver) + return RETUNE; + if (m_RetuneRequested) + { + (void)m_Device->Receiving(); // wait for the receivers mutex + if (m_DummyReceiver->BeenDetached()) // DetachAllReceivers() was called + return CLOSE; + else + return RETUNE; + } + return NORMAL; } diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/videoinput.h vdr-plugin-vnsiserver-1.5.2+git2017-02-25/videoinput.h --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/videoinput.h 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/videoinput.h 2017-02-25 17:14:09.000000000 +0000 @@ -24,6 +24,8 @@ #pragma once +#include +#include #include #include @@ -31,31 +33,35 @@ class cLiveReceiver; class cVideoBuffer; class cDevice; +class cDummyReceiver; +class cCamSlot; class cVideoInput { friend class cLivePatFilter; friend class cLiveReceiver; public: - cVideoInput(cCondVar &condVar, cMutex &mutex, bool &retune); + cVideoInput(cCondWait &event); virtual ~cVideoInput(); bool Open(const cChannel *channel, int priority, cVideoBuffer *videoBuffer); void Close(); bool IsOpen(); - + void RequestRetune(); + enum eReceivingStatus {NORMAL, RETUNE, CLOSE}; + eReceivingStatus ReceivingStatus(); protected: cChannel *PmtChannel(); void Receive(const uchar *data, int length); - void Retune(); cDevice *m_Device; + cCamSlot *m_camSlot; cLivePatFilter *m_PatFilter; cLiveReceiver *m_Receiver; const cChannel *m_Channel; cVideoBuffer *m_VideoBuffer; int m_Priority; - bool m_PmtChange; - cCondVar &m_Event; - cMutex &m_Mutex; - bool &m_IsRetune; + bool m_DataSeen; cChannel m_PmtChannel; + cCondWait &m_Event; + std::shared_ptr m_DummyReceiver; + std::atomic m_RetuneRequested; }; diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsi.c vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsi.c --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsi.c 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsi.c 2017-02-25 17:14:09.000000000 +0000 @@ -154,12 +154,17 @@ if (*TimeshiftBufferDir && TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/') /* strip trailing slash */ TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] = 0; - } else if (!strcasecmp(Name, CONFNAME_PLAYRECORDING)) + } + else if (!strcasecmp(Name, CONFNAME_PLAYRECORDING)) PlayRecording = atoi(Value); + else if (!strcasecmp(Name, CONFNAME_GROUPRECORDINGS)) + GroupRecordings = atoi(Value); else if (!strcasecmp(Name, CONFNAME_AVOIDEPGSCAN)) AvoidEPGScan = atoi(Value); else if (!strcasecmp(Name, CONFNAME_DISABLESCRAMBLETIMEOUT)) DisableScrambleTimeout = atoi(Value); + else if (!strcasecmp(Name, CONFNAME_DISABLECAMBLACKLIST)) + DisableCamBlacklist = atoi(Value); else return false; return true; diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsiclient.c vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsiclient.c --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsiclient.c 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsiclient.c 2017-02-25 17:14:09.000000000 +0000 @@ -37,11 +37,13 @@ #include "hash.h" #include "channelfilter.h" #include "channelscancontrol.h" - +#include +#include #include #include #include #include +#include #include #include @@ -214,9 +216,9 @@ m_socket.write(resp.getPtr(), resp.getLen()); } -bool cVNSIClient::EpgChange() +int cVNSIClient::EpgChange() { - bool callAgain = false; + int callAgain = 0; cMutexLock lock(&m_msgLock); @@ -266,7 +268,15 @@ continue; } + time_t now = time(nullptr); + if ((now - it->second.lastTrigger) < 5) + { + callAgain = VNSI_EPG_PAUSE; + continue; + } + it->second.attempts++; + it->second.lastTrigger = now; DEBUGLOG("Trigger EPG update for channel %s, id: %d", channel->Name(), channelId); @@ -276,7 +286,7 @@ resp.finalise(); m_socket.write(resp.getPtr(), resp.getLen()); - callAgain = true; + callAgain = VNSI_EPG_AGAIN; break; } @@ -1429,20 +1439,20 @@ { cMutexLock lock(&m_timerLock); - uint32_t number = req.extract_U32(); + uint32_t id = req.extract_U32(); cResponsePacket resp; resp.init(req.getRequestID()); - if (number & m_vnsiTimers.INDEX_MASK) + if (id & m_vnsiTimers.VNSITIMER_MASK) { CVNSITimer timer; - if (m_vnsiTimers.GetTimer(number, timer)) + if (m_vnsiTimers.GetTimer(id, timer)) { resp.add_U32(VNSI_RET_OK); resp.add_U32(VNSI_TIMER_TYPE_EPG_SEARCH); - resp.add_U32(number); + resp.add_U32(id); resp.add_U32(timer.m_enabled); resp.add_U32(0); resp.add_U32(0); @@ -1456,6 +1466,10 @@ resp.add_U32(0); resp.add_String(timer.m_name.c_str()); resp.add_String(timer.m_search.c_str()); + if (m_protocolVersion >= 10) + { + resp.add_U32(0); + } } else { @@ -1469,12 +1483,12 @@ int numTimers = Timers->Count(); if (numTimers > 0) { - const cTimer *timer = Timers->Get(number-1); + const cTimer *timer = Timers->GetById(id); #else int numTimers = Timers.Count(); if (numTimers > 0) { - cTimer *timer = Timers.Get(number-1); + cTimer *timer = Timers.Get(id-1); #endif if (timer) { @@ -1489,7 +1503,11 @@ type = VNSI_TIMER_TYPE_MAN; resp.add_U32(type); } +#if VDRVERSNUM >= 20301 + resp.add_U32(timer->Id()); +#else resp.add_U32(timer->Index()+1); +#endif resp.add_U32(timer->HasFlags(tfActive)); resp.add_U32(timer->Recording()); resp.add_U32(timer->Pending()); @@ -1506,6 +1524,10 @@ { resp.add_String(""); } + if (m_protocolVersion >= 10) + { + resp.add_U32(m_vnsiTimers.GetParent(timer)); + } } else resp.add_U32(VNSI_RET_DATAUNKNOWN); @@ -1553,7 +1575,11 @@ type = VNSI_TIMER_TYPE_MAN; resp.add_U32(type); } +#if VDRVERSNUM >= 20301 + resp.add_U32(timer->Id()); +#else resp.add_U32(timer->Index()+1); +#endif resp.add_U32(timer->HasFlags(tfActive)); resp.add_U32(timer->Recording()); resp.add_U32(timer->Pending()); @@ -1570,14 +1596,17 @@ { resp.add_String(""); } + if (m_protocolVersion >= 10) + { + resp.add_U32(m_vnsiTimers.GetParent(timer)); + } } std::vector vnsitimers = m_vnsiTimers.GetTimers(); - int idx = m_vnsiTimers.INDEX_MASK; for (auto &vnsitimer : vnsitimers) { resp.add_U32(VNSI_TIMER_TYPE_EPG_SEARCH); - resp.add_U32(idx); + resp.add_U32(vnsitimer.m_id | m_vnsiTimers.VNSITIMER_MASK); resp.add_U32(vnsitimer.m_enabled); resp.add_U32(0); resp.add_U32(0); @@ -1591,7 +1620,10 @@ resp.add_U32(0); resp.add_String(vnsitimer.m_name.c_str()); resp.add_String(vnsitimer.m_search.c_str()); - idx++; + if (m_protocolVersion >= 10) + { + resp.add_U32(0); + } } resp.finalise(); m_socket.write(resp.getPtr(), resp.getLen()); @@ -1621,6 +1653,14 @@ if (m_protocolVersion >= 9) epgsearch = req.extract_String(); + uint32_t marginStart = 0; + uint32_t marginEnd = 0; + if (m_protocolVersion >= 10) + { + marginStart = req.extract_U32(); + marginEnd = req.extract_U32(); + } + // handle instant timers if(startTime == -1 || startTime == 0) { @@ -1655,7 +1695,10 @@ vnsitimer.m_channelUID = channelid; vnsitimer.m_search = epgsearch; vnsitimer.m_enabled = flags; + vnsitimer.m_priority = priority; vnsitimer.m_lifetime = lifetime; + vnsitimer.m_marginStart = marginStart; + vnsitimer.m_marginEnd = marginEnd; m_vnsiTimers.Add(std::move(vnsitimer)); resp.add_U32(VNSI_RET_OK); } @@ -1712,17 +1755,17 @@ { cMutexLock lock(&m_timerLock); - uint32_t number = req.extract_U32(); - bool force = req.extract_U32(); + uint32_t id = req.extract_U32(); + bool force = req.extract_U32(); cResponsePacket resp; resp.init(req.getRequestID()); - if (number & m_vnsiTimers.INDEX_MASK) + if (id & m_vnsiTimers.VNSITIMER_MASK) { - if (m_vnsiTimers.DeleteTimer(number)) + if (m_vnsiTimers.DeleteTimer(id)) { - INFOLOG("Deleting vnsitimer %d", number); + INFOLOG("Deleting vnsitimer %d", id); resp.add_U32(VNSI_RET_OK); } else @@ -1735,85 +1778,84 @@ { #if VDRVERSNUM >= 20301 LOCK_TIMERS_WRITE; - int timersCount = Timers->Count(); + cTimer *timer = Timers->GetById(id); + if (timer) + { + Timers->SetExplicitModify(); + { + if (timer->Recording()) + { + if (force) + { + timer->Skip(); + cRecordControls::Process(Timers, time(NULL)); + } + else + { + ERRORLOG("Timer \"%i\" is recording and can be deleted (use force=1 to stop it)", id); + resp.add_U32(VNSI_RET_RECRUNNING); + resp.finalise(); + m_socket.write(resp.getPtr(), resp.getLen()); + return true; + } + } + INFOLOG("Deleting timer %s", *timer->ToDescr()); + Timers->Del(timer); + Timers->SetModified(); + resp.add_U32(VNSI_RET_OK); + } + } + else + { + ERRORLOG("Error in timer settings"); + resp.add_U32(VNSI_RET_DATAINVALID); + } #else int timersCount = Timers.Count(); -#endif - - if (number <= 0 || number > (uint32_t)timersCount) + if (id <= 0 || id > (uint32_t)timersCount) { ERRORLOG("Unable to delete timer - invalid timer identifier"); resp.add_U32(VNSI_RET_DATAINVALID); } - else + cTimer *timer = Timers.Get(id-1); + if (timer) { -#if VDRVERSNUM >= 20301 - cTimer *timer = Timers->Get(number-1); - if (timer) + if (!Timers.BeingEdited()) { - Timers->SetExplicitModify(); + if (timer->Recording()) { - if (timer->Recording()) + if (force) { - if (force) - { - timer->Skip(); - cRecordControls::Process(Timers, time(NULL)); - } - else - { - ERRORLOG("Timer \"%i\" is recording and can be deleted (use force=1 to stop it)", number); - resp.add_U32(VNSI_RET_RECRUNNING); - resp.finalise(); - m_socket.write(resp.getPtr(), resp.getLen()); - return true; - } + timer->Skip(); + cRecordControls::Process(time(NULL)); } - INFOLOG("Deleting timer %s", *timer->ToDescr()); - Timers->Del(timer); - Timers->SetModified(); - resp.add_U32(VNSI_RET_OK); - } -#else - cTimer *timer = Timers.Get(number-1); - if (timer) - { - if (!Timers.BeingEdited()) - { - if (timer->Recording()) + else { - if (force) - { - timer->Skip(); - cRecordControls::Process(time(NULL)); - } - else - { - ERRORLOG("Timer \"%i\" is recording and can be deleted (use force=1 to stop it)", number); - resp.add_U32(VNSI_RET_RECRUNNING); - resp.finalise(); - m_socket.write(resp.getPtr(), resp.getLen()); - return true; - } + ERRORLOG("Timer \"%i\" is recording and can be deleted (use force=1 to stop it)", id); + resp.add_U32(VNSI_RET_RECRUNNING); + resp.finalise(); + m_socket.write(resp.getPtr(), resp.getLen()); + return true; } - INFOLOG("Deleting timer %s", *timer->ToDescr()); - Timers.Del(timer); - Timers.SetModified(); - resp.add_U32(VNSI_RET_OK); } - else - { - ERRORLOG("Unable to delete timer - timers being edited at VDR"); - resp.add_U32(VNSI_RET_DATALOCKED); - } -#endif + INFOLOG("Deleting timer %s", *timer->ToDescr()); + Timers.Del(timer); + Timers.SetModified(); + resp.add_U32(VNSI_RET_OK); } else { - ERRORLOG("Unable to delete timer - invalid timer identifier"); - resp.add_U32(VNSI_RET_DATAINVALID); + ERRORLOG("Unable to delete timer - timers being edited at VDR"); + resp.add_U32(VNSI_RET_DATALOCKED); } } + else + { + ERRORLOG("Error in timer settings"); + resp.add_U32(VNSI_RET_DATAINVALID); + } +#endif + } resp.finalise(); m_socket.write(resp.getPtr(), resp.getLen()); @@ -1832,7 +1874,7 @@ const char *aux; std::string epgsearch; - uint32_t index = req.extract_U32(); + uint32_t id = req.extract_U32(); cResponsePacket resp; resp.init(req.getRequestID()); @@ -1856,7 +1898,7 @@ epgsearch = req.extract_String(); } - if (index & m_vnsiTimers.INDEX_MASK) + if (id & m_vnsiTimers.VNSITIMER_MASK) { CVNSITimer vnsitimer; vnsitimer.m_name = aux; @@ -1865,9 +1907,9 @@ vnsitimer.m_enabled = active; vnsitimer.m_priority = priority; vnsitimer.m_lifetime = lifetime; - if (!m_vnsiTimers.UpdateTimer(index, vnsitimer)) + if (!m_vnsiTimers.UpdateTimer(id, vnsitimer)) { - ERRORLOG("Timer \"%u\" not defined", index); + ERRORLOG("Timer \"%u\" not defined", id); resp.add_U32(VNSI_RET_DATAUNKNOWN); resp.finalise(); m_socket.write(resp.getPtr(), resp.getLen()); @@ -1878,14 +1920,14 @@ { #if VDRVERSNUM >= 20301 LOCK_TIMERS_WRITE; - cTimer *timer = Timers->Get(index - 1); + cTimer *timer = Timers->GetById(id); #else - cTimer *timer = Timers.Get(index - 1); + cTimer *timer = Timers.Get(id - 1); #endif if (!timer) { - ERRORLOG("Timer \"%u\" not defined", index); + ERRORLOG("Timer \"%u\" not defined", id); resp.add_U32(VNSI_RET_DATAUNKNOWN); resp.finalise(); m_socket.write(resp.getPtr(), resp.getLen()); @@ -2090,7 +2132,7 @@ char* fullname = strdup(recording->Name()); char* recname = strrchr(fullname, FOLDERDELIMCHAR); - char* directory = NULL; + char* directory = nullptr; if(recname == NULL) { @@ -2122,15 +2164,50 @@ if(directory != NULL) { char* p = directory; - while(*p != 0) { - if(*p == FOLDERDELIMCHAR) *p = '/'; - if(*p == '_') *p = ' '; + while(*p != 0) + { + if (*p == FOLDERDELIMCHAR) + *p = '/'; + else if (*p == '_') + *p = ' '; p++; } - while(*directory == '/') directory++; + while(*directory == '/') + directory++; + } + + std::string strDirectory; + if (directory) + strDirectory = directory; + + if (GroupRecordings) + { + int noOfEntries = 1; + char* filename = strdup(recording->FileName()); + char *pch = strrchr(filename, '/'); + if (pch) + { + int noOfRecs = 0; + *pch = 0; + char* foldername = filename; + struct dirent **fileListTemp; + noOfEntries = scandir(foldername, &fileListTemp, NULL, alphasort); + for (int i=0; id_name); + if (name.find(".rec") != std::string::npos) + noOfRecs++; + } + if (noOfRecs > 1) + { + strDirectory += "/"; + strDirectory += recname; + } + } + free(filename); } - resp.add_String((isempty(directory)) ? "" : m_toUTF8.Convert(directory)); + resp.add_String(strDirectory.empty() ? "" : m_toUTF8.Convert(strDirectory.c_str())); // filename / uid of recording uint32_t uid = cRecordingsCache::GetInstance().Register(recording, false); diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsiclient.h vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsiclient.h --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsiclient.h 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsiclient.h 2017-02-25 17:14:09.000000000 +0000 @@ -23,8 +23,7 @@ * */ -#ifndef VNSI_CLIENT_H -#define VNSI_CLIENT_H +#pragma once #include #include @@ -38,6 +37,9 @@ #include #include +#define VNSI_EPG_AGAIN 1 +#define VNSI_EPG_PAUSE 2 + class cChannel; class cDevice; class cLiveStreamer; @@ -71,6 +73,7 @@ { int attempts = 0; time_t lastEvent = 0; + time_t lastTrigger = 0; } sEpgUpdate; std::map m_epgUpdate; CVNSITimers &m_vnsiTimers; @@ -97,7 +100,7 @@ void ChannelsChange(); void RecordingsChange(); void SignalTimerChange(); - bool EpgChange(); + int EpgChange(); static bool InhibidDataUpdates() { return m_inhibidDataUpdates; } unsigned int GetID() { return m_Id; } @@ -204,5 +207,3 @@ void processSCAN_IsFinished(); void processSCAN_SetStatus(int status); }; - -#endif // VNSI_CLIENT_H diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsicommand.h vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsicommand.h --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsicommand.h 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsicommand.h 2017-02-25 17:14:09.000000000 +0000 @@ -27,7 +27,7 @@ #define VNSI_COMMAND_H /** Current VNSI Protocol Version number */ -#define VNSI_PROTOCOLVERSION 9 +#define VNSI_PROTOCOLVERSION 10 /** Start of RDS support protocol Version */ #define VNSI_RDS_PROTOCOLVERSION 8 @@ -54,6 +54,8 @@ #define CONFNAME_PLAYRECORDING "PlayRecording" #define CONFNAME_AVOIDEPGSCAN "AvoidEPGScan" #define CONFNAME_DISABLESCRAMBLETIMEOUT "DisableScrambleTimeout" +#define CONFNAME_DISABLECAMBLACKLIST "DisableCamBlacklist" +#define CONFNAME_GROUPRECORDINGS "GroupRecordings" /* OPCODE 1 - 19: VNSI network functions for general purpose */ #define VNSI_LOGIN 1 diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsi.h vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsi.h --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsi.h 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsi.h 2017-02-25 17:14:09.000000000 +0000 @@ -28,7 +28,7 @@ #include #include "vnsiserver.h" -static const char *VERSION = "1.5.0"; +static const char *VERSION = "1.5.2"; static const char *DESCRIPTION = "VDR-Network-Streaming-Interface (VNSI) Server"; extern int PmtTimeout; @@ -37,8 +37,10 @@ extern int TimeshiftBufferFileSize; extern char TimeshiftBufferDir[PATH_MAX]; extern int PlayRecording; +extern int GroupRecordings; extern int AvoidEPGScan; extern int DisableScrambleTimeout; +extern int DisableCamBlacklist; class cDvbVsniDeviceProbe : public cDvbDeviceProbe { diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsiosd.c vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsiosd.c --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsiosd.c 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsiosd.c 2017-02-25 17:14:09.000000000 +0000 @@ -29,7 +29,7 @@ #include "vnsi.h" #include #include -#include +#include #include #include #include "cxsocket.h" diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsitimer.c vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsitimer.c --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsitimer.c 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsitimer.c 2017-02-25 17:14:09.000000000 +0000 @@ -112,8 +112,32 @@ std::string lifetime = line.substr(0, pos); timer.m_lifetime = strtol(lifetime.c_str(), &pend, 10); - timer.m_search = line.substr(pos+1); + // searchstring + line = line.substr(pos+1); + pos = line.find(";"); + if (pos == line.npos) + { + timer.m_search = line.substr(pos+1); + } + else + { + timer.m_search = line.substr(0, pos); + } + // created timers + if (pos != line.npos) + { + line = line.substr(pos+1); + while ((pos = line.find(",")) != line.npos) + { + std::string tmp = line.substr(0, pos); + time_t starttime = strtol(tmp.c_str(), &pend, 10); + timer.m_timersCreated.push_back(starttime); + line = line.substr(pos+1); + } + } + + timer.m_id = ++m_nextId; m_timers.emplace_back(std::move(timer)); } rfile.close(); @@ -138,7 +162,13 @@ << timer.m_enabled << ';' << timer.m_priority << ';' << timer.m_lifetime << ';' - << timer.m_search << '\n'; + << timer.m_search << ';'; + + for (auto &starttime : timer.m_timersCreated) + { + wfile << starttime << ','; + } + wfile << '\n'; } wfile.close(); } @@ -151,6 +181,7 @@ return; timer.m_channelID = channel->GetChannelID(); + timer.m_id = ++m_nextId; cMutexLock lock(&m_timerLock); m_timers.emplace_back(std::move(timer)); @@ -176,38 +207,151 @@ return m_timers; } -bool CVNSITimers::GetTimer(int idx, CVNSITimer &timer) +bool CVNSITimers::GetTimer(int id, CVNSITimer &timer) { cMutexLock lock(&m_timerLock); - idx &= ~INDEX_MASK; - if (idx < 0 || idx >= (int)m_timers.size()) - return false; - timer = m_timers[idx]; - return true; + id &= ~VNSITIMER_MASK; + + for (auto &searchtimer : m_timers) + { + if (searchtimer.m_id == id) + { + timer = searchtimer; + return true; + } + } + return false; } -bool CVNSITimers::UpdateTimer(int idx, CVNSITimer &timer) +bool CVNSITimers::UpdateTimer(int id, CVNSITimer &timer) { cMutexLock lock(&m_timerLock); - idx &= ~INDEX_MASK; - if (idx < 0 || idx >= (int)m_timers.size()) - return false; - m_timers[idx] = timer; - m_state++; - Save(); - return true; + id &= ~VNSITIMER_MASK; + + for (auto &searchtimer : m_timers) + { + if (searchtimer.m_id == id) + { + const cChannel *channel = FindChannelByUID(timer.m_channelUID); + if (!channel) + return false; + + if (timer.m_channelUID != searchtimer.m_channelUID || + timer.m_search != searchtimer.m_search) + { + DeleteChildren(searchtimer); + } + + timer.m_id = id; + timer.m_channelID = channel->GetChannelID(); + timer.m_timersCreated = searchtimer.m_timersCreated; + + searchtimer = timer; + m_state++; + Save(); + return true; + } + } + return false; } -bool CVNSITimers::DeleteTimer(int idx) +bool CVNSITimers::DeleteTimer(int id) { cMutexLock lock(&m_timerLock); - idx &= ~INDEX_MASK; - if (idx < 0 || idx >= (int)m_timers.size()) - return false; - m_timers.erase(m_timers.begin()+idx); - m_state++; - Save(); - return true; + id &= ~VNSITIMER_MASK; + + std::vector::iterator it; + for (it = m_timers.begin(); it != m_timers.end(); ++it) + { + if (it->m_id == id) + { + DeleteChildren(*it); + m_timers.erase(it); + m_state++; + Save(); + return true; + } + } + return false; +} + +void CVNSITimers::DeleteChildren(CVNSITimer &vnsitimer) +{ +#if VDRVERSNUM >= 20301 + cStateKey timerState; + timerState.Reset(); + bool modified = false; + cTimers *Timers = cTimers::GetTimersWrite(timerState); + if (Timers) + { + Timers->SetExplicitModify(); + cTimer *timer = Timers->First(); + while (timer) + { + if (!timer->Channel()) + continue; + + timer->Matches(); + cTimer* nextTimer = Timers->Next(timer); + for (auto &starttime : vnsitimer.m_timersCreated) + { + if (vnsitimer.m_channelID == timer->Channel()->GetChannelID() && + timer->StartTime() == starttime && + !timer->Recording()) + { + Timers->Del(timer); + Timers->SetModified(); + modified = true; + break; + } + } + timer = nextTimer; + } + timerState.Remove(modified); + vnsitimer.m_timersCreated.clear(); + } +#endif +} + +int CVNSITimers::GetParent(const cTimer *timer) +{ + if (!timer->Channel()) + return 0; + + timer->Matches(); + cMutexLock lock(&m_timerLock); + for (auto &searchTimer : m_timers) + { + if (searchTimer.m_channelID == timer->Channel()->GetChannelID()) + { + for (auto &starttime : searchTimer.m_timersCreated) + { + if (timer->StartTime() == starttime) + { + return searchTimer.m_id | VNSITIMER_MASK; + } + } + } + } + return 0; +} + +bool CVNSITimers::IsChild(int id, time_t starttime) +{ + cMutexLock lock(&m_timerLock); + + for (auto &timer : m_timers) + { + if (timer.m_id != id) + continue; + + for (auto &time : timer.m_timersCreated) + { + if (time == starttime) + return true; + } + } + return false; } bool CVNSITimers::StateChange(int &state) @@ -243,9 +387,15 @@ if (timerEvent == nullptr) continue; if (timer->HasFlags(tfActive) && - strcmp(timerEvent->Title(), event->Title()) == 0 && - strcmp(timerEvent->ShortText(), event->ShortText()) == 0) - return true; + strcmp(timerEvent->Title(), event->Title()) == 0) + { + if (timerEvent->ShortText() != nullptr && event->ShortText() != nullptr && + strcmp(timerEvent->ShortText(), event->ShortText()) == 0) + return true; + + if (abs(difftime(timerEvent->StartTime(), event->StartTime())) < 300) + return true; + } } return false; } @@ -296,11 +446,14 @@ for (const cEvent *event = schedule->Events()->First(); event; event = schedule->Events()->Next(event)) { + if (event->EndTime() < time(nullptr)) + continue; + std::string title(event->Title()); std::smatch m; std::regex e(Convert(searchTimer.m_search)); - if (std::regex_search(title, m, e, std::regex_constants::match_not_null)) + if (std::regex_search(title, m, e, std::regex_constants::match_not_null)) { bool duplicate = false; LOCK_RECORDINGS_READ; @@ -308,14 +461,17 @@ { if (recording->Info() != nullptr) { - if (strcmp(recording->Info()->Title(), event->Title()) == 0 && - strcmp(recording->Info()->ShortText(), event->ShortText()) == 0) + if (strcmp(recording->Info()->Title(), event->Title()) == 0) { - duplicate = true; - break; + if (recording->Info()->ShortText() != nullptr && event->ShortText() != nullptr && + strcmp(recording->Info()->ShortText(), event->ShortText()) == 0) + { + duplicate = true; + break; + } } } - if (difftime(event->StartTime(), recording->Start()) < 300) + if (abs(difftime(event->StartTime(), recording->Start())) < 300) { duplicate = true; break; @@ -327,9 +483,55 @@ if (IsDuplicateEvent(Timers, event)) continue; - std::unique_ptr newTimer(new cTimer(event)); - Timers->Add(newTimer.release()); + cTimer *newTimer = new cTimer(event); + + if (!Setup.MarginStart) + { + unsigned int start = newTimer->Start(); + if (start < searchTimer.m_marginStart) + { + newTimer->SetDay(cTimer::IncDay(newTimer->Day(), -1)); + start = 24*3600 - (searchTimer.m_marginStart - start); + } + else + start -= searchTimer.m_marginStart; + newTimer->SetStart(start); + } + + if (!Setup.MarginStop) + { + unsigned int stop = newTimer->Stop(); + if (stop + searchTimer.m_marginEnd >= 24*3600) + { + newTimer->SetDay(cTimer::IncDay(newTimer->Day(), 1)); + stop = stop + searchTimer.m_marginEnd - 24*3600; + } + else + stop += searchTimer.m_marginEnd; + newTimer->SetStop(stop); + } + + if (IsChild(searchTimer.m_id, newTimer->StartTime())) + { + delete newTimer; + continue; + } + + Timers->Add(newTimer); modified = true; + + { + cMutexLock lock(&m_timerLock); + for (auto &origTimer : m_timers) + { + if (origTimer.m_id == searchTimer.m_id) + { + origTimer.m_timersCreated.push_back(newTimer->StartTime()); + Save(); + break; + } + } + } } } } diff -Nru vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsitimer.h vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsitimer.h --- vdr-plugin-vnsiserver-1.5.0+git20160801-5f956c0/vnsitimer.h 2016-08-01 13:38:27.000000000 +0000 +++ vdr-plugin-vnsiserver-1.5.2+git2017-02-25/vnsitimer.h 2017-02-25 17:14:09.000000000 +0000 @@ -32,17 +32,22 @@ class cEvent; class cTimers; +class cTimer; class CVNSITimer { public: + int m_id; std::string m_name; uint32_t m_channelUID; int32_t m_enabled; int32_t m_priority; int32_t m_lifetime; + uint32_t m_marginStart; + uint32_t m_marginEnd; std::string m_search; tChannelID m_channelID; + std::vector m_timersCreated; }; class CVNSITimers : public cThread @@ -57,19 +62,23 @@ size_t GetTimersCount(); bool StateChange(int &state); std::vector GetTimers(); - bool GetTimer(int idx, CVNSITimer &timer); - bool UpdateTimer(int idx, CVNSITimer &timer); - bool DeleteTimer(int idx); + bool GetTimer(int id, CVNSITimer &timer); + bool UpdateTimer(int id, CVNSITimer &timer); + bool DeleteTimer(int id); + int GetParent(const cTimer *timer); - static constexpr uint32_t INDEX_MASK = 0xF0000000; + static constexpr uint32_t VNSITIMER_MASK = 0xF0000000; protected: virtual void Action(void) override; std::string Convert(std::string search); bool IsDuplicateEvent(cTimers *timers, const cEvent *event); + void DeleteChildren(CVNSITimer &vnsitimer); + bool IsChild(int id, time_t starttime); std::vector m_timers; std::atomic_bool m_doScan; std::atomic_int m_state; cMutex m_timerLock; + int m_nextId = 0; };