- using SIGINT in externremux to kill mencoder works better than SIGTERM;

especially x264 still needs a SIGKILL sometimes
- added --remove-destination to cp commands installing plugins
- updated Italian translation (thanks to Diego Pierotto)
- config option "client may suspend" hidden if not applicable
- updated and enhanced README
- added support for HTTP method HEAD
- rewrite of externremux.sh, including support for various URL parameters,
  logging and improved shutdown
- start externremux script in a separate process group
- changed HTTP URL path for externremux from EXTERN to EXT (suggested by
  Rolf Ahrenberg)
- HTTP headers now have to be emitted by externremux script
- pass channel related information and URL parameters to externremux script
  through environment
- implement CGI like interface for externremux script
Modified Files:
 Tag: v0_4
	CONTRIBUTORS HISTORY Makefile README common.c common.h i18n.c
	remux/extern.c remux/extern.h server/connection.c
	server/connection.h server/connectionHTTP.c
	server/connectionHTTP.h server/connectionIGMP.c
	server/connectionVTP.c server/livestreamer.c
	server/livestreamer.h server/menuHTTP.c server/setup.c
	server/setup.h server/streamer.c server/streamer.h
	streamdev/externremux.sh streamdev/streamdevhosts.conf
This commit is contained in:
schmirl
2010-07-19 13:50:11 +00:00
parent a43455f660
commit fa578940f7
24 changed files with 865 additions and 331 deletions

View File

@@ -1,5 +1,5 @@
/*
* $Id: connection.c,v 1.10.2.2 2009/09/18 10:41:11 schmirl Exp $
* $Id: connection.c,v 1.10.2.3 2010/07/19 13:50:14 schmirl Exp $
*/
#include "server/connection.h"
@@ -27,7 +27,7 @@ cServerConnection::~cServerConnection()
{
}
const cChannel* cServerConnection::ChannelFromString(const char *String, int *Apid) {
const cChannel* cServerConnection::ChannelFromString(const char *String, int *Apid, int *Dpid) {
const cChannel *channel = NULL;
char *string = strdup(String);
char *ptr, *end;
@@ -58,7 +58,8 @@ const cChannel* cServerConnection::ChannelFromString(const char *String, int *Ap
}
if (channel != NULL && apididx > 0) {
int apid = 0, index = 1;
int apid = 0, dpid = 0;
int index = 1;
for (int i = 0; channel->Apid(i) != 0; ++i, ++index) {
if (index == apididx) {
@@ -70,7 +71,7 @@ const cChannel* cServerConnection::ChannelFromString(const char *String, int *Ap
if (apid == 0) {
for (int i = 0; channel->Dpid(i) != 0; ++i, ++index) {
if (index == apididx) {
apid = channel->Dpid(i);
dpid = channel->Dpid(i);
break;
}
}
@@ -78,6 +79,8 @@ const cChannel* cServerConnection::ChannelFromString(const char *String, int *Ap
if (Apid != NULL)
*Apid = apid;
if (Dpid != NULL)
*Dpid = dpid;
}
free(string);

View File

@@ -1,5 +1,5 @@
/*
* $Id: connection.h,v 1.5.2.3 2009/09/18 10:41:11 schmirl Exp $
* $Id: connection.h,v 1.5.2.4 2010/07/19 13:50:14 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_SERVER_CONNECTION_H
@@ -8,6 +8,11 @@
#include "tools/socket.h"
#include "common.h"
#include <map>
typedef std::map<std::string,std::string> tStrStrMap;
typedef std::pair<std::string,std::string> tStrStr;
class cChannel;
class cDevice;
@@ -28,6 +33,8 @@ private:
uint m_WriteBytes;
uint m_WriteIndex;
tStrStrMap m_Headers;
protected:
/* Will be called when a command terminated by a newline has been
received */
@@ -41,7 +48,10 @@ protected:
virtual bool Respond(const char *Message, bool Last = true, ...);
//__attribute__ ((format (printf, 2, 4)));
static const cChannel *ChannelFromString(const char *String, int *Apid = NULL);
/* Add a request header */
void SetHeader(const char *Name, const char *Value, const char *Prefix = "") { m_Headers.insert(tStrStr(std::string(Prefix) + Name, Value)); }
static const cChannel *ChannelFromString(const char *String, int *Apid = NULL, int *Dpid = NULL);
public:
/* If you derive, specify a short string such as HTTP for Protocol, which
@@ -89,6 +99,12 @@ public:
virtual void Detach(void) = 0;
virtual void Attach(void) = 0;
/* This connections protocol name */
virtual const char* Protocol(void) const { return m_Protocol; }
/* std::map with additional information */
const tStrStrMap& Headers(void) const { return m_Headers; }
};
inline bool cServerConnection::HasData(void) const

View File

@@ -1,5 +1,5 @@
/*
* $Id: connectionHTTP.c,v 1.13.2.2 2009/02/13 07:02:26 schmirl Exp $
* $Id: connectionHTTP.c,v 1.13.2.3 2010/07/19 13:50:14 schmirl Exp $
*/
#include <ctype.h>
@@ -13,13 +13,13 @@ 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");
m_Apid[0] = m_Apid[1] = 0;
m_Dpid[0] = m_Dpid[1] = 0;
}
cConnectionHTTP::~cConnectionHTTP()
@@ -37,30 +37,64 @@ 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;
// parse METHOD PATH[?QUERY] VERSION
{
char *p, *q, *v;
p = strchr(Cmd, ' ');
if (p) {
*p = 0;
v = strchr(++p, ' ');
if (v) {
*v = 0;
SetHeader("REQUEST_METHOD", Cmd);
q = strchr(p, '?');
if (q)
*q = 0;
SetHeader("QUERY_STRING", q ? ++q : "");
SetHeader("PATH_INFO", p);
m_Status = hsHeaders;
return true;
}
}
}
return false;
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 (isspace(*Cmd)) {
; //TODO: multi-line header
}
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;
else {
// convert header name to CGI conventions:
// uppercase, '-' replaced with '_', prefix "HTTP_"
char *p;
for (p = Cmd; *p != 0 && *p != ':'; p++) {
if (*p == '-')
*p = '_';
else
*p = toupper(*p);
}
if (*p == ':') {
*p = 0;
p = skipspace(++p);
// don't disclose Authorization header
if (strcmp(Cmd, "AUTHORIZATION") == 0) {
char *q;
for (q = p; *q != 0 && *q != ' '; q++)
*q = toupper(*q);
if (p != q) {
*q = 0;
SetHeader("AUTH_TYPE", p);
m_Authorization = (std::string) skipspace(++q);
}
}
else
SetHeader(Cmd, p, "HTTP_");
}
}
Dprintf("header\n");
return true;
default:
// skip additional blank lines
@@ -73,10 +107,31 @@ bool cConnectionHTTP::Command(char *Cmd)
bool cConnectionHTTP::ProcessRequest(void)
{
// keys for Headers() hash
const static std::string AUTH_TYPE("AUTH_TYPE");
const static std::string REQUEST_METHOD("REQUEST_METHOD");
const static std::string PATH_INFO("PATH_INFO");
Dprintf("process\n");
if (!StreamdevHosts.Acceptable(RemoteIpAddr()))
{
if (!opt_auth || m_Authorization.empty() || m_Authorization.compare(opt_auth) != 0) {
if (!StreamdevHosts.Acceptable(RemoteIpAddr())) {
bool authOk = opt_auth && !m_Authorization.empty();
if (authOk) {
tStrStrMap::const_iterator it = Headers().find(AUTH_TYPE);
if (it == Headers().end()) {
// no authorization header present
authOk = false;
}
else if (it->second.compare("BASIC") == 0) {
// basic auth
authOk &= m_Authorization.compare(opt_auth) == 0;
}
else {
// unsupported auth type
authOk = false;
}
}
if (!authOk) {
isyslog("streamdev-server: HTTP authorization required");
DeferClose();
return Respond("HTTP/1.0 401 Authorization Required")
@@ -84,28 +139,22 @@ bool cConnectionHTTP::ProcessRequest(void)
&& 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);
if (Headers().at(REQUEST_METHOD).compare("GET") == 0 && ProcessURI(Headers().at(PATH_INFO))) {
if (m_ChannelList)
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
else if (m_Channel != NULL) {
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 = new cStreamdevLiveStreamer(0, this);
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) {
m_LiveStreamer->SetDevice(device);
if (!SetDSCP())
LOG_ERROR_STR("unable to set DSCP sockopt");
if (m_StreamType == stES && (m_Apid != 0 || ISRADIO(m_Channel))) {
if (m_StreamType == stEXT) {
return Respond("HTTP/1.0 200 OK");
} else if (ISRADIO(m_Channel) || (m_StreamType == stES && (m_Apid[0] || m_Dpid[0]))) {
return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: audio/mpeg")
&& Respond("icy-name: %s", true, m_Channel->Name())
@@ -116,12 +165,46 @@ bool cConnectionHTTP::ProcessRequest(void)
&& Respond("");
}
}
DELETENULL(m_LiveStreamer);
}
DELETENULL(m_LiveStreamer);
DeferClose();
return Respond("HTTP/1.0 409 Channel not available")
&& Respond("");
}
else {
DeferClose();
return Respond("HTTP/1.0 404 not found")
&& Respond("");
}
} else if (Headers().at(REQUEST_METHOD).compare("HEAD") == 0 && ProcessURI(Headers().at(PATH_INFO))) {
DeferClose();
if (m_ChannelList)
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
else if (m_Channel != NULL) {
cDevice *device = GetDevice(m_Channel, 0);
if (device != NULL) {
if (m_StreamType == stEXT) {
// TODO
return Respond("HTTP/1.0 200 OK")
&& Respond("");
} else if (ISRADIO(m_Channel) || (m_StreamType == stES && (m_Apid[0] || m_Dpid[0]))) {
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("");
}
}
return Respond("HTTP/1.0 409 Channel not available")
&& Respond("");
}
else {
return Respond("HTTP/1.0 404 not found")
&& Respond("");
}
}
DeferClose();
@@ -136,78 +219,108 @@ void cConnectionHTTP::Flushed(void)
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;
if (m_ChannelList) {
if (m_ChannelList->HasNext()) {
if (!Respond("%s", true, m_ChannelList->Next().c_str()))
DeferClose();
}
return;
}
// should never be reached
esyslog("streamdev-server cConnectionHTTP::Flushed(): no channel list");
m_Status = hsFinished;
break;
case hjTransfer:
else {
DELETENULL(m_ChannelList);
m_Status = hsFinished;
DeferClose();
}
return;
}
else if (m_Channel != NULL) {
Dprintf("streamer start\n");
m_LiveStreamer->Start(this);
m_Status = hsFinished;
break;
}
else {
// should never be reached
esyslog("streamdev-server cConnectionHTTP::Flushed(): no job to do");
m_Status = hsFinished;
}
}
bool cConnectionHTTP::CmdGET(const std::string &Opts)
cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, const std::string& Filebase, const std::string& Fileext) const
{
const char *ptr, *sp, *pp, *fp, *xp, *qp, *ep;
const cChannel *chan;
int apid = 0;
// keys for Headers() hash
const static std::string QUERY_STRING("QUERY_STRING");
const static std::string HOST("HTTP_HOST");
ptr = Opts.c_str();
const std::string query = Headers().at(QUERY_STRING);
// 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 groupTarget;
cChannelIterator *iterator = NULL;
if (Filebase.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 = Filebase + Fileext;
} else if (Filebase.compare("groups") == 0) {
iterator = new cListGroups();
groupTarget = (std::string) "group" + Fileext;
} else if (Filebase.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 (Filebase.compare("channels") == 0) {
iterator = new cListChannels();
} else if (Filebase.compare("all") == 0 ||
(Filebase.empty() && Fileext.empty())) {
iterator = new cListAll();
}
if (iterator) {
if (Filebase.empty() || Fileext.compare(".htm") == 0 || Fileext.compare(".html") == 0) {
std::string self = Filebase + Fileext;
if (!query.empty())
self += '?' + query;
return new cHtmlChannelList(iterator, m_StreamType, self.c_str(), groupTarget.c_str());
} else if (Fileext.compare(".m3u") == 0) {
std::string base;
tStrStrMap::const_iterator it = Headers().find(HOST);
if (it != Headers().end())
base = "http://" + it->second + "/";
else
base = (std::string) "http://" + LocalIp() + ":" +
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
base += Path;
return new cM3uChannelList(iterator, base.c_str());
} else {
delete iterator;
}
}
return NULL;
}
bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
{
std::string filespec, fileext;
size_t file_pos = PathInfo.rfind('/');
if (file_pos != std::string::npos) {
size_t ext_pos = PathInfo.rfind('.');
// file basename with leading / stripped off
filespec = PathInfo.substr(file_pos + 1, ext_pos - file_pos - 1);
if (ext_pos != std::string::npos)
// file extension including leading .
fileext = PathInfo.substr(ext_pos);
}
if (fileext.length() > 5) {
//probably not an extension
filespec += fileext;
fileext.clear();
}
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());
std::string type = PathInfo.substr(1, PathInfo.find_first_of("/;", 1) - 1);
const char* pType = type.c_str();
if (strcasecmp(pType, "PS") == 0) {
m_StreamType = stPS;
@@ -217,80 +330,19 @@ bool cConnectionHTTP::CmdGET(const std::string &Opts)
m_StreamType = stTS;
} else if (strcasecmp(pType, "ES") == 0) {
m_StreamType = stES;
} else if (strcasecmp(pType, "Extern") == 0) {
m_StreamType = stExtern;
} else if (strcasecmp(pType, "EXT") == 0) {
m_StreamType = stEXT;
}
std::string groupTarget;
cChannelIterator *iterator = NULL;
Dprintf("before channelfromstring: type(%s) filespec(%s) fileext(%s)\n", type.c_str(), filespec.c_str(), fileext.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();
}
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;
if ((m_ChannelList = ChannelListFromString(PathInfo.substr(0, file_pos), filespec.c_str(), fileext.c_str())) != NULL) {
Dprintf("Channel list requested\n");
return true;
} else if ((m_Channel = ChannelFromString(filespec.c_str(), &m_Apid[0], &m_Dpid[0])) != NULL) {
Dprintf("Channel found. Apid/Dpid is %d/%d\n", m_Apid[0], m_Dpid[0]);
return true;
} else
return false;
Dprintf("after channelfromstring\n");
return true;
}

View File

@@ -1,5 +1,5 @@
/*
* $Id: connectionHTTP.h,v 1.5.2.1 2008/10/14 11:05:59 schmirl Exp $
* $Id: connectionHTTP.h,v 1.5.2.2 2010/07/19 13:50:14 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
@@ -8,6 +8,7 @@
#include "connection.h"
#include "server/livestreamer.h"
#include <map>
#include <tools/select.h>
class cChannel;
@@ -23,26 +24,19 @@ private:
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;
int m_Apid[2];
int m_Dpid[2];
eStreamType m_StreamType;
// job: listing
cChannelList *m_ChannelList;
cChannelList* ChannelListFromString(const std::string &PathInfo, const std::string &Filebase, const std::string &Fileext) const;
bool ProcessURI(const std::string &PathInfo);
protected:
bool ProcessRequest(void);
@@ -56,7 +50,6 @@ public:
virtual bool CanAuthenticate(void);
virtual bool Command(char *Cmd);
bool CmdGET(const std::string &Opts);
virtual bool Abort(void) const;
virtual void Flushed(void);

View File

@@ -1,5 +1,5 @@
/*
* $Id: connectionIGMP.c,v 1.1.2.2 2009/02/13 10:39:42 schmirl Exp $
* $Id: connectionIGMP.c,v 1.1.2.3 2010/07/19 13:50:14 schmirl Exp $
*/
#include <ctype.h>
@@ -31,7 +31,7 @@ bool cConnectionIGMP::Start(cChannel *Channel, in_addr_t Dst)
struct in_addr ip;
ip.s_addr = Dst;
if (Connect(inet_ntoa(ip), m_ClientPort)) {
m_LiveStreamer = new cStreamdevLiveStreamer(0);
m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
if (m_LiveStreamer->SetChannel(Channel, m_StreamType)) {
m_LiveStreamer->SetDevice(device);
if (!SetDSCP())

View File

@@ -1,5 +1,5 @@
/*
* $Id: connectionVTP.c,v 1.18.2.6 2010/01/29 12:02:44 schmirl Exp $
* $Id: connectionVTP.c,v 1.18.2.7 2010/07/19 13:50:14 schmirl Exp $
*/
#include "server/connectionVTP.h"
@@ -861,8 +861,8 @@ bool cConnectionVTP::CmdCAPS(char *Opts)
return Respond(220, "Capability \"%s\" accepted", Opts);
}
if (strcasecmp(Opts, "EXTERN") == 0) {
m_StreamType = stExtern;
if (strcasecmp(Opts, "EXT") == 0) {
m_StreamType = stEXT;
return Respond(220, "Capability \"%s\" accepted", Opts);
}
@@ -1058,7 +1058,7 @@ bool cConnectionVTP::CmdTUNE(char *Opts)
return Respond(560, "Channel not available");
delete m_LiveStreamer;
m_LiveStreamer = new cStreamdevLiveStreamer(1);
m_LiveStreamer = new cStreamdevLiveStreamer(1, this);
m_LiveStreamer->SetChannel(chan, m_StreamType);
m_LiveStreamer->SetDevice(dev);
if(m_LiveSocket)

View File

@@ -343,10 +343,9 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
// --- cStreamdevLiveStreamer -------------------------------------------------
cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, std::string Parameter):
cStreamdevStreamer("streamdev-livestreaming"),
cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection):
cStreamdevStreamer("streamdev-livestreaming", Connection),
m_Priority(Priority),
m_Parameter(Parameter),
m_NumPids(0),
m_StreamType(stTSPIDS),
m_Channel(NULL),
@@ -460,40 +459,38 @@ void cStreamdevLiveStreamer::StartReceiver(void)
}
}
bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, int Apid)
bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid, const int *Dpid)
{
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();
const int *Apids = Apid ? Apid : m_Channel->Apids();
const int *Dpids = Dpid ? Dpid : 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;
if (Apid && Apid[0])
pid = Apid[0];
else if (Dpid && Dpid[0])
pid = Dpid[0];
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());
m_Remux = new cTS2PESRemux(m_Channel->Vpid(), Apids, 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());
m_Remux = new cTS2PSRemux(m_Channel->Vpid(), Apids, 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);
case stEXT:
m_Remux = new cExternRemux(Connection(), m_Channel, Apids, Dpids);
// fall through
case stTS:
// This should never happen, but ...

View File

@@ -18,7 +18,6 @@ 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;
@@ -32,13 +31,13 @@ private:
bool HasPid(int Pid);
public:
cStreamdevLiveStreamer(int Priority, std::string Parameter = "");
cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection);
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 SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
virtual int Put(const uchar *Data, int Count);
virtual uchar *Get(int &Count);

View File

@@ -205,8 +205,8 @@ std::string cHtmlChannelList::StreamTypeMenu()
(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>] ");
typeMenu += (streamType == stEXT ? (std::string) "[EXT] " :
(std::string) "[<a href=\"/EXT/" + self + "\">EXT</a>] ");
return typeMenu;
}

View File

@@ -1,5 +1,5 @@
/*
* $Id: setup.c,v 1.3.2.3 2009/10/13 06:38:58 schmirl Exp $
* $Id: setup.c,v 1.3.2.4 2010/07/19 13:50:14 schmirl Exp $
*/
#include <vdr/menuitems.h>
@@ -51,7 +51,7 @@ const char* cStreamdevServerMenuSetupPage::StreamTypes[st_Count - 1] = {
"PES",
"PS",
"ES",
"Extern"
"EXT"
};
const char* cStreamdevServerMenuSetupPage::SuspendModes[sm_Count] = {
@@ -63,15 +63,25 @@ const char* cStreamdevServerMenuSetupPage::SuspendModes[sm_Count] = {
cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
m_NewSetup = StreamdevServerSetup;
Set();
}
cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() {
}
void cStreamdevServerMenuSetupPage::Set(void) {
static const char* modes[sm_Count];
for (int i = 0; i < sm_Count; i++)
modes[i] = tr(SuspendModes[i]);
int current = Current();
Clear();
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));
if (m_NewSetup.SuspendMode == smOffer)
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));
@@ -88,10 +98,8 @@ cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
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() {
SetCurrent(Get(current));
Display();
}
void cStreamdevServerMenuSetupPage::AddCategory(const char *Title) {
@@ -140,3 +148,10 @@ void cStreamdevServerMenuSetupPage::Store(void) {
cStreamdevServer::Initialize();
}
eOSState cStreamdevServerMenuSetupPage::ProcessKey(eKeys Key) {
int oldMode = m_NewSetup.SuspendMode;
eOSState state = cMenuSetupPage::ProcessKey(Key);
if (oldMode != m_NewSetup.SuspendMode)
Set();
return state;
}

View File

@@ -1,5 +1,5 @@
/*
* $Id: setup.h,v 1.1.1.1.2.2 2009/09/18 10:41:12 schmirl Exp $
* $Id: setup.h,v 1.1.1.1.2.3 2010/07/19 13:50:14 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_SETUPSERVER_H
@@ -37,8 +37,10 @@ private:
cStreamdevServerSetup m_NewSetup;
void AddCategory(const char *Title);
void Set();
protected:
virtual void Store(void);
virtual eOSState ProcessKey(eKeys Key);
public:
cStreamdevServerMenuSetupPage(void);

View File

@@ -1,5 +1,5 @@
/*
* $Id: streamer.c,v 1.16.2.3 2009/06/29 06:25:30 schmirl Exp $
* $Id: streamer.c,v 1.16.2.4 2010/07/19 13:50:14 schmirl Exp $
*/
#include <vdr/ringbuffer.h>
@@ -100,8 +100,9 @@ void cStreamdevWriter::Action(void)
// --- cStreamdevStreamer -----------------------------------------------------
cStreamdevStreamer::cStreamdevStreamer(const char *Name):
cStreamdevStreamer::cStreamdevStreamer(const char *Name, const cServerConnection *Connection):
cThread(Name),
m_Connection(Connection),
m_Writer(NULL),
m_RingBuffer(new cStreamdevBuffer(STREAMERBUFSIZE, TS_SIZE * 2,
true, "streamdev-streamer")),

View File

@@ -1,5 +1,5 @@
/*
* $Id: streamer.h,v 1.8.2.3 2009/06/29 06:25:30 schmirl Exp $
* $Id: streamer.h,v 1.8.2.4 2010/07/19 13:50:14 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_STREAMER_H
@@ -11,6 +11,7 @@
class cTBSocket;
class cStreamdevStreamer;
class cServerConnection;
#ifndef TS_SIZE
#define TS_SIZE 188
@@ -64,6 +65,7 @@ public:
class cStreamdevStreamer: public cThread {
private:
const cServerConnection *m_Connection;
cStreamdevWriter *m_Writer;
cStreamdevBuffer *m_RingBuffer;
cStreamdevBuffer *m_SendBuffer;
@@ -74,9 +76,11 @@ protected:
bool IsRunning(void) const { return m_Writer; }
public:
cStreamdevStreamer(const char *Name);
cStreamdevStreamer(const char *Name, const cServerConnection *Connection = NULL);
virtual ~cStreamdevStreamer();
const cServerConnection* Connection(void) const { return m_Connection; }
virtual void Start(cTBSocket *Socket);
virtual void Stop(void);
bool Abort(void);