summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-12 17:43:04 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-12 17:43:04 +0000
commit31bc2039ba95532aafee8f028673c2a53d76b24a (patch)
treeb70c4e29d583cf1084675df8323f6375ef1c4f91 /media
parent8453535eedd7e7789fcc315436cead1a030312a4 (diff)
downloadchromium_src-31bc2039ba95532aafee8f028673c2a53d76b24a.zip
chromium_src-31bc2039ba95532aafee8f028673c2a53d76b24a.tar.gz
chromium_src-31bc2039ba95532aafee8f028673c2a53d76b24a.tar.bz2
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@114030 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/filters/chunk_demuxer.cc52
-rw-r--r--media/filters/chunk_demuxer.h11
-rw-r--r--media/media.gyp1
-rw-r--r--media/webm/webm_cluster_parser.cc36
-rw-r--r--media/webm/webm_cluster_parser.h2
-rw-r--r--media/webm/webm_constants.h2
-rw-r--r--media/webm/webm_info_parser.cc18
-rw-r--r--media/webm/webm_parser.cc519
-rw-r--r--media/webm/webm_parser.h113
-rw-r--r--media/webm/webm_parser_unittest.cc264
-rw-r--r--media/webm/webm_tracks_parser.cc38
11 files changed, 793 insertions, 263 deletions
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index d437d91..f81b80f 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -130,7 +130,7 @@ void ChunkDemuxerStream::Flush() {
bool ChunkDemuxerStream::CanAddBuffers(const BufferQueue& buffers) const {
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)
return true;
@@ -434,7 +434,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
int cur_size = 0;
int bytes_parsed = 0;
int result = -1;
- bool parsed_a_cluster = false;
+ bool can_complete_seek = false;
byte_queue_.Peek(&cur, &cur_size);
@@ -449,16 +449,19 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
}
break;
- case INITIALIZED:
- result = ParseCluster_Locked(cur, cur_size);
+ case INITIALIZED: {
+ bool buffers_added = false;
+ result = ParseCluster_Locked(cur, cur_size, &buffers_added);
if (result < 0) {
VLOG(1) << "AppendData(): parsing data failed";
ReportError_Locked(PIPELINE_ERROR_DECODE);
return true;
}
- parsed_a_cluster = (result > 0);
- break;
+ // We can complete the seek if we have successfully parsed
+ // some data and buffers were added to one of the DemuxerStreams.
+ can_complete_seek |= (result > 0 && buffers_added);
+ } break;
case WAITING_FOR_INIT:
case ENDED:
@@ -477,7 +480,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, size_t length) {
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;
if (!seek_cb_.is_null())
@@ -730,7 +733,8 @@ bool ChunkDemuxer::SetupStreams() {
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();
if (!cluster_parser_.get())
return -1;
@@ -749,9 +753,6 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) {
}
// Skip the element.
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);
@@ -759,20 +760,25 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) {
if (bytes_parsed <= 0)
return bytes_parsed;
- // Make sure we can add the buffers to both streams before we actutally
- // add them. This allows us to accept all of the data or none of it.
- if ((audio_.get() &&
- !audio_->CanAddBuffers(cluster_parser_->audio_buffers())) ||
- (video_.get() &&
- !video_->CanAddBuffers(cluster_parser_->video_buffers()))) {
- return -1;
- }
+ if (!cluster_parser_->audio_buffers().empty() ||
+ !cluster_parser_->video_buffers().empty()) {
+ // Make sure we can add the buffers to both streams before we actually
+ // add them. This allows us to accept all of the data or none of it.
+ if ((audio_.get() &&
+ !audio_->CanAddBuffers(cluster_parser_->audio_buffers())) ||
+ (video_.get() &&
+ !video_->CanAddBuffers(cluster_parser_->video_buffers()))) {
+ return -1;
+ }
- if (audio_.get())
- audio_->AddBuffers(cluster_parser_->audio_buffers());
+ if (audio_.get())
+ audio_->AddBuffers(cluster_parser_->audio_buffers());
- if (video_.get())
- video_->AddBuffers(cluster_parser_->video_buffers());
+ if (video_.get())
+ video_->AddBuffers(cluster_parser_->video_buffers());
+
+ *buffers_added = true;
+ }
// TODO(acolwell) : make this more representative of what is actually
// buffered.
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index b0f0d02..42f1097 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -81,13 +81,18 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
// found.
bool SetupStreams();
- // Parse a cluster add add the buffers to the appropriate DemxuerStream.
- // |data| is expected to point to the beginning of a cluster element.
+ // Parse a cluster and add the buffers to the appropriate DemuxerStream. This
+ // 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 0 if more data is needed.
// 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
// data.
diff --git a/media/media.gyp b/media/media.gyp
index a52d4a5..abe8310 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -621,6 +621,7 @@
'video/capture/video_capture_device_unittest.cc',
'webm/cluster_builder.cc',
'webm/cluster_builder.h',
+ 'webm/webm_parser_unittest.cc',
],
'conditions': [
['os_posix==1 and OS!="mac"', {
diff --git a/media/webm/webm_cluster_parser.cc b/media/webm/webm_cluster_parser.cc
index 8eb045c..fd716b2 100644
--- a/media/webm/webm_cluster_parser.cc
+++ b/media/webm/webm_cluster_parser.cc
@@ -26,6 +26,7 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale,
audio_default_duration_(audio_default_duration),
video_track_num_(video_track_num),
video_default_duration_(video_default_duration),
+ parser_(kWebMIdCluster),
last_block_timecode_(-1),
cluster_timecode_(-1) {
}
@@ -33,12 +34,25 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale,
WebMClusterParser::~WebMClusterParser() {}
int WebMClusterParser::Parse(const uint8* buf, int size) {
- last_block_timecode_ = -1;
- cluster_timecode_ = -1;
audio_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) {
@@ -67,17 +81,17 @@ bool WebMClusterParser::OnUInt(int id, int64 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;
}
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;
}
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;
}
@@ -85,17 +99,17 @@ bool WebMClusterParser::OnSimpleBlock(int track_num, int timecode,
int flags,
const uint8* data, int size) {
if (cluster_timecode_ == -1) {
- VLOG(1) << "Got SimpleBlock before cluster timecode.";
+ DVLOG(1) << "Got SimpleBlock before cluster timecode.";
return false;
}
if (timecode < 0) {
- VLOG(1) << "Got SimpleBlock with negative timecode offset " << timecode;
+ DVLOG(1) << "Got SimpleBlock with negative timecode offset " << timecode;
return false;
}
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;
}
@@ -115,13 +129,13 @@ bool WebMClusterParser::OnSimpleBlock(int track_num, int timecode,
buffer->SetDuration(video_default_duration_);
queue = &video_buffers_;
} else {
- VLOG(1) << "Unexpected track number " << track_num;
+ DVLOG(1) << "Unexpected track number " << track_num;
return false;
}
if (!queue->empty() &&
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;
return false;
}
diff --git a/media/webm/webm_cluster_parser.h b/media/webm/webm_cluster_parser.h
index 5a367b0..7fb1b94 100644
--- a/media/webm/webm_cluster_parser.h
+++ b/media/webm/webm_cluster_parser.h
@@ -53,6 +53,8 @@ class WebMClusterParser : public WebMParserClient {
int video_track_num_;
base::TimeDelta video_default_duration_;
+ WebMListParser parser_;
+
int64 last_block_timecode_;
int64 cluster_timecode_;
diff --git a/media/webm/webm_constants.h b/media/webm/webm_constants.h
index 471115b..96e7c01 100644
--- a/media/webm/webm_constants.h
+++ b/media/webm/webm_constants.h
@@ -64,6 +64,8 @@ const int kWebMIdVideo = 0xE0;
const int kWebMIdVoid = 0xEC;
const int kWebMIdWritingApp = 0x5741;
+const int64 kWebMUnknownSize = GG_LONGLONG(0x00FFFFFFFFFFFFFF);
+
// Default timecode scale if the TimecodeScale element is
// not specified in the INFO element.
const int kWebMDefaultTimecodeScale = 1000000;
diff --git a/media/webm/webm_info_parser.cc b/media/webm/webm_info_parser.cc
index 41e1a25..5807d7e 100644
--- a/media/webm/webm_info_parser.cc
+++ b/media/webm/webm_info_parser.cc
@@ -17,7 +17,17 @@ WebMInfoParser::WebMInfoParser()
WebMInfoParser::~WebMInfoParser() {}
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; }
@@ -36,7 +46,7 @@ bool WebMInfoParser::OnUInt(int id, int64 val) {
return true;
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;
}
@@ -46,12 +56,12 @@ bool WebMInfoParser::OnUInt(int id, int64 val) {
bool WebMInfoParser::OnFloat(int id, double val) {
if (id != kWebMIdDuration) {
- VLOG(1) << "Unexpected float for id" << std::hex << id;
+ DVLOG(1) << "Unexpected float for id" << std::hex << id;
return false;
}
if (duration_ != -1) {
- VLOG(1) << "Multiple values for duration.";
+ DVLOG(1) << "Multiple values for duration.";
return false;
}
diff --git a/media/webm/webm_parser.cc b/media/webm/webm_parser.cc
index 1e42815..1422308 100644
--- a/media/webm/webm_parser.cc
+++ b/media/webm/webm_parser.cc
@@ -20,6 +20,7 @@ namespace media {
static const int kMaxLevelDepth = 6;
enum ElementType {
+ UNKNOWN,
LIST,
UINT,
FLOAT,
@@ -30,13 +31,13 @@ enum ElementType {
};
struct ElementIdInfo {
- int level_;
ElementType type_;
int id_;
};
struct ListElementInfo {
int id_;
+ int level_;
const ElementIdInfo* id_info_;
int id_info_size_;
};
@@ -47,84 +48,83 @@ struct ListElementInfo {
// marked as SKIP because they are valid, but we don't care about them
// right now.
static const ElementIdInfo kClusterIds[] = {
- {2, UINT, kWebMIdTimecode},
- {2, SBLOCK, kWebMIdSimpleBlock},
- {2, LIST, kWebMIdBlockGroup},
+ {UINT, kWebMIdTimecode},
+ {SBLOCK, kWebMIdSimpleBlock},
+ {LIST, kWebMIdBlockGroup},
+};
+
+static const ElementIdInfo kSegmentIds[] = {
+ {SKIP, kWebMIdSeekHead}, // TODO(acolwell): add SeekHead info
+ {LIST, kWebMIdInfo},
+ {LIST, kWebMIdCluster},
+ {LIST, kWebMIdTracks},
+ {SKIP, kWebMIdCues}, // TODO(acolwell): add CUES info
};
static const ElementIdInfo kInfoIds[] = {
- {2, SKIP, kWebMIdSegmentUID},
- {2, UINT, kWebMIdTimecodeScale},
- {2, FLOAT, kWebMIdDuration},
- {2, SKIP, kWebMIdDateUTC},
- {2, SKIP, kWebMIdTitle},
- {2, SKIP, kWebMIdMuxingApp},
- {2, SKIP, kWebMIdWritingApp},
+ {SKIP, kWebMIdSegmentUID},
+ {UINT, kWebMIdTimecodeScale},
+ {FLOAT, kWebMIdDuration},
+ {SKIP, kWebMIdDateUTC},
+ {SKIP, kWebMIdTitle},
+ {SKIP, kWebMIdMuxingApp},
+ {SKIP, kWebMIdWritingApp},
};
static const ElementIdInfo kTracksIds[] = {
- {2, LIST, kWebMIdTrackEntry},
+ {LIST, kWebMIdTrackEntry},
};
static const ElementIdInfo kTrackEntryIds[] = {
- {3, UINT, kWebMIdTrackNumber},
- {3, SKIP, kWebMIdTrackUID},
- {3, UINT, kWebMIdTrackType},
- {3, SKIP, kWebMIdFlagEnabled},
- {3, SKIP, kWebMIdFlagDefault},
- {3, SKIP, kWebMIdFlagForced},
- {3, UINT, kWebMIdFlagLacing},
- {3, UINT, kWebMIdDefaultDuration},
- {3, SKIP, kWebMIdName},
- {3, SKIP, kWebMIdLanguage},
- {3, STRING, kWebMIdCodecID},
- {3, BINARY, kWebMIdCodecPrivate},
- {3, SKIP, kWebMIdCodecName},
- {3, LIST, kWebMIdVideo},
- {3, LIST, kWebMIdAudio},
+ {UINT, kWebMIdTrackNumber},
+ {SKIP, kWebMIdTrackUID},
+ {UINT, kWebMIdTrackType},
+ {SKIP, kWebMIdFlagEnabled},
+ {SKIP, kWebMIdFlagDefault},
+ {SKIP, kWebMIdFlagForced},
+ {UINT, kWebMIdFlagLacing},
+ {UINT, kWebMIdDefaultDuration},
+ {SKIP, kWebMIdName},
+ {SKIP, kWebMIdLanguage},
+ {STRING, kWebMIdCodecID},
+ {BINARY, kWebMIdCodecPrivate},
+ {SKIP, kWebMIdCodecName},
+ {LIST, kWebMIdVideo},
+ {LIST, kWebMIdAudio},
};
static const ElementIdInfo kVideoIds[] = {
- {4, SKIP, kWebMIdFlagInterlaced},
- {4, SKIP, kWebMIdStereoMode},
- {4, UINT, kWebMIdPixelWidth},
- {4, UINT, kWebMIdPixelHeight},
- {4, SKIP, kWebMIdPixelCropBottom},
- {4, SKIP, kWebMIdPixelCropTop},
- {4, SKIP, kWebMIdPixelCropLeft},
- {4, SKIP, kWebMIdPixelCropRight},
- {4, SKIP, kWebMIdDisplayWidth},
- {4, SKIP, kWebMIdDisplayHeight},
- {4, SKIP, kWebMIdDisplayUnit},
- {4, SKIP, kWebMIdAspectRatioType},
+ {SKIP, kWebMIdFlagInterlaced},
+ {SKIP, kWebMIdStereoMode},
+ {UINT, kWebMIdPixelWidth},
+ {UINT, kWebMIdPixelHeight},
+ {SKIP, kWebMIdPixelCropBottom},
+ {SKIP, kWebMIdPixelCropTop},
+ {SKIP, kWebMIdPixelCropLeft},
+ {SKIP, kWebMIdPixelCropRight},
+ {SKIP, kWebMIdDisplayWidth},
+ {SKIP, kWebMIdDisplayHeight},
+ {SKIP, kWebMIdDisplayUnit},
+ {SKIP, kWebMIdAspectRatioType},
};
static const ElementIdInfo kAudioIds[] = {
- {4, SKIP, kWebMIdSamplingFrequency},
- {4, SKIP, kWebMIdOutputSamplingFrequency},
- {4, UINT, kWebMIdChannels},
- {4, SKIP, kWebMIdBitDepth},
-};
-
-static const ElementIdInfo kClustersOnly[] = {
- {1, LIST, kWebMIdCluster},
+ {SKIP, kWebMIdSamplingFrequency},
+ {SKIP, kWebMIdOutputSamplingFrequency},
+ {UINT, kWebMIdChannels},
+ {SKIP, kWebMIdBitDepth},
};
static const ListElementInfo kListElementInfo[] = {
- { kWebMIdCluster, kClusterIds, sizeof(kClusterIds) },
- { kWebMIdInfo, kInfoIds, sizeof(kInfoIds) },
- { kWebMIdTracks, kTracksIds, sizeof(kTracksIds) },
- { kWebMIdTrackEntry, kTrackEntryIds, sizeof(kTrackEntryIds) },
- { kWebMIdVideo, kVideoIds, sizeof(kVideoIds) },
- { kWebMIdAudio, kAudioIds, sizeof(kAudioIds) },
+ { kWebMIdCluster, 1, kClusterIds, sizeof(kClusterIds) },
+ { kWebMIdSegment, 0, kSegmentIds, sizeof(kSegmentIds) },
+ { kWebMIdInfo, 1, kInfoIds, sizeof(kInfoIds) },
+ { kWebMIdTracks, 1, kTracksIds, sizeof(kTracksIds) },
+ { kWebMIdTrackEntry, 2, kTrackEntryIds, sizeof(kTrackEntryIds) },
+ { kWebMIdVideo, 3, kVideoIds, sizeof(kVideoIds) },
+ { kWebMIdAudio, 3, kAudioIds, sizeof(kAudioIds) },
};
-// Number of elements in kListElementInfo.
-const int kListElementInfoCount =
- sizeof(kListElementInfo) / sizeof(ListElementInfo);
-
-WebMParserClient::~WebMParserClient() {}
-
// Parses an element header id or size field. These fields are variable length
// encoded. The first byte indicates how many bytes the field occupies.
// |buf| - The buffer to parse.
@@ -206,22 +206,27 @@ int WebMParseElementHeader(const uint8* buf, int size,
return num_id_bytes + num_size_bytes;
}
-// Finds ElementIdInfo for a specific ID.
-static const ElementIdInfo* FindIdInfo(int id,
- const ElementIdInfo* id_info,
- int id_info_size) {
+// Finds ElementType for a specific ID.
+static ElementType FindIdType(int id,
+ const ElementIdInfo* id_info,
+ int id_info_size) {
+
+ // Check for global element IDs that can be anywhere.
+ if (id == kWebMIdVoid || id == kWebMIdCRC32)
+ return SKIP;
+
int count = id_info_size / sizeof(*id_info);
for (int i = 0; i < count; ++i) {
if (id == id_info[i].id_)
- return &id_info[i];
+ return id_info[i].type_;
}
- return NULL;
+ return UNKNOWN;
}
// Finds ListElementInfo for a specific ID.
static const ListElementInfo* FindListInfo(int id) {
- for (int i = 0; i < kListElementInfoCount; ++i) {
+ for (size_t i = 0; i < arraysize(kListElementInfo); ++i) {
if (id == kListElementInfo[i].id_)
return &kListElementInfo[i];
}
@@ -237,7 +242,7 @@ static int ParseSimpleBlock(const uint8* buf, int size,
// Return an error if the trackNum > 127. We just aren't
// going to support large track numbers right now.
if ((buf[0] & 0x80) != 0x80) {
- VLOG(1) << "TrackNumber over 127 not supported";
+ DVLOG(1) << "TrackNumber over 127 not supported";
return -1;
}
@@ -247,7 +252,7 @@ static int ParseSimpleBlock(const uint8* buf, int size,
int lacing = (flags >> 1) & 0x3;
if (lacing != 0) {
- VLOG(1) << "Lacing " << lacing << " not supported yet.";
+ DVLOG(1) << "Lacing " << lacing << " not supported yet.";
return -1;
}
@@ -265,39 +270,6 @@ static int ParseSimpleBlock(const uint8* buf, int size,
return size;
}
-static int ParseElements(const ElementIdInfo* id_info,
- int id_info_size,
- const uint8* buf, int size, int level,
- WebMParserClient* client);
-
-static int ParseElementList(const uint8* buf, int size,
- int id, int level,
- WebMParserClient* client) {
- const ListElementInfo* list_info = FindListInfo(id);
-
- if (!list_info) {
- VLOG(1) << "Failed to find list info for ID " << std::hex << id;
- return -1;
- }
-
- if (!client->OnListStart(id))
- return -1;
-
- int result = ParseElements(list_info->id_info_,
- list_info->id_info_size_,
- buf, size,
- level + 1,
- client);
-
- if (result <= 0)
- return result;
-
- if (!client->OnListEnd(id))
- return -1;
-
- DCHECK_EQ(result, size);
- return result;
-}
static int ParseUInt(const uint8* buf, int size, int id,
WebMParserClient* client) {
@@ -354,142 +326,285 @@ static int ParseFloat(const uint8* buf, int size, int id,
return size;
}
-static int ParseElements(const ElementIdInfo* id_info,
- int id_info_size,
- const uint8* buf, int size, int level,
- WebMParserClient* client) {
- DCHECK_GE(id_info_size, 0);
- DCHECK_GE(size, 0);
- DCHECK_GE(level, 0);
+static int ParseNonListElement(ElementType type, int id, int64 element_size,
+ const uint8* buf, int size,
+ WebMParserClient* client) {
+ DCHECK_GE(size, element_size);
- const uint8* cur = buf;
- int cur_size = size;
- int used = 0;
+ int result = -1;
+ switch(type) {
+ case SBLOCK:
+ result = ParseSimpleBlock(buf, element_size, client);
+ break;
+ case LIST:
+ NOTIMPLEMENTED();
+ result = -1;
+ break;
+ case UINT:
+ result = ParseUInt(buf, element_size, id, client);
+ break;
+ case FLOAT:
+ result = ParseFloat(buf, element_size, id, client);
+ break;
+ case BINARY:
+ if (client->OnBinary(id, buf, element_size)) {
+ result = element_size;
+ } else {
+ result = -1;
+ }
+ break;
+ case STRING:
+ if (client->OnString(id,
+ std::string(reinterpret_cast<const char*>(buf),
+ element_size))) {
+ result = element_size;
+ } else {
+ result = -1;
+ }
+ break;
+ case SKIP:
+ result = element_size;
+ break;
+ default:
+ DVLOG(1) << "Unhandled ID type " << type;
+ return -1;
+ };
- if (level > kMaxLevelDepth)
- return -1;
+ DCHECK_LE(result, size);
+ return result;
+}
- while (cur_size > 0) {
- int id = 0;
- int64 element_size = 0;
- int result = WebMParseElementHeader(cur, cur_size, &id, &element_size);
+WebMParserClient::WebMParserClient() {}
+WebMParserClient::~WebMParserClient() {}
- if (result <= 0)
- return result;
+WebMListParser::WebMListParser(int id)
+ : state_(NEED_LIST_HEADER),
+ root_id_(id) {
+ const ListElementInfo* list_info = FindListInfo(id);
- cur += result;
- cur_size -= result;
- used += result;
+ DCHECK(list_info);
- // Check to see if the element is larger than the remaining data.
- if (element_size > cur_size)
- return 0;
+ root_level_ = list_info->level_;
+}
- const ElementIdInfo* info = FindIdInfo(id, id_info, id_info_size);
+WebMListParser::~WebMListParser() {}
- if (info == NULL) {
- VLOG(1) << "No info for ID " << std::hex << id;
+void WebMListParser::Reset() {
+ ChangeState(NEED_LIST_HEADER);
+ list_state_stack_.clear();
+}
- // TODO(acolwell): Change this to return -1 after the API has solidified.
- // We don't want to allow elements we don't recognize.
- cur += element_size;
- cur_size -= element_size;
- used += element_size;
- continue;
- }
+int WebMListParser::Parse(const uint8* buf, int size,
+ WebMParserClient* client) {
+ DCHECK(buf);
+ DCHECK(client);
- if (info->level_ != level) {
- VLOG(1) << "ID " << std::hex << id << std::dec << " at level "
- << level << " instead of " << info->level_;
- return -1;
- }
+ if (size < 0 || state_ == PARSE_ERROR || state_ == DONE_PARSING_LIST)
+ return -1;
- switch(info->type_) {
- case SBLOCK:
- if (ParseSimpleBlock(cur, element_size, client) <= 0)
- return -1;
- break;
- case LIST:
- if (ParseElementList(cur, element_size, id, level, client) < 0)
- return -1;
- break;
- case UINT:
- if (ParseUInt(cur, element_size, id, client) <= 0)
+ if (size == 0)
+ return 0;
+
+ const uint8* cur = buf;
+ int cur_size = size;
+ int bytes_parsed = 0;
+
+ while (cur_size > 0 && state_ != PARSE_ERROR && state_ != DONE_PARSING_LIST) {
+ int element_id = 0;
+ int64 element_size = 0;
+ int result = WebMParseElementHeader(cur, cur_size, &element_id,
+ &element_size);
+
+ if (result < 0)
+ return result;
+
+ if (result == 0)
+ return bytes_parsed;
+
+ switch(state_) {
+ case NEED_LIST_HEADER: {
+ if (element_id != root_id_) {
+ ChangeState(PARSE_ERROR);
return -1;
- break;
- case FLOAT:
- if (ParseFloat(cur, element_size, id, client) <= 0)
+ }
+
+ // TODO(acolwell): Add support for lists of unknown size.
+ if (element_size == kWebMUnknownSize) {
+ ChangeState(PARSE_ERROR);
return -1;
- break;
- case BINARY:
- if (!client->OnBinary(id, cur, element_size))
+ }
+
+ ChangeState(INSIDE_LIST);
+ if (!OnListStart(root_id_, element_size, client))
return -1;
+
break;
- case STRING:
- if (!client->OnString(id,
- std::string(reinterpret_cast<const char*>(cur),
- element_size)))
+ }
+
+ case INSIDE_LIST: {
+ int header_size = result;
+ const uint8* element_data = cur + header_size;
+ int element_data_size = cur_size - header_size;
+
+ if (element_size < element_data_size)
+ element_data_size = element_size;
+
+ result = ParseListElement(header_size, element_id, element_size,
+ element_data, element_data_size, client);
+
+ DCHECK_LE(result, header_size + element_data_size);
+ if (result < 0) {
+ ChangeState(PARSE_ERROR);
return -1;
+ }
+
+ if (result == 0)
+ return bytes_parsed;
+
break;
- case SKIP:
- // Do nothing.
+ }
+ case DONE_PARSING_LIST:
+ case PARSE_ERROR:
+ // Shouldn't be able to get here.
+ NOTIMPLEMENTED();
break;
- default:
- VLOG(1) << "Unhandled id type " << info->type_;
- return -1;
- };
-
- cur += element_size;
- cur_size -= element_size;
- used += element_size;
+ }
+
+ cur += result;
+ cur_size -= result;
+ bytes_parsed += result;
}
- return used;
+ return (state_ == PARSE_ERROR) ? -1 : bytes_parsed;
}
-// Parses a single list element that matches |id|. This method fails if the
-// buffer points to an element that does not match |id|.
-int WebMParseListElement(const uint8* buf, int size, int id,
- int level, WebMParserClient* client) {
- if (size < 0)
- return -1;
+bool WebMListParser::IsParsingComplete() const {
+ return state_ == DONE_PARSING_LIST;
+}
- if (size == 0)
- return 0;
+void WebMListParser::ChangeState(State new_state) {
+ state_ = new_state;
+}
- const uint8* cur = buf;
- int cur_size = size;
- int bytes_parsed = 0;
- int element_id = 0;
- int64 element_size = 0;
- int result = WebMParseElementHeader(cur, cur_size, &element_id,
- &element_size);
+int WebMListParser::ParseListElement(int header_size,
+ int id, int64 element_size,
+ const uint8* data, int size,
+ WebMParserClient* client) {
+ DCHECK_GT(list_state_stack_.size(), 0u);
- if (result <= 0)
- return result;
+ ListState& list_state = list_state_stack_.back();
+ DCHECK(list_state.element_info_);
- cur += result;
- cur_size -= result;
- bytes_parsed += result;
+ const ListElementInfo* element_info = list_state.element_info_;
+ ElementType id_type =
+ FindIdType(id, element_info->id_info_, element_info->id_info_size_);
+
+ // Unexpected ID.
+ if (id_type == UNKNOWN) {
+ DVLOG(1) << "No ElementType info for ID 0x" << std::hex << id;
+ return -1;
+ }
- if (element_id != id)
+ // Make sure the whole element can fit inside the current list.
+ int64 total_element_size = header_size + element_size;
+ if (list_state.size_ != kWebMUnknownSize &&
+ list_state.size_ < list_state.bytes_parsed_ + total_element_size) {
return -1;
+ }
+
+ if (id_type == LIST) {
+ list_state.bytes_parsed_ += header_size;
+
+ if (!OnListStart(id, element_size, client))
+ return -1;
+ return header_size;
+ }
- if (element_size > cur_size)
+ // Make sure we have the entire element before trying to parse a non-list
+ // element.
+ if (size < element_size)
return 0;
- if (element_size > 0) {
- result = ParseElementList(cur, element_size, element_id, level, client);
+ int bytes_parsed = ParseNonListElement(id_type, id, element_size,
+ data, size, client);
+ DCHECK_LE(bytes_parsed, size);
- if (result <= 0)
- return result;
+ // Return if an error occurred or we need more data.
+ // Note: bytes_parsed is 0 for a successful parse of a size 0 element. We
+ // need to check the element_size to disambiguate the "need more data" case
+ // from a successful parse.
+ if (bytes_parsed < 0 || (bytes_parsed == 0 && element_size != 0))
+ return bytes_parsed;
- cur += result;
- cur_size -= result;
- bytes_parsed += result;
+ int result = header_size + bytes_parsed;
+ list_state.bytes_parsed_ += result;
+
+ // See if we have reached the end of the current list.
+ if (list_state.bytes_parsed_ == list_state.size_) {
+ if (!OnListEnd(client))
+ return -1;
+ }
+
+ return result;
+}
+
+bool WebMListParser::OnListStart(int id, int64 size, WebMParserClient* client) {
+ ListState list_state = { id, size, 0, FindListInfo(id)};
+
+ if (!list_state.element_info_)
+ return false;
+
+ int current_level = root_level_ + list_state_stack_.size() - 1;
+ if (current_level + 1 != list_state.element_info_->level_)
+ return false;
+
+ if (!list_state_stack_.empty()) {
+
+ // Make sure the new list doesn't go past the end of the current list.
+ ListState current_list = list_state_stack_.back();
+ if (current_list.size_ != kWebMUnknownSize &&
+ current_list.size_ < current_list.bytes_parsed_ + size)
+ return false;
}
- return bytes_parsed;
+ if (!client->OnListStart(id))
+ return false;
+
+ list_state_stack_.push_back(list_state);
+
+ if (size == 0) {
+ return OnListEnd(client);
+ }
+
+ return true;
+}
+
+bool WebMListParser::OnListEnd(WebMParserClient* client) {
+ int lists_ended = 0;
+ for (; !list_state_stack_.empty(); ++lists_ended) {
+ const ListState& list_state = list_state_stack_.back();
+
+ if (list_state.bytes_parsed_ != list_state.size_)
+ break;
+
+ if (!client->OnListEnd(list_state.id_))
+ return false;
+
+ int64 bytes_parsed = list_state.bytes_parsed_;
+ list_state_stack_.pop_back();
+
+ if (!list_state_stack_.empty()) {
+ // Update the bytes_parsed_ for the parent element.
+ list_state_stack_.back().bytes_parsed_ += bytes_parsed;
+ }
+ }
+
+ DCHECK_GE(lists_ended, 1);
+
+ if (list_state_stack_.empty())
+ ChangeState(DONE_PARSING_LIST);
+
+ return true;
}
} // namespace media
diff --git a/media/webm/webm_parser.h b/media/webm/webm_parser.h
index 7d15f9b..bc3e09f 100644
--- a/media/webm/webm_parser.h
+++ b/media/webm/webm_parser.h
@@ -6,8 +6,10 @@
#define MEDIA_WEBM_WEBM_PARSER_H_
#include <string>
+#include <vector>
#include "base/basictypes.h"
+#include "media/base/media_export.h"
namespace media {
@@ -21,7 +23,7 @@ namespace media {
// indicates whether the parsed data is valid. If false is returned
// then the parse is immediately terminated and an error is reported by the
// parser.
-class WebMParserClient {
+class MEDIA_EXPORT WebMParserClient {
public:
virtual ~WebMParserClient();
@@ -34,6 +36,106 @@ class WebMParserClient {
virtual bool OnSimpleBlock(int track_num, int timecode,
int flags,
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.
@@ -47,15 +149,6 @@ class WebMParserClient {
int WebMParseElementHeader(const uint8* buf, int 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
#endif // MEDIA_WEBM_WEBM_PARSER_H_
diff --git a/media/webm/webm_parser_unittest.cc b/media/webm/webm_parser_unittest.cc
new file mode 100644
index 0000000..e377fda
--- /dev/null
+++ b/media/webm/webm_parser_unittest.cc
@@ -0,0 +1,264 @@
+// 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
diff --git a/media/webm/webm_tracks_parser.cc b/media/webm/webm_tracks_parser.cc
index 29b4f33..7cd9569 100644
--- a/media/webm/webm_tracks_parser.cc
+++ b/media/webm/webm_tracks_parser.cc
@@ -9,21 +9,39 @@
namespace media {
+static const base::TimeDelta kNoDuration =
+ base::TimeDelta::FromMicroseconds(-1);
+
WebMTracksParser::WebMTracksParser(int64 timecode_scale)
: timecode_scale_(timecode_scale),
track_type_(-1),
track_num_(-1),
track_default_duration_(-1),
audio_track_num_(-1),
- audio_default_duration_(base::TimeDelta::FromMicroseconds(-1)),
+ audio_default_duration_(kNoDuration),
video_track_num_(-1),
- video_default_duration_(base::TimeDelta::FromMicroseconds(-1)) {
+ video_default_duration_(kNoDuration) {
}
WebMTracksParser::~WebMTracksParser() {}
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_ = kNoDuration;
+ video_track_num_ = -1;
+ video_default_duration_ = kNoDuration;
+
+ 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,9 +58,9 @@ bool WebMTracksParser::OnListStart(int id) {
bool WebMTracksParser::OnListEnd(int id) {
if (id == kWebMIdTrackEntry) {
if (track_type_ == -1 || track_num_ == -1) {
- VLOG(1) << "Missing TrackEntry data"
- << " TrackType " << track_type_
- << " TrackNum " << track_num_;
+ DVLOG(1) << "Missing TrackEntry data"
+ << " TrackType " << track_type_
+ << " TrackNum " << track_num_;
return false;
}
@@ -57,7 +75,7 @@ bool WebMTracksParser::OnListEnd(int id) {
audio_track_num_ = track_num_;
audio_default_duration_ = default_duration;
} else {
- VLOG(1) << "Unexpected TrackType " << track_type_;
+ DVLOG(1) << "Unexpected TrackType " << track_type_;
return false;
}
@@ -86,7 +104,7 @@ bool WebMTracksParser::OnUInt(int id, int64 val) {
}
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;
}
@@ -95,7 +113,7 @@ bool WebMTracksParser::OnUInt(int id, int64 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;
}
@@ -108,7 +126,7 @@ bool WebMTracksParser::OnString(int id, const std::string& str) {
return false;
if (str != "A_VORBIS" && str != "V_VP8") {
- VLOG(1) << "Unexpected CodecID " << str;
+ DVLOG(1) << "Unexpected CodecID " << str;
return false;
}