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(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  int open_sleep = 500;
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  int remainder = 0;
134  while (m_running_desired)
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(std::chrono::milliseconds(open_sleep));
143  if (open_sleep < 20000)
144  open_sleep += 500;
145  continue;
146  }
147  open_sleep = 500;
149  m_throttle = false;
150  }
151 
152  int 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(std::chrono::milliseconds(nil_cnt *250));
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, QLatin1Char('0')));
185  continue;
186  }
187 
188  {
189  QMutexLocker locker(&m_listener_lock);
190  HLSStreamHandler::StreamDataList::const_iterator sit;
191  sit = m_stream_data_list.begin();
192  for (; sit != m_stream_data_list.end(); ++sit)
193  remainder = sit.key()->ProcessData(m_readbuffer, size);
194  }
195 
196  if (remainder > 0)
197  {
198  if (size > remainder) // unprocessed bytes
199  memmove(m_readbuffer, &(m_readbuffer[size - remainder]),
200  remainder);
201 
202  LOG(VB_RECORD, LOG_INFO, LOC +
203  QString("data_length = %1 remainder = %2")
204  .arg(size).arg(remainder));
205  }
206 
207  if (m_hls->IsThrottled())
208  std::this_thread::sleep_for(std::chrono::seconds(1));
209  else if (size < BUFFER_SIZE)
210  {
211  LOG(VB_RECORD, LOG_DEBUG, LOC +
212  QString("Requested %1 bytes, got %2 bytes.")
213  .arg(BUFFER_SIZE).arg(size));
214  // hundredth of a second.
215  std::this_thread::sleep_for(std::chrono::milliseconds(10));
216  }
217  else
218  std::this_thread::sleep_for(std::chrono::milliseconds(1));
219  }
220 
221  m_hls->Throttle(false);
222 
223  SetRunning(false, false, false);
224  RunEpilog();
225 
226  LOG(VB_GENERAL, LOG_INFO, LOC + "run() -- done");
227 }
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:191
int Read(uint8_t *buffer, int len)
Definition: HLSReader.cpp:205
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