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;
76 
77  if ((Ikey = m_aesKeys.find(keypath)) == m_aesKeys.end())
78  {
79  auto* key = new AES_KEY;
80  DownloadKey(downloader, keypath, key);
81  Ikey = m_aesKeys.insert(keypath, key);
82  if (Ikey == m_aesKeys.end())
83  {
84  LOG(VB_RECORD, LOG_ERR, LOC +
85  "DecodeData: Unable to add AES key to map");
86  return false;
87  }
88  }
89 
90  /* Decrypt data using AES-128 */
91  int aeslen = data.size() & ~0xf;
92  std::array<uint8_t,AES_BLOCK_SIZE> iv {};
93  auto *decrypted_data = new uint8_t[data.size()];
94  if (IV.isEmpty())
95  {
96  /*
97  * If the EXT-X-KEY tag does not have the IV attribute,
98  * implementations MUST use the sequence number of the
99  * media file as the IV when encrypting or decrypting that
100  * media file. The big-endian binary representation of
101  * the sequence number SHALL be placed in a 16-octet
102  * buffer and padded (on the left) with zeros.
103  */
104  iv[15] = sequence & 0xff;
105  iv[14] = (sequence >> 8) & 0xff;
106  iv[13] = (sequence >> 16) & 0xff;
107  iv[12] = (sequence >> 24) & 0xff;
108  }
109  else
110  {
111  std::copy(IV.cbegin(), IV.cend(), iv.data());
112  }
113  AES_cbc_encrypt((unsigned char*)data.constData(),
114  decrypted_data, aeslen,
115  *Ikey, iv.data(), AES_DECRYPT);
116  std::copy(data.cbegin() + aeslen, data.cend(), decrypted_data + aeslen);
117 
118  // remove the PKCS#7 padding from the buffer
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(reinterpret_cast<char*>(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 - (static_cast<int>(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:22
LOC
#define LOC
Definition: HLSStream.cpp:11
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:13
HLSRecStream::operator<
bool operator<(const HLSRecStream &b) const
Definition: HLSStream.cpp:152
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:264
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:157
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