summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-09 04:13:07 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-09 04:13:07 +0000
commit9730c0548c8c68bce5565bc6faeee8a1840adf10 (patch)
tree51b4decb87d6ba59b92ad516eb556d8a78cabe87 /media
parent7a68a440417aa3ac19836423642482b0bf5f42f7 (diff)
downloadchromium_src-9730c0548c8c68bce5565bc6faeee8a1840adf10.zip
chromium_src-9730c0548c8c68bce5565bc6faeee8a1840adf10.tar.gz
chromium_src-9730c0548c8c68bce5565bc6faeee8a1840adf10.tar.bz2
Fix various operations in ChunkDemuxer that were not being applied to text tracks.
- Fixed range removal. - Fixed shutdown on error. - Fixed memory limit setting for testing. - Cleaned up waiting for seek logic and documented behavior. BUG=230708 Review URL: https://codereview.chromium.org/110693007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@243756 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/filters/chunk_demuxer.cc145
-rw-r--r--media/filters/chunk_demuxer.h6
-rw-r--r--media/filters/chunk_demuxer_unittest.cc277
3 files changed, 320 insertions, 108 deletions
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index f5102b9..b38274a 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -114,6 +114,10 @@ class SourceState {
// Aborts the current append sequence and resets the parser.
void Abort();
+ // Calls Remove(|start|, |end|, |duration|) on all
+ // ChunkDemuxerStreams managed by this object.
+ void Remove(TimeDelta start, TimeDelta end, TimeDelta duration);
+
// Sets |timestamp_offset_| if possible.
// Returns if the offset was set. Returns false if the offset could not be
// updated at this time.
@@ -145,6 +149,11 @@ class SourceState {
void OnSetDuration(TimeDelta duration);
void MarkEndOfStream();
void UnmarkEndOfStream();
+ void Shutdown();
+ // Sets the memory limit on each stream. |memory_limit| is the
+ // maximum number of bytes each stream is allowed to hold in its buffer.
+ void SetMemoryLimitsForTesting(int memory_limit);
+ bool IsSeekWaitingForData() const;
private:
// Called by the |stream_parser_| when a new initialization segment is
@@ -305,6 +314,7 @@ class ChunkDemuxerStream : public DemuxerStream {
// if type() != TEXT.
TextTrackConfig text_track_config();
+ // Sets the memory limit, in bytes, on the SourceBufferStream.
void set_memory_limit_for_testing(int memory_limit) {
stream_->set_memory_limit_for_testing(memory_limit);
}
@@ -360,17 +370,9 @@ SourceState::SourceState(scoped_ptr<StreamParser> stream_parser,
}
SourceState::~SourceState() {
- if (audio_)
- audio_->Shutdown();
-
- if (video_)
- video_->Shutdown();
+ Shutdown();
- for (TextStreamMap::iterator itr = text_stream_map_.begin();
- itr != text_stream_map_.end(); ++itr) {
- itr->second->Shutdown();
- delete itr->second;
- }
+ STLDeleteValues(&text_stream_map_);
}
void SourceState::Init(const StreamParser::InitCB& init_cb,
@@ -422,6 +424,19 @@ void SourceState::Abort() {
can_update_offset_ = true;
}
+void SourceState::Remove(TimeDelta start, TimeDelta end, TimeDelta duration) {
+ if (audio_)
+ audio_->Remove(start, end, duration);
+
+ if (video_)
+ video_->Remove(start, end, duration);
+
+ for (TextStreamMap::iterator itr = text_stream_map_.begin();
+ itr != text_stream_map_.end(); ++itr) {
+ itr->second->Remove(start, end, duration);
+ }
+}
+
Ranges<TimeDelta> SourceState::GetBufferedRanges(TimeDelta duration,
bool ended) const {
// TODO(acolwell): When we start allowing disabled tracks we'll need to update
@@ -450,7 +465,6 @@ TimeDelta SourceState::GetMaxBufferedDuration() const {
if (video_)
max_duration = std::max(max_duration, video_->GetBufferedDuration());
-
for (TextStreamMap::const_iterator itr = text_stream_map_.begin();
itr != text_stream_map_.end(); ++itr) {
max_duration = std::max(max_duration, itr->second->GetBufferedDuration());
@@ -550,6 +564,49 @@ void SourceState::UnmarkEndOfStream() {
}
}
+void SourceState::Shutdown() {
+ if (audio_)
+ audio_->Shutdown();
+
+ if (video_)
+ video_->Shutdown();
+
+ for (TextStreamMap::iterator itr = text_stream_map_.begin();
+ itr != text_stream_map_.end(); ++itr) {
+ itr->second->Shutdown();
+ }
+}
+
+void SourceState::SetMemoryLimitsForTesting(int memory_limit) {
+ if (audio_)
+ audio_->set_memory_limit_for_testing(memory_limit);
+
+ if (video_)
+ video_->set_memory_limit_for_testing(memory_limit);
+
+ for (TextStreamMap::iterator itr = text_stream_map_.begin();
+ itr != text_stream_map_.end(); ++itr) {
+ itr->second->set_memory_limit_for_testing(memory_limit);
+ }
+}
+
+bool SourceState::IsSeekWaitingForData() const {
+ if (audio_ && audio_->IsSeekWaitingForData())
+ return true;
+
+ if (video_ && video_->IsSeekWaitingForData())
+ return true;
+
+ // NOTE: We are intentionally not checking the text tracks
+ // because text tracks are discontinuous and may not have data
+ // for the seek position. This is ok and playback should not be
+ // stalled because we don't have cues. If cues, with timestamps after
+ // the seek time, eventually arrive they will be delivered properly
+ // in response to ChunkDemuxerStream::Read() calls.
+
+ return false;
+}
+
void SourceState::AdjustBufferTimestamps(
const StreamParser::BufferQueue& buffers) {
if (timestamp_offset_ == TimeDelta())
@@ -884,6 +941,11 @@ void ChunkDemuxerStream::Shutdown() {
bool ChunkDemuxerStream::IsSeekWaitingForData() const {
base::AutoLock auto_lock(lock_);
+
+ // This method should not be called for text tracks. See the note in
+ // SourceState::IsSeekWaitingForData().
+ DCHECK_NE(type_, DemuxerStream::TEXT);
+
return stream_->IsSeekPending();
}
@@ -1382,17 +1444,15 @@ void ChunkDemuxer::Abort(const std::string& id) {
source_state_map_[id]->Abort();
}
-void ChunkDemuxer::Remove(const std::string& id, base::TimeDelta start,
- base::TimeDelta end) {
+void ChunkDemuxer::Remove(const std::string& id, TimeDelta start,
+ TimeDelta end) {
DVLOG(1) << "Remove(" << id << ", " << start.InSecondsF()
<< ", " << end.InSecondsF() << ")";
base::AutoLock auto_lock(lock_);
- if (id == source_id_audio_ && audio_)
- audio_->Remove(start, end, duration_);
-
- if (id == source_id_video_ && video_)
- video_->Remove(start, end, duration_);
+ DCHECK(!id.empty());
+ CHECK(IsValidId(id));
+ source_state_map_[id]->Remove(start, end, duration_);
}
double ChunkDemuxer::GetDuration() {
@@ -1537,11 +1597,7 @@ void ChunkDemuxer::Shutdown() {
if (state_ == SHUTDOWN)
return;
- if (audio_)
- audio_->Shutdown();
-
- if (video_)
- video_->Shutdown();
+ ShutdownAllStreams();
ChangeState_Locked(SHUTDOWN);
@@ -1550,11 +1606,10 @@ void ChunkDemuxer::Shutdown() {
}
void ChunkDemuxer::SetMemoryLimitsForTesting(int memory_limit) {
- if (audio_)
- audio_->set_memory_limit_for_testing(memory_limit);
-
- if (video_)
- video_->set_memory_limit_for_testing(memory_limit);
+ for (SourceStateMap::iterator itr = source_state_map_.begin();
+ itr != source_state_map_.end(); ++itr) {
+ itr->second->SetMemoryLimitsForTesting(memory_limit);
+ }
}
void ChunkDemuxer::ChangeState_Locked(State new_state) {
@@ -1566,11 +1621,8 @@ void ChunkDemuxer::ChangeState_Locked(State new_state) {
ChunkDemuxer::~ChunkDemuxer() {
DCHECK_NE(state_, INITIALIZED);
- for (SourceStateMap::iterator it = source_state_map_.begin();
- it != source_state_map_.end(); ++it) {
- delete it->second;
- }
- source_state_map_.clear();
+
+ STLDeleteValues(&source_state_map_);
}
void ChunkDemuxer::ReportError_Locked(PipelineStatus error) {
@@ -1588,11 +1640,7 @@ void ChunkDemuxer::ReportError_Locked(PipelineStatus error) {
if (!seek_cb_.is_null())
std::swap(cb, seek_cb_);
- if (audio_)
- audio_->Shutdown();
-
- if (video_)
- video_->Shutdown();
+ ShutdownAllStreams();
}
if (!cb.is_null()) {
@@ -1606,15 +1654,13 @@ void ChunkDemuxer::ReportError_Locked(PipelineStatus error) {
bool ChunkDemuxer::IsSeekWaitingForData_Locked() const {
lock_.AssertAcquired();
- bool waiting_for_data = false;
-
- if (audio_)
- waiting_for_data = audio_->IsSeekWaitingForData();
-
- if (!waiting_for_data && video_)
- waiting_for_data = video_->IsSeekWaitingForData();
+ for (SourceStateMap::const_iterator itr = source_state_map_.begin();
+ itr != source_state_map_.end(); ++itr) {
+ if (itr->second->IsSeekWaitingForData())
+ return true;
+ }
- return waiting_for_data;
+ return false;
}
void ChunkDemuxer::OnSourceInitDone(bool success, TimeDelta duration) {
@@ -1773,4 +1819,11 @@ void ChunkDemuxer::CompletePendingReadsIfPossible() {
}
}
+void ChunkDemuxer::ShutdownAllStreams() {
+ for (SourceStateMap::iterator itr = source_state_map_.begin();
+ itr != source_state_map_.end(); ++itr) {
+ itr->second->Shutdown();
+ }
+}
+
} // namespace media
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index d050632..5f37fc2 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -137,6 +137,8 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
void Shutdown();
+ // Sets the memory limit on each stream. |memory_limit| is the
+ // maximum number of bytes each stream is allowed to hold in its buffer.
void SetMemoryLimitsForTesting(int memory_limit);
// Returns the ranges representing the buffered data in the demuxer.
@@ -218,6 +220,10 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
// Seeks all SourceBufferStreams to |seek_time|.
void SeekAllSources(base::TimeDelta seek_time);
+ // Shuts down all DemuxerStreams by calling Shutdown() on
+ // all objects in |source_state_map_|.
+ void ShutdownAllStreams();
+
mutable base::Lock lock_;
State state_;
bool cancel_next_seek_;
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index be8a2a2..2a88a3e 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -171,10 +171,13 @@ class ChunkDemuxerTest : public testing::Test {
ShutdownDemuxer();
}
- void CreateInitSegment(bool has_audio, bool has_video, bool has_text,
+ void CreateInitSegment(int stream_flags,
bool is_audio_encrypted, bool is_video_encrypted,
scoped_ptr<uint8[]>* buffer,
int* size) {
+ bool has_audio = (stream_flags & HAS_AUDIO) != 0;
+ bool has_video = (stream_flags & HAS_VIDEO) != 0;
+ bool has_text = (stream_flags & HAS_TEXT) != 0;
scoped_refptr<DecoderBuffer> ebml_header;
scoped_refptr<DecoderBuffer> info;
scoped_refptr<DecoderBuffer> audio_track_entry;
@@ -286,11 +289,12 @@ class ChunkDemuxerTest : public testing::Test {
}
ChunkDemuxer::Status AddId() {
- return AddId(kSourceId, true, true);
+ return AddId(kSourceId, HAS_AUDIO | HAS_VIDEO);
}
- ChunkDemuxer::Status AddId(const std::string& source_id,
- bool has_audio, bool has_video) {
+ ChunkDemuxer::Status AddId(const std::string& source_id, int stream_flags) {
+ bool has_audio = (stream_flags & HAS_AUDIO) != 0;
+ bool has_video = (stream_flags & HAS_VIDEO) != 0;
std::vector<std::string> codecs;
std::string type;
@@ -305,7 +309,7 @@ class ChunkDemuxerTest : public testing::Test {
}
if (!has_audio && !has_video) {
- return AddId(kSourceId, true, true);
+ return AddId(kSourceId, HAS_AUDIO | HAS_VIDEO);
}
return demuxer_->AddId(source_id, type, codecs);
@@ -408,25 +412,22 @@ class ChunkDemuxerTest : public testing::Test {
}
}
- void AppendInitSegment(bool has_audio, bool has_video) {
- AppendInitSegmentWithSourceId(kSourceId, has_audio, has_video, false);
+ void AppendInitSegment(int stream_flags) {
+ AppendInitSegmentWithSourceId(kSourceId, stream_flags);
}
void AppendInitSegmentWithSourceId(const std::string& source_id,
- bool has_audio, bool has_video,
- bool has_text) {
- AppendInitSegmentWithEncryptedInfo(
- source_id, has_audio, has_video, has_text, false, false);
+ int stream_flags) {
+ AppendInitSegmentWithEncryptedInfo(source_id, stream_flags, false, false);
}
void AppendInitSegmentWithEncryptedInfo(const std::string& source_id,
- bool has_audio, bool has_video,
- bool has_text,
+ int stream_flags,
bool is_audio_encrypted,
bool is_video_encrypted) {
scoped_ptr<uint8[]> info_tracks;
int info_tracks_size = 0;
- CreateInitSegment(has_audio, has_video, has_text,
+ CreateInitSegment(stream_flags,
is_audio_encrypted, is_video_encrypted,
&info_tracks, &info_tracks_size);
AppendData(source_id, info_tracks.get(), info_tracks_size);
@@ -470,18 +471,14 @@ class ChunkDemuxerTest : public testing::Test {
};
bool InitDemuxer(int stream_flags) {
- return InitDemuxerWithEncryptionInfo(
- (stream_flags & HAS_AUDIO) != 0,
- (stream_flags & HAS_VIDEO) != 0,
- (stream_flags & HAS_TEXT) != 0,
- false, false);
+ return InitDemuxerWithEncryptionInfo(stream_flags, false, false);
}
bool InitDemuxerWithEncryptionInfo(
- bool has_audio, bool has_video, bool has_text,
- bool is_audio_encrypted, bool is_video_encrypted) {
+ int stream_flags, bool is_audio_encrypted, bool is_video_encrypted) {
+
PipelineStatus expected_status =
- (has_audio || has_video) ? PIPELINE_OK : DEMUXER_ERROR_COULD_NOT_OPEN;
+ (stream_flags != 0) ? PIPELINE_OK : DEMUXER_ERROR_COULD_NOT_OPEN;
base::TimeDelta expected_duration = kNoTimestamp();
if (expected_status == PIPELINE_OK)
@@ -491,11 +488,11 @@ class ChunkDemuxerTest : public testing::Test {
demuxer_->Initialize(
&host_, CreateInitDoneCB(expected_duration, expected_status), true);
- if (AddId(kSourceId, has_audio, has_video) != ChunkDemuxer::kOk)
+ if (AddId(kSourceId, stream_flags) != ChunkDemuxer::kOk)
return false;
AppendInitSegmentWithEncryptedInfo(
- kSourceId, has_audio, has_video, has_text,
+ kSourceId, stream_flags,
is_audio_encrypted, is_video_encrypted);
return true;
}
@@ -507,13 +504,21 @@ class ChunkDemuxerTest : public testing::Test {
demuxer_->Initialize(
&host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true);
- if (AddId(audio_id, true, false) != ChunkDemuxer::kOk)
+ if (AddId(audio_id, HAS_AUDIO) != ChunkDemuxer::kOk)
return false;
- if (AddId(video_id, false, true) != ChunkDemuxer::kOk)
+ if (AddId(video_id, HAS_VIDEO) != ChunkDemuxer::kOk)
return false;
- AppendInitSegmentWithSourceId(audio_id, true, false, has_text);
- AppendInitSegmentWithSourceId(video_id, false, true, has_text);
+ int audio_flags = HAS_AUDIO;
+ int video_flags = HAS_VIDEO;
+
+ if (has_text) {
+ audio_flags |= HAS_TEXT;
+ video_flags |= HAS_TEXT;
+ }
+
+ AppendInitSegmentWithSourceId(audio_id, audio_flags);
+ AppendInitSegmentWithSourceId(video_id, video_flags);
return true;
}
@@ -548,7 +553,7 @@ class ChunkDemuxerTest : public testing::Test {
&host_, CreateInitDoneCB(base::TimeDelta::FromMilliseconds(2744),
PIPELINE_OK), true);
- if (AddId(kSourceId, true, true) != ChunkDemuxer::kOk)
+ if (AddId(kSourceId, HAS_AUDIO | HAS_VIDEO) != ChunkDemuxer::kOk)
return false;
// Append the whole bear1 file.
@@ -863,18 +868,18 @@ class ChunkDemuxerTest : public testing::Test {
bool ParseWebMFile(const std::string& filename,
const BufferTimestamps* timestamps,
const base::TimeDelta& duration) {
- return ParseWebMFile(filename, timestamps, duration, true, true);
+ return ParseWebMFile(filename, timestamps, duration, HAS_AUDIO | HAS_VIDEO);
}
bool ParseWebMFile(const std::string& filename,
const BufferTimestamps* timestamps,
const base::TimeDelta& duration,
- bool has_audio, bool has_video) {
+ int stream_flags) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(duration, PIPELINE_OK), true);
- if (AddId(kSourceId, has_audio, has_video) != ChunkDemuxer::kOk)
+ if (AddId(kSourceId, stream_flags) != ChunkDemuxer::kOk)
return false;
// Read a WebM file into memory and send the data to the demuxer.
@@ -968,8 +973,15 @@ TEST_F(ChunkDemuxerTest, Init) {
.Times(Exactly(need_key_count));
}
+ int stream_flags = 0;
+ if (has_audio)
+ stream_flags |= HAS_AUDIO;
+
+ if (has_video)
+ stream_flags |= HAS_VIDEO;
+
ASSERT_TRUE(InitDemuxerWithEncryptionInfo(
- has_audio, has_video, false, is_audio_encrypted, is_video_encrypted));
+ stream_flags, is_audio_encrypted, is_video_encrypted));
DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO);
if (has_audio) {
@@ -1003,6 +1015,8 @@ TEST_F(ChunkDemuxerTest, Init) {
}
}
+// TODO(acolwell): Fold this test into Init tests since the tests are
+// almost identical.
TEST_F(ChunkDemuxerTest, InitText) {
// Test with 1 video stream and 1 text streams, and 0 or 1 audio streams.
// No encryption cases handled here.
@@ -1020,8 +1034,15 @@ TEST_F(ChunkDemuxerTest, InitText) {
.WillOnce(DoAll(SaveArg<0>(&text_stream),
SaveArg<1>(&text_config)));
+ int stream_flags = HAS_TEXT;
+ if (has_audio)
+ stream_flags |= HAS_AUDIO;
+
+ if (has_video)
+ stream_flags |= HAS_VIDEO;
+
ASSERT_TRUE(InitDemuxerWithEncryptionInfo(
- has_audio, has_video, true, is_audio_encrypted, is_video_encrypted));
+ stream_flags, is_audio_encrypted, is_video_encrypted));
ASSERT_TRUE(text_stream);
EXPECT_EQ(DemuxerStream::TEXT, text_stream->type());
EXPECT_EQ(kTextSubtitles, text_config.kind());
@@ -1060,31 +1081,65 @@ TEST_F(ChunkDemuxerTest, InitText) {
// Make sure that the demuxer reports an error if Shutdown()
// is called before all the initialization segments are appended.
-TEST_F(ChunkDemuxerTest, ShutdownBeforeAllInitSegmentsAppended) {
+TEST_F(ChunkDemuxerTest, Shutdown_BeforeAllInitSegmentsAppended) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(
kDefaultDuration(), DEMUXER_ERROR_COULD_NOT_OPEN), true);
- EXPECT_EQ(AddId("audio", true, false), ChunkDemuxer::kOk);
- EXPECT_EQ(AddId("video", false, true), ChunkDemuxer::kOk);
+ EXPECT_EQ(AddId("audio", HAS_AUDIO), ChunkDemuxer::kOk);
+ EXPECT_EQ(AddId("video", HAS_VIDEO), ChunkDemuxer::kOk);
+
+ AppendInitSegmentWithSourceId("audio", HAS_AUDIO);
- AppendInitSegmentWithSourceId("audio", true, false, false);
+ ShutdownDemuxer();
}
-TEST_F(ChunkDemuxerTest, ShutdownBeforeAllInitSegmentsAppendedText) {
+TEST_F(ChunkDemuxerTest, Shutdown_BeforeAllInitSegmentsAppendedText) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(
&host_, CreateInitDoneCB(
kDefaultDuration(), DEMUXER_ERROR_COULD_NOT_OPEN), true);
- EXPECT_EQ(AddId("audio", true, false), ChunkDemuxer::kOk);
- EXPECT_EQ(AddId("video", false, true), ChunkDemuxer::kOk);
+ EXPECT_EQ(AddId("audio", HAS_AUDIO), ChunkDemuxer::kOk);
+ EXPECT_EQ(AddId("video_and_text", HAS_VIDEO), ChunkDemuxer::kOk);
EXPECT_CALL(host_, AddTextStream(_, _))
.Times(Exactly(1));
- AppendInitSegmentWithSourceId("video", false, true, true);
+ AppendInitSegmentWithSourceId("video_and_text", HAS_VIDEO | HAS_TEXT);
+
+ ShutdownDemuxer();
+}
+
+// Verifies that all streams waiting for data receive an end of stream
+// buffer when Shutdown() is called.
+TEST_F(ChunkDemuxerTest, Shutdown_EndOfStreamWhileWaitingForData) {
+ DemuxerStream* text_stream = NULL;
+ EXPECT_CALL(host_, AddTextStream(_, _))
+ .WillOnce(SaveArg<0>(&text_stream));
+ ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO | HAS_TEXT));
+
+ DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO);
+ DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO);
+
+ bool audio_read_done = false;
+ bool video_read_done = false;
+ bool text_read_done = false;
+ audio_stream->Read(base::Bind(&OnReadDone_EOSExpected, &audio_read_done));
+ video_stream->Read(base::Bind(&OnReadDone_EOSExpected, &video_read_done));
+ text_stream->Read(base::Bind(&OnReadDone_EOSExpected, &text_read_done));
+ message_loop_.RunUntilIdle();
+
+ EXPECT_FALSE(audio_read_done);
+ EXPECT_FALSE(video_read_done);
+ EXPECT_FALSE(text_read_done);
+
+ ShutdownDemuxer();
+
+ EXPECT_TRUE(audio_read_done);
+ EXPECT_TRUE(video_read_done);
+ EXPECT_TRUE(text_read_done);
}
// Test that Seek() completes successfully when the first cluster
@@ -1159,7 +1214,7 @@ TEST_F(ChunkDemuxerTest, SeekWhileParsingCluster) {
TEST_F(ChunkDemuxerTest, AppendDataBeforeInit) {
scoped_ptr<uint8[]> info_tracks;
int info_tracks_size = 0;
- CreateInitSegment(true, true, false,
+ CreateInitSegment(HAS_AUDIO | HAS_VIDEO,
false, false, &info_tracks, &info_tracks_size);
demuxer_->AppendData(kSourceId, info_tracks.get(), info_tracks_size);
@@ -1528,7 +1583,7 @@ TEST_F(ChunkDemuxerTest, AppendingInPieces) {
scoped_ptr<uint8[]> info_tracks;
int info_tracks_size = 0;
- CreateInitSegment(true, true, false,
+ CreateInitSegment(HAS_AUDIO | HAS_VIDEO,
false, false, &info_tracks, &info_tracks_size);
scoped_ptr<Cluster> cluster_a(kDefaultFirstCluster());
@@ -1591,7 +1646,7 @@ TEST_F(ChunkDemuxerTest, WebMFile_AudioOnly) {
ASSERT_TRUE(ParseWebMFile("bear-320x240-audio-only.webm", buffer_timestamps,
base::TimeDelta::FromMilliseconds(2744),
- true, false));
+ HAS_AUDIO));
}
TEST_F(ChunkDemuxerTest, WebMFile_VideoOnly) {
@@ -1606,7 +1661,7 @@ TEST_F(ChunkDemuxerTest, WebMFile_VideoOnly) {
ASSERT_TRUE(ParseWebMFile("bear-320x240-video-only.webm", buffer_timestamps,
base::TimeDelta::FromMilliseconds(2703),
- false, true));
+ HAS_VIDEO));
}
TEST_F(ChunkDemuxerTest, WebMFile_AltRefFrames) {
@@ -1710,7 +1765,7 @@ TEST_F(ChunkDemuxerTest, AVHeadersWithAudioOnlyType) {
ASSERT_EQ(demuxer_->AddId(kSourceId, "audio/webm", codecs),
ChunkDemuxer::kOk);
- AppendInitSegment(true, true);
+ AppendInitSegment(HAS_AUDIO | HAS_VIDEO);
}
TEST_F(ChunkDemuxerTest, AVHeadersWithVideoOnlyType) {
@@ -1724,7 +1779,7 @@ TEST_F(ChunkDemuxerTest, AVHeadersWithVideoOnlyType) {
ASSERT_EQ(demuxer_->AddId(kSourceId, "video/webm", codecs),
ChunkDemuxer::kOk);
- AppendInitSegment(true, true);
+ AppendInitSegment(HAS_AUDIO | HAS_VIDEO);
}
TEST_F(ChunkDemuxerTest, MultipleHeaders) {
@@ -1733,7 +1788,7 @@ TEST_F(ChunkDemuxerTest, MultipleHeaders) {
AppendCluster(kDefaultFirstCluster());
// Append another identical initialization segment.
- AppendInitSegment(true, true);
+ AppendInitSegment(HAS_AUDIO | HAS_VIDEO);
AppendCluster(kDefaultSecondCluster());
@@ -1782,15 +1837,15 @@ TEST_F(ChunkDemuxerTest, AddIdFailures) {
std::string audio_id = "audio1";
std::string video_id = "video1";
- ASSERT_EQ(AddId(audio_id, true, false), ChunkDemuxer::kOk);
+ ASSERT_EQ(AddId(audio_id, HAS_AUDIO), ChunkDemuxer::kOk);
// Adding an id with audio/video should fail because we already added audio.
ASSERT_EQ(AddId(), ChunkDemuxer::kReachedIdLimit);
- AppendInitSegmentWithSourceId(audio_id, true, false, false);
+ AppendInitSegmentWithSourceId(audio_id, HAS_AUDIO);
// Adding an id after append should fail.
- ASSERT_EQ(AddId(video_id, false, true), ChunkDemuxer::kReachedIdLimit);
+ ASSERT_EQ(AddId(video_id, HAS_VIDEO), ChunkDemuxer::kReachedIdLimit);
}
// Test that Read() calls after a RemoveId() return "end of stream" buffers.
@@ -1825,11 +1880,11 @@ TEST_F(ChunkDemuxerTest, RemoveId) {
// quota for new IDs in the future.
TEST_F(ChunkDemuxerTest, RemoveAndAddId) {
std::string audio_id_1 = "audio1";
- ASSERT_TRUE(AddId(audio_id_1, true, false) == ChunkDemuxer::kOk);
+ ASSERT_TRUE(AddId(audio_id_1, HAS_AUDIO) == ChunkDemuxer::kOk);
demuxer_->RemoveId(audio_id_1);
std::string audio_id_2 = "audio2";
- ASSERT_TRUE(AddId(audio_id_2, true, false) == ChunkDemuxer::kOk);
+ ASSERT_TRUE(AddId(audio_id_2, HAS_AUDIO) == ChunkDemuxer::kOk);
}
TEST_F(ChunkDemuxerTest, SeekCanceled) {
@@ -2018,8 +2073,8 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioIdOnly) {
demuxer_->Initialize(
&host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true);
- ASSERT_EQ(AddId(kSourceId, true, false), ChunkDemuxer::kOk);
- AppendInitSegment(true, false);
+ ASSERT_EQ(AddId(kSourceId, HAS_AUDIO), ChunkDemuxer::kOk);
+ AppendInitSegment(HAS_AUDIO);
// Test a simple cluster.
AppendCluster(
@@ -2040,8 +2095,8 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_VideoIdOnly) {
demuxer_->Initialize(
&host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true);
- ASSERT_EQ(AddId(kSourceId, false, true), ChunkDemuxer::kOk);
- AppendInitSegment(false, true);
+ ASSERT_EQ(AddId(kSourceId, HAS_VIDEO), ChunkDemuxer::kOk);
+ AppendInitSegment(HAS_VIDEO);
// Test a simple cluster.
AppendCluster(
@@ -2349,8 +2404,8 @@ TEST_F(ChunkDemuxerTest, EndOfStreamStillSetAfterSeek) {
TEST_F(ChunkDemuxerTest, GetBufferedRangesBeforeInitSegment) {
EXPECT_CALL(*this, DemuxerOpened());
demuxer_->Initialize(&host_, CreateInitDoneCB(PIPELINE_OK), true);
- ASSERT_EQ(AddId("audio", true, false), ChunkDemuxer::kOk);
- ASSERT_EQ(AddId("video", false, true), ChunkDemuxer::kOk);
+ ASSERT_EQ(AddId("audio", HAS_AUDIO), ChunkDemuxer::kOk);
+ ASSERT_EQ(AddId("video", HAS_VIDEO), ChunkDemuxer::kOk);
CheckExpectedRanges("audio", "{ }");
CheckExpectedRanges("video", "{ }");
@@ -2668,7 +2723,7 @@ TEST_F(ChunkDemuxerTest, AppendAfterEndOfStream) {
// Test receiving a Shutdown() call before we get an Initialize()
// call. This can happen if video element gets destroyed before
// the pipeline has a chance to initialize the demuxer.
-TEST_F(ChunkDemuxerTest, ShutdownBeforeInitialize) {
+TEST_F(ChunkDemuxerTest, Shutdown_BeforeInitialize) {
demuxer_->Shutdown();
demuxer_->Initialize(
&host_, CreateInitDoneCB(DEMUXER_ERROR_COULD_NOT_OPEN), true);
@@ -2822,7 +2877,7 @@ TEST_F(ChunkDemuxerTest, RemoveBeforeInitSegment) {
demuxer_->Initialize(
&host_, CreateInitDoneCB(kNoTimestamp(), PIPELINE_OK), true);
- EXPECT_EQ(ChunkDemuxer::kOk, AddId(kSourceId, true, true));
+ EXPECT_EQ(ChunkDemuxer::kOk, AddId(kSourceId, HAS_AUDIO | HAS_VIDEO));
demuxer_->Remove(kSourceId, base::TimeDelta::FromMilliseconds(0),
base::TimeDelta::FromMilliseconds(1));
@@ -2941,4 +2996,102 @@ TEST_F(ChunkDemuxerTest, StartWaitingForSeekAfterParseError) {
demuxer_->StartWaitingForSeek(seek_time);
}
+TEST_F(ChunkDemuxerTest, Remove_AudioVideoText) {
+ DemuxerStream* text_stream = NULL;
+ EXPECT_CALL(host_, AddTextStream(_, _))
+ .WillOnce(SaveArg<0>(&text_stream));
+ ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO | HAS_TEXT));
+
+ DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO);
+ DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO);
+
+ AppendSingleStreamCluster(kSourceId, kAudioTrackNum,
+ "0K 20K 40K 60K 80K 100K 120K 140K");
+ AppendSingleStreamCluster(kSourceId, kVideoTrackNum,
+ "0K 30 60 90 120K 150 180");
+ AppendSingleStreamCluster(kSourceId, kTextTrackNum, "0K 100K 200K");
+
+ CheckExpectedBuffers(audio_stream, "0 20 40 60 80 100 120 140");
+ CheckExpectedBuffers(video_stream, "0 30 60 90 120 150 180");
+ CheckExpectedBuffers(text_stream, "0 100 200");
+
+ // Remove the buffers that were added.
+ demuxer_->Remove(kSourceId, base::TimeDelta(),
+ base::TimeDelta::FromMilliseconds(300));
+
+ // Verify that all the appended data has been removed.
+ CheckExpectedRanges(kSourceId, "{ }");
+
+ // Append new buffers that are clearly different than the original
+ // ones and verify that only the new buffers are returned.
+ AppendSingleStreamCluster(kSourceId, kAudioTrackNum,
+ "1K 21K 41K 61K 81K 101K 121K 141K");
+ AppendSingleStreamCluster(kSourceId, kVideoTrackNum,
+ "1K 31 61 91 121K 151 181");
+ AppendSingleStreamCluster(kSourceId, kTextTrackNum, "1K 101K 201K");
+
+ Seek(base::TimeDelta());
+ CheckExpectedBuffers(audio_stream, "1 21 41 61 81 101 121 141");
+ CheckExpectedBuffers(video_stream, "1 31 61 91 121 151 181");
+ CheckExpectedBuffers(text_stream, "1 101 201");
+}
+
+// Verifies that a Seek() will complete without text cues for
+// the seek point and will return cues after the seek position
+// when they are eventually appended.
+TEST_F(ChunkDemuxerTest, SeekCompletesWithoutTextCues) {
+ DemuxerStream* text_stream = NULL;
+ EXPECT_CALL(host_, AddTextStream(_, _))
+ .WillOnce(SaveArg<0>(&text_stream));
+ ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO | HAS_TEXT));
+
+ DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO);
+ DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO);
+
+ base::TimeDelta seek_time = base::TimeDelta::FromMilliseconds(120);
+ bool seek_cb_was_called = false;
+ demuxer_->StartWaitingForSeek(seek_time);
+ demuxer_->Seek(seek_time,
+ base::Bind(OnSeekDone_OKExpected, &seek_cb_was_called));
+ message_loop_.RunUntilIdle();
+
+ EXPECT_FALSE(seek_cb_was_called);
+
+ bool text_read_done = false;
+ text_stream->Read(base::Bind(&OnReadDone,
+ base::TimeDelta::FromMilliseconds(125),
+ &text_read_done));
+
+ // Append audio & video data so the seek completes.
+ AppendSingleStreamCluster(kSourceId, kAudioTrackNum,
+ "0K 20K 40K 60K 80K 100K 120K 140K 160K 180K");
+ AppendSingleStreamCluster(kSourceId, kVideoTrackNum,
+ "0K 30 60 90 120K 150 180 210");
+
+ message_loop_.RunUntilIdle();
+ EXPECT_TRUE(seek_cb_was_called);
+ EXPECT_FALSE(text_read_done);
+
+ // Read some audio & video buffers to further verify seek completion.
+ CheckExpectedBuffers(audio_stream, "120 140");
+ CheckExpectedBuffers(video_stream, "120 150");
+
+ EXPECT_FALSE(text_read_done);
+
+ // Append text cues that start after the seek point and verify that
+ // they are returned by Read() calls.
+ AppendSingleStreamCluster(kSourceId, kTextTrackNum, "125K 175K 225K");
+
+ message_loop_.RunUntilIdle();
+ EXPECT_TRUE(text_read_done);
+
+ // NOTE: we start at 175 here because the buffer at 125 was returned
+ // to the pending read initiated above.
+ CheckExpectedBuffers(text_stream, "175 225");
+
+ // Verify that audio & video streams contiue to return expected values.
+ CheckExpectedBuffers(audio_stream, "160 180");
+ CheckExpectedBuffers(video_stream, "180 210");
+}
+
} // namespace media