+int eDVBServicePlay::selectAudioStream(int i)
+{
+ eDVBServicePMTHandler::program program;
+ eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+
+ if (h.getProgramInfo(program))
+ return -1;
+
+ if ((i != -1) && ((unsigned int)i >= program.audioStreams.size()))
+ return -2;
+
+ if (!m_decoder)
+ return -3;
+
+ int stream = i;
+ if (stream == -1)
+ stream = program.defaultAudioStream;
+
+ int apid = -1, apidtype = -1;
+
+ if (((unsigned int)stream) < program.audioStreams.size())
+ {
+ apid = program.audioStreams[stream].pid;
+ apidtype = program.audioStreams[stream].type;
+ }
+
+ m_current_audio_pid = apid;
+
+ if (m_decoder->setAudioPID(apid, apidtype))
+ {
+ eDebug("set audio pid failed");
+ return -4;
+ }
+
+ /* if we are not in PVR mode, timeshift is not active and we are not in pip mode, check if we need to enable the rds reader */
+ if (!(m_is_pvr || m_timeshift_active || !m_is_primary))
+ if (!m_rds_decoder)
+ {
+ ePtr<iDVBDemux> data_demux;
+ if (!h.getDataDemux(data_demux))
+ {
+ m_rds_decoder = new eDVBRdsDecoder(data_demux);
+ m_rds_decoder->connectEvent(slot(*this, &eDVBServicePlay::rdsDecoderEvent), m_rds_decoder_event_connection);
+ }
+ }
+
+ /* if we decided that we need one, update the pid */
+ if (m_rds_decoder)
+ m_rds_decoder->start(apid);
+
+ /* store new pid as default only when:
+ a.) we have an entry in the service db for the current service,
+ b.) we are not playing back something,
+ c.) we are not selecting the default entry. (we wouldn't change
+ anything in the best case, or destroy the default setting in
+ case the real default is not yet available.)
+ */
+ if (m_dvb_service && !m_is_pvr && ((i != -1)
+ || ((m_dvb_service->getCacheEntry(eDVBService::cAPID) == -1) && (m_dvb_service->getCacheEntry(eDVBService::cAC3PID)==-1))))
+ {
+ if (apidtype == eDVBAudio::aMPEG)
+ {
+ m_dvb_service->setCacheEntry(eDVBService::cAPID, apid);
+ m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
+ }
+ else
+ {
+ m_dvb_service->setCacheEntry(eDVBService::cAPID, -1);
+ m_dvb_service->setCacheEntry(eDVBService::cAC3PID, apid);
+ }
+ }
+
+ h.resetCachedProgram();
+
+ return 0;
+}
+
+int eDVBServicePlay::getCurrentChannel()
+{
+ return m_decoder ? m_decoder->getAudioChannel() : STEREO;
+}
+
+RESULT eDVBServicePlay::selectChannel(int i)
+{
+ if (i < LEFT || i > RIGHT || i == STEREO)
+ i = -1; // Stereo
+ if (m_dvb_service)
+ m_dvb_service->setCacheEntry(eDVBService::cACHANNEL, i);
+ if (m_decoder)
+ m_decoder->setAudioChannel(i);
+ return 0;
+}
+
+std::string eDVBServicePlay::getText(int x)
+{
+ if (m_rds_decoder)
+ switch(x)
+ {
+ case RadioText:
+ return convertLatin1UTF8(m_rds_decoder->getRadioText());
+ case RtpText:
+ return convertLatin1UTF8(m_rds_decoder->getRtpText());
+ }
+ return "";
+}
+
+void eDVBServicePlay::rdsDecoderEvent(int what)
+{
+ switch(what)
+ {
+ case eDVBRdsDecoder::RadioTextChanged:
+ m_event((iPlayableService*)this, evUpdatedRadioText);
+ break;
+ case eDVBRdsDecoder::RtpTextChanged:
+ m_event((iPlayableService*)this, evUpdatedRtpText);
+ break;
+ case eDVBRdsDecoder::RassInteractivePicMaskChanged:
+ m_event((iPlayableService*)this, evUpdatedRassInteractivePicMask);
+ break;
+ case eDVBRdsDecoder::RecvRassSlidePic:
+ m_event((iPlayableService*)this, evUpdatedRassSlidePic);
+ break;
+ }
+}
+
+void eDVBServicePlay::showRassSlidePicture()
+{
+ if (m_rds_decoder)
+ {
+ if (m_decoder)
+ {
+ std::string rass_slide_pic = m_rds_decoder->getRassSlideshowPicture();
+ if (rass_slide_pic.length())
+ m_decoder->showSinglePic(rass_slide_pic.c_str());
+ else
+ eDebug("empty filename for rass slide picture received!!");
+ }
+ else
+ eDebug("no MPEG Decoder to show iframes avail");
+ }
+ else
+ eDebug("showRassSlidePicture called.. but not decoder");
+}
+
+void eDVBServicePlay::showRassInteractivePic(int page, int subpage)
+{
+ if (m_rds_decoder)
+ {
+ if (m_decoder)
+ {
+ std::string rass_interactive_pic = m_rds_decoder->getRassPicture(page, subpage);
+ if (rass_interactive_pic.length())
+ m_decoder->showSinglePic(rass_interactive_pic.c_str());
+ else
+ eDebug("empty filename for rass interactive picture %d/%d received!!", page, subpage);
+ }
+ else
+ eDebug("no MPEG Decoder to show iframes avail");
+ }
+ else
+ eDebug("showRassInteractivePic called.. but not decoder");
+}
+
+ePyObject eDVBServicePlay::getRassInteractiveMask()
+{
+ if (m_rds_decoder)
+ return m_rds_decoder->getRassPictureMask();
+ Py_RETURN_NONE;
+}
+
+int eDVBServiceBase::getFrontendInfo(int w)
+{
+ eUsePtr<iDVBChannel> channel;
+ if(m_service_handler.getChannel(channel))
+ return 0;
+ ePtr<iDVBFrontend> fe;
+ if(channel->getFrontend(fe))
+ return 0;
+ return fe->readFrontendData(w);
+}
+
+PyObject *eDVBServiceBase::getFrontendData()
+{
+ ePyObject ret = PyDict_New();
+ if (ret)
+ {
+ eUsePtr<iDVBChannel> channel;
+ if(!m_service_handler.getChannel(channel))
+ {
+ ePtr<iDVBFrontend> fe;
+ if(!channel->getFrontend(fe))
+ fe->getFrontendData(ret);
+ }
+ }
+ else
+ Py_RETURN_NONE;
+ return ret;
+}
+
+PyObject *eDVBServiceBase::getFrontendStatus()
+{
+ ePyObject ret = PyDict_New();
+ if (ret)
+ {
+ eUsePtr<iDVBChannel> channel;
+ if(!m_service_handler.getChannel(channel))
+ {
+ ePtr<iDVBFrontend> fe;
+ if(!channel->getFrontend(fe))
+ fe->getFrontendStatus(ret);
+ }
+ }
+ else
+ Py_RETURN_NONE;
+ return ret;
+}
+
+PyObject *eDVBServiceBase::getTransponderData(bool original)
+{
+ ePyObject ret = PyDict_New();
+ if (ret)
+ {
+ eUsePtr<iDVBChannel> channel;
+ if(!m_service_handler.getChannel(channel))
+ {
+ ePtr<iDVBFrontend> fe;
+ if(!channel->getFrontend(fe))
+ {
+ fe->getTransponderData(ret, original);
+ ePtr<iDVBFrontendParameters> feparm;
+ channel->getCurrentFrontendParameters(feparm);
+ if (feparm)
+ {
+ eDVBFrontendParametersSatellite osat;
+ if (!feparm->getDVBS(osat))
+ {
+ void PutToDict(ePyObject &, const char*, long);
+ void PutToDict(ePyObject &, const char*, const char*);
+ PutToDict(ret, "orbital_position", osat.orbital_position);
+ const char *tmp = "UNKNOWN";
+ switch(osat.polarisation)
+ {
+ case eDVBFrontendParametersSatellite::Polarisation::Horizontal: tmp="HORIZONTAL"; break;
+ case eDVBFrontendParametersSatellite::Polarisation::Vertical: tmp="VERTICAL"; break;
+ case eDVBFrontendParametersSatellite::Polarisation::CircularLeft: tmp="CIRCULAR_LEFT"; break;
+ case eDVBFrontendParametersSatellite::Polarisation::CircularRight: tmp="CIRCULAR_RIGHT"; break;
+ default:break;
+ }
+ PutToDict(ret, "polarization", tmp);
+ }
+ }
+ }
+ }
+ }
+ else
+ Py_RETURN_NONE;
+ return ret;
+}
+
+PyObject *eDVBServiceBase::getAll(bool original)
+{
+ ePyObject ret = getTransponderData(original);
+ if (ret != Py_None)
+ {
+ eUsePtr<iDVBChannel> channel;
+ if(!m_service_handler.getChannel(channel))
+ {
+ ePtr<iDVBFrontend> fe;
+ if(!channel->getFrontend(fe))
+ {
+ fe->getFrontendData(ret);
+ fe->getFrontendStatus(ret);
+ }
+ }
+ }
+ return ret;
+}
+
+int eDVBServicePlay::getNumberOfSubservices()
+{
+ ePtr<eServiceEvent> evt;
+ if (!m_event_handler.getEvent(evt, 0))
+ return evt->getNumOfLinkageServices();
+ return 0;
+}
+
+RESULT eDVBServicePlay::getSubservice(eServiceReference &sub, unsigned int n)
+{
+ ePtr<eServiceEvent> evt;
+ if (!m_event_handler.getEvent(evt, 0))
+ {
+ if (!evt->getLinkageService(sub, m_reference, n))
+ return 0;
+ }
+ sub.type=eServiceReference::idInvalid;
+ return -1;
+}
+
+RESULT eDVBServicePlay::startTimeshift()
+{
+ ePtr<iDVBDemux> demux;
+
+ eDebug("Start timeshift!");
+
+ if (m_timeshift_enabled)
+ return -1;
+
+ /* start recording with the data demux. */
+ if (m_service_handler.getDataDemux(demux))
+ return -2;
+
+ demux->createTSRecorder(m_record);
+ if (!m_record)
+ return -3;
+
+ char templ[]=TSPATH "/timeshift.XXXXXX";
+ m_timeshift_fd = mkstemp(templ);
+ m_timeshift_file = templ;
+
+ eDebug("recording to %s", templ);
+
+ if (m_timeshift_fd < 0)
+ {
+ m_record = 0;
+ return -4;
+ }
+
+ m_record->setTargetFD(m_timeshift_fd);
+
+ m_timeshift_enabled = 1;
+
+ updateTimeshiftPids();
+ m_record->start();
+
+ return 0;
+}
+
+RESULT eDVBServicePlay::stopTimeshift()
+{
+ if (!m_timeshift_enabled)
+ return -1;
+
+ switchToLive();
+
+ m_timeshift_enabled = 0;
+
+ m_record->stop();
+ m_record = 0;
+
+ close(m_timeshift_fd);
+ eDebug("remove timeshift file");
+ eBackgroundFileEraser::getInstance()->erase(m_timeshift_file.c_str());
+
+ return 0;
+}
+
+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_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(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;
+
+ 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;
+
+ /* 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_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;
+
+ 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 */
+
+ eDebug("eDVBServicePlay::switchToTimeshift, in pause mode now.");
+ pause();
+ updateDecoder(); /* mainly to switch off PCR, and to set pause */
+
+ m_event((iPlayableService*)this, evSeekableStatusChanged);
+}
+
+void eDVBServicePlay::updateDecoder()
+{
+ int vpid = -1, vpidtype = -1, pcrpid = -1, tpid = -1, achannel = -1, ac3_delay=-1, pcm_delay=-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;
+ 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_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);
+ 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);
+ } else
+ {
+ m_teletext_parser = 0;
+ m_subtitle_parser = 0;
+ }
+
+ if (m_cue)
+ m_cue->setDecodingDemux(m_decode_demux, m_decoder);
+ }
+
+ if (m_decoder)
+ {
+ 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 or recording
+ {
+ 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);
+ }
+ }
+ }
+ }
+ }
+ m_decoder->setAC3Delay(ac3_delay == -1 ? 0 : ac3_delay);
+ m_decoder->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);
+
+ m_decoder->setTextPID(tpid);
+
+ m_teletext_parser->start(program.textPid);
+
+ if (!m_is_primary)
+ m_decoder->setTrickmode(1);
+
+ if (m_is_paused)
+ m_decoder->preroll();
+ else
+ m_decoder->start();
+
+ if (vpid > 0 && vpid < 0x2000)
+ ;
+ else
+ {
+ std::string radio_pic;
+ if (!ePythonConfigQuery::getConfigValue("config.misc.radiopic", radio_pic))
+ m_decoder->setRadioPic(radio_pic);
+ }
+
+ m_decoder->setAudioChannel(achannel);
+
+ /* don't worry about non-existing services, nor pvr services */
+ if (m_dvb_service && !m_is_pvr)
+ {
+ /* (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);
+ }
+ }
+ m_have_video_pid = (vpid > 0 && vpid < 0x2000);
+}
+
+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());
+
+ 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 (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)
+ m_cue->addSourceSpan(in, out);
+
+ 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((data&0xFFFF0000)>>16)); // 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 (diff < 0)
+ {
+ eDebug("[late (%d ms)]", -diff / 90);
+ diff = 0;
+ }
+// if (diff > 900000)
+// {
+// eDebug("[invalid]");
+// 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)
+ m_decoder->setAC3Delay(delay);
+}
+
+void eDVBServicePlay::setPCMDelay(int delay)
+{
+ if (m_dvb_service)
+ m_dvb_service->setCacheEntry(eDVBService::cPCMDELAY, delay ? delay : -1);
+ if (m_decoder)
+ m_decoder->setPCMDelay(delay);
+}
+
+void eDVBServicePlay::video_event(struct iTSMPEGDecoder::videoEvent event)
+{
+ memcpy(&m_videoEventData, &event, sizeof(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_INCREF(Py_None);
+ return Py_None;
+ }
+
+ PyObject *r = program.createPythonObject();
+ ePtr<iDVBDemux> demux;
+ if (!m_service_handler.getDataDemux(demux))
+ {
+ uint8_t demux_id;
+ demux->getCADemuxID(demux_id);
+
+ PyDict_SetItemString(r, "demux", PyInt_FromLong(demux_id));
+ }
+
+ return r;
+}
+
+
+DEFINE_REF(eDVBServicePlay)
+
+PyObject *eDVBService::getInfoObject(const eServiceReference &ref, int w)
+{
+ switch (w)
+ {
+ case iServiceInformation::sTransponderData:
+ return eStaticServiceDVBInformation().getInfoObject(ref, w);
+ default:
+ break;
+ }
+ return iStaticServiceInformation::getInfoObject(ref, w);
+}