Ticket #1704: fb-scan-v1.patch

File fb-scan-v1.patch, 19.5 KB (added by danielk, 14 years ago)

Basic "Channel Scanner" for freebox, this only downloads the channels, no verification is done.

  • libs/libmythtv/cardutil.h

     
    8484        FIREWIRE,
    8585        HDHOMERUN,
    8686        CRC_IP,
     87        FREEBOX,
    8788    };
    8889
    8990    static enum CARD_TYPES toCardType(const QString &name)
     
    114115            return HDHOMERUN;
    115116        if ("CRC_IP" == name)
    116117            return CRC_IP;
     118        if ("FREEBOX" == name)
     119            return FREEBOX;
    117120        return ERROR_UNKNOWN;
    118121    }
    119122
     
    134137    static bool         IsCardTypePresent(const QString &strType);
    135138
    136139    static QString      GetRawCardType(uint cardid, uint sourceid)
    137         { return get_on_source("cardtype", cardid, sourceid); }
     140        { return get_on_source("cardtype", cardid, sourceid).upper(); }
    138141    static QString      GetVideoDevice(uint cardid, uint sourceid)
    139142        { return get_on_source("videodevice", cardid, sourceid); }
    140143    static bool         GetVBIDevice(uint cardid, uint sourceid)
     
    145148        { return get_on_source("dbox2_port", cardid, sourceid).toUInt(); }
    146149
    147150    static QString      GetRawCardType(uint cardid, const QString &input)
    148         { return get_on_input("cardtype", cardid, input); }
     151        { return get_on_input("cardtype", cardid, input).upper(); }
    149152    static QString      GetVideoDevice(uint cardid, const QString &input)
    150153        { return get_on_input("videodevice", cardid, input); }
    151154    static QString      GetVBIDevice(uint cardid, const QString &input)
  • libs/libmythtv/freeboxchannelfetcher.h

     
    11#ifndef _FREEBOXCHANNELFETCHER_H_
    22#define _FREEBOXCHANNELFETCHER_H_
    33
     4// POSIX headers
     5#include <pthread.h>
     6
     7// Qt headers
     8#include <qobject.h>
     9#include <qmutex.h>
     10
     11// MythTV headers
    412#include "freeboxchannelinfo.h"
    513
    6 class FreeboxChannelFetcher
     14class FreeboxChannelFetcher : public QObject
    715{
    8   private:
    9     FreeboxChannelFetcher();
     16    Q_OBJECT
     17
     18    friend void *run_scan_thunk(void *param);
     19
     20  public:
     21    FreeboxChannelFetcher(unsigned _sourceid, unsigned _cardid);
    1022    ~FreeboxChannelFetcher();
    1123
    12   public:
    13     static QString DownloadPlaylist(const QString& url);
    14     static fbox_chan_map_t ParsePlaylist(const QString& rawdata);
     24    /// Stops the scanning thread running
     25    void Stop(void);
     26    /// Scans the given frequency list, implies starting the thread()
     27    bool Scan(void);
     28
     29    static QString DownloadPlaylist(const QString &url, bool inQtThread);
     30    static fbox_chan_map_t ParsePlaylist(
     31        const QString &rawdata, FreeboxChannelFetcher *fetcher = NULL);
     32
     33  signals:
     34    /** \brief Tells listener how far along we are from 0..100%
     35     *  \param p percentage completion
     36     */
     37    void ServiceScanPercentComplete(int p);
     38    /** \brief Status message from the scanner
     39     *  \param status The latest status message
     40     */
     41    void ServiceScanUpdateText(const QString &status);
     42    /// Signals that the scan is complete
     43    void ServiceScanComplete(void);
     44
     45  private:
     46    void SetTotalNumChannels(uint val) { chan_cnt = (val) ? val : 1; }
     47    void SetNumChannelsParsed(uint);
     48    void SetNumChannelsInserted(uint);
     49    void SetMessage(const QString &status);
     50    void RunScan(void);
     51
     52    uint      sourceid;
     53    uint      cardid;
     54    uint      chan_cnt;
     55    bool      thread_running;
     56    bool      stop_now;
     57    pthread_t thread;
     58    QMutex    lock;
    1559};
    1660
    17 #endif//_FREEBOXCHANNELFETCHER_H_
     61#endif //_FREEBOXCHANNELFETCHER_H_
    1862
    1963/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • libs/libmythtv/freeboxchannel.cpp

     
    3434   
    3535    if (m_freeboxchannels.empty())
    3636    {
    37         QString content = FreeboxChannelFetcher::DownloadPlaylist(m_videodev);
     37        QString content = FreeboxChannelFetcher::DownloadPlaylist(
     38            m_videodev, true);
    3839        m_freeboxchannels = FreeboxChannelFetcher::ParsePlaylist(content);
    3940        VERBOSE(VB_IMPORTANT, LOC + QString("Loaded %1 channels from %2")
    4041            .arg(m_freeboxchannels.size())
  • libs/libmythtv/scanwizardhelpers.h

     
    219219        FullTransportScan,
    220220        // Scan of one transport already in the database
    221221        TransportScan,
     222        // Freebox import of channels from M3U URL
     223        FreeBoxImport,
    222224        // Imports lists from dvb-utils scanners
    223225        Import
    224226    };
  • libs/libmythtv/scanwizardscanner.cpp

     
    6161#include "hdhrsignalmonitor.h"
    6262#endif
    6363
     64#include "freeboxchannelfetcher.h"
     65
    6466#define LOC QString("SWizScan: ")
    6567#define LOC_ERR QString("SWizScan, Error: ")
    6668
     
    103105      log(new LogList()),
    104106      channel(NULL),                popupProgress(NULL),
    105107      scanner(NULL),                analogScanner(NULL),
     108      freeboxScanner(NULL),
    106109      nScanType(-1),
    107110      nMultiplexToTuneTo(0),        nVideoSource(0),
    108111      frequency(0),                 modulation("8vsb")
     
    134137        analogScanner = NULL;
    135138    }
    136139#endif
     140
     141#ifdef USING_FREEBOX
     142    if (freeboxScanner)
     143    {
     144        freeboxScanner->Stop();
     145        delete freeboxScanner;
     146        freeboxScanner = NULL;
     147    }
     148#endif
    137149}
    138150
    139151void ScanWizardScanner::customEvent(QCustomEvent *e)
     
    410422            startChan["modulation"]);
    411423#endif // USING_DVB
    412424    }
     425    else if (nScanType == ScanTypeSetting::FreeBoxImport)
     426    {
     427        do_scan = false;
     428        ScanFreeBox(cardid, nVideoSource);
     429    }
    413430    else
    414431    {
    415432        do_scan = false;
     
    630647#endif
    631648}
    632649
     650void ScanWizardScanner::ScanFreeBox(uint cardid, uint sourceid)
     651{
     652#ifdef USING_FREEBOX
     653    //Create an analog scan object
     654    freeboxScanner = new FreeboxChannelFetcher(sourceid, cardid);
     655    popupProgress  = new ScanProgressPopup(this, false);
     656
     657    connect(freeboxScanner, SIGNAL(ServiceScanComplete(void)),
     658            this,           SLOT(  scanComplete(void)));
     659    connect(freeboxScanner, SIGNAL(ServiceScanUpdateText(const QString&)),
     660            this,           SLOT(  updateText(const QString&)));
     661    connect(freeboxScanner, SIGNAL(ServiceScanPercentComplete(int)),
     662            this,           SLOT(  serviceScanPctComplete(int)));
     663
     664    popupProgress->progress(0);
     665    popupProgress->exec(this);
     666
     667    if (!freeboxScanner->Scan())
     668    {
     669        MythPopupBox::showOkPopup(gContext->GetMainWindow(),
     670                                  tr("ScanWizard"),
     671                                  tr("Error starting scan"));
     672        cancelScan();
     673    }
     674#endif // USING_FREEBOX
     675}
     676
    633677void ScanWizardScanner::HandleTuneComplete(void)
    634678{
    635679    VERBOSE(VB_SIPARSER, LOC + "HandleTuneComplete()");
  • libs/libmythtv/freeboxchannelfetcher.cpp

     
     1// -*- Mode: c++ -*-
     2
     3// Std C headers
     4#include <cmath>
     5
     6// MythTV headers
     7#include "mythcontext.h"
     8#include "httpcomms.h"
     9#include "cardutil.h"
     10#include "channelutil.h"
    111#include "freeboxchannelfetcher.h"
    212
    3 #include "libmyth/httpcomms.h"
    4 #include "libmyth/mythcontext.h"
    5 
    613#define LOC QString("FBChanFetch: ")
    714#define LOC_ERR QString("FBChanFetch, Error: ")
    815
    9 QString FreeboxChannelFetcher::DownloadPlaylist(const QString& url)
     16static bool parse_chan_info(
     17    const QString &line1, QString &channum, QString &name);
     18
     19FreeboxChannelFetcher::FreeboxChannelFetcher(unsigned _sourceid,
     20                                             unsigned _cardid) :
     21    sourceid(_sourceid),    cardid(_cardid),
     22    chan_cnt(1),            thread_running(false),
     23    stop_now(false),        lock(false)
    1024{
    11     QString rwUrl(url);
    12     return QString::fromUtf8(HttpComms::getHttp(rwUrl));
    1325}
    1426
     27FreeboxChannelFetcher::~FreeboxChannelFetcher()
     28{
     29    do
     30    {
     31        Stop();
     32        usleep(5000);
     33    }
     34    while (thread_running);
     35}
     36
     37void FreeboxChannelFetcher::Stop(void)
     38{
     39    lock.lock();
     40
     41    if (thread_running)
     42    {
     43        stop_now = true;
     44        lock.unlock();
     45
     46        pthread_join(thread, NULL);
     47        return;
     48    }
     49
     50    lock.unlock();
     51}
     52
     53bool FreeboxChannelFetcher::Scan(void)
     54{
     55    lock.lock();
     56    do { lock.unlock(); Stop(); lock.lock(); } while (thread_running);
     57
     58    // Should now have lock and no thread should be running.
     59
     60    stop_now = false;
     61
     62    pthread_create(&thread, NULL, run_scan_thunk, this);
     63
     64    while (!thread_running && !stop_now)
     65        usleep(5000);
     66
     67    lock.unlock();
     68
     69    return thread_running;
     70}
     71
     72void *run_scan_thunk(void *param)
     73{
     74    FreeboxChannelFetcher *chanscan = (FreeboxChannelFetcher*) param;
     75    chanscan->RunScan();
     76
     77    return NULL;
     78}
     79
     80void FreeboxChannelFetcher::RunScan(void)
     81{
     82    thread_running = true;
     83
     84    // Step 1/4 : Get info from DB
     85    QString url = CardUtil::GetVideoDevice(cardid, sourceid);
     86
     87    if (stop_now || url.isEmpty())
     88    {
     89        thread_running = false;
     90        return;
     91    }
     92
     93    VERBOSE(VB_CHANNEL, QString("Playlist URL: %1").arg(url));
     94
     95    // Step 2/4 : Download
     96    emit ServiceScanPercentComplete(5);
     97    emit ServiceScanUpdateText(tr("Downloading Playlist"));
     98
     99    QString playlist = DownloadPlaylist(url, false);
     100
     101    if (stop_now || playlist.isEmpty())
     102    {
     103        thread_running = false;
     104        return;
     105    }
     106
     107    // Step 3/4 : Process
     108    emit ServiceScanPercentComplete(35);
     109    emit ServiceScanUpdateText(tr("Processing Playlist"));
     110
     111    const fbox_chan_map_t channels = ParsePlaylist(playlist, this);
     112
     113    // Step 4/4 : Finish up
     114    emit ServiceScanUpdateText(tr("Adding Channels"));
     115    SetTotalNumChannels(channels.size());
     116    fbox_chan_map_t::const_iterator it = channels.begin();   
     117    for (uint i = 1; it != channels.end(); ++it, ++i)
     118    {
     119        QString channum = it.key();
     120        QString name    = (*it).m_name;
     121        QString msg     = tr("Channel #%1 : %2").arg(channum).arg(name);
     122
     123        int chanid = ChannelUtil::GetChanID(sourceid, channum);
     124        if (chanid <= 0)
     125        {
     126            emit ServiceScanUpdateText(tr("Adding %1").arg(msg));
     127            chanid = ChannelUtil::CreateChanID(sourceid, channum);
     128            ChannelUtil::CreateChannel(
     129                0, sourceid, chanid, name, name, channum,
     130                0, 0, 0, false, false, false, 0);
     131        }
     132        else
     133        {
     134            emit ServiceScanUpdateText(tr("Updating %1").arg(msg));
     135            ChannelUtil::UpdateChannel(
     136                0, sourceid, chanid, name, name, channum, 0, 0, 0, 0);
     137        }
     138
     139        SetNumChannelsInserted(i);
     140    }
     141
     142    emit ServiceScanUpdateText(tr("Done"));
     143    emit ServiceScanUpdateText("");
     144    emit ServiceScanPercentComplete(100);
     145    emit ServiceScanComplete();
     146
     147    thread_running = false;
     148}
     149
     150void FreeboxChannelFetcher::SetNumChannelsParsed(uint val)
     151{
     152    uint minval = 35, range = 70 - minval;
     153    uint pct = minval + (uint) truncf((((float)val) / chan_cnt) * range);
     154    emit ServiceScanPercentComplete(pct);
     155}
     156
     157void FreeboxChannelFetcher::SetNumChannelsInserted(uint val)
     158{
     159    uint minval = 70, range = 100 - minval;
     160    uint pct = minval + (uint) truncf((((float)val) / chan_cnt) * range);
     161    emit ServiceScanPercentComplete(pct);
     162}
     163
     164void FreeboxChannelFetcher::SetMessage(const QString &status)
     165{
     166    emit ServiceScanUpdateText(status);
     167}
     168
     169QString FreeboxChannelFetcher::DownloadPlaylist(const QString &url,
     170                                                bool inQtThread)
     171{
     172    QString redirected_url = url;
     173
     174    QString tmp = HttpComms::getHttp(
     175        redirected_url,
     176        10000 /* ms        */, 3     /* retries      */,
     177        3     /* redirects */, true  /* allow gzip   */,
     178        NULL  /* login     */, inQtThread);
     179
     180    if (redirected_url != url)
     181    {
     182        VERBOSE(VB_CHANNEL, QString("Channel URL redirected to %1")
     183                .arg(redirected_url));
     184    }
     185
     186    return QString::fromUtf8(tmp);
     187}
     188
     189fbox_chan_map_t FreeboxChannelFetcher::ParsePlaylist(
     190    const QString &rawdata, FreeboxChannelFetcher *fetcher)
     191{
     192    fbox_chan_map_t chanmap;
     193
     194    // Verify header is ok
     195    QString header = rawdata.section("\n", 0, 0);
     196    if (header != "#EXTM3U")
     197    {
     198        VERBOSE(VB_IMPORTANT, LOC_ERR +
     199                QString("Invalid channel list header (%1)").arg(header));
     200
     201        if (fetcher)
     202            fetcher->SetMessage(tr("ERROR: M3U channel list is malformed"));
     203
     204        return chanmap;
     205    }
     206
     207    // estimate number of channels
     208    for (uint i = 1; fetcher; i += 2)
     209    {
     210        QString url = rawdata.section("\n", i+1, i+1);
     211        if (url.isEmpty())
     212        {
     213            fetcher->SetTotalNumChannels(i>>1);
     214
     215            VERBOSE(VB_CHANNEL, "Estimating there are "<<(i>>1)
     216                    <<" channels in playlist");
     217
     218            break;
     219        }
     220    }
     221
     222    // Parse each channel
     223    for (int i = 1; true; i += 2)
     224    {
     225        QString tmp = rawdata.section("\n", i+0, i+0);
     226        QString url = rawdata.section("\n", i+1, i+1);
     227        if (tmp.isEmpty() || url.isEmpty())
     228            break;
     229
     230        QString channum, name;
     231        QString msg = tr("Encountered malformed channel");
     232        if (parse_chan_info(tmp, channum, name))
     233        {
     234            chanmap[channum] = FreeboxChannelInfo(name, url);
     235
     236            msg = tr("Parsing Channel #%1 : %2 : %3")
     237                .arg(channum).arg(name).arg(url);
     238
     239            VERBOSE(VB_CHANNEL, msg);
     240
     241            msg = QString::null; // don't tell fetcher
     242        }
     243
     244        if (fetcher)
     245        {
     246            if (!msg.isEmpty())
     247                fetcher->SetMessage(msg);
     248            fetcher->SetNumChannelsParsed(1+(i>>1));
     249        }
     250    }
     251
     252    return chanmap;
     253}
     254
    15255static bool parse_chan_info(const QString &line1,
    16256                            QString &channum, QString &name)
    17257{
     
    63303    return true;
    64304}
    65305
    66 fbox_chan_map_t FreeboxChannelFetcher::ParsePlaylist(const QString& rawdata)
    67 {
    68     fbox_chan_map_t chanmap;
    69 
    70     // Verify header is ok
    71     QString header = rawdata.section("\n", 0, 0);
    72     if (header != "#EXTM3U")
    73     {
    74         VERBOSE(VB_IMPORTANT, LOC_ERR +
    75                 QString("Invalid channel list header (%1)").arg(header));
    76 
    77         return chanmap;
    78     }
    79 
    80     // Parse each channel
    81     for (int i = 1; true; i += 2)
    82     {
    83         QString tmp = rawdata.section("\n", i+0, i+0);
    84         QString url = rawdata.section("\n", i+1, i+1);
    85         if (tmp.isEmpty() || url.isEmpty())
    86             break;
    87 
    88         QString channum, name;
    89         if (parse_chan_info(tmp, channum, name))
    90         {
    91             chanmap[channum] = FreeboxChannelInfo(name, url);
    92         }
    93     }
    94 
    95     return chanmap;
    96 }
    97 
    98306/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • libs/libmythtv/scanwizardscanner.h

     
    4343
    4444class ScanWizard;
    4545class AnalogScan;
     46class FreeboxChannelFetcher;
    4647class LogList;
    4748class SIScan;
    4849class ScanProgressPopup;
     
    8283    void PreScanCommon(uint cardid, uint sourceid);
    8384    void TunedScanCommon(uint cardid, uint sourceid, bool ok);
    8485    void ScanAnalog(uint cardid, uint sourceid);
     86    void ScanFreeBox(uint cardid, uint sourceid);
    8587
    8688    void dvbLock(int);
    8789    void dvbSNR(int);
     
    101103
    102104    SIScan            *scanner;
    103105    AnalogScan        *analogScanner;
     106    FreeboxChannelFetcher *freeboxScanner;
    104107
    105108    int                nScanType;
    106109    int                nMultiplexToTuneTo;
  • libs/libmythtv/scanwizardhelpers.cpp

     
    5858# endif // USING_IVTV
    5959#endif // USING_V4L
    6060
     61#ifdef USING_FREEBOX
     62    if (!cardTypes.isEmpty())
     63        cardTypes += ",";
     64    cardTypes += "'FREEBOX'";
     65#endif // USING_FREEBOX
     66
    6167#ifdef USING_HDHOMERUN
    6268    if (!cardTypes.isEmpty())
    6369        cardTypes += ",";
     
    335341#endif
    336342*/
    337343        break;
     344    case CardUtil::FREEBOX:
     345        addSelection(tr("M3U Import"),
     346                     QString::number(FreeBoxImport), true);
     347        return;
    338348    case CardUtil::ERROR_PROBE:
    339349        addSelection(QObject::tr("Failed to probe the card"),
    340350                     QString::number(Error_Probe), true);
     
    422432              wizard->paneSingle);
    423433    addTarget(QString::number(ScanTypeSetting::FullTransportScan),
    424434              scanAllTransports);
     435    addTarget(QString::number(ScanTypeSetting::FreeBoxImport),
     436              new BlankSetting());
    425437    addTarget(QString::number(ScanTypeSetting::Import),
    426438              filename);
    427439}
  • libs/libmyth/httpcomms.cpp

     
    141141   
    142142    VERBOSE(VB_NETWORK, QString("done: %1 bytes").arg(m_data.size()));
    143143
    144     m_done = true;
    145 
    146144    if (m_timer)
    147145        m_timer->stop();
     146
     147    m_done = true;
    148148}
    149149
    150150void HttpComms::stateChanged(int state)
     
    260260}
    261261
    262262
    263 // getHttp - static function for grabbing http data for a url
    264 //           this is a synchronous function, it will block according to the vars
    265 QString HttpComms::getHttp(QString& url, int timeoutMS, int maxRetries,
    266                            int maxRedirects, bool allowGzip,  Credentials* webCred)
     263/** \fn HttpComms::getHttp(QString&,int,int,int,bool,Credentials*,bool)
     264 *  \brief Static function for grabbing http data for a url.
     265 *
     266 *   This is a synchronous function, it will block according to the vars.
     267 */
     268QString HttpComms::getHttp(QString     &url,
     269                           int          timeoutMS,    int  maxRetries,
     270                           int          maxRedirects, bool allowGzip,
     271                           Credentials *webCred,      bool isInQtEventThread)
    267272{
    268273    int redirectCount = 0;
    269274    int timeoutCount = 0;
     
    294299
    295300        while (!httpGrabber->isDone())
    296301        {
    297             qApp->processEvents();
     302            if (isInQtEventThread)
     303                qApp->processEvents();
    298304            usleep(10000);
    299305        }
    300306
  • libs/libmyth/httpcomms.h

     
    5252    static QString getHttp(QString& url, int timeoutMS = 10000,
    5353                           int maxRetries = 3, int maxRedirects = 3,
    5454                           bool allowGzip = false,
    55                            Credentials* webCred = NULL);
     55                           Credentials* webCred = NULL,
     56                           bool isInQtEventThread = true);
    5657   
    5758    static bool getHttpFile(const QString& file, QString& url, int timeoutMS = 10000,
    5859                            int maxRetries = 3, int maxRedirects = 3,