[vdr-plugin-xvdr] 01/03: Imported Upstream version 0.9.9+git20150304
Tobias Grimm
tiber-guest at moszumanska.debian.org
Wed Mar 4 19:40: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-xvdr.
commit cc97566c6fce4ada09162bd6f8503780cd0e474d
Author: etobi <git at e-tobi.net>
Date: Wed Mar 4 20:37:17 2015 +0100
Imported Upstream version 0.9.9+git20150304
---
.gitignore | 3 +
Makefile | 8 +-
debian/install | 1 -
gentoo/media-plugins/vdr-plugin-xvdr/Manifest | 1 -
.../vdr-plugin-xvdr/vdr-plugin-xvdr-9999.ebuild | 43 --
gentoo/media-plugins/vdr-xvdr/ChangeLog | 29 ++
gentoo/media-plugins/vdr-xvdr/Manifest | 2 +
gentoo/media-plugins/vdr-xvdr/vdr-xvdr-9999.ebuild | 65 +++
po/.gitignore | 0
po/de_AT.po | 31 ++
po/de_DE.po | 31 ++
src/config/config.c | 4 +
src/demuxer/demuxer.c | 1 -
src/demuxer/demuxer.h | 2 +
src/demuxer/demuxer_H264.c | 111 ++++--
src/demuxer/demuxer_H264.h | 4 +
src/demuxer/demuxer_LATM.c | 2 +-
src/demuxer/demuxer_MPEGVideo.c | 33 +-
src/demuxer/demuxer_MPEGVideo.h | 3 +-
src/demuxer/parser.c | 12 +-
src/demuxer/parser.h | 1 +
src/demuxer/streaminfo.c | 110 +++---
src/demuxer/streaminfo.h | 23 +-
src/live/channelcache.c | 168 +++++---
src/live/channelcache.h | 9 +-
src/live/livepatfilter.c | 46 ++-
src/live/livepatfilter.h | 4 +-
src/live/livequeue.c | 26 +-
src/live/livequeue.h | 9 +-
src/live/livestreamer.c | 292 ++++++++++----
src/live/livestreamer.h | 21 +-
src/net/socketlock.c | 12 -
src/net/socketlock.h | 30 --
src/recordings/recordingscache.c | 59 ++-
src/recordings/recordingscache.h | 6 +-
src/recordings/recplayer.c | 138 ++-----
src/recordings/recplayer.h | 45 ++-
src/tools/hash.c | 27 ++
src/tools/hash.h | 6 +-
src/xvdr/timerconflicts.c | 120 ++++++
src/{tools/hash.h => xvdr/timerconflicts.h} | 19 +-
src/xvdr/xvdrclient.c | 437 ++++++++++-----------
src/xvdr/xvdrclient.h | 24 +-
src/xvdr/xvdrcommand.h | 4 +-
src/xvdr/xvdrserver.c | 45 ++-
tools/serviceref.c | 9 +-
46 files changed, 1314 insertions(+), 762 deletions(-)
diff --git a/.gitignore b/.gitignore
index 027623e..01d4392 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,6 @@
.dependencies
*.so
libvdr-vnsiserver.so*
+po/*.mo
+tools/serviceref
+po/xvdr.pot
diff --git a/Makefile b/Makefile
index 72833ae..da54323 100644
--- a/Makefile
+++ b/Makefile
@@ -48,7 +48,7 @@ SOFILE = libvdr-$(PLUGIN).so
### Includes and Defines (add further entries here):
-INCLUDES += -I./src
+INCLUDES += -I./src -I./src/vdr
ifdef DEBUG
INCLUDES += -DDEBUG
@@ -78,11 +78,11 @@ OBJS = \
src/live/livestreamer.o \
src/net/msgpacket.o \
src/net/os-config.o \
- src/net/socketlock.o \
src/recordings/recordingscache.o \
src/recordings/recplayer.o \
src/scanner/wirbelscan.o \
src/tools/hash.o \
+ src/xvdr/timerconflicts.o \
src/xvdr/xvdr.o \
src/xvdr/xvdrclient.o \
src/xvdr/xvdrserver.o \
@@ -95,7 +95,7 @@ all: $(SOFILE) i18n
### Implicit rules:
%.o: %.c
- $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
+ $(CXX) $(CXXFLAGS) -fPIC -c $(DEFINES) $(INCLUDES) -o $@ $<
### Dependencies:
@@ -117,7 +117,7 @@ I18Npot = $(PODIR)/$(PLUGIN).pot
%.mo: %.po
msgfmt -c -o $@ $<
-$(I18Npot): $(wildcard *.c)
+$(I18Npot): $(wildcard src/*/*.c)
xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ `find ./src -name *.c`
%.po: $(I18Npot)
diff --git a/debian/install b/debian/install
index fd616ed..647121d 100644
--- a/debian/install
+++ b/debian/install
@@ -1,3 +1,2 @@
-libvdr-xvdr.so.* usr/lib/vdr/plugins/
debian/plugin.xvdr.conf etc/vdr/plugins/
xvdr/allowed_hosts.conf var/lib/vdr/plugins/xvdr/
diff --git a/gentoo/media-plugins/vdr-plugin-xvdr/Manifest b/gentoo/media-plugins/vdr-plugin-xvdr/Manifest
deleted file mode 100644
index db34762..0000000
--- a/gentoo/media-plugins/vdr-plugin-xvdr/Manifest
+++ /dev/null
@@ -1 +0,0 @@
-EBUILD vdr-plugin-xvdr-9999.ebuild 782 RMD160 48a26bc63bafb4c475bc1f2e9ac65cae506a56d8 SHA1 4ff709850e61018713cbd593bf245d1ac8fe7551 SHA256 9c53e7868c8a5f335ecfd463b9ada92e8444cff70f35a5b2270c35f7600ec882
diff --git a/gentoo/media-plugins/vdr-plugin-xvdr/vdr-plugin-xvdr-9999.ebuild b/gentoo/media-plugins/vdr-plugin-xvdr/vdr-plugin-xvdr-9999.ebuild
deleted file mode 100644
index fa32f60..0000000
--- a/gentoo/media-plugins/vdr-plugin-xvdr/vdr-plugin-xvdr-9999.ebuild
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 1999-2010 Gentoo Foundation
-# Distributed under the terms of the GNU General Public License v2
-# $Header: $
-
-EAPI=2
-
-RESTRICT="strip"
-
-inherit vdr-plugin git-2
-
-EGIT_REPO_URI="git://github.com/pipelka/vdr-plugin-xvdr.git"
-PATCHES=""
-
-SRC_URI=""
-S=${WORKDIR}/${PN}
-
-DESCRIPTION="VDR plugin: XVDR Streamserver Plugin"
-HOMEPAGE="https://github.com/pipelka/vdr-plugin-xvdr"
-
-LICENSE="GPL-2"
-SLOT="0"
-
-KEYWORDS="~x86 ~amd64"
-IUSE=""
-
-DEPEND=">=media-video/vdr-1.6"
-RDEPEND="${DEPEND}"
-
-src_prepare() {
- vdr-plugin_src_prepare
-
- fix_vdr_libsi_include recplayer.c
- fix_vdr_libsi_include receiver.c
-}
-
-src_install() {
- vdr-plugin_src_install
-
- insinto /etc/vdr/plugins/xvdr
- doins xvdr/allowed_hosts.conf
- diropts -gvdr -ovdr
-}
-
diff --git a/gentoo/media-plugins/vdr-xvdr/ChangeLog b/gentoo/media-plugins/vdr-xvdr/ChangeLog
new file mode 100644
index 0000000..a575d82
--- /dev/null
+++ b/gentoo/media-plugins/vdr-xvdr/ChangeLog
@@ -0,0 +1,29 @@
+# ChangeLog for media-plugins/vdr-xvdr
+# Copyright 1999-2013 Gentoo Foundation; Distributed under the GPL v2
+# $Header: $
+
+ 29 Mar 2013; Torsten Kurbad <gentoo at tk-webart.de> vdr-xvdr-9999.ebuild:
+ Added debug use flag; Added tools use flag for optional installation of
+ serviceref tool
+
+ 16 Jan 2013; Joerg Bornkessel <hd_brummy at gentoo.org> vdr-xvdr-9999.ebuild:
+ eapi-5, fixed libsi includes #451522
+
+ 01 May 2012; Joerg Bornkessel <hd_brummy at gentoo.org> vdr-xvdr-9999.ebuild:
+ eapi=4; vdr-plugin-2.eclass
+
+ 18 Sep 2011; Diego E. Pettenò <flameeyes at gentoo.org> vdr-xvdr-9999.ebuild:
+ QA: do not keyword live ebuild.
+
+ 17 Sep 2011; Joerg Bornkessel <hd_brummy at gentoo.org> vdr-xvdr-9999.ebuild:
+ minor fixes
+
+ 17 Sep 2011; Joerg Bornkessel <hd_brummy at gentoo.org> vdr-xvdr-9999.ebuild:
+ removed useless strict restrict
+
+*vdr-xvdr-9999 (17 Sep 2011)
+
+ 17 Sep 2011; Joerg Bornkessel <hd_brummy at gentoo.org> +vdr-xvdr-9999.ebuild,
+ +metadata.xml:
+ initial ebuild, pmasked
+
diff --git a/gentoo/media-plugins/vdr-xvdr/Manifest b/gentoo/media-plugins/vdr-xvdr/Manifest
new file mode 100644
index 0000000..c4c2a6d
--- /dev/null
+++ b/gentoo/media-plugins/vdr-xvdr/Manifest
@@ -0,0 +1,2 @@
+EBUILD vdr-xvdr-9999.ebuild 1165 SHA256 4d1455818585f113ebe56475dde0b76321c7eefc71371d07a2a597fdf0f9af65 SHA512 028452c92988c77de5a4bf5001d2f585cdf9aeed34356d420fb1f73ef4b7a8cac210e559248569a44be646e7cf52247c101a4e9c4a32bbec5ce3fce4ec70a672 WHIRLPOOL a281ffb3998bd68a8ced091dbd9dc3ea61d6e3ed5fec6113e8086b713518db3e818f9d29a9a6eccb925a39e5cd7ade66b6a213213adbf1e652e43b6d0d2e3e4a
+MISC ChangeLog 984 SHA256 f54055b09168e596256465bdbb43a7c1fcdaa7384ff7d9695d283171d3cb69a5 SHA512 241f33cebb6f3b3b5e5b08ec63b4679bc67d44aead59c33f850270d5692c69f5624e73086824aea153aeac6595d4453a93c2e43d51aa42878da6b1a8d95e2e4d WHIRLPOOL 47ed6e717b1848e67fa675d810612d8d37d293331055e1c8c1e559473163efd9d6a788395013bf4ed205c02d530b81301d97ef5d3128ee2f63b10d62ec116de6
diff --git a/gentoo/media-plugins/vdr-xvdr/vdr-xvdr-9999.ebuild b/gentoo/media-plugins/vdr-xvdr/vdr-xvdr-9999.ebuild
new file mode 100644
index 0000000..2576fa5
--- /dev/null
+++ b/gentoo/media-plugins/vdr-xvdr/vdr-xvdr-9999.ebuild
@@ -0,0 +1,65 @@
+# Copyright 1999-2013 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+EAPI="5"
+
+inherit vdr-plugin-2 git-2 flag-o-matic
+
+EGIT_REPO_URI="git://github.com/pipelka/vdr-plugin-xvdr.git"
+
+DESCRIPTION="VDR plugin: XVDR Streamserver Plugin"
+HOMEPAGE="https://github.com/pipelka/vdr-plugin-xvdr"
+SRC_URI=""
+KEYWORDS=""
+LICENSE="GPL-2"
+SLOT="0"
+IUSE="debug tools"
+
+DEPEND=">=media-video/vdr-1.6"
+RDEPEND="${DEPEND}"
+
+S="${WORKDIR}/${PN}-plugin"
+
+src_prepare() {
+ vdr-plugin-2_src_prepare
+
+ fix_vdr_libsi_include "${S}"/src/live/livepatfilter.h
+}
+
+src_compile() {
+ if use debug; then
+ BUILD_PARAMS="DEBUG=1"
+ append-flags -g
+ fi
+
+ vdr-plugin-2_src_compile
+
+ if use tools ; then
+ cd "${S}/tools"
+ emake || die "emake failed for tools"
+ fi
+}
+
+src_install() {
+ vdr-plugin-2_src_install
+
+ insinto /etc/vdr/plugins/xvdr
+ doins xvdr/*.conf
+ diropts -gvdr -ovdr
+
+ if use tools ; then
+ exeinto /usr/bin
+ newexe "${S}/tools/serviceref" xvdr-serviceref
+ fi
+}
+
+pkg_postinst() {
+ vdr-plugin-2_pkg_postinst
+
+ if use tools ; then
+ elog
+ elog "The 'serviceref' tool has been installed as"
+ elog "\t/usr/bin/xvdr-serviceref"
+ fi
+}
diff --git a/po/.gitignore b/po/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/po/de_AT.po b/po/de_AT.po
new file mode 100644
index 0000000..8d19217
--- /dev/null
+++ b/po/de_AT.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
+# Alexander Pipelka <alexander.pipelka at gmail.com>, 2014.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr-xvdr 0.9.9\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2014-02-10 21:02+0100\n"
+"PO-Revision-Date: 2014-02-10 21:02+0100\n"
+"Last-Translator: Alexander Pipelka <alexander.pipelka at gmail.com>\n"
+"Language-Team: Deutsch <>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+
+msgid "Unable to decrypt channel"
+msgstr "Kanal verschlüsselt"
+
+msgid "All tuners busy"
+msgstr "Alle Empfänger belegt"
+
+msgid "Blocked by active recording"
+msgstr "Durch Aufnahme blockiert"
+
+msgid "Failed to switch"
+msgstr "Fehler beim Umschalten"
diff --git a/po/de_DE.po b/po/de_DE.po
new file mode 100644
index 0000000..8d19217
--- /dev/null
+++ b/po/de_DE.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
+# Alexander Pipelka <alexander.pipelka at gmail.com>, 2014.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr-xvdr 0.9.9\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2014-02-10 21:02+0100\n"
+"PO-Revision-Date: 2014-02-10 21:02+0100\n"
+"Last-Translator: Alexander Pipelka <alexander.pipelka at gmail.com>\n"
+"Language-Team: Deutsch <>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+
+msgid "Unable to decrypt channel"
+msgstr "Kanal verschlüsselt"
+
+msgid "All tuners busy"
+msgstr "Alle Empfänger belegt"
+
+msgid "Blocked by active recording"
+msgstr "Durch Aufnahme blockiert"
+
+msgid "Failed to switch"
+msgstr "Fehler beim Umschalten"
diff --git a/src/config/config.c b/src/config/config.c
index e058e96..7df4892 100644
--- a/src/config/config.c
+++ b/src/config/config.c
@@ -45,7 +45,11 @@ cXVDRServerConfig::cXVDRServerConfig()
}
void cXVDRServerConfig::Load() {
+#if VDRVERSNUM >= 20102
+ cLiveQueue::SetTimeShiftDir(cVideoDirectory::Name());
+#else
cLiveQueue::SetTimeShiftDir(VideoDirectory);
+#endif
cRecordingsCache::GetInstance().LoadResumeData();
if(!cConfig<cSetupLine>::Load(AddDirectory(ConfigDirectory, GENERAL_CONFIG_FILE), true, false))
diff --git a/src/demuxer/demuxer.c b/src/demuxer/demuxer.c
index 421f296..0b010cb 100644
--- a/src/demuxer/demuxer.c
+++ b/src/demuxer/demuxer.c
@@ -129,7 +129,6 @@ bool cTSDemuxer::ProcessTSPacket(unsigned char *data)
return false;
if (TsIsScrambled(data)) {
- INFOLOG("scrambled packet");
return false;
}
diff --git a/src/demuxer/demuxer.h b/src/demuxer/demuxer.h
index b87c1c4..295b2f8 100644
--- a/src/demuxer/demuxer.h
+++ b/src/demuxer/demuxer.h
@@ -39,8 +39,10 @@ struct sStreamPacket
sStreamPacket() {
type = cStreamInfo::stNONE;
content = cStreamInfo::scNONE;
+ frametype = cStreamInfo::ftUNKNOWN;
}
+ cStreamInfo::FrameType frametype;
cStreamInfo::Type type;
cStreamInfo::Content content;
diff --git a/src/demuxer/demuxer_H264.c b/src/demuxer/demuxer_H264.c
index 9bd3749..584d21f 100644
--- a/src/demuxer/demuxer_H264.c
+++ b/src/demuxer/demuxer_H264.c
@@ -43,9 +43,9 @@ const struct cParserH264::pixel_aspect_t cParserH264::m_aspect_ratios[] = {
#define PROFILE_CAVLC444 44
// NAL SPS ID
+#define NAL_SLH 0x01
#define NAL_SPS 0x07
-
// golomb decoding
uint32_t read_golomb_ue(cBitStream* bs)
{
@@ -76,35 +76,69 @@ cParserH264::cParserH264(cTSDemuxer *demuxer) : cParserPES(demuxer, 512 * 1024)
m_rate = 0;
}
+uint8_t* cParserH264::ExtractNAL(uint8_t* packet, int length, int nal_offset, int& nal_len) {
+ int e = FindStartCode(packet, length, nal_offset, 0x00000001);
+ if (e == -1) {
+ e = length;
+ }
+
+ int l = e - nal_offset;
+
+ if(l <= 0) {
+ return NULL;
+ }
+
+ uint8_t* nal_data = new uint8_t[l];
+ nal_len = nalUnescape(nal_data, packet + nal_offset, l);
+
+ if(nal_len + nal_offset > length) {
+ ERRORLOG("nal overrun: nal len: %i, offset: %i, packet length: %i", nal_len, nal_offset, length);
+ }
+
+ return nal_data;
+}
+
void cParserH264::ParsePayload(unsigned char* data, int length) {
int o = 0;
- bool spsfound = false;
+ int sps_start = -1;
+ int nal_len = 0;
+
+ if(length < 4) {
+ return;
+ }
- // iterate through all NAL units (and look for SPS)
+ // iterate through all NAL units
while((o = FindStartCode(data, length, o, 0x00000001)) >= 0) {
o += 4;
if(o >= length)
return;
- // NAL_SPS found ?
- if((data[o] & 0x1F) == NAL_SPS && length - o > 1) {
+ // NAL_SLH
+ if((data[o] & 0x1F) == NAL_SLH && length - o > 1) {
o++;
- spsfound = true;
- break;
+ uint8_t* nal_data = ExtractNAL(data, length, o, nal_len);
+
+ if(nal_data != NULL) {
+ Parse_SLH(nal_data, nal_len);
+ delete[] nal_data;
+ }
+ }
+
+ // NAL_SPS
+ else if((data[o] & 0x1F) == NAL_SPS && length - o > 1) {
+ o++;
+ sps_start = o;
}
}
- if(!spsfound)
+ if(sps_start == -1)
return;
- int e = FindStartCode(data, length, o, 0x00000001);
- if (e == -1)
- e = length;
-
- int l = e - o;
- uint8_t* nal_data = new uint8_t[l];
+ uint8_t* nal_data = ExtractNAL(data, length, sps_start, nal_len);
- int nal_len = nalUnescape(nal_data, data + o, l);
+ if(nal_data == NULL) {
+ return;
+ }
int width = 0;
int height = 0;
@@ -126,27 +160,48 @@ int cParserH264::nalUnescape(uint8_t *dst, const uint8_t *src, int len)
{
int s = 0, d = 0;
- while (s < len)
- {
- if (!src[s] && !src[s + 1])
- {
- // hit 00 00 xx
- dst[d] = dst[d + 1] = 0;
- s += 2;
- d += 2;
- if (src[s] == 3)
- {
- s++; // 00 00 03 xx --> 00 00 xx
- if (s >= len)
- return d;
+ while (s < len) {
+ if(s >= 2 && s < len - 1) {
+ // hit 00 00 03 ?
+ if(src[s - 2] == 0 && src[s - 1] == 0 && src[s] == 3) {
+ s++; // skip 03
}
}
+
dst[d++] = src[s++];
}
return d;
}
+void cParserH264::Parse_SLH(uint8_t *buf, int len) {
+ cBitStream bs(buf, len*8);
+
+ read_golomb_ue(&bs); // first_mb_in_slice
+ int type = read_golomb_ue(&bs);;
+
+ if(type > 4) {
+ type -= 5;
+ }
+
+ switch(type) {
+ case 0:
+ m_frametype = cStreamInfo::ftPFRAME;
+ break;
+ case 1:
+ m_frametype = cStreamInfo::ftBFRAME;
+ break;
+ case 2:
+ m_frametype = cStreamInfo::ftIFRAME;
+ break;
+ default:
+ m_frametype = cStreamInfo::ftUNKNOWN;
+ break;
+ }
+
+ return;
+}
+
bool cParserH264::Parse_SPS(uint8_t *buf, int len, struct pixel_aspect_t& pixelaspect, int& width, int& height)
{
bool seq_scaling_matrix_present = false;
diff --git a/src/demuxer/demuxer_H264.h b/src/demuxer/demuxer_H264.h
index 6b00498..391516a 100644
--- a/src/demuxer/demuxer_H264.h
+++ b/src/demuxer/demuxer_H264.h
@@ -44,8 +44,12 @@ private:
static const struct pixel_aspect_t m_aspect_ratios[];
+ uint8_t* ExtractNAL(uint8_t* packet, int length, int nal_offset, int& nal_len);
+
bool Parse_SPS(uint8_t *buf, int len, struct pixel_aspect_t& pixel_aspect, int& width, int& height);
+ void Parse_SLH(uint8_t *buf, int len);
+
int nalUnescape(uint8_t *dst, const uint8_t *src, int len);
int m_scale;
diff --git a/src/demuxer/demuxer_LATM.c b/src/demuxer/demuxer_LATM.c
index 8bf1430..5ea8234 100644
--- a/src/demuxer/demuxer_LATM.c
+++ b/src/demuxer/demuxer_LATM.c
@@ -83,7 +83,7 @@ void cParserLATM::ParsePayload(unsigned char* data, int len) {
slotLen += tmp;
} while (tmp == 255);
- if (slotLen * 8 > (bs.Length() - bs.Index()))
+ if (slotLen * 8 > (bs.Length() - (unsigned)bs.Index()))
return;
if (m_curDTS == DVD_NOPTS_VALUE)
diff --git a/src/demuxer/demuxer_MPEGVideo.c b/src/demuxer/demuxer_MPEGVideo.c
index ecbbfc0..50621f2 100644
--- a/src/demuxer/demuxer_MPEGVideo.c
+++ b/src/demuxer/demuxer_MPEGVideo.c
@@ -53,17 +53,34 @@ static int GetFrameType(unsigned char* data, int length) {
return bs.GetBits(3);
}
+static cStreamInfo::FrameType ConvertFrameType(int frametype) {
+ switch (frametype) {
+ case 1:
+ return cStreamInfo::ftIFRAME;
+ case 2:
+ return cStreamInfo::ftPFRAME;
+ case 3:
+ return cStreamInfo::ftBFRAME;
+ case 4:
+ return cStreamInfo::ftDFRAME;
+ default:
+ break;
+ }
+
+ return cStreamInfo::ftUNKNOWN;
+}
+
cParserMPEG2Video::cParserMPEG2Video(cTSDemuxer *demuxer) : cParserPES(demuxer, 512* 1024), m_pdiff(0), m_lastDTS(DVD_NOPTS_VALUE) {
}
-void cParserMPEG2Video::ParsePicture(unsigned char* data, int length) {
+cStreamInfo::FrameType cParserMPEG2Video::ParsePicture(unsigned char* data, int length) {
int frametype = GetFrameType(data, length);
// get I,P frames distance
if(frametype < 3 && m_curDTS != DVD_NOPTS_VALUE && m_curPTS != DVD_NOPTS_VALUE) {
m_pdiff = m_curPTS - m_curDTS;
m_lastDTS = m_curDTS;
- return;
+ return ConvertFrameType(frametype);
}
// extrapolate DTS
@@ -79,6 +96,8 @@ void cParserMPEG2Video::ParsePicture(unsigned char* data, int length) {
// extrapolate PTS of I/P frame
if(frametype < 3 && m_curPTS == DVD_NOPTS_VALUE)
m_curPTS = PtsAdd(m_curDTS, m_pdiff);
+
+ return ConvertFrameType(frametype);
}
void cParserMPEG2Video::ParsePayload(unsigned char* data, int length) {
@@ -98,6 +117,12 @@ void cParserMPEG2Video::ParsePayload(unsigned char* data, int length) {
// check for picture start codes
int s = FindStartCode(data, length, 0, MPEG2_PICTURE_START);
+
+ // abort if there isn't any picture information
+ if(s == -1) {
+ return;
+ }
+
int e = FindStartCode(data, length, s + 4, MPEG2_PICTURE_START);
o = s;
s = 0;
@@ -106,7 +131,7 @@ void cParserMPEG2Video::ParsePayload(unsigned char* data, int length) {
while(e != -1) {
// parse and send payload data
- ParsePicture(data + o, e - o);
+ m_frametype = ParsePicture(data + o, e - o);
cParser::SendPayload(data + s, e - s);
// get next picture offsets
@@ -120,7 +145,7 @@ void cParserMPEG2Video::ParsePayload(unsigned char* data, int length) {
}
// append last part
- ParsePicture(data + o, length - o);
+ m_frametype = ParsePicture(data + o, length - o);
cParser::SendPayload(data + s, length - s);
}
diff --git a/src/demuxer/demuxer_MPEGVideo.h b/src/demuxer/demuxer_MPEGVideo.h
index 3836a42..5a34af5 100644
--- a/src/demuxer/demuxer_MPEGVideo.h
+++ b/src/demuxer/demuxer_MPEGVideo.h
@@ -26,6 +26,7 @@
#define XVDR_DEMUXER_MPEGVIDEO_H
#include "demuxer_PES.h"
+#include "streaminfo.h"
#include <map>
class cParserMPEG2Video : public cParserPES
@@ -44,7 +45,7 @@ private:
void ParseSequenceStart(unsigned char* data, int length);
- void ParsePicture(unsigned char* data, int length);
+ cStreamInfo::FrameType ParsePicture(unsigned char* data, int length);
int64_t m_pdiff;
diff --git a/src/demuxer/parser.c b/src/demuxer/parser.c
index 2fb9c6d..2a4609e 100644
--- a/src/demuxer/parser.c
+++ b/src/demuxer/parser.c
@@ -34,6 +34,7 @@ cParser::cParser(cTSDemuxer *demuxer, int buffersize, int packetsize) : cRingBuf
m_channels = 0;
m_duration = 0;
m_headersize = 0;
+ m_frametype = cStreamInfo::ftUNKNOWN;
m_curPTS = DVD_NOPTS_VALUE;
m_curDTS = DVD_NOPTS_VALUE;
@@ -64,11 +65,12 @@ int cParser::ParsePESHeader(uint8_t *buf, size_t len)
void cParser::SendPayload(unsigned char* payload, int length)
{
sStreamPacket pkt;
- pkt.data = payload;
- pkt.size = length;
- pkt.duration = m_duration;
- pkt.dts = m_curDTS;
- pkt.pts = m_curPTS;
+ pkt.data = payload;
+ pkt.size = length;
+ pkt.duration = m_duration;
+ pkt.dts = m_curDTS;
+ pkt.pts = m_curPTS;
+ pkt.frametype = m_frametype;
m_demuxer->SendPacket(&pkt);
}
diff --git a/src/demuxer/parser.h b/src/demuxer/parser.h
index 17bbede..bd450a7 100644
--- a/src/demuxer/parser.h
+++ b/src/demuxer/parser.h
@@ -60,6 +60,7 @@ protected:
int m_channels;
int m_duration;
int m_headersize;
+ cStreamInfo::FrameType m_frametype;
bool m_startup;
diff --git a/src/demuxer/streaminfo.c b/src/demuxer/streaminfo.c
index 5252f1c..91ab5dd 100644
--- a/src/demuxer/streaminfo.c
+++ b/src/demuxer/streaminfo.c
@@ -109,7 +109,15 @@ bool cStreamInfo::operator ==(const cStreamInfo& rhs) const {
}
bool cStreamInfo::ismetaof(const cStreamInfo& rhs) const {
- return (m_pid == rhs.m_pid && m_type == rhs.m_type && m_content == rhs.m_content);
+ if(m_content != rhs.m_content) {
+ return false;
+ }
+
+ if(m_type != rhs.m_type && (m_type != stAC3 && rhs.m_type != stEAC3)) {
+ return false;
+ }
+
+ return (m_pid == rhs.m_pid);
}
bool cStreamInfo::operator !=(const cStreamInfo& rhs) const {
@@ -176,41 +184,41 @@ void cStreamInfo::SetSubtitlingDescriptor(unsigned char SubtitlingType, uint16_t
m_parsed = true;
}
-std::fstream& operator<< (std::fstream& lhs, const cStreamInfo& rhs) {
+MsgPacket& operator<< (MsgPacket& lhs, const cStreamInfo& rhs) {
// write item sync
- lhs << (int)0xFEFEFEFE << std::endl;
+ lhs.put_U32(0xFEFEFEFE);
// write general data
- lhs << rhs.m_type << std::endl;
- lhs << rhs.m_content << std::endl;
- lhs << rhs.m_pid << std::endl;
- lhs << rhs.m_parsed << std::endl;
+ lhs.put_U8(rhs.m_type);
+ lhs.put_U8(rhs.m_content);
+ lhs.put_U16(rhs.m_pid);
+ lhs.put_U8(rhs.m_parsed);
const char* lang = (rhs.m_language[0] == 0 ? "XXX" : rhs.m_language);
// write specific data
switch(rhs.m_content) {
case cStreamInfo::scAUDIO:
- lhs << lang << std::endl;
- lhs << (int)rhs.m_audiotype << std::endl;
- lhs << rhs.m_channels << std::endl;
- lhs << rhs.m_samplerate << std::endl;
- lhs << rhs.m_bitrate << std::endl;
- lhs << rhs.m_bitspersample << std::endl;
- lhs << rhs.m_blockalign << std::endl;
+ lhs.put_String(lang);
+ lhs.put_U8(rhs.m_audiotype);
+ lhs.put_U8(rhs.m_channels);
+ lhs.put_U32(rhs.m_samplerate);
+ lhs.put_U32(rhs.m_bitrate);
+ lhs.put_U8(rhs.m_bitspersample);
+ lhs.put_U32(rhs.m_blockalign);
break;
case cStreamInfo::scVIDEO:
- lhs << rhs.m_fpsscale << std::endl;
- lhs << rhs.m_fpsrate << std::endl;
- lhs << rhs.m_height << std::endl;
- lhs << rhs.m_width << std::endl;
- lhs << (unsigned long long)(rhs.m_aspect * 1000000000) << std::endl;
+ lhs.put_U32(rhs.m_fpsscale);
+ lhs.put_U32(rhs.m_fpsrate);
+ lhs.put_U16(rhs.m_height);
+ lhs.put_U16(rhs.m_width);
+ lhs.put_U64((unsigned long long)(rhs.m_aspect * 1000000000));
break;
case cStreamInfo::scSUBTITLE:
- lhs << lang << std::endl;
- lhs << rhs.m_subtitlingtype << std::endl;
- lhs << rhs.m_compositionpageid << std::endl;
- lhs << rhs.m_ancillarypageid << std::endl;
+ lhs.put_String(lang);
+ lhs.put_U8(rhs.m_subtitlingtype);
+ lhs.put_U16(rhs.m_compositionpageid);
+ lhs.put_U16(rhs.m_ancillarypageid);
break;
case cStreamInfo::scTELETEXT:
break;
@@ -221,10 +229,9 @@ std::fstream& operator<< (std::fstream& lhs, const cStreamInfo& rhs) {
return lhs;
}
-std::fstream& operator>> (std::fstream& lhs, cStreamInfo& rhs) {
- unsigned long long a;
+MsgPacket& operator>> (MsgPacket& lhs, cStreamInfo& rhs) {
unsigned int check = 0;
- lhs >> check;
+ check = lhs.get_U32();
if(check != 0xFEFEFEFE)
return lhs;
@@ -232,16 +239,10 @@ std::fstream& operator>> (std::fstream& lhs, cStreamInfo& rhs) {
rhs.Initialize();
// read general data
- int t = 0;
- lhs >> t;
- rhs.m_type = static_cast<cStreamInfo::Type>(t);
-
- int c = 0;
- lhs >> c;
- rhs.m_content = static_cast<cStreamInfo::Content>(c);
-
- lhs >> rhs.m_pid;
- lhs >> rhs.m_parsed;
+ rhs.m_type = static_cast<cStreamInfo::Type>(lhs.get_U8());
+ rhs.m_content = static_cast<cStreamInfo::Content>(lhs.get_U8());
+ rhs.m_pid = lhs.get_U16();
+ rhs.m_parsed = lhs.get_U8();
// read specific data
int at = 0;
@@ -249,28 +250,26 @@ std::fstream& operator>> (std::fstream& lhs, cStreamInfo& rhs) {
switch(rhs.m_content) {
case cStreamInfo::scAUDIO:
- lhs >> lang;
- lhs >> at;
- rhs.m_audiotype = at;
- lhs >> rhs.m_channels;
- lhs >> rhs.m_samplerate;
- lhs >> rhs.m_bitrate;
- lhs >> rhs.m_bitspersample;
- lhs >> rhs.m_blockalign;
+ lang = lhs.get_String();
+ rhs.m_audiotype = lhs.get_U8();
+ rhs.m_channels = lhs.get_U8();
+ rhs.m_samplerate = lhs.get_U32();
+ rhs.m_bitrate = lhs.get_U32();
+ rhs.m_bitspersample = lhs.get_U8();
+ rhs.m_blockalign = lhs.get_U32();
break;
case cStreamInfo::scVIDEO:
- lhs >> rhs.m_fpsscale;
- lhs >> rhs.m_fpsrate;
- lhs >> rhs.m_height;
- lhs >> rhs.m_width;
- lhs >> a;
- rhs.m_aspect = (double)a / 1000000000.0;
+ rhs.m_fpsscale = lhs.get_U32();
+ rhs.m_fpsrate = lhs.get_U32();
+ rhs.m_height = lhs.get_U16();
+ rhs.m_width = lhs.get_U16();
+ rhs.m_aspect = (double)lhs.get_U64() / 1000000000.0;
break;
case cStreamInfo::scSUBTITLE:
- lhs >> lang;
- lhs >> rhs.m_subtitlingtype;
- lhs >> rhs.m_compositionpageid;
- lhs >> rhs.m_ancillarypageid;
+ lang = lhs.get_String();
+ rhs.m_subtitlingtype = lhs.get_U8();
+ rhs.m_compositionpageid = lhs.get_U16();
+ rhs.m_ancillarypageid = lhs.get_U16();
break;
case cStreamInfo::scTELETEXT:
break;
@@ -278,8 +277,7 @@ std::fstream& operator>> (std::fstream& lhs, cStreamInfo& rhs) {
break;
}
- if(lang != "XXX")
- strncpy(rhs.m_language, lang.c_str(), 4);
+ strncpy(rhs.m_language, lang.c_str(), sizeof(rhs.m_language));
return lhs;
}
diff --git a/src/demuxer/streaminfo.h b/src/demuxer/streaminfo.h
index 9920b86..77a1476 100644
--- a/src/demuxer/streaminfo.h
+++ b/src/demuxer/streaminfo.h
@@ -29,6 +29,8 @@
#include <fstream>
#include <string>
+#include "net/msgpacket.h"
+
class cStreamInfo {
public:
@@ -38,7 +40,8 @@ public:
scVIDEO,
scAUDIO,
scSUBTITLE,
- scTELETEXT
+ scTELETEXT,
+ scSTREAMINFO
};
enum Type
@@ -55,6 +58,14 @@ public:
stTELETEXT,
};
+ enum FrameType{
+ ftUNKNOWN,
+ ftIFRAME,
+ ftPFRAME,
+ ftBFRAME,
+ ftDFRAME
+ };
+
public:
cStreamInfo();
@@ -118,9 +129,8 @@ protected:
friend class cLivePatFilter;
- friend std::fstream& operator<< (std::fstream& lhs, const cStreamInfo& rhs);
-
- friend std::fstream& operator>> (std::fstream& lhs, cStreamInfo& rhs);
+ friend MsgPacket& operator<< (MsgPacket& lhs, const cStreamInfo& rhs);
+ friend MsgPacket& operator>> (MsgPacket& lhs, cStreamInfo& rhs);
private:
@@ -128,8 +138,7 @@ private:
};
-std::fstream& operator<< (std::fstream& lhs, const cStreamInfo& rhs);
-
-std::fstream& operator>> (std::fstream& lhs, cStreamInfo& rhs);
+MsgPacket& operator>> (MsgPacket& lhs, cStreamInfo& rhs);
+MsgPacket& operator<< (MsgPacket& lhs, const cStreamInfo& rhs);
#endif // XVDR_STREAMINFO_H
diff --git a/src/live/channelcache.c b/src/live/channelcache.c
index 0e7ef6a..79ed333 100644
--- a/src/live/channelcache.c
+++ b/src/live/channelcache.c
@@ -38,6 +38,15 @@ void cChannelCache::AddStream(const cStreamInfo& s) {
if(s.GetPID() == 0 || s.GetType() == cStreamInfo::stNONE)
return;
+ // allow only one video stream
+ if(s.GetContent() == cStreamInfo::scVIDEO) {
+ for(iterator i = begin(); i != end(); i++) {
+ if(i->second.GetContent() == cStreamInfo::scVIDEO && i->second.GetPID() != s.GetPID()) {
+ return;
+ }
+ }
+ }
+
cStreamInfo old = (*this)[s.GetPID()];
(*this)[s.GetPID()] = s;
@@ -134,22 +143,7 @@ void cChannelCache::AddToCache(uint32_t channeluid, const cChannelCache& channel
Unlock();
}
-void cChannelCache::AddToCache(const cChannel* channel) {
- cMutexLock lock(&m_access);
-
- uint32_t uid = CreateChannelUID(channel);
-
- // ignore invalid channels
- if(uid == 0)
- return;
-
- std::map<uint32_t, cChannelCache>::iterator i = m_cache.find(uid);
-
- // channel already in cache
- if(i != m_cache.end())
- return;
-
- // create new cache item
+cChannelCache cChannelCache::ItemFromChannel(const cChannel* channel) {
cChannelCache item;
// add video stream
@@ -192,11 +186,45 @@ void cChannelCache::AddToCache(const cChannel* channel) {
item.AddStream(stream);
}
+ return item;
+}
+
+
+void cChannelCache::AddToCache(const cChannel* channel) {
+ cMutexLock lock(&m_access);
+
+ uint32_t uid = CreateChannelUID(channel);
+
+ // ignore invalid channels
+ if(uid == 0)
+ return;
+
+ std::map<uint32_t, cChannelCache>::iterator i = m_cache.find(uid);
+
+ // valid channel already in cache
+ if(i != m_cache.end()) {
+ if(i->second.size() != 0) {
+ return;
+ }
+ }
+
+ // create new cache item
+ cChannelCache item = ItemFromChannel(channel);
+
AddToCache(uid, item);
}
cChannelCache cChannelCache::GetFromCache(uint32_t channeluid) {
+ static cChannelCache empty;
+
Lock();
+
+ std::map<uint32_t, cChannelCache>::iterator i = m_cache.find(channeluid);
+ if(i == m_cache.end()) {
+ Unlock();
+ return empty;
+ }
+
cChannelCache result = m_cache[channeluid];
Unlock();
@@ -204,86 +232,129 @@ cChannelCache cChannelCache::GetFromCache(uint32_t channeluid) {
}
void cChannelCache::SaveChannelCacheData() {
- std::fstream out;
cString filename = AddDirectory(XVDRServerConfig.CacheDirectory, CHANNEL_CACHE_FILE".bak");
- out.open(filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
-
- if(!out.is_open()) {
+ int fd = open(*filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if(fd == -1) {
+ ERRORLOG("Unable to open channel cache data file (%s) !", (const char*)filename);
return;
}
Lock();
- out << "V2" << std::endl;
- out << m_cache.size() << std::endl;
+ MsgPacket* p = new MsgPacket;
+ p->put_String("V2");
+ p->put_U32(m_cache.size());
for(std::map<uint32_t, cChannelCache>::iterator i = m_cache.begin(); i != m_cache.end(); i++) {
- out << i->first << std::endl;
- out << i->second;
+ p->put_U32(i->first);
+ *p << i->second;
}
Unlock();
+ p->write(fd, 1000);
+ delete p;
+ close(fd);
+
cString filenamenew = AddDirectory(XVDRServerConfig.CacheDirectory, CHANNEL_CACHE_FILE);
rename(filename, filenamenew);
}
-void cChannelCache::LoadChannelCacheData() {
- m_cache.clear();
+void cChannelCache::gc() {
+ cMutexLock lock(&m_access);
+ std::map<uint32_t, cChannelCache> m_newcache;
+
+ INFOLOG("channel cache garbage collection ...");
+ INFOLOG("before: %zu channels in cache", m_cache.size());
- // preload cache with VDR channel entries
+ // remove orphaned cache entries
XVDRChannels.Lock(false);
cChannels *channels = XVDRChannels.Get();
+
for (cChannel *channel = channels->First(); channel; channel = channels->Next(channel)) {
- AddToCache(channel);
+ uint32_t uid = CreateChannelUID(channel);
+
+ // ignore invalid channels
+ if(uid == 0)
+ continue;
+
+ // lookup channel in current cache
+ std::map<uint32_t, cChannelCache>::iterator i = m_cache.find(uid);
+ if(i == m_cache.end())
+ continue;
+
+ // add to new cache if it exists
+ m_newcache[uid] = i->second;
}
XVDRChannels.Unlock();
+ // regenerate cache
+ m_cache.clear();
+ std::map<uint32_t, cChannelCache>::iterator i;
+
+ for(i = m_newcache.begin(); i != m_newcache.end(); i++) {
+ m_cache[i->first] = i->second;
+ }
+
+ INFOLOG("after: %zu channels in cache", m_cache.size());
+}
+
+void cChannelCache::LoadChannelCacheData() {
+ m_cache.clear();
+
// load cache
- std::fstream in;
cString filename = AddDirectory(XVDRServerConfig.CacheDirectory, CHANNEL_CACHE_FILE);
- in.open(filename, std::ios_base::in | std::ios_base::binary);
-
- if(!in.is_open()) {
+ int fd = open(*filename, O_RDONLY);
+ if(fd == -1) {
ERRORLOG("Unable to open channel cache data file (%s) !", (const char*)filename);
return;
}
- std::string version;
- in >> version;
+ MsgPacket* p = MsgPacket::read(fd, 1000);
+ if(p == NULL) {
+ ERRORLOG("Unable to load channel cache data file (%s) !", (const char*)filename);
+ close(fd);
+ return;
+ }
+ std::string version = p->get_String();
if(version != "V2") {
INFOLOG("old channel cache detected - skipped");
return;
}
- int c = 0;
- in >> c;
+ uint32_t c = p->get_U32();
// sanity check
- if(c > 10000)
+ if(c > 10000) {
+ delete p;
+ close(fd);
return;
+ }
- INFOLOG("Loading %i channels from cache", c);
+ INFOLOG("Loading %u channels from cache", c);
- for(int i = 0; i < c; i++) {
- int uid = 0;
- in >> uid;
+ for(uint32_t i = 0; i < c; i++) {
+ uint32_t uid = p->get_U32();
cChannelCache cache;
- in >> cache;
+ *p >> cache;
if(uid != 0)
m_cache[uid] = cache;
}
-}
+ delete p;
+ close(fd);
+
+ gc();
+}
-std::fstream& operator<< (std::fstream& lhs, const cChannelCache& rhs) {
- lhs << (int)rhs.size() << std::endl;
+MsgPacket& operator<< (MsgPacket& lhs, const cChannelCache& rhs) {
+ lhs.put_U32((int)rhs.size());
for(cChannelCache::const_iterator i = rhs.begin(); i != rhs.end(); i++) {
lhs << i->second;
@@ -292,12 +363,11 @@ std::fstream& operator<< (std::fstream& lhs, const cChannelCache& rhs) {
return lhs;
}
-std::fstream& operator>> (std::fstream& lhs, cChannelCache& rhs) {
+MsgPacket& operator>> (MsgPacket& lhs, cChannelCache& rhs) {
rhs.clear();
- int c = 0;
- lhs >> c;
+ uint32_t c = lhs.get_U32();
- for(int i = 0; i < c; i++) {
+ for(uint32_t i = 0; i < c; i++) {
cStreamInfo s;
lhs >> s;
rhs.AddStream(s);
diff --git a/src/live/channelcache.h b/src/live/channelcache.h
index 0df78ee..c5d7edf 100644
--- a/src/live/channelcache.h
+++ b/src/live/channelcache.h
@@ -66,6 +66,10 @@ public:
static cChannelCache GetFromCache(uint32_t channeluid);
+ static cChannelCache ItemFromChannel(const cChannel* channel);
+
+ static void gc();
+
private:
static void Lock() { m_access.Lock(); }
@@ -79,8 +83,7 @@ private:
bool m_bChanged;
};
-std::fstream& operator<< (std::fstream& lhs, const cChannelCache& rhs);
-
-std::fstream& operator>> (std::fstream& lhs, cChannelCache& rhs);
+MsgPacket& operator<< (MsgPacket& lhs, const cChannelCache& rhs);
+MsgPacket& operator>> (MsgPacket& lhs, cChannelCache& rhs);
#endif // XVDR_CHANNELCACHEITEM_H
diff --git a/src/live/livepatfilter.c b/src/live/livepatfilter.c
index c14bbcf..bc5049c 100644
--- a/src/live/livepatfilter.c
+++ b/src/live/livepatfilter.c
@@ -23,6 +23,8 @@
*
*/
+#include <unistd.h>
+
#include "vdr/config.h"
#include "config/config.h"
#include "tools/hash.h"
@@ -55,10 +57,10 @@ static const char * const psStreamTypes[] = {
"",
};
-cLivePatFilter::cLivePatFilter(cLiveStreamer *Streamer, const cChannel *Channel)
+cLivePatFilter::cLivePatFilter(cLiveStreamer *Streamer)
{
DEBUGLOG("cStreamdevPatFilter(\"%s\")", Channel->Name());
- m_Channel = Channel;
+ m_Channel = NULL;
m_Streamer = Streamer;
m_pmtPid = 0;
m_pmtSid = 0;
@@ -67,6 +69,11 @@ cLivePatFilter::cLivePatFilter(cLiveStreamer *Streamer, const cChannel *Channel)
}
+void cLivePatFilter::SetChannel(const cChannel *Channel) {
+ cMutexLock lock(&m_Mutex);
+ m_Channel = Channel;
+}
+
void cLivePatFilter::GetLanguage(SI::PMT::Stream& stream, char *langs, uint8_t& type)
{
SI::Descriptor *d;
@@ -286,6 +293,13 @@ bool cLivePatFilter::GetStreamInfo(SI::PMT::Stream& stream, cStreamInfo& info)
void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{
+ {
+ cMutexLock lock(&m_Mutex);
+ if(m_Channel == NULL) {
+ return;
+ }
+ }
+
if (Pid == 0x00 && Tid == 0x00)
{
SI::PAT pat(Data, false);
@@ -297,7 +311,8 @@ void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Le
{
if (!assoc.isNITPid())
{
- XVDRChannels.Lock(false);
+ cMutexLock lock(&m_Mutex);
+ XVDRChannels.Lock(false);
const cChannel *Channel = XVDRChannels.Get()->GetByServiceID(Source(), Transponder(), assoc.getServiceId());
if (Channel && (Channel == m_Channel))
@@ -338,8 +353,10 @@ void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Le
m_pmtVersion = pmt.getVersionNumber();
// get cached channel data
- if(m_ChannelCache.size() == 0)
+ if(m_ChannelCache.size() == 0) {
+ cMutexLock lock(&m_Mutex);
m_ChannelCache = cChannelCache::GetFromCache(CreateChannelUID(m_Channel));
+ }
// get all streams and check if there are new (currently unknown) streams
SI::PMT::Stream stream;
@@ -359,9 +376,18 @@ void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Le
m_Streamer->m_FilterMutex.Lock();
+ // do not restart the receiver (detach / attach) for VDR >= 2.1.6
+ // VDR's ChannelChange notification will trigger the detach / attach procedure
+ // and also recreate the demuxers
+
+#if VDRVERSNUM < 20106 // VDR VERSION < 2.1.6
+ if(m_Streamer->IsAttached()) {
+ m_Streamer->Detach();
+ }
+
// create new stream demuxers
- m_Streamer->Detach();
cache.CreateDemuxers(m_Streamer);
+#endif
m_Streamer->m_ready = false;
INFOLOG("Currently unknown new streams found, requesting stream change");
@@ -370,10 +396,14 @@ void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Le
// write changed data back to the cache
m_ChannelCache = cache;
- cChannelCache::AddToCache(CreateChannelUID(m_Channel), m_ChannelCache);
+ {
+ cMutexLock lock(&m_Mutex);
+ cChannelCache::AddToCache(CreateChannelUID(m_Channel), m_ChannelCache);
+ }
- if(!m_Streamer->IsAttached())
- m_Streamer->Attach();
+#if VDRVERSNUM < 20106 // VDR VERSION < 2.1.6
+ m_Streamer->Attach();
+#endif
m_Streamer->m_FilterMutex.Unlock();
}
diff --git a/src/live/livepatfilter.h b/src/live/livepatfilter.h
index 47eee37..97f9ec1 100644
--- a/src/live/livepatfilter.h
+++ b/src/live/livepatfilter.h
@@ -44,13 +44,15 @@ private:
const cChannel *m_Channel;
cLiveStreamer *m_Streamer;
cChannelCache m_ChannelCache;
+ cMutex m_Mutex;
bool GetStreamInfo(SI::PMT::Stream& stream, cStreamInfo& info);
void GetLanguage(SI::PMT::Stream& stream, char *langs, uint8_t& type);
virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
public:
- cLivePatFilter(cLiveStreamer *Streamer, const cChannel *Channel);
+ cLivePatFilter(cLiveStreamer *Streamer);
+ void SetChannel(const cChannel *Channel);
};
#endif // XVDR_LIVEPATFILTER_H
diff --git a/src/live/livequeue.c b/src/live/livequeue.c
index 3d2a12d..bf27125 100644
--- a/src/live/livequeue.c
+++ b/src/live/livequeue.c
@@ -33,7 +33,7 @@
cString cLiveQueue::TimeShiftDir = "/video";
uint64_t cLiveQueue::BufferSize = 1024*1024*1024;
-cLiveQueue::cLiveQueue(int sock) : m_socket(sock), m_readfd(-1), m_writefd(-1)
+cLiveQueue::cLiveQueue(int sock) : m_socket(sock), m_readfd(-1), m_writefd(-1), m_queuesize(400)
{
m_pause = false;
}
@@ -98,7 +98,7 @@ bool cLiveQueue::TimeShiftMode()
return (m_pause || (!m_pause && m_writefd != -1));
}
-bool cLiveQueue::Add(MsgPacket* p)
+bool cLiveQueue::Add(MsgPacket* p, cStreamInfo::Content content)
{
cMutexLock lock(&m_lock);
@@ -125,14 +125,24 @@ bool cLiveQueue::Add(MsgPacket* p)
return true;
}
- // queue too long ?
- if (size() > 100) {
- delete p;
- return false;
+ // discard teletext / signalinfo packets if the buffer fills up, ...
+ if(size() > (m_queuesize / 2)) {
+ if(content == cStreamInfo::scTELETEXT || content == cStreamInfo::scNONE) {
+ delete p;
+ m_cond.Signal();
+ return true;
+ }
}
// add packet to queue
push(p);
+
+ // queue too long ?
+ while (size() > m_queuesize) {
+ p = front();
+ pop();
+ }
+
m_cond.Signal();
return true;
@@ -192,7 +202,9 @@ void cLiveQueue::CloseTimeShift()
close(m_writefd);
m_writefd = -1;
- unlink(m_storage);
+ if(*m_storage) {
+ unlink(m_storage);
+ }
}
bool cLiveQueue::Pause(bool on)
diff --git a/src/live/livequeue.h b/src/live/livequeue.h
index 4dd6257..804d845 100644
--- a/src/live/livequeue.h
+++ b/src/live/livequeue.h
@@ -27,6 +27,7 @@
#include <queue>
#include <vdr/thread.h>
+#include "demuxer/streaminfo.h"
class MsgPacket;
@@ -38,7 +39,7 @@ public:
virtual ~cLiveQueue();
- bool Add(MsgPacket* p);
+ bool Add(MsgPacket* p, cStreamInfo::Content content);
void Request();
@@ -54,12 +55,12 @@ public:
static void RemoveTimeShiftFiles();
+ void Cleanup();
+
protected:
void Action();
- void Cleanup();
-
void CloseTimeShift();
int m_socket;
@@ -76,6 +77,8 @@ protected:
cString m_storage;
+ int m_queuesize;
+
static cString TimeShiftDir;
static uint64_t BufferSize;
diff --git a/src/live/livestreamer.c b/src/live/livestreamer.c
index 1578610..b92b8be 100644
--- a/src/live/livestreamer.c
+++ b/src/live/livestreamer.c
@@ -42,6 +42,7 @@
#include "config/config.h"
#include "net/msgpacket.h"
#include "xvdr/xvdrcommand.h"
+#include "xvdr/xvdrclient.h"
#include "tools/hash.h"
#include "livestreamer.h"
@@ -49,31 +50,37 @@
#include "livequeue.h"
#include "channelcache.h"
-cLiveStreamer::cLiveStreamer(int priority, uint32_t timeout, uint32_t protocolVersion)
+cLiveStreamer::cLiveStreamer(cXVDRClient* parent, const cChannel *channel, int priority)
: cThread("cLiveStreamer stream processor")
, cRingBufferLinear(MEGABYTE(10), TS_SIZE * 2, true)
, cReceiver(NULL, priority)
- , m_scanTimeout(timeout)
+ , m_scanTimeout(10)
+ , m_parent(parent)
{
- m_Priority = priority;
m_Device = NULL;
m_Queue = NULL;
- m_PatFilter = NULL;
m_startup = true;
m_SignalLost = false;
m_LangStreamType = cStreamInfo::stMPEG2AUDIO;
m_LanguageIndex = -1;
- m_uid = 0;
+ m_uid = CreateChannelUID(channel);
m_ready = false;
- m_protocolVersion = protocolVersion;
+ m_protocolVersion = XVDR_PROTOCOLVERSION;
+ m_waitforiframe = false;
+ m_PatFilter = NULL;
- m_requestStreamChange = false;
+ m_requestStreamChange = false;
if(m_scanTimeout == 0)
m_scanTimeout = XVDRServerConfig.stream_timeout;
+ // create send queue
+ m_Queue = new cLiveQueue(m_parent->GetSocket());
+ m_Queue->Start();
+
SetTimeouts(0, 10);
+ Start();
}
cLiveStreamer::~cLiveStreamer()
@@ -86,13 +93,19 @@ cLiveStreamer::~cLiveStreamer()
Cancel(5);
DEBUGLOG("Done.");
+ cMutexLock lock(&m_FilterMutex);
+
DEBUGLOG("Detaching");
- if (m_Device) {
- Detach();
+
+ if(m_PatFilter != NULL && m_Device != NULL) {
m_Device->Detach(m_PatFilter);
+ delete m_PatFilter;
+ m_PatFilter = NULL;
}
- delete m_PatFilter;
+ if (IsAttached()) {
+ Detach();
+ }
for (std::list<cTSDemuxer*>::iterator i = m_Demuxers.begin(); i != m_Demuxers.end(); i++) {
if ((*i) != NULL) {
@@ -104,33 +117,95 @@ cLiveStreamer::~cLiveStreamer()
delete m_Queue;
+ m_uid = 0;
+
+ {
+ cMutexLock lock(&m_DeviceMutex);
+ m_Device = NULL;
+ }
+
DEBUGLOG("Finished to delete live streamer (took %llu ms)", t.Elapsed());
}
+void cLiveStreamer::SetTimeout(uint32_t timeout) {
+ m_scanTimeout = timeout;
+}
+
+void cLiveStreamer::SetProtocolVersion(uint32_t protocolVersion) {
+ m_protocolVersion = protocolVersion;
+}
+
+void cLiveStreamer::SetWaitForIFrame(bool waitforiframe) {
+ m_waitforiframe = waitforiframe;
+}
+
void cLiveStreamer::RequestStreamChange()
{
m_requestStreamChange = true;
}
+void cLiveStreamer::TryChannelSwitch() {
+ // find channel from uid
+ const cChannel* channel = FindChannelByUID(m_uid);
+
+ // try to switch channel
+ int rc = SwitchChannel(channel);
+
+ // succeeded -> exit
+ if(rc == XVDR_RET_OK) {
+ return;
+ }
+
+ // time limit not exceeded -> relax & exit
+ if(m_last_tick.Elapsed() < (uint64_t)(m_scanTimeout*1000)) {
+ cCondWait::SleepMs(10);
+ return;
+ }
+
+ // push notification after timeout
+ switch(rc) {
+ case XVDR_RET_ENCRYPTED:
+ ERRORLOG("Unable to decrypt channel %i - %s", channel->Number(), channel->Name());
+ m_parent->StatusMessage(tr("Unable to decrypt channel"));
+ break;
+ case XVDR_RET_DATALOCKED:
+ ERRORLOG("Can't get device for channel %i - %s", channel->Number(), channel->Name());
+ m_parent->StatusMessage(tr("All tuners busy"));
+ break;
+ case XVDR_RET_RECRUNNING:
+ ERRORLOG("Active recording blocking channel %i - %s", channel->Number(), channel->Name());
+ m_parent->StatusMessage(tr("Blocked by active recording"));
+ break;
+ case XVDR_RET_ERROR:
+ ERRORLOG("Error switching to channel %i - %s", channel->Number(), channel->Name());
+ m_parent->StatusMessage(tr("Failed to switch"));
+ break;
+ }
+
+ m_last_tick.Set(0);
+}
+
void cLiveStreamer::Action(void)
{
int size = 0;
unsigned char *buf = NULL;
m_startup = true;
+ // reset timer
+ m_last_tick.Set(0);
+
+ INFOLOG("streamer thread started.");
+
while (Running())
{
size = 0;
buf = Get(size);
+ // try to switch channel if we aren't attached yet
{
cMutexLock lock(&m_FilterMutex);
- if (!IsAttached())
- {
- INFOLOG("returning from streamer thread, receiver is no more attached");
- Clear();
- sendDetach();
- return;
+ if (!IsAttached()) {
+ TryChannelSwitch();
}
}
@@ -139,6 +214,18 @@ void cLiveStreamer::Action(void)
INFOLOG("timeout. signal lost!");
sendStatus(XVDR_STREAM_STATUS_SIGNALLOST);
m_SignalLost = true;
+
+ // retune to restore
+ cMutexLock lock(&m_FilterMutex);
+ if(m_PatFilter != NULL && m_Device != NULL) {
+ m_Device->Detach(m_PatFilter);
+ delete m_PatFilter;
+ m_PatFilter = NULL;
+ }
+
+ if(IsAttached()) {
+ Detach();
+ }
}
// no data
@@ -157,7 +244,6 @@ void cLiveStreamer::Action(void)
}
Del(used);
-
while (size >= TS_SIZE)
{
if(!Running())
@@ -182,17 +268,25 @@ void cLiveStreamer::Action(void)
Del(TS_SIZE);
}
}
+
+ INFOLOG("streamer thread ended.");
}
-int cLiveStreamer::StreamChannel(const cChannel *channel, int sock)
+int cLiveStreamer::SwitchChannel(const cChannel *channel)
{
- if (channel == NULL)
- {
- ERRORLOG("Starting streaming of channel without valid channel");
+ if (channel == NULL) {
return XVDR_RET_ERROR;
}
- m_uid = CreateChannelUID(channel);
+ if(m_PatFilter != NULL && m_Device != NULL) {
+ m_Device->Detach(m_PatFilter);
+ delete m_PatFilter;
+ m_PatFilter = NULL;
+ }
+
+ if(IsAttached()) {
+ Detach();
+ }
// check if any device is able to decrypt the channel - code taken from VDR
int NumUsableSlots = 0;
@@ -208,29 +302,26 @@ int cLiveStreamer::StreamChannel(const cChannel *channel, int sock)
}
}
if (!NumUsableSlots) {
- ERRORLOG("Unable to decrypt channel %i - %s", channel->Number(), channel->Name());
return XVDR_RET_ENCRYPTED;
}
}
// get device for this channel
- m_Device = cDevice::GetDevice(channel, m_Priority, true);
-
- // try a bit harder if we can't find a device
- if(m_Device == NULL)
- m_Device = cDevice::GetDevice(channel, m_Priority, false);
-
- INFOLOG("--------------------------------------");
- INFOLOG("Channel streaming request: %i - %s", channel->Number(), channel->Name());
+ {
+ cMutexLock lock(&m_DeviceMutex);
+ m_Device = cDevice::GetDevice(channel, LIVEPRIORITY, false);
+ }
if (m_Device == NULL)
{
- ERRORLOG("Can't get device for channel %i - %s", channel->Number(), channel->Name());
-
// return status "recording running" if there is an active timer
time_t now = time(NULL);
- if(Timers.GetMatch(now) != NULL)
- return XVDR_RET_RECRUNNING;
+
+ for (cTimer *ti = Timers.First(); ti; ti = Timers.Next(ti)) {
+ if (ti->Recording() && ti->Matches(now)) {
+ return XVDR_RET_RECRUNNING;
+ }
+ }
return XVDR_RET_DATALOCKED;
}
@@ -243,35 +334,58 @@ int cLiveStreamer::StreamChannel(const cChannel *channel, int sock)
return XVDR_RET_ERROR;
}
- // create send queue
- if (m_Queue == NULL)
- {
- m_Queue = new cLiveQueue(sock);
- m_Queue->Start();
- }
-
- m_PatFilter = new cLivePatFilter(this, channel);
-
// get cached demuxer data
- DEBUGLOG("Creating demuxers");
cChannelCache cache = cChannelCache::GetFromCache(m_uid);
+
+ // channel already in cache
if(cache.size() != 0) {
INFOLOG("Channel information found in cache");
+ }
+ // channel not found in cache -> add it from vdr
+ else {
+ INFOLOG("adding channel to cache");
+ cChannelCache::AddToCache(channel);
+ cache = cChannelCache::GetFromCache(m_uid);
+ }
+
+ // recheck cache item
+ cChannelCache currentitem = cChannelCache::ItemFromChannel(channel);
+ if(!currentitem.ismetaof(cache)) {
+ INFOLOG("current channel differs from cache item - updating");
+ cache = currentitem;
+ cChannelCache::AddToCache(m_uid, cache);
+ }
+
+ if(cache.size() != 0) {
+ INFOLOG("Creating demuxers");
cache.CreateDemuxers(this);
- if(!Attach()) {
- INFOLOG("Unable to attach receiver !");
- return XVDR_RET_DATALOCKED;
- }
- RequestStreamChange();
}
- DEBUGLOG("Starting PAT scanner");
- m_Device->AttachFilter(m_PatFilter);
+ RequestStreamChange();
INFOLOG("Successfully switched to channel %i - %s", channel->Number(), channel->Name());
- Start();
+ if(m_waitforiframe) {
+ INFOLOG("Will wait for first I-Frame ...");
+ }
+
+ // clear cached data
+ Clear();
+ m_Queue->Cleanup();
+ m_uid = CreateChannelUID(channel);
+
+ if(!Attach()) {
+ INFOLOG("Unable to attach receiver !");
+ return XVDR_RET_DATALOCKED;
+ }
+
+ INFOLOG("Starting PAT scanner");
+ m_PatFilter = new cLivePatFilter(this);
+ m_PatFilter->SetChannel(channel);
+ m_Device->AttachFilter(m_PatFilter);
+
+ INFOLOG("done switching.");
return XVDR_RET_OK;
}
@@ -310,6 +424,11 @@ void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt)
// Send stream information as the first packet on startup
if (IsStarting() && bReady)
{
+ // wait for AV frames (we start with an audio or video packet)
+ if(!(pkt->content == cStreamInfo::scAUDIO || pkt->content == cStreamInfo::scVIDEO)) {
+ return;
+ }
+
INFOLOG("streaming of channel started");
m_last_tick.Set(0);
m_requestStreamChange = true;
@@ -320,6 +439,13 @@ void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt)
if(m_requestStreamChange)
sendStreamChange();
+ // wait for first I-Frame (if enabled)
+ if(m_waitforiframe && pkt->frametype != cStreamInfo::ftIFRAME) {
+ return;
+ }
+
+ m_waitforiframe = false;
+
// if a audio or video packet was sent, the signal is restored
if(m_SignalLost && (pkt->content == cStreamInfo::scVIDEO || pkt->content == cStreamInfo::scAUDIO)) {
INFOLOG("signal restored");
@@ -345,18 +471,21 @@ void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt)
packet->put_U32(pkt->duration);
}
+ // write frame type into unused header field clientid
+ packet->setClientID((uint16_t)pkt->frametype);
+
// write payload into stream packet
packet->put_U32(pkt->size);
packet->put_Blob(pkt->data, pkt->size);
- m_Queue->Add(packet);
+ m_Queue->Add(packet, pkt->content);
m_last_tick.Set(0);
}
void cLiveStreamer::sendDetach() {
INFOLOG("sending detach message");
MsgPacket* resp = new MsgPacket(XVDR_STREAM_DETACH, XVDR_CHANNEL_STREAM);
- m_Queue->Add(resp);
+ m_parent->QueueMessage(resp);
}
void cLiveStreamer::sendStreamChange()
@@ -429,7 +558,7 @@ void cLiveStreamer::sendStreamChange()
m_FilterMutex.Unlock();
- m_Queue->Add(resp);
+ m_Queue->Add(resp, cStreamInfo::scSTREAMINFO);
m_requestStreamChange = false;
}
@@ -437,11 +566,17 @@ void cLiveStreamer::sendStatus(int status)
{
MsgPacket* packet = new MsgPacket(XVDR_STREAM_STATUS, XVDR_CHANNEL_STREAM);
packet->put_U32(status);
- m_Queue->Add(packet);
+ m_parent->QueueMessage(packet);
}
void cLiveStreamer::RequestSignalInfo()
{
+ cMutexLock lock(&m_DeviceMutex);
+
+ if(!Running() || m_Device == NULL) {
+ return;
+ }
+
// do not send (and pollute the client with) signal information
// if we are paused
if(IsPaused())
@@ -499,8 +634,23 @@ void cLiveStreamer::RequestSignalInfo()
resp->put_U32(0);
resp->put_U32(0);
+ // get provider & service information
+ const cChannel* channel = FindChannelByUID(m_uid);
+ if(channel != NULL) {
+ // put in provider name
+ resp->put_String(channel->Provider());
+
+ // what the heck should be the service name ?
+ // using PortalName for now
+ resp->put_String(channel->PortalName());
+ }
+ else {
+ resp->put_String("");
+ resp->put_String("");
+ }
+
DEBUGLOG("RequestSignalInfo");
- m_Queue->Add(resp);
+ m_Queue->Add(resp, cStreamInfo::scNONE);
}
void cLiveStreamer::reorderStreams(int lang, cStreamInfo::Type type)
@@ -594,24 +744,18 @@ bool cLiveStreamer::IsReady()
if(m_ready)
return true;
- bool bAllParsed = true;
+ cMutexLock lock(&m_FilterMutex);
for (std::list<cTSDemuxer*>::iterator i = m_Demuxers.begin(); i != m_Demuxers.end(); i++)
{
- /*if((*i)->IsParsed() && (*i)->GetContent() == cStreamInfo::scVIDEO) {
- bAllParsed = true;
- break;
- }*/
if (!(*i)->IsParsed()) {
DEBUGLOG("Stream with PID %i not parsed", (*i)->GetPID());
- bAllParsed = false;
- break;
+ return false;
}
}
- m_ready = bAllParsed;
-
- return bAllParsed;
+ m_ready = true;
+ return true;
}
bool cLiveStreamer::IsPaused()
@@ -652,3 +796,15 @@ void cLiveStreamer::Receive(uchar *Data, int Length)
if (p != Length)
ReportOverflow(Length - p);
}
+
+void cLiveStreamer::ChannelChange(const cChannel* channel) {
+ cMutexLock lock(&m_FilterMutex);
+
+ if(CreateChannelUID(channel) != m_uid || !Running()) {
+ return;
+ }
+
+ INFOLOG("ChannelChange()");
+
+ SwitchChannel(channel);
+}
diff --git a/src/live/livestreamer.h b/src/live/livestreamer.h
index af3f004..efdf021 100644
--- a/src/live/livestreamer.h
+++ b/src/live/livestreamer.h
@@ -42,6 +42,7 @@ class cTSDemuxer;
class MsgPacket;
class cLivePatFilter;
class cLiveQueue;
+class cXVDRClient;
class cLiveStreamer : public cThread
, public cRingBufferLinear
@@ -65,7 +66,6 @@ private:
cDevice *m_Device; /*!> The receiving device the channel depents to */
cLivePatFilter *m_PatFilter; /*!> Filter processor to get changed pid's */
- int m_Priority; /*!> The priority over other streamers */
std::list<cTSDemuxer*> m_Demuxers;
bool m_startup;
bool m_requestStreamChange;
@@ -73,12 +73,15 @@ private:
cTimeMs m_last_tick;
bool m_SignalLost;
cMutex m_FilterMutex;
+ cMutex m_DeviceMutex;
int m_LanguageIndex;
cStreamInfo::Type m_LangStreamType;
cLiveQueue* m_Queue;
uint32_t m_uid;
bool m_ready;
uint32_t m_protocolVersion;
+ bool m_waitforiframe;
+ cXVDRClient* m_parent;
protected:
void Action(void);
@@ -86,21 +89,31 @@ protected:
void RequestStreamChange();
+ int SwitchChannel(const cChannel *channel);
+
+private:
+
+ void TryChannelSwitch();
+
public:
- cLiveStreamer(int priority, uint32_t timeout = 0, uint32_t protocolVersion = XVDR_PROTOCOLVERSION);
+ cLiveStreamer(cXVDRClient* parent, const cChannel *channel, int priority);
virtual ~cLiveStreamer();
- int StreamChannel(const cChannel *channel, int sock);
-
bool IsReady();
bool IsStarting() { return m_startup; }
bool IsPaused();
bool TimeShiftMode();
void SetLanguage(int lang, cStreamInfo::Type streamtype = cStreamInfo::stAC3);
+ void SetTimeout(uint32_t timeout);
+ void SetProtocolVersion(uint32_t protocolVersion);
+ void SetWaitForIFrame(bool waitforiframe);
+
void Pause(bool on);
void RequestPacket();
void RequestSignalInfo();
+
+ void ChannelChange(const cChannel* Channel);
};
#endif // XVDR_RECEIVER_H
diff --git a/src/net/socketlock.c b/src/net/socketlock.c
deleted file mode 100644
index 36a7b7d..0000000
--- a/src/net/socketlock.c
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "socketlock.h"
-
-cSocketMutex cSocketLock::m_sockets;
-
-void cSocketLock::erase(int sock) {
- cSocketMutex::iterator i = m_sockets.find(sock);
-
- if(i == m_sockets.end())
- return;
-
- m_sockets.erase(sock);
-}
diff --git a/src/net/socketlock.h b/src/net/socketlock.h
deleted file mode 100644
index b11d72d..0000000
--- a/src/net/socketlock.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef XVDR_SOCKETLOCK_H
-#define XVDR_SOCKETLOCK_H
-
-#include <map>
-#include <vdr/thread.h>
-
-class cSocketMutex : public std::map<int, cMutex> {
-};
-
-class cSocketLock {
-public:
-
- cSocketLock(int sock) : m_socket(sock) {
- m_sockets[m_socket].Lock();
- }
-
- ~cSocketLock() {
- m_sockets[m_socket].Unlock();
- }
-
- static void erase(int sock);
-
-private:
-
- static cSocketMutex m_sockets;
-
- int m_socket;
-};
-
-#endif // XVDR_SOCKETLOCK_H
diff --git a/src/recordings/recordingscache.c b/src/recordings/recordingscache.c
index e27a361..4c853f5 100644
--- a/src/recordings/recordingscache.c
+++ b/src/recordings/recordingscache.c
@@ -23,16 +23,24 @@
*/
#include <stdio.h>
+#define __STDC_FORMAT_MACROS // Required for format specifiers
+#include <inttypes.h>
#include "config/config.h"
#include "recordingscache.h"
#include "tools/hash.h"
-cRecordingsCache::cRecordingsCache() : m_changed(false)
-{
+cRecordingsCache::cRecordingsCache() : m_changed(false) {
+ cMutexLock lock(&m_mutex);
+
// initialize cache
- for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
- Register(recording);
+ Update();
+}
+
+void cRecordingsCache::Update() {
+ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
+ RegisterNoLock(recording);
+ }
}
cRecordingsCache::~cRecordingsCache() {
@@ -46,15 +54,14 @@ cRecordingsCache& cRecordingsCache::GetInstance() {
uint32_t cRecordingsCache::Register(cRecording* recording) {
cMutexLock lock(&m_mutex);
+ return RegisterNoLock(recording);
+}
+
+uint32_t cRecordingsCache::RegisterNoLock(cRecording* recording) {
cString filename = recording->FileName();
uint32_t uid = CreateStringHash(filename);
- if(m_recordings.find(uid) == m_recordings.end())
- {
- DEBUGLOG("%s - uid: %08x '%s'", __FUNCTION__, uid, (const char*)filename);
- m_recordings[uid].filename = filename;
- }
-
+ m_recordings[uid].filename = filename;
return uid;
}
@@ -68,6 +75,12 @@ cRecording* cRecordingsCache::Lookup(uint32_t uid) {
}
cString filename = m_recordings[uid].filename;
+
+ if(isempty(filename)) {
+ DEBUGLOG("%s - empty filename for uid: %08x !", __FUNCTION__, uid);
+ return NULL;
+ }
+
DEBUGLOG("%s - filename: %s", __FUNCTION__, (const char*)filename);
cRecording* r = Recordings.GetByName(filename);
@@ -139,12 +152,8 @@ void cRecordingsCache::LoadResumeData()
uint64_t pos = 0;
int count = 0;
- while(fscanf(f, "%08x = %llu, %i", &uid, &pos, &count) != EOF)
+ while(fscanf(f, "%08x = %"PRIu64", %i", &uid, &pos, &count) != EOF)
{
- // skip unknown entries
- if(m_recordings.find(uid) == m_recordings.end())
- continue;
-
m_recordings[uid].lastplayedposition = pos;
m_recordings[uid].playcount = count;
@@ -174,7 +183,7 @@ void cRecordingsCache::SaveResumeData()
for(i = m_recordings.begin(); i != m_recordings.end(); i++)
{
if(i->second.lastplayedposition != 0 || i->second.playcount != 0)
- fprintf(f, "%08x = %llu, %i\n", i->first, i->second.lastplayedposition, i->second.playcount);
+ fprintf(f, "%08x = %"PRIu64", %i\n", i->first, i->second.lastplayedposition, i->second.playcount);
}
fclose(f);
@@ -190,7 +199,21 @@ bool cRecordingsCache::Changed() {
return rc;
}
-void cRecordingsCache::SetChanged() {
+void cRecordingsCache::gc() {
cMutexLock lock(&m_mutex);
- m_changed = true;
+
+ Update();
+
+ std::map<uint32_t, struct RecEntry>::iterator i = m_recordings.begin();
+
+ while(i != m_recordings.end()) {
+ if(!isempty(i->second.filename) && Recordings.GetByName(i->second.filename) == NULL) {
+ INFOLOG("removing outdated recording (%08x) '%s' from cache", i->first, (const char*)i->second.filename);
+ std::map<uint32_t, struct RecEntry>::iterator n = i++;
+ m_recordings.erase(n);
+ }
+ else {
+ i++;
+ }
+ }
}
diff --git a/src/recordings/recordingscache.h b/src/recordings/recordingscache.h
index d3394fb..a80fe9b 100644
--- a/src/recordings/recordingscache.h
+++ b/src/recordings/recordingscache.h
@@ -61,9 +61,13 @@ public:
bool Changed();
+ void gc();
+
protected:
- void SetChanged();
+ void Update();
+
+ uint32_t RegisterNoLock(cRecording* recording);
private:
diff --git a/src/recordings/recplayer.c b/src/recordings/recplayer.c
index 647d816..6211b52 100644
--- a/src/recordings/recplayer.c
+++ b/src/recordings/recplayer.c
@@ -1,9 +1,8 @@
/*
* vdr-plugin-xvdr - XVDR server plugin for VDR
*
- * Copyright (C) 2004-2005 Chris Tallon
* Copyright (C) 2010 Alwin Esch (Team XBMC)
- * Copyright (C) 2010, 2011 Alexander Pipelka
+ * Copyright (C) 2010-2013 Alexander Pipelka
*
* https://github.com/pipelka/vdr-plugin-xvdr
*
@@ -33,6 +32,8 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
+#define __STDC_FORMAT_MACROS // Required for format specifiers
+#include <inttypes.h>
#include "config/config.h"
@@ -42,19 +43,17 @@
cRecPlayer::cRecPlayer(cRecording* rec)
{
- m_file = -1;
- m_fileOpen = -1;
- m_rescanInterval = 2000; // 2000 ms rescan interval
+ m_file = -1;
+ m_fileOpen = -1;
+ m_rescanInterval = 2000; // 2000 ms rescan interval
m_recordingFilename = strdup(rec->FileName());
+ m_totalLength = 0;
// FIXME find out max file path / name lengths
#if VDRVERSNUM < 10703
m_pesrecording = true;
- m_indexFile = new cIndexFile(m_recordingFilename, false);
#else
m_pesrecording = rec->IsPesRecording();
- if(m_pesrecording) INFOLOG("recording '%s' is a PES recording", m_recordingFilename);
- m_indexFile = new cIndexFile(m_recordingFilename, false, m_pesrecording);
#endif
scan();
@@ -78,17 +77,12 @@ void cRecPlayer::cleanup() {
void cRecPlayer::scan()
{
struct stat s;
-
- closeFile();
-
+ uint64_t len = m_totalLength;
m_totalLength = 0;
- m_fileOpen = -1;
- m_totalFrames = 0;
cleanup();
- for(int i = 0; ; i++) // i think we only need one possible loop
- {
+ for(int i = 0; ; i++) {
fileNameFromIndex(i);
if(stat(m_fileName, &s) == -1) {
@@ -102,11 +96,11 @@ void cRecPlayer::scan()
m_segments.Append(segment);
m_totalLength += s.st_size;
- INFOLOG("File %i found, size: %llu, totalLength now %llu", i, s.st_size, m_totalLength);
}
- m_totalFrames = m_indexFile->Last();
- INFOLOG("total frames: %u", m_totalFrames);
+ if(len != m_totalLength) {
+ INFOLOG("recording scan: %"PRIu64" bytes", m_totalLength);
+ }
}
void cRecPlayer::update()
@@ -118,10 +112,6 @@ void cRecPlayer::update()
DEBUGLOG("%s", __FUNCTION__);
m_rescanTime.Set(0);
- // no change ?
- if(m_totalFrames == (uint32_t)m_indexFile->Last())
- return;
-
scan();
}
@@ -140,15 +130,23 @@ bool cRecPlayer::openFile(int index)
closeFile();
fileNameFromIndex(index);
- INFOLOG("openFile called for index %i string:%s", index, m_fileName);
+ INFOLOG("openFile called for index %i (%s)", index, m_fileName);
+ // first try to open with NOATIME flag
m_file = open(m_fileName, O_RDONLY | O_NOATIME);
- if (m_file == -1)
- {
+
+ // fallback if FS doesn't support NOATIME
+ if (m_file == -1) {
+ m_file = open(m_fileName, O_RDONLY);
+ }
+
+ // failed to open file
+ if (m_file == -1) {
INFOLOG("file failed to open");
m_fileOpen = -1;
return false;
}
+
m_fileOpen = index;
return true;
}
@@ -171,11 +169,6 @@ uint64_t cRecPlayer::getLengthBytes()
return m_totalLength;
}
-uint32_t cRecPlayer::getLengthFrames()
-{
- return m_totalFrames;
-}
-
int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
{
// dont let the block be larger than 256 kb
@@ -212,7 +205,7 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
// seek to position
if(lseek(m_file, filePosition, SEEK_SET) == -1) {
- ERRORLOG("unable to seek to position: %llu", filePosition);
+ ERRORLOG("unable to seek to position: %"PRIu64, filePosition);
return 0;
}
@@ -236,86 +229,3 @@ int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount)
return bytes_read;
}
-
-uint64_t cRecPlayer::positionFromFrameNumber(uint32_t frameNumber)
-{
- if (!m_indexFile) return 0;
-#if VDRVERSNUM < 10703
- unsigned char retFileNumber;
- int retFileOffset;
- unsigned char retPicType;
-#else
- uint16_t retFileNumber;
- off_t retFileOffset;
- bool retPicType;
-#endif
- int retLength;
-
-
- if (!m_indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset, &retPicType, &retLength))
- return 0;
-
- if (retFileNumber >= m_segments.Size())
- return 0;
-
- uint64_t position = m_segments[retFileNumber]->start + retFileOffset;
- return position;
-}
-
-uint32_t cRecPlayer::frameNumberFromPosition(uint64_t position)
-{
- if (!m_indexFile) return 0;
-
- if (position >= m_totalLength)
- {
- DEBUGLOG("Client asked for data starting past end of recording!");
- return m_totalFrames;
- }
-
- int segmentNumber = -1;
- for(int i = 0; i < m_segments.Size(); i++)
- {
- if ((position >= m_segments[i]->start) && (position < m_segments[i]->end)) {
- segmentNumber = i;
- break;
- }
- }
-
- if(segmentNumber == -1) {
- return m_totalFrames;
- }
-
- uint32_t askposition = position - m_segments[segmentNumber]->start;
- return m_indexFile->Get((int)segmentNumber, askposition);
-}
-
-
-bool cRecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength)
-{
- // 0 = backwards
- // 1 = forwards
-
- if (!m_indexFile) return false;
-
-#if VDRVERSNUM < 10703
- unsigned char waste1;
- int waste2;
-#else
- uint16_t waste1;
- off_t waste2;
-#endif
-
- int iframeLength;
- int indexReturnFrameNumber;
-
- indexReturnFrameNumber = (uint32_t)m_indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), &waste1, &waste2, &iframeLength);
- DEBUGLOG("GNIF input framenumber:%u, direction=%u, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength);
-
- if (indexReturnFrameNumber == -1) return false;
-
- *rfilePosition = positionFromFrameNumber(indexReturnFrameNumber);
- *rframeNumber = (uint32_t)indexReturnFrameNumber;
- *rframeLength = (uint32_t)iframeLength;
-
- return true;
-}
diff --git a/src/recordings/recplayer.h b/src/recordings/recplayer.h
index b6e1beb..481c4fe 100644
--- a/src/recordings/recplayer.h
+++ b/src/recordings/recplayer.h
@@ -32,8 +32,8 @@
#define XVDR_RECPLAYER_H
#include <stdio.h>
-#include <vdr/recording.h>
#include <vdr/tools.h>
+#include <vdr/recording.h>
class cSegment
{
@@ -42,41 +42,50 @@ class cSegment
uint64_t end;
};
-class cRecPlayer
-{
+class cRecPlayer {
public:
+
cRecPlayer(cRecording* rec);
+
~cRecPlayer();
+
uint64_t getLengthBytes();
- uint32_t getLengthFrames();
+
int getBlock(unsigned char* buffer, uint64_t position, int amount);
bool openFile(int index);
+
void closeFile();
void scan();
- void update();
- uint64_t positionFromFrameNumber(uint32_t frameNumber);
- uint32_t frameNumberFromPosition(uint64_t position);
- bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
+ void update();
private:
+
void cleanup();
+
char* fileNameFromIndex(int index);
+
void checkBufferSize(int s);
- char m_fileName[512];
- cIndexFile *m_indexFile;
- int m_file;
- int m_fileOpen;
+ bool m_pesrecording;
+
+ char m_fileName[512];
+
+ int m_file;
+
+ int m_fileOpen;
+
cVector<cSegment*> m_segments;
- uint64_t m_totalLength;
- uint32_t m_totalFrames;
- char *m_recordingFilename;
- bool m_pesrecording;
- cTimeMs m_rescanTime;
- uint32_t m_rescanInterval;
+
+ uint64_t m_totalLength;
+
+ char* m_recordingFilename;
+
+ cTimeMs m_rescanTime;
+
+ uint32_t m_rescanInterval;
};
#endif // XVDR_RECPLAYER_H
diff --git a/src/tools/hash.c b/src/tools/hash.c
index d78178c..65f746f 100644
--- a/src/tools/hash.c
+++ b/src/tools/hash.c
@@ -115,3 +115,30 @@ const cChannel* FindChannelByUID(uint32_t channelUID) {
XVDRChannels.Unlock();
return result;
}
+
+uint32_t CreateTimerUID(const cTimer* timer) {
+ cString timerid = cString::sprintf("%s:%s:%04d:%04d:%s",
+ *timer->Channel()->GetChannelID().ToString(),
+ *timer->PrintDay(timer->Day(), timer->WeekDays(), true),
+ timer->Start(),
+ timer->Stop(),
+ timer->File());
+
+ return CreateStringHash(timerid);
+}
+
+cTimer* FindTimerByUID(uint32_t timerUID) {
+ int numTimers = Timers.Count();
+
+ for (int i = 0; i < numTimers; i++) {
+ cTimer* timer = Timers.Get(i);
+ if (!timer)
+ continue;
+
+ if(CreateTimerUID(timer) == timerUID) {
+ return timer;
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/tools/hash.h b/src/tools/hash.h
index 985e1f3..1a3f334 100644
--- a/src/tools/hash.h
+++ b/src/tools/hash.h
@@ -28,12 +28,14 @@
#include <stdint.h>
#include <vdr/channels.h>
-
-class cChannel;
+#include <vdr/timers.h>
uint32_t CreateChannelUID(const cChannel* channel);
const cChannel* FindChannelByUID(uint32_t channelUID);
+uint32_t CreateTimerUID(const cTimer* channel);
+cTimer* FindTimerByUID(uint32_t timerUID);
+
uint32_t CreateStringHash(const cString& string);
#endif // XVDR_HASH_H
diff --git a/src/xvdr/timerconflicts.c b/src/xvdr/timerconflicts.c
new file mode 100644
index 0000000..57a7c66
--- /dev/null
+++ b/src/xvdr/timerconflicts.c
@@ -0,0 +1,120 @@
+/*
+ * vdr-plugin-xvdr - XVDR server plugin for VDR
+ *
+ * Copyright (C) 2013 Alexander Pipelka
+ *
+ * https://github.com/pipelka/vdr-plugin-xvdr
+ *
+ * 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 this program; if not, write to the Free Software
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <map>
+#include <set>
+
+#include <vdr/timers.h>
+#include <vdr/device.h>
+#include "config/config.h"
+#include "timerconflicts.h"
+#include "xvdrchannels.h"
+
+int CheckTimerConflicts(cTimer* timer) {
+ XVDRChannels.Lock(false);
+
+ // check for timer conflicts
+ DEBUGLOG("Checking conflicts for: %s", (const char*)timer->ToText(true));
+
+ // order active timers by starttime
+ std::map<time_t, cTimer*> timeline;
+ int numTimers = Timers.Count();
+ for (int i = 0; i < numTimers; i++)
+ {
+ cTimer* t = Timers.Get(i);
+
+ // same timer -> skip
+ if (!t || timer->Index() == i)
+ continue;
+
+ // timer not active -> skip
+ if(!(t->Flags() & tfActive))
+ continue;
+
+ // this one is earlier -> no match
+ if(t->StopTime() <= timer->StartTime())
+ continue;
+
+ // this one is later -> no match
+ if(t->StartTime() >= timer->StopTime())
+ continue;
+
+ timeline[t->StartTime()] = t;
+ }
+
+ std::set<int> transponders;
+ transponders.insert(timer->Channel()->Transponder()); // we also count ourself
+ cTimer* to_check = timer;
+
+ std::map<time_t, cTimer*>::iterator i;
+ for (i = timeline.begin(); i != timeline.end(); i++) {
+ cTimer* t = i->second;
+
+ // this one is earlier -> no match
+ if(t->StopTime() <= to_check->StartTime())
+ continue;
+
+ // this one is later -> no match
+ if(t->StartTime() >= to_check->StopTime())
+ continue;
+
+ // same transponder -> no conflict
+ if(t->Channel()->Transponder() == to_check->Channel()->Transponder())
+ continue;
+
+ // different source -> no conflict
+ if(t->Channel()->Source() != to_check->Channel()->Source())
+ continue;
+
+ DEBUGLOG("Possible conflict: %s", (const char*)t->ToText(true));
+ transponders.insert(t->Channel()->Transponder());
+
+ // now check conflicting timer
+ to_check = t;
+ }
+
+ uint32_t number_of_devices_for_this_channel = 0;
+ for(int i = 0; i < cDevice::NumDevices(); i++) {
+ cDevice* device = cDevice::GetDevice(i);
+ if(device != NULL && device->ProvidesTransponder(timer->Channel()))
+ number_of_devices_for_this_channel++;
+ }
+
+ int cflags = 0;
+ if(transponders.size() > number_of_devices_for_this_channel) {
+ DEBUGLOG("ERROR - Not enough devices");
+ cflags += 2048;
+ }
+ else if(transponders.size() > 1) {
+ DEBUGLOG("Overlapping timers - Will record");
+ cflags += 1024;
+ }
+ else {
+ DEBUGLOG("No conflicts");
+ }
+
+ XVDRChannels.Unlock();
+
+ return cflags;
+}
diff --git a/src/tools/hash.h b/src/xvdr/timerconflicts.h
similarity index 69%
copy from src/tools/hash.h
copy to src/xvdr/timerconflicts.h
index 985e1f3..4c298d2 100644
--- a/src/tools/hash.h
+++ b/src/xvdr/timerconflicts.h
@@ -1,8 +1,7 @@
/*
* vdr-plugin-xvdr - XVDR server plugin for VDR
*
- * Copyright (C) 1986 Gary S. Brown (CRC32 code)
- * Copyright (C) 2011 Alexander Pipelka
+ * Copyright (C) 2013 Alexander Pipelka
*
* https://github.com/pipelka/vdr-plugin-xvdr
*
@@ -23,17 +22,9 @@
*
*/
-#ifndef XVDR_HASH_H
-#define XVDR_HASH_H
+#ifndef XVDR_TIMERCONFLICTS_H
+#define XVDR_TIMERCONFLICTS_H
-#include <stdint.h>
-#include <vdr/channels.h>
+int CheckTimerConflicts(cTimer* timer);
-class cChannel;
-
-uint32_t CreateChannelUID(const cChannel* channel);
-const cChannel* FindChannelByUID(uint32_t channelUID);
-
-uint32_t CreateStringHash(const cString& string);
-
-#endif // XVDR_HASH_H
+#endif // XVDR_TIMERCONFLICTS_H
diff --git a/src/xvdr/xvdrclient.c b/src/xvdr/xvdrclient.c
index 7ae64c0..ce249bc 100644
--- a/src/xvdr/xvdrclient.c
+++ b/src/xvdr/xvdrclient.c
@@ -26,7 +26,8 @@
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
-#include <set>
+#include <unistd.h>
+#include <sys/types.h>
#include <map>
#include <string>
@@ -43,7 +44,6 @@
#include "config/config.h"
#include "live/livestreamer.h"
#include "net/msgpacket.h"
-#include "net/socketlock.h"
#include "recordings/recordingscache.h"
#include "recordings/recplayer.h"
#include "tools/hash.h"
@@ -52,6 +52,7 @@
#include "xvdrcommand.h"
#include "xvdrclient.h"
#include "xvdrserver.h"
+#include "timerconflicts.h"
static bool IsRadio(const cChannel* channel)
@@ -83,6 +84,11 @@ cString cXVDRClient::CreateServiceReference(cChannel* channel)
if(cSource::IsSat(channel->Source()))
{
hash = channel->Source() & cSource::st_Pos;
+
+#if VDRVERSNUM >= 20101
+ hash = -hash;
+#endif
+
if(hash > 0x00007FFF)
hash |= 0xFFFF0000;
@@ -98,10 +104,10 @@ cString cXVDRClient::CreateServiceReference(cChannel* channel)
else if(cSource::IsTerr(channel->Source()))
hash = 0xEEEE0000;
else if(cSource::IsAtsc(channel->Source()))
- ; // how should we handle ATSC ?
+ hash = 0xDDDD0000;
cString serviceref = cString::sprintf("1_0_%i_%X_%X_%X_%X_0_0_0",
- (channel->Vpid() == 0) ? 2 : (channel->Vtype() == 27) ? 19 : 1,
+ IsRadio(channel) ? 2 : (channel->Vtype() == 27) ? 19 : 1,
channel->Sid(),
channel->Tid(),
channel->Nid(),
@@ -121,95 +127,10 @@ cString cXVDRClient::CreateLogoURL(cChannel* channel)
void cXVDRClient::PutTimer(cTimer* timer, MsgPacket* p)
{
- XVDRChannels.Lock(false);
-
- // check for conflicts
- DEBUGLOG("Checking conflicts for: %s", (const char*)timer->ToText(true));
-
- // order active timers by starttime
- std::map<time_t, cTimer*> timeline;
- int numTimers = Timers.Count();
- for (int i = 0; i < numTimers; i++)
- {
- cTimer* t = Timers.Get(i);
-
- // same timer -> skip
- if (!t || timer->Index() == i)
- continue;
-
- // timer not active -> skip
- if(!(t->Flags() & tfActive))
- continue;
-
- // this one is earlier -> no match
- if(t->StopTime() <= timer->StartTime())
- continue;
-
- // this one is later -> no match
- if(t->StartTime() >= timer->StopTime())
- continue;
-
- timeline[t->StartTime()] = t;
- }
-
- std::set<int> transponders;
- transponders.insert(timer->Channel()->Transponder()); // we also count ourself
- cTimer* to_check = timer;
-
- std::map<time_t, cTimer*>::iterator i;
- for (i = timeline.begin(); i != timeline.end(); i++)
- {
- cTimer* t = i->second;
-
- // this one is earlier -> no match
- if(t->StopTime() <= to_check->StartTime())
- continue;
+ int flags = CheckTimerConflicts(timer);
- // this one is later -> no match
- if(t->StartTime() >= to_check->StopTime())
- continue;
-
- // same transponder -> no conflict
- if(t->Channel()->Transponder() == to_check->Channel()->Transponder())
- continue;
-
- // different source -> no conflict
- if(t->Channel()->Source() != to_check->Channel()->Source())
- continue;
-
- DEBUGLOG("Possible conflict: %s", (const char*)t->ToText(true));
- transponders.insert(t->Channel()->Transponder());
-
- // now check conflicting timer
- to_check = t;
- }
-
- uint32_t number_of_devices_for_this_channel = 0;
- for(int i = 0; i < cDevice::NumDevices(); i++)
- {
- cDevice* device = cDevice::GetDevice(i);
- if(device != NULL && device->ProvidesTransponder(timer->Channel()))
- number_of_devices_for_this_channel++;
- }
-
- int cflags = 0;
- if(transponders.size() > number_of_devices_for_this_channel)
- {
- DEBUGLOG("ERROR - Not enough devices");
- cflags += 2048;
- }
- else if(transponders.size() > 1)
- {
- DEBUGLOG("Overlapping timers - Will record");
- cflags += 1024;
- }
- else
- DEBUGLOG("No conflicts");
-
- XVDRChannels.Unlock();
-
- p->put_U32(timer->Index()+1);
- p->put_U32(timer->Flags() | cflags);
+ p->put_U32(CreateTimerUID(timer));
+ p->put_U32(timer->Flags() | flags);
p->put_U32(timer->Priority());
p->put_U32(timer->Lifetime());
p->put_U32(CreateChannelUID(timer->Channel()));
@@ -221,14 +142,12 @@ void cXVDRClient::PutTimer(cTimer* timer, MsgPacket* p)
}
cMutex cXVDRClient::m_timerLock;
-cMutex cXVDRClient::m_switchLock;
cXVDRClient::cXVDRClient(int fd, unsigned int id)
{
m_Id = id;
m_loggedIn = false;
m_Streamer = NULL;
- m_isStreaming = false;
m_StatusInterfaceEnabled = false;
m_RecPlayer = NULL;
m_req = NULL;
@@ -238,12 +157,15 @@ cXVDRClient::cXVDRClient(int fd, unsigned int id)
m_LangStreamType = cStreamInfo::stMPEG2AUDIO;
m_channelCount = 0;
m_timeout = 3000;
+ m_scanSupported = false;
m_socket = fd;
m_wantfta = true;
m_filterlanguage = false;
Start();
+
+ m_scanSupported = m_scanner.Connect();
}
cXVDRClient::~cXVDRClient()
@@ -255,11 +177,22 @@ cXVDRClient::~cXVDRClient()
shutdown(m_socket, SHUT_RDWR);
Cancel(10);
- // remove socket lock
- cSocketLock::erase(m_socket);
-
// close connection
close(m_socket);
+
+ // remove recplayer
+ delete m_RecPlayer;
+
+ // delete messagequeue
+ {
+ cMutexLock lock(&m_queueLock);
+ while(!m_queue.empty()) {
+ MsgPacket* p = m_queue.front();
+ m_queue.pop();
+ delete p;
+ }
+ }
+
DEBUGLOG("done");
}
@@ -267,9 +200,29 @@ void cXVDRClient::Action(void)
{
bool bClosed(false);
- SetPriority(10);
+ // only root may change the priority
+ if(geteuid() == 0) {
+ SetPriority(10);
+ }
while (Running()) {
+
+ // send pending messages
+ {
+ cMutexLock lock(&m_queueLock);
+
+ while(!m_queue.empty()) {
+ MsgPacket* p = m_queue.front();
+
+ if(!p->write(m_socket, m_timeout)) {
+ break;
+ }
+
+ m_queue.pop();
+ delete p;
+ }
+ }
+
m_req = MsgPacket::read(m_socket, bClosed, 1000);
if(bClosed) {
@@ -292,31 +245,47 @@ void cXVDRClient::Action(void)
StopChannelStreaming();
}
-int cXVDRClient::StartChannelStreaming(const cChannel *channel, uint32_t timeout, int32_t priority)
+int cXVDRClient::StartChannelStreaming(const cChannel *channel, uint32_t timeout, int32_t priority, bool waitforiframe)
{
- cMutexLock lock(&m_switchLock);
- m_Streamer = new cLiveStreamer(priority, timeout, m_protocolVersion);
+ cMutexLock lock(&m_streamerLock);
+
+ m_Streamer = new cLiveStreamer(this, channel, priority);
m_Streamer->SetLanguage(m_LanguageIndex, m_LangStreamType);
+ m_Streamer->SetTimeout(timeout);
+ m_Streamer->SetProtocolVersion(m_protocolVersion);
+ m_Streamer->SetWaitForIFrame(waitforiframe);
- return m_Streamer->StreamChannel(channel, m_socket);
+ return XVDR_RET_OK;
}
void cXVDRClient::StopChannelStreaming()
{
- cMutexLock lock(&m_switchLock);
+ cMutexLock lock(&m_streamerLock);
+
delete m_Streamer;
m_Streamer = NULL;
- m_isStreaming = false;
}
void cXVDRClient::TimerChange(const cTimer *Timer, eTimerChange Change)
{
- if(Change != tcAdd && Change != tcDel)
+ // ignore invalid timers
+ if(Timer == NULL) {
return;
+ }
TimerChange();
}
+void cXVDRClient::ChannelChange(const cChannel *Channel) {
+ cMutexLock lock(&m_streamerLock);
+
+ if(m_Streamer == NULL) {
+ return;
+ }
+
+ m_Streamer->ChannelChange(Channel);
+}
+
void cXVDRClient::TimerChange()
{
cMutexLock lock(&m_msgLock);
@@ -324,14 +293,12 @@ void cXVDRClient::TimerChange()
if (m_StatusInterfaceEnabled)
{
INFOLOG("Sending timer change request to client #%i ...", m_Id);
- cSocketLock locks(m_socket);
MsgPacket* resp = new MsgPacket(XVDR_STATUS_TIMERCHANGE, XVDR_CHANNEL_STATUS);
- resp->write(m_socket, m_timeout);
- delete resp;
+ QueueMessage(resp);
}
}
-void cXVDRClient::ChannelChange()
+void cXVDRClient::ChannelsChanged()
{
cMutexLock lock(&m_msgLock);
@@ -350,10 +317,8 @@ void cXVDRClient::ChannelChange()
else
INFOLOG("Client %i : %i channels, %i available - sending request", m_Id, m_channelCount, count);
- cSocketLock locks(m_socket);
MsgPacket* resp = new MsgPacket(XVDR_STATUS_CHANNELCHANGE, XVDR_CHANNEL_STATUS);
- resp->write(m_socket, m_timeout);
- delete resp;
+ QueueMessage(resp);
}
void cXVDRClient::RecordingsChange()
@@ -363,10 +328,8 @@ void cXVDRClient::RecordingsChange()
if (!m_StatusInterfaceEnabled)
return;
- cSocketLock locks(m_socket);
MsgPacket* resp = new MsgPacket(XVDR_STATUS_RECORDINGSCHANGE, XVDR_CHANNEL_STATUS);
- resp->write(m_socket, m_timeout);
- delete resp;
+ QueueMessage(resp);
}
void cXVDRClient::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
@@ -375,7 +338,6 @@ void cXVDRClient::Recording(const cDevice *Device, const char *Name, const char
if (m_StatusInterfaceEnabled)
{
- cSocketLock locks(m_socket);
MsgPacket* resp = new MsgPacket(XVDR_STATUS_RECORDING, XVDR_CHANNEL_STATUS);
resp->put_U32(Device->CardIndex());
@@ -390,8 +352,7 @@ void cXVDRClient::Recording(const cDevice *Device, const char *Name, const char
else
resp->put_String("");
- resp->write(m_socket, m_timeout);
- delete resp;
+ QueueMessage(resp);
}
}
@@ -423,15 +384,17 @@ void cXVDRClient::OsdStatusMessage(const char *Message)
else if (strcasecmp(Message, trVDR("Cutter already running - Add to cutting queue?")) == 0) return;
else if (strcasecmp(Message, trVDR("No index-file found. Creating may take minutes. Create one?")) == 0) return;
- cSocketLock locks(m_socket);
- MsgPacket* resp = new MsgPacket(XVDR_STATUS_MESSAGE, XVDR_CHANNEL_STATUS);
+ StatusMessage(Message);
+ }
+}
- resp->put_U32(0);
- resp->put_String(Message);
+void cXVDRClient::StatusMessage(const char *Message) {
+ MsgPacket* resp = new MsgPacket(XVDR_STATUS_MESSAGE, XVDR_CHANNEL_STATUS);
- resp->write(m_socket, m_timeout);
- delete resp;
- }
+ resp->put_U32(0);
+ resp->put_String(Message);
+
+ QueueMessage(resp);
}
bool cXVDRClient::IsChannelWanted(cChannel* channel, bool radio)
@@ -582,18 +545,6 @@ bool cXVDRClient::processRequest()
result = processRecStream_Update();
break;
- case XVDR_RECSTREAM_POSTOFRAME:
- result = processRecStream_PositionFromFrameNumber();
- break;
-
- case XVDR_RECSTREAM_FRAMETOPOS:
- result = processRecStream_FrameNumberFromPosition();
- break;
-
- case XVDR_RECSTREAM_GETIFRAME:
- result = processRecStream_GetIFrame();
- break;
-
/** OPCODE 60 - 79: XVDR network functions for channel access */
case XVDR_CHANNELS_GETCOUNT:
@@ -675,6 +626,10 @@ bool cXVDRClient::processRequest()
result = processRECORDINGS_GetPosition();
break;
+ case XVDR_RECORDINGS_GETMARKS:
+ result = processRECORDINGS_GetMarks();
+ break;
+
/** OPCODE 120 - 139: XVDR network functions for epg access and manipulating */
case XVDR_EPG_GETFORCHANNEL:
@@ -713,11 +668,9 @@ bool cXVDRClient::processRequest()
if(result)
{
- cSocketLock locks(m_socket);
- m_resp->write(m_socket, m_timeout);
+ QueueMessage(m_resp);
}
- delete m_resp;
m_resp = NULL;
return result;
@@ -728,9 +681,9 @@ bool cXVDRClient::processRequest()
bool cXVDRClient::process_Login() /* OPCODE 1 */
{
- m_protocolVersion = m_req->getProtocolVersion();
- m_compressionLevel = m_req->get_U8();
- const char *clientName = m_req->get_String();
+ m_protocolVersion = m_req->getProtocolVersion();
+ m_compressionLevel = m_req->get_U8();
+ m_clientName = m_req->get_String();
const char *language = NULL;
// get preferred language
@@ -743,11 +696,11 @@ bool cXVDRClient::process_Login() /* OPCODE 1 */
if (m_protocolVersion > XVDR_PROTOCOLVERSION || m_protocolVersion < 4)
{
- ERRORLOG("Client '%s' has unsupported protocol version '%u', terminating client", clientName, m_protocolVersion);
+ ERRORLOG("Client '%s' has unsupported protocol version '%u', terminating client", m_clientName.c_str(), m_protocolVersion);
return false;
}
- INFOLOG("Welcome client '%s' with protocol version '%u'", clientName, m_protocolVersion);
+ INFOLOG("Welcome client '%s' with protocol version '%u'", m_clientName.c_str(), m_protocolVersion);
if(!m_LanguageIndex != -1) {
INFOLOG("Preferred language: %s / type: %i", I18nLanguageCode(m_LanguageIndex), (int)m_LangStreamType);
@@ -847,15 +800,24 @@ bool cXVDRClient::process_ChannelFilter()
bool cXVDRClient::processChannelStream_Open() /* OPCODE 20 */
{
cMutexLock lock(&m_timerLock);
- SetPriority(-15);
+
+ // only root may change the priority
+ if(geteuid() == 0) {
+ SetPriority(-15);
+ }
uint32_t uid = m_req->get_U32();
int32_t priority = 50;
+ bool waitforiframe = false;
if(!m_req->eop()) {
priority = m_req->get_S32();
}
+ if(!m_req->eop()) {
+ waitforiframe = m_req->get_U8();
+ }
+
uint32_t timeout = XVDRServerConfig.stream_timeout;
StopChannelStreaming();
@@ -878,12 +840,15 @@ bool cXVDRClient::processChannelStream_Open() /* OPCODE 20 */
}
else
{
- int status = StartChannelStreaming(channel, timeout, priority);
+ int status = StartChannelStreaming(channel, timeout, priority, waitforiframe);
- if (status == XVDR_RET_OK)
+ if (status == XVDR_RET_OK) {
+ INFOLOG("--------------------------------------");
INFOLOG("Started streaming of channel %s (timeout %i seconds, priority %i)", channel->Name(), timeout, priority);
- else
+ }
+ else {
DEBUGLOG("Can't stream channel %s", channel->Name());
+ }
m_resp->put_U32(status);
}
@@ -930,7 +895,11 @@ bool cXVDRClient::processChannelStream_Signal() /* OPCODE 24 */
bool cXVDRClient::processRecStream_Open() /* OPCODE 40 */
{
cRecording *recording = NULL;
- SetPriority(-15);
+
+ // only root may change the priority
+ if(geteuid() == 0) {
+ SetPriority(-15);
+ }
const char* recid = m_req->get_String();
unsigned int uid = recid2uid(recid);
@@ -942,7 +911,7 @@ bool cXVDRClient::processRecStream_Open() /* OPCODE 40 */
m_RecPlayer = new cRecPlayer(recording);
m_resp->put_U32(XVDR_RET_OK);
- m_resp->put_U32(m_RecPlayer->getLengthFrames());
+ m_resp->put_U32(0);
m_resp->put_U64(m_RecPlayer->getLengthBytes());
#if VDRVERSNUM < 10703
@@ -979,7 +948,7 @@ bool cXVDRClient::processRecStream_Update() /* OPCODE 46 */
return false;
m_RecPlayer->update();
- m_resp->put_U32(m_RecPlayer->getLengthFrames());
+ m_resp->put_U32(0);
m_resp->put_U64(m_RecPlayer->getLengthBytes());
return true;
@@ -987,12 +956,6 @@ bool cXVDRClient::processRecStream_Update() /* OPCODE 46 */
bool cXVDRClient::processRecStream_GetBlock() /* OPCODE 42 */
{
- if (m_isStreaming)
- {
- ERRORLOG("Get block called during live streaming");
- return false;
- }
-
if (!m_RecPlayer)
{
ERRORLOG("Get block called when no recording open");
@@ -1012,59 +975,6 @@ bool cXVDRClient::processRecStream_GetBlock() /* OPCODE 42 */
return true;
}
-bool cXVDRClient::processRecStream_PositionFromFrameNumber() /* OPCODE 43 */
-{
- uint64_t retval = 0;
- uint32_t frameNumber = m_req->get_U32();
-
- if (m_RecPlayer)
- retval = m_RecPlayer->positionFromFrameNumber(frameNumber);
-
- m_resp->put_U64(retval);
-
- return true;
-}
-
-bool cXVDRClient::processRecStream_FrameNumberFromPosition() /* OPCODE 44 */
-{
- uint32_t retval = 0;
- uint64_t position = m_req->get_U64();
-
- if (m_RecPlayer)
- retval = m_RecPlayer->frameNumberFromPosition(position);
-
- m_resp->put_U32(retval);
-
- return true;
-}
-
-bool cXVDRClient::processRecStream_GetIFrame() /* OPCODE 45 */
-{
- bool success = false;
- uint32_t frameNumber = m_req->get_U32();
- uint32_t direction = m_req->get_U32();
- uint64_t rfilePosition = 0;
- uint32_t rframeNumber = 0;
- uint32_t rframeLength = 0;
-
- if (m_RecPlayer)
- success = m_RecPlayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
-
- // returns file position, frame number, length
- if (success)
- {
- m_resp->put_U64(rfilePosition);
- m_resp->put_U32(rframeNumber);
- m_resp->put_U32(rframeLength);
- }
- else
- {
- m_resp->put_U32(0);
- }
-
- return true;
-}
-
int cXVDRClient::ChannelsCount()
{
XVDRChannels.Lock(false);
@@ -1297,6 +1207,13 @@ bool cXVDRClient::processTIMER_GetList() /* OPCODE 82 */
{
cMutexLock lock(&m_timerLock);
+ if (Timers.BeingEdited())
+ {
+ ERRORLOG("Unable to delete timer - timers being edited at VDR");
+ m_resp->put_U32(XVDR_RET_DATALOCKED);
+ return true;
+ }
+
cTimer *timer;
int numTimers = Timers.Count();
@@ -1318,6 +1235,13 @@ bool cXVDRClient::processTIMER_Add() /* OPCODE 83 */
{
cMutexLock lock(&m_timerLock);
+ if (Timers.BeingEdited())
+ {
+ ERRORLOG("Unable to add timer - timers being edited at VDR");
+ m_resp->put_U32(XVDR_RET_DATALOCKED);
+ return true;
+ }
+
m_req->get_U32(); // index unused
uint32_t flags = m_req->get_U32() > 0 ? tfActive : tfNone;
uint32_t priority = m_req->get_U32();
@@ -1385,19 +1309,12 @@ bool cXVDRClient::processTIMER_Delete() /* OPCODE 84 */
{
cMutexLock lock(&m_timerLock);
- uint32_t number = m_req->get_U32();
+ uint32_t uid = m_req->get_U32();
bool force = m_req->get_U32();
- if (number <= 0 || number > (uint32_t)Timers.Count())
- {
- ERRORLOG("Unable to delete timer - invalid timer identifier");
- m_resp->put_U32(XVDR_RET_DATAINVALID);
- return true;
- }
+ cTimer* timer = FindTimerByUID(uid);
- cTimer *timer = Timers.Get(number-1);
- if (timer == NULL)
- {
+ if (timer == NULL) {
ERRORLOG("Unable to delete timer - invalid timer identifier");
m_resp->put_U32(XVDR_RET_DATAINVALID);
return true;
@@ -1412,7 +1329,7 @@ bool cXVDRClient::processTIMER_Delete() /* OPCODE 84 */
if (timer->Recording() && !force)
{
- ERRORLOG("Timer \"%i\" is recording and can be deleted (use force=1 to stop it)", number);
+ ERRORLOG("Timer is recording and can be deleted (use force to stop it)");
m_resp->put_U32(XVDR_RET_RECRUNNING);
return true;
}
@@ -1432,20 +1349,19 @@ bool cXVDRClient::processTIMER_Update() /* OPCODE 85 */
{
cMutexLock lock(&m_timerLock);
- uint32_t index = m_req->get_U32();
- bool active = m_req->get_U32();
+ uint32_t uid = m_req->get_U32();
+ bool active = m_req->get_U32();
- cTimer *timer = Timers.Get(index - 1);
- if (!timer)
- {
- ERRORLOG("Timer \"%u\" not defined", index);
+ cTimer* timer = FindTimerByUID(uid);
+ if(timer == NULL) {
+ ERRORLOG("Timer not defined");
m_resp->put_U32(XVDR_RET_DATAUNKNOWN);
return true;
}
if(timer->Recording())
{
- INFOLOG("Will not update timer #%i - currently recording", index);
+ INFOLOG("Will not update timer - currently recording");
m_resp->put_U32(XVDR_RET_OK);
return true;
}
@@ -1491,6 +1407,7 @@ bool cXVDRClient::processTIMER_Update() /* OPCODE 85 */
*timer = t;
Timers.SetModified();
+ TimerChange();
m_resp->put_U32(XVDR_RET_OK);
@@ -1503,7 +1420,11 @@ bool cXVDRClient::processTIMER_Update() /* OPCODE 85 */
bool cXVDRClient::processRECORDINGS_GetDiskSpace() /* OPCODE 100 */
{
int FreeMB;
+#if VDRVERSNUM >= 20102
+ int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB);
+#else
int Percent = VideoDiskSpace(&FreeMB);
+#endif
int Total = (FreeMB / (100 - Percent)) * 100;
m_resp->put_U32(Total);
@@ -1770,6 +1691,53 @@ bool cXVDRClient::processRECORDINGS_GetPosition()
return true;
}
+bool cXVDRClient::processRECORDINGS_GetMarks() {
+#if VDRVERSNUM < 10732
+ m_resp->put_U32(XVDR_RET_NOTSUPPORTED);
+ return true;
+#endif
+
+ const char* recid = m_req->get_String();
+ uint32_t uid = recid2uid(recid);
+
+ cRecording* recording = cRecordingsCache::GetInstance().Lookup(uid);
+
+ if (recording == NULL) {
+ ERRORLOG("GetMarks: recording not found !");
+ m_resp->put_U32(XVDR_RET_DATAUNKNOWN);
+ return true;
+ }
+
+ cMarks marks;
+ if(!marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording())) {
+ INFOLOG("no marks found for: '%s'", recording->FileName());
+ m_resp->put_U32(XVDR_RET_NOTSUPPORTED);
+ return true;
+ }
+
+ m_resp->put_U32(XVDR_RET_OK);
+
+ m_resp->put_U64(recording->FramesPerSecond() * 10000);
+
+#if VDRVERSNUM >= 10732
+
+ cMark* end = NULL;
+ cMark* begin = NULL;
+
+ while((begin = marks.GetNextBegin(end)) != NULL) {
+ end = marks.GetNextEnd(begin);
+ if(end != NULL) {
+ m_resp->put_String("SCENE");
+ m_resp->put_U64(begin->Position());
+ m_resp->put_U64(end->Position());
+ m_resp->put_String(begin->ToText());
+ }
+ }
+#endif
+
+ return true;
+}
+
/** OPCODE 120 - 139: XVDR network functions for epg access and manipulating */
@@ -1893,7 +1861,7 @@ bool cXVDRClient::processEPG_GetForChannel() /* OPCODE 120 */
bool cXVDRClient::processSCAN_ScanSupported() /* OPCODE 140 */
{
- if(m_scanner.Connect()) {
+ if(m_scanSupported) {
m_resp->put_U32(XVDR_RET_OK);
}
else {
@@ -2080,6 +2048,11 @@ void cXVDRClient::SendScannerStatus() {
resp->put_String(status.transponder);
resp->compress(m_compressionLevel);
- resp->write(m_socket, m_timeout);
- delete resp;
+
+ QueueMessage(resp);
+}
+
+void cXVDRClient::QueueMessage(MsgPacket* p) {
+ cMutexLock lock(&m_queueLock);
+ m_queue.push(p);
}
diff --git a/src/xvdr/xvdrclient.h b/src/xvdr/xvdrclient.h
index 56360f4..a06ac94 100644
--- a/src/xvdr/xvdrclient.h
+++ b/src/xvdr/xvdrclient.h
@@ -29,6 +29,8 @@
#include <map>
#include <list>
#include <string>
+#include <queue>
+#include <set>
#include <vdr/thread.h>
#include <vdr/tools.h>
@@ -55,15 +57,14 @@ private:
bool m_loggedIn;
bool m_StatusInterfaceEnabled;
cLiveStreamer *m_Streamer;
- bool m_isStreaming;
cRecPlayer *m_RecPlayer;
MsgPacket *m_req;
MsgPacket *m_resp;
cCharSetConv m_toUTF8;
uint32_t m_protocolVersion;
cMutex m_msgLock;
+ cMutex m_streamerLock;
static cMutex m_timerLock;
- static cMutex m_switchLock;
int m_compressionLevel;
int m_LanguageIndex;
cStreamInfo::Type m_LangStreamType;
@@ -73,6 +74,11 @@ private:
int m_channelCount;
int m_timeout;
cWirbelScan m_scanner;
+ bool m_scanSupported;
+ std::string m_clientName;
+
+ std::queue<MsgPacket*> m_queue;
+ cMutex m_queueLock;
protected:
@@ -81,6 +87,7 @@ protected:
virtual void Action(void);
virtual void TimerChange(const cTimer *Timer, eTimerChange Change);
+ virtual void ChannelChange(const cChannel *Channel);
virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On);
virtual void OsdStatusMessage(const char *Message);
@@ -89,17 +96,22 @@ public:
cXVDRClient(int fd, unsigned int id);
virtual ~cXVDRClient();
- void ChannelChange();
+ void ChannelsChanged();
void RecordingsChange();
void TimerChange();
+ void QueueMessage(MsgPacket* p);
+ void StatusMessage(const char *Message);
+
unsigned int GetID() { return m_Id; }
+ const std::string& GetClientName() { return m_clientName; }
+ int GetSocket() { return m_socket; }
protected:
void SetLoggedIn(bool yesNo) { m_loggedIn = yesNo; }
void SetStatusInterface(bool yesNo) { m_StatusInterfaceEnabled = yesNo; }
- int StartChannelStreaming(const cChannel *channel, uint32_t timeout, int32_t priority);
+ int StartChannelStreaming(const cChannel *channel, uint32_t timeout, int32_t priority, bool waitforiframe = false);
void StopChannelStreaming();
private:
@@ -134,9 +146,6 @@ private:
bool processRecStream_Close();
bool processRecStream_GetBlock();
bool processRecStream_Update();
- bool processRecStream_PositionFromFrameNumber();
- bool processRecStream_FrameNumberFromPosition();
- bool processRecStream_GetIFrame();
bool processCHANNELS_GroupsCount();
bool processCHANNELS_ChannelsCount();
@@ -163,6 +172,7 @@ private:
bool processRECORDINGS_SetPlayCount();
bool processRECORDINGS_SetPosition();
bool processRECORDINGS_GetPosition();
+ bool processRECORDINGS_GetMarks();
bool processEPG_GetForChannel();
diff --git a/src/xvdr/xvdrcommand.h b/src/xvdr/xvdrcommand.h
index 919ec0c..ab9a95e 100644
--- a/src/xvdr/xvdrcommand.h
+++ b/src/xvdr/xvdrcommand.h
@@ -58,9 +58,6 @@
#define XVDR_RECSTREAM_OPEN 40
#define XVDR_RECSTREAM_CLOSE 41
#define XVDR_RECSTREAM_GETBLOCK 42
-#define XVDR_RECSTREAM_POSTOFRAME 43
-#define XVDR_RECSTREAM_FRAMETOPOS 44
-#define XVDR_RECSTREAM_GETIFRAME 45
#define XVDR_RECSTREAM_UPDATE 46
/* OPCODE 60 - 79: XVDR network functions for channel access */
@@ -87,6 +84,7 @@
#define XVDR_RECORDINGS_SETPLAYCOUNT 105
#define XVDR_RECORDINGS_SETPOSITION 106
#define XVDR_RECORDINGS_GETPOSITION 107
+#define XVDR_RECORDINGS_GETMARKS 108
/* OPCODE 120 - 139: XVDR network functions for epg access and manipulating */
#define XVDR_EPG_GETFORCHANNEL 120
diff --git a/src/xvdr/xvdrserver.c b/src/xvdr/xvdrserver.c
index 092e452..73b9229 100644
--- a/src/xvdr/xvdrserver.c
+++ b/src/xvdr/xvdrserver.c
@@ -142,15 +142,11 @@ cXVDRServer::cXVDRServer(int listenPort) : cThread("VDR XVDR Server")
cXVDRServer::~cXVDRServer()
{
- Cancel(-1);
+ Cancel(5);
for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
{
delete (*i);
}
- m_clients.erase(m_clients.begin(), m_clients.end());
- Cancel();
-
- cChannelCache::SaveChannelCacheData();
INFOLOG("XVDR Server stopped");
}
@@ -228,7 +224,10 @@ void cXVDRServer::Action(void)
struct timeval tv;
cTimeMs channelReloadTimer;
cTimeMs channelCacheTimer;
+ cTimeMs recordingReloadTimer;
+
bool channelReloadTrigger = false;
+ bool recordingReloadTrigger = false;
uint64_t channelsHash = 0;
SetPriority(19);
@@ -292,7 +291,7 @@ void cXVDRServer::Action(void)
{
INFOLOG("Checking for channel updates ...");
for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
- (*i)->ChannelChange();
+ (*i)->ChannelsChanged();
channelReloadTrigger = false;
INFOLOG("Done.");
}
@@ -308,21 +307,41 @@ void cXVDRServer::Action(void)
// store channel cache
if(m_clients.size() > 0 && channelCacheTimer.Elapsed() >= 60*1000) {
- cChannelCache::SaveChannelCacheData();
+ if(!bChanged) {
+ cChannelCache::SaveChannelCacheData();
+ }
channelCacheTimer.Set(0);
}
- // update recordings
+ // check for recording changes
Recordings.StateChanged(recState);
- if(recState != recStateOld || cRecordingsCache::GetInstance().Changed())
+ if(recState != recStateOld)
{
+ recordingReloadTrigger = true;
+ recordingReloadTimer.Set(2000);
INFOLOG("Recordings state changed (%i)", recState);
- INFOLOG("Requesting clients to reload recordings list");
-
recStateOld = recState;
+ }
+
+ // update recordings
+ if((recordingReloadTrigger && recordingReloadTimer.TimedOut()) || cRecordingsCache::GetInstance().Changed()) {
+
+ // start gc if reload was triggered
+ if(!cRecordingsCache::GetInstance().Changed()) {
+ INFOLOG("Starting garbage collection in recordings cache");
+ cRecordingsCache::GetInstance().gc();
+ }
+
+ // request clients to reload recordings
+ if(!m_clients.empty()) {
+ INFOLOG("Requesting clients to reload recordings list");
+
+ for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++) {
+ (*i)->RecordingsChange();
+ }
+ }
- for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++)
- (*i)->RecordingsChange();
+ recordingReloadTrigger = false;
}
// no connect request -> continue waiting
diff --git a/tools/serviceref.c b/tools/serviceref.c
index ea0866d..a78450e 100644
--- a/tools/serviceref.c
+++ b/tools/serviceref.c
@@ -91,7 +91,7 @@ int CodeFromString(const char* s) {
return stNone;
}
-std::string CreateServiceReference(char* source, int frequency, char* vpid, int sid, int nid, int tid) {
+std::string CreateServiceReference(char* source, int frequency, char* vpid, char* apid, int sid, int nid, int tid) {
uint32_t hash;
// usually (on Enigma) the frequency is part of the namespace
@@ -120,10 +120,13 @@ std::string CreateServiceReference(char* source, int frequency, char* vpid, int
else if(source[0] == 'T') {
hash = 0xEEEE0000 | ((frequency / 1000000) & 0xFFFF);
}
+ else if(source[0] == 'A') {
+ hash = 0xDDDD0000 | ((frequency / 1000) & 0xFFFF);
+ }
int type = 1;
- if(strcmp(vpid, "0") == 0 || strcmp(vpid, "1") == 0) {
+ if((strcmp(vpid, "0") == 0 || strcmp(vpid, "1") == 0) && strcmp(apid, "0") != 0) {
type = 2;
}
else {
@@ -228,7 +231,7 @@ int main(int argc, char* argv[]) {
continue;
}
- std::cout << name << " - " << CreateServiceReference(source, freq, vpid, sid, nid, tid) << std::endl;
+ std::cout << name << " - " << CreateServiceReference(source, freq, vpid, apid, sid, nid, tid) << std::endl;
free(name);
free(parameters);
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-vdr-dvb/vdr-plugin-xvdr.git
More information about the pkg-vdr-dvb-changes
mailing list