mkvparser: add support for MKV chapters

Change-Id: I2404b6886ed592fe505ee973bf05c769a9d134b1
This commit is contained in:
Matthew Heaney
2012-10-24 15:51:18 -07:00
parent ad54bfb572
commit 50ee255b8c
2 changed files with 655 additions and 12 deletions

View File

@@ -336,7 +336,7 @@ long mkvparser::ParseElementHeader(
id = ReadUInt(pReader, pos, len);
if (id <= 0)
if (id < 0)
return E_FILE_FORMAT_INVALID;
pos += len; //consume id
@@ -692,6 +692,7 @@ Segment::Segment(
m_pInfo(NULL),
m_pTracks(NULL),
m_pCues(NULL),
m_pChapters(NULL),
m_clusters(NULL),
m_clusterCount(0),
m_clusterPreloadCount(0),
@@ -720,6 +721,7 @@ Segment::~Segment()
delete m_pTracks;
delete m_pInfo;
delete m_pCues;
delete m_pChapters;
delete m_pSeekHead;
}
@@ -1022,6 +1024,26 @@ long long Segment::ParseHeaders()
return status;
}
}
else if (id == 0x0043A770) //Chapters ID
{
if (m_pChapters == NULL)
{
m_pChapters = new (std::nothrow) Chapters(
this,
pos,
size,
element_start,
element_size);
if (m_pChapters == NULL)
return -1;
const long status = m_pChapters->Parse();
if (status)
return status;
}
}
m_pos = pos + size; //consume payload
}
@@ -3291,10 +3313,6 @@ const Cluster* Segment::GetNext(const Cluster* pCurr)
assert(m_clusterPreloadCount > 0);
//const long long off_ = pCurr->m_pos;
//const long long off = off_ * ((off_ < 0) ? -1 : 1);
//long long pos = m_start + off;
long long pos = pCurr->m_element_start;
assert(m_size >= 0); //TODO
@@ -3330,8 +3348,6 @@ const Cluster* Segment::GetNext(const Cluster* pCurr)
}
long long off_next = 0;
//long long element_start_next = 0;
long long element_size_next = 0;
while (pos < stop)
{
@@ -3359,8 +3375,6 @@ const Cluster* Segment::GetNext(const Cluster* pCurr)
pos += len; //consume length of size of element
assert((pos + size) <= stop); //TODO
const long long element_size = size + pos - idpos;
//Pos now points to start of payload
if (size == 0) //weird
@@ -3384,8 +3398,6 @@ const Cluster* Segment::GetNext(const Cluster* pCurr)
if (status > 0)
{
off_next = off_next_;
//element_start_next = idpos;
element_size_next = element_size;
break;
}
}
@@ -3435,7 +3447,6 @@ const Cluster* Segment::GetNext(const Cluster* pCurr)
Cluster* const pNext = Cluster::Create(this,
-1,
off_next);
//element_size_next);
assert(pNext);
const ptrdiff_t idx_next = i - m_clusters; //insertion position
@@ -4208,6 +4219,12 @@ const Cues* Segment::GetCues() const
}
const Chapters* Segment::GetChapters() const
{
return m_pChapters;
}
const SeekHead* Segment::GetSeekHead() const
{
return m_pSeekHead;
@@ -4221,6 +4238,515 @@ long long Segment::GetDuration() const
}
Chapters::Chapters(
Segment* pSegment,
long long payload_start,
long long payload_size,
long long element_start,
long long element_size) :
m_pSegment(pSegment),
m_start(payload_start),
m_size(payload_size),
m_element_start(element_start),
m_element_size(element_size),
m_editions(NULL),
m_editions_size(0),
m_editions_count(0)
{
}
Chapters::~Chapters()
{
while (m_editions_count > 0)
{
Edition& e = m_editions[--m_editions_count];
e.Clear();
}
}
long Chapters::Parse()
{
IMkvReader* const pReader = m_pSegment->m_pReader;
long long pos = m_start; // payload start
const long long stop = pos + m_size; // payload stop
while (pos < stop)
{
long long id, size;
long status = ParseElementHeader(
pReader,
pos,
stop,
id,
size);
if (status < 0) // error
return status;
if (size == 0) // weird
continue;
if (id == 0x05B9) // EditionEntry ID
{
status = ParseEdition(pos, size);
if (status < 0) // error
return status;
}
pos += size;
assert(pos <= stop);
}
assert(pos == stop);
return 0;
}
int Chapters::GetEditionCount() const
{
return m_editions_count;
}
const Chapters::Edition* Chapters::GetEdition(int idx) const
{
if (idx < 0)
return NULL;
if (idx >= m_editions_count)
return NULL;
return m_editions + idx;
}
bool Chapters::ExpandEditionsArray()
{
if (m_editions_size > m_editions_count)
return true; // nothing else to do
const int size = (m_editions_size == 0) ? 1 : 2 * m_editions_size;
Edition* const editions = new (std::nothrow) Edition[size];
if (editions == NULL)
return false;
for (int idx = 0; idx < m_editions_count; ++idx)
{
m_editions[idx].ShallowCopy(editions[idx]);
}
delete[] m_editions;
m_editions = editions;
m_editions_size = size;
return true;
}
long Chapters::ParseEdition(
long long pos,
long long size)
{
if (!ExpandEditionsArray())
return -1;
Edition& e = m_editions[m_editions_count++];
e.Init();
return e.Parse(m_pSegment->m_pReader, pos, size);
}
Chapters::Edition::Edition()
{
}
Chapters::Edition::~Edition()
{
}
void Chapters::Edition::Init()
{
m_atoms = NULL;
m_atoms_size = 0;
m_atoms_count = 0;
}
void Chapters::Edition::ShallowCopy(Edition& rhs) const
{
rhs.m_atoms = m_atoms;
rhs.m_atoms_size = m_atoms_size;
rhs.m_atoms_count = m_atoms_count;
}
void Chapters::Edition::Clear()
{
while (m_atoms_count > 0)
{
Atom& a = m_atoms[--m_atoms_count];
a.Clear();
}
delete[] m_atoms;
m_atoms = NULL;
m_atoms_size = 0;
}
long Chapters::Edition::Parse(
IMkvReader* pReader,
long long pos,
long long size)
{
const long long stop = pos + size;
while (pos < stop)
{
long long id, size;
long status = ParseElementHeader(
pReader,
pos,
stop,
id,
size);
if (status < 0) // error
return status;
if (size == 0) // weird
continue;
if (id == 0x36) // Atom ID
{
status = ParseAtom(pReader, pos, size);
if (status < 0) // error
return status;
}
pos += size;
assert(pos <= stop);
}
assert(pos == stop);
return 0;
}
long Chapters::Edition::ParseAtom(
IMkvReader* pReader,
long long pos,
long long size)
{
if (!ExpandAtomsArray())
return -1;
Atom& a = m_atoms[m_atoms_count++];
a.Init();
return a.Parse(pReader, pos, size);
}
bool Chapters::Edition::ExpandAtomsArray()
{
if (m_atoms_size > m_atoms_count)
return true; // nothing else to do
const int size = (m_atoms_size == 0) ? 1 : 2 * m_atoms_size;
Atom* const atoms = new (std::nothrow) Atom[size];
if (atoms == NULL)
return false;
for (int idx = 0; idx < m_atoms_count; ++idx)
{
m_atoms[idx].ShallowCopy(atoms[idx]);
}
delete[] m_atoms;
m_atoms = atoms;
m_atoms_size = size;
return true;
}
Chapters::Atom::Atom()
{
}
Chapters::Atom::~Atom()
{
}
void Chapters::Atom::Init()
{
m_displays = NULL;
m_displays_size = 0;
m_displays_count = 0;
}
void Chapters::Atom::ShallowCopy(Atom& rhs) const
{
rhs.m_displays = m_displays;
rhs.m_displays_size = m_displays_size;
rhs.m_displays_count = m_displays_count;
}
void Chapters::Atom::Clear()
{
while (m_displays_count > 0)
{
Display& d = m_displays[--m_displays_count];
d.Clear();
}
delete[] m_displays;
m_displays = NULL;
m_displays_size = 0;
}
long Chapters::Atom::Parse(
IMkvReader* pReader,
long long pos,
long long size)
{
const long long stop = pos + size;
while (pos < stop)
{
long long id, size;
long status = ParseElementHeader(
pReader,
pos,
stop,
id,
size);
if (status < 0) // error
return status;
if (size == 0) // weird
continue;
if (id == 0x00) // Display ID
{
status = ParseDisplay(pReader, pos, size);
if (status < 0) // error
return status;
}
else if (id == 0x33C4) // UID ID
{
const long long val = UnserializeUInt(pReader, pos, size);
if (val < 0) // error
return static_cast<long>(val);
m_uid = val;
}
else if (id == 0x11) // TimeStart ID
{
const long long val = UnserializeUInt(pReader, pos, size);
if (val < 0) // error
return static_cast<long>(val);
m_start_timecode = val;
}
else if (id == 0x12) // TimeEnd ID
{
const long long val = UnserializeUInt(pReader, pos, size);
if (val < 0) // error
return static_cast<long>(val);
m_stop_timecode = val;
}
pos += size;
assert(pos <= stop);
}
assert(pos == stop);
return 0;
}
long Chapters::Atom::ParseDisplay(
IMkvReader* pReader,
long long pos,
long long size)
{
if (!ExpandDisplaysArray())
return -1;
Display& d = m_displays[m_displays_count++];
d.Init();
return d.Parse(pReader, pos, size);
}
bool Chapters::Atom::ExpandDisplaysArray()
{
if (m_displays_size > m_displays_count)
return true; // nothing else to do
const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size;
Display* const displays = new (std::nothrow) Display[size];
if (displays == NULL)
return false;
for (int idx = 0; idx < m_displays_count; ++idx)
{
m_displays[idx].ShallowCopy(displays[idx]);
}
delete[] m_displays;
m_displays = displays;
m_displays_size = size;
return true;
}
Chapters::Display::Display()
{
}
Chapters::Display::~Display()
{
}
const char* Chapters::Display::GetString() const
{
return m_string;
}
const char* Chapters::Display::GetLanguage() const
{
return m_language;
}
const char* Chapters::Display::GetCountry() const
{
return m_country;
}
void Chapters::Display::Init()
{
m_string = NULL;
m_language = NULL;
m_country = NULL;
}
void Chapters::Display::ShallowCopy(Display& rhs) const
{
rhs.m_string = m_string;
rhs.m_language = m_language;
rhs.m_country = m_country;
}
void Chapters::Display::Clear()
{
delete[] m_string;
m_string = NULL;
delete[] m_language;
m_language = NULL;
delete[] m_country;
m_country = NULL;
}
long Chapters::Display::Parse(
IMkvReader* pReader,
long long pos,
long long size)
{
const long long stop = pos + size;
while (pos < stop)
{
long long id, size;
long status = ParseElementHeader(
pReader,
pos,
stop,
id,
size);
if (status < 0) // error
return status;
if (size == 0) // weird
continue;
if (id == 0x05) // ChapterString ID
{
status = UnserializeString(pReader, pos, size, m_string);
if (status)
return status;
}
else if (id == 0x037C) // ChapterLanguage ID
{
status = UnserializeString(pReader, pos, size, m_language);
if (status)
return status;
}
else if (id == 0x037E) // ChapterCountry ID
{
status = UnserializeString(pReader, pos, size, m_country);
if (status)
return status;
}
pos += size;
assert(pos <= stop);
}
assert(pos == stop);
return 0;
}
SegmentInfo::SegmentInfo(
Segment* pSegment,
long long start,

View File

@@ -511,6 +511,121 @@ private:
};
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:
int GetDisplayCount() const;
const Atom* GetAtom(int index) const;
private:
void Init();
void ShallowCopy(Atom&) const;
void Clear();
long Parse(IMkvReader*, long long pos, long long size);
long ParseDisplay(IMkvReader*, long long pos, long long size);
bool ExpandDisplaysArray();
unsigned long long m_uid;
// TODO(matthewjheaney): Cue Identifier (string)
unsigned long long m_start_timecode;
unsigned 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 SegmentInfo
{
SegmentInfo(const SegmentInfo&);
@@ -863,6 +978,7 @@ public:
const Tracks* GetTracks() const;
const SegmentInfo* GetInfo() const;
const Cues* GetCues() const;
const Chapters* GetChapters() const;
long long GetDuration() const;
@@ -890,6 +1006,7 @@ private:
SegmentInfo* m_pInfo;
Tracks* m_pTracks;
Cues* m_pCues;
Chapters* m_pChapters;
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