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
Reference in a new issue