summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/connection.c9
-rw-r--r--server/connection.h6
-rw-r--r--server/connectionHTTP.c8
-rw-r--r--server/connectionVTP.c117
-rw-r--r--server/connectionVTP.h18
-rw-r--r--server/livefilter.c18
-rw-r--r--server/livefilter.h18
-rw-r--r--server/livestreamer.c506
-rw-r--r--server/livestreamer.h66
9 files changed, 622 insertions, 144 deletions
diff --git a/server/connection.c b/server/connection.c
index dff1945..629ed1d 100644
--- a/server/connection.c
+++ b/server/connection.c
@@ -1,5 +1,5 @@
/*
- * $Id: connection.c,v 1.8 2007/01/15 12:00:19 schmirl Exp $
+ * $Id: connection.c,v 1.10 2007/05/07 12:25:11 schmirl Exp $
*/
#include "server/connection.h"
@@ -101,9 +101,16 @@ bool cServerConnection::Respond(const char *Message, bool 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);
diff --git a/server/connection.h b/server/connection.h
index 2df850e..fe828d9 100644
--- a/server/connection.h
+++ b/server/connection.h
@@ -1,5 +1,5 @@
/*
- * $Id: connection.h,v 1.4 2007/04/02 10:32:34 schmirl Exp $
+ * $Id: connection.h,v 1.5 2007/04/16 11:01:02 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_SERVER_CONNECTION_H
@@ -38,8 +38,8 @@ protected:
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)));
+ virtual bool Respond(const char *Message, bool Last = true, ...);
+ //__attribute__ ((format (printf, 2, 4)));
public:
/* If you derive, specify a short string such as HTTP for Protocol, which
diff --git a/server/connectionHTTP.c b/server/connectionHTTP.c
index a526da9..8bde3aa 100644
--- a/server/connectionHTTP.c
+++ b/server/connectionHTTP.c
@@ -1,5 +1,5 @@
/*
- * $Id: connectionHTTP.c,v 1.10 2006/01/26 19:40:18 lordjaxom Exp $
+ * $Id: connectionHTTP.c,v 1.12 2007/05/09 09:12:42 schmirl Exp $
*/
#include <ctype.h>
@@ -41,6 +41,8 @@ bool cConnectionHTTP::Command(char *Cmd)
}
Dprintf("header\n");
return true;
+ default:
+ break;
}
return false; // ??? shouldn't happen
}
@@ -69,6 +71,8 @@ bool cConnectionHTTP::ProcessRequest(void)
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")
@@ -153,7 +157,7 @@ bool cConnectionHTTP::CmdGET(const std::string &Opts)
{
const char *sp = Opts.c_str(), *ptr = sp, *ep;
const cChannel *chan;
- int apid = 0, pos;
+ int apid = 0;
ptr = skipspace(ptr);
while (*ptr == '/')
diff --git a/server/connectionVTP.c b/server/connectionVTP.c
index 18ea353..c847bf3 100644
--- a/server/connectionVTP.c
+++ b/server/connectionVTP.c
@@ -1,5 +1,5 @@
/*
- * $Id: connectionVTP.c,v 1.8 2007/03/02 15:27:07 schmirl Exp $
+ * $Id: connectionVTP.c,v 1.14 2007/05/09 09:12:42 schmirl Exp $
*/
#include "server/connectionVTP.h"
@@ -153,8 +153,6 @@ cLSTEHandler::~cLSTEHandler()
bool cLSTEHandler::Next(bool &Last)
{
- char *buffer;
-
if (m_Error != NULL) {
Last = true;
cString str(m_Error, true);
@@ -468,6 +466,8 @@ cConnectionVTP::cConnectionVTP(void):
cServerConnection("VTP"),
m_LiveSocket(NULL),
m_LiveStreamer(NULL),
+ m_FilterSocket(NULL),
+ m_FilterStreamer(NULL),
m_LastCommand(NULL),
m_NoTSPIDS(false),
m_LSTEHandler(NULL),
@@ -482,11 +482,18 @@ cConnectionVTP::~cConnectionVTP()
free(m_LastCommand);
delete m_LiveStreamer;
delete m_LiveSocket;
+ delete m_FilterStreamer;
+ delete m_FilterSocket;
delete m_LSTTHandler;
delete m_LSTCHandler;
delete m_LSTEHandler;
}
+inline bool cConnectionVTP::Abort(void) const
+{
+ return m_LiveStreamer && m_LiveStreamer->Abort();
+}
+
void cConnectionVTP::Welcome(void)
{
Respond(220, "Welcome to Video Disk Recorder (VTP)");
@@ -500,12 +507,14 @@ void cConnectionVTP::Reject(void)
void cConnectionVTP::Detach(void)
{
- if (m_LiveStreamer != NULL) m_LiveStreamer->Detach();
+ if (m_LiveStreamer) m_LiveStreamer->Detach();
+ if (m_FilterStreamer) m_FilterStreamer->Detach();
}
void cConnectionVTP::Attach(void)
{
- if (m_LiveStreamer != NULL) m_LiveStreamer->Attach();
+ if (m_LiveStreamer) m_LiveStreamer->Attach();
+ if (m_FilterStreamer) m_FilterStreamer->Attach();
}
bool cConnectionVTP::Command(char *Cmd)
@@ -549,8 +558,8 @@ bool cConnectionVTP::Command(char *Cmd)
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(param);
- else if (strcasecmp(Cmd, "SUSP") == 0) return CmdSUSP(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, "DELR") == 0) return CmdDELR(param);
else if (strcasecmp(Cmd, "MODT") == 0) return CmdMODT(param);
@@ -562,8 +571,6 @@ bool cConnectionVTP::Command(char *Cmd)
bool cConnectionVTP::CmdCAPS(char *Opts)
{
- char *buffer;
-
if (strcasecmp(Opts, "TS") == 0) {
m_NoTSPIDS = true;
return Respond(220, "Ignored, capability \"%s\" accepted for "
@@ -575,6 +582,14 @@ bool cConnectionVTP::CmdCAPS(char *Opts)
return Respond(220, "Capability \"%s\" accepted", Opts);
}
+#if VDRVERSNUM >= 10300
+ //
+ // Deliver section filters data in separate, channel-independent data stream
+ //
+ if (strcasecmp(Opts, "FILTERS") == 0)
+ return Respond(220, "Capability \"%s\" accepted", Opts);
+#endif
+
return Respond(561, "Capability \"%s\" not known", Opts);
}
@@ -607,9 +622,14 @@ bool cConnectionVTP::CmdPORT(char *Opts)
id = strtoul(Opts, &ep, 10);
if (ep == Opts || !isspace(*ep))
return Respond(500, "Use: PORT Id Destination");
-
- if (id != 0)
+
+#if VDRVERSNUM >= 10300
+ if (id != siLive && id != siLiveFilter)
+ return Respond(501, "Wrong connection id %d", id);
+#else
+ if (id != siLive)
return Respond(501, "Wrong connection id %d", id);
+#endif
Opts = skipspace(ep);
n = 0;
@@ -636,6 +656,33 @@ bool cConnectionVTP::CmdPORT(char *Opts)
isyslog("Streamdev: Setting data connection to %s:%d", dataip, dataport);
+#if VDRVERSNUM >= 10300
+ if (id == siLiveFilter) {
+ 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");
+ }
+#endif
+
+ 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",
@@ -644,10 +691,12 @@ bool cConnectionVTP::CmdPORT(char *Opts)
return Respond(551, "Couldn't open data connection");
}
- if (id == siLive)
+ 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");
+ return Respond(220, "Port command ok, data connection opened");
}
bool cConnectionVTP::CmdTUNE(char *Opts)
@@ -668,7 +717,16 @@ bool cConnectionVTP::CmdTUNE(char *Opts)
m_LiveStreamer = new cStreamdevLiveStreamer(1);
m_LiveStreamer->SetChannel(chan, m_NoTSPIDS ? stTS : stTSPIDS);
m_LiveStreamer->SetDevice(dev);
+ if(m_LiveSocket)
+ m_LiveStreamer->Start(m_LiveSocket);
+#if VDRVERSNUM >= 10300
+ if(!m_FilterStreamer)
+ m_FilterStreamer = new cStreamdevFilterStreamer;
+ m_FilterStreamer->SetDevice(dev);
+ //m_FilterStreamer->SetChannel(chan);
+#endif
+
return Respond(220, "Channel tuned");
}
@@ -706,8 +764,8 @@ bool cConnectionVTP::CmdADDF(char *Opts)
int pid, tid, mask;
char *ep;
- if (m_LiveStreamer == NULL)
- return Respond(560, "Can't set filters without a stream");
+ if (m_FilterStreamer == NULL)
+ return Respond(560, "Can't set filters without a filter stream");
pid = strtol(Opts, &ep, 10);
if (ep == Opts || (*ep != ' '))
@@ -721,7 +779,7 @@ bool cConnectionVTP::CmdADDF(char *Opts)
if (ep == Opts || (*ep != '\0' && *ep != ' '))
return Respond(500, "Use: ADDF Pid Tid Mask");
- return m_LiveStreamer->SetFilter(pid, tid, mask, true)
+ return m_FilterStreamer->SetFilter(pid, tid, mask, true)
? Respond(220, "Filter %d transferring", pid)
: Respond(560, "Filter %d not available", pid);
#else
@@ -735,7 +793,7 @@ bool cConnectionVTP::CmdDELF(char *Opts)
int pid, tid, mask;
char *ep;
- if (m_LiveStreamer == NULL)
+ if (m_FilterStreamer == NULL)
return Respond(560, "Can't delete filters without a stream");
pid = strtol(Opts, &ep, 10);
@@ -750,9 +808,8 @@ bool cConnectionVTP::CmdDELF(char *Opts)
if (ep == Opts || (*ep != '\0' && *ep != ' '))
return Respond(500, "Use: DELF Pid Tid Mask");
- return m_LiveStreamer->SetFilter(pid, tid, mask, false)
- ? Respond(220, "Filter %d stopped", pid)
- : Respond(560, "Filter %d not transferring", pid);
+ m_FilterStreamer->SetFilter(pid, tid, mask, false);
+ return Respond(220, "Filter %d stopped", pid);
#else
return Respond(500, "DELF known but unimplemented with VDR < 1.3.0");
#endif
@@ -768,20 +825,32 @@ bool cConnectionVTP::CmdABRT(char *Opts)
return Respond(500, "Use: ABRT Id");
switch (id) {
- case 0: DELETENULL(m_LiveStreamer); break;
+ case siLive:
+ DELETENULL(m_LiveStreamer);
+ DELETENULL(m_LiveSocket);
+ break;
+#if VDRVERSNUM >= 10300
+ case siLiveFilter:
+ DELETENULL(m_FilterStreamer);
+ DELETENULL(m_FilterSocket);
+ break;
+#endif
+ default:
+ return Respond(501, "Wrong connection id %d", id);
+ break;
+
}
- DELETENULL(m_LiveSocket);
return Respond(220, "Data connection closed");
}
-bool cConnectionVTP::CmdQUIT(char *Opts)
+bool cConnectionVTP::CmdQUIT(void)
{
DeferClose();
return Respond(221, "Video Disk Recorder closing connection");
}
-bool cConnectionVTP::CmdSUSP(char *Opts)
+bool cConnectionVTP::CmdSUSP(void)
{
if (StreamdevServerSetup.SuspendMode == smAlways || cSuspendCtl::IsActive())
return Respond(220, "Server is suspended");
diff --git a/server/connectionVTP.h b/server/connectionVTP.h
index c6ab223..fffff4f 100644
--- a/server/connectionVTP.h
+++ b/server/connectionVTP.h
@@ -2,9 +2,10 @@
#define VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
#include "server/connection.h"
-#include "server/livestreamer.h"
class cTBSocket;
+class cStreamdevLiveStreamer;
+class cStreamdevFilterStreamer;
class cLSTEHandler;
class cLSTCHandler;
class cLSTTHandler;
@@ -16,8 +17,10 @@ class cConnectionVTP: public cServerConnection {
using cServerConnection::Respond;
private:
- cTBSocket *m_LiveSocket;
- cStreamdevLiveStreamer *m_LiveStreamer;
+ cTBSocket *m_LiveSocket;
+ cStreamdevLiveStreamer *m_LiveStreamer;
+ cTBSocket *m_FilterSocket;
+ cStreamdevFilterStreamer *m_FilterStreamer;
char *m_LastCommand;
bool m_NoTSPIDS;
@@ -53,8 +56,8 @@ public:
bool CmdADDF(char *Opts);
bool CmdDELF(char *Opts);
bool CmdABRT(char *Opts);
- bool CmdQUIT(char *Opts);
- bool CmdSUSP(char *Opts);
+ bool CmdQUIT(void);
+ bool CmdSUSP(void);
// Thread-safe implementations of SVDRP commands
bool CmdLSTE(char *Opts);
@@ -73,9 +76,4 @@ public:
__attribute__ ((format (printf, 3, 4)));
};
-inline bool cConnectionVTP::Abort(void) const
-{
- return m_LiveStreamer && m_LiveStreamer->Abort();
-}
-
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
diff --git a/server/livefilter.c b/server/livefilter.c
index 4524a88..e7d896c 100644
--- a/server/livefilter.c
+++ b/server/livefilter.c
@@ -1,20 +1,24 @@
/*
- * $Id: livefilter.c,v 1.2 2005/02/08 13:59:16 lordjaxom Exp $
+ * $Id: livefilter.c,v 1.4 2007/04/24 11:06:12 schmirl Exp $
*/
#include "server/livefilter.h"
-#include "server/livestreamer.h"
+#include "server/streamer.h"
#include "common.h"
+#ifndef TS_SIZE
+# define TS_SIZE 188
+#endif
+#ifndef TS_SYNC_BYTE
+# define TS_SYNC_BYTE 0x47
+#endif
+
#if VDRVERSNUM >= 10300
-cStreamdevLiveFilter::cStreamdevLiveFilter(cStreamdevLiveStreamer *Streamer) {
+cStreamdevLiveFilter::cStreamdevLiveFilter(cStreamdevStreamer *Streamer) {
m_Streamer = Streamer;
}
-cStreamdevLiveFilter::~cStreamdevLiveFilter() {
-}
-
void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{
uchar buffer[TS_SIZE];
@@ -24,7 +28,7 @@ void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data,
while (length > 0) {
int chunk = min(length, TS_SIZE - 5);
buffer[0] = TS_SYNC_BYTE;
- buffer[1] = (Pid >> 8) & 0xff;
+ buffer[1] = ((Pid >> 8) & 0x3f) | (pos==0 ? 0x40 : 0); /* bit 6: payload unit start indicator (PUSI) */
buffer[2] = Pid & 0xff;
buffer[3] = Tid;
buffer[4] = (uchar)chunk;
diff --git a/server/livefilter.h b/server/livefilter.h
index a30cba0..99c69d4 100644
--- a/server/livefilter.h
+++ b/server/livefilter.h
@@ -1,5 +1,5 @@
/*
- * $Id: livefilter.h,v 1.2 2005/11/07 19:28:41 lordjaxom Exp $
+ * $Id: livefilter.h,v 1.4 2007/04/24 11:29:29 schmirl Exp $
*/
#ifndef VDR_STREAMEV_LIVEFILTER_H
@@ -11,20 +11,24 @@
#include <vdr/filter.h>
-class cStreamdevLiveStreamer;
+class cStreamdevStreamer;
class cStreamdevLiveFilter: public cFilter {
- friend class cStreamdevLiveStreamer;
-
private:
- cStreamdevLiveStreamer *m_Streamer;
+ cStreamdevStreamer *m_Streamer;
protected:
virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
public:
- cStreamdevLiveFilter(cStreamdevLiveStreamer *Streamer);
- virtual ~cStreamdevLiveFilter();
+ 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 // VDRVERSNUM >= 10300
diff --git a/server/livestreamer.c b/server/livestreamer.c
index 6148720..1bbeddb 100644
--- a/server/livestreamer.c
+++ b/server/livestreamer.c
@@ -1,6 +1,12 @@
+#include <assert.h>
+
+#include <libsi/section.h>
+#include <libsi/descriptor.h>
+
#include <vdr/ringbuffer.h>
#include "server/livestreamer.h"
+#include "server/livefilter.h"
#include "remux/ts2ps.h"
#include "remux/ts2es.h"
#include "remux/extern.h"
@@ -8,12 +14,31 @@
// --- 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:
#if VDRVERSNUM < 10500
-cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, int Ca,
+ cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, int Ca, int Priority, const int *Pids);
+#else
+ cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, tChannelID ChannelID, int Priority, const int *Pids);
+#endif
+ virtual ~cStreamdevLiveReceiver();
+};
+
+#if VDRVERSNUM < 10500
+cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, int Ca,
int Priority, const int *Pids):
cReceiver(Ca, Priority, 0, Pids),
#else
-cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, tChannelID ChannelID,
+cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, tChannelID ChannelID,
int Priority, const int *Pids):
cReceiver(ChannelID, Priority, 0, Pids),
#endif
@@ -33,6 +58,228 @@ void cStreamdevLiveReceiver::Receive(uchar *Data, int 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;
+
+ 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);
+};
+
+cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel)
+{
+ Dprintf("cStreamdevPatFilter(\"%s\")", Channel->Name());
+ assert(Streamer);
+ m_Channel = Channel;
+ m_Streamer = Streamer;
+ pmtPid = 0;
+ pmtSid = 0;
+ pmtVersion = -1;
+ Set(0x00, 0x00); // PAT
+}
+
+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)",
+ 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",
+ stream.getPid(), psStreamTypes[stream.getStreamType()], "AC3");
+ return stream.getPid();
+ case SI::TeletextDescriptorTag:
+ Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s",
+ stream.getPid(), psStreamTypes[stream.getStreamType()], "Teletext");
+ return stream.getPid();
+ case SI::SubtitlingDescriptorTag:
+ Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s",
+ stream.getPid(), psStreamTypes[stream.getStreamType()], "DVBSUB");
+ return stream.getPid();
+ default:
+ Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s",
+ 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]);
+ 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",
+ 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 && !pmtPid) {
+ 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)) {
+ if (0 != (pmtPid = assoc.getPid())) {
+ Dprintf("cStreamdevPatFilter: PMT pid for channel %s: %d", Channel->Name(), pmtPid);
+ pmtSid = assoc.getServiceId();
+ if (Length < TS_SIZE-5) {
+ // repack PAT to TS frame and send to client
+ uint8_t pat_ts[TS_SIZE] = {TS_SYNC_BYTE, 0x40 /* pusi=1 */, 0 /* pid=0 */, 0x10 /* adaption=1 */, 0 /* pointer */};
+ memcpy(pat_ts + 5, Data, Length);
+ m_Streamer->Put(pat_ts, TS_SIZE);
+ } else
+ isyslog("cStreamdevPatFilter: PAT size %d too large to fit in one TS", Length);
+ 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");
+ 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++] = 0x12; // pid 0x12, tid 0x4E...0x6F: EIT
+ pids[npids++] = 0x14; // pid 0x14, tid 0x70: TDT
+#endif
+ 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):
@@ -43,6 +290,7 @@ cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority):
m_Channel(NULL),
m_Device(NULL),
m_Receiver(NULL),
+ m_PatFilter(NULL),
m_PESRemux(NULL),
m_ESRemux(NULL),
m_PSRemux(NULL),
@@ -54,14 +302,24 @@ cStreamdevLiveStreamer::~cStreamdevLiveStreamer()
{
Dprintf("Desctructing Live streamer\n");
Stop();
- delete m_Receiver;
+ if(m_PatFilter) {
+ Detach();
+ DELETENULL(m_PatFilter);
+ }
+ DELETENULL(m_Receiver);
delete m_PESRemux;
delete m_ESRemux;
delete m_PSRemux;
delete m_ExtRemux;
-#if VDRVERSNUM >= 10300
- //delete m_Filter; TODO
-#endif
+}
+
+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)
@@ -93,6 +351,44 @@ bool cStreamdevLiveStreamer::SetPid(int Pid, bool On)
}
}
+ 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");
@@ -106,15 +402,19 @@ bool cStreamdevLiveStreamer::SetPid(int Pid, bool On)
Attach();
}
}
- return true;
}
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());
+ //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:
{
@@ -122,51 +422,33 @@ bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType Str
if (Apid != 0)
pid = Apid;
m_ESRemux = new cTS2ESRemux(pid);
- return SetPid(pid, true);
+ return SetPids(pid);
}
case stPES:
- Dprintf("PES\n");
m_PESRemux = new cRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
m_Channel->Spids(), false);
- if (Apid != 0)
- return SetPid(m_Channel->Vpid(), true)
- && SetPid(Apid, true);
- else
- return SetPid(m_Channel->Vpid(), true)
- && SetPid(m_Channel->Apid(0), true)
- && SetPid(m_Channel->Dpid(0), true);
+ return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
case stPS:
m_PSRemux = new cTS2PSRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
m_Channel->Spids());
- if (Apid != 0)
- return SetPid(m_Channel->Vpid(), true)
- && SetPid(Apid, true);
- else
- return SetPid(m_Channel->Vpid(), true)
- && SetPid(m_Channel->Apid(0), true)
- && SetPid(m_Channel->Dpid(0), true);
+ return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
case stTS:
- if (Apid != 0)
- return SetPid(m_Channel->Vpid(), true)
- && SetPid(Apid, true);
- else
- return SetPid(m_Channel->Vpid(), true)
- && SetPid(m_Channel->Apid(0), true)
- && SetPid(m_Channel->Dpid(0), true);
+ // This should never happen, but ...
+ if (m_PatFilter) {
+ Detach();
+ DELETENULL(m_PatFilter);
+ }
+ // Set pids from PMT
+ m_PatFilter = new cStreamdevPatFilter(this, m_Channel);
+ return true;
case stExtern:
m_ExtRemux = new cExternRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
m_Channel->Spids());
- if (Apid != 0)
- return SetPid(m_Channel->Vpid(), true)
- && SetPid(Apid, true);
- else
- return SetPid(m_Channel->Vpid(), true)
- && SetPid(m_Channel->Apid(0), true)
- && SetPid(m_Channel->Dpid(0), true);
+ return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
case stTSPIDS:
Dprintf("pid streaming mode\n");
@@ -175,25 +457,6 @@ bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType Str
return false;
}
-bool cStreamdevLiveStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On)
-{
-#if 0
- Dprintf("setting filter\n");
- if (On) {
- if (m_Filter == NULL) {
- m_Filter = new cStreamdevLiveFilter(this);
- Dprintf("attaching filter to device\n");
- m_Device->AttachFilter(m_Filter);
- }
- m_Filter->Set(Pid, Tid, Mask);
- } else if (m_Filter != NULL)
- m_Filter->Del(Pid, Tid, Mask);
- return true;
-#else
- return false;
-#endif
-}
-
int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
{
switch (m_StreamType) {
@@ -270,14 +533,28 @@ void cStreamdevLiveStreamer::Del(int Count)
void cStreamdevLiveStreamer::Attach(void)
{
- printf("RIGHT ATTACH\n");
- m_Device->AttachReceiver(m_Receiver);
+ 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)
{
- printf("RIGHT DETACH\n");
- m_Device->Detach(m_Receiver);
+ 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)
@@ -296,3 +573,110 @@ std::string cStreamdevLiveStreamer::Report(void)
result += "\n";
return result;
}
+
+// --- cStreamdevFilterStreamer -------------------------------------------------
+
+#if VDRVERSNUM >= 10300
+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
+
+#endif // if VDRVERSNUM >= 10300
diff --git a/server/livestreamer.h b/server/livestreamer.h
index 0c525bf..1973f71 100644
--- a/server/livestreamer.h
+++ b/server/livestreamer.h
@@ -5,34 +5,14 @@
#include <vdr/receiver.h>
#include "server/streamer.h"
-#include "server/livefilter.h"
#include "common.h"
class cTS2PSRemux;
class cTS2ESRemux;
class cExternRemux;
class cRemux;
-
-// --- cStreamdevLiveReceiver -------------------------------------------------
-
-class cStreamdevLiveReceiver: public cReceiver {
- friend class cStreamdevLiveStreamer;
-
-private:
- cStreamdevLiveStreamer *m_Streamer;
-
-protected:
- virtual void Activate(bool On);
- virtual void Receive(uchar *Data, int Length);
-
-public:
-#if VDRVERSNUM < 10500
- cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, int Ca, int Priority, const int *Pids);
-#else
- cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, tChannelID ChannelID, int Priority, const int *Pids);
-#endif
- virtual ~cStreamdevLiveReceiver();
-};
+class cStreamdevPatFilter;
+class cStreamdevLiveReceiver;
// --- cStreamdevLiveStreamer -------------------------------------------------
@@ -45,19 +25,23 @@ private:
const cChannel *m_Channel;
cDevice *m_Device;
cStreamdevLiveReceiver *m_Receiver;
+ cStreamdevPatFilter *m_PatFilter;
cRemux *m_PESRemux;
cTS2ESRemux *m_ESRemux;
cTS2PSRemux *m_PSRemux;
cExternRemux *m_ExtRemux;
+ void StartReceiver(void);
+ bool HasPid(int Pid);
+
public:
cStreamdevLiveStreamer(int Priority);
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);
- bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On);
virtual int Put(const uchar *Data, int Count);
virtual uchar *Get(int &Count);
@@ -70,12 +54,36 @@ public:
virtual std::string Report(void);
};
-// --- cStreamdevLiveReceiver reverse inlines ---------------------------------
-inline void cStreamdevLiveReceiver::Activate(bool On)
-{
- Dprintf("LiveReceiver->Activate(%d)\n", On);
- m_Streamer->Activate(On);
-}
+// --- cStreamdevFilterStreamer -------------------------------------------------
+
+# if VDRVERSNUM >= 10300
+
+//#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 // if VDRVERSNUM >= 10300
#endif // VDR_STREAMDEV_LIVESTREAMER_H