159 lines
5.5 KiB
C++
159 lines
5.5 KiB
C++
#pragma once
|
|
#include "input.h"
|
|
#include <mist/util.h>
|
|
|
|
namespace Mist{
|
|
|
|
#define PKT_COUNT 24
|
|
|
|
class packetData{
|
|
public:
|
|
uint64_t time, offset, track, dsize, bpos;
|
|
bool key;
|
|
Util::ResizeablePointer ptr;
|
|
packetData() : time(0), offset(0), track(0), dsize(0), bpos(0), key(false){}
|
|
void set(uint64_t packTime, uint64_t packOffset, uint64_t packTrack, uint64_t packDataSize,
|
|
uint64_t packBytePos, bool isKeyframe, void *dataPtr = 0){
|
|
time = packTime;
|
|
offset = packOffset;
|
|
track = packTrack;
|
|
dsize = packDataSize;
|
|
bpos = packBytePos;
|
|
key = isKeyframe;
|
|
if (dataPtr){ptr.assign(dataPtr, packDataSize);}
|
|
}
|
|
packetData(uint64_t packTime, uint64_t packOffset, uint64_t packTrack, uint64_t packDataSize,
|
|
uint64_t packBytePos, bool isKeyframe, void *dataPtr = 0){
|
|
set(packTime, packOffset, packTrack, packDataSize, packBytePos, isKeyframe, dataPtr);
|
|
}
|
|
};
|
|
class trackPredictor{
|
|
public:
|
|
packetData pkts[PKT_COUNT]; /// Buffer for packet data
|
|
uint64_t times[PKT_COUNT]; /// Sorted timestamps of buffered packets
|
|
size_t maxDelay; /// Maximum amount of bframes we expect
|
|
uint32_t timeOffset; /// Milliseconds we need to subtract from times so that offsets are always > 0
|
|
uint64_t ctr; /// ingested frame count
|
|
uint64_t rem; /// removed frame count
|
|
bool initialized;
|
|
trackPredictor(){
|
|
initialized = false;
|
|
maxDelay = 0;
|
|
timeOffset = 0;
|
|
flush();
|
|
}
|
|
bool hasPackets(bool finished = false){
|
|
if (finished){
|
|
return (ctr - rem > 0);
|
|
}else{
|
|
return ((initialized || ctr > 16) && ctr - rem > maxDelay);
|
|
}
|
|
}
|
|
/// Clears all internal values, for reuse as-new.
|
|
void flush(){
|
|
ctr = 0;
|
|
rem = 0;
|
|
}
|
|
packetData &getPacketData(bool mustCalcOffsets){
|
|
// grab the next packet to output
|
|
packetData &p = pkts[rem % PKT_COUNT];
|
|
if (!mustCalcOffsets || !maxDelay){
|
|
return p;
|
|
}
|
|
//Calculate the timeOffset when extracting the first frame
|
|
if (!initialized){
|
|
size_t buffLen = (ctr-rem-1) % PKT_COUNT;
|
|
for (size_t i = 0; i <= buffLen; ++i){
|
|
if (pkts[i].time < times[i]){
|
|
if (times[i] - pkts[i].time > timeOffset){
|
|
timeOffset = times[i] - pkts[i].time;
|
|
}
|
|
}
|
|
DONTEVEN_MSG("Checking time offset against entry %zu/%zu: %" PRIu64 "-%" PRIu64 " = %" PRIu32, i, buffLen, times[i], pkts[i].time, timeOffset);
|
|
}
|
|
MEDIUM_MSG("timeOffset calculated to be %" PRIu32 ", max frame delay %zu", timeOffset, maxDelay);
|
|
initialized = true;
|
|
}
|
|
|
|
uint64_t origTime = p.time;
|
|
//Set new timestamp to first time in sorted array
|
|
p.time = times[0];
|
|
//Subtract timeOffset if possible
|
|
if (p.time >= timeOffset){p.time -= timeOffset;}
|
|
//If possible, calculate offset based on original timestamp difference with new timestamp
|
|
if (origTime > p.time){p.offset = origTime-p.time;}
|
|
//Less than 3 milliseconds off? Assume we needed 0 and it's a rounding error in timestamps.
|
|
if (p.offset < 3){p.offset = 0;}
|
|
DONTEVEN_MSG("Outputting%s %" PRIu64 "+%" PRIu64 " (#%" PRIu64 "), display at %" PRIu64,
|
|
(p.key ? " KEY" : ""), p.time, p.offset, rem, p.time + p.offset);
|
|
return p;
|
|
}
|
|
|
|
void add(uint64_t packTime, uint64_t packTrack, uint64_t packDataSize,
|
|
uint64_t packBytePos, bool isKeyframe, bool isVideo, void *dataPtr = 0){
|
|
pkts[ctr % PKT_COUNT].set(packTime, 0, packTrack, packDataSize, packBytePos, isKeyframe, dataPtr);
|
|
++ctr;
|
|
if (!isVideo){return;}
|
|
size_t buffLen = ctr-rem-1;
|
|
//Just in case somebody messed up, ensure we don't go out of our PKT_COUNT sized array
|
|
if (buffLen >= PKT_COUNT){buffLen = PKT_COUNT - 1;}
|
|
times[buffLen] = packTime;
|
|
if (buffLen){
|
|
//Swap the times while the previous is higher than the current
|
|
size_t i = buffLen;
|
|
while (i && times[i] < times[i-1]){
|
|
uint64_t tmp = times[i-1];
|
|
times[i-1] = times[i];
|
|
times[i] = tmp;
|
|
--i;
|
|
//Keep track of maximum delay
|
|
if (!initialized && buffLen - i + 1 > maxDelay){
|
|
maxDelay = buffLen - i + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void remove(){
|
|
++rem;
|
|
size_t buffLen = ctr-rem;
|
|
if (buffLen >= PKT_COUNT){buffLen = PKT_COUNT-1;}
|
|
for (size_t i = 0; i < buffLen; ++i){times[i] = times[i+1];}
|
|
}
|
|
};
|
|
|
|
class InputEBML : public Input{
|
|
public:
|
|
InputEBML(Util::Config *cfg);
|
|
bool needsLock();
|
|
|
|
protected:
|
|
virtual size_t streamByteCount(){
|
|
return totalBytes;
|
|
}; // For live streams: to update the stats with correct values.
|
|
void fillPacket(packetData &C);
|
|
bool checkArguments();
|
|
bool preRun();
|
|
bool readHeader();
|
|
bool readElement();
|
|
void getNext(size_t idx = INVALID_TRACK_ID);
|
|
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);
|
|
void clearPredictors();
|
|
FILE *inFile;
|
|
Util::ResizeablePointer ptr;
|
|
bool readingMinimal;
|
|
uint64_t lastClusterBPos;
|
|
uint64_t lastClusterTime;
|
|
uint64_t bufferedPacks;
|
|
std::map<uint64_t, trackPredictor> packBuf;
|
|
std::set<uint64_t> swapEndianness;
|
|
bool readExistingHeader();
|
|
void parseStreamHeader(){readHeader();}
|
|
bool openStreamSource(){return true;}
|
|
bool needHeader(){return needsLock() && !readExistingHeader();}
|
|
double timeScale;
|
|
bool wantBlocks;
|
|
size_t totalBytes;
|
|
};
|
|
}// namespace Mist
|
|
|
|
typedef Mist::InputEBML mistIn;
|