Improved buffer behaviour for streams that are faster than real-time. Improved documentation for those areas as well.
This commit is contained in:
parent
748960bb44
commit
c5870b02f1
3 changed files with 80 additions and 35 deletions
|
@ -271,6 +271,7 @@ namespace DTSC {
|
||||||
std::deque<unsigned long> keySizes;
|
std::deque<unsigned long> keySizes;
|
||||||
std::deque<Part> parts;
|
std::deque<Part> parts;
|
||||||
Key & getKey(unsigned int keyNum);
|
Key & getKey(unsigned int keyNum);
|
||||||
|
Fragment & getFrag(unsigned int fragNum);
|
||||||
unsigned int timeToKeynum(unsigned int timestamp);
|
unsigned int timeToKeynum(unsigned int timestamp);
|
||||||
unsigned int timeToFragnum(unsigned int timestamp);
|
unsigned int timeToFragnum(unsigned int timestamp);
|
||||||
void reset();
|
void reset();
|
||||||
|
@ -297,8 +298,11 @@ namespace DTSC {
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
int fpks;
|
int fpks;
|
||||||
|
void removeFirstKey();
|
||||||
|
uint32_t secsSinceFirstFragmentInsert();
|
||||||
private:
|
private:
|
||||||
std::string cachedIdent;
|
std::string cachedIdent;
|
||||||
|
std::deque<uint32_t> fragInsertTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
///\brief Class for storage of meta data
|
///\brief Class for storage of meta data
|
||||||
|
|
|
@ -1179,6 +1179,8 @@ namespace DTSC {
|
||||||
newFrag.setDuration(0);
|
newFrag.setDuration(0);
|
||||||
newFrag.setSize(0);
|
newFrag.setSize(0);
|
||||||
fragments.push_back(newFrag);
|
fragments.push_back(newFrag);
|
||||||
|
//We set the insert time lastms-firstms in the future, to prevent unstable playback
|
||||||
|
fragInsertTime.push_back(Util::bootSecs() + ((lastms - firstms)/1000));
|
||||||
} else {
|
} else {
|
||||||
Fragment & lastFrag = fragments[fragments.size() - 1];
|
Fragment & lastFrag = fragments[fragments.size() - 1];
|
||||||
lastFrag.setLength(lastFrag.getLength() + 1);
|
lastFrag.setLength(lastFrag.getLength() + 1);
|
||||||
|
@ -1189,6 +1191,41 @@ namespace DTSC {
|
||||||
fragments.rbegin()->setSize(fragments.rbegin()->getSize() + packDataSize);
|
fragments.rbegin()->setSize(fragments.rbegin()->getSize() + packDataSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes the first buffered key, including any fragments it was part of
|
||||||
|
void Track::removeFirstKey(){
|
||||||
|
HIGH_MSG("Erasing key %d:%lu", trackID, keys[0].getNumber());
|
||||||
|
//remove all parts of this key
|
||||||
|
for (int i = 0; i < keys[0].getParts(); i++) {
|
||||||
|
parts.pop_front();
|
||||||
|
}
|
||||||
|
//remove the key itself
|
||||||
|
keys.pop_front();
|
||||||
|
keySizes.pop_front();
|
||||||
|
//update firstms
|
||||||
|
firstms = keys[0].getTime();
|
||||||
|
//delete any fragments no longer fully buffered
|
||||||
|
while (fragments[0].getNumber() < keys[0].getNumber()) {
|
||||||
|
fragments.pop_front();
|
||||||
|
fragInsertTime.pop_front();
|
||||||
|
//and update the missed fragment counter
|
||||||
|
++missedFrags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of whole seconds since the first fragment was inserted into the buffer.
|
||||||
|
/// This assumes playback from the start of the buffer at time of insert, meaning that
|
||||||
|
/// the time is offset by that difference. E.g.: if a buffer is 50s long, the newest fragment
|
||||||
|
/// will have a value of 0 until 50s have passed, after which it will increase at a rate of
|
||||||
|
/// 1 per second.
|
||||||
|
uint32_t Track::secsSinceFirstFragmentInsert(){
|
||||||
|
uint32_t bs = Util::bootSecs();
|
||||||
|
if (bs > fragInsertTime.front()){
|
||||||
|
return bs - fragInsertTime.front();
|
||||||
|
}else{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Track::finalize(){
|
void Track::finalize(){
|
||||||
keys.rbegin()->setLength(lastms - keys.rbegin()->getTime() + parts.rbegin()->getDuration());
|
keys.rbegin()->setLength(lastms - keys.rbegin()->getTime() + parts.rbegin()->getDuration());
|
||||||
}
|
}
|
||||||
|
@ -1216,6 +1253,7 @@ namespace DTSC {
|
||||||
return keys[keyNum - keys[0].getNumber()];
|
return keys[keyNum - keys[0].getNumber()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of the key containing timestamp, or last key if nowhere.
|
||||||
unsigned int Track::timeToKeynum(unsigned int timestamp){
|
unsigned int Track::timeToKeynum(unsigned int timestamp){
|
||||||
unsigned int result = 0;
|
unsigned int result = 0;
|
||||||
for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++){
|
for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++){
|
||||||
|
@ -1227,13 +1265,12 @@ namespace DTSC {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets indice of the fragment containing timestamp, or last fragment if nowhere.
|
||||||
unsigned int Track::timeToFragnum(unsigned int timestamp){
|
unsigned int Track::timeToFragnum(unsigned int timestamp){
|
||||||
unsigned long long int totalTime = firstms;
|
|
||||||
for (unsigned int i = 0; i<fragments.size(); i++){
|
for (unsigned int i = 0; i<fragments.size(); i++){
|
||||||
if (timestamp <= totalTime){
|
if (timestamp <= getKey(fragments[i].getNumber()).getTime() + fragments[i].getDuration()){
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
totalTime += fragments[i].getDuration();
|
|
||||||
}
|
}
|
||||||
return fragments.size()-1;
|
return fragments.size()-1;
|
||||||
}
|
}
|
||||||
|
@ -1241,6 +1278,7 @@ namespace DTSC {
|
||||||
///\brief Resets a track, clears all meta values
|
///\brief Resets a track, clears all meta values
|
||||||
void Track::reset() {
|
void Track::reset() {
|
||||||
fragments.clear();
|
fragments.clear();
|
||||||
|
fragInsertTime.clear();
|
||||||
parts.clear();
|
parts.clear();
|
||||||
keySizes.clear();
|
keySizes.clear();
|
||||||
keys.clear();
|
keys.clear();
|
||||||
|
|
|
@ -207,18 +207,31 @@ namespace Mist {
|
||||||
liveMeta.post();
|
liveMeta.post();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Checks if removing a key from this track is allowed/safe, and if so, removes it.
|
||||||
|
///Returns true if a key was actually removed, false otherwise
|
||||||
|
///Aborts if any of the following conditions are true (while active):
|
||||||
|
/// * no keys present
|
||||||
|
/// * not at least 4 whole fragments present
|
||||||
|
/// * first fragment hasn't been at least lastms-firstms ms in buffer
|
||||||
|
/// * less than 8 times the biggest fragment duration is buffered
|
||||||
|
/// If a key was deleted and the first buffered data page is no longer used, it is deleted also.
|
||||||
bool inputBuffer::removeKey(unsigned int tid) {
|
bool inputBuffer::removeKey(unsigned int tid) {
|
||||||
DTSC::Track & Trk = myMeta.tracks[tid];
|
DTSC::Track & Trk = myMeta.tracks[tid];
|
||||||
//Make sure we have at least 3 whole fragments at all times,
|
//If this track is empty, abort
|
||||||
//unless we're shutting down the whole buffer right now
|
if (!Trk.keys.size()) {
|
||||||
if (Trk.fragments.size() < 5 && config->is_active) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//If we're shutting down, and this track is empty, abort
|
//the following checks only run if we're not shutting down
|
||||||
if (!myMeta.tracks[tid].keys.size()) {
|
if (config->is_active){
|
||||||
|
//Make sure we have at least 4 whole fragments at all times,
|
||||||
|
if (Trk.fragments.size() < 5) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (config->is_active && Trk.fragments.size() > 2){
|
//ensure we have each fragment buffered for at least the whole bufferTime
|
||||||
|
if (!Trk.secsSinceFirstFragmentInsert() || (Trk.lastms - Trk.firstms) < bufferTime){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Trk.fragments.size() > 2){
|
||||||
///Make sure we have at least 8X the target duration.
|
///Make sure we have at least 8X the target duration.
|
||||||
//The target duration is the biggest fragment, rounded up to whole seconds.
|
//The target duration is the biggest fragment, rounded up to whole seconds.
|
||||||
uint32_t targetDuration = (Trk.biggestFragment() / 1000 + 1) * 1000;
|
uint32_t targetDuration = (Trk.biggestFragment() / 1000 + 1) * 1000;
|
||||||
|
@ -230,25 +243,14 @@ namespace Mist {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HIGH_MSG("Erasing key %d:%lu", tid, myMeta.tracks[tid].keys[0].getNumber());
|
|
||||||
//remove all parts of this key
|
|
||||||
for (int i = 0; i < myMeta.tracks[tid].keys[0].getParts(); i++) {
|
|
||||||
myMeta.tracks[tid].parts.pop_front();
|
|
||||||
}
|
|
||||||
//remove the key itself
|
|
||||||
myMeta.tracks[tid].keys.pop_front();
|
|
||||||
myMeta.tracks[tid].keySizes.pop_front();
|
|
||||||
//re-calculate firstms
|
|
||||||
myMeta.tracks[tid].firstms = myMeta.tracks[tid].keys[0].getTime();
|
|
||||||
//delete the fragment if it's no longer fully buffered
|
|
||||||
if (myMeta.tracks[tid].fragments[0].getNumber() < myMeta.tracks[tid].keys[0].getNumber()) {
|
|
||||||
myMeta.tracks[tid].fragments.pop_front();
|
|
||||||
myMeta.tracks[tid].missedFrags ++;
|
|
||||||
}
|
}
|
||||||
|
//Alright, everything looks good, let's delete the key and possibly also fragment
|
||||||
|
Trk.removeFirstKey();
|
||||||
//if there is more than one page buffered for this track...
|
//if there is more than one page buffered for this track...
|
||||||
if (bufferLocations[tid].size() > 1) {
|
if (bufferLocations[tid].size() > 1) {
|
||||||
//Check if the first key starts on the second page or higher
|
//Check if the first key starts on the second page or higher
|
||||||
if (myMeta.tracks[tid].keys[0].getNumber() >= (++(bufferLocations[tid].begin()))->first || !config->is_active){
|
if (Trk.keys[0].getNumber() >= (++(bufferLocations[tid].begin()))->first || !config->is_active){
|
||||||
|
//If so, we can delete the first page entirely
|
||||||
HIGH_MSG("Erasing track %d, keys %lu-%lu from buffer", tid, bufferLocations[tid].begin()->first, bufferLocations[tid].begin()->first + bufferLocations[tid].begin()->second.keyNum - 1);
|
HIGH_MSG("Erasing track %d, keys %lu-%lu from buffer", tid, bufferLocations[tid].begin()->first, bufferLocations[tid].begin()->first + bufferLocations[tid].begin()->second.keyNum - 1);
|
||||||
bufferRemove(tid, bufferLocations[tid].begin()->first);
|
bufferRemove(tid, bufferLocations[tid].begin()->first);
|
||||||
|
|
||||||
|
@ -382,6 +384,7 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Buffer size management
|
//Buffer size management
|
||||||
|
/// \TODO Make sure data has been in the buffer for at least bufferTime after it goes in
|
||||||
while (it->second.keys.size() > 1 && (it->second.lastms - it->second.keys[1].getTime()) > bufferTime) {
|
while (it->second.keys.size() > 1 && (it->second.lastms - it->second.keys[1].getTime()) > bufferTime) {
|
||||||
if (!removeKey(it->first)) {
|
if (!removeKey(it->first)) {
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Reference in a new issue