diff options
author | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-04 23:26:03 +0000 |
---|---|---|
committer | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-04 23:26:03 +0000 |
commit | 42b933fe8d7733d3a5c9f2afa65b186db7c51533 (patch) | |
tree | f66dd5d673d0e446085724c1ced0543279baba07 /media | |
parent | fc0d94c64292dc6702e2b4c28e446dbcd14f0ad2 (diff) | |
download | chromium_src-42b933fe8d7733d3a5c9f2afa65b186db7c51533.zip chromium_src-42b933fe8d7733d3a5c9f2afa65b186db7c51533.tar.gz chromium_src-42b933fe8d7733d3a5c9f2afa65b186db7c51533.tar.bz2 |
Add basic parsing functionality to ChunkDemuxer for skipping WebM file headers.
BUG=86536
TEST=ChunkDemuxerTest::TestWebMFile
Review URL: http://codereview.chromium.org/8443004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@108740 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/chunk_demuxer.cc | 55 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 104 | ||||
-rw-r--r-- | media/webm/webm_constants.h | 7 | ||||
-rw-r--r-- | media/webm/webm_parser.cc | 13 | ||||
-rw-r--r-- | media/webm/webm_parser.h | 11 |
5 files changed, 156 insertions, 34 deletions
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index 8762003..0df22a5 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -324,6 +324,7 @@ void ChunkDemuxer::Init(const PipelineStatusCB& cb) { } void ChunkDemuxer::set_host(FilterHost* filter_host) { + DCHECK_EQ(state_, INITIALIZED); Demuxer::set_host(filter_host); filter_host->SetDuration(duration_); filter_host->SetCurrentReadPosition(0); @@ -577,8 +578,41 @@ int ChunkDemuxer::ParseInfoAndTracks_Locked(const uint8* data, int size) { const uint8* cur = data; int cur_size = size; int bytes_parsed = 0; + + int id; + int64 element_size; + int result = WebMParseElementHeader(cur, cur_size, &id, &element_size); + + if (result <= 0) + return result; + + switch (id) { + case kWebMIdEBML : + case kWebMIdSeekHead : + case kWebMIdVoid : + case kWebMIdCRC32 : + case kWebMIdCues : + if (cur_size < (result + element_size)) { + // We don't have the whole element yet. Signal we need more data. + return 0; + } + // Skip the element. + return result + element_size; + break; + case kWebMIdSegment : + // Just consume the segment header. + return result; + break; + case kWebMIdInfo : + // We've found the element we are looking for. + break; + default: + VLOG(1) << "Unexpected ID 0x" << std::hex << id; + return -1; + } + WebMInfoParser info_parser; - int result = info_parser.Parse(cur, cur_size); + result = info_parser.Parse(cur, cur_size); if (result <= 0) return result; @@ -686,6 +720,25 @@ int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) { if (!cluster_parser_.get()) return -1; + int id; + int64 element_size; + int result = WebMParseElementHeader(data, size, &id, &element_size); + + if (result <= 0) + return result; + + if (id == kWebMIdCues) { + if (size < (result + element_size)) { + // We don't have the whole element yet. Signal we need more data. + return 0; + } + // 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); if (bytes_parsed <= 0) diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index dc99422..bffb8ad 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -44,7 +44,7 @@ class MockChunkDemuxerClient : public ChunkDemuxerClient { DISALLOW_COPY_AND_ASSIGN(MockChunkDemuxerClient); }; -class ChunkDemuxerTest : public testing::Test{ +class ChunkDemuxerTest : public testing::Test { protected: enum CodecsIndex { AUDIO, @@ -121,11 +121,14 @@ class ChunkDemuxerTest : public testing::Test{ } void AppendDataInPieces(const uint8* data, size_t length) { + AppendDataInPieces(data, length, 7); + } + + void AppendDataInPieces(const uint8* data, size_t length, size_t piece_size) { const uint8* start = data; const uint8* end = data + length; - size_t default_size = 7; while (start < end) { - size_t append_size = std::min(default_size, + size_t append_size = std::min(piece_size, static_cast<size_t>(end - start)); AppendData(start, append_size); start += append_size; @@ -139,30 +142,35 @@ class ChunkDemuxerTest : public testing::Test{ AppendData(info_tracks.get(), info_tracks_size); } - static void InitDoneCalled(bool* was_called, PipelineStatus expectedStatus, - PipelineStatus status) { - EXPECT_EQ(status, expectedStatus); - *was_called = true; + void InitDoneCalled(const base::TimeDelta& expected_duration, + PipelineStatus expected_status, + PipelineStatus status) { + EXPECT_EQ(status, expected_status); + + if (status == PIPELINE_OK) { + EXPECT_CALL(mock_filter_host_, SetDuration(expected_duration)); + EXPECT_CALL(mock_filter_host_, SetCurrentReadPosition(_)); + + demuxer_->set_host(&mock_filter_host_); + } + } + + PipelineStatusCB CreateInitDoneCB(int duration, + PipelineStatus expected_status) { + return base::Bind(&ChunkDemuxerTest::InitDoneCalled, + base::Unretained(this), + base::TimeDelta::FromMilliseconds(duration), + expected_status); } void InitDemuxer(bool has_audio, bool has_video) { - bool init_done_called = false; - PipelineStatus expectedStatus = + PipelineStatus expected_status = (has_audio || has_video) ? PIPELINE_OK : DEMUXER_ERROR_COULD_NOT_OPEN; EXPECT_CALL(*client_, DemuxerOpened(_)); - demuxer_->Init(base::Bind(&ChunkDemuxerTest::InitDoneCalled, - &init_done_called, - expectedStatus)); - - EXPECT_FALSE(init_done_called); + demuxer_->Init(CreateInitDoneCB(201224, expected_status)); AppendInfoTracks(has_audio, has_video); - - EXPECT_TRUE(init_done_called); - EXPECT_CALL(mock_filter_host_, SetDuration(_)); - EXPECT_CALL(mock_filter_host_, SetCurrentReadPosition(_)); - demuxer_->set_host(&mock_filter_host_); } void ShutdownDemuxer() { @@ -651,11 +659,7 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) { TEST_F(ChunkDemuxerTest, TestAppendingInPieces) { EXPECT_CALL(*client_, DemuxerOpened(_)); - demuxer_->Init(NewExpectedStatusCB(PIPELINE_OK)); - - EXPECT_CALL(mock_filter_host_, SetDuration(_)); - EXPECT_CALL(mock_filter_host_, SetCurrentReadPosition(_)); - demuxer_->set_host(&mock_filter_host_); + demuxer_->Init(CreateInitDoneCB(201224, PIPELINE_OK)); scoped_array<uint8> info_tracks; int info_tracks_size = 0; @@ -721,4 +725,56 @@ TEST_F(ChunkDemuxerTest, TestAppendingInPieces) { EXPECT_TRUE(video_read_done); } +struct BufferTimestamps { + int video_time; + int audio_time; +}; + +TEST_F(ChunkDemuxerTest, TestWebMFile) { + scoped_array<uint8> buffer; + int buffer_size = 0; + + EXPECT_CALL(*client_, DemuxerOpened(_)); + demuxer_->Init(CreateInitDoneCB(2744, PIPELINE_OK)); + + // Read a WebM file into memory and send the data to the demuxer. + ReadTestDataFile("bear-320x240.webm", &buffer, &buffer_size); + AppendDataInPieces(buffer.get(), buffer_size, 512); + + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + scoped_refptr<DemuxerStream> video = + demuxer_->GetStream(DemuxerStream::VIDEO); + + ASSERT_TRUE(audio); + ASSERT_TRUE(video); + + struct BufferTimestamps buffer_timestamps[] = { + {0, 0}, + {33, 3}, + {67, 6}, + {100, 9}, + {133, 12}, + }; + + // Verify that the timestamps on the first few packets match what we + // expect. + for (size_t i = 0; i < arraysize(buffer_timestamps); i++) { + bool audio_read_done = false; + bool video_read_done = false; + audio->Read(base::Bind(&OnReadDone, + base::TimeDelta::FromMilliseconds( + buffer_timestamps[i].audio_time), + &audio_read_done)); + + video->Read(base::Bind(&OnReadDone, + base::TimeDelta::FromMilliseconds( + buffer_timestamps[i].video_time), + &video_read_done)); + + EXPECT_TRUE(audio_read_done); + EXPECT_TRUE(video_read_done); + } +} + } // namespace media diff --git a/media/webm/webm_constants.h b/media/webm/webm_constants.h index aa6dda0..471115b 100644 --- a/media/webm/webm_constants.h +++ b/media/webm/webm_constants.h @@ -20,12 +20,15 @@ const int kWebMIdCluster = 0x1f43b675; const int kWebMIdCodecID = 0x86; const int kWebMIdCodecName = 0x258688; const int kWebMIdCodecPrivate = 0x63A2; +const int kWebMIdCRC32 = 0xBF; +const int kWebMIdCues = 0x1C53BB6B; const int kWebMIdDateUTC = 0x4461; const int kWebMIdDefaultDuration = 0x23E383; const int kWebMIdDisplayHeight = 0x54BA; const int kWebMIdDisplayUnit = 0x54B2; const int kWebMIdDisplayWidth = 0x54B0; const int kWebMIdDuration = 0x4489; +const int kWebMIdEBML = 0x1A45DFA3; const int kWebMIdFlagDefault = 0x88; const int kWebMIdFlagEnabled = 0xB9; const int kWebMIdFlagForced = 0x55AA; @@ -42,7 +45,10 @@ const int kWebMIdPixelCropRight = 0x54DD; const int kWebMIdPixelCropTop = 0x54BB; const int kWebMIdPixelHeight = 0xBA; const int kWebMIdPixelWidth = 0xB0; +const int kWebMIdPrevSize = 0xAB; const int kWebMIdSamplingFrequency = 0xB5; +const int kWebMIdSeekHead = 0x114D9B74; +const int kWebMIdSegment = 0x18538067; const int kWebMIdSegmentUID = 0x73A4; const int kWebMIdSimpleBlock = 0xA3; const int kWebMIdStereoMode = 0x53B8; @@ -55,6 +61,7 @@ const int kWebMIdTrackType = 0x83; const int kWebMIdTrackUID = 0x73C5; const int kWebMIdTracks = 0x1654AE6B; const int kWebMIdVideo = 0xE0; +const int kWebMIdVoid = 0xEC; const int kWebMIdWritingApp = 0x5741; // Default timecode scale if the TimecodeScale element is diff --git a/media/webm/webm_parser.cc b/media/webm/webm_parser.cc index 43dd509..1e42815 100644 --- a/media/webm/webm_parser.cc +++ b/media/webm/webm_parser.cc @@ -177,13 +177,8 @@ static int ParseWebMElementHeaderField(const uint8* buf, int size, return bytes_used; } -// Parses an element header & returns the ID and element size. -// -// Returns: The number of bytes parsed on success. -1 on error. -// |*id| contains the element ID on success & undefined on error. -// |*element_size| contains the element size on success & undefined on error. -static int ParseWebMElementHeader(const uint8* buf, int size, - int* id, int64* element_size) { +int WebMParseElementHeader(const uint8* buf, int size, + int* id, int64* element_size) { DCHECK(buf); DCHECK_GE(size, 0); DCHECK(id); @@ -377,7 +372,7 @@ static int ParseElements(const ElementIdInfo* id_info, while (cur_size > 0) { int id = 0; int64 element_size = 0; - int result = ParseWebMElementHeader(cur, cur_size, &id, &element_size); + int result = WebMParseElementHeader(cur, cur_size, &id, &element_size); if (result <= 0) return result; @@ -467,7 +462,7 @@ int WebMParseListElement(const uint8* buf, int size, int id, int bytes_parsed = 0; int element_id = 0; int64 element_size = 0; - int result = ParseWebMElementHeader(cur, cur_size, &element_id, + int result = WebMParseElementHeader(cur, cur_size, &element_id, &element_size); if (result <= 0) diff --git a/media/webm/webm_parser.h b/media/webm/webm_parser.h index 92dd679..7d15f9b 100644 --- a/media/webm/webm_parser.h +++ b/media/webm/webm_parser.h @@ -36,6 +36,17 @@ class WebMParserClient { const uint8* data, int size) = 0; }; +// Parses an element header & returns the ID and element size. +// +// Returns < 0 if the parse fails. +// Returns 0 if more data is needed. +// Returning > 0 indicates success & the number of bytes parsed. +// |*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); + // Parses a single list element that matches |id|. This method fails if the // buffer points to an element that does not match |id|. // |