MythTV  0.26-pre
channelscanner.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 #include <algorithm>
00031 using namespace std;
00032 
00033 #include "analogsignalmonitor.h"
00034 #include "iptvchannelfetcher.h"
00035 #include "dvbsignalmonitor.h"
00036 #include "scanwizardconfig.h"
00037 #include "channelscan_sm.h"
00038 #include "channelscanner.h"
00039 #include "hdhrchannel.h"
00040 #include "scanmonitor.h"
00041 #include "asichannel.h"
00042 #include "dvbchannel.h"
00043 #include "v4lchannel.h"
00044 #include "cardutil.h"
00045 
00046 #define LOC QString("ChScan: ")
00047 
00048 ChannelScanner::ChannelScanner() :
00049     scanMonitor(NULL), channel(NULL), sigmonScanner(NULL), freeboxScanner(NULL),
00050     freeToAirOnly(false), serviceRequirements(kRequireAV)
00051 {
00052 }
00053 
00054 ChannelScanner::~ChannelScanner()
00055 {
00056     Teardown();
00057 
00058     if (scanMonitor)
00059     {
00060         scanMonitor->deleteLater();
00061         scanMonitor = NULL;
00062     }
00063 }
00064 
00065 void ChannelScanner::Teardown(void)
00066 {
00067     if (sigmonScanner)
00068     {
00069         delete sigmonScanner;
00070         sigmonScanner = NULL;
00071     }
00072 
00073     if (channel)
00074     {
00075         delete channel;
00076         channel = NULL;
00077     }
00078 
00079 #ifdef USING_IPTV
00080     if (freeboxScanner)
00081     {
00082         freeboxScanner->Stop();
00083         delete freeboxScanner;
00084         freeboxScanner = NULL;
00085     }
00086 #endif // USING_IPTV
00087 
00088     if (scanMonitor)
00089     {
00090         scanMonitor->deleteLater();
00091         scanMonitor = NULL;
00092     }
00093 }
00094 
00095 // full scan of existing transports broken
00096 // existing transport scan broken
00097 void ChannelScanner::Scan(
00098     int            scantype,
00099     uint           cardid,
00100     const QString &inputname,
00101     uint           sourceid,
00102     bool           do_ignore_signal_timeout,
00103     bool           do_follow_nit,
00104     bool           do_test_decryption,
00105     bool           do_fta_only,
00106     ServiceRequirements service_requirements,
00107     // stuff needed for particular scans
00108     uint           mplexid /* TransportScan */,
00109     const QMap<QString,QString> &startChan /* NITAddScan */,
00110     const QString &freq_std /* FullScan */,
00111     const QString &mod /* FullScan */,
00112     const QString &tbl /* FullScan */,
00113     const QString &tbl_start /* FullScan optional */,
00114     const QString &tbl_end   /* FullScan optional */)
00115 {
00116     freeToAirOnly = do_fta_only;
00117     serviceRequirements = service_requirements;
00118 
00119     PreScanCommon(scantype, cardid, inputname,
00120                   sourceid, do_ignore_signal_timeout, do_test_decryption);
00121 
00122     LOG(VB_CHANSCAN, LOG_INFO, LOC + "Scan()");
00123 
00124     if (!sigmonScanner)
00125     {
00126         LOG(VB_CHANSCAN, LOG_ERR, LOC + "Scan(): scanner does not exist...");
00127         return;
00128     }
00129 
00130     sigmonScanner->StartScanner();
00131     scanMonitor->ScanUpdateStatusText("");
00132 
00133     bool ok = false;
00134 
00135     if ((ScanTypeSetting::FullScan_ATSC   == scantype) ||
00136         (ScanTypeSetting::FullScan_DVBC   == scantype) ||
00137         (ScanTypeSetting::FullScan_DVBT   == scantype) ||
00138         (ScanTypeSetting::FullScan_Analog == scantype))
00139     {
00140         LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("ScanTransports(%1, %2, %3)")
00141                 .arg(freq_std).arg(mod).arg(tbl));
00142 
00143         // HACK HACK HACK -- begin
00144         // if using QAM we may need additional time... (at least with HD-3000)
00145         if ((mod.left(3).toLower() == "qam") &&
00146             (sigmonScanner->GetSignalTimeout() < 1000))
00147         {
00148             sigmonScanner->SetSignalTimeout(1000);
00149         }
00150         // HACK HACK HACK -- end
00151 
00152         sigmonScanner->SetAnalog(ScanTypeSetting::FullScan_Analog == scantype);
00153 
00154         ok = sigmonScanner->ScanTransports(
00155             sourceid, freq_std, mod, tbl, tbl_start, tbl_end);
00156     }
00157     else if ((ScanTypeSetting::NITAddScan_DVBT  == scantype) ||
00158              (ScanTypeSetting::NITAddScan_DVBS  == scantype) ||
00159              (ScanTypeSetting::NITAddScan_DVBS2 == scantype) ||
00160              (ScanTypeSetting::NITAddScan_DVBC  == scantype))
00161     {
00162         LOG(VB_CHANSCAN, LOG_INFO, LOC + "ScanTransports()");
00163 
00164         ok = sigmonScanner->ScanTransportsStartingOn(sourceid, startChan);
00165     }
00166     else if (ScanTypeSetting::FullTransportScan == scantype)
00167     {
00168         LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("ScanExistingTransports(%1)")
00169                 .arg(sourceid));
00170 
00171         ok = sigmonScanner->ScanExistingTransports(sourceid, do_follow_nit);
00172         if (ok)
00173         {
00174             scanMonitor->ScanPercentComplete(0);
00175         }
00176         else
00177         {
00178             InformUser(QObject::tr("Error tuning to transport"));
00179             Teardown();
00180         }
00181     }
00182     else if ((ScanTypeSetting::DVBUtilsImport == scantype) && channels.size())
00183     {
00184         ok = true;
00185 
00186         LOG(VB_CHANSCAN, LOG_INFO, LOC +
00187             QString("ScanForChannels(%1)").arg(sourceid));
00188 
00189         QString card_type = CardUtil::GetRawCardType(cardid);
00190         QString sub_type  = card_type;
00191         if (card_type == "DVB")
00192         {
00193             QString device = CardUtil::GetVideoDevice(cardid);
00194 
00195             ok = !device.isEmpty();
00196             if (ok)
00197                 sub_type = CardUtil::ProbeDVBType(device).toUpper();
00198         }
00199 
00200         if (ok)
00201         {
00202             ok = sigmonScanner->ScanForChannels(sourceid, freq_std,
00203                                           sub_type, channels);
00204         }
00205         if (ok)
00206         {
00207             scanMonitor->ScanPercentComplete(0);
00208         }
00209         else
00210         {
00211             InformUser(QObject::tr("Error tuning to transport"));
00212             Teardown();
00213         }
00214     }
00215     else if (ScanTypeSetting::TransportScan == scantype)
00216     {
00217         LOG(VB_CHANSCAN, LOG_INFO, LOC +
00218             QString("ScanTransport(%1)").arg(mplexid));
00219 
00220         ok = sigmonScanner->ScanTransport(mplexid, do_follow_nit);
00221     }
00222     else if (ScanTypeSetting::CurrentTransportScan == scantype)
00223     {
00224         QString sistandard = "mpeg";
00225         LOG(VB_CHANSCAN, LOG_INFO, LOC +
00226             "ScanCurrentTransport(" + sistandard + ")");
00227         ok = sigmonScanner->ScanCurrentTransport(sistandard);
00228     }
00229 
00230     if (!ok)
00231     {
00232         LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to handle tune complete.");
00233         InformUser(QObject::tr("Programmer Error: "
00234                                "Failed to handle tune complete."));
00235     }
00236 }
00237 
00238 DTVConfParser::return_t ChannelScanner::ImportDVBUtils(
00239     uint sourceid, int cardtype, const QString &file)
00240 {
00241     channels.clear();
00242 
00243     DTVConfParser::cardtype_t type = DTVConfParser::UNKNOWN;
00244     type = (CardUtil::DVBT == cardtype) ? DTVConfParser::OFDM : type;
00245     type = (CardUtil::QPSK == cardtype) ? DTVConfParser::QPSK : type;
00246     type = (CardUtil::DVBC == cardtype) ? DTVConfParser::QAM  : type;
00247     type = (CardUtil::DVBS2 == cardtype) ? DTVConfParser::DVBS2 : type;
00248     type = ((CardUtil::ATSC == cardtype) ||
00249             (CardUtil::HDHOMERUN == cardtype)) ? DTVConfParser::ATSC : type;
00250 
00251     DTVConfParser::return_t ret = DTVConfParser::OK;
00252     if (type == DTVConfParser::UNKNOWN)
00253         ret = DTVConfParser::ERROR_CARDTYPE;
00254     else
00255     {
00256         DTVConfParser parser(type, sourceid, file);
00257 
00258         ret = parser.Parse();
00259         if (DTVConfParser::OK == ret)
00260             channels = parser.GetChannels();
00261     }
00262 
00263     if (DTVConfParser::OK != ret)
00264     {
00265         QString msg = (DTVConfParser::ERROR_PARSE == ret) ?
00266             QObject::tr("Failed to parse '%1'") :
00267             ((DTVConfParser::ERROR_CARDTYPE == ret) ?
00268              QString("Programmer Error : incorrect card type") :
00269              QObject::tr("Failed to open '%1'"));
00270 
00271         InformUser(msg);
00272     }
00273 
00274     return ret;
00275 }
00276 
00277 bool ChannelScanner::ImportM3U(
00278     uint cardid, const QString &inputname, uint sourceid)
00279 {
00280     (void) cardid;
00281     (void) inputname;
00282     (void) sourceid;
00283     bool ok = false;
00284 
00285 #ifdef USING_IPTV
00286     // Create an IPTV scan object
00287     freeboxScanner = new IPTVChannelFetcher(
00288         cardid, inputname, sourceid, scanMonitor);
00289 
00290     MonitorProgress(false, false, false, false);
00291 
00292     ok = freeboxScanner->Scan();
00293 #endif // USING_IPTV
00294 
00295     if (!ok)
00296         InformUser(QObject::tr("Error starting scan"));
00297 
00298     return ok;
00299 }
00300 
00301 void ChannelScanner::PreScanCommon(
00302     int scantype,
00303     uint cardid,
00304     const QString &inputname,
00305     uint sourceid,
00306     bool do_ignore_signal_timeout,
00307     bool do_test_decryption)
00308 {
00309     uint signal_timeout  = 1000;
00310     uint channel_timeout = 40000;
00311     CardUtil::GetTimeouts(cardid, signal_timeout, channel_timeout);
00312 
00313     QString device = CardUtil::GetVideoDevice(cardid);
00314     if (device.isEmpty())
00315     {
00316         LOG(VB_GENERAL, LOG_ERR, LOC + "No Device");
00317         InformUser(QObject::tr("Programmer Error: No Device"));
00318         return;
00319     }
00320 
00321     if (!scanMonitor)
00322         scanMonitor = new ScanMonitor(this);
00323 
00324     QString card_type = CardUtil::GetRawCardType(cardid);
00325 
00326     if ("DVB" == card_type)
00327     {
00328         QString sub_type = CardUtil::ProbeDVBType(device).toUpper();
00329         bool need_nit = (("QAM"  == sub_type) ||
00330                          ("QPSK" == sub_type) ||
00331                          ("OFDM" == sub_type));
00332 
00333         // Ugh, Some DVB drivers don't fully support signal monitoring...
00334         if ((ScanTypeSetting::TransportScan     == scantype) ||
00335             (ScanTypeSetting::FullTransportScan == scantype))
00336         {
00337             signal_timeout = (do_ignore_signal_timeout) ?
00338                 channel_timeout * 10 : signal_timeout;
00339         }
00340 
00341         // ensure a minimal signal timeout of 1 second
00342         signal_timeout = max(signal_timeout, 1000U);
00343 
00344         // Make sure that channel_timeout is at least 7 seconds to catch
00345         // at least one SDT section. kDVBTableTimeout in ChannelScanSM
00346         // ensures that we catch the NIT then.
00347         channel_timeout = max(channel_timeout, need_nit * 7 * 1000U);
00348     }
00349 
00350 #ifdef USING_DVB
00351     if ("DVB" == card_type)
00352         channel = new DVBChannel(device);
00353 #endif
00354 
00355 #ifdef USING_V4L2
00356     if (("V4L" == card_type) || ("MPEG" == card_type))
00357         channel = new V4LChannel(NULL, device);
00358 #endif
00359 
00360 #ifdef USING_HDHOMERUN
00361     if ("HDHOMERUN" == card_type)
00362     {
00363         channel = new HDHRChannel(NULL, device);
00364     }
00365 #endif // USING_HDHOMERUN
00366 
00367 #ifdef USING_ASI
00368     if ("ASI" == card_type)
00369     {
00370         channel = new ASIChannel(NULL, device);
00371     }
00372 #endif // USING_ASI
00373 
00374     if (!channel)
00375     {
00376         LOG(VB_GENERAL, LOG_ERR, LOC + "Channel not created");
00377         InformUser(QObject::tr("Programmer Error: Channel not created"));
00378         return;
00379     }
00380 
00381     // explicitly set the cardid
00382     channel->SetCardID(cardid);
00383 
00384     // If the backend is running this may fail...
00385     if (!channel->Open())
00386     {
00387         LOG(VB_GENERAL, LOG_ERR, LOC + "Channel could not be opened");
00388         InformUser(QObject::tr("Channel could not be opened."));
00389         return;
00390     }
00391 
00392     ScanMonitor *lis = scanMonitor;
00393 
00394     sigmonScanner = new ChannelScanSM(
00395         lis, card_type, channel, sourceid,
00396         signal_timeout, channel_timeout, inputname,
00397         do_test_decryption);
00398 
00399     // If we know the channel types we can give the signal montior a hint.
00400     // Since we unfortunately do not record this info in the DB, we cannot
00401     // do this for the other scan types and have to guess later on...
00402     switch (scantype)
00403     {
00404         case ScanTypeSetting::FullScan_ATSC:
00405             sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeATSC);
00406             break;
00407         case ScanTypeSetting::FullScan_DVBC:
00408             sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBC);
00409             break;
00410         case ScanTypeSetting::FullScan_DVBT:
00411             sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBT);
00412             break;
00413         case ScanTypeSetting::NITAddScan_DVBT:
00414             sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBT);
00415             break;
00416         case ScanTypeSetting::NITAddScan_DVBS:
00417             sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBS1);
00418             break;
00419         case ScanTypeSetting::NITAddScan_DVBS2:
00420             sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBS2);
00421             break;
00422         case ScanTypeSetting::NITAddScan_DVBC:
00423             sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBC);
00424             break;
00425         default:
00426             break;
00427     }
00428 
00429     // Signal Meters are connected here
00430     SignalMonitor *mon = sigmonScanner->GetSignalMonitor();
00431     if (mon)
00432         mon->AddListener(lis);
00433 
00434     DVBSignalMonitor *dvbm = NULL;
00435     bool using_rotor = false;
00436 
00437 #ifdef USING_DVB
00438     dvbm = sigmonScanner->GetDVBSignalMonitor();
00439     if (dvbm && mon)
00440         using_rotor = mon->HasFlags(SignalMonitor::kDVBSigMon_WaitForPos);
00441 #endif // USING_DVB
00442 
00443     MonitorProgress(mon, mon, dvbm, using_rotor);
00444 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends