Ticket #12168: iptv_extension_master_01.patch

File iptv_extension_master_01.patch, 30.2 KB (added by lincoln@…, 10 years ago)

Patch against current master

  • mythtv/libs/libmythbase/mythsingledownload.cpp

    From 39397069cfd31dd5fb1c87848d728e204007277a Mon Sep 17 00:00:00 2001
    From: Lincoln Rogers <lincoln@performaplus.co.nz>
    Date: Fri, 16 May 2014 09:01:57 +1200
    Subject: [PATCH] 1. Subclass IPTVStreamHandler to HTTPStreamHandler to add
     IPTV stream support for Http TS streams. 2. Extend iptvtuningdata to include
     a protocol type for udp, rtp, rtsp, http_hls, http_ts. 3. Add to
     ChannelUtil::GetIPTVTuningData to set the type based on URL and by testing if
     the URL is a HLS playlist 4. Extend MythSingleDownload to support a maximum
     file size limit. Without this, if the URL from (3) is actually a video stream
        then the download would not terminate until the 30 second timeout. 5.
     Extend tv_rec to include a new system event for Tuning Timeout. Allows user
     to run a script to reboot a STB.
    
    Conflicts (Resolved):
    	mythtv/libs/libmythbase/mythsingledownload.h
    	mythtv/libs/libmythtv/channelscan/iptvchannelfetcher.h
    	mythtv/libs/libmythtv/recorders/HLS/HLSReader.h
    	mythtv/libs/libmythtv/tv_rec.cpp
    
    diff --git a/mythtv/libs/libmythbase/mythsingledownload.cpp b/mythtv/libs/libmythbase/mythsingledownload.cpp
    index 95db42d..600d9eb 100644
    a b  
    88 * ) works well.
    99 */
    1010
     11#define LOC QString("MythSingleDownload: ")
     12
    1113bool MythSingleDownload::DownloadURL(const QString &url, QByteArray *buffer,
    12                                      uint timeout)
     14                                     uint timeout, qint64 maxsize)
    1315{
    1416    QMutexLocker  lock(&m_lock);
    15 
    16     // create custom temporary event loop on stack
    1717    QEventLoop   event_loop;
     18    m_buffer = buffer;
    1819
    1920    // the HTTP request
    2021    QNetworkRequest req(url);
    bool MythSingleDownload::DownloadURL(const QString &url, QByteArray *buffer, 
    2627                     QNetworkRequest::AlwaysNetwork);
    2728
    2829    // "quit()" the event-loop, when the network request "finished()"
    29     connect(m_reply, SIGNAL(finished()), &event_loop, SLOT(quit()));
    3030    connect(&m_timer, SIGNAL(timeout()), &event_loop, SLOT(quit()));
     31    connect(m_reply, SIGNAL(finished()), &event_loop, SLOT(quit()));
     32    connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(Progress(qint64,qint64)));
    3133
    32     // Configure timeout
     34    // Configure timeout and size limit
     35    m_maxsize = maxsize;
    3336    m_timer.setSingleShot(true);
    3437    m_timer.start(timeout);  // 30 secs. by default
    3538
    bool MythSingleDownload::DownloadURL(const QString &url, QByteArray *buffer, 
    3740
    3841    disconnect(&m_timer, SIGNAL(timeout()), &event_loop, SLOT(quit()));
    3942    disconnect(m_reply, SIGNAL(finished()), &event_loop, SLOT(quit()));
     43    disconnect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(Progress(qint64,qint64)));
    4044
    4145    if (ret != 0)
    4246    {
    43         LOG(VB_GENERAL, LOG_ERR, QString("MythSingleDownload evenloop failed"));
     47        LOG(VB_GENERAL, LOG_ERR, LOC + "eventloop failed");
    4448    }
    4549
    4650    QMutexLocker  replylock(&m_replylock);
    47     if (m_timer.isActive())
     51    if (!m_timer.isActive())
     52    {
     53        m_errorstring = "timed-out";
     54        m_reply->abort();
     55        ret = false;
     56    }
     57    else
    4858    {
    4959        m_timer.stop();
    5060        m_errorcode = m_reply->error();
    5161        if (m_errorcode == QNetworkReply::NoError)
    5262        {
    53             *buffer += m_reply->readAll();
    54             delete m_reply;
    55             m_reply = NULL;
     63            *m_buffer += m_reply->readAll();
    5664            m_errorstring.clear();
    57             return true;
     65            ret = true;
    5866        }
    5967        else
    6068        {
    6169            m_errorstring = m_reply->errorString();
    62             delete m_reply;
    63             m_reply = NULL;
    64             return false;
     70            ret = false;
    6571        }
    6672    }
    67     else
    68     {
    69         m_errorstring = "timed-out";
    70         m_timer.stop();
    71         m_reply->abort();
    72         delete m_reply;
    73         m_reply = NULL;
    74         return false;
    75     }
     73    delete m_reply;
     74    m_reply = NULL;
     75    m_buffer = NULL;
     76    return ret;
    7677}
    7778
    7879void MythSingleDownload::Cancel(void)
    void MythSingleDownload::Cancel(void) 
    8081    QMutexLocker  replylock(&m_replylock);
    8182    if (m_reply)
    8283    {
    83         LOG(VB_GENERAL, LOG_INFO, "MythSingleDownload: Aborting download");
     84        LOG(VB_GENERAL, LOG_INFO, LOC + "Aborting download");
    8485        m_reply->abort();
    8586    }
    8687}
     88
     89void MythSingleDownload::Progress(qint64 bytesRead, qint64 totalBytes)
     90{
     91    if(m_maxsize && bytesRead>=m_maxsize)
     92    {
     93        LOG(VB_GENERAL, LOG_INFO, LOC + QString("Reached specified max file size (%1 bytes)").arg(m_maxsize));
     94        {
     95            QMutexLocker  replylock(&m_replylock);
     96            *m_buffer += m_reply->read(m_maxsize);
     97        }
     98        m_maxsize=0;
     99        Cancel();
     100    }
     101}
  • mythtv/libs/libmythbase/mythsingledownload.h

    diff --git a/mythtv/libs/libmythbase/mythsingledownload.h b/mythtv/libs/libmythbase/mythsingledownload.h
    index af7144d..cf3e1ef 100644
    a b class MBASE_PUBLIC MythSingleDownload : public QObject 
    2828   MythSingleDownload(void) : m_reply(NULL), m_errorcode(QNetworkReply::NoError) { ; }
    2929   ~MythSingleDownload(void) { ; }
    3030
    31    bool DownloadURL(const QString &url, QByteArray *buffer, uint timeout = 30000);
     31   bool DownloadURL(const QString &url, QByteArray *buffer, uint timeout = 30000, qint64 maxsize = 0);
    3232   void Cancel(void);
    3333   QString ErrorString(void) const { return m_errorstring; }
    3434   QNetworkReply::NetworkError ErrorCode(void) const { return m_errorcode; }
    3535
     36  private slots:
     37   void Progress(qint64 bytesRead, qint64 totalBytes);
     38
    3639  private:
    3740
    3841    QNetworkAccessManager m_mgr;
    class MBASE_PUBLIC MythSingleDownload : public QObject 
    4346
    4447    QString               m_errorstring;
    4548    QNetworkReply::NetworkError m_errorcode;
     49    QByteArray           *m_buffer;
     50    qint64                m_maxsize;
    4651};
    4752
    4853#endif
  • mythtv/libs/libmythtv/channelscan/iptvchannelfetcher.h

    diff --git a/mythtv/libs/libmythtv/channelscan/iptvchannelfetcher.h b/mythtv/libs/libmythtv/channelscan/iptvchannelfetcher.h
    index fd855dc..4060216 100644
    a b class IPTVChannelInfo 
    3939                    uint programnumber) :
    4040        m_name(name), m_xmltvid(xmltvid), m_programnumber(programnumber),
    4141        m_tuning(data_url, data_bitrate,
    42                  fec_type, fec_url0, fec_bitrate0, fec_url1, fec_bitrate1)
     42                 fec_type, fec_url0, fec_bitrate0, fec_url1, fec_bitrate1,
     43                 IPTVTuningData::inValid)
    4344    {
    4445    }
    4546
    46     bool IsValid(void) const
    47     {
    48         return !m_name.isEmpty() && m_tuning.IsValid();
    49     }
    50 
    5147  public:
    5248    QString m_name;
    5349    QString m_xmltvid;
  • mythtv/libs/libmythtv/channelutil.cpp

    diff --git a/mythtv/libs/libmythtv/channelutil.cpp b/mythtv/libs/libmythtv/channelutil.cpp
    index acf2da9..26028ae 100644
    a b IPTVTuningData ChannelUtil::GetIPTVTuningData(uint chanid) 
    19681968
    19691969    QString data_url, fec_url0, fec_url1;
    19701970    IPTVTuningData::FECType fec_type = IPTVTuningData::kNone;
     1971    IPTVTuningData::IPTVProtocol protocol = IPTVTuningData::inValid;
    19711972    uint bitrate[3] = { 0, 0, 0, };
    19721973    while (query.next())
    19731974    {
    IPTVTuningData ChannelUtil::GetIPTVTuningData(uint chanid) 
    20102011            case IPTVTuningData::kSMPTE2022_2:
    20112012                break; // will be handled by type of first FEC stream
    20122013        }
     2014
     2015        if (data_url.toLower().startsWith("udp"))
     2016            protocol = IPTVTuningData::udp;
     2017
     2018        else if (data_url.toLower().startsWith("rtp"))
     2019            protocol = IPTVTuningData::rtp;
     2020
     2021        else if (data_url.toLower().startsWith("rtsp"))
     2022            protocol = IPTVTuningData::rtsp;
     2023
     2024        else if (data_url.toLower().startsWith("http") && ChannelUtil::IsHLSPlaylist(data_url))
     2025            protocol = IPTVTuningData::http_hls;
     2026
     2027        else if (data_url.toLower().startsWith("http"))
     2028            protocol = IPTVTuningData::http_ts;
    20132029    }
    20142030
    20152031    IPTVTuningData tuning(data_url, bitrate[0], fec_type,
    2016                           fec_url0, bitrate[1], fec_url1, bitrate[2]);
     2032                          fec_url0, bitrate[1], fec_url1, bitrate[2],
     2033                          protocol);
    20172034    LOG(VB_GENERAL, LOG_INFO, QString("Loaded %1 for %2")
    20182035        .arg(tuning.GetDeviceName()).arg(chanid));
    20192036    return tuning;
    20202037}
    20212038
     2039
     2040
     2041
     2042
     2043
     2044
     2045#include "HLSReader.h"
     2046
     2047bool ChannelUtil::IsHLSPlaylist(QString url)
     2048{
     2049    QByteArray buffer;
     2050
     2051    MythSingleDownload downloader;
     2052    downloader.DownloadURL(url, &buffer, 5000, 10000);
     2053    if (!buffer.size())
     2054    {
     2055        LOG(VB_GENERAL, LOG_ERR,QString("IsHLSPlaylist - Open Failed: %1")
     2056            .arg(downloader.ErrorString()));
     2057        return false;
     2058    }
     2059
     2060    QTextStream text(&buffer);
     2061    text.setCodec("UTF-8");
     2062    return (HLSReader::IsValidPlaylist(text));
     2063}
     2064
     2065
     2066
     2067
     2068
     2069
     2070
    20222071// TODO This should be modified to load a complete channelinfo object including
    20232072//      all fields from the database
    20242073/**
  • mythtv/libs/libmythtv/channelutil.h

    diff --git a/mythtv/libs/libmythtv/channelutil.h b/mythtv/libs/libmythtv/channelutil.h
    index 93cb19b..a1ceb7d 100644
    a b class MTV_PUBLIC ChannelUtil 
    185185    static QString GetVideoFilters(uint sourceid, const QString &channum)
    186186        { return GetChannelValueStr("videofilters", sourceid, channum); }
    187187    static IPTVTuningData GetIPTVTuningData(uint chanid);
     188    static bool IsHLSPlaylist(QString url);
    188189
    189190    // Are there any other possibly useful sort orders?
    190191    // e.g. Sorting by sourceid would probably be better done by two distinct
  • mythtv/libs/libmythtv/iptvtuningdata.h

    diff --git a/mythtv/libs/libmythtv/iptvtuningdata.h b/mythtv/libs/libmythtv/iptvtuningdata.h
    index 2e89ffd..92aab07 100644
    a b  
    88
    99// MythTV headers
    1010#include "mythtvexp.h"
     11#include "mythlogging.h"
    1112
    1213class MTV_PUBLIC IPTVTuningData
    1314{
    class MTV_PUBLIC IPTVTuningData 
    3132        kSMPTE2022_2,
    3233    } IPTVType;
    3334
     35    typedef enum IPTVProtocol
     36    {
     37        inValid = 0,
     38        udp,
     39        rtp,
     40        rtsp,
     41        http_ts,
     42        http_hls
     43    } IPTVProtocol;
     44
    3445    IPTVTuningData() : m_fec_type(kNone)
    3546    {
    3647        memset(&m_bitrate, 0, sizeof(m_bitrate));
    3748    }
    3849
    3950    IPTVTuningData(const QString &data_url, uint data_bitrate,
    40                    FECType fec_type,
     51                   const FECType fec_type,
    4152                   const QString &fec_url0, uint fec_bitrate0,
    42                    const QString &fec_url1, uint fec_bitrate1) :
     53                   const QString &fec_url1, uint fec_bitrate1,
     54                   const IPTVProtocol protocol) :
    4355        m_data_url(data_url),
    44         m_fec_type(fec_type), m_fec_url0(fec_url0), m_fec_url1(fec_url1)
     56        m_fec_type(fec_type), m_fec_url0(fec_url0), m_fec_url1(fec_url1),
     57        m_protocol(protocol)
    4558    {
    4659        m_bitrate[0] = data_bitrate;
    4760        m_bitrate[1] = fec_bitrate0;
    class MTV_PUBLIC IPTVTuningData 
    5164    IPTVTuningData(const QString &data_url, uint data_bitrate,
    5265                   const QString &fec_type,
    5366                   const QString &fec_url0, uint fec_bitrate0,
    54                    const QString &fec_url1, uint fec_bitrate1) :
     67                   const QString &fec_url1, uint fec_bitrate1,
     68                   const IPTVProtocol protocol) :
    5569        m_data_url(data_url),
    56         m_fec_type(kNone), m_fec_url0(fec_url0), m_fec_url1(fec_url1)
     70        m_fec_type(kNone), m_fec_url0(fec_url0), m_fec_url1(fec_url1),
     71        m_protocol(protocol)
    5772    {
    5873        m_bitrate[0] = data_bitrate;
    5974        m_bitrate[1] = fec_bitrate0;
    class MTV_PUBLIC IPTVTuningData 
    134149
    135150    uint GetURLCount(void) const { return 3; }
    136151
    137     bool IsHLS(void) const
    138     {
    139         return m_data_url.scheme().startsWith("http") && m_data_url.isValid();
    140     }
    141 
    142152    bool IsValid(void) const
    143153    {
    144         return m_data_url.isValid() &&
    145                (IsHLS() ||
    146                 ((m_data_url.scheme() == "rtp" || m_data_url.scheme() == "udp") && m_data_url.port() != -1) ||
    147                 m_data_url.scheme() == "rtsp"
    148                );
     154        bool ret = (m_data_url.isValid() && (IsUDP() || IsRTP() || IsRTSP() || IsHLS() || IsHTTPTS()));
     155
     156        LOG(VB_CHANNEL, LOG_DEBUG, QString("IPTVTuningdata (%1): IsValid = %2")
     157            .arg(m_data_url.toString())
     158            .arg(ret ? "true" : "false"));
     159
     160        return ret;
     161    }
     162
     163    bool IsUDP(void) const
     164    {
     165        return (m_protocol == udp);
     166    }
     167
     168    bool IsRTP(void) const
     169    {
     170        return (m_protocol == rtp);
     171    }
     172
     173    bool IsRTSP(void) const
     174    {
     175        return (m_protocol == rtsp);
     176    }
     177
     178    bool IsHLS() const
     179    {
     180        return (m_protocol == http_hls);
     181    }
     182
     183    bool IsHTTPTS() const
     184    {
     185        return (m_protocol == http_ts);
    149186    }
    150187
    151188  public:
    class MTV_PUBLIC IPTVTuningData 
    154191    QUrl m_fec_url0;
    155192    QUrl m_fec_url1;
    156193    uint m_bitrate[3];
     194    IPTVProtocol m_protocol;
    157195};
    158196
    159197#endif // _IPTV_TUNING_DATA_H_
  • mythtv/libs/libmythtv/libmythtv.pro

    diff --git a/mythtv/libs/libmythtv/libmythtv.pro b/mythtv/libs/libmythtv/libmythtv.pro
    index e69cf8e..3d51281 100644
    a b using_backend { 
    666666    SOURCES += recorders/rtp/packetbuffer.cpp
    667667    SOURCES += recorders/rtp/rtppacketbuffer.cpp
    668668
     669    # Support for HTTP TS streams
     670    HEADERS += recorders/httptsstreamhandler.h
     671    SOURCES += recorders/httptsstreamhandler.cpp
     672
    669673    # Suppport for HLS recorder
    670674    HEADERS += recorders/hlsstreamhandler.h
    671675    SOURCES += recorders/hlsstreamhandler.cpp
  • mythtv/libs/libmythtv/mythsystemevent.cpp

    diff --git a/mythtv/libs/libmythtv/mythsystemevent.cpp b/mythtv/libs/libmythtv/mythsystemevent.cpp
    index 085cee5..30063a4 100644
    a b MythSystemEventEditor::MythSystemEventEditor(MythScreenStack *parent, 
    403403        tr("Playback unpaused");
    404404    m_settings["EventCmdPlayChanged"]          = // PLAY_CHANGED
    405405        tr("Playback program changed");
     406    m_settings["EventCmdTuningSignalTimeout"]  = // TUNING_SIGNAL_TIMEOUT
     407        tr("Tuning signal waiting");
    406408    m_settings["EventCmdMasterStarted"]        = // MASTER_STARTED
    407409        tr("Master backend started");
    408410    m_settings["EventCmdMasterShutdown"]       = // MASTER_SHUTDOWN
  • mythtv/libs/libmythtv/recorders/HLS/HLSReader.cpp

    diff --git a/mythtv/libs/libmythtv/recorders/HLS/HLSReader.cpp b/mythtv/libs/libmythtv/recorders/HLS/HLSReader.cpp
    index 60cfb15..822d608 100644
    a b bool HLSReader::IsValidPlaylist(QTextStream & text) 
    274274        line = text.readLine();
    275275        if (line.isNull())
    276276            break;
    277         LOG(VB_RECORD, LOG_DEBUG, LOC +
     277        LOG(VB_RECORD, LOG_DEBUG, /* LOC + */
    278278            QString("IsValidPlaylist: |'%1'").arg(line));
    279279        if (line.startsWith(QLatin1String("#EXT-X-TARGETDURATION"))  ||
    280280            line.startsWith(QLatin1String("#EXT-X-MEDIA-SEQUENCE"))  ||
  • mythtv/libs/libmythtv/recorders/HLS/HLSReader.h

    diff --git a/mythtv/libs/libmythtv/recorders/HLS/HLSReader.h b/mythtv/libs/libmythtv/recorders/HLS/HLSReader.h
    index 620b84c..6a71cf4 100644
    a b class HLSReader 
    6565    static void CancelURL(const QString &url);
    6666    static void CancelURL(const QStringList &urls);
    6767    static QString RelativeURI(const QString& surl, const QString& spath);
     68   
     69    static bool IsValidPlaylist(QTextStream & text);
    6870
    6971  protected:
    7072    void Cancel(bool quiet = false);
    class HLSReader 
    8183
    8284  private:
    8385    static QString DecodedURI(const QString& uri);
    84 
    85     bool IsValidPlaylist(QTextStream & text);
    86 
    8786    static QString ParseAttributes(const QString& line, const char* attr);
    8887    static bool ParseDecimalValue(const QString& line, int& target);
    8988    static bool ParseDecimalValue(const QString& line, int64_t& target);
  • mythtv/libs/libmythtv/recorders/cetonstreamhandler.cpp

    diff --git a/mythtv/libs/libmythtv/recorders/cetonstreamhandler.cpp b/mythtv/libs/libmythtv/recorders/cetonstreamhandler.cpp
    index f45782a..8d298f4 100644
    a b void CetonStreamHandler::Return(CetonStreamHandler * & ref) 
    107107
    108108CetonStreamHandler::CetonStreamHandler(const QString &device) :
    109109    IPTVStreamHandler(IPTVTuningData(
    110                           "", 0, IPTVTuningData::kNone, "", 0, "", 0)),
     110                          "", 0, IPTVTuningData::kNone, "", 0, "", 0, IPTVTuningData::inValid)),
    111111    _card(0),
    112112    _tuner(0),
    113113    _using_cablecard(false),
    CetonStreamHandler::CetonStreamHandler(const QString &device) : 
    150150    int rtspPort = 8554;
    151151    QString url = QString("rtsp://%1:%2/cetonmpeg%3")
    152152        .arg(_ip_address).arg(rtspPort).arg(_tuner);
    153     m_tuning = IPTVTuningData(url, 0, IPTVTuningData::kNone, "", 0, "", 0);
     153    m_tuning = IPTVTuningData(url, 0, IPTVTuningData::kNone, "", 0, "", 0, IPTVTuningData::rtsp);
    154154    m_use_rtp_streaming = true;
    155155
    156156    _valid = true;
  • new file mythtv/libs/libmythtv/recorders/httptsstreamhandler.cpp

    diff --git a/mythtv/libs/libmythtv/recorders/httptsstreamhandler.cpp b/mythtv/libs/libmythtv/recorders/httptsstreamhandler.cpp
    new file mode 100644
    index 0000000..6a03436
    - +  
     1// MythTV headers
     2#include "httptsstreamhandler.h"
     3#include "mythlogging.h"
     4
     5// POSIX headers
     6#include <unistd.h> // for usleep
     7
     8#define LOC QString("HTTPTSSH(%1): ").arg(_device)
     9
     10// BUFFER_SIZE is a multiple of TS_SIZE
     11#define TS_SIZE     188
     12#define BUFFER_SIZE (512 * TS_SIZE)
     13
     14QMap<QString,HTTPTSStreamHandler*>  HTTPTSStreamHandler::s_handlers;
     15QMap<QString,uint>               HTTPTSStreamHandler::s_handlers_refcnt;
     16QMutex                           HTTPTSStreamHandler::s_handlers_lock;
     17
     18HTTPTSStreamHandler* HTTPTSStreamHandler::Get(const IPTVTuningData& tuning)
     19{
     20    QMutexLocker locker(&s_handlers_lock);
     21
     22    QString devkey = tuning.GetDeviceKey();
     23
     24    QMap<QString,HTTPTSStreamHandler*>::iterator it = s_handlers.find(devkey);
     25
     26    if (it == s_handlers.end())
     27    {
     28        HTTPTSStreamHandler* newhandler = new HTTPTSStreamHandler(tuning);
     29        newhandler->Start();
     30        s_handlers[devkey] = newhandler;
     31        s_handlers_refcnt[devkey] = 1;
     32
     33        LOG(VB_RECORD, LOG_INFO,
     34            QString("HTTPTSSH: Creating new stream handler %1 for %2")
     35            .arg(devkey).arg(tuning.GetDeviceName()));
     36    }
     37    else
     38    {
     39        s_handlers_refcnt[devkey]++;
     40        uint rcount = s_handlers_refcnt[devkey];
     41        LOG(VB_RECORD, LOG_INFO,
     42            QString("HTTPTSSH: Using existing stream handler %1 for %2")
     43            .arg(devkey).arg(tuning.GetDeviceName()) +
     44            QString(" (%1 in use)").arg(rcount));
     45    }
     46
     47    return s_handlers[devkey];
     48}
     49
     50HTTPTSStreamHandler::HTTPTSStreamHandler(const IPTVTuningData& tuning) :
     51    IPTVStreamHandler(tuning)
     52{
     53    LOG(VB_GENERAL, LOG_INFO, LOC + "ctor");
     54}
     55
     56HTTPTSStreamHandler::~HTTPTSStreamHandler(void)
     57{
     58    LOG(VB_GENERAL, LOG_INFO, LOC + "dtor");
     59    Stop();
     60}
     61
     62void HTTPTSStreamHandler::run(void)
     63{
     64    RunProlog();
     65    int open_sleep = 250000;
     66    LOG(VB_RECORD, LOG_INFO, LOC + "run() -- begin");
     67    SetRunning(true, false, false);
     68
     69    m_reader   = new HTTPReader(this);
     70    while (_running_desired)
     71    {
     72        if (!m_reader->DownloadStream(m_tuning.GetURL(0)))
     73        {
     74            LOG(VB_RECORD, LOG_INFO, LOC + "DownloadStream failed to recieve bytes from " + m_tuning.GetURL(0).toString());
     75            usleep(open_sleep);
     76            if (open_sleep < 10000000)
     77                open_sleep += 250000;
     78            continue;
     79        }
     80        open_sleep = 250000;
     81    }
     82
     83    delete m_reader;
     84    SetRunning(false, false, false);
     85    RunEpilog();
     86    LOG(VB_RECORD, LOG_INFO, LOC + "run() -- done");
     87}
     88
     89
     90#undef  LOC
     91#define LOC QString("HTTPReader(%1): ").arg(m_parent->_device)
     92
     93bool HTTPReader::DownloadStream(const QUrl url)
     94{
     95    LOG(VB_RECORD, LOG_INFO, LOC + "DownloadStream -- begin");
     96
     97    QMutexLocker lock(&m_lock);
     98    QEventLoop   event_loop;
     99
     100    m_buffer = new uint8_t[BUFFER_SIZE];
     101    size = 0;
     102    m_ok = false;
     103
     104    // the HTTP request
     105    m_replylock.lock();
     106    m_reply = m_mgr.get(QNetworkRequest(url));
     107    m_replylock.unlock();
     108
     109    connect(&m_timer, SIGNAL(timeout()), &event_loop, SLOT(quit()));
     110    connect(m_reply, SIGNAL(finished()), &event_loop, SLOT(quit()));
     111    connect(m_reply,SIGNAL(readyRead()), this,        SLOT(HttpRead()));
     112
     113    // Configure timeout and size limit
     114    m_timer.setSingleShot(true);
     115    m_timer.start(10000);
     116
     117    event_loop.exec(); // blocks stack until quit() is called
     118
     119    disconnect(&m_timer, SIGNAL(timeout()), &event_loop, SLOT(quit()));
     120    disconnect(m_reply, SIGNAL(finished()), &event_loop, SLOT(quit()));
     121    disconnect(m_reply,SIGNAL(readyRead()), this,        SLOT(HttpRead()));
     122
     123    if (m_timer.isActive())
     124        m_timer.stop();
     125
     126    QMutexLocker  replylock(&m_replylock);
     127    if (!m_reply->error() == QNetworkReply::NoError)
     128    {
     129        LOG(VB_RECORD, LOG_ERR, LOC + "DownloadStream exited with " + m_reply->errorString());
     130    }
     131
     132    delete m_reply;
     133    m_reply = NULL;
     134    delete[] m_buffer;
     135    m_buffer=NULL;
     136
     137    LOG(VB_RECORD, LOG_INFO, LOC + "DownloadStream -- end");
     138    return m_ok;
     139}
     140
     141void HTTPReader::HttpRead()
     142{
     143    m_timer.stop();
     144    m_ok = true;
     145    ReadBytes();
     146    WriteBytes();
     147}
     148
     149void HTTPReader::ReadBytes()
     150{
     151    QMutexLocker replylock(&m_replylock);
     152    QMutexLocker bufferlock(&m_bufferlock);
     153
     154    if(m_reply == NULL || m_buffer == NULL || size > BUFFER_SIZE)
     155        return;
     156
     157    qint64 bytesRead = m_reply->read( reinterpret_cast<char*>(m_buffer+size), BUFFER_SIZE-size);
     158    size += (bytesRead > 0 ? bytesRead : 0);
     159    LOG(VB_RECORD, LOG_DEBUG, LOC + QString("ReadBytes: %1 bytes recieved").arg(bytesRead));
     160}
     161
     162void HTTPReader::WriteBytes()
     163{
     164    if (size < TS_SIZE)
     165        return;
     166
     167    QMutexLocker  replylock(&m_bufferlock);
     168    int remainder = 0;
     169    {
     170        QMutexLocker locker(&m_parent->_listener_lock);
     171        HTTPTSStreamHandler::StreamDataList::const_iterator sit;
     172        sit = m_parent->_stream_data_list.begin();
     173        for (; sit != m_parent->_stream_data_list.end(); ++sit)
     174        {
     175            remainder = sit.key()->ProcessData(m_buffer, size);
     176        }
     177    }
     178    LOG(VB_RECORD, LOG_DEBUG, LOC + QString("WriteBytes: %1/%2 bytes remain").arg(remainder).arg(size));
     179    memcpy(m_buffer, m_buffer+(size-remainder), remainder);
     180    size = remainder;
     181}
     182
     183void HTTPReader::Cancel(void)
     184{
     185    QMutexLocker  replylock(&m_replylock);
     186    if (m_reply)
     187    {
     188        LOG(VB_RECORD, LOG_INFO, LOC + "Cancel: Aborting stream download");
     189        m_reply->abort();
     190    }
     191}
  • new file mythtv/libs/libmythtv/recorders/httptsstreamhandler.h

    diff --git a/mythtv/libs/libmythtv/recorders/httptsstreamhandler.h b/mythtv/libs/libmythtv/recorders/httptsstreamhandler.h
    new file mode 100644
    index 0000000..c36952b
    - +  
     1#ifndef _HTTPTSSTREAMHANDLER_H_
     2#define _HTTPTSSTREAMHANDLER_H_
     3
     4#include <vector>
     5using namespace std;
     6
     7#include <QString>
     8#include <QMutex>
     9#include <QMap>
     10#include <QNetworkAccessManager>
     11#include <QNetworkRequest>
     12#include <QNetworkReply>
     13
     14#include "channelutil.h"
     15#include "iptvstreamhandler.h"
     16
     17class HTTPReader;
     18
     19class HTTPTSStreamHandler : public IPTVStreamHandler
     20{
     21    friend class HTTPReader;
     22  public:
     23    static HTTPTSStreamHandler* Get(const IPTVTuningData& tuning);
     24
     25protected:
     26    HTTPTSStreamHandler(const IPTVTuningData &tuning);
     27    virtual ~HTTPTSStreamHandler(void);
     28    virtual void run(void);
     29
     30  protected:
     31    HTTPReader*             m_reader;
     32    // for implementing Get & Return
     33    static QMutex                               s_handlers_lock;
     34    static QMap<QString, HTTPTSStreamHandler*>  s_handlers;
     35    static QMap<QString, uint>                  s_handlers_refcnt;
     36};
     37
     38
     39class MBASE_PUBLIC HTTPReader : public QObject
     40{
     41    Q_OBJECT
     42
     43  public:
     44    HTTPReader(HTTPTSStreamHandler* parent) : m_parent(parent){}
     45    void Cancel(void);
     46    bool DownloadStream(const QUrl url);
     47
     48  protected:
     49    void ReadBytes();
     50    void WriteBytes();
     51
     52  private slots:
     53    void HttpRead();
     54
     55  private:
     56    HTTPTSStreamHandler     *m_parent;
     57    QTimer                  m_timer;
     58    QNetworkAccessManager   m_mgr;
     59    QNetworkReply           *m_reply;
     60    QMutex                  m_lock;
     61    QMutex                  m_replylock;
     62    QMutex                  m_bufferlock;
     63    uint8_t                 *m_buffer;
     64    bool                    m_ok;
     65    int                     size;
     66};
     67
     68#endif // _HTTPTSSTREAMHANDLER_H_
  • mythtv/libs/libmythtv/recorders/iptvchannel.cpp

    diff --git a/mythtv/libs/libmythtv/recorders/iptvchannel.cpp b/mythtv/libs/libmythtv/recorders/iptvchannel.cpp
    index 4456976..0b7ae38 100644
    a b  
    1212
    1313// MythTV headers
    1414#include "iptvstreamhandler.h"
     15#include "httptsstreamhandler.h"
    1516#include "hlsstreamhandler.h"
    1617#include "iptvrecorder.h"
    1718#include "iptvchannel.h"
     
    2021
    2122#define LOC QString("IPTVChan[%1]: ").arg(GetCardID())
    2223
    23 IPTVChannel::IPTVChannel(TVRec *rec, const QString&) :
    24     DTVChannel(rec), m_firsttune(true),
     24IPTVChannel::IPTVChannel(TVRec *rec, const QString &videodev) :
     25    DTVChannel(rec), m_videodev(videodev), m_firsttune(true),
    2526    m_stream_handler(NULL), m_stream_data(NULL)
    2627{
    2728    LOG(VB_CHANNEL, LOG_INFO, LOC + "ctor");
    void IPTVChannel::OpenStreamHandler(void) 
    100101    {
    101102        m_stream_handler = HLSStreamHandler::Get(m_last_tuning);
    102103    }
     104    else if (m_last_tuning.IsHTTPTS())
     105    {
     106        m_stream_handler = HTTPTSStreamHandler::Get(m_last_tuning);
     107    }
    103108    else
    104109    {
    105110        m_stream_handler = IPTVStreamHandler::Get(m_last_tuning);
  • mythtv/libs/libmythtv/recorders/iptvchannel.h

    diff --git a/mythtv/libs/libmythtv/recorders/iptvchannel.h b/mythtv/libs/libmythtv/recorders/iptvchannel.h
    index db757c0..781ef55 100644
    a b class IPTVChannel : QObject, public DTVChannel 
    6464    mutable QMutex     m_stream_lock;
    6565    IPTVStreamHandler *m_stream_handler;
    6666    MPEGStreamData    *m_stream_data;
     67    QString            m_videodev;
    6768};
    6869
    6970#endif // _IPTV_CHANNEL_H_
  • mythtv/libs/libmythtv/recorders/iptvstreamhandler.cpp

    diff --git a/mythtv/libs/libmythtv/recorders/iptvstreamhandler.cpp b/mythtv/libs/libmythtv/recorders/iptvstreamhandler.cpp
    index cab9be2..f2c93b5 100644
    a b IPTVStreamHandler::IPTVStreamHandler(const IPTVTuningData &tuning) : 
    115115{
    116116    memset(m_sockets, 0, sizeof(m_sockets));
    117117    memset(m_read_helpers, 0, sizeof(m_read_helpers));
    118     m_use_rtp_streaming = m_tuning.GetDataURL().scheme().toUpper() == "RTP";
     118    m_use_rtp_streaming = m_tuning.IsRTP();
    119119}
    120120
    121121void IPTVStreamHandler::run(void)
    void IPTVStreamHandler::run(void) 
    131131    // Setup
    132132    CetonRTSP *rtsp = NULL;
    133133    IPTVTuningData tuning = m_tuning;
    134     if (m_tuning.GetURL(0).scheme().toLower() == "rtsp")
     134    if(m_tuning.IsRTSP())
    135135    {
    136136        rtsp = new CetonRTSP(m_tuning.GetURL(0));
    137137
    void IPTVStreamHandler::run(void) 
    165165        urltuned.setScheme("rtp");
    166166        urltuned.setPort(0);
    167167        tuning = IPTVTuningData(urltuned.toString(), 0, IPTVTuningData::kNone,
    168                                 urltuned.toString(), 0, "", 0);
     168                                urltuned.toString(), 0, "", 0, IPTVTuningData::rtp);
    169169    }
    170170
    171171    bool error = false;
     172
    172173    int start_port = 0;
    173174    for (uint i = 0; i < IPTV_SOCKET_COUNT; i++)
    174175    {
    void IPTVStreamHandler::run(void) 
    279280
    280281    if (!error)
    281282    {
    282         if (m_use_rtp_streaming)
     283        if (m_tuning.IsRTP() || m_tuning.IsRTSP())
    283284            m_buffer = new RTPPacketBuffer(tuning.GetBitrate(0));
    284285        else
    285286            m_buffer = new UDPPacketBuffer(tuning.GetBitrate(0));
  • mythtv/libs/libmythtv/recorders/iptvstreamhandler.h

    diff --git a/mythtv/libs/libmythtv/recorders/iptvstreamhandler.h b/mythtv/libs/libmythtv/recorders/iptvstreamhandler.h
    index 27f1a05..6f590af 100644
    a b using namespace std; 
    1212#include <QMutex>
    1313#include <QMap>
    1414
     15#include <QNetworkAccessManager>
     16#include <QtNetwork>
     17
    1518#include "channelutil.h"
    1619#include "streamhandler.h"
    1720
  • mythtv/libs/libmythtv/tv_rec.cpp

    diff --git a/mythtv/libs/libmythtv/tv_rec.cpp b/mythtv/libs/libmythtv/tv_rec.cpp
    index 778f36d..4697923 100644
    a b void TVRec::TuningFrequency(const TuningRequest &request) 
    38893889                        expire.addMSecs(genOpt.channel_timeout * 2);
    38903890                }
    38913891                signalMonitorCheckCnt = 0;
     3892
     3893                //System Event TUNING_TIMEOUT deadline
     3894                QDateTime expire = MythDate::current();
     3895                signalEventCmdTimeout = expire.addMSecs(genOpt.channel_timeout);
     3896                signalEventCmdSent = false;
    38923897            }
    38933898        }
    38943899
    void TVRec::TuningFrequency(const TuningRequest &request) 
    39273932MPEGStreamData *TVRec::TuningSignalCheck(void)
    39283933{
    39293934    RecStatusType newRecStatus = rsRecording;
     3935    if ((signalMonitor->IsErrored() ||
     3936         MythDate::current() > signalEventCmdTimeout) &&
     3937         !signalEventCmdSent)
     3938    {
     3939        gCoreContext->SendSystemEvent(QString("TUNING_SIGNAL_TIMEOUT CARDID %1").arg(cardid));
     3940        signalEventCmdSent=true;
     3941    }
    39303942    if (signalMonitor->IsAllGood())
    39313943    {
    39323944        LOG(VB_RECORD, LOG_INFO, LOC + "TuningSignalCheck: Have a good signal");
  • mythtv/libs/libmythtv/tv_rec.h

    diff --git a/mythtv/libs/libmythtv/tv_rec.h b/mythtv/libs/libmythtv/tv_rec.h
    index 91e5229..bad9f11 100644
    a b class MTV_PUBLIC TVRec : public SignalMonitorListener, public QRunnable 
    329329    SignalMonitor    *signalMonitor;
    330330    EITScanner       *scanner;
    331331
     332    QDateTime         signalEventCmdTimeout;
     333    bool              signalEventCmdSent;
     334
    332335    QDateTime         signalMonitorDeadline;
    333336    uint              signalMonitorCheckCnt;
    334337