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
nconfig.cpp \
rawfile.cpp \
smartptr.cpp \
- thread.cpp
+ thread.cpp \
+ httpstream.cpp \
+ socketbase.cpp
EXTRA_DIST = \
eenv.cpp.in
rawfile.h \
ringbuffer.h \
smartptr.h \
- thread.h
+ thread.h \
+ httpstream.h \
+ socketbase.h
// 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);
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
}
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;
}
}
}
return 0;
}
-
-RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
+RESULT eDVBResourceManager::allocatePVRChannel(const eDVBChannelID &channelid, eUsePtr<iDVBPVRChannel> &channel)
{
ePtr<eDVBAllocatedDemux> demux;
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;
}
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;
{
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;
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);
/* 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'.
{
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);
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;
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;
}
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;
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
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
};
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;
#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>
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);
{
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;
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;
}
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;
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)
{
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);
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()
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);
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;
#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 */
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);
/* 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;
}
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;