cpp/spectrum/codecs/isobmff/IsoBmffParser.cpp (108 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#include "IsoBmffParser.h"
#include <spectrum/core/utils/Endianness.h>
#include <cstring>
#include <limits>
namespace facebook {
namespace spectrum {
namespace codecs {
namespace isobmff {
namespace error {
const folly::StringPiece IsoBmffEarlyStreamEnd{"isobmff_early_stream_end"};
const folly::StringPiece IsoBmffBoxTooLarge{"isobmff_box_too_large"};
const folly::StringPiece IsoBmffFtypBoxTooSmall{"isobmff_ftyp_box_too_small"};
const folly::StringPiece IsoBmffFtypBoxExpectedButNotFound{
    "isobmff_ftyp_box_expected_but_not_found"};
} // namespace error
namespace boxtype {
constexpr auto uuid = folly::StringPiece{"uuid"};
constexpr auto ftyp = folly::StringPiece{"ftyp"};
} // namespace boxtype
namespace {
const bool isUuidType(const std::array<std::uint8_t, 4>& boxType) {
  return std::strncmp(
             boxtype::uuid.begin(),
             reinterpret_cast<const char*>(boxType.data()),
             boxType.size()) == 0;
}
} // namespace
Parser::Parser(io::IImageSource& source, const std::uint64_t maxBoxSize)
    : _source(source), _maxBoxSize(maxBoxSize){};
BoxHeader Parser::parseBoxHeader() {
  BoxHeader result;
  std::uint32_t size;
  SPECTRUM_ERROR_IF_NOT(
      _source.read(reinterpret_cast<char*>(&size), 4) == 4,
      error::IsoBmffEarlyStreamEnd);
  size = core::utils::convertValueToNativeByteOrder(size, false);
  SPECTRUM_ERROR_IF(size > _maxBoxSize, error::IsoBmffBoxTooLarge);
  SPECTRUM_ERROR_IF_NOT(
      _source.read(
          reinterpret_cast<char*>(result.boxType.data()),
          result.boxType.size()) == result.boxType.size(),
      error::IsoBmffEarlyStreamEnd);
  // handle special size cases: unlimited or 64-bit size
  if (size == 1) {
    std::uint64_t largeSize;
    SPECTRUM_ERROR_IF_NOT(
        _source.read(reinterpret_cast<char*>(&largeSize), 8) == 8,
        error::IsoBmffEarlyStreamEnd);
    result.size = core::utils::convertValueToNativeByteOrder(largeSize, false);
    SPECTRUM_ERROR_IF(largeSize > _maxBoxSize, error::IsoBmffBoxTooLarge);
  } else if (size == 0) {
    SPECTRUM_ERROR_IF(
        _maxBoxSize != std::numeric_limits<std::uint64_t>::max(),
        error::IsoBmffBoxTooLarge);
    result.size = folly::none;
  } else {
    result.size = size;
  }
  // read custom user ID if ftyp is `uuid`
  if (isUuidType(result.boxType)) {
    SPECTRUM_ERROR_IF_NOT(
        _source.read(
            reinterpret_cast<char*>(result.userType.data()),
            result.userType.size()) == result.userType.size(),
        error::IsoBmffEarlyStreamEnd);
  } else {
    std::memset(result.userType.data(), 0, result.userType.size());
  }
  return result;
}
FtypBox Parser::parseFtypBox() {
  FtypBox result;
  result.boxHeader = parseBoxHeader();
  // Do not allow ftyp boxes with unlimited length
  SPECTRUM_ENFORCE_IF_NOT(result.boxHeader.size.hasValue());
  SPECTRUM_ERROR_IF_NOT(
      *result.boxHeader.size >= 8, error::IsoBmffFtypBoxTooSmall);
  SPECTRUM_ERROR_IF(
      std::strncmp(
          reinterpret_cast<const char*>(result.boxHeader.boxType.data()),
          boxtype::ftyp.begin(),
          boxtype::ftyp.size()) != 0,
      error::IsoBmffFtypBoxExpectedButNotFound);
  SPECTRUM_ERROR_IF_NOT(
      _source.read(
          reinterpret_cast<char*>(result.majorBrand.data()),
          result.majorBrand.size()) == result.majorBrand.size(),
      error::IsoBmffEarlyStreamEnd);
  SPECTRUM_ERROR_IF_NOT(
      _source.read(
          reinterpret_cast<char*>(result.minorVersion.data()),
          result.minorVersion.size()) == result.minorVersion.size(),
      error::IsoBmffEarlyStreamEnd);
  uint64_t remainingSize = *result.boxHeader.size - 8;
  Brand brand;
  SPECTRUM_ENFORCE_IF_NOT(remainingSize % brand.size() == 0);
  while (remainingSize >= brand.size()) {
    SPECTRUM_ERROR_IF_NOT(
        _source.read(reinterpret_cast<char*>(brand.data()), brand.size()) ==
            brand.size(),
        error::IsoBmffEarlyStreamEnd);
    result.compatibleBrands.push_back(brand);
    remainingSize -= brand.size();
  }
  result.compatibleBrands.shrink_to_fit();
  return result;
}
} // namespace isobmff
} // namespace codecs
} // namespace spectrum
} // namespace facebook