Browse Source

Merge pull request #17378 from ShyRed/webm

Update libwebm
Rémi Verschelde 7 years ago
parent
commit
aeb1c67b5b

+ 4 - 0
modules/webm/SCsub

@@ -18,6 +18,10 @@ thirdparty_libsimplewebm_sources = [thirdparty_libsimplewebm_dir + file for file
 env_webm.add_source_files(env.modules_sources, thirdparty_libsimplewebm_sources)
 env_webm.Append(CPPPATH=[thirdparty_libsimplewebm_dir, thirdparty_libsimplewebm_dir + "libwebm/"])
 
+# upstream uses c++11
+if (not env_webm.msvc):
+	env_webm.Append(CCFLAGS="-std=c++11")
+
 # also requires libogg, libvorbis and libopus
 if env['builtin_libogg']:
     env_webm.Append(CPPPATH=["#thirdparty/libogg"])

+ 1 - 1
thirdparty/libsimplewebm/libwebm/README.libvpx

@@ -1,5 +1,5 @@
 URL: https://chromium.googlesource.com/webm/libwebm
-Version: 32d5ac49414a8914ec1e1f285f3f927c6e8ec29d
+Version: d7c62173ff6b4a5e0a2f86683a5b67db98cf09bf
 License: BSD
 License File: LICENSE.txt
 

+ 8 - 0
thirdparty/libsimplewebm/libwebm/common/webmids.h

@@ -124,6 +124,14 @@ enum MkvId {
   kMkvLuminanceMin = 0x55DA,
   // end mastering metadata
   // end colour
+  // projection
+  kMkvProjection = 0x7670,
+  kMkvProjectionType = 0x7671,
+  kMkvProjectionPrivate = 0x7672,
+  kMkvProjectionPoseYaw = 0x7673,
+  kMkvProjectionPosePitch = 0x7674,
+  kMkvProjectionPoseRoll = 0x7675,
+  // end projection
   // audio
   kMkvAudio = 0xE1,
   kMkvSamplingFrequency = 0xB5,

+ 324 - 108
thirdparty/libsimplewebm/libwebm/mkvparser/mkvparser.cc

@@ -12,51 +12,30 @@
 #define MSC_COMPAT
 #endif
 
-#include <assert.h>
-#include <float.h>
-#include <limits.h>
-#include <math.h>
-#include <string.h>
+#include <cassert>
+#include <cfloat>
+#include <climits>
+#include <cmath>
+#include <cstring>
+#include <memory>
+#include <new>
 
 #include "common/webmids.h"
 
 namespace mkvparser {
+const long long kStringElementSizeLimit = 20 * 1000 * 1000;
 const float MasteringMetadata::kValueNotPresent = FLT_MAX;
 const long long Colour::kValueNotPresent = LLONG_MAX;
+const float Projection::kValueNotPresent = FLT_MAX;
 
 #ifdef MSC_COMPAT
 inline bool isnan(double val) { return !!_isnan(val); }
 inline bool isinf(double val) { return !_finite(val); }
+#else
+inline bool isnan(double val) { return std::isnan(val); }
+inline bool isinf(double val) { return std::isinf(val); }
 #endif  // MSC_COMPAT
 
-template<typename T>
-class my_auto_ptr {
-  my_auto_ptr(const my_auto_ptr &);
-  T *operator =(const my_auto_ptr &);
-
-  T *m_ptr;
-public:
-  my_auto_ptr(T *ptr) :
-    m_ptr(ptr)
-  {}
-  my_auto_ptr() :
-    m_ptr(NULL)
-  {}
-  ~my_auto_ptr() {
-    delete m_ptr;
-  }
-
-  T *release() {
-    T *ptr = m_ptr;
-    m_ptr = NULL;
-    return ptr;
-  }
-
-  T *operator ->() const {
-    return m_ptr;
-  }
-};
-
 IMkvReader::~IMkvReader() {}
 
 template <typename Type>
@@ -72,7 +51,7 @@ Type* SafeArrayAlloc(unsigned long long num_elements,
   if (num_bytes != static_cast<size_t>(num_bytes))
     return NULL;
 
-  return new Type[static_cast<size_t>(num_bytes)];
+  return new (std::nothrow) Type[static_cast<size_t>(num_bytes)];
 }
 
 void GetVersion(int& major, int& minor, int& build, int& revision) {
@@ -304,7 +283,7 @@ long UnserializeFloat(IMkvReader* pReader, long long pos, long long size_,
     result = d;
   }
 
-  if (isinf(result) || isnan(result))
+  if (mkvparser::isinf(result) || mkvparser::isnan(result))
     return E_FILE_FORMAT_INVALID;
 
   return 0;
@@ -347,7 +326,7 @@ long UnserializeString(IMkvReader* pReader, long long pos, long long size,
   delete[] str;
   str = NULL;
 
-  if (size >= LONG_MAX || size < 0)
+  if (size >= LONG_MAX || size < 0 || size > kStringElementSizeLimit)
     return E_FILE_FORMAT_INVALID;
 
   // +1 for '\0' terminator
@@ -818,7 +797,9 @@ long long Segment::CreateInstance(IMkvReader* pReader, long long pos,
       else if ((pos + size) > total)
         size = -1;
 
-      pSegment = new Segment(pReader, idpos, pos, size);
+      pSegment = new (std::nothrow) Segment(pReader, idpos, pos, size);
+      if (pSegment == NULL)
+        return E_PARSE_FAILED;
 
       return 0;  // success
     }
@@ -954,7 +935,11 @@ long long Segment::ParseHeaders() {
       if (m_pInfo)
         return E_FILE_FORMAT_INVALID;
 
-      m_pInfo = new SegmentInfo(this, pos, size, element_start, element_size);
+      m_pInfo = new (std::nothrow)
+          SegmentInfo(this, pos, size, element_start, element_size);
+
+      if (m_pInfo == NULL)
+        return -1;
 
       const long status = m_pInfo->Parse();
 
@@ -964,7 +949,11 @@ long long Segment::ParseHeaders() {
       if (m_pTracks)
         return E_FILE_FORMAT_INVALID;
 
-      m_pTracks = new Tracks(this, pos, size, element_start, element_size);
+      m_pTracks = new (std::nothrow)
+          Tracks(this, pos, size, element_start, element_size);
+
+      if (m_pTracks == NULL)
+        return -1;
 
       const long status = m_pTracks->Parse();
 
@@ -972,11 +961,19 @@ long long Segment::ParseHeaders() {
         return status;
     } else if (id == libwebm::kMkvCues) {
       if (m_pCues == NULL) {
-        m_pCues = new Cues(this, pos, size, element_start, element_size);
+        m_pCues = new (std::nothrow)
+            Cues(this, pos, size, element_start, element_size);
+
+        if (m_pCues == NULL)
+          return -1;
       }
     } else if (id == libwebm::kMkvSeekHead) {
       if (m_pSeekHead == NULL) {
-        m_pSeekHead = new SeekHead(this, pos, size, element_start, element_size);
+        m_pSeekHead = new (std::nothrow)
+            SeekHead(this, pos, size, element_start, element_size);
+
+        if (m_pSeekHead == NULL)
+          return -1;
 
         const long status = m_pSeekHead->Parse();
 
@@ -985,7 +982,11 @@ long long Segment::ParseHeaders() {
       }
     } else if (id == libwebm::kMkvChapters) {
       if (m_pChapters == NULL) {
-        m_pChapters = new Chapters(this, pos, size, element_start, element_size);
+        m_pChapters = new (std::nothrow)
+            Chapters(this, pos, size, element_start, element_size);
+
+        if (m_pChapters == NULL)
+          return -1;
 
         const long status = m_pChapters->Parse();
 
@@ -994,7 +995,11 @@ long long Segment::ParseHeaders() {
       }
     } else if (id == libwebm::kMkvTags) {
       if (m_pTags == NULL) {
-        m_pTags = new Tags(this, pos, size, element_start, element_size);
+        m_pTags = new (std::nothrow)
+            Tags(this, pos, size, element_start, element_size);
+
+        if (m_pTags == NULL)
+          return -1;
 
         const long status = m_pTags->Parse();
 
@@ -1136,7 +1141,9 @@ long Segment::DoLoadCluster(long long& pos, long& len) {
       if (m_pCues == NULL) {
         const long long element_size = (pos - idpos) + size;
 
-        m_pCues = new Cues(this, pos, size, idpos, element_size);
+        m_pCues = new (std::nothrow) Cues(this, pos, size, idpos, element_size);
+        if (m_pCues == NULL)
+          return -1;
       }
 
       m_pos = pos + size;  // consume payload
@@ -1284,9 +1291,7 @@ long Segment::DoLoadCluster(long long& pos, long& len) {
     pos += cluster_size;
 
     m_pos = pos;
-    // -- GODOT start --
-    delete pCluster;
-    // -- GODOT end --
+
     if (segment_stop > 0 && m_pos > segment_stop)
       return E_FILE_FORMAT_INVALID;
 
@@ -1344,7 +1349,9 @@ bool Segment::AppendCluster(Cluster* pCluster) {
   if (count >= size) {
     const long n = (size <= 0) ? 2048 : 2 * size;
 
-    Cluster** const qq = new Cluster*[n];
+    Cluster** const qq = new (std::nothrow) Cluster*[n];
+    if (qq == NULL)
+      return false;
 
     Cluster** q = qq;
     Cluster** p = m_clusters;
@@ -1399,7 +1406,9 @@ bool Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) {
   if (count >= size) {
     const long n = (size <= 0) ? 2048 : 2 * size;
 
-    Cluster** const qq = new Cluster*[n];
+    Cluster** const qq = new (std::nothrow) Cluster*[n];
+    if (qq == NULL)
+      return false;
     Cluster** q = qq;
 
     Cluster** p = m_clusters;
@@ -1468,6 +1477,8 @@ long Segment::Load() {
   }
 }
 
+SeekHead::Entry::Entry() : id(0), pos(0), element_start(0), element_size(0) {}
+
 SeekHead::SeekHead(Segment* pSegment, long long start, long long size_,
                    long long element_start, long long element_size)
     : m_pSegment(pSegment),
@@ -1518,9 +1529,19 @@ long SeekHead::Parse() {
   if (pos != stop)
     return E_FILE_FORMAT_INVALID;
 
-  m_entries = new Entry[entry_count];
+  if (entry_count > 0) {
+    m_entries = new (std::nothrow) Entry[entry_count];
 
-  m_void_elements = new VoidElement[void_element_count];
+    if (m_entries == NULL)
+      return -1;
+  }
+
+  if (void_element_count > 0) {
+    m_void_elements = new (std::nothrow) VoidElement[void_element_count];
+
+    if (m_void_elements == NULL)
+      return -1;
+  }
 
   // now parse the entries and void elements
 
@@ -1539,14 +1560,14 @@ long SeekHead::Parse() {
     if (status < 0)  // error
       return status;
 
-    if (id == libwebm::kMkvSeek) {
+    if (id == libwebm::kMkvSeek && entry_count > 0) {
       if (ParseEntry(pReader, pos, size, pEntry)) {
         Entry& e = *pEntry++;
 
         e.element_start = idpos;
         e.element_size = (pos + size) - idpos;
       }
-    } else if (id == libwebm::kMkvVoid) {
+    } else if (id == libwebm::kMkvVoid && void_element_count > 0) {
       VoidElement& e = *pVoidElement++;
 
       e.element_start = idpos;
@@ -1708,7 +1729,10 @@ long Segment::ParseCues(long long off, long long& pos, long& len) {
 
   const long long element_size = element_stop - element_start;
 
-  m_pCues = new Cues(this, pos, size, element_start, element_size);
+  m_pCues =
+      new (std::nothrow) Cues(this, pos, size, element_start, element_size);
+  if (m_pCues == NULL)
+    return -1;
 
   return 0;  // success
 }
@@ -1750,18 +1774,7 @@ bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_,
   if ((pos + seekIdSize) > stop)
     return false;
 
-  // Note that the SeekId payload really is serialized
-  // as a "Matroska integer", not as a plain binary value.
-  // In fact, Matroska requires that ID values in the
-  // stream exactly match the binary representation as listed
-  // in the Matroska specification.
-  //
-  // This parser is more liberal, and permits IDs to have
-  // any width.  (This could make the representation in the stream
-  // different from what's in the spec, but it doesn't matter here,
-  // since we always normalize "Matroska integer" values.)
-
-  pEntry->id = ReadUInt(pReader, pos, len);  // payload
+  pEntry->id = ReadID(pReader, pos, len);  // payload
 
   if (pEntry->id <= 0)
     return false;
@@ -1900,7 +1913,9 @@ bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
   if (m_preload_count >= cue_points_size) {
     const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size;
 
-    CuePoint** const qq = new CuePoint*[n];
+    CuePoint** const qq = new (std::nothrow) CuePoint*[n];
+    if (qq == NULL)
+      return false;
 
     CuePoint** q = qq;  // beginning of target
 
@@ -1916,7 +1931,9 @@ bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const {
     cue_points_size = n;
   }
 
-  CuePoint* const pCP = new CuePoint(m_preload_count, pos);
+  CuePoint* const pCP = new (std::nothrow) CuePoint(m_preload_count, pos);
+  if (pCP == NULL)
+    return false;
 
   m_cue_points[m_preload_count++] = pCP;
   return true;
@@ -2322,7 +2339,9 @@ bool CuePoint::Load(IMkvReader* pReader) {
   //   << " timecode=" << m_timecode
   //   << endl;
 
-  m_track_positions = new TrackPosition[m_track_positions_count];
+  m_track_positions = new (std::nothrow) TrackPosition[m_track_positions_count];
+  if (m_track_positions == NULL)
+    return false;
 
   // Now parse track positions
 
@@ -2412,7 +2431,9 @@ bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_,
 }
 
 const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const {
-  assert(pTrack);
+  if (pTrack == NULL) {
+    return NULL;
+  }
 
   const long long n = pTrack->GetNumber();
 
@@ -2917,7 +2938,10 @@ long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) {
       const long long element_size = element_stop - element_start;
 
       if (m_pCues == NULL) {
-        m_pCues = new Cues(this, pos, size, element_start, element_size);
+        m_pCues = new (std::nothrow)
+            Cues(this, pos, size, element_start, element_size);
+        if (m_pCues == NULL)
+          return false;
       }
 
       pos += size;  // consume payload
@@ -3268,7 +3292,10 @@ bool Chapters::ExpandEditionsArray() {
 
   const int size = (m_editions_size == 0) ? 1 : 2 * m_editions_size;
 
-  Edition* const editions = new Edition[size];
+  Edition* const editions = new (std::nothrow) Edition[size];
+
+  if (editions == NULL)
+    return false;
 
   for (int idx = 0; idx < m_editions_count; ++idx) {
     m_editions[idx].ShallowCopy(editions[idx]);
@@ -3380,7 +3407,10 @@ bool Chapters::Edition::ExpandAtomsArray() {
 
   const int size = (m_atoms_size == 0) ? 1 : 2 * m_atoms_size;
 
-  Atom* const atoms = new Atom[size];
+  Atom* const atoms = new (std::nothrow) Atom[size];
+
+  if (atoms == NULL)
+    return false;
 
   for (int idx = 0; idx < m_atoms_count; ++idx) {
     m_atoms[idx].ShallowCopy(atoms[idx]);
@@ -3565,7 +3595,10 @@ bool Chapters::Atom::ExpandDisplaysArray() {
 
   const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size;
 
-  Display* const displays = new Display[size];
+  Display* const displays = new (std::nothrow) Display[size];
+
+  if (displays == NULL)
+    return false;
 
   for (int idx = 0; idx < m_displays_count; ++idx) {
     m_displays[idx].ShallowCopy(displays[idx]);
@@ -3725,7 +3758,10 @@ bool Tags::ExpandTagsArray() {
 
   const int size = (m_tags_size == 0) ? 1 : 2 * m_tags_size;
 
-  Tag* const tags = new Tag[size];
+  Tag* const tags = new (std::nothrow) Tag[size];
+
+  if (tags == NULL)
+    return false;
 
   for (int idx = 0; idx < m_tags_count; ++idx) {
     m_tags[idx].ShallowCopy(tags[idx]);
@@ -3836,7 +3872,10 @@ bool Tags::Tag::ExpandSimpleTagsArray() {
 
   const int size = (m_simple_tags_size == 0) ? 1 : 2 * m_simple_tags_size;
 
-  SimpleTag* const displays = new SimpleTag[size];
+  SimpleTag* const displays = new (std::nothrow) SimpleTag[size];
+
+  if (displays == NULL)
+    return false;
 
   for (int idx = 0; idx < m_simple_tags_count; ++idx) {
     m_simple_tags[idx].ShallowCopy(displays[idx]);
@@ -3994,7 +4033,7 @@ long SegmentInfo::Parse() {
   }
 
   const double rollover_check = m_duration * m_timecodeScale;
-  if (rollover_check > LLONG_MAX)
+  if (rollover_check > static_cast<double>(LLONG_MAX))
     return E_FILE_FORMAT_INVALID;
 
   if (pos != stop)
@@ -4085,7 +4124,7 @@ ContentEncoding::~ContentEncoding() {
 }
 
 const ContentEncoding::ContentCompression*
-    ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
+ContentEncoding::GetCompressionByIndex(unsigned long idx) const {
   const ptrdiff_t count = compression_entries_end_ - compression_entries_;
   assert(count >= 0);
 
@@ -4181,12 +4220,20 @@ long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
     return -1;
 
   if (compression_count > 0) {
-    compression_entries_ = new ContentCompression*[compression_count];
+    compression_entries_ =
+        new (std::nothrow) ContentCompression*[compression_count];
+    if (!compression_entries_)
+      return -1;
     compression_entries_end_ = compression_entries_;
   }
 
   if (encryption_count > 0) {
-    encryption_entries_ = new ContentEncryption*[encryption_count];
+    encryption_entries_ =
+        new (std::nothrow) ContentEncryption*[encryption_count];
+    if (!encryption_entries_) {
+      delete[] compression_entries_;
+      return -1;
+    }
     encryption_entries_end_ = encryption_entries_;
   }
 
@@ -4206,7 +4253,10 @@ long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
     } else if (id == libwebm::kMkvContentEncodingType) {
       encoding_type_ = UnserializeUInt(pReader, pos, size);
     } else if (id == libwebm::kMkvContentCompression) {
-      ContentCompression* const compression = new ContentCompression();
+      ContentCompression* const compression =
+          new (std::nothrow) ContentCompression();
+      if (!compression)
+        return -1;
 
       status = ParseCompressionEntry(pos, size, pReader, compression);
       if (status) {
@@ -4215,7 +4265,10 @@ long ContentEncoding::ParseContentEncodingEntry(long long start, long long size,
       }
       *compression_entries_end_++ = compression;
     } else if (id == libwebm::kMkvContentEncryption) {
-      ContentEncryption* const encryption = new ContentEncryption();
+      ContentEncryption* const encryption =
+          new (std::nothrow) ContentEncryption();
+      if (!encryption)
+        return -1;
 
       status = ParseEncryptionEntry(pos, size, pReader, encryption);
       if (status) {
@@ -4421,7 +4474,11 @@ long Track::Create(Segment* pSegment, const Info& info, long long element_start,
   if (pResult)
     return -1;
 
-  Track* const pTrack = new Track(pSegment, element_start, element_size);
+  Track* const pTrack =
+      new (std::nothrow) Track(pSegment, element_start, element_size);
+
+  if (pTrack == NULL)
+    return -1;  // generic error
 
   const int status = info.Copy(pTrack->m_info);
 
@@ -4873,7 +4930,10 @@ long Track::ParseContentEncodingsEntry(long long start, long long size) {
   if (count <= 0)
     return -1;
 
-  content_encoding_entries_ = new ContentEncoding*[count];
+  content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count];
+  if (!content_encoding_entries_)
+    return -1;
+
   content_encoding_entries_end_ = content_encoding_entries_;
 
   pos = start;
@@ -4885,7 +4945,10 @@ long Track::ParseContentEncodingsEntry(long long start, long long size) {
 
     // pos now designates start of element
     if (id == libwebm::kMkvContentEncoding) {
-      ContentEncoding* const content_encoding = new ContentEncoding();
+      ContentEncoding* const content_encoding =
+          new (std::nothrow) ContentEncoding();
+      if (!content_encoding)
+        return -1;
 
       status = content_encoding->ParseContentEncodingEntry(pos, size, pReader);
       if (status) {
@@ -4919,20 +4982,27 @@ bool PrimaryChromaticity::Parse(IMkvReader* reader, long long read_pos,
   if (!reader)
     return false;
 
-  my_auto_ptr<PrimaryChromaticity> chromaticity_ptr(*chromaticity ? *chromaticity : new PrimaryChromaticity());
+  if (!*chromaticity)
+    *chromaticity = new PrimaryChromaticity();
+
+  if (!*chromaticity)
+    return false;
 
-  float* value = is_x ? &chromaticity_ptr->x : &chromaticity_ptr->y;
+  PrimaryChromaticity* pc = *chromaticity;
+  float* value = is_x ? &pc->x : &pc->y;
 
   double parser_value = 0;
-  const long long value_parse_status =
+  const long long parse_status =
       UnserializeFloat(reader, read_pos, value_size, parser_value);
 
-  *value = static_cast<float>(parser_value);
-
-  if (value_parse_status < 0 || *value < 0.0 || *value > 1.0)
+  // Valid range is [0, 1]. Make sure the double is representable as a float
+  // before casting.
+  if (parse_status < 0 || parser_value < 0.0 || parser_value > 1.0 ||
+      (parser_value > 0.0 && parser_value < FLT_MIN))
     return false;
 
-  *chromaticity = chromaticity_ptr.release();
+  *value = static_cast<float>(parser_value);
+
   return true;
 }
 
@@ -4941,7 +5011,9 @@ bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start,
   if (!reader || *mm)
     return false;
 
-  my_auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
+  std::unique_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata());
+  if (!mm_ptr.get())
+    return false;
 
   const long long mm_end = mm_start + mm_size;
   long long read_pos = mm_start;
@@ -4959,6 +5031,10 @@ bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start,
       double value = 0;
       const long long value_parse_status =
           UnserializeFloat(reader, read_pos, child_size, value);
+      if (value < -FLT_MAX || value > FLT_MAX ||
+          (value > 0.0 && value < FLT_MIN)) {
+        return false;
+      }
       mm_ptr->luminance_max = static_cast<float>(value);
       if (value_parse_status < 0 || mm_ptr->luminance_max < 0.0 ||
           mm_ptr->luminance_max > 9999.99) {
@@ -4968,6 +5044,10 @@ bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start,
       double value = 0;
       const long long value_parse_status =
           UnserializeFloat(reader, read_pos, child_size, value);
+      if (value < -FLT_MAX || value > FLT_MAX ||
+          (value > 0.0 && value < FLT_MIN)) {
+        return false;
+      }
       mm_ptr->luminance_min = static_cast<float>(value);
       if (value_parse_status < 0 || mm_ptr->luminance_min < 0.0 ||
           mm_ptr->luminance_min > 999.9999) {
@@ -5020,7 +5100,9 @@ bool Colour::Parse(IMkvReader* reader, long long colour_start,
   if (!reader || *colour)
     return false;
 
-  my_auto_ptr<Colour> colour_ptr(new Colour());
+  std::unique_ptr<Colour> colour_ptr(new Colour());
+  if (!colour_ptr.get())
+    return false;
 
   const long long colour_end = colour_start + colour_size;
   long long read_pos = colour_start;
@@ -5111,11 +5193,94 @@ bool Colour::Parse(IMkvReader* reader, long long colour_start,
   return true;
 }
 
+bool Projection::Parse(IMkvReader* reader, long long start, long long size,
+                       Projection** projection) {
+  if (!reader || *projection)
+    return false;
+
+  std::unique_ptr<Projection> projection_ptr(new Projection());
+  if (!projection_ptr.get())
+    return false;
+
+  const long long end = start + size;
+  long long read_pos = start;
+
+  while (read_pos < end) {
+    long long child_id = 0;
+    long long child_size = 0;
+
+    const long long status =
+        ParseElementHeader(reader, read_pos, end, child_id, child_size);
+    if (status < 0)
+      return false;
+
+    if (child_id == libwebm::kMkvProjectionType) {
+      long long projection_type = kTypeNotPresent;
+      projection_type = UnserializeUInt(reader, read_pos, child_size);
+      if (projection_type < 0)
+        return false;
+
+      projection_ptr->type = static_cast<ProjectionType>(projection_type);
+    } else if (child_id == libwebm::kMkvProjectionPrivate) {
+      unsigned char* data = SafeArrayAlloc<unsigned char>(1, child_size);
+
+      if (data == NULL)
+        return false;
+
+      const int status =
+          reader->Read(read_pos, static_cast<long>(child_size), data);
+
+      if (status) {
+        delete[] data;
+        return false;
+      }
+
+      projection_ptr->private_data = data;
+      projection_ptr->private_data_length = static_cast<size_t>(child_size);
+    } else {
+      double value = 0;
+      const long long value_parse_status =
+          UnserializeFloat(reader, read_pos, child_size, value);
+      // Make sure value is representable as a float before casting.
+      if (value_parse_status < 0 || value < -FLT_MAX || value > FLT_MAX ||
+          (value > 0.0 && value < FLT_MIN)) {
+        return false;
+      }
+
+      switch (child_id) {
+        case libwebm::kMkvProjectionPoseYaw:
+          projection_ptr->pose_yaw = static_cast<float>(value);
+          break;
+        case libwebm::kMkvProjectionPosePitch:
+          projection_ptr->pose_pitch = static_cast<float>(value);
+          break;
+        case libwebm::kMkvProjectionPoseRoll:
+          projection_ptr->pose_roll = static_cast<float>(value);
+          break;
+        default:
+          return false;
+      }
+    }
+
+    read_pos += child_size;
+    if (read_pos > end)
+      return false;
+  }
+
+  *projection = projection_ptr.release();
+  return true;
+}
+
 VideoTrack::VideoTrack(Segment* pSegment, long long element_start,
                        long long element_size)
-    : Track(pSegment, element_start, element_size), m_colour(NULL) {}
+    : Track(pSegment, element_start, element_size),
+      m_colour(NULL),
+      m_projection(NULL) {}
 
-VideoTrack::~VideoTrack() { delete m_colour; }
+VideoTrack::~VideoTrack() {
+  delete m_colour;
+  delete m_projection;
+}
 
 long VideoTrack::Parse(Segment* pSegment, const Info& info,
                        long long element_start, long long element_size,
@@ -5147,6 +5312,7 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info,
   const long long stop = pos + s.size;
 
   Colour* colour = NULL;
+  Projection* projection = NULL;
 
   while (pos < stop) {
     long long id, size;
@@ -5197,6 +5363,9 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info,
     } else if (id == libwebm::kMkvColour) {
       if (!Colour::Parse(pReader, pos, size, &colour))
         return E_FILE_FORMAT_INVALID;
+    } else if (id == libwebm::kMkvProjection) {
+      if (!Projection::Parse(pReader, pos, size, &projection))
+        return E_FILE_FORMAT_INVALID;
     }
 
     pos += size;  // consume payload
@@ -5207,7 +5376,11 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info,
   if (pos != stop)
     return E_FILE_FORMAT_INVALID;
 
-  VideoTrack* const pTrack = new VideoTrack(pSegment, element_start, element_size);
+  VideoTrack* const pTrack =
+      new (std::nothrow) VideoTrack(pSegment, element_start, element_size);
+
+  if (pTrack == NULL)
+    return -1;  // generic error
 
   const int status = info.Copy(pTrack->m_info);
 
@@ -5224,6 +5397,7 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info,
   pTrack->m_stereo_mode = stereo_mode;
   pTrack->m_rate = rate;
   pTrack->m_colour = colour;
+  pTrack->m_projection = projection;
 
   pResult = pTrack;
   return 0;  // success
@@ -5324,6 +5498,8 @@ long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const {
 
 Colour* VideoTrack::GetColour() const { return m_colour; }
 
+Projection* VideoTrack::GetProjection() const { return m_projection; }
+
 long long VideoTrack::GetWidth() const { return m_width; }
 
 long long VideoTrack::GetHeight() const { return m_height; }
@@ -5406,7 +5582,11 @@ long AudioTrack::Parse(Segment* pSegment, const Info& info,
   if (pos != stop)
     return E_FILE_FORMAT_INVALID;
 
-  AudioTrack* const pTrack = new AudioTrack(pSegment, element_start, element_size);
+  AudioTrack* const pTrack =
+      new (std::nothrow) AudioTrack(pSegment, element_start, element_size);
+
+  if (pTrack == NULL)
+    return -1;  // generic error
 
   const int status = info.Copy(pTrack->m_info);
 
@@ -5474,7 +5654,11 @@ long Tracks::Parse() {
   if (count <= 0)
     return 0;  // success
 
-  m_trackEntries = new Track*[count];
+  m_trackEntries = new (std::nothrow) Track*[count];
+
+  if (m_trackEntries == NULL)
+    return -1;
+
   m_trackEntriesEnd = m_trackEntries;
 
   pos = m_start;
@@ -6577,7 +6761,10 @@ Cluster* Cluster::Create(Segment* pSegment, long idx, long long off) {
 
   const long long element_start = pSegment->m_start + off;
 
-  return new Cluster(pSegment, idx, element_start);
+  Cluster* const pCluster =
+      new (std::nothrow) Cluster(pSegment, idx, element_start);
+
+  return pCluster;
 }
 
 Cluster::Cluster()
@@ -6606,8 +6793,10 @@ Cluster::Cluster(Segment* pSegment, long idx, long long element_start
 {}
 
 Cluster::~Cluster() {
-  if (m_entries_count <= 0)
+  if (m_entries_count <= 0) {
+    delete[] m_entries;
     return;
+  }
 
   BlockEntry** i = m_entries;
   BlockEntry** const j = m_entries + m_entries_count;
@@ -6920,7 +7109,9 @@ long Cluster::CreateBlock(long long id,
     assert(m_entries_size == 0);
 
     m_entries_size = 1024;
-    m_entries = new BlockEntry*[m_entries_size];
+    m_entries = new (std::nothrow) BlockEntry*[m_entries_size];
+    if (m_entries == NULL)
+      return -1;
 
     m_entries_count = 0;
   } else {
@@ -6931,7 +7122,9 @@ long Cluster::CreateBlock(long long id,
     if (m_entries_count >= m_entries_size) {
       const long entries_size = 2 * m_entries_size;
 
-      BlockEntry** const entries = new BlockEntry*[entries_size];
+      BlockEntry** const entries = new (std::nothrow) BlockEntry*[entries_size];
+      if (entries == NULL)
+        return -1;
 
       BlockEntry** src = m_entries;
       BlockEntry** const src_end = src + m_entries_count;
@@ -7040,7 +7233,11 @@ long Cluster::CreateBlockGroup(long long start_offset, long long size,
   BlockEntry** const ppEntry = m_entries + idx;
   BlockEntry*& pEntry = *ppEntry;
 
-  pEntry = new BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding);
+  pEntry = new (std::nothrow)
+      BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding);
+
+  if (pEntry == NULL)
+    return -1;  // generic error
 
   BlockGroup* const p = static_cast<BlockGroup*>(pEntry);
 
@@ -7068,7 +7265,10 @@ long Cluster::CreateSimpleBlock(long long st, long long sz) {
   BlockEntry** const ppEntry = m_entries + idx;
   BlockEntry*& pEntry = *ppEntry;
 
-  pEntry = new SimpleBlock(this, idx, st, sz);
+  pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz);
+
+  if (pEntry == NULL)
+    return -1;  // generic error
 
   SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry);
 
@@ -7463,7 +7663,9 @@ long Block::Parse(const Cluster* pCluster) {
       return E_FILE_FORMAT_INVALID;
 
     m_frame_count = 1;
-    m_frames = new Frame[m_frame_count];
+    m_frames = new (std::nothrow) Frame[m_frame_count];
+    if (m_frames == NULL)
+      return -1;
 
     Frame& f = m_frames[0];
     f.pos = pos;
@@ -7494,7 +7696,9 @@ long Block::Parse(const Cluster* pCluster) {
 
   m_frame_count = int(biased_count) + 1;
 
-  m_frames = new Frame[m_frame_count];
+  m_frames = new (std::nothrow) Frame[m_frame_count];
+  if (m_frames == NULL)
+    return -1;
 
   if (!m_frames)
     return E_FILE_FORMAT_INVALID;
@@ -7703,6 +7907,10 @@ long Block::Parse(const Cluster* pCluster) {
         return E_FILE_FORMAT_INVALID;
 
       curr.len = static_cast<long>(frame_size);
+      // Check if size + curr.len could overflow.
+      if (size > LLONG_MAX - curr.len) {
+        return E_FILE_FORMAT_INVALID;
+      }
       size += curr.len;  // contribution of this frame
 
       --frame_count;
@@ -7743,7 +7951,6 @@ long Block::Parse(const Cluster* pCluster) {
     pf = m_frames;
     while (pf != pf_end) {
       Frame& f = *pf++;
-      assert((pos + f.len) <= stop);
       if ((pos + f.len) > stop)
         return E_FILE_FORMAT_INVALID;
 
@@ -7765,6 +7972,11 @@ long long Block::GetTimeCode(const Cluster* pCluster) const {
   const long long tc0 = pCluster->GetTimeCode();
   assert(tc0 >= 0);
 
+  // Check if tc0 + m_timecode would overflow.
+  if (tc0 < 0 || LLONG_MAX - tc0 < m_timecode) {
+    return -1;
+  }
+
   const long long tc = tc0 + m_timecode;
 
   return tc;  // unscaled timecode units
@@ -7782,6 +7994,10 @@ long long Block::GetTime(const Cluster* pCluster) const {
   const long long scale = pInfo->GetTimeCodeScale();
   assert(scale >= 1);
 
+  // Check if tc * scale could overflow.
+  if (tc != 0 && scale > LLONG_MAX / tc) {
+    return -1;
+  }
   const long long ns = tc * scale;
 
   return ns;

+ 35 - 1
thirdparty/libsimplewebm/libwebm/mkvparser/mkvparser.h

@@ -8,7 +8,7 @@
 #ifndef MKVPARSER_MKVPARSER_H_
 #define MKVPARSER_MKVPARSER_H_
 
-#include <stddef.h>
+#include <cstddef>
 
 namespace mkvparser {
 
@@ -21,6 +21,7 @@ class IMkvReader {
   virtual int Read(long long pos, long len, unsigned char* buf) = 0;
   virtual int Length(long long* total, long long* available) = 0;
 
+ public:
   virtual ~IMkvReader();
 };
 
@@ -472,6 +473,34 @@ struct Colour {
   MasteringMetadata* mastering_metadata;
 };
 
+struct Projection {
+  enum ProjectionType {
+    kTypeNotPresent = -1,
+    kRectangular = 0,
+    kEquirectangular = 1,
+    kCubeMap = 2,
+    kMesh = 3,
+  };
+  static const float kValueNotPresent;
+  Projection()
+      : type(kTypeNotPresent),
+        private_data(NULL),
+        private_data_length(0),
+        pose_yaw(kValueNotPresent),
+        pose_pitch(kValueNotPresent),
+        pose_roll(kValueNotPresent) {}
+  ~Projection() { delete[] private_data; }
+  static bool Parse(IMkvReader* reader, long long element_start,
+                    long long element_size, Projection** projection);
+
+  ProjectionType type;
+  unsigned char* private_data;
+  size_t private_data_length;
+  float pose_yaw;
+  float pose_pitch;
+  float pose_roll;
+};
+
 class VideoTrack : public Track {
   VideoTrack(const VideoTrack&);
   VideoTrack& operator=(const VideoTrack&);
@@ -496,6 +525,8 @@ class VideoTrack : public Track {
 
   Colour* GetColour() const;
 
+  Projection* GetProjection() const;
+
  private:
   long long m_width;
   long long m_height;
@@ -507,6 +538,7 @@ class VideoTrack : public Track {
   double m_rate;
 
   Colour* m_colour;
+  Projection* m_projection;
 };
 
 class AudioTrack : public Track {
@@ -812,6 +844,8 @@ class SeekHead {
   long Parse();
 
   struct Entry {
+    Entry();
+
     // the SeekHead entry payload
     long long id;
     long long pos;