summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkwacker <vdr@w-i-r.com>2010-04-11 13:46:11 +0200
committerkwacker <vdr@w-i-r.com>2010-04-11 13:46:11 +0200
commit9b144d30e0ea8ce900c37b96ba2cbdda14b0ae88 (patch)
tree3a52de029f950dcd9f9856a53fd67abef8519e68
parent9cd931834ecadbf5efefdf484abb966e9248ebbb (diff)
downloadx-vdr-9b144d30e0ea8ce900c37b96ba2cbdda14b0ae88.tar.gz
x-vdr-9b144d30e0ea8ce900c37b96ba2cbdda14b0ae88.tar.bz2
Burn 0.2.0-beta3 und Streamdev mit Paches aktualisiert
-rw-r--r--plugins/burn/plugin.sh8
-rw-r--r--plugins/streamdev/patches/p1/streamdev-cvs221109-AddCallbackMsg.diff120
-rw-r--r--plugins/streamdev/patches/p1/streamdev-cvs221109-AddFemonV1.diff210
-rwxr-xr-xplugins/streamdev/plugin.sh4
-rw-r--r--plugins/streamdev/streamdev-cvs/CONTRIBUTORS144
-rw-r--r--plugins/streamdev/streamdev-cvs/COPYING340
-rw-r--r--plugins/streamdev/streamdev-cvs/CVS/Entries13
-rw-r--r--plugins/streamdev/streamdev-cvs/CVS/Entries.Log8
-rw-r--r--plugins/streamdev/streamdev-cvs/CVS/Repository1
-rw-r--r--plugins/streamdev/streamdev-cvs/CVS/Root1
-rw-r--r--plugins/streamdev/streamdev-cvs/HISTORY333
-rw-r--r--plugins/streamdev/streamdev-cvs/Makefile150
-rw-r--r--plugins/streamdev/streamdev-cvs/PROTOCOL139
-rw-r--r--plugins/streamdev/streamdev-cvs/README432
-rw-r--r--plugins/streamdev/streamdev-cvs/client/CVS/Entries9
-rw-r--r--plugins/streamdev/streamdev-cvs/client/CVS/Repository1
-rw-r--r--plugins/streamdev/streamdev-cvs/client/CVS/Root1
-rw-r--r--plugins/streamdev/streamdev-cvs/client/device.c306
-rw-r--r--plugins/streamdev/streamdev-cvs/client/device.h68
-rw-r--r--plugins/streamdev/streamdev-cvs/client/filter.c292
-rw-r--r--plugins/streamdev/streamdev-cvs/client/filter.h33
-rw-r--r--plugins/streamdev/streamdev-cvs/client/setup.c80
-rw-r--r--plugins/streamdev/streamdev-cvs/client/setup.h39
-rw-r--r--plugins/streamdev/streamdev-cvs/client/socket.c374
-rw-r--r--plugins/streamdev/streamdev-cvs/client/socket.h60
-rw-r--r--plugins/streamdev/streamdev-cvs/common.c152
-rw-r--r--plugins/streamdev/streamdev-cvs/common.h80
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/.cvsignore1
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Entries11
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Repository1
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Root1
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/Makefile25
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/ctools.c2403
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/ctools.h404
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/remux.c1215
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/remux.h149
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/ringbuffy.c201
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/ringbuffy.h52
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/transform.c2681
-rw-r--r--plugins/streamdev/streamdev-cvs/libdvbmpeg/transform.h250
-rw-r--r--plugins/streamdev/streamdev-cvs/patches/CVS/Entries7
-rw-r--r--plugins/streamdev/streamdev-cvs/patches/CVS/Repository1
-rw-r--r--plugins/streamdev/streamdev-cvs/patches/CVS/Root1
-rw-r--r--plugins/streamdev/streamdev-cvs/patches/vdr-1.4.3-recursion.diff85
-rw-r--r--plugins/streamdev/streamdev-cvs/patches/vdr-1.4.3-recursion_bigpatch.diff88
-rw-r--r--plugins/streamdev/streamdev-cvs/patches/vdr-1.4.x-localchannelprovide.diff102
-rw-r--r--plugins/streamdev/streamdev-cvs/patches/vdr-1.6.0-ignore_missing_cam.diff13
-rw-r--r--plugins/streamdev/streamdev-cvs/patches/vdr-1.6.0-intcamdevices.patch78
-rw-r--r--plugins/streamdev/streamdev-cvs/patches/vdr-cap_net_raw.diff11
-rw-r--r--plugins/streamdev/streamdev-cvs/po/CVS/Entries8
-rw-r--r--plugins/streamdev/streamdev-cvs/po/CVS/Repository1
-rw-r--r--plugins/streamdev/streamdev-cvs/po/CVS/Root1
-rw-r--r--plugins/streamdev/streamdev-cvs/po/de_DE.po118
-rw-r--r--plugins/streamdev/streamdev-cvs/po/fi_FI.po118
-rw-r--r--plugins/streamdev/streamdev-cvs/po/fr_FR.po118
-rwxr-xr-xplugins/streamdev/streamdev-cvs/po/it_IT.po120
-rw-r--r--plugins/streamdev/streamdev-cvs/po/lt_LT.po118
-rw-r--r--plugins/streamdev/streamdev-cvs/po/ru_RU.po118
-rw-r--r--plugins/streamdev/streamdev-cvs/po/sk_SK.po121
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/CVS/Entries11
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/CVS/Repository1
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/CVS/Root1
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/extern.c192
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/extern.h28
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/ts2es.c146
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/ts2es.h28
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/ts2pes.c2017
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/ts2pes.h56
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/ts2ps.c218
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/ts2ps.h36
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/tsremux.c87
-rw-r--r--plugins/streamdev/streamdev-cvs/remux/tsremux.h28
-rw-r--r--plugins/streamdev/streamdev-cvs/server/CVS/Entries34
-rw-r--r--plugins/streamdev/streamdev-cvs/server/CVS/Entries.Log1
-rw-r--r--plugins/streamdev/streamdev-cvs/server/CVS/Repository1
-rw-r--r--plugins/streamdev/streamdev-cvs/server/CVS/Root1
-rw-r--r--plugins/streamdev/streamdev-cvs/server/component.c49
-rw-r--r--plugins/streamdev/streamdev-cvs/server/component.h51
-rw-r--r--plugins/streamdev/streamdev-cvs/server/componentHTTP.c18
-rw-r--r--plugins/streamdev/streamdev-cvs/server/componentHTTP.h18
-rw-r--r--plugins/streamdev/streamdev-cvs/server/componentIGMP.c447
-rw-r--r--plugins/streamdev/streamdev-cvs/server/componentIGMP.h62
-rw-r--r--plugins/streamdev/streamdev-cvs/server/componentVTP.c18
-rw-r--r--plugins/streamdev/streamdev-cvs/server/componentVTP.h18
-rw-r--r--plugins/streamdev/streamdev-cvs/server/connection.c255
-rw-r--r--plugins/streamdev/streamdev-cvs/server/connection.h99
-rw-r--r--plugins/streamdev/streamdev-cvs/server/connectionHTTP.c296
-rw-r--r--plugins/streamdev/streamdev-cvs/server/connectionHTTP.h70
-rw-r--r--plugins/streamdev/streamdev-cvs/server/connectionIGMP.c64
-rw-r--r--plugins/streamdev/streamdev-cvs/server/connectionIGMP.h45
-rw-r--r--plugins/streamdev/streamdev-cvs/server/connectionVTP.c1768
-rw-r--r--plugins/streamdev/streamdev-cvs/server/connectionVTP.h93
-rw-r--r--plugins/streamdev/streamdev-cvs/server/http/CVS/Entries1
-rw-r--r--plugins/streamdev/streamdev-cvs/server/http/CVS/Repository1
-rw-r--r--plugins/streamdev/streamdev-cvs/server/http/CVS/Root1
-rw-r--r--plugins/streamdev/streamdev-cvs/server/livefilter.c39
-rw-r--r--plugins/streamdev/streamdev-cvs/server/livefilter.h32
-rw-r--r--plugins/streamdev/streamdev-cvs/server/livestreamer.c689
-rw-r--r--plugins/streamdev/streamdev-cvs/server/livestreamer.h82
-rw-r--r--plugins/streamdev/streamdev-cvs/server/menuHTTP.c429
-rw-r--r--plugins/streamdev/streamdev-cvs/server/menuHTTP.h143
-rw-r--r--plugins/streamdev/streamdev-cvs/server/recplayer.c288
-rw-r--r--plugins/streamdev/streamdev-cvs/server/recplayer.h63
-rw-r--r--plugins/streamdev/streamdev-cvs/server/server.c173
-rw-r--r--plugins/streamdev/streamdev-cvs/server/server.h49
-rw-r--r--plugins/streamdev/streamdev-cvs/server/setup.c141
-rw-r--r--plugins/streamdev/streamdev-cvs/server/setup.h48
-rw-r--r--plugins/streamdev/streamdev-cvs/server/streamer.c162
-rw-r--r--plugins/streamdev/streamdev-cvs/server/streamer.h102
-rw-r--r--plugins/streamdev/streamdev-cvs/server/suspend.c57
-rw-r--r--plugins/streamdev/streamdev-cvs/server/suspend.dat1206
-rw-r--r--plugins/streamdev/streamdev-cvs/server/suspend.h36
-rw-r--r--plugins/streamdev/streamdev-cvs/streamdev-client.c60
-rw-r--r--plugins/streamdev/streamdev-cvs/streamdev-client.h29
-rw-r--r--plugins/streamdev/streamdev-cvs/streamdev-server.c143
-rw-r--r--plugins/streamdev/streamdev-cvs/streamdev-server.h33
-rw-r--r--plugins/streamdev/streamdev-cvs/streamdev/CVS/Entries3
-rw-r--r--plugins/streamdev/streamdev-cvs/streamdev/CVS/Repository1
-rw-r--r--plugins/streamdev/streamdev-cvs/streamdev/CVS/Root1
-rwxr-xr-xplugins/streamdev/streamdev-cvs/streamdev/externremux.sh48
-rw-r--r--plugins/streamdev/streamdev-cvs/streamdev/streamdevhosts.conf14
-rw-r--r--plugins/streamdev/streamdev-cvs/tools/CVS/Entries9
-rw-r--r--plugins/streamdev/streamdev-cvs/tools/CVS/Repository1
-rw-r--r--plugins/streamdev/streamdev-cvs/tools/CVS/Root1
-rw-r--r--plugins/streamdev/streamdev-cvs/tools/select.c51
-rw-r--r--plugins/streamdev/streamdev-cvs/tools/select.h75
-rw-r--r--plugins/streamdev/streamdev-cvs/tools/socket.c169
-rw-r--r--plugins/streamdev/streamdev-cvs/tools/socket.h119
-rw-r--r--plugins/streamdev/streamdev-cvs/tools/source.c171
-rw-r--r--plugins/streamdev/streamdev-cvs/tools/source.h109
-rw-r--r--plugins/streamdev/streamdev-cvs/tools/tools.c12
-rw-r--r--plugins/streamdev/streamdev-cvs/tools/tools.h67
-rw-r--r--plugins/streamdev/streamdev-cvs100210-ReplaceRecordingStreaming.patch581
133 files changed, 23709 insertions, 6 deletions
diff --git a/plugins/burn/plugin.sh b/plugins/burn/plugin.sh
index 59c7ae3..c100ed9 100644
--- a/plugins/burn/plugin.sh
+++ b/plugins/burn/plugin.sh
@@ -3,16 +3,16 @@
# x-vdr (Installations-Skript fuer einen VDR mit Debian als Basis)
# von Marc Wernecke - www.zulu-entertainment.de
# 05.03.2009
-
+# kwacker: Burn auf 0.2.0-beta3 aktualisiert
# vdr-burn
# defaults
source ./../../x-vdr.conf
source ./../../setup.conf
source ./../../functions
-
-WEB="http://www.zulu-entertainment.de/files/vdr-burn/vdr-burn-0.1.0-pre22x.tgz"
-VERSION="burn-0.1.0-pre22x"
+WEB="http://projects.vdr-developer.org/attachments/download/285/vdr-burn-0.2.0-beta3.tgz"
+#WEB="http://www.zulu-entertainment.de/files/vdr-burn/vdr-burn-0.1.0-pre22x.tgz"
+VERSION="burn-0.2.0-beta3"
LINK="burn"
VAR=`basename $WEB`
diff --git a/plugins/streamdev/patches/p1/streamdev-cvs221109-AddCallbackMsg.diff b/plugins/streamdev/patches/p1/streamdev-cvs221109-AddCallbackMsg.diff
new file mode 100644
index 0000000..7facc61
--- /dev/null
+++ b/plugins/streamdev/patches/p1/streamdev-cvs221109-AddCallbackMsg.diff
@@ -0,0 +1,120 @@
+diff -NaurwB streamdev-unpatched/common.h streamdev/common.h
+--- streamdev-unpatched/common.h 2009-09-18 12:41:41.000000000 +0200
++++ streamdev/common.h 2009-11-23 04:54:04.000000000 +0100
+@@ -57,6 +57,8 @@
+ si_Count
+ };
+
++#define MAX_RESPONSE_SIZE 1024
++
+ extern const char *VERSION;
+
+ class cMenuEditIpItem: public cMenuEditItem {
+diff -NaurwB streamdev-unpatched/server/connectionVTP.c streamdev/server/connectionVTP.c
+--- streamdev-unpatched/server/connectionVTP.c 2009-10-13 08:38:47.000000000 +0200
++++ streamdev/server/connectionVTP.c 2009-11-23 14:23:33.000000000 +0100
+@@ -1714,3 +1714,69 @@
+ Code < 0 ? -Code : Code,
+ Code < 0 ? '-' : ' ', *str);
+ }
++
++void cConnectionVTP::TimerChange(const cTimer *Timer, eTimerChange Change)
++{
++ if (m_DataSocket) {
++ char buf[MAX_RESPONSE_SIZE];
++ if (Change == tcMod) {
++ snprintf(buf, MAX_RESPONSE_SIZE, "MODT %s\0", Timer ? *Timer->ToText(true) : "-");
++ }
++ if (Change == tcAdd) {
++ snprintf(buf, MAX_RESPONSE_SIZE, "ADDT %s\0", Timer ? *Timer->ToText(true) : "-");
++ }
++ if (Change == tcDel) {
++ snprintf(buf, MAX_RESPONSE_SIZE, "DELT %s\0", Timer ? *Timer->ToText(true) : "-");
++ }
++
++ m_DataSocket->SysWrite(buf, strlen(buf));
++ }
++}
++
++#ifdef USE_STATUS_EXTENSION
++void cConnectionVTP::OsdStatusMessage(eMessageType type, const char *Message)
++#else
++void cConnectionVTP::OsdStatusMessage(const char *Message)
++#endif
++{
++ if (m_DataSocket && Message) {
++ char buf[MAX_RESPONSE_SIZE];
++
++ /* Ignore this messages */
++ if (strcasecmp(Message, trVDR("Channel not available!")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Delete timer?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Delete recording?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Press any key to cancel shutdown")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Press any key to cancel restart")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Editing - shut down anyway?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Recording - shut down anyway?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("shut down anyway?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Recording - restart anyway?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Editing - restart anyway?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Delete channel?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Timer still recording - really delete?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Delete marks information?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Delete resume information?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("CAM is in use - really reset?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Really restart?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Stop recording?")) == 0) return;
++ else if (strcasecmp(Message, trVDR("Cancel editing?")) == 0) return;
++ 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;
++
++#ifdef USE_STATUS_EXTENSION
++ if (type == mtStatus)
++ snprintf(buf, MAX_RESPONSE_SIZE, "SMSG %s\0", Message);
++ else if (type == mtInfo)
++ snprintf(buf, MAX_RESPONSE_SIZE, "IMSG %s\0", Message);
++ else if (type == mtWarning)
++ snprintf(buf, MAX_RESPONSE_SIZE, "WMSG %s\0", Message);
++ else if (type == mtError)
++ snprintf(buf, MAX_RESPONSE_SIZE, "EMSG %s\0", Message);
++ else
++#endif
++ snprintf(buf, MAX_RESPONSE_SIZE, "IMSG %s\0", Message);
++
++ m_DataSocket->SysWrite(buf, strlen(buf));
++ }
++}
+diff -NaurwB streamdev-unpatched/server/connectionVTP.h streamdev/server/connectionVTP.h
+--- streamdev-unpatched/server/connectionVTP.h 2009-07-01 12:46:16.000000000 +0200
++++ streamdev/server/connectionVTP.h 2009-11-23 14:23:33.000000000 +0100
+@@ -1,6 +1,7 @@
+ #ifndef VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
+ #define VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
+
++#include <vdr/status.h>
+ #include "server/connection.h"
+ #include "server/recplayer.h"
+
+@@ -12,7 +13,8 @@
+ class cLSTTHandler;
+ class cLSTRHandler;
+
+-class cConnectionVTP: public cServerConnection {
++class cConnectionVTP: public cServerConnection
++ , public cStatus {
+ friend class cLSTEHandler;
+ #if !defined __GNUC__ || __GNUC__ >= 3
+ using cServerConnection::Respond;
+@@ -41,6 +43,13 @@
+ template<class cHandler>
+ bool CmdLSTX(cHandler *&Handler, char *Option);
+
++ virtual void TimerChange(const cTimer *Timer, eTimerChange Change);
++#ifdef USE_STATUS_EXTENSION
++ virtual void OsdStatusMessage(eMessageType type, const char *Message);
++#else
++ virtual void OsdStatusMessage(const char *Message);
++#endif
++
+ public:
+ cConnectionVTP(void);
+ virtual ~cConnectionVTP();
diff --git a/plugins/streamdev/patches/p1/streamdev-cvs221109-AddFemonV1.diff b/plugins/streamdev/patches/p1/streamdev-cvs221109-AddFemonV1.diff
new file mode 100644
index 0000000..39872e4
--- /dev/null
+++ b/plugins/streamdev/patches/p1/streamdev-cvs221109-AddFemonV1.diff
@@ -0,0 +1,210 @@
+diff -NaurwB streamdev-unpatched/server/connectionVTP.c streamdev/server/connectionVTP.c
+--- streamdev-unpatched/server/connectionVTP.c 2009-10-13 08:38:47.000000000 +0200
++++ streamdev/server/connectionVTP.c 2009-11-22 20:04:07.000000000 +0100
+@@ -7,6 +7,8 @@
+ #include "server/suspend.h"
+ #include "setup.h"
+
++#include "../services/femonservice.h"
++
+ #include <vdr/tools.h>
+ #include <vdr/videodir.h>
+ #include <vdr/menu.h>
+@@ -710,6 +712,102 @@
+ return false;
+ }
+
++
++// --- cLSTQHandler -----------------------------------------------------------
++
++class cLSTQHandler
++{
++private:
++ enum eStates { Device, Status, Signal, SNR, BER, UNC, Video,
++ Audio, Dolby, EndQuality };
++ cConnectionVTP *m_Client;
++ FemonService_v1_0 m_femon;
++ int m_Errno;
++ int m_Channel;
++ cString m_Error;
++ eStates m_State;
++public:
++ cLSTQHandler(cConnectionVTP *Client, const char *Option);
++ ~cLSTQHandler();
++ bool Next(bool &Last);
++};
++
++cLSTQHandler::cLSTQHandler(cConnectionVTP *Client, const char *Option):
++ m_Client(Client),
++ m_Errno(0),
++ m_State(Device),
++ m_Channel(-1)
++{
++// if (*Option) {
++// if (isnumber(Option)) {
++// m_Channel = atoi(Option);
++// }
++// }
++
++ cPlugin *p;
++ p = cPluginManager::CallFirstService("FemonService-v1.0", &m_femon);
++ if (!p) {
++ m_Errno = 550;
++ m_Error = cString::sprintf("No support for Signal Quality found");
++ }
++}
++
++cLSTQHandler::~cLSTQHandler()
++{
++}
++
++bool cLSTQHandler::Next(bool &Last)
++{
++ if (*m_Error != NULL) {
++ Last = true;
++ cString str(m_Error);
++ m_Error = NULL;
++ return m_Client->Respond(m_Errno, "%s", *str);
++ }
++
++ Last = false;
++ switch (m_State) {
++ case Device:
++ m_State = Status;
++ if (*m_femon.fe_name != NULL)
++ return m_Client->Respond(-215, "Device : %s", *m_femon.fe_name);
++ else
++ return m_Client->Respond(-215, "Device : ");
++ case Status:
++ m_State = Signal;
++ if (*m_femon.fe_status != NULL)
++ return m_Client->Respond(-215, "Status : %s", *m_femon.fe_status);
++ else
++ return m_Client->Respond(-215, "Status : ");
++ case Signal:
++ m_State = SNR;
++ return m_Client->Respond(-215, "Signal : %04X (%2d%%)", m_femon.fe_signal, m_femon.fe_signal / 655);
++ case SNR:
++ m_State = BER;
++ return m_Client->Respond(-215, "SNR : %04X (%2d%%)", m_femon.fe_snr, m_femon.fe_snr / 655);
++ case BER:
++ m_State = UNC;
++ return m_Client->Respond(-215, "BER : %08X", m_femon.fe_ber);
++ case UNC:
++ m_State = Video;
++ return m_Client->Respond(-215, "UNC : %08X", m_femon.fe_unc);
++ case Video:
++ m_State = Audio;
++ return m_Client->Respond(-215, "Video : %.2f Mbit/s", m_femon.video_bitrate);
++ case Audio:
++ m_State = Dolby;
++ return m_Client->Respond(-215, "Audio : %.0f kbit/s", m_femon.audio_bitrate);
++ case Dolby:
++ m_State = EndQuality;
++ return m_Client->Respond(-215, "Dolby : %.0f kbit/s", m_femon.dolby_bitrate);
++ case EndQuality:
++ Last = true;
++ return m_Client->Respond(215, "End of quality information");
++ }
++ return false;
++}
++
++
+ // --- cConnectionVTP ---------------------------------------------------------
+
+ cConnectionVTP::cConnectionVTP(void):
+@@ -727,7 +825,8 @@
+ m_LSTEHandler(NULL),
+ m_LSTCHandler(NULL),
+ m_LSTTHandler(NULL),
+- m_LSTRHandler(NULL)
++ m_LSTRHandler(NULL),
++ m_LSTQHandler(NULL)
+ {
+ }
+
+@@ -745,6 +844,7 @@
+ delete m_LSTCHandler;
+ delete m_LSTEHandler;
+ delete m_LSTRHandler;
++ delete m_LSTQHandler;
+ delete m_RecPlayer;
+ }
+
+@@ -801,6 +901,7 @@
+ else if (strcasecmp(Cmd, "LSTR") == 0) return CmdLSTR(param);
+ else if (strcasecmp(Cmd, "LSTT") == 0) return CmdLSTT(param);
+ else if (strcasecmp(Cmd, "LSTC") == 0) return CmdLSTC(param);
++ else if (strcasecmp(Cmd, "LSTQ") == 0) return CmdLSTQ(param);
+
+ if (param == NULL) {
+ esyslog("ERROR: streamdev: this seriously shouldn't happen at %s:%d",
+@@ -1268,6 +1369,11 @@
+ return CmdLSTX(m_LSTRHandler, Option);
+ }
+
++bool cConnectionVTP::CmdLSTQ(char *Option)
++{
++ return CmdLSTX(m_LSTQHandler, Option);
++}
++
+ // Functions adopted from SVDRP
+ #define INIT_WRAPPER() bool _res
+ #define Reply(c,m...) _res = Respond(c,m)
+diff -NaurwB streamdev-unpatched/server/connectionVTP.h streamdev/server/connectionVTP.h
+--- streamdev-unpatched/server/connectionVTP.h 2009-07-01 12:46:16.000000000 +0200
++++ streamdev/server/connectionVTP.h 2009-11-22 16:08:51.000000000 +0100
+@@ -11,6 +11,7 @@
+ class cLSTCHandler;
+ class cLSTTHandler;
+ class cLSTRHandler;
++class cLSTQHandler;
+
+ class cConnectionVTP: public cServerConnection {
+ friend class cLSTEHandler;
+@@ -36,6 +37,7 @@
+ cLSTCHandler *m_LSTCHandler;
+ cLSTTHandler *m_LSTTHandler;
+ cLSTRHandler *m_LSTRHandler;
++ cLSTQHandler *m_LSTQHandler;
+
+ protected:
+ template<class cHandler>
+@@ -72,6 +74,7 @@
+ bool CmdLSTC(char *Opts);
+ bool CmdLSTT(char *Opts);
+ bool CmdLSTR(char *Opts);
++ bool CmdLSTQ(char *Opts);
+
+ // Commands adopted from SVDRP
+ bool CmdSTAT(const char *Option);
+diff -NaurwB streamdev-unpatched/services/femonservice.h streamdev/services/femonservice.h
+--- streamdev-unpatched/services/femonservice.h 1970-01-01 01:00:00.000000000 +0100
++++ streamdev/services/femonservice.h 2009-10-01 03:20:00.000000000 +0200
+@@ -0,0 +1,26 @@
++/*
++ * Frontend Status Monitor plugin for the Video Disk Recorder
++ *
++ * See the README file for copyright information and how to reach the author.
++ *
++ */
++
++#ifndef __FEMONSERVICE_H
++#define __FEMONSERVICE_H
++
++#include <linux/dvb/frontend.h>
++
++struct FemonService_v1_0 {
++ cString fe_name;
++ cString fe_status;
++ uint16_t fe_snr;
++ uint16_t fe_signal;
++ uint32_t fe_ber;
++ uint32_t fe_unc;
++ double video_bitrate;
++ double audio_bitrate;
++ double dolby_bitrate;
++ };
++
++#endif //__FEMONSERVICE_H
++
diff --git a/plugins/streamdev/plugin.sh b/plugins/streamdev/plugin.sh
index 81e6278..5bd439b 100755
--- a/plugins/streamdev/plugin.sh
+++ b/plugins/streamdev/plugin.sh
@@ -3,7 +3,7 @@
# x-vdr (Installations-Skript fuer einen VDR mit Debian als Basis)
# von Marc Wernecke - www.zulu-entertainment.de
# 07.04.2009
-#
+# kwacker: auf cvs umgestellt und Patches hinzugefuegt 11.04.2010
# vdr-streamdev
# defaults
@@ -14,7 +14,7 @@ source ./../../functions
WEB="http://www.zulu-entertainment.de/files/vdr-streamdev/vdr-streamdev-0.5.0-pre.tgz"
VERSION="streamdev-0.5.0-pre"
LINK="streamdev"
-CVS="0"
+CVS="1"
[ "$CVS" = "1" ] && VERSION="streamdev-cvs"
diff --git a/plugins/streamdev/streamdev-cvs/CONTRIBUTORS b/plugins/streamdev/streamdev-cvs/CONTRIBUTORS
new file mode 100644
index 0000000..33141a7
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/CONTRIBUTORS
@@ -0,0 +1,144 @@
+Special thanks go to the following persons (if you think your name is missing
+here, please send an email to vdrdev@schmirler.de):
+
+Klaus Schmidinger
+ for VDR as a whole
+ for permission to use VDR 1.6.0 cRemux code for PES remuxing
+
+Sascha Volkenandt, the original author,
+ for this great plugin
+
+The Metzler Brothers
+ as a lot of code has been taken from their libdvbmpeg package
+
+Angelus (DOm)
+ for providing Italian language texts
+ for reporting problems with the Elchi-Patch
+
+Michal
+ for sending a patch to select the HTTP streamtype via remote
+
+Rolf Ahrenberg
+ for providing Finnish language texts
+ for adding externremux.sh commandline parameter
+ for silencing compiler warnings
+ for adding PAT, PMT, PCR and EIT to HTTP TS streams
+ for fixing a memory leak in buffer overflow situations
+ for adding a return code check to vasprintf()
+ for suggesting a fix of the Makefile's default target
+ for a TS PAT repacker based on Petri Laine's VDR TS recording patch
+ for making it possible to pass parameters to externremux.sh
+ for removing pre VDR 1.4 legacy code
+ for adding gettext support
+ for fixing output format of some debug messages
+ for replacing private members by cThread::Running()/Active()
+ for improving externremux script termination
+ for fixing PAT repacker version field
+ for improving LIMIKUUTIO and PARENTALRATING patch detection
+ for suggesting to include the charset in HTTP replies
+ for requesting replacement of asprintf calls
+
+Rantanen Teemu
+ for providing vdr-incompletesections.diff
+
+Thomas Keil
+ for providing vdr-localchannelprovide.diff
+ for maintaining the plugin for a while
+
+Artur Skawina
+ for fixing an fd leak
+
+Norad
+ for reporting a problem terminated externremux.sh children
+
+Udo Richter
+ for fixing streamdev-server shutdown
+ for speeding up cPluginStreamdevServer::Active()
+ for adapting to VDR 1.5.0 API
+ for adapting to VDR 1.7.1
+
+greenman
+ for reporting that the log could get flooded on connection failures.
+
+Petri Hintukainen
+ for making section filtering work
+ for fixing a segfault with VDR 1.5
+ for fixing high CPU load when data stream is disconnected
+ for adding PAT, PMT and PCR to HTTP TS streams
+ for fixing a segfault / deadlock when shutting down
+ for fixing compiler warnings
+ for adding M3U playlists
+
+ollo
+ for suggesting support for WMM capable WLAN accesspoints
+
+vdr-freak
+ for reporting connection aborts when externremux ringbuffer is full
+
+alexw
+ for reporting client reconnect problems after a server restart
+ for a workaround for tuning problems with 1.5.x clients
+
+Olli Lammi
+ for fixing a busy wait when client isn't accepting data fast enough
+ for suggesting signaling instead of sleeping when writing to buffers
+
+Joerg Pulz
+ for his FreeBSD compatibility patch
+
+tobi
+ for pointing to unused files in the libdvbmpeg directory
+
+Diego Pierotto
+ for providing Italian language texts
+
+micky979
+ for providing French language texts
+
+Tiroler
+ for reporting a problem when switching between encrypted channels
+
+Pixelpeter
+ for an initial fix to the "switching between ecncrypted channels" problem
+
+Anssi Hannula
+ for the vdr-1.6.0-intcamdevices patch
+ for fixing insecure format strings in LSTX handlers
+
+wirbel
+ for pointing out that section filtering is optional for VDR devices
+ for reporting a problem with Makefile defines in VDR 1.7.4+
+
+Jori Hamalainen
+ for extensive testing while making stream compatible to Network Media Tank
+ for adding Network Media Tank browser support to HTML pages
+
+owagner
+ for pointing out a problem with the encrypted channel switching fix
+ for suggesting use of SO_KEEPALIVE socket option to detect dead sockets
+
+Joachim König-Baltes
+ for fixing Min/MaxPriority parsing
+
+Artem Makhutov
+ for suggesting and heavy testing IGMP based multicast streaming
+
+Alwin Esch
+ for adding XBMC support by extending VTP capabilities
+ for adding VDR 1.7.11 parental rating support for VTP LSTE command
+ for adding the DELT FORCE option to delete running timers
+
+BBlack
+ for reporting that updating recordings list on CmdPLAY is a bad idea
+
+Milan Hrala
+ for providing Slovak language texts
+
+Valdemaras Pipiras
+ for providing Lithuanian language texts
+
+sk8ter
+ for fixing failures when switching between two encrypted channels
+
+lhanisch
+ for fixing a memory leak in cStreamdevPatFilter::GetPid
diff --git a/plugins/streamdev/streamdev-cvs/COPYING b/plugins/streamdev/streamdev-cvs/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 of the License, 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
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/plugins/streamdev/streamdev-cvs/CVS/Entries b/plugins/streamdev/streamdev-cvs/CVS/Entries
new file mode 100644
index 0000000..e441497
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/CVS/Entries
@@ -0,0 +1,13 @@
+/CONTRIBUTORS/1.34/Sat Feb 20 23:02:10 2010//
+/COPYING/1.1.1.1/Thu Dec 30 22:43:56 2004//
+/HISTORY/1.56/Sat Feb 20 23:02:10 2010//
+/Makefile/1.20/Wed Nov 4 11:12:20 2009//
+/PROTOCOL/1.1.1.1/Thu Dec 30 22:43:58 2004//
+/README/1.20/Mon Oct 19 06:19:10 2009//
+/common.c/1.11/Fri Sep 18 10:41:41 2009//
+/common.h/1.15/Fri Sep 18 10:41:41 2009//
+/streamdev-client.c/1.6/Tue Apr 8 14:18:15 2008//
+/streamdev-client.h/1.1.1.1/Thu Dec 30 22:43:59 2004//
+/streamdev-server.c/1.12/Fri Jun 19 06:32:38 2009//
+/streamdev-server.h/1.4/Mon Feb 19 12:08:16 2007//
+D
diff --git a/plugins/streamdev/streamdev-cvs/CVS/Entries.Log b/plugins/streamdev/streamdev-cvs/CVS/Entries.Log
new file mode 100644
index 0000000..70e37b9
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/CVS/Entries.Log
@@ -0,0 +1,8 @@
+A D/client////
+A D/libdvbmpeg////
+A D/patches////
+A D/po////
+A D/remux////
+A D/server////
+A D/streamdev////
+A D/tools////
diff --git a/plugins/streamdev/streamdev-cvs/CVS/Repository b/plugins/streamdev/streamdev-cvs/CVS/Repository
new file mode 100644
index 0000000..d57072e
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/CVS/Repository
@@ -0,0 +1 @@
+streamdev
diff --git a/plugins/streamdev/streamdev-cvs/CVS/Root b/plugins/streamdev/streamdev-cvs/CVS/Root
new file mode 100644
index 0000000..2c7f6ce
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/CVS/Root
@@ -0,0 +1 @@
+:pserver:anoncvs@vdr-developer.org:/var/cvsroot
diff --git a/plugins/streamdev/streamdev-cvs/HISTORY b/plugins/streamdev/streamdev-cvs/HISTORY
new file mode 100644
index 0000000..c4fe3b7
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/HISTORY
@@ -0,0 +1,333 @@
+VDR Plugin 'streamdev' Revision History
+---------------------------------------
+
+- fixed a memory leak in cStreamdevPatFilter::GetPid (thanks to lhanisch)
+- length -1 is the correct value for streams in M3U playlists
+- switching between two encrypted channels on the same transponder didn't
+ always work (thanks to sk8ter@vdrportal)
+- added DELT FORCE option to delete running timers (thanks to Alwin Esch)
+- added VDR 1.7.11 parental rating support for VTP LSTE command (thanks to
+ Alwin Esch)
+- added Lithuanian translation (thanks to Valdemaras Pipiras)
+- fixed missing virtual destructor for cTSRemux
+- added defines for large file support to Makefile as required by VDR 1.7.4+
+ (reported by wirbel@vdrportal)
+- added Slovak translation (thanks to Milan Hrala)
+- fixed regression from fix for switching between encrypted channels. It was
+ no longer possible to receive multiple (FTA) streams from the same
+ transponder
+- silenced warnings concerning asprintf (requested by Rolf Ahrenberg)
+- don't update recordings list on CmdPLAY (reported by BBlack)
+- cleaned up common.h / common.c
+- dropped cStreamdevMenuSetupPage
+- report charset in HTTP replies (suggested by Rolf Ahrenberg)
+- use SO_KEEPALIVE option on all sockets do detect dead sockets (thanks to
+ owagner)
+- enable PatFilter for externremux, so VLC can be used as remuxer or client
+- fixed insecure format strings in LSTX handlers (thanks to Anssi Hannula)
+- updated Finish translation (thanks to Rolf Ahrenberg)
+- removed redefinitions in includes - caused problems in older compilers
+- fixed ts2ps.h defines
+- fixed missing virtual for cTS2PESRemux destructor
+- silenced format mismatch warning on 64bit OS
+- added XBMC support by extending VTP capabilities (thanks to Alwin Esch)
+- now there's a common baseclass for all remuxers, make use of it
+- added cDevice::NumProvidedSystems() which was introduced in VDR 1.7.0
+- added namespace to remuxers
+- increased WRITERBUFSIZE - buffer was too small for high bandwidth content
+- removed cStreamdevStreamer::m_Running
+- eliminated potential busy waits in remuxers
+- updated cTSRemux static helpers to code of their VDR 1.6.0 counterparts
+- re-enabled PES vor VDR 1.7.3+. Streamdev now uses a copy of VDR 1.6.0's
+ cRemux for TS to PES remuxing.
+- make sure that only complete TS packets are written to ringbuffers
+- use signaling instead of sleeps when writing to ringbuffers
+- optimized cStreamdevPatFilter PAT packet initialization
+- fixed cStreamdevPatFilter not processing PATs with length > TS_SIZE - 5
+- use a small ringbuffer for cStreamdevPatFilter instead of writing to
+ cStreamdevStreamers SendBuffer as two threads mustn't write to the same
+ ringbuffer
+- added missing call to StopSectionHandler which could cause crashes when
+ shutting down VDR
+- added IGMP based multicast streaming
+- ignore trailing blank lines in HTTP requests
+- fixed parsing Min/MaxPriority from config (thanks to Joachim König-Baltes)
+- updated Finnish translation (thanks to Rolf Ahrenberg)
+- added Min/MaxPriority parameters. Can be used to keep client VDR from
+ using streamdev e.g. when recording
+- disabled PES for VDR 1.7.3+
+- added Network Media Tank browser support to HTML pages (thanks to Jori
+ Hamalainen)
+- minor fixes of PAT repacker
+- repack and send every PAT packet we receive
+- fixed null pointer in server.c when cConnection::Accept() failes
+- consider Pids from channels.conf when HTTP TS streaming. Section filtering
+ is an optional feature for VDR devices, so we must not rely on the PMT
+ alone (pointed out by wirbel@vdrportal)
+- improved externremux script termination (thanks to Rolf Ahrenberg)
+- use cThread::Running()/Active() instead of private members (thanks to
+ Rolf Ahrenberg)
+- fixed output format of some debug messages (thanks to Rolf Ahrenberg)
+- added HTTP authentication
+- compatibility for VDR 1.7.1 (thanks to Udo Richter)
+- added vdr-1.6.0-intcamdevices.patch (thanks to Anssi Hannula)
+- fixed problem when switching from one encrypted channel to an other
+ (reported by Tiroler@vdrportal, initial bugfix by pixelpeter@vdrportal,
+ another fix by owagner@vdrportal)
+- added preprocessor directive for ancient gcc
+- added Russian translation (thanks to Oleg Roitburd)
+- fixed assignment of externremux.sh's default location (reported by plautze)
+- added French translation (thanks to micky979)
+- added Italian translation (thanks to Diego Pierotto)
+- added gettext support (thanks to Rolf Ahrenberg)
+- added vdr-1.6.0-ignore_missing_cam patch
+- dropped obsolete respect_ca patch
+- removed legacy code for < VDR 1.5.9 (thanks to Rolf Ahrenberg)
+
+2008-04-07: Branched v0_4
+
+- changed location of streamdevhosts.conf to VDRCONFDIR/plugins/streamdev
+- changed externremux.sh's default location to VDRCONFDIR/plugins/streamdev
+- added sample externremux.sh from http://www.vdr-wiki.de/
+- stop providing channels after client has been disabled at runtime
+- added logging of the client device's card index
+- changed default suspend mode to "Always suspended"
+- added "Hide Mainmenu Entry" setup option on client
+- resurrected clients "Suspend Server" menu item as its mainmenu entry
+- dropped unused code for remote timers/recordings on client side
+- dropped unused files client/{assembler,menu,remote}.[hc]
+- dropped unused files in libdvbmpeg (reported by tobi)
+- dropped patches for pre VDR 1.4
+- removed legacy code for pre VDR 1.4 (thanks to Rolf Ahrenberg)
+
+2008-03-31: Version 0.3.4
+
+- added possibility to pass parameter to externremux.sh (thanks to Rolf
+ Ahrenberg)
+- use HTTP host header in absolute URLs for DNAT / reverse proxy support
+- rewrite of the HTTP menu part
+- added M3U playlists (thanks to Petri Hinutkainen)
+- enable section filtering only with compatible clients (thanks to Petri
+ Hintukainen)
+- fixed compiler warning
+- added EIT to HTTP TS streams (thanks to Rolf Ahrenberg)
+- compatibility for FreeBSD (thanks to Joerg Pulz)
+- added TS PAT repacker (thanks to Rolf Ahrenberg)
+- fixed Makefile's default target (suggested by Rolf Ahrenberg)
+- workaround for tuning problems on 1.5.x clients (thanks to alexw)
+- added VTP support for PS, PES and EXTERN (PS requested by mpanczyk)
+- fixed gcc-4.3.0 warnings (thanks to Petri Hintukainen)
+- fixed busy wait when client isn't accepting data fast enough (thanks to
+ Olli Lammi)
+- fixed client reconnect after server restart (reported by alexw)
+- added lock in ~cStreamdevDevice (thanks to Petri Hintukainen)
+- externremux: check for ringbuffer full condition (reported by
+ vdr-freak@vdrportal)
+- diffserv support for traffic shaping and WMM capable WLAN accesspoint
+ (suggested by ollo@vdrportal)
+- check vasprintf() return code (thanks to Rolf Ahrenberg)
+- fixed memory leak in buffer overflow situations (thanks to Rolf Ahrenberg)
+- added PAT, PMT and PCR to HTTP TS streams (thanks to Petri Hintukainen and
+ Rolf Ahrenberg)
+- detect data stream disconnections. Fixes high CPU load (thanks to Petri
+ Hintukainen)
+- fixed segfault with VDR 1.5 (thanks to Petri Hintukainen)
+- made section filtering work (thanks to Petri Hintukainen)
+- added compiler flag -Wall and fixed corresponding warnings (thanks to
+ Rolf Ahrenberg)
+- close pipe when externremux is gone. Fixes high CPU load problem
+- close connection when client is gone. Fixes high CPU load problem
+- silenced compiler warnings (thanks to Rolf Ahrenberg)
+- added commandline parameter for externremux script (thanks to Rolf
+ Ahrenberg)
+- detach receivers before switching transponders
+- API changes for VDR 1.5.0 (thanks to Udo Richter)
+- log connections failures only every 10s (reported by greenman@vdrportal)
+- replaced uint64 by uint64_t
+- added Recursion patch for vdr 1.4
+- added LocalChannelProvide for vdr 1.4.x
+- added respect_ca patch
+- speedup cPluginStreamdevServer::Active() by caching translation (thanks
+ to Udo Richter)
+- periodically check if streamdev-server needs to shutdown (thanks to Udo
+ Richter)
+- collect terminated externremux.sh processes (reported by Norad@vdrportal)
+- avoid fd leaks when we fail to spawn externremux.sh
+- detach all receivers before tuning to a different transponder
+- Re-enabled logging for the Detach()/Attach() issue
+- Added -fPIC compiler flag required on AMD64 architectures
+
+2006-08-17: End of maintenance by Thomas Keil
+
+- updated Finish translation (thanks to Rolf Ahrenberg)
+- fixed fd leak (thanks to Artur Skawina)
+- re-enabled Detach/Attach to temporarily release the device used by
+ streamdev while checking if we can switch transponders (thanks to
+ PanamaJack@vdrportal)
+- adopted to VDR 1.4.x
+
+2006-01-26: End of maintenance by Sascha Volkenandt
+
+- fixed http error response
+- added class forward declaration for gcc >= 4.0
+- adopted to VDR >= 1.3.36
+- added LocalChannelProvide for vdr 1.3.24
+- fixed missing include
+- added TS compatibility mode
+- deleting whole block instead of fractions now
+- fixed wrong remux usage
+- added finish translations (thanks to Rolf Ahrenberg)
+- protected cStreamer::Stop() from being called concurrently
+- some compilers complained about missing declarations, added <ctype.h>
+- removed assembler and thus saving one ringbuffer
+- fixed destruction order on channel switch (fixes one crash that happens
+ occasionally when switching)
+- removed client menu code temporarily
+- streamer now gets stopped when connection terminates unexpectedly
+- fixed recursive delete in streamer
+- fixed pure virtual crash in server
+- audio track selection for http
+
+2004-??-??: Version 0.3.3
+
+- dropped support for non-ts streaming in vdr-to-vdr clients
+- implemented packet buffer that seems to improve distortions
+- greatly re-worked device selection on server and client
+ (vdr-to-vdr clients should behave exactly like clients with one card,
+ can't test conditional access, though)
+- now printing an error and exiting if streamdevhosts.conf is not existing
+- increased client stream priority to 1
+- implemented remote schedule to program remote timers directly from schedule
+- the servers are turned on by default now
+- new setup parameters "Bind to IP" for both servers for binding to a specific
+ interface
+- re-implemented section streaming (turned off by default, see setup menu)
+- implemented a possibility to prevent a shutdown when clients are connected
+ (patch VDR with patches/vdr-pluginactivity.diff if you want this feature)
+- implemented channel listing through channels.htm(l) URI
+
+????-??-??: Version 0.3.2
+
+... has myteriously disappeared :-)
+
+2004-02-16: Version 0.3.1 (unstable)
+
+- Added finnish language texts (thanks to Rolf Ahrenberg)
+- Increased all ringbuffer sizes to 3 MB
+- Autodetecting VDR 1.2.x, 1.2.x with AutoPID and 1.3.x on compilation
+- Server is only restarted if necessary after confirming setup
+- Implemented PID-based streaming (only needed PIDs are transferred instead of
+ all PIDs of the requested channel) (configurable)
+- Implemented an editor for remote timers
+- Implemented manual EPG synchronization from client
+- Implemented Server Suspend remotely from client (configurable)
+- Implemented an IP-Editor for the setup menu
+- Separated Client and Server into two PlugIns
+- Increased initial number of clients to five
+- Implemented host-based authorization (syntax is equal to svdrphosts.conf)
+- Removed two irritating messages that appeared sometimes while exiting VDR
+- Implemented "Choose, Always, Never" for Suspend Mode, so it can be configured
+ to behave like 0.2.0 (Always), 0.3.0 (Choose) or completely different (Never)
+- Added missing translation entries
+- Added PlugIn description to translation table
+- Fully upgraded to VDR 1.3.X regarding threading (but still works with 1.2.6)
+- Reworked manual (almost everything)
+
+2003-10-10: Version 0.3.0 (unstable)
+
+- Implemented "Suspend Live TV" in the VDR server (configurable)
+- Reimplemented choice of device for live streaming (better for switching on
+ client, and server doesn't loose live-tv)
+- Added missing translation entries
+- Increased client's streaming buffer size from 1 to 3 MB
+- Updated installation instructions (including a patch to VDR that is
+ recommended currently)
+- Updated manual
+
+2003-10-04: Version 0.2.0
+
+- Removed those silly warnings in the toolbox-headers
+- Implemented intelligent buffer overflow logging (doesn't flood syslog)
+- Implemented EPG synchronization in the VDR client (configurable)
+- Station name is transmitted in radio streaming now (Shoutcast-format).
+
+2003-09-24: Version 0.1.1beta1
+
+- Restructured remuxer code
+- Added an ES-remuxer for radio channels (currently only manually)
+
+2003-09-20: Version 0.1.0
+
+- Fixed thread-abortion timeout in server thread
+
+2003-08-31: Version 0.1.0beta4
+
+- Added italian language texts (thanks to Angelus (DOm))
+- Added a missing i18n translation (thanks to DOm)
+- Added an #ifdef so the setup menu is displayed correctly with ElchiAIO
+ (thanks to DOm for reporting this one)
+- It's possible to select the HTTP streamtype remotely, specified in the
+ URL in addition to the old behaviour (thanks to Michal Novotny)
+- Fixed creation ob remuxer objects in the server
+- Fixed handling of timeout in cTBSelect
+
+2003-06-08: Version 0.1.0beta3
+
+- Fixed setup menu - now the cursor starts at the first visible entry
+- Added PS streaming for HTTP (should work with most players now)
+- Debugging symbols are only compiled with DEBUG=1 set
+
+2003-06-06: Version 0.1.0beta2
+
+- Added an #ifdef so this PlugIn will compile cleanly with the next
+ AUTOPID-patches
+- Added categories to the menu
+- Fixed segfault when closing the menu with OK
+- Added an AnalogTV section to the README
+- Added some missing i18n entries
+- Corrected client reinitialization code (when changing client settings)
+- Added PS streaming for HTTP (should work with most players now)
+- Added -D_GNU_SOURCE to the Makefile (.......)
+
+2003-06-03: Version 0.1.0beta1
+
+- Replaced the toolbox with a current version
+- Rewrote the server core from scratch
+- Rewrote the client core from scratch
+- Reduced the size of blocks processed in a transceiver turn to 10 TS packets
+- Added TS transmission for HTTP (configurable via setup)
+- Most client settings can be done on-the-fly now
+- MIME type for radio channels now "audio/mpeg" instead of "video/mpeg"
+ (still doesn't work really)
+
+2003-05-08: Version 0.0.3beta1
+
+- Server stops correctly on VDR exit
+- Fixed a race condition when several threads access the client device
+- Made server code more modular
+- Structured the directories
+- Fixed a bug in informational log-message
+- Added Apid2, Dpid1 and Ppid in TS mode (silly me;) )
+
+2003-05-03: Version 0.0.2
+
+- Device is not deactivated anymore, since VDR does that itself
+- Server is correctly deactivated, so it can be faultlessly reactivated
+- Did some major code cleanup
+- Added new command to the PROTOCOL (to negotiate stream types)
+- Added the possibility to stream TS between two VDR's (which adds the
+ possibility of having AC3, Teletext etc. on the client) - this is
+ autonegotiated
+- Streamtype can be changed in the setup menu, if TS works too unreliable
+- Fixed a bug in multi-threaded device operation
+- Sharing an epg.data with a server will be possible even if there is no
+ DVB-Device present
+- Added a basic HTTP daemon to the server code
+
+2003-03-17: Version 0.0.1a
+
+- Corrected some bugs in the README and on the homepage *g*
+
+2003-03-17: Version 0.0.1
+
+- Initial revision.
diff --git a/plugins/streamdev/streamdev-cvs/Makefile b/plugins/streamdev/streamdev-cvs/Makefile
new file mode 100644
index 0000000..d4a7ed3
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/Makefile
@@ -0,0 +1,150 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id: Makefile,v 1.20 2009/11/04 11:12:20 schmirl Exp $
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+#
+PLUGIN = streamdev
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'const char \*VERSION *=' common.c | awk '{ print $$5 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -fPIC -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses
+
+### The directory environment:
+
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR (taken from VDR's "config.h"):
+
+APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+APIVERSNUM = $(shell grep 'define APIVERSNUM ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include -I.
+
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+ifeq ($(shell test $(APIVERSNUM) -ge 10704; echo $$?),0)
+ DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
+endif
+
+### The object files (add further files here):
+
+COMMONOBJS = common.o \
+ \
+ tools/source.o tools/select.o tools/socket.o tools/tools.o
+
+CLIENTOBJS = $(PLUGIN)-client.o \
+ \
+ client/socket.o client/device.o client/setup.o \
+ client/filter.o
+
+
+SERVEROBJS = $(PLUGIN)-server.o \
+ \
+ server/server.o server/component.o server/connection.o \
+ server/componentVTP.o server/componentHTTP.o server/componentIGMP.o \
+ server/connectionVTP.o server/connectionHTTP.o server/connectionIGMP.o \
+ server/streamer.o server/livestreamer.o server/livefilter.o \
+ server/suspend.o server/setup.o server/menuHTTP.o server/recplayer.o \
+ remux/tsremux.o remux/ts2pes.o remux/ts2ps.o remux/ts2es.o remux/extern.o
+
+ifdef DEBUG
+ DEFINES += -DDEBUG
+endif
+
+### The main target:
+
+.PHONY: all i18n dist clean
+all: libvdr-$(PLUGIN)-client.so libvdr-$(PLUGIN)-server.so i18n
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
+
+# Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+ifdef GCC3
+$(DEPFILE): Makefile
+ @rm -f $@
+ @for i in $(CLIENTOBJS:%.o=%.c) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) ; do \
+ $(MAKEDEP) $(DEFINES) $(INCLUDES) -MT "`dirname $$i`/`basename $$i .c`.o" $$i >>$@ ; \
+ done
+else
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(CLIENTOBJS:%.o=%.c) $(SERVEROBJS:%.o=%.c) \
+ $(COMMONOBJS:%.o=%.c) > $@
+endif
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR = po
+LOCALEDIR = $(VDRDIR)/locale
+I18Npo = $(wildcard $(PODIR)/*.po)
+I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
+I18Npot = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+ msgfmt -c -o $@ $<
+
+$(I18Npot): $(CLIENTOBJS:%.o=%.c) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c)
+ xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<http://www.vdr-developer.org/mantisbt/>' -o $@ $^
+
+%.po: $(I18Npot)
+ msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
+ @touch $@
+
+$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+ @mkdir -p $(dir $@)
+ cp $< $@
+
+i18n: $(I18Nmsgs)
+
+### Targets:
+
+libdvbmpeg/libdvbmpegtools.a: libdvbmpeg/*.c libdvbmpeg/*.h
+ $(MAKE) -C ./libdvbmpeg libdvbmpegtools.a
+
+libvdr-$(PLUGIN)-client.so: $(CLIENTOBJS) $(COMMONOBJS) libdvbmpeg/libdvbmpegtools.a
+libvdr-$(PLUGIN)-server.so: $(SERVEROBJS) $(COMMONOBJS) libdvbmpeg/libdvbmpegtools.a
+
+%.so:
+ $(CXX) $(CXXFLAGS) -shared $^ -o $@
+ @cp $@ $(LIBDIR)/$@.$(APIVERSION)
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz --exclude CVS -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(COMMONOBJS) $(CLIENTOBJS) $(SERVEROBJS) $(DEPFILE) $(PODIR)/*.mo $(PODIR)/*.pot *.so *.tgz core* *~
+ $(MAKE) -C ./libdvbmpeg clean
diff --git a/plugins/streamdev/streamdev-cvs/PROTOCOL b/plugins/streamdev/streamdev-cvs/PROTOCOL
new file mode 100644
index 0000000..a0c999e
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/PROTOCOL
@@ -0,0 +1,139 @@
+Written by: Sascha Volkenandt <sascha@akv-soft.de>
+
+Project's homepage: http://www.magoa.net/linux/
+
+Version: 0.0.3
+
+Description:
+------------
+
+I call this protocol "VTP", the Video Transfer Protocol. I hope that's not
+already claimed by someone ;).
+
+This Protocol was created for Video Transfers over a Network. It is a text-
+based protocol like the FTP, and is used by a client to communicate with a
+server providing different types of video data, such as live streams,
+recordings or disc media. The basic communication consists of short text
+commands sent by the client, answered by numerical codes accompanied by
+human-readable messages. All messages should be finished by a full CR/LF
+line-ending, which should preferably written as "\015\012", as this is fully
+platform-independent. Nevertheless, a client or (especially) a server should
+also act on "\n" line-endings. The MPEG data is being transmitted over a
+separate data connection.
+
+TODO:
+- PORT behaviour changed
+- TUNE syntax changed
+- connection IDs
+- new command PLAY
+
+
+Response Code Summary
+
+Code Meaning
+220 Last command ok / connection ready
+221 Service is closing the connection afterwards
+500 The command was not recognized
+501 The parameters couldn't be interpreted correctly
+550 Action not taken, for various reason
+551 Action not taken, a subsequent connection was unsuccessful
+560 Live-Stream not available currently [changed in 0.0.3]
+561 Capability not known [new in 0.0.2]
+562 Pid currently not available [new in 0.0.3]
+563 Stream not available currently [new in 0.0.3]
+
+
+Command Reference
+
+Command: Connect to VTP Server
+Responses: 220 - The server is ready
+Description: Upon connection to the server (which usually listens at port
+ 2004), the first thing the client has to expect is a welcome message with
+ the "220" response code. The client may now send a CAPS command, to tell
+ the server it's capabilities.
+
+Command: CAPS <Capability>
+Responses: 220 - This capability is known and will be used from now on.
+ 561 - This capability is unknown, try anotherone
+Description: This command tells the server to serve media data in a specific
+ format, like "PES" (for MPEG2-PES) or "TS" (for MPEG2-TS). A client can
+ do several CAPS commands until the server accepts one. So a client should
+ try all formats it can handle, descending from the most preffered one. If
+ no such command is sent, streaming is defaulted to PES.
+ Capabilities currently used:
+ TS Transport Stream (all PIDs belonging to a channel)
+ TSPIDS Only in conjunction with TS: Stream PIDs separately upon request
+ (this enables the ADDP/DELP commands)
+ PES Program Elementary stream (Video and primary Audio stream)
+[new in 0.0.2,updated in 0.0.3]
+
+Command: PROV <Priority> <Media>
+Responses: 220 - Media available for receive
+ 501 - The parameters were incorrect
+ 550 - The media couldn't be identified
+ 560 - This server can currently not serve that media
+Description: With this command, the server is asked if the given media can
+ be received. The Priority is a number between 0 and 100 (in case a media
+ can not be received by an unlimited number of clients, the server shall
+ grant higher priorities before lower ones, and it shall also quit streams
+ with lower permissions if a higher one is requested), or -1 to ask the
+ server if this media is available at all.
+ The Media is a string defining the wanted media type. This is currently for
+ free use, it could for example carry a VDR unique channel id, to specify
+ a TV channel.
+
+Command: PORT <Id> <Address and Port>
+Responses: 220 - The data connection was opened
+ 501 - The parameter list was wrong
+ 551 - The data connection was refused by the client or timed out
+Description: The PORT command tells the server the target of a following
+ media transmission. The parameter Id specifies an index which is used to
+ establish multiple data connections over one control connection. It is used
+ later in the ABRT command to close a specific data connection. The second
+ parameter argument has six comma-separated fields, of which the first four
+ represent the target IP address, in the byte-order as the dot-notation
+ would be printed. The last two fields represent the target port, with the
+ high-byte first. To calculate the actual values, you could use the
+ following:
+ Field(5) = (RealPort & 0xFF00) shr 8
+ Field(6) = RealPort & 0xFF
+ Reversed:
+ RealPort = (Field(5) shl 8) + Field(6)
+ After receiving the port command, the data connection is opened but no data
+ is transferred, yet.
+ Id's currently used:
+ 0 Data connection for live streams
+ 1 Data connection for saved streams
+[changed in 0.0.3]
+
+Command: TUNE <Priority> <Media>
+Responses: 220 - Data connection was opened successfully
+ 550 - The media couldn't be identified
+ 560 - The live stream is unavailable
+Description: This command tells the media server to start the transfer over a
+ connection to a remote target established by the PORT command before.
+ Please look at the PROV command for the meaning of the parameters. The
+ server begins to send MPEG data. After the transfer has been started, the
+ response code "220" is sent.
+
+Command: ADDP <Pid>
+Responses: 220 - The requested Pid is transferring
+ 560 - The requested Pid is not available currently
+Description: This tells the server to start the transfer of a specific Pid
+ over a data connection established by the PORT command before.
+
+Command: DELP <Pid>
+Responses: 220 - The requested Pid is transferring
+ 560 - The requested Pid was not transferring
+Description: This tells the server to stop the transfer of a specific Pid
+ enabled by DELP before.
+
+Command: ABRT <Id>
+Responses: 220 - Data connection closed
+Description: This one should be sent before requesting another media or when
+ a media isn't needed anymore. It terminates the data connection previously
+ opened by PORT.
+
+Command: QUIT
+Responses: 221 - Connection is being closed afterwards
+Description: This commands terminates the client connection.
diff --git a/plugins/streamdev/streamdev-cvs/README b/plugins/streamdev/streamdev-cvs/README
new file mode 100644
index 0000000..8909653
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/README
@@ -0,0 +1,432 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Sascha Volkenandt <sascha@akv-soft.de>
+Current maintainer: Frank Schmirler <vdrdev@schmirler.de>
+
+Project's homepage: http://streamdev.vdr-developer.org/
+Former project homepage: http://linux.kompiliert.net/
+
+Latest version available at: http://streamdev.vdr-developer.org/
+
+See the file COPYING for license information.
+
+Contents:
+---------
+
+1. Description
+2. Installation
+2.1 VDR 1.4.x and older
+2.2 VDR 1.6.0 and above
+2.3 Updating from streamdev 0.3.x
+3. Usage
+3.1 Usage HTTP server
+3.2 Usage IGMP multicast server
+3.3 Usage VDR-to-VDR server
+3.4 Usage VDR-to-VDR client
+4. Other useful Plugins
+4.1 Plugins for VDR-to-VDR clients
+4.2 Plugins for Server
+4.3 Alternatives
+5. Known Problems
+
+
+1. Description:
+---------------
+
+This PlugIn is a VDR implementation of the VTP (Video Transfer Protocol)
+Version 0.0.3 (see file PROTOCOL) and a basic HTTP Streaming Protocol.
+
+It consists of a server and a client part, but both parts are compiled together
+with the PlugIn source, but appear as separate PlugIns to VDR.
+
+The client part acts as a full Input Device, so it can be used in conjunction
+with a DXR3-Card, XINE, SoftDevice or others to act as a working VDR
+installation without any DVB-Hardware including EPG-Handling.
+
+The server part acts as a Receiver-Device and works transparently in the
+background within your running VDR. It can serve multiple clients and it can
+distribute multiple input streams (i.e. from multiple DVB-cards) to multiple
+clients using the native VTP protocol (for VDR-clients), or using the HTTP
+protocol supporting clients such as XINE, MPlayer and so on. With XMMS or
+WinAMP, you can also listen to radio channels over a HTTP connection.
+
+It is possible to attach as many clients as the bus and network can handle, as
+long as there is a device which can receive a specific channel. Multiple
+channels homed on the same transponder (which is determined by it's frequency)
+can be broadcasted with a single device.
+
+Additional clients can be programmed using the Protocol Instructions inside
+the PROTOCOL file.
+
+
+2. Installation:
+----------------
+
+Let's say streamdev's version is 0.4.0 and vdr's version is 1.X.X. If you
+use anything else please exchange the version numbers appropriately (this
+way I don't have to update this section all the times;) ).
+
+After compiling the PlugIn as stated below, start either (or both) parts of it
+by specifying "-P streamdev-client" and/or "-P streamdev-server" on the VDR
+command line.
+
+What's important is that the client requests a channel using its Unique Channel
+ID. So, in order to find the channel at the server, it must have the same ID
+that is used on the client. You can achieve this by putting the server's
+channels.conf on the client, preferably after scanning.
+
+If you want to drive additional Input-Devices (with different sources) on the
+client, you can merge the channels.conf files. VDR will detect if the local
+device or the network device can receive the channels.
+
+Last, but not least you have to copy the streamdev folder into the
+"plugins/streamdev" subfolder of VDR's config-directory (which is equal to your
+video-directory if not specified otherwise). For example, if you didn't specify
+a separate config-directory, and specified your video directory as "/video0",
+the directory has to be copied to /video0/plugins/streamdev.
+
+The directory contains a file named streamdevhosts.conf which you must adjust
+to your needs. The syntax is the same as for svdrphosts.conf, so please consult
+VDR's documentation on how to fill that file, if you can't do it on-the-fly.
+
+There's also a sample externremux.sh script in this directory. It is used by
+streamdev's external remux feature. The sample script uses mencoder. Please
+check the script for further information. You can specify a different script
+location with the -r parameter. The VDR commandline would then include a
+"-P 'streamdev-server -r /usr/local/bin/remux.sh'". Note the additional quotes,
+as otherwise -r will be passed to VDR and not to streamdev.
+
+
+2.1 VDR 1.4.x and older:
+------------------------
+
+This version is not compatible to VDR releases older than 1.5.9. Take one of
+the streamdev-0.4.x releases if you are running at least VDR 1.4.x. For older
+VDRs you will probably need one of the streamdev-0.3.x releases.
+
+2.2 VDR 1.6.0 and above:
+------------------------
+
+cd vdr-1.X.X/PLUGINS/src
+tar xvfz vdr-streamdev-0.4.0.tgz
+ln -s streamdev-0.4.0 streamdev
+cp -r streamdev/streamdev VDRCONFDIR/plugins/
+cd ../..
+make [options, if necessary] vdr
+make [options, if necessary] plugins
+
+2.3 Updating from streamdev 0.3.x
+----------------------------------
+
+Starting with streamdev 0.4.0, all additional files are kept in a directory
+called "streamdev" inside VDR's plugin config directory. It is the new default
+location of externremux.sh and the new place where streamdev-server expects the
+file "streamdevhosts.conf". You will have to move this file to its new location:
+
+mv VDRCONFDIR/plugins/streamdevhosts.conf VDRCONFDIR/plugins/streamdev/
+
+(Directory VDRCONFDIR/plugins/streamdev already exists, as you copied the
+whole folder from the sources directory as suggested above, right?)
+
+Now check the contents of streamdevhosts.conf. Does it contain a "0.0.0.0/0"
+entry? If your VDR machine is connected to the Internet, this line gives
+*anyone* full access to streamdev, unless you took some other measures to
+prevent this (e.g. firewall). You might want to remove this line and enable
+HTTP authentication instead.
+
+
+3. Usage:
+---------
+
+Start the server core itself by specifying -Pstreamdev-server on your VDR
+commandline. To use the client core, specify -Pstreamdev-client. Both parts
+can run in one VDR instance, if necessary.
+
+The parameter "Suspend behaviour" allows you to specify how the server should
+react in case the client requests a channel that would require switching the
+primary device (i.e. disrupt live-tv). If set to "Offer suspend mode", you will
+have a new entry in the main menu. Activating that will put the server into
+"Suspend Mode" (a picture is displayed on TV). Then, a client may switch the
+primary card to wherever it likes to. While watching TV (Suspend deactivated),
+the client may not switch the transponder on the primary device. If you set
+the behaviour to "Always suspended" (the default), there will be normal live-tv
+on the server, but whenever a client decides to switch the transponder, the
+server will lose it's live-tv. Set to "Never suspended", the server always
+prevents the client from switching transponders. If you set "Client may
+suspend" to yes, the client can suspend the server remotely (this only applies
+if "Offer suspend mode" is selected).
+
+NOTE: This mainly applies to One-Card-Systems, since with multiple cards there
+is no need to switch transponders on the primary interface, if the secondary
+can stream a given channel (i.e. if it is not blocked by a recording). If both
+cards are in use (i.e. when something is recorded, or by multiple clients),
+this applies to Multiple-Card-Systems as well.
+
+3.1 Usage HTTP server:
+----------------------
+
+You can use the HTTP part by accessing the server with a HTTP-capable media
+player (such as XINE, MPlayer, and so on, if you have appropriate MPEG2-codecs
+installed). In the PlugIn's Setup, you can specify the port the server will
+listen to with the parameter "HTTP Server Port". The parameter "HTTP Streamtype"
+allows you to specify a default stream type, which is used if no specific type
+has been requested in the URL (see below). The supported stream types are:
+
+TS Transport Stream (i.e. a dump from the device)
+PES Packetized Elemetary Stream (VDR's native recording format)
+PS Program Stream (SVCD, DVD like stream)
+ES Elementary Stream (only Video, if available, otherwise only Audio)
+EXTERN Pass stream through external script (e.g. for converting with mencoder)
+
+Assuming that you leave the default port (3000), point your web browser to
+
+http://hostname:3000/
+
+You will be presented a menu with links to various channel lists, including M3U
+playlist formats.
+
+If you don't want to use the HTML menu or the M3U playlists, you can access the
+streams directly like this:
+
+http://hostname:3000/3
+http://hostname:3000/S19.2E-0-12480-898
+
+The first one will deliver a channel by number on the server, the second one
+will request the channel by unique channel id. In addition, you can specify
+the desired stream type as a path to the channel.
+
+http://hostname:3000/TS/3
+http://hostname:3000/PES/S19.2E-0-12480-898
+
+The first one would deliver the stream in TS, the second one in PES format.
+Possible values are 'PES', 'TS', 'PS', 'ES' and 'EXTERN'. You need to specify
+the ES format explicitly if you want to listen to radio channels. Play them
+back i.e. with mpg123.
+
+mpg123 http://hostname:3000/ES/200
+
+With 'EXTERN' you can also add a parameter which is passed as argument to the
+externremux script.
+
+http://hostname:3000/EXTERN;some_parameter/3
+
+If you want to access streamdev's HTTP server from the Internet, do *not* grant
+access for anyone by allowing any IP in "streamdevhosts.conf". Instead, pass the
+"-a" commandline option to streamdev-server. It takes a username and a password
+as argument. Clients with an IP not accepted by "streamdevhosts.conf" will then
+have to login. The VDR commandline will have to look like this:
+
+vdr ... -P 'streamdev-server -a vdr:secret' ...
+
+Note the single quotes, as otherwise "-a" will be passed to VDR and not to
+streamdev-server. The login ("vdr" in the example above) doesn't have to exist
+as a system account.
+
+3.2 Usage IGMP multicast server:
+--------------------------------
+
+IGMP based multicast streaming is often used by settop boxes to receive IP TV.
+Streamdev's multicast server allows you to feed live TV from VDR to such a
+settop box. VLC is known to work well if you look for a software client.
+
+The advantage of multicasting is that the actual stream is sent out only once,
+regardless of how many clients want to receive it. The downside is, that you
+cannot simply multicast across network boundaries. You need multicast routers.
+For multicast streaming over the public Internet you would even need to register
+for your own IP range. So don't even think of multicasting via Internet with
+streamdev! Streamdev will send the stream only to one local ethernet segment and
+all clients must be connected to this same segment. There must not be a router
+inbetween. Also note that the client must not run on the streamdev-server
+machine.
+
+Each channel is offered on a different multicast IP. Channel 1 is available from
+multicast IP 239.255.0.1, channel 2 from 239.255.0.2 and so on. The upper limit
+is 239.255.254.255 which corresponds to channel 65279 (239.255.255.0/24 is
+reserved according to RFC-2365).
+
+Before you can use streamdev's multicast server, you might need to patch VDR.
+Binding an IGMP socket is a privileged operation, so you must start VDR as root.
+If you pass the -u option to VDR, it will drop almost all priviledges before
+streamdev is even loaded. Apply vdr-cap_net_raw.diff to keep VDR from dropping
+the CAP_NET_RAW capability required to bind the IGMP socket. The patch is part
+of streamdev's source distribution. Check the patches subdirectory. There's no
+need to patch VDR if it is kept running as root (not recommended).
+
+The multicast server is disabled by default. Enter the streamdev-server setup
+menu to enable it and - IMPORTANT - bind the multicast server to the IP of your
+VDR server's LAN ethernet card. The multicast server will refuse to start with
+the default bind adresse "0.0.0.0".
+
+Now edit your streamdevhosts.conf. To allow streaming of all channels, it must
+contain "239.255.0.0/16". Note that you cannot limit connections by client IP
+here. You can however restrict which channels are allowed to be multicasted.
+Enter individual multicast IPs instead of "239.255.0.0/16".
+
+By default, the linux kernel will refuse to join more than 20 multicast groups.
+You might want to increase this up to "number_of_channels + 1". Note that it's
+"number_of_channels", not "maximum_channel_number".
+
+ #First 100 channels:
+ bash# sysctl -w net.ipv4.igmp_max_memberships=101
+
+ #All channels:
+ bash# COUNT=$(grep -c '^[^:]' PATH_TO_YOUR/channels.conf)
+ bash# sysctl -w net.ipv4.igmp_max_memberships=$COUNT
+
+A multicast server never knows how many clients are actually receiving a stream.
+If a client signals that it leaves a multicast group, the server has to query
+for other listeners before it can stop the stream. This may delay zapping from
+one transponder to an other. The client will probably requests the new channel
+before the previous stream has been stopped. If there's no free DVB card, VDR
+won't be able to fulfill the request until a DVB card becomes available and the
+client resends the request.
+
+3.3 Usage VDR-to-VDR server:
+----------------------------
+
+You can activate the VDR-to-VDR server part in the PlugIn's Setup Menu. It is
+deactivated by default. The Parameter "VDR-to-VDR Server Port" specifies the
+port where you want the server to listen for incoming connections. The server
+will be activated when you push the OK button inside the setup menu, so there's
+no need to restart VDR.
+
+3.4 Usage VDR-to-VDR client:
+----------------------------
+
+Streamdev-client adds a "Suspend Server" item to VDR's mainmenu. With the
+setup parameter "Hide Mainmenu Entry" you can hide this menu item if you don't
+need it. "Suspend Server" is only useful if the server runs in "Offer suspend
+mode" with "Client may suspend" enabled.
+
+The parameter "Remote IP" uses an IP-Adress-Editor, where you can just enter
+the IP number with the number keys on your remote. After three digits (or if
+the next digit would result in an invalid IP adress, or if the first digit is
+0), the current position jumps to the next one. You can change positions with
+the left and right buttons, and you can cycle the current position using up
+and down. To confirm the entered address, press OK. So, if you want to enter
+the IP address "127.0.0.1", just mark the corresponding entry as active and
+type "127001<OK>" on your remote. If you want to enter "192.168.1.12", type
+"1921681<Right>12<OK>".
+
+The parameters "Remote IP" and "Remote Port" in the client's setup specify the
+address of the remote VDR-to-VDR server to connect to. Activate the client by
+setting "Start Client" to yes. It is disabled by default, because it wouldn't
+make much sense to start the client without specifying a server anyway. The
+client is activated after you push the OK button, so there's no need to restart
+VDR. Deactivation on-the-fly is not possible, so in order to deactivate the
+client, you will have to restart VDR. However requests to switch channels will
+be refused by streamdev-client once it has been deactivated. All other settings
+can be changed without restarting VDR.
+
+The client will try to connect to the server (in case it isn't yet) whenever
+a remote channel is requested. Just activate the client and switch to a
+channel that's not available by local devices. If anything goes wrong with the
+connection between the two, you will see it in the logfile instantly. If you
+now switch the client to a channel which isn't covered by it's own local
+devices, it will ask the server for it. If the server can (currently) receive
+that channel, the client will show it until you switch again, or until the
+server needs that card (if no other is free) for a recording on a different
+transponder.
+
+Only the needed PIDs are transferred, and additional PIDs can be turned on
+during an active transfer. This makes it possible to switch languages, receive
+additional channels (for recording on the client) and use plugins that use
+receivers themselves (like osdteletext).
+
+With "Filter Streaming" enabled, the client will receive meta information like
+EPG data and service information, just as if the client had its own DVB card.
+Link channels and even a client-side EPG scan have been reported to work.
+
+The next parameter, "Synchronize EPG", will have the client synchronize it's
+program table with the server every now and then, but not regularly. This
+happens when starting the client, and everytime VDR does its housekeeping
+tasks. The only thing that's guaranteed is, that there will be a minimum
+interval of ten seconds between each EPG synchronization. With "Filter
+Streaming" this option has been obsoleted. If you still need to synchronize
+EPG as additional information is available from the server, you should use the
+epgsync-plugin instead (http://vdr.schmirler.de).
+
+Finally with the maximum and minimum priority, you can keep VDR from considering
+streamdev in certain cases. If for instance you have a streamdev client with its
+own DVB card, VDR would normally use streamdev for recording. If this is not
+what you want, you could set the maximum priority to 0. As recordings usually
+have a much higher priority (default 50), streamdev is now no longer used for
+recordings. The two parameters define the inclusive range of priorities for
+which streamdev will accept to tune. Setting the minimum priority to a higher
+value than the maximum, you will get two ranges: "up to maximum" and "minimum
+and above".
+
+4. Other useful Plugins:
+------------------------
+
+4.1 Plugins for VDR-to-VDR clients:
+-----------------------------------
+
+The following plugins are useful for VDR-to-VDR clients (i.e. VDRs running the
+streamdev-client):
+
+* remotetimers (http://vdr.schmirler.de/)
+Add, edit, delete timers on client and server
+
+* timersync (http://phivdr.dyndns.org/vdr/vdr-timersync/)
+Automatically syncronizes timer lists of client and server. All recordings will
+be made on the server
+
+* remoteosd (http://vdr.schmirler.de/)
+Provides access to the server's OSD menu
+
+* epgsync (http://vdr.schmirler.de/)
+Import EPG from server VDR
+
+* femon (http://www.saunalahti.fi/~rahrenbe/vdr/femon/)
+Display signal information from server's DVB card. SVDRP support must be enabled
+in femon's setup
+
+4.2 Plugins for Server:
+-----------------------
+
+* dummydevice (http://phivdr.dyndns.org/vdr/vdr-dummydevice/)
+Recommended on a headless server (i.e. a server with no real output device).
+Without this plugin, a budget DVB card could become VDR's primary device. This
+causes unwanted sideeffects in certain situations.
+
+4.3 Alternatives:
+-----------------
+
+* xineliboutput (http://phivdr.dyndns.org/vdr/vdr-xineliboutput/)
+With its networking option, xineliboutput provides an alternative to streamdev.
+You will get the picture of the server VDR, including its OSD. However you
+won't get independent clients, as they all share the same output.
+
+5. Known Problems:
+------------------
+
+* In VDR-to-VDR setup, the availability of a channel is checked with a different
+priority than the actual channel switch. The later always uses priority 0.
+Usually a channel switch for live TV has priority 0 anyway, so it is not a
+problem here. However timers usually have a higher priority. Either avoid
+client side recordings or set the priority of client side timers to 0.
+
+* There have been reports that channel switching with VDR 1.5.x/1.6.x clients
+sometimes fails. Current version includes a workaround which seems to work, but
+YMMV ;)
+
+* Viewing encrypted channels became an issue with VDR's new CAM handling code.
+Streamdev doesn't provide a (dummy) CAM, so out of the box, VDR won't ever try
+to receive encrypted channels from streamdev. Pick one of the following
+solutions to work around the problem:
+
+1. Force VDR to use streamdev. Open the channels menu on the client (or edit its
+channels.conf if you know how to do this) and set the CA field of all channels
+that only the server can decrypt to streamdev's device index. Usually streamdev
+will get number 9 or 10. Streamdev logs the actual device number when starting
+up. So please consider the logs for the correct value. Remember to fill in
+hexadecimal values if you are using an editor to modify your channels.conf
+(number 10 becomes an "a", number 11 a "b", ...).
+
+2. Apply either patch "patches/vdr-1.6.0-intcamdevices.patch" or patch
+"patches/vdr-1.6.0-ignore_missing_cam.diff" to your client VDR. Intcamdevices
+is the clean solution. But as it modifies the VDR API, so you will need to
+recompile all of your plugins. The ignore_missing_cam patch is trivial, no need
+to recompile other plugins. However it is not suitable for clients with a DVB
+card of their own.
diff --git a/plugins/streamdev/streamdev-cvs/client/CVS/Entries b/plugins/streamdev/streamdev-cvs/client/CVS/Entries
new file mode 100644
index 0000000..8647f66
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/client/CVS/Entries
@@ -0,0 +1,9 @@
+/device.c/1.25/Wed Feb 17 12:39:03 2010//
+/device.h/1.9/Tue Jun 23 10:26:54 2009//
+/filter.c/1.14/Fri Feb 13 13:02:39 2009//
+/filter.h/1.5/Mon Apr 7 14:27:28 2008//
+/setup.c/1.9/Fri Sep 18 10:43:26 2009//
+/setup.h/1.6/Fri Sep 18 10:43:26 2009//
+/socket.c/1.12/Tue Apr 8 14:18:16 2008//
+/socket.h/1.6/Mon Apr 7 14:40:40 2008//
+D
diff --git a/plugins/streamdev/streamdev-cvs/client/CVS/Repository b/plugins/streamdev/streamdev-cvs/client/CVS/Repository
new file mode 100644
index 0000000..885b4a3
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/client/CVS/Repository
@@ -0,0 +1 @@
+streamdev/client
diff --git a/plugins/streamdev/streamdev-cvs/client/CVS/Root b/plugins/streamdev/streamdev-cvs/client/CVS/Root
new file mode 100644
index 0000000..2c7f6ce
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/client/CVS/Root
@@ -0,0 +1 @@
+:pserver:anoncvs@vdr-developer.org:/var/cvsroot
diff --git a/plugins/streamdev/streamdev-cvs/client/device.c b/plugins/streamdev/streamdev-cvs/client/device.c
new file mode 100644
index 0000000..551d7c2
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/client/device.c
@@ -0,0 +1,306 @@
+/*
+ * $Id: device.c,v 1.25 2010/02/17 12:39:03 schmirl Exp $
+ */
+
+#include "client/device.h"
+#include "client/setup.h"
+#include "client/filter.h"
+
+#include "tools/select.h"
+
+#include <vdr/channels.h>
+#include <vdr/ringbuffer.h>
+#include <vdr/eit.h>
+#include <vdr/timers.h>
+
+#include <time.h>
+#include <iostream>
+
+using namespace std;
+
+#define VIDEOBUFSIZE MEGABYTE(3)
+
+cStreamdevDevice *cStreamdevDevice::m_Device = NULL;
+
+cStreamdevDevice::cStreamdevDevice(void) {
+ m_Channel = NULL;
+ m_TSBuffer = NULL;
+
+ m_Filters = new cStreamdevFilters;
+ StartSectionHandler();
+ isyslog("streamdev-client: got device number %d", CardIndex() + 1);
+
+ m_Device = this;
+ m_Pids = 0;
+ m_DvrClosed = true;
+
+ if (StreamdevClientSetup.SyncEPG)
+ ClientSocket.SynchronizeEPG();
+}
+
+cStreamdevDevice::~cStreamdevDevice() {
+ Dprintf("Device gets destructed\n");
+
+ Lock();
+ m_Device = NULL;
+ m_Filters->SetConnection(-1);
+ ClientSocket.Quit();
+ ClientSocket.Reset();
+ Unlock();
+
+ Cancel(3);
+
+#if APIVERSNUM >= 10515
+ StopSectionHandler();
+#endif
+ DELETENULL(m_Filters);
+ DELETENULL(m_TSBuffer);
+}
+
+bool cStreamdevDevice::ProvidesSource(int Source) const {
+ Dprintf("ProvidesSource, Source=%d\n", Source);
+ return true;
+}
+
+bool cStreamdevDevice::ProvidesTransponder(const cChannel *Channel) const
+{
+ Dprintf("ProvidesTransponder\n");
+ return true;
+}
+
+bool cStreamdevDevice::IsTunedToTransponder(const cChannel *Channel)
+{
+ bool res = false;
+ if (ClientSocket.DataSocket(siLive) != NULL
+ && TRANSPONDER(Channel, m_Channel)
+ && Channel->Ca() == CA_FTA
+ && m_Channel->Ca() == CA_FTA)
+ res = true;
+ return res;
+}
+
+bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
+ bool *NeedsDetachReceivers) const {
+ bool res = false;
+ bool prio = Priority < 0 || Priority > this->Priority();
+ bool ndr = false;
+
+ if (!StreamdevClientSetup.StartClient)
+ return false;
+
+ Dprintf("ProvidesChannel, Channel=%s, Prio=%d\n", Channel->Name(), Priority);
+
+ if (StreamdevClientSetup.MinPriority <= StreamdevClientSetup.MaxPriority)
+ {
+ if (Priority < StreamdevClientSetup.MinPriority ||
+ Priority > StreamdevClientSetup.MaxPriority)
+ return false;
+ }
+ else
+ {
+ if (Priority < StreamdevClientSetup.MinPriority &&
+ Priority > StreamdevClientSetup.MaxPriority)
+ return false;
+ }
+
+ if (ClientSocket.DataSocket(siLive) != NULL
+ && TRANSPONDER(Channel, m_Channel))
+ res = true;
+ else {
+ res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
+ ndr = true;
+ }
+
+ if (NeedsDetachReceivers)
+ *NeedsDetachReceivers = ndr;
+ Dprintf("prov res = %d, ndr = %d\n", res, ndr);
+ return res;
+}
+
+bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
+ bool LiveView) {
+ Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(),
+ LiveView ? "true" : "false");
+
+ if (LiveView)
+ return false;
+
+ if (ClientSocket.DataSocket(siLive) != NULL
+ && TRANSPONDER(Channel, m_Channel)
+ && Channel->Ca() == CA_FTA
+ && m_Channel->Ca() == CA_FTA)
+ return true;
+
+ DetachAllReceivers();
+ m_Channel = Channel;
+ bool r = ClientSocket.SetChannelDevice(m_Channel);
+ Dprintf("setchanneldevice r=%d\n", r);
+ return r;
+}
+
+bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) {
+ Dprintf("SetPid, Pid=%d, Type=%d, On=%d, used=%d\n", Handle->pid, Type, On,
+ Handle->used);
+ LOCK_THREAD;
+
+ if (On && !m_TSBuffer) {
+ Dprintf("SetPid: no data connection -> OpenDvr()");
+ OpenDvrInt();
+ }
+
+ bool res = true;
+ if (Handle->pid && (On || !Handle->used)) {
+ res = ClientSocket.SetPid(Handle->pid, On);
+
+ m_Pids += (!res) ? 0 : On ? 1 : -1;
+ if (m_Pids < 0)
+ m_Pids = 0;
+
+ if(m_Pids < 1 && m_DvrClosed) {
+ Dprintf("SetPid: 0 pids left -> CloseDvr()");
+ CloseDvrInt();
+ }
+ }
+
+ return res;
+}
+
+bool cStreamdevDevice::OpenDvrInt(void) {
+ Dprintf("OpenDvrInt\n");
+ LOCK_THREAD;
+
+ CloseDvrInt();
+ if (m_TSBuffer) {
+ Dprintf("cStreamdevDevice::OpenDvrInt(): DVR connection already open\n");
+ return true;
+ }
+
+ Dprintf("cStreamdevDevice::OpenDvrInt(): Connecting ...\n");
+ if (ClientSocket.CreateDataConnection(siLive)) {
+ m_TSBuffer = new cTSBuffer(*ClientSocket.DataSocket(siLive), MEGABYTE(2), CardIndex() + 1);
+ return true;
+ }
+ esyslog("cStreamdevDevice::OpenDvrInt(): DVR connection FAILED");
+ return false;
+}
+
+bool cStreamdevDevice::OpenDvr(void) {
+ Dprintf("OpenDvr\n");
+ LOCK_THREAD;
+
+ m_DvrClosed = false;
+ return OpenDvrInt();
+}
+
+void cStreamdevDevice::CloseDvrInt(void) {
+ Dprintf("CloseDvrInt\n");
+ LOCK_THREAD;
+
+ if (ClientSocket.CheckConnection()) {
+ if (!m_DvrClosed) {
+ Dprintf("cStreamdevDevice::CloseDvrInt(): m_DvrClosed=false -> not closing yet\n");
+ return;
+ }
+ if (m_Pids > 0) {
+ Dprintf("cStreamdevDevice::CloseDvrInt(): %d active pids -> not closing yet\n", m_Pids);
+ return;
+ }
+ } else {
+ Dprintf("cStreamdevDevice::CloseDvrInt(): Control connection gone !\n");
+ }
+
+ Dprintf("cStreamdevDevice::CloseDvrInt(): Closing DVR connection\n");
+ // Hack for VDR 1.5.x clients (sometimes sending ABRT after TUNE)
+ // TODO: Find a clean solution to fix this
+ ClientSocket.SetChannelDevice(m_Channel);
+ ClientSocket.CloseDvr();
+ DELETENULL(m_TSBuffer);
+}
+
+void cStreamdevDevice::CloseDvr(void) {
+ Dprintf("CloseDvr\n");
+ LOCK_THREAD;
+
+ m_DvrClosed = true;
+ CloseDvrInt();
+}
+
+bool cStreamdevDevice::GetTSPacket(uchar *&Data) {
+ if (m_TSBuffer && m_Device) {
+ Data = m_TSBuffer->Get();
+#if 1 // TODO: this should be fixed in vdr cTSBuffer
+ // simple disconnect detection
+ static int m_TSFails = 0;
+ if (!Data) {
+ LOCK_THREAD;
+ if(!ClientSocket.DataSocket(siLive)) {
+ return false; // triggers CloseDvr() + OpenDvr() in cDevice
+ }
+ cPoller Poller(*ClientSocket.DataSocket(siLive));
+ errno = 0;
+ if (Poller.Poll() && !errno) {
+ char tmp[1];
+ if (recv(*ClientSocket.DataSocket(siLive), tmp, 1, MSG_PEEK) == 0 && !errno) {
+esyslog("cStreamDevice::GetTSPacket: GetChecked: NOTHING (%d)", m_TSFails);
+ m_TSFails++;
+ if (m_TSFails > 10) {
+ isyslog("cStreamdevDevice::GetTSPacket(): disconnected");
+ m_Pids = 0;
+ CloseDvrInt();
+ m_TSFails = 0;
+ return false;
+ }
+ return true;
+ }
+ }
+ m_TSFails = 0;
+ }
+#endif
+ return true;
+ }
+ return false;
+}
+
+int cStreamdevDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
+ Dprintf("OpenFilter\n");
+
+ if (!StreamdevClientSetup.StreamFilters)
+ return -1;
+
+
+ if (!ClientSocket.DataSocket(siLiveFilter)) {
+ if (ClientSocket.CreateDataConnection(siLiveFilter)) {
+ m_Filters->SetConnection(*ClientSocket.DataSocket(siLiveFilter));
+ } else {
+ isyslog("cStreamdevDevice::OpenFilter: connect failed: %m");
+ return -1;
+ }
+ }
+
+ if (ClientSocket.SetFilter(Pid, Tid, Mask, true))
+ return m_Filters->OpenFilter(Pid, Tid, Mask);
+
+ return -1;
+}
+
+bool cStreamdevDevice::Init(void) {
+ if (m_Device == NULL && StreamdevClientSetup.StartClient)
+ new cStreamdevDevice;
+ return true;
+}
+
+bool cStreamdevDevice::ReInit(void) {
+ if(m_Device) {
+ m_Device->Lock();
+ m_Device->m_Filters->SetConnection(-1);
+ m_Device->m_Pids = 0;
+ }
+ ClientSocket.Quit();
+ ClientSocket.Reset();
+ if (m_Device != NULL) {
+ //DELETENULL(m_Device->m_TSBuffer);
+ m_Device->Unlock();
+ }
+ return StreamdevClientSetup.StartClient ? Init() : true;
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/client/device.h b/plugins/streamdev/streamdev-cvs/client/device.h
new file mode 100644
index 0000000..e96b05f
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/client/device.h
@@ -0,0 +1,68 @@
+/*
+ * $Id: device.h,v 1.9 2009/06/23 10:26:54 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_DEVICE_H
+#define VDR_STREAMDEV_DEVICE_H
+
+#include <vdr/device.h>
+
+#include "client/socket.h"
+#include "client/filter.h"
+
+class cTBString;
+
+#define CMD_LOCK_OBJ(x) cMutexLock CmdLock((cMutex*)&(x)->m_Mutex)
+
+class cStreamdevDevice: public cDevice {
+ friend class cRemoteRecordings;
+
+private:
+ const cChannel *m_Channel;
+ cTSBuffer *m_TSBuffer;
+ cStreamdevFilters *m_Filters;
+ int m_Pids;
+ bool m_DvrClosed;
+
+ static cStreamdevDevice *m_Device;
+
+ bool OpenDvrInt(void);
+ void CloseDvrInt(void);
+
+protected:
+ virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+ virtual bool HasLock(int TimeoutMs)
+ {
+ //printf("HasLock is %d\n", (ClientSocket.DataSocket(siLive) != NULL));
+ //return ClientSocket.DataSocket(siLive) != NULL;
+ return true;
+ }
+
+ virtual bool SetPid(cPidHandle *Handle, int Type, bool On);
+ virtual bool OpenDvr(void);
+ virtual void CloseDvr(void);
+ virtual bool GetTSPacket(uchar *&Data);
+
+ virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
+
+public:
+ cStreamdevDevice(void);
+ virtual ~cStreamdevDevice();
+
+ virtual bool HasInternalCam(void) { return true; }
+ virtual bool ProvidesSource(int Source) const;
+ virtual bool ProvidesTransponder(const cChannel *Channel) const;
+ virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1,
+ bool *NeedsDetachReceivers = NULL) const;
+#if APIVERSNUM >= 10700
+ virtual int NumProvidedSystems(void) const { return 1; }
+#endif
+ virtual bool IsTunedToTransponder(const cChannel *Channel);
+
+ static bool Init(void);
+ static bool ReInit(void);
+
+ static cStreamdevDevice *GetDevice(void) { return m_Device; }
+};
+
+#endif // VDR_STREAMDEV_DEVICE_H
diff --git a/plugins/streamdev/streamdev-cvs/client/filter.c b/plugins/streamdev/streamdev-cvs/client/filter.c
new file mode 100644
index 0000000..c187e05
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/client/filter.c
@@ -0,0 +1,292 @@
+/*
+ * $Id: filter.c,v 1.14 2009/02/13 13:02:39 schmirl Exp $
+ */
+
+#include "client/filter.h"
+#include "client/socket.h"
+#include "tools/select.h"
+#include "common.h"
+
+#include <vdr/device.h>
+
+#define PID_MASK_HI 0x1F
+// --- cStreamdevFilter ------------------------------------------------------
+
+class cStreamdevFilter: public cListObject {
+private:
+ uchar m_Buffer[4096];
+ int m_Used;
+ int m_Pipe[2];
+ u_short m_Pid;
+ u_char m_Tid;
+ u_char m_Mask;
+
+public:
+ cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask);
+ virtual ~cStreamdevFilter();
+
+ bool Matches(u_short Pid, u_char Tid);
+ bool PutSection(const uchar *Data, int Length, bool Pusi);
+ int ReadPipe(void) const { return m_Pipe[0]; }
+
+ bool IsClosed(void);
+ void Reset(void);
+
+ u_short Pid(void) const { return m_Pid; }
+ u_char Tid(void) const { return m_Tid; }
+ u_char Mask(void) const { return m_Mask; }
+};
+
+inline bool cStreamdevFilter::Matches(u_short Pid, u_char Tid) {
+ return m_Pid == Pid && m_Tid == (Tid & m_Mask);
+}
+
+cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
+ m_Used = 0;
+ m_Pid = Pid;
+ m_Tid = Tid;
+ m_Mask = Mask;
+ m_Pipe[0] = m_Pipe[1] = -1;
+
+#ifdef SOCK_SEQPACKET
+ // SOCK_SEQPACKET (since kernel 2.6.4)
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, m_Pipe) != 0) {
+ esyslog("streamdev-client: socketpair(SOCK_SEQPACKET) failed: %m, trying SOCK_DGRAM");
+ }
+#endif
+ if (m_Pipe[0] < 0 && socketpair(AF_UNIX, SOCK_DGRAM, 0, m_Pipe) != 0) {
+ esyslog("streamdev-client: couldn't open section filter socket: %m");
+ }
+
+ else if(fcntl(m_Pipe[0], F_SETFL, O_NONBLOCK) != 0 ||
+ fcntl(m_Pipe[1], F_SETFL, O_NONBLOCK) != 0) {
+ esyslog("streamdev-client: couldn't set section filter socket to non-blocking mode: %m");
+ }
+}
+
+cStreamdevFilter::~cStreamdevFilter() {
+ Dprintf("~cStreamdevFilter %p\n", this);
+
+ // ownership of handle m_Pipe[0] has been transferred to VDR section handler
+ //if (m_Pipe[0] >= 0)
+ // close(m_Pipe[0]);
+ if (m_Pipe[1] >= 0)
+ close(m_Pipe[1]);
+}
+
+bool cStreamdevFilter::PutSection(const uchar *Data, int Length, bool Pusi) {
+
+ if (!m_Used && !Pusi) /* wait for payload unit start indicator */
+ return true;
+ if (m_Used && Pusi) /* reset at payload unit start */
+ Reset();
+
+ if (m_Used + Length >= (int)sizeof(m_Buffer)) {
+ esyslog("ERROR: Streamdev: Section handler buffer overflow (%d bytes lost)",
+ Length);
+ Reset();
+ return true;
+ }
+
+ memcpy(m_Buffer + m_Used, Data, Length);
+ m_Used += Length;
+ if (m_Used > 3) {
+ int length = (((m_Buffer[1] & 0x0F) << 8) | m_Buffer[2]) + 3;
+ if (m_Used == length) {
+ m_Used = 0;
+ if (write(m_Pipe[1], m_Buffer, length) < 0) {
+ if(errno == EAGAIN || errno == EWOULDBLOCK)
+ dsyslog("cStreamdevFilter::PutSection socket overflow, "
+ "Pid %4d Tid %3d", m_Pid, m_Tid);
+
+ else
+ return false;
+ }
+ }
+
+ if (m_Used > length) {
+ dsyslog("cStreamdevFilter::PutSection: m_Used > length ! Pid %2d, Tid%2d "
+ "(len %3d, got %d/%d)", m_Pid, m_Tid, Length, m_Used, length);
+ if(Length < TS_SIZE-5) {
+ // TS packet not full -> this must be last TS packet of section data -> safe to reset now
+ Reset();
+ }
+ }
+
+ }
+ return true;
+}
+
+void cStreamdevFilter::Reset(void) {
+ if(m_Used)
+ dsyslog("cStreamdevFilter::Reset skipping %d bytes", m_Used);
+ m_Used = 0;
+}
+
+bool cStreamdevFilter::IsClosed(void) {
+ char m_Buffer[3] = {0,0,0}; /* tid 0, 0 bytes */
+
+ // Test if pipe/socket has been closed by writing empty section
+ if (write(m_Pipe[1], m_Buffer, 3) < 0 &&
+ errno != EAGAIN &&
+ errno != EWOULDBLOCK) {
+
+ if (errno != ECONNREFUSED &&
+ errno != ECONNRESET &&
+ errno != EPIPE)
+ esyslog("cStreamdevFilter::IsClosed failed: %m");
+
+ return true;
+ }
+
+ return false;
+}
+
+// --- cStreamdevFilters -----------------------------------------------------
+
+cStreamdevFilters::cStreamdevFilters(void):
+ cThread("streamdev-client: sections assembler") {
+ m_TSBuffer = NULL;
+}
+
+cStreamdevFilters::~cStreamdevFilters() {
+ SetConnection(-1);
+}
+
+int cStreamdevFilters::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
+ CarbageCollect();
+
+ cStreamdevFilter *f = new cStreamdevFilter(Pid, Tid, Mask);
+ int fh = f->ReadPipe();
+
+ Lock();
+ Add(f);
+ Unlock();
+
+ return fh;
+}
+
+void cStreamdevFilters::CarbageCollect(void) {
+ LOCK_THREAD;
+ for (cStreamdevFilter *fi = First(); fi;) {
+ if (fi->IsClosed()) {
+ if (errno == ECONNREFUSED ||
+ errno == ECONNRESET ||
+ errno == EPIPE) {
+ ClientSocket.SetFilter(fi->Pid(), fi->Tid(), fi->Mask(), false);
+ Dprintf("cStreamdevFilters::CarbageCollector: filter closed: Pid %4d, Tid %3d, Mask %2x (%d filters left)",
+ (int)fi->Pid(), (int)fi->Tid(), fi->Mask(), Count()-1);
+
+ cStreamdevFilter *next = Prev(fi);
+ Del(fi);
+ fi = next ? Next(next) : First();
+ } else {
+ esyslog("cStreamdevFilters::CarbageCollector() error: "
+ "Pid %4d, Tid %3d, Mask %2x (%d filters left) failed",
+ (int)fi->Pid(), (int)fi->Tid(), fi->Mask(), Count()-1);
+ LOG_ERROR;
+ fi = Next(fi);
+ }
+ } else {
+ fi = Next(fi);
+ }
+ }
+}
+
+bool cStreamdevFilters::ReActivateFilters(void)
+{
+ LOCK_THREAD;
+
+ bool res = true;
+ CarbageCollect();
+ for (cStreamdevFilter *fi = First(); fi; fi = Next(fi)) {
+ res = ClientSocket.SetFilter(fi->Pid(), fi->Tid(), fi->Mask(), true) && res;
+ Dprintf("ReActivateFilters(%d, %d, %d) -> %s", fi->Pid(), fi->Tid(), fi->Mask(), res ? "Ok" :"FAIL");
+ }
+ return res;
+}
+
+void cStreamdevFilters::SetConnection(int Handle) {
+
+ Cancel(2);
+ DELETENULL(m_TSBuffer);
+
+ if (Handle >= 0) {
+ m_TSBuffer = new cTSBuffer(Handle, MEGABYTE(1), 1);
+ ReActivateFilters();
+ Start();
+ }
+}
+
+void cStreamdevFilters::Action(void) {
+ int fails = 0;
+
+ while (Running()) {
+ const uchar *block = m_TSBuffer->Get();
+ if (block) {
+ u_short pid = (((u_short)block[1] & PID_MASK_HI) << 8) | block[2];
+ u_char tid = block[3];
+ bool Pusi = block[1] & 0x40;
+ // proprietary extension
+ int len = block[4];
+#if 0
+ if (block[1] == 0xff &&
+ block[2] == 0xff &&
+ block[3] == 0xff &&
+ block[4] == 0x7f)
+ isyslog("*********** TRANSPONDER -> %s **********", block+5);
+#endif
+ LOCK_THREAD;
+ cStreamdevFilter *f = First();
+ while (f) {
+ cStreamdevFilter *next = Next(f);
+ if (f->Matches(pid, tid)) {
+
+ if (f->PutSection(block + 5, len, Pusi))
+ break;
+
+ if (errno != ECONNREFUSED &&
+ errno != ECONNRESET &&
+ errno != EPIPE) {
+ Dprintf("FATAL ERROR: %m\n");
+ esyslog("streamdev-client: couldn't send section packet: %m");
+ }
+ ClientSocket.SetFilter(f->Pid(), f->Tid(), f->Mask(), false);
+ Del(f);
+ // Filter was closed.
+ // - need to check remaining filters for another match
+ }
+ f = next;
+ }
+ } else {
+#if 1 // TODO: this should be fixed in vdr cTSBuffer
+ // Check disconnection
+ int fd = *ClientSocket.DataSocket(siLiveFilter);
+ if(fd < 0)
+ break;
+ cPoller Poller(fd);
+ if (Poller.Poll()) {
+ char tmp[1];
+ errno = 0;
+ Dprintf("cStreamdevFilters::Action(): checking connection");
+ if (recv(fd, tmp, 1, MSG_PEEK) == 0 && errno != EAGAIN) {
+ ++fails;
+ if (fails >= 10) {
+ esyslog("cStreamdevFilters::Action(): stream disconnected ?");
+ ClientSocket.CloseDataConnection(siLiveFilter);
+ break;
+ }
+ } else {
+ fails = 0;
+ }
+ } else {
+ fails = 0;
+ }
+ cCondWait::SleepMs(10);
+#endif
+ }
+ }
+
+ DELETENULL(m_TSBuffer);
+ dsyslog("StreamdevFilters::Action() ended");
+}
diff --git a/plugins/streamdev/streamdev-cvs/client/filter.h b/plugins/streamdev/streamdev-cvs/client/filter.h
new file mode 100644
index 0000000..889006a
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/client/filter.h
@@ -0,0 +1,33 @@
+/*
+ * $Id: filter.h,v 1.5 2008/04/07 14:27:28 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_FILTER_H
+#define VDR_STREAMDEV_FILTER_H
+
+#include <vdr/config.h>
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+
+class cTSBuffer;
+class cStreamdevFilter;
+
+class cStreamdevFilters: public cList<cStreamdevFilter>, public cThread {
+private:
+ cTSBuffer *m_TSBuffer;
+
+protected:
+ virtual void Action(void);
+ void CarbageCollect(void);
+
+ bool ReActivateFilters(void);
+
+public:
+ cStreamdevFilters(void);
+ virtual ~cStreamdevFilters();
+
+ void SetConnection(int Handle);
+ int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
+};
+
+#endif // VDR_STREAMDEV_FILTER_H
diff --git a/plugins/streamdev/streamdev-cvs/client/setup.c b/plugins/streamdev/streamdev-cvs/client/setup.c
new file mode 100644
index 0000000..2ac359f
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/client/setup.c
@@ -0,0 +1,80 @@
+/*
+ * $Id: setup.c,v 1.9 2009/09/18 10:43:26 schmirl Exp $
+ */
+
+#include <vdr/menuitems.h>
+
+#include "client/setup.h"
+#include "client/device.h"
+
+cStreamdevClientSetup StreamdevClientSetup;
+
+cStreamdevClientSetup::cStreamdevClientSetup(void) {
+ StartClient = false;
+ RemotePort = 2004;
+ StreamFilters = false;
+ SyncEPG = false;
+ HideMenuEntry = false;
+ MinPriority = -1;
+ MaxPriority = MAXPRIORITY;
+ strcpy(RemoteIp, "");
+}
+
+bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
+ if (strcmp(Name, "StartClient") == 0) StartClient = atoi(Value);
+ else if (strcmp(Name, "RemoteIp") == 0) {
+ if (strcmp(Value, "-none-") == 0)
+ strcpy(RemoteIp, "");
+ else
+ strcpy(RemoteIp, Value);
+ }
+ else if (strcmp(Name, "RemotePort") == 0) RemotePort = atoi(Value);
+ else if (strcmp(Name, "StreamFilters") == 0) StreamFilters = atoi(Value);
+ else if (strcmp(Name, "SyncEPG") == 0) SyncEPG = atoi(Value);
+ else if (strcmp(Name, "HideMenuEntry") == 0) HideMenuEntry = atoi(Value);
+ else if (strcmp(Name, "MinPriority") == 0) MinPriority = atoi(Value);
+ else if (strcmp(Name, "MaxPriority") == 0) MaxPriority = atoi(Value);
+ else return false;
+ return true;
+}
+
+cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(void) {
+ m_NewSetup = StreamdevClientSetup;
+
+ Add(new cMenuEditBoolItem(tr("Hide Mainmenu Entry"), &m_NewSetup.HideMenuEntry));
+ Add(new cMenuEditBoolItem(tr("Start Client"), &m_NewSetup.StartClient));
+ Add(new cMenuEditIpItem (tr("Remote IP"), m_NewSetup.RemoteIp));
+ Add(new cMenuEditIntItem (tr("Remote Port"), &m_NewSetup.RemotePort, 0, 65535));
+ Add(new cMenuEditBoolItem(tr("Filter Streaming"), &m_NewSetup.StreamFilters));
+ Add(new cMenuEditBoolItem(tr("Synchronize EPG"), &m_NewSetup.SyncEPG));
+ Add(new cMenuEditIntItem (tr("Minimum Priority"), &m_NewSetup.MinPriority, -1, MAXPRIORITY));
+ Add(new cMenuEditIntItem (tr("Maximum Priority"), &m_NewSetup.MaxPriority, -1, MAXPRIORITY));
+ SetCurrent(Get(0));
+}
+
+cStreamdevClientMenuSetupPage::~cStreamdevClientMenuSetupPage() {
+}
+
+void cStreamdevClientMenuSetupPage::Store(void) {
+ if (m_NewSetup.StartClient != StreamdevClientSetup.StartClient) {
+ if (m_NewSetup.StartClient)
+ cStreamdevDevice::Init();
+ }
+
+ SetupStore("StartClient", m_NewSetup.StartClient);
+ if (strcmp(m_NewSetup.RemoteIp, "") == 0)
+ SetupStore("RemoteIp", "-none-");
+ else
+ SetupStore("RemoteIp", m_NewSetup.RemoteIp);
+ SetupStore("RemotePort", m_NewSetup.RemotePort);
+ SetupStore("StreamFilters", m_NewSetup.StreamFilters);
+ SetupStore("SyncEPG", m_NewSetup.SyncEPG);
+ SetupStore("HideMenuEntry", m_NewSetup.HideMenuEntry);
+ SetupStore("MinPriority", m_NewSetup.MinPriority);
+ SetupStore("MaxPriority", m_NewSetup.MaxPriority);
+
+ StreamdevClientSetup = m_NewSetup;
+
+ cStreamdevDevice::ReInit();
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/client/setup.h b/plugins/streamdev/streamdev-cvs/client/setup.h
new file mode 100644
index 0000000..f7cba08
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/client/setup.h
@@ -0,0 +1,39 @@
+/*
+ * $Id: setup.h,v 1.6 2009/09/18 10:43:26 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SETUPCLIENT_H
+#define VDR_STREAMDEV_SETUPCLIENT_H
+
+#include "common.h"
+
+struct cStreamdevClientSetup {
+ cStreamdevClientSetup(void);
+
+ bool SetupParse(const char *Name, const char *Value);
+
+ int StartClient;
+ char RemoteIp[20];
+ int RemotePort;
+ int StreamFilters;
+ int SyncEPG;
+ int HideMenuEntry;
+ int MinPriority;
+ int MaxPriority;
+};
+
+extern cStreamdevClientSetup StreamdevClientSetup;
+
+class cStreamdevClientMenuSetupPage: public cMenuSetupPage {
+private:
+ cStreamdevClientSetup m_NewSetup;
+
+protected:
+ virtual void Store(void);
+
+public:
+ cStreamdevClientMenuSetupPage(void);
+ virtual ~cStreamdevClientMenuSetupPage();
+};
+
+#endif // VDR_STREAMDEV_SETUPCLIENT_H
diff --git a/plugins/streamdev/streamdev-cvs/client/socket.c b/plugins/streamdev/streamdev-cvs/client/socket.c
new file mode 100644
index 0000000..02f501d
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/client/socket.c
@@ -0,0 +1,374 @@
+/*
+ * $Id: socket.c,v 1.12 2008/04/08 14:18:16 schmirl Exp $
+ */
+
+#include <tools/select.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+
+#define MINLOGREPEAT 10 //don't log connect failures too often (seconds)
+
+#include "client/socket.h"
+#include "client/setup.h"
+#include "common.h"
+
+cClientSocket ClientSocket;
+
+cClientSocket::cClientSocket(void)
+{
+ memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
+ Reset();
+}
+
+cClientSocket::~cClientSocket()
+{
+ Reset();
+ if (IsOpen()) Quit();
+}
+
+void cClientSocket::Reset(void)
+{
+ for (int it = 0; it < si_Count; ++it) {
+ if (m_DataSockets[it] != NULL)
+ DELETENULL(m_DataSockets[it]);
+ }
+}
+
+cTBSocket *cClientSocket::DataSocket(eSocketId Id) const {
+ return m_DataSockets[Id];
+}
+
+bool cClientSocket::Command(const std::string &Command, uint Expected, uint TimeoutMs)
+{
+ errno = 0;
+
+ std::string pkt = Command + "\015\012";
+ Dprintf("OUT: |%s|\n", Command.c_str());
+
+ cTimeMs starttime;
+ if (!TimedWrite(pkt.c_str(), pkt.size(), TimeoutMs)) {
+ esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
+ strerror(errno));
+ Close();
+ return false;
+ }
+
+ uint64_t elapsed = starttime.Elapsed();
+ if (Expected != 0) { // XXX+ What if elapsed > TimeoutMs?
+ TimeoutMs -= elapsed;
+ return Expect(Expected, NULL, TimeoutMs);
+ }
+
+ return true;
+}
+
+bool cClientSocket::Expect(uint Expected, std::string *Result, uint TimeoutMs) {
+ char *endptr;
+ int bufcount;
+ bool res;
+
+ errno = 0;
+
+ if ((bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs)) == -1) {
+ esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
+ strerror(errno));
+ Close();
+ return false;
+ }
+ if (m_Buffer[bufcount - 1] == '\015')
+ --bufcount;
+ m_Buffer[bufcount] = '\0';
+ Dprintf("IN: |%s|\n", m_Buffer);
+
+ if (Result != NULL)
+ *Result = m_Buffer;
+
+ res = strtoul(m_Buffer, &endptr, 10) == Expected;
+ return res;
+}
+
+bool cClientSocket::CheckConnection(void) {
+ CMD_LOCK;
+
+ if (IsOpen()) {
+ cTBSelect select;
+
+ Dprintf("connection open\n");
+
+ // XXX+ check if connection is still alive (is there a better way?)
+ // There REALLY shouldn't be anything readable according to PROTOCOL here
+ // If there is, assume it's an eof signal (subseq. read would return 0)
+ select.Add(*this, false);
+ int res;
+ if ((res = select.Select(0)) == 0) {
+ Dprintf("select said nothing happened\n");
+ return true;
+ }
+ Dprintf("closing connection (res was %d)", res);
+ Close();
+ }
+
+ if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort)){
+ static time_t lastTime = 0;
+ if (time(NULL) - lastTime > MINLOGREPEAT) {
+ esyslog("ERROR: Streamdev: Couldn't connect to %s:%d: %s",
+ (const char*)StreamdevClientSetup.RemoteIp,
+ StreamdevClientSetup.RemotePort, strerror(errno));
+ lastTime = time(NULL);
+ }
+ return false;
+ }
+
+ if (!Expect(220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Didn't receive greeting from %s:%d",
+ RemoteIp().c_str(), RemotePort());
+ Close();
+ return false;
+ }
+
+ if (!Command("CAPS TSPIDS", 220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't negotiate capabilities on %s:%d",
+ RemoteIp().c_str(), RemotePort());
+ Close();
+ return false;
+ }
+
+ const char *Filters = "";
+ if(Command("CAPS FILTERS", 220))
+ Filters = ",FILTERS";
+
+ isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s",
+ RemoteIp().c_str(), RemotePort(), Filters);
+ return true;
+}
+
+bool cClientSocket::ProvidesChannel(const cChannel *Channel, int Priority) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ std::string command = (std::string)"PROV " + (const char*)itoa(Priority) + " "
+ + (const char*)Channel->GetChannelID().ToString();
+ if (!Command(command))
+ return false;
+
+ std::string buffer;
+ if (!Expect(220, &buffer)) {
+ if (buffer.substr(0, 3) != "560" && errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't check if %s:%d provides channel %s",
+ RemoteIp().c_str(), RemotePort(), Channel->Name());
+ return false;
+ }
+ return true;
+}
+
+bool cClientSocket::CreateDataConnection(eSocketId Id) {
+ cTBSocket listen(SOCK_STREAM);
+
+ if (!CheckConnection()) return false;
+
+ if (m_DataSockets[Id] != NULL)
+ DELETENULL(m_DataSockets[Id]);
+
+ if (!listen.Listen(LocalIp(), 0, 1)) {
+ esyslog("ERROR: Streamdev: Couldn't create data connection: %s",
+ strerror(errno));
+ return false;
+ }
+
+ std::string command = (std::string)"PORT " + (const char*)itoa(Id) + " "
+ + LocalIp().c_str() + ","
+ + (const char*)itoa((listen.LocalPort() >> 8) & 0xff) + ","
+ + (const char*)itoa(listen.LocalPort() & 0xff);
+ size_t idx = 4;
+ while ((idx = command.find('.', idx + 1)) != (size_t)-1)
+ command[idx] = ',';
+
+ CMD_LOCK;
+
+ if (!Command(command, 220)) {
+ Dprintf("error: %m\n");
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d",
+ RemoteIp().c_str(), RemotePort());
+ return false;
+ }
+
+ /* The server SHOULD do the following:
+ * - get PORT command
+ * - connect to socket
+ * - return 220
+ */
+
+ m_DataSockets[Id] = new cTBSocket;
+ if (!m_DataSockets[Id]->Accept(listen)) {
+ esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d%s%s",
+ RemoteIp().c_str(), RemotePort(), errno == 0 ? "" : ": ",
+ errno == 0 ? "" : strerror(errno));
+ DELETENULL(m_DataSockets[Id]);
+ return false;
+ }
+
+ return true;
+}
+
+bool cClientSocket::CloseDataConnection(eSocketId Id) {
+ //if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if(Id == siLive || Id == siLiveFilter)
+ if (m_DataSockets[Id] != NULL) {
+ std::string command = (std::string)"ABRT " + (const char*)itoa(Id);
+ if (!Command(command, 220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
+ //return false;
+ }
+ DELETENULL(m_DataSockets[Id]);
+ }
+ return true;
+}
+
+bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ std::string command = (std::string)"TUNE "
+ + (const char*)Channel->GetChannelID().ToString();
+ if (!Command(command, 220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s",
+ RemoteIp().c_str(), RemotePort(), Channel->Name());
+ return false;
+ }
+ return true;
+}
+
+bool cClientSocket::SetPid(int Pid, bool On) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid);
+ if (!Command(command, 220)) {
+ if (errno == 0)
+ esyslog("Streamdev: Pid %d not available from %s:%d", Pid, LocalIp().c_str(),
+ LocalPort());
+ return false;
+ }
+ return true;
+}
+
+bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ std::string command = (std::string)(On ? "ADDF " : "DELF ") + (const char*)itoa(Pid)
+ + " " + (const char*)itoa(Tid) + " " + (const char*)itoa(Mask);
+ if (!Command(command, 220)) {
+ if (errno == 0)
+ esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d",
+ Pid, Tid, Mask, LocalIp().c_str(), LocalPort());
+ return false;
+ }
+ return true;
+}
+
+bool cClientSocket::CloseDvr(void) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if (m_DataSockets[siLive] != NULL) {
+ std::string command = (std::string)"ABRT " + (const char*)itoa(siLive);
+ if (!Command(command, 220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
+ return false;
+ }
+
+ DELETENULL(m_DataSockets[siLive]);
+ }
+ return true;
+}
+
+bool cClientSocket::SynchronizeEPG(void) {
+ std::string buffer;
+ bool result;
+ FILE *epgfd;
+
+ if (!CheckConnection()) return false;
+
+ isyslog("Streamdev: Synchronizing EPG from server\n");
+
+ CMD_LOCK;
+
+ if (!Command("LSTE"))
+ return false;
+
+ if ((epgfd = tmpfile()) == NULL) {
+ esyslog("ERROR: Streamdev: Error while processing EPG data: %s",
+ strerror(errno));
+ return false;
+ }
+
+ while ((result = Expect(215, &buffer))) {
+ if (buffer[3] == ' ') break;
+ fputs(buffer.c_str() + 4, epgfd);
+ fputc('\n', epgfd);
+ }
+
+ if (!result) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't fetch EPG data from %s:%d",
+ RemoteIp().c_str(), RemotePort());
+ fclose(epgfd);
+ return false;
+ }
+
+ rewind(epgfd);
+ if (cSchedules::Read(epgfd))
+ cSchedules::Cleanup(true);
+ else {
+ esyslog("ERROR: Streamdev: Parsing EPG data failed");
+ fclose(epgfd);
+ return false;
+ }
+ fclose(epgfd);
+ return true;
+}
+
+bool cClientSocket::Quit(void) {
+ bool res;
+
+ if (!CheckConnection()) return false;
+
+ if (!(res = Command("QUIT", 221))) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't quit command connection to %s:%d",
+ RemoteIp().c_str(), RemotePort());
+ }
+ Close();
+ return res;
+}
+
+bool cClientSocket::SuspendServer(void) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ if (!Command("SUSP", 220)) {
+ if (errno == 0)
+ esyslog("ERROR: Streamdev: Couldn't suspend server");
+ return false;
+ }
+ return true;
+}
diff --git a/plugins/streamdev/streamdev-cvs/client/socket.h b/plugins/streamdev/streamdev-cvs/client/socket.h
new file mode 100644
index 0000000..a0400e6
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/client/socket.h
@@ -0,0 +1,60 @@
+/*
+ * $Id: socket.h,v 1.6 2008/04/07 14:40:40 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_CLIENT_CONNECTION_H
+#define VDR_STREAMDEV_CLIENT_CONNECTION_H
+
+#include <tools/socket.h>
+
+#include "common.h"
+
+#include <string>
+
+#define CMD_LOCK cMutexLock CmdLock((cMutex*)&m_Mutex)
+
+class cPES2TSRemux;
+
+class cClientSocket: public cTBSocket {
+private:
+ cTBSocket *m_DataSockets[si_Count];
+ cMutex m_Mutex;
+ char m_Buffer[BUFSIZ + 1]; // various uses
+
+protected:
+ /* Send Command, and return true if the command results in Expected.
+ Returns false on failure, setting errno appropriately if it has been
+ a system failure. If Expected is zero, returns immediately after
+ sending the command. */
+ bool Command(const std::string &Command, uint Expected = 0, uint TimeoutMs = 1500);
+
+ /* Fetch results from an ongoing Command called with Expected == 0. Returns
+ true if the response has the code Expected, returning an internal buffer
+ in the array pointer pointed to by Result. Returns false on failure,
+ setting errno appropriately if it has been a system failure. */
+ bool Expect(uint Expected, std::string *Result = NULL, uint TimeoutMs = 1500);
+
+public:
+ cClientSocket(void);
+ virtual ~cClientSocket();
+
+ void Reset(void);
+
+ bool CheckConnection(void);
+ bool ProvidesChannel(const cChannel *Channel, int Priority);
+ bool CreateDataConnection(eSocketId Id);
+ bool CloseDataConnection(eSocketId Id);
+ bool SetChannelDevice(const cChannel *Channel);
+ bool SetPid(int Pid, bool On);
+ bool SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On);
+ bool CloseDvr(void);
+ bool SynchronizeEPG(void);
+ bool SuspendServer(void);
+ bool Quit(void);
+
+ cTBSocket *DataSocket(eSocketId Id) const;
+};
+
+extern class cClientSocket ClientSocket;
+
+#endif // VDR_STREAMDEV_CLIENT_CONNECTION_H
diff --git a/plugins/streamdev/streamdev-cvs/common.c b/plugins/streamdev/streamdev-cvs/common.c
new file mode 100644
index 0000000..279e909
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/common.c
@@ -0,0 +1,152 @@
+/*
+ * $Id: common.c,v 1.11 2009/09/18 10:41:41 schmirl Exp $
+ */
+
+#include <vdr/channels.h>
+#include <iostream>
+
+#include "common.h"
+#include "tools/select.h"
+
+using namespace std;
+
+const char *VERSION = "0.5.0-pre";
+
+const char cMenuEditIpItem::IpCharacters[] = "0123456789.";
+
+cMenuEditIpItem::cMenuEditIpItem(const char *Name, char *Value):
+ cMenuEditItem(Name) {
+ value = Value;
+ curNum = -1;
+ pos = -1;
+ step = false;
+ Set();
+}
+
+cMenuEditIpItem::~cMenuEditIpItem() {
+}
+
+void cMenuEditIpItem::Set(void) {
+ char buf[1000];
+ if (pos >= 0) {
+ in_addr_t addr = inet_addr(value);
+ if ((int)addr == -1)
+ addr = 0;
+ int p = 0;
+ for (int i = 0; i < 4; ++i) {
+ p += snprintf(buf + p, sizeof(buf) - p, pos == i ? "[%d]" : "%d",
+ pos == i ? curNum : (addr >> (i * 8)) & 0xff);
+ if (i < 3)
+ buf[p++] = '.';
+ }
+ SetValue(buf);
+ } else
+ SetValue(value);
+}
+
+eOSState cMenuEditIpItem::ProcessKey(eKeys Key) {
+ in_addr addr;
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+
+ switch (Key) {
+ case kUp:
+ if (pos >= 0) {
+ if (curNum < 255) ++curNum;
+ } else
+ return cMenuEditItem::ProcessKey(Key);
+ break;
+
+ case kDown:
+ if (pos >= 0) {
+ if (curNum > 0) --curNum;
+ } else
+ return cMenuEditItem::ProcessKey(Key);
+ break;
+
+ case kOk:
+ if (pos >= 0) {
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ } else
+ return cMenuEditItem::ProcessKey(Key);
+ curNum = -1;
+ pos = -1;
+ break;
+
+ case kRight:
+ if (pos >= 0) {
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ }
+
+ if (pos == -1 || pos == 3)
+ pos = 0;
+ else
+ ++pos;
+
+ curNum = (addr.s_addr >> (pos * 8)) & 0xff;
+ step = true;
+ break;
+
+ case kLeft:
+ if (pos >= 0) {
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ }
+
+ if (pos <= 0)
+ pos = 3;
+ else
+ --pos;
+
+ curNum = (addr.s_addr >> (pos * 8)) & 0xff;
+ step = true;
+ break;
+
+ case k0 ... k9:
+ if (pos == -1)
+ pos = 0;
+
+ if (curNum == -1 || step) {
+ curNum = Key - k0;
+ step = false;
+ } else
+ curNum = curNum * 10 + (Key - k0);
+
+ if ((curNum * 10 > 255) || (curNum == 0)) {
+ in_addr addr;
+ addr.s_addr = inet_addr(value);
+ if ((int)addr.s_addr == -1)
+ addr.s_addr = 0;
+ addr.s_addr &= ~(0xff << (pos * 8));
+ addr.s_addr |= curNum << (pos * 8);
+ strcpy(value, inet_ntoa(addr));
+ if (++pos == 4)
+ pos = 0;
+ curNum = (addr.s_addr >> (pos * 8)) & 0xff;
+ step = true;
+ }
+ break;
+
+ default:
+ return cMenuEditItem::ProcessKey(Key);
+ }
+
+ Set();
+ return osContinue;
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/common.h b/plugins/streamdev/streamdev-cvs/common.h
new file mode 100644
index 0000000..ef6ef9e
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/common.h
@@ -0,0 +1,80 @@
+/*
+ * $Id: common.h,v 1.15 2009/09/18 10:41:41 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_COMMON_H
+#define VDR_STREAMDEV_COMMON_H
+
+/* FreeBSD has it's own version of isnumber(),
+ but VDR's version is incompatible */
+#ifdef __FreeBSD__
+#undef isnumber
+#endif
+
+#include <vdr/tools.h>
+#include <vdr/plugin.h>
+
+#include "tools/socket.h"
+
+#ifdef DEBUG
+# include <stdio.h>
+# define Dprintf(x...) fprintf(stderr, x)
+#else
+# define Dprintf(x...)
+#endif
+
+#define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder())
+
+#define MAXPARSEBUFFER KILOBYTE(16)
+
+/* Check if a channel is a radio station. */
+#define ISRADIO(x) ((x)->Vpid()==0||(x)->Vpid()==1||(x)->Vpid()==0x1fff)
+
+class cChannel;
+
+enum eStreamType {
+ stTS,
+ stPES,
+ stPS,
+ stES,
+ stExtern,
+ stTSPIDS,
+ st_Count
+};
+
+enum eSuspendMode {
+ smOffer,
+ smAlways,
+ smNever,
+ sm_Count
+};
+
+enum eSocketId {
+ siLive,
+ siReplay,
+ siLiveFilter,
+ siDataRespond,
+ si_Count
+};
+
+extern const char *VERSION;
+
+class cMenuEditIpItem: public cMenuEditItem {
+private:
+ static const char IpCharacters[];
+ char *value;
+ int curNum;
+ int pos;
+ bool step;
+
+protected:
+ virtual void Set(void);
+
+public:
+ cMenuEditIpItem(const char *Name, char *Value); // Value must be 16 bytes
+ ~cMenuEditIpItem();
+
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+#endif // VDR_STREAMDEV_COMMON_H
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/.cvsignore b/plugins/streamdev/streamdev-cvs/libdvbmpeg/.cvsignore
new file mode 100644
index 0000000..4671378
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Entries b/plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Entries
new file mode 100644
index 0000000..027b45f
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Entries
@@ -0,0 +1,11 @@
+/.cvsignore/1.1.1.1/Thu Dec 30 22:44:04 2004//
+/Makefile/1.3/Mon Apr 7 14:40:40 2008//
+/ctools.c/1.3/Wed Mar 12 09:36:27 2008//
+/ctools.h/1.1.1.1/Thu Dec 30 22:44:10 2004//
+/remux.c/1.2/Fri Sep 21 11:55:56 2007//
+/remux.h/1.1.1.1/Thu Dec 30 22:44:13 2004//
+/ringbuffy.c/1.1.1.1/Thu Dec 30 22:44:13 2004//
+/ringbuffy.h/1.1.1.1/Thu Dec 30 22:44:13 2004//
+/transform.c/1.1.1.1/Thu Dec 30 22:44:17 2004//
+/transform.h/1.2/Fri Jun 19 06:32:40 2009//
+D
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Repository b/plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Repository
new file mode 100644
index 0000000..7926483
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Repository
@@ -0,0 +1 @@
+streamdev/libdvbmpeg
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Root b/plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Root
new file mode 100644
index 0000000..2c7f6ce
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/CVS/Root
@@ -0,0 +1 @@
+:pserver:anoncvs@vdr-developer.org:/var/cvsroot
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/Makefile b/plugins/streamdev/streamdev-cvs/libdvbmpeg/Makefile
new file mode 100644
index 0000000..a586182
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/Makefile
@@ -0,0 +1,25 @@
+INCS = -I.
+CFLAGS = -g -Wall -O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -fPIC
+MFLAG = -M
+OBJS = ctools.o ringbuffy.o remux.o transform.o
+SRC = $(wildcard *.c)
+
+DESTDIR = /usr/local
+
+.PHONY: clean
+
+clean:
+ - rm -f *.o *~ *.a .depend
+
+libdvbmpegtools.a: $(OBJS)
+ ar -rcs libdvbmpegtools.a $(OBJS)
+
+%.o: %.c
+ $(CC) -c $(CFLAGS) $(INCS) $(DEFINES) $<
+
+.depend:
+ $(CXX) $(DEFINES) $(MFLAG) $(SRC) $(INCS)> .depend
+
+
+
+-include .depend
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/ctools.c b/plugins/streamdev/streamdev-cvs/libdvbmpeg/ctools.c
new file mode 100644
index 0000000..4766ea2
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/ctools.c
@@ -0,0 +1,2403 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ * 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
+ * of the License, 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
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de,
+ */
+
+#include "ctools.h"
+
+#define MAX_SEARCH 1024 * 1024
+
+
+/*
+
+ PES
+
+*/
+
+ssize_t save_read(int fd, void *buf, size_t count)
+{
+ ssize_t neof = 1;
+ size_t re = 0;
+
+ while(neof >= 0 && re < count){
+ neof = read(fd, buf+re, count - re);
+ if (neof > 0) re += neof;
+ else break;
+ }
+
+ if (neof < 0 && re == 0) return neof;
+ else return re;
+}
+
+void init_pes(pes_packet *p){
+ p->stream_id = 0;
+ p->llength[0] = 0;
+ p->llength[1] = 0;
+ p->length = 0;
+ p->flags1 = 0x80;
+ p->flags2 = 0;
+ p->pes_hlength = 0;
+ p->trick = 0;
+ p->add_cpy = 0;
+ p->priv_flags = 0;
+ p->pack_field_length = 0;
+ p->pack_header = (uint8_t *) NULL;
+ p->pck_sqnc_cntr = 0;
+ p->org_stuff_length = 0;
+ p->pes_ext_lngth = 0;
+ p->pes_ext = (uint8_t *) NULL;
+ p->pes_pckt_data = (uint8_t *) NULL;
+ p->padding = 0;
+ p->mpeg = 2; // DEFAULT MPEG2
+ p->mpeg1_pad = 0;
+ p->mpeg1_headr = NULL;
+ p->stuffing = 0;
+}
+
+void kill_pes(pes_packet *p){
+ if (p->pack_header)
+ free(p->pack_header);
+ if (p->pes_ext)
+ free(p->pes_ext);
+ if (p->pes_pckt_data)
+ free(p->pes_pckt_data);
+ if (p->mpeg1_headr)
+ free(p->mpeg1_headr);
+ init_pes(p);
+}
+
+void setlength_pes(pes_packet *p){
+ short *ll;
+ ll = (short *) p->llength;
+ p->length = ntohs(*ll);
+}
+
+static void setl_pes(pes_packet *p){
+ setlength_pes(p);
+ if (p->length)
+ p->pes_pckt_data = (uint8_t *)malloc(p->length);
+}
+
+void nlength_pes(pes_packet *p){
+ if (p->length <= 0xFFFF){
+ short *ll = (short *) p->llength;
+ short l = p->length;
+ *ll = htons(l);
+ } else {
+ p->llength[0] =0x00;
+ p->llength[1] =0x00;
+ }
+}
+
+static void nl_pes(pes_packet *p)
+{
+ nlength_pes(p);
+ p->pes_pckt_data = (uint8_t *) malloc(p->length);
+}
+
+void pts2pts(uint8_t *av_pts, uint8_t *pts)
+{
+
+ av_pts[0] = ((pts[0] & 0x06) << 5) |
+ ((pts[1] & 0xFC) >> 2);
+ av_pts[1] = ((pts[1] & 0x03) << 6) |
+ ((pts[2] & 0xFC) >> 2);
+ av_pts[2] = ((pts[2] & 0x02) << 6) |
+ ((pts[3] & 0xFE) >> 1);
+ av_pts[3] = ((pts[3] & 0x01) << 7) |
+ ((pts[4] & 0xFE) >> 1);
+
+}
+
+
+int cwrite_pes(uint8_t *buf, pes_packet *p, long length){
+ int count,i;
+ uint8_t dummy;
+ int more = 0;
+ uint8_t headr[3] = { 0x00, 0x00 , 0x01};
+
+ if (length < p->length+p->pes_hlength){
+ fprintf(stderr,"Wrong buffer size in cwrite_pes\n");
+ exit(1);
+ }
+
+
+ memcpy(buf,headr,3);
+ count = 3;
+ buf[count] = p->stream_id;
+ count++;
+
+ switch ( p->stream_id ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ buf[count] = p->llength[0];
+ count++;
+ buf[count] = p->llength[1];
+ count++;
+ memcpy(buf+count,p->pes_pckt_data,p->length);
+ count += p->length;
+ break;
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ buf[count] = p->llength[0];
+ count++;
+ buf[count] = p->llength[1];
+ count++;
+ more = 1;
+ break;
+ }
+
+
+ if ( more ) {
+ if ( p->mpeg == 2 ){
+ memcpy(buf+count,&p->flags1,1);
+ count++;
+ memcpy(buf+count,&p->flags2,1);
+ count++;
+ memcpy(buf+count,&p->pes_hlength,1);
+ count++;
+
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_ONLY){
+ memcpy(buf+count,p->pts,5);
+ count += 5;
+ } else
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_DTS){
+ memcpy(buf+count,p->pts,5);
+ count += 5;
+ memcpy(buf+count,p->dts,5);
+ count += 5;
+ }
+ if (p->flags2 & ESCR_FLAG){
+ memcpy(buf+count,p->escr,6);
+ count += 6;
+ }
+ if (p->flags2 & ES_RATE_FLAG){
+ memcpy(buf+count,p->es_rate,3);
+ count += 3;
+ }
+ if (p->flags2 & DSM_TRICK_FLAG){
+ memcpy(buf+count,&p->trick,1);
+ count++;
+ }
+ if (p->flags2 & ADD_CPY_FLAG){
+ memcpy(buf+count,&p->add_cpy,1);
+ count++;
+ }
+ if (p->flags2 & PES_CRC_FLAG){
+ memcpy(buf+count,p->prev_pes_crc,2);
+ count += 2;
+ }
+ if (p->flags2 & PES_EXT_FLAG){
+ memcpy(buf+count,&p->priv_flags,1);
+ count++;
+
+ if (p->priv_flags & PRIVATE_DATA){
+ memcpy(buf+count,p->pes_priv_data,16);
+ count += 16;
+ }
+ if (p->priv_flags & HEADER_FIELD){
+ memcpy(buf+count,&p->pack_field_length,
+ 1);
+ count++;
+ memcpy(buf+count,p->pack_header,
+ p->pack_field_length);
+ count += p->pack_field_length;
+
+ }
+
+ if ( p->priv_flags & PACK_SEQ_CTR){
+ memcpy(buf+count,&p->pck_sqnc_cntr,1);
+ count++;
+ memcpy(buf+count,&p->org_stuff_length,
+ 1);
+ count++;
+ }
+
+ if ( p->priv_flags & P_STD_BUFFER){
+ memcpy(buf+count,p->p_std,2);
+ count += 2;
+ }
+ if ( p->priv_flags & PES_EXT_FLAG2){
+ memcpy(buf+count,&p->pes_ext_lngth,1);
+ count++;
+ memcpy(buf+count,p->pes_ext,
+ p->pes_ext_lngth);
+ count += p->pes_ext_lngth;
+ }
+ }
+ dummy = 0xFF;
+ for (i=0;i<p->stuffing;i++) {
+ memcpy(buf+count,&dummy,1);
+ count++;
+ }
+ } else {
+ if (p->mpeg1_pad){
+ memcpy(buf+count,p->mpeg1_headr,p->mpeg1_pad);
+ count += p->mpeg1_pad;
+ }
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_ONLY){
+ memcpy(buf+count,p->pts,5);
+ count += 5;
+ }
+ else if ((p->flags2 & PTS_DTS_FLAGS) ==
+ PTS_DTS){
+ memcpy(buf+count,p->pts,5);
+ count += 5;
+ memcpy(buf+count,p->dts,5);
+ count += 5;
+ }
+ }
+ memcpy(buf+count,p->pes_pckt_data,p->length);
+ count += p->length;
+ }
+
+ return count;
+
+}
+
+void write_pes(int fd, pes_packet *p){
+ long length;
+ uint8_t *buf;
+ int l = p->length+p->pes_hlength;
+
+ buf = (uint8_t *) malloc(l);
+ length = cwrite_pes(buf,p,l);
+ write(fd,buf,length);
+ free(buf);
+}
+
+static unsigned int find_length(int f){
+ uint64_t p = 0;
+ uint64_t start = 0;
+ uint64_t q = 0;
+ int found = 0;
+ uint8_t sync4[4];
+ int neof = 1;
+
+ start = lseek(f,0,SEEK_CUR);
+ start -=2;
+ lseek(f,start,SEEK_SET);
+ while ( neof > 0 && !found ){
+ p = lseek(f,0,SEEK_CUR);
+ neof = save_read(f,&sync4,4);
+ if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
+ switch ( sync4[3] ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ found = 1;
+ break;
+ default:
+ q = lseek(f,0,SEEK_CUR);
+ break;
+ }
+ }
+ }
+ q = lseek(f,0,SEEK_CUR);
+ lseek(f,start+2,SEEK_SET);
+ if (found) return (unsigned int)(q-start)-4-2;
+ else return (unsigned int)(q-start-2);
+
+}
+
+
+void cread_pes(char *buf, pes_packet *p){
+
+ uint8_t count, dummy, check;
+ int i;
+ uint64_t po = 0;
+ int c=0;
+
+ switch ( p->stream_id ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ memcpy(p->pes_pckt_data,buf+c,p->length);
+ return;
+ break;
+ case PADDING_STREAM :
+ p->padding = p->length;
+ memcpy(p->pes_pckt_data,buf+c,p->length);
+ return;
+ break;
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ break;
+ default:
+ return;
+ break;
+ }
+
+ po = c;
+ memcpy(&p->flags1,buf+c,1);
+ c++;
+ if ( (p->flags1 & 0xC0) == 0x80 ) p->mpeg = 2;
+ else p->mpeg = 1;
+
+ if ( p->mpeg == 2 ){
+ memcpy(&p->flags2,buf+c,1);
+ c++;
+ memcpy(&p->pes_hlength,buf+c,1);
+ c++;
+
+ p->length -=p->pes_hlength+3;
+ count = p->pes_hlength;
+
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_ONLY){
+ memcpy(p->pts,buf+c,5);
+ c += 5;
+ count -=5;
+ } else
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_DTS){
+ memcpy(p->pts,buf+c,5);
+ c += 5;
+ memcpy(p->dts,buf+c,5);
+ c += 5;
+ count -= 10;
+ }
+
+ if (p->flags2 & ESCR_FLAG){
+ memcpy(p->escr,buf+c,6);
+ c += 6;
+ count -= 6;
+ }
+
+ if (p->flags2 & ES_RATE_FLAG){
+ memcpy(p->es_rate,buf+c,3);
+ c += 3;
+ count -= 3;
+ }
+
+ if (p->flags2 & DSM_TRICK_FLAG){
+ memcpy(&p->trick,buf+c,1);
+ c += 1;
+ count -= 1;
+ }
+
+ if (p->flags2 & ADD_CPY_FLAG){
+ memcpy(&p->add_cpy,buf+c,1);
+ c++;
+ count -= 1;
+ }
+
+ if (p->flags2 & PES_CRC_FLAG){
+ memcpy(p->prev_pes_crc,buf+c,2);
+ c += 2;
+ count -= 2;
+ }
+
+ if (p->flags2 & PES_EXT_FLAG){
+ memcpy(&p->priv_flags,buf+c,1);
+ c++;
+ count -= 1;
+
+ if (p->priv_flags & PRIVATE_DATA){
+ memcpy(p->pes_priv_data,buf+c,16);
+ c += 16;
+ count -= 16;
+ }
+
+ if (p->priv_flags & HEADER_FIELD){
+ memcpy(&p->pack_field_length,buf+c,1);
+ c++;
+ p->pack_header = (uint8_t *)
+ malloc(p->pack_field_length);
+ memcpy(p->pack_header,buf+c,
+ p->pack_field_length);
+ c += p->pack_field_length;
+ count -= 1+p->pack_field_length;
+ }
+
+ if ( p->priv_flags & PACK_SEQ_CTR){
+ memcpy(&p->pck_sqnc_cntr,buf+c,1);
+ c++;
+ memcpy(&p->org_stuff_length,buf+c,1);
+ c++;
+ count -= 2;
+ }
+
+ if ( p->priv_flags & P_STD_BUFFER){
+ memcpy(p->p_std,buf+c,2);
+ c += 2;
+ count -= 2;
+ }
+
+ if ( p->priv_flags & PES_EXT_FLAG2){
+ memcpy(&p->pes_ext_lngth,buf+c,1);
+ c++;
+ p->pes_ext = (uint8_t *)
+ malloc(p->pes_ext_lngth);
+ memcpy(p->pes_ext,buf+c,
+ p->pes_ext_lngth);
+ c += p->pes_ext_lngth;
+ count -= 1+p->pes_ext_lngth;
+ }
+ }
+ p->stuffing = count;
+ for(i = 0; i< count ;i++){
+ memcpy(&dummy,buf+c,1);
+ c++;
+ }
+ } else {
+ p->mpeg1_pad = 1;
+ check = p->flags1;
+ while (check == 0xFF){
+ memcpy(&check,buf+c,1);
+ c++;
+ p->mpeg1_pad++;
+ }
+
+ if ( (check & 0xC0) == 0x40){
+ memcpy(&check,buf+c,1);
+ c++;
+ p->mpeg1_pad++;
+ memcpy(&check,buf+c,1);
+ c++;
+ p->mpeg1_pad++;
+ }
+ p->flags2 = 0;
+ p->length -= p->mpeg1_pad;
+
+ c = po;
+ if ( (check & 0x30)){
+ p->length ++;
+ p->mpeg1_pad --;
+
+ if (check == p->flags1){
+ p->pes_hlength = 0;
+ } else {
+ p->mpeg1_headr = (uint8_t *)
+ malloc(p->mpeg1_pad);
+ p->pes_hlength = p->mpeg1_pad;
+ memcpy(p->mpeg1_headr,buf+c,
+ p->mpeg1_pad);
+ c += p->mpeg1_pad;
+ }
+
+ p->flags2 = (check & 0xF0) << 2;
+ if ((p->flags2 & PTS_DTS_FLAGS) == PTS_ONLY){
+ memcpy(p->pts,buf+c,5);
+ c += 5;
+ p->length -= 5;
+ p->pes_hlength += 5;
+ }
+ else if ((p->flags2 & PTS_DTS_FLAGS) ==
+ PTS_DTS){
+ memcpy(p->pts,buf+c,5);
+ c += 5;
+ memcpy(p->dts,buf+c,5);
+ c += 5;
+ p->length -= 10;
+ p->pes_hlength += 10;
+ }
+ } else {
+ p->mpeg1_headr = (uint8_t *) malloc(p->mpeg1_pad);
+ p->pes_hlength = p->mpeg1_pad;
+ memcpy(p->mpeg1_headr,buf+c,
+ p->mpeg1_pad);
+ c += p->mpeg1_pad;
+ }
+ }
+ memcpy(p->pes_pckt_data,buf+c,p->length);
+}
+
+
+int read_pes(int f, pes_packet *p){
+
+ uint8_t sync4[4];
+ int found=0;
+ uint64_t po = 0;
+ int neof = 1;
+ uint8_t *buf;
+
+ while (neof > 0 && !found) {
+ po = lseek(f,0,SEEK_CUR);
+ if (po < 0) return -1;
+ if ((neof = save_read(f,&sync4,4)) < 4) return -1;
+ if (sync4[0] == 0x00 && sync4[1] == 0x00 && sync4[2] == 0x01) {
+ p->stream_id = sync4[3];
+ switch ( sync4[3] ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ if((neof = save_read(f,p->llength,2)) < 2)
+ return -1;
+ setl_pes(p);
+ if (!p->length){
+ p->length = find_length(f);
+ nl_pes(p);
+ }
+ found = 1;
+ break;
+
+ default:
+ if (lseek(f,po+1,SEEK_SET) < po+1) return -1;
+ break;
+ }
+ } else if(lseek(f,po+1,SEEK_SET) < po+1) return -1;
+ }
+
+ if (!found || !p->length) return 0;
+
+ if (p->length >0){
+ buf = (uint8_t *) malloc(p->length);
+ if((neof = save_read(f,buf,p->length))< p->length) return -1;
+ cread_pes((char *)buf,p);
+ free(buf);
+ } else return 0;
+
+ return neof;
+}
+
+/*
+
+ Transport Stream
+
+*/
+
+void init_ts(ts_packet *p){
+ p->pid[0] = 0;
+ p->pid[1] = 0;
+ p->flags = 0;
+ p->count = 0;
+ p->adapt_length = 0;
+ p->adapt_flags = 0;
+ p->splice_count = 0;
+ p->priv_dat_len = 0;
+ p->priv_dat = NULL;
+ p->adapt_ext_len = 0;
+ p->adapt_eflags = 0;
+ p->rest = 0;
+ p->stuffing = 0;
+}
+
+void kill_ts(ts_packet *p){
+ if (p->priv_dat)
+ free(p->priv_dat);
+ init_ts(p);
+}
+
+
+
+unsigned short pid_ts(ts_packet *p)
+{
+ return get_pid(p->pid);
+}
+
+int cwrite_ts(uint8_t *buf, ts_packet *p, long length){
+ long count,i;
+ uint8_t sync,dummy;
+
+ sync = 0x47;
+ memcpy(buf,&sync,1);
+ count = 1;
+ memcpy(buf+count,p->pid,2);
+ count += 2;
+ memcpy(buf+count,&p->flags,1);
+ count++;
+
+
+ if (! (p->flags & ADAPT_FIELD) && (p->flags & PAYLOAD)){
+ memcpy(buf+count,p->data,184);
+ count += 184;
+ } else {
+ memcpy(buf+count,&p->adapt_length,1);
+ count++;
+ memcpy(buf+count,&p->adapt_flags,1);
+ count++;
+
+ if ( p->adapt_flags & PCR_FLAG ){
+ memcpy(buf+count, p->pcr,6);
+ count += 6;
+ }
+ if ( p->adapt_flags & OPCR_FLAG ){
+ memcpy(buf+count, p->opcr,6);
+ count += 6;
+ }
+ if ( p->adapt_flags & SPLICE_FLAG ){
+ memcpy(buf+count, &p->splice_count,1);
+ count++;
+ }
+ if( p->adapt_flags & TRANS_PRIV){
+ memcpy(buf+count,&p->priv_dat_len,1);
+ count++;
+ memcpy(buf+count,p->priv_dat,p->priv_dat_len);
+ count += p->priv_dat_len;
+ }
+
+ if( p->adapt_flags & ADAP_EXT_FLAG){
+ memcpy(buf+count,&p->adapt_ext_len,1);
+ count++;
+ memcpy(buf+count,&p->adapt_eflags,1);
+ count++;
+
+ if( p->adapt_eflags & LTW_FLAG){
+ memcpy(buf+count,p->ltw,2);
+ count += 2;
+ }
+ if( p->adapt_eflags & PIECE_RATE){
+ memcpy(buf+count,p->piece_rate,3);
+ count += 3;
+ }
+ if( p->adapt_eflags & SEAM_SPLICE){
+ memcpy(buf+count,p->dts,5);
+ count += 5;
+ }
+ }
+ dummy = 0xFF;
+ for(i=0; i < p->stuffing ; i++){
+ memcpy(buf+count,&dummy,1);
+ count++;
+ }
+ if (p->flags & PAYLOAD){
+ memcpy(buf+count,p->data,p->rest);
+ count += p->rest;
+ }
+ }
+
+
+ return count;
+}
+
+void write_ts(int fd, ts_packet *p){
+ long length;
+ uint8_t buf[TS_SIZE];
+
+ length = cwrite_ts(buf,p,TS_SIZE);
+ write(fd,buf,length);
+}
+
+int read_ts (int f, ts_packet *p){
+ uint8_t sync;
+ int found=0;
+ uint64_t po,q;
+ int neof = 1;
+
+ sync=0;
+ while (neof > 0 && !found) {
+ neof = save_read(f,&sync,1);
+ if (sync == 0x47)
+ found = 1;
+ }
+ neof = save_read(f,p->pid,2);
+ neof = save_read(f,&p->flags,1);
+ p->count = p->flags & COUNT_MASK;
+
+ if (!(p->flags & ADAPT_FIELD) && (p->flags & PAYLOAD)){
+ //no adapt. field only payload
+ neof = save_read(f,p->data,184);
+ p->rest = 184;
+ return neof;
+ }
+
+ if ( p->flags & ADAPT_FIELD ) {
+ // adaption field
+ neof = save_read(f,&p->adapt_length,1);
+ po = lseek(f,0,SEEK_CUR);
+ neof = save_read(f,&p->adapt_flags,1);
+
+ if ( p->adapt_flags & PCR_FLAG )
+ neof = save_read(f, p->pcr,6);
+
+ if ( p->adapt_flags & OPCR_FLAG )
+ neof = save_read(f, p->opcr,6);
+
+ if ( p->adapt_flags & SPLICE_FLAG )
+ neof = save_read(f, &p->splice_count,1);
+
+ if( p->adapt_flags & TRANS_PRIV){
+ neof = save_read(f,&p->priv_dat_len,1);
+ p->priv_dat = (uint8_t *) malloc(p->priv_dat_len);
+ neof = save_read(f,p->priv_dat,p->priv_dat_len);
+ }
+
+ if( p->adapt_flags & ADAP_EXT_FLAG){
+ neof = save_read(f,&p->adapt_ext_len,1);
+ neof = save_read(f,&p->adapt_eflags,1);
+ if( p->adapt_eflags & LTW_FLAG)
+ neof = save_read(f,p->ltw,2);
+
+ if( p->adapt_eflags & PIECE_RATE)
+ neof = save_read(f,p->piece_rate,3);
+
+ if( p->adapt_eflags & SEAM_SPLICE)
+ neof = save_read(f,p->dts,5);
+ }
+ q = lseek(f,0,SEEK_CUR);
+ p->stuffing = p->adapt_length -(q-po);
+ p->rest = 183-p->adapt_length;
+ lseek(f,q+p->stuffing,SEEK_SET);
+ if (p->flags & PAYLOAD) // payload
+ neof = save_read(f,p->data,p->rest);
+ else
+ lseek(f,q+p->rest,SEEK_SET);
+ }
+ return neof;
+}
+
+void cread_ts (char *buf, ts_packet *p, long length){
+ uint8_t sync;
+ int found=0;
+ uint64_t po,q;
+ long count=0;
+
+ sync=0;
+ while (count < length && !found) {
+ sync=buf[count];
+ count++;
+ if (sync == 0x47)
+ found = 1;
+ }
+ memcpy(p->pid,buf+count,2);
+ count += 2;
+ p->flags = buf[count];
+ count++;
+ p->count = p->flags & COUNT_MASK;
+
+ if (!(p->flags & ADAPT_FIELD) && (p->flags & PAYLOAD)){
+ //no adapt. field only payload
+ memcpy(p->data,buf+count,184);
+ p->rest = 184;
+ return;
+ }
+
+ if ( p->flags & ADAPT_FIELD ) {
+ // adaption field
+ p->adapt_length = buf[count];
+ count++;
+ po = count;
+ memcpy(&p->adapt_flags,buf+count,1);
+ count++;
+
+ if ( p->adapt_flags & PCR_FLAG ){
+ memcpy( p->pcr,buf+count,6);
+ count += 6;
+ }
+ if ( p->adapt_flags & OPCR_FLAG ){
+ memcpy( p->opcr,buf+count,6);
+ count += 6;
+ }
+ if ( p->adapt_flags & SPLICE_FLAG ){
+ memcpy( &p->splice_count,buf+count,1);
+ count++;
+ }
+ if( p->adapt_flags & TRANS_PRIV){
+ memcpy(&p->priv_dat_len,buf+count,1);
+ count++;
+ p->priv_dat = (uint8_t *) malloc(p->priv_dat_len);
+ memcpy(p->priv_dat,buf+count,p->priv_dat_len);
+ count += p->priv_dat_len;
+ }
+
+ if( p->adapt_flags & ADAP_EXT_FLAG){
+ memcpy(&p->adapt_ext_len,buf+count,1);
+ count++;
+ memcpy(&p->adapt_eflags,buf+count,1);
+ count++;
+ if( p->adapt_eflags & LTW_FLAG){
+ memcpy(p->ltw,buf+count,2);
+ count += 2;
+ }
+ if( p->adapt_eflags & PIECE_RATE){
+ memcpy(p->piece_rate,buf+count,3);
+ count += 3;
+ }
+ if( p->adapt_eflags & SEAM_SPLICE){
+ memcpy(p->dts,buf+count,5);
+ count += 5;
+ }
+ }
+ q = count;
+ p->stuffing = p->adapt_length -(q-po);
+ p->rest = 183-p->adapt_length;
+ count = q+p->stuffing;
+ if (p->flags & PAYLOAD){ // payload
+ memcpy(p->data,buf+count,p->rest);
+ count += p->rest;
+ } else
+ count = q+p->rest;
+ }
+}
+
+
+/*
+
+ Program Stream
+
+*/
+
+
+void init_ps(ps_packet *p)
+{
+ p->stuff_length=0xF8;
+ p->data = NULL;
+ p->sheader_length = 0;
+ p->audio_bound = 0;
+ p->video_bound = 0;
+ p->npes = 0;
+ p->mpeg = 2;
+}
+
+void kill_ps(ps_packet *p)
+{
+ if (p->data)
+ free(p->data);
+ init_ps(p);
+}
+
+void setlength_ps(ps_packet *p)
+{
+ short *ll;
+ ll = (short *) p->sheader_llength;
+ if (p->mpeg == 2)
+ p->sheader_length = ntohs(*ll) - 6;
+ else
+ p->sheader_length = ntohs(*ll);
+}
+
+static void setl_ps(ps_packet *p)
+{
+ setlength_ps(p);
+ p->data = (uint8_t *) malloc(p->sheader_length);
+}
+
+int mux_ps(ps_packet *p)
+{
+ uint32_t mux = 0;
+ uint8_t *i = (uint8_t *)&mux;
+
+ i[1] = p->mux_rate[0];
+ i[2] = p->mux_rate[1];
+ i[3] = p->mux_rate[2];
+ mux = ntohl(mux);
+ mux = (mux >>2);
+ return mux;
+}
+
+int rate_ps(ps_packet *p)
+{
+ uint32_t rate=0;
+ uint8_t *i= (uint8_t *) &rate;
+
+ i[1] = p->rate_bound[0] & 0x7F;
+ i[2] = p->rate_bound[1];
+ i[3] = p->rate_bound[2];
+
+ rate = ntohl(rate);
+ rate = (rate >> 1);
+ return rate;
+}
+
+
+uint32_t scr_base_ps(ps_packet *p) // only 32 bit!!
+{
+ uint32_t base = 0;
+ uint8_t *buf = (uint8_t *)&base;
+
+ buf[0] |= (long int)((p->scr[0] & 0x18) << 3);
+ buf[0] |= (long int)((p->scr[0] & 0x03) << 4);
+ buf[0] |= (long int)((p->scr[1] & 0xF0) >> 4);
+
+ buf[1] |= (long int)((p->scr[1] & 0x0F) << 4);
+ buf[1] |= (long int)((p->scr[2] & 0xF0) >> 4);
+
+ buf[2] |= (long int)((p->scr[2] & 0x08) << 4);
+ buf[2] |= (long int)((p->scr[2] & 0x03) << 5);
+ buf[2] |= (long int)((p->scr[3] & 0xF8) >> 3);
+
+ buf[3] |= (long int)((p->scr[3] & 0x07) << 5);
+ buf[3] |= (long int)((p->scr[4] & 0xF8) >> 3);
+
+ base = ntohl(base);
+ return base;
+}
+
+uint16_t scr_ext_ps(ps_packet *p)
+{
+ short ext = 0;
+
+ ext = (short)(p->scr[5] >> 1);
+ ext += (short) (p->scr[4] & 0x03) * 128;
+
+ return ext;
+}
+
+int cwrite_ps(uint8_t *buf, ps_packet *p, long length)
+{
+ long count,i;
+ uint8_t headr1[4] = {0x00, 0x00, 0x01, 0xBA };
+ uint8_t headr2[4] = {0x00, 0x00, 0x01, 0xBB };
+ uint8_t buffy = 0xFF;
+
+
+ memcpy(buf,headr1,4);
+ count = 4;
+ if (p->mpeg == 2){
+ memcpy(buf+count,p->scr,6);
+ count += 6;
+ memcpy(buf+count,p->mux_rate,3);
+ count += 3;
+ memcpy(buf+count,&p->stuff_length,1);
+ count++;
+ for(i=0; i< (p->stuff_length & 3); i++){
+ memcpy(buf+count,&buffy,1);
+ count++;
+ }
+ } else {
+ memcpy(buf+count,p->scr,5);
+ count += 5;
+ memcpy(buf+count,p->mux_rate,3);
+ count += 3;
+ }
+ if (p->sheader_length){
+ memcpy(buf+count,headr2,4);
+ count += 4;
+ memcpy(buf+count,p->sheader_llength,2);
+ count += 2;
+ if ( p->mpeg == 2){
+ memcpy(buf+count,p->rate_bound,3);
+ count += 3;
+ memcpy(buf+count,&p->audio_bound,1);
+ count++;
+ memcpy(buf+count,&p->video_bound,1);
+ count++;
+ memcpy(buf+count,&p->reserved,1);
+ count++;
+ }
+ memcpy(buf+count,p->data,p->sheader_length);
+ count += p->sheader_length;
+ }
+
+ return count;
+}
+
+void write_ps(int fd, ps_packet *p){
+ long length;
+ uint8_t buf[PS_MAX];
+
+ length = cwrite_ps(buf,p,PS_MAX);
+ write(fd,buf,length);
+}
+
+int read_ps (int f, ps_packet *p){
+ uint8_t headr[4];
+ pes_packet pes;
+ int i,done;
+ int found=0;
+ uint64_t po = 0;
+ uint64_t q = 0;
+ long count = 0;
+ int neof = 1;
+
+ po = lseek(f,0,SEEK_CUR);
+ while (neof > 0 && !found && count < MAX_SEARCH) {
+ neof = save_read(f,&headr,4);
+ if (headr[0] == 0x00 && headr[1] == 0x00 && headr[2] == 0x01){
+ if ( headr[3] == 0xBA )
+ found = 1;
+ else
+ if ( headr[3] == 0xB9 ) break;
+ else lseek(f,po+1,SEEK_SET);
+ }
+ count++;
+ }
+
+ if (found){
+ neof = save_read(f,p->scr,6);
+ if (p->scr[0] & 0x40)
+ p->mpeg = 2;
+ else
+ p->mpeg = 1;
+
+ if (p->mpeg == 2){
+ neof = save_read(f,p->mux_rate,3);
+ neof = save_read(f,&p->stuff_length,1);
+ po = lseek(f,0,SEEK_CUR);
+ lseek(f,po+(p->stuff_length & 3),SEEK_SET);
+ } else {
+ p->mux_rate[0] = p->scr[5]; //mpeg1 scr is only 5 bytes
+ neof = save_read(f,p->mux_rate+1,2);
+ }
+
+ po = lseek(f,0,SEEK_CUR);
+ neof = save_read(f,headr,4);
+ if (headr[0] == 0x00 && headr[1] == 0x00 &&
+ headr[2] == 0x01 && headr[3] == 0xBB ) {
+ neof = save_read(f,p->sheader_llength,2);
+ setl_ps(p);
+ if (p->mpeg == 2){
+ neof = save_read(f,p->rate_bound,3);
+ neof = save_read(f,&p->audio_bound,1);
+ neof = save_read(f,&p->video_bound,1);
+ neof = save_read(f,&p->reserved,1);
+ }
+ neof = save_read(f,p->data,p->sheader_length);
+ } else {
+ lseek(f,po,SEEK_SET);
+ p->sheader_length = 0;
+ }
+
+ i = 0;
+ done = 0;
+ q = lseek(f,0,SEEK_CUR);
+ do {
+ po = lseek(f,0,SEEK_CUR);
+ neof = save_read(f,headr,4);
+ lseek(f,po,SEEK_SET);
+ if ( headr[0] == 0x00 && headr[1] == 0x00
+ && headr[2] == 0x01 && headr[3] != 0xBA){
+ init_pes(&pes);
+ neof = read_pes(f,&pes);
+ i++;
+ } else done = 1;
+ kill_pes(&pes);
+ } while ( neof > 0 && !done);
+ p->npes = i;
+ lseek(f,q,SEEK_SET);
+ }
+ return neof;
+}
+
+void cread_ps (char *buf, ps_packet *p, long length){
+ uint8_t *headr;
+ pes_packet pes;
+ int i,done;
+ int found=0;
+ uint64_t po = 0;
+ uint64_t q = 0;
+ long count = 0;
+ long c = 0;
+
+ po = c;
+ while ( count < length && !found && count < MAX_SEARCH) {
+ headr = (uint8_t *)buf+c;
+ c += 4;
+ if (headr[0] == 0x00 && headr[1] == 0x00 && headr[2] == 0x01){
+ if ( headr[3] == 0xBA )
+ found = 1;
+ else
+ if ( headr[3] == 0xB9 ) break;
+ else c = po+1;
+ }
+ count++;
+ }
+
+ if (found){
+ memcpy(p->scr,buf+c,6);
+ c += 6;
+ if (p->scr[0] & 0x40)
+ p->mpeg = 2;
+ else
+ p->mpeg = 1;
+
+ if (p->mpeg == 2){
+ memcpy(p->mux_rate,buf+c,3);
+ c += 3;
+ memcpy(&p->stuff_length,buf+c,1);
+ c++;
+ po = c;
+ c = po+(p->stuff_length & 3);
+ } else {
+ p->mux_rate[0] = p->scr[5]; //mpeg1 scr is only 5 bytes
+ memcpy(p->mux_rate+1,buf+c,2);
+ c += 2;
+ }
+
+ po = c;
+ headr = (uint8_t *)buf+c;
+ c += 4;
+ if (headr[0] == 0x00 && headr[1] == 0x00 &&
+ headr[2] == 0x01 && headr[3] == 0xBB ) {
+ memcpy(p->sheader_llength,buf+c,2);
+ c += 2;
+ setl_ps(p);
+ if (p->mpeg == 2){
+ memcpy(p->rate_bound,buf+c,3);
+ c += 3;
+ memcpy(&p->audio_bound,buf+c,1);
+ c++;
+ memcpy(&p->video_bound,buf+c,1);
+ c++;
+ memcpy(&p->reserved,buf+c,1);
+ c++;
+ }
+ memcpy(p->data,buf+c,p->sheader_length);
+ c += p->sheader_length;
+ } else {
+ c = po;
+ p->sheader_length = 0;
+ }
+
+ i = 0;
+ done = 0;
+ q = c;
+ do {
+ headr = (uint8_t *)buf+c;
+ if ( headr[0] == 0x00 && headr[1] == 0x00
+ && headr[2] == 0x01 && headr[3] != 0xBA){
+ init_pes(&pes);
+ // cread_pes(buf+c,&pes);
+ i++;
+ } else done = 1;
+ kill_pes(&pes);
+ } while (c < length && !done);
+ p->npes = i;
+ c = q;
+ }
+}
+
+
+
+
+
+
+
+/*
+ conversion
+*/
+
+void init_trans(trans *p)
+{
+ int i;
+
+ p->found = 0;
+ p->pes = 0;
+ p->is_full = 0;
+ p->pes_start = 0;
+ p->pes_started = 0;
+ p->set = 0;
+
+ for (i = 0; i < MASKL*MAXFILT ; i++){
+ p->mask[i] = 0;
+ p->filt[i] = 0;
+ }
+ for (i = 0; i < MAXFILT ; i++){
+ p->sec[i].found = 0;
+ p->sec[i].length = 0;
+ }
+}
+
+int set_trans_filt(trans *p, int filtn, uint16_t pid, uint8_t *mask, uint8_t *filt, int pes)
+{
+ int i;
+ int off;
+
+ if ( filtn > MAXFILT-1 || filtn<0 ) return -1;
+ p->pid[filtn] = pid;
+ if (pes) p->pes |= (tflags)(1 << filtn);
+ else {
+ off = MASKL*filtn;
+ p->pes &= ~((tflags) (1 << filtn) );
+ for (i = 0; i < MASKL ; i++){
+ p->mask[off+i] = mask[i];
+ p->filt[off+i] = filt[i];
+ }
+ }
+ p->set |= (tflags) (1 << filtn);
+ return 0;
+}
+
+void clear_trans_filt(trans *p,int filtn)
+{
+ int i;
+
+ p->set &= ~((tflags) (1 << filtn) );
+ p->pes &= ~((tflags) (1 << filtn) );
+ p->is_full &= ~((tflags) (1 << filtn) );
+ p->pes_start &= ~((tflags) (1 << filtn) );
+ p->pes_started &= ~((tflags) (1 << filtn) );
+
+ for (i = MASKL*filtn; i < MASKL*(filtn+1) ; i++){
+ p->mask[i] = 0;
+ p->filt[i] = 0;
+ }
+ p->sec[filtn].found = 0;
+ p->sec[filtn].length = 0;
+}
+
+int filt_is_set(trans *p, int filtn)
+{
+ if (p->set & ((tflags)(1 << filtn))) return 1;
+ return 0;
+}
+
+int pes_is_set(trans *p, int filtn)
+{
+ if (p->pes & ((tflags)(1 << filtn))) return 1;
+ return 0;
+}
+
+int pes_is_started(trans *p, int filtn)
+{
+ if (p->pes_started & ((tflags)(1 << filtn))) return 1;
+ return 0;
+}
+
+int pes_is_start(trans *p, int filtn)
+{
+ if (p->pes_start & ((tflags)(1 << filtn))) return 1;
+ return 0;
+}
+
+int filt_is_ready(trans *p,int filtn)
+{
+ if (p->is_full & ((tflags)(1 << filtn))) return 1;
+ return 0;
+}
+
+void trans_filt(uint8_t *buf, int count, trans *p)
+{
+ int c=0;
+ //fprintf(stderr,"trans_filt\n");
+
+
+ while (c < count && p->found <1 ){
+ if ( buf[c] == 0x47) p->found = 1;
+ c++;
+ p->packet[0] = 0x47;
+ }
+ if (c == count) return;
+
+ while( c < count && p->found < 188 && p->found > 0 ){
+ p->packet[p->found] = buf[c];
+ c++;
+ p->found++;
+ }
+ if (p->found == 188){
+ p->found = 0;
+ tfilter(p);
+ }
+
+ if (c < count) trans_filt(buf+c,count-c,p);
+}
+
+
+void tfilter(trans *p)
+{
+ int l,c;
+ int tpid;
+ uint8_t flag,flags;
+ uint8_t adapt_length = 0;
+ uint8_t cpid[2];
+
+
+ // fprintf(stderr,"tfilter\n");
+
+ cpid[0] = p->packet[1];
+ cpid[1] = p->packet[2];
+ tpid = get_pid(cpid);
+
+ if ( p->packet[1]&0x80){
+ fprintf(stderr,"Error in TS for PID: %d\n",
+ tpid);
+ }
+
+ flag = cpid[0];
+ flags = p->packet[3];
+
+ if ( flags & ADAPT_FIELD ) {
+ // adaption field
+ adapt_length = p->packet[4];
+ }
+
+ c = 5 + adapt_length - (int)(!(flags & ADAPT_FIELD));
+ if (flags & PAYLOAD){
+ for ( l = 0; l < MAXFILT ; l++){
+ if ( filt_is_set(p,l) ) {
+ if ( p->pid[l] == tpid) {
+ if ( pes_is_set(p,l) ){
+ if (cpid[0] & PAY_START){
+ p->pes_started |=
+ (tflags)
+ (1 << l);
+ p->pes_start |=
+ (tflags)
+ (1 << l);
+ } else {
+ p->pes_start &= ~
+ ((tflags)
+ (1 << l));
+ }
+ pes_filter(p,l,c);
+ } else {
+ sec_filter(p,l,c);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void pes_filter(trans *p, int filtn, int off)
+{
+ int count,c;
+ uint8_t *buf;
+
+ if (filtn < 0 || filtn >= MAXFILT) return;
+
+ count = 188 - off;
+ c = 188*filtn;
+ buf = p->packet+off;
+ if (pes_is_started(p,filtn)){
+ p->is_full |= (tflags) (1 << filtn);
+ memcpy(p->transbuf+c,buf,count);
+ p->transcount[filtn] = count;
+ }
+}
+
+section *get_filt_sec(trans *p, int filtn)
+{
+ section *sec;
+
+ sec = &p->sec[filtn];
+ p->is_full &= ~((tflags) (1 << filtn) );
+ return sec;
+}
+
+int get_filt_buf(trans *p, int filtn,uint8_t **buf)
+{
+ *buf = p->transbuf+188*filtn;
+ p->is_full &= ~((tflags) (1 << filtn) );
+ return p->transcount[filtn];
+}
+
+
+
+
+void sec_filter(trans *p, int filtn, int off)
+{
+ int i,j;
+ int error;
+ int count,c;
+ uint8_t *buf, *secbuf;
+ section *sec;
+
+ // fprintf(stderr,"sec_filter\n");
+
+ if (filtn < 0 || filtn >= MAXFILT) return;
+
+ count = 188 - off;
+ c = 0;
+ buf = p->packet+off;
+ sec = &p->sec[filtn];
+ secbuf = sec->payload;
+ if(!filt_is_ready(p,filtn)){
+ p->is_full &= ~((tflags) (1 << filtn) );
+ sec->found = 0;
+ sec->length = 0;
+ }
+
+ if ( !sec->found ){
+ c = buf[c]+1;
+ if (c >= count) return;
+ sec->id = buf[c];
+ secbuf[0] = buf[c];
+ c++;
+ sec->found++;
+ sec->length = 0;
+ }
+
+ while ( c < count && sec->found < 3){
+ secbuf[sec->found] = buf[c];
+ c++;
+ sec->found++;
+ }
+ if (c == count) return;
+
+ if (!sec->length && sec->found == 3){
+ sec->length |= ((secbuf[1] & 0x0F) << 8);
+ sec->length |= (secbuf[2] & 0xFF);
+ }
+
+ while ( c < count && sec->found < sec->length+3){
+ secbuf[sec->found] = buf[c];
+ c++;
+ sec->found++;
+ }
+
+ if ( sec->length && sec->found == sec->length+3 ){
+ error=0;
+ for ( i = 0; i < MASKL; i++){
+ if (i > 0 ) j=2+i;
+ else j = 0;
+ error += (sec->payload[j]&p->mask[MASKL*filtn+i])^
+ (p->filt[MASKL*filtn+i]&
+ p->mask[MASKL*filtn+i]);
+ }
+ if (!error){
+ p->is_full |= (tflags) (1 << filtn);
+ }
+ if (buf[0]+1 < c ) c=count;
+ }
+
+ if ( c < count ) sec_filter(p, filtn, off);
+
+}
+
+#define MULT 1024
+
+
+void write_ps_headr( ps_packet *p, uint8_t *pts,int fd)
+{
+ long muxr = 37500;
+ uint8_t audio_bound = 1;
+ uint8_t fixed = 0;
+ uint8_t CSPS = 0;
+ uint8_t audio_lock = 1;
+ uint8_t video_lock = 1;
+ uint8_t video_bound = 1;
+ uint8_t stream1 = 0XC0;
+ uint8_t buffer1_scale = 1;
+ uint32_t buffer1_size = 32;
+ uint8_t stream2 = 0xE0;
+ uint8_t buffer2_scale = 1;
+ uint32_t buffer2_size = 230;
+
+ init_ps(p);
+
+ p->mpeg = 2;
+// SCR = 0
+ p->scr[0] = 0x44;
+ p->scr[1] = 0x00;
+ p->scr[2] = 0x04;
+ p->scr[3] = 0x00;
+ p->scr[4] = 0x04;
+ p->scr[5] = 0x01;
+
+// SCR = PTS
+ p->scr[0] = 0x44 | ((pts[0] >> 3)&0x18) | ((pts[0] >> 4)&0x03);
+ p->scr[1] = 0x00 | ((pts[0] << 4)&0xF0) | ((pts[1] >> 4)&0x0F);
+ p->scr[2] = 0x04 | ((pts[1] << 4)&0xF0) | ((pts[2] >> 4)&0x08)
+ | ((pts[2] >> 5)&0x03);
+ p->scr[3] = 0x00 | ((pts[2] << 3)&0xF8) | ((pts[3] >> 5)&0x07);
+ p->scr[4] = 0x04 | ((pts[3] << 3)&0xF8);
+ p->scr[5] = 0x01;
+
+ p->mux_rate[0] = (uint8_t)(muxr >> 14);
+ p->mux_rate[1] = (uint8_t)(0xff & (muxr >> 6));
+ p->mux_rate[2] = (uint8_t)(0x03 | ((muxr & 0x3f) << 2));
+
+ p->stuff_length = 0xF8;
+
+ p->sheader_llength[0] = 0x00;
+ p->sheader_llength[1] = 0x0c;
+
+ setl_ps(p);
+
+ p->rate_bound[0] = (uint8_t)(0x80 | (muxr >>15));
+ p->rate_bound[1] = (uint8_t)(0xff & (muxr >> 7));
+ p->rate_bound[2] = (uint8_t)(0x01 | ((muxr & 0x7f)<<1));
+
+
+ p->audio_bound = (uint8_t)((audio_bound << 2)|(fixed << 1)|CSPS);
+ p->video_bound = (uint8_t)((audio_lock << 7)|
+ (video_lock << 6)|0x20|video_bound);
+ p->reserved = (uint8_t)(0xFF);
+
+ p->data[0] = stream2;
+ p->data[1] = (uint8_t) (0xc0 | (buffer2_scale << 5) |
+ (buffer2_size >> 8));
+ p->data[2] = (uint8_t) (buffer2_size & 0xff);
+ p->data[3] = stream1;
+ p->data[4] = (uint8_t) (0xc0 | (buffer1_scale << 5) |
+ (buffer1_size >> 8));
+ p->data[5] = (uint8_t) (buffer1_size & 0xff);
+
+ write_ps(fd, p);
+ kill_ps(p);
+}
+
+
+
+void twrite(uint8_t const *buf)
+{
+ int l = TS_SIZE;
+ int c = 0;
+ int w;
+
+
+ while (l){
+ w = write(STDOUT_FILENO,buf+c,l);
+ if (w>=0){
+ l-=w;
+ c+=w;
+ }
+ }
+}
+
+void init_p2t(p2t_t *p, void (*fkt)(uint8_t const *buf))
+{
+ memset(p->pes,0,TS_SIZE);
+ p->counter = 0;
+ p->pos = 0;
+ p->frags = 0;
+ if (fkt) p->t_out = fkt;
+ else p->t_out = twrite;
+}
+
+void clear_p2t(p2t_t *p)
+{
+ memset(p->pes,0,TS_SIZE);
+ p->counter = 0;
+ p->pos = 0;
+ p->frags = 0;
+}
+
+
+long int find_pes_header(uint8_t const *buf, long int length, int *frags)
+{
+ int c = 0;
+ int found = 0;
+
+ *frags = 0;
+
+ while (c < length-3 && !found) {
+ if (buf[c] == 0x00 && buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01) {
+ switch ( buf[c+3] ) {
+ case 0xBA:
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ found = 1;
+ break;
+
+ default:
+ c++;
+ break;
+ }
+ } else c++;
+ }
+ if (c == length-3 && !found){
+ if (buf[length-1] == 0x00) *frags = 1;
+ if (buf[length-2] == 0x00 &&
+ buf[length-1] == 0x00) *frags = 2;
+ if (buf[length-3] == 0x00 &&
+ buf[length-2] == 0x00 &&
+ buf[length-1] == 0x01) *frags = 3;
+ return -1;
+ }
+
+ return c;
+}
+
+void pes_to_ts( uint8_t const *buf, long int length, uint16_t pid, p2t_t *p)
+{
+ int c,c2,l,add;
+ int check,rest;
+
+ c = 0;
+ c2 = 0;
+ if (p->frags){
+ check = 0;
+ switch(p->frags){
+ case 1:
+ if ( buf[c] == 0x00 && buf[c+1] == 0x01 ){
+ check = 1;
+ c += 2;
+ }
+ break;
+ case 2:
+ if ( buf[c] == 0x01 ){
+ check = 1;
+ c++;
+ }
+ break;
+ case 3:
+ check = 1;
+ }
+ if(check){
+ switch ( buf[c] ) {
+
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ p->pes[0] = 0x00;
+ p->pes[1] = 0x00;
+ p->pes[2] = 0x01;
+ p->pes[3] = buf[c];
+ p->pos=4;
+ memcpy(p->pes+p->pos,buf+c,TS_SIZE-4-p->pos);
+ c += TS_SIZE-4-p->pos;
+ p_to_t(p->pes,TS_SIZE-4,pid,&p->counter,
+ p->t_out);
+ clear_p2t(p);
+ break;
+
+ default:
+ c=0;
+ break;
+ }
+ }
+ p->frags = 0;
+ }
+
+ if (p->pos){
+ c2 = find_pes_header(buf+c,length-c,&p->frags);
+ if (c2 >= 0 && c2 < TS_SIZE-4-p->pos){
+ l = c2+c;
+ } else l = TS_SIZE-4-p->pos;
+ memcpy(p->pes+p->pos,buf,l);
+ c += l;
+ p->pos += l;
+ p_to_t(p->pes,p->pos,pid,&p->counter,
+ p->t_out);
+ clear_p2t(p);
+ }
+
+ add = 0;
+ while (c < length){
+ c2 = find_pes_header(buf+c+add,length-c-add,&p->frags);
+ if (c2 >= 0) {
+ c2 += c+add;
+ if (c2 > c){
+ p_to_t(buf+c,c2-c,pid,&p->counter,
+ p->t_out);
+ c = c2;
+ clear_p2t(p);
+ add = 0;
+ } else add = 1;
+ } else {
+ l = length-c;
+ rest = l % (TS_SIZE-4);
+ l -= rest;
+ p_to_t(buf+c,l,pid,&p->counter,
+ p->t_out);
+ memcpy(p->pes,buf+c+l,rest);
+ p->pos = rest;
+ c = length;
+ }
+ }
+}
+
+
+
+void p_to_t( uint8_t const *buf, long int length, uint16_t pid, uint8_t *counter,
+ void (*ts_write)(uint8_t const *))
+{
+
+ int l, pes_start;
+ uint8_t obuf[TS_SIZE];
+ long int c = 0;
+ pes_start = 0;
+ if ( length > 3 &&
+ buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01 )
+ switch (buf[3]){
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ pes_start = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ while ( c < length ){
+ memset(obuf,0,TS_SIZE);
+ if (length - c >= TS_SIZE-4){
+ l = write_ts_header(pid, counter, pes_start
+ , obuf, TS_SIZE-4);
+ memcpy(obuf+l, buf+c, TS_SIZE-l);
+ c += TS_SIZE-l;
+ } else {
+ l = write_ts_header(pid, counter, pes_start
+ , obuf, length-c);
+ memcpy(obuf+l, buf+c, TS_SIZE-l);
+ c = length;
+ }
+ ts_write(obuf);
+ pes_start = 0;
+ }
+}
+
+
+int write_ps_header(uint8_t *buf,
+ uint32_t SCR,
+ long muxr,
+ uint8_t audio_bound,
+ uint8_t fixed,
+ uint8_t CSPS,
+ uint8_t audio_lock,
+ uint8_t video_lock,
+ uint8_t video_bound,
+ uint8_t stream1,
+ uint8_t buffer1_scale,
+ uint32_t buffer1_size,
+ uint8_t stream2,
+ uint8_t buffer2_scale,
+ uint32_t buffer2_size)
+{
+ ps_packet p;
+ uint8_t *pts;
+ long lpts;
+ init_ps(&p);
+
+ lpts = htonl(SCR);
+ pts = (uint8_t *) &lpts;
+
+
+ p.mpeg = 2;
+// SCR = 0
+ p.scr[0] = 0x44;
+ p.scr[1] = 0x00;
+ p.scr[2] = 0x04;
+ p.scr[3] = 0x00;
+ p.scr[4] = 0x04;
+ p.scr[5] = 0x01;
+
+// SCR = PTS
+ p.scr[0] = 0x44 | ((pts[0] >> 3)&0x18) | ((pts[0] >> 4)&0x03);
+ p.scr[1] = 0x00 | ((pts[0] << 4)&0xF0) | ((pts[1] >> 4)&0x0F);
+ p.scr[2] = 0x04 | ((pts[1] << 4)&0xF0) | ((pts[2] >> 4)&0x08)
+ | ((pts[2] >> 5)&0x03);
+ p.scr[3] = 0x00 | ((pts[2] << 3)&0xF8) | ((pts[3] >> 5)&0x07);
+ p.scr[4] = 0x04 | ((pts[3] << 3)&0xF8);
+ p.scr[5] = 0x01;
+
+ p.mux_rate[0] = (uint8_t)(muxr >> 14);
+ p.mux_rate[1] = (uint8_t)(0xff & (muxr >> 6));
+ p.mux_rate[2] = (uint8_t)(0x03 | ((muxr & 0x3f) << 2));
+
+ p.stuff_length = 0xF8;
+
+ if (stream1 && stream2){
+ p.sheader_llength[0] = 0x00;
+ p.sheader_llength[1] = 0x0c;
+
+ setl_ps(&p);
+
+ p.rate_bound[0] = (uint8_t)(0x80 | (muxr >>15));
+ p.rate_bound[1] = (uint8_t)(0xff & (muxr >> 7));
+ p.rate_bound[2] = (uint8_t)(0x01 | ((muxr & 0x7f)<<1));
+
+
+ p.audio_bound = (uint8_t)((audio_bound << 2)|(fixed << 1)|CSPS);
+ p.video_bound = (uint8_t)((audio_lock << 7)|
+ (video_lock << 6)|0x20|video_bound);
+ p.reserved = (uint8_t)(0xFF >> 1);
+
+ p.data[0] = stream2;
+ p.data[1] = (uint8_t) (0xc0 | (buffer2_scale << 5) |
+ (buffer2_size >> 8));
+ p.data[2] = (uint8_t) (buffer2_size & 0xff);
+ p.data[3] = stream1;
+ p.data[4] = (uint8_t) (0xc0 | (buffer1_scale << 5) |
+ (buffer1_size >> 8));
+ p.data[5] = (uint8_t) (buffer1_size & 0xff);
+
+ cwrite_ps(buf, &p, PS_HEADER_L2);
+ kill_ps(&p);
+ return PS_HEADER_L2;
+ } else {
+ cwrite_ps(buf, &p, PS_HEADER_L1);
+ kill_ps(&p);
+ return PS_HEADER_L1;
+ }
+}
+
+
+
+#define MAX_BASE 80
+#define MAX_PATH 256
+#define MAX_EXT 10
+
+int break_up_filename(char *name, char *base_name, char *path, char *ext)
+{
+ int l,i,sstop,sstart;
+
+ l = strlen(name);
+ sstop = l;
+ sstart = -1;
+ for( i= l-1; i >= 0; i--){
+ if (sstop == l && name[i] == '.') sstop = i;
+ if (sstart<0 && name[i] == '/') sstart = i+1;
+ }
+ if (sstart < 0) sstart = 0;
+ if (sstop-sstart < MAX_BASE){
+ strncpy(base_name, name+sstart, sstop-sstart);
+ base_name[sstop-sstart]=0;
+ if(sstart > 0){
+ if( l - sstop + sstart < MAX_PATH){
+ strncpy(path, name, sstart);
+ path[sstart] = 0;
+ } else {
+ fprintf(stderr,"PATH too long\n");
+ return -1;
+ }
+
+ } else {
+ strcpy(path, "./");
+ }
+
+ if(sstop < l){
+ if( l - sstop -1 < MAX_EXT){
+ strncpy(ext, name+sstop+1, l-sstop-1);
+ ext[l-sstop-1]=0;
+ } else {
+ fprintf(stderr,"Extension too long\n");
+ return -1;
+ }
+
+ } else {
+ strcpy(ext, "");
+ }
+
+ } else {
+ fprintf(stderr,"Name too long\n");
+ return -1;
+ }
+/*
+ printf("%d %d\n",sstart, sstop);
+ printf("%s %d\n",name, strlen(name));
+ printf("%s %d\n",base_name, strlen(base_name));
+ printf("%s %d\n",path,strlen(path));
+ printf("%s %d\n",ext,strlen(ext));
+*/
+ return 0;
+}
+
+
+int seek_mpg_start(uint8_t *buf, int size)
+{
+ int found = 0;
+ int c=0;
+ int seq = 0;
+ int mpeg = 0;
+ int mark = 0;
+
+ while ( !seq ){
+ while (found != 4){
+ switch (found) {
+ case 0:
+ if ( buf[c] == 0x00 ) found++;
+ c++;
+ break;
+ case 1:
+ if ( buf[c] == 0x00 ) found++;
+ else found = 0;
+ c++;
+ break;
+ case 2:
+ if ( buf[c] == 0x01 ) found++;
+ else found = 0;
+ if ( buf[c] == 0x00 ) found = 2;
+ c++;
+ break;
+
+ case 3:
+ if ( (buf[c] & 0xe0) == 0xe0 ) found++;
+ else found = 0;
+ c++;
+ break;
+ }
+ if (c >= size) return -1;
+ }
+
+ if (found == 4){
+ mark = c-4;
+ c+=2;
+ if (c >= size) return -1;
+
+ if ( (buf[c] & 0xC0) == 0x80 ){
+ mpeg = 2;
+ c += 2;
+ if (c >= size) return -1;
+ c += buf[c]+1;
+ if (c >= size) return -1;
+ } else {
+ mpeg = 1;
+ while( buf[c] == 0xFF ) {
+ c++;
+ if (c >= size) return -1;
+ }
+ if ( (buf[c] & 0xC0) == 0x40) c+=2;
+ if (c >= size) return -1;
+ if ( (buf[c] & 0x30) ){
+ if ( (buf[c] & 0x30) == 0x20) c+=5;
+ else c+=10;
+ } else c++;
+ if (c >= size) return -1;
+ }
+
+ if ( buf[c] == 0x00 &&
+ buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01 &&
+ buf[c+3] == 0xB3 )
+ seq = 1;
+ }
+ found = 0;
+ }
+
+ return size-mark;
+}
+
+
+void write_mpg(int fstart, uint64_t length, int fdin, int fdout)
+{
+// uint8_t mpeg_end[4] = { 0x00, 0x00, 0x01, 0xB9 };
+ uint8_t *buf;
+ uint64_t l=0;
+ uint64_t count = 0;
+ struct stat sb;
+ int buf_size;
+
+ fstat (fdout, &sb);
+ buf_size = sb.st_blksize;
+
+ buf = (uint8_t *) alloca (buf_size + sizeof (int));
+
+ lseek(fdin, fstart, SEEK_SET);
+
+ while ( count < length && (l = read(fdin,buf,buf_size)) >= 0){
+ if (l > 0) count+=l;
+ write(fdout,buf,l);
+ printf("written %02.2f%%\r",(100.*count)/length);
+ }
+ printf("\n");
+
+ //write( fdout, mpeg_end, 4);
+}
+
+
+#define CHECKBUF (1024*1024)
+#define ONE_GIG (1024UL*1024UL*1024UL)
+void split_mpg(char *name, uint64_t size)
+{
+ char base_name[MAX_BASE];
+ char path[MAX_PATH];
+ char ext[MAX_EXT];
+ char new_name[256];
+ uint8_t buf[CHECKBUF];
+ int fdin;
+ int fdout;
+ uint64_t length = 0;
+ uint64_t last;
+ int i;
+ int mark, csize;
+ struct stat sb;
+
+ if (break_up_filename(name,base_name,path,ext) < 0) exit(1);
+
+
+#ifdef __FreeBSD__
+ if ( (fdin = open(name, O_RDONLY)) < 0){
+#else
+ if ( (fdin = open(name, O_RDONLY|O_LARGEFILE)) < 0){
+#endif
+ fprintf(stderr,"Can't open %s\n",name);
+ exit(1);
+ }
+
+ fstat (fdin, &sb);
+
+ length = sb.st_size;
+ if ( length < ONE_GIG )
+ printf("Filelength = %2.2f MB\n", length/1024./1024.);
+ else
+ printf("Filelength = %2.2f GB\n", length/1024./1024./1024.);
+
+ if ( length < size ) length = size;
+
+ printf("Splitting %s into Files with size <= %2.2f MB\n",name,
+ size/1024./1024.);
+
+ csize = CHECKBUF;
+ read(fdin, buf, csize);
+ if ( (mark = seek_mpg_start(buf,csize)) < 0){
+ fprintf(stderr,"Couldn't find sequence header\n");
+ exit(1);
+ }
+
+ last = csize-mark;
+
+ for ( i = 0 ; i < length/size; i++){
+ csize = CHECKBUF;
+
+ if (csize > length-last) csize = length-last;
+ lseek(fdin, last+size-csize, SEEK_SET);
+ read(fdin, buf, csize);
+ if ( (mark = seek_mpg_start(buf,csize)) < 0){
+ fprintf(stderr,"Couldn't find sequence header\n");
+ exit(1);
+ }
+
+ sprintf(new_name,"%s-%03d.%s",base_name,i,ext);
+ printf("writing %s\n",new_name);
+
+#ifdef __FreeBSD__
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
+#else
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
+ |O_LARGEFILE,
+#endif
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH)) < 0){
+ fprintf(stderr,"Can't open %s\n",new_name);
+ exit(1);
+ }
+ write_mpg(last, size-mark, fdin, fdout);
+ last = last + size - mark;
+ }
+ sprintf(new_name,"%s-%03d.%s",base_name,i,ext);
+ printf("writing %s\n",new_name);
+
+#ifdef __FreeBSD__
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
+#else
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
+ |O_LARGEFILE,
+#endif
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH)) < 0){
+ fprintf(stderr,"Can't open %s\n",new_name);
+ exit(1);
+ }
+ write_mpg(last, length-last, fdin, fdout);
+}
+
+
+
+
+void cut_mpg(char *name, uint64_t size)
+{
+ char base_name[MAX_BASE];
+ char path[MAX_PATH];
+ char ext[MAX_EXT];
+ char new_name[256];
+ uint8_t buf[CHECKBUF];
+ int fdin;
+ int fdout;
+ uint64_t length = 0;
+ uint64_t last;
+ int mark, csize;
+ struct stat sb;
+
+ if (break_up_filename(name,base_name,path,ext) < 0) exit(1);
+
+
+#ifdef __FreeBSD__
+ if ( (fdin = open(name, O_RDONLY)) < 0){
+#else
+ if ( (fdin = open(name, O_RDONLY|O_LARGEFILE)) < 0){
+#endif
+ fprintf(stderr,"Can't open %s\n",name);
+ exit(1);
+ }
+
+ fstat (fdin, &sb);
+
+ length = sb.st_size;
+ if ( length < ONE_GIG )
+ printf("Filelength = %2.2f MB\n", length/1024./1024.);
+ else
+ printf("Filelength = %2.2f GB\n", length/1024./1024./1024.);
+
+ if ( length < size ) length = size;
+
+ printf("Splitting %s into 2 Files with length %.2f MB and %.2f MB\n",
+ name, size/1024./1024., (length-size)/1024./1024.);
+
+ csize = CHECKBUF;
+ read(fdin, buf, csize);
+ if ( (mark = seek_mpg_start(buf,csize)) < 0){
+ fprintf(stderr,"Couldn't find sequence header\n");
+ exit(1);
+ }
+
+ last = csize-mark;
+
+ if (csize > length-last) csize = length-last;
+ lseek(fdin, last+size-csize, SEEK_SET);
+ read(fdin, buf, csize);
+ if ( (mark = seek_mpg_start(buf,csize)) < 0){
+ fprintf(stderr,"Couldn't find sequence header\n");
+ exit(1);
+ }
+
+ sprintf(new_name,"%s-1.%s",base_name,ext);
+ printf("writing %s\n",new_name);
+
+#ifdef __FreeBSD__
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
+#else
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
+ |O_LARGEFILE,
+#endif
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH)) < 0){
+ fprintf(stderr,"Can't open %s\n",new_name);
+ exit(1);
+ }
+ write_mpg(last, size-mark, fdin, fdout);
+ last = last + size - mark;
+
+ sprintf(new_name,"%s-2.%s",base_name,ext);
+ printf("writing %s\n",new_name);
+
+#ifdef __FreeBSD__
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC,
+#else
+ if ( (fdout = open(new_name,O_WRONLY|O_CREAT|O_TRUNC
+ |O_LARGEFILE,
+#endif
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH)) < 0){
+ fprintf(stderr,"Can't open %s\n",new_name);
+ exit(1);
+ }
+ write_mpg(last, length-last, fdin, fdout);
+}
+
+
+
+
+void write_all (int fd, const char *data, int length)
+{
+ int r;
+
+ while (length) {
+ if ((r = write(fd, data, length)) > 0) {
+ data += r;
+ length -= r;
+ }
+ }
+}
+
+
+
+void read_all (int fd, char *data, int length)
+{
+ int c = 0;
+
+ while(1) {
+ if( read(fd, data+c, 1) == 1) {
+ c++;
+ if(data[c-1] == '\n') {
+ data[c] = 0;
+ break;
+ }
+ }
+ else {
+ fprintf (stderr, "Error reading socket\n");
+ exit(1);
+ }
+ }
+}
+
+
+
+char *url2host (char *url, char **name, uint32_t *ip, uint32_t *port)
+{
+ char *murl;
+ struct hostent *hoste;
+ struct in_addr haddr;
+ int found_ip = 1;
+
+ if (!(strncmp(url, "http://", 7)))
+ url += 7;
+
+ *name = strdup(url);
+ if (!(*name)) {
+ *name = NULL;
+ return (NULL);
+ }
+
+ murl = url;
+ while (*murl && *murl != ':' && *murl != '/') {
+ if ((*murl < '0' || *murl > '9') && *murl != '.')
+ found_ip = 0;
+ murl++;
+ }
+
+ (*name)[murl - url] = 0;
+ if (found_ip) {
+ if ((*ip = inet_addr(*name)) == INADDR_NONE)
+ return (NULL);
+ } else {
+ if (!(hoste = gethostbyname(*name)))
+ return (NULL);
+ memcpy (&haddr, hoste->h_addr, sizeof(haddr));
+ *ip = haddr.s_addr;
+ }
+
+ if (!*murl || *murl == '/') {
+ *port = 80;
+ return (murl);
+ }
+ *port = atoi(++murl);
+
+ while (*murl && *murl != '/')
+ murl++;
+ return (murl);
+}
+
+#define ACCEPT "Accept: video/mpeg, video/x-mpegurl, */*\r\n"
+
+int http_open (char *url)
+{
+ char purl[1024], *host, req[1024], *sptr;
+ uint32_t ip;
+ uint32_t port;
+ int sock;
+ int reloc, relocnum = 0;
+ struct sockaddr_in server;
+ int mfd;
+
+ strncpy (purl, url, 1023);
+ purl[1023] = '\0';
+
+ do {
+ host = NULL;
+ strcpy (req, "GET ");
+ if (!(sptr = url2host(purl, &host, &ip, &port))) {
+ fprintf (stderr, "Unknown host\n");
+ exit (1);
+ }
+ strcat (req, sptr);
+ sprintf (req + strlen(req),
+ " HTTP/1.0\r\nUser-Agent: %s/%s\r\n",
+ "whatever", "you want");
+ if (host) {
+ sprintf(req + strlen(req),
+ "Host: %s:%u\r\n", host, port);
+ free (host);
+ }
+
+ strcat (req, ACCEPT);
+ strcat (req, "\r\n");
+
+ server.sin_port = htons(port);
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = ip;
+
+ if ((sock = socket(PF_INET, SOCK_STREAM, 6)) < 0) {
+ perror ("socket");
+ exit (1);
+ }
+
+ if (connect(sock, (struct sockaddr *)&server,
+ sizeof(server))) {
+ perror ("connect");
+ exit (1);
+ }
+
+ write_all (sock, req, strlen(req));
+ if (!(mfd = fileno(fdopen(sock, "rb")))) {
+ perror ("open");
+ exit (1);
+ }
+ reloc = 0;
+ purl[0] = '\0';
+ read_all (mfd, req, 1023);
+ if ((sptr = strchr(req, ' '))) {
+ switch (sptr[1]) {
+ case '2':
+ break;
+ case '3':
+ reloc = 1;
+ default:
+ fprintf (stderr, "HTTP req failed:%s",
+ sptr+1);
+ exit (1);
+ }
+ }
+ do {
+ read_all (mfd,req, 1023);
+ if (!strncmp(req, "Location:", 9))
+ strncpy (purl, req+10, 1023);
+ } while (req[0] != '\r' && req[0] != '\n');
+ } while (reloc && purl[0] && relocnum++ < 3);
+ if (reloc) {
+ fprintf (stderr, "Too many HTTP relocations.\n");
+ exit (1);
+ }
+
+ return sock;
+}
+
+extern int errno;
+const char * strerrno (void)
+{
+ return strerror(errno);
+}
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/ctools.h b/plugins/streamdev/streamdev-cvs/libdvbmpeg/ctools.h
new file mode 100644
index 0000000..a7b0271
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/ctools.h
@@ -0,0 +1,404 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002, 2003 Marcus Metzler
+ *
+ * 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
+ * of the License, 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
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+#include "ringbuffy.h"
+#include "transform.h"
+
+#ifndef _CTOOLS_H_
+#define _CTOOLS_H_
+
+#define VIDEO_MODE_PAL 0
+#define VIDEO_MODE_NTSC 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ enum {PS_STREAM, TS_STREAM, PES_STREAM};
+ enum {pDUNNO, pPAL, pNTSC};
+
+ uint64_t trans_pts_dts(uint8_t *pts);
+
+/*
+ PES
+*/
+
+#define PROG_STREAM_MAP 0xBC
+#ifndef PRIVATE_STREAM1
+#define PRIVATE_STREAM1 0xBD
+#endif
+#define PADDING_STREAM 0xBE
+#ifndef PRIVATE_STREAM2
+#define PRIVATE_STREAM2 0xBF
+#endif
+#define AUDIO_STREAM_S 0xC0
+#define AUDIO_STREAM_E 0xDF
+#define VIDEO_STREAM_S 0xE0
+#define VIDEO_STREAM_E 0xEF
+#define ECM_STREAM 0xF0
+#define EMM_STREAM 0xF1
+#define DSM_CC_STREAM 0xF2
+#define ISO13522_STREAM 0xF3
+#define PROG_STREAM_DIR 0xFF
+
+#define BUFFYSIZE 10*MAX_PLENGTH
+#define MAX_PTS 8192
+#define MAX_FRAME 8192
+#define MAX_PACK_L 4096
+#define PS_HEADER_L1 14
+#define PS_HEADER_L2 (PS_HEADER_L1+18)
+#define MAX_H_SIZE (PES_H_MIN + PS_HEADER_L1 + 5)
+#define PES_MIN 7
+#define PES_H_MIN 9
+
+//flags1
+#define FLAGS 0x40
+#define SCRAMBLE_FLAGS 0x30
+#define PRIORITY_FLAG 0x08
+#define DATA_ALIGN_FLAG 0x04
+#define COPYRIGHT_FLAG 0x02
+#define ORIGINAL_FLAG 0x01
+
+//flags2
+#define PTS_DTS_FLAGS 0xC0
+#define ESCR_FLAG 0x20
+#define ES_RATE_FLAG 0x10
+#define DSM_TRICK_FLAG 0x08
+#define ADD_CPY_FLAG 0x04
+#define PES_CRC_FLAG 0x02
+#define PES_EXT_FLAG 0x01
+
+//pts_dts flags
+#define PTS_ONLY 0x80
+#define PTS_DTS 0xC0
+
+//private flags
+#define PRIVATE_DATA 0x80
+#define HEADER_FIELD 0x40
+#define PACK_SEQ_CTR 0x20
+#define P_STD_BUFFER 0x10
+#define PES_EXT_FLAG2 0x01
+
+#define MPEG1_2_ID 0x40
+#define STFF_LNGTH_MASK 0x3F
+
+
+ typedef struct pes_packet_{
+ uint8_t stream_id;
+ uint8_t llength[2];
+ uint32_t length;
+ uint8_t flags1;
+ uint8_t flags2;
+ uint8_t pes_hlength;
+ uint8_t pts[5];
+ uint8_t dts[5];
+ uint8_t escr[6];
+ uint8_t es_rate[3];
+ uint8_t trick;
+ uint8_t add_cpy;
+ uint8_t prev_pes_crc[2];
+ uint8_t priv_flags;
+ uint8_t pes_priv_data[16];
+ uint8_t pack_field_length;
+ uint8_t *pack_header;
+ uint8_t pck_sqnc_cntr;
+ uint8_t org_stuff_length;
+ uint8_t p_std[2];
+ uint8_t pes_ext_lngth;
+ uint8_t *pes_ext;
+ uint8_t *pes_pckt_data;
+ int padding;
+ int mpeg;
+ int mpeg1_pad;
+ uint8_t *mpeg1_headr;
+ uint8_t stuffing;
+ } pes_packet;
+
+ void init_pes(pes_packet *p);
+ void kill_pes(pes_packet *p);
+ void setlength_pes(pes_packet *p);
+ void nlength_pes(pes_packet *p);
+ int cwrite_pes(uint8_t *buf, pes_packet *p, long length);
+ void write_pes(int fd, pes_packet *p);
+ int read_pes(int f, pes_packet *p);
+ void cread_pes(char *buf, pes_packet *p);
+
+/*
+ Transport Stream
+*/
+
+#define TS_SIZE 188
+#define TRANS_ERROR 0x80
+#define PAY_START 0x40
+#define TRANS_PRIO 0x20
+#define PID_MASK_HI 0x1F
+//flags
+#define TRANS_SCRMBL1 0x80
+#define TRANS_SCRMBL2 0x40
+#define ADAPT_FIELD 0x20
+#define PAYLOAD 0x10
+#define COUNT_MASK 0x0F
+
+// adaptation flags
+#define DISCON_IND 0x80
+#define RAND_ACC_IND 0x40
+#define ES_PRI_IND 0x20
+#define PCR_FLAG 0x10
+#define OPCR_FLAG 0x08
+#define SPLICE_FLAG 0x04
+#define TRANS_PRIV 0x02
+#define ADAP_EXT_FLAG 0x01
+
+// adaptation extension flags
+#define LTW_FLAG 0x80
+#define PIECE_RATE 0x40
+#define SEAM_SPLICE 0x20
+
+ typedef struct ts_packet_{
+ uint8_t pid[2];
+ uint8_t flags;
+ uint8_t count;
+ uint8_t data[184];
+ uint8_t adapt_length;
+ uint8_t adapt_flags;
+ uint8_t pcr[6];
+ uint8_t opcr[6];
+ uint8_t splice_count;
+ uint8_t priv_dat_len;
+ uint8_t *priv_dat;
+ uint8_t adapt_ext_len;
+ uint8_t adapt_eflags;
+ uint8_t ltw[2];
+ uint8_t piece_rate[3];
+ uint8_t dts[5];
+ int rest;
+ uint8_t stuffing;
+ } ts_packet;
+
+ void init_ts(ts_packet *p);
+ void kill_ts(ts_packet *p);
+ unsigned short pid_ts(ts_packet *p);
+ int cwrite_ts(uint8_t *buf, ts_packet *p, long length);
+ void write_ts(int fd, ts_packet *p);
+ int read_ts(int f, ts_packet *p);
+ void cread_ts (char *buf, ts_packet *p, long length);
+
+
+/*
+ Program Stream
+*/
+
+#define PACK_STUFF_MASK 0x07
+
+#define FIXED_FLAG 0x02
+#define CSPS_FLAG 0x01
+#define SAUDIO_LOCK_FLAG 0x80
+#define SVIDEO_LOCK_FLAG 0x40
+
+#define PS_MAX 200
+
+ typedef struct ps_packet_{
+ uint8_t scr[6];
+ uint8_t mux_rate[3];
+ uint8_t stuff_length;
+ uint8_t *data;
+ uint8_t sheader_llength[2];
+ int sheader_length;
+ uint8_t rate_bound[3];
+ uint8_t audio_bound;
+ uint8_t video_bound;
+ uint8_t reserved;
+ int npes;
+ int mpeg;
+ } ps_packet;
+
+ void init_ps(ps_packet *p);
+ void kill_ps(ps_packet *p);
+ void setlength_ps(ps_packet *p);
+ uint32_t scr_base_ps(ps_packet *p);
+ uint16_t scr_ext_ps(ps_packet *p);
+ int mux_ps(ps_packet *p);
+ int rate_ps(ps_packet *p);
+ int cwrite_ps(uint8_t *buf, ps_packet *p, long length);
+ void write_ps(int fd, ps_packet *p);
+ int read_ps (int f, ps_packet *p);
+ void cread_ps (char *buf, ps_packet *p, long length);
+
+
+
+#define MAX_PLENGTH 0xFFFF
+
+ typedef struct sectionstruct {
+ int id;
+ int length;
+ int found;
+ uint8_t payload[4096+3];
+ } section;
+
+
+ typedef uint32_t tflags;
+#define MAXFILT 32
+#define MASKL 16
+ typedef struct trans_struct {
+ int found;
+ uint8_t packet[188];
+ uint16_t pid[MAXFILT];
+ uint8_t mask[MAXFILT*MASKL];
+ uint8_t filt[MAXFILT*MASKL];
+ uint8_t transbuf[MAXFILT*188];
+ int transcount[MAXFILT];
+ section sec[MAXFILT];
+ tflags is_full;
+ tflags pes_start;
+ tflags pes_started;
+ tflags pes;
+ tflags set;
+ } trans;
+
+
+ void init_trans(trans *p);
+ int set_trans_filt(trans *p, int filtn, uint16_t pid, uint8_t *mask,
+ uint8_t *filt, int pes);
+
+ void clear_trans_filt(trans *p,int filtn);
+ int filt_is_set(trans *p, int filtn);
+ int pes_is_set(trans *p, int filtn);
+ int pes_is_started(trans *p, int filtn);
+ int pes_is_start(trans *p, int filtn);
+ int filt_is_ready(trans *p,int filtn);
+
+ void trans_filt(uint8_t *buf, int count, trans *p);
+ void tfilter(trans *p);
+ void pes_filter(trans *p, int filtn, int off);
+ void sec_filter(trans *p, int filtn, int off);
+ int get_filt_buf(trans *p, int filtn,uint8_t **buf);
+ section *get_filt_sec(trans *p, int filtn);
+
+
+ typedef struct a2pstruct{
+ int type;
+ int fd;
+ int found;
+ int length;
+ int headr;
+ int plength;
+ uint8_t cid;
+ uint8_t flags;
+ uint8_t abuf[MAX_PLENGTH];
+ int alength;
+ uint8_t vbuf[MAX_PLENGTH];
+ int vlength;
+ uint8_t last_av_pts[4];
+ uint8_t av_pts[4];
+ uint8_t scr[4];
+ uint8_t pid0;
+ uint8_t pid1;
+ uint8_t pidv;
+ uint8_t pida;
+ } a2p;
+
+
+
+ void get_pespts(uint8_t *av_pts,uint8_t *pts);
+ void init_a2p(a2p *p);
+ void av_pes_to_pes(uint8_t *buf,int count, a2p *p);
+ int w_pesh(uint8_t id,int length ,uint8_t *pts, uint8_t *obuf);
+ int w_tsh(uint8_t id,int length ,uint8_t *pts, uint8_t *obuf,a2p *p,int startpes);
+ void pts2pts(uint8_t *av_pts, uint8_t *pts);
+ void write_ps_headr(ps_packet *p,uint8_t *pts,int fd);
+
+ typedef struct p2t_s{
+ uint8_t pes[TS_SIZE];
+ uint8_t counter;
+ long int pos;
+ int frags;
+ void (*t_out)(uint8_t const *buf);
+ } p2t_t;
+
+ void twrite(uint8_t const *buf);
+ void init_p2t(p2t_t *p, void (*fkt)(uint8_t const *buf));
+ long int find_pes_header(uint8_t const *buf, long int length, int *frags);
+ void pes_to_ts( uint8_t const *buf, long int length, uint16_t pid, p2t_t *p);
+ void p_to_t( uint8_t const *buf, long int length, uint16_t pid,
+ uint8_t *counter, void (*ts_write)(uint8_t const *));
+
+
+ int write_pes_header(uint8_t id,int length , long PTS,
+ uint8_t *obuf, int stuffing);
+
+ int write_ps_header(uint8_t *buf,
+ uint32_t SCR,
+ long muxr,
+ uint8_t audio_bound,
+ uint8_t fixed,
+ uint8_t CSPS,
+ uint8_t audio_lock,
+ uint8_t video_lock,
+ uint8_t video_bound,
+ uint8_t stream1,
+ uint8_t buffer1_scale,
+ uint32_t buffer1_size,
+ uint8_t stream2,
+ uint8_t buffer2_scale,
+ uint32_t buffer2_size);
+
+
+ int seek_mpg_start(uint8_t *buf, int size);
+
+
+ void split_mpg(char *name, uint64_t size);
+ void cut_mpg(char *name, uint64_t size);
+ int http_open (char *url);
+ ssize_t save_read(int fd, void *buf, size_t count);
+
+ const char * strerrno(void);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /*_CTOOLS_H_*/
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/remux.c b/plugins/streamdev/streamdev-cvs/libdvbmpeg/remux.c
new file mode 100644
index 0000000..1d65525
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/remux.c
@@ -0,0 +1,1215 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ * 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
+ * of the License, 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
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de,
+ */
+
+#include "remux.h"
+
+unsigned int bitrates[3][16] =
+{{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},
+ {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0},
+ {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}};
+
+uint32_t freq[4] = {441, 480, 320, 0};
+static uint32_t samples[4] = { 384, 1152, 0, 0};
+char *frames[3] = {"I-Frame","P-Frame","B-Frame"};
+
+
+void copy_ptslm(PTS_List *a, PTS_List *b)
+{
+ a->pos = b->pos;
+ a->PTS = b->PTS;
+ a->dts = b->dts;
+ a->spos = b->spos;
+}
+
+void clear_ptslm(PTS_List *a)
+{
+ a->pos = 0;
+ a->PTS = 0;
+ a->dts = 0;
+ a->spos = 0;
+}
+
+void init_ptsl(PTS_List *ptsl)
+{
+ int i;
+ for (i=0;i< MAX_PTS;i++){
+ clear_ptslm(&ptsl[i]);
+ }
+}
+
+int del_pts(PTS_List *ptsl, int pos, int nr)
+{
+ int i;
+ int del = 0;
+
+ for( i = 0; i < nr-1; i++){
+ if(pos > ptsl[i].pos && pos >= ptsl[i+1].pos) del++;
+ }
+
+ if(del)
+ for( i = 0; i < nr-del; i++){
+ copy_ptslm(&ptsl[i], &ptsl[i+del]);
+ }
+
+ return nr-del;
+}
+
+int del_ptss(PTS_List *ptsl, int pts, int *nb)
+{
+ int i;
+ int del = 0;
+ int sum = 0;
+ int nr = *nb;
+
+ for( i = 0; i < nr; i++){
+ if(pts > ptsl[i].PTS){
+ del++;
+ sum += ptsl[i].pos;
+ }
+ }
+
+ if(del)
+ for( i = 0; i < nr-del; i++){
+ copy_ptslm(&ptsl[i], &ptsl[i+del]);
+ }
+
+ *nb = nr-del;
+ return sum;
+}
+
+int add_pts(PTS_List *ptsl, uint32_t pts, int pos, int spos, int nr, uint32_t dts)
+{
+ int i;
+
+ for ( i=0;i < nr; i++) if (spos && ptsl[i].pos == pos) return nr;
+ if (nr == MAX_PTS) {
+ nr = del_pts(ptsl, ptsl[1].pos+1, nr);
+ } else nr++;
+ i = nr-1;
+
+ ptsl[i].pos = pos;
+ ptsl[i].spos = spos;
+ ptsl[i].PTS = pts;
+ ptsl[i].dts = dts;
+ return nr;
+}
+
+void add_vpts(Remux *rem, uint8_t *pts)
+{
+ uint32_t PTS = trans_pts_dts(pts);
+ rem->vptsn = add_pts(rem->vpts_list, PTS, rem->vwrite, rem->awrite,
+ rem->vptsn, PTS);
+}
+
+void add_apts(Remux *rem, uint8_t *pts)
+{
+ uint32_t PTS = trans_pts_dts(pts);
+ rem->aptsn = add_pts(rem->apts_list, PTS, rem->awrite, rem->vwrite,
+ rem->aptsn, PTS);
+}
+
+void del_vpts(Remux *rem)
+{
+ rem->vptsn = del_pts(rem->vpts_list, rem->vread, rem->vptsn);
+}
+
+void del_apts(Remux *rem)
+{
+ rem->aptsn = del_pts(rem->apts_list, rem->aread, rem->aptsn);
+}
+
+
+void copy_framelm(FRAME_List *a, FRAME_List *b)
+{
+ a->type = b->type;
+ a->pos = b->pos;
+ a->FRAME = b->FRAME;
+ a->time = b->time;
+ a->pts = b->pts;
+ a->dts = b->dts;
+}
+
+void clear_framelm(FRAME_List *a)
+{
+ a->type = 0;
+ a->pos = 0;
+ a->FRAME = 0;
+ a->time = 0;
+ a->pts = 0;
+ a->dts = 0;
+}
+
+void init_framel(FRAME_List *framel)
+{
+ int i;
+ for (i=0;i< MAX_FRAME;i++){
+ clear_framelm(&framel[i]);
+ }
+}
+
+int del_frame(FRAME_List *framel, int pos, int nr)
+{
+ int i;
+ int del = 0;
+
+ for( i = 0; i < nr-1; i++){
+ if(pos > framel[i].pos && pos >= framel[i+1].pos) del++;
+ }
+
+ if(del)
+ for( i = 0; i < nr-del; i++){
+ copy_framelm(&framel[i], &framel[i+del]);
+ }
+
+ return nr-del;
+}
+
+int add_frame(FRAME_List *framel, uint32_t frame, int pos, int type, int nr,
+ uint32_t time, uint32_t pts, uint32_t dts)
+{
+ int i;
+
+ if (nr == MAX_FRAME) {
+ nr = del_frame(framel, framel[1].pos+1, nr);
+ } else nr++;
+ i = nr-1;
+
+ framel[i].type = type;
+ framel[i].pos = pos;
+ framel[i].FRAME = frame;
+ framel[i].time = time;
+ framel[i].pts = pts;
+ framel[i].dts = dts;
+ return nr;
+}
+
+void add_vframe(Remux *rem, uint32_t frame, long int pos, int type, int time,
+ uint32_t pts, uint32_t dts)
+{
+ rem->vframen = add_frame(rem->vframe_list, frame, pos, type,
+ rem->vframen, time, pts, dts);
+}
+
+void add_aframe(Remux *rem, uint32_t frame, long int pos, uint32_t pts)
+{
+ rem->aframen = add_frame(rem->aframe_list, frame, pos, 0,
+ rem->aframen, 0, pts, pts);
+}
+
+void del_vframe(Remux *rem)
+{
+ rem->vframen = del_frame(rem->vframe_list, rem->vread, rem->vframen);
+}
+
+void del_aframe(Remux *rem)
+{
+ rem->aframen = del_frame(rem->aframe_list, rem->aread, rem->aframen);
+}
+
+
+void printpts(uint32_t pts)
+{
+ fprintf(stderr,"%2d:%02d:%02d.%03d",
+ (int)(pts/90000.)/3600,
+ ((int)(pts/90000.)%3600)/60,
+ ((int)(pts/90000.)%3600)%60,
+ (((int)(pts/90.)%3600000)%60000)%1000
+ );
+}
+
+
+void find_vframes( Remux *rem, uint8_t *buf, int l)
+{
+ int c = 0;
+ int type;
+ uint32_t time = 0;
+ int hour;
+ int min;
+ int sec;
+ uint64_t pts=0;
+ uint64_t dts=0;
+ uint32_t tempref = 0;
+
+ while ( c < l - 6){
+ if (buf[c] == 0x00 &&
+ buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01 &&
+ buf[c+3] == 0xB8) {
+ c += 4;
+ hour = (int)((buf[c]>>2)& 0x1F);
+ min = (int)(((buf[c]<<4)& 0x30)|
+ ((buf[c+1]>>4)& 0x0F));
+ sec = (int)(((buf[c+1]<<3)& 0x38)|
+ ((buf[c+2]>>5)& 0x07));
+
+ time = 3600*hour + 60*min + sec;
+ if ( rem->time_off){
+ time = (uint32_t)((uint64_t)time - rem->time_off);
+ hour = time/3600;
+ min = (time%3600)/60;
+ sec = (time%3600)%60;
+ /*
+ buf[c] |= (hour & 0x1F) << 2;
+ buf[c] |= (min & 0x30) >> 4;
+ buf[c+1] |= (min & 0x0F) << 4;
+ buf[c+1] |= (sec & 0x38) >> 3;
+ buf[c+2] |= (sec & 0x07) >> 5;*/
+ }
+ rem->group++;
+ rem->groupframe = 0;
+ }
+ if ( buf[c] == 0x00 &&
+ buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01 &&
+ buf[c+3] == 0x00) {
+ c += 4;
+ tempref = (buf[c+1]>>6) & 0x03;
+ tempref |= buf[c] << 2;
+
+ type = ((buf[c+1]&0x38) >>3);
+ if ( rem->video_info.framerate){
+ pts = ((uint64_t)rem->vframe + tempref + 1
+ - rem->groupframe ) * 90000ULL
+ /rem->video_info.framerate
+ + rem->vpts_off;
+ dts = (uint64_t)rem->vframe * 90000ULL/
+ rem->video_info.framerate
+ + rem->vpts_off;
+
+
+fprintf(stderr,"MYPTS:");
+printpts((uint32_t)pts-rem->vpts_off);
+ fprintf(stderr," REALPTS:");
+ printpts(rem->vpts_list[rem->vptsn-1].PTS-rem->vpts_off);
+ fprintf(stderr," DIFF:");
+ printpts(pts-(uint64_t)rem->vpts_list[rem->vptsn-1].PTS);
+// fprintf(stderr," DIST: %4d",-rem->vpts_list[rem->vptsn-1].pos+(rem->vwrite+c-4));
+ //fprintf(stderr," ERR: %3f",(double)(-rem->vpts_list[rem->vptsn-1].PTS+pts)/(rem->vframe+1));
+ fprintf(stderr,"\r");
+
+
+
+ rem->vptsn = add_pts(rem->vpts_list,(uint32_t)pts
+ ,rem->vwrite+c-4,
+ rem->awrite,
+ rem->vptsn,
+ (uint32_t)dts);
+
+
+
+ }
+ rem->vframe++;
+ rem->groupframe++;
+ add_vframe( rem, rem->vframe, rem->vwrite+c, type,
+ time, pts, dts);
+ } else c++;
+ }
+}
+
+void find_aframes( Remux *rem, uint8_t *buf, int l)
+{
+ int c = 0;
+ uint64_t pts = 0;
+ int sam;
+ uint32_t fr;
+
+
+ while ( c < l - 2){
+ if ( buf[c] == 0xFF &&
+ (buf[c+1] & 0xF8) == 0xF8) {
+ c += 2;
+ if ( rem->audio_info.layer >= 0){
+ sam = samples[3-rem->audio_info.layer];
+ fr = freq[rem->audio_info.frequency] ;
+
+ pts = ( (uint64_t)rem->aframe * sam * 900ULL)/fr
+ + rem->apts_off;
+
+
+fprintf(stderr,"MYPTS:");
+printpts((uint32_t)pts-rem->apts_off);
+ fprintf(stderr," REALPTS:");
+ printpts(rem->apts_list[rem->aptsn-1].PTS-rem->apts_off);
+ fprintf(stderr," DIFF:");
+ printpts((uint32_t)((uint64_t)rem->apts_list[rem->aptsn-1].PTS-pts));
+// fprintf(stderr," DIST: %4d",-rem->apts_list[rem->aptsn-1].pos+(rem->awrite+c-2));
+ fprintf(stderr,"\r");
+
+ rem->aptsn = add_pts(rem->apts_list,(uint32_t)pts
+ ,rem->awrite+c-2,
+ rem->vwrite,
+ rem->aptsn,
+ (uint32_t)pts);
+ }
+
+ rem->aframe++;
+ add_aframe( rem, rem->aframe, rem->awrite+c, pts);
+
+ } else c++;
+ }
+}
+
+int refill_buffy(Remux *rem)
+{
+ pes_packet pes;
+ int count = 0;
+ int acount, vcount;
+ ringbuffy *vbuf = &rem->vid_buffy;
+ ringbuffy *abuf = &rem->aud_buffy;
+ int fin = rem->fin;
+
+ acount = abuf->size-ring_rest(abuf);
+ vcount = vbuf->size-ring_rest(vbuf);
+
+
+ while ( acount > MAX_PLENGTH && vcount > MAX_PLENGTH && count < 10){
+ int neof;
+ count++;
+ init_pes(&pes);
+ if ((neof = read_pes(fin,&pes)) <= 0) return -1;
+ switch(pes.stream_id){
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ rem->apes++;
+ if( rem->audio_info.layer < 0 &&
+ (pes.flags2 & PTS_DTS) )
+ add_apts(rem, pes.pts);
+ find_aframes( rem, pes.pes_pckt_data, pes.length);
+ ring_write(abuf,(char *)pes.pes_pckt_data,pes.length);
+ rem->awrite += pes.length;
+ break;
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ rem->vpes++;
+ if( !rem->video_info.framerate &&
+ (pes.flags2 & PTS_DTS) )
+ add_vpts(rem, pes.pts);
+
+ find_vframes( rem, pes.pes_pckt_data, pes.length);
+
+ ring_write(vbuf,(char *)pes.pes_pckt_data,pes.length);
+ rem->vwrite += pes.length;
+ break;
+ }
+ acount = abuf->size-ring_rest(abuf);
+ vcount = vbuf->size-ring_rest(vbuf);
+ kill_pes(&pes);
+ }
+ if (count < 10) return 0;
+ return 1;
+}
+
+int vring_read( Remux *rem, uint8_t *buf, int l)
+{
+ int c = 0;
+ int r = 0;
+
+ if (ring_rest(&rem->vid_buffy) <= l)
+ r = refill_buffy(rem);
+ if (r) return -1;
+
+ c = ring_read(&rem->vid_buffy, (char *) buf, l);
+ rem->vread += c;
+ del_vpts(rem);
+ del_vframe(rem);
+ return c;
+}
+
+int aring_read( Remux *rem, uint8_t *buf, int l)
+{
+ int c = 0;
+ int r = 0;
+
+ if (ring_rest(&rem->aud_buffy) <= l)
+ r = refill_buffy(rem);
+ if (r) return -1;
+
+ c = ring_read(&rem->aud_buffy, (char *)buf, l);
+ rem->aread += c;
+ del_apts(rem);
+ del_aframe(rem);
+ return c;
+}
+
+int vring_peek( Remux *rem, uint8_t *buf, int l, long off)
+{
+ int c = 0;
+
+ if (ring_rest(&rem->vid_buffy) <= l)
+ refill_buffy(rem);
+
+ c = ring_peek(&rem->vid_buffy, (char *) buf, l, off);
+ return c;
+}
+
+int aring_peek( Remux *rem, uint8_t *buf, int l, long off)
+{
+ int c = 0;
+
+ if (ring_rest(&rem->aud_buffy) <= l)
+ refill_buffy(rem);
+
+ c = ring_peek(&rem->aud_buffy, (char *)buf, l, off);
+ return c;
+}
+
+
+int get_video_info(Remux *rem)
+{
+ uint8_t buf[12];
+ uint8_t *headr;
+ int found = 0;
+ int sw;
+ long off = 0;
+ int form = -1;
+ ringbuffy *vid_buffy = &rem->vid_buffy;
+ VideoInfo *vi = &rem->video_info;
+
+ while (found < 4 && ring_rest(vid_buffy)){
+ uint8_t b[4];
+
+ vring_peek( rem, b, 4, 0);
+ if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01
+ && b[3] == 0xb3) found = 4;
+ else {
+ off++;
+ vring_read( rem, b, 1);
+ }
+ }
+ rem->vframe = rem->vframen-1;
+
+ if (! found) return -1;
+ buf[0] = 0x00; buf[1] = 0x00; buf[2] = 0x01; buf[3] = 0xb3;
+ headr = buf+4;
+ if(vring_peek(rem, buf, 12, 0) < 12) return -1;
+
+ vi->horizontal_size = ((headr[1] &0xF0) >> 4) | (headr[0] << 4);
+ vi->vertical_size = ((headr[1] &0x0F) << 8) | (headr[2]);
+
+ sw = (int)((headr[3]&0xF0) >> 4) ;
+
+ switch( sw ){
+ case 1:
+ fprintf(stderr,"Videostream: ASPECT: 1:1");
+ vi->aspect_ratio = 100;
+ break;
+ case 2:
+ fprintf(stderr,"Videostream: ASPECT: 4:3");
+ vi->aspect_ratio = 133;
+ break;
+ case 3:
+ fprintf(stderr,"Videostream: ASPECT: 16:9");
+ vi->aspect_ratio = 177;
+ break;
+ case 4:
+ fprintf(stderr,"Videostream: ASPECT: 2.21:1");
+ vi->aspect_ratio = 221;
+ break;
+
+ case 5 ... 15:
+ fprintf(stderr,"Videostream: ASPECT: reserved");
+ vi->aspect_ratio = 0;
+ break;
+
+ default:
+ vi->aspect_ratio = 0;
+ return -1;
+ }
+
+ fprintf(stderr," Size = %dx%d",vi->horizontal_size,vi->vertical_size);
+
+ sw = (int)(headr[3]&0x0F);
+
+ switch ( sw ) {
+ case 1:
+ fprintf(stderr," FRate: 23.976 fps");
+ vi->framerate = 24000/1001.;
+ form = -1;
+ break;
+ case 2:
+ fprintf(stderr," FRate: 24 fps");
+ vi->framerate = 24;
+ form = -1;
+ break;
+ case 3:
+ fprintf(stderr," FRate: 25 fps");
+ vi->framerate = 25;
+ form = VIDEO_MODE_PAL;
+ break;
+ case 4:
+ fprintf(stderr," FRate: 29.97 fps");
+ vi->framerate = 30000/1001.;
+ form = VIDEO_MODE_NTSC;
+ break;
+ case 5:
+ fprintf(stderr," FRate: 30 fps");
+ vi->framerate = 30;
+ form = VIDEO_MODE_NTSC;
+ break;
+ case 6:
+ fprintf(stderr," FRate: 50 fps");
+ vi->framerate = 50;
+ form = VIDEO_MODE_PAL;
+ break;
+ case 7:
+ fprintf(stderr," FRate: 60 fps");
+ vi->framerate = 60;
+ form = VIDEO_MODE_NTSC;
+ break;
+ }
+
+ rem->dts_delay = (int)(7.0/vi->framerate/2.0*90000);
+
+ vi->bit_rate = 400*(((headr[4] << 10) & 0x0003FC00UL)
+ | ((headr[5] << 2) & 0x000003FCUL) |
+ (((headr[6] & 0xC0) >> 6) & 0x00000003UL));
+
+ fprintf(stderr," BRate: %.2f Mbit/s",(vi->bit_rate)/1000000.);
+
+ fprintf(stderr,"\n");
+ vi->video_format = form;
+
+ /*
+ marker_bit (&video_bs, 1);
+ vi->vbv_buffer_size = getbits (&video_bs, 10);
+ vi->CSPF = get1bit (&video_bs);
+ */
+ return form;
+}
+
+
+int get_audio_info( Remux *rem)
+{
+ uint8_t *headr;
+ uint8_t buf[3];
+ long off = 0;
+ int found = 0;
+ ringbuffy *aud_buffy = &rem->aud_buffy;
+ AudioInfo *ai = &rem->audio_info;
+
+ while(!ring_rest(aud_buffy) && !refill_buffy(rem));
+ while (found < 2 && ring_rest(aud_buffy)){
+ uint8_t b[2];
+ refill_buffy(rem);
+ aring_peek( rem, b, 2, 0);
+
+ if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8)
+ found = 2;
+ else {
+ off++;
+ aring_read( rem, b, 1);
+ }
+ }
+
+ if (!found) return -1;
+ rem->aframe = rem->aframen-1;
+
+ if (aring_peek(rem, buf, 3, 0) < 1) return -1;
+ headr = buf+2;
+
+ ai->layer = (buf[1] & 0x06) >> 1;
+
+ fprintf(stderr,"Audiostream: Layer: %d", 4-ai->layer);
+
+
+ ai->bit_rate = bitrates[(3-ai->layer)][(headr[0] >> 4 )]*1000;
+
+ if (ai->bit_rate == 0)
+ fprintf (stderr," Bit rate: free");
+ else if (ai->bit_rate == 0xf)
+ fprintf (stderr," BRate: reserved");
+ else
+ fprintf (stderr," BRate: %d kb/s", ai->bit_rate/1000);
+
+
+ ai->frequency = (headr[0] & 0x0c ) >> 2;
+ if (ai->frequency == 3)
+ fprintf (stderr, " Freq: reserved\n");
+ else
+ fprintf (stderr," Freq: %2.1f kHz\n",
+ freq[ai->frequency]/10.);
+
+ return 0;
+}
+
+
+
+void init_remux(Remux *rem, int fin, int fout, int mult)
+{
+ rem->video_info.framerate = 0;
+ rem->audio_info.layer = -1;
+ rem->fin = fin;
+ rem->fout = fout;
+ ring_init(&rem->vid_buffy, 40*BUFFYSIZE*mult);
+ ring_init(&rem->aud_buffy, BUFFYSIZE*mult);
+ init_ptsl(rem->vpts_list);
+ init_ptsl(rem->apts_list);
+ init_framel(rem->vframe_list);
+ init_framel(rem->aframe_list);
+
+ rem->vptsn = 0;
+ rem->aptsn = 0;
+ rem->vframen = 0;
+ rem->aframen = 0;
+ rem->vframe = 0;
+ rem->aframe = 0;
+ rem->vcframe = 0;
+ rem->acframe = 0;
+ rem->vpts = 0;
+ rem->vdts = 0;
+ rem->apts_off = 0;
+ rem->vpts_off = 0;
+ rem->apts_delay= 0;
+ rem->vpts_delay= 0;
+ rem->dts_delay = 0;
+ rem->apts = 0;
+ rem->vpes = 0;
+ rem->apes = 0;
+ rem->vpts_old = 0;
+ rem->apts_old = 0;
+ rem->SCR = 0;
+ rem->vwrite = 0;
+ rem->awrite = 0;
+ rem->vread = 0;
+ rem->aread = 0;
+ rem->group = 0;
+ rem->groupframe= 0;
+ rem->pack_size = 0;
+ rem->muxr = 0;
+ rem->time_off = 0;
+}
+
+uint32_t bytes2pts(int bytes, int rate)
+{
+ if (bytes < 0xFFFFFFFFUL/720000UL)
+ return (uint32_t)(bytes*720000UL/rate);
+ else
+ return (uint32_t)(bytes/rate*720000UL);
+}
+
+long pts2bytes( uint32_t pts, int rate)
+{
+ if (pts < 0xEFFFFFFFUL/rate)
+ return (pts*rate/720000);
+ else
+ return (pts* (rate/720000));
+}
+
+int write_audio_pes( Remux *rem, uint8_t *buf, int *alength)
+{
+ int add;
+ int pos = 0;
+ int p = 0;
+ uint32_t pts = 0;
+ int stuff = 0;
+ int length = *alength;
+
+ if (!length) return 0;
+ p = PS_HEADER_L1+PES_H_MIN;
+
+ if (rem->apts_old != rem->apts){
+ pts = (uint32_t)((uint64_t)rem->apts + rem->apts_delay - rem->apts_off);
+ p += 5;
+ }
+ if ( length+p >= rem->pack_size){
+ length = rem->pack_size;
+ } else {
+ if (rem->pack_size-length-p <= PES_MIN){
+ stuff = rem->pack_size - length;
+ length = rem->pack_size;
+ } else
+ length = length+p;
+ }
+ pos = write_ps_header(buf,rem->SCR,rem->muxr, 1, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0);
+
+ pos += write_pes_header( 0xC0, length-pos, pts, buf+pos, stuff);
+ add = aring_read( rem, buf+pos, length-pos);
+ *alength = add;
+ if (add < 0) return -1;
+ pos += add;
+ rem->apts_old = rem->apts;
+ rem->apts = rem->apts_list[0].PTS;
+
+ if (pos+PES_MIN < rem->pack_size){
+ pos += write_pes_header( PADDING_STREAM, rem->pack_size-pos, 0,
+ buf+pos, 0);
+ pos = rem->pack_size;
+ }
+ if (pos != rem->pack_size) {
+ fprintf(stderr,"apos: %d\n",pos);
+ exit(1);
+ }
+
+ return pos;
+}
+
+int write_video_pes( Remux *rem, uint8_t *buf, int *vlength)
+{
+ int add;
+ int pos = 0;
+ int p = 0;
+ uint32_t pts = 0;
+ uint32_t dts = 0;
+ int stuff = 0;
+ int length = *vlength;
+ long diff = 0;
+
+ if (! length) return 0;
+ p = PS_HEADER_L1+PES_H_MIN;
+
+ if (rem->vpts_old != rem->vpts){
+ pts = (uint32_t)((uint64_t)rem->vpts + rem->vpts_delay - rem->vpts_off);
+ p += 5;
+ }
+ if ( length+p >= rem->pack_size){
+ length = rem->pack_size;
+ } else {
+ if (rem->pack_size - length - p <= PES_MIN){
+ stuff = rem->pack_size - length;
+ length = rem->pack_size;
+ } else
+ length = length+p;
+ }
+
+ pos = write_ps_header(buf,rem->SCR,rem->muxr, 1, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0);
+
+ pos += write_pes_header( 0xE0, length-pos, pts, buf+pos, stuff);
+ add = vring_read( rem, buf+pos, length-pos);
+ *vlength = add;
+ if (add < 0) return -1;
+ pos += add;
+ rem->vpts_old = rem->vpts;
+ dts = rem->vdts;
+ rem->vpts = rem->vpts_list[0].PTS;
+ rem->vdts = rem->vpts_list[0].dts;
+ if ( diff > 0) rem->SCR += diff;
+ if (pos+PES_MIN < rem->pack_size){
+ // fprintf(stderr,"vstuffing: %d \n",rem->pack_size-pos);
+ pos += write_pes_header( PADDING_STREAM, rem->pack_size-pos, 0,
+ buf+pos, 0);
+ pos = rem->pack_size;
+ }
+ return pos;
+}
+
+void print_info( Remux *rem , int ret)
+{
+ int newtime = 0;
+ static int time = 0;
+ int i = 0;
+
+ while(! newtime && i < rem->vframen) {
+ if( (newtime = rem->vframe_list[i].time)) break;
+ i++;
+ }
+ if (newtime) time = newtime;
+
+ fprintf(stderr,"SCR:");
+ printpts(rem->SCR);
+ fprintf(stderr," VDTS:");
+ printpts((uint32_t)((uint64_t)rem->vdts - rem->vpts_off + rem->vpts_delay));
+ fprintf(stderr," APTS:");
+ printpts((uint32_t)((uint64_t)rem->apts - rem->apts_off + rem->apts_delay));
+ fprintf(stderr," TIME:%2d:", time/3600);
+ fprintf(stderr,"%02d:", (time%3600)/60);
+ fprintf(stderr,"%02d", (time%3600)%60);
+ if (ret) fprintf(stderr,"\n");
+ else fprintf(stderr,"\r");
+}
+
+void remux(int fin, int fout, int pack_size, int mult)
+{
+ Remux rem;
+ long ptsdiff;
+ uint8_t buf[MAX_PACK_L];
+ long pos = 0;
+ int r = 0;
+ int i, r1, r2;
+ long packets = 0;
+ uint8_t mpeg_end[4] = { 0x00, 0x00, 0x01, 0xB9 };
+ uint32_t SCR_inc = 0;
+ int data_size;
+ long vbuf, abuf;
+ long vbuf_max, abuf_max;
+ PTS_List abufl[MAX_PTS];
+ PTS_List vbufl[MAX_PTS];
+ int abufn = 0;
+ int vbufn = 0;
+ uint64_t pts_d = 0;
+ int ok_audio;
+ int ok_video;
+ uint32_t apos = 0;
+ uint32_t vpos = 0;
+ int vpack_size = 0;
+ int apack_size = 0;
+
+ init_ptsl(abufl);
+ init_ptsl(vbufl);
+
+ if (mult < 0 || mult >1000){
+ fprintf(stderr,"Multipler too large\n");
+ exit(1);
+ }
+ init_remux(&rem, fin, fout, mult);
+ rem.pack_size = pack_size;
+ data_size = pack_size - MAX_H_SIZE;
+ fprintf(stderr,"pack_size: %d header_size: %d data size: %d\n",
+ pack_size, MAX_H_SIZE, data_size);
+ refill_buffy(&rem);
+ fprintf(stderr,"Package size: %d\n",pack_size);
+
+ if ( get_video_info(&rem) < 0 ){
+ fprintf(stderr,"ERROR: Can't find valid video stream\n");
+ exit(1);
+ }
+
+ i = 0;
+ while(! rem.time_off && i < rem.vframen) {
+ if( (rem.time_off = rem.vframe_list[i].time)) break;
+ i++;
+ }
+
+ if ( get_audio_info(&rem) < 0 ){
+ fprintf(stderr,"ERROR: Can't find valid audio stream\n");
+ exit(1);
+ }
+
+ rem.vpts = rem.vpts_list[0].PTS;
+ rem.vdts = rem.vpts;
+ rem.vpts_off = rem.vpts;
+ fprintf(stderr,"Video start PTS = %fs \n",rem.vpts_off/90000.);
+ rem.apts = rem.apts_list[0].PTS;
+ rem.apts_off = rem.apts;
+ ptsdiff = rem.vpts - rem.apts;
+ if (ptsdiff > 0) rem.vpts_off -= ptsdiff;
+ else rem.apts_off -= -ptsdiff;
+ fprintf(stderr,"Audio start PTS = %fs\n",rem.apts_off/90000.);
+ fprintf(stderr,"Difference Video - Audio = %fs\n",ptsdiff/90000.);
+ fprintf(stderr,"Time offset = %ds\n",rem.time_off);
+
+ rem.muxr = (rem.video_info.bit_rate +
+ rem.audio_info.bit_rate)/400;
+ fprintf(stderr,"MUXRATE: %.2f Mb/sec\n",rem.muxr/2500.);
+ SCR_inc = 1800 * pack_size / rem.muxr;
+
+ r = 0;
+ while ( rem.vptsn < 2 && !r) r = refill_buffy(&rem);
+ r = 0;
+ while ( rem.aptsn < 2 && !r) r = refill_buffy(&rem);
+
+ //rem.vpts_delay = (uint32_t)(2*90000ULL* (uint64_t)pack_size/rem.muxr);
+ rem.vpts_delay = rem.dts_delay;
+ rem.apts_delay = rem.vpts_delay;
+
+ vbuf_max = 29440;
+ abuf_max = 4096;
+ vbuf = 0;
+ abuf = 0;
+ pos = write_ps_header(buf,rem.SCR,rem.muxr, 1, 0, 0, 1, 1, 1,
+ 0xC0, 0, 32, 0xE0, 1, 230);
+ pos += write_pes_header( PADDING_STREAM, pack_size-pos, 0, buf+pos,0);
+ pos = rem.pack_size;
+ write( fout, buf, pos);
+
+ apos = rem.aread;
+ vpos = rem.vread;
+ print_info( &rem, 1 );
+
+ while( ring_rest(&rem.aud_buffy) && ring_rest(&rem.vid_buffy) ){
+ uint32_t next_apts;
+ uint32_t next_vdts;
+ int asize, vsize;
+
+ r1 = 0;
+ r2 = 0;
+ while ( rem.aframen < 2 && !r1)
+ r1 = refill_buffy(&rem);
+ while ( rem.vframen < 2 && !r2)
+ r2 = refill_buffy(&rem);
+ if (r1 && r2) break;
+
+ if ( !r1 && apos <= rem.aread)
+ apos = rem.aframe_list[1].pos;
+ if ( !r2 && vpos <= rem.vread)
+ vpos = rem.vframe_list[1].pos;
+ apack_size = apos - rem.aread;
+ vpack_size = vpos - rem.vread;
+
+
+ next_vdts = (uint32_t)((uint64_t)rem.vdts + rem.vpts_delay
+ - rem.vpts_off) ;
+ ok_video = ( rem.SCR < next_vdts);
+
+ next_apts = (uint32_t)((uint64_t)rem.apts + rem.apts_delay
+ - rem.apts_off) ;
+ ok_audio = ( rem.SCR < next_apts);
+
+ asize = (apack_size > data_size ? data_size: apack_size);
+ vsize = (vpack_size > data_size ? data_size: vpack_size);
+
+ fprintf(stderr,"vframen: %d aframen: %d v_ok: %d a_ok: %d v_buf: %d a_buf: %d vpacks: %d apacks: %d\n",rem.vframen,rem.aframen, ok_video, ok_audio, (int)vbuf,(int)abuf,vsize, asize);
+
+
+ if( vbuf+vsize < vbuf_max && vsize && ok_audio ){
+ fprintf(stderr,"1 ");
+ pos = write_video_pes( &rem, buf, &vpack_size);
+ write( fout, buf, pos);
+ vbuf += vpack_size;
+ vbufn = add_pts( vbufl, rem.vdts, vpack_size,
+ 0, vbufn, 0);
+ packets++;
+ } else if ( abuf+asize < abuf_max && asize &&
+ ok_video ){
+ fprintf(stderr,"2 ");
+ pos = write_audio_pes( &rem, buf, &apack_size);
+ write( fout, buf, pos);
+ abuf += apack_size;
+ abufn = add_pts( abufl, rem.apts, apack_size,
+ 0, abufn, 0);
+ packets++;
+ } else if ( abuf+asize < abuf_max && asize &&
+ !ok_audio){
+ fprintf(stderr,"3 ");
+ pos = write_audio_pes( &rem, buf, &apack_size);
+ write( fout, buf, pos);
+ abuf += apack_size;
+ abufn = add_pts( abufl, rem.apts, apack_size,
+ 0, abufn, 0);
+ packets++;
+ } else if (vbuf+vsize < vbuf_max && vsize &&
+ !ok_video){
+ fprintf(stderr,"4 ");
+ pos = write_video_pes( &rem, buf, &vpack_size);
+ write( fout, buf, pos);
+ vbuf += vpack_size;
+ vbufn = add_pts( vbufl, rem.vdts, vpack_size,
+ 0, vbufn, 0);
+ packets++;
+ } else {
+ fprintf(stderr,"5 ");
+ pos = write_ps_header(buf,rem.SCR,rem.muxr, 1, 0, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0, 0);
+
+ pos += write_pes_header( PADDING_STREAM, pack_size-pos,
+ 0, buf+pos, 0);
+ write( fout, buf, pos);
+ }
+
+
+ //fprintf(stderr,"vbufn: %d abufn: %d ", vbufn,abufn);
+ //fprintf(stderr,"vbuf: %5d abuf: %4d\n", vbuf,abuf);
+
+ if (rem.SCR > rem.vdts+rem.vpts_off -rem.vpts_delay)
+ rem.SCR = rem.vdts-rem.vpts_off;
+ rem.SCR = (uint32_t)((uint64_t) rem.SCR + SCR_inc);
+
+ if ( rem.apts_off + rem.SCR < rem.apts_delay ) pts_d = 0;
+ else pts_d = (uint64_t) rem.SCR + rem.apts_off - rem.apts_delay;
+ abuf -= del_ptss( abufl, (uint32_t) pts_d, &abufn);
+
+ if ( rem.vpts_off + rem.SCR < rem.vpts_delay ) pts_d = 0;
+ else pts_d = (uint64_t) rem.SCR + rem.vpts_off - rem.vpts_delay;
+ vbuf -= del_ptss( vbufl, (uint32_t) pts_d, &vbufn);
+
+ print_info( &rem, 1);
+ //fprintf(stderr,"vbufn: %d abufn: %d ", vbufn,abufn);
+ //fprintf(stderr,"vbuf: %5d abuf: %4d\n\n", vbuf,abuf);
+
+
+ }
+ pos = write_ps_header(buf,rem.SCR,rem.muxr, 1, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0);
+
+ pos += write_pes_header( PADDING_STREAM, pack_size-pos-4, 0,
+ buf+pos, 0);
+ pos = rem.pack_size-4;
+ write( fout, buf, pos);
+
+ write( fout, mpeg_end, 4);
+ fprintf(stderr,"\ndone\n");
+}
+
+
+typedef
+struct pes_buffer_s{
+ ringbuffy pes_buffy;
+ uint8_t type;
+ PTS_List pts_list[MAX_PTS];
+ FRAME_List frame_list[MAX_FRAME];
+ int pes_size;
+ uint64_t written;
+ uint64_t read;
+} PESBuffer;
+
+
+void init_PESBuffer(PESBuffer *pbuf, int pes_size, int buf_size, uint8_t type)
+{
+ init_framel( pbuf->frame_list);
+ init_ptsl( pbuf->pts_list);
+ ring_init( &pbuf->pes_buffy, buf_size);
+ pbuf->pes_size = pes_size;
+ pbuf->type = type;
+ pbuf->written = 0;
+ pbuf->read = 0;
+}
+
+
+#define MAX_PBUF 4
+
+typedef
+struct remux_s{
+ PESBuffer pbuf_list[MAX_PBUF];
+ int num_pbuf;
+} REMUX;
+
+
+void init_REMUX(REMUX *rem)
+{
+ rem->num_pbuf = 0;
+}
+
+
+
+#define REPACK 2048
+#define ABUF_SIZE REPACK*1024
+#define VBUF_SIZE REPACK*10240
+
+void remux_main(uint8_t *buf, int count, void *pr)
+{
+ int i, b;
+ int bufsize = 0;
+ p2p *p = (p2p *) pr;
+ PESBuffer *pbuf;
+ REMUX *rem = (REMUX *) p->data;
+ uint8_t type = buf[3];
+ int *npbuf = &(rem->num_pbuf);
+
+ switch ( type ){
+ case PRIVATE_STREAM1:
+ bufsize = ABUF_SIZE;
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ if (!bufsize) bufsize = VBUF_SIZE;
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ if (!bufsize) bufsize = ABUF_SIZE;
+ b = -1;
+ for ( i = 0; i < *npbuf; i++){
+ if ( type == rem->pbuf_list[i].type ){
+ b = i;
+ break;
+ }
+ }
+ if (b < 0){
+ if ( *npbuf < MAX_PBUF ){
+ init_PESBuffer(&rem->pbuf_list[*npbuf],
+ p->repack+6, bufsize, type);
+ b = *npbuf;
+ (*npbuf)++;
+ } else {
+ fprintf(stderr,"Not enough PES buffers\n");
+ exit(1);
+ }
+ }
+ break;
+ default:
+ return;
+ }
+
+ pbuf = &(rem->pbuf_list[b]);
+ if (ring_write(&(pbuf->pes_buffy),(char *)buf,count) != count){
+ fprintf(stderr,"buffer overflow type 0x%2x\n",type);
+ exit(1);
+ } else {
+ pbuf->written += count;
+ if ((p->flag2 & PTS_DTS_FLAGS)){
+ uint32_t PTS = trans_pts_dts(p->pts);
+ add_pts(pbuf->pts_list, PTS, pbuf->written,
+ pbuf->written, 0, 0);
+ }
+ p->flag2 = 0;
+ }
+
+}
+
+void output_mux(p2p *p)
+{
+ int i, filling;
+ PESBuffer *pbuf;
+ ringbuffy *pes_buffy;
+ REMUX *rem = (REMUX *) p->data;
+ int repack = p->repack;
+ int npbuf = rem->num_pbuf;
+
+ for ( i = 0; i < npbuf; i++){
+ pbuf = &(rem->pbuf_list[i]);
+ pes_buffy = &pbuf->pes_buffy;
+ filling = pes_buffy->size - ring_rest(pes_buffy);
+ if (filling/(2 *repack)){
+ pbuf->read += ring_read_file(pes_buffy, p->fd1,
+ (filling/repack)*repack);
+ }
+ }
+}
+
+
+
+#define SIZE 32768
+
+void remux2(int fdin, int fdout)
+{
+ p2p p;
+ int count = 1;
+ uint8_t buf[SIZE];
+ uint64_t length = 0;
+ uint64_t l = 0;
+ int verb = 0;
+ REMUX rem;
+
+ init_p2p(&p, remux_main, REPACK);
+ p.fd1 = fdout;
+ p.data = (void *) &rem;
+
+
+ if (fdin != STDIN_FILENO) verb = 1;
+
+ if (verb) {
+ length = lseek(fdin, 0, SEEK_END);
+ lseek(fdin,0,SEEK_SET);
+ }
+
+ while (count > 0){
+ count = read(fdin,buf,SIZE);
+ l += count;
+ if (verb)
+ fprintf(stderr,"Writing %2.2f %%\r",
+ 100.*l/length);
+
+ get_pes(buf,count,&p,pes_repack);
+ output_mux(&p);
+ }
+
+}
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/remux.h b/plugins/streamdev/streamdev-cvs/libdvbmpeg/remux.h
new file mode 100644
index 0000000..76c128b
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/remux.h
@@ -0,0 +1,149 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ *
+ * 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
+ * of the License, 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
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de,
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+//#include <libgen.h>
+#include <stdint.h>
+
+#include "ringbuffy.h"
+#include "ctools.h"
+
+#ifndef _REMUX_H_
+#define _REMUX_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ typedef struct video_i{
+ uint32_t horizontal_size;
+ uint32_t vertical_size ;
+ uint32_t aspect_ratio ;
+ double framerate ;
+ uint32_t video_format;
+ uint32_t bit_rate ;
+ uint32_t comp_bit_rate ;
+ uint32_t vbv_buffer_size;
+ uint32_t CSPF ;
+ uint32_t off;
+ } VideoInfo;
+
+ typedef struct audio_i{
+ int layer;
+ uint32_t bit_rate;
+ uint32_t frequency;
+ uint32_t mode;
+ uint32_t mode_extension;
+ uint32_t emphasis;
+ uint32_t framesize;
+ uint32_t off;
+ } AudioInfo;
+
+
+
+ typedef
+ struct PTS_list_struct{
+ uint32_t PTS;
+ int pos;
+ uint32_t dts;
+ int spos;
+ } PTS_List;
+
+ typedef
+ struct frame_list_struct{
+ int type;
+ int pos;
+ uint32_t FRAME;
+ uint32_t time;
+ uint32_t pts;
+ uint32_t dts;
+ } FRAME_List;
+
+ typedef
+ struct remux_struct{
+ ringbuffy vid_buffy;
+ ringbuffy aud_buffy;
+ PTS_List vpts_list[MAX_PTS];
+ PTS_List apts_list[MAX_PTS];
+ FRAME_List vframe_list[MAX_FRAME];
+ FRAME_List aframe_list[MAX_FRAME];
+ int vptsn;
+ int aptsn;
+ int vframen;
+ int aframen;
+ long apes;
+ long vpes;
+ uint32_t vframe;
+ uint32_t aframe;
+ uint32_t vcframe;
+ uint32_t acframe;
+ uint32_t vpts;
+ uint32_t vdts;
+ uint32_t apts;
+ uint32_t vpts_old;
+ uint32_t apts_old;
+ uint32_t SCR;
+ uint32_t apts_off;
+ uint32_t vpts_off;
+ uint32_t apts_delay;
+ uint32_t vpts_delay;
+ uint32_t dts_delay;
+ AudioInfo audio_info;
+ VideoInfo video_info;
+ int fin;
+ int fout;
+ long int awrite;
+ long int vwrite;
+ long int aread;
+ long int vread;
+ uint32_t group;
+ uint32_t groupframe;
+ uint32_t muxr;
+ int pack_size;
+ uint32_t time_off;
+ } Remux;
+
+ enum { NONE, I_FRAME, P_FRAME, B_FRAME, D_FRAME };
+
+ void remux(int fin, int fout, int pack_size, int mult);
+ void remux2(int fdin, int fdout);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /*_REMUX_H_*/
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/ringbuffy.c b/plugins/streamdev/streamdev-cvs/libdvbmpeg/ringbuffy.c
new file mode 100644
index 0000000..965e49a
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/ringbuffy.c
@@ -0,0 +1,201 @@
+/*
+ Ringbuffer Implementation for gtvscreen
+
+ Copyright (C) 2000 Marcus Metzler (mocm@metzlerbros.de)
+
+ 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 of the License, 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
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "ringbuffy.h"
+#include <string.h>
+
+int ring_init (ringbuffy *rbuf, int size)
+{
+ if (size > 0){
+ rbuf->size = size;
+ if( !(rbuf->buffy = (char *) malloc(sizeof(char)*size)) ){
+ fprintf(stderr,"Not enough memory for ringbuffy\n");
+ return -1;
+ }
+ } else {
+ fprintf(stderr,"Wrong size for ringbuffy\n");
+ return -1;
+ }
+ rbuf->read_pos = 0;
+ rbuf->write_pos = 0;
+ return 0;
+}
+
+
+void ring_destroy(ringbuffy *rbuf)
+{
+ free(rbuf->buffy);
+}
+
+
+int ring_write(ringbuffy *rbuf, char *data, int count)
+{
+
+ int diff, free, pos, rest;
+
+ if (count <=0 ) return 0;
+ pos = rbuf->write_pos;
+ rest = rbuf->size - pos;
+ diff = rbuf->read_pos - pos;
+ free = (diff > 0) ? diff-1 : rbuf->size+diff-1;
+
+ if ( free <= 0 ) return FULL_BUFFER;
+ if ( free < count ) count = free;
+
+ if (count >= rest){
+ memcpy (rbuf->buffy+pos, data, rest);
+ if (count - rest)
+ memcpy (rbuf->buffy, data+rest, count - rest);
+ rbuf->write_pos = count - rest;
+ } else {
+ memcpy (rbuf->buffy+pos, data, count);
+ rbuf->write_pos += count;
+ }
+
+ return count;
+}
+
+
+
+
+int ring_peek(ringbuffy *rbuf, char *data, int count, long off)
+{
+
+ int diff, free, pos, rest;
+
+ if (count <=0 ) return 0;
+ pos = rbuf->read_pos+off;
+ rest = rbuf->size - pos ;
+ diff = rbuf->write_pos - pos;
+ free = (diff >= 0) ? diff : rbuf->size+diff;
+
+ if ( free <= 0 ) return FULL_BUFFER;
+ if ( free < count ) count = free;
+
+ if ( count < rest ){
+ memcpy(data, rbuf->buffy+pos, count);
+ } else {
+ memcpy(data, rbuf->buffy+pos, rest);
+ if ( count - rest)
+ memcpy(data+rest, rbuf->buffy, count - rest);
+ }
+
+ return count;
+}
+
+int ring_read(ringbuffy *rbuf, char *data, int count)
+{
+
+ int diff, free, pos, rest;
+
+ if (count <=0 ) return 0;
+ pos = rbuf->read_pos;
+ rest = rbuf->size - pos;
+ diff = rbuf->write_pos - pos;
+ free = (diff >= 0) ? diff : rbuf->size+diff;
+
+ if ( rest <= 0 ) return 0;
+ if ( free < count ) count = free;
+
+ if ( count < rest ){
+ memcpy(data, rbuf->buffy+pos, count);
+ rbuf->read_pos += count;
+ } else {
+ memcpy(data, rbuf->buffy+pos, rest);
+ if ( count - rest)
+ memcpy(data+rest, rbuf->buffy, count - rest);
+ rbuf->read_pos = count - rest;
+ }
+
+ return count;
+}
+
+
+
+int ring_write_file(ringbuffy *rbuf, int fd, int count)
+{
+
+ int diff, free, pos, rest, rr;
+
+ if (count <=0 ) return 0;
+ pos = rbuf->write_pos;
+ rest = rbuf->size - pos;
+ diff = rbuf->read_pos - pos;
+ free = (diff > 0) ? diff-1 : rbuf->size+diff-1;
+
+ if ( rest <= 0 ) return 0;
+ if ( free < count ) count = free;
+
+ if (count >= rest){
+ rr = read (fd, rbuf->buffy+pos, rest);
+ if (rr == rest && count - rest)
+ rr += read (fd, rbuf->buffy, count - rest);
+ if (rr >=0)
+ rbuf->write_pos = (pos + rr) % rbuf->size;
+ } else {
+ rr = read (fd, rbuf->buffy+pos, count);
+ if (rr >=0)
+ rbuf->write_pos += rr;
+ }
+
+ return rr;
+}
+
+
+
+int ring_read_file(ringbuffy *rbuf, int fd, int count)
+{
+
+ int diff, free, pos, rest, rr;
+
+ if (count <=0 ) return 0;
+ pos = rbuf->read_pos;
+ rest = rbuf->size - pos;
+ diff = rbuf->write_pos - pos;
+ free = (diff >= 0) ? diff : rbuf->size+diff;
+
+ if ( free <= 0 ) return FULL_BUFFER;
+ if ( free < count ) count = free;
+
+ if (count >= rest){
+ rr = write (fd, rbuf->buffy+pos, rest);
+ if (rr == rest && count - rest)
+ rr += write (fd, rbuf->buffy, count - rest);
+ if (rr >=0)
+ rbuf->read_pos = (pos + rr) % rbuf->size;
+ } else {
+ rr = write (fd, rbuf->buffy+pos, count);
+ if (rr >=0)
+ rbuf->read_pos += rr;
+ }
+
+
+ return rr;
+}
+
+int ring_rest(ringbuffy *rbuf){
+ int diff, free, pos, rest;
+ pos = rbuf->read_pos;
+ rest = rbuf->size - pos;
+ diff = rbuf->write_pos - pos;
+ free = (diff >= 0) ? diff : rbuf->size+diff;
+
+ return free;
+}
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/ringbuffy.h b/plugins/streamdev/streamdev-cvs/libdvbmpeg/ringbuffy.h
new file mode 100644
index 0000000..16011d7
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/ringbuffy.h
@@ -0,0 +1,52 @@
+/*
+ Ringbuffer Implementation for gtvscreen
+
+ Copyright (C) 2000 Marcus Metzler (mocm@metzlerbros.de)
+
+ 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 of the License, 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
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef RINGBUFFY_H
+#define RINGBUFFY_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define FULL_BUFFER -1000
+typedef struct ringbuffy{
+ int read_pos;
+ int write_pos;
+ int size;
+ char *buffy;
+} ringbuffy;
+
+int ring_init (ringbuffy *rbuf, int size);
+void ring_destroy(ringbuffy *rbuf);
+int ring_write(ringbuffy *rbuf, char *data, int count);
+int ring_read(ringbuffy *rbuf, char *data, int count);
+int ring_write_file(ringbuffy *rbuf, int fd, int count);
+int ring_read_file(ringbuffy *rbuf, int fd, int count);
+int ring_rest(ringbuffy *rbuf);
+int ring_peek(ringbuffy *rbuf, char *data, int count, long off);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* RINGBUFFY_H */
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/transform.c b/plugins/streamdev/streamdev-cvs/libdvbmpeg/transform.c
new file mode 100644
index 0000000..c53f1fb
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/transform.c
@@ -0,0 +1,2681 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ * 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
+ * of the License, 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
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at marcus@convergence.de,
+
+ * the project's page is at http://linuxtv.org/dvb/
+ */
+
+
+#include "transform.h"
+#include <stdlib.h>
+#include <string.h>
+#include "ctools.h"
+
+static uint8_t tspid0[TS_SIZE] = {
+ 0x47, 0x40, 0x00, 0x10, 0x00, 0x00, 0xb0, 0x11,
+ 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x00, 0xe0,
+ 0x10, 0x00, 0x01, 0xe4, 0x00, 0x2a, 0xd6, 0x1a,
+ 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+
+static uint8_t tspid1[TS_SIZE] = {
+ 0x47, 0x44, 0x00, 0x10, 0x00, 0x02, 0xb0, 0x1c,
+ 0x00, 0x01, 0xcb, 0x00, 0x00, 0xe0, 0xa0, 0xf0,
+ 0x05, 0x48, 0x03, 0x01, 0x00, 0x00, 0x02, 0xe0,
+ 0xa0, 0xf0, 0x00, 0x03, 0xe0, 0x50, 0xf0, 0x00,
+ 0xae, 0xea, 0x4e, 0x48, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+
+// CRC32 lookup table for polynomial 0x04c11db7
+static const uint32_t crc_table[256] = {
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+ 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
+ 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
+ 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
+ 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
+ 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+ 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
+ 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+ 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
+ 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
+ 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+ 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
+ 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+ 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
+ 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
+ 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
+ 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
+ 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+ 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
+ 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+ 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
+ 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
+ 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+ 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
+ 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+ 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+static uint32_t
+calc_crc32 (const uint8_t *sec, uint8_t len)
+{
+ int i;
+ uint32_t crc = 0xffffffff;
+
+ for (i = 0; i < len; i++)
+ crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *sec++) & 0xff];
+
+ return crc;
+}
+
+
+uint64_t trans_pts_dts(uint8_t *pts)
+{
+ uint64_t wts;
+
+ wts = ((uint64_t)((pts[0] & 0x0E) << 5) |
+ ((pts[1] & 0xFC) >> 2)) << 24;
+ wts |= (((pts[1] & 0x03) << 6) |
+ ((pts[2] & 0xFC) >> 2)) << 16;
+ wts |= (((pts[2] & 0x02) << 6) |
+ ((pts[3] & 0xFE) >> 1)) << 8;
+ wts |= (((pts[3] & 0x01) << 7) |
+ ((pts[4] & 0xFE) >> 1));
+ return wts;
+}
+
+
+void get_pespts(uint8_t *av_pts,uint8_t *pts)
+{
+
+ pts[0] = 0x21 |
+ ((av_pts[0] & 0xC0) >>5);
+ pts[1] = ((av_pts[0] & 0x3F) << 2) |
+ ((av_pts[1] & 0xC0) >> 6);
+ pts[2] = 0x01 | ((av_pts[1] & 0x3F) << 2) |
+ ((av_pts[2] & 0x80) >> 6);
+ pts[3] = ((av_pts[2] & 0x7F) << 1) |
+ ((av_pts[3] & 0x80) >> 7);
+ pts[4] = 0x01 | ((av_pts[3] & 0x7F) << 1);
+}
+
+uint16_t get_pid(uint8_t *pid)
+{
+ uint16_t pp = 0;
+
+ pp = (pid[0] & PID_MASK_HI)<<8;
+ pp |= pid[1];
+
+ return pp;
+}
+
+int write_ts_header(uint16_t pid, uint8_t *counter, int pes_start,
+ uint8_t *buf, uint8_t length)
+{
+ int i;
+ int c = 0;
+ int fill;
+ uint8_t tshead[4] = { 0x47, 0x00, 0x00, 0x10};
+
+
+ fill = TS_SIZE-4-length;
+ if (pes_start) tshead[1] = 0x40;
+ if (fill) tshead[3] = 0x30;
+ tshead[1] |= (uint8_t)((pid & 0x1F00) >> 8);
+ tshead[2] |= (uint8_t)(pid & 0x00FF);
+ tshead[3] |= ((*counter)++ & 0x0F) ;
+ memcpy(buf,tshead,4);
+ c+=4;
+
+
+ if (fill){
+ buf[4] = fill-1;
+ c++;
+ if (fill >1){
+ buf[5] = 0x00;
+ c++;
+ }
+ for ( i = 6; i < fill+4; i++){
+ buf[i] = 0xFF;
+ c++;
+ }
+ }
+
+ return c;
+}
+
+
+int write_pes_header(uint8_t id,int length , long PTS, uint8_t *obuf,
+ int stuffing)
+{
+ uint8_t le[2];
+ uint8_t dummy[3];
+ uint8_t *pts;
+ uint8_t ppts[5];
+ long lpts;
+ int c;
+ uint8_t headr[3] = {0x00, 0x00, 0x01};
+
+ lpts = htonl(PTS);
+ pts = (uint8_t *) &lpts;
+
+ get_pespts(pts,ppts);
+
+ c = 0;
+ memcpy(obuf+c,headr,3);
+ c += 3;
+ memcpy(obuf+c,&id,1);
+ c++;
+
+ le[0] = 0;
+ le[1] = 0;
+ length -= 6+stuffing;
+
+ le[0] |= ((uint8_t)(length >> 8) & 0xFF);
+ le[1] |= ((uint8_t)(length) & 0xFF);
+ memcpy(obuf+c,le,2);
+ c += 2;
+
+ if (id == PADDING_STREAM){
+ memset(obuf+c,0xff,length);
+ c+= length;
+ return c;
+ }
+
+ dummy[0] = 0x80;
+ dummy[1] = 0;
+ dummy[2] = 0;
+ if (PTS){
+ dummy[1] |= PTS_ONLY;
+ dummy[2] = 5+stuffing;
+ }
+ memcpy(obuf+c,dummy,3);
+ c += 3;
+ memset(obuf+c,0xFF,stuffing);
+
+ if (PTS){
+ memcpy(obuf+c,ppts,5);
+ c += 5;
+ }
+
+ return c;
+}
+
+
+void init_p2p(p2p *p, void (*func)(uint8_t *buf, int count, void *p),
+ int repack){
+ p->found = 0;
+ p->cid = 0;
+ p->mpeg = 0;
+ memset(p->buf,0,MMAX_PLENGTH);
+ p->done = 0;
+ p->fd1 = -1;
+ p->func = func;
+ p->bigend_repack = 0;
+ p->repack = 0;
+ if ( repack < MAX_PLENGTH && repack > 265 ){
+ p->repack = repack-6;
+ p->bigend_repack = (uint16_t)htons((short)
+ ((repack-6) & 0xFFFF));
+ } else {
+ fprintf(stderr, "Repack size %d is out of range\n",repack);
+ exit(1);
+ }
+}
+
+
+
+void pes_repack(p2p *p)
+{
+ int count = 0;
+ int repack = p->repack;
+ int rest = p->plength;
+ uint8_t buf[MAX_PLENGTH];
+ int bfill = 0;
+ int diff;
+ uint16_t length;
+
+ if (rest < 0) {
+ fprintf(stderr,"Error in repack\n");
+ return;
+ }
+
+ if (!repack){
+ fprintf(stderr,"forgot to set repack size\n");
+ return;
+ }
+
+ if (p->plength == repack){
+ memcpy(p->buf+4,(char *)&p->bigend_repack,2);
+ p->func(p->buf, repack+6, p);
+ return;
+ }
+
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ buf[2] = 0x01;
+ buf[3] = p->cid;
+ memcpy(buf+4,(char *)&p->bigend_repack,2);
+ memset(buf+6,0,MAX_PLENGTH-6);
+
+ if (p->mpeg == 2){
+
+ if ( rest > repack){
+ memcpy(p->buf+4,(char *)&p->bigend_repack,2);
+ p->func(p->buf, repack+6, p);
+ count += repack+6;
+ rest -= repack;
+ } else {
+ memcpy(buf,p->buf,9+p->hlength);
+ bfill = p->hlength;
+ count += 9+p->hlength;
+ rest -= p->hlength+3;
+ }
+
+ while (rest >= repack-3){
+ memset(buf+6,0,MAX_PLENGTH-6);
+ buf[6] = 0x80;
+ buf[7] = 0x00;
+ buf[8] = 0x00;
+ memcpy(buf+9,p->buf+count,repack-3);
+ rest -= repack-3;
+ count += repack-3;
+ p->func(buf, repack+6, p);
+ }
+
+ if (rest){
+ diff = repack - 3 - rest - bfill;
+ if (!bfill){
+ buf[6] = 0x80;
+ buf[7] = 0x00;
+ buf[8] = 0x00;
+ }
+
+ if ( diff < PES_MIN){
+ length = rest+ diff + bfill+3;
+ buf[4] = (uint8_t)((length & 0xFF00) >> 8);
+ buf[5] = (uint8_t)(length & 0x00FF);
+ buf[8] = (uint8_t)(bfill+diff);
+ memset(buf+9+bfill,0xFF,diff);
+ memcpy(buf+9+bfill+diff,p->buf+count,rest);
+ } else {
+ length = rest+ bfill+3;
+ buf[4] = (uint8_t)((length & 0xFF00) >> 8);
+ buf[5] = (uint8_t)(length & 0x00FF);
+ memcpy(buf+9+bfill,p->buf+count,rest);
+ bfill += rest+9;
+ write_pes_header( PADDING_STREAM, diff, 0,
+ buf+bfill, 0);
+ }
+ p->func(buf, repack+6, p);
+ }
+ }
+
+ if (p->mpeg == 1){
+
+ if ( rest > repack){
+ memcpy(p->buf+4,(char *)&p->bigend_repack,2);
+ p->func(p->buf, repack+6, p);
+ count += repack+6;
+ rest -= repack;
+ } else {
+ memcpy(buf,p->buf,6+p->hlength);
+ bfill = p->hlength;
+ count += 6;
+ rest -= p->hlength;
+ }
+
+ while (rest >= repack-1){
+ memset(buf+6,0,MAX_PLENGTH-6);
+ buf[6] = 0x0F;
+ memcpy(buf+7,p->buf+count,repack-1);
+ rest -= repack-1;
+ count += repack-1;
+ p->func(buf, repack+6, p);
+ }
+
+
+ if (rest){
+ diff = repack - 1 - rest - bfill;
+
+ if ( diff < PES_MIN){
+ length = rest+ diff + bfill+1;
+ buf[4] = (uint8_t)((length & 0xFF00) >> 8);
+ buf[5] = (uint8_t)(length & 0x00FF);
+ memset(buf+6,0xFF,diff);
+ if (!bfill){
+ buf[6+diff] = 0x0F;
+ }
+ memcpy(buf+7+diff,p->buf+count,rest+bfill);
+ } else {
+ length = rest+ bfill+1;
+ buf[4] = (uint8_t)((length & 0xFF00) >> 8);
+ buf[5] = (uint8_t)(length & 0x00FF);
+ if (!bfill){
+ buf[6] = 0x0F;
+ memcpy(buf+7,p->buf+count,rest);
+ bfill = rest+7;
+ } else {
+ memcpy(buf+6,p->buf+count,rest+bfill);
+ bfill += rest+6;
+ }
+ write_pes_header( PADDING_STREAM, diff, 0,
+ buf+bfill, 0);
+ }
+ p->func(buf, repack+6, p);
+ }
+ }
+}
+
+
+
+
+
+
+
+
+int filter_pes (uint8_t *buf, int count, p2p *p, int (*func)(p2p *p))
+{
+
+ int l;
+ unsigned short *pl;
+ int c=0;
+ int ret = 1;
+
+ uint8_t headr[3] = { 0x00, 0x00, 0x01} ;
+
+ while (c < count && (p->mpeg == 0 ||
+ (p->mpeg == 1 && p->found < 7) ||
+ (p->mpeg == 2 && p->found < 9))
+ && (p->found < 5 || !p->done)){
+ switch ( p->found ){
+ case 0:
+ case 1:
+ if (buf[c] == 0x00) p->found++;
+ else {
+ if (p->fd1 >= 0)
+ write(p->fd1,buf+c,1);
+ p->found = 0;
+ }
+ c++;
+ break;
+ case 2:
+ if (buf[c] == 0x01) p->found++;
+ else if (buf[c] == 0){
+ p->found = 2;
+ } else {
+ if (p->fd1 >= 0)
+ write(p->fd1,buf+c,1);
+ p->found = 0;
+ }
+ c++;
+ break;
+ case 3:
+ p->cid = 0;
+ switch (buf[c]){
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ if (p->fd1 >= 0)
+ write(p->fd1,buf+c,1);
+ p->done = 1;
+ case PRIVATE_STREAM1:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ p->found++;
+ p->cid = buf[c];
+ c++;
+ break;
+ default:
+ if (p->fd1 >= 0)
+ write(p->fd1,buf+c,1);
+ p->found = 0;
+ break;
+ }
+ break;
+
+
+ case 4:
+ if (count-c > 1){
+ pl = (unsigned short *) (buf+c);
+ p->plength = ntohs(*pl);
+ p->plen[0] = buf[c];
+ c++;
+ p->plen[1] = buf[c];
+ c++;
+ p->found+=2;
+ } else {
+ p->plen[0] = buf[c];
+ p->found++;
+ return 1;
+ }
+ break;
+ case 5:
+ p->plen[1] = buf[c];
+ c++;
+ pl = (unsigned short *) p->plen;
+ p->plength = ntohs(*pl);
+ p->found++;
+ break;
+
+
+ case 6:
+ if (!p->done){
+ p->flag1 = buf[c];
+ c++;
+ p->found++;
+ if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2;
+ else {
+ p->hlength = 0;
+ p->which = 0;
+ p->mpeg = 1;
+ p->flag2 = 0;
+ }
+ }
+ break;
+
+ case 7:
+ if ( !p->done && p->mpeg == 2){
+ p->flag2 = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ case 8:
+ if ( !p->done && p->mpeg == 2){
+ p->hlength = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ default:
+
+ break;
+ }
+ }
+
+ if (!p->plength) p->plength = MMAX_PLENGTH-6;
+
+
+ if ( p->done || ((p->mpeg == 2 && p->found >= 9) ||
+ (p->mpeg == 1 && p->found >= 7)) ){
+ switch (p->cid){
+
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case PRIVATE_STREAM1:
+
+ memcpy(p->buf, headr, 3);
+ p->buf[3] = p->cid;
+ memcpy(p->buf+4,p->plen,2);
+
+ if (p->mpeg == 2 && p->found == 9){
+ p->buf[6] = p->flag1;
+ p->buf[7] = p->flag2;
+ p->buf[8] = p->hlength;
+ }
+
+ if (p->mpeg == 1 && p->found == 7){
+ p->buf[6] = p->flag1;
+ }
+
+
+ if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
+ p->found < 14){
+ while (c < count && p->found < 14){
+ p->pts[p->found-9] = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ }
+ if (c == count) return 1;
+ }
+
+ if (p->mpeg == 1 && p->which < 2000){
+
+ if (p->found == 7) {
+ p->check = p->flag1;
+ p->hlength = 1;
+ }
+
+ while (!p->which && c < count &&
+ p->check == 0xFF){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ }
+
+ if ( c == count) return 1;
+
+ if ( (p->check & 0xC0) == 0x40 && !p->which){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+
+ p->which = 1;
+ if ( c == count) return 1;
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return 1;
+ }
+
+ if (p->which == 1){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return 1;
+ }
+
+ if ( (p->check & 0x30) && p->check != 0xFF){
+ p->flag2 = (p->check & 0xF0) << 2;
+ p->pts[0] = p->check;
+ p->which = 3;
+ }
+
+ if ( c == count) return 1;
+ if (p->which > 2){
+ if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_ONLY){
+ while (c < count &&
+ p->which < 7){
+ p->pts[p->which-2] =
+ buf[c];
+ p->buf[p->found] =
+ buf[c];
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return 1;
+ } else if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_DTS){
+ while (c < count &&
+ p->which< 12){
+ if (p->which< 7)
+ p->pts[p->which
+ -2] =
+ buf[c];
+ p->buf[p->found] =
+ buf[c];
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return 1;
+ }
+ p->which = 2000;
+ }
+
+ }
+
+ while (c < count && p->found < p->plength+6){
+ l = count -c;
+ if (l+p->found > p->plength+6)
+ l = p->plength+6-p->found;
+ memcpy(p->buf+p->found, buf+c, l);
+ p->found += l;
+ c += l;
+ }
+ if(p->found == p->plength+6){
+ if (func(p)){
+ if (p->fd1 >= 0){
+ write(p->fd1,p->buf,
+ p->plength+6);
+ }
+ } else ret = 0;
+ }
+ break;
+ }
+
+
+ if ( p->done ){
+ if( p->found + count - c < p->plength+6){
+ p->found += count-c;
+ c = count;
+ } else {
+ c += p->plength+6 - p->found;
+ p->found = p->plength+6;
+ }
+ }
+
+ if (p->plength && p->found == p->plength+6) {
+ p->found = 0;
+ p->done = 0;
+ p->plength = 0;
+ memset(p->buf, 0, MAX_PLENGTH);
+ if (c < count)
+ return filter_pes(buf+c, count-c, p, func);
+ }
+ }
+ return ret;
+}
+
+
+#define SIZE 4096
+
+
+int audio_pes_filt(p2p *p)
+{
+ uint8_t off;
+
+ switch(p->cid){
+ case PRIVATE_STREAM1:
+ if ( p->cid == p->filter) {
+ off = 9+p->buf[8];
+ if (p->buf[off] == p->subid){
+ return 1;
+ }
+ }
+ break;
+
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ if ( p->cid == p->filter)
+ return 1;
+ break;
+
+ default:
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+
+void filter_audio_from_pes(int fdin, int fdout, uint8_t id, uint8_t subid)
+{
+ p2p p;
+ int count = 1;
+ uint8_t buf[2048];
+
+ init_p2p(&p, NULL, 2048);
+ p.fd1 = -1;
+ p.filter = id;
+ p.subid = subid;
+
+ while (count > 0){
+ count = read(fdin,buf,2048);
+ if(filter_pes(buf,count,&p,audio_pes_filt))
+ write(fdout,buf,2048);
+ }
+}
+
+
+void pes_filt(p2p *p)
+{
+ int factor = p->mpeg-1;
+
+ if ( p->cid == p->filter) {
+ if (p->es)
+ write(p->fd1,p->buf+p->hlength+6+3*factor,
+ p->plength-p->hlength-3*factor);
+ else
+ write(p->fd1,p->buf,p->plength+6);
+ }
+}
+
+void extract_from_pes(int fdin, int fdout, uint8_t id, int es)
+{
+ p2p p;
+ int count = 1;
+ uint8_t buf[SIZE];
+
+ init_p2p(&p, NULL, 2048);
+ p.fd1 = fdout;
+ p.filter = id;
+ p.es = es;
+
+ while (count > 0){
+ count = read(fdin,buf,SIZE);
+ get_pes(buf,count,&p,pes_filt);
+ }
+}
+
+
+void pes_dfilt(p2p *p)
+{
+ int factor = p->mpeg-1;
+ int fd =0;
+ int head=0;
+ int type = NOPES;
+ int streamid;
+ int c = 6+p->hlength+3*factor;
+
+
+ switch ( p->cid ) {
+ case PRIVATE_STREAM1:
+ streamid = p->buf[c];
+ head = 4;
+ if ((streamid & 0xF8) == 0x80+p->es-1){
+ fd = p->fd1;
+ type = AC3;
+ }
+ break;
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ fd = p->fd1;
+ type = AUDIO;
+ break;
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ fd = p->fd2;
+ type = VIDEO;
+ break;
+ }
+
+ if (p->es && !p->startv && type == VIDEO){
+ int found = 0;
+
+ if ( p->flag2 & PTS_DTS )
+ p->vpts = trans_pts_dts(p->pts);
+ else return;
+
+ while ( !found && c+3 < p->plength+6 ){
+ if ( p->buf[c] == 0x00 &&
+ p->buf[c+1] == 0x00 &&
+ p->buf[c+2] == 0x01 &&
+ p->buf[c+3] == 0xb3)
+ found = 1;
+ else c++;
+ }
+ if (found){
+ p->startv = 1;
+ write(fd, p->buf+c, p->plength+6-c);
+ }
+ fd = 0;
+ }
+
+
+ if ( p->es && !p->starta && type == AUDIO){
+ int found = 0;
+ if ( p->flag2 & PTS_DTS )
+ p->apts = trans_pts_dts(p->pts);
+ else return;
+
+ if (p->startv)
+ while ( !found && c+1 < p->plength+6){
+ if ( p->buf[c] == 0xFF &&
+ (p->buf[c+1] & 0xF8) == 0xF8)
+ found = 1;
+ else c++;
+ }
+ if (found){
+ p->starta = 1;
+ write(fd, p->buf+c, p->plength+6-c);
+ }
+ fd = 0;
+ }
+
+ if ( p->es && !p->starta && type == AC3){
+ if ( p->flag2 & PTS_DTS )
+ p->apts = trans_pts_dts(p->pts);
+ else return;
+
+ if (p->startv){
+ c+= ((p->buf[c+2] << 8)| p->buf[c+3]);
+ p->starta = 1;
+ write(fd, p->buf+c, p->plength+6-c);
+ }
+ fd = 0;
+ }
+
+
+ if (fd){
+ if (p->es)
+ write(fd,p->buf+p->hlength+6+3*factor+head,
+ p->plength-p->hlength-3*factor-head);
+ else
+ write(fd,p->buf,p->plength+6);
+ }
+}
+
+int64_t pes_dmx( int fdin, int fdouta, int fdoutv, int es)
+{
+ p2p p;
+ int count = 1;
+ uint8_t buf[SIZE];
+ uint64_t length = 0;
+ uint64_t l = 0;
+ int verb = 0;
+ int percent, oldPercent = -1;
+
+ init_p2p(&p, NULL, 2048);
+ p.fd1 = fdouta;
+ p.fd2 = fdoutv;
+ p.es = es;
+ p.startv = 0;
+ p.starta = 0;
+ p.apts=-1;
+ p.vpts=-1;
+
+ if (fdin != STDIN_FILENO) verb = 1;
+
+ if (verb) {
+ length = lseek(fdin, 0, SEEK_END);
+ lseek(fdin,0,SEEK_SET);
+ }
+
+ while (count > 0){
+ count = read(fdin,buf,SIZE);
+ l += count;
+ if (verb){
+ percent = 100 * l / length;
+
+ if (percent != oldPercent) {
+ fprintf(stderr, "Demuxing %d %%\r", percent);
+ oldPercent = percent;
+ }
+ }
+ get_pes(buf,count,&p,pes_dfilt);
+ }
+
+ return (int64_t)p.vpts - (int64_t)p.apts;
+
+}
+
+
+/* SV: made non-static */
+void pes_in_ts(p2p *p)
+{
+ int l, pes_start;
+ uint8_t obuf[TS_SIZE];
+ long int c = 0;
+ int length = p->plength+6;
+ uint16_t pid;
+ uint8_t *counter;
+ pes_start = 1;
+ switch ( p->cid ) {
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ pid = p->pida;
+ counter = &p->acounter;
+ break;
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ pid = p->pidv;
+ counter = &p->acounter;
+
+ tspid0[3] |= (p->count0++)
+ & 0x0F ;
+ tspid1[3] |= (p->count1++)
+ & 0x0F ;
+
+ tspid1[24] = p->pidv;
+ tspid1[23] |= (p->pidv >> 8) & 0x3F;
+ tspid1[29] = p->pida;
+ tspid1[28] |= (p->pida >> 8) & 0x3F;
+
+ p->func(tspid0,188,p);
+ p->func(tspid1,188,p);
+ break;
+ default:
+ return;
+ }
+
+ while ( c < length ){
+ memset(obuf,0,TS_SIZE);
+ if (length - c >= TS_SIZE-4){
+ l = write_ts_header(pid, counter, pes_start
+ , obuf, TS_SIZE-4);
+ memcpy(obuf+l, p->buf+c, TS_SIZE-l);
+ c += TS_SIZE-l;
+ } else {
+ l = write_ts_header(pid, counter, pes_start
+ , obuf, length-c);
+ memcpy(obuf+l, p->buf+c, TS_SIZE-l);
+ c = length;
+ }
+ p->func(obuf,188,p);
+ pes_start = 0;
+ }
+}
+
+static
+void write_out(uint8_t *buf, int count,void *p)
+{
+ write(STDOUT_FILENO, buf, count);
+}
+
+
+void pes_to_ts2( int fdin, int fdout, uint16_t pida, uint16_t pidv)
+{
+ p2p p;
+ int count = 1;
+ uint8_t buf[SIZE];
+ uint64_t length = 0;
+ uint64_t l = 0;
+ int verb = 0;
+
+ init_p2p(&p, NULL, 2048);
+ p.fd1 = fdout;
+ p.pida = pida;
+ p.pidv = pidv;
+ p.acounter = 0;
+ p.vcounter = 0;
+ p.count1 = 0;
+ p.count0 = 0;
+ p.func = write_out;
+
+ if (fdin != STDIN_FILENO) verb = 1;
+
+ if (verb) {
+ length = lseek(fdin, 0, SEEK_END);
+ lseek(fdin,0,SEEK_SET);
+ }
+
+ while (count > 0){
+ count = read(fdin,buf,SIZE);
+ l += count;
+ if (verb)
+ fprintf(stderr,"Writing TS %2.2f %%\r",
+ 100.*l/length);
+
+ get_pes(buf,count,&p,pes_in_ts);
+ }
+
+}
+
+
+#define IN_SIZE TS_SIZE*10
+void find_avpids(int fd, uint16_t *vpid, uint16_t *apid)
+{
+ uint8_t buf[IN_SIZE];
+ int count;
+ int i;
+ int off =0;
+
+ while ( *apid == 0 || *vpid == 0){
+ count = read(fd, buf, IN_SIZE);
+ for (i = 0; i < count-7; i++){
+ if (buf[i] == 0x47){
+ if (buf[i+1] & 0x40){
+ off = 0;
+ if ( buf[3+i] & 0x20)//adapt field?
+ off = buf[4+i] + 1;
+ switch(buf[i+7+off]){
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ *vpid = get_pid(buf+i+1);
+ break;
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ *apid = get_pid(buf+i+1);
+ break;
+ }
+ }
+ i += 187;
+ }
+ if (*apid != 0 && *vpid != 0) break;
+ }
+ }
+}
+
+void find_bavpids(uint8_t *buf, int count, uint16_t *vpid, uint16_t *apid)
+{
+ int i;
+ int founda = 0;
+ int foundb = 0;
+ int off = 0;
+
+ *vpid = 0;
+ *apid = 0;
+ for (i = 0; i < count-7; i++){
+ if (buf[i] == 0x47){
+ if ((buf[i+1] & 0xF0) == 0x40){
+ off = 0;
+ if ( buf[3+i] & 0x20) // adaptation field?
+ off = buf[4+i] + 1;
+
+ if (buf[off+i+4] == 0x00 &&
+ buf[off+i+5] == 0x00 &&
+ buf[off+i+6] == 0x01){
+ switch(buf[off+i+7]){
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ *vpid = get_pid(buf+i+1);
+ foundb=1;
+ break;
+ case PRIVATE_STREAM1:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ *apid = get_pid(buf+i+1);
+ founda=1;
+ break;
+ }
+ }
+ }
+ i += 187;
+ }
+ if (founda && foundb) break;
+ }
+}
+
+
+void ts_to_pes( int fdin, uint16_t pida, uint16_t pidv, int ps)
+{
+
+ uint8_t buf[IN_SIZE];
+ uint8_t mbuf[TS_SIZE];
+ int i;
+ int count = 1;
+ uint16_t pid;
+ uint16_t dummy;
+ ipack pa, pv;
+ ipack *p;
+
+ if (fdin != STDIN_FILENO && (!pida || !pidv))
+ find_avpids(fdin, &pidv, &pida);
+
+ init_ipack(&pa, IPACKS,write_out, ps);
+ init_ipack(&pv, IPACKS,write_out, ps);
+
+ if ((count = save_read(fdin,mbuf,TS_SIZE))<0)
+ perror("reading");
+
+ for ( i = 0; i < 188 ; i++){
+ if ( mbuf[i] == 0x47 ) break;
+ }
+ if ( i == 188){
+ fprintf(stderr,"Not a TS\n");
+ return;
+ } else {
+ memcpy(buf,mbuf+i,TS_SIZE-i);
+ if ((count = save_read(fdin,mbuf,i))<0)
+ perror("reading");
+ memcpy(buf+TS_SIZE-i,mbuf,i);
+ i = 188;
+ }
+ count = 1;
+ while (count > 0){
+ if ((count = save_read(fdin,buf+i,IN_SIZE-i)+i)<0)
+ perror("reading");
+
+
+ if (!pidv){
+ find_bavpids(buf+i, IN_SIZE-i, &pidv, &dummy);
+ if (pidv) fprintf(stderr, "vpid %d (0x%02x)\n",
+ pidv,pidv);
+ }
+
+ if (!pida){
+ find_bavpids(buf+i, IN_SIZE-i, &dummy, &pida);
+ if (pida) fprintf(stderr, "apid %d (0x%02x)\n",
+ pida,pida);
+ }
+
+
+ for( i = 0; i < count; i+= TS_SIZE){
+ uint8_t off = 0;
+
+ if ( count - i < TS_SIZE) break;
+
+ pid = get_pid(buf+i+1);
+ if (!(buf[3+i]&0x10)) // no payload?
+ continue;
+ if ( buf[1+i]&0x80){
+ fprintf(stderr,"Error in TS for PID: %d\n",
+ pid);
+ }
+ if (pid == pidv){
+ p = &pv;
+ } else {
+ if (pid == pida){
+ p = &pa;
+ } else continue;
+ }
+
+ if ( buf[1+i]&0x40) {
+ if (p->plength == MMAX_PLENGTH-6){
+ p->plength = p->found-6;
+ p->found = 0;
+ send_ipack(p);
+ reset_ipack(p);
+ }
+ }
+
+ if ( buf[3+i] & 0x20) { // adaptation field?
+ off = buf[4+i] + 1;
+ }
+
+ instant_repack(buf+4+off+i, TS_SIZE-4-off, p);
+ }
+ i = 0;
+
+ }
+
+}
+
+
+#define INN_SIZE 2*IN_SIZE
+void insert_pat_pmt( int fdin, int fdout)
+{
+
+ uint8_t buf[INN_SIZE];
+ uint8_t mbuf[TS_SIZE];
+ int i;
+ int count = 1;
+ uint16_t pida = 0;
+ uint16_t pidv = 0;
+ int written,c;
+ uint8_t c0 = 0;
+ uint8_t c1 = 0;
+ uint8_t pmt_len;
+ uint32_t crc32;
+
+
+ find_avpids(fdin, &pidv, &pida);
+
+ count = save_read(fdin,mbuf,TS_SIZE);
+ for ( i = 0; i < 188 ; i++){
+ if ( mbuf[i] == 0x47 ) break;
+ }
+ if ( i == 188){
+ fprintf(stderr,"Not a TS\n");
+ return;
+ } else {
+ memcpy(buf,mbuf+i,TS_SIZE-i);
+ count = save_read(fdin,mbuf,i);
+ memcpy(buf+TS_SIZE-i,mbuf,i);
+ i = 188;
+ }
+
+ count = 1;
+ /* length is not correct, but we only create a very small
+ * PMT, so it doesn't matter :-)
+ */
+ pmt_len = tspid1[7] + 3;
+ while (count > 0){
+ tspid1[24] = pidv;
+ tspid1[23] |= (pidv >> 8) & 0x3F;
+ tspid1[29] = pida;
+ tspid1[28] |= (pida >> 8) & 0x3F;
+ crc32 = calc_crc32 (&tspid1[5], pmt_len - 4);
+ tspid1[5 + pmt_len - 4] = (crc32 & 0xff000000) >> 24;
+ tspid1[5 + pmt_len - 3] = (crc32 & 0x00ff0000) >> 16;
+ tspid1[5 + pmt_len - 2] = (crc32 & 0x0000ff00) >> 8;
+ tspid1[5 + pmt_len - 1] = (crc32 & 0x000000ff) >> 0;
+
+ write(fdout,tspid0,188);
+ write(fdout,tspid1,188);
+
+ count = save_read(fdin,buf+i,INN_SIZE-i);
+
+ written = 0;
+ while (written < IN_SIZE){
+ c = write(fdout,buf,INN_SIZE);
+ if (c>0) written += c;
+ }
+ tspid0[3] &= 0xF0 ;
+ tspid0[3] |= (c0++)& 0x0F ;
+
+ tspid1[3] &= 0xF0 ;
+ tspid1[3] |= (c1++)& 0x0F ;
+
+ i=0;
+ }
+
+}
+
+void get_pes (uint8_t *buf, int count, p2p *p, void (*func)(p2p *p))
+{
+
+ int l;
+ unsigned short *pl;
+ int c=0;
+
+ uint8_t headr[3] = { 0x00, 0x00, 0x01} ;
+
+ while (c < count && (p->mpeg == 0 ||
+ (p->mpeg == 1 && p->found < 7) ||
+ (p->mpeg == 2 && p->found < 9))
+ && (p->found < 5 || !p->done)){
+ switch ( p->found ){
+ case 0:
+ case 1:
+ if (buf[c] == 0x00) p->found++;
+ else p->found = 0;
+ c++;
+ break;
+ case 2:
+ if (buf[c] == 0x01) p->found++;
+ else if (buf[c] == 0){
+ p->found = 2;
+ } else p->found = 0;
+ c++;
+ break;
+ case 3:
+ p->cid = 0;
+ switch (buf[c]){
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ p->done = 1;
+ case PRIVATE_STREAM1:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ p->found++;
+ p->cid = buf[c];
+ c++;
+ break;
+ default:
+ p->found = 0;
+ break;
+ }
+ break;
+
+
+ case 4:
+ if (count-c > 1){
+ pl = (unsigned short *) (buf+c);
+ p->plength = ntohs(*pl);
+ p->plen[0] = buf[c];
+ c++;
+ p->plen[1] = buf[c];
+ c++;
+ p->found+=2;
+ } else {
+ p->plen[0] = buf[c];
+ p->found++;
+ return;
+ }
+ break;
+ case 5:
+ p->plen[1] = buf[c];
+ c++;
+ pl = (unsigned short *) p->plen;
+ p->plength = ntohs(*pl);
+ p->found++;
+ break;
+
+
+ case 6:
+ if (!p->done){
+ p->flag1 = buf[c];
+ c++;
+ p->found++;
+ if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2;
+ else {
+ p->hlength = 0;
+ p->which = 0;
+ p->mpeg = 1;
+ p->flag2 = 0;
+ }
+ }
+ break;
+
+ case 7:
+ if ( !p->done && p->mpeg == 2){
+ p->flag2 = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ case 8:
+ if ( !p->done && p->mpeg == 2){
+ p->hlength = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ default:
+
+ break;
+ }
+ }
+
+ if (!p->plength) p->plength = MMAX_PLENGTH-6;
+
+
+ if ( p->done || ((p->mpeg == 2 && p->found >= 9) ||
+ (p->mpeg == 1 && p->found >= 7)) ){
+ switch (p->cid){
+
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case PRIVATE_STREAM1:
+
+ memcpy(p->buf, headr, 3);
+ p->buf[3] = p->cid;
+ memcpy(p->buf+4,p->plen,2);
+
+ if (p->mpeg == 2 && p->found == 9){
+ p->buf[6] = p->flag1;
+ p->buf[7] = p->flag2;
+ p->buf[8] = p->hlength;
+ }
+
+ if (p->mpeg == 1 && p->found == 7){
+ p->buf[6] = p->flag1;
+ }
+
+
+ if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
+ p->found < 14){
+ while (c < count && p->found < 14){
+ p->pts[p->found-9] = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ }
+ if (c == count) return;
+ }
+
+ if (p->mpeg == 1 && p->which < 2000){
+
+ if (p->found == 7) {
+ p->check = p->flag1;
+ p->hlength = 1;
+ }
+
+ while (!p->which && c < count &&
+ p->check == 0xFF){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ }
+
+ if ( c == count) return;
+
+ if ( (p->check & 0xC0) == 0x40 && !p->which){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+
+ p->which = 1;
+ if ( c == count) return;
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return;
+ }
+
+ if (p->which == 1){
+ p->check = buf[c];
+ p->buf[p->found] = buf[c];
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return;
+ }
+
+ if ( (p->check & 0x30) && p->check != 0xFF){
+ p->flag2 = (p->check & 0xF0) << 2;
+ p->pts[0] = p->check;
+ p->which = 3;
+ }
+
+ if ( c == count) return;
+ if (p->which > 2){
+ if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_ONLY){
+ while (c < count &&
+ p->which < 7){
+ p->pts[p->which-2] =
+ buf[c];
+ p->buf[p->found] =
+ buf[c];
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return;
+ } else if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_DTS){
+ while (c < count &&
+ p->which< 12){
+ if (p->which< 7)
+ p->pts[p->which
+ -2] =
+ buf[c];
+ p->buf[p->found] =
+ buf[c];
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return;
+ }
+ p->which = 2000;
+ }
+
+ }
+
+ while (c < count && p->found < p->plength+6){
+ l = count -c;
+ if (l+p->found > p->plength+6)
+ l = p->plength+6-p->found;
+ memcpy(p->buf+p->found, buf+c, l);
+ p->found += l;
+ c += l;
+ }
+ if(p->found == p->plength+6)
+ func(p);
+
+ break;
+ }
+
+
+ if ( p->done ){
+ if( p->found + count - c < p->plength+6){
+ p->found += count-c;
+ c = count;
+ } else {
+ c += p->plength+6 - p->found;
+ p->found = p->plength+6;
+ }
+ }
+
+ if (p->plength && p->found == p->plength+6) {
+ p->found = 0;
+ p->done = 0;
+ p->plength = 0;
+ memset(p->buf, 0, MAX_PLENGTH);
+ if (c < count)
+ get_pes(buf+c, count-c, p, func);
+ }
+ }
+ return;
+}
+
+
+
+
+void setup_pes2ts( p2p *p, uint32_t pida, uint32_t pidv,
+ void (*ts_write)(uint8_t *buf, int count, void *p))
+{
+ init_p2p( p, ts_write, 2048);
+ p->pida = pida;
+ p->pidv = pidv;
+ p->acounter = 0;
+ p->vcounter = 0;
+ p->count1 = 0;
+ p->count0 = 0;
+}
+
+void kpes_to_ts( p2p *p,uint8_t *buf ,int count )
+{
+ get_pes(buf,count, p,pes_in_ts);
+}
+
+
+void setup_ts2pes( p2p *pa, p2p *pv, uint32_t pida, uint32_t pidv,
+ void (*pes_write)(uint8_t *buf, int count, void *p))
+{
+ init_p2p( pa, pes_write, 2048);
+ init_p2p( pv, pes_write, 2048);
+ pa->pid = pida;
+ pv->pid = pidv;
+}
+
+void kts_to_pes( p2p *p, uint8_t *buf) // don't need count (=188)
+{
+ uint8_t off = 0;
+ uint16_t pid = 0;
+
+ if (!(buf[3]&PAYLOAD)) // no payload?
+ return;
+
+ pid = get_pid(buf+1);
+
+ if (pid != p->pid) return;
+ if ( buf[1]&0x80){
+ fprintf(stderr,"Error in TS for PID: %d\n",
+ pid);
+ }
+
+ if ( buf[1]&PAY_START) {
+ if (p->plength == MMAX_PLENGTH-6){
+ p->plength = p->found-6;
+ p->found = 0;
+ pes_repack(p);
+ }
+ }
+
+ if ( buf[3] & ADAPT_FIELD) { // adaptation field?
+ off = buf[4] + 1;
+ if (off+4 > 187) return;
+ }
+
+ get_pes(buf+4+off, TS_SIZE-4-off, p , pes_repack);
+}
+
+
+
+
+// instant repack
+
+
+void reset_ipack(ipack *p)
+{
+ p->found = 0;
+ p->cid = 0;
+ p->plength = 0;
+ p->flag1 = 0;
+ p->flag2 = 0;
+ p->hlength = 0;
+ p->mpeg = 0;
+ p->check = 0;
+ p->which = 0;
+ p->done = 0;
+ p->count = 0;
+ p->size = p->size_orig;
+}
+
+void init_ipack(ipack *p, int size,
+ void (*func)(uint8_t *buf, int size, void *priv), int ps)
+{
+ if ( !(p->buf = malloc(size)) ){
+ fprintf(stderr,"Couldn't allocate memory for ipack\n");
+ exit(1);
+ }
+ p->ps = ps;
+ p->size_orig = size;
+ p->func = func;
+ reset_ipack(p);
+ p->has_ai = 0;
+ p->has_vi = 0;
+ p->start = 0;
+}
+
+void free_ipack(ipack * p)
+{
+ if (p->buf) free(p->buf);
+}
+
+
+
+int get_vinfo(uint8_t *mbuf, int count, VideoInfo *vi, int pr)
+{
+ uint8_t *headr;
+ int found = 0;
+ int sw;
+ int form = -1;
+ int c = 0;
+
+ while (found < 4 && c+4 < count){
+ uint8_t *b;
+
+ b = mbuf+c;
+ if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01
+ && b[3] == 0xb3) found = 4;
+ else {
+ c++;
+ }
+ }
+
+ if (! found) return -1;
+ c += 4;
+ if (c+12 >= count) return -1;
+ headr = mbuf+c;
+
+ vi->horizontal_size = ((headr[1] &0xF0) >> 4) | (headr[0] << 4);
+ vi->vertical_size = ((headr[1] &0x0F) << 8) | (headr[2]);
+
+ sw = (int)((headr[3]&0xF0) >> 4) ;
+
+ switch( sw ){
+ case 1:
+ if (pr)
+ fprintf(stderr,"Videostream: ASPECT: 1:1");
+ vi->aspect_ratio = 100;
+ break;
+ case 2:
+ if (pr)
+ fprintf(stderr,"Videostream: ASPECT: 4:3");
+ vi->aspect_ratio = 133;
+ break;
+ case 3:
+ if (pr)
+ fprintf(stderr,"Videostream: ASPECT: 16:9");
+ vi->aspect_ratio = 177;
+ break;
+ case 4:
+ if (pr)
+ fprintf(stderr,"Videostream: ASPECT: 2.21:1");
+ vi->aspect_ratio = 221;
+ break;
+
+ case 5 ... 15:
+ if (pr)
+ fprintf(stderr,"Videostream: ASPECT: reserved");
+ vi->aspect_ratio = 0;
+ break;
+
+ default:
+ vi->aspect_ratio = 0;
+ return -1;
+ }
+
+ if (pr)
+ fprintf(stderr," Size = %dx%d",vi->horizontal_size,
+ vi->vertical_size);
+
+ sw = (int)(headr[3]&0x0F);
+
+ switch ( sw ) {
+ case 1:
+ if (pr)
+ fprintf(stderr," FRate: 23.976 fps");
+ vi->framerate = 24000/1001.;
+ form = -1;
+ break;
+ case 2:
+ if (pr)
+ fprintf(stderr," FRate: 24 fps");
+ vi->framerate = 24;
+ form = -1;
+ break;
+ case 3:
+ if (pr)
+ fprintf(stderr," FRate: 25 fps");
+ vi->framerate = 25;
+ form = VIDEO_MODE_PAL;
+ break;
+ case 4:
+ if (pr)
+ fprintf(stderr," FRate: 29.97 fps");
+ vi->framerate = 30000/1001.;
+ form = VIDEO_MODE_NTSC;
+ break;
+ case 5:
+ if (pr)
+ fprintf(stderr," FRate: 30 fps");
+ vi->framerate = 30;
+ form = VIDEO_MODE_NTSC;
+ break;
+ case 6:
+ if (pr)
+ fprintf(stderr," FRate: 50 fps");
+ vi->framerate = 50;
+ form = VIDEO_MODE_PAL;
+ break;
+ case 7:
+ if (pr)
+ fprintf(stderr," FRate: 60 fps");
+ vi->framerate = 60;
+ form = VIDEO_MODE_NTSC;
+ break;
+ }
+
+ vi->bit_rate = 400*(((headr[4] << 10) & 0x0003FC00UL)
+ | ((headr[5] << 2) & 0x000003FCUL) |
+ (((headr[6] & 0xC0) >> 6) & 0x00000003UL));
+
+ if (pr){
+ fprintf(stderr," BRate: %.2f Mbit/s",(vi->bit_rate)/1000000.);
+ fprintf(stderr,"\n");
+ }
+ vi->video_format = form;
+
+ vi->off = c-4;
+ return c-4;
+}
+
+extern unsigned int bitrates[3][16];
+extern uint32_t freq[4];
+
+int get_ainfo(uint8_t *mbuf, int count, AudioInfo *ai, int pr)
+{
+ uint8_t *headr;
+ int found = 0;
+ int c = 0;
+ int fr =0;
+
+ while (!found && c < count){
+ uint8_t *b = mbuf+c;
+
+ if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8)
+ found = 1;
+ else {
+ c++;
+ }
+ }
+
+ if (!found) return -1;
+
+ if (c+3 >= count) return -1;
+ headr = mbuf+c;
+
+ ai->layer = (headr[1] & 0x06) >> 1;
+
+ if (pr)
+ fprintf(stderr,"Audiostream: Layer: %d", 4-ai->layer);
+
+
+ ai->bit_rate = bitrates[(3-ai->layer)][(headr[2] >> 4 )]*1000;
+
+ if (pr){
+ if (ai->bit_rate == 0)
+ fprintf (stderr," Bit rate: free");
+ else if (ai->bit_rate == 0xf)
+ fprintf (stderr," BRate: reserved");
+ else
+ fprintf (stderr," BRate: %d kb/s", ai->bit_rate/1000);
+ }
+
+ fr = (headr[2] & 0x0c ) >> 2;
+ ai->frequency = freq[fr]*100;
+
+ if (pr){
+ if (ai->frequency == 3)
+ fprintf (stderr, " Freq: reserved\n");
+ else
+ fprintf (stderr," Freq: %2.1f kHz\n",
+ ai->frequency/1000.);
+ }
+ ai->off = c;
+ return c;
+}
+
+unsigned int ac3_bitrates[32] =
+ {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+uint32_t ac3_freq[4] = {480, 441, 320, 0};
+uint32_t ac3_frames[3][32] =
+ {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024,
+ 1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114,
+ 1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ {96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344,
+ 1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+
+int get_ac3info(uint8_t *mbuf, int count, AudioInfo *ai, int pr)
+{
+ uint8_t *headr;
+ int found = 0;
+ int c = 0;
+ uint8_t frame;
+ int fr = 0;
+
+ while ( !found && c < count){
+ uint8_t *b = mbuf+c;
+ if ( b[0] == 0x0b && b[1] == 0x77 )
+ found = 1;
+ else {
+ c++;
+ }
+ }
+
+
+ if (!found){
+ return -1;
+ }
+ ai->off = c;
+
+ if (c+5 >= count) return -1;
+
+ ai->layer = 0; // 0 for AC3
+ headr = mbuf+c+2;
+
+ frame = (headr[2]&0x3f);
+ ai->bit_rate = ac3_bitrates[frame>>1]*1000;
+
+ if (pr) fprintf (stderr," BRate: %d kb/s", ai->bit_rate/1000);
+
+ fr = (headr[2] & 0xc0 ) >> 6;
+ ai->frequency = freq[fr]*100;
+ if (pr) fprintf (stderr," Freq: %d Hz\n", ai->frequency);
+
+ ai->framesize = ac3_frames[fr][frame >> 1];
+ if ((frame & 1) && (fr == 1)) ai->framesize++;
+ ai->framesize = ai->framesize << 1;
+ if (pr) fprintf (stderr," Framesize %d\n", ai->framesize);
+
+ return c;
+}
+
+
+void ps_pes(ipack *p)
+{
+ int check;
+ uint8_t pbuf[PS_HEADER_L2];
+ static int muxr = 0;
+ static int ai = 0;
+ static int vi = 0;
+ static int start = 0;
+ static uint32_t SCR = 0;
+
+ if (p->mpeg == 2){
+ switch(p->buf[3]){
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ if (!p->has_vi){
+ if(get_vinfo(p->buf, p->count, &p->vi,1) >=0) {
+ p->has_vi = 1;
+ vi = p->vi.bit_rate;
+ }
+ }
+ break;
+
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ if (!p->has_ai){
+ if(get_ainfo(p->buf, p->count, &p->ai,1) >=0) {
+ p->has_ai = 1;
+ ai = p->ai.bit_rate;
+ }
+ }
+ break;
+ }
+
+ if (p->has_vi && vi && !muxr){
+ muxr = (vi+ai)/400;
+ }
+
+ if ( start && muxr && (p->buf[7] & PTS_ONLY) && (p->has_ai ||
+ p->buf[9+p->buf[8]+4] == 0xb3)){
+ SCR = trans_pts_dts(p->pts)-3600;
+
+ check = write_ps_header(pbuf,
+ SCR,
+ muxr, 1, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0);
+
+ p->func(pbuf, check , p->data);
+ }
+
+ if (muxr && !start && vi){
+ SCR = trans_pts_dts(p->pts)-3600;
+ check = write_ps_header(pbuf,
+ SCR,
+ muxr, 1, 0, 0, 1, 1, 1,
+ 0xC0, 0, 64, 0xE0, 1, 460);
+ start = 1;
+ p->func(pbuf, check , p->data);
+ }
+
+ if (start)
+ p->func(p->buf, p->count, p->data);
+ }
+}
+
+void send_ipack(ipack *p)
+{
+ int streamid=0;
+ int off;
+ int ac3_off = 0;
+ AudioInfo ai;
+ int nframes= 0;
+ int f=0;
+
+ if (p->count < 10) return;
+ p->buf[3] = p->cid;
+ p->buf[4] = (uint8_t)(((p->count-6) & 0xFF00) >> 8);
+ p->buf[5] = (uint8_t)((p->count-6) & 0x00FF);
+
+
+ if (p->cid == PRIVATE_STREAM1){
+
+ off = 9+p->buf[8];
+ streamid = p->buf[off];
+ if ((streamid & 0xF8) == 0x80){
+ ai.off = 0;
+ ac3_off = ((p->buf[off+2] << 8)| p->buf[off+3]);
+ if (ac3_off < p->count)
+ f=get_ac3info(p->buf+off+3+ac3_off,
+ p->count-ac3_off, &ai,0);
+ if ( !f ){
+ nframes = (p->count-off-3-ac3_off)/
+ ai.framesize + 1;
+ p->buf[off+1] = nframes;
+ p->buf[off+2] = (ac3_off >> 8)& 0xFF;
+ p->buf[off+3] = (ac3_off)& 0xFF;
+
+ ac3_off += nframes * ai.framesize - p->count;
+ }
+ }
+ }
+
+ if (p->ps) ps_pes(p);
+ else p->func(p->buf, p->count, p->data);
+
+ switch ( p->mpeg ){
+ case 2:
+
+ p->buf[6] = 0x80;
+ p->buf[7] = 0x00;
+ p->buf[8] = 0x00;
+ p->count = 9;
+
+ if (p->cid == PRIVATE_STREAM1 && (streamid & 0xF8)==0x80 ){
+ p->count += 4;
+ p->buf[9] = streamid;
+ p->buf[10] = 0;
+ p->buf[11] = (ac3_off >> 8)& 0xFF;
+ p->buf[12] = (ac3_off)& 0xFF;
+ }
+
+ break;
+ case 1:
+ p->buf[6] = 0x0F;
+ p->count = 7;
+ break;
+ }
+
+}
+
+
+static void write_ipack(ipack *p, uint8_t *data, int count)
+{
+ AudioInfo ai;
+ uint8_t headr[3] = { 0x00, 0x00, 0x01} ;
+ int diff =0;
+
+ if (p->count < 6){
+ if (trans_pts_dts(p->pts) > trans_pts_dts(p->last_pts))
+ memcpy(p->last_pts, p->pts, 5);
+ p->count = 0;
+ memcpy(p->buf+p->count, headr, 3);
+ p->count += 6;
+ }
+ if ( p->size == p->size_orig && p->plength &&
+ (diff = 6+p->plength - p->found + p->count +count) > p->size &&
+ diff < 3*p->size/2){
+
+ p->size = diff/2;
+// fprintf(stderr,"size: %d \n",p->size);
+ }
+
+ if (p->cid == PRIVATE_STREAM1 && p->count == p->hlength+9){
+ if ((data[0] & 0xF8) != 0x80){
+ int ac3_off;
+
+ ac3_off = get_ac3info(data, count, &ai,0);
+ if (ac3_off>=0 && ai.framesize){
+ p->buf[p->count] = 0x80;
+ p->buf[p->count+1] = (p->size - p->count
+ - 4 - ac3_off)/
+ ai.framesize + 1;
+ p->buf[p->count+2] = (ac3_off >> 8)& 0xFF;
+ p->buf[p->count+3] = (ac3_off)& 0xFF;
+ p->count+=4;
+
+ }
+ }
+ }
+
+ if (p->count + count < p->size){
+ memcpy(p->buf+p->count, data, count);
+ p->count += count;
+ } else {
+ int rest = p->size - p->count;
+ if (rest < 0) rest = 0;
+ memcpy(p->buf+p->count, data, rest);
+ p->count += rest;
+// fprintf(stderr,"count: %d \n",p->count);
+ send_ipack(p);
+ if (count - rest > 0)
+ write_ipack(p, data+rest, count-rest);
+ }
+}
+
+void instant_repack (uint8_t *buf, int count, ipack *p)
+{
+
+ int l;
+ unsigned short *pl;
+ int c=0;
+
+ while (c < count && (p->mpeg == 0 ||
+ (p->mpeg == 1 && p->found < 7) ||
+ (p->mpeg == 2 && p->found < 9))
+ && (p->found < 5 || !p->done)){
+ switch ( p->found ){
+ case 0:
+ case 1:
+ if (buf[c] == 0x00) p->found++;
+ else p->found = 0;
+ c++;
+ break;
+ case 2:
+ if (buf[c] == 0x01) p->found++;
+ else if (buf[c] == 0){
+ p->found = 2;
+ } else p->found = 0;
+ c++;
+ break;
+ case 3:
+ p->cid = 0;
+ switch (buf[c]){
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ p->done = 1;
+ case PRIVATE_STREAM1:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ p->found++;
+ p->cid = buf[c];
+ c++;
+ break;
+ default:
+ p->found = 0;
+ break;
+ }
+ break;
+
+
+ case 4:
+ if (count-c > 1){
+ pl = (unsigned short *) (buf+c);
+ p->plength = ntohs(*pl);
+ p->plen[0] = buf[c];
+ c++;
+ p->plen[1] = buf[c];
+ c++;
+ p->found+=2;
+ } else {
+ p->plen[0] = buf[c];
+ p->found++;
+ return;
+ }
+ break;
+ case 5:
+ p->plen[1] = buf[c];
+ c++;
+ pl = (unsigned short *) p->plen;
+ p->plength = ntohs(*pl);
+ p->found++;
+ break;
+
+
+ case 6:
+ if (!p->done){
+ p->flag1 = buf[c];
+ c++;
+ p->found++;
+ if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2;
+ else {
+ p->hlength = 0;
+ p->which = 0;
+ p->mpeg = 1;
+ p->flag2 = 0;
+ }
+ }
+ break;
+
+ case 7:
+ if ( !p->done && p->mpeg == 2){
+ p->flag2 = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ case 8:
+ if ( !p->done && p->mpeg == 2){
+ p->hlength = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ default:
+
+ break;
+ }
+ }
+
+
+ if (c == count) return;
+
+ if (!p->plength) p->plength = MMAX_PLENGTH-6;
+
+
+ if ( p->done || ((p->mpeg == 2 && p->found >= 9) ||
+ (p->mpeg == 1 && p->found >= 7)) ){
+ switch (p->cid){
+
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case PRIVATE_STREAM1:
+
+ if (p->mpeg == 2 && p->found == 9){
+ write_ipack(p, &p->flag1, 1);
+ write_ipack(p, &p->flag2, 1);
+ write_ipack(p, &p->hlength, 1);
+ }
+
+ if (p->mpeg == 1 && p->found == 7){
+ write_ipack(p, &p->flag1, 1);
+ }
+
+
+ if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
+ p->found < 14){
+ while (c < count && p->found < 14){
+ p->pts[p->found-9] = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ }
+ if (c == count) return;
+ }
+
+ if (p->mpeg == 1 && p->which < 2000){
+
+ if (p->found == 7) {
+ p->check = p->flag1;
+ p->hlength = 1;
+ }
+
+ while (!p->which && c < count &&
+ p->check == 0xFF){
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+ }
+
+ if ( c == count) return;
+
+ if ( (p->check & 0xC0) == 0x40 && !p->which){
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+
+ p->which = 1;
+ if ( c == count) return;
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return;
+ }
+
+ if (p->which == 1){
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return;
+ }
+
+ if ( (p->check & 0x30) && p->check != 0xFF){
+ p->flag2 = (p->check & 0xF0) << 2;
+ p->pts[0] = p->check;
+ p->which = 3;
+ }
+
+ if ( c == count) return;
+ if (p->which > 2){
+ if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_ONLY){
+ while (c < count &&
+ p->which < 7){
+ p->pts[p->which-2] =
+ buf[c];
+ write_ipack(p,buf+c,1);
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return;
+ } else if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_DTS){
+ while (c < count &&
+ p->which< 12){
+ if (p->which< 7)
+ p->pts[p->which
+ -2] =
+ buf[c];
+ write_ipack(p,buf+c,1);
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return;
+ }
+ p->which = 2000;
+ }
+
+ }
+
+ while (c < count && p->found < p->plength+6){
+ l = count -c;
+ if (l+p->found > p->plength+6)
+ l = p->plength+6-p->found;
+ write_ipack(p, buf+c, l);
+ p->found += l;
+ c += l;
+ }
+
+ break;
+ }
+
+
+ if ( p->done ){
+ if( p->found + count - c < p->plength+6){
+ p->found += count-c;
+ c = count;
+ } else {
+ c += p->plength+6 - p->found;
+ p->found = p->plength+6;
+ }
+ }
+
+ if (p->plength && p->found == p->plength+6) {
+ send_ipack(p);
+ reset_ipack(p);
+ if (c < count)
+ instant_repack(buf+c, count-c, p);
+ }
+ }
+ return;
+}
+
+void write_out_es(uint8_t *buf, int count,void *priv)
+{
+ ipack *p = (ipack *) priv;
+ uint8_t payl = buf[8]+9+p->start-1;
+
+ write(p->fd, buf+payl, count-payl);
+ p->start = 1;
+}
+
+void write_out_pes(uint8_t *buf, int count,void *priv)
+{
+ ipack *p = (ipack *) priv;
+ write(p->fd, buf, count);
+}
+
+
+
+int64_t ts_demux(int fdin, int fdv_out,int fda_out,uint16_t pida,
+ uint16_t pidv, int es)
+{
+ uint8_t buf[IN_SIZE];
+ uint8_t mbuf[TS_SIZE];
+ int i;
+ int count = 1;
+ uint16_t pid;
+ ipack pa, pv;
+ ipack *p;
+ uint8_t *sb;
+ int64_t apts=0;
+ int64_t vpts=0;
+ int verb = 0;
+ uint64_t length =0;
+ uint64_t l=0;
+ int perc =0;
+ int last_perc =0;
+
+ if (fdin != STDIN_FILENO) verb = 1;
+
+ if (verb) {
+ length = lseek(fdin, 0, SEEK_END);
+ lseek(fdin,0,SEEK_SET);
+ }
+
+ if (!pida || !pidv)
+ find_avpids(fdin, &pidv, &pida);
+
+ if (es){
+ init_ipack(&pa, IPACKS,write_out_es, 0);
+ init_ipack(&pv, IPACKS,write_out_es, 0);
+ } else {
+ init_ipack(&pa, IPACKS,write_out_pes, 0);
+ init_ipack(&pv, IPACKS,write_out_pes, 0);
+ }
+ pa.fd = fda_out;
+ pv.fd = fdv_out;
+ pa.data = (void *)&pa;
+ pv.data = (void *)&pv;
+
+ count = save_read(fdin,mbuf,TS_SIZE);
+ if (count) l+=count;
+ for ( i = 0; i < 188 ; i++){
+ if ( mbuf[i] == 0x47 ) break;
+ }
+ if ( i == 188){
+ fprintf(stderr,"Not a TS\n");
+ return 0;
+ } else {
+ memcpy(buf,mbuf+i,TS_SIZE-i);
+ count = save_read(fdin,mbuf,i);
+ if (count) l+=count;
+ memcpy(buf+TS_SIZE-i,mbuf,i);
+ i = 188;
+ }
+
+ count = 1;
+ while (count > 0){
+ count = save_read(fdin,buf+i,IN_SIZE-i)+i;
+ if (count) l+=count;
+ if (verb && perc >last_perc){
+ perc = (100*l)/length;
+ fprintf(stderr,"Reading TS %d %%\r",perc);
+ last_perc = perc;
+ }
+
+ for( i = 0; i < count; i+= TS_SIZE){
+ uint8_t off = 0;
+
+ if ( count - i < TS_SIZE) break;
+
+ pid = get_pid(buf+i+1);
+ if (!(buf[3+i]&0x10)) // no payload?
+ continue;
+ if ( buf[1+i]&0x80){
+ fprintf(stderr,"Error in TS for PID: %d\n",
+ pid);
+ }
+ if (pid == pidv){
+ p = &pv;
+ } else {
+ if (pid == pida){
+ p = &pa;
+ } else continue;
+ }
+
+ if ( buf[3+i] & 0x20) { // adaptation field?
+ off = buf[4+i] + 1;
+ }
+
+ if ( buf[1+i]&0x40) {
+ if (p->plength == MMAX_PLENGTH-6){
+ p->plength = p->found-6;
+ p->found = 0;
+ send_ipack(p);
+ reset_ipack(p);
+ }
+ sb = buf+4+off+i;
+ if( es &&
+ !p->start && (sb[7] & PTS_DTS_FLAGS)){
+ uint8_t *pay = sb+sb[8]+9;
+ int l = TS_SIZE - 13 - off - sb[8];
+ if ( pid == pidv &&
+ (p->start =
+ get_vinfo( pay, l,&p->vi,1)+1) >0
+ ){
+ vpts = trans_pts_dts(sb+9);
+ printf("vpts : %fs\n",
+ vpts/90000.);
+ }
+ if ( pid == pida && es==1 &&
+ (p->start =
+ get_ainfo( pay, l,&p->ai,1)+1) >0
+ ){
+ apts = trans_pts_dts(sb+9);
+ printf("apts : %fs\n",
+ apts/90000.);
+ }
+ if ( pid == pida && es==2 &&
+ (p->start =
+ get_ac3info( pay, l,&p->ai,1)+1) >0
+ ){
+ apts = trans_pts_dts(sb+9);
+ printf("apts : %fs\n",
+ apts/90000.);
+ }
+ }
+ }
+
+ if (p->start)
+ instant_repack(buf+4+off+i, TS_SIZE-4-off, p);
+ }
+ i = 0;
+
+ }
+
+ return (vpts-apts);
+}
+
+void ts2es_opt(int fdin, uint16_t pidv, ipack *p, int verb)
+{
+ uint8_t buf[IN_SIZE];
+ uint8_t mbuf[TS_SIZE];
+ int i;
+ int count = 1;
+ uint64_t length =0;
+ uint64_t l=0;
+ int perc =0;
+ int last_perc =0;
+ uint16_t pid;
+
+ if (verb) {
+ length = lseek(fdin, 0, SEEK_END);
+ lseek(fdin,0,SEEK_SET);
+ }
+
+ count = save_read(fdin,mbuf,TS_SIZE);
+ if (count) l+=count;
+ for ( i = 0; i < 188 ; i++){
+ if ( mbuf[i] == 0x47 ) break;
+ }
+ if ( i == 188){
+ fprintf(stderr,"Not a TS\n");
+ return;
+ } else {
+ memcpy(buf,mbuf+i,TS_SIZE-i);
+ count = save_read(fdin,mbuf,i);
+ if (count) l+=count;
+ memcpy(buf+TS_SIZE-i,mbuf,i);
+ i = 188;
+ }
+
+ count = 1;
+ while (count > 0){
+ count = save_read(fdin,buf+i,IN_SIZE-i)+i;
+ if (count) l+=count;
+ if (verb && perc >last_perc){
+ perc = (100*l)/length;
+ fprintf(stderr,"Reading TS %d %%\r",perc);
+ last_perc = perc;
+ }
+
+ for( i = 0; i < count; i+= TS_SIZE){
+ uint8_t off = 0;
+
+ if ( count - i < TS_SIZE) break;
+
+ pid = get_pid(buf+i+1);
+ if (!(buf[3+i]&0x10)) // no payload?
+ continue;
+ if ( buf[1+i]&0x80){
+ fprintf(stderr,"Error in TS for PID: %d\n",
+ pid);
+ }
+ if (pid != pidv){
+ continue;
+ }
+
+ if ( buf[3+i] & 0x20) { // adaptation field?
+ off = buf[4+i] + 1;
+ }
+
+ if ( buf[1+i]&0x40) {
+ if (p->plength == MMAX_PLENGTH-6){
+ p->plength = p->found-6;
+ p->found = 0;
+ send_ipack(p);
+ reset_ipack(p);
+ }
+ }
+
+ instant_repack(buf+4+off+i, TS_SIZE-4-off, p);
+ }
+ i = 0;
+
+ }
+}
+
+void ts2es(int fdin, uint16_t pidv)
+{
+ ipack p;
+ int verb = 0;
+
+ init_ipack(&p, IPACKS,write_out_es, 0);
+ p.fd = STDOUT_FILENO;
+ p.data = (void *)&p;
+
+ if (fdin != STDIN_FILENO) verb = 1;
+
+ ts2es_opt(fdin, pidv, &p, verb);
+}
+
+
+void change_aspect(int fdin, int fdout, int aspect)
+{
+ ps_packet ps;
+ pes_packet pes;
+ int neof,i;
+
+ do {
+ init_ps(&ps);
+ neof = read_ps(fdin,&ps);
+ write_ps(fdout,&ps);
+ for (i = 0; i < ps.npes; i++){
+ uint8_t *buf;
+ int c = 0;
+ int l;
+
+ init_pes(&pes);
+ read_pes(fdin, &pes);
+
+ buf = pes.pes_pckt_data;
+
+ switch (pes.stream_id){
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ l=pes.length;
+ break;
+ default:
+ l = 0;
+ break;
+ }
+ while ( c < l - 6){
+ if (buf[c] == 0x00 &&
+ buf[c+1] == 0x00 &&
+ buf[c+2] == 0x01 &&
+ buf[c+3] == 0xB3) {
+ c += 4;
+ buf[c+3] &= 0x0f;
+ buf[c+3] |= aspect;
+ }
+ else c++;
+ }
+ write_pes(fdout,&pes);
+ }
+ } while( neof > 0 );
+}
diff --git a/plugins/streamdev/streamdev-cvs/libdvbmpeg/transform.h b/plugins/streamdev/streamdev-cvs/libdvbmpeg/transform.h
new file mode 100644
index 0000000..c65fa0c
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/libdvbmpeg/transform.h
@@ -0,0 +1,250 @@
+/*
+ * dvb-mpegtools for the Siemens Fujitsu DVB PCI card
+ *
+ * Copyright (C) 2000, 2001 Marcus Metzler
+ * for convergence integrated media GmbH
+ * Copyright (C) 2002 Marcus Metzler
+ *
+ * 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
+ * of the License, 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
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+
+ * The author can be reached at mocm@metzlerbros.de,
+
+ */
+
+#ifndef _TS_TRANSFORM_H_
+#define _TS_TRANSFORM_H_
+
+#include <stdint.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "remux.h"
+
+#define PROG_STREAM_MAP 0xBC
+#ifndef PRIVATE_STREAM1
+#define PRIVATE_STREAM1 0xBD
+#endif
+#define PADDING_STREAM 0xBE
+#ifndef PRIVATE_STREAM2
+#define PRIVATE_STREAM2 0xBF
+#endif
+#define AUDIO_STREAM_S 0xC0
+#define AUDIO_STREAM_E 0xDF
+#define VIDEO_STREAM_S 0xE0
+#define VIDEO_STREAM_E 0xEF
+#define ECM_STREAM 0xF0
+#define EMM_STREAM 0xF1
+#define DSM_CC_STREAM 0xF2
+#define ISO13522_STREAM 0xF3
+#define PROG_STREAM_DIR 0xFF
+
+#define BUFFYSIZE 10*MAX_PLENGTH
+#define MAX_PTS 8192
+#define MAX_FRAME 8192
+#define MAX_PACK_L 4096
+#define PS_HEADER_L1 14
+#define PS_HEADER_L2 (PS_HEADER_L1+18)
+#define MAX_H_SIZE (PES_H_MIN + PS_HEADER_L1 + 5)
+#define PES_MIN 7
+#define PES_H_MIN 9
+
+//flags2
+#define PTS_DTS_FLAGS 0xC0
+#define ESCR_FLAG 0x20
+#define ES_RATE_FLAG 0x10
+#define DSM_TRICK_FLAG 0x08
+#define ADD_CPY_FLAG 0x04
+#define PES_CRC_FLAG 0x02
+#define PES_EXT_FLAG 0x01
+
+//pts_dts flags
+#define PTS_ONLY 0x80
+#define PTS_DTS 0xC0
+
+#define TS_SIZE 188
+#define TRANS_ERROR 0x80
+#define PAY_START 0x40
+#define TRANS_PRIO 0x20
+#define PID_MASK_HI 0x1F
+//flags
+#define TRANS_SCRMBL1 0x80
+#define TRANS_SCRMBL2 0x40
+#define ADAPT_FIELD 0x20
+#define PAYLOAD 0x10
+#define COUNT_MASK 0x0F
+
+// adaptation flags
+#define DISCON_IND 0x80
+#define RAND_ACC_IND 0x40
+#define ES_PRI_IND 0x20
+#define PCR_FLAG 0x10
+#define OPCR_FLAG 0x08
+#define SPLICE_FLAG 0x04
+#define TRANS_PRIV 0x02
+#define ADAP_EXT_FLAG 0x01
+
+// adaptation extension flags
+#define LTW_FLAG 0x80
+#define PIECE_RATE 0x40
+#define SEAM_SPLICE 0x20
+
+
+#define MAX_PLENGTH 0xFFFF
+#define MMAX_PLENGTH (64*MAX_PLENGTH)
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define P2P_LENGTH 2048
+
+ enum{NOPES, AUDIO, VIDEO, AC3};
+
+ typedef struct p2pstruct {
+ int found;
+ uint8_t buf[MMAX_PLENGTH];
+ uint8_t cid;
+ uint8_t subid;
+ uint32_t plength;
+ uint8_t plen[2];
+ uint8_t flag1;
+ uint8_t flag2;
+ uint8_t hlength;
+ uint8_t pts[5];
+ int mpeg;
+ uint8_t check;
+ int fd1;
+ int fd2;
+ int es;
+ int filter;
+ int which;
+ int done;
+ int repack;
+ uint16_t bigend_repack;
+ void (*func)(uint8_t *buf, int count, void *p);
+ int startv;
+ int starta;
+ int64_t apts;
+ int64_t vpts;
+ uint16_t pid;
+ uint16_t pida;
+ uint16_t pidv;
+ uint8_t acounter;
+ uint8_t vcounter;
+ uint8_t count0;
+ uint8_t count1;
+ void *data;
+ } p2p;
+
+
+ uint64_t trans_pts_dts(uint8_t *pts);
+ int write_ts_header(uint16_t pid, uint8_t *counter, int pes_start,
+ uint8_t *buf, uint8_t length);
+ uint16_t get_pid(uint8_t *pid);
+ void init_p2p(p2p *p, void (*func)(uint8_t *buf, int count, void *p),
+ int repack);
+ void get_pes (uint8_t *buf, int count, p2p *p, void (*func)(p2p *p));
+ void get_pes (uint8_t *buf, int count, p2p *p, void (*func)(p2p *p));
+ void pes_repack(p2p *p);
+ void setup_pes2ts( p2p *p, uint32_t pida, uint32_t pidv,
+ void (*ts_write)(uint8_t *buf, int count, void *p));
+ void kpes_to_ts( p2p *p,uint8_t *buf ,int count );
+ void setup_ts2pes( p2p *pa, p2p *pv, uint32_t pida, uint32_t pidv,
+ void (*pes_write)(uint8_t *buf, int count, void *p));
+ void kts_to_pes( p2p *p, uint8_t *buf);
+ void pes_repack(p2p *p);
+ void extract_from_pes(int fdin, int fdout, uint8_t id, int es);
+ int64_t pes_dmx(int fdin, int fdouta, int fdoutv, int es);
+ void pes_to_ts2( int fdin, int fdout, uint16_t pida, uint16_t pidv);
+ void ts_to_pes( int fdin, uint16_t pida, uint16_t pidv, int pad);
+ int get_ainfo(uint8_t *mbuf, int count, AudioInfo *ai, int pr);
+ int get_vinfo(uint8_t *mbuf, int count, VideoInfo *vi, int pr);
+ int get_ac3info(uint8_t *mbuf, int count, AudioInfo *ai, int pr);
+ void filter_audio_from_pes(int fdin, int fdout, uint8_t id,
+ uint8_t subid);
+
+
+//instant repack
+
+ typedef struct ipack_s {
+ int size;
+ int size_orig;
+ int found;
+ int ps;
+ int has_ai;
+ int has_vi;
+ AudioInfo ai;
+ VideoInfo vi;
+ uint8_t *buf;
+ uint8_t cid;
+ uint32_t plength;
+ uint8_t plen[2];
+ uint8_t flag1;
+ uint8_t flag2;
+ uint8_t hlength;
+ uint8_t pts[5];
+ uint8_t last_pts[5];
+ int mpeg;
+ uint8_t check;
+ int which;
+ int done;
+ void *data;
+ void *data2;
+ void (*func)(uint8_t *buf, int size, void *priv);
+ int count;
+ int start;
+ int fd;
+ int fd1;
+ int fd2;
+ int ffd;
+ int playing;
+ } ipack;
+
+ void instant_repack (uint8_t *buf, int count, ipack *p);
+ void init_ipack(ipack *p, int size,
+ void (*func)(uint8_t *buf, int size, void *priv),
+ int pad);
+ void free_ipack(ipack * p);
+ void send_ipack(ipack *p);
+ void reset_ipack(ipack *p);
+ void ps_pes(ipack *p);
+ // use with ipack structure, repack size and callback func
+
+ int64_t ts_demux(int fd_in, int fdv_out,int fda_out,uint16_t pida,
+ uint16_t pidv, int es);
+
+ void ts2es(int fdin, uint16_t pidv);
+ void ts2es_opt(int fdin, uint16_t pidv, ipack *p, int verb);
+ void insert_pat_pmt( int fdin, int fdout);
+ void change_aspect(int fdin, int fdout, int aspect);
+
+// SV: all made non-static:
+ void pes_in_ts(p2p *p);
+
+// SV: moved from .c file:
+#define IPACKS 2048
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _TS_TRANSFORM_H_*/
+
+
+
diff --git a/plugins/streamdev/streamdev-cvs/patches/CVS/Entries b/plugins/streamdev/streamdev-cvs/patches/CVS/Entries
new file mode 100644
index 0000000..820adef
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/patches/CVS/Entries
@@ -0,0 +1,7 @@
+/vdr-1.4.3-recursion.diff/1.1/Thu Jan 11 11:48:23 2007//
+/vdr-1.4.3-recursion_bigpatch.diff/1.1/Thu Jan 11 11:48:23 2007//
+/vdr-1.4.x-localchannelprovide.diff/1.1/Thu Jan 11 11:44:01 2007//
+/vdr-1.6.0-ignore_missing_cam.diff/1.1/Tue Apr 8 14:18:17 2008//
+/vdr-1.6.0-intcamdevices.patch/1.1/Thu Oct 2 07:14:48 2008//
+/vdr-cap_net_raw.diff/1.1/Fri Feb 13 10:39:21 2009//
+D
diff --git a/plugins/streamdev/streamdev-cvs/patches/CVS/Repository b/plugins/streamdev/streamdev-cvs/patches/CVS/Repository
new file mode 100644
index 0000000..b4967d6
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/patches/CVS/Repository
@@ -0,0 +1 @@
+streamdev/patches
diff --git a/plugins/streamdev/streamdev-cvs/patches/CVS/Root b/plugins/streamdev/streamdev-cvs/patches/CVS/Root
new file mode 100644
index 0000000..2c7f6ce
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/patches/CVS/Root
@@ -0,0 +1 @@
+:pserver:anoncvs@vdr-developer.org:/var/cvsroot
diff --git a/plugins/streamdev/streamdev-cvs/patches/vdr-1.4.3-recursion.diff b/plugins/streamdev/streamdev-cvs/patches/vdr-1.4.3-recursion.diff
new file mode 100644
index 0000000..7a06c92
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/patches/vdr-1.4.3-recursion.diff
@@ -0,0 +1,85 @@
+# If you have two or more VDRs and you like them to mutually share
+# there DVB cards you might need to apply this patch first.
+#
+# IMPORTANT: As this patch does not only modify streamdev-server but
+# also an exported method of VDR, you will need to
+#
+# !!!!! RECOMPILE VDR AND ALL PLUGINS !!!!!
+#
+# Why do I need the patch?
+# --------------------------
+# Before switching channels VDR will consider all of its devices to
+# find the one with the least impact. This includes the device provided
+# by the streamdev-client plugin. Streamdev-client will forward the
+# request to its server which in turn checks all of its devices. Now if
+# the server is running streamdev-client, too, the request will again
+# be forwarded to its server and finally you will endup in a loop.
+#
+# What does the patch do?
+# -----------------------
+# The patch adds the additional parameter "bool DVBCardsOnly" to VDR's
+# device selection method cDevice::GetDevice(...). The parameter
+# defaults to false which gives you the standard behaviour of GetDevice.
+# When set to true, GetDevice will use only those devices with a card
+# index < MAXDVBDEVICES, so only real DVB cards will be considered.
+# Other devices like streamdev-client or DVB cards provided by plugin
+# (Hauppauge PVR) won't be used.
+#
+# Author: Frank Schmirler (http://vdr.schmirler.de)
+#
+--- device.h.orig 2006-11-15 12:01:34.000000000 +0100
++++ device.h 2006-11-15 12:02:15.000000000 +0100
+@@ -128,7 +128,7 @@
+ ///< Gets the device with the given Index.
+ ///< \param Index must be in the range 0..numDevices-1.
+ ///< \return A pointer to the device, or NULL if the Index was invalid.
+- static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
++ static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL, bool DVBCardsOnly = false);
+ ///< Returns a device that is able to receive the given Channel at the
+ ///< given Priority, with the least impact on active recordings and
+ ///< live viewing.
+--- device.c.orig 2006-11-15 12:01:30.000000000 +0100
++++ device.c 2006-11-22 12:28:05.000000000 +0100
+@@ -8,6 +8,7 @@
+ */
+
+ #include "device.h"
++#include "dvbdevice.h"
+ #include <errno.h>
+ #include <sys/ioctl.h>
+ #include <sys/mman.h>
+@@ -278,11 +279,13 @@
+ return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
+ }
+
+-cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
++cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers, bool DVBCardsOnly)
+ {
+ cDevice *d = NULL;
+ uint Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
+ for (int i = 0; i < numDevices; i++) {
++ if (DVBCardsOnly && device[i]->CardIndex() >= MAXDVBDEVICES)
++ continue;
+ bool ndr;
+ if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
+ // Put together an integer number that reflects the "impact" using
+--- PLUGINS/src/streamdev/server/connection.c.orig 2006-11-15 12:10:11.000000000 +0100
++++ PLUGINS/src/streamdev/server/connection.c 2006-11-15 12:10:59.000000000 +0100
+@@ -132,7 +132,7 @@
+ Dprintf(" * GetDevice(const cChannel*, int)\n");
+ Dprintf(" * -------------------------------\n");
+
+- device = cDevice::GetDevice(Channel, Priority);
++ device = cDevice::GetDevice(Channel, Priority, NULL, true);
+
+ Dprintf(" * Found following device: %p (%d)\n", device,
+ device ? device->CardIndex() + 1 : 0);
+@@ -150,7 +150,7 @@
+ const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
+ isyslog("streamdev-server: Detaching current receiver");
+ Detach();
+- device = cDevice::GetDevice(Channel, Priority);
++ device = cDevice::GetDevice(Channel, Priority, NULL, true);
+ Attach();
+ Dprintf(" * Found following device: %p (%d)\n", device,
+ device ? device->CardIndex() + 1 : 0);
diff --git a/plugins/streamdev/streamdev-cvs/patches/vdr-1.4.3-recursion_bigpatch.diff b/plugins/streamdev/streamdev-cvs/patches/vdr-1.4.3-recursion_bigpatch.diff
new file mode 100644
index 0000000..5bb7854
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/patches/vdr-1.4.3-recursion_bigpatch.diff
@@ -0,0 +1,88 @@
+# If you have two or more VDRs and you like them to mutually share
+# there DVB cards you might need to apply this patch first.
+#
+# This is a modified version of the patch for VDRs with BIGPATCH.
+# Thanks to p_body@vdrportal.
+#
+# IMPORTANT: As this patch does not only modify streamdev-server but
+# also an exported method of VDR, you will need to
+#
+# !!!!! RECOMPILE VDR AND ALL PLUGINS !!!!!
+#
+# Why do I need the patch?
+# --------------------------
+# Before switching channels VDR will consider all of its devices to
+# find the one with the least impact. This includes the device provided
+# by the streamdev-client plugin. Streamdev-client will forward the
+# request to its server which in turn checks all of its devices. Now if
+# the server is running streamdev-client, too, the request will again
+# be forwarded to its server and finally you will endup in a loop.
+#
+# What does the patch do?
+# -----------------------
+# The patch adds the additional parameter "bool DVBCardsOnly" to VDR's
+# device selection method cDevice::GetDevice(...). The parameter
+# defaults to false which gives you the standard behaviour of GetDevice.
+# When set to true, GetDevice will use only those devices with a card
+# index < MAXDVBDEVICES, so only real DVB cards will be considered.
+# Other devices like streamdev-client or DVB cards provided by plugin
+# (Hauppauge PVR) won't be used.
+#
+# Author: Frank Schmirler (http://vdr.schmirler.de)
+#
+--- device.h.orig 2006-11-15 12:01:34.000000000 +0100
++++ device.h 2006-11-15 12:02:15.000000000 +0100
+@@ -128,7 +128,7 @@
+ ///< Gets the device with the given Index.
+ ///< \param Index must be in the range 0..numDevices-1.
+ ///< \return A pointer to the device, or NULL if the Index was invalid.
+- static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL, bool LiveView = false);
++ static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL, bool LiveView = false, bool DVBCardsOnly = false);
+ ///< Returns a device that is able to receive the given Channel at the
+ ///< given Priority, with the least impact on active recordings and
+ ///< live viewing.
+--- device.c.orig 2006-11-15 12:01:30.000000000 +0100
++++ device.c 2006-11-22 12:28:05.000000000 +0100
+@@ -8,6 +8,7 @@
+ */
+
+ #include "device.h"
++#include "dvbdevice.h"
+ #include <errno.h>
+ #include <sys/ioctl.h>
+ #include <sys/mman.h>
+@@ -278,11 +279,13 @@
+ return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
+ }
+
+-cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers, bool LiveView)
++cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers, bool LiveView, bool DVBCardsOnly)
+ {
+ cDevice *d = NULL;
+ uint Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
+ for (int i = 0; i < numDevices; i++) {
++ if (DVBCardsOnly && device[i]->CardIndex() >= MAXDVBDEVICES)
++ continue;
+ bool ndr;
+ if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
+ // Put together an integer number that reflects the "impact" using
+--- PLUGINS/src/streamdev/server/connection.c.orig 2006-11-15 12:10:11.000000000 +0100
++++ PLUGINS/src/streamdev/server/connection.c 2006-11-15 12:10:59.000000000 +0100
+@@ -132,7 +132,7 @@
+ Dprintf(" * GetDevice(const cChannel*, int)\n");
+ Dprintf(" * -------------------------------\n");
+
+- device = cDevice::GetDevice(Channel, Priority);
++ device = cDevice::GetDevice(Channel, Priority, NULL, NULL, true);
+
+ Dprintf(" * Found following device: %p (%d)\n", device,
+ device ? device->CardIndex() + 1 : 0);
+@@ -150,7 +150,7 @@
+ const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
+ isyslog("streamdev-server: Detaching current receiver");
+ Detach();
+- device = cDevice::GetDevice(Channel, Priority);
++ device = cDevice::GetDevice(Channel, Priority, NULL, NULL, true);
+ Attach();
+ Dprintf(" * Found following device: %p (%d)\n", device,
+ device ? device->CardIndex() + 1 : 0);
diff --git a/plugins/streamdev/streamdev-cvs/patches/vdr-1.4.x-localchannelprovide.diff b/plugins/streamdev/streamdev-cvs/patches/vdr-1.4.x-localchannelprovide.diff
new file mode 100644
index 0000000..857c1a2
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/patches/vdr-1.4.x-localchannelprovide.diff
@@ -0,0 +1,102 @@
+# Apply this patch to VDR if you want to use a fullfeatured DVB card
+# as pure output device. Infact the patch will keep VDR from using the
+# tuner of any local DVB card (also budget cards). It will not affect
+# other input devices like e.g. streamdev-client or DVB cards provided
+# by plugins (e.g. Hauppauge PVR).
+#
+# By default the patch is DISABLED. There will be a new OSD menu entry
+# in Setup->DVB which allows you to enable or disable the patch at any
+# time.
+diff -ru vdr-1.4.3.orig/config.c vdr-1.4.3/config.c
+--- vdr-1.4.3.orig/config.c 2006-07-22 13:57:51.000000000 +0200
++++ vdr-1.4.3/config.c 2006-11-16 08:16:37.000000000 +0100
+@@ -273,6 +273,7 @@
+ CurrentChannel = -1;
+ CurrentVolume = MAXVOLUME;
+ CurrentDolby = 0;
++ LocalChannelProvide = 1;
+ InitialChannel = 0;
+ InitialVolume = -1;
+ }
+@@ -434,6 +435,7 @@
+ else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
+ else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value);
+ else if (!strcasecmp(Name, "CurrentDolby")) CurrentDolby = atoi(Value);
++ else if (!strcasecmp(Name, "LocalChannelProvide")) LocalChannelProvide = atoi(Value);
+ else if (!strcasecmp(Name, "InitialChannel")) InitialChannel = atoi(Value);
+ else if (!strcasecmp(Name, "InitialVolume")) InitialVolume = atoi(Value);
+ else
+@@ -502,6 +504,7 @@
+ Store("CurrentChannel", CurrentChannel);
+ Store("CurrentVolume", CurrentVolume);
+ Store("CurrentDolby", CurrentDolby);
++ Store("LocalChannelProvide",LocalChannelProvide);
+ Store("InitialChannel", InitialChannel);
+ Store("InitialVolume", InitialVolume);
+
+diff -ru vdr-1.4.3.orig/config.h vdr-1.4.3/config.h
+--- vdr-1.4.3.orig/config.h 2006-09-23 15:56:08.000000000 +0200
++++ vdr-1.4.3/config.h 2006-11-16 08:16:57.000000000 +0100
+@@ -250,6 +250,7 @@
+ int CurrentChannel;
+ int CurrentVolume;
+ int CurrentDolby;
++ int LocalChannelProvide;
+ int InitialChannel;
+ int InitialVolume;
+ int __EndData__;
+diff -ru vdr-1.4.3.orig/dvbdevice.c vdr-1.4.3/dvbdevice.c
+--- vdr-1.4.3.orig/dvbdevice.c 2006-08-14 11:38:32.000000000 +0200
++++ vdr-1.4.3/dvbdevice.c 2006-11-16 08:17:58.000000000 +0100
+@@ -766,6 +766,8 @@
+
+ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
+ {
++ if (Setup.LocalChannelProvide != 1)
++ return false;
+ bool result = false;
+ bool hasPriority = Priority < 0 || Priority > this->Priority();
+ bool needsDetachReceivers = false;
+diff -ru vdr-1.4.3.orig/i18n.c vdr-1.4.3/i18n.c
+--- vdr-1.4.3.orig/i18n.c 2006-09-16 11:08:30.000000000 +0200
++++ vdr-1.4.3/i18n.c 2006-11-16 08:36:53.000000000 +0100
+@@ -3546,6 +3546,28 @@
+ "Foretrukket sprog",
+ "Preferovaný jazyk",
+ },
++ { "Setup.DVB$Use DVB receivers",
++ "DVB Empfangsteile benutzen",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++ },
+ { "Setup.DVB$Primary DVB interface",
+ "Primäres DVB-Interface",
+ "Primarna naprava",
+diff -ru vdr-1.4.3.orig/menu.c vdr-1.4.3/menu.c
+--- vdr-1.4.3.orig/menu.c 2006-07-23 11:23:11.000000000 +0200
++++ vdr-1.4.3/menu.c 2006-11-16 08:37:27.000000000 +0100
+@@ -2354,6 +2354,7 @@
+
+ Clear();
+
++ Add(new cMenuEditBoolItem(tr("Setup.DVB$Use DVB receivers"), &data.LocalChannelProvide));
+ Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
+ Add(new cMenuEditBoolItem(tr("Setup.DVB$Video format"), &data.VideoFormat, "4:3", "16:9"));
+ if (data.VideoFormat == 0)
diff --git a/plugins/streamdev/streamdev-cvs/patches/vdr-1.6.0-ignore_missing_cam.diff b/plugins/streamdev/streamdev-cvs/patches/vdr-1.6.0-ignore_missing_cam.diff
new file mode 100644
index 0000000..60d93bd
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/patches/vdr-1.6.0-ignore_missing_cam.diff
@@ -0,0 +1,13 @@
+--- device.c.orig 2008-03-28 11:47:25.000000000 +0100
++++ device.c 2008-03-28 11:47:09.000000000 +0100
+@@ -375,8 +375,8 @@
+ }
+ }
+ }
+- if (!NumUsableSlots)
+- return NULL; // no CAM is able to decrypt this channel
++// if (!NumUsableSlots)
++// return NULL; // no CAM is able to decrypt this channel
+ }
+
+ bool NeedsDetachReceivers = false;
diff --git a/plugins/streamdev/streamdev-cvs/patches/vdr-1.6.0-intcamdevices.patch b/plugins/streamdev/streamdev-cvs/patches/vdr-1.6.0-intcamdevices.patch
new file mode 100644
index 0000000..aab1fb4
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/patches/vdr-1.6.0-intcamdevices.patch
@@ -0,0 +1,78 @@
+Index: vdr-1.6.0-nocamdevices/device.c
+===================================================================
+--- vdr-1.6.0-nocamdevices/device.c
++++ vdr-1.6.0-nocamdevices/device.c 2008-04-27 18:55:37.000000000 +0300
+@@ -363,6 +363,7 @@
+ int NumCamSlots = CamSlots.Count();
+ int SlotPriority[NumCamSlots];
+ int NumUsableSlots = 0;
++ bool InternalCamNeeded = false;
+ if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
+ for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
+ SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
+@@ -376,7 +377,7 @@
+ }
+ }
+ if (!NumUsableSlots)
+- return NULL; // no CAM is able to decrypt this channel
++ InternalCamNeeded = true; // no CAM is able to decrypt this channel
+ }
+
+ bool NeedsDetachReceivers = false;
+@@ -392,11 +393,13 @@
+ continue; // this device shall be temporarily avoided
+ if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device[i]->CardIndex() + 1)
+ continue; // a specific card was requested, but not this one
+- if (NumUsableSlots && !CamSlots.Get(j)->Assign(device[i], true))
++ if (InternalCamNeeded && !device[i]->HasInternalCam())
++ continue; // no CAM is able to decrypt this channel and the device uses vdr handled CAMs
++ if (NumUsableSlots && !device[i]->HasInternalCam() && !CamSlots.Get(j)->Assign(device[i], true))
+ continue; // CAM slot can't be used with this device
+ bool ndr;
+ if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
+- if (NumUsableSlots && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
++ if (NumUsableSlots && !device[i]->HasInternalCam() && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
+ ndr = true; // using a different CAM slot requires detaching receivers
+ // Put together an integer number that reflects the "impact" using
+ // this device would have on the overall system. Each condition is represented
+@@ -410,18 +413,18 @@
+ imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
+ imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
+ imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
+- imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
++ imp <<= 8; imp |= min(max(((NumUsableSlots && !device[i]->HasInternalCam()) ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
+ imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
+ imp <<= 1; imp |= device[i]->IsPrimaryDevice(); // avoid the primary device
+- imp <<= 1; imp |= NumUsableSlots ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels
++ imp <<= 1; imp |= (NumUsableSlots || InternalCamNeeded) ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels
+ imp <<= 1; imp |= device[i]->HasDecoder(); // avoid full featured cards
+- imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
++ imp <<= 1; imp |= (NumUsableSlots && !device[i]->HasInternalCam()) ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
+ if (imp < Impact) {
+ // This device has less impact than any previous one, so we take it.
+ Impact = imp;
+ d = device[i];
+ NeedsDetachReceivers = ndr;
+- if (NumUsableSlots)
++ if (NumUsableSlots && !device[i]->HasInternalCam())
+ s = CamSlots.Get(j);
+ }
+ }
+Index: vdr-1.6.0-nocamdevices/device.h
+===================================================================
+--- vdr-1.6.0-nocamdevices/device.h
++++ vdr-1.6.0-nocamdevices/device.h 2008-04-27 18:55:49.000000000 +0300
+@@ -335,6 +335,12 @@
+ public:
+ virtual bool HasCi(void);
+ ///< Returns true if this device has a Common Interface.
++ virtual bool HasInternalCam(void) { return false; }
++ ///< Returns true if this device handles encrypted channels itself
++ ///< without VDR assistance. This can be e.g. when the device is a
++ ///< client that gets the stream from another VDR instance that has
++ ///< already decrypted the stream. In this case ProvidesChannel()
++ ///< shall check whether the channel can be decrypted.
+ void SetCamSlot(cCamSlot *CamSlot);
+ ///< Sets the given CamSlot to be used with this device.
+ cCamSlot *CamSlot(void) const { return camSlot; }
+
diff --git a/plugins/streamdev/streamdev-cvs/patches/vdr-cap_net_raw.diff b/plugins/streamdev/streamdev-cvs/patches/vdr-cap_net_raw.diff
new file mode 100644
index 0000000..2f714b1
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/patches/vdr-cap_net_raw.diff
@@ -0,0 +1,11 @@
+--- vdr.c.orig 2009-02-13 09:45:55.000000000 +0100
++++ vdr.c 2009-02-13 09:46:24.000000000 +0100
+@@ -115,7 +115,7 @@
+ static bool SetCapSysTime(void)
+ {
+ // drop all capabilities except cap_sys_time
+- cap_t caps = cap_from_text("= cap_sys_time=ep");
++ cap_t caps = cap_from_text("= cap_sys_time,cap_net_raw=ep");
+ if (!caps) {
+ fprintf(stderr, "vdr: cap_from_text failed: %s\n", strerror(errno));
+ return false;
diff --git a/plugins/streamdev/streamdev-cvs/po/CVS/Entries b/plugins/streamdev/streamdev-cvs/po/CVS/Entries
new file mode 100644
index 0000000..2b4c7f3
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/po/CVS/Entries
@@ -0,0 +1,8 @@
+/de_DE.po/1.5/Fri Feb 13 10:39:21 2009//
+/fi_FI.po/1.5/Tue Jul 7 10:47:36 2009//
+/fr_FR.po/1.4/Fri Feb 13 10:39:21 2009//
+/it_IT.po/1.3/Fri Feb 13 10:39:21 2009//
+/lt_LT.po/1.1/Tue Dec 15 13:38:29 2009//
+/ru_RU.po/1.3/Fri Feb 13 10:39:21 2009//
+/sk_SK.po/1.1/Tue Nov 3 11:21:15 2009//
+D
diff --git a/plugins/streamdev/streamdev-cvs/po/CVS/Repository b/plugins/streamdev/streamdev-cvs/po/CVS/Repository
new file mode 100644
index 0000000..40242d3
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/po/CVS/Repository
@@ -0,0 +1 @@
+streamdev/po
diff --git a/plugins/streamdev/streamdev-cvs/po/CVS/Root b/plugins/streamdev/streamdev-cvs/po/CVS/Root
new file mode 100644
index 0000000..2c7f6ce
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/po/CVS/Root
@@ -0,0 +1 @@
+:pserver:anoncvs@vdr-developer.org:/var/cvsroot
diff --git a/plugins/streamdev/streamdev-cvs/po/de_DE.po b/plugins/streamdev/streamdev-cvs/po/de_DE.po
new file mode 100644
index 0000000..82fa808
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/po/de_DE.po
@@ -0,0 +1,118 @@
+# VDR streamdev plugin language source file.
+# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
+# This file is distributed under the same license as the VDR streamdev package.
+# Frank Schmirler <vdrdev@schmirler.de>, 2008
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: streamdev 0.5.0\n"
+"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
+"POT-Creation-Date: 2009-02-13 11:53+0100\n"
+"PO-Revision-Date: 2008-03-30 02:11+0200\n"
+"Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "VTP Streaming Client"
+msgstr "VTP Streaming Client"
+
+msgid "Suspend Server"
+msgstr "Server pausieren"
+
+msgid "Server is suspended"
+msgstr "Server ist pausiert"
+
+msgid "Couldn't suspend Server!"
+msgstr "Konnte Server nicht pausieren!"
+
+msgid "Hide Mainmenu Entry"
+msgstr "Hauptmenüeintrag verstecken"
+
+msgid "Start Client"
+msgstr "Client starten"
+
+msgid "Remote IP"
+msgstr "IP der Gegenseite"
+
+msgid "Remote Port"
+msgstr "Port der Gegenseite"
+
+msgid "Filter Streaming"
+msgstr "Filter-Daten streamen"
+
+msgid "Synchronize EPG"
+msgstr "EPG synchronisieren"
+
+msgid "Minimum Priority"
+msgstr "Minimale Priorität"
+
+msgid "Maximum Priority"
+msgstr "Maximale Priorität"
+
+msgid "VDR Streaming Server"
+msgstr "VDR Streaming Server"
+
+msgid "Streaming active"
+msgstr "Streamen im Gange"
+
+msgid "Suspend Live TV"
+msgstr "Live-TV pausieren"
+
+msgid "Common Settings"
+msgstr "Allgemeines"
+
+msgid "Maximum Number of Clients"
+msgstr "Maximalanzahl an Clients"
+
+msgid "Suspend behaviour"
+msgstr "Pausierverhalten"
+
+msgid "Client may suspend"
+msgstr "Client darf pausieren"
+
+msgid "VDR-to-VDR Server"
+msgstr "VDR-zu-VDR Server"
+
+msgid "Start VDR-to-VDR Server"
+msgstr "VDR-zu-VDR Server starten"
+
+msgid "VDR-to-VDR Server Port"
+msgstr "Port des VDR-zu-VDR Servers"
+
+msgid "Bind to IP"
+msgstr "Binde an IP"
+
+msgid "HTTP Server"
+msgstr "HTTP Server"
+
+msgid "Start HTTP Server"
+msgstr "HTTP Server starten"
+
+msgid "HTTP Server Port"
+msgstr "Port des HTTP Servers"
+
+msgid "HTTP Streamtype"
+msgstr "HTTP Streamtyp"
+
+msgid "Multicast Streaming Server"
+msgstr "Multicast Streaming Server"
+
+msgid "Start IGMP Server"
+msgstr "IGMP Server starten"
+
+msgid "Multicast Client Port"
+msgstr "Port des Multicast Clients"
+
+msgid "Multicast Streamtype"
+msgstr "Multicast Streamtyp"
+
+msgid "Offer suspend mode"
+msgstr "Pausieren anbieten"
+
+msgid "Always suspended"
+msgstr "Immer pausiert"
+
+msgid "Never suspended"
+msgstr "Nie pausiert"
diff --git a/plugins/streamdev/streamdev-cvs/po/fi_FI.po b/plugins/streamdev/streamdev-cvs/po/fi_FI.po
new file mode 100644
index 0000000..1cd5566
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/po/fi_FI.po
@@ -0,0 +1,118 @@
+# VDR streamdev plugin language source file.
+# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
+# This file is distributed under the same license as the VDR streamdev package.
+# Rolf Ahrenberg <rahrenbe@cc.hut.fi>, 2008
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: streamdev 0.5.0\n"
+"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
+"POT-Creation-Date: 2009-02-13 11:53+0100\n"
+"PO-Revision-Date: 2008-03-30 02:11+0200\n"
+"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "VTP Streaming Client"
+msgstr "VTP-suoratoistoasiakas"
+
+msgid "Suspend Server"
+msgstr "Pysäytä palvelin"
+
+msgid "Server is suspended"
+msgstr "Palvelin on pysäytetty"
+
+msgid "Couldn't suspend Server!"
+msgstr "Palvelinta ei onnistuttu pysäyttämään!"
+
+msgid "Hide Mainmenu Entry"
+msgstr "Piilota valinta päävalikosta"
+
+msgid "Start Client"
+msgstr "Käynnistä VDR-asiakas"
+
+msgid "Remote IP"
+msgstr "Etäkoneen IP-osoite"
+
+msgid "Remote Port"
+msgstr "Etäkoneen portti"
+
+msgid "Filter Streaming"
+msgstr "Suodatetun tiedon suoratoisto"
+
+msgid "Synchronize EPG"
+msgstr "Päivitä ohjelmaopas"
+
+msgid "Minimum Priority"
+msgstr "Pienin prioriteetti"
+
+msgid "Maximum Priority"
+msgstr "Suurin prioriteetti"
+
+msgid "VDR Streaming Server"
+msgstr "VDR-suoratoistopalvelin"
+
+msgid "Streaming active"
+msgstr "Suoratoistopalvelin aktiivinen"
+
+msgid "Suspend Live TV"
+msgstr "Pysäytä suora TV-lähetys"
+
+msgid "Common Settings"
+msgstr "Yleiset asetukset"
+
+msgid "Maximum Number of Clients"
+msgstr "Suurin sallittu asiakkaiden määrä"
+
+msgid "Suspend behaviour"
+msgstr "Pysäytystoiminto"
+
+msgid "Client may suspend"
+msgstr "Asiakas saa pysäyttää palvelimen"
+
+msgid "VDR-to-VDR Server"
+msgstr "VDR-palvelin"
+
+msgid "Start VDR-to-VDR Server"
+msgstr "Käynnistä VDR-palvelin"
+
+msgid "VDR-to-VDR Server Port"
+msgstr "VDR-palvelimen portti"
+
+msgid "Bind to IP"
+msgstr "Sido osoitteeseen"
+
+msgid "HTTP Server"
+msgstr "HTTP-palvelin"
+
+msgid "Start HTTP Server"
+msgstr "Käynnistä HTTP-palvelin"
+
+msgid "HTTP Server Port"
+msgstr "HTTP-palvelimen portti"
+
+msgid "HTTP Streamtype"
+msgstr "HTTP-lähetysmuoto"
+
+msgid "Multicast Streaming Server"
+msgstr "Multicast-suoratoistopalvelin"
+
+msgid "Start IGMP Server"
+msgstr "Käynnistä IGMP-palvelin"
+
+msgid "Multicast Client Port"
+msgstr "Multicast-portti"
+
+msgid "Multicast Streamtype"
+msgstr "Multicast-lähetysmuoto"
+
+msgid "Offer suspend mode"
+msgstr "tyrkytä"
+
+msgid "Always suspended"
+msgstr "aina"
+
+msgid "Never suspended"
+msgstr "ei koskaan"
diff --git a/plugins/streamdev/streamdev-cvs/po/fr_FR.po b/plugins/streamdev/streamdev-cvs/po/fr_FR.po
new file mode 100644
index 0000000..3ba78c0
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/po/fr_FR.po
@@ -0,0 +1,118 @@
+# VDR streamdev plugin language source file.
+# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
+# This file is distributed under the same license as the VDR streamdev package.
+# Frank Schmirler <vdrdev@schmirler.de>, 2008
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: streamdev 0.5.0\n"
+"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
+"POT-Creation-Date: 2009-02-13 11:53+0100\n"
+"PO-Revision-Date: 2008-03-30 02:11+0200\n"
+"Last-Translator: micky979 <micky979@free.fr>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "VTP Streaming Client"
+msgstr "Client de streaming VTP"
+
+msgid "Suspend Server"
+msgstr "Suspendre le serveur"
+
+msgid "Server is suspended"
+msgstr "Le serveur est suspendu"
+
+msgid "Couldn't suspend Server!"
+msgstr "Impossible de suspendre le serveur!"
+
+msgid "Hide Mainmenu Entry"
+msgstr "Masquer dans le menu principal"
+
+msgid "Start Client"
+msgstr "Démarrage du client"
+
+msgid "Remote IP"
+msgstr "Adresse IP du serveur"
+
+msgid "Remote Port"
+msgstr "Port du serveur"
+
+msgid "Filter Streaming"
+msgstr "Filtre streaming"
+
+msgid "Synchronize EPG"
+msgstr "Synchroniser l'EPG"
+
+msgid "Minimum Priority"
+msgstr ""
+
+msgid "Maximum Priority"
+msgstr ""
+
+msgid "VDR Streaming Server"
+msgstr "Serveur de streaming VDR"
+
+msgid "Streaming active"
+msgstr "Streaming actif"
+
+msgid "Suspend Live TV"
+msgstr "Suspendre Live TV"
+
+msgid "Common Settings"
+msgstr "Paramètres communs"
+
+msgid "Maximum Number of Clients"
+msgstr "Nombre maximun de clients"
+
+msgid "Suspend behaviour"
+msgstr "Suspendre"
+
+msgid "Client may suspend"
+msgstr "Le client peut suspendre"
+
+msgid "VDR-to-VDR Server"
+msgstr "VDR-to-VDR Serveur"
+
+msgid "Start VDR-to-VDR Server"
+msgstr "Démarrer le serveur VDR-to-VDR"
+
+msgid "VDR-to-VDR Server Port"
+msgstr "Port du serveur VDR-to-VDR"
+
+msgid "Bind to IP"
+msgstr "Attacher aux IP"
+
+msgid "HTTP Server"
+msgstr "Serveur HTTP"
+
+msgid "Start HTTP Server"
+msgstr "Démarrer le serveur HTTP"
+
+msgid "HTTP Server Port"
+msgstr "Port du serveur HTTP"
+
+msgid "HTTP Streamtype"
+msgstr "Type de Streaming HTTP"
+
+msgid "Multicast Streaming Server"
+msgstr ""
+
+msgid "Start IGMP Server"
+msgstr ""
+
+msgid "Multicast Client Port"
+msgstr ""
+
+msgid "Multicast Streamtype"
+msgstr ""
+
+msgid "Offer suspend mode"
+msgstr "Offrir le mode suspendre"
+
+msgid "Always suspended"
+msgstr "Toujours suspendre"
+
+msgid "Never suspended"
+msgstr "Jamais suspendre"
diff --git a/plugins/streamdev/streamdev-cvs/po/it_IT.po b/plugins/streamdev/streamdev-cvs/po/it_IT.po
new file mode 100755
index 0000000..4db80ed
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/po/it_IT.po
@@ -0,0 +1,120 @@
+# VDR streamdev plugin language source file.
+# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
+# This file is distributed under the same license as the VDR streamdev package.
+# Alberto Carraro <bertocar@tin.it>, 2001
+# Antonio Ospite <ospite@studenti.unina.it>, 2003
+# Sean Carlos <seanc@libero.it>, 2005
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: streamdev 0.5.0\n"
+"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
+"POT-Creation-Date: 2009-02-13 11:53+0100\n"
+"PO-Revision-Date: 2008-04-13 23:42+0100\n"
+"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "VTP Streaming Client"
+msgstr "Client trasmissione VTP"
+
+msgid "Suspend Server"
+msgstr "Sospendi Server"
+
+msgid "Server is suspended"
+msgstr "Server sospeso"
+
+msgid "Couldn't suspend Server!"
+msgstr "Impossibile sospendere il server!"
+
+msgid "Hide Mainmenu Entry"
+msgstr "Nascondi voce menu principale"
+
+msgid "Start Client"
+msgstr "Avvia Client"
+
+msgid "Remote IP"
+msgstr "Indirizzo IP del Server"
+
+msgid "Remote Port"
+msgstr "Porta Server Remoto"
+
+msgid "Filter Streaming"
+msgstr "Filtra trasmissione"
+
+msgid "Synchronize EPG"
+msgstr "Sincronizza EPG"
+
+msgid "Minimum Priority"
+msgstr ""
+
+msgid "Maximum Priority"
+msgstr ""
+
+msgid "VDR Streaming Server"
+msgstr "Server trasmissione VDR"
+
+msgid "Streaming active"
+msgstr "Trasmissione attiva"
+
+msgid "Suspend Live TV"
+msgstr "Sospendi TV dal vivo"
+
+msgid "Common Settings"
+msgstr "Impostazioni comuni"
+
+msgid "Maximum Number of Clients"
+msgstr "Numero massimo di Client"
+
+msgid "Suspend behaviour"
+msgstr "Tipo sospensione"
+
+msgid "Client may suspend"
+msgstr "Permetti sospensione al Client"
+
+msgid "VDR-to-VDR Server"
+msgstr "Server VDR-a-VDR"
+
+msgid "Start VDR-to-VDR Server"
+msgstr "Avvia Server VDR-a-VDR"
+
+msgid "VDR-to-VDR Server Port"
+msgstr "Porta Server VDR-a-VDR"
+
+msgid "Bind to IP"
+msgstr "IP associati"
+
+msgid "HTTP Server"
+msgstr "Server HTTP"
+
+msgid "Start HTTP Server"
+msgstr "Avvia Server HTTP"
+
+msgid "HTTP Server Port"
+msgstr "Porta Server HTTP"
+
+msgid "HTTP Streamtype"
+msgstr "Tipo flusso HTTP"
+
+msgid "Multicast Streaming Server"
+msgstr ""
+
+msgid "Start IGMP Server"
+msgstr ""
+
+msgid "Multicast Client Port"
+msgstr ""
+
+msgid "Multicast Streamtype"
+msgstr ""
+
+msgid "Offer suspend mode"
+msgstr "Offri mod. sospensione"
+
+msgid "Always suspended"
+msgstr "Sempre sospeso"
+
+msgid "Never suspended"
+msgstr "Mai sospeso"
diff --git a/plugins/streamdev/streamdev-cvs/po/lt_LT.po b/plugins/streamdev/streamdev-cvs/po/lt_LT.po
new file mode 100644
index 0000000..7ba3b5c
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/po/lt_LT.po
@@ -0,0 +1,118 @@
+# VDR streamdev plugin language source file.
+# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
+# This file is distributed under the same license as the VDR streamdev package.
+# Frank Schmirler <vdrdev@schmirler.de>, 2008
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: streamdev 0.5.0\n"
+"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
+"POT-Creation-Date: 2009-02-13 11:53+0100\n"
+"PO-Revision-Date: 2009-11-26 21:57+0200\n"
+"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
+"Language-Team: Lietuvių\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "VTP Streaming Client"
+msgstr "VTP transliavimo standartas"
+
+msgid "Suspend Server"
+msgstr "Sustabdyti serverį"
+
+msgid "Server is suspended"
+msgstr "Serveris sustabdytas"
+
+msgid "Couldn't suspend Server!"
+msgstr "Negali sustabdyti serverio!"
+
+msgid "Hide Mainmenu Entry"
+msgstr "Paslėpti pagrindinio meniu įrašą"
+
+msgid "Start Client"
+msgstr "Paleisti klientÄ…"
+
+msgid "Remote IP"
+msgstr "Nuotolinis IP adresas"
+
+msgid "Remote Port"
+msgstr "Nuotolinis portas"
+
+msgid "Filter Streaming"
+msgstr "Filtruoti transliavimÄ…"
+
+msgid "Synchronize EPG"
+msgstr "Sinchronizuoti EPG"
+
+msgid "Minimum Priority"
+msgstr "Minimalus prioritetas"
+
+msgid "Maximum Priority"
+msgstr "Maksimalus prioritetas"
+
+msgid "VDR Streaming Server"
+msgstr "VDR transliavimo serveris"
+
+msgid "Streaming active"
+msgstr "Transliavimas vyksta"
+
+msgid "Suspend Live TV"
+msgstr "Pristabdyti Live TV"
+
+msgid "Common Settings"
+msgstr "Bendri nustatymai"
+
+msgid "Maximum Number of Clients"
+msgstr "Maksimalus klientų skaiÄius"
+
+msgid "Suspend behaviour"
+msgstr "Pristabdyti veikimÄ…"
+
+msgid "Client may suspend"
+msgstr "Klientas gali pristabdyti"
+
+msgid "VDR-to-VDR Server"
+msgstr "VDR-su-VDR Serveris"
+
+msgid "Start VDR-to-VDR Server"
+msgstr "Paleisti VDR-su-VDR serverį"
+
+msgid "VDR-to-VDR Server Port"
+msgstr "VDR-su-VDR Serverio portas"
+
+msgid "Bind to IP"
+msgstr "Pririšti IP"
+
+msgid "HTTP Server"
+msgstr "HTTP Serveris"
+
+msgid "Start HTTP Server"
+msgstr "Paleisti HTTP serverį"
+
+msgid "HTTP Server Port"
+msgstr "HTTP serverio portas"
+
+msgid "HTTP Streamtype"
+msgstr "HTTP transliavimo tipas"
+
+msgid "Multicast Streaming Server"
+msgstr "Multicast transliavimo serveris"
+
+msgid "Start IGMP Server"
+msgstr "Paleisti IGMP serverį"
+
+msgid "Multicast Client Port"
+msgstr "Multicast kliento portas"
+
+msgid "Multicast Streamtype"
+msgstr "Multicast transliavimo tipas"
+
+msgid "Offer suspend mode"
+msgstr "Klausti dÄ—l sustabdymo"
+
+msgid "Always suspended"
+msgstr "Visada stabdyti"
+
+msgid "Never suspended"
+msgstr "Niekada nestabdyti"
diff --git a/plugins/streamdev/streamdev-cvs/po/ru_RU.po b/plugins/streamdev/streamdev-cvs/po/ru_RU.po
new file mode 100644
index 0000000..b80fcd3
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/po/ru_RU.po
@@ -0,0 +1,118 @@
+# VDR streamdev plugin language source file.
+# Copyright (C) 2008 streamdev development team. See http://streamdev.vdr-developer.org
+# This file is distributed under the same license as the VDR streamdev package.
+# Frank Schmirler <vdrdev@schmirler.de>, 2008
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: streamdev 0.5.0\n"
+"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
+"POT-Creation-Date: 2009-02-13 11:53+0100\n"
+"PO-Revision-Date: 2008-06-26 15:36+0100\n"
+"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-5\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "VTP Streaming Client"
+msgstr "VTP Streaming ÚÛØÕÝâ"
+
+msgid "Suspend Server"
+msgstr "¾áâÐÝÞÒØâì áÕàÒÕà"
+
+msgid "Server is suspended"
+msgstr "ÁÕàÒÕà ÞáâÐÝÞÒÛÕÝ"
+
+msgid "Couldn't suspend Server!"
+msgstr "ÝÕ ÜÞÓã ÞáâÐÝÞÒØâì áÕàÒÕà"
+
+msgid "Hide Mainmenu Entry"
+msgstr "ÁßàïâÐâì Ò ÓÛÐÒÝÞÜ ÜÕÝî"
+
+msgid "Start Client"
+msgstr "ÁâÐàâ ÚÛØÕÝâÐ"
+
+msgid "Remote IP"
+msgstr "ÃÔÐÛÕÝÝëÙ IP"
+
+msgid "Remote Port"
+msgstr "ÃÔÐÛÕÝÝëÙ ßÞàâ"
+
+msgid "Filter Streaming"
+msgstr "ÄØÛìâà ßÞâÞÚÐ"
+
+msgid "Synchronize EPG"
+msgstr "ÁØÝåàÞÝØ×ÐæØï EPG"
+
+msgid "Minimum Priority"
+msgstr ""
+
+msgid "Maximum Priority"
+msgstr ""
+
+msgid "VDR Streaming Server"
+msgstr "VDR Streaming áÕàÒÕà"
+
+msgid "Streaming active"
+msgstr "ÁâàØÜØÝÓ ÐÚâØÒÕÝ"
+
+msgid "Suspend Live TV"
+msgstr "¾áâÐÝÞÒÚÐ Live TV"
+
+msgid "Common Settings"
+msgstr "½ÐáâàÞÙÚØ"
+
+msgid "Maximum Number of Clients"
+msgstr "¼ÐÚá. ÚÞÛØçÕáâÒÞ ÚÛØÕÝâÞÒ"
+
+msgid "Suspend behaviour"
+msgstr "¿ÞÒÕÔÕÝØÕ ÞáâÐÝÞÒÚØ"
+
+msgid "Client may suspend"
+msgstr "ºÛØÕÝâ ÜÞÖÕâ ÞáâÐÝÐÒÛØÒÐâì"
+
+msgid "VDR-to-VDR Server"
+msgstr "VDR-to-VDR áÕàÒÕà"
+
+msgid "Start VDR-to-VDR Server"
+msgstr "ÁâÐàâ VDR-to-VDR áÕàÒÕà"
+
+msgid "VDR-to-VDR Server Port"
+msgstr "VDR-to-VDR ßÞàâ áÕàÒÕàÐ"
+
+msgid "Bind to IP"
+msgstr "¿àØáÞÕÔØÝØâìáï Ú IP"
+
+msgid "HTTP Server"
+msgstr "HTTP áÕàÒÕà"
+
+msgid "Start HTTP Server"
+msgstr "ÁâÐàâ HTTP áÕàÒÕàÐ"
+
+msgid "HTTP Server Port"
+msgstr "HTTP áÕàÒÕà ¿Þàâ"
+
+msgid "HTTP Streamtype"
+msgstr "ÂØß HTTP ßÞâÞÚÐ"
+
+msgid "Multicast Streaming Server"
+msgstr ""
+
+msgid "Start IGMP Server"
+msgstr ""
+
+msgid "Multicast Client Port"
+msgstr ""
+
+msgid "Multicast Streamtype"
+msgstr ""
+
+msgid "Offer suspend mode"
+msgstr "¿àÕÔÛÐÓÐâì ÞáâÐÝÞÒÚã"
+
+msgid "Always suspended"
+msgstr "²áÕÓÔÐ ÞáâÐÝÞÒÛÕÝ"
+
+msgid "Never suspended"
+msgstr "½ØÚÞÓÔÐ ÝÕ ÞáâÐÝÞÒÛÕÝ"
diff --git a/plugins/streamdev/streamdev-cvs/po/sk_SK.po b/plugins/streamdev/streamdev-cvs/po/sk_SK.po
new file mode 100644
index 0000000..9a7102e
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/po/sk_SK.po
@@ -0,0 +1,121 @@
+# VDR streamdev plugin language source file.
+# Copyright (C) 2009 streamdev development team. See http://streamdev.vdr-developer.org
+# This file is distributed under the same license as the VDR streamdev package.
+# Milan Hrala <hrala.milan@gmail.com>, 2009
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: streamdev_SK\n"
+"Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n"
+"POT-Creation-Date: 2009-09-23 13:59+0200\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
+"Language-Team: Slovak <hrala.milan@gmail.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-2\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Slovak\n"
+"X-Poedit-Country: SLOVAKIA\n"
+
+msgid "VTP Streaming Client"
+msgstr "VTP prúdový klient"
+
+msgid "Suspend Server"
+msgstr "Server pozastavený"
+
+msgid "Server is suspended"
+msgstr "Server je doèasne preru¹ený"
+
+msgid "Couldn't suspend Server!"
+msgstr "Nepodarilo sa pozastavi» Server!"
+
+msgid "Hide Mainmenu Entry"
+msgstr "Schova» polo¾ku v hlavnom menu"
+
+msgid "Start Client"
+msgstr "Spusti» Klienta"
+
+msgid "Remote IP"
+msgstr "Vzdialená IP"
+
+msgid "Remote Port"
+msgstr "Vzdialený port"
+
+msgid "Filter Streaming"
+msgstr "filtrova» prúdy"
+
+msgid "Synchronize EPG"
+msgstr "zosúladi» EPG"
+
+msgid "Minimum Priority"
+msgstr "minimálna priorita"
+
+msgid "Maximum Priority"
+msgstr "maximálna priorita"
+
+msgid "VDR Streaming Server"
+msgstr "VDR prúdový server"
+
+msgid "Streaming active"
+msgstr "streamovanie aktivne"
+
+msgid "Suspend Live TV"
+msgstr "Pozastavenie ¾ivého vysielania"
+
+msgid "Common Settings"
+msgstr "V¹eobecné nastavenia"
+
+msgid "Maximum Number of Clients"
+msgstr "Maximály poèet klientov"
+
+msgid "Suspend behaviour"
+msgstr "Správanie preru¹enia"
+
+msgid "Client may suspend"
+msgstr "Klient mô¾e pozastavi»"
+
+msgid "VDR-to-VDR Server"
+msgstr "VDR-do-VDR server"
+
+msgid "Start VDR-to-VDR Server"
+msgstr "Spusti» VDR-do-VDR Server"
+
+msgid "VDR-to-VDR Server Port"
+msgstr "Port serveru pre VDR-do-VDR"
+
+msgid "Bind to IP"
+msgstr "viaza» na IP"
+
+msgid "HTTP Server"
+msgstr "server HTTP"
+
+msgid "Start HTTP Server"
+msgstr "Spusti» HTTP Server"
+
+msgid "HTTP Server Port"
+msgstr "Port serveru HTTP"
+
+msgid "HTTP Streamtype"
+msgstr "typ prúdu HTTP"
+
+msgid "Multicast Streaming Server"
+msgstr "Multicast prúdový server"
+
+msgid "Start IGMP Server"
+msgstr "Spusti» IGMP Server"
+
+msgid "Multicast Client Port"
+msgstr "Port klienta Multicast"
+
+msgid "Multicast Streamtype"
+msgstr "Multicast typ streamu"
+
+msgid "Offer suspend mode"
+msgstr "Výber re¾ímu pozastavenia"
+
+msgid "Always suspended"
+msgstr "V¾dy pozastavi»"
+
+msgid "Never suspended"
+msgstr "Nikdy nepozastavi»"
+
diff --git a/plugins/streamdev/streamdev-cvs/remux/CVS/Entries b/plugins/streamdev/streamdev-cvs/remux/CVS/Entries
new file mode 100644
index 0000000..3529acd
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/CVS/Entries
@@ -0,0 +1,11 @@
+/extern.c/1.11/Fri Jun 19 06:32:40 2009//
+/extern.h/1.5/Fri Jun 19 06:32:40 2009//
+/ts2es.c/1.4/Fri Jun 19 06:32:40 2009//
+/ts2es.h/1.3/Fri Jun 19 06:32:40 2009//
+/ts2pes.c/1.2/Tue Jun 30 06:04:33 2009//
+/ts2pes.h/1.4/Mon Jul 6 06:11:11 2009//
+/ts2ps.c/1.4/Fri Jun 19 06:32:40 2009//
+/ts2ps.h/1.5/Mon Jul 6 06:14:21 2009//
+/tsremux.c/1.5/Fri Jun 19 06:32:40 2009//
+/tsremux.h/1.8/Thu Dec 3 07:26:13 2009//
+D
diff --git a/plugins/streamdev/streamdev-cvs/remux/CVS/Repository b/plugins/streamdev/streamdev-cvs/remux/CVS/Repository
new file mode 100644
index 0000000..e57f4ec
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/CVS/Repository
@@ -0,0 +1 @@
+streamdev/remux
diff --git a/plugins/streamdev/streamdev-cvs/remux/CVS/Root b/plugins/streamdev/streamdev-cvs/remux/CVS/Root
new file mode 100644
index 0000000..2c7f6ce
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/CVS/Root
@@ -0,0 +1 @@
+:pserver:anoncvs@vdr-developer.org:/var/cvsroot
diff --git a/plugins/streamdev/streamdev-cvs/remux/extern.c b/plugins/streamdev/streamdev-cvs/remux/extern.c
new file mode 100644
index 0000000..3791d10
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/extern.c
@@ -0,0 +1,192 @@
+#include "remux/extern.h"
+#include "server/server.h"
+#include "server/streamer.h"
+#include <vdr/tools.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+
+namespace Streamdev {
+
+class cTSExt: public cThread {
+private:
+ cRingBufferLinear *m_ResultBuffer;
+ bool m_Active;
+ int m_Process;
+ int m_Inpipe, m_Outpipe;
+
+protected:
+ virtual void Action(void);
+
+public:
+ cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter);
+ virtual ~cTSExt();
+
+ void Put(const uchar *Data, int Count);
+};
+
+} // namespace Streamdev
+using namespace Streamdev;
+
+cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter):
+ m_ResultBuffer(ResultBuffer),
+ m_Active(false),
+ m_Process(-1),
+ m_Inpipe(0),
+ m_Outpipe(0)
+{
+ int inpipe[2];
+ int outpipe[2];
+
+ if (pipe(inpipe) == -1) {
+ LOG_ERROR_STR("pipe failed");
+ return;
+ }
+
+ if (pipe(outpipe) == -1) {
+ LOG_ERROR_STR("pipe failed");
+ close(inpipe[0]);
+ close(inpipe[1]);
+ return;
+ }
+
+ if ((m_Process = fork()) == -1) {
+ LOG_ERROR_STR("fork failed");
+ close(inpipe[0]);
+ close(inpipe[1]);
+ close(outpipe[0]);
+ close(outpipe[1]);
+ return;
+ }
+
+ if (m_Process == 0) {
+ // child process
+ dup2(inpipe[0], STDIN_FILENO);
+ close(inpipe[1]);
+ dup2(outpipe[1], STDOUT_FILENO);
+ close(outpipe[0]);
+
+ int MaxPossibleFileDescriptors = getdtablesize();
+ for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
+ close(i); //close all dup'ed filedescriptors
+
+ std::string cmd = std::string(opt_remux) + " " + Parameter;
+ if (execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL) == -1) {
+ esyslog("streamdev-server: externremux script '%s' execution failed: %m", cmd.c_str());
+ _exit(-1);
+ }
+ // should never be reached
+ _exit(0);
+ }
+
+ close(inpipe[0]);
+ close(outpipe[1]);
+ m_Inpipe = inpipe[1];
+ m_Outpipe = outpipe[0];
+ Start();
+}
+
+cTSExt::~cTSExt()
+{
+ m_Active = false;
+ Cancel(3);
+ if (m_Process > 0) {
+ // close pipes
+ close(m_Outpipe);
+ close(m_Inpipe);
+ // signal and wait for termination
+ if (kill(m_Process, SIGINT) < 0) {
+ esyslog("streamdev-server: externremux SIGINT failed: %m");
+ }
+ else {
+ int i = 0;
+ int retval;
+ while ((retval = waitpid(m_Process, NULL, WNOHANG)) == 0) {
+
+ if ((++i % 20) == 0) {
+ esyslog("streamdev-server: externremux process won't stop - killing it");
+ kill(m_Process, SIGKILL);
+ }
+ cCondWait::SleepMs(100);
+ }
+
+ if (retval < 0)
+ esyslog("streamdev-server: externremux process waitpid failed: %m");
+ else
+ Dprintf("streamdev-server: externremux child (%d) exited as expected\n", m_Process);
+ }
+ m_Process = -1;
+ }
+}
+
+void cTSExt::Action(void)
+{
+ m_Active = true;
+ while (m_Active) {
+ fd_set rfds;
+ struct timeval tv;
+
+ FD_ZERO(&rfds);
+ FD_SET(m_Outpipe, &rfds);
+
+ while (FD_ISSET(m_Outpipe, &rfds)) {
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ if (select(m_Outpipe + 1, &rfds, NULL, NULL, &tv) == -1) {
+ LOG_ERROR_STR("poll failed");
+ break;;
+ }
+
+ if (FD_ISSET(m_Outpipe, &rfds)) {
+ int result;
+ //Read returns 0 if buffer full or EOF
+ bool bufferFull = m_ResultBuffer->Free() <= 0; //Free may be < 0
+ while ((result = m_ResultBuffer->Read(m_Outpipe)) == 0 && bufferFull)
+ dsyslog("streamdev-server: buffer full while reading from externremux");
+
+ if (result == -1) {
+ if (errno != EINTR) {
+ LOG_ERROR_STR("read failed");
+ m_Active = false;
+ }
+ break;
+ }
+ else if (result == 0) {
+ esyslog("streamdev-server: EOF reading from externremux");
+ m_Active = false;
+ break;
+ }
+ }
+ }
+ }
+ m_Active = false;
+}
+
+
+void cTSExt::Put(const uchar *Data, int Count)
+{
+ if (safe_write(m_Inpipe, Data, Count) == -1) {
+ LOG_ERROR_STR("write failed");
+ return;
+ }
+}
+
+cExternRemux::cExternRemux(int VPid, const int *APids, const int *Dpids, const int *SPids, std::string Parameter):
+ m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2)),
+ m_Remux(new cTSExt(m_ResultBuffer, Parameter))
+{
+ m_ResultBuffer->SetTimeouts(500, 100);
+}
+
+cExternRemux::~cExternRemux()
+{
+ delete m_Remux;
+ delete m_ResultBuffer;
+}
+
+int cExternRemux::Put(const uchar *Data, int Count)
+{
+ m_Remux->Put(Data, Count);
+ return Count;
+}
diff --git a/plugins/streamdev/streamdev-cvs/remux/extern.h b/plugins/streamdev/streamdev-cvs/remux/extern.h
new file mode 100644
index 0000000..ff4ddec
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/extern.h
@@ -0,0 +1,28 @@
+#ifndef VDR_STREAMDEV_EXTERNREMUX_H
+#define VDR_STREAMDEV_EXTERNREMUX_H
+
+#include "remux/tsremux.h"
+#include <vdr/ringbuffer.h>
+#include <string>
+
+namespace Streamdev {
+
+class cTSExt;
+
+class cExternRemux: public cTSRemux {
+private:
+ cRingBufferLinear *m_ResultBuffer;
+ cTSExt *m_Remux;
+
+public:
+ cExternRemux(int VPid, const int *APids, const int *Dpids, const int *SPids, std::string Parameter);
+ virtual ~cExternRemux();
+
+ int Put(const uchar *Data, int Count);
+ uchar *Get(int &Count) { return m_ResultBuffer->Get(Count); }
+ void Del(int Count) { m_ResultBuffer->Del(Count); }
+};
+
+} // namespace Streamdev
+
+#endif // VDR_STREAMDEV_EXTERNREMUX_H
diff --git a/plugins/streamdev/streamdev-cvs/remux/ts2es.c b/plugins/streamdev/streamdev-cvs/remux/ts2es.c
new file mode 100644
index 0000000..6ff4e87
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/ts2es.c
@@ -0,0 +1,146 @@
+#include "remux/ts2es.h"
+#include "server/streamer.h"
+#include "common.h"
+#include <vdr/device.h>
+
+// from VDR's remux.c
+#define MAXNONUSEFULDATA (10*1024*1024)
+
+namespace Streamdev {
+
+class cTS2ES: public ipack {
+ friend void PutES(uint8_t *Buffer, int Size, void *Data);
+
+private:
+ cRingBufferLinear *m_ResultBuffer;
+
+public:
+ cTS2ES(cRingBufferLinear *ResultBuffer);
+ ~cTS2ES();
+
+ void PutTSPacket(const uint8_t *Buffer);
+};
+
+void PutES(uint8_t *Buffer, int Size, void *Data)
+{
+ cTS2ES *This = (cTS2ES*)Data;
+ uint8_t payl = Buffer[8] + 9 + This->start - 1;
+ int count = Size - payl;
+
+ int n = This->m_ResultBuffer->Put(Buffer + payl, count);
+ if (n != count)
+ esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", count - n, count);
+ This->start = 1;
+}
+
+} // namespace Streamdev
+using namespace Streamdev;
+
+cTS2ES::cTS2ES(cRingBufferLinear *ResultBuffer)
+{
+ m_ResultBuffer = ResultBuffer;
+
+ init_ipack(this, IPACKS, PutES, 0);
+ data = (void*)this;
+}
+
+cTS2ES::~cTS2ES()
+{
+ free_ipack(this);
+}
+
+void cTS2ES::PutTSPacket(const uint8_t *Buffer) {
+ if (!Buffer)
+ return;
+
+ if (Buffer[1] & 0x80) { // ts error
+ // TODO
+ }
+
+ if (Buffer[1] & 0x40) { // payload start
+ if (plength == MMAX_PLENGTH - 6) {
+ plength = found - 6;
+ found = 0;
+ send_ipack(this);
+ reset_ipack(this);
+ }
+ }
+
+ uint8_t off = 0;
+
+ if (Buffer[3] & 0x20) { // adaptation field?
+ off = Buffer[4] + 1;
+ if (off + 4 > TS_SIZE - 1)
+ return;
+ }
+
+ instant_repack((uint8_t*)(Buffer + 4 + off), TS_SIZE - 4 - off, this);
+}
+
+cTS2ESRemux::cTS2ESRemux(int Pid):
+ m_Pid(Pid),
+ m_ResultBuffer(new cStreamdevBuffer(WRITERBUFSIZE, IPACKS)),
+ m_Remux(new cTS2ES(m_ResultBuffer))
+{
+ m_ResultBuffer->SetTimeouts(100, 100);
+}
+
+cTS2ESRemux::~cTS2ESRemux()
+{
+ delete m_Remux;
+ delete m_ResultBuffer;
+}
+
+int cTS2ESRemux::Put(const uchar *Data, int Count)
+{
+ int used = 0;
+
+ // Make sure we are looking at a TS packet:
+
+ while (Count > TS_SIZE) {
+ if (Data[0] == TS_SYNC_BYTE && Data[TS_SIZE] == TS_SYNC_BYTE)
+ break;
+ Data++;
+ Count--;
+ used++;
+ }
+
+ if (used)
+ esyslog("ERROR: skipped %d byte to sync on TS packet", used);
+
+ // Convert incoming TS data into ES:
+
+ for (int i = 0; i < Count; i += TS_SIZE) {
+ if (Count - i < TS_SIZE)
+ break;
+ if (Data[i] != TS_SYNC_BYTE)
+ break;
+ if (m_ResultBuffer->Free() < 2 * IPACKS) {
+ m_ResultBuffer->WaitForPut();
+ break; // A cTS2ES might write one full packet and also a small rest
+ }
+ int pid = cTSRemux::GetPid(Data + i + 1);
+ if (Data[i + 3] & 0x10) { // got payload
+ if (m_Pid == pid)
+ m_Remux->PutTSPacket(Data + i);
+ }
+ used += TS_SIZE;
+ }
+
+/*
+ // Check if we're getting anywhere here:
+ if (!synced && skipped >= 0) {
+ if (skipped > MAXNONUSEFULDATA) {
+ esyslog("ERROR: no useful data seen within %d byte of video stream", skipped);
+ skipped = -1;
+ if (exitOnFailure)
+ cThread::EmergencyExit(true);
+ }
+ else
+ skipped += used;
+ }
+*/
+
+ return used;
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/remux/ts2es.h b/plugins/streamdev/streamdev-cvs/remux/ts2es.h
new file mode 100644
index 0000000..95eceb9
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/ts2es.h
@@ -0,0 +1,28 @@
+#ifndef VDR_STREAMDEV_TS2ESREMUX_H
+#define VDR_STREAMDEV_TS2ESREMUX_H
+
+#include "remux/tsremux.h"
+#include "server/streamer.h"
+
+namespace Streamdev {
+
+class cTS2ES;
+
+class cTS2ESRemux: public cTSRemux {
+private:
+ int m_Pid;
+ cStreamdevBuffer *m_ResultBuffer;
+ cTS2ES *m_Remux;
+
+public:
+ cTS2ESRemux(int Pid);
+ virtual ~cTS2ESRemux();
+
+ int Put(const uchar *Data, int Count);
+ uchar *Get(int &Count) { return m_ResultBuffer->Get(Count); }
+ void Del(int Count) { m_ResultBuffer->Del(Count); }
+};
+
+} // namespace Streamdev
+
+#endif // VDR_STREAMDEV_TS2ESREMUX_H
diff --git a/plugins/streamdev/streamdev-cvs/remux/ts2pes.c b/plugins/streamdev/streamdev-cvs/remux/ts2pes.c
new file mode 100644
index 0000000..eeb56d5
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/ts2pes.c
@@ -0,0 +1,2017 @@
+/*
+ * ts2pes.c: A streaming MPEG2 remultiplexer
+ *
+ * This file is based on remux.c from Klaus Schmidinger's VDR, version 1.6.0.
+ *
+ * The parts of this code that implement cTS2PES have been taken from
+ * the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
+ * VDR's needs.
+ *
+ * The cRepacker family's code was originally written by Reinhard Nissl <rnissl@gmx.de>,
+ * and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de.
+ *
+ * $Id: ts2pes.c,v 1.2 2009/06/30 06:04:33 schmirl Exp $
+ */
+
+#include "remux/ts2pes.h"
+#include <stdlib.h>
+#include <vdr/channels.h>
+#include <vdr/shutdown.h>
+
+namespace Streamdev {
+
+// --- cRepacker -------------------------------------------------------------
+
+#define MIN_LOG_INTERVAL 10 // min. # of seconds between two consecutive log messages of a cRepacker
+#define LOG(a...) (LogAllowed() && (esyslog(a), true))
+
+class cRepacker {
+protected:
+ bool initiallySyncing;
+ int maxPacketSize;
+ uint8_t subStreamId;
+ time_t lastLog;
+ int suppressedLogMessages;
+ bool LogAllowed(void);
+ void DroppedData(const char *Reason, int Count) { LOG("%s (dropped %d bytes)", Reason, Count); }
+public:
+ static int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
+ cRepacker(void);
+ virtual ~cRepacker() {}
+ virtual void Reset(void) { initiallySyncing = true; }
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0;
+ virtual int BreakAt(const uchar *Data, int Count) = 0;
+ virtual int QuerySnoopSize(void) { return 0; }
+ void SetMaxPacketSize(int MaxPacketSize) { maxPacketSize = MaxPacketSize; }
+ void SetSubStreamId(uint8_t SubStreamId) { subStreamId = SubStreamId; }
+ };
+
+cRepacker::cRepacker(void)
+{
+ initiallySyncing = true;
+ maxPacketSize = 6 + 65535;
+ subStreamId = 0;
+ suppressedLogMessages = 0;;
+ lastLog = 0;
+}
+
+bool cRepacker::LogAllowed(void)
+{
+ bool Allowed = time(NULL) - lastLog >= MIN_LOG_INTERVAL;
+ lastLog = time(NULL);
+ if (Allowed) {
+ if (suppressedLogMessages) {
+ esyslog("%d cRepacker messages suppressed", suppressedLogMessages);
+ suppressedLogMessages = 0;
+ }
+ }
+ else
+ suppressedLogMessages++;
+ return Allowed;
+}
+
+int cRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded)
+{
+ if (CapacityNeeded >= Count && ResultBuffer->Free() < CapacityNeeded) {
+ esyslog("ERROR: possible result buffer overflow, dropped %d out of %d byte", CapacityNeeded, CapacityNeeded);
+ return 0;
+ }
+ int n = ResultBuffer->Put(Data, Count);
+ if (n != Count)
+ esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
+ return n;
+}
+
+// --- cCommonRepacker -------------------------------------------------------
+
+class cCommonRepacker : public cRepacker {
+protected:
+ int skippedBytes;
+ int packetTodo;
+ uchar fragmentData[6 + 65535 + 3];
+ int fragmentLen;
+ uchar pesHeader[6 + 3 + 255 + 3];
+ int pesHeaderLen;
+ uchar pesHeaderBackup[6 + 3 + 255];
+ int pesHeaderBackupLen;
+ uint32_t scanner;
+ uint32_t localScanner;
+ int localStart;
+ bool PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int QuerySnoopSize() { return 4; }
+ virtual void Reset(void);
+ };
+
+void cCommonRepacker::Reset(void)
+{
+ cRepacker::Reset();
+ skippedBytes = 0;
+ packetTodo = 0;
+ fragmentLen = 0;
+ pesHeaderLen = 0;
+ pesHeaderBackupLen = 0;
+ localStart = -1;
+}
+
+bool cCommonRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
+{
+ // enter packet length into PES header ...
+ if (fragmentLen > 0) { // ... which is contained in the fragment buffer
+ // determine PES packet payload
+ int PacketLen = fragmentLen + Count - 6;
+ fragmentData[ 4 ] = PacketLen >> 8;
+ fragmentData[ 5 ] = PacketLen & 0xFF;
+ // just skip packets with no payload
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(fragmentData, fragmentLen, PesPayloadOffset) <= phInvalid)
+ LOG("cCommonRepacker: invalid PES packet encountered in fragment buffer!");
+ else if (6 + PacketLen <= PesPayloadOffset) {
+ fragmentLen = 0;
+ return true; // skip empty packet
+ }
+ // amount of data to put into result buffer: a negative Count value means
+ // to strip off any partially contained start code.
+ int Bite = fragmentLen + (Count >= 0 ? 0 : Count);
+ // put data into result buffer
+ int n = Put(ResultBuffer, fragmentData, Bite, 6 + PacketLen);
+ fragmentLen = 0;
+ if (n != Bite)
+ return false;
+ }
+ else if (pesHeaderLen > 0) { // ... which is contained in the PES header buffer
+ int PacketLen = pesHeaderLen + Count - 6;
+ pesHeader[ 4 ] = PacketLen >> 8;
+ pesHeader[ 5 ] = PacketLen & 0xFF;
+ // just skip packets with no payload
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(pesHeader, pesHeaderLen, PesPayloadOffset) <= phInvalid)
+ LOG("cCommonRepacker: invalid PES packet encountered in header buffer!");
+ else if (6 + PacketLen <= PesPayloadOffset) {
+ pesHeaderLen = 0;
+ return true; // skip empty packet
+ }
+ // amount of data to put into result buffer: a negative Count value means
+ // to strip off any partially contained start code.
+ int Bite = pesHeaderLen + (Count >= 0 ? 0 : Count);
+ // put data into result buffer
+ int n = Put(ResultBuffer, pesHeader, Bite, 6 + PacketLen);
+ pesHeaderLen = 0;
+ if (n != Bite)
+ return false;
+ }
+ // append further payload
+ if (Count > 0) {
+ // amount of data to put into result buffer
+ int Bite = Count;
+ // put data into result buffer
+ int n = Put(ResultBuffer, Data, Bite, Bite);
+ if (n != Bite)
+ return false;
+ }
+ // we did it ;-)
+ return true;
+}
+
+// --- cVideoRepacker --------------------------------------------------------
+
+class cVideoRepacker : public cCommonRepacker {
+private:
+ enum eState {
+ syncing,
+ findPicture,
+ scanPicture
+ };
+ int state;
+ void HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel);
+ inline bool ScanDataForStartCodeSlow(const uchar *const Data);
+ inline bool ScanDataForStartCodeFast(const uchar *&Data, const uchar *Limit);
+ inline bool ScanDataForStartCode(const uchar *&Data, int &Done, int &Todo);
+ inline void AdjustCounters(const int Delta, int &Done, int &Todo);
+ inline bool ScanForEndOfPictureSlow(const uchar *&Data);
+ inline bool ScanForEndOfPictureFast(const uchar *&Data, const uchar *Limit);
+ inline bool ScanForEndOfPicture(const uchar *&Data, const uchar *Limit);
+public:
+ cVideoRepacker(void);
+ virtual void Reset(void);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int BreakAt(const uchar *Data, int Count);
+ };
+
+cVideoRepacker::cVideoRepacker(void)
+{
+ Reset();
+}
+
+void cVideoRepacker::Reset(void)
+{
+ cCommonRepacker::Reset();
+ scanner = 0xFFFFFFFF;
+ state = syncing;
+}
+
+void cVideoRepacker::HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel)
+{
+ // synchronisation is detected some bytes after frame start.
+ const int SkippedBytesLimit = 4;
+
+ // which kind of start code have we got?
+ switch (*Data) {
+ case 0xB9 ... 0xFF: // system start codes
+ LOG("cVideoRepacker: found system start code: stream seems to be scrambled or not demultiplexed");
+ break;
+ case 0xB0 ... 0xB1: // reserved start codes
+ case 0xB6:
+ LOG("cVideoRepacker: found reserved start code: stream seems to be scrambled");
+ break;
+ case 0xB4: // sequence error code
+ LOG("cVideoRepacker: found sequence error code: stream seems to be damaged");
+ case 0xB2: // user data start code
+ case 0xB5: // extension start code
+ break;
+ case 0xB7: // sequence end code
+ case 0xB3: // sequence header code
+ case 0xB8: // group start code
+ case 0x00: // picture start code
+ if (state == scanPicture) {
+ // the above start codes indicate that the current picture is done. So
+ // push out the packet to start a new packet for the next picuture. If
+ // the byte count get's negative then the current buffer ends in a
+ // partitial start code that must be stripped off, as it shall be put
+ // in the next packet.
+ PushOutPacket(ResultBuffer, Payload, Data - 3 - Payload);
+ // go on with syncing to the next picture
+ state = syncing;
+ }
+ if (state == syncing) {
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
+ LOG("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - SkippedBytesLimit);
+ skippedBytes = 0;
+ // if there is a PES header available, then use it ...
+ if (pesHeaderBackupLen > 0) {
+ // ISO 13818-1 says:
+ // In the case of video, if a PTS is present in a PES packet header
+ // it shall refer to the access unit containing the first picture start
+ // code that commences in this PES packet. A picture start code commences
+ // in PES packet if the first byte of the picture start code is present
+ // in the PES packet.
+ memcpy(pesHeader, pesHeaderBackup, pesHeaderBackupLen);
+ pesHeaderLen = pesHeaderBackupLen;
+ pesHeaderBackupLen = 0;
+ }
+ else {
+ // ... otherwise create a continuation PES header
+ pesHeaderLen = 0;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x01;
+ pesHeader[pesHeaderLen++] = StreamID; // video stream ID
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+
+ if (MpegLevel == phMPEG2) {
+ pesHeader[pesHeaderLen++] = 0x80;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ }
+ else
+ pesHeader[pesHeaderLen++] = 0x0F;
+ }
+ // append the first three bytes of the start code
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x01;
+ // the next packet's payload will begin with the fourth byte of
+ // the start code (= the actual code)
+ Payload = Data;
+ // as there is no length information available, assume the
+ // maximum we can hold in one PES packet
+ packetTodo = maxPacketSize - pesHeaderLen;
+ // go on with finding the picture data
+ state++;
+ }
+ break;
+ case 0x01 ... 0xAF: // slice start codes
+ if (state == findPicture) {
+ // go on with scanning the picture data
+ state++;
+ }
+ break;
+ }
+}
+
+bool cVideoRepacker::ScanDataForStartCodeSlow(const uchar *const Data)
+{
+ scanner <<= 8;
+ bool FoundStartCode = (scanner == 0x00000100);
+ scanner |= *Data;
+ return FoundStartCode;
+}
+
+bool cVideoRepacker::ScanDataForStartCodeFast(const uchar *&Data, const uchar *Limit)
+{
+ Limit--;
+
+ while (Data < Limit && (Data = (const uchar *)memchr(Data, 0x01, Limit - Data))) {
+ if (Data[-2] || Data[-1])
+ Data += 3;
+ else {
+ scanner = 0x00000100 | *++Data;
+ return true;
+ }
+ }
+
+ Data = Limit;
+ uint32_t *Scanner = (uint32_t *)(Data - 3);
+ scanner = ntohl(*Scanner);
+ return false;
+}
+
+bool cVideoRepacker::ScanDataForStartCode(const uchar *&Data, int &Done, int &Todo)
+{
+ const uchar *const DataOrig = Data;
+ const int MinDataSize = 4;
+
+ if (Todo < MinDataSize || (state != syncing && packetTodo < MinDataSize))
+ return ScanDataForStartCodeSlow(Data);
+
+ int Limit = Todo;
+ if (state != syncing && Limit > packetTodo)
+ Limit = packetTodo;
+
+ if (ScanDataForStartCodeSlow(Data))
+ return true;
+
+ if (ScanDataForStartCodeSlow(++Data)) {
+ AdjustCounters(1, Done, Todo);
+ return true;
+ }
+ ++Data;
+
+ bool FoundStartCode = ScanDataForStartCodeFast(Data, DataOrig + Limit);
+ AdjustCounters(Data - DataOrig, Done, Todo);
+ return FoundStartCode;
+}
+
+void cVideoRepacker::AdjustCounters(const int Delta, int &Done, int &Todo)
+{
+ Done += Delta;
+ Todo -= Delta;
+
+ if (state <= syncing)
+ skippedBytes += Delta;
+ else
+ packetTodo -= Delta;
+}
+
+void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
+{
+ // synchronisation is detected some bytes after frame start.
+ const int SkippedBytesLimit = 4;
+
+ // reset local scanner
+ localStart = -1;
+
+ int pesPayloadOffset = 0;
+ bool continuationHeader = false;
+ ePesHeader mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
+ if (mpegLevel <= phInvalid) {
+ DroppedData("cVideoRepacker: no valid PES packet header found", Count);
+ return;
+ }
+ if (!continuationHeader) {
+ // backup PES header
+ pesHeaderBackupLen = pesPayloadOffset;
+ memcpy(pesHeaderBackup, Data, pesHeaderBackupLen);
+ }
+
+ // skip PES header
+ int done = pesPayloadOffset;
+ int todo = Count - done;
+ const uchar *data = Data + done;
+ // remember start of the data
+ const uchar *payload = data;
+
+ while (todo > 0) {
+ // collect number of skipped bytes while syncing
+ if (state <= syncing)
+ skippedBytes++;
+ // did we reach a start code?
+ if (ScanDataForStartCode(data, done, todo))
+ HandleStartCode(data, ResultBuffer, payload, Data[3], mpegLevel);
+ // move on
+ data++;
+ done++;
+ todo--;
+ // do we have to start a new packet as there is no more space left?
+ if (state != syncing && --packetTodo <= 0) {
+ // we connot start a new packet here if the current might end in a start
+ // code and this start code shall possibly be put in the next packet. So
+ // overfill the current packet until we can safely detect that we won't
+ // break a start code into pieces:
+ //
+ // A) the last four bytes were a start code.
+ // B) the current byte introduces a start code.
+ // C) the last three bytes begin a start code.
+ //
+ // Todo : Data : Rule : Result
+ // -----:-------------------------------:------:-------
+ // : XX 00 00 00 01 YY|YY YY YY YY : :
+ // 0 : ^^| : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX 00 00 00 01|YY YY YY YY : :
+ // 0 : ^^| : B : wait
+ // -1 : |^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX 00 00 00|01 YY YY YY : :
+ // 0 : ^^| : C : wait
+ // -1 : |^^ : B : wait
+ // -2 : | ^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX XX 00 00|00 01 YY YY : :
+ // 0 : ^^| : C : wait
+ // -1 : |^^ : C : wait
+ // -2 : | ^^ : B : wait
+ // -3 : | ^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX XX XX 00|00 00 01 YY : :
+ // 0 : ^^| : C : wait
+ // -1 : |^^ : C : wait
+ // -2 : | ^^ : : push
+ // -----:-------------------------------:------:-------
+ bool A = ((scanner & 0xFFFFFF00) == 0x00000100);
+ bool B = ((scanner & 0xFFFFFF) == 0x000001);
+ bool C = ((scanner & 0xFF) == 0x00) && (packetTodo >= -1);
+ if (A || (!B && !C)) {
+ // actually we cannot push out an overfull packet. So we'll have to
+ // adjust the byte count and payload start as necessary. If the byte
+ // count get's negative we'll have to append the excess from fragment's
+ // tail to the next PES header.
+ int bite = data + packetTodo - payload;
+ const uchar *excessData = fragmentData + fragmentLen + bite;
+ // a negative byte count means to drop some bytes from the current
+ // fragment's tail, to not exceed the maximum packet size.
+ PushOutPacket(ResultBuffer, payload, bite);
+ // create a continuation PES header
+ pesHeaderLen = 0;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x01;
+ pesHeader[pesHeaderLen++] = Data[3]; // video stream ID
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+
+ if (mpegLevel == phMPEG2) {
+ pesHeader[pesHeaderLen++] = 0x80;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ }
+ else
+ pesHeader[pesHeaderLen++] = 0x0F;
+
+ // copy any excess data
+ while (bite++ < 0) {
+ // append the excess data here
+ pesHeader[pesHeaderLen++] = *excessData++;
+ packetTodo++;
+ }
+ // the next packet's payload will begin here
+ payload = data + packetTodo;
+ // as there is no length information available, assume the
+ // maximum we can hold in one PES packet
+ packetTodo += maxPacketSize - pesHeaderLen;
+ }
+ }
+ }
+ // the packet is done. Now store any remaining data into fragment buffer
+ // if we are no longer syncing.
+ if (state != syncing) {
+ // append the PES header ...
+ int bite = pesHeaderLen;
+ pesHeaderLen = 0;
+ if (bite > 0) {
+ memcpy(fragmentData + fragmentLen, pesHeader, bite);
+ fragmentLen += bite;
+ }
+ // append payload. It may contain part of a start code at it's end,
+ // which will be removed when the next packet gets processed.
+ bite = data - payload;
+ if (bite > 0) {
+ memcpy(fragmentData + fragmentLen, payload, bite);
+ fragmentLen += bite;
+ }
+ }
+ // report that syncing dropped some bytes
+ if (skippedBytes > SkippedBytesLimit) {
+ if (!initiallySyncing) // omit report for the typical initial case
+ LOG("cVideoRepacker: skipped %d bytes while syncing on next picture", skippedBytes - SkippedBytesLimit);
+ skippedBytes = SkippedBytesLimit;
+ }
+}
+
+bool cVideoRepacker::ScanForEndOfPictureSlow(const uchar *&Data)
+{
+ localScanner <<= 8;
+ localScanner |= *Data++;
+ // check start codes which follow picture data
+ switch (localScanner) {
+ case 0x00000100: // picture start code
+ case 0x000001B8: // group start code
+ case 0x000001B3: // sequence header code
+ case 0x000001B7: // sequence end code
+ return true;
+ }
+ return false;
+}
+
+bool cVideoRepacker::ScanForEndOfPictureFast(const uchar *&Data, const uchar *Limit)
+{
+ Limit--;
+
+ while (Data < Limit && (Data = (const uchar *)memchr(Data, 0x01, Limit - Data))) {
+ if (Data[-2] || Data[-1])
+ Data += 3;
+ else {
+ localScanner = 0x00000100 | *++Data;
+ // check start codes which follow picture data
+ switch (localScanner) {
+ case 0x00000100: // picture start code
+ case 0x000001B8: // group start code
+ case 0x000001B3: // sequence header code
+ case 0x000001B7: // sequence end code
+ Data++;
+ return true;
+ default:
+ Data += 3;
+ }
+ }
+ }
+
+ Data = Limit + 1;
+ uint32_t *LocalScanner = (uint32_t *)(Data - 4);
+ localScanner = ntohl(*LocalScanner);
+ return false;
+}
+
+bool cVideoRepacker::ScanForEndOfPicture(const uchar *&Data, const uchar *Limit)
+{
+ const uchar *const DataOrig = Data;
+ const int MinDataSize = 4;
+ bool FoundEndOfPicture;
+
+ if (Limit - Data <= MinDataSize) {
+ FoundEndOfPicture = false;
+ while (Data < Limit) {
+ if (ScanForEndOfPictureSlow(Data)) {
+ FoundEndOfPicture = true;
+ break;
+ }
+ }
+ }
+ else {
+ FoundEndOfPicture = true;
+ if (!ScanForEndOfPictureSlow(Data)) {
+ if (!ScanForEndOfPictureSlow(Data)) {
+ if (!ScanForEndOfPictureFast(Data, Limit))
+ FoundEndOfPicture = false;
+ }
+ }
+ }
+
+ localStart += (Data - DataOrig);
+ return FoundEndOfPicture;
+}
+
+int cVideoRepacker::BreakAt(const uchar *Data, int Count)
+{
+ if (initiallySyncing)
+ return -1; // fill the packet buffer completely until we have synced once
+
+ int PesPayloadOffset = 0;
+
+ if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= phInvalid)
+ return -1; // not enough data for test
+
+ // just detect end of picture
+ if (state == scanPicture) {
+ // setup local scanner
+ if (localStart < 0) {
+ localScanner = scanner;
+ localStart = 0;
+ }
+ // start where we've stopped at the last run
+ const uchar *data = Data + PesPayloadOffset + localStart;
+ const uchar *limit = Data + Count;
+ // scan data
+ if (ScanForEndOfPicture(data, limit))
+ return data - Data;
+ }
+ // just fill up packet and append next start code
+ return PesPayloadOffset + packetTodo + 4;
+}
+
+// --- cAudioRepacker --------------------------------------------------------
+
+class cAudioRepacker : public cCommonRepacker {
+private:
+ static int bitRates[2][3][16];
+ enum eState {
+ syncing,
+ scanFrame
+ };
+ int state;
+ int frameTodo;
+ int frameSize;
+ int cid;
+ static bool IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize = NULL);
+public:
+ cAudioRepacker(int Cid);
+ virtual void Reset(void);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int BreakAt(const uchar *Data, int Count);
+ };
+
+int cAudioRepacker::bitRates[2][3][16] = { // all values are specified as kbits/s
+ {
+ { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, // MPEG 1, Layer I
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, // MPEG 1, Layer II
+ { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 } // MPEG 1, Layer III
+ },
+ {
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, // MPEG 2, Layer I
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // MPEG 2, Layer II/III
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } // MPEG 2, Layer II/III
+ }
+ };
+
+cAudioRepacker::cAudioRepacker(int Cid)
+{
+ cid = Cid;
+ Reset();
+}
+
+void cAudioRepacker::Reset(void)
+{
+ cCommonRepacker::Reset();
+ scanner = 0;
+ state = syncing;
+ frameTodo = 0;
+ frameSize = 0;
+}
+
+bool cAudioRepacker::IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize)
+{
+ int syncword = (Header & 0xFFF00000) >> 20;
+ int id = (Header & 0x00080000) >> 19;
+ int layer = (Header & 0x00060000) >> 17;
+//int protection_bit = (Header & 0x00010000) >> 16;
+ int bitrate_index = (Header & 0x0000F000) >> 12;
+ int sampling_frequency = (Header & 0x00000C00) >> 10;
+ int padding_bit = (Header & 0x00000200) >> 9;
+//int private_bit = (Header & 0x00000100) >> 8;
+//int mode = (Header & 0x000000C0) >> 6;
+//int mode_extension = (Header & 0x00000030) >> 4;
+//int copyright = (Header & 0x00000008) >> 3;
+//int orignal_copy = (Header & 0x00000004) >> 2;
+ int emphasis = (Header & 0x00000003);
+
+ if (syncword != 0xFFF)
+ return false;
+
+ if (id == 0 && !Mpeg2) // reserved in MPEG 1
+ return false;
+
+ if (layer == 0) // reserved
+ return false;
+
+ if (bitrate_index == 0xF) // forbidden
+ return false;
+
+ if (sampling_frequency == 3) // reserved
+ return false;
+
+ if (emphasis == 2) // reserved
+ return false;
+
+ if (FrameSize) {
+ if (bitrate_index == 0)
+ *FrameSize = 0;
+ else {
+ static int samplingFrequencies[2][4] = { // all values are specified in Hz
+ { 44100, 48000, 32000, -1 }, // MPEG 1
+ { 22050, 24000, 16000, -1 } // MPEG 2
+ };
+
+ static int slots_per_frame[2][3] = {
+ { 12, 144, 144 }, // MPEG 1, Layer I, II, III
+ { 12, 144, 72 } // MPEG 2, Layer I, II, III
+ };
+
+ int mpegIndex = 1 - id;
+ int layerIndex = 3 - layer;
+
+ // Layer I (i. e., layerIndex == 0) has a larger slot size
+ int slotSize = (layerIndex == 0) ? 4 : 1; // bytes
+
+ int br = 1000 * bitRates[mpegIndex][layerIndex][bitrate_index]; // bits/s
+ int sf = samplingFrequencies[mpegIndex][sampling_frequency];
+
+ int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots
+
+ *FrameSize = (N + padding_bit) * slotSize; // bytes
+ }
+ }
+
+ return true;
+}
+
+void cAudioRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
+{
+ // synchronisation is detected some bytes after frame start.
+ const int SkippedBytesLimit = 4;
+
+ // reset local scanner
+ localStart = -1;
+
+ int pesPayloadOffset = 0;
+ bool continuationHeader = false;
+ ePesHeader mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
+ if (mpegLevel <= phInvalid) {
+ DroppedData("cAudioRepacker: no valid PES packet header found", Count);
+ return;
+ }
+ if (!continuationHeader) {
+ // backup PES header
+ pesHeaderBackupLen = pesPayloadOffset;
+ memcpy(pesHeaderBackup, Data, pesHeaderBackupLen);
+ }
+
+ // skip PES header
+ int done = pesPayloadOffset;
+ int todo = Count - done;
+ const uchar *data = Data + done;
+ // remember start of the data
+ const uchar *payload = data;
+
+ while (todo > 0) {
+ // collect number of skipped bytes while syncing
+ if (state <= syncing)
+ skippedBytes++;
+ // did we reach an audio frame header?
+ scanner <<= 8;
+ scanner |= *data;
+ if ((scanner & 0xFFF00000) == 0xFFF00000) {
+ if (frameTodo <= 0 && (frameSize == 0 || skippedBytes >= 4) && IsValidAudioHeader(scanner, mpegLevel == phMPEG2, &frameSize)) {
+ if (state == scanFrame) {
+ // As a new audio frame starts here, the previous one is done. So push
+ // out the packet to start a new packet for the next audio frame. If
+ // the byte count gets negative then the current buffer ends in a
+ // partitial audio frame header that must be stripped off, as it shall
+ // be put in the next packet.
+ PushOutPacket(ResultBuffer, payload, data - 3 - payload);
+ // go on with syncing to the next audio frame
+ state = syncing;
+ }
+ if (state == syncing) {
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
+ LOG("cAudioRepacker(0x%02X): skipped %d bytes to sync on next audio frame", cid, skippedBytes - SkippedBytesLimit);
+ skippedBytes = 0;
+ // if there is a PES header available, then use it ...
+ if (pesHeaderBackupLen > 0) {
+ // ISO 13818-1 says:
+ // In the case of audio, if a PTS is present in a PES packet header
+ // it shall refer to the access unit commencing in the PES packet. An
+ // audio access unit commences in a PES packet if the first byte of
+ // the audio access unit is present in the PES packet.
+ memcpy(pesHeader, pesHeaderBackup, pesHeaderBackupLen);
+ pesHeaderLen = pesHeaderBackupLen;
+ pesHeaderBackupLen = 0;
+ }
+ else {
+ // ... otherwise create a continuation PES header
+ pesHeaderLen = 0;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x01;
+ pesHeader[pesHeaderLen++] = Data[3]; // audio stream ID
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+
+ if (mpegLevel == phMPEG2) {
+ pesHeader[pesHeaderLen++] = 0x80;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ }
+ else
+ pesHeader[pesHeaderLen++] = 0x0F;
+ }
+ // append the first three bytes of the audio frame header
+ pesHeader[pesHeaderLen++] = 0xFF;
+ pesHeader[pesHeaderLen++] = (scanner >> 16) & 0xFF;
+ pesHeader[pesHeaderLen++] = (scanner >> 8) & 0xFF;
+ // the next packet's payload will begin with the fourth byte of
+ // the audio frame header (= the actual byte)
+ payload = data;
+ // maximum we can hold in one PES packet
+ packetTodo = maxPacketSize - pesHeaderLen;
+ // expected remainder of audio frame: so far we have read 3 bytes from the frame header
+ frameTodo = frameSize - 3;
+ // go on with collecting the frame's data
+ state++;
+ }
+ }
+ }
+ data++;
+ done++;
+ todo--;
+ // do we have to start a new packet as the current is done?
+ if (frameTodo > 0) {
+ if (--frameTodo == 0) {
+ // the current audio frame is is done now. So push out the packet to
+ // start a new packet for the next audio frame.
+ PushOutPacket(ResultBuffer, payload, data - payload);
+ // go on with syncing to the next audio frame
+ state = syncing;
+ }
+ }
+ // do we have to start a new packet as there is no more space left?
+ if (state != syncing && --packetTodo <= 0) {
+ // We connot start a new packet here if the current might end in an audio
+ // frame header and this header shall possibly be put in the next packet. So
+ // overfill the current packet until we can safely detect that we won't
+ // break an audio frame header into pieces:
+ //
+ // A) the last four bytes were an audio frame header.
+ // B) the last three bytes introduce an audio frame header.
+ // C) the last two bytes introduce an audio frame header.
+ // D) the last byte introduces an audio frame header.
+ //
+ // Todo : Data : Rule : Result
+ // -----:-------------------------------:------:-------
+ // : XX XX FF Fz zz zz|YY YY YY YY : :
+ // 0 : ^^| : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX FF Fz zz|zz YY YY YY : :
+ // 0 : ^^| : B : wait
+ // -1 : |^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX XX FF Fz|zz zz YY YY : :
+ // 0 : ^^| : C : wait
+ // -1 : |^^ : B : wait
+ // -2 : | ^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX XX XX FF|Fz zz zz YY : :
+ // 0 : ^^| : D : wait
+ // -1 : |^^ : C : wait
+ // -2 : | ^^ : B : wait
+ // -3 : | ^^ : A : push
+ // -----:-------------------------------:------:-------
+ bool A = ((scanner & 0xFFF00000) == 0xFFF00000);
+ bool B = ((scanner & 0xFFF000) == 0xFFF000);
+ bool C = ((scanner & 0xFFF0) == 0xFFF0);
+ bool D = ((scanner & 0xFF) == 0xFF);
+ if (A || (!B && !C && !D)) {
+ // Actually we cannot push out an overfull packet. So we'll have to
+ // adjust the byte count and payload start as necessary. If the byte
+ // count gets negative we'll have to append the excess from fragment's
+ // tail to the next PES header.
+ int bite = data + packetTodo - payload;
+ const uchar *excessData = fragmentData + fragmentLen + bite;
+ // A negative byte count means to drop some bytes from the current
+ // fragment's tail, to not exceed the maximum packet size.
+ PushOutPacket(ResultBuffer, payload, bite);
+ // create a continuation PES header
+ pesHeaderLen = 0;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x01;
+ pesHeader[pesHeaderLen++] = Data[3]; // audio stream ID
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+
+ if (mpegLevel == phMPEG2) {
+ pesHeader[pesHeaderLen++] = 0x80;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ }
+ else
+ pesHeader[pesHeaderLen++] = 0x0F;
+
+ // copy any excess data
+ while (bite++ < 0) {
+ // append the excess data here
+ pesHeader[pesHeaderLen++] = *excessData++;
+ packetTodo++;
+ }
+ // the next packet's payload will begin here
+ payload = data + packetTodo;
+ // as there is no length information available, assume the
+ // maximum we can hold in one PES packet
+ packetTodo += maxPacketSize - pesHeaderLen;
+ }
+ }
+ }
+ // The packet is done. Now store any remaining data into fragment buffer
+ // if we are no longer syncing.
+ if (state != syncing) {
+ // append the PES header ...
+ int bite = pesHeaderLen;
+ pesHeaderLen = 0;
+ if (bite > 0) {
+ memcpy(fragmentData + fragmentLen, pesHeader, bite);
+ fragmentLen += bite;
+ }
+ // append payload. It may contain part of an audio frame header at it's
+ // end, which will be removed when the next packet gets processed.
+ bite = data - payload;
+ if (bite > 0) {
+ memcpy(fragmentData + fragmentLen, payload, bite);
+ fragmentLen += bite;
+ }
+ }
+ // report that syncing dropped some bytes
+ if (skippedBytes > SkippedBytesLimit) {
+ if (!initiallySyncing) // omit report for the typical initial case
+ LOG("cAudioRepacker(0x%02X): skipped %d bytes while syncing on next audio frame", cid, skippedBytes - SkippedBytesLimit);
+ skippedBytes = SkippedBytesLimit;
+ }
+}
+
+int cAudioRepacker::BreakAt(const uchar *Data, int Count)
+{
+ if (initiallySyncing)
+ return -1; // fill the packet buffer completely until we have synced once
+
+ int PesPayloadOffset = 0;
+
+ ePesHeader MpegLevel = AnalyzePesHeader(Data, Count, PesPayloadOffset);
+ if (MpegLevel <= phInvalid)
+ return -1; // not enough data for test
+
+ // determine amount of data to fill up packet and to append next audio frame header
+ int packetRemainder = PesPayloadOffset + packetTodo + 4;
+
+ // just detect end of an audio frame
+ if (state == scanFrame) {
+ // when remaining audio frame size is known, then omit scanning
+ if (frameTodo > 0) {
+ // determine amount of data to fill up audio frame and to append next audio frame header
+ int remaining = PesPayloadOffset + frameTodo + 4;
+ if (remaining < packetRemainder)
+ return remaining;
+ return packetRemainder;
+ }
+ // setup local scanner
+ if (localStart < 0) {
+ localScanner = scanner;
+ localStart = 0;
+ }
+ // start where we've stopped at the last run
+ const uchar *data = Data + PesPayloadOffset + localStart;
+ const uchar *limit = Data + Count;
+ // scan data
+ while (data < limit) {
+ localStart++;
+ localScanner <<= 8;
+ localScanner |= *data++;
+ // check whether the next audio frame follows
+ if (((localScanner & 0xFFF00000) == 0xFFF00000) && IsValidAudioHeader(localScanner, MpegLevel == phMPEG2))
+ return data - Data;
+ }
+ }
+ // just fill up packet and append next audio frame header
+ return packetRemainder;
+}
+
+// --- cDolbyRepacker --------------------------------------------------------
+
+class cDolbyRepacker : public cRepacker {
+private:
+ static int frameSizes[];
+ uchar fragmentData[6 + 65535];
+ int fragmentLen;
+ int fragmentTodo;
+ uchar pesHeader[6 + 3 + 255 + 4 + 4];
+ int pesHeaderLen;
+ uchar pesHeaderBackup[6 + 3 + 255];
+ int pesHeaderBackupLen;
+ uchar chk1;
+ uchar chk2;
+ int ac3todo;
+ enum eState {
+ find_0b,
+ find_77,
+ store_chk1,
+ store_chk2,
+ get_length,
+ output_packet
+ };
+ int state;
+ int skippedBytes;
+ void ResetPesHeader(bool ContinuationFrame = false);
+ void AppendSubStreamID(bool ContinuationFrame = false);
+ bool FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite);
+ bool StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite);
+public:
+ cDolbyRepacker(void);
+ virtual void Reset(void);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int BreakAt(const uchar *Data, int Count);
+ };
+
+// frameSizes are in words, i. e. multiply them by 2 to get bytes
+int cDolbyRepacker::frameSizes[] = {
+ // fs = 48 kHz
+ 64, 64, 80, 80, 96, 96, 112, 112, 128, 128, 160, 160, 192, 192, 224, 224,
+ 256, 256, 320, 320, 384, 384, 448, 448, 512, 512, 640, 640, 768, 768, 896, 896,
+ 1024, 1024, 1152, 1152, 1280, 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // fs = 44.1 kHz
+ 69, 70, 87, 88, 104, 105, 121, 122, 139, 140, 174, 175, 208, 209, 243, 244,
+ 278, 279, 348, 349, 417, 418, 487, 488, 557, 558, 696, 697, 835, 836, 975, 976,
+ 1114, 1115, 1253, 1254, 1393, 1394, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // fs = 32 kHz
+ 96, 96, 120, 120, 144, 144, 168, 168, 192, 192, 240, 240, 288, 288, 336, 336,
+ 384, 384, 480, 480, 576, 576, 672, 672, 768, 768, 960, 960, 1152, 1152, 1344, 1344,
+ 1536, 1536, 1728, 1728, 1920, 1920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+
+cDolbyRepacker::cDolbyRepacker(void)
+{
+ pesHeader[0] = 0x00;
+ pesHeader[1] = 0x00;
+ pesHeader[2] = 0x01;
+ pesHeader[3] = 0xBD;
+ pesHeader[4] = 0x00;
+ pesHeader[5] = 0x00;
+ Reset();
+}
+
+void cDolbyRepacker::AppendSubStreamID(bool ContinuationFrame)
+{
+ if (subStreamId) {
+ pesHeader[pesHeaderLen++] = subStreamId;
+ // number of ac3 frames "starting" in this packet (1 by design).
+ pesHeader[pesHeaderLen++] = 0x01;
+ // offset to start of first ac3 frame (0 means "no ac3 frame starting"
+ // so 1 (by design) addresses the first byte after the next two bytes).
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = (ContinuationFrame ? 0x00 : 0x01);
+ }
+}
+
+void cDolbyRepacker::ResetPesHeader(bool ContinuationFrame)
+{
+ pesHeader[6] = 0x80;
+ pesHeader[7] = 0x00;
+ pesHeader[8] = 0x00;
+ pesHeaderLen = 9;
+ AppendSubStreamID(ContinuationFrame);
+}
+
+void cDolbyRepacker::Reset(void)
+{
+ cRepacker::Reset();
+ ResetPesHeader();
+ state = find_0b;
+ ac3todo = 0;
+ chk1 = 0;
+ chk2 = 0;
+ fragmentLen = 0;
+ fragmentTodo = 0;
+ pesHeaderBackupLen = 0;
+ skippedBytes = 0;
+}
+
+bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite)
+{
+ bool success = true;
+ // enough data available to put PES packet into buffer?
+ if (fragmentTodo <= Todo) {
+ // output a previous fragment first
+ if (fragmentLen > 0) {
+ Bite = fragmentLen;
+ int n = Put(ResultBuffer, fragmentData, Bite, fragmentLen + fragmentTodo);
+ if (Bite != n)
+ success = false;
+ fragmentLen = 0;
+ }
+ Bite = fragmentTodo;
+ if (success) {
+ int n = Put(ResultBuffer, Data, Bite, Bite);
+ if (Bite != n)
+ success = false;
+ }
+ fragmentTodo = 0;
+ // ac3 frame completely processed?
+ if (Bite >= ac3todo)
+ state = find_0b; // go on with finding start of next packet
+ }
+ else {
+ // copy the fragment into separate buffer for later processing
+ Bite = Todo;
+ memcpy(fragmentData + fragmentLen, Data, Bite);
+ fragmentLen += Bite;
+ fragmentTodo -= Bite;
+ }
+ return success;
+}
+
+bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite)
+{
+ bool success = true;
+ int packetLen = pesHeaderLen + ac3todo;
+ // limit packet to maximum size
+ if (packetLen > maxPacketSize)
+ packetLen = maxPacketSize;
+ pesHeader[4] = (packetLen - 6) >> 8;
+ pesHeader[5] = (packetLen - 6) & 0xFF;
+ Bite = pesHeaderLen;
+ // enough data available to put PES packet into buffer?
+ if (packetLen - pesHeaderLen <= Todo) {
+ int n = Put(ResultBuffer, pesHeader, Bite, packetLen);
+ if (Bite != n)
+ success = false;
+ Bite = packetLen - pesHeaderLen;
+ if (success) {
+ n = Put(ResultBuffer, Data, Bite, Bite);
+ if (Bite != n)
+ success = false;
+ }
+ // ac3 frame completely processed?
+ if (Bite >= ac3todo)
+ state = find_0b; // go on with finding start of next packet
+ }
+ else {
+ fragmentTodo = packetLen;
+ // copy the pesheader into separate buffer for later processing
+ memcpy(fragmentData + fragmentLen, pesHeader, Bite);
+ fragmentLen += Bite;
+ fragmentTodo -= Bite;
+ // copy the fragment into separate buffer for later processing
+ Bite = Todo;
+ memcpy(fragmentData + fragmentLen, Data, Bite);
+ fragmentLen += Bite;
+ fragmentTodo -= Bite;
+ }
+ return success;
+}
+
+void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
+{
+ // synchronisation is detected some bytes after frame start.
+ const int SkippedBytesLimit = 4;
+
+ // check for MPEG 2
+ if ((Data[6] & 0xC0) != 0x80) {
+ DroppedData("cDolbyRepacker: MPEG 2 PES header expected", Count);
+ return;
+ }
+
+ // backup PES header
+ if (Data[6] != 0x80 || Data[7] != 0x00 || Data[8] != 0x00) {
+ pesHeaderBackupLen = 6 + 3 + Data[8];
+ memcpy(pesHeaderBackup, Data, pesHeaderBackupLen);
+ }
+
+ // skip PES header
+ int done = 6 + 3 + Data[8];
+ int todo = Count - done;
+ const uchar *data = Data + done;
+
+ // look for 0x0B 0x77 <chk1> <chk2> <frameSize>
+ while (todo > 0) {
+ switch (state) {
+ case find_0b:
+ if (*data == 0x0B) {
+ state++;
+ // copy header information once for later use
+ if (pesHeaderBackupLen > 0) {
+ pesHeaderLen = pesHeaderBackupLen;
+ pesHeaderBackupLen = 0;
+ memcpy(pesHeader, pesHeaderBackup, pesHeaderLen);
+ AppendSubStreamID();
+ }
+ }
+ data++;
+ done++;
+ todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
+ continue;
+ case find_77:
+ if (*data != 0x77) {
+ state = find_0b;
+ continue;
+ }
+ data++;
+ done++;
+ todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
+ state++;
+ continue;
+ case store_chk1:
+ chk1 = *data++;
+ done++;
+ todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
+ state++;
+ continue;
+ case store_chk2:
+ chk2 = *data++;
+ done++;
+ todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
+ state++;
+ continue;
+ case get_length:
+ ac3todo = 2 * frameSizes[*data];
+ // frameSizeCode was invalid => restart searching
+ if (ac3todo <= 0) {
+ // reset PES header instead of using a wrong one
+ ResetPesHeader();
+ if (chk1 == 0x0B) {
+ if (chk2 == 0x77) {
+ state = store_chk1;
+ continue;
+ }
+ if (chk2 == 0x0B) {
+ state = find_77;
+ continue;
+ }
+ state = find_0b;
+ continue;
+ }
+ if (chk2 == 0x0B) {
+ state = find_77;
+ continue;
+ }
+ state = find_0b;
+ continue;
+ }
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
+ LOG("cDolbyRepacker: skipped %d bytes to sync on next AC3 frame", skippedBytes - SkippedBytesLimit);
+ skippedBytes = 0;
+ // append read data to header for common output processing
+ pesHeader[pesHeaderLen++] = 0x0B;
+ pesHeader[pesHeaderLen++] = 0x77;
+ pesHeader[pesHeaderLen++] = chk1;
+ pesHeader[pesHeaderLen++] = chk2;
+ ac3todo -= 4;
+ state++;
+ // fall through to output
+ case output_packet: {
+ int bite = 0;
+ // finish remainder of ac3 frame?
+ if (fragmentTodo > 0)
+ FinishRemainder(ResultBuffer, data, todo, bite);
+ else {
+ // start a new packet
+ StartNewPacket(ResultBuffer, data, todo, bite);
+ // prepare for next (continuation) packet
+ ResetPesHeader(state == output_packet);
+ }
+ data += bite;
+ done += bite;
+ todo -= bite;
+ ac3todo -= bite;
+ }
+ }
+ }
+ // report that syncing dropped some bytes
+ if (skippedBytes > SkippedBytesLimit) {
+ if (!initiallySyncing) // omit report for the typical initial case
+ LOG("cDolbyRepacker: skipped %d bytes while syncing on next AC3 frame", skippedBytes - 4);
+ skippedBytes = SkippedBytesLimit;
+ }
+}
+
+int cDolbyRepacker::BreakAt(const uchar *Data, int Count)
+{
+ if (initiallySyncing)
+ return -1; // fill the packet buffer completely until we have synced once
+ // enough data for test?
+ if (Count < 6 + 3)
+ return -1;
+ // check for MPEG 2
+ if ((Data[6] & 0xC0) != 0x80)
+ return -1;
+ int headerLen = Data[8] + 6 + 3;
+ // break after fragment tail?
+ if (ac3todo > 0)
+ return headerLen + ac3todo;
+ // enough data for test?
+ if (Count < headerLen + 5)
+ return -1;
+ const uchar *data = Data + headerLen;
+ // break after ac3 frame?
+ if (data[0] == 0x0B && data[1] == 0x77 && frameSizes[data[4]] > 0)
+ return headerLen + 2 * frameSizes[data[4]];
+ return -1;
+}
+
+// --- cTS2PES ---------------------------------------------------------------
+
+#include <netinet/in.h>
+
+//XXX TODO: these should really be available in some driver header file!
+#define PROG_STREAM_MAP 0xBC
+#ifndef PRIVATE_STREAM1
+#define PRIVATE_STREAM1 0xBD
+#endif
+#define PADDING_STREAM 0xBE
+#ifndef PRIVATE_STREAM2
+#define PRIVATE_STREAM2 0xBF
+#endif
+#define AUDIO_STREAM_S 0xC0
+#define AUDIO_STREAM_E 0xDF
+#define VIDEO_STREAM_S 0xE0
+#define VIDEO_STREAM_E 0xEF
+#define ECM_STREAM 0xF0
+#define EMM_STREAM 0xF1
+#define DSM_CC_STREAM 0xF2
+#define ISO13522_STREAM 0xF3
+#define PROG_STREAM_DIR 0xFF
+
+//pts_dts flags
+#define PTS_ONLY 0x80
+
+#define TS_SIZE 188
+#define PID_MASK_HI 0x1F
+#define CONT_CNT_MASK 0x0F
+
+// Flags:
+#define PAY_LOAD 0x10
+#define ADAPT_FIELD 0x20
+#define PAY_START 0x40
+#define TS_ERROR 0x80
+
+#define MAX_PLENGTH 0xFFFF // the maximum PES packet length (theoretically)
+#define MMAX_PLENGTH (64*MAX_PLENGTH) // some stations send PES packets that are extremely large, e.g. DVB-T in Finland or HDTV 1920x1080
+
+#define IPACKS 2048
+
+// Start codes:
+#define SC_SEQUENCE 0xB3 // "sequence header code"
+#define SC_GROUP 0xB8 // "group start code"
+#define SC_PICTURE 0x00 // "picture start code"
+
+#define MAXNONUSEFULDATA (10*1024*1024)
+#define MAXNUMUPTERRORS 10
+
+class cTS2PES {
+private:
+ int pid;
+ int size;
+ int found;
+ int count;
+ uint8_t *buf;
+ uint8_t cid;
+ uint8_t rewriteCid;
+ uint8_t subStreamId;
+ int plength;
+ uint8_t plen[2];
+ uint8_t flag1;
+ uint8_t flag2;
+ uint8_t hlength;
+ int mpeg;
+ uint8_t check;
+ int mpeg1_required;
+ int mpeg1_stuffing;
+ bool done;
+ cRingBufferLinear *resultBuffer;
+ int tsErrors;
+ int ccErrors;
+ int ccCounter;
+ cRepacker *repacker;
+ static uint8_t headr[];
+ void store(uint8_t *Data, int Count);
+ void reset_ipack(void);
+ void send_ipack(void);
+ void write_ipack(const uint8_t *Data, int Count);
+ void instant_repack(const uint8_t *Buf, int Count);
+public:
+ cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t RewriteCid = 0x00, uint8_t SubStreamId = 0x00, cRepacker *Repacker = NULL);
+ ~cTS2PES();
+ int Pid(void) { return pid; }
+ void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
+ void Clear(void);
+ };
+
+uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 };
+
+cTS2PES::cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t RewriteCid, uint8_t SubStreamId, cRepacker *Repacker)
+{
+ pid = Pid;
+ resultBuffer = ResultBuffer;
+ size = Size;
+ rewriteCid = RewriteCid;
+ subStreamId = SubStreamId;
+ repacker = Repacker;
+ if (repacker) {
+ repacker->SetMaxPacketSize(size);
+ repacker->SetSubStreamId(subStreamId);
+ size += repacker->QuerySnoopSize();
+ }
+
+ tsErrors = 0;
+ ccErrors = 0;
+ ccCounter = -1;
+
+ if (!(buf = MALLOC(uint8_t, size)))
+ esyslog("Not enough memory for ts_transform");
+
+ reset_ipack();
+}
+
+cTS2PES::~cTS2PES()
+{
+ if (tsErrors || ccErrors)
+ dsyslog("cTS2PES got %d TS errors, %d TS continuity errors", tsErrors, ccErrors);
+ free(buf);
+ delete repacker;
+}
+
+void cTS2PES::Clear(void)
+{
+ reset_ipack();
+ if (repacker)
+ repacker->Reset();
+}
+
+void cTS2PES::store(uint8_t *Data, int Count)
+{
+ if (repacker)
+ repacker->Repack(resultBuffer, Data, Count);
+ else
+ cRepacker::Put(resultBuffer, Data, Count, Count);
+}
+
+void cTS2PES::reset_ipack(void)
+{
+ found = 0;
+ cid = 0;
+ plength = 0;
+ flag1 = 0;
+ flag2 = 0;
+ hlength = 0;
+ mpeg = 0;
+ check = 0;
+ mpeg1_required = 0;
+ mpeg1_stuffing = 0;
+ done = false;
+ count = 0;
+}
+
+void cTS2PES::send_ipack(void)
+{
+ if (count <= ((mpeg == 2) ? 9 : 7)) // skip empty packets
+ return;
+ buf[3] = rewriteCid ? rewriteCid : cid;
+ buf[4] = (uint8_t)(((count - 6) & 0xFF00) >> 8);
+ buf[5] = (uint8_t)((count - 6) & 0x00FF);
+ store(buf, count);
+
+ switch (mpeg) {
+ case 2:
+ buf[6] = 0x80;
+ buf[7] = 0x00;
+ buf[8] = 0x00;
+ count = 9;
+ if (!repacker && subStreamId) {
+ buf[9] = subStreamId;
+ buf[10] = 1;
+ buf[11] = 0;
+ buf[12] = 1;
+ count = 13;
+ }
+ break;
+ case 1:
+ buf[6] = 0x0F;
+ count = 7;
+ break;
+ }
+}
+
+void cTS2PES::write_ipack(const uint8_t *Data, int Count)
+{
+ if (count < 6) {
+ memcpy(buf, headr, 3);
+ count = 6;
+ }
+
+ // determine amount of data to process
+ int bite = Count;
+ if (count + bite > size)
+ bite = size - count;
+ if (repacker) {
+ int breakAt = repacker->BreakAt(buf, count);
+ // avoid memcpy of data after break location
+ if (0 <= breakAt && breakAt < count + bite) {
+ bite = breakAt - count;
+ if (bite < 0) // should never happen
+ bite = 0;
+ }
+ }
+
+ memcpy(buf + count, Data, bite);
+ count += bite;
+
+ if (repacker) {
+ // determine break location
+ int breakAt = repacker->BreakAt(buf, count);
+ if (breakAt > size) // won't fit into packet?
+ breakAt = -1;
+ if (breakAt > count) // not enough data?
+ breakAt = -1;
+ // push out data before break location
+ if (breakAt > 0) {
+ // adjust bite if above memcpy was to large
+ bite -= count - breakAt;
+ count = breakAt;
+ send_ipack();
+ // recurse for data after break location
+ if (Count - bite > 0)
+ write_ipack(Data + bite, Count - bite);
+ }
+ }
+
+ // push out data when buffer is full
+ if (count >= size) {
+ send_ipack();
+ // recurse for remaining data
+ if (Count - bite > 0)
+ write_ipack(Data + bite, Count - bite);
+ }
+}
+
+void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
+{
+ int c = 0;
+
+ while (c < Count && (mpeg == 0 || (mpeg == 1 && found < mpeg1_required) || (mpeg == 2 && found < 9)) && (found < 5 || !done)) {
+ switch (found ) {
+ case 0:
+ case 1:
+ if (Buf[c] == 0x00)
+ found++;
+ else
+ found = 0;
+ c++;
+ break;
+ case 2:
+ if (Buf[c] == 0x01)
+ found++;
+ else if (Buf[c] != 0)
+ found = 0;
+ c++;
+ break;
+ case 3:
+ cid = 0;
+ switch (Buf[c]) {
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ done = true;
+ case PRIVATE_STREAM1:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ found++;
+ cid = Buf[c++];
+ break;
+ default:
+ found = 0;
+ break;
+ }
+ break;
+ case 4:
+ if (Count - c > 1) {
+ unsigned short *pl = (unsigned short *)(Buf + c);
+ plength = ntohs(*pl);
+ c += 2;
+ found += 2;
+ mpeg1_stuffing = 0;
+ }
+ else {
+ plen[0] = Buf[c];
+ found++;
+ return;
+ }
+ break;
+ case 5: {
+ plen[1] = Buf[c++];
+ unsigned short *pl = (unsigned short *)plen;
+ plength = ntohs(*pl);
+ found++;
+ mpeg1_stuffing = 0;
+ }
+ break;
+ case 6:
+ if (!done) {
+ flag1 = Buf[c++];
+ found++;
+ if (mpeg1_stuffing == 0) { // first stuffing iteration: determine MPEG level
+ if ((flag1 & 0xC0) == 0x80)
+ mpeg = 2;
+ else {
+ mpeg = 1;
+ mpeg1_required = 7;
+ }
+ }
+ if (mpeg == 1) {
+ if (flag1 == 0xFF) { // MPEG1 stuffing
+ if (++mpeg1_stuffing > 16)
+ found = 0; // invalid MPEG1 header
+ else { // ignore stuffing
+ found--;
+ if (plength > 0)
+ plength--;
+ }
+ }
+ else if ((flag1 & 0xC0) == 0x40) // STD_buffer_scale/size
+ mpeg1_required += 2;
+ else if (flag1 != 0x0F && (flag1 & 0xF0) != 0x20 && (flag1 & 0xF0) != 0x30)
+ found = 0; // invalid MPEG1 header
+ else {
+ flag2 = 0;
+ hlength = 0;
+ }
+ }
+ }
+ break;
+ case 7:
+ if (!done && (mpeg == 2 || mpeg1_required > 7)) {
+ flag2 = Buf[c++];
+ found++;
+ }
+ break;
+ case 8:
+ if (!done && (mpeg == 2 || mpeg1_required > 7)) {
+ hlength = Buf[c++];
+ found++;
+ if (mpeg == 1 && hlength != 0x0F && (hlength & 0xF0) != 0x20 && (hlength & 0xF0) != 0x30)
+ found = 0; // invalid MPEG1 header
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!plength)
+ plength = MMAX_PLENGTH - 6;
+
+ if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= mpeg1_required))) {
+ switch (cid) {
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case PRIVATE_STREAM1:
+
+ if (mpeg == 2 && found == 9 && count < found) { // make sure to not write the data twice by looking at count
+ write_ipack(&flag1, 1);
+ write_ipack(&flag2, 1);
+ write_ipack(&hlength, 1);
+ }
+
+ if (mpeg == 1 && found == mpeg1_required && count < found) { // make sure to not write the data twice by looking at count
+ write_ipack(&flag1, 1);
+ if (mpeg1_required > 7) {
+ write_ipack(&flag2, 1);
+ write_ipack(&hlength, 1);
+ }
+ }
+
+ if (mpeg == 2 && (flag2 & PTS_ONLY) && found < 14) {
+ while (c < Count && found < 14) {
+ write_ipack(Buf + c, 1);
+ c++;
+ found++;
+ }
+ if (c == Count)
+ return;
+ }
+
+ if (!repacker && subStreamId) {
+ while (c < Count && found < (hlength + 9) && found < plength + 6) {
+ write_ipack(Buf + c, 1);
+ c++;
+ found++;
+ }
+ if (found == (hlength + 9)) {
+ uchar sbuf[] = { 0x01, 0x00, 0x00 };
+ write_ipack(&subStreamId, 1);
+ write_ipack(sbuf, 3);
+ }
+ }
+
+ while (c < Count && found < plength + 6) {
+ int l = Count - c;
+ if (l + found > plength + 6)
+ l = plength + 6 - found;
+ write_ipack(Buf + c, l);
+ found += l;
+ c += l;
+ }
+
+ break;
+ }
+
+ if (done) {
+ if (found + Count - c < plength + 6) {
+ found += Count - c;
+ c = Count;
+ }
+ else {
+ c += plength + 6 - found;
+ found = plength + 6;
+ }
+ }
+
+ if (plength && found == plength + 6) {
+ if (plength == MMAX_PLENGTH - 6)
+ esyslog("ERROR: PES packet length overflow in remuxer (stream corruption)");
+ send_ipack();
+ reset_ipack();
+ if (c < Count)
+ instant_repack(Buf + c, Count - c);
+ }
+ }
+ return;
+}
+
+void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188)
+{
+ if (!Buf)
+ return;
+
+ if (Buf[1] & TS_ERROR)
+ tsErrors++;
+
+ if (!(Buf[3] & (ADAPT_FIELD | PAY_LOAD)))
+ return; // discard TS packet with adaption_field_control set to '00'.
+
+ if ((Buf[3] & PAY_LOAD) && ((Buf[3] ^ ccCounter) & CONT_CNT_MASK)) {
+ // This should check duplicates and packets which do not increase the counter.
+ // But as the errors usually come in bursts this should be enough to
+ // show you there is something wrong with signal quality.
+ if (ccCounter != -1 && ((Buf[3] ^ (ccCounter + 1)) & CONT_CNT_MASK)) {
+ ccErrors++;
+ // Enable this if you are having problems with signal quality.
+ // These are the errors I used to get with Nova-T when antenna
+ // was not positioned correcly (not transport errors). //tvr
+ //dsyslog("TS continuity error (%d)", ccCounter);
+ }
+ ccCounter = Buf[3] & CONT_CNT_MASK;
+ }
+
+ if (Buf[1] & PAY_START) {
+ if (found > 6) {
+ if (plength != MMAX_PLENGTH - 6 && plength != found - 6)
+ dsyslog("PES packet shortened to %d bytes (expected: %d bytes)", found, plength + 6);
+ plength = found - 6;
+ send_ipack();
+ reset_ipack();
+ }
+ found = 0;
+ }
+
+ uint8_t off = 0;
+
+ if (Buf[3] & ADAPT_FIELD) { // adaptation field?
+ off = Buf[4] + 1;
+ if (off + 4 > 187)
+ return;
+ }
+
+ if (Buf[3] & PAY_LOAD)
+ instant_repack(Buf + 4 + off, TS_SIZE - 4 - off);
+}
+
+// --- cRingBufferLinearPes --------------------------------------------------
+
+class cRingBufferLinearPes : public cStreamdevBuffer {
+protected:
+ virtual int DataReady(const uchar *Data, int Count);
+public:
+ cRingBufferLinearPes(int Size, int Margin = 0, bool Statistics = false, const char *Description = NULL)
+ :cStreamdevBuffer(Size, Margin, Statistics, Description) {}
+ };
+
+int cRingBufferLinearPes::DataReady(const uchar *Data, int Count)
+{
+ int c = cRingBufferLinear::DataReady(Data, Count);
+ if (!c && Count >= 6) {
+ if (!Data[0] && !Data[1] && Data[2] == 0x01) {
+ int Length = 6 + Data[4] * 256 + Data[5];
+ if (Length <= Count)
+ return Length;
+ }
+ }
+ return c;
+}
+
+// --- cTS2PESRemux ----------------------------------------------------------------
+
+#define RESULTBUFFERSIZE KILOBYTE(256)
+
+cTS2PESRemux::cTS2PESRemux(int VPid, const int *APids, const int *DPids, const int *SPids)
+{
+ noVideo = VPid == 0 || VPid == 1 || VPid == 0x1FFF;
+ synced = false;
+ skipped = 0;
+ numTracks = 0;
+ resultSkipped = 0;
+ resultBuffer = new cRingBufferLinearPes(RESULTBUFFERSIZE, IPACKS, false, "Result");
+ resultBuffer->SetTimeouts(100, 100);
+ if (VPid)
+#define TEST_cVideoRepacker
+#ifdef TEST_cVideoRepacker
+ ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0, 0x00, new cVideoRepacker);
+#else
+ ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0);
+#endif
+ if (APids) {
+ int n = 0;
+ while (*APids && numTracks < MAXTRACKS && n < MAXAPIDS) {
+#define TEST_cAudioRepacker
+#ifdef TEST_cAudioRepacker
+ ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n, 0x00, new cAudioRepacker(0xC0 + n));
+ n++;
+#else
+ ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n++);
+#endif
+ }
+ }
+ if (DPids) {
+ int n = 0;
+ while (*DPids && numTracks < MAXTRACKS && n < MAXDPIDS)
+ ts2pes[numTracks++] = new cTS2PES(*DPids++, resultBuffer, IPACKS, 0x00, 0x80 + n++, new cDolbyRepacker);
+ }
+ if (SPids) {
+ int n = 0;
+ while (*SPids && numTracks < MAXTRACKS && n < MAXSPIDS)
+ ts2pes[numTracks++] = new cTS2PES(*SPids++, resultBuffer, IPACKS, 0x00, 0x20 + n++);
+ }
+}
+
+cTS2PESRemux::~cTS2PESRemux()
+{
+ for (int t = 0; t < numTracks; t++)
+ delete ts2pes[t];
+ delete resultBuffer;
+}
+
+#define TS_SYNC_BYTE 0x47
+
+int cTS2PESRemux::Put(const uchar *Data, int Count)
+{
+ int used = 0;
+
+ // Make sure we are looking at a TS packet:
+
+ while (Count > TS_SIZE) {
+ if (Data[0] == TS_SYNC_BYTE && Data[TS_SIZE] == TS_SYNC_BYTE)
+ break;
+ Data++;
+ Count--;
+ used++;
+ }
+ if (used)
+ esyslog("ERROR: skipped %d byte to sync on TS packet", used);
+
+ // Convert incoming TS data into multiplexed PES:
+
+ for (int i = 0; i < Count; i += TS_SIZE) {
+ if (Count - i < TS_SIZE)
+ break;
+ if (Data[i] != TS_SYNC_BYTE)
+ break;
+ if (resultBuffer->Free() < 2 * IPACKS) {
+ resultBuffer->WaitForPut();
+ break; // A cTS2PES might write one full packet and also a small rest
+ }
+ int pid = cTSRemux::GetPid(Data + i + 1);
+ if (Data[i + 3] & 0x10) { // got payload
+ for (int t = 0; t < numTracks; t++) {
+ if (ts2pes[t]->Pid() == pid) {
+ ts2pes[t]->ts_to_pes(Data + i);
+ break;
+ }
+ }
+ }
+ used += TS_SIZE;
+ }
+
+ // Check if we're getting anywhere here:
+ if (!synced && skipped >= 0) {
+ if (skipped > MAXNONUSEFULDATA) {
+ esyslog("ERROR: no useful data seen within %d byte of video stream", skipped);
+ skipped = -1;
+ }
+ else
+ skipped += used;
+ }
+
+ return used;
+}
+
+uchar *cTS2PESRemux::Get(int &Count)
+{
+ // Remove any previously skipped data from the result buffer:
+
+ if (resultSkipped > 0) {
+ resultBuffer->Del(resultSkipped);
+ resultSkipped = 0;
+ }
+
+ // Check for frame borders:
+
+ Count = 0;
+ uchar *resultData = NULL;
+ int resultCount = 0;
+ uchar *data = resultBuffer->Get(resultCount);
+ if (data) {
+ for (int i = 0; i < resultCount - 3; i++) {
+ if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
+ int l = 0;
+ uchar StreamType = data[i + 3];
+ if (VIDEO_STREAM_S <= StreamType && StreamType <= VIDEO_STREAM_E) {
+ uchar pt = NO_PICTURE;
+ l = cTSRemux::ScanVideoPacket(data, resultCount, i, pt);
+ if (l < 0)
+ return resultData;
+ if (pt != NO_PICTURE) {
+ if (pt < I_FRAME || B_FRAME < pt) {
+ esyslog("ERROR: unknown picture type '%d'", pt);
+ }
+ else if (!synced) {
+ if (pt == I_FRAME) {
+ resultSkipped = i; // will drop everything before this position
+ cTSRemux::SetBrokenLink(data + i, l);
+ synced = true;
+ }
+ }
+ else if (Count)
+ return resultData;
+ }
+ }
+ else { //if (AUDIO_STREAM_S <= StreamType && StreamType <= AUDIO_STREAM_E || StreamType == PRIVATE_STREAM1) {
+ l = cTSRemux::GetPacketLength(data, resultCount, i);
+ if (l < 0)
+ return resultData;
+ if (noVideo) {
+ if (!synced) {
+ resultSkipped = i; // will drop everything before this position
+ synced = true;
+ }
+ else if (Count)
+ return resultData;
+ }
+ }
+ if (synced) {
+ if (!Count)
+ resultData = data + i;
+ Count += l;
+ }
+ else
+ resultSkipped = i + l;
+ if (l > 0)
+ i += l - 1; // the loop increments, too
+ }
+ }
+ }
+ return resultData;
+}
+
+void cTS2PESRemux::Del(int Count)
+{
+ resultBuffer->Del(Count);
+}
+
+void cTS2PESRemux::Clear(void)
+{
+ for (int t = 0; t < numTracks; t++)
+ ts2pes[t]->Clear();
+ resultBuffer->Clear();
+ synced = false;
+ skipped = 0;
+ resultSkipped = 0;
+}
+
+} // namespace Streamdev
diff --git a/plugins/streamdev/streamdev-cvs/remux/ts2pes.h b/plugins/streamdev/streamdev-cvs/remux/ts2pes.h
new file mode 100644
index 0000000..e9d7237
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/ts2pes.h
@@ -0,0 +1,56 @@
+/*
+ * ts2pes.h: A streaming MPEG2 remultiplexer
+ *
+ * This file is based on a copy of remux.h from Klaus Schmidinger's
+ * VDR, version 1.6.0.
+ *
+ * $Id: ts2pes.h,v 1.4 2009/07/06 06:11:11 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_TS2PES_H
+#define VDR_STREAMDEV_TS2PES_H
+
+#include "remux/tsremux.h"
+#include "server/streamer.h"
+
+#define MAXTRACKS 64
+
+namespace Streamdev {
+
+class cTS2PES;
+
+class cTS2PESRemux: public cTSRemux {
+private:
+ bool noVideo;
+ bool synced;
+ int skipped;
+ cTS2PES *ts2pes[MAXTRACKS];
+ int numTracks;
+ cStreamdevBuffer *resultBuffer;
+ int resultSkipped;
+public:
+ cTS2PESRemux(int VPid, const int *APids, const int *DPids, const int *SPids);
+ ///< Creates a new remuxer for the given PIDs. VPid is the video PID, while
+ ///< APids, DPids and SPids are pointers to zero terminated lists of audio,
+ ///< dolby and subtitle PIDs (the pointers may be NULL if there is no such
+ ///< PID).
+ virtual ~cTS2PESRemux();
+ int Put(const uchar *Data, int Count);
+ ///< Puts at most Count bytes of Data into the remuxer.
+ ///< \return Returns the number of bytes actually consumed from Data.
+ uchar *Get(int &Count);
+ ///< Gets all currently available data from the remuxer.
+ ///< \return Count contains the number of bytes the result points to, and
+ void Del(int Count);
+ ///< Deletes Count bytes from the remuxer. Count must be the number returned
+ ///< from a previous call to Get(). Several calls to Del() with fractions of
+ ///< a previously returned Count may be made, but the total sum of all Count
+ ///< values must be exactly what the previous Get() has returned.
+ void Clear(void);
+ ///< Clears the remuxer of all data it might still contain, keeping the PID
+ ///< settings as they are.
+ };
+
+} // namespace Streamdev
+
+#endif // VDR_STREAMDEV_TS2PES_H
diff --git a/plugins/streamdev/streamdev-cvs/remux/ts2ps.c b/plugins/streamdev/streamdev-cvs/remux/ts2ps.c
new file mode 100644
index 0000000..2a97dee
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/ts2ps.c
@@ -0,0 +1,218 @@
+#include "remux/ts2ps.h"
+#include "server/streamer.h"
+#include <vdr/channels.h>
+#include <vdr/device.h>
+
+namespace Streamdev {
+
+class cTS2PS {
+ friend void PutPES(uint8_t *Buffer, int Size, void *Data);
+
+private:
+ ipack m_Ipack;
+ int m_Pid;
+ cRingBufferLinear *m_ResultBuffer;
+
+public:
+ cTS2PS(cRingBufferLinear *ResultBuffer, int Pid, uint8_t AudioCid = 0x00);
+ ~cTS2PS();
+
+ void PutTSPacket(const uint8_t *Buffer);
+
+ int Pid(void) const { return m_Pid; }
+};
+
+void PutPES(uint8_t *Buffer, int Size, void *Data)
+{
+ cTS2PS *This = (cTS2PS*)Data;
+ int n = This->m_ResultBuffer->Put(Buffer, Size);
+ if (n != Size)
+ esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Size - n, Size);
+}
+
+} // namespace Streamdev
+using namespace Streamdev;
+
+cTS2PS::cTS2PS(cRingBufferLinear *ResultBuffer, int Pid, uint8_t AudioCid)
+{
+ m_ResultBuffer = ResultBuffer;
+ m_Pid = Pid;
+
+ init_ipack(&m_Ipack, IPACKS, PutPES, false);
+ m_Ipack.cid = AudioCid;
+ m_Ipack.data = (void*)this;
+}
+
+cTS2PS::~cTS2PS()
+{
+ free_ipack(&m_Ipack);
+}
+
+void cTS2PS::PutTSPacket(const uint8_t *Buffer)
+{
+ if (!Buffer)
+ return;
+
+ if (Buffer[1] & 0x80) { // ts error
+ // TODO
+ }
+
+ if (Buffer[1] & 0x40) { // payload start
+ if (m_Ipack.plength == MMAX_PLENGTH - 6 && m_Ipack.found > 6) {
+ m_Ipack.plength = m_Ipack.found - 6;
+ m_Ipack.found = 0;
+ send_ipack(&m_Ipack);
+ reset_ipack(&m_Ipack);
+ }
+ }
+
+ uint8_t off = 0;
+
+ if (Buffer[3] & 0x20) { // adaptation field?
+ off = Buffer[4] + 1;
+ if (off + 4 > TS_SIZE - 1)
+ return;
+ }
+
+ instant_repack((uint8_t*)(Buffer + 4 + off), TS_SIZE - 4 - off, &m_Ipack);
+}
+
+cTS2PSRemux::cTS2PSRemux(int VPid, const int *APids, const int *DPids, const int *SPids):
+ m_NumTracks(0),
+ m_ResultBuffer(new cStreamdevBuffer(WRITERBUFSIZE, IPACKS)),
+ m_ResultSkipped(0),
+ m_Skipped(0),
+ m_Synced(false),
+ m_IsRadio(VPid == 0 || VPid == 1 || VPid == 0x1FFF)
+{
+ m_ResultBuffer->SetTimeouts(100, 100);
+
+ if (VPid)
+ m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, VPid);
+ if (APids) {
+ int n = 0;
+ while (*APids && m_NumTracks < MAXTRACKS && n < MAXAPIDS)
+ m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, *APids++, 0xC0 + n++);
+ }
+ if (DPids) {
+ int n = 0;
+ while (*DPids && m_NumTracks < MAXTRACKS && n < MAXDPIDS)
+ m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, *DPids++, 0x80 + n++);
+ }
+}
+
+cTS2PSRemux::~cTS2PSRemux() {
+ for (int i = 0; i < m_NumTracks; ++i)
+ delete m_Remux[i];
+ delete m_ResultBuffer;
+}
+
+int cTS2PSRemux::Put(const uchar *Data, int Count)
+{
+ int used = 0;
+
+ // Make sure we are looking at a TS packet:
+ while (Count > TS_SIZE) {
+ if (Data[0] == TS_SYNC_BYTE && Data[TS_SIZE] == TS_SYNC_BYTE)
+ break;
+ Data++;
+ Count--;
+ used++;
+ }
+ if (used)
+ esyslog("ERROR: m_Skipped %d byte to sync on TS packet", used);
+
+ // Convert incoming TS data into multiplexed PS:
+
+ for (int i = 0; i < Count; i += TS_SIZE) {
+ if (Count - i < TS_SIZE)
+ break;
+ if (Data[i] != TS_SYNC_BYTE)
+ break;
+ if (m_ResultBuffer->Free() < 2 * IPACKS) {
+ m_ResultBuffer->WaitForPut();
+ break; // A cTS2PS might write one full packet and also a small rest
+ }
+ int pid = GetPid(Data + i + 1);
+ if (Data[i + 3] & 0x10) { // got payload
+ for (int t = 0; t < m_NumTracks; t++) {
+ if (m_Remux[t]->Pid() == pid) {
+ m_Remux[t]->PutTSPacket(Data + i);
+ break;
+ }
+ }
+ }
+ used += TS_SIZE;
+ }
+
+ // Check if we're getting anywhere here:
+ if (!m_Synced && m_Skipped >= 0)
+ m_Skipped += used;
+
+ return used;
+}
+
+uchar *cTS2PSRemux::Get(int &Count)
+{
+ // Remove any previously skipped data from the result buffer:
+
+ if (m_ResultSkipped > 0) {
+ m_ResultBuffer->Del(m_ResultSkipped);
+ m_ResultSkipped = 0;
+ }
+
+ // Special VPID case to enable recording radio channels:
+ if (m_IsRadio) {
+ // Force syncing of radio channels to avoid "no useful data" error
+ m_Synced = true;
+ return m_ResultBuffer->Get(Count);
+ }
+
+ // Check for frame borders:
+ Count = 0;
+ uchar *resultData = NULL;
+ int resultCount = 0;
+ uchar *data = m_ResultBuffer->Get(resultCount);
+ if (data) {
+ for (int i = 0; i < resultCount - 3; i++) {
+ if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
+ int l = 0;
+ uchar StreamType = data[i + 3];
+ if (VIDEO_STREAM_S <= StreamType && StreamType <= VIDEO_STREAM_E) {
+ uchar pt = NO_PICTURE;
+ l = ScanVideoPacket(data, resultCount, i, pt);
+ if (l < 0)
+ return resultData;
+ if (pt != NO_PICTURE) {
+ if (pt < I_FRAME || B_FRAME < pt) {
+ esyslog("ERROR: unknown picture type '%d'", pt);
+ }
+ else if (!m_Synced) {
+ if (pt == I_FRAME) {
+ m_ResultSkipped = i; // will drop everything before this position
+ SetBrokenLink(data + i, l);
+ m_Synced = true;
+ }
+ }
+ else if (Count)
+ return resultData;
+ }
+ } else {
+ l = GetPacketLength(data, resultCount, i);
+ if (l < 0)
+ return resultData;
+ }
+ if (m_Synced) {
+ if (!Count)
+ resultData = data + i;
+ Count += l;
+ } else
+ m_ResultSkipped = i + l;
+ if (l > 0)
+ i += l - 1; // the loop increments, too
+ }
+ }
+ }
+ return resultData;
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/remux/ts2ps.h b/plugins/streamdev/streamdev-cvs/remux/ts2ps.h
new file mode 100644
index 0000000..2380c15
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/ts2ps.h
@@ -0,0 +1,36 @@
+#ifndef VDR_STREAMDEV_TS2PSREMUX_H
+#define VDR_STREAMDEV_TS2PSREMUX_H
+
+#include "remux/tsremux.h"
+#include "server/streamer.h"
+
+#ifndef MAXTRACKS
+#define MAXTRACKS 64
+#endif
+
+namespace Streamdev {
+
+class cTS2PS;
+
+class cTS2PSRemux: public cTSRemux {
+private:
+ int m_NumTracks;
+ cTS2PS *m_Remux[MAXTRACKS];
+ cStreamdevBuffer *m_ResultBuffer;
+ int m_ResultSkipped;
+ int m_Skipped;
+ bool m_Synced;
+ bool m_IsRadio;
+
+public:
+ cTS2PSRemux(int VPid, const int *Apids, const int *Dpids, const int *Spids);
+ virtual ~cTS2PSRemux();
+
+ int Put(const uchar *Data, int Count);
+ uchar *Get(int &Count);
+ void Del(int Count) { m_ResultBuffer->Del(Count); }
+};
+
+} // namespace Streamdev
+
+#endif // VDR_STREAMDEV_TS2PSREMUX_H
diff --git a/plugins/streamdev/streamdev-cvs/remux/tsremux.c b/plugins/streamdev/streamdev-cvs/remux/tsremux.c
new file mode 100644
index 0000000..a503ed0
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/tsremux.c
@@ -0,0 +1,87 @@
+#include "remux/tsremux.h"
+
+#define SC_PICTURE 0x00 // "picture header"
+#define PID_MASK_HI 0x1F
+#define VIDEO_STREAM_S 0xE0
+
+using namespace Streamdev;
+
+void cTSRemux::SetBrokenLink(uchar *Data, int Length)
+{
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(Data, Length, PesPayloadOffset) >= phMPEG1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
+ for (int i = PesPayloadOffset; i < Length - 7; i++) {
+ if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) {
+ if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed
+ Data[i + 7] |= 0x20;
+ return;
+ }
+ }
+ dsyslog("SetBrokenLink: no GOP header found in video packet");
+ }
+ else
+ dsyslog("SetBrokenLink: no video packet in frame");
+}
+
+int cTSRemux::GetPid(const uchar *Data)
+{
+ return (((uint16_t)Data[0] & PID_MASK_HI) << 8) | (Data[1] & 0xFF);
+}
+
+int cTSRemux::GetPacketLength(const uchar *Data, int Count, int Offset)
+{
+ // Returns the length of the packet starting at Offset, or -1 if Count is
+ // too small to contain the entire packet.
+ int Length = (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
+ if (Length > 0 && Offset + Length <= Count)
+ return Length;
+ return -1;
+}
+
+int cTSRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType)
+{
+ // Scans the video packet starting at Offset and returns its length.
+ // If the return value is -1 the packet was not completely in the buffer.
+ int Length = GetPacketLength(Data, Count, Offset);
+ if (Length > 0) {
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) {
+ const uchar *p = Data + Offset + PesPayloadOffset + 2;
+ const uchar *pLimit = Data + Offset + Length - 3;
+#ifdef TEST_cVideoRepacker
+ // cVideoRepacker ensures that a new PES packet is started for a new sequence,
+ // group or picture which allows us to easily skip scanning through a huge
+ // amount of video data.
+ if (p < pLimit) {
+ if (p[-2] || p[-1] || p[0] != 0x01)
+ pLimit = 0; // skip scanning: packet doesn't start with 0x000001
+ else {
+ switch (p[1]) {
+ case SC_SEQUENCE:
+ case SC_GROUP:
+ case SC_PICTURE:
+ break;
+ default: // skip scanning: packet doesn't start a new sequence, group or picture
+ pLimit = 0;
+ }
+ }
+ }
+#endif
+ while (p < pLimit && (p = (const uchar *)memchr(p, 0x01, pLimit - p))) {
+ if (!p[-2] && !p[-1]) { // found 0x000001
+ switch (p[1]) {
+ case SC_PICTURE: PictureType = (p[3] >> 3) & 0x07;
+ return Length;
+ }
+ p += 4; // continue scanning after 0x01ssxxyy
+ }
+ else
+ p += 3; // continue scanning after 0x01xxyy
+ }
+ }
+ PictureType = NO_PICTURE;
+ return Length;
+ }
+ return -1;
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/remux/tsremux.h b/plugins/streamdev/streamdev-cvs/remux/tsremux.h
new file mode 100644
index 0000000..8f22b1d
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/remux/tsremux.h
@@ -0,0 +1,28 @@
+#ifndef VDR_STREAMDEV_TSREMUX_H
+#define VDR_STREAMDEV_TSREMUX_H
+
+#include "libdvbmpeg/transform.h"
+#include <vdr/remux.h>
+
+// Picture types:
+#define NO_PICTURE 0
+
+namespace Streamdev {
+
+class cTSRemux {
+public:
+ virtual ~cTSRemux() {};
+
+ virtual int Put(const uchar *Data, int Count) = 0;
+ virtual uchar *Get(int &Count) = 0;
+ virtual void Del(int Count) = 0;
+
+ static void SetBrokenLink(uchar *Data, int Length);
+ static int GetPid(const uchar *Data);
+ static int GetPacketLength(const uchar *Data, int Count, int Offset);
+ static int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
+};
+
+} // namespace Streamdev
+
+#endif // VDR_STREAMDEV_TSREMUX_H
diff --git a/plugins/streamdev/streamdev-cvs/server/CVS/Entries b/plugins/streamdev/streamdev-cvs/server/CVS/Entries
new file mode 100644
index 0000000..1a9cebe
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/CVS/Entries
@@ -0,0 +1,34 @@
+/component.c/1.4/Fri Feb 13 10:39:22 2009//
+/component.h/1.3/Fri Feb 13 10:39:22 2009//
+/componentHTTP.c/1.2/Mon May 9 20:22:29 2005//
+/componentHTTP.h/1.2/Mon May 9 20:22:29 2005//
+/componentIGMP.c/1.2/Fri Jul 3 21:44:19 2009//
+/componentIGMP.h/1.1/Fri Feb 13 10:39:22 2009//
+/componentVTP.c/1.2/Mon May 9 20:22:29 2005//
+/componentVTP.h/1.2/Mon May 9 20:22:29 2005//
+/connection.c/1.13/Fri Sep 18 10:43:26 2009//
+/connection.h/1.8/Fri Sep 18 10:43:26 2009//
+/connectionHTTP.c/1.17/Fri Jun 19 06:32:45 2009//
+/connectionHTTP.h/1.6/Tue Oct 14 11:05:48 2008//
+/connectionIGMP.c/1.1/Fri Feb 13 10:39:22 2009//
+/connectionIGMP.h/1.1/Fri Feb 13 10:39:22 2009//
+/connectionVTP.c/1.27/Fri Jan 29 12:03:02 2010//
+/connectionVTP.h/1.11/Wed Jul 1 10:46:16 2009//
+/livefilter.c/1.7/Fri Feb 13 13:02:40 2009//
+/livefilter.h/1.5/Mon Apr 7 14:27:31 2008//
+/livestreamer.c/1.30/Sat Feb 20 23:02:10 2010//
+/livestreamer.h/1.20/Mon Jul 6 06:23:29 2009//
+/menuHTTP.c/1.6/Sat Feb 20 22:18:14 2010//
+/menuHTTP.h/1.3/Tue Sep 15 10:39:18 2009//
+/recplayer.c/1.1/Wed Jul 1 11:00:49 2009//
+/recplayer.h/1.1/Wed Jul 1 11:00:49 2009//
+/server.c/1.10/Fri Feb 13 10:39:22 2009//
+/server.h/1.6/Wed Oct 22 11:59:32 2008//
+/setup.c/1.9/Tue Oct 13 06:38:47 2009//
+/setup.h/1.3/Fri Sep 18 10:43:26 2009//
+/streamer.c/1.19/Fri Jun 19 06:32:45 2009//
+/streamer.h/1.11/Fri Jun 19 06:32:45 2009//
+/suspend.c/1.3/Wed Oct 22 11:59:32 2008//
+/suspend.dat/1.1.1.1/Thu Dec 30 22:44:26 2004//
+/suspend.h/1.2/Wed Oct 22 11:59:32 2008//
+D
diff --git a/plugins/streamdev/streamdev-cvs/server/CVS/Entries.Log b/plugins/streamdev/streamdev-cvs/server/CVS/Entries.Log
new file mode 100644
index 0000000..5674fc6
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/CVS/Entries.Log
@@ -0,0 +1 @@
+A D/http////
diff --git a/plugins/streamdev/streamdev-cvs/server/CVS/Repository b/plugins/streamdev/streamdev-cvs/server/CVS/Repository
new file mode 100644
index 0000000..785255b
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/CVS/Repository
@@ -0,0 +1 @@
+streamdev/server
diff --git a/plugins/streamdev/streamdev-cvs/server/CVS/Root b/plugins/streamdev/streamdev-cvs/server/CVS/Root
new file mode 100644
index 0000000..2c7f6ce
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/CVS/Root
@@ -0,0 +1 @@
+:pserver:anoncvs@vdr-developer.org:/var/cvsroot
diff --git a/plugins/streamdev/streamdev-cvs/server/component.c b/plugins/streamdev/streamdev-cvs/server/component.c
new file mode 100644
index 0000000..70d861a
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/component.c
@@ -0,0 +1,49 @@
+/*
+ * $Id: component.c,v 1.4 2009/02/13 10:39:22 schmirl Exp $
+ */
+
+#include "server/component.h"
+#include "server/connection.h"
+
+cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp,
+ uint ListenPort, int Type, int IpProto):
+ m_Protocol(Protocol),
+ m_Listen(Type, IpProto),
+ m_ListenIp(ListenIp),
+ m_ListenPort(ListenPort)
+{
+}
+
+cServerComponent::~cServerComponent()
+{
+}
+
+bool cServerComponent::Initialize(void)
+{
+ if (!m_Listen.Listen(m_ListenIp, m_ListenPort, 5)) {
+ esyslog("Streamdev: Couldn't listen (%s) %s:%d: %m",
+ m_Protocol, m_ListenIp, m_ListenPort);
+ return false;
+ }
+ isyslog("Streamdev: Listening (%s) on port %d", m_Protocol, m_ListenPort);
+ return true;
+}
+
+void cServerComponent::Destruct(void)
+{
+ m_Listen.Close();
+}
+
+cServerConnection *cServerComponent::Accept(void)
+{
+ cServerConnection *client = NewClient();
+ if (client->Accept(m_Listen)) {
+ isyslog("Streamdev: Accepted new client (%s) %s:%d", m_Protocol,
+ client->RemoteIp().c_str(), client->RemotePort());
+ return client;
+ } else {
+ esyslog("Streamdev: Couldn't accept (%s): %m", m_Protocol);
+ delete client;
+ }
+ return NULL;
+}
diff --git a/plugins/streamdev/streamdev-cvs/server/component.h b/plugins/streamdev/streamdev-cvs/server/component.h
new file mode 100644
index 0000000..7efd4ba
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/component.h
@@ -0,0 +1,51 @@
+/*
+ * $Id: component.h,v 1.3 2009/02/13 10:39:22 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SERVERS_COMPONENT_H
+#define VDR_STREAMDEV_SERVERS_COMPONENT_H
+
+#include "tools/socket.h"
+#include "tools/select.h"
+
+#include <vdr/tools.h>
+
+class cServerConnection;
+
+/* Basic TCP listen server, all functions virtual if a derivation wants to do
+ things different */
+
+class cServerComponent: public cListObject {
+private:
+ const char *m_Protocol;
+ cTBSocket m_Listen;
+ const char *m_ListenIp;
+ uint m_ListenPort;
+
+protected:
+ /* Returns a new connection object for Accept() */
+ virtual cServerConnection *NewClient(void) = 0;
+
+public:
+ cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort, int Type = SOCK_STREAM, int IpProto = 0);
+ virtual ~cServerComponent();
+
+ /* Starts listening on the specified Port, override if you want to do things
+ different */
+ virtual bool Initialize(void);
+
+ /* Stops listening, override if you want to do things different */
+ virtual void Destruct(void);
+
+ /* Get the listening socket's file number */
+ virtual int Socket(void) const { return (int)m_Listen; }
+
+ /* Adds the listening socket to the Select object */
+ virtual void Add(cTBSelect &Select) const { Select.Add(m_Listen); }
+
+ /* Accepts the connection on a NewClient() object and calls the
+ Welcome() on it, override if you want to do things different */
+ virtual cServerConnection *Accept(void);
+};
+
+#endif // VDR_STREAMDEV_SERVERS_COMPONENT_H
diff --git a/plugins/streamdev/streamdev-cvs/server/componentHTTP.c b/plugins/streamdev/streamdev-cvs/server/componentHTTP.c
new file mode 100644
index 0000000..70e95e9
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/componentHTTP.c
@@ -0,0 +1,18 @@
+/*
+ * $Id: componentHTTP.c,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $
+ */
+
+#include "server/componentHTTP.h"
+#include "server/connectionHTTP.h"
+#include "server/setup.h"
+
+cComponentHTTP::cComponentHTTP(void):
+ cServerComponent("HTTP", StreamdevServerSetup.HTTPBindIP,
+ StreamdevServerSetup.HTTPServerPort)
+{
+}
+
+cServerConnection *cComponentHTTP::NewClient(void)
+{
+ return new cConnectionHTTP;
+}
diff --git a/plugins/streamdev/streamdev-cvs/server/componentHTTP.h b/plugins/streamdev/streamdev-cvs/server/componentHTTP.h
new file mode 100644
index 0000000..29dc087
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/componentHTTP.h
@@ -0,0 +1,18 @@
+/*
+ * $Id: componentHTTP.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_HTTPSERVER_H
+#define VDR_STREAMDEV_HTTPSERVER_H
+
+#include "server/component.h"
+
+class cComponentHTTP: public cServerComponent {
+protected:
+ virtual cServerConnection *NewClient(void);
+
+public:
+ cComponentHTTP(void);
+};
+
+#endif // VDR_STREAMDEV_HTTPSERVER_H
diff --git a/plugins/streamdev/streamdev-cvs/server/componentIGMP.c b/plugins/streamdev/streamdev-cvs/server/componentIGMP.c
new file mode 100644
index 0000000..d9f8811
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/componentIGMP.c
@@ -0,0 +1,447 @@
+/*
+ * $Id: componentIGMP.c,v 1.2 2009/07/03 21:44:19 schmirl Exp $
+ */
+#include <netinet/ip.h>
+#include <netinet/igmp.h>
+
+#include "server/componentIGMP.h"
+#include "server/connectionIGMP.h"
+#include "server/setup.h"
+
+#ifndef IGMP_ALL_HOSTS
+#define IGMP_ALL_HOSTS htonl(0xE0000001L)
+#endif
+#ifndef IGMP_ALL_ROUTER
+#define IGMP_ALL_ROUTER htonl(0xE0000002L)
+#endif
+
+// IGMP parameters according to RFC2236. All time values in seconds.
+#define IGMP_ROBUSTNESS 2
+#define IGMP_QUERY_INTERVAL 125
+#define IGMP_QUERY_RESPONSE_INTERVAL 10
+#define IGMP_GROUP_MEMBERSHIP_INTERVAL (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL)
+#define IGMP_OTHER_QUERIER_PRESENT_INTERVAL (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL / 2)
+#define IGMP_STARTUP_QUERY_INTERVAL (IGMP_QUERY_INTERVAL / 4)
+#define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS
+// This value is 1/10 sec. RFC default is 10. Reduced to minimum to free unused channels ASAP
+#define IGMP_LAST_MEMBER_QUERY_INTERVAL_TS 1
+#define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS
+
+// operations on struct timeval
+#define TV_CMP(a, cmp, b) (a.tv_sec == b.tv_sec ? a.tv_usec cmp b.tv_usec : a.tv_sec cmp b.tv_sec)
+#define TV_SET(tv) (tv.tv_sec || tv.tv_usec)
+#define TV_CLR(tv) memset(&tv, 0, sizeof(tv))
+#define TV_CPY(dst, src) memcpy(&dst, &src, sizeof(dst))
+#define TV_ADD(dst, ts) dst.tv_sec += ts / 10; dst.tv_usec += (ts % 10) * 100000; if (dst.tv_usec >= 1000000) { dst.tv_usec -= 1000000; dst.tv_sec++; }
+
+class cMulticastGroup: public cListObject
+{
+public:
+ cConnectionIGMP *connection;
+ in_addr_t group;
+ in_addr_t reporter;
+ struct timeval timeout;
+ struct timeval v1timer;
+ struct timeval retransmit;
+
+ cMulticastGroup(in_addr_t Group);
+};
+
+cMulticastGroup::cMulticastGroup(in_addr_t Group) :
+ connection(NULL),
+ group(Group),
+ reporter(0)
+{
+ TV_CLR(timeout);
+ TV_CLR(v1timer);
+ TV_CLR(retransmit);
+}
+
+void logIGMP(uint8_t type, struct in_addr Src, struct in_addr Dst, struct in_addr Grp)
+{
+ const char* msg;
+ switch (type) {
+ case IGMP_MEMBERSHIP_QUERY: msg = "membership query"; break;
+ case IGMP_V1_MEMBERSHIP_REPORT: msg = "V1 membership report"; break;
+ case IGMP_V2_MEMBERSHIP_REPORT: msg = "V2 membership report"; break;
+ case IGMP_V2_LEAVE_GROUP: msg = "leave group"; break;
+ default: msg = "unknown"; break;
+ }
+ char* s = strdup(inet_ntoa(Src));
+ char* d = strdup(inet_ntoa(Dst));
+ dsyslog("streamdev-server IGMP: Received %s from %s (dst %s) for %s", msg, s, d, inet_ntoa(Grp));
+ free(s);
+ free(d);
+}
+
+/* Taken from http://tools.ietf.org/html/rfc1071 */
+uint16_t inetChecksum(uint16_t *addr, int count)
+{
+ uint32_t sum = 0;
+ while (count > 1) {
+ sum += *addr++;
+ count -= 2;
+ }
+
+ if( count > 0 )
+ sum += * (uint8_t *) addr;
+
+ while (sum>>16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return ~sum;
+}
+
+cComponentIGMP::cComponentIGMP(void):
+ cServerComponent("IGMP", "0.0.0.0", 0, SOCK_RAW, IPPROTO_IGMP),
+ cThread("IGMP timeout handler"),
+ m_BindIp(inet_addr(StreamdevServerSetup.IGMPBindIP)),
+ m_MaxChannelNumber(0),
+ m_StartupQueryCount(IGMP_STARTUP_QUERY_COUNT),
+ m_Querier(true)
+{
+}
+
+cComponentIGMP::~cComponentIGMP(void)
+{
+}
+
+cMulticastGroup* cComponentIGMP::FindGroup(in_addr_t Group) const
+{
+ cMulticastGroup *group = m_Groups.First();
+ while (group && group->group != Group)
+ group = m_Groups.Next(group);
+ return group;
+}
+
+bool cComponentIGMP::Initialize(void)
+{
+ if (cServerComponent::Initialize() && IGMPMembership(IGMP_ALL_ROUTER))
+ {
+ for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+ {
+ if (channel->GroupSep())
+ continue;
+ int num = channel->Number();
+ if (!IGMPMembership(htonl(MULTICAST_PRIV_MIN + num)))
+ break;
+ m_MaxChannelNumber = num;
+ }
+ if (m_MaxChannelNumber == 0)
+ {
+ IGMPMembership(IGMP_ALL_ROUTER, false);
+ esyslog("streamdev-server IGMP: no multicast group joined");
+ }
+ else
+ {
+ Start();
+ }
+ }
+ return m_MaxChannelNumber > 0;
+}
+
+void cComponentIGMP::Destruct(void)
+{
+ if (m_MaxChannelNumber > 0)
+ {
+ Cancel(3);
+ for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+ {
+ if (channel->GroupSep())
+ continue;
+ int num = channel->Number();
+ if (num > m_MaxChannelNumber)
+ break;
+ IGMPMembership(htonl(MULTICAST_PRIV_MIN + num), false);
+ }
+ IGMPMembership(IGMP_ALL_ROUTER, false);
+ }
+ m_MaxChannelNumber = 0;
+ cServerComponent::Destruct();
+}
+
+cServerConnection *cComponentIGMP::NewClient(void)
+{
+ return new cConnectionIGMP("IGMP", StreamdevServerSetup.IGMPClientPort, (eStreamType) StreamdevServerSetup.IGMPStreamType);
+}
+
+cServerConnection* cComponentIGMP::Accept(void)
+{
+ ssize_t recv_len;
+ int ip_hdrlen, ip_datalen;
+ struct ip *ip;
+ struct igmp *igmp;
+
+ while ((recv_len = ::recvfrom(Socket(), m_ReadBuffer, sizeof(m_ReadBuffer), 0, NULL, NULL)) < 0 && errno == EINTR)
+ errno = 0;
+
+ if (recv_len < 0) {
+ esyslog("streamdev-server IGMP: read failed: %m");
+ return NULL;
+ }
+ else if (recv_len < (ssize_t) sizeof(struct ip)) {
+ esyslog("streamdev-server IGMP: IP packet too short");
+ return NULL;
+ }
+
+ ip = (struct ip*) m_ReadBuffer;
+
+ // filter out my own packets
+ if (ip->ip_src.s_addr == m_BindIp)
+ return NULL;
+
+ ip_hdrlen = ip->ip_hl << 2;
+#ifdef __FreeBSD__
+ ip_datalen = ip->ip_len;
+#else
+ ip_datalen = ntohs(ip->ip_len) - ip_hdrlen;
+#endif
+ if (ip->ip_p != IPPROTO_IGMP) {
+ esyslog("streamdev-server IGMP: Unexpected protocol %hhu", ip->ip_p);
+ return NULL;
+ }
+ if (recv_len < ip_hdrlen + IGMP_MINLEN) {
+ esyslog("streamdev-server IGMP: packet too short");
+ return NULL;
+ }
+ igmp = (struct igmp*) (m_ReadBuffer + ip_hdrlen);
+ uint16_t chksum = igmp->igmp_cksum;
+ igmp->igmp_cksum = 0;
+ if (chksum != inetChecksum((uint16_t *)igmp, ip_datalen))
+ {
+ esyslog("INVALID CHECKSUM %d %d %d %lu 0x%x 0x%x", (int) ntohs(ip->ip_len), ip_hdrlen, ip_datalen, (unsigned long int) recv_len, chksum, inetChecksum((uint16_t *)igmp, ip_datalen));
+ return NULL;
+ }
+ logIGMP(igmp->igmp_type, ip->ip_src, ip->ip_dst, igmp->igmp_group);
+ return ProcessMessage(igmp, igmp->igmp_group.s_addr, ip->ip_src.s_addr);
+}
+
+cServerConnection* cComponentIGMP::ProcessMessage(struct igmp *Igmp, in_addr_t Group, in_addr_t Sender)
+{
+ cServerConnection* conn = NULL;
+ cMulticastGroup* group;
+ LOCK_THREAD;
+ switch (Igmp->igmp_type) {
+ case IGMP_MEMBERSHIP_QUERY:
+ if (ntohl(Sender) < ntohl(m_BindIp))
+ IGMPStartOtherQuerierPresentTimer();
+ break;
+ case IGMP_V1_MEMBERSHIP_REPORT:
+ case IGMP_V2_MEMBERSHIP_REPORT:
+ group = FindGroup(Group);
+ if (!group) {
+ group = new cMulticastGroup(Group);
+ m_Groups.Add(group);
+ }
+ if (!group->connection) {
+ IGMPStartMulticast(group);
+ conn = group->connection;
+ }
+ IGMPStartTimer(group, Sender);
+ if (Igmp->igmp_type == IGMP_V1_MEMBERSHIP_REPORT)
+ IGMPStartV1HostTimer(group);
+ break;
+ case IGMP_V2_LEAVE_GROUP:
+ group = FindGroup(Group);
+ if (group && !TV_SET(group->v1timer)) {
+ if (group->reporter == Sender) {
+ IGMPStartTimerAfterLeave(group, m_Querier ? IGMP_LAST_MEMBER_QUERY_INTERVAL_TS : Igmp->igmp_code);
+ if (m_Querier)
+ IGMPSendGroupQuery(group);
+ IGMPStartRetransmitTimer(group);
+ }
+ m_CondWait.Signal();
+ }
+ break;
+ default:
+ break;
+ }
+ return conn;
+}
+
+void cComponentIGMP::Action()
+{
+ while (Running()) {
+ struct timeval now;
+ struct timeval next;
+
+ gettimeofday(&now, NULL);
+ TV_CPY(next, now);
+ next.tv_sec += IGMP_QUERY_INTERVAL;
+
+ cMulticastGroup *del = NULL;
+ {
+ LOCK_THREAD;
+ if (TV_CMP(m_GeneralQueryTimer, <, now)) {
+ dsyslog("General Query");
+ IGMPSendGeneralQuery();
+ IGMPStartGeneralQueryTimer();
+ }
+ if (TV_CMP(next, >, m_GeneralQueryTimer))
+ TV_CPY(next, m_GeneralQueryTimer);
+
+ for (cMulticastGroup *group = m_Groups.First(); group; group = m_Groups.Next(group)) {
+ if (TV_CMP(group->timeout, <, now)) {
+ IGMPStopMulticast(group);
+ IGMPClearRetransmitTimer(group);
+ if (del)
+ m_Groups.Del(del);
+ del = group;
+ }
+ else if (m_Querier && TV_SET(group->retransmit) && TV_CMP(group->retransmit, <, now)) {
+ IGMPSendGroupQuery(group);
+ IGMPStartRetransmitTimer(group);
+ if (TV_CMP(next, >, group->retransmit))
+ TV_CPY(next, group->retransmit);
+ }
+ else if (TV_SET(group->v1timer) && TV_CMP(group->v1timer, <, now)) {
+ TV_CLR(group->v1timer);
+ }
+ else {
+ if (TV_CMP(next, >, group->timeout))
+ TV_CPY(next, group->timeout);
+ if (TV_SET(group->retransmit) && TV_CMP(next, >, group->retransmit))
+ TV_CPY(next, group->retransmit);
+ if (TV_SET(group->v1timer) && TV_CMP(next, >, group->v1timer))
+ TV_CPY(next, group->v1timer);
+ }
+ }
+ if (del)
+ m_Groups.Del(del);
+ }
+
+ int sleep = (next.tv_sec - now.tv_sec) * 1000;
+ sleep += (next.tv_usec - now.tv_usec) / 1000;
+ if (next.tv_usec < now.tv_usec)
+ sleep += 1000;
+ dsyslog("Sleeping %d ms", sleep);
+ m_CondWait.Wait(sleep);
+ }
+}
+
+bool cComponentIGMP::IGMPMembership(in_addr_t Group, bool Add)
+{
+ struct ip_mreqn mreq;
+ mreq.imr_multiaddr.s_addr = Group;
+ mreq.imr_address.s_addr = INADDR_ANY;
+ mreq.imr_ifindex = 0;
+ if (setsockopt(Socket(), IPPROTO_IP, Add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
+ {
+ esyslog("streamdev-server IGMP: unable to %s %s: %m", Add ? "join" : "leave", inet_ntoa(mreq.imr_multiaddr));
+ if (errno == ENOBUFS)
+ esyslog("consider increasing sys.net.ipv4.igmp_max_memberships");
+ return false;
+ }
+ return true;
+}
+
+void cComponentIGMP::IGMPSendQuery(in_addr_t Group, int Timeout)
+{
+ struct sockaddr_in dst;
+ struct igmp query;
+
+ dst.sin_family = AF_INET;
+ dst.sin_port = IPPROTO_IGMP;
+ dst.sin_addr.s_addr = Group;
+ query.igmp_type = IGMP_MEMBERSHIP_QUERY;
+ query.igmp_code = Timeout * 10;
+ query.igmp_cksum = 0;
+ query.igmp_group.s_addr = (Group == IGMP_ALL_HOSTS) ? 0 : Group;
+ query.igmp_cksum = inetChecksum((uint16_t *) &query, sizeof(query));
+
+ for (int i = 0; i < 5 && ::sendto(Socket(), &query, sizeof(query), 0, (sockaddr*)&dst, sizeof(dst)) == -1; i++) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ esyslog("streamdev-server IGMP: unable to query group %s: %m", inet_ntoa(dst.sin_addr));
+ break;
+ }
+ cCondWait::SleepMs(10);
+ }
+}
+
+// Querier state actions
+void cComponentIGMP::IGMPStartGeneralQueryTimer()
+{
+ m_Querier = true;
+ if (m_StartupQueryCount) {
+ gettimeofday(&m_GeneralQueryTimer, NULL);
+ m_GeneralQueryTimer.tv_sec += IGMP_STARTUP_QUERY_INTERVAL;
+ m_StartupQueryCount--;
+ }
+ else {
+ gettimeofday(&m_GeneralQueryTimer, NULL);
+ m_GeneralQueryTimer.tv_sec += IGMP_QUERY_INTERVAL;
+ }
+}
+
+void cComponentIGMP::IGMPStartOtherQuerierPresentTimer()
+{
+ m_Querier = false;
+ m_StartupQueryCount = 0;
+ gettimeofday(&m_GeneralQueryTimer, NULL);
+ m_GeneralQueryTimer.tv_sec += IGMP_OTHER_QUERIER_PRESENT_INTERVAL;
+}
+
+void cComponentIGMP::IGMPSendGeneralQuery()
+{
+ IGMPSendQuery(IGMP_ALL_HOSTS, IGMP_QUERY_RESPONSE_INTERVAL);
+}
+
+// Group state actions
+void cComponentIGMP::IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member)
+{
+ gettimeofday(&Group->timeout, NULL);
+ Group->timeout.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL;
+ TV_CLR(Group->retransmit);
+ Group->reporter = Member;
+
+}
+
+void cComponentIGMP::IGMPStartV1HostTimer(cMulticastGroup* Group)
+{
+ gettimeofday(&Group->v1timer, NULL);
+ Group->v1timer.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL;
+}
+
+void cComponentIGMP::IGMPStartTimerAfterLeave(cMulticastGroup* Group, unsigned int MaxResponseTimeTs)
+{
+ //Group->Update(time(NULL) + MaxResponseTime * IGMP_LAST_MEMBER_QUERY_COUNT / 10);
+ MaxResponseTimeTs *= IGMP_LAST_MEMBER_QUERY_COUNT;
+ gettimeofday(&Group->timeout, NULL);
+ TV_ADD(Group->timeout, MaxResponseTimeTs);
+ TV_CLR(Group->retransmit);
+ Group->reporter = 0;
+}
+
+void cComponentIGMP::IGMPStartRetransmitTimer(cMulticastGroup* Group)
+{
+ gettimeofday(&Group->retransmit, NULL);
+ TV_ADD(Group->retransmit, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS);
+}
+
+void cComponentIGMP::IGMPClearRetransmitTimer(cMulticastGroup* Group)
+{
+ TV_CLR(Group->retransmit);
+}
+
+void cComponentIGMP::IGMPSendGroupQuery(cMulticastGroup* Group)
+{
+ IGMPSendQuery(Group->group, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS);
+}
+
+void cComponentIGMP::IGMPStartMulticast(cMulticastGroup* Group)
+{
+ in_addr_t g = ntohl(Group->group);
+ if (g > MULTICAST_PRIV_MIN && g <= MULTICAST_PRIV_MAX) {
+ cChannel *channel = Channels.GetByNumber(g - MULTICAST_PRIV_MIN);
+ Group->connection = (cConnectionIGMP*) NewClient();
+ if (!Group->connection->Start(channel, Group->group)) {
+ DELETENULL(Group->connection);
+ }
+ }
+}
+
+void cComponentIGMP::IGMPStopMulticast(cMulticastGroup* Group)
+{
+ if (Group->connection)
+ Group->connection->Stop();
+}
diff --git a/plugins/streamdev/streamdev-cvs/server/componentIGMP.h b/plugins/streamdev/streamdev-cvs/server/componentIGMP.h
new file mode 100644
index 0000000..09d8fde
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/componentIGMP.h
@@ -0,0 +1,62 @@
+/*
+ * $Id: componentIGMP.h,v 1.1 2009/02/13 10:39:22 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_IGMPSERVER_H
+#define VDR_STREAMDEV_IGMPSERVER_H
+
+#include <sys/time.h>
+#include <time.h>
+#include <vdr/thread.h>
+#include "server/component.h"
+
+class cConnectionIGMP;
+class cMulticastGroup;
+
+class cComponentIGMP: public cServerComponent, public cThread {
+private:
+ char m_ReadBuffer[2048];
+ cList<cMulticastGroup> m_Groups;
+ in_addr_t m_BindIp;
+ int m_MaxChannelNumber;
+ struct timeval m_GeneralQueryTimer;
+ int m_StartupQueryCount;
+ bool m_Querier;
+ cCondWait m_CondWait;
+
+ cMulticastGroup* FindGroup(in_addr_t Group) const;
+
+ /* Add or remove local host to multicast group */
+ bool IGMPMembership(in_addr_t Group, bool Add = true);
+ void IGMPSendQuery(in_addr_t Group, int Timeout);
+
+ cServerConnection* ProcessMessage(struct igmp *Igmp, in_addr_t Group, in_addr_t Sender);
+
+ void IGMPStartGeneralQueryTimer();
+ void IGMPStartOtherQuerierPresentTimer();
+ void IGMPSendGeneralQuery();
+
+ void IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member);
+ void IGMPStartV1HostTimer(cMulticastGroup* Group);
+ void IGMPStartTimerAfterLeave(cMulticastGroup* Group, unsigned int MaxResponseTime);
+ void IGMPStartRetransmitTimer(cMulticastGroup* Group);
+ void IGMPClearRetransmitTimer(cMulticastGroup* Group);
+ void IGMPSendGroupQuery(cMulticastGroup* Group);
+ void IGMPStartMulticast(cMulticastGroup* Group);
+ void IGMPStopMulticast(cMulticastGroup* Group);
+
+ virtual void Action();
+
+protected:
+ virtual cServerConnection *NewClient(void);
+
+public:
+ virtual bool Initialize(void);
+ virtual void Destruct(void);
+ virtual cServerConnection* Accept(void);
+
+ cComponentIGMP(void);
+ ~cComponentIGMP(void);
+};
+
+#endif // VDR_STREAMDEV_IGMPSERVER_H
diff --git a/plugins/streamdev/streamdev-cvs/server/componentVTP.c b/plugins/streamdev/streamdev-cvs/server/componentVTP.c
new file mode 100644
index 0000000..ed2df1c
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/componentVTP.c
@@ -0,0 +1,18 @@
+/*
+ * $Id: componentVTP.c,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $
+ */
+
+#include "server/componentVTP.h"
+#include "server/connectionVTP.h"
+#include "server/setup.h"
+
+cComponentVTP::cComponentVTP(void):
+ cServerComponent("VTP", StreamdevServerSetup.VTPBindIP,
+ StreamdevServerSetup.VTPServerPort)
+{
+}
+
+cServerConnection *cComponentVTP::NewClient(void)
+{
+ return new cConnectionVTP;
+}
diff --git a/plugins/streamdev/streamdev-cvs/server/componentVTP.h b/plugins/streamdev/streamdev-cvs/server/componentVTP.h
new file mode 100644
index 0000000..4f61eb5
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/componentVTP.h
@@ -0,0 +1,18 @@
+/*
+ * $Id: componentVTP.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SERVERS_SERVERVTP_H
+#define VDR_STREAMDEV_SERVERS_SERVERVTP_H
+
+#include "server/component.h"
+
+class cComponentVTP: public cServerComponent {
+protected:
+ virtual cServerConnection *NewClient(void);
+
+public:
+ cComponentVTP(void);
+};
+
+#endif // VDR_STREAMDEV_SERVERS_SERVERVTP_H
diff --git a/plugins/streamdev/streamdev-cvs/server/connection.c b/plugins/streamdev/streamdev-cvs/server/connection.c
new file mode 100644
index 0000000..eccb3c4
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/connection.c
@@ -0,0 +1,255 @@
+/*
+ * $Id: connection.c,v 1.13 2009/09/18 10:43:26 schmirl Exp $
+ */
+
+#include "server/connection.h"
+#include "server/setup.h"
+#include "server/suspend.h"
+#include "common.h"
+
+#include <vdr/tools.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+
+cServerConnection::cServerConnection(const char *Protocol, int Type):
+ cTBSocket(Type),
+ m_Protocol(Protocol),
+ m_DeferClose(false),
+ m_Pending(false),
+ m_ReadBytes(0),
+ m_WriteBytes(0),
+ m_WriteIndex(0)
+{
+}
+
+cServerConnection::~cServerConnection()
+{
+}
+
+const cChannel* cServerConnection::ChannelFromString(const char *String, int *Apid) {
+ const cChannel *channel = NULL;
+ char *string = strdup(String);
+ char *ptr, *end;
+ int apididx = 0;
+
+ if ((ptr = strrchr(string, '+')) != NULL) {
+ *(ptr++) = '\0';
+ apididx = strtoul(ptr, &end, 10);
+ Dprintf("found apididx: %d\n", apididx);
+ }
+
+ if (isnumber(string)) {
+ int temp = strtol(String, NULL, 10);
+ if (temp >= 1 && temp <= Channels.MaxNumber())
+ channel = Channels.GetByNumber(temp);
+ } else {
+ channel = Channels.GetByChannelID(tChannelID::FromString(string));
+
+ if (channel == NULL) {
+ int i = 1;
+ while ((channel = Channels.GetByNumber(i, 1)) != NULL) {
+ if (String == channel->Name())
+ break;
+
+ i = channel->Number() + 1;
+ }
+ }
+ }
+
+ if (channel != NULL && apididx > 0) {
+ int apid = 0, index = 1;
+
+ for (int i = 0; channel->Apid(i) != 0; ++i, ++index) {
+ if (index == apididx) {
+ apid = channel->Apid(i);
+ break;
+ }
+ }
+
+ if (apid == 0) {
+ for (int i = 0; channel->Dpid(i) != 0; ++i, ++index) {
+ if (index == apididx) {
+ apid = channel->Dpid(i);
+ break;
+ }
+ }
+ }
+
+ if (Apid != NULL)
+ *Apid = apid;
+ }
+
+ free(string);
+ return channel;
+}
+
+bool cServerConnection::Read(void)
+{
+ int b;
+ if ((b = cTBSocket::Read(m_ReadBuffer + m_ReadBytes,
+ sizeof(m_ReadBuffer) - m_ReadBytes - 1)) < 0) {
+ esyslog("ERROR: read from client (%s) %s:%d failed: %m",
+ m_Protocol, RemoteIp().c_str(), RemotePort());
+ return false;
+ }
+
+ if (b == 0) {
+ isyslog("client (%s) %s:%d has closed connection",
+ m_Protocol, RemoteIp().c_str(), RemotePort());
+ return false;
+ }
+
+ m_ReadBytes += b;
+ m_ReadBuffer[m_ReadBytes] = '\0';
+
+ char *end;
+ bool result = true;
+ while ((end = strchr(m_ReadBuffer, '\012')) != NULL) {
+ *end = '\0';
+ if (end > m_ReadBuffer && *(end - 1) == '\015')
+ *(end - 1) = '\0';
+
+ if (!Command(m_ReadBuffer))
+ return false;
+
+ m_ReadBytes -= ++end - m_ReadBuffer;
+ if (m_ReadBytes > 0)
+ memmove(m_ReadBuffer, end, m_ReadBytes);
+ }
+
+ if (m_ReadBytes == sizeof(m_ReadBuffer) - 1) {
+ esyslog("ERROR: streamdev: input buffer overflow (%s) for %s:%d",
+ m_Protocol, RemoteIp().c_str(), RemotePort());
+ return false;
+ }
+
+ return result;
+}
+
+bool cServerConnection::Write(void)
+{
+ int b;
+ if ((b = cTBSocket::Write(m_WriteBuffer + m_WriteIndex,
+ m_WriteBytes - m_WriteIndex)) < 0) {
+ esyslog("ERROR: streamdev: write to client (%s) %s:%d failed: %m",
+ m_Protocol, RemoteIp().c_str(), RemotePort());
+ return false;
+ }
+
+ m_WriteIndex += b;
+ if (m_WriteIndex == m_WriteBytes) {
+ m_WriteIndex = 0;
+ m_WriteBytes = 0;
+ if (m_Pending)
+ Command(NULL);
+ if (m_DeferClose)
+ return false;
+ Flushed();
+ }
+ return true;
+}
+
+bool cServerConnection::Respond(const char *Message, bool Last, ...)
+{
+ char *buffer;
+ int length;
+ va_list ap;
+ va_start(ap, Last);
+ length = vasprintf(&buffer, Message, ap);
+ va_end(ap);
+
+ if (length < 0) {
+ esyslog("ERROR: streamdev: buffer allocation failed (%s) for %s:%d",
+ m_Protocol, RemoteIp().c_str(), RemotePort());
+ return false;
+ }
+
+ if (m_WriteBytes + length + 2 > sizeof(m_WriteBuffer)) {
+ esyslog("ERROR: streamdev: output buffer overflow (%s) for %s:%d",
+ m_Protocol, RemoteIp().c_str(), RemotePort());
+ free(buffer);
+ return false;
+ }
+ Dprintf("OUT: |%s|\n", buffer);
+ memcpy(m_WriteBuffer + m_WriteBytes, buffer, length);
+ free(buffer);
+
+ m_WriteBytes += length;
+ m_WriteBuffer[m_WriteBytes++] = '\015';
+ m_WriteBuffer[m_WriteBytes++] = '\012';
+ m_Pending = !Last;
+ return true;
+}
+
+cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority)
+{
+ cDevice *device = NULL;
+
+ /*Dprintf("+ Statistics:\n");
+ Dprintf("+ Current Channel: %d\n", cDevice::CurrentChannel());
+ Dprintf("+ Current Device: %d\n", cDevice::ActualDevice()->CardIndex());
+ Dprintf("+ Transfer Mode: %s\n", cDevice::ActualDevice()
+ == cDevice::PrimaryDevice() ? "false" : "true");
+ Dprintf("+ Replaying: %s\n", cDevice::PrimaryDevice()->Replaying() ? "true"
+ : "false");*/
+
+ Dprintf(" * GetDevice(const cChannel*, int)\n");
+ Dprintf(" * -------------------------------\n");
+
+ device = cDevice::GetDevice(Channel, Priority, false);
+
+ Dprintf(" * Found following device: %p (%d)\n", device,
+ device ? device->CardIndex() + 1 : 0);
+ if (device == cDevice::ActualDevice())
+ Dprintf(" * is actual device\n");
+ if (!cSuspendCtl::IsActive() && StreamdevServerSetup.SuspendMode != smAlways)
+ Dprintf(" * NOT suspended\n");
+
+ if (!device || (device == cDevice::ActualDevice()
+ && !cSuspendCtl::IsActive()
+ && StreamdevServerSetup.SuspendMode != smAlways)) {
+ // mustn't switch actual device
+ // maybe a device would be free if THIS connection did turn off its streams?
+ Dprintf(" * trying again...\n");
+ const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
+ isyslog("streamdev-server: Detaching current receiver");
+ Detach();
+ device = cDevice::GetDevice(Channel, Priority, false);
+ Attach();
+ Dprintf(" * Found following device: %p (%d)\n", device,
+ device ? device->CardIndex() + 1 : 0);
+ if (device == cDevice::ActualDevice())
+ Dprintf(" * is actual device\n");
+ if (!cSuspendCtl::IsActive()
+ && StreamdevServerSetup.SuspendMode != smAlways)
+ Dprintf(" * NOT suspended\n");
+ if (current && !TRANSPONDER(Channel, current))
+ Dprintf(" * NOT same transponder\n");
+ if (device && (device == cDevice::ActualDevice()
+ && !cSuspendCtl::IsActive()
+ && StreamdevServerSetup.SuspendMode != smAlways
+ && current != NULL
+ && !TRANSPONDER(Channel, current))) {
+ // now we would have to switch away live tv...let's see if live tv
+ // can be handled by another device
+ cDevice *newdev = NULL;
+ for (int i = 0; i < cDevice::NumDevices(); ++i) {
+ cDevice *dev = cDevice::GetDevice(i);
+ if (dev->ProvidesChannel(current, 0) && dev != device) {
+ newdev = dev;
+ break;
+ }
+ }
+ Dprintf(" * Found device for live tv: %p (%d)\n", newdev,
+ newdev ? newdev->CardIndex() + 1 : 0);
+ if (newdev == NULL || newdev == device)
+ // no suitable device to continue live TV, giving up...
+ device = NULL;
+ else
+ newdev->SwitchChannel(current, true);
+ }
+ }
+
+ return device;
+}
diff --git a/plugins/streamdev/streamdev-cvs/server/connection.h b/plugins/streamdev/streamdev-cvs/server/connection.h
new file mode 100644
index 0000000..73cb3d5
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/connection.h
@@ -0,0 +1,99 @@
+/*
+ * $Id: connection.h,v 1.8 2009/09/18 10:43:26 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SERVER_CONNECTION_H
+#define VDR_STREAMDEV_SERVER_CONNECTION_H
+
+#include "tools/socket.h"
+#include "common.h"
+
+class cChannel;
+class cDevice;
+
+/* Basic capabilities of a straight text-based protocol, most functions
+ virtual to support more complicated protocols */
+
+class cServerConnection: public cListObject, public cTBSocket
+{
+private:
+ const char *m_Protocol;
+ bool m_DeferClose;
+ bool m_Pending;
+
+ char m_ReadBuffer[MAXPARSEBUFFER];
+ uint m_ReadBytes;
+
+ char m_WriteBuffer[MAXPARSEBUFFER];
+ uint m_WriteBytes;
+ uint m_WriteIndex;
+
+protected:
+ /* Will be called when a command terminated by a newline has been
+ received */
+ virtual bool Command(char *Cmd) = 0;
+
+ /* Will put Message into the response queue, which will be sent in the next
+ server cycle. Note that Message will be line-terminated by Respond.
+ Only one line at a time may be sent. If there are lines to follow, set
+ Last to false. Command(NULL) will be called in the next cycle, so you can
+ post the next line. */
+ virtual bool Respond(const char *Message, bool Last = true, ...);
+ //__attribute__ ((format (printf, 2, 4)));
+
+ static const cChannel *ChannelFromString(const char *String, int *Apid = NULL);
+
+public:
+ /* If you derive, specify a short string such as HTTP for Protocol, which
+ will be displayed in error messages */
+ cServerConnection(const char *Protocol, int Type = SOCK_STREAM);
+ virtual ~cServerConnection();
+
+ /* If true, any client IP will be accepted */
+ virtual bool CanAuthenticate(void) { return false; }
+
+ /* Gets called if the client has been accepted by the core */
+ virtual void Welcome(void) { }
+
+ /* Gets called if the client has been rejected by the core */
+ virtual void Reject(void) { DeferClose(); }
+
+ /* Get the client socket's file number */
+ virtual int Socket(void) const { return (int)*this; }
+
+ /* Determine if there is data to send or any command pending */
+ virtual bool HasData(void) const;
+
+ /* Gets called by server when the socket can accept more data. Writes
+ the buffer filled up by Respond(). Calls Command(NULL) if there is a
+ command pending. Returns false in case of an error */
+ virtual bool Write(void);
+
+ /* Gets called by server when there is incoming data to read. Calls
+ Command() for each line. Returns false in case of an error, or if
+ the connection shall be closed and removed by the server */
+ virtual bool Read(void);
+
+ /* Is polled regularely by the server. Returns true if the connection
+ needs to be terminated. */
+ virtual bool Abort(void) const = 0;
+
+ /* Will make the socket close after sending all queued output data */
+ void DeferClose(void) { m_DeferClose = true; }
+
+ /* Will retrieve an unused device for transmitting data. Use the returned
+ cDevice in a following call to StartTransfer */
+ cDevice *GetDevice(const cChannel *Channel, int Priority);
+
+ virtual void Flushed(void) {}
+
+ virtual void Detach(void) = 0;
+ virtual void Attach(void) = 0;
+};
+
+inline bool cServerConnection::HasData(void) const
+{
+ return m_WriteBytes > 0 || m_Pending || m_DeferClose;
+}
+
+#endif // VDR_STREAMDEV_SERVER_CONNECTION_H
diff --git a/plugins/streamdev/streamdev-cvs/server/connectionHTTP.c b/plugins/streamdev/streamdev-cvs/server/connectionHTTP.c
new file mode 100644
index 0000000..83e568d
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/connectionHTTP.c
@@ -0,0 +1,296 @@
+/*
+ * $Id: connectionHTTP.c,v 1.17 2009/06/19 06:32:45 schmirl Exp $
+ */
+
+#include <ctype.h>
+
+#include "server/connectionHTTP.h"
+#include "server/menuHTTP.h"
+#include "server/server.h"
+#include "server/setup.h"
+
+cConnectionHTTP::cConnectionHTTP(void):
+ cServerConnection("HTTP"),
+ m_Status(hsRequest),
+ m_LiveStreamer(NULL),
+ m_StreamerParameter(""),
+ m_Channel(NULL),
+ m_Apid(0),
+ m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
+ m_ChannelList(NULL)
+{
+ Dprintf("constructor hsRequest\n");
+}
+
+cConnectionHTTP::~cConnectionHTTP()
+{
+ delete m_LiveStreamer;
+}
+
+bool cConnectionHTTP::CanAuthenticate(void)
+{
+ return opt_auth != NULL;
+}
+
+bool cConnectionHTTP::Command(char *Cmd)
+{
+ Dprintf("command %s\n", Cmd);
+ switch (m_Status) {
+ case hsRequest:
+ Dprintf("Request\n");
+ m_Request = Cmd;
+ m_Status = hsHeaders;
+ return true;
+
+ case hsHeaders:
+ if (*Cmd == '\0') {
+ m_Status = hsBody;
+ return ProcessRequest();
+ }
+ if (strncasecmp(Cmd, "Host:", 5) == 0) {
+ Dprintf("Host-Header\n");
+ m_Host = (std::string) skipspace(Cmd + 5);
+ return true;
+ }
+ else if (strncasecmp(Cmd, "Authorization:", 14) == 0) {
+ Cmd = skipspace(Cmd + 14);
+ if (strncasecmp(Cmd, "Basic", 5) == 0) {
+ Dprintf("'Authorization Basic'-Header\n");
+ m_Authorization = (std::string) skipspace(Cmd + 5);
+ return true;
+ }
+ }
+ Dprintf("header\n");
+ return true;
+ default:
+ // skip additional blank lines
+ if (*Cmd == '\0')
+ return true;
+ break;
+ }
+ return false; // ??? shouldn't happen
+}
+
+bool cConnectionHTTP::ProcessRequest(void)
+{
+ Dprintf("process\n");
+ if (!StreamdevHosts.Acceptable(RemoteIpAddr()))
+ {
+ if (!opt_auth || m_Authorization.empty() || m_Authorization.compare(opt_auth) != 0) {
+ isyslog("streamdev-server: HTTP authorization required");
+ DeferClose();
+ return Respond("HTTP/1.0 401 Authorization Required")
+ && Respond("WWW-authenticate: basic Realm=\"Streamdev-Server\")")
+ && Respond("");
+ }
+ }
+ if (m_Request.substr(0, 4) == "GET " && CmdGET(m_Request.substr(4))) {
+ switch (m_Job) {
+ case hjListing:
+ if (m_ChannelList)
+ return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
+ break;
+
+ case hjTransfer:
+ if (m_Channel == NULL) {
+ DeferClose();
+ return Respond("HTTP/1.0 404 not found");
+ }
+
+ m_LiveStreamer = new cStreamdevLiveStreamer(0, m_StreamerParameter);
+ cDevice *device = GetDevice(m_Channel, 0);
+ if (device != NULL) {
+ device->SwitchChannel(m_Channel, false);
+ if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid)) {
+ m_LiveStreamer->SetDevice(device);
+ if (!SetDSCP())
+ LOG_ERROR_STR("unable to set DSCP sockopt");
+ if (m_StreamType == stES && (m_Apid != 0 || ISRADIO(m_Channel))) {
+ return Respond("HTTP/1.0 200 OK")
+ && Respond("Content-Type: audio/mpeg")
+ && Respond("icy-name: %s", true, m_Channel->Name())
+ && Respond("");
+ } else {
+ return Respond("HTTP/1.0 200 OK")
+ && Respond("Content-Type: video/mpeg")
+ && Respond("");
+ }
+ }
+ }
+ DELETENULL(m_LiveStreamer);
+ DeferClose();
+ return Respond("HTTP/1.0 409 Channel not available")
+ && Respond("");
+ }
+ }
+
+ DeferClose();
+ return Respond("HTTP/1.0 400 Bad Request")
+ && Respond("");
+}
+
+void cConnectionHTTP::Flushed(void)
+{
+ std::string line;
+
+ if (m_Status != hsBody)
+ return;
+
+ switch (m_Job) {
+ case hjListing:
+ if (m_ChannelList) {
+ if (m_ChannelList->HasNext()) {
+ if (!Respond("%s", true, m_ChannelList->Next().c_str()))
+ DeferClose();
+ }
+ else {
+ DELETENULL(m_ChannelList);
+ m_Status = hsFinished;
+ DeferClose();
+ }
+ return;
+ }
+ // should never be reached
+ esyslog("streamdev-server cConnectionHTTP::Flushed(): no channel list");
+ m_Status = hsFinished;
+ break;
+
+ case hjTransfer:
+ Dprintf("streamer start\n");
+ m_LiveStreamer->Start(this);
+ m_Status = hsFinished;
+ break;
+ }
+}
+
+bool cConnectionHTTP::CmdGET(const std::string &Opts)
+{
+ const char *ptr, *sp, *pp, *fp, *xp, *qp, *ep;
+ const cChannel *chan;
+ int apid = 0;
+
+ ptr = Opts.c_str();
+
+ // find begin of URL
+ sp = skipspace(ptr);
+ // find end of URL (\0 or first space character)
+ for (ep = sp; *ep && !isspace(*ep); ep++)
+ ;
+ // find begin of query string (first ?)
+ for (qp = sp; qp < ep && *qp != '?'; qp++)
+ ;
+ // find begin of filename (last /)
+ for (fp = qp; fp > sp && *fp != '/'; --fp)
+ ;
+ // find begin of section params (first ;)
+ for (pp = sp; pp < fp && *pp != ';'; pp++)
+ ;
+ // find filename extension (first .)
+ for (xp = fp; xp < qp && *xp != '.'; xp++)
+ ;
+ if (qp - xp > 5) // too long for a filename extension
+ xp = qp;
+
+ std::string type, filespec, fileext, query;
+ // Streamtype with leading / stripped off
+ if (pp > sp)
+ type = Opts.substr(sp - ptr + 1, pp - sp - 1);
+ // Section parameters with leading ; stripped off
+ if (fp > pp)
+ m_StreamerParameter = Opts.substr(pp - ptr + 1, fp - pp - 1);
+ // file basename with leading / stripped off
+ if (xp > fp)
+ filespec = Opts.substr(fp - ptr + 1, xp - fp - 1);
+ // file extension including leading .
+ fileext = Opts.substr(xp - ptr, qp - xp);
+ // query string including leading ?
+ query = Opts.substr(qp - ptr, ep - qp);
+
+ Dprintf("before channelfromstring: type(%s) param(%s) filespec(%s) fileext(%s) query(%s)\n", type.c_str(), m_StreamerParameter.c_str(), filespec.c_str(), fileext.c_str(), query.c_str());
+
+ const char* pType = type.c_str();
+ if (strcasecmp(pType, "PS") == 0) {
+ m_StreamType = stPS;
+ } else if (strcasecmp(pType, "PES") == 0) {
+ m_StreamType = stPES;
+ } else if (strcasecmp(pType, "TS") == 0) {
+ m_StreamType = stTS;
+ } else if (strcasecmp(pType, "ES") == 0) {
+ m_StreamType = stES;
+ } else if (strcasecmp(pType, "Extern") == 0) {
+ m_StreamType = stExtern;
+ }
+
+ std::string groupTarget;
+ cChannelIterator *iterator = NULL;
+
+ if (filespec.compare("tree") == 0) {
+ const cChannel* c = NULL;
+ size_t groupIndex = query.find("group=");
+ if (groupIndex != std::string::npos)
+ c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
+ iterator = new cListTree(c);
+ groupTarget = filespec + fileext;
+ } else if (filespec.compare("groups") == 0) {
+ iterator = new cListGroups();
+ groupTarget = (std::string) "group" + fileext;
+ } else if (filespec.compare("group") == 0) {
+ const cChannel* c = NULL;
+ size_t groupIndex = query.find("group=");
+ if (groupIndex != std::string::npos)
+ c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6));
+ iterator = new cListGroup(c);
+ } else if (filespec.compare("channels") == 0) {
+ iterator = new cListChannels();
+ } else if (filespec.compare("all") == 0 ||
+ (filespec.empty() && fileext.empty())) {
+ iterator = new cListAll();
+ }
+
+ if (iterator) {
+ if (filespec.empty() || fileext.compare(".htm") == 0 || fileext.compare(".html") == 0) {
+ m_ChannelList = new cHtmlChannelList(iterator, m_StreamType, (filespec + fileext + query).c_str(), groupTarget.c_str());
+ m_Job = hjListing;
+ } else if (fileext.compare(".m3u") == 0) {
+ std::string base;
+ if (*(m_Host.c_str()))
+ base = "http://" + m_Host + "/";
+ else
+ base = (std::string) "http://" + LocalIp() + ":" +
+ (const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
+ if (type.empty())
+ {
+ switch (m_StreamType)
+ {
+ case stTS: base += "TS/"; break;
+ case stPS: base += "PS/"; break;
+ case stPES: base += "PES/"; break;
+ case stES: base += "ES/"; break;
+ case stExtern: base += "Extern/"; break;
+ default: break;
+
+ }
+ } else {
+ base += type;
+ if (!m_StreamerParameter.empty())
+ base += ";" + m_StreamerParameter;
+ base += "/";
+ }
+ m_ChannelList = new cM3uChannelList(iterator, base.c_str());
+ m_Job = hjListing;
+ } else {
+ delete iterator;
+ return false;
+ }
+ } else if ((chan = ChannelFromString(filespec.c_str(), &apid)) != NULL) {
+ m_Channel = chan;
+ m_Apid = apid;
+ Dprintf("Apid is %d\n", apid);
+ m_Job = hjTransfer;
+ } else
+ return false;
+
+ Dprintf("after channelfromstring\n");
+ return true;
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/server/connectionHTTP.h b/plugins/streamdev/streamdev-cvs/server/connectionHTTP.h
new file mode 100644
index 0000000..0548959
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/connectionHTTP.h
@@ -0,0 +1,70 @@
+/*
+ * $Id: connectionHTTP.h,v 1.6 2008/10/14 11:05:48 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
+#define VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
+
+#include "connection.h"
+#include "server/livestreamer.h"
+
+#include <tools/select.h>
+
+class cChannel;
+class cStreamdevLiveStreamer;
+class cChannelList;
+
+class cConnectionHTTP: public cServerConnection {
+private:
+ enum eHTTPStatus {
+ hsRequest,
+ hsHeaders,
+ hsBody,
+ hsFinished,
+ };
+
+ enum eHTTPJob {
+ hjTransfer,
+ hjListing,
+ };
+
+ std::string m_Request;
+ std::string m_Host;
+ std::string m_Authorization;
+ //std::map<std::string,std::string> m_Headers; TODO: later?
+ eHTTPStatus m_Status;
+ eHTTPJob m_Job;
+ // job: transfer
+ cStreamdevLiveStreamer *m_LiveStreamer;
+ std::string m_StreamerParameter;
+ const cChannel *m_Channel;
+ int m_Apid;
+ eStreamType m_StreamType;
+ // job: listing
+ cChannelList *m_ChannelList;
+
+protected:
+ bool ProcessRequest(void);
+
+public:
+ cConnectionHTTP(void);
+ virtual ~cConnectionHTTP();
+
+ virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
+ virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
+
+ virtual bool CanAuthenticate(void);
+
+ virtual bool Command(char *Cmd);
+ bool CmdGET(const std::string &Opts);
+
+ virtual bool Abort(void) const;
+ virtual void Flushed(void);
+};
+
+inline bool cConnectionHTTP::Abort(void) const
+{
+ return m_LiveStreamer && m_LiveStreamer->Abort();
+}
+
+#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
diff --git a/plugins/streamdev/streamdev-cvs/server/connectionIGMP.c b/plugins/streamdev/streamdev-cvs/server/connectionIGMP.c
new file mode 100644
index 0000000..dc08798
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/connectionIGMP.c
@@ -0,0 +1,64 @@
+/*
+ * $Id: connectionIGMP.c,v 1.1 2009/02/13 10:39:22 schmirl Exp $
+ */
+
+#include <ctype.h>
+
+#include "server/connectionIGMP.h"
+#include "server/server.h"
+#include "server/setup.h"
+#include <vdr/channels.h>
+
+cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType) :
+ cServerConnection(Name, SOCK_DGRAM),
+ m_LiveStreamer(NULL),
+ m_ClientPort(ClientPort),
+ m_StreamType(StreamType)
+{
+}
+
+cConnectionIGMP::~cConnectionIGMP()
+{
+ delete m_LiveStreamer;
+}
+
+bool cConnectionIGMP::Start(cChannel *Channel, in_addr_t Dst)
+{
+ if (Channel != NULL) {
+ cDevice *device = GetDevice(Channel, 0);
+ if (device != NULL) {
+ device->SwitchChannel(Channel, false);
+ struct in_addr ip;
+ ip.s_addr = Dst;
+ if (Connect(inet_ntoa(ip), m_ClientPort)) {
+ m_LiveStreamer = new cStreamdevLiveStreamer(0);
+ if (m_LiveStreamer->SetChannel(Channel, m_StreamType)) {
+ m_LiveStreamer->SetDevice(device);
+ if (!SetDSCP())
+ LOG_ERROR_STR("unable to set DSCP sockopt");
+ Dprintf("streamer start\n");
+ m_LiveStreamer->Start(this);
+ return true;
+ }
+ else
+ esyslog("streamdev-server IGMP: SetDevice failed");
+ DELETENULL(m_LiveStreamer);
+ }
+ else
+ esyslog("streamdev-server IGMP: Connect failed: %m");
+ }
+ else
+ esyslog("streamdev-server IGMP: GetDevice failed");
+ }
+ else
+ esyslog("streamdev-server IGMP: Channel not found");
+ return false;
+}
+
+void cConnectionIGMP::Stop()
+{
+ if (m_LiveStreamer) {
+ m_LiveStreamer->Stop();
+ DELETENULL(m_LiveStreamer);
+ }
+}
diff --git a/plugins/streamdev/streamdev-cvs/server/connectionIGMP.h b/plugins/streamdev/streamdev-cvs/server/connectionIGMP.h
new file mode 100644
index 0000000..90abd58
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/connectionIGMP.h
@@ -0,0 +1,45 @@
+/*
+ * $Id: connectionIGMP.h,v 1.1 2009/02/13 10:39:22 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H
+#define VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H
+
+#include "connection.h"
+#include "server/livestreamer.h"
+
+#include <tools/select.h>
+
+#define MULTICAST_PRIV_MIN ((uint32_t) 0xefff0000)
+#define MULTICAST_PRIV_MAX ((uint32_t) 0xeffffeff)
+
+class cStreamdevLiveStreamer;
+
+class cConnectionIGMP: public cServerConnection {
+private:
+ cStreamdevLiveStreamer *m_LiveStreamer;
+ int m_ClientPort;
+ eStreamType m_StreamType;
+
+public:
+ cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType);
+ virtual ~cConnectionIGMP();
+
+ bool Start(cChannel *Channel, in_addr_t Dst);
+ void Stop();
+
+ /* Not used here */
+ virtual bool Command(char *Cmd) { return false; }
+
+ virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
+ virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
+
+ virtual bool Abort(void) const;
+};
+
+inline bool cConnectionIGMP::Abort(void) const
+{
+ return !m_LiveStreamer || m_LiveStreamer->Abort();
+}
+
+#endif // VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H
diff --git a/plugins/streamdev/streamdev-cvs/server/connectionVTP.c b/plugins/streamdev/streamdev-cvs/server/connectionVTP.c
new file mode 100644
index 0000000..7ff60e5
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/connectionVTP.c
@@ -0,0 +1,1768 @@
+/*
+ * $Id: connectionVTP.c,v 1.27 2010/01/29 12:03:02 schmirl Exp $
+ */
+
+#include "server/connectionVTP.h"
+#include "server/livestreamer.h"
+#include "server/suspend.h"
+#include "setup.h"
+
+#include <vdr/tools.h>
+#include <vdr/videodir.h>
+#include <vdr/menu.h>
+#include <tools/select.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+
+/* VTP Response codes:
+ 220: Service ready
+ 221: Service closing connection
+ 451: Requested action aborted: try again
+ 500: Syntax error or Command unrecognized
+ 501: Wrong parameters or missing parameters
+ 550: Requested action not taken
+ 551: Data connection not accepted
+ 560: Channel not available currently
+ 561: Capability not known
+ 562: Pid not available currently
+ 563: Recording not available (currently?)
+*/
+
+enum eDumpModeStreamdev { dmsdAll, dmsdPresent, dmsdFollowing, dmsdAtTime, dmsdFromToTime };
+
+// --- cLSTEHandler -----------------------------------------------------------
+
+class cLSTEHandler
+{
+private:
+#if defined(USE_PARENTALRATING) || defined(PARENTALRATINGCONTENTVERSNUM)
+ enum eStates { Channel, Event, Title, Subtitle, Description, Vps, Content,
+ EndEvent, EndChannel, EndEPG };
+#elif APIVERSNUM >= 10711
+ enum eStates { Channel, Event, Title, Subtitle, Description, Vps, Content, Rating,
+ EndEvent, EndChannel, EndEPG };
+#else
+ enum eStates { Channel, Event, Title, Subtitle, Description, Vps,
+ EndEvent, EndChannel, EndEPG };
+#endif /* PARENTALRATING */
+ cConnectionVTP *m_Client;
+ cSchedulesLock *m_SchedulesLock;
+ const cSchedules *m_Schedules;
+ const cSchedule *m_Schedule;
+ const cEvent *m_Event;
+ int m_Errno;
+ cString m_Error;
+ eStates m_State;
+ bool m_Traverse;
+ time_t m_ToTime;
+public:
+ cLSTEHandler(cConnectionVTP *Client, const char *Option);
+ ~cLSTEHandler();
+ bool Next(bool &Last);
+};
+
+cLSTEHandler::cLSTEHandler(cConnectionVTP *Client, const char *Option):
+ m_Client(Client),
+ m_SchedulesLock(new cSchedulesLock(false, 500)),
+ m_Schedules(cSchedules::Schedules(*m_SchedulesLock)),
+ m_Schedule(NULL),
+ m_Event(NULL),
+ m_Errno(0),
+ m_State(Channel),
+ m_Traverse(false),
+ m_ToTime(0)
+{
+ eDumpModeStreamdev dumpmode = dmsdAll;
+ time_t attime = 0;
+ time_t fromtime = 0;
+
+ if (m_Schedules != NULL && *Option) {
+ char buf[strlen(Option) + 1];
+ strcpy(buf, Option);
+ const char *delim = " \t";
+ char *strtok_next;
+ char *p = strtok_r(buf, delim, &strtok_next);
+ while (p && dumpmode == dmsdAll) {
+ if (strcasecmp(p, "NOW") == 0)
+ dumpmode = dmsdPresent;
+ else if (strcasecmp(p, "NEXT") == 0)
+ dumpmode = dmsdFollowing;
+ else if (strcasecmp(p, "AT") == 0) {
+ dumpmode = dmsdAtTime;
+ if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
+ if (isnumber(p))
+ attime = strtol(p, NULL, 10);
+ else {
+ m_Errno = 501;
+ m_Error = "Invalid time";
+ break;
+ }
+ } else {
+ m_Errno = 501;
+ m_Error = "Missing time";
+ break;
+ }
+ }
+ else if (strcasecmp(p, "FROM") == 0) {
+ dumpmode = dmsdFromToTime;
+ if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
+ if (isnumber(p))
+ fromtime = strtol(p, NULL, 10);
+ else {
+ m_Errno = 501;
+ m_Error = "Invalid time";
+ break;
+ }
+ if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
+ if (strcasecmp(p, "TO") == 0) {
+ if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
+ if (isnumber(p))
+ m_ToTime = strtol(p, NULL, 10);
+ else {
+ m_Errno = 501;
+ m_Error = "Invalid time";
+ break;
+ }
+ } else {
+ m_Errno = 501;
+ m_Error = "Missing time";
+ break;
+ }
+ }
+ }
+ } else {
+ m_Errno = 501;
+ m_Error = "Missing time";
+ break;
+ }
+ } else if (!m_Schedule) {
+ cChannel* Channel = NULL;
+ if (isnumber(p))
+ Channel = Channels.GetByNumber(strtol(Option, NULL, 10));
+ else
+ Channel = Channels.GetByChannelID(tChannelID::FromString(
+ Option));
+ if (Channel) {
+ m_Schedule = m_Schedules->GetSchedule(Channel->GetChannelID());
+ if (!m_Schedule) {
+ m_Errno = 550;
+ m_Error = "No schedule found";
+ break;
+ }
+ } else {
+ m_Errno = 550;
+ m_Error = cString::sprintf("Channel \"%s\" not defined", p);
+ break;
+ }
+ } else {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Unknown option: \"%s\"", p);
+ break;
+ }
+ p = strtok_r(NULL, delim, &strtok_next);
+ }
+ } else if (m_Schedules == NULL) {
+ m_Errno = 451;
+ m_Error = "EPG data is being modified, try again";
+ }
+
+ if (*m_Error == NULL) {
+ if (m_Schedule != NULL)
+ m_Schedules = NULL;
+ else if (m_Schedules != NULL)
+ m_Schedule = m_Schedules->First();
+
+ if (m_Schedule != NULL && m_Schedule->Events() != NULL) {
+ switch (dumpmode) {
+ case dmsdAll: m_Event = m_Schedule->Events()->First();
+ m_Traverse = true;
+ break;
+ case dmsdPresent: m_Event = m_Schedule->GetPresentEvent();
+ break;
+ case dmsdFollowing: m_Event = m_Schedule->GetFollowingEvent();
+ break;
+ case dmsdAtTime: m_Event = m_Schedule->GetEventAround(attime);
+ break;
+ case dmsdFromToTime:
+ if (m_Schedule->Events()->Count() <= 1) {
+ m_Event = m_Schedule->Events()->First();
+ break;
+ }
+ if (fromtime < m_Schedule->Events()->First()->StartTime()) {
+ fromtime = m_Schedule->Events()->First()->StartTime();
+ }
+ if (m_ToTime > m_Schedule->Events()->Last()->EndTime()) {
+ m_ToTime = m_Schedule->Events()->Last()->EndTime();
+ }
+ m_Event = m_Schedule->GetEventAround(fromtime);
+ m_Traverse = true;
+ break;
+ }
+ }
+ }
+}
+
+cLSTEHandler::~cLSTEHandler()
+{
+ delete m_SchedulesLock;
+}
+
+bool cLSTEHandler::Next(bool &Last)
+{
+ if (*m_Error != NULL) {
+ Last = true;
+ cString str(m_Error);
+ m_Error = NULL;
+ return m_Client->Respond(m_Errno, "%s", *str);
+ }
+
+ Last = false;
+ switch (m_State) {
+ case Channel:
+ if (m_Schedule != NULL) {
+ cChannel *channel = Channels.GetByChannelID(m_Schedule->ChannelID(),
+ true);
+ if (channel != NULL) {
+ m_State = Event;
+ return m_Client->Respond(-215, "C %s %s",
+ *channel->GetChannelID().ToString(),
+ channel->Name());
+ } else {
+ esyslog("ERROR: vdr streamdev: unable to find channel %s by ID",
+ *m_Schedule->ChannelID().ToString());
+ m_State = EndChannel;
+ return Next(Last);
+ }
+ } else {
+ m_State = EndEPG;
+ return Next(Last);
+ }
+ break;
+
+ case Event:
+ if (m_Event != NULL) {
+ m_State = Title;
+#ifdef __FreeBSD__
+ return m_Client->Respond(-215, "E %u %d %d %X", m_Event->EventID(),
+#else
+ return m_Client->Respond(-215, "E %u %ld %d %X", m_Event->EventID(),
+#endif
+ m_Event->StartTime(), m_Event->Duration(),
+ m_Event->TableID());
+ } else {
+ m_State = EndChannel;
+ return Next(Last);
+ }
+ break;
+
+ case Title:
+ m_State = Subtitle;
+ if (!isempty(m_Event->Title()))
+ return m_Client->Respond(-215, "T %s", m_Event->Title());
+ else
+ return Next(Last);
+ break;
+
+ case Subtitle:
+ m_State = Description;
+ if (!isempty(m_Event->ShortText()))
+ return m_Client->Respond(-215, "S %s", m_Event->ShortText());
+ else
+ return Next(Last);
+ break;
+
+ case Description:
+ m_State = Vps;
+ if (!isempty(m_Event->Description())) {
+ char *copy = strdup(m_Event->Description());
+ cString cpy(copy, true);
+ strreplace(copy, '\n', '|');
+ return m_Client->Respond(-215, "D %s", copy);
+ } else
+ return Next(Last);
+ break;
+
+ case Vps:
+#if defined(USE_PARENTALRATING) || defined(PARENTALRATINGCONTENTVERSNUM) || APIVERSNUM >= 10711
+ m_State = Content;
+#else
+ m_State = EndEvent;
+#endif /* PARENTALRATING */
+ if (m_Event->Vps())
+#ifdef __FreeBSD__
+ return m_Client->Respond(-215, "V %d", m_Event->Vps());
+#else
+ return m_Client->Respond(-215, "V %ld", m_Event->Vps());
+#endif
+ else
+ return Next(Last);
+ break;
+
+#if defined(USE_PARENTALRATING) || defined(PARENTALRATINGCONTENTVERSNUM)
+ case Content:
+ m_State = EndEvent;
+ if (!isempty(m_Event->GetContentsString())) {
+ char *copy = strdup(m_Event->GetContentsString());
+ cString cpy(copy, true);
+ strreplace(copy, '\n', '|');
+ return m_Client->Respond(-215, "G %i %i %s", m_Event->Contents() & 0xF0, m_Event->Contents() & 0x0F, copy);
+ } else
+ return Next(Last);
+ break;
+#elif APIVERSNUM >= 10711
+ case Content:
+ m_State = Rating;
+ if (!isempty(m_Event->ContentToString(m_Event->Contents()))) {
+ char *copy = strdup(m_Event->ContentToString(m_Event->Contents()));
+ cString cpy(copy, true);
+ strreplace(copy, '\n', '|');
+ return m_Client->Respond(-215, "G %i %i %s", m_Event->Contents() & 0xF0, m_Event->Contents() & 0x0F, copy);
+ } else
+ return Next(Last);
+ break;
+
+ case Rating:
+ m_State = EndEvent;
+ if (m_Event->ParentalRating())
+ return m_Client->Respond(-215, "R %d", m_Event->ParentalRating());
+ else
+ return Next(Last);
+ break;
+#endif
+
+ case EndEvent:
+ if (m_Traverse) {
+ m_Event = m_Schedule->Events()->Next(m_Event);
+ if ((m_Event != NULL) && (m_ToTime != 0) && (m_Event->StartTime() > m_ToTime)) {
+ m_Event = NULL;
+ }
+ }
+ else
+ m_Event = NULL;
+
+ if (m_Event != NULL)
+ m_State = Event;
+ else
+ m_State = EndChannel;
+
+ return m_Client->Respond(-215, "e");
+
+ case EndChannel:
+ if (m_Schedules != NULL) {
+ m_Schedule = m_Schedules->Next(m_Schedule);
+ if (m_Schedule != NULL) {
+ if (m_Schedule->Events() != NULL)
+ m_Event = m_Schedule->Events()->First();
+ m_State = Channel;
+ }
+ }
+
+ if (m_Schedules == NULL || m_Schedule == NULL)
+ m_State = EndEPG;
+
+ return m_Client->Respond(-215, "c");
+
+ case EndEPG:
+ Last = true;
+ return m_Client->Respond(215, "End of EPG data");
+ }
+ return false;
+}
+
+// --- cLSTCHandler -----------------------------------------------------------
+
+class cLSTCHandler
+{
+private:
+ cConnectionVTP *m_Client;
+ const cChannel *m_Channel;
+ char *m_Option;
+ int m_Errno;
+ cString m_Error;
+ bool m_Traverse;
+public:
+ cLSTCHandler(cConnectionVTP *Client, const char *Option);
+ ~cLSTCHandler();
+ bool Next(bool &Last);
+};
+
+cLSTCHandler::cLSTCHandler(cConnectionVTP *Client, const char *Option):
+ m_Client(Client),
+ m_Channel(NULL),
+ m_Option(NULL),
+ m_Errno(0),
+ m_Traverse(false)
+{
+ if (!Channels.Lock(false, 500)) {
+ m_Errno = 451;
+ m_Error = "Channels are being modified - try again";
+ } else if (*Option) {
+ if (isnumber(Option)) {
+ m_Channel = Channels.GetByNumber(strtol(Option, NULL, 10));
+ if (m_Channel == NULL) {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Channel \"%s\" not defined", Option);
+ return;
+ }
+ } else {
+ int i = 1;
+ m_Traverse = true;
+ m_Option = strdup(Option);
+ while (i <= Channels.MaxNumber()) {
+ m_Channel = Channels.GetByNumber(i, 1);
+ if (strcasestr(m_Channel->Name(), Option) != NULL)
+ break;
+ i = m_Channel->Number() + 1;
+ }
+
+ if (i > Channels.MaxNumber()) {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Channel \"%s\" not defined", Option);
+ return;
+ }
+ }
+ } else if (Channels.MaxNumber() >= 1) {
+ m_Channel = Channels.GetByNumber(1, 1);
+ m_Traverse = true;
+ } else {
+ m_Errno = 550;
+ m_Error = "No channels defined";
+ }
+}
+
+cLSTCHandler::~cLSTCHandler()
+{
+ Channels.Unlock();
+ if (m_Option != NULL)
+ free(m_Option);
+}
+
+bool cLSTCHandler::Next(bool &Last)
+{
+ if (*m_Error != NULL) {
+ Last = true;
+ cString str(m_Error);
+ m_Error = NULL;
+ return m_Client->Respond(m_Errno, "%s", *str);
+ }
+
+ int number;
+ char *buffer;
+
+ number = m_Channel->Number();
+ buffer = strdup(*m_Channel->ToText());
+ buffer[strlen(buffer) - 1] = '\0'; // remove \n
+ cString str(buffer, true);
+
+ Last = true;
+ if (m_Traverse) {
+ int i = m_Channel->Number() + 1;
+ while (i <= Channels.MaxNumber()) {
+ m_Channel = Channels.GetByNumber(i, 1);
+ if (m_Channel != NULL) {
+ if (m_Option == NULL || strcasestr(m_Channel->Name(),
+ m_Option) != NULL)
+ break;
+ i = m_Channel->Number() + 1;
+ } else {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Channel \"%d\" not found", i);
+ }
+ }
+
+ if (i < Channels.MaxNumber() + 1)
+ Last = false;
+ }
+
+ return m_Client->Respond(Last ? 250 : -250, "%d %s", number, buffer);
+}
+
+// --- cLSTTHandler -----------------------------------------------------------
+
+class cLSTTHandler
+{
+private:
+ cConnectionVTP *m_Client;
+ cTimer *m_Timer;
+ int m_Index;
+ int m_Errno;
+ cString m_Error;
+ bool m_Traverse;
+public:
+ cLSTTHandler(cConnectionVTP *Client, const char *Option);
+ ~cLSTTHandler();
+ bool Next(bool &Last);
+};
+
+cLSTTHandler::cLSTTHandler(cConnectionVTP *Client, const char *Option):
+ m_Client(Client),
+ m_Timer(NULL),
+ m_Index(0),
+ m_Errno(0),
+ m_Traverse(false)
+{
+ if (*Option) {
+ if (isnumber(Option)) {
+ m_Timer = Timers.Get(strtol(Option, NULL, 10) - 1);
+ if (m_Timer == NULL) {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Timer \"%s\" not defined", Option);
+ }
+ } else {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Error in timer number \"%s\"", Option);
+ }
+ } else if (Timers.Count()) {
+ m_Traverse = true;
+ m_Index = 0;
+ m_Timer = Timers.Get(m_Index);
+ if (m_Timer == NULL) {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Timer \"%d\" not found", m_Index + 1);
+ }
+ } else {
+ m_Errno = 550;
+ m_Error = "No timers defined";
+ }
+}
+
+cLSTTHandler::~cLSTTHandler()
+{
+}
+
+bool cLSTTHandler::Next(bool &Last)
+{
+ if (*m_Error != NULL) {
+ Last = true;
+ cString str(m_Error);
+ m_Error = NULL;
+ return m_Client->Respond(m_Errno, "%s", *str);
+ }
+
+ bool result;
+ char *buffer;
+ Last = !m_Traverse || m_Index >= Timers.Count() - 1;
+ buffer = strdup(*m_Timer->ToText());
+ buffer[strlen(buffer) - 1] = '\0'; // strip \n
+ result = m_Client->Respond(Last ? 250 : -250, "%d %s", m_Timer->Index() + 1,
+ buffer);
+ free(buffer);
+
+ if (m_Traverse && !Last) {
+ m_Timer = Timers.Get(++m_Index);
+ if (m_Timer == NULL) {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Timer \"%d\" not found", m_Index + 1);
+ }
+ }
+ return result;
+}
+
+// --- cLSTRHandler -----------------------------------------------------------
+
+class cLSTRHandler
+{
+private:
+ enum eStates { Recording, Event, Title, Subtitle, Description, Components, Vps,
+ EndRecording };
+ cConnectionVTP *m_Client;
+ cRecording *m_Recording;
+ const cEvent *m_Event;
+ int m_Index;
+ int m_Errno;
+ cString m_Error;
+ bool m_Traverse;
+ bool m_Info;
+ eStates m_State;
+ int m_CurrentComponent;
+public:
+ cLSTRHandler(cConnectionVTP *Client, const char *Option);
+ ~cLSTRHandler();
+ bool Next(bool &Last);
+};
+
+cLSTRHandler::cLSTRHandler(cConnectionVTP *Client, const char *Option):
+ m_Client(Client),
+ m_Recording(NULL),
+ m_Event(NULL),
+ m_Index(0),
+ m_Errno(0),
+ m_Traverse(false),
+ m_Info(false),
+ m_State(Recording),
+ m_CurrentComponent(0)
+{
+ if (*Option) {
+ if (isnumber(Option)) {
+ m_Recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
+#if defined(USE_STREAMDEVEXT) || APIVERSNUM >= 10705
+ m_Event = m_Recording->Info()->GetEvent();
+#endif
+ m_Info = true;
+ if (m_Recording == NULL) {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Recording \"%s\" not found", Option);
+ }
+ }
+ else {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Error in Recording number \"%s\"", Option);
+ }
+ }
+ else if (Recordings.Count()) {
+ m_Traverse = true;
+ m_Index = 0;
+ m_Recording = Recordings.Get(m_Index);
+ if (m_Recording == NULL) {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Recording \"%d\" not found", m_Index + 1);
+ }
+ }
+ else {
+ m_Errno = 550;
+ m_Error = "No recordings available";
+ }
+}
+
+cLSTRHandler::~cLSTRHandler()
+{
+}
+
+bool cLSTRHandler::Next(bool &Last)
+{
+ if (*m_Error != NULL) {
+ Last = true;
+ cString str(m_Error);
+ m_Error = NULL;
+ return m_Client->Respond(m_Errno, "%s", *str);
+ }
+
+ if (m_Info) {
+ Last = false;
+ switch (m_State) {
+ case Recording:
+ if (m_Recording != NULL) {
+ m_State = Event;
+ return m_Client->Respond(-215, "C %s%s%s",
+ *m_Recording->Info()->ChannelID().ToString(),
+ m_Recording->Info()->ChannelName() ? " " : "",
+ m_Recording->Info()->ChannelName() ? m_Recording->Info()->ChannelName() : "");
+ }
+ else {
+ m_State = EndRecording;
+ return Next(Last);
+ }
+ break;
+
+ case Event:
+ m_State = Title;
+ if (m_Event != NULL) {
+ return m_Client->Respond(-215, "E %u %ld %d %X %X", (unsigned int) m_Event->EventID(),
+ m_Event->StartTime(), m_Event->Duration(),
+ m_Event->TableID(), m_Event->Version());
+ }
+ return Next(Last);
+
+ case Title:
+ m_State = Subtitle;
+ return m_Client->Respond(-215, "T %s", m_Recording->Info()->Title());
+
+ case Subtitle:
+ m_State = Description;
+ if (!isempty(m_Recording->Info()->ShortText())) {
+ return m_Client->Respond(-215, "S %s", m_Recording->Info()->ShortText());
+ }
+ return Next(Last);
+
+ case Description:
+ m_State = Components;
+ if (!isempty(m_Recording->Info()->Description())) {
+ m_State = Components;
+ char *copy = strdup(m_Recording->Info()->Description());
+ cString cpy(copy, true);
+ strreplace(copy, '\n', '|');
+ return m_Client->Respond(-215, "D %s", copy);
+ }
+ return Next(Last);
+
+ case Components:
+ if (m_Recording->Info()->Components()) {
+ if (m_CurrentComponent < m_Recording->Info()->Components()->NumComponents()) {
+ tComponent *p = m_Recording->Info()->Components()->Component(m_CurrentComponent);
+ m_CurrentComponent++;
+ if (!Setup.UseDolbyDigital && p->stream == 0x02 && p->type == 0x05)
+ return Next(Last);
+
+ return m_Client->Respond(-215, "X %s", *p->ToString());
+ }
+ }
+ m_State = Vps;
+ return Next(Last);
+
+ case Vps:
+ m_State = EndRecording;
+ if (m_Event != NULL) {
+ if (m_Event->Vps()) {
+ return m_Client->Respond(-215, "V %ld", m_Event->Vps());
+ }
+ }
+ return Next(Last);
+
+ case EndRecording:
+ Last = true;
+ return m_Client->Respond(215, "End of recording information");
+ }
+ }
+ else {
+ bool result;
+ Last = !m_Traverse || m_Index >= Recordings.Count() - 1;
+ result = m_Client->Respond(Last ? 250 : -250, "%d %s", m_Recording->Index() + 1, m_Recording->Title(' ', true));
+
+ if (m_Traverse && !Last) {
+ m_Recording = Recordings.Get(++m_Index);
+ if (m_Recording == NULL) {
+ m_Errno = 501;
+ m_Error = cString::sprintf("Recording \"%d\" not found", m_Index + 1);
+ }
+ }
+ return result;
+ }
+ return false;
+}
+
+// --- cConnectionVTP ---------------------------------------------------------
+
+cConnectionVTP::cConnectionVTP(void):
+ cServerConnection("VTP"),
+ m_LiveSocket(NULL),
+ m_LiveStreamer(NULL),
+ m_FilterSocket(NULL),
+ m_FilterStreamer(NULL),
+ m_RecSocket(NULL),
+ m_DataSocket(NULL),
+ m_LastCommand(NULL),
+ m_StreamType(stTSPIDS),
+ m_FiltersSupport(false),
+ m_RecPlayer(NULL),
+ m_LSTEHandler(NULL),
+ m_LSTCHandler(NULL),
+ m_LSTTHandler(NULL),
+ m_LSTRHandler(NULL)
+{
+}
+
+cConnectionVTP::~cConnectionVTP()
+{
+ if (m_LastCommand != NULL)
+ free(m_LastCommand);
+ delete m_LiveStreamer;
+ delete m_LiveSocket;
+ delete m_RecSocket;
+ delete m_FilterStreamer;
+ delete m_FilterSocket;
+ delete m_DataSocket;
+ delete m_LSTTHandler;
+ delete m_LSTCHandler;
+ delete m_LSTEHandler;
+ delete m_LSTRHandler;
+ delete m_RecPlayer;
+}
+
+inline bool cConnectionVTP::Abort(void) const
+{
+ return m_LiveStreamer && m_LiveStreamer->Abort();
+}
+
+void cConnectionVTP::Welcome(void)
+{
+ Respond(220, "Welcome to Video Disk Recorder (VTP)");
+}
+
+void cConnectionVTP::Reject(void)
+{
+ Respond(221, "Too many clients or client not allowed to connect");
+ cServerConnection::Reject();
+}
+
+void cConnectionVTP::Detach(void)
+{
+ if (m_LiveStreamer) m_LiveStreamer->Detach();
+ if (m_FilterStreamer) m_FilterStreamer->Detach();
+}
+
+void cConnectionVTP::Attach(void)
+{
+ if (m_LiveStreamer) m_LiveStreamer->Attach();
+ if (m_FilterStreamer) m_FilterStreamer->Attach();
+}
+
+bool cConnectionVTP::Command(char *Cmd)
+{
+ char *param = NULL;
+
+ if (Cmd != NULL) {
+ if (m_LastCommand != NULL) {
+ esyslog("ERROR: streamdev: protocol violation (VTP) from %s:%d",
+ RemoteIp().c_str(), RemotePort());
+ return false;
+ }
+
+ if ((param = strchr(Cmd, ' ')) != NULL)
+ *(param++) = '\0';
+ else
+ param = Cmd + strlen(Cmd);
+ m_LastCommand = strdup(Cmd);
+ } else {
+ Cmd = m_LastCommand;
+ param = NULL;
+ }
+
+ if (strcasecmp(Cmd, "LSTE") == 0) return CmdLSTE(param);
+ else if (strcasecmp(Cmd, "LSTR") == 0) return CmdLSTR(param);
+ else if (strcasecmp(Cmd, "LSTT") == 0) return CmdLSTT(param);
+ else if (strcasecmp(Cmd, "LSTC") == 0) return CmdLSTC(param);
+
+ if (param == NULL) {
+ esyslog("ERROR: streamdev: this seriously shouldn't happen at %s:%d",
+ __FILE__, __LINE__);
+ return false;
+ }
+
+ if (strcasecmp(Cmd, "CAPS") == 0) return CmdCAPS(param);
+ else if (strcasecmp(Cmd, "PROV") == 0) return CmdPROV(param);
+ else if (strcasecmp(Cmd, "PORT") == 0) return CmdPORT(param);
+ else if (strcasecmp(Cmd, "READ") == 0) return CmdREAD(param);
+ else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(param);
+ else if (strcasecmp(Cmd, "PLAY") == 0) return CmdPLAY(param);
+ else if (strcasecmp(Cmd, "ADDP") == 0) return CmdADDP(param);
+ else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(param);
+ else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(param);
+ else if (strcasecmp(Cmd, "DELF") == 0) return CmdDELF(param);
+ else if (strcasecmp(Cmd, "ABRT") == 0) return CmdABRT(param);
+ else if (strcasecmp(Cmd, "QUIT") == 0) return CmdQUIT();
+ else if (strcasecmp(Cmd, "SUSP") == 0) return CmdSUSP();
+ // Commands adopted from SVDRP
+ else if (strcasecmp(Cmd, "STAT") == 0) return CmdSTAT(param);
+ else if (strcasecmp(Cmd, "MODT") == 0) return CmdMODT(param);
+ else if (strcasecmp(Cmd, "NEWT") == 0) return CmdNEWT(param);
+ else if (strcasecmp(Cmd, "DELT") == 0) return CmdDELT(param);
+ else if (strcasecmp(Cmd, "NEXT") == 0) return CmdNEXT(param);
+ else if (strcasecmp(Cmd, "NEWC") == 0) return CmdNEWC(param);
+ else if (strcasecmp(Cmd, "MODC") == 0) return CmdMODC(param);
+ else if (strcasecmp(Cmd, "MOVC") == 0) return CmdMOVC(param);
+ else if (strcasecmp(Cmd, "DELC") == 0) return CmdDELC(param);
+ else if (strcasecmp(Cmd, "DELR") == 0) return CmdDELR(param);
+ else if (strcasecmp(Cmd, "RENR") == 0) return CmdRENR(param);
+ else
+ return Respond(500, "Unknown Command \"%s\"", Cmd);
+}
+
+bool cConnectionVTP::CmdCAPS(char *Opts)
+{
+ if (strcasecmp(Opts, "TS") == 0) {
+ m_StreamType = stTS;
+ return Respond(220, "Capability \"%s\" accepted", Opts);
+ }
+
+ if (strcasecmp(Opts, "TSPIDS") == 0) {
+ m_StreamType = stTSPIDS;
+ return Respond(220, "Capability \"%s\" accepted", Opts);
+ }
+
+ if (strcasecmp(Opts, "PS") == 0) {
+ m_StreamType = stPS;
+ return Respond(220, "Capability \"%s\" accepted", Opts);
+ }
+
+ if (strcasecmp(Opts, "PES") == 0) {
+ m_StreamType = stPES;
+ return Respond(220, "Capability \"%s\" accepted", Opts);
+ }
+
+ if (strcasecmp(Opts, "EXTERN") == 0) {
+ m_StreamType = stExtern;
+ return Respond(220, "Capability \"%s\" accepted", Opts);
+ }
+
+ //
+ // Deliver section filters data in separate, channel-independent data stream
+ //
+ if (strcasecmp(Opts, "FILTERS") == 0) {
+ m_FiltersSupport = true;
+ return Respond(220, "Capability \"%s\" accepted", Opts);
+ }
+
+ return Respond(561, "Capability \"%s\" not known", Opts);
+}
+
+bool cConnectionVTP::CmdPROV(char *Opts)
+{
+ const cChannel *chan;
+ int prio;
+ char *ep;
+
+ prio = strtol(Opts, &ep, 10);
+ if (ep == Opts || !isspace(*ep))
+ return Respond(501, "Use: PROV Priority Channel");
+
+ Opts = skipspace(ep);
+ if ((chan = ChannelFromString(Opts)) == NULL)
+ return Respond(550, "Undefined channel \"%s\"", Opts);
+
+ return GetDevice(chan, prio) != NULL
+ ? Respond(220, "Channel available")
+ : Respond(560, "Channel not available");
+}
+
+bool cConnectionVTP::CmdPORT(char *Opts)
+{
+ uint id, dataport = 0;
+ char dataip[20];
+ char *ep, *ipoffs;
+ int n;
+
+ id = strtoul(Opts, &ep, 10);
+ if (ep == Opts || !isspace(*ep))
+ return Respond(500, "Use: PORT Id Destination");
+
+ if (id >= si_Count)
+ return Respond(501, "Wrong connection id %d", id);
+
+ Opts = skipspace(ep);
+ n = 0;
+ ipoffs = dataip;
+ while ((ep = strchr(Opts, ',')) != NULL) {
+ if (n < 4) {
+ memcpy(ipoffs, Opts, ep - Opts);
+ ipoffs += ep - Opts;
+ if (n < 3) *(ipoffs++) = '.';
+ } else if (n == 4) {
+ *ep = 0;
+ dataport = strtoul(Opts, NULL, 10) << 8;
+ } else
+ break;
+ Opts = ep + 1;
+ ++n;
+ }
+ *ipoffs = '\0';
+
+ if (n != 5)
+ return Respond(501, "Argument count invalid (must be 6 values)");
+
+ dataport |= strtoul(Opts, NULL, 10);
+
+ isyslog("Streamdev: Setting data connection to %s:%d", dataip, dataport);
+
+ switch (id) {
+ case siLiveFilter:
+ m_FiltersSupport = true;
+ if(m_FilterStreamer)
+ m_FilterStreamer->Stop();
+ delete m_FilterSocket;
+
+ m_FilterSocket = new cTBSocket(SOCK_STREAM);
+ if (!m_FilterSocket->Connect(dataip, dataport)) {
+ esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s",
+ dataip, dataport, strerror(errno));
+ DELETENULL(m_FilterSocket);
+ return Respond(551, "Couldn't open data connection");
+ }
+
+ if(!m_FilterStreamer)
+ m_FilterStreamer = new cStreamdevFilterStreamer;
+ m_FilterStreamer->Start(m_FilterSocket);
+ m_FilterStreamer->Activate(true);
+
+ return Respond(220, "Port command ok, data connection opened");
+ break;
+
+ case siLive:
+ if(m_LiveSocket && m_LiveStreamer)
+ m_LiveStreamer->Stop();
+ delete m_LiveSocket;
+
+ m_LiveSocket = new cTBSocket(SOCK_STREAM);
+ if (!m_LiveSocket->Connect(dataip, dataport)) {
+ esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s",
+ dataip, dataport, strerror(errno));
+ DELETENULL(m_LiveSocket);
+ return Respond(551, "Couldn't open data connection");
+ }
+
+ if (!m_LiveSocket->SetDSCP())
+ LOG_ERROR_STR("unable to set DSCP sockopt");
+ if (m_LiveStreamer)
+ m_LiveStreamer->Start(m_LiveSocket);
+
+ return Respond(220, "Port command ok, data connection opened");
+ break;
+
+ case siReplay:
+ delete m_RecSocket;
+
+ m_RecSocket = new cTBSocket(SOCK_STREAM);
+ if (!m_RecSocket->Connect(dataip, dataport)) {
+ esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s",
+ dataip, dataport, strerror(errno));
+ DELETENULL(m_RecSocket);
+ return Respond(551, "Couldn't open data connection");
+ }
+
+ if (!m_RecSocket->SetDSCP())
+ LOG_ERROR_STR("unable to set DSCP sockopt");
+
+ return Respond(220, "Port command ok, data connection opened");
+ break;
+
+ case siDataRespond:
+ delete m_DataSocket;
+
+ m_DataSocket = new cTBSocket(SOCK_STREAM);
+ if (!m_DataSocket->Connect(dataip, dataport)) {
+ esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s",
+ dataip, dataport, strerror(errno));
+ DELETENULL(m_DataSocket);
+ return Respond(551, "Couldn't open data connection");
+ }
+ return Respond(220, "Port command ok, data connection opened");
+ break;
+
+ default:
+ return Respond(501, "No handler for id %u", id);
+ }
+}
+
+bool cConnectionVTP::CmdREAD(char *Opts)
+{
+ if (*Opts) {
+ char *tail;
+ uint64_t position = strtoll(Opts, &tail, 10);
+ if (tail && tail != Opts) {
+ tail = skipspace(tail);
+ if (tail && tail != Opts) {
+ int size = strtol(tail, NULL, 10);
+ uint8_t* data = (uint8_t*)malloc(size+4);
+ unsigned long count_readed = m_RecPlayer->getBlock(data, position, size);
+ unsigned long count_written = m_RecSocket->SysWrite(data, count_readed);
+
+ free(data);
+ return Respond(220, "%lu Bytes submitted", count_written);
+ }
+ else {
+ return Respond(501, "Missing position");
+ }
+ }
+ else {
+ return Respond(501, "Missing size");
+ }
+ }
+ else {
+ return Respond(501, "Missing position");
+ }
+}
+
+bool cConnectionVTP::CmdTUNE(char *Opts)
+{
+ const cChannel *chan;
+ cDevice *dev;
+
+ if ((chan = ChannelFromString(Opts)) == NULL)
+ return Respond(550, "Undefined channel \"%s\"", Opts);
+
+ if ((dev = GetDevice(chan, 0)) == NULL)
+ return Respond(560, "Channel not available");
+
+ if (!dev->SwitchChannel(chan, false))
+ return Respond(560, "Channel not available");
+
+ delete m_LiveStreamer;
+ m_LiveStreamer = new cStreamdevLiveStreamer(1);
+ m_LiveStreamer->SetChannel(chan, m_StreamType);
+ m_LiveStreamer->SetDevice(dev);
+ if(m_LiveSocket)
+ m_LiveStreamer->Start(m_LiveSocket);
+
+ if(m_FiltersSupport) {
+ if(!m_FilterStreamer)
+ m_FilterStreamer = new cStreamdevFilterStreamer;
+ m_FilterStreamer->SetDevice(dev);
+ //m_FilterStreamer->SetChannel(chan);
+ }
+
+ return Respond(220, "Channel tuned");
+}
+
+bool cConnectionVTP::CmdPLAY(char *Opts)
+{
+ if (*Opts) {
+ if (isnumber(Opts)) {
+ cRecording *recording = Recordings.Get(strtol(Opts, NULL, 10) - 1);
+ if (recording) {
+ if (m_RecPlayer) {
+ delete m_RecPlayer;
+ }
+ m_RecPlayer = new RecPlayer(recording);
+ return Respond(220, "%llu (Bytes), %u (Frames)", (long long unsigned int) m_RecPlayer->getLengthBytes(), (unsigned int) m_RecPlayer->getLengthFrames());
+ }
+ else {
+ return Respond(550, "Recording \"%s\" not found", Opts);
+ }
+ }
+ else {
+ return Respond(500, "Use: PLAY record");
+ }
+ }
+ else {
+ return Respond(500, "Use: PLAY record");
+ }
+}
+
+bool cConnectionVTP::CmdADDP(char *Opts)
+{
+ int pid;
+ char *end;
+
+ pid = strtoul(Opts, &end, 10);
+ if (end == Opts || (*end != '\0' && *end != ' '))
+ return Respond(500, "Use: ADDP Pid");
+
+ return m_LiveStreamer && m_LiveStreamer->SetPid(pid, true)
+ ? Respond(220, "Pid %d available", pid)
+ : Respond(560, "Pid %d not available", pid);
+}
+
+bool cConnectionVTP::CmdDELP(char *Opts)
+{
+ int pid;
+ char *end;
+
+ pid = strtoul(Opts, &end, 10);
+ if (end == Opts || (*end != '\0' && *end != ' '))
+ return Respond(500, "Use: DELP Pid");
+
+ return m_LiveStreamer && m_LiveStreamer->SetPid(pid, false)
+ ? Respond(220, "Pid %d stopped", pid)
+ : Respond(560, "Pid %d not transferring", pid);
+}
+
+bool cConnectionVTP::CmdADDF(char *Opts)
+{
+ int pid, tid, mask;
+ char *ep;
+
+ if (m_FilterStreamer == NULL)
+ return Respond(560, "Can't set filters without a filter stream");
+
+ pid = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != ' '))
+ return Respond(500, "Use: ADDF Pid Tid Mask");
+ Opts = skipspace(ep);
+ tid = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != ' '))
+ return Respond(500, "Use: ADDF Pid Tid Mask");
+ Opts = skipspace(ep);
+ mask = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != '\0' && *ep != ' '))
+ return Respond(500, "Use: ADDF Pid Tid Mask");
+
+ return m_FilterStreamer->SetFilter(pid, tid, mask, true)
+ ? Respond(220, "Filter %d transferring", pid)
+ : Respond(560, "Filter %d not available", pid);
+}
+
+bool cConnectionVTP::CmdDELF(char *Opts)
+{
+ int pid, tid, mask;
+ char *ep;
+
+ if (m_FilterStreamer == NULL)
+ return Respond(560, "Can't delete filters without a stream");
+
+ pid = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != ' '))
+ return Respond(500, "Use: DELF Pid Tid Mask");
+ Opts = skipspace(ep);
+ tid = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != ' '))
+ return Respond(500, "Use: DELF Pid Tid Mask");
+ Opts = skipspace(ep);
+ mask = strtol(Opts, &ep, 10);
+ if (ep == Opts || (*ep != '\0' && *ep != ' '))
+ return Respond(500, "Use: DELF Pid Tid Mask");
+
+ m_FilterStreamer->SetFilter(pid, tid, mask, false);
+ return Respond(220, "Filter %d stopped", pid);
+}
+
+bool cConnectionVTP::CmdABRT(char *Opts)
+{
+ uint id;
+ char *ep;
+
+ id = strtoul(Opts, &ep, 10);
+ if (ep == Opts || (*ep != '\0' && *ep != ' '))
+ return Respond(500, "Use: ABRT Id");
+
+ switch (id) {
+ case siLive:
+ DELETENULL(m_LiveStreamer);
+ DELETENULL(m_LiveSocket);
+ break;
+ case siLiveFilter:
+ DELETENULL(m_FilterStreamer);
+ DELETENULL(m_FilterSocket);
+ break;
+ case siReplay:
+ DELETENULL(m_RecPlayer);
+ DELETENULL(m_RecSocket);
+ break;
+ case siDataRespond:
+ DELETENULL(m_DataSocket);
+ break;
+ default:
+ return Respond(501, "Wrong connection id %d", id);
+ break;
+
+ }
+
+ return Respond(220, "Data connection closed");
+}
+
+bool cConnectionVTP::CmdQUIT(void)
+{
+ DeferClose();
+ return Respond(221, "Video Disk Recorder closing connection");
+}
+
+bool cConnectionVTP::CmdSUSP(void)
+{
+ if (StreamdevServerSetup.SuspendMode == smAlways || cSuspendCtl::IsActive())
+ return Respond(220, "Server is suspended");
+ else if (StreamdevServerSetup.SuspendMode == smOffer
+ && StreamdevServerSetup.AllowSuspend) {
+ cControl::Launch(new cSuspendCtl);
+ return Respond(220, "Server is suspended");
+ } else
+ return Respond(550, "Client may not suspend server");
+}
+
+// Functions extended from SVDRP
+
+template<class cHandler>
+bool cConnectionVTP::CmdLSTX(cHandler *&Handler, char *Option)
+{
+ if (Option != NULL) {
+ delete Handler;
+ Handler = new cHandler(this, Option);
+ }
+
+ bool last = false;
+ bool result = false;
+ if (Handler != NULL)
+ result = Handler->Next(last);
+ else
+ esyslog("ERROR: vdr streamdev: Handler in LSTX command is NULL");
+ if (!result || last)
+ DELETENULL(Handler);
+
+ return result;
+}
+
+bool cConnectionVTP::CmdLSTE(char *Option)
+{
+ return CmdLSTX(m_LSTEHandler, Option);
+}
+
+bool cConnectionVTP::CmdLSTC(char *Option)
+{
+ return CmdLSTX(m_LSTCHandler, Option);
+}
+
+bool cConnectionVTP::CmdLSTT(char *Option)
+{
+ return CmdLSTX(m_LSTTHandler, Option);
+}
+
+bool cConnectionVTP::CmdLSTR(char *Option)
+{
+ return CmdLSTX(m_LSTRHandler, Option);
+}
+
+// Functions adopted from SVDRP
+#define INIT_WRAPPER() bool _res
+#define Reply(c,m...) _res = Respond(c,m)
+#define EXIT_WRAPPER() return _res
+
+bool cConnectionVTP::CmdSTAT(const char *Option)
+{
+ INIT_WRAPPER();
+ if (*Option) {
+ if (strcasecmp(Option, "DISK") == 0) {
+ int FreeMB, UsedMB;
+ int Percent = VideoDiskSpace(&FreeMB, &UsedMB);
+ Reply(250, "%dMB %dMB %d%%", FreeMB + UsedMB, FreeMB, Percent);
+ }
+ else if (strcasecmp(Option, "NAME") == 0) {
+ Reply(250, "vdr - The Video Disk Recorder with Streamdev-Server");
+ }
+ else if (strcasecmp(Option, "VERSION") == 0) {
+ Reply(250, "VDR: %s | Streamdev: %s", VDRVERSION, VERSION);
+ }
+ else if (strcasecmp(Option, "RECORDS") == 0) {
+ bool recordings = Recordings.Load();
+ Recordings.Sort();
+ if (recordings) {
+ cRecording *recording = Recordings.Last();
+ Reply(250, "%d", recording->Index() + 1);
+ }
+ else {
+ Reply(250, "0");
+ }
+ }
+ else if (strcasecmp(Option, "CHANNELS") == 0) {
+ Reply(250, "%d", Channels.MaxNumber());
+ }
+ else if (strcasecmp(Option, "TIMERS") == 0) {
+ Reply(250, "%d", Timers.Count());
+ }
+ else if (strcasecmp(Option, "CHARSET") == 0) {
+ Reply(250, "%s", cCharSetConv::SystemCharacterTable());
+ }
+ else if (strcasecmp(Option, "TIME") == 0) {
+ time_t timeNow = time(NULL);
+ struct tm* timeStruct = localtime(&timeNow);
+ int timeOffset = timeStruct->tm_gmtoff;
+
+ Reply(250, "%lu %i", (unsigned long) timeNow, timeOffset);
+ }
+ else
+ Reply(501, "Invalid Option \"%s\"", Option);
+ }
+ else
+ Reply(501, "No option given");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdMODT(const char *Option)
+{
+ INIT_WRAPPER();
+ if (*Option) {
+ char *tail;
+ int n = strtol(Option, &tail, 10);
+ if (tail && tail != Option) {
+ tail = skipspace(tail);
+ cTimer *timer = Timers.Get(n - 1);
+ if (timer) {
+ cTimer t = *timer;
+ if (strcasecmp(tail, "ON") == 0)
+ t.SetFlags(tfActive);
+ else if (strcasecmp(tail, "OFF") == 0)
+ t.ClrFlags(tfActive);
+ else if (!t.Parse(tail)) {
+ Reply(501, "Error in timer settings");
+ EXIT_WRAPPER();
+ }
+ *timer = t;
+ Timers.SetModified();
+ isyslog("timer %s modified (%s)", *timer->ToDescr(),
+ timer->HasFlags(tfActive) ? "active" : "inactive");
+ Reply(250, "%d %s", timer->Index() + 1, *timer->ToText());
+ } else
+ Reply(501, "Timer \"%d\" not defined", n);
+ } else
+ Reply(501, "Error in timer number");
+ } else
+ Reply(501, "Missing timer settings");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdNEWT(const char *Option)
+{
+ INIT_WRAPPER();
+ if (*Option) {
+ cTimer *timer = new cTimer;
+ if (timer->Parse(Option)) {
+ cTimer *t = Timers.GetTimer(timer);
+ if (!t) {
+ Timers.Add(timer);
+ Timers.SetModified();
+ isyslog("timer %s added", *timer->ToDescr());
+ Reply(250, "%d %s", timer->Index() + 1, *timer->ToText());
+ EXIT_WRAPPER();
+ } else
+ Reply(550, "Timer already defined: %d %s", t->Index() + 1,
+ *t->ToText());
+ } else
+ Reply(501, "Error in timer settings");
+ delete timer;
+ } else
+ Reply(501, "Missing timer settings");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdDELT(const char *Option)
+{
+ INIT_WRAPPER();
+ if (*Option) {
+ int number = 0;
+ bool force = false;
+ char buf[strlen(Option) + 1];
+ strcpy(buf, Option);
+ const char *delim = " \t";
+ char *strtok_next;
+ char *p = strtok_r(buf, delim, &strtok_next);
+
+ if (isnumber(p)) {
+ number = strtol(p, NULL, 10) - 1;
+ }
+ else if (strcasecmp(p, "FORCE") == 0) {
+ force = true;
+ }
+ if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
+ if (isnumber(p)) {
+ number = strtol(p, NULL, 10) - 1;
+ }
+ else if (strcasecmp(p, "FORCE") == 0) {
+ force = true;
+ }
+ else {
+ Reply(501, "Timer not found or wrong syntax");
+ }
+ }
+
+ cTimer *timer = Timers.Get(number);
+ if (timer) {
+ if (timer->Recording()) {
+ if (force) {
+ timer->Skip();
+ cRecordControls::Process(time(NULL));
+ }
+ else {
+ Reply(550, "Timer \"%i\" is recording", number);
+ EXIT_WRAPPER();
+ }
+ }
+ isyslog("deleting timer %s", *timer->ToDescr());
+ Timers.Del(timer);
+ Timers.SetModified();
+ Reply(250, "Timer \"%i\" deleted", number);
+ } else
+ Reply(501, "Timer \"%i\" not defined", number);
+ } else
+ Reply(501, "Missing timer option");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdNEXT(const char *Option)
+{
+ INIT_WRAPPER();
+ cTimer *t = Timers.GetNextActiveTimer();
+ if (t) {
+ time_t Start = t->StartTime();
+ int Number = t->Index() + 1;
+ if (!*Option)
+ Reply(250, "%d %s", Number, *TimeToString(Start));
+ else if (strcasecmp(Option, "ABS") == 0)
+ Reply(250, "%d %ld", Number, Start);
+ else if (strcasecmp(Option, "REL") == 0)
+ Reply(250, "%d %ld", Number, Start - time(NULL));
+ else
+ Reply(501, "Unknown option: \"%s\"", Option);
+ }
+ else
+ Reply(550, "No active timers");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdNEWC(const char *Option)
+{
+ INIT_WRAPPER();
+ if (*Option) {
+ cChannel ch;
+ if (ch.Parse(Option)) {
+ if (Channels.HasUniqueChannelID(&ch)) {
+ cChannel *channel = new cChannel;
+ *channel = ch;
+ Channels.Add(channel);
+ Channels.ReNumber();
+ Channels.SetModified(true);
+ isyslog("new channel %d %s", channel->Number(), *channel->ToText());
+ Reply(250, "%d %s", channel->Number(), *channel->ToText());
+ }
+ else {
+ Reply(501, "Channel settings are not unique");
+ }
+ }
+ else {
+ Reply(501, "Error in channel settings");
+ }
+ }
+ else {
+ Reply(501, "Missing channel settings");
+ }
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdMODC(const char *Option)
+{
+ INIT_WRAPPER();
+ if (*Option) {
+ char *tail;
+ int n = strtol(Option, &tail, 10);
+ if (tail && tail != Option) {
+ tail = skipspace(tail);
+ if (!Channels.BeingEdited()) {
+ cChannel *channel = Channels.GetByNumber(n);
+ if (channel) {
+ cChannel ch;
+ if (ch.Parse(tail)) {
+ if (Channels.HasUniqueChannelID(&ch, channel)) {
+ *channel = ch;
+ Channels.ReNumber();
+ Channels.SetModified(true);
+ isyslog("modifed channel %d %s", channel->Number(), *channel->ToText());
+ Reply(250, "%d %s", channel->Number(), *channel->ToText());
+ }
+ else {
+ Reply(501, "Channel settings are not unique");
+ }
+ }
+ else {
+ Reply(501, "Error in channel settings");
+ }
+ }
+ else {
+ Reply(501, "Channel \"%d\" not defined", n);
+ }
+ }
+ else {
+ Reply(550, "Channels are being edited - try again later");
+ }
+ }
+ else {
+ Reply(501, "Error in channel number");
+ }
+ }
+ else {
+ Reply(501, "Missing channel settings");
+ }
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdMOVC(const char *Option)
+{
+ INIT_WRAPPER();
+ if (*Option) {
+ if (!Channels.BeingEdited() && !Timers.BeingEdited()) {
+ char *tail;
+ int From = strtol(Option, &tail, 10);
+ if (tail && tail != Option) {
+ tail = skipspace(tail);
+ if (tail && tail != Option) {
+ int To = strtol(tail, NULL, 10);
+ int CurrentChannelNr = cDevice::CurrentChannel();
+ cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr);
+ cChannel *FromChannel = Channels.GetByNumber(From);
+ if (FromChannel) {
+ cChannel *ToChannel = Channels.GetByNumber(To);
+ if (ToChannel) {
+ int FromNumber = FromChannel->Number();
+ int ToNumber = ToChannel->Number();
+ if (FromNumber != ToNumber) {
+ Channels.Move(FromChannel, ToChannel);
+ Channels.ReNumber();
+ Channels.SetModified(true);
+ if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) {
+ if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) {
+ Channels.SwitchTo(CurrentChannel->Number());
+ }
+ else {
+ cDevice::SetCurrentChannel(CurrentChannel);
+ }
+ }
+ isyslog("channel %d moved to %d", FromNumber, ToNumber);
+ Reply(250,"Channel \"%d\" moved to \"%d\"", From, To);
+ }
+ else {
+ Reply(501, "Can't move channel to same postion");
+ }
+ }
+ else {
+ Reply(501, "Channel \"%d\" not defined", To);
+ }
+ }
+ else {
+ Reply(501, "Channel \"%d\" not defined", From);
+ }
+ }
+ else {
+ Reply(501, "Error in channel number");
+ }
+ }
+ else {
+ Reply(501, "Error in channel number");
+ }
+ }
+ else {
+ Reply(550, "Channels or timers are being edited - try again later");
+ }
+ }
+ else {
+ Reply(501, "Missing channel number");
+ }
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdDELC(const char *Option)
+{
+ INIT_WRAPPER();
+ if (*Option) {
+ if (isnumber(Option)) {
+ if (!Channels.BeingEdited()) {
+ cChannel *channel = Channels.GetByNumber(strtol(Option, NULL, 10));
+ if (channel) {
+ for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) {
+ if (timer->Channel() == channel) {
+ Reply(550, "Channel \"%s\" is in use by timer %d", Option, timer->Index() + 1);
+ return false;
+ }
+ }
+ int CurrentChannelNr = cDevice::CurrentChannel();
+ cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr);
+ if (CurrentChannel && channel == CurrentChannel) {
+ int n = Channels.GetNextNormal(CurrentChannel->Index());
+ if (n < 0)
+ n = Channels.GetPrevNormal(CurrentChannel->Index());
+ CurrentChannel = Channels.Get(n);
+ CurrentChannelNr = 0; // triggers channel switch below
+ }
+ Channels.Del(channel);
+ Channels.ReNumber();
+ Channels.SetModified(true);
+ isyslog("channel %s deleted", Option);
+ if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) {
+ if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring())
+ Channels.SwitchTo(CurrentChannel->Number());
+ else
+ cDevice::SetCurrentChannel(CurrentChannel);
+ }
+ Reply(250, "Channel \"%s\" deleted", Option);
+ }
+ else
+ Reply(501, "Channel \"%s\" not defined", Option);
+ }
+ else
+ Reply(550, "Channels are being edited - try again later");
+ }
+ else
+ Reply(501, "Error in channel number \"%s\"", Option);
+ }
+ else {
+ Reply(501, "Missing channel number");
+ }
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdDELR(const char *Option)
+{
+ INIT_WRAPPER();
+ if (*Option) {
+ if (isnumber(Option)) {
+ cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
+ if (recording) {
+ cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
+ if (!rc) {
+ if (recording->Delete()) {
+ Reply(250, "Recording \"%s\" deleted", Option);
+ ::Recordings.DelByName(recording->FileName());
+ }
+ else
+ Reply(554, "Error while deleting recording!");
+ }
+ else
+ Reply(550, "Recording \"%s\" is in use by timer %d", Option, rc->Timer()->Index() + 1);
+ }
+ else
+ Reply(550, "Recording \"%s\" not found%s", Option, Recordings.Count() ? "" : " (use LSTR before deleting)");
+ }
+ else
+ Reply(501, "Error in recording number \"%s\"", Option);
+ }
+ else
+ Reply(501, "Missing recording number");
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::CmdRENR(const char *Option)
+{
+ INIT_WRAPPER();
+#if defined(LIEMIKUUTIO)
+ bool recordings = Recordings.Update(true);
+ if (recordings) {
+ if (*Option) {
+ char *tail;
+ int n = strtol(Option, &tail, 10);
+ cRecording *recording = Recordings.Get(n - 1);
+ if (recording && tail && tail != Option) {
+#if APIVERSNUM < 10704
+ int priority = recording->priority;
+ int lifetime = recording->lifetime;
+#endif
+ char *oldName = strdup(recording->Name());
+ tail = skipspace(tail);
+#if APIVERSNUM < 10704
+ if (recording->Rename(tail, &priority, &lifetime)) {
+#else
+ if (recording->Rename(tail)) {
+#endif
+ Reply(250, "Renamed \"%s\" to \"%s\"", oldName, recording->Name());
+ Recordings.ChangeState();
+ Recordings.TouchUpdate();
+ }
+ else {
+ Reply(501, "Renaming \"%s\" to \"%s\" failed", oldName, tail);
+ }
+ free(oldName);
+ }
+ else {
+ Reply(501, "Recording not found or wrong syntax");
+ }
+ }
+ else {
+ Reply(501, "Missing Input settings");
+ }
+ }
+ else {
+ Reply(550, "No recordings available");
+ }
+#else
+ Reply(501, "Rename not supported, please use LIEMIEXT");
+#endif /* LIEMIKUUTIO */
+ EXIT_WRAPPER();
+}
+
+bool cConnectionVTP::Respond(int Code, const char *Message, ...)
+{
+ va_list ap;
+ va_start(ap, Message);
+#if APIVERSNUM < 10515
+ char *buffer;
+ if (vasprintf(&buffer, Message, ap) < 0)
+ buffer = strdup("???");
+ cString str(buffer, true);
+#else
+ cString str = cString::sprintf(Message, ap);
+#endif
+ va_end(ap);
+
+ if (Code >= 0 && m_LastCommand != NULL) {
+ free(m_LastCommand);
+ m_LastCommand = NULL;
+ }
+
+ return cServerConnection::Respond("%03d%c%s", Code >= 0,
+ Code < 0 ? -Code : Code,
+ Code < 0 ? '-' : ' ', *str);
+}
diff --git a/plugins/streamdev/streamdev-cvs/server/connectionVTP.h b/plugins/streamdev/streamdev-cvs/server/connectionVTP.h
new file mode 100644
index 0000000..b938fe6
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/connectionVTP.h
@@ -0,0 +1,93 @@
+#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
+#define VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
+
+#include "server/connection.h"
+#include "server/recplayer.h"
+
+class cTBSocket;
+class cStreamdevLiveStreamer;
+class cStreamdevFilterStreamer;
+class cLSTEHandler;
+class cLSTCHandler;
+class cLSTTHandler;
+class cLSTRHandler;
+
+class cConnectionVTP: public cServerConnection {
+ friend class cLSTEHandler;
+#if !defined __GNUC__ || __GNUC__ >= 3
+ using cServerConnection::Respond;
+#endif
+
+private:
+ cTBSocket *m_LiveSocket;
+ cStreamdevLiveStreamer *m_LiveStreamer;
+ cTBSocket *m_FilterSocket;
+ cStreamdevFilterStreamer *m_FilterStreamer;
+ cTBSocket *m_RecSocket;
+ cTBSocket *m_DataSocket;
+
+ char *m_LastCommand;
+ eStreamType m_StreamType;
+ bool m_FiltersSupport;
+ RecPlayer *m_RecPlayer;
+
+ // Members adopted for SVDRP
+ cLSTEHandler *m_LSTEHandler;
+ cLSTCHandler *m_LSTCHandler;
+ cLSTTHandler *m_LSTTHandler;
+ cLSTRHandler *m_LSTRHandler;
+
+protected:
+ template<class cHandler>
+ bool CmdLSTX(cHandler *&Handler, char *Option);
+
+public:
+ cConnectionVTP(void);
+ virtual ~cConnectionVTP();
+
+ virtual void Welcome(void);
+ virtual void Reject(void);
+
+ virtual bool Abort(void) const;
+ virtual void Detach(void);
+ virtual void Attach(void);
+
+ virtual bool Command(char *Cmd);
+ bool CmdCAPS(char *Opts);
+ bool CmdPROV(char *Opts);
+ bool CmdPORT(char *Opts);
+ bool CmdREAD(char *Opts);
+ bool CmdTUNE(char *Opts);
+ bool CmdPLAY(char *Opts);
+ bool CmdADDP(char *Opts);
+ bool CmdDELP(char *Opts);
+ bool CmdADDF(char *Opts);
+ bool CmdDELF(char *Opts);
+ bool CmdABRT(char *Opts);
+ bool CmdQUIT(void);
+ bool CmdSUSP(void);
+
+ // Thread-safe implementations of SVDRP commands
+ bool CmdLSTE(char *Opts);
+ bool CmdLSTC(char *Opts);
+ bool CmdLSTT(char *Opts);
+ bool CmdLSTR(char *Opts);
+
+ // Commands adopted from SVDRP
+ bool CmdSTAT(const char *Option);
+ bool CmdMODT(const char *Option);
+ bool CmdNEWT(const char *Option);
+ bool CmdDELT(const char *Option);
+ bool CmdNEXT(const char *Option);
+ bool CmdNEWC(const char *Option);
+ bool CmdMODC(const char *Option);
+ bool CmdMOVC(const char *Option);
+ bool CmdDELC(const char *Option);
+ bool CmdDELR(const char *Option);
+ bool CmdRENR(const char *Option);
+
+ bool Respond(int Code, const char *Message, ...)
+ __attribute__ ((format (printf, 3, 4)));
+};
+
+#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
diff --git a/plugins/streamdev/streamdev-cvs/server/http/CVS/Entries b/plugins/streamdev/streamdev-cvs/server/http/CVS/Entries
new file mode 100644
index 0000000..1784810
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/http/CVS/Entries
@@ -0,0 +1 @@
+D
diff --git a/plugins/streamdev/streamdev-cvs/server/http/CVS/Repository b/plugins/streamdev/streamdev-cvs/server/http/CVS/Repository
new file mode 100644
index 0000000..49fadb8
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/http/CVS/Repository
@@ -0,0 +1 @@
+streamdev/server/http
diff --git a/plugins/streamdev/streamdev-cvs/server/http/CVS/Root b/plugins/streamdev/streamdev-cvs/server/http/CVS/Root
new file mode 100644
index 0000000..2c7f6ce
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/http/CVS/Root
@@ -0,0 +1 @@
+:pserver:anoncvs@vdr-developer.org:/var/cvsroot
diff --git a/plugins/streamdev/streamdev-cvs/server/livefilter.c b/plugins/streamdev/streamdev-cvs/server/livefilter.c
new file mode 100644
index 0000000..67b0e37
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/livefilter.c
@@ -0,0 +1,39 @@
+/*
+ * $Id: livefilter.c,v 1.7 2009/02/13 13:02:40 schmirl Exp $
+ */
+
+#include "server/livefilter.h"
+#include "server/streamer.h"
+#include "common.h"
+
+#ifndef TS_SYNC_BYTE
+# define TS_SYNC_BYTE 0x47
+#endif
+
+cStreamdevLiveFilter::cStreamdevLiveFilter(cStreamdevStreamer *Streamer) {
+ m_Streamer = Streamer;
+}
+
+void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
+{
+ uchar buffer[TS_SIZE];
+ int length = Length;
+ int pos = 0;
+
+ while (length > 0) {
+ int chunk = min(length, TS_SIZE - 5);
+ buffer[0] = TS_SYNC_BYTE;
+ buffer[1] = ((Pid >> 8) & 0x3f) | (pos==0 ? 0x40 : 0); /* bit 6: payload unit start indicator (PUSI) */
+ buffer[2] = Pid & 0xff;
+ buffer[3] = Tid;
+ // this makes it a proprietary stream
+ buffer[4] = (uchar)chunk;
+ memcpy(buffer + 5, Data + pos, chunk);
+ length -= chunk;
+ pos += chunk;
+
+ int p = m_Streamer->Put(buffer, TS_SIZE);
+ if (p != TS_SIZE)
+ m_Streamer->ReportOverflow(TS_SIZE - p);
+ }
+}
diff --git a/plugins/streamdev/streamdev-cvs/server/livefilter.h b/plugins/streamdev/streamdev-cvs/server/livefilter.h
new file mode 100644
index 0000000..13e8956
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/livefilter.h
@@ -0,0 +1,32 @@
+/*
+ * $Id: livefilter.h,v 1.5 2008/04/07 14:27:31 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMEV_LIVEFILTER_H
+#define VDR_STREAMEV_LIVEFILTER_H
+
+#include <vdr/config.h>
+
+#include <vdr/filter.h>
+
+class cStreamdevStreamer;
+
+class cStreamdevLiveFilter: public cFilter {
+private:
+ cStreamdevStreamer *m_Streamer;
+
+protected:
+ virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
+
+public:
+ cStreamdevLiveFilter(cStreamdevStreamer *Streamer);
+
+ void Set(u_short Pid, u_char Tid, u_char Mask) {
+ cFilter::Set(Pid, Tid, Mask);
+ }
+ void Del(u_short Pid, u_char Tid, u_char Mask) {
+ cFilter::Del(Pid, Tid, Mask);
+ }
+};
+
+#endif // VDR_STREAMEV_LIVEFILTER_H
diff --git a/plugins/streamdev/streamdev-cvs/server/livestreamer.c b/plugins/streamdev/streamdev-cvs/server/livestreamer.c
new file mode 100644
index 0000000..3162fa2
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/livestreamer.c
@@ -0,0 +1,689 @@
+#include <assert.h>
+
+#include <libsi/section.h>
+#include <libsi/descriptor.h>
+
+#include "remux/ts2ps.h"
+#include "remux/ts2pes.h"
+#include "remux/ts2es.h"
+#include "remux/extern.h"
+
+#include <vdr/ringbuffer.h>
+
+#include "server/livestreamer.h"
+#include "server/livefilter.h"
+#include "common.h"
+
+using namespace Streamdev;
+
+// --- cStreamdevLiveReceiver -------------------------------------------------
+
+class cStreamdevLiveReceiver: public cReceiver {
+ friend class cStreamdevStreamer;
+
+private:
+ cStreamdevStreamer *m_Streamer;
+
+protected:
+ virtual void Activate(bool On);
+ virtual void Receive(uchar *Data, int Length);
+
+public:
+ cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, tChannelID ChannelID, int Priority, const int *Pids);
+ virtual ~cStreamdevLiveReceiver();
+};
+
+cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, tChannelID ChannelID,
+ int Priority, const int *Pids):
+ cReceiver(ChannelID, Priority, 0, Pids),
+ m_Streamer(Streamer)
+{
+}
+
+cStreamdevLiveReceiver::~cStreamdevLiveReceiver()
+{
+ Dprintf("Killing live receiver\n");
+ Detach();
+}
+
+void cStreamdevLiveReceiver::Receive(uchar *Data, int Length) {
+ int p = m_Streamer->Receive(Data, Length);
+ if (p != Length)
+ m_Streamer->ReportOverflow(Length - p);
+}
+
+inline void cStreamdevLiveReceiver::Activate(bool On)
+{
+ Dprintf("LiveReceiver->Activate(%d)\n", On);
+ m_Streamer->Activate(On);
+}
+
+// --- cStreamdevPatFilter ----------------------------------------------------
+
+class cStreamdevPatFilter : public cFilter {
+private:
+ int pmtPid;
+ int pmtSid;
+ int pmtVersion;
+ uchar tspat_buf[TS_SIZE];
+ cStreamdevBuffer siBuffer;
+
+ const cChannel *m_Channel;
+ cStreamdevLiveStreamer *m_Streamer;
+
+ virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
+
+ int GetPid(SI::PMT::Stream& stream);
+public:
+ cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel);
+ uchar* Get(int &Count) { return siBuffer.Get(Count); }
+ void Del(int Count) { return siBuffer.Del(Count); }
+};
+
+cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel): siBuffer(10 * TS_SIZE, TS_SIZE)
+{
+ Dprintf("cStreamdevPatFilter(\"%s\")\n", Channel->Name());
+ assert(Streamer);
+ m_Channel = Channel;
+ m_Streamer = Streamer;
+ pmtPid = 0;
+ pmtSid = 0;
+ pmtVersion = -1;
+ Set(0x00, 0x00); // PAT
+ // initialize PAT buffer. Only some values are dynamic (see comments)
+ memset(tspat_buf, 0xff, TS_SIZE);
+ tspat_buf[0] = TS_SYNC_BYTE; // Transport packet header sunchronization byte (1000011 = 0x47h)
+ tspat_buf[1] = 0x40; // Set payload unit start indicator bit
+ tspat_buf[2] = 0x0; // PID
+ tspat_buf[3] = 0x10; // Set payload flag, DYNAMIC: Continuity counter
+ tspat_buf[4] = 0x0; // SI pointer field
+ tspat_buf[5] = 0x0; // PAT table id
+ tspat_buf[6] = 0xb0; // Section syntax indicator bit and reserved bits set
+ tspat_buf[7] = 12 + 1; // Section length (12 bit): PAT_TABLE_LEN + 1
+ tspat_buf[8] = 0; // DYNAMIC: Transport stream ID (bits 8-15)
+ tspat_buf[9] = 0; // DYNAMIC: Transport stream ID (bits 0-7)
+ tspat_buf[10] = 0xc0; // Reserved, DYNAMIC: Version number, DYNAMIC: Current next indicator
+ tspat_buf[11] = 0x0; // Section number
+ tspat_buf[12] = 0x0; // Last section number
+ tspat_buf[13] = 0; // DYNAMIC: Program number (bits 8-15)
+ tspat_buf[14] = 0; // DYNAMIC: Program number (bits 0-7)
+ tspat_buf[15] = 0xe0; // Reserved, DYNAMIC: Network ID (bits 8-12)
+ tspat_buf[16] = 0; // DYNAMIC: Network ID (bits 0-7)
+ tspat_buf[17] = 0; // DYNAMIC: Checksum
+ tspat_buf[18] = 0; // DYNAMIC: Checksum
+ tspat_buf[19] = 0; // DYNAMIC: Checksum
+ tspat_buf[20] = 0; // DYNAMIC: Checksum
+}
+
+static const char * const psStreamTypes[] = {
+ "UNKNOWN",
+ "ISO/IEC 11172 Video",
+ "ISO/IEC 13818-2 Video",
+ "ISO/IEC 11172 Audio",
+ "ISO/IEC 13818-3 Audio",
+ "ISO/IEC 13818-1 Privete sections",
+ "ISO/IEC 13818-1 Private PES data",
+ "ISO/IEC 13512 MHEG",
+ "ISO/IEC 13818-1 Annex A DSM CC",
+ "0x09",
+ "ISO/IEC 13818-6 Multiprotocol encapsulation",
+ "ISO/IEC 13818-6 DSM-CC U-N Messages",
+ "ISO/IEC 13818-6 Stream Descriptors",
+ "ISO/IEC 13818-6 Sections (any type, including private data)",
+ "ISO/IEC 13818-1 auxiliary",
+ "ISO/IEC 13818-7 Audio with ADTS transport sytax",
+ "ISO/IEC 14496-2 Visual (MPEG-4)",
+ "ISO/IEC 14496-3 Audio with LATM transport syntax",
+ "0x12", "0x13", "0x14", "0x15", "0x16", "0x17", "0x18", "0x19", "0x1a",
+ "ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264)",
+ "",
+};
+
+int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
+{
+ SI::Descriptor *d;
+
+ if (!stream.getPid())
+ return 0;
+
+ switch (stream.getStreamType()) {
+ case 0x01: // ISO/IEC 11172 Video
+ case 0x02: // ISO/IEC 13818-2 Video
+ case 0x03: // ISO/IEC 11172 Audio
+ case 0x04: // ISO/IEC 13818-3 Audio
+#if 0
+ case 0x07: // ISO/IEC 13512 MHEG
+ case 0x08: // ISO/IEC 13818-1 Annex A DSM CC
+ case 0x0a: // ISO/IEC 13818-6 Multiprotocol encapsulation
+ case 0x0b: // ISO/IEC 13818-6 DSM-CC U-N Messages
+ case 0x0c: // ISO/IEC 13818-6 Stream Descriptors
+ case 0x0d: // ISO/IEC 13818-6 Sections (any type, including private data)
+ case 0x0e: // ISO/IEC 13818-1 auxiliary
+#endif
+ case 0x0f: // ISO/IEC 13818-7 Audio with ADTS transport syntax
+ case 0x10: // ISO/IEC 14496-2 Visual (MPEG-4)
+ case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax
+ case 0x1b: // ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264)
+ Dprintf("cStreamdevPatFilter PMT scanner adding PID %d (%s)\n",
+ stream.getPid(), psStreamTypes[stream.getStreamType()]);
+ return stream.getPid();
+ case 0x05: // ISO/IEC 13818-1 private sections
+ case 0x06: // ISO/IEC 13818-1 PES packets containing private data
+ for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
+ switch (d->getDescriptorTag()) {
+ case SI::AC3DescriptorTag:
+ Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n",
+ stream.getPid(), psStreamTypes[stream.getStreamType()], "AC3");
+ delete d;
+ return stream.getPid();
+ case SI::TeletextDescriptorTag:
+ Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n",
+ stream.getPid(), psStreamTypes[stream.getStreamType()], "Teletext");
+ delete d;
+ return stream.getPid();
+ case SI::SubtitlingDescriptorTag:
+ Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n",
+ stream.getPid(), psStreamTypes[stream.getStreamType()], "DVBSUB");
+ delete d;
+ return stream.getPid();
+ default:
+ Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s\n",
+ stream.getPid(), psStreamTypes[stream.getStreamType()], "UNKNOWN");
+ break;
+ }
+ delete d;
+ }
+ break;
+ default:
+ /* This following section handles all the cases where the audio track
+ * info is stored in PMT user info with stream id >= 0x80
+ * we check the registration format identifier to see if it
+ * holds "AC-3"
+ */
+ if (stream.getStreamType() >= 0x80) {
+ bool found = false;
+ for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
+ switch (d->getDescriptorTag()) {
+ case SI::RegistrationDescriptorTag:
+ /* unfortunately libsi does not implement RegistrationDescriptor */
+ if (d->getLength() >= 4) {
+ found = true;
+ SI::CharArray rawdata = d->getData();
+ if (/*rawdata[0] == 5 && rawdata[1] >= 4 && */
+ rawdata[2] == 'A' && rawdata[3] == 'C' &&
+ rawdata[4] == '-' && rawdata[5] == '3') {
+ isyslog("cStreamdevPatFilter PMT scanner:"
+ "Adding pid %d (type 0x%x) RegDesc len %d (%c%c%c%c)",
+ stream.getPid(), stream.getStreamType(),
+ d->getLength(), rawdata[2], rawdata[3],
+ rawdata[4], rawdata[5]);
+ delete d;
+ return stream.getPid();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ delete d;
+ }
+ if(!found) {
+ isyslog("Adding pid %d (type 0x%x) RegDesc not found -> assume AC-3",
+ stream.getPid(), stream.getStreamType());
+ return stream.getPid();
+ }
+ }
+ Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s\n",
+ stream.getPid(), psStreamTypes[stream.getStreamType()<0x1c?stream.getStreamType():0], "UNKNOWN");
+ break;
+ }
+ return 0;
+}
+
+void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
+{
+ if (Pid == 0x00) {
+ if (Tid == 0x00) {
+ SI::PAT pat(Data, false);
+ if (!pat.CheckCRCAndParse())
+ return;
+ SI::PAT::Association assoc;
+ for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
+ if (!assoc.isNITPid()) {
+ const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId());
+ if (Channel && (Channel == m_Channel)) {
+ int prevPmtPid = pmtPid;
+ if (0 != (pmtPid = assoc.getPid())) {
+ Dprintf("cStreamdevPatFilter: PMT pid for channel %s: %d\n", Channel->Name(), pmtPid);
+ pmtSid = assoc.getServiceId();
+ // repack PAT to TS frame and send to client
+ int ts_id;
+ unsigned int crc, i, len;
+ uint8_t *tmp;
+ static uint8_t ccounter = 0;
+ ccounter = (ccounter + 1) % 16;
+ ts_id = Channel->Tid(); // Get transport stream id of the channel
+ tspat_buf[3] = 0x10 | ccounter; // Set payload flag, Continuity counter
+ tspat_buf[8] = (ts_id >> 8); // Transport stream ID (bits 8-15)
+ tspat_buf[9] = (ts_id & 0xff); // Transport stream ID (bits 0-7)
+ tspat_buf[10] = 0xc0 | ((pat.getVersionNumber() << 1) & 0x3e) |
+ pat.getCurrentNextIndicator();// Version number, Current next indicator
+ tspat_buf[13] = (pmtSid >> 8); // Program number (bits 8-15)
+ tspat_buf[14] = (pmtSid & 0xff); // Program number (bits 0-7)
+ tspat_buf[15] = 0xe0 | (pmtPid >> 8); // Network ID (bits 8-12)
+ tspat_buf[16] = (pmtPid & 0xff); // Network ID (bits 0-7)
+ crc = 0xffffffff;
+ len = 12; // PAT_TABLE_LEN
+ tmp = &tspat_buf[4 + 1]; // TS_HDR_LEN + 1
+ while (len--) {
+ crc ^= *tmp++ << 24;
+ for (i = 0; i < 8; i++)
+ crc = (crc << 1) ^ ((crc & 0x80000000) ? 0x04c11db7 : 0); // CRC32POLY
+ }
+ tspat_buf[17] = crc >> 24 & 0xff; // Checksum
+ tspat_buf[18] = crc >> 16 & 0xff; // Checksum
+ tspat_buf[19] = crc >> 8 & 0xff; // Checksum
+ tspat_buf[20] = crc & 0xff; // Checksum
+ int written = siBuffer.PutTS(tspat_buf, TS_SIZE);
+ if (written != TS_SIZE)
+ siBuffer.ReportOverflow(TS_SIZE - written);
+ if (pmtPid != prevPmtPid) {
+ m_Streamer->SetPids(pmtPid);
+ Add(pmtPid, 0x02);
+ pmtVersion = -1;
+ }
+ return;
+ }
+ }
+ }
+ }
+ }
+ } else if (Pid == pmtPid && Tid == SI::TableIdPMT && Source() && Transponder()) {
+ SI::PMT pmt(Data, false);
+ if (!pmt.CheckCRCAndParse())
+ return;
+ if (pmt.getServiceId() != pmtSid)
+ return; // skip broken PMT records
+ if (pmtVersion != -1) {
+ if (pmtVersion != pmt.getVersionNumber()) {
+ Dprintf("cStreamdevPatFilter: PMT version changed, detaching all pids\n");
+ cFilter::Del(pmtPid, 0x02);
+ pmtPid = 0; // this triggers PAT scan
+ }
+ return;
+ }
+ pmtVersion = pmt.getVersionNumber();
+
+ SI::PMT::Stream stream;
+ int pids[MAXRECEIVEPIDS + 1], npids = 0;
+ pids[npids++] = pmtPid;
+#if 0
+ pids[npids++] = 0x10; // pid 0x10, tid 0x40: NIT
+ pids[npids++] = 0x11; // pid 0x11, tid 0x42: SDT
+ pids[npids++] = 0x14; // pid 0x14, tid 0x70: TDT
+#endif
+ pids[npids++] = 0x12; // pid 0x12, tid 0x4E...0x6F: EIT
+ for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); )
+ if (0 != (pids[npids] = GetPid(stream)) && npids < MAXRECEIVEPIDS)
+ npids++;
+
+ pids[npids] = 0;
+ m_Streamer->SetPids(pmt.getPCRPid(), pids);
+ }
+}
+
+// --- cStreamdevLiveStreamer -------------------------------------------------
+
+cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, std::string Parameter):
+ cStreamdevStreamer("streamdev-livestreaming"),
+ m_Priority(Priority),
+ m_Parameter(Parameter),
+ m_NumPids(0),
+ m_StreamType(stTSPIDS),
+ m_Channel(NULL),
+ m_Device(NULL),
+ m_Receiver(NULL),
+ m_PatFilter(NULL),
+ m_Remux(NULL)
+{
+}
+
+cStreamdevLiveStreamer::~cStreamdevLiveStreamer()
+{
+ Dprintf("Desctructing Live streamer\n");
+ Stop();
+ if(m_PatFilter) {
+ Detach();
+ DELETENULL(m_PatFilter);
+ }
+ DELETENULL(m_Receiver);
+ delete m_Remux;
+}
+
+bool cStreamdevLiveStreamer::HasPid(int Pid)
+{
+ int idx;
+ for (idx = 0; idx < m_NumPids; ++idx)
+ if (m_Pids[idx] == Pid)
+ return true;
+ return false;
+}
+
+bool cStreamdevLiveStreamer::SetPid(int Pid, bool On)
+{
+ int idx;
+
+ if (Pid == 0)
+ return true;
+
+ if (On) {
+ for (idx = 0; idx < m_NumPids; ++idx) {
+ if (m_Pids[idx] == Pid)
+ return true; // No change needed
+ }
+
+ if (m_NumPids == MAXRECEIVEPIDS) {
+ esyslog("ERROR: Streamdev: No free slot to receive pid %d\n", Pid);
+ return false;
+ }
+
+ m_Pids[m_NumPids++] = Pid;
+ m_Pids[m_NumPids] = 0;
+ } else {
+ for (idx = 0; idx < m_NumPids; ++idx) {
+ if (m_Pids[idx] == Pid) {
+ --m_NumPids;
+ memmove(&m_Pids[idx], &m_Pids[idx + 1], sizeof(int) * (m_NumPids - idx));
+ }
+ }
+ }
+
+ StartReceiver();
+ return true;
+}
+
+bool cStreamdevLiveStreamer::SetPids(int Pid, const int *Pids1, const int *Pids2, const int *Pids3)
+{
+ m_NumPids = 0;
+
+ if (Pid)
+ m_Pids[m_NumPids++] = Pid;
+
+ if (Pids1)
+ for ( ; *Pids1 && m_NumPids < MAXRECEIVEPIDS; Pids1++)
+ if (!HasPid(*Pids1))
+ m_Pids[m_NumPids++] = *Pids1;
+
+ if (Pids2)
+ for ( ; *Pids2 && m_NumPids < MAXRECEIVEPIDS; Pids2++)
+ if (!HasPid(*Pids2))
+ m_Pids[m_NumPids++] = *Pids2;
+
+ if (Pids3)
+ for ( ; *Pids3 && m_NumPids < MAXRECEIVEPIDS; Pids3++)
+ if (!HasPid(*Pids3))
+ m_Pids[m_NumPids++] = *Pids3;
+
+ if (m_NumPids >= MAXRECEIVEPIDS) {
+ esyslog("ERROR: Streamdev: No free slot to receive pid %d\n", Pid);
+ return false;
+ }
+
+ m_Pids[m_NumPids] = 0;
+ StartReceiver();
+ return true;
+}
+
+void cStreamdevLiveStreamer::StartReceiver(void)
+{
+ DELETENULL(m_Receiver);
+ if (m_NumPids > 0) {
+ Dprintf("Creating Receiver to respect changed pids\n");
+ m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->GetChannelID(), m_Priority, m_Pids);
+ if (IsRunning() && m_Device != NULL) {
+ Dprintf("Attaching new receiver\n");
+ Attach();
+ }
+ }
+}
+
+bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, int Apid)
+{
+ Dprintf("Initializing Remuxer for full channel transfer\n");
+ //printf("ca pid: %d\n", Channel->Ca());
+ m_Channel = Channel;
+ m_StreamType = StreamType;
+
+ int apid[2] = { Apid, 0 };
+ const int *Apids = Apid ? apid : m_Channel->Apids();
+ const int *Dpids = Apid ? NULL : m_Channel->Dpids();
+
+ switch (m_StreamType) {
+ case stES:
+ {
+ int pid = ISRADIO(m_Channel) ? m_Channel->Apid(0) : m_Channel->Vpid();
+ if (Apid != 0)
+ pid = Apid;
+ m_Remux = new cTS2ESRemux(pid);
+ return SetPids(pid);
+ }
+
+ case stPES:
+ m_Remux = new cTS2PESRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
+ m_Channel->Spids());
+ return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
+
+ case stPS:
+ m_Remux = new cTS2PSRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
+ m_Channel->Spids());
+ return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
+
+ case stExtern:
+ m_Remux = new cExternRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
+ m_Channel->Spids(), m_Parameter);
+ // fall through
+ case stTS:
+ // This should never happen, but ...
+ if (m_PatFilter) {
+ Detach();
+ DELETENULL(m_PatFilter);
+ }
+ // Set pids from cChannel
+ SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
+ if (m_Channel->Vpid() != m_Channel->Ppid())
+ SetPid(m_Channel->Ppid(), true);
+ // Set pids from PMT
+ m_PatFilter = new cStreamdevPatFilter(this, m_Channel);
+ return true;
+
+ case stTSPIDS:
+ Dprintf("pid streaming mode\n");
+ return true;
+ default:
+ return false;
+ }
+}
+
+int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
+{
+ // insert si data
+ if (m_PatFilter) {
+ int siCount;
+ uchar *siData = m_PatFilter->Get(siCount);
+ if (siData) {
+ if (m_Remux)
+ siCount = m_Remux->Put(siData, siCount);
+ else
+ siCount = cStreamdevStreamer::Put(siData, siCount);
+ if (siCount)
+ m_PatFilter->Del(siCount);
+ }
+ }
+ if (m_Remux)
+ return m_Remux->Put(Data, Count);
+ else
+ return cStreamdevStreamer::Put(Data, Count);
+}
+
+uchar *cStreamdevLiveStreamer::Get(int &Count)
+{
+ if (m_Remux)
+ return m_Remux->Get(Count);
+ else
+ return cStreamdevStreamer::Get(Count);
+}
+
+void cStreamdevLiveStreamer::Del(int Count)
+{
+ if (m_Remux)
+ m_Remux->Del(Count);
+ else
+ cStreamdevStreamer::Del(Count);
+}
+
+void cStreamdevLiveStreamer::Attach(void)
+{
+ Dprintf("cStreamdevLiveStreamer::Attach()\n");
+ if (m_Device) {
+ if (m_Receiver) {
+ m_Device->Detach(m_Receiver);
+ m_Device->AttachReceiver(m_Receiver);
+ }
+ if (m_PatFilter) {
+ m_Device->Detach(m_PatFilter);
+ m_Device->AttachFilter(m_PatFilter);
+ }
+ }
+}
+
+void cStreamdevLiveStreamer::Detach(void)
+{
+ Dprintf("cStreamdevLiveStreamer::Detach()\n");
+ if (m_Device) {
+ if (m_Receiver)
+ m_Device->Detach(m_Receiver);
+ if (m_PatFilter)
+ m_Device->Detach(m_PatFilter);
+ }
+}
+
+std::string cStreamdevLiveStreamer::Report(void)
+{
+ std::string result;
+
+ if (m_Device != NULL)
+ result += (std::string)"+- Device is " + (const char*)itoa(m_Device->CardIndex()) + "\n";
+ if (m_Receiver != NULL)
+ result += "+- Receiver is allocated\n";
+
+ result += "+- Pids are ";
+ for (int i = 0; i < MAXRECEIVEPIDS; ++i)
+ if (m_Pids[i] != 0)
+ result += (std::string)(const char*)itoa(m_Pids[i]) + ", ";
+ result += "\n";
+ return result;
+}
+
+// --- cStreamdevFilterStreamer -------------------------------------------------
+
+cStreamdevFilterStreamer::cStreamdevFilterStreamer():
+ cStreamdevStreamer("streamdev-filterstreaming"),
+ m_Device(NULL),
+ m_Filter(NULL)/*,
+ m_Channel(NULL)*/
+{
+}
+
+cStreamdevFilterStreamer::~cStreamdevFilterStreamer()
+{
+ Dprintf("Desctructing Filter streamer\n");
+ Detach();
+ m_Device = NULL;
+ DELETENULL(m_Filter);
+ Stop();
+}
+
+void cStreamdevFilterStreamer::Attach(void)
+{
+ Dprintf("cStreamdevFilterStreamer::Attach()\n");
+ LOCK_THREAD;
+ if(m_Device && m_Filter)
+ m_Device->AttachFilter(m_Filter);
+}
+
+void cStreamdevFilterStreamer::Detach(void)
+{
+ Dprintf("cStreamdevFilterStreamer::Detach()\n");
+ LOCK_THREAD;
+ if(m_Device && m_Filter)
+ m_Device->Detach(m_Filter);
+}
+
+#if 0
+void cStreamdevFilterStreamer::SetChannel(const cChannel *Channel)
+{
+ LOCK_THREAD;
+ Dprintf("cStreamdevFilterStreamer::SetChannel(%s : %s)", Channel?Channel->Name():"<null>",
+ Channel ? *Channel->GetChannelID().ToString() : "");
+ m_Channel = Channel;
+}
+#endif
+
+void cStreamdevFilterStreamer::SetDevice(cDevice *Device)
+{
+ Dprintf("cStreamdevFilterStreamer::SetDevice()\n");
+ LOCK_THREAD;
+ if(Device != m_Device) {
+ Detach();
+ m_Device = Device;
+ //m_Channel = NULL;
+ Attach();
+ }
+}
+
+bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On)
+{
+ Dprintf("cStreamdevFilterStreamer::SetFilter(%u,0x%x,0x%x,%s)\n", Pid, Tid, Mask, On?"On":"Off");
+
+ if(!m_Device)
+ return false;
+
+ if (On) {
+ if (m_Filter == NULL) {
+ m_Filter = new cStreamdevLiveFilter(this);
+ Dprintf("attaching filter to device\n");
+ Attach();
+ }
+ m_Filter->Set(Pid, Tid, Mask);
+ } else if (m_Filter != NULL)
+ m_Filter->Del(Pid, Tid, Mask);
+
+ return true;
+}
+
+#if 0
+void cStreamdevFilterStreamer::ChannelSwitch(const cDevice *Device, int ChannelNumber) {
+ LOCK_THREAD;
+ if(Device == m_Device) {
+ if(ChannelNumber > 0) {
+ cChannel *ch = Channels.GetByNumber(ChannelNumber);
+ if(ch != NULL) {
+ if(m_Filter != NULL &&
+ m_Channel != NULL &&
+ (! TRANSPONDER(ch, m_Channel))) {
+
+ isyslog("***** LiveFilterStreamer: transponder changed ! %s",
+ *ch->GetChannelID().ToString());
+
+ uchar buffer[TS_SIZE] = {TS_SYNC_BYTE, 0xff, 0xff, 0xff, 0x7f, 0};
+ strcpy((char*)(buffer + 5), ch->GetChannelID().ToString());
+ int p = Put(buffer, TS_SIZE);
+ if (p != TS_SIZE)
+ ReportOverflow(TS_SIZE - p);
+ }
+ m_Channel = ch;
+ }
+ }
+ }
+}
+#endif
diff --git a/plugins/streamdev/streamdev-cvs/server/livestreamer.h b/plugins/streamdev/streamdev-cvs/server/livestreamer.h
new file mode 100644
index 0000000..92448bb
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/livestreamer.h
@@ -0,0 +1,82 @@
+#ifndef VDR_STREAMDEV_LIVESTREAMER_H
+#define VDR_STREAMDEV_LIVESTREAMER_H
+
+#include <vdr/config.h>
+#include <vdr/receiver.h>
+
+#include "server/streamer.h"
+#include "common.h"
+
+namespace Streamdev {
+ class cTSRemux;
+}
+class cStreamdevPatFilter;
+class cStreamdevLiveReceiver;
+
+// --- cStreamdevLiveStreamer -------------------------------------------------
+
+class cStreamdevLiveStreamer: public cStreamdevStreamer {
+private:
+ int m_Priority;
+ std::string m_Parameter;
+ int m_Pids[MAXRECEIVEPIDS + 1];
+ int m_NumPids;
+ eStreamType m_StreamType;
+ const cChannel *m_Channel;
+ cDevice *m_Device;
+ cStreamdevLiveReceiver *m_Receiver;
+ cStreamdevPatFilter *m_PatFilter;
+ Streamdev::cTSRemux *m_Remux;
+
+ void StartReceiver(void);
+ bool HasPid(int Pid);
+
+public:
+ cStreamdevLiveStreamer(int Priority, std::string Parameter = "");
+ virtual ~cStreamdevLiveStreamer();
+
+ void SetDevice(cDevice *Device) { m_Device = Device; }
+ bool SetPid(int Pid, bool On);
+ bool SetPids(int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
+ bool SetChannel(const cChannel *Channel, eStreamType StreamType, int Apid = 0);
+
+ virtual int Put(const uchar *Data, int Count);
+ virtual uchar *Get(int &Count);
+ virtual void Del(int Count);
+
+ virtual void Attach(void);
+ virtual void Detach(void);
+
+ // Statistical purposes:
+ virtual std::string Report(void);
+};
+
+
+// --- cStreamdevFilterStreamer -------------------------------------------------
+
+//#include <vdr/status.h>
+
+class cStreamdevLiveFilter;
+
+class cStreamdevFilterStreamer: public cStreamdevStreamer /*, public cStatus*/ {
+private:
+ cDevice *m_Device;
+ cStreamdevLiveFilter *m_Filter;
+ //const cChannel *m_Channel;
+
+public:
+ cStreamdevFilterStreamer();
+ virtual ~cStreamdevFilterStreamer();
+
+ void SetDevice(cDevice *Device);
+ //void SetChannel(const cChannel *Channel);
+ bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On);
+
+ virtual void Attach(void);
+ virtual void Detach(void);
+
+ // cStatus message handlers
+ //virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
+};
+
+#endif // VDR_STREAMDEV_LIVESTREAMER_H
diff --git a/plugins/streamdev/streamdev-cvs/server/menuHTTP.c b/plugins/streamdev/streamdev-cvs/server/menuHTTP.c
new file mode 100644
index 0000000..84a4fb7
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/menuHTTP.c
@@ -0,0 +1,429 @@
+#include <vdr/channels.h>
+#include "server/menuHTTP.h"
+
+//**************************** cChannelIterator **************
+cChannelIterator::cChannelIterator(cChannel *First): channel(First)
+{}
+
+const cChannel* cChannelIterator::Next()
+{
+ const cChannel *current = channel;
+ channel = NextChannel(channel);
+ return current;
+}
+
+//**************************** cListAll **************
+cListAll::cListAll(): cChannelIterator(Channels.First())
+{}
+
+const cChannel* cListAll::NextChannel(const cChannel *Channel)
+{
+ if (Channel)
+ Channel = Channels.Next(Channel);
+ return Channel;
+}
+
+//**************************** cListChannels **************
+cListChannels::cListChannels(): cChannelIterator(Channels.Get(Channels.GetNextNormal(-1)))
+{}
+
+const cChannel* cListChannels::NextChannel(const cChannel *Channel)
+{
+ if (Channel)
+ Channel = Channels.Get(Channels.GetNextNormal(Channel->Index()));
+ return Channel;
+}
+
+// ********************* cListGroups ****************
+cListGroups::cListGroups(): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1)))
+{}
+
+const cChannel* cListGroups::NextChannel(const cChannel *Channel)
+{
+ if (Channel)
+ Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
+ return Channel;
+}
+//
+// ********************* cListGroup ****************
+cListGroup::cListGroup(const cChannel *Group): cChannelIterator((Group && Group->GroupSep() && Channels.Next(Group) && !Channels.Next(Group)->GroupSep()) ? Channels.Next(Group) : NULL)
+{}
+
+const cChannel* cListGroup::NextChannel(const cChannel *Channel)
+{
+ if (Channel)
+ Channel = Channels.Next(Channel);
+ return (Channel && !Channel->GroupSep()) ? Channel : NULL;
+}
+//
+// ********************* cListTree ****************
+cListTree::cListTree(const cChannel *SelectedGroup): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1)))
+{
+ selectedGroup = SelectedGroup;
+ currentGroup = Channels.Get(Channels.GetNextGroup(-1));
+}
+
+const cChannel* cListTree::NextChannel(const cChannel *Channel)
+{
+ if (currentGroup == selectedGroup)
+ {
+ if (Channel)
+ Channel = Channels.Next(Channel);
+ if (Channel && Channel->GroupSep())
+ currentGroup = Channel;
+ }
+ else
+ {
+ if (Channel)
+ Channel = Channels.Get(Channels.GetNextGroup(Channel->Index()));
+ currentGroup = Channel;
+ }
+ return Channel;
+}
+
+// ******************** cChannelList ******************
+cChannelList::cChannelList(cChannelIterator *Iterator) : iterator(Iterator)
+{}
+
+cChannelList::~cChannelList()
+{
+ delete iterator;
+}
+
+int cChannelList::GetGroupIndex(const cChannel *Group)
+{
+ int index = 0;
+ for (int curr = Channels.GetNextGroup(-1); curr >= 0; curr = Channels.GetNextGroup(curr))
+ {
+ if (Channels.Get(curr) == Group)
+ return index;
+ index++;
+ }
+ return -1;
+}
+
+const cChannel* cChannelList::GetGroup(int Index)
+{
+ int group = Channels.GetNextGroup(-1);
+ while (Index-- && group >= 0)
+ group = Channels.GetNextGroup(group);
+ return group >= 0 ? Channels.Get(group) : NULL;
+}
+
+// ******************** cHtmlChannelList ******************
+const char* cHtmlChannelList::menu =
+ "[<a href=\"/\">Home</a> (<a href=\"all.html\" tvid=\"RED\">no script</a>)] "
+ "[<a href=\"tree.html\" tvid=\"GREEN\">Tree View</a>] "
+ "[<a href=\"groups.html\" tvid=\"YELLOW\">Groups</a> (<a href=\"groups.m3u\">Playlist</a>)] "
+ "[<a href=\"channels.html\" tvid=\"BLUE\">Channels</a> (<a href=\"channels.m3u\">Playlist</a>)] ";
+
+const char* cHtmlChannelList::css =
+ "<style type=\"text/css\">\n"
+ "<!--\n"
+ "a:link, a:visited, a:hover, a:active, a:focus { color:#333399; }\n"
+ "body { font:100% Verdana, Arial, Helvetica, sans-serif; background-color:#9999FF; margin:1em; }\n"
+ ".menu { position:fixed; top:0px; left:1em; right:1em; height:3em; text-align:center; background-color:white; border:inset 2px #9999ff; }\n"
+ "h2 { font-size:150%; margin:0em; padding:0em 1.5em; }\n"
+ ".contents { margin-top:5em; background-color:white; }\n"
+ ".group { background:url(data:image/gif;base64,R0lGODdhAQAeAIQeAJub/5yc/6Cf/6Oj/6am/6qq/66u/7Gx/7S0/7i4/7u8/7+//8LD/8bG/8nK/83N/9HQ/9TU/9fX/9va/97e/+Lh/+Xl/+no/+3t//Dw//Pz//b3//v7//7+/////////ywAAAAAAQAeAAAFGCAQCANRGAeSKAvTOA8USRNVWReWaRvXhQA7) repeat-x; border:inset 2px #9999ff; }\n"
+ ".items { border-top:dashed 1px; margin-top:0px; margin-bottom:0px; padding:0.7em 5em; }\n"
+ ".apid { padding-left:28px; margin:0.5em; background:url(data:image/gif;base64,R0lGODlhGwASAKEBAAAAAP///////////yH5BAEKAAEALAAAAAAbABIAAAJAjI+pywj5WgPxVAmpNRcHqnGb94FPhE7m+okts7JvusSmSys2iLv6TstldjMfhhUkcXi+zjFUVFo6TiVVij0UAAA7) no-repeat; }\n"
+ ".dpid { padding-left:28px; margin:0.5em; background:url(data:image/gif;base64,R0lGODlhGwASAKEBAAAAAP///////////yH5BAEKAAEALAAAAAAbABIAAAJFjI+py+0BopwAUoqivRvr83UaZ4RWMnVoBbLZaJbuqcCLGcv0+t5Vvgu2hLrh6pfDzVSpnlGEbAZhnIutZaVmH9yuV1EAADs=) no-repeat; }\n"
+ "button { width:2em; margin:0.2em 0.5em; vertical-align:top; }\n"
+ "-->\n"
+ "</style>";
+
+const char* cHtmlChannelList::js =
+ "<script language=\"JavaScript\">\n"
+ "<!--\n"
+
+ "function eventTarget(evt) {\n"
+ " if (!evt) evt = window.event;\n"
+ " if (evt.target) return evt.target;\n"
+ " else if (evt.srcElement) return evt.srcElement;\n"
+ " else return null;\n"
+ "}\n"
+
+ // toggle visibility of a group
+ "function clickHandler(evt) {\n"
+ " var button = eventTarget(evt);\n"
+ " if (button) {\n"
+ " var group = document.getElementById('c' + button.id);\n"
+ " if (group) {\n"
+ " button.removeChild(button.firstChild);\n"
+ " if (group.style.display == 'block') {\n"
+ " button.appendChild(document.createTextNode(\"+\"));\n"
+ " group.style.display = 'none';\n"
+ " } else {\n"
+ " button.appendChild(document.createTextNode(\"-\"));\n"
+ " group.style.display = 'block';\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n"
+
+ // insert a click button infront of each h2 and an id to the corresponding list
+ "function init() {\n"
+ " var titles = document.getElementsByTagName('h2');\n"
+ " for (var i = 0; i < titles.length; i++) {\n"
+ " var button = document.createElement('button');\n"
+ " button.id = 'g' + i;\n"
+ " button.onclick = clickHandler;\n"
+ " button.appendChild(document.createTextNode('+'));\n"
+ " titles[i].insertBefore(button, titles[i].firstChild);\n"
+ " var group = titles[i].nextSibling;\n"
+ " while (group) {\n"
+ " if (group.className && group.className == 'items') {\n"
+ " group.id = 'cg' + i;\n"
+ " break;\n"
+ " }\n"
+ " group = group.nextSibling;\n"
+ " }\n"
+ " }\n"
+ "}\n"
+
+ "window.onload = init;\n"
+
+ // hide lists before the browser renders it
+ "if (document.styleSheets[0].insertRule)\n"
+ " document.styleSheets[0].insertRule('.items { display:none }', 0);\n"
+ "else if (document.styleSheets[0].addRule)\n"
+ " document.styleSheets[0].addRule('.items', 'display:none');\n"
+
+ "//-->\n"
+ "</script>";
+
+
+std::string cHtmlChannelList::StreamTypeMenu()
+{
+ std::string typeMenu;
+ typeMenu += (streamType == stTS ? (std::string) "[TS] " :
+ (std::string) "[<a href=\"/TS/" + self + "\">TS</a>] ");
+ typeMenu += (streamType == stPS ? (std::string) "[PS] " :
+ (std::string) "[<a href=\"/PS/" + self + "\">PS</a>] ");
+ typeMenu += (streamType == stPES ? (std::string) "[PES] " :
+ (std::string) "[<a href=\"/PES/" + self + "\">PES</a>] ");
+ typeMenu += (streamType == stES ? (std::string) "[ES] " :
+ (std::string) "[<a href=\"/ES/" + self + "\">ES</a>] ");
+ typeMenu += (streamType == stExtern ? (std::string) "[Extern] " :
+ (std::string) "[<a href=\"/Extern/" + self + "\">Extern</a>] ");
+ return typeMenu;
+}
+
+cHtmlChannelList::cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget): cChannelList(Iterator)
+{
+ streamType = StreamType;
+ self = strdup(Self);
+ groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL;
+ htmlState = hsRoot;
+ current = NULL;
+}
+
+cHtmlChannelList::~cHtmlChannelList()
+{
+ free((void *) self);
+ free((void *) groupTarget);
+}
+
+bool cHtmlChannelList::HasNext()
+{
+ return htmlState != hsPageBottom;
+}
+
+std::string cHtmlChannelList::Next()
+{
+ switch (htmlState)
+ {
+ case hsRoot:
+ htmlState = hsHtmlHead;
+ break;
+ case hsHtmlHead:
+ htmlState = hsCss;
+ break;
+ case hsCss:
+ htmlState = *self ? hsPageTop : hsJs;
+ break;
+ case hsJs:
+ htmlState = hsPageTop;
+ break;
+ case hsPageTop:
+ current = NextChannel();
+ htmlState = current ? (current->GroupSep() ? hsGroupTop : hsPlainTop) : hsPageBottom;
+ break;
+ case hsPlainTop:
+ htmlState = hsPlainItem;
+ break;
+ case hsPlainItem:
+ current = NextChannel();
+ htmlState = current && !current->GroupSep() ? hsPlainItem : hsPlainBottom;
+ break;
+ case hsPlainBottom:
+ htmlState = current ? hsGroupTop : hsPageBottom;
+ break;
+ case hsGroupTop:
+ current = NextChannel();
+ htmlState = current && !current->GroupSep() ? hsItemsTop : hsGroupBottom;
+ break;
+ case hsItemsTop:
+ htmlState = hsItem;
+ break;
+ case hsItem:
+ current = NextChannel();
+ htmlState = current && !current->GroupSep() ? hsItem : hsItemsBottom;
+ break;
+ case hsItemsBottom:
+ htmlState = hsGroupBottom;
+ break;
+ case hsGroupBottom:
+ htmlState = current ? hsGroupTop : hsPageBottom;
+ break;
+ case hsPageBottom:
+ default:
+ esyslog("streamdev-server cHtmlChannelList: invalid call to Next()");
+ break;
+ }
+ switch (htmlState)
+ {
+ // NOTE: JavaScript requirements:
+ // Group title is identified by <h2> tag
+ // Channel list must be a sibling of <h2> with class "items"
+ case hsHtmlHead: return "<html><head>" + HtmlHead();
+ case hsCss: return css;
+ case hsJs: return js;
+ case hsPageTop: return "</head><body>" + PageTop() + "<div class=\"contents\">";
+ case hsGroupTop: return "<div class=\"group\"><h2>" + GroupTitle() + "</h2>";
+ case hsItemsTop:
+ case hsPlainTop: return "<ol class=\"items\">";
+ case hsItem:
+ case hsPlainItem: return ItemText();
+ case hsItemsBottom:
+ case hsPlainBottom: return "</ol>";
+ case hsGroupBottom: return "</div>";
+ case hsPageBottom: return "</div>" + PageBottom() + "</body></html>";
+ default: return "";
+ }
+}
+
+std::string cHtmlChannelList::HtmlHead()
+{
+ return (std::string) "";
+}
+
+std::string cHtmlChannelList::PageTop()
+{
+ return (std::string) "<div class=\"menu\"><div>" + menu + "</div><div>" + StreamTypeMenu() + "</div></div>";
+}
+
+std::string cHtmlChannelList::PageBottom()
+{
+ return (std::string) "";
+}
+
+std::string cHtmlChannelList::GroupTitle()
+{
+ if (groupTarget)
+ {
+ return (std::string) "<a href=\"" + groupTarget + "?group=" +
+ (const char*) itoa(cChannelList::GetGroupIndex(current)) +
+ "\">" + current->Name() + "</a>";
+ }
+ else
+ {
+ return (std::string) current->Name();
+ }
+}
+
+std::string cHtmlChannelList::ItemText()
+{
+ std::string line;
+ std::string suffix;
+
+ switch (streamType) {
+ case stTS: suffix = (std::string) ".ts"; break;
+ case stPS: suffix = (std::string) ".vob"; break;
+ // for Network Media Tank
+ case stPES: suffix = (std::string) ".vdr"; break;
+ default: suffix = "";
+ }
+ line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">";
+ line += (std::string) "<a href=\"" + (std::string) current->GetChannelID().ToString() + suffix + "\"";
+
+ // for Network Media Tank
+ line += (std::string) " vod ";
+ if (current->Number() < 1000)
+ line += (std::string) " tvid=\"" + (const char*) itoa(current->Number()) + "\"";
+
+ line += (std::string) ">" + current->Name() + "</a>";
+
+ int count = 0;
+ for (int i = 0; current->Apid(i) != 0; ++i, ++count)
+ ;
+ for (int i = 0; current->Dpid(i) != 0; ++i, ++count)
+ ;
+
+ if (count > 1)
+ {
+ int index = 1;
+ for (int i = 0; current->Apid(i) != 0; ++i, ++index) {
+ line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
+ "+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + current->Alang(i) + "</a>";
+ }
+ for (int i = 0; current->Dpid(i) != 0; ++i, ++index) {
+ line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() +
+ "+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + current->Dlang(i) + "</a>";
+ }
+ }
+ line += "</li>";
+ return line;
+}
+
+// ******************** cM3uChannelList ******************
+cM3uChannelList::cM3uChannelList(cChannelIterator *Iterator, const char* Base)
+: cChannelList(Iterator),
+ m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
+{
+ base = strdup(Base);
+ m3uState = msFirst;
+}
+
+cM3uChannelList::~cM3uChannelList()
+{
+ free(base);
+}
+
+bool cM3uChannelList::HasNext()
+{
+ return m3uState != msLast;
+}
+
+std::string cM3uChannelList::Next()
+{
+ if (m3uState == msFirst)
+ {
+ m3uState = msContinue;
+ return "#EXTM3U";
+ }
+
+ const cChannel *channel = NextChannel();
+ if (!channel)
+ {
+ m3uState = msLast;
+ return "";
+ }
+
+ std::string name = (std::string) m_IConv.Convert(channel->Name());
+
+ if (channel->GroupSep())
+ {
+ return (std::string) "#EXTINF:-1," + name + "\r\n" +
+ base + "group.m3u?group=" +
+ (const char*) itoa(cChannelList::GetGroupIndex(channel));
+ }
+ else
+ {
+ return (std::string) "#EXTINF:-1," +
+ (const char*) itoa(channel->Number()) + " " + name + "\r\n" +
+ base + (std::string) channel->GetChannelID().ToString();
+ }
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/server/menuHTTP.h b/plugins/streamdev/streamdev-cvs/server/menuHTTP.h
new file mode 100644
index 0000000..cbd7b59
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/menuHTTP.h
@@ -0,0 +1,143 @@
+#ifndef VDR_STREAMDEV_SERVERS_MENUHTTP_H
+#define VDR_STREAMDEV_SERVERS_MENUHTTP_H
+
+#include <string>
+#include "../common.h"
+
+class cChannel;
+
+// ******************** cChannelIterator ******************
+class cChannelIterator
+{
+ private:
+ const cChannel *channel;
+ protected:
+ virtual const cChannel* NextChannel(const cChannel *Channel) = 0;
+ public:
+ const cChannel* Next();
+ cChannelIterator(cChannel *First);
+ virtual ~cChannelIterator() {};
+};
+
+class cListAll: public cChannelIterator
+{
+ protected:
+ virtual const cChannel* NextChannel(const cChannel *Channel);
+ public:
+ cListAll();
+ virtual ~cListAll() {};
+};
+
+class cListChannels: public cChannelIterator
+{
+ protected:
+ virtual const cChannel* NextChannel(const cChannel *Channel);
+ public:
+ cListChannels();
+ virtual ~cListChannels() {};
+};
+
+class cListGroups: public cChannelIterator
+{
+ protected:
+ virtual const cChannel* NextChannel(const cChannel *Channel);
+ public:
+ cListGroups();
+ virtual ~cListGroups() {};
+};
+
+class cListGroup: public cChannelIterator
+{
+ protected:
+ virtual const cChannel* NextChannel(const cChannel *Channel);
+ public:
+ cListGroup(const cChannel *Group);
+ virtual ~cListGroup() {};
+};
+
+class cListTree: public cChannelIterator
+{
+ private:
+ const cChannel* selectedGroup;
+ const cChannel* currentGroup;
+ protected:
+ virtual const cChannel* NextChannel(const cChannel *Channel);
+ public:
+ cListTree(const cChannel *SelectedGroup);
+ virtual ~cListTree() {};
+};
+
+// ******************** cChannelList ******************
+class cChannelList
+{
+ private:
+ cChannelIterator *iterator;
+ protected:
+ const cChannel* NextChannel() { return iterator->Next(); }
+ public:
+ // Helper which returns the group index
+ static int GetGroupIndex(const cChannel* Group);
+ // Helper which returns the group by its index
+ static const cChannel* GetGroup(int Index);
+
+ virtual std::string HttpHeader() { return "HTTP/1.0 200 OK\r\n"; };
+ virtual bool HasNext() = 0;
+ virtual std::string Next() = 0;
+ cChannelList(cChannelIterator *Iterator);
+ virtual ~cChannelList();
+};
+
+class cHtmlChannelList: public cChannelList
+{
+ private:
+ static const char* menu;
+ static const char* css;
+ static const char* js;
+
+ enum eHtmlState {
+ hsRoot, hsHtmlHead, hsCss, hsJs, hsPageTop, hsPageBottom,
+ hsGroupTop, hsGroupBottom,
+ hsPlainTop, hsPlainItem, hsPlainBottom,
+ hsItemsTop, hsItem, hsItemsBottom
+ };
+ eHtmlState htmlState;
+ const cChannel *current;
+ eStreamType streamType;
+ const char* self;
+ const char* groupTarget;
+
+ std::string StreamTypeMenu();
+ std::string HtmlHead();
+ std::string PageTop();
+ std::string GroupTitle();
+ std::string ItemText();
+ std::string PageBottom();
+ public:
+ virtual std::string HttpHeader() {
+ return cChannelList::HttpHeader()
+ + "Content-type: text/html; charset="
+ + (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8")
+ + "\r\n";
+ }
+ virtual bool HasNext();
+ virtual std::string Next();
+ cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget);
+ virtual ~cHtmlChannelList();
+};
+
+class cM3uChannelList: public cChannelList
+{
+ private:
+ char *base;
+ enum eM3uState { msFirst, msContinue, msLast };
+ eM3uState m3uState;
+ cCharSetConv m_IConv;
+ public:
+ virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: audio/x-mpegurl; charset=UTF-8\r\n"; };
+ virtual bool HasNext();
+ virtual std::string Next();
+ cM3uChannelList(cChannelIterator *Iterator, const char* Base);
+ virtual ~cM3uChannelList();
+};
+
+#endif
diff --git a/plugins/streamdev/streamdev-cvs/server/recplayer.c b/plugins/streamdev/streamdev-cvs/server/recplayer.c
new file mode 100644
index 0000000..f45d8c3
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/recplayer.c
@@ -0,0 +1,288 @@
+/*
+ Copyright 2004-2005 Chris Tallon
+
+ This file is part of VOMP.
+ and adopted for streamdev to play recordings
+
+ VOMP 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 of the License, or
+ (at your option) any later version.
+
+ VOMP 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 VOMP; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include "recplayer.h"
+
+#define _XOPEN_SOURCE 600
+#include <fcntl.h>
+
+RecPlayer::RecPlayer(cRecording* rec)
+{
+ file = NULL;
+ fileOpen = 0;
+ lastPosition = 0;
+ recording = rec;
+ for(int i = 1; i < 1000; i++) segments[i] = NULL;
+
+ // FIXME find out max file path / name lengths
+
+#if VDRVERSNUM >= 10703
+ indexFile = new cIndexFile(recording->FileName(), false, rec->IsPesRecording());
+#else
+ indexFile = new cIndexFile(recording->FileName(), false);
+#endif
+ if (!indexFile) esyslog("ERROR: Streamdev: Failed to create indexfile!");
+
+ scan();
+}
+
+void RecPlayer::scan()
+{
+ if (file) fclose(file);
+ totalLength = 0;
+ fileOpen = 0;
+ totalFrames = 0;
+
+ int i = 1;
+ while(segments[i++]) delete segments[i];
+
+ char fileName[2048];
+ for(i = 1; i < 1000; i++)
+ {
+
+#if APIVERSNUM < 10703
+ snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
+ //log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
+ file = fopen(fileName, "r");
+#else
+ snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
+ file = fopen(fileName, "r");
+ if (!file) {
+ snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
+ file = fopen(fileName, "r");
+ }
+#endif
+ if (!file) break;
+
+ segments[i] = new Segment();
+ segments[i]->start = totalLength;
+ fseek(file, 0, SEEK_END);
+ totalLength += ftell(file);
+ totalFrames = indexFile->Last();
+ //log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
+ segments[i]->end = totalLength;
+ fclose(file);
+ }
+
+ file = NULL;
+}
+
+RecPlayer::~RecPlayer()
+{
+ //log->log("RecPlayer", Log::DEBUG, "destructor");
+ int i = 1;
+ while(segments[i++]) delete segments[i];
+ if (file) fclose(file);
+}
+
+int RecPlayer::openFile(int index)
+{
+ if (file) fclose(file);
+
+ char fileName[2048];
+
+#if APIVERSNUM >= 10703
+ snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index);
+ isyslog("openFile called for index %i string:%s", index, fileName);
+
+ file = fopen(fileName, "r");
+ if (file)
+ {
+ fileOpen = index;
+ return 1;
+ }
+#endif
+
+ snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
+ isyslog("openFile called for index %i string:%s", index, fileName);
+ //log->log("RecPlayer", Log::DEBUG, "openFile called for index %i string:%s", index, fileName);
+
+ file = fopen(fileName, "r");
+ if (file)
+ {
+ fileOpen = index;
+ return 1;
+ }
+
+ //log->log("RecPlayer", Log::DEBUG, "file failed to open");
+ fileOpen = 0;
+ return 0;
+}
+
+uint64_t RecPlayer::getLengthBytes()
+{
+ return totalLength;
+}
+
+uint32_t RecPlayer::getLengthFrames()
+{
+ return totalFrames;
+}
+
+unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsigned long amount)
+{
+ if ((amount > totalLength) || (amount > 500000))
+ {
+ //log->log("RecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount);
+ return 0;
+ }
+
+ if (position >= totalLength)
+ {
+ //log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
+ return 0;
+ }
+
+ if ((position + amount) > totalLength)
+ {
+ //log->log("RecPlayer", Log::DEBUG, "Client asked for some data past the end of recording, adjusting amount");
+ amount = totalLength - position;
+ }
+
+ // work out what block position is in
+ int segmentNumber;
+ for(segmentNumber = 1; segmentNumber < 1000; segmentNumber++)
+ {
+ if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
+ // position is in this block
+ }
+
+ // we could be seeking around
+ if (segmentNumber != fileOpen)
+ {
+ if (!openFile(segmentNumber)) return 0;
+ }
+
+ uint64_t currentPosition = position;
+ uint32_t yetToGet = amount;
+ uint32_t got = 0;
+ uint32_t getFromThisSegment = 0;
+ uint32_t filePosition;
+
+ while(got < amount)
+ {
+ if (got)
+ {
+ // if(got) then we have already got some and we are back around
+ // advance the file pointer to the next file
+ if (!openFile(++segmentNumber)) return 0;
+ }
+
+ // is the request completely in this block?
+ if ((currentPosition + yetToGet) <= segments[segmentNumber]->end)
+ getFromThisSegment = yetToGet;
+ else
+ getFromThisSegment = segments[segmentNumber]->end - currentPosition;
+
+ filePosition = currentPosition - segments[segmentNumber]->start;
+ fseek(file, filePosition, SEEK_SET);
+ if (fread(&buffer[got], getFromThisSegment, 1, file) != 1) return 0; // umm, big problem.
+
+ // Tell linux not to bother keeping the data in the FS cache
+ posix_fadvise(file->_fileno, filePosition, getFromThisSegment, POSIX_FADV_DONTNEED);
+
+ got += getFromThisSegment;
+ currentPosition += getFromThisSegment;
+ yetToGet -= getFromThisSegment;
+ }
+
+ lastPosition = position;
+ return got;
+}
+
+uint64_t RecPlayer::getLastPosition()
+{
+ return lastPosition;
+}
+
+cRecording* RecPlayer::getCurrentRecording()
+{
+ return recording;
+}
+
+uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber)
+{
+ if (!indexFile) return 0;
+
+#if VDRVERSNUM >= 10703
+ uint16_t retFileNumber;
+ off_t retFileOffset;
+#else
+ uchar retFileNumber;
+ int retFileOffset;
+#endif
+
+ if (!indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset))
+ {
+ return 0;
+ }
+
+// log->log("RecPlayer", Log::DEBUG, "FN: %u FO: %i", retFileNumber, retFileOffset);
+ if (!segments[retFileNumber]) return 0;
+ uint64_t position = segments[retFileNumber]->start + retFileOffset;
+// log->log("RecPlayer", Log::DEBUG, "Pos: %llu", position);
+
+ return position;
+}
+
+uint32_t RecPlayer::frameNumberFromPosition(uint64_t position)
+{
+ if (!indexFile) return 0;
+
+ if (position >= totalLength)
+ {
+ //log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
+ return 0;
+ }
+
+ uint8_t segmentNumber;
+ for(segmentNumber = 1; segmentNumber < 255; segmentNumber++)
+ {
+ if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break;
+ // position is in this block
+ }
+ uint32_t askposition = position - segments[segmentNumber]->start;
+ return indexFile->Get((int)segmentNumber, askposition);
+
+}
+
+
+bool RecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength)
+{
+ // 0 = backwards
+ // 1 = forwards
+
+ if (!indexFile) return false;
+
+ int iframeLength;
+ int indexReturnFrameNumber;
+
+ indexReturnFrameNumber = (uint32_t)indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), NULL, NULL, &iframeLength);
+ //log->log("RecPlayer", Log::DEBUG, "GNIF input framenumber:%lu, direction=%lu, 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/plugins/streamdev/streamdev-cvs/server/recplayer.h b/plugins/streamdev/streamdev-cvs/server/recplayer.h
new file mode 100644
index 0000000..3da6c89
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/recplayer.h
@@ -0,0 +1,63 @@
+/*
+ Copyright 2004-2005 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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 of the License, or
+ (at your option) any later version.
+
+ VOMP 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 VOMP; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef RECPLAYER_H
+#define RECPLAYER_H
+
+#include <stdio.h>
+#include <vdr/recording.h>
+
+#include "server/streamer.h"
+
+class Segment
+{
+ public:
+ uint64_t start;
+ uint64_t end;
+};
+
+class RecPlayer
+{
+ public:
+ RecPlayer(cRecording* rec);
+ ~RecPlayer();
+ uint64_t getLengthBytes();
+ uint32_t getLengthFrames();
+ unsigned long getBlock(unsigned char* buffer, uint64_t position, unsigned long amount);
+ int openFile(int index);
+ uint64_t getLastPosition();
+ cRecording* getCurrentRecording();
+ void scan();
+ 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);
+
+ private:
+ cRecording* recording;
+ cIndexFile* indexFile;
+ FILE* file;
+ int fileOpen;
+ Segment* segments[1000];
+ uint64_t totalLength;
+ uint64_t lastPosition;
+ uint32_t totalFrames;
+};
+
+#endif
diff --git a/plugins/streamdev/streamdev-cvs/server/server.c b/plugins/streamdev/streamdev-cvs/server/server.c
new file mode 100644
index 0000000..1bdb20a
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/server.c
@@ -0,0 +1,173 @@
+/*
+ * $Id: server.c,v 1.10 2009/02/13 10:39:22 schmirl Exp $
+ */
+
+#include "server/server.h"
+#include "server/componentVTP.h"
+#include "server/componentHTTP.h"
+#include "server/componentIGMP.h"
+#include "server/setup.h"
+
+#include <vdr/tools.h>
+#include <tools/select.h>
+#include <string.h>
+#include <errno.h>
+
+cSVDRPhosts StreamdevHosts;
+char *opt_auth = NULL;
+char *opt_remux = NULL;
+
+cStreamdevServer *cStreamdevServer::m_Instance = NULL;
+cList<cServerComponent> cStreamdevServer::m_Servers;
+cList<cServerConnection> cStreamdevServer::m_Clients;
+
+cStreamdevServer::cStreamdevServer(void):
+ cThread("streamdev server")
+{
+ Start();
+}
+
+cStreamdevServer::~cStreamdevServer()
+{
+ Stop();
+}
+
+void cStreamdevServer::Initialize(void)
+{
+ if (m_Instance == NULL) {
+ if (StreamdevServerSetup.StartVTPServer) Register(new cComponentVTP);
+ if (StreamdevServerSetup.StartHTTPServer) Register(new cComponentHTTP);
+ if (StreamdevServerSetup.StartIGMPServer) {
+ if (strcmp(StreamdevServerSetup.IGMPBindIP, "0.0.0.0") == 0)
+ esyslog("streamdev-server: Not starting IGMP. IGMP must be bound to a local IP");
+ else
+ Register(new cComponentIGMP);
+ }
+
+ m_Instance = new cStreamdevServer;
+ }
+}
+
+void cStreamdevServer::Destruct(void)
+{
+ DELETENULL(m_Instance);
+}
+
+void cStreamdevServer::Stop(void)
+{
+ if (Running())
+ Cancel(3);
+}
+
+void cStreamdevServer::Register(cServerComponent *Server)
+{
+ m_Servers.Add(Server);
+}
+
+void cStreamdevServer::Action(void)
+{
+ /* Initialize Server components, deleting those that failed */
+ for (cServerComponent *c = m_Servers.First(); c;) {
+ cServerComponent *next = m_Servers.Next(c);
+ if (!c->Initialize())
+ m_Servers.Del(c);
+ c = next;
+ }
+
+ if (m_Servers.Count() == 0) {
+ esyslog("ERROR: no streamdev server activated, exiting");
+ Cancel(-1);
+ }
+
+ cTBSelect select;
+ while (Running()) {
+ select.Clear();
+
+ /* Ask all Server components to register to the selector */
+ for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c))
+ select.Add(c->Socket(), false);
+
+ /* Ask all Client connections to register to the selector */
+ for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s))
+ {
+ select.Add(s->Socket(), false);
+ if (s->HasData())
+ select.Add(s->Socket(), true);
+ }
+
+ int sel;
+ do
+ {
+ sel = select.Select(400);
+ if (sel < 0 && errno == ETIMEDOUT) {
+ // check for aborted clients
+ for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s)) {
+ if (s->Abort())
+ sel = 0;
+ }
+ }
+ } while (sel < 0 && errno == ETIMEDOUT && Running());
+
+ if (!Running())
+ break;
+ if (sel < 0) {
+ esyslog("fatal error, server exiting: %m");
+ break;
+ }
+
+ /* Ask all Server components to act on signalled sockets */
+ for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)){
+ if (sel && select.CanRead(c->Socket())) {
+ cServerConnection *client = c->Accept();
+ if (!client)
+ continue;
+ m_Clients.Add(client);
+
+ if (m_Clients.Count() > StreamdevServerSetup.MaxClients) {
+ esyslog("streamdev: too many clients, rejecting %s:%d",
+ client->RemoteIp().c_str(), client->RemotePort());
+ client->Reject();
+ } else if (!client->CanAuthenticate() && !StreamdevHosts.Acceptable(client->RemoteIpAddr())) {
+ esyslog("streamdev: client %s:%d not allowed to connect",
+ client->RemoteIp().c_str(), client->RemotePort());
+ client->Reject();
+ } else
+ client->Welcome();
+ }
+ }
+
+ /* Ask all Client connections to act on signalled sockets */
+ for (cServerConnection *s = m_Clients.First(); s;) {
+ bool result = true;
+
+ if (sel && select.CanWrite(s->Socket()))
+ result = s->Write();
+
+ if (sel && result && select.CanRead(s->Socket()))
+ result = s->Read();
+
+ result &= !s->Abort();
+
+ cServerConnection *next = m_Clients.Next(s);
+ if (!result) {
+ isyslog("streamdev: closing streamdev connection to %s:%d",
+ s->RemoteIp().c_str(), s->RemotePort());
+ s->Close();
+ m_Clients.Del(s);
+ }
+ s = next;
+ }
+ }
+
+ while (m_Clients.Count() > 0) {
+ cServerConnection *s = m_Clients.First();
+ s->Close();
+ m_Clients.Del(s);
+ }
+
+ while (m_Servers.Count() > 0) {
+ cServerComponent *c = m_Servers.First();
+ c->Destruct();
+ m_Servers.Del(c);
+ }
+}
diff --git a/plugins/streamdev/streamdev-cvs/server/server.h b/plugins/streamdev/streamdev-cvs/server/server.h
new file mode 100644
index 0000000..a44df1c
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/server.h
@@ -0,0 +1,49 @@
+/*
+ * $Id: server.h,v 1.6 2008/10/22 11:59:32 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SERVER_H
+#define VDR_STREAMDEV_SERVER_H
+
+#include <vdr/thread.h>
+
+#include "server/component.h"
+#include "server/connection.h"
+
+#define DEFAULT_EXTERNREMUX (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "externremux.sh"))
+#define STREAMDEVHOSTSPATH (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "streamdevhosts.conf"))
+
+extern char *opt_auth;
+extern char *opt_remux;
+
+class cStreamdevServer: public cThread {
+private:
+ static cStreamdevServer *m_Instance;
+ static cList<cServerComponent> m_Servers;
+ static cList<cServerConnection> m_Clients;
+
+protected:
+ void Stop(void);
+
+ virtual void Action(void);
+
+ static void Register(cServerComponent *Server);
+
+public:
+ cStreamdevServer(void);
+ virtual ~cStreamdevServer();
+
+ static void Initialize(void);
+ static void Destruct(void);
+ static bool Active(void);
+};
+
+inline bool cStreamdevServer::Active(void)
+{
+ return m_Instance != NULL
+ && m_Instance->m_Clients.Count() > 0;
+}
+
+extern cSVDRPhosts StreamdevHosts;
+
+#endif // VDR_STREAMDEV_SERVER_H
diff --git a/plugins/streamdev/streamdev-cvs/server/setup.c b/plugins/streamdev/streamdev-cvs/server/setup.c
new file mode 100644
index 0000000..db709db
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/setup.c
@@ -0,0 +1,141 @@
+/*
+ * $Id: setup.c,v 1.9 2009/10/13 06:38:47 schmirl Exp $
+ */
+
+#include <vdr/menuitems.h>
+
+#include "server/setup.h"
+#include "server/server.h"
+
+cStreamdevServerSetup StreamdevServerSetup;
+
+cStreamdevServerSetup::cStreamdevServerSetup(void) {
+ MaxClients = 5;
+ StartVTPServer = true;
+ VTPServerPort = 2004;
+ StartHTTPServer = true;
+ HTTPServerPort = 3000;
+ HTTPStreamType = stTS;
+ StartIGMPServer = false;
+ IGMPClientPort = 1234;
+ IGMPStreamType = stTS;
+ SuspendMode = smAlways;
+ AllowSuspend = false;
+ strcpy(VTPBindIP, "0.0.0.0");
+ strcpy(HTTPBindIP, "0.0.0.0");
+ strcpy(IGMPBindIP, "0.0.0.0");
+}
+
+bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) {
+ if (strcmp(Name, "MaxClients") == 0) MaxClients = atoi(Value);
+ else if (strcmp(Name, "StartServer") == 0) StartVTPServer = atoi(Value);
+ else if (strcmp(Name, "ServerPort") == 0) VTPServerPort = atoi(Value);
+ else if (strcmp(Name, "VTPBindIP") == 0) strcpy(VTPBindIP, Value);
+ else if (strcmp(Name, "StartHTTPServer") == 0) StartHTTPServer = atoi(Value);
+ else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value);
+ else if (strcmp(Name, "HTTPStreamType") == 0) HTTPStreamType = atoi(Value);
+ else if (strcmp(Name, "HTTPBindIP") == 0) strcpy(HTTPBindIP, Value);
+ else if (strcmp(Name, "StartIGMPServer") == 0) StartIGMPServer = atoi(Value);
+ else if (strcmp(Name, "IGMPClientPort") == 0) IGMPClientPort = atoi(Value);
+ else if (strcmp(Name, "IGMPStreamType") == 0) IGMPStreamType = atoi(Value);
+ else if (strcmp(Name, "IGMPBindIP") == 0) strcpy(IGMPBindIP, Value);
+ else if (strcmp(Name, "SuspendMode") == 0) SuspendMode = atoi(Value);
+ else if (strcmp(Name, "AllowSuspend") == 0) AllowSuspend = atoi(Value);
+ else return false;
+ return true;
+}
+
+const char* cStreamdevServerMenuSetupPage::StreamTypes[st_Count - 1] = {
+ "TS",
+ "PES",
+ "PS",
+ "ES",
+ "Extern"
+};
+
+const char* cStreamdevServerMenuSetupPage::SuspendModes[sm_Count] = {
+ trNOOP("Offer suspend mode"),
+ trNOOP("Always suspended"),
+ trNOOP("Never suspended")
+};
+
+cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
+ m_NewSetup = StreamdevServerSetup;
+
+ static const char* modes[sm_Count];
+ for (int i = 0; i < sm_Count; i++)
+ modes[i] = tr(SuspendModes[i]);
+
+ AddCategory (tr("Common Settings"));
+ Add(new cMenuEditIntItem (tr("Maximum Number of Clients"), &m_NewSetup.MaxClients, 0, 100));
+
+ Add(new cMenuEditStraItem(tr("Suspend behaviour"), &m_NewSetup.SuspendMode, sm_Count, modes));
+ Add(new cMenuEditBoolItem(tr("Client may suspend"), &m_NewSetup.AllowSuspend));
+
+ AddCategory (tr("VDR-to-VDR Server"));
+ Add(new cMenuEditBoolItem(tr("Start VDR-to-VDR Server"), &m_NewSetup.StartVTPServer));
+ Add(new cMenuEditIntItem (tr("VDR-to-VDR Server Port"), &m_NewSetup.VTPServerPort, 0, 65535));
+ Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.VTPBindIP));
+
+ AddCategory (tr("HTTP Server"));
+ Add(new cMenuEditBoolItem(tr("Start HTTP Server"), &m_NewSetup.StartHTTPServer));
+ Add(new cMenuEditIntItem (tr("HTTP Server Port"), &m_NewSetup.HTTPServerPort, 0, 65535));
+ Add(new cMenuEditStraItem(tr("HTTP Streamtype"), &m_NewSetup.HTTPStreamType, st_Count - 1, StreamTypes));
+ Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.HTTPBindIP));
+ AddCategory (tr("Multicast Streaming Server"));
+ Add(new cMenuEditBoolItem(tr("Start IGMP Server"), &m_NewSetup.StartIGMPServer));
+ Add(new cMenuEditIntItem (tr("Multicast Client Port"), &m_NewSetup.IGMPClientPort, 0, 65535));
+ Add(new cMenuEditStraItem(tr("Multicast Streamtype"), &m_NewSetup.IGMPStreamType, st_Count - 1, StreamTypes));
+ Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.IGMPBindIP));
+ SetCurrent(Get(1));
+}
+
+cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() {
+}
+
+void cStreamdevServerMenuSetupPage::AddCategory(const char *Title) {
+
+ cString str = cString::sprintf("--- %s -------------------------------------------------"
+ "---------------", Title );
+
+ cOsdItem *item = new cOsdItem(*str);
+ item->SetSelectable(false);
+ Add(item);
+}
+
+void cStreamdevServerMenuSetupPage::Store(void) {
+ bool restart = false;
+ if (m_NewSetup.StartVTPServer != StreamdevServerSetup.StartVTPServer
+ || m_NewSetup.VTPServerPort != StreamdevServerSetup.VTPServerPort
+ || strcmp(m_NewSetup.VTPBindIP, StreamdevServerSetup.VTPBindIP) != 0
+ || m_NewSetup.StartHTTPServer != StreamdevServerSetup.StartHTTPServer
+ || m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort
+ || strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0
+ || m_NewSetup.StartIGMPServer != StreamdevServerSetup.StartIGMPServer
+ || m_NewSetup.IGMPClientPort != StreamdevServerSetup.IGMPClientPort
+ || strcmp(m_NewSetup.IGMPBindIP, StreamdevServerSetup.IGMPBindIP) != 0) {
+ restart = true;
+ cStreamdevServer::Destruct();
+ }
+
+ SetupStore("MaxClients", m_NewSetup.MaxClients);
+ SetupStore("StartServer", m_NewSetup.StartVTPServer);
+ SetupStore("ServerPort", m_NewSetup.VTPServerPort);
+ SetupStore("VTPBindIP", m_NewSetup.VTPBindIP);
+ SetupStore("StartHTTPServer", m_NewSetup.StartHTTPServer);
+ SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort);
+ SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType);
+ SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
+ SetupStore("StartIGMPServer", m_NewSetup.StartIGMPServer);
+ SetupStore("IGMPClientPort", m_NewSetup.IGMPClientPort);
+ SetupStore("IGMPStreamType", m_NewSetup.IGMPStreamType);
+ SetupStore("IGMPBindIP", m_NewSetup.IGMPBindIP);
+ SetupStore("SuspendMode", m_NewSetup.SuspendMode);
+ SetupStore("AllowSuspend", m_NewSetup.AllowSuspend);
+
+ StreamdevServerSetup = m_NewSetup;
+
+ if (restart)
+ cStreamdevServer::Initialize();
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/server/setup.h b/plugins/streamdev/streamdev-cvs/server/setup.h
new file mode 100644
index 0000000..d22ab34
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/setup.h
@@ -0,0 +1,48 @@
+/*
+ * $Id: setup.h,v 1.3 2009/09/18 10:43:26 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SETUPSERVER_H
+#define VDR_STREAMDEV_SETUPSERVER_H
+
+#include "common.h"
+
+struct cStreamdevServerSetup {
+ cStreamdevServerSetup(void);
+
+ bool SetupParse(const char *Name, const char *Value);
+
+ int MaxClients;
+ int StartVTPServer;
+ int VTPServerPort;
+ char VTPBindIP[20];
+ int StartHTTPServer;
+ int HTTPServerPort;
+ int HTTPStreamType;
+ char HTTPBindIP[20];
+ int StartIGMPServer;
+ int IGMPClientPort;
+ int IGMPStreamType;
+ char IGMPBindIP[20];
+ int SuspendMode;
+ int AllowSuspend;
+};
+
+extern cStreamdevServerSetup StreamdevServerSetup;
+
+class cStreamdevServerMenuSetupPage: public cMenuSetupPage {
+private:
+ static const char* StreamTypes[];
+ static const char* SuspendModes[];
+ cStreamdevServerSetup m_NewSetup;
+
+ void AddCategory(const char *Title);
+protected:
+ virtual void Store(void);
+
+public:
+ cStreamdevServerMenuSetupPage(void);
+ virtual ~cStreamdevServerMenuSetupPage();
+};
+
+#endif // VDR_STREAMDEV_SETUPSERVER_H
diff --git a/plugins/streamdev/streamdev-cvs/server/streamer.c b/plugins/streamdev/streamdev-cvs/server/streamer.c
new file mode 100644
index 0000000..42e7efa
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/streamer.c
@@ -0,0 +1,162 @@
+/*
+ * $Id: streamer.c,v 1.19 2009/06/19 06:32:45 schmirl Exp $
+ */
+
+#include <vdr/ringbuffer.h>
+#include <vdr/device.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "server/streamer.h"
+#include "server/suspend.h"
+#include "server/setup.h"
+#include "tools/socket.h"
+#include "tools/select.h"
+#include "common.h"
+
+// --- cStreamdevBuffer -------------------------------------------------------
+
+cStreamdevBuffer::cStreamdevBuffer(int Size, int Margin, bool Statistics, const char *Description):
+ cRingBufferLinear(Size, Margin, Statistics, Description)
+{
+}
+
+// --- cStreamdevWriter -------------------------------------------------------
+
+cStreamdevWriter::cStreamdevWriter(cTBSocket *Socket,
+ cStreamdevStreamer *Streamer):
+ cThread("streamdev-writer"),
+ m_Streamer(Streamer),
+ m_Socket(Socket)
+{
+}
+
+cStreamdevWriter::~cStreamdevWriter()
+{
+ Dprintf("destructing writer\n");
+ if (Running())
+ Cancel(3);
+}
+
+void cStreamdevWriter::Action(void)
+{
+ cTBSelect sel;
+ Dprintf("Writer start\n");
+ int max = 0;
+ uchar *block = NULL;
+ int count, offset = 0;
+
+ sel.Clear();
+ sel.Add(*m_Socket, true);
+ while (Running()) {
+ if (block == NULL) {
+ block = m_Streamer->Get(count);
+ offset = 0;
+ }
+
+ if (block != NULL) {
+ if (sel.Select(15000) == -1) {
+ esyslog("ERROR: streamdev-server: couldn't send data: %m");
+ break;
+ }
+
+ if (sel.CanWrite(*m_Socket)) {
+ int written;
+ int pkgsize = count;
+ // SOCK_DGRAM indicates multicast
+ if (m_Socket->Type() == SOCK_DGRAM) {
+ // don't fragment multicast packets
+ // max. payload on standard local ethernet is 1416 to 1456 bytes
+ // and some STBs expect complete TS packets
+ // so let's always limit to 7 * TS_SIZE = 1316
+ if (pkgsize > 7 * TS_SIZE)
+ pkgsize = 7 * TS_SIZE;
+ else
+ pkgsize -= pkgsize % TS_SIZE;
+ }
+ if ((written = m_Socket->Write(block + offset, pkgsize)) == -1) {
+ esyslog("ERROR: streamdev-server: couldn't send %d bytes: %m", pkgsize);
+ break;
+ }
+
+ // statistics
+ if (count > max)
+ max = count;
+
+ offset += written;
+ count -= written;
+
+ // less than one TS packet left:
+ // delete what we've written so far and get next chunk
+ if (count < TS_SIZE) {
+ m_Streamer->Del(offset);
+ block = NULL;
+ }
+ }
+ }
+ }
+ Dprintf("Max. Transmit Blocksize was: %d\n", max);
+}
+
+// --- cStreamdevStreamer -----------------------------------------------------
+
+cStreamdevStreamer::cStreamdevStreamer(const char *Name):
+ cThread(Name),
+ m_Writer(NULL),
+ m_RingBuffer(new cStreamdevBuffer(STREAMERBUFSIZE, TS_SIZE * 2,
+ true, "streamdev-streamer")),
+ m_SendBuffer(new cStreamdevBuffer(WRITERBUFSIZE, TS_SIZE * 2))
+{
+ m_RingBuffer->SetTimeouts(0, 100);
+ m_SendBuffer->SetTimeouts(100, 100);
+}
+
+cStreamdevStreamer::~cStreamdevStreamer()
+{
+ Dprintf("Desctructing streamer\n");
+ delete m_RingBuffer;
+ delete m_SendBuffer;
+}
+
+void cStreamdevStreamer::Start(cTBSocket *Socket)
+{
+ Dprintf("start streamer\n");
+ m_Writer = new cStreamdevWriter(Socket, this);
+ Attach();
+}
+
+void cStreamdevStreamer::Activate(bool On)
+{
+ if (On && !Active()) {
+ Dprintf("activate streamer\n");
+ m_Writer->Start();
+ cThread::Start();
+ }
+}
+
+void cStreamdevStreamer::Stop(void)
+{
+ if (Running()) {
+ Dprintf("stopping streamer\n");
+ Cancel(3);
+ }
+ if (m_Writer) {
+ Detach();
+ DELETENULL(m_Writer);
+ }
+}
+
+void cStreamdevStreamer::Action(void)
+{
+ while (Running()) {
+ int got;
+ uchar *block = m_RingBuffer->Get(got);
+
+ if (block) {
+ int count = Put(block, got);
+ if (count)
+ m_RingBuffer->Del(count);
+ }
+ }
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/server/streamer.h b/plugins/streamdev/streamdev-cvs/server/streamer.h
new file mode 100644
index 0000000..6561bc2
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/streamer.h
@@ -0,0 +1,102 @@
+/*
+ * $Id: streamer.h,v 1.11 2009/06/19 06:32:45 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_STREAMER_H
+#define VDR_STREAMDEV_STREAMER_H
+
+#include <vdr/thread.h>
+#include <vdr/ringbuffer.h>
+#include <vdr/tools.h>
+
+class cTBSocket;
+class cStreamdevStreamer;
+
+#ifndef TS_SIZE
+#define TS_SIZE 188
+#endif
+
+#define STREAMERBUFSIZE (20000 * TS_SIZE)
+#define WRITERBUFSIZE (5000 * TS_SIZE)
+
+// --- cStreamdevBuffer -------------------------------------------------------
+
+class cStreamdevBuffer: public cRingBufferLinear {
+public:
+ // make public
+ void WaitForPut(void) { cRingBuffer::WaitForPut(); }
+ // Always write complete TS packets
+ // (assumes Count is a multiple of TS_SIZE)
+ int PutTS(const uchar *Data, int Count);
+ cStreamdevBuffer(int Size, int Margin = 0, bool Statistics = false, const char *Description = NULL);
+};
+
+inline int cStreamdevBuffer::PutTS(const uchar *Data, int Count)
+{
+ int free = Free();
+ if (free < Count)
+ Count = free;
+
+ Count -= Count % TS_SIZE;
+ if (Count)
+ Count = Put(Data, Count);
+ else
+ WaitForPut();
+ return Count;
+}
+
+// --- cStreamdevWriter -------------------------------------------------------
+
+class cStreamdevWriter: public cThread {
+private:
+ cStreamdevStreamer *m_Streamer;
+ cTBSocket *m_Socket;
+
+protected:
+ virtual void Action(void);
+
+public:
+ cStreamdevWriter(cTBSocket *Socket, cStreamdevStreamer *Streamer);
+ virtual ~cStreamdevWriter();
+};
+
+// --- cStreamdevStreamer -----------------------------------------------------
+
+class cStreamdevStreamer: public cThread {
+private:
+ cStreamdevWriter *m_Writer;
+ cStreamdevBuffer *m_RingBuffer;
+ cStreamdevBuffer *m_SendBuffer;
+
+protected:
+ virtual void Action(void);
+
+ bool IsRunning(void) const { return m_Writer; }
+
+public:
+ cStreamdevStreamer(const char *Name);
+ virtual ~cStreamdevStreamer();
+
+ virtual void Start(cTBSocket *Socket);
+ virtual void Stop(void);
+ bool Abort(void);
+
+ void Activate(bool On);
+ int Receive(uchar *Data, int Length) { return m_RingBuffer->PutTS(Data, Length); }
+ void ReportOverflow(int Bytes) { m_RingBuffer->ReportOverflow(Bytes); }
+
+ virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->PutTS(Data, Count); }
+ virtual uchar *Get(int &Count) { return m_SendBuffer->Get(Count); }
+ virtual void Del(int Count) { m_SendBuffer->Del(Count); }
+
+ virtual void Detach(void) {}
+ virtual void Attach(void) {}
+};
+
+inline bool cStreamdevStreamer::Abort(void)
+{
+ return Active() && !m_Writer->Active();
+}
+
+#endif // VDR_STREAMDEV_STREAMER_H
+
diff --git a/plugins/streamdev/streamdev-cvs/server/suspend.c b/plugins/streamdev/streamdev-cvs/server/suspend.c
new file mode 100644
index 0000000..b6e1382
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/suspend.c
@@ -0,0 +1,57 @@
+/*
+ * $Id: suspend.c,v 1.3 2008/10/22 11:59:32 schmirl Exp $
+ */
+
+#include "server/suspend.h"
+#include "server/suspend.dat"
+#include "common.h"
+
+cSuspendLive::cSuspendLive(void)
+ : cThread("Streamdev: server suspend")
+{
+}
+
+cSuspendLive::~cSuspendLive() {
+ Stop();
+ Detach();
+}
+
+void cSuspendLive::Activate(bool On) {
+ Dprintf("Activate cSuspendLive %d\n", On);
+ if (On)
+ Start();
+ else
+ Stop();
+}
+
+void cSuspendLive::Stop(void) {
+ if (Running())
+ Cancel(3);
+}
+
+void cSuspendLive::Action(void) {
+ while (Running()) {
+ DeviceStillPicture(suspend_mpg, sizeof(suspend_mpg));
+ cCondWait::SleepMs(100);
+ }
+}
+
+bool cSuspendCtl::m_Active = false;
+
+cSuspendCtl::cSuspendCtl(void):
+ cControl(m_Suspend = new cSuspendLive) {
+ m_Active = true;
+}
+
+cSuspendCtl::~cSuspendCtl() {
+ m_Active = false;
+ DELETENULL(m_Suspend);
+}
+
+eOSState cSuspendCtl::ProcessKey(eKeys Key) {
+ if (!m_Suspend->Active() || Key == kBack) {
+ DELETENULL(m_Suspend);
+ return osEnd;
+ }
+ return osContinue;
+}
diff --git a/plugins/streamdev/streamdev-cvs/server/suspend.dat b/plugins/streamdev/streamdev-cvs/server/suspend.dat
new file mode 100644
index 0000000..7b1b890
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/suspend.dat
@@ -0,0 +1,1206 @@
+const unsigned char suspend_mpg[] = {
+ 0x00, 0x00, 0x01, 0xb3, 0x2d, 0x02, 0x40, 0x83, 0x02, 0xd0, 0x20, 0xa0,
+ 0x00, 0x00, 0x01, 0xb2, 0x4d, 0x50, 0x45, 0x47, 0x0a, 0x00, 0x00, 0x01,
+ 0xb8, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0a, 0xbb,
+ 0x58, 0x00, 0x00, 0x01, 0x01, 0x52, 0x97, 0xe6, 0x54, 0xa5, 0x2f, 0xdc,
+ 0xaf, 0x9a, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x45, 0xe2,
+ 0x2c, 0x52, 0x67, 0x98, 0x6c, 0x9a, 0x2e, 0xb9, 0x9e, 0xb4, 0x6c, 0xdd,
+ 0x34, 0x8c, 0x8b, 0xab, 0xa6, 0x61, 0xb5, 0xbd, 0x33, 0x8d, 0xa7, 0x22,
+ 0x6f, 0x75, 0x74, 0xcc, 0x36, 0xb4, 0x6c, 0xfd, 0x34, 0x64, 0x4d, 0xee,
+ 0x91, 0xb3, 0xf4, 0xd6, 0xf4, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b,
+ 0x37, 0x4d, 0x77, 0x4c, 0xdd, 0x34, 0xe4, 0x43, 0xdd, 0x43, 0x66, 0xe9,
+ 0xad, 0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x4f, 0x4c, 0xe3, 0x6b, 0x46,
+ 0xce, 0x36, 0x8c, 0x89, 0xbd, 0xd3, 0xd3, 0x3f, 0x4d, 0x68, 0xd9, 0x86,
+ 0xd3, 0x91, 0x37, 0xba, 0x86, 0xcc, 0x36, 0xbb, 0xa6, 0x6e, 0x9a, 0x32,
+ 0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xbd, 0x33, 0xf4, 0xd1, 0x91, 0x37,
+ 0xba, 0x46, 0xcf, 0xd3, 0x5a, 0x36, 0x7e, 0x9a, 0x72, 0x26, 0xf7, 0x4f,
+ 0x4c, 0xfd, 0x35, 0xa3, 0x66, 0x1b, 0x46, 0x44, 0xde, 0xea, 0xe9, 0x9c,
+ 0x6d, 0x67, 0x4c, 0xfd, 0x34, 0x64, 0x43, 0xdd, 0x43, 0x66, 0xe9, 0xad,
+ 0xe9, 0x9c, 0x6d, 0x39, 0x13, 0x7b, 0xa7, 0xa6, 0x7e, 0x9a, 0xd1, 0xb3,
+ 0x0d, 0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcd, 0xd3, 0x5c, 0x36, 0x61, 0xb4,
+ 0x0c, 0x9b, 0xdd, 0x43, 0x66, 0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x72, 0x26,
+ 0xf7, 0x50, 0xd9, 0xba, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea,
+ 0x1b, 0x30, 0xda, 0xd1, 0xb3, 0x8d, 0xa3, 0x22, 0x6f, 0x74, 0x8d, 0x9c,
+ 0x6d, 0x68, 0xd9, 0xba, 0x69, 0xc8, 0x87, 0xba, 0xba, 0x66, 0x1b, 0x5c,
+ 0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xdd, 0x35, 0xdd, 0x33,
+ 0x74, 0xd1, 0x91, 0x37, 0xba, 0x9e, 0x6e, 0x9a, 0xde, 0x99, 0xfa, 0x68,
+ 0xc8, 0x9b, 0xdd, 0x23, 0x67, 0xe9, 0xad, 0x79, 0xc6, 0xd3, 0x91, 0x37,
+ 0xba, 0x7a, 0x67, 0xe9, 0xad, 0x1b, 0x30, 0xda, 0x32, 0x26, 0xf7, 0x50,
+ 0xd9, 0xba, 0x6b, 0x7a, 0x67, 0x1b, 0x46, 0x44, 0x3d, 0xd4, 0x36, 0x61,
+ 0xb5, 0xbd, 0x33, 0xbd, 0x39, 0x13, 0x7b, 0xa7, 0xa6, 0x71, 0xb5, 0xbf,
+ 0xcc, 0x36, 0x8c, 0x89, 0xbd, 0xd4, 0xf3, 0x0d, 0xae, 0xe9, 0x9b, 0xa6,
+ 0x9c, 0x89, 0xbd, 0xd4, 0xf3, 0x74, 0xd7, 0x74, 0xcc, 0x36, 0x8c, 0x89,
+ 0xbd, 0xd2, 0x36, 0x7e, 0x9a, 0xd1, 0xb3, 0x0d, 0xa3, 0x22, 0x6f, 0x75,
+ 0x74, 0xcf, 0xd3, 0x5b, 0xd3, 0x37, 0x4d, 0x19, 0x10, 0xf7, 0x53, 0xcc,
+ 0x36, 0xb7, 0xa6, 0x71, 0xb4, 0xe4, 0x4d, 0xee, 0x91, 0xb3, 0xf4, 0xd6,
+ 0x8d, 0x98, 0x6d, 0x03, 0x26, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x2b, 0xf9,
+ 0x95, 0x29, 0x4b, 0xf7, 0x2b, 0xe6, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x45, 0xe2, 0x2c, 0x52, 0x71, 0xb3, 0x74, 0xc9, 0xa2,
+ 0xeb, 0x79, 0x86, 0xd6, 0xf4, 0xcc, 0x36, 0x9c, 0x88, 0xba, 0xba, 0x66,
+ 0xe9, 0xad, 0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x50, 0xd9, 0xba, 0x6b,
+ 0x7a, 0x67, 0xe9, 0xa7, 0x22, 0x6f, 0x74, 0x8d, 0x9f, 0xa6, 0xb4, 0x6c,
+ 0xc3, 0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x0d, 0xae, 0x1b, 0x37, 0x4d,
+ 0x19, 0x13, 0x7b, 0xab, 0xa6, 0x61, 0xb5, 0xc3, 0x66, 0xe9, 0xa3, 0x22,
+ 0x1e, 0xea, 0x1b, 0x30, 0xda, 0xde, 0x99, 0xfa, 0x69, 0xc8, 0x9b, 0xdd,
+ 0x23, 0x67, 0x1b, 0x5b, 0xd3, 0x37, 0x4d, 0x19, 0x13, 0x7b, 0xa8, 0x6c,
+ 0xc3, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0xe9, 0x9b, 0xa6,
+ 0xb7, 0xa6, 0x7e, 0x9a, 0x72, 0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xa3,
+ 0x67, 0xe9, 0xa3, 0x22, 0x6f, 0x74, 0xf4, 0xce, 0x36, 0xb7, 0xa6, 0x67,
+ 0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0xf5, 0xc3, 0x66, 0x1b, 0x4e, 0x44,
+ 0xde, 0xea, 0xe9, 0x9b, 0xa6, 0xb4, 0x6c, 0xfd, 0x34, 0x0c, 0x87, 0xba,
+ 0x46, 0xcf, 0xd3, 0x5a, 0x36, 0x61, 0xb4, 0x64, 0x4d, 0xee, 0xa1, 0xb3,
+ 0xf4, 0xd6, 0x8d, 0x9b, 0xa6, 0x9c, 0x89, 0xbd, 0xd5, 0xd3, 0x30, 0xda,
+ 0xee, 0x99, 0x86, 0xd1, 0x91, 0x37, 0xba, 0x86, 0xcc, 0x36, 0xb8, 0x6c,
+ 0xc3, 0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x74, 0xd6, 0x8d, 0x9c, 0x6d,
+ 0x39, 0x13, 0x7b, 0xa4, 0x6c, 0xe3, 0x6b, 0x7a, 0x66, 0xe9, 0xa3, 0x22,
+ 0x6f, 0x75, 0x3c, 0xdd, 0x35, 0xdd, 0x33, 0x0d, 0xa3, 0x22, 0x1e, 0xea,
+ 0xe9, 0x9b, 0xa6, 0xb8, 0x6c, 0xdd, 0x34, 0xe4, 0x4d, 0xee, 0xa1, 0xb3,
+ 0x0d, 0xad, 0x1b, 0x3f, 0x4d, 0x19, 0x13, 0x7b, 0xa4, 0x6c, 0xe3, 0x6b,
+ 0x7a, 0x67, 0x1b, 0x46, 0x44, 0xde, 0xe9, 0x1b, 0x38, 0xda, 0xd1, 0xb3,
+ 0x3d, 0x39, 0x13, 0x7b, 0xab, 0xa6, 0x61, 0xb5, 0xdd, 0x33, 0x74, 0xd1,
+ 0x91, 0x37, 0xba, 0x5e, 0x7e, 0x9a, 0xee, 0x99, 0x9e, 0x8c, 0x88, 0x7b,
+ 0xa4, 0x6c, 0xfd, 0x35, 0xbf, 0xcc, 0x36, 0x9c, 0x89, 0xbd, 0xd5, 0xd3,
+ 0x33, 0xd7, 0x0d, 0x9b, 0xa6, 0x8c, 0x89, 0xbd, 0xd5, 0xd3, 0x37, 0x4d,
+ 0x68, 0xd9, 0xc6, 0xd1, 0x91, 0x37, 0xba, 0xba, 0x66, 0x1b, 0x5b, 0xd3,
+ 0x33, 0xd3, 0x91, 0x37, 0xba, 0x86, 0xce, 0x36, 0xb7, 0xa6, 0x61, 0xb4,
+ 0x64, 0x4d, 0xee, 0xa1, 0xb3, 0x74, 0xd7, 0x0d, 0x98, 0x6d, 0x19, 0x13,
+ 0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xd1, 0xb3, 0xf4, 0xd3, 0x91, 0x0f, 0x74,
+ 0x8d, 0x9f, 0xa6, 0xb4, 0x6c, 0xe3, 0x68, 0xc8, 0x9b, 0xdd, 0x23, 0x66,
+ 0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xc3, 0x6b,
+ 0xba, 0x66, 0x1b, 0x4e, 0x44, 0xde, 0x00, 0x00, 0x01, 0x03, 0x2b, 0xf9,
+ 0x95, 0x29, 0x4b, 0xf7, 0x2b, 0xe6, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x28, 0xf3, 0x3c, 0x9b, 0xf8, 0xb5, 0xa5, 0x67, 0x4c,
+ 0xe3, 0x64, 0xd1, 0x75, 0x3c, 0xc3, 0x6b, 0x7a, 0x66, 0x1b, 0x4e, 0x44,
+ 0x3d, 0xd5, 0xd3, 0x30, 0xda, 0xe1, 0xb3, 0x74, 0xd1, 0x91, 0x37, 0xba,
+ 0x86, 0xcc, 0x36, 0xb4, 0x6c, 0xfd, 0x34, 0x64, 0x4d, 0xee, 0x9e, 0x99,
+ 0xc6, 0xd6, 0x8d, 0x9c, 0x6d, 0x38, 0xa9, 0xbd, 0xd2, 0x36, 0x71, 0xb5,
+ 0xbd, 0x33, 0x0d, 0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0x36, 0xbb, 0xa6,
+ 0x6e, 0x9a, 0x31, 0x53, 0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xd1, 0xb3, 0x8d,
+ 0xa7, 0x15, 0x37, 0xba, 0xba, 0x66, 0x1b, 0x5a, 0x36, 0x6e, 0x9a, 0x06,
+ 0x43, 0xdd, 0x5d, 0x33, 0x0d, 0xae, 0x1b, 0x30, 0xda, 0x72, 0x26, 0xf7,
+ 0x57, 0x4c, 0xc3, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b,
+ 0x37, 0x4d, 0x6f, 0x4c, 0xe3, 0x68, 0x19, 0x37, 0xba, 0x86, 0xcd, 0xd3,
+ 0x5a, 0x36, 0x61, 0xb4, 0xe4, 0x4d, 0xee, 0xae, 0x99, 0x86, 0xd7, 0x74,
+ 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, 0x30, 0xda, 0xde, 0x99, 0xfa,
+ 0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x0d, 0xad, 0x1b, 0x3f, 0x4d, 0x23,
+ 0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xbd, 0x33, 0x74, 0xd1, 0x91, 0x0f,
+ 0x75, 0x0d, 0x9f, 0xa6, 0xb7, 0xa6, 0x71, 0xb4, 0x64, 0x4d, 0xee, 0x9e,
+ 0x99, 0xba, 0x6b, 0x86, 0xcd, 0xd3, 0x4e, 0x44, 0xde, 0xea, 0x1b, 0x37,
+ 0x4d, 0x6f, 0x4c, 0xfd, 0x34, 0x64, 0x4d, 0xee, 0xa1, 0xb3, 0x0d, 0xad,
+ 0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x4f, 0x4c, 0xe3, 0x6b, 0x46, 0xcd,
+ 0xd3, 0x4e, 0x44, 0xde, 0xea, 0xe9, 0x98, 0x6d, 0x70, 0xd9, 0xba, 0x68,
+ 0xc8, 0x87, 0xba, 0x86, 0xcd, 0xd3, 0x5d, 0xd3, 0x30, 0xda, 0x32, 0x26,
+ 0xf7, 0x57, 0x4c, 0xdd, 0x35, 0xa3, 0x67, 0x1b, 0x4e, 0x44, 0xde, 0xe9,
+ 0x1b, 0x38, 0xda, 0xde, 0x99, 0x86, 0xd1, 0x91, 0x37, 0xba, 0xba, 0x66,
+ 0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xdd, 0x35,
+ 0xc3, 0x66, 0xe9, 0xa7, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0x36, 0xb5, 0xe6,
+ 0xe9, 0xa3, 0x22, 0x1e, 0xea, 0xe9, 0x99, 0xeb, 0xba, 0x66, 0xe9, 0xa3,
+ 0x22, 0x6f, 0x75, 0x0d, 0x98, 0x6d, 0x70, 0xd9, 0x86, 0xd3, 0x91, 0x37,
+ 0xba, 0x86, 0xcd, 0xd3, 0x5a, 0x36, 0x61, 0xb4, 0x64, 0x4e, 0xea, 0xe9,
+ 0x9b, 0xa6, 0xbb, 0xa6, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x53, 0xcc, 0x36,
+ 0xbb, 0xa6, 0x6e, 0x9a, 0x72, 0x26, 0xf7, 0x57, 0x4c, 0xc3, 0x6b, 0x46,
+ 0xcf, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, 0x37, 0x4d, 0x6f, 0x4c, 0xfd,
+ 0x34, 0x64, 0x43, 0xdd, 0x23, 0x67, 0xe9, 0xad, 0xe9, 0x98, 0x6d, 0x39,
+ 0x13, 0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xe1, 0xb3, 0x74, 0xd1, 0x91, 0x37,
+ 0xba, 0xba, 0x66, 0xe9, 0xad, 0x1b, 0x3f, 0x4d, 0x19, 0x13, 0x7b, 0xa4,
+ 0x6c, 0xfd, 0x35, 0xa3, 0x67, 0x1b, 0x4e, 0x44, 0xde, 0xe9, 0xe9, 0x9c,
+ 0x6d, 0x6f, 0x4c, 0xdd, 0x34, 0x64, 0x4d, 0xe0, 0x00, 0x00, 0x01, 0x04,
+ 0x2b, 0xf9, 0x95, 0x22, 0xf3, 0x74, 0xdf, 0xb8, 0xb5, 0xf3, 0x56, 0x91,
+ 0x7a, 0xce, 0x99, 0xc6, 0xc8, 0x64, 0x5d, 0x23, 0x67, 0xe9, 0xad, 0xe9,
+ 0x9b, 0xa6, 0x9c, 0x89, 0xdd, 0xe0, 0x19, 0xf4, 0x80, 0x54, 0x5f, 0x41,
+ 0x2c, 0x20, 0x61, 0x7f, 0x14, 0x4b, 0x25, 0xa5, 0x05, 0x27, 0x25, 0x25,
+ 0x20, 0x95, 0xd0, 0x58, 0xd2, 0xd1, 0xba, 0x33, 0x73, 0x4d, 0xb1, 0xe0,
+ 0x0c, 0x00, 0x60, 0x1a, 0x8d, 0xf2, 0x40, 0xaf, 0xc9, 0xcf, 0xbe, 0x01,
+ 0x88, 0x0e, 0xce, 0xe6, 0xe0, 0x0a, 0x6d, 0x1d, 0x32, 0x00, 0x36, 0xf8,
+ 0xa2, 0x62, 0x40, 0x74, 0xb0, 0x42, 0x08, 0x49, 0x29, 0x0f, 0xbe, 0x00,
+ 0xa3, 0xe1, 0xa1, 0x8c, 0xff, 0xff, 0x90, 0x8e, 0x06, 0x06, 0xba, 0xd2,
+ 0x57, 0xbe, 0x6c, 0x01, 0xa2, 0x51, 0xd0, 0x58, 0x67, 0xdb, 0xfd, 0xc9,
+ 0x79, 0xf6, 0xea, 0x3b, 0x91, 0x2f, 0x54, 0x03, 0x1c, 0x9d, 0x90, 0x5a,
+ 0x3f, 0xc6, 0x75, 0xde, 0xc0, 0x13, 0x10, 0xd2, 0x94, 0x16, 0x50, 0x6f,
+ 0x1a, 0x1a, 0x05, 0x0b, 0x49, 0x30, 0x37, 0xb3, 0x3f, 0x43, 0x74, 0x24,
+ 0x90, 0x84, 0x01, 0x72, 0xba, 0x0b, 0xe3, 0x30, 0xcd, 0xd2, 0xc6, 0x59,
+ 0x7a, 0x6d, 0x20, 0x21, 0xc0, 0x07, 0xc0, 0x19, 0xe4, 0xac, 0x60, 0x09,
+ 0xca, 0x18, 0x49, 0xed, 0x89, 0xa4, 0x27, 0xea, 0x43, 0x30, 0xd2, 0xc3,
+ 0x3f, 0x7e, 0xa5, 0x13, 0x31, 0xf0, 0x7b, 0xbc, 0x03, 0x10, 0x29, 0x88,
+ 0x40, 0x55, 0x39, 0x19, 0x90, 0x90, 0x94, 0x08, 0xe4, 0x0f, 0x72, 0x00,
+ 0xe8, 0x34, 0x0c, 0x86, 0x23, 0x9a, 0x77, 0xba, 0xba, 0x66, 0xe9, 0xad,
+ 0xe9, 0x9f, 0xa6, 0xec, 0xc8, 0xb9, 0xde, 0xe9, 0xe9, 0xb4, 0x13, 0x12,
+ 0x1a, 0x02, 0x14, 0x96, 0x80, 0x28, 0xdb, 0x06, 0x27, 0x20, 0xb2, 0x19,
+ 0x78, 0x68, 0x66, 0xfb, 0xf2, 0x59, 0x45, 0x6c, 0x94, 0x24, 0xb2, 0xd0,
+ 0xcc, 0x96, 0xf9, 0x08, 0xfd, 0x69, 0xe9, 0x47, 0x45, 0x79, 0x0d, 0x25,
+ 0xfc, 0x82, 0x83, 0x3e, 0xc1, 0xa4, 0xd2, 0xd0, 0x18, 0x57, 0x7d, 0xbe,
+ 0xe9, 0xeb, 0x2d, 0x19, 0x3b, 0xe4, 0xf0, 0xc2, 0x86, 0x86, 0x8c, 0x6e,
+ 0xf9, 0x1e, 0xf2, 0xc8, 0x41, 0x40, 0x3a, 0x0c, 0xdb, 0x29, 0x05, 0x63,
+ 0x7b, 0x87, 0xff, 0xd7, 0xff, 0x37, 0xf5, 0xfe, 0xbf, 0x78, 0x80, 0x0f,
+ 0x80, 0x34, 0x40, 0x6e, 0x03, 0x25, 0xa0, 0x6e, 0x76, 0x46, 0x1f, 0xf1,
+ 0xbc, 0x7e, 0xbd, 0x10, 0x10, 0x80, 0xef, 0x86, 0x32, 0x10, 0x7f, 0x27,
+ 0x55, 0x3d, 0x0c, 0x02, 0x02, 0x94, 0x35, 0x21, 0xa5, 0xf6, 0x67, 0xe5,
+ 0xa5, 0x0e, 0x02, 0x20, 0xce, 0x52, 0x1c, 0x5b, 0x55, 0x80, 0x1d, 0x13,
+ 0x07, 0x16, 0x09, 0x3f, 0x81, 0xa0, 0x9b, 0xfb, 0x64, 0x09, 0x4a, 0xdc,
+ 0x65, 0xeb, 0x50, 0x03, 0xb0, 0x1d, 0x97, 0x9c, 0x96, 0x56, 0x5a, 0x73,
+ 0x12, 0xbf, 0x5a, 0x3f, 0xdd, 0x60, 0x65, 0x6b, 0xc7, 0xaa, 0xf0, 0x53,
+ 0x8a, 0x2b, 0x3a, 0x7a, 0x36, 0x38, 0xe2, 0x05, 0xe9, 0xa0, 0x96, 0x5f,
+ 0x57, 0xe1, 0x75, 0x9f, 0x9d, 0x01, 0xb2, 0x05, 0x80, 0x46, 0x92, 0x0d,
+ 0x6f, 0x21, 0x15, 0xc0, 0x98, 0x24, 0xfe, 0x96, 0x00, 0x31, 0xbb, 0x24,
+ 0xb0, 0xd0, 0x26, 0x80, 0x08, 0xb8, 0x00, 0xc6, 0x80, 0x64, 0x37, 0x1a,
+ 0x8e, 0x1d, 0xf1, 0x17, 0xdb, 0x0d, 0x28, 0x7e, 0x0a, 0xb1, 0x18, 0x9b,
+ 0xce, 0x1a, 0x05, 0x06, 0xaf, 0x06, 0x96, 0x80, 0xa6, 0xaf, 0x46, 0x42,
+ 0x3e, 0x41, 0x7b, 0xef, 0xce, 0xe3, 0x52, 0xdc, 0x9e, 0x78, 0xab, 0x22,
+ 0x08, 0x60, 0x12, 0xe6, 0x17, 0xbb, 0xe2, 0x25, 0xc5, 0x2c, 0xac, 0x5a,
+ 0x3e, 0xeb, 0xde, 0xe6, 0xcc, 0x29, 0xae, 0x8e, 0xf8, 0xfb, 0xd2, 0x03,
+ 0x62, 0x85, 0x80, 0x46, 0x92, 0x0d, 0x00, 0x6c, 0x50, 0xee, 0x01, 0x1a,
+ 0x48, 0x37, 0xad, 0x1a, 0x43, 0x43, 0xa0, 0x97, 0x94, 0x3d, 0x0b, 0xf8,
+ 0xce, 0x49, 0x02, 0x34, 0x25, 0x93, 0x40, 0x25, 0x40, 0x04, 0x5c, 0x00,
+ 0x63, 0x69, 0xc6, 0x63, 0x70, 0x55, 0x8b, 0x26, 0xfc, 0x98, 0x0d, 0x8a,
+ 0x1d, 0xc0, 0x23, 0x49, 0x06, 0x80, 0x36, 0x28, 0x77, 0x00, 0x8d, 0x24,
+ 0x1b, 0xd7, 0x92, 0xc8, 0x60, 0x12, 0xa0, 0x02, 0x2e, 0x00, 0x31, 0x89,
+ 0x2c, 0x86, 0x01, 0x2a, 0x00, 0x22, 0x00, 0x17, 0xd4, 0xe5, 0x0f, 0xc1,
+ 0x54, 0x8c, 0x4d, 0xf9, 0x40, 0x0b, 0x0a, 0x30, 0x6e, 0x01, 0x1f, 0xe4,
+ 0x7f, 0xf5, 0xd9, 0x2c, 0x31, 0x25, 0x2f, 0x04, 0xe0, 0x08, 0x44, 0xa9,
+ 0x5c, 0x39, 0xab, 0xb1, 0x0c, 0x35, 0x07, 0xb2, 0x44, 0x2b, 0x91, 0xfd,
+ 0xe3, 0xd0, 0x9c, 0x5e, 0xdf, 0x1b, 0x0c, 0x8e, 0x3b, 0xd6, 0x12, 0x8a,
+ 0x9f, 0xdd, 0xe2, 0xf2, 0x76, 0xfb, 0xef, 0xbe, 0x7d, 0xf2, 0xf7, 0xdc,
+ 0xdd, 0xe2, 0xf5, 0x00, 0x1a, 0x86, 0x00, 0x1f, 0x00, 0xef, 0xa3, 0x12,
+ 0x03, 0x77, 0x0d, 0x28, 0x53, 0x7d, 0x8b, 0x46, 0x51, 0xdb, 0xfd, 0xf3,
+ 0xf0, 0x3e, 0xd9, 0x5a, 0xdc, 0xf7, 0xb6, 0x1a, 0x18, 0x06, 0x13, 0xf0,
+ 0x50, 0x8b, 0xc9, 0x28, 0xb2, 0x5a, 0x37, 0xba, 0x9e, 0x77, 0xad, 0x79,
+ 0x9e, 0xd1, 0xa8, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4,
+ 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x77,
+ 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x2d, 0x17, 0x4b, 0xce,
+ 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86,
+ 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb,
+ 0x5e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b,
+ 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc,
+ 0xef, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7,
+ 0x99, 0xeb, 0x9e, 0x67, 0xa5, 0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d,
+ 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x33,
+ 0xd7, 0x3c, 0xcf, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a, 0x1a,
+ 0x2e, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a,
+ 0xf3, 0x3d, 0x2d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd,
+ 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7,
+ 0x7a, 0x5a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd1, 0x75, 0x3c,
+ 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9,
+ 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9, 0x79, 0xde,
+ 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3,
+ 0xba, 0x9e, 0x67, 0xac, 0x8d, 0x2d, 0x16, 0xaf, 0x17, 0x8d, 0x29, 0x9c,
+ 0x00, 0x00, 0x01, 0x05, 0x3b, 0xf9, 0xd3, 0xce, 0xf5, 0xaf, 0x33, 0xdf,
+ 0xb5, 0x35, 0xf3, 0x97, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd1, 0x75,
+ 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa9, 0xe7, 0x28, 0xb0, 0x1b,
+ 0x00, 0xec, 0x31, 0x26, 0x06, 0xb9, 0x31, 0x21, 0x06, 0x9e, 0x57, 0xe9,
+ 0x1d, 0xbb, 0xa3, 0x61, 0xe1, 0xb5, 0x8f, 0x68, 0x26, 0xee, 0x59, 0x49,
+ 0xdb, 0xed, 0xc0, 0xf7, 0xf9, 0xc6, 0x27, 0x2d, 0x09, 0x61, 0xe8, 0xed,
+ 0xf0, 0xbd, 0x71, 0x01, 0x44, 0x81, 0x80, 0xd4, 0x24, 0xc3, 0xb8, 0x8f,
+ 0x6e, 0x1a, 0x82, 0x5a, 0x3e, 0xab, 0x78, 0x80, 0x3b, 0x47, 0x48, 0x67,
+ 0xf9, 0x7d, 0x01, 0x38, 0xc7, 0x63, 0x03, 0xf3, 0xe0, 0xfb, 0xab, 0xbe,
+ 0xfb, 0x7d, 0xdf, 0x73, 0x6f, 0x3a, 0x52, 0x49, 0x68, 0xdc, 0x7e, 0x5e,
+ 0x1d, 0xcd, 0xc1, 0xdd, 0x57, 0xae, 0xc0, 0x5b, 0xa4, 0xa0, 0x25, 0xc5,
+ 0x12, 0x3a, 0xd0, 0x1c, 0xca, 0x0e, 0xf7, 0x8a, 0x43, 0x43, 0xa3, 0x71,
+ 0xc4, 0x6b, 0xd2, 0x26, 0x76, 0x7d, 0x53, 0xe3, 0x79, 0x9e, 0xb9, 0xeb,
+ 0x12, 0x9c, 0x8d, 0xff, 0xfb, 0x6c, 0xbf, 0xfa, 0xb6, 0xc3, 0xfd, 0xef,
+ 0x64, 0x3d, 0xe4, 0xbd, 0x6d, 0xce, 0x2b, 0x3a, 0x5c, 0x09, 0x62, 0x7f,
+ 0x20, 0x62, 0x3c, 0x84, 0x34, 0xb3, 0x6c, 0x69, 0x1b, 0x00, 0xfa, 0xe6,
+ 0x86, 0x66, 0x46, 0x73, 0xfa, 0xf8, 0x07, 0xc7, 0x5e, 0xb5, 0x3c, 0x6a,
+ 0x53, 0xc7, 0x2b, 0x11, 0x40, 0x3e, 0xbe, 0x47, 0x04, 0x6b, 0xdd, 0x3f,
+ 0xd4, 0xfe, 0x02, 0xfa, 0x09, 0x33, 0xa8, 0xb0, 0x47, 0xfb, 0x21, 0x82,
+ 0x5f, 0xd9, 0x77, 0xcf, 0xaf, 0xa3, 0x06, 0x70, 0x1c, 0x82, 0x3f, 0xd9,
+ 0x0c, 0x12, 0xfe, 0xd3, 0x74, 0x80, 0x9c, 0x10, 0xc1, 0x81, 0x23, 0xf7,
+ 0x11, 0xbd, 0x00, 0x30, 0x24, 0xf1, 0x17, 0x8d, 0xee, 0x70, 0x60, 0x05,
+ 0xc0, 0x21, 0x26, 0x95, 0x83, 0x71, 0x41, 0x85, 0x15, 0xf3, 0x80, 0x52,
+ 0x5a, 0x50, 0x5b, 0x0f, 0x19, 0x83, 0x0b, 0x65, 0x2f, 0x64, 0xf4, 0x73,
+ 0x3e, 0xd7, 0x7c, 0x9a, 0x9d, 0x8a, 0x2d, 0xd7, 0x86, 0x77, 0x10, 0xd7,
+ 0x9b, 0x21, 0x80, 0x27, 0x2c, 0xa4, 0x96, 0x05, 0x39, 0x34, 0x07, 0x41,
+ 0x84, 0xde, 0x4d, 0x6c, 0x94, 0xe2, 0xf9, 0x41, 0x85, 0x37, 0x7c, 0x5e,
+ 0x35, 0x39, 0x38, 0x61, 0xed, 0xdd, 0xec, 0x00, 0x1e, 0x00, 0x60, 0x80,
+ 0x18, 0xe2, 0xfa, 0x51, 0xd2, 0x30, 0xcc, 0x47, 0xb9, 0x08, 0x64, 0xd2,
+ 0x53, 0x2d, 0xab, 0x4e, 0xe7, 0x37, 0x7c, 0x1d, 0xc7, 0x72, 0x27, 0xb8,
+ 0x84, 0x24, 0x9e, 0x81, 0xc7, 0x08, 0x1c, 0x1c, 0x40, 0xaf, 0x21, 0xfe,
+ 0x94, 0x10, 0x8b, 0x3b, 0xa7, 0x37, 0x0a, 0x7b, 0xa0, 0x19, 0xd8, 0x35,
+ 0x02, 0xdf, 0x13, 0x9c, 0x89, 0x57, 0x41, 0x29, 0x2c, 0x67, 0xb5, 0xeb,
+ 0xd5, 0x47, 0x28, 0x01, 0xe9, 0x4d, 0x83, 0x09, 0xb8, 0x0f, 0x24, 0x94,
+ 0x93, 0xc9, 0x05, 0xf6, 0xc1, 0x45, 0xb1, 0xe7, 0x6f, 0xee, 0xfb, 0xde,
+ 0x34, 0x03, 0x32, 0x80, 0xc8, 0x0c, 0x08, 0x68, 0xca, 0xe4, 0xa4, 0xb7,
+ 0xc7, 0xef, 0xb3, 0xef, 0x83, 0xef, 0x6f, 0xb2, 0x53, 0xb7, 0xc8, 0x4f,
+ 0xdf, 0x3e, 0x57, 0x36, 0xe9, 0x02, 0xa5, 0x38, 0xc4, 0xe1, 0x1c, 0x3a,
+ 0x60, 0x51, 0x2a, 0xfb, 0xda, 0x3c, 0xef, 0x02, 0x51, 0x60, 0x49, 0x21,
+ 0x5e, 0x7e, 0x50, 0x6a, 0xc0, 0xb1, 0x7d, 0x87, 0x21, 0x3f, 0x85, 0x6d,
+ 0xff, 0xbb, 0xc9, 0x49, 0x1d, 0xee, 0x21, 0xb8, 0x7e, 0xb8, 0xf7, 0x97,
+ 0x00, 0x70, 0xe0, 0x60, 0x9a, 0xee, 0x08, 0x7f, 0x6a, 0x70, 0x47, 0x04,
+ 0x28, 0xe1, 0x44, 0x57, 0xbd, 0x08, 0x60, 0x09, 0x80, 0x76, 0x43, 0x61,
+ 0x85, 0x72, 0xb7, 0x3d, 0x21, 0xbb, 0xa1, 0x81, 0x20, 0x0f, 0xb1, 0x7f,
+ 0x8a, 0x6d, 0x85, 0xeb, 0xc2, 0x10, 0x88, 0x48, 0x02, 0x81, 0x88, 0x0c,
+ 0x41, 0xd8, 0xd1, 0xff, 0xf7, 0xff, 0xb3, 0x67, 0xfd, 0x7e, 0xf3, 0x08,
+ 0x41, 0x9d, 0x0f, 0xfa, 0x9f, 0x33, 0x5e, 0x81, 0x0c, 0xbd, 0xf3, 0x65,
+ 0xdf, 0xa4, 0x00, 0x66, 0xa0, 0x03, 0xee, 0x00, 0x29, 0x4e, 0x13, 0xb9,
+ 0x49, 0xec, 0x17, 0xb7, 0x0e, 0xbd, 0x2b, 0xdc, 0xa0, 0x07, 0x64, 0xdc,
+ 0x1a, 0x43, 0x28, 0xac, 0xbc, 0x4c, 0xdd, 0x3c, 0xf2, 0x4a, 0x73, 0x61,
+ 0x44, 0x87, 0x71, 0x3a, 0xee, 0xbc, 0xc0, 0x77, 0x82, 0x76, 0x1c, 0x85,
+ 0xd8, 0x10, 0x94, 0x55, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf7, 0x6b, 0x5c,
+ 0xd7, 0x4b, 0xce, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad,
+ 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0x5d,
+ 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe7,
+ 0x7a, 0x1a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e,
+ 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4,
+ 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa5, 0xa2, 0xea, 0x79, 0x9e,
+ 0xb9, 0xe7, 0x7a, 0x1a, 0x77, 0x4b, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3,
+ 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d, 0xd2, 0xf3, 0x3d, 0x73,
+ 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x9d, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75,
+ 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x17, 0x4b, 0xce, 0xf5, 0xaf, 0x3b,
+ 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3,
+ 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1,
+ 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe7, 0x7a,
+ 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x45,
+ 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e,
+ 0x77, 0xa5, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9,
+ 0xe6, 0x7a, 0xca, 0x52, 0xd3, 0xb5, 0x79, 0xa9, 0x49, 0x45, 0xca, 0x52,
+ 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x00, 0x00, 0x00,
+ 0x01, 0x06, 0x43, 0xf1, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x7e, 0xc4, 0xd7,
+ 0xcf, 0x5d, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0xde,
+ 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd2, 0xd3,
+ 0xad, 0xf9, 0x04, 0xce, 0xdf, 0xe0, 0xfc, 0x45, 0x27, 0xac, 0x3e, 0xb4,
+ 0x9b, 0x99, 0x3b, 0x8f, 0x57, 0x23, 0xe2, 0x2d, 0xd3, 0x71, 0x9f, 0x71,
+ 0xc4, 0x63, 0x48, 0x21, 0xd7, 0x94, 0x0d, 0x40, 0xd4, 0x7c, 0x69, 0x82,
+ 0x71, 0xd7, 0xcd, 0x60, 0x9c, 0x4e, 0xbd, 0x9e, 0xaf, 0x54, 0x7a, 0x97,
+ 0xa1, 0x48, 0x00, 0xb4, 0x9a, 0x18, 0x18, 0x3f, 0x8d, 0x4a, 0xd8, 0x20,
+ 0xa6, 0xfc, 0x4e, 0xfd, 0x91, 0xf0, 0xe4, 0xda, 0xd0, 0x58, 0xd4, 0x64,
+ 0xe7, 0x5e, 0xdd, 0x67, 0xae, 0xf3, 0xe0, 0x0f, 0x8a, 0x72, 0x59, 0x34,
+ 0x7f, 0x25, 0x21, 0x6a, 0x64, 0x3b, 0xfd, 0x8c, 0xfd, 0x2f, 0xb8, 0xf1,
+ 0x9a, 0xf4, 0x40, 0xa0, 0x60, 0x17, 0x58, 0x1e, 0x58, 0x7d, 0xc4, 0x4d,
+ 0x2d, 0x8a, 0x13, 0x7c, 0xa0, 0x13, 0x93, 0x40, 0x2c, 0x21, 0x16, 0x9c,
+ 0x7b, 0x32, 0x30, 0xf4, 0xf5, 0x6c, 0xb1, 0xfe, 0xf7, 0x28, 0xc4, 0xa0,
+ 0xcc, 0x83, 0xfe, 0xcb, 0xec, 0xcf, 0xcd, 0xec, 0x2f, 0xaf, 0xdc, 0x80,
+ 0x07, 0xa1, 0xa4, 0xb2, 0x68, 0x61, 0x33, 0x1c, 0x80, 0x14, 0xa5, 0x4e,
+ 0x29, 0x18, 0x52, 0xc2, 0x77, 0x01, 0xe5, 0xd8, 0xfe, 0xf3, 0xc0, 0xaf,
+ 0x59, 0x45, 0x00, 0x8f, 0x89, 0xf7, 0x41, 0x31, 0x0c, 0x57, 0xe3, 0xef,
+ 0xe6, 0x97, 0xd3, 0x11, 0xc0, 0x72, 0x08, 0xff, 0x84, 0xd0, 0x4b, 0xfc,
+ 0x4d, 0xf3, 0x4b, 0xea, 0x3b, 0xac, 0xb0, 0x47, 0xfc, 0x26, 0x82, 0x5f,
+ 0xe2, 0x68, 0x02, 0xab, 0x0c, 0x01, 0x1e, 0x23, 0x7b, 0x60, 0x1d, 0x12,
+ 0x78, 0x8b, 0xcf, 0xca, 0x01, 0xbe, 0x4a, 0x3b, 0xe4, 0x0c, 0x4f, 0x0d,
+ 0x4e, 0x01, 0x21, 0x5b, 0x71, 0xa9, 0x16, 0xd8, 0xec, 0xcb, 0xff, 0x6b,
+ 0xe9, 0xc0, 0x54, 0x01, 0xb9, 0x41, 0xa9, 0x0d, 0x0d, 0xc4, 0xc0, 0x2e,
+ 0x92, 0xd3, 0xf3, 0x99, 0xbf, 0xff, 0x28, 0xe7, 0x61, 0xd7, 0x89, 0x00,
+ 0x6e, 0x05, 0x4b, 0x00, 0xd0, 0x01, 0xd0, 0x40, 0x42, 0x40, 0x35, 0x4b,
+ 0x3e, 0x3c, 0x02, 0xc2, 0x97, 0x97, 0xbf, 0x24, 0xfe, 0xfb, 0x81, 0x24,
+ 0xdf, 0x6b, 0xbe, 0x60, 0x03, 0x04, 0x24, 0x06, 0x01, 0x8a, 0xff, 0x74,
+ 0xaf, 0xc1, 0x35, 0x8b, 0xeb, 0x32, 0xbc, 0xfc, 0xd6, 0x4c, 0xe0, 0x16,
+ 0x90, 0x80, 0x7a, 0x82, 0x21, 0x07, 0x5b, 0xca, 0x01, 0xca, 0x04, 0xf2,
+ 0x37, 0xbd, 0x80, 0x06, 0x45, 0xf0, 0x32, 0x30, 0x7e, 0x23, 0x5f, 0x1c,
+ 0x49, 0x4c, 0x1a, 0xe6, 0x33, 0x87, 0x9c, 0x46, 0xba, 0x13, 0x9d, 0x70,
+ 0x65, 0x63, 0x8d, 0x24, 0x8e, 0x23, 0xda, 0x03, 0x3e, 0xe5, 0x21, 0xbe,
+ 0x3b, 0x4c, 0x43, 0x48, 0xc0, 0xc2, 0xf8, 0xed, 0xef, 0xaa, 0x64, 0x6d,
+ 0x99, 0x6a, 0x1d, 0xfe, 0x22, 0xde, 0xab, 0xed, 0x96, 0xf6, 0xe8, 0x43,
+ 0xd5, 0x3f, 0x1c, 0x7c, 0x3f, 0x49, 0x7d, 0x98, 0x95, 0xb0, 0x1f, 0x0d,
+ 0x04, 0x9f, 0xb0, 0xd0, 0x01, 0x8d, 0xc2, 0x01, 0xd0, 0x05, 0xe5, 0x80,
+ 0xe9, 0x3b, 0x06, 0xfe, 0x59, 0x33, 0x9e, 0x9e, 0x49, 0x0c, 0x16, 0x2d,
+ 0x03, 0xcd, 0xc7, 0xae, 0xac, 0x92, 0x92, 0xad, 0xfe, 0xe2, 0xec, 0xd2,
+ 0x4c, 0xfb, 0x56, 0xfe, 0x09, 0x25, 0xa1, 0x07, 0xf6, 0xbd, 0x59, 0x60,
+ 0x55, 0xd4, 0x80, 0xbe, 0x03, 0x5b, 0xc9, 0xa5, 0x18, 0xbc, 0xde, 0xfa,
+ 0x71, 0x68, 0xe5, 0x76, 0xe1, 0xdc, 0x78, 0x0b, 0xe8, 0x76, 0xab, 0x35,
+ 0x53, 0xab, 0x29, 0xcd, 0xff, 0xbf, 0xfc, 0xff, 0xc7, 0xfa, 0x3d, 0x6b,
+ 0xcc, 0xf7, 0xab, 0xdf, 0xf7, 0xf7, 0x93, 0x75, 0x3c, 0xef, 0x5a, 0xf3,
+ 0x3c, 0x9a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd1, 0x75, 0x3c,
+ 0xcf, 0x5c, 0xf3, 0x3d, 0x2d, 0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x9d, 0xe8,
+ 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0xde,
+ 0xb1, 0xe7, 0x7a, 0x5a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3,
+ 0xba, 0x5e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x8b, 0xa9, 0xe7, 0x7a, 0xd7,
+ 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9,
+ 0x79, 0xde, 0xb5, 0xe7, 0x7a, 0x1a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33,
+ 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86, 0x9d, 0xd2, 0xf3,
+ 0xbd, 0x6b, 0xce, 0xf4, 0x34, 0x5d, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x4b,
+ 0x4e, 0xea, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5,
+ 0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d,
+ 0xd2, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x9e,
+ 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x2d, 0x17, 0x53,
+ 0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x78, 0xd2,
+ 0xd3, 0xba, 0xde, 0x67, 0x9e, 0x94, 0xce, 0xc4, 0x69, 0x49, 0x45, 0xca,
+ 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94,
+ 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x40, 0x00, 0x00,
+ 0x01, 0x07, 0x4b, 0xf3, 0x2f, 0x33, 0xd6, 0xbc, 0xef, 0x7e, 0xb2, 0xd7,
+ 0x83, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa5, 0xe7, 0x7a,
+ 0xd7, 0x9d, 0xe9, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e,
+ 0xd3, 0xb7, 0xdf, 0x76, 0xfb, 0xab, 0xee, 0x77, 0xbc, 0x80, 0x06, 0x41,
+ 0xa0, 0x64, 0x85, 0xb7, 0x3c, 0xae, 0x4a, 0xef, 0xf7, 0xea, 0x3b, 0xf6,
+ 0x35, 0x28, 0x6e, 0x1f, 0x7b, 0x47, 0xb9, 0x40, 0x19, 0x06, 0x92, 0x88,
+ 0x63, 0x3f, 0x50, 0x6e, 0x6d, 0x80, 0x7a, 0x87, 0xea, 0x12, 0x87, 0x71,
+ 0x1a, 0xf3, 0x40, 0xa7, 0x6f, 0xf0, 0xf2, 0x3e, 0xbb, 0x48, 0x68, 0x74,
+ 0x38, 0xea, 0xe3, 0x7a, 0x70, 0xc4, 0xfd, 0x87, 0xbe, 0x0f, 0x03, 0xb7,
+ 0x83, 0xd8, 0x62, 0x36, 0x1d, 0xad, 0xa9, 0x28, 0x69, 0x68, 0x48, 0xe7,
+ 0x0f, 0xb4, 0x90, 0xd2, 0xc8, 0x17, 0xd5, 0x79, 0x3b, 0x5e, 0x8b, 0xbb,
+ 0x55, 0x3e, 0xc3, 0x86, 0x80, 0xdc, 0xb2, 0xc3, 0x40, 0x25, 0x4f, 0x65,
+ 0x01, 0xec, 0xac, 0x27, 0x75, 0xa7, 0x09, 0x8b, 0xd7, 0xab, 0x66, 0xca,
+ 0xd8, 0xc8, 0x1f, 0x7a, 0xff, 0x92, 0xcb, 0x71, 0xfe, 0xf2, 0x19, 0x90,
+ 0xc2, 0xaf, 0xe4, 0xf7, 0xde, 0x40, 0x4e, 0x43, 0x0d, 0x03, 0x21, 0xa1,
+ 0x85, 0x27, 0x14, 0x12, 0x5f, 0x4a, 0x59, 0xd2, 0xb0, 0xd2, 0xd4, 0x83,
+ 0x31, 0xc7, 0xf1, 0xea, 0xbe, 0x0f, 0x7d, 0x30, 0x10, 0x46, 0x8b, 0x48,
+ 0x15, 0x26, 0x96, 0x94, 0x95, 0x90, 0x1b, 0xdd, 0x5d, 0x3d, 0x2e, 0x37,
+ 0xf5, 0xee, 0x8d, 0xb2, 0xbb, 0x2b, 0x99, 0x7c, 0xb0, 0x09, 0x80, 0x2c,
+ 0xe1, 0xb8, 0x02, 0xd2, 0x46, 0xef, 0x7b, 0xa3, 0x7a, 0x32, 0x58, 0xfa,
+ 0xf3, 0xd6, 0xb0, 0x0d, 0xd9, 0x09, 0xe0, 0x2a, 0xe3, 0x80, 0xe5, 0xf4,
+ 0x94, 0xa3, 0xa3, 0xfd, 0xcc, 0x55, 0xf0, 0xa0, 0x03, 0xce, 0x3f, 0x5c,
+ 0x20, 0x03, 0xc2, 0x11, 0xfd, 0x0f, 0xc4, 0xf6, 0xbe, 0x5c, 0xbe, 0x51,
+ 0x9e, 0xd3, 0x57, 0xff, 0x58, 0x01, 0xd7, 0x25, 0x7c, 0x5f, 0x17, 0xaf,
+ 0x90, 0xa0, 0x87, 0x8b, 0x4f, 0xc7, 0x0e, 0x89, 0x34, 0x98, 0x35, 0x86,
+ 0x66, 0xd8, 0xca, 0xdc, 0x4c, 0x28, 0x31, 0x69, 0x5f, 0xc1, 0x5a, 0xcc,
+ 0x0b, 0x74, 0x0b, 0xad, 0x6f, 0x54, 0xa0, 0x03, 0x72, 0x68, 0xc2, 0x90,
+ 0x19, 0xba, 0xcb, 0x4e, 0x70, 0x15, 0x21, 0x7c, 0x2c, 0xe0, 0xfb, 0xdb,
+ 0xbc, 0xa4, 0x32, 0x1f, 0x01, 0xd9, 0x37, 0x93, 0x7b, 0xe7, 0x52, 0x37,
+ 0x03, 0xc8, 0x20, 0xf3, 0xf8, 0x76, 0x32, 0xd2, 0xf7, 0xe8, 0xe4, 0xdf,
+ 0xbf, 0x6c, 0x72, 0x15, 0x78, 0x41, 0x88, 0x19, 0xae, 0xa7, 0x99, 0xeb,
+ 0x9e, 0x67, 0xba, 0x5a, 0xc2, 0xb4, 0xf1, 0x00, 0x3a, 0x0d, 0x01, 0x27,
+ 0x01, 0x11, 0x00, 0x81, 0x79, 0xc2, 0x68, 0x60, 0x19, 0x43, 0x1a, 0x67,
+ 0x17, 0x7d, 0x75, 0xcd, 0xc7, 0xe5, 0xe1, 0x37, 0xc8, 0x09, 0x81, 0xb8,
+ 0x35, 0x0e, 0x1d, 0xc4, 0xde, 0x7d, 0xea, 0x27, 0x35, 0x55, 0xf5, 0xbe,
+ 0xf9, 0xf7, 0xdf, 0x7c, 0xaf, 0xb9, 0xfb, 0xe3, 0xf4, 0xef, 0x78, 0xd0,
+ 0x07, 0x20, 0x3a, 0x19, 0xc0, 0x2d, 0x46, 0x3d, 0xb1, 0x6d, 0x8d, 0x3b,
+ 0x24, 0x89, 0xc3, 0x85, 0xde, 0xe9, 0xed, 0xfa, 0x1a, 0xe1, 0x42, 0x46,
+ 0xeb, 0xa5, 0xe7, 0x7a, 0xd7, 0x9d, 0xe6, 0xd6, 0x77, 0x4b, 0xce, 0xf5,
+ 0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d,
+ 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e,
+ 0x77, 0xa5, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x17, 0x53,
+ 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e,
+ 0x96, 0x9d, 0xd4, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99,
+ 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d,
+ 0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6,
+ 0xbc, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee,
+ 0xa7, 0x99, 0xeb, 0x9e, 0x67, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3,
+ 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f,
+ 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a,
+ 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xb1, 0xa5, 0xa2, 0xeb, 0x79, 0x9e, 0x6a,
+ 0x4a, 0x77, 0x29, 0x4a, 0x4a, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0x80, 0x00, 0x00, 0x01, 0x08, 0x53, 0xe4, 0x3c, 0xef,
+ 0x5a, 0xf3, 0x3d, 0xfa, 0x83, 0x5e, 0x25, 0xd4, 0xf3, 0x3d, 0x6b, 0xce,
+ 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc,
+ 0xef, 0x69, 0x49, 0xc1, 0x41, 0xf9, 0xd7, 0x8c, 0x67, 0xca, 0x73, 0x2d,
+ 0x3c, 0x6a, 0x0b, 0x42, 0x3a, 0x73, 0xaf, 0xf0, 0x1e, 0xa0, 0xed, 0xc7,
+ 0x39, 0xc9, 0x53, 0x29, 0x2c, 0x65, 0xc4, 0x51, 0x30, 0x6a, 0x3f, 0x7c,
+ 0xa3, 0xed, 0x7a, 0x5b, 0xb5, 0xf3, 0xde, 0x61, 0xbd, 0x1f, 0x7c, 0x7e,
+ 0xfb, 0xe3, 0x17, 0x96, 0xb1, 0x43, 0xed, 0x43, 0x06, 0x97, 0xca, 0x61,
+ 0xa4, 0x95, 0x30, 0xd7, 0x00, 0xa3, 0xa4, 0xef, 0xbe, 0xed, 0xce, 0x54,
+ 0xe4, 0x81, 0x9b, 0xab, 0x8c, 0xef, 0x8e, 0x61, 0x9d, 0x95, 0x9b, 0x33,
+ 0xee, 0x3b, 0x65, 0x3e, 0xcb, 0xb9, 0x30, 0x14, 0xf9, 0x6e, 0xbd, 0xed,
+ 0x49, 0x6e, 0xaa, 0xf7, 0xe7, 0x00, 0x37, 0x28, 0x7e, 0xbd, 0x70, 0x6a,
+ 0x72, 0x52, 0xa5, 0xec, 0x47, 0xba, 0xe9, 0xcc, 0x93, 0x42, 0xb6, 0x23,
+ 0x4e, 0xc4, 0x63, 0xc3, 0x88, 0x26, 0x5e, 0x5b, 0x5d, 0x95, 0x55, 0x2f,
+ 0x11, 0xac, 0xbb, 0x5a, 0x12, 0x35, 0x1b, 0x8f, 0xdb, 0x85, 0xd4, 0x96,
+ 0x8e, 0x82, 0x5f, 0xe8, 0x6c, 0x4a, 0x42, 0x50, 0x96, 0xc8, 0x74, 0xed,
+ 0x94, 0xf8, 0xa0, 0x97, 0xcf, 0x97, 0xff, 0x37, 0x1d, 0x7a, 0xa1, 0x84,
+ 0xce, 0x97, 0x15, 0x71, 0x25, 0x2c, 0x1f, 0x5c, 0x7c, 0xd8, 0x02, 0xd0,
+ 0xc0, 0x27, 0x82, 0xf4, 0xa0, 0x37, 0x0c, 0x02, 0x78, 0x2f, 0x5f, 0x54,
+ 0xea, 0xce, 0x24, 0x89, 0x64, 0x4e, 0x6c, 0xe7, 0x2c, 0xdc, 0xc0, 0x6c,
+ 0x4d, 0xe5, 0x30, 0xab, 0xd0, 0x6a, 0xbf, 0xe6, 0xc0, 0x07, 0xa4, 0x20,
+ 0x45, 0xfc, 0x40, 0x5e, 0xad, 0xe0, 0x27, 0x21, 0x60, 0x12, 0xa3, 0x88,
+ 0xee, 0x46, 0xbe, 0x9b, 0xca, 0x01, 0x42, 0x3e, 0x4a, 0xfa, 0x0a, 0xe9,
+ 0x14, 0xfb, 0x2f, 0x61, 0xce, 0x78, 0x1b, 0xb5, 0xa7, 0xab, 0x88, 0xab,
+ 0xb2, 0x19, 0x44, 0xfa, 0x72, 0x37, 0xaf, 0xbe, 0x7b, 0xc0, 0xae, 0x33,
+ 0x7e, 0x4e, 0xbd, 0x8a, 0x53, 0xd2, 0x78, 0x55, 0x52, 0x76, 0xce, 0xc6,
+ 0x71, 0xd9, 0x41, 0x56, 0xaf, 0x82, 0x1d, 0x66, 0x99, 0x87, 0x8f, 0x3e,
+ 0xcd, 0x74, 0x55, 0x57, 0xbc, 0xcf, 0x56, 0x82, 0x93, 0xd0, 0x35, 0x08,
+ 0x25, 0x81, 0x76, 0xfb, 0xed, 0xce, 0xdb, 0xe3, 0xd1, 0xd8, 0xd5, 0xe6,
+ 0xe2, 0xd5, 0x5e, 0x19, 0x89, 0x68, 0x2d, 0xb3, 0xfd, 0xbb, 0x65, 0x25,
+ 0xf0, 0xd6, 0x01, 0xee, 0xea, 0x1e, 0xea, 0xff, 0xb2, 0xae, 0xc0, 0x0b,
+ 0x0e, 0x59, 0xf7, 0x38, 0xd3, 0xee, 0x3c, 0xcf, 0x63, 0x42, 0x30, 0x0d,
+ 0xc0, 0xa2, 0x3a, 0x18, 0x97, 0xf0, 0xd2, 0x94, 0xa6, 0x2f, 0x20, 0xe3,
+ 0xb6, 0xe8, 0x6e, 0x31, 0xa6, 0x26, 0x94, 0x18, 0x51, 0x48, 0x25, 0x7d,
+ 0xc0, 0xba, 0x50, 0xf9, 0x05, 0x21, 0x94, 0x48, 0xf8, 0xa5, 0x3a, 0x9f,
+ 0x7e, 0x22, 0x90, 0x0b, 0x0f, 0x15, 0x50, 0xd3, 0xaf, 0x63, 0xdc, 0x80,
+ 0x10, 0x00, 0x9c, 0x00, 0xf0, 0x35, 0xff, 0x02, 0xc9, 0x51, 0x62, 0x87,
+ 0x24, 0x38, 0xd1, 0x42, 0x2b, 0x80, 0xc0, 0x46, 0x74, 0x36, 0xe4, 0x20,
+ 0xc4, 0xe4, 0xb6, 0x40, 0x17, 0x25, 0x6e, 0x78, 0xc7, 0x4b, 0x9d, 0xdf,
+ 0xf3, 0xff, 0x32, 0x25, 0x13, 0x06, 0x90, 0x89, 0x79, 0x09, 0xfc, 0x6a,
+ 0x70, 0x40, 0x61, 0x49, 0x03, 0xc8, 0x35, 0x39, 0xf0, 0x75, 0x8e, 0x26,
+ 0x32, 0x4f, 0xaf, 0xfd, 0xf5, 0x77, 0xf3, 0x2e, 0x1a, 0x19, 0xf3, 0x0d,
+ 0xc1, 0x7a, 0xfa, 0xef, 0x6d, 0xfe, 0xdd, 0xf0, 0xfa, 0xb2, 0xb6, 0xfb,
+ 0xbf, 0x02, 0x4b, 0x6d, 0xcf, 0x71, 0xf6, 0x84, 0xa9, 0x3e, 0x8f, 0x95,
+ 0x5e, 0xaa, 0xae, 0x79, 0x9e, 0xb9, 0xe6, 0x7b, 0xb5, 0xae, 0x5b, 0xa5,
+ 0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6, 0xbc, 0xcf,
+ 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a, 0x5a, 0x2e, 0xa7, 0x99,
+ 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0x3d, 0x0d,
+ 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6,
+ 0xbc, 0xef, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x5a, 0x77,
+ 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79,
+ 0x9e, 0x86, 0x8b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f,
+ 0x3b, 0xd6, 0xbc, 0xef, 0x4b, 0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a,
+ 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67,
+ 0xad, 0x79, 0xde, 0x96, 0x9d, 0xd4, 0xf3, 0x3d, 0x64, 0x69, 0x68, 0xb4,
+ 0x79, 0xa3, 0x4a, 0x67, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17,
+ 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca,
+ 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94,
+ 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29,
+ 0x11, 0x72, 0x94, 0xa4, 0x44, 0x00, 0x00, 0x01, 0x09, 0x53, 0xa1, 0xe6,
+ 0x7a, 0xd7, 0x9d, 0xef, 0xd2, 0x1a, 0xf1, 0xee, 0x97, 0x9d, 0xeb, 0x5e,
+ 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9,
+ 0xed, 0x08, 0x46, 0x41, 0xc1, 0x46, 0x6c, 0x3b, 0x0a, 0xa3, 0xcc, 0xf6,
+ 0x59, 0xf0, 0x7c, 0xed, 0xf1, 0x5b, 0x66, 0xca, 0x5b, 0x88, 0x1c, 0xc6,
+ 0x28, 0xea, 0xc4, 0x14, 0x84, 0x66, 0x3c, 0xf3, 0xf6, 0x34, 0xfc, 0xdb,
+ 0x6c, 0xb3, 0x54, 0xba, 0xa7, 0x9d, 0xec, 0x46, 0xa5, 0x99, 0xfa, 0xeb,
+ 0x6d, 0x76, 0xe2, 0x99, 0x23, 0x46, 0xff, 0xc4, 0x21, 0x62, 0x7e, 0x46,
+ 0xe4, 0xf8, 0xf2, 0xb7, 0xee, 0xfc, 0x42, 0x8d, 0x3d, 0x6c, 0xf8, 0x3b,
+ 0xea, 0xb7, 0x9d, 0xec, 0xc9, 0x69, 0x77, 0x7e, 0xe7, 0x4d, 0xb5, 0xd4,
+ 0xf6, 0xb2, 0x56, 0x0e, 0x66, 0x66, 0x43, 0x30, 0xe3, 0x9d, 0x85, 0x3d,
+ 0x5b, 0xcc, 0xf1, 0xbf, 0x41, 0xdd, 0x9e, 0x47, 0xd8, 0x65, 0xbd, 0x68,
+ 0xcc, 0xe3, 0xce, 0xcc, 0xa3, 0x0e, 0x5b, 0x3f, 0x61, 0x7e, 0x0f, 0x33,
+ 0xd1, 0x92, 0x9c, 0x6f, 0xa9, 0xb5, 0xbf, 0x2b, 0x2f, 0xaf, 0x7e, 0xa7,
+ 0x14, 0xb5, 0x08, 0x5f, 0xa9, 0x7a, 0xe7, 0x99, 0xe4, 0xff, 0x9f, 0xe2,
+ 0xd4, 0x3f, 0x81, 0x17, 0x62, 0x75, 0x6a, 0x50, 0xea, 0x75, 0x2d, 0x4c,
+ 0x8e, 0x79, 0xe7, 0xa3, 0xc1, 0xe6, 0x7b, 0x2c, 0x94, 0xe3, 0x66, 0xda,
+ 0xd7, 0x81, 0xb3, 0xbb, 0x89, 0x7d, 0xe2, 0x1f, 0xc4, 0x89, 0xac, 0x79,
+ 0x9e, 0x4f, 0xc7, 0x9d, 0x3b, 0x53, 0x18, 0x0e, 0x11, 0x9b, 0x4c, 0x1f,
+ 0xcf, 0x7e, 0x2f, 0xaf, 0xac, 0x3f, 0xce, 0xf3, 0x3d, 0x0f, 0xc7, 0xf9,
+ 0xdd, 0x7f, 0xf1, 0xe2, 0x8e, 0xc7, 0x0e, 0x76, 0x72, 0x7c, 0xcf, 0xce,
+ 0x75, 0x0e, 0x64, 0x75, 0x8f, 0x5e, 0xf5, 0x8f, 0x3b, 0xd0, 0xe9, 0x1f,
+ 0x36, 0xd6, 0x86, 0x35, 0x1e, 0xb5, 0xe6, 0x79, 0x2e, 0x2e, 0xa7, 0x99,
+ 0xeb, 0x9e, 0x67, 0x86, 0x8b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe9, 0x69,
+ 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73,
+ 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x9e, 0x67, 0xa5, 0xa7, 0x75,
+ 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x99,
+ 0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79,
+ 0x9e, 0xb9, 0xe6, 0x7a, 0x5a, 0x2e, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1,
+ 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa5, 0xe7, 0x7a,
+ 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0x68, 0x69, 0xdd,
+ 0x6f, 0x33, 0xd6, 0x52, 0x99, 0xda, 0xbc, 0xd4, 0xa4, 0xa2, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00,
+ 0x01, 0x0a, 0x53, 0xd0, 0xf3, 0xbd, 0x6b, 0xcc, 0xf7, 0xdf, 0xb5, 0xe5,
+ 0x5d, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x45, 0xd2, 0xf3, 0xbd, 0x6b,
+ 0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75,
+ 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99,
+ 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79,
+ 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2,
+ 0xd1, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xad, 0xe6, 0x7a,
+ 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x4b, 0x4e,
+ 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf,
+ 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x77, 0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9,
+ 0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef,
+ 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x5a, 0x77, 0x4b, 0xce,
+ 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86,
+ 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb,
+ 0x5e, 0x77, 0xa1, 0xa2, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77,
+ 0x53, 0xcc, 0xf5, 0xcf, 0x1a, 0x5a, 0x77, 0x5b, 0xcc, 0xf3, 0xd2, 0x99,
+ 0xd8, 0x8d, 0x29, 0x28, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x20, 0x00, 0x00, 0x01, 0x0b, 0x53, 0xeb, 0xbc, 0xef,
+ 0x5a, 0xf3, 0xbd, 0xf6, 0xcd, 0x79, 0x97, 0x4b, 0xce, 0xf5, 0xaf, 0x33,
+ 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3,
+ 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa5,
+ 0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a,
+ 0xd7, 0x9d, 0xe8, 0x68, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d,
+ 0xd2, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e,
+ 0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x3b, 0xa5,
+ 0xe7, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf,
+ 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99,
+ 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d,
+ 0x3b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6,
+ 0xbc, 0xef, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x1a, 0x77,
+ 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x77, 0xac, 0x9a,
+ 0x86, 0x8b, 0xad, 0xe6, 0x79, 0xa9, 0x29, 0xd8, 0x8d, 0x29, 0x28, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00,
+ 0x01, 0x0c, 0x53, 0xf4, 0xef, 0x33, 0xd6, 0xbc, 0xef, 0x7d, 0x73, 0x5e,
+ 0x7d, 0xd2, 0xf3, 0xbd, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x99, 0xeb,
+ 0x9e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x58, 0xf3, 0xbd, 0x2d, 0x3b,
+ 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x68, 0xba, 0x5e, 0x77, 0xad, 0x79,
+ 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0xb4, 0xee, 0xa7,
+ 0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd,
+ 0x0d, 0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33,
+ 0xd7, 0x3c, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34,
+ 0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x67, 0xa5, 0xa7, 0x75, 0x3c, 0xef, 0x5a,
+ 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd,
+ 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe6,
+ 0x7a, 0x1a, 0x77, 0x53, 0xce, 0xf5, 0x91, 0xa5, 0xa2, 0xd1, 0xe6, 0x8d,
+ 0x29, 0x9d, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29,
+ 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44,
+ 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17,
+ 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca,
+ 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94,
+ 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29,
+ 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44,
+ 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17,
+ 0x29, 0x4a, 0x44, 0x40, 0x00, 0x00, 0x01, 0x0d, 0x53, 0xf6, 0xcf, 0x3b,
+ 0xd6, 0x3c, 0xef, 0x7d, 0x2b, 0x5c, 0x57, 0x53, 0xcc, 0xf5, 0xaf, 0x3b,
+ 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9, 0xe7,
+ 0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43,
+ 0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe7, 0x7a, 0x5a, 0x77, 0x4b, 0xce, 0xf5,
+ 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d,
+ 0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e,
+ 0x77, 0xa1, 0xa2, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53,
+ 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e,
+ 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x9d,
+ 0xeb, 0x29, 0x4c, 0xed, 0x5e, 0x31, 0xa4, 0xa2, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00, 0x01,
+ 0x0e, 0x53, 0xfa, 0x2b, 0xce, 0xf5, 0xaf, 0x33, 0xde, 0xe3, 0x5c, 0xb7,
+ 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd1, 0x75, 0x3c, 0xcf, 0x5a, 0xf3,
+ 0xbd, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f,
+ 0x3b, 0xd6, 0xbc, 0xcf, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a,
+ 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77,
+ 0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69,
+ 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5,
+ 0xe6, 0xa5, 0xa7, 0x75, 0x3c, 0xef, 0x3d, 0x29, 0x9d, 0x88, 0xd2, 0x92,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x88, 0x00, 0x00, 0x01, 0x0f, 0x53, 0xfa, 0xab, 0xcc, 0xf5, 0xaf, 0x3b,
+ 0xde, 0xa3, 0x5c, 0xf7, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba,
+ 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce,
+ 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc,
+ 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe9,
+ 0x68, 0xba, 0x9e, 0x67, 0xad, 0x78, 0xd0, 0xd3, 0xb4, 0x79, 0x9e, 0x6a,
+ 0x4a, 0x76, 0x23, 0x4a, 0x4a, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x97, 0xcf,
+ 0xca, 0x00, 0x58, 0x90, 0xd0, 0x1d, 0x00, 0x1f, 0xa0, 0x0a, 0x86, 0x01,
+ 0x81, 0x89, 0x4b, 0xb1, 0x6e, 0xc5, 0x3b, 0xa9, 0x9c, 0xcf, 0x6e, 0x58,
+ 0x6a, 0x03, 0x18, 0x6f, 0x67, 0xed, 0x84, 0xd3, 0xb7, 0xf7, 0xf4, 0x0a,
+ 0x5c, 0x22, 0x10, 0x0c, 0x0a, 0x21, 0x93, 0x40, 0x6c, 0x82, 0x1a, 0x1f,
+ 0xf4, 0x21, 0x1c, 0x61, 0x6c, 0x9e, 0x90, 0xe5, 0x31, 0xaf, 0x54, 0x01,
+ 0xa8, 0x06, 0x98, 0x03, 0x44, 0x92, 0xb9, 0x34, 0xa2, 0xcb, 0xcc, 0xe8,
+ 0x40, 0xd5, 0x38, 0xff, 0xf3, 0x07, 0x1e, 0xb9, 0x12, 0xd0, 0x34, 0x61,
+ 0xac, 0xec, 0xdc, 0xe8, 0x6e, 0x7d, 0xfa, 0x2a, 0x52, 0x96, 0x35, 0x5c,
+ 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0xf1, 0xa0, 0x18,
+ 0x00, 0x38, 0x48, 0x05, 0xe8, 0x4f, 0x28, 0x9a, 0x56, 0xe8, 0x61, 0xa8,
+ 0xf9, 0x2a, 0x77, 0x65, 0xb3, 0x73, 0xad, 0x8b, 0x2d, 0x01, 0x98, 0x6a,
+ 0x7b, 0x61, 0xf4, 0xec, 0xf7, 0xec, 0xa9, 0x72, 0x80, 0x62, 0x05, 0x10,
+ 0x03, 0xa2, 0xf9, 0x40, 0x67, 0x16, 0x94, 0x25, 0x28, 0x4b, 0x64, 0xe6,
+ 0xe8, 0xec, 0x6e, 0xce, 0x1d, 0x71, 0x09, 0x80, 0x0f, 0x31, 0x34, 0x07,
+ 0x5c, 0x07, 0x60, 0x15, 0x86, 0x20, 0xa4, 0x29, 0x23, 0x72, 0x5f, 0x25,
+ 0xb7, 0x18, 0x72, 0xfa, 0xcc, 0x3e, 0x7c, 0xb4, 0x6d, 0xbf, 0xea, 0xe7,
+ 0x2b, 0x9b, 0x26, 0xf7, 0xec, 0x29, 0x78, 0x00, 0x13, 0x00, 0x2e, 0x48,
+ 0x06, 0xa1, 0x9c, 0x34, 0x98, 0x43, 0x0c, 0xfb, 0x71, 0x9d, 0xf1, 0x79,
+ 0x6d, 0xb8, 0xe1, 0x76, 0xde, 0x00, 0xe9, 0xdb, 0x13, 0x00, 0x62, 0x03,
+ 0xa2, 0x6a, 0x7f, 0x24, 0x62, 0xdb, 0x9d, 0xfb, 0x84, 0xb6, 0xb5, 0x00,
+ 0xb4, 0x31, 0xfe, 0x1a, 0xbc, 0x7a, 0xba, 0x84, 0xaa, 0x43, 0x3d, 0xfb,
+ 0x2a, 0x5c, 0xd0, 0x1d, 0x80, 0xc0, 0x9a, 0x02, 0x62, 0xc9, 0x44, 0xd2,
+ 0xba, 0x39, 0x8e, 0x77, 0x5b, 0x3e, 0xe6, 0xeb, 0x20, 0x06, 0x80, 0x26,
+ 0x00, 0x37, 0x0d, 0xc4, 0xcc, 0x5a, 0x0a, 0x4f, 0x24, 0x6f, 0xba, 0xdb,
+ 0x7f, 0xd7, 0xb2, 0x9b, 0x63, 0x6c, 0x39, 0x68, 0xc8, 0x1a, 0xea, 0x67,
+ 0x73, 0xd5, 0x53, 0x2d, 0xaf, 0xbb, 0x4b, 0xe7, 0x25, 0x80, 0x2a, 0xe1,
+ 0x81, 0xa8, 0x0c, 0x48, 0x0c, 0x40, 0xa2, 0x0b, 0x4b, 0x0c, 0x47, 0x1a,
+ 0xfd, 0xfb, 0x66, 0x51, 0xbe, 0xf5, 0x60, 0x27, 0x00, 0xc8, 0x0a, 0x80,
+ 0x65, 0xd1, 0x89, 0xa8, 0x61, 0xbb, 0xfd, 0xf7, 0xdf, 0x76, 0x3d, 0x78,
+ 0xdd, 0x6c, 0x82, 0x18, 0x61, 0x7f, 0x6c, 0x9d, 0xdd, 0x78, 0x58, 0x81,
+ 0xf2, 0xe8, 0xfa, 0xfb, 0x74, 0xa5, 0xe4, 0x00, 0x60, 0x00, 0xd8, 0x0a,
+ 0x80, 0x9c, 0x0a, 0x16, 0x43, 0x43, 0x64, 0x23, 0x76, 0xdc, 0xf2, 0xc6,
+ 0x65, 0xec, 0xc1, 0x4b, 0xb1, 0x49, 0x68, 0x2b, 0x3a, 0x7b, 0x63, 0xaa,
+ 0xd9, 0xef, 0xd7, 0xd2, 0x94, 0xb2, 0x9d, 0xca, 0x52, 0x91, 0x17, 0x29,
+ 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52,
+ 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xb8,
+ 0x40, 0x1a, 0x00, 0x98, 0x00, 0xdc, 0x37, 0x13, 0x31, 0x68, 0x29, 0x3c,
+ 0x91, 0xbe, 0xeb, 0x6d, 0xff, 0x5e, 0xca, 0x6d, 0x8d, 0xad, 0x29, 0xc8,
+ 0xdf, 0xf5, 0x6c, 0x7c, 0x8f, 0xbe, 0xed, 0x2f, 0x22, 0x00, 0xec, 0x03,
+ 0x04, 0x00, 0x5c, 0x90, 0xc2, 0x80, 0xcf, 0x2c, 0xb4, 0xa0, 0x6a, 0x73,
+ 0x36, 0xc6, 0x66, 0xe6, 0xbf, 0xdc, 0x2a, 0xf5, 0xd5, 0x14, 0x5e, 0x2d,
+ 0x19, 0x28, 0x5e, 0xe7, 0x4b, 0xe6, 0xb9, 0x4a, 0x52, 0xca, 0xab, 0x94,
+ 0xbe, 0x6a, 0x03, 0xa0, 0x05, 0x88, 0x26, 0x90, 0xb8, 0x0e, 0xd2, 0x05,
+ 0x70, 0x0d, 0x86, 0x25, 0x2e, 0x84, 0x24, 0x68, 0x16, 0xfd, 0xd4, 0x96,
+ 0x1d, 0xef, 0xa5, 0x5d, 0x25, 0x06, 0xa0, 0xbe, 0x9c, 0x8e, 0xe3, 0x3b,
+ 0x71, 0x23, 0xa8, 0xfb, 0xeb, 0x94, 0xa5, 0x2e, 0x6a, 0xee, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88,
+ 0x00, 0x00, 0x01, 0x10, 0x53, 0xfb, 0x23, 0xcc, 0xf5, 0xcf, 0x33, 0xdd,
+ 0x6d, 0x65, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x3b, 0xa5, 0xe7,
+ 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43,
+ 0x4e, 0xea, 0x79, 0x9e, 0xb6, 0x34, 0xb4, 0x5a, 0x3c, 0xd1, 0xa5, 0x33,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x7c, 0x24, 0x00, 0xb8, 0x37,
+ 0x06, 0x80, 0xe8, 0x9a, 0x4c, 0xf9, 0xc6, 0x25, 0xd6, 0xbc, 0x30, 0xbc,
+ 0xcc, 0xc0, 0x22, 0xee, 0x26, 0xfb, 0xb5, 0xf2, 0xb0, 0x02, 0xac, 0x90,
+ 0x03, 0xf2, 0x6f, 0x58, 0xc0, 0x30, 0xdc, 0x73, 0xf4, 0xe6, 0x71, 0x6c,
+ 0x35, 0x83, 0xef, 0x64, 0x03, 0x54, 0x38, 0x14, 0x2c, 0x53, 0x7c, 0x27,
+ 0x7b, 0x00, 0xce, 0xc9, 0xd5, 0xff, 0x41, 0x00, 0x84, 0x0c, 0x8e, 0x9e,
+ 0x49, 0xef, 0x91, 0x8d, 0xc3, 0xfd, 0xea, 0x4a, 0x29, 0x21, 0xa9, 0x6c,
+ 0x1f, 0xee, 0x70, 0x15, 0x4f, 0x48, 0x6a, 0x45, 0x76, 0x0a, 0xbe, 0x58,
+ 0x03, 0xad, 0x8a, 0x26, 0x72, 0xc7, 0xbf, 0xe1, 0xd6, 0xbf, 0x8c, 0xce,
+ 0x6f, 0xa2, 0xfd, 0xa0, 0x05, 0x48, 0xdf, 0x23, 0x96, 0x13, 0xcc, 0x65,
+ 0x0a, 0xbd, 0x29, 0x64, 0xce, 0x18, 0x96, 0x4f, 0x0f, 0xb9, 0x64, 0x2d,
+ 0xc3, 0x49, 0xbb, 0xf7, 0xeb, 0x7f, 0xc0, 0x3f, 0xbc, 0x88, 0x0e, 0xd8,
+ 0xb0, 0xd2, 0x8a, 0x0b, 0x0f, 0x6a, 0x9f, 0xe4, 0xec, 0xee, 0xa7, 0xe2,
+ 0xa2, 0xfd, 0x29, 0x60, 0x50, 0x94, 0x50, 0xde, 0x91, 0xcc, 0x2e, 0x72,
+ 0x60, 0x14, 0xc1, 0xa8, 0xe9, 0xfd, 0xb6, 0xbc, 0x58, 0x09, 0xfb, 0x92,
+ 0x8a, 0xc6, 0xf7, 0x20, 0x11, 0x6e, 0x71, 0x60, 0x28, 0x02, 0x8d, 0xbb,
+ 0xbe, 0x61, 0x2e, 0x7b, 0x13, 0xee, 0xd1, 0xbc, 0x66, 0x1f, 0xf9, 0x3a,
+ 0xcd, 0xaf, 0xec, 0xa1, 0x85, 0x01, 0x94, 0xb3, 0xb5, 0xe3, 0x80, 0x0f,
+ 0x00, 0xbb, 0x6e, 0xac, 0x7f, 0xe4, 0x6c, 0x1d, 0x56, 0x02, 0x62, 0x80,
+ 0xbf, 0xff, 0x12, 0x09, 0xdc, 0x07, 0xf7, 0x64, 0x0a, 0x86, 0xfe, 0x1b,
+ 0xdb, 0x92, 0x15, 0x88, 0xb7, 0x07, 0xff, 0xa5, 0xb7, 0x32, 0xd0, 0xca,
+ 0xb3, 0xd4, 0x00, 0xc4, 0x99, 0x80, 0xa1, 0x0c, 0xb0, 0xcc, 0x9c, 0x5e,
+ 0x47, 0x61, 0xb8, 0xd0, 0x3b, 0x8e, 0xe2, 0xef, 0x00, 0x00, 0x91, 0x3c,
+ 0x02, 0xb0, 0xd4, 0x3a, 0xb8, 0x63, 0xfc, 0x2f, 0x27, 0x31, 0x1f, 0xdf,
+ 0x53, 0xbe, 0x7e, 0x00, 0xb0, 0x34, 0x94, 0x02, 0x1c, 0x86, 0x01, 0xca,
+ 0x12, 0x81, 0x0d, 0x86, 0x80, 0x0c, 0xae, 0x50, 0x2b, 0x9c, 0x84, 0x5e,
+ 0x3c, 0x23, 0x93, 0xad, 0x10, 0x49, 0xd5, 0xc6, 0xf0, 0x1d, 0x94, 0x08,
+ 0x5f, 0x29, 0xd8, 0xdf, 0x82, 0xfa, 0xc8, 0xfe, 0xa4, 0x0a, 0xa7, 0x23,
+ 0x20, 0x99, 0x8e, 0x28, 0xfe, 0x3d, 0x26, 0x85, 0x1d, 0x78, 0x52, 0x1a,
+ 0x00, 0xc0, 0x69, 0x64, 0x3e, 0x3b, 0x0c, 0x01, 0x5f, 0xcc, 0x4e, 0x67,
+ 0xbe, 0xd8, 0x7f, 0xb4, 0x28, 0x27, 0x61, 0x5b, 0x05, 0xd3, 0xea, 0xdf,
+ 0x81, 0x00, 0x98, 0x10, 0x82, 0x40, 0x1d, 0x72, 0xc0, 0x70, 0x9e, 0x51,
+ 0x69, 0x70, 0x1e, 0xf7, 0xc8, 0xe1, 0xe1, 0x0d, 0x7e, 0x81, 0xbe, 0x00,
+ 0x00, 0xec, 0x10, 0x81, 0xc0, 0x30, 0x41, 0x30, 0xf7, 0x25, 0x06, 0x39,
+ 0xac, 0x8c, 0xee, 0xc0, 0x22, 0x27, 0xdf, 0x68, 0x01, 0x80, 0x14, 0xe0,
+ 0x3b, 0x26, 0x94, 0x19, 0x80, 0xff, 0x5f, 0x1d, 0x8d, 0xe1, 0x57, 0x50,
+ 0x0e, 0xbe, 0x18, 0x1a, 0x1b, 0xc0, 0xfa, 0x7f, 0x71, 0x5a, 0x84, 0x9f,
+ 0xb5, 0x7f, 0xf2, 0x32, 0x18, 0x49, 0x09, 0x1f, 0x9e, 0x11, 0xc2, 0xf5,
+ 0xe9, 0xc0, 0x40, 0x87, 0x2f, 0xf6, 0x3b, 0x13, 0xc0, 0x7f, 0x79, 0x80,
+ 0x13, 0xa7, 0x64, 0x64, 0x63, 0x56, 0x1d, 0xa0, 0x02, 0x0c, 0x10, 0x9e,
+ 0x3f, 0xb0, 0x50, 0x1b, 0xbd, 0x27, 0x01, 0x47, 0x16, 0x70, 0x7d, 0x91,
+ 0xb7, 0xeb, 0xc8, 0x63, 0x4b, 0x21, 0xfc, 0x85, 0xb3, 0x6c, 0x28, 0x08,
+ 0xde, 0x4c, 0x04, 0x29, 0xc3, 0x36, 0x35, 0xdc, 0x3a, 0x50, 0x28, 0x4d,
+ 0xe1, 0xa5, 0xf5, 0x6c, 0xaf, 0xd5, 0x96, 0x7e, 0x36, 0xfa, 0x81, 0x0d,
+ 0x03, 0x03, 0x53, 0x9f, 0xf1, 0x22, 0x2e, 0x3d, 0x82, 0x76, 0x38, 0x66,
+ 0xae, 0xfb, 0xbb, 0x3f, 0x1d, 0x7c, 0x9f, 0x86, 0x24, 0x30, 0x99, 0xf9,
+ 0x2d, 0x5c, 0xcc, 0x77, 0x7b, 0xea, 0x48, 0x40, 0xd4, 0xec, 0x3f, 0xb5,
+ 0xf1, 0x90, 0x13, 0xf5, 0x21, 0x1b, 0x0b, 0x11, 0xb6, 0xb8, 0x53, 0xd6,
+ 0x8d, 0x6b, 0xaf, 0xe8, 0x85, 0x13, 0x1d, 0x04, 0xb4, 0xf6, 0x45, 0x9c,
+ 0xb2, 0x16, 0x42, 0x31, 0x68, 0x48, 0xdd, 0x79, 0x50, 0x28, 0x5e, 0xd8,
+ 0x0c, 0x17, 0x8f, 0x39, 0xc5, 0x99, 0x7a, 0xa0, 0x2a, 0x18, 0x03, 0x6e,
+ 0xdb, 0xf6, 0xeb, 0xad, 0xf8, 0xd6, 0x0a, 0x1d, 0xf4, 0xaf, 0xd9, 0x6c,
+ 0xfd, 0xb5, 0xf3, 0xc0, 0x0a, 0x91, 0xbe, 0x41, 0x69, 0x64, 0xf1, 0x4a,
+ 0x0b, 0xbd, 0xda, 0x1d, 0xd4, 0x7e, 0x58, 0x55, 0xf0, 0xa0, 0x2c, 0x03,
+ 0x14, 0x0e, 0x4e, 0x0b, 0xc0, 0x72, 0x82, 0xfb, 0x08, 0x34, 0x54, 0xf5,
+ 0xfd, 0x40, 0x02, 0xc6, 0x2c, 0x95, 0xfe, 0xdc, 0xc3, 0xd7, 0x12, 0x19,
+ 0x33, 0x13, 0x06, 0x67, 0x7f, 0xcf, 0xb8, 0x60, 0x3a, 0x47, 0xe4, 0xd5,
+ 0xbb, 0xa3, 0x72, 0x05, 0x8c, 0x37, 0x00, 0xdc, 0x86, 0xcf, 0x9b, 0x36,
+ 0x03, 0x82, 0x5f, 0xda, 0x3f, 0x1a, 0x7a, 0xb6, 0x7f, 0x4d, 0xf6, 0x52,
+ 0x05, 0x32, 0x03, 0x31, 0x7d, 0x2b, 0xec, 0xa1, 0x20, 0x72, 0xe8, 0x94,
+ 0x4c, 0x74, 0x12, 0xd3, 0xd9, 0x17, 0xcf, 0x00, 0x4f, 0xff, 0x18, 0x8c,
+ 0x2f, 0xf0, 0xe0, 0x23, 0x74, 0x40, 0xa1, 0x7b, 0x60, 0x30, 0x5e, 0x3c,
+ 0xe7, 0x16, 0x65, 0xd1, 0x92, 0x91, 0x8e, 0x2c, 0x9d, 0x49, 0xb7, 0xea,
+ 0x8b, 0x21, 0x64, 0x23, 0x16, 0x84, 0x8d, 0xd7, 0x19, 0x00, 0x3a, 0xdc,
+ 0xa1, 0xbd, 0x2a, 0x5f, 0x56, 0x33, 0x5d, 0x90, 0x2a, 0x18, 0x03, 0x6f,
+ 0xb6, 0xfd, 0xba, 0xef, 0x98, 0x80, 0x4c, 0x03, 0xac, 0x23, 0x70, 0xff,
+ 0xdc, 0x0d, 0x8a, 0x8e, 0x9e, 0xd9, 0xb9, 0xfe, 0x2f, 0xe8, 0xc1, 0x80,
+ 0x50, 0x96, 0x7f, 0xdb, 0xb3, 0xec, 0x68, 0x1a, 0xbb, 0xc0, 0x36, 0x49,
+ 0x2f, 0xa3, 0x67, 0xf8, 0x07, 0xf7, 0xce, 0x00, 0x4d, 0x9f, 0x96, 0x5f,
+ 0x0b, 0x27, 0xfb, 0xc6, 0x06, 0x77, 0xc9, 0x2d, 0x41, 0x3f, 0xf7, 0x15,
+ 0x95, 0x9e, 0xd3, 0x97, 0xf7, 0xe6, 0x9c, 0x17, 0xe4, 0xd7, 0xf5, 0xa0,
+ 0x0a, 0xb0, 0xd2, 0x51, 0x2f, 0x0d, 0x30, 0x70, 0x7d, 0xaf, 0x10, 0x80,
+ 0xb9, 0x2b, 0xa7, 0x6e, 0xe4, 0xe6, 0x7b, 0x86, 0x18, 0x92, 0x8a, 0xef,
+ 0x9c, 0xb4, 0xfb, 0x92, 0x03, 0xa7, 0x2c, 0x34, 0xb4, 0xa4, 0xfc, 0xa0,
+ 0x0e, 0x2c, 0x3f, 0xfb, 0x7e, 0x28, 0x9f, 0x55, 0xfa, 0xa2, 0xc0, 0xa0,
+ 0xc4, 0xb6, 0x49, 0x8f, 0xff, 0x56, 0xcb, 0x69, 0x49, 0xa0, 0x50, 0x06,
+ 0xc8, 0xdd, 0xdc, 0xcf, 0x72, 0x12, 0x02, 0x90, 0x2a, 0x30, 0xc7, 0x58,
+ 0xb6, 0x01, 0xf5, 0x84, 0x06, 0x2e, 0x5a, 0x72, 0x31, 0x87, 0x89, 0xfd,
+ 0x96, 0x02, 0xf9, 0xef, 0xdd, 0x19, 0xcc, 0x7f, 0x2b, 0xf7, 0x01, 0x84,
+ 0x24, 0x86, 0xbf, 0x3b, 0x63, 0x7d, 0xe1, 0x48, 0x41, 0x85, 0x12, 0xdd,
+ 0x09, 0xfc, 0x63, 0xba, 0x83, 0xae, 0x78, 0x0e, 0xf8, 0x18, 0x0c, 0xc5,
+ 0x63, 0x54, 0x1d, 0xae, 0x68, 0x0e, 0x91, 0x8a, 0x2b, 0xf3, 0x4f, 0xe2,
+ 0x80, 0x82, 0xee, 0xd4, 0xa7, 0xf4, 0x7e, 0x3d, 0x5b, 0x50, 0xd7, 0xf4,
+ 0xd2, 0x1b, 0x86, 0x93, 0x7e, 0xcf, 0xd9, 0x41, 0x47, 0x93, 0xe4, 0x00,
+ 0xab, 0x23, 0xec, 0x94, 0xed, 0xf9, 0xdb, 0x2d, 0x62, 0xef, 0x0c, 0x03,
+ 0x04, 0x70, 0x92, 0xf9, 0xe9, 0x02, 0x37, 0xa4, 0xc9, 0x0c, 0x41, 0x7c,
+ 0xbf, 0xfa, 0x52, 0xe7, 0x63, 0x8d, 0x1e, 0x7d, 0xc6, 0xe6, 0x63, 0x35,
+ 0x77, 0xec, 0x0b, 0x26, 0x24, 0xa4, 0x8c, 0x3f, 0xf3, 0x9a, 0xf3, 0xdc,
+ 0x0a, 0x62, 0xb0, 0x17, 0x4b, 0x39, 0x99, 0x77, 0xcf, 0x88, 0x43, 0x4b,
+ 0x2d, 0x25, 0xa7, 0x37, 0xc1, 0x1b, 0x0e, 0x36, 0xfa, 0x2e, 0x04, 0x2f,
+ 0xa2, 0x6b, 0xf3, 0x9c, 0x9c, 0x66, 0x0f, 0xa4, 0x6f, 0xe1, 0xd9, 0x73,
+ 0xab, 0x7c, 0x28, 0x0e, 0xf0, 0x01, 0xb0, 0x03, 0xac, 0x18, 0x37, 0xbf,
+ 0xd9, 0x0e, 0xcc, 0x03, 0x9e, 0x35, 0x39, 0x8c, 0x53, 0xeb, 0xef, 0x77,
+ 0xc4, 0x40, 0x16, 0xf4, 0xa4, 0x06, 0xc1, 0x88, 0x5a, 0x8b, 0x2d, 0x28,
+ 0xc3, 0xb6, 0x52, 0x78, 0x70, 0xdb, 0xec, 0x25, 0x93, 0x03, 0x43, 0x18,
+ 0x6a, 0x3a, 0x1f, 0x6f, 0x9f, 0x1f, 0xbd, 0x90, 0x09, 0x80, 0x70, 0x37,
+ 0x9e, 0x5f, 0xe9, 0xa8, 0xb3, 0xc4, 0x5e, 0xda, 0x58, 0xf9, 0x30, 0x35,
+ 0x08, 0xc5, 0xf4, 0x27, 0x21, 0x27, 0x76, 0x57, 0x68, 0x00, 0xd8, 0x34,
+ 0x94, 0x4b, 0x19, 0xc6, 0x3b, 0x0d, 0x65, 0x3d, 0xe2, 0x3e, 0x1f, 0x74,
+ 0x5e, 0xda, 0x52, 0x94, 0x4e, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x20, 0x00, 0x00, 0x01, 0x11, 0x6b, 0xfb, 0x9b, 0xce,
+ 0xf5, 0xaf, 0x35, 0x4d, 0x4d, 0xd4, 0xf3, 0x3d, 0x64, 0x65, 0x16, 0xaf,
+ 0x18, 0xd2, 0x99, 0xdc, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca,
+ 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94,
+ 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29,
+ 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x2f, 0x99, 0x00,
+ 0x64, 0x01, 0x98, 0x0e, 0x80, 0x34, 0xc5, 0xb2, 0x09, 0xa9, 0xe5, 0xfc,
+ 0xff, 0x8c, 0xc7, 0x1c, 0x68, 0xbf, 0x7d, 0x36, 0x97, 0x49, 0x65, 0x60,
+ 0xdc, 0xdc, 0xea, 0x37, 0x6b, 0xc4, 0x1b, 0xd1, 0xd2, 0xe8, 0xe9, 0xee,
+ 0x7f, 0x4b, 0x3b, 0x87, 0x7f, 0x79, 0x60, 0x13, 0x00, 0x5f, 0xc0, 0xa1,
+ 0x31, 0x24, 0x24, 0xa0, 0x96, 0x51, 0x6e, 0xeb, 0x6f, 0xb2, 0x73, 0x25,
+ 0x8c, 0x10, 0x2e, 0xf6, 0xd4, 0xa5, 0x25, 0x64, 0xe6, 0xeb, 0x9b, 0xb5,
+ 0xfc, 0xd4, 0x30, 0x01, 0xf9, 0x08, 0x04, 0xc8, 0x4e, 0x01, 0xd1, 0x31,
+ 0x2e, 0x93, 0xf9, 0x7d, 0xce, 0x38, 0xe1, 0xcb, 0xbd, 0x38, 0x14, 0x01,
+ 0x3a, 0x49, 0x80, 0x31, 0x40, 0x15, 0x2d, 0xcb, 0xef, 0xdc, 0x7f, 0x7d,
+ 0xf1, 0x15, 0x57, 0xa3, 0xa5, 0xae, 0x26, 0x23, 0x0d, 0x51, 0x25, 0x5f,
+ 0xf3, 0xf8, 0x7d, 0x2f, 0xc2, 0xaf, 0xc0, 0x80, 0x6a, 0x00, 0xff, 0x00,
+ 0xef, 0xf2, 0x50, 0x66, 0x43, 0xa0, 0x60, 0xde, 0x49, 0x18, 0xee, 0xc3,
+ 0xb2, 0xdc, 0x4e, 0xf7, 0x2c, 0x86, 0x00, 0xcc, 0x0c, 0x86, 0x21, 0x08,
+ 0x26, 0x64, 0x27, 0x2f, 0x73, 0xf1, 0x5c, 0xed, 0xd1, 0x87, 0xb7, 0xe7,
+ 0x9d, 0xef, 0xa9, 0x52, 0xd0, 0x02, 0xc2, 0x99, 0x19, 0x89, 0x07, 0x2f,
+ 0xc8, 0x6f, 0xbf, 0x9c, 0x80, 0x64, 0x00, 0xfc, 0xa0, 0x13, 0x06, 0x06,
+ 0xb9, 0x49, 0x48, 0x69, 0x7d, 0xc6, 0xb9, 0xea, 0x56, 0xe1, 0xee, 0x17,
+ 0x7d, 0x01, 0x29, 0xe5, 0xa7, 0xa5, 0x3d, 0xfa, 0x7f, 0xff, 0x8f, 0xfe,
+ 0xba, 0x96, 0x18, 0xa4, 0x23, 0xa5, 0x3b, 0x2d, 0x27, 0x87, 0x54, 0xf7,
+ 0x2e, 0x00, 0x06, 0x80, 0x57, 0x00, 0xc0, 0x0a, 0x94, 0x1a, 0x9d, 0xfe,
+ 0x40, 0xd5, 0x63, 0xd6, 0x8f, 0x88, 0xb7, 0x7e, 0x96, 0x5f, 0x64, 0xef,
+ 0xb9, 0xdd, 0x55, 0x1d, 0x7e, 0x18, 0x01, 0xf8, 0x06, 0x20, 0x06, 0xfc,
+ 0xac, 0x8d, 0xd0, 0x84, 0x63, 0xb8, 0x13, 0xe6, 0xa8, 0x51, 0xc1, 0xf7,
+ 0x7c, 0x06, 0x05, 0x86, 0x06, 0x06, 0xa0, 0xb2, 0xc6, 0x81, 0x64, 0xa1,
+ 0x3b, 0x23, 0x65, 0xb7, 0x75, 0xbf, 0xd7, 0x6a, 0x96, 0xa8, 0x29, 0x08,
+ 0x40, 0xcc, 0xad, 0x4b, 0xbf, 0x7b, 0xf9, 0x28, 0x0c, 0x40, 0x1f, 0x80,
+ 0x80, 0x04, 0xf9, 0x18, 0x34, 0x98, 0x4d, 0x08, 0xfd, 0xbe, 0xfb, 0x75,
+ 0x9f, 0x76, 0x4a, 0x00, 0x7e, 0x50, 0x0c, 0x32, 0x70, 0x0e, 0x88, 0x59,
+ 0x0e, 0xc0, 0x5c, 0xbe, 0xe9, 0x1e, 0xb3, 0x02, 0xaf, 0xa0, 0xd2, 0xe9,
+ 0xc0, 0x50, 0xad, 0x86, 0x2d, 0x87, 0x3f, 0x6a, 0x06, 0xfb, 0xe7, 0x16,
+ 0x4d, 0x49, 0x0c, 0xb1, 0xa4, 0xd4, 0xf4, 0x9c, 0xfc, 0xd6, 0x14, 0x65,
+ 0xf3, 0xa2, 0x10, 0x03, 0xf0, 0x28, 0x42, 0x01, 0xd7, 0x21, 0x21, 0x01,
+ 0xab, 0xc9, 0xc4, 0xb0, 0x8d, 0xdd, 0x2e, 0xb3, 0xcf, 0xc4, 0x4b, 0xea,
+ 0xd4, 0xb1, 0x48, 0x62, 0x0b, 0xdb, 0x73, 0xb2, 0xf6, 0xca, 0x3a, 0xad,
+ 0xfe, 0xbf, 0x2e, 0x02, 0x00, 0x13, 0x80, 0x1c, 0x10, 0xc9, 0x7b, 0x0d,
+ 0x47, 0x74, 0x7d, 0xcf, 0x19, 0xf9, 0x1d, 0x55, 0x60, 0x19, 0x81, 0x52,
+ 0x60, 0x0c, 0x43, 0x78, 0x69, 0x58, 0xb4, 0x27, 0xb3, 0xee, 0xcc, 0xac,
+ 0xcf, 0x9b, 0x88, 0xbd, 0x65, 0x28, 0x25, 0x20, 0x69, 0x2c, 0xd6, 0x60,
+ 0xe9, 0xba, 0x9a, 0xf8, 0x80, 0x20, 0x02, 0xa0, 0x07, 0x80, 0x54, 0xa0,
+ 0xd6, 0x71, 0x8e, 0xf8, 0x57, 0xdf, 0x11, 0x6f, 0x2e, 0x02, 0x10, 0x07,
+ 0x7c, 0x86, 0x90, 0x14, 0x20, 0x94, 0x76, 0xe9, 0xeb, 0x4a, 0x76, 0x58,
+ 0xce, 0x63, 0x3b, 0xa8, 0xd3, 0x6f, 0xa1, 0xd2, 0xae, 0x18, 0x8e, 0x9c,
+ 0x9f, 0x97, 0xb3, 0x49, 0xfb, 0x35, 0xfc, 0xcc, 0x34, 0x02, 0xf0, 0x28,
+ 0x02, 0x74, 0x20, 0x00, 0xfc, 0x98, 0x92, 0x5a, 0x00, 0xf9, 0x5f, 0x65,
+ 0x30, 0xb1, 0x66, 0x5e, 0x98, 0x03, 0x50, 0x0d, 0x4b, 0x00, 0xd5, 0x3b,
+ 0xa7, 0x64, 0x64, 0x12, 0x11, 0x8c, 0x73, 0xd6, 0x2e, 0xf4, 0xd4, 0xa9,
+ 0x04, 0xc4, 0x23, 0xef, 0xb1, 0xce, 0xc2, 0x9c, 0xf6, 0x8f, 0xff, 0xbf,
+ 0x36, 0x01, 0xa0, 0x08, 0x52, 0x01, 0xa1, 0x7d, 0x28, 0xd9, 0x3b, 0xe6,
+ 0xfb, 0x90, 0x4f, 0xd7, 0x97, 0x00, 0x13, 0x81, 0x42, 0x11, 0x65, 0x93,
+ 0x40, 0xa1, 0x31, 0x28, 0x2c, 0xbd, 0xf0, 0x4f, 0xc4, 0xac, 0xeb, 0x7d,
+ 0xb0, 0xbd, 0x95, 0x7d, 0x32, 0x92, 0x49, 0x31, 0x09, 0xef, 0xf1, 0xf9,
+ 0xba, 0xcf, 0xc2, 0xe1, 0xfd, 0xf8, 0x00, 0x0c, 0x00, 0x1f, 0x80, 0x1e,
+ 0x16, 0x4d, 0x48, 0x6e, 0xe5, 0x81, 0x96, 0xff, 0xe6, 0x77, 0xdf, 0x91,
+ 0xdf, 0xda, 0x80, 0x04, 0xe0, 0x85, 0xf3, 0x89, 0xb8, 0x98, 0x56, 0xd9,
+ 0x29, 0x2c, 0x62, 0xd6, 0x94, 0xe6, 0x7f, 0xcd, 0x6e, 0xbc, 0xa5, 0x5e,
+ 0xd6, 0x93, 0x01, 0xb1, 0x4f, 0xd2, 0xcb, 0x4e, 0x6e, 0x22, 0x06, 0xfb,
+ 0xf9, 0xe0, 0x08, 0x40, 0x1d, 0xf2, 0x1a, 0x40, 0x50, 0x82, 0x51, 0xdb,
+ 0xa7, 0xad, 0x29, 0xd9, 0x63, 0x39, 0x8c, 0xee, 0xa3, 0x4d, 0xb3, 0x06,
+ 0x80, 0x5e, 0x05, 0x00, 0x4e, 0x84, 0x00, 0x1f, 0x93, 0x12, 0x4b, 0x40,
+ 0x1f, 0x2b, 0xec, 0xa6, 0x16, 0x2c, 0xcb, 0xe9, 0x94, 0xac, 0x02, 0xc2,
+ 0xb6, 0xcd, 0x98, 0x61, 0xdf, 0x7e, 0xe1, 0x72, 0x1b, 0xeb, 0xff, 0x1a,
+ 0x58, 0x6a, 0x40, 0xa9, 0x68, 0xc7, 0x19, 0x83, 0xef, 0x97, 0x00, 0xc5,
+ 0x28, 0xc3, 0x32, 0x77, 0xfb, 0xbf, 0x51, 0xea, 0x03, 0x77, 0xdb, 0x8a,
+ 0x49, 0x29, 0x39, 0x2c, 0xec, 0x65, 0xcd, 0x26, 0x86, 0x80, 0xdd, 0x2e,
+ 0xcc, 0xa6, 0xbb, 0x3a, 0x17, 0x87, 0x67, 0xb3, 0x7a, 0xbb, 0x92, 0x1a,
+ 0x01, 0x7b, 0x13, 0x00, 0x2c, 0x02, 0x85, 0x01, 0x82, 0x99, 0xbb, 0xad,
+ 0x3d, 0xcf, 0x71, 0x2b, 0x0e, 0xaa, 0x01, 0x00, 0x05, 0xe0, 0x16, 0x06,
+ 0x24, 0x98, 0x1a, 0x8e, 0x59, 0x5f, 0x76, 0xd8, 0xfc, 0xe7, 0xe1, 0x1e,
+ 0xfa, 0x15, 0x2e, 0x20, 0x1b, 0x15, 0xf3, 0x31, 0xae, 0x2d, 0xc7, 0xb8,
+ 0xfb, 0x47, 0xf7, 0xe5, 0xb8, 0x15, 0x0c, 0x0c, 0xe9, 0x00, 0xb0, 0x33,
+ 0xa3, 0xf5, 0x64, 0xa5, 0x2e, 0x9d, 0xba, 0x3e, 0xec, 0xcd, 0xb7, 0x59,
+ 0xeb, 0xbc, 0xf0, 0x06, 0x80, 0x17, 0x86, 0x00, 0x80, 0x99, 0xd0, 0x4a,
+ 0x4a, 0x53, 0xff, 0xee, 0xad, 0x8e, 0x56, 0x73, 0xd8, 0x83, 0x7d, 0x2a,
+ 0x94, 0x24, 0x31, 0x09, 0xcd, 0xd6, 0x1e, 0x3a, 0x6f, 0xf5, 0xf9, 0x20,
+ 0x0c, 0x80, 0x4e, 0x00, 0x78, 0x43, 0xe5, 0xa7, 0x74, 0x8d, 0xdf, 0x71,
+ 0xeb, 0xfb, 0xba, 0xc5, 0xeb, 0x30, 0x0c, 0x40, 0x2f, 0x49, 0x34, 0xb7,
+ 0x2b, 0x01, 0x8c, 0x37, 0x25, 0x47, 0xa7, 0xa0, 0x91, 0xd2, 0x23, 0x6c,
+ 0xd8, 0xeb, 0xdb, 0x52, 0x40, 0x61, 0x03, 0x46, 0xb2, 0xd9, 0xa1, 0xfb,
+ 0x35, 0xfc, 0xbc, 0x07, 0x60, 0x0e, 0xca, 0x21, 0xe2, 0x81, 0x08, 0x1b,
+ 0x74, 0xa1, 0x6e, 0x95, 0xed, 0xd4, 0x76, 0x34, 0xe3, 0x6b, 0xc0, 0xa0,
+ 0x03, 0xdc, 0x4c, 0x02, 0x9c, 0x98, 0x90, 0xc2, 0x5f, 0xee, 0xb7, 0x24,
+ 0x8d, 0xfd, 0xc6, 0x9a, 0x72, 0xaf, 0xa4, 0xd2, 0xdc, 0x02, 0xc2, 0x86,
+ 0x0c, 0x65, 0x3b, 0x9f, 0xe9, 0x1b, 0xef, 0xe7, 0x00, 0x30, 0x00, 0x72,
+ 0x1a, 0x4c, 0x2d, 0x3c, 0xb0, 0x94, 0x24, 0xcc, 0xa1, 0x8a, 0x50, 0xc1,
+ 0x46, 0x35, 0xd6, 0xe0, 0x3b, 0x02, 0x85, 0x16, 0x4b, 0x21, 0x13, 0x3e,
+ 0xe7, 0xe2, 0x5f, 0x7c, 0x8e, 0xee, 0xad, 0x89, 0xcf, 0xef, 0x63, 0x4b,
+ 0x20, 0x30, 0x8d, 0xff, 0xd9, 0x9c, 0xf6, 0x36, 0xa7, 0xf7, 0xf3, 0xe0,
+ 0x0c, 0x00, 0x2f, 0x00, 0x3c, 0x25, 0xe2, 0xf2, 0x4b, 0xc9, 0xe3, 0x45,
+ 0x8d, 0x37, 0xac, 0x79, 0x12, 0xe8, 0xa0, 0x03, 0x5c, 0x51, 0x34, 0xa2,
+ 0xd0, 0x4c, 0x29, 0x08, 0x47, 0xfc, 0x95, 0x9d, 0x7f, 0xa1, 0x9f, 0xfe,
+ 0x7b, 0x1f, 0x87, 0x5e, 0xc2, 0x90, 0x06, 0x10, 0xff, 0xb1, 0x24, 0xf8,
+ 0x7f, 0x7f, 0x36, 0x01, 0xd8, 0x03, 0xb0, 0x32, 0x5e, 0xe5, 0x15, 0xb6,
+ 0xc3, 0x06, 0xb9, 0xe5, 0xa3, 0x77, 0x19, 0xc7, 0x67, 0x77, 0x1f, 0x6a,
+ 0x00, 0xcc, 0x01, 0xf8, 0x01, 0xd9, 0x45, 0x15, 0x90, 0x33, 0x66, 0x4f,
+ 0x01, 0x5b, 0x32, 0x85, 0x2e, 0xf7, 0xf4, 0xab, 0x13, 0x0a, 0xc3, 0x1b,
+ 0x12, 0x0e, 0x90, 0xdf, 0x7f, 0x36, 0x01, 0x88, 0x03, 0xb0, 0x0a, 0xc0,
+ 0xa6, 0x26, 0x62, 0xb1, 0x4e, 0x49, 0x1c, 0xdb, 0xbe, 0xc8, 0xa0, 0x00,
+ 0x84, 0x10, 0x82, 0x80, 0x2c, 0xc5, 0x6d, 0xc6, 0x12, 0x9c, 0xc1, 0x8c,
+ 0xdf, 0x89, 0xf7, 0xd2, 0x69, 0x30, 0x0b, 0x0a, 0xdb, 0x66, 0x3b, 0x8d,
+ 0x34, 0xf8, 0x1b, 0xee, 0x52, 0x94, 0xbc, 0xfb, 0x5b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0x80, 0x00, 0x00, 0x01, 0x12,
+ 0x73, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4b, 0x31, 0x5b, 0x25, 0x3b, 0x7f, 0xf3, 0x8e, 0xce,
+ 0x45, 0xd7, 0x9c, 0x0d, 0x01, 0x32, 0x03, 0x08, 0x60, 0x16, 0x01, 0x90,
+ 0x97, 0xd9, 0xfa, 0x19, 0x0b, 0xdb, 0x9f, 0xbf, 0xe6, 0x81, 0x2b, 0x5e,
+ 0x9d, 0xbb, 0x07, 0xd2, 0xd7, 0xeb, 0x29, 0x63, 0x0d, 0x0c, 0x49, 0x30,
+ 0xbe, 0x52, 0x73, 0x74, 0x76, 0xf6, 0xcb, 0x33, 0xe7, 0x72, 0x94, 0xa4,
+ 0xa7, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x5e, 0x5c, 0x06, 0x00, 0x3a, 0x28,
+ 0x9a, 0x51, 0x34, 0xb4, 0xa5, 0x38, 0x0c, 0x21, 0xdb, 0x6c, 0xb1, 0xcb,
+ 0xfc, 0x2d, 0x57, 0xa8, 0xb4, 0xdf, 0x7f, 0xf0, 0xea, 0x6e, 0x52, 0x94,
+ 0xb3, 0xaa, 0xe5, 0x29, 0x79, 0x70, 0x1d, 0x00, 0xe8, 0xa2, 0x69, 0x44,
+ 0xd2, 0xd2, 0x94, 0xe0, 0x30, 0x87, 0x6d, 0xb2, 0xc7, 0x2f, 0xf0, 0xbb,
+ 0x4f, 0xf2, 0x33, 0xfa, 0x4f, 0xbf, 0x51, 0x4a, 0x52, 0xce, 0xab, 0x94,
+ 0xa9, 0x4a, 0x3a, 0x0f, 0xcc, 0xa7, 0x0e, 0xf5, 0xd1, 0x17, 0x29, 0x4a,
+ 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91,
+ 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x97, 0x9a, 0x00,
+ 0x3e, 0x40, 0x01, 0xe7, 0x21, 0x8d, 0xe4, 0x9c, 0x8d, 0x98, 0xfe, 0x69,
+ 0xbc, 0xc1, 0x16, 0xe0, 0x1b, 0x60, 0x0b, 0x30, 0x69, 0x2d, 0x0e, 0x34,
+ 0x61, 0x28, 0x0f, 0x89, 0xb6, 0x1b, 0x89, 0xec, 0x1f, 0x4b, 0x5f, 0x5e,
+ 0x94, 0xa5, 0x8d, 0x57, 0x29, 0x4b, 0x20, 0x61, 0x5c, 0x34, 0xb4, 0x23,
+ 0x01, 0xff, 0xb4, 0xe2, 0xf5, 0xd2, 0xf2, 0x21, 0x80, 0x26, 0x28, 0x9b,
+ 0xcb, 0x18, 0x1b, 0xd8, 0x33, 0x7f, 0xd2, 0x4a, 0xdb, 0x36, 0x1c, 0x05,
+ 0xdd, 0x66, 0x2a, 0xb0, 0x98, 0x03, 0xa0, 0xc2, 0x19, 0xc4, 0xdf, 0x82,
+ 0x50, 0x53, 0x72, 0x8d, 0x49, 0xc7, 0x27, 0x8e, 0xba, 0x09, 0x79, 0x62,
+ 0x85, 0x7a, 0x1a, 0xfd, 0x55, 0x2e, 0x50, 0x15, 0x26, 0x62, 0x10, 0x6a,
+ 0x4a, 0x1a, 0x37, 0xa3, 0xb7, 0xec, 0xcb, 0x14, 0xbf, 0x64, 0x26, 0x81,
+ 0x44, 0x13, 0x0a, 0x42, 0x49, 0x7d, 0xcb, 0xcf, 0x91, 0xd0, 0xdd, 0x9d,
+ 0x9f, 0x84, 0xf6, 0x39, 0x47, 0xd9, 0x8d, 0xca, 0x16, 0x74, 0xda, 0xfd,
+ 0x05, 0x29, 0x4b, 0x2a, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x20, 0x00,
+ 0x00, 0x01, 0x13, 0x73, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xad, 0xfc, 0xc8, 0x0e, 0x90, 0x34, 0x9a,
+ 0x50, 0xae, 0xe1, 0x4b, 0xb8, 0x5c, 0xaf, 0xc8, 0x7c, 0xb4, 0x84, 0x20,
+ 0x6b, 0xbf, 0x22, 0x0f, 0xb7, 0x80, 0xeb, 0xa0, 0xa1, 0xa9, 0xff, 0x6e,
+ 0x33, 0xe4, 0x09, 0xbd, 0x87, 0xbd, 0x13, 0xd3, 0xc4, 0x35, 0x8d, 0x7f,
+ 0xf3, 0x07, 0x41, 0x35, 0x25, 0xa7, 0xec, 0xdb, 0x1f, 0xdc, 0x89, 0xee,
+ 0xea, 0x32, 0x0b, 0x3b, 0x36, 0x46, 0x74, 0xf3, 0x78, 0x1d, 0xb4, 0xe4,
+ 0xec, 0x4c, 0xf9, 0xd6, 0x94, 0x93, 0xb5, 0x90, 0x84, 0x5e, 0xd8, 0xb4,
+ 0x7e, 0x30, 0x6e, 0xcd, 0xc5, 0xeb, 0xa1, 0x98, 0x89, 0x45, 0xb8, 0xcc,
+ 0x4a, 0xfd, 0x38, 0x60, 0x12, 0x1e, 0x06, 0xaa, 0xba, 0x10, 0x8c, 0x8f,
+ 0xf0, 0xd4, 0x7c, 0x79, 0xaf, 0x76, 0xc2, 0x1f, 0x3f, 0x75, 0xaf, 0x90,
+ 0x2e, 0x08, 0xd4, 0x21, 0x05, 0xa1, 0xc6, 0xee, 0x71, 0x06, 0xca, 0xaa,
+ 0xdf, 0x6a, 0x0a, 0xc0, 0x60, 0x97, 0xb0, 0xd6, 0xfc, 0x97, 0xb9, 0x98,
+ 0xc0, 0x37, 0x7a, 0x42, 0xc6, 0x74, 0xf1, 0x9b, 0xec, 0x66, 0xf7, 0x88,
+ 0x28, 0xb0, 0xc2, 0x62, 0x72, 0x50, 0x97, 0x47, 0x40, 0x4a, 0x39, 0xee,
+ 0xc7, 0x01, 0xdb, 0xe8, 0x1e, 0xc3, 0x1f, 0xea, 0xaf, 0xfe, 0x7e, 0x8c,
+ 0xc4, 0x2f, 0xf8, 0x1f, 0xe4, 0xec, 0x38, 0x3e, 0xd6, 0x33, 0x24, 0x98,
+ 0x37, 0x6d, 0xb8, 0xd6, 0x53, 0xac, 0x2f, 0xd4, 0x27, 0xa1, 0x21, 0x85,
+ 0x23, 0x76, 0x18, 0x7b, 0x8b, 0x97, 0x06, 0xa3, 0x24, 0xac, 0x07, 0x90,
+ 0x4f, 0xe3, 0xda, 0xec, 0x59, 0x3e, 0xce, 0xf7, 0x96, 0x8c, 0x19, 0xc6,
+ 0x6c, 0x35, 0x26, 0x89, 0x1d, 0xae, 0x70, 0x68, 0x66, 0x03, 0x38, 0xa4,
+ 0x36, 0x52, 0xc8, 0xff, 0xda, 0xb7, 0x42, 0x03, 0x77, 0x1a, 0xeb, 0x3c,
+ 0x70, 0x59, 0xf7, 0x3f, 0x06, 0xa3, 0x13, 0x7f, 0x77, 0xdd, 0xdf, 0xb0,
+ 0xf9, 0xac, 0x9c, 0x4f, 0x8b, 0xf4, 0x19, 0x25, 0x6f, 0xf0, 0xc7, 0xc6,
+ 0x0f, 0x12, 0x7d, 0xe6, 0x83, 0x0a, 0xfc, 0x85, 0xc0, 0x7a, 0x95, 0x88,
+ 0xcb, 0x70, 0xb5, 0x5e, 0xcd, 0x39, 0x08, 0x3b, 0x8d, 0xbc, 0x40, 0x0d,
+ 0xf1, 0x40, 0x63, 0x65, 0xed, 0x82, 0x43, 0xcf, 0xa3, 0x8a, 0x01, 0xf4,
+ 0xef, 0xda, 0xa5, 0x25, 0x33, 0x21, 0x01, 0xff, 0xac, 0x81, 0x02, 0x66,
+ 0x1a, 0x5e, 0xc3, 0xfc, 0x4b, 0xc5, 0x23, 0x84, 0x71, 0xa2, 0xee, 0x58,
+ 0x14, 0x4e, 0x42, 0x7f, 0xfd, 0x95, 0xd8, 0x4d, 0xcc, 0x65, 0x77, 0xcd,
+ 0x04, 0x3f, 0x90, 0xde, 0x73, 0x1e, 0xe2, 0xb5, 0x68, 0x62, 0x10, 0x4d,
+ 0x4f, 0x46, 0xcd, 0x9b, 0xac, 0xd1, 0x35, 0xa1, 0x85, 0xa1, 0x1d, 0x38,
+ 0xc4, 0xa3, 0xa0, 0x69, 0x15, 0xa7, 0x42, 0x50, 0x18, 0x84, 0x9a, 0x94,
+ 0xa8, 0x46, 0xba, 0x96, 0x46, 0x6b, 0x3a, 0xc3, 0xda, 0xde, 0x19, 0x08,
+ 0x0c, 0x40, 0x0c, 0x30, 0x6a, 0x4a, 0x43, 0xf2, 0xdb, 0x12, 0x5d, 0x3b,
+ 0x61, 0xef, 0xf6, 0x16, 0x4e, 0xbd, 0xd5, 0xe4, 0xc0, 0x62, 0x1a, 0x84,
+ 0xa0, 0x84, 0x52, 0x3b, 0xe2, 0x8a, 0x58, 0x0a, 0xf9, 0xf7, 0x12, 0x5f,
+ 0xb6, 0x16, 0xbb, 0x5d, 0x5f, 0xe3, 0x19, 0xbe, 0xe5, 0xe4, 0x1f, 0xd9,
+ 0x08, 0x23, 0xd1, 0x28, 0x46, 0x25, 0x6e, 0xaf, 0xc9, 0x2c, 0x66, 0x20,
+ 0xd9, 0x72, 0x72, 0x49, 0x99, 0x03, 0x46, 0x74, 0x7e, 0x3d, 0x7b, 0x88,
+ 0xbb, 0xc8, 0xc5, 0x7e, 0xdf, 0xfc, 0xa5, 0x71, 0x63, 0xfd, 0xd6, 0x16,
+ 0xab, 0x3b, 0xca, 0x5e, 0xc5, 0x3a, 0x12, 0x86, 0xe6, 0x30, 0xe2, 0x2c,
+ 0x03, 0x72, 0x32, 0x0a, 0x53, 0x7f, 0x9f, 0x91, 0x0f, 0x90, 0xb2, 0xf7,
+ 0x41, 0x48, 0xeb, 0xf8, 0xd1, 0x43, 0xae, 0x68, 0x6a, 0x4a, 0xe5, 0x8d,
+ 0x1a, 0xe7, 0x32, 0x8e, 0x85, 0xa8, 0x3e, 0x2f, 0xa2, 0x43, 0x46, 0x28,
+ 0x66, 0x65, 0xb3, 0xfb, 0x0a, 0x50, 0x8c, 0x18, 0x57, 0x66, 0xfd, 0x64,
+ 0x03, 0x42, 0xed, 0xc4, 0x34, 0xa0, 0xb2, 0xdf, 0x77, 0x16, 0x2a, 0x62,
+ 0x6a, 0x50, 0x92, 0xf3, 0x70, 0x8c, 0xda, 0x79, 0xd4, 0x17, 0xe2, 0xf9,
+ 0xa1, 0xa8, 0x42, 0x32, 0x3b, 0x33, 0xa0, 0xe7, 0x34, 0x8f, 0x10, 0xd0,
+ 0xcd, 0xd2, 0x52, 0x11, 0x9b, 0xfd, 0x9c, 0x73, 0xdb, 0x8b, 0x2f, 0x16,
+ 0x4d, 0x70, 0x3d, 0x94, 0x1d, 0xae, 0x20, 0x19, 0x42, 0x08, 0x63, 0x5d,
+ 0xf2, 0xed, 0x16, 0x4e, 0xf4, 0xd6, 0x1b, 0x0a, 0x26, 0x06, 0x0d, 0x4f,
+ 0xe8, 0x62, 0x12, 0x1f, 0x72, 0xfb, 0x8d, 0x4a, 0x94, 0xc2, 0xdd, 0xba,
+ 0xb3, 0x5f, 0x42, 0xbc, 0xd1, 0x34, 0x94, 0x8f, 0xc0, 0x52, 0x03, 0x7e,
+ 0xb6, 0x3c, 0xd3, 0x7f, 0xe7, 0xf1, 0x43, 0xef, 0x47, 0x8f, 0x5d, 0xe7,
+ 0xa0, 0x7a, 0xed, 0xbd, 0x7f, 0xe1, 0xcb, 0xe3, 0x03, 0x03, 0x10, 0x02,
+ 0x3f, 0x69, 0x0d, 0xc8, 0x42, 0x0a, 0x6c, 0xdf, 0xe7, 0xe1, 0xe7, 0x59,
+ 0x88, 0x69, 0x29, 0x29, 0xcd, 0xdf, 0x2b, 0x08, 0xd3, 0x90, 0xd2, 0x84,
+ 0x96, 0xfb, 0xb9, 0x19, 0xae, 0xcc, 0x61, 0xe6, 0xfb, 0x2b, 0xb8, 0x9b,
+ 0xb6, 0xc5, 0x6d, 0xb6, 0xec, 0xfc, 0xfb, 0x9b, 0xc3, 0x10, 0xe2, 0x90,
+ 0x8c, 0x34, 0x48, 0xea, 0xe2, 0x1a, 0x51, 0xcb, 0x1b, 0xbb, 0x9c, 0xd8,
+ 0x74, 0xc4, 0xc2, 0xf0, 0xc2, 0xd1, 0xd2, 0x33, 0xa5, 0x97, 0x52, 0xdb,
+ 0x45, 0x69, 0xa5, 0x25, 0x23, 0x13, 0x3f, 0x2d, 0x19, 0x09, 0xff, 0x12,
+ 0xb6, 0x3c, 0x5b, 0x9e, 0xa6, 0x1f, 0x7b, 0x17, 0x6b, 0x9c, 0x58, 0xcc,
+ 0xad, 0xc6, 0x80, 0xf4, 0xe0, 0xec, 0x41, 0xba, 0x37, 0x9b, 0xad, 0xaf,
+ 0xc0, 0x93, 0x00, 0xb0, 0x0c, 0x08, 0x69, 0x02, 0x7f, 0x23, 0x11, 0x49,
+ 0xd7, 0xad, 0x26, 0x24, 0x61, 0x68, 0xe3, 0x93, 0xb8, 0x57, 0xbc, 0xa8,
+ 0x6f, 0x46, 0x02, 0x85, 0x14, 0x82, 0xd1, 0xfb, 0xf4, 0xa7, 0x0b, 0x22,
+ 0xde, 0xef, 0x2a, 0xe9, 0x42, 0xc6, 0x8b, 0xfc, 0x81, 0x47, 0x32, 0xb0,
+ 0xf0, 0x00, 0x3b, 0x26, 0x20, 0x00, 0xe4, 0x34, 0x6f, 0x08, 0xc0, 0x5d,
+ 0x22, 0x3e, 0xdd, 0x82, 0xfb, 0x5e, 0xc4, 0x9a, 0x1b, 0xc9, 0xa5, 0xf4,
+ 0xf3, 0x49, 0xc2, 0xaf, 0x1a, 0x01, 0x98, 0x15, 0x28, 0x37, 0xb1, 0x4d,
+ 0xc6, 0xe4, 0x72, 0xd2, 0x6f, 0xfb, 0x33, 0x08, 0x22, 0xdf, 0x42, 0x90,
+ 0x63, 0x8c, 0xdf, 0x8e, 0x69, 0xfb, 0x94, 0xa5, 0x2e, 0x0b, 0x5b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00, 0x01, 0x14, 0x83, 0xfb, 0xfd,
+ 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0x00, 0x00, 0x01, 0x15, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x16, 0x83,
+ 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0x00, 0x00, 0x01, 0x17, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01,
+ 0x18, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x19, 0x83, 0xfb, 0xfd, 0x29,
+ 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00,
+ 0x00, 0x01, 0x1a, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1b, 0x83, 0xfb,
+ 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0x00, 0x00, 0x01, 0x1c, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1d,
+ 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1e, 0x83, 0xfb, 0xfd, 0x29, 0x49,
+ 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00,
+ 0x01, 0x1f, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x20, 0x83, 0xfb, 0xfd,
+ 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0x00, 0x00, 0x01, 0x21, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x22, 0x83,
+ 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94,
+ 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22,
+ 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b,
+ 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5,
+ 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a,
+ 0x52, 0x22, 0x00, 0x00, 0x01, 0x23, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48,
+ 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22,
+ 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9,
+ 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52,
+ 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5,
+ 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01,
+ 0x24, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e,
+ 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94,
+ 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29,
+ 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52,
+ 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88,
+ 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0xb7,
+};
diff --git a/plugins/streamdev/streamdev-cvs/server/suspend.h b/plugins/streamdev/streamdev-cvs/server/suspend.h
new file mode 100644
index 0000000..bea25ee
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/server/suspend.h
@@ -0,0 +1,36 @@
+/*
+ * $Id: suspend.h,v 1.2 2008/10/22 11:59:32 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEV_SUSPEND_H
+#define VDR_STREAMDEV_SUSPEND_H
+
+#include <vdr/player.h>
+
+class cSuspendLive: public cPlayer, public cThread {
+protected:
+ virtual void Activate(bool On);
+ virtual void Action(void);
+
+ void Stop(void);
+
+public:
+ cSuspendLive(void);
+ virtual ~cSuspendLive();
+};
+
+class cSuspendCtl: public cControl {
+private:
+ cSuspendLive *m_Suspend;
+ static bool m_Active;
+
+public:
+ cSuspendCtl(void);
+ virtual ~cSuspendCtl();
+ virtual void Hide(void) {}
+ virtual eOSState ProcessKey(eKeys Key);
+
+ static bool IsActive(void) { return m_Active; }
+};
+
+#endif // VDR_STREAMDEV_SUSPEND_H
diff --git a/plugins/streamdev/streamdev-cvs/streamdev-client.c b/plugins/streamdev/streamdev-cvs/streamdev-client.c
new file mode 100644
index 0000000..bc9403c
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/streamdev-client.c
@@ -0,0 +1,60 @@
+/*
+ * streamdev.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id: streamdev-client.c,v 1.6 2008/04/08 14:18:15 schmirl Exp $
+ */
+
+#include "streamdev-client.h"
+#include "client/device.h"
+#include "client/setup.h"
+
+#if !defined(APIVERSNUM) || APIVERSNUM < 10509
+#error "VDR-1.5.9 API version or greater is required!"
+#endif
+
+const char *cPluginStreamdevClient::DESCRIPTION = trNOOP("VTP Streaming Client");
+
+cPluginStreamdevClient::cPluginStreamdevClient(void) {
+}
+
+cPluginStreamdevClient::~cPluginStreamdevClient() {
+}
+
+const char *cPluginStreamdevClient::Description(void) {
+ return tr(DESCRIPTION);
+}
+
+bool cPluginStreamdevClient::Start(void) {
+ I18nRegister(PLUGIN_NAME_I18N);
+ cStreamdevDevice::Init();
+ return true;
+}
+
+void cPluginStreamdevClient::Housekeeping(void) {
+ if (StreamdevClientSetup.StartClient && StreamdevClientSetup.SyncEPG)
+ ClientSocket.SynchronizeEPG();
+}
+
+const char *cPluginStreamdevClient::MainMenuEntry(void) {
+ return StreamdevClientSetup.StartClient && !StreamdevClientSetup.HideMenuEntry ? tr("Suspend Server") : NULL;
+}
+
+cOsdObject *cPluginStreamdevClient::MainMenuAction(void) {
+ if (ClientSocket.SuspendServer())
+ Skins.Message(mtInfo, tr("Server is suspended"));
+ else
+ Skins.Message(mtError, tr("Couldn't suspend Server!"));
+ return NULL;
+}
+
+cMenuSetupPage *cPluginStreamdevClient::SetupMenu(void) {
+ return new cStreamdevClientMenuSetupPage;
+}
+
+bool cPluginStreamdevClient::SetupParse(const char *Name, const char *Value) {
+ return StreamdevClientSetup.SetupParse(Name, Value);
+}
+
+VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this!
diff --git a/plugins/streamdev/streamdev-cvs/streamdev-client.h b/plugins/streamdev/streamdev-cvs/streamdev-client.h
new file mode 100644
index 0000000..b01e0d7
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/streamdev-client.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: streamdev-client.h,v 1.1.1.1 2004/12/30 22:43:59 lordjaxom Exp $
+ */
+
+#ifndef VDR_STREAMDEVCLIENT_H
+#define VDR_STREAMDEVCLIENT_H
+
+#include "common.h"
+
+#include <vdr/plugin.h>
+
+class cPluginStreamdevClient : public cPlugin {
+private:
+ static const char *DESCRIPTION;
+
+public:
+ cPluginStreamdevClient(void);
+ virtual ~cPluginStreamdevClient();
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void);
+ virtual bool Start(void);
+ virtual void Housekeeping(void);
+ virtual const char *MainMenuEntry(void);
+ virtual cOsdObject *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+};
+
+#endif // VDR_STREAMDEVCLIENT_H
diff --git a/plugins/streamdev/streamdev-cvs/streamdev-server.c b/plugins/streamdev/streamdev-cvs/streamdev-server.c
new file mode 100644
index 0000000..3593d9f
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/streamdev-server.c
@@ -0,0 +1,143 @@
+/*
+ * streamdev.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id: streamdev-server.c,v 1.12 2009/06/19 06:32:38 schmirl Exp $
+ */
+
+#include <getopt.h>
+#include <vdr/tools.h>
+#include "streamdev-server.h"
+#include "server/setup.h"
+#include "server/server.h"
+#include "server/suspend.h"
+
+#if !defined(APIVERSNUM) || APIVERSNUM < 10509
+#error "VDR-1.5.9 API version or greater is required!"
+#endif
+
+const char *cPluginStreamdevServer::DESCRIPTION = trNOOP("VDR Streaming Server");
+
+cPluginStreamdevServer::cPluginStreamdevServer(void)
+{
+}
+
+cPluginStreamdevServer::~cPluginStreamdevServer()
+{
+ free(opt_auth);
+ free(opt_remux);
+}
+
+const char *cPluginStreamdevServer::Description(void)
+{
+ return tr(DESCRIPTION);
+}
+
+const char *cPluginStreamdevServer::CommandLineHelp(void)
+{
+ // return a string that describes all known command line options.
+ return
+ " -a <LOGIN:PASSWORD>, --auth=<LOGIN:PASSWORD> Credentials for HTTP authentication.\n"
+ " -r <CMD>, --remux=<CMD> Define an external command for remuxing.\n"
+ ;
+}
+
+bool cPluginStreamdevServer::ProcessArgs(int argc, char *argv[])
+{
+ // implement command line argument processing here if applicable.
+ static const struct option long_options[] = {
+ { "auth", required_argument, NULL, 'a' },
+ { "remux", required_argument, NULL, 'r' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+ while((c = getopt_long(argc, argv, "a:r:", long_options, NULL)) != -1) {
+ switch (c) {
+ case 'a':
+ {
+ if (opt_auth)
+ free(opt_auth);
+ int l = strlen(optarg);
+ cBase64Encoder Base64((uchar*) optarg, l, l * 4 / 3 + 3);
+ const char *s = Base64.NextLine();
+ if (s)
+ opt_auth = strdup(s);
+ }
+ break;
+ case 'r':
+ if (opt_remux)
+ free(opt_remux);
+ opt_remux = strdup(optarg);
+ break;
+ default:
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cPluginStreamdevServer::Start(void)
+{
+ I18nRegister(PLUGIN_NAME_I18N);
+ if (!StreamdevHosts.Load(STREAMDEVHOSTSPATH, true, true)) {
+ esyslog("streamdev-server: error while loading %s", STREAMDEVHOSTSPATH);
+ fprintf(stderr, "streamdev-server: error while loading %s\n", STREAMDEVHOSTSPATH);
+ if (access(STREAMDEVHOSTSPATH, F_OK) != 0) {
+ fprintf(stderr, " Please install streamdevhosts.conf into the path "
+ "printed above. Without it\n"
+ " no client will be able to access your streaming-"
+ "server. An example can be\n"
+ " found together with this plugin's sources.\n");
+ }
+ return false;
+ }
+ if (!opt_remux)
+ opt_remux = strdup(DEFAULT_EXTERNREMUX);
+
+ cStreamdevServer::Initialize();
+
+ return true;
+}
+
+void cPluginStreamdevServer::Stop(void)
+{
+ cStreamdevServer::Destruct();
+}
+
+cString cPluginStreamdevServer::Active(void)
+{
+ if (cStreamdevServer::Active())
+ {
+ static const char *Message = NULL;
+ if (!Message) Message = tr("Streaming active");
+ return Message;
+ }
+ return NULL;
+}
+
+const char *cPluginStreamdevServer::MainMenuEntry(void)
+{
+ if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive())
+ return tr("Suspend Live TV");
+ return NULL;
+}
+
+cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
+{
+ cControl::Launch(new cSuspendCtl);
+ return NULL;
+}
+
+cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void)
+{
+ return new cStreamdevServerMenuSetupPage;
+}
+
+bool cPluginStreamdevServer::SetupParse(const char *Name, const char *Value)
+{
+ return StreamdevServerSetup.SetupParse(Name, Value);
+}
+
+VDRPLUGINCREATOR(cPluginStreamdevServer); // Don't touch this!
diff --git a/plugins/streamdev/streamdev-cvs/streamdev-server.h b/plugins/streamdev/streamdev-cvs/streamdev-server.h
new file mode 100644
index 0000000..8149e4b
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/streamdev-server.h
@@ -0,0 +1,33 @@
+/*
+ * $Id: streamdev-server.h,v 1.4 2007/02/19 12:08:16 schmirl Exp $
+ */
+
+#ifndef VDR_STREAMDEVSERVER_H
+#define VDR_STREAMDEVSERVER_H
+
+#include "common.h"
+
+#include <vdr/plugin.h>
+
+class cPluginStreamdevServer : public cPlugin {
+private:
+ static const char *DESCRIPTION;
+
+public:
+ cPluginStreamdevServer(void);
+ virtual ~cPluginStreamdevServer();
+
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void);
+ virtual const char *CommandLineHelp(void);
+ virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Start(void);
+ virtual void Stop(void);
+ virtual cString Active(void);
+ virtual const char *MainMenuEntry(void);
+ virtual cOsdObject *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+};
+
+#endif // VDR_STREAMDEVSERVER_H
diff --git a/plugins/streamdev/streamdev-cvs/streamdev/CVS/Entries b/plugins/streamdev/streamdev-cvs/streamdev/CVS/Entries
new file mode 100644
index 0000000..15bfaed
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/streamdev/CVS/Entries
@@ -0,0 +1,3 @@
+/externremux.sh/1.1/Mon Apr 7 14:50:36 2008//
+/streamdevhosts.conf/1.2/Fri Feb 13 10:39:24 2009//
+D
diff --git a/plugins/streamdev/streamdev-cvs/streamdev/CVS/Repository b/plugins/streamdev/streamdev-cvs/streamdev/CVS/Repository
new file mode 100644
index 0000000..90fe6a1
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/streamdev/CVS/Repository
@@ -0,0 +1 @@
+streamdev/streamdev
diff --git a/plugins/streamdev/streamdev-cvs/streamdev/CVS/Root b/plugins/streamdev/streamdev-cvs/streamdev/CVS/Root
new file mode 100644
index 0000000..2c7f6ce
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/streamdev/CVS/Root
@@ -0,0 +1 @@
+:pserver:anoncvs@vdr-developer.org:/var/cvsroot
diff --git a/plugins/streamdev/streamdev-cvs/streamdev/externremux.sh b/plugins/streamdev/streamdev-cvs/streamdev/externremux.sh
new file mode 100755
index 0000000..e2b4156
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/streamdev/externremux.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# externremux.sh - sample remux script using mencoder for remuxing.
+#
+# Install this script as VDRCONFDIR/plugins/streamdev/externremux.sh
+#
+# The parameter STREAMQUALITY selects the default remux parameters. Adjust
+# to your needs and point your web browser to http://servername:3000/extern/
+# To select different remux parameters on the fly, insert a semicolon and
+# the name of the requested quality: http://servername:3000/extern;WLAN11/
+
+# CONFIG START
+ STREAMQUALITY="DSL6000" # DSL{1,2,3,6}000, LAN10, WLAN{11,54}, IPAQ
+ TMP=/tmp/externremux-${RANDOM:-$$}
+ MENCODER=mencoder
+# CONFIG END
+
+mkdir -p $TMP
+mkfifo $TMP/out.avi
+(trap "rm -rf $TMP" EXIT HUP INT TERM ABRT; cat $TMP/out.avi) &
+
+case ${1:-$STREAMQUALITY} in
+ DSL1000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=100 \
+ -oac mp3lame -lameopts preset=15:mode=3 -vf scale=160:104 \
+ -o $TMP/out.avi -- - &>$TMP/out.log ;;
+ DSL2000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=128 \
+ -oac mp3lame -lameopts preset=15:mode=3 -vf scale=160:104 \
+ -o $TMP/out.avi -- - &>$TMP/out.log ;;
+ DSL3000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=250 \
+ -oac mp3lame -lameopts preset=15:mode=3 -vf scale=320:208 \
+ -o $TMP/out.avi -- - &>$TMP/out.log ;;
+ DSL6000) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=350 \
+ -oac mp3lame -lameopts preset=15:mode=3 -vf scale=320:208 \
+ -o $TMP/out.avi -- - &>$TMP/out.log ;;
+ LAN10) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=4096 \
+ -oac mp3lame -lameopts preset=standard \
+ -o $TMP/out.avi -- - &>$TMP/out.log ;;
+ WLAN11) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=768 \
+ -oac mp3lame -lameopts preset=standard -vf scale=640:408 \
+ -o $TMP/out.avi -- - &>$TMP/out.log ;;
+ WLAN54) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=2048 \
+ -oac mp3lame -lameopts preset=standard \
+ -o $TMP/out.avi -- - &>$TMP/out.log ;;
+ IPAQ) exec $MENCODER -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=350 \
+ -oac mp3lame -lameopts preset=15:mode=3 -vf scale=320:208 \
+ -o $TMP/out.avi -- - &>$TMP/out.log ;;
+ *) touch $TMP/out.avi ;;
+esac
diff --git a/plugins/streamdev/streamdev-cvs/streamdev/streamdevhosts.conf b/plugins/streamdev/streamdev-cvs/streamdev/streamdevhosts.conf
new file mode 100644
index 0000000..49882a2
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/streamdev/streamdevhosts.conf
@@ -0,0 +1,14 @@
+#
+# streamdevhosts This file describes a number of host addresses that
+# are allowed to connect to the streamdev server running
+# with the Video Disk Recorder (VDR) on this system.
+# Syntax:
+#
+# IP-Address[/Netmask]
+#
+
+127.0.0.1 # always accept localhost
+#192.168.100.0/24 # any host on the local net
+#204.152.189.113 # a specific host
+#239.255.0.0/16 # uncomment for IGMP multicast streaming
+#0.0.0.0/0 # any host on any net (USE THIS WITH CARE!)
diff --git a/plugins/streamdev/streamdev-cvs/tools/CVS/Entries b/plugins/streamdev/streamdev-cvs/tools/CVS/Entries
new file mode 100644
index 0000000..b27688c
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/tools/CVS/Entries
@@ -0,0 +1,9 @@
+/select.c/1.4/Thu Mar 13 15:58:24 2008//
+/select.h/1.2/Mon Apr 2 10:32:35 2007//
+/socket.c/1.7/Fri Sep 4 13:24:30 2009//
+/socket.h/1.4/Fri Feb 13 10:39:24 2009//
+/source.c/1.7/Fri Jul 20 06:54:03 2007//
+/source.h/1.3/Tue Feb 8 19:54:52 2005//
+/tools.c/1.1.1.1/Thu Dec 30 22:44:30 2004//
+/tools.h/1.1.1.1/Thu Dec 30 22:44:30 2004//
+D
diff --git a/plugins/streamdev/streamdev-cvs/tools/CVS/Repository b/plugins/streamdev/streamdev-cvs/tools/CVS/Repository
new file mode 100644
index 0000000..ba0320d
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/tools/CVS/Repository
@@ -0,0 +1 @@
+streamdev/tools
diff --git a/plugins/streamdev/streamdev-cvs/tools/CVS/Root b/plugins/streamdev/streamdev-cvs/tools/CVS/Root
new file mode 100644
index 0000000..2c7f6ce
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/tools/CVS/Root
@@ -0,0 +1 @@
+:pserver:anoncvs@vdr-developer.org:/var/cvsroot
diff --git a/plugins/streamdev/streamdev-cvs/tools/select.c b/plugins/streamdev/streamdev-cvs/tools/select.c
new file mode 100644
index 0000000..2a3abe5
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/tools/select.c
@@ -0,0 +1,51 @@
+#include "tools/select.h"
+
+#include <vdr/tools.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+cTBSelect::cTBSelect(void) {
+ Clear();
+}
+
+cTBSelect::~cTBSelect() {
+}
+
+int cTBSelect::Select(uint TimeoutMs) {
+ struct timeval tv;
+ ssize_t res = 0;
+ int ms;
+
+ tv.tv_usec = (TimeoutMs % 1000) * 1000;
+ tv.tv_sec = TimeoutMs / 1000;
+ memcpy(m_FdsResult, m_FdsQuery, sizeof(m_FdsResult));
+
+ if (TimeoutMs == 0)
+ return ::select(m_MaxFiled + 1, &m_FdsResult[0], &m_FdsResult[1], NULL, &tv);
+
+ cTimeMs starttime;
+ ms = TimeoutMs;
+ while (ms > 0 && (res = ::select(m_MaxFiled + 1, &m_FdsResult[0], &m_FdsResult[1], NULL,
+ &tv)) == -1 && errno == EINTR) {
+ ms = TimeoutMs - starttime.Elapsed();
+ tv.tv_usec = (ms % 1000) * 1000;
+ tv.tv_sec = ms / 1000;
+ memcpy(m_FdsResult, m_FdsQuery, sizeof(m_FdsResult));
+ }
+ if (ms <= 0 || res == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ return res;
+}
+
+int cTBSelect::Select(void) {
+ ssize_t res;
+ do {
+ memcpy(m_FdsResult, m_FdsQuery, sizeof(m_FdsResult));
+ } while ((res = ::select(m_MaxFiled + 1, &m_FdsResult[0], &m_FdsResult[1], NULL, NULL)) == -1 && errno == EINTR);
+ return res;
+}
diff --git a/plugins/streamdev/streamdev-cvs/tools/select.h b/plugins/streamdev/streamdev-cvs/tools/select.h
new file mode 100644
index 0000000..a3622fd
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/tools/select.h
@@ -0,0 +1,75 @@
+#ifndef TOOLBOX_SELECT_H
+#define TOOLBOX_SELECT_H
+
+#include "tools/tools.h"
+
+#include <sys/types.h>
+
+/* cTBSelect provides an interface for polling UNIX-like file descriptors. */
+
+class cTBSelect {
+private:
+ int m_MaxFiled;
+
+ fd_set m_FdsQuery[2];
+ fd_set m_FdsResult[2];
+
+public:
+ cTBSelect(void);
+ virtual ~cTBSelect();
+
+ /* Clear() resets the object for use in a new Select() call. All file
+ descriptors and their previous states are invalidated. */
+ virtual void Clear(void);
+
+ /* Add() adds a file descriptor to be polled in the next Select() call.
+ That call polls if the file is readable if Output is set to false,
+ writeable otherwise. */
+ virtual bool Add(int Filed, bool Output = false);
+
+ /* Select() polls all descriptors added by Add() and returns as soon as
+ one of those changes state (gets readable/writeable), or after
+ TimeoutMs milliseconds, whichever happens first. It returns the number
+ of filedescriptors that have changed state. On error, -1 is returned
+ and errno is set appropriately. */
+ virtual int Select(uint TimeoutMs);
+
+ /* Select() polls all descriptors added by Add() and returns as soon as
+ one of those changes state (gets readable/writeable). It returns the
+ number of filedescriptors that have changed state. On error, -1 is
+ returned and errno is set appropriately. */
+ virtual int Select(void);
+
+ /* CanRead() returns true if the descriptor has changed to readable during
+ the last Select() call. Otherwise false is returned. */
+ virtual bool CanRead(int FileNo) const;
+
+ /* CanWrite() returns true if the descriptor has changed to writeable
+ during the last Select() call. Otherwise false is returned. */
+ virtual bool CanWrite(int FileNo) const;
+};
+
+inline void cTBSelect::Clear(void) {
+ FD_ZERO(&m_FdsQuery[0]);
+ FD_ZERO(&m_FdsQuery[1]);
+ m_MaxFiled = -1;
+}
+
+inline bool cTBSelect::Add(int Filed, bool Output /* = false */) {
+ if (Filed < 0) return false;
+ FD_SET(Filed, &m_FdsQuery[Output ? 1 : 0]);
+ if (Filed > m_MaxFiled) m_MaxFiled = Filed;
+ return true;
+}
+
+inline bool cTBSelect::CanRead(int FileNo) const {
+ if (FileNo < 0) return false;
+ return FD_ISSET(FileNo, &m_FdsResult[0]);
+}
+
+inline bool cTBSelect::CanWrite(int FileNo) const {
+ if (FileNo < 0) return false;
+ return FD_ISSET(FileNo, &m_FdsResult[1]);
+}
+
+#endif // TOOLBOX_SELECT_H
diff --git a/plugins/streamdev/streamdev-cvs/tools/socket.c b/plugins/streamdev/streamdev-cvs/tools/socket.c
new file mode 100644
index 0000000..5dde45a
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/tools/socket.c
@@ -0,0 +1,169 @@
+#include "tools/socket.h"
+
+#include <vdr/tools.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+// default class: best effort
+#define DSCP_BE 0
+// gold class (video): assured forwarding 4 with lowest drop precedence
+#define DSCP_AF41 34 << 2
+// premium class (voip): expedited forwarding
+#define DSCP_EF 46 << 2
+// actual DSCP value used
+#define STREAMDEV_DSCP DSCP_AF41
+
+cTBSocket::cTBSocket(int Type, int Protocol) {
+ memset(&m_LocalAddr, 0, sizeof(m_LocalAddr));
+ memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr));
+ m_Type = Type;
+ m_Protocol = Protocol;
+}
+
+cTBSocket::~cTBSocket() {
+ if (IsOpen()) Close();
+}
+
+bool cTBSocket::Connect(const std::string &Host, unsigned int Port) {
+ socklen_t len;
+ int socket;
+
+ if (IsOpen()) Close();
+
+ if ((socket = ::socket(PF_INET, m_Type, m_Protocol)) == -1)
+ return false;
+
+ m_LocalAddr.sin_family = AF_INET;
+ m_LocalAddr.sin_port = 0;
+ m_LocalAddr.sin_addr.s_addr = INADDR_ANY;
+ if (::bind(socket, (struct sockaddr*)&m_LocalAddr, sizeof(m_LocalAddr))
+ == -1) {
+ ::close(socket);
+ return false;
+ }
+
+ m_RemoteAddr.sin_family = AF_INET;
+ m_RemoteAddr.sin_port = htons(Port);
+ m_RemoteAddr.sin_addr.s_addr = inet_addr(Host.c_str());
+ if (::connect(socket, (struct sockaddr*)&m_RemoteAddr,
+ sizeof(m_RemoteAddr)) == -1) {
+ ::close(socket);
+ return false;
+ }
+
+ if (m_Type == SOCK_STREAM) {
+ len = sizeof(struct sockaddr_in);
+ if (::getpeername(socket, (struct sockaddr*)&m_RemoteAddr, &len) == -1) {
+ ::close(socket);
+ return false;
+ }
+ }
+
+ len = sizeof(struct sockaddr_in);
+ if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &len) == -1) {
+ ::close(socket);
+ return false;
+ }
+
+ if (!cTBSource::Open(socket)) {
+ ::close(socket);
+ return false;
+ }
+ return true;
+}
+
+bool cTBSocket::Listen(const std::string &Ip, unsigned int Port, int BackLog) {
+ int val;
+ socklen_t len;
+ int socket;
+
+ if (IsOpen()) Close();
+
+ if ((socket = ::socket(PF_INET, m_Type, m_Protocol)) == -1)
+ return false;
+
+ val = 1;
+ if (::setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) == -1)
+ return false;
+
+ m_LocalAddr.sin_family = AF_INET;
+ m_LocalAddr.sin_port = htons(Port);
+ m_LocalAddr.sin_addr.s_addr = inet_addr(Ip.c_str());
+ if (::bind(socket, (struct sockaddr*)&m_LocalAddr, sizeof(m_LocalAddr))
+ == -1)
+ return false;
+
+ len = sizeof(struct sockaddr_in);
+ if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &len) == -1)
+ return false;
+
+ if (m_Type == SOCK_STREAM && ::listen(socket, BackLog) == -1)
+ return false;
+
+ if (!cTBSource::Open(socket))
+ return false;
+
+ return true;
+}
+
+bool cTBSocket::Accept(const cTBSocket &Listener) {
+ socklen_t addrlen;
+ int socket;
+
+ if (IsOpen()) Close();
+
+ addrlen = sizeof(struct sockaddr_in);
+ if ((socket = ::accept(Listener, (struct sockaddr*)&m_RemoteAddr,
+ &addrlen)) == -1)
+ return false;
+
+ addrlen = sizeof(struct sockaddr_in);
+ if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &addrlen) == -1)
+ return false;
+
+ int sol=1;
+ // Ignore possible errors here, proceed as usual
+ ::setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &sol, sizeof(sol));
+
+ if (!cTBSource::Open(socket))
+ return false;
+
+ return true;
+}
+
+RETURNS(cTBSocket, cTBSocket::Accept(void) const, ret)
+ ret.Accept(*this);
+RETURN(ret)
+
+bool cTBSocket::Close(void) {
+ bool ret = true;
+
+ if (!IsOpen())
+ ERRNUL(EBADF);
+
+ if (::close(*this) == -1)
+ ret = false;
+
+ if (!cTBSource::Close())
+ ret = false;
+
+ memset(&m_LocalAddr, 0, sizeof(m_LocalAddr));
+ memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr));
+
+ return ret;
+}
+
+bool cTBSocket::Shutdown(int how) {
+ if (!IsOpen())
+ ERRNUL(EBADF);
+
+ return ::shutdown(*this, how) != -1;
+}
+
+bool cTBSocket::SetDSCP(void) {
+ int dscp = STREAMDEV_DSCP;
+ return ::setsockopt(*this, IPPROTO_IP, IP_TOS, &dscp, sizeof(dscp)) != -1;
+}
diff --git a/plugins/streamdev/streamdev-cvs/tools/socket.h b/plugins/streamdev/streamdev-cvs/tools/socket.h
new file mode 100644
index 0000000..3dc7a33
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/tools/socket.h
@@ -0,0 +1,119 @@
+#ifndef TOOLBOX_SOCKET_H
+#define TOOLBOX_SOCKET_H
+
+#include "tools/tools.h"
+#include "tools/source.h"
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string>
+
+/* cTBSocket provides a cTBSource-derived interface for input and output on
+ TCP/IPv4-sockets. */
+
+class cTBSocket: public cTBSource {
+private:
+ struct sockaddr_in m_LocalAddr;
+ struct sockaddr_in m_RemoteAddr;
+
+ int m_Type;
+ int m_Protocol;
+
+public:
+ cTBSocket(int Type = SOCK_STREAM, int Protocol = 0);
+ virtual ~cTBSocket();
+
+ /* See cTBSource::SysRead()
+ Reimplemented for TCP/IPv4 sockets. */
+ virtual ssize_t SysRead(void *Buffer, size_t Length) const;
+
+ /* See cTBSource::SysWrite()
+ Reimplemented for TCP/IPv4 sockets. */
+ virtual ssize_t SysWrite(const void *Buffer, size_t Length) const;
+
+ /* Connect() tries to connect an available local socket to the port given
+ by Port of the target host given by Host in numbers-and-dots notation
+ (i.e. "212.43.45.21"). Returns true if the connection attempt was
+ successful and false otherwise, setting errno appropriately. */
+ virtual bool Connect(const std::string &Host, uint Port);
+
+ /* Shutdown() shuts down one or both ends of a socket. If called with How
+ set to SHUT_RD, further reads on this socket will be denied. If called
+ with SHUT_WR, all writes are denied. Called with SHUT_RDWR, all firther
+ action on this socket will be denied. Returns true on success and false
+ otherwise, setting errno appropriately. */
+ virtual bool Shutdown(int How);
+
+ /* Close() closes the associated socket and releases all structures.
+ Returns true on success and false otherwise, setting errno
+ appropriately. The object is in the closed state afterwards, regardless
+ of any errors. */
+ virtual bool Close(void);
+
+ /* Listen() listens on the local port Port for incoming connections. The
+ BackLog parameter defines the maximum length the queue of pending
+ connections may grow to. Returns true if the object is listening on
+ the specified port and false otherwise, setting errno appropriately. */
+ virtual bool Listen(const std::string &Ip, uint Port, int BackLog);
+
+ /* Accept() returns a newly created cTBSocket, which is connected to the
+ first connection request on the queue of pending connections of a
+ listening socket. If no connection request was pending, or if any other
+ error occured, the resulting cTBSocket is closed. */
+ virtual cTBSocket Accept(void) const;
+
+ /* Accept() extracts the first connection request on the queue of pending
+ connections of the listening socket Listener and connects it to this
+ object. Returns true on success and false otherwise, setting errno to
+ an appropriate value. */
+ virtual bool Accept(const cTBSocket &Listener);
+
+ /* Sets DSCP sockopt */
+ bool SetDSCP(void);
+
+ /* LocalPort() returns the port number this socket is connected to locally.
+ The result is undefined for a non-open socket. */
+ int LocalPort(void) const { return ntohs(m_LocalAddr.sin_port); }
+
+ /* RemotePort() returns the port number this socket is connected to on the
+ remote side. The result is undefined for a non-open socket. */
+ int RemotePort(void) const { return ntohs(m_RemoteAddr.sin_port); }
+
+ /* LocalIp() returns the internet address in numbers-and-dots notation of
+ the interface this socket is connected to locally. This can be
+ "0.0.0.0" for a listening socket listening to all interfaces. If the
+ socket is in its closed state, the result is undefined. */
+ std::string LocalIp(void) const { return inet_ntoa(m_LocalAddr.sin_addr); }
+
+ /* RemoteIp() returns the internet address in numbers-and-dots notation of
+ the interface this socket is connected to on the remote side. If the
+ socket is in its closed state, the result is undefined. */
+ std::string RemoteIp(void) const { return inet_ntoa(m_RemoteAddr.sin_addr); }
+
+ in_addr_t LocalIpAddr(void) const { return m_LocalAddr.sin_addr.s_addr; }
+ in_addr_t RemoteIpAddr(void) const { return m_RemoteAddr.sin_addr.s_addr; }
+
+ int Type(void) const { return m_Type; }
+};
+
+inline ssize_t cTBSocket::SysRead(void *Buffer, size_t Length) const {
+ if (m_Type == SOCK_STREAM)
+ return ::recv(*this, Buffer, Length, 0);
+ else {
+ socklen_t len = sizeof(m_RemoteAddr);
+ return ::recvfrom(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, &len);
+ }
+}
+
+inline ssize_t cTBSocket::SysWrite(const void *Buffer, size_t Length) const {
+ return ::send(*this, Buffer, Length, 0);
+ if (m_Type == SOCK_STREAM)
+ return ::send(*this, Buffer, Length, 0);
+ else {
+ socklen_t len = sizeof(m_RemoteAddr);
+ return ::sendto(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, len);
+ }
+}
+
+#endif // TOOLBOX_SOCKET_H
diff --git a/plugins/streamdev/streamdev-cvs/tools/source.c b/plugins/streamdev/streamdev-cvs/tools/source.c
new file mode 100644
index 0000000..8a6b5f5
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/tools/source.c
@@ -0,0 +1,171 @@
+#include "tools/source.h"
+#include "tools/select.h"
+#include "common.h"
+
+#include <vdr/tools.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+cTBSource::cTBSource(void) {
+ m_BytesRead = 0;
+ m_BytesWritten = 0;
+ m_Filed = -1;
+}
+
+bool cTBSource::Open(int Filed, bool IsUnixFd) {
+ if (IsOpen())
+ Close();
+
+ m_Filed = Filed;
+ if (IsUnixFd && ::fcntl(m_Filed, F_SETFL, O_NONBLOCK) == -1)
+ return false;
+
+ return true;
+}
+
+cTBSource::~cTBSource() {
+}
+
+bool cTBSource::Close(void) {
+ if (!IsOpen()) {
+ errno = EBADF;
+ return false;
+ }
+
+ m_Filed = -1;
+ return true;
+}
+
+ssize_t cTBSource::Read(void *Buffer, size_t Length) {
+ ssize_t res;
+ while ((res = SysRead(Buffer, Length)) < 0 && errno == EINTR)
+ errno = 0;
+ if (res > 0) m_BytesRead += res;
+ return res;
+}
+
+ssize_t cTBSource::Write(const void *Buffer, size_t Length) {
+ ssize_t res;
+ while ((res = SysWrite(Buffer, Length)) < 0 && errno == EINTR)
+ errno = 0;
+ if (res > 0) m_BytesWritten += res;
+ return res;
+}
+
+bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) {
+ cTBSelect sel;
+ int ms, offs;
+
+ cTimeMs starttime;
+ ms = TimeoutMs;
+ offs = 0;
+ sel.Clear();
+ sel.Add(m_Filed, true);
+ while (Length > 0) {
+ int b;
+
+ if (sel.Select(ms) == -1)
+ return false;
+
+ if (sel.CanWrite(m_Filed)) {
+ if ((b = Write((char*)Buffer + offs, Length)) == -1)
+ return false;
+ offs += b;
+ Length -= b;
+ }
+
+ ms = TimeoutMs - starttime.Elapsed();
+ if (ms <= 0) {
+ errno = ETIMEDOUT;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cTBSource::SafeWrite(const void *Buffer, size_t Length) {
+ cTBSelect sel;
+ int offs;
+
+ offs = 0;
+ sel.Clear();
+ sel.Add(m_Filed, true);
+ while (Length > 0) {
+ int b;
+
+ if (sel.Select() == -1)
+ return false;
+
+ if (sel.CanWrite(m_Filed)) {
+ if ((b = Write((char*)Buffer + offs, Length)) == -1)
+ return false;
+ offs += b;
+ Length -= b;
+ }
+ }
+ return true;
+}
+
+ssize_t cTBSource::ReadUntil(void *Buffer, size_t Length, const char *Seq,
+ uint TimeoutMs) {
+ int ms;
+ size_t len;
+ cTBSelect sel;
+
+ if ((len = m_LineBuffer.find(Seq)) != (size_t)-1) {
+ if (len > Length) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(Buffer, m_LineBuffer.data(), len);
+ m_LineBuffer.erase(0, len + strlen(Seq));
+ Dprintf("ReadUntil: Served from Linebuffer: %d, |%.*s|\n", len, len - 1,
+ (char*)Buffer);
+ return len;
+ }
+
+ cTimeMs starttime;
+ ms = TimeoutMs;
+ sel.Clear();
+ sel.Add(m_Filed, false);
+ while (m_LineBuffer.size() < BUFSIZ) {
+
+ if (sel.Select(ms) == -1)
+ return -1;
+
+ if (sel.CanRead(m_Filed)) {
+ int b;
+
+ len = m_LineBuffer.size();
+ m_LineBuffer.resize(BUFSIZ);
+ if ((b = Read((char*)m_LineBuffer.data() + len, BUFSIZ - len)) == -1) {
+ m_LineBuffer.resize(len);
+ return -1;
+ }
+ m_LineBuffer.resize(len + b);
+
+ if ((len = m_LineBuffer.find(Seq)) != (size_t)-1) {
+ if (len > Length) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(Buffer, m_LineBuffer.data(), len);
+ m_LineBuffer.erase(0, len + strlen(Seq));
+ Dprintf("ReadUntil: Served from Linebuffer: %d, |%.*s|\n", len, len - 1,
+ (char*)Buffer);
+ return len;
+ }
+ }
+
+ ms = TimeoutMs - starttime.Elapsed();
+ if (ms <= 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+ errno = ENOBUFS;
+ return -1;
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/tools/source.h b/plugins/streamdev/streamdev-cvs/tools/source.h
new file mode 100644
index 0000000..09c4bf3
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/tools/source.h
@@ -0,0 +1,109 @@
+#ifndef TOOLBOX_SOURCE_H
+#define TOOLBOX_SOURCE_H
+
+#include "tools/tools.h"
+
+#include <sys/types.h>
+#include <string>
+
+/* cTBSource provides an abstract interface for input and output. It can
+ be used to have common access to different types of UNIX-files. */
+
+class cTBSource {
+private:
+ int m_Filed;
+
+ size_t m_BytesRead;
+ size_t m_BytesWritten;
+
+ std::string m_LineBuffer;
+
+public:
+ cTBSource(void);
+ virtual ~cTBSource();
+
+ /* SysRead() implements the low-level read on the source. It will store
+ data into the area pointed to by Buffer, which is at least Length
+ bytes in size. It will return the exact number of bytes read (which
+ can be fewer than requested). On error, -1 is returned, and errno
+ is set to an appropriate value. */
+ virtual ssize_t SysRead(void *Buffer, size_t Length) const = 0;
+
+ /* SysWrite() implements the low-level write on the source. It will write
+ at most Length bytes of the data pointed to by Buffer. It will return
+ the exact number of bytes written (which can be fewer than requested).
+ On error, -1 is returned, and errno is set to an appropriate value. */
+ virtual ssize_t SysWrite(const void *Buffer, size_t Length) const = 0;
+
+ /* IsOpen() returns true, if this source refers to a valid descriptor.
+ It is not checked whether this source is really open, so only if
+ opened by the appropriate Methods this function will return the
+ correct value */
+ virtual bool IsOpen(void) const { return m_Filed != -1; }
+
+ /* Open() associates this source with the descriptor Filed, setting it
+ to non-blocking mode if IsUnixFd in true. Returns true on success,
+ and false on error, setting errno to appropriately.
+ If you want to implement sources that can't be represented by UNIX
+ filedescriptors, you can use Filed to store any useful information
+ about the source.
+ This must be called by any derivations in an appropriate Method (like
+ open for files, connect for sockets). */
+ virtual bool Open(int Filed, bool IsUnixFd = true);
+
+ /* Close() resets the source to the uninitialized state (IsOpen() == false)
+ and must be called by any derivations after really closing the source.
+ Returns true on success and false on error, setting errno appropriately.
+ The object is in closed state afterwards, even if an error occured. */
+ virtual bool Close(void);
+
+ /* Read() reads at most Length bytes into the storage pointed to by Buffer,
+ which must be at least Length bytes in size, using the SysRead()-
+ Interface. It retries if an EINTR occurs (i.e. the low-level call was
+ interrupted). It returns the exact number of bytes read (which can be
+ fewer than requested). On error, -1 is returned, and errno is set
+ appropriately. */
+ ssize_t Read(void *Buffer, size_t Length);
+
+ /* Write() writes at most Length bytes from the storage pointed to by
+ Buffer, using the SysWrite()-Interface. It retries if EINTR occurs
+ (i.e. the low-level call was interrupted). It returns the exact number
+ of bytes written (which can be fewer than requested). On error, -1 is
+ returned and errno is set appropriately. */
+ ssize_t Write(const void *Buffer, size_t Length);
+
+ /* TimedWrite() tries to write Length bytes from the storage pointed to by
+ Buffer within the time specified by TimeoutMs, using the Write()-
+ Interface. On success, true is returned. On error, false is returned
+ and errno is set appropriately. TimedRead only works on UNIX file
+ descriptor sources. */
+ bool TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs);
+
+ bool SafeWrite(const void *Buffer, size_t Length);
+
+ /* ReadUntil() tries to read at most Length bytes into the storage pointed
+ to by Buffer, which must be at least Length bytes in size, within the
+ time specified by TimeoutMs, using the Read()-Interface. Reading stops
+ after the character sequence Seq has been read and on end-of-file.
+ Returns the number of bytes read (if that is equal to Length, you have
+ to check if the buffer ends with Seq), or -1 on error, in which case
+ errno is set appropriately. */
+ ssize_t ReadUntil(void *Buffer, size_t Length, const char *Seq,
+ uint TimeoutMs);
+
+ /* BytesRead() returns the exact number of bytes read through the Read()
+ method since Close() has been called on this source (or since its
+ creation). */
+ size_t BytesRead(void) const { return m_BytesRead; }
+
+ /* BytesWritten() returns the exact number of bytes written through the
+ Write() method since Close() has been called on this source (or since
+ its creation). */
+ size_t BytesWritten(void) const { return m_BytesWritten; }
+
+ /* operator int() returns the descriptor (or informative number) associated
+ with this source. */
+ operator int() const { return m_Filed; }
+};
+
+#endif // TOOLBOX_SOURCE_H
diff --git a/plugins/streamdev/streamdev-cvs/tools/tools.c b/plugins/streamdev/streamdev-cvs/tools/tools.c
new file mode 100644
index 0000000..fa813fa
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/tools/tools.c
@@ -0,0 +1,12 @@
+#include "tools/tools.h"
+
+#include <sys/time.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+void *operator new(size_t nSize, void *p) throw () {
+ return p;
+}
+
diff --git a/plugins/streamdev/streamdev-cvs/tools/tools.h b/plugins/streamdev/streamdev-cvs/tools/tools.h
new file mode 100644
index 0000000..ab00c60
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs/tools/tools.h
@@ -0,0 +1,67 @@
+#ifndef TOOLBOX_TOOLS_H
+#define TOOLBOX_TOOLS_H
+
+//#include <stdio.h>
+//#include <iostream>
+#include <sys/types.h>
+
+//#define KILOBYTE(x) ((x)*1024)
+//#define MEGABYTE(x) (KILOBYTE(x)*1024)
+
+//typedef unsigned int uint;
+//typedef unsigned long ulong;
+typedef unsigned char uchar;
+//typedef unsigned short ushort;
+
+// Special constructor for CreateElements
+void *operator new(size_t, void*) throw ();
+
+#ifdef TOOLBOX_DEBUG
+# define ASSERT(x) if ((x)) cerr << "Warning: ASSERT failed At " << __FILE__ << ":" << __LINE__ << " ["#x"]" << endl
+# define CHECK_PTR(x) if (!(x)) cerr << "Warning: Pointer is NULL At " << __FILE__ << ":" << __LINE__ << endl;
+# define CHECK_NEXT_ALLOC() _checkNextAlloc()
+# define DPRINT(x...) LOGi(x)
+#else
+# define ASSERT(x)
+# define CHECK_PTR(x)
+# define CHECK_NEXT_ALLOC()
+# define DPRINT(x...)
+#endif
+
+#define ERRNUL(e) {errno=e;return 0;}
+#define ERRSYS(e) {errno=e;return -1;}
+
+/* RETURNS() and RETURN() are macros that can be used if a class object is
+ being returned. They make use of the GNU C-Compiler's named return value
+ feature, if available. In this case, the class object isn't returned and
+ copied, but the result itself is filled.
+
+ RETURNS(ReturnType, FunctionDeclaration, Result)
+ ... function-body working on Result ...
+ RETURN(Result)
+
+ A function like this (cXYZ is a class type):
+
+ cXYZ myfunction(int a, char *b) {
+ cXYZ result;
+ ... something happens with result ...
+ return result;
+ }
+
+ can be written like this:
+
+ RETURNS(cXYZ, myfunction(int a, char *b), result)
+ ... something happens with result ...
+ RETURN(result)
+
+ DISABLED SINCE GCC 3.x
+*/
+//#ifdef __GNUC__
+//# define RETURNS(t,x,r) t x return r {
+//# define RETURN(x) }
+//#else
+# define RETURNS(t,x,r) t x { t r;
+# define RETURN(x) return x; }
+//#endif
+
+#endif // TOOLBOX_TOOLS_H
diff --git a/plugins/streamdev/streamdev-cvs100210-ReplaceRecordingStreaming.patch b/plugins/streamdev/streamdev-cvs100210-ReplaceRecordingStreaming.patch
new file mode 100644
index 0000000..a5cc5d6
--- /dev/null
+++ b/plugins/streamdev/streamdev-cvs100210-ReplaceRecordingStreaming.patch
@@ -0,0 +1,581 @@
+diff -NaurwB streamdev/Makefile streamdev-patched/Makefile
+--- streamdev/Makefile 2009-11-04 12:12:20.000000000 +0100
++++ streamdev-patched/Makefile 2010-02-09 15:58:13.000000000 +0100
+@@ -65,7 +65,7 @@
+ server/server.o server/component.o server/connection.o \
+ server/componentVTP.o server/componentHTTP.o server/componentIGMP.o \
+ server/connectionVTP.o server/connectionHTTP.o server/connectionIGMP.o \
+- server/streamer.o server/livestreamer.o server/livefilter.o \
++ server/streamer.o server/livestreamer.o server/livefilter.o server/recordingstreamer.o \
+ server/suspend.o server/setup.o server/menuHTTP.o server/recplayer.o \
+ remux/tsremux.o remux/ts2pes.o remux/ts2ps.o remux/ts2es.o remux/extern.o
+
+diff -NaurwB streamdev/server/connectionVTP.c streamdev-patched/server/connectionVTP.c
+--- streamdev/server/connectionVTP.c 2010-01-29 13:03:02.000000000 +0100
++++ streamdev-patched/server/connectionVTP.c 2010-02-10 08:56:22.000000000 +0100
+@@ -4,6 +4,7 @@
+
+ #include "server/connectionVTP.h"
+ #include "server/livestreamer.h"
++#include "server/recordingstreamer.h"
+ #include "server/suspend.h"
+ #include "setup.h"
+
+@@ -741,11 +742,11 @@
+ m_FilterSocket(NULL),
+ m_FilterStreamer(NULL),
+ m_RecSocket(NULL),
++ m_RecStreamer(NULL),
+ m_DataSocket(NULL),
+ m_LastCommand(NULL),
+ m_StreamType(stTSPIDS),
+ m_FiltersSupport(false),
+- m_RecPlayer(NULL),
+ m_LSTEHandler(NULL),
+ m_LSTCHandler(NULL),
+ m_LSTTHandler(NULL),
+@@ -759,6 +760,7 @@
+ free(m_LastCommand);
+ delete m_LiveStreamer;
+ delete m_LiveSocket;
++ delete m_RecStreamer;
+ delete m_RecSocket;
+ delete m_FilterStreamer;
+ delete m_FilterSocket;
+@@ -767,7 +769,6 @@
+ delete m_LSTCHandler;
+ delete m_LSTEHandler;
+ delete m_LSTRHandler;
+- delete m_RecPlayer;
+ }
+
+ inline bool cConnectionVTP::Abort(void) const
+@@ -833,9 +834,10 @@
+ if (strcasecmp(Cmd, "CAPS") == 0) return CmdCAPS(param);
+ else if (strcasecmp(Cmd, "PROV") == 0) return CmdPROV(param);
+ else if (strcasecmp(Cmd, "PORT") == 0) return CmdPORT(param);
+- else if (strcasecmp(Cmd, "READ") == 0) return CmdREAD(param);
+ else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(param);
+ else if (strcasecmp(Cmd, "PLAY") == 0) return CmdPLAY(param);
++ else if (strcasecmp(Cmd, "SEEK") == 0) return CmdSEEK(param);
++ else if (strcasecmp(Cmd, "SIZE") == 0) return CmdSIZE(param);
+ else if (strcasecmp(Cmd, "ADDP") == 0) return CmdADDP(param);
+ else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(param);
+ else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(param);
+@@ -1012,6 +1014,8 @@
+
+ if (!m_RecSocket->SetDSCP())
+ LOG_ERROR_STR("unable to set DSCP sockopt");
++ if(m_RecSocket)
++ m_RecStreamer->Start(m_RecSocket);
+
+ return Respond(220, "Port command ok, data connection opened");
+ break;
+@@ -1034,35 +1038,6 @@
+ }
+ }
+
+-bool cConnectionVTP::CmdREAD(char *Opts)
+-{
+- if (*Opts) {
+- char *tail;
+- uint64_t position = strtoll(Opts, &tail, 10);
+- if (tail && tail != Opts) {
+- tail = skipspace(tail);
+- if (tail && tail != Opts) {
+- int size = strtol(tail, NULL, 10);
+- uint8_t* data = (uint8_t*)malloc(size+4);
+- unsigned long count_readed = m_RecPlayer->getBlock(data, position, size);
+- unsigned long count_written = m_RecSocket->SysWrite(data, count_readed);
+-
+- free(data);
+- return Respond(220, "%lu Bytes submitted", count_written);
+- }
+- else {
+- return Respond(501, "Missing position");
+- }
+- }
+- else {
+- return Respond(501, "Missing size");
+- }
+- }
+- else {
+- return Respond(501, "Missing position");
+- }
+-}
+-
+ bool cConnectionVTP::CmdTUNE(char *Opts)
+ {
+ const cChannel *chan;
+@@ -1096,27 +1071,40 @@
+
+ bool cConnectionVTP::CmdPLAY(char *Opts)
+ {
+- if (*Opts) {
+- if (isnumber(Opts)) {
+- cRecording *recording = Recordings.Get(strtol(Opts, NULL, 10) - 1);
+- if (recording) {
+- if (m_RecPlayer) {
+- delete m_RecPlayer;
+- }
+- m_RecPlayer = new RecPlayer(recording);
+- return Respond(220, "%llu (Bytes), %u (Frames)", (long long unsigned int) m_RecPlayer->getLengthBytes(), (unsigned int) m_RecPlayer->getLengthFrames());
+- }
+- else {
+- return Respond(550, "Recording \"%s\" not found", Opts);
+- }
+- }
+- else {
+- return Respond(500, "Use: PLAY record");
++ const cRecording *recording;
++ cDevice *dev;
++
++ if ((recording = Recordings.Get(strtol(Opts, NULL, 10) - 1)) == NULL)
++ return Respond(550, "Undefined recording \"%s\"", Opts);
++
++ delete m_RecStreamer;
++ m_RecStreamer = new cStreamdevRecStreamer(recording);
++ if(m_RecSocket)
++ m_RecStreamer->Start(m_RecSocket);
++
++ return Respond(220, "Recording opened");
+ }
++
++bool cConnectionVTP::CmdSEEK(char *Opts)
++{
++ if (m_RecStreamer)
++ {
++ uint64_t TotalBytesWritten = m_RecStreamer->getTotalBytesWritten();
++ uint64_t newPosition = atoll(Opts);
++ m_RecStreamer->seekPosition(newPosition);
++
++ return Respond(220, "%llu (TCP Stack Position) %llu (New Position)", TotalBytesWritten, newPosition);
+ }
+- else {
+- return Respond(500, "Use: PLAY record");
++
++ return Respond(550, "Curretly no record playing");
+ }
++
++bool cConnectionVTP::CmdSIZE(char *Opts)
++{
++ if (m_RecStreamer)
++ return Respond(220, "%llu (Bytes), %u (Frames)", (long long unsigned int) m_RecStreamer->getLengthBytes(), (unsigned int) m_RecStreamer->getLengthFrames());
++
++ return Respond(550, "Curretly no record playing");
+ }
+
+ bool cConnectionVTP::CmdADDP(char *Opts)
+@@ -1215,7 +1203,7 @@
+ DELETENULL(m_FilterSocket);
+ break;
+ case siReplay:
+- DELETENULL(m_RecPlayer);
++ DELETENULL(m_RecStreamer);
+ DELETENULL(m_RecSocket);
+ break;
+ case siDataRespond:
+diff -NaurwB streamdev/server/connectionVTP.h streamdev-patched/server/connectionVTP.h
+--- streamdev/server/connectionVTP.h 2009-07-01 12:46:16.000000000 +0200
++++ streamdev-patched/server/connectionVTP.h 2010-02-09 18:08:35.000000000 +0100
+@@ -7,6 +7,7 @@
+ class cTBSocket;
+ class cStreamdevLiveStreamer;
+ class cStreamdevFilterStreamer;
++class cStreamdevRecStreamer;
+ class cLSTEHandler;
+ class cLSTCHandler;
+ class cLSTTHandler;
+@@ -24,12 +25,12 @@
+ cTBSocket *m_FilterSocket;
+ cStreamdevFilterStreamer *m_FilterStreamer;
+ cTBSocket *m_RecSocket;
++ cStreamdevRecStreamer *m_RecStreamer;
+ cTBSocket *m_DataSocket;
+
+ char *m_LastCommand;
+ eStreamType m_StreamType;
+ bool m_FiltersSupport;
+- RecPlayer *m_RecPlayer;
+
+ // Members adopted for SVDRP
+ cLSTEHandler *m_LSTEHandler;
+@@ -56,9 +57,10 @@
+ bool CmdCAPS(char *Opts);
+ bool CmdPROV(char *Opts);
+ bool CmdPORT(char *Opts);
+- bool CmdREAD(char *Opts);
+ bool CmdTUNE(char *Opts);
+ bool CmdPLAY(char *Opts);
++ bool CmdSEEK(char *Opts);
++ bool CmdSIZE(char *Opts);
+ bool CmdADDP(char *Opts);
+ bool CmdDELP(char *Opts);
+ bool CmdADDF(char *Opts);
+diff -NaurwB streamdev/server/recordingstreamer.c streamdev-patched/server/recordingstreamer.c
+--- streamdev/server/recordingstreamer.c 1970-01-01 01:00:00.000000000 +0100
++++ streamdev-patched/server/recordingstreamer.c 2010-02-10 08:58:01.000000000 +0100
+@@ -0,0 +1,89 @@
++#include <vdr/recording.h>
++
++#include "tools/socket.h"
++#include "tools/select.h"
++
++#include "server/recordingstreamer.h"
++#include "recplayer.h"
++#include "common.h"
++
++// --- cStreamdevRecStreamer -------------------------------------------------
++
++cStreamdevRecStreamer::cStreamdevRecStreamer(const cRecording *Recording):
++ cThread("streamdev-recordingstreaming"),
++ m_Recording(Recording),
++ m_RecPlayer(new cRecPlayer(Recording)),
++ m_Position(0),
++ m_TotalBytesWritten(0)
++{
++}
++
++cStreamdevRecStreamer::~cStreamdevRecStreamer()
++{
++ Dprintf("Desctructing Recording streamer\n");
++ Stop();
++
++ DELETENULL(m_RecPlayer);
++}
++
++void cStreamdevRecStreamer::Start(cTBSocket *Socket)
++{
++ Dprintf("start streamer\n");
++ m_Socket = Socket;
++ cThread::Start();
++}
++
++void cStreamdevRecStreamer::Stop(void)
++{
++ if (Running()) {
++ Dprintf("stopping streamer\n");
++ Cancel(3);
++ }
++}
++
++uint64_t cStreamdevRecStreamer::getLengthBytes()
++{
++ return m_RecPlayer->getLengthBytes();
++}
++
++uint32_t cStreamdevRecStreamer::getLengthFrames()
++{
++ return m_RecPlayer->getLengthFrames();
++}
++
++void cStreamdevRecStreamer::seekPosition(uint64_t position)
++{
++ m_Position = position;
++}
++
++void cStreamdevRecStreamer::Action(void)
++{
++ cTBSelect sel;
++ uint8_t data[32768];
++ Dprintf("Writer start\n");
++
++ sel.Clear();
++ sel.Add(*m_Socket, true);
++ while (Running()) {
++
++ if (sel.Select(15000) == -1) {
++ esyslog("ERROR: streamdev-server: couldn't send recording data: %m");
++ continue; /* Continue here instead of break, the recording playback can be paused */
++ }
++
++ if (sel.CanWrite(*m_Socket)) {
++ int written;
++ unsigned long readed = m_RecPlayer->getBlock(&*data, m_Position, sizeof(data));
++ if (readed <= 0)
++ continue;
++
++ if ((written = m_Socket->Write(&*data, readed)) == -1) {
++ esyslog("ERROR: streamdev-server: couldn't send %d bytes: %m", written);
++ break;
++ }
++
++ m_Position += written;
++ m_TotalBytesWritten += written;
++ }
++ }
++}
+diff -NaurwB streamdev/server/recordingstreamer.h streamdev-patched/server/recordingstreamer.h
+--- streamdev/server/recordingstreamer.h 1970-01-01 01:00:00.000000000 +0100
++++ streamdev-patched/server/recordingstreamer.h 2010-02-10 08:58:13.000000000 +0100
+@@ -0,0 +1,38 @@
++#ifndef VDR_STREAMDEV_RECORDINGSTREAMER_H
++#define VDR_STREAMDEV_RECORDINGSTREAMER_H
++
++#include <vdr/thread.h>
++#include <vdr/config.h>
++#include <vdr/receiver.h>
++
++#include "common.h"
++
++class cRecording;
++class cRecPlayer;
++
++// --- cStreamdevRecStreamer -------------------------------------------------
++
++class cStreamdevRecStreamer: public cThread {
++private:
++ const cRecording *m_Recording;
++ cRecPlayer *m_RecPlayer;
++ cTBSocket *m_Socket;
++ uint64_t m_Position;
++ uint64_t m_TotalBytesWritten;
++
++protected:
++ virtual void Action(void);
++
++public:
++ cStreamdevRecStreamer(const cRecording *Recording);
++ virtual ~cStreamdevRecStreamer();
++
++ void Start(cTBSocket *Socket);
++ void Stop(void);
++ uint64_t getTotalBytesWritten() { return m_TotalBytesWritten; }
++ uint64_t getLengthBytes();
++ uint32_t getLengthFrames();
++ void seekPosition(uint64_t position);
++};
++
++#endif // VDR_STREAMDEV_RECORDINGSTREAMER_H
+diff -NaurwB streamdev/server/recplayer.c streamdev-patched/server/recplayer.c
+--- streamdev/server/recplayer.c 2009-07-01 13:00:49.000000000 +0200
++++ streamdev-patched/server/recplayer.c 2010-02-10 08:24:01.000000000 +0100
+@@ -24,7 +24,7 @@
+ #define _XOPEN_SOURCE 600
+ #include <fcntl.h>
+
+-RecPlayer::RecPlayer(cRecording* rec)
++cRecPlayer::cRecPlayer(const cRecording* rec)
+ {
+ file = NULL;
+ fileOpen = 0;
+@@ -44,7 +44,7 @@
+ scan();
+ }
+
+-void RecPlayer::scan()
++void cRecPlayer::scan()
+ {
+ if (file) fclose(file);
+ totalLength = 0;
+@@ -60,7 +60,7 @@
+
+ #if APIVERSNUM < 10703
+ snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
+- //log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
++ //log->log("cRecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
+ file = fopen(fileName, "r");
+ #else
+ snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
+@@ -77,7 +77,7 @@
+ fseek(file, 0, SEEK_END);
+ totalLength += ftell(file);
+ totalFrames = indexFile->Last();
+- //log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
++ //log->log("cRecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
+ segments[i]->end = totalLength;
+ fclose(file);
+ }
+@@ -85,15 +85,15 @@
+ file = NULL;
+ }
+
+-RecPlayer::~RecPlayer()
++cRecPlayer::~cRecPlayer()
+ {
+- //log->log("RecPlayer", Log::DEBUG, "destructor");
++ //log->log("cRecPlayer", Log::DEBUG, "destructor");
+ int i = 1;
+ while(segments[i++]) delete segments[i];
+ if (file) fclose(file);
+ }
+
+-int RecPlayer::openFile(int index)
++int cRecPlayer::openFile(int index)
+ {
+ if (file) fclose(file);
+
+@@ -113,7 +113,7 @@
+
+ snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
+ isyslog("openFile called for index %i string:%s", index, fileName);
+- //log->log("RecPlayer", Log::DEBUG, "openFile called for index %i string:%s", index, fileName);
++ //log->log("cRecPlayer", Log::DEBUG, "openFile called for index %i string:%s", index, fileName);
+
+ file = fopen(fileName, "r");
+ if (file)
+@@ -122,38 +122,58 @@
+ return 1;
+ }
+
+- //log->log("RecPlayer", Log::DEBUG, "file failed to open");
++ //log->log("cRecPlayer", Log::DEBUG, "file failed to open");
+ fileOpen = 0;
+ return 0;
+ }
+
+-uint64_t RecPlayer::getLengthBytes()
++uint64_t cRecPlayer::getLengthBytes()
+ {
++ int totalLength = 0;
++ char fileName[2048];
++ struct stat st;
++
++ for(int i = 1; i < 1000; i++)
++ {
++#if APIVERSNUM < 10703
++ snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
++#else
++ snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
++ if (stat(fileName, &st) < 0) {
++ snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
++ }
++#endif
++
++ if (stat(fileName, &st) == 0) {
++ totalLength += st.st_size;
++ }
++ }
++
+ return totalLength;
+ }
+
+-uint32_t RecPlayer::getLengthFrames()
++uint32_t cRecPlayer::getLengthFrames()
+ {
+ return totalFrames;
+ }
+
+-unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsigned long amount)
++unsigned long cRecPlayer::getBlock(uint8_t* buffer, uint64_t position, unsigned long amount)
+ {
+ if ((amount > totalLength) || (amount > 500000))
+ {
+- //log->log("RecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount);
++ //log->log("cRecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount);
+ return 0;
+ }
+
+ if (position >= totalLength)
+ {
+- //log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
++ //log->log("cRecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
+ return 0;
+ }
+
+ if ((position + amount) > totalLength)
+ {
+- //log->log("RecPlayer", Log::DEBUG, "Client asked for some data past the end of recording, adjusting amount");
++ //log->log("cRecPlayer", Log::DEBUG, "Client asked for some data past the end of recording, adjusting amount");
+ amount = totalLength - position;
+ }
+
+@@ -208,17 +228,17 @@
+ return got;
+ }
+
+-uint64_t RecPlayer::getLastPosition()
++uint64_t cRecPlayer::getLastPosition()
+ {
+ return lastPosition;
+ }
+
+-cRecording* RecPlayer::getCurrentRecording()
++const cRecording* cRecPlayer::getCurrentRecording()
+ {
+ return recording;
+ }
+
+-uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber)
++uint64_t cRecPlayer::positionFromFrameNumber(uint32_t frameNumber)
+ {
+ if (!indexFile) return 0;
+
+@@ -235,21 +255,21 @@
+ return 0;
+ }
+
+-// log->log("RecPlayer", Log::DEBUG, "FN: %u FO: %i", retFileNumber, retFileOffset);
++// log->log("cRecPlayer", Log::DEBUG, "FN: %u FO: %i", retFileNumber, retFileOffset);
+ if (!segments[retFileNumber]) return 0;
+ uint64_t position = segments[retFileNumber]->start + retFileOffset;
+-// log->log("RecPlayer", Log::DEBUG, "Pos: %llu", position);
++// log->log("cRecPlayer", Log::DEBUG, "Pos: %llu", position);
+
+ return position;
+ }
+
+-uint32_t RecPlayer::frameNumberFromPosition(uint64_t position)
++uint32_t cRecPlayer::frameNumberFromPosition(uint64_t position)
+ {
+ if (!indexFile) return 0;
+
+ if (position >= totalLength)
+ {
+- //log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
++ //log->log("cRecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
+ return 0;
+ }
+
+@@ -265,7 +285,7 @@
+ }
+
+
+-bool RecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength)
++bool cRecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength)
+ {
+ // 0 = backwards
+ // 1 = forwards
+@@ -276,7 +296,7 @@
+ int indexReturnFrameNumber;
+
+ indexReturnFrameNumber = (uint32_t)indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), NULL, NULL, &iframeLength);
+- //log->log("RecPlayer", Log::DEBUG, "GNIF input framenumber:%lu, direction=%lu, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength);
++ //log->log("cRecPlayer", Log::DEBUG, "GNIF input framenumber:%lu, direction=%lu, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength);
+
+ if (indexReturnFrameNumber == -1) return false;
+
+diff -NaurwB streamdev/server/recplayer.h streamdev-patched/server/recplayer.h
+--- streamdev/server/recplayer.h 2009-07-01 13:00:49.000000000 +0200
++++ streamdev-patched/server/recplayer.h 2010-02-09 17:18:23.000000000 +0100
+@@ -33,24 +33,24 @@
+ uint64_t end;
+ };
+
+-class RecPlayer
++class cRecPlayer
+ {
+ public:
+- RecPlayer(cRecording* rec);
+- ~RecPlayer();
++ cRecPlayer(const cRecording* rec);
++ ~cRecPlayer();
+ uint64_t getLengthBytes();
+ uint32_t getLengthFrames();
+- unsigned long getBlock(unsigned char* buffer, uint64_t position, unsigned long amount);
++ unsigned long getBlock(uint8_t* buffer, uint64_t position, unsigned long amount);
+ int openFile(int index);
+ uint64_t getLastPosition();
+- cRecording* getCurrentRecording();
++ const cRecording* getCurrentRecording();
+ void scan();
+ 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);
+
+ private:
+- cRecording* recording;
++ const cRecording* recording;
+ cIndexFile* indexFile;
+ FILE* file;
+ int fileOpen;