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