| Index: media/filters/chunk_demuxer.cc | 
| diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc | 
| index d641ded72c00e74a3fd48b9805c1e5f319cf9efa..7ad4c84a436e2695c55abba43d8a9c76f6fdf2ab 100644 | 
| --- a/media/filters/chunk_demuxer.cc | 
| +++ b/media/filters/chunk_demuxer.cc | 
| @@ -133,6 +133,14 @@ class SourceState { | 
| // ChunkDemuxerStreams managed by this object. | 
| void Remove(TimeDelta start, TimeDelta end, TimeDelta duration); | 
|  | 
| +  // Attempts to perform garbage collection in SourceBuffers associated with | 
| +  // this ChunkDemuxer. Returns true if garbage collection was successful. | 
| +  // |media_time| parameter should indicate the current playback position. | 
| +  // If it's a valid time value, the garbage collection algorithm will make sure | 
| +  // to preserve unconsumed data buffers in the range between last read position | 
| +  // and the current playback position. | 
| +  bool EvictFrames(DecodeTimestamp media_time); | 
| + | 
| // Returns true if currently parsing a media segment, or false otherwise. | 
| bool parsing_media_segment() const { return parsing_media_segment_; } | 
|  | 
| @@ -374,6 +382,23 @@ void SourceState::Remove(TimeDelta start, TimeDelta end, TimeDelta duration) { | 
| } | 
| } | 
|  | 
| +bool SourceState::EvictFrames(DecodeTimestamp media_time) { | 
| +  bool success = true; | 
| + | 
| +  if (audio_) | 
| +    success = audio_->EvictFrames(media_time) && success; | 
| + | 
| +  if (video_) | 
| +    success = video_->EvictFrames(media_time) && success; | 
| + | 
| +  for (TextStreamMap::iterator itr = text_stream_map_.begin(); | 
| +       itr != text_stream_map_.end(); ++itr) { | 
| +    success = itr->second->EvictFrames(media_time) && success; | 
| +  } | 
| + | 
| +  return success; | 
| +} | 
| + | 
| Ranges<TimeDelta> SourceState::GetBufferedRanges(TimeDelta duration, | 
| bool ended) const { | 
| // TODO(acolwell): When we start allowing disabled tracks we'll need to update | 
| @@ -883,6 +908,11 @@ void ChunkDemuxerStream::Remove(TimeDelta start, TimeDelta end, | 
| stream_->Remove(start, end, duration); | 
| } | 
|  | 
| +bool ChunkDemuxerStream::EvictFrames(DecodeTimestamp media_time) { | 
| +  base::AutoLock auto_lock(lock_); | 
| +  return stream_->GarbageCollectIfNeeded(media_time); | 
| +} | 
| + | 
| void ChunkDemuxerStream::OnSetDuration(TimeDelta duration) { | 
| base::AutoLock auto_lock(lock_); | 
| stream_->OnSetDuration(duration); | 
| @@ -1106,6 +1136,7 @@ ChunkDemuxer::ChunkDemuxer( | 
| const base::Closure& open_cb, | 
| const EncryptedMediaInitDataCB& encrypted_media_init_data_cb, | 
| const LogCB& log_cb, | 
| +    const GetMediaTimeCB& get_media_time_cb, | 
| const scoped_refptr<MediaLog>& media_log, | 
| bool splice_frames_enabled) | 
| : state_(WAITING_FOR_INIT), | 
| @@ -1115,6 +1146,7 @@ ChunkDemuxer::ChunkDemuxer( | 
| encrypted_media_init_data_cb_(encrypted_media_init_data_cb), | 
| enable_text_(false), | 
| log_cb_(log_cb), | 
| +      get_media_time_cb_(get_media_time_cb), | 
| media_log_(media_log), | 
| duration_(kNoTimestamp()), | 
| user_specified_duration_(-1), | 
| @@ -1317,6 +1349,23 @@ Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { | 
| return itr->second->GetBufferedRanges(duration_, state_ == ENDED); | 
| } | 
|  | 
| +bool ChunkDemuxer::EvictFrames() { | 
| +  DecodeTimestamp current_media_time; | 
| +  if (!get_media_time_cb_.is_null()) { | 
| +    current_media_time = | 
| +        DecodeTimestamp::FromPresentationTime(get_media_time_cb_.Run()); | 
| +  } else { | 
| +    current_media_time = kNoDecodeTimestamp(); | 
| +  } | 
| + | 
| +  bool success = true; | 
| +  for (SourceStateMap::iterator itr = source_state_map_.begin(); | 
| +       itr != source_state_map_.end(); ++itr) { | 
| +    success = itr->second->EvictFrames(current_media_time) && success; | 
| +  } | 
| +  return success; | 
| +} | 
| + | 
| void ChunkDemuxer::AppendData( | 
| const std::string& id, | 
| const uint8* data, | 
|  |