summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorschmirl <schmirl>2008-03-28 15:11:40 +0000
committerschmirl <schmirl>2008-03-28 15:11:40 +0000
commitb66bf7a698738389a030d1512d5442c149ab27db (patch)
tree07c58ba6891b29ffd53b22b7d964e1cc32bb3bcd
parent79836e69a99afd7a9a7b60ce41c5907fbed288d4 (diff)
downloadvdr-plugin-streamdev-b66bf7a698738389a030d1512d5442c149ab27db.tar.gz
vdr-plugin-streamdev-b66bf7a698738389a030d1512d5442c149ab27db.tar.bz2
Rewrite of http menu (#439)
Including - m3u playlists by Petri Hintukainen (#254) - way to pass parameters to externremux by Rolf Ahrenberg - using host header for absolute URLs for better DNAT / Reverse Proxy support
-rw-r--r--CONTRIBUTORS2
-rw-r--r--HISTORY5
-rw-r--r--Makefile4
-rw-r--r--README15
-rw-r--r--remux/extern.c13
-rw-r--r--remux/extern.h3
-rw-r--r--server/connectionHTTP.c198
-rw-r--r--server/connectionHTTP.h7
-rw-r--r--server/livestreamer.c5
-rw-r--r--server/livestreamer.h3
10 files changed, 172 insertions, 83 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 2894921..6f9c812 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -23,6 +23,7 @@ Rolf Ahrenberg
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
Rantanen Teemu
for providing vdr-incompletesections.diff
@@ -52,6 +53,7 @@ Petri Hintukainen
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
diff --git a/HISTORY b/HISTORY
index e6956d4..3f29f7c 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,6 +1,11 @@
VDR Plugin 'streamdev' Revision History
---------------------------------------
+- 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
diff --git a/Makefile b/Makefile
index b339c3f..ee84345 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
#
# Makefile for a Video Disk Recorder plugin
#
-# $Id: Makefile,v 1.10 2008/03/12 09:36:27 schmirl Exp $
+# $Id: Makefile,v 1.11 2008/03/28 15:11:40 schmirl Exp $
# The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin.
@@ -61,7 +61,7 @@ SERVEROBJS = $(PLUGIN)-server.o \
server/server.o server/connectionVTP.o server/connectionHTTP.o \
server/componentHTTP.o server/componentVTP.o server/connection.o \
server/component.o server/suspend.o server/setup.o server/streamer.o \
- server/livestreamer.o server/livefilter.o \
+ server/livestreamer.o server/livefilter.o server/menuHTTP.o \
\
remux/tsremux.o remux/ts2ps.o remux/ts2es.o remux/extern.o
diff --git a/README b/README
index 99ed13b..5a82acb 100644
--- a/README
+++ b/README
@@ -154,7 +154,15 @@ 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)
-If you leave the default port (3000), you can access the streams like this:
+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
@@ -173,6 +181,11 @@ 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
+
3.2 Usage VDR-to-VDR server:
----------------------------
diff --git a/remux/extern.c b/remux/extern.c
index 01d4b33..e137c4a 100644
--- a/remux/extern.c
+++ b/remux/extern.c
@@ -19,13 +19,13 @@ protected:
virtual void Action(void);
public:
- cTSExt(cRingBufferLinear *ResultBuffer);
+ cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter);
virtual ~cTSExt();
void Put(const uchar *Data, int Count);
};
-cTSExt::cTSExt(cRingBufferLinear *ResultBuffer):
+cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, std::string Parameter):
m_ResultBuffer(ResultBuffer),
m_Active(false),
m_Process(0),
@@ -67,9 +67,8 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer):
for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
close(i); //close all dup'ed filedescriptors
- //printf("starting externremux.sh\n");
- execl("/bin/sh", "sh", "-c", g_ExternRemux, NULL);
- //printf("failed externremux.sh\n");
+ std::string cmd = std::string(g_ExternRemux) + " " + Parameter;
+ execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL);
_exit(-1);
}
@@ -150,9 +149,9 @@ void cTSExt::Put(const uchar *Data, int Count)
}
}
-cExternRemux::cExternRemux(int VPid, const int *APids, const int *Dpids, const int *SPids):
+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))
+ m_Remux(new cTSExt(m_ResultBuffer, Parameter))
{
m_ResultBuffer->SetTimeouts(500, 100);
}
diff --git a/remux/extern.h b/remux/extern.h
index ae055ac..7a44852 100644
--- a/remux/extern.h
+++ b/remux/extern.h
@@ -3,6 +3,7 @@
#include "remux/tsremux.h"
#include <vdr/ringbuffer.h>
+#include <string>
extern const char *g_ExternRemux;
@@ -14,7 +15,7 @@ private:
cTSExt *m_Remux;
public:
- cExternRemux(int VPid, const int *APids, const int *Dpids, const int *SPids);
+ cExternRemux(int VPid, const int *APids, const int *Dpids, const int *SPids, std::string Parameter);
virtual ~cExternRemux();
int Put(const uchar *Data, int Count);
diff --git a/server/connectionHTTP.c b/server/connectionHTTP.c
index 8bde3aa..38a82a3 100644
--- a/server/connectionHTTP.c
+++ b/server/connectionHTTP.c
@@ -1,20 +1,22 @@
/*
- * $Id: connectionHTTP.c,v 1.12 2007/05/09 09:12:42 schmirl Exp $
+ * $Id: connectionHTTP.c,v 1.13 2008/03/28 15:11:40 schmirl Exp $
*/
#include <ctype.h>
#include "server/connectionHTTP.h"
+#include "server/menuHTTP.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_ListChannel(NULL)
+ m_ChannelList(NULL)
{
Dprintf("constructor hsRequest\n");
}
@@ -39,6 +41,10 @@ bool cConnectionHTTP::Command(char *Cmd)
m_Status = hsBody;
return ProcessRequest();
}
+ if (strncasecmp(Cmd, "Host:", 5) == 0) {
+ Dprintf("Host-Header\n");
+ m_Host = (std::string) skipspace(Cmd + 5);
+ }
Dprintf("header\n");
return true;
default:
@@ -53,11 +59,9 @@ bool cConnectionHTTP::ProcessRequest(void)
if (m_Request.substr(0, 4) == "GET " && CmdGET(m_Request.substr(4))) {
switch (m_Job) {
case hjListing:
- return Respond("HTTP/1.0 200 OK")
- && Respond("Content-Type: text/html")
- && Respond("")
- && Respond("<html><head><title>VDR Channel Listing</title></head>")
- && Respond("<body><ul>");
+ if (m_ChannelList)
+ return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
+ break;
case hjTransfer:
if (m_Channel == NULL) {
@@ -65,7 +69,7 @@ bool cConnectionHTTP::ProcessRequest(void)
return Respond("HTTP/1.0 404 not found");
}
- m_LiveStreamer = new cStreamdevLiveStreamer(0);
+ m_LiveStreamer = new cStreamdevLiveStreamer(0, m_StreamerParameter);
cDevice *device = GetDevice(m_Channel, 0);
if (device != NULL) {
device->SwitchChannel(m_Channel, false);
@@ -106,43 +110,21 @@ void cConnectionHTTP::Flushed(void)
switch (m_Job) {
case hjListing:
- if (m_ListChannel == NULL) {
- Respond("</ul></body></html>");
- DeferClose();
- m_Status = hsFinished;
- return;
- }
-
- if (m_ListChannel->GroupSep())
- line = (std::string)"<li>--- " + m_ListChannel->Name() + "---</li>";
- else {
- int index = 1;
- line = (std::string)"<li><a href=\"http://" + LocalIp() + ":"
- + (const char*)itoa(StreamdevServerSetup.HTTPServerPort) + "/"
- + StreamTypes[m_StreamType] + "/"
- + (const char*)m_ListChannel->GetChannelID().ToString() + "\">"
- + m_ListChannel->Name() + "</a> ";
- for (int i = 0; m_ListChannel->Apid(i) != 0; ++i, ++index) {
- line += "<a href=\"http://" + LocalIp() + ":"
- + (const char*)itoa(StreamdevServerSetup.HTTPServerPort) + "/"
- + StreamTypes[m_StreamType] + "/"
- + (const char*)m_ListChannel->GetChannelID().ToString() + "+"
- + (const char*)itoa(index) + "\">("
- + m_ListChannel->Alang(i) + ")</a> ";
+ if (m_ChannelList) {
+ if (m_ChannelList->HasNext()) {
+ if (!Respond("%s", true, m_ChannelList->Next().c_str()))
+ DeferClose();
}
- for (int i = 0; m_ListChannel->Dpid(i) != 0; ++i, ++index) {
- line += "<a href=\"http://" + LocalIp() + ":"
- + (const char*)itoa(StreamdevServerSetup.HTTPServerPort) + "/"
- + StreamTypes[m_StreamType] + "/"
- + (const char*)m_ListChannel->GetChannelID().ToString() + "+"
- + (const char*)itoa(index) + "\">("
- + m_ListChannel->Dlang(i) + ")</a> ";
+ else {
+ DELETENULL(m_ChannelList);
+ m_Status = hsFinished;
+ DeferClose();
}
- line += "</li>";
+ return;
}
- if (!Respond(line.c_str()))
- DeferClose();
- m_ListChannel = Channels.Next(m_ListChannel);
+ // should never be reached
+ esyslog("streamdev-server cConnectionHTTP::Flushed(): no channel list");
+ m_Status = hsFinished;
break;
case hjTransfer:
@@ -155,49 +137,131 @@ void cConnectionHTTP::Flushed(void)
bool cConnectionHTTP::CmdGET(const std::string &Opts)
{
- const char *sp = Opts.c_str(), *ptr = sp, *ep;
+ const char *ptr, *sp, *pp, *fp, *xp, *qp, *ep;
const cChannel *chan;
int apid = 0;
- ptr = skipspace(ptr);
- while (*ptr == '/')
- ++ptr;
+ 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());
- if (strncasecmp(ptr, "PS/", 3) == 0) {
+ const char* pType = type.c_str();
+ if (strcasecmp(pType, "PS") == 0) {
m_StreamType = stPS;
- ptr += 3;
- } else if (strncasecmp(ptr, "PES/", 4) == 0) {
+ } else if (strcasecmp(pType, "PES") == 0) {
m_StreamType = stPES;
- ptr += 4;
- } else if (strncasecmp(ptr, "TS/", 3) == 0) {
+ } else if (strcasecmp(pType, "TS") == 0) {
m_StreamType = stTS;
- ptr += 3;
- } else if (strncasecmp(ptr, "ES/", 3) == 0) {
+ } else if (strcasecmp(pType, "ES") == 0) {
m_StreamType = stES;
- ptr += 3;
- } else if (strncasecmp(ptr, "Extern/", 3) == 0) {
+ } else if (strcasecmp(pType, "Extern") == 0) {
m_StreamType = stExtern;
- ptr += 7;
}
- while (*ptr == '/')
- ++ptr;
- for (ep = ptr + strlen(ptr); ep >= ptr && !isspace(*ep); --ep)
- ;
+ std::string groupTarget;
+ cChannelIterator *iterator = NULL;
- std::string filespec = Opts.substr(ptr - sp, ep - ptr);
- Dprintf("substr: %s\n", filespec.c_str());
+ 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();
+ }
- Dprintf("before channelfromstring\n");
- if (filespec == "" || filespec.substr(0, 12) == "channels.htm") {
- m_ListChannel = Channels.First();
- m_Job = hjListing;
+ 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/server/connectionHTTP.h b/server/connectionHTTP.h
index 11e97b7..a3558ad 100644
--- a/server/connectionHTTP.h
+++ b/server/connectionHTTP.h
@@ -1,5 +1,5 @@
/*
- * $Id: connectionHTTP.h,v 1.4 2007/04/02 10:32:34 schmirl Exp $
+ * $Id: connectionHTTP.h,v 1.5 2008/03/28 15:11:40 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
@@ -12,6 +12,7 @@
class cChannel;
class cStreamdevLiveStreamer;
+class cChannelList;
class cConnectionHTTP: public cServerConnection {
private:
@@ -28,16 +29,18 @@ private:
};
std::string m_Request;
+ std::string m_Host;
//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
- const cChannel *m_ListChannel;
+ cChannelList *m_ChannelList;
protected:
bool ProcessRequest(void);
diff --git a/server/livestreamer.c b/server/livestreamer.c
index cd3c696..5e19d2b 100644
--- a/server/livestreamer.c
+++ b/server/livestreamer.c
@@ -323,9 +323,10 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
// --- cStreamdevLiveStreamer -------------------------------------------------
-cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority):
+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),
@@ -488,7 +489,7 @@ bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType Str
case stExtern:
m_ExtRemux = new cExternRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
- m_Channel->Spids());
+ m_Channel->Spids(), m_Parameter);
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
case stTSPIDS:
diff --git a/server/livestreamer.h b/server/livestreamer.h
index 1973f71..57c3a90 100644
--- a/server/livestreamer.h
+++ b/server/livestreamer.h
@@ -19,6 +19,7 @@ class cStreamdevLiveReceiver;
class cStreamdevLiveStreamer: public cStreamdevStreamer {
private:
int m_Priority;
+ std::string m_Parameter;
int m_Pids[MAXRECEIVEPIDS + 1];
int m_NumPids;
eStreamType m_StreamType;
@@ -35,7 +36,7 @@ private:
bool HasPid(int Pid);
public:
- cStreamdevLiveStreamer(int Priority);
+ cStreamdevLiveStreamer(int Priority, std::string Parameter = "");
virtual ~cStreamdevLiveStreamer();
void SetDevice(cDevice *Device) { m_Device = Device; }