mistserver/lib/dtsc.h
2021-10-19 22:29:40 +02:00

475 lines
16 KiB
C++

/// \file dtsc.h
/// Holds all headers for DDVTECH Stream Container parsing/generation.
#pragma once
#include "json.h"
#include "socket.h"
#include "timing.h"
#include <deque>
#include <iostream>
#include <set>
#include <stdint.h> //for uint64_t
#include <stdio.h> //for FILE
#include <string>
#include <vector>
#define DTSC_INT 0x01
#define DTSC_STR 0x02
#define DTSC_OBJ 0xE0
#define DTSC_ARR 0x0A
#define DTSC_CON 0xFF
// Increase this value every time the DTSH file format changes in an incompatible way
// Changelog:
// Version 0-2: Undocumented changes
// Version 3: switched to bigMeta-style by default, Parts layout switched from 3/2/4 to 3/3/3 bytes
// Version 4: renamed bps to maxbps (peak bit rate) and added new value bps (average bit rate)
#define DTSH_VERSION 4
namespace DTSC{
///\brief This enum holds all possible datatypes for DTSC packets.
enum datatype{
AUDIO, ///< Stream Audio data
VIDEO, ///< Stream Video data
META, ///< Stream Metadata
PAUSEMARK, ///< Pause marker
MODIFIEDHEADER, ///< Modified header data.
INVALID ///< Anything else or no data available.
};
extern char Magic_Header[]; ///< The magic bytes for a DTSC header
extern char Magic_Packet[]; ///< The magic bytes for a DTSC packet
extern char Magic_Packet2[]; ///< The magic bytes for a DTSC packet version 2
extern char Magic_Command[]; ///< The magic bytes for a DTCM packet
///\brief A simple structure used for ordering byte seek positions.
struct seekPos{
///\brief Less-than comparison for seekPos structures.
///\param rhs The seekPos to compare with.
///\return Whether this object is smaller than rhs.
bool operator<(const seekPos &rhs) const{
if (seekTime < rhs.seekTime){
return true;
}else{
if (seekTime == rhs.seekTime){
if (trackID < rhs.trackID){return true;}
}
}
return false;
}
long long unsigned int seekTime; ///< Stores the timestamp of the DTSC packet referenced by this structure.
long long unsigned int bytePos; ///< Stores the byteposition of the DTSC packet referenced by this structure.
unsigned int trackID; ///< Stores the track the DTSC packet referenced by this structure is associated with.
};
enum packType{DTSC_INVALID, DTSC_HEAD, DTSC_V1, DTSC_V2, DTCM};
/// This class allows scanning through raw binary format DTSC data.
/// It can be used as an iterator or as a direct accessor.
class Scan{
public:
Scan();
Scan(char *pointer, size_t len);
operator bool() const;
std::string toPrettyString(size_t indent = 0) const;
bool hasMember(const std::string &indice) const;
bool hasMember(const char *indice, size_t ind_len) const;
Scan getMember(const std::string &indice) const;
Scan getMember(const char *indice) const;
Scan getMember(const char *indice, size_t ind_len) const;
void nullMember(const std::string &indice);
void nullMember(const char *indice, size_t ind_len);
Scan getIndice(size_t num) const;
std::string getIndiceName(size_t num) const;
size_t getSize() const;
char getType() const;
bool asBool() const;
int64_t asInt() const;
std::string asString() const;
void getString(char *&result, size_t &len) const;
JSON::Value asJSON() const;
private:
char *p;
size_t len;
};
/// DTSC::Packets can currently be three types:
/// DTSC_HEAD packets are the "DTSC" header string, followed by 4 bytes len and packed content.
/// DTSC_V1 packets are "DTPD", followed by 4 bytes len and packed content.
/// DTSC_V2 packets are "DTP2", followed by 4 bytes len, 4 bytes trackID, 8 bytes time, and packed
/// content. The len is always without the first 8 bytes counted.
class Packet{
public:
Packet();
Packet(const Packet &rhs);
Packet(const char *data_, unsigned int len, bool noCopy = false);
virtual ~Packet();
void null();
void operator=(const Packet &rhs);
operator bool() const;
packType getVersion() const;
void reInit(Socket::Connection &src);
void reInit(const char *data_, unsigned int len, bool noCopy = false);
void genericFill(long long packTime, long long packOffset, long long packTrack,
const char *packData, long long packDataSize, uint64_t packBytePos,
bool isKeyframe, int64_t bootMsOffset = 0);
void appendData(const char *appendData, uint32_t appendLen);
void getString(const char *identifier, char *&result, size_t &len) const;
void getString(const char *identifier, std::string &result) const;
void getInt(const char *identifier, uint64_t &result) const;
uint64_t getInt(const char *identifier) const;
void getFlag(const char *identifier, bool &result) const;
bool getFlag(const char *identifier) const;
bool hasMember(const char *identifier) const;
void appendNal(const char *appendData, uint32_t appendLen);
void upgradeNal(const char *appendData, uint32_t appendLen);
void setKeyFrame(bool kf);
virtual uint64_t getTime() const;
void setTime(uint64_t _time);
void nullMember(const std::string &memb);
size_t getTrackId() const;
char *getData() const;
size_t getDataLen() const;
size_t getPayloadLen() const;
size_t getDataStringLen();
size_t getDataStringLenOffset();
JSON::Value toJSON() const;
std::string toSummary() const;
Scan getScan() const;
Scan getScan();
protected:
bool master;
packType version;
void resize(size_t size);
char *data;
size_t bufferLen;
size_t dataLen;
uint64_t prevNalSize;
};
/// A child class of DTSC::Packet, which allows overriding the packet time efficiently.
class RetimedPacket : public Packet{
public:
RetimedPacket(uint64_t reTime){timeOverride = reTime;}
RetimedPacket(uint64_t reTime, const Packet &rhs) : Packet(rhs){timeOverride = reTime;}
RetimedPacket(uint64_t reTime, const char *data_, unsigned int len, bool noCopy = false)
: Packet(data_, len, noCopy){
timeOverride = reTime;
}
virtual uint64_t getTime() const{return timeOverride;}
protected:
uint64_t timeOverride;
};
/// A simple structure used for ordering byte seek positions.
struct livePos{
livePos(){
seekTime = 0;
trackID = 0;
}
livePos(const livePos &rhs){
seekTime = rhs.seekTime;
trackID = rhs.trackID;
}
void operator=(const livePos &rhs){
seekTime = rhs.seekTime;
trackID = rhs.trackID;
}
bool operator==(const livePos &rhs){
return seekTime == rhs.seekTime && trackID == rhs.trackID;
}
bool operator!=(const livePos &rhs){
return seekTime != rhs.seekTime || trackID != rhs.trackID;
}
bool operator<(const livePos &rhs) const{
if (seekTime < rhs.seekTime){
return true;
}else{
if (seekTime > rhs.seekTime){return false;}
}
return (trackID < rhs.trackID);
}
long long unsigned int seekTime;
unsigned int trackID;
};
/*LTS-START*/
///\brief Basic class supporting initialization Vectors.
///
/// These are used for encryption of data.
class Ivec{
public:
Ivec();
Ivec(long long int iVec);
void setIvec(long long int iVec);
void setIvec(std::string iVec);
void setIvec(const char *iVec, int len);
long long int asInt();
char *getData();
private:
///\brief Data storage for this initialization vector.
///
/// - 8 bytes: MSB storage of the initialization vector.
char data[8];
};
/*LTS-END*/
///\brief Basic class for storage of data associated with single DTSC packets, a.k.a. parts.
class Part{
public:
uint32_t getSize();
void setSize(uint32_t newSize);
uint32_t getDuration();
void setDuration(uint32_t newDuration);
uint32_t getOffset();
void setOffset(uint32_t newOffset);
char *getData();
void toPrettyString(std::ostream &str, int indent = 0);
private:
#define PACKED_PART_SIZE 9
///\brief Data storage for this Part.
///
/// - 3 bytes: MSB storage of the payload size of this packet in bytes.
/// - 3 bytes: MSB storage of the duration of this packet in milliseconds.
/// - 3 bytes: MSB storage of the presentation time offset of this packet in milliseconds.
char data[PACKED_PART_SIZE];
};
///\brief Basic class for storage of data associated with keyframes.
///
/// When deleting this object, make sure to remove all DTSC::Part associated with it, if any. If you fail doing this, it *will* cause data corruption.
class Key{
public:
unsigned long long getBpos();
void setBpos(unsigned long long newBpos);
unsigned long getLength();
void setLength(unsigned long newLength);
unsigned long getNumber();
void setNumber(unsigned long newNumber);
unsigned short getParts();
void setParts(unsigned short newParts);
unsigned long long getTime();
void setTime(unsigned long long newTime);
char *getData();
void toPrettyString(std::ostream &str, int indent = 0);
private:
#define PACKED_KEY_SIZE 25
///\brief Data storage for this Key.
///
/// - 8 bytes: MSB storage of the position of the first packet of this keyframe within the file.
/// - 3 bytes: MSB storage of the duration of this keyframe.
/// - 4 bytes: MSB storage of the number of this keyframe.
/// - 2 bytes: MSB storage of the amount of parts in this keyframe.
/// - 8 bytes: MSB storage of the timestamp associated with this keyframe's first packet.
char data[PACKED_KEY_SIZE];
};
///\brief Basic class for storage of data associated with fragments.
class Fragment{
public:
unsigned long getDuration();
void setDuration(unsigned long newDuration);
char getLength();
void setLength(char newLength);
unsigned long getNumber();
void setNumber(unsigned long newNumber);
unsigned long getSize();
void setSize(unsigned long newSize);
char *getData();
void toPrettyString(std::ostream &str, int indent = 0);
private:
#define PACKED_FRAGMENT_SIZE 13
///\brief Data storage for this Fragment.
///
/// - 4 bytes: duration (in milliseconds)
/// - 1 byte: length (amount of keyframes)
/// - 4 bytes: number of first keyframe in fragment
/// - 4 bytes: size of fragment in bytes
char data[PACKED_FRAGMENT_SIZE];
};
///\brief Class for storage of track data
class Track{
public:
Track();
Track(JSON::Value &trackRef);
Track(Scan &trackRef);
void clearParts();
inline operator bool() const{
return (parts.size() && keySizes.size() && (keySizes.size() == keys.size()));
}
/*
void update(long long packTime, long long packOffset, long long packDataSize, uint64_t
packBytePos, bool isKeyframe, long long packSendSize, unsigned long segment_size = 1900);
*/
void update(long long packTime, long long packOffset, long long packDataSize,
uint64_t packBytePos, bool isKeyframe, long long packSendSize,
unsigned long segment_size = 1900, const char *iVec = 0);
int getSendLen(bool skipDynamic = false);
void send(Socket::Connection &conn, bool skipDynamic = false);
void writeTo(char *&p);
JSON::Value toJSON(bool skipDynamic = false);
std::deque<Fragment> fragments;
std::deque<Key> keys;
std::deque<unsigned long> keySizes;
std::deque<Part> parts;
std::deque<Ivec> ivecs; /*LTS*/
Key &getKey(unsigned int keyNum);
Fragment &getFrag(unsigned int fragNum);
unsigned int timeToKeynum(unsigned int timestamp);
uint32_t timeToFragnum(uint64_t timestamp);
void reset();
void toPrettyString(std::ostream &str, int indent = 0, int verbosity = 0);
void finalize();
uint32_t biggestFragment();
std::string getIdentifier();
std::string getWritableIdentifier();
unsigned int trackID;
uint64_t firstms;
uint64_t lastms;
int bps;
int max_bps;
int missedFrags;
std::string init;
std::string codec;
std::string type;
std::string lang; ///< ISO 639-2 Language of track, empty or und if unknown.
uint32_t minKeepAway; ///< Time in MS to never seek closer than live point to
// audio only
int rate;
int size;
int channels;
// video only
int width;
int height;
int fpks;
void removeFirstKey();
uint32_t secsSinceFirstFragmentInsert();
private:
std::string cachedIdent;
std::deque<uint32_t> fragInsertTime;
};
///\brief Class for storage of meta data
class Meta{
/// \todo Make toJSON().toNetpacked() shorter
public:
Meta();
Meta(const DTSC::Packet &source);
Meta(JSON::Value &meta);
bool nextIsKey;
inline operator bool() const{// returns if the object contains valid meta data BY LOOKING AT vod/live FLAGS
return vod || live;
}
void reinit(const DTSC::Packet &source);
void update(const DTSC::Packet &pack, unsigned long segment_size = 1900);
void updatePosOverride(DTSC::Packet &pack, uint64_t bpos);
void update(JSON::Value &pack, unsigned long segment_size = 1900);
/*LTS
void update(long long packTime, long long packOffset, long long packTrack, long long
packDataSize, uint64_t packBytePos, bool isKeyframe, long long packSendSize = 0, unsigned long
segment_size = 1900); LTS*/
void update(long long packTime, long long packOffset, long long packTrack,
long long packDataSize, uint64_t packBytePos, bool isKeyframe,
long long packSendSize = 0, unsigned long segment_size = 1900, const char *iVec = 0);
unsigned int getSendLen(bool skipDynamic = false,
std::set<unsigned long> selectedTracks = std::set<unsigned long>());
void send(Socket::Connection &conn, bool skipDynamic = false,
std::set<unsigned long> selectedTracks = std::set<unsigned long>());
void writeTo(char *p);
JSON::Value toJSON();
void reset();
bool toFile(const std::string &fileName);
void toPrettyString(std::ostream &str, int indent = 0, int verbosity = 0);
// members:
std::map<unsigned int, Track> tracks;
Track &mainTrack();
uint32_t biggestFragment();
bool vod;
bool live;
bool merged;
uint16_t version;
int64_t moreheader;
int64_t bufferWindow;
int64_t bootMsOffset; ///< Millis to add to packet timestamps to get millis since system boot.
std::string sourceURI;
JSON::Value inputLocalVars;
};
/// An iterator helper for easily iterating over the parts in a Fragment.
class PartIter{
public:
PartIter(Track &Trk, Fragment &frag);
Part &operator*() const; ///< Dereferences into a Value reference.
Part *operator->() const; ///< Dereferences into a Value reference.
operator bool() const; ///< True if not done iterating.
PartIter &operator++(); ///< Go to next iteration.
private:
uint32_t lastKey;
uint32_t currInKey;
Track *tRef;
std::deque<Part>::iterator pIt;
std::deque<Key>::iterator kIt;
};
/// A simple wrapper class that will open a file and allow easy reading/writing of DTSC data from/to it.
class File{
public:
File();
File(const File &rhs);
File(std::string filename, bool create = false);
File &operator=(const File &rhs);
operator bool() const;
~File();
Meta &getMeta();
long long int getLastReadPos();
bool writeHeader(std::string &header, bool force = false);
long long int addHeader(std::string &header);
long int getBytePosEOF();
long int getBytePos();
bool reachedEOF();
void seekNext();
void parseNext();
DTSC::Packet &getPacket();
bool seek_time(unsigned int ms);
bool seek_time(unsigned int ms, unsigned int trackNo, bool forceSeek = false);
bool seek_bpos(int bpos);
void rewritePacket(std::string &newPacket, int bytePos);
void writePacket(std::string &newPacket);
void writePacket(JSON::Value &newPacket);
bool atKeyframe();
void selectTracks(std::set<unsigned long> &tracks);
private:
long int endPos;
void readHeader(int pos);
DTSC::Packet myPack;
Meta metadata;
std::map<unsigned int, std::string> trackMapping;
long long int currtime;
long long int lastreadpos;
int currframe;
FILE *F;
unsigned long headerSize;
void *buffer;
bool created;
std::set<seekPos> currentPositions;
std::set<unsigned long> selectedTracks;
};
// FileWriter
}// namespace DTSC