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