From: Chang.H.S Date: Fri, 22 Jun 2012 01:20:28 +0000 (+0900) Subject: add support http ts stream.(client) X-Git-Url: http://code.vuplus.com/gitweb/?p=vuplus_dvbapp;a=commitdiff_plain;h=377c2adbf11ed3b293bced1a4195cefd8aa6110f add support http ts stream.(client) --- diff --git a/ServiceReference.py b/ServiceReference.py index 4907fa2..0a46992 100644 --- a/ServiceReference.py +++ b/ServiceReference.py @@ -32,4 +32,4 @@ class ServiceReference(eServiceReference): def isRecordable(self): ref = self.ref - return ref.flags & eServiceReference.isGroup or (ref.type == eServiceReference.idDVB and ref.getPath() == "") \ No newline at end of file + return ref.flags & eServiceReference.isGroup or (ref.type == eServiceReference.idDVB) \ No newline at end of file diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index d76dfc9..ca9483d 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -24,7 +24,9 @@ libenigma_base_a_SOURCES = \ nconfig.cpp \ rawfile.cpp \ smartptr.cpp \ - thread.cpp + thread.cpp \ + httpstream.cpp \ + socketbase.cpp EXTRA_DIST = \ eenv.cpp.in @@ -53,4 +55,6 @@ baseinclude_HEADERS = \ rawfile.h \ ringbuffer.h \ smartptr.h \ - thread.h + thread.h \ + httpstream.h \ + socketbase.h diff --git a/lib/base/filepush.cpp b/lib/base/filepush.cpp index e3e2a13..eb80914 100644 --- a/lib/base/filepush.cpp +++ b/lib/base/filepush.cpp @@ -106,7 +106,7 @@ void eFilePushThread::thread() // eDebug("wrote %d bytes", w); if (w <= 0) { - if (errno == EINTR || errno == EAGAIN || errno == EBUSY) + if (w < 0 && (errno == EINTR || errno == EAGAIN || errno == EBUSY)) continue; eDebug("eFilePushThread WRITE ERROR"); sendEvent(evtWriteError); diff --git a/lib/base/httpstream.cpp b/lib/base/httpstream.cpp new file mode 100644 index 0000000..343109e --- /dev/null +++ b/lib/base/httpstream.cpp @@ -0,0 +1,124 @@ +#include + +#include +#include + +DEFINE_REF(eHttpStream); + +eHttpStream::eHttpStream() +{ + streamSocket = -1; +} + +eHttpStream::~eHttpStream() +{ + close(); +} + +int eHttpStream::open(const char *url) +{ + int port; + std::string hostname; + std::string uri = url; + std::string request; + size_t buflen = 1024; + char *linebuf = NULL; + int result; + char proto[100]; + int statuscode = 0; + char statusmsg[100]; + + close(); + + int pathindex = uri.find("/", 7); + if (pathindex > 0) + { + hostname = uri.substr(7, pathindex - 7); + uri = uri.substr(pathindex, uri.length() - pathindex); + } + else + { + hostname = uri.substr(7, uri.length() - 7); + uri = ""; + } + int customportindex = hostname.find(":"); + if (customportindex > 0) + { + port = atoi(hostname.substr(customportindex + 1, hostname.length() - customportindex - 1).c_str()); + hostname = hostname.substr(0, customportindex); + } + else if (customportindex == 0) + { + port = atoi(hostname.substr(1, hostname.length() - 1).c_str()); + hostname = "localhost"; + } + else + { + port = 80; + } + streamSocket = connect(hostname.c_str(), port, 10); + if (streamSocket < 0) goto error; + + request = "GET "; + request.append(uri).append(" HTTP/1.1\r\n"); + request.append("Host: ").append(hostname).append("\r\n"); + request.append("Accept: */*\r\n"); + request.append("Connection: close\r\n"); + request.append("\r\n"); + writeAll(streamSocket, request.c_str(), request.length()); + + linebuf = (char*)malloc(buflen); + + result = readLine(streamSocket, &linebuf, &buflen); + if (result <= 0) goto error; + + result = sscanf(linebuf, "%99s %d %99s", proto, &statuscode, statusmsg); + if (result != 3 || statuscode != 200) + { + eDebug("eHttpStream::open: wrong http response code: %d", statuscode); + goto error; + } + while (result > 0) + { + result = readLine(streamSocket, &linebuf, &buflen); + } + + free(linebuf); + return 0; +error: + eDebug("eHttpStream::open failed"); + free(linebuf); + close(); + return -1; +} + +off_t eHttpStream::lseek(off_t offset, int whence) +{ + return (off_t)-1; +} + +int eHttpStream::close() +{ + int retval = -1; + if (streamSocket >= 0) + { + retval = ::close(streamSocket); + streamSocket = -1; + } + return retval; +} + +ssize_t eHttpStream::read(off_t offset, void *buf, size_t count) +{ + return timedRead(streamSocket, buf, count, 5000, 500); +} + +int eHttpStream::valid() +{ + return streamSocket >= 0; +} + +off_t eHttpStream::length() +{ + return (off_t)-1; +} diff --git a/lib/base/httpstream.h b/lib/base/httpstream.h new file mode 100644 index 0000000..f02bb24 --- /dev/null +++ b/lib/base/httpstream.h @@ -0,0 +1,28 @@ +#ifndef __lib_base_httpstream_h +#define __lib_base_httpstream_h + +#include +#include +#include +#include + +class eHttpStream: public iTsSource, public eSocketBase, public Object +{ + DECLARE_REF(eHttpStream); + + int streamSocket; + + /* iTsSource */ + off_t lseek(off_t offset, int whence); + ssize_t read(off_t offset, void *buf, size_t count); + off_t length(); + int valid(); + +public: + eHttpStream(); + ~eHttpStream(); + int open(const char *url); + int close(); +}; + +#endif diff --git a/lib/base/socketbase.cpp b/lib/base/socketbase.cpp new file mode 100644 index 0000000..d96b057 --- /dev/null +++ b/lib/base/socketbase.cpp @@ -0,0 +1,261 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "socketbase.h" + +#include +#include + +int eSocketBase::select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ + int retval; + fd_set rset, wset, xset; + timeval interval; + + /* make a backup of all fd_set's and timeval struct */ + if (readfds) rset = *readfds; + if (writefds) wset = *writefds; + if (exceptfds) xset = *exceptfds; + if (timeout) + { + interval = *timeout; + } + else + { + /* make gcc happy... */ + timerclear(&interval); + } + + while (1) + { + retval = ::select(maxfd, readfds, writefds, exceptfds, timeout); + + if (retval < 0) + { + /* restore the backup before we continue */ + if (readfds) *readfds = rset; + if (writefds) *writefds = wset; + if (exceptfds) *exceptfds = xset; + if (timeout) *timeout = interval; + if (errno == EINTR) continue; + eDebug("eSocketBase::select error (%m)"); + break; + } + + break; + } + return retval; +} + +ssize_t eSocketBase::singleRead(int fd, void *buf, size_t count) +{ + int retval; + while (1) + { + retval = ::read(fd, buf, count); + if (retval < 0) + { + if (errno == EINTR) continue; + eDebug("eSocketBase::singleRead error (%m)"); + } + return retval; + } +} + +ssize_t eSocketBase::timedRead(int fd, void *buf, size_t count, int initialtimeout, int interbytetimeout) +{ + fd_set rset; + struct timeval timeout; + int result; + size_t totalread = 0; + + while (totalread < count) + { + FD_ZERO(&rset); + FD_SET(fd, &rset); + if (totalread == 0) + { + timeout.tv_sec = initialtimeout/1000; + timeout.tv_usec = (initialtimeout%1000) * 1000; + } + else + { + timeout.tv_sec = interbytetimeout / 1000; + timeout.tv_usec = (interbytetimeout%1000) * 1000; + } + if ((result = select(fd + 1, &rset, NULL, NULL, &timeout)) < 0) return -1; /* error */ + if (result == 0) break; + if ((result = singleRead(fd, ((char*)buf) + totalread, count - totalread)) < 0) + { + return -1; + } + if (result == 0) break; + totalread += result; + } + return totalread; +} + +ssize_t eSocketBase::readLine(int fd, char** buffer, size_t* bufsize) +{ + size_t i = 0; + int result; + while (1) + { + if (i >= *bufsize) + { + char *newbuf = (char*)realloc(*buffer, (*bufsize)+1024); + if (newbuf == NULL) + return -ENOMEM; + *buffer = newbuf; + *bufsize = (*bufsize) + 1024; + } + result = timedRead(fd, (*buffer) + i, 1, 3000, 100); + if (result <= 0 || (*buffer)[i] == '\n') + { + (*buffer)[i] = '\0'; + return result <= 0 ? -1 : i; + } + if ((*buffer)[i] != '\r') i++; + } + return -1; +} + +int eSocketBase::connect(const char *hostname, int port, int timeoutsec) +{ + int sd = -1; + std::vector addresses; + struct addrinfo *info = NULL; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* both ipv4 and ipv6 */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; /* any */ +#ifdef AI_ADDRCONFIG + hints.ai_flags = AI_ADDRCONFIG; /* only return ipv6 if we have an ipv6 address ourselves, and ipv4 if we have an ipv4 address ourselves */ +#else + hints.ai_flags = 0; /* we have only IPV4 support, if AI_ADDRCONFIG is not available */ +#endif + char portstring[15]; + /* this is suboptimal, but the alternative would require us to mess with the memberdata of an 'abstract' addressinfo struct */ + snprintf(portstring, sizeof(portstring), "%d", port); + if (getaddrinfo(hostname, portstring, &hints, &info) || !info) return -1; + struct addrinfo *ptr = info; + while (ptr) + { + addresses.push_back(ptr); + ptr = ptr->ai_next; + } + + for (unsigned int i = 0; i < addresses.size(); i++) + { + sd = ::socket(addresses[i]->ai_family, addresses[i]->ai_socktype, addresses[i]->ai_protocol); + if (sd < 0) break; + int flags; + bool setblocking = false; + if ((flags = fcntl(sd, F_GETFL, 0)) < 0) + { + ::close(sd); + sd = -1; + continue; + } + if (!(flags & O_NONBLOCK)) + { + /* set socket nonblocking, to allow for our own timeout on a nonblocking connect */ + flags |= O_NONBLOCK; + if (fcntl(sd, F_SETFL, flags) < 0) + { + ::close(sd); + sd = -1; + continue; + } + /* remember to restore O_NONBLOCK when we're connected */ + setblocking = true; + } + int connectresult; + while (1) + { + connectresult = ::connect(sd, addresses[i]->ai_addr, addresses[i]->ai_addrlen); + if (connectresult < 0) + { + if (errno == EINTR || errno == EINPROGRESS) + { + int error; + socklen_t len = sizeof(error); + timeval timeout; + fd_set wset; + FD_ZERO(&wset); + FD_SET(sd, &wset); + + timeout.tv_sec = timeoutsec; + timeout.tv_usec = 0; + + if (select(sd + 1, NULL, &wset, NULL, &timeout) <= 0) break; + + if (getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) break; + + if (error) break; + /* we are connected */ + connectresult = 0; + break; + } + } + break; + } + if (connectresult < 0) + { + ::close(sd); + sd = -1; + continue; + } + if (setblocking) + { + /* set socket blocking again */ + flags &= ~O_NONBLOCK; + if (fcntl(sd, F_SETFL, flags) < 0) + { + ::close(sd); + sd = -1; + continue; + } + } + if (sd >= 0) + { +#ifdef SO_NOSIGPIPE + int val = 1; + setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (char*)&val, sizeof(val)); +#endif + /* we have a working connection */ + break; + } + } + freeaddrinfo(info); + return sd; +} + +ssize_t eSocketBase::writeAll(int fd, const void *buf, size_t count) +{ + int retval; + char *ptr = (char*)buf; + size_t handledcount = 0; + while (handledcount < count) + { + retval = ::write(fd, &ptr[handledcount], count - handledcount); + + if (retval == 0) return -1; + if (retval < 0) + { + if (errno == EINTR) continue; + eDebug("eSocketBase::writeAll error (%m)"); + return retval; + } + handledcount += retval; + } + return handledcount; +} diff --git a/lib/base/socketbase.h b/lib/base/socketbase.h new file mode 100644 index 0000000..9d041aa --- /dev/null +++ b/lib/base/socketbase.h @@ -0,0 +1,15 @@ +#ifndef _socketbase_h +#define _socketbase_h + +class eSocketBase +{ +protected: + ssize_t singleRead(int fd, void *buf, size_t count); + ssize_t timedRead(int fd, void *buf, size_t count, int initialtimeout, int interbytetimeout); + ssize_t readLine(int fd, char** buffer, size_t* bufsize); + ssize_t writeAll(int fd, const void *buf, size_t count); + int select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); + int connect(const char *hostname, int port, int timeoutsec); +}; + +#endif diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp old mode 100755 new mode 100644 index af46823..ce09603 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -525,30 +525,46 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtrm_adapter : m_adapter.begin(); /* look for a demux on the same adapter as the frontend, or the first adapter for dvr playback */ + int source = fe ? fe->m_frontend->getDVBID() : -1; cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux - for (; i != m_demux.end(); ++i, ++n) + if (!fe) { - if (fe) + /* + * For pvr playback, start with the last demux. + * On some hardware, we have less ca devices than demuxes, + * so we should try to leave the first demuxes for live tv, + * and start with the last for pvr playback + */ + i = m_demux.end(); + --i; + } + while (i != m_demux.end()) + { + if (i->m_adapter == adapter) { if (!i->m_inuse) { - if (!unused) - unused = i; + /* mark the first unused demux, we'll use that when we do not find a better match */ + if (!unused) unused = i; } - else if (i->m_adapter == fe->m_adapter && - i->m_demux->getSource() == fe->m_frontend->getDVBID()) + else { - demux = new eDVBAllocatedDemux(i); - return 0; + /* demux is in use, see if we can share it */ + if (source >= 0 && i->m_demux->getSource() == source) + { + demux = new eDVBAllocatedDemux(i); + return 0; + } } } - else if (n == 4) // always use demux4 for PVR (demux 4 can not descramble...) + if (fe) { - if (i->m_inuse) { - demux = new eDVBAllocatedDemux(i); - return 0; - } - unused = i; + ++i; + } + else + { + --i; } } } @@ -719,8 +735,7 @@ RESULT eDVBResourceManager::allocateRawChannel(eUsePtr &channel, in return 0; } - -RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr &channel) +RESULT eDVBResourceManager::allocatePVRChannel(const eDVBChannelID &channelid, eUsePtr &channel) { ePtr demux; @@ -731,7 +746,18 @@ RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr &channel) m_releaseCachedChannelTimer->stop(); } - channel = new eDVBChannel(this, 0); + ePtr ch = new eDVBChannel(this, 0); + if (channelid) + { + /* + * user provided a channelid, with the clear intention for + * this channel to be registered at the resource manager. + * (allowing e.g. epgcache to be started) + */ + ePtr feparm; + ch->setChannel(channelid, feparm); + } + channel = ch; return 0; } @@ -1605,14 +1631,15 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtraddChannel(channelid, this); + if (!m_frontend) { - eDebug("no frontend to tune!"); - return -ENODEV; + /* no frontend, no need to tune (must be a streamed service) */ + return 0; } - m_channel_id = channelid; - m_mgr->addChannel(channelid, this); m_state = state_tuning; /* if tuning fails, shutdown the channel immediately. */ int res; @@ -1723,6 +1750,12 @@ RESULT eDVBChannel::getDemux(ePtr &demux, int cap) { ePtr &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux; + if (m_frontend == NULL) + { + /* in dvr mode, we have to stick to a single demux (the one connected to our dvr device) */ + our_demux = m_decoder_demux ? m_decoder_demux : m_demux; + } + if (!our_demux) { demux = 0; @@ -1833,7 +1866,8 @@ RESULT eDVBChannel::playSource(ePtr &source, const char *streaminfo_f m_pvr_thread = new eDVBChannelFilePush(); m_pvr_thread->enablePVRCommit(1); - m_pvr_thread->setStreamMode(1); + /* If the source specifies a length, it's a file. If not, it's a stream */ + m_pvr_thread->setStreamMode(source->length() <= 0); m_pvr_thread->setScatterGather(this); m_event(this, evtPreStart); diff --git a/lib/dvb/dvb.h b/lib/dvb/dvb.h index 3349014..fb90ae5 100644 --- a/lib/dvb/dvb.h +++ b/lib/dvb/dvb.h @@ -195,7 +195,7 @@ public: /* allocate channel... */ RESULT allocateChannel(const eDVBChannelID &channelid, eUsePtr &channel, bool simulate=false); - RESULT allocatePVRChannel(eUsePtr &channel); + RESULT allocatePVRChannel(const eDVBChannelID &channelid, eUsePtr &channel); static RESULT getInstance(ePtr &); /* allocates a frontend able to tune to frontend paramters 'feperm'. diff --git a/lib/dvb/frontend.cpp b/lib/dvb/frontend.cpp old mode 100755 new mode 100644 diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp index 227928b..25577f0 100644 --- a/lib/dvb/pmt.cpp +++ b/lib/dvb/pmt.cpp @@ -25,6 +25,7 @@ eDVBServicePMTHandler::eDVBServicePMTHandler() { m_use_decode_demux = 0; m_pmt_pid = -1; + m_isstreamclient = false; eDVBResourceManager::getInstance(m_resourceManager); CONNECT(m_PMT.tableReady, eDVBServicePMTHandler::PMTready); CONNECT(m_PAT.tableReady, eDVBServicePMTHandler::PATready); @@ -109,7 +110,7 @@ void eDVBServicePMTHandler::PMTready(int error) if (!m_pvr_channel) // don't send campmt to camd.socket for playbacked services { eEPGCache::getInstance()->PMTready(this); - if(!m_ca_servicePtr) + if(!m_ca_servicePtr && !m_isstreamclient) { int demuxes[2] = {0,0}; uint8_t tmp; @@ -782,12 +783,13 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, return tuneExt(ref, use_decode_demux, s, NULL, cue, simulate, service); } -int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr &source, const char *streaminfo_file, eCueSheet *cue, bool simulate, eDVBService *service) +int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr &source, const char *streaminfo_file, eCueSheet *cue, bool simulate, eDVBService *service, bool isstreamclient) { RESULT res=0; m_reference = ref; m_use_decode_demux = use_decode_demux; m_no_pat_entry_delay->stop(); + m_isstreamclient = isstreamclient; /* use given service as backup. This is used for timeshift where we want to clone the live stream using the cache, but in fact have a PVR channel */ m_service = service; @@ -831,7 +833,9 @@ int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_dem } eDebug("alloc PVR"); /* allocate PVR */ - res = m_resourceManager->allocatePVRChannel(m_pvr_channel); + eDVBChannelID chid; + if (m_isstreamclient) ref.getChannelID(chid); + res = m_resourceManager->allocatePVRChannel(chid, m_pvr_channel); if (res) eDebug("allocatePVRChannel failed!\n"); m_channel = m_pvr_channel; @@ -871,7 +875,15 @@ int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_dem m_pvr_channel->setCueSheet(cue); if (m_pvr_channel->getDemux(m_pvr_demux_tmp, (!m_use_decode_demux) ? 0 : iDVBChannel::capDecode)) - eDebug("Allocating %s-decoding a demux for PVR channel failed.", m_use_decode_demux ? "" : "non-"); + { + if (m_isstreamclient) + { + eDebug("Allocating %s-decoding a demux for http channel failed.", m_use_decode_demux ? "" : "non-"); + return -2; + } + else + eDebug("Allocating %s-decoding a demux for PVR channel failed.", m_use_decode_demux ? "" : "non-"); + } else if (source) m_pvr_channel->playSource(source, streaminfo_file); else diff --git a/lib/dvb/pmt.h b/lib/dvb/pmt.h index 4be8000..363176d 100644 --- a/lib/dvb/pmt.h +++ b/lib/dvb/pmt.h @@ -217,12 +217,13 @@ public: int tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0); /* new interface */ - int tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr &, const char *streaminfo_file, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0); + int tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr &, const char *streaminfo_file, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0, bool isstreamclient=false); void free(); private: bool m_have_cached_program; program m_cached_program; + bool m_isstreamclient; #endif }; diff --git a/lib/service/service.cpp b/lib/service/service.cpp index 8c674c5..11cf95b 100644 --- a/lib/service/service.cpp +++ b/lib/service/service.cpp @@ -72,10 +72,23 @@ eServiceReference::eServiceReference(const std::string &string) const char *namestr = strchr(pathstr, ':'); if (namestr) { - if (pathstr != namestr) - path.assign(pathstr, namestr-pathstr); - if (*(namestr+1)) - name=namestr+1; + if (!strncmp(namestr, "://", 3)) // The path is a url (e.g. "http://...") + { + namestr = strchr(namestr, ' '); + if (namestr) + { + path.assign(pathstr, namestr - pathstr); + if (*(namestr + 1)) + name = namestr + 1; + } + } + else + { + if (pathstr != namestr) + path.assign(pathstr, namestr-pathstr); + if (*(namestr+1)) + name=namestr+1; + } } else path=pathstr; diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp index 383b38e..93b0cc6 100644 --- a/lib/service/servicedvb.cpp +++ b/lib/service/servicedvb.cpp @@ -17,6 +17,7 @@ #include #include #include // access to python config +#include /* for subtitles */ #include @@ -292,7 +293,7 @@ public: RESULT getName(const eServiceReference &ref, std::string &name); int getLength(const eServiceReference &ref); RESULT getEvent(const eServiceReference &ref, ePtr &SWIG_OUTPUT, time_t start_time); - int isPlayable(const eServiceReference &ref, const eServiceReference &ignore) { return 1; } + int isPlayable(const eServiceReference &ref, const eServiceReference &ignore, bool simulate) { return 1; } int getInfo(const eServiceReference &ref, int w); std::string getInfoString(const eServiceReference &ref,int w); PyObject *getInfoObject(const eServiceReference &r, int what); @@ -404,14 +405,25 @@ RESULT eStaticServiceDVBPVRInformation::getEvent(const eServiceReference &ref, e { if (!ref.path.empty()) { - ePtr event = new eServiceEvent; - std::string filename = ref.path; - filename.erase(filename.length()-2, 2); - filename+="eit"; - if (!event->parseFrom(filename, (m_parser.m_ref.getTransportStreamID().get()<<16)|m_parser.m_ref.getOriginalNetworkID().get())) + if (ref.path.substr(0, 7) == "http://") { - evt = event; - return 0; + eServiceReference equivalentref(ref); + /* this might be a scrambled stream (id + 0x100), force equivalent dvb type */ + equivalentref.type = eServiceFactoryDVB::id; + equivalentref.path.clear(); + return eEPGCache::getInstance()->lookupEventTime(equivalentref, start_time, evt); + } + else + { + ePtr event = new eServiceEvent; + std::string filename = ref.path; + filename.erase(filename.length()-2, 2); + filename+="eit"; + if (!event->parseFrom(filename, (m_parser.m_ref.getTransportStreamID().get()<<16)|m_parser.m_ref.getOriginalNetworkID().get())) + { + evt = event; + return 0; + } } } evt = 0; @@ -816,6 +828,12 @@ RESULT eServiceFactoryDVB::record(const eServiceReference &ref, ePtr source = createTsSource(service); - m_service_handler.tuneExt(service, m_is_pvr, source, service.path.c_str(), m_cue, false, m_dvb_service); + m_service_handler.tuneExt(service, m_is_pvr, source, service.path.c_str(), m_cue, false, m_dvb_service, m_is_stream); if (m_is_pvr) { @@ -1569,6 +1588,18 @@ RESULT eDVBServicePlay::getName(std::string &name) ePtr i = new eStaticServiceDVBPVRInformation(m_reference); return i->getName(m_reference, name); } + else if (m_is_stream) + { + name = m_reference.name; + if (name.empty()) + { + name = m_reference.path; + } + if (name.empty()) + { + name = "(...)"; + } + } else if (m_dvb_service) { m_dvb_service->getName(m_reference, name); @@ -2370,9 +2401,18 @@ void eDVBServicePlay::resetTimeshift(int start) ePtr eDVBServicePlay::createTsSource(eServiceReferenceDVB &ref) { - eRawFile *f = new eRawFile(); - f->open(ref.path.c_str()); - return ePtr(f); + if (m_is_stream) + { + eHttpStream *f = new eHttpStream(); + f->open(ref.path.c_str()); + return ePtr(f); + } + else + { + eRawFile *f = new eRawFile(); + f->open(ref.path.c_str()); + return ePtr(f); + } } void eDVBServicePlay::switchToTimeshift() @@ -2526,7 +2566,7 @@ void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged) m_decoder->setVideoPID(vpid, vpidtype); selectAudioStream(); - if (!(m_is_pvr || m_timeshift_active || !m_is_primary)) + if (!(m_is_pvr || m_is_stream || m_timeshift_active || !m_is_primary)) m_decoder->setSyncPCR(pcrpid); else m_decoder->setSyncPCR(-1); diff --git a/lib/service/servicedvb.h b/lib/service/servicedvb.h index 3efc259..6ced102 100644 --- a/lib/service/servicedvb.h +++ b/lib/service/servicedvb.h @@ -209,6 +209,8 @@ protected: void serviceEvent(int event); void serviceEventTimeshift(int event); Signal2 m_event; + + int m_is_stream; /* pvr */ int m_is_pvr, m_is_paused, m_timeshift_enabled, m_timeshift_active, m_timeshift_changed; diff --git a/lib/service/servicedvbrecord.cpp b/lib/service/servicedvbrecord.cpp index 08cd247..0287acf 100644 --- a/lib/service/servicedvbrecord.cpp +++ b/lib/service/servicedvbrecord.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include /* for cutlist */ @@ -14,7 +15,8 @@ DEFINE_REF(eDVBServiceRecord); -eDVBServiceRecord::eDVBServiceRecord(const eServiceReferenceDVB &ref): m_ref(ref) +eDVBServiceRecord::eDVBServiceRecord(const eServiceReferenceDVB &ref, bool isstreamclient = false): + m_ref(ref),m_is_stream_client(isstreamclient) { CONNECT(m_service_handler.serviceEvent, eDVBServiceRecord::serviceEvent); CONNECT(m_event_handler.m_eit_changed, eDVBServiceRecord::gotNewEvent); @@ -232,9 +234,28 @@ int eDVBServiceRecord::doPrepare() /* allocate a ts recorder if we don't already have one. */ if (m_state == stateIdle) { + bool isstreamclient = false; m_pids_active.clear(); m_state = statePrepared; - return m_service_handler.tune(m_ref, 0, 0, m_simulate); + ePtr source; + if (!m_ref.path.empty()) + { + if (m_is_stream_client) + { + isstreamclient = true; + eHttpStream *f = new eHttpStream(); + f->open(m_ref.path.c_str()); + source = ePtr(f); + } + else + { + /* re-record a recording */ + eRawFile *f = new eRawFile(); + f->open(m_ref.path.c_str()); + source = ePtr(f); + } + } + return m_service_handler.tuneExt(m_ref, 0, source, m_ref.path.c_str(), 0, m_simulate, 0, isstreamclient); } return 0; } diff --git a/lib/service/servicedvbrecord.h b/lib/service/servicedvbrecord.h index 0535f02..7b53803 100644 --- a/lib/service/servicedvbrecord.h +++ b/lib/service/servicedvbrecord.h @@ -38,8 +38,9 @@ private: enum { stateIdle, statePrepared, stateRecording }; bool m_simulate; int m_state, m_want_record; + bool m_is_stream_client; friend class eServiceFactoryDVB; - eDVBServiceRecord(const eServiceReferenceDVB &ref); + eDVBServiceRecord(const eServiceReferenceDVB &ref, bool isstreamclient = false); eDVBServiceEITHandler m_event_handler;