HLS input converted to URIReader, temporarily disabled clearkey encryptionHLS input fixes:

- Re-enabled file-based input
- Added support for clearkey-encrypted file-based input
- Converted to HTTP::URIReader for all input types
- Both live and VoD functional
 support
This commit is contained in:
Thulinma 2020-09-07 17:13:00 +02:00
parent 61b0a90598
commit 19f1df18aa
3 changed files with 224 additions and 157 deletions

View file

@ -250,7 +250,7 @@ namespace TS{
uint64_t firstTime = 0xffffffffffffffffull, lastTime = 0;
for (std::map<size_t, uint32_t>::const_iterator it = pidToCodec.begin();
it != pidToCodec.end(); it++){
if (!hasPacket(it->first)){
if (!hasPacket(it->first) || !outPackets.count(it->first) || !outPackets.at(it->first).size()){
missing++;
}else{
if (outPackets.at(it->first).front().getTime() < firstTime){

View file

@ -114,7 +114,7 @@ namespace Mist{
// These are used in the HTTP::Downloader callback, to prevent timeouts when downloading
// segments/playlists.
inputHLS *self = 0;
bool callbackFunc(){return self->callback();}
bool callbackFunc(uint8_t){return self->callback();}
/// Called by the global callbackFunc, to prevent timeouts
bool inputHLS::callback(){
@ -160,12 +160,6 @@ namespace Mist{
MEDIUM_MSG("Downloader thread for '%s' exiting", pls.uri.c_str());
}
SegmentDownloader::SegmentDownloader(){
segDL.progressCallback = callbackFunc;
segDL.dataTimeout = 5;
segDL.retryCount = 5;
}
Playlist::Playlist(const std::string &uriSrc){
nextUTC = 0;
id = 0; // to be set later
@ -184,6 +178,9 @@ namespace Mist{
reloadNext = 0;
}
/// Returns true if there is no protocol defined in the playlist root URL.
bool Playlist::isUrl() const{return root.protocol.size();}
void parseKey(std::string key, char *newKey, unsigned int len){
memset(newKey, 0, len);
for (size_t i = 0; i < key.size() && i < (len << 1); ++i){
@ -213,13 +210,93 @@ namespace Mist{
return output;
}
/// Returns true if packetPtr is at the end of the current segment.
bool SegmentDownloader::atEnd() const{
return (packetPtr - segDL.const_data().data() + 188) > segDL.const_data().size();
SegmentDownloader::SegmentDownloader(){
segDL.onProgress(callbackFunc);
encrypted = false;
}
/// Returns true if there is no protocol defined in the playlist root URL.
bool Playlist::isUrl() const{return root.protocol.size();}
/// Returns true if packetPtr is at the end of the current segment.
bool SegmentDownloader::atEnd() const{
return segDL.isEOF();
// return (packetPtr - segDL.const_data().data() + 188) > segDL.const_data().size();
}
/// Attempts to read a single TS packet from the current segment, setting packetPtr on success
bool SegmentDownloader::readNext(){
if (encrypted){
// Encrypted, need to decrypt
#ifdef SSL
// Are we exactly at the end of the buffer? Truncate it entirely.
if (encOffset == outData.size() || !outData.size()){
outData.truncate(0);
packetPtr = 0;
encOffset = 0;
}
// Do we already have some data ready? Just serve it.
if (encOffset + 188 <= outData.size()){
packetPtr = outData + encOffset;
encOffset += 188;
if (packetPtr[0] != 0x47){
FAIL_MSG("Not TS! Starts with byte %" PRIu8, (uint8_t)packetPtr[0]);
return false;
}
return true;
}
// Alright, we need to read some more data.
// We read 192 bytes at a time: a single TS packet is 188 bytes but AES-128-CBC encryption works in 16-byte blocks.
size_t len = 0;
segDL.readSome(packetPtr, len, 192);
if (!len){return false;}
if (len % 16 != 0){
FAIL_MSG("Read a non-16-multiple of bytes (%zu), cannot decode!", len);
return false;
}
outData.allocate(outData.size() + len);
mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, len, tmpIvec, (const unsigned char *)packetPtr,
((unsigned char *)(char *)outData) + outData.size());
outData.append(0, len);
// End of the segment? Remove padding data.
if (segDL.isEOF()){
// The padding consists of X bytes of padding, all containing the raw value X.
// Since padding is mandatory, we can simply read the last byte and remove X bytes from the length.
if (outData.size() <= outData[outData.size() - 1]){
FAIL_MSG("Encryption padding is >= one TS packet. We probably returned some invalid TS "
"data earlier :-(");
return false;
}
outData.truncate(outData.size() - outData[outData.size() - 1]);
}
// Okay, we have more data. Let's see if we can return it...
if (encOffset + 188 <= outData.size()){
packetPtr = outData + encOffset;
encOffset += 188;
if (packetPtr[0] != 0x47){
FAIL_MSG("Not TS! Starts with byte %" PRIu8, (uint8_t)packetPtr[0]);
return false;
}
return true;
}
#endif
// No? Then we've failed in our task :'(
FAIL_MSG("Could not load encrypted packet :'(");
return false;
}else{
// Plaintext
size_t len = 0;
segDL.readSome(packetPtr, len, 188);
if (len != 188 || packetPtr[0] != 0x47){
FAIL_MSG("Not a valid TS packet: len %zu, first byte %" PRIu8, len, (uint8_t)packetPtr[0]);
return false;
}
return true;
}
}
/// Attempts to read a single TS packet from the current segment, setting packetPtr on success
void SegmentDownloader::close(){
packetPtr = 0;
segDL.close();
}
/// Loads the given segment URL into the segment buffer.
bool SegmentDownloader::loadSegment(const playListEntries &entry){
@ -228,73 +305,30 @@ namespace Mist{
MEDIUM_MSG("Loading segment: %s, key: %s, ivec: %s", entry.filename.c_str(), hexKey.c_str(),
hexIvec.c_str());
if (!segDL.get(entry.filename)){
FAIL_MSG("failed download: %s", entry.filename.c_str());
if (!segDL.open(entry.filename)){
FAIL_MSG("Could not open %s", entry.filename.c_str());
return false;
}
if (!segDL.isOk()){
FAIL_MSG("HTTP response not OK!. statuscode: %d, statustext: %s", segDL.getStatusCode(),
segDL.getStatusText().c_str());
return false;
}
if (segDL.getHeader("Content-Length") != ""){
if (segDL.data().size() != atoi(segDL.getHeader("Content-Length").c_str())){
FAIL_MSG("Expected %s bytes of data, but only received %lu.",
segDL.getHeader("Content-Length").c_str(), segDL.data().size());
return false;
}
}
if (!segDL){return false;}
encrypted = false;
outData.truncate(0);
// If we have a non-null key, decrypt
if (entry.keyAES[0] != 0 || entry.keyAES[1] != 0 || entry.keyAES[2] != 0 || entry.keyAES[3] != 0 ||
entry.keyAES[4] != 0 || entry.keyAES[5] != 0 || entry.keyAES[6] != 0 || entry.keyAES[7] != 0 ||
entry.keyAES[8] != 0 || entry.keyAES[9] != 0 || entry.keyAES[10] != 0 || entry.keyAES[11] != 0 ||
entry.keyAES[12] != 0 || entry.keyAES[13] != 0 || entry.keyAES[14] != 0 || entry.keyAES[15] != 0){
// Setup AES context
mbedtls_aes_context aes;
// Load key for decryption
encrypted = true;
#ifdef SSL
// Load key
mbedtls_aes_setkey_dec(&aes, (const unsigned char *)entry.keyAES, 128);
// Allocate a pointer for writing the decrypted data to
static Util::ResizeablePointer outdata;
outdata.allocate(segDL.data().size());
// Actually decrypt the data
unsigned char tmpIvec[16];
// Load initialization vector
memcpy(tmpIvec, entry.ivec, 16);
mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, segDL.data().size(), tmpIvec,
(const unsigned char *)segDL.data().data(), (unsigned char *)(char *)outdata);
// Data is now still padded, the padding consists of X bytes of padding, all containing the raw value X.
// Since padding is mandatory, we can simply read the last byte and remove X bytes from the length.
if (segDL.data().size() <= outdata[segDL.data().size() - 1]){
FAIL_MSG("Encryption padding is >= entire segment. Considering download failed.");
return false;
}
size_t newSize = segDL.data().size() - outdata[segDL.data().size() - 1];
// Finally, overwrite the original data buffer with the new one
segDL.data().assign(outdata, newSize);
#endif
}
// check first byte = 0x47. begin of ts file, then check if it is a multiple of 188bytes
if (segDL.data().data()[0] == 0x47){
if (segDL.data().size() % 188){
FAIL_MSG("Expected a multiple of 188 bytes, received %zu bytes. url: %s",
segDL.data().size(), entry.filename.c_str());
return false;
}
}else if (segDL.data().data()[5] == 0x47){
if (segDL.data().size() % 192){
FAIL_MSG("Expected a multiple of 192 bytes, received %zu bytes. url: %s",
segDL.data().size(), entry.filename.c_str());
return false;
}
}else{
FAIL_MSG("Segment does not appear to contain TS data. Considering download failed.");
return false;
}
packetPtr = segDL.data().data();
packetPtr = 0;
HIGH_MSG("Segment download complete and passed sanity checks");
return true;
}
@ -359,16 +393,23 @@ namespace Mist{
keyUri = val.substr(tmpPos + 5, tmpPos2);
tmpPos = val.find("IV=");
keyIV = val.substr(tmpPos + 5, 32);
if (tmpPos != std::string::npos){keyIV = val.substr(tmpPos + 5, 32);}
// when key not found, download and store it in the map
if (!keys.count(keyUri)){
HTTP::Downloader keyDL;
if (!keyDL.get(root.link(keyUri)) || !keyDL.isOk()){
HTTP::URIReader keyDL;
if (!keyDL.open(root.link(keyUri)) || !keyDL){
FAIL_MSG("Could not retrieve decryption key from '%s'", root.link(keyUri).getUrl().c_str());
continue;
}
keys.insert(std::pair<std::string, std::string>(keyUri, keyDL.data()));
char *keyPtr;
size_t keyLen;
keyDL.readAll(keyPtr, keyLen);
if (!keyLen){
FAIL_MSG("Could not retrieve decryption key from '%s'", root.link(keyUri).getUrl().c_str());
continue;
}
keys.insert(std::pair<std::string, std::string>(keyUri, std::string(keyPtr, keyLen)));
}
}
@ -411,7 +452,12 @@ namespace Mist{
cleanLine(filename);
filename = root.link(filename).getUrl();
char ivec[16];
if (keyIV.size()){
parseKey(keyIV, ivec, 16);
}else{
memset(ivec, 0, 16);
Bit::htobll(ivec + 8, fileNo);
}
addEntry(filename, f, totalBytes, keys[keyUri], std::string(ivec, 16));
lastFileIndex = fileNo + 1;
++count;
@ -491,7 +537,7 @@ namespace Mist{
// The mutex assures we have a unique count/number.
if (!id){id = listEntries.size() + 1;}
listEntries[id].push_back(entry);
MEDIUM_MSG("Added segment to variant %" PRIu32 " (#%d, now %zu queued): %s", id,
DONTEVEN_MSG("Added segment to variant %" PRIu32 " (#%" PRIu64 ", now %zu queued): %s", id,
lastFileIndex, listEntries[id].size(), filename.c_str());
}
}
@ -563,8 +609,8 @@ namespace Mist{
return;
}
myMeta = DTSC::Meta();
myMeta.live = false;
myMeta.vod = true;
myMeta.live = true;
myMeta.vod = false;
INFO_MSG("Parsing live stream to create header...");
TS::Packet packet; // to analyse and extract data
int counter = 1;
@ -579,7 +625,6 @@ namespace Mist{
for (std::deque<playListEntries>::iterator entryIt = pListIt->second.begin();
entryIt != pListIt->second.end(); ++entryIt){
uint64_t lastBpos = entryIt->bytePos;
nProxy.userClient.keepAlive();
if (!segDowner.loadSegment(*entryIt)){
WARN_MSG("Skipping segment that could not be loaded in an attempt to recover");
@ -588,13 +633,12 @@ namespace Mist{
}
do{
if (!packet.FromPointer(segDowner.packetPtr)){
WARN_MSG("Could not load TS packet, aborting segment parse");
if (!segDowner.readNext() || !packet.FromPointer(segDowner.packetPtr)){
WARN_MSG("Could not load TS packet from %s, aborting segment parse", entryIt->filename.c_str());
tsStream.clear();
break; // Abort load
}
tsStream.parse(packet, lastBpos);
segDowner.packetPtr += 188;
tsStream.parse(packet, entryIt->bytePos);
if (tsStream.hasPacketOnEachTrack()){
while (tsStream.hasPacket()){
@ -607,15 +651,15 @@ namespace Mist{
pidMapping[(((uint64_t)pListIt->first) << 32) + headerPack.getTrackId()] = counter;
pidMappingR[counter] = (((uint64_t)pListIt->first) << 32) + headerPack.getTrackId();
packetId = counter;
VERYHIGH_MSG("Added file %s, trackid: %d, mapped to: %d", entryIt->filename.c_str(),
headerPack.getTrackId(), counter);
VERYHIGH_MSG("Added file %s, trackid: %zu, mapped to: %d",
entryIt->filename.c_str(), headerPack.getTrackId(), counter);
counter++;
}
if ((!myMeta.tracks.count(packetId) || !myMeta.tracks[packetId].codec.size())){
tsStream.initializeMetadata(myMeta, tmpTrackId, packetId);
myMeta.tracks[packetId].minKeepAway = globalWaitTime * 2000;
VERYHIGH_MSG("setting minKeepAway = %d for track: %d",
VERYHIGH_MSG("setting minKeepAway = %d for track: %" PRIu64,
myMeta.tracks[packetId].minKeepAway, packetId);
}
}
@ -627,17 +671,13 @@ namespace Mist{
}
tsStream.clear();
currentPlaylist = 0;
segDowner.segDL.data().clear(); // make sure we have nothing left over
segDowner.close(); // make sure we have nothing left over
INFO_MSG("header complete, beginning live ingest of %d tracks", counter - 1);
}
bool inputHLS::readHeader(){
if (streamIsLive){return true;}
std::istringstream urlSource;
std::ifstream fileSource;
bool endOfFile = false;
bool hasHeader = false;
// See whether a separate header file exists.
@ -664,23 +704,13 @@ namespace Mist{
for (std::deque<playListEntries>::iterator entryIt = pListIt->second.begin();
entryIt != pListIt->second.end(); entryIt++){
tsStream.partialClear();
endOfFile = false;
segDowner.loadSegment(*entryIt);
endOfFile = !segDowner.atEnd();
if (!endOfFile){packet.FromPointer(segDowner.packetPtr);}
segDowner.packetPtr += 188;
if (!segDowner.loadSegment(*entryIt)){
FAIL_MSG("Failed to load segment - skipping to next");
continue;
}
entId++;
uint64_t lastBpos = entryIt->bytePos;
while (!endOfFile){
tsStream.parse(packet, lastBpos);
// if (pListIt->isUrl()){
lastBpos = entryIt->bytePos + segDowner.segDL.data().size();
//}else{
// lastBpos = entryIt->bytePos + in.tellg();
//}
while (!segDowner.atEnd()){
while (tsStream.hasPacketOnEachTrack()){
DTSC::Packet headerPack;
@ -693,6 +723,8 @@ namespace Mist{
pidMapping[(((uint64_t)pListIt->first) << 32) + headerPack.getTrackId()] = counter;
pidMappingR[counter] = (((uint64_t)pListIt->first) << 32) + headerPack.getTrackId();
packetId = counter;
INFO_MSG("Added file %s, trackid: %zu, mapped to: %d", entryIt->filename.c_str(),
headerPack.getTrackId(), counter);
counter++;
}
@ -712,16 +744,10 @@ namespace Mist{
}
}
// if (pListIt->isUrl()){
endOfFile = segDowner.atEnd();
if (!endOfFile){
if (segDowner.readNext()){
packet.FromPointer(segDowner.packetPtr);
segDowner.packetPtr += 188;
tsStream.parse(packet, entId);
}
//}else{
// packet.FromStream(in);
// endOfFile = in.eof();
//}
}
// get last packets
tsStream.finish();
@ -735,7 +761,7 @@ namespace Mist{
pidMapping[(((uint64_t)pListIt->first) << 32) + headerPack.getTrackId()] = counter;
pidMappingR[counter] = (((uint64_t)pListIt->first) << 32) + headerPack.getTrackId();
packetId = counter;
INFO_MSG("Added file %s, trackid: %d, mapped to: %d", entryIt->filename.c_str(),
INFO_MSG("Added file %s, trackid: %zu, mapped to: %d", entryIt->filename.c_str(),
headerPack.getTrackId(), counter);
counter++;
}
@ -757,8 +783,6 @@ namespace Mist{
tsStream.getEarliestPacket(headerPack);
}
// if (!pListIt->isUrl()){in.close();}
if (hasHeader){break;}
}
}
@ -781,7 +805,7 @@ namespace Mist{
void inputHLS::getNext(bool smart){
INSANE_MSG("Getting next");
uint32_t tid = 0;
static bool endOfFile = false;
bool finished = false;
if (selectedTracks.size()){tid = *selectedTracks.begin();}
thisPacket.null();
while (config->is_active && (needsLock() || nProxy.userClient.isAlive())){
@ -789,7 +813,7 @@ namespace Mist{
// Check if we have a packet
bool hasPacket = false;
if (streamIsLive){
hasPacket = tsStream.hasPacketOnEachTrack() || (endOfFile && tsStream.hasPacket());
hasPacket = tsStream.hasPacketOnEachTrack() || (segDowner.atEnd() && tsStream.hasPacket());
}else{
hasPacket = tsStream.hasPacket(getMappedTrackId(tid));
}
@ -826,7 +850,7 @@ namespace Mist{
int64_t prevOffset = plsTimeOffset[currentPlaylist];
plsTimeOffset[currentPlaylist] = (nUTC - zUTC) - thisPacket.getTime();
newTime = thisPacket.getTime() + plsTimeOffset[currentPlaylist];
INFO_MSG("[UTC; New offset: %" PRId64 " -> %" PRId64 "] Packet %lu@%" PRIu64
INFO_MSG("[UTC; New offset: %" PRId64 " -> %" PRId64 "] Packet %" PRIu32 "@%" PRIu64
"ms -> %" PRIu64 "ms",
prevOffset, plsTimeOffset[currentPlaylist], tid, thisPacket.getTime(), newTime);
}
@ -842,8 +866,8 @@ namespace Mist{
plsTimeOffset[currentPlaylist] +=
(int64_t)(plsLastTime[currentPlaylist] + plsInterval[currentPlaylist]) - (int64_t)newTime;
newTime = thisPacket.getTime() + plsTimeOffset[currentPlaylist];
INFO_MSG("[Guess; New offset: %" PRId64 " -> %" PRId64 "] Packet %lu@%" PRIu64
"ms -> %" PRIu64 "ms",
INFO_MSG("[Guess; New offset: %" PRId64 " -> %" PRId64 "] Packet %" PRIu32
"@%" PRIu64 "ms -> %" PRIu64 "ms",
prevOffset, plsTimeOffset[currentPlaylist], tid, thisPacket.getTime(), newTime);
}
}
@ -866,25 +890,24 @@ namespace Mist{
}
// No? Let's read some more data and check again.
if (!segDowner.atEnd()){
if (!segDowner.atEnd() && segDowner.readNext()){
tsBuf.FromPointer(segDowner.packetPtr);
segDowner.packetPtr += 188;
tsStream.parse(tsBuf, 0);
tsStream.parse(tsBuf, streamIsLive ? 0 : currentIndex);
continue; // check again
}
// Okay, reading more is not possible. Let's call finish() and check again.
if (!endOfFile){
endOfFile = true; // we reached the end of file
if (!finished && segDowner.atEnd()){
tsStream.finish();
finished = true;
VERYHIGH_MSG("Finishing reading TS segment");
continue; // Check again!
}
// No? Then we try to read the next file.
//
currentPlaylist = firstSegment();
// No? Then we want to try reading the next file.
// No segments? Wait until next playlist reloading time.
currentPlaylist = firstSegment();
if (currentPlaylist < 0){
VERYHIGH_MSG("Waiting for segments...");
if (nProxy.userClient.isAlive()){nProxy.userClient.keepAlive();}
@ -896,15 +919,17 @@ namespace Mist{
VERYHIGH_MSG("Moving on to next TS segment (variant %" PRIu32 ")", currentPlaylist);
if (readNextFile()){
MEDIUM_MSG("Next segment read successfully");
endOfFile = false; // no longer at end of file
finished = false;
continue; // Success! Continue regular parsing.
}else{
if (selectedTracks.size() > 1){
// failed to read segment for playlist, dropping it
WARN_MSG("Dropping variant %" PRIu32 " because we couldn't read anything from it", currentPlaylist);
tthread::lock_guard<tthread::mutex> guard(entryMutex);
listEntries.erase(currentPlaylist);
if (listEntries.size()){continue;}
}
}
// Nothing works!
// HLS input will now quit trying to prevent severe mental depression.
@ -914,22 +939,11 @@ namespace Mist{
}
}
void inputHLS::readPMT(){
HIGH_MSG("readPMT()");
TS::Packet tsBuffer;
const char *tmpPtr = segDowner.segDL.data().data();
while (!tsStream.hasPacketOnEachTrack() &&
(tmpPtr - segDowner.segDL.data().data() + 188 <= segDowner.segDL.data().size())){
tsBuffer.FromPointer(tmpPtr);
tsStream.parse(tsBuffer, 0);
tmpPtr += 188;
}
tsStream.partialClear();
}
// Note: bpos is overloaded here for playlist entry!
void inputHLS::seek(int seekTime){
plsTimeOffset.clear();
plsLastTime.clear();
plsInterval.clear();
tsStream.clear();
int trackId = 0;
@ -953,17 +967,32 @@ namespace Mist{
}
currentIndex = plistEntry - 1;
INFO_MSG("Current index = %d", currentIndex);
currentPlaylist = getMappedTrackPlaylist(trackId);
INFO_MSG("Seeking to index %d on playlist %d", currentIndex, currentPlaylist);
{// Lock mutex for listEntries
tthread::lock_guard<tthread::mutex> guard(entryMutex);
if (!listEntries.count(currentPlaylist)){
WARN_MSG("Playlist %d not loaded, aborting seek", currentPlaylist);
return;
}
std::deque<playListEntries> &curPlaylist = listEntries[currentPlaylist];
if (curPlaylist.size() <= currentIndex){
WARN_MSG("Playlist %d has %zu <= %d entries, aborting seek", currentPlaylist,
curPlaylist.size(), currentIndex);
return;
}
playListEntries &entry = curPlaylist.at(currentIndex);
segDowner.loadSegment(entry);
}
readPMT();
HIGH_MSG("readPMT()");
TS::Packet tsBuffer;
while (!tsStream.hasPacketOnEachTrack() && !segDowner.atEnd()){
if (!segDowner.readNext()){break;}
tsBuffer.FromPointer(segDowner.packetPtr);
tsStream.parse(tsBuffer, 0);
}
}
int inputHLS::getEntryId(int playlistId, uint64_t bytePos){
@ -990,6 +1019,10 @@ namespace Mist{
}
uint32_t inputHLS::getMappedTrackPlaylist(uint64_t id){
if (!pidMappingR.count(id)){
FAIL_MSG("No mapping found for track ID %" PRIu64, id);
return 0;
}
static uint64_t lastIn = id;
static uint32_t lastOut = (pidMappingR[id] >> 32);
if (lastIn != id){
@ -1171,8 +1204,33 @@ namespace Mist{
WARN_MSG("no entries found in playlist: %d!", currentPlaylist);
return false;
}
ntry = curList.front();
if (!streamIsLive){
// VoD advances the index by one and attempts to read
// The playlist is not altered in this case, since we may need to seek back later
currentIndex++;
if (curList.size() - 1 < currentIndex){
INFO_MSG("Reached last entry");
return false;
}
ntry = curList[currentIndex];
}else{
// Live does not use the currentIndex, but simply takes the first segment
// That segment is then removed from the playlist so we don't read it again - live streams can't seek anyway
if (!curList.size()){
INFO_MSG("Reached last entry");
return false;
}
ntry = *curList.begin();
curList.pop_front();
if (Util::bootSecs() < ntry.timestamp){
VERYHIGH_MSG("Slowing down to realtime...");
while (Util::bootSecs() < ntry.timestamp){
if (nProxy.userClient.isAlive()){nProxy.userClient.keepAlive();}
Util::wait(250);
}
}
}
}
if (!segDowner.loadSegment(ntry)){

View file

@ -9,8 +9,8 @@
#include <string>
#include <vector>
//#include <stdint.h>
#include <mist/downloader.h>
#include <mist/http_parser.h>
#include <mist/urireader.h>
#define BUFFERTIME 10
@ -39,10 +39,19 @@ namespace Mist{
class SegmentDownloader{
public:
SegmentDownloader();
HTTP::Downloader segDL;
const char *packetPtr;
HTTP::URIReader segDL;
char *packetPtr;
bool loadSegment(const playListEntries &entry);
bool readNext();
void close();
bool atEnd() const;
private:
bool encrypted;
Util::ResizeablePointer outData;
size_t encOffset;
unsigned char tmpIvec[16];
mbedtls_aes_context aes;
};
class Playlist{