summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorannacc@chromium.org <annacc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-29 22:49:31 +0000
committerannacc@chromium.org <annacc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-29 22:49:31 +0000
commita4dcf06e3e0d6fd5be7376d191e6a5291fe4a416 (patch)
treeabe2ef62e756a4065c57d8f90111a71f25cf94c2 /media
parent7c5b9b43fac48a72a8481658a3adbcbd1d2b87f6 (diff)
downloadchromium_src-a4dcf06e3e0d6fd5be7376d191e6a5291fe4a416.zip
chromium_src-a4dcf06e3e0d6fd5be7376d191e6a5291fe4a416.tar.gz
chromium_src-a4dcf06e3e0d6fd5be7376d191e6a5291fe4a416.tar.bz2
This patch integrates SourceBufferStreams into SourceBuffer, removes all buffering functionality from ChunkDemuxerStream so that it now refers to SourceBufferStream (through ChunkDemuxer/SourceBuffer) for buffer data.
A few notes about this patch: * We are still assuming only 1 source ID (and 1 SourceBuffer) * SourceBuffer still has only 1 audio and 1 video streams * ChunkDemuxerStream now has a handle to its ChunkDemuxer so that it can ask for data from ChunkDemuxer's SourceBuffer. BUG=125208 TEST=source_buffer_unittest.cc (coming soon) Review URL: https://chromiumcodereview.appspot.com/10388099 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@139403 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/filters/chunk_demuxer.cc346
-rw-r--r--media/filters/chunk_demuxer.h39
-rw-r--r--media/filters/chunk_demuxer_unittest.cc72
-rw-r--r--media/filters/pipeline_integration_test.cc2
-rw-r--r--media/filters/source_buffer.cc100
-rw-r--r--media/filters/source_buffer.h57
-rw-r--r--media/filters/source_buffer_stream.cc53
-rw-r--r--media/filters/source_buffer_stream.h33
-rw-r--r--media/webm/webm_cluster_parser.cc3
-rw-r--r--media/webm/webm_cluster_parser_unittest.cc10
-rw-r--r--media/webm/webm_constants.h2
11 files changed, 407 insertions, 310 deletions
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 6af153a..1ceba74 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -121,25 +121,16 @@ class ChunkDemuxerStream : public DemuxerStream {
typedef std::deque<ReadCB> ReadCBQueue;
typedef std::deque<base::Closure> ClosureQueue;
- explicit ChunkDemuxerStream(const AudioDecoderConfig& audio_config);
- explicit ChunkDemuxerStream(const VideoDecoderConfig& video_config);
+ ChunkDemuxerStream(Type type, ChunkDemuxer* chunk_demuxer);
- void Flush();
+ void StartWaitingForSeek();
void Seek(base::TimeDelta time);
- // Checks if it is ok to add the |buffers| to the stream.
- bool CanAddBuffers(const BufferQueue& buffers) const;
+ // Notifies this stream of new buffers.
+ void OnBuffersAvailable();
- void AddBuffers(const BufferQueue& buffers);
void Shutdown();
- // Gets the time range buffered by this object.
- // Returns true if there is buffered data. |start_out| & |end_out| are set to
- // the start and end time of the buffered data respectively.
- // Returns false if no data is buffered.
- bool GetBufferedRange(base::TimeDelta* start_out,
- base::TimeDelta* end_out) const;
-
// DemuxerStream methods.
virtual void Read(const ReadCB& read_cb) OVERRIDE;
virtual Type type() OVERRIDE;
@@ -154,9 +145,6 @@ class ChunkDemuxerStream : public DemuxerStream {
enum State {
RETURNING_DATA_FOR_READS,
WAITING_FOR_SEEK,
- RECEIVED_EOS_WHILE_WAITING_FOR_SEEK, // EOS = End of stream.
- RECEIVED_EOS,
- RETURNING_EOS_FOR_READS,
SHUTDOWN,
};
@@ -171,46 +159,31 @@ class ChunkDemuxerStream : public DemuxerStream {
// |buffers_| and pops the callbacks & buffers from the respecive queues.
void CreateReadDoneClosures_Locked(ClosureQueue* closures);
+ // Specifies the type of the stream (must be AUDIO or VIDEO for now).
Type type_;
- AudioDecoderConfig audio_config_;
- VideoDecoderConfig video_config_;
+
+ // Pointer to the ChunkDemuxer that owns this ChunkDemuxerStream. Used for
+ // read requests to SourceBuffer.
+ ChunkDemuxer* chunk_demuxer_;
mutable base::Lock lock_;
State state_;
ReadCBQueue read_cbs_;
- BufferQueue buffers_;
-
- // Keeps track of the timestamp of the last buffer we have
- // added to |buffers_|. This is used to enforce buffers with strictly
- // monotonically increasing timestamps.
- base::TimeDelta last_buffer_timestamp_;
DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream);
};
-ChunkDemuxerStream::ChunkDemuxerStream(const AudioDecoderConfig& audio_config)
- : type_(AUDIO),
- state_(RETURNING_DATA_FOR_READS),
- last_buffer_timestamp_(kNoTimestamp()) {
- audio_config_.CopyFrom(audio_config);
-}
-
-
-ChunkDemuxerStream::ChunkDemuxerStream(const VideoDecoderConfig& video_config)
- : type_(VIDEO),
- state_(RETURNING_DATA_FOR_READS),
- last_buffer_timestamp_(kNoTimestamp()) {
- video_config_.CopyFrom(video_config);
-}
+ChunkDemuxerStream::ChunkDemuxerStream(Type type, ChunkDemuxer* chunk_demuxer)
+ : type_(type),
+ chunk_demuxer_(chunk_demuxer),
+ state_(RETURNING_DATA_FOR_READS) {}
-void ChunkDemuxerStream::Flush() {
- DVLOG(1) << "Flush()";
+void ChunkDemuxerStream::StartWaitingForSeek() {
+ DVLOG(1) << "StartWaitingForSeek()";
ReadCBQueue read_cbs;
{
base::AutoLock auto_lock(lock_);
- buffers_.clear();
ChangeState_Locked(WAITING_FOR_SEEK);
- last_buffer_timestamp_ = kNoTimestamp();
std::swap(read_cbs_, read_cbs);
}
@@ -224,64 +197,15 @@ void ChunkDemuxerStream::Seek(base::TimeDelta time) {
DCHECK(read_cbs_.empty());
- if (state_ == WAITING_FOR_SEEK) {
+ if (state_ == WAITING_FOR_SEEK)
ChangeState_Locked(RETURNING_DATA_FOR_READS);
- return;
- }
-
- if (state_ == RECEIVED_EOS_WHILE_WAITING_FOR_SEEK) {
- ChangeState_Locked(RECEIVED_EOS);
- return;
- }
}
-bool ChunkDemuxerStream::CanAddBuffers(const BufferQueue& buffers) const {
- base::AutoLock auto_lock(lock_);
-
- // If we haven't seen any buffers yet, then anything can be added.
- if (last_buffer_timestamp_ == kNoTimestamp())
- return true;
-
- if (buffers.empty())
- return true;
-
- return (buffers.front()->GetTimestamp() > last_buffer_timestamp_);
-}
-
-void ChunkDemuxerStream::AddBuffers(const BufferQueue& buffers) {
- if (buffers.empty())
- return;
-
+void ChunkDemuxerStream::OnBuffersAvailable() {
ClosureQueue closures;
{
base::AutoLock auto_lock(lock_);
-
- for (BufferQueue::const_iterator itr = buffers.begin();
- itr != buffers.end(); itr++) {
- // Make sure we aren't trying to add a buffer after we have received and
- // "end of stream" buffer.
- DCHECK_NE(state_, RECEIVED_EOS_WHILE_WAITING_FOR_SEEK);
- DCHECK_NE(state_, RECEIVED_EOS);
- DCHECK_NE(state_, RETURNING_EOS_FOR_READS);
-
- if ((*itr)->IsEndOfStream()) {
- if (state_ == WAITING_FOR_SEEK) {
- ChangeState_Locked(RECEIVED_EOS_WHILE_WAITING_FOR_SEEK);
- } else {
- ChangeState_Locked(RECEIVED_EOS);
- }
- } else {
- base::TimeDelta current_ts = (*itr)->GetTimestamp();
- if (last_buffer_timestamp_ != kNoTimestamp()) {
- DCHECK_GT(current_ts.ToInternalValue(),
- last_buffer_timestamp_.ToInternalValue());
- }
-
- last_buffer_timestamp_ = current_ts;
- buffers_.push_back(*itr);
- }
- }
-
+ DCHECK_NE(state_, SHUTDOWN);
CreateReadDoneClosures_Locked(&closures);
}
@@ -296,7 +220,6 @@ void ChunkDemuxerStream::Shutdown() {
ChangeState_Locked(SHUTDOWN);
std::swap(read_cbs_, read_cbs);
- buffers_.clear();
}
// Pass end of stream buffers to all callbacks to signal that no more data
@@ -305,23 +228,6 @@ void ChunkDemuxerStream::Shutdown() {
it->Run(StreamParserBuffer::CreateEOSBuffer());
}
-bool ChunkDemuxerStream::GetBufferedRange(
- base::TimeDelta* start_out, base::TimeDelta* end_out) const {
- base::AutoLock auto_lock(lock_);
-
- if (buffers_.empty())
- return false;
-
- *start_out = buffers_.front()->GetTimestamp();
- *end_out = buffers_.back()->GetTimestamp();
-
- base::TimeDelta end_duration = buffers_.back()->GetDuration();
- if (end_duration != kNoTimestamp())
- *end_out += end_duration;
-
- return true;
-}
-
// Helper function that makes sure |read_cb| runs on |message_loop|.
static void RunOnMessageLoop(const DemuxerStream::ReadCB& read_cb,
MessageLoop* message_loop,
@@ -337,46 +243,50 @@ static void RunOnMessageLoop(const DemuxerStream::ReadCB& read_cb,
// DemuxerStream methods.
void ChunkDemuxerStream::Read(const ReadCB& read_cb) {
- scoped_refptr<Buffer> buffer;
-
+ scoped_refptr<StreamParserBuffer> buffer;
{
base::AutoLock auto_lock(lock_);
switch (state_) {
case RETURNING_DATA_FOR_READS:
- // If we don't have any buffers ready or already have
- // pending reads, then defer this read.
- if (buffers_.empty() || !read_cbs_.empty()) {
+ // If we already have pending reads, then defer this read.
+ if (!read_cbs_.empty()) {
DeferRead_Locked(read_cb);
return;
}
- buffer = buffers_.front();
- buffers_.pop_front();
- break;
+ bool read_success;
+ {
+ // Unlock is necessary to avoid deadlock with OnBuffersAvailable().
+ base::AutoUnlock auto_unlock(lock_);
+ read_success = chunk_demuxer_->SourceBufferRead(type_, &buffer);
+ }
+
+ // Check whether the state may have changed during the unlock.
+ switch (state_) {
+ case RETURNING_DATA_FOR_READS:
+ if (!read_success) {
+ DeferRead_Locked(read_cb);
+ return;
+ }
+ break;
+ case WAITING_FOR_SEEK:
+ DCHECK(read_cbs_.empty());
+ buffer = NULL;
+ break;
+ case SHUTDOWN:
+ DCHECK(read_cbs_.empty());
+ buffer = StreamParserBuffer::CreateEOSBuffer();
+ }
+ break;
case WAITING_FOR_SEEK:
- case RECEIVED_EOS_WHILE_WAITING_FOR_SEEK:
// Null buffers should be returned in this state since we are waiting
- // for a seek. Any buffers in |buffers_| should NOT be returned because
- // they are associated with the seek.
- DCHECK(read_cbs_.empty());
- break;
- case RECEIVED_EOS:
+ // for a seek. Any buffers in the SourceBuffer should NOT be returned
+ // because they are associated with the seek.
DCHECK(read_cbs_.empty());
-
- if (buffers_.empty()) {
- ChangeState_Locked(RETURNING_EOS_FOR_READS);
- buffer = StreamParserBuffer::CreateEOSBuffer();
- } else {
- buffer = buffers_.front();
- buffers_.pop_front();
- }
break;
-
- case RETURNING_EOS_FOR_READS:
case SHUTDOWN:
- DCHECK(buffers_.empty());
DCHECK(read_cbs_.empty());
buffer = StreamParserBuffer::CreateEOSBuffer();
}
@@ -391,12 +301,12 @@ void ChunkDemuxerStream::EnableBitstreamConverter() {}
const AudioDecoderConfig& ChunkDemuxerStream::audio_decoder_config() {
CHECK_EQ(type_, AUDIO);
- return audio_config_;
+ return chunk_demuxer_->GetCurrentAudioDecoderConfig();
}
const VideoDecoderConfig& ChunkDemuxerStream::video_decoder_config() {
CHECK_EQ(type_, VIDEO);
- return video_config_;
+ return chunk_demuxer_->GetCurrentVideoDecoderConfig();
}
void ChunkDemuxerStream::ChangeState_Locked(State state) {
@@ -417,35 +327,23 @@ void ChunkDemuxerStream::DeferRead_Locked(const ReadCB& read_cb) {
void ChunkDemuxerStream::CreateReadDoneClosures_Locked(ClosureQueue* closures) {
lock_.AssertAcquired();
- if (state_ != RETURNING_DATA_FOR_READS && state_ != RECEIVED_EOS)
+ if (state_ != RETURNING_DATA_FOR_READS)
return;
- while (!buffers_.empty() && !read_cbs_.empty()) {
- closures->push_back(base::Bind(read_cbs_.front(), buffers_.front()));
- buffers_.pop_front();
- read_cbs_.pop_front();
- }
-
- if (state_ != RECEIVED_EOS || !buffers_.empty() || read_cbs_.empty())
- return;
-
- // Push enough EOS buffers to satisfy outstanding Read() requests.
- scoped_refptr<Buffer> end_of_stream_buffer =
- StreamParserBuffer::CreateEOSBuffer();
+ scoped_refptr<StreamParserBuffer> buffer;
while (!read_cbs_.empty()) {
- closures->push_back(base::Bind(read_cbs_.front(), end_of_stream_buffer));
+ if (!chunk_demuxer_->SourceBufferRead_Locked(type_, &buffer))
+ return;
+ closures->push_back(base::Bind(read_cbs_.front(), buffer));
read_cbs_.pop_front();
}
-
- ChangeState_Locked(RETURNING_EOS_FOR_READS);
}
ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client)
: state_(WAITING_FOR_INIT),
host_(NULL),
client_(client),
- buffered_bytes_(0),
- seek_waits_for_data_(true) {
+ buffered_bytes_(0) {
DCHECK(client);
}
@@ -484,7 +382,9 @@ void ChunkDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
if (video_)
video_->Seek(time);
- if (seek_waits_for_data_) {
+ source_buffer_->Seek(time);
+
+ if (source_buffer_->IsSeekPending()) {
DVLOG(1) << "Seek() : waiting for more data to arrive.";
seek_cb_ = cb;
return;
@@ -525,8 +425,8 @@ base::TimeDelta ChunkDemuxer::GetStartTime() const {
return base::TimeDelta();
}
-void ChunkDemuxer::FlushData() {
- DVLOG(1) << "FlushData()";
+void ChunkDemuxer::StartWaitingForSeek() {
+ DVLOG(1) << "StartWaitingForSeek()";
base::AutoLock auto_lock(lock_);
DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN);
@@ -534,14 +434,13 @@ void ChunkDemuxer::FlushData() {
return;
if (audio_.get())
- audio_->Flush();
+ audio_->StartWaitingForSeek();
if (video_.get())
- video_->Flush();
+ video_->StartWaitingForSeek();
- source_buffer_->Flush();
+ source_buffer_->ResetParser();
- seek_waits_for_data_ = true;
ChangeState_Locked(INITIALIZED);
}
@@ -563,8 +462,8 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
source_id_ = id;
- StreamParser::NewBuffersCB audio_cb;
- StreamParser::NewBuffersCB video_cb;
+ SourceBuffer::NewBuffersCB audio_cb;
+ SourceBuffer::NewBuffersCB video_cb;
if (has_audio) {
audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers,
@@ -605,33 +504,8 @@ bool ChunkDemuxer::GetBufferedRanges(const std::string& id,
DCHECK(ranges_out);
base::AutoLock auto_lock(lock_);
- base::TimeDelta start = kNoTimestamp();
- base::TimeDelta end;
- base::TimeDelta tmp_start;
- base::TimeDelta tmp_end;
-
- if (audio_ && audio_->GetBufferedRange(&tmp_start, &tmp_end)) {
- start = tmp_start;
- end = tmp_end;
- }
-
- if (video_ && video_->GetBufferedRange(&tmp_start, &tmp_end)) {
- if (start == kNoTimestamp()) {
- start = tmp_start;
- end = tmp_end;
- } else {
- start = std::min(start, tmp_start);
- end = std::max(end, tmp_end);
- }
- }
- if (start == kNoTimestamp())
- return false;
-
- ranges_out->resize(1);
- (*ranges_out)[0].first = start;
- (*ranges_out)[0].second = end;
- return true;
+ return source_buffer_->GetBufferedRanges(ranges_out);
}
bool ChunkDemuxer::AppendData(const std::string& id,
@@ -659,10 +533,8 @@ bool ChunkDemuxer::AppendData(const std::string& id,
{
base::AutoLock auto_lock(lock_);
- // Capture |seek_waits_for_data_| state before we start parsing.
- // Its state can be changed by OnAudioBuffers() or OnVideoBuffers()
- // calls during the parse.
- bool old_seek_waits_for_data = seek_waits_for_data_;
+ // Capture if the SourceBuffer has a pending seek before we start parsing.
+ bool old_seek_pending = source_buffer_->IsSeekPending();
switch (state_) {
case INITIALIZING:
@@ -688,9 +560,9 @@ bool ChunkDemuxer::AppendData(const std::string& id,
return false;
}
- // Check to see if parsing triggered seek_waits_for_data_ to go from true to
- // false. This indicates we have parsed enough data to complete the seek.
- if (old_seek_waits_for_data && !seek_waits_for_data_ &&
+ // Check to see if data was appended at the pending seek point. This
+ // indicates we have parsed enough data to complete the seek.
+ if (old_seek_pending && !source_buffer_->IsSeekPending() &&
!seek_cb_.is_null()) {
std::swap(cb, seek_cb_);
}
@@ -714,44 +586,54 @@ void ChunkDemuxer::Abort(const std::string& id) {
DCHECK(!id.empty());
DCHECK_EQ(source_id_, id);
- source_buffer_->Flush();
+ source_buffer_->ResetParser();
+}
+
+const AudioDecoderConfig& ChunkDemuxer::GetCurrentAudioDecoderConfig() {
+ base::AutoLock auto_lock(lock_);
+ return source_buffer_->GetCurrentAudioDecoderConfig();
+}
+
+const VideoDecoderConfig& ChunkDemuxer::GetCurrentVideoDecoderConfig() {
+ base::AutoLock auto_lock(lock_);
+ return source_buffer_->GetCurrentVideoDecoderConfig();
+}
+
+bool ChunkDemuxer::SourceBufferRead(DemuxerStream::Type type,
+ scoped_refptr<StreamParserBuffer>* out_buffer) {
+ base::AutoLock auto_lock(lock_);
+ return SourceBufferRead_Locked(type, out_buffer);
+}
+
+bool ChunkDemuxer::SourceBufferRead_Locked(DemuxerStream::Type type,
+ scoped_refptr<StreamParserBuffer>* out_buffer) {
+ lock_.AssertAcquired();
+ return source_buffer_->Read(type, out_buffer);
}
-void ChunkDemuxer::EndOfStream(PipelineStatus status) {
+bool ChunkDemuxer::EndOfStream(PipelineStatus status) {
DVLOG(1) << "EndOfStream(" << status << ")";
base::AutoLock auto_lock(lock_);
DCHECK_NE(state_, WAITING_FOR_INIT);
DCHECK_NE(state_, ENDED);
if (state_ == SHUTDOWN || state_ == PARSE_ERROR)
- return;
+ return true;
if (state_ == INITIALIZING) {
ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
- return;
+ return true;
}
+ if (!source_buffer_->EndOfStream() && status == PIPELINE_OK)
+ return false;
+
ChangeState_Locked(ENDED);
- if (status != PIPELINE_OK) {
+ if (status != PIPELINE_OK)
ReportError_Locked(status);
- return;
- }
-
- // Create an end of stream buffer.
- ChunkDemuxerStream::BufferQueue buffers;
- buffers.push_back(StreamParserBuffer::CreateEOSBuffer());
- if (audio_.get())
- audio_->AddBuffers(buffers);
-
- if (video_.get())
- video_->AddBuffers(buffers);
-}
-
-bool ChunkDemuxer::HasEnded() {
- base::AutoLock auto_lock(lock_);
- return (state_ == ENDED);
+ return true;
}
void ChunkDemuxer::Shutdown() {
@@ -851,7 +733,7 @@ bool ChunkDemuxer::OnNewConfigs(const AudioDecoderConfig& audio_config,
if (audio_.get())
return false;
- audio_ = new ChunkDemuxerStream(audio_config);
+ audio_ = new ChunkDemuxerStream(DemuxerStream::AUDIO, this);
}
// Only allow a single video config for now.
@@ -859,34 +741,30 @@ bool ChunkDemuxer::OnNewConfigs(const AudioDecoderConfig& audio_config,
if (video_.get())
return false;
- video_ = new ChunkDemuxerStream(video_config);
+ video_ = new ChunkDemuxerStream(DemuxerStream::VIDEO, this);
}
return true;
}
-bool ChunkDemuxer::OnAudioBuffers(const StreamParser::BufferQueue& buffers) {
- if (!audio_.get())
- return false;
+bool ChunkDemuxer::OnAudioBuffers() {
+ DCHECK_NE(state_, SHUTDOWN);
- if (!audio_->CanAddBuffers(buffers))
+ if (!audio_.get())
return false;
- audio_->AddBuffers(buffers);
- seek_waits_for_data_ = false;
+ audio_->OnBuffersAvailable();
return true;
}
-bool ChunkDemuxer::OnVideoBuffers(const StreamParser::BufferQueue& buffers) {
- if (!video_.get())
- return false;
+bool ChunkDemuxer::OnVideoBuffers() {
+ DCHECK_NE(state_, SHUTDOWN);
- if (!video_->CanAddBuffers(buffers))
+ if (!video_.get())
return false;
- video_->AddBuffers(buffers);
- seek_waits_for_data_ = false;
+ video_->OnBuffersAvailable();
return true;
}
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 9f570a8..d57ec7d 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -47,7 +47,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
virtual int GetBitrate() OVERRIDE;
// Methods used by an external object to control this demuxer.
- void FlushData();
+ void StartWaitingForSeek();
// Registers a new |id| to use for AppendData() calls. |type| indicates
// the MIME type for the data that we intend to append for this ID.
@@ -77,8 +77,30 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
// it can accept a new segment.
void Abort(const std::string& id);
- void EndOfStream(PipelineStatus status);
- bool HasEnded();
+ // Accesses the configs associated with the current playback buffers.
+ const AudioDecoderConfig& GetCurrentAudioDecoderConfig();
+ const VideoDecoderConfig& GetCurrentVideoDecoderConfig();
+
+ // Fills |out_buffer| with a new buffer from the current SourceBufferStream
+ // indicated by |type|.
+ // Returns true if |out_buffer| is filled with a valid buffer.
+ // Returns false if SourceBuffer can not fulfill the request or if |type| is
+ // not AUDIO or VIDEO.
+ // Two versions needed (Locked and not Locked) because this method is called
+ // during a Read() when ChunkDemuxer IS NOT locked and from
+ // ChunkDemuxerStream::CreateReadDoneClosures_Locked() when
+ // OnBuffersAvailable() is called and ChunkDemuxer IS locked.
+ // TODO(acolwell): Investigate a cleaner solution to SourceBufferRead locking
+ // requirements. crbug.com/129849
+ bool SourceBufferRead_Locked(DemuxerStream::Type type,
+ scoped_refptr<StreamParserBuffer>* out_buffer);
+ bool SourceBufferRead(DemuxerStream::Type type,
+ scoped_refptr<StreamParserBuffer>* out_buffer);
+
+ // Signals an EndOfStream request.
+ // Returns false if called in an unexpected state or if there is a gap between
+ // the current position and the end of the buffered data.
+ bool EndOfStream(PipelineStatus status);
void Shutdown();
protected:
@@ -100,13 +122,12 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
// data.
void ReportError_Locked(PipelineStatus error);
- void OnSourceBufferInitDone(bool success, base::TimeDelta duration);
-
// SourceBuffer callbacks.
+ void OnSourceBufferInitDone(bool success, base::TimeDelta duration);
bool OnNewConfigs(const AudioDecoderConfig& audio_config,
const VideoDecoderConfig& video_config);
- bool OnAudioBuffers(const StreamParser::BufferQueue& buffer);
- bool OnVideoBuffers(const StreamParser::BufferQueue& buffer);
+ bool OnAudioBuffers();
+ bool OnVideoBuffers();
bool OnKeyNeeded(scoped_array<uint8> init_data, int init_data_size);
mutable base::Lock lock_;
@@ -126,10 +147,6 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
scoped_ptr<SourceBuffer> source_buffer_;
- // Should a Seek() call wait for more data before calling the
- // callback.
- bool seek_waits_for_data_;
-
// TODO(acolwell): Remove this when fixing http://crbug.com/122909
std::string source_id_;
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 61ee9c2..ae59363 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -10,6 +10,7 @@
#include "media/filters/chunk_demuxer.h"
#include "media/filters/chunk_demuxer_client.h"
#include "media/webm/cluster_builder.h"
+#include "media/webm/webm_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::AnyNumber;
@@ -265,46 +266,54 @@ class ChunkDemuxerTest : public testing::Test {
}
scoped_ptr<Cluster> GenerateCluster(int timecode, int block_count) {
+ return GenerateCluster(timecode, timecode, block_count);
+ }
+
+ scoped_ptr<Cluster> GenerateCluster(int audio_timecode, int video_timecode,
+ int block_count) {
CHECK_GT(block_count, 0);
int size = 10;
scoped_array<uint8> data(new uint8[size]);
ClusterBuilder cb;
- cb.SetClusterTimecode(timecode);
- int audio_timecode = timecode;
- int video_timecode = timecode + 1;
+ cb.SetClusterTimecode(std::min(audio_timecode, video_timecode));
if (block_count == 1) {
- cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, 0,
- data.get(), size);
+ cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration,
+ kWebMFlagKeyframe, data.get(), size);
return cb.Finish();
}
// Create simple blocks for everything except the last 2 blocks.
+ // The first video frame must be a keyframe.
+ uint8 video_flag = kWebMFlagKeyframe;
for (int i = 0; i < block_count - 2; i++) {
if (audio_timecode <= video_timecode) {
- cb.AddSimpleBlock(kAudioTrackNum, audio_timecode, 0, data.get(), size);
+ cb.AddSimpleBlock(kAudioTrackNum, audio_timecode, kWebMFlagKeyframe,
+ data.get(), size);
audio_timecode += kAudioBlockDuration;
continue;
}
- cb.AddSimpleBlock(kVideoTrackNum, video_timecode, 0, data.get(), size);
+ cb.AddSimpleBlock(kVideoTrackNum, video_timecode, video_flag, data.get(),
+ size);
video_timecode += kVideoBlockDuration;
+ video_flag = 0;
}
// Make the last 2 blocks BlockGroups so that they don't get delayed by the
// block duration calculation logic.
if (audio_timecode <= video_timecode) {
- cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, 0,
- data.get(), size);
- cb.AddBlockGroup(kVideoTrackNum, video_timecode, kVideoBlockDuration, 0,
- data.get(), size);
+ cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration,
+ kWebMFlagKeyframe, data.get(), size);
+ cb.AddBlockGroup(kVideoTrackNum, video_timecode, kVideoBlockDuration,
+ video_flag, data.get(), size);
} else {
- cb.AddBlockGroup(kVideoTrackNum, video_timecode, kVideoBlockDuration, 0,
- data.get(), size);
- cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, 0,
- data.get(), size);
+ cb.AddBlockGroup(kVideoTrackNum, video_timecode, kVideoBlockDuration,
+ video_flag, data.get(), size);
+ cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration,
+ kWebMFlagKeyframe, data.get(), size);
}
return cb.Finish();
@@ -315,7 +324,7 @@ class ChunkDemuxerTest : public testing::Test {
DemuxerStream* video) {
CHECK_GT(block_count, 0);
int audio_timecode = timecode;
- int video_timecode = timecode + 1;
+ int video_timecode = timecode;
if (block_count == 1) {
ExpectRead(audio, audio_timecode);
@@ -523,14 +532,14 @@ TEST_F(ChunkDemuxerTest, TestSeekWhileParsingCluster) {
ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size() - 1));
ExpectRead(audio, 0);
- ExpectRead(video, 1);
+ ExpectRead(video, 0);
ExpectRead(audio, kAudioBlockDuration);
// Note: We skip trying to read a video buffer here because computing
// the duration for this block relies on successfully parsing the last block
// in the cluster the cluster.
ExpectRead(audio, 2 * kAudioBlockDuration);
- demuxer_->FlushData();
+ demuxer_->StartWaitingForSeek();
demuxer_->Seek(base::TimeDelta::FromSeconds(5),
NewExpectedStatusCB(PIPELINE_OK));
@@ -567,7 +576,7 @@ TEST_F(ChunkDemuxerTest, TestRead) {
&audio_read_done));
video->Read(base::Bind(&OnReadDone,
- base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(0),
&video_read_done));
scoped_ptr<Cluster> cluster(GenerateCluster(0, 4));
@@ -589,15 +598,13 @@ TEST_F(ChunkDemuxerTest, TestOutOfOrderClusters) {
// that overlaps.
scoped_ptr<Cluster> cluster_b(GenerateCluster(5, 4));
- // Make sure that AppendData() fails because this cluster data
- // is before previous data.
- EXPECT_CALL(host_, OnDemuxerError(PIPELINE_ERROR_DECODE));
+ // Make sure that AppendData() does not fail.
ASSERT_TRUE(AppendData(cluster_b->data(), cluster_b->size()));
- // Verify that AppendData() doesn't accept more data now.
+ // Verify that AppendData() can still accept more data.
scoped_ptr<Cluster> cluster_c(GenerateCluster(45, 2));
- EXPECT_FALSE(demuxer_->AppendData(kSourceId, cluster_c->data(),
- cluster_c->size()));
+ ASSERT_TRUE(demuxer_->AppendData(kSourceId, cluster_c->data(),
+ cluster_c->size()));
}
TEST_F(ChunkDemuxerTest, TestNonMonotonicButAboveClusterTimecode) {
@@ -803,7 +810,7 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamWithPendingReads) {
&audio_read_done_1));
video->Read(base::Bind(&OnReadDone,
- base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(0),
&video_read_done_1));
end_of_stream_helper_1.RequestReads();
@@ -845,7 +852,7 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) {
&audio_read_done_1));
video->Read(base::Bind(&OnReadDone,
- base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(0),
&video_read_done_1));
end_of_stream_helper_1.RequestReads();
@@ -858,7 +865,7 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) {
EXPECT_TRUE(video_read_done_1);
end_of_stream_helper_1.CheckIfReadDonesWereCalled(false);
- demuxer_->EndOfStream(PIPELINE_OK);
+ EXPECT_TRUE(demuxer_->EndOfStream(PIPELINE_OK));
end_of_stream_helper_1.CheckIfReadDonesWereCalled(true);
@@ -885,7 +892,7 @@ TEST_F(ChunkDemuxerTest, TestAppendingInPieces) {
CreateInfoTracks(true, true, false, &info_tracks, &info_tracks_size);
scoped_ptr<Cluster> cluster_a(GenerateCluster(0, 4));
- scoped_ptr<Cluster> cluster_b(GenerateCluster(68, 4));
+ scoped_ptr<Cluster> cluster_b(GenerateCluster(46, 66, 5));
size_t buffer_size = info_tracks_size + cluster_a->size() + cluster_b->size();
scoped_array<uint8> buffer(new uint8[buffer_size]);
@@ -909,8 +916,7 @@ TEST_F(ChunkDemuxerTest, TestAppendingInPieces) {
ASSERT_TRUE(audio);
ASSERT_TRUE(video);
- GenerateExpectedReads(0, 4, audio, video);
- GenerateExpectedReads(68, 4, audio, video);
+ GenerateExpectedReads(0, 9, audio, video);
}
TEST_F(ChunkDemuxerTest, TestWebMFile_AudioAndVideo) {
@@ -986,7 +992,7 @@ TEST_F(ChunkDemuxerTest, TestIncrementalClusterParsing) {
&audio_read_done));
video->Read(base::Bind(&OnReadDone,
- base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(0),
&video_read_done));
// Make sure the reads haven't completed yet.
@@ -1019,7 +1025,7 @@ TEST_F(ChunkDemuxerTest, TestIncrementalClusterParsing) {
&audio_read_done));
video->Read(base::Bind(&OnReadDone,
- base::TimeDelta::FromMilliseconds(34),
+ base::TimeDelta::FromMilliseconds(33),
&video_read_done));
// Make sure the reads haven't completed yet.
diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc
index a9347ef..2ca8fd7 100644
--- a/media/filters/pipeline_integration_test.cc
+++ b/media/filters/pipeline_integration_test.cc
@@ -42,7 +42,7 @@ class MockMediaSource : public ChunkDemuxerClient {
const std::string& url() const { return url_; }
void Seek(int new_position, int seek_append_size) {
- chunk_demuxer_->FlushData();
+ chunk_demuxer_->StartWaitingForSeek();
DCHECK_GE(new_position, 0);
DCHECK_LT(new_position, file_data_size_);
diff --git a/media/filters/source_buffer.cc b/media/filters/source_buffer.cc
index 90f0434..84efa91 100644
--- a/media/filters/source_buffer.cc
+++ b/media/filters/source_buffer.cc
@@ -47,10 +47,75 @@ bool SourceBuffer::AppendData(const uint8* data, size_t length) {
return stream_parser_->Parse(data, length);
}
-void SourceBuffer::Flush() {
+bool SourceBuffer::Read(DemuxerStream::Type type,
+ scoped_refptr<StreamParserBuffer>* out_buffer) {
+ if (type == DemuxerStream::AUDIO) {
+ return audio_->GetNextBuffer(out_buffer);
+ } else if (type == DemuxerStream::VIDEO) {
+ return video_->GetNextBuffer(out_buffer);
+ }
+ return false;
+}
+
+void SourceBuffer::Seek(base::TimeDelta time) {
+ if (audio_.get())
+ audio_->Seek(time);
+
+ if (video_.get())
+ video_->Seek(time);
+}
+
+bool SourceBuffer::IsSeekPending() const {
+ bool seek_pending = false;
+
+ if (audio_.get())
+ seek_pending = audio_->IsSeekPending();
+
+ if (!seek_pending && video_.get())
+ seek_pending = video_->IsSeekPending();
+
+ return seek_pending;
+}
+
+void SourceBuffer::ResetParser() {
stream_parser_->Flush();
}
+bool SourceBuffer::GetBufferedRanges(Ranges* ranges_out) const {
+ // TODO(annacc): calculate buffered ranges. (crbug.com/129852)
+ return false;
+}
+
+bool SourceBuffer::EndOfStream() {
+
+ if ((audio_.get() && !audio_->CanEndOfStream()) ||
+ (video_.get() && !video_->CanEndOfStream()))
+ return false;
+
+ if (audio_.get())
+ audio_->EndOfStream();
+
+ if (video_.get())
+ video_->EndOfStream();
+
+ // Run callbacks so that any pending read requests can be fulfilled.
+ if (!audio_cb_.is_null())
+ audio_cb_.Run();
+
+ if (!video_cb_.is_null())
+ video_cb_.Run();
+
+ return true;
+}
+
+const AudioDecoderConfig& SourceBuffer::GetCurrentAudioDecoderConfig() {
+ return audio_->GetCurrentAudioDecoderConfig();
+}
+
+const VideoDecoderConfig& SourceBuffer::GetCurrentVideoDecoderConfig() {
+ return video_->GetCurrentVideoDecoderConfig();
+}
+
void SourceBuffer::OnStreamParserInitDone(bool success,
base::TimeDelta duration) {
init_cb_.Run(success, duration);
@@ -58,25 +123,46 @@ void SourceBuffer::OnStreamParserInitDone(bool success,
}
bool SourceBuffer::OnNewConfigs(const AudioDecoderConfig& audio_config,
- const VideoDecoderConfig& video_config) {
+ const VideoDecoderConfig& video_config) {
CHECK(audio_config.IsValidConfig() || video_config.IsValidConfig());
// Signal an error if we get configuration info for stream types
// we don't have a callback to handle.
- if ((audio_config.IsValidConfig() && audio_cb_.is_null()) ||
- (video_config.IsValidConfig() && video_cb_.is_null())) {
- return false;
+ if (audio_config.IsValidConfig()) {
+ // Disallow multiple audio configs and new audio configs after stream is
+ // initialized.
+ if (audio_cb_.is_null() || audio_.get())
+ return false;
+
+ audio_.reset(new SourceBufferStream(audio_config));
+ }
+
+ if (video_config.IsValidConfig()) {
+ // Disallow multiple video configs and new video configs after stream is
+ // initialized.
+ if (video_cb_.is_null() || video_.get())
+ return false;
+
+ video_.reset(new SourceBufferStream(video_config));
}
return config_cb_.Run(audio_config, video_config);
}
bool SourceBuffer::OnAudioBuffers(const StreamParser::BufferQueue& buffer) {
- return audio_cb_.Run(buffer);
+ if (!audio_.get())
+ return false;
+ audio_->Append(buffer);
+
+ return audio_cb_.Run();
}
bool SourceBuffer::OnVideoBuffers(const StreamParser::BufferQueue& buffer) {
- return video_cb_.Run(buffer);
+ if (!video_.get())
+ return false;
+ video_->Append(buffer);
+
+ return video_cb_.Run();
}
bool SourceBuffer::OnKeyNeeded(scoped_array<uint8> init_data,
diff --git a/media/filters/source_buffer.h b/media/filters/source_buffer.h
index a03c75e..78e02ec 100644
--- a/media/filters/source_buffer.h
+++ b/media/filters/source_buffer.h
@@ -5,24 +5,26 @@
#ifndef MEDIA_FILTERS_SOURCE_BUFFER_H_
#define MEDIA_FILTERS_SOURCE_BUFFER_H_
-#include "media/base/audio_decoder_config.h"
+#include "media/base/demuxer_stream.h"
#include "media/base/stream_parser.h"
-#include "media/base/video_decoder_config.h"
+#include "media/filters/source_buffer_stream.h"
namespace media {
-// SourceBuffer will store media segments and initialization segments that are
-// dynamically appended to a particular source ID, hand them to a StreamParser
-// to be parsed, and provide buffers to ChunkDemuxerStreams during playback.
+// SourceBuffer stores media segments and initialization segments that are
+// dynamically appended to a particular source ID, hands them to a StreamParser
+// to be parsed, and provides buffers to ChunkDemuxerStreams during playback.
class MEDIA_EXPORT SourceBuffer {
public:
+ typedef std::vector<std::pair<base::TimeDelta, base::TimeDelta> > Ranges;
+
SourceBuffer();
~SourceBuffer();
typedef base::Callback<void(bool, base::TimeDelta)> InitCB;
typedef base::Callback<bool(const AudioDecoderConfig&,
const VideoDecoderConfig&)> NewConfigCB;
- typedef base::Callback<bool(const StreamParser::BufferQueue&)> NewBuffersCB;
+ typedef base::Callback<bool()> NewBuffersCB;
typedef base::Callback<bool(scoped_array<uint8>, int)> KeyNeededCB;
void Init(scoped_ptr<StreamParser> parser,
@@ -32,14 +34,42 @@ class MEDIA_EXPORT SourceBuffer {
const NewBuffersCB& video_cb,
const KeyNeededCB& key_needed_cb);
- // Add buffers to this source. Incoming data will be parsed and then stored
- // in SourceBufferStreams, which will handle ordering and overlap resolution.
- // Returns true if data was successfully appended, false if the parse failed.
+ // Add buffers to this source. Incoming data is parsed and stored in
+ // SourceBufferStreams, which handle ordering and overlap resolution.
+ // Returns true if parsing was successful.
bool AppendData(const uint8* data, size_t length);
- // Clears the data from this buffer. Used during a seek to ensure the next
- // buffers provided during a read are the buffers appended after the seek.
- void Flush();
+ // Fills |out_buffer| with a new buffer from the SourceBufferStream indicated
+ // by |type|.
+ // Returns true if |out_buffer| is filled with a valid buffer, false if
+ // there is not enough data buffered to fulfill the request or if |type| is
+ // not a supported stream type.
+ bool Read(DemuxerStream::Type type,
+ scoped_refptr<StreamParserBuffer>* out_buffer);
+
+ // Seeks the SourceBufferStreams to a new |time|.
+ void Seek(base::TimeDelta time);
+
+ // Returns true if this SourceBuffer has seeked to a time without buffered
+ // data and is waiting for more data to be appended.
+ bool IsSeekPending() const;
+
+ // Resets StreamParser so it can accept a new segment.
+ void ResetParser();
+
+ // Fills |ranges_out| with the Ranges that are currently buffered.
+ // Returns false if no data is buffered.
+ bool GetBufferedRanges(Ranges* ranges_out) const;
+
+ // Notifies all the streams in this SourceBuffer that EndOfStream has been
+ // called so that they can return EOS buffers for reads requested after the
+ // last buffer in the stream.
+ // Returns false if called when there is a gap between the current position
+ // and the end of the buffered data.
+ bool EndOfStream();
+
+ const AudioDecoderConfig& GetCurrentAudioDecoderConfig();
+ const VideoDecoderConfig& GetCurrentVideoDecoderConfig();
private:
// StreamParser callbacks.
@@ -58,6 +88,9 @@ class MEDIA_EXPORT SourceBuffer {
NewBuffersCB video_cb_;
KeyNeededCB key_needed_cb_;
+ scoped_ptr<SourceBufferStream> audio_;
+ scoped_ptr<SourceBufferStream> video_;
+
DISALLOW_COPY_AND_ASSIGN(SourceBuffer);
};
diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc
index 52ce36c..e12af6b 100644
--- a/media/filters/source_buffer_stream.cc
+++ b/media/filters/source_buffer_stream.cc
@@ -40,6 +40,7 @@ class SourceBufferRange {
// filled with a valid buffer, false if there is not enough data to fulfill
// the request.
bool GetNextBuffer(scoped_refptr<StreamParserBuffer>* out_buffer);
+ bool HasNextBuffer() const;
base::TimeDelta GetNextTimestamp() const;
// Returns the Timespan of buffered time in this range.
@@ -156,10 +157,29 @@ static bool IsNextInSequence(
namespace media {
SourceBufferStream::SourceBufferStream()
- : seek_pending_(false),
- seek_buffer_timestamp_(kNoTimestamp()),
+ : seek_pending_(true),
+ seek_buffer_timestamp_(base::TimeDelta()),
selected_range_(NULL),
- waiting_for_keyframe_(false) {
+ waiting_for_keyframe_(false),
+ end_of_stream_(false) {
+}
+
+SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config)
+ : seek_pending_(true),
+ seek_buffer_timestamp_(base::TimeDelta()),
+ selected_range_(NULL),
+ waiting_for_keyframe_(false),
+ end_of_stream_(false) {
+ audio_config_.CopyFrom(audio_config);
+}
+
+SourceBufferStream::SourceBufferStream(const VideoDecoderConfig& video_config)
+ : seek_pending_(true),
+ seek_buffer_timestamp_(base::TimeDelta()),
+ selected_range_(NULL),
+ waiting_for_keyframe_(false),
+ end_of_stream_(false) {
+ video_config_.CopyFrom(video_config);
}
SourceBufferStream::~SourceBufferStream() {
@@ -325,6 +345,11 @@ void SourceBufferStream::Seek(base::TimeDelta timestamp) {
selected_range_ = *itr;
selected_range_->Seek(timestamp);
seek_pending_ = false;
+ end_of_stream_ = false;
+}
+
+bool SourceBufferStream::IsSeekPending() const {
+ return seek_pending_;
}
bool SourceBufferStream::GetNextBuffer(
@@ -334,6 +359,13 @@ bool SourceBufferStream::GetNextBuffer(
track_buffer_.pop_front();
return true;
}
+
+ if (end_of_stream_ && (!selected_range_ ||
+ !selected_range_->HasNextBuffer())) {
+ *out_buffer = StreamParserBuffer::CreateEOSBuffer();
+ return true;
+ }
+
return selected_range_ && selected_range_->GetNextBuffer(out_buffer);
}
@@ -347,6 +379,15 @@ SourceBufferStream::GetBufferedTime() const {
return timespans;
}
+void SourceBufferStream::EndOfStream() {
+ DCHECK(CanEndOfStream());
+ end_of_stream_ = true;
+}
+
+bool SourceBufferStream::CanEndOfStream() const {
+ return ranges_.empty() || selected_range_ == ranges_.back();
+}
+
SourceBufferRange::SourceBufferRange()
: next_buffer_index_(-1) {
}
@@ -419,7 +460,6 @@ base::TimeDelta SourceBufferRange::SeekAfter(base::TimeDelta timestamp) {
return result->first;
}
-
bool SourceBufferRange::GetNextBuffer(
scoped_refptr<StreamParserBuffer>* out_buffer) {
DCHECK_GE(next_buffer_index_, 0);
@@ -431,6 +471,11 @@ bool SourceBufferRange::GetNextBuffer(
return true;
}
+bool SourceBufferRange::HasNextBuffer() const {
+ return next_buffer_index_ >= 0 &&
+ next_buffer_index_ < static_cast<int>(buffers_.size());
+}
+
base::TimeDelta SourceBufferRange::GetNextTimestamp() const {
DCHECK_GE(next_buffer_index_, 0);
DCHECK(!buffers_.empty());
diff --git a/media/filters/source_buffer_stream.h b/media/filters/source_buffer_stream.h
index 8e80414..ed3dfc3 100644
--- a/media/filters/source_buffer_stream.h
+++ b/media/filters/source_buffer_stream.h
@@ -10,8 +10,10 @@
#include <utility>
#include "base/memory/ref_counted.h"
+#include "media/base/audio_decoder_config.h"
#include "media/base/media_export.h"
#include "media/base/stream_parser_buffer.h"
+#include "media/base/video_decoder_config.h"
namespace media {
@@ -28,6 +30,9 @@ class MEDIA_EXPORT SourceBufferStream {
typedef std::list<Timespan> TimespanList;
SourceBufferStream();
+ explicit SourceBufferStream(const AudioDecoderConfig& audio_config);
+ explicit SourceBufferStream(const VideoDecoderConfig& video_config);
+
~SourceBufferStream();
// Add the |buffers| to the SourceBufferStream. Buffers within the queue are
@@ -44,6 +49,10 @@ class MEDIA_EXPORT SourceBufferStream {
// buffers starting from the closest keyframe before |timestamp|.
void Seek(base::TimeDelta timestamp);
+ // Returns true if the SourceBufferStream has seeked to a time without
+ // buffered data and is waiting for more data to be appended.
+ bool IsSeekPending() const;
+
// Fills |out_buffer| with a new buffer. Seek() must be called before calling
// this method. Buffers are presented in order from the last call to Seek().
// |out_buffer|'s timestamp may be earlier than the |timestamp| passed to
@@ -55,6 +64,23 @@ class MEDIA_EXPORT SourceBufferStream {
// Returns a list of the buffered time ranges.
TimespanList GetBufferedTime() const;
+ // Notifies this SourceBufferStream that EndOfStream has been called and that
+ // GetNextBuffer() should return EOS buffers after all other buffered data.
+ // Returns false if called when there is a gap between the current position
+ // and the end of the buffered data.
+ void EndOfStream();
+
+ // Returns true if this SourceBufferStream can successfully call EndOfStream()
+ // (if there are no gaps between the current position and the remaining data).
+ bool CanEndOfStream() const;
+
+ const AudioDecoderConfig& GetCurrentAudioDecoderConfig() {
+ return audio_config_;
+ }
+ const VideoDecoderConfig& GetCurrentVideoDecoderConfig() {
+ return video_config_;
+ }
+
private:
typedef std::list<SourceBufferRange*> RangeList;
@@ -76,6 +102,9 @@ class MEDIA_EXPORT SourceBufferStream {
// List of disjoint buffered ranges, ordered by start time.
RangeList ranges_;
+ AudioDecoderConfig audio_config_;
+ VideoDecoderConfig video_config_;
+
// True if more data needs to be appended before the Seek() can complete,
// false if no Seek() has been requested or the Seek() is completed.
bool seek_pending_;
@@ -97,6 +126,10 @@ class MEDIA_EXPORT SourceBufferStream {
// |track_buffer_| to be appended.
bool waiting_for_keyframe_;
+ // True when EndOfStream() has been called and GetNextBuffer() should return
+ // EOS buffers for read requests beyond the buffered data. False initially.
+ bool end_of_stream_;
+
DISALLOW_COPY_AND_ASSIGN(SourceBufferStream);
};
diff --git a/media/webm/webm_cluster_parser.cc b/media/webm/webm_cluster_parser.cc
index 8271899..67ae9a7 100644
--- a/media/webm/webm_cluster_parser.cc
+++ b/media/webm/webm_cluster_parser.cc
@@ -234,9 +234,6 @@ bool WebMClusterParser::Track::AddBuffer(
return false;
}
- if (buffer->GetDuration() == kNoTimestamp())
- buffer->SetDuration(default_duration_);
-
if (delayed_buffer_) {
// Update the duration of the delayed buffer and place it into the queue.
base::TimeDelta new_duration =
diff --git a/media/webm/webm_cluster_parser_unittest.cc b/media/webm/webm_cluster_parser_unittest.cc
index cbf3150..96586d1 100644
--- a/media/webm/webm_cluster_parser_unittest.cc
+++ b/media/webm/webm_cluster_parser_unittest.cc
@@ -40,11 +40,11 @@ struct BlockInfo {
const BlockInfo kDefaultBlockInfo[] = {
{ kAudioTrackNum, 0, 23, true },
{ kAudioTrackNum, 23, 23, true },
- { kVideoTrackNum, kVideoDefaultDurationInMs, kVideoDefaultDurationInMs,
- true },
- { kAudioTrackNum, 46, 23, false },
- { kVideoTrackNum, 2 * kVideoDefaultDurationInMs, kVideoDefaultDurationInMs,
- true },
+ { kVideoTrackNum, 33, 34, true },
+ { kAudioTrackNum, 46, 23, true },
+ { kVideoTrackNum, 67, 33, false },
+ { kAudioTrackNum, 69, 23, false },
+ { kVideoTrackNum, 100, 33, false },
};
static scoped_ptr<Cluster> CreateCluster(int timecode,
diff --git a/media/webm/webm_constants.h b/media/webm/webm_constants.h
index f6afef4..b6ba0a2 100644
--- a/media/webm/webm_constants.h
+++ b/media/webm/webm_constants.h
@@ -195,6 +195,8 @@ const int kWebMIdWritingApp = 0x5741;
const int64 kWebMReservedId = 0x1FFFFFFF;
const int64 kWebMUnknownSize = GG_LONGLONG(0x00FFFFFFFFFFFFFF);
+const uint8 kWebMFlagKeyframe = 0x80;
+
} // namespace media
#endif // MEDIA_WEBM_WEBM_CONSTANTS_H_