From 49ee109b50b7ea7351384810f9cdb431f36af08d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 24 Aug 2020 23:16:53 +0200 Subject: [PATCH] Added maxKeepAway option for live streams, renamed minkeepaway/keepaway to "jitter" externally, added global jitter and bframe checks in all JSON-like metadata outputs --- lib/dtsc.cpp | 48 +++++++++++++++++++++-------- lib/dtsc.h | 4 +++ src/input/input_buffer.cpp | 32 +++++++++++++++++++ src/input/input_buffer.h | 1 + src/input/input_ts.cpp | 19 +++++++----- src/output/output.cpp | 7 +++++ src/output/output_http_internal.cpp | 2 -- 7 files changed, 90 insertions(+), 23 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 3d25cefe..86cb700c 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -1132,6 +1132,7 @@ namespace DTSC{ stream.addField("live", RAX_UINT); stream.addField("tracks", RAX_NESTED, META_TRACK_OFFSET + (trackCount * META_TRACK_RECORDSIZE)); stream.addField("source", RAX_STRING, 512); + stream.addField("maxkeepaway", RAX_16UINT); stream.addField("bufferwindow", RAX_64UINT); stream.addField("bootmsoffset", RAX_64INT); stream.addField("minfragduration", RAX_64UINT); @@ -1163,6 +1164,7 @@ namespace DTSC{ streamVodField = stream.getFieldData("vod"); streamLiveField = stream.getFieldData("live"); streamSourceField = stream.getFieldData("source"); + streamMaxKeepAwayField = stream.getFieldData("maxkeepaway"); streamBufferWindowField = stream.getFieldData("bufferwindow"); streamBootMsOffsetField = stream.getFieldData("bootmsoffset"); streamMinimumFragmentDurationField = stream.getFieldData("minfragduration"); @@ -1853,11 +1855,19 @@ namespace DTSC{ } 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{ - 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){ @@ -2602,19 +2612,11 @@ namespace DTSC{ /// Converts the current Meta object to JSON format void Meta::toJSON(JSON::Value &res, bool skipDynamic, bool tracksOnly) const{ 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){ WARN_MSG("Skipping dynamic stuff even though skipDynamic is set to false"); } - + uint64_t jitter = 0; + bool bframes = false; std::set validTracks = getValidTracks(); for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ JSON::Value &trackJSON = res["tracks"][getTrackIdentifier(*it, true)]; @@ -2631,7 +2633,11 @@ namespace DTSC{ trackJSON["maxbps"] = getMaxBps(*it); if (!skipDynamic && getLive()){ 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);} @@ -2643,12 +2649,28 @@ namespace DTSC{ trackJSON["width"] = getWidth(*it); trackJSON["height"] = getHeight(*it); trackJSON["fpks"] = getFpks(*it); + if (hasBFrames(*it)){ + bframes = true; + trackJSON["bframes"] = 1; + } } } if (tracksOnly){ JSON::Value v = res["tracks"]; 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 diff --git a/lib/dtsc.h b/lib/dtsc.h index 2e134dfe..f04bbf68 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -377,6 +377,9 @@ namespace DTSC{ void setMinKeepAway(size_t trackIdx, uint64_t minKeepAway); uint64_t getMinKeepAway(size_t trackIdx) const; + void setMaxKeepAway(uint64_t maxKeepAway); + uint64_t getMaxKeepAway() const; + /*LTS-START*/ void setSourceTrack(size_t trackIdx, size_t sourceTrack); uint64_t getSourceTrack(size_t trackIdx) const; @@ -486,6 +489,7 @@ namespace DTSC{ Util::RelAccXFieldData streamVodField; Util::RelAccXFieldData streamLiveField; Util::RelAccXFieldData streamSourceField; + Util::RelAccXFieldData streamMaxKeepAwayField; Util::RelAccXFieldData streamBufferWindowField; Util::RelAccXFieldData streamBootMsOffsetField; Util::RelAccXFieldData streamMinimumFragmentDurationField; diff --git a/src/input/input_buffer.cpp b/src/input/input_buffer.cpp index cf006e96..1e889781 100644 --- a/src/input/input_buffer.cpp +++ b/src/input/input_buffer.cpp @@ -84,6 +84,20 @@ namespace Mist{ capa["optional"]["resume"]["default"] = 0; 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["long"] = "segment-size"; option["short"] = "S"; @@ -163,9 +177,13 @@ namespace Mist{ bool hasAAC = false; std::stringstream issues; std::set validTracks = M.getValidTracks(); + uint64_t jitter = 0; for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ size_t i = *it; 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 type = M.getType(i); track["kbits"] = M.getBps(i) * 8 / 1024; @@ -218,6 +236,13 @@ namespace Mist{ 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){issues << "HLS no audio!";} if (!hasH264){issues << "HLS no video!";} @@ -600,6 +625,13 @@ namespace Mist{ 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*/ return true; } diff --git a/src/input/input_buffer.h b/src/input/input_buffer.h index 69ef877e..2a78294c 100644 --- a/src/input/input_buffer.h +++ b/src/input/input_buffer.h @@ -20,6 +20,7 @@ namespace Mist{ bool hasPush;//Is a push currently being received? bool everHadPush;//Was there ever a push received? bool resumeMode; + uint64_t maxKeepAway; IPC::semaphore *liveMeta; protected: diff --git a/src/input/input_ts.cpp b/src/input/input_ts.cpp index 9def0470..266d4033 100644 --- a/src/input/input_ts.cpp +++ b/src/input/input_ts.cpp @@ -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"]["help"] = "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 " "well as the minimum duration needed for stable playback."; - capa["optional"]["DVR"]["option"] = "--buffer"; capa["optional"]["DVR"]["type"] = "uint"; 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(){ diff --git a/src/output/output.cpp b/src/output/output.cpp index 6f778475..6798d061 100644 --- a/src/output/output.cpp +++ b/src/output/output.cpp @@ -953,6 +953,13 @@ namespace Mist{ if (ti->first == INVALID_TRACK_ID){continue;} 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; } diff --git a/src/output/output_http_internal.cpp b/src/output/output_http_internal.cpp index 867435bb..43768e05 100644 --- a/src/output/output_http_internal.cpp +++ b/src/output/output_http_internal.cpp @@ -485,10 +485,8 @@ namespace Mist{ if (it->isMember("lang")){ (*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"]["bframes"] = (M.hasBFrames() ? 1 : 0); // Get sources/protocols information Util::DTSCShmReader rCapa(SHM_CAPA);