#include <lib/dvb/tstools.h>
#include <lib/python/python.h>
+#include <sys/vfs.h>
+
+#include <byteswap.h>
+#include <netinet/in.h>
+
+#ifndef BYTE_ORDER
+#error no byte order defined!
+#endif
+
+#define TSPATH "/media/hdd"
+
class eStaticServiceDVBInformation: public iStaticServiceInformation
{
DECLARE_REF(eStaticServiceDVBInformation);
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()
break;
}
case eDVBServicePMTHandler::eventEOF:
- {
- m_event((iPlayableService*)this, evEnd);
+ m_event((iPlayableService*)this, evEOF);
+ break;
+ case eDVBServicePMTHandler::eventSOF:
+ m_event((iPlayableService*)this, evSOF);
break;
- }
}
}
/* 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();
+ r = m_service_handler.tune((eServiceReferenceDVB&)m_reference, m_is_pvr, m_cue);
m_event(this, evStart);
m_event((iPlayableService*)this, evSeekableStatusChanged);
return 0;
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;
}
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<iSeekableService> &ptr)
{
- if (m_is_pvr || m_timeshift_active)
+ if (m_is_pvr || m_timeshift_enabled)
{
ptr = this;
return 0;
return -1;
}
+ /* TODO: when timeshift is enabled but not active, this doesn't work. */
RESULT eDVBServicePlay::getLength(pts_t &len)
{
ePtr<iDVBPVRChannel> 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);
}
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)
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)
return 0;
}
+RESULT eDVBServicePlay::isCurrentlySeekable()
+{
+ return m_is_pvr || m_timeshift_active;
+}
+
RESULT eDVBServicePlay::frontendStatusInfo(ePtr<iFrontendStatusInformation> &ptr)
{
ptr = this;
RESULT eDVBServicePlay::timeshift(ePtr<iTimeshiftService> &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<iCueSheet> &ptr)
+{
+ if (m_is_pvr)
{
ptr = this;
return 0;
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;
m_record = 0;
close(m_timeshift_fd);
+ eDebug("remove timeshift file");
remove(m_timeshift_file.c_str());
return 0;
return -2;
}
+PyObject *eDVBServicePlay::getCutList()
+{
+ PyObject *list = PyList_New(0);
+
+ for (std::multiset<struct cueEntry>::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; i<size; ++i)
+ {
+ PyObject *tuple = PyList_GetItem(list, i);
+ if (!PyTuple_Check(tuple))
+ {
+ eDebug("non-tuple in cutlist");
+ continue;
+ }
+ if (PyTuple_Size(tuple) != 2)
+ {
+ eDebug("cutlist entries need to be a 2-tuple");
+ continue;
+ }
+ PyObject *ppts = PyTuple_GetItem(tuple, 0), *ptype = PyTuple_GetItem(tuple, 1);
+ if (!(PyLong_Check(ppts) && PyInt_Check(ptype)))
+ {
+ eDebug("cutlist entries need to be (pts, type)-tuples (%d %d)", PyLong_Check(ppts), PyInt_Check(ptype));
+ continue;
+ }
+ pts_t pts = PyLong_AsLongLong(ppts);
+ int type = PyInt_AsLong(ptype);
+ m_cue_entries.insert(cueEntry(pts, type));
+ eDebug("adding %08llx, %d", pts, type);
+ }
+ m_cuesheet_changed = 1;
+}
+
void eDVBServicePlay::updateTimeshiftPids()
{
if (!m_record)
eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference;
r.path = m_timeshift_file;
- m_service_handler_timeshift.tune(r, 1); /* use the decoder demux for everything */
+ m_service_handler_timeshift.tune(r, 1, m_cue); /* use the decoder demux for everything */
}
void eDVBServicePlay::updateDecoder()
h.getDecodeDemux(m_decode_demux);
if (m_decode_demux)
m_decode_demux->getMPEGDecoder(m_decoder);
+ if (m_cue)
+ m_cue->setDecodingDemux(m_decode_demux);
}
if (m_decoder)
}
}
+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<cueEntry>::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<eServiceFactoryDVB> init_eServiceFactoryDVB(eAutoInitNumbers::service+1, "eServiceFactoryDVB");