/// \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