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
19static constexpr qint64 TS_SIZE { 188 };
20static constexpr qint64 BUFFER_SIZE { 2048 * TS_SIZE };
21
22QMap<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
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_RECORD, LOG_DEBUG, LOC + "ctor");
105 m_readbuffer = new uint8_t[BUFFER_SIZE];
106}
107
109{
110 LOG(VB_RECORD, LOG_DEBUG, 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_RECORD, 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(static_cast<uint>(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 {
217 std::this_thread::sleep_for(1ms);
218 }
219 }
220
221 m_hls->Throttle(false);
222
223 SetRunning(false, false, false);
224 RunEpilog();
225
226 LOG(VB_RECORD, LOG_DEBUG, LOC + "run() -- done");
227}
bool IsThrottled(void) const
Definition: HLSReader.h:49
bool IsOpen(const QString &url) const
Definition: HLSReader.h:50
bool FatalError(void) const
Definition: HLSReader.h:52
void Throttle(bool val)
Definition: HLSReader.cpp:200
qint64 Read(uint8_t *buffer, qint64 len)
Definition: HLSReader.cpp:214
bool Open(const QString &m3u, int bitrate_index=0)
Definition: HLSReader.cpp:38
static QMap< QString, uint > s_hlshandlers_refcnt
static QMutex s_hlshandlers_lock
static QMap< QString, HLSStreamHandler * > s_hlshandlers
HLSStreamHandler(const HLSStreamHandler &)=delete
static HLSStreamHandler * Get(const IPTVTuningData &tuning, int inputid)
~HLSStreamHandler(void) override
uint8_t * m_readbuffer
static void Return(HLSStreamHandler *&ref, int inputid)
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
IPTVTuningData m_tuning
QString GetDeviceKey(void) const
uint GetBitrate(uint i) const
QString GetDeviceName(void) const
QUrl GetURL(uint i) const
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
StreamDataList m_streamDataList
QString m_device
volatile bool m_runningDesired
void SetRunning(bool running, bool using_buffering, bool using_section_reader)
void Stop(void)
QRecursiveMutex m_listenerLock
unsigned int uint
Definition: freesurround.h:24
#define LOC
-*- Mode: c++ -*- HLSStreamHandler Copyright (c) 2013 Bubblestuff Pty Ltd based on IPTVStreamHandler ...
static constexpr qint64 TS_SIZE
static constexpr qint64 BUFFER_SIZE
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39