Improvements and fixes to track wait code

This commit is contained in:
Thulinma 2023-08-07 11:35:17 +02:00
parent 797534b2e2
commit 110a539151
3 changed files with 74 additions and 38 deletions

View file

@ -118,6 +118,7 @@ namespace Mist{
Util::Config::binaryType = Util::OUTPUT; Util::Config::binaryType = Util::OUTPUT;
lastRecv = Util::bootSecs(); lastRecv = Util::bootSecs();
outputStartMs = Util::bootMS();
if (myConn){ if (myConn){
setBlocking(true); setBlocking(true);
//Make sure that if the socket is a non-stdio socket, we close it when forking //Make sure that if the socket is a non-stdio socket, we close it when forking
@ -233,33 +234,59 @@ namespace Mist{
bool Output::isReadyForPlay(){ bool Output::isReadyForPlay(){
// If a protocol does not support any codecs, we assume you know what you're doing // If a protocol does not support any codecs, we assume you know what you're doing
if (!capa.isMember("codecs") || !capa["codecs"].size() || !capa["codecs"].isArray() || !capa["codecs"][0u].size()){return true;} if (!capa.isMember("codecs") || !capa["codecs"].size() || !capa["codecs"].isArray() || !capa["codecs"][0u].size()){return true;}
if (!isInitialized){return false;}
meta.reloadReplacedPagesIfNeeded(); // Defaults for the below values are configured here, and may be overridden with query parameters:
if (getSupportedTracks().size()){ size_t minTracks = 2; // Supported tracks to attempt to wait for
size_t minTracks = 2; size_t minMs = 500; // Minimum time that must be buffered for a track to count as available
size_t minMs = 5000; size_t minKeys = 2; // Minimum keyframes that must be buffered for a track to count as available
if (targetParams.count("waittrackcount")){ size_t maxWait = 5000; // Maximum time to wait for tracks
minTracks = JSON::Value(targetParams["waittrackcount"]).asInt(); size_t maxMs = 20000; // If this much time is buffered in any track, don't wait on anything else anymore
minMs = 120000;
} if (targetParams.count("waittrackcount")){
if (targetParams.count("maxwaittrackms")){ minTracks = JSON::Value(targetParams["waittrackcount"]).asInt();
minMs = JSON::Value(targetParams["maxwaittrackms"]).asInt(); // Non-default wait times get a 2 minute default timeout instead of the usually 5 seconds default.
} // (May still be overridden)
if (!userSelect.size()){selectDefaultTracks();} maxWait = 120000;
size_t mainTrack = getMainSelectedTrack();
if (mainTrack != INVALID_TRACK_ID){
DTSC::Keys keys(M.getKeys(mainTrack));
if (keys.getValidCount() >= minTracks || M.getNowms(mainTrack) - M.getFirstms(mainTrack) > minMs){
return true;
}
HIGH_MSG("NOT READY YET (%zu tracks, main track: %zu, with %zu keys)",
M.getValidTracks().size(), getMainSelectedTrack(), keys.getValidCount());
}else{
HIGH_MSG("NOT READY YET (%zu tracks)", getSupportedTracks().size());
}
}else{
HIGH_MSG("NOT READY (%zu tracks)", getSupportedTracks().size());
} }
if (targetParams.count("mintrackms")){
minMs = JSON::Value(targetParams["mintrackms"]).asInt();
}
if (targetParams.count("maxtrackms")){
maxMs = JSON::Value(targetParams["maxtrackms"]).asInt();
}
if (targetParams.count("mintrackkeys")){
minKeys = JSON::Value(targetParams["mintrackkeys"]).asInt();
}
if (targetParams.count("maxwaittrackms")){
maxWait = JSON::Value(targetParams["maxwaittrackms"]).asInt();
}
if (outputStartMs + maxWait < Util::bootMS()){
HIGH_MSG("isReadyForPlay timed out waiting for tracks (%" PRId64 " > %zu); continuing with what we have", Util::bootMS()-outputStartMs, maxWait);
return true;
}
//This logic is copied from selectDefaultTracks, and should be kept in sync with it
if (!isInitialized){initialize();}
meta.reloadReplacedPagesIfNeeded();
bool autoSeek = buffer.size();
uint64_t seekTarget = buffer.getSyncMode()?currentTime():0;
std::set<size_t> trks = Util::wouldSelect(M, targetParams, capa, UA, autoSeek ? seekTarget : 0);
size_t trkCount = 0;
for (std::set<size_t>::iterator it = trks.begin(); it != trks.end(); ++it){
DTSC::Keys keys(M.keys(*it));
if (keys.getValidCount() >= minKeys || M.getLastms(*it) - M.getFirstms(*it) > minMs){
++trkCount;
if (trkCount >= minTracks){return true;}
}
if (M.getLastms(*it) - M.getFirstms(*it) > maxMs){
HIGH_MSG("isReadyForPlay skipping wait because track %zu has %" PRId64 "ms buffered", *it, M.getLastms(*it) - M.getFirstms(*it));
return true;
}
}
HIGH_MSG("isReadyForPlay waiting for tracks: %zu/%zu/%zu tracks, maxWait %zu, minMs %zu, minKeys %zu", trkCount, trks.size(), minTracks, maxWait, minMs, minKeys);
return false; return false;
} }

View file

@ -106,6 +106,7 @@ namespace Mist{
uint32_t seekCount; uint32_t seekCount;
bool firstData; bool firstData;
uint64_t lastPushUpdate; uint64_t lastPushUpdate;
uint64_t outputStartMs; ///< bootMS() at time of output start (unrelated to media start)
bool newUA; bool newUA;
protected: // these are to be messed with by child classes protected: // these are to be messed with by child classes

View file

@ -379,15 +379,23 @@ namespace Mist{
/*LTS-END*/ /*LTS-END*/
if (H.hasHeader("User-Agent")){UA = H.GetHeader("User-Agent");} if (H.hasHeader("User-Agent")){UA = H.GetHeader("User-Agent");}
if (H.GetVar("audio") != ""){targetParams["audio"] = H.GetVar("audio");} #define HTTP_CONVERT(var) if (H.GetVar(var) != ""){targetParams[var] = H.GetVar(var);}
if (H.GetVar("video") != ""){targetParams["video"] = H.GetVar("video");}
if (H.GetVar("meta") != ""){targetParams["meta"] = H.GetVar("meta");} HTTP_CONVERT("audio");
if (H.GetVar("subtitle") != ""){targetParams["subtitle"] = H.GetVar("subtitle");} HTTP_CONVERT("video");
if (H.GetVar("start") != ""){targetParams["start"] = H.GetVar("start");} HTTP_CONVERT("meta");
if (H.GetVar("stop") != ""){targetParams["stop"] = H.GetVar("stop");} HTTP_CONVERT("subtitle");
if (H.GetVar("startunix") != ""){targetParams["startunix"] = H.GetVar("startunix");} HTTP_CONVERT("start");
if (H.GetVar("stopunix") != ""){targetParams["stopunix"] = H.GetVar("stopunix");} HTTP_CONVERT("stop");
if (H.GetVar("duration") != ""){targetParams["duration"] = H.GetVar("duration");} HTTP_CONVERT("startunix");
HTTP_CONVERT("stopunix");
HTTP_CONVERT("duration");
HTTP_CONVERT("waittrackcount");
HTTP_CONVERT("mintrackms");
HTTP_CONVERT("maxtrackms");
HTTP_CONVERT("mintrackkeys");
HTTP_CONVERT("maxwaittrackms");
// allow setting of max lead time through buffer variable. // allow setting of max lead time through buffer variable.
// max lead time is set in MS, but the variable is in integer seconds for simplicity. // max lead time is set in MS, but the variable is in integer seconds for simplicity.
if (H.GetVar("buffer") != ""){ if (H.GetVar("buffer") != ""){
@ -461,7 +469,7 @@ namespace Mist{
//Parse JSON and check command type //Parse JSON and check command type
JSON::Value command = JSON::fromString(webSock->data, webSock->data.size()); JSON::Value command = JSON::fromString(webSock->data, webSock->data.size());
if (!command || !command.isMember("type")){return false;} if (!command || !command.isMember("type")){return false;}
//Seek command, for changing playback position //Seek command, for changing playback position
if (command["type"] == "seek") { if (command["type"] == "seek") {
handleWebsocketSeek(command); handleWebsocketSeek(command);
@ -566,7 +574,7 @@ namespace Mist{
parseData = true; parseData = true;
selectDefaultTracks(); selectDefaultTracks();
} }
if (target_rate == 0.0){ if (target_rate == 0.0){
r["data"]["play_rate_prev"] = "auto"; r["data"]["play_rate_prev"] = "auto";
}else{ }else{
@ -841,7 +849,7 @@ namespace Mist{
if (Comms::tknMode & 0x08){ if (Comms::tknMode & 0x08){
std::stringstream cookieHeader; std::stringstream cookieHeader;
cookieHeader << "tkn=" << tkn << "; Max-Age=" << SESS_TIMEOUT; cookieHeader << "tkn=" << tkn << "; Max-Age=" << SESS_TIMEOUT;
H.SetHeader("Set-Cookie", cookieHeader.str()); H.SetHeader("Set-Cookie", cookieHeader.str());
} }
} }
//Set attachment header to force download, if applicable //Set attachment header to force download, if applicable