diff options
author | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-06 19:16:53 +0000 |
---|---|---|
committer | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-06 19:16:53 +0000 |
commit | 07b405307121188af8edc4c65c46c134f419a662 (patch) | |
tree | 3fd0febf03149bd1519791b9989334c754990ba6 /media/webm | |
parent | cea8417218ddd2f3012aaf221dd46dc80c7c2c2f (diff) | |
download | chromium_src-07b405307121188af8edc4c65c46c134f419a662.zip chromium_src-07b405307121188af8edc4c65c46c134f419a662.tar.gz chromium_src-07b405307121188af8edc4c65c46c134f419a662.tar.bz2 |
WebM parser bug fixes for live streams.
- Update ChunkDemuxer so kInfiniteDuration streams are not seekable.
- Add support for Segment & Cluster elements of unknown size to WebMListParser.
- Update WebMStreamParser to treat content without a Duration header as having kInfiniteDuration.
BUG=116824
TEST=ChunkDemuxerTest.TestWebMFile_LiveAudioAndVideo, WebMParserTest.ReservedIds, WebMParserTest.ReservedSizes
Review URL: http://codereview.chromium.org/9600028
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@125197 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/webm')
-rw-r--r-- | media/webm/webm_constants.h | 3 | ||||
-rw-r--r-- | media/webm/webm_parser.cc | 68 | ||||
-rw-r--r-- | media/webm/webm_parser.h | 13 | ||||
-rw-r--r-- | media/webm/webm_parser_unittest.cc | 57 | ||||
-rw-r--r-- | media/webm/webm_stream_parser.cc | 10 |
5 files changed, 133 insertions, 18 deletions
diff --git a/media/webm/webm_constants.h b/media/webm/webm_constants.h index ecd01df..f6afef4 100644 --- a/media/webm/webm_constants.h +++ b/media/webm/webm_constants.h @@ -21,7 +21,7 @@ const int kWebMIdBitDepth = 0x6264; const int kWebMIdBlock = 0xA1; const int kWebMIdBlockAddID = 0xEE; const int kWebMIdBlockAdditions = 0x75A1; -const int kWebMIdBlockAdditional = 0xA4; +const int kWebMIdBlockAdditional = 0xA5; const int kWebMIdBlockDuration = 0x9B; const int kWebMIdBlockGroup = 0xA0; const int kWebMIdBlockMore = 0xA6; @@ -192,6 +192,7 @@ const int kWebMIdVideo = 0xE0; const int kWebMIdVoid = 0xEC; const int kWebMIdWritingApp = 0x5741; +const int64 kWebMReservedId = 0x1FFFFFFF; const int64 kWebMUnknownSize = GG_LONGLONG(0x00FFFFFFFFFFFFFF); } // namespace media diff --git a/media/webm/webm_parser.cc b/media/webm/webm_parser.cc index 777ec91..ea7eea9 100644 --- a/media/webm/webm_parser.cc +++ b/media/webm/webm_parser.cc @@ -424,13 +424,16 @@ static int ParseWebMElementHeaderField(const uint8* buf, int size, int mask = 0x80; uint8 ch = buf[0]; int extra_bytes = -1; + bool all_ones = false; for (int i = 0; i < max_bytes; ++i) { - if ((ch & mask) == mask) { - *num = mask_first_byte ? ch & ~mask : ch; + if ((ch & mask) != 0) { + mask = ~mask & 0xff; + *num = mask_first_byte ? ch & mask : ch; + all_ones = (ch & mask) == mask; extra_bytes = i; break; } - mask >>= 1; + mask = 0x80 | mask >> 1; } if (extra_bytes == -1) @@ -442,8 +445,14 @@ static int ParseWebMElementHeaderField(const uint8* buf, int size, int bytes_used = 1; - for (int i = 0; i < extra_bytes; ++i) - *num = (*num << 8) | (0xff & buf[bytes_used++]); + for (int i = 0; i < extra_bytes; ++i) { + ch = buf[bytes_used++]; + all_ones &= (ch == 0xff); + *num = (*num << 8) | ch; + } + + if (all_ones) + *num = kint64max; return bytes_used; } @@ -464,6 +473,9 @@ int WebMParseElementHeader(const uint8* buf, int size, if (num_id_bytes <= 0) return num_id_bytes; + if (tmp == kint64max) + tmp = kWebMReservedId; + *id = static_cast<int>(tmp); int num_size_bytes = ParseWebMElementHeaderField(buf + num_id_bytes, @@ -473,7 +485,12 @@ int WebMParseElementHeader(const uint8* buf, int size, if (num_size_bytes <= 0) return num_size_bytes; + if (tmp == kint64max) + tmp = kWebMUnknownSize; + *element_size = tmp; + DVLOG(3) << "WebMParseElementHeader() : id " << std::hex << *id << std::dec + << " size " << *element_size; return num_id_bytes + num_size_bytes; } @@ -740,8 +757,10 @@ int WebMListParser::Parse(const uint8* buf, int size) { return -1; } - // TODO(acolwell): Add support for lists of unknown size. - if (element_size == kWebMUnknownSize) { + // Only allow Segment & Cluster to have an unknown size. + if (element_size == kWebMUnknownSize && + (element_id != kWebMIdSegment) && + (element_id != kWebMIdCluster)) { ChangeState(PARSE_ERROR); return -1; } @@ -812,8 +831,24 @@ int WebMListParser::ParseListElement(int header_size, // Unexpected ID. if (id_type == UNKNOWN) { - DVLOG(1) << "No ElementType info for ID 0x" << std::hex << id; - return -1; + if (list_state.size_ != kWebMUnknownSize || + !IsSiblingOrAncestor(list_state.id_, id)) { + DVLOG(1) << "No ElementType info for ID 0x" << std::hex << id; + return -1; + } + + // We've reached the end of a list of unknown size. Update the size now that + // we know it and dispatch the end of list calls. + list_state.size_ = list_state.bytes_parsed_; + + if (!OnListEnd()) + return -1; + + // Check to see if all open lists have ended. + if (list_state_stack_.size() == 0) + return 0; + + list_state = list_state_stack_.back(); } // Make sure the whole element can fit inside the current list. @@ -925,4 +960,19 @@ bool WebMListParser::OnListEnd() { return true; } +bool WebMListParser::IsSiblingOrAncestor(int id_a, int id_b) const { + DCHECK((id_a == kWebMIdSegment) || (id_a == kWebMIdCluster)); + + if (id_a == kWebMIdCluster) { + // kWebMIdCluster siblings. + for (size_t i = 0; i < arraysize(kSegmentIds); i++) { + if (kSegmentIds[i].id_ == id_b) + return true; + } + } + + // kWebMIdSegment siblings. + return ((id_b == kWebMIdSegment) || (id_b == kWebMIdEBMLHeader)); +} + } // namespace media diff --git a/media/webm/webm_parser.h b/media/webm/webm_parser.h index 4575286..2c6d459 100644 --- a/media/webm/webm_parser.h +++ b/media/webm/webm_parser.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -82,8 +82,8 @@ class MEDIA_EXPORT WebMListParser { struct ListState { int id_; - int size_; - int bytes_parsed_; + int64 size_; + int64 bytes_parsed_; const ListElementInfo* element_info_; WebMParserClient* client_; }; @@ -123,6 +123,9 @@ class MEDIA_EXPORT WebMListParser { // Returns true if no errors occurred while ending this list(s). bool OnListEnd(); + // Checks to see if |id_b| is a sibling or ancestor of |id_a|. + bool IsSiblingOrAncestor(int id_a, int id_b) const; + State state_; // Element ID passed to the constructor. @@ -150,8 +153,8 @@ class MEDIA_EXPORT WebMListParser { // |*id| contains the element ID on success and is undefined otherwise. // |*element_size| contains the element size on success and is undefined // otherwise. -int WebMParseElementHeader(const uint8* buf, int size, - int* id, int64* element_size); +int MEDIA_EXPORT WebMParseElementHeader(const uint8* buf, int size, + int* id, int64* element_size); } // namespace media diff --git a/media/webm/webm_parser_unittest.cc b/media/webm/webm_parser_unittest.cc index c7b30f7..e518386 100644 --- a/media/webm/webm_parser_unittest.cc +++ b/media/webm/webm_parser_unittest.cc @@ -352,4 +352,61 @@ TEST_F(WebMParserTest, InvalidClient) { EXPECT_FALSE(parser.IsParsingComplete()); } +TEST_F(WebMParserTest, ReservedIds) { + const uint8 k1ByteReservedId[] = { 0xFF, 0x81 }; + const uint8 k2ByteReservedId[] = { 0x7F, 0xFF, 0x81 }; + const uint8 k3ByteReservedId[] = { 0x3F, 0xFF, 0xFF, 0x81 }; + const uint8 k4ByteReservedId[] = { 0x1F, 0xFF, 0xFF, 0xFF, 0x81 }; + const uint8* kBuffers[] = { + k1ByteReservedId, + k2ByteReservedId, + k3ByteReservedId, + k4ByteReservedId + }; + + for (size_t i = 0; i < arraysize(kBuffers); i++) { + int id; + int64 element_size; + int buffer_size = 2 + i; + EXPECT_EQ(buffer_size, WebMParseElementHeader(kBuffers[i], buffer_size, + &id, &element_size)); + EXPECT_EQ(id, kWebMReservedId); + EXPECT_EQ(element_size, 1); + } +} + +TEST_F(WebMParserTest, ReservedSizes) { + const uint8 k1ByteReservedSize[] = { 0xA3, 0xFF }; + const uint8 k2ByteReservedSize[] = { 0xA3, 0x7F, 0xFF }; + const uint8 k3ByteReservedSize[] = { 0xA3, 0x3F, 0xFF, 0xFF }; + const uint8 k4ByteReservedSize[] = { 0xA3, 0x1F, 0xFF, 0xFF, 0xFF }; + const uint8 k5ByteReservedSize[] = { 0xA3, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF }; + const uint8 k6ByteReservedSize[] = { 0xA3, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF }; + const uint8 k7ByteReservedSize[] = { 0xA3, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF }; + const uint8 k8ByteReservedSize[] = { 0xA3, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF }; + const uint8* kBuffers[] = { + k1ByteReservedSize, + k2ByteReservedSize, + k3ByteReservedSize, + k4ByteReservedSize, + k5ByteReservedSize, + k6ByteReservedSize, + k7ByteReservedSize, + k8ByteReservedSize + }; + + for (size_t i = 0; i < arraysize(kBuffers); i++) { + int id; + int64 element_size; + int buffer_size = 2 + i; + EXPECT_EQ(buffer_size, WebMParseElementHeader(kBuffers[i], buffer_size, + &id, &element_size)); + EXPECT_EQ(id, 0xA3); + EXPECT_EQ(element_size, kWebMUnknownSize); + } +} + } // namespace media diff --git a/media/webm/webm_stream_parser.cc b/media/webm/webm_stream_parser.cc index d49ed7c..036f7e7b0 100644 --- a/media/webm/webm_stream_parser.cc +++ b/media/webm/webm_stream_parser.cc @@ -281,9 +281,13 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) { bytes_parsed += result; - double mult = info_parser.timecode_scale() / 1000.0; - base::TimeDelta duration = - base::TimeDelta::FromMicroseconds(info_parser.duration() * mult); + base::TimeDelta duration = kInfiniteDuration(); + + if (info_parser.duration() > 0) { + double mult = info_parser.timecode_scale() / 1000.0; + int64 duration_in_us = info_parser.duration() * mult; + duration = base::TimeDelta::FromMicroseconds(duration_in_us); + } FFmpegConfigHelper config_helper; |