diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/Makefile | 82 | ||||
-rw-r--r-- | server/connection.c | 62 | ||||
-rw-r--r-- | server/connection.h | 20 | ||||
-rw-r--r-- | server/connectionHTTP.c | 360 | ||||
-rw-r--r-- | server/connectionHTTP.h | 19 | ||||
-rw-r--r-- | server/connectionIGMP.c | 4 | ||||
-rw-r--r-- | server/connectionVTP.c | 194 | ||||
-rw-r--r-- | server/livestreamer.c | 40 | ||||
-rw-r--r-- | server/livestreamer.h | 5 | ||||
-rw-r--r-- | server/menuHTTP.c | 8 | ||||
-rw-r--r-- | server/menuHTTP.h | 9 | ||||
-rw-r--r-- | server/po/de_DE.po | 83 | ||||
-rw-r--r-- | server/po/fi_FI.po | 83 | ||||
-rw-r--r-- | server/po/fr_FR.po | 83 | ||||
-rw-r--r-- | server/po/it_IT.po | 85 | ||||
-rw-r--r-- | server/po/lt_LT.po | 83 | ||||
-rw-r--r-- | server/po/ru_RU.po | 83 | ||||
-rw-r--r-- | server/po/sk_SK.po | 85 | ||||
-rw-r--r-- | server/recplayer.c | 11 | ||||
-rw-r--r-- | server/setup.c | 80 | ||||
-rw-r--r-- | server/setup.h | 9 | ||||
-rw-r--r-- | server/streamdev-server.c | 143 | ||||
-rw-r--r-- | server/streamdev-server.h | 33 | ||||
-rw-r--r-- | server/streamer.c | 5 | ||||
-rw-r--r-- | server/streamer.h | 8 |
25 files changed, 1375 insertions, 302 deletions
diff --git a/server/Makefile b/server/Makefile new file mode 100644 index 0000000..91c2a93 --- /dev/null +++ b/server/Makefile @@ -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* *~ diff --git a/server/connection.c b/server/connection.c index 74b2783..6121b7a 100644 --- a/server/connection.c +++ b/server/connection.c @@ -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; diff --git a/server/connection.h b/server/connection.h index 2c28a09..dfaff91 100644 --- a/server/connection.h +++ b/server/connection.h @@ -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 diff --git a/server/connectionHTTP.c b/server/connectionHTTP.c index 83e568d..b963d64 100644 --- a/server/connectionHTTP.c +++ b/server/connectionHTTP.c @@ -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,161 +219,130 @@ 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; - - ptr = Opts.c_str(); - - // find begin of URL - sp = skipspace(ptr); - // find end of URL (\0 or first space character) - for (ep = sp; *ep && !isspace(*ep); ep++) - ; - // find begin of query string (first ?) - for (qp = sp; qp < ep && *qp != '?'; qp++) - ; - // find begin of filename (last /) - for (fp = qp; fp > sp && *fp != '/'; --fp) - ; - // find begin of section params (first ;) - for (pp = sp; pp < fp && *pp != ';'; pp++) - ; - // find filename extension (first .) - for (xp = fp; xp < qp && *xp != '.'; xp++) - ; - if (qp - xp > 5) // too long for a filename extension - xp = qp; - - std::string type, filespec, fileext, query; - // Streamtype with leading / stripped off - if (pp > sp) - type = Opts.substr(sp - ptr + 1, pp - sp - 1); - // Section parameters with leading ; stripped off - if (fp > pp) - m_StreamerParameter = Opts.substr(pp - ptr + 1, fp - pp - 1); - // file basename with leading / stripped off - if (xp > fp) - filespec = Opts.substr(fp - ptr + 1, xp - fp - 1); - // file extension including leading . - fileext = Opts.substr(xp - ptr, qp - xp); - // query string including leading ? - query = Opts.substr(qp - ptr, ep - qp); + // keys for Headers() hash + const static std::string QUERY_STRING("QUERY_STRING"); + const static std::string HOST("HTTP_HOST"); - Dprintf("before channelfromstring: type(%s) param(%s) filespec(%s) fileext(%s) query(%s)\n", type.c_str(), m_StreamerParameter.c_str(), filespec.c_str(), fileext.c_str(), query.c_str()); - - const char* pType = type.c_str(); - if (strcasecmp(pType, "PS") == 0) { - m_StreamType = stPS; - } else if (strcasecmp(pType, "PES") == 0) { - m_StreamType = stPES; - } else if (strcasecmp(pType, "TS") == 0) { - m_StreamType = stTS; - } else if (strcasecmp(pType, "ES") == 0) { - m_StreamType = stES; - } else if (strcasecmp(pType, "Extern") == 0) { - m_StreamType = stExtern; - } + const std::string query = Headers().at(QUERY_STRING); std::string groupTarget; cChannelIterator *iterator = NULL; - if (filespec.compare("tree") == 0) { + 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 = filespec + fileext; - } else if (filespec.compare("groups") == 0) { + groupTarget = Filebase + Fileext; + } else if (Filebase.compare("groups") == 0) { iterator = new cListGroups(); - groupTarget = (std::string) "group" + fileext; - } else if (filespec.compare("group") == 0) { + 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 (filespec.compare("channels") == 0) { + } else if (Filebase.compare("channels") == 0) { iterator = new cListChannels(); - } else if (filespec.compare("all") == 0 || - (filespec.empty() && fileext.empty())) { + } else if (Filebase.compare("all") == 0 || + (Filebase.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) { + 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; - if (*(m_Host.c_str())) - base = "http://" + m_Host + "/"; + 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) + "/"; - 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; + base += Path; + return new cM3uChannelList(iterator, base.c_str()); } 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; + } + 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(); + } + + // Streamtype with leading / stripped off + 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; + } else if (strcasecmp(pType, "PES") == 0) { + m_StreamType = stPES; + } else if (strcasecmp(pType, "TS") == 0) { + m_StreamType = stTS; + } else if (strcasecmp(pType, "ES") == 0) { + m_StreamType = stES; + } else if (strcasecmp(pType, "EXT") == 0) { + m_StreamType = stEXT; + } + + Dprintf("before channelfromstring: type(%s) filespec(%s) fileext(%s)\n", type.c_str(), filespec.c_str(), fileext.c_str()); + + 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; } diff --git a/server/connectionHTTP.h b/server/connectionHTTP.h index 0548959..1ea6ed2 100644 --- a/server/connectionHTTP.h +++ b/server/connectionHTTP.h @@ -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); diff --git a/server/connectionIGMP.c b/server/connectionIGMP.c index dc08798..7ea7e11 100644 --- a/server/connectionIGMP.c +++ b/server/connectionIGMP.c @@ -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()) diff --git a/server/connectionVTP.c b/server/connectionVTP.c index 6037ecc..ca73cfb 100644 --- a/server/connectionVTP.c +++ b/server/connectionVTP.c @@ -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); } diff --git a/server/livestreamer.c b/server/livestreamer.c index 71a3565..2b0065c 100644 --- a/server/livestreamer.c +++ b/server/livestreamer.c @@ -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) diff --git a/server/livestreamer.h b/server/livestreamer.h index 92448bb..283a395 100644 --- a/server/livestreamer.h +++ b/server/livestreamer.h @@ -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); diff --git a/server/menuHTTP.c b/server/menuHTTP.c index 8d3e404..5fb5010 100644 --- a/server/menuHTTP.c +++ b/server/menuHTTP.c @@ -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(); } diff --git a/server/menuHTTP.h b/server/menuHTTP.h index fa699b9..cbd7b59 100644 --- a/server/menuHTTP.h +++ b/server/menuHTTP.h @@ -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); diff --git a/server/po/de_DE.po b/server/po/de_DE.po new file mode 100644 index 0000000..5fb611d --- /dev/null +++ b/server/po/de_DE.po @@ -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" + diff --git a/server/po/fi_FI.po b/server/po/fi_FI.po new file mode 100644 index 0000000..99c6ee5 --- /dev/null +++ b/server/po/fi_FI.po @@ -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 "Pysyt suora TV-lhetys" + +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 mr" + +msgid "Suspend behaviour" +msgstr "Pysytystoiminto" + +msgid "Client may suspend" +msgstr "Asiakas saa pysytt palvelimen" + +msgid "VDR-to-VDR Server" +msgstr "VDR-palvelin" + +msgid "Start VDR-to-VDR Server" +msgstr "Kynnist 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 "Kynnist HTTP-palvelin" + +msgid "HTTP Server Port" +msgstr "HTTP-palvelimen portti" + +msgid "HTTP Streamtype" +msgstr "HTTP-lhetysmuoto" + +msgid "Multicast Streaming Server" +msgstr "Multicast-suoratoistopalvelin" + +msgid "Start IGMP Server" +msgstr "Kynnist IGMP-palvelin" + +msgid "Multicast Client Port" +msgstr "Multicast-portti" + +msgid "Multicast Streamtype" +msgstr "Multicast-lhetysmuoto" + diff --git a/server/po/fr_FR.po b/server/po/fr_FR.po new file mode 100644 index 0000000..c4b458e --- /dev/null +++ b/server/po/fr_FR.po @@ -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 "Paramtres 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 "Dmarrer 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 "Dmarrer 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 "" + diff --git a/server/po/it_IT.po b/server/po/it_IT.po new file mode 100644 index 0000000..d51f62a --- /dev/null +++ b/server/po/it_IT.po @@ -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 "" + diff --git a/server/po/lt_LT.po b/server/po/lt_LT.po new file mode 100644 index 0000000..f12de4d --- /dev/null +++ b/server/po/lt_LT.po @@ -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" + diff --git a/server/po/ru_RU.po b/server/po/ru_RU.po new file mode 100644 index 0000000..21abeaf --- /dev/null +++ b/server/po/ru_RU.po @@ -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 "" + diff --git a/server/po/sk_SK.po b/server/po/sk_SK.po new file mode 100644 index 0000000..78d98c9 --- /dev/null +++ b/server/po/sk_SK.po @@ -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 prdov server" + +msgid "Streaming active" +msgstr "streamovanie aktivne" + +msgid "Suspend Live TV" +msgstr "Pozastavenie ivho vysielania" + +msgid "Offer suspend mode" +msgstr "Vber remu pozastavenia" + +msgid "Always suspended" +msgstr "Vdy pozastavi" + +msgid "Never suspended" +msgstr "Nikdy nepozastavi" + +msgid "Common Settings" +msgstr "Veobecn nastavenia" + +msgid "Maximum Number of Clients" +msgstr "Maximly poet klientov" + +msgid "Suspend behaviour" +msgstr "Sprvanie preruenia" + +msgid "Client may suspend" +msgstr "Klient me 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 prdu HTTP" + +msgid "Multicast Streaming Server" +msgstr "Multicast prdov server" + +msgid "Start IGMP Server" +msgstr "Spusti IGMP Server" + +msgid "Multicast Client Port" +msgstr "Port klienta Multicast" + +msgid "Multicast Streamtype" +msgstr "Multicast typ streamu" + diff --git a/server/recplayer.c b/server/recplayer.c index f45d8c3..384ac13 100644 --- a/server/recplayer.c +++ b/server/recplayer.c @@ -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 diff --git a/server/setup.c b/server/setup.c index 710b1fa..986f876 100644 --- a/server/setup.c +++ b/server/setup.c @@ -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; + 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")); - 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); + 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")); - 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); + 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")); - 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); + 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")); - 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)); + 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(); } -cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() { -} +void cStreamdevServerMenuSetupPage::AddCategory(const char *Title) { + + cString str = cString::sprintf("--- %s -------------------------------------------------" + "---------------", Title ); + cOsdItem *item = new cOsdItem(*str); + item->SetSelectable(false); + Add(item); +} + void cStreamdevServerMenuSetupPage::Store(void) { bool restart = false; if (m_NewSetup.StartVTPServer != StreamdevServerSetup.StartVTPServer @@ -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; +} diff --git a/server/setup.h b/server/setup.h index d2b1592..f71162f 100644 --- a/server/setup.h +++ b/server/setup.h @@ -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); diff --git a/server/streamdev-server.c b/server/streamdev-server.c new file mode 100644 index 0000000..8e9f979 --- /dev/null +++ b/server/streamdev-server.c @@ -0,0 +1,143 @@ +/* + * streamdev.c: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * $Id: streamdev-server.c,v 1.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! diff --git a/server/streamdev-server.h b/server/streamdev-server.h new file mode 100644 index 0000000..1224ecf --- /dev/null +++ b/server/streamdev-server.h @@ -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 diff --git a/server/streamer.c b/server/streamer.c index 42e7efa..b545b21 100644 --- a/server/streamer.c +++ b/server/streamer.c @@ -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")), diff --git a/server/streamer.h b/server/streamer.h index 6561bc2..d3ddd23 100644 --- a/server/streamer.h +++ b/server/streamer.h @@ -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); |