MythTV  master
httptsstreamhandler.cpp
Go to the documentation of this file.
1 // MythTV headers
2 #include "httptsstreamhandler.h"
3 #include "mythlogging.h"
4 
5 #include <chrono> // for milliseconds
6 #include <thread> // for sleep_for
7 
8 using namespace std::chrono_literals;
9 
10 #define LOC QString("HTTPTSSH[%1](%2): ").arg(m_inputId).arg(m_device)
11 
12 // BUFFER_SIZE is a multiple of TS_SIZE
13 #define TS_SIZE 188
14 #define BUFFER_SIZE (512 * TS_SIZE)
15 
16 QMap<QString, HTTPTSStreamHandler*> HTTPTSStreamHandler::s_httphandlers;
19 
21  int inputid)
22 {
23  QMutexLocker locker(&s_httphandlers_lock);
24 
25  QString devkey = tuning.GetDeviceKey();
26 
27  QMap<QString,HTTPTSStreamHandler*>::iterator it = s_httphandlers.find(devkey);
28 
29  if (it == s_httphandlers.end())
30  {
31  auto* newhandler = new HTTPTSStreamHandler(tuning, inputid);
32  newhandler->Start();
33  s_httphandlers[devkey] = newhandler;
34  s_httphandlers_refcnt[devkey] = 1;
35 
36  LOG(VB_RECORD, LOG_INFO,
37  QString("HTTPTSSH[%1]: Creating new stream handler %2 for %3")
38  .arg(inputid).arg(devkey).arg(tuning.GetDeviceName()));
39  }
40  else
41  {
42  s_httphandlers_refcnt[devkey]++;
43  uint rcount = s_httphandlers_refcnt[devkey];
44  LOG(VB_RECORD, LOG_INFO,
45  QString("HTTPTSSH[%1]: Using existing stream handler %2 for %3")
46  .arg(inputid).arg(devkey).arg(tuning.GetDeviceName()) +
47  QString(" (%1 in use)").arg(rcount));
48  }
49 
50  return s_httphandlers[devkey];
51 }
52 
54 {
55  QMutexLocker locker(&s_httphandlers_lock);
56 
57  QString devname = ref->m_device;
58 
59  QMap<QString,uint>::iterator rit = s_httphandlers_refcnt.find(devname);
60  if (rit == s_httphandlers_refcnt.end())
61  return;
62 
63  LOG(VB_RECORD, LOG_INFO, QString("HTTPTSSH[%1]: Return(%2) has %3 handlers")
64  .arg(inputid).arg(devname).arg(*rit));
65 
66  if (*rit > 1)
67  {
68  ref = nullptr;
69  (*rit)--;
70  return;
71  }
72 
73  QMap<QString,HTTPTSStreamHandler*>::iterator it = s_httphandlers.find(devname);
74  if ((it != s_httphandlers.end()) && (*it == ref))
75  {
76  LOG(VB_RECORD, LOG_INFO, QString("HTTPTSSH[%1]: Closing handler for %1")
77  .arg(inputid).arg(devname));
78  ref->Stop();
79  delete *it;
80  s_httphandlers.erase(it);
81  }
82  else
83  {
84  LOG(VB_GENERAL, LOG_ERR,
85  QString("HTTPTSSH[%1] Error: Couldn't find handler for %2")
86  .arg(inputid).arg(devname));
87  }
88 
89  s_httphandlers_refcnt.erase(rit);
90  ref = nullptr;
91 }
92 
94  int inputid)
95  : IPTVStreamHandler(tuning, inputid)
96 {
97  LOG(VB_GENERAL, LOG_INFO, LOC + "ctor");
98 }
99 
101 {
102  LOG(VB_GENERAL, LOG_INFO, LOC + "dtor");
103  Stop();
104 }
105 
107 {
108  RunProlog();
109  int open_sleep = 250;
110  LOG(VB_RECORD, LOG_INFO, LOC + "run() -- begin");
111  SetRunning(true, false, false);
112 
113  m_reader = new HTTPReader(this);
114  while (m_runningDesired)
115  {
117  {
118  LOG(VB_RECORD, LOG_INFO, LOC + "DownloadStream failed to receive bytes from " + m_tuning.GetURL(0).toString());
119  std::this_thread::sleep_for(std::chrono::milliseconds(open_sleep));
120  if (open_sleep < 10000)
121  open_sleep += 250;
122  continue;
123  }
124  open_sleep = 250;
125  }
126 
127  delete m_reader;
128  SetRunning(false, false, false);
129  RunEpilog();
130  LOG(VB_RECORD, LOG_INFO, LOC + "run() -- done");
131 }
132 
133 
134 #undef LOC
135 #define LOC QString("HTTPReader(%1): ").arg(m_url)
136 
137 bool HTTPReader::DownloadStream(const QUrl& url)
138 {
139  m_url = url.toString();
140 
141  LOG(VB_RECORD, LOG_INFO, LOC + "DownloadStream -- begin");
142 
143  QMutexLocker lock(&m_lock);
144  QEventLoop event_loop;
145 
146  m_buffer = new uint8_t[BUFFER_SIZE];
147  m_size = 0;
148  m_ok = false;
149 
150  // the HTTP request
151  m_replylock.lock();
152  m_reply = m_mgr.get(QNetworkRequest(url));
153  m_replylock.unlock();
154 
155  connect(&m_timer, &QTimer::timeout, &event_loop, &QEventLoop::quit);
156  connect(m_reply, &QNetworkReply::finished, &event_loop, &QEventLoop::quit);
157  connect(m_reply,&QIODevice::readyRead, this, &HTTPReader::HttpRead);
158 
159  // Configure timeout and size limit
160  m_timer.setSingleShot(true);
161  m_timer.start(10s);
162 
163  event_loop.exec(); // blocks stack until quit() is called
164 
165  disconnect(&m_timer, &QTimer::timeout, &event_loop, &QEventLoop::quit);
166  disconnect(m_reply, &QNetworkReply::finished, &event_loop, &QEventLoop::quit);
167  disconnect(m_reply,&QIODevice::readyRead, this, &HTTPReader::HttpRead);
168 
169  if (m_timer.isActive())
170  m_timer.stop();
171 
172  QMutexLocker replylock(&m_replylock);
173  if (m_reply->error() != QNetworkReply::NoError)
174  {
175  LOG(VB_RECORD, LOG_ERR, LOC + "DownloadStream exited with " + m_reply->errorString());
176  }
177 
178  delete m_reply;
179  m_reply = nullptr;
180  delete[] m_buffer;
181  m_buffer=nullptr;
182 
183  LOG(VB_RECORD, LOG_INFO, LOC + "DownloadStream -- end");
184  return m_ok;
185 }
186 
188 {
189  m_timer.stop();
190  m_ok = true;
191  ReadBytes();
192  WriteBytes();
193 }
194 
196 {
197  QMutexLocker replylock(&m_replylock);
198  QMutexLocker bufferlock(&m_bufferlock);
199 
200  if(m_reply == nullptr || m_buffer == nullptr || m_size > BUFFER_SIZE)
201  return;
202 
203  qint64 bytesRead = m_reply->read( reinterpret_cast<char*>(m_buffer + m_size), BUFFER_SIZE - m_size);
204  m_size += (bytesRead > 0 ? bytesRead : 0);
205  LOG(VB_RECORD, LOG_DEBUG, LOC + QString("ReadBytes: %1 bytes received").arg(bytesRead));
206 }
207 
209 {
210  if (m_size < TS_SIZE)
211  return;
212 
213  QMutexLocker bufferlock(&m_bufferlock);
214  int remainder = 0;
215  {
216  QMutexLocker locker(&m_parent->m_listenerLock);
217  for (auto sit = m_parent->m_streamDataList.cbegin();
218  sit != m_parent->m_streamDataList.cend(); ++sit)
219  {
220  remainder = sit.key()->ProcessData(m_buffer, m_size);
221  }
222  }
223  LOG(VB_RECORD, LOG_DEBUG, LOC + QString("WriteBytes: %1/%2 bytes remain").arg(remainder).arg(m_size));
224 
225  /* move the remaining data to the beginning of the buffer */
226  memmove(m_buffer, m_buffer + (m_size - remainder), remainder);
227  m_size = remainder;
228 }
229 
231 {
232  QMutexLocker replylock(&m_replylock);
233  if (m_reply)
234  {
235  LOG(VB_RECORD, LOG_INFO, LOC + "Cancel: Aborting stream download");
236  m_reply->abort();
237  }
238 }
IPTVStreamHandler::m_tuning
IPTVTuningData m_tuning
Definition: iptvstreamhandler.h:103
hardwareprofile.smolt.timeout
float timeout
Definition: smolt.py:103
HTTPReader::m_url
QString m_url
Definition: httptsstreamhandler.h:57
StreamHandler::SetRunning
void SetRunning(bool running, bool using_buffering, bool using_section_reader)
Definition: streamhandler.cpp:171
StreamHandler::Stop
void Stop(void)
Definition: streamhandler.cpp:153
HTTPTSStreamHandler::~HTTPTSStreamHandler
~HTTPTSStreamHandler(void) override
Definition: httptsstreamhandler.cpp:100
BUFFER_SIZE
#define BUFFER_SIZE
Definition: httptsstreamhandler.cpp:14
HTTPTSStreamHandler::s_httphandlers
static QMap< QString, HTTPTSStreamHandler * > s_httphandlers
Definition: httptsstreamhandler.h:34
HTTPReader::m_size
int m_size
Definition: httptsstreamhandler.h:67
arg
arg(title).arg(filename).arg(doDelete))
HTTPReader::DownloadStream
bool DownloadStream(const QUrl &url)
Definition: httptsstreamhandler.cpp:137
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
MThread::RunProlog
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:198
HTTPReader::WriteBytes
void WriteBytes()
Definition: httptsstreamhandler.cpp:208
HTTPTSStreamHandler::s_httphandlers_refcnt
static QMap< QString, uint > s_httphandlers_refcnt
Definition: httptsstreamhandler.h:35
IPTVTuningData::GetDeviceKey
QString GetDeviceKey(void) const
Definition: iptvtuningdata.h:97
quit
@ quit
Definition: lirc_client.h:30
mythlogging.h
HTTPReader::ReadBytes
void ReadBytes()
Definition: httptsstreamhandler.cpp:195
HTTPReader::Cancel
void Cancel(void)
Definition: httptsstreamhandler.cpp:230
HTTPReader::m_lock
QMutex m_lock
Definition: httptsstreamhandler.h:62
IPTVTuningData::GetDeviceName
QString GetDeviceName(void) const
Definition: iptvtuningdata.h:108
HTTPTSStreamHandler
Definition: httptsstreamhandler.h:19
IPTVTuningData
Definition: iptvtuningdata.h:17
HTTPReader::m_bufferlock
QMutex m_bufferlock
Definition: httptsstreamhandler.h:64
MThread::RunEpilog
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:211
HTTPReader::m_buffer
uint8_t * m_buffer
Definition: httptsstreamhandler.h:65
LOC
#define LOC
Definition: httptsstreamhandler.cpp:135
HTTPReader::m_reply
QNetworkReply * m_reply
Definition: httptsstreamhandler.h:61
uint
unsigned int uint
Definition: compat.h:141
HTTPReader::m_mgr
QNetworkAccessManager m_mgr
Definition: httptsstreamhandler.h:60
HTTPTSStreamHandler::Get
static HTTPTSStreamHandler * Get(const IPTVTuningData &tuning, int inputid)
Definition: httptsstreamhandler.cpp:20
HTTPReader::m_ok
bool m_ok
Definition: httptsstreamhandler.h:66
HTTPReader::HttpRead
void HttpRead()
Definition: httptsstreamhandler.cpp:187
StreamHandler::m_listenerLock
QMutex m_listenerLock
Definition: streamhandler.h:141
httptsstreamhandler.h
StreamHandler::m_runningDesired
volatile bool m_runningDesired
Definition: streamhandler.h:117
StreamHandler::m_streamDataList
StreamDataList m_streamDataList
Definition: streamhandler.h:142
HTTPTSStreamHandler::Return
static void Return(HTTPTSStreamHandler *&ref, int inputid)
Definition: httptsstreamhandler.cpp:53
HTTPTSStreamHandler::HTTPTSStreamHandler
HTTPTSStreamHandler(const IPTVTuningData &tuning, int inputid)
Definition: httptsstreamhandler.cpp:93
HTTPReader::m_timer
QTimer m_timer
Definition: httptsstreamhandler.h:59
HTTPReader::m_parent
HTTPTSStreamHandler * m_parent
Definition: httptsstreamhandler.h:58
IPTVStreamHandler
Definition: iptvstreamhandler.h:81
HTTPReader::m_replylock
QMutex m_replylock
Definition: httptsstreamhandler.h:63
HTTPTSStreamHandler::HTTPReader
friend class HTTPReader
Definition: httptsstreamhandler.h:20
HTTPTSStreamHandler::m_reader
HTTPReader * m_reader
Definition: httptsstreamhandler.h:31
StreamHandler::m_device
QString m_device
Definition: streamhandler.h:109
TS_SIZE
#define TS_SIZE
Definition: httptsstreamhandler.cpp:13
IPTVTuningData::GetURL
QUrl GetURL(uint i) const
Definition: iptvtuningdata.h:137
HTTPTSStreamHandler::s_httphandlers_lock
static QMutex s_httphandlers_lock
Definition: httptsstreamhandler.h:33
HTTPTSStreamHandler::run
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: httptsstreamhandler.cpp:106