123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- // Copyright 2014 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "media/formats/mp4/box_reader.h"
- #include <stddef.h>
- #include <string.h>
- #include <algorithm>
- #include <set>
- #include "base/big_endian.h"
- #include "media/formats/mp4/box_definitions.h"
- namespace media {
- namespace mp4 {
- Box::~Box() = default;
- bool BufferReader::Read1(uint8_t* v) {
- RCHECK(HasBytes(1));
- *v = buf_[pos_++];
- return true;
- }
- // Internal implementation of multi-byte reads
- template<typename T> bool BufferReader::Read(T* v) {
- RCHECK(HasBytes(sizeof(T)));
- // MPEG-4 uses big endian byte order
- base::ReadBigEndian(buf_ + pos_, v);
- pos_ += sizeof(T);
- return true;
- }
- bool BufferReader::Read2(uint16_t* v) {
- return Read(v);
- }
- bool BufferReader::Read2s(int16_t* v) {
- return Read(v);
- }
- bool BufferReader::Read4(uint32_t* v) {
- return Read(v);
- }
- bool BufferReader::Read4s(int32_t* v) {
- return Read(v);
- }
- bool BufferReader::Read8(uint64_t* v) {
- return Read(v);
- }
- bool BufferReader::Read8s(int64_t* v) {
- return Read(v);
- }
- bool BufferReader::ReadFourCC(FourCC* v) {
- return Read4(reinterpret_cast<uint32_t*>(v));
- }
- bool BufferReader::ReadVec(std::vector<uint8_t>* vec, uint64_t count) {
- RCHECK(HasBytes(count));
- vec->clear();
- vec->insert(vec->end(), buf_ + pos_, buf_ + pos_ + count);
- pos_ += count;
- return true;
- }
- bool BufferReader::SkipBytes(uint64_t bytes) {
- RCHECK(HasBytes(bytes));
- pos_ += bytes;
- return true;
- }
- bool BufferReader::Read4Into8(uint64_t* v) {
- uint32_t tmp;
- RCHECK(Read4(&tmp));
- *v = tmp;
- return true;
- }
- bool BufferReader::Read4sInto8s(int64_t* v) {
- // Beware of the need for sign extension.
- int32_t tmp;
- RCHECK(Read4s(&tmp));
- *v = tmp;
- return true;
- }
- BoxReader::BoxReader(const uint8_t* buf,
- const size_t buf_size,
- MediaLog* media_log,
- bool is_EOS)
- : BufferReader(buf, buf_size),
- media_log_(media_log),
- box_size_(0),
- box_size_known_(false),
- type_(FOURCC_NULL),
- version_(0),
- flags_(0),
- scanned_(false),
- is_EOS_(is_EOS) {}
- BoxReader::BoxReader(const BoxReader& other) = default;
- BoxReader::~BoxReader() {
- if (scanned_ && !children_.empty()) {
- for (auto itr = children_.begin(); itr != children_.end(); ++itr) {
- DVLOG(1) << "Skipping unknown box: " << FourCCToString(itr->first);
- }
- }
- }
- // static
- ParseResult BoxReader::ReadTopLevelBox(const uint8_t* buf,
- const size_t buf_size,
- MediaLog* media_log,
- std::unique_ptr<BoxReader>* out_reader) {
- DCHECK(out_reader);
- std::unique_ptr<BoxReader> reader(
- new BoxReader(buf, buf_size, media_log, false));
- RCHECK_OK_PARSE_RESULT(reader->ReadHeader());
- if (!IsValidTopLevelBox(reader->type(), media_log))
- return ParseResult::kError;
- *out_reader = std::move(reader);
- return ParseResult::kOk;
- }
- // static
- ParseResult BoxReader::StartTopLevelBox(const uint8_t* buf,
- const size_t buf_size,
- MediaLog* media_log,
- FourCC* out_type,
- size_t* out_box_size) {
- std::unique_ptr<BoxReader> reader;
- RCHECK_OK_PARSE_RESULT(ReadTopLevelBox(buf, buf_size, media_log, &reader));
- *out_type = reader->type();
- *out_box_size = reader->box_size();
- return ParseResult::kOk;
- }
- // static
- BoxReader* BoxReader::ReadConcatentatedBoxes(const uint8_t* buf,
- const size_t buf_size,
- MediaLog* media_log) {
- BoxReader* reader = new BoxReader(buf, buf_size, media_log, true);
- // Concatenated boxes are passed in without a wrapping parent box. Set
- // |box_size_| to the concatenated buffer length to mimic having already
- // parsed the parent box.
- reader->box_size_ = buf_size;
- reader->box_size_known_ = true;
- return reader;
- }
- // static
- bool BoxReader::IsValidTopLevelBox(const FourCC& type, MediaLog* media_log) {
- switch (type) {
- case FOURCC_FTYP:
- case FOURCC_PDIN:
- case FOURCC_BLOC:
- case FOURCC_MOOV:
- case FOURCC_MOOF:
- case FOURCC_MFRA:
- case FOURCC_MDAT:
- case FOURCC_FREE:
- case FOURCC_SKIP:
- case FOURCC_META:
- case FOURCC_MECO:
- case FOURCC_STYP:
- case FOURCC_SIDX:
- case FOURCC_SSIX:
- case FOURCC_PRFT:
- case FOURCC_UUID:
- case FOURCC_EMSG:
- return true;
- default:
- // Hex is used to show nonprintable characters and aid in debugging.
- MEDIA_LOG(ERROR, media_log)
- << "Invalid top-level ISO BMFF box type " << FourCCToString(type);
- return false;
- }
- }
- bool BoxReader::ScanChildren() {
- // Must be able to trust box_size_ below.
- RCHECK(box_size_known_);
- DCHECK(!scanned_);
- scanned_ = true;
- DCHECK_LE(pos_, box_size_);
- while (pos_ < box_size_) {
- BoxReader child(&buf_[pos_], box_size_ - pos_, media_log_, is_EOS_);
- if (child.ReadHeader() != ParseResult::kOk)
- return false;
- children_.insert(std::pair<FourCC, BoxReader>(child.type(), child));
- pos_ += child.box_size();
- }
- DCHECK_EQ(pos_, box_size_);
- return true;
- }
- bool BoxReader::ReadDisplayMatrix(DisplayMatrix matrix) {
- for (int i = 0; i < kDisplayMatrixDimension; i++) {
- if (!Read4s(&matrix[i])) {
- return false;
- }
- }
- return true;
- }
- bool BoxReader::HasChild(Box* child) {
- DCHECK(scanned_);
- DCHECK(child);
- return children_.count(child->BoxType()) > 0;
- }
- bool BoxReader::ReadChild(Box* child) {
- DCHECK(scanned_);
- FourCC child_type = child->BoxType();
- auto itr = children_.find(child_type);
- RCHECK(itr != children_.end());
- DVLOG(2) << "Found a " << FourCCToString(child_type) << " box.";
- RCHECK(child->Parse(&itr->second));
- children_.erase(itr);
- return true;
- }
- bool BoxReader::MaybeReadChild(Box* child) {
- if (!children_.count(child->BoxType())) return true;
- return ReadChild(child);
- }
- bool BoxReader::ReadFullBoxHeader() {
- uint32_t vflags;
- RCHECK(Read4(&vflags));
- version_ = vflags >> 24;
- flags_ = vflags & 0xffffff;
- return true;
- }
- ParseResult BoxReader::ReadHeader() {
- uint64_t box_size = 0;
- if (!HasBytes(8))
- return is_EOS_ ? ParseResult::kError : ParseResult::kNeedMoreData;
- CHECK(Read4Into8(&box_size));
- CHECK(ReadFourCC(&type_));
- if (box_size == 0) {
- if (is_EOS_) {
- // All the data bytes are expected to be provided.
- // TODO(sandersd): The whole |is_EOS_| feature seems to exist just for
- // this special case (and is used only for PSSH parsing). Can we get rid
- // of it? The caller can treat kNeedMoreData as an error, and the only
- // difference would be lack of support for |box_size == 0|.
- box_size = base::strict_cast<uint64_t>(buf_size_);
- } else {
- MEDIA_LOG(DEBUG, media_log_)
- << "ISO BMFF boxes that run to EOS are not supported";
- return ParseResult::kError;
- }
- } else if (box_size == 1) {
- if (!HasBytes(8))
- return is_EOS_ ? ParseResult::kError : ParseResult::kNeedMoreData;
- CHECK(Read8(&box_size));
- }
- // Implementation-specific: support for boxes larger than 2^31 has been
- // removed.
- if (box_size < base::strict_cast<uint64_t>(pos_) ||
- box_size > static_cast<uint64_t>(std::numeric_limits<int32_t>::max())) {
- return ParseResult::kError;
- }
- // Make sure the buffer contains at least the expected number of bytes.
- // Since the data may be appended in pieces, this is only an error if EOS.
- if (box_size > base::strict_cast<uint64_t>(buf_size_))
- return is_EOS_ ? ParseResult::kError : ParseResult::kNeedMoreData;
- // Note that the pos_ head has advanced to the byte immediately after the
- // header, which is where we want it.
- box_size_ = base::checked_cast<size_t>(box_size);
- box_size_known_ = true;
- // We don't want future reads to go beyond the box.
- buf_size_ = std::min(buf_size_, box_size_);
- return ParseResult::kOk;
- }
- } // namespace mp4
- } // namespace media
|