diff options
author | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-30 21:04:13 +0000 |
---|---|---|
committer | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-30 21:04:13 +0000 |
commit | 898271fc1f5d8da5ac517b45540f03e1e2be8043 (patch) | |
tree | 0209e48667ecd914443f7d181a0299f9b2fa36dc /media | |
parent | 12c561d3a704e6ca0a8b20e3821c94d2de14af91 (diff) | |
download | chromium_src-898271fc1f5d8da5ac517b45540f03e1e2be8043.zip chromium_src-898271fc1f5d8da5ac517b45540f03e1e2be8043.tar.gz chromium_src-898271fc1f5d8da5ac517b45540f03e1e2be8043.tar.bz2 |
Improve ChunkDemuxer so AppendData() calls no longer have to be done on element boundries.
BUG=86536
TEST=ChunkDemuxerTest::TestAppendingInPieces
Review URL: http://codereview.chromium.org/7981024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@103543 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/byte_queue.cc | 79 | ||||
-rw-r--r-- | media/base/byte_queue.h | 54 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.cc | 187 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.h | 28 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 95 | ||||
-rw-r--r-- | media/media.gyp | 2 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser.h | 5 | ||||
-rw-r--r-- | media/webm/webm_info_parser.h | 5 | ||||
-rw-r--r-- | media/webm/webm_parser.cc | 85 | ||||
-rw-r--r-- | media/webm/webm_parser.h | 4 | ||||
-rw-r--r-- | media/webm/webm_tracks_parser.h | 5 |
11 files changed, 415 insertions, 134 deletions
diff --git a/media/base/byte_queue.cc b/media/base/byte_queue.cc new file mode 100644 index 0000000..8f4fee8 --- /dev/null +++ b/media/base/byte_queue.cc @@ -0,0 +1,79 @@ +// 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/base/byte_queue.h" + +#include "base/logging.h" + +namespace media { + +// Default starting size for the queue. +enum { kDefaultQueueSize = 1024 }; + +ByteQueue::ByteQueue() + : buffer_(new uint8[kDefaultQueueSize]), + size_(kDefaultQueueSize), + offset_(0), + used_(0) { +} + +ByteQueue::~ByteQueue() {} + +void ByteQueue::Push(const uint8* data, int size) { + DCHECK(data); + DCHECK_GT(size, 0); + + size_t size_needed = used_ + size; + + // Check to see if we need a bigger buffer. + if (size_needed > size_) { + size_t new_size = 2 * size_; + while (size_needed > new_size && new_size > size_) + new_size *= 2; + + // Sanity check to make sure we didn't overflow. + CHECK_GT(new_size, size_); + + scoped_array<uint8> new_buffer(new uint8[new_size]); + + // Copy the data from the old buffer to the start of the new one. + if (used_ > 0) + memcpy(new_buffer.get(), front(), used_); + + buffer_.reset(new_buffer.release()); + size_ = new_size; + offset_ = 0; + } else if ((offset_ + used_ + size) > size_) { + // The buffer is big enough, but we need to move the data in the queue. + memmove(buffer_.get(), front(), used_); + offset_ = 0; + } + + memcpy(front() + used_, data, size); + used_ += size; +} + +void ByteQueue::Peek(const uint8** data, int* size) const { + DCHECK(data); + DCHECK(size); + *data = front(); + *size = used_; +} + +void ByteQueue::Pop(int count) { + DCHECK_LE(count, used_); + + offset_ += count; + used_ -= count; + + // Move the offset back to 0 if we have reached the end of the buffer. + if (offset_ == size_) { + DCHECK_EQ(used_, 0); + offset_ = 0; + } +} + +uint8* ByteQueue::front() const { return buffer_.get() + offset_; } + +} // namespace media diff --git a/media/base/byte_queue.h b/media/base/byte_queue.h new file mode 100644 index 0000000..69f2728 --- /dev/null +++ b/media/base/byte_queue.h @@ -0,0 +1,54 @@ +// 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. + +#ifndef MEDIA_BASE_BYTE_QUEUE_H_ +#define MEDIA_BASE_BYTE_QUEUE_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +namespace media { + +// Represents a queue of bytes. +// Data is added to the end of the queue via an Push() call and removed via +// Pop(). The contents of the queue can be observed via the Peek() method. +// This class manages the underlying storage of the queue and tries to minimize +// the number of buffer copies when data is appended and removed. +class ByteQueue { + public: + ByteQueue(); + ~ByteQueue(); + + // Appends new bytes onto the end of the queue. + void Push(const uint8* data, int size); + + // Get a pointer to the front of the queue and the queue size. + // These values are only valid until the next Push() or + // Pop() call. + void Peek(const uint8** data, int* size) const; + + // Remove |count| bytes from the front of the queue. + void Pop(int count); + + private: + // Returns a pointer to the front of the queue. + uint8* front() const; + + scoped_array<uint8> buffer_; + + // Size of |buffer_|. + size_t size_; + + // Offset from the start of |buffer_| that marks the front of the queue. + size_t offset_; + + // Number of bytes stored in the queue. + int used_; + + DISALLOW_COPY_AND_ASSIGN(ByteQueue); +}; + +} // namespace media + +#endif // MEDIA_BASE_BYTE_QUEUE_H_ diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index ea066d8..d8e0604 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -322,7 +322,7 @@ void ChunkDemuxer::Init(const PipelineStatusCB& cb) { base::AutoLock auto_lock(lock_); DCHECK_EQ(state_, WAITING_FOR_INIT); - ChangeState(INITIALIZING); + ChangeState_Locked(INITIALIZING); init_cb_ = cb; } @@ -402,13 +402,14 @@ void ChunkDemuxer::FlushData() { video_->Flush(); seek_waits_for_data_ = true; - ChangeState(INITIALIZED); + ChangeState_Locked(INITIALIZED); } -bool ChunkDemuxer::AppendData(const uint8* data, unsigned length) { +bool ChunkDemuxer::AppendData(const uint8* data, size_t length) { VLOG(1) << "AppendData(" << length << ")"; - DCHECK(data); - DCHECK_GT(length, 0u); + + if (!data || length == 0u) + return false; int64 buffered_bytes = 0; base::TimeDelta buffered_ts = base::TimeDelta::FromSeconds(-1); @@ -416,32 +417,62 @@ bool ChunkDemuxer::AppendData(const uint8* data, unsigned length) { PipelineStatusCB cb; { base::AutoLock auto_lock(lock_); - switch(state_) { - case INITIALIZING: - if (!ParseInfoAndTracks_Locked(data, length)) { - VLOG(1) << "AppendData(): parsing info & tracks failed"; - ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); - } - return true; - break; - - case INITIALIZED: - if (!ParseAndAppendData_Locked(data, length)) { - VLOG(1) << "AppendData(): parsing data failed"; - ReportError_Locked(PIPELINE_ERROR_DECODE); - return true; - } - break; - - case WAITING_FOR_INIT: - case ENDED: - case PARSE_ERROR: - case SHUTDOWN: - VLOG(1) << "AppendData(): called in unexpected state " << state_; - return false; - } - seek_waits_for_data_ = false; + byte_queue_.Push(data, length); + + const uint8* cur = NULL; + int cur_size = 0; + int bytes_parsed = 0; + int result = -1; + bool parsed_a_cluster = false; + + byte_queue_.Peek(&cur, &cur_size); + + do { + switch(state_) { + case INITIALIZING: + result = ParseInfoAndTracks_Locked(cur, cur_size); + if (result < 0) { + VLOG(1) << "AppendData(): parsing info & tracks failed"; + ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); + return true; + } + break; + + case INITIALIZED: + result = ParseCluster_Locked(cur, cur_size); + if (result < 0) { + VLOG(1) << "AppendData(): parsing data failed"; + ReportError_Locked(PIPELINE_ERROR_DECODE); + return true; + } + + parsed_a_cluster = (result > 0); + break; + + case WAITING_FOR_INIT: + case ENDED: + case PARSE_ERROR: + case SHUTDOWN: + VLOG(1) << "AppendData(): called in unexpected state " << state_; + return false; + } + + if (result > 0) { + cur += result; + cur_size -= result; + bytes_parsed += result; + } + } while (result > 0 && cur_size > 0); + + byte_queue_.Pop(bytes_parsed); + + if (parsed_a_cluster && seek_waits_for_data_) { + seek_waits_for_data_ = false; + + if (!seek_cb_.is_null()) + std::swap(cb, seek_cb_); + } base::TimeDelta tmp; if (audio_.get() && audio_->GetLastBufferTimestamp(&tmp) && @@ -455,9 +486,6 @@ bool ChunkDemuxer::AppendData(const uint8* data, unsigned length) { } buffered_bytes = buffered_bytes_; - - if (!seek_cb_.is_null()) - std::swap(cb, seek_cb_); } // Notify the host of 'network activity' because we got data. @@ -491,7 +519,7 @@ void ChunkDemuxer::EndOfStream(PipelineStatus status) { return; } - ChangeState(ENDED); + ChangeState_Locked(ENDED); if (status != PIPELINE_OK) { ReportError_Locked(status); @@ -531,7 +559,7 @@ void ChunkDemuxer::Shutdown() { if (video_.get()) video_->Shutdown(); - ChangeState(SHUTDOWN); + ChangeState_Locked(SHUTDOWN); } if (!cb.is_null()) @@ -540,12 +568,13 @@ void ChunkDemuxer::Shutdown() { client_->DemuxerClosed(); } -void ChunkDemuxer::ChangeState(State new_state) { +void ChunkDemuxer::ChangeState_Locked(State new_state) { lock_.AssertAcquired(); state_ = new_state; } -bool ChunkDemuxer::ParseInfoAndTracks_Locked(const uint8* data, int size) { +int ChunkDemuxer::ParseInfoAndTracks_Locked(const uint8* data, int size) { + lock_.AssertAcquired(); DCHECK(data); DCHECK_GT(size, 0); @@ -553,20 +582,24 @@ bool ChunkDemuxer::ParseInfoAndTracks_Locked(const uint8* data, int size) { const uint8* cur = data; int cur_size = size; + int bytes_parsed = 0; WebMInfoParser info_parser; - int res = info_parser.Parse(cur, cur_size); + int result = info_parser.Parse(cur, cur_size); - if (res <= 0) - return false; + if (result <= 0) + return result; - cur += res; - cur_size -= res; + cur += result; + cur_size -= result; + bytes_parsed += result; WebMTracksParser tracks_parser(info_parser.timecode_scale()); - res = tracks_parser.Parse(cur, cur_size); + result = tracks_parser.Parse(cur, cur_size); - if (res <= 0) - return false; + if (result <= 0) + return result; + + bytes_parsed += result; double mult = info_parser.timecode_scale() / 1000.0; duration_ = base::TimeDelta::FromMicroseconds(info_parser.duration() * mult); @@ -578,16 +611,14 @@ bool ChunkDemuxer::ParseInfoAndTracks_Locked(const uint8* data, int size) { tracks_parser.video_track_num(), tracks_parser.video_default_duration())); - format_context_ = CreateFormatContext(data, size); - - if (!format_context_ || !SetupStreams()) { - return false; - } + format_context_ = CreateFormatContext(data, bytes_parsed); + if (!format_context_ || !SetupStreams()) + return -1; - ChangeState(INITIALIZED); + ChangeState_Locked(INITIALIZED); init_cb_.Run(PIPELINE_OK); init_cb_.Reset(); - return true; + return bytes_parsed; } AVFormatContext* ChunkDemuxer::CreateFormatContext(const uint8* data, @@ -656,51 +687,43 @@ bool ChunkDemuxer::SetupStreams() { return !no_supported_streams; } -bool ChunkDemuxer::ParseAndAppendData_Locked(const uint8* data, int length) { +int ChunkDemuxer::ParseCluster_Locked(const uint8* data, int size) { + lock_.AssertAcquired(); if (!cluster_parser_.get()) - return false; + return -1; - const uint8* cur = data; - int cur_size = length; + int bytes_parsed = cluster_parser_->Parse(data, size); - while (cur_size > 0) { - int res = cluster_parser_->Parse(cur, cur_size); + if (bytes_parsed <= 0) + return bytes_parsed; - if (res <= 0) { - VLOG(1) << "ParseAndAppendData_Locked() : cluster parsing failed."; - return false; - } - - // Make sure we can add the buffers to both streams before we acutally - // 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 false; - } - - if (audio_.get()) - audio_->AddBuffers(cluster_parser_->audio_buffers()); + // 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 (video_.get()) - video_->AddBuffers(cluster_parser_->video_buffers()); + if (audio_.get()) + audio_->AddBuffers(cluster_parser_->audio_buffers()); - cur += res; - cur_size -= res; - } + if (video_.get()) + video_->AddBuffers(cluster_parser_->video_buffers()); // TODO(acolwell) : make this more representative of what is actually // buffered. - buffered_bytes_ += length; + buffered_bytes_ += bytes_parsed; - return true; + return bytes_parsed; } void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { + lock_.AssertAcquired(); DCHECK_NE(error, PIPELINE_OK); - ChangeState(PARSE_ERROR); + ChangeState_Locked(PARSE_ERROR); PipelineStatusCB cb; diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h index 14f6fb5..8f09761 100644 --- a/media/filters/chunk_demuxer.h +++ b/media/filters/chunk_demuxer.h @@ -8,6 +8,7 @@ #include <list> #include "base/synchronization/lock.h" +#include "media/base/byte_queue.h" #include "media/base/demuxer.h" #include "media/webm/webm_cluster_parser.h" @@ -42,7 +43,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { // Appends media data to the stream. Returns false if this method // is called in an invalid state. - bool AppendData(const uint8* data, unsigned length); + bool AppendData(const uint8* data, size_t length); void EndOfStream(PipelineStatus status); bool HasEnded(); void Shutdown(); @@ -57,12 +58,15 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { SHUTDOWN, }; - void ChangeState(State new_state); + void ChangeState_Locked(State new_state); - // Parses a buffer that contains an INFO & TRACKS element. Returns false if - // the parse fails. This method handles calling & clearing |init_cb_| - // before it returns. - bool ParseInfoAndTracks_Locked(const uint8* data, int size); + // Parses a buffer that contains an INFO & TRACKS element. This method handles + // calling & clearing |init_cb_| before it returns. + // + // Returns -1 if the parse fails. + // Returns 0 if more data is needed. + // Returns the number of bytes parsed on success. + int ParseInfoAndTracks_Locked(const uint8* data, int size); // Generates an AVFormatContext for the INFO & TRACKS elements contained // in |data|. Returns NULL if parsing |data| fails. @@ -73,9 +77,13 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { // found. bool SetupStreams(); - // Parse a buffer that was passed to AppendData(). |data| is expected to - // contain one or more WebM Clusters. Returns false if parsing the data fails. - bool ParseAndAppendData_Locked(const uint8* data, int length); + // Parse a cluster add add the buffers to the appropriate DemxuerStream. + // |data| is expected to point to the beginning of a cluster element. + // + // 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); // Reports an error and puts the demuxer in a state where it won't accept more // data. @@ -112,6 +120,8 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { // callback. bool seek_waits_for_data_; + ByteQueue byte_queue_; + DISALLOW_COPY_AND_ASSIGN(ChunkDemuxer); }; diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index 6b0ee3b..dd3dbd98 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -113,18 +113,29 @@ class ChunkDemuxerTest : public testing::Test{ } } - void AppendData(const uint8* data, unsigned length) { + void AppendData(const uint8* data, size_t length) { EXPECT_CALL(mock_filter_host_, SetBufferedBytes(_)).Times(AnyNumber()); EXPECT_CALL(mock_filter_host_, SetBufferedTime(_)).Times(AnyNumber()); EXPECT_CALL(mock_filter_host_, SetNetworkActivity(true)).Times(AnyNumber()); EXPECT_TRUE(demuxer_->AppendData(data, length)); } + void AppendDataInPieces(const uint8* data, size_t length) { + 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, + static_cast<size_t>(end - start)); + AppendData(start, append_size); + start += append_size; + } + } + void AppendInfoTracks(bool has_audio, bool has_video) { scoped_array<uint8> info_tracks; int info_tracks_size = 0; CreateInfoTracks(has_audio, has_video, &info_tracks, &info_tracks_size); - AppendData(info_tracks.get(), info_tracks_size); } @@ -166,6 +177,12 @@ class ChunkDemuxerTest : public testing::Test{ cb->AddSimpleBlock(track_num, timecode, 0, data, sizeof(data)); } + void AddSimpleBlock(ClusterBuilder* cb, int track_num, int64 timecode, + int size) { + scoped_array<uint8> data(new uint8[size]); + cb->AddSimpleBlock(track_num, timecode, 0, data.get(), size); + } + MOCK_METHOD1(Checkpoint, void(int id)); MockFilterHost mock_filter_host_; @@ -629,4 +646,78 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) { end_of_stream_helper_3.CheckIfReadDonesWereCalled(true); } +// Make sure AppendData() will accept elements that span multiple calls. +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_); + + scoped_array<uint8> info_tracks; + int info_tracks_size = 0; + CreateInfoTracks(true, true, &info_tracks, &info_tracks_size); + + ClusterBuilder cb; + cb.SetClusterTimecode(0); + AddSimpleBlock(&cb, kAudioTrackNum, 32, 512); + AddSimpleBlock(&cb, kVideoTrackNum, 123, 1024); + scoped_ptr<Cluster> clusterA(cb.Finish()); + + cb.SetClusterTimecode(125); + AddSimpleBlock(&cb, kAudioTrackNum, 125, 2048); + AddSimpleBlock(&cb, kVideoTrackNum, 150, 2048); + scoped_ptr<Cluster> clusterB(cb.Finish()); + + size_t buffer_size = info_tracks_size + clusterA->size() + clusterB->size(); + scoped_array<uint8> buffer(new uint8[buffer_size]); + uint8* dst = buffer.get(); + memcpy(dst, info_tracks.get(), info_tracks_size); + dst += info_tracks_size; + + memcpy(dst, clusterA->data(), clusterA->size()); + dst += clusterA->size(); + + memcpy(dst, clusterB->data(), clusterB->size()); + dst += clusterB->size(); + + AppendDataInPieces(buffer.get(), buffer_size); + + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + scoped_refptr<DemuxerStream> video = + demuxer_->GetStream(DemuxerStream::VIDEO); + + ASSERT_TRUE(audio); + ASSERT_TRUE(video); + + bool audio_read_done = false; + bool video_read_done = false; + audio->Read(base::Bind(&OnReadDone, + base::TimeDelta::FromMilliseconds(32), + &audio_read_done)); + + video->Read(base::Bind(&OnReadDone, + base::TimeDelta::FromMilliseconds(123), + &video_read_done)); + + EXPECT_TRUE(audio_read_done); + EXPECT_TRUE(video_read_done); + + audio_read_done = false; + video_read_done = false; + audio->Read(base::Bind(&OnReadDone, + base::TimeDelta::FromMilliseconds(125), + &audio_read_done)); + + video->Read(base::Bind(&OnReadDone, + base::TimeDelta::FromMilliseconds(150), + &video_read_done)); + + EXPECT_TRUE(audio_read_done); + EXPECT_TRUE(video_read_done); +} + } // namespace media diff --git a/media/media.gyp b/media/media.gyp index 598378b..3b49e7b 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -89,6 +89,8 @@ 'base/bitstream_buffer.h', 'base/buffers.cc', 'base/buffers.h', + 'base/byte_queue.cc', + 'base/byte_queue.h', 'base/channel_layout.cc', 'base/channel_layout.h', 'base/clock.cc', diff --git a/media/webm/webm_cluster_parser.h b/media/webm/webm_cluster_parser.h index 0266086..ee1caf8 100644 --- a/media/webm/webm_cluster_parser.h +++ b/media/webm/webm_cluster_parser.h @@ -27,8 +27,9 @@ class WebMClusterParser : public WebMParserClient { // Parses a WebM cluster element in |buf|. // - // Returns the number of bytes parsed on success. Returns -1 - // if a parse error occurs. + // Returns -1 if the parse fails. + // Returns 0 if more data is needed. + // Returns the number of bytes parsed on success. int Parse(const uint8* buf, int size); const BufferQueue& audio_buffers() const { return audio_buffers_; } diff --git a/media/webm/webm_info_parser.h b/media/webm/webm_info_parser.h index 6391f8b..37cf01c 100644 --- a/media/webm/webm_info_parser.h +++ b/media/webm/webm_info_parser.h @@ -17,8 +17,9 @@ class WebMInfoParser : public WebMParserClient { // Parses a WebM Info element in |buf|. // - // Returns the number of bytes parsed on success. Returns -1 - // on error. + // Returns -1 if the parse fails. + // Returns 0 if more data is needed. + // Returns the number of bytes parsed on success. int Parse(const uint8* buf, int size); int64 timecode_scale() const { return timecode_scale_; } diff --git a/media/webm/webm_parser.cc b/media/webm/webm_parser.cc index 083ebff..43dd509 100644 --- a/media/webm/webm_parser.cc +++ b/media/webm/webm_parser.cc @@ -144,9 +144,12 @@ static int ParseWebMElementHeaderField(const uint8* buf, int size, DCHECK(buf); DCHECK(num); - if (size <= 0) + if (size < 0) return -1; + if (size == 0) + return 0; + int mask = 0x80; uint8 ch = buf[0]; int extra_bytes = -1; @@ -159,9 +162,13 @@ static int ParseWebMElementHeaderField(const uint8* buf, int size, mask >>= 1; } - if ((extra_bytes == -1) || ((1 + extra_bytes) > size)) + if (extra_bytes == -1) return -1; + // Return 0 if we need more data. + if ((1 + extra_bytes) > size) + return 0; + int bytes_used = 1; for (int i = 0; i < extra_bytes; ++i) @@ -281,20 +288,20 @@ static int ParseElementList(const uint8* buf, int size, if (!client->OnListStart(id)) return -1; - int res = ParseElements(list_info->id_info_, - list_info->id_info_size_, - buf, size, - level + 1, - client); + int result = ParseElements(list_info->id_info_, + list_info->id_info_size_, + buf, size, + level + 1, + client); - if (res < 0) - return -1; + if (result <= 0) + return result; if (!client->OnListEnd(id)) return -1; - DCHECK_EQ(res, size); - return res; + DCHECK_EQ(result, size); + return result; } static int ParseUInt(const uint8* buf, int size, int id, @@ -370,21 +377,18 @@ static int ParseElements(const ElementIdInfo* id_info, while (cur_size > 0) { int id = 0; int64 element_size = 0; - int res = ParseWebMElementHeader(cur, cur_size, &id, &element_size); - - if (res < 0) - return res; + int result = ParseWebMElementHeader(cur, cur_size, &id, &element_size); - if (res == 0) - break; + if (result <= 0) + return result; - cur += res; - cur_size -= res; - used += res; + cur += result; + cur_size -= result; + used += result; // Check to see if the element is larger than the remaining data. if (element_size > cur_size) - return -1; + return 0; const ElementIdInfo* info = FindIdInfo(id, id_info, id_info_size); @@ -452,34 +456,45 @@ static int ParseElements(const ElementIdInfo* id_info, // 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) + if (size < 0) return -1; + if (size == 0) + return 0; + const uint8* cur = buf; int cur_size = size; - + int bytes_parsed = 0; int element_id = 0; int64 element_size = 0; - int res = ParseWebMElementHeader(cur, cur_size, &element_id, &element_size); + int result = ParseWebMElementHeader(cur, cur_size, &element_id, + &element_size); - if (res <= 0) - return res; + if (result <= 0) + return result; - cur += res; - cur_size -= res; + cur += result; + cur_size -= result; + bytes_parsed += result; - if (element_id != id || element_size > cur_size) + if (element_id != id) return -1; - res = ParseElementList(cur, element_size, element_id, level, client); + if (element_size > cur_size) + return 0; - if (res < 0) - return -1; + if (element_size > 0) { + result = ParseElementList(cur, element_size, element_id, level, client); - cur += res; - cur_size -= res; + if (result <= 0) + return result; + + cur += result; + cur_size -= result; + bytes_parsed += result; + } - return size - cur_size; + return bytes_parsed; } } // namespace media diff --git a/media/webm/webm_parser.h b/media/webm/webm_parser.h index 0716b80..92dd679 100644 --- a/media/webm/webm_parser.h +++ b/media/webm/webm_parser.h @@ -38,6 +38,10 @@ class WebMParserClient { // 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); diff --git a/media/webm/webm_tracks_parser.h b/media/webm/webm_tracks_parser.h index a1d0081..02dc10d 100644 --- a/media/webm/webm_tracks_parser.h +++ b/media/webm/webm_tracks_parser.h @@ -19,8 +19,9 @@ class WebMTracksParser : public WebMParserClient { // Parses a WebM Tracks element in |buf|. // - // Returns the number of bytes parsed on success. Returns -1 - // on error. + // Returns -1 if the parse fails. + // Returns 0 if more data is needed. + // Returns the number of bytes parsed on success. int Parse(const uint8* buf, int size); int64 audio_track_num() const { return audio_track_num_; } |