X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/b8ae6b0738b4e70f370ad12c8f4912a5bdfba9ac..8654739391001e76f8a6e571537f8b0f42999294:/lib/service/servicedvb.cpp diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp index 61221ecb..248a5482 100644 --- a/lib/service/servicedvb.cpp +++ b/lib/service/servicedvb.cpp @@ -13,6 +13,18 @@ #include #include #include +#include + +#include + +#include +#include + +#ifndef BYTE_ORDER +#error no byte order defined! +#endif + +#define TSPATH "/media/hdd" class eStaticServiceDVBInformation: public iStaticServiceInformation { @@ -305,7 +317,32 @@ RESULT eDVBServiceList::startQuery() return 0; } -RESULT eDVBServiceList::getContent(std::list &list) +RESULT eDVBServiceList::getContent(PyObject *list, bool sorted) +{ + eServiceReferenceDVB ref; + + if (!m_query || !list || !PyList_Check(list)) + return -1; + + std::list tmplist; + + while (!m_query->getNextResult(ref)) + tmplist.push_back(ref); + + if (sorted) + tmplist.sort(iListableServiceCompare(this)); + + for (std::list::iterator it(tmplist.begin()); + it != tmplist.end(); ++it) + { + PyObject *refobj = New_eServiceReference(*it); + PyList_Append(list, refobj); + Py_DECREF(refobj); + } + return 0; +} + +RESULT eDVBServiceList::getContent(std::list &list, bool sorted) { eServiceReferenceDVB ref; @@ -314,6 +351,10 @@ RESULT eDVBServiceList::getContent(std::list &list) while (!m_query->getNextResult(ref)) list.push_back(ref); + + if (sorted) + list.sort(iListableServiceCompare(this)); + return 0; } @@ -379,6 +420,13 @@ RESULT eDVBServiceList::flushChanges() return m_bouquet->flushChanges(); } +RESULT eDVBServiceList::setListName(const std::string &name) +{ + if (!m_bouquet) + return -1; + return m_bouquet->setListName(name); +} + RESULT eServiceFactoryDVB::play(const eServiceReference &ref, ePtr &ptr) { ePtr service; @@ -488,11 +536,18 @@ eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *serv m_reference(ref), m_dvb_service(service), m_is_paused(0) { m_is_pvr = !ref.path.empty(); + m_timeshift_enabled = m_timeshift_active = 0; + m_skipmode = 0; CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent); CONNECT(m_service_handler_timeshift.serviceEvent, eDVBServicePlay::serviceEventTimeshift); CONNECT(m_event_handler.m_eit_changed, eDVBServicePlay::gotNewEvent); + + m_cuesheet_changed = 0; + + if (m_is_pvr) + loadCuesheet(); } eDVBServicePlay::~eDVBServicePlay() @@ -549,14 +604,20 @@ void eDVBServicePlay::serviceEvent(int event) updateTimeshiftPids(); if (!m_timeshift_active) updateDecoder(); + if (m_first_program_info && m_is_pvr) + { + m_first_program_info = 0; + seekTo(0); + } m_event((iPlayableService*)this, evUpdatedInfo); break; } case eDVBServicePMTHandler::eventEOF: - { - m_event((iPlayableService*)this, evEnd); + m_event((iPlayableService*)this, evEOF); + break; + case eDVBServicePMTHandler::eventSOF: + m_event((iPlayableService*)this, evSOF); break; - } } } @@ -580,7 +641,9 @@ RESULT eDVBServicePlay::start() /* in pvr mode, we only want to use one demux. in tv mode, we're using two (one for decoding, one for data source), as we must be prepared to start recording from the data demux. */ - r = m_service_handler.tune((eServiceReferenceDVB&)m_reference, m_is_pvr); + m_cue = new eCueSheet(); + m_first_program_info = 1; + r = m_service_handler.tune((eServiceReferenceDVB&)m_reference, m_is_pvr, m_cue); m_event(this, evStart); m_event((iPlayableService*)this, evSeekableStatusChanged); return 0; @@ -588,6 +651,14 @@ RESULT eDVBServicePlay::start() RESULT eDVBServicePlay::stop() { + stopTimeshift(); /* in case timeshift was enabled, remove buffer etc. */ + + m_service_handler_timeshift.free(); + m_service_handler.free(); + + if (m_is_pvr && m_cuesheet_changed) + saveCuesheet(); + return 0; } @@ -622,15 +693,44 @@ RESULT eDVBServicePlay::setSlowMotion(int ratio) RESULT eDVBServicePlay::setFastForward(int ratio) { - if (m_decoder) - return m_decoder->setFastForward(ratio); - else + int skipmode, ffratio; + + if (ratio > 8) + { + skipmode = ratio; + ffratio = 1; + } else if (ratio > 0) + { + skipmode = 0; + ffratio = ratio; + } else if (!ratio) + { + skipmode = 0; + ffratio = 0; + } else // if (ratio < 0) + { + skipmode = ratio; + ffratio = 1; + } + + if (m_skipmode != skipmode) + { + eDebug("setting cue skipmode to %d", skipmode); + if (m_cue) + m_cue->setSkipmode(skipmode * 90000); /* convert to 90000 per second */ + } + + m_skipmode = skipmode; + + if (!m_decoder) return -1; + + return m_decoder->setFastForward(ffratio); } RESULT eDVBServicePlay::seek(ePtr &ptr) { - if (m_is_pvr || m_timeshift_active) + if (m_is_pvr || m_timeshift_enabled) { ptr = this; return 0; @@ -640,15 +740,13 @@ RESULT eDVBServicePlay::seek(ePtr &ptr) return -1; } + /* TODO: when timeshift is enabled but not active, this doesn't work. */ RESULT eDVBServicePlay::getLength(pts_t &len) { ePtr pvr_channel; - if (m_service_handler.getPVRChannel(pvr_channel)) - { - eDebug("getPVRChannel failed!"); + if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel)) return -1; - } return pvr_channel->getLength(len); } @@ -685,7 +783,11 @@ RESULT eDVBServicePlay::seekTo(pts_t to) if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel)) return -1; - return pvr_channel->seekTo(m_decode_demux, 0, to); + if (!m_cue) + return -1; + + m_cue->seekTo(0, to); + return 0; } RESULT eDVBServicePlay::seekRelative(int direction, pts_t to) @@ -702,7 +804,11 @@ RESULT eDVBServicePlay::seekRelative(int direction, pts_t to) to *= direction; - return pvr_channel->seekTo(m_decode_demux, 1, to); + if (!m_cue) + return 0; + + m_cue->seekTo(1, to); + return 0; } RESULT eDVBServicePlay::getPlayPosition(pts_t &pos) @@ -715,7 +821,18 @@ RESULT eDVBServicePlay::getPlayPosition(pts_t &pos) if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel)) return -1; - return pvr_channel->getCurrentPosition(m_decode_demux, pos, 1); + int r = 0; + + /* if there is a decoder, use audio or video PTS */ + if (m_decoder) + { + r = m_decoder->getPTS(0, pos); + if (r) + return r; + } + + /* fixup */ + return pvr_channel->getCurrentPosition(m_decode_demux, pos, m_decoder ? 1 : 0); } RESULT eDVBServicePlay::setTrickmode(int trick) @@ -725,6 +842,11 @@ RESULT eDVBServicePlay::setTrickmode(int trick) return 0; } +RESULT eDVBServicePlay::isCurrentlySeekable() +{ + return m_is_pvr || m_timeshift_active; +} + RESULT eDVBServicePlay::frontendStatusInfo(ePtr &ptr) { ptr = this; @@ -751,7 +873,34 @@ RESULT eDVBServicePlay::subServices(ePtr &ptr) RESULT eDVBServicePlay::timeshift(ePtr &ptr) { + ptr = 0; if (m_timeshift_enabled || !m_is_pvr) + { + if (!m_timeshift_enabled) + { + /* we need enough diskspace */ + struct statfs fs; + if (statfs(TSPATH "/.", &fs) < 0) + { + eDebug("statfs failed!"); + return -2; + } + + if (((off_t)fs.f_bavail) * ((off_t)fs.f_bsize) < 1024*1024*1024LL) + { + eDebug("not enough diskspace for timeshift! (less than 1GB)"); + return -3; + } + } + ptr = this; + return 0; + } + return -1; +} + +RESULT eDVBServicePlay::cueSheet(ePtr &ptr) +{ + if (m_is_pvr) { ptr = this; return 0; @@ -999,7 +1148,7 @@ RESULT eDVBServicePlay::startTimeshift() if (!m_record) return -3; - char templ[]="/media/hdd/timeshift.XXXXXX"; + char templ[]=TSPATH "/timeshift.XXXXXX"; m_timeshift_fd = mkstemp(templ); m_timeshift_file = templ; @@ -1034,6 +1183,7 @@ RESULT eDVBServicePlay::stopTimeshift() m_record = 0; close(m_timeshift_fd); + eDebug("remove timeshift file"); remove(m_timeshift_file.c_str()); return 0; @@ -1058,6 +1208,58 @@ RESULT eDVBServicePlay::activateTimeshift() return -2; } +PyObject *eDVBServicePlay::getCutList() +{ + PyObject *list = PyList_New(0); + + for (std::multiset::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i) + { + PyObject *tuple = PyTuple_New(2); + PyTuple_SetItem(tuple, 0, PyLong_FromLongLong(i->where)); + PyTuple_SetItem(tuple, 1, PyInt_FromLong(i->what)); + PyList_Append(list, tuple); + Py_DECREF(tuple); + } + + return list; +} + +void eDVBServicePlay::setCutList(PyObject *list) +{ + if (!PyList_Check(list)) + return; + int size = PyList_Size(list); + int i; + + m_cue_entries.clear(); + + for (i=0; igetMPEGDecoder(m_decoder); + if (m_cue) + m_cue->setDecodingDemux(m_decode_demux, m_decoder); } if (m_decoder) @@ -1233,6 +1437,74 @@ void eDVBServicePlay::updateDecoder() } } +void eDVBServicePlay::loadCuesheet() +{ + std::string filename = m_reference.path + ".cuts"; + + m_cue_entries.clear(); + + FILE *f = fopen(filename.c_str(), "rb"); + + if (f) + { + eDebug("loading cuts.."); + while (1) + { + unsigned long long where; + unsigned int what; + + if (!fread(&where, sizeof(where), 1, f)) + break; + if (!fread(&what, sizeof(what), 1, f)) + break; + +#if BYTE_ORDER == LITTLE_ENDIAN + where = bswap_64(where); +#endif + what = ntohl(what); + + if (what > 2) + break; + + m_cue_entries.insert(cueEntry(where, what)); + } + fclose(f); + eDebug("%d entries", m_cue_entries.size()); + } else + eDebug("cutfile not found!"); + + m_cuesheet_changed = 0; +} + +void eDVBServicePlay::saveCuesheet() +{ + std::string filename = m_reference.path + ".cuts"; + + FILE *f = fopen(filename.c_str(), "wb"); + + if (f) + { + unsigned long long where; + int what; + + for (std::multiset::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i) + { +#if BYTE_ORDER == BIG_ENDIAN + where = i->where; +#else + where = bswap_64(i->where); +#endif + what = htonl(i->what); + fwrite(&where, sizeof(where), 1, f); + fwrite(&what, sizeof(what), 1, f); + + } + fclose(f); + } + + m_cuesheet_changed = 0; +} + DEFINE_REF(eDVBServicePlay) eAutoInitPtr init_eServiceFactoryDVB(eAutoInitNumbers::service+1, "eServiceFactoryDVB");