// Copyright (c) 2010 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 <cstdlib> | |
#include <cstdio> | |
#include <cstddef> | |
namespace mkvparser | |
{ | |
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(); | |
}; | |
long long GetUIntLength(IMkvReader*, long long, long&); | |
long long ReadUInt(IMkvReader*, long long, long&); | |
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 len, 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); | |
~Block(); | |
long Parse(IMkvReader*); | |
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; | |
private: | |
long long m_track; //Track::Number() | |
short m_timecode; //relative to cluster | |
unsigned char m_flags; | |
Frame* m_frames; | |
int m_frame_count; | |
}; | |
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 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 GetDuration() 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: | |
ContentEncoding(); | |
~ContentEncoding(); | |
// ContentCompression element names | |
struct ContentCompression { | |
ContentCompression(); | |
~ContentCompression(); | |
unsigned long long algo; | |
unsigned char* settings; | |
}; | |
// 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; | |
}; | |
// 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; | |
// 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 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. | |
bool ParseContentEncodingEntry(long long start, | |
long long size, | |
IMkvReader* const 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. | |
void ParseEncryptionEntry(long long start, | |
long long size, | |
IMkvReader* const pReader, | |
ContentEncryption* const 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: | |
enum Type { kVideo = 1, kAudio = 2 }; | |
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* GetCodecNameAsUTF8() const; | |
const char* GetCodecId() const; | |
const unsigned char* GetCodecPrivate(size_t&) const; | |
bool GetLacing() const; | |
const BlockEntry* GetEOS() const; | |
struct Settings | |
{ | |
long long start; | |
long long size; | |
}; | |
class Info | |
{ | |
public: | |
Info(); | |
~Info(); | |
int Copy(Info&) const; | |
void Clear(); | |
private: | |
Info(const Info&); | |
Info& operator=(const Info&); | |
public: | |
long type; | |
long number; | |
unsigned long long uid; | |
char* nameAsUTF8; | |
char* codecId; | |
char* codecNameAsUTF8; | |
unsigned char* codecPrivate; | |
size_t codecPrivateSize; | |
bool lacing; | |
Settings settings; | |
private: | |
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 = 0; | |
virtual long Seek(long long time_ns, const BlockEntry*&) const = 0; | |
const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const; | |
unsigned long GetContentEncodingCount() const; | |
void 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; | |
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; | |
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; | |
bool VetEntry(const BlockEntry*) const; | |
long Seek(long long time_ns, 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; | |
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 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; | |
void 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 | |
void 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; | |
#if 0 | |
bool FindNext( //upper_bound of time_ns | |
long long time_ns, | |
const Track*, | |
const CuePoint*&, | |
const CuePoint::TrackPosition*&) const; | |
#endif | |
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: | |
void Init() const; | |
void 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 CreateBlockGroup(long long, long long); | |
long CreateSimpleBlock(long long, long long); | |
}; | |
class Segment | |
{ | |
friend class Cues; | |
friend class VideoTrack; | |
friend class AudioTrack; | |
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); | |
#if 0 | |
//This pair parses one cluster, but only changes the state of the | |
//segment object when the cluster is actually added to the index. | |
long ParseCluster(long long& cluster_pos, long long& new_pos) const; | |
bool AddCluster(long long cluster_pos, long long new_pos); | |
#endif | |
const SeekHead* GetSeekHead() const; | |
const Tracks* GetTracks() const; | |
const SegmentInfo* GetInfo() const; | |
const Cues* GetCues() 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; | |
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&); | |
void AppendCluster(Cluster*); | |
void 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 |