Define BoxBuffer which wraps mp4 box read/write.
BoxBuffer wraps either BoxReader for reading or BufferWriter for writing. Thus it is capable of doing either reading or writing, but not both. Change-Id: Id57370755a586bfdef1291a23af29f5b1feec903
This commit is contained in:
parent
e9b77add23
commit
3318ad715d
|
@ -0,0 +1,176 @@
|
||||||
|
// Copyright (c) 2013 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef MEDIA_MP4_BOX_BUFFER_H_
|
||||||
|
#define MEDIA_MP4_BOX_BUFFER_H_
|
||||||
|
|
||||||
|
#include "base/compiler_specific.h"
|
||||||
|
#include "media/base/buffer_writer.h"
|
||||||
|
#include "media/mp4/box.h"
|
||||||
|
#include "media/mp4/box_reader.h"
|
||||||
|
|
||||||
|
namespace media {
|
||||||
|
namespace mp4 {
|
||||||
|
|
||||||
|
// Defines a wrapper for mp4 box reading/writing, which is symmetric in most
|
||||||
|
// cases, i.e. we can use one single routine for the reading and writing.
|
||||||
|
// BoxBuffer wraps either BoxReader for reading or BufferWriter for writing.
|
||||||
|
// Thus it is capable of doing either reading or writing, but not both.
|
||||||
|
class BoxBuffer {
|
||||||
|
public:
|
||||||
|
// Creates a "reader" version of the BoxBuffer.
|
||||||
|
// Caller retains |reader| ownership. |reader| should not be NULL.
|
||||||
|
explicit BoxBuffer(BoxReader* reader) : reader_(reader), writer_(NULL) {
|
||||||
|
DCHECK(reader);
|
||||||
|
}
|
||||||
|
// Creates a "writer" version of the BoxBuffer.
|
||||||
|
// Caller retains |writer| ownership. |writer| should not be NULL.
|
||||||
|
explicit BoxBuffer(BufferWriter* writer) : reader_(NULL), writer_(writer) {
|
||||||
|
DCHECK(writer);
|
||||||
|
}
|
||||||
|
~BoxBuffer() {}
|
||||||
|
|
||||||
|
// Reading or writing?
|
||||||
|
bool Reading() const { return reader_ != NULL; }
|
||||||
|
|
||||||
|
// Returns current read/write position. In read mode, this is the current
|
||||||
|
// read position. In write mode, it is the same as Size().
|
||||||
|
size_t Pos() const {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->pos();
|
||||||
|
return writer_->Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns total buffer size.In read mode, it includes data that has already
|
||||||
|
// been read or skipped, and will not change. In write mode, it includes all
|
||||||
|
// data that has been written, and will change as data is written.
|
||||||
|
size_t Size() const {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->size();
|
||||||
|
return writer_->Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read/write integers of various size and unsigned/signed.
|
||||||
|
bool ReadWriteUInt8(uint8* v) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->Read1(v);
|
||||||
|
writer_->AppendInt(*v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ReadWriteUInt16(uint16* v) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->Read2(v);
|
||||||
|
writer_->AppendInt(*v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ReadWriteUInt32(uint32* v) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->Read4(v);
|
||||||
|
writer_->AppendInt(*v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ReadWriteUInt64(uint64* v) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->Read8(v);
|
||||||
|
writer_->AppendInt(*v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ReadWriteInt16(int16* v) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->Read2s(v);
|
||||||
|
writer_->AppendInt(*v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ReadWriteInt32(int32* v) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->Read4s(v);
|
||||||
|
writer_->AppendInt(*v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ReadWriteInt64(int64* v) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->Read8s(v);
|
||||||
|
writer_->AppendInt(*v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read/write the least significant |num_bytes| of |v| from/to buffer.
|
||||||
|
// |num_bytes| should not be larger than sizeof(v), i.e. 8.
|
||||||
|
bool ReadWriteUInt64NBytes(uint64* v, size_t num_bytes) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->ReadNBytesInto8(v, num_bytes);
|
||||||
|
writer_->AppendNBytes(*v, num_bytes);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ReadWriteInt64NBytes(int64* v, size_t num_bytes) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->ReadNBytesInto8s(v, num_bytes);
|
||||||
|
writer_->AppendNBytes(*v, num_bytes);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ReadWriteVector(std::vector<uint8>* vector, size_t count) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->ReadToVector(vector, count);
|
||||||
|
DCHECK_EQ(vector->size(), count);
|
||||||
|
writer_->AppendVector(*vector);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ReadWriteFourCC(FourCC* fourcc) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->ReadFourCC(fourcc);
|
||||||
|
writer_->AppendInt(static_cast<uint32>(*fourcc));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare child boxes for read/write.
|
||||||
|
bool PrepareChildren() {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->ScanChildren();
|
||||||
|
// NOP in write mode.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read/write child box.
|
||||||
|
bool ReadWriteChild(Box* box) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->ReadChild(box);
|
||||||
|
// The box is mandatory, i.e. the box size should not be 0.
|
||||||
|
DCHECK_NE(0, box->atom_size);
|
||||||
|
CHECK(box->ReadWrite(this));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read/write child box if exist.
|
||||||
|
bool TryReadWriteChild(Box* box) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->TryReadChild(box);
|
||||||
|
// The box is optional, i.e. it can be skipped if the box size is 0.
|
||||||
|
if (box->atom_size != 0)
|
||||||
|
CHECK(box->ReadWrite(this));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip |num_bytes| in read mode, otherwise fill with |num_bytes| of '\0'.
|
||||||
|
bool IgnoreBytes(size_t num_bytes) {
|
||||||
|
if (reader_)
|
||||||
|
return reader_->SkipBytes(num_bytes);
|
||||||
|
std::vector<uint8> vector(num_bytes, 0);
|
||||||
|
writer_->AppendVector(vector);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoxReader* reader() { return reader_; }
|
||||||
|
BufferWriter* writer() { return writer_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
BoxReader* reader_;
|
||||||
|
BufferWriter* writer_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(BoxBuffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mp4
|
||||||
|
} // namespace media
|
||||||
|
|
||||||
|
#endif // MEDIA_MP4_BOX_BUFFER_H_
|
|
@ -4,34 +4,25 @@
|
||||||
|
|
||||||
#include "media/mp4/box_reader.h"
|
#include "media/mp4/box_reader.h"
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/memory/scoped_ptr.h"
|
#include "base/memory/scoped_ptr.h"
|
||||||
#include "media/mp4/box_definitions.h"
|
#include "media/mp4/box.h"
|
||||||
#include "media/mp4/rcheck.h"
|
|
||||||
|
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
Box::~Box() {}
|
BoxReader::BoxReader(const uint8* buf, size_t size)
|
||||||
|
: BufferReader(buf, size), type_(FOURCC_NULL), scanned_(false) {
|
||||||
BoxReader::BoxReader(const uint8* buf, const int size)
|
DCHECK(buf);
|
||||||
: BufferReader(buf, size),
|
DCHECK_LT(0, size);
|
||||||
type_(FOURCC_NULL),
|
|
||||||
version_(0),
|
|
||||||
flags_(0),
|
|
||||||
scanned_(false) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BoxReader::~BoxReader() {
|
BoxReader::~BoxReader() {
|
||||||
if (scanned_ && !children_.empty()) {
|
if (scanned_ && !children_.empty()) {
|
||||||
for (ChildMap::iterator itr = children_.begin();
|
for (ChildMap::iterator itr = children_.begin(); itr != children_.end();
|
||||||
itr != children_.end(); ++itr) {
|
++itr) {
|
||||||
DVLOG(1) << "Skipping unknown box: " << FourCCToString(itr->first);
|
DVLOG(1) << "Skipping unknown box: " << FourCCToString(itr->first);
|
||||||
|
delete itr->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +53,8 @@ bool BoxReader::StartTopLevelBox(const uint8* buf,
|
||||||
int* box_size,
|
int* box_size,
|
||||||
bool* err) {
|
bool* err) {
|
||||||
BoxReader reader(buf, buf_size);
|
BoxReader reader(buf, buf_size);
|
||||||
if (!reader.ReadHeader(err)) return false;
|
if (!reader.ReadHeader(err))
|
||||||
|
return false;
|
||||||
if (!IsValidTopLevelBox(reader.type())) {
|
if (!IsValidTopLevelBox(reader.type())) {
|
||||||
*err = true;
|
*err = true;
|
||||||
return false;
|
return false;
|
||||||
|
@ -102,17 +94,20 @@ bool BoxReader::ScanChildren() {
|
||||||
DCHECK(!scanned_);
|
DCHECK(!scanned_);
|
||||||
scanned_ = true;
|
scanned_ = true;
|
||||||
|
|
||||||
bool err = false;
|
|
||||||
while (pos() < size()) {
|
while (pos() < size()) {
|
||||||
BoxReader child(&buf_[pos_], size_ - pos_);
|
scoped_ptr<BoxReader> child(
|
||||||
if (!child.ReadHeader(&err)) break;
|
new BoxReader(&data()[pos()], size() - pos()));
|
||||||
|
bool err;
|
||||||
|
if (!child->ReadHeader(&err))
|
||||||
|
return false;
|
||||||
|
|
||||||
children_.insert(std::pair<FourCC, BoxReader>(child.type(), child));
|
FourCC box_type = child->type();
|
||||||
pos_ += child.size();
|
size_t box_size = child->size();
|
||||||
|
children_.insert(std::pair<FourCC, BoxReader*>(box_type, child.release()));
|
||||||
|
RCHECK(SkipBytes(box_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
DCHECK(!err);
|
return true;
|
||||||
return !err && pos() == size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BoxReader::ReadChild(Box* child) {
|
bool BoxReader::ReadChild(Box* child) {
|
||||||
|
@ -122,7 +117,8 @@ bool BoxReader::ReadChild(Box* child) {
|
||||||
ChildMap::iterator itr = children_.find(child_type);
|
ChildMap::iterator itr = children_.find(child_type);
|
||||||
RCHECK(itr != children_.end());
|
RCHECK(itr != children_.end());
|
||||||
DVLOG(2) << "Found a " << FourCCToString(child_type) << " box.";
|
DVLOG(2) << "Found a " << FourCCToString(child_type) << " box.";
|
||||||
RCHECK(child->Parse(&itr->second));
|
RCHECK(child->Parse(itr->second));
|
||||||
|
delete itr->second;
|
||||||
children_.erase(itr);
|
children_.erase(itr);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -131,38 +127,31 @@ bool BoxReader::ChildExist(Box* child) {
|
||||||
return children_.count(child->BoxType()) > 0;
|
return children_.count(child->BoxType()) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BoxReader::MaybeReadChild(Box* child) {
|
bool BoxReader::TryReadChild(Box* child) {
|
||||||
if (!children_.count(child->BoxType())) return true;
|
if (!children_.count(child->BoxType()))
|
||||||
return ReadChild(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BoxReader::ReadFullBoxHeader() {
|
|
||||||
uint32 vflags;
|
|
||||||
RCHECK(Read4(&vflags));
|
|
||||||
version_ = vflags >> 24;
|
|
||||||
flags_ = vflags & 0xffffff;
|
|
||||||
return true;
|
return true;
|
||||||
|
return ReadChild(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BoxReader::ReadHeader(bool* err) {
|
bool BoxReader::ReadHeader(bool* err) {
|
||||||
uint64 size = 0;
|
uint64 size = 0;
|
||||||
*err = false;
|
*err = false;
|
||||||
|
|
||||||
if (!HasBytes(8)) return false;
|
if (!ReadNBytesInto8(&size, sizeof(uint32)) || !ReadFourCC(&type_))
|
||||||
CHECK(Read4Into8(&size) && ReadFourCC(&type_));
|
return false;
|
||||||
|
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
// Media Source specific: we do not support boxes that run to EOS.
|
// Media Source specific: we do not support boxes that run to EOS.
|
||||||
*err = true;
|
*err = true;
|
||||||
return false;
|
return false;
|
||||||
} else if (size == 1) {
|
} else if (size == 1) {
|
||||||
if (!HasBytes(8)) return false;
|
if (!Read8(&size))
|
||||||
CHECK(Read8(&size));
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation-specific: support for boxes larger than 2^31 has been
|
// Implementation-specific: support for boxes larger than 2^31 has been
|
||||||
// removed.
|
// removed.
|
||||||
if (size < static_cast<uint64>(pos_) ||
|
if (size < static_cast<uint64>(pos()) ||
|
||||||
size > static_cast<uint64>(kint32max)) {
|
size > static_cast<uint64>(kint32max)) {
|
||||||
*err = true;
|
*err = true;
|
||||||
return false;
|
return false;
|
||||||
|
@ -170,7 +159,7 @@ bool BoxReader::ReadHeader(bool* err) {
|
||||||
|
|
||||||
// Note that the pos_ head has advanced to the byte immediately after the
|
// Note that the pos_ head has advanced to the byte immediately after the
|
||||||
// header, which is where we want it.
|
// header, which is where we want it.
|
||||||
size_ = size;
|
set_size(size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,7 @@
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
class BoxReader;
|
class Box;
|
||||||
|
|
||||||
struct Box {
|
|
||||||
virtual ~Box();
|
|
||||||
virtual bool Parse(BoxReader* reader) = 0;
|
|
||||||
virtual FourCC BoxType() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BoxReader : public BufferReader {
|
class BoxReader : public BufferReader {
|
||||||
public:
|
public:
|
||||||
|
@ -69,33 +63,34 @@ class BoxReader : public BufferReader {
|
||||||
|
|
||||||
// Read one child if available. Returns false on error, true on successful
|
// Read one child if available. Returns false on error, true on successful
|
||||||
// read or on child absent.
|
// read or on child absent.
|
||||||
bool MaybeReadChild(Box* child) WARN_UNUSED_RESULT;
|
bool TryReadChild(Box* child) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
// Read at least one child. False means error or no such child present.
|
// Read at least one child. False means error or no such child present.
|
||||||
template<typename T> bool ReadChildren(
|
template <typename T>
|
||||||
std::vector<T>* children) WARN_UNUSED_RESULT;
|
bool ReadChildren(std::vector<T>* children) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
// Read any number of children. False means error.
|
// Read any number of children. False means error.
|
||||||
template<typename T> bool MaybeReadChildren(
|
template <typename T>
|
||||||
std::vector<T>* children) WARN_UNUSED_RESULT;
|
bool TryReadChildren(std::vector<T>* children) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
// Read all children, regardless of FourCC. This is used from exactly one box,
|
// Read all children, regardless of FourCC. This is used from exactly one box,
|
||||||
// corresponding to a rather significant inconsistency in the BMFF spec.
|
// corresponding to a rather significant inconsistency in the BMFF spec.
|
||||||
// Note that this method is mutually exclusive with ScanChildren().
|
// Note that this method is mutually exclusive with ScanChildren().
|
||||||
template<typename T> bool ReadAllChildren(
|
template <typename T>
|
||||||
std::vector<T>* children) WARN_UNUSED_RESULT;
|
bool ReadAllChildren(std::vector<T>* children) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
// Populate the values of 'version()' and 'flags()' from a full box header.
|
bool ReadFourCC(FourCC* fourcc) {
|
||||||
// Many boxes, but not all, use these values. This call should happen after
|
uint32 val;
|
||||||
// the box has been initialized, and does not re-read the main box header.
|
if (!Read4(&val))
|
||||||
bool ReadFullBoxHeader() WARN_UNUSED_RESULT;
|
return false;
|
||||||
|
*fourcc = static_cast<FourCC>(val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
FourCC type() const { return type_; }
|
FourCC type() const { return type_; }
|
||||||
uint8 version() const { return version_; }
|
|
||||||
uint32 flags() const { return flags_; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BoxReader(const uint8* buf, const int size);
|
BoxReader(const uint8* buf, size_t size);
|
||||||
|
|
||||||
// Must be called immediately after init. If the return is false, this
|
// Must be called immediately after init. If the return is false, this
|
||||||
// indicates that the box header and its contents were not available in the
|
// indicates that the box header and its contents were not available in the
|
||||||
|
@ -107,25 +102,26 @@ class BoxReader : public BufferReader {
|
||||||
bool ReadHeader(bool* err);
|
bool ReadHeader(bool* err);
|
||||||
|
|
||||||
FourCC type_;
|
FourCC type_;
|
||||||
uint8 version_;
|
|
||||||
uint32 flags_;
|
|
||||||
|
|
||||||
typedef std::multimap<FourCC, BoxReader> ChildMap;
|
typedef std::multimap<FourCC, BoxReader*> ChildMap;
|
||||||
|
|
||||||
// The set of child box FourCCs and their corresponding buffer readers. Only
|
// The set of child box FourCCs and their corresponding buffer readers. Only
|
||||||
// valid if scanned_ is true.
|
// valid if scanned_ is true.
|
||||||
ChildMap children_;
|
ChildMap children_;
|
||||||
bool scanned_;
|
bool scanned_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(BoxReader);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Template definitions
|
// Template definitions.
|
||||||
template<typename T> bool BoxReader::ReadChildren(std::vector<T>* children) {
|
template <typename T>
|
||||||
RCHECK(MaybeReadChildren(children) && !children->empty());
|
bool BoxReader::ReadChildren(std::vector<T>* children) {
|
||||||
|
RCHECK(TryReadChildren(children) && !children->empty());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
bool BoxReader::MaybeReadChildren(std::vector<T>* children) {
|
bool BoxReader::TryReadChildren(std::vector<T>* children) {
|
||||||
DCHECK(scanned_);
|
DCHECK(scanned_);
|
||||||
DCHECK(children->empty());
|
DCHECK(children->empty());
|
||||||
|
|
||||||
|
@ -137,32 +133,35 @@ bool BoxReader::MaybeReadChildren(std::vector<T>* children) {
|
||||||
children->resize(std::distance(start_itr, end_itr));
|
children->resize(std::distance(start_itr, end_itr));
|
||||||
typename std::vector<T>::iterator child_itr = children->begin();
|
typename std::vector<T>::iterator child_itr = children->begin();
|
||||||
for (ChildMap::iterator itr = start_itr; itr != end_itr; ++itr) {
|
for (ChildMap::iterator itr = start_itr; itr != end_itr; ++itr) {
|
||||||
RCHECK(child_itr->Parse(&itr->second));
|
RCHECK(child_itr->Parse(itr->second));
|
||||||
|
delete itr->second;
|
||||||
++child_itr;
|
++child_itr;
|
||||||
}
|
}
|
||||||
children_.erase(start_itr, end_itr);
|
children_.erase(start_itr, end_itr);
|
||||||
|
|
||||||
DVLOG(2) << "Found " << children->size() << " "
|
DVLOG(2) << "Found " << children->size() << " " << FourCCToString(child_type)
|
||||||
<< FourCCToString(child_type) << " boxes.";
|
<< " boxes.";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
bool BoxReader::ReadAllChildren(std::vector<T>* children) {
|
bool BoxReader::ReadAllChildren(std::vector<T>* children) {
|
||||||
DCHECK(!scanned_);
|
DCHECK(!scanned_);
|
||||||
scanned_ = true;
|
scanned_ = true;
|
||||||
|
|
||||||
bool err = false;
|
|
||||||
while (pos() < size()) {
|
while (pos() < size()) {
|
||||||
BoxReader child_reader(&buf_[pos_], size_ - pos_);
|
BoxReader child_reader(&data()[pos()], size() - pos());
|
||||||
if (!child_reader.ReadHeader(&err)) break;
|
bool err;
|
||||||
|
if (!child_reader.ReadHeader(&err))
|
||||||
|
return false;
|
||||||
|
|
||||||
T child;
|
T child;
|
||||||
RCHECK(child.Parse(&child_reader));
|
RCHECK(child.Parse(&child_reader));
|
||||||
children->push_back(child);
|
children->push_back(child);
|
||||||
pos_ += child_reader.size();
|
RCHECK(SkipBytes(child_reader.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return !err;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mp4
|
} // namespace mp4
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "base/basictypes.h"
|
#include "base/basictypes.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/memory/scoped_ptr.h"
|
#include "base/memory/scoped_ptr.h"
|
||||||
#include "media/mp4/box_reader.h"
|
#include "media/mp4/box_buffer.h"
|
||||||
#include "media/mp4/rcheck.h"
|
#include "media/mp4/rcheck.h"
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
|
@ -15,66 +15,75 @@ namespace media {
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
static const uint8 kSkipBox[] = {
|
static const uint8 kSkipBox[] = {
|
||||||
// Top-level test box containing three children
|
// Top-level test box containing three children.
|
||||||
0x00, 0x00, 0x00, 0x40, 's', 'k', 'i', 'p',
|
0x00, 0x00, 0x00, 0x40, 's', 'k', 'i', 'p', 0x01, 0x02, 0x03, 0x04,
|
||||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
0x05, 0x06, 0x07, 0x08, 0xf9, 0x0a, 0x0b, 0x0c, 0xfd, 0x0e, 0x0f, 0x10,
|
||||||
0xf9, 0x0a, 0x0b, 0x0c, 0xfd, 0x0e, 0x0f, 0x10,
|
// Ordinary (8-byte header) child box.
|
||||||
// Ordinary (8-byte header) child box
|
|
||||||
0x00, 0x00, 0x00, 0x0c, 'p', 's', 's', 'h', 0xde, 0xad, 0xbe, 0xef,
|
0x00, 0x00, 0x00, 0x0c, 'p', 's', 's', 'h', 0xde, 0xad, 0xbe, 0xef,
|
||||||
// Extended-size header child box
|
// Extended-size header child box.
|
||||||
0x00, 0x00, 0x00, 0x01, 'p', 's', 's', 'h',
|
0x00, 0x00, 0x00, 0x01, 'p', 's', 's', 'h', 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
|
0x00, 0x00, 0x00, 0x14, 0xfa, 0xce, 0xca, 0xfe,
|
||||||
0xfa, 0xce, 0xca, 0xfe,
|
// Empty free box.
|
||||||
// Empty free box
|
|
||||||
0x00, 0x00, 0x00, 0x08, 'f', 'r', 'e', 'e',
|
0x00, 0x00, 0x00, 0x08, 'f', 'r', 'e', 'e',
|
||||||
// Trailing garbage
|
// Trailing garbage.
|
||||||
0x00 };
|
0x00};
|
||||||
|
|
||||||
struct FreeBox : Box {
|
struct FreeBox : Box {
|
||||||
virtual bool Parse(BoxReader* reader) OVERRIDE {
|
virtual bool ReadWrite(BoxBuffer* buffer) OVERRIDE {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
virtual FourCC BoxType() const OVERRIDE { return FOURCC_FREE; }
|
virtual FourCC BoxType() const OVERRIDE { return FOURCC_FREE; }
|
||||||
|
virtual uint32 ComputeSize() {
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PsshBox : Box {
|
struct PsshBox : Box {
|
||||||
uint32 val;
|
virtual bool ReadWrite(BoxBuffer* buffer) OVERRIDE {
|
||||||
|
return buffer->ReadWriteUInt32(&val);
|
||||||
virtual bool Parse(BoxReader* reader) OVERRIDE {
|
|
||||||
return reader->Read4(&val);
|
|
||||||
}
|
}
|
||||||
virtual FourCC BoxType() const OVERRIDE { return FOURCC_PSSH; }
|
virtual FourCC BoxType() const OVERRIDE { return FOURCC_PSSH; }
|
||||||
|
virtual uint32 ComputeSize() {
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 val;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SkipBox : Box {
|
struct SkipBox : FullBox {
|
||||||
|
virtual bool ReadWrite(BoxBuffer* buffer) OVERRIDE {
|
||||||
|
RCHECK(FullBox::ReadWrite(buffer) &&
|
||||||
|
buffer->ReadWriteUInt8(&a) &&
|
||||||
|
buffer->ReadWriteUInt8(&b) &&
|
||||||
|
buffer->ReadWriteUInt16(&c) &&
|
||||||
|
buffer->ReadWriteInt32(&d) &&
|
||||||
|
buffer->ReadWriteInt64NBytes(&e, sizeof(uint32)));
|
||||||
|
RCHECK(buffer->PrepareChildren());
|
||||||
|
if (buffer->Reading()) {
|
||||||
|
DCHECK(buffer->reader());
|
||||||
|
RCHECK(buffer->reader()->ReadChildren(&kids));
|
||||||
|
} else {
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
}
|
||||||
|
return buffer->TryReadWriteChild(&empty);
|
||||||
|
}
|
||||||
|
virtual FourCC BoxType() const OVERRIDE { return FOURCC_SKIP; }
|
||||||
|
virtual uint32 ComputeSize() {
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint8 a, b;
|
uint8 a, b;
|
||||||
uint16 c;
|
uint16 c;
|
||||||
int32 d;
|
int32 d;
|
||||||
int64 e;
|
int64 e;
|
||||||
|
|
||||||
std::vector<PsshBox> kids;
|
std::vector<PsshBox> kids;
|
||||||
FreeBox mpty;
|
FreeBox empty;
|
||||||
|
|
||||||
virtual bool Parse(BoxReader* reader) OVERRIDE {
|
|
||||||
RCHECK(reader->ReadFullBoxHeader() &&
|
|
||||||
reader->Read1(&a) &&
|
|
||||||
reader->Read1(&b) &&
|
|
||||||
reader->Read2(&c) &&
|
|
||||||
reader->Read4s(&d) &&
|
|
||||||
reader->Read4sInto8s(&e));
|
|
||||||
return reader->ScanChildren() &&
|
|
||||||
reader->ReadChildren(&kids) &&
|
|
||||||
reader->MaybeReadChild(&mpty);
|
|
||||||
}
|
|
||||||
virtual FourCC BoxType() const OVERRIDE { return FOURCC_SKIP; }
|
|
||||||
|
|
||||||
SkipBox();
|
|
||||||
virtual ~SkipBox();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SkipBox::SkipBox() {}
|
|
||||||
SkipBox::~SkipBox() {}
|
|
||||||
|
|
||||||
class BoxReaderTest : public testing::Test {
|
class BoxReaderTest : public testing::Test {
|
||||||
protected:
|
protected:
|
||||||
std::vector<uint8> GetBuf() {
|
std::vector<uint8> GetBuf() {
|
||||||
|
@ -92,8 +101,8 @@ TEST_F(BoxReaderTest, ExpectedOperationTest) {
|
||||||
|
|
||||||
SkipBox box;
|
SkipBox box;
|
||||||
EXPECT_TRUE(box.Parse(reader.get()));
|
EXPECT_TRUE(box.Parse(reader.get()));
|
||||||
EXPECT_EQ(0x01, reader->version());
|
EXPECT_EQ(0x01, box.version);
|
||||||
EXPECT_EQ(0x020304u, reader->flags());
|
EXPECT_EQ(0x020304u, box.flags);
|
||||||
EXPECT_EQ(0x05, box.a);
|
EXPECT_EQ(0x05, box.a);
|
||||||
EXPECT_EQ(0x06, box.b);
|
EXPECT_EQ(0x06, box.b);
|
||||||
EXPECT_EQ(0x0708, box.c);
|
EXPECT_EQ(0x0708, box.c);
|
||||||
|
@ -104,7 +113,7 @@ TEST_F(BoxReaderTest, ExpectedOperationTest) {
|
||||||
EXPECT_EQ(0xdeadbeef, box.kids[0].val);
|
EXPECT_EQ(0xdeadbeef, box.kids[0].val);
|
||||||
EXPECT_EQ(0xfacecafe, box.kids[1].val);
|
EXPECT_EQ(0xfacecafe, box.kids[1].val);
|
||||||
|
|
||||||
// Accounting for the extra byte outside of the box above
|
// Accounting for the extra byte outside of the box above.
|
||||||
EXPECT_EQ(buf.size(), static_cast<uint64>(reader->size() + 1));
|
EXPECT_EQ(buf.size(), static_cast<uint64>(reader->size() + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +165,7 @@ TEST_F(BoxReaderTest, ScanChildrenTest) {
|
||||||
FreeBox free;
|
FreeBox free;
|
||||||
EXPECT_TRUE(reader->ReadChild(&free));
|
EXPECT_TRUE(reader->ReadChild(&free));
|
||||||
EXPECT_FALSE(reader->ReadChild(&free));
|
EXPECT_FALSE(reader->ReadChild(&free));
|
||||||
EXPECT_TRUE(reader->MaybeReadChild(&free));
|
EXPECT_TRUE(reader->TryReadChild(&free));
|
||||||
|
|
||||||
std::vector<PsshBox> kids;
|
std::vector<PsshBox> kids;
|
||||||
|
|
||||||
|
@ -164,12 +173,12 @@ TEST_F(BoxReaderTest, ScanChildrenTest) {
|
||||||
EXPECT_EQ(2u, kids.size());
|
EXPECT_EQ(2u, kids.size());
|
||||||
kids.clear();
|
kids.clear();
|
||||||
EXPECT_FALSE(reader->ReadChildren(&kids));
|
EXPECT_FALSE(reader->ReadChildren(&kids));
|
||||||
EXPECT_TRUE(reader->MaybeReadChildren(&kids));
|
EXPECT_TRUE(reader->TryReadChildren(&kids));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BoxReaderTest, ReadAllChildrenTest) {
|
TEST_F(BoxReaderTest, ReadAllChildrenTest) {
|
||||||
std::vector<uint8> buf = GetBuf();
|
std::vector<uint8> buf = GetBuf();
|
||||||
// Modify buffer to exclude its last 'free' box
|
// Modify buffer to exclude its last 'free' box.
|
||||||
buf[3] = 0x38;
|
buf[3] = 0x38;
|
||||||
bool err;
|
bool err;
|
||||||
scoped_ptr<BoxReader> reader(
|
scoped_ptr<BoxReader> reader(
|
||||||
|
@ -178,13 +187,13 @@ TEST_F(BoxReaderTest, ReadAllChildrenTest) {
|
||||||
std::vector<PsshBox> kids;
|
std::vector<PsshBox> kids;
|
||||||
EXPECT_TRUE(reader->SkipBytes(16) && reader->ReadAllChildren(&kids));
|
EXPECT_TRUE(reader->SkipBytes(16) && reader->ReadAllChildren(&kids));
|
||||||
EXPECT_EQ(2u, kids.size());
|
EXPECT_EQ(2u, kids.size());
|
||||||
EXPECT_EQ(kids[0].val, 0xdeadbeef); // Ensure order is preserved
|
EXPECT_EQ(kids[0].val, 0xdeadbeef); // Ensure order is preserved.
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BoxReaderTest, SkippingBloc) {
|
TEST_F(BoxReaderTest, SkippingBloc) {
|
||||||
static const uint8 kData[] = {
|
static const uint8 kData[] = {0x00, 0x00, 0x00, 0x09, // Box size.
|
||||||
0x00, 0x00, 0x00, 0x09, 'b', 'l', 'o', 'c', 0x00
|
'b', 'l', 'o', 'c', // FourCC.
|
||||||
};
|
0x00}; // Reserved byte.
|
||||||
|
|
||||||
std::vector<uint8> buf(kData, kData + sizeof(kData));
|
std::vector<uint8> buf(kData, kData + sizeof(kData));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue