MythTV  0.26-pre
channelscan_sm.cpp
Go to the documentation of this file.
00001 /* -*- Mode: c++ -*-
00002  * vim: set expandtab tabstop=4 shiftwidth=4:
00003  *
00004  * Original Project
00005  *      MythTV      http://www.mythtv.org
00006  *
00007  * Copyright (c) 2004, 2005 John Pullan <john@pullan.org>
00008  * Copyright (c) 2005 - 2007 Daniel Kristjansson
00009  *
00010  * Description:
00011  *     Collection of classes to provide channel scanning functionallity
00012  *
00013  * This program is free software; you can redistribute it and/or
00014  * modify it under the terms of the GNU General Public License
00015  * as published by the Free Software Foundation; either version 2
00016  * of the License, or (at your option) any later version.
00017  *
00018  * This program is distributed in the hope that it will be useful,
00019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00021  * GNU General Public License for more details.
00022  *
00023  * You should have received a copy of the GNU General Public License
00024  * along with this program; if not, write to the Free Software
00025  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00026  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
00027  *
00028  */
00029 
00030 // C includes
00031 #include <unistd.h>
00032 
00033 // C++ includes
00034 #include <algorithm>
00035 using namespace std;
00036 
00037 // Qt includes
00038 #include <QObject>
00039 
00040 // MythTV includes - General
00041 #include "channelscan_sm.h"
00042 #include "frequencies.h"
00043 #include "mythdbcon.h"
00044 #include "channelutil.h"
00045 #include "cardutil.h"
00046 #include "sourceutil.h"
00047 #include "mthread.h"
00048 #include "mythdb.h"
00049 #include "mythlogging.h"
00050 
00051 // MythTV includes - DTV
00052 #include "dtvsignalmonitor.h"
00053 #include "scanstreamdata.h"
00054 
00055 // MythTV includes - ATSC
00056 #include "atsctables.h"
00057 
00058 // MythTV includes - DVB
00059 #include "dvbsignalmonitor.h"
00060 #include "dvbtables.h"
00061 
00062 #include "dvbchannel.h"
00063 #include "hdhrchannel.h"
00064 #include "v4lchannel.h"
00065 
00069 const uint ChannelScanSM::kDVBTableTimeout  = 30 * 1000;
00071 const uint ChannelScanSM::kATSCTableTimeout = 10 * 1000;
00073 const uint ChannelScanSM::kMPEGTableTimeout = 15 * 1000;
00074 
00075 QString ChannelScanSM::loc(const ChannelScanSM *siscan)
00076 {
00077     if (siscan && siscan->channel)
00078         return QString("ChannelScanSM(%1)").arg(siscan->channel->GetDevice());
00079     return "ChannelScanSM(u)";
00080 }
00081 
00082 #define LOC     (ChannelScanSM::loc(this) + ": ")
00083 
00084 #define kDecryptionTimeout 4250
00085 
00086 class ScannedChannelInfo
00087 {
00088   public:
00089     ScannedChannelInfo() : mgt(NULL) {}
00090 
00091     bool IsEmpty() const
00092     {
00093         return pats.empty() && pmts.empty()        &&
00094                program_encryption_status.isEmpty() &&
00095                !mgt         && cvcts.empty()       && tvcts.empty() &&
00096                nits.empty() && sdts.empty();
00097     }
00098 
00099     // MPEG
00100     pat_map_t         pats;
00101     pmt_vec_t         pmts;
00102     QMap<uint,uint>   program_encryption_status; // pnum->enc_status
00103 
00104     // ATSC
00105     const MasterGuideTable *mgt;
00106     cvct_vec_t        cvcts;
00107     tvct_vec_t        tvcts;
00108 
00109     // DVB
00110     nit_vec_t         nits;
00111     sdt_map_t         sdts;
00112 };
00113 
00137 ChannelScanSM::ChannelScanSM(
00138     ScanMonitor *_scan_monitor,
00139     const QString &_cardtype, ChannelBase *_channel,
00140     int _sourceID, uint signal_timeout, uint channel_timeout,
00141     const QString &_inputname, bool test_decryption)
00142     : // Set in constructor
00143       scan_monitor(_scan_monitor),
00144       channel(_channel),
00145       signalMonitor(SignalMonitor::Init(_cardtype, -1, _channel)),
00146       sourceID(_sourceID),
00147       signalTimeout(signal_timeout),
00148       channelTimeout(channel_timeout),
00149       otherTableTimeout(0),
00150       otherTableTime(0),
00151       setOtherTables(false),
00152       inputname(_inputname),
00153       m_test_decryption(test_decryption),
00154       extend_scan_list(false),
00155       // Optional state
00156       scanDTVTunerType(DTVTunerType::kTunerTypeUnknown),
00157       // State
00158       scanning(false),
00159       threadExit(false),
00160       waitingForTables(false),
00161       // Transports List
00162       transportsScanned(0),
00163       currentTestingDecryption(false),
00164       // Misc
00165       channelsFound(999),
00166       currentInfo(NULL),
00167       analogSignalHandler(new AnalogSignalHandler(this)),
00168       scannerThread(NULL)
00169 {
00170     inputname.detach();
00171 
00172     current = scanTransports.end();
00173 
00174     // Create a stream data for digital signal monitors
00175     DTVSignalMonitor* dtvSigMon = GetDTVSignalMonitor();
00176     if (dtvSigMon)
00177     {
00178         LOG(VB_CHANSCAN, LOG_INFO, LOC + "Connecting up DTVSignalMonitor");
00179         ScanStreamData *data = new ScanStreamData();
00180 
00181         dtvSigMon->SetStreamData(data);
00182         dtvSigMon->AddFlags(SignalMonitor::kDTVSigMon_WaitForMGT |
00183                             SignalMonitor::kDTVSigMon_WaitForVCT |
00184                             SignalMonitor::kDTVSigMon_WaitForNIT |
00185                             SignalMonitor::kDTVSigMon_WaitForSDT);
00186 
00187 #ifdef USING_DVB
00188         DVBChannel *dvbchannel = dynamic_cast<DVBChannel*>(channel);
00189         if (dvbchannel && dvbchannel->GetRotor())
00190             dtvSigMon->AddFlags(SignalMonitor::kDVBSigMon_WaitForPos);
00191 #endif
00192 
00193         data->AddMPEGListener(this);
00194         data->AddATSCMainListener(this);
00195         data->AddDVBMainListener(this);
00196         data->AddDVBOtherListener(this);
00197     }
00198 }
00199 
00200 ChannelScanSM::~ChannelScanSM(void)
00201 {
00202     StopScanner();
00203     LOG(VB_CHANSCAN, LOG_INFO, LOC + "ChannelScanSM Stopped");
00204 
00205     ScanStreamData *sd = NULL;
00206     if (GetDTVSignalMonitor())
00207     {
00208         sd = GetDTVSignalMonitor()->GetScanStreamData();
00209     }
00210 
00211     if (signalMonitor)
00212     {
00213         signalMonitor->RemoveListener(analogSignalHandler);
00214         delete signalMonitor;
00215         signalMonitor = NULL;
00216     }
00217 
00218     delete sd;
00219 
00220     if (analogSignalHandler)
00221     {
00222         delete analogSignalHandler;
00223         analogSignalHandler = NULL;
00224     }
00225 
00226     teardown_frequency_tables();
00227 }
00228 
00229 void ChannelScanSM::SetAnalog(bool is_analog)
00230 {
00231     signalMonitor->RemoveListener(analogSignalHandler);
00232 
00233     if (is_analog)
00234         signalMonitor->AddListener(analogSignalHandler);
00235 }
00236 
00237 void ChannelScanSM::HandleAllGood(void)
00238 {
00239     QMutexLocker locker(&lock);
00240 
00241     QString cur_chan = (*current).FriendlyName;
00242     QStringList list = cur_chan.split(" ", QString::SkipEmptyParts);
00243     QString freqid = (list.size() >= 2) ? list[1] : cur_chan;
00244 
00245     bool ok = false;
00246 
00247     QString msg = QObject::tr("Updated Channel %1").arg(cur_chan);
00248 
00249     if (!ChannelUtil::FindChannel(sourceID, freqid))
00250     {
00251         int chanid = ChannelUtil::CreateChanID(sourceID, freqid);
00252 
00253         QString callsign = QString("%1-%2")
00254             .arg(ChannelUtil::GetUnknownCallsign()).arg(chanid);
00255 
00256         ok = ChannelUtil::CreateChannel(
00257             0      /* mplexid */,
00258             sourceID,
00259             chanid,
00260             callsign,
00261             ""     /* service name       */,
00262             freqid /* channum            */,
00263             0      /* service id         */,
00264             0      /* ATSC major channel */,
00265             0      /* ATSC minor channel */,
00266             false  /* use on air guide   */,
00267             false  /* hidden             */,
00268             false  /* hidden in guide    */,
00269             freqid);
00270 
00271         msg = (ok) ?
00272             QObject::tr("Added Channel %1").arg(cur_chan) :
00273             QObject::tr("Failed to add channel %1").arg(cur_chan);
00274     }
00275     else
00276     {
00277         // nothing to do here, XMLTV & DataDirect have better info
00278     }
00279 
00280     scan_monitor->ScanAppendTextToLog(msg);
00281 
00282     // tell UI we are done with these channels
00283     if (scanning)
00284     {
00285         UpdateScanPercentCompleted();
00286         waitingForTables = false;
00287         nextIt = current.nextTransport();
00288     }
00289 }
00290 
00302 bool ChannelScanSM::ScanExistingTransports(uint sourceid, bool follow_nit)
00303 {
00304     if (scanning)
00305         return false;
00306 
00307     scanTransports.clear();
00308     nextIt = scanTransports.end();
00309 
00310     vector<uint> multiplexes = SourceUtil::GetMplexIDs(sourceid);
00311 
00312     if (multiplexes.empty())
00313     {
00314         LOG(VB_CHANSCAN, LOG_ERR, LOC + "Unable to find any transports for " +
00315             QString("sourceid %1").arg(sourceid));
00316 
00317         return false;
00318     }
00319 
00320     for (uint i = 0; i < multiplexes.size(); i++)
00321         AddToList(multiplexes[i]);
00322 
00323     extend_scan_list = follow_nit;
00324     waitingForTables  = false;
00325     transportsScanned = 0;
00326     if (scanTransports.size())
00327     {
00328         nextIt   = scanTransports.begin();
00329         scanning = true;
00330     }
00331     else
00332     {
00333         LOG(VB_CHANSCAN, LOG_ERR, LOC +
00334             "Unable to find add any transports for " +
00335             QString("sourceid %1").arg(sourceid));
00336 
00337         return false;
00338     }
00339 
00340 
00341     return scanning;
00342 }
00343 
00344 void ChannelScanSM::HandlePAT(const ProgramAssociationTable *pat)
00345 {
00346     QMutexLocker locker(&lock);
00347 
00348     LOG(VB_CHANSCAN, LOG_INFO, LOC +
00349         QString("Got a Program Association Table for %1")
00350             .arg((*current).FriendlyName) + "\n" + pat->toString());
00351 
00352     // Add pmts to list, so we can do MPEG scan properly.
00353     ScanStreamData *sd = GetDTVSignalMonitor()->GetScanStreamData();
00354     for (uint i = 0; i < pat->ProgramCount(); i++)
00355     {
00356         if (pat->ProgramPID(i)) // don't add NIT "program", MPEG/ATSC safe.
00357             sd->AddListeningPID(pat->ProgramPID(i));
00358     }
00359 }
00360 
00361 void ChannelScanSM::HandlePMT(uint, const ProgramMapTable *pmt)
00362 {
00363     QMutexLocker locker(&lock);
00364 
00365     LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Got a Program Map Table for %1")
00366             .arg((*current).FriendlyName) + "\n" + pmt->toString());
00367 
00368     if (!currentTestingDecryption && pmt->IsEncrypted(GetDTVChannel()->GetSIStandard()))
00369         currentEncryptionStatus[pmt->ProgramNumber()] = kEncUnknown;
00370 }
00371 
00372 void ChannelScanSM::HandleVCT(uint, const VirtualChannelTable *vct)
00373 {
00374     QMutexLocker locker(&lock);
00375 
00376     LOG(VB_CHANSCAN, LOG_INFO, LOC +
00377         QString("Got a Virtual Channel Table for %1")
00378             .arg((*current).FriendlyName) + "\n" + vct->toString());
00379 
00380     for (uint i = 0; !currentTestingDecryption && i < vct->ChannelCount(); i++)
00381     {
00382         if (vct->IsAccessControlled(i))
00383         {
00384             currentEncryptionStatus[vct->ProgramNumber(i)] = kEncUnknown;
00385         }
00386     }
00387 
00388     UpdateChannelInfo(true);
00389 }
00390 
00391 void ChannelScanSM::HandleMGT(const MasterGuideTable *mgt)
00392 {
00393     QMutexLocker locker(&lock);
00394 
00395     LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Got the Master Guide for %1")
00396             .arg((*current).FriendlyName) + "\n" + mgt->toString());
00397 
00398     UpdateChannelInfo(true);
00399 }
00400 
00401 void ChannelScanSM::HandleSDT(uint tsid, const ServiceDescriptionTable *sdt)
00402 {
00403     QMutexLocker locker(&lock);
00404 
00405     LOG(VB_CHANSCAN, LOG_INFO, LOC +
00406         QString("Got a Service Description Table for %1")
00407             .arg((*current).FriendlyName) + "\n" + sdt->toString());
00408 
00409     // If this is Astra 28.2 add start listening for Freesat BAT and SDTo
00410     if (!setOtherTables && (sdt->OriginalNetworkID() == 2 ||
00411         sdt->OriginalNetworkID() == 59))
00412     {
00413         GetDTVSignalMonitor()->GetScanStreamData()->
00414                                SetFreesatAdditionalSI(true);
00415         setOtherTables = true;
00416         // The whole BAT & SDTo group comes round in 10s
00417         otherTableTimeout = 10000; 
00418         // Delay processing the SDT until we've seen BATs and SDTos
00419         otherTableTime = timer.elapsed() + otherTableTimeout;
00420 
00421         LOG(VB_CHANSCAN, LOG_INFO, LOC +
00422             QString("SDT has OriginalNetworkID %1, look for "
00423                     "additional Freesat SI").arg(sdt->OriginalNetworkID()));
00424     }
00425 
00426     if ((uint)timer.elapsed() < otherTableTime)
00427     {
00428         // Set the version for the SDT so we see it again.
00429         GetDTVSignalMonitor()->GetDVBStreamData()->SetVersionSDT(sdt->TSID(), -1, 0);
00430     }
00431 
00432     uint id = sdt->OriginalNetworkID() << 16 | sdt->TSID();
00433     ts_scanned.insert(id);
00434 
00435     for (uint i = 0; !currentTestingDecryption && i < sdt->ServiceCount(); i++)
00436     {
00437         if (sdt->IsEncrypted(i))
00438         {
00439             currentEncryptionStatus[sdt->ServiceID(i)] = kEncUnknown;
00440         }
00441     }
00442 
00443     UpdateChannelInfo(true);
00444 }
00445 
00446 void ChannelScanSM::HandleNIT(const NetworkInformationTable *nit)
00447 {
00448     QMutexLocker locker(&lock);
00449 
00450     LOG(VB_CHANSCAN, LOG_INFO, LOC +
00451         QString("Got a Network Information Table for %1")
00452             .arg((*current).FriendlyName) + "\n" + nit->toString());
00453 
00454     UpdateChannelInfo(true);
00455 }
00456 
00457 void ChannelScanSM::HandleBAT(const BouquetAssociationTable *bat)
00458 {
00459     QMutexLocker locker(&lock);
00460 
00461     LOG(VB_CHANSCAN, LOG_INFO, LOC + "Got a Bouquet Association Table\n" +
00462         bat->toString());
00463 
00464     otherTableTime = timer.elapsed() + otherTableTimeout;
00465 
00466     for (uint i = 0; i < bat->TransportStreamCount(); i++)
00467     {
00468         uint tsid = bat->TSID(i);
00469         uint netid = bat->OriginalNetworkID(i);
00470         desc_list_t parsed =
00471             MPEGDescriptor::Parse(bat->TransportDescriptors(i),
00472                                   bat->TransportDescriptorsLength(i));
00473         // Look for default authority
00474         const unsigned char *def_auth =
00475             MPEGDescriptor::Find(parsed, DescriptorID::default_authority);
00476         const unsigned char *serv_list =
00477             MPEGDescriptor::Find(parsed, DescriptorID::service_list);
00478 
00479         if (def_auth && serv_list)
00480         {
00481             DefaultAuthorityDescriptor authority(def_auth);
00482             ServiceListDescriptor services(serv_list);
00483 
00484             for (uint j = 0; j < services.ServiceCount(); j++)
00485             {
00486                 // If the default authority is given in the SDT this
00487                 // overrides any definition in the BAT (or in the NIT)
00488                 LOG(VB_CHANSCAN, LOG_INFO, LOC +
00489                     QString("found default authority(BAT) for service %1 %2 %3")
00490                         .arg(netid).arg(tsid).arg(services.ServiceID(j)));
00491                uint64_t index = ((uint64_t)netid << 32) | (tsid << 16) |
00492                                  services.ServiceID(j);
00493                if (! defAuthorities.contains(index))
00494                    defAuthorities[index] = authority.DefaultAuthority();
00495             }
00496         }
00497     }
00498 }
00499 
00500 void ChannelScanSM::HandleSDTo(uint tsid, const ServiceDescriptionTable *sdt)
00501 {
00502     QMutexLocker locker(&lock);
00503 
00504     LOG(VB_CHANSCAN, LOG_INFO, LOC +
00505         "Got a Service Description Table (other)\n" + sdt->toString());
00506 
00507     otherTableTime = timer.elapsed() + otherTableTimeout;
00508 
00509     uint netid = sdt->OriginalNetworkID();
00510 
00511     for (uint i = 0; i < sdt->ServiceCount(); i++)
00512     {
00513         uint serviceId = sdt->ServiceID(i);
00514         desc_list_t parsed =
00515             MPEGDescriptor::Parse(sdt->ServiceDescriptors(i),
00516                                   sdt->ServiceDescriptorsLength(i));
00517         // Look for default authority
00518         const unsigned char *def_auth =
00519             MPEGDescriptor::Find(parsed, DescriptorID::default_authority);
00520         if (def_auth)
00521         {
00522             DefaultAuthorityDescriptor authority(def_auth);
00523             LOG(VB_CHANSCAN, LOG_INFO, LOC +
00524                 QString("found default authority(SDTo) for service %1 %2 %3")
00525                     .arg(netid).arg(tsid).arg(serviceId));
00526             defAuthorities[((uint64_t)netid << 32) | (tsid << 16) | serviceId] =
00527                 authority.DefaultAuthority();
00528         }
00529     }
00530 }
00531 
00532 void ChannelScanSM::HandleEncryptionStatus(uint pnum, bool encrypted)
00533 {
00534     QMutexLocker locker(&lock);
00535 
00536     currentEncryptionStatus[pnum] = encrypted ? kEncEncrypted : kEncDecrypted;
00537 
00538     if (kEncDecrypted == currentEncryptionStatus[pnum])
00539         currentTestingDecryption = false;
00540 
00541     UpdateChannelInfo(true);
00542 }
00543 
00544 bool ChannelScanSM::TestNextProgramEncryption(void)
00545 {
00546     if (!currentInfo || currentInfo->pmts.empty())
00547     {
00548         LOG(VB_GENERAL, LOG_ERR, LOC + "Can't monitor decryption -- no pmts");
00549         currentTestingDecryption = false;
00550         return false;
00551     }
00552 
00553     do
00554     {
00555         uint pnum = 0;
00556         QMap<uint, uint>::const_iterator it = currentEncryptionStatus.begin();
00557 #if 0
00558         LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("%1/%2 checked")
00559             .arg(currentEncryptionStatusChecked.size())
00560             .arg(currentEncryptionStatus.size()));
00561 #endif
00562         while (it != currentEncryptionStatus.end())
00563         {
00564             if (!currentEncryptionStatusChecked[it.key()])
00565             {
00566                 pnum = it.key();
00567                 break;
00568             }
00569             ++it;
00570         }
00571 
00572         if (!pnum)
00573             break;
00574 
00575         currentEncryptionStatusChecked[pnum] = true;
00576 
00577         if (!m_test_decryption)
00578         {
00579             currentEncryptionStatus[pnum] = kEncEncrypted;
00580             continue;
00581         }
00582 
00583         const ProgramMapTable *pmt = NULL;
00584         for (uint i = 0; !pmt && (i < currentInfo->pmts.size()); i++)
00585         {
00586             pmt = (currentInfo->pmts[i]->ProgramNumber() == pnum) ?
00587                 currentInfo->pmts[i] : NULL;
00588         }
00589 
00590         if (pmt)
00591         {
00592             QString cur_chan, cur_chan_tr;
00593             GetCurrentTransportInfo(cur_chan, cur_chan_tr);
00594 
00595             QString msg_tr =
00596                 QObject::tr("%1 -- Testing decryption of program %2")
00597                 .arg(cur_chan_tr).arg(pnum);
00598             QString msg =
00599                 QString("%1 -- Testing decryption of program %2")
00600                 .arg(cur_chan).arg(pnum);
00601 
00602             scan_monitor->ScanAppendTextToLog(msg_tr);
00603             LOG(VB_CHANSCAN, LOG_INFO, LOC + msg);
00604 
00605 #ifdef USING_DVB
00606             if (GetDVBChannel())
00607                 GetDVBChannel()->SetPMT(pmt);
00608 #endif // USING_DVB
00609 
00610             GetDTVSignalMonitor()->GetStreamData()->TestDecryption(pmt);
00611 
00612             currentTestingDecryption = true;
00613             timer.start();
00614             return true;
00615         }
00616 
00617         LOG(VB_GENERAL, LOG_INFO, LOC +
00618             QString("Can't monitor decryption of program %1 -- no pmt")
00619                 .arg(pnum));
00620 
00621     } while (true);
00622 
00623     currentTestingDecryption = false;
00624     return false;
00625 }
00626 
00627 DTVTunerType ChannelScanSM::GuessDTVTunerType(DTVTunerType type) const
00628 {
00629     if (scanDTVTunerType != (int)DTVTunerType::kTunerTypeUnknown)
00630         type = scanDTVTunerType;
00631 
00632     const DTVChannel *chan = GetDTVChannel();
00633 
00634     if (!chan)
00635         return type;
00636 
00637     vector<DTVTunerType> tts = chan->GetTunerTypes();
00638 
00639     for (uint i = 0; i < tts.size(); ++i)
00640     {
00641         if (tts[i] == type)
00642             return type;
00643     }
00644 
00645     if (!tts.empty())
00646         return tts[0];
00647 
00648     return type;
00649 }
00650 
00651 void ChannelScanSM::UpdateScanTransports(const NetworkInformationTable *nit)
00652 {
00653     for (uint i = 0; i < nit->TransportStreamCount(); ++i)
00654     {
00655         uint32_t tsid  = nit->TSID(i);
00656         uint32_t netid = nit->OriginalNetworkID(i);
00657         uint32_t id    = netid << 16 | tsid;
00658 
00659         if (ts_scanned.contains(id) || extend_transports.contains(id))
00660             continue;
00661 
00662         const desc_list_t& list =
00663             MPEGDescriptor::Parse(nit->TransportDescriptors(i),
00664                                   nit->TransportDescriptorsLength(i));
00665 
00666         for (uint j = 0; j < list.size(); ++j)
00667         {
00668             int mplexid = -1;
00669             uint64_t frequency = 0;
00670             const MPEGDescriptor desc(list[j]);
00671             uint tag = desc.DescriptorTag();
00672             DTVTunerType tt = DTVTunerType::kTunerTypeUnknown;
00673 
00674             switch (tag)
00675             {
00676                 case DescriptorID::terrestrial_delivery_system:
00677                 {
00678                     const TerrestrialDeliverySystemDescriptor cd(desc);
00679                     frequency = cd.FrequencyHz();
00680                     tt = DTVTunerType::kTunerTypeDVBT;
00681                     break;
00682                 }
00683                 case DescriptorID::satellite_delivery_system:
00684                 {
00685                     const SatelliteDeliverySystemDescriptor cd(desc);
00686                     frequency = cd.FrequencyHz()/1000;
00687                     tt = DTVTunerType::kTunerTypeDVBS1;
00688                     break;
00689                 }
00690                 case DescriptorID::cable_delivery_system:
00691                 {
00692                     const CableDeliverySystemDescriptor cd(desc);
00693                     frequency = cd.FrequencyHz();
00694                     tt = DTVTunerType::kTunerTypeDVBC;
00695                     break;
00696                 }
00697                 default:
00698                     LOG(VB_CHANSCAN, LOG_ERR, LOC +
00699                         "unknown delivery system descriptor");
00700                     continue;
00701             }
00702 
00703             mplexid = ChannelUtil::GetMplexID(sourceID, frequency, tsid, netid);
00704             mplexid = max(0, mplexid);
00705 
00706             tt = GuessDTVTunerType(tt);
00707 
00708             DTVMultiplex tuning;
00709             if (mplexid)
00710             {
00711                 if (!tuning.FillFromDB(tt, mplexid))
00712                     continue;
00713             }
00714             else if (!tuning.FillFromDeliverySystemDesc(tt, desc))
00715             {
00716                 continue;
00717             }
00718 
00719             extend_transports[id] = tuning;
00720             break;
00721         }
00722     }
00723 }
00724 
00725 bool ChannelScanSM::UpdateChannelInfo(bool wait_until_complete)
00726 {
00727     if (current == scanTransports.end())
00728         return true;
00729 
00730     if (wait_until_complete && currentTestingDecryption)
00731         return false;
00732 
00733     DTVSignalMonitor *dtv_sm = GetDTVSignalMonitor();
00734     if (!dtv_sm)
00735         return false;
00736 
00737     const ScanStreamData *sd = dtv_sm->GetScanStreamData();
00738 
00739     if (!currentInfo)
00740         currentInfo = new ScannedChannelInfo();
00741 
00742     bool transport_tune_complete = true;
00743 
00744     // MPEG
00745 
00746     // Grab PAT tables
00747     pat_vec_t pattmp = sd->GetCachedPATs();
00748     QMap<uint,bool> tsid_checked;
00749     for (uint i = 0; i < pattmp.size(); i++)
00750     {
00751         uint tsid = pattmp[i]->TransportStreamID();
00752         if (tsid_checked[tsid])
00753             continue;
00754         tsid_checked[tsid] = true;
00755         if (currentInfo->pats.contains(tsid))
00756             continue;
00757 
00758         if (!wait_until_complete || sd->HasCachedAllPAT(tsid))
00759         {
00760             currentInfo->pats[tsid] = sd->GetCachedPATs(tsid);
00761             if (!currentInfo->pmts.empty())
00762             {
00763                 sd->ReturnCachedPMTTables(currentInfo->pmts);
00764                 currentInfo->pmts.clear();
00765             }
00766         }
00767         else
00768             transport_tune_complete = false;
00769     }
00770     transport_tune_complete &= !pattmp.empty();
00771     sd->ReturnCachedPATTables(pattmp);
00772 
00773     // Grab PMT tables
00774     if ((!wait_until_complete || sd->HasCachedAllPMTs()) && currentInfo->pmts.empty())
00775         currentInfo->pmts = sd->GetCachedPMTs();
00776 
00777     // ATSC
00778     if (!currentInfo->mgt && sd->HasCachedMGT())
00779         currentInfo->mgt = sd->GetCachedMGT();
00780 
00781     if ((!wait_until_complete || sd->HasCachedAllCVCTs()) &&
00782         currentInfo->cvcts.empty())
00783     {
00784         currentInfo->cvcts = sd->GetCachedCVCTs();
00785     }
00786 
00787     if ((!wait_until_complete || sd->HasCachedAllTVCTs()) &&
00788         currentInfo->tvcts.empty())
00789     {
00790         currentInfo->tvcts = sd->GetCachedTVCTs();
00791     }
00792 
00793     // DVB
00794     if ((!wait_until_complete || sd->HasCachedAllNIT()) && (currentInfo->nits.empty() ||
00795         timer.elapsed() > (int)otherTableTime))
00796     {
00797         currentInfo->nits = sd->GetCachedNIT();
00798     }
00799 
00800     sdt_vec_t sdttmp = sd->GetCachedSDTs();
00801     tsid_checked.clear();
00802     for (uint i = 0; i < sdttmp.size(); i++)
00803     {
00804         uint tsid = sdttmp[i]->TSID();
00805         if (tsid_checked[tsid])
00806             continue;
00807         tsid_checked[tsid] = true;
00808         if (currentInfo->sdts.contains(tsid))
00809             continue;
00810 
00811         if (!wait_until_complete || sd->HasCachedAllSDT(tsid))
00812             currentInfo->sdts[tsid] = sd->GetCachedSDTs(tsid);
00813     }
00814     sd->ReturnCachedSDTTables(sdttmp);
00815 
00816     // Check if transport tuning is complete
00817     if (transport_tune_complete)
00818     {
00819         transport_tune_complete &= !currentInfo->pmts.empty();
00820         if (sd->HasCachedMGT() || sd->HasCachedAnyVCTs())
00821         {
00822             transport_tune_complete &= sd->HasCachedMGT();
00823             transport_tune_complete &=
00824                 (!currentInfo->tvcts.empty() || !currentInfo->cvcts.empty());
00825         }
00826         if (sd->HasCachedAnyNIT() || sd->HasCachedAnySDTs())
00827         {
00828             transport_tune_complete &= !currentInfo->nits.empty();
00829             transport_tune_complete &= !currentInfo->sdts.empty();
00830         }
00831         if (transport_tune_complete)
00832         {
00833             LOG(VB_CHANSCAN, LOG_INFO, LOC +
00834                 QString("transport_tune_complete: "
00835                         "\n\t\t\tcurrentInfo->pmts.empty():     %1"
00836                         "\n\t\t\tsd->HasCachedAnyNIT():         %2"
00837                         "\n\t\t\tsd->HasCachedAnySDTs():        %3"
00838                         "\n\t\t\tcurrentInfo->nits.empty():     %4"
00839                         "\n\t\t\tcurrentInfo->sdts.empty():     %5")
00840                     .arg(currentInfo->pmts.empty())
00841                     .arg(sd->HasCachedAnyNIT())
00842                     .arg(sd->HasCachedAnySDTs())
00843                     .arg(currentInfo->nits.empty())
00844                     .arg(currentInfo->sdts.empty()));
00845         }
00846     }
00847     transport_tune_complete |= !wait_until_complete;
00848     if (transport_tune_complete)
00849     {
00850         LOG(VB_CHANSCAN, LOG_INFO, LOC +
00851             QString("transport_tune_complete: wait_until_complete %1")
00852                 .arg(wait_until_complete));
00853     }
00854 
00855     if (transport_tune_complete && currentEncryptionStatus.size())
00857     {
00858         //GetDTVSignalMonitor()->GetStreamData()->StopTestingDecryption();
00859 
00860         if (TestNextProgramEncryption())
00861             return false;
00862 
00863         QMap<uint, uint>::const_iterator it = currentEncryptionStatus.begin();
00864         for (; it != currentEncryptionStatus.end(); ++it)
00865         {
00866             currentInfo->program_encryption_status[it.key()] = *it;
00867 
00868             QString msg_tr1 = QObject::tr("Program %1").arg(it.key());
00869             QString msg_tr2 = QObject::tr("Unknown decryption status");
00870             if (kEncEncrypted == *it)
00871                 msg_tr2 = QObject::tr("Encrypted");
00872             else if (kEncDecrypted == *it)
00873                 msg_tr2 = QObject::tr("Decrypted");
00874             QString msg_tr =QString("%1, %2").arg(msg_tr1).arg(msg_tr2);
00875 
00876             QString msg = LOC + QString("Program %1").arg(it.key());
00877             if (kEncEncrypted == *it)
00878                 msg = msg + " -- Encrypted";
00879             else if (kEncDecrypted == *it)
00880                 msg = msg + " -- Decrypted";
00881             else if (kEncUnknown == *it)
00882                 msg = msg + " -- Unknown decryption status";
00883 
00884             scan_monitor->ScanAppendTextToLog(msg_tr);
00885             LOG(VB_CHANSCAN, LOG_INFO, LOC + msg);
00886         }
00887     }
00888 
00889     // append transports from the NIT to the scan list
00890     if (transport_tune_complete && extend_scan_list &&
00891         !currentInfo->nits.empty())
00892     {
00893         // append delivery system descriptos to scan list
00894         nit_vec_t::const_iterator it = currentInfo->nits.begin();
00895         while (it != currentInfo->nits.end())
00896         {
00897             UpdateScanTransports(*it);
00898             ++it;
00899         }
00900     }
00901 
00902     // Start scanning next transport if we are done with this one..
00903     if (transport_tune_complete)
00904     {
00905         QString cchan, cchan_tr;
00906         uint cchan_cnt = GetCurrentTransportInfo(cchan, cchan_tr);
00907         channelsFound += cchan_cnt;
00908         QString chan_tr = QObject::tr("%1 -- Timed out").arg(cchan_tr);
00909         QString chan    = QString(    "%1 -- Timed out").arg(cchan);
00910         QString msg_tr  = "";
00911         QString msg     = "";
00912 
00913         if (currentInfo && !currentInfo->IsEmpty())
00914         {
00915             LOG(VB_CHANSCAN, LOG_INFO, LOC +
00916                 QString("Adding %1, offset %2 to channelList.")
00917                     .arg((*current).tuning.toString()).arg(current.offset()));
00918             channelList << ChannelListItem(current, currentInfo);
00919             currentInfo = NULL;
00920         }
00921         else
00922         {
00923             delete currentInfo;
00924             currentInfo = NULL;
00925         }
00926 
00927         SignalMonitor *sm = GetSignalMonitor();
00928         if ((timer.elapsed() > (int)channelTimeout))
00929         {
00930             msg_tr = (cchan_cnt) ?
00931                 QObject::tr("%1 possible channels").arg(cchan_cnt) :
00932                 QObject::tr("no channels");
00933             msg_tr = QString("%1, %2").arg(chan_tr).arg(msg_tr);
00934             msg = (cchan_cnt) ?
00935                 QString("%1 possible channels").arg(cchan_cnt) :
00936                 QString("no channels");
00937             msg = QString("%1, %2").arg(chan_tr).arg(msg);
00938         }
00939         else if ((current != scanTransports.end()) &&
00940                  (timer.elapsed() > (int)(*current).timeoutTune) &&
00941                  sm && !sm->HasSignalLock())
00942         {
00943             msg_tr = QObject::tr("%1, no signal").arg(chan_tr);
00944             msg = QString("%1, no signal").arg(chan);
00945         }
00946         else
00947         {
00948             msg_tr = QObject::tr("%1 -- Found %2 probable channels")
00949                 .arg(cchan_tr).arg(cchan_cnt);
00950 
00951             msg = QString("%1 -- Found %2 probable channels")
00952                 .arg(cchan).arg(cchan_cnt);
00953         }
00954 
00955         scan_monitor->ScanAppendTextToLog(msg_tr);
00956         LOG(VB_CHANSCAN, LOG_INFO, LOC + msg);
00957 
00958         currentEncryptionStatus.clear();
00959         currentEncryptionStatusChecked.clear();
00960 
00961         setOtherTables = false;
00962         otherTableTime = 0;
00963 
00964         if (scanning)
00965         {
00966             transportsScanned++;
00967             UpdateScanPercentCompleted();
00968             waitingForTables = false;
00969             nextIt = current.nextTransport();
00970         }
00971         else
00972         {
00973             scan_monitor->ScanPercentComplete(100);
00974             scan_monitor->ScanComplete();
00975         }
00976 
00977         return true;
00978     }
00979 
00980     return false;
00981 }
00982 
00983 #define PCM_INFO_INIT(SISTD) \
00984     ChannelInsertInfo &info = pnum_to_dbchan[pnum]; \
00985     info.db_mplexid   = mplexid;   info.source_id    = sourceID;  \
00986     info.service_id   = pnum;      info.freqid       = freqidStr; \
00987     info.si_standard  = SISTD;
00988 
00989 static void update_info(ChannelInsertInfo &info,
00990                         const VirtualChannelTable *vct, uint i)
00991 {
00992     if (vct->ModulationMode(i) == 0x01 /* NTSC Modulation */ ||
00993         vct->ServiceType(i)    == 0x01 /* Analog TV */)
00994     {
00995         info.si_standard = "ntsc";
00996         info.format      = "ntsc";
00997     }
00998 
00999     info.callsign = vct->ShortChannelName(i);
01000 
01001     info.service_name = vct->GetExtendedChannelName(i);
01002     if (info.service_name.isEmpty())
01003         info.service_name = vct->ShortChannelName(i);
01004 
01005     info.chan_num           = QString::null;
01006 
01007     info.service_id         = vct->ProgramNumber(i);
01008     info.atsc_major_channel = vct->MajorChannel(i);
01009     info.atsc_minor_channel = vct->MinorChannel(i);
01010 
01011     info.use_on_air_guide = !vct->IsHidden(i) ||
01012         (vct->IsHidden(i) && !vct->IsHiddenInGuide(i));
01013 
01014     info.hidden           = vct->IsHidden(i);
01015     info.hidden_in_guide  = vct->IsHiddenInGuide(i);
01016 
01017     info.vct_tsid         = vct->TransportStreamID();
01018     info.vct_chan_tsid    = vct->ChannelTransportStreamID(i);
01019     info.is_encrypted    |= vct->IsAccessControlled(i);
01020     info.is_data_service  = vct->ServiceType(i) == 0x04;
01021     info.is_audio_service = vct->ServiceType(i) == 0x03;
01022 
01023     info.in_vct       = true;
01024 }
01025 
01026 static void update_info(ChannelInsertInfo &info,
01027                         const ServiceDescriptionTable *sdt, uint i,
01028                         const QMap<uint64_t, QString> &defAuthorities)
01029 {
01030     // HACK beg -- special exception for this network
01031     //             (dbver == "1067")
01032     bool force_guide_present = (sdt->OriginalNetworkID() == 70);
01033     // HACK end -- special exception for this network
01034 
01035     // Figure out best service name and callsign...
01036     ServiceDescriptor *desc = sdt->GetServiceDescriptor(i);
01037     QString callsign = QString::null;
01038     QString service_name = QString::null;
01039     if (desc)
01040     {
01041         callsign = desc->ServiceShortName();
01042         if (callsign.trimmed().isEmpty())
01043             callsign = QString("%1-%2-%3")
01044                 .arg(ChannelUtil::GetUnknownCallsign()).arg(sdt->TSID())
01045                 .arg(sdt->ServiceID(i));
01046 
01047         service_name = desc->ServiceName();
01048         if (service_name.trimmed().isEmpty())
01049             service_name = QString::null;
01050     }
01051 
01052     if (info.callsign.isEmpty())
01053         info.callsign = callsign;
01054     if (info.service_name.isEmpty())
01055         info.service_name = service_name;
01056 
01057     info.use_on_air_guide =
01058         sdt->HasEITPresentFollowing(i) ||
01059         sdt->HasEITSchedule(i) ||
01060         force_guide_present;
01061 
01062     info.hidden           = false;
01063     info.hidden_in_guide  = false;
01064 
01065     info.is_data_service =
01066         (desc && !desc->IsDTV() && !desc->IsDigitalAudio());
01067     info.is_audio_service = (desc && desc->IsDigitalAudio());
01068     delete desc;
01069 
01070     info.service_id = sdt->ServiceID(i);
01071     info.sdt_tsid   = sdt->TSID();
01072     info.orig_netid = sdt->OriginalNetworkID();
01073     info.in_sdt     = true;
01074 
01075     desc_list_t parsed =
01076         MPEGDescriptor::Parse(sdt->ServiceDescriptors(i),
01077                                 sdt->ServiceDescriptorsLength(i));
01078     // Look for default authority
01079     const unsigned char *def_auth =
01080         MPEGDescriptor::Find(parsed, DescriptorID::default_authority);
01081     if (def_auth)
01082     {
01083         DefaultAuthorityDescriptor authority(def_auth);
01084         LOG(VB_CHANSCAN, LOG_INFO, QString("ChannelScanSM: found default "
01085                                           "authority(SDT) for service %1 %2 %3")
01086                 .arg(info.orig_netid).arg(info.sdt_tsid).arg(info.service_id));
01087         info.default_authority = authority.DefaultAuthority();
01088     }
01089     else
01090     {
01091         uint64_t index = (uint64_t)info.orig_netid << 32 |
01092                         info.sdt_tsid << 16 | info.service_id;
01093         if (defAuthorities.contains(index))
01094             info.default_authority = defAuthorities[index];
01095     }
01096 }
01097 
01098 uint ChannelScanSM::GetCurrentTransportInfo(
01099     QString &cur_chan, QString &cur_chan_tr) const
01100 {
01101     if (current.iter() == scanTransports.end())
01102     {
01103         cur_chan = cur_chan_tr = QString::null;
01104         return 0;
01105     }
01106 
01107     uint max_chan_cnt = 0;
01108 
01109     QMap<uint,ChannelInsertInfo> list = GetChannelList(current, currentInfo);
01110     {
01111         for (int i = 0; i < list.size(); i++)
01112         {
01113             max_chan_cnt +=
01114                 (list[i].in_pat || list[i].in_pmt ||
01115                  list[i].in_sdt || list[i].in_vct) ? 1 : 0;
01116         }
01117     }
01118 
01119     QString offset_str_tr = current.offset() ?
01120         QObject::tr(" offset %2").arg(current.offset()) : "";
01121      cur_chan_tr = QString("%1%2")
01122         .arg((*current).FriendlyName).arg(offset_str_tr);
01123 
01124     QString offset_str = current.offset() ?
01125         QString(" offset %2").arg(current.offset()) : "";
01126     cur_chan = QString("%1%2")
01127         .arg((*current).FriendlyName).arg(offset_str);
01128 
01129     return max_chan_cnt;
01130 }
01131 
01132 QMap<uint,ChannelInsertInfo>
01133 ChannelScanSM::GetChannelList(transport_scan_items_it_t trans_info,
01134                               ScannedChannelInfo *scan_info) const
01135 {
01136     QMap<uint,ChannelInsertInfo> pnum_to_dbchan;
01137 
01138     uint    mplexid   = (*trans_info).mplexid;
01139     int     freqid    = (*trans_info).friendlyNum;
01140     QString freqidStr = (freqid) ? QString::number(freqid) : QString::null;
01141 
01142     // channels.conf
01143     const DTVChannelInfoList &echan = (*trans_info).expectedChannels;
01144     for (uint i = 0; i < echan.size(); i++)
01145     {
01146         uint pnum = echan[i].serviceid;
01147         PCM_INFO_INIT("mpeg");
01148         info.service_name = echan[i].name;
01149         info.in_channels_conf = true;
01150     }
01151 
01152     // PATs
01153     pat_map_t::const_iterator pat_list_it = scan_info->pats.begin();
01154     for (; pat_list_it != scan_info->pats.end(); ++pat_list_it)
01155     {
01156         pat_vec_t::const_iterator pat_it = (*pat_list_it).begin();
01157         for (; pat_it != (*pat_list_it).end(); ++pat_it)
01158         {
01159             bool could_be_opencable = false;
01160             for (uint i = 0; i < (*pat_it)->ProgramCount(); i++)
01161             {
01162                 if (((*pat_it)->ProgramNumber(i) == 0) &&
01163                     ((*pat_it)->ProgramPID(i) == 0x1ffc))
01164                 {
01165                     could_be_opencable = true;
01166                 }
01167             }
01168 
01169             for (uint i = 0; i < (*pat_it)->ProgramCount(); i++)
01170             {
01171                 uint pnum = (*pat_it)->ProgramNumber(i);
01172                 if (pnum)
01173                 {
01174                     PCM_INFO_INIT("mpeg");
01175                     info.pat_tsid = (*pat_it)->TransportStreamID();
01176                     info.could_be_opencable = could_be_opencable;
01177                     info.in_pat = true;
01178                 }
01179             }
01180         }
01181     }
01182 
01183     // PMTs
01184     pmt_vec_t::const_iterator pmt_it = scan_info->pmts.begin();
01185     for (; pmt_it != scan_info->pmts.end(); ++pmt_it)
01186     {
01187         const ProgramMapTable *pmt = *pmt_it;
01188         uint pnum = pmt->ProgramNumber();
01189         PCM_INFO_INIT("mpeg");
01190         for (uint i = 0; i < pmt->StreamCount(); i++)
01191         {
01192             info.could_be_opencable |=
01193                 (StreamID::OpenCableVideo == pmt->StreamType(i));
01194         }
01195 
01196         desc_list_t descs = MPEGDescriptor::ParseOnlyInclude(
01197             pmt->ProgramInfo(), pmt->ProgramInfoLength(),
01198             DescriptorID::registration);
01199 
01200         for (uint i = 0; i < descs.size(); i++)
01201         {
01202             RegistrationDescriptor reg(descs[i]);
01203             if (reg.FormatIdentifierString() == "CUEI" ||
01204                 reg.FormatIdentifierString() == "SCTE")
01205                 info.is_opencable = true;
01206         }
01207 
01208         info.is_encrypted |= pmt->IsEncrypted(GetDTVChannel()->GetSIStandard());
01209         info.in_pmt = true;
01210     }
01211 
01212     // Cable VCTs
01213     cvct_vec_t::const_iterator cvct_it = scan_info->cvcts.begin();
01214     for (; cvct_it != scan_info->cvcts.end(); ++cvct_it)
01215     {
01216         for (uint i = 0; i < (*cvct_it)->ChannelCount(); i++)
01217         {
01218             uint pnum = (*cvct_it)->ProgramNumber(i);
01219             PCM_INFO_INIT("atsc");
01220             update_info(info, *cvct_it, i);
01221         }
01222     }
01223 
01224     // Terrestrial VCTs
01225     tvct_vec_t::const_iterator tvct_it = scan_info->tvcts.begin();
01226     for (; tvct_it != scan_info->tvcts.end(); ++tvct_it)
01227     {
01228         for (uint i = 0; i < (*tvct_it)->ChannelCount(); i++)
01229         {
01230             uint pnum = (*tvct_it)->ProgramNumber(i);
01231             PCM_INFO_INIT("atsc");
01232             update_info(info, *tvct_it, i);
01233         }
01234     }
01235 
01236     // SDTs
01237     sdt_map_t::const_iterator sdt_list_it = scan_info->sdts.begin();
01238     for (; sdt_list_it != scan_info->sdts.end(); ++sdt_list_it)
01239     {
01240         sdt_vec_t::const_iterator sdt_it = (*sdt_list_it).begin();
01241         for (; sdt_it != (*sdt_list_it).end(); ++sdt_it)
01242         {
01243             for (uint i = 0; i < (*sdt_it)->ServiceCount(); i++)
01244             {
01245                 uint pnum = (*sdt_it)->ServiceID(i);
01246                 PCM_INFO_INIT("dvb");
01247                 update_info(info, *sdt_it, i, defAuthorities);
01248             }
01249         }
01250     }
01251 
01252     // NIT
01253     QMap<qlonglong, uint> ukChanNums;
01254     QMap<uint,ChannelInsertInfo>::iterator dbchan_it;
01255     for (dbchan_it = pnum_to_dbchan.begin();
01256          dbchan_it != pnum_to_dbchan.end(); ++dbchan_it)
01257     {
01258         ChannelInsertInfo &info = *dbchan_it;
01259 
01260         // NIT
01261         nit_vec_t::const_iterator nits_it = scan_info->nits.begin();
01262         for (; nits_it != scan_info->nits.end(); ++nits_it)
01263         {
01264             for (uint i = 0; i < (*nits_it)->TransportStreamCount(); i++)
01265             {
01266                 const NetworkInformationTable *nit = (*nits_it);
01267                 if ((nit->TSID(i)              == info.sdt_tsid) &&
01268                     (nit->OriginalNetworkID(i) == info.orig_netid))
01269                 {
01270                     info.netid = nit->NetworkID();
01271                     info.in_nit = true;
01272                 }
01273                 else
01274                 {
01275                     continue;
01276                 }
01277 
01278                 // Get channel numbers from UK Frequency List Descriptors
01279                 const desc_list_t &list =
01280                     MPEGDescriptor::Parse(nit->TransportDescriptors(i),
01281                                           nit->TransportDescriptorsLength(i));
01282 
01283                 const unsigned char *desc =
01284                     MPEGDescriptor::Find(
01285                         list, PrivateDescriptorID::dvb_uk_channel_list);
01286 
01287                 if (desc)
01288                 {
01289                     UKChannelListDescriptor uklist(desc);
01290                     for (uint j = 0; j < uklist.ChannelCount(); j++)
01291                     {
01292                         ukChanNums[((qlonglong)info.orig_netid<<32) |
01293                                    uklist.ServiceID(j)] =
01294                             uklist.ChannelNumber(j);
01295                     }
01296                 }
01297             }
01298         }
01299     }
01300 
01301     // Get UK channel numbers
01302     for (dbchan_it = pnum_to_dbchan.begin();
01303          dbchan_it != pnum_to_dbchan.end(); ++dbchan_it)
01304     {
01305         ChannelInsertInfo &info = *dbchan_it;
01306 
01307         if (!info.chan_num.isEmpty())
01308             continue;
01309 
01310         QMap<qlonglong, uint>::const_iterator it = ukChanNums.find(
01311             ((qlonglong)info.orig_netid<<32) | info.service_id);
01312 
01313         if (it != ukChanNums.end())
01314             info.chan_num = QString::number(*it);
01315     }
01316 
01317     // Get QAM/SCTE/MPEG channel numbers
01318     for (dbchan_it = pnum_to_dbchan.begin();
01319          dbchan_it != pnum_to_dbchan.end(); ++dbchan_it)
01320     {
01321         ChannelInsertInfo &info = *dbchan_it;
01322 
01323         if (!info.chan_num.isEmpty())
01324             continue;
01325 
01326         if ((info.si_standard == "mpeg") ||
01327             (info.si_standard == "scte") ||
01328             (info.si_standard == "opencable"))
01329             info.chan_num = QString("%1-%2")
01330                               .arg(info.freqid)
01331                               .arg(info.service_id);
01332     }
01333 
01334     // Check for decryption success
01335     for (dbchan_it = pnum_to_dbchan.begin();
01336          dbchan_it != pnum_to_dbchan.end(); ++dbchan_it)
01337     {
01338         uint pnum = dbchan_it.key();
01339         ChannelInsertInfo &info = *dbchan_it;
01340         info.decryption_status = scan_info->program_encryption_status[pnum];
01341     }
01342 
01343     return pnum_to_dbchan;
01344 }
01345 
01346 ScanDTVTransportList ChannelScanSM::GetChannelList(void) const
01347 {
01348     ScanDTVTransportList list;
01349 
01350     uint cardid = channel->GetCardID();
01351 
01352     DTVTunerType tuner_type = GuessDTVTunerType(DTVTunerType::kTunerTypeATSC);
01353 
01354     ChannelList::const_iterator it = channelList.begin();
01355     for (; it != channelList.end(); ++it)
01356     {
01357         QMap<uint,ChannelInsertInfo> pnum_to_dbchan =
01358             GetChannelList(it->first, it->second);
01359 
01360         ScanDTVTransport item((*it->first).tuning, tuner_type, cardid);
01361 
01362         QMap<uint,ChannelInsertInfo>::iterator dbchan_it;
01363         for (dbchan_it = pnum_to_dbchan.begin();
01364              dbchan_it != pnum_to_dbchan.end(); ++dbchan_it)
01365         {
01366             item.channels.push_back(*dbchan_it);
01367         }
01368         if (item.channels.size())
01369             list.push_back(item);
01370     }
01371 
01372     return list;
01373 }
01374 
01375 
01376 DTVSignalMonitor* ChannelScanSM::GetDTVSignalMonitor(void)
01377 {
01378     return dynamic_cast<DTVSignalMonitor*>(signalMonitor);
01379 }
01380 
01381 DVBSignalMonitor* ChannelScanSM::GetDVBSignalMonitor(void)
01382 {
01383 #ifdef USING_DVB
01384     return dynamic_cast<DVBSignalMonitor*>(signalMonitor);
01385 #else
01386     return NULL;
01387 #endif
01388 }
01389 
01390 DTVChannel *ChannelScanSM::GetDTVChannel(void)
01391 {
01392     return dynamic_cast<DTVChannel*>(channel);
01393 }
01394 
01395 const DTVChannel *ChannelScanSM::GetDTVChannel(void) const
01396 {
01397     return dynamic_cast<const DTVChannel*>(channel);
01398 }
01399 
01400 HDHRChannel *ChannelScanSM::GetHDHRChannel(void)
01401 {
01402 #ifdef USING_HDHOMERUN
01403     return dynamic_cast<HDHRChannel*>(channel);
01404 #else
01405     return NULL;
01406 #endif
01407 }
01408 
01409 DVBChannel *ChannelScanSM::GetDVBChannel(void)
01410 {
01411 #ifdef USING_DVB
01412     return dynamic_cast<DVBChannel*>(channel);
01413 #else
01414     return NULL;
01415 #endif
01416 }
01417 
01418 const DVBChannel *ChannelScanSM::GetDVBChannel(void) const
01419 {
01420 #ifdef USING_DVB
01421     return dynamic_cast<const DVBChannel*>(channel);
01422 #else
01423     return NULL;
01424 #endif
01425 }
01426 
01427 V4LChannel *ChannelScanSM::GetV4LChannel(void)
01428 {
01429 #ifdef USING_V4L2
01430     return dynamic_cast<V4LChannel*>(channel);
01431 #else
01432     return NULL;
01433 #endif
01434 }
01435 
01439 void ChannelScanSM::StartScanner(void)
01440 {
01441     while (scannerThread)
01442     {
01443         threadExit = true;
01444         if (scannerThread->wait(1000))
01445         {
01446             delete scannerThread;
01447             scannerThread = NULL;
01448         }
01449     }
01450     threadExit = false;
01451     scannerThread = new MThread("Scanner", this);
01452     scannerThread->start();
01453 }
01454 
01458 void ChannelScanSM::run(void)
01459 {
01460     LOG(VB_CHANSCAN, LOG_INFO, LOC + "run -- begin");
01461 
01462     while (!threadExit)
01463     {
01464         if (scanning)
01465             HandleActiveScan();
01466 
01467         usleep(10 * 1000);
01468     }
01469 
01470     LOG(VB_CHANSCAN, LOG_INFO, LOC + "run -- end");
01471 }
01472 
01473 // See if we have timed out
01474 bool ChannelScanSM::HasTimedOut(void)
01475 {
01476     if (currentTestingDecryption &&
01477         (timer.elapsed() > (int)kDecryptionTimeout))
01478     {
01479         currentTestingDecryption = false;
01480         return true;
01481     }
01482 
01483     if (!waitingForTables)
01484         return true;
01485 
01486 #ifdef USING_DVB
01487     // If the rotor is still moving, reset the timer and keep waiting
01488     DVBSignalMonitor *sigmon = GetDVBSignalMonitor();
01489     if (sigmon)
01490     {
01491         const DiSEqCDevRotor *rotor = GetDVBChannel()->GetRotor();
01492         if (rotor)
01493         {
01494             bool was_moving, is_moving;
01495             sigmon->GetRotorStatus(was_moving, is_moving);
01496             if (was_moving && !is_moving)
01497             {
01498                 timer.restart();
01499                 return false;
01500             }
01501         }
01502     }
01503 #endif // USING_DVB
01504 
01505 
01506     // have the tables have timed out?
01507     if (timer.elapsed() > (int)channelTimeout)
01508     {
01509         // the channelTimeout alone is only valid if we have seen no tables..
01510         const ScanStreamData *sd = NULL;
01511         if (GetDTVSignalMonitor())
01512             sd = GetDTVSignalMonitor()->GetScanStreamData();
01513 
01514         if (!sd)
01515             return true;
01516 
01517         if (sd->HasCachedAnyNIT() || sd->HasCachedAnySDTs())
01518             return timer.elapsed() > (int) kDVBTableTimeout;
01519         if (sd->HasCachedMGT() || sd->HasCachedAnyVCTs())
01520             return timer.elapsed() > (int) kATSCTableTimeout;
01521         if (sd->HasCachedAnyPAT() || sd->HasCachedAnyPMTs())
01522             return timer.elapsed() > (int) kMPEGTableTimeout;
01523 
01524         return true;
01525     }
01526 
01527     // ok the tables haven't timed out, but have we hit the signal timeout?
01528     SignalMonitor *sm = GetSignalMonitor();
01529     if ((timer.elapsed() > (int)(*current).timeoutTune) &&
01530         sm && !sm->HasSignalLock())
01531     {
01532         const ScanStreamData *sd = NULL;
01533         if (GetDTVSignalMonitor())
01534             sd = GetDTVSignalMonitor()->GetScanStreamData();
01535 
01536         if (!sd)
01537             return true;
01538 
01539         // Just is case we temporarily lose the signal after we've seen
01540         // tables...
01541         if (!sd->HasCachedAnyPAT() && !sd->HasCachedAnyPMTs() &&
01542             !sd->HasCachedMGT()    && !sd->HasCachedAnyVCTs() &&
01543             !sd->HasCachedAnyNIT() && !sd->HasCachedAnySDTs())
01544         {
01545             return true;
01546         }
01547     }
01548 
01549     return false;
01550 }
01551 
01555 void ChannelScanSM::HandleActiveScan(void)
01556 {
01557     QMutexLocker locker(&lock);
01558 
01559     bool do_post_insertion = waitingForTables;
01560 
01561     if (!HasTimedOut())
01562         return;
01563 
01564     if (0 == nextIt.offset() && nextIt != scanTransports.begin())
01565     {
01566         // Add channel to scanned list and potentially check decryption
01567         if (do_post_insertion && !UpdateChannelInfo(false))
01568             return;
01569 
01570         // Stop signal monitor for previous transport
01571         locker.unlock();
01572         signalMonitor->Stop();
01573         locker.relock();
01574     }
01575 
01576     if (0 == nextIt.offset() && nextIt == scanTransports.begin())
01577     {
01578         channelList.clear();
01579         channelsFound = 0;
01580     }
01581 
01582     current = nextIt; // Increment current
01583 
01584     if (current != scanTransports.end())
01585     {
01586         ScanTransport(current);
01587 
01588         // Increment nextIt
01589         nextIt = current;
01590         ++nextIt;
01591     }
01592     else if (!extend_transports.isEmpty())
01593     {
01594         --current;
01595         QMap<uint32_t,DTVMultiplex>::iterator it = extend_transports.begin();
01596         while (it != extend_transports.end())
01597         {
01598             if (!ts_scanned.contains(it.key()))
01599             {
01600                 QString name = QString("TransportID %1").arg(it.key() & 0xffff);
01601                 TransportScanItem item(sourceID, name, *it, signalTimeout);
01602                 LOG(VB_CHANSCAN, LOG_INFO, LOC + "Adding " + name + " - " +
01603                     item.
01604 tuning.toString());
01605                 scanTransports.push_back(item);
01606                 ts_scanned.insert(it.key());
01607             }
01608             ++it;
01609         }
01610         extend_transports.clear();
01611         nextIt = current;
01612         ++nextIt;
01613     }
01614     else
01615     {
01616         scan_monitor->ScanComplete();
01617         scanning = false;
01618         current = nextIt = scanTransports.end();
01619     }
01620 }
01621 
01622 bool ChannelScanSM::Tune(const transport_scan_items_it_t transport)
01623 {
01624     const TransportScanItem &item = *transport;
01625 
01626 #ifdef USING_DVB
01627     if (GetDVBSignalMonitor())
01628     {
01629         // always wait for rotor to finish
01630         GetDVBSignalMonitor()->AddFlags(SignalMonitor::kDVBSigMon_WaitForPos);
01631         GetDVBSignalMonitor()->SetRotorTarget(1.0f);
01632     }
01633 #endif // USING_DVB
01634 
01635     if (!GetDTVChannel())
01636         return false;
01637 
01638     if (item.mplexid > 0 && transport.offset() == 0)
01639         return GetDTVChannel()->TuneMultiplex(item.mplexid, inputname);
01640 
01641     const uint64_t freq = item.freq_offset(transport.offset());
01642     DTVMultiplex tuning = item.tuning;
01643     tuning.frequency = freq;
01644     return GetDTVChannel()->Tune(tuning, inputname);
01645 }
01646 
01647 void ChannelScanSM::ScanTransport(const transport_scan_items_it_t transport)
01648 {
01649     QString offset_str = (transport.offset()) ?
01650         QObject::tr(" offset %2").arg(transport.offset()) : "";
01651     QString cur_chan = QString("%1%2")
01652         .arg((*current).FriendlyName).arg(offset_str);
01653     QString tune_msg_str =
01654         QObject::tr("Tuning to %1 mplexid(%2)")
01655         .arg(cur_chan).arg((*current).mplexid);
01656 
01657     const TransportScanItem &item = *transport;
01658 
01659     if (transport.offset() &&
01660         (item.freq_offset(transport.offset()) == item.freq_offset(0)))
01661     {
01662         waitingForTables = false;
01663         return; // nothing to do
01664     }
01665 
01666     if (channelsFound)
01667     {
01668         QString progress = QObject::tr(": Found %n", "", channelsFound);
01669         scan_monitor->ScanUpdateStatusTitleText(progress);
01670     }
01671 
01672     scan_monitor->ScanUpdateStatusText(cur_chan);
01673     LOG(VB_CHANSCAN, LOG_INFO, LOC + tune_msg_str);
01674 
01675     if (!Tune(transport))
01676     {   // If we did not tune successfully, bail with message
01677         UpdateScanPercentCompleted();
01678         LOG(VB_CHANSCAN, LOG_ERR, LOC +
01679             QString("Failed to tune %1 mplexid(%2) at offset %3")
01680                 .arg(item.FriendlyName).arg(item.mplexid)
01681                 .arg(transport.offset()));
01682         return;
01683     }
01684 
01685     // If we have a DTV Signal Monitor, perform table scanner reset
01686     if (GetDTVSignalMonitor() && GetDTVSignalMonitor()->GetScanStreamData())
01687     {
01688         GetDTVSignalMonitor()->GetScanStreamData()->Reset();
01689         GetDTVSignalMonitor()->SetChannel(-1,-1);
01690     }
01691 
01692     // Start signal monitor for this channel
01693     signalMonitor->Start();
01694 
01695     timer.start();
01696     waitingForTables = (item.tuning.sistandard != "analog");
01697 }
01698 
01703 void ChannelScanSM::StopScanner(void)
01704 {
01705     LOG(VB_CHANSCAN, LOG_INFO, LOC + "StopScanner");
01706 
01707     while (scannerThread)
01708     {
01709         threadExit = true;
01710         if (scannerThread->wait(1000))
01711         {
01712             delete scannerThread;
01713             scannerThread = NULL;
01714         }
01715     }
01716 
01717     if (signalMonitor)
01718         signalMonitor->Stop();
01719 }
01720 
01725 bool ChannelScanSM::ScanTransports(
01726     int SourceID,
01727     const QString &std,
01728     const QString &modulation,
01729     const QString &country,
01730     const QString &table_start,
01731     const QString &table_end)
01732 {
01733     QString name("");
01734     if (scanning)
01735         return false;
01736 
01737     scanTransports.clear();
01738     nextIt = scanTransports.end();
01739 
01740     freq_table_list_t tables =
01741         get_matching_freq_tables(std, modulation, country);
01742 
01743     if (tables.size() == 0)
01744     {
01745         QString msg = QString("No freq table for (%1, %2, %3) found")
01746                       .arg(std).arg(modulation).arg(country);
01747         scan_monitor->ScanAppendTextToLog(msg);
01748     }
01749     LOG(VB_CHANSCAN, LOG_INFO, LOC +
01750         QString("Looked up freq table (%1, %2, %3) w/%4 entries")
01751             .arg(std).arg(modulation).arg(country).arg(tables.size()));
01752 
01753     QString start = table_start;
01754     QString end   = table_end;
01755     freq_table_list_t::iterator it = tables.begin();
01756     for (; it != tables.end(); ++it)
01757     {
01758         const FrequencyTable &ft = **it;
01759         int     name_num         = ft.name_offset;
01760         QString strNameFormat    = ft.name_format;
01761         uint    freq             = ft.frequencyStart;
01762         while (freq <= ft.frequencyEnd)
01763         {
01764             name = strNameFormat;
01765             if (strNameFormat.indexOf("%") >= 0)
01766                 name = strNameFormat.arg(name_num);
01767 
01768             if (start.isEmpty() || name == start)
01769             {
01770                 start = QString::null;
01771 
01772                 TransportScanItem item(SourceID, std, name, name_num,
01773                                        freq, ft, signalTimeout);
01774                 scanTransports.push_back(item);
01775 
01776                 LOG(VB_CHANSCAN, LOG_INFO, LOC + item.toString());
01777             }
01778 
01779             name_num++;
01780             freq += ft.frequencyStep;
01781 
01782             if (!end.isEmpty() && name == end)
01783                 break;
01784         }
01785         if (!end.isEmpty() && name == end)
01786             break;
01787     }
01788 
01789     while (!tables.empty())
01790     {
01791         delete tables.back();
01792         tables.pop_back();
01793     }
01794 
01795     extend_scan_list = true;
01796     timer.start();
01797     waitingForTables = false;
01798 
01799     nextIt            = scanTransports.begin();
01800     transportsScanned = 0;
01801     scanning          = true;
01802 
01803     return true;
01804 }
01805 
01806 bool ChannelScanSM::ScanForChannels(uint sourceid,
01807                              const QString &std,
01808                              const QString &cardtype,
01809                              const DTVChannelList &channels)
01810 {
01811     scanTransports.clear();
01812     nextIt = scanTransports.end();
01813 
01814     DTVTunerType tunertype;
01815     tunertype.Parse(cardtype);
01816 
01817     DTVChannelList::const_iterator it = channels.begin();
01818     for (uint i = 0; it != channels.end(); ++it, i++)
01819     {
01820         DTVTransport tmp = *it;
01821         tmp.sistandard = std;
01822         TransportScanItem item(sourceid, QString::number(i),
01823                                tunertype, tmp, signalTimeout);
01824 
01825         scanTransports.push_back(item);
01826 
01827         LOG(VB_CHANSCAN, LOG_INFO, LOC + item.toString());
01828     }
01829 
01830     if (scanTransports.empty())
01831     {
01832         LOG(VB_GENERAL, LOG_ERR, LOC + "ScanForChannels() no transports");
01833         return false;
01834     }
01835 
01836     timer.start();
01837     waitingForTables = false;
01838 
01839     nextIt            = scanTransports.begin();
01840     transportsScanned = 0;
01841     scanning          = true;
01842 
01843     return true;
01844 }
01845 
01850 bool ChannelScanSM::ScanTransportsStartingOn(
01851     int sourceid, const QMap<QString,QString> &startChan)
01852 {
01853     QMap<QString,QString>::const_iterator it;
01854 
01855     if (startChan.find("std")        == startChan.end() ||
01856         startChan.find("type")       == startChan.end())
01857     {
01858         return false;
01859     }
01860 
01861     QString std    = *startChan.find("std");
01862     QString si_std = (std.toLower() != "atsc") ? "dvb" : "atsc";
01863     bool    ok     = false;
01864 
01865     if (scanning)
01866         return false;
01867 
01868     scanTransports.clear();
01869     nextIt = scanTransports.end();
01870 
01871     DTVMultiplex tuning;
01872 
01873     DTVTunerType type;
01874     ok = type.Parse(startChan["type"]);
01875 
01876     if (ok)
01877     {
01878         ok = tuning.ParseTuningParams(
01879             type,
01880             startChan["frequency"],      startChan["inversion"],
01881             startChan["symbolrate"],     startChan["fec"],
01882             startChan["polarity"],
01883             startChan["coderate_hp"],    startChan["coderate_lp"],
01884             startChan["constellation"],  startChan["trans_mode"],
01885             startChan["guard_interval"], startChan["hierarchy"],
01886             startChan["modulation"],     startChan["bandwidth"],
01887             startChan["mod_sys"],        startChan["rolloff"]);
01888     }
01889 
01890     if (ok)
01891     {
01892         tuning.sistandard = si_std;
01893         TransportScanItem item(
01894             sourceid, QObject::tr("Frequency %1").arg(startChan["frequency"]),
01895             tuning, signalTimeout);
01896         scanTransports.push_back(item);
01897     }
01898 
01899     if (!ok)
01900         return false;
01901 
01902     extend_scan_list = true;
01903 
01904     timer.start();
01905     waitingForTables = false;
01906 
01907     nextIt            = scanTransports.begin();
01908     transportsScanned = 0;
01909     scanning          = true;
01910 
01911     return true;
01912 }
01913 
01914 bool ChannelScanSM::AddToList(uint mplexid)
01915 {
01916     MSqlQuery query(MSqlQuery::InitCon());
01917     query.prepare(
01918         "SELECT sourceid, sistandard, transportid, frequency, modulation "
01919         "FROM dtv_multiplex "
01920         "WHERE mplexid = :MPLEXID");
01921     query.bindValue(":MPLEXID", mplexid);
01922     if (!query.exec())
01923     {
01924         MythDB::DBError("ChannelScanSM::AddToList()", query);
01925         return false;
01926     }
01927 
01928     if (!query.next())
01929     {
01930         LOG(VB_GENERAL, LOG_ERR, LOC + "AddToList() " +
01931                 QString("Failed to locate mplexid(%1) in DB").arg(mplexid));
01932         return false;
01933     }
01934 
01935     uint    sourceid   = query.value(0).toUInt();
01936     QString sistandard = query.value(1).toString();
01937     uint    tsid       = query.value(2).toUInt();
01938     DTVTunerType tt = DTVTunerType::kTunerTypeUnknown;
01939 
01940     QString fn = (tsid) ? QString("Transport ID %1").arg(tsid) :
01941         QString("Multiplex #%1").arg(mplexid);
01942 
01943     if (query.value(4).toString() == "8vsb")
01944     {
01945         QString chan = QString("%1 Hz").arg(query.value(3).toInt());
01946         struct CHANLIST *curList = chanlists[0].list;
01947         int totalChannels = chanlists[0].count;
01948         int findFrequency = (query.value(3).toInt() / 1000) - 1750;
01949         for (int x = 0 ; x < totalChannels ; x++)
01950         {
01951             if ((curList[x].freq <= findFrequency + 200) &&
01952                 (curList[x].freq >= findFrequency - 200))
01953             {
01954                 chan = QString("%1").arg(curList[x].name);
01955             }
01956         }
01957         fn = QObject::tr("ATSC Channel %1").arg(chan);
01958         tt = DTVTunerType::kTunerTypeATSC;
01959     }
01960 
01961     tt = GuessDTVTunerType(tt);
01962 
01963     TransportScanItem item(sourceid, sistandard, fn, mplexid, signalTimeout);
01964 
01965     if (item.tuning.FillFromDB(tt, mplexid))
01966     {
01967         LOG(VB_CHANSCAN, LOG_INFO, LOC + "Adding " + fn);
01968         scanTransports.push_back(item);
01969         return true;
01970     }
01971 
01972     LOG(VB_CHANSCAN, LOG_INFO, LOC + "Not adding incomplete transport " + fn);
01973     return false;
01974 }
01975 
01976 bool ChannelScanSM::ScanTransport(uint mplexid, bool follow_nit)
01977 {
01978     scanTransports.clear();
01979     nextIt = scanTransports.end();
01980 
01981     AddToList(mplexid);
01982 
01983     timer.start();
01984     waitingForTables  = false;
01985 
01986     extend_scan_list = follow_nit;
01987     transportsScanned = 0;
01988     if (scanTransports.size())
01989     {
01990         nextIt   = scanTransports.begin();
01991         scanning = true;
01992         return true;
01993     }
01994 
01995     return false;
01996 }
01997 
01998 bool ChannelScanSM::ScanCurrentTransport(const QString &sistandard)
01999 {
02000     scanTransports.clear();
02001     nextIt = scanTransports.end();
02002 
02003     signalTimeout = 30000;
02004     QString name;
02005     TransportScanItem item(sourceID, sistandard, name, 0, signalTimeout);
02006     scanTransports.push_back(item);
02007 
02008     timer.start();
02009     waitingForTables = false;
02010     extend_scan_list = false;
02011     transportsScanned = 0;
02012     nextIt   = scanTransports.begin();
02013     scanning = true;
02014     return true;
02015 }
02016 
02020 bool ChannelScanSM::CheckImportedList(
02021     const DTVChannelInfoList &channels,
02022     uint mpeg_program_num,
02023     QString &service_name,
02024     QString &callsign,
02025     QString &common_status_info)
02026 {
02027     if (channels.empty())
02028         return true;
02029 
02030     bool found = false;
02031     for (uint i = 0; i < channels.size(); i++)
02032     {
02033         LOG(VB_GENERAL, LOG_DEBUG, LOC +
02034             QString("comparing %1 %2 against %3 %4")
02035                 .arg(channels[i].serviceid).arg(channels[i].name)
02036                 .arg(mpeg_program_num).arg(common_status_info));
02037 
02038         if (channels[i].serviceid == mpeg_program_num)
02039         {
02040             found = true;
02041             if (!channels[i].name.isEmpty())
02042             {
02043                 service_name = channels[i].name; service_name.detach();
02044                 callsign     = channels[i].name; callsign.detach();
02045             }
02046         }
02047     }
02048 
02049     if (found)
02050     {
02051         common_status_info += QString(" %1 %2")
02052             .arg(QObject::tr("as")).arg(service_name);
02053     }
02054     else
02055     {
02056         scan_monitor->ScanAppendTextToLog(
02057             QObject::tr("Skipping %1, not in imported channel map")
02058             .arg(common_status_info));
02059     }
02060 
02061     return found;
02062 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends