MythTV  master
HLSStream.cpp
Go to the documentation of this file.
1 #include <unistd.h>
2 
3 #include <utility>
4 
5 #include "mythlogging.h"
6 
7 #include "HLSReader.h"
8 #include "HLSStream.h"
9 
10 #define LOC QString("%1 stream: ").arg(m_m3u8Url)
11 
12 HLSRecStream::HLSRecStream(int seq, uint64_t bitrate, QString m3u8_url, QString segment_base_url)
13  : m_id(seq),
14  m_bitrate(bitrate),
15  m_m3u8Url(std::move(m3u8_url)),
16  m_segmentBaseUrl(std::move(segment_base_url))
17 {
18  LOG(VB_RECORD, LOG_DEBUG, LOC + "ctor");
19 }
20 
22 {
23  LOG(VB_RECORD, LOG_DEBUG, LOC + "dtor");
24 
25 #ifdef USING_LIBCRYPTO
26  AESKeyMap::iterator Iaes;
27 
28  for (Iaes = m_aesKeys.begin(); Iaes != m_aesKeys.end(); ++Iaes)
29  delete *Iaes;
30 #endif
31 }
32 
33 QString HLSRecStream::toString(void) const
34 {
35  return QString("%1 bitrate %2").arg(Id()).arg(Bitrate());
36 }
37 
38 #ifdef USING_LIBCRYPTO
39 bool HLSRecStream::DownloadKey(MythSingleDownload& downloader,
40  const QString& keypath, AES_KEY* aeskey)
41 {
42  QByteArray key;
43 
44 #ifdef HLS_USE_MYTHDOWNLOADMANAGER // MythDownloadManager leaks memory
45  bool ret = HLSReader::DownloadURL(keypath, &key);
46 #else
47  bool ret = downloader.DownloadURL(keypath, &key);
48 #endif
49 
50  if (!ret || key.size() != AES_BLOCK_SIZE)
51  {
52  if (ret)
53  {
54  LOG(VB_RECORD, LOG_ERR, LOC +
55  QString("The AES key loaded doesn't have the right size (%1)")
56  .arg(key.size()));
57  }
58  else
59  {
60  LOG(VB_RECORD, LOG_ERR, LOC + "Failed to download AES key: " +
61  downloader.ErrorString());
62  }
63  return false;
64  }
65  AES_set_decrypt_key((const unsigned char*)key.constData(),
66  128, aeskey);
67  return true;
68 }
69 
70 bool HLSRecStream::DecodeData(MythSingleDownload& downloader,
71  const QByteArray& IV, const QString& keypath,
72  QByteArray& data, int64_t sequence)
73 {
74  AESKeyMap::iterator Ikey;
75 
76  if ((Ikey = m_aesKeys.find(keypath)) == 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  char *decrypted_data = new char[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  (unsigned char*)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  // NOLINTNEXTLINE(bugprone-signed-char-misuse)
119  int pad = decrypted_data[data.size()-1];
120  if (pad <= 0 || pad > AES_BLOCK_SIZE)
121  {
122  LOG(VB_RECORD, LOG_ERR, LOC +
123  QString("bad padding character (0x%1)")
124  .arg(pad, 0, 16, QLatin1Char('0')));
125  delete[] decrypted_data;
126  return false;
127  }
128  aeslen = data.size() - pad;
129  data = QByteArray(decrypted_data, aeslen);
130  delete[] decrypted_data;
131 
132  return true;
133 }
134 #endif // USING_LIBCRYPTO
135 
136 void HLSRecStream::AverageBandwidth(int64_t bandwidth)
137 {
138  // Average the last 20 segments
139  if (m_bandwidthSegs.size() > 19)
140  m_sumBandwidth -= m_bandwidthSegs.dequeue();
141  m_bandwidthSegs.enqueue(bandwidth);
142  m_sumBandwidth += bandwidth;
144 }
145 
146 std::chrono::seconds HLSRecStream::Duration(void) const
147 {
148  QMutexLocker lock(&m_lock);
149  return m_duration;
150 }
151 
153 {
154  return this->Bitrate() < b.Bitrate();
155 }
156 
158 {
159  return this->Bitrate() > b.Bitrate();
160 }
161 
162 #ifdef USING_LIBCRYPTO
163 bool HLSRecStream::SetAESIV(QString line)
164 {
165  /*
166  * If the EXT-X-KEY tag has the IV attribute, implementations MUST use
167  * the attribute value as the IV when encrypting or decrypting with that
168  * key. The value MUST be interpreted as a 128-bit hexadecimal number
169  * and MUST be prefixed with 0x or 0X.
170  */
171  if (!line.startsWith(QLatin1String("0x"), Qt::CaseInsensitive))
172  return false;
173  if (line.size() % 2)
174  {
175  // not even size, pad with front 0
176  line.insert(2, QLatin1String("0"));
177  }
178  int padding = std::max(0, AES_BLOCK_SIZE - (line.size() - 2));
179  QByteArray ba = QByteArray(padding, 0x0);
180  ba.append(QByteArray::fromHex(QByteArray(line.toLatin1().constData() + 2)));
181  m_aesIV = ba;
182  m_ivLoaded = true;
183  return true;
184 }
185 #endif // USING_LIBCRYPTO
HLSRecStream::~HLSRecStream
~HLSRecStream(void)
Definition: HLSStream.cpp:21
copy
long long copy(QFile &dst, QFile &src, uint block_size)
Copies src file to dst file.
Definition: mythmiscutil.cpp:311
LOC
#define LOC
Definition: HLSStream.cpp:10
HLSRecStream::Duration
std::chrono::seconds Duration(void) const
Definition: HLSStream.cpp:146
HLSRecStream::HLSRecStream
HLSRecStream(int seq, uint64_t bitrate, QString m3u8_url, QString segment_base_url)
Definition: HLSStream.cpp:12
HLSRecStream::operator<
bool operator<(const HLSRecStream &b) const
Definition: HLSStream.cpp:152
arg
arg(title).arg(filename).arg(doDelete))
x0
static int x0
Definition: mythsocket.cpp:56
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
HLSRecStream::m_duration
std::chrono::seconds m_duration
Definition: HLSStream.h:85
HLSRecStream
Definition: HLSStream.h:16
mythlogging.h
HLSStream.h
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:36
HLSRecStream::toString
QString toString(void) const
Definition: HLSStream.cpp:33
HLSRecStream::m_bandwidthSegs
QQueue< int64_t > m_bandwidthSegs
Definition: HLSStream.h:89
MythSingleDownload
Definition: mythsingledownload.h:25
HLSRecStream::Bitrate
uint64_t Bitrate(void) const
Definition: HLSStream.h:37
std
Definition: mythchrono.h:23
HLSRecStream::m_lock
QMutex m_lock
Definition: HLSStream.h:93
HLSRecStream::operator>
bool operator>(const HLSRecStream &b) const
Definition: HLSStream.cpp:157
MythSingleDownload::ErrorString
QString ErrorString(void) const
Definition: mythsingledownload.h:36
HLSRecStream::m_sumBandwidth
double m_sumBandwidth
Definition: HLSStream.h:88
HLSRecStream::Id
int Id(void) const
Definition: HLSStream.h:31
HLSRecStream::m_bandwidth
int64_t m_bandwidth
Definition: HLSStream.h:87
HLSReader.h