Support for byte ranges in HLS input
This commit is contained in:
		
							parent
							
								
									7a3fd0c280
								
							
						
					
					
						commit
						6a3ae16b2d
					
				
					 2 changed files with 122 additions and 56 deletions
				
			
		| 
						 | 
					@ -112,10 +112,10 @@ namespace Mist{
 | 
				
			||||||
  static std::map<uint64_t, Playlist> playlistMapping;
 | 
					  static std::map<uint64_t, Playlist> playlistMapping;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Local RAM buffer for recently accessed segments
 | 
					  /// Local RAM buffer for recently accessed segments
 | 
				
			||||||
  std::map<std::string, Util::ResizeablePointer> segBufs;
 | 
					  std::map<playListEntries, Util::ResizeablePointer> segBufs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Order of adding/accessing for local RAM buffer of segments
 | 
					  /// Order of adding/accessing for local RAM buffer of segments
 | 
				
			||||||
  std::deque<std::string> segBufAccs;
 | 
					  std::deque<playListEntries> segBufAccs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Order of adding/accessing sizes for local RAM buffer of segments
 | 
					  /// Order of adding/accessing sizes for local RAM buffer of segments
 | 
				
			||||||
  std::deque<size_t> segBufSize;
 | 
					  std::deque<size_t> segBufSize;
 | 
				
			||||||
| 
						 | 
					@ -264,6 +264,7 @@ namespace Mist{
 | 
				
			||||||
  bool SegmentDownloader::atEnd() const{
 | 
					  bool SegmentDownloader::atEnd() const{
 | 
				
			||||||
    if (!isOpen || !currBuf){return true;}
 | 
					    if (!isOpen || !currBuf){return true;}
 | 
				
			||||||
    if (buffered){return currBuf->size() <= offset + 188;}
 | 
					    if (buffered){return currBuf->size() <= offset + 188;}
 | 
				
			||||||
 | 
					    if (stopAtByte && (stopAtByte - startAtByte) <= offset + 188){return true;}
 | 
				
			||||||
    return !segDL && currBuf->size() <= offset + 188;
 | 
					    return !segDL && currBuf->size() <= offset + 188;
 | 
				
			||||||
    // return (packetPtr - segDL.const_data().data() + 188) > segDL.const_data().size();
 | 
					    // return (packetPtr - segDL.const_data().data() + 188) > segDL.const_data().size();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -334,6 +335,7 @@ namespace Mist{
 | 
				
			||||||
      }else{
 | 
					      }else{
 | 
				
			||||||
        if (!currBuf){return false;}
 | 
					        if (!currBuf){return false;}
 | 
				
			||||||
        size_t retries = 0;
 | 
					        size_t retries = 0;
 | 
				
			||||||
 | 
					        if (stopAtByte && (stopAtByte - startAtByte) <= currBuf->size()){return false;}
 | 
				
			||||||
        while (segDL && currBuf->size() < offset + 188 + 188){
 | 
					        while (segDL && currBuf->size() < offset + 188 + 188){
 | 
				
			||||||
          size_t preSize = currBuf->size();
 | 
					          size_t preSize = currBuf->size();
 | 
				
			||||||
          segDL.readSome(offset + 188 + 188 - currBuf->size(), *this);
 | 
					          segDL.readSome(offset + 188 + 188 - currBuf->size(), *this);
 | 
				
			||||||
| 
						 | 
					@ -348,7 +350,7 @@ namespace Mist{
 | 
				
			||||||
                  segDL.close();
 | 
					                  segDL.close();
 | 
				
			||||||
                  return false;
 | 
					                  return false;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                segDL.seek(currBuf->size());
 | 
					                segDL.seek(startAtByte+currBuf->size());
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (currBuf->size() <= preSize){
 | 
					            if (currBuf->size() <= preSize){
 | 
				
			||||||
| 
						 | 
					@ -387,7 +389,7 @@ namespace Mist{
 | 
				
			||||||
    segBufTotalSize += segBufSize.front();
 | 
					    segBufTotalSize += segBufSize.front();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  size_t SegmentDownloader::getDataCallbackPos() const{return currBuf->size();}
 | 
					  size_t SegmentDownloader::getDataCallbackPos() const{return startAtByte+currBuf->size();}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Attempts to read a single TS packet from the current segment, setting packetPtr on success
 | 
					  /// Attempts to read a single TS packet from the current segment, setting packetPtr on success
 | 
				
			||||||
  void SegmentDownloader::close(){
 | 
					  void SegmentDownloader::close(){
 | 
				
			||||||
| 
						 | 
					@ -401,33 +403,41 @@ namespace Mist{
 | 
				
			||||||
    std::string hexKey = printhex(entry.keyAES, 16);
 | 
					    std::string hexKey = printhex(entry.keyAES, 16);
 | 
				
			||||||
    std::string hexIvec = printhex(entry.ivec, 16);
 | 
					    std::string hexIvec = printhex(entry.ivec, 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    MEDIUM_MSG("Loading segment: %s, key: %s, ivec: %s", entry.filename.c_str(), hexKey.c_str(),
 | 
					    MEDIUM_MSG("Loading segment: %s, key: %s, ivec: %s", entry.shortName().c_str(), hexKey.c_str(),
 | 
				
			||||||
               hexIvec.c_str());
 | 
					               hexIvec.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    startAtByte = entry.startAtByte;
 | 
				
			||||||
 | 
					    stopAtByte = entry.stopAtByte;
 | 
				
			||||||
    offset = 0;
 | 
					    offset = 0;
 | 
				
			||||||
    firstPacket = true;
 | 
					    firstPacket = true;
 | 
				
			||||||
    buffered = segBufs.count(entry.filename);
 | 
					    buffered = segBufs.count(entry);
 | 
				
			||||||
    if (!buffered){
 | 
					    if (!buffered){
 | 
				
			||||||
      HIGH_MSG("Reading non-cache: %s", entry.filename.c_str());
 | 
					      HIGH_MSG("Reading non-cache: %s", entry.shortName().c_str());
 | 
				
			||||||
      if (!segDL.open(entry.filename)){
 | 
					      if (!segDL.open(entry.filename)){
 | 
				
			||||||
        FAIL_MSG("Could not open %s", entry.filename.c_str());
 | 
					        FAIL_MSG("Could not open %s", entry.shortName().c_str());
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (!segDL){return false;}
 | 
					      if (!segDL){return false;}
 | 
				
			||||||
      //Remove cache entries while above 16MiB in total size, unless we only have 1 entry (we keep two at least at all times)
 | 
					      //Remove cache entries while above 16MiB in total size, unless we only have 1 entry (we keep two at least at all times)
 | 
				
			||||||
      while (segBufTotalSize > 16 * 1024 * 1024 && segBufs.size() > 1){
 | 
					      while (segBufTotalSize > 16 * 1024 * 1024 && segBufs.size() > 1){
 | 
				
			||||||
        HIGH_MSG("Dropping from segment cache: %s", segBufAccs.back().c_str());
 | 
					        HIGH_MSG("Dropping from segment cache: %s", segBufAccs.back().shortName().c_str());
 | 
				
			||||||
        segBufs.erase(segBufAccs.back());
 | 
					        segBufs.erase(segBufAccs.back());
 | 
				
			||||||
        segBufTotalSize -= segBufSize.back();
 | 
					        segBufTotalSize -= segBufSize.back();
 | 
				
			||||||
        segBufAccs.pop_back();
 | 
					        segBufAccs.pop_back();
 | 
				
			||||||
        segBufSize.pop_back();
 | 
					        segBufSize.pop_back();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      segBufAccs.push_front(entry.filename);
 | 
					      segBufAccs.push_front(entry);
 | 
				
			||||||
      segBufSize.push_front(0);
 | 
					      segBufSize.push_front(0);
 | 
				
			||||||
      currBuf = &(segBufs[entry.filename]);
 | 
					      currBuf = &(segBufs[entry]);
 | 
				
			||||||
 | 
					      // Non-seekable case is handled further down
 | 
				
			||||||
 | 
					      if (segDL.isSeekable() && startAtByte){
 | 
				
			||||||
 | 
					        //Seek to startAtByte position, since it's not the beginning of the file
 | 
				
			||||||
 | 
					        MEDIUM_MSG("Seeking to %zu", startAtByte);
 | 
				
			||||||
 | 
					        segDL.seek(startAtByte);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }else{
 | 
					    }else{
 | 
				
			||||||
      HIGH_MSG("Reading from segment cache: %s", entry.filename.c_str());
 | 
					      HIGH_MSG("Reading from segment cache: %s", entry.shortName().c_str());
 | 
				
			||||||
      currBuf = &(segBufs[entry.filename]);
 | 
					      currBuf = &(segBufs[entry]);
 | 
				
			||||||
      if (currBuf->rsize() != currBuf->size()){
 | 
					      if (currBuf->rsize() != currBuf->size()){
 | 
				
			||||||
        MEDIUM_MSG("Cache was incomplete (%zu/%" PRIu32 "), resuming", currBuf->size(), currBuf->rsize());
 | 
					        MEDIUM_MSG("Cache was incomplete (%zu/%" PRIu32 "), resuming", currBuf->size(), currBuf->rsize());
 | 
				
			||||||
        buffered = false;
 | 
					        buffered = false;
 | 
				
			||||||
| 
						 | 
					@ -436,23 +446,31 @@ namespace Mist{
 | 
				
			||||||
        HTTP::URL B = HTTP::localURIResolver().link(entry.filename);
 | 
					        HTTP::URL B = HTTP::localURIResolver().link(entry.filename);
 | 
				
			||||||
        if (A != B){
 | 
					        if (A != B){
 | 
				
			||||||
          if (!segDL.open(entry.filename)){
 | 
					          if (!segDL.open(entry.filename)){
 | 
				
			||||||
            FAIL_MSG("Could not open %s", entry.filename.c_str());
 | 
					            FAIL_MSG("Could not open %s", entry.shortName().c_str());
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (!segDL){return false;}
 | 
					          if (!segDL){return false;}
 | 
				
			||||||
          //Seek to current position in segment for resuming
 | 
					          //Seek to current position in segment for resuming
 | 
				
			||||||
          currBuf->truncate(currBuf->size() / 188 * 188);
 | 
					          MEDIUM_MSG("Seeking to %zu", currBuf->size()+startAtByte);
 | 
				
			||||||
          MEDIUM_MSG("Seeking to %zu", currBuf->size());
 | 
					          segDL.seek(currBuf->size()+startAtByte);
 | 
				
			||||||
          segDL.seek(currBuf->size());
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (!buffered){
 | 
					    if (!buffered){
 | 
				
			||||||
      // Allocate full size if known
 | 
					      // Allocate full size if known
 | 
				
			||||||
      if (segDL.getSize() != std::string::npos){currBuf->allocate(segDL.getSize());}
 | 
					      if (stopAtByte || segDL.getSize() != std::string::npos){currBuf->allocate(stopAtByte?(stopAtByte - startAtByte):segDL.getSize());}
 | 
				
			||||||
      // Download full segment if not seekable, pretend it was cached all along
 | 
					      // Download full segment if not seekable, pretend it was cached all along
 | 
				
			||||||
      if (!segDL.isSeekable()){
 | 
					      if (!segDL.isSeekable()){
 | 
				
			||||||
        segDL.readAll(*this);
 | 
					        segDL.readAll(*this);
 | 
				
			||||||
 | 
					        if (startAtByte || stopAtByte){
 | 
				
			||||||
 | 
					          WARN_MSG("Wasting data: downloaded whole segment due to unavailability of range requests, but caching only part of it");
 | 
				
			||||||
 | 
					          if (startAtByte){currBuf->shift(startAtByte);}
 | 
				
			||||||
 | 
					          if (stopAtByte){currBuf->truncate(stopAtByte - startAtByte);}
 | 
				
			||||||
 | 
					          //Overwrite the current segment size
 | 
				
			||||||
 | 
					          segBufTotalSize -= segBufSize.front();
 | 
				
			||||||
 | 
					          segBufSize.front() = currBuf->size();
 | 
				
			||||||
 | 
					          segBufTotalSize += segBufSize.front();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        buffered = true;
 | 
					        buffered = true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -487,6 +505,8 @@ namespace Mist{
 | 
				
			||||||
    std::string line;
 | 
					    std::string line;
 | 
				
			||||||
    std::string key;
 | 
					    std::string key;
 | 
				
			||||||
    std::string val;
 | 
					    std::string val;
 | 
				
			||||||
 | 
					    float segDur = 0.0;
 | 
				
			||||||
 | 
					    uint64_t startByte = std::string::npos, lenByte = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string keyMethod;
 | 
					    std::string keyMethod;
 | 
				
			||||||
    std::string keyUri;
 | 
					    std::string keyUri;
 | 
				
			||||||
| 
						 | 
					@ -527,6 +547,10 @@ namespace Mist{
 | 
				
			||||||
      cleanLine(line);
 | 
					      cleanLine(line);
 | 
				
			||||||
      if (line.empty()){continue;}// skip empty lines
 | 
					      if (line.empty()){continue;}// skip empty lines
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (line.compare(0, 7, "#EXTINF") == 0){
 | 
				
			||||||
 | 
					        segDur = atof(line.c_str() + 8);
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      if (line.compare(0, 7, "#EXT-X-") == 0){
 | 
					      if (line.compare(0, 7, "#EXT-X-") == 0){
 | 
				
			||||||
        size_t pos = line.find(":");
 | 
					        size_t pos = line.find(":");
 | 
				
			||||||
        key = line.substr(7, pos - 7);
 | 
					        key = line.substr(7, pos - 7);
 | 
				
			||||||
| 
						 | 
					@ -560,11 +584,26 @@ namespace Mist{
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            keys.insert(std::pair<std::string, std::string>(keyUri, std::string(keyPtr, keyLen)));
 | 
					            keys.insert(std::pair<std::string, std::string>(keyUri, std::string(keyPtr, keyLen)));
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (key == "BYTERANGE"){
 | 
				
			||||||
 | 
					          size_t atSign = val.find('@');
 | 
				
			||||||
 | 
					          if (atSign != std::string::npos){
 | 
				
			||||||
 | 
					            std::string len = val.substr(0, atSign);
 | 
				
			||||||
 | 
					            std::string pos = val.substr(atSign+1);
 | 
				
			||||||
 | 
					            lenByte = atoll(len.c_str());
 | 
				
			||||||
 | 
					            startByte = atoll(pos.c_str());
 | 
				
			||||||
 | 
					          }else{
 | 
				
			||||||
 | 
					            lenByte = atoll(val.c_str());
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (key == "TARGETDURATION"){
 | 
					        if (key == "TARGETDURATION"){
 | 
				
			||||||
          waitTime = atoi(val.c_str()) / 2;
 | 
					          waitTime = atoi(val.c_str()) / 2;
 | 
				
			||||||
          if (waitTime < 2){waitTime = 2;}
 | 
					          if (waitTime < 2){waitTime = 2;}
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Assuming this always comes before any segment
 | 
					        // Assuming this always comes before any segment
 | 
				
			||||||
| 
						 | 
					@ -572,47 +611,45 @@ namespace Mist{
 | 
				
			||||||
          // Reinit the segment counter
 | 
					          // Reinit the segment counter
 | 
				
			||||||
          firstIndex = atoll(val.c_str());
 | 
					          firstIndex = atoll(val.c_str());
 | 
				
			||||||
          bposCounter = firstIndex + 1;
 | 
					          bposCounter = firstIndex + 1;
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (key == "PROGRAM-DATE-TIME"){nextUTC = ISO8601toUnixmillis(val);}
 | 
					        if (key == "PROGRAM-DATE-TIME"){
 | 
				
			||||||
 | 
					          nextUTC = ISO8601toUnixmillis(val);
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (key == "PLAYLIST-TYPE"){
 | 
					        if (key == "PLAYLIST-TYPE"){
 | 
				
			||||||
          if (val == "VOD"){
 | 
					          if (val == "VOD"){
 | 
				
			||||||
            streamIsVOD = true;
 | 
					            streamIsVOD = true;
 | 
				
			||||||
            streamIsLive = false;
 | 
					            streamIsLive = false;
 | 
				
			||||||
            INFO_MSG("SIL=F");
 | 
					 | 
				
			||||||
          }else if (val == "LIVE"){
 | 
					          }else if (val == "LIVE"){
 | 
				
			||||||
            streamIsVOD = false;
 | 
					            streamIsVOD = false;
 | 
				
			||||||
            streamIsLive = true;
 | 
					            streamIsLive = true;
 | 
				
			||||||
            INFO_MSG("SIL=T");
 | 
					 | 
				
			||||||
          }else if (val == "EVENT"){
 | 
					          }else if (val == "EVENT"){
 | 
				
			||||||
            streamIsVOD = true;
 | 
					            streamIsVOD = true;
 | 
				
			||||||
            streamIsLive = true;
 | 
					            streamIsLive = true;
 | 
				
			||||||
            INFO_MSG("SIL=T");
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Once we see this tag, the entire playlist becomes VOD
 | 
					        // Once we see this tag, the entire playlist becomes VOD
 | 
				
			||||||
        if (key == "ENDLIST"){
 | 
					        if (key == "ENDLIST"){
 | 
				
			||||||
          streamIsLive = false;
 | 
					          streamIsLive = false;
 | 
				
			||||||
          INFO_MSG("SIL=F");
 | 
					 | 
				
			||||||
          streamIsVOD = true;
 | 
					          streamIsVOD = true;
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        VERYHIGH_MSG("ignoring line: %s.", line.c_str());
 | 
				
			||||||
        continue;
 | 
					        continue;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (line.compare(0, 7, "#EXTINF") != 0){
 | 
					      if (line[0] == '#'){
 | 
				
			||||||
        VERYHIGH_MSG("ignoring line: %s.", line.c_str());
 | 
					        VERYHIGH_MSG("ignoring line: %s.", line.c_str());
 | 
				
			||||||
        continue;
 | 
					        continue;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      float f = atof(line.c_str() + 8);
 | 
					 | 
				
			||||||
      std::string filename;
 | 
					 | 
				
			||||||
      std::getline(input, filename);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // check for already added segments
 | 
					      // check for already added segments
 | 
				
			||||||
      DONTEVEN_MSG("Current segment #%" PRIu64 ", last segment was #%" PRIu64 "", bposCounter, lastSegment);
 | 
					      DONTEVEN_MSG("Current segment #%" PRIu64 ", last segment was #%" PRIu64 "", bposCounter, lastSegment);
 | 
				
			||||||
      if (bposCounter > lastSegment){
 | 
					      if (bposCounter > lastSegment){
 | 
				
			||||||
        cleanLine(filename);
 | 
					 | 
				
			||||||
        char ivec[16];
 | 
					        char ivec[16];
 | 
				
			||||||
        if (keyIV.size()){
 | 
					        if (keyIV.size()){
 | 
				
			||||||
          parseKey(keyIV, ivec, 16);
 | 
					          parseKey(keyIV, ivec, 16);
 | 
				
			||||||
| 
						 | 
					@ -620,11 +657,14 @@ namespace Mist{
 | 
				
			||||||
          memset(ivec, 0, 16);
 | 
					          memset(ivec, 0, 16);
 | 
				
			||||||
          Bit::htobll(ivec + 8, bposCounter);
 | 
					          Bit::htobll(ivec + 8, bposCounter);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        addEntry(root.link(filename).getUrl(), filename, f, bposCounter, keys[keyUri], std::string(ivec, 16));
 | 
					        addEntry(root.link(line).getUrl(), line, segDur, bposCounter, keys[keyUri], std::string(ivec, 16), startByte, lenByte);
 | 
				
			||||||
        lastSegment = bposCounter;
 | 
					        lastSegment = bposCounter;
 | 
				
			||||||
        ++count;
 | 
					        ++count;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      nextUTC = 0;
 | 
					      nextUTC = 0;
 | 
				
			||||||
 | 
					      segDur = 0.0;
 | 
				
			||||||
 | 
					      startByte = std::string::npos;
 | 
				
			||||||
 | 
					      lenByte = 0;
 | 
				
			||||||
      ++bposCounter;
 | 
					      ++bposCounter;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -639,30 +679,9 @@ namespace Mist{
 | 
				
			||||||
    return (count > 0);
 | 
					    return (count > 0);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool Playlist::isSupportedFile(const std::string filename){
 | 
					 | 
				
			||||||
    // only ts files
 | 
					 | 
				
			||||||
    if (filename.find_last_of(".") != std::string::npos){
 | 
					 | 
				
			||||||
      std::string ext = filename.substr(filename.find_last_of(".") + 1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (ext.compare(0, 2, "ts") == 0){
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
      }else{
 | 
					 | 
				
			||||||
        DEBUG_MSG(DLVL_HIGH, "Not supported extension: %s", ext.c_str());
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // No extension. We assume it's fine.
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Adds playlist segments to be processed
 | 
					  /// Adds playlist segments to be processed
 | 
				
			||||||
  void Playlist::addEntry(const std::string &absolute_filename, const std::string &filename, float duration, uint64_t &bpos,
 | 
					  void Playlist::addEntry(const std::string &absolute_filename, const std::string &filename, float duration, uint64_t &bpos,
 | 
				
			||||||
                          const std::string &key, const std::string &iv){
 | 
					                          const std::string &key, const std::string &iv, uint64_t startByte, uint64_t lenByte){
 | 
				
			||||||
    // if (!isSupportedFile(filename)){
 | 
					 | 
				
			||||||
    //  WARN_MSG("Ignoring unsupported file: %s", filename.c_str());
 | 
					 | 
				
			||||||
    //  return;
 | 
					 | 
				
			||||||
    //}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    playListEntries entry;
 | 
					    playListEntries entry;
 | 
				
			||||||
    entry.filename = absolute_filename;
 | 
					    entry.filename = absolute_filename;
 | 
				
			||||||
    entry.relative_filename = filename;
 | 
					    entry.relative_filename = filename;
 | 
				
			||||||
| 
						 | 
					@ -687,8 +706,24 @@ namespace Mist{
 | 
				
			||||||
        if (!nextUTC && prev.mUTC){
 | 
					        if (!nextUTC && prev.mUTC){
 | 
				
			||||||
          nextUTC = prev.mUTC + (uint64_t)(prev.duration * 1000);
 | 
					          nextUTC = prev.mUTC + (uint64_t)(prev.duration * 1000);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        // If startByte unknown and we have a length, calculate it from previous entry
 | 
				
			||||||
 | 
					        if (startByte == std::string::npos && lenByte){
 | 
				
			||||||
 | 
					          if (filename == prev.relative_filename){startByte = prev.stopAtByte;}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }else{
 | 
				
			||||||
 | 
					        // If startByte unknown and we have a length, set to zero
 | 
				
			||||||
 | 
					        if (startByte == std::string::npos && lenByte){startByte = 0;}
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if ((lenByte && startByte == std::string::npos) || (!lenByte && startByte != std::string::npos)){
 | 
				
			||||||
 | 
					      WARN_MSG("Invalid byte range entry for segment: %s", filename.c_str());
 | 
				
			||||||
 | 
					      lenByte = 0;
 | 
				
			||||||
 | 
					      startByte = std::string::npos;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (lenByte){
 | 
				
			||||||
 | 
					      entry.startAtByte = startByte;
 | 
				
			||||||
 | 
					      entry.stopAtByte = startByte + lenByte;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    entry.mUTC = nextUTC;
 | 
					    entry.mUTC = nextUTC;
 | 
				
			||||||
    if (nextUTC && !oUTC){
 | 
					    if (nextUTC && !oUTC){
 | 
				
			||||||
| 
						 | 
					@ -716,7 +751,11 @@ namespace Mist{
 | 
				
			||||||
      // Note: This method requires never removing playlists, only adding.
 | 
					      // Note: This method requires never removing playlists, only adding.
 | 
				
			||||||
      // The mutex assures we have a unique count/number.
 | 
					      // The mutex assures we have a unique count/number.
 | 
				
			||||||
      if (!id){id = listEntries.size() + 1;}
 | 
					      if (!id){id = listEntries.size() + 1;}
 | 
				
			||||||
      HIGH_MSG("Adding entry '%s' to ID %u", filename.c_str(), id);
 | 
					      if (entry.startAtByte){
 | 
				
			||||||
 | 
					        HIGH_MSG("Adding entry '%s' (%" PRIu64 "-%" PRIu64 ") to ID %u", filename.c_str(), entry.startAtByte, entry.stopAtByte, id);
 | 
				
			||||||
 | 
					      }else{
 | 
				
			||||||
 | 
					        HIGH_MSG("Adding entry '%s' to ID %u", filename.c_str(), id);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      playlist_urls[JSON::Value(id).asString()] = relurl;
 | 
					      playlist_urls[JSON::Value(id).asString()] = relurl;
 | 
				
			||||||
      listEntries[id].push_back(entry);
 | 
					      listEntries[id].push_back(entry);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -867,6 +906,10 @@ namespace Mist{
 | 
				
			||||||
          memset(newEntry.ivec, 0, 16);
 | 
					          memset(newEntry.ivec, 0, 16);
 | 
				
			||||||
          memset(newEntry.keyAES, 0, 16);
 | 
					          memset(newEntry.keyAES, 0, 16);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (thisEntry.size() >= 11){
 | 
				
			||||||
 | 
					          newEntry.startAtByte = thisEntry[9u].asInt();
 | 
				
			||||||
 | 
					          newEntry.stopAtByte = thisEntry[10u].asInt();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        newList.push_back(newEntry);
 | 
					        newList.push_back(newEntry);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      listEntries[plNum] = newList;
 | 
					      listEntries[plNum] = newList;
 | 
				
			||||||
| 
						 | 
					@ -1059,6 +1102,10 @@ namespace Mist{
 | 
				
			||||||
        thisEntries.append(entryIt->wait);
 | 
					        thisEntries.append(entryIt->wait);
 | 
				
			||||||
        thisEntries.append(entryIt->ivec);
 | 
					        thisEntries.append(entryIt->ivec);
 | 
				
			||||||
        thisEntries.append(entryIt->keyAES);
 | 
					        thisEntries.append(entryIt->keyAES);
 | 
				
			||||||
 | 
					        if (entryIt->startAtByte || entryIt->stopAtByte){
 | 
				
			||||||
 | 
					          thisEntries.append(entryIt->startAtByte);
 | 
				
			||||||
 | 
					          thisEntries.append(entryIt->stopAtByte);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        thisPlaylist.append(thisEntries);
 | 
					        thisPlaylist.append(thisEntries);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      allEntries[JSON::Value(pListIt->first).asString()] = thisPlaylist;
 | 
					      allEntries[JSON::Value(pListIt->first).asString()] = thisPlaylist;
 | 
				
			||||||
| 
						 | 
					@ -1119,7 +1166,7 @@ namespace Mist{
 | 
				
			||||||
          if (!hasOffset && curList.at(segmentIndex).mUTC){
 | 
					          if (!hasOffset && curList.at(segmentIndex).mUTC){
 | 
				
			||||||
            hasOffset = true;
 | 
					            hasOffset = true;
 | 
				
			||||||
            DVRTimeOffsets[currentPlaylist] = (curList.at(segmentIndex).mUTC - zUTC) - packetTime;
 | 
					            DVRTimeOffsets[currentPlaylist] = (curList.at(segmentIndex).mUTC - zUTC) - packetTime;
 | 
				
			||||||
            INFO_MSG("Setting current live segment time offset to %" PRId64, DVRTimeOffsets[currentPlaylist]);
 | 
					            MEDIUM_MSG("Setting current live segment time offset to %" PRId64, DVRTimeOffsets[currentPlaylist]);
 | 
				
			||||||
            curList.at(segmentIndex).timeOffset = DVRTimeOffsets[currentPlaylist];
 | 
					            curList.at(segmentIndex).timeOffset = DVRTimeOffsets[currentPlaylist];
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (hasOffset || DVRTimeOffsets.count(currentPlaylist)){
 | 
					          if (hasOffset || DVRTimeOffsets.count(currentPlaylist)){
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,8 @@ namespace Mist{
 | 
				
			||||||
  struct playListEntries{
 | 
					  struct playListEntries{
 | 
				
			||||||
    std::string filename;
 | 
					    std::string filename;
 | 
				
			||||||
    std::string relative_filename;
 | 
					    std::string relative_filename;
 | 
				
			||||||
 | 
					    uint64_t startAtByte; ///< Byte position inside filename where to start reading
 | 
				
			||||||
 | 
					    uint64_t stopAtByte; ///< Byte position inside filename where to stop sending
 | 
				
			||||||
    uint64_t bytePos;
 | 
					    uint64_t bytePos;
 | 
				
			||||||
    uint64_t mUTC; ///< UTC unix millis timestamp of first packet, if known
 | 
					    uint64_t mUTC; ///< UTC unix millis timestamp of first packet, if known
 | 
				
			||||||
    float duration; ///< Duration of entry in seconds
 | 
					    float duration; ///< Duration of entry in seconds
 | 
				
			||||||
| 
						 | 
					@ -36,13 +38,29 @@ namespace Mist{
 | 
				
			||||||
      timestamp = 0;
 | 
					      timestamp = 0;
 | 
				
			||||||
      timeOffset = 0;
 | 
					      timeOffset = 0;
 | 
				
			||||||
      wait = 0;
 | 
					      wait = 0;
 | 
				
			||||||
 | 
					      startAtByte = 0;
 | 
				
			||||||
 | 
					      stopAtByte = 0;
 | 
				
			||||||
      for (size_t i = 0; i < 16; ++i){
 | 
					      for (size_t i = 0; i < 16; ++i){
 | 
				
			||||||
        ivec[i] = 0;
 | 
					        ivec[i] = 0;
 | 
				
			||||||
        keyAES[i] = 0;
 | 
					        keyAES[i] = 0;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    std::string shortName() const{
 | 
				
			||||||
 | 
					      if (!startAtByte && !stopAtByte){return filename;}
 | 
				
			||||||
 | 
					      std::string ret = filename;
 | 
				
			||||||
 | 
					      ret += " (";
 | 
				
			||||||
 | 
					      ret += JSON::Value(startAtByte).asString();
 | 
				
			||||||
 | 
					      ret += "-";
 | 
				
			||||||
 | 
					      ret += JSON::Value(stopAtByte).asString();
 | 
				
			||||||
 | 
					      ret += ")";
 | 
				
			||||||
 | 
					      return ret;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  inline bool operator< (const playListEntries a, const playListEntries b){
 | 
				
			||||||
 | 
					    return a.filename < b.filename || (a.filename == b.filename && a.startAtByte < b.startAtByte);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Keeps the segment entry list by playlist ID
 | 
					  /// Keeps the segment entry list by playlist ID
 | 
				
			||||||
  extern std::map<uint32_t, std::deque<playListEntries> > listEntries;
 | 
					  extern std::map<uint32_t, std::deque<playListEntries> > listEntries;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,6 +77,8 @@ namespace Mist{
 | 
				
			||||||
    bool atEnd() const;
 | 
					    bool atEnd() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private:
 | 
					  private:
 | 
				
			||||||
 | 
					    uint64_t startAtByte;
 | 
				
			||||||
 | 
					    uint64_t stopAtByte;
 | 
				
			||||||
    bool encrypted;
 | 
					    bool encrypted;
 | 
				
			||||||
    bool buffered;
 | 
					    bool buffered;
 | 
				
			||||||
    size_t offset;
 | 
					    size_t offset;
 | 
				
			||||||
| 
						 | 
					@ -79,8 +99,7 @@ namespace Mist{
 | 
				
			||||||
    bool isUrl() const;
 | 
					    bool isUrl() const;
 | 
				
			||||||
    bool reload();
 | 
					    bool reload();
 | 
				
			||||||
    void addEntry(const std::string & absolute_filename, const std::string &filename, float duration, uint64_t &bpos,
 | 
					    void addEntry(const std::string & absolute_filename, const std::string &filename, float duration, uint64_t &bpos,
 | 
				
			||||||
                  const std::string &key, const std::string &keyIV);
 | 
					                  const std::string &key, const std::string &keyIV, uint64_t startByte, uint64_t lenByte);
 | 
				
			||||||
    bool isSupportedFile(const std::string filename);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string uri; // link to the current playlistfile
 | 
					    std::string uri; // link to the current playlistfile
 | 
				
			||||||
    HTTP::URL root;
 | 
					    HTTP::URL root;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue