diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index b821450e..7c62d4b0 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -3430,7 +3430,6 @@ namespace DTSC{ } void Keys::applyLimiter(uint64_t _min, uint64_t _max, DTSC::Parts _p){ - // Determine first and last key available within the limits // Note: limMax replaces getEndValid(), and is thus one _past_ the end key index! limMin = getFirstValid(); diff --git a/src/output/output_sanitycheck.cpp b/src/output/output_sanitycheck.cpp index d86f11d2..9c637c5c 100644 --- a/src/output/output_sanitycheck.cpp +++ b/src/output/output_sanitycheck.cpp @@ -14,55 +14,67 @@ namespace Mist{ //} parseData = true; wantRequest = false; - if (config->getBool("sync")){ - setSyncMode(true); - }else{ + syncMode = true; + if (config->getBool("async")){ setSyncMode(false); + syncMode = false; + }else{ + setSyncMode(true); } - initialize(); - initialSeek(); + } + + void OutSanityCheck::sendHeader(){ + Output::sendHeader(); sortSet.clear(); - if (!M.getLive()){ - realTime = 0; + realTime = 0; + if (syncMode){ for (std::map::const_iterator it = userSelect.begin(); it != userSelect.end(); it++){ - keyPart temp; - temp.trackID = it->first; - temp.time = M.getFirstms(it->first); // timeplace of frame - DTSC::Parts parts(M.parts(it->first)); - temp.endTime = M.getFirstms(it->first) + parts.getDuration(parts.getFirstValid()); - temp.size = parts.getSize(parts.getFirstValid()); // bytesize of frame (alle parts all together) - temp.index = 0; + trkTime[it->first] = 0; + Util::sortedPageInfo temp; + temp.tid = it->first; + DTSC::Keys keys = M.getKeys(it->first); + size_t firstKey = keys.getFirstValid(); + temp.time = keys.getTime(firstKey); + 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); } if (config->getInteger("seek")){ uint64_t seekPoint = config->getInteger("seek"); - while (!sortSet.empty() && sortSet.begin()->time < seekPoint){ - keyPart temp = *sortSet.begin(); - temp.index++; - DTSC::Parts parts(M.parts(temp.trackID)); - if (temp.index < parts.getEndValid()){// only insert when there are parts left - temp.time = temp.endTime; // timeplace of frame - temp.endTime = temp.time + parts.getDuration(temp.index); - temp.size = parts.getSize(temp.index); - ; // bytesize of frame - sortSet.insert(temp); + while (sortSet.size() && sortSet.begin()->time < seekPoint){ + Util::sortedPageInfo temp = *sortSet.begin(); + temp.partIndex++; + DTSC::Parts parts(M.parts(temp.tid)); + if (temp.partIndex < parts.getEndValid()){// only insert when there are parts left + temp.time += parts.getDuration(temp.partIndex - 1); // timeplace of frame + sortSet.replaceFirst(temp); + }else{ + sortSet.dropTrack(temp.tid); } - // remove highest keyPart - sortSet.erase(sortSet.begin()); } seek(seekPoint); } } - } void OutSanityCheck::init(Util::Config *cfg){ Output::init(cfg); capa["name"] = "SanityCheck"; + capa["friendly"] = "Development tool: Sanity checker"; capa["desc"] = "Does sanity check on a stream"; 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\":" "\"stream\",\"help\":\"The name of the stream " "that this connector will transmit.\"}")); @@ -70,8 +82,8 @@ namespace Mist{ "seek", JSON::fromString("{\"arg\":\"string\",\"short\":\"k\",\"long\":\"seek\",\"help\":" "\"Time in ms to check from - by default start of stream\"}")); cfg->addOption( - "sync", JSON::fromString("{\"short\":\"y\",\"long\":\"sync\",\"help\":" - "\"Retrieve tracks in sync (default async)\"}")); + "async", JSON::fromString("{\"short\":\"y\",\"long\":\"async\",\"help\":" + "\"Retrieve tracks in async track sorting mode (default sync)\"}")); cfg->addBasicConnectorOptions(capa); config = cfg; } @@ -87,44 +99,82 @@ 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) + 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(){ - static std::map trkTime; - if (M.getLive()){ - if (thisTime < trkTime[thisIdx]){ - std::cout << "Time error in track " << thisIdx << ": "; - std::cout << printTime(thisTime) << " < " << printTime(trkTime[thisIdx]) << std::endl << std::endl; - }else{ - trkTime[thisIdx] = thisTime; + + if (thisTime < trkTime[thisIdx]){ + std::cout << "Time error in track " << thisIdx << ": "; + std::cout << printTime(thisTime) << " < " << printTime(trkTime[thisIdx]) << std::endl << std::endl; + }else{ + trkTime[thisIdx] = thisTime; + } + std::cout << "\033[A"; + for (std::map::iterator it = trkTime.begin(); it != trkTime.end(); ++it){ + uint64_t t = M.getLastms(it->first); + std::cout << it->first << ":" << printTime(it->second) << "/" << printTime(t) << ", "; + } + std::cout << std::endl; + + if (syncMode){ + if (thisIdx != sortSet.begin()->tid || thisPacket.getTime() != sortSet.begin()->time){ + std::cout << "Input is inconsistent! Expected " << sortSet.begin()->tid << ":" + << sortSet.begin()->time << " but got " << thisIdx << ":" << thisPacket.getTime() + << " (expected part " << sortSet.begin()->partIndex << " in " + << M.getCodec(sortSet.begin()->tid) << " track)" << std::endl; + writeContext(); + myConn.close(); + return; } - std::cout << "\033[A"; - for (std::map::iterator it = trkTime.begin(); it != trkTime.end(); ++it){ - uint64_t t = M.getLastms(it->first); - std::cout << it->first << ":" << printTime(it->second) << "/" << printTime(t) << ", "; - } - std::cout << std::endl; - return; } - if (thisIdx != sortSet.begin()->trackID || thisPacket.getTime() != sortSet.begin()->time){ - while (packets.size()){ - std::cout << packets.front() << std::endl; - packets.pop_front(); + size_t keyIndex = M.getKeyIndexForTime(thisIdx, thisTime); + uint64_t keyTime = M.getTimeForKeyIndex(thisIdx, keyIndex); + bool isKey = thisPacket.getFlag("keyframe"); + if (keyTime > thisTime){ + std::cout << "Corruption? Our time is " << thisTime << ", but our key time is " << keyTime << std::endl; + writeContext(); + myConn.close(); + 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; + } + } } - std::cout << "Input is inconsistent! Expected " << sortSet.begin()->trackID << ":" - << sortSet.begin()->time << " but got " << thisIdx << ":" << thisPacket.getTime() - << " (expected part " << sortSet.begin()->index << " in " - << M.getCodec(sortSet.begin()->trackID) << " track)" << std::endl; - myConn.close(); - return; - } - - - size_t keyIndex = M.getKeyIndexForTime(getMainSelectedTrack(), thisPacket.getTime()); - uint64_t keyTime = M.getTimeForKeyIndex(getMainSelectedTrack(), keyIndex); - if (keyTime > thisPacket.getTime()){ - std::cout << "Corruption? Our time is " << thisPacket.getTime() << ", but our key time is " << keyTime << std::endl; - myConn.close(); - return; } // Packet is normally sent here @@ -132,18 +182,16 @@ namespace Mist{ while (packets.size() > 10){packets.pop_front();} // keep track of where we are - if (!sortSet.empty()){ - keyPart temp = *sortSet.begin(); - temp.index++; - DTSC::Parts parts(M.parts(temp.trackID)); - if (temp.index < parts.getEndValid()){// only insert when there are parts left - temp.time = temp.endTime; // timeplace of frame - temp.endTime = temp.time + parts.getDuration(temp.index); - temp.size = parts.getSize(temp.index); // bytesize of frame - sortSet.insert(temp); + if (syncMode && sortSet.size()){ + Util::sortedPageInfo temp = *sortSet.begin(); + temp.partIndex++; + DTSC::Parts parts(M.parts(temp.tid)); + if (temp.partIndex < parts.getEndValid()){// only insert when there are parts left + temp.time += parts.getDuration(temp.partIndex-1); + sortSet.replaceFirst(temp); + }else{ + sortSet.dropTrack(temp.tid); } - // remove highest keyPart - sortSet.erase(sortSet.begin()); } } diff --git a/src/output/output_sanitycheck.h b/src/output/output_sanitycheck.h index 8dc211a4..12b133fe 100644 --- a/src/output/output_sanitycheck.h +++ b/src/output/output_sanitycheck.h @@ -2,36 +2,20 @@ #include 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{ public: OutSanityCheck(Socket::Connection &conn); static void init(Util::Config *cfg); void sendNext(); + void sendHeader(); static bool listenMode(){return false;} protected: + void writeContext(); + bool syncMode; std::deque packets; - std::set sortSet; // needed for unfragmented MP4, remembers the order of keyparts + Util::packetSorter sortSet; + std::map trkTime; }; }// namespace Mist