From 4080d141f850c007726bb82fb9863ac9ef9239d9 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 2 Feb 2021 01:20:23 +0100 Subject: [PATCH] Fix bug in Output::seek() when seeking to a timestamp that is between the last part of a key and the first part of the next key --- lib/dtsc.cpp | 41 +++++++++++++++++++++++++++++----------- lib/dtsc.h | 2 +- src/input/input_dtsc.cpp | 2 +- src/input/input_flv.cpp | 2 +- src/input/input_mp3.cpp | 2 +- src/input/input_ts.cpp | 4 ++-- src/output/output.cpp | 17 ++++++++--------- 7 files changed, 44 insertions(+), 26 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 4baaf409..5f2ead11 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -6,6 +6,7 @@ #include "dtsc.h" #include "encode.h" #include "lib/shared_memory.h" +#include "lib/util.h" #include //for htonl/ntohl #include #include @@ -3159,6 +3160,35 @@ namespace DTSC{ return pages.getInt("firstkey", res); } + /// Returns the key number containing a given time. + /// Or, closest key if given time is not available. + /// Or, zero if no keys are available at all. + /// If the time is in the gap before a key, returns that next key instead. + size_t Meta::getKeyNumForTime(uint32_t idx, uint64_t time) const{ + const Track &trk = tracks.at(idx); + const Util::RelAccX &keys = trk.keys; + const Util::RelAccX &parts = trk.parts; + if (!keys.getEndPos()){return 0;} + size_t res = keys.getStartPos(); + for (size_t i = res; i < keys.getEndPos(); i++){ + if (keys.getInt(trk.keyTimeField, i) > time){ + //It's possible we overshot our timestamp, but the previous key does not contain it. + //This happens when seeking to a timestamp past the last part of the previous key, but + //before the first part of the next key. + //In this case, we should _not_ return the previous key, but the current key. + //That prevents getting stuck at the end of the page, waiting for a part to show up that never will. + if (keys.getInt(trk.keyFirstPartField, i) > parts.getStartPos()){ + uint64_t dur = parts.getInt(trk.partDurationField, keys.getInt(trk.keyFirstPartField, i)-1); + if (keys.getInt(trk.keyTimeField, i) - dur < time){res = i;} + } + break; + } + res = i; + } + return res; + } + + Parts::Parts(const Util::RelAccX &_parts) : parts(_parts){ sizeField = parts.getFieldData("size"); durationField = parts.getFieldData("duration"); @@ -3208,17 +3238,6 @@ namespace DTSC{ } size_t Keys::getSize(size_t idx) const{return cKeys.getInt(sizeField, idx);} - /// Returns the key number containing a given timestamp. - /// Returns the closest key number if the timestamp is not available. - size_t Keys::getNumForTime(uint64_t time) const{ - size_t res = getFirstValid(); - for (size_t i = getFirstValid(); i < getEndValid(); i++){ - if (getTime(i) > time){break;} - res = i; - } - return res; - } - Fragments::Fragments(const Util::RelAccX &_fragments) : fragments(_fragments){} size_t Fragments::getFirstValid() const{return fragments.getDeleted();} size_t Fragments::getEndValid() const{return fragments.getEndPos();} diff --git a/lib/dtsc.h b/lib/dtsc.h index 059d8acf..127d00e9 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -189,7 +189,6 @@ namespace DTSC{ uint64_t getTime(size_t idx) const; void setSize(size_t idx, size_t _size); size_t getSize(size_t idx) const; - size_t getNumForTime(uint64_t time) const; private: bool isConst; @@ -442,6 +441,7 @@ namespace DTSC{ bool nextPageAvailable(uint32_t idx, size_t currentPage) const; size_t getPageNumberForTime(uint32_t idx, uint64_t time) const; size_t getPageNumberForKey(uint32_t idx, uint64_t keynumber) const; + size_t getKeyNumForTime(uint32_t idx, uint64_t time) const; const Util::RelAccX &parts(size_t idx) const; Util::RelAccX &keys(size_t idx); diff --git a/src/input/input_dtsc.cpp b/src/input/input_dtsc.cpp index 3cf184d8..7fe4759f 100644 --- a/src/input/input_dtsc.cpp +++ b/src/input/input_dtsc.cpp @@ -394,7 +394,7 @@ namespace Mist{ tmpPos.seekTime = 0; } DTSC::Keys keys(M.keys(trackIdx)); - uint32_t keyNum = keys.getNumForTime(ms); + uint32_t keyNum = M.getKeyNumForTime(trackIdx, ms); if (keys.getTime(keyNum) > tmpPos.seekTime){ tmpPos.seekTime = keys.getTime(keyNum); tmpPos.bytePos = keys.getBpos(keyNum); diff --git a/src/input/input_flv.cpp b/src/input/input_flv.cpp index 5ac9157a..459c6943 100644 --- a/src/input/input_flv.cpp +++ b/src/input/input_flv.cpp @@ -163,7 +163,7 @@ namespace Mist{ // keyframe. Flv files are never multi-track, so track 1 is video, track 2 is audio. size_t seekTrack = (idx == INVALID_TRACK_ID ? M.mainTrack() : idx); DTSC::Keys keys(M.keys(seekTrack)); - uint32_t keyNum = keys.getNumForTime(seekTime); + uint32_t keyNum = M.getKeyNumForTime(seekTrack, seekTime); Util::fseek(inFile, keys.getBpos(keyNum), SEEK_SET); } }// namespace Mist diff --git a/src/input/input_mp3.cpp b/src/input/input_mp3.cpp index be1a6360..83bc7f37 100644 --- a/src/input/input_mp3.cpp +++ b/src/input/input_mp3.cpp @@ -150,7 +150,7 @@ namespace Mist{ void inputMP3::seek(uint64_t seekTime, size_t idx){ DTSC::Keys keys(M.keys(idx)); - uint32_t keyNum = keys.getNumForTime(seekTime); + uint32_t keyNum = M.getKeyNumForTime(idx, seekTime); fseek(inFile, keys.getBpos(keyNum), SEEK_SET); timestamp = keys.getTime(keyNum); } diff --git a/src/input/input_ts.cpp b/src/input/input_ts.cpp index c9959142..1ca762b6 100644 --- a/src/input/input_ts.cpp +++ b/src/input/input_ts.cpp @@ -457,14 +457,14 @@ namespace Mist{ readPMT(); uint64_t seekPos = 0xFFFFFFFFull; if (idx != INVALID_TRACK_ID){ + uint32_t keyNum = M.getKeyNumForTime(idx, seekTime); DTSC::Keys keys(M.keys(idx)); - uint32_t keyNum = keys.getNumForTime(seekTime); seekPos = keys.getBpos(keyNum); }else{ std::set tracks = M.getValidTracks(); for (std::set::iterator it = tracks.begin(); it != tracks.end(); it++){ + uint32_t keyNum = M.getKeyNumForTime(*it, seekTime); DTSC::Keys keys(M.keys(*it)); - uint32_t keyNum = keys.getNumForTime(seekTime); uint64_t thisBPos = keys.getBpos(keyNum); if (thisBPos < seekPos){seekPos = thisBPos;} } diff --git a/src/output/output.cpp b/src/output/output.cpp index 64344c8c..da70af24 100644 --- a/src/output/output.cpp +++ b/src/output/output.cpp @@ -491,11 +491,11 @@ namespace Mist{ } //Abort if the track is not loaded if (!M.trackLoaded(trk)){return 0;} - DTSC::Keys keys(M.keys(trk)); + const DTSC::Keys &keys = M.keys(trk); //Abort if there are no keys if (!keys.getValidCount()){return 0;} //Get the key for the current time - size_t keyNum = keys.getNumForTime(lastPacketTime); + size_t keyNum = M.getKeyNumForTime(trk, lastPacketTime); if (keys.getEndValid() <= keyNum+1){return 0;} //Return the next key return keys.getTime(keyNum+1); @@ -687,7 +687,7 @@ namespace Mist{ } if (M.getType(mainTrack) == "video"){ DTSC::Keys keys(M.keys(mainTrack)); - size_t keyNum = keys.getNumForTime(pos); + size_t keyNum = M.getKeyNumForTime(mainTrack, pos); pos = keys.getTime(keyNum); } } @@ -732,7 +732,7 @@ namespace Mist{ return false; } DTSC::Keys keys(M.keys(tid)); - size_t keyNum = keys.getNumForTime(pos); + size_t keyNum = M.getKeyNumForTime(tid, pos); uint64_t actualKeyTime = keys.getTime(keyNum); HIGH_MSG("Seeking to track %zu key %zu => time %" PRIu64, tid, keyNum, pos); if (actualKeyTime > pos){ @@ -1419,7 +1419,7 @@ namespace Mist{ userSelect[mainTrack].reload(streamName, mainTrack); // now, seek to the exact timestamp of the keyframe DTSC::Keys keys(M.keys(mainTrack)); - unsigned int targetKey = keys.getNumForTime(currTime); + size_t targetKey = M.getKeyNumForTime(mainTrack, currTime); seek(keys.getTime(targetKey)); // attempt to load the key into thisPacket bool ret = prepareNext(); @@ -1547,11 +1547,11 @@ namespace Mist{ dropTrack(nxt.tid, "end of VoD track reached", false); return true; } - DTSC::Keys keys(M.keys(nxt.tid)); - size_t thisKey = keys.getNumForTime(nxt.time); + size_t thisKey = M.getKeyNumForTime(nxt.tid, nxt.time); //Check if there exists a different page for the next key size_t nextKeyPage = M.getPageNumberForKey(nxt.tid, thisKey + 1); if (nextKeyPage != INVALID_KEY_NUM && nextKeyPage != currentPage[nxt.tid]){ + DTSC::Keys keys(M.keys(nxt.tid)); // If so, the next key is our next packet nextTime = keys.getTime(thisKey + 1); }else{ @@ -1617,8 +1617,7 @@ namespace Mist{ //Update keynum only when the second flips over in the timestamp //We do this because DTSC::Keys is pretty CPU-heavy if (nxt.time / 1000 < nextTime/1000){ - DTSC::Keys keys(M.keys(nxt.tid)); - size_t thisKey = keys.getNumForTime(nxt.time); + size_t thisKey = M.getKeyNumForTime(nxt.tid, nxt.time); userSelect[nxt.tid].setKeyNum(thisKey); }