/* Copyright (C) 2013 CoolStream International Ltd License: GPLv2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include //#define EPG_RESCAN_TIME (24*60*60) extern CBouquetList * bouquetList; extern CBouquetList * TVfavList; extern CBouquetList * RADIOfavList; extern CBouquetList * TVbouquetList; CEpgScan::CEpgScan() { current_mode = 0; standby = false; rescan_timer = 0; scan_in_progress = false; Clear(); } CEpgScan::~CEpgScan() { } CEpgScan * CEpgScan::getInstance() { static CEpgScan * inst; if (inst == NULL) inst = new CEpgScan(); return inst; } void CEpgScan::ConfigureEIT() { CEitManager::getInstance()->clearChannelFilters(); if (g_settings.epg_save_mode == 0) return; int count = 0; for (unsigned j = 0; j < TVfavList->Bouquets.size(); ++j) { CChannelList * clist = TVfavList->Bouquets[j]->channelList; for (unsigned i = 0; i < clist->Size(); i++) { CZapitChannel * chan = clist->getChannelFromIndex(i); CEitManager::getInstance()->addChannelFilter(chan->getOriginalNetworkId(), chan->getTransportStreamId(), chan->getServiceId()); count++; } } for (unsigned j = 0; j < RADIOfavList->Bouquets.size(); ++j) { CChannelList * clist = RADIOfavList->Bouquets[j]->channelList; for (unsigned i = 0; i < clist->Size(); i++) { CZapitChannel * chan = clist->getChannelFromIndex(i); CEitManager::getInstance()->addChannelFilter(chan->getOriginalNetworkId(), chan->getTransportStreamId(), chan->getServiceId()); count++; } } INFO("added %d channels to EIT white list", count); } void CEpgScan::Clear() { scanmap.clear(); current_bnum = -1; current_bmode = -1; next_chid = 0; allfav_done = false; selected_done = false; } bool CEpgScan::Running() { return (CheckMode() && !scanmap.empty()); } void CEpgScan::AddBouquet(CChannelList * clist) { for (unsigned i = 0; i < clist->Size(); i++) { CZapitChannel * chan = clist->getChannelFromIndex(i); if (!IS_WEBCHAN(chan->getChannelID()) && scanned.find(chan->getTransponderId()) == scanned.end()) scanmap.insert(eit_scanmap_pair_t(chan->getTransponderId(), chan->getChannelID())); } } bool CEpgScan::AddFavorites() { INFO("allfav_done: %d", allfav_done); if ((g_settings.epg_scan != SCAN_FAV) || allfav_done) return false; allfav_done = true; unsigned old_size = scanmap.size(); for (unsigned j = 0; j < TVfavList->Bouquets.size(); ++j) { CChannelList * clist = TVfavList->Bouquets[j]->channelList; AddBouquet(clist); } INFO("scan map size: %d -> %zd\n", old_size, scanmap.size()); return (old_size != scanmap.size()); } bool CEpgScan::AddSelected() { INFO("selected_done: %d", selected_done); if ((g_settings.epg_scan != SCAN_SEL) || selected_done) return false; selected_done = true; unsigned old_size = scanmap.size(); for (unsigned j = 0; j < TVfavList->Bouquets.size(); ++j) { if (TVfavList->Bouquets[j]->zapitBouquet && TVfavList->Bouquets[j]->zapitBouquet->bScanEpg) { CChannelList * clist = TVfavList->Bouquets[j]->channelList; AddBouquet(clist); } } for (unsigned j = 0; j < TVbouquetList->Bouquets.size(); ++j) { if (TVbouquetList->Bouquets[j]->zapitBouquet && TVbouquetList->Bouquets[j]->zapitBouquet->bScanEpg) { CChannelList * clist = TVbouquetList->Bouquets[j]->channelList; AddBouquet(clist); } } INFO("scan map size: %d -> %zd\n", old_size, scanmap.size()); return (old_size != scanmap.size()); } void CEpgScan::AddTransponders() { if(!bouquetList || bouquetList->Bouquets.empty()) return; if (current_mode != g_settings.epg_scan) { current_mode = g_settings.epg_scan; Clear(); } int mode = CNeutrinoApp::getInstance()->GetChannelMode(); if (g_settings.epg_scan == SCAN_SEL) { if (current_bmode != mode) { current_bmode = mode; current_bnum = -1; } int bnum = bouquetList->getActiveBouquetNumber(); bool bscan = bouquetList->Bouquets[bnum]->zapitBouquet && bouquetList->Bouquets[bnum]->zapitBouquet->bScanEpg; if ((current_bnum != bnum) && bscan) { current_bnum = bnum; AddBouquet(bouquetList->Bouquets[current_bnum]->channelList); } else { AddSelected(); } return; } if ((g_settings.epg_scan == SCAN_CURRENT) || (mode == LIST_MODE_FAV)) { /* current bouquet mode */ if (current_bmode != mode) { current_bmode = mode; current_bnum = -1; } if (current_bnum != bouquetList->getActiveBouquetNumber()) { allfav_done = false; scanmap.clear(); current_bnum = bouquetList->getActiveBouquetNumber(); AddBouquet(bouquetList->Bouquets[current_bnum]->channelList); INFO("Added bouquet #%d, scan map size: %zd", current_bnum, scanmap.size()); } } else if (g_settings.epg_scan == SCAN_FAV) { AddFavorites(); } } bool CEpgScan::CheckMode() { bool webchan = IS_WEBCHAN(CZapit::getInstance()->GetCurrentChannelID()); if ((g_settings.epg_scan_mode == CEpgScan::MODE_OFF) || (standby && !(g_settings.epg_scan_mode & MODE_STANDBY)) || (!standby && !(g_settings.epg_scan_mode & MODE_LIVE)) || (!standby && !webchan && (CFEManager::getInstance()->getEnabledCount() <= 1))) { return false; } return true; } void CEpgScan::Start(bool instandby) { standby = instandby; live_channel_id = CZapit::getInstance()->GetCurrentChannelID(); AddTransponders(); INFO("starting %s scan, scanning %d, scan map size: %zd", standby ? "standby" : "live", scan_in_progress, scanmap.size()); if (standby || !scan_in_progress) Next(); } void CEpgScan::Stop() { if (g_settings.epg_scan_mode == CEpgScan::MODE_OFF) return; INFO("stopping %s scan...", standby ? "standby" : "live"); if (standby) { standby = false; CZapit::getInstance()->SetCurrentChannelID(live_channel_id); } } int CEpgScan::handleMsg(const neutrino_msg_t msg, neutrino_msg_data_t data) { if ((msg == NeutrinoMessages::EVT_TIMER) && (data == rescan_timer)) { INFO("rescan timer in %s mode, scanning %d", standby ? "standby" : "live", scan_in_progress); scanned.clear(); Clear(); g_RCInput->killTimer(rescan_timer); if (CheckMode()) { if (standby) CNeutrinoApp::getInstance()->wakeupFromStandby(); Start(standby); } else { AddTimer(); } return messages_return::handled; } if (!CheckMode()) { int ret = messages_return::handled; if (msg == NeutrinoMessages::EVT_EIT_COMPLETE) scan_in_progress = false; else if (msg == NeutrinoMessages::EVT_BACK_ZAP_COMPLETE) scan_in_progress = true; else ret = messages_return::unhandled; return ret; } CZapitChannel * newchan; if (msg == NeutrinoMessages::EVT_ZAP_COMPLETE) { /* live channel changed, block scan channel change by timer */ scan_in_progress = true; AddTransponders(); INFO("EVT_ZAP_COMPLETE, scan map size: %zd\n", scanmap.size()); #if 0 t_channel_id chid = *(t_channel_id *)data; if (IS_WEBCHAN(chid)) Next(); #endif return messages_return::handled; } else if (msg == NeutrinoMessages::EVT_EIT_COMPLETE) { scan_in_progress = false; t_channel_id chid = *(t_channel_id *)data; newchan = CServiceManager::getInstance()->FindChannel(chid); if (newchan) { scanned.insert(newchan->getTransponderId()); scanmap.erase(newchan->getTransponderId()); } INFO("EIT read complete [" PRINTF_CHANNEL_ID_TYPE "], scan map size: %zd", chid, scanmap.size()); Next(); return messages_return::handled; } else if (msg == NeutrinoMessages::EVT_BACK_ZAP_COMPLETE) { scan_in_progress = true; t_channel_id chid = *(t_channel_id *)data; INFO("EVT_BACK_ZAP_COMPLETE [" PRINTF_CHANNEL_ID_TYPE "]", chid); if (next_chid) { newchan = CServiceManager::getInstance()->FindChannel(next_chid); if (newchan) { if(chid) { if (!CRecordManager::getInstance()->RecordingStatus()) { INFO("try to scan [%s]", newchan->getName().c_str()); if (standby && !g_Sectionsd->getIsScanningActive()) g_Sectionsd->setPauseScanning(false); g_Sectionsd->setServiceChanged(newchan->getChannelID(), false, newchan->getRecordDemux()); } } else { INFO("tune failed [%s]", newchan->getName().c_str()); scanmap.erase(newchan->getTransponderId()); Next(); } } } return messages_return::handled; } return messages_return::unhandled; } void CEpgScan::AddTimer() { if (rescan_timer == 0) rescan_timer = g_RCInput->addTimer((g_settings.epg_scan_rescan*60*60)*1000ULL*1000ULL, true); INFO("rescan timer id %d", rescan_timer); INFO("rescan time is %d*60*60", g_settings.epg_scan_rescan); } void CEpgScan::EnterStandby() { AddTimer(); if (standby) { CZapit::getInstance()->SetCurrentChannelID(live_channel_id); CNeutrinoApp::getInstance()->standbyToStandby(); if (g_settings.epg_save && g_settings.epg_save_standby) CNeutrinoApp::getInstance()->saveEpg(NeutrinoModes::mode_standby); } } void CEpgScan::Next() { bool llocked = false; #ifdef ENABLE_PIP bool plocked = false; #endif next_chid = 0; if (!standby && CNeutrinoApp::getInstance()->getMode() == NeutrinoModes::mode_standby) return; if (CRecordManager::getInstance()->RecordingStatus() || CStreamManager::getInstance()->StreamStatus()) return; if (g_settings.epg_scan == SCAN_FAV && scanmap.empty()) AddFavorites(); if (g_settings.epg_scan == SCAN_SEL && scanmap.empty()) AddSelected(); if (!CheckMode() || scanmap.empty()) { EnterStandby(); return; } /* executed in neutrino thread - possible race with locks in zapit zap NOWAIT : send zapTo_NOWAIT -> EIT_COMPLETE from sectionsd -> zap and this at the same time */ CFEManager::getInstance()->Lock(); CFrontend *live_fe = NULL; #ifdef ENABLE_PIP CFrontend *pip_fe = NULL; #endif if (!standby) { bool webchan = IS_WEBCHAN(CZapit::getInstance()->GetCurrentChannelID()); if (!webchan) { llocked = true; live_fe = CZapit::getInstance()->GetLiveFrontend(); CFEManager::getInstance()->lockFrontend(live_fe); } #ifdef ENABLE_PIP pip_fe = CZapit::getInstance()->GetPipFrontend(); if (pip_fe /* && pip_fe != live_fe*/) { plocked = true; CFEManager::getInstance()->lockFrontend(pip_fe); } #endif } _repeat: for (eit_scanmap_iterator_t it = scanmap.begin(); it != scanmap.end(); /* ++it*/) { CZapitChannel * newchan = CServiceManager::getInstance()->FindChannel(it->second); if (newchan == NULL) { scanmap.erase(it++); continue; } if (CFEManager::getInstance()->canTune(newchan)) { INFO("try to tune [%s]", newchan->getName().c_str()); next_chid = newchan->getChannelID(); break; } else INFO("skip [%s], cannot tune", newchan->getName().c_str()); ++it; } if (!next_chid && ((g_settings.epg_scan == SCAN_FAV) && AddFavorites())) goto _repeat; if (!next_chid && ((g_settings.epg_scan == SCAN_SEL) && AddSelected())) goto _repeat; if (llocked) CFEManager::getInstance()->unlockFrontend(live_fe); #ifdef ENABLE_PIP if (plocked) CFEManager::getInstance()->unlockFrontend(pip_fe); #endif CFEManager::getInstance()->Unlock(); if (next_chid) g_Zapit->zapTo_epg(next_chid, standby); else EnterStandby(); }