Pulled the latest version of libwebm...

Change-Id: Ie47f3788693405c181ccad7c92aa5c5c2233df48
diff --git a/mkvparser/mkvparser.cpp b/mkvparser/mkvparser.cpp
index b43b038..9f83c8f 100644
--- a/mkvparser/mkvparser.cpp
+++ b/mkvparser/mkvparser.cpp
@@ -10,98 +10,81 @@
 #include <cassert>

 #include <cstring>

 #include <new>

-//#include <windows.h>

-//#include "odbgstream.hpp"

-//using std::endl;

+#include <climits>

 

 mkvparser::IMkvReader::~IMkvReader()

 {

 }

 

-

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

 {

     major = 1;

     minor = 0;

     build = 0;

-    revision = 4;

+    revision = 20;

 }

 

-

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

 {

     assert(pReader);

     assert(pos >= 0);

 

-    long long total, available;

+    int status;

 

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

-    assert(hr >= 0);

-    assert(pos < available);

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

+//#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;

 

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

-    if (hr < 0)

-        return hr;

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

 

-    assert(hr == 0L);

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

+        return status;

 

-    if (b & 0x80)       //1000 0000

+    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))

     {

-        len = 1;

-        b &= 0x7F;      //0111 1111

-    }

-    else if (b & 0x40)  //0100 0000

-    {

-        len = 2;

-        b &= 0x3F;      //0011 1111

-    }

-    else if (b & 0x20)  //0010 0000

-    {

-        len = 3;

-        b &= 0x1F;      //0001 1111

-    }

-    else if (b & 0x10)  //0001 0000

-    {

-        len = 4;

-        b &= 0x0F;      //0000 1111

-    }

-    else if (b & 0x08)  //0000 1000

-    {

-        len = 5;

-        b &= 0x07;      //0000 0111

-    }

-    else if (b & 0x04)  //0000 0100

-    {

-        len = 6;

-        b &= 0x03;      //0000 0011

-    }

-    else if (b & 0x02)  //0000 0010

-    {

-        len = 7;

-        b &= 0x01;      //0000 0001

-    }

-    else

-    {

-        assert(b & 0x01);  //0000 0001

-        len = 8;

-        b = 0;             //0000 0000

+        m >>= 1;

+        ++len;

     }

 

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

+//#ifdef _DEBUG

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

+//#endif

 

-    long long result = b;

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

     ++pos;

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

+

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

     {

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

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

 

-        if (hr < 0)

-            return hr;

+        if (status < 0)

+        {

+            len = 1;

+            return status;

+        }

 

-        assert(hr == 0L);

+        if (status > 0)

+        {

+            len = 1;

+            return E_BUFFER_NOT_FULL;

+        }

 

         result <<= 8;

         result |= b;

@@ -112,7 +95,6 @@
     return result;

 }

 

-

 long long mkvparser::GetUIntLength(

     IMkvReader* pReader,

     long long pos,

@@ -123,27 +105,28 @@
 

     long long total, available;

 

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

-    assert(hr >= 0);

-    assert(available <= total);

+    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;

 

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

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

 

-    if (hr < 0)

-        return hr;

+    if (status < 0)

+        return status;

 

-    assert(hr == 0L);

+    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;

-    len = 1;

 

     while (!(b & m))

     {

@@ -154,7 +137,6 @@
     return 0;  //success

 }

 

-

 long long mkvparser::SyncReadUInt(

     IMkvReader* pReader,

     long long pos,

@@ -230,10 +212,11 @@
     {

         unsigned char b;

 

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

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

 

-        if (hr < 0)

-            return hr;

+        if (status < 0)

+            return status;

+

         result <<= 8;

         result |= b;

 

@@ -251,13 +234,18 @@
     assert(pReader);

     assert(pos >= 0);

 

-    long long total, available;

+#ifdef _DEBUG

+    {

+        long long total, available;

 

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

-    assert(hr >= 0);

-    assert(available <= total);

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

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

+        assert(status >= 0);

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

+        assert((pos + 4) <= available);

+    }

+#endif

 

+#if 0

     float result;

 

     unsigned char* const p = (unsigned char*)&result;

@@ -273,6 +261,32 @@
 

         ++pos;

     }

+#else

+    union

+    {

+        float result;

+        unsigned long buf;

+    };

+

+    buf = 0;

+

+    for (int i = 0;;)

+    {

+        unsigned char b;

+

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

+

+        if (status < 0)  //error

+            return static_cast<float>(status);

+

+        buf |= b;

+

+        if (++i >= 4)

+            break;

+

+        buf <<= 8;

+    }

+#endif

 

     return result;

 }

@@ -285,6 +299,7 @@
     assert(pReader);

     assert(pos >= 0);

 

+#if 0

     double result;

 

     unsigned char* const p = (unsigned char*)&result;

@@ -300,6 +315,32 @@
 

         ++pos;

     }

+#else

+    union

+    {

+        double result;

+        long long buf;

+    };

+

+    buf = 0;

+

+    for (int i = 0;;)

+    {

+        unsigned char b;

+

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

+

+        if (status < 0)  //error

+            return static_cast<double>(status);

+

+        buf |= b;

+

+        if (++i >= 8)

+            break;

+

+        buf <<= 8;

+    }

+#endif

 

     return result;

 }

@@ -311,17 +352,22 @@
     assert(pReader);

     assert(pos >= 0);

 

-    long long total, available;

+#ifdef _DEBUG

+    {

+        long long total, available;

 

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

-    assert(hr == 0);

-    assert(available <= total);

-    assert(pos < available);

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

+        assert(status == 0);

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

+        assert(pos < available);

+    }

+#endif

 

     signed char result;

+    unsigned char& b = reinterpret_cast<unsigned char&>(result);

 

-    hr = pReader->Read(pos, 1, (unsigned char*)&result);

-    assert(hr == 0);

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

+    assert(status == 0);  //TODO: must be handled somehow

 

     return result;

 }

@@ -333,13 +379,18 @@
     assert(pReader);

     assert(pos >= 0);

 

-    long long total, available;

+#ifdef _DEBUG

+    {

+        long long total, available;

 

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

-    assert(hr >= 0);

-    assert(available <= total);

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

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

+        assert(status >= 0);

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

+        assert((pos + 2) <= available);

+    }

+#endif

 

+#if 0

     short result;

 

     unsigned char* const p = (unsigned char*)&result;

@@ -355,6 +406,24 @@
 

         ++pos;

     }

+#else

+    short result = 0;

+

+    for (int i = 0;;)

+    {

+        unsigned char b;

+

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

+        assert(status == 0);  //TODO: must be handled somehow

+

+        result |= b;

+

+        if (++i >= 2)

+            break;

+

+        result <<= 8;

+    }

+#endif

 

     return result;

 }

@@ -365,16 +434,15 @@
     long long& pos,

     unsigned long id_,

     long long& val)

-

 {

     assert(pReader);

     assert(pos >= 0);

 

     long long total, available;

 

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

-    assert(hr >= 0);

-    assert(available <= total);

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

+    assert(status >= 0);

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

 

     long len;

 

@@ -417,9 +485,9 @@
 

     long long total, available;

 

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

-    assert(hr >= 0);

-    assert(available <= total);

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

+    assert(status >= 0);

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

 

     long len;

 

@@ -450,14 +518,13 @@
     {

         char c;

 

-        hr = pReader->Read(pos + i, 1, (unsigned char*)&c);

-        assert(hr == 0L);

+        status = pReader->Read(pos + i, 1, (unsigned char*)&c);

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

 

         val[i] = c;

 

         if (c == '\0')

             break;

-

     }

 

     val[size] = '\0';

@@ -478,9 +545,9 @@
 

     long long total, available;

 

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

-    assert(hr >= 0);

-    assert(available <= total);

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

+    assert(status >= 0);

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

 

     long len;

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

@@ -508,8 +575,8 @@
     buf = new (std::nothrow) unsigned char[buflen_];

     assert(buf);  //TODO

 

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

-    assert(hr == 0L);

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

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

 

     buflen = buflen_;

 

@@ -529,10 +596,12 @@
 

     long long total, available;

 

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

-    assert(hr >= 0);

-    assert(available <= total);

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

+    assert(status >= 0);

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

+

     long idlen;

+

     const long long id = ReadUInt(pReader, pos, idlen);

     assert(id >= 0);  //TODO

 

@@ -579,9 +648,9 @@
 

     long long total, available;

 

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

-    assert(hr >= 0);

-    assert(available <= total);

+    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);

@@ -616,9 +685,10 @@
 namespace mkvparser

 {

 

-EBMLHeader::EBMLHeader():

+EBMLHeader::EBMLHeader() :

     m_docType(NULL)

 {

+    Init();

 }

 

 EBMLHeader::~EBMLHeader()

@@ -626,6 +696,23 @@
     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)

@@ -634,13 +721,13 @@
 

     long long total, available;

 

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

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

 

-    if (hr < 0)

-        return hr;

+    if (status < 0)  //error

+        return status;

 

     pos = 0;

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

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

 

     for (;;)

     {

@@ -648,10 +735,10 @@
 

         while (pos < end)

         {

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

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

 

-            if (hr < 0)

-                return hr;

+            if (status < 0)  //error

+                return status;

 

             if (b == 0x1A)

                 break;

@@ -661,15 +748,16 @@
 

         if (b != 0x1A)

         {

-            if ((pos >= 1024) ||

-                (available >= total) ||

-                ((total - available) < 5))

-                  return -1;

+            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 - pos) < 5)

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

             return E_FILE_FORMAT_INVALID;

 

         if ((available - pos) < 5)

@@ -682,16 +770,19 @@
         if (result < 0)  //error

             return result;

 

-        if (result == 0x0A45DFA3)  //ReadId masks-off length indicator bits

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

         {

-            assert(len == 4);

-            pos += len;

+            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);

 

@@ -704,19 +795,24 @@
     assert(len > 0);

     assert(len <= 8);

 

-    if ((total -  pos) < len)

+    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 u-int

+    pos += len;  //consume size field

 

-    if ((total - pos) < result)

+    //pos now designates start of payload

+

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

         return E_FILE_FORMAT_INVALID;

 

     if ((available - pos) < result)

@@ -724,12 +820,7 @@
 

     end = pos + result;

 

-    m_version = 1;

-    m_readVersion = 1;

-    m_maxIdLength = 4;

-    m_maxSizeLength = 8;

-    m_docTypeVersion = 1;

-    m_docTypeReadVersion = 1;

+    Init();

 

     while (pos < end)

     {

@@ -768,7 +859,6 @@
     }

 

     assert(pos == end);

-

     return 0;

 }

 

@@ -781,6 +871,8 @@
     m_start(start),

     m_size(size),

     m_pos(start),

+    m_pUnknownSize(0),

+    m_pSeekHead(NULL),

     m_pInfo(NULL),

     m_pTracks(NULL),

     m_pCues(NULL),

@@ -812,6 +904,7 @@
     delete m_pTracks;

     delete m_pInfo;

     delete m_pCues;

+    delete m_pSeekHead;

 }

 

 

@@ -827,9 +920,19 @@
 

     long long total, available;

 

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

-    assert(hr >= 0);

-    assert(available <= total);

+    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)

@@ -838,8 +941,17 @@
     //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 < total)

+    while (pos < end)

     {

         //Read ID

 

@@ -849,14 +961,12 @@
         if (result)  //error, or too few available bytes

             return result;

 

-        if ((pos + len) > total)

+        if ((pos + len) > end)

             return E_FILE_FORMAT_INVALID;

 

         if ((pos + len) > available)

             return pos + len;

 

-        //TODO: if we liberalize the behavior of ReadUInt, we can

-        //probably eliminate having to use GetUIntLength here.

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

 

         if (id < 0)  //error

@@ -871,43 +981,59 @@
         if (result)  //error, or too few available bytes

             return result;

 

-        if ((pos + len) > total)

+        if ((pos + len) > end)

             return E_FILE_FORMAT_INVALID;

 

         if ((pos + len) > available)

             return pos + len;

 

-        //TODO: if we liberalize the behavior of ReadUInt, we can

-        //probably eliminate having to use GetUIntLength here.

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

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

 

-        if (size < 0)

+        if (size < 0)  //error

             return size;

 

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

 

         //Pos now points to start of payload

 

-        if ((pos + size) > total)

-            return E_FILE_FORMAT_INVALID;

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

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

 

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

         {

-            pSegment = new  Segment(pReader, pos, size);

-            assert(pSegment);  //TODO

+            if (size == unknown_size)

+                size = -1;

+

+            else if (total < 0)

+                size = -1;

+

+#if 0  //this turned out to be too conservative:

+            else if ((pos + size) > end)

+                return E_FILE_FORMAT_INVALID;

+#else  //so do this instead

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

+                size = -1;

+#endif

+

+            pSegment = new (std::nothrow) Segment(pReader, 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

     }

 

-    assert(pos == total);

-

-    pSegment = new Segment(pReader, pos, 0);

-    assert(pSegment);  //TODO

-

-    return 0;  //success (sort of)

+    return E_FILE_FORMAT_INVALID;  //there is no segment

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

 }

 

 

@@ -918,27 +1044,38 @@
     //inner (level 1) elements.

     long long total, available;

 

-    long hr = m_pReader->Length(&total, &available);

-    assert(hr >= 0);

-    assert(available <= total);

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

+    assert(status == 0);

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

 

-    const long long stop = m_start + m_size;

-    assert(stop <= total);

-    assert(m_pos <= stop);

+    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));

 

-    bool bQuit = false;

-

-    while ((m_pos < stop) && !bQuit)

+    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)  //error, or too few available bytes

+        if (result < 0)  //error

             return result;

 

-        if ((pos + len) > stop)

+        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)

@@ -950,15 +1087,24 @@
         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)  //error, or too few available bytes

+        if (result < 0)  //error

             return result;

 

-        if ((pos + len) > stop)

+        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)

@@ -966,14 +1112,16 @@
 

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

 

-        if (size < 0)

+        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 ((pos + size) > stop)

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

             return E_FILE_FORMAT_INVALID;

 

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

@@ -985,38 +1133,59 @@
         {

             assert(m_pInfo == NULL);

 

-            m_pInfo = new SegmentInfo(this, pos, size);

+            m_pInfo = new SegmentInfo(this,

+                                      pos,

+                                      size,

+                                      element_start,

+                                      element_size);

             assert(m_pInfo);  //TODO

         }

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

         {

             assert(m_pTracks == NULL);

 

-            m_pTracks = new Tracks(this, pos, size);

+            m_pTracks = new Tracks(this,

+                                   pos,

+                                   size,

+                                   element_start,

+                                   element_size);

             assert(m_pTracks);  //TODO

         }

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

         {

             if (m_pCues == NULL)

             {

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

+                m_pCues = new Cues(this,

+                                   pos,

+                                   size,

+                                   element_start,

+                                   element_size);

                 assert(m_pCues);  //TODO

             }

         }

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

         {

-            ParseSeekHead(pos, size);

-        }

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

-        {

-            bQuit = true;

+#if 0

+            if (available >= total)

+                ParseSeekHead(pos, size);

+#else

+            if (m_pSeekHead == NULL)

+            {

+                m_pSeekHead = new SeekHead(this,

+                                           pos,

+                                           size,

+                                           element_start,

+                                           element_size);

+

+                assert(m_pSeekHead);  //TODO

+            }

+#endif

         }

 

-        if (!bQuit)

-            m_pos = pos + size;  //consume payload

+        m_pos = pos + size;  //consume payload

     }

 

-    assert(m_pos <= stop);

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

 

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

         return E_FILE_FORMAT_INVALID;

@@ -1029,16 +1198,102 @@
 

 

 #if 0

-long Segment::ParseCluster(Cluster*& pCluster, long long& pos_) const

+long Segment::FindNextCluster(long long& pos, size& len) const

 {

-    pCluster = NULL;

-    pos_ = -1;

+    //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);

+    assert(status == 0);

+    assert(total >= 0);

+    assert(available <= total);

+

+    const long long stop = m_start + m_size;

+    assert(stop <= total);

+    assert(m_pos <= stop);

+

+    pos = m_pos;

+

+    while (pos < stop)

+    {

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

+

+        if (result < 0)

+            return static_cast<long>(result);

+

+        if (result > 0)

+            return E_BUFFER_NOT_FULL;

+

+        if ((pos + len) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        if ((pos + len) > available)

+            return E_BUFFER_NOT_FULL;

+

+        const long long idpos = pos;

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

+

+        if (id < 0)  //error

+            return static_cast<long>(id);

+

+        pos += len;  //consume ID

+

+        //Read Size

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

+

+        if (result < 0)  //error

+            return static_cast<long>(result);

+

+        if (result > 0)

+            return E_BUFFER_NOT_FULL;

+

+        if ((pos + len) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        if ((pos + len) > available)

+            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 ((pos + size) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        if ((pos + size) > available)

+            return E_BUFFER_NOT_FULL;

+

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

+        {

+            len = static_cast<long>(size);

+            return 0;  //success

+        }

+

+        pos += size;  //consume payload

+    }

+

+    return E_FILE_FORMAT_INVALID;

+}

+#endif

+

+

+#if 0

+long Segment::ParseCluster(long long& off, long long& new_pos) const

+{

+    off = -1;

+    new_pos = -1;

 

     const long long stop = m_start + m_size;

     assert(m_pos <= stop);

 

     long long pos = m_pos;

-    long long off = -1;

 

     while (pos < stop)

     {

@@ -1074,8 +1329,13 @@
 

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

         {

-            off = idpos - m_start;  // >= 0 means we found a cluster

-            break;

+            const long long off_ = idpos - m_start;

+

+            if (Cluster::HasBlockEntries(this, off_))

+            {

+                off = off_;   // >= 0 means we found a cluster

+                break;

+            }

         }

     }

 

@@ -1088,8 +1348,8 @@
 

     if (off < 0)  //we did not found any more clusters

     {

-        pos_ = stop;  //pos_ >= 0 here means EOF (cluster is NULL)

-        return 0;     //TODO: confirm this return value

+        new_pos = stop;  //pos >= 0 here means EOF (cluster is NULL)

+        return 0;        //TODO: confirm this return value

     }

 

     //We found a cluster.  Now read something, to ensure that it is

@@ -1121,7 +1381,7 @@
         const int result = m_pReader->Read(pos - 1, 1, &b);

         assert(result == 0);

 

-        pos_ = stop;

+        new_pos = stop;

     }

     else

     {

@@ -1144,33 +1404,29 @@
         if (size < 0)  //error

             return static_cast<long>(size);

 

-        pos_ = idpos;

+        new_pos = idpos;

     }

 

-    //We found a cluster, and it has been completely loaded into the

-    //network cache.  (We can guarantee this because we actually read

-    //the EBML tag that follows the cluster, or, if we reached EOF,

-    //because we actually read the last byte of the cluster).

-

-    Segment* const this_ = const_cast<Segment*>(this);

-

-    pCluster = Cluster::Parse(this_, m_clusterCount, off);

-    assert(pCluster);

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

-

     return 0;

 }

 

 

-bool Segment::AddCluster(Cluster* pCluster, long long pos)

+bool Segment::AddCluster(long long off, long long pos)

 {

     assert(pos >= m_start);

 

     const long long stop = m_start + m_size;

     assert(pos <= stop);

 

-    if (pCluster)

+    if (off >= 0)

     {

+        Cluster* const pCluster = Cluster::Parse(this,

+                                                 m_clusterCount,

+                                                 off,

+                                                 0,

+                                                 0);

+        assert(pCluster);

+

         AppendCluster(pCluster);

         assert(m_clusters);

         assert(m_clusterSize > pCluster->m_index);

@@ -1178,47 +1434,108 @@
     }

 

     m_pos = pos;  //m_pos >= stop is now we know we have all clusters

-

     return (pos >= stop);

 }

 #endif

 

 

-long Segment::LoadCluster()

+long Segment::LoadCluster(

+    long long& pos,

+    long& len)

 {

-    const long long stop = m_start + m_size;

-

-    while (m_pos < stop)

+    for (;;)

     {

-        long long pos = m_pos;

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

 

-        long 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 ((pos + len) > stop)

+        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

+        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 ((pos + len) > stop)

+        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

@@ -1226,22 +1543,49 @@
 

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

 

+        //pos now points to start of payload

+

         if (size == 0)  //weird

         {

             m_pos = pos;

             continue;

         }

 

-        //Pos now points to start of payload

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

 

-        if ((pos + size) > stop)

+#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)

             {

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

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

+

+                m_pCues = new Cues(this,

+                                   pos,

+                                   size,

+                                   idpos,

+                                   element_size);

                 assert(m_pCues);  //TODO

             }

 

@@ -1251,53 +1595,490 @@
 

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

         {

+            if (size == unknown_size)

+                return E_FILE_FORMAT_INVALID;  //TODO: liberalize

+

             m_pos = pos + size;  //consume payload

             continue;

         }

 

-        const long idx = m_clusterCount;

-        const long long idoff = idpos - m_start;

+        //We have a cluster.

 

-        if (m_clusterPreloadCount > 0)

-        {

-            assert(idx < m_clusterSize);

+        cluster_off = idpos - m_start;  //relative pos

 

-            Cluster* const pCluster = m_clusters[idx];

-            assert(pCluster);

-            assert(pCluster->m_index < 0);

+        if (size != unknown_size)

+            cluster_size = size;

 

-            const long long off_ = pCluster->m_pos;

-            assert(off_);

-

-            const long long off = off_ * ((off_ >= 0) ? 1 : -1);

-            assert(idoff <= off);

-

-            if (idoff == off)  //cluster has been preloaded already

-            {

-                pCluster->m_index = idx;

-                ++m_clusterCount;

-                --m_clusterPreloadCount;

-

-                m_pos = pos + size;  //consume payload

-                break;

-            }

-        }

-

-        Cluster* const pCluster = Cluster::Parse(this, idx, idoff);

-        assert(pCluster);

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

-

-        AppendCluster(pCluster);

-        assert(m_clusters);

-        assert(idx < m_clusterSize);

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

-

-        m_pos = pos + size;  //consume payload

         break;

     }

 

-    assert(m_pos <= stop);

-    return 0;

+    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

 }

 

 

@@ -1453,27 +2234,35 @@
     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 stop = m_start + m_size;

 

-#ifdef _DEBUG  //TODO: this is really Microsoft-specific

-    {

-        long long total, available;

+    long long total, avail;

 

-        long hr = m_pReader->Length(&total, &available);

-        assert(hr >= 0);

-        assert(available >= total);

-        assert(stop <= total);

-    }

-#endif

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

 

-    while (m_pos < stop)

+    if (status < 0)  //error

+        return status;

+

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

+

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

+

+    for (;;)

     {

         long long pos = m_pos;

 

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

+            break;

+

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

+            break;

+

+        const long long element_start = pos;

+

         long len;

 

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

@@ -1481,7 +2270,7 @@
         if (result < 0)  //error

             return static_cast<long>(result);

 

-        if ((pos + len) > stop)

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

             return E_FILE_FORMAT_INVALID;

 

         const long long idpos = pos;

@@ -1498,7 +2287,7 @@
         if (result < 0)  //error

             return static_cast<long>(result);

 

-        if ((pos + len) > stop)

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

             return E_FILE_FORMAT_INVALID;

 

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

@@ -1506,11 +2295,18 @@
         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 length of size of element

 

         //Pos now points to start of payload

 

-        if ((pos + size) > stop)

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

+

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

             return E_FILE_FORMAT_INVALID;

 

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

@@ -1518,42 +2314,61 @@
             const long idx = m_clusterCount;

             const long long off = idpos - m_start;

 

-            Cluster* const pCluster = Cluster::Parse(this, idx, off);

-            assert(pCluster);

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

+            long long pos_;

+            long len_;

 

-            AppendCluster(pCluster);

-            assert(m_clusters);

-            assert(m_clusterSize > idx);

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

+            status = Cluster::HasBlockEntries(this, off, pos_, len_);

+

+            if (status < 0)  //weird: error or underflow

+                return status;

+

+            if (status > 0)  //have block entries

+            {

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

+                                                         idx,

+                                                         off);

+                                                         //element_size);

+                assert(pCluster);

+

+                AppendCluster(pCluster);

+                assert(m_clusters);

+                assert(m_clusterSize > idx);

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

+            }

         }

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

         {

             assert(m_pCues == NULL);

 

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

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

             assert(m_pCues);  //TODO

         }

         else if (id == 0x0549A966)  //SegmentInfo ID

         {

             assert(m_pInfo == NULL);

 

-            m_pInfo = new  SegmentInfo(this, pos, size);

+            m_pInfo = new SegmentInfo(this,

+                                      pos,

+                                      size,

+                                      element_start,

+                                      element_size);

             assert(m_pInfo);

         }

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

         {

             assert(m_pTracks == NULL);

 

-            m_pTracks = new Tracks(this, pos, size);

+            m_pTracks = new Tracks(this,

+                                   pos,

+                                   size,

+                                   element_start,

+                                   element_size);

             assert(m_pTracks);  //TODO

         }

 

         m_pos = pos + size;  //consume payload

     }

 

-    assert(m_pos >= stop);

-

     if (m_pInfo == NULL)

         return E_FILE_FORMAT_INVALID;  //TODO: ignore this case?

 

@@ -1563,14 +2378,11 @@
     if (m_clusters == NULL)  //TODO: ignore this case?

         return E_FILE_FORMAT_INVALID;

 

-    //TODO: decide whether we require Cues element

-    //if (m_pCues == NULL)

-    //   return E_FILE_FORMAT_INVALID;

-

     return 0;

 }

 

 

+#if 0

 void Segment::ParseSeekHead(long long start, long long size_)

 {

     long long pos = start;

@@ -1602,8 +2414,124 @@
 

     assert(pos == stop);

 }

+#else

+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_count(0)

+{

+    long long pos = start;

+    const long long stop = start + size_;

+

+    IMkvReader* const pReader = m_pSegment->m_pReader;

+

+    //first count the seek head entries

+

+    int count = 0;

+

+    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 == 0x0DBB)  //SeekEntry ID

+            ++count;

+

+        pos += size;  //consume payload

+        assert(pos <= stop);

+    }

+

+    assert(pos == stop);

+

+    if (count <= 0)

+        return;  //nothing else for us to do

+

+    m_entries = new (std::nothrow) Entry[count];

+    assert(m_entries);  //TODO

+

+    //now parse the entries

+

+    Entry* pEntry = m_entries;

+    pos = start;

+

+    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 == 0x0DBB)  //SeekEntry ID

+            ParseEntry(pReader, pos, size, pEntry);

+

+        pos += size;  //consume payload

+        assert(pos <= stop);

+    }

+

+    assert(pos == stop);

+

+    const ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);

+    assert(count_ >= 0);

+    assert(count_ <= count);

+

+    m_count = static_cast<int>(count_);

+}

+

+SeekHead::~SeekHead()

+{

+    delete[] m_entries;

+}

+

+int SeekHead::GetCount() const

+{

+    return m_count;

+}

+

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

+{

+    if (idx < 0)

+        return 0;

+

+    if (idx >= m_count)

+        return 0;

+

+    return m_entries + idx;

+}

+#endif

 

 

+#if 0

 void Segment::ParseCues(long long off)

 {

     if (m_pCues)

@@ -1613,6 +2541,7 @@
     //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;

@@ -1641,18 +2570,147 @@
     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);

+    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 start,

+    long long size_)

 {

     long long pos = start;

 

@@ -1707,23 +2765,119 @@
     if (seekId == 0x0C53BB6B)  //Cues ID

         ParseCues(seekOff);

 }

+#else

+void SeekHead::ParseEntry(

+    IMkvReader* pReader,

+    long long start,

+    long long size_,

+    Entry*& pEntry)

+{

+    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;

+

+    if ((pos + len) > stop)

+        return;

+

+    pos += len;  //consume SeekID id

+

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

+

+    if (seekIdSize <= 0)

+        return;

+

+    if ((pos + len) > stop)

+        return;

+

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

+

+    if ((pos + seekIdSize) > stop)

+        return;

+

+    //TODO: it's not clear whether this is correct

+    //It seems as if the payload here is "binary" which

+    //means the value of the ID should be unserialized,

+    //not parsed as an uint.

+    //

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

+

+    if (pEntry->id <= 0)

+        return;

+

+    if (len != seekIdSize)

+        return;

+

+    pos += seekIdSize;  //consume SeekID payload

+

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

+

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

+        return;

+

+    if ((pos + len) > stop)

+        return;

+

+    pos += len;  //consume id

+

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

+

+    if (seekPosSize <= 0)

+        return;

+

+    if ((pos + len) > stop)

+        return;

+

+    pos += len;  //consume size

+

+    if ((pos + seekPosSize) > stop)

+        return;

+

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

+

+    if (pEntry->pos < 0)

+        return;

+

+    pos += seekPosSize;  //consume payload

+

+    if (pos != stop)

+        return;

+

+    ++pEntry;  //success

+}

+#endif

 

 

-Cues::Cues(Segment* pSegment, long long start_, long long size_) :

+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_cue_points(NULL),

     m_count(0),

     m_preload_count(0),

-    m_pos(start_)

+    m_pos(start_),

+    m_element_start(element_start),

+    m_element_size(element_size)

 {

 }

 

 

 Cues::~Cues()

 {

-    const size_t n = m_count + m_preload_count;

+    const long n = m_count + m_preload_count;

 

     CuePoint** p = m_cue_points;

     CuePoint** const q = p + n;

@@ -1740,6 +2894,22 @@
 }

 

 

+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)

@@ -1753,7 +2923,7 @@
     const long long stop = m_start + m_size;

     long long pos = m_start;

 

-    size_t cue_points_size = 0;

+    long cue_points_size = 0;

 

     while (pos < stop)

     {

@@ -1784,17 +2954,17 @@
 

 

 void Cues::PreloadCuePoint(

-    size_t& cue_points_size,

+    long& cue_points_size,

     long long pos) const

 {

     assert(m_count == 0);

 

     if (m_preload_count >= cue_points_size)

     {

-        size_t n;

+        long n;

 

         if (cue_points_size > 0)

-            n = static_cast<size_t>(2 * cue_points_size);

+            n = 2 * cue_points_size;

         else

         {

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

@@ -1810,7 +2980,7 @@
                 else

                 {

                     const long long sec = (ns + 999999999LL) / 1000000000LL;

-                    n = static_cast<size_t>(sec);

+                    n = static_cast<long>(sec);

                 }

             }

         }

@@ -1905,7 +3075,8 @@
     assert(time_ns >= 0);

     assert(pTrack);

 

-    LoadCuePoint();

+#if 0

+    LoadCuePoint();  //establish invariant

 

     assert(m_cue_points);

     assert(m_count > 0);

@@ -1959,6 +3130,59 @@
     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

@@ -2035,12 +3259,20 @@
 

 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);

@@ -2055,6 +3287,13 @@
 

 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;

@@ -2072,6 +3311,16 @@
 

     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;

 }

@@ -2086,6 +3335,7 @@
     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;

@@ -2104,6 +3354,23 @@
     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;

 }

@@ -2148,10 +3415,12 @@
         Cluster* const pCluster = *k;

         assert(pCluster);

 

-        const long long pos_ = pCluster->m_pos;

-        assert(pos_);

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

+        //assert(pos_);

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

 

-        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;

@@ -2162,8 +3431,11 @@
     }

 

     assert(i == j);

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

 

-    Cluster* const pCluster = Cluster::Parse(this, -1, 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);

@@ -2175,8 +3447,71 @@
 }

 

 

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

+{

+    if (requested_pos < 0)

+        return 0;

 

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

+    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),

@@ -2204,6 +3539,7 @@
     assert(m_track_positions_count == 0);

 

     long long pos_ = -m_timecode;

+    const long long element_start = pos_;

 

     long long stop;

 

@@ -2228,6 +3564,8 @@
         stop = pos_ + size;

     }

 

+    const long long element_size = stop - element_start;

+

     long long pos = pos_;

 

     //First count number of track positions

@@ -2301,6 +3639,9 @@
     }

 

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

+

+    m_element_start = element_start;

+    m_element_size = element_size;

 }

 

 

@@ -2348,7 +3689,7 @@
     }

 

     assert(m_pos >= 0);

-    //assert(m_track > 0);

+    assert(m_track > 0);

     //assert(m_block > 0);

 }

 

@@ -2379,7 +3720,7 @@
     return m_timecode;

 }

 

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

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

 {

     assert(pSegment);

     assert(m_timecode >= 0);

@@ -2398,6 +3739,9 @@
 

 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;

@@ -2407,7 +3751,7 @@
 }

 

 

-Cluster* Segment::GetFirst()

+const Cluster* Segment::GetFirst() const

 {

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

        return &m_eos;

@@ -2419,7 +3763,7 @@
 }

 

 

-Cluster* Segment::GetLast()

+const Cluster* Segment::GetLast() const

 {

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

         return &m_eos;

@@ -2439,7 +3783,7 @@
 }

 

 

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

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

 {

     assert(pCurr);

     assert(pCurr != &m_eos);

@@ -2468,10 +3812,13 @@
 

     assert(m_clusterPreloadCount > 0);

 

-    const long long off_ = pCurr->m_pos;

-    const long long off = off_ * ((off_ < 0) ? -1 : 1);

+    //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 = 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

 

     {

@@ -2493,7 +3840,7 @@
 

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

         assert(size > 0);  //TODO

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

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

 

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

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

@@ -2504,6 +3851,8 @@
     }

 

     long long off_next = 0;

+    //long long element_start_next = 0;

+    long long element_size_next = 0;

 

     while (pos < stop)

     {

@@ -2531,6 +3880,8 @@
         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

@@ -2538,8 +3889,26 @@
 

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

         {

-            off_next = idpos - m_start;

-            break;

+            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

@@ -2568,10 +3937,11 @@
         assert(pNext);

         assert(pNext->m_index < 0);

 

-        const long long pos_ = pNext->m_pos;

-        assert(pos_);

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

+        //assert(pos_);

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

 

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

+        pos = pNext->GetPosition();

 

         if (pos < off_next)

             i = k + 1;

@@ -2583,7 +3953,12 @@
 

     assert(i == j);

 

-    Cluster* const pNext = Cluster::Parse(this, -1, off_next);

+    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);

@@ -2595,7 +3970,513 @@
 }

 

 

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

+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;

@@ -2653,9 +4534,10 @@
 }

 

 

+#if 0

 const BlockEntry* Segment::Seek(

     long long time_ns,

-    const Track* pTrack)

+    const Track* pTrack) const

 {

     assert(pTrack);

 

@@ -2716,11 +4598,21 @@
         assert(lo > i);

         assert(lo <= j);

 

-        Cluster* const pCluster = *--lo;

-        assert(pCluster);

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

+        while (lo > i)

+        {

+            Cluster* const pCluster = *--lo;

+            assert(pCluster);

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

 

-        return pCluster->GetEntry(pTrack);

+            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

@@ -2760,23 +4652,10 @@
     assert(pCluster->GetTime() <= time_ns);

 

     {

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

-        assert(pBlockEntry);

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

 

-        if (!pBlockEntry->EOS())  //found a keyframe

-        {

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

-            assert(pBlock);

-

-            //TODO: this isn't necessarily the keyframe we want,

-            //since there might another keyframe on this same

-            //cluster with a greater timecode that but that is

-            //still less than the requested time.  For now we

-            //simply return the first keyframe we find.

-

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

-                return pBlockEntry;

-        }

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

+            return pBE;

     }

 

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

@@ -2788,9 +4667,8 @@
         assert(pCluster->GetTime() <= time_ns);

 

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

-        assert(pBlockEntry);

 

-        if (!pBlockEntry->EOS())

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

             return pBlockEntry;

     }

 

@@ -2799,6 +4677,7 @@
 

     return pTrack->GetEOS();

 }

+#endif

 

 

 #if 0

@@ -2832,7 +4711,7 @@
 #endif

 

 

-Tracks* Segment::GetTracks() const

+const Tracks* Segment::GetTracks() const

 {

     return m_pTracks;

 }

@@ -2850,6 +4729,12 @@
 }

 

 

+const SeekHead* Segment::GetSeekHead() const

+{

+    return m_pSeekHead;

+}

+

+

 long long Segment::GetDuration() const

 {

     assert(m_pInfo);

@@ -2857,13 +4742,20 @@
 }

 

 

-SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_) :

+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_pMuxingAppAsUTF8(NULL),

     m_pWritingAppAsUTF8(NULL),

-    m_pTitleAsUTF8(NULL)

+    m_pTitleAsUTF8(NULL),

+    m_element_start(element_start),

+    m_element_size(element_size)

 {

     IMkvReader* const pReader = m_pSegment->m_pReader;

 

@@ -2970,9 +4862,15 @@
     return m_pTitleAsUTF8;

 }

 

-Track::Track(Segment* pSegment, const Info& i) :

+Track::Track(

+    Segment* pSegment,

+    const Info& i,

+    long long element_start,

+    long long element_size) :

     m_pSegment(pSegment),

-    m_info(i)

+    m_info(i),

+    m_element_start(element_start),

+    m_element_size(element_size)

 {

 }

 

@@ -2985,7 +4883,7 @@
 Track::Info::Info():

     type(-1),

     number(-1),

-    uid(-1),

+    uid(ULLONG_MAX),

     nameAsUTF8(NULL),

     codecId(NULL),

     codecPrivate(NULL),

@@ -2994,7 +4892,6 @@
 {

 }

 

-

 void Track::Info::Clear()

 {

     delete[] nameAsUTF8;

@@ -3027,6 +4924,11 @@
     return m_info.number;

 }

 

+unsigned long long Track::GetUid() const

+{

+    return m_info.uid;

+}

+

 const char* Track::GetNameAsUTF8() const

 {

     return m_info.nameAsUTF8;

@@ -3050,15 +4952,17 @@
 }

 

 

+bool Track::GetLacing() const

+{

+    return m_info.lacing;

+}

+

+

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

 {

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

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

 

-    //If Segment::GetFirst returns NULL, then this must be a network

-    //download, and we haven't loaded any clusters yet.  In this case,

-    //returning NULL from Track::GetFirst means the same thing.

-

-    for (int i = 0; i < 100; ++i)  //arbitrary upper bound

+    for (int i = 0; ; )

     {

         if (pCluster == NULL)

         {

@@ -3080,17 +4984,33 @@
 

         pBlockEntry = pCluster->GetFirst();

 

-        while (pBlockEntry)

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

+        {

+            pCluster = m_pSegment->GetNext(pCluster);

+            continue;

+        }

+

+        for (;;)

         {

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

             assert(pBlock);

 

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

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

+

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

                 return 0;

 

             pBlockEntry = pCluster->GetNext(pBlockEntry);

+

+            if (pBlockEntry == 0)

+                break;

         }

 

+        ++i;

+

+        if (i >= 100)

+            break;

+

         pCluster = m_pSegment->GetNext(pCluster);

     }

 

@@ -3113,13 +5033,13 @@
     const Block* const pCurrBlock = pCurrEntry->GetBlock();

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

 

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

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

     assert(pCluster);

     assert(!pCluster->EOS());

 

     pNextEntry = pCluster->GetNext(pCurrEntry);

 

-    for (int i = 0; i < 100; ++i)  //arbitrary upper bound to search

+    for (int i = 0; ; )

     {

         while (pNextEntry)

         {

@@ -3165,6 +5085,14 @@
         }

 

         pNextEntry = pCluster->GetFirst();

+

+        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

@@ -3187,7 +5115,7 @@
 }

 

 

-Cluster* Track::EOSBlock::GetCluster() const

+const Cluster* Track::EOSBlock::GetCluster() const

 {

     return NULL;

 }

@@ -3211,8 +5139,12 @@
 }

 

 

-VideoTrack::VideoTrack(Segment* pSegment, const Info& i) :

-    Track(pSegment, i),

+VideoTrack::VideoTrack(

+    Segment* pSegment,

+    const Info& i,

+    long long element_start,

+    long long element_size) :

+    Track(pSegment, i, element_start, element_size),

     m_width(-1),

     m_height(-1),

     m_rate(-1)

@@ -3284,6 +5216,106 @@
 }

 

 

+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);

+

+        //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);

+

+        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;

@@ -3302,10 +5334,14 @@
 }

 

 

-AudioTrack::AudioTrack(Segment* pSegment, const Info& i) :

-    Track(pSegment, i),

+AudioTrack::AudioTrack(

+    Segment* pSegment,

+    const Info& i,

+    long long element_start,

+    long long element_size) :

+    Track(pSegment, i, element_start, element_size),

     m_rate(0.0),

-    m_channels(1),

+    m_channels(0),

     m_bitDepth(-1)

 {

     assert(i.type == 2);

@@ -3359,6 +5395,9 @@
         }

     }

 

+    if (m_channels <= 0)

+        m_channels = 1;  //Matroska spec says this is the default

+

     return;

 }

 

@@ -3375,6 +5414,91 @@
 }

 

 

+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;

@@ -3391,12 +5515,19 @@
     return m_bitDepth;

 }

 

-Tracks::Tracks(Segment* pSegment, long long start, long long size_) :

+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_trackEntries(NULL),

-    m_trackEntriesEnd(NULL)

+    m_trackEntriesEnd(NULL),

+    m_element_start(element_start),

+    m_element_size(element_size)

 {

     long long stop = m_start + m_size;

     IMkvReader* const pReader = m_pSegment->m_pReader;

@@ -3442,6 +5573,8 @@
         assert(id >= 0);

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

 

+        const long long element_start = pos;

+

         pos += len;  //consume id

 

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

@@ -3452,8 +5585,16 @@
 

         //pos now desinates start of element

 

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

+

         if (id == 0x2E)  //TrackEntry ID

-            ParseTrackEntry(pos, size1, *m_trackEntriesEnd++);

+        {

+            Track*& pTrack = *m_trackEntriesEnd;

+            ParseTrackEntry(pos, size1, pTrack, element_start, element_size);

+

+            if (pTrack)

+                ++m_trackEntriesEnd;

+        }

 

         pos += size1;  //consume payload

         assert(pos <= stop);

@@ -3473,7 +5614,9 @@
 void Tracks::ParseTrackEntry(

     long long start,

     long long size,

-    Track*& pTrack)

+    Track*& pTrack,

+    long long element_start,

+    long long element_size)

 {

     IMkvReader* const pReader = m_pSegment->m_pReader;

 

@@ -3484,11 +5627,11 @@
 

     Track::Settings videoSettings;

     videoSettings.start = -1;

-    videoSettings.size = 0;

 

     Track::Settings audioSettings;

     audioSettings.start = -1;

-    audioSettings.size = 0;

+

+    long long lacing = 1;  //default is true

 

     while (pos < stop)

     {

@@ -3500,14 +5643,16 @@
 #endif

         if (Match(pReader, pos, 0x57, i.number))

             assert(i.number > 0);

-        else if (Match(pReader, pos, 0x33C5, i.uid))

-            ;

+        //else if (Match(pReader, pos, 0x33C5, i.uid))

+        //    ;

         else if (Match(pReader, pos, 0x03, i.type))

             ;

         else if (Match(pReader, pos, 0x136E, i.nameAsUTF8))

             assert(i.nameAsUTF8);

         else if (Match(pReader, pos, 0x06, i.codecId))

             ;

+        else if (Match(pReader, pos, 0x1C, lacing))

+            assert(lacing <= 1);

         else if (Match(pReader,

                        pos,

                        0x23A2,

@@ -3520,6 +5665,9 @@
         {

             long len;

 

+            const long long idpos = pos;

+            idpos;

+

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

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

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

@@ -3546,6 +5694,27 @@
                 audioSettings.start = start;

                 audioSettings.size = size;

             }

+            else if (id == 0x33C5)  //Track UID

+            {

+                assert(size <= 8);

+

+                i.uid = 0;

+                long long pos_ = start;

+                const long long pos_end = start + size;

+

+                while (pos_ != pos_end)

+                {

+                    unsigned char b;

+

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

+                    assert(status == 0);

+

+                    i.uid <<= 8;

+                    i.uid |= b;

+

+                    ++pos_;

+                }

+            }

         }

     }

 

@@ -3554,6 +5723,8 @@
     //and that it is unique among all tracks.

     assert(i.number > 0);

 

+    i.lacing = (lacing > 0) ? true : false;

+

     //TODO: vet settings, to ensure that video settings (0x60)

     //were specified when type = 1, and that audio settings (0x61)

     //were specified when type = 2.

@@ -3564,7 +5735,11 @@
 

         i.settings = videoSettings;

 

-        VideoTrack* const t = new VideoTrack(m_pSegment, i);

+        VideoTrack* const t = new VideoTrack(

+            m_pSegment,

+            i,

+            element_start,

+            element_size);

         assert(t);  //TODO

         pTrack = t;

     }

@@ -3575,7 +5750,11 @@
 

         i.settings = audioSettings;

 

-        AudioTrack* const t = new  AudioTrack(m_pSegment, i);

+        AudioTrack* const t = new  AudioTrack(

+            m_pSegment,

+            i,

+            element_start,

+            element_size);

         assert(t);  //TODO

         pTrack = t;

     }

@@ -3606,8 +5785,7 @@
     delete[] m_trackEntries;

 }

 

-

-Track* Tracks::GetTrackByNumber(unsigned long tn_) const

+const Track* Tracks::GetTrackByNumber(unsigned long tn_) const

 {

     const long long tn = tn_;

 

@@ -3629,7 +5807,7 @@
 }

 

 

-Track* Tracks::GetTrackByIndex(unsigned long idx) const

+const Track* Tracks::GetTrackByIndex(unsigned long idx) const

 {

     const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;

 

@@ -3639,64 +5817,83 @@
     return m_trackEntries[idx];

 }

 

+#if 0

+long long Cluster::Unparsed() const

+{

+    if (m_timecode < 0)  //not even partially loaded

+        return LLONG_MAX;

 

-void Cluster::Load()

+    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

+

+

+void Cluster::Load() const

 {

     assert(m_pSegment);

-    assert(m_pos);

-    assert(m_size);

+    assert(m_pos >= m_element_start);

+    //assert(m_size);

 

-    if (m_pos > 0)  //loaded

-    {

-        assert(m_size > 0);

-        assert(m_timecode >= 0);

+    if (m_timecode >= 0)  //loaded

         return;

-    }

 

-    assert(m_pos < 0);  //not loaded yet

-    assert(m_size < 0);

-    assert(m_timecode < 0);

+    assert(m_pos == m_element_start);

 

     IMkvReader* const pReader = m_pSegment->m_pReader;

 

-    m_pos *= -1;                                  //relative to segment

-    long long pos = m_pSegment->m_start + m_pos;  //absolute

-

     long len;

 

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

+    const long long id_ = ReadUInt(pReader, m_pos, len);

     assert(id_ >= 0);

     assert(id_ == 0x0F43B675);  //Cluster ID

 

-    pos += len;  //consume id

+    m_pos += len;  //consume id

 

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

-    assert(size_ >= 0);

+    const long long cluster_size = ReadUInt(pReader, m_pos, len);

+    assert(cluster_size >= 0);  //TODO

 

-    pos += len;  //consume size

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

+    unknown_size;

+    assert(cluster_size != unknown_size);  //TODO

 

-    m_size = size_;

-    const long long stop = pos + size_;

+    m_pos += len;  //consume size field

+

+    const long long stop = m_pos + cluster_size;

+

+    const long long element_size = stop - m_element_start;

+    assert((m_element_size <= 0) || (m_element_size == element_size));

+

+    if (m_element_size <= 0)

+        m_element_size = element_size;

 

     long long timecode = -1;

 

-    while (pos < stop)

+    while (m_pos < stop)

     {

-        if (Match(pReader, pos, 0x67, timecode))

+        if (Match(pReader, m_pos, 0x67, timecode))

             break;

         else

         {

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

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

             assert(id >= 0);  //TODO

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

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

 

-            pos += len;  //consume id

+            m_pos += len;  //consume id

 

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

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

             assert(size >= 0);  //TODO

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

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

 

-            pos += len;  //consume size

+            m_pos += len;  //consume size

 

             if (id == 0x20)  //BlockGroup ID

                 break;

@@ -3704,28 +5901,845 @@
             if (id == 0x23)  //SimpleBlock ID

                 break;

 

-            pos += size;  //consume payload

-            assert(pos <= stop);

+            m_pos += size;  //consume payload

+            assert(m_pos <= stop);

         }

     }

 

-    assert(pos <= stop);

+    assert(m_pos <= stop);

     assert(timecode >= 0);

 

     m_timecode = timecode;

 }

 

 

-Cluster* Cluster::Parse(

+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;

+        }

+

+        if (id == 0x20)  //BlockGroup

+            return ParseBlockGroup(size, pos, len);

+

+        if (id == 0x23)  //SimpleBlock

+            return 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

+{

+    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;

+

+    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;

+

+    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;

+    }

+

+    CreateBlock(0x23, block_start, block_size);  //simple block id

+    m_pos = block_stop;

+

+    return 0;  //success

+}

+

+

+long Cluster::ParseBlockGroup(

+    long long payload_size,

+    long long& pos,

+    long& len) const

+{

+    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;

+

+        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;

+

+        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);

+

+    CreateBlock(0x20, payload_start, payload_size);  //BlockGroup ID

+    m_pos = payload_stop;

+

+    return 0;  //success

+}

+

+

+long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const

+{

+    assert(m_pos >= m_element_start);

+

+    pEntry = 0;

+

+    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);

-    assert(off < pSegment->m_size);

 

-    Cluster* const pCluster = new Cluster(pSegment, idx, -off);

+    const long long element_start = pSegment->m_start + off;

+

+    Cluster* const pCluster = new Cluster(pSegment,

+                                          idx,

+                                          element_start);

+                                          //element_size);

     assert(pCluster);

 

     return pCluster;

@@ -3736,10 +6750,13 @@
     m_pSegment(NULL),

     m_index(0),

     m_pos(0),

-    m_size(0),

+    //m_size(0),

+    m_element_start(0),

+    m_element_size(0),

     m_timecode(0),

     m_entries(NULL),

-    m_entriesCount(0)

+    m_entries_size(0),

+    m_entries_count(0)  //means "no entries"

 {

 }

 

@@ -3747,22 +6764,29 @@
 Cluster::Cluster(

     Segment* pSegment,

     long idx,

-    long long off) :

+    long long element_start

+    /* long long element_size */ ) :

     m_pSegment(pSegment),

     m_index(idx),

-    m_pos(off),

-    m_size(-1),

+    m_pos(element_start),

+    m_element_start(element_start),

+    m_element_size(-1 /* element_size */ ),

+    //m_size(-1),

     m_timecode(-1),

     m_entries(NULL),

-    m_entriesCount(0)

+    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_entriesCount;

+    BlockEntry** const j = m_entries + m_entries_count;

 

     while (i != j)

     {

@@ -3782,22 +6806,39 @@
 }

 

 

-void Cluster::LoadBlockEntries()

+long Cluster::GetIndex() const

 {

-    if (m_entries)

-        return;

+    return m_index;

+}

 

-    assert(m_pSegment);

-    assert(m_pos);

-    assert(m_size);

-    assert(m_entriesCount == 0);

 

-    IMkvReader* const pReader = m_pSegment->m_pReader;

+long long Cluster::GetPosition() const

+{

+    const long long pos = m_element_start - m_pSegment->m_start;

+    assert(pos >= 0);

 

-    if (m_pos < 0)

-        m_pos *= -1;  //relative to segment

+    return pos;

+}

 

-    long long pos = m_pSegment->m_start + m_pos;  //absolute

+

+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;

@@ -3809,30 +6850,339 @@
 

         pos += len;  //consume id

 

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

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

         assert(size > 0);

 

         pos += len;  //consume size

 

         //pos now points to start of payload

-

-        if (m_size >= 0)

-            assert(size == m_size);

-        else

-            m_size = size;

     }

 

-    const long long stop = pos + m_size;

+    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));

+    }

+}

+

+

+void Cluster::LoadBlockEntries() const

+{

+    //LoadBlockEntries loads all of the entries on the cluster.

+

+    //if (m_entries)

+    //    return;

+

+    //if (m_entries_count == 0)  //already parsed, and no entries found

+    //    return;

+

+    if (m_pSegment == 0)  //EOS cluster

+        return;

+

+    assert(m_pos >= m_element_start);

+    //assert(m_size);  //preloaded only, or (partially) loaded

+    //assert(m_entries_count < 0);

+

+    IMkvReader* const pReader = m_pSegment->m_pReader;

+

+    //if (m_pos < 0)

+    //    m_pos *= -1;  //relative to segment

+    //long long pos = m_pSegment->m_start + m_pos;  //absolute

+

+    if (m_element_size < 0)

+    {

+        assert(m_pos == m_element_start);

+

+        long len;

+

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

+        id;

+        assert(id >= 0);

+        assert(id == 0x0F43B675);  //Cluster ID

+

+        m_pos += len;  //consume id

+

+        const long long cluster_size = ReadUInt(pReader, m_pos, len);

+        assert(cluster_size > 0);

+

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

+        unknown_size;

+        assert(cluster_size != unknown_size);

+

+        m_pos += len;  //consume size field

+

+        //m_pos now points to start of cluster payload

+

+        const long long cluster_stop = m_pos + cluster_size;

+        const long long element_size = cluster_stop - m_element_start;

+        assert((m_element_size <= 0) || (m_element_size == element_size));

+

+        if (m_element_size <= 0)

+            m_element_size = element_size;

+    }

+

+    //assert(m_size > 0);

+    //assert(m_element_size > m_size);

+

+    const long long cluster_stop = m_element_start + m_element_size;

+

+    if (m_pos >= cluster_stop)

+        return;

+

     long long timecode = -1;  //of cluster itself

 

-    //First count the number of entries

+    //First count the number of entries (that remain)

 

-    long long idx = pos;  //points to start of payload

-    m_entriesCount = 0;

+    long long pos = m_pos;

+    int entries_count = 0;  //that remain

 

-    while (idx < stop)

+    while (pos < cluster_stop)

     {

-        if (Match(pReader, idx, 0x67, timecode))

+        if (Match(pReader, pos, 0x67, timecode))

         {

             if (m_timecode >= 0)

                 assert(timecode == m_timecode);

@@ -3843,81 +7193,124 @@
         {

             long len;

 

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

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

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

-

-            idx += len;  //consume id

-

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

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

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

-

-            idx += len;  //consume size

-

-            if (id == 0x20)  //BlockGroup ID

-                ++m_entriesCount;

-            else if (id == 0x23)  //SimpleBlock ID

-                ++m_entriesCount;

-

-            idx += size;  //consume payload

-            assert(idx <= stop);

-        }

-    }

-

-    assert(idx == stop);

-    assert(m_timecode >= 0);

-

-    if (m_entriesCount == 0)  //TODO: handle empty clusters

-        return;

-

-    m_entries = new BlockEntry*[m_entriesCount];

-    size_t index = 0;

-

-    while (pos < stop)

-    {

-        if (Match(pReader, pos, 0x67, timecode))

-            assert(timecode == m_timecode);

-        else

-        {

-            long len;

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

             assert(id >= 0);  //TODO

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

+            assert((pos + len) <= cluster_stop);

 

             pos += len;  //consume id

 

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

             assert(size >= 0);  //TODO

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

+            assert((pos + len) <= cluster_stop);

 

             pos += len;  //consume size

 

             if (id == 0x20)  //BlockGroup ID

-                ParseBlockGroup(pos, size, index++);

+                ++entries_count;

             else if (id == 0x23)  //SimpleBlock ID

-                ParseSimpleBlock(pos, size, index++);

+                ++entries_count;

 

             pos += size;  //consume payload

-            assert(pos <= stop);

+            assert(pos <= cluster_stop);

         }

     }

 

-    assert(pos == stop);

-    assert(timecode >= 0);

-    assert(index == m_entriesCount);

+    assert(pos == cluster_stop);

+    assert(m_timecode >= 0);

+

+    if (entries_count == 0)  //nothing remains to be done

+    {

+        m_pos = pos;

+

+        if (m_entries_count < 0)

+            m_entries_count = 0;

+

+        return;

+    }

+

+    BlockEntry** ppEntry;

+

+    if (m_entries_count < 0)  //haven't parsed anything yet

+    {

+        assert(m_entries == NULL);

+        assert(m_entries_size == 0);

+

+        m_entries_size = entries_count;

+        m_entries = new BlockEntry*[m_entries_size];

+

+        ppEntry = m_entries;

+        m_entries_count = entries_count;

+    }

+    else

+    {

+        assert(m_entries);

+        assert(m_entries_size > 0);

+        assert(m_entries_count > 0);

+        assert(m_entries_count <= m_entries_size);

+

+        const long entries_size = m_entries_count + entries_count;

+

+        if (m_entries_size < 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;

+        }

+

+        ppEntry = m_entries + m_entries_count;

+        m_entries_count = entries_size;

+    }

+

+    while (m_pos < cluster_stop)

+    {

+        long len;

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

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

+        assert((m_pos + len) <= cluster_stop);

+

+        m_pos += len;  //consume id

+

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

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

+        assert((m_pos + len) <= cluster_stop);

+

+        m_pos += len;  //consume size

+

+        if (id == 0x20)  //BlockGroup ID

+            CreateBlockGroup(m_pos, size, ppEntry);

+        else if (id == 0x23)  //SimpleBlock ID

+            CreateSimpleBlock(m_pos, size, ppEntry);

+

+        m_pos += size;  //consume payload

+        assert(m_pos <= cluster_stop);

+    }

+

+    assert(m_pos == cluster_stop);

+    assert((ppEntry - m_entries) == m_entries_count);

 }

 

 

 

-long long Cluster::GetTimeCode()

+long long Cluster::GetTimeCode() const

 {

     Load();

     return m_timecode;

 }

 

 

-long long Cluster::GetTime()

+long long Cluster::GetTime() const

 {

     const long long tc = GetTimeCode();

     assert(tc >= 0);

@@ -3934,7 +7327,7 @@
 }

 

 

-long long Cluster::GetFirstTime()

+long long Cluster::GetFirstTime() const

 {

     const BlockEntry* const pEntry = GetFirst();

 

@@ -3948,7 +7341,7 @@
 }

 

 

-long long Cluster::GetLastTime()

+long long Cluster::GetLastTime() const

 {

     const BlockEntry* const pEntry = GetLast();

 

@@ -3962,42 +7355,110 @@
 }

 

 

-void Cluster::ParseBlockGroup(long long start, long long size, size_t index)

+void Cluster::CreateBlock(

+    long long id,

+    long long pos,   //absolute pos of payload

+    long long size) const

+{

+    BlockEntry** ppEntry;

+

+    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];

+

+        ppEntry = m_entries;

+        m_entries_count = 1;

+    }

+    else

+    {

+        assert(m_entries);

+        assert(m_entries_size > 0);

+        assert(m_entries_count > 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;

+        }

+

+        ppEntry = m_entries + m_entries_count;

+        ++m_entries_count;

+    }

+

+    if (id == 0x20)  //BlockGroup ID

+        CreateBlockGroup(pos, size, ppEntry);

+    else

+    {

+        assert(id == 0x23);  //SimpleBlock ID

+        CreateSimpleBlock(pos, size, ppEntry);

+    }

+}

+

+

+void Cluster::CreateBlockGroup(

+    long long st,

+    long long sz,

+    BlockEntry**& ppEntry) const

 {

     assert(m_entries);

-    assert(m_entriesCount);

-    assert(index < m_entriesCount);

+    assert(m_entries_size > 0);

+    assert(ppEntry);

+    assert(ppEntry >= m_entries);

 

-    BlockGroup* const pGroup =

-        new (std::nothrow) BlockGroup(this, index, start, size);

-    assert(pGroup);  //TODO

+    const ptrdiff_t idx = ppEntry - m_entries;

+    assert(idx >= 0);

+    assert(idx < m_entries_size);

 

-    m_entries[index] = pGroup;

+    Cluster* const this_ = const_cast<Cluster*>(this);

+    *ppEntry++ = new BlockGroup(this_, idx, st, sz);

 }

 

 

 

-void Cluster::ParseSimpleBlock(long long start, long long size, size_t index)

+void Cluster::CreateSimpleBlock(

+    long long st,

+    long long sz,

+    BlockEntry**& ppEntry) const

 {

     assert(m_entries);

-    assert(m_entriesCount);

-    assert(index < m_entriesCount);

+    assert(m_entries_size > 0);

+    assert(ppEntry);

+    assert(ppEntry >= m_entries);

 

-    SimpleBlock* const pSimpleBlock =

-        new (std::nothrow) SimpleBlock(this, index, start, size);

-    assert(pSimpleBlock);  //TODO

+    const ptrdiff_t idx = ppEntry - m_entries;

+    assert(idx >= 0);

+    assert(idx < m_entries_size);

 

-    m_entries[index] = pSimpleBlock;

+    Cluster* const this_ = const_cast<Cluster*>(this);

+    *ppEntry++ = new SimpleBlock(this_, idx, st, sz);

 }

 

 

-const BlockEntry* Cluster::GetFirst()

+const BlockEntry* Cluster::GetFirst() const

 {

     LoadBlockEntries();

-    //assert(m_entries);

-    //assert(m_entriesCount >= 1);

 

-    if ((m_entries == NULL) || (m_entriesCount == 0))

+    if ((m_entries == NULL) || (m_entries_count <= 0))

         return NULL;

 

     const BlockEntry* const pFirst = m_entries[0];

@@ -4007,16 +7468,14 @@
 }

 

 

-const BlockEntry* Cluster::GetLast()

+const BlockEntry* Cluster::GetLast() const

 {

     LoadBlockEntries();

-    //assert(m_entries);

-    //assert(m_entriesCount >= 1);

 

-    if ((m_entries == NULL) || (m_entriesCount == 0))

+    if ((m_entries == NULL) || (m_entries_count <= 0))

         return NULL;

 

-    const size_t idx = m_entriesCount - 1;

+    const long idx = m_entries_count - 1;

 

     const BlockEntry* const pLast = m_entries[idx];

     assert(pLast);

@@ -4028,38 +7487,48 @@
 const BlockEntry* Cluster::GetNext(const BlockEntry* pEntry) const

 {

     assert(pEntry);

-    assert(m_entries);

-    assert(m_entriesCount);

+    assert(m_entries != NULL);

+    assert(m_entries_count > 0);

 

     size_t idx = pEntry->GetIndex();

-    assert(idx < m_entriesCount);

+    assert(idx < size_t(m_entries_count));

     assert(m_entries[idx] == pEntry);

 

     ++idx;

 

-    if (idx >= m_entriesCount)

+    if (idx >= size_t(m_entries_count))

       return NULL;

 

     return m_entries[idx];

 }

 

 

-const BlockEntry* Cluster::GetEntry(const Track* pTrack)

+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)  //EOS

+    if (m_pSegment == NULL)  //this is the special EOS cluster

         return pTrack->GetEOS();

 

     LoadBlockEntries();

 

-    if ((m_entries == NULL) || (m_entriesCount == 0))

-        return NULL;

+    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_entriesCount;

+    BlockEntry** const j = i + m_entries_count;

 

     while (i != j)

     {

@@ -4074,17 +7543,34 @@
             continue;

 

         if (pTrack->VetEntry(pEntry))

-            return 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 pTrack->GetEOS();  //no satisfactory block found

+    return pResult;

 }

 

 

 const BlockEntry*

 Cluster::GetEntry(

     const CuePoint& cp,

-    const CuePoint::TrackPosition& tp)

+    const CuePoint::TrackPosition& tp) const

 {

     assert(m_pSegment);

 

@@ -4093,7 +7579,7 @@
     if (m_entries == NULL)

         return NULL;

 

-    const long long count = m_entriesCount;

+    const long long count = m_entries_count;

 

     if (count <= 0)

         return NULL;

@@ -4124,6 +7610,11 @@
 

     while (i != j)

     {

+#ifdef _DEBUG

+        const ptrdiff_t idx = i - m_entries;

+        idx;

+#endif

+

         const BlockEntry* const pEntry = *i++;

         assert(pEntry);

         assert(!pEntry->EOS());

@@ -4135,6 +7626,7 @@
             continue;

 

         const long long tc_ = pBlock->GetTimeCode(this);

+        assert(tc_ >= 0);

 

         if (tc_ < tc)

             continue;

@@ -4169,7 +7661,7 @@
 }

 

 

-const BlockEntry* Cluster::GetMaxKey(const VideoTrack* pTrack)

+const BlockEntry* Cluster::GetMaxKey(const VideoTrack* pTrack) const

 {

     assert(pTrack);

 

@@ -4177,9 +7669,11 @@
         return pTrack->GetEOS();

 

     LoadBlockEntries();

-    //assert(m_entries);

 

-    BlockEntry** i = m_entries + m_entriesCount;

+    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)

@@ -4231,7 +7725,7 @@
 }

 

 

-Cluster* SimpleBlock::GetCluster() const

+const Cluster* SimpleBlock::GetCluster() const

 {

     return m_pCluster;

 }

@@ -4249,10 +7743,10 @@
 }

 

 

-bool SimpleBlock::IsBFrame() const

-{

-    return false;

-}

+//bool SimpleBlock::IsBFrame() const

+//{

+//    return false;

+//}

 

 

 BlockGroup::BlockGroup(

@@ -4358,7 +7852,7 @@
 }

 

 

-Cluster* BlockGroup::GetCluster() const

+const Cluster* BlockGroup::GetCluster() const

 {

     return m_pCluster;

 }

@@ -4388,11 +7882,10 @@
 }

 

 

-bool BlockGroup::IsBFrame() const

-{

-    return (m_nextTimeCode > 0);

-}

-

+//bool BlockGroup::IsBFrame() const

+//{

+//    return (m_nextTimeCode > 0);

+//}

 

 

 Block::Block(long long start, long long size_, IMkvReader* pReader) :

@@ -4416,23 +7909,240 @@
     pos += 2;

     assert((stop - pos) >= 1);

 

-    const long hr = pReader->Read(pos, 1, &m_flags);

-    assert(hr == 0L);

+    long status = pReader->Read(pos, 1, &m_flags);

+    assert(status == 0);

 

-    ++pos;

+#if 0

+    const int invisible = int(m_flags & 0x08) >> 3;

+    invisible;

+    assert(!invisible);  //TODO

+#endif

+

+    const int lacing = int(m_flags & 0x06) >> 1;

+

+    ++pos;  //consume flags byte

     assert(pos <= stop);

 

-    m_frameOff = pos;

+    if (lacing == 0)  //no lacing

+    {

+        m_frame_count = 1;

+        m_frames = new Frame[m_frame_count];

 

-    const long long frame_size = stop - pos;

+        Frame& f = m_frames[0];

+        f.pos = pos;

 

-    assert(frame_size <= 2147483647L);

+        const long long frame_size = stop - pos;

+        assert(frame_size <= LONG_MAX);

 

-    m_frameSize = static_cast<long>(frame_size);

+        f.len = static_cast<long>(frame_size);

+

+        return;

+    }

+

+    assert(pos < stop);

+

+    unsigned char count;

+

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

+    assert(status == 0);

+

+    ++pos;  //consume frame count

+    assert(pos <= stop);

+

+    m_frame_count = ++count;

+    m_frames = new Frame[m_frame_count];

+

+    if (lacing == 1)  //Xiph

+    {

+        Frame* pf = m_frames;

+        Frame* const pf_end = pf + m_frame_count;

+

+        long size = 0;

+

+        while (count > 1)

+        {

+            long frame_size = 0;

+

+            for (;;)

+            {

+                unsigned char val;

+

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

+                assert(status == 0);

+

+                ++pos;  //consume xiph size byte

+

+                frame_size += val;

+

+                if (val < 255)

+                    break;

+            }

+

+            Frame& f = *pf++;

+            assert(pf < pf_end);

+

+            f.len = frame_size;

+            size += frame_size;  //contribution of this frame

+

+            --count;

+        }

+

+        assert(pf < pf_end);

+        assert(pos < stop);

+

+        {

+            Frame& f = *pf++;

+            assert(pf == pf_end);

+

+            const long long total_size = stop - pos;

+            assert(total_size > size);

+

+            const long long frame_size = total_size - size;

+            assert(frame_size <= LONG_MAX);

+

+            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;

+        assert((total_size % m_frame_count) == 0);

+

+        const long long frame_size = total_size / m_frame_count;

+        assert(frame_size <= LONG_MAX);

+

+        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

+        assert(pos < stop);

+

+        long size = 0;

+

+        long long frame_size = ReadUInt(pReader, pos, len);

+        assert(frame_size > 0);

+        assert(frame_size <= LONG_MAX);

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

+

+        pos += len; //consume length of size of first frame

+        assert((pos + frame_size) <= stop);

+

+        Frame* pf = m_frames;

+        Frame* const pf_end = pf + m_frame_count;

+

+        {

+            Frame& curr = *pf;

+

+            curr.len = static_cast<long>(frame_size);

+            size += curr.len;  //contribution of this frame

+        }

+

+        --count;

+

+        while (count > 1)

+        {

+            assert(pos < stop);

+            assert(pf < pf_end);

+

+            const Frame& prev = *pf++;

+            assert(pf < pf_end);

+            assert(prev.len == frame_size);

+

+            Frame& curr = *pf;

+

+            const long long delta_size_ = ReadUInt(pReader, pos, len);

+            assert(delta_size_ >= 0);

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

+

+            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;

+            assert(frame_size > 0);

+            assert(frame_size <= LONG_MAX);

+

+            curr.len = static_cast<long>(frame_size);

+            size += curr.len;  //contribution of this frame

+

+            --count;

+        }

+

+        {

+            assert(pos < stop);

+            assert(pf < pf_end);

+

+            const Frame& prev = *pf++;

+            assert(pf < pf_end);

+            assert(prev.len == frame_size);

+

+            Frame& curr = *pf++;

+            assert(pf == pf_end);

+

+            const long long total_size = stop - pos;

+            assert(total_size > 0);

+            assert(total_size > size);

+

+            frame_size = total_size - size;

+            assert(frame_size > 0);

+            assert(frame_size <= LONG_MAX);

+

+            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);

+    }

 }

 

 

-long long Block::GetTimeCode(Cluster* pCluster) const

+Block::~Block()

+{

+    delete[] m_frames;

+}

+

+

+long long Block::GetTimeCode(const Cluster* pCluster) const

 {

     assert(pCluster);

 

@@ -4446,7 +8156,7 @@
 }

 

 

-long long Block::GetTime(Cluster* pCluster) const

+long long Block::GetTime(const Cluster* pCluster) const

 {

     assert(pCluster);

 

@@ -4476,9 +8186,6 @@
     return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);

 }

 

-unsigned char Block::Flags() const {

-    return m_flags;

-}

 

 void Block::SetKey(bool bKey)

 {

@@ -4489,27 +8196,38 @@
 }

 

 

-long long Block::GetOffset() const

+bool Block::IsInvisible() const

 {

-  return m_frameOff;

+    return bool(int(m_flags & 0x08) != 0);

 }

 

 

-long Block::GetSize() const

+int Block::GetFrameCount() const

 {

-    return m_frameSize;

+    return m_frame_count;

 }

 

 

-long Block::Read(IMkvReader* pReader, unsigned char* buf) const

+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 hr = pReader->Read(m_frameOff, m_frameSize, buf);

-

-    return hr;

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

+    return status;

 }

 

 

diff --git a/mkvparser/mkvparser.hpp b/mkvparser/mkvparser.hpp
index f7d8948..f769972 100644
--- a/mkvparser/mkvparser.hpp
+++ b/mkvparser/mkvparser.hpp
@@ -37,7 +37,7 @@
 signed char Unserialize1SInt(IMkvReader*, long long);

 bool Match(IMkvReader*, long long&, unsigned long, long long&);

 bool Match(IMkvReader*, long long&, unsigned long, char*&);

-bool Match(IMkvReader*, long long&, unsigned long,unsigned char*&, size_t&);

+bool Match(IMkvReader*, long long&, unsigned long, unsigned char*&, size_t&);

 bool Match(IMkvReader*, long long&, unsigned long, double&);

 bool Match(IMkvReader*, long long&, unsigned long, short&);

 

@@ -56,6 +56,7 @@
     long long m_docTypeReadVersion;

 

     long long Parse(IMkvReader*, long long&);

+    void Init();

 };

 

 

@@ -73,25 +74,34 @@
     const long long m_size;

 

     Block(long long start, long long size, IMkvReader*);

+    ~Block();

 

     long long GetTrackNumber() const;

-    long long GetTimeCode(Cluster*) const;  //absolute, but not scaled

-    long long GetTime(Cluster*) const;      //absolute, and scaled (ns units)

+    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;

 

-    unsigned char Flags() const;

+    int GetFrameCount() const;  //to index frames: [0, count)

 

-    long long GetOffset() const;

-    long GetSize() const;

-    long Read(IMkvReader*, unsigned char*) const;

+    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;

-    long long m_frameOff;

-    long m_frameSize;

+

+    Frame* m_frames;

+    int m_frame_count;

 

 };

 

@@ -104,10 +114,10 @@
 public:

     virtual ~BlockEntry();

     virtual bool EOS() const = 0;

-    virtual Cluster* GetCluster() const = 0;

+    virtual const Cluster* GetCluster() const = 0;

     virtual size_t GetIndex() const = 0;

     virtual const Block* GetBlock() const = 0;

-    virtual bool IsBFrame() const = 0;

+    //virtual bool IsBFrame() const = 0;

 

 protected:

     BlockEntry();

@@ -124,10 +134,10 @@
     SimpleBlock(Cluster*, size_t, long long start, long long size);

 

     bool EOS() const;

-    Cluster* GetCluster() const;

+    const Cluster* GetCluster() const;

     size_t GetIndex() const;

     const Block* GetBlock() const;

-    bool IsBFrame() const;

+    //bool IsBFrame() const;

 

 protected:

     Cluster* const m_pCluster;

@@ -147,10 +157,10 @@
     ~BlockGroup();

 

     bool EOS() const;

-    Cluster* GetCluster() const;

+    const Cluster* GetCluster() const;

     size_t GetIndex() const;

     const Block* GetBlock() const;

-    bool IsBFrame() const;

+    //bool IsBFrame() const;

 

     short GetPrevTimeCode() const;  //relative to block's time

     short GetNextTimeCode() const;  //as above

@@ -186,14 +196,18 @@
 

 public:

     Segment* const m_pSegment;

+    const long long m_element_start;

+    const long long m_element_size;

     virtual ~Track();

 

     long long GetType() const;

     long 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;

 

@@ -207,13 +221,15 @@
     {

         long long type;

         long long number;

-        long long uid;

+        unsigned long long uid;

         char* nameAsUTF8;

         char* codecId;

         unsigned char* codecPrivate;

         size_t codecPrivateSize;

         char* codecNameAsUTF8;

+        bool lacing;

         Settings settings;

+

         Info();

         void Clear();

     };

@@ -221,9 +237,14 @@
     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;

 

 protected:

-    Track(Segment*, const Info&);

+    Track(

+        Segment*,

+        const Info&,

+        long long element_start,

+        long long element_size);

     const Info m_info;

 

     class EOSBlock : public BlockEntry

@@ -232,7 +253,7 @@
         EOSBlock();

 

         bool EOS() const;

-        Cluster* GetCluster() const;

+        const Cluster* GetCluster() const;

         size_t GetIndex() const;

         const Block* GetBlock() const;

         bool IsBFrame() const;

@@ -249,12 +270,17 @@
     VideoTrack& operator=(const VideoTrack&);

 

 public:

-    VideoTrack(Segment*, const Info&);

+    VideoTrack(

+        Segment*,

+        const Info&,

+        long long element_start,

+        long long element_size);

     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;

@@ -270,11 +296,16 @@
     AudioTrack& operator=(const AudioTrack&);

 

 public:

-    AudioTrack(Segment*, const Info&);

+    AudioTrack(

+        Segment*,

+        const Info&,

+        long long element_start,

+        long long element_size);

     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;

@@ -292,18 +323,30 @@
     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);

+    Tracks(

+        Segment*,

+        long long start,

+        long long size,

+        long long element_start,

+        long long element_size);

     virtual ~Tracks();

 

-    Track* GetTrackByNumber(unsigned long tn) const;

-    Track* GetTrackByIndex(unsigned long idx) const;

+    const Track* GetTrackByNumber(unsigned long tn) const;

+    const Track* GetTrackByIndex(unsigned long idx) const;

 

 private:

     Track** m_trackEntries;

     Track** m_trackEntriesEnd;

 

-    void ParseTrackEntry(long long, long long, Track*&);

+    void ParseTrackEntry(

+        long long,

+        long long,

+        Track*&,

+        long long element_start,

+        long long element_size);

 

 public:

     unsigned long GetTracksCount() const;

@@ -319,9 +362,18 @@
     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);

+    SegmentInfo(

+        Segment*,

+        long long start,

+        long long size,

+        long long element_start,

+        long long element_size);

+

     ~SegmentInfo();

+

     long long GetTimeCodeScale() const;

     long long GetDuration() const;  //scaled

     const char* GetMuxingAppAsUTF8() const;

@@ -336,22 +388,68 @@
     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();

+

+    struct Entry

+    {

+        long long id;

+        long long pos;

+    };

+

+    int GetCount() const;

+    const Entry* GetEntry(int idx) const;

+

+private:

+    Entry* m_entries;

+    int m_count;

+

+    static void ParseEntry(

+        IMkvReader*,

+        long long pos,

+        long long size,

+        Entry*&);

+

+};

+

 class Cues;

 class CuePoint

 {

     friend class Cues;

 

-    CuePoint(size_t, long long);

+    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(Segment*) const;  //absolute and scaled (ns units)

+    long long GetTime(const Segment*) const;  //absolute and scaled (ns units)

 

     struct TrackPosition

     {

@@ -368,7 +466,7 @@
     const TrackPosition* Find(const Track*) const;

 

 private:

-    const size_t m_index;

+    const long m_index;

     long long m_timecode;

     TrackPosition* m_track_positions;

     size_t m_track_positions_count;

@@ -380,7 +478,12 @@
 {

     friend class Segment;

 

-    Cues(Segment*, long long start, long long size);

+    Cues(

+        Segment*,

+        long long start,

+        long long size,

+        long long element_start,

+        long long element_size);

     ~Cues();

 

     Cues(const Cues&);

@@ -390,6 +493,8 @@
     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,

@@ -407,21 +512,24 @@
 

     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;

-    bool LoadCuePoint() const;

-    void PreloadCuePoint(size_t&, long long) const;

+    void PreloadCuePoint(long&, long long) const;

 

     mutable CuePoint** m_cue_points;

-    mutable size_t m_count;

-    mutable size_t m_preload_count;

+    mutable long m_count;

+    mutable long m_preload_count;

     mutable long long m_pos;

 

 };

@@ -429,6 +537,8 @@
 

 class Cluster

 {

+    friend class Segment;

+

     Cluster(const Cluster&);

     Cluster& operator=(const Cluster&);

 

@@ -436,45 +546,82 @@
     Segment* const m_pSegment;

 

 public:

-    static Cluster* Parse(Segment*, long, long long off);

+    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();   //absolute, but not scaled

-    long long GetTime();       //absolute, and scaled (nanosecond units)

-    long long GetFirstTime();  //time (ns) of first (earliest) block

-    long long GetLastTime();   //time (ns) of last (latest) block

+    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

 

-    const BlockEntry* GetFirst();

-    const BlockEntry* GetLast();

+    const BlockEntry* GetFirst() const;

+    const BlockEntry* GetLast() const;

     const BlockEntry* GetNext(const BlockEntry*) const;

-    const BlockEntry* GetEntry(const Track*);

+    const BlockEntry* GetEntry(const Track*, long long ns = -1) const;

     const BlockEntry* GetEntry(

         const CuePoint&,

-        const CuePoint::TrackPosition&);

-    const BlockEntry* GetMaxKey(const VideoTrack*);

+        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;

+

+    void Load() const;

+    long Load(long long& pos, long& size) const;

+

+    void LoadBlockEntries() const;

+

+    long Parse(long long& pos, long& size) const;

+    long GetEntry(long index, const mkvparser::BlockEntry*&) const;

 

 protected:

-    Cluster(Segment*, long, long long off);

+    Cluster(

+        Segment*,

+        long index,

+        long long element_start);

+        //long long element_size);

 

 public:

-    //TODO: these should all be private, with public selector functions

-    long m_index;

-    long long m_pos;

-    long long m_size;

+    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 long m_timecode;

-    BlockEntry** m_entries;

-    size_t m_entriesCount;

+    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;

 

-    void Load();

-    void LoadBlockEntries();

-    void ParseBlockGroup(long long, long long, size_t);

-    void ParseSimpleBlock(long long, long long, size_t);

+    long ParseSimpleBlock(long long, long long&, long&) const;

+    long ParseBlockGroup(long long, long long&, long&) const;

+

+    void CreateBlock(long long id, long long pos, long long size) const;

+    void CreateBlockGroup(long long, long long, BlockEntry**&) const;

+    void CreateSimpleBlock(long long, long long, BlockEntry**&) const;

 

 };

 

@@ -482,6 +629,8 @@
 class Segment

 {

     friend class Cues;

+    friend class VideoTrack;

+    friend class AudioTrack;

 

     Segment(const Segment&);

     Segment& operator=(const Segment&);

@@ -500,35 +649,54 @@
 

     long Load();  //loads headers and all clusters

 

-    //for incremental loading (splitter)

+    //for incremental loading

     long long Unparsed() const;

     long long ParseHeaders();  //stops when first cluster is found

-    long LoadCluster();        //loads one cluster

+    //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(Cluster*&, long long& newpos) const;

-    bool AddCluster(Cluster*, long long);

+    long ParseCluster(long long& cluster_pos, long long& new_pos) const;

+    bool AddCluster(long long cluster_pos, long long new_pos);

 #endif

 

-    Tracks* GetTracks() const;

+    const SeekHead* GetSeekHead() const;

+    const Tracks* GetTracks() const;

     const SegmentInfo* GetInfo() const;

     const Cues* GetCues() const;

 

     long long GetDuration() const;

 

     unsigned long GetCount() const;

-    Cluster* GetFirst();

-    Cluster* GetLast();

-    Cluster* GetNext(const Cluster*);

+    const Cluster* GetFirst() const;

+    const Cluster* GetLast() const;

+    const Cluster* GetNext(const Cluster*);

 

-    Cluster* FindCluster(long long time_nanoseconds);

-    const BlockEntry* Seek(long long time_nanoseconds, const Track*);

+    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;

@@ -537,12 +705,16 @@
     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);

+    //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&,

@@ -550,7 +722,14 @@
 

 };

 

-

 }  //end namespace mkvparser

 

+inline long mkvparser::Segment::LoadCluster()

+{

+    long long pos;

+    long size;

+

+    return LoadCluster(pos, size);

+}

+

 #endif  //MKVPARSER_HPP