#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);
res.clear();
res.push_back(m_ref.path);
res.push_back(m_ref.path + ".meta");
+ res.push_back(m_ref.path + ".ap");
+ res.push_back(m_ref.path + ".cuts");
return 0;
}
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()
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;
}
two (one for decoding, one for data source), as we must be prepared
to start recording from the data demux. */
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);
RESULT eDVBServicePlay::stop()
{
- eDebug("stop timeshift");
stopTimeshift(); /* in case timeshift was enabled, remove buffer etc. */
-
- eDebug("free ts handler");
+
m_service_handler_timeshift.free();
- eDebug("stop service handler");
m_service_handler.free();
- eDebug("ok");
+
+ if (m_is_pvr && m_cuesheet_changed)
+ saveCuesheet();
return 0;
}
{
skipmode = 0;
ffratio = 0;
- } else if (ratio < 0)
+ } else // if (ratio < 0)
{
skipmode = ratio;
ffratio = 1;
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->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)
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)
if (m_decode_demux)
m_decode_demux->getMPEGDecoder(m_decoder);
if (m_cue)
- m_cue->setDecodingDemux(m_decode_demux);
+ m_cue->setDecodingDemux(m_decode_demux, m_decoder);
}
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");