• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 "media/formats/mp4/box_reader.h"
6 
7 #include <string.h>
8 #include <algorithm>
9 #include <map>
10 #include <set>
11 
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "media/formats/mp4/box_definitions.h"
15 #include "media/formats/mp4/rcheck.h"
16 
17 namespace media {
18 namespace mp4 {
19 
~Box()20 Box::~Box() {}
21 
Read1(uint8 * v)22 bool BufferReader::Read1(uint8* v) {
23   RCHECK(HasBytes(1));
24   *v = buf_[pos_++];
25   return true;
26 }
27 
28 // Internal implementation of multi-byte reads
Read(T * v)29 template<typename T> bool BufferReader::Read(T* v) {
30   RCHECK(HasBytes(sizeof(T)));
31 
32   T tmp = 0;
33   for (size_t i = 0; i < sizeof(T); i++) {
34     tmp <<= 8;
35     tmp += buf_[pos_++];
36   }
37   *v = tmp;
38   return true;
39 }
40 
Read2(uint16 * v)41 bool BufferReader::Read2(uint16* v) { return Read(v); }
Read2s(int16 * v)42 bool BufferReader::Read2s(int16* v) { return Read(v); }
Read4(uint32 * v)43 bool BufferReader::Read4(uint32* v) { return Read(v); }
Read4s(int32 * v)44 bool BufferReader::Read4s(int32* v) { return Read(v); }
Read8(uint64 * v)45 bool BufferReader::Read8(uint64* v) { return Read(v); }
Read8s(int64 * v)46 bool BufferReader::Read8s(int64* v) { return Read(v); }
47 
ReadFourCC(FourCC * v)48 bool BufferReader::ReadFourCC(FourCC* v) {
49   return Read4(reinterpret_cast<uint32*>(v));
50 }
51 
ReadVec(std::vector<uint8> * vec,int count)52 bool BufferReader::ReadVec(std::vector<uint8>* vec, int count) {
53   RCHECK(HasBytes(count));
54   vec->clear();
55   vec->insert(vec->end(), buf_ + pos_, buf_ + pos_ + count);
56   pos_ += count;
57   return true;
58 }
59 
SkipBytes(int bytes)60 bool BufferReader::SkipBytes(int bytes) {
61   RCHECK(HasBytes(bytes));
62   pos_ += bytes;
63   return true;
64 }
65 
Read4Into8(uint64 * v)66 bool BufferReader::Read4Into8(uint64* v) {
67   uint32 tmp;
68   RCHECK(Read4(&tmp));
69   *v = tmp;
70   return true;
71 }
72 
Read4sInto8s(int64 * v)73 bool BufferReader::Read4sInto8s(int64* v) {
74   // Beware of the need for sign extension.
75   int32 tmp;
76   RCHECK(Read4s(&tmp));
77   *v = tmp;
78   return true;
79 }
80 
81 
BoxReader(const uint8 * buf,const int size,const LogCB & log_cb)82 BoxReader::BoxReader(const uint8* buf, const int size,
83                      const LogCB& log_cb)
84     : BufferReader(buf, size),
85       log_cb_(log_cb),
86       type_(FOURCC_NULL),
87       version_(0),
88       flags_(0),
89       scanned_(false) {
90 }
91 
~BoxReader()92 BoxReader::~BoxReader() {
93   if (scanned_ && !children_.empty()) {
94     for (ChildMap::iterator itr = children_.begin();
95          itr != children_.end(); ++itr) {
96       DVLOG(1) << "Skipping unknown box: " << FourCCToString(itr->first);
97     }
98   }
99 }
100 
101 // static
ReadTopLevelBox(const uint8 * buf,const int buf_size,const LogCB & log_cb,bool * err)102 BoxReader* BoxReader::ReadTopLevelBox(const uint8* buf,
103                                       const int buf_size,
104                                       const LogCB& log_cb,
105                                       bool* err) {
106   scoped_ptr<BoxReader> reader(new BoxReader(buf, buf_size, log_cb));
107   if (!reader->ReadHeader(err))
108     return NULL;
109 
110   if (!IsValidTopLevelBox(reader->type(), log_cb)) {
111     *err = true;
112     return NULL;
113   }
114 
115   if (reader->size() <= buf_size)
116     return reader.release();
117 
118   return NULL;
119 }
120 
121 // static
StartTopLevelBox(const uint8 * buf,const int buf_size,const LogCB & log_cb,FourCC * type,int * box_size,bool * err)122 bool BoxReader::StartTopLevelBox(const uint8* buf,
123                                  const int buf_size,
124                                  const LogCB& log_cb,
125                                  FourCC* type,
126                                  int* box_size,
127                                  bool* err) {
128   BoxReader reader(buf, buf_size, log_cb);
129   if (!reader.ReadHeader(err)) return false;
130   if (!IsValidTopLevelBox(reader.type(), log_cb)) {
131     *err = true;
132     return false;
133   }
134   *type = reader.type();
135   *box_size = reader.size();
136   return true;
137 }
138 
139 // static
IsValidTopLevelBox(const FourCC & type,const LogCB & log_cb)140 bool BoxReader::IsValidTopLevelBox(const FourCC& type,
141                                    const LogCB& log_cb) {
142   switch (type) {
143     case FOURCC_FTYP:
144     case FOURCC_PDIN:
145     case FOURCC_BLOC:
146     case FOURCC_MOOV:
147     case FOURCC_MOOF:
148     case FOURCC_MFRA:
149     case FOURCC_MDAT:
150     case FOURCC_FREE:
151     case FOURCC_SKIP:
152     case FOURCC_META:
153     case FOURCC_MECO:
154     case FOURCC_STYP:
155     case FOURCC_SIDX:
156     case FOURCC_SSIX:
157     case FOURCC_PRFT:
158     case FOURCC_UUID:
159     case FOURCC_EMSG:
160       return true;
161     default:
162       // Hex is used to show nonprintable characters and aid in debugging
163       MEDIA_LOG(log_cb) << "Unrecognized top-level box type "
164                         << FourCCToString(type);
165       return false;
166   }
167 }
168 
ScanChildren()169 bool BoxReader::ScanChildren() {
170   DCHECK(!scanned_);
171   scanned_ = true;
172 
173   bool err = false;
174   while (pos() < size()) {
175     BoxReader child(&buf_[pos_], size_ - pos_, log_cb_);
176     if (!child.ReadHeader(&err)) break;
177 
178     children_.insert(std::pair<FourCC, BoxReader>(child.type(), child));
179     pos_ += child.size();
180   }
181 
182   DCHECK(!err);
183   return !err && pos() == size();
184 }
185 
HasChild(Box * child)186 bool BoxReader::HasChild(Box* child) {
187   DCHECK(scanned_);
188   DCHECK(child);
189   return children_.count(child->BoxType()) > 0;
190 }
191 
ReadChild(Box * child)192 bool BoxReader::ReadChild(Box* child) {
193   DCHECK(scanned_);
194   FourCC child_type = child->BoxType();
195 
196   ChildMap::iterator itr = children_.find(child_type);
197   RCHECK(itr != children_.end());
198   DVLOG(2) << "Found a " << FourCCToString(child_type) << " box.";
199   RCHECK(child->Parse(&itr->second));
200   children_.erase(itr);
201   return true;
202 }
203 
MaybeReadChild(Box * child)204 bool BoxReader::MaybeReadChild(Box* child) {
205   if (!children_.count(child->BoxType())) return true;
206   return ReadChild(child);
207 }
208 
ReadFullBoxHeader()209 bool BoxReader::ReadFullBoxHeader() {
210   uint32 vflags;
211   RCHECK(Read4(&vflags));
212   version_ = vflags >> 24;
213   flags_ = vflags & 0xffffff;
214   return true;
215 }
216 
ReadHeader(bool * err)217 bool BoxReader::ReadHeader(bool* err) {
218   uint64 size = 0;
219   *err = false;
220 
221   if (!HasBytes(8)) return false;
222   CHECK(Read4Into8(&size) && ReadFourCC(&type_));
223 
224   if (size == 0) {
225     // Media Source specific: we do not support boxes that run to EOS.
226     *err = true;
227     return false;
228   } else if (size == 1) {
229     if (!HasBytes(8)) return false;
230     CHECK(Read8(&size));
231   }
232 
233   // Implementation-specific: support for boxes larger than 2^31 has been
234   // removed.
235   if (size < static_cast<uint64>(pos_) ||
236       size > static_cast<uint64>(kint32max)) {
237     *err = true;
238     return false;
239   }
240 
241   // Note that the pos_ head has advanced to the byte immediately after the
242   // header, which is where we want it.
243   size_ = size;
244   return true;
245 }
246 
247 }  // namespace mp4
248 }  // namespace media
249