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 <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  VLOG(2) << "Child " << FourCCToString(box_type) << " size 0x" << std::hex
83  << box_size << std::dec;
84  RCHECK(SkipBytes(box_size));
85  }
86 
87  return true;
88 }
89 
90 bool BoxReader::ReadChild(Box* child) {
91  DCHECK(scanned_);
92  FourCC child_type = child->BoxType();
93 
94  ChildMap::iterator itr = children_.find(child_type);
95  RCHECK(itr != children_.end());
96  DVLOG(2) << "Found a " << FourCCToString(child_type) << " box.";
97  RCHECK(child->Parse(itr->second.get()));
98  children_.erase(itr);
99  return true;
100 }
101 
103  return children_.count(child->BoxType()) > 0;
104 }
105 
107  if (!children_.count(child->BoxType()))
108  return true;
109  return ReadChild(child);
110 }
111 
112 bool BoxReader::ReadHeader(bool* err) {
113  uint64_t size = 0;
114  *err = false;
115 
116  if (!ReadNBytesInto8(&size, sizeof(uint32_t)) || !ReadFourCC(&type_))
117  return false;
118 
119  if (size == 0) {
120  // Boxes that run to EOS are not supported.
121  NOTIMPLEMENTED() << base::StringPrintf("Box '%s' run to EOS.",
122  FourCCToString(type_).c_str());
123  *err = true;
124  return false;
125  } else if (size == 1) {
126  if (!Read8(&size))
127  return false;
128  }
129 
130  // The box should have at least the size of what have been parsed.
131  if (size < pos()) {
132  LOG(ERROR) << base::StringPrintf("Box '%s' with size (%" PRIu64
133  ") is invalid.",
134  FourCCToString(type_).c_str(),
135  size);
136  *err = true;
137  return false;
138  }
139 
140  // 'mdat' box could have a 64-bit size; other boxes should be very small.
141  if (size > static_cast<uint64_t>(std::numeric_limits<int32_t>::max()) &&
142  type_ != FOURCC_mdat) {
143  LOG(ERROR) << base::StringPrintf("Box '%s' size (%" PRIu64
144  ") is too large.",
145  FourCCToString(type_).c_str(),
146  size);
147  *err = true;
148  return false;
149  }
150 
151  // Note that the pos_ head has advanced to the byte immediately after the
152  // header, which is where we want it.
153  set_size(size);
154  return true;
155 }
156 
157 } // namespace mp4
158 } // namespace media
159 } // namespace shaka
shaka::media::mp4::BoxReader::ReadChild
bool ReadChild(Box *child) WARN_UNUSED_RESULT
Definition: box_reader.cc:90
shaka
All the methods that are virtual are virtual for mocking.
Definition: gflags_hex_bytes.cc:11
shaka::media::BufferReader::SkipBytes
bool SkipBytes(size_t num_bytes) WARN_UNUSED_RESULT
Definition: buffer_reader.cc:77
shaka::media::mp4::BoxReader::StartBox
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
shaka::media::BufferReader::ReadNBytesInto8
bool ReadNBytesInto8(uint64_t *v, size_t num_bytes) WARN_UNUSED_RESULT
Definition: buffer_reader.cc:40
shaka::media::mp4::BoxReader::ReadBox
static BoxReader * ReadBox(const uint8_t *buf, const size_t buf_size, bool *err)
Definition: box_reader.cc:36
shaka::media::mp4::BoxReader
Class for reading MP4 boxes.
Definition: box_reader.h:25
shaka::media::mp4::BoxReader::TryReadChild
bool TryReadChild(Box *child) WARN_UNUSED_RESULT
Definition: box_reader.cc:106
shaka::media::mp4::BoxReader::ChildExist
bool ChildExist(Box *child) WARN_UNUSED_RESULT
Definition: box_reader.cc:102
shaka::media::mp4::Box
Definition: box.h:27
shaka::media::mp4::BoxReader::ScanChildren
bool ScanChildren() WARN_UNUSED_RESULT
Definition: box_reader.cc:67
shaka::media::mp4::Box::BoxType
virtual FourCC BoxType() const =0
shaka::media::mp4::Box::Parse
bool Parse(BoxReader *reader)
Definition: box.cc:19