[vdr-plugin-vnsiserver] 01/05: Imported Upstream version 1.3.0+git20150214
Tobias Grimm
tiber-guest at moszumanska.debian.org
Sat Feb 14 12:15:29 UTC 2015
This is an automated email from the git hooks/post-receive script.
tiber-guest pushed a commit to branch master
in repository vdr-plugin-vnsiserver.
commit 01052e5a27822f0fb4562d48522e4d74618f7408
Author: etobi <git at e-tobi.net>
Date: Sat Feb 14 13:04:44 2015 +0100
Imported Upstream version 1.3.0+git20150214
---
HISTORY | 50 +++-
Makefile | 4 +-
README | 36 ++-
bitstream.c | 5 +-
bitstream.h | 5 +-
channelfilter.c | 332 +++++++++++++++++++++
channelfilter.h | 55 ++++
config.c | 7 +-
config.h | 8 +-
cxsocket.c | 5 +-
cxsocket.h | 5 +-
demuxer.c | 57 +++-
demuxer.h | 12 +-
hash.c | 5 +-
hash.h | 5 +-
parser.c | 71 ++++-
parser.h | 19 +-
parser_AAC.c | 26 +-
parser_AAC.h | 3 +-
parser_AC3.c | 2 +-
parser_AC3.h | 2 +-
parser_DTS.c | 2 +-
parser_DTS.h | 2 +-
parser_MPEGAudio.c | 81 +++++-
parser_MPEGAudio.h | 11 +-
parser_MPEGVideo.c | 21 +-
parser_MPEGVideo.h | 3 +-
parser_Subtitle.c | 2 +-
parser_Subtitle.h | 2 +-
parser_Teletext.c | 2 +-
parser_Teletext.h | 2 +-
parser_h264.c | 22 +-
parser_h264.h | 2 +-
po/de_DE.po | 61 ++++
po/lt_LT.po | 61 ++++
recordingscache.c | 20 +-
recordingscache.h | 15 +-
recplayer.c | 28 +-
recplayer.h | 5 +-
requestpacket.c | 5 +-
requestpacket.h | 5 +-
responsepacket.c | 7 +-
responsepacket.h | 5 +-
setup.c | 18 +-
setup.h | 2 +
status.c | 160 ++++++++++
parser_DTS.c => status.h | 32 +-
streamer.c | 135 +++++++--
streamer.h | 12 +-
videobuffer.c | 58 +++-
videobuffer.h | 7 +-
videoinput.c | 36 ++-
videoinput.h | 8 +-
vnsi.c | 74 ++++-
vnsi.h | 27 +-
vnsiclient.c | 743 ++++++++++++++++++++++++++++++++++++++++++++---
vnsiclient.h | 33 ++-
vnsicommand.h | 31 +-
vnsiserver.c | 180 +++---------
vnsiserver.h | 11 +-
60 files changed, 2222 insertions(+), 423 deletions(-)
diff --git a/HISTORY b/HISTORY
index 4be1019..3f40920 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,9 +1,51 @@
VDR Plugin 'vnsiserver' Revision History
----------------------------------------
-2010-03-23: Version 0.0.1
+2015-02-07: Version 1.3.0
-- Initial revision.
+- add support for undelete recordings
+- add support for rds data if present on dvb radio and send to addon if supported there
+
+2015-01-25: Version 1.2.1
+
+- add cmd line switch for setting tcp port
+- fix potential segfault on stop streaming
+- stop triggering epg updates if client does not fetch epg
+
+2014-09-03: Version 1.2.0
+
+- add cmd line switch "-d". if set, vnsi creates a dummy dvb device which makes femon work
+ on all channels
+- add setup parameter AvoidEPGScan. if set to 1, vnsi disables EPG scan while streaming.
+ EPG scan during streaming can lead to artifacs on some dual tuner cards.
+- fix div by zero on AAC audio, thanks to Jakob Sloth Jepsen
+
+2014-03-20: Version 1.1.0
+
+- add support for picons
+- add setup paramter to select if current recording should start when tuning to a channel
+- only activate AAC mux workaround if environment variable VNSI_AAC_MUXMODE is set
+- bump max timshift buffer RAM to 8 gig
+
+2014-03-20: Version 1.0.0
+
+- release 1.0
+- various fixes
+- remove suffix from plugin name vnsiserver5 -> vnsiserver
+
+2014-01-08: Version 0.9.4
+
+- update length of recorings in progress
+ while playing
+
+2013-12-04: Version 0.9.3
+
+- add support for EDL (marks)
+- add channel filter
+- send buffer times for timeshift
+- bump protocol to XBMC to 5
+- suffix plugin with version of protocol: vnsiserver5
+- this version is compatible with XBMC 13
2013-02-03: Version 0.9.1
@@ -12,3 +54,7 @@ VDR Plugin 'vnsiserver' Revision History
- proper handling of PMT changes
- suffix plugin with version of protocol: vnsiserver3
- this version is compatible with XBMC 12.0
+
+2010-03-23: Version 0.0.1
+
+- Initial revision.
diff --git a/Makefile b/Makefile
index 8f37c92..d7e69bb 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@
# This name will be used in the '-P...' option of VDR to load the plugin.
# By default the main source file also carries this name.
-PLUGIN = vnsiserver4
+PLUGIN = vnsiserver
### The version number of this plugin (taken from the main source file):
@@ -90,7 +90,7 @@ OBJS = vnsi.o bitstream.o vnsiclient.o config.o cxsocket.o parser.o parser_AAC.o
parser_AC3.o parser_DTS.o parser_h264.o parser_MPEGAudio.o parser_MPEGVideo.o \
parser_Subtitle.o parser_Teletext.o streamer.o recplayer.o requestpacket.o responsepacket.o \
vnsiserver.o hash.o recordingscache.o setup.o vnsiosd.o demuxer.o videobuffer.o \
- videoinput.o
+ videoinput.o channelfilter.o status.o
### The main target:
diff --git a/README b/README
index a1f5a45..f4f5adc 100644
--- a/README
+++ b/README
@@ -1,9 +1,13 @@
-This is a "plugin" for the Video Disk Recorder (VDR).
-Project's homepage: xbmc.org
+-----------------------------------------------------------------------------
+1. Introduction
+-----------------------------------------------------------------------------
-Latest version available at:
-https://github.com/opdenkamp/xbmc-pvr-addons/tree/master/addons/pvr.vdr.vnsi/vdr-plugin-vnsiserver
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+VDR plugin to handle XBMC clients.
+The vdr-plugin-vnsiserver is able to handle serveral XBMC clients connecting
+via the VNSI addon.
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
@@ -11,15 +15,21 @@ the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
See the file COPYING for more information.
+Latest version available at:
+https://github.com/FernetMenta/vdr-plugin-vnsiserver
+
+-----------------------------------------------------------------------------
+2. Compile in VDR's source tree
+-----------------------------------------------------------------------------
-Description:
-------------
+Get VDR sources.
-VDR plugin to handle XBMC clients.
-The vdr-plugin-vnsiserver is able to handle serveral XBMC clients connecting via the VNSI addon.
+ $ cd <path to vdr>/PLUGINS/src
+ $ git clone https://github.com/FernetMenta/vdr-plugin-vnsiserver
+ $ ln -s vdr-plugin-vnsiserver vnsiserver
+
+ -----------------------------------------------------------------------------
+3. Help and Suppport
+-----------------------------------------------------------------------------
-VNSI supports also dynamic PID switching of the received DVB-TS stream. Further it detect and demuxing several
-not by VDR implemented Audio Streams, this are:
-- Enhanced AC3 (not tested)
-- Advanced Audio Coding (AAC) (not tested)
-- DTS (demuxer not finished now, and does not work)
+http://forum.xbmc.org/forumdisplay.php?fid=169
diff --git a/bitstream.c b/bitstream.c
index b5fa53a..3e44ff9 100644
--- a/bitstream.c
+++ b/bitstream.c
@@ -16,9 +16,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
diff --git a/bitstream.h b/bitstream.h
index 4607715..626142f 100644
--- a/bitstream.h
+++ b/bitstream.h
@@ -16,9 +16,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
diff --git a/channelfilter.c b/channelfilter.c
new file mode 100644
index 0000000..d8a61ca
--- /dev/null
+++ b/channelfilter.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "channelfilter.h"
+#include "config.h"
+#include "hash.h"
+#include <string>
+#include <algorithm>
+#include <iostream>
+#include <fstream>
+#include <vdr/tools.h>
+
+cVNSIProvider::cVNSIProvider()
+ :m_name(""), m_caid(0)
+{
+
+}
+
+cVNSIProvider::cVNSIProvider(std::string name, int caid)
+ :m_name(name), m_caid(caid)
+{
+};
+
+bool cVNSIProvider::operator==(const cVNSIProvider &rhs)
+{
+ if (rhs.m_caid != m_caid)
+ return false;
+ if (rhs.m_name.compare(m_name) != 0)
+ return false;
+ return true;
+}
+
+
+bool cVNSIChannelFilter::IsRadio(const cChannel* channel)
+{
+ bool isRadio = false;
+
+ // assume channels without VPID & APID are video channels
+ if (channel->Vpid() == 0 && channel->Apid(0) == 0)
+ isRadio = false;
+ // channels without VPID are radio channels (channels with VPID 1 are encrypted radio channels)
+ else if (channel->Vpid() == 0 || channel->Vpid() == 1)
+ isRadio = true;
+
+ return isRadio;
+}
+
+void cVNSIChannelFilter::Load()
+{
+ cMutexLock lock(&m_Mutex);
+
+ cString filename;
+ std::string line;
+ std::ifstream rfile;
+ cVNSIProvider provider;
+ std::vector<cVNSIProvider>::iterator p_it;
+
+ filename = cString::sprintf("%s/videowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory);
+ m_providersVideo.clear();
+ rfile.open(filename);
+ if (rfile.is_open())
+ {
+ while(std::getline(rfile,line))
+ {
+ size_t pos = line.find("|");
+ if(pos == line.npos)
+ {
+ provider.m_name = line;
+ provider.m_caid = 0;
+ }
+ else
+ {
+ provider.m_name = line.substr(0, pos);
+ std::string tmp = line.substr(pos+1);
+ char *pend;
+ provider.m_caid = strtol(tmp.c_str(), &pend, 10);
+ }
+ p_it = std::find(m_providersVideo.begin(), m_providersVideo.end(), provider);
+ if(p_it == m_providersVideo.end())
+ {
+ m_providersVideo.push_back(provider);
+ }
+ }
+ rfile.close();
+ }
+
+ filename = cString::sprintf("%s/radiowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory);
+ rfile.open(filename);
+ m_providersRadio.clear();
+ if (rfile.is_open())
+ {
+ while(std::getline(rfile,line))
+ {
+ unsigned int pos = line.find("|");
+ if(pos == line.npos)
+ {
+ provider.m_name = line;
+ provider.m_caid = 0;
+ }
+ else
+ {
+ provider.m_name = line.substr(0, pos);
+ std::string tmp = line.substr(pos+1);
+ char *pend;
+ provider.m_caid = strtol(tmp.c_str(), &pend, 10);
+ }
+ p_it = std::find(m_providersRadio.begin(), m_providersRadio.end(), provider);
+ if(p_it == m_providersRadio.end())
+ {
+ m_providersRadio.push_back(provider);
+ }
+ }
+ rfile.close();
+ }
+
+ filename = cString::sprintf("%s/videoblacklist.vnsi", *VNSIServerConfig.ConfigDirectory);
+ rfile.open(filename);
+ m_channelsVideo.clear();
+ if (rfile.is_open())
+ {
+ while(getline(rfile,line))
+ {
+ char *pend;
+ int id = strtol(line.c_str(), &pend, 10);
+ m_channelsVideo.push_back(id);
+ }
+ rfile.close();
+ }
+
+ filename = cString::sprintf("%s/radioblacklist.vnsi", *VNSIServerConfig.ConfigDirectory);
+ rfile.open(filename);
+ m_channelsRadio.clear();
+ if (rfile.is_open())
+ {
+ while(getline(rfile,line))
+ {
+ char *pend;
+ int id = strtol(line.c_str(), &pend, 10);
+ m_channelsRadio.push_back(id);
+ }
+ rfile.close();
+ }
+}
+
+void cVNSIChannelFilter::StoreWhitelist(bool radio)
+{
+ cMutexLock lock(&m_Mutex);
+
+ cString filename;
+ std::ofstream wfile;
+ cVNSIProvider provider;
+ std::vector<cVNSIProvider>::iterator p_it;
+ std::vector<cVNSIProvider> *whitelist;
+
+ if (radio)
+ {
+ filename = cString::sprintf("%s/radiowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory);
+ whitelist = &m_providersRadio;
+ }
+ else
+ {
+ filename = cString::sprintf("%s/videowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory);
+ whitelist = &m_providersVideo;
+ }
+
+ wfile.open(filename);
+ if(wfile.is_open())
+ {
+ std::string tmp;
+ char buf[16];
+ for(p_it=whitelist->begin(); p_it!=whitelist->end(); ++p_it)
+ {
+ tmp = p_it->m_name;
+ tmp += "|";
+ sprintf(buf, "%d\n", p_it->m_caid);
+ tmp += buf;
+ wfile << tmp;
+ }
+ wfile.close();
+ }
+
+ SortChannels();
+}
+
+void cVNSIChannelFilter::StoreBlacklist(bool radio)
+{
+ cMutexLock lock(&m_Mutex);
+
+ cString filename;
+ std::ofstream wfile;
+ cVNSIProvider provider;
+ std::vector<int>::iterator it;
+ std::vector<int> *blacklist;
+
+ if (radio)
+ {
+ filename = cString::sprintf("%s/radioblacklist.vnsi", *VNSIServerConfig.ConfigDirectory);
+ blacklist = &m_channelsRadio;
+ }
+ else
+ {
+ filename = cString::sprintf("%s/videoblacklist.vnsi", *VNSIServerConfig.ConfigDirectory);
+ blacklist = &m_channelsVideo;
+ }
+
+ wfile.open(filename);
+ if(wfile.is_open())
+ {
+ std::string tmp;
+ char buf[16];
+ for(it=blacklist->begin(); it!=blacklist->end(); ++it)
+ {
+ sprintf(buf, "%d\n", *it);
+ tmp = buf;
+ wfile << tmp;
+ }
+ wfile.close();
+ }
+
+ SortChannels();
+}
+
+bool cVNSIChannelFilter::IsWhitelist(const cChannel &channel)
+{
+ cVNSIProvider provider;
+ std::vector<cVNSIProvider>::iterator p_it;
+ std::vector<cVNSIProvider> *providers;
+ provider.m_name = channel.Provider();
+
+ if (IsRadio(&channel))
+ providers = &m_providersRadio;
+ else
+ providers = &m_providersVideo;
+
+ if(providers->empty())
+ return true;
+
+ if (channel.Ca(0) == 0)
+ {
+ provider.m_caid = 0;
+ p_it = std::find(providers->begin(), providers->end(), provider);
+ if(p_it!=providers->end())
+ return true;
+ else
+ return false;
+ }
+
+ int caid;
+ int idx = 0;
+ while((caid = channel.Ca(idx)) != 0)
+ {
+ provider.m_caid = caid;
+ p_it = std::find(providers->begin(), providers->end(), provider);
+ if(p_it!=providers->end())
+ return true;
+
+ idx++;
+ }
+ return false;
+}
+
+bool cVNSIChannelFilter::PassFilter(const cChannel &channel)
+{
+ cMutexLock lock(&m_Mutex);
+
+ if(channel.GroupSep())
+ return true;
+
+ if (!IsWhitelist(channel))
+ return false;
+
+ std::vector<int>::iterator it;
+ if (IsRadio(&channel))
+ {
+ it = std::find(m_channelsRadio.begin(), m_channelsRadio.end(), CreateChannelUID(&channel));
+ if(it!=m_channelsRadio.end())
+ return false;
+ }
+ else
+ {
+ it = std::find(m_channelsVideo.begin(), m_channelsVideo.end(), CreateChannelUID(&channel));
+ if(it!=m_channelsVideo.end())
+ return false;
+ }
+
+ return true;
+}
+
+void cVNSIChannelFilter::SortChannels()
+{
+ Channels.IncBeingEdited();
+ Channels.Lock(true);
+
+ for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+ {
+ if(!PassFilter(*channel))
+ {
+ for (cChannel *whitechan = Channels.Next(channel); whitechan; whitechan = Channels.Next(whitechan))
+ {
+ if(PassFilter(*whitechan))
+ {
+ Channels.Move(whitechan, channel);
+ channel = whitechan;
+ break;
+ }
+ }
+ }
+ }
+
+ Channels.SetModified(true);
+ Channels.Unlock();
+ Channels.DecBeingEdited();
+}
+
+cVNSIChannelFilter VNSIChannelFilter;
diff --git a/channelfilter.h b/channelfilter.h
new file mode 100644
index 0000000..dc49728
--- /dev/null
+++ b/channelfilter.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include <vdr/thread.h>
+#include <vdr/channels.h>
+
+class cVNSIProvider
+{
+public:
+ cVNSIProvider();
+ cVNSIProvider(std::string name, int caid);
+ bool operator==(const cVNSIProvider &rhs);
+ std::string m_name;
+ int m_caid;
+};
+
+class cVNSIChannelFilter
+{
+public:
+ void Load();
+ void StoreWhitelist(bool radio);
+ void StoreBlacklist(bool radio);
+ bool IsWhitelist(const cChannel &channel);
+ bool PassFilter(const cChannel &channel);
+ void SortChannels();
+ static bool IsRadio(const cChannel* channel);
+ std::vector<cVNSIProvider> m_providersVideo;
+ std::vector<cVNSIProvider> m_providersRadio;
+ std::vector<int> m_channelsVideo;
+ std::vector<int> m_channelsRadio;
+ cMutex m_Mutex;
+};
+
+extern cVNSIChannelFilter VNSIChannelFilter;
diff --git a/config.c b/config.c
index 5ba1ba8..5ce3549 100644
--- a/config.c
+++ b/config.c
@@ -17,9 +17,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -36,6 +35,8 @@ cVNSIServerConfig::cVNSIServerConfig()
listen_port = LISTEN_PORT;
ConfigDirectory = NULL;
stream_timeout = 10;
+ device = false;
+ testStreamActive = false;
}
/* Global instance */
diff --git a/config.h b/config.h
index 24657ad..50feb2e 100644
--- a/config.h
+++ b/config.h
@@ -17,9 +17,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -79,6 +78,9 @@ public:
cString ConfigDirectory; // config directory path
uint16_t listen_port; // Port of remote server
uint16_t stream_timeout; // timeout in seconds for stream data
+ bool device; // true if vnsi should act as dummy device
+ cString testStreamFile; // TS file to simulate channel
+ bool testStreamActive; // true if test mode is enabled
};
// Global instance
diff --git a/cxsocket.c b/cxsocket.c
index 8f2fa5d..68fbaa9 100644
--- a/cxsocket.c
+++ b/cxsocket.c
@@ -18,9 +18,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
diff --git a/cxsocket.h b/cxsocket.h
index 4088152..3d9e64f 100644
--- a/cxsocket.h
+++ b/cxsocket.h
@@ -18,9 +18,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
diff --git a/demuxer.c b/demuxer.c
index 1f3c9e8..cb910c6 100644
--- a/demuxer.c
+++ b/demuxer.c
@@ -27,7 +27,8 @@
#include <vdr/channels.h>
#include <libsi/si.h>
-cVNSIDemuxer::cVNSIDemuxer()
+cVNSIDemuxer::cVNSIDemuxer(bool bAllowRDS)
+ : m_bAllowRDS(bAllowRDS)
{
m_OldPmtVersion = -1;
}
@@ -50,13 +51,14 @@ void cVNSIDemuxer::Open(const cChannel &channel, cVideoBuffer *videoBuffer)
else
m_WaitIFrame = false;
- m_FirstFrameDTS = 0;
+ m_FirstFramePTS = 0;
m_PtsWrap.m_Wrap = false;
m_PtsWrap.m_NoOfWraps = 0;
m_PtsWrap.m_ConfirmCount = 0;
m_MuxPacketSerial = 0;
m_Error = ERROR_DEMUX_NODATA;
+ m_SetRefTime = true;
}
void cVNSIDemuxer::Close()
@@ -72,7 +74,7 @@ void cVNSIDemuxer::Close()
m_StreamInfos.clear();
}
-int cVNSIDemuxer::Read(sStreamPacket *packet)
+int cVNSIDemuxer::Read(sStreamPacket *packet, sStreamPacket *packet_side_data)
{
uint8_t *buf;
int len;
@@ -88,7 +90,7 @@ int cVNSIDemuxer::Read(sStreamPacket *packet)
packet->pmtChange = false;
// read TS Packet from buffer
- len = m_VideoBuffer->Read(&buf, TS_SIZE);
+ len = m_VideoBuffer->Read(&buf, TS_SIZE, m_endTime, m_wrapTime);
// eof
if (len == -2)
return -2;
@@ -131,19 +133,26 @@ int cVNSIDemuxer::Read(sStreamPacket *packet)
}
else if (stream = FindStream(ts_pid))
{
- int error = stream->ProcessTSPacket(buf, packet, m_WaitIFrame);
+ int error = stream->ProcessTSPacket(buf, packet, packet_side_data, m_WaitIFrame);
if (error == 0)
{
if (m_WaitIFrame)
{
- m_FirstFrameDTS = packet->dts;
+ if (packet->pts != DVD_NOPTS_VALUE)
+ m_FirstFramePTS = packet->pts;
m_WaitIFrame = false;
}
- if (packet->dts < m_FirstFrameDTS)
+ if (packet->pts < m_FirstFramePTS)
return 0;
packet->serial = m_MuxPacketSerial;
+ if (m_SetRefTime)
+ {
+ m_refTime = m_VideoBuffer->GetRefTime();
+ packet->reftime = m_refTime;
+ m_SetRefTime = false;
+ }
return 1;
}
else if (error < 0)
@@ -316,9 +325,27 @@ bool cVNSIDemuxer::SeekTime(int64_t time)
return true;
}
-void cVNSIDemuxer::BufferStatus(bool ×hift, int &start, int ¤t, int &end)
+void cVNSIDemuxer::BufferStatus(bool ×hift, uint32_t &start, uint32_t &end)
{
timeshift = m_VideoBuffer->HasBuffer();
+
+ if (timeshift)
+ {
+ if (!m_wrapTime)
+ {
+ start = m_refTime;
+ }
+ else
+ {
+ start = m_endTime - (m_wrapTime - m_refTime);
+ }
+ end = m_endTime;
+ }
+ else
+ {
+ start = 0;
+ end = 0;
+ }
}
cTSStream *cVNSIDemuxer::GetFirstStream()
@@ -408,7 +435,7 @@ bool cVNSIDemuxer::EnsureParsers()
}
else if (it->type == stMPEG2AUDIO)
{
- stream = new cTSStream(stMPEG2AUDIO, it->pID, &m_PtsWrap);
+ stream = new cTSStream(stMPEG2AUDIO, it->pID, &m_PtsWrap, it->handleRDS);
stream->SetLanguage(it->language);
}
else if (it->type == stAACADTS)
@@ -458,6 +485,8 @@ bool cVNSIDemuxer::EnsureParsers()
void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
{
sStreamInfo newStream;
+ bool containsVideo = false;
+ int index = 0;
if (channel->Vpid())
{
newStream.pID = channel->Vpid();
@@ -469,12 +498,13 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
newStream.type = stMPEG2VIDEO;
AddStreamInfo(newStream);
+ containsVideo = true;
}
const int *DPids = channel->Dpids();
+ index = 0;
for ( ; *DPids; DPids++)
{
- int index = 0;
if (!FindStream(*DPids))
{
newStream.pID = *DPids;
@@ -490,9 +520,9 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
}
const int *APids = channel->Apids();
+ index = 0;
for ( ; *APids; APids++)
{
- int index = 0;
if (!FindStream(*APids))
{
newStream.pID = *APids;
@@ -503,6 +533,7 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
else if (channel->Atype(index) == 0x11)
newStream.type = stAACLATM;
#endif
+ 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);
}
@@ -512,7 +543,7 @@ void cVNSIDemuxer::SetChannelStreams(const cChannel *channel)
const int *SPids = channel->Spids();
if (SPids)
{
- int index = 0;
+ index = 0;
for ( ; *SPids; SPids++)
{
if (!FindStream(*SPids))
@@ -600,7 +631,7 @@ bool cVNSIDemuxer::GetTimeAtPos(off_t *pos, int64_t *time)
m_VideoBuffer->SetPos(*pos);
ResetParsers();
- while (len = m_VideoBuffer->Read(&buf, TS_SIZE) == TS_SIZE)
+ while (len = m_VideoBuffer->Read(&buf, TS_SIZE, m_endTime, m_wrapTime) == TS_SIZE)
{
ts_pid = TsPid(buf);
if (stream = FindStream(ts_pid))
diff --git a/demuxer.h b/demuxer.h
index 019595f..f1bb857 100644
--- a/demuxer.h
+++ b/demuxer.h
@@ -38,6 +38,7 @@ struct sStreamInfo
int subtitlingType;
int compositionPageId;
int ancillaryPageId;
+ bool handleRDS;
void SetLanguage(const char* lang)
{
language[0] = lang[0];
@@ -50,9 +51,9 @@ struct sStreamInfo
class cVNSIDemuxer
{
public:
- cVNSIDemuxer();
+ cVNSIDemuxer(bool bAllowRDS);
virtual ~cVNSIDemuxer();
- int Read(sStreamPacket *packet);
+ int Read(sStreamPacket *packet, sStreamPacket *packet_side_data);
cTSStream *GetFirstStream();
cTSStream *GetNextStream();
void Open(const cChannel &channel, cVideoBuffer *videoBuffer);
@@ -60,7 +61,7 @@ public:
bool SeekTime(int64_t time);
uint32_t GetSerial() { return m_MuxPacketSerial; }
void SetSerial(uint32_t serial) { m_MuxPacketSerial = serial; }
- void BufferStatus(bool ×hift, int &start, int ¤t, int &end);
+ void BufferStatus(bool ×hift, uint32_t &start, uint32_t &end);
uint16_t GetError();
protected:
@@ -78,10 +79,13 @@ protected:
cPatPmtParser m_PatPmtParser;
int m_OldPmtVersion;
bool m_WaitIFrame;
- int64_t m_FirstFrameDTS;
+ int64_t m_FirstFramePTS;
cVideoBuffer *m_VideoBuffer;
cMutex m_Mutex;
uint32_t m_MuxPacketSerial;
sPtsWrap m_PtsWrap;
uint16_t m_Error;
+ bool m_SetRefTime;
+ time_t m_refTime, m_endTime, m_wrapTime;
+ bool m_bAllowRDS;
};
diff --git a/hash.c b/hash.c
index 888fd52..6afa505 100644
--- a/hash.c
+++ b/hash.c
@@ -15,9 +15,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
diff --git a/hash.h b/hash.h
index eba8a25..0aef425 100644
--- a/hash.h
+++ b/hash.h
@@ -15,9 +15,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
diff --git a/parser.c b/parser.c
index d325fae..d5f12ea 100644
--- a/parser.c
+++ b/parser.c
@@ -67,6 +67,7 @@ void cParser::Reset()
m_PesParserPtr = 0;
m_PesNextFramePtr = 0;
m_FoundFrame = false;
+ m_FrameValid = false;
m_PesPacketLength = 0;
m_PesHeaderPtr = 0;
m_Error = ERROR_PES_GENERAL;
@@ -433,7 +434,9 @@ inline bool cParser::IsValidStartCode(uint8_t *buf, int size)
// --- cTSStream ----------------------------------------------------
-cTSStream::cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap)
+uint32_t cTSStream::m_UniqueSideDataIDs = 0;
+
+cTSStream::cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap, bool handleSideData)
: m_streamType(type)
, m_pID(pid)
{
@@ -464,7 +467,7 @@ cTSStream::cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap)
}
else if (m_streamType == stMPEG2AUDIO)
{
- m_pesParser = new cParserMPEG2Audio(m_pID, this, ptsWrap, true);
+ m_pesParser = new cParserMPEG2Audio(m_pID, this, ptsWrap, true, handleSideData);
m_streamContent = scAUDIO;
}
else if (m_streamType == stAACADTS)
@@ -518,17 +521,19 @@ cTSStream::~cTSStream()
}
}
-int cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
+int cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, sStreamPacket *pkt_side_data, bool iframe)
{
+ int ret = 1;
+
if (!data)
- return false;
+ return ret;
if (!m_pesParser)
- return false;
+ return ret;
int payloadSize = m_pesParser->ParsePacketHeader(data);
if (payloadSize == 0)
- return 1;
+ return ret;
else if (payloadSize < 0)
{
return -m_pesParser->GetError();
@@ -539,26 +544,41 @@ int cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe)
return -m_pesParser->GetError();
}
- m_pesParser->Parse(pkt);
+ m_pesParser->Parse(pkt, pkt_side_data);
if (iframe && !m_pesParser->IsVideo())
- return 1;
+ return ret;
if (pkt->data)
{
int64_t dts = pkt->dts;
int64_t pts = pkt->pts;
- if (dts == DVD_NOPTS_VALUE)
- dts = pts;
-
// Rescale for XBMC
- pkt->dts = Rescale(dts, DVD_TIME_BASE, 90000);
- pkt->pts = Rescale(pts, DVD_TIME_BASE, 90000);
+ if (pkt->dts != DVD_NOPTS_VALUE)
+ pkt->dts = Rescale(dts, DVD_TIME_BASE, 90000);
+ if (pkt->pts != DVD_NOPTS_VALUE)
+ pkt->pts = Rescale(pts, DVD_TIME_BASE, 90000);
pkt->duration = Rescale(pkt->duration, DVD_TIME_BASE, 90000);
- return 0;
+
+ ret = 0;
+ }
+
+ if (pkt_side_data && pkt_side_data->data)
+ {
+ int64_t dts = pkt_side_data->dts;
+ int64_t pts = pkt_side_data->pts;
+
+ // Rescale for XBMC
+ if (pkt_side_data->dts != DVD_NOPTS_VALUE)
+ pkt_side_data->dts = Rescale(dts, DVD_TIME_BASE, 90000);
+ if (pkt_side_data->pts != DVD_NOPTS_VALUE)
+ pkt_side_data->pts = Rescale(pts, DVD_TIME_BASE, 90000);
+ pkt_side_data->duration = Rescale(pkt_side_data->duration, DVD_TIME_BASE, 90000);
+
+ ret = 0;
}
- return 1;
+ return ret;
}
bool cTSStream::ReadTime(uint8_t *data, int64_t *dts)
@@ -578,6 +598,7 @@ bool cTSStream::ReadTime(uint8_t *data, int64_t *dts)
data += TS_SIZE-payloadSize;
if (payloadSize >= 6 && m_pesParser->IsValidStartCode(data, payloadSize))
{
+ m_pesParser->m_curPTS = DVD_NOPTS_VALUE;
m_pesParser->m_curDTS = DVD_NOPTS_VALUE;
m_pesParser->ParsePESHeader(data, payloadSize);
if (m_pesParser->m_curDTS != DVD_NOPTS_VALUE)
@@ -585,6 +606,11 @@ bool cTSStream::ReadTime(uint8_t *data, int64_t *dts)
*dts = m_pesParser->m_curDTS;
return true;
}
+ else if (m_pesParser->m_curPTS != DVD_NOPTS_VALUE)
+ {
+ *dts = m_pesParser->m_curPTS;
+ return true;
+ }
}
m_pesParser->m_IsPusi = false;
}
@@ -626,7 +652,7 @@ int64_t cTSStream::Rescale(int64_t a, int64_t b, int64_t c)
{
a1+= a1 + ((a0>>i)&1);
t1+=t1;
- if (c <= a1)
+ if (c <= (int64_t)a1)
{
a1 -= c;
t1++;
@@ -636,6 +662,19 @@ int64_t cTSStream::Rescale(int64_t a, int64_t b, int64_t c)
}
}
+uint32_t cTSStream::AddSideDataType(eStreamContent content)
+{
+ m_UniqueSideDataIDs++;
+ if (m_UniqueSideDataIDs == 0)
+ m_UniqueSideDataIDs++;
+
+ uint32_t sidePid = m_UniqueSideDataIDs << 16;
+
+ m_SideDataTypes.push_back(std::make_pair(sidePid, content));
+
+ return sidePid;
+}
+
void cTSStream::SetLanguage(const char *language)
{
m_language[0] = language[0];
diff --git a/parser.h b/parser.h
index f3e8db4..b4872c0 100644
--- a/parser.h
+++ b/parser.h
@@ -23,6 +23,7 @@
#include <vdr/device.h>
#include <queue>
+#include <vector>
#define DVD_TIME_BASE 1000000
#define DVD_NOPTS_VALUE (-1LL<<52) // should be possible to represent in both double and __int64
@@ -84,7 +85,8 @@ enum eStreamContent
scAUDIO,
scSUBTITLE,
scTELETEXT,
- scPROGRAMM
+ scPROGRAMM,
+ scRDS
};
enum eStreamType
@@ -122,6 +124,7 @@ struct sStreamPacket
bool streamChange;
bool pmtChange;
uint32_t serial;
+ uint32_t reftime;
};
struct sPtsWrap
@@ -143,7 +146,7 @@ public:
virtual ~cParser();
bool AddPESPacket(uint8_t *data, int size);
- virtual void Parse(sStreamPacket *pkt) = 0;
+ virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) = 0;
// void ClearFrame() {m_PesBufferPtr = 0;}
int ParsePacketHeader(uint8_t *data);
int ParsePESHeader(uint8_t *buf, size_t len);
@@ -166,6 +169,7 @@ protected:
int m_PesTimePos;
bool m_FoundFrame;
+ bool m_FrameValid;
int m_pID;
int64_t m_curPTS;
@@ -208,15 +212,18 @@ private:
int m_BitsPerSample;
int m_BlockAlign;
+ std::vector< std::pair<uint32_t, eStreamContent> > m_SideDataTypes;
+ static uint32_t m_UniqueSideDataIDs;
+
unsigned char m_subtitlingType;
uint16_t m_compositionPageId;
uint16_t m_ancillaryPageId;
public:
- cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap);
+ cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap, bool handleSideData = false);
virtual ~cTSStream();
- int ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, bool iframe);
+ int ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, sStreamPacket *pkt_side_data, bool iframe);
bool ReadTime(uint8_t *data, int64_t *dts);
void ResetParser();
@@ -224,8 +231,12 @@ public:
const char *GetLanguage() { return m_language; }
const eStreamContent Content() const { return m_streamContent; }
const eStreamType Type() const { return m_streamType; }
+ void SetType(eStreamType type) { m_streamType = type; }
const int GetPID() const { return m_pID; }
+ uint32_t AddSideDataType(eStreamContent content);
+ const std::vector< std::pair<uint32_t, eStreamContent> > *GetSideDataTypes() const { return &m_SideDataTypes; }
+
/* Video Stream Information */
bool SetVideoInformation(int FpsScale, int FpsRate, int Height, int Width, float Aspect);
void GetVideoInformation(uint32_t &FpsScale, uint32_t &FpsRate, uint32_t &Height, uint32_t &Width, double &Aspect);
diff --git a/parser_AAC.c b/parser_AAC.c
index 2ef3cb1..508a783 100644
--- a/parser_AAC.c
+++ b/parser_AAC.c
@@ -43,6 +43,14 @@ cParserAAC::cParserAAC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool obser
m_Channels = 0;
m_BitRate = 0;
m_PesBufferInitialSize = 1920*2;
+ m_DetectMuxMode = false;
+
+ char *detectEnv = getenv("VNSI_AAC_MUXMODE");
+ if (detectEnv)
+ {
+ m_DetectMuxMode = true;
+ INFOLOG("cParserAAC - detect muxing mode while parsing");
+ }
}
cParserAAC::~cParserAAC()
@@ -50,7 +58,7 @@ cParserAAC::~cParserAAC()
}
-void cParserAAC::Parse(sStreamPacket *pkt)
+void cParserAAC::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
{
int p = m_PesParserPtr;
int l;
@@ -68,6 +76,8 @@ void cParserAAC::Parse(sStreamPacket *pkt)
pkt->id = m_pID;
pkt->data = &m_PesBuffer[p];
pkt->size = m_FrameSize;
+ if(!m_SampleRate)
+ m_SampleRate = aac_sample_rates[4];
pkt->duration = 1024 * 90000 / m_SampleRate;
pkt->dts = m_DTS;
pkt->pts = m_PTS;
@@ -103,9 +113,17 @@ int cParserAAC::FindHeaders(uint8_t *buf, int buf_size)
m_FoundFrame = true;
m_DTS = m_curPTS;
m_PTS = m_curPTS;
+ if(!m_SampleRate)
+ m_SampleRate = aac_sample_rates[4];
m_curPTS += 90000 * 1024 / m_SampleRate;
return -1;
}
+ else if (m_DetectMuxMode && buf_ptr[0] == 0xFF && (buf_ptr[1] & 0xF0) == 0xF0)
+ {
+ m_Stream->SetType(stAACADTS);
+ INFOLOG("cParserAAC::FindHeaders - detected ADTS muxing mode");
+ return -1;
+ }
}
else if (m_Stream->Type() == stAACADTS)
{
@@ -138,6 +156,12 @@ int cParserAAC::FindHeaders(uint8_t *buf, int buf_size)
m_curPTS += 90000 * 1024 / m_SampleRate;
return -1;
}
+ else if (m_DetectMuxMode && buf_ptr[0] == 0x56 && (buf_ptr[1] & 0xE0) == 0xE0)
+ {
+ m_Stream->SetType(stAACLATM);
+ INFOLOG("cParserAAC::FindHeaders - detected LATM muxing mode");
+ return -1;
+ }
}
return 0;
}
diff --git a/parser_AAC.h b/parser_AAC.h
index 15c2122..d95fcee 100644
--- a/parser_AAC.h
+++ b/parser_AAC.h
@@ -40,6 +40,7 @@ private:
bool m_Configured;
int m_AudioMuxVersion_A;
int m_FrameLengthType;
+ bool m_DetectMuxMode;
int FindHeaders(uint8_t *buf, int buf_size);
bool ParseLATMAudioMuxElement(cBitstream *bs);
@@ -50,7 +51,7 @@ private:
public:
cParserAAC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserAAC();
- virtual void Parse(sStreamPacket *pkt);
+ virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
virtual void Reset();
};
diff --git a/parser_AC3.c b/parser_AC3.c
index a1645a2..b8ace56 100644
--- a/parser_AC3.c
+++ b/parser_AC3.c
@@ -121,7 +121,7 @@ cParserAC3::~cParserAC3()
{
}
-void cParserAC3::Parse(sStreamPacket *pkt)
+void cParserAC3::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
{
int p = m_PesParserPtr;
int l;
diff --git a/parser_AC3.h b/parser_AC3.h
index b789423..2863b54 100644
--- a/parser_AC3.h
+++ b/parser_AC3.h
@@ -42,7 +42,7 @@ public:
cParserAC3(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserAC3();
- virtual void Parse(sStreamPacket *pkt);
+ virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
virtual void Reset();
};
diff --git a/parser_DTS.c b/parser_DTS.c
index 2ad2385..2c82ba8 100644
--- a/parser_DTS.c
+++ b/parser_DTS.c
@@ -34,7 +34,7 @@ cParserDTS::~cParserDTS()
{
}
-void cParserDTS::Parse(sStreamPacket *pkt)
+void cParserDTS::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
{
}
diff --git a/parser_DTS.h b/parser_DTS.h
index c28b4ba..741afe5 100644
--- a/parser_DTS.h
+++ b/parser_DTS.h
@@ -33,7 +33,7 @@ public:
cParserDTS(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserDTS();
- virtual void Parse(sStreamPacket *pkt);
+ virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
};
diff --git a/parser_MPEGAudio.c b/parser_MPEGAudio.c
index 2c62030..9dba379 100644
--- a/parser_MPEGAudio.c
+++ b/parser_MPEGAudio.c
@@ -25,6 +25,8 @@
#include "parser_MPEGAudio.h"
#include "bitstream.h"
+#define MAX_RDS_BUFFER_SIZE 100000
+
const uint16_t FrequencyTable[3] = { 44100, 48000, 32000 };
const uint16_t BitrateTable[2][3][15] =
{
@@ -40,7 +42,7 @@ const uint16_t BitrateTable[2][3][15] =
}
};
-cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
+cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps, bool enableRDS)
: cParser(pID, stream, ptsWrap, observePtsWraps)
{
m_PTS = 0;
@@ -50,13 +52,23 @@ cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWr
m_Channels = 0;
m_BitRate = 0;
m_PesBufferInitialSize = 2048;
+ m_RDSEnabled = enableRDS;
+ m_RDSBufferInitialSize = 384;
+ m_RDSBuffer = NULL;
+ m_RDSBufferSize = 0;
+ m_RDSExtPID = 0;
}
cParserMPEG2Audio::~cParserMPEG2Audio()
{
+ if (m_RDSBuffer)
+ {
+ delete m_RDSBuffer;
+ m_RDSBuffer = NULL;
+ }
}
-void cParserMPEG2Audio::Parse(sStreamPacket *pkt)
+void cParserMPEG2Audio::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
{
int p = m_PesParserPtr;
int l;
@@ -82,9 +94,74 @@ void cParserMPEG2Audio::Parse(sStreamPacket *pkt)
m_PesNextFramePtr = p + m_FrameSize;
m_PesParserPtr = 0;
m_FoundFrame = false;
+
+ if (m_RDSEnabled)
+ {
+ /*
+ * Reading of RDS Universal Encoder Communication Protocol
+ * If present it starts on end of a mpeg audio stream and goes
+ * backwards.
+ * See ETSI TS 101 154 - C.4.2.18 for details.
+ */
+ int rdsl = m_PesBuffer[p+m_FrameSize-2]; // RDS DataFieldLength
+ if (m_PesBuffer[p+m_FrameSize-1] == 0xfd && rdsl > 0) // RDS DataSync = 0xfd @ end
+ {
+ if (m_RDSBuffer == NULL)
+ {
+ m_RDSBufferSize = m_RDSBufferInitialSize;
+ m_RDSBuffer = (uint8_t*)malloc(m_RDSBufferSize);
+
+ if (m_RDSBuffer == NULL)
+ {
+ ERRORLOG("PVR Parser MPEG2-Audio - %s - malloc failed for RDS data", __FUNCTION__);
+ m_RDSEnabled = false;
+ return;
+ }
+
+ m_RDSExtPID = m_Stream->AddSideDataType(scRDS);
+ if (!m_RDSExtPID)
+ {
+ ERRORLOG("PVR Parser MPEG2-Audio - %s - failed to add RDS data stream", __FUNCTION__);
+ m_RDSEnabled = false;
+ return;
+ }
+ }
+
+ if (rdsl >= m_RDSBufferSize)
+ {
+ if (rdsl >= MAX_RDS_BUFFER_SIZE)
+ {
+ ERRORLOG("PVR Parser MPEG2-Audio - %s - max buffer size (%i kB) for RDS data reached, pid: %d", __FUNCTION__, MAX_RDS_BUFFER_SIZE/1024, m_pID);
+ m_RDSEnabled = false;
+ return;
+ }
+ m_RDSBufferSize += m_RDSBufferInitialSize / 10;
+ m_RDSBuffer = (uint8_t*)realloc(m_RDSBuffer, m_RDSBufferSize);
+ if (m_RDSBuffer == NULL)
+ {
+ ERRORLOG("PVR Parser MPEG2-Audio - %s - realloc for RDS data failed", __FUNCTION__);
+ m_RDSEnabled = false;
+ return;
+ }
+ }
+
+ int pes_buffer_ptr = 0;
+ for (int i = m_FrameSize-3; i > m_FrameSize-3-rdsl; i--) // <-- data reverse, from end to start
+ m_RDSBuffer[pes_buffer_ptr++] = m_PesBuffer[p+i];
+
+ pkt_side_data->id = m_RDSExtPID;
+ pkt_side_data->data = m_RDSBuffer;
+ pkt_side_data->size = pes_buffer_ptr;
+ pkt_side_data->duration = 0;
+ pkt_side_data->dts = m_curDTS;
+ pkt_side_data->pts = m_curPTS;
+ pkt_side_data->streamChange = false;
+ }
+ }
}
}
+
int cParserMPEG2Audio::FindHeaders(uint8_t *buf, int buf_size)
{
if (m_FoundFrame)
diff --git a/parser_MPEGAudio.h b/parser_MPEGAudio.h
index 32a3989..108ace9 100644
--- a/parser_MPEGAudio.h
+++ b/parser_MPEGAudio.h
@@ -36,13 +36,20 @@ private:
int64_t m_PTS;
int64_t m_DTS;
+ bool m_RDSEnabled;
+ uint32_t m_RDSExtPID;
+ uint8_t *m_RDSBuffer;
+ int m_RDSBufferSize;
+ int m_RDSBufferPtr;
+ size_t m_RDSBufferInitialSize;
+
int FindHeaders(uint8_t *buf, int buf_size);
public:
- cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
+ cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps, bool enableRDS);
virtual ~cParserMPEG2Audio();
- virtual void Parse(sStreamPacket *pkt);
+ virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
};
diff --git a/parser_MPEGVideo.c b/parser_MPEGVideo.c
index 5aa911a..01dc9a4 100644
--- a/parser_MPEGVideo.c
+++ b/parser_MPEGVideo.c
@@ -57,6 +57,7 @@ cParserMPEG2Video::cParserMPEG2Video(int pID, cTSStream *stream, sPtsWrap *ptsWr
m_Height = 0;
m_Width = 0;
m_Dar = 0.0;
+ m_FpsScale = 0;
m_PesBufferInitialSize = 80000;
m_IsVideo = true;
Reset();
@@ -66,7 +67,7 @@ cParserMPEG2Video::~cParserMPEG2Video()
{
}
-void cParserMPEG2Video::Parse(sStreamPacket *pkt)
+void cParserMPEG2Video::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
{
if (m_PesBufferPtr < 4)
return;
@@ -91,10 +92,16 @@ void cParserMPEG2Video::Parse(sStreamPacket *pkt)
if (frameComplete)
{
- if (!m_NeedSPS && !m_NeedIFrame)
+ if (!m_NeedSPS && !m_NeedIFrame && m_FrameValid)
{
- int fpsScale = m_Stream->Rescale(m_FrameDuration, DVD_TIME_BASE, 90000);
- bool streamChange = m_Stream->SetVideoInformation(fpsScale,DVD_TIME_BASE, m_Height, m_Width, m_Dar);
+ if (m_FpsScale == 0)
+ {
+ if (m_FrameDuration != DVD_NOPTS_VALUE)
+ m_FpsScale = m_Stream->Rescale(m_FrameDuration, DVD_TIME_BASE, 90000);
+ else
+ m_FpsScale = 40000;
+ }
+ bool streamChange = m_Stream->SetVideoInformation(m_FpsScale, DVD_TIME_BASE, m_Height, m_Width, m_Dar);
pkt->id = m_pID;
pkt->size = m_PesNextFramePtr;
@@ -107,6 +114,7 @@ void cParserMPEG2Video::Parse(sStreamPacket *pkt)
m_StartCode = 0xffffffff;
m_PesParserPtr = 0;
m_FoundFrame = false;
+ m_FrameValid = true;
}
}
@@ -148,13 +156,12 @@ int cParserMPEG2Video::Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &c
m_AuPrevDTS = m_AuDTS;
if (buf_ptr - 4 >= m_PesTimePos)
{
-
- m_AuDTS = m_curDTS;
+ m_AuDTS = m_curDTS != DVD_NOPTS_VALUE ? m_curDTS : m_curPTS;
m_AuPTS = m_curPTS;
}
else
{
- m_AuDTS = m_prevDTS;
+ m_AuDTS = m_prevDTS != DVD_NOPTS_VALUE ? m_prevDTS : m_prevPTS;;
m_AuPTS = m_prevPTS;
}
}
diff --git a/parser_MPEGVideo.h b/parser_MPEGVideo.h
index 18da858..5a03480 100644
--- a/parser_MPEGVideo.h
+++ b/parser_MPEGVideo.h
@@ -46,6 +46,7 @@ private:
int m_TemporalReference;
int m_TrLastTime;
int m_PicNumber;
+ int m_FpsScale;
int Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &complete);
bool Parse_MPEG2Video_SeqStart(uint8_t *buf);
@@ -55,7 +56,7 @@ public:
cParserMPEG2Video(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserMPEG2Video();
- virtual void Parse(sStreamPacket *pkt);
+ virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
virtual void Reset();
};
diff --git a/parser_Subtitle.c b/parser_Subtitle.c
index 6408fbd..cd30514 100644
--- a/parser_Subtitle.c
+++ b/parser_Subtitle.c
@@ -35,7 +35,7 @@ cParserSubtitle::~cParserSubtitle()
}
-void cParserSubtitle::Parse(sStreamPacket *pkt)
+void cParserSubtitle::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
{
int l = m_PesBufferPtr;
diff --git a/parser_Subtitle.h b/parser_Subtitle.h
index 296ac7c..d6e208e 100644
--- a/parser_Subtitle.h
+++ b/parser_Subtitle.h
@@ -31,7 +31,7 @@ public:
cParserSubtitle(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserSubtitle();
- virtual void Parse(sStreamPacket *pkt);
+ virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
};
diff --git a/parser_Teletext.c b/parser_Teletext.c
index b2ecaee..028e852 100644
--- a/parser_Teletext.c
+++ b/parser_Teletext.c
@@ -33,7 +33,7 @@ cParserTeletext::~cParserTeletext()
{
}
-void cParserTeletext::Parse(sStreamPacket *pkt)
+void cParserTeletext::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
{
int l = m_PesBufferPtr;
if (l < 1)
diff --git a/parser_Teletext.h b/parser_Teletext.h
index 831e7f5..2898176 100644
--- a/parser_Teletext.h
+++ b/parser_Teletext.h
@@ -35,7 +35,7 @@ public:
cParserTeletext(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserTeletext();
- virtual void Parse(sStreamPacket *pkt);
+ virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
};
#endif // VNSI_DEMUXER_TELETEXT_H
diff --git a/parser_h264.c b/parser_h264.c
index 6d45e30..d0b2620 100644
--- a/parser_h264.c
+++ b/parser_h264.c
@@ -68,7 +68,7 @@ cParserH264::~cParserH264()
{
}
-void cParserH264::Parse(sStreamPacket *pkt)
+void cParserH264::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data)
{
if (m_PesBufferPtr < 4)
return;
@@ -93,30 +93,38 @@ void cParserH264::Parse(sStreamPacket *pkt)
if (frameComplete)
{
- if (!m_NeedSPS && !m_NeedIFrame)
+ if (!m_NeedSPS && !m_NeedIFrame && m_FrameValid)
{
double PAR = (double)m_PixelAspect.num/(double)m_PixelAspect.den;
double DAR = (PAR * m_Width) / m_Height;
DEBUGLOG("H.264 SPS: PAR %i:%i", m_PixelAspect.num, m_PixelAspect.den);
DEBUGLOG("H.264 SPS: DAR %.2f", DAR);
-// int fpsScale = DVD_TIME_BASE / m_FPS;
+
+ int duration;
+ if (m_curDTS != DVD_NOPTS_VALUE && m_prevDTS != DVD_NOPTS_VALUE)
+ duration = m_curDTS - m_prevDTS;
+ else
+ duration = m_Stream->Rescale(20000, 90000, DVD_TIME_BASE);
+
if (m_FpsScale == 0)
{
- m_FpsScale = m_Stream->Rescale(m_curDTS - m_prevDTS, DVD_TIME_BASE, 90000);
+ m_FpsScale = m_Stream->Rescale(duration, DVD_TIME_BASE, 90000);
}
- bool streamChange = m_Stream->SetVideoInformation(m_FpsScale,DVD_TIME_BASE, m_Height, m_Width, DAR);
+
+ bool streamChange = m_Stream->SetVideoInformation(m_FpsScale, DVD_TIME_BASE, m_Height, m_Width, DAR);
pkt->id = m_pID;
pkt->size = m_PesNextFramePtr;
pkt->data = m_PesBuffer;
pkt->dts = m_DTS;
pkt->pts = m_PTS;
- pkt->duration = m_curDTS - m_prevDTS;
+ pkt->duration = duration;
pkt->streamChange = streamChange;
}
m_StartCode = 0xffffffff;
m_PesParserPtr = 0;
m_FoundFrame = false;
+ m_FrameValid = true;
}
}
@@ -226,7 +234,7 @@ int cParserH264::Parse_H264(uint32_t startcode, int buf_ptr, bool &complete)
}
case NAL_AUD:
- if (m_FoundFrame && (m_prevDTS != DVD_NOPTS_VALUE))
+ if (m_FoundFrame && (m_prevPTS != DVD_NOPTS_VALUE))
{
complete = true;
m_PesNextFramePtr = buf_ptr - 4;
diff --git a/parser_h264.h b/parser_h264.h
index 609ce0a..5ec7b31 100644
--- a/parser_h264.h
+++ b/parser_h264.h
@@ -108,7 +108,7 @@ public:
cParserH264(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps);
virtual ~cParserH264();
- virtual void Parse(sStreamPacket *pkt);
+ virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data);
virtual void Reset();
};
diff --git a/po/de_DE.po b/po/de_DE.po
new file mode 100644
index 0000000..b85488b
--- /dev/null
+++ b/po/de_DE.po
@@ -0,0 +1,61 @@
+# VDR VNSI plugin language source file.
+# Copyright (C) 2015 Alwin Esch
+# This file is distributed under the same license as the VDR package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VNSI-Server 1.0.0\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2015-01-23 22:25+0100\n"
+"PO-Revision-Date: 2015-01-23 21:46+0100\n"
+"Last-Translator: Alwin Esch\n"
+"Language-Team: German\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "PMT Timeout (0-10)"
+msgstr "PMT Auszeit (0-10)"
+
+msgid "Off"
+msgstr "Aus"
+
+msgid "RAM"
+msgstr "RAM"
+
+msgid "File"
+msgstr "Datei"
+
+msgid "Time Shift Mode"
+msgstr "Time Shift Modus"
+
+msgid "TS Buffersize (RAM) (1-80) x 100MB"
+msgstr "TS Puffergröße (RAM) (1-80) x 100MB"
+
+msgid "TS Buffersize (File) (1-10) x 1GB"
+msgstr "TS Puffergröße (Datei) (1-10) x 1GB"
+
+msgid "TS Buffer Directory"
+msgstr "TS-Puffer-Verzeichnis"
+
+msgid "Play Recording instead of live"
+msgstr "Wiedergeben als Aufzeichnung statt Live"
+
+msgid "Avoid EPG scan while streaming"
+msgstr "Keine EPG suche während der Wiedergabe durchführen"
+
+msgid "Recording with the same name exists"
+msgstr "Aufnahme mit der selben größe existiert"
+
+msgid "Error while read last filenumber"
+msgstr "Fehler beim lesen der letzen Dateinummer"
+
+msgid "Error while accessing vdrfile"
+msgstr "Fehler beim zugriff auf VDR-Datei"
+
+msgid "Error while accessing indexfile"
+msgstr "Fehler beim Zugriff der Indexdatei"
+
+msgid "Deleted recording vanished"
+msgstr "Gelöschte Aufnahme verschwunden"
diff --git a/po/lt_LT.po b/po/lt_LT.po
new file mode 100644
index 0000000..5b25d79
--- /dev/null
+++ b/po/lt_LT.po
@@ -0,0 +1,61 @@
+# VDR VNSI plugin language source file.
+# Copyright (C) 2015 Alwin Esch
+# This file is distributed under the same license as the VDR package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VNSI-Server 1.0.0\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2015-01-23 22:25+0100\n"
+"PO-Revision-Date: 2015-02-11 22:30+0200\n"
+"Last-Translator: Valdemaras Pipiras\n"
+"Language-Team: Lithuanian\n"
+"Language: lt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "PMT Timeout (0-10)"
+msgstr "PMT (0-10)"
+
+msgid "Off"
+msgstr "Išjungta"
+
+msgid "RAM"
+msgstr "RAM"
+
+msgid "File"
+msgstr "Failas"
+
+msgid "Time Shift Mode"
+msgstr "Atidėto žiūrėjimo (TS) būsena"
+
+msgid "TS Buffersize (RAM) (1-80) x 100MB"
+msgstr "TS buferio dydis (RAM) (1-80) x 100MB"
+
+msgid "TS Buffersize (File) (1-10) x 1GB"
+msgstr "TS buferio dydis (Failas) (1-10) x 1GB"
+
+msgid "TS Buffer Directory"
+msgstr "TS katalogas buferiavimui"
+
+msgid "Play Recording instead of live"
+msgstr "Groti įrašą vietoj gyvos transliacijos"
+
+msgid "Avoid EPG scan while streaming"
+msgstr "Vengti EPG skanavimo kol vyksta transliacija"
+
+msgid "Recording with the same name exists"
+msgstr "Jau yra įrašų tokiu pat pavadinimu"
+
+msgid "Error while read last filenumber"
+msgstr "Klaida nuskaitant paskutinių failų skaičių"
+
+msgid "Error while accessing vdrfile"
+msgstr "Klaida bandant atidaryti VDR failą"
+
+msgid "Error while accessing indexfile"
+msgstr "Klaida bandant atidaryti index failą"
+
+msgid "Deleted recording vanished"
+msgstr "Ištrintas įrašas galutinai išvalytas"
diff --git a/recordingscache.c b/recordingscache.c
index b435ce4..36f3593 100644
--- a/recordingscache.c
+++ b/recordingscache.c
@@ -14,14 +14,14 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include "recordingscache.h"
+#include "vnsiclient.h"
#include "hash.h"
cRecordingsCache::cRecordingsCache() {
@@ -35,7 +35,7 @@ cRecordingsCache& cRecordingsCache::GetInstance() {
return singleton;
}
-uint32_t cRecordingsCache::Register(cRecording* recording) {
+uint32_t cRecordingsCache::Register(cRecording* recording, bool deleted) {
cString filename = recording->FileName();
uint32_t uid = CreateStringHash(filename);
@@ -43,7 +43,8 @@ uint32_t cRecordingsCache::Register(cRecording* recording) {
if(m_recordings.find(uid) == m_recordings.end())
{
DEBUGLOG("%s - uid: %08x '%s'", __FUNCTION__, uid, (const char*)filename);
- m_recordings[uid] = filename;
+ m_recordings[uid].filename = filename;
+ m_recordings[uid].isDeleted = deleted;
}
m_mutex.Unlock();
@@ -59,10 +60,15 @@ cRecording* cRecordingsCache::Lookup(uint32_t uid) {
}
m_mutex.Lock();
- cString filename = m_recordings[uid];
+ cString filename = m_recordings[uid].filename;
DEBUGLOG("%s - filename: %s", __FUNCTION__, (const char*)filename);
- cRecording* r = Recordings.GetByName(filename);
+ cRecording* r;
+ if (!m_recordings[uid].isDeleted)
+ r = Recordings.GetByName(filename);
+ else
+ r = DeletedRecordings.GetByName(filename);
+
DEBUGLOG("%s - recording %s", __FUNCTION__, (r == NULL) ? "not found !" : "found");
m_mutex.Unlock();
diff --git a/recordingscache.h b/recordingscache.h
index 964e948..24933be 100644
--- a/recordingscache.h
+++ b/recordingscache.h
@@ -14,9 +14,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -41,13 +40,17 @@ public:
static cRecordingsCache& GetInstance();
- uint32_t Register(cRecording* recording);
+ uint32_t Register(cRecording* recording, bool deleted = false);
cRecording* Lookup(uint32_t uid);
private:
-
- std::map<uint32_t, cString> m_recordings;
+ struct RecordingsInfo
+ {
+ cString filename;
+ bool isDeleted;
+ };
+ std::map<uint32_t, RecordingsInfo> m_recordings;
cMutex m_mutex;
};
diff --git a/recplayer.c b/recplayer.c
index f5af3e4..99d3997 100644
--- a/recplayer.c
+++ b/recplayer.c
@@ -18,9 +18,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -200,7 +199,13 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
amount = m_totalLength;
if (position >= m_totalLength)
- return 0;
+ {
+ reScan();
+ if (position >= m_totalLength)
+ {
+ return 0;
+ }
+ }
if ((position + amount) > m_totalLength)
amount = m_totalLength - position;
@@ -216,16 +221,19 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
}
// segment not found / invalid position
- if (segmentNumber == -1) return 0;
+ if (segmentNumber == -1)
+ return 0;
// open file (if not already open)
- if (!openFile(segmentNumber)) return 0;
+ if (!openFile(segmentNumber))
+ return 0;
// work out position in current file
uint64_t filePosition = position - m_segments[segmentNumber]->start;
// seek to position
- if(lseek(m_file, filePosition, SEEK_SET) == -1) {
+ if(lseek(m_file, filePosition, SEEK_SET) == -1)
+ {
ERRORLOG("unable to seek to position: %lu", filePosition);
return 0;
}
@@ -237,7 +245,8 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
if ((bytes_read == 0) && (position < m_totalLength))
bytes_read += getBlock(buffer, position+1 , amount);
- if(bytes_read <= 0) {
+ if(bytes_read <= 0)
+ {
return 0;
}
@@ -254,7 +263,8 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
uint64_t cRecPlayer::positionFromFrameNumber(uint32_t frameNumber)
{
- if (!m_indexFile) return 0;
+ if (!m_indexFile)
+ return 0;
#if VDRVERSNUM < 10703
unsigned char retFileNumber;
int retFileOffset;
diff --git a/recplayer.h b/recplayer.h
index 784489c..f7a4af5 100644
--- a/recplayer.h
+++ b/recplayer.h
@@ -18,9 +18,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
diff --git a/requestpacket.c b/requestpacket.c
index 51245ca..77a6cf2 100644
--- a/requestpacket.c
+++ b/requestpacket.c
@@ -18,9 +18,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
diff --git a/requestpacket.h b/requestpacket.h
index 44fab53..24328a0 100644
--- a/requestpacket.h
+++ b/requestpacket.h
@@ -18,9 +18,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
diff --git a/responsepacket.c b/responsepacket.c
index 33ea5d0..439dfa7 100644
--- a/responsepacket.c
+++ b/responsepacket.c
@@ -18,9 +18,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -214,7 +213,7 @@ bool cResponsePacket::copyin(const uint8_t* src, uint32_t len)
}
uint8_t* cResponsePacket::reserve(uint32_t len) {
- if (!checkExtend(len)) return false;
+ if (!checkExtend(len)) return 0;
uint8_t* result = buffer + bufUsed;
bufUsed += len;
return result;
diff --git a/responsepacket.h b/responsepacket.h
index 0e0c9a0..0f5c457 100644
--- a/responsepacket.h
+++ b/responsepacket.h
@@ -18,9 +18,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
diff --git a/setup.c b/setup.c
index 61df5be..0e6249f 100644
--- a/setup.c
+++ b/setup.c
@@ -26,6 +26,8 @@ int TimeshiftMode = 0;
int TimeshiftBufferSize = 5;
int TimeshiftBufferFileSize = 6;
char TimeshiftBufferDir[PATH_MAX] = "\0";
+int PlayRecording = 0;
+int AvoidEPGScan = 0;
cMenuSetupVNSI::cMenuSetupVNSI(void)
{
@@ -39,13 +41,19 @@ cMenuSetupVNSI::cMenuSetupVNSI(void)
Add(new cMenuEditStraItem( tr("Time Shift Mode"), &newTimeshiftMode, 3, timeshiftModesTexts));
newTimeshiftBufferSize = TimeshiftBufferSize;
- Add(new cMenuEditIntItem( tr("TS Buffersize (RAM) (1-20) x 100MB"), &newTimeshiftBufferSize));
+ Add(new cMenuEditIntItem( tr("TS Buffersize (RAM) (1-80) x 100MB"), &newTimeshiftBufferSize));
newTimeshiftBufferFileSize = TimeshiftBufferFileSize;
Add(new cMenuEditIntItem( tr("TS Buffersize (File) (1-10) x 1GB"), &newTimeshiftBufferFileSize));
strn0cpy(newTimeshiftBufferDir, TimeshiftBufferDir, sizeof(newTimeshiftBufferDir));
Add(new cMenuEditStrItem(tr("TS Buffer Directory"), newTimeshiftBufferDir, sizeof(newTimeshiftBufferDir)));
+
+ newPlayRecording = PlayRecording;
+ Add(new cMenuEditBoolItem( tr("Play Recording instead of live"), &newPlayRecording));
+
+ newAvoidEPGScan = AvoidEPGScan;
+ Add(new cMenuEditBoolItem( tr("Avoid EPG scan while streaming"), &newAvoidEPGScan));
}
void cMenuSetupVNSI::Store(void)
@@ -56,8 +64,8 @@ void cMenuSetupVNSI::Store(void)
SetupStore(CONFNAME_TIMESHIFT, TimeshiftMode = newTimeshiftMode);
- if (newTimeshiftBufferSize > 40)
- newTimeshiftBufferSize = 40;
+ if (newTimeshiftBufferSize > 80)
+ newTimeshiftBufferSize = 80;
else if (newTimeshiftBufferSize < 1)
newTimeshiftBufferSize = 1;
SetupStore(CONFNAME_TIMESHIFTBUFFERSIZE, TimeshiftBufferSize = newTimeshiftBufferSize);
@@ -69,4 +77,8 @@ void cMenuSetupVNSI::Store(void)
SetupStore(CONFNAME_TIMESHIFTBUFFERFILESIZE, TimeshiftBufferFileSize = newTimeshiftBufferFileSize);
SetupStore(CONFNAME_TIMESHIFTBUFFERDIR, strn0cpy(TimeshiftBufferDir, newTimeshiftBufferDir, sizeof(TimeshiftBufferDir)));
+
+ SetupStore(CONFNAME_PLAYRECORDING, PlayRecording = newPlayRecording);
+
+ SetupStore(CONFNAME_AVOIDEPGSCAN, AvoidEPGScan = newAvoidEPGScan);
}
diff --git a/setup.h b/setup.h
index acd584f..cf116cf 100644
--- a/setup.h
+++ b/setup.h
@@ -32,6 +32,8 @@ private:
int newTimeshiftBufferSize;
int newTimeshiftBufferFileSize;
char newTimeshiftBufferDir[PATH_MAX];
+ int newPlayRecording;
+ int newAvoidEPGScan;
protected:
virtual void Store(void);
public:
diff --git a/status.c b/status.c
new file mode 100644
index 0000000..b94d877
--- /dev/null
+++ b/status.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2005-2014 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "vnsi.h"
+#include "status.h"
+#include "vnsiclient.h"
+#include <vdr/tools.h>
+#include <vdr/recording.h>
+#include <vdr/videodir.h>
+#include <vdr/shutdown.h>
+
+cVNSIStatus::~cVNSIStatus()
+{
+ Shutdown();
+}
+
+void cVNSIStatus::Shutdown()
+{
+ Cancel(5);
+ cMutexLock lock(&m_mutex);
+ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+ {
+ delete (*i);
+ }
+ m_clients.erase(m_clients.begin(), m_clients.end());
+}
+
+void cVNSIStatus::AddClient(cVNSIClient* client)
+{
+ cMutexLock lock(&m_mutex);
+ m_clients.push_back(client);
+}
+
+void cVNSIStatus::Action(void)
+{
+ cTimeMs chanTimer(0);
+
+ // get initial state of the recordings
+ int recState = -1;
+ Recordings.StateChanged(recState);
+
+ // get initial state of the timers
+ int timerState = -1;
+ Timers.Modified(timerState);
+
+ // last update of epg
+ time_t epgUpdate = cSchedules::Modified();
+
+ // delete old timeshift file
+ cString cmd;
+ struct stat sb;
+ if ((*TimeshiftBufferDir) && stat(TimeshiftBufferDir, &sb) == 0 && S_ISDIR(sb.st_mode))
+ {
+ if (TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/')
+ cmd = cString::sprintf("rm -f %s*.vnsi", TimeshiftBufferDir);
+ else
+ cmd = cString::sprintf("rm -f %s/*.vnsi", TimeshiftBufferDir);
+ }
+ else
+ {
+#if VDRVERSNUM >= 20102
+ cmd = cString::sprintf("rm -f %s/*.vnsi", cVideoDirectory::Name());
+#else
+ cmd = cString::sprintf("rm -f %s/*.vnsi", VideoDirectory);
+#endif
+ }
+ int ret = system(cmd);
+
+ // set thread priority
+ SetPriority(1);
+
+ while (Running())
+ {
+ m_mutex.Lock();
+
+ // remove disconnected clients
+ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end();)
+ {
+ if (!(*i)->Active())
+ {
+ INFOLOG("Client with ID %u seems to be disconnected, removing from client list", (*i)->GetID());
+ delete (*i);
+ i = m_clients.erase(i);
+ }
+ else {
+ i++;
+ }
+ }
+
+ // trigger clients to reload the modified channel list
+ if(m_clients.size() > 0 && chanTimer.TimedOut())
+ {
+ int modified = Channels.Modified();
+ if (modified)
+ {
+ Channels.SetModified((modified == CHANNELSMOD_USER) ? true : false);
+ INFOLOG("Requesting clients to reload channel list");
+ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+ (*i)->ChannelsChange();
+ }
+ chanTimer.Set(5000);
+ }
+
+ // reset inactivity timeout as long as there are clients connected
+ if(m_clients.size() > 0) {
+ ShutdownHandler.SetUserInactiveTimeout();
+ }
+
+ // update recordings
+ if(Recordings.StateChanged(recState))
+ {
+ INFOLOG("Recordings state changed (%i)", recState);
+ INFOLOG("Requesting clients to reload recordings list");
+ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+ (*i)->RecordingsChange();
+ }
+
+ // update timers
+ if(Timers.Modified(timerState))
+ {
+ INFOLOG("Timers state changed (%i)", timerState);
+ INFOLOG("Requesting clients to reload timers");
+ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+ {
+ (*i)->TimerChange();
+ }
+ }
+
+ // update epg
+ if((cSchedules::Modified() > epgUpdate + 10) || time(NULL) > epgUpdate + 300)
+ {
+ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
+ {
+ (*i)->EpgChange();
+ }
+ epgUpdate = time(NULL);
+ }
+
+ m_mutex.Unlock();
+
+ usleep(250*1000);
+ }
+}
diff --git a/parser_DTS.c b/status.h
similarity index 65%
copy from parser_DTS.c
copy to status.h
index 2ad2385..96e1726 100644
--- a/parser_DTS.c
+++ b/status.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2012 Team XBMC
+ * Copyright (C) 2005-2014 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -18,23 +18,25 @@
*
*/
-#include <stdlib.h>
-#include <assert.h>
-#include "config.h"
+#pragma once
-#include "parser_DTS.h"
-#include "bitstream.h"
+#include <vdr/thread.h>
+#include <list>
-cParserDTS::cParserDTS(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps)
- : cParser(pID, stream, ptsWrap, observePtsWraps)
-{
-}
+class cVNSIClient;
-cParserDTS::~cParserDTS()
-{
-}
+typedef std::list<cVNSIClient*> ClientList;
-void cParserDTS::Parse(sStreamPacket *pkt)
+class cVNSIStatus : public cThread
{
+public:
+ virtual ~cVNSIStatus();
+ void Shutdown();
+ void AddClient(cVNSIClient* client);
+
+protected:
+ virtual void Action(void);
-}
+ ClientList m_clients;
+ cMutex m_mutex;
+};
diff --git a/streamer.c b/streamer.c
index b9b81a3..fb23f58 100644
--- a/streamer.c
+++ b/streamer.c
@@ -17,9 +17,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -28,6 +27,7 @@
#include <time.h>
#include <vdr/channels.h>
+#include <vdr/eitscan.h>
#include "config.h"
#include "streamer.h"
@@ -40,10 +40,12 @@
// --- cLiveStreamer -------------------------------------------------
-cLiveStreamer::cLiveStreamer(int clientID, uint8_t timeshift, uint32_t timeout)
+cLiveStreamer::cLiveStreamer(int clientID, bool bAllowRDS, uint8_t timeshift, uint32_t timeout)
: cThread("cLiveStreamer stream processor")
, m_ClientID(clientID)
, m_scanTimeout(timeout)
+ , m_Demuxer(bAllowRDS)
+ , m_VideoInput(m_Event, m_Mutex, m_IsRetune)
{
m_Channel = NULL;
m_Socket = NULL;
@@ -55,6 +57,7 @@ cLiveStreamer::cLiveStreamer(int clientID, uint8_t timeshift, uint32_t timeout)
m_IFrameSeen = false;
m_VideoBuffer = NULL;
m_Timeshift = timeshift;
+ m_IsRetune = false;
memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
@@ -86,12 +89,12 @@ bool cLiveStreamer::Open(int serial)
return false;
bool recording = false;
- if (0) // test harness
+ if (VNSIServerConfig.testStreamActive) // test harness
{
recording = true;
- m_VideoBuffer = cVideoBuffer::Create("/home/xbmc/test.ts");
+ m_VideoBuffer = cVideoBuffer::Create(VNSIServerConfig.testStreamFile);
}
- else if (serial == -1)
+ else if (PlayRecording && serial == -1)
{
for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer))
{
@@ -129,6 +132,7 @@ bool cLiveStreamer::Open(int serial)
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());
@@ -164,26 +168,53 @@ void cLiveStreamer::Close(void)
void cLiveStreamer::Action(void)
{
int ret;
- sStreamPacket pkt;
- bool requestStreamChange = false;
+ sStreamPacket pkt_data;
+ sStreamPacket pkt_side_data; // Additional data
+ memset(&pkt_data, 0, sizeof(sStreamPacket));
+ memset(&pkt_side_data, 0, sizeof(sStreamPacket));
+ bool requestStreamChangeStdData = false;
+ bool requestStreamChangeExtData = false;
cTimeMs last_info(1000);
cTimeMs bufferStatsTimer(1000);
while (Running())
{
- ret = m_Demuxer.Read(&pkt);
+ ret = m_Demuxer.Read(&pkt_data, &pkt_side_data);
if (ret > 0)
{
- if (pkt.pmtChange)
+ if (pkt_data.pmtChange)
{
- requestStreamChange = true;
+ requestStreamChangeStdData = true;
+ requestStreamChangeExtData = true;
}
- if (pkt.data)
+
+ // Process normal data if present
+ if (pkt_data.data)
{
- if (pkt.streamChange || requestStreamChange)
+ if (pkt_data.streamChange || requestStreamChangeStdData)
sendStreamChange();
- requestStreamChange = false;
- sendStreamPacket(&pkt);
+ requestStreamChangeStdData = false;
+ if (pkt_data.reftime)
+ {
+ sendRefTime(&pkt_data);
+ pkt_data.reftime = 0;
+ }
+ sendStreamPacket(&pkt_data);
+ }
+
+ // If some additional data is present inside the stream, it is written there (currently RDS inside MPEG2-Audio)
+ if (pkt_side_data.data)
+ {
+ if (pkt_side_data.streamChange || requestStreamChangeExtData)
+ sendStreamChange();
+ requestStreamChangeExtData = false;
+ if (pkt_side_data.reftime)
+ {
+ sendRefTime(&pkt_side_data);
+ pkt_side_data.reftime = 0;
+ }
+ sendStreamPacket(&pkt_side_data);
+ pkt_side_data.data = NULL;
}
// send signal info every 10 sec.
@@ -191,6 +222,13 @@ void cLiveStreamer::Action(void)
{
last_info.Set(0);
sendSignalInfo();
+
+ // prevent EPG scan (activity timeout is 60s)
+ // EPG scan can cause artifacts on dual tuner cards
+ if (AvoidEPGScan)
+ {
+ EITScanner.Activity();
+ }
}
// send buffer stats
@@ -203,7 +241,17 @@ void cLiveStreamer::Action(void)
else if (ret == -1)
{
// no data
- usleep(10000);
+ {
+ cMutexLock lock(&m_Mutex);
+ if (m_IsRetune)
+ {
+ m_VideoInput.Close();
+ m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer);
+ m_IsRetune = false;
+ }
+ else
+ m_Event.TimedWait(m_Mutex, 10);
+ }
if(m_last_tick.Elapsed() >= (uint64_t)(m_scanTimeout*1000))
{
sendStreamStatus();
@@ -314,6 +362,17 @@ void cLiveStreamer::sendStreamChange()
resp->add_U32(BlockAlign);
resp->add_U32(BitRate);
resp->add_U32(BitsPerSample);
+
+ for (unsigned int i = 0; i < stream->GetSideDataTypes()->size(); i++)
+ {
+ resp->add_U32(stream->GetSideDataTypes()->at(i).first);
+ if (stream->GetSideDataTypes()->at(i).second == scRDS)
+ {
+ resp->add_String("RDS");
+ resp->add_String(stream->GetLanguage());
+ resp->add_U32(stream->GetPID());
+ }
+ }
}
else if (stream->Type() == stMPEG2VIDEO)
{
@@ -374,7 +433,7 @@ void cLiveStreamer::sendStreamChange()
else if (stream->Type() == stAACLATM)
{
stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign);
- resp->add_String("AACLATM");
+ resp->add_String("AAC_LATM");
resp->add_String(stream->GetLanguage());
resp->add_U32(Channels);
resp->add_U32(SampleRate);
@@ -601,13 +660,32 @@ void cLiveStreamer::sendBufferStatus()
delete resp;
return;
}
- int32_t start, current, end;
+ uint32_t start, end;
bool timeshift;
- m_Demuxer.BufferStatus(timeshift, start, current, end);
+ m_Demuxer.BufferStatus(timeshift, start, end);
resp->add_U8(timeshift);
- resp->add_S32(start);
- resp->add_S32(current);
- resp->add_S32(end);
+ resp->add_U32(start);
+ resp->add_U32(end);
+ resp->finaliseStream();
+ m_Socket->write(resp->getPtr(), resp->getLen());
+ delete resp;
+}
+
+void cLiveStreamer::sendRefTime(sStreamPacket *pkt)
+{
+ if(pkt == NULL)
+ return;
+
+ cResponsePacket *resp = new cResponsePacket();
+ if (!resp->initStream(VNSI_STREAM_REFTIME, 0, 0, 0, 0, 0))
+ {
+ ERRORLOG("stream response packet init fail");
+ delete resp;
+ return;
+ }
+
+ resp->add_U32(pkt->reftime);
+ resp->add_U64(pkt->pts);
resp->finaliseStream();
m_Socket->write(resp->getPtr(), resp->getLen());
delete resp;
@@ -619,3 +697,14 @@ bool cLiveStreamer::SeekTime(int64_t time, uint32_t &serial)
serial = m_Demuxer.GetSerial();
return ret;
}
+
+void cLiveStreamer::RetuneChannel(const cChannel *channel)
+{
+ if (m_Channel != channel || !m_VideoInput.IsOpen())
+ return;
+
+ INFOLOG("re-tune to channel %s", m_Channel->Name());
+ cMutexLock lock(&m_Mutex);
+ m_IsRetune = true;
+ m_Event.Broadcast();
+}
diff --git a/streamer.h b/streamer.h
index 3c308e5..602248c 100644
--- a/streamer.h
+++ b/streamer.h
@@ -17,9 +17,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -58,6 +57,7 @@ private:
void sendSignalInfo();
void sendStreamStatus();
void sendBufferStatus();
+ void sendRefTime(sStreamPacket *pkt);
int m_ClientID;
const cChannel *m_Channel; /*!> Channel to stream */
@@ -80,6 +80,9 @@ private:
cVideoInput m_VideoInput;
int m_Priority;
uint8_t m_Timeshift;
+ cCondVar m_Event;
+ cMutex m_Mutex;
+ bool m_IsRetune;
protected:
virtual void Action(void);
@@ -87,7 +90,7 @@ protected:
void Close();
public:
- cLiveStreamer(int clientID, uint8_t timeshift, uint32_t timeout = 0);
+ cLiveStreamer(int clientID, bool bAllowRDS, uint8_t timeshift, uint32_t timeout = 0);
virtual ~cLiveStreamer();
void Activate(bool On);
@@ -97,6 +100,7 @@ public:
bool IsAudioOnly() { return m_IsAudioOnly; }
bool IsMPEGPS() { return m_IsMPEGPS; }
bool SeekTime(int64_t time, uint32_t &serial);
+ void RetuneChannel(const cChannel *channel);
};
#endif // VNSI_RECEIVER_H
diff --git a/videobuffer.c b/videobuffer.c
index 92c38f0..94292bb 100644
--- a/videobuffer.c
+++ b/videobuffer.c
@@ -37,7 +37,7 @@ class cVideoBufferSimple : public cVideoBuffer
friend class cVideoBuffer;
public:
virtual void Put(uint8_t *buf, unsigned int size);
- virtual int ReadBlock(uint8_t **buf, unsigned int size);
+ virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime);
protected:
cVideoBufferSimple();
@@ -64,7 +64,7 @@ void cVideoBufferSimple::Put(uint8_t *buf, unsigned int size)
m_Buffer->Put(buf, size);
}
-int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size)
+int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime)
{
int readBytes;
if (m_BytesConsumed)
@@ -96,6 +96,8 @@ int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size)
}
m_BytesConsumed += TS_SIZE;
+ endTime = 0;
+ wrapTime = 0;
return TS_SIZE;
}
@@ -193,7 +195,7 @@ class cVideoBufferRAM : public cVideoBufferTimeshift
friend class cVideoBuffer;
public:
virtual void Put(uint8_t *buf, unsigned int size);
- virtual int ReadBlock(uint8_t **buf, unsigned int size);
+ virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime);
virtual void SetPos(off_t pos);
protected:
@@ -241,7 +243,6 @@ void cVideoBufferRAM::Put(uint8_t *buf, unsigned int size)
{
if (Available() + MARGIN >= m_BufferSize)
{
- ERRORLOG("------------- skipping data");
return;
}
@@ -263,11 +264,16 @@ void cVideoBufferRAM::Put(uint8_t *buf, unsigned int size)
if (!m_BufferFull)
{
if ((m_WritePtr + 2*MARGIN) > m_BufferSize)
+ {
m_BufferFull = true;
+ time(&m_bufferWrapTime);
+ }
}
+
+ time(&m_bufferEndTime);
}
-int cVideoBufferRAM::ReadBlock(uint8_t **buf, unsigned int size)
+int cVideoBufferRAM::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime)
{
// move read pointer
if (m_BytesConsumed)
@@ -276,6 +282,9 @@ int cVideoBufferRAM::ReadBlock(uint8_t **buf, unsigned int size)
m_ReadPtr += m_BytesConsumed;
if (m_ReadPtr >= m_BufferSize)
m_ReadPtr -= m_BufferSize;
+
+ endTime = m_bufferEndTime;
+ wrapTime = m_bufferWrapTime;
}
m_BytesConsumed = 0;
@@ -323,7 +332,7 @@ friend class cVideoBuffer;
public:
virtual off_t GetPosMax();
virtual void Put(uint8_t *buf, unsigned int size);
- virtual int ReadBlock(uint8_t **buf, unsigned int size);
+ virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime);
virtual void SetPos(off_t pos);
protected:
@@ -442,7 +451,6 @@ void cVideoBufferFile::Put(uint8_t *buf, unsigned int size)
{
if (Available() + MARGIN >= m_BufferSize)
{
- ERRORLOG("------------- skipping data");
return;
}
@@ -491,8 +499,13 @@ void cVideoBufferFile::Put(uint8_t *buf, unsigned int size)
if (!m_BufferFull)
{
if ((m_WritePtr + 2*MARGIN) > m_BufferSize)
+ {
m_BufferFull = true;
+ time(&m_bufferWrapTime);
+ }
}
+
+ time(&m_bufferEndTime);
}
int cVideoBufferFile::ReadBytes(uint8_t *buf, off_t pos, unsigned int size)
@@ -509,7 +522,7 @@ int cVideoBufferFile::ReadBytes(uint8_t *buf, off_t pos, unsigned int size)
}
}
-int cVideoBufferFile::ReadBlock(uint8_t **buf, unsigned int size)
+int cVideoBufferFile::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime)
{
// move read pointer
if (m_BytesConsumed)
@@ -519,6 +532,9 @@ int cVideoBufferFile::ReadBlock(uint8_t **buf, unsigned int size)
if (m_ReadPtr >= m_BufferSize)
m_ReadPtr -= m_BufferSize;
m_ReadCachePtr += m_BytesConsumed;
+
+ endTime = m_bufferEndTime;
+ wrapTime = m_bufferWrapTime;
}
m_BytesConsumed = 0;
@@ -607,7 +623,8 @@ friend class cVideoBuffer;
public:
virtual off_t GetPosMax();
virtual void Put(uint8_t *buf, unsigned int size);
- virtual int ReadBlock(uint8_t **buf, unsigned int size);
+ virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime);
+ virtual time_t GetRefTime();
protected:
cVideoBufferRecording(cRecording *rec);
@@ -663,9 +680,15 @@ bool cVideoBufferRecording::Init()
m_ReadCacheSize = 0;
m_InputAttached = false;
m_ScanTimer.Set(0);
+
return true;
}
+time_t cVideoBufferRecording::GetRefTime()
+{
+ return m_Recording->Start();
+}
+
off_t cVideoBufferRecording::Available()
{
if (m_ScanTimer.TimedOut())
@@ -677,7 +700,7 @@ off_t cVideoBufferRecording::Available()
return cVideoBufferTimeshift::Available();
}
-int cVideoBufferRecording::ReadBlock(uint8_t **buf, unsigned int size)
+int cVideoBufferRecording::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime)
{
// move read pointer
if (m_BytesConsumed)
@@ -739,6 +762,8 @@ int cVideoBufferRecording::ReadBlock(uint8_t **buf, unsigned int size)
}
m_BytesConsumed += TS_SIZE;
+ time(&endTime);
+ wrapTime = 0;
return TS_SIZE;
}
@@ -829,6 +854,8 @@ cVideoBuffer::cVideoBuffer()
{
m_CheckEof = false;
m_InputAttached = true;
+ m_bufferEndTime = 0;
+ m_bufferWrapTime = 0;
}
cVideoBuffer::~cVideoBuffer()
@@ -897,9 +924,9 @@ cVideoBuffer* cVideoBuffer::Create(cRecording *rec)
return buffer;
}
-int cVideoBuffer::Read(uint8_t **buf, unsigned int size)
+int cVideoBuffer::Read(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime)
{
- int count = ReadBlock(buf, size);
+ int count = ReadBlock(buf, size, endTime, wrapTime);
// check for end of file
if (!m_InputAttached && count != TS_SIZE)
@@ -925,3 +952,10 @@ void cVideoBuffer::AttachInput(bool attach)
{
m_InputAttached = attach;
}
+
+time_t cVideoBuffer::GetRefTime()
+{
+ time_t t;
+ time(&t);
+ return t;
+}
diff --git a/videobuffer.h b/videobuffer.h
index 8ea74fc..c61bd3c 100644
--- a/videobuffer.h
+++ b/videobuffer.h
@@ -34,7 +34,7 @@ public:
static cVideoBuffer* Create(cString filename);
static cVideoBuffer* Create(cRecording *rec);
virtual void Put(uint8_t *buf, unsigned int size) = 0;
- virtual int ReadBlock(uint8_t **buf, unsigned int size) = 0;
+ virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime) = 0;
virtual off_t GetPosMin() { return 0; };
virtual off_t GetPosMax() { return 0; };
virtual off_t GetPosCur() { return 0; };
@@ -42,11 +42,14 @@ public:
virtual void SetPos(off_t pos) {};
virtual void SetCache(bool on) {};
virtual bool HasBuffer() { return false; };
- int Read(uint8_t **buf, unsigned int size);
+ virtual time_t GetRefTime();
+ int Read(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime);
void AttachInput(bool attach);
protected:
cVideoBuffer();
cTimeMs m_Timer;
bool m_CheckEof;
bool m_InputAttached;
+ time_t m_bufferEndTime;
+ time_t m_bufferWrapTime;
};
diff --git a/videoinput.c b/videoinput.c
index 45769ae..d7e45eb 100644
--- a/videoinput.c
+++ b/videoinput.c
@@ -67,8 +67,11 @@ void cLiveReceiver::Receive(uchar *Data, int Length)
inline void cLiveReceiver::Activate(bool On)
{
- m_VideoInput->Attach(On);
DEBUGLOG("activate live receiver: %d", On);
+ if (!On && !m_VideoInput->m_PmtChange)
+ {
+ m_VideoInput->Retune();
+ }
}
// --- cLivePatFilter ----------------------------------------------------
@@ -387,7 +390,8 @@ void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Le
// ----------------------------------------------------------------------------
-cVideoInput::cVideoInput()
+cVideoInput::cVideoInput(cCondVar &condVar, cMutex &mutex, bool &retune) :
+ m_Event(condVar), m_Mutex(mutex), m_IsRetune(retune)
{
m_Device = NULL;;
m_PatFilter = NULL;
@@ -408,7 +412,7 @@ bool cVideoInput::Open(const cChannel *channel, int priority, cVideoBuffer *vide
m_VideoBuffer = videoBuffer;
m_Channel = channel;
m_Priority = priority;
- m_Device = cDevice::GetDevice(m_Channel, m_Priority, true);
+ m_Device = cDevice::GetDevice(m_Channel, m_Priority, false);
if (m_Device != NULL)
{
@@ -423,6 +427,7 @@ bool cVideoInput::Open(const cChannel *channel, int priority, cVideoBuffer *vide
m_Receiver = new cLiveReceiver(this, m_Channel, m_Priority);
m_Device->AttachReceiver(m_Receiver0);
m_Device->AttachFilter(m_PatFilter);
+ m_VideoBuffer->AttachInput(true);
Start();
return true;
}
@@ -432,6 +437,7 @@ bool cVideoInput::Open(const cChannel *channel, int priority, cVideoBuffer *vide
void cVideoInput::Close()
{
+ INFOLOG("close video input ...");
Cancel(5);
if (m_Device)
@@ -484,6 +490,21 @@ void cVideoInput::Close()
DELETENULL(m_PatFilter);
}
}
+ m_Channel = NULL;
+ m_Device = NULL;
+ if (m_VideoBuffer)
+ {
+ m_VideoBuffer->AttachInput(false);
+ m_VideoBuffer = NULL;
+ }
+}
+
+bool cVideoInput::IsOpen()
+{
+ if (m_Channel)
+ return true;
+ else
+ return false;
}
cChannel *cVideoInput::PmtChannel()
@@ -496,11 +517,11 @@ void cVideoInput::PmtChange(int pidChange)
if (pidChange)
{
INFOLOG("Video Input - new pmt, attaching receiver");
+ m_PmtChange = true;
m_Device->Detach(m_Receiver);
m_Receiver->SetPids(NULL);
m_Receiver->SetPids(&m_Receiver->m_PmtChannel);
m_Receiver->AddPid(m_Receiver->m_PmtChannel.Tpid());
- m_PmtChange = true;
m_Device->AttachReceiver(m_Receiver);
m_SeenPmt = true;
}
@@ -521,9 +542,12 @@ inline void cVideoInput::Receive(uchar *data, int length)
m_VideoBuffer->Put(data, length);
}
-inline void cVideoInput::Attach(bool on)
+void cVideoInput::Retune()
{
- m_VideoBuffer->AttachInput(on);
+ INFOLOG("call retune ...");
+ cMutexLock lock(&m_Mutex);
+ m_IsRetune = true;
+ m_Event.Broadcast();
}
void cVideoInput::Action()
diff --git a/videoinput.h b/videoinput.h
index 19a1d7d..f8e175d 100644
--- a/videoinput.h
+++ b/videoinput.h
@@ -33,17 +33,18 @@ class cVideoInput : public cThread
friend class cLivePatFilter;
friend class cLiveReceiver;
public:
- cVideoInput();
+ cVideoInput(cCondVar &condVar, cMutex &mutex, bool &retune);
virtual ~cVideoInput();
bool Open(const cChannel *channel, int priority, cVideoBuffer *videoBuffer);
void Close();
+ bool IsOpen();
protected:
virtual void Action(void);
void PmtChange(int pidChange);
cChannel *PmtChannel();
void Receive(uchar *data, int length);
- void Attach(bool on);
+ void Retune();
cDevice *m_Device;
cLivePatFilter *m_PatFilter;
cLiveReceiver *m_Receiver;
@@ -53,4 +54,7 @@ protected:
int m_Priority;
bool m_PmtChange;
bool m_SeenPmt;
+ cCondVar &m_Event;
+ cMutex &m_Mutex;
+ bool &m_IsRetune;
};
diff --git a/vnsi.c b/vnsi.c
index 6ae4cf5..18c8616 100644
--- a/vnsi.c
+++ b/vnsi.c
@@ -17,9 +17,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -35,32 +34,56 @@ cPluginVNSIServer::cPluginVNSIServer(void)
{
Server = NULL;
VNSIServer = NULL;
+ probe = new cDvbVsniDeviceProbe();
}
cPluginVNSIServer::~cPluginVNSIServer()
{
// Clean up after yourself!
+ delete probe;
}
const char *cPluginVNSIServer::CommandLineHelp(void)
{
- return " -t n, --timeout=n stream data timeout in seconds (default: 10)\n";
+ return " -t n, --timeout=n stream data timeout in seconds (default: 10)\n"
+ " -d , --device act as the primary device\n"
+ " -s n, --test=n TS stream test file to simulate as channel\n"
+ " -p n, --port=n tcp port to listen on\n";
}
bool cPluginVNSIServer::ProcessArgs(int argc, char *argv[])
{
// Implement command line argument processing here if applicable.
static struct option long_options[] = {
+ { "port", required_argument, NULL, 'p' },
{ "timeout", required_argument, NULL, 't' },
+ { "device", no_argument, NULL, 'd' },
+ { "test", required_argument, NULL, 'T' },
{ NULL, no_argument, NULL, 0 }
};
int c;
- while ((c = getopt_long(argc, argv, "t:", long_options, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "t:dT:p:", long_options, NULL)) != -1) {
switch (c) {
+ case 'p': if(optarg != NULL) VNSIServerConfig.listen_port = atoi(optarg);
+ break;
case 't': if(optarg != NULL) VNSIServerConfig.stream_timeout = atoi(optarg);
break;
+ case 'd': VNSIServerConfig.device = true;
+ break;
+ case 'T': if(optarg != NULL) {
+ VNSIServerConfig.testStreamFile = optarg;
+
+ struct stat file_stat;
+ if (stat(VNSIServerConfig.testStreamFile, &file_stat) == 0) {
+ VNSIServerConfig.testStreamActive = true;
+ printf("vnsiserver: requested test stream file '%s' played now on all channels\n", *VNSIServerConfig.testStreamFile);
+ }
+ else
+ printf("vnsiserver: requested test stream file '%s' not present, started without\n", *VNSIServerConfig.testStreamFile);
+ }
+ break;
default: return false;
}
}
@@ -78,6 +101,7 @@ bool cPluginVNSIServer::Initialize(void)
bool cPluginVNSIServer::Start(void)
{
+ INFOLOG("Starting vnsi server at port=%d\n", VNSIServerConfig.listen_port);
Server = new cVNSIServer(VNSIServerConfig.listen_port);
return true;
@@ -131,6 +155,10 @@ bool cPluginVNSIServer::SetupParse(const char *Name, const char *Value)
TimeshiftBufferFileSize = atoi(Value);
else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERDIR))
strn0cpy(TimeshiftBufferDir, Value, sizeof(TimeshiftBufferDir));
+ else if (!strcasecmp(Name, CONFNAME_PLAYRECORDING))
+ PlayRecording = atoi(Value);
+ else if (!strcasecmp(Name, CONFNAME_AVOIDEPGSCAN))
+ AvoidEPGScan = atoi(Value);
else
return false;
return true;
@@ -166,4 +194,40 @@ void cPluginVNSIServer::StoreSetup(const char *Name, int Value)
}
}
+bool cDvbVsniDeviceProbe::Probe(int Adapter, int Frontend)
+{
+ if (VNSIServerConfig.device)
+ {
+ new cDvbVnsiDevice(Adapter, Frontend);
+ return true;
+ }
+ else
+ return false;
+}
+
+cDvbVnsiDevice::cDvbVnsiDevice(int Adapter, int Frontend) :cDvbDevice(Adapter, Frontend)
+{
+
+}
+
+cDvbVnsiDevice::~cDvbVnsiDevice()
+{
+
+}
+
+bool cDvbVnsiDevice::HasDecoder(void) const
+{
+ return true;
+}
+
+int cDvbVnsiDevice::PlayVideo(const uchar *Data, int Length)
+{
+ return Length;
+}
+
+int cDvbVnsiDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
+{
+ return Length;
+}
+
VDRPLUGINCREATOR(cPluginVNSIServer); // Don't touch this!
diff --git a/vnsi.h b/vnsi.h
index eb7ed79..49a3740 100644
--- a/vnsi.h
+++ b/vnsi.h
@@ -17,9 +17,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -27,7 +26,7 @@
#include <vdr/plugin.h>
#include "vnsiserver.h"
-static const char *VERSION = "0.9.2";
+static const char *VERSION = "1.3.0";
static const char *DESCRIPTION = "VDR-Network-Streaming-Interface (VNSI) Server";
extern int PmtTimeout;
@@ -35,11 +34,16 @@ extern int TimeshiftMode;
extern int TimeshiftBufferSize;
extern int TimeshiftBufferFileSize;
extern char TimeshiftBufferDir[PATH_MAX];
+extern int PlayRecording;
+extern int AvoidEPGScan;
+
+class cDvbVsniDeviceProbe;
class cPluginVNSIServer : public cPlugin {
private:
cVNSIServer *Server;
static cPluginVNSIServer *VNSIServer;
+ cDvbVsniDeviceProbe *probe;
public:
cPluginVNSIServer(void);
@@ -66,3 +70,18 @@ public:
static void StoreSetup(const char *Name, int Value);
};
+class cDvbVsniDeviceProbe : public cDvbDeviceProbe
+{
+public:
+ virtual bool Probe(int Adapter, int Frontend);
+};
+
+class cDvbVnsiDevice : public cDvbDevice
+{
+public:
+ cDvbVnsiDevice(int Adapter, int Frontend);
+ virtual ~cDvbVnsiDevice();
+ virtual bool HasDecoder(void) const;
+ int PlayVideo(const uchar *Data, int Length);
+ int PlayAudio(const uchar *Data, int Length, uchar Id);
+};
diff --git a/vnsiclient.c b/vnsiclient.c
index f88d00b..82b7926 100644
--- a/vnsiclient.c
+++ b/vnsiclient.c
@@ -17,9 +17,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -47,24 +46,11 @@
#include "requestpacket.h"
#include "responsepacket.h"
#include "hash.h"
+#include "channelfilter.h"
#include "wirbelscanservice.h" /// copied from modified wirbelscan plugin
/// must be hold up to date with wirbelscan
-static bool IsRadio(const cChannel* channel)
-{
- bool isRadio = false;
-
- // assume channels without VPID & APID are video channels
- if (channel->Vpid() == 0 && channel->Apid(0) == 0)
- isRadio = false;
- // channels without VPID are radio channels (channels with VPID 1 are encrypted radio channels)
- else if (channel->Vpid() == 0 || channel->Vpid() == 1)
- isRadio = true;
-
- return isRadio;
-}
-
cMutex cVNSIClient::m_timerLock;
cVNSIClient::cVNSIClient(int fd, unsigned int id, const char *ClientAdr)
@@ -80,6 +66,7 @@ cVNSIClient::cVNSIClient(int fd, unsigned int id, const char *ClientAdr)
m_processSCAN_Response = NULL;
m_processSCAN_Socket = NULL;
m_Osd = NULL;
+ m_bSupportRDS = false;
m_socket.SetHandle(fd);
@@ -179,7 +166,7 @@ void cVNSIClient::Action(void)
bool cVNSIClient::StartChannelStreaming(const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout)
{
- m_Streamer = new cLiveStreamer(m_Id, timeshift, timeout);
+ m_Streamer = new cLiveStreamer(m_Id, m_bSupportRDS, timeshift, timeout);
m_isStreaming = m_Streamer->StreamChannel(channel, priority, &m_socket, m_resp);
return m_isStreaming;
}
@@ -218,7 +205,7 @@ void cVNSIClient::TimerChange()
}
}
-void cVNSIClient::ChannelChange()
+void cVNSIClient::ChannelsChange()
{
cMutexLock lock(&m_msgLock);
@@ -268,22 +255,37 @@ void cVNSIClient::EpgChange()
if (!schedules)
return;
- std::map<int, time_t>::iterator it;
+ std::map<int, sEpgUpdate>::iterator it;
for (const cSchedule *schedule = schedules->First(); schedule; schedule = schedules->Next(schedule))
{
cEvent *lastEvent = schedule->Events()->Last();
if (!lastEvent)
continue;
+ Channels.Lock(false);
+ const cChannel *channel = Channels.GetByChannelID(schedule->ChannelID());
+ Channels.Unlock();
+
+ if (!channel)
+ continue;
+
+ if (!VNSIChannelFilter.PassFilter(*channel))
+ continue;
+
uint32_t channelId = CreateStringHash(schedule->ChannelID().ToString());
it = m_epgUpdate.find(channelId);
- if (it == m_epgUpdate.end())
+ if (it != m_epgUpdate.end() && it->second.lastEvent >= lastEvent->StartTime())
{
continue;
}
- if (it->second >= lastEvent->StartTime())
+ if (it->second.attempts > 3)
+ {
continue;
+ }
+ it->second.attempts++;
+
+ INFOLOG("Trigger EPG update for channel %s, id: %d", channel->Name(), channelId);
cResponsePacket *resp = new cResponsePacket();
if (!resp->initStatus(VNSI_STATUS_EPGCHANGE))
@@ -295,10 +297,6 @@ void cVNSIClient::EpgChange()
resp->finalise();
m_socket.write(resp->getPtr(), resp->getLen());
delete resp;
-
- const cChannel *channel = FindChannelByUID(channelId);
- if (channel)
- INFOLOG("Trigger EPG update for channel %s", channel->Name());
}
}
@@ -376,6 +374,15 @@ void cVNSIClient::OsdStatusMessage(const char *Message)
}
}
+void cVNSIClient::ChannelChange(const cChannel *Channel)
+{
+ cMutexLock lock(&m_msgLock);
+ if (m_isStreaming && m_Streamer)
+ {
+ m_Streamer->RetuneChannel(Channel);
+ }
+}
+
bool cVNSIClient::processRequest(cRequestPacket* req)
{
cMutexLock lock(&m_msgLock);
@@ -458,6 +465,10 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
result = processRecStream_GetIFrame();
break;
+ case VNSI_RECSTREAM_GETLENGTH:
+ result = processRecStream_GetLength();
+ break;
+
/** OPCODE 60 - 79: VNSI network functions for channel access */
case VNSI_CHANNELS_GETCOUNT:
@@ -480,6 +491,26 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
result = processCHANNELS_GetGroupMembers();
break;
+ case VNSI_CHANNELS_GETCAIDS:
+ result = processCHANNELS_GetCaids();
+ break;
+
+ case VNSI_CHANNELS_GETWHITELIST:
+ result = processCHANNELS_GetWhitelist();
+ break;
+
+ case VNSI_CHANNELS_GETBLACKLIST:
+ result = processCHANNELS_GetBlacklist();
+ break;
+
+ case VNSI_CHANNELS_SETWHITELIST:
+ result = processCHANNELS_SetWhitelist();
+ break;
+
+ case VNSI_CHANNELS_SETBLACKLIST:
+ result = processCHANNELS_SetBlacklist();
+ break;
+
/** OPCODE 80 - 99: VNSI network functions for timer access */
case VNSI_TIMER_GETCOUNT:
result = processTIMER_GetCount();
@@ -527,6 +558,10 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
result = processRECORDINGS_Delete();
break;
+ case VNSI_RECORDINGS_GETEDL:
+ result = processRECORDINGS_GetEdl();
+ break;
+
/** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
case VNSI_EPG_GETFORCHANNEL:
@@ -567,6 +602,32 @@ bool cVNSIClient::processRequest(cRequestPacket* req)
case VNSI_OSD_HITKEY:
result = processOSD_Hitkey();
break;
+
+
+ /** OPCODE 180 - 189: VNSI network functions for deleted recording access */
+ case VNSI_RECORDINGS_DELETED_ACCESS_SUPPORTED:
+ result = processRECORDINGS_DELETED_Supported();
+ break;
+
+ case VNSI_RECORDINGS_DELETED_GETCOUNT:
+ result = processRECORDINGS_DELETED_GetCount();
+ break;
+
+ case VNSI_RECORDINGS_DELETED_GETLIST:
+ result = processRECORDINGS_DELETED_GetList();
+ break;
+
+ case VNSI_RECORDINGS_DELETED_DELETE:
+ result = processRECORDINGS_DELETED_Delete();
+ break;
+
+ case VNSI_RECORDINGS_DELETED_UNDELETE:
+ result = processRECORDINGS_DELETED_Undelete();
+ break;
+
+ case VNSI_RECORDINGS_DELETED_DELETE_ALL:
+ result = processRECORDINGS_DELETED_DeleteAll();
+ break;
}
delete m_resp;
@@ -603,11 +664,21 @@ bool cVNSIClient::process_Login() /* OPCODE 1 */
m_resp->add_String(VNSI_SERVER_VERSION);
m_resp->finalise();
- if (m_protocolVersion != VNSI_PROTOCOLVERSION)
+ if (m_protocolVersion < VNSI_MIN_PROTOCOLVERSION)
ERRORLOG("Client '%s' have a not allowed protocol version '%u', terminating client", clientName, m_protocolVersion);
else
SetLoggedIn(true);
+ if (m_protocolVersion < VNSI_RDS_PROTOCOLVERSION)
+ {
+ INFOLOG("RDS not supported on client '%s' and stream type disabled", clientName);
+ m_bSupportRDS = false;
+ }
+ else
+ {
+ m_bSupportRDS = true;
+ }
+
m_socket.write(m_resp->getPtr(), m_resp->getLen());
delete[] clientName;
@@ -633,6 +704,7 @@ bool cVNSIClient::process_EnableStatusInterface()
bool enabled = m_req->extract_U8();
SetStatusInterface(enabled);
+ SetPriority(1);
m_resp->add_U32(VNSI_RET_OK);
m_resp->finalise();
@@ -689,6 +761,11 @@ bool cVNSIClient::process_StoreSetup() /* OPCODE 9 */
int value = m_req->extract_U32();
cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFTBUFFERFILESIZE, value);
}
+ else if (!strcasecmp(name, CONFNAME_PLAYRECORDING))
+ {
+ int value = m_req->extract_U32();
+ cPluginVNSIServer::StoreSetup(CONFNAME_PLAYRECORDING, value);
+ }
m_resp->add_U32(VNSI_RET_OK);
m_resp->finalise();
@@ -919,6 +996,23 @@ bool cVNSIClient::processRecStream_GetIFrame() /* OPCODE 45 */
return true;
}
+bool cVNSIClient::processRecStream_GetLength() /* OPCODE 46 */
+{
+ uint64_t length = 0;
+
+ if (m_RecPlayer)
+ {
+ m_RecPlayer->reScan();
+ length = m_RecPlayer->getLengthBytes();
+ }
+
+ m_resp->add_U64(length);
+
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+
+ return true;
+}
/** OPCODE 60 - 79: VNSI network functions for channel access */
@@ -937,31 +1031,56 @@ bool cVNSIClient::processCHANNELS_ChannelsCount() /* OPCODE 61 */
bool cVNSIClient::processCHANNELS_GetChannels() /* OPCODE 63 */
{
- if (m_req->getDataLength() != 4) return false;
+ if (m_req->getDataLength() != 5) return false;
bool radio = m_req->extract_U32();
+ bool filter = m_req->extract_U8();
Channels.Lock(false);
+ cString caids;
+ int caid;
+ int caid_idx;
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
{
- if (radio != IsRadio(channel))
+ if (radio != cVNSIChannelFilter::IsRadio(channel))
continue;
// skip invalid channels
if (channel->Sid() == 0)
continue;
+ // check filter
+ if (filter && !VNSIChannelFilter.PassFilter(*channel))
+ continue;
+
+ uint32_t uuid = CreateChannelUID(channel);
m_resp->add_U32(channel->Number());
m_resp->add_String(m_toUTF8.Convert(channel->Name()));
- m_resp->add_U32(CreateChannelUID(channel));
- m_resp->add_U32(0); // groupindex unused
- m_resp->add_U32(channel->Ca());
-#if APIVERSNUM >= 10701
- m_resp->add_U32(channel->Vtype());
-#else
- m_resp->add_U32(2);
-#endif
+ m_resp->add_String(m_toUTF8.Convert(channel->Provider()));
+ m_resp->add_U32(uuid);
+ m_resp->add_U32(channel->Ca(0));
+ caid_idx = 0;
+ caids = "caids:";
+ while((caid = channel->Ca(caid_idx)) != 0)
+ {
+ caids = cString::sprintf("%s%d;", (const char*)caids, caid);
+ caid_idx++;
+ }
+ m_resp->add_String((const char*)caids);
+ if (m_protocolVersion >= 6)
+ {
+ m_resp->add_String(CreatePiconRef(channel));
+ }
+
+ // create entry in EPG map on first query
+ std::map<int, sEpgUpdate>::iterator it;
+ it = m_epgUpdate.find(uuid);
+ if (it == m_epgUpdate.end())
+ {
+ m_epgUpdate[uuid].lastEvent = 0;
+ m_epgUpdate[uuid].attempts = 0;
+ }
}
Channels.Unlock();
@@ -1024,6 +1143,7 @@ bool cVNSIClient::processCHANNELS_GetGroupMembers()
{
char* groupname = m_req->extract_String();
uint32_t radio = m_req->extract_U8();
+ bool filter = m_req->extract_U8();
int index = 0;
// unknown group
@@ -1057,7 +1177,11 @@ bool cVNSIClient::processCHANNELS_GetGroupMembers()
if(name.empty())
continue;
- if(IsRadio(channel) != radio)
+ if(cVNSIChannelFilter::IsRadio(channel) != radio)
+ continue;
+
+ // check filter
+ if (filter && !VNSIChannelFilter.PassFilter(*channel))
continue;
if(name == groupname)
@@ -1075,13 +1199,142 @@ bool cVNSIClient::processCHANNELS_GetGroupMembers()
return true;
}
+bool cVNSIClient::processCHANNELS_GetCaids()
+{
+ uint32_t uid = m_req->extract_U32();
+
+ Channels.Lock(false);
+ const cChannel *channel = NULL;
+ channel = FindChannelByUID(uid);
+ Channels.Unlock();
+
+ if (channel != NULL)
+ {
+ int caid;
+ int idx = 0;
+ while((caid = channel->Ca(idx)) != 0)
+ {
+ m_resp->add_U32(caid);
+ idx++;
+ }
+ }
+
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+
+ return true;
+}
+
+bool cVNSIClient::processCHANNELS_GetWhitelist()
+{
+ bool radio = m_req->extract_U8();
+ std::vector<cVNSIProvider> *providers;
+
+ if(radio)
+ providers = &VNSIChannelFilter.m_providersRadio;
+ else
+ providers = &VNSIChannelFilter.m_providersVideo;
+
+ VNSIChannelFilter.m_Mutex.Lock();
+ for(unsigned int i=0; i<providers->size(); i++)
+ {
+ m_resp->add_String((*providers)[i].m_name.c_str());
+ m_resp->add_U32((*providers)[i].m_caid);
+ }
+ VNSIChannelFilter.m_Mutex.Unlock();
+
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+ return true;
+}
+
+bool cVNSIClient::processCHANNELS_GetBlacklist()
+{
+ bool radio = m_req->extract_U8();
+ std::vector<int> *channels;
+
+ if(radio)
+ channels = &VNSIChannelFilter.m_channelsRadio;
+ else
+ channels = &VNSIChannelFilter.m_channelsVideo;
+
+ VNSIChannelFilter.m_Mutex.Lock();
+ for(unsigned int i=0; i<channels->size(); i++)
+ {
+ m_resp->add_U32((*channels)[i]);
+ }
+ VNSIChannelFilter.m_Mutex.Unlock();
+
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+ return true;
+}
+
+bool cVNSIClient::processCHANNELS_SetWhitelist()
+{
+ bool radio = m_req->extract_U8();
+ cVNSIProvider provider;
+ std::vector<cVNSIProvider> *providers;
+
+ if(radio)
+ providers = &VNSIChannelFilter.m_providersRadio;
+ else
+ providers = &VNSIChannelFilter.m_providersVideo;
+
+ VNSIChannelFilter.m_Mutex.Lock();
+ providers->clear();
+
+ while(!m_req->end())
+ {
+ char *str = m_req->extract_String();
+ provider.m_name = str;
+ provider.m_caid = m_req->extract_U32();
+ delete [] str;
+ providers->push_back(provider);
+ }
+ VNSIChannelFilter.StoreWhitelist(radio);
+ VNSIChannelFilter.m_Mutex.Unlock();
+
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+ return true;
+}
+
+bool cVNSIClient::processCHANNELS_SetBlacklist()
+{
+ bool radio = m_req->extract_U8();
+ cVNSIProvider provider;
+ std::vector<int> *channels;
+
+ if(radio)
+ channels = &VNSIChannelFilter.m_channelsRadio;
+ else
+ channels = &VNSIChannelFilter.m_channelsVideo;
+
+ VNSIChannelFilter.m_Mutex.Lock();
+ channels->clear();
+
+ int id;
+ while(!m_req->end())
+ {
+ id = m_req->extract_U32();
+ channels->push_back(id);
+ }
+ VNSIChannelFilter.StoreBlacklist(radio);
+ VNSIChannelFilter.m_Mutex.Unlock();
+
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+ return true;
+}
+
void cVNSIClient::CreateChannelGroups(bool automatic)
{
std::string groupname;
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
{
- bool isRadio = IsRadio(channel);
+ bool isRadio = cVNSIChannelFilter::IsRadio(channel);
if(automatic && !channel->GroupSep())
groupname = channel->Provider();
@@ -1533,7 +1786,7 @@ bool cVNSIClient::processRECORDINGS_GetList() /* OPCODE 102 */
m_resp->add_String((isempty(directory)) ? "" : m_toUTF8.Convert(directory));
// filename / uid of recording
- uint32_t uid = cRecordingsCache::GetInstance().Register(recording);
+ uint32_t uid = cRecordingsCache::GetInstance().Register(recording, false);
m_resp->add_U32(uid);
free(fullname);
@@ -1631,6 +1884,37 @@ bool cVNSIClient::processRECORDINGS_Delete() /* OPCODE 104 */
return true;
}
+bool cVNSIClient::processRECORDINGS_GetEdl() /* OPCODE 105 */
+{
+ cString recName;
+ cRecording* recording = NULL;
+
+ uint32_t uid = m_req->extract_U32();
+ recording = cRecordingsCache::GetInstance().Lookup(uid);
+
+ if (recording)
+ {
+ cMarks marks;
+ if(marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording()))
+ {
+#if VDRVERSNUM >= 10732
+ cMark* mark = NULL;
+ double fps = recording->FramesPerSecond();
+ while((mark = marks.GetNextBegin(mark)) != NULL)
+ {
+ m_resp->add_U64(mark->Position() *1000 / fps);
+ m_resp->add_U64(mark->Position() *1000 / fps);
+ m_resp->add_S32(2);
+ }
+#endif
+ }
+ }
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+
+ return true;
+}
+
/** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
@@ -1666,7 +1950,6 @@ bool cVNSIClient::processEPG_GetForChannel() /* OPCODE 120 */
cSchedulesLock MutexLock;
const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
- m_epgUpdate[channelUID] = 0;
if (!Schedules)
{
m_resp->add_U32(0);
@@ -1761,7 +2044,8 @@ bool cVNSIClient::processEPG_GetForChannel() /* OPCODE 120 */
cEvent *lastEvent = Schedule->Events()->Last();
if (lastEvent)
{
- m_epgUpdate[channelUID] = lastEvent->StartTime();
+ m_epgUpdate[channelUID].lastEvent = lastEvent->StartTime();
+ m_epgUpdate[channelUID].attempts = 0;
}
DEBUGLOG("written schedules packet");
@@ -2056,3 +2340,378 @@ bool cVNSIClient::processOSD_Hitkey() /* OPCODE 162 */
}
return true;
}
+
+/** OPCODE 180 - 189: VNSI network functions for deleted recording access */
+
+bool cVNSIClient::processRECORDINGS_DELETED_Supported() /* OPCODE 180 */
+{
+ m_resp->add_U32(VNSI_RET_OK);
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+ return true;
+}
+
+bool cVNSIClient::processRECORDINGS_DELETED_GetCount() /* OPCODE 181 */
+{
+ DeletedRecordings.Load();
+ m_resp->add_U32(DeletedRecordings.Count());
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+ return true;
+}
+
+bool cVNSIClient::processRECORDINGS_DELETED_GetList() /* OPCODE 182 */
+{
+ cMutexLock lock(&m_timerLock);
+ cThreadLock RecordingsLock(&Recordings);
+
+ for (cRecording *recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording))
+ {
+#if APIVERSNUM >= 10705
+ const cEvent *event = recording->Info()->GetEvent();
+#else
+ const cEvent *event = NULL;
+#endif
+
+ time_t recordingStart = 0;
+ int recordingDuration = 0;
+ if (event)
+ {
+ recordingStart = event->StartTime();
+ recordingDuration = event->Duration();
+ }
+ else
+ {
+ cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
+ if (rc)
+ {
+ recordingStart = rc->Timer()->StartTime();
+ recordingDuration = rc->Timer()->StopTime() - recordingStart;
+ }
+ else
+ {
+#if APIVERSNUM >= 10727
+ recordingStart = recording->Start();
+#else
+ recordingStart = recording->start;
+#endif
+ }
+ }
+ DEBUGLOG("GRI: RC: recordingStart=%lu recordingDuration=%i", recordingStart, recordingDuration);
+
+ // recording_time
+ m_resp->add_U32(recordingStart);
+
+ // duration
+ m_resp->add_U32(recordingDuration);
+
+ // priority
+#if APIVERSNUM >= 10727
+ m_resp->add_U32(recording->Priority());
+#else
+ m_resp->add_U32(recording->priority);
+#endif
+
+ // lifetime
+#if APIVERSNUM >= 10727
+ m_resp->add_U32(recording->Lifetime());
+#else
+ m_resp->add_U32(recording->lifetime);
+#endif
+
+ // channel_name
+ m_resp->add_String(recording->Info()->ChannelName() ? m_toUTF8.Convert(recording->Info()->ChannelName()) : "");
+
+ char* fullname = strdup(recording->Name());
+ char* recname = strrchr(fullname, FOLDERDELIMCHAR);
+ char* directory = NULL;
+
+ if(recname == NULL) {
+ recname = fullname;
+ }
+ else {
+ *recname = 0;
+ recname++;
+ directory = fullname;
+ }
+
+ // title
+ m_resp->add_String(m_toUTF8.Convert(recname));
+
+ // subtitle
+ if (!isempty(recording->Info()->ShortText()))
+ m_resp->add_String(m_toUTF8.Convert(recording->Info()->ShortText()));
+ else
+ m_resp->add_String("");
+
+ // description
+ if (!isempty(recording->Info()->Description()))
+ m_resp->add_String(m_toUTF8.Convert(recording->Info()->Description()));
+ else
+ m_resp->add_String("");
+
+ // directory
+ if(directory != NULL) {
+ char* p = directory;
+ while(*p != 0) {
+ if(*p == FOLDERDELIMCHAR) *p = '/';
+ if(*p == '_') *p = ' ';
+ p++;
+ }
+ while(*directory == '/') directory++;
+ }
+
+ m_resp->add_String((isempty(directory)) ? "" : m_toUTF8.Convert(directory));
+
+ // filename / uid of recording
+ uint32_t uid = cRecordingsCache::GetInstance().Register(recording, false);
+ m_resp->add_U32(uid);
+
+ free(fullname);
+ }
+
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+ return true;
+}
+
+bool cVNSIClient::processRECORDINGS_DELETED_Delete() /* OPCODE 183 */
+{
+ cString recName;
+ cRecording* recording = NULL;
+
+#if VDRVERSNUM >= 20102
+ cLockFile LockFile(cVideoDirectory::Name());
+#else
+ cLockFile LockFile(VideoDirectory);
+#endif
+ if (LockFile.Lock())
+ {
+ uint32_t uid = m_req->extract_U32();
+
+ cThreadLock DeletedRecordingsLock(&DeletedRecordings);
+
+ for (recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording))
+ {
+ if (uid == CreateStringHash(recording->FileName()))
+ {
+#if VDRVERSNUM >= 20102
+ if (!cVideoDirectory::RemoveVideoFile(recording->FileName()))
+#else
+ if (!RemoveVideoFile(recording->FileName()))
+#endif
+ {
+ ERRORLOG("Error while remove deleted recording (%s)", recording->FileName());
+ m_resp->add_U32(VNSI_RET_ERROR);
+ }
+ else
+ {
+ DeletedRecordings.Del(recording);
+ DeletedRecordings.Update();
+ INFOLOG("Recording \"%s\" permanent deleted", recording->FileName());
+ m_resp->add_U32(VNSI_RET_OK);
+ }
+ break;
+ }
+ }
+ }
+
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+
+ return true;
+}
+
+bool cVNSIClient::Undelete(cRecording* recording)
+{
+ DEBUGLOG("undelete recording: %s", recording->Name());
+
+ char *NewName = strdup(recording->FileName());
+ char *ext = strrchr(NewName, '.');
+ if (ext && strcmp(ext, ".del") == 0)
+ {
+ strncpy(ext, ".rec", strlen(ext));
+ if (!access(NewName, F_OK))
+ {
+ ERRORLOG("Recording with the same name exists (%s)", NewName);
+ OsdStatusMessage(*cString::sprintf("%s (%s)", tr("Recording with the same name exists"), NewName));
+ }
+ else
+ {
+ if (access(recording->FileName(), F_OK) == 0)
+ {
+#if VDRVERSNUM >= 20102
+ if (!cVideoDirectory::RenameVideoFile(recording->FileName(), NewName))
+#else
+ if (!RenameVideoFile(recording->FileName(), NewName))
+#endif
+ {
+ ERRORLOG("Error while rename deleted recording (%s) to (%s)", recording->FileName(), NewName);
+ }
+
+ cIndexFile *index = new cIndexFile(NewName, false, recording->IsPesRecording());
+ int LastFrame = index->Last() - 1;
+ if (LastFrame > 0)
+ {
+ uint16_t FileNumber = 0;
+ off_t FileOffset = 0;
+ index->Get(LastFrame, &FileNumber, &FileOffset);
+ delete index;
+ if (FileNumber == 0)
+ {
+ ERRORLOG("while read last filenumber (%s)", NewName);
+ OsdStatusMessage(*cString::sprintf("%s (%s)", tr("Error while read last filenumber"), NewName));
+ }
+ else
+ {
+ for (int i = 1; i <= FileNumber; i++)
+ {
+ cString temp = cString::sprintf(recording->IsPesRecording() ? "%s/%03d.vdr" : "%s/%05d.ts", (const char *)NewName, i);
+ if (access(*temp, R_OK) != 0)
+ {
+ i = FileNumber;
+ OsdStatusMessage(*cString::sprintf("%s %03d (%s)", tr("Error while accessing vdrfile"), i, NewName));
+ }
+ }
+ }
+ }
+ else
+ {
+ delete index;
+ ERRORLOG("accessing indexfile (%s)", NewName);
+ OsdStatusMessage(*cString::sprintf("%s (%s)", tr("Error while accessing indexfile"), NewName));
+ }
+
+ DeletedRecordings.Del(recording);
+ Recordings.Update();
+ DeletedRecordings.Update();
+ }
+ else
+ {
+ ERRORLOG("deleted recording '%s' vanished", recording->FileName());
+ OsdStatusMessage(*cString::sprintf("%s \"%s\"", tr("Deleted recording vanished"), recording->FileName()));
+ }
+ }
+ }
+ free(NewName);
+ return true;
+}
+
+bool cVNSIClient::processRECORDINGS_DELETED_Undelete() /* OPCODE 184 */
+{
+ int ret = VNSI_RET_DATAUNKNOWN;
+
+#if VDRVERSNUM >= 20102
+ cLockFile LockFile(cVideoDirectory::Name());
+#else
+ cLockFile LockFile(VideoDirectory);
+#endif
+ if (LockFile.Lock())
+ {
+ uint32_t uid = m_req->extract_U32();
+
+ cThreadLock DeletedRecordingsLock(&DeletedRecordings);
+
+ for (cRecording* recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording))
+ {
+ if (uid == CreateStringHash(recording->FileName()))
+ {
+ if (Undelete(recording))
+ {
+ INFOLOG("Recording \"%s\" undeleted", recording->FileName());
+ ret = VNSI_RET_OK;
+ }
+ else
+ ret = VNSI_RET_ERROR;
+ break;
+ }
+ }
+ }
+
+ m_resp->add_U32(ret);
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+ return true;
+}
+
+bool cVNSIClient::processRECORDINGS_DELETED_DeleteAll() /* OPCODE 185 */
+{
+ int ret = VNSI_RET_OK;
+
+#if VDRVERSNUM >= 20102
+ cLockFile LockFile(cVideoDirectory::Name());
+#else
+ cLockFile LockFile(VideoDirectory);
+#endif
+
+ if (LockFile.Lock())
+ {
+ cThreadLock DeletedRecordingsLock(&DeletedRecordings);
+
+ for (cRecording *recording = DeletedRecordings.First(); recording; )
+ {
+ cRecording *next = DeletedRecordings.Next(recording);
+#if VDRVERSNUM >= 20102
+ if (!cVideoDirectory::RemoveVideoFile(recording->FileName()))
+#else
+ if (!RemoveVideoFile(recording->FileName()))
+#endif
+ {
+ ERRORLOG("Error while remove deleted recording (%s)", recording->FileName());
+ ret = VNSI_RET_ERROR;
+ break;
+ }
+ else
+ INFOLOG("Recording \"%s\" permanent deleted", recording->FileName());
+ recording = next;
+ }
+ DeletedRecordings.Clear();
+ DeletedRecordings.Update();
+ }
+
+ m_resp->add_U32(ret);
+ m_resp->finalise();
+ m_socket.write(m_resp->getPtr(), m_resp->getLen());
+
+ return true;
+}
+
+// this method is taken from XVDR
+cString cVNSIClient::CreatePiconRef(cChannel* channel)
+{
+ int hash = 0;
+
+ if(cSource::IsSat(channel->Source()))
+ {
+ hash = channel->Source() & cSource::st_Pos;
+
+#if VDRVERSNUM >= 20101
+ hash = -hash;
+#endif
+
+ if(hash > 0x00007FFF)
+ hash |= 0xFFFF0000;
+
+ if(hash < 0)
+ hash = -hash;
+ else
+ hash = 1800 + hash;
+
+ hash = hash << 16;
+ }
+ else if(cSource::IsCable(channel->Source()))
+ hash = 0xFFFF0000;
+ else if(cSource::IsTerr(channel->Source()))
+ hash = 0xEEEE0000;
+ else if(cSource::IsAtsc(channel->Source()))
+ hash = 0xDDDD0000;
+
+ cString serviceref = cString::sprintf("1_0_%i_%X_%X_%X_%X_0_0_0",
+ cVNSIChannelFilter::IsRadio(channel) ? 2 : (channel->Vtype() == 27) ? 19 : 1,
+ channel->Sid(),
+ channel->Tid(),
+ channel->Nid(),
+ hash);
+
+ return serviceref;
+}
diff --git a/vnsiclient.h b/vnsiclient.h
index 948940a..efbd7de 100644
--- a/vnsiclient.h
+++ b/vnsiclient.h
@@ -17,9 +17,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -57,6 +56,7 @@ private:
bool m_StatusInterfaceEnabled;
cLiveStreamer *m_Streamer;
bool m_isStreaming;
+ bool m_bSupportRDS;
cString m_ClientAddress;
cRecPlayer *m_RecPlayer;
cRequestPacket *m_req;
@@ -66,7 +66,12 @@ private:
cMutex m_msgLock;
static cMutex m_timerLock;
cVnsiOsdProvider *m_Osd;
- std::map<int, time_t> m_epgUpdate;
+ typedef struct
+ {
+ int attempts;
+ time_t lastEvent;
+ } sEpgUpdate;
+ std::map<int, sEpgUpdate> m_epgUpdate;
protected:
@@ -77,13 +82,14 @@ protected:
virtual void TimerChange(const cTimer *Timer, eTimerChange Change);
virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On);
virtual void OsdStatusMessage(const char *Message);
+ virtual void ChannelChange(const cChannel *Channel);
public:
cVNSIClient(int fd, unsigned int id, const char *ClientAdr);
virtual ~cVNSIClient();
- void ChannelChange();
+ void ChannelsChange();
void RecordingsChange();
void TimerChange();
void EpgChange();
@@ -124,12 +130,18 @@ private:
bool processRecStream_PositionFromFrameNumber();
bool processRecStream_FrameNumberFromPosition();
bool processRecStream_GetIFrame();
+ bool processRecStream_GetLength();
bool processCHANNELS_GroupsCount();
bool processCHANNELS_ChannelsCount();
bool processCHANNELS_GroupList();
bool processCHANNELS_GetChannels();
bool processCHANNELS_GetGroupMembers();
+ bool processCHANNELS_GetCaids();
+ bool processCHANNELS_GetWhitelist();
+ bool processCHANNELS_GetBlacklist();
+ bool processCHANNELS_SetWhitelist();
+ bool processCHANNELS_SetBlacklist();
void CreateChannelGroups(bool automatic);
@@ -147,6 +159,13 @@ private:
bool processRECORDINGS_Rename();
bool processRECORDINGS_Delete();
bool processRECORDINGS_Move();
+ bool processRECORDINGS_GetEdl();
+ bool processRECORDINGS_DELETED_Supported();
+ bool processRECORDINGS_DELETED_GetCount();
+ bool processRECORDINGS_DELETED_GetList();
+ bool processRECORDINGS_DELETED_Delete();
+ bool processRECORDINGS_DELETED_Undelete();
+ bool processRECORDINGS_DELETED_DeleteAll();
bool processEPG_GetForChannel();
@@ -156,6 +175,8 @@ private:
bool processSCAN_Start();
bool processSCAN_Stop();
+ bool Undelete(cRecording* recording);
+
/** Static callback functions to interact with wirbelscan plugin over
the plugin service interface */
static void processSCAN_AddCountry(int index, const char *isoName, const char *longName);
@@ -173,6 +194,8 @@ private:
bool processOSD_Connect();
bool processOSD_Disconnect();
bool processOSD_Hitkey();
+
+ cString CreatePiconRef(cChannel* channel);
};
#endif // VNSI_CLIENT_H
diff --git a/vnsicommand.h b/vnsicommand.h
index 17d3d08..9889d82 100644
--- a/vnsicommand.h
+++ b/vnsicommand.h
@@ -17,9 +17,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -27,7 +26,13 @@
#define VNSI_COMMAND_H
/** Current VNSI Protocol Version number */
-#define VNSI_PROTOCOLVERSION 4
+#define VNSI_PROTOCOLVERSION 8
+
+/** Start of RDS support protocol Version */
+#define VNSI_RDS_PROTOCOLVERSION 8
+
+/** Minimum VNSI Protocol Version number */
+#define VNSI_MIN_PROTOCOLVERSION 5
/** Packet types */
#define VNSI_CHANNEL_REQUEST_RESPONSE 1
@@ -45,6 +50,8 @@
#define CONFNAME_TIMESHIFTBUFFERSIZE "TimeshiftBufferSize"
#define CONFNAME_TIMESHIFTBUFFERFILESIZE "TimeshiftBufferFileSize"
#define CONFNAME_TIMESHIFTBUFFERDIR "TimeshiftBufferDir"
+#define CONFNAME_PLAYRECORDING "PlayRecording"
+#define CONFNAME_AVOIDEPGSCAN "AvoidEPGScan"
/* OPCODE 1 - 19: VNSI network functions for general purpose */
#define VNSI_LOGIN 1
@@ -66,6 +73,7 @@
#define VNSI_RECSTREAM_POSTOFRAME 43
#define VNSI_RECSTREAM_FRAMETOPOS 44
#define VNSI_RECSTREAM_GETIFRAME 45
+#define VNSI_RECSTREAM_GETLENGTH 46
/* OPCODE 60 - 79: VNSI network functions for channel access */
#define VNSI_CHANNELS_GETCOUNT 61
@@ -73,6 +81,11 @@
#define VNSI_CHANNELGROUP_GETCOUNT 65
#define VNSI_CHANNELGROUP_LIST 66
#define VNSI_CHANNELGROUP_MEMBERS 67
+#define VNSI_CHANNELS_GETCAIDS 68
+#define VNSI_CHANNELS_GETWHITELIST 69
+#define VNSI_CHANNELS_GETBLACKLIST 70
+#define VNSI_CHANNELS_SETWHITELIST 71
+#define VNSI_CHANNELS_SETBLACKLIST 72
/* OPCODE 80 - 99: VNSI network functions for timer access */
#define VNSI_TIMER_GETCOUNT 80
@@ -88,6 +101,7 @@
#define VNSI_RECORDINGS_GETLIST 102
#define VNSI_RECORDINGS_RENAME 103
#define VNSI_RECORDINGS_DELETE 104
+#define VNSI_RECORDINGS_GETEDL 105
/* OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
#define VNSI_EPG_GETFORCHANNEL 120
@@ -104,6 +118,14 @@
#define VNSI_OSD_DISCONNECT 161
#define VNSI_OSD_HITKEY 162
+/* OPCODE 180 - 189: VNSI network functions for deleted recording access */
+#define VNSI_RECORDINGS_DELETED_ACCESS_SUPPORTED 180
+#define VNSI_RECORDINGS_DELETED_GETCOUNT 181
+#define VNSI_RECORDINGS_DELETED_GETLIST 182
+#define VNSI_RECORDINGS_DELETED_DELETE 183
+#define VNSI_RECORDINGS_DELETED_UNDELETE 184
+#define VNSI_RECORDINGS_DELETED_DELETE_ALL 185
+
/** Stream packet types (server -> client) */
#define VNSI_STREAM_CHANGE 1
#define VNSI_STREAM_STATUS 2
@@ -112,6 +134,7 @@
#define VNSI_STREAM_SIGNALINFO 5
#define VNSI_STREAM_CONTENTINFO 6
#define VNSI_STREAM_BUFFERSTATS 7
+#define VNSI_STREAM_REFTIME 8
/** Scan packet types (server -> client) */
#define VNSI_SCANNER_PERCENTAGE 1
diff --git a/vnsiserver.c b/vnsiserver.c
index db2e033..2919308 100644
--- a/vnsiserver.c
+++ b/vnsiserver.c
@@ -17,9 +17,8 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
@@ -39,12 +38,11 @@
#include <sys/stat.h>
#include <vdr/plugin.h>
-#include <vdr/shutdown.h>
-#include <vdr/videodir.h>
#include "vnsi.h"
#include "vnsiserver.h"
#include "vnsiclient.h"
+#include "channelfilter.h"
unsigned int cVNSIServer::m_IdCnt = 0;
@@ -74,41 +72,6 @@ cVNSIServer::cVNSIServer(int listenPort) : cThread("VDR VNSI Server")
{
m_ServerPort = listenPort;
- if(*VNSIServerConfig.ConfigDirectory)
- {
- m_AllowedHostsFile = cString::sprintf("%s/" ALLOWED_HOSTS_FILE, *VNSIServerConfig.ConfigDirectory);
- }
- else
- {
- ERRORLOG("cVNSIServer: missing ConfigDirectory!");
- m_AllowedHostsFile = cString::sprintf("/video/" ALLOWED_HOSTS_FILE);
- }
-
- m_ServerFD = socket(AF_INET, SOCK_STREAM, 0);
- if(m_ServerFD == -1)
- return;
-
- fcntl(m_ServerFD, F_SETFD, fcntl(m_ServerFD, F_GETFD) | FD_CLOEXEC);
-
- int one = 1;
- setsockopt(m_ServerFD, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
-
- struct sockaddr_in s;
- memset(&s, 0, sizeof(s));
- s.sin_family = AF_INET;
- s.sin_port = htons(m_ServerPort);
-
- int x = bind(m_ServerFD, (struct sockaddr *)&s, sizeof(s));
- if (x < 0)
- {
- close(m_ServerFD);
- INFOLOG("Unable to start VNSI Server, port already in use ?");
- m_ServerFD = -1;
- return;
- }
-
- listen(m_ServerFD, 10);
-
Start();
INFOLOG("VNSI Server started");
@@ -118,12 +81,7 @@ cVNSIServer::cVNSIServer(int listenPort) : cThread("VDR VNSI Server")
cVNSIServer::~cVNSIServer()
{
- Cancel(-1);
- for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
- {
- delete (*i);
- }
- m_clients.erase(m_clients.begin(), m_clients.end());
+ m_Status.Shutdown();
Cancel();
INFOLOG("VNSI Server stopped");
}
@@ -175,7 +133,7 @@ void cVNSIServer::NewClientConnected(int fd)
INFOLOG("Client with ID %d connected: %s", m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf));
cVNSIClient *connection = new cVNSIClient(fd, m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf));
- m_clients.push_back(connection);
+ m_Status.AddClient(connection);
m_IdCnt++;
}
@@ -184,42 +142,44 @@ void cVNSIServer::Action(void)
fd_set fds;
struct timeval tv;
- // initial time for channels change
- struct timespec channelsUpdate;
- channelsUpdate.tv_sec = 0;
- channelsUpdate.tv_nsec = 0;
- cTimeMs chanTimer(0);
+ if(*VNSIServerConfig.ConfigDirectory)
+ {
+ m_AllowedHostsFile = cString::sprintf("%s/" ALLOWED_HOSTS_FILE, *VNSIServerConfig.ConfigDirectory);
+ }
+ else
+ {
+ ERRORLOG("cVNSIServer: missing ConfigDirectory!");
+ m_AllowedHostsFile = cString::sprintf("/video/" ALLOWED_HOSTS_FILE);
+ }
- // get initial state of the recordings
- int recState = -1;
- Recordings.StateChanged(recState);
+ VNSIChannelFilter.Load();
+ VNSIChannelFilter.SortChannels();
+ m_Status.Start();
- // get initial state of the timers
- int timerState = -1;
- Timers.Modified(timerState);
+ m_ServerFD = socket(AF_INET, SOCK_STREAM, 0);
+ if(m_ServerFD == -1)
+ return;
- // last update of epg
- time_t epgUpdate = cSchedules::Modified();
+ fcntl(m_ServerFD, F_SETFD, fcntl(m_ServerFD, F_GETFD) | FD_CLOEXEC);
- // delete old timeshift file
- cString cmd;
- struct stat sb;
- if ((*TimeshiftBufferDir) && stat(TimeshiftBufferDir, &sb) == 0 && S_ISDIR(sb.st_mode))
- {
- if (TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/')
- cmd = cString::sprintf("rm -f %s*.vnsi", TimeshiftBufferDir);
- else
- cmd = cString::sprintf("rm -f %s/*.vnsi", TimeshiftBufferDir);
- }
- else
+ int one = 1;
+ setsockopt(m_ServerFD, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
+
+ struct sockaddr_in s;
+ memset(&s, 0, sizeof(s));
+ s.sin_family = AF_INET;
+ s.sin_port = htons(m_ServerPort);
+
+ int x = bind(m_ServerFD, (struct sockaddr *)&s, sizeof(s));
+ if (x < 0)
{
-#if VDRVERSNUM >= 20102
- cmd = cString::sprintf("rm -f %s/*.vnsi", cVideoDirectory::Name());
-#else
- cmd = cString::sprintf("rm -f %s/*.vnsi", VideoDirectory);
-#endif
+ close(m_ServerFD);
+ INFOLOG("Unable to start VNSI Server, port already in use ?");
+ m_ServerFD = -1;
+ return;
}
- int ret = system(cmd);
+
+ listen(m_ServerFD, 10);
while (Running())
{
@@ -237,72 +197,6 @@ void cVNSIServer::Action(void)
}
if (r == 0)
{
- // remove disconnected clients
- for (ClientList::iterator i = m_clients.begin(); i != m_clients.end();)
- {
- if (!(*i)->Active())
- {
- INFOLOG("Client with ID %u seems to be disconnected, removing from client list", (*i)->GetID());
- delete (*i);
- i = m_clients.erase(i);
- }
- else {
- i++;
- }
- }
-
- // trigger clients to reload the modified channel list
- if(m_clients.size() > 0 && chanTimer.TimedOut())
- {
- struct stat s;
- if(stat(Channels.FileName(), &s) != -1)
- {
- if ((s.st_mtim.tv_sec != channelsUpdate.tv_sec) &&
- (s.st_mtim.tv_nsec != channelsUpdate.tv_nsec))
- {
- INFOLOG("Requesting clients to reload channel list");
- for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
- (*i)->ChannelChange();
- channelsUpdate = s.st_mtim;
- }
- }
- chanTimer.Set(5000);
- }
-
- // reset inactivity timeout as long as there are clients connected
- if(m_clients.size() > 0) {
- ShutdownHandler.SetUserInactiveTimeout();
- }
-
- // update recordings
- if(Recordings.StateChanged(recState))
- {
- INFOLOG("Recordings state changed (%i)", recState);
- INFOLOG("Requesting clients to reload recordings list");
- for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
- (*i)->RecordingsChange();
- }
-
- // update timers
- if(Timers.Modified(timerState))
- {
- INFOLOG("Timers state changed (%i)", timerState);
- INFOLOG("Requesting clients to reload timers");
- for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
- {
- (*i)->TimerChange();
- }
- }
-
- // update epg
- if((cSchedules::Modified() > epgUpdate + 10) || time(NULL) > epgUpdate + 300)
- {
- for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
- {
- (*i)->EpgChange();
- }
- epgUpdate = cSchedules::Modified();
- }
continue;
}
diff --git a/vnsiserver.h b/vnsiserver.h
index bf52b41..472d0bb 100644
--- a/vnsiserver.h
+++ b/vnsiserver.h
@@ -17,19 +17,18 @@
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
*/
#ifndef VNSI_SERVER_H
#define VNSI_SERVER_H
-#include <list>
#include <vdr/thread.h>
#include "config.h"
+#include "status.h"
class cVNSIClient;
@@ -37,15 +36,13 @@ class cVNSIServer : public cThread
{
protected:
- typedef std::list<cVNSIClient*> ClientList;
-
virtual void Action(void);
void NewClientConnected(int fd);
int m_ServerPort;
int m_ServerFD;
cString m_AllowedHostsFile;
- ClientList m_clients;
+ cVNSIStatus m_Status;
static unsigned int m_IdCnt;
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-vdr-dvb/vdr-plugin-vnsiserver.git
More information about the pkg-vdr-dvb-changes
mailing list