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