MythTV  0.26-pre
eitscanner.cpp
Go to the documentation of this file.
00001 // -*- Mode: c++ -*-
00002 
00003 // POSIX headers
00004 #include <sys/time.h>
00005 #include "compat.h"
00006 
00007 #include <cstdlib>
00008 
00009 #include "tv_rec.h"
00010 
00011 #include "channelbase.h"
00012 #include "iso639.h"
00013 #include "eitscanner.h"
00014 #include "eithelper.h"
00015 #include "scheduledrecording.h"
00016 #include "mythmiscutil.h"
00017 #include "mythdb.h"
00018 #include "mythlogging.h"
00019 #include "mthread.h"
00020 
00021 #define LOC QString("EITScanner: ")
00022 #define LOC_ID QString("EITScanner (%1): ").arg(cardnum)
00023 
00031 QMutex     EITScanner::resched_lock;
00032 QDateTime  EITScanner::resched_next_time      = QDateTime::currentDateTime();
00033 const uint EITScanner::kMinRescheduleInterval = 150;
00034 
00035 EITScanner::EITScanner(uint _cardnum)
00036     : channel(NULL),              eitSource(NULL),
00037       eitHelper(new EITHelper()), eventThread(new MThread("EIT", this)),
00038       exitThread(false),
00039       rec(NULL),                  activeScan(false),
00040       activeScanStopped(true),    activeScanTrigTime(0),
00041       cardnum(_cardnum)
00042 {
00043     QStringList langPref = iso639_get_language_list();
00044     eitHelper->SetLanguagePreferences(langPref);
00045 
00046     eventThread->start(QThread::IdlePriority);
00047 }
00048 
00049 void EITScanner::TeardownAll(void)
00050 {
00051     StopActiveScan();
00052     if (!exitThread)
00053     {
00054         lock.lock();
00055         exitThread = true;
00056         exitThreadCond.wakeAll();
00057         lock.unlock();
00058     }
00059     eventThread->wait();
00060     delete eventThread;
00061     eventThread = NULL;
00062 
00063     if (eitHelper)
00064     {
00065         delete eitHelper;
00066         eitHelper = NULL;
00067     }
00068 }
00069 
00073 void EITScanner::run(void)
00074 {
00075     static const uint  sz[] = { 2000, 1800, 1600, 1400, 1200, };
00076     static const float rt[] = { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, };
00077 
00078     lock.lock();
00079 
00080     MythTimer t;
00081     uint eitCount = 0;
00082 
00083     while (!exitThread)
00084     {
00085         lock.unlock();
00086         uint list_size = eitHelper->GetListSize();
00087 
00088         float rate = 1.0f;
00089         for (uint i = 0; i < 5; i++)
00090         {
00091             if (list_size >= sz[i])
00092             {
00093                 rate = rt[i];
00094                 break;
00095             }
00096         }
00097 
00098         lock.lock();
00099         if (eitSource)
00100             eitSource->SetEITRate(rate);
00101         lock.unlock();
00102 
00103         if (list_size)
00104         {
00105             eitCount += eitHelper->ProcessEvents();
00106             t.start();
00107         }
00108 
00109         // If there have been any new events and we haven't
00110         // seen any in a while, tell scheduler to run.
00111         if (eitCount && (t.elapsed() > 60 * 1000))
00112         {
00113             LOG(VB_EIT, LOG_INFO,
00114                 LOC_ID + QString("Added %1 EIT Events").arg(eitCount));
00115             eitCount = 0;
00116             RescheduleRecordings();
00117         }
00118 
00119         if (activeScan && (QDateTime::currentDateTime() > activeScanNextTrig))
00120         {
00121             // if there have been any new events, tell scheduler to run.
00122             if (eitCount)
00123             {
00124                 LOG(VB_EIT, LOG_INFO,
00125                     LOC_ID + QString("Added %1 EIT Events").arg(eitCount));
00126                 eitCount = 0;
00127                 RescheduleRecordings();
00128             }
00129 
00130             if (activeScanNextChan == activeScanChannels.end())
00131                 activeScanNextChan = activeScanChannels.begin();
00132 
00133             if (!(*activeScanNextChan).isEmpty())
00134             {
00135                 eitHelper->WriteEITCache();
00136                 rec->SetChannel(*activeScanNextChan, TVRec::kFlagEITScan);
00137                 LOG(VB_EIT, LOG_INFO,
00138                     LOC_ID + QString("Now looking for EIT data on "
00139                                      "multiplex of channel %1")
00140                         .arg(*activeScanNextChan));
00141             }
00142 
00143             activeScanNextTrig = QDateTime::currentDateTime()
00144                 .addSecs(activeScanTrigTime);
00145             ++activeScanNextChan;
00146 
00147             // 24 hours ago
00148             eitHelper->PruneEITCache(activeScanNextTrig.toTime_t() - 86400);
00149         }
00150 
00151         lock.lock();
00152         if ((activeScan || activeScanStopped) && !exitThread)
00153             exitThreadCond.wait(&lock, 400); // sleep up to 400 ms.
00154 
00155         if (!activeScan && !activeScanStopped)
00156         {
00157             activeScanStopped = true;
00158             activeScanCond.wakeAll();
00159         }
00160     }
00161     activeScanStopped = true;
00162     activeScanCond.wakeAll();
00163     lock.unlock();
00164 }
00165 
00172 void EITScanner::RescheduleRecordings(void)
00173 {
00174     if (!resched_lock.tryLock())
00175         return;
00176 
00177     if (resched_next_time > QDateTime::currentDateTime())
00178     {
00179         LOG(VB_EIT, LOG_INFO, LOC + "Rate limiting reschedules..");
00180         resched_lock.unlock();
00181         return;
00182     }
00183 
00184     resched_next_time =
00185         QDateTime::currentDateTime().addSecs(kMinRescheduleInterval);
00186     resched_lock.unlock();
00187 
00188     ScheduledRecording::RescheduleMatch(0, 0, 0, QDateTime(), "EITScanner");
00189 }
00190 
00195 void EITScanner::StartPassiveScan(ChannelBase *_channel,
00196                                   EITSource *_eitSource)
00197 {
00198     QMutexLocker locker(&lock);
00199 
00200     uint sourceid = _channel->GetCurrentSourceID();
00201     eitSource     = _eitSource;
00202     channel       = _channel;
00203 
00204     eitHelper->SetSourceID(sourceid);
00205     eitSource->SetEITHelper(eitHelper);
00206     eitSource->SetEITRate(1.0f);
00207 
00208     LOG(VB_EIT, LOG_INFO, LOC_ID + "Started passive scan.");
00209 }
00210 
00214 void EITScanner::StopPassiveScan(void)
00215 {
00216     QMutexLocker locker(&lock);
00217 
00218     if (eitSource)
00219     {
00220         eitSource->SetEITHelper(NULL);
00221         eitSource  = NULL;
00222     }
00223     channel = NULL;
00224 
00225     eitHelper->WriteEITCache();
00226     eitHelper->SetSourceID(0);
00227 }
00228 
00229 void EITScanner::StartActiveScan(TVRec *_rec, uint max_seconds_per_source)
00230 {
00231     rec = _rec;
00232 
00233     if (!activeScanChannels.size())
00234     {
00235         // TODO get input name and use it in crawl.
00236         MSqlQuery query(MSqlQuery::InitCon());
00237         query.prepare(
00238             "SELECT channum, MIN(chanid) "
00239             "FROM channel, cardinput, capturecard, videosource "
00240             "WHERE cardinput.sourceid   = channel.sourceid AND "
00241             "      videosource.sourceid = channel.sourceid AND "
00242             "      capturecard.cardid   = cardinput.cardid AND "
00243             "      channel.mplexid        IS NOT NULL      AND "
00244             "      useonairguide        = 1                AND "
00245             "      useeit               = 1                AND "
00246             "      channum             != ''               AND "
00247             "      cardinput.cardid     = :CARDID "
00248             "GROUP BY mplexid "
00249             "ORDER BY cardinput.sourceid, mplexid, "
00250             "         atsc_major_chan, atsc_minor_chan ");
00251         query.bindValue(":CARDID", rec->GetCaptureCardNum());
00252 
00253         if (!query.exec() || !query.isActive())
00254         {
00255             MythDB::DBError("EITScanner::StartActiveScan", query);
00256             return;
00257         }
00258 
00259         while (query.next())
00260             activeScanChannels.push_back(query.value(0).toString());
00261 
00262         activeScanNextChan = activeScanChannels.begin();
00263     }
00264 
00265     LOG(VB_EIT, LOG_INFO, LOC_ID +
00266         QString("StartActiveScan called with %1 multiplexes")
00267             .arg(activeScanChannels.size()));
00268 
00269     // Start at a random channel. This is so that multiple cards with
00270     // the same source don't all scan the same channels in the same
00271     // order when the backend is first started up.
00272     if (activeScanChannels.size())
00273     {
00274         uint randomStart = random() % activeScanChannels.size();
00275         activeScanNextChan = activeScanChannels.begin()+randomStart;
00276 
00277         activeScanNextTrig = QDateTime::currentDateTime();
00278         activeScanTrigTime = max_seconds_per_source;
00279         // Add a little randomness to trigger time so multiple
00280         // cards will have a staggered channel changing time.
00281         activeScanTrigTime += random() % 29;
00282         activeScanStopped = false;
00283         activeScan = true;
00284     }
00285 }
00286 
00287 void EITScanner::StopActiveScan(void)
00288 {
00289     QMutexLocker locker(&lock);
00290 
00291     activeScanStopped = false;
00292     activeScan = false;
00293     exitThreadCond.wakeAll();
00294 
00295     locker.unlock();
00296     StopPassiveScan();
00297     locker.relock();
00298 
00299     while (!activeScan && !activeScanStopped)
00300         activeScanCond.wait(&lock, 100);
00301 
00302     rec = NULL;
00303 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends