Home | History | Annotate | Download | only in mp4
      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 "media/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/mp4/box_definitions.h"
     15 #include "media/mp4/rcheck.h"
     16 
     17 namespace media {
     18 namespace mp4 {
     19 
     20 Box::~Box() {}
     21 
     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
     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 
     41 bool BufferReader::Read2(uint16* v) { return Read(v); }
     42 bool BufferReader::Read2s(int16* v) { return Read(v); }
     43 bool BufferReader::Read4(uint32* v) { return Read(v); }
     44 bool BufferReader::Read4s(int32* v) { return Read(v); }
     45 bool BufferReader::Read8(uint64* v) { return Read(v); }
     46 bool BufferReader::Read8s(int64* v) { return Read(v); }
     47 
     48 bool BufferReader::ReadFourCC(FourCC* v) {
     49   return Read4(reinterpret_cast<uint32*>(v));
     50 }
     51 
     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 
     60 bool BufferReader::SkipBytes(int bytes) {
     61   RCHECK(HasBytes(bytes));
     62   pos_ += bytes;
     63   return true;
     64 }
     65 
     66 bool BufferReader::Read4Into8(uint64* v) {
     67   uint32 tmp;
     68   RCHECK(Read4(&tmp));
     69   *v = tmp;
     70   return true;
     71 }
     72 
     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 
     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 
     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
    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
    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
    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       return true;
    159     default:
    160       // Hex is used to show nonprintable characters and aid in debugging
    161       MEDIA_LOG(log_cb) << "Unrecognized top-level box type 0x"
    162                         << std::hex << type;
    163       return false;
    164   }
    165 }
    166 
    167 bool BoxReader::ScanChildren() {
    168   DCHECK(!scanned_);
    169   scanned_ = true;
    170 
    171   bool err = false;
    172   while (pos() < size()) {
    173     BoxReader child(&buf_[pos_], size_ - pos_, log_cb_);
    174     if (!child.ReadHeader(&err)) break;
    175 
    176     children_.insert(std::pair<FourCC, BoxReader>(child.type(), child));
    177     pos_ += child.size();
    178   }
    179 
    180   DCHECK(!err);
    181   return !err && pos() == size();
    182 }
    183 
    184 bool BoxReader::ReadChild(Box* child) {
    185   DCHECK(scanned_);
    186   FourCC child_type = child->BoxType();
    187 
    188   ChildMap::iterator itr = children_.find(child_type);
    189   RCHECK(itr != children_.end());
    190   DVLOG(2) << "Found a " << FourCCToString(child_type) << " box.";
    191   RCHECK(child->Parse(&itr->second));
    192   children_.erase(itr);
    193   return true;
    194 }
    195 
    196 bool BoxReader::MaybeReadChild(Box* child) {
    197   if (!children_.count(child->BoxType())) return true;
    198   return ReadChild(child);
    199 }
    200 
    201 bool BoxReader::ReadFullBoxHeader() {
    202   uint32 vflags;
    203   RCHECK(Read4(&vflags));
    204   version_ = vflags >> 24;
    205   flags_ = vflags & 0xffffff;
    206   return true;
    207 }
    208 
    209 bool BoxReader::ReadHeader(bool* err) {
    210   uint64 size = 0;
    211   *err = false;
    212 
    213   if (!HasBytes(8)) return false;
    214   CHECK(Read4Into8(&size) && ReadFourCC(&type_));
    215 
    216   if (size == 0) {
    217     // Media Source specific: we do not support boxes that run to EOS.
    218     *err = true;
    219     return false;
    220   } else if (size == 1) {
    221     if (!HasBytes(8)) return false;
    222     CHECK(Read8(&size));
    223   }
    224 
    225   // Implementation-specific: support for boxes larger than 2^31 has been
    226   // removed.
    227   if (size < static_cast<uint64>(pos_) ||
    228       size > static_cast<uint64>(kint32max)) {
    229     *err = true;
    230     return false;
    231   }
    232 
    233   // Note that the pos_ head has advanced to the byte immediately after the
    234   // header, which is where we want it.
    235   size_ = size;
    236   return true;
    237 }
    238 
    239 }  // namespace mp4
    240 }  // namespace media
    241