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:
Thulinma 2020-08-24 23:16:53 +02:00
parent 7b523d53c7
commit 49ee109b50
7 changed files with 90 additions and 23 deletions

View file

@ -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

View file

@ -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;

View file

@ -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;
} }

View file

@ -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:

View file

@ -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(){

View file

@ -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;
} }

View file

@ -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);