DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs 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 edash_packager {
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  return true;
96  default:
97  // Hex is used to show nonprintable characters and aid in debugging
98  LOG(ERROR) << "Unrecognized top-level box type 0x" << std::hex << type;
99  return false;
100  }
101 }
102 
104  DCHECK(!scanned_);
105  scanned_ = true;
106 
107  while (pos() < size()) {
108  scoped_ptr<BoxReader> child(
109  new BoxReader(&data()[pos()], size() - pos()));
110  bool err;
111  if (!child->ReadHeader(&err))
112  return false;
113 
114  FourCC box_type = child->type();
115  size_t box_size = child->size();
116  children_.insert(std::pair<FourCC, BoxReader*>(box_type, child.release()));
117  RCHECK(SkipBytes(box_size));
118  }
119 
120  return true;
121 }
122 
123 bool BoxReader::ReadChild(Box* child) {
124  DCHECK(scanned_);
125  FourCC child_type = child->BoxType();
126 
127  ChildMap::iterator itr = children_.find(child_type);
128  RCHECK(itr != children_.end());
129  DVLOG(2) << "Found a " << FourCCToString(child_type) << " box.";
130  RCHECK(child->Parse(itr->second));
131  delete itr->second;
132  children_.erase(itr);
133  return true;
134 }
135 
137  return children_.count(child->BoxType()) > 0;
138 }
139 
141  if (!children_.count(child->BoxType()))
142  return true;
143  return ReadChild(child);
144 }
145 
146 bool BoxReader::ReadHeader(bool* err) {
147  uint64_t size = 0;
148  *err = false;
149 
150  if (!ReadNBytesInto8(&size, sizeof(uint32_t)) || !ReadFourCC(&type_))
151  return false;
152 
153  if (size == 0) {
154  // Boxes that run to EOS are not supported.
155  NOTIMPLEMENTED() << base::StringPrintf("Box '%s' run to EOS.",
156  FourCCToString(type_).c_str());
157  *err = true;
158  return false;
159  } else if (size == 1) {
160  if (!Read8(&size))
161  return false;
162  }
163 
164  // The box should have at least the size of what have been parsed.
165  if (size < pos()) {
166  LOG(ERROR) << base::StringPrintf("Box '%s' with size (%" PRIu64
167  ") is invalid.",
168  FourCCToString(type_).c_str(),
169  size);
170  *err = true;
171  return false;
172  }
173 
174  // 'mdat' box could have a 64-bit size; other boxes should be very small.
175  if (size > static_cast<uint64_t>(std::numeric_limits<int32_t>::max()) &&
176  type_ != FOURCC_MDAT) {
177  LOG(ERROR) << base::StringPrintf("Box '%s' size (%" PRIu64
178  ") is too large.",
179  FourCCToString(type_).c_str(),
180  size);
181  *err = true;
182  return false;
183  }
184 
185  // Note that the pos_ head has advanced to the byte immediately after the
186  // header, which is where we want it.
187  set_size(size);
188  return true;
189 }
190 
191 } // namespace mp4
192 } // namespace media
193 } // namespace edash_packager
bool ReadChild(Box *child) WARN_UNUSED_RESULT
Definition: box_reader.cc:123
static BoxReader * ReadTopLevelBox(const uint8_t *buf, const size_t buf_size, bool *err)
Definition: box_reader.cc:37
bool TryReadChild(Box *child) WARN_UNUSED_RESULT
Definition: box_reader.cc:140
bool ChildExist(Box *child) WARN_UNUSED_RESULT
Definition: box_reader.cc:136
static bool IsValidTopLevelBox(const FourCC &type)
Definition: box_reader.cc:78
bool ScanChildren() WARN_UNUSED_RESULT
Definition: box_reader.cc:103
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
bool SkipBytes(size_t num_bytes) WARN_UNUSED_RESULT
bool Parse(BoxReader *reader)
Definition: box.cc:19
bool ReadNBytesInto8(uint64_t *v, size_t num_bytes) WARN_UNUSED_RESULT
Class for reading MP4 boxes.
Definition: box_reader.h:24