MythTV  master
hlsstreamhandler.cpp
Go to the documentation of this file.
1 
8 #include <chrono> // for milliseconds
9 #include <thread> // for sleep_for
10 
11 // MythTV headers
12 #include "hlsstreamhandler.h"
15 
16 #define LOC QString("HLSSH[%1](%2): ").arg(m_inputId).arg(m_device)
17 
18 // BUFFER_SIZE is a multiple of TS_SIZE
19 static constexpr qint64 TS_SIZE { 188 };
20 static constexpr qint64 BUFFER_SIZE { 512 * TS_SIZE };
21 
22 QMap<QString,HLSStreamHandler*> HLSStreamHandler::s_hlshandlers;
25 
27 {
28  QMutexLocker locker(&s_hlshandlers_lock);
29 
30  QString devkey = tuning.GetDeviceKey();
31 
32  QMap<QString,HLSStreamHandler*>::iterator it = s_hlshandlers.find(devkey);
33 
34  if (it == s_hlshandlers.end())
35  {
36  auto* newhandler = new HLSStreamHandler(tuning, inputid);
37  newhandler->Start();
38  s_hlshandlers[devkey] = newhandler;
39  s_hlshandlers_refcnt[devkey] = 1;
40 
41  LOG(VB_RECORD, LOG_INFO,
42  QString("HLSSH[%1]: Creating new stream handler %2 for %3")
43  .arg(QString::number(inputid), devkey, tuning.GetDeviceName()));
44  }
45  else
46  {
47  s_hlshandlers_refcnt[devkey]++;
48  uint rcount = s_hlshandlers_refcnt[devkey];
49  LOG(VB_RECORD, LOG_INFO,
50  QString("HLSSH[%1]: Using existing stream handler %2 for %3")
51  .arg(QString::number(inputid), devkey, tuning.GetDeviceName()) +
52  QString(" (%1 in use)").arg(rcount));
53  }
54 
55  return s_hlshandlers[devkey];
56 }
57 
58 void HLSStreamHandler::Return(HLSStreamHandler* & ref, int inputid)
59 {
60  QMutexLocker locker(&s_hlshandlers_lock);
61 
62  QString devname = ref->m_device;
63 
64  QMap<QString,uint>::iterator rit = s_hlshandlers_refcnt.find(devname);
65  if (rit == s_hlshandlers_refcnt.end())
66  return;
67 
68  LOG(VB_RECORD, LOG_INFO, QString("HLSSH[%1]: Return(%2) has %3 handlers")
69  .arg(inputid).arg(devname).arg(*rit));
70 
71  if (*rit > 1)
72  {
73  ref = nullptr;
74  (*rit)--;
75  return;
76  }
77 
78  QMap<QString,HLSStreamHandler*>::iterator it = s_hlshandlers.find(devname);
79  if ((it != s_hlshandlers.end()) && (*it == ref))
80  {
81  LOG(VB_RECORD, LOG_INFO, QString("HLSSH[%1]: Closing handler for %2")
82  .arg(inputid).arg(devname));
83  ref->Stop();
84  LOG(VB_RECORD, LOG_DEBUG, QString("HLSSH[%1]: handler for %2 stopped")
85  .arg(inputid).arg(devname));
86  delete *it;
87  s_hlshandlers.erase(it);
88  }
89  else
90  {
91  LOG(VB_GENERAL, LOG_ERR,
92  QString("HLSSH[%1] Error: Couldn't find handler for %2")
93  .arg(inputid).arg(devname));
94  }
95 
96  s_hlshandlers_refcnt.erase(rit);
97  ref = nullptr;
98 }
99 
101  : IPTVStreamHandler(tuning, inputid)
102 {
103  LOG(VB_GENERAL, LOG_INFO, LOC + "ctor");
104  m_hls = new HLSReader();
105  m_readbuffer = new uint8_t[BUFFER_SIZE];
106 }
107 
109 {
110  LOG(VB_CHANNEL, LOG_INFO, LOC + "dtor");
111  Stop();
112  delete m_hls;
113  delete[] m_readbuffer;
114 }
115 
117 {
118  RunProlog();
119 
120  QString url = m_tuning.GetURL(0).toString();
121  int err_cnt = 0;
122  int nil_cnt = 0;
123  std::chrono::milliseconds open_sleep = 500ms;
124 
125  LOG(VB_GENERAL, LOG_INFO, LOC + "run() -- begin");
126 
127  SetRunning(true, false, false);
128 
129  if (!m_hls)
130  return;
131  m_hls->Throttle(false);
132 
133  qint64 remainder = 0;
134  while (m_runningDesired)
135  {
136  if (!m_hls->IsOpen(url))
137  {
138  if (!m_hls->Open(url, m_tuning.GetBitrate(0)))
139  {
140  if (m_hls->FatalError())
141  break;
142  std::this_thread::sleep_for(open_sleep);
143  if (open_sleep < 20s)
144  open_sleep += 500ms;
145  continue;
146  }
147  open_sleep = 500ms;
149  m_throttle = false;
150  }
151 
152  qint64 size = m_hls->Read(&m_readbuffer[remainder],
153  BUFFER_SIZE - remainder);
154 
155  size += remainder;
156 
157  if (size < 0)
158  {
159  // error
160  if (++err_cnt > 10)
161  {
162  LOG(VB_RECORD, LOG_ERR, LOC + "HLSReader failed");
163  Stop();
164  break;
165  }
166  continue;
167  }
168  err_cnt = 0;
169 
170  if (size == 0)
171  {
172  if (nil_cnt < 4)
173  ++nil_cnt;
174  // range .25 to 1 second
175  std::this_thread::sleep_for(nil_cnt * 250ms);
176  continue;
177  }
178  nil_cnt = 0;
179 
180  if (m_readbuffer[0] != 0x47)
181  {
182  LOG(VB_RECORD, LOG_INFO, LOC +
183  QString("Packet not starting with SYNC Byte (got 0x%1)")
184  .arg((char)m_readbuffer[0], 2, 16, QLatin1Char('0')));
185  continue;
186  }
187 
188  {
189  QMutexLocker locker(&m_listenerLock);
190  for (auto sit = m_streamDataList.cbegin(); sit != m_streamDataList.cend(); ++sit)
191  remainder = sit.key()->ProcessData(m_readbuffer, size);
192  }
193 
194  if (remainder > 0)
195  {
196  if (size > remainder) // unprocessed bytes
197  memmove(m_readbuffer, &(m_readbuffer[size - remainder]),
198  remainder);
199 
200  LOG(VB_RECORD, LOG_INFO, LOC +
201  QString("data_length = %1 remainder = %2")
202  .arg(size).arg(remainder));
203  }
204 
205  if (m_hls->IsThrottled())
206  std::this_thread::sleep_for(1s);
207  else if (size < BUFFER_SIZE)
208  {
209  LOG(VB_RECORD, LOG_DEBUG, LOC +
210  QString("Requested %1 bytes, got %2 bytes.")
211  .arg(BUFFER_SIZE).arg(size));
212  // hundredth of a second.
213  std::this_thread::sleep_for(10ms);
214  }
215  else
216  std::this_thread::sleep_for(1ms);
217  }
218 
219  m_hls->Throttle(false);
220 
221  SetRunning(false, false, false);
222  RunEpilog();
223 
224  LOG(VB_GENERAL, LOG_INFO, LOC + "run() -- done");
225 }
IPTVStreamHandler::m_tuning
IPTVTuningData m_tuning
Definition: iptvstreamhandler.h:102
HLSReader::FatalError
bool FatalError(void) const
Definition: HLSReader.h:53
StreamHandler::SetRunning
void SetRunning(bool running, bool using_buffering, bool using_section_reader)
Definition: streamhandler.cpp:173
StreamHandler::Stop
void Stop(void)
Definition: streamhandler.cpp:155
HLSReader::IsOpen
bool IsOpen(const QString &url) const
Definition: HLSReader.h:51
LOC
#define LOC
-*- Mode: c++ -*- HLSStreamHandler Copyright (c) 2013 Bubblestuff Pty Ltd based on IPTVStreamHandler ...
Definition: hlsstreamhandler.cpp:16
HLSReader::IsThrottled
bool IsThrottled(void) const
Definition: HLSReader.h:50
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MThread::RunProlog
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
HLSStreamHandler::HLSStreamHandler
HLSStreamHandler(const HLSStreamHandler &)=delete
HLSStreamHandler
Definition: hlsstreamhandler.h:23
HLSStreamHandler::m_throttle
bool m_throttle
Definition: hlsstreamhandler.h:42
IPTVTuningData::GetDeviceKey
QString GetDeviceKey(void) const
Definition: iptvtuningdata.h:102
HLSStreamHandler::m_hls
HLSReader * m_hls
Definition: hlsstreamhandler.h:40
StreamHandler::m_listenerLock
QRecursiveMutex m_listenerLock
Definition: streamhandler.h:144
mythlogging.h
IPTVTuningData::GetDeviceName
QString GetDeviceName(void) const
Definition: iptvtuningdata.h:113
HLSStreamHandler::s_hlshandlers
static QMap< QString, HLSStreamHandler * > s_hlshandlers
Definition: hlsstreamhandler.h:46
IPTVTuningData
Definition: iptvtuningdata.h:21
MThread::RunEpilog
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
HLSReader::Read
qint64 Read(uint8_t *buffer, qint64 len)
Definition: HLSReader.cpp:210
TS_SIZE
static constexpr qint64 TS_SIZE
Definition: hlsstreamhandler.cpp:19
HLSStreamHandler::s_hlshandlers_refcnt
static QMap< QString, uint > s_hlshandlers_refcnt
Definition: hlsstreamhandler.h:47
uint
unsigned int uint
Definition: compat.h:81
HLSStreamHandler::Get
static HLSStreamHandler * Get(const IPTVTuningData &tuning, int inputid)
Definition: hlsstreamhandler.cpp:26
HLSReader
Definition: HLSReader.h:34
HLSStreamHandler::m_readbuffer
uint8_t * m_readbuffer
Definition: hlsstreamhandler.h:41
HLSStreamHandler::~HLSStreamHandler
~HLSStreamHandler(void) override
Definition: hlsstreamhandler.cpp:108
HLSStreamHandler::Return
static void Return(HLSStreamHandler *&ref, int inputid)
Definition: hlsstreamhandler.cpp:58
hlsstreamhandler.h
StreamHandler::m_runningDesired
volatile bool m_runningDesired
Definition: streamhandler.h:119
StreamHandler::m_streamDataList
StreamDataList m_streamDataList
Definition: streamhandler.h:145
HLSStreamHandler::s_hlshandlers_lock
static QMutex s_hlshandlers_lock
Definition: hlsstreamhandler.h:45
HLSReader::Throttle
void Throttle(bool val)
Definition: HLSReader.cpp:196
HLSStreamHandler::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: hlsstreamhandler.cpp:116
IPTVStreamHandler
Definition: iptvstreamhandler.h:79
HLSReader::Open
bool Open(const QString &m3u, int bitrate_index=0)
Definition: HLSReader.cpp:34
BUFFER_SIZE
static constexpr qint64 BUFFER_SIZE
Definition: hlsstreamhandler.cpp:20
StreamHandler::m_device
QString m_device
Definition: streamhandler.h:111
IPTVTuningData::GetBitrate
uint GetBitrate(uint i) const
Definition: iptvtuningdata.h:152
IPTVTuningData::GetURL
QUrl GetURL(uint i) const
Definition: iptvtuningdata.h:142
HLSReader.h