Ticket #12168: iptv_extension_01.patch

File iptv_extension_01.patch, 30.3 KB (added by lincoln@…, 5 years ago)

Patch for fixes/0.27

  • mythtv/libs/libmythbase/mythsingledownload.cpp

    From a69fc939082a453816d4617a485295fbe1b563cd 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.
    
    
    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 d43f083..56686fa 100644
    a b class MBASE_PUBLIC MythSingleDownload : public QObject 
    2929   ~MythSingleDownload(void) { ; }
    3030
    3131   bool DownloadURL(const QString &url, QByteArray *buffer,
    32                     uint timeout = 30000);
     32                    uint timeout = 30000,
     33                    qint64 maxsize = 0);
    3334   void Cancel(void);
    3435   QString ErrorString(void) const { return m_errorstring; }
    3536   QNetworkReply::NetworkError ErrorCode(void) const { return m_errorcode; }
    3637
     38  private slots:
     39   void Progress(qint64 bytesRead, qint64 totalBytes);
     40
    3741  private:
    3842
    3943    QNetworkAccessManager m_mgr;
    class MBASE_PUBLIC MythSingleDownload : public QObject 
    4145    QNetworkReply        *m_reply;
    4246    QMutex                m_lock;
    4347    QMutex                m_replylock;
    44 
    4548    QString               m_errorstring;
    4649    QNetworkReply::NetworkError m_errorcode;
     50    QByteArray           *m_buffer;
     51    qint64                m_maxsize;
    4752};
    4853
    4954#endif
  • mythtv/libs/libmythtv/channelscan/iptvchannelfetcher.h

    diff --git a/mythtv/libs/libmythtv/channelscan/iptvchannelfetcher.h b/mythtv/libs/libmythtv/channelscan/iptvchannelfetcher.h
    index 030da62..9e8f954 100644
    a b class IPTVChannelInfo 
    3838                    uint fec_bitrate1) :
    3939        m_name(name), m_xmltvid(xmltvid),
    4040        m_tuning(data_url, data_bitrate,
    41                  fec_type, fec_url0, fec_bitrate0, fec_url1, fec_bitrate1)
     41                 fec_type, fec_url0, fec_bitrate0, fec_url1, fec_bitrate1,
     42                 IPTVTuningData::inValid)
    4243    {
    4344    }
    4445
    45     bool IsValid(void) const
    46     {
    47         return !m_name.isEmpty() && !m_tuning.IsValid();
    48     }
    49 
    5046  public:
    5147    QString m_name;
    5248    QString m_xmltvid;
  • mythtv/libs/libmythtv/channelutil.cpp

    diff --git a/mythtv/libs/libmythtv/channelutil.cpp b/mythtv/libs/libmythtv/channelutil.cpp
    index b998cff..9111373 100644
    a b IPTVTuningData ChannelUtil::GetIPTVTuningData(uint chanid) 
    20492049
    20502050    QString data_url, fec_url0, fec_url1;
    20512051    IPTVTuningData::FECType fec_type = IPTVTuningData::kNone;
     2052    IPTVTuningData::IPTVProtocol protocol = IPTVTuningData::inValid;
    20522053    uint bitrate[3] = { 0, 0, 0, };
    20532054    while (query.next())
    20542055    {
    IPTVTuningData ChannelUtil::GetIPTVTuningData(uint chanid) 
    20912092            case IPTVTuningData::kSMPTE2022_2:
    20922093                break; // will be handled by type of first FEC stream
    20932094        }
     2095
     2096        if (data_url.toLower().startsWith("udp"))
     2097            protocol = IPTVTuningData::udp;
     2098
     2099        else if (data_url.toLower().startsWith("rtp"))
     2100            protocol = IPTVTuningData::rtp;
     2101
     2102        else if (data_url.toLower().startsWith("rtsp"))
     2103            protocol = IPTVTuningData::rtsp;
     2104
     2105        else if (data_url.toLower().startsWith("http") && ChannelUtil::IsHLSPlaylist(data_url))
     2106            protocol = IPTVTuningData::http_hls;
     2107
     2108        else if (data_url.toLower().startsWith("http"))
     2109            protocol = IPTVTuningData::http_ts;
    20942110    }
    20952111
    20962112    IPTVTuningData tuning(data_url, bitrate[0], fec_type,
    2097                           fec_url0, bitrate[1], fec_url1, bitrate[2]);
     2113                          fec_url0, bitrate[1], fec_url1, bitrate[2],
     2114                          protocol);
    20982115    LOG(VB_GENERAL, LOG_INFO, QString("Loaded %1 for %2")
    20992116        .arg(tuning.GetDeviceName()).arg(chanid));
    21002117    return tuning;
    21012118}
    21022119
     2120
     2121
     2122
     2123
     2124
     2125
     2126#include "HLSReader.h"
     2127
     2128bool ChannelUtil::IsHLSPlaylist(QString url)
     2129{
     2130    QByteArray buffer;
     2131
     2132    MythSingleDownload downloader;
     2133    downloader.DownloadURL(url, &buffer, 5000, 10000);
     2134    if (!buffer.size())
     2135    {
     2136        LOG(VB_GENERAL, LOG_ERR,QString("IsHLSPlaylist - Open Failed: %1")
     2137            .arg(downloader.ErrorString()));
     2138        return false;
     2139    }
     2140
     2141    QTextStream text(&buffer);
     2142    text.setCodec("UTF-8");
     2143    return (HLSReader::IsValidPlaylist(text));
     2144}
     2145
     2146
     2147
     2148
     2149
     2150
     2151
    21032152// TODO This should be modified to load a complete channelinfo object including
    21042153//      all fields from the database
    21052154ChannelInfoList ChannelUtil::GetChannelsInternal(
  • mythtv/libs/libmythtv/channelutil.h

    diff --git a/mythtv/libs/libmythtv/channelutil.h b/mythtv/libs/libmythtv/channelutil.h
    index ddafd54..a91970b 100644
    a b class MTV_PUBLIC ChannelUtil 
    196196    static QString GetVideoFilters(uint sourceid, const QString &channum)
    197197        { return GetChannelValueStr("videofilters", sourceid, channum); }
    198198    static IPTVTuningData GetIPTVTuningData(uint chanid);
     199    static bool IsHLSPlaylist(QString url);
    199200
    200201    static ChannelInfoList GetChannels(
    201202        uint sourceid, bool visible_only,
  • mythtv/libs/libmythtv/iptvtuningdata.h

    diff --git a/mythtv/libs/libmythtv/iptvtuningdata.h b/mythtv/libs/libmythtv/iptvtuningdata.h
    index 471f54f..8dd9ce3 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 
    133148
    134149    uint GetURLCount(void) const { return 3; }
    135150
    136     bool IsHLS(void) const
    137     {
    138         return m_data_url.scheme().startsWith("http") && m_data_url.isValid();
    139     }
    140 
    141151    bool IsValid(void) const
    142152    {
    143         return m_data_url.isValid() &&
    144                (IsHLS() ||
    145                 ((m_data_url.scheme() == "rtp" || m_data_url.scheme() == "udp") && m_data_url.port() != -1) ||
    146                 m_data_url.scheme() == "rtsp"
    147                );
     153        bool ret = (m_data_url.isValid() && (IsUDP() || IsRTP() || IsRTSP() || IsHLS() || IsHTTPTS()));
     154
     155        LOG(VB_CHANNEL, LOG_DEBUG, QString("IPTVTuningdata (%1): IsValid = %2")
     156            .arg(m_data_url.toString())
     157            .arg(ret ? "true" : "false"));
     158
     159        return ret;
     160    }
     161
     162    bool IsUDP(void) const
     163    {
     164        return (m_protocol == udp);
     165    }
     166
     167    bool IsRTP(void) const
     168    {
     169        return (m_protocol == rtp);
     170    }
     171
     172    bool IsRTSP(void) const
     173    {
     174        return (m_protocol == rtsp);
     175    }
     176
     177    bool IsHLS() const
     178    {
     179        return (m_protocol == http_hls);
     180    }
     181
     182    bool IsHTTPTS() const
     183    {
     184        return (m_protocol == http_ts);
    148185    }
    149186
    150187  public:
    class MTV_PUBLIC IPTVTuningData 
    153190    QUrl m_fec_url0;
    154191    QUrl m_fec_url1;
    155192    uint m_bitrate[3];
     193    IPTVProtocol m_protocol;
    156194};
    157195
    158196#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 c0a3145..d0091bf 100644
    a b using_backend { 
    651651    SOURCES += recorders/rtp/packetbuffer.cpp
    652652    SOURCES += recorders/rtp/rtppacketbuffer.cpp
    653653
     654    # Support for HTTP TS streams
     655    HEADERS += recorders/httptsstreamhandler.h
     656    SOURCES += recorders/httptsstreamhandler.cpp
     657
    654658    # Suppport for HLS recorder
    655659    HEADERS += recorders/hlsstreamhandler.h
    656660    SOURCES += recorders/hlsstreamhandler.cpp
  • mythtv/libs/libmythtv/mythsystemevent.cpp

    diff --git a/mythtv/libs/libmythtv/mythsystemevent.cpp b/mythtv/libs/libmythtv/mythsystemevent.cpp
    index 02de65e..240d74b 100644
    a b MythSystemEventEditor::MythSystemEventEditor(MythScreenStack *parent, 
    402402        tr("Playback unpaused");
    403403    m_settings["EventCmdPlayChanged"]          = // PLAY_CHANGED
    404404        tr("Playback program changed");
     405    m_settings["EventCmdTuningSignalTimeout"]  = // TUNING_SIGNAL_TIMEOUT
     406        tr("Tuning signal waiting");
    405407    m_settings["EventCmdMasterStarted"]        = // MASTER_STARTED
    406408        tr("Master backend started");
    407409    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 194c85f..4c3db87 100644
    a b bool HLSReader::IsValidPlaylist(QTextStream & text) 
    252252        line = text.readLine();
    253253        if (line.isNull())
    254254            break;
    255         LOG(VB_RECORD, LOG_DEBUG, LOC +
     255        LOG(VB_RECORD, LOG_DEBUG, /*LOC + */
    256256            QString("IsValidPlaylist: |'%1'").arg(line));
    257257        if (line.startsWith(QLatin1String("#EXT-X-TARGETDURATION"))  ||
    258258            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 58a8380..17ec133 100644
    a b class HLSReader 
    6464    static void CancelURL(const QString &url);
    6565    static void CancelURL(const QStringList &urls);
    6666    static QString RelativeURI(const QString surl, const QString spath);
     67    static bool IsValidPlaylist(QTextStream & text);
    6768
    6869  protected:
    6970    bool FatalError(void) const { return m_fatal; }
    class HLSReader 
    8182
    8283  private:
    8384    static QString DecodedURI(const QString uri);
    84 
    85     bool IsValidPlaylist(QTextStream & text);
    86 
    8785    static QString ParseAttributes(const QString& line, const char* attr);
    8886    static bool ParseDecimalValue(const QString& line, int& target);
    8987    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 188c747..45cb37e 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 0b21319..8531372 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) 
    103104    {
    104105        m_stream_handler = HLSStreamHandler::Get(m_last_tuning);
    105106    }
     107    else if (m_last_tuning.IsHTTPTS())
     108    {
     109        m_stream_handler = HTTPTSStreamHandler::Get(m_last_tuning);
     110    }
    106111    else
    107112    {
    108113        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 0f2211b..c0c08f2 100644
    a b class IPTVChannel : QObject, public DTVChannel 
    6060    IPTVTuningData     m_last_tuning;
    6161    IPTVStreamHandler *m_stream_handler;
    6262    MPEGStreamData    *m_stream_data;
     63    QString            m_videodev;
    6364};
    6465
    6566#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 89c14f3..8de7e76 100644
    a b IPTVStreamHandler::IPTVStreamHandler(const IPTVTuningData &tuning) : 
    117117{
    118118    memset(m_sockets, 0, sizeof(m_sockets));
    119119    memset(m_read_helpers, 0, sizeof(m_read_helpers));
    120     m_use_rtp_streaming = m_tuning.GetDataURL().scheme().toUpper() == "RTP";
     120    m_use_rtp_streaming = m_tuning.IsRTP();
    121121}
    122122
    123123void IPTVStreamHandler::run(void)
    void IPTVStreamHandler::run(void) 
    133133    // Setup
    134134    CetonRTSP *rtsp = NULL;
    135135    IPTVTuningData tuning = m_tuning;
    136     if (m_tuning.GetURL(0).scheme().toLower() == "rtsp")
     136    if(m_tuning.IsRTSP())
    137137    {
    138138        rtsp = new CetonRTSP(m_tuning.GetURL(0));
    139139
    void IPTVStreamHandler::run(void) 
    167167        urltuned.setScheme("rtp");
    168168        urltuned.setPort(0);
    169169        tuning = IPTVTuningData(urltuned.toString(), 0, IPTVTuningData::kNone,
    170                                 urltuned.toString(), 0, "", 0);
     170                                urltuned.toString(), 0, "", 0, IPTVTuningData::rtp);
    171171    }
    172172
    173173    bool error = false;
     174
    174175    int start_port = 0;
    175176    for (uint i = 0; i < IPTV_SOCKET_COUNT; i++)
    176177    {
    void IPTVStreamHandler::run(void) 
    281282
    282283    if (!error)
    283284    {
    284         if (m_use_rtp_streaming)
     285        if (m_tuning.IsRTP() || m_tuning.IsRTSP())
    285286            m_buffer = new RTPPacketBuffer(tuning.GetBitrate(0));
    286287        else
    287288            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 ceb5605..84fdf3d 100644
    a b TVRec::TVRec(int capturecardnum) 
    8484       // Various components TVRec coordinates
    8585    : recorder(NULL), channel(NULL), signalMonitor(NULL),
    8686      scanner(NULL),
     87      signalEventCmdSent(false),
    8788      // Various threads
    8889      eventThread(new MThread("TVRecEvent", this)),
    8990      recorderThread(NULL),
    void TVRec::TuningFrequency(const TuningRequest &request) 
    38823883                        expire.addMSecs(genOpt.channel_timeout * 2);
    38833884                }
    38843885                signalMonitorCheckCnt = 0;
     3886
     3887                //System Event TUNING_TIMEOUT deadline
     3888                QDateTime expire = MythDate::current();
     3889                signalEventCmdTimeout = expire.addMSecs(genOpt.channel_timeout);
     3890                signalEventCmdSent = false;
    38853891            }
    38863892        }
    38873893
    void TVRec::TuningFrequency(const TuningRequest &request) 
    39203926MPEGStreamData *TVRec::TuningSignalCheck(void)
    39213927{
    39223928    RecStatusType newRecStatus = rsRecording;
     3929
     3930    if ((signalMonitor->IsErrored() ||
     3931         MythDate::current() > signalEventCmdTimeout) &&
     3932         !signalEventCmdSent)
     3933    {
     3934        gCoreContext->SendSystemEvent(QString("TUNING_SIGNAL_TIMEOUT CARDID %1").arg(cardid));
     3935        signalEventCmdSent=true;
     3936    }
     3937
    39233938    if (signalMonitor->IsAllGood())
    39243939    {
    39253940        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 0c2f00c..b0cce23 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