DO NOT MERGE - external/libvpx/libwebm: Update snapshot

Update libwebm snapshot.

Upstream git hash: 229f49347d19b0ca0941e072b199a242ef6c5f2b

BUG=23167726

Change-Id: I1b58e8bf9dcaa54d853134165bc8107c8c19b2ab
diff --git a/libwebm/mkvparser.cpp b/libwebm/mkvparser.cpp
index 894d470..f0cd97f 100644
--- a/libwebm/mkvparser.cpp
+++ b/libwebm/mkvparser.cpp
@@ -1,8619 +1,7694 @@
-// 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.

-

-#include "mkvparser.hpp"

-#include <cassert>

-#include <cstring>

-#include <new>

-#include <climits>

-

-mkvparser::IMkvReader::~IMkvReader()

-{

-}

-

-void mkvparser::GetVersion(int& major, int& minor, int& build, int& revision)

-{

-    major = 1;

-    minor = 0;

-    build = 0;

-    revision = 24;

-}

-

-long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len)

-{

-    assert(pReader);

-    assert(pos >= 0);

-

-    int status;

-

-//#ifdef _DEBUG

-//    long long total, available;

-//    status = pReader->Length(&total, &available);

-//    assert(status >= 0);

-//    assert((total < 0) || (available <= total));

-//    assert(pos < available);

-//    assert((available - pos) >= 1);  //assume here max u-int len is 8

-//#endif

-

-    len = 1;

-

-    unsigned char b;

-

-    status = pReader->Read(pos, 1, &b);

-

-    if (status < 0)  //error or underflow

-        return status;

-

-    if (status > 0)  //interpreted as "underflow"

-        return E_BUFFER_NOT_FULL;

-

-    if (b == 0)  //we can't handle u-int values larger than 8 bytes

-        return E_FILE_FORMAT_INVALID;

-

-    unsigned char m = 0x80;

-

-    while (!(b & m))

-    {

-        m >>= 1;

-        ++len;

-    }

-

-//#ifdef _DEBUG

-//    assert((available - pos) >= len);

-//#endif

-

-    long long result = b & (~m);

-    ++pos;

-

-    for (int i = 1; i < len; ++i)

-    {

-        status = pReader->Read(pos, 1, &b);

-

-        if (status < 0)

-        {

-            len = 1;

-            return status;

-        }

-

-        if (status > 0)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result <<= 8;

-        result |= b;

-

-        ++pos;

-    }

-

-    return result;

-}

-

-long long mkvparser::GetUIntLength(

-    IMkvReader* pReader,

-    long long pos,

-    long& len)

-{

-    assert(pReader);

-    assert(pos >= 0);

-

-    long long total, available;

-

-    int status = pReader->Length(&total, &available);

-    assert(status >= 0);

-    assert((total < 0) || (available <= total));

-

-    len = 1;

-

-    if (pos >= available)

-        return pos;  //too few bytes available

-

-    unsigned char b;

-

-    status = pReader->Read(pos, 1, &b);

-

-    if (status < 0)

-        return status;

-

-    assert(status == 0);

-

-    if (b == 0)  //we can't handle u-int values larger than 8 bytes

-        return E_FILE_FORMAT_INVALID;

-

-    unsigned char m = 0x80;

-

-    while (!(b & m))

-    {

-        m >>= 1;

-        ++len;

-    }

-

-    return 0;  //success

-}

-

-

-long long mkvparser::UnserializeUInt(

-    IMkvReader* pReader,

-    long long pos,

-    long long size)

-{

-    assert(pReader);

-    assert(pos >= 0);

-

-    if ((size <= 0) || (size > 8))

-        return E_FILE_FORMAT_INVALID;

-

-    long long result = 0;

-

-    for (long long i = 0; i < size; ++i)

-    {

-        unsigned char b;

-

-        const long status = pReader->Read(pos, 1, &b);

-

-        if (status < 0)

-            return status;

-

-        result <<= 8;

-        result |= b;

-

-        ++pos;

-    }

-

-    return result;

-}

-

-

-long mkvparser::UnserializeFloat(

-    IMkvReader* pReader,

-    long long pos,

-    long long size_,

-    double& result)

-{

-    assert(pReader);

-    assert(pos >= 0);

-

-    if ((size_ != 4) && (size_ != 8))

-        return E_FILE_FORMAT_INVALID;

-

-    const long size = static_cast<long>(size_);

-

-    unsigned char buf[8];

-

-    const int status = pReader->Read(pos, size, buf);

-

-    if (status < 0)  //error

-        return status;

-

-    if (size == 4)

-    {

-        union

-        {

-            float f;

-            unsigned long ff;

-        };

-

-        ff = 0;

-

-        for (int i = 0;;)

-        {

-            ff |= buf[i];

-

-            if (++i >= 4)

-                break;

-

-            ff <<= 8;

-        }

-

-        result = f;

-    }

-    else

-    {

-        assert(size == 8);

-

-        union

-        {

-            double d;

-            unsigned long long dd;

-        };

-

-        dd = 0;

-

-        for (int i = 0;;)

-        {

-            dd |= buf[i];

-

-            if (++i >= 8)

-                break;

-

-            dd <<= 8;

-        }

-

-        result = d;

-    }

-

-    return 0;

-}

-

-

-long mkvparser::UnserializeInt(

-    IMkvReader* pReader,

-    long long pos,

-    long size,

-    long long& result)

-{

-    assert(pReader);

-    assert(pos >= 0);

-    assert(size > 0);

-    assert(size <= 8);

-

-    {

-        signed char b;

-

-        const long status = pReader->Read(pos, 1, (unsigned char*)&b);

-

-        if (status < 0)

-            return status;

-

-        result = b;

-

-        ++pos;

-    }

-

-    for (long i = 1; i < size; ++i)

-    {

-        unsigned char b;

-

-        const long status = pReader->Read(pos, 1, &b);

-

-        if (status < 0)

-            return status;

-

-        result <<= 8;

-        result |= b;

-

-        ++pos;

-    }

-

-    return 0;  //success

-}

-

-

-long mkvparser::UnserializeString(

-    IMkvReader* pReader,

-    long long pos,

-    long long size_,

-    char*& str)

-{

-    delete[] str;

-    str = NULL;

-

-    if (size_ >= LONG_MAX)  //we need (size+1) chars

-        return E_FILE_FORMAT_INVALID;

-

-    const long size = static_cast<long>(size_);

-

-    str = new (std::nothrow) char[size+1];

-

-    if (str == NULL)

-        return -1;

-

-    unsigned char* const buf = reinterpret_cast<unsigned char*>(str);

-

-    const long status = pReader->Read(pos, size, buf);

-

-    if (status)

-    {

-        delete[] str;

-        str = NULL;

-

-        return status;

-    }

-

-    str[size] = '\0';

-

-    return 0;  //success

-}

-

-

-long mkvparser::ParseElementHeader(

-    IMkvReader* pReader,

-    long long& pos,

-    long long stop,

-    long long& id,

-    long long& size)

-{

-    if ((stop >= 0) && (pos >= stop))

-        return E_FILE_FORMAT_INVALID;

-

-    long len;

-

-    id = ReadUInt(pReader, pos, len);

-

-    if (id <= 0)

-        return E_FILE_FORMAT_INVALID;

-

-    pos += len;  //consume id

-

-    if ((stop >= 0) && (pos >= stop))

-        return E_FILE_FORMAT_INVALID;

-

-    size = ReadUInt(pReader, pos, len);

-

-    if (size < 0)

-        return E_FILE_FORMAT_INVALID;

-

-    pos += len;  //consume length of size

-

-    //pos now designates payload

-

-    if ((stop >= 0) && ((pos + size) > stop))

-        return E_FILE_FORMAT_INVALID;

-

-    return 0;  //success

-}

-

-

-bool mkvparser::Match(

-    IMkvReader* pReader,

-    long long& pos,

-    unsigned long id_,

-    long long& val)

-{

-    assert(pReader);

-    assert(pos >= 0);

-

-    long long total, available;

-

-    const long status = pReader->Length(&total, &available);

-    assert(status >= 0);

-    assert((total < 0) || (available <= total));

-

-    long len;

-

-    const long long id = ReadUInt(pReader, pos, len);

-    assert(id >= 0);

-    assert(len > 0);

-    assert(len <= 8);

-    assert((pos + len) <= available);

-

-    if ((unsigned long)id != id_)

-        return false;

-

-    pos += len;  //consume id

-

-    const long long size = ReadUInt(pReader, pos, len);

-    assert(size >= 0);

-    assert(size <= 8);

-    assert(len > 0);

-    assert(len <= 8);

-    assert((pos + len) <= available);

-

-    pos += len;  //consume length of size of payload

-

-    val = UnserializeUInt(pReader, pos, size);

-    assert(val >= 0);

-

-    pos += size;  //consume size of payload

-

-    return true;

-}

-

-bool mkvparser::Match(

-    IMkvReader* pReader,

-    long long& pos,

-    unsigned long id_,

-    unsigned char*& buf,

-    size_t& buflen)

-{

-    assert(pReader);

-    assert(pos >= 0);

-

-    long long total, available;

-

-    long status = pReader->Length(&total, &available);

-    assert(status >= 0);

-    assert((total < 0) || (available <= total));

-

-    long len;

-    const long long id = ReadUInt(pReader, pos, len);

-    assert(id >= 0);

-    assert(len > 0);

-    assert(len <= 8);

-    assert((pos + len) <= available);

-

-    if ((unsigned long)id != id_)

-        return false;

-

-    pos += len;  //consume id

-

-    const long long size_ = ReadUInt(pReader, pos, len);

-    assert(size_ >= 0);

-    assert(len > 0);

-    assert(len <= 8);

-    assert((pos + len) <= available);

-

-    pos += len;  //consume length of size of payload

-    assert((pos + size_) <= available);

-

-    const long buflen_ = static_cast<long>(size_);

-

-    buf = new (std::nothrow) unsigned char[buflen_];

-    assert(buf);  //TODO

-

-    status = pReader->Read(pos, buflen_, buf);

-    assert(status == 0);  //TODO

-

-    buflen = buflen_;

-

-    pos += size_;  //consume size of payload

-    return true;

-}

-

-

-namespace mkvparser

-{

-

-EBMLHeader::EBMLHeader() :

-    m_docType(NULL)

-{

-    Init();

-}

-

-EBMLHeader::~EBMLHeader()

-{

-    delete[] m_docType;

-}

-

-void EBMLHeader::Init()

-{

-    m_version = 1;

-    m_readVersion = 1;

-    m_maxIdLength = 4;

-    m_maxSizeLength = 8;

-

-    if (m_docType)

-    {

-        delete[] m_docType;

-        m_docType = NULL;

-    }

-

-    m_docTypeVersion = 1;

-    m_docTypeReadVersion = 1;

-}

-

-long long EBMLHeader::Parse(

-    IMkvReader* pReader,

-    long long& pos)

-{

-    assert(pReader);

-

-    long long total, available;

-

-    long status = pReader->Length(&total, &available);

-

-    if (status < 0)  //error

-        return status;

-

-    pos = 0;

-    long long end = (available >= 1024) ? 1024 : available;

-

-    for (;;)

-    {

-        unsigned char b = 0;

-

-        while (pos < end)

-        {

-            status = pReader->Read(pos, 1, &b);

-

-            if (status < 0)  //error

-                return status;

-

-            if (b == 0x1A)

-                break;

-

-            ++pos;

-        }

-

-        if (b != 0x1A)

-        {

-            if (pos >= 1024)

-                return E_FILE_FORMAT_INVALID;  //don't bother looking anymore

-

-            if ((total >= 0) && ((total - available) < 5))

-                return E_FILE_FORMAT_INVALID;

-

-            return available + 5;  //5 = 4-byte ID + 1st byte of size

-        }

-

-        if ((total >= 0) && ((total - pos) < 5))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((available - pos) < 5)

-            return pos + 5;  //try again later

-

-        long len;

-

-        const long long result = ReadUInt(pReader, pos, len);

-

-        if (result < 0)  //error

-            return result;

-

-        if (result == 0x0A45DFA3)  //EBML Header ID

-        {

-            pos += len;  //consume ID

-            break;

-        }

-

-        ++pos;  //throw away just the 0x1A byte, and try again

-    }

-

-    //pos designates start of size field

-

-    //get length of size field

-

-    long len;

-    long long result = GetUIntLength(pReader, pos, len);

-

-    if (result < 0)  //error

-        return result;

-

-    if (result > 0)  //need more data

-        return result;

-

-    assert(len > 0);

-    assert(len <= 8);

-

-    if ((total >= 0) && ((total -  pos) < len))

-        return E_FILE_FORMAT_INVALID;

-

-    if ((available - pos) < len)

-        return pos + len;  //try again later

-

-    //get the EBML header size

-

-    result = ReadUInt(pReader, pos, len);

-

-    if (result < 0)  //error

-        return result;

-

-    pos += len;  //consume size field

-

-    //pos now designates start of payload

-

-    if ((total >= 0) && ((total - pos) < result))

-        return E_FILE_FORMAT_INVALID;

-

-    if ((available - pos) < result)

-        return pos + result;

-

-    end = pos + result;

-

-    Init();

-

-    while (pos < end)

-    {

-        long long id, size;

-

-        status = ParseElementHeader(

-                    pReader,

-                    pos,

-                    end,

-                    id,

-                    size);

-

-        if (status < 0) //error

-            return status;

-

-        if (size == 0)  //weird

-            return E_FILE_FORMAT_INVALID;

-

-        if (id == 0x0286)  //version

-        {

-            m_version = UnserializeUInt(pReader, pos, size);

-

-            if (m_version <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x02F7)  //read version

-        {

-            m_readVersion = UnserializeUInt(pReader, pos, size);

-

-            if (m_readVersion <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x02F2)  //max id length

-        {

-            m_maxIdLength = UnserializeUInt(pReader, pos, size);

-

-            if (m_maxIdLength <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x02F3)  //max size length

-        {

-            m_maxSizeLength = UnserializeUInt(pReader, pos, size);

-

-            if (m_maxSizeLength <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x0282)  //doctype

-        {

-            if (m_docType)

-                return E_FILE_FORMAT_INVALID;

-

-            status = UnserializeString(pReader, pos, size, m_docType);

-

-            if (status)  //error

-                return status;

-        }

-        else if (id == 0x0287)  //doctype version

-        {

-            m_docTypeVersion = UnserializeUInt(pReader, pos, size);

-

-            if (m_docTypeVersion <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x0285)  //doctype read version

-        {

-            m_docTypeReadVersion = UnserializeUInt(pReader, pos, size);

-

-            if (m_docTypeReadVersion <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-

-        pos += size;

-    }

-

-    assert(pos == end);

-    return 0;

-}

-

-

-Segment::Segment(

-    IMkvReader* pReader,

-    long long elem_start,

-    //long long elem_size,

-    long long start,

-    long long size) :

-    m_pReader(pReader),

-    m_element_start(elem_start),

-    //m_element_size(elem_size),

-    m_start(start),

-    m_size(size),

-    m_pos(start),

-    m_pUnknownSize(0),

-    m_pSeekHead(NULL),

-    m_pInfo(NULL),

-    m_pTracks(NULL),

-    m_pCues(NULL),

-    m_clusters(NULL),

-    m_clusterCount(0),

-    m_clusterPreloadCount(0),

-    m_clusterSize(0)

-{

-}

-

-

-Segment::~Segment()

-{

-    const long count = m_clusterCount + m_clusterPreloadCount;

-

-    Cluster** i = m_clusters;

-    Cluster** j = m_clusters + count;

-

-    while (i != j)

-    {

-        Cluster* const p = *i++;

-        assert(p);

-

-        delete p;

-    }

-

-    delete[] m_clusters;

-

-    delete m_pTracks;

-    delete m_pInfo;

-    delete m_pCues;

-    delete m_pSeekHead;

-}

-

-

-long long Segment::CreateInstance(

-    IMkvReader* pReader,

-    long long pos,

-    Segment*& pSegment)

-{

-    assert(pReader);

-    assert(pos >= 0);

-

-    pSegment = NULL;

-

-    long long total, available;

-

-    const long status = pReader->Length(&total, &available);

-

-    if (status < 0) //error

-        return status;

-

-    if (available < 0)

-        return -1;

-

-    if ((total >= 0) && (available > total))

-        return -1;

-

-    const long long end = (total >= 0) ? total : available;

-    //TODO: this might need to be liberalized

-

-    //I would assume that in practice this loop would execute

-    //exactly once, but we allow for other elements (e.g. Void)

-    //to immediately follow the EBML header.  This is fine for

-    //the source filter case (since the entire file is available),

-    //but in the splitter case over a network we should probably

-    //just give up early.  We could for example decide only to

-    //execute this loop a maximum of, say, 10 times.

-    //TODO:

-    //There is an implied "give up early" by only parsing up

-    //to the available limit.  We do do that, but only if the

-    //total file size is unknown.  We could decide to always

-    //use what's available as our limit (irrespective of whether

-    //we happen to know the total file length).  This would have

-    //as its sense "parse this much of the file before giving up",

-    //which a slightly different sense from "try to parse up to

-    //10 EMBL elements before giving up".

-

-    while (pos < end)

-    {

-        //Read ID

-

-        long len;

-        long long result = GetUIntLength(pReader, pos, len);

-

-        if (result)  //error, or too few available bytes

-            return result;

-

-        if ((pos + len) > end)

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > available)

-            return pos + len;

-

-        const long long idpos = pos;

-        const long long id = ReadUInt(pReader, pos, len);

-

-        if (id < 0)  //error

-            return id;

-

-        pos += len;  //consume ID

-

-        //Read Size

-

-        result = GetUIntLength(pReader, pos, len);

-

-        if (result)  //error, or too few available bytes

-            return result;

-

-        if ((pos + len) > end)

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > available)

-            return pos + len;

-

-        long long size = ReadUInt(pReader, pos, len);

-

-        if (size < 0)  //error

-            return size;

-

-        pos += len;  //consume length of size of element

-

-        //Pos now points to start of payload

-

-        //Handle "unknown size" for live streaming of webm files.

-        const long long unknown_size = (1LL << (7 * len)) - 1;

-

-        if (id == 0x08538067)  //Segment ID

-        {

-            if (size == unknown_size)

-                size = -1;

-

-            else if (total < 0)

-                size = -1;

-

-            else if ((pos + size) > total)

-                size = -1;

-

-            pSegment = new (std::nothrow) Segment(

-                                            pReader,

-                                            idpos,

-                                            //elem_size

-                                            pos,

-                                            size);

-

-            if (pSegment == 0)

-                return -1;  //generic error

-

-            return 0;    //success

-        }

-

-        if (size == unknown_size)

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + size) > end)

-            return E_FILE_FORMAT_INVALID;

-

-        pos += size;  //consume payload

-    }

-

-    return E_FILE_FORMAT_INVALID;  //there is no segment

-    //TODO: this might need to be liberalized.  See comments above.

-}

-

-

-long long Segment::ParseHeaders()

-{

-    //Outermost (level 0) segment object has been constructed,

-    //and pos designates start of payload.  We need to find the

-    //inner (level 1) elements.

-    long long total, available;

-

-    const int status = m_pReader->Length(&total, &available);

-

-    if (status < 0) //error

-        return status;

-

-    assert((total < 0) || (available <= total));

-

-    const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;

-    assert((segment_stop < 0) || (total < 0) || (segment_stop <= total));

-    assert((segment_stop < 0) || (m_pos <= segment_stop));

-

-    for (;;)

-    {

-        if ((total >= 0) && (m_pos >= total))

-            break;

-

-        if ((segment_stop >= 0) && (m_pos >= segment_stop))

-            break;

-

-        long long pos = m_pos;

-        const long long element_start = pos;

-

-        if ((pos + 1) > available)

-            return (pos + 1);

-

-        long len;

-        long long result = GetUIntLength(m_pReader, pos, len);

-

-        if (result < 0)  //error

-            return result;

-

-        if (result > 0)  //underflow (weird)

-            return (pos + 1);

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > available)

-            return pos + len;

-

-        const long long idpos = pos;

-        const long long id = ReadUInt(m_pReader, idpos, len);

-

-        if (id < 0)  //error

-            return id;

-

-        if (id == 0x0F43B675)  //Cluster ID

-            break;

-

-        pos += len;  //consume ID

-

-        if ((pos + 1) > available)

-            return (pos + 1);

-

-        //Read Size

-        result = GetUIntLength(m_pReader, pos, len);

-

-        if (result < 0)  //error

-            return result;

-

-        if (result > 0)  //underflow (weird)

-            return (pos + 1);

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > available)

-            return pos + len;

-

-        const long long size = ReadUInt(m_pReader, pos, len);

-

-        if (size < 0)  //error

-            return size;

-

-        pos += len;  //consume length of size of element

-

-        const long long element_size = size + pos - element_start;

-

-        //Pos now points to start of payload

-

-        if ((segment_stop >= 0) && ((pos + size) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        //We read EBML elements either in total or nothing at all.

-

-        if ((pos + size) > available)

-            return pos + size;

-

-        if (id == 0x0549A966)  //Segment Info ID

-        {

-            if (m_pInfo)

-                return E_FILE_FORMAT_INVALID;

-

-            m_pInfo = new (std::nothrow) SegmentInfo(

-                                          this,

-                                          pos,

-                                          size,

-                                          element_start,

-                                          element_size);

-

-            if (m_pInfo == NULL)

-                return -1;

-

-            const long status = m_pInfo->Parse();

-

-            if (status)

-                return status;

-        }

-        else if (id == 0x0654AE6B)  //Tracks ID

-        {

-            if (m_pTracks)

-                return E_FILE_FORMAT_INVALID;

-

-            m_pTracks = new (std::nothrow) Tracks(this,

-                                                  pos,

-                                                  size,

-                                                  element_start,

-                                                  element_size);

-

-            if (m_pTracks == NULL)

-                return -1;

-

-            const long status = m_pTracks->Parse();

-

-            if (status)

-                return status;

-        }

-        else if (id == 0x0C53BB6B)  //Cues ID

-        {

-            if (m_pCues == NULL)

-            {

-                m_pCues = new (std::nothrow) Cues(

-                                                this,

-                                                pos,

-                                                size,

-                                                element_start,

-                                                element_size);

-

-                if (m_pCues == NULL)

-                    return -1;

-            }

-        }

-        else if (id == 0x014D9B74)  //SeekHead ID

-        {

-            if (m_pSeekHead == NULL)

-            {

-                m_pSeekHead = new (std::nothrow) SeekHead(

-                                                    this,

-                                                    pos,

-                                                    size,

-                                                    element_start,

-                                                    element_size);

-

-                if (m_pSeekHead == NULL)

-                    return -1;

-

-                const long status = m_pSeekHead->Parse();

-

-                if (status)

-                    return status;

-            }

-        }

-

-        m_pos = pos + size;  //consume payload

-    }

-

-    assert((segment_stop < 0) || (m_pos <= segment_stop));

-

-    if (m_pInfo == NULL)  //TODO: liberalize this behavior

-        return E_FILE_FORMAT_INVALID;

-

-    if (m_pTracks == NULL)

-        return E_FILE_FORMAT_INVALID;

-

-    return 0;  //success

-}

-

-

-long Segment::LoadCluster(

-    long long& pos,

-    long& len)

-{

-    for (;;)

-    {

-        const long result = DoLoadCluster(pos, len);

-

-        if (result <= 1)

-            return result;

-    }

-}

-

-

-long Segment::DoLoadCluster(

-    long long& pos,

-    long& len)

-{

-    if (m_pos < 0)

-        return DoLoadClusterUnknownSize(pos, len);

-

-    long long total, avail;

-

-    long status = m_pReader->Length(&total, &avail);

-

-    if (status < 0)  //error

-        return status;

-

-    assert((total < 0) || (avail <= total));

-

-    const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;

-

-    long long cluster_off = -1;   //offset relative to start of segment

-    long long cluster_size = -1;  //size of cluster payload

-

-    for (;;)

-    {

-        if ((total >= 0) && (m_pos >= total))

-            return 1;  //no more clusters

-

-        if ((segment_stop >= 0) && (m_pos >= segment_stop))

-            return 1;  //no more clusters

-

-        pos = m_pos;

-

-        //Read ID

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        long long result = GetUIntLength(m_pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long idpos = pos;

-        const long long id = ReadUInt(m_pReader, idpos, len);

-

-        if (id < 0)  //error (or underflow)

-            return static_cast<long>(id);

-

-        pos += len;  //consume ID

-

-        //Read Size

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result = GetUIntLength(m_pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long size = ReadUInt(m_pReader, pos, len);

-

-        if (size < 0)  //error

-            return static_cast<long>(size);

-

-        pos += len;  //consume length of size of element

-

-        //pos now points to start of payload

-

-        if (size == 0)  //weird

-        {

-            m_pos = pos;

-            continue;

-        }

-

-        const long long unknown_size = (1LL << (7 * len)) - 1;

-

-#if 0  //we must handle this to support live webm

-        if (size == unknown_size)

-            return E_FILE_FORMAT_INVALID;  //TODO: allow this

-#endif

-

-        if ((segment_stop >= 0) &&

-            (size != unknown_size) &&

-            ((pos + size) > segment_stop))

-        {

-            return E_FILE_FORMAT_INVALID;

-        }

-

-#if 0  //commented-out, to support incremental cluster parsing

-        len = static_cast<long>(size);

-

-        if ((pos + size) > avail)

-            return E_BUFFER_NOT_FULL;

-#endif

-

-        if (id == 0x0C53BB6B)  //Cues ID

-        {

-            if (size == unknown_size)

-                return E_FILE_FORMAT_INVALID;  //TODO: liberalize

-

-            if (m_pCues == NULL)

-            {

-                const long long element_size = (pos - idpos) + size;

-

-                m_pCues = new Cues(this,

-                                   pos,

-                                   size,

-                                   idpos,

-                                   element_size);

-                assert(m_pCues);  //TODO

-            }

-

-            m_pos = pos + size;  //consume payload

-            continue;

-        }

-

-        if (id != 0x0F43B675)  //Cluster ID

-        {

-            if (size == unknown_size)

-                return E_FILE_FORMAT_INVALID;  //TODO: liberalize

-

-            m_pos = pos + size;  //consume payload

-            continue;

-        }

-

-        //We have a cluster.

-

-        cluster_off = idpos - m_start;  //relative pos

-

-        if (size != unknown_size)

-            cluster_size = size;

-

-        break;

-    }

-

-    assert(cluster_off >= 0);  //have cluster

-

-    long long pos_;

-    long len_;

-

-    status = Cluster::HasBlockEntries(this, cluster_off, pos_, len_);

-

-    if (status < 0) //error, or underflow

-    {

-        pos = pos_;

-        len = len_;

-

-        return status;

-    }

-

-    //status == 0 means "no block entries found"

-    //status > 0 means "found at least one block entry"

-

-    //TODO:

-    //The issue here is that the segment increments its own

-    //pos ptr past the most recent cluster parsed, and then

-    //starts from there to parse the next cluster.  If we

-    //don't know the size of the current cluster, then we

-    //must either parse its payload (as we do below), looking

-    //for the cluster (or cues) ID to terminate the parse.

-    //This isn't really what we want: rather, we really need

-    //a way to create the curr cluster object immediately.

-    //The pity is that cluster::parse can determine its own

-    //boundary, and we largely duplicate that same logic here.

-    //

-    //Maybe we need to get rid of our look-ahead preloading

-    //in source::parse???

-    //

-    //As we're parsing the blocks in the curr cluster

-    //(in cluster::parse), we should have some way to signal

-    //to the segment that we have determined the boundary,

-    //so it can adjust its own segment::m_pos member.

-    //

-    //The problem is that we're asserting in asyncreadinit,

-    //because we adjust the pos down to the curr seek pos,

-    //and the resulting adjusted len is > 2GB.  I'm suspicious

-    //that this is even correct, but even if it is, we can't

-    //be loading that much data in the cache anyway.

-

-    const long idx = m_clusterCount;

-

-    if (m_clusterPreloadCount > 0)

-    {

-        assert(idx < m_clusterSize);

-

-        Cluster* const pCluster = m_clusters[idx];

-        assert(pCluster);

-        assert(pCluster->m_index < 0);

-

-        const long long off = pCluster->GetPosition();

-        assert(off >= 0);

-

-        if (off == cluster_off)  //preloaded already

-        {

-            if (status == 0)  //no entries found

-                return E_FILE_FORMAT_INVALID;

-

-            if (cluster_size >= 0)

-                pos += cluster_size;

-            else

-            {

-                const long long element_size = pCluster->GetElementSize();

-

-                if (element_size <= 0)

-                    return E_FILE_FORMAT_INVALID;  //TODO: handle this case

-

-                pos = pCluster->m_element_start + element_size;

-            }

-

-            pCluster->m_index = idx;  //move from preloaded to loaded

-            ++m_clusterCount;

-            --m_clusterPreloadCount;

-

-            m_pos = pos;  //consume payload

-            assert((segment_stop < 0) || (m_pos <= segment_stop));

-

-            return 0;  //success

-        }

-    }

-

-    if (status == 0)  //no entries found

-    {

-        if (cluster_size < 0)

-            return E_FILE_FORMAT_INVALID;  //TODO: handle this

-

-        pos += cluster_size;

-

-        if ((total >= 0) && (pos >= total))

-        {

-            m_pos = total;

-            return 1;  //no more clusters

-        }

-

-        if ((segment_stop >= 0) && (pos >= segment_stop))

-        {

-            m_pos = segment_stop;

-            return 1;  //no more clusters

-        }

-

-        m_pos = pos;

-        return 2;  //try again

-    }

-

-    //status > 0 means we have an entry

-

-    Cluster* const pCluster = Cluster::Create(this,

-                                              idx,

-                                              cluster_off);

-                                              //element_size);

-    assert(pCluster);

-

-    AppendCluster(pCluster);

-    assert(m_clusters);

-    assert(idx < m_clusterSize);

-    assert(m_clusters[idx] == pCluster);

-

-    if (cluster_size >= 0)

-    {

-        pos += cluster_size;

-

-        m_pos = pos;

-        assert((segment_stop < 0) || (m_pos <= segment_stop));

-

-        return 0;

-    }

-

-    m_pUnknownSize = pCluster;

-    m_pos = -pos;

-

-    return 0;  //partial success, since we have a new cluster

-

-    //status == 0 means "no block entries found"

-

-    //pos designates start of payload

-    //m_pos has NOT been adjusted yet (in case we need to come back here)

-

-#if 0

-

-    if (cluster_size < 0)  //unknown size

-    {

-        const long long payload_pos = pos;  //absolute pos of cluster payload

-

-        for (;;)  //determine cluster size

-        {

-            if ((total >= 0) && (pos >= total))

-                break;

-

-            if ((segment_stop >= 0) && (pos >= segment_stop))

-                break;  //no more clusters

-

-            //Read ID

-

-            if ((pos + 1) > avail)

-            {

-                len = 1;

-                return E_BUFFER_NOT_FULL;

-            }

-

-            long long result = GetUIntLength(m_pReader, pos, len);

-

-            if (result < 0)  //error

-                return static_cast<long>(result);

-

-            if (result > 0)  //weird

-                return E_BUFFER_NOT_FULL;

-

-            if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-                return E_FILE_FORMAT_INVALID;

-

-            if ((pos + len) > avail)

-                return E_BUFFER_NOT_FULL;

-

-            const long long idpos = pos;

-            const long long id = ReadUInt(m_pReader, idpos, len);

-

-            if (id < 0)  //error (or underflow)

-                return static_cast<long>(id);

-

-            //This is the distinguished set of ID's we use to determine

-            //that we have exhausted the sub-element's inside the cluster

-            //whose ID we parsed earlier.

-

-            if (id == 0x0F43B675)  //Cluster ID

-                break;

-

-            if (id == 0x0C53BB6B)  //Cues ID

-                break;

-

-            switch (id)

-            {

-                case 0x20:  //BlockGroup

-                case 0x23:  //Simple Block

-                case 0x67:  //TimeCode

-                case 0x2B:  //PrevSize

-                    break;

-

-                default:

-                    assert(false);

-                    break;

-            }

-

-            pos += len;  //consume ID (of sub-element)

-

-            //Read Size

-

-            if ((pos + 1) > avail)

-            {

-                len = 1;

-                return E_BUFFER_NOT_FULL;

-            }

-

-            result = GetUIntLength(m_pReader, pos, len);

-

-            if (result < 0)  //error

-                return static_cast<long>(result);

-

-            if (result > 0)  //weird

-                return E_BUFFER_NOT_FULL;

-

-            if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-                return E_FILE_FORMAT_INVALID;

-

-            if ((pos + len) > avail)

-                return E_BUFFER_NOT_FULL;

-

-            const long long size = ReadUInt(m_pReader, pos, len);

-

-            if (size < 0)  //error

-                return static_cast<long>(size);

-

-            pos += len;  //consume size field of element

-

-            //pos now points to start of sub-element's payload

-

-            if (size == 0)  //weird

-                continue;

-

-            const long long unknown_size = (1LL << (7 * len)) - 1;

-

-            if (size == unknown_size)

-                return E_FILE_FORMAT_INVALID;  //not allowed for sub-elements

-

-            if ((segment_stop >= 0) && ((pos + size) > segment_stop))  //weird

-                return E_FILE_FORMAT_INVALID;

-

-            pos += size;  //consume payload of sub-element

-            assert((segment_stop < 0) || (pos <= segment_stop));

-        }  //determine cluster size

-

-        cluster_size = pos - payload_pos;

-        assert(cluster_size >= 0);

-

-        pos = payload_pos;  //reset and re-parse original cluster

-    }

-

-    if (m_clusterPreloadCount > 0)

-    {

-        assert(idx < m_clusterSize);

-

-        Cluster* const pCluster = m_clusters[idx];

-        assert(pCluster);

-        assert(pCluster->m_index < 0);

-

-        const long long off = pCluster->GetPosition();

-        assert(off >= 0);

-

-        if (off == cluster_off)  //preloaded already

-            return E_FILE_FORMAT_INVALID;  //subtle

-    }

-

-    m_pos = pos + cluster_size;  //consume payload

-    assert((segment_stop < 0) || (m_pos <= segment_stop));

-

-    return 2;     //try to find another cluster

-

-#endif

-

-}

-

-

-long Segment::DoLoadClusterUnknownSize(

-    long long& pos,

-    long& len)

-{

-    assert(m_pos < 0);

-    assert(m_pUnknownSize);

-

-#if 0

-    assert(m_pUnknownSize->GetElementSize() < 0);  //TODO: verify this

-

-    const long long element_start = m_pUnknownSize->m_element_start;

-

-    pos = -m_pos;

-    assert(pos > element_start);

-

-    //We have already consumed the (cluster) ID and size fields.

-    //We just need to consume the blocks and other sub-elements

-    //of this cluster, until we discover the boundary.

-

-    long long total, avail;

-

-    long status = m_pReader->Length(&total, &avail);

-

-    if (status < 0)  //error

-        return status;

-

-    assert((total < 0) || (avail <= total));

-

-    const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;

-

-    long long element_size = -1;

-

-    for (;;)  //determine cluster size

-    {

-        if ((total >= 0) && (pos >= total))

-        {

-            element_size = total - element_start;

-            assert(element_size > 0);

-

-            break;

-        }

-

-        if ((segment_stop >= 0) && (pos >= segment_stop))

-        {

-            element_size = segment_stop - element_start;

-            assert(element_size > 0);

-

-            break;

-        }

-

-        //Read ID

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        long long result = GetUIntLength(m_pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long idpos = pos;

-        const long long id = ReadUInt(m_pReader, idpos, len);

-

-        if (id < 0)  //error (or underflow)

-            return static_cast<long>(id);

-

-        //This is the distinguished set of ID's we use to determine

-        //that we have exhausted the sub-element's inside the cluster

-        //whose ID we parsed earlier.

-

-        if ((id == 0x0F43B675) || (id == 0x0C53BB6B)) //Cluster ID or Cues ID

-        {

-            element_size = pos - element_start;

-            assert(element_size > 0);

-

-            break;

-        }

-

-#ifdef _DEBUG

-        switch (id)

-        {

-            case 0x20:  //BlockGroup

-            case 0x23:  //Simple Block

-            case 0x67:  //TimeCode

-            case 0x2B:  //PrevSize

-                break;

-

-            default:

-                assert(false);

-                break;

-        }

-#endif

-

-        pos += len;  //consume ID (of sub-element)

-

-        //Read Size

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result = GetUIntLength(m_pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long size = ReadUInt(m_pReader, pos, len);

-

-        if (size < 0)  //error

-            return static_cast<long>(size);

-

-        pos += len;  //consume size field of element

-

-        //pos now points to start of sub-element's payload

-

-        if (size == 0)  //weird

-            continue;

-

-        const long long unknown_size = (1LL << (7 * len)) - 1;

-

-        if (size == unknown_size)

-            return E_FILE_FORMAT_INVALID;  //not allowed for sub-elements

-

-        if ((segment_stop >= 0) && ((pos + size) > segment_stop))  //weird

-            return E_FILE_FORMAT_INVALID;

-

-        pos += size;  //consume payload of sub-element

-        assert((segment_stop < 0) || (pos <= segment_stop));

-    }  //determine cluster size

-

-    assert(element_size >= 0);

-

-    m_pos = element_start + element_size;

-    m_pUnknownSize = 0;

-

-    return 2;  //continue parsing

-#else

-    const long status = m_pUnknownSize->Parse(pos, len);

-

-    if (status < 0)  //error or underflow

-        return status;

-

-    if (status == 0)  //parsed a block

-        return 2;     //continue parsing

-

-    assert(status > 0);   //nothing left to parse of this cluster

-

-    const long long start = m_pUnknownSize->m_element_start;

-

-    const long long size = m_pUnknownSize->GetElementSize();

-    assert(size >= 0);

-

-    pos = start + size;

-    m_pos = pos;

-

-    m_pUnknownSize = 0;

-

-    return 2;  //continue parsing

-#endif

-}

-

-

-void Segment::AppendCluster(Cluster* pCluster)

-{

-    assert(pCluster);

-    assert(pCluster->m_index >= 0);

-

-    const long count = m_clusterCount + m_clusterPreloadCount;

-

-    long& size = m_clusterSize;

-    assert(size >= count);

-

-    const long idx = pCluster->m_index;

-    assert(idx == m_clusterCount);

-

-    if (count >= size)

-    {

-        const long n = (size <= 0) ? 2048 : 2*size;

-

-        Cluster** const qq = new Cluster*[n];

-        Cluster** q = qq;

-

-        Cluster** p = m_clusters;

-        Cluster** const pp = p + count;

-

-        while (p != pp)

-            *q++ = *p++;

-

-        delete[] m_clusters;

-

-        m_clusters = qq;

-        size = n;

-    }

-

-    if (m_clusterPreloadCount > 0)

-    {

-        assert(m_clusters);

-

-        Cluster** const p = m_clusters + m_clusterCount;

-        assert(*p);

-        assert((*p)->m_index < 0);

-

-        Cluster** q = p + m_clusterPreloadCount;

-        assert(q < (m_clusters + size));

-

-        for (;;)

-        {

-            Cluster** const qq = q - 1;

-            assert((*qq)->m_index < 0);

-

-            *q = *qq;

-            q = qq;

-

-            if (q == p)

-                break;

-        }

-    }

-

-    m_clusters[idx] = pCluster;

-    ++m_clusterCount;

-}

-

-

-void Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx)

-{

-    assert(pCluster);

-    assert(pCluster->m_index < 0);

-    assert(idx >= m_clusterCount);

-

-    const long count = m_clusterCount + m_clusterPreloadCount;

-

-    long& size = m_clusterSize;

-    assert(size >= count);

-

-    if (count >= size)

-    {

-        const long n = (size <= 0) ? 2048 : 2*size;

-

-        Cluster** const qq = new Cluster*[n];

-        Cluster** q = qq;

-

-        Cluster** p = m_clusters;

-        Cluster** const pp = p + count;

-

-        while (p != pp)

-            *q++ = *p++;

-

-        delete[] m_clusters;

-

-        m_clusters = qq;

-        size = n;

-    }

-

-    assert(m_clusters);

-

-    Cluster** const p = m_clusters + idx;

-

-    Cluster** q = m_clusters + count;

-    assert(q >= p);

-    assert(q < (m_clusters + size));

-

-    while (q > p)

-    {

-        Cluster** const qq = q - 1;

-        assert((*qq)->m_index < 0);

-

-        *q = *qq;

-        q = qq;

-    }

-

-    m_clusters[idx] = pCluster;

-    ++m_clusterPreloadCount;

-}

-

-

-long Segment::Load()

-{

-    assert(m_clusters == NULL);

-    assert(m_clusterSize == 0);

-    assert(m_clusterCount == 0);

-    //assert(m_size >= 0);

-

-    //Outermost (level 0) segment object has been constructed,

-    //and pos designates start of payload.  We need to find the

-    //inner (level 1) elements.

-

-    const long long header_status = ParseHeaders();

-

-    if (header_status < 0)  //error

-        return static_cast<long>(header_status);

-

-    if (header_status > 0)  //underflow

-        return E_BUFFER_NOT_FULL;

-

-    assert(m_pInfo);

-    assert(m_pTracks);

-

-    for (;;)

-    {

-        const int status = LoadCluster();

-

-        if (status < 0)  //error

-            return status;

-

-        if (status >= 1)  //no more clusters

-            return 0;

-    }

-}

-

-

-SeekHead::SeekHead(

-    Segment* pSegment,

-    long long start,

-    long long size_,

-    long long element_start,

-    long long element_size) :

-    m_pSegment(pSegment),

-    m_start(start),

-    m_size(size_),

-    m_element_start(element_start),

-    m_element_size(element_size),

-    m_entries(0),

-    m_entry_count(0),

-    m_void_elements(0),

-    m_void_element_count(0)

-{

-}

-

-

-SeekHead::~SeekHead()

-{

-    delete[] m_entries;

-    delete[] m_void_elements;

-}

-

-

-long SeekHead::Parse()

-{

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    long long pos = m_start;

-    const long long stop = m_start + m_size;

-

-    //first count the seek head entries

-

-    int entry_count = 0;

-    int void_element_count = 0;

-

-    while (pos < stop)

-    {

-        long long id, size;

-

-        const long status = ParseElementHeader(

-                                pReader,

-                                pos,

-                                stop,

-                                id,

-                                size);

-

-        if (status < 0)  //error

-            return status;

-

-        if (id == 0x0DBB)  //SeekEntry ID

-            ++entry_count;

-        else if (id == 0x6C)  //Void ID

-            ++void_element_count;

-

-        pos += size;  //consume payload

-        assert(pos <= stop);

-    }

-

-    assert(pos == stop);

-

-    m_entries = new (std::nothrow) Entry[entry_count];

-

-    if (m_entries == NULL)

-        return -1;

-

-    m_void_elements = new (std::nothrow) VoidElement[void_element_count];

-

-    if (m_void_elements == NULL)

-        return -1;

-

-    //now parse the entries and void elements

-

-    Entry* pEntry = m_entries;

-    VoidElement* pVoidElement = m_void_elements;

-

-    pos = m_start;

-

-    while (pos < stop)

-    {

-        const long long idpos = pos;

-

-        long long id, size;

-

-        const long status = ParseElementHeader(

-                                pReader,

-                                pos,

-                                stop,

-                                id,

-                                size);

-

-        if (status < 0)  //error

-            return status;

-

-        if (id == 0x0DBB)  //SeekEntry ID

-        {

-            if (ParseEntry(pReader, pos, size, pEntry))

-            {

-                Entry& e = *pEntry++;

-

-                e.element_start = idpos;

-                e.element_size = (pos + size) - idpos;

-            }

-        }

-        else if (id == 0x6C)  //Void ID

-        {

-            VoidElement& e = *pVoidElement++;

-

-            e.element_start = idpos;

-            e.element_size = (pos + size) - idpos;

-        }

-

-        pos += size;  //consume payload

-        assert(pos <= stop);

-    }

-

-    assert(pos == stop);

-

-    ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);

-    assert(count_ >= 0);

-    assert(count_ <= entry_count);

-

-    m_entry_count = static_cast<int>(count_);

-

-    count_ = ptrdiff_t(pVoidElement - m_void_elements);

-    assert(count_ >= 0);

-    assert(count_ <= void_element_count);

-

-    m_void_element_count = static_cast<int>(count_);

-

-    return 0;

-}

-

-

-int SeekHead::GetCount() const

-{

-    return m_entry_count;

-}

-

-const SeekHead::Entry* SeekHead::GetEntry(int idx) const

-{

-    if (idx < 0)

-        return 0;

-

-    if (idx >= m_entry_count)

-        return 0;

-

-    return m_entries + idx;

-}

-

-int SeekHead::GetVoidElementCount() const

-{

-    return m_void_element_count;

-}

-

-const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const

-{

-    if (idx < 0)

-        return 0;

-

-    if (idx >= m_void_element_count)

-        return 0;

-

-    return m_void_elements + idx;

-}

-

-

-#if 0

-void Segment::ParseCues(long long off)

-{

-    if (m_pCues)

-        return;

-

-    //odbgstream os;

-    //os << "Segment::ParseCues (begin)" << endl;

-

-    long long pos = m_start + off;

-    const long long element_start = pos;

-    const long long stop = m_start + m_size;

-

-    long len;

-

-    long long result = GetUIntLength(m_pReader, pos, len);

-    assert(result == 0);

-    assert((pos + len) <= stop);

-

-    const long long idpos = pos;

-

-    const long long id = ReadUInt(m_pReader, idpos, len);

-    assert(id == 0x0C53BB6B);  //Cues ID

-

-    pos += len;  //consume ID

-    assert(pos < stop);

-

-    //Read Size

-

-    result = GetUIntLength(m_pReader, pos, len);

-    assert(result == 0);

-    assert((pos + len) <= stop);

-

-    const long long size = ReadUInt(m_pReader, pos, len);

-    assert(size >= 0);

-

-    pos += len;  //consume length of size of element

-    assert((pos + size) <= stop);

-

-    const long long element_size = size + pos - element_start;

-

-    //Pos now points to start of payload

-

-    m_pCues = new Cues(this, pos, size, element_start, element_size);

-    assert(m_pCues);  //TODO

-

-    //os << "Segment::ParseCues (end)" << endl;

-}

-#else

-long Segment::ParseCues(

-    long long off,

-    long long& pos,

-    long& len)

-{

-    if (m_pCues)

-        return 0;  //success

-

-    if (off < 0)

-        return -1;

-

-    long long total, avail;

-

-    const int status = m_pReader->Length(&total, &avail);

-

-    if (status < 0)  //error

-        return status;

-

-    assert((total < 0) || (avail <= total));

-

-    pos = m_start + off;

-

-    if ((total < 0) || (pos >= total))

-        return 1;  //don't bother parsing cues

-

-    const long long element_start = pos;

-    const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;

-

-    if ((pos + 1) > avail)

-    {

-        len = 1;

-        return E_BUFFER_NOT_FULL;

-    }

-

-    long long result = GetUIntLength(m_pReader, pos, len);

-

-    if (result < 0)  //error

-        return static_cast<long>(result);

-

-    if (result > 0) //underflow (weird)

-    {

-        len = 1;

-        return E_BUFFER_NOT_FULL;

-    }

-

-    if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-        return E_FILE_FORMAT_INVALID;

-

-    if ((pos + len) > avail)

-        return E_BUFFER_NOT_FULL;

-

-    const long long idpos = pos;

-

-    const long long id = ReadUInt(m_pReader, idpos, len);

-

-    if (id != 0x0C53BB6B)  //Cues ID

-        return E_FILE_FORMAT_INVALID;

-

-    pos += len;  //consume ID

-    assert((segment_stop < 0) || (pos <= segment_stop));

-

-    //Read Size

-

-    if ((pos + 1) > avail)

-    {

-        len = 1;

-        return E_BUFFER_NOT_FULL;

-    }

-

-    result = GetUIntLength(m_pReader, pos, len);

-

-    if (result < 0)  //error

-        return static_cast<long>(result);

-

-    if (result > 0) //underflow (weird)

-    {

-        len = 1;

-        return E_BUFFER_NOT_FULL;

-    }

-

-    if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-        return E_FILE_FORMAT_INVALID;

-

-    if ((pos + len) > avail)

-        return E_BUFFER_NOT_FULL;

-

-    const long long size = ReadUInt(m_pReader, pos, len);

-

-    if (size < 0)  //error

-        return static_cast<long>(size);

-

-    if (size == 0)  //weird, although technically not illegal

-        return 1;   //done

-

-    pos += len;  //consume length of size of element

-    assert((segment_stop < 0) || (pos <= segment_stop));

-

-    //Pos now points to start of payload

-

-    const long long element_stop = pos + size;

-

-    if ((segment_stop >= 0) && (element_stop > segment_stop))

-        return E_FILE_FORMAT_INVALID;

-

-    if ((total >= 0) && (element_stop > total))

-        return 1;  //don't bother parsing anymore

-

-    len = static_cast<long>(size);

-

-    if (element_stop > avail)

-        return E_BUFFER_NOT_FULL;

-

-    const long long element_size = element_stop - element_start;

-

-    m_pCues = new (std::nothrow) Cues(

-                                    this,

-                                    pos,

-                                    size,

-                                    element_start,

-                                    element_size);

-    assert(m_pCues);  //TODO

-

-    return 0;  //success

-}

-#endif

-

-

-#if 0

-void Segment::ParseSeekEntry(

-    long long start,

-    long long size_)

-{

-    long long pos = start;

-

-    const long long stop = start + size_;

-

-    long len;

-

-    const long long seekIdId = ReadUInt(m_pReader, pos, len);

-    //seekIdId;

-    assert(seekIdId == 0x13AB);  //SeekID ID

-    assert((pos + len) <= stop);

-

-    pos += len;  //consume id

-

-    const long long seekIdSize = ReadUInt(m_pReader, pos, len);

-    assert(seekIdSize >= 0);

-    assert((pos + len) <= stop);

-

-    pos += len;  //consume size

-

-    const long long seekId = ReadUInt(m_pReader, pos, len);  //payload

-    assert(seekId >= 0);

-    assert(len == seekIdSize);

-    assert((pos + len) <= stop);

-

-    pos += seekIdSize;  //consume payload

-

-    const long long seekPosId = ReadUInt(m_pReader, pos, len);

-    //seekPosId;

-    assert(seekPosId == 0x13AC);  //SeekPos ID

-    assert((pos + len) <= stop);

-

-    pos += len;  //consume id

-

-    const long long seekPosSize = ReadUInt(m_pReader, pos, len);

-    assert(seekPosSize >= 0);

-    assert((pos + len) <= stop);

-

-    pos += len;  //consume size

-    assert((pos + seekPosSize) <= stop);

-

-    const long long seekOff = UnserializeUInt(m_pReader, pos, seekPosSize);

-    assert(seekOff >= 0);

-    assert(seekOff < m_size);

-

-    pos += seekPosSize;  //consume payload

-    assert(pos == stop);

-

-    const long long seekPos = m_start + seekOff;

-    assert(seekPos < (m_start + m_size));

-

-    if (seekId == 0x0C53BB6B)  //Cues ID

-        ParseCues(seekOff);

-}

-#else

-bool SeekHead::ParseEntry(

-    IMkvReader* pReader,

-    long long start,

-    long long size_,

-    Entry* pEntry)

-{

-    if (size_ <= 0)

-        return false;

-

-    long long pos = start;

-    const long long stop = start + size_;

-

-    long len;

-

-    //parse the container for the level-1 element ID

-

-    const long long seekIdId = ReadUInt(pReader, pos, len);

-    //seekIdId;

-

-    if (seekIdId != 0x13AB)  //SeekID ID

-        return false;

-

-    if ((pos + len) > stop)

-        return false;

-

-    pos += len;  //consume SeekID id

-

-    const long long seekIdSize = ReadUInt(pReader, pos, len);

-

-    if (seekIdSize <= 0)

-        return false;

-

-    if ((pos + len) > stop)

-        return false;

-

-    pos += len;  //consume size of field

-

-    if ((pos + seekIdSize) > stop)

-        return false;

-

-    //Note that the SeekId payload really is serialized

-    //as a "Matroska integer", not as a plain binary value.

-    //In fact, Matroska requires that ID values in the

-    //stream exactly match the binary representation as listed

-    //in the Matroska specification.

-    //

-    //This parser is more liberal, and permits IDs to have

-    //any width.  (This could make the representation in the stream

-    //different from what's in the spec, but it doesn't matter here,

-    //since we always normalize "Matroska integer" values.)

-

-    pEntry->id = ReadUInt(pReader, pos, len);  //payload

-

-    if (pEntry->id <= 0)

-        return false;

-

-    if (len != seekIdSize)

-        return false;

-

-    pos += seekIdSize;  //consume SeekID payload

-

-    const long long seekPosId = ReadUInt(pReader, pos, len);

-

-    if (seekPosId != 0x13AC)  //SeekPos ID

-        return false;

-

-    if ((pos + len) > stop)

-        return false;

-

-    pos += len;  //consume id

-

-    const long long seekPosSize = ReadUInt(pReader, pos, len);

-

-    if (seekPosSize <= 0)

-        return false;

-

-    if ((pos + len) > stop)

-        return false;

-

-    pos += len;  //consume size

-

-    if ((pos + seekPosSize) > stop)

-        return false;

-

-    pEntry->pos = UnserializeUInt(pReader, pos, seekPosSize);

-

-    if (pEntry->pos < 0)

-        return false;

-

-    pos += seekPosSize;  //consume payload

-

-    if (pos != stop)

-        return false;

-

-    return true;

-}

-#endif

-

-

-Cues::Cues(

-    Segment* pSegment,

-    long long start_,

-    long long size_,

-    long long element_start,

-    long long element_size) :

-    m_pSegment(pSegment),

-    m_start(start_),

-    m_size(size_),

-    m_element_start(element_start),

-    m_element_size(element_size),

-    m_cue_points(NULL),

-    m_count(0),

-    m_preload_count(0),

-    m_pos(start_)

-{

-}

-

-

-Cues::~Cues()

-{

-    const long n = m_count + m_preload_count;

-

-    CuePoint** p = m_cue_points;

-    CuePoint** const q = p + n;

-

-    while (p != q)

-    {

-        CuePoint* const pCP = *p++;

-        assert(pCP);

-

-        delete pCP;

-    }

-

-    delete[] m_cue_points;

-}

-

-

-long Cues::GetCount() const

-{

-    if (m_cue_points == NULL)

-        return -1;

-

-    return m_count;  //TODO: really ignore preload count?

-}

-

-

-bool Cues::DoneParsing() const

-{

-    const long long stop = m_start + m_size;

-    return (m_pos >= stop);

-}

-

-

-void Cues::Init() const

-{

-    if (m_cue_points)

-        return;

-

-    assert(m_count == 0);

-    assert(m_preload_count == 0);

-

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    const long long stop = m_start + m_size;

-    long long pos = m_start;

-

-    long cue_points_size = 0;

-

-    while (pos < stop)

-    {

-        const long long idpos = pos;

-

-        long len;

-

-        const long long id = ReadUInt(pReader, pos, len);

-        assert(id >= 0);  //TODO

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume ID

-

-        const long long size = ReadUInt(pReader, pos, len);

-        assert(size >= 0);

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume Size field

-        assert((pos + size) <= stop);

-

-        if (id == 0x3B)  //CuePoint ID

-            PreloadCuePoint(cue_points_size, idpos);

-

-        pos += size;  //consume payload

-        assert(pos <= stop);

-    }

-}

-

-

-void Cues::PreloadCuePoint(

-    long& cue_points_size,

-    long long pos) const

-{

-    assert(m_count == 0);

-

-    if (m_preload_count >= cue_points_size)

-    {

-        const long n = (cue_points_size <= 0) ? 2048 : 2*cue_points_size;

-

-        CuePoint** const qq = new CuePoint*[n];

-        CuePoint** q = qq;  //beginning of target

-

-        CuePoint** p = m_cue_points;                //beginning of source

-        CuePoint** const pp = p + m_preload_count;  //end of source

-

-        while (p != pp)

-            *q++ = *p++;

-

-        delete[] m_cue_points;

-

-        m_cue_points = qq;

-        cue_points_size = n;

-    }

-

-    CuePoint* const pCP = new CuePoint(m_preload_count, pos);

-    m_cue_points[m_preload_count++] = pCP;

-}

-

-

-bool Cues::LoadCuePoint() const

-{

-    //odbgstream os;

-    //os << "Cues::LoadCuePoint" << endl;

-

-    const long long stop = m_start + m_size;

-

-    if (m_pos >= stop)

-        return false;  //nothing else to do

-

-    Init();

-

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    while (m_pos < stop)

-    {

-        const long long idpos = m_pos;

-

-        long len;

-

-        const long long id = ReadUInt(pReader, m_pos, len);

-        assert(id >= 0);  //TODO

-        assert((m_pos + len) <= stop);

-

-        m_pos += len;  //consume ID

-

-        const long long size = ReadUInt(pReader, m_pos, len);

-        assert(size >= 0);

-        assert((m_pos + len) <= stop);

-

-        m_pos += len;  //consume Size field

-        assert((m_pos + size) <= stop);

-

-        if (id != 0x3B)  //CuePoint ID

-        {

-            m_pos += size;  //consume payload

-            assert(m_pos <= stop);

-

-            continue;

-        }

-

-        assert(m_preload_count > 0);

-

-        CuePoint* const pCP = m_cue_points[m_count];

-        assert(pCP);

-        assert((pCP->GetTimeCode() >= 0) || (-pCP->GetTimeCode() == idpos));

-

-        pCP->Load(pReader);

-        ++m_count;

-        --m_preload_count;

-

-        m_pos += size;  //consume payload

-        assert(m_pos <= stop);

-

-        return true;  //yes, we loaded a cue point

-    }

-

-    //return (m_pos < stop);

-    return false;  //no, we did not load a cue point

-}

-

-

-bool Cues::Find(

-    long long time_ns,

-    const Track* pTrack,

-    const CuePoint*& pCP,

-    const CuePoint::TrackPosition*& pTP) const

-{

-    assert(time_ns >= 0);

-    assert(pTrack);

-

-#if 0

-    LoadCuePoint();  //establish invariant

-

-    assert(m_cue_points);

-    assert(m_count > 0);

-

-    CuePoint** const ii = m_cue_points;

-    CuePoint** i = ii;

-

-    CuePoint** const jj = ii + m_count + m_preload_count;

-    CuePoint** j = jj;

-

-    pCP = *i;

-    assert(pCP);

-

-    if (time_ns <= pCP->GetTime(m_pSegment))

-    {

-        pTP = pCP->Find(pTrack);

-        return (pTP != NULL);

-    }

-

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    while (i < j)

-    {

-        //INVARIANT:

-        //[ii, i) <= time_ns

-        //[i, j)  ?

-        //[j, jj) > time_ns

-

-        CuePoint** const k = i + (j - i) / 2;

-        assert(k < jj);

-

-        CuePoint* const pCP = *k;

-        assert(pCP);

-

-        pCP->Load(pReader);

-

-        const long long t = pCP->GetTime(m_pSegment);

-

-        if (t <= time_ns)

-            i = k + 1;

-        else

-            j = k;

-

-        assert(i <= j);

-    }

-

-    assert(i == j);

-    assert(i <= jj);

-    assert(i > ii);

-

-    pCP = *--i;

-    assert(pCP);

-    assert(pCP->GetTime(m_pSegment) <= time_ns);

-#else

-    if (m_cue_points == NULL)

-        return false;

-

-    if (m_count == 0)

-        return false;

-

-    CuePoint** const ii = m_cue_points;

-    CuePoint** i = ii;

-

-    CuePoint** const jj = ii + m_count;

-    CuePoint** j = jj;

-

-    pCP = *i;

-    assert(pCP);

-

-    if (time_ns <= pCP->GetTime(m_pSegment))

-    {

-        pTP = pCP->Find(pTrack);

-        return (pTP != NULL);

-    }

-

-    while (i < j)

-    {

-        //INVARIANT:

-        //[ii, i) <= time_ns

-        //[i, j)  ?

-        //[j, jj) > time_ns

-

-        CuePoint** const k = i + (j - i) / 2;

-        assert(k < jj);

-

-        CuePoint* const pCP = *k;

-        assert(pCP);

-

-        const long long t = pCP->GetTime(m_pSegment);

-

-        if (t <= time_ns)

-            i = k + 1;

-        else

-            j = k;

-

-        assert(i <= j);

-    }

-

-    assert(i == j);

-    assert(i <= jj);

-    assert(i > ii);

-

-    pCP = *--i;

-    assert(pCP);

-    assert(pCP->GetTime(m_pSegment) <= time_ns);

-#endif

-

-    //TODO: here and elsewhere, it's probably not correct to search

-    //for the cue point with this time, and then search for a matching

-    //track.  In principle, the matching track could be on some earlier

-    //cue point, and with our current algorithm, we'd miss it.  To make

-    //this bullet-proof, we'd need to create a secondary structure,

-    //with a list of cue points that apply to a track, and then search

-    //that track-based structure for a matching cue point.

-

-    pTP = pCP->Find(pTrack);

-    return (pTP != NULL);

-}

-

-

-#if 0

-bool Cues::FindNext(

-    long long time_ns,

-    const Track* pTrack,

-    const CuePoint*& pCP,

-    const CuePoint::TrackPosition*& pTP) const

-{

-    pCP = 0;

-    pTP = 0;

-

-    if (m_count == 0)

-        return false;

-

-    assert(m_cue_points);

-

-    const CuePoint* const* const ii = m_cue_points;

-    const CuePoint* const* i = ii;

-

-    const CuePoint* const* const jj = ii + m_count;

-    const CuePoint* const* j = jj;

-

-    while (i < j)

-    {

-        //INVARIANT:

-        //[ii, i) <= time_ns

-        //[i, j)  ?

-        //[j, jj) > time_ns

-

-        const CuePoint* const* const k = i + (j - i) / 2;

-        assert(k < jj);

-

-        pCP = *k;

-        assert(pCP);

-

-        const long long t = pCP->GetTime(m_pSegment);

-

-        if (t <= time_ns)

-            i = k + 1;

-        else

-            j = k;

-

-        assert(i <= j);

-    }

-

-    assert(i == j);

-    assert(i <= jj);

-

-    if (i >= jj)  //time_ns is greater than max cue point

-        return false;

-

-    pCP = *i;

-    assert(pCP);

-    assert(pCP->GetTime(m_pSegment) > time_ns);

-

-    pTP = pCP->Find(pTrack);

-    return (pTP != NULL);

-}

-#endif

-

-

-const CuePoint* Cues::GetFirst() const

-{

-    if (m_cue_points == NULL)

-        return NULL;

-

-    if (m_count == 0)

-        return NULL;

-

-#if 0

-    LoadCuePoint();  //init cues

-

-    const size_t count = m_count + m_preload_count;

-

-    if (count == 0)  //weird

-        return NULL;

-#endif

-

-    CuePoint* const* const pp = m_cue_points;

-    assert(pp);

-

-    CuePoint* const pCP = pp[0];

-    assert(pCP);

-    assert(pCP->GetTimeCode() >= 0);

-

-    return pCP;

-}

-

-

-const CuePoint* Cues::GetLast() const

-{

-    if (m_cue_points == NULL)

-        return NULL;

-

-    if (m_count <= 0)

-        return NULL;

-

-#if 0

-    LoadCuePoint();  //init cues

-

-    const size_t count = m_count + m_preload_count;

-

-    if (count == 0)  //weird

-        return NULL;

-

-    const size_t index = count - 1;

-

-    CuePoint* const* const pp = m_cue_points;

-    assert(pp);

-

-    CuePoint* const pCP = pp[index];

-    assert(pCP);

-

-    pCP->Load(m_pSegment->m_pReader);

-    assert(pCP->GetTimeCode() >= 0);

-#else

-    const long index = m_count - 1;

-

-    CuePoint* const* const pp = m_cue_points;

-    assert(pp);

-

-    CuePoint* const pCP = pp[index];

-    assert(pCP);

-    assert(pCP->GetTimeCode() >= 0);

-#endif

-

-    return pCP;

-}

-

-

-const CuePoint* Cues::GetNext(const CuePoint* pCurr) const

-{

-    if (pCurr == NULL)

-        return NULL;

-

-    assert(pCurr->GetTimeCode() >= 0);

-    assert(m_cue_points);

-    assert(m_count >= 1);

-

-#if 0

-    const size_t count = m_count + m_preload_count;

-

-    size_t index = pCurr->m_index;

-    assert(index < count);

-

-    CuePoint* const* const pp = m_cue_points;

-    assert(pp);

-    assert(pp[index] == pCurr);

-

-    ++index;

-

-    if (index >= count)

-        return NULL;

-

-    CuePoint* const pNext = pp[index];

-    assert(pNext);

-

-    pNext->Load(m_pSegment->m_pReader);

-#else

-    long index = pCurr->m_index;

-    assert(index < m_count);

-

-    CuePoint* const* const pp = m_cue_points;

-    assert(pp);

-    assert(pp[index] == pCurr);

-

-    ++index;

-

-    if (index >= m_count)

-        return NULL;

-

-    CuePoint* const pNext = pp[index];

-    assert(pNext);

-    assert(pNext->GetTimeCode() >= 0);

-#endif

-

-    return pNext;

-}

-

-

-const BlockEntry* Cues::GetBlock(

-    const CuePoint* pCP,

-    const CuePoint::TrackPosition* pTP) const

-{

-    if (pCP == NULL)

-        return NULL;

-

-    if (pTP == NULL)

-        return NULL;

-

-    return m_pSegment->GetBlock(*pCP, *pTP);

-}

-

-

-const BlockEntry* Segment::GetBlock(

-    const CuePoint& cp,

-    const CuePoint::TrackPosition& tp)

-{

-    Cluster** const ii = m_clusters;

-    Cluster** i = ii;

-

-    const long count = m_clusterCount + m_clusterPreloadCount;

-

-    Cluster** const jj = ii + count;

-    Cluster** j = jj;

-

-    while (i < j)

-    {

-        //INVARIANT:

-        //[ii, i) < pTP->m_pos

-        //[i, j) ?

-        //[j, jj)  > pTP->m_pos

-

-        Cluster** const k = i + (j - i) / 2;

-        assert(k < jj);

-

-        Cluster* const pCluster = *k;

-        assert(pCluster);

-

-        //const long long pos_ = pCluster->m_pos;

-        //assert(pos_);

-        //const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);

-

-        const long long pos = pCluster->GetPosition();

-        assert(pos >= 0);

-

-        if (pos < tp.m_pos)

-            i = k + 1;

-        else if (pos > tp.m_pos)

-            j = k;

-        else

-            return pCluster->GetEntry(cp, tp);

-    }

-

-    assert(i == j);

-    //assert(Cluster::HasBlockEntries(this, tp.m_pos));

-

-    Cluster* const pCluster = Cluster::Create(this, -1, tp.m_pos); //, -1);

-    assert(pCluster);

-

-    const ptrdiff_t idx = i - m_clusters;

-

-    PreloadCluster(pCluster, idx);

-    assert(m_clusters);

-    assert(m_clusterPreloadCount > 0);

-    assert(m_clusters[idx] == pCluster);

-

-    return pCluster->GetEntry(cp, tp);

-}

-

-

-const Cluster* Segment::FindOrPreloadCluster(long long requested_pos)

-{

-    if (requested_pos < 0)

-        return 0;

-

-    Cluster** const ii = m_clusters;

-    Cluster** i = ii;

-

-    const long count = m_clusterCount + m_clusterPreloadCount;

-

-    Cluster** const jj = ii + count;

-    Cluster** j = jj;

-

-    while (i < j)

-    {

-        //INVARIANT:

-        //[ii, i) < pTP->m_pos

-        //[i, j) ?

-        //[j, jj)  > pTP->m_pos

-

-        Cluster** const k = i + (j - i) / 2;

-        assert(k < jj);

-

-        Cluster* const pCluster = *k;

-        assert(pCluster);

-

-        //const long long pos_ = pCluster->m_pos;

-        //assert(pos_);

-        //const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);

-

-        const long long pos = pCluster->GetPosition();

-        assert(pos >= 0);

-

-        if (pos < requested_pos)

-            i = k + 1;

-        else if (pos > requested_pos)

-            j = k;

-        else

-            return pCluster;

-    }

-

-    assert(i == j);

-    //assert(Cluster::HasBlockEntries(this, tp.m_pos));

-

-    Cluster* const pCluster = Cluster::Create(

-                                this,

-                                -1,

-                                requested_pos);

-                                //-1);

-    assert(pCluster);

-

-    const ptrdiff_t idx = i - m_clusters;

-

-    PreloadCluster(pCluster, idx);

-    assert(m_clusters);

-    assert(m_clusterPreloadCount > 0);

-    assert(m_clusters[idx] == pCluster);

-

-    return pCluster;

-}

-

-

-CuePoint::CuePoint(long idx, long long pos) :

-    m_element_start(0),

-    m_element_size(0),

-    m_index(idx),

-    m_timecode(-1 * pos),

-    m_track_positions(NULL),

-    m_track_positions_count(0)

-{

-    assert(pos > 0);

-}

-

-

-CuePoint::~CuePoint()

-{

-    delete[] m_track_positions;

-}

-

-

-void CuePoint::Load(IMkvReader* pReader)

-{

-    //odbgstream os;

-    //os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;

-

-    if (m_timecode >= 0)  //already loaded

-        return;

-

-    assert(m_track_positions == NULL);

-    assert(m_track_positions_count == 0);

-

-    long long pos_ = -m_timecode;

-    const long long element_start = pos_;

-

-    long long stop;

-

-    {

-        long len;

-

-        const long long id = ReadUInt(pReader, pos_, len);

-        assert(id == 0x3B);  //CuePoint ID

-        //assert((pos + len) <= stop);

-

-        pos_ += len;  //consume ID

-

-        const long long size = ReadUInt(pReader, pos_, len);

-        assert(size >= 0);

-        //assert((pos + len) <= stop);

-

-        pos_ += len;  //consume Size field

-        //assert((pos + size) <= stop);

-

-        //pos_ now points to start of payload

-

-        stop = pos_ + size;

-    }

-

-    const long long element_size = stop - element_start;

-

-    long long pos = pos_;

-

-    //First count number of track positions

-

-    while (pos < stop)

-    {

-        long len;

-

-        const long long id = ReadUInt(pReader, pos, len);

-        assert(id >= 0);  //TODO

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume ID

-

-        const long long size = ReadUInt(pReader, pos, len);

-        assert(size >= 0);

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume Size field

-        assert((pos + size) <= stop);

-

-        if (id == 0x33)  //CueTime ID

-            m_timecode = UnserializeUInt(pReader, pos, size);

-

-        else if (id == 0x37) //CueTrackPosition(s) ID

-            ++m_track_positions_count;

-

-        pos += size;  //consume payload

-        assert(pos <= stop);

-    }

-

-    assert(m_timecode >= 0);

-    assert(m_track_positions_count > 0);

-

-    //os << "CuePoint::Load(cont'd): idpos=" << idpos

-    //   << " timecode=" << m_timecode

-    //   << endl;

-

-    m_track_positions = new TrackPosition[m_track_positions_count];

-

-    //Now parse track positions

-

-    TrackPosition* p = m_track_positions;

-    pos = pos_;

-

-    while (pos < stop)

-    {

-        long len;

-

-        const long long id = ReadUInt(pReader, pos, len);

-        assert(id >= 0);  //TODO

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume ID

-

-        const long long size = ReadUInt(pReader, pos, len);

-        assert(size >= 0);

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume Size field

-        assert((pos + size) <= stop);

-

-        if (id == 0x37) //CueTrackPosition(s) ID

-        {

-            TrackPosition& tp = *p++;

-            tp.Parse(pReader, pos, size);

-        }

-

-        pos += size;  //consume payload

-        assert(pos <= stop);

-    }

-

-    assert(size_t(p - m_track_positions) == m_track_positions_count);

-

-    m_element_start = element_start;

-    m_element_size = element_size;

-}

-

-

-

-void CuePoint::TrackPosition::Parse(

-    IMkvReader* pReader,

-    long long start_,

-    long long size_)

-{

-    const long long stop = start_ + size_;

-    long long pos = start_;

-

-    m_track = -1;

-    m_pos = -1;

-    m_block = 1;  //default

-

-    while (pos < stop)

-    {

-        long len;

-

-        const long long id = ReadUInt(pReader, pos, len);

-        assert(id >= 0);  //TODO

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume ID

-

-        const long long size = ReadUInt(pReader, pos, len);

-        assert(size >= 0);

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume Size field

-        assert((pos + size) <= stop);

-

-        if (id == 0x77)  //CueTrack ID

-            m_track = UnserializeUInt(pReader, pos, size);

-

-        else if (id == 0x71)  //CueClusterPos ID

-            m_pos = UnserializeUInt(pReader, pos, size);

-

-        else if (id == 0x1378)  //CueBlockNumber

-            m_block = UnserializeUInt(pReader, pos, size);

-

-        pos += size;  //consume payload

-        assert(pos <= stop);

-    }

-

-    assert(m_pos >= 0);

-    assert(m_track > 0);

-    //assert(m_block > 0);

-}

-

-

-const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const

-{

-    assert(pTrack);

-

-    const long long n = pTrack->GetNumber();

-

-    const TrackPosition* i = m_track_positions;

-    const TrackPosition* const j = i + m_track_positions_count;

-

-    while (i != j)

-    {

-        const TrackPosition& p = *i++;

-

-        if (p.m_track == n)

-            return &p;

-    }

-

-    return NULL;  //no matching track number found

-}

-

-

-long long CuePoint::GetTimeCode() const

-{

-    return m_timecode;

-}

-

-long long CuePoint::GetTime(const Segment* pSegment) const

-{

-    assert(pSegment);

-    assert(m_timecode >= 0);

-

-    const SegmentInfo* const pInfo = pSegment->GetInfo();

-    assert(pInfo);

-

-    const long long scale = pInfo->GetTimeCodeScale();

-    assert(scale >= 1);

-

-    const long long time = scale * m_timecode;

-

-    return time;

-}

-

-

-#if 0

-long long Segment::Unparsed() const

-{

-    if (m_size < 0)

-        return LLONG_MAX;

-

-    const long long stop = m_start + m_size;

-

-    const long long result = stop - m_pos;

-    assert(result >= 0);

-

-    return result;

-}

-#else

-bool Segment::DoneParsing() const

-{

-    if (m_size < 0)

-    {

-        long long total, avail;

-

-        const int status = m_pReader->Length(&total, &avail);

-

-        if (status < 0)  //error

-            return true;  //must assume done

-

-        if (total < 0)

-            return false;  //assume live stream

-

-        return (m_pos >= total);

-    }

-

-    const long long stop = m_start + m_size;

-

-    return (m_pos >= stop);

-}

-#endif

-

-

-const Cluster* Segment::GetFirst() const

-{

-    if ((m_clusters == NULL) || (m_clusterCount <= 0))

-       return &m_eos;

-

-    Cluster* const pCluster = m_clusters[0];

-    assert(pCluster);

-

-    return pCluster;

-}

-

-

-const Cluster* Segment::GetLast() const

-{

-    if ((m_clusters == NULL) || (m_clusterCount <= 0))

-        return &m_eos;

-

-    const long idx = m_clusterCount - 1;

-

-    Cluster* const pCluster = m_clusters[idx];

-    assert(pCluster);

-

-    return pCluster;

-}

-

-

-unsigned long Segment::GetCount() const

-{

-    return m_clusterCount;

-}

-

-

-const Cluster* Segment::GetNext(const Cluster* pCurr)

-{

-    assert(pCurr);

-    assert(pCurr != &m_eos);

-    assert(m_clusters);

-

-    long idx =  pCurr->m_index;

-

-    if (idx >= 0)

-    {

-        assert(m_clusterCount > 0);

-        assert(idx < m_clusterCount);

-        assert(pCurr == m_clusters[idx]);

-

-        ++idx;

-

-        if (idx >= m_clusterCount)

-            return &m_eos;  //caller will LoadCluster as desired

-

-        Cluster* const pNext = m_clusters[idx];

-        assert(pNext);

-        assert(pNext->m_index >= 0);

-        assert(pNext->m_index == idx);

-

-        return pNext;

-    }

-

-    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

-    const long long stop = m_start + m_size;  //end of segment

-

-    {

-        long len;

-

-        long long result = GetUIntLength(m_pReader, pos, len);

-        assert(result == 0);  //TODO

-        assert((pos + len) <= stop);  //TODO

-

-        const long long id = ReadUInt(m_pReader, pos, len);

-        assert(id == 0x0F43B675);  //Cluster ID   //TODO

-

-        pos += len;  //consume ID

-

-        //Read Size

-        result = GetUIntLength(m_pReader, pos, len);

-        assert(result == 0);  //TODO

-        assert((pos + len) <= stop);  //TODO

-

-        const long long size = ReadUInt(m_pReader, pos, len);

-        assert(size > 0);  //TODO

-        //assert((pCurr->m_size <= 0) || (pCurr->m_size == size));

-

-        pos += len;  //consume length of size of element

-        assert((pos + size) <= stop);  //TODO

-

-        //Pos now points to start of payload

-

-        pos += size;  //consume payload

-    }

-

-    long long off_next = 0;

-    //long long element_start_next = 0;

-    long long element_size_next = 0;

-

-    while (pos < stop)

-    {

-        long len;

-

-        long long result = GetUIntLength(m_pReader, pos, len);

-        assert(result == 0);  //TODO

-        assert((pos + len) <= stop);  //TODO

-

-        const long long idpos = pos;  //pos of next (potential) cluster

-

-        const long long id = ReadUInt(m_pReader, idpos, len);

-        assert(id > 0);  //TODO

-

-        pos += len;  //consume ID

-

-        //Read Size

-        result = GetUIntLength(m_pReader, pos, len);

-        assert(result == 0);  //TODO

-        assert((pos + len) <= stop);  //TODO

-

-        const long long size = ReadUInt(m_pReader, pos, len);

-        assert(size >= 0);  //TODO

-

-        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

-            continue;

-

-        if (id == 0x0F43B675)  //Cluster ID

-        {

-            const long long off_next_ = idpos - m_start;

-

-            long long pos_;

-            long len_;

-

-            const long status = Cluster::HasBlockEntries(

-                                    this,

-                                    off_next_,

-                                    pos_,

-                                    len_);

-

-            assert(status >= 0);

-

-            if (status > 0)

-            {

-                off_next = off_next_;

-                //element_start_next = idpos;

-                element_size_next = element_size;

-                break;

-            }

-        }

-

-        pos += size;  //consume payload

-    }

-

-    if (off_next <= 0)

-        return 0;

-

-    Cluster** const ii = m_clusters + m_clusterCount;

-    Cluster** i = ii;

-

-    Cluster** const jj = ii + m_clusterPreloadCount;

-    Cluster** j = jj;

-

-    while (i < j)

-    {

-        //INVARIANT:

-        //[0, i) < pos_next

-        //[i, j) ?

-        //[j, jj)  > pos_next

-

-        Cluster** const k = i + (j - i) / 2;

-        assert(k < jj);

-

-        Cluster* const pNext = *k;

-        assert(pNext);

-        assert(pNext->m_index < 0);

-

-        //const long long pos_ = pNext->m_pos;

-        //assert(pos_);

-        //pos = pos_ * ((pos_ < 0) ? -1 : 1);

-

-        pos = pNext->GetPosition();

-

-        if (pos < off_next)

-            i = k + 1;

-        else if (pos > off_next)

-            j = k;

-        else

-            return pNext;

-    }

-

-    assert(i == j);

-

-    Cluster* const pNext = Cluster::Create(this,

-                                          -1,

-                                          off_next);

-                                          //element_size_next);

-    assert(pNext);

-

-    const ptrdiff_t idx_next = i - m_clusters;  //insertion position

-

-    PreloadCluster(pNext, idx_next);

-    assert(m_clusters);

-    assert(idx_next < m_clusterSize);

-    assert(m_clusters[idx_next] == pNext);

-

-    return pNext;

-}

-

-

-long Segment::ParseNext(

-    const Cluster* pCurr,

-    const Cluster*& pResult,

-    long long& pos,

-    long& len)

-{

-    assert(pCurr);

-    assert(!pCurr->EOS());

-    assert(m_clusters);

-

-    pResult = 0;

-

-    if (pCurr->m_index >= 0)  //loaded (not merely preloaded)

-    {

-        assert(m_clusters[pCurr->m_index] == pCurr);

-

-        const long next_idx = pCurr->m_index + 1;

-

-        if (next_idx < m_clusterCount)

-        {

-            pResult = m_clusters[next_idx];

-            return 0;  //success

-        }

-

-        //curr cluster is last among loaded

-

-        const long result = LoadCluster(pos, len);

-

-        if (result < 0)  //error or underflow

-            return result;

-

-        if (result > 0)  //no more clusters

-        {

-            //pResult = &m_eos;

-            return 1;

-        }

-

-        pResult = GetLast();

-        return 0;  //success

-    }

-

-    assert(m_pos > 0);

-

-    long long total, avail;

-

-    long status = m_pReader->Length(&total, &avail);

-

-    if (status < 0)  //error

-        return status;

-

-    assert((total < 0) || (avail <= total));

-

-    const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;

-

-    //interrogate curr cluster

-

-    pos = pCurr->m_element_start;

-

-    if (pCurr->m_element_size >= 0)

-        pos += pCurr->m_element_size;

-    else

-    {

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        long long result = GetUIntLength(m_pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long id = ReadUInt(m_pReader, pos, len);

-

-        if (id != 0x0F43B675)  //weird: not Cluster ID

-            return -1;

-

-        pos += len;  //consume ID

-

-        //Read Size

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result = GetUIntLength(m_pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long size = ReadUInt(m_pReader, pos, len);

-

-        if (size < 0) //error

-            return static_cast<long>(size);

-

-        pos += len;  //consume size field

-

-        const long long unknown_size = (1LL << (7 * len)) - 1;

-

-        if (size == unknown_size)          //TODO: should never happen

-            return E_FILE_FORMAT_INVALID;  //TODO: resolve this

-

-        //assert((pCurr->m_size <= 0) || (pCurr->m_size == size));

-

-        if ((segment_stop >= 0) && ((pos + size) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        //Pos now points to start of payload

-

-        pos += size;  //consume payload (that is, the current cluster)

-        assert((segment_stop < 0) || (pos <= segment_stop));

-

-        //By consuming the payload, we are assuming that the curr

-        //cluster isn't interesting.  That is, we don't bother checking

-        //whether the payload of the curr cluster is less than what

-        //happens to be available (obtained via IMkvReader::Length).

-        //Presumably the caller has already dispensed with the current

-        //cluster, and really does want the next cluster.

-    }

-

-    //pos now points to just beyond the last fully-loaded cluster

-

-    for (;;)

-    {

-        const long status = DoParseNext(pResult, pos, len);

-

-        if (status <= 1)

-            return status;

-    }

-}

-

-

-long Segment::DoParseNext(

-    const Cluster*& pResult,

-    long long& pos,

-    long& len)

-{

-    long long total, avail;

-

-    long status = m_pReader->Length(&total, &avail);

-

-    if (status < 0)  //error

-        return status;

-

-    assert((total < 0) || (avail <= total));

-

-    const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;

-

-    //Parse next cluster.  This is strictly a parsing activity.

-    //Creation of a new cluster object happens later, after the

-    //parsing is done.

-

-    long long off_next = 0;

-    long long cluster_size = -1;

-

-    for (;;)

-    {

-        if ((total >= 0) && (pos >= total))

-            return 1;  //EOF

-

-        if ((segment_stop >= 0) && (pos >= segment_stop))

-            return 1;  //EOF

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        long long result = GetUIntLength(m_pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long idpos = pos;             //absolute

-        const long long idoff = pos - m_start;   //relative

-

-        const long long id = ReadUInt(m_pReader, idpos, len);  //absolute

-

-        if (id < 0)  //error

-            return static_cast<long>(id);

-

-        if (id == 0)  //weird

-            return -1;  //generic error

-

-        pos += len;  //consume ID

-

-        //Read Size

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result = GetUIntLength(m_pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long size = ReadUInt(m_pReader, pos, len);

-

-        if (size < 0)  //error

-            return static_cast<long>(size);

-

-        pos += len;  //consume length of size of element

-

-        //Pos now points to start of payload

-

-        if (size == 0)  //weird

-            continue;

-

-        const long long unknown_size = (1LL << (7 * len)) - 1;

-

-        if ((segment_stop >= 0) &&

-            (size != unknown_size) &&

-            ((pos + size) > segment_stop))

-        {

-            return E_FILE_FORMAT_INVALID;

-        }

-

-        if (id == 0x0C53BB6B)  //Cues ID

-        {

-            if (size == unknown_size)

-                return E_FILE_FORMAT_INVALID;

-

-            const long long element_stop = pos + size;

-

-            if ((segment_stop >= 0) && (element_stop > segment_stop))

-                return E_FILE_FORMAT_INVALID;

-

-            const long long element_start = idpos;

-            const long long element_size = element_stop - element_start;

-

-            if (m_pCues == NULL)

-            {

-                m_pCues = new Cues(this,

-                                    pos,

-                                    size,

-                                    element_start,

-                                    element_size);

-                assert(m_pCues);  //TODO

-            }

-

-            pos += size;  //consume payload

-            assert((segment_stop < 0) || (pos <= segment_stop));

-

-            continue;

-        }

-

-        if (id != 0x0F43B675)  //not a Cluster ID

-        {

-            if (size == unknown_size)

-                return E_FILE_FORMAT_INVALID;

-

-            pos += size;  //consume payload

-            assert((segment_stop < 0) || (pos <= segment_stop));

-

-            continue;

-        }

-

-#if 0 //this is commented-out to support incremental cluster parsing

-        len = static_cast<long>(size);

-

-        if (element_stop > avail)

-            return E_BUFFER_NOT_FULL;

-#endif

-

-        //We have a cluster.

-

-        off_next = idoff;

-

-        if (size != unknown_size)

-            cluster_size = size;

-

-        break;

-    }

-

-    assert(off_next > 0);  //have cluster

-

-    //We have parsed the next cluster.

-    //We have not created a cluster object yet.  What we need

-    //to do now is determine whether it has already be preloaded

-    //(in which case, an object for this cluster has already been

-    //created), and if not, create a new cluster object.

-

-    Cluster** const ii = m_clusters + m_clusterCount;

-    Cluster** i = ii;

-

-    Cluster** const jj = ii + m_clusterPreloadCount;

-    Cluster** j = jj;

-

-    while (i < j)

-    {

-        //INVARIANT:

-        //[0, i) < pos_next

-        //[i, j) ?

-        //[j, jj)  > pos_next

-

-        Cluster** const k = i + (j - i) / 2;

-        assert(k < jj);

-

-        const Cluster* const pNext = *k;

-        assert(pNext);

-        assert(pNext->m_index < 0);

-

-        pos = pNext->GetPosition();

-        assert(pos >= 0);

-

-        if (pos < off_next)

-            i = k + 1;

-        else if (pos > off_next)

-            j = k;

-        else

-        {

-            pResult = pNext;

-            return 0;  //success

-        }

-    }

-

-    assert(i == j);

-

-    long long pos_;

-    long len_;

-

-    status = Cluster::HasBlockEntries(this, off_next, pos_, len_);

-

-    if (status < 0)  //error or underflow

-    {

-        pos = pos_;

-        len = len_;

-

-        return status;

-    }

-

-    if (status > 0)  //means "found at least one block entry"

-    {

-        Cluster* const pNext = Cluster::Create(this,

-                                                -1,   //preloaded

-                                                off_next);

-                                                //element_size);

-        assert(pNext);

-

-        const ptrdiff_t idx_next = i - m_clusters;  //insertion position

-

-        PreloadCluster(pNext, idx_next);

-        assert(m_clusters);

-        assert(idx_next < m_clusterSize);

-        assert(m_clusters[idx_next] == pNext);

-

-        pResult = pNext;

-        return 0;  //success

-    }

-

-    //status == 0 means "no block entries found"

-

-    if (cluster_size < 0)  //unknown size

-    {

-        const long long payload_pos = pos;  //absolute pos of cluster payload

-

-        for (;;)  //determine cluster size

-        {

-            if ((total >= 0) && (pos >= total))

-                break;

-

-            if ((segment_stop >= 0) && (pos >= segment_stop))

-                break;  //no more clusters

-

-            //Read ID

-

-            if ((pos + 1) > avail)

-            {

-                len = 1;

-                return E_BUFFER_NOT_FULL;

-            }

-

-            long long result = GetUIntLength(m_pReader, pos, len);

-

-            if (result < 0)  //error

-                return static_cast<long>(result);

-

-            if (result > 0)  //weird

-                return E_BUFFER_NOT_FULL;

-

-            if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-                return E_FILE_FORMAT_INVALID;

-

-            if ((pos + len) > avail)

-                return E_BUFFER_NOT_FULL;

-

-            const long long idpos = pos;

-            const long long id = ReadUInt(m_pReader, idpos, len);

-

-            if (id < 0)  //error (or underflow)

-                return static_cast<long>(id);

-

-            //This is the distinguished set of ID's we use to determine

-            //that we have exhausted the sub-element's inside the cluster

-            //whose ID we parsed earlier.

-

-            if (id == 0x0F43B675)  //Cluster ID

-                break;

-

-            if (id == 0x0C53BB6B)  //Cues ID

-                break;

-

-            pos += len;  //consume ID (of sub-element)

-

-            //Read Size

-

-            if ((pos + 1) > avail)

-            {

-                len = 1;

-                return E_BUFFER_NOT_FULL;

-            }

-

-            result = GetUIntLength(m_pReader, pos, len);

-

-            if (result < 0)  //error

-                return static_cast<long>(result);

-

-            if (result > 0)  //weird

-                return E_BUFFER_NOT_FULL;

-

-            if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-                return E_FILE_FORMAT_INVALID;

-

-            if ((pos + len) > avail)

-                return E_BUFFER_NOT_FULL;

-

-            const long long size = ReadUInt(m_pReader, pos, len);

-

-            if (size < 0)  //error

-                return static_cast<long>(size);

-

-            pos += len;  //consume size field of element

-

-            //pos now points to start of sub-element's payload

-

-            if (size == 0)  //weird

-                continue;

-

-            const long long unknown_size = (1LL << (7 * len)) - 1;

-

-            if (size == unknown_size)

-                return E_FILE_FORMAT_INVALID;  //not allowed for sub-elements

-

-            if ((segment_stop >= 0) && ((pos + size) > segment_stop))  //weird

-                return E_FILE_FORMAT_INVALID;

-

-            pos += size;  //consume payload of sub-element

-            assert((segment_stop < 0) || (pos <= segment_stop));

-        }  //determine cluster size

-

-        cluster_size = pos - payload_pos;

-        assert(cluster_size >= 0);  //TODO: handle cluster_size = 0

-

-        pos = payload_pos;  //reset and re-parse original cluster

-    }

-

-    pos += cluster_size;  //consume payload

-    assert((segment_stop < 0) || (pos <= segment_stop));

-

-    return 2;             //try to find a cluster that follows next

-}

-

-

-const Cluster* Segment::FindCluster(long long time_ns) const

-{

-    if ((m_clusters == NULL) || (m_clusterCount <= 0))

-        return &m_eos;

-

-    {

-        Cluster* const pCluster = m_clusters[0];

-        assert(pCluster);

-        assert(pCluster->m_index == 0);

-

-        if (time_ns <= pCluster->GetTime())

-            return pCluster;

-    }

-

-    //Binary search of cluster array

-

-    long i = 0;

-    long j = m_clusterCount;

-

-    while (i < j)

-    {

-        //INVARIANT:

-        //[0, i) <= time_ns

-        //[i, j) ?

-        //[j, m_clusterCount)  > time_ns

-

-        const long k = i + (j - i) / 2;

-        assert(k < m_clusterCount);

-

-        Cluster* const pCluster = m_clusters[k];

-        assert(pCluster);

-        assert(pCluster->m_index == k);

-

-        const long long t = pCluster->GetTime();

-

-        if (t <= time_ns)

-            i = k + 1;

-        else

-            j = k;

-

-        assert(i <= j);

-    }

-

-    assert(i == j);

-    assert(i > 0);

-    assert(i <= m_clusterCount);

-

-    const long k = i - 1;

-

-    Cluster* const pCluster = m_clusters[k];

-    assert(pCluster);

-    assert(pCluster->m_index == k);

-    assert(pCluster->GetTime() <= time_ns);

-

-    return pCluster;

-}

-

-

-#if 0

-const BlockEntry* Segment::Seek(

-    long long time_ns,

-    const Track* pTrack) const

-{

-    assert(pTrack);

-

-    if ((m_clusters == NULL) || (m_clusterCount <= 0))

-        return pTrack->GetEOS();

-

-    Cluster** const i = m_clusters;

-    assert(i);

-

-    {

-        Cluster* const pCluster = *i;

-        assert(pCluster);

-        assert(pCluster->m_index == 0);  //m_clusterCount > 0

-        assert(pCluster->m_pSegment == this);

-

-        if (time_ns <= pCluster->GetTime())

-            return pCluster->GetEntry(pTrack);

-    }

-

-    Cluster** const j = i + m_clusterCount;

-

-    if (pTrack->GetType() == 2)  //audio

-    {

-        //TODO: we could decide to use cues for this, as we do for video.

-        //But we only use it for video because looking around for a keyframe

-        //can get expensive.  Audio doesn't require anything special so a

-        //straight cluster search is good enough (we assume).

-

-        Cluster** lo = i;

-        Cluster** hi = j;

-

-        while (lo < hi)

-        {

-            //INVARIANT:

-            //[i, lo) <= time_ns

-            //[lo, hi) ?

-            //[hi, j)  > time_ns

-

-            Cluster** const mid = lo + (hi - lo) / 2;

-            assert(mid < hi);

-

-            Cluster* const pCluster = *mid;

-            assert(pCluster);

-            assert(pCluster->m_index == long(mid - m_clusters));

-            assert(pCluster->m_pSegment == this);

-

-            const long long t = pCluster->GetTime();

-

-            if (t <= time_ns)

-                lo = mid + 1;

-            else

-                hi = mid;

-

-            assert(lo <= hi);

-        }

-

-        assert(lo == hi);

-        assert(lo > i);

-        assert(lo <= j);

-

-        while (lo > i)

-        {

-            Cluster* const pCluster = *--lo;

-            assert(pCluster);

-            assert(pCluster->GetTime() <= time_ns);

-

-            const BlockEntry* const pBE = pCluster->GetEntry(pTrack);

-

-            if ((pBE != 0) && !pBE->EOS())

-                return pBE;

-

-            //landed on empty cluster (no entries)

-        }

-

-        return pTrack->GetEOS();  //weird

-    }

-

-    assert(pTrack->GetType() == 1);  //video

-

-    Cluster** lo = i;

-    Cluster** hi = j;

-

-    while (lo < hi)

-    {

-        //INVARIANT:

-        //[i, lo) <= time_ns

-        //[lo, hi) ?

-        //[hi, j)  > time_ns

-

-        Cluster** const mid = lo + (hi - lo) / 2;

-        assert(mid < hi);

-

-        Cluster* const pCluster = *mid;

-        assert(pCluster);

-

-        const long long t = pCluster->GetTime();

-

-        if (t <= time_ns)

-            lo = mid + 1;

-        else

-            hi = mid;

-

-        assert(lo <= hi);

-    }

-

-    assert(lo == hi);

-    assert(lo > i);

-    assert(lo <= j);

-

-    Cluster* pCluster = *--lo;

-    assert(pCluster);

-    assert(pCluster->GetTime() <= time_ns);

-

-    {

-        const BlockEntry* const pBE = pCluster->GetEntry(pTrack, time_ns);

-

-        if ((pBE != 0) && !pBE->EOS())  //found a keyframe

-            return pBE;

-    }

-

-    const VideoTrack* const pVideo = static_cast<const VideoTrack*>(pTrack);

-

-    while (lo != i)

-    {

-        pCluster = *--lo;

-        assert(pCluster);

-        assert(pCluster->GetTime() <= time_ns);

-

-        const BlockEntry* const pBlockEntry = pCluster->GetMaxKey(pVideo);

-

-        if ((pBlockEntry != 0) && !pBlockEntry->EOS())

-            return pBlockEntry;

-    }

-

-    //weird: we're on the first cluster, but no keyframe found

-    //should never happen but we must return something anyway

-

-    return pTrack->GetEOS();

-}

-#endif

-

-

-#if 0

-bool Segment::SearchCues(

-    long long time_ns,

-    Track* pTrack,

-    Cluster*& pCluster,

-    const BlockEntry*& pBlockEntry,

-    const CuePoint*& pCP,

-    const CuePoint::TrackPosition*& pTP)

-{

-    if (pTrack->GetType() != 1)  //not video

-        return false;  //TODO: for now, just handle video stream

-

-    if (m_pCues == NULL)

-        return false;

-

-    if (!m_pCues->Find(time_ns, pTrack, pCP, pTP))

-        return false;  //weird

-

-    assert(pCP);

-    assert(pTP);

-    assert(pTP->m_track == pTrack->GetNumber());

-

-    //We have the cue point and track position we want,

-    //so we now need to search for the cluster having

-    //the indicated position.

-

-    return GetCluster(pCP, pTP, pCluster, pBlockEntry);

-}

-#endif

-

-

-const Tracks* Segment::GetTracks() const

-{

-    return m_pTracks;

-}

-

-

-const SegmentInfo* Segment::GetInfo() const

-{

-    return m_pInfo;

-}

-

-

-const Cues* Segment::GetCues() const

-{

-    return m_pCues;

-}

-

-

-const SeekHead* Segment::GetSeekHead() const

-{

-    return m_pSeekHead;

-}

-

-

-long long Segment::GetDuration() const

-{

-    assert(m_pInfo);

-    return m_pInfo->GetDuration();

-}

-

-

-SegmentInfo::SegmentInfo(

-    Segment* pSegment,

-    long long start,

-    long long size_,

-    long long element_start,

-    long long element_size) :

-    m_pSegment(pSegment),

-    m_start(start),

-    m_size(size_),

-    m_element_start(element_start),

-    m_element_size(element_size),

-    m_pMuxingAppAsUTF8(NULL),

-    m_pWritingAppAsUTF8(NULL),

-    m_pTitleAsUTF8(NULL)

-{

-}

-

-SegmentInfo::~SegmentInfo()

-{

-    delete[] m_pMuxingAppAsUTF8;

-    m_pMuxingAppAsUTF8 = NULL;

-

-    delete[] m_pWritingAppAsUTF8;

-    m_pWritingAppAsUTF8 = NULL;

-

-    delete[] m_pTitleAsUTF8;

-    m_pTitleAsUTF8 = NULL;

-}

-

-

-long SegmentInfo::Parse()

-{

-    assert(m_pMuxingAppAsUTF8 == NULL);

-    assert(m_pWritingAppAsUTF8 == NULL);

-    assert(m_pTitleAsUTF8 == NULL);

-

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    long long pos = m_start;

-    const long long stop = m_start + m_size;

-

-    m_timecodeScale = 1000000;

-    m_duration = -1;

-

-    while (pos < stop)

-    {

-        long long id, size;

-

-        const long status = ParseElementHeader(

-                                pReader,

-                                pos,

-                                stop,

-                                id,

-                                size);

-

-        if (status < 0)  //error

-            return status;

-

-        if (id == 0x0AD7B1)  //Timecode Scale

-        {

-            m_timecodeScale = UnserializeUInt(pReader, pos, size);

-

-            if (m_timecodeScale <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x0489)  //Segment duration

-        {

-            const long status = UnserializeFloat(

-                                    pReader,

-                                    pos,

-                                    size,

-                                    m_duration);

-

-            if (status < 0)

-                return status;

-

-            if (m_duration < 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x0D80)  //MuxingApp

-        {

-            const long status = UnserializeString(

-                                    pReader,

-                                    pos,

-                                    size,

-                                    m_pMuxingAppAsUTF8);

-

-            if (status)

-                return status;

-        }

-        else if (id == 0x1741)  //WritingApp

-        {

-            const long status = UnserializeString(

-                                    pReader,

-                                    pos,

-                                    size,

-                                    m_pWritingAppAsUTF8);

-

-            if (status)

-                return status;

-        }

-        else if (id == 0x3BA9)  //Title

-        {

-            const long status = UnserializeString(

-                                    pReader,

-                                    pos,

-                                    size,

-                                    m_pTitleAsUTF8);

-

-            if (status)

-                return status;

-        }

-

-        pos += size;

-        assert(pos <= stop);

-    }

-

-    assert(pos == stop);

-

-    return 0;

-}

-

-

-long long SegmentInfo::GetTimeCodeScale() const

-{

-    return m_timecodeScale;

-}

-

-

-long long SegmentInfo::GetDuration() const

-{

-    if (m_duration < 0)

-        return -1;

-

-    assert(m_timecodeScale >= 1);

-

-    const double dd = double(m_duration) * double(m_timecodeScale);

-    const long long d = static_cast<long long>(dd);

-

-    return d;

-}

-

-const char* SegmentInfo::GetMuxingAppAsUTF8() const

-{

-    return m_pMuxingAppAsUTF8;

-}

-

-

-const char* SegmentInfo::GetWritingAppAsUTF8() const

-{

-    return m_pWritingAppAsUTF8;

-}

-

-const char* SegmentInfo::GetTitleAsUTF8() const

-{

-    return m_pTitleAsUTF8;

-}

-

-///////////////////////////////////////////////////////////////

-// ContentEncoding element

-ContentEncoding::ContentCompression::ContentCompression()

-    : algo(0),

-      settings(NULL) {

-}

-

-ContentEncoding::ContentCompression::~ContentCompression() {

-  delete [] settings;

-}

-

-ContentEncoding::ContentEncryption::ContentEncryption()

-    : algo(0),

-      key_id(NULL),

-      key_id_len(0),

-      signature(NULL),

-      signature_len(0),

-      sig_key_id(NULL),

-      sig_key_id_len(0),

-      sig_algo(0),

-      sig_hash_algo(0) {

-}

-

-ContentEncoding::ContentEncryption::~ContentEncryption() {

-  delete [] key_id;

-  delete [] signature;

-  delete [] sig_key_id;

-}

-

-ContentEncoding::ContentEncoding()

-    : compression_entries_(NULL),

-      compression_entries_end_(NULL),

-      encryption_entries_(NULL),

-      encryption_entries_end_(NULL),

-      encoding_order_(0),

-      encoding_scope_(1),

-      encoding_type_(0) {

-}

-

-ContentEncoding::~ContentEncoding() {

-  ContentCompression** comp_i = compression_entries_;

-  ContentCompression** const comp_j = compression_entries_end_;

-

-  while (comp_i != comp_j) {

-    ContentCompression* const comp = *comp_i++;

-    delete comp;

-  }

-

-  delete [] compression_entries_;

-

-  ContentEncryption** enc_i = encryption_entries_;

-  ContentEncryption** const enc_j = encryption_entries_end_;

-

-  while (enc_i != enc_j) {

-    ContentEncryption* const enc = *enc_i++;

-    delete enc;

-  }

-

-  delete [] encryption_entries_;

-}

-

-

-const ContentEncoding::ContentCompression*

-ContentEncoding::GetCompressionByIndex(unsigned long idx) const {

-  const ptrdiff_t count = compression_entries_end_ - compression_entries_;

-  assert(count >= 0);

-

-  if (idx >= static_cast<unsigned long>(count))

-    return NULL;

-

-  return compression_entries_[idx];

-}

-

-unsigned long ContentEncoding::GetCompressionCount() const {

-  const ptrdiff_t count = compression_entries_end_ - compression_entries_;

-  assert(count >= 0);

-

-  return static_cast<unsigned long>(count);

-}

-

-const ContentEncoding::ContentEncryption*

-ContentEncoding::GetEncryptionByIndex(unsigned long idx) const {

-  const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;

-  assert(count >= 0);

-

-  if (idx >= static_cast<unsigned long>(count))

-    return NULL;

-

-  return encryption_entries_[idx];

-}

-

-unsigned long ContentEncoding::GetEncryptionCount() const {

-  const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;

-  assert(count >= 0);

-

-  return static_cast<unsigned long>(count);

-}

-

-void ContentEncoding::ParseEncryptionEntry(

-    long long start,

-    long long size,

-    IMkvReader* const pReader,

-    ContentEncryption* const encryption) {

-  assert(pReader);

-  assert(encryption);

-

-  long long pos = start;

-  const long long stop = start + size;

-

-  while (pos < stop) {

-#ifdef _DEBUG

-    long len;

-    const long long id = ReadUInt(pReader, pos, len);

-    assert(id >= 0);  //TODO: handle error case

-    assert((pos + len) <= stop);

-#endif

-

-    long long value;

-    unsigned char* buf;

-    size_t buf_len;

-

-    if (Match(pReader, pos, 0x7E1, value)) {

-      // ContentEncAlgo

-      encryption->algo = value;

-    } else if (Match(pReader, pos, 0x7E2, buf, buf_len)) {

-      // ContentEncKeyID

-      encryption->key_id = buf;

-      encryption->key_id_len = buf_len;

-    } else if (Match(pReader, pos, 0x7E3, buf, buf_len)) {

-      // ContentSignature

-      encryption->signature = buf;

-      encryption->signature_len = buf_len;

-    } else if (Match(pReader, pos, 0x7E4, buf, buf_len)) {

-      // ContentSigKeyID

-      encryption->sig_key_id = buf;

-      encryption->sig_key_id_len = buf_len;

-    } else if (Match(pReader, pos, 0x7E5, value)) {

-      // ContentSigAlgo

-      encoding_type_ = value;

-    } else if (Match(pReader, pos, 0x7E6, value)) {

-      // ContentSigHashAlgo

-      encoding_type_ = value;

-    } else {

-      long len;

-      const long long id = ReadUInt(pReader, pos, len);

-      assert(id >= 0);  //TODO: handle error case

-      assert((pos + len) <= stop);

-

-      pos += len;  //consume id

-

-      const long long size = ReadUInt(pReader, pos, len);

-      assert(size >= 0);  //TODO: handle error case

-      assert((pos + len) <= stop);

-

-      pos += len;  //consume length of size

-      assert((pos + size) <= stop);

-

-      pos += size;  //consume payload

-      assert(pos <= stop);

-    }

-  }

-}

-

-bool ContentEncoding::ParseContentEncodingEntry(long long start,

-                                                long long size,

-                                                IMkvReader* const pReader) {

-  assert(pReader);

-

-  long long pos = start;

-  const long long stop = start + size;

-

-  // Count ContentCompression and ContentEncryption elements.

-  long long pos1 = start;

-  int compression_count = 0;

-  int encryption_count = 0;

-

-  while (pos1 < stop) {

-    long len;

-    const long long id = ReadUInt(pReader, pos1, len);

-    assert(id >= 0);

-    assert((pos1 + len) <= stop);

-

-    pos1 += len;  //consume id

-

-    const long long size = ReadUInt(pReader, pos1, len);

-    assert(size >= 0);

-    assert((pos1 + len) <= stop);

-

-    pos1 += len;  //consume length of size

-

-    //pos now designates start of element

-    if (id == 0x1034)  // ContentCompression ID

-      ++compression_count;

-

-    if (id == 0x1035)  // ContentEncryption ID

-      ++encryption_count;

-

-    pos1 += size;  //consume payload

-    assert(pos1 <= stop);

-  }

-

-  if (compression_count <= 0 && encryption_count <= 0)

-    return false;

-

-  if (compression_count > 0) {

-    compression_entries_ = new ContentCompression*[compression_count];

-    compression_entries_end_ = compression_entries_;

-  }

-

-  if (encryption_count > 0) {

-    encryption_entries_ = new ContentEncryption*[encryption_count];

-    encryption_entries_end_ = encryption_entries_;

-  }

-

-  while (pos < stop) {

-#ifdef _DEBUG

-    long len;

-    const long long id = ReadUInt(pReader, pos, len);

-    assert(id >= 0);  //TODO: handle error case

-    assert((pos + len) <= stop);

-#endif

-

-    long long value;

-    if (Match(pReader, pos, 0x1031, value)) {

-      // ContentEncodingOrder

-      encoding_order_ = value;

-    } else if (Match(pReader, pos, 0x1032, value)) {

-      // ContentEncodingScope

-      encoding_scope_ = value;

-      assert(encoding_scope_ > 0);

-    } else if (Match(pReader, pos, 0x1033, value)) {

-      // ContentEncodingType

-      encoding_type_ = value;

-    } else {

-      long len;

-      const long long id = ReadUInt(pReader, pos, len);

-      assert(id >= 0);  //TODO: handle error case

-      assert((pos + len) <= stop);

-

-      pos += len;  //consume id

-

-      const long long size = ReadUInt(pReader, pos, len);

-      assert(size >= 0);  //TODO: handle error case

-      assert((pos + len) <= stop);

-

-      pos += len;  //consume length of size

-      assert((pos + size) <= stop);

-

-      //pos now designates start of payload

-

-      if (id == 0x1034) {

-        // ContentCompression ID

-        // TODO(fgaligan): Add code to parse ContentCompression elements.

-      } else if (id == 0x1035) {

-        // ContentEncryption ID

-        ContentEncryption* const encryption = new ContentEncryption();

-

-        ParseEncryptionEntry(pos, size, pReader, encryption);

-        *encryption_entries_end_ = encryption;

-        ++encryption_entries_end_;

-      }

-

-      pos += size;  //consume payload

-      assert(pos <= stop);

-    }

-  }

-

-  assert(pos == stop);

-

-  return true;

-}

-

-Track::Track(

-    Segment* pSegment,

-    long long element_start,

-    long long element_size) :

-    m_pSegment(pSegment),

-    m_element_start(element_start),

-    m_element_size(element_size),

-    content_encoding_entries_(NULL),

-    content_encoding_entries_end_(NULL)

-{

-}

-

-Track::~Track()

-{

-    Info& info = const_cast<Info&>(m_info);

-    info.Clear();

-

-    ContentEncoding** i = content_encoding_entries_;

-    ContentEncoding** const j = content_encoding_entries_end_;

-

-    while (i != j) {

-        ContentEncoding* const encoding = *i++;

-        delete encoding;

-    }

-

-    delete [] content_encoding_entries_;

-}

-

-Track::Info::Info():

-    nameAsUTF8(NULL),

-    codecId(NULL),

-    codecNameAsUTF8(NULL),

-    codecPrivate(NULL),

-    codecPrivateSize(0)

-{

-}

-

-Track::Info::~Info()

-{

-    Clear();

-}

-

-void Track::Info::Clear()

-{

-    delete[] nameAsUTF8;

-    nameAsUTF8 = NULL;

-

-    delete[] codecId;

-    codecId = NULL;

-

-    delete[] codecPrivate;

-    codecPrivate = NULL;

-    codecPrivateSize = 0;

-

-    delete[] codecNameAsUTF8;

-    codecNameAsUTF8 = NULL;

-}

-

-int Track::Info::CopyStr(char* Info::*str, Info& dst_) const

-{

-    if (str == static_cast<char* Info::*>(NULL))

-        return -1;

-

-    char*& dst = dst_.*str;

-

-    if (dst)  //should be NULL already

-        return -1;

-

-    const char* const src = this->*str;

-

-    if (src == NULL)

-        return 0;

-

-    const size_t len = strlen(src);

-

-    dst = new (std::nothrow) char[len+1];

-

-    if (dst == NULL)

-        return -1;

-

-    strcpy(dst, src);

-

-    return 0;

-}

-

-

-int Track::Info::Copy(Info& dst) const

-{

-    if (&dst == this)

-        return 0;

-

-    dst.type = type;

-    dst.number = number;

-    dst.uid = uid;

-    dst.lacing = lacing;

-    dst.settings = settings;

-

-    //We now copy the string member variables from src to dst.

-    //This involves memory allocation so in principle the operation

-    //can fail (indeed, that's why we have Info::Copy), so we must

-    //report this to the caller.  An error return from this function

-    //therefore implies that the copy was only partially successful.

-

-    if (int status = CopyStr(&Info::nameAsUTF8, dst))

-        return status;

-

-    if (int status = CopyStr(&Info::codecId, dst))

-        return status;

-

-    if (int status = CopyStr(&Info::codecNameAsUTF8, dst))

-        return status;

-

-    if (codecPrivateSize > 0)

-    {

-        if (codecPrivate == NULL)

-            return -1;

-

-        if (dst.codecPrivate)

-            return -1;

-

-        if (dst.codecPrivateSize != 0)

-            return -1;

-

-        dst.codecPrivate = new (std::nothrow) unsigned char[codecPrivateSize];

-

-        if (dst.codecPrivate == NULL)

-            return -1;

-

-        memcpy(dst.codecPrivate, codecPrivate, codecPrivateSize);

-        dst.codecPrivateSize = codecPrivateSize;

-    }

-

-    return 0;

-}

-

-const BlockEntry* Track::GetEOS() const

-{

-    return &m_eos;

-}

-

-long Track::GetType() const

-{

-    return m_info.type;

-}

-

-long Track::GetNumber() const

-{

-    return m_info.number;

-}

-

-unsigned long long Track::GetUid() const

-{

-    return m_info.uid;

-}

-

-const char* Track::GetNameAsUTF8() const

-{

-    return m_info.nameAsUTF8;

-}

-

-const char* Track::GetCodecNameAsUTF8() const

-{

-    return m_info.codecNameAsUTF8;

-}

-

-

-const char* Track::GetCodecId() const

-{

-    return m_info.codecId;

-}

-

-const unsigned char* Track::GetCodecPrivate(size_t& size) const

-{

-    size = m_info.codecPrivateSize;

-    return m_info.codecPrivate;

-}

-

-

-bool Track::GetLacing() const

-{

-    return m_info.lacing;

-}

-

-

-long Track::GetFirst(const BlockEntry*& pBlockEntry) const

-{

-    const Cluster* pCluster = m_pSegment->GetFirst();

-

-    for (int i = 0; ; )

-    {

-        if (pCluster == NULL)

-        {

-            pBlockEntry = GetEOS();

-            return 1;

-        }

-

-        if (pCluster->EOS())

-        {

-#if 0

-            if (m_pSegment->Unparsed() <= 0)  //all clusters have been loaded

-            {

-                pBlockEntry = GetEOS();

-                return 1;

-            }

-#else

-            if (m_pSegment->DoneParsing())

-            {

-                pBlockEntry = GetEOS();

-                return 1;

-            }

-#endif

-

-            pBlockEntry = 0;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        long status = pCluster->GetFirst(pBlockEntry);

-

-        if (status < 0)  //error

-            return status;

-

-        if (pBlockEntry == 0)  //empty cluster

-        {

-            pCluster = m_pSegment->GetNext(pCluster);

-            continue;

-        }

-

-        for (;;)

-        {

-            const Block* const pBlock = pBlockEntry->GetBlock();

-            assert(pBlock);

-

-            const long long tn = pBlock->GetTrackNumber();

-

-            if ((tn == m_info.number) && VetEntry(pBlockEntry))

-                return 0;

-

-            const BlockEntry* pNextEntry;

-

-            status = pCluster->GetNext(pBlockEntry, pNextEntry);

-

-            if (status < 0)  //error

-                return status;

-

-            if (pNextEntry == 0)

-                break;

-

-            pBlockEntry = pNextEntry;

-        }

-

-        ++i;

-

-        if (i >= 100)

-            break;

-

-        pCluster = m_pSegment->GetNext(pCluster);

-    }

-

-    //NOTE: if we get here, it means that we didn't find a block with

-    //a matching track number.  We interpret that as an error (which

-    //might be too conservative).

-

-    pBlockEntry = GetEOS();  //so we can return a non-NULL value

-    return 1;

-}

-

-

-long Track::GetNext(

-    const BlockEntry* pCurrEntry,

-    const BlockEntry*& pNextEntry) const

-{

-    assert(pCurrEntry);

-    assert(!pCurrEntry->EOS());  //?

-

-    const Block* const pCurrBlock = pCurrEntry->GetBlock();

-    assert(pCurrBlock->GetTrackNumber() == m_info.number);

-

-    const Cluster* pCluster = pCurrEntry->GetCluster();

-    assert(pCluster);

-    assert(!pCluster->EOS());

-

-    long status = pCluster->GetNext(pCurrEntry, pNextEntry);

-

-    if (status < 0)  //error

-        return status;

-

-    for (int i = 0; ; )

-    {

-        while (pNextEntry)

-        {

-            const Block* const pNextBlock = pNextEntry->GetBlock();

-            assert(pNextBlock);

-

-            if (pNextBlock->GetTrackNumber() == m_info.number)

-                return 0;

-

-            pCurrEntry = pNextEntry;

-

-            status = pCluster->GetNext(pCurrEntry, pNextEntry);

-

-            if (status < 0) //error

-                return status;

-        }

-

-        pCluster = m_pSegment->GetNext(pCluster);

-

-        if (pCluster == NULL)

-        {

-            pNextEntry = GetEOS();

-            return 1;

-        }

-

-        if (pCluster->EOS())

-        {

-#if 0

-            if (m_pSegment->Unparsed() <= 0)   //all clusters have been loaded

-            {

-                pNextEntry = GetEOS();

-                return 1;

-            }

-#else

-            if (m_pSegment->DoneParsing())

-            {

-                pNextEntry = GetEOS();

-                return 1;

-            }

-#endif

-

-            //TODO: there is a potential O(n^2) problem here: we tell the

-            //caller to (pre)load another cluster, which he does, but then he

-            //calls GetNext again, which repeats the same search.  This is

-            //a pathological case, since the only way it can happen is if

-            //there exists a long sequence of clusters none of which contain a

-            // block from this track.  One way around this problem is for the

-            //caller to be smarter when he loads another cluster: don't call

-            //us back until you have a cluster that contains a block from this

-            //track. (Of course, that's not cheap either, since our caller

-            //would have to scan the each cluster as it's loaded, so that

-            //would just push back the problem.)

-

-            pNextEntry = NULL;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        status = pCluster->GetFirst(pNextEntry);

-

-        if (status < 0)  //error

-            return status;

-

-        if (pNextEntry == NULL)  //empty cluster

-            continue;

-

-        ++i;

-

-        if (i >= 100)

-            break;

-    }

-

-    //NOTE: if we get here, it means that we didn't find a block with

-    //a matching track number after lots of searching, so we give

-    //up trying.

-

-    pNextEntry = GetEOS();  //so we can return a non-NULL value

-    return 1;

-}

-

-const ContentEncoding*

-Track::GetContentEncodingByIndex(unsigned long idx) const {

-  const ptrdiff_t count =

-      content_encoding_entries_end_ - content_encoding_entries_;

-  assert(count >= 0);

-

-  if (idx >= static_cast<unsigned long>(count))

-    return NULL;

-

-  return content_encoding_entries_[idx];

-}

-

-unsigned long Track::GetContentEncodingCount() const {

-  const ptrdiff_t count =

-      content_encoding_entries_end_ - content_encoding_entries_;

-  assert(count >= 0);

-

-  return static_cast<unsigned long>(count);

-}

-

-void Track::ParseContentEncodingsEntry(long long start, long long size) {

-  IMkvReader* const pReader = m_pSegment->m_pReader;

-  assert(pReader);

-

-  long long pos = start;

-  const long long stop = start + size;

-

-  // Count ContentEncoding elements.

-  long long pos1 = start;

-  int count = 0;

-

-  while (pos1 < stop) {

-    long len;

-    const long long id = ReadUInt(pReader, pos1, len);

-    assert(id >= 0);

-    assert((pos1 + len) <= stop);

-

-    pos1 += len;  //consume id

-

-    const long long size = ReadUInt(pReader, pos1, len);

-    assert(size >= 0);

-    assert((pos1 + len) <= stop);

-

-    pos1 += len;  //consume length of size

-

-    //pos now designates start of element

-    if (id == 0x2240)  // ContentEncoding ID

-      ++count;

-

-    pos1 += size;  //consume payload

-    assert(pos1 <= stop);

-  }

-

-  if (count <= 0)

-    return;

-

-  content_encoding_entries_ = new ContentEncoding*[count];

-  content_encoding_entries_end_ = content_encoding_entries_;

-

-  while (pos < stop) {

-    long len;

-    const long long id = ReadUInt(pReader, pos, len);

-    assert(id >= 0);

-    assert((pos + len) <= stop);

-

-    pos += len;  //consume id

-

-    const long long size1 = ReadUInt(pReader, pos, len);

-    assert(size1 >= 0);

-    assert((pos + len) <= stop);

-

-    pos += len;  //consume length of size

-

-    //pos now designates start of element

-    if (id == 0x2240) { // ContentEncoding ID

-      ContentEncoding* const content_encoding = new ContentEncoding();

-

-      if (!content_encoding->ParseContentEncodingEntry(pos, size1, pReader)) {

-        delete content_encoding;

-      } else {

-        *content_encoding_entries_end_ = content_encoding;

-        ++content_encoding_entries_end_;

-      }

-    }

-

-    pos += size1;  //consume payload

-    assert(pos <= stop);

-  }

-

-  assert(pos == stop);

-

-  return;

-}

-

-Track::EOSBlock::EOSBlock() :

-    BlockEntry(NULL, LONG_MIN)

-{

-}

-

-BlockEntry::Kind Track::EOSBlock::GetKind() const

-{

-    return kBlockEOS;

-}

-

-

-const Block* Track::EOSBlock::GetBlock() const

-{

-    return NULL;

-}

-

-

-VideoTrack::VideoTrack(

-    Segment* pSegment,

-    long long element_start,

-    long long element_size) :

-    Track(pSegment, element_start, element_size)

-{

-}

-

-

-long VideoTrack::Parse(

-    Segment* pSegment,

-    const Info& i,

-    long long elem_st,

-    long long elem_sz,

-    VideoTrack*& pTrack)

-{

-    if (pTrack)

-        return -1;

-

-    if (i.type != Track::kVideo)

-        return -1;

-

-    long long width = 0;

-    long long height = 0;

-    double rate = 0.0;

-

-    IMkvReader* const pReader = pSegment->m_pReader;

-

-    const Settings& s = i.settings;

-    assert(s.start >= 0);

-    assert(s.size >= 0);

-

-    long long pos = s.start;

-    assert(pos >= 0);

-

-    const long long stop = pos + s.size;

-

-    while (pos < stop)

-    {

-        long long id, size;

-

-        const long status = ParseElementHeader(

-                                pReader,

-                                pos,

-                                stop,

-                                id,

-                                size);

-

-        if (status < 0)  //error

-            return status;

-

-        if (id == 0x30)  //pixel width

-        {

-            width = UnserializeUInt(pReader, pos, size);

-

-            if (width <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x3A)  //pixel height

-        {

-            height = UnserializeUInt(pReader, pos, size);

-

-            if (height <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x0383E3)  //frame rate

-        {

-            const long status = UnserializeFloat(

-                                    pReader,

-                                    pos,

-                                    size,

-                                    rate);

-

-            if (status < 0)

-                return status;

-

-            if (rate <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-

-        pos += size;  //consume payload

-        assert(pos <= stop);

-    }

-

-    assert(pos == stop);

-

-    pTrack = new (std::nothrow) VideoTrack(pSegment, elem_st, elem_sz);

-

-    if (pTrack == NULL)

-        return -1;  //generic error

-

-    const int status = i.Copy(pTrack->m_info);

-

-    if (status)

-        return status;

-

-    pTrack->m_width = width;

-    pTrack->m_height = height;

-    pTrack->m_rate = rate;

-

-    return 0;  //success

-}

-

-

-bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const

-{

-    assert(pBlockEntry);

-

-    const Block* const pBlock = pBlockEntry->GetBlock();

-    assert(pBlock);

-    assert(pBlock->GetTrackNumber() == m_info.number);

-

-    return pBlock->IsKey();

-}

-

-

-long VideoTrack::Seek(

-    long long time_ns,

-    const BlockEntry*& pResult) const

-{

-    const long status = GetFirst(pResult);

-

-    if (status < 0)  //buffer underflow, etc

-        return status;

-

-    assert(pResult);

-

-    if (pResult->EOS())

-        return 0;

-

-    const Cluster* pCluster = pResult->GetCluster();

-    assert(pCluster);

-    assert(pCluster->GetIndex() >= 0);

-

-    if (time_ns <= pResult->GetBlock()->GetTime(pCluster))

-        return 0;

-

-    Cluster** const clusters = m_pSegment->m_clusters;

-    assert(clusters);

-

-    const long count = m_pSegment->GetCount();  //loaded only, not pre-loaded

-    assert(count > 0);

-

-    Cluster** const i = clusters + pCluster->GetIndex();

-    assert(i);

-    assert(*i == pCluster);

-    assert(pCluster->GetTime() <= time_ns);

-

-    Cluster** const j = clusters + count;

-

-    Cluster** lo = i;

-    Cluster** hi = j;

-

-    while (lo < hi)

-    {

-        //INVARIANT:

-        //[i, lo) <= time_ns

-        //[lo, hi) ?

-        //[hi, j)  > time_ns

-

-        Cluster** const mid = lo + (hi - lo) / 2;

-        assert(mid < hi);

-

-        pCluster = *mid;

-        assert(pCluster);

-        assert(pCluster->GetIndex() >= 0);

-        assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));

-

-        const long long t = pCluster->GetTime();

-

-        if (t <= time_ns)

-            lo = mid + 1;

-        else

-            hi = mid;

-

-        assert(lo <= hi);

-    }

-

-    assert(lo == hi);

-    assert(lo > i);

-    assert(lo <= j);

-

-    pCluster = *--lo;

-    assert(pCluster);

-    assert(pCluster->GetTime() <= time_ns);

-

-    pResult = pCluster->GetEntry(this, time_ns);

-

-    if ((pResult != 0) && !pResult->EOS())  //found a keyframe

-        return 0;

-

-    while (lo != i)

-    {

-        pCluster = *--lo;

-        assert(pCluster);

-        assert(pCluster->GetTime() <= time_ns);

-

-#if 0

-        //TODO:

-        //We need to handle the case when a cluster

-        //contains multiple keyframes.  Simply returning

-        //the largest keyframe on the cluster isn't

-        //good enough.

-        pResult = pCluster->GetMaxKey(this);

-#else

-        pResult = pCluster->GetEntry(this, time_ns);

-#endif

-

-        if ((pResult != 0) && !pResult->EOS())

-            return 0;

-    }

-

-    //weird: we're on the first cluster, but no keyframe found

-    //should never happen but we must return something anyway

-

-    pResult = GetEOS();

-    return 0;

-}

-

-

-long long VideoTrack::GetWidth() const

-{

-    return m_width;

-}

-

-

-long long VideoTrack::GetHeight() const

-{

-    return m_height;

-}

-

-

-double VideoTrack::GetFrameRate() const

-{

-    return m_rate;

-}

-

-

-AudioTrack::AudioTrack(

-    Segment* pSegment,

-    long long element_start,

-    long long element_size) :

-    Track(pSegment, element_start, element_size)

-{

-}

-

-

-long AudioTrack::Parse(

-    Segment* pSegment,

-    const Info& i,

-    long long elem_st,

-    long long elem_sz,

-    AudioTrack*& pTrack)

-{

-    if (pTrack)

-        return -1;

-

-    if (i.type != Track::kAudio)

-        return -1;

-

-    IMkvReader* const pReader = pSegment->m_pReader;

-

-    const Settings& s = i.settings;

-    assert(s.start >= 0);

-    assert(s.size >= 0);

-

-    long long pos = s.start;

-    assert(pos >= 0);

-

-    const long long stop = pos + s.size;

-

-    double rate = 8000.0;

-    long long channels = 1;

-    long long bit_depth = 0;

-

-    while (pos < stop)

-    {

-        long long id, size;

-

-        long status = ParseElementHeader(

-                                pReader,

-                                pos,

-                                stop,

-                                id,

-                                size);

-

-        if (status < 0)  //error

-            return status;

-

-        if (id == 0x35)  //Sample Rate

-        {

-            status = UnserializeFloat(pReader, pos, size, rate);

-

-            if (status < 0)

-                return status;

-

-            if (rate <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x1F)  //Channel Count

-        {

-            channels = UnserializeUInt(pReader, pos, size);

-

-            if (channels <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x2264)  //Bit Depth

-        {

-            bit_depth = UnserializeUInt(pReader, pos, size);

-

-            if (bit_depth <= 0)

-                return E_FILE_FORMAT_INVALID;

-        }

-

-        pos += size;  //consume payload

-        assert(pos <= stop);

-    }

-

-    assert(pos == stop);

-

-    pTrack = new (std::nothrow) AudioTrack(pSegment, elem_st, elem_sz);

-

-    if (pTrack == NULL)

-        return -1;  //generic error

-

-    const int status = i.Copy(pTrack->m_info);

-

-    if (status)

-        return status;

-

-    pTrack->m_rate = rate;

-    pTrack->m_channels = channels;

-    pTrack->m_bitDepth = bit_depth;

-

-    return 0;  //success

-}

-

-

-bool AudioTrack::VetEntry(const BlockEntry* pBlockEntry) const

-{

-    assert(pBlockEntry);

-

-    const Block* const pBlock = pBlockEntry->GetBlock();

-    assert(pBlock);

-    assert(pBlock->GetTrackNumber() == m_info.number);

-

-    return true;

-}

-

-

-long AudioTrack::Seek(

-    long long time_ns,

-    const BlockEntry*& pResult) const

-{

-    const long status = GetFirst(pResult);

-

-    if (status < 0)  //buffer underflow, etc

-        return status;

-

-    assert(pResult);

-

-    if (pResult->EOS())

-        return 0;

-

-    const Cluster* pCluster = pResult->GetCluster();

-    assert(pCluster);

-    assert(pCluster->GetIndex() >= 0);

-

-    if (time_ns <= pResult->GetBlock()->GetTime(pCluster))

-        return 0;

-

-    Cluster** const clusters = m_pSegment->m_clusters;

-    assert(clusters);

-

-    const long count = m_pSegment->GetCount();  //loaded only, not preloaded

-    assert(count > 0);

-

-    Cluster** const i = clusters + pCluster->GetIndex();

-    assert(i);

-    assert(*i == pCluster);

-    assert(pCluster->GetTime() <= time_ns);

-

-    Cluster** const j = clusters + count;

-

-    Cluster** lo = i;

-    Cluster** hi = j;

-

-    while (lo < hi)

-    {

-        //INVARIANT:

-        //[i, lo) <= time_ns

-        //[lo, hi) ?

-        //[hi, j)  > time_ns

-

-        Cluster** const mid = lo + (hi - lo) / 2;

-        assert(mid < hi);

-

-        pCluster = *mid;

-        assert(pCluster);

-        assert(pCluster->GetIndex() >= 0);

-        assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));

-

-        const long long t = pCluster->GetTime();

-

-        if (t <= time_ns)

-            lo = mid + 1;

-        else

-            hi = mid;

-

-        assert(lo <= hi);

-    }

-

-    assert(lo == hi);

-    assert(lo > i);

-    assert(lo <= j);

-

-    while (lo > i)

-    {

-        pCluster = *--lo;

-        assert(pCluster);

-        assert(pCluster->GetTime() <= time_ns);

-

-        pResult = pCluster->GetEntry(this);

-

-        if ((pResult != 0) && !pResult->EOS())

-            return 0;

-

-        //landed on empty cluster (no entries)

-    }

-

-    pResult = GetEOS();  //weird

-    return 0;

-}

-

-

-double AudioTrack::GetSamplingRate() const

-{

-    return m_rate;

-}

-

-

-long long AudioTrack::GetChannels() const

-{

-    return m_channels;

-}

-

-long long AudioTrack::GetBitDepth() const

-{

-    return m_bitDepth;

-}

-

-Tracks::Tracks(

-    Segment* pSegment,

-    long long start,

-    long long size_,

-    long long element_start,

-    long long element_size) :

-    m_pSegment(pSegment),

-    m_start(start),

-    m_size(size_),

-    m_element_start(element_start),

-    m_element_size(element_size),

-    m_trackEntries(NULL),

-    m_trackEntriesEnd(NULL)

-{

-}

-

-

-long Tracks::Parse()

-{

-    assert(m_trackEntries == NULL);

-    assert(m_trackEntriesEnd == NULL);

-

-    const long long stop = m_start + m_size;

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    int count = 0;

-    long long pos = m_start;

-

-    while (pos < stop)

-    {

-        long long id, size;

-

-        const long status = ParseElementHeader(

-                                pReader,

-                                pos,

-                                stop,

-                                id,

-                                size);

-

-        if (status < 0)  //error

-            return status;

-

-        if (size == 0)  //weird

-            continue;

-

-        if (id == 0x2E)  //TrackEntry ID

-            ++count;

-

-        pos += size;  //consume payload

-        assert(pos <= stop);

-    }

-

-    assert(pos == stop);

-

-    if (count <= 0)

-        return 0;  //success

-

-    m_trackEntries = new (std::nothrow) Track*[count];

-

-    if (m_trackEntries == NULL)

-        return -1;

-

-    m_trackEntriesEnd = m_trackEntries;

-

-    pos = m_start;

-

-    while (pos < stop)

-    {

-        const long long element_start = pos;

-

-        long long id, payload_size;

-

-        const long status = ParseElementHeader(

-                                pReader,

-                                pos,

-                                stop,

-                                id,

-                                payload_size);

-

-        if (status < 0)  //error

-            return status;

-

-        if (payload_size == 0)  //weird

-            continue;

-

-        const long long payload_stop = pos + payload_size;

-        assert(payload_stop <= stop);  //checked in ParseElement

-

-        const long long element_size = payload_stop - element_start;

-

-        if (id == 0x2E)  //TrackEntry ID

-        {

-            Track*& pTrack = *m_trackEntriesEnd;

-            pTrack = NULL;

-

-            const long status = ParseTrackEntry(

-                                    pos,

-                                    payload_size,

-                                    element_start,

-                                    element_size,

-                                    pTrack);

-

-            if (status)

-                return status;

-

-            if (pTrack)

-                ++m_trackEntriesEnd;

-        }

-

-        pos = payload_stop;

-        assert(pos <= stop);

-    }

-

-    assert(pos == stop);

-

-    return 0;  //success

-}

-

-

-unsigned long Tracks::GetTracksCount() const

-{

-    const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;

-    assert(result >= 0);

-

-    return static_cast<unsigned long>(result);

-}

-

-long Tracks::ParseTrackEntry(

-    long long track_start,

-    long long track_size,

-    long long elem_st,

-    long long elem_sz,

-    Track*& pTrack) const

-{

-    if (pTrack)

-        return -1;

-

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    long long pos = track_start;

-    const long long track_stop = track_start + track_size;

-

-    Track::Info i;

-

-    i.type = 0;

-    i.number = 0;

-    i.uid = 0;

-

-    Track::Settings v;

-    v.start = -1;

-    v.size = -1;

-

-    Track::Settings a;

-    a.start = -1;

-    a.size = -1;

-

-    Track::Settings e;  //content_encodings_settings;

-    e.start = -1;

-    e.size = -1;

-

-    long long lacing = 1;  //default is true

-

-    while (pos < track_stop)

-    {

-        long long id, size;

-

-        const long status = ParseElementHeader(

-                                pReader,

-                                pos,

-                                track_stop,

-                                id,

-                                size);

-

-        if (status < 0)  //error

-            return status;

-

-        const long long start = pos;

-

-        if (id == 0x60)  // VideoSettings ID

-        {

-            if (size < 0)

-                return E_FILE_FORMAT_INVALID;

-

-            v.start = start;

-            v.size = size;

-        }

-        else if (id == 0x61)  // AudioSettings ID

-        {

-            if (size < 0)

-                return E_FILE_FORMAT_INVALID;

-

-            a.start = start;

-            a.size = size;

-        }

-        else if (id == 0x2D80) // ContentEncodings ID

-        {

-            if (size < 0)

-                return E_FILE_FORMAT_INVALID;

-

-            e.start = start;

-            e.size = size;

-        }

-        else if (id == 0x33C5)  //Track UID

-        {

-            if ((size <= 0) || (size > 8))

-                return E_FILE_FORMAT_INVALID;

-

-            i.uid = 0;

-

-            long long pos_ = start;

-            const long long pos_end = start + size;

-

-            while (pos_ != pos_end)

-            {

-                unsigned char b;

-

-                const int status = pReader->Read(pos_, 1, &b);

-

-                if (status)

-                    return status;

-

-                i.uid <<= 8;

-                i.uid |= b;

-

-                ++pos_;

-            }

-        }

-        else if (id == 0x57)  //Track Number

-        {

-            const long long num = UnserializeUInt(pReader, pos, size);

-

-            if ((num <= 0) || (num > 127))

-                return E_FILE_FORMAT_INVALID;

-

-            i.number = static_cast<long>(num);

-        }

-        else if (id == 0x03)  //Track Type

-        {

-            const long long type = UnserializeUInt(pReader, pos, size);

-

-            if ((type <= 0) || (type > 254))

-                return E_FILE_FORMAT_INVALID;

-

-            i.type = static_cast<long>(type);

-        }

-        else if (id == 0x136E)  //Track Name

-        {

-            const long status = UnserializeString(

-                                    pReader,

-                                    pos,

-                                    size,

-                                    i.nameAsUTF8);

-

-            if (status)

-                return status;

-        }

-        else if (id == 0x06)  //CodecID

-        {

-            const long status = UnserializeString(

-                                    pReader,

-                                    pos,

-                                    size,

-                                    i.codecId);

-

-            if (status)

-                return status;

-        }

-        else if (id == 0x1C)  //lacing

-        {

-            lacing = UnserializeUInt(pReader, pos, size);

-

-            if ((lacing < 0) || (lacing > 1))

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if (id == 0x23A2)  //Codec Private

-        {

-            delete[] i.codecPrivate;

-            i.codecPrivate = NULL;

-            i.codecPrivateSize = 0;

-

-            if (size <= 0)

-                return E_FILE_FORMAT_INVALID;

-

-            const size_t buflen = static_cast<size_t>(size);

-

-            typedef unsigned char* buf_t;

-

-            const buf_t buf = new (std::nothrow) unsigned char[buflen];

-

-            if (buf == NULL)

-                return -1;

-

-            const int status = pReader->Read(pos, buflen, buf);

-

-            if (status)

-            {

-                delete[] buf;

-                return status;

-            }

-

-            i.codecPrivate = buf;

-            i.codecPrivateSize = buflen;

-        }

-        else if (id == 0x058688)  //Codec Name

-        {

-            const long status = UnserializeString(

-                                    pReader,

-                                    pos,

-                                    size,

-                                    i.codecNameAsUTF8);

-

-            if (status)

-                return status;

-        }

-

-        pos += size;  //consume payload

-        assert(pos <= track_stop);

-    }

-

-    assert(pos == track_stop);

-

-    if (i.number <= 0)  //not specified

-        return E_FILE_FORMAT_INVALID;

-

-    if (GetTrackByNumber(i.number))

-        return E_FILE_FORMAT_INVALID;

-

-    if (i.type <= 0)  //not specified

-        return E_FILE_FORMAT_INVALID;

-

-    if ((i.type != Track::kVideo) && (i.type != Track::kAudio))

-    {

-        //TODO(matthewjheaney): go ahead and create a "generic" track

-        //object, so that GetTrackByXXX always returns something, even

-        //if the object it returns has a type that is not kVideo or kAudio.

-

-        return 0;  //no error

-    }

-

-    i.lacing = (lacing > 0) ? true : false;

-

-    long status;

-

-    if (i.type == Track::kVideo)

-    {

-        if (v.start < 0)

-            return E_FILE_FORMAT_INVALID;

-

-        if (a.start >= 0)

-            return E_FILE_FORMAT_INVALID;

-

-        i.settings = v;

-

-        VideoTrack* p = NULL;

-

-        status = VideoTrack::Parse(m_pSegment, i, elem_st, elem_sz, p);

-        pTrack = p;

-    }

-    else

-    {

-        assert(i.type == Track::kAudio);

-

-        if (a.start < 0)

-            return E_FILE_FORMAT_INVALID;

-

-        if (v.start >= 0)

-            return E_FILE_FORMAT_INVALID;

-

-        i.settings = a;

-

-        AudioTrack* p = NULL;

-

-        status = AudioTrack::Parse(m_pSegment, i, elem_st, elem_sz, p);

-        pTrack = p;

-    }

-

-    if (status)

-        return status;

-

-    assert(pTrack);

-

-    if (e.start >= 0)

-        pTrack->ParseContentEncodingsEntry(e.start, e.size);

-

-    return 0;  //success

-}

-

-

-Tracks::~Tracks()

-{

-    Track** i = m_trackEntries;

-    Track** const j = m_trackEntriesEnd;

-

-    while (i != j)

-    {

-        Track* const pTrack = *i++;

-        delete pTrack;

-    }

-

-    delete[] m_trackEntries;

-}

-

-const Track* Tracks::GetTrackByNumber(long tn) const

-{

-    if (tn < 0)

-        return NULL;

-

-    Track** i = m_trackEntries;

-    Track** const j = m_trackEntriesEnd;

-

-    while (i != j)

-    {

-        Track* const pTrack = *i++;

-

-        if (pTrack == NULL)

-            continue;

-

-        if (tn == pTrack->GetNumber())

-            return pTrack;

-    }

-

-    return NULL;  //not found

-}

-

-

-const Track* Tracks::GetTrackByIndex(unsigned long idx) const

-{

-    const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;

-

-    if (idx >= static_cast<unsigned long>(count))

-         return NULL;

-

-    return m_trackEntries[idx];

-}

-

-#if 0

-long long Cluster::Unparsed() const

-{

-    if (m_timecode < 0)  //not even partially loaded

-        return LLONG_MAX;

-

-    assert(m_pos >= m_element_start);

-    //assert(m_element_size > m_size);

-

-    const long long element_stop = m_element_start + m_element_size;

-    assert(m_pos <= element_stop);

-

-    const long long result = element_stop - m_pos;

-    assert(result >= 0);

-

-    return result;

-}

-#endif

-

-

-long Cluster::Load(long long& pos, long& len) const

-{

-    assert(m_pSegment);

-    assert(m_pos >= m_element_start);

-

-    if (m_timecode >= 0)  //at least partially loaded

-        return 0;

-

-    assert(m_pos == m_element_start);

-    assert(m_element_size < 0);

-

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    long long total, avail;

-

-    const int status = pReader->Length(&total, &avail);

-

-    if (status < 0)  //error

-        return status;

-

-    assert((total < 0) || (avail <= total));

-    assert((total < 0) || (m_pos <= total));  //TODO: verify this

-

-    pos = m_pos;

-

-    long long cluster_size = -1;

-

-    {

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        long long result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error or underflow

-            return static_cast<long>(result);

-

-        if (result > 0)  //underflow (weird)

-            return E_BUFFER_NOT_FULL;

-

-        //if ((pos + len) > segment_stop)

-        //    return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long id_ = ReadUInt(pReader, pos, len);

-

-        if (id_ < 0)  //error

-            return static_cast<long>(id_);

-

-        if (id_ != 0x0F43B675)  //Cluster ID

-            return E_FILE_FORMAT_INVALID;

-

-        pos += len;  //consume id

-

-        //read cluster size

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        //if ((pos + len) > segment_stop)

-        //    return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long size = ReadUInt(pReader, pos, len);

-

-        if (size < 0)  //error

-            return static_cast<long>(cluster_size);

-

-        if (size == 0)

-            return E_FILE_FORMAT_INVALID;  //TODO: verify this

-

-        pos += len;  //consume length of size of element

-

-        const long long unknown_size = (1LL << (7 * len)) - 1;

-

-        if (size != unknown_size)

-            cluster_size = size;

-    }

-

-    //pos points to start of payload

-

-#if 0

-    len = static_cast<long>(size_);

-

-    if (cluster_stop > avail)

-        return E_BUFFER_NOT_FULL;

-#endif

-

-    long long timecode = -1;

-    long long new_pos = -1;

-    bool bBlock = false;

-

-    long long cluster_stop = (cluster_size < 0) ? -1 : pos + cluster_size;

-

-    for (;;)

-    {

-        if ((cluster_stop >= 0) && (pos >= cluster_stop))

-            break;

-

-        //Parse ID

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        long long result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long id = ReadUInt(pReader, pos, len);

-

-        if (id < 0) //error

-            return static_cast<long>(id);

-

-        if (id == 0)

-            return E_FILE_FORMAT_INVALID;

-

-        //This is the distinguished set of ID's we use to determine

-        //that we have exhausted the sub-element's inside the cluster

-        //whose ID we parsed earlier.

-

-        if (id == 0x0F43B675)  //Cluster ID

-            break;

-

-        if (id == 0x0C53BB6B)  //Cues ID

-            break;

-

-        pos += len;  //consume ID field

-

-        //Parse Size

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long size = ReadUInt(pReader, pos, len);

-

-        if (size < 0)  //error

-            return static_cast<long>(size);

-

-        const long long unknown_size = (1LL << (7 * len)) - 1;

-

-        if (size == unknown_size)

-            return E_FILE_FORMAT_INVALID;

-

-        pos += len;  //consume size field

-

-        if ((cluster_stop >= 0) && (pos > cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        //pos now points to start of payload

-

-        if (size == 0)  //weird

-            continue;

-

-        if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if (id == 0x67)  //TimeCode ID

-        {

-            len = static_cast<long>(size);

-

-            if ((pos + size) > avail)

-                return E_BUFFER_NOT_FULL;

-

-            timecode = UnserializeUInt(pReader, pos, size);

-

-            if (timecode < 0)  //error (or underflow)

-                return static_cast<long>(timecode);

-

-            new_pos = pos + size;

-

-            if (bBlock)

-                break;

-        }

-        else if (id == 0x20)  //BlockGroup ID

-        {

-            bBlock = true;

-            break;

-        }

-        else if (id == 0x23)  //SimpleBlock ID

-        {

-            bBlock = true;

-            break;

-        }

-

-        pos += size;  //consume payload

-        assert((cluster_stop < 0) || (pos <= cluster_stop));

-    }

-

-    assert((cluster_stop < 0) || (pos <= cluster_stop));

-

-    if (timecode < 0)  //no timecode found

-        return E_FILE_FORMAT_INVALID;

-

-    if (!bBlock)

-        return E_FILE_FORMAT_INVALID;

-

-    m_pos = new_pos;  //designates position just beyond timecode payload

-    m_timecode = timecode;  // m_timecode >= 0 means we're partially loaded

-

-    if (cluster_size >= 0)

-        m_element_size = cluster_stop - m_element_start;

-

-    return 0;

-}

-

-

-long Cluster::Parse(long long& pos, long& len) const

-{

-    long status = Load(pos, len);

-

-    if (status < 0)

-        return status;

-

-    assert(m_pos >= m_element_start);

-    assert(m_timecode >= 0);

-    //assert(m_size > 0);

-    //assert(m_element_size > m_size);

-

-    const long long cluster_stop =

-        (m_element_size < 0) ? -1 : m_element_start + m_element_size;

-

-    if ((cluster_stop >= 0) && (m_pos >= cluster_stop))

-        return 1;  //nothing else to do

-

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    long long total, avail;

-

-    status = pReader->Length(&total, &avail);

-

-    if (status < 0)  //error

-        return status;

-

-    assert((total < 0) || (avail <= total));

-

-    pos = m_pos;

-

-    for (;;)

-    {

-        if ((cluster_stop >= 0) && (pos >= cluster_stop))

-            break;

-

-        if ((total >= 0) && (pos >= total))

-        {

-            if (m_element_size < 0)

-                m_element_size = pos - m_element_start;

-

-            break;

-        }

-

-        //Parse ID

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        long long result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long id = ReadUInt(pReader, pos, len);

-

-        if (id < 0) //error

-            return static_cast<long>(id);

-

-        if (id == 0)  //weird

-            return E_FILE_FORMAT_INVALID;

-

-        //This is the distinguished set of ID's we use to determine

-        //that we have exhausted the sub-element's inside the cluster

-        //whose ID we parsed earlier.

-

-        if ((id == 0x0F43B675) || (id == 0x0C53BB6B)) //Cluster or Cues ID

-        {

-            if (m_element_size < 0)

-                m_element_size = pos - m_element_start;

-

-            break;

-        }

-

-        pos += len;  //consume ID field

-

-        //Parse Size

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long size = ReadUInt(pReader, pos, len);

-

-        if (size < 0)  //error

-            return static_cast<long>(size);

-

-        const long long unknown_size = (1LL << (7 * len)) - 1;

-

-        if (size == unknown_size)

-            return E_FILE_FORMAT_INVALID;

-

-        pos += len;  //consume size field

-

-        if ((cluster_stop >= 0) && (pos > cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        //pos now points to start of payload

-

-        if (size == 0)  //weird

-            continue;

-

-        //const long long block_start = pos;

-        const long long block_stop = pos + size;

-

-        if (cluster_stop >= 0)

-        {

-            if (block_stop > cluster_stop)

-                return E_FILE_FORMAT_INVALID;

-        }

-        else if ((total >= 0) && (block_stop > total))

-        {

-            m_element_size = total - m_element_start;

-            pos = total;

-            break;

-        }

-        else if (block_stop > avail)

-        {

-            len = static_cast<long>(size);

-            return E_BUFFER_NOT_FULL;

-        }

-

-        Cluster* const this_ = const_cast<Cluster*>(this);

-

-        if (id == 0x20)  //BlockGroup

-            return this_->ParseBlockGroup(size, pos, len);

-

-        if (id == 0x23)  //SimpleBlock

-            return this_->ParseSimpleBlock(size, pos, len);

-

-        pos += size;  //consume payload

-        assert((cluster_stop < 0) || (pos <= cluster_stop));

-    }

-

-    assert(m_element_size > 0);

-

-    m_pos = pos;

-    assert((cluster_stop < 0) || (m_pos <= cluster_stop));

-

-    if (m_entries_count > 0)

-    {

-        const long idx = m_entries_count - 1;

-

-        const BlockEntry* const pLast = m_entries[idx];

-        assert(pLast);

-

-        const Block* const pBlock = pLast->GetBlock();

-        assert(pBlock);

-

-        const long long start = pBlock->m_start;

-

-        if ((total >= 0) && (start > total))

-            return -1;  //defend against trucated stream

-

-        const long long size = pBlock->m_size;

-

-        const long long stop = start + size;

-        assert((cluster_stop < 0) || (stop <= cluster_stop));

-

-        if ((total >= 0) && (stop > total))

-            return -1;  //defend against trucated stream

-    }

-

-    return 1;  //no more entries

-}

-

-

-long Cluster::ParseSimpleBlock(

-    long long block_size,

-    long long& pos,

-    long& len)

-{

-    const long long block_start = pos;

-    const long long block_stop = pos + block_size;

-

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    long long total, avail;

-

-    long status = pReader->Length(&total, &avail);

-

-    if (status < 0)  //error

-        return status;

-

-    assert((total < 0) || (avail <= total));

-

-    //parse track number

-

-    if ((pos + 1) > avail)

-    {

-        len = 1;

-        return E_BUFFER_NOT_FULL;

-    }

-

-    long long result = GetUIntLength(pReader, pos, len);

-

-    if (result < 0)  //error

-        return static_cast<long>(result);

-

-    if (result > 0)  //weird

-        return E_BUFFER_NOT_FULL;

-

-    if ((pos + len) > block_stop)

-        return E_FILE_FORMAT_INVALID;

-

-    if ((pos + len) > avail)

-        return E_BUFFER_NOT_FULL;

-

-    const long long track = ReadUInt(pReader, pos, len);

-

-    if (track < 0) //error

-        return static_cast<long>(track);

-

-    if (track == 0)

-        return E_FILE_FORMAT_INVALID;

-

-#if 0

-    //TODO(matthewjheaney)

-    //This turned out to be too conservative.  The problem is that

-    //if we see a track header in the tracks element with an unsupported

-    //track type, we throw that track header away, so it is not present

-    //in the track map.  But even though we don't understand the track

-    //header, there are still blocks in the cluster with that track

-    //number.  It was our decision to ignore that track header, so it's

-    //up to us to deal with blocks associated with that track -- we

-    //cannot simply report an error since technically there's nothing

-    //wrong with the file.

-    //

-    //For now we go ahead and finish the parse, creating a block entry

-    //for this block.  This is somewhat wasteful, because without a

-    //track header there's nothing you can do with the block. What

-    //we really need here is a special return value that indicates to

-    //the caller that he should ignore this particular block, and

-    //continue parsing.

-

-    const Tracks* const pTracks = m_pSegment->GetTracks();

-    assert(pTracks);

-

-    const long tn = static_cast<long>(track);

-

-    const Track* const pTrack = pTracks->GetTrackByNumber(tn);

-

-    if (pTrack == NULL)

-        return E_FILE_FORMAT_INVALID;

-#endif

-

-    pos += len;  //consume track number

-

-    if ((pos + 2) > block_stop)

-        return E_FILE_FORMAT_INVALID;

-

-    if ((pos + 2) > avail)

-    {

-        len = 2;

-        return E_BUFFER_NOT_FULL;

-    }

-

-    pos += 2;  //consume timecode

-

-    if ((pos + 1) > block_stop)

-        return E_FILE_FORMAT_INVALID;

-

-    if ((pos + 1) > avail)

-    {

-        len = 1;

-        return E_BUFFER_NOT_FULL;

-    }

-

-    unsigned char flags;

-

-    status = pReader->Read(pos, 1, &flags);

-

-    if (status < 0)  //error or underflow

-    {

-        len = 1;

-        return status;

-    }

-

-    ++pos;  //consume flags byte

-    assert(pos <= avail);

-

-    if (pos >= block_stop)

-        return E_FILE_FORMAT_INVALID;

-

-    const int lacing = int(flags & 0x06) >> 1;

-

-    if ((lacing != 0) && (block_stop > avail))

-    {

-        len = static_cast<long>(block_stop - pos);

-        return E_BUFFER_NOT_FULL;

-    }

-

-    status = CreateBlock(0x23, block_start, block_size);  //simple block id

-

-    if (status != 0)

-        return status;

-

-    m_pos = block_stop;

-

-    return 0;  //success

-}

-

-

-long Cluster::ParseBlockGroup(

-    long long payload_size,

-    long long& pos,

-    long& len)

-{

-    const long long payload_start = pos;

-    const long long payload_stop = pos + payload_size;

-

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    long long total, avail;

-

-    long status = pReader->Length(&total, &avail);

-

-    if (status < 0)  //error

-        return status;

-

-    assert((total < 0) || (avail <= total));

-

-    if ((total >= 0) && (payload_stop > total))

-        return E_FILE_FORMAT_INVALID;

-

-    if (payload_stop > avail)

-    {

-         len = static_cast<long>(payload_size);

-         return E_BUFFER_NOT_FULL;

-    }

-

-    while (pos < payload_stop)

-    {

-        //parse sub-block element ID

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        long long result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((pos + len) > payload_stop)

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long id = ReadUInt(pReader, pos, len);

-

-        if (id < 0) //error

-            return static_cast<long>(id);

-

-        if (id == 0)  //not a value ID

-            return E_FILE_FORMAT_INVALID;

-

-        pos += len;  //consume ID field

-

-        //Parse Size

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((pos + len) > payload_stop)

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long size = ReadUInt(pReader, pos, len);

-

-        if (size < 0)  //error

-            return static_cast<long>(size);

-

-        pos += len;  //consume size field

-

-        //pos now points to start of sub-block group payload

-

-        if (pos > payload_stop)

-            return E_FILE_FORMAT_INVALID;

-

-        if (size == 0)  //weird

-            continue;

-

-        const long long unknown_size = (1LL << (7 * len)) - 1;

-

-        if (size == unknown_size)

-            return E_FILE_FORMAT_INVALID;

-

-        if (id != 0x21)  //sub-part of BlockGroup is not a Block

-        {

-            pos += size;  //consume sub-part of block group

-

-            if (pos > payload_stop)

-                return E_FILE_FORMAT_INVALID;

-

-            continue;

-        }

-

-        const long long block_stop = pos + size;

-

-        if (block_stop > payload_stop)

-            return E_FILE_FORMAT_INVALID;

-

-        //parse track number

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((pos + len) > block_stop)

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long track = ReadUInt(pReader, pos, len);

-

-        if (track < 0) //error

-            return static_cast<long>(track);

-

-        if (track == 0)

-            return E_FILE_FORMAT_INVALID;

-

-#if 0

-        //TODO(matthewjheaney)

-        //This turned out to be too conservative.  The problem is that

-        //if we see a track header in the tracks element with an unsupported

-        //track type, we throw that track header away, so it is not present

-        //in the track map.  But even though we don't understand the track

-        //header, there are still blocks in the cluster with that track

-        //number.  It was our decision to ignore that track header, so it's

-        //up to us to deal with blocks associated with that track -- we

-        //cannot simply report an error since technically there's nothing

-        //wrong with the file.

-        //

-        //For now we go ahead and finish the parse, creating a block entry

-        //for this block.  This is somewhat wasteful, because without a

-        //track header there's nothing you can do with the block. What

-        //we really need here is a special return value that indicates to

-        //the caller that he should ignore this particular block, and

-        //continue parsing.

-

-        const Tracks* const pTracks = m_pSegment->GetTracks();

-        assert(pTracks);

-

-        const long tn = static_cast<long>(track);

-

-        const Track* const pTrack = pTracks->GetTrackByNumber(tn);

-

-        if (pTrack == NULL)

-            return E_FILE_FORMAT_INVALID;

-#endif

-

-        pos += len;  //consume track number

-

-        if ((pos + 2) > block_stop)

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + 2) > avail)

-        {

-            len = 2;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        pos += 2;  //consume timecode

-

-        if ((pos + 1) > block_stop)

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        unsigned char flags;

-

-        status = pReader->Read(pos, 1, &flags);

-

-        if (status < 0)  //error or underflow

-        {

-            len = 1;

-            return status;

-        }

-

-        ++pos;  //consume flags byte

-        assert(pos <= avail);

-

-        if (pos >= block_stop)

-            return E_FILE_FORMAT_INVALID;

-

-        const int lacing = int(flags & 0x06) >> 1;

-

-        if ((lacing != 0) && (block_stop > avail))

-        {

-            len = static_cast<long>(block_stop - pos);

-            return E_BUFFER_NOT_FULL;

-        }

-

-        pos = block_stop;  //consume block-part of block group

-        assert(pos <= payload_stop);

-    }

-

-    assert(pos == payload_stop);

-

-    status = CreateBlock(0x20, payload_start, payload_size);  //BlockGroup ID

-

-    if (status != 0)

-        return status;

-

-    m_pos = payload_stop;

-

-    return 0;  //success

-}

-

-

-long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const

-{

-    assert(m_pos >= m_element_start);

-

-    pEntry = NULL;

-

-    if (index < 0)

-        return -1;  //generic error

-

-    if (m_entries_count < 0)

-        return E_BUFFER_NOT_FULL;

-

-    assert(m_entries);

-    assert(m_entries_size > 0);

-    assert(m_entries_count <= m_entries_size);

-

-    if (index < m_entries_count)

-    {

-        pEntry = m_entries[index];

-        assert(pEntry);

-

-        return 1;  //found entry

-    }

-

-    if (m_element_size < 0)        //we don't know cluster end yet

-        return E_BUFFER_NOT_FULL;  //underflow

-

-    const long long element_stop = m_element_start + m_element_size;

-

-    if (m_pos >= element_stop)

-        return 0;  //nothing left to parse

-

-    return E_BUFFER_NOT_FULL;  //underflow, since more remains to be parsed

-}

-

-

-Cluster* Cluster::Create(

-    Segment* pSegment,

-    long idx,

-    long long off)

-    //long long element_size)

-{

-    assert(pSegment);

-    assert(off >= 0);

-

-    const long long element_start = pSegment->m_start + off;

-

-    Cluster* const pCluster = new Cluster(pSegment,

-                                          idx,

-                                          element_start);

-                                          //element_size);

-    assert(pCluster);

-

-    return pCluster;

-}

-

-

-Cluster::Cluster() :

-    m_pSegment(NULL),

-    m_element_start(0),

-    m_index(0),

-    m_pos(0),

-    m_element_size(0),

-    m_timecode(0),

-    m_entries(NULL),

-    m_entries_size(0),

-    m_entries_count(0)  //means "no entries"

-{

-}

-

-

-Cluster::Cluster(

-    Segment* pSegment,

-    long idx,

-    long long element_start

-    /* long long element_size */ ) :

-    m_pSegment(pSegment),

-    m_element_start(element_start),

-    m_index(idx),

-    m_pos(element_start),

-    m_element_size(-1 /* element_size */ ),

-    m_timecode(-1),

-    m_entries(NULL),

-    m_entries_size(0),

-    m_entries_count(-1)  //means "has not been parsed yet"

-{

-}

-

-

-Cluster::~Cluster()

-{

-    if (m_entries_count <= 0)

-        return;

-

-    BlockEntry** i = m_entries;

-    BlockEntry** const j = m_entries + m_entries_count;

-

-    while (i != j)

-    {

-         BlockEntry* p = *i++;

-         assert(p);

-

-         delete p;

-    }

-

-    delete[] m_entries;

-}

-

-

-bool Cluster::EOS() const

-{

-    return (m_pSegment == NULL);

-}

-

-

-long Cluster::GetIndex() const

-{

-    return m_index;

-}

-

-

-long long Cluster::GetPosition() const

-{

-    const long long pos = m_element_start - m_pSegment->m_start;

-    assert(pos >= 0);

-

-    return pos;

-}

-

-

-long long Cluster::GetElementSize() const

-{

-    return m_element_size;

-}

-

-

-#if 0

-bool Cluster::HasBlockEntries(

-    const Segment* pSegment,

-    long long off)  //relative to start of segment payload

-{

-    assert(pSegment);

-    assert(off >= 0);  //relative to segment

-

-    IMkvReader* const pReader = pSegment->m_pReader;

-

-    long long pos = pSegment->m_start + off;  //absolute

-    long long size;

-

-    {

-        long len;

-

-        const long long id = ReadUInt(pReader, pos, len);

-        (void)id;

-        assert(id >= 0);

-        assert(id == 0x0F43B675);  //Cluster ID

-

-        pos += len;  //consume id

-

-        size = ReadUInt(pReader, pos, len);

-        assert(size > 0);

-

-        pos += len;  //consume size

-

-        //pos now points to start of payload

-    }

-

-    const long long stop = pos + size;

-

-    while (pos < stop)

-    {

-        long len;

-

-        const long long id = ReadUInt(pReader, pos, len);

-        assert(id >= 0);  //TODO

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume id

-

-        const long long size = ReadUInt(pReader, pos, len);

-        assert(size >= 0);  //TODO

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume size

-

-        if (id == 0x20)  //BlockGroup ID

-            return true;

-

-        if (id == 0x23)  //SimpleBlock ID

-            return true;

-

-        pos += size;  //consume payload

-        assert(pos <= stop);

-    }

-

-    return false;

-}

-#endif

-

-

-long Cluster::HasBlockEntries(

-    const Segment* pSegment,

-    long long off,  //relative to start of segment payload

-    long long& pos,

-    long& len)

-{

-    assert(pSegment);

-    assert(off >= 0);  //relative to segment

-

-    IMkvReader* const pReader = pSegment->m_pReader;

-

-    long long total, avail;

-

-    long status = pReader->Length(&total, &avail);

-

-    if (status < 0)  //error

-        return status;

-

-    assert((total < 0) || (avail <= total));

-

-    pos = pSegment->m_start + off;  //absolute

-

-    if ((total >= 0) && (pos >= total))

-        return 0;  //we don't even have a complete cluster

-

-    const long long segment_stop =

-        (pSegment->m_size < 0) ? -1 : pSegment->m_start + pSegment->m_size;

-

-    long long cluster_stop = -1;  //interpreted later to mean "unknown size"

-

-    {

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        long long result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //need more data

-            return E_BUFFER_NOT_FULL;

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((total >= 0) && ((pos + len) > total))

-            return 0;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long id = ReadUInt(pReader, pos, len);

-

-        if (id < 0)  //error

-            return static_cast<long>(id);

-

-        if (id != 0x0F43B675)  //weird: not cluster ID

-            return -1;         //generic error

-

-        pos += len;  //consume Cluster ID field

-

-        //read size field

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //weird

-            return E_BUFFER_NOT_FULL;

-

-        if ((segment_stop >= 0) && ((pos + len) > segment_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((total >= 0) && ((pos + len) > total))

-            return 0;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long size = ReadUInt(pReader, pos, len);

-

-        if (size < 0)  //error

-            return static_cast<long>(size);

-

-        if (size == 0)

-            return 0;  //cluster does not have entries

-

-        pos += len;  //consume size field

-

-        //pos now points to start of payload

-

-        const long long unknown_size = (1LL << (7 * len)) - 1;

-

-        if (size != unknown_size)

-        {

-            cluster_stop = pos + size;

-            assert(cluster_stop >= 0);

-

-            if ((segment_stop >= 0) && (cluster_stop > segment_stop))

-                return E_FILE_FORMAT_INVALID;

-

-            if ((total >= 0) && (cluster_stop > total))

-                //return E_FILE_FORMAT_INVALID;  //too conservative

-                return 0;  //cluster does not have any entries

-        }

-    }

-

-    for (;;)

-    {

-        if ((cluster_stop >= 0) && (pos >= cluster_stop))

-            return 0;  //no entries detected

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        long long result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //need more data

-            return E_BUFFER_NOT_FULL;

-

-        if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long id = ReadUInt(pReader, pos, len);

-

-        if (id < 0)  //error

-            return static_cast<long>(id);

-

-        //This is the distinguished set of ID's we use to determine

-        //that we have exhausted the sub-element's inside the cluster

-        //whose ID we parsed earlier.

-

-        if (id == 0x0F43B675)  //Cluster ID

-            return 0;  //no entries found

-

-        if (id == 0x0C53BB6B)  //Cues ID

-            return 0;  //no entries found

-

-        pos += len;  //consume id field

-

-        if ((cluster_stop >= 0) && (pos >= cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        //read size field

-

-        if ((pos + 1) > avail)

-        {

-            len = 1;

-            return E_BUFFER_NOT_FULL;

-        }

-

-        result = GetUIntLength(pReader, pos, len);

-

-        if (result < 0)  //error

-            return static_cast<long>(result);

-

-        if (result > 0)  //underflow

-            return E_BUFFER_NOT_FULL;

-

-        if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > avail)

-            return E_BUFFER_NOT_FULL;

-

-        const long long size = ReadUInt(pReader, pos, len);

-

-        if (size < 0)  //error

-            return static_cast<long>(size);

-

-        pos += len;  //consume size field

-

-        //pos now points to start of payload

-

-        if ((cluster_stop >= 0) && (pos > cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if (size == 0)  //weird

-            continue;

-

-        const long long unknown_size = (1LL << (7 * len)) - 1;

-

-        if (size == unknown_size)

-            return E_FILE_FORMAT_INVALID;  //not supported inside cluster

-

-        if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))

-            return E_FILE_FORMAT_INVALID;

-

-        if (id == 0x20)  //BlockGroup ID

-            return 1;    //have at least one entry

-

-        if (id == 0x23)  //SimpleBlock ID

-            return 1;    //have at least one entry

-

-        pos += size;  //consume payload

-        assert((cluster_stop < 0) || (pos <= cluster_stop));

-    }

-}

-

-

-long long Cluster::GetTimeCode() const

-{

-    long long pos;

-    long len;

-

-    const long status = Load(pos, len);

-

-    if (status < 0) //error

-        return status;

-

-    return m_timecode;

-}

-

-

-long long Cluster::GetTime() const

-{

-    const long long tc = GetTimeCode();

-

-    if (tc < 0)

-        return tc;

-

-    const SegmentInfo* const pInfo = m_pSegment->GetInfo();

-    assert(pInfo);

-

-    const long long scale = pInfo->GetTimeCodeScale();

-    assert(scale >= 1);

-

-    const long long t = m_timecode * scale;

-

-    return t;

-}

-

-

-long long Cluster::GetFirstTime() const

-{

-    const BlockEntry* pEntry;

-

-    const long status = GetFirst(pEntry);

-

-    if (status < 0)  //error

-        return status;

-

-    if (pEntry == NULL)  //empty cluster

-        return GetTime();

-

-    const Block* const pBlock = pEntry->GetBlock();

-    assert(pBlock);

-

-    return pBlock->GetTime(this);

-}

-

-

-long long Cluster::GetLastTime() const

-{

-    const BlockEntry* pEntry;

-

-    const long status = GetLast(pEntry);

-

-    if (status < 0)  //error

-        return status;

-

-    if (pEntry == NULL)  //empty cluster

-        return GetTime();

-

-    const Block* const pBlock = pEntry->GetBlock();

-    assert(pBlock);

-

-    return pBlock->GetTime(this);

-}

-

-

-long Cluster::CreateBlock(

-    long long id,

-    long long pos,   //absolute pos of payload

-    long long size)

-{

-    assert((id == 0x20) || (id == 0x23));  //BlockGroup or SimpleBlock

-

-    if (m_entries_count < 0)  //haven't parsed anything yet

-    {

-        assert(m_entries == NULL);

-        assert(m_entries_size == 0);

-

-        m_entries_size = 1024;

-        m_entries = new BlockEntry*[m_entries_size];

-

-        m_entries_count = 0;

-    }

-    else

-    {

-        assert(m_entries);

-        assert(m_entries_size > 0);

-        assert(m_entries_count <= m_entries_size);

-

-        if (m_entries_count >= m_entries_size)

-        {

-            const long entries_size = 2 * m_entries_size;

-

-            BlockEntry** const entries = new BlockEntry*[entries_size];

-            assert(entries);

-

-            BlockEntry** src = m_entries;

-            BlockEntry** const src_end = src + m_entries_count;

-

-            BlockEntry** dst = entries;

-

-            while (src != src_end)

-                *dst++ = *src++;

-

-            delete[] m_entries;

-

-            m_entries = entries;

-            m_entries_size = entries_size;

-        }

-    }

-

-    if (id == 0x20)  //BlockGroup ID

-        return CreateBlockGroup(pos, size);

-    else  //SimpleBlock ID

-        return CreateSimpleBlock(pos, size);

-}

-

-

-long Cluster::CreateBlockGroup(

-    long long st,

-    long long sz)

-{

-    assert(m_entries);

-    assert(m_entries_size > 0);

-    assert(m_entries_count >= 0);

-    assert(m_entries_count < m_entries_size);

-

-    IMkvReader* const pReader = m_pSegment->m_pReader;

-

-    long long pos = st;

-    const long long stop = st + sz;

-

-    //For WebM files, there is a bias towards previous reference times

-    //(in order to support alt-ref frames, which refer back to the previous

-    //keyframe).  Normally a 0 value is not possible, but here we tenatively

-    //allow 0 as the value of a reference frame, with the interpretation

-    //that this is a "previous" reference time.

-

-    long long prev = 1;  //nonce

-    long long next = 0;  //nonce

-    long long duration = -1;  //really, this is unsigned

-

-    long long bpos = -1;

-    long long bsize = -1;

-

-    while (pos < stop)

-    {

-        long len;

-        const long long id = ReadUInt(pReader, pos, len);

-        assert(id >= 0);  //TODO

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume ID

-

-        const long long size = ReadUInt(pReader, pos, len);

-        assert(size >= 0);  //TODO

-        assert((pos + len) <= stop);

-

-        pos += len;  //consume size

-

-        if (id == 0x21) //Block ID

-        {

-            if (bpos < 0) //Block ID

-            {

-                bpos = pos;

-                bsize = size;

-            }

-        }

-        else if (id == 0x1B)  //Duration ID

-        {

-            assert(size <= 8);

-

-            duration = UnserializeUInt(pReader, pos, size);

-            assert(duration >= 0);  //TODO

-        }

-        else if (id == 0x7B)  //ReferenceBlock

-        {

-            assert(size <= 8);

-            const long size_ = static_cast<long>(size);

-

-            long long time;

-

-            long status = UnserializeInt(pReader, pos, size_, time);

-            assert(status == 0);  //TODO

-

-            if (time <= 0)  //see note above

-                prev = time;

-            else  //weird

-                next = time;

-        }

-

-        pos += size;  //consume payload

-        assert(pos <= stop);

-    }

-

-    assert(pos == stop);

-    assert(bpos >= 0);

-    assert(bsize >= 0);

-

-    const long idx = m_entries_count;

-

-    BlockEntry** const ppEntry = m_entries + idx;

-    BlockEntry*& pEntry = *ppEntry;

-

-    pEntry = new (std::nothrow) BlockGroup(

-                                  this,

-                                  idx,

-                                  bpos,

-                                  bsize,

-                                  prev,

-                                  next,

-                                  duration);

-

-    if (pEntry == NULL)

-        return -1;  //generic error

-

-    BlockGroup* const p = static_cast<BlockGroup*>(pEntry);

-

-    const long status = p->Parse();

-

-    if (status == 0)  //success

-    {

-        ++m_entries_count;

-        return 0;

-    }

-

-    delete pEntry;

-    pEntry = 0;

-

-    return status;

-}

-

-

-

-long Cluster::CreateSimpleBlock(

-    long long st,

-    long long sz)

-{

-    assert(m_entries);

-    assert(m_entries_size > 0);

-    assert(m_entries_count >= 0);

-    assert(m_entries_count < m_entries_size);

-

-    const long idx = m_entries_count;

-

-    BlockEntry** const ppEntry = m_entries + idx;

-    BlockEntry*& pEntry = *ppEntry;

-

-    pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz);

-

-    if (pEntry == NULL)

-        return -1;  //generic error

-

-    SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry);

-

-    const long status = p->Parse();

-

-    if (status == 0)

-    {

-        ++m_entries_count;

-        return 0;

-    }

-

-    delete pEntry;

-    pEntry = 0;

-

-    return status;

-}

-

-

-long Cluster::GetFirst(const BlockEntry*& pFirst) const

-{

-    if (m_entries_count <= 0)

-    {

-        long long pos;

-        long len;

-

-        const long status = Parse(pos, len);

-

-        if (status < 0)  //error

-        {

-            pFirst = NULL;

-            return status;

-        }

-

-        if (m_entries_count <= 0)  //empty cluster

-        {

-            pFirst = NULL;

-            return 0;

-        }

-    }

-

-    assert(m_entries);

-

-    pFirst = m_entries[0];

-    assert(pFirst);

-

-    return 0;  //success

-}

-

-long Cluster::GetLast(const BlockEntry*& pLast) const

-{

-    for (;;)

-    {

-        long long pos;

-        long len;

-

-        const long status = Parse(pos, len);

-

-        if (status < 0)  //error

-        {

-            pLast = NULL;

-            return status;

-        }

-

-        if (status > 0)  //no new block

-            break;

-    }

-

-    if (m_entries_count <= 0)

-    {

-        pLast = NULL;

-        return 0;

-    }

-

-    assert(m_entries);

-

-    const long idx = m_entries_count - 1;

-

-    pLast = m_entries[idx];

-    assert(pLast);

-

-    return 0;

-}

-

-

-long Cluster::GetNext(

-    const BlockEntry* pCurr,

-    const BlockEntry*& pNext) const

-{

-    assert(pCurr);

-    assert(m_entries);

-    assert(m_entries_count > 0);

-

-    size_t idx = pCurr->GetIndex();

-    assert(idx < size_t(m_entries_count));

-    assert(m_entries[idx] == pCurr);

-

-    ++idx;

-

-    if (idx >= size_t(m_entries_count))

-    {

-        long long pos;

-        long len;

-

-        const long status = Parse(pos, len);

-

-        if (status < 0)  //error

-        {

-            pNext = NULL;

-            return status;

-        }

-

-        if (status > 0)

-        {

-            pNext = NULL;

-            return 0;

-        }

-

-        assert(m_entries);

-        assert(m_entries_count > 0);

-        assert(idx < size_t(m_entries_count));

-    }

-

-    pNext = m_entries[idx];

-    assert(pNext);

-

-    return 0;

-}

-

-

-long Cluster::GetEntryCount() const

-{

-    return m_entries_count;

-}

-

-

-const BlockEntry* Cluster::GetEntry(

-    const Track* pTrack,

-    long long time_ns) const

-{

-    assert(pTrack);

-

-    if (m_pSegment == NULL)  //this is the special EOS cluster

-        return pTrack->GetEOS();

-

-#if 0

-

-    LoadBlockEntries();

-

-    if ((m_entries == NULL) || (m_entries_count <= 0))

-        return NULL;  //return EOS here?

-

-    const BlockEntry* pResult = pTrack->GetEOS();

-

-    BlockEntry** i = m_entries;

-    assert(i);

-

-    BlockEntry** const j = i + m_entries_count;

-

-    while (i != j)

-    {

-        const BlockEntry* const pEntry = *i++;

-        assert(pEntry);

-        assert(!pEntry->EOS());

-

-        const Block* const pBlock = pEntry->GetBlock();

-        assert(pBlock);

-

-        if (pBlock->GetTrackNumber() != pTrack->GetNumber())

-            continue;

-

-        if (pTrack->VetEntry(pEntry))

-        {

-            if (time_ns < 0)  //just want first candidate block

-                return pEntry;

-

-            const long long ns = pBlock->GetTime(this);

-

-            if (ns > time_ns)

-                break;

-

-            pResult = pEntry;

-        }

-        else if (time_ns >= 0)

-        {

-            const long long ns = pBlock->GetTime(this);

-

-            if (ns > time_ns)

-                break;

-        }

-    }

-

-    return pResult;

-

-#else

-

-    const BlockEntry* pResult = pTrack->GetEOS();

-

-    long index = 0;

-

-    for (;;)

-    {

-        if (index >= m_entries_count)

-        {

-            long long pos;

-            long len;

-

-            const long status = Parse(pos, len);

-            assert(status >= 0);

-

-            if (status > 0)  //completely parsed, and no more entries

-                return pResult;

-

-            if (status < 0)  //should never happen

-                return 0;

-

-            assert(m_entries);

-            assert(index < m_entries_count);

-        }

-

-        const BlockEntry* const pEntry = m_entries[index];

-        assert(pEntry);

-        assert(!pEntry->EOS());

-

-        const Block* const pBlock = pEntry->GetBlock();

-        assert(pBlock);

-

-        if (pBlock->GetTrackNumber() != pTrack->GetNumber())

-        {

-            ++index;

-            continue;

-        }

-

-        if (pTrack->VetEntry(pEntry))

-        {

-            if (time_ns < 0)  //just want first candidate block

-                return pEntry;

-

-            const long long ns = pBlock->GetTime(this);

-

-            if (ns > time_ns)

-                return pResult;

-

-            pResult = pEntry;  //have a candidate

-        }

-        else if (time_ns >= 0)

-        {

-            const long long ns = pBlock->GetTime(this);

-

-            if (ns > time_ns)

-                return pResult;

-        }

-

-        ++index;

-    }

-

-#endif

-}

-

-

-const BlockEntry*

-Cluster::GetEntry(

-    const CuePoint& cp,

-    const CuePoint::TrackPosition& tp) const

-{

-    assert(m_pSegment);

-

-#if 0

-

-    LoadBlockEntries();

-

-    if (m_entries == NULL)

-        return NULL;

-

-    const long long count = m_entries_count;

-

-    if (count <= 0)

-        return NULL;

-

-    const long long tc = cp.GetTimeCode();

-

-    if ((tp.m_block > 0) && (tp.m_block <= count))

-    {

-        const size_t block = static_cast<size_t>(tp.m_block);

-        const size_t index = block - 1;

-

-        const BlockEntry* const pEntry = m_entries[index];

-        assert(pEntry);

-        assert(!pEntry->EOS());

-

-        const Block* const pBlock = pEntry->GetBlock();

-        assert(pBlock);

-

-        if ((pBlock->GetTrackNumber() == tp.m_track) &&

-            (pBlock->GetTimeCode(this) == tc))

-        {

-            return pEntry;

-        }

-    }

-

-    const BlockEntry* const* i = m_entries;

-    const BlockEntry* const* const j = i + count;

-

-    while (i != j)

-    {

-#ifdef _DEBUG

-        const ptrdiff_t idx = i - m_entries;

-        idx;

-#endif

-

-        const BlockEntry* const pEntry = *i++;

-        assert(pEntry);

-        assert(!pEntry->EOS());

-

-        const Block* const pBlock = pEntry->GetBlock();

-        assert(pBlock);

-

-        if (pBlock->GetTrackNumber() != tp.m_track)

-            continue;

-

-        const long long tc_ = pBlock->GetTimeCode(this);

-        assert(tc_ >= 0);

-

-        if (tc_ < tc)

-            continue;

-

-        if (tc_ > tc)

-            return NULL;

-

-        const Tracks* const pTracks = m_pSegment->GetTracks();

-        assert(pTracks);

-

-        const long tn = static_cast<long>(tp.m_track);

-        const Track* const pTrack = pTracks->GetTrackByNumber(tn);

-

-        if (pTrack == NULL)

-            return NULL;

-

-        const long long type = pTrack->GetType();

-

-        if (type == 2)  //audio

-            return pEntry;

-

-        if (type != 1)  //not video

-            return NULL;

-

-        if (!pBlock->IsKey())

-            return NULL;

-

-        return pEntry;

-    }

-

-    return NULL;

-

-#else

-

-    const long long tc = cp.GetTimeCode();

-

-    if (tp.m_block > 0)

-    {

-        const long block = static_cast<long>(tp.m_block);

-        const long index = block - 1;

-

-        while (index >= m_entries_count)

-        {

-            long long pos;

-            long len;

-

-            const long status = Parse(pos, len);

-

-            if (status < 0)  //TODO: can this happen?

-                return NULL;

-

-            if (status > 0)  //nothing remains to be parsed

-                return NULL;

-        }

-

-        const BlockEntry* const pEntry = m_entries[index];

-        assert(pEntry);

-        assert(!pEntry->EOS());

-

-        const Block* const pBlock = pEntry->GetBlock();

-        assert(pBlock);

-

-        if ((pBlock->GetTrackNumber() == tp.m_track) &&

-            (pBlock->GetTimeCode(this) == tc))

-        {

-            return pEntry;

-        }

-    }

-

-    long index = 0;

-

-    for (;;)

-    {

-        if (index >= m_entries_count)

-        {

-            long long pos;

-            long len;

-

-            const long status = Parse(pos, len);

-

-            if (status < 0)  //TODO: can this happen?

-                return NULL;

-

-            if (status > 0)  //nothing remains to be parsed

-                return NULL;

-

-            assert(m_entries);

-            assert(index < m_entries_count);

-        }

-

-        const BlockEntry* const pEntry = m_entries[index];

-        assert(pEntry);

-        assert(!pEntry->EOS());

-

-        const Block* const pBlock = pEntry->GetBlock();

-        assert(pBlock);

-

-        if (pBlock->GetTrackNumber() != tp.m_track)

-        {

-            ++index;

-            continue;

-        }

-

-        const long long tc_ = pBlock->GetTimeCode(this);

-        assert(tc_ >= 0);

-

-        if (tc_ < tc)

-        {

-            ++index;

-            continue;

-        }

-

-        if (tc_ > tc)

-            return NULL;

-

-        const Tracks* const pTracks = m_pSegment->GetTracks();

-        assert(pTracks);

-

-        const long tn = static_cast<long>(tp.m_track);

-        const Track* const pTrack = pTracks->GetTrackByNumber(tn);

-

-        if (pTrack == NULL)

-            return NULL;

-

-        const long long type = pTrack->GetType();

-

-        if (type == 2)  //audio

-            return pEntry;

-

-        if (type != 1)  //not video

-            return NULL;

-

-        if (!pBlock->IsKey())

-            return NULL;

-

-        return pEntry;

-    }

-

-#endif

-

-}

-

-

-#if 0

-const BlockEntry* Cluster::GetMaxKey(const VideoTrack* pTrack) const

-{

-    assert(pTrack);

-

-    if (m_pSegment == NULL)  //EOS

-        return pTrack->GetEOS();

-

-    LoadBlockEntries();

-

-    if ((m_entries == NULL) || (m_entries_count <= 0))

-        return pTrack->GetEOS();

-

-    BlockEntry** i = m_entries + m_entries_count;

-    BlockEntry** const j = m_entries;

-

-    while (i != j)

-    {

-        const BlockEntry* const pEntry = *--i;

-        assert(pEntry);

-        assert(!pEntry->EOS());

-

-        const Block* const pBlock = pEntry->GetBlock();

-        assert(pBlock);

-

-        if (pBlock->GetTrackNumber() != pTrack->GetNumber())

-            continue;

-

-        if (pBlock->IsKey())

-            return pEntry;

-    }

-

-    return pTrack->GetEOS();  //no satisfactory block found

-}

-#endif

-

-

-BlockEntry::BlockEntry(Cluster* p, long idx) :

-    m_pCluster(p),

-    m_index(idx)

-{

-}

-

-

-BlockEntry::~BlockEntry()

-{

-}

-

-

-bool BlockEntry::EOS() const

-{

-    return (GetKind() == kBlockEOS);

-}

-

-

-const Cluster* BlockEntry::GetCluster() const

-{

-    return m_pCluster;

-}

-

-

-long BlockEntry::GetIndex() const

-{

-    return m_index;

-}

-

-

-SimpleBlock::SimpleBlock(

-    Cluster* pCluster,

-    long idx,

-    long long start,

-    long long size) :

-    BlockEntry(pCluster, idx),

-    m_block(start, size)

-{

-}

-

-

-long SimpleBlock::Parse()

-{

-    return m_block.Parse(m_pCluster->m_pSegment->m_pReader);

-}

-

-

-BlockEntry::Kind SimpleBlock::GetKind() const

-{

-    return kBlockSimple;

-}

-

-

-const Block* SimpleBlock::GetBlock() const

-{

-    return &m_block;

-}

-

-

-BlockGroup::BlockGroup(

-    Cluster* pCluster,

-    long idx,

-    long long block_start,

-    long long block_size,

-    long long prev,

-    long long next,

-    long long duration) :

-    BlockEntry(pCluster, idx),

-    m_block(block_start, block_size),

-    m_prev(prev),

-    m_next(next),

-    m_duration(duration)

-{

-}

-

-

-long BlockGroup::Parse()

-{

-    const long status = m_block.Parse(m_pCluster->m_pSegment->m_pReader);

-

-    if (status)

-        return status;

-

-    m_block.SetKey((m_prev > 0) && (m_next <= 0));

-

-    return 0;

-}

-

-

-#if 0

-void BlockGroup::ParseBlock(long long start, long long size)

-{

-    IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;

-

-    Block* const pBlock = new Block(start, size, pReader);

-    assert(pBlock);  //TODO

-

-    //TODO: the Matroska spec says you have multiple blocks within the

-    //same block group, with blocks ranked by priority (the flag bits).

-

-    assert(m_pBlock == NULL);

-    m_pBlock = pBlock;

-}

-#endif

-

-

-BlockEntry::Kind BlockGroup::GetKind() const

-{

-    return kBlockGroup;

-}

-

-

-const Block* BlockGroup::GetBlock() const

-{

-    return &m_block;

-}

-

-

-long long BlockGroup::GetPrevTimeCode() const

-{

-    return m_prev;

-}

-

-

-long long BlockGroup::GetNextTimeCode() const

-{

-    return m_next;

-}

-

-

-Block::Block(long long start, long long size_) :

-    m_start(start),

-    m_size(size_),

-    m_track(0),

-    m_timecode(-1),

-    m_flags(0),

-    m_frames(NULL),

-    m_frame_count(-1)

-{

-}

-

-

-Block::~Block()

-{

-    delete[] m_frames;

-}

-

-

-long Block::Parse(IMkvReader* pReader)

-{

-    assert(pReader);

-    assert(m_start >= 0);

-    assert(m_size >= 0);

-    assert(m_track <= 0);

-    assert(m_frames == NULL);

-    assert(m_frame_count <= 0);

-

-    long long pos = m_start;

-    const long long stop = m_start + m_size;

-

-    long len;

-

-    m_track = ReadUInt(pReader, pos, len);

-

-    if (m_track <= 0)

-        return E_FILE_FORMAT_INVALID;

-

-    if ((pos + len) > stop)

-        return E_FILE_FORMAT_INVALID;

-

-    pos += len;  //consume track number

-

-    if ((stop - pos) < 2)

-        return E_FILE_FORMAT_INVALID;

-

-    long status;

-    long long value;

-

-    status = UnserializeInt(pReader, pos, 2, value);

-

-    if (status)

-        return E_FILE_FORMAT_INVALID;

-

-    if (value < SHRT_MIN)

-        return E_FILE_FORMAT_INVALID;

-

-    if (value > SHRT_MAX)

-        return E_FILE_FORMAT_INVALID;

-

-    m_timecode = static_cast<short>(value);

-

-    pos += 2;

-

-    if ((stop - pos) <= 0)

-        return E_FILE_FORMAT_INVALID;

-

-    status = pReader->Read(pos, 1, &m_flags);

-

-    if (status)

-        return E_FILE_FORMAT_INVALID;

-

-    const int lacing = int(m_flags & 0x06) >> 1;

-

-    ++pos;  //consume flags byte

-

-    if (lacing == 0)  //no lacing

-    {

-        if (pos > stop)

-            return E_FILE_FORMAT_INVALID;

-

-        m_frame_count = 1;

-        m_frames = new Frame[m_frame_count];

-

-        Frame& f = m_frames[0];

-        f.pos = pos;

-

-        const long long frame_size = stop - pos;

-

-        if (frame_size > LONG_MAX)

-            return E_FILE_FORMAT_INVALID;

-

-        f.len = static_cast<long>(frame_size);

-

-        return 0;  //success

-    }

-

-    if (pos >= stop)

-        return E_FILE_FORMAT_INVALID;

-

-    unsigned char biased_count;

-

-    status = pReader->Read(pos, 1, &biased_count);

-

-    if (status)

-        return E_FILE_FORMAT_INVALID;

-

-    ++pos;  //consume frame count

-    assert(pos <= stop);

-

-    m_frame_count = int(biased_count) + 1;

-

-    m_frames = new Frame[m_frame_count];

-    assert(m_frames);

-

-    if (lacing == 1)  //Xiph

-    {

-        Frame* pf = m_frames;

-        Frame* const pf_end = pf + m_frame_count;

-

-        long size = 0;

-        int frame_count = m_frame_count;

-

-        while (frame_count > 1)

-        {

-            long frame_size = 0;

-

-            for (;;)

-            {

-                unsigned char val;

-

-                if (pos >= stop)

-                    return E_FILE_FORMAT_INVALID;

-

-                status = pReader->Read(pos, 1, &val);

-

-                if (status)

-                    return E_FILE_FORMAT_INVALID;

-

-                ++pos;  //consume xiph size byte

-

-                frame_size += val;

-

-                if (val < 255)

-                    break;

-            }

-

-            Frame& f = *pf++;

-            assert(pf < pf_end);

-

-            f.pos = 0;  //patch later

-

-            f.len = frame_size;

-            size += frame_size;  //contribution of this frame

-

-            --frame_count;

-        }

-

-        assert(pf < pf_end);

-        assert(pos <= stop);

-

-        {

-            Frame& f = *pf++;

-

-            if (pf != pf_end)

-                return E_FILE_FORMAT_INVALID;

-

-            f.pos = 0;  //patch later

-

-            const long long total_size = stop - pos;

-

-            if (total_size < size)

-                return E_FILE_FORMAT_INVALID;

-

-            const long long frame_size = total_size - size;

-

-            if (frame_size > LONG_MAX)

-                return E_FILE_FORMAT_INVALID;

-

-            f.len = static_cast<long>(frame_size);

-        }

-

-        pf = m_frames;

-        while (pf != pf_end)

-        {

-            Frame& f = *pf++;

-            assert((pos + f.len) <= stop);

-

-            f.pos = pos;

-            pos += f.len;

-        }

-

-        assert(pos == stop);

-    }

-    else if (lacing == 2)  //fixed-size lacing

-    {

-        const long long total_size = stop - pos;

-

-        if ((total_size % m_frame_count) != 0)

-            return E_FILE_FORMAT_INVALID;

-

-        const long long frame_size = total_size / m_frame_count;

-

-        if (frame_size > LONG_MAX)

-            return E_FILE_FORMAT_INVALID;

-

-        Frame* pf = m_frames;

-        Frame* const pf_end = pf + m_frame_count;

-

-        while (pf != pf_end)

-        {

-            assert((pos + frame_size) <= stop);

-

-            Frame& f = *pf++;

-

-            f.pos = pos;

-            f.len = static_cast<long>(frame_size);

-

-            pos += frame_size;

-        }

-

-        assert(pos == stop);

-    }

-    else

-    {

-        assert(lacing == 3);  //EBML lacing

-

-        if (pos >= stop)

-            return E_FILE_FORMAT_INVALID;

-

-        long size = 0;

-        int frame_count = m_frame_count;

-

-        long long frame_size = ReadUInt(pReader, pos, len);

-

-        if (frame_size < 0)

-            return E_FILE_FORMAT_INVALID;

-

-        if (frame_size > LONG_MAX)

-            return E_FILE_FORMAT_INVALID;

-

-        if ((pos + len) > stop)

-            return E_FILE_FORMAT_INVALID;

-

-        pos += len; //consume length of size of first frame

-

-        if ((pos + frame_size) > stop)

-            return E_FILE_FORMAT_INVALID;

-

-        Frame* pf = m_frames;

-        Frame* const pf_end = pf + m_frame_count;

-

-        {

-            Frame& curr = *pf;

-

-            curr.pos = 0;  //patch later

-

-            curr.len = static_cast<long>(frame_size);

-            size += curr.len;  //contribution of this frame

-        }

-

-        --frame_count;

-

-        while (frame_count > 1)

-        {

-            if (pos >= stop)

-                return E_FILE_FORMAT_INVALID;

-

-            assert(pf < pf_end);

-

-            const Frame& prev = *pf++;

-            assert(prev.len == frame_size);

-

-            assert(pf < pf_end);

-

-            Frame& curr = *pf;

-

-            curr.pos = 0;  //patch later

-

-            const long long delta_size_ = ReadUInt(pReader, pos, len);

-

-            if (delta_size_ < 0)

-                return E_FILE_FORMAT_INVALID;

-

-            if ((pos + len) > stop)

-                return E_FILE_FORMAT_INVALID;

-

-            pos += len;  //consume length of (delta) size

-            assert(pos <= stop);

-

-            const int exp = 7*len - 1;

-            const long long bias = (1LL << exp) - 1LL;

-            const long long delta_size = delta_size_ - bias;

-

-            frame_size += delta_size;

-

-            if (frame_size < 0)

-                return E_FILE_FORMAT_INVALID;

-

-            if (frame_size > LONG_MAX)

-                return E_FILE_FORMAT_INVALID;

-

-            curr.len = static_cast<long>(frame_size);

-            size += curr.len;  //contribution of this frame

-

-            --frame_count;

-        }

-

-        {

-            assert(pos <= stop);

-            assert(pf < pf_end);

-

-            const Frame& prev = *pf++;

-            assert(prev.len == frame_size);

-

-            assert(pf < pf_end);

-

-            Frame& curr = *pf++;

-            assert(pf == pf_end);

-

-            curr.pos = 0;  //patch later

-

-            const long long total_size = stop - pos;

-

-            if (total_size < size)

-                return E_FILE_FORMAT_INVALID;

-

-            frame_size = total_size - size;

-

-            if (frame_size > LONG_MAX)

-                return E_FILE_FORMAT_INVALID;

-

-            curr.len = static_cast<long>(frame_size);

-        }

-

-        pf = m_frames;

-        while (pf != pf_end)

-        {

-            Frame& f = *pf++;

-            assert((pos + f.len) <= stop);

-

-            f.pos = pos;

-            pos += f.len;

-        }

-

-        assert(pos == stop);

-    }

-

-    return 0;  //success

-}

-

-

-long long Block::GetTimeCode(const Cluster* pCluster) const

-{

-    if (pCluster == 0)

-        return m_timecode;

-

-    const long long tc0 = pCluster->GetTimeCode();

-    assert(tc0 >= 0);

-

-    const long long tc = tc0 + m_timecode;

-    assert(tc >= 0);

-

-    return tc;  //unscaled timecode units

-}

-

-

-long long Block::GetTime(const Cluster* pCluster) const

-{

-    assert(pCluster);

-

-    const long long tc = GetTimeCode(pCluster);

-

-    const Segment* const pSegment = pCluster->m_pSegment;

-    const SegmentInfo* const pInfo = pSegment->GetInfo();

-    assert(pInfo);

-

-    const long long scale = pInfo->GetTimeCodeScale();

-    assert(scale >= 1);

-

-    const long long ns = tc * scale;

-

-    return ns;

-}

-

-

-long long Block::GetTrackNumber() const

-{

-    return m_track;

-}

-

-

-bool Block::IsKey() const

-{

-    return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);

-}

-

-

-void Block::SetKey(bool bKey)

-{

-    if (bKey)

-        m_flags |= static_cast<unsigned char>(1 << 7);

-    else

-        m_flags &= 0x7F;

-}

-

-

-bool Block::IsInvisible() const

-{

-    return bool(int(m_flags & 0x08) != 0);

-}

-

-

-Block::Lacing Block::GetLacing() const

-{

-    const int value = int(m_flags & 0x06) >> 1;

-    return static_cast<Lacing>(value);

-}

-

-

-int Block::GetFrameCount() const

-{

-    return m_frame_count;

-}

-

-

-const Block::Frame& Block::GetFrame(int idx) const

-{

-    assert(idx >= 0);

-    assert(idx < m_frame_count);

-

-    const Frame& f = m_frames[idx];

-    assert(f.pos > 0);

-    assert(f.len > 0);

-

-    return f;

-}

-

-

-long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const

-{

-    assert(pReader);

-    assert(buf);

-

-    const long status = pReader->Read(pos, len, buf);

-    return status;

-}

-

-

-}  //end namespace mkvparser

+// 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.
+
+#include "mkvparser.hpp"
+
+#include <cassert>
+#include <climits>
+#include <cmath>
+#include <cstring>
+#include <new>
+
+#ifdef _MSC_VER
+// Disable MSVC warnings that suggest making code non-portable.
+#pragma warning(disable : 4996)
+#endif
+
+namespace mkvparser {
+
+IMkvReader::~IMkvReader() {}
+
+template<typename Type> Type* SafeArrayAlloc(unsigned long long num_elements,
+                                             unsigned long long element_size) {
+  if (num_elements == 0 || element_size == 0)
+    return NULL;
+
+  const size_t kMaxAllocSize = 0x80000000;  // 2GiB
+  const unsigned long long num_bytes = num_elements * element_size;
+  if (element_size > (kMaxAllocSize / num_elements))
+    return NULL;
+
+  return new (std::nothrow) Type[num_bytes];
+}
+
+void GetVersion(int& major, int& minor, int& build, int& revision) {
+  major = 1;
+  minor = 0;
+  build = 0;
+  revision = 30;
+}
+
+long long ReadUInt(IMkvReader* pReader, long long pos, long& len) {
+  if (!pReader || pos < 0)
+    return E_FILE_FORMAT_INVALID;
+
+  len = 1;
+  unsigned char b;
+  int status = pReader->Read(pos, 1, &b);
+
+  if (status < 0)  // error or underflow
+    return status;
+
+  if (status > 0)  // interpreted as "underflow"
+    return E_BUFFER_NOT_FULL;
+
+  if (b == 0)  // we can't handle u-int values larger than 8 bytes
+    return E_FILE_FORMAT_INVALID;
+
+  unsigned char m = 0x80;
+
+  while (!(b & m)) {
+    m >>= 1;
+    ++len;
+  }
+
+  long long result = b & (~m);
+  ++pos;
+
+  for (int i = 1; i < len; ++i) {
+    status = pReader->Read(pos, 1, &b);
+
+    if (status < 0) {
+      len = 1;
+      return status;
+    }
+
+    if (status > 0) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result <<= 8;
+    result |= b;
+
+    ++pos;
+  }
+
+  return result;
+}
+
+long long ReadID(IMkvReader* pReader, long long pos, long& len) {
+  const long long id = ReadUInt(pReader, pos, len);
+  if (id < 0 || len < 1 || len > 4) {
+    // An ID must be at least 1 byte long, and cannot exceed 4.
+    // See EBMLMaxIDLength: http://www.matroska.org/technical/specs/index.html
+    return E_FILE_FORMAT_INVALID;
+  }
+  return id;
+}
+
+long long GetUIntLength(IMkvReader* pReader, long long pos, long& len) {
+  if (!pReader || pos < 0)
+    return E_FILE_FORMAT_INVALID;
+
+  long long total, available;
+
+  int status = pReader->Length(&total, &available);
+  if (status < 0 || (total >= 0 && available > total))
+    return E_FILE_FORMAT_INVALID;
+
+  len = 1;
+
+  if (pos >= available)
+    return pos;  // too few bytes available
+
+  unsigned char b;
+
+  status = pReader->Read(pos, 1, &b);
+
+  if (status != 0)
+    return status;
+
+  if (b == 0)  // we can't handle u-int values larger than 8 bytes
+    return E_FILE_FORMAT_INVALID;
+
+  unsigned char m = 0x80;
+
+  while (!(b & m)) {
+    m >>= 1;
+    ++len;
+  }
+
+  return 0;  // success
+}
+
+// TODO(vigneshv): This function assumes that unsigned values never have their
+// high bit set.
+long long UnserializeUInt(IMkvReader* pReader, long long pos, long long size) {
+  if (!pReader || pos < 0 || (size <= 0) || (size > 8))
+    return E_FILE_FORMAT_INVALID;
+
+  long long result = 0;
+
+  for (long long i = 0; i < size; ++i) {
+    unsigned char b;
+
+    const long status = pReader->Read(pos, 1, &b);
+
+    if (status < 0)
+      return status;
+
+    result <<= 8;
+    result |= b;
+
+    ++pos;
+  }
+
+  return result;
+}
+
+long UnserializeFloat(IMkvReader* pReader, long long pos, long long size_,
+                      double& result) {
+  if (!pReader || pos < 0 || ((size_ != 4) && (size_ != 8)))
+    return E_FILE_FORMAT_INVALID;
+
+  const long size = static_cast<long>(size_);
+
+  unsigned char buf[8];
+
+  const int status = pReader->Read(pos, size, buf);
+
+  if (status < 0)  // error
+    return status;
+
+  if (size == 4) {
+    union {
+      float f;
+      unsigned long ff;
+    };
+
+    ff = 0;
+
+    for (int i = 0;;) {
+      ff |= buf[i];
+
+      if (++i >= 4)
+        break;
+
+      ff <<= 8;
+    }
+
+    result = f;
+  } else {
+    union {
+      double d;
+      unsigned long long dd;
+    };
+
+    dd = 0;
+
+    for (int i = 0;;) {
+      dd |= buf[i];
+
+      if (++i >= 8)
+        break;
+
+      dd <<= 8;
+    }
+
+    result = d;
+  }
+
+  if (isinf(result) || isnan(result))
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;
+}
+
+long UnserializeInt(IMkvReader* pReader, long long pos, long long size,
+                    long long& result_ref) {
+  if (!pReader || pos < 0 || size < 1 || size > 8)
+    return E_FILE_FORMAT_INVALID;
+
+  signed char first_byte = 0;
+  const long status = pReader->Read(pos, 1, (unsigned char*)&first_byte);
+
+  if (status < 0)
+    return status;
+
+  unsigned long long result = first_byte;
+  ++pos;
+
+  for (long i = 1; i < size; ++i) {
+    unsigned char b;
+
+    const long status = pReader->Read(pos, 1, &b);
+
+    if (status < 0)
+      return status;
+
+    result <<= 8;
+    result |= b;
+
+    ++pos;
+  }
+
+  result_ref = static_cast<long long>(result);
+  return 0;
+}
+
+long UnserializeString(IMkvReader* pReader, long long pos, long long size,
+                       char*& str) {
+  delete[] str;
+  str = NULL;
+
+  if (size >= LONG_MAX || size < 0)
+    return E_FILE_FORMAT_INVALID;
+
+  // +1 for '\0' terminator
+  const long required_size = static_cast<long>(size) + 1;
+
+  str = SafeArrayAlloc<char>(1, required_size);
+  if (str == NULL)
+    return E_FILE_FORMAT_INVALID;
+
+  unsigned char* const buf = reinterpret_cast<unsigned char*>(str);
+
+  const long status = pReader->Read(pos, size, buf);
+
+  if (status) {
+    delete[] str;
+    str = NULL;
+
+    return status;
+  }
+
+  str[required_size - 1] = '\0';
+  return 0;
+}
+
+long ParseElementHeader(IMkvReader* pReader, long long& pos,
+                        long long stop, long long& id,
+                        long long& size) {
+  if (stop >= 0 && pos >= stop)
+    return E_FILE_FORMAT_INVALID;
+
+  long len;
+
+  id = ReadID(pReader, pos, len);
+
+  if (id < 0)
+    return E_FILE_FORMAT_INVALID;
+
+  pos += len;  // consume id
+
+  if (stop >= 0 && pos >= stop)
+    return E_FILE_FORMAT_INVALID;
+
+  size = ReadUInt(pReader, pos, len);
+
+  if (size < 0 || len < 1 || len > 8) {
+    // Invalid: Negative payload size, negative or 0 length integer, or integer
+    // larger than 64 bits (libwebm cannot handle them).
+    return E_FILE_FORMAT_INVALID;
+  }
+
+  // Avoid rolling over pos when very close to LONG_LONG_MAX.
+  const unsigned long long rollover_check =
+      static_cast<unsigned long long>(pos) + len;
+  if (rollover_check > LONG_LONG_MAX)
+    return E_FILE_FORMAT_INVALID;
+
+  pos += len;  // consume length of size
+
+  // pos now designates payload
+
+  if (stop >= 0 && pos > stop)
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;  // success
+}
+
+bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
+           long long& val) {
+  if (!pReader || pos < 0)
+    return false;
+
+  long long total = 0;
+  long long available = 0;
+
+  const long status = pReader->Length(&total, &available);
+  if (status < 0 || (total >= 0 && available > total))
+    return false;
+
+  long len = 0;
+
+  const long long id = ReadID(pReader, pos, len);
+  if (id < 0 || (available - pos) > len)
+    return false;
+
+  if (static_cast<unsigned long>(id) != expected_id)
+    return false;
+
+  pos += len;  // consume id
+
+  const long long size = ReadUInt(pReader, pos, len);
+  if (size < 0 || size > 8 || len < 1 || len > 8 || (available - pos) > len)
+    return false;
+
+  pos += len;  // consume length of size of payload
+
+  val = UnserializeUInt(pReader, pos, size);
+  if (val < 0)
+    return false;
+
+  pos += size;  // consume size of payload
+
+  return true;
+}
+
+bool Match(IMkvReader* pReader, long long& pos, unsigned long expected_id,
+           unsigned char*& buf, size_t& buflen) {
+  if (!pReader || pos < 0)
+    return false;
+
+  long long total = 0;
+  long long available = 0;
+
+  long status = pReader->Length(&total, &available);
+  if (status < 0 || (total >= 0 && available > total))
+    return false;
+
+  long len = 0;
+  const long long id = ReadID(pReader, pos, len);
+  if (id < 0 || (available - pos) > len)
+    return false;
+
+  if (static_cast<unsigned long>(id) != expected_id)
+    return false;
+
+  pos += len;  // consume id
+
+  const long long size = ReadUInt(pReader, pos, len);
+  if (size < 0 || len <= 0 || len > 8 || (available - pos) > len)
+    return false;
+
+  unsigned long long rollover_check =
+      static_cast<unsigned long long>(pos) + len;
+  if (rollover_check > LONG_LONG_MAX)
+    return false;
+
+  pos += len;  // consume length of size of payload
+
+  rollover_check = static_cast<unsigned long long>(pos) + size;
+  if (rollover_check > LONG_LONG_MAX)
+    return false;
+
+  if ((pos + size) > available)
+    return false;
+
+  if (size >= LONG_MAX)
+    return false;
+
+  const long buflen_ = static_cast<long>(size);
+
+  buf = SafeArrayAlloc<unsigned char>(1, buflen_);
+  if (!buf)
+    return false;
+
+  status = pReader->Read(pos, buflen_, buf);
+  if (status != 0)
+    return false;
+
+  buflen = buflen_;
+
+  pos += size;  // consume size of payload
+  return true;
+}
+
+EBMLHeader::EBMLHeader() : m_docType(NULL) { Init(); }
+
+EBMLHeader::~EBMLHeader() { delete[] m_docType; }
+
+void EBMLHeader::Init() {
+  m_version = 1;
+  m_readVersion = 1;
+  m_maxIdLength = 4;
+  m_maxSizeLength = 8;
+
+  if (m_docType) {
+    delete[] m_docType;
+    m_docType = NULL;
+  }
+
+  m_docTypeVersion = 1;
+  m_docTypeReadVersion = 1;
+}
+
+long long EBMLHeader::Parse(IMkvReader* pReader, long long& pos) {
+  if (!pReader)
+    return E_FILE_FORMAT_INVALID;
+
+  long long total, available;
+
+  long status = pReader->Length(&total, &available);
+
+  if (status < 0)  // error
+    return status;
+
+  pos = 0;
+  long long end = (available >= 1024) ? 1024 : available;
+
+  for (;;) {
+    unsigned char b = 0;
+
+    while (pos < end) {
+      status = pReader->Read(pos, 1, &b);
+
+      if (status < 0)  // error
+        return status;
+
+      if (b == 0x1A)
+        break;
+
+      ++pos;
+    }
+
+    if (b != 0x1A) {
+      if (pos >= 1024)
+        return E_FILE_FORMAT_INVALID;  // don't bother looking anymore
+
+      if ((total >= 0) && ((total - available) < 5))
+        return E_FILE_FORMAT_INVALID;
+
+      return available + 5;  // 5 = 4-byte ID + 1st byte of size
+    }
+
+    if ((total >= 0) && ((total - pos) < 5))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((available - pos) < 5)
+      return pos + 5;  // try again later
+
+    long len;
+
+    const long long result = ReadUInt(pReader, pos, len);
+
+    if (result < 0)  // error
+      return result;
+
+    if (result == 0x0A45DFA3) {  // EBML Header ID
+      pos += len;  // consume ID
+      break;
+    }
+
+    ++pos;  // throw away just the 0x1A byte, and try again
+  }
+
+  // pos designates start of size field
+
+  // get length of size field
+
+  long len;
+  long long result = GetUIntLength(pReader, pos, len);
+
+  if (result < 0)  // error
+    return result;
+
+  if (result > 0)  // need more data
+    return result;
+
+  if (len < 1 || len > 8)
+    return E_FILE_FORMAT_INVALID;
+
+  if ((total >= 0) && ((total - pos) < len))
+    return E_FILE_FORMAT_INVALID;
+
+  if ((available - pos) < len)
+    return pos + len;  // try again later
+
+  // get the EBML header size
+
+  result = ReadUInt(pReader, pos, len);
+
+  if (result < 0)  // error
+    return result;
+
+  pos += len;  // consume size field
+
+  // pos now designates start of payload
+
+  if ((total >= 0) && ((total - pos) < result))
+    return E_FILE_FORMAT_INVALID;
+
+  if ((available - pos) < result)
+    return pos + result;
+
+  end = pos + result;
+
+  Init();
+
+  while (pos < end) {
+    long long id, size;
+
+    status = ParseElementHeader(pReader, pos, end, id, size);
+
+    if (status < 0)  // error
+      return status;
+
+    if (size == 0)  // weird
+      return E_FILE_FORMAT_INVALID;
+
+    if (id == 0x0286) {  // version
+      m_version = UnserializeUInt(pReader, pos, size);
+
+      if (m_version <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x02F7) {  // read version
+      m_readVersion = UnserializeUInt(pReader, pos, size);
+
+      if (m_readVersion <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x02F2) {  // max id length
+      m_maxIdLength = UnserializeUInt(pReader, pos, size);
+
+      if (m_maxIdLength <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x02F3) {  // max size length
+      m_maxSizeLength = UnserializeUInt(pReader, pos, size);
+
+      if (m_maxSizeLength <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x0282) {  // doctype
+      if (m_docType)
+        return E_FILE_FORMAT_INVALID;
+
+      status = UnserializeString(pReader, pos, size, m_docType);
+
+      if (status)  // error
+        return status;
+    } else if (id == 0x0287) {  // doctype version
+      m_docTypeVersion = UnserializeUInt(pReader, pos, size);
+
+      if (m_docTypeVersion <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x0285) {  // doctype read version
+      m_docTypeReadVersion = UnserializeUInt(pReader, pos, size);
+
+      if (m_docTypeReadVersion <= 0)
+        return E_FILE_FORMAT_INVALID;
+    }
+
+    pos += size;
+  }
+
+  if (pos != end)
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;
+}
+
+Segment::Segment(IMkvReader* pReader, long long elem_start,
+                 // long long elem_size,
+                 long long start, long long size)
+    : m_pReader(pReader),
+      m_element_start(elem_start),
+      // m_element_size(elem_size),
+      m_start(start),
+      m_size(size),
+      m_pos(start),
+      m_pUnknownSize(0),
+      m_pSeekHead(NULL),
+      m_pInfo(NULL),
+      m_pTracks(NULL),
+      m_pCues(NULL),
+      m_pChapters(NULL),
+      m_pTags(NULL),
+      m_clusters(NULL),
+      m_clusterCount(0),
+      m_clusterPreloadCount(0),
+      m_clusterSize(0) {}
+
+Segment::~Segment() {
+  const long count = m_clusterCount + m_clusterPreloadCount;
+
+  Cluster** i = m_clusters;
+  Cluster** j = m_clusters + count;
+
+  while (i != j) {
+    Cluster* const p = *i++;
+    delete p;
+  }
+
+  delete[] m_clusters;
+
+  delete m_pTracks;
+  delete m_pInfo;
+  delete m_pCues;
+  delete m_pChapters;
+  delete m_pTags;
+  delete m_pSeekHead;
+}
+
+long long Segment::CreateInstance(IMkvReader* pReader, long long pos,
+                                  Segment*& pSegment) {
+  if (pReader == NULL || pos < 0)
+    return E_PARSE_FAILED;
+
+  pSegment = NULL;
+
+  long long total, available;
+
+  const long status = pReader->Length(&total, &available);
+
+  if (status < 0)  // error
+    return status;
+
+  if (available < 0)
+    return -1;
+
+  if ((total >= 0) && (available > total))
+    return -1;
+
+  // I would assume that in practice this loop would execute
+  // exactly once, but we allow for other elements (e.g. Void)
+  // to immediately follow the EBML header.  This is fine for
+  // the source filter case (since the entire file is available),
+  // but in the splitter case over a network we should probably
+  // just give up early.  We could for example decide only to
+  // execute this loop a maximum of, say, 10 times.
+  // TODO:
+  // There is an implied "give up early" by only parsing up
+  // to the available limit.  We do do that, but only if the
+  // total file size is unknown.  We could decide to always
+  // use what's available as our limit (irrespective of whether
+  // we happen to know the total file length).  This would have
+  // as its sense "parse this much of the file before giving up",
+  // which a slightly different sense from "try to parse up to
+  // 10 EMBL elements before giving up".
+
+  for (;;) {
+    if ((total >= 0) && (pos >= total))
+      return E_FILE_FORMAT_INVALID;
+
+    // Read ID
+    long len;
+    long long result = GetUIntLength(pReader, pos, len);
+
+    if (result)  // error, or too few available bytes
+      return result;
+
+    if ((total >= 0) && ((pos + len) > total))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > available)
+      return pos + len;
+
+    const long long idpos = pos;
+    const long long id = ReadID(pReader, pos, len);
+
+    if (id < 0)
+      return E_FILE_FORMAT_INVALID;
+
+    pos += len;  // consume ID
+
+    // Read Size
+
+    result = GetUIntLength(pReader, pos, len);
+
+    if (result)  // error, or too few available bytes
+      return result;
+
+    if ((total >= 0) && ((pos + len) > total))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > available)
+      return pos + len;
+
+    long long size = ReadUInt(pReader, pos, len);
+
+    if (size < 0)  // error
+      return size;
+
+    pos += len;  // consume length of size of element
+
+    // Pos now points to start of payload
+
+    // Handle "unknown size" for live streaming of webm files.
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if (id == 0x08538067) {  // Segment ID
+      if (size == unknown_size)
+        size = -1;
+
+      else if (total < 0)
+        size = -1;
+
+      else if ((pos + size) > total)
+        size = -1;
+
+      pSegment = new (std::nothrow) Segment(pReader, idpos,
+                                            // elem_size
+                                            pos, size);
+
+      if (pSegment == 0)
+        return -1;  // generic error
+
+      return 0;  // success
+    }
+
+    if (size == unknown_size)
+      return E_FILE_FORMAT_INVALID;
+
+    if ((total >= 0) && ((pos + size) > total))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + size) > available)
+      return pos + size;
+
+    pos += size;  // consume payload
+  }
+}
+
+long long Segment::ParseHeaders() {
+  // Outermost (level 0) segment object has been constructed,
+  // and pos designates start of payload.  We need to find the
+  // inner (level 1) elements.
+  long long total, available;
+
+  const int status = m_pReader->Length(&total, &available);
+
+  if (status < 0)  // error
+    return status;
+
+  if (total > 0 && available > total)
+    return E_FILE_FORMAT_INVALID;
+
+  const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+  if ((segment_stop >= 0 && total >= 0 && segment_stop > total) ||
+      (segment_stop >= 0 && m_pos > segment_stop)) {
+    return E_FILE_FORMAT_INVALID;
+  }
+
+  for (;;) {
+    if ((total >= 0) && (m_pos >= total))
+      break;
+
+    if ((segment_stop >= 0) && (m_pos >= segment_stop))
+      break;
+
+    long long pos = m_pos;
+    const long long element_start = pos;
+
+    // Avoid rolling over pos when very close to LONG_LONG_MAX.
+    unsigned long long rollover_check = pos + 1ULL;
+    if (rollover_check > LONG_LONG_MAX)
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + 1) > available)
+      return (pos + 1);
+
+    long len;
+    long long result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return result;
+
+    if (result > 0) {
+      // MkvReader doesn't have enough data to satisfy this read attempt.
+      return (pos + 1);
+    }
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > available)
+      return pos + len;
+
+    const long long idpos = pos;
+    const long long id = ReadID(m_pReader, idpos, len);
+
+    if (id < 0)
+      return E_FILE_FORMAT_INVALID;
+
+    if (id == 0x0F43B675)  // Cluster ID
+      break;
+
+    pos += len;  // consume ID
+
+    if ((pos + 1) > available)
+      return (pos + 1);
+
+    // Read Size
+    result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return result;
+
+    if (result > 0) {
+      // MkvReader doesn't have enough data to satisfy this read attempt.
+      return (pos + 1);
+    }
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > available)
+      return pos + len;
+
+    const long long size = ReadUInt(m_pReader, pos, len);
+
+    if (size < 0 || len < 1 || len > 8) {
+      // TODO(tomfinegan): ReadUInt should return an error when len is < 1 or
+      // len > 8 is true instead of checking this _everywhere_.
+      return size;
+    }
+
+    pos += len;  // consume length of size of element
+
+    // Avoid rolling over pos when very close to LONG_LONG_MAX.
+    rollover_check = static_cast<unsigned long long>(pos) + size;
+    if (rollover_check > LONG_LONG_MAX)
+      return E_FILE_FORMAT_INVALID;
+
+    const long long element_size = size + pos - element_start;
+
+    // Pos now points to start of payload
+
+    if ((segment_stop >= 0) && ((pos + size) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    // We read EBML elements either in total or nothing at all.
+
+    if ((pos + size) > available)
+      return pos + size;
+
+    if (id == 0x0549A966) {  // Segment Info ID
+      if (m_pInfo)
+        return E_FILE_FORMAT_INVALID;
+
+      m_pInfo = new (std::nothrow)
+          SegmentInfo(this, pos, size, element_start, element_size);
+
+      if (m_pInfo == NULL)
+        return -1;
+
+      const long status = m_pInfo->Parse();
+
+      if (status)
+        return status;
+    } else if (id == 0x0654AE6B) {  // Tracks ID
+      if (m_pTracks)
+        return E_FILE_FORMAT_INVALID;
+
+      m_pTracks = new (std::nothrow)
+          Tracks(this, pos, size, element_start, element_size);
+
+      if (m_pTracks == NULL)
+        return -1;
+
+      const long status = m_pTracks->Parse();
+
+      if (status)
+        return status;
+    } else if (id == 0x0C53BB6B) {  // Cues ID
+      if (m_pCues == NULL) {
+        m_pCues = new (std::nothrow)
+            Cues(this, pos, size, element_start, element_size);
+
+        if (m_pCues == NULL)
+          return -1;
+      }
+    } else if (id == 0x014D9B74) {  // SeekHead ID
+      if (m_pSeekHead == NULL) {
+        m_pSeekHead = new (std::nothrow)
+            SeekHead(this, pos, size, element_start, element_size);
+
+        if (m_pSeekHead == NULL)
+          return -1;
+
+        const long status = m_pSeekHead->Parse();
+
+        if (status)
+          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;
+      }
+    } else if (id == 0x0254C367) {  // Tags ID
+      if (m_pTags == NULL) {
+        m_pTags = new (std::nothrow)
+            Tags(this, pos, size, element_start, element_size);
+
+        if (m_pTags == NULL)
+          return -1;
+
+        const long status = m_pTags->Parse();
+
+        if (status)
+          return status;
+      }
+    }
+
+    m_pos = pos + size;  // consume payload
+  }
+
+  if (segment_stop >= 0 && m_pos > segment_stop)
+    return E_FILE_FORMAT_INVALID;
+
+  if (m_pInfo == NULL)  // TODO: liberalize this behavior
+    return E_FILE_FORMAT_INVALID;
+
+  if (m_pTracks == NULL)
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;  // success
+}
+
+long Segment::LoadCluster(long long& pos, long& len) {
+  for (;;) {
+    const long result = DoLoadCluster(pos, len);
+
+    if (result <= 1)
+      return result;
+  }
+}
+
+long Segment::DoLoadCluster(long long& pos, long& len) {
+  if (m_pos < 0)
+    return DoLoadClusterUnknownSize(pos, len);
+
+  long long total, avail;
+
+  long status = m_pReader->Length(&total, &avail);
+
+  if (status < 0)  // error
+    return status;
+
+  if (total >= 0 && avail > total)
+    return E_FILE_FORMAT_INVALID;
+
+  const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+  long long cluster_off = -1;  // offset relative to start of segment
+  long long cluster_size = -1;  // size of cluster payload
+
+  for (;;) {
+    if ((total >= 0) && (m_pos >= total))
+      return 1;  // no more clusters
+
+    if ((segment_stop >= 0) && (m_pos >= segment_stop))
+      return 1;  // no more clusters
+
+    pos = m_pos;
+
+    // Read ID
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    long long result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long idpos = pos;
+    const long long id = ReadID(m_pReader, idpos, len);
+
+    if (id < 0)
+      return E_FILE_FORMAT_INVALID;
+
+    pos += len;  // consume ID
+
+    // Read Size
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long size = ReadUInt(m_pReader, pos, len);
+
+    if (size < 0)  // error
+      return static_cast<long>(size);
+
+    pos += len;  // consume length of size of element
+
+    // pos now points to start of payload
+
+    if (size == 0) {  // weird
+      m_pos = pos;
+      continue;
+    }
+
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if ((segment_stop >= 0) && (size != unknown_size) &&
+        ((pos + size) > segment_stop)) {
+      return E_FILE_FORMAT_INVALID;
+    }
+
+    if (id == 0x0C53BB6B) {  // Cues ID
+      if (size == unknown_size)
+        return E_FILE_FORMAT_INVALID;  // TODO: liberalize
+
+      if (m_pCues == NULL) {
+        const long long element_size = (pos - idpos) + size;
+
+        m_pCues = new (std::nothrow) Cues(this, pos, size, idpos, element_size);
+        if (m_pCues == NULL)
+          return -1;
+      }
+
+      m_pos = pos + size;  // consume payload
+      continue;
+    }
+
+    if (id != 0x0F43B675) {  // Cluster ID
+      if (size == unknown_size)
+        return E_FILE_FORMAT_INVALID;  // TODO: liberalize
+
+      m_pos = pos + size;  // consume payload
+      continue;
+    }
+
+    // We have a cluster.
+
+    cluster_off = idpos - m_start;  // relative pos
+
+    if (size != unknown_size)
+      cluster_size = size;
+
+    break;
+  }
+
+  if (cluster_off < 0) {
+    // No cluster, die.
+    return E_FILE_FORMAT_INVALID;
+  }
+
+  long long pos_;
+  long len_;
+
+  status = Cluster::HasBlockEntries(this, cluster_off, pos_, len_);
+
+  if (status < 0) {  // error, or underflow
+    pos = pos_;
+    len = len_;
+
+    return status;
+  }
+
+  // status == 0 means "no block entries found"
+  // status > 0 means "found at least one block entry"
+
+  // TODO:
+  // The issue here is that the segment increments its own
+  // pos ptr past the most recent cluster parsed, and then
+  // starts from there to parse the next cluster.  If we
+  // don't know the size of the current cluster, then we
+  // must either parse its payload (as we do below), looking
+  // for the cluster (or cues) ID to terminate the parse.
+  // This isn't really what we want: rather, we really need
+  // a way to create the curr cluster object immediately.
+  // The pity is that cluster::parse can determine its own
+  // boundary, and we largely duplicate that same logic here.
+  //
+  // Maybe we need to get rid of our look-ahead preloading
+  // in source::parse???
+  //
+  // As we're parsing the blocks in the curr cluster
+  //(in cluster::parse), we should have some way to signal
+  // to the segment that we have determined the boundary,
+  // so it can adjust its own segment::m_pos member.
+  //
+  // The problem is that we're asserting in asyncreadinit,
+  // because we adjust the pos down to the curr seek pos,
+  // and the resulting adjusted len is > 2GB.  I'm suspicious
+  // that this is even correct, but even if it is, we can't
+  // be loading that much data in the cache anyway.
+
+  const long idx = m_clusterCount;
+
+  if (m_clusterPreloadCount > 0) {
+    if (idx >= m_clusterSize)
+      return E_FILE_FORMAT_INVALID;
+
+    Cluster* const pCluster = m_clusters[idx];
+    if (pCluster == NULL || pCluster->m_index >= 0)
+      return E_FILE_FORMAT_INVALID;
+
+    const long long off = pCluster->GetPosition();
+    if (off < 0)
+      return E_FILE_FORMAT_INVALID;
+
+    if (off == cluster_off) {  // preloaded already
+      if (status == 0)  // no entries found
+        return E_FILE_FORMAT_INVALID;
+
+      if (cluster_size >= 0)
+        pos += cluster_size;
+      else {
+        const long long element_size = pCluster->GetElementSize();
+
+        if (element_size <= 0)
+          return E_FILE_FORMAT_INVALID;  // TODO: handle this case
+
+        pos = pCluster->m_element_start + element_size;
+      }
+
+      pCluster->m_index = idx;  // move from preloaded to loaded
+      ++m_clusterCount;
+      --m_clusterPreloadCount;
+
+      m_pos = pos;  // consume payload
+      if (segment_stop >= 0 && m_pos > segment_stop)
+        return E_FILE_FORMAT_INVALID;
+
+      return 0;  // success
+    }
+  }
+
+  if (status == 0) {  // no entries found
+    if (cluster_size >= 0)
+      pos += cluster_size;
+
+    if ((total >= 0) && (pos >= total)) {
+      m_pos = total;
+      return 1;  // no more clusters
+    }
+
+    if ((segment_stop >= 0) && (pos >= segment_stop)) {
+      m_pos = segment_stop;
+      return 1;  // no more clusters
+    }
+
+    m_pos = pos;
+    return 2;  // try again
+  }
+
+  // status > 0 means we have an entry
+
+  Cluster* const pCluster = Cluster::Create(this, idx, cluster_off);
+  if (pCluster == NULL)
+    return -1;
+
+  if (!AppendCluster(pCluster)) {
+    delete pCluster;
+    return -1;
+  }
+
+  if (cluster_size >= 0) {
+    pos += cluster_size;
+
+    m_pos = pos;
+
+    if (segment_stop > 0 && m_pos > segment_stop)
+      return E_FILE_FORMAT_INVALID;
+
+    return 0;
+  }
+
+  m_pUnknownSize = pCluster;
+  m_pos = -pos;
+
+  return 0;  // partial success, since we have a new cluster
+
+  // status == 0 means "no block entries found"
+  // pos designates start of payload
+  // m_pos has NOT been adjusted yet (in case we need to come back here)
+}
+
+long Segment::DoLoadClusterUnknownSize(long long& pos, long& len) {
+  if (m_pos >= 0 || m_pUnknownSize == NULL)
+    return E_PARSE_FAILED;
+
+  const long status = m_pUnknownSize->Parse(pos, len);
+
+  if (status < 0)  // error or underflow
+    return status;
+
+  if (status == 0)  // parsed a block
+    return 2;  // continue parsing
+
+  const long long start = m_pUnknownSize->m_element_start;
+  const long long size = m_pUnknownSize->GetElementSize();
+
+  if (size < 0)
+    return E_FILE_FORMAT_INVALID;
+
+  pos = start + size;
+  m_pos = pos;
+
+  m_pUnknownSize = 0;
+
+  return 2;  // continue parsing
+}
+
+bool Segment::AppendCluster(Cluster* pCluster) {
+  if (pCluster == NULL || pCluster->m_index < 0)
+    return false;
+
+  const long count = m_clusterCount + m_clusterPreloadCount;
+
+  long& size = m_clusterSize;
+  const long idx = pCluster->m_index;
+
+  if (size < count || idx != m_clusterCount)
+    return false;
+
+  if (count >= size) {
+    const long n = (size <= 0) ? 2048 : 2 * size;
+
+    Cluster** const qq = new (std::nothrow) Cluster*[n];
+    if (qq == NULL)
+      return false;
+
+    Cluster** q = qq;
+    Cluster** p = m_clusters;
+    Cluster** const pp = p + count;
+
+    while (p != pp)
+      *q++ = *p++;
+
+    delete[] m_clusters;
+
+    m_clusters = qq;
+    size = n;
+  }
+
+  if (m_clusterPreloadCount > 0) {
+    Cluster** const p = m_clusters + m_clusterCount;
+    if (*p == NULL || (*p)->m_index >= 0)
+      return false;
+
+    Cluster** q = p + m_clusterPreloadCount;
+    if (q >= (m_clusters + size))
+      return false;
+
+    for (;;) {
+      Cluster** const qq = q - 1;
+      if ((*qq)->m_index >= 0)
+        return false;
+
+      *q = *qq;
+      q = qq;
+
+      if (q == p)
+        break;
+    }
+  }
+
+  m_clusters[idx] = pCluster;
+  ++m_clusterCount;
+  return true;
+}
+
+bool Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) {
+  assert(pCluster);
+  assert(pCluster->m_index < 0);
+  assert(idx >= m_clusterCount);
+
+  const long count = m_clusterCount + m_clusterPreloadCount;
+
+  long& size = m_clusterSize;
+  assert(size >= count);
+
+  if (count >= size) {
+    const long n = (size <= 0) ? 2048 : 2 * size;
+
+    Cluster** const qq = new (std::nothrow) Cluster*[n];
+    if (qq == NULL)
+      return false;
+    Cluster** q = qq;
+
+    Cluster** p = m_clusters;
+    Cluster** const pp = p + count;
+
+    while (p != pp)
+      *q++ = *p++;
+
+    delete[] m_clusters;
+
+    m_clusters = qq;
+    size = n;
+  }
+
+  assert(m_clusters);
+
+  Cluster** const p = m_clusters + idx;
+
+  Cluster** q = m_clusters + count;
+  assert(q >= p);
+  assert(q < (m_clusters + size));
+
+  while (q > p) {
+    Cluster** const qq = q - 1;
+    assert((*qq)->m_index < 0);
+
+    *q = *qq;
+    q = qq;
+  }
+
+  m_clusters[idx] = pCluster;
+  ++m_clusterPreloadCount;
+  return true;
+}
+
+long Segment::Load() {
+  assert(m_clusters == NULL);
+  assert(m_clusterSize == 0);
+  assert(m_clusterCount == 0);
+  // assert(m_size >= 0);
+
+  // Outermost (level 0) segment object has been constructed,
+  // and pos designates start of payload.  We need to find the
+  // inner (level 1) elements.
+
+  const long long header_status = ParseHeaders();
+
+  if (header_status < 0)  // error
+    return static_cast<long>(header_status);
+
+  if (header_status > 0)  // underflow
+    return E_BUFFER_NOT_FULL;
+
+  if (m_pInfo == NULL || m_pTracks == NULL)
+    return E_FILE_FORMAT_INVALID;
+
+  for (;;) {
+    const int status = LoadCluster();
+
+    if (status < 0)  // error
+      return status;
+
+    if (status >= 1)  // no more clusters
+      return 0;
+  }
+}
+
+SeekHead::SeekHead(Segment* pSegment, long long start, long long size_,
+                   long long element_start, long long element_size)
+    : m_pSegment(pSegment),
+      m_start(start),
+      m_size(size_),
+      m_element_start(element_start),
+      m_element_size(element_size),
+      m_entries(0),
+      m_entry_count(0),
+      m_void_elements(0),
+      m_void_element_count(0) {}
+
+SeekHead::~SeekHead() {
+  delete[] m_entries;
+  delete[] m_void_elements;
+}
+
+long SeekHead::Parse() {
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  long long pos = m_start;
+  const long long stop = m_start + m_size;
+
+  // first count the seek head entries
+
+  int entry_count = 0;
+  int void_element_count = 0;
+
+  while (pos < stop) {
+    long long id, size;
+
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+    if (status < 0)  // error
+      return status;
+
+    if (id == 0x0DBB)  // SeekEntry ID
+      ++entry_count;
+    else if (id == 0x6C)  // Void ID
+      ++void_element_count;
+
+    pos += size;  // consume payload
+
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+
+  m_entries = new (std::nothrow) Entry[entry_count];
+
+  if (m_entries == NULL)
+    return -1;
+
+  m_void_elements = new (std::nothrow) VoidElement[void_element_count];
+
+  if (m_void_elements == NULL)
+    return -1;
+
+  // now parse the entries and void elements
+
+  Entry* pEntry = m_entries;
+  VoidElement* pVoidElement = m_void_elements;
+
+  pos = m_start;
+
+  while (pos < stop) {
+    const long long idpos = pos;
+
+    long long id, size;
+
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+    if (status < 0)  // error
+      return status;
+
+    if (id == 0x0DBB) {  // SeekEntry ID
+      if (ParseEntry(pReader, pos, size, pEntry)) {
+        Entry& e = *pEntry++;
+
+        e.element_start = idpos;
+        e.element_size = (pos + size) - idpos;
+      }
+    } else if (id == 0x6C) {  // Void ID
+      VoidElement& e = *pVoidElement++;
+
+      e.element_start = idpos;
+      e.element_size = (pos + size) - idpos;
+    }
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+
+  ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);
+  assert(count_ >= 0);
+  assert(count_ <= entry_count);
+
+  m_entry_count = static_cast<int>(count_);
+
+  count_ = ptrdiff_t(pVoidElement - m_void_elements);
+  assert(count_ >= 0);
+  assert(count_ <= void_element_count);
+
+  m_void_element_count = static_cast<int>(count_);
+
+  return 0;
+}
+
+int SeekHead::GetCount() const { return m_entry_count; }
+
+const SeekHead::Entry* SeekHead::GetEntry(int idx) const {
+  if (idx < 0)
+    return 0;
+
+  if (idx >= m_entry_count)
+    return 0;
+
+  return m_entries + idx;
+}
+
+int SeekHead::GetVoidElementCount() const { return m_void_element_count; }
+
+const SeekHead::VoidElement* SeekHead::GetVoidElement(int idx) const {
+  if (idx < 0)
+    return 0;
+
+  if (idx >= m_void_element_count)
+    return 0;
+
+  return m_void_elements + idx;
+}
+
+long Segment::ParseCues(long long off, long long& pos, long& len) {
+  if (m_pCues)
+    return 0;  // success
+
+  if (off < 0)
+    return -1;
+
+  long long total, avail;
+
+  const int status = m_pReader->Length(&total, &avail);
+
+  if (status < 0)  // error
+    return status;
+
+  assert((total < 0) || (avail <= total));
+
+  pos = m_start + off;
+
+  if ((total < 0) || (pos >= total))
+    return 1;  // don't bother parsing cues
+
+  const long long element_start = pos;
+  const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+  if ((pos + 1) > avail) {
+    len = 1;
+    return E_BUFFER_NOT_FULL;
+  }
+
+  long long result = GetUIntLength(m_pReader, pos, len);
+
+  if (result < 0)  // error
+    return static_cast<long>(result);
+
+  if (result > 0)  // underflow (weird)
+  {
+    len = 1;
+    return E_BUFFER_NOT_FULL;
+  }
+
+  if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+    return E_FILE_FORMAT_INVALID;
+
+  if ((pos + len) > avail)
+    return E_BUFFER_NOT_FULL;
+
+  const long long idpos = pos;
+
+  const long long id = ReadID(m_pReader, idpos, len);
+
+  if (id != 0x0C53BB6B)  // Cues ID
+    return E_FILE_FORMAT_INVALID;
+
+  pos += len;  // consume ID
+  assert((segment_stop < 0) || (pos <= segment_stop));
+
+  // Read Size
+
+  if ((pos + 1) > avail) {
+    len = 1;
+    return E_BUFFER_NOT_FULL;
+  }
+
+  result = GetUIntLength(m_pReader, pos, len);
+
+  if (result < 0)  // error
+    return static_cast<long>(result);
+
+  if (result > 0)  // underflow (weird)
+  {
+    len = 1;
+    return E_BUFFER_NOT_FULL;
+  }
+
+  if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+    return E_FILE_FORMAT_INVALID;
+
+  if ((pos + len) > avail)
+    return E_BUFFER_NOT_FULL;
+
+  const long long size = ReadUInt(m_pReader, pos, len);
+
+  if (size < 0)  // error
+    return static_cast<long>(size);
+
+  if (size == 0)  // weird, although technically not illegal
+    return 1;  // done
+
+  pos += len;  // consume length of size of element
+  assert((segment_stop < 0) || (pos <= segment_stop));
+
+  // Pos now points to start of payload
+
+  const long long element_stop = pos + size;
+
+  if ((segment_stop >= 0) && (element_stop > segment_stop))
+    return E_FILE_FORMAT_INVALID;
+
+  if ((total >= 0) && (element_stop > total))
+    return 1;  // don't bother parsing anymore
+
+  len = static_cast<long>(size);
+
+  if (element_stop > avail)
+    return E_BUFFER_NOT_FULL;
+
+  const long long element_size = element_stop - element_start;
+
+  m_pCues =
+      new (std::nothrow) Cues(this, pos, size, element_start, element_size);
+  if (m_pCues == NULL)
+    return -1;
+
+  return 0;  // success
+}
+
+bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
+                          Entry* pEntry) {
+  if (size_ <= 0)
+    return false;
+
+  long long pos = start;
+  const long long stop = start + size_;
+
+  long len;
+
+  // parse the container for the level-1 element ID
+
+  const long long seekIdId = ReadID(pReader, pos, len);
+  if (seekIdId < 0)
+    return false;
+
+  if (seekIdId != 0x13AB)  // SeekID ID
+    return false;
+
+  if ((pos + len) > stop)
+    return false;
+
+  pos += len;  // consume SeekID id
+
+  const long long seekIdSize = ReadUInt(pReader, pos, len);
+
+  if (seekIdSize <= 0)
+    return false;
+
+  if ((pos + len) > stop)
+    return false;
+
+  pos += len;  // consume size of field
+
+  if ((pos + seekIdSize) > stop)
+    return false;
+
+  // Note that the SeekId payload really is serialized
+  // as a "Matroska integer", not as a plain binary value.
+  // In fact, Matroska requires that ID values in the
+  // stream exactly match the binary representation as listed
+  // in the Matroska specification.
+  //
+  // This parser is more liberal, and permits IDs to have
+  // any width.  (This could make the representation in the stream
+  // different from what's in the spec, but it doesn't matter here,
+  // since we always normalize "Matroska integer" values.)
+
+  pEntry->id = ReadUInt(pReader, pos, len);  // payload
+
+  if (pEntry->id <= 0)
+    return false;
+
+  if (len != seekIdSize)
+    return false;
+
+  pos += seekIdSize;  // consume SeekID payload
+
+  const long long seekPosId = ReadUInt(pReader, pos, len);
+
+  if (seekPosId != 0x13AC)  // SeekPos ID
+    return false;
+
+  if ((pos + len) > stop)
+    return false;
+
+  pos += len;  // consume id
+
+  const long long seekPosSize = ReadUInt(pReader, pos, len);
+
+  if (seekPosSize <= 0)
+    return false;
+
+  if ((pos + len) > stop)
+    return false;
+
+  pos += len;  // consume size
+
+  if ((pos + seekPosSize) > stop)
+    return false;
+
+  pEntry->pos = UnserializeUInt(pReader, pos, seekPosSize);
+
+  if (pEntry->pos < 0)
+    return false;
+
+  pos += seekPosSize;  // consume payload
+
+  if (pos != stop)
+    return false;
+
+  return true;
+}
+
+Cues::Cues(Segment* pSegment, long long start_, long long size_,
+           long long element_start, long long element_size)
+    : m_pSegment(pSegment),
+      m_start(start_),
+      m_size(size_),
+      m_element_start(element_start),
+      m_element_size(element_size),
+      m_cue_points(NULL),
+      m_count(0),
+      m_preload_count(0),
+      m_pos(start_) {}
+
+Cues::~Cues() {
+  const long n = m_count + m_preload_count;
+
+  CuePoint** p = m_cue_points;
+  CuePoint** const q = p + n;
+
+  while (p != q) {
+    CuePoint* const pCP = *p++;
+    assert(pCP);
+
+    delete pCP;
+  }
+
+  delete[] m_cue_points;
+}
+
+long Cues::GetCount() const {
+  if (m_cue_points == NULL)
+    return -1;
+
+  return m_count;  // TODO: really ignore preload count?
+}
+
+bool Cues::DoneParsing() const {
+  const long long stop = m_start + m_size;
+  return (m_pos >= stop);
+}
+
+bool Cues::Init() const {
+  if (m_cue_points)
+    return true;
+
+  if (m_count != 0 || m_preload_count != 0)
+    return false;
+
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  const long long stop = m_start + m_size;
+  long long pos = m_start;
+
+  long cue_points_size = 0;
+
+  while (pos < stop) {
+    const long long idpos = pos;
+
+    long len;
+
+    const long long id = ReadID(pReader, pos, len);
+    if (id < 0 || (pos + len) > stop) {
+      return false;
+    }
+
+    pos += len;  // consume ID
+
+    const long long size = ReadUInt(pReader, pos, len);
+    if (size < 0 || (pos + len > stop)) {
+      return false;
+    }
+
+    pos += len;  // consume Size field
+    if (pos + size > stop) {
+      return false;
+    }
+
+    if (id == 0x3B) {  // CuePoint ID
+      if (!PreloadCuePoint(cue_points_size, idpos))
+        return false;
+    }
+
+    pos += size;  // skip payload
+  }
+  return true;
+}
+
+bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
+  if (m_count != 0)
+    return false;
+
+  if (m_preload_count >= cue_points_size) {
+    const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size;
+
+    CuePoint** const qq = new (std::nothrow) CuePoint*[n];
+    if (qq == NULL)
+      return false;
+
+    CuePoint** q = qq;  // beginning of target
+
+    CuePoint** p = m_cue_points;  // beginning of source
+    CuePoint** const pp = p + m_preload_count;  // end of source
+
+    while (p != pp)
+      *q++ = *p++;
+
+    delete[] m_cue_points;
+
+    m_cue_points = qq;
+    cue_points_size = n;
+  }
+
+  CuePoint* const pCP = new (std::nothrow) CuePoint(m_preload_count, pos);
+  if (pCP == NULL)
+    return false;
+
+  m_cue_points[m_preload_count++] = pCP;
+  return true;
+}
+
+bool Cues::LoadCuePoint() const {
+  const long long stop = m_start + m_size;
+
+  if (m_pos >= stop)
+    return false;  // nothing else to do
+
+  if (!Init()) {
+    m_pos = stop;
+    return false;
+  }
+
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  while (m_pos < stop) {
+    const long long idpos = m_pos;
+
+    long len;
+
+    const long long id = ReadID(pReader, m_pos, len);
+    if (id < 0 || (m_pos + len) > stop)
+      return false;
+
+    m_pos += len;  // consume ID
+
+    const long long size = ReadUInt(pReader, m_pos, len);
+    if (size < 0 || (m_pos + len) > stop)
+      return false;
+
+    m_pos += len;  // consume Size field
+    if ((m_pos + size) > stop)
+      return false;
+
+    if (id != 0x3B) {  // CuePoint ID
+      m_pos += size;  // consume payload
+      if (m_pos > stop)
+        return false;
+
+      continue;
+    }
+
+    if (m_preload_count < 1)
+      return false;
+
+    CuePoint* const pCP = m_cue_points[m_count];
+    if (!pCP || (pCP->GetTimeCode() < 0 && (-pCP->GetTimeCode() != idpos)))
+      return false;
+
+    if (!pCP->Load(pReader)) {
+      m_pos = stop;
+      return false;
+    }
+    ++m_count;
+    --m_preload_count;
+
+    m_pos += size;  // consume payload
+    if (m_pos > stop)
+      return false;
+
+    return true;  // yes, we loaded a cue point
+  }
+
+  return false;  // no, we did not load a cue point
+}
+
+bool Cues::Find(long long time_ns, const Track* pTrack, const CuePoint*& pCP,
+                const CuePoint::TrackPosition*& pTP) const {
+  if (time_ns < 0 || pTrack == NULL || m_cue_points == NULL || m_count == 0)
+    return false;
+
+  CuePoint** const ii = m_cue_points;
+  CuePoint** i = ii;
+
+  CuePoint** const jj = ii + m_count;
+  CuePoint** j = jj;
+
+  pCP = *i;
+  if (pCP == NULL)
+    return false;
+
+  if (time_ns <= pCP->GetTime(m_pSegment)) {
+    pTP = pCP->Find(pTrack);
+    return (pTP != NULL);
+  }
+
+  while (i < j) {
+    // INVARIANT:
+    //[ii, i) <= time_ns
+    //[i, j)  ?
+    //[j, jj) > time_ns
+
+    CuePoint** const k = i + (j - i) / 2;
+    if (k >= jj)
+      return false;
+
+    CuePoint* const pCP = *k;
+    if (pCP == NULL)
+      return false;
+
+    const long long t = pCP->GetTime(m_pSegment);
+
+    if (t <= time_ns)
+      i = k + 1;
+    else
+      j = k;
+
+    if (i > j)
+      return false;
+  }
+
+  if (i != j || i > jj || i <= ii)
+    return false;
+
+  pCP = *--i;
+
+  if (pCP == NULL || pCP->GetTime(m_pSegment) > time_ns)
+    return false;
+
+  // TODO: here and elsewhere, it's probably not correct to search
+  // for the cue point with this time, and then search for a matching
+  // track.  In principle, the matching track could be on some earlier
+  // cue point, and with our current algorithm, we'd miss it.  To make
+  // this bullet-proof, we'd need to create a secondary structure,
+  // with a list of cue points that apply to a track, and then search
+  // that track-based structure for a matching cue point.
+
+  pTP = pCP->Find(pTrack);
+  return (pTP != NULL);
+}
+
+const CuePoint* Cues::GetFirst() const {
+  if (m_cue_points == NULL || m_count == 0)
+    return NULL;
+
+  CuePoint* const* const pp = m_cue_points;
+  if (pp == NULL)
+    return NULL;
+
+  CuePoint* const pCP = pp[0];
+  if (pCP == NULL || pCP->GetTimeCode() < 0)
+    return NULL;
+
+  return pCP;
+}
+
+const CuePoint* Cues::GetLast() const {
+  if (m_cue_points == NULL || m_count <= 0)
+    return NULL;
+
+  const long index = m_count - 1;
+
+  CuePoint* const* const pp = m_cue_points;
+  if (pp == NULL)
+    return NULL;
+
+  CuePoint* const pCP = pp[index];
+  if (pCP == NULL || pCP->GetTimeCode() < 0)
+    return NULL;
+
+  return pCP;
+}
+
+const CuePoint* Cues::GetNext(const CuePoint* pCurr) const {
+  if (pCurr == NULL || pCurr->GetTimeCode() < 0 ||
+      m_cue_points == NULL || m_count < 1) {
+    return NULL;
+  }
+
+  long index = pCurr->m_index;
+  if (index >= m_count)
+    return NULL;
+
+  CuePoint* const* const pp = m_cue_points;
+  if (pp == NULL || pp[index] != pCurr)
+    return NULL;
+
+  ++index;
+
+  if (index >= m_count)
+    return NULL;
+
+  CuePoint* const pNext = pp[index];
+
+  if (pNext == NULL || pNext->GetTimeCode() < 0)
+    return NULL;
+
+  return pNext;
+}
+
+const BlockEntry* Cues::GetBlock(const CuePoint* pCP,
+                                 const CuePoint::TrackPosition* pTP) const {
+  if (pCP == NULL || pTP == NULL)
+    return NULL;
+
+  return m_pSegment->GetBlock(*pCP, *pTP);
+}
+
+const BlockEntry* Segment::GetBlock(const CuePoint& cp,
+                                    const CuePoint::TrackPosition& tp) {
+  Cluster** const ii = m_clusters;
+  Cluster** i = ii;
+
+  const long count = m_clusterCount + m_clusterPreloadCount;
+
+  Cluster** const jj = ii + count;
+  Cluster** j = jj;
+
+  while (i < j) {
+    // INVARIANT:
+    //[ii, i) < pTP->m_pos
+    //[i, j) ?
+    //[j, jj)  > pTP->m_pos
+
+    Cluster** const k = i + (j - i) / 2;
+    assert(k < jj);
+
+    Cluster* const pCluster = *k;
+    assert(pCluster);
+
+    // const long long pos_ = pCluster->m_pos;
+    // assert(pos_);
+    // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
+
+    const long long pos = pCluster->GetPosition();
+    assert(pos >= 0);
+
+    if (pos < tp.m_pos)
+      i = k + 1;
+    else if (pos > tp.m_pos)
+      j = k;
+    else
+      return pCluster->GetEntry(cp, tp);
+  }
+
+  assert(i == j);
+  // assert(Cluster::HasBlockEntries(this, tp.m_pos));
+
+  Cluster* const pCluster = Cluster::Create(this, -1, tp.m_pos);  //, -1);
+  if (pCluster == NULL)
+    return NULL;
+
+  const ptrdiff_t idx = i - m_clusters;
+
+  if (!PreloadCluster(pCluster, idx)) {
+    delete pCluster;
+    return NULL;
+  }
+  assert(m_clusters);
+  assert(m_clusterPreloadCount > 0);
+  assert(m_clusters[idx] == pCluster);
+
+  return pCluster->GetEntry(cp, tp);
+}
+
+const Cluster* Segment::FindOrPreloadCluster(long long requested_pos) {
+  if (requested_pos < 0)
+    return 0;
+
+  Cluster** const ii = m_clusters;
+  Cluster** i = ii;
+
+  const long count = m_clusterCount + m_clusterPreloadCount;
+
+  Cluster** const jj = ii + count;
+  Cluster** j = jj;
+
+  while (i < j) {
+    // INVARIANT:
+    //[ii, i) < pTP->m_pos
+    //[i, j) ?
+    //[j, jj)  > pTP->m_pos
+
+    Cluster** const k = i + (j - i) / 2;
+    assert(k < jj);
+
+    Cluster* const pCluster = *k;
+    assert(pCluster);
+
+    // const long long pos_ = pCluster->m_pos;
+    // assert(pos_);
+    // const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
+
+    const long long pos = pCluster->GetPosition();
+    assert(pos >= 0);
+
+    if (pos < requested_pos)
+      i = k + 1;
+    else if (pos > requested_pos)
+      j = k;
+    else
+      return pCluster;
+  }
+
+  assert(i == j);
+  // assert(Cluster::HasBlockEntries(this, tp.m_pos));
+
+  Cluster* const pCluster = Cluster::Create(this, -1, requested_pos);
+  if (pCluster == NULL)
+    return NULL;
+
+  const ptrdiff_t idx = i - m_clusters;
+
+  if (!PreloadCluster(pCluster, idx)) {
+    delete pCluster;
+    return NULL;
+  }
+  assert(m_clusters);
+  assert(m_clusterPreloadCount > 0);
+  assert(m_clusters[idx] == pCluster);
+
+  return pCluster;
+}
+
+CuePoint::CuePoint(long idx, long long pos)
+    : m_element_start(0),
+      m_element_size(0),
+      m_index(idx),
+      m_timecode(-1 * pos),
+      m_track_positions(NULL),
+      m_track_positions_count(0) {
+  assert(pos > 0);
+}
+
+CuePoint::~CuePoint() { delete[] m_track_positions; }
+
+bool CuePoint::Load(IMkvReader* pReader) {
+  // odbgstream os;
+  // os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
+
+  if (m_timecode >= 0)  // already loaded
+    return true;
+
+  assert(m_track_positions == NULL);
+  assert(m_track_positions_count == 0);
+
+  long long pos_ = -m_timecode;
+  const long long element_start = pos_;
+
+  long long stop;
+
+  {
+    long len;
+
+    const long long id = ReadID(pReader, pos_, len);
+    if (id != 0x3B)
+      return false;
+
+    pos_ += len;  // consume ID
+
+    const long long size = ReadUInt(pReader, pos_, len);
+    assert(size >= 0);
+
+    pos_ += len;  // consume Size field
+    // pos_ now points to start of payload
+
+    stop = pos_ + size;
+  }
+
+  const long long element_size = stop - element_start;
+
+  long long pos = pos_;
+
+  // First count number of track positions
+
+  while (pos < stop) {
+    long len;
+
+    const long long id = ReadID(pReader, pos, len);
+    if ((id < 0) || (pos + len > stop)) {
+      return false;
+    }
+
+    pos += len;  // consume ID
+
+    const long long size = ReadUInt(pReader, pos, len);
+    if ((size < 0) || (pos + len > stop)) {
+      return false;
+    }
+
+    pos += len;  // consume Size field
+    if ((pos + size) > stop) {
+      return false;
+    }
+
+    if (id == 0x33)  // CueTime ID
+      m_timecode = UnserializeUInt(pReader, pos, size);
+
+    else if (id == 0x37)  // CueTrackPosition(s) ID
+      ++m_track_positions_count;
+
+    pos += size;  // consume payload
+  }
+
+  if (m_timecode < 0 || m_track_positions_count <= 0) {
+    return false;
+  }
+
+  // os << "CuePoint::Load(cont'd): idpos=" << idpos
+  //   << " timecode=" << m_timecode
+  //   << endl;
+
+  m_track_positions = new (std::nothrow) TrackPosition[m_track_positions_count];
+  if (m_track_positions == NULL)
+    return false;
+
+  // Now parse track positions
+
+  TrackPosition* p = m_track_positions;
+  pos = pos_;
+
+  while (pos < stop) {
+    long len;
+
+    const long long id = ReadID(pReader, pos, len);
+    if (id < 0 || (pos + len) > stop)
+      return false;
+
+    pos += len;  // consume ID
+
+    const long long size = ReadUInt(pReader, pos, len);
+    assert(size >= 0);
+    assert((pos + len) <= stop);
+
+    pos += len;  // consume Size field
+    assert((pos + size) <= stop);
+
+    if (id == 0x37) {  // CueTrackPosition(s) ID
+      TrackPosition& tp = *p++;
+      if (!tp.Parse(pReader, pos, size)) {
+        return false;
+      }
+    }
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return false;
+  }
+
+  assert(size_t(p - m_track_positions) == m_track_positions_count);
+
+  m_element_start = element_start;
+  m_element_size = element_size;
+
+  return true;
+}
+
+bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
+                                    long long size_) {
+  const long long stop = start_ + size_;
+  long long pos = start_;
+
+  m_track = -1;
+  m_pos = -1;
+  m_block = 1;  // default
+
+  while (pos < stop) {
+    long len;
+
+    const long long id = ReadID(pReader, pos, len);
+    if ((id < 0) || ((pos + len) > stop)) {
+      return false;
+    }
+
+    pos += len;  // consume ID
+
+    const long long size = ReadUInt(pReader, pos, len);
+    if ((size < 0) || ((pos + len) > stop)) {
+      return false;
+    }
+
+    pos += len;  // consume Size field
+    if ((pos + size) > stop) {
+      return false;
+    }
+
+    if (id == 0x77)  // CueTrack ID
+      m_track = UnserializeUInt(pReader, pos, size);
+
+    else if (id == 0x71)  // CueClusterPos ID
+      m_pos = UnserializeUInt(pReader, pos, size);
+
+    else if (id == 0x1378)  // CueBlockNumber
+      m_block = UnserializeUInt(pReader, pos, size);
+
+    pos += size;  // consume payload
+  }
+
+  if ((m_pos < 0) || (m_track <= 0)) {
+    return false;
+  }
+
+  return true;
+}
+
+const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const {
+  assert(pTrack);
+
+  const long long n = pTrack->GetNumber();
+
+  const TrackPosition* i = m_track_positions;
+  const TrackPosition* const j = i + m_track_positions_count;
+
+  while (i != j) {
+    const TrackPosition& p = *i++;
+
+    if (p.m_track == n)
+      return &p;
+  }
+
+  return NULL;  // no matching track number found
+}
+
+long long CuePoint::GetTimeCode() const { return m_timecode; }
+
+long long CuePoint::GetTime(const Segment* pSegment) const {
+  assert(pSegment);
+  assert(m_timecode >= 0);
+
+  const SegmentInfo* const pInfo = pSegment->GetInfo();
+  assert(pInfo);
+
+  const long long scale = pInfo->GetTimeCodeScale();
+  assert(scale >= 1);
+
+  const long long time = scale * m_timecode;
+
+  return time;
+}
+
+bool Segment::DoneParsing() const {
+  if (m_size < 0) {
+    long long total, avail;
+
+    const int status = m_pReader->Length(&total, &avail);
+
+    if (status < 0)  // error
+      return true;  // must assume done
+
+    if (total < 0)
+      return false;  // assume live stream
+
+    return (m_pos >= total);
+  }
+
+  const long long stop = m_start + m_size;
+
+  return (m_pos >= stop);
+}
+
+const Cluster* Segment::GetFirst() const {
+  if ((m_clusters == NULL) || (m_clusterCount <= 0))
+    return &m_eos;
+
+  Cluster* const pCluster = m_clusters[0];
+  assert(pCluster);
+
+  return pCluster;
+}
+
+const Cluster* Segment::GetLast() const {
+  if ((m_clusters == NULL) || (m_clusterCount <= 0))
+    return &m_eos;
+
+  const long idx = m_clusterCount - 1;
+
+  Cluster* const pCluster = m_clusters[idx];
+  assert(pCluster);
+
+  return pCluster;
+}
+
+unsigned long Segment::GetCount() const { return m_clusterCount; }
+
+const Cluster* Segment::GetNext(const Cluster* pCurr) {
+  assert(pCurr);
+  assert(pCurr != &m_eos);
+  assert(m_clusters);
+
+  long idx = pCurr->m_index;
+
+  if (idx >= 0) {
+    assert(m_clusterCount > 0);
+    assert(idx < m_clusterCount);
+    assert(pCurr == m_clusters[idx]);
+
+    ++idx;
+
+    if (idx >= m_clusterCount)
+      return &m_eos;  // caller will LoadCluster as desired
+
+    Cluster* const pNext = m_clusters[idx];
+    assert(pNext);
+    assert(pNext->m_index >= 0);
+    assert(pNext->m_index == idx);
+
+    return pNext;
+  }
+
+  assert(m_clusterPreloadCount > 0);
+
+  long long pos = pCurr->m_element_start;
+
+  assert(m_size >= 0);  // TODO
+  const long long stop = m_start + m_size;  // end of segment
+
+  {
+    long len;
+
+    long long result = GetUIntLength(m_pReader, pos, len);
+    assert(result == 0);
+    assert((pos + len) <= stop);  // TODO
+    if (result != 0)
+      return NULL;
+
+    const long long id = ReadID(m_pReader, pos, len);
+    if (id != 0x0F43B675)  // Cluster ID
+      return NULL;
+
+    pos += len;  // consume ID
+
+    // Read Size
+    result = GetUIntLength(m_pReader, pos, len);
+    assert(result == 0);  // TODO
+    assert((pos + len) <= stop);  // TODO
+
+    const long long size = ReadUInt(m_pReader, pos, len);
+    assert(size > 0);  // TODO
+    // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
+
+    pos += len;  // consume length of size of element
+    assert((pos + size) <= stop);  // TODO
+
+    // Pos now points to start of payload
+
+    pos += size;  // consume payload
+  }
+
+  long long off_next = 0;
+
+  while (pos < stop) {
+    long len;
+
+    long long result = GetUIntLength(m_pReader, pos, len);
+    assert(result == 0);
+    assert((pos + len) <= stop);  // TODO
+    if (result != 0)
+      return NULL;
+
+    const long long idpos = pos;  // pos of next (potential) cluster
+
+    const long long id = ReadID(m_pReader, idpos, len);
+    if (id < 0)
+      return NULL;
+
+    pos += len;  // consume ID
+
+    // Read Size
+    result = GetUIntLength(m_pReader, pos, len);
+    assert(result == 0);  // TODO
+    assert((pos + len) <= stop);  // TODO
+
+    const long long size = ReadUInt(m_pReader, pos, len);
+    assert(size >= 0);  // TODO
+
+    pos += len;  // consume length of size of element
+    assert((pos + size) <= stop);  // TODO
+
+    // Pos now points to start of payload
+
+    if (size == 0)  // weird
+      continue;
+
+    if (id == 0x0F43B675) {  // Cluster ID
+      const long long off_next_ = idpos - m_start;
+
+      long long pos_;
+      long len_;
+
+      const long status = Cluster::HasBlockEntries(this, off_next_, pos_, len_);
+
+      assert(status >= 0);
+
+      if (status > 0) {
+        off_next = off_next_;
+        break;
+      }
+    }
+
+    pos += size;  // consume payload
+  }
+
+  if (off_next <= 0)
+    return 0;
+
+  Cluster** const ii = m_clusters + m_clusterCount;
+  Cluster** i = ii;
+
+  Cluster** const jj = ii + m_clusterPreloadCount;
+  Cluster** j = jj;
+
+  while (i < j) {
+    // INVARIANT:
+    //[0, i) < pos_next
+    //[i, j) ?
+    //[j, jj)  > pos_next
+
+    Cluster** const k = i + (j - i) / 2;
+    assert(k < jj);
+
+    Cluster* const pNext = *k;
+    assert(pNext);
+    assert(pNext->m_index < 0);
+
+    // const long long pos_ = pNext->m_pos;
+    // assert(pos_);
+    // pos = pos_ * ((pos_ < 0) ? -1 : 1);
+
+    pos = pNext->GetPosition();
+
+    if (pos < off_next)
+      i = k + 1;
+    else if (pos > off_next)
+      j = k;
+    else
+      return pNext;
+  }
+
+  assert(i == j);
+
+  Cluster* const pNext = Cluster::Create(this, -1, off_next);
+  if (pNext == NULL)
+    return NULL;
+
+  const ptrdiff_t idx_next = i - m_clusters;  // insertion position
+
+  if (!PreloadCluster(pNext, idx_next)) {
+    delete pNext;
+    return NULL;
+  }
+  assert(m_clusters);
+  assert(idx_next < m_clusterSize);
+  assert(m_clusters[idx_next] == pNext);
+
+  return pNext;
+}
+
+long Segment::ParseNext(const Cluster* pCurr, const Cluster*& pResult,
+                        long long& pos, long& len) {
+  assert(pCurr);
+  assert(!pCurr->EOS());
+  assert(m_clusters);
+
+  pResult = 0;
+
+  if (pCurr->m_index >= 0) {  // loaded (not merely preloaded)
+    assert(m_clusters[pCurr->m_index] == pCurr);
+
+    const long next_idx = pCurr->m_index + 1;
+
+    if (next_idx < m_clusterCount) {
+      pResult = m_clusters[next_idx];
+      return 0;  // success
+    }
+
+    // curr cluster is last among loaded
+
+    const long result = LoadCluster(pos, len);
+
+    if (result < 0)  // error or underflow
+      return result;
+
+    if (result > 0)  // no more clusters
+    {
+      // pResult = &m_eos;
+      return 1;
+    }
+
+    pResult = GetLast();
+    return 0;  // success
+  }
+
+  assert(m_pos > 0);
+
+  long long total, avail;
+
+  long status = m_pReader->Length(&total, &avail);
+
+  if (status < 0)  // error
+    return status;
+
+  assert((total < 0) || (avail <= total));
+
+  const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+  // interrogate curr cluster
+
+  pos = pCurr->m_element_start;
+
+  if (pCurr->m_element_size >= 0)
+    pos += pCurr->m_element_size;
+  else {
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    long long result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long id = ReadUInt(m_pReader, pos, len);
+
+    if (id != 0x0F43B675)  // weird: not Cluster ID
+      return -1;
+
+    pos += len;  // consume ID
+
+    // Read Size
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long size = ReadUInt(m_pReader, pos, len);
+
+    if (size < 0)  // error
+      return static_cast<long>(size);
+
+    pos += len;  // consume size field
+
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if (size == unknown_size)  // TODO: should never happen
+      return E_FILE_FORMAT_INVALID;  // TODO: resolve this
+
+    // assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
+
+    if ((segment_stop >= 0) && ((pos + size) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    // Pos now points to start of payload
+
+    pos += size;  // consume payload (that is, the current cluster)
+    if (segment_stop >= 0 && pos > segment_stop)
+      return E_FILE_FORMAT_INVALID;
+
+    // By consuming the payload, we are assuming that the curr
+    // cluster isn't interesting.  That is, we don't bother checking
+    // whether the payload of the curr cluster is less than what
+    // happens to be available (obtained via IMkvReader::Length).
+    // Presumably the caller has already dispensed with the current
+    // cluster, and really does want the next cluster.
+  }
+
+  // pos now points to just beyond the last fully-loaded cluster
+
+  for (;;) {
+    const long status = DoParseNext(pResult, pos, len);
+
+    if (status <= 1)
+      return status;
+  }
+}
+
+long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) {
+  long long total, avail;
+
+  long status = m_pReader->Length(&total, &avail);
+
+  if (status < 0)  // error
+    return status;
+
+  assert((total < 0) || (avail <= total));
+
+  const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+  // Parse next cluster.  This is strictly a parsing activity.
+  // Creation of a new cluster object happens later, after the
+  // parsing is done.
+
+  long long off_next = 0;
+  long long cluster_size = -1;
+
+  for (;;) {
+    if ((total >= 0) && (pos >= total))
+      return 1;  // EOF
+
+    if ((segment_stop >= 0) && (pos >= segment_stop))
+      return 1;  // EOF
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    long long result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long idpos = pos;  // absolute
+    const long long idoff = pos - m_start;  // relative
+
+    const long long id = ReadUInt(m_pReader, idpos, len);  // absolute
+
+    if (id < 0)  // error
+      return static_cast<long>(id);
+
+    if (id == 0)  // weird
+      return -1;  // generic error
+
+    pos += len;  // consume ID
+
+    // Read Size
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result = GetUIntLength(m_pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long size = ReadUInt(m_pReader, pos, len);
+
+    if (size < 0)  // error
+      return static_cast<long>(size);
+
+    pos += len;  // consume length of size of element
+
+    // Pos now points to start of payload
+
+    if (size == 0)  // weird
+      continue;
+
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if ((segment_stop >= 0) && (size != unknown_size) &&
+        ((pos + size) > segment_stop)) {
+      return E_FILE_FORMAT_INVALID;
+    }
+
+    if (id == 0x0C53BB6B) {  // Cues ID
+      if (size == unknown_size)
+        return E_FILE_FORMAT_INVALID;
+
+      const long long element_stop = pos + size;
+
+      if ((segment_stop >= 0) && (element_stop > segment_stop))
+        return E_FILE_FORMAT_INVALID;
+
+      const long long element_start = idpos;
+      const long long element_size = element_stop - element_start;
+
+      if (m_pCues == NULL) {
+        m_pCues = new (std::nothrow)
+            Cues(this, pos, size, element_start, element_size);
+        if (m_pCues == NULL)
+          return false;
+      }
+
+      pos += size;  // consume payload
+      if (segment_stop >= 0 && pos > segment_stop)
+        return E_FILE_FORMAT_INVALID;
+
+      continue;
+    }
+
+    if (id != 0x0F43B675) {  // not a Cluster ID
+      if (size == unknown_size)
+        return E_FILE_FORMAT_INVALID;
+
+      pos += size;  // consume payload
+      if (segment_stop >= 0 && pos > segment_stop)
+        return E_FILE_FORMAT_INVALID;
+
+      continue;
+    }
+
+    // We have a cluster.
+    off_next = idoff;
+
+    if (size != unknown_size)
+      cluster_size = size;
+
+    break;
+  }
+
+  assert(off_next > 0);  // have cluster
+
+  // We have parsed the next cluster.
+  // We have not created a cluster object yet.  What we need
+  // to do now is determine whether it has already be preloaded
+  //(in which case, an object for this cluster has already been
+  // created), and if not, create a new cluster object.
+
+  Cluster** const ii = m_clusters + m_clusterCount;
+  Cluster** i = ii;
+
+  Cluster** const jj = ii + m_clusterPreloadCount;
+  Cluster** j = jj;
+
+  while (i < j) {
+    // INVARIANT:
+    //[0, i) < pos_next
+    //[i, j) ?
+    //[j, jj)  > pos_next
+
+    Cluster** const k = i + (j - i) / 2;
+    assert(k < jj);
+
+    const Cluster* const pNext = *k;
+    assert(pNext);
+    assert(pNext->m_index < 0);
+
+    pos = pNext->GetPosition();
+    assert(pos >= 0);
+
+    if (pos < off_next)
+      i = k + 1;
+    else if (pos > off_next)
+      j = k;
+    else {
+      pResult = pNext;
+      return 0;  // success
+    }
+  }
+
+  assert(i == j);
+
+  long long pos_;
+  long len_;
+
+  status = Cluster::HasBlockEntries(this, off_next, pos_, len_);
+
+  if (status < 0) {  // error or underflow
+    pos = pos_;
+    len = len_;
+
+    return status;
+  }
+
+  if (status > 0) {  // means "found at least one block entry"
+    Cluster* const pNext = Cluster::Create(this,
+                                           -1,  // preloaded
+                                           off_next);
+    if (pNext == NULL)
+      return -1;
+
+    const ptrdiff_t idx_next = i - m_clusters;  // insertion position
+
+    if (!PreloadCluster(pNext, idx_next)) {
+      delete pNext;
+      return -1;
+    }
+    assert(m_clusters);
+    assert(idx_next < m_clusterSize);
+    assert(m_clusters[idx_next] == pNext);
+
+    pResult = pNext;
+    return 0;  // success
+  }
+
+  // status == 0 means "no block entries found"
+
+  if (cluster_size < 0) {  // unknown size
+    const long long payload_pos = pos;  // absolute pos of cluster payload
+
+    for (;;) {  // determine cluster size
+      if ((total >= 0) && (pos >= total))
+        break;
+
+      if ((segment_stop >= 0) && (pos >= segment_stop))
+        break;  // no more clusters
+
+      // Read ID
+
+      if ((pos + 1) > avail) {
+        len = 1;
+        return E_BUFFER_NOT_FULL;
+      }
+
+      long long result = GetUIntLength(m_pReader, pos, len);
+
+      if (result < 0)  // error
+        return static_cast<long>(result);
+
+      if (result > 0)  // weird
+        return E_BUFFER_NOT_FULL;
+
+      if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+        return E_FILE_FORMAT_INVALID;
+
+      if ((pos + len) > avail)
+        return E_BUFFER_NOT_FULL;
+
+      const long long idpos = pos;
+      const long long id = ReadUInt(m_pReader, idpos, len);
+
+      if (id < 0)  // error (or underflow)
+        return static_cast<long>(id);
+
+      // This is the distinguished set of ID's we use to determine
+      // that we have exhausted the sub-element's inside the cluster
+      // whose ID we parsed earlier.
+
+      if (id == 0x0F43B675)  // Cluster ID
+        break;
+
+      if (id == 0x0C53BB6B)  // Cues ID
+        break;
+
+      pos += len;  // consume ID (of sub-element)
+
+      // Read Size
+
+      if ((pos + 1) > avail) {
+        len = 1;
+        return E_BUFFER_NOT_FULL;
+      }
+
+      result = GetUIntLength(m_pReader, pos, len);
+
+      if (result < 0)  // error
+        return static_cast<long>(result);
+
+      if (result > 0)  // weird
+        return E_BUFFER_NOT_FULL;
+
+      if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+        return E_FILE_FORMAT_INVALID;
+
+      if ((pos + len) > avail)
+        return E_BUFFER_NOT_FULL;
+
+      const long long size = ReadUInt(m_pReader, pos, len);
+
+      if (size < 0)  // error
+        return static_cast<long>(size);
+
+      pos += len;  // consume size field of element
+
+      // pos now points to start of sub-element's payload
+
+      if (size == 0)  // weird
+        continue;
+
+      const long long unknown_size = (1LL << (7 * len)) - 1;
+
+      if (size == unknown_size)
+        return E_FILE_FORMAT_INVALID;  // not allowed for sub-elements
+
+      if ((segment_stop >= 0) && ((pos + size) > segment_stop))  // weird
+        return E_FILE_FORMAT_INVALID;
+
+      pos += size;  // consume payload of sub-element
+      if (segment_stop >= 0 && pos > segment_stop)
+        return E_FILE_FORMAT_INVALID;
+    }  // determine cluster size
+
+    cluster_size = pos - payload_pos;
+    assert(cluster_size >= 0);  // TODO: handle cluster_size = 0
+
+    pos = payload_pos;  // reset and re-parse original cluster
+  }
+
+  pos += cluster_size;  // consume payload
+  if (segment_stop >= 0 && pos > segment_stop)
+    return E_FILE_FORMAT_INVALID;
+
+  return 2;  // try to find a cluster that follows next
+}
+
+const Cluster* Segment::FindCluster(long long time_ns) const {
+  if ((m_clusters == NULL) || (m_clusterCount <= 0))
+    return &m_eos;
+
+  {
+    Cluster* const pCluster = m_clusters[0];
+    assert(pCluster);
+    assert(pCluster->m_index == 0);
+
+    if (time_ns <= pCluster->GetTime())
+      return pCluster;
+  }
+
+  // Binary search of cluster array
+
+  long i = 0;
+  long j = m_clusterCount;
+
+  while (i < j) {
+    // INVARIANT:
+    //[0, i) <= time_ns
+    //[i, j) ?
+    //[j, m_clusterCount)  > time_ns
+
+    const long k = i + (j - i) / 2;
+    assert(k < m_clusterCount);
+
+    Cluster* const pCluster = m_clusters[k];
+    assert(pCluster);
+    assert(pCluster->m_index == k);
+
+    const long long t = pCluster->GetTime();
+
+    if (t <= time_ns)
+      i = k + 1;
+    else
+      j = k;
+
+    assert(i <= j);
+  }
+
+  assert(i == j);
+  assert(i > 0);
+  assert(i <= m_clusterCount);
+
+  const long k = i - 1;
+
+  Cluster* const pCluster = m_clusters[k];
+  assert(pCluster);
+  assert(pCluster->m_index == k);
+  assert(pCluster->GetTime() <= time_ns);
+
+  return pCluster;
+}
+
+const Tracks* Segment::GetTracks() const { return m_pTracks; }
+const SegmentInfo* Segment::GetInfo() const { return m_pInfo; }
+const Cues* Segment::GetCues() const { return m_pCues; }
+const Chapters* Segment::GetChapters() const { return m_pChapters; }
+const Tags* Segment::GetTags() const { return m_pTags; }
+const SeekHead* Segment::GetSeekHead() const { return m_pSeekHead; }
+
+long long Segment::GetDuration() const {
+  assert(m_pInfo);
+  return m_pInfo->GetDuration();
+}
+
+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();
+  }
+  delete[] m_editions;
+}
+
+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;
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+  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() {}
+
+int Chapters::Edition::GetAtomCount() const { return m_atoms_count; }
+
+const Chapters::Atom* Chapters::Edition::GetAtom(int index) const {
+  if (index < 0)
+    return NULL;
+
+  if (index >= m_atoms_count)
+    return NULL;
+
+  return m_atoms + index;
+}
+
+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;
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+  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() {}
+
+unsigned long long Chapters::Atom::GetUID() const { return m_uid; }
+
+const char* Chapters::Atom::GetStringUID() const { return m_string_uid; }
+
+long long Chapters::Atom::GetStartTimecode() const { return m_start_timecode; }
+
+long long Chapters::Atom::GetStopTimecode() const { return m_stop_timecode; }
+
+long long Chapters::Atom::GetStartTime(const Chapters* pChapters) const {
+  return GetTime(pChapters, m_start_timecode);
+}
+
+long long Chapters::Atom::GetStopTime(const Chapters* pChapters) const {
+  return GetTime(pChapters, m_stop_timecode);
+}
+
+int Chapters::Atom::GetDisplayCount() const { return m_displays_count; }
+
+const Chapters::Display* Chapters::Atom::GetDisplay(int index) const {
+  if (index < 0)
+    return NULL;
+
+  if (index >= m_displays_count)
+    return NULL;
+
+  return m_displays + index;
+}
+
+void Chapters::Atom::Init() {
+  m_string_uid = NULL;
+  m_uid = 0;
+  m_start_timecode = -1;
+  m_stop_timecode = -1;
+
+  m_displays = NULL;
+  m_displays_size = 0;
+  m_displays_count = 0;
+}
+
+void Chapters::Atom::ShallowCopy(Atom& rhs) const {
+  rhs.m_string_uid = m_string_uid;
+  rhs.m_uid = m_uid;
+  rhs.m_start_timecode = m_start_timecode;
+  rhs.m_stop_timecode = m_stop_timecode;
+
+  rhs.m_displays = m_displays;
+  rhs.m_displays_size = m_displays_size;
+  rhs.m_displays_count = m_displays_count;
+}
+
+void Chapters::Atom::Clear() {
+  delete[] m_string_uid;
+  m_string_uid = NULL;
+
+  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 == 0x1654) {  // StringUID ID
+      status = UnserializeString(pReader, pos, size, m_string_uid);
+
+      if (status < 0)  // error
+        return status;
+    } else if (id == 0x33C4) {  // UID ID
+      long long val;
+      status = UnserializeInt(pReader, pos, size, val);
+
+      if (status < 0)  // error
+        return status;
+
+      m_uid = static_cast<unsigned long long>(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;
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+  return 0;
+}
+
+long long Chapters::Atom::GetTime(const Chapters* pChapters,
+                                  long long timecode) {
+  if (pChapters == NULL)
+    return -1;
+
+  Segment* const pSegment = pChapters->m_pSegment;
+
+  if (pSegment == NULL)  // weird
+    return -1;
+
+  const SegmentInfo* const pInfo = pSegment->GetInfo();
+
+  if (pInfo == NULL)
+    return -1;
+
+  const long long timecode_scale = pInfo->GetTimeCodeScale();
+
+  if (timecode_scale < 1)  // weird
+    return -1;
+
+  if (timecode < 0)
+    return -1;
+
+  const long long result = timecode_scale * timecode;
+
+  return result;
+}
+
+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;
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+  return 0;
+}
+
+Tags::Tags(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_tags(NULL),
+      m_tags_size(0),
+      m_tags_count(0) {}
+
+Tags::~Tags() {
+  while (m_tags_count > 0) {
+    Tag& t = m_tags[--m_tags_count];
+    t.Clear();
+  }
+  delete[] m_tags;
+}
+
+long Tags::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)
+      return status;
+
+    if (size == 0)  // 0 length tag, read another
+      continue;
+
+    if (id == 0x3373) {  // Tag ID
+      status = ParseTag(pos, size);
+
+      if (status < 0)
+        return status;
+    }
+
+    pos += size;
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;
+}
+
+int Tags::GetTagCount() const { return m_tags_count; }
+
+const Tags::Tag* Tags::GetTag(int idx) const {
+  if (idx < 0)
+    return NULL;
+
+  if (idx >= m_tags_count)
+    return NULL;
+
+  return m_tags + idx;
+}
+
+bool Tags::ExpandTagsArray() {
+  if (m_tags_size > m_tags_count)
+    return true;  // nothing else to do
+
+  const int size = (m_tags_size == 0) ? 1 : 2 * m_tags_size;
+
+  Tag* const tags = new (std::nothrow) Tag[size];
+
+  if (tags == NULL)
+    return false;
+
+  for (int idx = 0; idx < m_tags_count; ++idx) {
+    m_tags[idx].ShallowCopy(tags[idx]);
+  }
+
+  delete[] m_tags;
+  m_tags = tags;
+
+  m_tags_size = size;
+  return true;
+}
+
+long Tags::ParseTag(long long pos, long long size) {
+  if (!ExpandTagsArray())
+    return -1;
+
+  Tag& t = m_tags[m_tags_count++];
+  t.Init();
+
+  return t.Parse(m_pSegment->m_pReader, pos, size);
+}
+
+Tags::Tag::Tag() {}
+
+Tags::Tag::~Tag() {}
+
+int Tags::Tag::GetSimpleTagCount() const { return m_simple_tags_count; }
+
+const Tags::SimpleTag* Tags::Tag::GetSimpleTag(int index) const {
+  if (index < 0)
+    return NULL;
+
+  if (index >= m_simple_tags_count)
+    return NULL;
+
+  return m_simple_tags + index;
+}
+
+void Tags::Tag::Init() {
+  m_simple_tags = NULL;
+  m_simple_tags_size = 0;
+  m_simple_tags_count = 0;
+}
+
+void Tags::Tag::ShallowCopy(Tag& rhs) const {
+  rhs.m_simple_tags = m_simple_tags;
+  rhs.m_simple_tags_size = m_simple_tags_size;
+  rhs.m_simple_tags_count = m_simple_tags_count;
+}
+
+void Tags::Tag::Clear() {
+  while (m_simple_tags_count > 0) {
+    SimpleTag& d = m_simple_tags[--m_simple_tags_count];
+    d.Clear();
+  }
+
+  delete[] m_simple_tags;
+  m_simple_tags = NULL;
+
+  m_simple_tags_size = 0;
+}
+
+long Tags::Tag::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)
+      return status;
+
+    if (size == 0)  // 0 length tag, read another
+      continue;
+
+    if (id == 0x27C8) {  // SimpleTag ID
+      status = ParseSimpleTag(pReader, pos, size);
+
+      if (status < 0)
+        return status;
+    }
+
+    pos += size;
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+  return 0;
+}
+
+long Tags::Tag::ParseSimpleTag(IMkvReader* pReader, long long pos,
+                               long long size) {
+  if (!ExpandSimpleTagsArray())
+    return -1;
+
+  SimpleTag& st = m_simple_tags[m_simple_tags_count++];
+  st.Init();
+
+  return st.Parse(pReader, pos, size);
+}
+
+bool Tags::Tag::ExpandSimpleTagsArray() {
+  if (m_simple_tags_size > m_simple_tags_count)
+    return true;  // nothing else to do
+
+  const int size = (m_simple_tags_size == 0) ? 1 : 2 * m_simple_tags_size;
+
+  SimpleTag* const displays = new (std::nothrow) SimpleTag[size];
+
+  if (displays == NULL)
+    return false;
+
+  for (int idx = 0; idx < m_simple_tags_count; ++idx) {
+    m_simple_tags[idx].ShallowCopy(displays[idx]);
+  }
+
+  delete[] m_simple_tags;
+  m_simple_tags = displays;
+
+  m_simple_tags_size = size;
+  return true;
+}
+
+Tags::SimpleTag::SimpleTag() {}
+
+Tags::SimpleTag::~SimpleTag() {}
+
+const char* Tags::SimpleTag::GetTagName() const { return m_tag_name; }
+
+const char* Tags::SimpleTag::GetTagString() const { return m_tag_string; }
+
+void Tags::SimpleTag::Init() {
+  m_tag_name = NULL;
+  m_tag_string = NULL;
+}
+
+void Tags::SimpleTag::ShallowCopy(SimpleTag& rhs) const {
+  rhs.m_tag_name = m_tag_name;
+  rhs.m_tag_string = m_tag_string;
+}
+
+void Tags::SimpleTag::Clear() {
+  delete[] m_tag_name;
+  m_tag_name = NULL;
+
+  delete[] m_tag_string;
+  m_tag_string = NULL;
+}
+
+long Tags::SimpleTag::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 == 0x5A3) {  // TagName ID
+      status = UnserializeString(pReader, pos, size, m_tag_name);
+
+      if (status)
+        return status;
+    } else if (id == 0x487) {  // TagString ID
+      status = UnserializeString(pReader, pos, size, m_tag_string);
+
+      if (status)
+        return status;
+    }
+
+    pos += size;
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+  return 0;
+}
+
+SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_,
+                         long long element_start, long long element_size)
+    : m_pSegment(pSegment),
+      m_start(start),
+      m_size(size_),
+      m_element_start(element_start),
+      m_element_size(element_size),
+      m_pMuxingAppAsUTF8(NULL),
+      m_pWritingAppAsUTF8(NULL),
+      m_pTitleAsUTF8(NULL) {}
+
+SegmentInfo::~SegmentInfo() {
+  delete[] m_pMuxingAppAsUTF8;
+  m_pMuxingAppAsUTF8 = NULL;
+
+  delete[] m_pWritingAppAsUTF8;
+  m_pWritingAppAsUTF8 = NULL;
+
+  delete[] m_pTitleAsUTF8;
+  m_pTitleAsUTF8 = NULL;
+}
+
+long SegmentInfo::Parse() {
+  assert(m_pMuxingAppAsUTF8 == NULL);
+  assert(m_pWritingAppAsUTF8 == NULL);
+  assert(m_pTitleAsUTF8 == NULL);
+
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  long long pos = m_start;
+  const long long stop = m_start + m_size;
+
+  m_timecodeScale = 1000000;
+  m_duration = -1;
+
+  while (pos < stop) {
+    long long id, size;
+
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+    if (status < 0)  // error
+      return status;
+
+    if (id == 0x0AD7B1) {  // Timecode Scale
+      m_timecodeScale = UnserializeUInt(pReader, pos, size);
+
+      if (m_timecodeScale <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x0489) {  // Segment duration
+      const long status = UnserializeFloat(pReader, pos, size, m_duration);
+
+      if (status < 0)
+        return status;
+
+      if (m_duration < 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x0D80) {  // MuxingApp
+      const long status =
+          UnserializeString(pReader, pos, size, m_pMuxingAppAsUTF8);
+
+      if (status)
+        return status;
+    } else if (id == 0x1741) {  // WritingApp
+      const long status =
+          UnserializeString(pReader, pos, size, m_pWritingAppAsUTF8);
+
+      if (status)
+        return status;
+    } else if (id == 0x3BA9) {  // Title
+      const long status = UnserializeString(pReader, pos, size, m_pTitleAsUTF8);
+
+      if (status)
+        return status;
+    }
+
+    pos += size;
+
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  const double rollover_check = m_duration * m_timecodeScale;
+  if (rollover_check > LONG_LONG_MAX)
+    return E_FILE_FORMAT_INVALID;
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;
+}
+
+long long SegmentInfo::GetTimeCodeScale() const { return m_timecodeScale; }
+
+long long SegmentInfo::GetDuration() const {
+  if (m_duration < 0)
+    return -1;
+
+  assert(m_timecodeScale >= 1);
+
+  const double dd = double(m_duration) * double(m_timecodeScale);
+  const long long d = static_cast<long long>(dd);
+
+  return d;
+}
+
+const char* SegmentInfo::GetMuxingAppAsUTF8() const {
+  return m_pMuxingAppAsUTF8;
+}
+
+const char* SegmentInfo::GetWritingAppAsUTF8() const {
+  return m_pWritingAppAsUTF8;
+}
+
+const char* SegmentInfo::GetTitleAsUTF8() const { return m_pTitleAsUTF8; }
+
+///////////////////////////////////////////////////////////////
+// ContentEncoding element
+ContentEncoding::ContentCompression::ContentCompression()
+    : algo(0), settings(NULL), settings_len(0) {}
+
+ContentEncoding::ContentCompression::~ContentCompression() {
+  delete[] settings;
+}
+
+ContentEncoding::ContentEncryption::ContentEncryption()
+    : algo(0),
+      key_id(NULL),
+      key_id_len(0),
+      signature(NULL),
+      signature_len(0),
+      sig_key_id(NULL),
+      sig_key_id_len(0),
+      sig_algo(0),
+      sig_hash_algo(0) {}
+
+ContentEncoding::ContentEncryption::~ContentEncryption() {
+  delete[] key_id;
+  delete[] signature;
+  delete[] sig_key_id;
+}
+
+ContentEncoding::ContentEncoding()
+    : compression_entries_(NULL),
+      compression_entries_end_(NULL),
+      encryption_entries_(NULL),
+      encryption_entries_end_(NULL),
+      encoding_order_(0),
+      encoding_scope_(1),
+      encoding_type_(0) {}
+
+ContentEncoding::~ContentEncoding() {
+  ContentCompression** comp_i = compression_entries_;
+  ContentCompression** const comp_j = compression_entries_end_;
+
+  while (comp_i != comp_j) {
+    ContentCompression* const comp = *comp_i++;
+    delete comp;
+  }
+
+  delete[] compression_entries_;
+
+  ContentEncryption** enc_i = encryption_entries_;
+  ContentEncryption** const enc_j = encryption_entries_end_;
+
+  while (enc_i != enc_j) {
+    ContentEncryption* const enc = *enc_i++;
+    delete enc;
+  }
+
+  delete[] encryption_entries_;
+}
+
+const ContentEncoding::ContentCompression*
+    ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
+  const ptrdiff_t count = compression_entries_end_ - compression_entries_;
+  assert(count >= 0);
+
+  if (idx >= static_cast<unsigned long>(count))
+    return NULL;
+
+  return compression_entries_[idx];
+}
+
+unsigned long ContentEncoding::GetCompressionCount() const {
+  const ptrdiff_t count = compression_entries_end_ - compression_entries_;
+  assert(count >= 0);
+
+  return static_cast<unsigned long>(count);
+}
+
+const ContentEncoding::ContentEncryption* ContentEncoding::GetEncryptionByIndex(
+    unsigned long idx) const {
+  const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
+  assert(count >= 0);
+
+  if (idx >= static_cast<unsigned long>(count))
+    return NULL;
+
+  return encryption_entries_[idx];
+}
+
+unsigned long ContentEncoding::GetEncryptionCount() const {
+  const ptrdiff_t count = encryption_entries_end_ - encryption_entries_;
+  assert(count >= 0);
+
+  return static_cast<unsigned long>(count);
+}
+
+long ContentEncoding::ParseContentEncAESSettingsEntry(
+    long long start, long long size, IMkvReader* pReader,
+    ContentEncAESSettings* aes) {
+  assert(pReader);
+  assert(aes);
+
+  long long pos = start;
+  const long long stop = start + size;
+
+  while (pos < stop) {
+    long long id, size;
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+    if (status < 0)  // error
+      return status;
+
+    if (id == 0x7E8) {
+      // AESSettingsCipherMode
+      aes->cipher_mode = UnserializeUInt(pReader, pos, size);
+      if (aes->cipher_mode != 1)
+        return E_FILE_FORMAT_INVALID;
+    }
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  return 0;
+}
+
+long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
+                                                IMkvReader* pReader) {
+  assert(pReader);
+
+  long long pos = start;
+  const long long stop = start + size;
+
+  // Count ContentCompression and ContentEncryption elements.
+  int compression_count = 0;
+  int encryption_count = 0;
+
+  while (pos < stop) {
+    long long id, size;
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+    if (status < 0)  // error
+      return status;
+
+    if (id == 0x1034)  // ContentCompression ID
+      ++compression_count;
+
+    if (id == 0x1035)  // ContentEncryption ID
+      ++encryption_count;
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (compression_count <= 0 && encryption_count <= 0)
+    return -1;
+
+  if (compression_count > 0) {
+    compression_entries_ =
+        new (std::nothrow) ContentCompression*[compression_count];
+    if (!compression_entries_)
+      return -1;
+    compression_entries_end_ = compression_entries_;
+  }
+
+  if (encryption_count > 0) {
+    encryption_entries_ =
+        new (std::nothrow) ContentEncryption*[encryption_count];
+    if (!encryption_entries_) {
+      delete[] compression_entries_;
+      return -1;
+    }
+    encryption_entries_end_ = encryption_entries_;
+  }
+
+  pos = start;
+  while (pos < stop) {
+    long long id, size;
+    long status = ParseElementHeader(pReader, pos, stop, id, size);
+    if (status < 0)  // error
+      return status;
+
+    if (id == 0x1031) {
+      // ContentEncodingOrder
+      encoding_order_ = UnserializeUInt(pReader, pos, size);
+    } else if (id == 0x1032) {
+      // ContentEncodingScope
+      encoding_scope_ = UnserializeUInt(pReader, pos, size);
+      if (encoding_scope_ < 1)
+        return -1;
+    } else if (id == 0x1033) {
+      // ContentEncodingType
+      encoding_type_ = UnserializeUInt(pReader, pos, size);
+    } else if (id == 0x1034) {
+      // ContentCompression ID
+      ContentCompression* const compression =
+          new (std::nothrow) ContentCompression();
+      if (!compression)
+        return -1;
+
+      status = ParseCompressionEntry(pos, size, pReader, compression);
+      if (status) {
+        delete compression;
+        return status;
+      }
+      *compression_entries_end_++ = compression;
+    } else if (id == 0x1035) {
+      // ContentEncryption ID
+      ContentEncryption* const encryption =
+          new (std::nothrow) ContentEncryption();
+      if (!encryption)
+        return -1;
+
+      status = ParseEncryptionEntry(pos, size, pReader, encryption);
+      if (status) {
+        delete encryption;
+        return status;
+      }
+      *encryption_entries_end_++ = encryption;
+    }
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+  return 0;
+}
+
+long ContentEncoding::ParseCompressionEntry(long long start, long long size,
+                                            IMkvReader* pReader,
+                                            ContentCompression* compression) {
+  assert(pReader);
+  assert(compression);
+
+  long long pos = start;
+  const long long stop = start + size;
+
+  bool valid = false;
+
+  while (pos < stop) {
+    long long id, size;
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+    if (status < 0)  // error
+      return status;
+
+    if (id == 0x254) {
+      // ContentCompAlgo
+      long long algo = UnserializeUInt(pReader, pos, size);
+      if (algo < 0)
+        return E_FILE_FORMAT_INVALID;
+      compression->algo = algo;
+      valid = true;
+    } else if (id == 0x255) {
+      // ContentCompSettings
+      if (size <= 0)
+        return E_FILE_FORMAT_INVALID;
+
+      const size_t buflen = static_cast<size_t>(size);
+      unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
+      if (buf == NULL)
+        return -1;
+
+      const int read_status =
+          pReader->Read(pos, static_cast<long>(buflen), buf);
+      if (read_status) {
+        delete[] buf;
+        return status;
+      }
+
+      compression->settings = buf;
+      compression->settings_len = buflen;
+    }
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  // ContentCompAlgo is mandatory
+  if (!valid)
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;
+}
+
+long ContentEncoding::ParseEncryptionEntry(long long start, long long size,
+                                           IMkvReader* pReader,
+                                           ContentEncryption* encryption) {
+  assert(pReader);
+  assert(encryption);
+
+  long long pos = start;
+  const long long stop = start + size;
+
+  while (pos < stop) {
+    long long id, size;
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+    if (status < 0)  // error
+      return status;
+
+    if (id == 0x7E1) {
+      // ContentEncAlgo
+      encryption->algo = UnserializeUInt(pReader, pos, size);
+      if (encryption->algo != 5)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x7E2) {
+      // ContentEncKeyID
+      delete[] encryption->key_id;
+      encryption->key_id = NULL;
+      encryption->key_id_len = 0;
+
+      if (size <= 0)
+        return E_FILE_FORMAT_INVALID;
+
+      const size_t buflen = static_cast<size_t>(size);
+      unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
+      if (buf == NULL)
+        return -1;
+
+      const int read_status =
+          pReader->Read(pos, static_cast<long>(buflen), buf);
+      if (read_status) {
+        delete[] buf;
+        return status;
+      }
+
+      encryption->key_id = buf;
+      encryption->key_id_len = buflen;
+    } else if (id == 0x7E3) {
+      // ContentSignature
+      delete[] encryption->signature;
+      encryption->signature = NULL;
+      encryption->signature_len = 0;
+
+      if (size <= 0)
+        return E_FILE_FORMAT_INVALID;
+
+      const size_t buflen = static_cast<size_t>(size);
+      unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
+      if (buf == NULL)
+        return -1;
+
+      const int read_status =
+          pReader->Read(pos, static_cast<long>(buflen), buf);
+      if (read_status) {
+        delete[] buf;
+        return status;
+      }
+
+      encryption->signature = buf;
+      encryption->signature_len = buflen;
+    } else if (id == 0x7E4) {
+      // ContentSigKeyID
+      delete[] encryption->sig_key_id;
+      encryption->sig_key_id = NULL;
+      encryption->sig_key_id_len = 0;
+
+      if (size <= 0)
+        return E_FILE_FORMAT_INVALID;
+
+      const size_t buflen = static_cast<size_t>(size);
+      unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
+      if (buf == NULL)
+        return -1;
+
+      const int read_status =
+          pReader->Read(pos, static_cast<long>(buflen), buf);
+      if (read_status) {
+        delete[] buf;
+        return status;
+      }
+
+      encryption->sig_key_id = buf;
+      encryption->sig_key_id_len = buflen;
+    } else if (id == 0x7E5) {
+      // ContentSigAlgo
+      encryption->sig_algo = UnserializeUInt(pReader, pos, size);
+    } else if (id == 0x7E6) {
+      // ContentSigHashAlgo
+      encryption->sig_hash_algo = UnserializeUInt(pReader, pos, size);
+    } else if (id == 0x7E7) {
+      // ContentEncAESSettings
+      const long status = ParseContentEncAESSettingsEntry(
+          pos, size, pReader, &encryption->aes_settings);
+      if (status)
+        return status;
+    }
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  return 0;
+}
+
+Track::Track(Segment* pSegment, long long element_start, long long element_size)
+    : m_pSegment(pSegment),
+      m_element_start(element_start),
+      m_element_size(element_size),
+      content_encoding_entries_(NULL),
+      content_encoding_entries_end_(NULL) {}
+
+Track::~Track() {
+  Info& info = const_cast<Info&>(m_info);
+  info.Clear();
+
+  ContentEncoding** i = content_encoding_entries_;
+  ContentEncoding** const j = content_encoding_entries_end_;
+
+  while (i != j) {
+    ContentEncoding* const encoding = *i++;
+    delete encoding;
+  }
+
+  delete[] content_encoding_entries_;
+}
+
+long Track::Create(Segment* pSegment, const Info& info, long long element_start,
+                   long long element_size, Track*& pResult) {
+  if (pResult)
+    return -1;
+
+  Track* const pTrack =
+      new (std::nothrow) Track(pSegment, element_start, element_size);
+
+  if (pTrack == NULL)
+    return -1;  // generic error
+
+  const int status = info.Copy(pTrack->m_info);
+
+  if (status) {  // error
+    delete pTrack;
+    return status;
+  }
+
+  pResult = pTrack;
+  return 0;  // success
+}
+
+Track::Info::Info()
+    : uid(0),
+      defaultDuration(0),
+      codecDelay(0),
+      seekPreRoll(0),
+      nameAsUTF8(NULL),
+      language(NULL),
+      codecId(NULL),
+      codecNameAsUTF8(NULL),
+      codecPrivate(NULL),
+      codecPrivateSize(0),
+      lacing(false) {}
+
+Track::Info::~Info() { Clear(); }
+
+void Track::Info::Clear() {
+  delete[] nameAsUTF8;
+  nameAsUTF8 = NULL;
+
+  delete[] language;
+  language = NULL;
+
+  delete[] codecId;
+  codecId = NULL;
+
+  delete[] codecPrivate;
+  codecPrivate = NULL;
+  codecPrivateSize = 0;
+
+  delete[] codecNameAsUTF8;
+  codecNameAsUTF8 = NULL;
+}
+
+int Track::Info::CopyStr(char* Info::*str, Info& dst_) const {
+  if (str == static_cast<char * Info::*>(NULL))
+    return -1;
+
+  char*& dst = dst_.*str;
+
+  if (dst)  // should be NULL already
+    return -1;
+
+  const char* const src = this->*str;
+
+  if (src == NULL)
+    return 0;
+
+  const size_t len = strlen(src);
+
+  dst = SafeArrayAlloc<char>(1, len + 1);
+
+  if (dst == NULL)
+    return -1;
+
+  strcpy(dst, src);
+
+  return 0;
+}
+
+int Track::Info::Copy(Info& dst) const {
+  if (&dst == this)
+    return 0;
+
+  dst.type = type;
+  dst.number = number;
+  dst.defaultDuration = defaultDuration;
+  dst.codecDelay = codecDelay;
+  dst.seekPreRoll = seekPreRoll;
+  dst.uid = uid;
+  dst.lacing = lacing;
+  dst.settings = settings;
+
+  // We now copy the string member variables from src to dst.
+  // This involves memory allocation so in principle the operation
+  // can fail (indeed, that's why we have Info::Copy), so we must
+  // report this to the caller.  An error return from this function
+  // therefore implies that the copy was only partially successful.
+
+  if (int status = CopyStr(&Info::nameAsUTF8, dst))
+    return status;
+
+  if (int status = CopyStr(&Info::language, dst))
+    return status;
+
+  if (int status = CopyStr(&Info::codecId, dst))
+    return status;
+
+  if (int status = CopyStr(&Info::codecNameAsUTF8, dst))
+    return status;
+
+  if (codecPrivateSize > 0) {
+    if (codecPrivate == NULL)
+      return -1;
+
+    if (dst.codecPrivate)
+      return -1;
+
+    if (dst.codecPrivateSize != 0)
+      return -1;
+
+    dst.codecPrivate = SafeArrayAlloc<unsigned char>(1, codecPrivateSize);
+
+    if (dst.codecPrivate == NULL)
+      return -1;
+
+    memcpy(dst.codecPrivate, codecPrivate, codecPrivateSize);
+    dst.codecPrivateSize = codecPrivateSize;
+  }
+
+  return 0;
+}
+
+const BlockEntry* Track::GetEOS() const { return &m_eos; }
+
+long Track::GetType() const { return m_info.type; }
+
+long Track::GetNumber() const { return m_info.number; }
+
+unsigned long long Track::GetUid() const { return m_info.uid; }
+
+const char* Track::GetNameAsUTF8() const { return m_info.nameAsUTF8; }
+
+const char* Track::GetLanguage() const { return m_info.language; }
+
+const char* Track::GetCodecNameAsUTF8() const { return m_info.codecNameAsUTF8; }
+
+const char* Track::GetCodecId() const { return m_info.codecId; }
+
+const unsigned char* Track::GetCodecPrivate(size_t& size) const {
+  size = m_info.codecPrivateSize;
+  return m_info.codecPrivate;
+}
+
+bool Track::GetLacing() const { return m_info.lacing; }
+
+unsigned long long Track::GetDefaultDuration() const {
+  return m_info.defaultDuration;
+}
+
+unsigned long long Track::GetCodecDelay() const { return m_info.codecDelay; }
+
+unsigned long long Track::GetSeekPreRoll() const { return m_info.seekPreRoll; }
+
+long Track::GetFirst(const BlockEntry*& pBlockEntry) const {
+  const Cluster* pCluster = m_pSegment->GetFirst();
+
+  for (int i = 0;;) {
+    if (pCluster == NULL) {
+      pBlockEntry = GetEOS();
+      return 1;
+    }
+
+    if (pCluster->EOS()) {
+      if (m_pSegment->DoneParsing()) {
+        pBlockEntry = GetEOS();
+        return 1;
+      }
+
+      pBlockEntry = 0;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    long status = pCluster->GetFirst(pBlockEntry);
+
+    if (status < 0)  // error
+      return status;
+
+    if (pBlockEntry == 0) {  // empty cluster
+      pCluster = m_pSegment->GetNext(pCluster);
+      continue;
+    }
+
+    for (;;) {
+      const Block* const pBlock = pBlockEntry->GetBlock();
+      assert(pBlock);
+
+      const long long tn = pBlock->GetTrackNumber();
+
+      if ((tn == m_info.number) && VetEntry(pBlockEntry))
+        return 0;
+
+      const BlockEntry* pNextEntry;
+
+      status = pCluster->GetNext(pBlockEntry, pNextEntry);
+
+      if (status < 0)  // error
+        return status;
+
+      if (pNextEntry == 0)
+        break;
+
+      pBlockEntry = pNextEntry;
+    }
+
+    ++i;
+
+    if (i >= 100)
+      break;
+
+    pCluster = m_pSegment->GetNext(pCluster);
+  }
+
+  // NOTE: if we get here, it means that we didn't find a block with
+  // a matching track number.  We interpret that as an error (which
+  // might be too conservative).
+
+  pBlockEntry = GetEOS();  // so we can return a non-NULL value
+  return 1;
+}
+
+long Track::GetNext(const BlockEntry* pCurrEntry,
+                    const BlockEntry*& pNextEntry) const {
+  assert(pCurrEntry);
+  assert(!pCurrEntry->EOS());  //?
+
+  const Block* const pCurrBlock = pCurrEntry->GetBlock();
+  assert(pCurrBlock && pCurrBlock->GetTrackNumber() == m_info.number);
+  if (!pCurrBlock || pCurrBlock->GetTrackNumber() != m_info.number)
+    return -1;
+
+  const Cluster* pCluster = pCurrEntry->GetCluster();
+  assert(pCluster);
+  assert(!pCluster->EOS());
+
+  long status = pCluster->GetNext(pCurrEntry, pNextEntry);
+
+  if (status < 0)  // error
+    return status;
+
+  for (int i = 0;;) {
+    while (pNextEntry) {
+      const Block* const pNextBlock = pNextEntry->GetBlock();
+      assert(pNextBlock);
+
+      if (pNextBlock->GetTrackNumber() == m_info.number)
+        return 0;
+
+      pCurrEntry = pNextEntry;
+
+      status = pCluster->GetNext(pCurrEntry, pNextEntry);
+
+      if (status < 0)  // error
+        return status;
+    }
+
+    pCluster = m_pSegment->GetNext(pCluster);
+
+    if (pCluster == NULL) {
+      pNextEntry = GetEOS();
+      return 1;
+    }
+
+    if (pCluster->EOS()) {
+      if (m_pSegment->DoneParsing()) {
+        pNextEntry = GetEOS();
+        return 1;
+      }
+
+      // TODO: there is a potential O(n^2) problem here: we tell the
+      // caller to (pre)load another cluster, which he does, but then he
+      // calls GetNext again, which repeats the same search.  This is
+      // a pathological case, since the only way it can happen is if
+      // there exists a long sequence of clusters none of which contain a
+      // block from this track.  One way around this problem is for the
+      // caller to be smarter when he loads another cluster: don't call
+      // us back until you have a cluster that contains a block from this
+      // track. (Of course, that's not cheap either, since our caller
+      // would have to scan the each cluster as it's loaded, so that
+      // would just push back the problem.)
+
+      pNextEntry = NULL;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    status = pCluster->GetFirst(pNextEntry);
+
+    if (status < 0)  // error
+      return status;
+
+    if (pNextEntry == NULL)  // empty cluster
+      continue;
+
+    ++i;
+
+    if (i >= 100)
+      break;
+  }
+
+  // NOTE: if we get here, it means that we didn't find a block with
+  // a matching track number after lots of searching, so we give
+  // up trying.
+
+  pNextEntry = GetEOS();  // so we can return a non-NULL value
+  return 1;
+}
+
+bool Track::VetEntry(const BlockEntry* pBlockEntry) const {
+  assert(pBlockEntry);
+  const Block* const pBlock = pBlockEntry->GetBlock();
+  assert(pBlock);
+  assert(pBlock->GetTrackNumber() == m_info.number);
+  if (!pBlock || pBlock->GetTrackNumber() != m_info.number)
+    return false;
+
+  // This function is used during a seek to determine whether the
+  // frame is a valid seek target.  This default function simply
+  // returns true, which means all frames are valid seek targets.
+  // It gets overridden by the VideoTrack class, because only video
+  // keyframes can be used as seek target.
+
+  return true;
+}
+
+long Track::Seek(long long time_ns, const BlockEntry*& pResult) const {
+  const long status = GetFirst(pResult);
+
+  if (status < 0)  // buffer underflow, etc
+    return status;
+
+  assert(pResult);
+
+  if (pResult->EOS())
+    return 0;
+
+  const Cluster* pCluster = pResult->GetCluster();
+  assert(pCluster);
+  assert(pCluster->GetIndex() >= 0);
+
+  if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
+    return 0;
+
+  Cluster** const clusters = m_pSegment->m_clusters;
+  assert(clusters);
+
+  const long count = m_pSegment->GetCount();  // loaded only, not preloaded
+  assert(count > 0);
+
+  Cluster** const i = clusters + pCluster->GetIndex();
+  assert(i);
+  assert(*i == pCluster);
+  assert(pCluster->GetTime() <= time_ns);
+
+  Cluster** const j = clusters + count;
+
+  Cluster** lo = i;
+  Cluster** hi = j;
+
+  while (lo < hi) {
+    // INVARIANT:
+    //[i, lo) <= time_ns
+    //[lo, hi) ?
+    //[hi, j)  > time_ns
+
+    Cluster** const mid = lo + (hi - lo) / 2;
+    assert(mid < hi);
+
+    pCluster = *mid;
+    assert(pCluster);
+    assert(pCluster->GetIndex() >= 0);
+    assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
+
+    const long long t = pCluster->GetTime();
+
+    if (t <= time_ns)
+      lo = mid + 1;
+    else
+      hi = mid;
+
+    assert(lo <= hi);
+  }
+
+  assert(lo == hi);
+  assert(lo > i);
+  assert(lo <= j);
+
+  while (lo > i) {
+    pCluster = *--lo;
+    assert(pCluster);
+    assert(pCluster->GetTime() <= time_ns);
+
+    pResult = pCluster->GetEntry(this);
+
+    if ((pResult != 0) && !pResult->EOS())
+      return 0;
+
+    // landed on empty cluster (no entries)
+  }
+
+  pResult = GetEOS();  // weird
+  return 0;
+}
+
+const ContentEncoding* Track::GetContentEncodingByIndex(
+    unsigned long idx) const {
+  const ptrdiff_t count =
+      content_encoding_entries_end_ - content_encoding_entries_;
+  assert(count >= 0);
+
+  if (idx >= static_cast<unsigned long>(count))
+    return NULL;
+
+  return content_encoding_entries_[idx];
+}
+
+unsigned long Track::GetContentEncodingCount() const {
+  const ptrdiff_t count =
+      content_encoding_entries_end_ - content_encoding_entries_;
+  assert(count >= 0);
+
+  return static_cast<unsigned long>(count);
+}
+
+long Track::ParseContentEncodingsEntry(long long start, long long size) {
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+  assert(pReader);
+
+  long long pos = start;
+  const long long stop = start + size;
+
+  // Count ContentEncoding elements.
+  int count = 0;
+  while (pos < stop) {
+    long long id, size;
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+    if (status < 0)  // error
+      return status;
+
+    // pos now designates start of element
+    if (id == 0x2240)  // ContentEncoding ID
+      ++count;
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (count <= 0)
+    return -1;
+
+  content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count];
+  if (!content_encoding_entries_)
+    return -1;
+
+  content_encoding_entries_end_ = content_encoding_entries_;
+
+  pos = start;
+  while (pos < stop) {
+    long long id, size;
+    long status = ParseElementHeader(pReader, pos, stop, id, size);
+    if (status < 0)  // error
+      return status;
+
+    // pos now designates start of element
+    if (id == 0x2240) {  // ContentEncoding ID
+      ContentEncoding* const content_encoding =
+          new (std::nothrow) ContentEncoding();
+      if (!content_encoding)
+        return -1;
+
+      status = content_encoding->ParseContentEncodingEntry(pos, size, pReader);
+      if (status) {
+        delete content_encoding;
+        return status;
+      }
+
+      *content_encoding_entries_end_++ = content_encoding;
+    }
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;
+}
+
+Track::EOSBlock::EOSBlock() : BlockEntry(NULL, LONG_MIN) {}
+
+BlockEntry::Kind Track::EOSBlock::GetKind() const { return kBlockEOS; }
+
+const Block* Track::EOSBlock::GetBlock() const { return NULL; }
+
+VideoTrack::VideoTrack(Segment* pSegment, long long element_start,
+                       long long element_size)
+    : Track(pSegment, element_start, element_size) {}
+
+long VideoTrack::Parse(Segment* pSegment, const Info& info,
+                       long long element_start, long long element_size,
+                       VideoTrack*& pResult) {
+  if (pResult)
+    return -1;
+
+  if (info.type != Track::kVideo)
+    return -1;
+
+  long long width = 0;
+  long long height = 0;
+  long long display_width = 0;
+  long long display_height = 0;
+  long long display_unit = 0;
+  long long stereo_mode = 0;
+
+  double rate = 0.0;
+
+  IMkvReader* const pReader = pSegment->m_pReader;
+
+  const Settings& s = info.settings;
+  assert(s.start >= 0);
+  assert(s.size >= 0);
+
+  long long pos = s.start;
+  assert(pos >= 0);
+
+  const long long stop = pos + s.size;
+
+  while (pos < stop) {
+    long long id, size;
+
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+    if (status < 0)  // error
+      return status;
+
+    if (id == 0x30) {  // pixel width
+      width = UnserializeUInt(pReader, pos, size);
+
+      if (width <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x3A) {  // pixel height
+      height = UnserializeUInt(pReader, pos, size);
+
+      if (height <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x14B0) {  // display width
+      display_width = UnserializeUInt(pReader, pos, size);
+
+      if (display_width <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x14BA) {  // display height
+      display_height = UnserializeUInt(pReader, pos, size);
+
+      if (display_height <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x14B2) {  // display unit
+      display_unit = UnserializeUInt(pReader, pos, size);
+
+      if (display_unit < 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x13B8) {  // stereo mode
+      stereo_mode = UnserializeUInt(pReader, pos, size);
+
+      if (stereo_mode < 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x0383E3) {  // frame rate
+      const long status = UnserializeFloat(pReader, pos, size, rate);
+
+      if (status < 0)
+        return status;
+
+      if (rate <= 0)
+        return E_FILE_FORMAT_INVALID;
+    }
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+
+  VideoTrack* const pTrack =
+      new (std::nothrow) VideoTrack(pSegment, element_start, element_size);
+
+  if (pTrack == NULL)
+    return -1;  // generic error
+
+  const int status = info.Copy(pTrack->m_info);
+
+  if (status) {  // error
+    delete pTrack;
+    return status;
+  }
+
+  pTrack->m_width = width;
+  pTrack->m_height = height;
+  pTrack->m_display_width = display_width;
+  pTrack->m_display_height = display_height;
+  pTrack->m_display_unit = display_unit;
+  pTrack->m_stereo_mode = stereo_mode;
+  pTrack->m_rate = rate;
+
+  pResult = pTrack;
+  return 0;  // success
+}
+
+bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const {
+  return Track::VetEntry(pBlockEntry) && pBlockEntry->GetBlock()->IsKey();
+}
+
+long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const {
+  const long status = GetFirst(pResult);
+
+  if (status < 0)  // buffer underflow, etc
+    return status;
+
+  assert(pResult);
+
+  if (pResult->EOS())
+    return 0;
+
+  const Cluster* pCluster = pResult->GetCluster();
+  assert(pCluster);
+  assert(pCluster->GetIndex() >= 0);
+
+  if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
+    return 0;
+
+  Cluster** const clusters = m_pSegment->m_clusters;
+  assert(clusters);
+
+  const long count = m_pSegment->GetCount();  // loaded only, not pre-loaded
+  assert(count > 0);
+
+  Cluster** const i = clusters + pCluster->GetIndex();
+  assert(i);
+  assert(*i == pCluster);
+  assert(pCluster->GetTime() <= time_ns);
+
+  Cluster** const j = clusters + count;
+
+  Cluster** lo = i;
+  Cluster** hi = j;
+
+  while (lo < hi) {
+    // INVARIANT:
+    //[i, lo) <= time_ns
+    //[lo, hi) ?
+    //[hi, j)  > time_ns
+
+    Cluster** const mid = lo + (hi - lo) / 2;
+    assert(mid < hi);
+
+    pCluster = *mid;
+    assert(pCluster);
+    assert(pCluster->GetIndex() >= 0);
+    assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
+
+    const long long t = pCluster->GetTime();
+
+    if (t <= time_ns)
+      lo = mid + 1;
+    else
+      hi = mid;
+
+    assert(lo <= hi);
+  }
+
+  assert(lo == hi);
+  assert(lo > i);
+  assert(lo <= j);
+
+  pCluster = *--lo;
+  assert(pCluster);
+  assert(pCluster->GetTime() <= time_ns);
+
+  pResult = pCluster->GetEntry(this, time_ns);
+
+  if ((pResult != 0) && !pResult->EOS())  // found a keyframe
+    return 0;
+
+  while (lo != i) {
+    pCluster = *--lo;
+    assert(pCluster);
+    assert(pCluster->GetTime() <= time_ns);
+
+    pResult = pCluster->GetEntry(this, time_ns);
+
+    if ((pResult != 0) && !pResult->EOS())
+      return 0;
+  }
+
+  // weird: we're on the first cluster, but no keyframe found
+  // should never happen but we must return something anyway
+
+  pResult = GetEOS();
+  return 0;
+}
+
+long long VideoTrack::GetWidth() const { return m_width; }
+
+long long VideoTrack::GetHeight() const { return m_height; }
+
+long long VideoTrack::GetDisplayWidth() const {
+  return m_display_width > 0 ? m_display_width : GetWidth();
+}
+
+long long VideoTrack::GetDisplayHeight() const {
+  return m_display_height > 0 ? m_display_height : GetHeight();
+}
+
+long long VideoTrack::GetDisplayUnit() const { return m_display_unit; }
+
+long long VideoTrack::GetStereoMode() const { return m_stereo_mode; }
+
+double VideoTrack::GetFrameRate() const { return m_rate; }
+
+AudioTrack::AudioTrack(Segment* pSegment, long long element_start,
+                       long long element_size)
+    : Track(pSegment, element_start, element_size) {}
+
+long AudioTrack::Parse(Segment* pSegment, const Info& info,
+                       long long element_start, long long element_size,
+                       AudioTrack*& pResult) {
+  if (pResult)
+    return -1;
+
+  if (info.type != Track::kAudio)
+    return -1;
+
+  IMkvReader* const pReader = pSegment->m_pReader;
+
+  const Settings& s = info.settings;
+  assert(s.start >= 0);
+  assert(s.size >= 0);
+
+  long long pos = s.start;
+  assert(pos >= 0);
+
+  const long long stop = pos + s.size;
+
+  double rate = 8000.0;  // MKV default
+  long long channels = 1;
+  long long bit_depth = 0;
+
+  while (pos < stop) {
+    long long id, size;
+
+    long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+    if (status < 0)  // error
+      return status;
+
+    if (id == 0x35) {  // Sample Rate
+      status = UnserializeFloat(pReader, pos, size, rate);
+
+      if (status < 0)
+        return status;
+
+      if (rate <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x1F) {  // Channel Count
+      channels = UnserializeUInt(pReader, pos, size);
+
+      if (channels <= 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x2264) {  // Bit Depth
+      bit_depth = UnserializeUInt(pReader, pos, size);
+
+      if (bit_depth <= 0)
+        return E_FILE_FORMAT_INVALID;
+    }
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+
+  AudioTrack* const pTrack =
+      new (std::nothrow) AudioTrack(pSegment, element_start, element_size);
+
+  if (pTrack == NULL)
+    return -1;  // generic error
+
+  const int status = info.Copy(pTrack->m_info);
+
+  if (status) {
+    delete pTrack;
+    return status;
+  }
+
+  pTrack->m_rate = rate;
+  pTrack->m_channels = channels;
+  pTrack->m_bitDepth = bit_depth;
+
+  pResult = pTrack;
+  return 0;  // success
+}
+
+double AudioTrack::GetSamplingRate() const { return m_rate; }
+
+long long AudioTrack::GetChannels() const { return m_channels; }
+
+long long AudioTrack::GetBitDepth() const { return m_bitDepth; }
+
+Tracks::Tracks(Segment* pSegment, long long start, long long size_,
+               long long element_start, long long element_size)
+    : m_pSegment(pSegment),
+      m_start(start),
+      m_size(size_),
+      m_element_start(element_start),
+      m_element_size(element_size),
+      m_trackEntries(NULL),
+      m_trackEntriesEnd(NULL) {}
+
+long Tracks::Parse() {
+  assert(m_trackEntries == NULL);
+  assert(m_trackEntriesEnd == NULL);
+
+  const long long stop = m_start + m_size;
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  int count = 0;
+  long long pos = m_start;
+
+  while (pos < stop) {
+    long long id, size;
+
+    const long status = ParseElementHeader(pReader, pos, stop, id, size);
+
+    if (status < 0)  // error
+      return status;
+
+    if (size == 0)  // weird
+      continue;
+
+    if (id == 0x2E)  // TrackEntry ID
+      ++count;
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+
+  if (count <= 0)
+    return 0;  // success
+
+  m_trackEntries = new (std::nothrow) Track*[count];
+
+  if (m_trackEntries == NULL)
+    return -1;
+
+  m_trackEntriesEnd = m_trackEntries;
+
+  pos = m_start;
+
+  while (pos < stop) {
+    const long long element_start = pos;
+
+    long long id, payload_size;
+
+    const long status =
+        ParseElementHeader(pReader, pos, stop, id, payload_size);
+
+    if (status < 0)  // error
+      return status;
+
+    if (payload_size == 0)  // weird
+      continue;
+
+    const long long payload_stop = pos + payload_size;
+    assert(payload_stop <= stop);  // checked in ParseElement
+
+    const long long element_size = payload_stop - element_start;
+
+    if (id == 0x2E) {  // TrackEntry ID
+      Track*& pTrack = *m_trackEntriesEnd;
+      pTrack = NULL;
+
+      const long status = ParseTrackEntry(pos, payload_size, element_start,
+                                          element_size, pTrack);
+
+      if (status)
+        return status;
+
+      if (pTrack)
+        ++m_trackEntriesEnd;
+    }
+
+    pos = payload_stop;
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+
+  return 0;  // success
+}
+
+unsigned long Tracks::GetTracksCount() const {
+  const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
+  assert(result >= 0);
+
+  return static_cast<unsigned long>(result);
+}
+
+long Tracks::ParseTrackEntry(long long track_start, long long track_size,
+                             long long element_start, long long element_size,
+                             Track*& pResult) const {
+  if (pResult)
+    return -1;
+
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  long long pos = track_start;
+  const long long track_stop = track_start + track_size;
+
+  Track::Info info;
+
+  info.type = 0;
+  info.number = 0;
+  info.uid = 0;
+  info.defaultDuration = 0;
+
+  Track::Settings v;
+  v.start = -1;
+  v.size = -1;
+
+  Track::Settings a;
+  a.start = -1;
+  a.size = -1;
+
+  Track::Settings e;  // content_encodings_settings;
+  e.start = -1;
+  e.size = -1;
+
+  long long lacing = 1;  // default is true
+
+  while (pos < track_stop) {
+    long long id, size;
+
+    const long status = ParseElementHeader(pReader, pos, track_stop, id, size);
+
+    if (status < 0)  // error
+      return status;
+
+    if (size < 0)
+      return E_FILE_FORMAT_INVALID;
+
+    const long long start = pos;
+
+    if (id == 0x60) {  // VideoSettings ID
+      v.start = start;
+      v.size = size;
+    } else if (id == 0x61) {  // AudioSettings ID
+      a.start = start;
+      a.size = size;
+    } else if (id == 0x2D80) {  // ContentEncodings ID
+      e.start = start;
+      e.size = size;
+    } else if (id == 0x33C5) {  // Track UID
+      if (size > 8)
+        return E_FILE_FORMAT_INVALID;
+
+      info.uid = 0;
+
+      long long pos_ = start;
+      const long long pos_end = start + size;
+
+      while (pos_ != pos_end) {
+        unsigned char b;
+
+        const int status = pReader->Read(pos_, 1, &b);
+
+        if (status)
+          return status;
+
+        info.uid <<= 8;
+        info.uid |= b;
+
+        ++pos_;
+      }
+    } else if (id == 0x57) {  // Track Number
+      const long long num = UnserializeUInt(pReader, pos, size);
+
+      if ((num <= 0) || (num > 127))
+        return E_FILE_FORMAT_INVALID;
+
+      info.number = static_cast<long>(num);
+    } else if (id == 0x03) {  // Track Type
+      const long long type = UnserializeUInt(pReader, pos, size);
+
+      if ((type <= 0) || (type > 254))
+        return E_FILE_FORMAT_INVALID;
+
+      info.type = static_cast<long>(type);
+    } else if (id == 0x136E) {  // Track Name
+      const long status =
+          UnserializeString(pReader, pos, size, info.nameAsUTF8);
+
+      if (status)
+        return status;
+    } else if (id == 0x02B59C) {  // Track Language
+      const long status = UnserializeString(pReader, pos, size, info.language);
+
+      if (status)
+        return status;
+    } else if (id == 0x03E383) {  // Default Duration
+      const long long duration = UnserializeUInt(pReader, pos, size);
+
+      if (duration < 0)
+        return E_FILE_FORMAT_INVALID;
+
+      info.defaultDuration = static_cast<unsigned long long>(duration);
+    } else if (id == 0x06) {  // CodecID
+      const long status = UnserializeString(pReader, pos, size, info.codecId);
+
+      if (status)
+        return status;
+    } else if (id == 0x1C) {  // lacing
+      lacing = UnserializeUInt(pReader, pos, size);
+
+      if ((lacing < 0) || (lacing > 1))
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x23A2) {  // Codec Private
+      delete[] info.codecPrivate;
+      info.codecPrivate = NULL;
+      info.codecPrivateSize = 0;
+
+      const size_t buflen = static_cast<size_t>(size);
+
+      if (buflen) {
+        unsigned char* buf = SafeArrayAlloc<unsigned char>(1, buflen);
+
+        if (buf == NULL)
+          return -1;
+
+        const int status = pReader->Read(pos, static_cast<long>(buflen), buf);
+
+        if (status) {
+          delete[] buf;
+          return status;
+        }
+
+        info.codecPrivate = buf;
+        info.codecPrivateSize = buflen;
+      }
+    } else if (id == 0x058688) {  // Codec Name
+      const long status =
+          UnserializeString(pReader, pos, size, info.codecNameAsUTF8);
+
+      if (status)
+        return status;
+    } else if (id == 0x16AA) {  // Codec Delay
+      info.codecDelay = UnserializeUInt(pReader, pos, size);
+    } else if (id == 0x16BB) {  // Seek Pre Roll
+      info.seekPreRoll = UnserializeUInt(pReader, pos, size);
+    }
+
+    pos += size;  // consume payload
+    if (pos > track_stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != track_stop)
+    return E_FILE_FORMAT_INVALID;
+
+  if (info.number <= 0)  // not specified
+    return E_FILE_FORMAT_INVALID;
+
+  if (GetTrackByNumber(info.number))
+    return E_FILE_FORMAT_INVALID;
+
+  if (info.type <= 0)  // not specified
+    return E_FILE_FORMAT_INVALID;
+
+  info.lacing = (lacing > 0) ? true : false;
+
+  if (info.type == Track::kVideo) {
+    if (v.start < 0)
+      return E_FILE_FORMAT_INVALID;
+
+    if (a.start >= 0)
+      return E_FILE_FORMAT_INVALID;
+
+    info.settings = v;
+
+    VideoTrack* pTrack = NULL;
+
+    const long status = VideoTrack::Parse(m_pSegment, info, element_start,
+                                          element_size, pTrack);
+
+    if (status)
+      return status;
+
+    pResult = pTrack;
+    assert(pResult);
+
+    if (e.start >= 0)
+      pResult->ParseContentEncodingsEntry(e.start, e.size);
+  } else if (info.type == Track::kAudio) {
+    if (a.start < 0)
+      return E_FILE_FORMAT_INVALID;
+
+    if (v.start >= 0)
+      return E_FILE_FORMAT_INVALID;
+
+    info.settings = a;
+
+    AudioTrack* pTrack = NULL;
+
+    const long status = AudioTrack::Parse(m_pSegment, info, element_start,
+                                          element_size, pTrack);
+
+    if (status)
+      return status;
+
+    pResult = pTrack;
+    assert(pResult);
+
+    if (e.start >= 0)
+      pResult->ParseContentEncodingsEntry(e.start, e.size);
+  } else {
+    // neither video nor audio - probably metadata or subtitles
+
+    if (a.start >= 0)
+      return E_FILE_FORMAT_INVALID;
+
+    if (v.start >= 0)
+      return E_FILE_FORMAT_INVALID;
+
+    if (info.type == Track::kMetadata && e.start >= 0)
+      return E_FILE_FORMAT_INVALID;
+
+    info.settings.start = -1;
+    info.settings.size = 0;
+
+    Track* pTrack = NULL;
+
+    const long status =
+        Track::Create(m_pSegment, info, element_start, element_size, pTrack);
+
+    if (status)
+      return status;
+
+    pResult = pTrack;
+    assert(pResult);
+  }
+
+  return 0;  // success
+}
+
+Tracks::~Tracks() {
+  Track** i = m_trackEntries;
+  Track** const j = m_trackEntriesEnd;
+
+  while (i != j) {
+    Track* const pTrack = *i++;
+    delete pTrack;
+  }
+
+  delete[] m_trackEntries;
+}
+
+const Track* Tracks::GetTrackByNumber(long tn) const {
+  if (tn < 0)
+    return NULL;
+
+  Track** i = m_trackEntries;
+  Track** const j = m_trackEntriesEnd;
+
+  while (i != j) {
+    Track* const pTrack = *i++;
+
+    if (pTrack == NULL)
+      continue;
+
+    if (tn == pTrack->GetNumber())
+      return pTrack;
+  }
+
+  return NULL;  // not found
+}
+
+const Track* Tracks::GetTrackByIndex(unsigned long idx) const {
+  const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
+
+  if (idx >= static_cast<unsigned long>(count))
+    return NULL;
+
+  return m_trackEntries[idx];
+}
+
+long Cluster::Load(long long& pos, long& len) const {
+  assert(m_pSegment);
+  assert(m_pos >= m_element_start);
+
+  if (m_timecode >= 0)  // at least partially loaded
+    return 0;
+
+  assert(m_pos == m_element_start);
+  assert(m_element_size < 0);
+
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  long long total, avail;
+
+  const int status = pReader->Length(&total, &avail);
+
+  if (status < 0)  // error
+    return status;
+
+  assert((total < 0) || (avail <= total));
+  assert((total < 0) || (m_pos <= total));  // TODO: verify this
+
+  pos = m_pos;
+
+  long long cluster_size = -1;
+
+  {
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    long long result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error or underflow
+      return static_cast<long>(result);
+
+    if (result > 0)  // underflow (weird)
+      return E_BUFFER_NOT_FULL;
+
+    // if ((pos + len) > segment_stop)
+    //    return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long id_ = ReadUInt(pReader, pos, len);
+
+    if (id_ < 0)  // error
+      return static_cast<long>(id_);
+
+    if (id_ != 0x0F43B675)  // Cluster ID
+      return E_FILE_FORMAT_INVALID;
+
+    pos += len;  // consume id
+
+    // read cluster size
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    // if ((pos + len) > segment_stop)
+    //    return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long size = ReadUInt(pReader, pos, len);
+
+    if (size < 0)  // error
+      return static_cast<long>(cluster_size);
+
+    if (size == 0)
+      return E_FILE_FORMAT_INVALID;  // TODO: verify this
+
+    pos += len;  // consume length of size of element
+
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if (size != unknown_size)
+      cluster_size = size;
+  }
+
+  // pos points to start of payload
+  long long timecode = -1;
+  long long new_pos = -1;
+  bool bBlock = false;
+
+  long long cluster_stop = (cluster_size < 0) ? -1 : pos + cluster_size;
+
+  for (;;) {
+    if ((cluster_stop >= 0) && (pos >= cluster_stop))
+      break;
+
+    // Parse ID
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    long long result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long id = ReadUInt(pReader, pos, len);
+
+    if (id < 0)  // error
+      return static_cast<long>(id);
+
+    if (id == 0)
+      return E_FILE_FORMAT_INVALID;
+
+    // This is the distinguished set of ID's we use to determine
+    // that we have exhausted the sub-element's inside the cluster
+    // whose ID we parsed earlier.
+
+    if (id == 0x0F43B675)  // Cluster ID
+      break;
+
+    if (id == 0x0C53BB6B)  // Cues ID
+      break;
+
+    pos += len;  // consume ID field
+
+    // Parse Size
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long size = ReadUInt(pReader, pos, len);
+
+    if (size < 0)  // error
+      return static_cast<long>(size);
+
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if (size == unknown_size)
+      return E_FILE_FORMAT_INVALID;
+
+    pos += len;  // consume size field
+
+    if ((cluster_stop >= 0) && (pos > cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    // pos now points to start of payload
+
+    if (size == 0)  // weird
+      continue;
+
+    if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if (id == 0x67) {  // TimeCode ID
+      len = static_cast<long>(size);
+
+      if ((pos + size) > avail)
+        return E_BUFFER_NOT_FULL;
+
+      timecode = UnserializeUInt(pReader, pos, size);
+
+      if (timecode < 0)  // error (or underflow)
+        return static_cast<long>(timecode);
+
+      new_pos = pos + size;
+
+      if (bBlock)
+        break;
+    } else if (id == 0x20) {  // BlockGroup ID
+      bBlock = true;
+      break;
+    } else if (id == 0x23) {  // SimpleBlock ID
+      bBlock = true;
+      break;
+    }
+
+    pos += size;  // consume payload
+    if (cluster_stop >= 0 && pos > cluster_stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (cluster_stop >= 0 && pos > cluster_stop)
+    return E_FILE_FORMAT_INVALID;
+
+  if (timecode < 0)  // no timecode found
+    return E_FILE_FORMAT_INVALID;
+
+  if (!bBlock)
+    return E_FILE_FORMAT_INVALID;
+
+  m_pos = new_pos;  // designates position just beyond timecode payload
+  m_timecode = timecode;  // m_timecode >= 0 means we're partially loaded
+
+  if (cluster_size >= 0)
+    m_element_size = cluster_stop - m_element_start;
+
+  return 0;
+}
+
+long Cluster::Parse(long long& pos, long& len) const {
+  long status = Load(pos, len);
+
+  if (status < 0)
+    return status;
+
+  assert(m_pos >= m_element_start);
+  assert(m_timecode >= 0);
+  // assert(m_size > 0);
+  // assert(m_element_size > m_size);
+
+  const long long cluster_stop =
+      (m_element_size < 0) ? -1 : m_element_start + m_element_size;
+
+  if ((cluster_stop >= 0) && (m_pos >= cluster_stop))
+    return 1;  // nothing else to do
+
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  long long total, avail;
+
+  status = pReader->Length(&total, &avail);
+
+  if (status < 0)  // error
+    return status;
+
+  assert((total < 0) || (avail <= total));
+
+  pos = m_pos;
+
+  for (;;) {
+    if ((cluster_stop >= 0) && (pos >= cluster_stop))
+      break;
+
+    if ((total >= 0) && (pos >= total)) {
+      if (m_element_size < 0)
+        m_element_size = pos - m_element_start;
+
+      break;
+    }
+
+    // Parse ID
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    long long result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long id = ReadUInt(pReader, pos, len);
+
+    if (id < 0)  // error
+      return static_cast<long>(id);
+
+    if (id == 0)  // weird
+      return E_FILE_FORMAT_INVALID;
+
+    // This is the distinguished set of ID's we use to determine
+    // that we have exhausted the sub-element's inside the cluster
+    // whose ID we parsed earlier.
+
+    if ((id == 0x0F43B675) || (id == 0x0C53BB6B)) {  // Cluster or Cues ID
+      if (m_element_size < 0)
+        m_element_size = pos - m_element_start;
+
+      break;
+    }
+
+    pos += len;  // consume ID field
+
+    // Parse Size
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long size = ReadUInt(pReader, pos, len);
+
+    if (size < 0)  // error
+      return static_cast<long>(size);
+
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if (size == unknown_size)
+      return E_FILE_FORMAT_INVALID;
+
+    pos += len;  // consume size field
+
+    if ((cluster_stop >= 0) && (pos > cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    // pos now points to start of payload
+
+    if (size == 0)  // weird
+      continue;
+
+    // const long long block_start = pos;
+    const long long block_stop = pos + size;
+
+    if (cluster_stop >= 0) {
+      if (block_stop > cluster_stop) {
+        if ((id == 0x20) || (id == 0x23))
+          return E_FILE_FORMAT_INVALID;
+
+        pos = cluster_stop;
+        break;
+      }
+    } else if ((total >= 0) && (block_stop > total)) {
+      m_element_size = total - m_element_start;
+      pos = total;
+      break;
+    } else if (block_stop > avail) {
+      len = static_cast<long>(size);
+      return E_BUFFER_NOT_FULL;
+    }
+
+    Cluster* const this_ = const_cast<Cluster*>(this);
+
+    if (id == 0x20)  // BlockGroup
+      return this_->ParseBlockGroup(size, pos, len);
+
+    if (id == 0x23)  // SimpleBlock
+      return this_->ParseSimpleBlock(size, pos, len);
+
+    pos += size;  // consume payload
+    if (cluster_stop >= 0 && pos > cluster_stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  assert(m_element_size > 0);
+
+  m_pos = pos;
+  if (cluster_stop >= 0 && m_pos > cluster_stop)
+    return E_FILE_FORMAT_INVALID;
+
+  if (m_entries_count > 0) {
+    const long idx = m_entries_count - 1;
+
+    const BlockEntry* const pLast = m_entries[idx];
+    assert(pLast);
+
+    const Block* const pBlock = pLast->GetBlock();
+    assert(pBlock);
+
+    const long long start = pBlock->m_start;
+
+    if ((total >= 0) && (start > total))
+      return -1;  // defend against trucated stream
+
+    const long long size = pBlock->m_size;
+
+    const long long stop = start + size;
+    assert((cluster_stop < 0) || (stop <= cluster_stop));
+
+    if ((total >= 0) && (stop > total))
+      return -1;  // defend against trucated stream
+  }
+
+  return 1;  // no more entries
+}
+
+long Cluster::ParseSimpleBlock(long long block_size, long long& pos,
+                               long& len) {
+  const long long block_start = pos;
+  const long long block_stop = pos + block_size;
+
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  long long total, avail;
+
+  long status = pReader->Length(&total, &avail);
+
+  if (status < 0)  // error
+    return status;
+
+  assert((total < 0) || (avail <= total));
+
+  // parse track number
+
+  if ((pos + 1) > avail) {
+    len = 1;
+    return E_BUFFER_NOT_FULL;
+  }
+
+  long long result = GetUIntLength(pReader, pos, len);
+
+  if (result < 0)  // error
+    return static_cast<long>(result);
+
+  if (result > 0)  // weird
+    return E_BUFFER_NOT_FULL;
+
+  if ((pos + len) > block_stop)
+    return E_FILE_FORMAT_INVALID;
+
+  if ((pos + len) > avail)
+    return E_BUFFER_NOT_FULL;
+
+  const long long track = ReadUInt(pReader, pos, len);
+
+  if (track < 0)  // error
+    return static_cast<long>(track);
+
+  if (track == 0)
+    return E_FILE_FORMAT_INVALID;
+
+  pos += len;  // consume track number
+
+  if ((pos + 2) > block_stop)
+    return E_FILE_FORMAT_INVALID;
+
+  if ((pos + 2) > avail) {
+    len = 2;
+    return E_BUFFER_NOT_FULL;
+  }
+
+  pos += 2;  // consume timecode
+
+  if ((pos + 1) > block_stop)
+    return E_FILE_FORMAT_INVALID;
+
+  if ((pos + 1) > avail) {
+    len = 1;
+    return E_BUFFER_NOT_FULL;
+  }
+
+  unsigned char flags;
+
+  status = pReader->Read(pos, 1, &flags);
+
+  if (status < 0) {  // error or underflow
+    len = 1;
+    return status;
+  }
+
+  ++pos;  // consume flags byte
+  assert(pos <= avail);
+
+  if (pos >= block_stop)
+    return E_FILE_FORMAT_INVALID;
+
+  const int lacing = int(flags & 0x06) >> 1;
+
+  if ((lacing != 0) && (block_stop > avail)) {
+    len = static_cast<long>(block_stop - pos);
+    return E_BUFFER_NOT_FULL;
+  }
+
+  status = CreateBlock(0x23,  // simple block id
+                       block_start, block_size,
+                       0);  // DiscardPadding
+
+  if (status != 0)
+    return status;
+
+  m_pos = block_stop;
+
+  return 0;  // success
+}
+
+long Cluster::ParseBlockGroup(long long payload_size, long long& pos,
+                              long& len) {
+  const long long payload_start = pos;
+  const long long payload_stop = pos + payload_size;
+
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  long long total, avail;
+
+  long status = pReader->Length(&total, &avail);
+
+  if (status < 0)  // error
+    return status;
+
+  assert((total < 0) || (avail <= total));
+
+  if ((total >= 0) && (payload_stop > total))
+    return E_FILE_FORMAT_INVALID;
+
+  if (payload_stop > avail) {
+    len = static_cast<long>(payload_size);
+    return E_BUFFER_NOT_FULL;
+  }
+
+  long long discard_padding = 0;
+
+  while (pos < payload_stop) {
+    // parse sub-block element ID
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    long long result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((pos + len) > payload_stop)
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long id = ReadUInt(pReader, pos, len);
+
+    if (id < 0)  // error
+      return static_cast<long>(id);
+
+    if (id == 0)  // not a value ID
+      return E_FILE_FORMAT_INVALID;
+
+    pos += len;  // consume ID field
+
+    // Parse Size
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((pos + len) > payload_stop)
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long size = ReadUInt(pReader, pos, len);
+
+    if (size < 0)  // error
+      return static_cast<long>(size);
+
+    pos += len;  // consume size field
+
+    // pos now points to start of sub-block group payload
+
+    if (pos > payload_stop)
+      return E_FILE_FORMAT_INVALID;
+
+    if (size == 0)  // weird
+      continue;
+
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if (size == unknown_size)
+      return E_FILE_FORMAT_INVALID;
+
+    if (id == 0x35A2) {  // DiscardPadding
+      status = UnserializeInt(pReader, pos, size, discard_padding);
+
+      if (status < 0)  // error
+        return status;
+    }
+
+    if (id != 0x21) {  // sub-part of BlockGroup is not a Block
+      pos += size;  // consume sub-part of block group
+
+      if (pos > payload_stop)
+        return E_FILE_FORMAT_INVALID;
+
+      continue;
+    }
+
+    const long long block_stop = pos + size;
+
+    if (block_stop > payload_stop)
+      return E_FILE_FORMAT_INVALID;
+
+    // parse track number
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((pos + len) > block_stop)
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long track = ReadUInt(pReader, pos, len);
+
+    if (track < 0)  // error
+      return static_cast<long>(track);
+
+    if (track == 0)
+      return E_FILE_FORMAT_INVALID;
+
+    pos += len;  // consume track number
+
+    if ((pos + 2) > block_stop)
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + 2) > avail) {
+      len = 2;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    pos += 2;  // consume timecode
+
+    if ((pos + 1) > block_stop)
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    unsigned char flags;
+
+    status = pReader->Read(pos, 1, &flags);
+
+    if (status < 0) {  // error or underflow
+      len = 1;
+      return status;
+    }
+
+    ++pos;  // consume flags byte
+    assert(pos <= avail);
+
+    if (pos >= block_stop)
+      return E_FILE_FORMAT_INVALID;
+
+    const int lacing = int(flags & 0x06) >> 1;
+
+    if ((lacing != 0) && (block_stop > avail)) {
+      len = static_cast<long>(block_stop - pos);
+      return E_BUFFER_NOT_FULL;
+    }
+
+    pos = block_stop;  // consume block-part of block group
+    if (pos > payload_stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  if (pos != payload_stop)
+    return E_FILE_FORMAT_INVALID;
+
+  status = CreateBlock(0x20,  // BlockGroup ID
+                       payload_start, payload_size, discard_padding);
+  if (status != 0)
+    return status;
+
+  m_pos = payload_stop;
+
+  return 0;  // success
+}
+
+long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const {
+  assert(m_pos >= m_element_start);
+
+  pEntry = NULL;
+
+  if (index < 0)
+    return -1;  // generic error
+
+  if (m_entries_count < 0)
+    return E_BUFFER_NOT_FULL;
+
+  assert(m_entries);
+  assert(m_entries_size > 0);
+  assert(m_entries_count <= m_entries_size);
+
+  if (index < m_entries_count) {
+    pEntry = m_entries[index];
+    assert(pEntry);
+
+    return 1;  // found entry
+  }
+
+  if (m_element_size < 0)  // we don't know cluster end yet
+    return E_BUFFER_NOT_FULL;  // underflow
+
+  const long long element_stop = m_element_start + m_element_size;
+
+  if (m_pos >= element_stop)
+    return 0;  // nothing left to parse
+
+  return E_BUFFER_NOT_FULL;  // underflow, since more remains to be parsed
+}
+
+Cluster* Cluster::Create(Segment* pSegment, long idx, long long off) {
+  if (!pSegment || off < 0)
+    return NULL;
+
+  const long long element_start = pSegment->m_start + off;
+
+  Cluster* const pCluster =
+      new (std::nothrow) Cluster(pSegment, idx, element_start);
+
+  return pCluster;
+}
+
+Cluster::Cluster()
+    : m_pSegment(NULL),
+      m_element_start(0),
+      m_index(0),
+      m_pos(0),
+      m_element_size(0),
+      m_timecode(0),
+      m_entries(NULL),
+      m_entries_size(0),
+      m_entries_count(0)  // means "no entries"
+{}
+
+Cluster::Cluster(Segment* pSegment, long idx, long long element_start
+                 /* long long element_size */)
+    : m_pSegment(pSegment),
+      m_element_start(element_start),
+      m_index(idx),
+      m_pos(element_start),
+      m_element_size(-1 /* element_size */),
+      m_timecode(-1),
+      m_entries(NULL),
+      m_entries_size(0),
+      m_entries_count(-1)  // means "has not been parsed yet"
+{}
+
+Cluster::~Cluster() {
+  if (m_entries_count <= 0)
+    return;
+
+  BlockEntry** i = m_entries;
+  BlockEntry** const j = m_entries + m_entries_count;
+
+  while (i != j) {
+    BlockEntry* p = *i++;
+    assert(p);
+
+    delete p;
+  }
+
+  delete[] m_entries;
+}
+
+bool Cluster::EOS() const { return (m_pSegment == NULL); }
+
+long Cluster::GetIndex() const { return m_index; }
+
+long long Cluster::GetPosition() const {
+  const long long pos = m_element_start - m_pSegment->m_start;
+  assert(pos >= 0);
+
+  return pos;
+}
+
+long long Cluster::GetElementSize() const { return m_element_size; }
+
+long Cluster::HasBlockEntries(
+    const Segment* pSegment,
+    long long off,  // relative to start of segment payload
+    long long& pos, long& len) {
+  assert(pSegment);
+  assert(off >= 0);  // relative to segment
+
+  IMkvReader* const pReader = pSegment->m_pReader;
+
+  long long total, avail;
+
+  long status = pReader->Length(&total, &avail);
+
+  if (status < 0)  // error
+    return status;
+
+  assert((total < 0) || (avail <= total));
+
+  pos = pSegment->m_start + off;  // absolute
+
+  if ((total >= 0) && (pos >= total))
+    return 0;  // we don't even have a complete cluster
+
+  const long long segment_stop =
+      (pSegment->m_size < 0) ? -1 : pSegment->m_start + pSegment->m_size;
+
+  long long cluster_stop = -1;  // interpreted later to mean "unknown size"
+
+  {
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    long long result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // need more data
+      return E_BUFFER_NOT_FULL;
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((total >= 0) && ((pos + len) > total))
+      return 0;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long id = ReadUInt(pReader, pos, len);
+
+    if (id < 0)  // error
+      return static_cast<long>(id);
+
+    if (id != 0x0F43B675)  // weird: not cluster ID
+      return -1;  // generic error
+
+    pos += len;  // consume Cluster ID field
+
+    // read size field
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // weird
+      return E_BUFFER_NOT_FULL;
+
+    if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((total >= 0) && ((pos + len) > total))
+      return 0;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long size = ReadUInt(pReader, pos, len);
+
+    if (size < 0)  // error
+      return static_cast<long>(size);
+
+    if (size == 0)
+      return 0;  // cluster does not have entries
+
+    pos += len;  // consume size field
+
+    // pos now points to start of payload
+
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if (size != unknown_size) {
+      cluster_stop = pos + size;
+      assert(cluster_stop >= 0);
+
+      if ((segment_stop >= 0) && (cluster_stop > segment_stop))
+        return E_FILE_FORMAT_INVALID;
+
+      if ((total >= 0) && (cluster_stop > total))
+        // return E_FILE_FORMAT_INVALID;  //too conservative
+        return 0;  // cluster does not have any entries
+    }
+  }
+
+  for (;;) {
+    if ((cluster_stop >= 0) && (pos >= cluster_stop))
+      return 0;  // no entries detected
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    long long result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // need more data
+      return E_BUFFER_NOT_FULL;
+
+    if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long id = ReadUInt(pReader, pos, len);
+
+    if (id < 0)  // error
+      return static_cast<long>(id);
+
+    // This is the distinguished set of ID's we use to determine
+    // that we have exhausted the sub-element's inside the cluster
+    // whose ID we parsed earlier.
+
+    if (id == 0x0F43B675)  // Cluster ID
+      return 0;  // no entries found
+
+    if (id == 0x0C53BB6B)  // Cues ID
+      return 0;  // no entries found
+
+    pos += len;  // consume id field
+
+    if ((cluster_stop >= 0) && (pos >= cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    // read size field
+
+    if ((pos + 1) > avail) {
+      len = 1;
+      return E_BUFFER_NOT_FULL;
+    }
+
+    result = GetUIntLength(pReader, pos, len);
+
+    if (result < 0)  // error
+      return static_cast<long>(result);
+
+    if (result > 0)  // underflow
+      return E_BUFFER_NOT_FULL;
+
+    if ((cluster_stop >= 0) && ((pos + len) > cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > avail)
+      return E_BUFFER_NOT_FULL;
+
+    const long long size = ReadUInt(pReader, pos, len);
+
+    if (size < 0)  // error
+      return static_cast<long>(size);
+
+    pos += len;  // consume size field
+
+    // pos now points to start of payload
+
+    if ((cluster_stop >= 0) && (pos > cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if (size == 0)  // weird
+      continue;
+
+    const long long unknown_size = (1LL << (7 * len)) - 1;
+
+    if (size == unknown_size)
+      return E_FILE_FORMAT_INVALID;  // not supported inside cluster
+
+    if ((cluster_stop >= 0) && ((pos + size) > cluster_stop))
+      return E_FILE_FORMAT_INVALID;
+
+    if (id == 0x20)  // BlockGroup ID
+      return 1;  // have at least one entry
+
+    if (id == 0x23)  // SimpleBlock ID
+      return 1;  // have at least one entry
+
+    pos += size;  // consume payload
+    if (cluster_stop >= 0 && pos > cluster_stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+}
+
+long long Cluster::GetTimeCode() const {
+  long long pos;
+  long len;
+
+  const long status = Load(pos, len);
+
+  if (status < 0)  // error
+    return status;
+
+  return m_timecode;
+}
+
+long long Cluster::GetTime() const {
+  const long long tc = GetTimeCode();
+
+  if (tc < 0)
+    return tc;
+
+  const SegmentInfo* const pInfo = m_pSegment->GetInfo();
+  assert(pInfo);
+
+  const long long scale = pInfo->GetTimeCodeScale();
+  assert(scale >= 1);
+
+  const long long t = m_timecode * scale;
+
+  return t;
+}
+
+long long Cluster::GetFirstTime() const {
+  const BlockEntry* pEntry;
+
+  const long status = GetFirst(pEntry);
+
+  if (status < 0)  // error
+    return status;
+
+  if (pEntry == NULL)  // empty cluster
+    return GetTime();
+
+  const Block* const pBlock = pEntry->GetBlock();
+  assert(pBlock);
+
+  return pBlock->GetTime(this);
+}
+
+long long Cluster::GetLastTime() const {
+  const BlockEntry* pEntry;
+
+  const long status = GetLast(pEntry);
+
+  if (status < 0)  // error
+    return status;
+
+  if (pEntry == NULL)  // empty cluster
+    return GetTime();
+
+  const Block* const pBlock = pEntry->GetBlock();
+  assert(pBlock);
+
+  return pBlock->GetTime(this);
+}
+
+long Cluster::CreateBlock(long long id,
+                          long long pos,  // absolute pos of payload
+                          long long size, long long discard_padding) {
+  assert((id == 0x20) || (id == 0x23));  // BlockGroup or SimpleBlock
+
+  if (m_entries_count < 0) {  // haven't parsed anything yet
+    assert(m_entries == NULL);
+    assert(m_entries_size == 0);
+
+    m_entries_size = 1024;
+    m_entries = new (std::nothrow) BlockEntry*[m_entries_size];
+    if (m_entries == NULL)
+      return -1;
+
+    m_entries_count = 0;
+  } else {
+    assert(m_entries);
+    assert(m_entries_size > 0);
+    assert(m_entries_count <= m_entries_size);
+
+    if (m_entries_count >= m_entries_size) {
+      const long entries_size = 2 * m_entries_size;
+
+      BlockEntry** const entries = new (std::nothrow) BlockEntry*[entries_size];
+      if (entries == NULL)
+        return -1;
+
+      BlockEntry** src = m_entries;
+      BlockEntry** const src_end = src + m_entries_count;
+
+      BlockEntry** dst = entries;
+
+      while (src != src_end)
+        *dst++ = *src++;
+
+      delete[] m_entries;
+
+      m_entries = entries;
+      m_entries_size = entries_size;
+    }
+  }
+
+  if (id == 0x20)  // BlockGroup ID
+    return CreateBlockGroup(pos, size, discard_padding);
+  else  // SimpleBlock ID
+    return CreateSimpleBlock(pos, size);
+}
+
+long Cluster::CreateBlockGroup(long long start_offset, long long size,
+                               long long discard_padding) {
+  assert(m_entries);
+  assert(m_entries_size > 0);
+  assert(m_entries_count >= 0);
+  assert(m_entries_count < m_entries_size);
+
+  IMkvReader* const pReader = m_pSegment->m_pReader;
+
+  long long pos = start_offset;
+  const long long stop = start_offset + size;
+
+  // For WebM files, there is a bias towards previous reference times
+  //(in order to support alt-ref frames, which refer back to the previous
+  // keyframe).  Normally a 0 value is not possible, but here we tenatively
+  // allow 0 as the value of a reference frame, with the interpretation
+  // that this is a "previous" reference time.
+
+  long long prev = 1;  // nonce
+  long long next = 0;  // nonce
+  long long duration = -1;  // really, this is unsigned
+
+  long long bpos = -1;
+  long long bsize = -1;
+
+  while (pos < stop) {
+    long len;
+    const long long id = ReadID(pReader, pos, len);
+    if (id < 0 || (pos + len) > stop)
+      return E_FILE_FORMAT_INVALID;
+
+    pos += len;  // consume ID
+
+    const long long size = ReadUInt(pReader, pos, len);
+    assert(size >= 0);  // TODO
+    assert((pos + len) <= stop);
+
+    pos += len;  // consume size
+
+    if (id == 0x21) {  // Block ID
+      if (bpos < 0) {  // Block ID
+        bpos = pos;
+        bsize = size;
+      }
+    } else if (id == 0x1B) {  // Duration ID
+      if (size > 8)
+        return E_FILE_FORMAT_INVALID;
+
+      duration = UnserializeUInt(pReader, pos, size);
+
+      if (duration < 0)
+        return E_FILE_FORMAT_INVALID;
+    } else if (id == 0x7B) {  // ReferenceBlock
+      if (size > 8 || size <= 0)
+        return E_FILE_FORMAT_INVALID;
+      const long size_ = static_cast<long>(size);
+
+      long long time;
+
+      long status = UnserializeInt(pReader, pos, size_, time);
+      assert(status == 0);
+      if (status != 0)
+        return -1;
+
+      if (time <= 0)  // see note above
+        prev = time;
+      else  // weird
+        next = time;
+    }
+
+    pos += size;  // consume payload
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+  if (bpos < 0)
+    return E_FILE_FORMAT_INVALID;
+
+  if (pos != stop)
+    return E_FILE_FORMAT_INVALID;
+  assert(bsize >= 0);
+
+  const long idx = m_entries_count;
+
+  BlockEntry** const ppEntry = m_entries + idx;
+  BlockEntry*& pEntry = *ppEntry;
+
+  pEntry = new (std::nothrow)
+      BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding);
+
+  if (pEntry == NULL)
+    return -1;  // generic error
+
+  BlockGroup* const p = static_cast<BlockGroup*>(pEntry);
+
+  const long status = p->Parse();
+
+  if (status == 0) {  // success
+    ++m_entries_count;
+    return 0;
+  }
+
+  delete pEntry;
+  pEntry = 0;
+
+  return status;
+}
+
+long Cluster::CreateSimpleBlock(long long st, long long sz) {
+  assert(m_entries);
+  assert(m_entries_size > 0);
+  assert(m_entries_count >= 0);
+  assert(m_entries_count < m_entries_size);
+
+  const long idx = m_entries_count;
+
+  BlockEntry** const ppEntry = m_entries + idx;
+  BlockEntry*& pEntry = *ppEntry;
+
+  pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz);
+
+  if (pEntry == NULL)
+    return -1;  // generic error
+
+  SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry);
+
+  const long status = p->Parse();
+
+  if (status == 0) {
+    ++m_entries_count;
+    return 0;
+  }
+
+  delete pEntry;
+  pEntry = 0;
+
+  return status;
+}
+
+long Cluster::GetFirst(const BlockEntry*& pFirst) const {
+  if (m_entries_count <= 0) {
+    long long pos;
+    long len;
+
+    const long status = Parse(pos, len);
+
+    if (status < 0) {  // error
+      pFirst = NULL;
+      return status;
+    }
+
+    if (m_entries_count <= 0) {  // empty cluster
+      pFirst = NULL;
+      return 0;
+    }
+  }
+
+  assert(m_entries);
+
+  pFirst = m_entries[0];
+  assert(pFirst);
+
+  return 0;  // success
+}
+
+long Cluster::GetLast(const BlockEntry*& pLast) const {
+  for (;;) {
+    long long pos;
+    long len;
+
+    const long status = Parse(pos, len);
+
+    if (status < 0) {  // error
+      pLast = NULL;
+      return status;
+    }
+
+    if (status > 0)  // no new block
+      break;
+  }
+
+  if (m_entries_count <= 0) {
+    pLast = NULL;
+    return 0;
+  }
+
+  assert(m_entries);
+
+  const long idx = m_entries_count - 1;
+
+  pLast = m_entries[idx];
+  assert(pLast);
+
+  return 0;
+}
+
+long Cluster::GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const {
+  assert(pCurr);
+  assert(m_entries);
+  assert(m_entries_count > 0);
+
+  size_t idx = pCurr->GetIndex();
+  assert(idx < size_t(m_entries_count));
+  assert(m_entries[idx] == pCurr);
+
+  ++idx;
+
+  if (idx >= size_t(m_entries_count)) {
+    long long pos;
+    long len;
+
+    const long status = Parse(pos, len);
+
+    if (status < 0) {  // error
+      pNext = NULL;
+      return status;
+    }
+
+    if (status > 0) {
+      pNext = NULL;
+      return 0;
+    }
+
+    assert(m_entries);
+    assert(m_entries_count > 0);
+    assert(idx < size_t(m_entries_count));
+  }
+
+  pNext = m_entries[idx];
+  assert(pNext);
+
+  return 0;
+}
+
+long Cluster::GetEntryCount() const { return m_entries_count; }
+
+const BlockEntry* Cluster::GetEntry(const Track* pTrack,
+                                    long long time_ns) const {
+  assert(pTrack);
+
+  if (m_pSegment == NULL)  // this is the special EOS cluster
+    return pTrack->GetEOS();
+
+  const BlockEntry* pResult = pTrack->GetEOS();
+
+  long index = 0;
+
+  for (;;) {
+    if (index >= m_entries_count) {
+      long long pos;
+      long len;
+
+      const long status = Parse(pos, len);
+      assert(status >= 0);
+
+      if (status > 0)  // completely parsed, and no more entries
+        return pResult;
+
+      if (status < 0)  // should never happen
+        return 0;
+
+      assert(m_entries);
+      assert(index < m_entries_count);
+    }
+
+    const BlockEntry* const pEntry = m_entries[index];
+    assert(pEntry);
+    assert(!pEntry->EOS());
+
+    const Block* const pBlock = pEntry->GetBlock();
+    assert(pBlock);
+
+    if (pBlock->GetTrackNumber() != pTrack->GetNumber()) {
+      ++index;
+      continue;
+    }
+
+    if (pTrack->VetEntry(pEntry)) {
+      if (time_ns < 0)  // just want first candidate block
+        return pEntry;
+
+      const long long ns = pBlock->GetTime(this);
+
+      if (ns > time_ns)
+        return pResult;
+
+      pResult = pEntry;  // have a candidate
+    } else if (time_ns >= 0) {
+      const long long ns = pBlock->GetTime(this);
+
+      if (ns > time_ns)
+        return pResult;
+    }
+
+    ++index;
+  }
+}
+
+const BlockEntry* Cluster::GetEntry(const CuePoint& cp,
+                                    const CuePoint::TrackPosition& tp) const {
+  assert(m_pSegment);
+  const long long tc = cp.GetTimeCode();
+
+  if (tp.m_block > 0) {
+    const long block = static_cast<long>(tp.m_block);
+    const long index = block - 1;
+
+    while (index >= m_entries_count) {
+      long long pos;
+      long len;
+
+      const long status = Parse(pos, len);
+
+      if (status < 0)  // TODO: can this happen?
+        return NULL;
+
+      if (status > 0)  // nothing remains to be parsed
+        return NULL;
+    }
+
+    const BlockEntry* const pEntry = m_entries[index];
+    assert(pEntry);
+    assert(!pEntry->EOS());
+
+    const Block* const pBlock = pEntry->GetBlock();
+    assert(pBlock);
+
+    if ((pBlock->GetTrackNumber() == tp.m_track) &&
+        (pBlock->GetTimeCode(this) == tc)) {
+      return pEntry;
+    }
+  }
+
+  long index = 0;
+
+  for (;;) {
+    if (index >= m_entries_count) {
+      long long pos;
+      long len;
+
+      const long status = Parse(pos, len);
+
+      if (status < 0)  // TODO: can this happen?
+        return NULL;
+
+      if (status > 0)  // nothing remains to be parsed
+        return NULL;
+
+      assert(m_entries);
+      assert(index < m_entries_count);
+    }
+
+    const BlockEntry* const pEntry = m_entries[index];
+    assert(pEntry);
+    assert(!pEntry->EOS());
+
+    const Block* const pBlock = pEntry->GetBlock();
+    assert(pBlock);
+
+    if (pBlock->GetTrackNumber() != tp.m_track) {
+      ++index;
+      continue;
+    }
+
+    const long long tc_ = pBlock->GetTimeCode(this);
+
+    if (tc_ < tc) {
+      ++index;
+      continue;
+    }
+
+    if (tc_ > tc)
+      return NULL;
+
+    const Tracks* const pTracks = m_pSegment->GetTracks();
+    assert(pTracks);
+
+    const long tn = static_cast<long>(tp.m_track);
+    const Track* const pTrack = pTracks->GetTrackByNumber(tn);
+
+    if (pTrack == NULL)
+      return NULL;
+
+    const long long type = pTrack->GetType();
+
+    if (type == 2)  // audio
+      return pEntry;
+
+    if (type != 1)  // not video
+      return NULL;
+
+    if (!pBlock->IsKey())
+      return NULL;
+
+    return pEntry;
+  }
+}
+
+BlockEntry::BlockEntry(Cluster* p, long idx) : m_pCluster(p), m_index(idx) {}
+BlockEntry::~BlockEntry() {}
+bool BlockEntry::EOS() const { return (GetKind() == kBlockEOS); }
+const Cluster* BlockEntry::GetCluster() const { return m_pCluster; }
+long BlockEntry::GetIndex() const { return m_index; }
+
+SimpleBlock::SimpleBlock(Cluster* pCluster, long idx, long long start,
+                         long long size)
+    : BlockEntry(pCluster, idx), m_block(start, size, 0) {}
+
+long SimpleBlock::Parse() { return m_block.Parse(m_pCluster); }
+BlockEntry::Kind SimpleBlock::GetKind() const { return kBlockSimple; }
+const Block* SimpleBlock::GetBlock() const { return &m_block; }
+
+BlockGroup::BlockGroup(Cluster* pCluster, long idx, long long block_start,
+                       long long block_size, long long prev, long long next,
+                       long long duration, long long discard_padding)
+    : BlockEntry(pCluster, idx),
+      m_block(block_start, block_size, discard_padding),
+      m_prev(prev),
+      m_next(next),
+      m_duration(duration) {}
+
+long BlockGroup::Parse() {
+  const long status = m_block.Parse(m_pCluster);
+
+  if (status)
+    return status;
+
+  m_block.SetKey((m_prev > 0) && (m_next <= 0));
+
+  return 0;
+}
+
+BlockEntry::Kind BlockGroup::GetKind() const { return kBlockGroup; }
+const Block* BlockGroup::GetBlock() const { return &m_block; }
+long long BlockGroup::GetPrevTimeCode() const { return m_prev; }
+long long BlockGroup::GetNextTimeCode() const { return m_next; }
+long long BlockGroup::GetDurationTimeCode() const { return m_duration; }
+
+Block::Block(long long start, long long size_, long long discard_padding)
+    : m_start(start),
+      m_size(size_),
+      m_track(0),
+      m_timecode(-1),
+      m_flags(0),
+      m_frames(NULL),
+      m_frame_count(-1),
+      m_discard_padding(discard_padding) {}
+
+Block::~Block() { delete[] m_frames; }
+
+long Block::Parse(const Cluster* pCluster) {
+  if (pCluster == NULL)
+    return -1;
+
+  if (pCluster->m_pSegment == NULL)
+    return -1;
+
+  assert(m_start >= 0);
+  assert(m_size >= 0);
+  assert(m_track <= 0);
+  assert(m_frames == NULL);
+  assert(m_frame_count <= 0);
+
+  long long pos = m_start;
+  const long long stop = m_start + m_size;
+
+  long len;
+
+  IMkvReader* const pReader = pCluster->m_pSegment->m_pReader;
+
+  m_track = ReadUInt(pReader, pos, len);
+
+  if (m_track <= 0)
+    return E_FILE_FORMAT_INVALID;
+
+  if ((pos + len) > stop)
+    return E_FILE_FORMAT_INVALID;
+
+  pos += len;  // consume track number
+
+  if ((stop - pos) < 2)
+    return E_FILE_FORMAT_INVALID;
+
+  long status;
+  long long value;
+
+  status = UnserializeInt(pReader, pos, 2, value);
+
+  if (status)
+    return E_FILE_FORMAT_INVALID;
+
+  if (value < SHRT_MIN)
+    return E_FILE_FORMAT_INVALID;
+
+  if (value > SHRT_MAX)
+    return E_FILE_FORMAT_INVALID;
+
+  m_timecode = static_cast<short>(value);
+
+  pos += 2;
+
+  if ((stop - pos) <= 0)
+    return E_FILE_FORMAT_INVALID;
+
+  status = pReader->Read(pos, 1, &m_flags);
+
+  if (status)
+    return E_FILE_FORMAT_INVALID;
+
+  const int lacing = int(m_flags & 0x06) >> 1;
+
+  ++pos;  // consume flags byte
+
+  if (lacing == 0) {  // no lacing
+    if (pos > stop)
+      return E_FILE_FORMAT_INVALID;
+
+    m_frame_count = 1;
+    m_frames = new (std::nothrow) Frame[m_frame_count];
+    if (m_frames == NULL)
+      return -1;
+
+    Frame& f = m_frames[0];
+    f.pos = pos;
+
+    const long long frame_size = stop - pos;
+
+    if (frame_size > LONG_MAX || frame_size <= 0)
+      return E_FILE_FORMAT_INVALID;
+
+    f.len = static_cast<long>(frame_size);
+
+    return 0;  // success
+  }
+
+  if (pos >= stop)
+    return E_FILE_FORMAT_INVALID;
+
+  unsigned char biased_count;
+
+  status = pReader->Read(pos, 1, &biased_count);
+
+  if (status)
+    return E_FILE_FORMAT_INVALID;
+
+  ++pos;  // consume frame count
+  if (pos > stop)
+    return E_FILE_FORMAT_INVALID;
+
+  m_frame_count = int(biased_count) + 1;
+
+  m_frames = new (std::nothrow) Frame[m_frame_count];
+  if (m_frames == NULL)
+    return -1;
+
+  if (!m_frames)
+    return E_FILE_FORMAT_INVALID;
+
+  if (lacing == 1) {  // Xiph
+    Frame* pf = m_frames;
+    Frame* const pf_end = pf + m_frame_count;
+
+    long long size = 0;
+    int frame_count = m_frame_count;
+
+    while (frame_count > 1) {
+      long frame_size = 0;
+
+      for (;;) {
+        unsigned char val;
+
+        if (pos >= stop)
+          return E_FILE_FORMAT_INVALID;
+
+        status = pReader->Read(pos, 1, &val);
+
+        if (status)
+          return E_FILE_FORMAT_INVALID;
+
+        ++pos;  // consume xiph size byte
+
+        frame_size += val;
+
+        if (val < 255)
+          break;
+      }
+
+      Frame& f = *pf++;
+      assert(pf < pf_end);
+      if (pf >= pf_end)
+        return E_FILE_FORMAT_INVALID;
+
+      f.pos = 0;  // patch later
+
+      if (frame_size <= 0)
+        return E_FILE_FORMAT_INVALID;
+
+      f.len = frame_size;
+      size += frame_size;  // contribution of this frame
+
+      --frame_count;
+    }
+
+    if (pf >= pf_end || pos > stop)
+      return E_FILE_FORMAT_INVALID;
+
+    {
+      Frame& f = *pf++;
+
+      if (pf != pf_end)
+        return E_FILE_FORMAT_INVALID;
+
+      f.pos = 0;  // patch later
+
+      const long long total_size = stop - pos;
+
+      if (total_size < size)
+        return E_FILE_FORMAT_INVALID;
+
+      const long long frame_size = total_size - size;
+
+      if (frame_size > LONG_MAX || frame_size <= 0)
+        return E_FILE_FORMAT_INVALID;
+
+      f.len = static_cast<long>(frame_size);
+    }
+
+    pf = m_frames;
+    while (pf != pf_end) {
+      Frame& f = *pf++;
+      assert((pos + f.len) <= stop);
+
+      if ((pos + f.len) > stop)
+        return E_FILE_FORMAT_INVALID;
+
+      f.pos = pos;
+      pos += f.len;
+    }
+
+    assert(pos == stop);
+    if (pos != stop)
+      return E_FILE_FORMAT_INVALID;
+
+  } else if (lacing == 2) {  // fixed-size lacing
+    if (pos >= stop)
+      return E_FILE_FORMAT_INVALID;
+
+    const long long total_size = stop - pos;
+
+    if ((total_size % m_frame_count) != 0)
+      return E_FILE_FORMAT_INVALID;
+
+    const long long frame_size = total_size / m_frame_count;
+
+    if (frame_size > LONG_MAX || frame_size <= 0)
+      return E_FILE_FORMAT_INVALID;
+
+    Frame* pf = m_frames;
+    Frame* const pf_end = pf + m_frame_count;
+
+    while (pf != pf_end) {
+      assert((pos + frame_size) <= stop);
+      if ((pos + frame_size) > stop)
+        return E_FILE_FORMAT_INVALID;
+
+      Frame& f = *pf++;
+
+      f.pos = pos;
+      f.len = static_cast<long>(frame_size);
+
+      pos += frame_size;
+    }
+
+    assert(pos == stop);
+    if (pos != stop)
+      return E_FILE_FORMAT_INVALID;
+
+  } else {
+    assert(lacing == 3);  // EBML lacing
+
+    if (pos >= stop)
+      return E_FILE_FORMAT_INVALID;
+
+    long long size = 0;
+    int frame_count = m_frame_count;
+
+    long long frame_size = ReadUInt(pReader, pos, len);
+
+    if (frame_size <= 0)
+      return E_FILE_FORMAT_INVALID;
+
+    if (frame_size > LONG_MAX)
+      return E_FILE_FORMAT_INVALID;
+
+    if ((pos + len) > stop)
+      return E_FILE_FORMAT_INVALID;
+
+    pos += len;  // consume length of size of first frame
+
+    if ((pos + frame_size) > stop)
+      return E_FILE_FORMAT_INVALID;
+
+    Frame* pf = m_frames;
+    Frame* const pf_end = pf + m_frame_count;
+
+    {
+      Frame& curr = *pf;
+
+      curr.pos = 0;  // patch later
+
+      curr.len = static_cast<long>(frame_size);
+      size += curr.len;  // contribution of this frame
+    }
+
+    --frame_count;
+
+    while (frame_count > 1) {
+      if (pos >= stop)
+        return E_FILE_FORMAT_INVALID;
+
+      assert(pf < pf_end);
+      if (pf >= pf_end)
+        return E_FILE_FORMAT_INVALID;
+
+
+      const Frame& prev = *pf++;
+      assert(prev.len == frame_size);
+      if (prev.len != frame_size)
+        return E_FILE_FORMAT_INVALID;
+
+      assert(pf < pf_end);
+      if (pf >= pf_end)
+        return E_FILE_FORMAT_INVALID;
+
+      Frame& curr = *pf;
+
+      curr.pos = 0;  // patch later
+
+      const long long delta_size_ = ReadUInt(pReader, pos, len);
+
+      if (delta_size_ < 0)
+        return E_FILE_FORMAT_INVALID;
+
+      if ((pos + len) > stop)
+        return E_FILE_FORMAT_INVALID;
+
+      pos += len;  // consume length of (delta) size
+      if (pos > stop)
+        return E_FILE_FORMAT_INVALID;
+
+      const int exp = 7 * len - 1;
+      const long long bias = (1LL << exp) - 1LL;
+      const long long delta_size = delta_size_ - bias;
+
+      frame_size += delta_size;
+
+      if (frame_size <= 0)
+        return E_FILE_FORMAT_INVALID;
+
+      if (frame_size > LONG_MAX)
+        return E_FILE_FORMAT_INVALID;
+
+      curr.len = static_cast<long>(frame_size);
+      size += curr.len;  // contribution of this frame
+
+      --frame_count;
+    }
+
+    // parse last frame
+    if (frame_count > 0) {
+      if (pos > stop || pf >= pf_end)
+        return E_FILE_FORMAT_INVALID;
+
+      const Frame& prev = *pf++;
+      assert(prev.len == frame_size);
+      if (prev.len != frame_size)
+        return E_FILE_FORMAT_INVALID;
+
+      if (pf >= pf_end)
+        return E_FILE_FORMAT_INVALID;
+
+      Frame& curr = *pf++;
+      if (pf != pf_end)
+        return E_FILE_FORMAT_INVALID;
+
+      curr.pos = 0;  // patch later
+
+      const long long total_size = stop - pos;
+
+      if (total_size < size)
+        return E_FILE_FORMAT_INVALID;
+
+      frame_size = total_size - size;
+
+      if (frame_size > LONG_MAX || frame_size <= 0)
+        return E_FILE_FORMAT_INVALID;
+
+      curr.len = static_cast<long>(frame_size);
+    }
+
+    pf = m_frames;
+    while (pf != pf_end) {
+      Frame& f = *pf++;
+      assert((pos + f.len) <= stop);
+      if ((pos + f.len) > stop)
+        return E_FILE_FORMAT_INVALID;
+
+      f.pos = pos;
+      pos += f.len;
+    }
+
+    if (pos != stop)
+      return E_FILE_FORMAT_INVALID;
+  }
+
+  return 0;  // success
+}
+
+long long Block::GetTimeCode(const Cluster* pCluster) const {
+  if (pCluster == 0)
+    return m_timecode;
+
+  const long long tc0 = pCluster->GetTimeCode();
+  assert(tc0 >= 0);
+
+  const long long tc = tc0 + m_timecode;
+
+  return tc;  // unscaled timecode units
+}
+
+long long Block::GetTime(const Cluster* pCluster) const {
+  assert(pCluster);
+
+  const long long tc = GetTimeCode(pCluster);
+
+  const Segment* const pSegment = pCluster->m_pSegment;
+  const SegmentInfo* const pInfo = pSegment->GetInfo();
+  assert(pInfo);
+
+  const long long scale = pInfo->GetTimeCodeScale();
+  assert(scale >= 1);
+
+  const long long ns = tc * scale;
+
+  return ns;
+}
+
+long long Block::GetTrackNumber() const { return m_track; }
+
+bool Block::IsKey() const {
+  return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
+}
+
+void Block::SetKey(bool bKey) {
+  if (bKey)
+    m_flags |= static_cast<unsigned char>(1 << 7);
+  else
+    m_flags &= 0x7F;
+}
+
+bool Block::IsInvisible() const { return bool(int(m_flags & 0x08) != 0); }
+
+Block::Lacing Block::GetLacing() const {
+  const int value = int(m_flags & 0x06) >> 1;
+  return static_cast<Lacing>(value);
+}
+
+int Block::GetFrameCount() const { return m_frame_count; }
+
+const Block::Frame& Block::GetFrame(int idx) const {
+  assert(idx >= 0);
+  assert(idx < m_frame_count);
+
+  const Frame& f = m_frames[idx];
+  assert(f.pos > 0);
+  assert(f.len > 0);
+
+  return f;
+}
+
+long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const {
+  assert(pReader);
+  assert(buf);
+
+  const long status = pReader->Read(pos, len, buf);
+  return status;
+}
+
+long long Block::GetDiscardPadding() const { return m_discard_padding; }
+
+}  // end namespace mkvparser
diff --git a/libwebm/mkvparser.hpp b/libwebm/mkvparser.hpp
index 7e4abec..75ef69d 100644
--- a/libwebm/mkvparser.hpp
+++ b/libwebm/mkvparser.hpp
@@ -1,895 +1,1025 @@
-// 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

+// 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