add support http ts stream.(client)
authorChang.H.S <jhs@dev3>
Fri, 22 Jun 2012 01:20:28 +0000 (10:20 +0900)
committerChang.H.S <jhs@dev3>
Fri, 22 Jun 2012 08:50:40 +0000 (17:50 +0900)
17 files changed:
ServiceReference.py
lib/base/Makefile.am
lib/base/filepush.cpp
lib/base/httpstream.cpp [new file with mode: 0644]
lib/base/httpstream.h [new file with mode: 0644]
lib/base/socketbase.cpp [new file with mode: 0644]
lib/base/socketbase.h [new file with mode: 0644]
lib/dvb/dvb.cpp [changed mode: 0755->0644]
lib/dvb/dvb.h
lib/dvb/frontend.cpp [changed mode: 0755->0644]
lib/dvb/pmt.cpp
lib/dvb/pmt.h
lib/service/service.cpp
lib/service/servicedvb.cpp
lib/service/servicedvb.h
lib/service/servicedvbrecord.cpp
lib/service/servicedvbrecord.h

index 4907fa2..0a46992 100644 (file)
@@ -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
index d76dfc9..ca9483d 100644 (file)
@@ -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
index e3e2a13..eb80914 100644 (file)
@@ -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 (file)
index 0000000..343109e
--- /dev/null
@@ -0,0 +1,124 @@
+#include <cstdio>
+
+#include <lib/base/httpstream.h>
+#include <lib/base/eerror.h>
+
+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 (file)
index 0000000..f02bb24
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __lib_base_httpstream_h
+#define __lib_base_httpstream_h
+
+#include <string>
+#include <lib/base/ebase.h>
+#include <lib/base/itssource.h>
+#include <lib/base/socketbase.h>
+
+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 (file)
index 0000000..d96b057
--- /dev/null
@@ -0,0 +1,261 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <vector>
+#include <string>
+
+#include "socketbase.h"
+
+#include <lib/base/ebase.h>
+#include <lib/base/eerror.h>
+
+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<struct addrinfo *> 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 (file)
index 0000000..9d041aa
--- /dev/null
@@ -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
old mode 100755 (executable)
new mode 100644 (file)
index af46823..ce09603
@@ -525,30 +525,46 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
        }
        else if (m_boxtype == DM8000 || m_boxtype == DM500HD || m_boxtype == DM800SE || m_boxtype == DM7020HD)
        {
+               iDVBAdapter *adapter = fe ? fe->m_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<iDVBChannel> &channel, in
        return 0;
 }
 
-
-RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
+RESULT eDVBResourceManager::allocatePVRChannel(const eDVBChannelID &channelid, eUsePtr<iDVBPVRChannel> &channel)
 {
        ePtr<eDVBAllocatedDemux> demux;
 
@@ -731,7 +746,18 @@ RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
                m_releaseCachedChannelTimer->stop();
        }
 
-       channel = new eDVBChannel(this, 0);
+       ePtr<eDVBChannel> 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<iDVBFrontendParameters> feparm;
+               ch->setChannel(channelid, feparm);
+       }
+       channel = ch;
        return 0;
 }
 
@@ -1605,14 +1631,15 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontend
        if (!channelid)
                return 0;
 
+       m_channel_id = channelid;
+       m_mgr->addChannel(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<iDVBDemux> &demux, int cap)
 {
        ePtr<eDVBAllocatedDemux> &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<iTsSource> &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);
index 3349014..fb90ae5 100644 (file)
@@ -195,7 +195,7 @@ public:
 
                /* allocate channel... */
        RESULT allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel, bool simulate=false);
-       RESULT allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel);
+       RESULT allocatePVRChannel(const eDVBChannelID &channelid, eUsePtr<iDVBPVRChannel> &channel);
        static RESULT getInstance(ePtr<eDVBResourceManager> &);
 
                        /* allocates a frontend able to tune to frontend paramters 'feperm'.
old mode 100755 (executable)
new mode 100644 (file)
index 227928b..25577f0 100644 (file)
@@ -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<iTsSource> &source, const char *streaminfo_file, eCueSheet *cue, bool simulate, eDVBService *service)
+int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr<iTsSource> &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
index 4be8000..363176d 100644 (file)
@@ -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<iTsSource> &, const char *streaminfo_file, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0);
+       int tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr<iTsSource> &, 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
 };
 
index 8c674c5..11cf95b 100644 (file)
@@ -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;
index 383b38e..93b0cc6 100644 (file)
@@ -17,6 +17,7 @@
 #include <lib/dvb/tstools.h>
 #include <lib/python/python.h>
 #include <lib/base/nconfig.h> // access to python config
+#include <lib/base/httpstream.h>
 
                /* for subtitles */
 #include <lib/gui/esubtitle.h>
@@ -292,7 +293,7 @@ public:
        RESULT getName(const eServiceReference &ref, std::string &name);
        int getLength(const eServiceReference &ref);
        RESULT getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &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<eServiceEvent> 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<eServiceEvent> 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<iRecordable
                return 0;
        } else
        {
+               bool isstream = ref.path.substr(0, 7) == "http://";
+               if(isstream)
+               {
+                       ptr = new eDVBServiceRecord((eServiceReferenceDVB&)ref, isstream);
+                       return 0;
+               }
                ptr = 0;
                return -1;
        }
@@ -917,7 +935,8 @@ eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *serv
        m_reference(ref), m_dvb_service(service), m_have_video_pid(0), m_is_paused(0)
 {
        m_is_primary = 1;
-       m_is_pvr = !m_reference.path.empty();
+       m_is_stream = m_reference.path.substr(0, 7) == "http://";
+       m_is_pvr = (!m_reference.path.empty() && !m_is_stream);
        
        m_timeshift_enabled = m_timeshift_active = 0, m_timeshift_changed = 0;
        m_skipmode = m_fastforward = m_slowmotion = 0;
@@ -1158,7 +1177,7 @@ RESULT eDVBServicePlay::start()
 
        m_first_program_info = 1;
        ePtr<iTsSource> 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<iStaticServiceInformation> 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<iTsSource> eDVBServicePlay::createTsSource(eServiceReferenceDVB &ref)
 {
-       eRawFile *f = new eRawFile();
-       f->open(ref.path.c_str());
-       return ePtr<iTsSource>(f);
+       if (m_is_stream)
+       {
+               eHttpStream *f = new eHttpStream();
+               f->open(ref.path.c_str());
+               return ePtr<iTsSource>(f);
+       }
+       else
+       {
+               eRawFile *f = new eRawFile();
+               f->open(ref.path.c_str());
+               return ePtr<iTsSource>(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);
index 3efc259..6ced102 100644 (file)
@@ -209,6 +209,8 @@ protected:
        void serviceEvent(int event);
        void serviceEventTimeshift(int event);
        Signal2<void,iPlayableService*,int> m_event;
+
+       int m_is_stream;
        
                /* pvr */
        int m_is_pvr, m_is_paused, m_timeshift_enabled, m_timeshift_active, m_timeshift_changed;
index 08cd247..0287acf 100644 (file)
@@ -2,6 +2,7 @@
 #include <lib/base/eerror.h>
 #include <lib/dvb/epgcache.h>
 #include <lib/dvb/metaparser.h>
+#include <lib/base/httpstream.h>
 #include <fcntl.h>
 
        /* 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<iTsSource> 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<iTsSource>(f);
+                       }
+                       else
+                       {
+                               /* re-record a recording */
+                               eRawFile *f = new eRawFile();
+                               f->open(m_ref.path.c_str());
+                               source = ePtr<iTsSource>(f);
+                       }
+               }
+               return m_service_handler.tuneExt(m_ref, 0, source, m_ref.path.c_str(), 0, m_simulate, 0, isstreamclient);
        }
        return 0;
 }
index 0535f02..7b53803 100644 (file)
@@ -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;