+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()
+{
+ ePyObject list = PyList_New(0);
+
+ for (std::multiset<struct cueEntry>::iterator i(m_cue_entries.begin()); i != m_cue_entries.end(); ++i)
+ {
+ ePyObject tuple = PyTuple_New(2);
+ PyTuple_SET_ITEM(tuple, 0, PyLong_FromLongLong(i->where));
+ PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(i->what));
+ PyList_Append(list, tuple);
+ Py_DECREF(tuple);
+ }
+
+ return list;
+}
+
+void eDVBServicePlay::setCutList(ePyObject list)
+{
+ if (!PyList_Check(list))
+ return;
+ int size = PyList_Size(list);
+ int i;
+
+ m_cue_entries.clear();
+
+ for (i=0; i<size; ++i)
+ {
+ ePyObject tuple = PyList_GET_ITEM(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;
+ }
+ ePyObject ppts = PyTuple_GET_ITEM(tuple, 0), ptype = PyTuple_GET_ITEM(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;
+ eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+
+ if (h.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);
+
+ for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
+ i(program.subtitleStreams.begin());
+ i != program.subtitleStreams.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;
+
+ eDebug("SwitchToLive");
+
+ m_cue = 0;
+ m_decoder = 0;
+ m_decode_demux = 0;
+ m_teletext_parser = 0;
+ m_rds_decoder = 0;
+ m_subtitle_parser = 0;
+ m_new_dvb_subtitle_page_connection = 0;
+ m_new_subtitle_page_connection = 0;
+ m_rds_decoder_event_connection = 0;
+ m_video_event_connection = 0;
+ m_is_paused = m_skipmode = m_fastforward = m_slowmotion = 0; /* not supported in live mode */
+
+ /* free the timeshift service handler, we need the resources */
+ m_service_handler_timeshift.free();
+ m_timeshift_active = 0;
+ m_timeshift_changed = 1;
+
+ updateDecoder(true);
+}
+
+void eDVBServicePlay::switchToTimeshift()
+{
+ if (m_timeshift_active)
+ return;
+
+ m_decode_demux = 0;
+ m_decoder = 0;
+ m_teletext_parser = 0;
+ m_rds_decoder = 0;
+ m_subtitle_parser = 0;
+ m_new_subtitle_page_connection = 0;
+ m_new_dvb_subtitle_page_connection = 0;
+ m_rds_decoder_event_connection = 0;
+ m_video_event_connection = 0;
+
+ m_timeshift_active = 1;
+ m_timeshift_changed = 1;
+
+ eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference;
+ r.path = m_timeshift_file;
+
+ m_cue = new eCueSheet();
+ m_cue->seekTo(0, -1000);
+ m_service_handler_timeshift.tune(r, 1, m_cue, 0, m_dvb_service); /* use the decoder demux for everything */
+
+ eDebug("eDVBServicePlay::switchToTimeshift, in pause mode now.");
+ pause();
+ updateDecoder(true); /* mainly to switch off PCR, and to set pause */
+}
+
+void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged)
+{
+ int vpid = -1, vpidtype = -1, pcrpid = -1, tpid = -1, achannel = -1, ac3_delay=-1, pcm_delay=-1;
+ bool mustPlay = false;
+
+ 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;
+ vpidtype = i->type;
+ }
+ 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 (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_timeshift_changed)
+ m_decoder = 0;
+ if (m_decode_demux)
+ {
+ m_decode_demux->getMPEGDecoder(m_decoder, m_is_primary);
+ if (m_decoder)
+ m_decoder->connectVideoEvent(slot(*this, &eDVBServicePlay::video_event), m_video_event_connection);
+ if (m_is_primary)
+ {
+ m_teletext_parser = new eDVBTeletextParser(m_decode_demux);
+ m_teletext_parser->connectNewPage(slot(*this, &eDVBServicePlay::newSubtitlePage), m_new_subtitle_page_connection);
+ m_subtitle_parser = new eDVBSubtitleParser(m_decode_demux);
+ m_subtitle_parser->connectNewPage(slot(*this, &eDVBServicePlay::newDVBSubtitlePage), m_new_dvb_subtitle_page_connection);
+ if (m_timeshift_changed)
+ {
+ ePyObject subs = getCachedSubtitle();
+ if (subs != Py_None)
+ {
+ int type = PyInt_AsLong(PyTuple_GET_ITEM(subs, 0)),
+ pid = PyInt_AsLong(PyTuple_GET_ITEM(subs, 1)),
+ comp_page = PyInt_AsLong(PyTuple_GET_ITEM(subs, 2)), // ttx page
+ anc_page = PyInt_AsLong(PyTuple_GET_ITEM(subs, 3)); // ttx magazine
+ if (type == 0) // dvb
+ m_subtitle_parser->start(pid, comp_page, anc_page);
+ else if (type == 1) // ttx
+ m_teletext_parser->setPageAndMagazine(comp_page, anc_page);
+ }
+ Py_DECREF(subs);
+ }
+ }
+ }
+ if (m_cue)
+ m_cue->setDecodingDemux(m_decode_demux, m_decoder);
+ mustPlay = true;
+ }
+
+ m_timeshift_changed = 0;
+
+ if (m_decoder)
+ {
+ bool wasSeekable = m_decoder->getVideoProgressive() != -1;
+ if (m_dvb_service)
+ {
+ achannel = m_dvb_service->getCacheEntry(eDVBService::cACHANNEL);
+ ac3_delay = m_dvb_service->getCacheEntry(eDVBService::cAC3DELAY);
+ pcm_delay = m_dvb_service->getCacheEntry(eDVBService::cPCMDELAY);
+ }
+ else // subservice
+ {
+ eServiceReferenceDVB ref;
+ m_service_handler.getServiceReference(ref);
+ eServiceReferenceDVB parent = ref.getParentServiceReference();
+ if (!parent)
+ parent = ref;
+ if (parent)
+ {
+ ePtr<eDVBResourceManager> res_mgr;
+ if (!eDVBResourceManager::getInstance(res_mgr))
+ {
+ ePtr<iDVBChannelList> db;
+ if (!res_mgr->getChannelList(db))
+ {
+ ePtr<eDVBService> origService;
+ if (!db->getService(parent, origService))
+ {
+ ac3_delay = origService->getCacheEntry(eDVBService::cAC3DELAY);
+ pcm_delay = origService->getCacheEntry(eDVBService::cPCMDELAY);
+ }
+ }
+ }
+ }
+ }
+
+ setAC3Delay(ac3_delay == -1 ? 0 : ac3_delay);
+ setPCMDelay(pcm_delay == -1 ? 0 : pcm_delay);
+
+ m_decoder->setVideoPID(vpid, vpidtype);
+ selectAudioStream();
+
+ if (!(m_is_pvr || m_timeshift_active || !m_is_primary))
+ m_decoder->setSyncPCR(pcrpid);
+ else
+ m_decoder->setSyncPCR(-1);
+
+ if (m_is_primary)
+ {
+ m_decoder->setTextPID(tpid);
+ m_teletext_parser->start(program.textPid);
+ }
+
+ if (vpid > 0 && vpid < 0x2000)
+ ;
+ else
+ {
+ std::string radio_pic;
+ if (!ePythonConfigQuery::getConfigValue("config.misc.radiopic", radio_pic))
+ m_decoder->setRadioPic(radio_pic);
+ }
+
+ if (mustPlay)
+ m_decoder->play();
+ else
+ m_decoder->set();
+
+ m_decoder->setAudioChannel(achannel);
+
+ /* don't worry about non-existing services, nor pvr services */
+ if (m_dvb_service)
+ {
+ /* (audio pid will be set in selectAudioTrack */
+ m_dvb_service->setCacheEntry(eDVBService::cVPID, vpid);
+ m_dvb_service->setCacheEntry(eDVBService::cVTYPE, vpidtype == eDVBVideo::MPEG2 ? -1 : vpidtype);
+ m_dvb_service->setCacheEntry(eDVBService::cPCRPID, pcrpid);
+ m_dvb_service->setCacheEntry(eDVBService::cTPID, tpid);
+ }
+ if (!sendSeekableStateChanged && (m_decoder->getVideoProgressive() != -1) != wasSeekable)
+ sendSeekableStateChanged = true;
+ }
+ m_have_video_pid = (vpid > 0 && vpid < 0x2000);
+
+ if (sendSeekableStateChanged)
+ m_event((iPlayableService*)this, evSeekableStatusChanged);
+}
+
+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 > 3)
+ 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 were disabled");
+ return;
+ }
+
+ pts_t in = 0, out = 0, length = 0;
+
+ getLength(length);
+
+ std::multiset<cueEntry>::iterator i(m_cue_entries.begin());
+
+ int have_any_span = 0;
+
+ while (1)
+ {
+ if (i == m_cue_entries.end())
+ {
+ if (!have_any_span && !in)
+ break;
+ out = length;
+ } else {
+ if (i->what == 0) /* in */
+ {
+ in = i++->where;
+ continue;
+ } else if (i->what == 1) /* out */
+ out = i++->where;
+ else /* mark (2) or last play position (3) */
+ {
+ i++;
+ continue;
+ }
+ }
+
+ if (in < 0)
+ in = 0;
+ if (out < 0)
+ out = 0;
+ if (in > length)
+ in = length;
+ if (out > length)
+ out = length;
+
+ if (in < out)
+ {
+ have_any_span = 1;
+ m_cue->addSourceSpan(in, out);
+ in = out = 0;
+ }
+
+ in = length;
+
+ if (i == m_cue_entries.end())
+ break;
+ }
+ m_cue->commitSpans();
+}
+
+RESULT eDVBServicePlay::enableSubtitles(eWidget *parent, ePyObject tuple)
+{
+ if (m_subtitle_widget)
+ disableSubtitles(parent);
+
+ ePyObject entry;
+ int tuplesize = PyTuple_Size(tuple);
+ int type = 0;
+
+ if (!PyTuple_Check(tuple))
+ goto error_out;
+
+ if (tuplesize < 1)
+ goto error_out;
+
+ entry = PyTuple_GET_ITEM(tuple, 0);
+
+ if (!PyInt_Check(entry))
+ goto error_out;
+
+ type = PyInt_AsLong(entry);
+
+ if (type == 1) // teletext subtitles
+ {
+ int page, magazine, pid;
+ if (tuplesize < 4)
+ goto error_out;
+
+ if (!m_teletext_parser)
+ {
+ eDebug("enable teletext subtitles.. no parser !!!");
+ return -1;
+ }
+
+ entry = PyTuple_GET_ITEM(tuple, 1);
+ if (!PyInt_Check(entry))
+ goto error_out;
+ pid = PyInt_AsLong(entry);
+
+ entry = PyTuple_GET_ITEM(tuple, 2);
+ if (!PyInt_Check(entry))
+ goto error_out;
+ page = PyInt_AsLong(entry);
+
+ entry = PyTuple_GET_ITEM(tuple, 3);
+ if (!PyInt_Check(entry))
+ goto error_out;
+ magazine = PyInt_AsLong(entry);
+
+ m_subtitle_widget = new eSubtitleWidget(parent);
+ m_subtitle_widget->resize(parent->size()); /* full size */
+ m_teletext_parser->setPageAndMagazine(page, magazine);
+ if (m_dvb_service)
+ m_dvb_service->setCacheEntry(eDVBService::cSUBTITLE,((pid&0xFFFF)<<16)|((page&0xFF)<<8)|(magazine&0xFF));
+ }
+ else if (type == 0)
+ {
+ int pid = 0, composition_page_id = 0, ancillary_page_id = 0;
+ if (!m_subtitle_parser)
+ {
+ eDebug("enable dvb subtitles.. no parser !!!");
+ return -1;
+ }
+ if (tuplesize < 4)
+ goto error_out;
+
+ entry = PyTuple_GET_ITEM(tuple, 1);
+ if (!PyInt_Check(entry))
+ goto error_out;
+ pid = PyInt_AsLong(entry);
+
+ entry = PyTuple_GET_ITEM(tuple, 2);
+ if (!PyInt_Check(entry))
+ goto error_out;
+ composition_page_id = PyInt_AsLong(entry);
+
+ entry = PyTuple_GET_ITEM(tuple, 3);
+ if (!PyInt_Check(entry))
+ goto error_out;
+ ancillary_page_id = PyInt_AsLong(entry);
+
+ m_subtitle_widget = new eSubtitleWidget(parent);
+ m_subtitle_widget->resize(parent->size()); /* full size */
+ m_subtitle_parser->start(pid, composition_page_id, ancillary_page_id);
+ if (m_dvb_service)
+ m_dvb_service->setCacheEntry(eDVBService::cSUBTITLE, ((pid&0xFFFF)<<16)|((composition_page_id&0xFF)<<8)|(ancillary_page_id&0xFF));
+ }
+ else
+ goto error_out;
+ return 0;
+error_out:
+ eDebug("enableSubtitles needs a tuple as 2nd argument!\n"
+ "for teletext subtitles (0, pid, teletext_page, teletext_magazine)\n"
+ "for dvb subtitles (1, pid, composition_page_id, ancillary_page_id)");
+ return -1;
+}
+
+RESULT eDVBServicePlay::disableSubtitles(eWidget *parent)
+{
+ delete m_subtitle_widget;
+ m_subtitle_widget = 0;
+ if (m_subtitle_parser)
+ {
+ m_subtitle_parser->stop();
+ m_dvb_subtitle_pages.clear();
+ }
+ if (m_teletext_parser)
+ {
+ m_teletext_parser->setPageAndMagazine(-1, -1);
+ m_subtitle_pages.clear();
+ }
+ if (m_dvb_service)
+ m_dvb_service->setCacheEntry(eDVBService::cSUBTITLE, -1);
+ return 0;
+}
+
+PyObject *eDVBServicePlay::getCachedSubtitle()
+{
+ if (m_dvb_service)
+ {
+ int tmp = m_dvb_service->getCacheEntry(eDVBService::cSUBTITLE);
+ if (tmp != -1)
+ {
+ unsigned int data = (unsigned int)tmp;
+ int pid = (data&0xFFFF0000)>>16;
+ ePyObject tuple = PyTuple_New(4);
+ eDVBServicePMTHandler::program program;
+ eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+ if (!h.getProgramInfo(program))
+ {
+ if (program.textPid==pid) // teletext
+ PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(1)); // type teletext
+ else
+ PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(0)); // type dvb
+ PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(pid)); // pid
+ PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong((data&0xFF00)>>8)); // composition_page / page
+ PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(data&0xFF)); // ancillary_page / magazine
+ return tuple;
+ }
+ }
+ }
+ Py_RETURN_NONE;
+}
+
+PyObject *eDVBServicePlay::getSubtitleList()
+{
+ if (!m_teletext_parser)
+ Py_RETURN_NONE;
+
+ ePyObject l = PyList_New(0);
+ std::set<int> added_ttx_pages;
+
+ std::set<eDVBServicePMTHandler::subtitleStream> &subs =
+ m_teletext_parser->m_found_subtitle_pages;
+
+ 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
+ {
+ for (std::vector<eDVBServicePMTHandler::subtitleStream>::iterator it(program.subtitleStreams.begin());
+ it != program.subtitleStreams.end(); ++it)
+ {
+ switch(it->subtitling_type)
+ {
+ case 0x01: // ebu teletext subtitles
+ {
+ int page_number = it->teletext_page_number & 0xFF;
+ int magazine_number = it->teletext_magazine_number & 7;
+ int hash = magazine_number << 8 | page_number;
+ if (added_ttx_pages.find(hash) == added_ttx_pages.end())
+ {
+ ePyObject tuple = PyTuple_New(5);
+ PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(1));
+ PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(it->pid));
+ PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(page_number));
+ PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(magazine_number));
+ PyTuple_SET_ITEM(tuple, 4, PyString_FromString(it->language_code.c_str()));
+ PyList_Append(l, tuple);
+ Py_DECREF(tuple);
+ added_ttx_pages.insert(hash);
+ }
+ break;
+ }
+ case 0x10 ... 0x13:
+ case 0x20 ... 0x23: // dvb subtitles
+ {
+ ePyObject tuple = PyTuple_New(5);
+ PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(0));
+ PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(it->pid));
+ PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(it->composition_page_id));
+ PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(it->ancillary_page_id));
+ PyTuple_SET_ITEM(tuple, 4, PyString_FromString(it->language_code.c_str()));
+ PyList_Insert(l, 0, tuple);
+ Py_DECREF(tuple);
+ break;
+ }
+ }
+ }
+ }
+
+ for (std::set<eDVBServicePMTHandler::subtitleStream>::iterator it(subs.begin());
+ it != subs.end(); ++it)
+ {
+ int page_number = it->teletext_page_number & 0xFF;
+ int magazine_number = it->teletext_magazine_number & 7;
+ int hash = magazine_number << 8 | page_number;
+ if (added_ttx_pages.find(hash) == added_ttx_pages.end())
+ {
+ ePyObject tuple = PyTuple_New(5);
+ PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(1));
+ PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(it->pid));
+ PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(page_number));
+ PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(magazine_number));
+ PyTuple_SET_ITEM(tuple, 4, PyString_FromString("und")); // undetermined
+ PyList_Append(l, tuple);
+ Py_DECREF(tuple);
+ }
+ }
+
+ return l;
+}
+
+void eDVBServicePlay::newSubtitlePage(const eDVBTeletextSubtitlePage &page)
+{
+ if (m_subtitle_widget)
+ {
+ pts_t pos = 0;
+ if (m_decoder)
+ m_decoder->getPTS(0, pos);
+ eDebug("got new subtitle page %lld %lld %d", pos, page.m_pts, page.m_have_pts);
+ m_subtitle_pages.push_back(page);
+ checkSubtitleTiming();
+ }
+}
+
+void eDVBServicePlay::checkSubtitleTiming()
+{
+ eDebug("checkSubtitleTiming");
+ if (!m_subtitle_widget)
+ return;
+ while (1)
+ {
+ enum { TELETEXT, DVB } type;
+ eDVBTeletextSubtitlePage page;
+ eDVBSubtitlePage dvb_page;
+ pts_t show_time;
+ if (!m_subtitle_pages.empty())
+ {
+ page = m_subtitle_pages.front();
+ type = TELETEXT;
+ show_time = page.m_pts;
+ }
+ else if (!m_dvb_subtitle_pages.empty())
+ {
+ dvb_page = m_dvb_subtitle_pages.front();
+ type = DVB;
+ show_time = dvb_page.m_show_time;
+ }
+ else
+ return;
+
+ pts_t pos = 0;
+
+ if (m_decoder)
+ m_decoder->getPTS(0, pos);
+
+ eDebug("%lld %lld", pos, show_time);
+ int diff = show_time - pos;
+ if (type == TELETEXT && !page.m_have_pts)
+ {
+ eDebug("ttx subtitle page without pts... immediate show");
+ diff = 0;
+ }
+ if (diff < 0)
+ {
+ eDebug("[late (%d ms)]", -diff / 90);
+ diff = 0;
+ }
+ if (abs(diff) > 1800000)
+ {
+ eDebug("[invalid]... immediate show!");
+ diff = 0;
+ }
+ if ((diff/90)<20)
+ {
+ if (type == TELETEXT)
+ {
+ eDebug("display teletext subtitle page %lld", show_time);
+ m_subtitle_widget->setPage(page);
+ m_subtitle_pages.pop_front();
+ }
+ else
+ {
+ eDebug("display dvb subtitle Page %lld", show_time);
+ m_subtitle_widget->setPage(dvb_page);
+ m_dvb_subtitle_pages.pop_front();
+ }
+ } else
+ {
+ eDebug("start subtitle delay %d", diff / 90);
+ m_subtitle_sync_timer->start(diff / 90, 1);
+ break;
+ }
+ }
+}
+
+void eDVBServicePlay::newDVBSubtitlePage(const eDVBSubtitlePage &p)
+{
+ if (m_subtitle_widget)
+ {
+ pts_t pos = 0;
+ if (m_decoder)
+ m_decoder->getPTS(0, pos);
+ eDebug("got new subtitle page %lld %lld", pos, p.m_show_time);
+ m_dvb_subtitle_pages.push_back(p);
+ checkSubtitleTiming();
+ }
+}
+
+int eDVBServicePlay::getAC3Delay()
+{
+ if (m_dvb_service)
+ return m_dvb_service->getCacheEntry(eDVBService::cAC3DELAY);
+ else if (m_decoder)
+ return m_decoder->getAC3Delay();
+ else
+ return 0;
+}
+
+int eDVBServicePlay::getPCMDelay()
+{
+ if (m_dvb_service)
+ return m_dvb_service->getCacheEntry(eDVBService::cPCMDELAY);
+ else if (m_decoder)
+ return m_decoder->getPCMDelay();
+ else
+ return 0;
+}
+
+void eDVBServicePlay::setAC3Delay(int delay)
+{
+ if (m_dvb_service)
+ m_dvb_service->setCacheEntry(eDVBService::cAC3DELAY, delay ? delay : -1);
+ if (m_decoder) {
+ std::string config_delay;
+ int config_delay_int = 0;
+ if(ePythonConfigQuery::getConfigValue("config.av.generalAC3delay", config_delay) == 0)
+ config_delay_int = atoi(config_delay.c_str());
+ m_decoder->setAC3Delay(delay + config_delay_int);
+ }
+}
+
+void eDVBServicePlay::setPCMDelay(int delay)
+{
+ if (m_dvb_service)
+ m_dvb_service->setCacheEntry(eDVBService::cPCMDELAY, delay ? delay : -1);
+ if (m_decoder) {
+ std::string config_delay;
+ int config_delay_int = 0;
+ if(ePythonConfigQuery::getConfigValue("config.av.generalPCMdelay", config_delay) == 0)
+ config_delay_int = atoi(config_delay.c_str());
+ else
+ config_delay_int = 0;
+ m_decoder->setPCMDelay(delay + config_delay_int);
+ }
+}
+
+void eDVBServicePlay::video_event(struct iTSMPEGDecoder::videoEvent event)
+{
+ switch(event.type) {
+ case iTSMPEGDecoder::videoEvent::eventSizeChanged:
+ m_event((iPlayableService*)this, evVideoSizeChanged);
+ break;
+ case iTSMPEGDecoder::videoEvent::eventFrameRateChanged:
+ m_event((iPlayableService*)this, evVideoFramerateChanged);
+ break;
+ case iTSMPEGDecoder::videoEvent::eventProgressiveChanged:
+ m_event((iPlayableService*)this, evVideoProgressiveChanged);
+ break;
+ default:
+ break;
+ }
+}
+
+RESULT eDVBServicePlay::stream(ePtr<iStreamableService> &ptr)
+{
+ ptr = this;
+ return 0;
+}
+
+PyObject *eDVBServicePlay::getStreamingData()
+{
+ eDVBServicePMTHandler::program program;
+ if (m_service_handler.getProgramInfo(program))
+ {
+ Py_RETURN_NONE;
+ }
+
+ ePyObject r = program.createPythonObject();
+ ePtr<iDVBDemux> demux;
+ if (!m_service_handler.getDataDemux(demux))
+ {
+ uint8_t demux_id;
+ if (!demux->getCADemuxID(demux_id))
+ PutToDict(r, "demux", demux_id);
+ }
+
+ return r;
+}
+
+