Several fixes:

- Fixed bug in stream health function causing loop if track not active
- Fixed DTSC pulls ignoring data before the live point
- Improved async buffers (deque mode) to spread the tracks more fairly
- DTSC pull now implements "ping" and "error" commands
- DTSC pulls report suspicious keyframe intervals to the origin and ask for confirmation
- DTSC output now accepts these reports and disconnects if there is no match in keyframe intervals
- Outputs in async mode now keep the seek point in all tracks when reselecting
- Outputs in async mode now default to a starting position in each track that is at a keyframe roughly halfway in the buffer
- Outputs in async mode now ignore playback rate (always fastest possible)
- Removed code duplication in prepareNext function
- Reordered the prepareNext function somewhat to be easier to follow for humans
- DTSC output no longer overrides initialSeek function, now uses default implementation
- Sanitycheck output now supports both sync and async modes, supports printing multiple timestamps for multiple tracks
This commit is contained in:
Thulinma 2022-01-06 12:52:47 +01:00
parent b89875ea37
commit f560b88bfe
9 changed files with 257 additions and 222 deletions

View file

@ -115,47 +115,6 @@ namespace Mist{
std::string OutDTSC::getStatsName(){return (pushing ? "INPUT:DTSC" : "OUTPUT:DTSC");}
/// Seeks to the first sync'ed keyframe of the main track.
/// Aborts if there is no main track or it has no keyframes.
void OutDTSC::initialSeek(){
uint64_t seekPos = 0;
if (M.getLive()){
size_t mainTrack = getMainSelectedTrack();
// cancel if there are no keys in the main track
if (mainTrack == INVALID_TRACK_ID){return;}
DTSC::Keys keys(M.keys(mainTrack));
if (!keys.getValidCount()){return;}
// seek to the oldest keyframe
std::set<size_t> validTracks = M.getValidTracks();
for (size_t i = keys.getFirstValid(); i < keys.getEndValid(); ++i){
seekPos = keys.getTime(i);
bool good = true;
// check if all tracks have data for this point in time
for (std::map<size_t, Comms::Users>::iterator ti = userSelect.begin(); ti != userSelect.end(); ++ti){
if (mainTrack == ti->first){continue;}// skip self
if (!validTracks.count(ti->first)){
HIGH_MSG("Skipping track %zu, not in tracks", ti->first);
continue;
}// ignore missing tracks
if (M.getLastms(ti->first) == M.getFirstms(ti->first)){
HIGH_MSG("Skipping track %zu, last equals first", ti->first);
continue;
}// ignore point-tracks
if (M.getFirstms(ti->first) > seekPos){
good = false;
break;
}
HIGH_MSG("Track %zu is good", ti->first);
}
// if yes, seek here
if (good){break;}
}
}
MEDIUM_MSG("Initial seek to %" PRIu64 "ms", seekPos);
seek(seekPos);
}
void OutDTSC::sendNext(){
// If selectable tracks changed, set sentHeader to false to force it to send init data
static uint64_t lastMeta = 0;
@ -167,7 +126,7 @@ namespace Mist{
return;
}
}
DTSC::Packet p(thisPacket, thisIdx + 1);
DTSC::Packet p(thisPacket, thisIdx+1);
myConn.SendNow(p.getData(), p.getDataLen());
lastActive = Util::epoch();
}
@ -201,6 +160,10 @@ namespace Mist{
std::string dataPacket = myConn.Received().remove(rSize);
DTSC::Scan dScan((char *)dataPacket.data(), rSize);
HIGH_MSG("Received DTCM: %s", dScan.asJSON().toString().c_str());
if (dScan.getMember("cmd").asString() == "ok"){
INFO_MSG("Remote OK: %s", dScan.getMember("msg").asString().c_str());
continue;
}
if (dScan.getMember("cmd").asString() == "push"){
handlePush(dScan);
continue;
@ -230,6 +193,29 @@ namespace Mist{
sendOk("Internal state reset");
continue;
}
if (dScan.getMember("cmd").asString() == "check_key_duration"){
size_t idx = dScan.getMember("id").asInt() - 1;
size_t dur = dScan.getMember("duration").asInt();
if (!M.trackValid(idx)){
ERROR_MSG("Cannot check key duration %zu for track %zu: not valid", dur, idx);
return;
}
uint32_t longest_key = 0;
DTSC::Keys Mkeys(M.keys(idx));
uint32_t firstKey = Mkeys.getFirstValid();
uint32_t endKey = Mkeys.getEndValid();
for (uint32_t k = firstKey; k+1 < endKey; k++){
uint64_t kDur = Mkeys.getDuration(k);
if (kDur > longest_key){longest_key = kDur;}
}
if (dur > longest_key*1.2){
onFail("Key duration mismatch; disconnecting "+myConn.getHost()+" to recover ("+JSON::Value(longest_key).asString()+" -> "+JSON::Value(dur).asString()+")", true);
return;
}else{
sendOk("Key duration matches upstream");
}
continue;
}
WARN_MSG("Unhandled DTCM command: '%s'", dScan.getMember("cmd").asString().c_str());
}else if (myConn.Received().copy(4) == "DTSC"){
// Header packet