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