MythTV master
HLSStream.cpp
Go to the documentation of this file.
1#include "HLSStream.h"
2
3#include <algorithm>
4#include <array>
5#include <cstdint>
6#include <cstring>
7#include <utility>
8
9#if CONFIG_LIBCRYPTO
10#include <openssl/aes.h>
11#include <openssl/evp.h>
12#endif
13
14#include <QMutexLocker>
15
17
18#ifdef HLS_USE_MYTHDOWNLOADMANAGER
19#include "HLSReader.h"
20#endif
21
22#define LOC QString("HLSRecstream[%1]: ").arg(m_inputId)
23
24HLSRecStream::HLSRecStream(int inputId, int seq, uint64_t bitrate, QString m3u8_url, QString segment_base_url)
25 : m_inputId(inputId),
26 m_id(seq),
27 m_bitrate(bitrate),
28 m_m3u8Url(std::move(m3u8_url)),
29 m_segmentBaseUrl(std::move(segment_base_url))
30{
31 LOG(VB_RECORD, LOG_DEBUG, LOC + "ctor");
32}
33
35{
36 LOG(VB_RECORD, LOG_DEBUG, LOC + "dtor");
37#if CONFIG_LIBCRYPTO
38 AESKeyMap::iterator Iaes;
39
40 for (Iaes = m_aesKeys.begin(); Iaes != m_aesKeys.end(); ++Iaes)
41 delete *Iaes;
42#endif // CONFIG_LIBCRYPTO
43}
44
45QString HLSRecStream::toString(void) const
46{
47 return QString("%1 bitrate %2").arg(Id()).arg(Bitrate());
48}
49
50#if CONFIG_LIBCRYPTO
51bool HLSRecStream::DownloadKey(MythSingleDownload& downloader,
52 const QString& keypath, HLS_AES_KEY* aeskey) const
53{
54 QByteArray key;
55
56#ifdef HLS_USE_MYTHDOWNLOADMANAGER // MythDownloadManager leaks memory
57 bool ret = HLSReader::DownloadURL(keypath, &key);
58#else
59 bool ret = downloader.DownloadURL(keypath, &key);
60#endif
61
62 if (!ret || key.size() != AES128_KEY_SIZE)
63 {
64 if (ret)
65 {
66 LOG(VB_RECORD, LOG_ERR, LOC +
67 QString("The AES key loaded doesn't have the right size (%1)")
68 .arg(key.size()));
69 }
70 else
71 {
72 LOG(VB_RECORD, LOG_ERR, LOC + "Failed to download AES key: " +
73 downloader.ErrorString());
74 }
75 return false;
76 }
77 memcpy(aeskey->key.data(), key.constData(), AES128_KEY_SIZE);
78
79 LOG(VB_RECORD, LOG_DEBUG, LOC + "Downloaded AES key");
80
81 return true;
82}
83
84// AES decryption based on OpenSSL example found on
85// https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption
86//
87int HLSRecStream::Decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
88 unsigned char *iv, unsigned char *plaintext) const
89{
90 int len = 0;
91
92 int plaintext_len = 0;
93
94 /* Create and initialise the context */
95 EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
96 if(!ctx)
97 {
98 LOG(VB_RECORD, LOG_ERR, LOC + "Failed to create and initialize cipher context");
99 return 0;
100 }
101
102 /*
103 * Initialise the decryption operation. IMPORTANT - ensure you use a key
104 * and IV size appropriate for your cipher
105 * In this example we are using 128 bit AES (i.e. a 128 bit key). The
106 * IV size for *most* modes is the same as the block size. For AES this
107 * is 128 bits
108 */
109 if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
110 {
111 LOG(VB_RECORD, LOG_ERR, LOC + "Failed to initialize decryption operation");
112 return 0;
113 }
114
115 /*
116 * Provide the message to be decrypted, and obtain the plaintext output.
117 * EVP_DecryptUpdate can be called multiple times if necessary.
118 */
119 if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
120 {
121 LOG(VB_RECORD, LOG_ERR, LOC + "Failed to decrypt");
122 return 0;
123 }
124 plaintext_len = len;
125
126 /*
127 * Finalise the decryption. Further plaintext bytes may be written at
128 * this stage.
129 */
130 if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
131 {
132 LOG(VB_RECORD, LOG_ERR, LOC + "Failed to finalize decryption" +
133 QString(" len:%1").arg(len) +
134 QString(" plaintext_len:%1").arg(plaintext_len) );
135 return 0;
136 }
137 plaintext_len += len;
138
139 /* Clean up */
140 EVP_CIPHER_CTX_free(ctx);
141
142 return plaintext_len;
143}
144
145bool HLSRecStream::DecodeData(MythSingleDownload& downloader,
146 const QByteArray& IV, const QString& keypath,
147 QByteArray& data, int64_t sequence)
148{
149 LOG(VB_RECORD, LOG_INFO, LOC + "DecodeData:" +
150 QString(" IV.size():%1").arg(IV.size()) +
151 QString(" keypath:%1..%2").arg(keypath.left(20),keypath.right(20)) +
152 QString(" sequence:%1").arg(sequence));
153
154 AESKeyMap::iterator Ikey = m_aesKeys.find(keypath);
155 if (Ikey == m_aesKeys.end())
156 {
157 auto* key = new HLS_AES_KEY;
158 DownloadKey(downloader, keypath, key);
159 Ikey = m_aesKeys.insert(keypath, key);
160 if (Ikey == m_aesKeys.end())
161 {
162 LOG(VB_RECORD, LOG_ERR, LOC +
163 "DecodeData: Unable to add AES key to map");
164 return false;
165 }
166 }
167
168 /* Decrypt data using AES-128 */
169 std::array<uint8_t,AES_BLOCK_SIZE> iv {};
170 auto *decrypted_data = new uint8_t[data.size()];
171 if (IV.isEmpty())
172 {
173 /*
174 * If the EXT-X-KEY tag does not have the IV attribute,
175 * implementations MUST use the sequence number of the
176 * media file as the IV when encrypting or decrypting that
177 * media file. The big-endian binary representation of
178 * the sequence number SHALL be placed in a 16-octet
179 * buffer and padded (on the left) with zeros.
180 */
181 iv[15] = sequence & 0xff;
182 iv[14] = (sequence >> 8) & 0xff;
183 iv[13] = (sequence >> 16) & 0xff;
184 iv[12] = (sequence >> 24) & 0xff;
185 }
186 else
187 {
188 std::copy(IV.cbegin(), IV.cend(), iv.data());
189 }
190
191 int aeslen = data.size() & ~0xf;
192 if (aeslen != data.size())
193 {
194 LOG(VB_RECORD, LOG_WARNING, LOC +
195 QString("Data size %1 not multiple of 16 bytes, rounding to %2")
196 .arg(data.size()).arg(aeslen));
197 }
198
199 int plaintext_len = Decrypt((unsigned char*)data.constData(), aeslen, (*Ikey)->key.data(),
200 iv.data(), decrypted_data);
201
202 LOG(VB_RECORD, LOG_INFO, LOC +
203 QString("Segment data.size()):%1 plaintext_len:%2")
204 .arg(data.size()).arg(plaintext_len));
205
206 data = QByteArray(reinterpret_cast<char*>(decrypted_data), plaintext_len);
207 delete[] decrypted_data;
208
209 return true;
210}
211#endif // CONFIG_LIBCRYPTO
212
213void HLSRecStream::AverageBandwidth(int64_t bandwidth)
214{
215 // Average the last 20 segments
216 if (m_bandwidthSegs.size() > 19)
217 m_sumBandwidth -= m_bandwidthSegs.dequeue();
218 m_bandwidthSegs.enqueue(bandwidth);
219 m_sumBandwidth += bandwidth;
221}
222
223std::chrono::seconds HLSRecStream::Duration(void) const
224{
225 QMutexLocker lock(&m_lock);
226 return m_duration;
227}
228
230{
231 return this->Bitrate() < b.Bitrate();
232}
233
235{
236 return this->Bitrate() > b.Bitrate();
237}
#define LOC
Definition: HLSStream.cpp:22
static constexpr uint8_t AES128_KEY_SIZE
Definition: HLSStream.h:19
hls_aes_key_st HLS_AES_KEY
Definition: HLSStream.h:23
std::chrono::seconds Duration(void) const
Definition: HLSStream.cpp:223
bool operator>(const HLSRecStream &b) const
Definition: HLSStream.cpp:234
std::chrono::seconds m_duration
Definition: HLSStream.h:97
int64_t m_bandwidth
Definition: HLSStream.h:100
~HLSRecStream(void)
Definition: HLSStream.cpp:34
double m_sumBandwidth
Definition: HLSStream.h:101
QString toString(void) const
Definition: HLSStream.cpp:45
uint64_t Bitrate(void) const
Definition: HLSStream.h:48
HLSRecStream(int inputId, int seq, uint64_t bitrate, QString m3u8_url, QString segment_base_url)
Definition: HLSStream.cpp:24
int Id(void) const
Definition: HLSStream.h:40
bool operator<(const HLSRecStream &b) const
Definition: HLSStream.cpp:229
QQueue< int64_t > m_bandwidthSegs
Definition: HLSStream.h:102
uint64_t AverageBandwidth(void) const
Definition: HLSStream.h:47
QMutex m_lock
Definition: HLSStream.h:106
QString ErrorString(void) const
bool DownloadURL(const QUrl &url, QByteArray *buffer, std::chrono::seconds timeout=30s, uint redirs=0, qint64 maxsize=0, QString *final_url=nullptr)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
STL namespace.
std::array< uint8_t, AES128_KEY_SIZE > key
Definition: HLSStream.h:21