ref: 3cbc26f31b82a045d44ba2fde1ce54a3d8e39cd6
dir: /third_party/libwebm/mkvparser.hpp/
// Copyright (c) 2012 The WebM project authors. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the LICENSE file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. #ifndef MKVPARSER_HPP #define MKVPARSER_HPP #include <cstddef> #include <cstdio> #include <cstdlib> namespace mkvparser { const int E_PARSE_FAILED = -1; const int E_FILE_FORMAT_INVALID = -2; const int E_BUFFER_NOT_FULL = -3; class IMkvReader { public: virtual int Read(long long pos, long len, unsigned char* buf) = 0; virtual int Length(long long* total, long long* available) = 0; protected: virtual ~IMkvReader(); }; template<typename Type> Type* SafeArrayAlloc(unsigned long long num_elements, unsigned long long element_size); long long GetUIntLength(IMkvReader*, long long, long&); long long ReadUInt(IMkvReader*, long long, long&); long long ReadID(IMkvReader* pReader, long long pos, long& len); long long UnserializeUInt(IMkvReader*, long long pos, long long size); long UnserializeFloat(IMkvReader*, long long pos, long long size, double&); long UnserializeInt(IMkvReader*, long long pos, long long size, long long& result); long UnserializeString(IMkvReader*, long long pos, long long size, char*& str); long ParseElementHeader(IMkvReader* pReader, long long& pos, // consume id and size fields long long stop, // if you know size of element's parent long long& id, long long& size); bool Match(IMkvReader*, long long&, unsigned long, long long&); bool Match(IMkvReader*, long long&, unsigned long, unsigned char*&, size_t&); void GetVersion(int& major, int& minor, int& build, int& revision); 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&); void Init(); }; 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, long long discard_padding); ~Block(); long Parse(const Cluster*); long long GetTrackNumber() const; long long GetTimeCode(const Cluster*) const; // absolute, but not scaled long long GetTime(const Cluster*) const; // absolute, and scaled (ns) bool IsKey() const; void SetKey(bool); bool IsInvisible() const; enum Lacing { kLacingNone, kLacingXiph, kLacingFixed, kLacingEbml }; Lacing GetLacing() const; int GetFrameCount() const; // to index frames: [0, count) struct Frame { long long pos; // absolute offset long len; long Read(IMkvReader*, unsigned char*) const; }; const Frame& GetFrame(int frame_index) const; long long GetDiscardPadding() const; private: long long m_track; // Track::Number() short m_timecode; // relative to cluster unsigned char m_flags; Frame* m_frames; int m_frame_count; protected: const long long m_discard_padding; }; class BlockEntry { BlockEntry(const BlockEntry&); BlockEntry& operator=(const BlockEntry&); protected: BlockEntry(Cluster*, long index); public: virtual ~BlockEntry(); bool EOS() const; const Cluster* GetCluster() const; long GetIndex() const; virtual const Block* GetBlock() const = 0; enum Kind { kBlockEOS, kBlockSimple, kBlockGroup }; virtual Kind GetKind() const = 0; protected: Cluster* const m_pCluster; const long m_index; }; class SimpleBlock : public BlockEntry { SimpleBlock(const SimpleBlock&); SimpleBlock& operator=(const SimpleBlock&); public: SimpleBlock(Cluster*, long index, long long start, long long size); long Parse(); Kind GetKind() const; const Block* GetBlock() const; protected: Block m_block; }; class BlockGroup : public BlockEntry { BlockGroup(const BlockGroup&); BlockGroup& operator=(const BlockGroup&); public: BlockGroup(Cluster*, long index, long long block_start, // absolute pos of block's payload long long block_size, // size of block's payload long long prev, long long next, long long duration, long long discard_padding); long Parse(); Kind GetKind() const; const Block* GetBlock() const; long long GetPrevTimeCode() const; // relative to block's time long long GetNextTimeCode() const; // as above long long GetDurationTimeCode() const; private: Block m_block; const long long m_prev; const long long m_next; const long long m_duration; }; /////////////////////////////////////////////////////////////// // ContentEncoding element // Elements used to describe if the track data has been encrypted or // compressed with zlib or header stripping. class ContentEncoding { public: enum { kCTR = 1 }; ContentEncoding(); ~ContentEncoding(); // ContentCompression element names struct ContentCompression { ContentCompression(); ~ContentCompression(); unsigned long long algo; unsigned char* settings; long long settings_len; }; // ContentEncAESSettings element names struct ContentEncAESSettings { ContentEncAESSettings() : cipher_mode(kCTR) {} ~ContentEncAESSettings() {} unsigned long long cipher_mode; }; // ContentEncryption element names struct ContentEncryption { ContentEncryption(); ~ContentEncryption(); unsigned long long algo; unsigned char* key_id; long long key_id_len; unsigned char* signature; long long signature_len; unsigned char* sig_key_id; long long sig_key_id_len; unsigned long long sig_algo; unsigned long long sig_hash_algo; ContentEncAESSettings aes_settings; }; // Returns ContentCompression represented by |idx|. Returns NULL if |idx| // is out of bounds. const ContentCompression* GetCompressionByIndex(unsigned long idx) const; // Returns number of ContentCompression elements in this ContentEncoding // element. unsigned long GetCompressionCount() const; // Parses the ContentCompression element from |pReader|. |start| is the // starting offset of the ContentCompression payload. |size| is the size in // bytes of the ContentCompression payload. |compression| is where the parsed // values will be stored. long ParseCompressionEntry(long long start, long long size, IMkvReader* pReader, ContentCompression* compression); // Returns ContentEncryption represented by |idx|. Returns NULL if |idx| // is out of bounds. const ContentEncryption* GetEncryptionByIndex(unsigned long idx) const; // Returns number of ContentEncryption elements in this ContentEncoding // element. unsigned long GetEncryptionCount() const; // Parses the ContentEncAESSettings element from |pReader|. |start| is the // starting offset of the ContentEncAESSettings payload. |size| is the // size in bytes of the ContentEncAESSettings payload. |encryption| is // where the parsed values will be stored. long ParseContentEncAESSettingsEntry(long long start, long long size, IMkvReader* pReader, ContentEncAESSettings* aes); // Parses the ContentEncoding element from |pReader|. |start| is the // starting offset of the ContentEncoding payload. |size| is the size in // bytes of the ContentEncoding payload. Returns true on success. long ParseContentEncodingEntry(long long start, long long size, IMkvReader* pReader); // Parses the ContentEncryption element from |pReader|. |start| is the // starting offset of the ContentEncryption payload. |size| is the size in // bytes of the ContentEncryption payload. |encryption| is where the parsed // values will be stored. long ParseEncryptionEntry(long long start, long long size, IMkvReader* pReader, ContentEncryption* encryption); unsigned long long encoding_order() const { return encoding_order_; } unsigned long long encoding_scope() const { return encoding_scope_; } unsigned long long encoding_type() const { return encoding_type_; } private: // Member variables for list of ContentCompression elements. ContentCompression** compression_entries_; ContentCompression** compression_entries_end_; // Member variables for list of ContentEncryption elements. ContentEncryption** encryption_entries_; ContentEncryption** encryption_entries_end_; // ContentEncoding element names unsigned long long encoding_order_; unsigned long long encoding_scope_; unsigned long long encoding_type_; // LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding); ContentEncoding(const ContentEncoding&); ContentEncoding& operator=(const ContentEncoding&); }; class Track { Track(const Track&); Track& operator=(const Track&); public: class Info; static long Create(Segment*, const Info&, long long element_start, long long element_size, Track*&); enum Type { kVideo = 1, kAudio = 2, kSubtitle = 0x11, kMetadata = 0x21 }; Segment* const m_pSegment; const long long m_element_start; const long long m_element_size; virtual ~Track(); long GetType() const; long GetNumber() const; unsigned long long GetUid() const; const char* GetNameAsUTF8() const; const char* GetLanguage() const; const char* GetCodecNameAsUTF8() const; const char* GetCodecId() const; const unsigned char* GetCodecPrivate(size_t&) const; bool GetLacing() const; unsigned long long GetDefaultDuration() const; unsigned long long GetCodecDelay() const; unsigned long long GetSeekPreRoll() const; const BlockEntry* GetEOS() const; struct Settings { long long start; long long size; }; class Info { public: Info(); ~Info(); int Copy(Info&) const; void Clear(); long type; long number; unsigned long long uid; unsigned long long defaultDuration; unsigned long long codecDelay; unsigned long long seekPreRoll; char* nameAsUTF8; char* language; char* codecId; char* codecNameAsUTF8; unsigned char* codecPrivate; size_t codecPrivateSize; bool lacing; Settings settings; private: Info(const Info&); Info& operator=(const Info&); int CopyStr(char* Info::*str, Info&) const; }; long GetFirst(const BlockEntry*&) const; long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const; virtual bool VetEntry(const BlockEntry*) const; virtual long Seek(long long time_ns, const BlockEntry*&) const; const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const; unsigned long GetContentEncodingCount() const; long ParseContentEncodingsEntry(long long start, long long size); protected: Track(Segment*, long long element_start, long long element_size); Info m_info; class EOSBlock : public BlockEntry { public: EOSBlock(); Kind GetKind() const; const Block* GetBlock() const; }; EOSBlock m_eos; private: ContentEncoding** content_encoding_entries_; ContentEncoding** content_encoding_entries_end_; }; class VideoTrack : public Track { VideoTrack(const VideoTrack&); VideoTrack& operator=(const VideoTrack&); VideoTrack(Segment*, long long element_start, long long element_size); public: static long Parse(Segment*, const Info&, long long element_start, long long element_size, VideoTrack*&); long long GetWidth() const; long long GetHeight() const; long long GetDisplayWidth() const; long long GetDisplayHeight() const; long long GetDisplayUnit() const; long long GetStereoMode() const; double GetFrameRate() const; bool VetEntry(const BlockEntry*) const; long Seek(long long time_ns, const BlockEntry*&) const; private: long long m_width; long long m_height; long long m_display_width; long long m_display_height; long long m_display_unit; long long m_stereo_mode; double m_rate; }; class AudioTrack : public Track { AudioTrack(const AudioTrack&); AudioTrack& operator=(const AudioTrack&); AudioTrack(Segment*, long long element_start, long long element_size); public: static long Parse(Segment*, const Info&, long long element_start, long long element_size, AudioTrack*&); double GetSamplingRate() const; long long GetChannels() const; long long GetBitDepth() 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; const long long m_element_start; const long long m_element_size; Tracks(Segment*, long long start, long long size, long long element_start, long long element_size); ~Tracks(); long Parse(); unsigned long GetTracksCount() const; const Track* GetTrackByNumber(long tn) const; const Track* GetTrackByIndex(unsigned long idx) const; private: Track** m_trackEntries; Track** m_trackEntriesEnd; long ParseTrackEntry(long long payload_start, long long payload_size, long long element_start, long long element_size, Track*&) const; }; class Chapters { Chapters(const Chapters&); Chapters& operator=(const Chapters&); public: Segment* const m_pSegment; const long long m_start; const long long m_size; const long long m_element_start; const long long m_element_size; Chapters(Segment*, long long payload_start, long long payload_size, long long element_start, long long element_size); ~Chapters(); long Parse(); class Atom; class Edition; class Display { friend class Atom; Display(); Display(const Display&); ~Display(); Display& operator=(const Display&); public: const char* GetString() const; const char* GetLanguage() const; const char* GetCountry() const; private: void Init(); void ShallowCopy(Display&) const; void Clear(); long Parse(IMkvReader*, long long pos, long long size); char* m_string; char* m_language; char* m_country; }; class Atom { friend class Edition; Atom(); Atom(const Atom&); ~Atom(); Atom& operator=(const Atom&); public: unsigned long long GetUID() const; const char* GetStringUID() const; long long GetStartTimecode() const; long long GetStopTimecode() const; long long GetStartTime(const Chapters*) const; long long GetStopTime(const Chapters*) const; int GetDisplayCount() const; const Display* GetDisplay(int index) const; private: void Init(); void ShallowCopy(Atom&) const; void Clear(); long Parse(IMkvReader*, long long pos, long long size); static long long GetTime(const Chapters*, long long timecode); long ParseDisplay(IMkvReader*, long long pos, long long size); bool ExpandDisplaysArray(); char* m_string_uid; unsigned long long m_uid; long long m_start_timecode; long long m_stop_timecode; Display* m_displays; int m_displays_size; int m_displays_count; }; class Edition { friend class Chapters; Edition(); Edition(const Edition&); ~Edition(); Edition& operator=(const Edition&); public: int GetAtomCount() const; const Atom* GetAtom(int index) const; private: void Init(); void ShallowCopy(Edition&) const; void Clear(); long Parse(IMkvReader*, long long pos, long long size); long ParseAtom(IMkvReader*, long long pos, long long size); bool ExpandAtomsArray(); Atom* m_atoms; int m_atoms_size; int m_atoms_count; }; int GetEditionCount() const; const Edition* GetEdition(int index) const; private: long ParseEdition(long long pos, long long size); bool ExpandEditionsArray(); Edition* m_editions; int m_editions_size; int m_editions_count; }; class Tags { Tags(const Tags&); Tags& operator=(const Tags&); public: Segment* const m_pSegment; const long long m_start; const long long m_size; const long long m_element_start; const long long m_element_size; Tags(Segment*, long long payload_start, long long payload_size, long long element_start, long long element_size); ~Tags(); long Parse(); class Tag; class SimpleTag; class SimpleTag { friend class Tag; SimpleTag(); SimpleTag(const SimpleTag&); ~SimpleTag(); SimpleTag& operator=(const SimpleTag&); public: const char* GetTagName() const; const char* GetTagString() const; private: void Init(); void ShallowCopy(SimpleTag&) const; void Clear(); long Parse(IMkvReader*, long long pos, long long size); char* m_tag_name; char* m_tag_string; }; class Tag { friend class Tags; Tag(); Tag(const Tag&); ~Tag(); Tag& operator=(const Tag&); public: int GetSimpleTagCount() const; const SimpleTag* GetSimpleTag(int index) const; private: void Init(); void ShallowCopy(Tag&) const; void Clear(); long Parse(IMkvReader*, long long pos, long long size); long ParseSimpleTag(IMkvReader*, long long pos, long long size); bool ExpandSimpleTagsArray(); SimpleTag* m_simple_tags; int m_simple_tags_size; int m_simple_tags_count; }; int GetTagCount() const; const Tag* GetTag(int index) const; private: long ParseTag(long long pos, long long size); bool ExpandTagsArray(); Tag* m_tags; int m_tags_size; int m_tags_count; }; class SegmentInfo { SegmentInfo(const SegmentInfo&); SegmentInfo& operator=(const SegmentInfo&); public: Segment* const m_pSegment; const long long m_start; const long long m_size; const long long m_element_start; const long long m_element_size; SegmentInfo(Segment*, long long start, long long size, long long element_start, long long element_size); ~SegmentInfo(); long Parse(); 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 SeekHead { SeekHead(const SeekHead&); SeekHead& operator=(const SeekHead&); public: Segment* const m_pSegment; const long long m_start; const long long m_size; const long long m_element_start; const long long m_element_size; SeekHead(Segment*, long long start, long long size, long long element_start, long long element_size); ~SeekHead(); long Parse(); struct Entry { // the SeekHead entry payload long long id; long long pos; // absolute pos of SeekEntry ID long long element_start; // SeekEntry ID size + size size + payload long long element_size; }; int GetCount() const; const Entry* GetEntry(int idx) const; struct VoidElement { // absolute pos of Void ID long long element_start; // ID size + size size + payload size long long element_size; }; int GetVoidElementCount() const; const VoidElement* GetVoidElement(int idx) const; private: Entry* m_entries; int m_entry_count; VoidElement* m_void_elements; int m_void_element_count; static bool ParseEntry(IMkvReader*, long long pos, // payload long long size, Entry*); }; class Cues; class CuePoint { friend class Cues; CuePoint(long, long long); ~CuePoint(); CuePoint(const CuePoint&); CuePoint& operator=(const CuePoint&); public: long long m_element_start; long long m_element_size; bool Load(IMkvReader*); long long GetTimeCode() const; // absolute but unscaled long long GetTime(const Segment*) const; // absolute and scaled (ns units) struct TrackPosition { long long m_track; long long m_pos; // of cluster long long m_block; // codec_state //defaults to 0 // reference = clusters containing req'd referenced blocks // reftime = timecode of the referenced block bool Parse(IMkvReader*, long long, long long); }; const TrackPosition* Find(const Track*) const; private: const long m_index; long long m_timecode; TrackPosition* m_track_positions; size_t m_track_positions_count; }; class Cues { friend class Segment; Cues(Segment*, long long start, long long size, long long element_start, long long element_size); ~Cues(); Cues(const Cues&); Cues& operator=(const Cues&); public: Segment* const m_pSegment; const long long m_start; const long long m_size; const long long m_element_start; const long long m_element_size; bool Find( // lower bound of time_ns long long time_ns, const Track*, const CuePoint*&, const CuePoint::TrackPosition*&) const; const CuePoint* GetFirst() const; const CuePoint* GetLast() const; const CuePoint* GetNext(const CuePoint*) const; const BlockEntry* GetBlock(const CuePoint*, const CuePoint::TrackPosition*) const; bool LoadCuePoint() const; long GetCount() const; // loaded only // long GetTotal() const; //loaded + preloaded bool DoneParsing() const; private: bool Init() const; bool PreloadCuePoint(long&, long long) const; mutable CuePoint** m_cue_points; mutable long m_count; mutable long m_preload_count; mutable long long m_pos; }; class Cluster { friend class Segment; Cluster(const Cluster&); Cluster& operator=(const Cluster&); public: Segment* const m_pSegment; public: static Cluster* Create(Segment*, long index, // index in segment long long off); // offset relative to segment // long long element_size); Cluster(); // EndOfStream ~Cluster(); bool EOS() const; long long GetTimeCode() const; // absolute, but not scaled long long GetTime() const; // absolute, and scaled (nanosecond units) long long GetFirstTime() const; // time (ns) of first (earliest) block long long GetLastTime() const; // time (ns) of last (latest) block long GetFirst(const BlockEntry*&) const; long GetLast(const BlockEntry*&) const; long GetNext(const BlockEntry* curr, const BlockEntry*& next) const; const BlockEntry* GetEntry(const Track*, long long ns = -1) const; const BlockEntry* GetEntry(const CuePoint&, const CuePoint::TrackPosition&) const; // const BlockEntry* GetMaxKey(const VideoTrack*) const; // static bool HasBlockEntries(const Segment*, long long); static long HasBlockEntries(const Segment*, long long idoff, long long& pos, long& size); long GetEntryCount() const; long Load(long long& pos, long& size) const; long Parse(long long& pos, long& size) const; long GetEntry(long index, const mkvparser::BlockEntry*&) const; protected: Cluster(Segment*, long index, long long element_start); // long long element_size); public: const long long m_element_start; long long GetPosition() const; // offset relative to segment long GetIndex() const; long long GetElementSize() const; // long long GetPayloadSize() const; // long long Unparsed() const; private: long m_index; mutable long long m_pos; // mutable long long m_size; mutable long long m_element_size; mutable long long m_timecode; mutable BlockEntry** m_entries; mutable long m_entries_size; mutable long m_entries_count; long ParseSimpleBlock(long long, long long&, long&); long ParseBlockGroup(long long, long long&, long&); long CreateBlock(long long id, long long pos, long long size, long long discard_padding); long CreateBlockGroup(long long start_offset, long long size, long long discard_padding); long CreateSimpleBlock(long long, long long); }; class Segment { friend class Cues; friend class Track; friend class VideoTrack; Segment(const Segment&); Segment& operator=(const Segment&); private: Segment(IMkvReader*, long long elem_start, // long long elem_size, long long pos, long long size); public: IMkvReader* const m_pReader; const long long m_element_start; // const long long m_element_size; 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(); long Load(); // loads headers and all clusters // for incremental loading // long long Unparsed() const; bool DoneParsing() const; long long ParseHeaders(); // stops when first cluster is found // long FindNextCluster(long long& pos, long& size) const; long LoadCluster(long long& pos, long& size); // load one cluster long LoadCluster(); long ParseNext(const Cluster* pCurr, const Cluster*& pNext, long long& pos, long& size); const SeekHead* GetSeekHead() const; const Tracks* GetTracks() const; const SegmentInfo* GetInfo() const; const Cues* GetCues() const; const Chapters* GetChapters() const; const Tags* GetTags() const; long long GetDuration() const; unsigned long GetCount() const; const Cluster* GetFirst() const; const Cluster* GetLast() const; const Cluster* GetNext(const Cluster*); const Cluster* FindCluster(long long time_nanoseconds) const; // const BlockEntry* Seek(long long time_nanoseconds, const Track*) const; const Cluster* FindOrPreloadCluster(long long pos); long ParseCues(long long cues_off, // offset relative to start of segment long long& parse_pos, long& parse_len); private: long long m_pos; // absolute file posn; what has been consumed so far Cluster* m_pUnknownSize; SeekHead* m_pSeekHead; SegmentInfo* m_pInfo; Tracks* m_pTracks; Cues* m_pCues; Chapters* m_pChapters; Tags* m_pTags; Cluster** m_clusters; long m_clusterCount; // number of entries for which m_index >= 0 long m_clusterPreloadCount; // number of entries for which m_index < 0 long m_clusterSize; // array size long DoLoadCluster(long long&, long&); long DoLoadClusterUnknownSize(long long&, long&); long DoParseNext(const Cluster*&, long long&, long&); bool AppendCluster(Cluster*); bool PreloadCluster(Cluster*, ptrdiff_t); // void ParseSeekHead(long long pos, long long size); // void ParseSeekEntry(long long pos, long long size); // void ParseCues(long long); const BlockEntry* GetBlock(const CuePoint&, const CuePoint::TrackPosition&); }; } // end namespace mkvparser inline long mkvparser::Segment::LoadCluster() { long long pos; long size; return LoadCluster(pos, size); } #endif // MKVPARSER_HPP