MythTV  master
HLSStream.cpp
Go to the documentation of this file.
1 #include <unistd.h>
2 
3 #include <array>
4 #include <utility>
5 
7 
8 #include "HLSReader.h"
9 #include "HLSStream.h"
10 
11 #define LOC QString("%1 stream: ").arg(m_m3u8Url)
12 
13 HLSRecStream::HLSRecStream(int seq, uint64_t bitrate, QString m3u8_url, QString segment_base_url)
14  : m_id(seq),
15  m_bitrate(bitrate),
16  m_m3u8Url(std::move(m3u8_url)),
17  m_segmentBaseUrl(std::move(segment_base_url))
18 {
19  LOG(VB_RECORD, LOG_DEBUG, LOC + "ctor");
20 }
21 
23 {
24  LOG(VB_RECORD, LOG_DEBUG, LOC + "dtor");
25 
26 #ifdef USING_LIBCRYPTO
27  AESKeyMap::iterator Iaes;
28 
29  for (Iaes = m_aesKeys.begin(); Iaes != m_aesKeys.end(); ++Iaes)
30  delete *Iaes;
31 #endif
32 }
33 
34 QString HLSRecStream::toString(void) const
35 {
36  return QString("%1 bitrate %2").arg(Id()).arg(Bitrate());
37 }
38 
39 #ifdef USING_LIBCRYPTO
40 bool HLSRecStream::DownloadKey(MythSingleDownload& downloader,
41  const QString& keypath, AES_KEY* aeskey)
42 {
43  QByteArray key;
44 
45 #ifdef HLS_USE_MYTHDOWNLOADMANAGER // MythDownloadManager leaks memory
46  bool ret = HLSReader::DownloadURL(keypath, &key);
47 #else
48  bool ret = downloader.DownloadURL(keypath, &key);
49 #endif
50 
51  if (!ret || key.size() != AES_BLOCK_SIZE)
52  {
53  if (ret)
54  {
55  LOG(VB_RECORD, LOG_ERR, LOC +
56  QString("The AES key loaded doesn't have the right size (%1)")
57  .arg(key.size()));
58  }
59  else
60  {
61  LOG(VB_RECORD, LOG_ERR, LOC + "Failed to download AES key: " +
62  downloader.ErrorString());
63  }
64  return false;
65  }
66  AES_set_decrypt_key((const unsigned char*)key.constData(),
67  128, aeskey);
68  return true;
69 }
70 
71 bool HLSRecStream::DecodeData(MythSingleDownload& downloader,
72  const QByteArray& IV, const QString& keypath,
73  QByteArray& data, int64_t sequence)
74 {
75  AESKeyMap::iterator Ikey = m_aesKeys.find(keypath);
76  if (Ikey == m_aesKeys.end())
77  {
78  auto* key = new AES_KEY;
79  DownloadKey(downloader, keypath, key);
80  Ikey = m_aesKeys.insert(keypath, key);
81  if (Ikey == m_aesKeys.end())
82  {
83  LOG(VB_RECORD, LOG_ERR, LOC +
84  "DecodeData: Unable to add AES key to map");
85  return false;
86  }
87  }
88 
89  /* Decrypt data using AES-128 */
90  int aeslen = data.size() & ~0xf;
91  std::array<uint8_t,AES_BLOCK_SIZE> iv {};
92  auto *decrypted_data = new uint8_t[data.size()];
93  if (IV.isEmpty())
94  {
95  /*
96  * If the EXT-X-KEY tag does not have the IV attribute,
97  * implementations MUST use the sequence number of the
98  * media file as the IV when encrypting or decrypting that
99  * media file. The big-endian binary representation of
100  * the sequence number SHALL be placed in a 16-octet
101  * buffer and padded (on the left) with zeros.
102  */
103  iv[15] = sequence & 0xff;
104  iv[14] = (sequence >> 8) & 0xff;
105  iv[13] = (sequence >> 16) & 0xff;
106  iv[12] = (sequence >> 24) & 0xff;
107  }
108  else
109  {
110  std::copy(IV.cbegin(), IV.cend(), iv.data());
111  }
112  AES_cbc_encrypt((unsigned char*)data.constData(),
113  decrypted_data, aeslen,
114  *Ikey, iv.data(), AES_DECRYPT);
115  std::copy(data.cbegin() + aeslen, data.cend(), decrypted_data + aeslen);
116 
117  // remove the PKCS#7 padding from the buffer
118  int pad = decrypted_data[data.size()-1];
119  if (pad <= 0 || pad > AES_BLOCK_SIZE)
120  {
121  LOG(VB_RECORD, LOG_ERR, LOC +
122  QString("bad padding character (0x%1)")
123  .arg(pad, 0, 16, QLatin1Char('0')));
124  delete[] decrypted_data;
125  return false;
126  }
127  aeslen = data.size() - pad;
128  data = QByteArray(reinterpret_cast<char*>(decrypted_data), aeslen);
129  delete[] decrypted_data;
130 
131  return true;
132 }
133 #endif // USING_LIBCRYPTO
134 
135 void HLSRecStream::AverageBandwidth(int64_t bandwidth)
136 {
137  // Average the last 20 segments
138  if (m_bandwidthSegs.size() > 19)
139  m_sumBandwidth -= m_bandwidthSegs.dequeue();
140  m_bandwidthSegs.enqueue(bandwidth);
141  m_sumBandwidth += bandwidth;
143 }
144 
145 std::chrono::seconds HLSRecStream::Duration(void) const
146 {
147  QMutexLocker lock(&m_lock);
148  return m_duration;
149 }
150 
152 {
153  return this->Bitrate() < b.Bitrate();
154 }
155 
157 {
158  return this->Bitrate() > b.Bitrate();
159 }
160 
161 #ifdef USING_LIBCRYPTO
162 bool HLSRecStream::SetAESIV(QString line)
163 {
164  /*
165  * If the EXT-X-KEY tag has the IV attribute, implementations MUST use
166  * the attribute value as the IV when encrypting or decrypting with that
167  * key. The value MUST be interpreted as a 128-bit hexadecimal number
168  * and MUST be prefixed with 0x or 0X.
169  */
170  if (!line.startsWith(QLatin1String("0x"), Qt::CaseInsensitive))
171  return false;
172  if (line.size() % 2)
173  {
174  // not even size, pad with front 0
175  line.insert(2, QLatin1String("0"));
176  }
177 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
178  int padding = std::max(0, AES_BLOCK_SIZE - (line.size() - 2));
179 #else
180  int padding = std::max(0LL, AES_BLOCK_SIZE - (line.size() - 2));
181 #endif
182  QByteArray ba = QByteArray(padding, 0x0);
183  ba.append(QByteArray::fromHex(QByteArray(line.toLatin1().constData() + 2)));
184  m_aesIV = ba;
185  m_ivLoaded = true;
186  return true;
187 }
188 #endif // USING_LIBCRYPTO
HLSRecStream::~HLSRecStream
~HLSRecStream(void)
Definition: HLSStream.cpp:22
LOC
#define LOC
Definition: HLSStream.cpp:11
HLSRecStream::Duration
std::chrono::seconds Duration(void) const
Definition: HLSStream.cpp:145
HLSRecStream::HLSRecStream
HLSRecStream(int seq, uint64_t bitrate, QString m3u8_url, QString segment_base_url)
Definition: HLSStream.cpp:13
HLSRecStream::operator<
bool operator<(const HLSRecStream &b) const
Definition: HLSStream.cpp:151
x0
static int x0
Definition: mythsocket.cpp:49
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
HLSRecStream::m_duration
std::chrono::seconds m_duration
Definition: HLSStream.h:84
HLSRecStream
Definition: HLSStream.h:15
mythlogging.h
HLSStream.h
MythFile::copy
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
Definition: mythmiscutil.cpp:263
MythSingleDownload::DownloadURL
bool DownloadURL(const QUrl &url, QByteArray *buffer, std::chrono::seconds timeout=30s, uint redirs=0, qint64 maxsize=0, QString *final_url=nullptr)
Definition: mythsingledownload.cpp:14
HLSRecStream::AverageBandwidth
uint64_t AverageBandwidth(void) const
Definition: HLSStream.h:35
HLSRecStream::toString
QString toString(void) const
Definition: HLSStream.cpp:34
HLSRecStream::m_bandwidthSegs
QQueue< int64_t > m_bandwidthSegs
Definition: HLSStream.h:88
MythSingleDownload
Definition: mythsingledownload.h:25
HLSRecStream::Bitrate
uint64_t Bitrate(void) const
Definition: HLSStream.h:36
std
Definition: mythchrono.h:23
HLSRecStream::m_lock
QMutex m_lock
Definition: HLSStream.h:92
HLSRecStream::operator>
bool operator>(const HLSRecStream &b) const
Definition: HLSStream.cpp:156
MythSingleDownload::ErrorString
QString ErrorString(void) const
Definition: mythsingledownload.h:36
HLSRecStream::m_sumBandwidth
double m_sumBandwidth
Definition: HLSStream.h:87
HLSRecStream::Id
int Id(void) const
Definition: HLSStream.h:30
HLSRecStream::m_bandwidth
int64_t m_bandwidth
Definition: HLSStream.h:86
HLSReader.h