diff --git a/src/input/input_hls.cpp b/src/input/input_hls.cpp index 0c7576b4..c50aa8de 100644 --- a/src/input/input_hls.cpp +++ b/src/input/input_hls.cpp @@ -24,14 +24,261 @@ namespace Mist { + //remove trailing \r for windows generated playlist files + int cleanLine(std::string & s) { + if (s.length() > 0 && s.at(s.length() - 1) == '\r') { + s.erase(s.size() - 1); + } + } - Playlist::Playlist(){ + + Playlist::Playlist(const std::string & uriSrc) { lastFileIndex = 0; entryCount = 0; waitTime = 2; playlistEnd = false; noChangeCount = 0; - vodLive = false; + initDone = false; + lastTimestamp = 0; + uri = uriSrc; + + if (uri.size()){ + std::string line; + std::string key; + std::string val; + int count = 0; + uint64_t totalBytes = 0; + uri_root = uri.substr(0, uri.rfind("/") + 1); + playlistType = LIVE;//Temporary value + INFO_MSG("readplaylist: %s", uri.c_str()); + + std::istringstream urlSource; + std::ifstream fileSource; + + if (isUrl()){ + loadURL(uri); + urlSource.str(source); + } else { + fileSource.open(uri.c_str()); + } + + std::istream & input = (isUrl() ? (std::istream &)urlSource : (std::istream &)fileSource); + std::getline(input, line); + + while (std::getline(input, line)) { + cleanLine(line); + + if (!line.empty()) { + if (line.compare(0, 7, "#EXT-X-") == 0) { + size_t pos = line.find(":"); + key = line.substr(7, pos - 7); + val = line.c_str() + pos + 1; + + if (key == "VERSION") { + version = atoi(val.c_str()); + } + + if (key == "TARGETDURATION") { + waitTime = atoi(val.c_str()); + } + + if (key == "MEDIA-SEQUENCE") { + media_sequence = atoi(val.c_str()); + lastFileIndex = media_sequence; + } + + if (key == "PLAYLIST-TYPE") { + if (val == "VOD") { + playlistType = VOD; + } else if (val == "LIVE") { + playlistType = LIVE; + } else if (val == "EVENT") { + playlistType = EVENT; + } + } + + if (key == "ENDLIST") { + //end of playlist reached! + playlistEnd = true; + playlistType = VOD; + } + continue; + } else if (line.compare(0, 7, "#EXTINF") != 0) { + VERYHIGH_MSG("ignoring wrong line: %s.", line.c_str()); + continue; + } + float f = atof(line.c_str() + 8); + std::string filename; + std::getline(input, filename); + addEntry(filename, f, totalBytes); + count++; + } + } + + if (isUrl()) { + playlistType = LIVE;//VOD over HTTP needs to be processed as LIVE. + fileSource.close(); + } + } + initDone = true; + } + + bool Playlist::atEnd() const { + return (packetPtr - source.data() + 188) > source.size(); + } + + bool Playlist::isUrl() const { + return (uri_root.size() ? uri_root.find("http://") == 0 : uri.find("http://") == 0); + } + + bool Playlist::loadURL(const std::string & loadUrl){ + HIGH_MSG("opening URL: %s", loadUrl.c_str()); + + HTTP::URL url(loadUrl); + if (url.protocol != "http") { + FAIL_MSG("Protocol %s is not supported", url.protocol.c_str()); + return false; + } + + Socket::Connection conn(url.host, url.getPort(), false); + if (!conn) { + FAIL_MSG("Failed to reach %s on port %lu", url.host.c_str(), url.getPort()); + return false; + } + + HTTP::Parser http; + http.url = "/" + url.path; + http.method = "GET"; + http.SetHeader("Host", url.host); + http.SetHeader("X-MistServer", PACKAGE_VERSION); + + conn.SendNow(http.BuildRequest()); + http.Clean(); + + uint64_t startTime = Util::epoch(); + source.clear(); + packetPtr = 0; + while ((Util::epoch() - startTime < 10) && (conn || conn.Received().size())) { + if (conn.spool() || conn.Received().size()) { + if (http.Read(conn)) { + source = http.body; + packetPtr = source.data(); + conn.close(); + return true; + } + } + } + + FAIL_MSG("Failed to load %s: %s", loadUrl.c_str(), conn ? "timeout" : "connection closed"); + if (conn) { + conn.close(); + } + return false; + } + + ///Function for reloading the playlist in case of live streams. + bool Playlist::reload() { + int skip = lastFileIndex - media_sequence; + bool ret = false; + std::string line; + std::string key; + std::string val; + int count = 0; + + uint64_t totalBytes = 0; + + std::istringstream urlSource; + std::ifstream fileSource; + + if (isUrl()) { + loadURL(uri.c_str()); //get size only! + urlSource.str(source); + } else { + fileSource.open(uri.c_str()); + } + + std::istream & input = (isUrl() ? (std::istream &)urlSource : (std::istream &)fileSource); + std::getline(input, line); + + while (std::getline(input, line)) { + cleanLine(line); + if (line.compare(0, 21, "#EXT-X-MEDIA-SEQUENCE") == 0) { + media_sequence = atoi(line.c_str() + line.find(":") + 1); + skip = (lastFileIndex - media_sequence); + continue; + } + if (line.compare(0, 7, "#EXTINF") != 0) { + continue; + } + float f = atof(line.c_str() + 8); + //next line belongs to this item + std::string filename; + std::getline(input, filename); + + //check for already added segments + if (skip) { + skip--; + }else{ + cleanLine(filename); + addEntry(filename, f, totalBytes); + count++; + } + } + + if (!isUrl()) { + fileSource.close(); + } + + ret = (count > 0); + + if (ret) { + noChangeCount = 0; + } else { + ++noChangeCount; + if (noChangeCount > 3) { + VERYHIGH_MSG("enough!"); + } + } + + return ret; + } + + ///function for adding segments to the playlist to be processed. used for VOD and live + void Playlist::addEntry(const std::string & filename, float duration, uint64_t & totalBytes) { + playListEntries entry; + entry.filename = filename; + cleanLine(entry.filename); + std::string test = uri_root + entry.filename; + + std::istringstream urlSource; + std::ifstream fileSource; + + if (isUrl()) { + urlSource.str(source); + } else { + fileSource.open(test.c_str(), std::ios::ate | std::ios::binary); + if ((fileSource.rdstate() & std::ifstream::failbit) != 0) { + WARN_MSG("file: %s, error: %s", test.c_str(), strerror(errno)); + } + } + + entry.bytePos = totalBytes; + entry.duration = duration; + if (!isUrl()) { + totalBytes += fileSource.tellg(); + } + + if (initDone) { + lastTimestamp += duration; + entry.timestamp = lastTimestamp + startTime; + entry.wait = entryCount * duration; + } else { + entry.timestamp = 0; //read all segments immediatly at the beginning, then use delays + } + ++entryCount; + entries.push_back(entry); + ++lastFileIndex; + } /// Constructor of HLS Input @@ -49,9 +296,6 @@ namespace Mist { capa["codecs"][0u][1u].append("AC3"); capa["codecs"][0u][1u].append("MP3"); - isUrl = false; - - initDone = false; inFile = NULL; } @@ -61,33 +305,30 @@ namespace Mist { } } - void inputHLS::printContent() { - for(int i=0;i< playlists.size();i++) { - std::cout << i << ": " << playlists[i].uri << std::endl; - for(int j = 0;j < playlists[i].entries.size(); j++){ - std::cout << " " << j << ": " << playlists[i].entries.at(j).filename << " bytePos: " << playlists[i].entries[j].bytePos << std::endl; - } - } - } - bool inputHLS::setup() { - if (config->getString("input") != "-") { - playlistFile = config->getString("input"); - INFO_MSG("opening playlist file... %s" , playlistFile.c_str()); - playlistRootPath = playlistFile.substr(0,playlistFile.rfind("/")+1); + if (config->getString("input") == "-") { + return false; + } - if(initPlaylist(playlistFile)) { -// printContent(); - return true; + if (!initPlaylist(config->getString("input"))) { + return false; + } + + if (Util::Config::printDebugLevel >= DLVL_HIGH){ + for (std::vector::iterator pListIt = playlists.begin(); pListIt != playlists.end(); pListIt++){ + std::cout << pListIt->id << ": " << pListIt->uri << std::endl; + int j = 0; + for (std::deque::iterator entryIt = pListIt->entries.begin(); entryIt != pListIt->entries.end(); entryIt++){ + std::cout << " " << j++ << ": " << entryIt->filename << " bytePos: " << entryIt->bytePos << std::endl; + } } } - - return false; + return true; } void inputHLS::trackSelect(std::string trackSpec) { selectedTracks.clear(); - long long int index; + size_t index; while (trackSpec != "") { index = trackSpec.find(' '); selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str())); @@ -101,13 +342,11 @@ namespace Mist { void inputHLS::parseStreamHeader() { bool hasHeader = false; - if(!hasHeader){ + if (!hasHeader) { myMeta = DTSC::Meta(); } TS::Packet packet;//to analyse and extract data - int thisEntry = 0; - int thisPlaylist =0; int counter = 1; int packetId = 0; @@ -115,84 +354,79 @@ namespace Mist { unsigned int dataLen; bool keepReading = false; - for(int i = 0;i::iterator pListIt = playlists.begin(); pListIt != playlists.end(); pListIt++){ + if (!pListIt->entries.size()) { + continue; + } + std::deque::iterator entryIt = pListIt->entries.begin(); - std::deque::iterator entryIt = playlists[i].entries.begin(); - //INFO_MSG("opening...: %s",(playlists[i].uri_root + entryIt->filename).c_str()); + tsStream.clear(); + uint64_t lastBpos = entryIt->bytePos; - tsStream.clear(); - uint64_t lastBpos = entryIt->bytePos; + if (pListIt->isUrl()) { + pListIt->loadURL(pListIt->uri_root + entryIt->filename); - if(isUrl){ - openURL((playlists[i].uri_root + entryIt->filename).c_str(),playlists[i]); -// packetPtr = source.c_str(); + keepReading = packet.FromPointer(pListIt->packetPtr); + pListIt->packetPtr += 188; + } else { + in.open((pListIt->uri_root + entryIt->filename).c_str()); + keepReading = packet.FromStream(in); + } - keepReading = packet.FromPointer(playlists[i].packetPtr); - playlists[i].packetPtr += 188; - }else{ - in.open((playlists[i].uri_root + entryIt->filename).c_str()); - keepReading = packet.FromStream(in); + while (keepReading) { + tsStream.parse(packet, lastBpos); + if (pListIt->isUrl()) { + lastBpos = entryIt->bytePos + pListIt->source.size(); + ///\todo get size... + } else { + lastBpos = entryIt->bytePos + in.tellg(); } - //while(packet.FromStream(in)) { - while(keepReading) { - tsStream.parse(packet, lastBpos); -// INFO_MSG("keepreading"); - if(isUrl){ - lastBpos = entryIt->bytePos + playlists[i].source.size(); - //get size... TODO - }else{ - lastBpos = entryIt->bytePos + in.tellg(); + while (tsStream.hasPacketOnEachTrack()) { + DTSC::Packet headerPack; + tsStream.getEarliestPacket(headerPack); + int tmpTrackId = headerPack.getTrackId(); + packetId = pidMapping[(pListIt->id << 16) + tmpTrackId]; + + if (packetId == 0) { + pidMapping[(pListIt->id << 16) + headerPack.getTrackId()] = counter; + pidMappingR[counter] = (pListIt->id << 16) + headerPack.getTrackId(); + packetId = counter; + HIGH_MSG("Added file %s, trackid: %d, mapped to: %d", (pListIt->uri_root + entryIt->filename).c_str(), headerPack.getTrackId(), counter); + counter++; } - while (tsStream.hasPacketOnEachTrack()) { - DTSC::Packet headerPack; - tsStream.getEarliestPacket(headerPack); - int tmpTrackId = headerPack.getTrackId(); - packetId = pidMapping[(i<<16)+tmpTrackId]; - - if(packetId == 0) { - pidMapping[(i<<16)+headerPack.getTrackId()] = counter; - pidMappingR[counter] = (i<<16)+headerPack.getTrackId(); - packetId = counter; - HIGH_MSG("Added file %s, trackid: %d, mapped to: %d",(playlists[i].uri_root + entryIt->filename).c_str(),headerPack.getTrackId(),counter); - counter++; - } - -myMeta.live = (playlists[currentPlaylist].playlistType == LIVE); -myMeta.vod = !myMeta.live; + myMeta.live = (playlists.size() && playlists[0].playlistType == LIVE); + myMeta.vod = !myMeta.live; // myMeta.live = true; // myMeta.vod = false; - myMeta.live = false; - myMeta.vod = true; + myMeta.live = false; + myMeta.vod = true; - if (!hasHeader && (!myMeta.tracks.count(packetId) || !myMeta.tracks[packetId].codec.size())) { - tsStream.initializeMetadata(myMeta, tmpTrackId, packetId); - } - } - - if(isUrl){ - if((playlists[i].packetPtr - playlists[i].source.c_str()) +188 < playlists[i].source.size()){ - keepReading = packet.FromPointer(playlists[i].packetPtr); - playlists[i].packetPtr += 188; - }else{ - keepReading = false; - } - - }else{ - keepReading = packet.FromStream(in); + if (!hasHeader && (!myMeta.tracks.count(packetId) || !myMeta.tracks[packetId].codec.size())) { + tsStream.initializeMetadata(myMeta, tmpTrackId, packetId); } } - in.close(); + if (pListIt->isUrl()) { + keepReading = !pListIt->atEnd(); + if (keepReading){ + packet.FromPointer(pListIt->packetPtr); + pListIt->packetPtr += 188; + } + } else { + keepReading = packet.FromStream(in); + } } - tsStream.clear(); -INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); - if(hasHeader) { + in.close(); + } + tsStream.clear(); + + INFO_MSG("end stream header tracks: %d", myMeta.tracks.size()); + if (hasHeader) { return; } @@ -202,14 +436,13 @@ INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); } bool inputHLS::readHeader() { - //if(playlists[currentPlaylist].playlistType == LIVE || isUrl){ - if(playlists[currentPlaylist].playlistType == LIVE){ + if (playlists.size() && playlists[0].playlistType == LIVE) { return true; } std::istringstream urlSource; std::ifstream fileSource; - + bool endOfFile = false; bool hasHeader = false; @@ -219,62 +452,55 @@ INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); myMeta = tmp.getMeta(); if (myMeta) { hasHeader = true; - } + } } - if(!hasHeader){ + if (!hasHeader) { myMeta = DTSC::Meta(); } TS::Packet packet;//to analyse and extract data - int thisEntry = 0; - int thisPlaylist =0; int counter = 1; int packetId = 0; char * data; unsigned int dataLen; - for(int i = 0;i::iterator pListIt = playlists.begin(); pListIt != playlists.end(); pListIt++){ tsStream.clear(); uint32_t entId = 0; - - INFO_MSG("reading new playlist...%d",i); - for(std::deque::iterator entryIt = playlists[i].entries.begin(); entryIt != playlists[i].entries.end();entryIt++) { - //WORK - tsStream.partialClear(); - endOfFile = false; - if(isUrl){ - openURL((playlists[i].uri_root + entryIt->filename).c_str(),playlists[i]); - urlSource.str(playlists[i].source); - - if ((playlists[i].packetPtr - playlists[i].source.data() +188) < playlists[i].source.size()) - { - packet.FromPointer(playlists[i].packetPtr); - endOfFile = false; - }else{ - endOfFile = true; + for (std::deque::iterator entryIt = pListIt->entries.begin(); entryIt != pListIt->entries.end(); entryIt++) { + //WORK + tsStream.partialClear(); + endOfFile = false; + + if (pListIt->isUrl()) { + pListIt->loadURL(pListIt->uri_root + entryIt->filename); + urlSource.str(pListIt->source); + + endOfFile = !pListIt->atEnd(); + if (!endOfFile){ + packet.FromPointer(pListIt->packetPtr); + } + pListIt->packetPtr += 188; + } else { + in.close(); + in.open((pListIt->uri_root + entryIt->filename).c_str()); + packet.FromStream(in); + endOfFile = in.eof(); } - playlists[i].packetPtr += 188; - }else{ - // fileSource.open(uri.c_str()) - in.close(); - in.open((playlists[i].uri_root + entryIt->filename).c_str()); - packet.FromStream(in); - endOfFile = in.eof(); - } entId++; - uint64_t lastBpos = entryIt->bytePos; - while(!endOfFile) { + uint64_t lastBpos = entryIt->bytePos; + while (!endOfFile) { tsStream.parse(packet, lastBpos); - if(isUrl){ - lastBpos = entryIt->bytePos + playlists[currentPlaylist].source.size(); - }else{ - lastBpos = entryIt->bytePos + in.tellg(); + if (pListIt->isUrl()) { + lastBpos = entryIt->bytePos + pListIt->source.size(); + } else { + lastBpos = entryIt->bytePos + in.tellg(); } while (tsStream.hasPacketOnEachTrack()) { @@ -282,13 +508,13 @@ INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); tsStream.getEarliestPacket(headerPack); int tmpTrackId = headerPack.getTrackId(); - packetId = pidMapping[(i<<16)+tmpTrackId]; - - if(packetId == 0) { - pidMapping[(i<<16)+headerPack.getTrackId()] = counter; - pidMappingR[counter] = (i<<16)+headerPack.getTrackId(); + packetId = pidMapping[(pListIt->id << 16) + tmpTrackId]; + + if (packetId == 0) { + pidMapping[(pListIt->id << 16) + headerPack.getTrackId()] = counter; + pidMappingR[counter] = (pListIt->id << 16) + headerPack.getTrackId(); packetId = counter; - INFO_MSG("Added file %s, trackid: %d, mapped to: %d",(playlists[i].uri_root + entryIt->filename).c_str(),headerPack.getTrackId(),counter); + INFO_MSG("Added file %s, trackid: %d, mapped to: %d", (pListIt->uri_root + entryIt->filename).c_str(), headerPack.getTrackId(), counter); counter++; } @@ -296,80 +522,71 @@ INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); tsStream.initializeMetadata(myMeta, tmpTrackId, packetId); } - if(!hasHeader){ + if (!hasHeader) { headerPack.getString("data", data, dataLen); uint64_t pBPos = headerPack.getInt("bpos"); - - //keyframe data exists, so always add 19 bytes keyframedata. - long long packOffset = headerPack.hasMember("offset")?headerPack.getInt("offset"):0; - long long packSendSize = 24 + (packOffset?17:0) + (entId>=0?15:0) + 19 + dataLen+11; - myMeta.update(headerPack.getTime(), packOffset, packetId, dataLen, entId, headerPack.hasMember("keyframe"),packSendSize); + + //keyframe data exists, so always add 19 bytes keyframedata. + long long packOffset = headerPack.hasMember("offset") ? headerPack.getInt("offset") : 0; + long long packSendSize = 24 + (packOffset ? 17 : 0) + (entId >= 0 ? 15 : 0) + 19 + dataLen + 11; + myMeta.update(headerPack.getTime(), packOffset, packetId, dataLen, entId, headerPack.hasMember("keyframe"), packSendSize); } } - if(isUrl){ - if ((playlists[i].packetPtr - playlists[i].source.data() +188) < playlists[i].source.size()) - { - packet.FromPointer(playlists[i].packetPtr); - endOfFile = false; - }else{ - endOfFile = true; + if (pListIt->isUrl()) { + endOfFile = pListIt->atEnd(); + if (!endOfFile){ + packet.FromPointer(pListIt->packetPtr); + pListIt->packetPtr += 188; } - playlists[i].packetPtr += 188; - }else{ - // fileSource.open(uri.c_str()) - packet.FromStream(in); - endOfFile = in.eof(); + } else { + packet.FromStream(in); + endOfFile = in.eof(); } - } //get last packets - tsStream.finish(); - DTSC::Packet headerPack; - tsStream.getEarliestPacket(headerPack); - while (headerPack) { + tsStream.finish(); + DTSC::Packet headerPack; + tsStream.getEarliestPacket(headerPack); + while (headerPack) { + int tmpTrackId = headerPack.getTrackId(); + packetId = pidMapping[(pListIt->id << 16) + tmpTrackId]; - int tmpTrackId = headerPack.getTrackId(); - packetId = pidMapping[(i<<16)+tmpTrackId]; - - if(packetId == 0) { - pidMapping[(i<<16)+headerPack.getTrackId()] = counter; - pidMappingR[counter] = (i<<16)+headerPack.getTrackId(); - packetId = counter; - INFO_MSG("Added file %s, trackid: %d, mapped to: %d",(playlists[i].uri_root + entryIt->filename).c_str(),headerPack.getTrackId(),counter); - counter++; - } - - if (!hasHeader && (!myMeta.tracks.count(packetId) || !myMeta.tracks[packetId].codec.size())) { - tsStream.initializeMetadata(myMeta, tmpTrackId, packetId); - } - - if(!hasHeader){ - headerPack.getString("data", data, dataLen); - uint64_t pBPos = headerPack.getInt("bpos"); - - //keyframe data exists, so always add 19 bytes keyframedata. - long long packOffset = headerPack.hasMember("offset")?headerPack.getInt("offset"):0; - long long packSendSize = 24 + (packOffset?17:0) + (entId>=0?15:0) + 19 + dataLen+11; - myMeta.update(headerPack.getTime(), packOffset, packetId, dataLen, entId, headerPack.hasMember("keyframe"),packSendSize); - } - tsStream.getEarliestPacket(headerPack); + if (packetId == 0) { + pidMapping[(pListIt->id << 16) + headerPack.getTrackId()] = counter; + pidMappingR[counter] = (pListIt->id << 16) + headerPack.getTrackId(); + packetId = counter; + INFO_MSG("Added file %s, trackid: %d, mapped to: %d", (pListIt->uri_root + entryIt->filename).c_str(), headerPack.getTrackId(), counter); + counter++; } + if (!hasHeader && (!myMeta.tracks.count(packetId) || !myMeta.tracks[packetId].codec.size())) { + tsStream.initializeMetadata(myMeta, tmpTrackId, packetId); + } - if(isUrl){ + if (!hasHeader) { + headerPack.getString("data", data, dataLen); + uint64_t pBPos = headerPack.getInt("bpos"); + + //keyframe data exists, so always add 19 bytes keyframedata. + long long packOffset = headerPack.hasMember("offset") ? headerPack.getInt("offset") : 0; + long long packSendSize = 24 + (packOffset ? 17 : 0) + (entId >= 0 ? 15 : 0) + 19 + dataLen + 11; + myMeta.update(headerPack.getTime(), packOffset, packetId, dataLen, entId, headerPack.hasMember("keyframe"), packSendSize); + } + tsStream.getEarliestPacket(headerPack); + } + + if (!pListIt->isUrl()) { in.close(); } - if(hasHeader) { + if (hasHeader) { break; } - } - } - if(hasHeader || isUrl) { + if (hasHeader || (playlists.size() && playlists[0].isUrl())) { return true; } @@ -385,23 +602,20 @@ INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); } bool inputHLS::needsLock() { - if(isUrl){ + if (playlists.size() && playlists[0].isUrl()) { return false; } return (playlists.size() <= currentPlaylist) || !(playlists[currentPlaylist].playlistType == LIVE); } - bool inputHLS::openStreamSource(){ + bool inputHLS::openStreamSource() { return true; } - int inputHLS::getFirstPlaylistToReload(){ - //at this point, we need to check which playlist we need to reload, and keep reading from that playlist until EndOfPlaylist + int inputHLS::getFirstPlaylistToReload() { + //at this point, we need to check which playlist we need to reload, and keep reading from that playlist until EndOfPlaylist std::vector::iterator result = std::min_element(reloadNext.begin(), reloadNext.end()); - int playlistToReload = std::distance(reloadNext.begin(), result); - // std::cout << "min element at: " << std::distance(std::begin(reloadNext), result); - // currentPlaylist = playlistToReload; - return playlistToReload; + return std::distance(reloadNext.begin(), result); } void inputHLS::getNext(bool smart) { @@ -415,37 +629,30 @@ INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); thisPacket.null(); while (!hasPacket && config->is_active) { - //tsBuf.FromStream(in); - - if(isUrl){ + if (playlists[currentPlaylist].isUrl()) { - if ((playlists[currentPlaylist].packetPtr - playlists[currentPlaylist].source.data() +188) <= playlists[currentPlaylist].source.size()) - { - tsBuf.FromPointer(playlists[currentPlaylist].packetPtr); - endOfFile = false; - }else{ - endOfFile = true; + endOfFile = playlists[currentPlaylist].atEnd(); + if (!endOfFile){ + tsBuf.FromPointer(playlists[currentPlaylist].packetPtr); + playlists[currentPlaylist].packetPtr += 188; } - playlists[currentPlaylist].packetPtr += 188; - }else{ + } else { tsBuf.FromStream(in); endOfFile = in.eof(); } //eof flag is set after unsuccesful read, so check again - //if(in.eof()) { - - if(endOfFile){ + if (endOfFile) { tsStream.finish(); } - if(playlists[currentPlaylist].playlistType == LIVE){ + if (playlists[currentPlaylist].playlistType == LIVE) { hasPacket = tsStream.hasPacketOnEachTrack() || (endOfFile && tsStream.hasPacket()); - }else{ + } else { - if(!selectedTracks.size()) { + if (!selectedTracks.size()) { return; } @@ -453,193 +660,127 @@ INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); hasPacket = tsStream.hasPacket(getMappedTrackId(tid)); } - if(endOfFile && !hasPacket) { - INFO_MSG("end of file: bootsecs: %d",Util::bootSecs()); + if (endOfFile && !hasPacket) { + if (playlists[currentPlaylist].playlistType == LIVE) { - if(playlists[currentPlaylist].playlistType == LIVE){ - - int a = getFirstPlaylistToReload(); - int segmentTime = 30; - HIGH_MSG("need to reload playlist %d, time: %d",a,reloadNext[a]- Util::bootSecs()); + int a = getFirstPlaylistToReload(); + int segmentTime = 30; + HIGH_MSG("need to reload playlist %d, time: %d", a, reloadNext[a] - Util::bootSecs()); - int f = firstSegment(); - if(f >= 0){ - segmentTime = playlists[f].entries.front().timestamp - Util::bootSecs(); - } - - int playlistTime = reloadNext.at(currentPlaylist) - Util::bootSecs() -1; - - if(playlistTime < segmentTime){ -// printBuffer(); - INFO_MSG("playlist waiting... %d ms",playlistTime * 900); - - while(playlistTime > 0){ - Util::wait(900); - nProxy.userClient.keepAlive(); - playlistTime--; - } - - //on eof, first reload playlist. - if(reloadPlaylist(playlists[a])){ -// INFO_MSG("playlist %d reloaded!",a); -// playlists[currentPlaylist].noChangeCount = 0; - }else{ - - // INFO_MSG("playlist %d reloaded without changes!, checked %d times...",currentPlaylist,playlists[currentPlaylist].noChangeCount); - // playlists[currentPlaylist].noChangeCount++; - -// if(playlists[currentPlaylist].noChangeCount > 3){ - // INFO_MSG("enough!"); -// return; - // } - } - } - - //check if other playlists have to be reloaded, and do so. -// printBuffer(); - getNextSegment(); + int f = firstSegment(); + if (f >= 0) { + segmentTime = playlists[f].entries.front().timestamp - Util::bootSecs(); } - int b = Util::bootSecs(); + int playlistTime = reloadNext.at(currentPlaylist) - Util::bootSecs() - 1; - if(!readNextFile()) { + if (playlistTime < segmentTime) { + while (playlistTime > 0) { + Util::wait(900); + nProxy.userClient.keepAlive(); + playlistTime--; + } - if(playlists[currentPlaylist].playlistType == LIVE){ - //need to reload all available playlists. update the map with the amount of ms to wait before the next check. - - if(reloadNext.size() < playlists.size()) - { - reloadNext.push_back(Util::bootSecs() + playlists[currentPlaylist].waitTime); - currentPlaylist++; - }else{ - //set specific elements with the correct bootsecs() - //reloadNext.at(currentPlaylist) = Util::bootSecs() + playlists[currentPlaylist].waitTime; - reloadNext.at(currentPlaylist) = b + playlists[currentPlaylist].waitTime; - //for(int i = 0; i < reloadNext.size(); i++) - //{ - //INFO_MSG("Reload vector index %d, time: %d", i,reloadNext[i]- Util::bootSecs()); - //} - initDone = true; - } - - int timeToWait = reloadNext.at(currentPlaylist) - Util::bootSecs(); + //update reloadTime before reading the playlist + reloadNext.at(playlists[a].id) = Util::bootSecs() + playlists[a].waitTime; + playlists[a].reload(); + } - if(playlists[currentPlaylist].vodLive){ - //if(currentPlaylist == playlists.size()-1)//if last playlist, put a delay - if(currentPlaylist == 0) - { - timeToWait = 0; //playlists[currentPlaylist].waitTime; - }else{ - timeToWait = 0; - } - }else{ - //at this point, we need to check which playlist we need to reload, and keep reading from that playlist until EndOfPlaylist - std::vector::iterator result = std::min_element(reloadNext.begin(), reloadNext.end()); - int playlistToReload = std::distance(reloadNext.begin(), result); - currentPlaylist = playlistToReload; - } - //dont wait the first time. - if(timeToWait > 0 && initDone && playlists[currentPlaylist].noChangeCount > 0) - { - if(timeToWait > playlists[currentPlaylist].waitTime){ - WARN_MSG("something is not right..."); - return; - } + waitForNextSegment(); + } - if(playlists[currentPlaylist].noChangeCount < 2){ - timeToWait /= 2;//wait half of the segment size when no segments are found. - } - }else{ -// INFO_MSG("no need to delay, update time already past"); - } - - if(playlists[currentPlaylist].playlistEnd){ - INFO_MSG("Playlist %d has reached his end!"); - thisPacket.null(); - return; - } + int b = Util::bootSecs(); - if(playlists[currentPlaylist].vodLive){ - currentPlaylist++; -// INFO_MSG("currentplaylist: %d, playlistsize: %d",currentPlaylist, playlists.size()); - if(currentPlaylist >= playlists.size()) - { - currentPlaylist = 0; - for(int i = 0;i < playlists.size();i++) - { - INFO_MSG("p %d entry 0: %s",i, playlists[i].entries[0].filename.c_str()); - } + if (!readNextFile()) { - Util::wait(1000); - } - } - }else{ + if (playlists[currentPlaylist].playlistType != LIVE) { + return; + } + //need to reload all available playlists. update the map with the amount of ms to wait before the next check. + + //set specific elements with the correct bootsecs() + reloadNext.at(currentPlaylist) = b + playlists[currentPlaylist].waitTime; + + int timeToWait = reloadNext.at(currentPlaylist) - Util::bootSecs(); + + //at this point, we need to check which playlist we need to reload, and keep reading from that playlist until EndOfPlaylist + std::vector::iterator result = std::min_element(reloadNext.begin(), reloadNext.end()); + int playlistToReload = std::distance(reloadNext.begin(), result); + currentPlaylist = playlistToReload; + + //dont wait the first time. + if (timeToWait > 0 && playlists[currentPlaylist].initDone && playlists[currentPlaylist].noChangeCount > 0) { + if (timeToWait > playlists[currentPlaylist].waitTime) { + WARN_MSG("something is not right..."); return; } - } - if(isUrl){ - if (playlists[currentPlaylist].packetPtr - playlists[currentPlaylist].source.c_str() +188 <= playlists[currentPlaylist].source.size()) - { - tsBuf.FromPointer(playlists[currentPlaylist].packetPtr); - endOfFile = false; - }else{ - endOfFile = true; + if (playlists[currentPlaylist].noChangeCount < 2) { + timeToWait /= 2;//wait half of the segment size when no segments are found. } + } + if (playlists[currentPlaylist].playlistEnd) { + INFO_MSG("Playlist %d has reached his end!"); + thisPacket.null(); + return; + } + + } + + if (playlists[currentPlaylist].isUrl()) { + endOfFile = playlists[currentPlaylist].atEnd(); + if (!endOfFile){ + tsBuf.FromPointer(playlists[currentPlaylist].packetPtr); playlists[currentPlaylist].packetPtr += 188; - - }else{ - tsBuf.FromStream(in); - endOfFile = in.eof(); } - }else{ -// INFO_MSG("not eof, read: %d, total: %d", packetPtr-source.data(), source.size()); + } else { + tsBuf.FromStream(in); + endOfFile = in.eof(); } + } - if(!endOfFile){ - tsStream.parse(tsBuf, 0); - if(playlists[currentPlaylist].playlistType == LIVE){ - hasPacket = tsStream.hasPacketOnEachTrack() || (endOfFile && tsStream.hasPacket()); - }else{ - hasPacket = tsStream.hasPacket(getMappedTrackId(tid)); - } + if (!endOfFile) { + tsStream.parse(tsBuf, 0); + if (playlists[currentPlaylist].playlistType == LIVE) { + hasPacket = tsStream.hasPacketOnEachTrack() || (endOfFile && tsStream.hasPacket()); + } else { + hasPacket = tsStream.hasPacket(getMappedTrackId(tid)); } + } } - - if(playlists[currentPlaylist].playlistType == LIVE){ + + if (playlists[currentPlaylist].playlistType == LIVE) { tsStream.getEarliestPacket(thisPacket); - tid = getOriginalTrackId(currentPlaylist,thisPacket.getTrackId()); - }else{ - tsStream.getPacket(getMappedTrackId( tid), thisPacket); + tid = getOriginalTrackId(currentPlaylist, thisPacket.getTrackId()); + } else { + tsStream.getPacket(getMappedTrackId(tid), thisPacket); } - if(!thisPacket) { + if (!thisPacket) { FAIL_MSG("Could not getNExt TS packet!"); return; } //overwrite trackId - Bit::htobl(thisPacket.getData()+8,tid); + Bit::htobl(thisPacket.getData() + 8, tid); } - void inputHLS::readPMT(){ - if(isUrl){ - + void inputHLS::readPMT() { + if (playlists[currentPlaylist].isUrl()) { size_t bpos; TS::Packet tsBuffer; - const char *tmpPtr = playlists[currentPlaylist].source.data(); - - while (!tsStream.hasPacketOnEachTrack() && (tmpPtr - playlists[currentPlaylist].source.c_str() +188 <= playlists[currentPlaylist].source.size())) - { + const char * tmpPtr = playlists[currentPlaylist].source.data(); + + while (!tsStream.hasPacketOnEachTrack() && (tmpPtr - playlists[currentPlaylist].source.c_str() + 188 <= playlists[currentPlaylist].source.size())) { tsBuffer.FromPointer(tmpPtr); tsStream.parse(tsBuffer, 0); tmpPtr += 188; } tsStream.partialClear(); - }else{ + } else { size_t bpos = in.tellg(); in.seekg(0, in.beg); TS::Packet tsBuffer; @@ -650,17 +791,18 @@ INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); //tsStream.clear(); tsStream.partialClear(); //?? partialclear gebruiken?, input raakt hierdoor inconsistent.. - in.seekg(bpos,in.beg); + in.seekg(bpos, in.beg); } } + //Note: bpos is overloaded here for playlist entry! void inputHLS::seek(int seekTime) { INFO_MSG("SEEK"); tsStream.clear(); readPMT(); int trackId = 0; - - unsigned long seekPos = 0xFFFFFFFFull; + + unsigned long plistEntry = 0xFFFFFFFFull; for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++) { unsigned long thisBPos = 0; for (std::deque::iterator keyIt = myMeta.tracks[*it].keys.begin(); keyIt != myMeta.tracks[*it].keys.end(); keyIt++) { @@ -669,46 +811,47 @@ INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); } thisBPos = keyIt->getBpos(); } - if (thisBPos < seekPos) { - seekPos = thisBPos; + if (thisBPos < plistEntry) { + plistEntry = thisBPos; trackId = *it; } } - int playlistId = getMappedTrackPlaylist(trackId); - int entryId = seekPos-1; - if(entryId < 0) { + if (plistEntry < 1){ WARN_MSG("attempted to seek outside the file"); return; } - currentIndex = entryId; - currentPlaylist = playlistId; - - if(isUrl){ - openURL((playlists[currentPlaylist].uri_root+ playlists[currentPlaylist].entries.at(entryId).filename).c_str(), playlists[currentPlaylist]); + currentIndex = plistEntry - 1; + currentPlaylist = getMappedTrackPlaylist(trackId); - }else{ + Playlist & curPlaylist = playlists[currentPlaylist]; + playListEntries & entry = curPlaylist.entries.at(currentIndex); + if (curPlaylist.isUrl()) { + curPlaylist.loadURL(curPlaylist.uri_root + entry.filename); + } else { in.close(); - in.open((playlists[currentPlaylist].uri_root+ playlists[currentPlaylist].entries.at(entryId).filename).c_str()); + in.open((curPlaylist.uri_root + entry.filename).c_str()); } } int inputHLS::getEntryId(int playlistId, uint64_t bytePos) { - if(bytePos == 0) { return 0;} + if (bytePos == 0) { + return 0; + } - for(int i = 0;i bytePos) { - return i-1; + for (int i = 0; i < playlists[playlistId].entries.size(); i++) { + if (playlists[playlistId].entries.at(i).bytePos > bytePos) { + return i - 1; } } - return playlists[playlistId].entries.size()-1; + return playlists[playlistId].entries.size() - 1; } - int inputHLS::getOriginalTrackId(int playlistId,int id) { - return pidMapping[(playlistId << 16) + id]; + int inputHLS::getOriginalTrackId(int playlistId, int id) { + return pidMapping[(playlistId << 16) + id]; } int inputHLS::getMappedTrackId(int id) { @@ -720,165 +863,76 @@ INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); } ///Very first function to be called on a regular playlist or variant playlist. - bool inputHLS::initPlaylist(std::string uri) { + bool inputHLS::initPlaylist(const std::string & uri) { std::string line; bool ret = false; startTime = Util::bootSecs(); + std::string init_source; + + std::string playlistRootPath = uri.substr(0, uri.rfind("/") + 1); - playlistRootPath = uri.substr(0,uri.rfind("/")+1); - std::istringstream urlSource; std::ifstream fileSource; - - if(uri.compare(0,7,"http://") == 0){ + + bool isUrl = false; + if (uri.compare(0, 7, "http://") == 0) { isUrl = true; Playlist p; - openURL(uri,p); + p.loadURL(uri); init_source = p.source; urlSource.str(init_source); - }else{ + } else { fileSource.open(uri.c_str()); } - std::istream & input = (isUrl ? (std::istream&)urlSource : (std::istream&)fileSource); - std::getline(input,line); + std::istream & input = (isUrl ? (std::istream &)urlSource : (std::istream &)fileSource); + std::getline(input, line); - while(std::getline(input, line)) { - if(!line.empty()){ //skip empty lines in the playlist - if (line.compare(0,17,"#EXT-X-STREAM-INF") == 0) { + while (std::getline(input, line)) { + if (!line.empty()) { //skip empty lines in the playlist + if (line.compare(0, 17, "#EXT-X-STREAM-INF") == 0) { //this is a variant playlist file.. next line is an uri to a playlist file std::getline(input, line); ret = readPlaylist(playlistRootPath + line); - }else if(line.compare(0,12,"#EXT-X-MEDIA") == 0){ + } else if (line.compare(0, 12, "#EXT-X-MEDIA") == 0) { //this is also a variant playlist, but streams need to be processed another way std::string mediafile; - if(line.compare(18,5,"AUDIO") == 0) { - //find URI attribute - int pos = line.find("URI"); - if (pos != std::string::npos) { - mediafile = line.substr(pos+5,line.length()-pos-6); - ret = readPlaylist(playlistRootPath + mediafile); - } + if (line.compare(18, 5, "AUDIO") == 0) { + //find URI attribute + int pos = line.find("URI"); + if (pos != std::string::npos) { + mediafile = line.substr(pos + 5, line.length() - pos - 6); + ret = readPlaylist(playlistRootPath + mediafile); + } } - }else if(line.compare(0,7,"#EXTINF") ==0) { + } else if (line.compare(0, 7, "#EXTINF") == 0) { //current file is not a variant playlist, but regular playlist. ret = readPlaylist(uri); break; - }else{ + } else { //ignore wrong lines - WARN_MSG("ignore wrong line: %s",line.c_str()); + WARN_MSG("ignore wrong line: %s", line.c_str()); } } } - if(!isUrl){ + if (!isUrl){ fileSource.close(); } - - initDone = true; + return ret; } ///Function for reading every playlist. - bool inputHLS::readPlaylist(std::string uri) { - std::string line; - std::string key; - std::string val; - Playlist p; - int count = 0; - p.lastTimestamp = 0; - p.uri = uri; - uint64_t totalBytes = 0; - p.uri_root = uri.substr(0,uri.rfind("/")+1); - p.playlistType = LIVE; //TMP - INFO_MSG("readplaylist: %s",uri.c_str()); - - std::istringstream urlSource; - std::ifstream fileSource; - + bool inputHLS::readPlaylist(const std::string & uri) { + Playlist p(uri); p.id = playlists.size(); - - if(uri.compare(0,7,"http://") == 0){ - isUrl = true; - openURL(uri,p); - urlSource.str(p.source); - }else{ - fileSource.open(uri.c_str()); - isUrl = false; - } - - std::istream & input = (isUrl ? (std::istream&)urlSource : (std::istream&)fileSource); - std::getline(input,line); - - while(std::getline(input, line)) { - cleanLine(line); - - if(!line.empty()){ - if (line.compare(0,7,"#EXT-X-") == 0) { - size_t pos = line.find(":"); - key = line.substr(7,pos-7); - val = line.c_str() + pos + 1; - - if(key == "VERSION") { - p.version = atoi(line.c_str()+pos+1); - } - - if(key == "TARGETDURATION") { - p.targetDuration = atoi(line.c_str()+pos+1); - p.waitTime = p.targetDuration; - } - - if(key == "MEDIA-SEQUENCE") { - p.media_sequence = atoi(line.c_str()+pos+1); - p.lastFileIndex = p.media_sequence; - } - - if(key == "PLAYLIST-TYPE") { - if(val == "VOD") { - p.playlistType = VOD; - }else if(val == "LIVE") { - p.playlistType = LIVE; - }else if(val == "EVENT") { - p.playlistType = EVENT; - } - } - - if(key == "ENDLIST"){ - //end of playlist reached! - p.playlistEnd = true; - p.playlistType = VOD; - } - - } - else if(line.compare(0,7,"#EXTINF") == 0) { - float f = atof(line.c_str()+8); - std::string filename; - std::getline(input,filename); - addEntryToPlaylist(p,filename,f,totalBytes); - count++; - } - else { - VERYHIGH_MSG("ignoring wrong line: %s.", line.c_str()); - continue; - } - } - } - - if(isUrl) - { - p.playlistType = LIVE;//VOD over HTTP needs to be processed as LIVE. - //p.vodLive= true; - - p.playlistEnd = false; - fileSource.close(); - } - //set size of reloadNext to playlist count with default value 0 playlists.push_back(p); - - if(reloadNext.size() < playlists.size()){ + + if (reloadNext.size() < playlists.size()) { reloadNext.resize(playlists.size()); } @@ -886,331 +940,77 @@ INFO_MSG("end stream header tracks: %d",myMeta.tracks.size()); return true; } - ///For debugging purposes only. prints the entries for every playlist which needs to be processed. - void inputHLS::printBuffer() - { - INFO_MSG("--------------------------- printbuffer---------------------#######"); - for(int i = 0;i < playlists.size();i++){ - for(int j = 0;j 0){ - INFO_MSG("breaking here!!!!!!!!!!!!"); - fileSource.close(); - return true; - - break;//max files to process - }*/ - - } - - if(isUrl){ - fileSource.close(); - } - - ret = (count >0); - - if(ret){ - - p.noChangeCount = 0; - }else{ -// INFO_MSG("playlist %d reloaded without changes!, checked %d times...",p.id,p.noChangeCount); - p.noChangeCount++; - if(p.noChangeCount > 3){ - VERYHIGH_MSG("enough!"); - //return; - } - } - - return ret; - } - - //remove trailing \r for windows generated playlist files - int inputHLS::cleanLine(std::string &s) { - if (s.length() > 0 && s.at( s.length() - 1 ) == '\r') { - s.erase(s.size() - 1); - } - } - - bool inputHLS::openURL(std::string urlString, Playlist &p){ - //HTTP::URL url("http://nikujkjk"); - HIGH_MSG("opening URL: %s",urlString.c_str()); - - HTTP::URL url(urlString); - if (url.protocol != "http"){ - FAIL_MSG("Protocol %s is not supported", url.protocol.c_str()); - return false; - } - - //if connection is open, reuse this connection - if(!conn){ -// INFO_MSG("init not connected"); - conn = Socket::Connection(url.host, url.getPort(), false); - if(!conn){ - INFO_MSG("Failed to reach %s on port %lu", url.host.c_str(), url.getPort()); - return false; - } - - } - - HTTP::Parser http; - http.url = "/" + url.path; - http.method = "GET"; - http.SetHeader("Host", url.host); - http.SetHeader("X-MistServer", PACKAGE_VERSION); - - conn.SendNow(http.BuildRequest()); - http.Clean(); - - unsigned int startTime = Util::epoch(); - p.source.clear(); - p.packetPtr = 0; - while ((Util::epoch() - startTime < 10) && (conn || conn.Received().size())){ - if (conn.spool() || conn.Received().size()){ - if (http.Read(conn)){ - p.source = http.body; - p.packetPtr = p.source.data(); - conn.close(); - return true; - } - } - } - - if (conn){ - FAIL_MSG("Timeout!"); - conn.close(); - }else{ - FAIL_MSG("Lost connection!"); - INFO_MSG("bytes received %d",conn.Received().size()); - } - - return false; - } - ///Read next .ts file from the playlist. (from the list of entries which needs to be processed) bool inputHLS::readNextFile() { tsStream.clear(); + Playlist & curList = playlists[currentPlaylist]; - if(!playlists[currentPlaylist].entries.size()){ - VERYHIGH_MSG("no entries found in playlist: %d!",currentPlaylist); + if (!curList.entries.size()) { + VERYHIGH_MSG("no entries found in playlist: %d!", currentPlaylist); return false; } - std::string url = (playlists[currentPlaylist].uri_root + playlists[currentPlaylist].entries.front().filename).c_str(); + std::string url = (curList.uri_root + curList.entries.front().filename).c_str(); - if(isUrl){ - if(openURL(url,playlists[currentPlaylist])){ - playlists[currentPlaylist].entries.pop_front(); //remove the item which is opened for reading. - }else{ - - } + if (curList.isUrl() && curList.loadURL(url)){ + curList.entries.pop_front(); //remove the item which is opened for reading. } - if(playlists[currentPlaylist].playlistType == LIVE){ + if (curList.playlistType == LIVE) { in.close(); in.open(url.c_str()); - // INFO_MSG("\t\t\t\t\t\t\t\t ############ reading segment: %s for playlist: %d",url.c_str(), currentPlaylist) ; - if(in.good()){ - playlists[currentPlaylist].entries.pop_front(); //remove the item which is opened for reading. - return true; - }else{ - return false; - } - }else{ - currentIndex++; - if(playlists[currentPlaylist].entries.size() <= currentIndex) { - INFO_MSG("end of playlist reached!"); - return false; - }else{ - in.close(); - url = playlists[currentPlaylist].uri_root + playlists[currentPlaylist].entries.at(currentIndex).filename; - - // INFO_MSG("\t\t\t\t\t\t\t\t ############ reading segment: %s for playlist: %d",url.c_str(), currentPlaylist) ; - in.open(url.c_str()); + if (in.good()) { + curList.entries.pop_front(); //remove the item which is opened for reading. return true; } + return false; } + ++currentIndex; + if (curList.entries.size() <= currentIndex) { + INFO_MSG("end of playlist reached!"); + return false; + } + in.close(); + url = curList.uri_root + curList.entries.at(currentIndex).filename; + + in.open(url.c_str()); + return true; } ///return the playlist id from which we need to read the first upcoming segment by timestamp. this will keep the playlists in sync while reading segments. - int inputHLS::firstSegment(){ - bool foundSegment = false; - int firstTimeStamp = 0; - int tmp = 0; + int inputHLS::firstSegment() { + uint64_t firstTimeStamp = 0; + int tmpId = -1; - if(playlists.size() <=0){ - //do nothing, there is only one playlist, but return true when there are segments to process - return (playlists[0].entries.size() > 0); - } - - for(int i = 0;i 0){ - if(playlists[i].entries.front().timestamp < firstTimeStamp || !foundSegment){ - firstTimeStamp = playlists[i].entries.front().timestamp; - foundSegment = true; -// currentPlaylist = i; - tmp = i; + for (std::vector::iterator pListIt = playlists.begin(); pListIt != playlists.end(); pListIt++){ + if (pListIt->entries.size()) { + if (pListIt->entries.front().timestamp < firstTimeStamp || tmpId < 0) { + firstTimeStamp = pListIt->entries.front().timestamp; + tmpId = pListIt->id; } } } - - if(foundSegment){ - return tmp; - }else{ - return -1; - } + return tmpId; } //read the next segment - bool inputHLS::getNextSegment(){ - int tmp = 0; - bool foundSegment = false; - - tmp = firstSegment(); - foundSegment = (tmp >= 0); -// currentPlaylist = tmp; - - if(foundSegment){ - int segmentTime = playlists[tmp].entries.front().timestamp - Util::bootSecs(); - if(playlists[tmp].entries.front().timestamp - Util::bootSecs() > 0) - { - int t = playlists[tmp].entries.front().timestamp - Util::bootSecs() -1; - while(t > 1){ - Util::wait(1000); - t--; - nProxy.userClient.keepAlive(); - } -// printBuffer(); - } - - }else{ + void inputHLS::waitForNextSegment() { + uint32_t pListId = firstSegment(); + if (pListId == -1){ VERYHIGH_MSG("no segments found!"); + return; + } + int segmentTime = playlists[pListId].entries.front().timestamp - Util::bootSecs(); + if (segmentTime){ + --segmentTime; + while (segmentTime > 1) { + Util::wait(1000); + --segmentTime; + continueNegotiate(); + nProxy.userClient.keepAlive(); + } } - - //first segment is set currentPlaylist with the first entry. - return foundSegment; } - } diff --git a/src/input/input_hls.h b/src/input/input_hls.h index dc25bcea..30b09d5a 100644 --- a/src/input/input_hls.h +++ b/src/input/input_hls.h @@ -28,31 +28,34 @@ namespace Mist { class Playlist { public: - Playlist(); - std::string codecs; - std::string video; - std::string audio; - std::string uri; - std::string uri_root; + Playlist(const std::string & uriSrc = ""); + bool atEnd() const; + bool isUrl() const; + bool reload(); + void addEntry(const std::string & filename, float duration, uint64_t & totalBytes); + bool loadURL(const std::string & loadUrl); - std::string source; - const char *packetPtr; + std::string uri; + std::string uri_root; - int id; - bool playlistEnd; - int noChangeCount; - int version; - int targetDuration; - uint64_t media_sequence; - int lastFileIndex; - int waitTime; - bool vodLive; - PlaylistType playlistType; - std::deque entries; - int entryCount; - int programId; - int bandwidth; - unsigned int lastTimestamp; + std::string source; + const char *packetPtr; + + int id; + bool initDone; + bool playlistEnd; + int noChangeCount; + int version; + uint64_t media_sequence; + int lastFileIndex; + + + int waitTime; + PlaylistType playlistType; + std::deque entries; + int entryCount; + unsigned int lastTimestamp; + unsigned int startTime; }; @@ -79,9 +82,6 @@ namespace Mist { int media_sequence; bool endPlaylist; int currentPlaylist; - - bool initDone; - std::string init_source; //std::vector entries; std::vector playlists; @@ -89,28 +89,21 @@ namespace Mist { std::map pidMapping; std::map pidMappingR; - std::string playlistFile; - std::string playlistRootPath; std::vector reloadNext; - - bool liveStream; int currentIndex; std::string currentFile; std::ifstream in; - bool isUrl; TS::Stream tsStream;///