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"
13 #include "mythlogging.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 #define TS_SIZE 188
20 #define 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  HLSStreamHandler* 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(inputid).arg(devkey).arg(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(inputid).arg(devkey).arg(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(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  m_hls = new HLSReader();
104  m_readbuffer = new uint8_t[BUFFER_SIZE];
105 }
106 
108 {
109  LOG(VB_CHANNEL, LOG_INFO, LOC + "dtor");
110  Stop();
111  delete m_hls;
112  delete[] m_readbuffer;
113 }
114 
116 {
117  RunProlog();
118 
119  QString url = m_tuning.GetURL(0).toString();
120  int err_cnt = 0;
121  int nil_cnt = 0;
122  int open_sleep = 500;
123 
124  LOG(VB_GENERAL, LOG_INFO, LOC + "run() -- begin");
125 
126  SetRunning(true, false, false);
127 
128  if (!m_hls)
129  return;
130  m_hls->Throttle(false);
131 
132  int remainder = 0;
133  while (m_running_desired)
134  {
135  if (!m_hls->IsOpen(url))
136  {
137  if (!m_hls->Open(url, m_tuning.GetBitrate(0)))
138  {
139  if (m_hls->FatalError())
140  break;
141  std::this_thread::sleep_for(std::chrono::milliseconds(open_sleep));
142  if (open_sleep < 20000)
143  open_sleep += 500;
144  continue;
145  }
146  open_sleep = 500;
148  m_throttle = false;
149  }
150 
151  int size = m_hls->Read(&m_readbuffer[remainder],
152  BUFFER_SIZE - remainder);
153 
154  size += remainder;
155 
156  if (size < 0)
157  {
158  // error
159  if (++err_cnt > 10)
160  {
161  LOG(VB_RECORD, LOG_ERR, LOC + "HLSReader failed");
162  Stop();
163  break;
164  }
165  continue;
166  }
167  err_cnt = 0;
168 
169  if (size == 0)
170  {
171  if (nil_cnt < 4)
172  ++nil_cnt;
173  // range .25 to 1 second
174  std::this_thread::sleep_for(std::chrono::milliseconds(nil_cnt *250));
175  continue;
176  }
177  nil_cnt = 0;
178 
179  if (m_readbuffer[0] != 0x47)
180  {
181  LOG(VB_RECORD, LOG_INFO, LOC +
182  QString("Packet not starting with SYNC Byte (got 0x%1)")
183  .arg((char)m_readbuffer[0], 2, QLatin1Char('0')));
184  continue;
185  }
186 
187  {
188  QMutexLocker locker(&m_listener_lock);
189  HLSStreamHandler::StreamDataList::const_iterator sit;
190  sit = m_stream_data_list.begin();
191  for (; sit != m_stream_data_list.end(); ++sit)
192  remainder = sit.key()->ProcessData(m_readbuffer, size);
193  }
194 
195  if (remainder > 0)
196  {
197  if (size > remainder) // unprocessed bytes
198  memmove(m_readbuffer, &(m_readbuffer[size - remainder]),
199  remainder);
200 
201  LOG(VB_RECORD, LOG_INFO, LOC +
202  QString("data_length = %1 remainder = %2")
203  .arg(size).arg(remainder));
204  }
205 
206  if (m_hls->IsThrottled())
207  std::this_thread::sleep_for(std::chrono::seconds(1));
208  else if (size < BUFFER_SIZE)
209  {
210  LOG(VB_RECORD, LOG_DEBUG, LOC +
211  QString("Requested %1 bytes, got %2 bytes.")
212  .arg(BUFFER_SIZE).arg(size));
213  // hundredth of a second.
214  std::this_thread::sleep_for(std::chrono::milliseconds(10));
215  }
216  else
217  std::this_thread::sleep_for(std::chrono::milliseconds(1));
218  }
219 
220  m_hls->Throttle(false);
221 
222  SetRunning(false, false, false);
223  RunEpilog();
224 
225  LOG(VB_GENERAL, LOG_INFO, LOC + "run() -- done");
226 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
void Throttle(bool val)
Definition: HLSReader.cpp:183
int Read(uint8_t *buffer, int len)
Definition: HLSReader.cpp:197
QUrl GetURL(uint i) const
IPTVTuningData m_tuning
static QMap< QString, uint > s_hlshandlers_refcnt
volatile bool m_running_desired
uint8_t * m_readbuffer
QString m_device
bool IsThrottled(void) const
Definition: HLSReader.h:50
HLSReader(void)=default
bool FatalError(void) const
Definition: HLSReader.h:53
#define LOC
-*- Mode: c++ -*- HLSStreamHandler Copyright (c) 2013 Bubblestuff Pty Ltd based on IPTVStreamHandler ...
unsigned int uint
Definition: compat.h:140
QMutex m_listener_lock
virtual ~HLSStreamHandler(void)
static HLSStreamHandler * Get(const IPTVTuningData &tuning, int inputid)
void SetRunning(bool running, bool using_buffering, bool using_section_reader)
void Stop(void)
void Start(void)
uint GetBitrate(uint i) const
static QMutex s_hlshandlers_lock
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
QString GetDeviceName(void) const
#define BUFFER_SIZE
bool Open(const QString &m3u, int bitrate_index=0)
Definition: HLSReader.cpp:36
QString GetDeviceKey(void) const
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
StreamDataList m_stream_data_list
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
static void Return(HLSStreamHandler *&ref, int inputid)
static QMap< QString, HLSStreamHandler * > s_hlshandlers
HLSStreamHandler(const IPTVTuningData &tuning, int inputid)
bool IsOpen(const QString &url) const
Definition: HLSReader.h:51