Streamdev 0.5.0-rc1

This commit is contained in:
Frank Schmirler
2010-12-02 09:48:23 +01:00
parent 435f01649c
commit 11b22d9f33
57 changed files with 2101 additions and 1153 deletions

82
server/Makefile Normal file
View File

@@ -0,0 +1,82 @@
#
# Makefile for a Video Disk Recorder plugin
#
# $Id: Makefile,v 1.1.2.1 2010/06/14 10:40: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-server
### Includes and Defines (add further entries here):
DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
### The object files (add further files here):
COMMONOBJS = ../common.o
SERVEROBJS = $(PLUGIN).o \
server.o component.o connection.o \
componentVTP.o connectionVTP.o \
componentHTTP.o connectionHTTP.o menuHTTP.o \
componentIGMP.o connectionIGMP.o \
streamer.o livestreamer.o livefilter.o recplayer.o \
suspend.o setup.o
### The main target:
.PHONY: all i18n clean
all: libvdr-$(PLUGIN).so i18n
### Implicit rules:
%.o: %.c
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
### Dependencies:
MAKEDEP = $(CXX) -MM -MG
DEPFILE = .dependencies
$(DEPFILE): Makefile
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) > $@
-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): $(SERVEROBJS:%.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:
libvdr-$(PLUGIN).so: $(SERVEROBJS) $(COMMONOBJS) \
../tools/sockettools.a ../remux/remux.a ../libdvbmpeg/libdvbmpegtools.a
%.so:
$(CXX) $(CXXFLAGS) -shared $^ -o $@
@cp $@ $(LIBDIR)/$@.$(APIVERSION)
clean:
@-rm -f $(COMMONOBJS) $(SERVEROBJS) $(DEPFILE) $(PODIR)/*.mo $(PODIR)/*.pot *.so *.tgz core* *~

View File

@@ -1,5 +1,5 @@
/*
* $Id: connection.c,v 1.12 2009/02/13 10:39:22 schmirl Exp $
* $Id: connection.c,v 1.13.2.1 2010/06/11 06:06:02 schmirl Exp $
*/
#include "server/connection.h"
@@ -27,6 +27,66 @@ cServerConnection::~cServerConnection()
{
}
const cChannel* cServerConnection::ChannelFromString(const char *String, int *Apid, int *Dpid) {
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, dpid = 0;
int 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) {
dpid = channel->Dpid(i);
break;
}
}
}
if (Apid != NULL)
*Apid = apid;
if (Dpid != NULL)
*Dpid = dpid;
}
free(string);
return channel;
}
bool cServerConnection::Read(void)
{
int b;

View File

@@ -1,5 +1,5 @@
/*
* $Id: connection.h,v 1.7 2009/02/13 10:39:22 schmirl Exp $
* $Id: connection.h,v 1.8.2.1 2010/06/11 06:06:02 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,6 +48,11 @@ protected:
virtual bool Respond(const char *Message, bool Last = true, ...);
//__attribute__ ((format (printf, 2, 4)));
/* 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
will be displayed in error messages */
@@ -87,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.17 2009/06/19 06:32:45 schmirl Exp $
* $Id: connectionHTTP.c,v 1.17.2.1 2010/06/11 06:06:02 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.6 2008/10/14 11:05:48 schmirl Exp $
* $Id: connectionHTTP.h,v 1.6.2.1 2010/06/11 06:06:02 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 2009/02/13 10:39:22 schmirl Exp $
* $Id: connectionIGMP.c,v 1.1.4.1 2010/06/11 06:06:02 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.22 2009/07/02 06:03:51 schmirl Exp $
* $Id: connectionVTP.c,v 1.27.2.1 2010/06/11 06:06:03 schmirl Exp $
*/
#include "server/connectionVTP.h"
@@ -40,6 +40,9 @@ 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 };
@@ -50,7 +53,7 @@ private:
const cSchedule *m_Schedule;
const cEvent *m_Event;
int m_Errno;
char *m_Error;
cString m_Error;
eStates m_State;
bool m_Traverse;
time_t m_ToTime;
@@ -67,7 +70,6 @@ cLSTEHandler::cLSTEHandler(cConnectionVTP *Client, const char *Option):
m_Schedule(NULL),
m_Event(NULL),
m_Errno(0),
m_Error(NULL),
m_State(Channel),
m_Traverse(false),
m_ToTime(0)
@@ -94,12 +96,12 @@ cLSTEHandler::cLSTEHandler(cConnectionVTP *Client, const char *Option):
attime = strtol(p, NULL, 10);
else {
m_Errno = 501;
m_Error = strdup("Invalid time");
m_Error = "Invalid time";
break;
}
} else {
m_Errno = 501;
m_Error = strdup("Missing time");
m_Error = "Missing time";
break;
}
}
@@ -110,7 +112,7 @@ cLSTEHandler::cLSTEHandler(cConnectionVTP *Client, const char *Option):
fromtime = strtol(p, NULL, 10);
else {
m_Errno = 501;
m_Error = strdup("Invalid time");
m_Error = "Invalid time";
break;
}
if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
@@ -120,19 +122,19 @@ cLSTEHandler::cLSTEHandler(cConnectionVTP *Client, const char *Option):
m_ToTime = strtol(p, NULL, 10);
else {
m_Errno = 501;
m_Error = strdup("Invalid time");
m_Error = "Invalid time";
break;
}
} else {
m_Errno = 501;
m_Error = strdup("Missing time");
m_Error = "Missing time";
break;
}
}
}
} else {
m_Errno = 501;
m_Error = strdup("Missing time");
m_Error = "Missing time";
break;
}
} else if (!m_Schedule) {
@@ -146,27 +148,27 @@ cLSTEHandler::cLSTEHandler(cConnectionVTP *Client, const char *Option):
m_Schedule = m_Schedules->GetSchedule(Channel->GetChannelID());
if (!m_Schedule) {
m_Errno = 550;
m_Error = strdup("No schedule found");
m_Error = "No schedule found";
break;
}
} else {
m_Errno = 550;
asprintf(&m_Error, "Channel \"%s\" not defined", p);
m_Error = cString::sprintf("Channel \"%s\" not defined", p);
break;
}
} else {
m_Errno = 501;
asprintf(&m_Error, "Unknown option: \"%s\"", p);
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 = strdup("EPG data is being modified, try again");
m_Error = "EPG data is being modified, try again";
}
if (m_Error == NULL) {
if (*m_Error == NULL) {
if (m_Schedule != NULL)
m_Schedules = NULL;
else if (m_Schedules != NULL)
@@ -205,17 +207,15 @@ cLSTEHandler::cLSTEHandler(cConnectionVTP *Client, const char *Option):
cLSTEHandler::~cLSTEHandler()
{
delete m_SchedulesLock;
if (m_Error != NULL)
free(m_Error);
}
bool cLSTEHandler::Next(bool &Last)
{
if (m_Error != NULL) {
if (*m_Error != NULL) {
Last = true;
cString str(m_Error, true);
cString str(m_Error);
m_Error = NULL;
return m_Client->Respond(m_Errno, *str);
return m_Client->Respond(m_Errno, "%s", *str);
}
Last = false;
@@ -285,7 +285,7 @@ bool cLSTEHandler::Next(bool &Last)
break;
case Vps:
#if defined(USE_PARENTALRATING) || defined(PARENTALRATINGCONTENTVERSNUM)
#if defined(USE_PARENTALRATING) || defined(PARENTALRATINGCONTENTVERSNUM) || APIVERSNUM >= 10711
m_State = Content;
#else
m_State = EndEvent;
@@ -311,6 +311,25 @@ bool cLSTEHandler::Next(bool &Last)
} 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:
@@ -361,7 +380,7 @@ private:
const cChannel *m_Channel;
char *m_Option;
int m_Errno;
char *m_Error;
cString m_Error;
bool m_Traverse;
public:
cLSTCHandler(cConnectionVTP *Client, const char *Option);
@@ -374,18 +393,17 @@ cLSTCHandler::cLSTCHandler(cConnectionVTP *Client, const char *Option):
m_Channel(NULL),
m_Option(NULL),
m_Errno(0),
m_Error(NULL),
m_Traverse(false)
{
if (!Channels.Lock(false, 500)) {
m_Errno = 451;
m_Error = strdup("Channels are being modified - try again");
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;
asprintf(&m_Error, "Channel \"%s\" not defined", Option);
m_Error = cString::sprintf("Channel \"%s\" not defined", Option);
return;
}
} else {
@@ -401,7 +419,7 @@ cLSTCHandler::cLSTCHandler(cConnectionVTP *Client, const char *Option):
if (i > Channels.MaxNumber()) {
m_Errno = 501;
asprintf(&m_Error, "Channel \"%s\" not defined", Option);
m_Error = cString::sprintf("Channel \"%s\" not defined", Option);
return;
}
}
@@ -410,26 +428,24 @@ cLSTCHandler::cLSTCHandler(cConnectionVTP *Client, const char *Option):
m_Traverse = true;
} else {
m_Errno = 550;
m_Error = strdup("No channels defined");
m_Error = "No channels defined";
}
}
cLSTCHandler::~cLSTCHandler()
{
Channels.Unlock();
if (m_Error != NULL)
free(m_Error);
if (m_Option != NULL)
free(m_Option);
}
bool cLSTCHandler::Next(bool &Last)
{
if (m_Error != NULL) {
if (*m_Error != NULL) {
Last = true;
cString str(m_Error, true);
cString str(m_Error);
m_Error = NULL;
return m_Client->Respond(m_Errno, *str);
return m_Client->Respond(m_Errno, "%s", *str);
}
int number;
@@ -452,7 +468,7 @@ bool cLSTCHandler::Next(bool &Last)
i = m_Channel->Number() + 1;
} else {
m_Errno = 501;
asprintf(&m_Error, "Channel \"%d\" not found", i);
m_Error = cString::sprintf("Channel \"%d\" not found", i);
}
}
@@ -472,7 +488,7 @@ private:
cTimer *m_Timer;
int m_Index;
int m_Errno;
char *m_Error;
cString m_Error;
bool m_Traverse;
public:
cLSTTHandler(cConnectionVTP *Client, const char *Option);
@@ -485,7 +501,6 @@ cLSTTHandler::cLSTTHandler(cConnectionVTP *Client, const char *Option):
m_Timer(NULL),
m_Index(0),
m_Errno(0),
m_Error(NULL),
m_Traverse(false)
{
if (*Option) {
@@ -493,11 +508,11 @@ cLSTTHandler::cLSTTHandler(cConnectionVTP *Client, const char *Option):
m_Timer = Timers.Get(strtol(Option, NULL, 10) - 1);
if (m_Timer == NULL) {
m_Errno = 501;
asprintf(&m_Error, "Timer \"%s\" not defined", Option);
m_Error = cString::sprintf("Timer \"%s\" not defined", Option);
}
} else {
m_Errno = 501;
asprintf(&m_Error, "Error in timer number \"%s\"", Option);
m_Error = cString::sprintf("Error in timer number \"%s\"", Option);
}
} else if (Timers.Count()) {
m_Traverse = true;
@@ -505,27 +520,25 @@ cLSTTHandler::cLSTTHandler(cConnectionVTP *Client, const char *Option):
m_Timer = Timers.Get(m_Index);
if (m_Timer == NULL) {
m_Errno = 501;
asprintf(&m_Error, "Timer \"%d\" not found", m_Index + 1);
m_Error = cString::sprintf("Timer \"%d\" not found", m_Index + 1);
}
} else {
m_Errno = 550;
m_Error = strdup("No timers defined");
m_Error = "No timers defined";
}
}
cLSTTHandler::~cLSTTHandler()
{
if (m_Error != NULL)
free(m_Error);
}
bool cLSTTHandler::Next(bool &Last)
{
if (m_Error != NULL) {
if (*m_Error != NULL) {
Last = true;
cString str(m_Error, true);
cString str(m_Error);
m_Error = NULL;
return m_Client->Respond(m_Errno, *str);
return m_Client->Respond(m_Errno, "%s", *str);
}
bool result;
@@ -541,7 +554,7 @@ bool cLSTTHandler::Next(bool &Last)
m_Timer = Timers.Get(++m_Index);
if (m_Timer == NULL) {
m_Errno = 501;
asprintf(&m_Error, "Timer \"%d\" not found", m_Index + 1);
m_Error = cString::sprintf("Timer \"%d\" not found", m_Index + 1);
}
}
return result;
@@ -559,7 +572,7 @@ private:
const cEvent *m_Event;
int m_Index;
int m_Errno;
char *m_Error;
cString m_Error;
bool m_Traverse;
bool m_Info;
eStates m_State;
@@ -576,7 +589,6 @@ cLSTRHandler::cLSTRHandler(cConnectionVTP *Client, const char *Option):
m_Event(NULL),
m_Index(0),
m_Errno(0),
m_Error(NULL),
m_Traverse(false),
m_Info(false),
m_State(Recording),
@@ -591,12 +603,12 @@ cLSTRHandler::cLSTRHandler(cConnectionVTP *Client, const char *Option):
m_Info = true;
if (m_Recording == NULL) {
m_Errno = 501;
asprintf(&m_Error, "Recording \"%s\" not found", Option);
m_Error = cString::sprintf("Recording \"%s\" not found", Option);
}
}
else {
m_Errno = 501;
asprintf(&m_Error, "Error in Recording number \"%s\"", Option);
m_Error = cString::sprintf("Error in Recording number \"%s\"", Option);
}
}
else if (Recordings.Count()) {
@@ -605,28 +617,26 @@ cLSTRHandler::cLSTRHandler(cConnectionVTP *Client, const char *Option):
m_Recording = Recordings.Get(m_Index);
if (m_Recording == NULL) {
m_Errno = 501;
asprintf(&m_Error, "Recording \"%d\" not found", m_Index + 1);
m_Error = cString::sprintf("Recording \"%d\" not found", m_Index + 1);
}
}
else {
m_Errno = 550;
m_Error = strdup("No recordings available");
m_Error = "No recordings available";
}
}
cLSTRHandler::~cLSTRHandler()
{
if (m_Error != NULL)
free(m_Error);
}
bool cLSTRHandler::Next(bool &Last)
{
if (m_Error != NULL) {
if (*m_Error != NULL) {
Last = true;
cString str(m_Error, true);
cString str(m_Error);
m_Error = NULL;
return m_Client->Respond(m_Errno, *str);
return m_Client->Respond(m_Errno, "%s", *str);
}
if (m_Info) {
@@ -714,7 +724,7 @@ bool cLSTRHandler::Next(bool &Last)
m_Recording = Recordings.Get(++m_Index);
if (m_Recording == NULL) {
m_Errno = 501;
asprintf(&m_Error, "Recording \"%d\" not found", m_Index + 1);
m_Error = cString::sprintf("Recording \"%d\" not found", m_Index + 1);
}
}
return result;
@@ -871,8 +881,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);
}
@@ -1068,7 +1078,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)
@@ -1086,7 +1096,6 @@ bool cConnectionVTP::CmdTUNE(char *Opts)
bool cConnectionVTP::CmdPLAY(char *Opts)
{
Recordings.Update(true);
if (*Opts) {
if (isnumber(Opts)) {
cRecording *recording = Recordings.Get(strtol(Opts, NULL, 10) - 1);
@@ -1397,22 +1406,52 @@ bool cConnectionVTP::CmdDELT(const char *Option)
{
INIT_WRAPPER();
if (*Option) {
if (isnumber(Option)) {
cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
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 (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 \"%s\" deleted", Option);
Reply(250, "Timer \"%i\" deleted", number);
} else
Reply(550, "Timer \"%s\" is recording", Option);
Reply(501, "Timer \"%i\" not defined", number);
} else
Reply(501, "Timer \"%s\" not defined", Option);
} else
Reply(501, "Error in timer number \"%s\"", Option);
} else
Reply(501, "Missing timer number");
Reply(501, "Missing timer option");
EXIT_WRAPPER();
}
@@ -1706,12 +1745,17 @@ bool cConnectionVTP::CmdRENR(const char *Option)
bool cConnectionVTP::Respond(int Code, const char *Message, ...)
{
char *buffer;
va_list ap;
va_start(ap, Message);
vasprintf(&buffer, Message, ap);
va_end(ap);
#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);
@@ -1719,6 +1763,6 @@ bool cConnectionVTP::Respond(int Code, const char *Message, ...)
}
return cServerConnection::Respond("%03d%c%s", Code >= 0,
Code < 0 ? -Code : Code,
Code < 0 ? '-' : ' ', buffer);
Code < 0 ? -Code : Code,
Code < 0 ? '-' : ' ', *str);
}

View File

@@ -172,16 +172,20 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
switch (d->getDescriptorTag()) {
case SI::AC3DescriptorTag:
case SI::EnhancedAC3DescriptorTag:
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",
@@ -214,6 +218,7 @@ int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream)
stream.getPid(), stream.getStreamType(),
d->getLength(), rawdata[2], rawdata[3],
rawdata[4], rawdata[5]);
delete d;
return stream.getPid();
}
}
@@ -330,10 +335,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),
@@ -443,37 +447,39 @@ 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 stEXT:
m_Remux = new cExternRemux(Connection(), m_Channel, Apids, Dpids);
// fall through
case stTS:
// This should never happen, but ...
if (m_PatFilter) {
@@ -488,16 +494,12 @@ bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType Str
m_PatFilter = new cStreamdevPatFilter(this, m_Channel);
return true;
case stExtern:
m_Remux = new cExternRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(),
m_Channel->Spids(), m_Parameter);
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
case stTSPIDS:
Dprintf("pid streaming mode\n");
return true;
default:
return false;
}
return false;
}
int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)

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;
}
@@ -415,13 +415,13 @@ std::string cM3uChannelList::Next()
if (channel->GroupSep())
{
return (std::string) "#EXTINF:0," + name + "\r\n" +
return (std::string) "#EXTINF:-1," + name + "\r\n" +
base + "group.m3u?group=" +
(const char*) itoa(cChannelList::GetGroupIndex(channel));
}
else
{
return (std::string) "#EXTINF:0," +
return (std::string) "#EXTINF:-1," +
(const char*) itoa(channel->Number()) + " " + name + "\r\n" +
base + (std::string) channel->GetChannelID().ToString();
}

View File

@@ -113,7 +113,12 @@ class cHtmlChannelList: public cChannelList
std::string ItemText();
std::string PageBottom();
public:
virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: text/html\r\n\r\n"; }
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);
@@ -128,7 +133,7 @@ class cM3uChannelList: public cChannelList
eM3uState m3uState;
cCharSetConv m_IConv;
public:
virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: audio/x-mpegurl\r\n"; };
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);

83
server/po/de_DE.po Normal file
View File

@@ -0,0 +1,83 @@
# 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: 2010-06-14 13:06+0200\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 "VDR Streaming Server"
msgstr "VDR Streaming Server"
msgid "Streaming active"
msgstr "Streamen im Gange"
msgid "Suspend Live TV"
msgstr "Live-TV pausieren"
msgid "Offer suspend mode"
msgstr "Pausieren anbieten"
msgid "Always suspended"
msgstr "Immer pausiert"
msgid "Never suspended"
msgstr "Nie pausiert"
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"

83
server/po/fi_FI.po Normal file
View File

@@ -0,0 +1,83 @@
# 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: 2010-06-14 13:06+0200\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 "VDR Streaming Server"
msgstr "VDR-suoratoistopalvelin"
msgid "Streaming active"
msgstr "Suoratoistopalvelin aktiivinen"
msgid "Suspend Live TV"
msgstr "Pysäytä suora TV-lähetys"
msgid "Offer suspend mode"
msgstr "tyrkytä"
msgid "Always suspended"
msgstr "aina"
msgid "Never suspended"
msgstr "ei koskaan"
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"

83
server/po/fr_FR.po Normal file
View File

@@ -0,0 +1,83 @@
# 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: 2010-06-14 13:06+0200\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 "VDR Streaming Server"
msgstr "Serveur de streaming VDR"
msgid "Streaming active"
msgstr "Streaming actif"
msgid "Suspend Live TV"
msgstr "Suspendre Live TV"
msgid "Offer suspend mode"
msgstr "Offrir le mode suspendre"
msgid "Always suspended"
msgstr "Toujours suspendre"
msgid "Never suspended"
msgstr "Jamais suspendre"
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 ""

85
server/po/it_IT.po Normal file
View File

@@ -0,0 +1,85 @@
# 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: 2010-06-14 13:06+0200\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 "VDR Streaming Server"
msgstr "Server trasmissione VDR"
msgid "Streaming active"
msgstr "Trasmissione attiva"
msgid "Suspend Live TV"
msgstr "Sospendi TV dal vivo"
msgid "Offer suspend mode"
msgstr "Offri mod. sospensione"
msgid "Always suspended"
msgstr "Sempre sospeso"
msgid "Never suspended"
msgstr "Mai sospeso"
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 ""

83
server/po/lt_LT.po Normal file
View File

@@ -0,0 +1,83 @@
# 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: 2010-06-14 13:06+0200\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 "VDR Streaming Server"
msgstr "VDR transliavimo serveris"
msgid "Streaming active"
msgstr "Transliavimas vyksta"
msgid "Suspend Live TV"
msgstr "Pristabdyti Live TV"
msgid "Offer suspend mode"
msgstr "Klausti dėl sustabdymo"
msgid "Always suspended"
msgstr "Visada stabdyti"
msgid "Never suspended"
msgstr "Niekada nestabdyti"
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"

83
server/po/ru_RU.po Normal file
View File

@@ -0,0 +1,83 @@
# 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: 2010-06-14 13:06+0200\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 "VDR Streaming Server"
msgstr "VDR Streaming áÕàÒÕà"
msgid "Streaming active"
msgstr "ÁâàØÜØÝÓ ÐÚâØÒÕÝ"
msgid "Suspend Live TV"
msgstr "¾áâÐÝÞÒÚÐ Live TV"
msgid "Offer suspend mode"
msgstr "¿àÕÔÛÐÓÐâì ÞáâÐÝÞÒÚã"
msgid "Always suspended"
msgstr "²áÕÓÔÐ ÞáâÐÝÞÒÛÕÝ"
msgid "Never suspended"
msgstr "½ØÚÞÓÔÐ ÝÕ ÞáâÐÝÞÒÛÕÝ"
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 ""

85
server/po/sk_SK.po Normal file
View File

@@ -0,0 +1,85 @@
# 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: 2010-06-14 13:06+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 "VDR Streaming Server"
msgstr "VDR prúdový server"
msgid "Streaming active"
msgstr "streamovanie aktivne"
msgid "Suspend Live TV"
msgstr "Pozastavenie ¾ivého vysielania"
msgid "Offer suspend mode"
msgstr "Výber re¾ímu pozastavenia"
msgid "Always suspended"
msgstr "V¾dy pozastavi»"
msgid "Never suspended"
msgstr "Nikdy nepozastavi»"
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"

View File

@@ -21,6 +21,9 @@
#include "recplayer.h"
// for TSPLAY patch detection
#include "vdr/device.h"
#define _XOPEN_SOURCE 600
#include <fcntl.h>
@@ -34,7 +37,7 @@ RecPlayer::RecPlayer(cRecording* rec)
// FIXME find out max file path / name lengths
#if VDRVERSNUM >= 10703
#if VDRVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
indexFile = new cIndexFile(recording->FileName(), false, rec->IsPesRecording());
#else
indexFile = new cIndexFile(recording->FileName(), false);
@@ -58,7 +61,7 @@ void RecPlayer::scan()
for(i = 1; i < 1000; i++)
{
#if APIVERSNUM < 10703
#if APIVERSNUM < 10703 || !defined(TSPLAY_PATCH_VERSION)
snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
//log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
file = fopen(fileName, "r");
@@ -99,7 +102,7 @@ int RecPlayer::openFile(int index)
char fileName[2048];
#if APIVERSNUM >= 10703
#if APIVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index);
isyslog("openFile called for index %i string:%s", index, fileName);
@@ -222,7 +225,7 @@ uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber)
{
if (!indexFile) return 0;
#if VDRVERSNUM >= 10703
#if VDRVERSNUM >= 10703 || defined(TSPLAY_PATCH_VERSION)
uint16_t retFileNumber;
off_t retFileOffset;
#else

View File

@@ -1,5 +1,5 @@
/*
* $Id: setup.c,v 1.6 2009/02/13 10:39:22 schmirl Exp $
* $Id: setup.c,v 1.9.2.2 2010/06/18 19:07:32 schmirl Exp $
*/
#include <vdr/menuitems.h>
@@ -45,35 +45,72 @@ bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) {
return true;
}
const char* cStreamdevServerMenuSetupPage::StreamTypes[st_Count - 1] = {
"TS",
"PES",
"PS",
"ES",
"EXT"
};
const char* cStreamdevServerMenuSetupPage::SuspendModes[sm_Count] = {
trNOOP("Offer suspend mode"),
trNOOP("Always suspended"),
trNOOP("Never suspended")
};
cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
m_NewSetup = StreamdevServerSetup;
AddCategory (tr("Common Settings"));
AddRangeEdit(tr("Maximum Number of Clients"), m_NewSetup.MaxClients, 0, 100);
AddSuspEdit (tr("Suspend behaviour"), m_NewSetup.SuspendMode);
AddBoolEdit (tr("Client may suspend"), m_NewSetup.AllowSuspend);
AddCategory (tr("VDR-to-VDR Server"));
AddBoolEdit (tr("Start VDR-to-VDR Server"), m_NewSetup.StartVTPServer);
AddShortEdit(tr("VDR-to-VDR Server Port"), m_NewSetup.VTPServerPort);
AddIpEdit (tr("Bind to IP"), m_NewSetup.VTPBindIP);
AddCategory (tr("HTTP Server"));
AddBoolEdit (tr("Start HTTP Server"), m_NewSetup.StartHTTPServer);
AddShortEdit(tr("HTTP Server Port"), m_NewSetup.HTTPServerPort);
AddTypeEdit (tr("HTTP Streamtype"), m_NewSetup.HTTPStreamType);
AddIpEdit (tr("Bind to IP"), m_NewSetup.HTTPBindIP);
AddCategory (tr("Multicast Streaming Server"));
AddBoolEdit (tr("Start IGMP Server"), m_NewSetup.StartIGMPServer);
AddShortEdit(tr("Multicast Client Port"), m_NewSetup.IGMPClientPort);
AddTypeEdit (tr("Multicast Streamtype"), m_NewSetup.IGMPStreamType);
AddIpEdit (tr("Bind to IP"), m_NewSetup.IGMPBindIP);
SetCurrent(Get(1));
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));
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));
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(current));
Display();
}
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
@@ -110,3 +147,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.2 2009/02/13 10:39:22 schmirl Exp $
* $Id: setup.h,v 1.3.2.1 2010/06/18 19:07:32 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_SETUPSERVER_H
@@ -30,12 +30,17 @@ struct cStreamdevServerSetup {
extern cStreamdevServerSetup StreamdevServerSetup;
class cStreamdevServerMenuSetupPage: public cStreamdevMenuSetupPage {
class cStreamdevServerMenuSetupPage: public cMenuSetupPage {
private:
static const char* StreamTypes[];
static const char* SuspendModes[];
cStreamdevServerSetup m_NewSetup;
void AddCategory(const char *Title);
void Set();
protected:
virtual void Store(void);
virtual eOSState ProcessKey(eKeys Key);
public:
cStreamdevServerMenuSetupPage(void);

143
server/streamdev-server.c Normal file
View File

@@ -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.1.2.1 2010/06/14 10:40:20 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!

33
server/streamdev-server.h Normal file
View File

@@ -0,0 +1,33 @@
/*
* $Id: streamdev-server.h,v 1.1.2.1 2010/06/14 10:40:20 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

View File

@@ -1,5 +1,5 @@
/*
* $Id: streamer.c,v 1.19 2009/06/19 06:32:45 schmirl Exp $
* $Id: streamer.c,v 1.19.2.1 2010/06/11 06:06:03 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.11 2009/06/19 06:32:45 schmirl Exp $
* $Id: streamer.h,v 1.11.2.1 2010/06/11 06:06:03 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);