Shaka Packager SDK
box_reader.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <packager/media/formats/mp4/box_reader.h>
6 
7 #include <cinttypes>
8 #include <limits>
9 #include <memory>
10 
11 #include <absl/log/check.h>
12 #include <absl/log/log.h>
13 #include <absl/strings/str_format.h>
14 
15 #include <packager/macros/logging.h>
16 #include <packager/media/formats/mp4/box.h>
17 
18 namespace shaka {
19 namespace media {
20 namespace mp4 {
21 
22 BoxReader::BoxReader(const uint8_t* buf, size_t size)
23  : BufferReader(buf, size), type_(FOURCC_NULL), scanned_(false) {
24  DCHECK(buf);
25  DCHECK_LT(0u, size);
26 }
27 
28 BoxReader::~BoxReader() {
29  if (scanned_ && !children_.empty()) {
30  for (ChildMap::iterator itr = children_.begin(); itr != children_.end();
31  ++itr) {
32  DVLOG(1) << "Skipping unknown box: " << FourCCToString(itr->first);
33  }
34  }
35 }
36 
37 // static
38 BoxReader* BoxReader::ReadBox(const uint8_t* buf,
39  const size_t buf_size,
40  bool* err) {
41  std::unique_ptr<BoxReader> reader(new BoxReader(buf, buf_size));
42  if (!reader->ReadHeader(err))
43  return NULL;
44 
45  // We don't require the complete box to be available for MDAT box.
46  if (reader->type() == FOURCC_mdat)
47  return reader.release();
48 
49  if (reader->size() <= buf_size)
50  return reader.release();
51 
52  return NULL;
53 }
54 
55 // static
56 bool BoxReader::StartBox(const uint8_t* buf,
57  const size_t buf_size,
58  FourCC* type,
59  uint64_t* box_size,
60  bool* err) {
61  BoxReader reader(buf, buf_size);
62  if (!reader.ReadHeader(err))
63  return false;
64  *type = reader.type();
65  *box_size = reader.size();
66  return true;
67 }
68 
69 bool BoxReader::ScanChildren() {
70  DCHECK(!scanned_);
71  scanned_ = true;
72 
73  while (pos() < size()) {
74  std::unique_ptr<BoxReader> child(
75  new BoxReader(&data()[pos()], size() - pos()));
76  bool err;
77  if (!child->ReadHeader(&err))
78  return false;
79 
80  FourCC box_type = child->type();
81  size_t box_size = child->size();
82  children_.insert(std::pair<FourCC, std::unique_ptr<BoxReader>>(
83  box_type, std::move(child)));
84  VLOG(2) << "Child " << FourCCToString(box_type) << " size 0x" << std::hex
85  << box_size << std::dec;
86  RCHECK(SkipBytes(box_size));
87  }
88 
89  return true;
90 }
91 
92 bool BoxReader::ReadChild(Box* child) {
93  DCHECK(scanned_);
94  FourCC child_type = child->BoxType();
95 
96  ChildMap::iterator itr = children_.find(child_type);
97  RCHECK(itr != children_.end());
98  DVLOG(2) << "Found a " << FourCCToString(child_type) << " box.";
99  RCHECK(child->Parse(itr->second.get()));
100  children_.erase(itr);
101  return true;
102 }
103 
104 bool BoxReader::ChildExist(Box* child) {
105  return children_.count(child->BoxType()) > 0;
106 }
107 
108 bool BoxReader::TryReadChild(Box* child) {
109  if (!children_.count(child->BoxType()))
110  return true;
111  return ReadChild(child);
112 }
113 
114 bool BoxReader::ReadHeader(bool* err) {
115  uint64_t size = 0;
116  *err = false;
117 
118  if (!ReadNBytesInto8(&size, sizeof(uint32_t)) || !ReadFourCC(&type_))
119  return false;
120 
121  if (size == 0) {
122  // Boxes that run to EOS are not supported.
123  NOTIMPLEMENTED() << absl::StrFormat("Box '%s' run to EOS.",
124  FourCCToString(type_).c_str());
125  *err = true;
126  return false;
127  } else if (size == 1) {
128  if (!Read8(&size))
129  return false;
130  }
131 
132  // The box should have at least the size of what have been parsed.
133  if (size < pos()) {
134  LOG(ERROR) << absl::StrFormat("Box '%s' with size (%" PRIu64
135  ") is invalid.",
136  FourCCToString(type_).c_str(), size);
137  *err = true;
138  return false;
139  }
140 
141  // 'mdat' box could have a 64-bit size; other boxes should be very small.
142  if (size > static_cast<uint64_t>(std::numeric_limits<int32_t>::max()) &&
143  type_ != FOURCC_mdat) {
144  LOG(ERROR) << absl::StrFormat("Box '%s' size (%" PRIu64 ") is too large.",
145  FourCCToString(type_).c_str(), size);
146  *err = true;
147  return false;
148  }
149 
150  // Note that the pos_ head has advanced to the byte immediately after the
151  // header, which is where we want it.
152  set_size(size);
153  return true;
154 }
155 
156 } // namespace mp4
157 } // namespace media
158 } // namespace shaka
Class for reading MP4 boxes.
Definition: box_reader.h:28
All the methods that are virtual are virtual for mocking.
Definition: crypto_flags.cc:66
bool Parse(BoxReader *reader)
Definition: box.cc:21
virtual FourCC BoxType() const =0