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:
Kongqun Yang 2013-11-22 13:24:25 -08:00 committed by KongQun Yang
parent e9b77add23
commit 3318ad715d
4 changed files with 305 additions and 132 deletions

176
media/mp4/box_buffer.h Normal file
View File

@ -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_

View File

@ -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 true;
return ReadChild(child); return ReadChild(child);
} }
bool BoxReader::ReadFullBoxHeader() {
uint32 vflags;
RCHECK(Read4(&vflags));
version_ = vflags >> 24;
flags_ = vflags & 0xffffff;
return true;
}
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;
} }

View File

@ -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

View File

@ -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, 0x00,
0x00, 0x00, 0x00, 0x01, 'p', 's', 's', 'h', 0x00, 0x00, 0x00, 0x14, 0xfa, 0xce, 0xca, 0xfe,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, // Empty free box.
0xfa, 0xce, 0xca, 0xfe, 0x00, 0x00, 0x00, 0x08, 'f', 'r', 'e', 'e',
// Empty free box // Trailing garbage.
0x00, 0x00, 0x00, 0x08, 'f', 'r', 'e', 'e', 0x00};
// Trailing garbage
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));