DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
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 
11 #include "packager/base/logging.h"
12 #include "packager/base/memory/scoped_ptr.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  delete itr->second;
32  }
33  }
34 }
35 
36 // static
38  const size_t buf_size,
39  bool* err) {
40  scoped_ptr<BoxReader> reader(new BoxReader(buf, buf_size));
41  if (!reader->ReadHeader(err))
42  return NULL;
43 
44  // We don't require the complete box to be available for MDAT box.
45  if (reader->type() == FOURCC_mdat)
46  return reader.release();
47 
48  if (!IsValidTopLevelBox(reader->type())) {
49  *err = true;
50  return NULL;
51  }
52 
53  if (reader->size() <= buf_size)
54  return reader.release();
55 
56  return NULL;
57 }
58 
59 // static
60 bool BoxReader::StartTopLevelBox(const uint8_t* buf,
61  const size_t buf_size,
62  FourCC* type,
63  uint64_t* box_size,
64  bool* err) {
65  BoxReader reader(buf, buf_size);
66  if (!reader.ReadHeader(err))
67  return false;
68  if (!IsValidTopLevelBox(reader.type())) {
69  *err = true;
70  return false;
71  }
72  *type = reader.type();
73  *box_size = reader.size();
74  return true;
75 }
76 
77 // static
78 bool BoxReader::IsValidTopLevelBox(const FourCC& type) {
79  switch (type) {
80  case FOURCC_ftyp:
81  case FOURCC_pdin:
82  case FOURCC_bloc:
83  case FOURCC_moov:
84  case FOURCC_moof:
85  case FOURCC_mfra:
86  case FOURCC_mdat:
87  case FOURCC_free:
88  case FOURCC_skip:
89  case FOURCC_meta:
90  case FOURCC_meco:
91  case FOURCC_styp:
92  case FOURCC_sidx:
93  case FOURCC_ssix:
94  case FOURCC_prft:
95  case FOURCC_uuid:
96  return true;
97  default:
98  // Hex is used to show nonprintable characters and aid in debugging
99  LOG(ERROR) << "Unrecognized top-level box type 0x" << std::hex << type;
100  return false;
101  }
102 }
103 
105  DCHECK(!scanned_);
106  scanned_ = true;
107 
108  while (pos() < size()) {
109  scoped_ptr<BoxReader> child(
110  new BoxReader(&data()[pos()], size() - pos()));
111  bool err;
112  if (!child->ReadHeader(&err))
113  return false;
114 
115  FourCC box_type = child->type();
116  size_t box_size = child->size();
117  children_.insert(std::pair<FourCC, BoxReader*>(box_type, child.release()));
118  RCHECK(SkipBytes(box_size));
119  }
120 
121  return true;
122 }
123 
124 bool BoxReader::ReadChild(Box* child) {
125  DCHECK(scanned_);
126  FourCC child_type = child->BoxType();
127 
128  ChildMap::iterator itr = children_.find(child_type);
129  RCHECK(itr != children_.end());
130  DVLOG(2) << "Found a " << FourCCToString(child_type) << " box.";
131  RCHECK(child->Parse(itr->second));
132  delete itr->second;
133  children_.erase(itr);
134  return true;
135 }
136 
138  return children_.count(child->BoxType()) > 0;
139 }
140 
142  if (!children_.count(child->BoxType()))
143  return true;
144  return ReadChild(child);
145 }
146 
147 bool BoxReader::ReadHeader(bool* err) {
148  uint64_t size = 0;
149  *err = false;
150 
151  if (!ReadNBytesInto8(&size, sizeof(uint32_t)) || !ReadFourCC(&type_))
152  return false;
153 
154  if (size == 0) {
155  // Boxes that run to EOS are not supported.
156  NOTIMPLEMENTED() << base::StringPrintf("Box '%s' run to EOS.",
157  FourCCToString(type_).c_str());
158  *err = true;
159  return false;
160  } else if (size == 1) {
161  if (!Read8(&size))
162  return false;
163  }
164 
165  // The box should have at least the size of what have been parsed.
166  if (size < pos()) {
167  LOG(ERROR) << base::StringPrintf("Box '%s' with size (%" PRIu64
168  ") is invalid.",
169  FourCCToString(type_).c_str(),
170  size);
171  *err = true;
172  return false;
173  }
174 
175  // 'mdat' box could have a 64-bit size; other boxes should be very small.
176  if (size > static_cast<uint64_t>(std::numeric_limits<int32_t>::max()) &&
177  type_ != FOURCC_mdat) {
178  LOG(ERROR) << base::StringPrintf("Box '%s' size (%" PRIu64
179  ") is too large.",
180  FourCCToString(type_).c_str(),
181  size);
182  *err = true;
183  return false;
184  }
185 
186  // Note that the pos_ head has advanced to the byte immediately after the
187  // header, which is where we want it.
188  set_size(size);
189  return true;
190 }
191 
192 } // namespace mp4
193 } // namespace media
194 } // namespace shaka
virtual FourCC BoxType() const =0
static BoxReader * ReadTopLevelBox(const uint8_t *buf, const size_t buf_size, bool *err)
Definition: box_reader.cc:37
static bool IsValidTopLevelBox(const FourCC &type)
Definition: box_reader.cc:78
bool ScanChildren() WARN_UNUSED_RESULT
Definition: box_reader.cc:104
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:24
bool ReadChild(Box *child) WARN_UNUSED_RESULT
Definition: box_reader.cc:124
bool Parse(BoxReader *reader)
Definition: box.cc:19
bool TryReadChild(Box *child) WARN_UNUSED_RESULT
Definition: box_reader.cc:141
bool ChildExist(Box *child) WARN_UNUSED_RESULT
Definition: box_reader.cc:137
static bool StartTopLevelBox(const uint8_t *buf, const size_t buf_size, FourCC *type, uint64_t *box_size, bool *err) WARN_UNUSED_RESULT
Definition: box_reader.cc:60