| #ifndef MKVPARSER_HPP |
| #define MKVPARSER_HPP |
| |
| #include <cstdlib> |
| #include <cstdio> |
| |
| namespace mkvparser |
| { |
| |
| const int E_FILE_FORMAT_INVALID = -2; |
| const int E_BUFFER_NOT_FULL = -3; |
| |
| class IMkvReader |
| { |
| public: |
| virtual int Read(long long position, long length, unsigned char* buffer) = 0; |
| virtual int Length(long long* total, long long* available) = 0; |
| protected: |
| virtual ~IMkvReader(); |
| }; |
| |
| long long GetUIntLength(IMkvReader*, long long, long&); |
| long long ReadUInt(IMkvReader*, long long, long&); |
| long long SyncReadUInt(IMkvReader*, long long pos, long long stop, long&); |
| long long UnserializeUInt(IMkvReader*, long long pos, long long size); |
| float Unserialize4Float(IMkvReader*, long long); |
| double Unserialize8Double(IMkvReader*, long long); |
| short Unserialize2SInt(IMkvReader*, long long); |
| signed char Unserialize1SInt(IMkvReader*, long long); |
| bool Match(IMkvReader*, long long&, unsigned long, long long&); |
| bool Match(IMkvReader*, long long&, unsigned long, char*&); |
| bool Match(IMkvReader*, long long&, unsigned long,unsigned char*&, |
| size_t *optionalSize = NULL); |
| bool Match(IMkvReader*, long long&, unsigned long, double&); |
| bool Match(IMkvReader*, long long&, unsigned long, short&); |
| |
| |
| struct EBMLHeader |
| { |
| EBMLHeader(); |
| ~EBMLHeader(); |
| long long m_version; |
| long long m_readVersion; |
| long long m_maxIdLength; |
| long long m_maxSizeLength; |
| char* m_docType; |
| long long m_docTypeVersion; |
| long long m_docTypeReadVersion; |
| |
| long long Parse(IMkvReader*, long long&); |
| }; |
| |
| |
| class Segment; |
| class Track; |
| class Cluster; |
| |
| class Block |
| { |
| Block(const Block&); |
| Block& operator=(const Block&); |
| |
| public: |
| const long long m_start; |
| const long long m_size; |
| |
| Block(long long start, long long size, IMkvReader*); |
| |
| unsigned long GetTrackNumber() const; |
| |
| long long GetTimeCode(Cluster*) const; //absolute, but not scaled |
| long long GetTime(Cluster*) const; //absolute, and scaled (nanosecond units) |
| bool IsKey() const; |
| void SetKey(bool); |
| |
| long GetSize() const; |
| long Read(IMkvReader*, unsigned char*) const; |
| |
| private: |
| long long m_track; //Track::Number() |
| short m_timecode; //relative to cluster |
| unsigned char m_flags; |
| long long m_frameOff; |
| long m_frameSize; |
| |
| }; |
| |
| |
| class BlockEntry |
| { |
| BlockEntry(const BlockEntry&); |
| BlockEntry& operator=(const BlockEntry&); |
| |
| public: |
| virtual ~BlockEntry(); |
| virtual bool EOS() const = 0; |
| virtual Cluster* GetCluster() const = 0; |
| virtual size_t GetIndex() const = 0; |
| virtual const Block* GetBlock() const = 0; |
| virtual bool IsBFrame() const = 0; |
| |
| protected: |
| BlockEntry(); |
| |
| }; |
| |
| |
| class SimpleBlock : public BlockEntry |
| { |
| SimpleBlock(const SimpleBlock&); |
| SimpleBlock& operator=(const SimpleBlock&); |
| |
| public: |
| SimpleBlock(Cluster*, size_t, long long start, long long size); |
| |
| bool EOS() const; |
| Cluster* GetCluster() const; |
| size_t GetIndex() const; |
| const Block* GetBlock() const; |
| bool IsBFrame() const; |
| |
| protected: |
| Cluster* const m_pCluster; |
| const size_t m_index; |
| Block m_block; |
| |
| }; |
| |
| |
| class BlockGroup : public BlockEntry |
| { |
| BlockGroup(const BlockGroup&); |
| BlockGroup& operator=(const BlockGroup&); |
| |
| public: |
| BlockGroup(Cluster*, size_t, long long, long long); |
| ~BlockGroup(); |
| |
| bool EOS() const; |
| Cluster* GetCluster() const; |
| size_t GetIndex() const; |
| const Block* GetBlock() const; |
| bool IsBFrame() const; |
| |
| short GetPrevTimeCode() const; //relative to block's time |
| short GetNextTimeCode() const; //as above |
| |
| protected: |
| Cluster* const m_pCluster; |
| const size_t m_index; |
| |
| private: |
| BlockGroup(Cluster*, size_t, unsigned long); |
| void ParseBlock(long long start, long long size); |
| |
| short m_prevTimeCode; |
| short m_nextTimeCode; |
| |
| //TODO: the Matroska spec says you can have multiple blocks within the |
| //same block group, with blocks ranked by priority (the flag bits). |
| //For now we just cache a single block. |
| #if 0 |
| typedef std::deque<Block*> blocks_t; |
| blocks_t m_blocks; //In practice should contain only a single element. |
| #else |
| Block* m_pBlock; |
| #endif |
| |
| }; |
| |
| |
| class Track |
| { |
| Track(const Track&); |
| Track& operator=(const Track&); |
| |
| public: |
| Segment* const m_pSegment; |
| virtual ~Track(); |
| |
| long long GetType() const; |
| unsigned long GetNumber() const; |
| const char* GetNameAsUTF8() const; |
| const char* GetCodecNameAsUTF8() const; |
| const char* GetCodecId() const; |
| const unsigned char* GetCodecPrivate( |
| size_t *optionalSize = NULL) const; |
| |
| const BlockEntry* GetEOS() const; |
| |
| struct Settings |
| { |
| long long start; |
| long long size; |
| }; |
| |
| struct Info |
| { |
| long long type; |
| long long number; |
| long long uid; |
| char* nameAsUTF8; |
| char* codecId; |
| unsigned char* codecPrivate; |
| size_t codecPrivateSize; |
| char* codecNameAsUTF8; |
| Settings settings; |
| Info(); |
| void Clear(); |
| }; |
| |
| long GetFirst(const BlockEntry*&) const; |
| long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const; |
| virtual bool VetEntry(const BlockEntry*) const = 0; |
| |
| protected: |
| Track(Segment*, const Info&); |
| const Info m_info; |
| |
| class EOSBlock : public BlockEntry |
| { |
| public: |
| EOSBlock(); |
| |
| bool EOS() const; |
| Cluster* GetCluster() const; |
| size_t GetIndex() const; |
| const Block* GetBlock() const; |
| bool IsBFrame() const; |
| }; |
| |
| EOSBlock m_eos; |
| |
| }; |
| |
| |
| class VideoTrack : public Track |
| { |
| VideoTrack(const VideoTrack&); |
| VideoTrack& operator=(const VideoTrack&); |
| |
| public: |
| VideoTrack(Segment*, const Info&); |
| long long GetWidth() const; |
| long long GetHeight() const; |
| double GetFrameRate() const; |
| |
| bool VetEntry(const BlockEntry*) const; |
| |
| private: |
| long long m_width; |
| long long m_height; |
| double m_rate; |
| |
| }; |
| |
| |
| class AudioTrack : public Track |
| { |
| AudioTrack(const AudioTrack&); |
| AudioTrack& operator=(const AudioTrack&); |
| |
| public: |
| AudioTrack(Segment*, const Info&); |
| double GetSamplingRate() const; |
| long long GetChannels() const; |
| long long GetBitDepth() const; |
| bool VetEntry(const BlockEntry*) const; |
| |
| private: |
| double m_rate; |
| long long m_channels; |
| long long m_bitDepth; |
| }; |
| |
| |
| class Tracks |
| { |
| Tracks(const Tracks&); |
| Tracks& operator=(const Tracks&); |
| |
| public: |
| Segment* const m_pSegment; |
| const long long m_start; |
| const long long m_size; |
| |
| Tracks(Segment*, long long start, long long size); |
| virtual ~Tracks(); |
| |
| Track* GetTrackByNumber(unsigned long tn) const; |
| Track* GetTrackByIndex(unsigned long idx) const; |
| |
| private: |
| Track** m_trackEntries; |
| Track** m_trackEntriesEnd; |
| |
| void ParseTrackEntry(long long, long long, Track*&); |
| |
| public: |
| unsigned long GetTracksCount() const; |
| }; |
| |
| |
| class SegmentInfo |
| { |
| SegmentInfo(const SegmentInfo&); |
| SegmentInfo& operator=(const SegmentInfo&); |
| |
| public: |
| Segment* const m_pSegment; |
| const long long m_start; |
| const long long m_size; |
| |
| SegmentInfo(Segment*, long long start, long long size); |
| ~SegmentInfo(); |
| long long GetTimeCodeScale() const; |
| long long GetDuration() const; //scaled |
| const char* GetMuxingAppAsUTF8() const; |
| const char* GetWritingAppAsUTF8() const; |
| const char* GetTitleAsUTF8() const; |
| |
| private: |
| long long m_timecodeScale; |
| double m_duration; |
| char* m_pMuxingAppAsUTF8; |
| char* m_pWritingAppAsUTF8; |
| char* m_pTitleAsUTF8; |
| }; |
| |
| |
| class Cluster |
| { |
| Cluster(const Cluster&); |
| Cluster& operator=(const Cluster&); |
| |
| public: |
| Segment* const m_pSegment; |
| const size_t m_index; |
| |
| public: |
| static Cluster* Parse(Segment*, size_t, long long off); |
| |
| Cluster(); //EndOfStream |
| ~Cluster(); |
| |
| bool EOS() const; |
| |
| long long GetTimeCode(); //absolute, but not scaled |
| long long GetTime(); //absolute, and scaled (nanosecond units) |
| |
| const BlockEntry* GetFirst(); |
| const BlockEntry* GetLast(); |
| const BlockEntry* GetNext(const BlockEntry*) const; |
| const BlockEntry* GetEntry(const Track*); |
| protected: |
| Cluster(Segment*, size_t, long long off); |
| |
| private: |
| long long m_start; |
| long long m_size; |
| long long m_timecode; |
| BlockEntry** m_pEntries; |
| size_t m_entriesCount; |
| |
| void Load(); |
| void LoadBlockEntries(); |
| void ParseBlockGroup(long long, long long, size_t); |
| void ParseSimpleBlock(long long, long long, size_t); |
| |
| }; |
| |
| |
| class Segment |
| { |
| Segment(const Segment&); |
| Segment& operator=(const Segment&); |
| |
| private: |
| Segment(IMkvReader*, long long pos, long long size); |
| |
| public: |
| IMkvReader* const m_pReader; |
| const long long m_start; //posn of segment payload |
| const long long m_size; //size of segment payload |
| Cluster m_eos; //TODO: make private? |
| |
| static long long CreateInstance(IMkvReader*, long long, Segment*&); |
| ~Segment(); |
| |
| //for big-bang loading (source filter) |
| long Load(); |
| |
| //for incremental loading (splitter) |
| long long Unparsed() const; |
| long long ParseHeaders(); |
| long ParseCluster(Cluster*&, long long& newpos) const; |
| bool AddCluster(Cluster*, long long); |
| |
| Tracks* GetTracks() const; |
| const SegmentInfo* const GetInfo() const; |
| long long GetDuration() const; |
| |
| //NOTE: this turned out to be too inefficient. |
| //long long Load(long long time_nanoseconds); |
| |
| Cluster* GetFirst(); |
| Cluster* GetLast(); |
| unsigned long GetCount() const; |
| |
| Cluster* GetNext(const Cluster*); |
| Cluster* GetCluster(long long time_nanoseconds); |
| |
| private: |
| long long m_pos; //absolute file posn; what has been consumed so far |
| SegmentInfo* m_pInfo; |
| Tracks* m_pTracks; |
| Cluster** m_clusters; |
| size_t m_clusterCount; |
| |
| void ParseSeekHead(long long pos, long long size, size_t*); |
| void ParseSeekEntry(long long pos, long long size, size_t*); |
| void ParseSecondarySeekHead(long long off, size_t*); |
| }; |
| |
| |
| } //end namespace mkvparser |
| |
| #endif //MKVPARSER_HPP |