diff options
-rw-r--r-- | media/filters/chunk_demuxer.cc | 3 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 44 | ||||
-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 |
7 files changed, 163 insertions, 35 deletions
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index 95cd9e7..70b0f46 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -424,8 +424,7 @@ bool ChunkDemuxer::IsLocalSource() { } bool ChunkDemuxer::IsSeekable() { - // TODO(acolwell): Report whether source is seekable or not. - return true; + return duration_ != kInfiniteDuration(); } // Demuxer implementation. diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index 087fafe..38e960ec 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -38,6 +38,10 @@ static const int kVideoTrackEntryHeaderSize = kVideoTrackSizeOffset + static const int kVideoTrackNum = 1; static const int kAudioTrackNum = 2; +base::TimeDelta kDefaultDuration() { + return base::TimeDelta::FromMilliseconds(201224); +} + // Write an integer into buffer in the form of vint that spans 8 bytes. // The data pointed by |buffer| should be at least 8 bytes long. // |number| should be in the range 0 <= number < 0x00FFFFFFFFFFFFFF. @@ -204,17 +208,12 @@ class ChunkDemuxerTest : public testing::Test { } } - PipelineStatusCB CreateInitDoneCB(int duration, - PipelineStatus expected_status) { - return CreateInitDoneCB(duration, expected_status, true); - } - - PipelineStatusCB CreateInitDoneCB(int duration, + PipelineStatusCB CreateInitDoneCB(const base::TimeDelta& duration, PipelineStatus expected_status, bool call_set_host) { return base::Bind(&ChunkDemuxerTest::InitDoneCalled, base::Unretained(this), - base::TimeDelta::FromMilliseconds(duration), + duration, expected_status, call_set_host); } @@ -225,7 +224,7 @@ class ChunkDemuxerTest : public testing::Test { (has_audio || has_video) ? PIPELINE_OK : DEMUXER_ERROR_COULD_NOT_OPEN; EXPECT_CALL(*client_, DemuxerOpened(_)); - demuxer_->Init(CreateInitDoneCB(201224, expected_status)); + demuxer_->Init(CreateInitDoneCB(kDefaultDuration(), expected_status, true)); return AppendInfoTracks(has_audio, has_video, video_content_encoded); } @@ -272,12 +271,12 @@ class ChunkDemuxerTest : public testing::Test { // a kSkip then the loop will terminate. bool ParseWebMFile(const std::string& filename, const BufferTimestamps* timestamps, - int duration) { + const base::TimeDelta& duration) { scoped_array<uint8> buffer; int buffer_size = 0; EXPECT_CALL(*client_, DemuxerOpened(_)); - demuxer_->Init(CreateInitDoneCB(duration, PIPELINE_OK)); + demuxer_->Init(CreateInitDoneCB(duration, PIPELINE_OK, true)); // Read a WebM file into memory and send the data to the demuxer. ReadTestDataFile(filename, &buffer, &buffer_size); @@ -837,7 +836,7 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) { TEST_F(ChunkDemuxerTest, TestAppendingInPieces) { EXPECT_CALL(*client_, DemuxerOpened(_)); - demuxer_->Init(CreateInitDoneCB(201224, PIPELINE_OK)); + demuxer_->Init(CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK, true)); scoped_array<uint8> info_tracks; int info_tracks_size = 0; @@ -913,7 +912,22 @@ TEST_F(ChunkDemuxerTest, TestWebMFile_AudioAndVideo) { {kSkip, kSkip}, }; - ASSERT_TRUE(ParseWebMFile("bear-320x240.webm", buffer_timestamps, 2744)); + ASSERT_TRUE(ParseWebMFile("bear-320x240.webm", buffer_timestamps, + base::TimeDelta::FromMilliseconds(2744))); +} + +TEST_F(ChunkDemuxerTest, TestWebMFile_LiveAudioAndVideo) { + struct BufferTimestamps buffer_timestamps[] = { + {0, 0}, + {33, 3}, + {67, 6}, + {100, 9}, + {133, 12}, + {kSkip, kSkip}, + }; + + ASSERT_TRUE(ParseWebMFile("bear-320x240-live.webm", buffer_timestamps, + kInfiniteDuration())); } TEST_F(ChunkDemuxerTest, TestWebMFile_AudioOnly) { @@ -927,7 +941,7 @@ TEST_F(ChunkDemuxerTest, TestWebMFile_AudioOnly) { }; ASSERT_TRUE(ParseWebMFile("bear-320x240-audio-only.webm", buffer_timestamps, - 2744)); + base::TimeDelta::FromMilliseconds(2744))); } TEST_F(ChunkDemuxerTest, TestWebMFile_VideoOnly) { @@ -941,7 +955,7 @@ TEST_F(ChunkDemuxerTest, TestWebMFile_VideoOnly) { }; ASSERT_TRUE(ParseWebMFile("bear-320x240-video-only.webm", buffer_timestamps, - 2703)); + base::TimeDelta::FromMilliseconds(2703))); } // Verify that we output buffers before the entire cluster has been parsed. @@ -1018,7 +1032,7 @@ TEST_F(ChunkDemuxerTest, TestIncrementalClusterParsing) { TEST_F(ChunkDemuxerTest, TestParseErrorDuringInit) { EXPECT_CALL(*client_, DemuxerOpened(_)); - demuxer_->Init(CreateInitDoneCB(201224, PIPELINE_OK, false)); + demuxer_->Init(CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK, false)); ASSERT_TRUE(AppendInfoTracks(true, true, false)); uint8 tmp = 0; 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; |