summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorghost <andreas.monzner@multimedia-labs.de>2010-11-26 14:02:25 (GMT)
committerghost <andreas.monzner@multimedia-labs.de>2010-11-26 14:02:25 (GMT)
commitd98f00f0fdd8b95b819b17c1d4779610c657c198 (patch)
treed23a9c8007eff9e0c268befe8f930f458ac2981a /lib
parentc4a0d51174feca310d0c45cc34be3c910a306228 (diff)
parent070b9256befecb72bbdc7052aef61e5f2f0b9515 (diff)
Merge branch 'bug_124_m2ts_support' into experimental
Conflicts: lib/dvb/pmt.cpp lib/service/Makefile.am
Diffstat (limited to 'lib')
-rw-r--r--lib/dvb/decoder.cpp3
-rw-r--r--lib/dvb/decoder.h2
-rw-r--r--lib/dvb/idvb.h2
-rw-r--r--lib/dvb/pmt.cpp76
-rw-r--r--lib/dvb/pmt.h2
-rw-r--r--lib/dvb/tstools.cpp2
-rwxr-xr-xlib/python/Components/FileList.py3
-rwxr-xr-xlib/python/Plugins/Extensions/MediaPlayer/plugin.py2
-rw-r--r--lib/service/Makefile.am6
-rw-r--r--lib/service/servicedvb.cpp2
-rw-r--r--lib/service/servicem2ts.cpp380
-rw-r--r--lib/service/servicem2ts.h33
12 files changed, 485 insertions, 28 deletions
diff --git a/lib/dvb/decoder.cpp b/lib/dvb/decoder.cpp
index 88cd3ee..8ed9f43 100644
--- a/lib/dvb/decoder.cpp
+++ b/lib/dvb/decoder.cpp
@@ -203,6 +203,9 @@ int eDVBAudio::startPid(int pid, int type)
case aLPCM:
bypass = 6;
break;
+ case aDTSHD:
+ bypass = 0x10;
+ break;
}
eDebugNoNewLine("AUDIO_SET_BYPASS(%d) - ", bypass);
diff --git a/lib/dvb/decoder.h b/lib/dvb/decoder.h
index 3a0fbac..7610b65 100644
--- a/lib/dvb/decoder.h
+++ b/lib/dvb/decoder.h
@@ -13,7 +13,7 @@ private:
ePtr<eDVBDemux> m_demux;
int m_fd, m_fd_demux, m_dev, m_is_freezed;
public:
- enum { aMPEG, aAC3, aDTS, aAAC, aAACHE, aLPCM };
+ enum { aMPEG, aAC3, aDTS, aAAC, aAACHE, aLPCM, aDTSHD };
eDVBAudio(eDVBDemux *demux, int dev);
enum { aMonoLeft, aStereo, aMonoRight };
void setChannel(int channel);
diff --git a/lib/dvb/idvb.h b/lib/dvb/idvb.h
index f15cd04..e56a2c7 100644
--- a/lib/dvb/idvb.h
+++ b/lib/dvb/idvb.h
@@ -650,7 +650,7 @@ public:
/** Set Displayed Video PID and type */
virtual RESULT setVideoPID(int vpid, int type)=0;
- enum { af_MPEG, af_AC3, af_DTS, af_AAC };
+ enum { af_MPEG, af_AC3, af_DTS, af_AAC, af_DTSHD };
/** Set Displayed Audio PID and type */
virtual RESULT setAudioPID(int apid, int type)=0;
diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp
index 0de78b4..d6f2685 100644
--- a/lib/dvb/pmt.cpp
+++ b/lib/dvb/pmt.cpp
@@ -131,6 +131,8 @@ void eDVBServicePMTHandler::PATready(int)
ePtr<eTable<ProgramAssociationSection> > ptr;
if (!m_PAT.getCurrent(ptr))
{
+ int service_id_single = -1;
+ int pmtpid_single = -1;
int pmtpid = -1;
std::vector<ProgramAssociationSection*>::const_iterator i;
for (i = ptr->getSections().begin(); pmtpid == -1 && i != ptr->getSections().end(); ++i)
@@ -138,8 +140,22 @@ void eDVBServicePMTHandler::PATready(int)
const ProgramAssociationSection &pat = **i;
ProgramAssociationConstIterator program;
for (program = pat.getPrograms()->begin(); pmtpid == -1 && program != pat.getPrograms()->end(); ++program)
+ {
if (eServiceID((*program)->getProgramNumber()) == m_reference.getServiceID())
pmtpid = (*program)->getProgramMapPid();
+ if (pmtpid_single == -1 && pmtpid == -1)
+ {
+ pmtpid_single = (*program)->getProgramMapPid();
+ service_id_single = (*program)->getProgramNumber();
+ }
+ else
+ pmtpid_single = service_id_single = -1;
+ }
+ }
+ if (pmtpid_single != -1) // only one PAT entry .. so we use this one
+ {
+ m_reference.setServiceID(eServiceID(service_id_single));
+ pmtpid = pmtpid_single;
}
if (pmtpid == -1)
serviceEvent(eventNoPATEntry);
@@ -226,8 +242,29 @@ int eDVBServicePMTHandler::getProgramInfo(program &program)
for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i)
{
const ProgramMapSection &pmt = **i;
+ int is_hdmv = 0;
+
program.pcrPid = pmt.getPcrPid();
+ for (DescriptorConstIterator desc = pmt.getDescriptors()->begin();
+ desc != pmt.getDescriptors()->end(); ++desc)
+ {
+ if ((*desc)->getTag() == CA_DESCRIPTOR)
+ {
+ CaDescriptor *descr = (CaDescriptor*)(*desc);
+ program::capid_pair pair;
+ pair.caid = descr->getCaSystemId();
+ pair.capid = descr->getCaPid();
+ program.caids.insert(pair);
+ }
+ else if ((*desc)->getTag() == REGISTRATION_DESCRIPTOR)
+ {
+ RegistrationDescriptor *d = (RegistrationDescriptor*)(*desc);
+ if (d->getFormatIdentifier() == 0x48444d56) // HDMV
+ is_hdmv = 1;
+ }
+ }
+
ElementaryStreamInfoConstIterator es;
for (es = pmt.getEsInfo()->begin(); es != pmt.getEsInfo()->end(); ++es)
{
@@ -283,25 +320,34 @@ int eDVBServicePMTHandler::getProgramInfo(program &program)
audio.type = audioStream::atAACHE;
forced_audio = 1;
}
- case 0x80: // user private ... but blueray LPCM
- if (!isvideo && !isaudio)
+ case 0x80: // user private ... but bluray LPCM
+ case 0xA0: // bluray secondary LPCM
+ if (!isvideo && !isaudio && is_hdmv)
{
isaudio = 1;
audio.type = audioStream::atLPCM;
}
- case 0x81: // user private ... but blueray AC3
- if (!isvideo && !isaudio)
+ case 0x81: // user private ... but bluray AC3
+ case 0xA1: // bluray secondary AC3
+ if (!isvideo && !isaudio && is_hdmv)
{
isaudio = 1;
audio.type = audioStream::atAC3;
}
- case 0x82: // Blueray DTS (dvb user private...)
- case 0xA2: // Blueray secondary DTS
- if (!isvideo && !isaudio)
+ case 0x82: // bluray DTS (dvb user private...)
+ case 0xA2: // bluray secondary DTS
+ if (!isvideo && !isaudio && is_hdmv)
{
isaudio = 1;
audio.type = audioStream::atDTS;
}
+ case 0x86: // bluray DTS-HD (dvb user private...)
+ case 0xA6: // bluray secondary DTS-HD
+ if (!isvideo && !isaudio && is_hdmv)
+ {
+ isaudio = 1;
+ audio.type = audioStream::atDTSHD;
+ }
case 0x06: // PES Private
case 0xEA: // TS_PSI_ST_SMPTE_VC1
{
@@ -493,9 +539,9 @@ int eDVBServicePMTHandler::getProgramInfo(program &program)
default:
break;
}
- if (isteletext && (isaudio || isvideo))
+ if (isteletext && (isaudio || isvideo))
{
- eDebug("ambiguous streamtype for PID %04x detected.. forced as teletext!", (*es)->getPid());
+ eDebug("ambiguous streamtype for PID %04x detected.. forced as teletext!", (*es)->getPid());
continue; // continue with next PID
}
else if (issubtitle && (isaudio || isvideo))
@@ -533,18 +579,6 @@ int eDVBServicePMTHandler::getProgramInfo(program &program)
else
continue;
}
- for (DescriptorConstIterator desc = pmt.getDescriptors()->begin();
- desc != pmt.getDescriptors()->end(); ++desc)
- {
- if ((*desc)->getTag() == CA_DESCRIPTOR)
- {
- CaDescriptor *descr = (CaDescriptor*)(*desc);
- program::capid_pair pair;
- pair.caid = descr->getCaSystemId();
- pair.capid = descr->getCaPid();
- program.caids.insert(pair);
- }
- }
}
ret = 0;
diff --git a/lib/dvb/pmt.h b/lib/dvb/pmt.h
index d03267b..c4e4f3b 100644
--- a/lib/dvb/pmt.h
+++ b/lib/dvb/pmt.h
@@ -144,7 +144,7 @@ public:
{
int pid,
rdsPid; // hack for some radio services which transmit radiotext on different pid (i.e. harmony fm, HIT RADIO FFH, ...)
- enum { atMPEG, atAC3, atDTS, atAAC, atAACHE, atLPCM };
+ enum { atMPEG, atAC3, atDTS, atAAC, atAACHE, atLPCM, atDTSHD };
int type; // mpeg2, ac3, dts, ...
int component_tag;
diff --git a/lib/dvb/tstools.cpp b/lib/dvb/tstools.cpp
index ad55509..717ccfc 100644
--- a/lib/dvb/tstools.cpp
+++ b/lib/dvb/tstools.cpp
@@ -212,6 +212,8 @@ int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed)
break;
case 0x71: // AC3 / DTS
break;
+ case 0x72: // DTS - HD
+ break;
default:
eDebug("skip unknwn stream_id_extension %02x\n", payload[9+offs]);
continue;
diff --git a/lib/python/Components/FileList.py b/lib/python/Components/FileList.py
index 1d71514..1b7e81f 100755
--- a/lib/python/Components/FileList.py
+++ b/lib/python/Components/FileList.py
@@ -28,7 +28,8 @@ EXTENSIONS = {
"mpeg": "movie",
"mkv": "movie",
"mp4": "movie",
- "mov": "movie"
+ "mov": "movie",
+ "m2ts": "movie",
}
def FileEntryComponent(name, absolute = None, isDir = False):
diff --git a/lib/python/Plugins/Extensions/MediaPlayer/plugin.py b/lib/python/Plugins/Extensions/MediaPlayer/plugin.py
index 9ae886f..d61dbc7 100755
--- a/lib/python/Plugins/Extensions/MediaPlayer/plugin.py
+++ b/lib/python/Plugins/Extensions/MediaPlayer/plugin.py
@@ -110,7 +110,7 @@ class MediaPlayer(Screen, InfoBarBase, InfoBarSeek, InfoBarAudioSelection, InfoB
# 'None' is magic to start at the list of mountpoints
defaultDir = config.mediaplayer.defaultDir.getValue()
- self.filelist = FileList(defaultDir, matchingPattern = "(?i)^.*\.(mp2|mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob|avi|divx|m4v|mkv|mp4|m4a|dat|flac|mov)", useServiceRef = True, additionalExtensions = "4098:m3u 4098:e2pls 4098:pls")
+ self.filelist = FileList(defaultDir, matchingPattern = "(?i)^.*\.(mp2|mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob|avi|divx|m4v|mkv|mp4|m4a|dat|flac|mov|m2ts)", useServiceRef = True, additionalExtensions = "4098:m3u 4098:e2pls 4098:pls")
self["filelist"] = self.filelist
self.playlist = MyPlayList()
diff --git a/lib/service/Makefile.am b/lib/service/Makefile.am
index edafd1a..9f956b6 100644
--- a/lib/service/Makefile.am
+++ b/lib/service/Makefile.am
@@ -16,7 +16,8 @@ libenigma_service_a_SOURCES = \
servicedvb.cpp \
servicedvbrecord.cpp \
servicefs.cpp \
- servicemp3.cpp
+ servicemp3.cpp \
+ servicem2ts.cpp
serviceincludedir = $(pkgincludedir)/lib/service
serviceinclude_HEADERS = \
@@ -27,7 +28,8 @@ serviceinclude_HEADERS = \
servicedvb.h \
servicedvbrecord.h \
servicefs.h \
- servicemp3.h
+ servicemp3.h \
+ servicem2ts.h
if HAVE_LIBXINE
libenigma_service_a_SOURCES += \
diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp
index 6a464af..2889ad3 100644
--- a/lib/service/servicedvb.cpp
+++ b/lib/service/servicedvb.cpp
@@ -1801,6 +1801,8 @@ RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int
info.m_description = "AAC-HE";
else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTS)
info.m_description = "DTS";
+ else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTSHD)
+ info.m_description = "DTS-HD";
else
info.m_description = "???";
diff --git a/lib/service/servicem2ts.cpp b/lib/service/servicem2ts.cpp
new file mode 100644
index 0000000..e79907d
--- /dev/null
+++ b/lib/service/servicem2ts.cpp
@@ -0,0 +1,380 @@
+#include <lib/base/init_num.h>
+#include <lib/base/init.h>
+#include <lib/dvb/metaparser.h>
+#include <lib/service/servicem2ts.h>
+
+DEFINE_REF(eServiceFactoryM2TS)
+
+class eM2TSFile: public iTsSource
+{
+ DECLARE_REF(eM2TSFile);
+ eSingleLock m_lock;
+public:
+ eM2TSFile(const char *filename, bool cached=false);
+ ~eM2TSFile();
+
+ // 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();
+private:
+ int m_sync_offset;
+ int m_fd; /* for uncached */
+ FILE *m_file; /* for cached */
+ off_t m_current_offset, m_length;
+ bool m_cached;
+ off_t lseek_internal(off_t offset, int whence);
+};
+
+class eStaticServiceM2TSInformation: public iStaticServiceInformation
+{
+ DECLARE_REF(eStaticServiceM2TSInformation);
+ eServiceReference m_ref;
+ eDVBMetaParser m_parser;
+public:
+ eStaticServiceM2TSInformation(const eServiceReference &ref);
+ 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 getInfo(const eServiceReference &ref, int w);
+ std::string getInfoString(const eServiceReference &ref,int w);
+ PyObject *getInfoObject(const eServiceReference &r, int what);
+};
+
+DEFINE_REF(eStaticServiceM2TSInformation);
+
+eStaticServiceM2TSInformation::eStaticServiceM2TSInformation(const eServiceReference &ref)
+{
+ m_ref = ref;
+ m_parser.parseFile(ref.path);
+}
+
+RESULT eStaticServiceM2TSInformation::getName(const eServiceReference &ref, std::string &name)
+{
+ ASSERT(ref == m_ref);
+ if (m_parser.m_name.size())
+ name = m_parser.m_name;
+ else
+ {
+ name = ref.path;
+ size_t n = name.rfind('/');
+ if (n != std::string::npos)
+ name = name.substr(n + 1);
+ }
+ return 0;
+}
+
+int eStaticServiceM2TSInformation::getLength(const eServiceReference &ref)
+{
+ ASSERT(ref == m_ref);
+
+ eDVBTSTools tstools;
+
+ struct stat s;
+ stat(ref.path.c_str(), &s);
+
+ eM2TSFile *file = new eM2TSFile(ref.path.c_str());
+ ePtr<iTsSource> source = file;
+
+ if (!source->valid())
+ return 0;
+
+ tstools.setSource(source);
+
+ /* check if cached data is still valid */
+ if (m_parser.m_data_ok && (s.st_size == m_parser.m_filesize) && (m_parser.m_length))
+ return m_parser.m_length / 90000;
+
+ /* open again, this time with stream info */
+ tstools.setSource(source, ref.path.c_str());
+
+ /* otherwise, re-calc length and update meta file */
+ pts_t len;
+ if (tstools.calcLen(len))
+ return 0;
+
+ m_parser.m_length = len;
+ m_parser.m_filesize = s.st_size;
+ m_parser.updateMeta(ref.path);
+ return m_parser.m_length / 90000;
+}
+
+int eStaticServiceM2TSInformation::getInfo(const eServiceReference &ref, int w)
+{
+ switch (w)
+ {
+ case iServiceInformation::sDescription:
+ return iServiceInformation::resIsString;
+ case iServiceInformation::sServiceref:
+ return iServiceInformation::resIsString;
+ case iServiceInformation::sFileSize:
+ return m_parser.m_filesize;
+ case iServiceInformation::sTimeCreate:
+ if (m_parser.m_time_create)
+ return m_parser.m_time_create;
+ else
+ return iServiceInformation::resNA;
+ default:
+ return iServiceInformation::resNA;
+ }
+}
+
+std::string eStaticServiceM2TSInformation::getInfoString(const eServiceReference &ref,int w)
+{
+ switch (w)
+ {
+ case iServiceInformation::sDescription:
+ return m_parser.m_description;
+ case iServiceInformation::sServiceref:
+ return m_parser.m_ref.toString();
+ case iServiceInformation::sTags:
+ return m_parser.m_tags;
+ default:
+ return "";
+ }
+}
+
+PyObject *eStaticServiceM2TSInformation::getInfoObject(const eServiceReference &r, int what)
+{
+ switch (what)
+ {
+ case iServiceInformation::sFileSize:
+ return PyLong_FromLongLong(m_parser.m_filesize);
+ default:
+ Py_RETURN_NONE;
+ }
+}
+
+RESULT eStaticServiceM2TSInformation::getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &evt, time_t start_time)
+{
+ if (!ref.path.empty())
+ {
+ ePtr<eServiceEvent> event = new eServiceEvent;
+ std::string filename = ref.path;
+ filename.erase(filename.length()-4, 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 -1;
+}
+
+DEFINE_REF(eM2TSFile);
+
+eM2TSFile::eM2TSFile(const char *filename, bool cached)
+ :m_lock(false), m_sync_offset(0), m_fd(-1), m_file(NULL), m_current_offset(0), m_length(0), m_cached(cached)
+{
+ if (!m_cached)
+ m_fd = ::open(filename, O_RDONLY | O_LARGEFILE);
+ else
+ m_file = ::fopen64(filename, "rb");
+ if (valid())
+ m_current_offset = m_length = lseek_internal(0, SEEK_END);
+}
+
+eM2TSFile::~eM2TSFile()
+{
+ if (m_cached)
+ {
+ if (m_file)
+ {
+ ::fclose(m_file);
+ m_file = 0;
+ }
+ }
+ else
+ {
+ if (m_fd >= 0)
+ ::close(m_fd);
+ m_fd = -1;
+ }
+}
+
+off_t eM2TSFile::lseek(off_t offset, int whence)
+{
+ eSingleLocker l(m_lock);
+
+ offset = (offset % 188) + (offset * 192) / 188;
+
+ if (offset != m_current_offset)
+ m_current_offset = lseek_internal(offset, whence);
+
+ return m_current_offset;
+}
+
+off_t eM2TSFile::lseek_internal(off_t offset, int whence)
+{
+ off_t ret;
+
+ if (!m_cached)
+ ret = ::lseek(m_fd, offset, whence);
+ else
+ {
+ if (::fseeko(m_file, offset, whence) < 0)
+ perror("fseeko");
+ ret = ::ftello(m_file);
+ }
+ return ret <= 0 ? ret : (ret % 192) + (ret*188) / 192;
+}
+
+ssize_t eM2TSFile::read(off_t offset, void *b, size_t count)
+{
+ eSingleLocker l(m_lock);
+ unsigned char tmp[192*3];
+ unsigned char *buf = (unsigned char*)b;
+
+ size_t rd=0;
+ offset = (offset % 188) + (offset * 192) / 188;
+
+sync:
+ if ((offset+m_sync_offset) != m_current_offset)
+ {
+// eDebug("seekTo %lld", offset+m_sync_offset);
+ m_current_offset = lseek_internal(offset+m_sync_offset, SEEK_SET);
+ if (m_current_offset < 0)
+ return m_current_offset;
+ }
+
+ while (rd < count) {
+ size_t ret;
+ if (!m_cached)
+ ret = ::read(m_fd, tmp, 192);
+ else
+ ret = ::fread(tmp, 1, 192, m_file);
+ if (ret < 0 || ret < 192)
+ return rd ? rd : ret;
+
+ if (tmp[4] != 0x47)
+ {
+ if (rd > 0) {
+ eDebug("short read at pos %lld async!!", m_current_offset);
+ return rd;
+ }
+ else {
+ int x=0;
+ if (!m_cached)
+ ret = ::read(m_fd, tmp+192, 384);
+ else
+ ret = ::fread(tmp+192, 1, 384, m_file);
+
+#if 0
+ eDebugNoNewLine("m2ts out of sync at pos %lld, real %lld:", offset + m_sync_offset, m_current_offset);
+ for (; x < 192; ++x)
+ eDebugNoNewLine(" %02x", tmp[x]);
+ eDebug("");
+ x=0;
+#else
+ eDebug("m2ts out of sync at pos %lld, real %lld", offset + m_sync_offset, m_current_offset);
+#endif
+ for (; x < 192; ++x)
+ {
+ if (tmp[x] == 0x47 && tmp[x+192] == 0x47)
+ {
+ int add_offs = (x - 4);
+ eDebug("sync found at pos %d, sync_offset is now %d, old was %d", x, add_offs + m_sync_offset, m_sync_offset);
+ m_sync_offset += add_offs;
+ goto sync;
+ }
+ }
+ }
+ }
+
+ memcpy(buf+rd, tmp+4, 188);
+
+ rd += 188;
+ m_current_offset += 188;
+ }
+
+ m_sync_offset %= 188;
+
+ return rd;
+}
+
+int eM2TSFile::valid()
+{
+ if (!m_cached)
+ return m_fd != -1;
+ else
+ return !!m_file;
+}
+
+off_t eM2TSFile::length()
+{
+ return m_length;
+}
+
+eServiceFactoryM2TS::eServiceFactoryM2TS()
+{
+ ePtr<eServiceCenter> sc;
+ eServiceCenter::getPrivInstance(sc);
+ if (sc)
+ {
+ std::list<std::string> extensions;
+ extensions.push_back("m2ts");
+ extensions.push_back("mts");
+ sc->addServiceFactory(eServiceFactoryM2TS::id, this, extensions);
+ }
+}
+
+eServiceFactoryM2TS::~eServiceFactoryM2TS()
+{
+ ePtr<eServiceCenter> sc;
+
+ eServiceCenter::getPrivInstance(sc);
+ if (sc)
+ sc->removeServiceFactory(eServiceFactoryM2TS::id);
+}
+
+RESULT eServiceFactoryM2TS::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
+{
+ ptr = new eServiceM2TS(ref);
+ return 0;
+}
+
+RESULT eServiceFactoryM2TS::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
+{
+ ptr=0;
+ return -1;
+}
+
+RESULT eServiceFactoryM2TS::list(const eServiceReference &ref, ePtr<iListableService> &ptr)
+{
+ ptr=0;
+ return -1;
+}
+
+RESULT eServiceFactoryM2TS::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr)
+{
+ ptr=new eStaticServiceM2TSInformation(ref);
+ return 0;
+}
+
+RESULT eServiceFactoryM2TS::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr)
+{
+ ptr = 0;
+ return -1;
+}
+
+eServiceM2TS::eServiceM2TS(const eServiceReference &ref)
+ :eDVBServicePlay(ref, NULL)
+{
+}
+
+ePtr<iTsSource> eServiceM2TS::createTsSource(eServiceReferenceDVB &ref)
+{
+ ePtr<iTsSource> source = new eM2TSFile(ref.path.c_str());
+ return source;
+}
+
+RESULT eServiceM2TS::isCurrentlySeekable()
+{
+ return 1; // for fast winding we need index files... so only skip forward/backward yet
+}
+
+eAutoInitPtr<eServiceFactoryM2TS> init_eServiceFactoryM2TS(eAutoInitNumbers::service+1, "eServiceFactoryM2TS");
diff --git a/lib/service/servicem2ts.h b/lib/service/servicem2ts.h
new file mode 100644
index 0000000..bfa4f7d
--- /dev/null
+++ b/lib/service/servicem2ts.h
@@ -0,0 +1,33 @@
+#ifndef __servicem2ts_h
+#define __servicem2ts_h
+
+#include <lib/service/servicedvb.h>
+
+class eServiceFactoryM2TS: public iServiceHandler
+{
+ DECLARE_REF(eServiceFactoryM2TS);
+public:
+ eServiceFactoryM2TS();
+ virtual ~eServiceFactoryM2TS();
+ enum { id = 0x3 };
+
+ // iServiceHandler
+ RESULT play(const eServiceReference &, ePtr<iPlayableService> &ptr);
+ RESULT record(const eServiceReference &, ePtr<iRecordableService> &ptr);
+ RESULT list(const eServiceReference &, ePtr<iListableService> &ptr);
+ RESULT info(const eServiceReference &, ePtr<iStaticServiceInformation> &ptr);
+ RESULT offlineOperations(const eServiceReference &, ePtr<iServiceOfflineOperations> &ptr);
+};
+
+class eServiceM2TS: public eDVBServicePlay
+{
+ friend class eServiceFactoryM2TS;
+protected:
+ eServiceM2TS(const eServiceReference &ref);
+ ePtr<iTsSource> createTsSource(eServiceReferenceDVB &ref);
+
+ // iSeekableService
+ RESULT isCurrentlySeekable();
+};
+
+#endif