+int eDVBServicePlay::isTimeshiftActive()
+{
+ return m_timeshift_enabled && m_timeshift_active;
+}
+
+RESULT eDVBServicePlay::activateTimeshift()
+{
+ if (!m_timeshift_enabled)
+ return -1;
+
+ if (!m_timeshift_active)
+ {
+ switchToTimeshift();
+ 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;
+
+ cutlistToCuesheet();
+ m_event((iPlayableService*)this, evCuesheetChanged);
+}
+
+void eDVBServicePlay::setCutListEnable(int enable)
+{
+ m_cutlist_enabled = enable;
+ cutlistToCuesheet();
+}
+
+void eDVBServicePlay::updateTimeshiftPids()
+{
+ if (!m_record)
+ return;
+
+ eDVBServicePMTHandler::program program;
+ if (m_service_handler.getProgramInfo(program))
+ return;
+ else
+ {
+ std::set<int> pids_to_record;
+ pids_to_record.insert(0); // PAT
+ if (program.pmtPid != -1)
+ pids_to_record.insert(program.pmtPid); // PMT
+
+ if (program.textPid != -1)
+ pids_to_record.insert(program.textPid); // Videotext
+
+ for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
+ i(program.videoStreams.begin());
+ i != program.videoStreams.end(); ++i)
+ pids_to_record.insert(i->pid);
+
+ for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
+ i(program.audioStreams.begin());
+ i != program.audioStreams.end(); ++i)
+ pids_to_record.insert(i->pid);
+
+ std::set<int> new_pids, obsolete_pids;
+
+ std::set_difference(pids_to_record.begin(), pids_to_record.end(),
+ m_pids_active.begin(), m_pids_active.end(),
+ std::inserter(new_pids, new_pids.begin()));
+
+ std::set_difference(
+ m_pids_active.begin(), m_pids_active.end(),
+ pids_to_record.begin(), pids_to_record.end(),
+ std::inserter(new_pids, new_pids.begin())
+ );
+
+ for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
+ m_record->addPID(*i);
+
+ for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
+ m_record->removePID(*i);
+ }
+}
+
+void eDVBServicePlay::switchToLive()
+{
+ if (!m_timeshift_active)
+ return;
+
+ m_cue = 0;
+ m_decoder = 0;
+ m_decode_demux = 0;
+ /* free the timeshift service handler, we need the resources */
+ m_service_handler_timeshift.free();
+ m_timeshift_active = 0;
+
+ m_event((iPlayableService*)this, evSeekableStatusChanged);
+
+ updateDecoder();
+}
+
+void eDVBServicePlay::switchToTimeshift()
+{
+ if (m_timeshift_active)
+ return;
+
+ m_decode_demux = 0;
+ m_decoder = 0;
+
+ m_timeshift_active = 1;
+
+ m_event((iPlayableService*)this, evSeekableStatusChanged);
+
+ eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference;
+ r.path = m_timeshift_file;
+
+ m_cue = new eCueSheet();
+ m_service_handler_timeshift.tune(r, 1, m_cue); /* use the decoder demux for everything */
+ updateDecoder(); /* mainly to switch off PCR */
+}
+
+void eDVBServicePlay::updateDecoder()
+{
+ int vpid = -1, apid = -1, apidtype = -1, pcrpid = -1, tpid = -1;
+ eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+
+ eDVBServicePMTHandler::program program;
+ if (h.getProgramInfo(program))
+ eDebug("getting program info failed.");
+ else
+ {
+ eDebugNoNewLine("have %d video stream(s)", program.videoStreams.size());
+ if (!program.videoStreams.empty())
+ {
+ eDebugNoNewLine(" (");
+ for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
+ i(program.videoStreams.begin());
+ i != program.videoStreams.end(); ++i)
+ {
+ if (vpid == -1)
+ vpid = i->pid;
+ if (i != program.videoStreams.begin())
+ eDebugNoNewLine(", ");
+ eDebugNoNewLine("%04x", i->pid);
+ }
+ eDebugNoNewLine(")");
+ }
+ eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
+ if (!program.audioStreams.empty())
+ {
+ eDebugNoNewLine(" (");
+ for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
+ i(program.audioStreams.begin());
+ i != program.audioStreams.end(); ++i)
+ {
+ if (apid == -1)
+ {
+ apid = i->pid;
+ apidtype = i->type;
+ }
+ if (i != program.audioStreams.begin())
+ eDebugNoNewLine(", ");
+ eDebugNoNewLine("%04x", i->pid);
+ }
+ eDebugNoNewLine(")");
+ }
+ eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
+ pcrpid = program.pcrPid;
+ eDebug(", and the text pid is %04x", program.textPid);
+ tpid = program.textPid;
+ }
+
+ if (!m_decoder)
+ {
+ h.getDecodeDemux(m_decode_demux);
+ if (m_decode_demux)
+ m_decode_demux->getMPEGDecoder(m_decoder, m_is_primary);
+ if (m_cue)
+ m_cue->setDecodingDemux(m_decode_demux, m_decoder);
+ }
+
+ if (m_decoder)
+ {
+ m_decoder->setVideoPID(vpid);
+ m_current_audio_stream = 0;
+ m_decoder->setAudioPID(apid, apidtype);
+ if (!(m_is_pvr || m_timeshift_active || !m_is_primary))
+ m_decoder->setSyncPCR(pcrpid);
+ else
+ m_decoder->setSyncPCR(-1);
+ m_decoder->setTextPID(tpid);
+ if (!m_is_primary)
+ m_decoder->setTrickmode(1);
+ m_decoder->start();
+// how we can do this better?
+// update cache pid when the user changed the audio track or video track
+// TODO handling of difference audio types.. default audio types..
+
+ /* don't worry about non-existing services, nor pvr services */
+ if (m_dvb_service && !m_is_pvr)
+ {
+ if (apidtype == eDVBAudio::aMPEG)
+ {
+ m_dvb_service->setCachePID(eDVBService::cAPID, apid);
+ m_dvb_service->setCachePID(eDVBService::cAC3PID, -1);
+ }
+ else
+ {
+ m_dvb_service->setCachePID(eDVBService::cAPID, -1);
+ m_dvb_service->setCachePID(eDVBService::cAC3PID, apid);
+ }
+ m_dvb_service->setCachePID(eDVBService::cVPID, vpid);
+ m_dvb_service->setCachePID(eDVBService::cPCRPID, pcrpid);
+ m_dvb_service->setCachePID(eDVBService::cTPID, tpid);
+ }
+ }
+}
+
+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;
+ cutlistToCuesheet();
+ m_event((iPlayableService*)this, evCuesheetChanged);
+}
+
+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;
+}
+
+void eDVBServicePlay::cutlistToCuesheet()
+{
+ if (!m_cue)
+ {
+ eDebug("no cue sheet");
+ return;
+ }
+ m_cue->clear();
+
+ if (!m_cutlist_enabled)
+ {
+ m_cue->commitSpans();
+ eDebug("cutlists where disabled");
+ return;
+ }
+
+ pts_t in = 0, out = 0, length = 0;
+
+ getLength(length);
+
+ std::multiset<cueEntry>::iterator i(m_cue_entries.begin());
+
+ while (1)
+ {
+ if (i == m_cue_entries.end())
+ out = length;
+ else {
+ if (i->what == 0) /* in */
+ {
+ in = i++->where;
+ continue;
+ } else if (i->what == 1) /* out */
+ out = i++->where;
+ else /* mark */
+ {
+ i++;
+ continue;
+ }
+ }
+
+ if (in != out)
+ m_cue->addSourceSpan(in, out);
+
+ in = length;
+
+ if (i == m_cue_entries.end())
+ break;
+ }
+ m_cue->commitSpans();
+}
+