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

This commit is contained in:
Thulinma 2021-02-02 01:20:23 +01:00
parent dd5ae98002
commit 4080d141f8
7 changed files with 44 additions and 26 deletions

View file

@ -6,6 +6,7 @@
#include "dtsc.h" #include "dtsc.h"
#include "encode.h" #include "encode.h"
#include "lib/shared_memory.h" #include "lib/shared_memory.h"
#include "lib/util.h"
#include <arpa/inet.h> //for htonl/ntohl #include <arpa/inet.h> //for htonl/ntohl
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@ -3159,6 +3160,35 @@ namespace DTSC{
return pages.getInt("firstkey", res); 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){ Parts::Parts(const Util::RelAccX &_parts) : parts(_parts){
sizeField = parts.getFieldData("size"); sizeField = parts.getFieldData("size");
durationField = parts.getFieldData("duration"); durationField = parts.getFieldData("duration");
@ -3208,17 +3238,6 @@ namespace DTSC{
} }
size_t Keys::getSize(size_t idx) const{return cKeys.getInt(sizeField, idx);} 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){} Fragments::Fragments(const Util::RelAccX &_fragments) : fragments(_fragments){}
size_t Fragments::getFirstValid() const{return fragments.getDeleted();} size_t Fragments::getFirstValid() const{return fragments.getDeleted();}
size_t Fragments::getEndValid() const{return fragments.getEndPos();} size_t Fragments::getEndValid() const{return fragments.getEndPos();}

View file

@ -189,7 +189,6 @@ namespace DTSC{
uint64_t getTime(size_t idx) const; uint64_t getTime(size_t idx) const;
void setSize(size_t idx, size_t _size); void setSize(size_t idx, size_t _size);
size_t getSize(size_t idx) const; size_t getSize(size_t idx) const;
size_t getNumForTime(uint64_t time) const;
private: private:
bool isConst; bool isConst;
@ -442,6 +441,7 @@ namespace DTSC{
bool nextPageAvailable(uint32_t idx, size_t currentPage) const; bool nextPageAvailable(uint32_t idx, size_t currentPage) const;
size_t getPageNumberForTime(uint32_t idx, uint64_t time) const; size_t getPageNumberForTime(uint32_t idx, uint64_t time) const;
size_t getPageNumberForKey(uint32_t idx, uint64_t keynumber) 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; const Util::RelAccX &parts(size_t idx) const;
Util::RelAccX &keys(size_t idx); Util::RelAccX &keys(size_t idx);

View file

@ -394,7 +394,7 @@ namespace Mist{
tmpPos.seekTime = 0; tmpPos.seekTime = 0;
} }
DTSC::Keys keys(M.keys(trackIdx)); 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){ if (keys.getTime(keyNum) > tmpPos.seekTime){
tmpPos.seekTime = keys.getTime(keyNum); tmpPos.seekTime = keys.getTime(keyNum);
tmpPos.bytePos = keys.getBpos(keyNum); tmpPos.bytePos = keys.getBpos(keyNum);

View file

@ -163,7 +163,7 @@ namespace Mist{
// keyframe. Flv files are never multi-track, so track 1 is video, track 2 is audio. // 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); size_t seekTrack = (idx == INVALID_TRACK_ID ? M.mainTrack() : idx);
DTSC::Keys keys(M.keys(seekTrack)); 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); Util::fseek(inFile, keys.getBpos(keyNum), SEEK_SET);
} }
}// namespace Mist }// namespace Mist

View file

@ -150,7 +150,7 @@ namespace Mist{
void inputMP3::seek(uint64_t seekTime, size_t idx){ void inputMP3::seek(uint64_t seekTime, size_t idx){
DTSC::Keys keys(M.keys(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); fseek(inFile, keys.getBpos(keyNum), SEEK_SET);
timestamp = keys.getTime(keyNum); timestamp = keys.getTime(keyNum);
} }

View file

@ -457,14 +457,14 @@ namespace Mist{
readPMT(); readPMT();
uint64_t seekPos = 0xFFFFFFFFull; uint64_t seekPos = 0xFFFFFFFFull;
if (idx != INVALID_TRACK_ID){ if (idx != INVALID_TRACK_ID){
uint32_t keyNum = M.getKeyNumForTime(idx, seekTime);
DTSC::Keys keys(M.keys(idx)); DTSC::Keys keys(M.keys(idx));
uint32_t keyNum = keys.getNumForTime(seekTime);
seekPos = keys.getBpos(keyNum); seekPos = keys.getBpos(keyNum);
}else{ }else{
std::set<size_t> tracks = M.getValidTracks(); std::set<size_t> tracks = M.getValidTracks();
for (std::set<size_t>::iterator it = tracks.begin(); it != tracks.end(); it++){ for (std::set<size_t>::iterator it = tracks.begin(); it != tracks.end(); it++){
uint32_t keyNum = M.getKeyNumForTime(*it, seekTime);
DTSC::Keys keys(M.keys(*it)); DTSC::Keys keys(M.keys(*it));
uint32_t keyNum = keys.getNumForTime(seekTime);
uint64_t thisBPos = keys.getBpos(keyNum); uint64_t thisBPos = keys.getBpos(keyNum);
if (thisBPos < seekPos){seekPos = thisBPos;} if (thisBPos < seekPos){seekPos = thisBPos;}
} }

View file

@ -491,11 +491,11 @@ namespace Mist{
} }
//Abort if the track is not loaded //Abort if the track is not loaded
if (!M.trackLoaded(trk)){return 0;} 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 //Abort if there are no keys
if (!keys.getValidCount()){return 0;} if (!keys.getValidCount()){return 0;}
//Get the key for the current time //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;} if (keys.getEndValid() <= keyNum+1){return 0;}
//Return the next key //Return the next key
return keys.getTime(keyNum+1); return keys.getTime(keyNum+1);
@ -687,7 +687,7 @@ namespace Mist{
} }
if (M.getType(mainTrack) == "video"){ if (M.getType(mainTrack) == "video"){
DTSC::Keys keys(M.keys(mainTrack)); DTSC::Keys keys(M.keys(mainTrack));
size_t keyNum = keys.getNumForTime(pos); size_t keyNum = M.getKeyNumForTime(mainTrack, pos);
pos = keys.getTime(keyNum); pos = keys.getTime(keyNum);
} }
} }
@ -732,7 +732,7 @@ namespace Mist{
return false; return false;
} }
DTSC::Keys keys(M.keys(tid)); 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); uint64_t actualKeyTime = keys.getTime(keyNum);
HIGH_MSG("Seeking to track %zu key %zu => time %" PRIu64, tid, keyNum, pos); HIGH_MSG("Seeking to track %zu key %zu => time %" PRIu64, tid, keyNum, pos);
if (actualKeyTime > pos){ if (actualKeyTime > pos){
@ -1419,7 +1419,7 @@ namespace Mist{
userSelect[mainTrack].reload(streamName, mainTrack); userSelect[mainTrack].reload(streamName, mainTrack);
// now, seek to the exact timestamp of the keyframe // now, seek to the exact timestamp of the keyframe
DTSC::Keys keys(M.keys(mainTrack)); DTSC::Keys keys(M.keys(mainTrack));
unsigned int targetKey = keys.getNumForTime(currTime); size_t targetKey = M.getKeyNumForTime(mainTrack, currTime);
seek(keys.getTime(targetKey)); seek(keys.getTime(targetKey));
// attempt to load the key into thisPacket // attempt to load the key into thisPacket
bool ret = prepareNext(); bool ret = prepareNext();
@ -1547,11 +1547,11 @@ namespace Mist{
dropTrack(nxt.tid, "end of VoD track reached", false); dropTrack(nxt.tid, "end of VoD track reached", false);
return true; return true;
} }
DTSC::Keys keys(M.keys(nxt.tid)); size_t thisKey = M.getKeyNumForTime(nxt.tid, nxt.time);
size_t thisKey = keys.getNumForTime(nxt.time);
//Check if there exists a different page for the next key //Check if there exists a different page for the next key
size_t nextKeyPage = M.getPageNumberForKey(nxt.tid, thisKey + 1); size_t nextKeyPage = M.getPageNumberForKey(nxt.tid, thisKey + 1);
if (nextKeyPage != INVALID_KEY_NUM && nextKeyPage != currentPage[nxt.tid]){ 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 // If so, the next key is our next packet
nextTime = keys.getTime(thisKey + 1); nextTime = keys.getTime(thisKey + 1);
}else{ }else{
@ -1617,8 +1617,7 @@ namespace Mist{
//Update keynum only when the second flips over in the timestamp //Update keynum only when the second flips over in the timestamp
//We do this because DTSC::Keys is pretty CPU-heavy //We do this because DTSC::Keys is pretty CPU-heavy
if (nxt.time / 1000 < nextTime/1000){ if (nxt.time / 1000 < nextTime/1000){
DTSC::Keys keys(M.keys(nxt.tid)); size_t thisKey = M.getKeyNumForTime(nxt.tid, nxt.time);
size_t thisKey = keys.getNumForTime(nxt.time);
userSelect[nxt.tid].setKeyNum(thisKey); userSelect[nxt.tid].setKeyNum(thisKey);
} }