Added maxKeepAway option for live streams, renamed minkeepaway/keepaway to "jitter" externally, added global jitter and bframe checks in all JSON-like metadata outputs
This commit is contained in:
		
							parent
							
								
									7b523d53c7
								
							
						
					
					
						commit
						49ee109b50
					
				
					 7 changed files with 90 additions and 23 deletions
				
			
		
							
								
								
									
										48
									
								
								lib/dtsc.cpp
									
										
									
									
									
								
							
							
						
						
									
										48
									
								
								lib/dtsc.cpp
									
										
									
									
									
								
							|  | @ -1132,6 +1132,7 @@ namespace DTSC{ | ||||||
|       stream.addField("live", RAX_UINT); |       stream.addField("live", RAX_UINT); | ||||||
|       stream.addField("tracks", RAX_NESTED, META_TRACK_OFFSET + (trackCount * META_TRACK_RECORDSIZE)); |       stream.addField("tracks", RAX_NESTED, META_TRACK_OFFSET + (trackCount * META_TRACK_RECORDSIZE)); | ||||||
|       stream.addField("source", RAX_STRING, 512); |       stream.addField("source", RAX_STRING, 512); | ||||||
|  |       stream.addField("maxkeepaway", RAX_16UINT); | ||||||
|       stream.addField("bufferwindow", RAX_64UINT); |       stream.addField("bufferwindow", RAX_64UINT); | ||||||
|       stream.addField("bootmsoffset", RAX_64INT); |       stream.addField("bootmsoffset", RAX_64INT); | ||||||
|       stream.addField("minfragduration", RAX_64UINT); |       stream.addField("minfragduration", RAX_64UINT); | ||||||
|  | @ -1163,6 +1164,7 @@ namespace DTSC{ | ||||||
|     streamVodField = stream.getFieldData("vod"); |     streamVodField = stream.getFieldData("vod"); | ||||||
|     streamLiveField = stream.getFieldData("live"); |     streamLiveField = stream.getFieldData("live"); | ||||||
|     streamSourceField = stream.getFieldData("source"); |     streamSourceField = stream.getFieldData("source"); | ||||||
|  |     streamMaxKeepAwayField = stream.getFieldData("maxkeepaway"); | ||||||
|     streamBufferWindowField = stream.getFieldData("bufferwindow"); |     streamBufferWindowField = stream.getFieldData("bufferwindow"); | ||||||
|     streamBootMsOffsetField = stream.getFieldData("bootmsoffset"); |     streamBootMsOffsetField = stream.getFieldData("bootmsoffset"); | ||||||
|     streamMinimumFragmentDurationField = stream.getFieldData("minfragduration"); |     streamMinimumFragmentDurationField = stream.getFieldData("minfragduration"); | ||||||
|  | @ -1853,11 +1855,19 @@ namespace DTSC{ | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void Meta::setMinKeepAway(size_t trackIdx, uint64_t minKeepAway){ |   void Meta::setMinKeepAway(size_t trackIdx, uint64_t minKeepAway){ | ||||||
|     trackList.setInt(trackMinKeepAwayField, minKeepAway); |     trackList.setInt(trackMinKeepAwayField, minKeepAway, trackIdx); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   uint64_t Meta::getMinKeepAway(size_t trackIdx) const{ |   uint64_t Meta::getMinKeepAway(size_t trackIdx) const{ | ||||||
|     return trackList.getInt(trackMinKeepAwayField); |     return trackList.getInt(trackMinKeepAwayField, trackIdx); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void Meta::setMaxKeepAway(uint64_t maxKeepAway){ | ||||||
|  |     stream.setInt(streamMaxKeepAwayField, maxKeepAway); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   uint64_t Meta::getMaxKeepAway() const{ | ||||||
|  |     return stream.getInt(streamMaxKeepAwayField); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void Meta::setEncryption(size_t trackIdx, const std::string &encryption){ |   void Meta::setEncryption(size_t trackIdx, const std::string &encryption){ | ||||||
|  | @ -2602,19 +2612,11 @@ namespace DTSC{ | ||||||
|   /// Converts the current Meta object to JSON format
 |   /// Converts the current Meta object to JSON format
 | ||||||
|   void Meta::toJSON(JSON::Value &res, bool skipDynamic, bool tracksOnly) const{ |   void Meta::toJSON(JSON::Value &res, bool skipDynamic, bool tracksOnly) const{ | ||||||
|     res.null(); |     res.null(); | ||||||
|     if (getLive()){ |  | ||||||
|       res["live"] = 1u; |  | ||||||
|     }else{ |  | ||||||
|       res["vod"] = 1u; |  | ||||||
|     } |  | ||||||
|     res["version"] = DTSH_VERSION; |  | ||||||
|     if (getBufferWindow()){res["buffer_window"] = getBufferWindow();} |  | ||||||
|     if (getSource() != ""){res["source"] = getSource();} |  | ||||||
| 
 |  | ||||||
|     if (!skipDynamic){ |     if (!skipDynamic){ | ||||||
|       WARN_MSG("Skipping dynamic stuff even though skipDynamic is set to false"); |       WARN_MSG("Skipping dynamic stuff even though skipDynamic is set to false"); | ||||||
|     } |     } | ||||||
| 
 |     uint64_t jitter = 0; | ||||||
|  |     bool bframes = false; | ||||||
|     std::set<size_t> validTracks = getValidTracks(); |     std::set<size_t> validTracks = getValidTracks(); | ||||||
|     for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){ |     for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){ | ||||||
|       JSON::Value &trackJSON = res["tracks"][getTrackIdentifier(*it, true)]; |       JSON::Value &trackJSON = res["tracks"][getTrackIdentifier(*it, true)]; | ||||||
|  | @ -2631,7 +2633,11 @@ namespace DTSC{ | ||||||
|       trackJSON["maxbps"] = getMaxBps(*it); |       trackJSON["maxbps"] = getMaxBps(*it); | ||||||
|       if (!skipDynamic && getLive()){ |       if (!skipDynamic && getLive()){ | ||||||
|         if (getMissedFragments(*it)){trackJSON["missed_frags"] = getMissedFragments(*it);} |         if (getMissedFragments(*it)){trackJSON["missed_frags"] = getMissedFragments(*it);} | ||||||
|         if (getMinKeepAway(*it)){trackJSON["keepaway"] = getMinKeepAway(*it);} |       } | ||||||
|  |       uint64_t trkJitter = getMinKeepAway(*it); | ||||||
|  |       if (trkJitter){ | ||||||
|  |         trackJSON["jitter"] = trkJitter; | ||||||
|  |         if (trkJitter > jitter){jitter = trkJitter;} | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (getLang(*it) != "" && getLang(*it) != "und"){trackJSON["lang"] = getLang(*it);} |       if (getLang(*it) != "" && getLang(*it) != "und"){trackJSON["lang"] = getLang(*it);} | ||||||
|  | @ -2643,12 +2649,28 @@ namespace DTSC{ | ||||||
|         trackJSON["width"] = getWidth(*it); |         trackJSON["width"] = getWidth(*it); | ||||||
|         trackJSON["height"] = getHeight(*it); |         trackJSON["height"] = getHeight(*it); | ||||||
|         trackJSON["fpks"] = getFpks(*it); |         trackJSON["fpks"] = getFpks(*it); | ||||||
|  |         if (hasBFrames(*it)){ | ||||||
|  |           bframes = true; | ||||||
|  |           trackJSON["bframes"] = 1; | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     if (tracksOnly){ |     if (tracksOnly){ | ||||||
|       JSON::Value v = res["tracks"]; |       JSON::Value v = res["tracks"]; | ||||||
|       res = v; |       res = v; | ||||||
|  |       return; | ||||||
|     } |     } | ||||||
|  |     if (jitter){res["jitter"] = jitter;} | ||||||
|  |     res["bframes"] = bframes?1:0; | ||||||
|  |     if (getMaxKeepAway()){res["maxkeepaway"] = getMaxKeepAway();} | ||||||
|  |     if (getLive()){ | ||||||
|  |       res["live"] = 1u; | ||||||
|  |     }else{ | ||||||
|  |       res["vod"] = 1u; | ||||||
|  |     } | ||||||
|  |     res["version"] = DTSH_VERSION; | ||||||
|  |     if (getBufferWindow()){res["buffer_window"] = getBufferWindow();} | ||||||
|  |     if (getSource() != ""){res["source"] = getSource();} | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Sends the current Meta object through a socket in DTSH format
 |   /// Sends the current Meta object through a socket in DTSH format
 | ||||||
|  |  | ||||||
|  | @ -377,6 +377,9 @@ namespace DTSC{ | ||||||
|     void setMinKeepAway(size_t trackIdx, uint64_t minKeepAway); |     void setMinKeepAway(size_t trackIdx, uint64_t minKeepAway); | ||||||
|     uint64_t getMinKeepAway(size_t trackIdx) const; |     uint64_t getMinKeepAway(size_t trackIdx) const; | ||||||
| 
 | 
 | ||||||
|  |     void setMaxKeepAway(uint64_t maxKeepAway); | ||||||
|  |     uint64_t getMaxKeepAway() const; | ||||||
|  | 
 | ||||||
|     /*LTS-START*/ |     /*LTS-START*/ | ||||||
|     void setSourceTrack(size_t trackIdx, size_t sourceTrack); |     void setSourceTrack(size_t trackIdx, size_t sourceTrack); | ||||||
|     uint64_t getSourceTrack(size_t trackIdx) const; |     uint64_t getSourceTrack(size_t trackIdx) const; | ||||||
|  | @ -486,6 +489,7 @@ namespace DTSC{ | ||||||
|     Util::RelAccXFieldData streamVodField; |     Util::RelAccXFieldData streamVodField; | ||||||
|     Util::RelAccXFieldData streamLiveField; |     Util::RelAccXFieldData streamLiveField; | ||||||
|     Util::RelAccXFieldData streamSourceField; |     Util::RelAccXFieldData streamSourceField; | ||||||
|  |     Util::RelAccXFieldData streamMaxKeepAwayField; | ||||||
|     Util::RelAccXFieldData streamBufferWindowField; |     Util::RelAccXFieldData streamBufferWindowField; | ||||||
|     Util::RelAccXFieldData streamBootMsOffsetField; |     Util::RelAccXFieldData streamBootMsOffsetField; | ||||||
|     Util::RelAccXFieldData streamMinimumFragmentDurationField; |     Util::RelAccXFieldData streamMinimumFragmentDurationField; | ||||||
|  |  | ||||||
|  | @ -84,6 +84,20 @@ namespace Mist{ | ||||||
|     capa["optional"]["resume"]["default"] = 0; |     capa["optional"]["resume"]["default"] = 0; | ||||||
|     option.null(); |     option.null(); | ||||||
| 
 | 
 | ||||||
|  |     option["arg"] = "integer"; | ||||||
|  |     option["long"] = "maxkeepaway"; | ||||||
|  |     option["short"] = "M"; | ||||||
|  |     option["help"] = "Maximum distance in milliseconds to fall behind the live point for stable playback."; | ||||||
|  |     option["value"].append(45000); | ||||||
|  |     config->addOption("maxkeepaway", option); | ||||||
|  |     capa["optional"]["maxkeepaway"]["name"] = "Maximum live keep-away distance"; | ||||||
|  |     capa["optional"]["maxkeepaway"]["help"] = "Maximum distance in milliseconds to fall behind the live point for stable playback."; | ||||||
|  |     capa["optional"]["maxkeepaway"]["option"] = "--resume"; | ||||||
|  |     capa["optional"]["maxkeepaway"]["type"] = "uint"; | ||||||
|  |     capa["optional"]["maxkeepaway"]["default"] = 45000; | ||||||
|  |     maxKeepAway = 45000; | ||||||
|  |     option.null(); | ||||||
|  | 
 | ||||||
|     option["arg"] = "integer"; |     option["arg"] = "integer"; | ||||||
|     option["long"] = "segment-size"; |     option["long"] = "segment-size"; | ||||||
|     option["short"] = "S"; |     option["short"] = "S"; | ||||||
|  | @ -163,9 +177,13 @@ namespace Mist{ | ||||||
|     bool hasAAC = false; |     bool hasAAC = false; | ||||||
|     std::stringstream issues; |     std::stringstream issues; | ||||||
|     std::set<size_t> validTracks = M.getValidTracks(); |     std::set<size_t> validTracks = M.getValidTracks(); | ||||||
|  |     uint64_t jitter = 0; | ||||||
|     for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){ |     for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){ | ||||||
|       size_t i = *it; |       size_t i = *it; | ||||||
|       JSON::Value &track = details[M.getTrackIdentifier(i)]; |       JSON::Value &track = details[M.getTrackIdentifier(i)]; | ||||||
|  |       uint64_t minKeep = M.getMinKeepAway(*it); | ||||||
|  |       track["jitter"] = minKeep; | ||||||
|  |       if (jitter < minKeep){jitter = minKeep;} | ||||||
|       std::string codec = M.getCodec(i); |       std::string codec = M.getCodec(i); | ||||||
|       std::string type = M.getType(i); |       std::string type = M.getType(i); | ||||||
|       track["kbits"] = M.getBps(i) * 8 / 1024; |       track["kbits"] = M.getBps(i) * 8 / 1024; | ||||||
|  | @ -218,6 +236,13 @@ namespace Mist{ | ||||||
|         track["channels"] = M.getChannels(i); |         track["channels"] = M.getChannels(i); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |     if (jitter > 500){ | ||||||
|  |       issues << "High jitter (" << jitter << "ms)! "; | ||||||
|  |     } | ||||||
|  |     details["jitter"] = jitter; | ||||||
|  |     if (M.getMaxKeepAway()){ | ||||||
|  |       details["maxkeepaway"] = M.getMaxKeepAway(); | ||||||
|  |     } | ||||||
|     if ((hasAAC || hasH264) && validTracks.size() > 1){ |     if ((hasAAC || hasH264) && validTracks.size() > 1){ | ||||||
|       if (!hasAAC){issues << "HLS no audio!";} |       if (!hasAAC){issues << "HLS no audio!";} | ||||||
|       if (!hasH264){issues << "HLS no video!";} |       if (!hasH264){issues << "HLS no video!";} | ||||||
|  | @ -600,6 +625,13 @@ namespace Mist{ | ||||||
|       meta.setMinimumFragmentDuration(segmentSize); |       meta.setMinimumFragmentDuration(segmentSize); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     //Check if segmentsize setting is correct
 | ||||||
|  |     tmpNum = retrieveSetting(streamCfg, "maxkeepaway"); | ||||||
|  |     if (M.getMaxKeepAway() != tmpNum){ | ||||||
|  |       INFO_MSG("Setting maxKeepAway from %" PRIu64 " to new value of %" PRIu64, M.getMaxKeepAway(), tmpNum); | ||||||
|  |       meta.setMaxKeepAway(tmpNum); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /*LTS-END*/ |     /*LTS-END*/ | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ namespace Mist{ | ||||||
|     bool hasPush;//Is a push currently being received?
 |     bool hasPush;//Is a push currently being received?
 | ||||||
|     bool everHadPush;//Was there ever a push received?
 |     bool everHadPush;//Was there ever a push received?
 | ||||||
|     bool resumeMode; |     bool resumeMode; | ||||||
|  |     uint64_t maxKeepAway; | ||||||
|     IPC::semaphore *liveMeta; |     IPC::semaphore *liveMeta; | ||||||
| 
 | 
 | ||||||
|   protected: |   protected: | ||||||
|  |  | ||||||
|  | @ -207,21 +207,24 @@ namespace Mist{ | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     JSON::Value option; |  | ||||||
|     option["arg"] = "integer"; |  | ||||||
|     option["long"] = "buffer"; |  | ||||||
|     option["short"] = "b"; |  | ||||||
|     option["help"] = "DVR buffer time in ms"; |  | ||||||
|     option["value"].append(50000); |  | ||||||
|     config->addOption("bufferTime", option); |  | ||||||
|     capa["optional"]["DVR"]["name"] = "Buffer time (ms)"; |     capa["optional"]["DVR"]["name"] = "Buffer time (ms)"; | ||||||
|     capa["optional"]["DVR"]["help"] = |     capa["optional"]["DVR"]["help"] = | ||||||
|         "The target available buffer time for this live stream, in milliseconds. This is the time " |         "The target available buffer time for this live stream, in milliseconds. This is the time " | ||||||
|         "available to seek around in, and will automatically be extended to fit whole keyframes as " |         "available to seek around in, and will automatically be extended to fit whole keyframes as " | ||||||
|         "well as the minimum duration needed for stable playback."; |         "well as the minimum duration needed for stable playback."; | ||||||
|     capa["optional"]["DVR"]["option"] = "--buffer"; |  | ||||||
|     capa["optional"]["DVR"]["type"] = "uint"; |     capa["optional"]["DVR"]["type"] = "uint"; | ||||||
|     capa["optional"]["DVR"]["default"] = 50000; |     capa["optional"]["DVR"]["default"] = 50000; | ||||||
|  | 
 | ||||||
|  |     capa["optional"]["maxkeepaway"]["name"] = "Maximum live keep-away distance"; | ||||||
|  |     capa["optional"]["maxkeepaway"]["help"] = "Maximum distance in milliseconds to fall behind the live point for stable playback."; | ||||||
|  |     capa["optional"]["maxkeepaway"]["type"] = "uint"; | ||||||
|  |     capa["optional"]["maxkeepaway"]["default"] = 45000; | ||||||
|  | 
 | ||||||
|  |     capa["optional"]["segmentsize"]["name"] = "Segment size (ms)"; | ||||||
|  |     capa["optional"]["segmentsize"]["help"] = "Target time duration in milliseconds for segments."; | ||||||
|  |     capa["optional"]["segmentsize"]["type"] = "uint"; | ||||||
|  |     capa["optional"]["segmentsize"]["default"] = 1900; | ||||||
|  | 
 | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   inputTS::~inputTS(){ |   inputTS::~inputTS(){ | ||||||
|  |  | ||||||
|  | @ -953,6 +953,13 @@ namespace Mist{ | ||||||
|       if (ti->first == INVALID_TRACK_ID){continue;} |       if (ti->first == INVALID_TRACK_ID){continue;} | ||||||
|       if (M.getMinKeepAway(ti->first) > r){r = M.getMinKeepAway(ti->first);} |       if (M.getMinKeepAway(ti->first) > r){r = M.getMinKeepAway(ti->first);} | ||||||
|     } |     } | ||||||
|  |     //Limit the value to the maxKeepAway setting
 | ||||||
|  |     //Also lowers extraKeepAway if needed
 | ||||||
|  |     uint64_t maxKeepAway = M.getMaxKeepAway(); | ||||||
|  |     if (maxKeepAway){ | ||||||
|  |       if (r > maxKeepAway){r = maxKeepAway;} | ||||||
|  |       if (r+extraKeepAway > maxKeepAway){extraKeepAway = maxKeepAway - r;} | ||||||
|  |     } | ||||||
|     return r; |     return r; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -485,10 +485,8 @@ namespace Mist{ | ||||||
|       if (it->isMember("lang")){ |       if (it->isMember("lang")){ | ||||||
|         (*it)["language"] = Encodings::ISO639::decode((*it)["lang"].asStringRef()); |         (*it)["language"] = Encodings::ISO639::decode((*it)["lang"].asStringRef()); | ||||||
|       } |       } | ||||||
|       if (M.hasBFrames((*it)["idx"].asInt())){(*it)["bframes"] = 1;} |  | ||||||
|     } |     } | ||||||
|     json_resp["meta"].removeMember("source"); |     json_resp["meta"].removeMember("source"); | ||||||
|     json_resp["meta"]["bframes"] = (M.hasBFrames() ? 1 : 0); |  | ||||||
| 
 | 
 | ||||||
|     // Get sources/protocols information
 |     // Get sources/protocols information
 | ||||||
|     Util::DTSCShmReader rCapa(SHM_CAPA); |     Util::DTSCShmReader rCapa(SHM_CAPA); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Thulinma
						Thulinma