Commit 70485400 authored by acolwell@chromium.org's avatar acolwell@chromium.org

Adding support for incremental cluster parsing.


BUG=104160
TEST=Covered by ChunkDemuxer unittests.



Review URL: http://codereview.chromium.org/8775035

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@114163 0039d316-1c4b-4281-b951-d872f2087c98
parent 375003a5
...@@ -130,7 +130,7 @@ void ChunkDemuxerStream::Flush() { ...@@ -130,7 +130,7 @@ void ChunkDemuxerStream::Flush() {
bool ChunkDemuxerStream::CanAddBuffers(const BufferQueue& buffers) const { bool ChunkDemuxerStream::CanAddBuffers(const BufferQueue& buffers) const {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
// If we haven't seen any buffers yet than anything can be added. // If we haven't seen any buffers yet, then anything can be added.
if (last_buffer_timestamp_ == kNoTimestamp) if (last_buffer_timestamp_ == kNoTimestamp)
return true; return true;
...@@ -434,7 +434,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) { ...@@ -434,7 +434,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
int cur_size = 0; int cur_size = 0;
int bytes_parsed = 0; int bytes_parsed = 0;
int result = -1; int result = -1;
bool parsed_a_cluster = false; bool can_complete_seek = false;
byte_queue_.Peek(&cur, &cur_size); byte_queue_.Peek(&cur, &cur_size);
...@@ -449,16 +449,19 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) { ...@@ -449,16 +449,19 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
} }
break; break;
case INITIALIZED: case INITIALIZED: {
result = ParseCluster_Locked(cur, cur_size); bool buffers_added = false;
result = ParseCluster_Locked(cur, cur_size, &buffers_added);
if (result < 0) { if (result < 0) {
VLOG(1) << "AppendData(): parsing data failed"; VLOG(1) << "AppendData(): parsing data failed";
ReportError_Locked(PIPELINE_ERROR_DECODE); ReportError_Locked(PIPELINE_ERROR_DECODE);
return true; return true;
} }
parsed_a_cluster = (result > 0); // We can complete the seek if we have successfully parsed
break; // some data and buffers were added to one of the DemuxerStreams.
can_complete_seek |= (result > 0 && buffers_added);
} break;
case WAITING_FOR_INIT: case WAITING_FOR_INIT:
case ENDED: case ENDED:
...@@ -477,7 +480,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) { ...@@ -477,7 +480,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
byte_queue_.Pop(bytes_parsed); byte_queue_.Pop(bytes_parsed);
if (parsed_a_cluster && seek_waits_for_data_) { if (can_complete_seek && seek_waits_for_data_) {
seek_waits_for_data_ = false; seek_waits_for_data_ = false;
if (!seek_cb_.is_null()) if (!seek_cb_.is_null())
...@@ -730,7 +733,8 @@ bool ChunkDemuxer::SetupStreams() { ...@@ -730,7 +733,8 @@ bool ChunkDemuxer::SetupStreams() {
return !no_supported_streams; return !no_supported_streams;
} }
int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) { int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size,
bool* buffers_added) {
lock_.AssertAcquired(); lock_.AssertAcquired();
if (!cluster_parser_.get()) if (!cluster_parser_.get())
return -1; return -1;
...@@ -749,9 +753,6 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) { ...@@ -749,9 +753,6 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) {
} }
// Skip the element. // Skip the element.
return result + element_size; return result + element_size;
} else if (id != kWebMIdCluster) {
VLOG(1) << "Unexpected ID 0x" << std::hex << id;
return -1;
} }
int bytes_parsed = cluster_parser_->Parse(data, size); int bytes_parsed = cluster_parser_->Parse(data, size);
...@@ -759,20 +760,25 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) { ...@@ -759,20 +760,25 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) {
if (bytes_parsed <= 0) if (bytes_parsed <= 0)
return bytes_parsed; return bytes_parsed;
// Make sure we can add the buffers to both streams before we actutally if (!cluster_parser_->audio_buffers().empty() ||
// add them. This allows us to accept all of the data or none of it. !cluster_parser_->video_buffers().empty()) {
if ((audio_.get() && // Make sure we can add the buffers to both streams before we actually
!audio_->CanAddBuffers(cluster_parser_->audio_buffers())) || // add them. This allows us to accept all of the data or none of it.
(video_.get() && if ((audio_.get() &&
!video_->CanAddBuffers(cluster_parser_->video_buffers()))) { !audio_->CanAddBuffers(cluster_parser_->audio_buffers())) ||
return -1; (video_.get() &&
} !video_->CanAddBuffers(cluster_parser_->video_buffers()))) {
return -1;
}
if (audio_.get()) if (audio_.get())
audio_->AddBuffers(cluster_parser_->audio_buffers()); audio_->AddBuffers(cluster_parser_->audio_buffers());
if (video_.get()) if (video_.get())
video_->AddBuffers(cluster_parser_->video_buffers()); video_->AddBuffers(cluster_parser_->video_buffers());
*buffers_added = true;
}
// TODO(acolwell) : make this more representative of what is actually // TODO(acolwell) : make this more representative of what is actually
// buffered. // buffered.
......
...@@ -81,13 +81,18 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { ...@@ -81,13 +81,18 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
// found. // found.
bool SetupStreams(); bool SetupStreams();
// Parse a cluster add add the buffers to the appropriate DemxuerStream. // Parse a cluster and add the buffers to the appropriate DemuxerStream. This
// |data| is expected to point to the beginning of a cluster element. // method also skips over CUES elements if it happens to encounter them.
//
// |data| is expected to point to the beginning of an element.
//
// |buffers_added| - Indicates whether Buffers were added to DemuxerStreams
// during the call. This is only valid if the return value > 0.
// //
// Returns -1 if the parse fails. // Returns -1 if the parse fails.
// Returns 0 if more data is needed. // Returns 0 if more data is needed.
// Returns the number of bytes parsed on success. // Returns the number of bytes parsed on success.
int ParseCluster_Locked(const uint8* data, int size); int ParseCluster_Locked(const uint8* data, int size, bool* buffers_added);
// Reports an error and puts the demuxer in a state where it won't accept more // Reports an error and puts the demuxer in a state where it won't accept more
// data. // data.
......
...@@ -621,6 +621,7 @@ ...@@ -621,6 +621,7 @@
'video/capture/video_capture_device_unittest.cc', 'video/capture/video_capture_device_unittest.cc',
'webm/cluster_builder.cc', 'webm/cluster_builder.cc',
'webm/cluster_builder.h', 'webm/cluster_builder.h',
'webm/webm_parser_unittest.cc',
], ],
'conditions': [ 'conditions': [
['os_posix==1 and OS!="mac"', { ['os_posix==1 and OS!="mac"', {
......
...@@ -26,6 +26,7 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale, ...@@ -26,6 +26,7 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale,
audio_default_duration_(audio_default_duration), audio_default_duration_(audio_default_duration),
video_track_num_(video_track_num), video_track_num_(video_track_num),
video_default_duration_(video_default_duration), video_default_duration_(video_default_duration),
parser_(kWebMIdCluster),
last_block_timecode_(-1), last_block_timecode_(-1),
cluster_timecode_(-1) { cluster_timecode_(-1) {
} }
...@@ -33,12 +34,25 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale, ...@@ -33,12 +34,25 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale,
WebMClusterParser::~WebMClusterParser() {} WebMClusterParser::~WebMClusterParser() {}
int WebMClusterParser::Parse(const uint8* buf, int size) { int WebMClusterParser::Parse(const uint8* buf, int size) {
last_block_timecode_ = -1;
cluster_timecode_ = -1;
audio_buffers_.clear(); audio_buffers_.clear();
video_buffers_.clear(); video_buffers_.clear();
return WebMParseListElement(buf, size, kWebMIdCluster, 1, this); int result = parser_.Parse(buf, size, this);
if (result <= 0)
return result;
if (parser_.IsParsingComplete()) {
// Reset the parser if we're done parsing so that
// it is ready to accept another cluster on the next
// call.
parser_.Reset();
last_block_timecode_ = -1;
cluster_timecode_ = -1;
}
return result;
} }
bool WebMClusterParser::OnListStart(int id) { bool WebMClusterParser::OnListStart(int id) {
...@@ -67,17 +81,17 @@ bool WebMClusterParser::OnUInt(int id, int64 val) { ...@@ -67,17 +81,17 @@ bool WebMClusterParser::OnUInt(int id, int64 val) {
} }
bool WebMClusterParser::OnFloat(int id, double val) { bool WebMClusterParser::OnFloat(int id, double val) {
VLOG(1) << "Unexpected float element with ID " << std::hex << id; DVLOG(1) << "Unexpected float element with ID " << std::hex << id;
return false; return false;
} }
bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) { bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) {
VLOG(1) << "Unexpected binary element with ID " << std::hex << id; DVLOG(1) << "Unexpected binary element with ID " << std::hex << id;
return false; return false;
} }
bool WebMClusterParser::OnString(int id, const std::string& str) { bool WebMClusterParser::OnString(int id, const std::string& str) {
VLOG(1) << "Unexpected string element with ID " << std::hex << id; DVLOG(1) << "Unexpected string element with ID " << std::hex << id;
return false; return false;
} }
...@@ -85,17 +99,17 @@ bool WebMClusterParser::OnSimpleBlock(int track_num, int timecode, ...@@ -85,17 +99,17 @@ bool WebMClusterParser::OnSimpleBlock(int track_num, int timecode,
int flags, int flags,
const uint8* data, int size) { const uint8* data, int size) {
if (cluster_timecode_ == -1) { if (cluster_timecode_ == -1) {
VLOG(1) << "Got SimpleBlock before cluster timecode."; DVLOG(1) << "Got SimpleBlock before cluster timecode.";
return false; return false;
} }
if (timecode < 0) { if (timecode < 0) {
VLOG(1) << "Got SimpleBlock with negative timecode offset " << timecode; DVLOG(1) << "Got SimpleBlock with negative timecode offset " << timecode;
return false; return false;
} }
if (last_block_timecode_ != -1 && timecode < last_block_timecode_) { if (last_block_timecode_ != -1 && timecode < last_block_timecode_) {
VLOG(1) << "Got SimpleBlock with a timecode before the previous block."; DVLOG(1) << "Got SimpleBlock with a timecode before the previous block.";
return false; return false;
} }
...@@ -115,13 +129,13 @@ bool WebMClusterParser::OnSimpleBlock(int track_num, int timecode, ...@@ -115,13 +129,13 @@ bool WebMClusterParser::OnSimpleBlock(int track_num, int timecode,
buffer->SetDuration(video_default_duration_); buffer->SetDuration(video_default_duration_);
queue = &video_buffers_; queue = &video_buffers_;
} else { } else {
VLOG(1) << "Unexpected track number " << track_num; DVLOG(1) << "Unexpected track number " << track_num;
return false; return false;
} }
if (!queue->empty() && if (!queue->empty() &&
buffer->GetTimestamp() == queue->back()->GetTimestamp()) { buffer->GetTimestamp() == queue->back()->GetTimestamp()) {
VLOG(1) << "Got SimpleBlock timecode is not strictly monotonically " DVLOG(1) << "Got SimpleBlock timecode is not strictly monotonically "
<< "increasing for track " << track_num; << "increasing for track " << track_num;
return false; return false;
} }
......
...@@ -53,6 +53,8 @@ class WebMClusterParser : public WebMParserClient { ...@@ -53,6 +53,8 @@ class WebMClusterParser : public WebMParserClient {
int video_track_num_; int video_track_num_;
base::TimeDelta video_default_duration_; base::TimeDelta video_default_duration_;
WebMListParser parser_;
int64 last_block_timecode_; int64 last_block_timecode_;
int64 cluster_timecode_; int64 cluster_timecode_;
......
...@@ -64,6 +64,8 @@ const int kWebMIdVideo = 0xE0; ...@@ -64,6 +64,8 @@ const int kWebMIdVideo = 0xE0;
const int kWebMIdVoid = 0xEC; const int kWebMIdVoid = 0xEC;
const int kWebMIdWritingApp = 0x5741; const int kWebMIdWritingApp = 0x5741;
const int64 kWebMUnknownSize = GG_LONGLONG(0x00FFFFFFFFFFFFFF);
// Default timecode scale if the TimecodeScale element is // Default timecode scale if the TimecodeScale element is
// not specified in the INFO element. // not specified in the INFO element.
const int kWebMDefaultTimecodeScale = 1000000; const int kWebMDefaultTimecodeScale = 1000000;
......
...@@ -17,7 +17,17 @@ WebMInfoParser::WebMInfoParser() ...@@ -17,7 +17,17 @@ WebMInfoParser::WebMInfoParser()
WebMInfoParser::~WebMInfoParser() {} WebMInfoParser::~WebMInfoParser() {}
int WebMInfoParser::Parse(const uint8* buf, int size) { int WebMInfoParser::Parse(const uint8* buf, int size) {
return WebMParseListElement(buf, size, kWebMIdInfo, 1, this); timecode_scale_ = -1;
duration_ = -1;
WebMListParser parser(kWebMIdInfo);
int result = parser.Parse(buf, size, this);
if (result <= 0)
return result;
// For now we do all or nothing parsing.
return parser.IsParsingComplete() ? result : 0;
} }
bool WebMInfoParser::OnListStart(int id) { return true; } bool WebMInfoParser::OnListStart(int id) { return true; }
...@@ -36,7 +46,7 @@ bool WebMInfoParser::OnUInt(int id, int64 val) { ...@@ -36,7 +46,7 @@ bool WebMInfoParser::OnUInt(int id, int64 val) {
return true; return true;
if (timecode_scale_ != -1) { if (timecode_scale_ != -1) {
VLOG(1) << "Multiple values for id " << std::hex << id << " specified"; DVLOG(1) << "Multiple values for id " << std::hex << id << " specified";
return false; return false;
} }
...@@ -46,12 +56,12 @@ bool WebMInfoParser::OnUInt(int id, int64 val) { ...@@ -46,12 +56,12 @@ bool WebMInfoParser::OnUInt(int id, int64 val) {
bool WebMInfoParser::OnFloat(int id, double val) { bool WebMInfoParser::OnFloat(int id, double val) {
if (id != kWebMIdDuration) { if (id != kWebMIdDuration) {
VLOG(1) << "Unexpected float for id" << std::hex << id; DVLOG(1) << "Unexpected float for id" << std::hex << id;
return false; return false;
} }
if (duration_ != -1) { if (duration_ != -1) {
VLOG(1) << "Multiple values for duration."; DVLOG(1) << "Multiple values for duration.";
return false; return false;
} }
......
This diff is collapsed.
...@@ -6,8 +6,10 @@ ...@@ -6,8 +6,10 @@
#define MEDIA_WEBM_WEBM_PARSER_H_ #define MEDIA_WEBM_WEBM_PARSER_H_
#include <string> #include <string>
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "media/base/media_export.h"
namespace media { namespace media {
...@@ -21,7 +23,7 @@ namespace media { ...@@ -21,7 +23,7 @@ namespace media {
// indicates whether the parsed data is valid. If false is returned // indicates whether the parsed data is valid. If false is returned
// then the parse is immediately terminated and an error is reported by the // then the parse is immediately terminated and an error is reported by the
// parser. // parser.
class WebMParserClient { class MEDIA_EXPORT WebMParserClient {
public: public:
virtual ~WebMParserClient(); virtual ~WebMParserClient();
...@@ -34,6 +36,106 @@ class WebMParserClient { ...@@ -34,6 +36,106 @@ class WebMParserClient {
virtual bool OnSimpleBlock(int track_num, int timecode, virtual bool OnSimpleBlock(int track_num, int timecode,
int flags, int flags,
const uint8* data, int size) = 0; const uint8* data, int size) = 0;
protected:
WebMParserClient();
DISALLOW_COPY_AND_ASSIGN(WebMParserClient);
};
struct ListElementInfo;
// Parses a WebM list element and all of its children. This
// class supports incremental parsing of the list so Parse()
// can be called multiple times with pieces of the list.
// IsParsingComplete() will return true once the entire list has
// been parsed.
class MEDIA_EXPORT WebMListParser {
public:
// |id| - Element ID of the list we intend to parse.
explicit WebMListParser(int id);
~WebMListParser();
// Resets the state of the parser so it can start parsing a new list.
void Reset();
// Parses list data contained in |buf|.
// |client| Called as different elements in the list are parsed.
//
// Returns < 0 if the parse fails.
// Returns 0 if more data is needed.
// Returning > 0 indicates success & the number of bytes parsed.
int Parse(const uint8* buf, int size, WebMParserClient* client);
// Returns true if the entire list has been parsed.
bool IsParsingComplete() const;
private:
enum State {
NEED_LIST_HEADER,
INSIDE_LIST,
DONE_PARSING_LIST,
PARSE_ERROR,
};
struct ListState {
int id_;
int size_;
int bytes_parsed_;
const ListElementInfo* element_info_;
};
void ChangeState(State new_state);
// Parses a single element in the current list.
//
// |header_size| - The size of the element header
// |id| - The ID of the element being parsed.
// |element_size| - The size of the element body.
// |data| - Pointer to the element contents.
// |size| - Number of bytes in |data|
// |client| - Client to pass the parsed data to.
//
// Returns < 0 if the parse fails.
// Returns 0 if more data is needed.
// Returning > 0 indicates success & the number of bytes parsed.
int ParseListElement(int header_size,
int id, int64 element_size,
const uint8* data, int size,
WebMParserClient* client);
// Called when starting to parse a new list.
//
// |id| - The ID of the new list.
// |size| - The size of the new list.
// |client| - The client object to notify that a new list is being parsed.
//
// Returns true if this list can be started in the current context. False
// if starting this list causes some sort of parse error.
bool OnListStart(int id, int64 size, WebMParserClient* client);
// Called when the end of the current list has been reached. This may also
// signal the end of the current list's ancestors if the current list happens
// to be at the end of its parent.
//
// |client| - The client to notify about lists ending.
//
// Returns true if no errors occurred while ending this list(s).
bool OnListEnd(WebMParserClient* client);
State state_;
// Element ID passed to the constructor.
int root_id_;
// Element level for |root_id_|. Used to verify that elements appear at
// the correct level.
int root_level_;
// Stack of state for all the lists currently being parsed. Lists are
// added and removed from this stack as they are parsed.
std::vector<ListState> list_state_stack_;
DISALLOW_COPY_AND_ASSIGN(WebMListParser);
}; };
// Parses an element header & returns the ID and element size. // Parses an element header & returns the ID and element size.
...@@ -47,15 +149,6 @@ class WebMParserClient { ...@@ -47,15 +149,6 @@ class WebMParserClient {
int WebMParseElementHeader(const uint8* buf, int size, int WebMParseElementHeader(const uint8* buf, int size,
int* id, int64* element_size); int* id, int64* element_size);
// Parses a single list element that matches |id|. This method fails if the
// buffer points to an element that does not match |id|.
//
// Returns -1 if the parse fails.
// Returns 0 if more data is needed.
// Returns the number of bytes parsed on success.
int WebMParseListElement(const uint8* buf, int size, int id,
int level, WebMParserClient* client);
} // namespace media } // namespace media
#endif // MEDIA_WEBM_WEBM_PARSER_H_ #endif // MEDIA_WEBM_WEBM_PARSER_H_
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/webm/cluster_builder.h"
#include "media/webm/webm_constants.h"
#include "media/webm/webm_parser.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::InSequence;
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::_;
namespace media {
class MockWebMParserClient : public WebMParserClient {
public:
virtual ~MockWebMParserClient() {}
// WebMParserClient methods.
MOCK_METHOD1(OnListStart, bool(int));
MOCK_METHOD1(OnListEnd, bool(int));
MOCK_METHOD2(OnUInt, bool(int, int64));
MOCK_METHOD2(OnFloat, bool(int, double));
MOCK_METHOD3(OnBinary, bool(int, const uint8*, int));
MOCK_METHOD2(OnString, bool(int, const std::string&));
MOCK_METHOD5(OnSimpleBlock, bool(int, int, int, const uint8*, int));
};
class WebMParserTest : public testing::Test {
protected:
StrictMock<MockWebMParserClient> client_;
};
struct SimpleBlockInfo {
int track_num;
int timestamp;
};
static void AddSimpleBlock(ClusterBuilder* cb, int track_num,
int64 timecode) {
uint8 data[] = { 0x00 };
cb->AddSimpleBlock(track_num, timecode, 0, data, sizeof(data));
}
static Cluster* CreateCluster(int timecode,
const SimpleBlockInfo* block_info,
int block_count) {
ClusterBuilder cb;
cb.SetClusterTimecode(0);
for (int i = 0; i < block_count; i++)
AddSimpleBlock(&cb, block_info[i].track_num, block_info[i].timestamp);
return cb.Finish();
}
static void CreateClusterExpectations(int timecode,
const SimpleBlockInfo* block_info,
int block_count,
MockWebMParserClient* client) {
InSequence s;
EXPECT_CALL(*client, OnListStart(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(*client, OnUInt(kWebMIdTimecode, 0)).WillOnce(Return(true));
for (int i = 0; i < block_count; i++) {
EXPECT_CALL(*client, OnSimpleBlock(block_info[i].track_num,
block_info[i].timestamp,
_, _, _))
.WillOnce(Return(true));
}
EXPECT_CALL(*client, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
}
TEST_F(WebMParserTest, EmptyCluster) {
const uint8 kEmptyCluster[] = {
0x1F, 0x43, 0xB6, 0x75, 0x80 // CLUSTER (size = 0)
};
int size = sizeof(kEmptyCluster);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
WebMListParser parser(kWebMIdCluster);
int result = parser.Parse(kEmptyCluster, size, &client_);
EXPECT_EQ(size, result);
EXPECT_TRUE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, EmptyClusterInSegment) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x85, // SEGMENT (size = 5)
0x1F, 0x43, 0xB6, 0x75, 0x80, // CLUSTER (size = 0)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdSegment)).WillOnce(Return(true));
WebMListParser parser(kWebMIdSegment);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(size, result);
EXPECT_TRUE(parser.IsParsingComplete());
}
// Test the case where a non-list child element has a size
// that is beyond the end of the parent.
TEST_F(WebMParserTest, ChildNonListLargerThanParent) {
const uint8 kBuffer[] = {
0x1F, 0x43, 0xB6, 0x75, 0x81, // CLUSTER (size = 1)
0xE7, 0x81, 0x01, // Timecode (size=1, value=1)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(true));
WebMListParser parser(kWebMIdCluster);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
// Test the case where a list child element has a size
// that is beyond the end of the parent.
TEST_F(WebMParserTest, ChildListLargerThanParent) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x85, // SEGMENT (size = 5)
0x1F, 0x43, 0xB6, 0x75, 0x81, 0x11 // CLUSTER (size = 1)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(true));
WebMListParser parser(kWebMIdSegment);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
// Expecting to parse a Cluster, but get a Segment.
TEST_F(WebMParserTest, ListIdDoesNotMatch) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x80, // SEGMENT (size = 0)
};
int size = sizeof(kBuffer);
WebMListParser parser(kWebMIdCluster);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, InvalidElementInList) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x82, // SEGMENT (size = 2)
0xAE, 0x80, // TrackEntry (size = 0)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(true));
WebMListParser parser(kWebMIdSegment);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, VoidAndCRC32InList) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x99, // SEGMENT (size = 25)
0xEC, 0x83, 0x00, 0x00, 0x00, // Void (size = 3)
0xBF, 0x83, 0x00, 0x00, 0x00, // CRC32 (size = 3)
0x1F, 0x43, 0xB6, 0x75, 0x8A, // CLUSTER (size = 10)
0xEC, 0x83, 0x00, 0x00, 0x00, // Void (size = 3)
0xBF, 0x83, 0x00, 0x00, 0x00, // CRC32 (size = 3)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdSegment)).WillOnce(Return(true));
WebMListParser parser(kWebMIdSegment);
int result = parser.Parse(kBuffer, size, &client_);
EXPECT_EQ(size, result);
EXPECT_TRUE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, ParseListElementWithSingleCall) {
const SimpleBlockInfo kBlockInfo[] = {
{ 0, 1 },
{ 1, 2 },
{ 0, 3 },
{ 0, 4 },
{ 1, 4 },
};
int block_count = arraysize(kBlockInfo);
scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
CreateClusterExpectations(0, kBlockInfo, block_count, &client_);
WebMListParser parser(kWebMIdCluster);
int result = parser.Parse(cluster->data(), cluster->size(), &client_);
EXPECT_EQ(cluster->size(), result);
EXPECT_TRUE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, ParseListElementWithMultipleCalls) {
const SimpleBlockInfo kBlockInfo[] = {
{ 0, 1 },
{ 1, 2 },
{ 0, 3 },
{ 0, 4 },
{ 1, 4 },
};
int block_count = arraysize(kBlockInfo);
scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
CreateClusterExpectations(0, kBlockInfo, block_count, &client_);
const uint8* data = cluster->data();
int size = cluster->size();
int default_parse_size = 3;
WebMListParser parser(kWebMIdCluster);
int parse_size = std::min(default_parse_size, size);
while (size > 0) {
int result = parser.Parse(data, parse_size, &client_);
EXPECT_GE(result, 0);
EXPECT_LE(result, parse_size);
if (result == 0) {
// The parser needs more data so increase the parse_size a little.
EXPECT_FALSE(parser.IsParsingComplete());
parse_size += default_parse_size;
parse_size = std::min(parse_size, size);
continue;
}
parse_size = default_parse_size;
data += result;
size -= result;
EXPECT_EQ((size == 0), parser.IsParsingComplete());
}
EXPECT_TRUE(parser.IsParsingComplete());
}
} // namespace media
...@@ -15,15 +15,28 @@ WebMTracksParser::WebMTracksParser(int64 timecode_scale) ...@@ -15,15 +15,28 @@ WebMTracksParser::WebMTracksParser(int64 timecode_scale)
track_num_(-1), track_num_(-1),
track_default_duration_(-1), track_default_duration_(-1),
audio_track_num_(-1), audio_track_num_(-1),
audio_default_duration_(base::TimeDelta::FromMicroseconds(-1)), video_track_num_(-1) {
video_track_num_(-1),
video_default_duration_(base::TimeDelta::FromMicroseconds(-1)) {
} }
WebMTracksParser::~WebMTracksParser() {} WebMTracksParser::~WebMTracksParser() {}
int WebMTracksParser::Parse(const uint8* buf, int size) { int WebMTracksParser::Parse(const uint8* buf, int size) {
return WebMParseListElement(buf, size, kWebMIdTracks, 1, this); track_type_ =-1;
track_num_ = -1;
track_default_duration_ = -1;
audio_track_num_ = -1;
audio_default_duration_ = base::TimeDelta();
video_track_num_ = -1;
video_default_duration_ = base::TimeDelta();
WebMListParser parser(kWebMIdTracks);
int result = parser.Parse(buf, size, this);
if (result <= 0)
return result;
// For now we do all or nothing parsing.
return parser.IsParsingComplete() ? result : 0;
} }
...@@ -40,15 +53,19 @@ bool WebMTracksParser::OnListStart(int id) { ...@@ -40,15 +53,19 @@ bool WebMTracksParser::OnListStart(int id) {
bool WebMTracksParser::OnListEnd(int id) { bool WebMTracksParser::OnListEnd(int id) {
if (id == kWebMIdTrackEntry) { if (id == kWebMIdTrackEntry) {
if (track_type_ == -1 || track_num_ == -1) { if (track_type_ == -1 || track_num_ == -1) {
VLOG(1) << "Missing TrackEntry data" DVLOG(1) << "Missing TrackEntry data"
<< " TrackType " << track_type_ << " TrackType " << track_type_
<< " TrackNum " << track_num_; << " TrackNum " << track_num_;
return false; return false;
} }
// Convert nanoseconds to base::TimeDelta. base::TimeDelta default_duration;
base::TimeDelta default_duration = base::TimeDelta::FromMicroseconds(
track_default_duration_ / 1000.0); if (track_default_duration_ > 0) {
// Convert nanoseconds to base::TimeDelta.
default_duration= base::TimeDelta::FromMicroseconds(
track_default_duration_ / 1000.0);
}
if (track_type_ == kWebMTrackTypeVideo) { if (track_type_ == kWebMTrackTypeVideo) {
video_track_num_ = track_num_; video_track_num_ = track_num_;
...@@ -57,7 +74,7 @@ bool WebMTracksParser::OnListEnd(int id) { ...@@ -57,7 +74,7 @@ bool WebMTracksParser::OnListEnd(int id) {
audio_track_num_ = track_num_; audio_track_num_ = track_num_;
audio_default_duration_ = default_duration; audio_default_duration_ = default_duration;
} else { } else {
VLOG(1) << "Unexpected TrackType " << track_type_; DVLOG(1) << "Unexpected TrackType " << track_type_;
return false; return false;
} }
...@@ -86,7 +103,7 @@ bool WebMTracksParser::OnUInt(int id, int64 val) { ...@@ -86,7 +103,7 @@ bool WebMTracksParser::OnUInt(int id, int64 val) {
} }
if (*dst != -1) { if (*dst != -1) {
VLOG(1) << "Multiple values for id " << std::hex << id << " specified"; DVLOG(1) << "Multiple values for id " << std::hex << id << " specified";
return false; return false;
} }
...@@ -95,7 +112,7 @@ bool WebMTracksParser::OnUInt(int id, int64 val) { ...@@ -95,7 +112,7 @@ bool WebMTracksParser::OnUInt(int id, int64 val) {
} }
bool WebMTracksParser::OnFloat(int id, double val) { bool WebMTracksParser::OnFloat(int id, double val) {
VLOG(1) << "Unexpected float for id" << std::hex << id; DVLOG(1) << "Unexpected float for id" << std::hex << id;
return false; return false;
} }
...@@ -108,7 +125,7 @@ bool WebMTracksParser::OnString(int id, const std::string& str) { ...@@ -108,7 +125,7 @@ bool WebMTracksParser::OnString(int id, const std::string& str) {
return false; return false;
if (str != "A_VORBIS" && str != "V_VP8") { if (str != "A_VORBIS" && str != "V_VP8") {
VLOG(1) << "Unexpected CodecID " << str; DVLOG(1) << "Unexpected CodecID " << str;
return false; return false;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment