Initial version
This commit is contained in:
433
mkvparser.hpp
Normal file
433
mkvparser.hpp
Normal file
@@ -0,0 +1,433 @@
|
||||
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license and patent
|
||||
// grant that can be found in the LICENSE file in the root of the source
|
||||
// tree. 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>
|
||||
|
||||
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&);
|
||||
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&) 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
|
||||
Reference in New Issue
Block a user