Fixes to MistOutSanityCheck utility
This commit is contained in:
parent
431d03c733
commit
4d3f86aec1
3 changed files with 126 additions and 95 deletions
|
@ -3430,7 +3430,6 @@ namespace DTSC{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Keys::applyLimiter(uint64_t _min, uint64_t _max, DTSC::Parts _p){
|
void Keys::applyLimiter(uint64_t _min, uint64_t _max, DTSC::Parts _p){
|
||||||
|
|
||||||
// Determine first and last key available within the limits
|
// Determine first and last key available within the limits
|
||||||
// Note: limMax replaces getEndValid(), and is thus one _past_ the end key index!
|
// Note: limMax replaces getEndValid(), and is thus one _past_ the end key index!
|
||||||
limMin = getFirstValid();
|
limMin = getFirstValid();
|
||||||
|
|
|
@ -14,55 +14,67 @@ namespace Mist{
|
||||||
//}
|
//}
|
||||||
parseData = true;
|
parseData = true;
|
||||||
wantRequest = false;
|
wantRequest = false;
|
||||||
if (config->getBool("sync")){
|
syncMode = true;
|
||||||
setSyncMode(true);
|
if (config->getBool("async")){
|
||||||
}else{
|
|
||||||
setSyncMode(false);
|
setSyncMode(false);
|
||||||
|
syncMode = false;
|
||||||
|
}else{
|
||||||
|
setSyncMode(true);
|
||||||
}
|
}
|
||||||
initialize();
|
}
|
||||||
initialSeek();
|
|
||||||
|
void OutSanityCheck::sendHeader(){
|
||||||
|
Output::sendHeader();
|
||||||
sortSet.clear();
|
sortSet.clear();
|
||||||
if (!M.getLive()){
|
|
||||||
realTime = 0;
|
realTime = 0;
|
||||||
|
if (syncMode){
|
||||||
for (std::map<size_t, Comms::Users>::const_iterator it = userSelect.begin();
|
for (std::map<size_t, Comms::Users>::const_iterator it = userSelect.begin();
|
||||||
it != userSelect.end(); it++){
|
it != userSelect.end(); it++){
|
||||||
keyPart temp;
|
trkTime[it->first] = 0;
|
||||||
temp.trackID = it->first;
|
Util::sortedPageInfo temp;
|
||||||
temp.time = M.getFirstms(it->first); // timeplace of frame
|
temp.tid = it->first;
|
||||||
DTSC::Parts parts(M.parts(it->first));
|
DTSC::Keys keys = M.getKeys(it->first);
|
||||||
temp.endTime = M.getFirstms(it->first) + parts.getDuration(parts.getFirstValid());
|
size_t firstKey = keys.getFirstValid();
|
||||||
temp.size = parts.getSize(parts.getFirstValid()); // bytesize of frame (alle parts all together)
|
temp.time = keys.getTime(firstKey);
|
||||||
temp.index = 0;
|
temp.partIndex = keys.getFirstPart(firstKey);
|
||||||
|
INFO_MSG("Enabling part ordering checker: expecting track %zu, part %zu at time %" PRIu64, temp.tid, temp.partIndex, temp.time);
|
||||||
sortSet.insert(temp);
|
sortSet.insert(temp);
|
||||||
}
|
}
|
||||||
if (config->getInteger("seek")){
|
if (config->getInteger("seek")){
|
||||||
uint64_t seekPoint = config->getInteger("seek");
|
uint64_t seekPoint = config->getInteger("seek");
|
||||||
|
|
||||||
while (!sortSet.empty() && sortSet.begin()->time < seekPoint){
|
while (sortSet.size() && sortSet.begin()->time < seekPoint){
|
||||||
keyPart temp = *sortSet.begin();
|
Util::sortedPageInfo temp = *sortSet.begin();
|
||||||
temp.index++;
|
temp.partIndex++;
|
||||||
DTSC::Parts parts(M.parts(temp.trackID));
|
DTSC::Parts parts(M.parts(temp.tid));
|
||||||
if (temp.index < parts.getEndValid()){// only insert when there are parts left
|
if (temp.partIndex < parts.getEndValid()){// only insert when there are parts left
|
||||||
temp.time = temp.endTime; // timeplace of frame
|
temp.time += parts.getDuration(temp.partIndex - 1); // timeplace of frame
|
||||||
temp.endTime = temp.time + parts.getDuration(temp.index);
|
sortSet.replaceFirst(temp);
|
||||||
temp.size = parts.getSize(temp.index);
|
}else{
|
||||||
; // bytesize of frame
|
sortSet.dropTrack(temp.tid);
|
||||||
sortSet.insert(temp);
|
|
||||||
}
|
}
|
||||||
// remove highest keyPart
|
|
||||||
sortSet.erase(sortSet.begin());
|
|
||||||
}
|
}
|
||||||
seek(seekPoint);
|
seek(seekPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutSanityCheck::init(Util::Config *cfg){
|
void OutSanityCheck::init(Util::Config *cfg){
|
||||||
Output::init(cfg);
|
Output::init(cfg);
|
||||||
capa["name"] = "SanityCheck";
|
capa["name"] = "SanityCheck";
|
||||||
|
capa["friendly"] = "Development tool: Sanity checker";
|
||||||
capa["desc"] = "Does sanity check on a stream";
|
capa["desc"] = "Does sanity check on a stream";
|
||||||
capa["codecs"][0u][0u].append("+*");
|
capa["codecs"][0u][0u].append("+*");
|
||||||
|
|
||||||
|
JSON::Value opt;
|
||||||
|
opt["arg"] = "string";
|
||||||
|
opt["default"] = "";
|
||||||
|
opt["arg_num"] = 1;
|
||||||
|
opt["help"] = "Ignored, only exists to handle targetParams";
|
||||||
|
cfg->addOption("target", opt);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cfg->addOption("streamname", JSON::fromString("{\"arg\":\"string\",\"short\":\"s\",\"long\":"
|
cfg->addOption("streamname", JSON::fromString("{\"arg\":\"string\",\"short\":\"s\",\"long\":"
|
||||||
"\"stream\",\"help\":\"The name of the stream "
|
"\"stream\",\"help\":\"The name of the stream "
|
||||||
"that this connector will transmit.\"}"));
|
"that this connector will transmit.\"}"));
|
||||||
|
@ -70,8 +82,8 @@ namespace Mist{
|
||||||
"seek", JSON::fromString("{\"arg\":\"string\",\"short\":\"k\",\"long\":\"seek\",\"help\":"
|
"seek", JSON::fromString("{\"arg\":\"string\",\"short\":\"k\",\"long\":\"seek\",\"help\":"
|
||||||
"\"Time in ms to check from - by default start of stream\"}"));
|
"\"Time in ms to check from - by default start of stream\"}"));
|
||||||
cfg->addOption(
|
cfg->addOption(
|
||||||
"sync", JSON::fromString("{\"short\":\"y\",\"long\":\"sync\",\"help\":"
|
"async", JSON::fromString("{\"short\":\"y\",\"long\":\"async\",\"help\":"
|
||||||
"\"Retrieve tracks in sync (default async)\"}"));
|
"\"Retrieve tracks in async track sorting mode (default sync)\"}"));
|
||||||
cfg->addBasicConnectorOptions(capa);
|
cfg->addBasicConnectorOptions(capa);
|
||||||
config = cfg;
|
config = cfg;
|
||||||
}
|
}
|
||||||
|
@ -87,9 +99,31 @@ namespace Mist{
|
||||||
|
|
||||||
#define printTime(t) std::setfill('0') << std::setw(2) << (t / 3600000) << ":" << std::setw(2) << ((t % 3600000) / 60000) << ":" << std::setw(2) << ((t % 60000) / 1000) << "." << std::setw(3) << (t % 1000)
|
#define printTime(t) std::setfill('0') << std::setw(2) << (t / 3600000) << ":" << std::setw(2) << ((t % 3600000) / 60000) << ":" << std::setw(2) << ((t % 60000) / 1000) << "." << std::setw(3) << (t % 1000)
|
||||||
|
|
||||||
|
void OutSanityCheck::writeContext(){
|
||||||
|
std::cout << "Last few good packets:" << std::endl;
|
||||||
|
while (packets.size()){
|
||||||
|
std::cout << " " << packets.front() << std::endl;
|
||||||
|
packets.pop_front();
|
||||||
|
}
|
||||||
|
std::cout << "Nearby keyframes for this track (" << thisIdx << ", " << M.getType(thisIdx) << "):" << std::endl;
|
||||||
|
size_t keyIndex = M.getKeyIndexForTime(thisIdx, thisTime);
|
||||||
|
DTSC::Keys keys = M.getKeys(thisIdx);
|
||||||
|
size_t from = keyIndex - 5;
|
||||||
|
size_t to = keyIndex + 5;
|
||||||
|
|
||||||
|
if (from < keys.getFirstValid()){from = keys.getFirstValid();}
|
||||||
|
if (to > keys.getEndValid()){to = keys.getEndValid();}
|
||||||
|
for (size_t i = from; i <= to; ++i){
|
||||||
|
std::cout << " " << i << ": " << keys.getTime(i) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void OutSanityCheck::sendNext(){
|
void OutSanityCheck::sendNext(){
|
||||||
static std::map<size_t, uint64_t> trkTime;
|
|
||||||
if (M.getLive()){
|
|
||||||
if (thisTime < trkTime[thisIdx]){
|
if (thisTime < trkTime[thisIdx]){
|
||||||
std::cout << "Time error in track " << thisIdx << ": ";
|
std::cout << "Time error in track " << thisIdx << ": ";
|
||||||
std::cout << printTime(thisTime) << " < " << printTime(trkTime[thisIdx]) << std::endl << std::endl;
|
std::cout << printTime(thisTime) << " < " << printTime(trkTime[thisIdx]) << std::endl << std::endl;
|
||||||
|
@ -102,29 +136,45 @@ namespace Mist{
|
||||||
std::cout << it->first << ":" << printTime(it->second) << "/" << printTime(t) << ", ";
|
std::cout << it->first << ":" << printTime(it->second) << "/" << printTime(t) << ", ";
|
||||||
}
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thisIdx != sortSet.begin()->trackID || thisPacket.getTime() != sortSet.begin()->time){
|
if (syncMode){
|
||||||
while (packets.size()){
|
if (thisIdx != sortSet.begin()->tid || thisPacket.getTime() != sortSet.begin()->time){
|
||||||
std::cout << packets.front() << std::endl;
|
std::cout << "Input is inconsistent! Expected " << sortSet.begin()->tid << ":"
|
||||||
packets.pop_front();
|
|
||||||
}
|
|
||||||
std::cout << "Input is inconsistent! Expected " << sortSet.begin()->trackID << ":"
|
|
||||||
<< sortSet.begin()->time << " but got " << thisIdx << ":" << thisPacket.getTime()
|
<< sortSet.begin()->time << " but got " << thisIdx << ":" << thisPacket.getTime()
|
||||||
<< " (expected part " << sortSet.begin()->index << " in "
|
<< " (expected part " << sortSet.begin()->partIndex << " in "
|
||||||
<< M.getCodec(sortSet.begin()->trackID) << " track)" << std::endl;
|
<< M.getCodec(sortSet.begin()->tid) << " track)" << std::endl;
|
||||||
|
writeContext();
|
||||||
myConn.close();
|
myConn.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t keyIndex = M.getKeyIndexForTime(thisIdx, thisTime);
|
||||||
size_t keyIndex = M.getKeyIndexForTime(getMainSelectedTrack(), thisPacket.getTime());
|
uint64_t keyTime = M.getTimeForKeyIndex(thisIdx, keyIndex);
|
||||||
uint64_t keyTime = M.getTimeForKeyIndex(getMainSelectedTrack(), keyIndex);
|
bool isKey = thisPacket.getFlag("keyframe");
|
||||||
if (keyTime > thisPacket.getTime()){
|
if (keyTime > thisTime){
|
||||||
std::cout << "Corruption? Our time is " << thisPacket.getTime() << ", but our key time is " << keyTime << std::endl;
|
std::cout << "Corruption? Our time is " << thisTime << ", but our key time is " << keyTime << std::endl;
|
||||||
|
writeContext();
|
||||||
myConn.close();
|
myConn.close();
|
||||||
return;
|
return;
|
||||||
|
}else{
|
||||||
|
if (M.getType(thisIdx) == "video"){
|
||||||
|
if (keyTime == thisTime){
|
||||||
|
if (!isKey){
|
||||||
|
std::cout << "Corruption? Video packet at time " << thisTime << " should be a keyframe, but isn't!" << std::endl;
|
||||||
|
writeContext();
|
||||||
|
myConn.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if (isKey){
|
||||||
|
std::cout << "Corruption? Video packet at time " << thisTime << " should not be a keyframe, but is!" << std::endl;
|
||||||
|
writeContext();
|
||||||
|
myConn.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Packet is normally sent here
|
// Packet is normally sent here
|
||||||
|
@ -132,18 +182,16 @@ namespace Mist{
|
||||||
while (packets.size() > 10){packets.pop_front();}
|
while (packets.size() > 10){packets.pop_front();}
|
||||||
|
|
||||||
// keep track of where we are
|
// keep track of where we are
|
||||||
if (!sortSet.empty()){
|
if (syncMode && sortSet.size()){
|
||||||
keyPart temp = *sortSet.begin();
|
Util::sortedPageInfo temp = *sortSet.begin();
|
||||||
temp.index++;
|
temp.partIndex++;
|
||||||
DTSC::Parts parts(M.parts(temp.trackID));
|
DTSC::Parts parts(M.parts(temp.tid));
|
||||||
if (temp.index < parts.getEndValid()){// only insert when there are parts left
|
if (temp.partIndex < parts.getEndValid()){// only insert when there are parts left
|
||||||
temp.time = temp.endTime; // timeplace of frame
|
temp.time += parts.getDuration(temp.partIndex-1);
|
||||||
temp.endTime = temp.time + parts.getDuration(temp.index);
|
sortSet.replaceFirst(temp);
|
||||||
temp.size = parts.getSize(temp.index); // bytesize of frame
|
}else{
|
||||||
sortSet.insert(temp);
|
sortSet.dropTrack(temp.tid);
|
||||||
}
|
}
|
||||||
// remove highest keyPart
|
|
||||||
sortSet.erase(sortSet.begin());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,36 +2,20 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
namespace Mist{
|
namespace Mist{
|
||||||
struct keyPart{
|
|
||||||
public:
|
|
||||||
bool operator<(const keyPart &rhs) const{
|
|
||||||
if (time < rhs.time){return true;}
|
|
||||||
if (time == rhs.time){
|
|
||||||
if (trackID < rhs.trackID){return true;}
|
|
||||||
if (trackID == rhs.trackID){return index < rhs.index;}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
long unsigned int trackID;
|
|
||||||
long unsigned int size;
|
|
||||||
long long unsigned int time;
|
|
||||||
long long unsigned int endTime;
|
|
||||||
long long unsigned int byteOffset; // added for MP4 fragmented
|
|
||||||
long int timeOffset; // added for MP4 fragmented
|
|
||||||
long unsigned int duration; // added for MP4 fragmented
|
|
||||||
long unsigned int index;
|
|
||||||
};
|
|
||||||
|
|
||||||
class OutSanityCheck : public Output{
|
class OutSanityCheck : public Output{
|
||||||
public:
|
public:
|
||||||
OutSanityCheck(Socket::Connection &conn);
|
OutSanityCheck(Socket::Connection &conn);
|
||||||
static void init(Util::Config *cfg);
|
static void init(Util::Config *cfg);
|
||||||
void sendNext();
|
void sendNext();
|
||||||
|
void sendHeader();
|
||||||
static bool listenMode(){return false;}
|
static bool listenMode(){return false;}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void writeContext();
|
||||||
|
bool syncMode;
|
||||||
std::deque<std::string> packets;
|
std::deque<std::string> packets;
|
||||||
std::set<keyPart> sortSet; // needed for unfragmented MP4, remembers the order of keyparts
|
Util::packetSorter sortSet;
|
||||||
|
std::map<size_t, uint64_t> trkTime;
|
||||||
};
|
};
|
||||||
}// namespace Mist
|
}// namespace Mist
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue