summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/base/stream_parser.h3
-rw-r--r--media/filters/chunk_demuxer.cc74
-rw-r--r--media/filters/chunk_demuxer.h22
-rw-r--r--media/filters/chunk_demuxer_unittest.cc66
-rw-r--r--media/filters/source_buffer_stream.cc2
-rw-r--r--media/mp4/mp4_stream_parser.cc13
-rw-r--r--media/mp4/mp4_stream_parser.h4
-rw-r--r--media/mp4/mp4_stream_parser_unittest.cc8
-rw-r--r--media/webm/webm_cluster_parser.cc9
-rw-r--r--media/webm/webm_cluster_parser.h4
-rw-r--r--media/webm/webm_stream_parser.cc9
-rw-r--r--media/webm/webm_stream_parser.h4
-rw-r--r--webkit/media/webmediaplayer_impl.cc5
-rw-r--r--webkit/media/webmediaplayer_impl.h2
-rw-r--r--webkit/media/webmediaplayer_proxy.cc5
-rw-r--r--webkit/media/webmediaplayer_proxy.h1
16 files changed, 211 insertions, 20 deletions
diff --git a/media/base/stream_parser.h b/media/base/stream_parser.h
index 3ad3cd7..64a8c49 100644
--- a/media/base/stream_parser.h
+++ b/media/base/stream_parser.h
@@ -74,7 +74,8 @@ class MEDIA_EXPORT StreamParser {
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_cb,
const NeedKeyCB& need_key_cb,
- const NewMediaSegmentCB& new_segment_cb) = 0;
+ const NewMediaSegmentCB& new_segment_cb,
+ const base::Closure& end_of_segment_cb) = 0;
// Called when a seek occurs. This flushes the current parser state
// and puts the parser in a state where it can receive data for the new seek
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index be4fa60..29e31af 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -111,7 +111,7 @@ static bool IsSupported(const std::string& type,
*has_audio = false;
*has_video = false;
- // Search for the SupportedTypeInfo for |type|
+ // Search for the SupportedTypeInfo for |type|.
for (size_t i = 0; i < arraysize(kSupportedTypeInfo); ++i) {
const SupportedTypeInfo& type_info = kSupportedTypeInfo[i];
if (type == type_info.type) {
@@ -637,19 +637,24 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
audio_cb,
video_cb,
base::Bind(&ChunkDemuxer::OnNeedKey, base::Unretained(this)),
- base::Bind(&ChunkDemuxer::OnNewMediaSegment, base::Unretained(this), id));
+ base::Bind(&ChunkDemuxer::OnNewMediaSegment, base::Unretained(this), id),
+ base::Bind(&ChunkDemuxer::OnEndOfMediaSegment,
+ base::Unretained(this), id));
stream_parser_map_[id] = stream_parser.release();
+ SourceInfo info = { base::TimeDelta(), true };
+ source_info_map_[id] = info;
return kOk;
}
void ChunkDemuxer::RemoveId(const std::string& id) {
- CHECK_GT(stream_parser_map_.count(id), 0u);
+ CHECK(IsValidId(id));
base::AutoLock auto_lock(lock_);
delete stream_parser_map_[id];
stream_parser_map_.erase(id);
+ source_info_map_.erase(id);
if (source_id_audio_ == id && audio_)
audio_->Shutdown();
@@ -660,7 +665,7 @@ void ChunkDemuxer::RemoveId(const std::string& id) {
Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const {
DCHECK(!id.empty());
- DCHECK_GT(stream_parser_map_.count(id), 0u);
+ DCHECK(IsValidId(id));
DCHECK(id == source_id_audio_ || id == source_id_video_);
base::AutoLock auto_lock(lock_);
@@ -730,7 +735,7 @@ bool ChunkDemuxer::AppendData(const std::string& id,
switch (state_) {
case INITIALIZING:
case WAITING_FOR_START_TIME:
- DCHECK_GT(stream_parser_map_.count(id), 0u);
+ DCHECK(IsValidId(id));
if (!stream_parser_map_[id]->Parse(data, length)) {
ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
return true;
@@ -738,7 +743,7 @@ bool ChunkDemuxer::AppendData(const std::string& id,
break;
case INITIALIZED: {
- DCHECK_GT(stream_parser_map_.count(id), 0u);
+ DCHECK(IsValidId(id));
if (!stream_parser_map_[id]->Parse(data, length)) {
ReportError_Locked(PIPELINE_ERROR_DECODE);
return true;
@@ -780,11 +785,24 @@ bool ChunkDemuxer::AppendData(const std::string& id,
void ChunkDemuxer::Abort(const std::string& id) {
DVLOG(1) << "Abort(" << id << ")";
DCHECK(!id.empty());
- DCHECK_GT(stream_parser_map_.count(id), 0u);
+ CHECK(IsValidId(id));
stream_parser_map_[id]->Flush();
}
+bool ChunkDemuxer::SetTimestampOffset(const std::string& id, double offset) {
+ DVLOG(1) << "SetTimestampOffset(" << id << ", " << offset << ")";
+ CHECK(IsValidId(id));
+
+ if (!source_info_map_[id].can_update_offset)
+ return false;
+
+ TimeDelta time_offset = TimeDelta::FromMicroseconds(
+ offset * base::Time::kMicrosecondsPerSecond);
+ source_info_map_[id].timestamp_offset = time_offset;
+ return true;
+}
+
bool ChunkDemuxer::EndOfStream(PipelineStatus status) {
DVLOG(1) << "EndOfStream(" << status << ")";
base::AutoLock auto_lock(lock_);
@@ -983,6 +1001,10 @@ bool ChunkDemuxer::OnAudioBuffers(const StreamParser::BufferQueue& buffers) {
if (!audio_)
return false;
+ CHECK(IsValidId(source_id_audio_));
+ AdjustBufferTimestamps(
+ buffers, source_info_map_[source_id_audio_].timestamp_offset);
+
return audio_->Append(buffers);
}
@@ -993,6 +1015,10 @@ bool ChunkDemuxer::OnVideoBuffers(const StreamParser::BufferQueue& buffers) {
if (!video_)
return false;
+ CHECK(IsValidId(source_id_video_));
+ AdjustBufferTimestamps(
+ buffers, source_info_map_[source_id_video_].timestamp_offset);
+
return video_->Append(buffers);
}
@@ -1003,11 +1029,16 @@ bool ChunkDemuxer::OnNeedKey(scoped_array<uint8> init_data,
}
void ChunkDemuxer::OnNewMediaSegment(const std::string& source_id,
- TimeDelta start_timestamp) {
+ TimeDelta timestamp) {
DVLOG(2) << "OnNewMediaSegment(" << source_id << ", "
- << start_timestamp.InSecondsF() << ")";
+ << timestamp.InSecondsF() << ")";
lock_.AssertAcquired();
+ CHECK(IsValidId(source_id));
+ source_info_map_[source_id].can_update_offset = false;
+ base::TimeDelta start_timestamp =
+ timestamp + source_info_map_[source_id].timestamp_offset;
+
if (start_time_ == kNoTimestamp()) {
DCHECK(state_ == INITIALIZING || state_ == WAITING_FOR_START_TIME);
// Use the first reported media segment start time as the |start_time_|
@@ -1037,4 +1068,29 @@ void ChunkDemuxer::OnNewMediaSegment(const std::string& source_id,
base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
}
+void ChunkDemuxer::OnEndOfMediaSegment(const std::string& source_id) {
+ DVLOG(2) << "OnEndOfMediaSegment(" << source_id << ")";
+ CHECK(IsValidId(source_id));
+ source_info_map_[source_id].can_update_offset = true;
+}
+
+void ChunkDemuxer::AdjustBufferTimestamps(
+ const StreamParser::BufferQueue& buffers,
+ base::TimeDelta timestamp_offset) {
+ if (timestamp_offset == base::TimeDelta())
+ return;
+
+ for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
+ itr != buffers.end(); ++itr) {
+ (*itr)->SetDecodeTimestamp(
+ (*itr)->GetDecodeTimestamp() + timestamp_offset);
+ (*itr)->SetTimestamp((*itr)->GetTimestamp() + timestamp_offset);
+ }
+}
+
+bool ChunkDemuxer::IsValidId(const std::string& source_id) const {
+ return source_info_map_.count(source_id) > 0u &&
+ stream_parser_map_.count(source_id) > 0u;
+}
+
} // namespace media
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index ced6d97..773f1d5 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -74,6 +74,12 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
// it can accept a new segment.
void Abort(const std::string& id);
+ // Sets a time |offset| in seconds to be applied to subsequent buffers
+ // appended to the source buffer assicated with |id|. Returns true if the
+ // offset is set properly, false if the offset cannot be applied because we're
+ // in the middle of parsing a media segment.
+ bool SetTimestampOffset(const std::string& id, double offset);
+
// 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.
@@ -117,11 +123,19 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
bool OnNeedKey(scoped_array<uint8> init_data, int init_data_size);
void OnNewMediaSegment(const std::string& source_id,
base::TimeDelta start_timestamp);
+ void OnEndOfMediaSegment(const std::string& source_id);
// Computes the intersection between the video & audio
// buffered ranges.
Ranges<base::TimeDelta> ComputeIntersection() const;
+ // Applies |time_offset| to the timestamps of |buffers|.
+ void AdjustBufferTimestamps(const StreamParser::BufferQueue& buffers,
+ base::TimeDelta timestamp_offset);
+
+ // Returns true if |source_id| is valid, false otherwise.
+ bool IsValidId(const std::string& source_id) const;
+
mutable base::Lock lock_;
State state_;
@@ -138,6 +152,14 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
typedef std::map<std::string, StreamParser*> StreamParserMap;
StreamParserMap stream_parser_map_;
+ // Contains state belonging to a source id.
+ struct SourceInfo {
+ base::TimeDelta timestamp_offset;
+ bool can_update_offset;
+ };
+ typedef std::map<std::string, SourceInfo> SourceInfoMap;
+ SourceInfoMap source_info_map_;
+
// Used to ensure that (1) config data matches the type and codec provided in
// AddId(), (2) only 1 audio and 1 video sources are added, and (3) ids may be
// removed with RemoveID() but can not be re-added (yet).
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index fda9bb7..927636a 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -2064,4 +2064,70 @@ TEST_F(ChunkDemuxerTest, TestConfigChange_Seek) {
ASSERT_TRUE(video_config_1.Matches(stream->video_decoder_config()));
}
+TEST_F(ChunkDemuxerTest, TestTimestampPositiveOffset) {
+ ASSERT_TRUE(InitDemuxer(true, true, false));
+
+ ASSERT_TRUE(demuxer_->SetTimestampOffset(kSourceId, 30));
+ scoped_ptr<Cluster> cluster(GenerateCluster(0, 2));
+ ASSERT_TRUE(AppendData(cluster->data(), cluster->size()));
+
+ scoped_refptr<DemuxerStream> audio =
+ demuxer_->GetStream(DemuxerStream::AUDIO);
+ scoped_refptr<DemuxerStream> video =
+ demuxer_->GetStream(DemuxerStream::VIDEO);
+ GenerateExpectedReads(30000, 2, audio, video);
+}
+
+TEST_F(ChunkDemuxerTest, TestTimestampNegativeOffset) {
+ ASSERT_TRUE(InitDemuxer(true, true, false));
+
+ ASSERT_TRUE(demuxer_->SetTimestampOffset(kSourceId, -1));
+ scoped_ptr<Cluster> cluster = GenerateCluster(1000, 2);
+ ASSERT_TRUE(AppendData(cluster->data(), cluster->size()));
+
+ scoped_refptr<DemuxerStream> audio =
+ demuxer_->GetStream(DemuxerStream::AUDIO);
+ scoped_refptr<DemuxerStream> video =
+ demuxer_->GetStream(DemuxerStream::VIDEO);
+ GenerateExpectedReads(0, 2, audio, video);
+}
+
+TEST_F(ChunkDemuxerTest, TestTimestampOffsetSeparateStreams) {
+ std::string audio_id = "audio1";
+ std::string video_id = "video1";
+ ASSERT_TRUE(InitDemuxerAudioAndVideoSources(audio_id, video_id));
+
+ scoped_refptr<DemuxerStream> audio =
+ demuxer_->GetStream(DemuxerStream::AUDIO);
+ scoped_refptr<DemuxerStream> video =
+ demuxer_->GetStream(DemuxerStream::VIDEO);
+
+ scoped_ptr<Cluster> cluster_a(
+ GenerateSingleStreamCluster(
+ 2500, 2500 + kAudioBlockDuration * 4, kAudioTrackNum,
+ kAudioBlockDuration));
+
+ scoped_ptr<Cluster> cluster_v(
+ GenerateSingleStreamCluster(
+ 0, kVideoBlockDuration * 4, kVideoTrackNum, kVideoBlockDuration));
+
+ ASSERT_TRUE(demuxer_->SetTimestampOffset(audio_id, -2.5));
+ ASSERT_TRUE(AppendData(audio_id, cluster_a->data(), cluster_a->size()));
+ GenerateSingleStreamExpectedReads(0, 4, audio, kAudioBlockDuration);
+
+ ASSERT_TRUE(demuxer_->SetTimestampOffset(video_id, 27.3));
+ ASSERT_TRUE(AppendData(video_id, cluster_v->data(), cluster_v->size()));
+ GenerateSingleStreamExpectedReads(27300, 4, video, kVideoBlockDuration);
+}
+
+TEST_F(ChunkDemuxerTest, TestTimestampOffsetMidParse) {
+ ASSERT_TRUE(InitDemuxer(true, true, false));
+
+ scoped_ptr<Cluster> cluster = GenerateCluster(0, 2);
+ // Append only part of the cluster data.
+ ASSERT_TRUE(AppendData(cluster->data(), cluster->size() - 13));
+
+ // Setting a timestamp should fail because we're in the middle of a cluster.
+ ASSERT_FALSE(demuxer_->SetTimestampOffset(kSourceId, 25));
+}
} // namespace media
diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc
index 2ffbb6a..8b85984 100644
--- a/media/filters/source_buffer_stream.cc
+++ b/media/filters/source_buffer_stream.cc
@@ -982,7 +982,7 @@ SourceBufferRange* SourceBufferRange::SplitRange(base::TimeDelta timestamp) {
SourceBufferRange::KeyframeMap::iterator
SourceBufferRange::GetFirstKeyframeAt(base::TimeDelta timestamp,
- bool skip_given_timestamp) {
+ bool skip_given_timestamp) {
KeyframeMap::iterator result = keyframe_map_.lower_bound(timestamp);
// lower_bound() returns the first element >= |timestamp|, so if we don't want
// to include keyframes == |timestamp|, we have to increment the iterator
diff --git a/media/mp4/mp4_stream_parser.cc b/media/mp4/mp4_stream_parser.cc
index 757ea503..9d51959 100644
--- a/media/mp4/mp4_stream_parser.cc
+++ b/media/mp4/mp4_stream_parser.cc
@@ -37,13 +37,15 @@ void MP4StreamParser::Init(const InitCB& init_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_cb,
const NeedKeyCB& need_key_cb,
- const NewMediaSegmentCB& new_segment_cb) {
+ const NewMediaSegmentCB& new_segment_cb,
+ const base::Closure& end_of_segment_cb) {
DCHECK_EQ(state_, kWaitingForInit);
DCHECK(init_cb_.is_null());
DCHECK(!init_cb.is_null());
DCHECK(!config_cb.is_null());
DCHECK(!audio_cb.is_null() || !video_cb.is_null());
DCHECK(!need_key_cb.is_null());
+ DCHECK(!end_of_segment_cb.is_null());
ChangeState(kParsingBoxes);
init_cb_ = init_cb;
@@ -52,6 +54,7 @@ void MP4StreamParser::Init(const InitCB& init_cb,
video_cb_ = video_cb;
need_key_cb_ = need_key_cb;
new_segment_cb_ = new_segment_cb;
+ end_of_segment_cb_ = end_of_segment_cb;
}
void MP4StreamParser::Flush() {
@@ -316,8 +319,11 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers,
// Flush any buffers we've gotten in this chunk so that buffers don't
// cross NewSegment() calls
*err = !SendAndFlushSamples(audio_buffers, video_buffers);
- if (*err) return false;
+ if (*err)
+ return false;
+
ChangeState(kParsingBoxes);
+ end_of_segment_cb_.Run();
return true;
}
@@ -337,7 +343,8 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers,
bool video = has_video_ && video_track_id_ == runs_->track_id();
// Skip this entire track if it's not one we're interested in
- if (!audio && !video) runs_->AdvanceRun();
+ if (!audio && !video)
+ runs_->AdvanceRun();
// Attempt to cache the auxiliary information first. Aux info is usually
// placed in a contiguous block before the sample data, rather than being
diff --git a/media/mp4/mp4_stream_parser.h b/media/mp4/mp4_stream_parser.h
index 53100a95..b950af2 100644
--- a/media/mp4/mp4_stream_parser.h
+++ b/media/mp4/mp4_stream_parser.h
@@ -31,7 +31,8 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser {
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_cb,
const NeedKeyCB& need_key_cb,
- const NewMediaSegmentCB& new_segment_cb) OVERRIDE;
+ const NewMediaSegmentCB& new_segment_cb,
+ const base::Closure& end_of_segment_cb) OVERRIDE;
virtual void Flush() OVERRIDE;
virtual bool Parse(const uint8* buf, int size) OVERRIDE;
@@ -70,6 +71,7 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser {
NewBuffersCB video_cb_;
NeedKeyCB need_key_cb_;
NewMediaSegmentCB new_segment_cb_;
+ base::Closure end_of_segment_cb_;
OffsetByteQueue queue_;
diff --git a/media/mp4/mp4_stream_parser_unittest.cc b/media/mp4/mp4_stream_parser_unittest.cc
index 63e1382..56f40df 100644
--- a/media/mp4/mp4_stream_parser_unittest.cc
+++ b/media/mp4/mp4_stream_parser_unittest.cc
@@ -93,6 +93,10 @@ class MP4StreamParserTest : public testing::Test {
segment_start_ = start_dts;
}
+ void EndOfSegmentF() {
+ DVLOG(1) << "EndOfSegmentF()";
+ }
+
void InitializeParser() {
parser_->Init(
base::Bind(&MP4StreamParserTest::InitF, base::Unretained(this)),
@@ -100,7 +104,9 @@ class MP4StreamParserTest : public testing::Test {
base::Bind(&MP4StreamParserTest::NewBuffersF, base::Unretained(this)),
base::Bind(&MP4StreamParserTest::NewBuffersF, base::Unretained(this)),
base::Bind(&MP4StreamParserTest::KeyNeededF, base::Unretained(this)),
- base::Bind(&MP4StreamParserTest::NewSegmentF, base::Unretained(this)));
+ base::Bind(&MP4StreamParserTest::NewSegmentF, base::Unretained(this)),
+ base::Bind(&MP4StreamParserTest::EndOfSegmentF,
+ base::Unretained(this)));
}
bool ParseMP4File(const std::string& filename, int append_bytes) {
diff --git a/media/webm/webm_cluster_parser.cc b/media/webm/webm_cluster_parser.cc
index 14a360d..e9ef93e 100644
--- a/media/webm/webm_cluster_parser.cc
+++ b/media/webm/webm_cluster_parser.cc
@@ -43,6 +43,7 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale,
block_duration_(-1),
cluster_timecode_(-1),
cluster_start_time_(kNoTimestamp()),
+ cluster_ended_(false),
audio_(audio_track_num),
video_(video_track_num) {
CHECK_GE(video_encryption_key_id_size, 0);
@@ -59,6 +60,7 @@ void WebMClusterParser::Reset() {
last_block_timecode_ = -1;
cluster_timecode_ = -1;
cluster_start_time_ = kNoTimestamp();
+ cluster_ended_ = false;
parser_.Reset();
audio_.Reset();
video_.Reset();
@@ -70,10 +72,13 @@ int WebMClusterParser::Parse(const uint8* buf, int size) {
int result = parser_.Parse(buf, size);
- if (result <= 0)
+ if (result <= 0) {
+ cluster_ended_ = false;
return result;
+ }
- if (parser_.IsParsingComplete()) {
+ cluster_ended_ = parser_.IsParsingComplete();
+ if (cluster_ended_) {
// If there were no buffers in this cluster, set the cluster start time to
// be the |cluster_timecode_|.
if (cluster_start_time_ == kNoTimestamp()) {
diff --git a/media/webm/webm_cluster_parser.h b/media/webm/webm_cluster_parser.h
index 2bdb1e7..926e6a1 100644
--- a/media/webm/webm_cluster_parser.h
+++ b/media/webm/webm_cluster_parser.h
@@ -44,6 +44,9 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
const BufferQueue& audio_buffers() const { return audio_.buffers(); }
const BufferQueue& video_buffers() const { return video_.buffers(); }
+ // Returns true if the last Parse() call stopped at the end of a cluster.
+ bool cluster_ended() const { return cluster_ended_; }
+
private:
// Helper class that manages per-track state.
class Track {
@@ -88,6 +91,7 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
int64 cluster_timecode_;
base::TimeDelta cluster_start_time_;
+ bool cluster_ended_;
Track audio_;
Track video_;
diff --git a/media/webm/webm_stream_parser.cc b/media/webm/webm_stream_parser.cc
index e9c8233..f27f7e7 100644
--- a/media/webm/webm_stream_parser.cc
+++ b/media/webm/webm_stream_parser.cc
@@ -192,7 +192,8 @@ void WebMStreamParser::Init(const InitCB& init_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_cb,
const NeedKeyCB& need_key_cb,
- const NewMediaSegmentCB& new_segment_cb) {
+ const NewMediaSegmentCB& new_segment_cb,
+ const base::Closure& end_of_segment_cb) {
DCHECK_EQ(state_, kWaitingForInit);
DCHECK(init_cb_.is_null());
DCHECK(!init_cb.is_null());
@@ -200,6 +201,7 @@ void WebMStreamParser::Init(const InitCB& init_cb,
DCHECK(!audio_cb.is_null() || !video_cb.is_null());
DCHECK(!need_key_cb.is_null());
DCHECK(!new_segment_cb.is_null());
+ DCHECK(!end_of_segment_cb.is_null());
ChangeState(kParsingHeaders);
init_cb_ = init_cb;
@@ -208,6 +210,7 @@ void WebMStreamParser::Init(const InitCB& init_cb,
video_cb_ = video_cb;
need_key_cb_ = need_key_cb;
new_segment_cb_ = new_segment_cb;
+ end_of_segment_cb_ = end_of_segment_cb;
}
void WebMStreamParser::Flush() {
@@ -416,6 +419,7 @@ int WebMStreamParser::ParseCluster(const uint8* data, int size) {
const BufferQueue& audio_buffers = cluster_parser_->audio_buffers();
const BufferQueue& video_buffers = cluster_parser_->video_buffers();
base::TimeDelta cluster_start_time = cluster_parser_->cluster_start_time();
+ bool cluster_ended = cluster_parser_->cluster_ended();
if (waiting_for_buffers_ && cluster_start_time != kNoTimestamp()) {
new_segment_cb_.Run(cluster_start_time);
@@ -428,6 +432,9 @@ int WebMStreamParser::ParseCluster(const uint8* data, int size) {
if (!video_buffers.empty() && !video_cb_.Run(video_buffers))
return -1;
+ if (cluster_ended)
+ end_of_segment_cb_.Run();
+
return bytes_parsed;
}
diff --git a/media/webm/webm_stream_parser.h b/media/webm/webm_stream_parser.h
index f555278..c5d06d5 100644
--- a/media/webm/webm_stream_parser.h
+++ b/media/webm/webm_stream_parser.h
@@ -26,7 +26,8 @@ class WebMStreamParser : public StreamParser {
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_cb,
const NeedKeyCB& need_key_cb,
- const NewMediaSegmentCB& new_segment_cb) OVERRIDE;
+ const NewMediaSegmentCB& new_segment_cb,
+ const base::Closure& end_of_segment_cb) OVERRIDE;
virtual void Flush() OVERRIDE;
virtual bool Parse(const uint8* buf, int size) OVERRIDE;
@@ -66,6 +67,7 @@ class WebMStreamParser : public StreamParser {
NewBuffersCB video_cb_;
NeedKeyCB need_key_cb_;
NewMediaSegmentCB new_segment_cb_;
+ base::Closure end_of_segment_cb_;
// True if a new cluster id has been seen, but no audio or video buffers have
// been parsed yet.
diff --git a/webkit/media/webmediaplayer_impl.cc b/webkit/media/webmediaplayer_impl.cc
index a8b4ab9..b5f65d0 100644
--- a/webkit/media/webmediaplayer_impl.cc
+++ b/webkit/media/webmediaplayer_impl.cc
@@ -653,6 +653,11 @@ WebKit::WebMediaPlayer::AddIdStatus WebMediaPlayerImpl::sourceAddId(
new_codecs));
}
+bool WebMediaPlayerImpl::sourceTimestampOffset(
+ const WebKit::WebString& id, double offset) {
+ return proxy_->DemuxerSetTimestampOffset(id.utf8().data(), offset);
+}
+
bool WebMediaPlayerImpl::sourceRemoveId(const WebKit::WebString& id) {
DCHECK(!id.isEmpty());
proxy_->DemuxerRemoveId(id.utf8().data());
diff --git a/webkit/media/webmediaplayer_impl.h b/webkit/media/webmediaplayer_impl.h
index 2b4a34c..22343cd 100644
--- a/webkit/media/webmediaplayer_impl.h
+++ b/webkit/media/webmediaplayer_impl.h
@@ -208,6 +208,8 @@ class WebMediaPlayerImpl
unsigned length);
virtual bool sourceAbort(const WebKit::WebString& id);
virtual void sourceEndOfStream(EndOfStreamStatus status);
+ virtual bool sourceTimestampOffset(
+ const WebKit::WebString& id, double offset);
virtual MediaKeyException generateKeyRequest(
const WebKit::WebString& key_system,
diff --git a/webkit/media/webmediaplayer_proxy.cc b/webkit/media/webmediaplayer_proxy.cc
index 2809e68..911c9bc 100644
--- a/webkit/media/webmediaplayer_proxy.cc
+++ b/webkit/media/webmediaplayer_proxy.cc
@@ -196,6 +196,11 @@ media::ChunkDemuxer::Status WebMediaPlayerProxy::DemuxerAddId(
return chunk_demuxer_->AddId(id, type, codecs);
}
+bool WebMediaPlayerProxy::DemuxerSetTimestampOffset(
+ const std::string& id, double offset) {
+ return chunk_demuxer_->SetTimestampOffset(id, offset);
+}
+
void WebMediaPlayerProxy::DemuxerRemoveId(const std::string& id) {
chunk_demuxer_->RemoveId(id);
}
diff --git a/webkit/media/webmediaplayer_proxy.h b/webkit/media/webmediaplayer_proxy.h
index dcca88d..7dedcdf 100644
--- a/webkit/media/webmediaplayer_proxy.h
+++ b/webkit/media/webmediaplayer_proxy.h
@@ -105,6 +105,7 @@ class WebMediaPlayerProxy
void DemuxerAbort(const std::string& id);
void DemuxerEndOfStream(media::PipelineStatus status);
void DemuxerShutdown();
+ bool DemuxerSetTimestampOffset(const std::string& id, double offset);
// DecryptorClient implementation.
virtual void KeyAdded(const std::string& key_system,