Home | History | Annotate | Download | only in filters
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "media/filters/chunk_demuxer.h"
      6 
      7 #include <algorithm>
      8 #include <limits>
      9 #include <list>
     10 
     11 #include "base/bind.h"
     12 #include "base/callback_helpers.h"
     13 #include "base/location.h"
     14 #include "base/message_loop/message_loop_proxy.h"
     15 #include "base/stl_util.h"
     16 #include "media/base/audio_decoder_config.h"
     17 #include "media/base/bind_to_current_loop.h"
     18 #include "media/base/stream_parser_buffer.h"
     19 #include "media/base/video_decoder_config.h"
     20 #include "media/filters/frame_processor.h"
     21 #include "media/filters/stream_parser_factory.h"
     22 
     23 using base::TimeDelta;
     24 
     25 namespace media {
     26 
     27 static TimeDelta EndTimestamp(const StreamParser::BufferQueue& queue) {
     28   return queue.back()->timestamp() + queue.back()->duration();
     29 }
     30 
     31 // List of time ranges for each SourceBuffer.
     32 typedef std::list<Ranges<TimeDelta> > RangesList;
     33 static Ranges<TimeDelta> ComputeIntersection(const RangesList& activeRanges,
     34                                              bool ended) {
     35   // Implementation of HTMLMediaElement.buffered algorithm in MSE spec.
     36   // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#dom-htmlmediaelement.buffered
     37 
     38   // Step 1: If activeSourceBuffers.length equals 0 then return an empty
     39   //  TimeRanges object and abort these steps.
     40   if (activeRanges.empty())
     41     return Ranges<TimeDelta>();
     42 
     43   // Step 2: Let active ranges be the ranges returned by buffered for each
     44   //  SourceBuffer object in activeSourceBuffers.
     45   // Step 3: Let highest end time be the largest range end time in the active
     46   //  ranges.
     47   TimeDelta highest_end_time;
     48   for (RangesList::const_iterator itr = activeRanges.begin();
     49        itr != activeRanges.end(); ++itr) {
     50     if (!itr->size())
     51       continue;
     52 
     53     highest_end_time = std::max(highest_end_time, itr->end(itr->size() - 1));
     54   }
     55 
     56   // Step 4: Let intersection ranges equal a TimeRange object containing a
     57   //  single range from 0 to highest end time.
     58   Ranges<TimeDelta> intersection_ranges;
     59   intersection_ranges.Add(TimeDelta(), highest_end_time);
     60 
     61   // Step 5: For each SourceBuffer object in activeSourceBuffers run the
     62   //  following steps:
     63   for (RangesList::const_iterator itr = activeRanges.begin();
     64        itr != activeRanges.end(); ++itr) {
     65     // Step 5.1: Let source ranges equal the ranges returned by the buffered
     66     //  attribute on the current SourceBuffer.
     67     Ranges<TimeDelta> source_ranges = *itr;
     68 
     69     // Step 5.2: If readyState is "ended", then set the end time on the last
     70     //  range in source ranges to highest end time.
     71     if (ended && source_ranges.size() > 0u) {
     72       source_ranges.Add(source_ranges.start(source_ranges.size() - 1),
     73                         highest_end_time);
     74     }
     75 
     76     // Step 5.3: Let new intersection ranges equal the intersection between
     77     // the intersection ranges and the source ranges.
     78     // Step 5.4: Replace the ranges in intersection ranges with the new
     79     // intersection ranges.
     80     intersection_ranges = intersection_ranges.IntersectionWith(source_ranges);
     81   }
     82 
     83   return intersection_ranges;
     84 }
     85 
     86 // Contains state belonging to a source id.
     87 class SourceState {
     88  public:
     89   // Callback signature used to create ChunkDemuxerStreams.
     90   typedef base::Callback<ChunkDemuxerStream*(
     91       DemuxerStream::Type)> CreateDemuxerStreamCB;
     92 
     93   typedef ChunkDemuxer::InitSegmentReceivedCB InitSegmentReceivedCB;
     94 
     95   typedef base::Callback<void(
     96       ChunkDemuxerStream*, const TextTrackConfig&)> NewTextTrackCB;
     97 
     98   SourceState(
     99       scoped_ptr<StreamParser> stream_parser,
    100       scoped_ptr<FrameProcessor> frame_processor, const LogCB& log_cb,
    101       const CreateDemuxerStreamCB& create_demuxer_stream_cb);
    102 
    103   ~SourceState();
    104 
    105   void Init(const StreamParser::InitCB& init_cb,
    106             bool allow_audio,
    107             bool allow_video,
    108             const StreamParser::NeedKeyCB& need_key_cb,
    109             const NewTextTrackCB& new_text_track_cb);
    110 
    111   // Appends new data to the StreamParser.
    112   // Returns true if the data was successfully appended. Returns false if an
    113   // error occurred. |*timestamp_offset| is used and possibly updated by the
    114   // append. |append_window_start| and |append_window_end| correspond to the MSE
    115   // spec's similarly named source buffer attributes that are used in coded
    116   // frame processing. |init_segment_received_cb| is run for each new fully
    117   // parsed initialization segment.
    118   bool Append(const uint8* data,
    119               size_t length,
    120               TimeDelta append_window_start,
    121               TimeDelta append_window_end,
    122               TimeDelta* timestamp_offset,
    123               const InitSegmentReceivedCB& init_segment_received_cb);
    124 
    125   // Aborts the current append sequence and resets the parser.
    126   void Abort(TimeDelta append_window_start,
    127              TimeDelta append_window_end,
    128              TimeDelta* timestamp_offset);
    129 
    130   // Calls Remove(|start|, |end|, |duration|) on all
    131   // ChunkDemuxerStreams managed by this object.
    132   void Remove(TimeDelta start, TimeDelta end, TimeDelta duration);
    133 
    134   // Returns true if currently parsing a media segment, or false otherwise.
    135   bool parsing_media_segment() const { return parsing_media_segment_; }
    136 
    137   // Sets |frame_processor_|'s sequence mode to |sequence_mode|.
    138   void SetSequenceMode(bool sequence_mode);
    139 
    140   // Signals the coded frame processor to update its group start timestamp to be
    141   // |timestamp_offset| if it is in sequence append mode.
    142   void SetGroupStartTimestampIfInSequenceMode(base::TimeDelta timestamp_offset);
    143 
    144   // Returns the range of buffered data in this source, capped at |duration|.
    145   // |ended| - Set to true if end of stream has been signaled and the special
    146   // end of stream range logic needs to be executed.
    147   Ranges<TimeDelta> GetBufferedRanges(TimeDelta duration, bool ended) const;
    148 
    149   // Returns the highest buffered duration across all streams managed
    150   // by this object.
    151   // Returns TimeDelta() if none of the streams contain buffered data.
    152   TimeDelta GetMaxBufferedDuration() const;
    153 
    154   // Helper methods that call methods with similar names on all the
    155   // ChunkDemuxerStreams managed by this object.
    156   void StartReturningData();
    157   void AbortReads();
    158   void Seek(TimeDelta seek_time);
    159   void CompletePendingReadIfPossible();
    160   void OnSetDuration(TimeDelta duration);
    161   void MarkEndOfStream();
    162   void UnmarkEndOfStream();
    163   void Shutdown();
    164   // Sets the memory limit on each stream of a specific type.
    165   // |memory_limit| is the maximum number of bytes each stream of type |type|
    166   // is allowed to hold in its buffer.
    167   void SetMemoryLimits(DemuxerStream::Type type, int memory_limit);
    168   bool IsSeekWaitingForData() const;
    169 
    170  private:
    171   // Called by the |stream_parser_| when a new initialization segment is
    172   // encountered.
    173   // Returns true on a successful call. Returns false if an error occurred while
    174   // processing decoder configurations.
    175   bool OnNewConfigs(bool allow_audio, bool allow_video,
    176                     const AudioDecoderConfig& audio_config,
    177                     const VideoDecoderConfig& video_config,
    178                     const StreamParser::TextTrackConfigMap& text_configs);
    179 
    180   // Called by the |stream_parser_| at the beginning of a new media segment.
    181   void OnNewMediaSegment();
    182 
    183   // Called by the |stream_parser_| at the end of a media segment.
    184   void OnEndOfMediaSegment();
    185 
    186   // Called by the |stream_parser_| when new buffers have been parsed.
    187   // It processes the new buffers using |frame_processor_|, which includes
    188   // appending the processed frames to associated demuxer streams for each
    189   // frame's track.
    190   // Returns true on a successful call. Returns false if an error occurred while
    191   // processing the buffers.
    192   bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers,
    193                     const StreamParser::BufferQueue& video_buffers,
    194                     const StreamParser::TextBufferQueueMap& text_map);
    195 
    196   void OnSourceInitDone(bool success,
    197                         const StreamParser::InitParameters& params);
    198 
    199   CreateDemuxerStreamCB create_demuxer_stream_cb_;
    200   NewTextTrackCB new_text_track_cb_;
    201 
    202   // During Append(), if OnNewBuffers() coded frame processing updates the
    203   // timestamp offset then |*timestamp_offset_during_append_| is also updated
    204   // so Append()'s caller can know the new offset. This pointer is only non-NULL
    205   // during the lifetime of an Append() call.
    206   TimeDelta* timestamp_offset_during_append_;
    207 
    208   // During Append(), coded frame processing triggered by OnNewBuffers()
    209   // requires these two attributes. These are only valid during the lifetime of
    210   // an Append() call.
    211   TimeDelta append_window_start_during_append_;
    212   TimeDelta append_window_end_during_append_;
    213 
    214   // Set to true if the next buffers appended within the append window
    215   // represent the start of a new media segment. This flag being set
    216   // triggers a call to |new_segment_cb_| when the new buffers are
    217   // appended. The flag is set on actual media segment boundaries and
    218   // when the "append window" filtering causes discontinuities in the
    219   // appended data.
    220   // TODO(wolenetz/acolwell): Investigate if we need this, or if coded frame
    221   // processing's discontinuity logic is enough. See http://crbug.com/351489.
    222   bool new_media_segment_;
    223 
    224   // Keeps track of whether a media segment is being parsed.
    225   bool parsing_media_segment_;
    226 
    227   // The object used to parse appended data.
    228   scoped_ptr<StreamParser> stream_parser_;
    229 
    230   ChunkDemuxerStream* audio_;  // Not owned by |this|.
    231   ChunkDemuxerStream* video_;  // Not owned by |this|.
    232 
    233   typedef std::map<StreamParser::TrackId, ChunkDemuxerStream*> TextStreamMap;
    234   TextStreamMap text_stream_map_;  // |this| owns the map's stream pointers.
    235 
    236   scoped_ptr<FrameProcessor> frame_processor_;
    237   LogCB log_cb_;
    238   StreamParser::InitCB init_cb_;
    239 
    240   // During Append(), OnNewConfigs() will trigger the initialization segment
    241   // received algorithm. This callback is only non-NULL during the lifetime of
    242   // an Append() call. Note, the MSE spec explicitly disallows this algorithm
    243   // during an Abort(), since Abort() is allowed only to emit coded frames, and
    244   // only if the parser is PARSING_MEDIA_SEGMENT (not an INIT segment).
    245   InitSegmentReceivedCB init_segment_received_cb_;
    246 
    247   // Indicates that timestampOffset should be updated automatically during
    248   // OnNewBuffers() based on the earliest end timestamp of the buffers provided.
    249   // TODO(wolenetz): Refactor this function while integrating April 29, 2014
    250   // changes to MSE spec. See http://crbug.com/371499.
    251   bool auto_update_timestamp_offset_;
    252 
    253   DISALLOW_COPY_AND_ASSIGN(SourceState);
    254 };
    255 
    256 SourceState::SourceState(scoped_ptr<StreamParser> stream_parser,
    257                          scoped_ptr<FrameProcessor> frame_processor,
    258                          const LogCB& log_cb,
    259                          const CreateDemuxerStreamCB& create_demuxer_stream_cb)
    260     : create_demuxer_stream_cb_(create_demuxer_stream_cb),
    261       timestamp_offset_during_append_(NULL),
    262       new_media_segment_(false),
    263       parsing_media_segment_(false),
    264       stream_parser_(stream_parser.release()),
    265       audio_(NULL),
    266       video_(NULL),
    267       frame_processor_(frame_processor.release()),
    268       log_cb_(log_cb),
    269       auto_update_timestamp_offset_(false) {
    270   DCHECK(!create_demuxer_stream_cb_.is_null());
    271   DCHECK(frame_processor_);
    272 }
    273 
    274 SourceState::~SourceState() {
    275   Shutdown();
    276 
    277   STLDeleteValues(&text_stream_map_);
    278 }
    279 
    280 void SourceState::Init(const StreamParser::InitCB& init_cb,
    281                        bool allow_audio,
    282                        bool allow_video,
    283                        const StreamParser::NeedKeyCB& need_key_cb,
    284                        const NewTextTrackCB& new_text_track_cb) {
    285   new_text_track_cb_ = new_text_track_cb;
    286   init_cb_ = init_cb;
    287 
    288   stream_parser_->Init(
    289       base::Bind(&SourceState::OnSourceInitDone, base::Unretained(this)),
    290       base::Bind(&SourceState::OnNewConfigs,
    291                  base::Unretained(this),
    292                  allow_audio,
    293                  allow_video),
    294       base::Bind(&SourceState::OnNewBuffers, base::Unretained(this)),
    295       new_text_track_cb_.is_null(),
    296       need_key_cb,
    297       base::Bind(&SourceState::OnNewMediaSegment, base::Unretained(this)),
    298       base::Bind(&SourceState::OnEndOfMediaSegment, base::Unretained(this)),
    299       log_cb_);
    300 }
    301 
    302 void SourceState::SetSequenceMode(bool sequence_mode) {
    303   DCHECK(!parsing_media_segment_);
    304 
    305   frame_processor_->SetSequenceMode(sequence_mode);
    306 }
    307 
    308 void SourceState::SetGroupStartTimestampIfInSequenceMode(
    309     base::TimeDelta timestamp_offset) {
    310   DCHECK(!parsing_media_segment_);
    311 
    312   frame_processor_->SetGroupStartTimestampIfInSequenceMode(timestamp_offset);
    313 }
    314 
    315 bool SourceState::Append(
    316     const uint8* data,
    317     size_t length,
    318     TimeDelta append_window_start,
    319     TimeDelta append_window_end,
    320     TimeDelta* timestamp_offset,
    321     const InitSegmentReceivedCB& init_segment_received_cb) {
    322   DCHECK(timestamp_offset);
    323   DCHECK(!timestamp_offset_during_append_);
    324   DCHECK(!init_segment_received_cb.is_null());
    325   DCHECK(init_segment_received_cb_.is_null());
    326   append_window_start_during_append_ = append_window_start;
    327   append_window_end_during_append_ = append_window_end;
    328   timestamp_offset_during_append_ = timestamp_offset;
    329   init_segment_received_cb_= init_segment_received_cb;
    330 
    331   // TODO(wolenetz/acolwell): Curry and pass a NewBuffersCB here bound with
    332   // append window and timestamp offset pointer. See http://crbug.com/351454.
    333   bool err = stream_parser_->Parse(data, length);
    334   timestamp_offset_during_append_ = NULL;
    335   init_segment_received_cb_.Reset();
    336   return err;
    337 }
    338 
    339 void SourceState::Abort(TimeDelta append_window_start,
    340                         TimeDelta append_window_end,
    341                         base::TimeDelta* timestamp_offset) {
    342   DCHECK(timestamp_offset);
    343   DCHECK(!timestamp_offset_during_append_);
    344   timestamp_offset_during_append_ = timestamp_offset;
    345   append_window_start_during_append_ = append_window_start;
    346   append_window_end_during_append_ = append_window_end;
    347 
    348   stream_parser_->Flush();
    349   timestamp_offset_during_append_ = NULL;
    350 
    351   frame_processor_->Reset();
    352   parsing_media_segment_ = false;
    353 }
    354 
    355 void SourceState::Remove(TimeDelta start, TimeDelta end, TimeDelta duration) {
    356   if (audio_)
    357     audio_->Remove(start, end, duration);
    358 
    359   if (video_)
    360     video_->Remove(start, end, duration);
    361 
    362   for (TextStreamMap::iterator itr = text_stream_map_.begin();
    363        itr != text_stream_map_.end(); ++itr) {
    364     itr->second->Remove(start, end, duration);
    365   }
    366 }
    367 
    368 Ranges<TimeDelta> SourceState::GetBufferedRanges(TimeDelta duration,
    369                                                  bool ended) const {
    370   // TODO(acolwell): When we start allowing disabled tracks we'll need to update
    371   // this code to only add ranges from active tracks.
    372   RangesList ranges_list;
    373   if (audio_)
    374     ranges_list.push_back(audio_->GetBufferedRanges(duration));
    375 
    376   if (video_)
    377     ranges_list.push_back(video_->GetBufferedRanges(duration));
    378 
    379   for (TextStreamMap::const_iterator itr = text_stream_map_.begin();
    380        itr != text_stream_map_.end(); ++itr) {
    381     ranges_list.push_back(itr->second->GetBufferedRanges(duration));
    382   }
    383 
    384   return ComputeIntersection(ranges_list, ended);
    385 }
    386 
    387 TimeDelta SourceState::GetMaxBufferedDuration() const {
    388   TimeDelta max_duration;
    389 
    390   if (audio_)
    391     max_duration = std::max(max_duration, audio_->GetBufferedDuration());
    392 
    393   if (video_)
    394     max_duration = std::max(max_duration, video_->GetBufferedDuration());
    395 
    396   for (TextStreamMap::const_iterator itr = text_stream_map_.begin();
    397        itr != text_stream_map_.end(); ++itr) {
    398     max_duration = std::max(max_duration, itr->second->GetBufferedDuration());
    399   }
    400 
    401   return max_duration;
    402 }
    403 
    404 void SourceState::StartReturningData() {
    405   if (audio_)
    406     audio_->StartReturningData();
    407 
    408   if (video_)
    409     video_->StartReturningData();
    410 
    411   for (TextStreamMap::iterator itr = text_stream_map_.begin();
    412        itr != text_stream_map_.end(); ++itr) {
    413     itr->second->StartReturningData();
    414   }
    415 }
    416 
    417 void SourceState::AbortReads() {
    418   if (audio_)
    419     audio_->AbortReads();
    420 
    421   if (video_)
    422     video_->AbortReads();
    423 
    424   for (TextStreamMap::iterator itr = text_stream_map_.begin();
    425        itr != text_stream_map_.end(); ++itr) {
    426     itr->second->AbortReads();
    427   }
    428 }
    429 
    430 void SourceState::Seek(TimeDelta seek_time) {
    431   if (audio_)
    432     audio_->Seek(seek_time);
    433 
    434   if (video_)
    435     video_->Seek(seek_time);
    436 
    437   for (TextStreamMap::iterator itr = text_stream_map_.begin();
    438        itr != text_stream_map_.end(); ++itr) {
    439     itr->second->Seek(seek_time);
    440   }
    441 }
    442 
    443 void SourceState::CompletePendingReadIfPossible() {
    444   if (audio_)
    445     audio_->CompletePendingReadIfPossible();
    446 
    447   if (video_)
    448     video_->CompletePendingReadIfPossible();
    449 
    450   for (TextStreamMap::iterator itr = text_stream_map_.begin();
    451        itr != text_stream_map_.end(); ++itr) {
    452     itr->second->CompletePendingReadIfPossible();
    453   }
    454 }
    455 
    456 void SourceState::OnSetDuration(TimeDelta duration) {
    457   if (audio_)
    458     audio_->OnSetDuration(duration);
    459 
    460   if (video_)
    461     video_->OnSetDuration(duration);
    462 
    463   for (TextStreamMap::iterator itr = text_stream_map_.begin();
    464        itr != text_stream_map_.end(); ++itr) {
    465     itr->second->OnSetDuration(duration);
    466   }
    467 }
    468 
    469 void SourceState::MarkEndOfStream() {
    470   if (audio_)
    471     audio_->MarkEndOfStream();
    472 
    473   if (video_)
    474     video_->MarkEndOfStream();
    475 
    476   for (TextStreamMap::iterator itr = text_stream_map_.begin();
    477        itr != text_stream_map_.end(); ++itr) {
    478     itr->second->MarkEndOfStream();
    479   }
    480 }
    481 
    482 void SourceState::UnmarkEndOfStream() {
    483   if (audio_)
    484     audio_->UnmarkEndOfStream();
    485 
    486   if (video_)
    487     video_->UnmarkEndOfStream();
    488 
    489   for (TextStreamMap::iterator itr = text_stream_map_.begin();
    490        itr != text_stream_map_.end(); ++itr) {
    491     itr->second->UnmarkEndOfStream();
    492   }
    493 }
    494 
    495 void SourceState::Shutdown() {
    496   if (audio_)
    497     audio_->Shutdown();
    498 
    499   if (video_)
    500     video_->Shutdown();
    501 
    502   for (TextStreamMap::iterator itr = text_stream_map_.begin();
    503        itr != text_stream_map_.end(); ++itr) {
    504     itr->second->Shutdown();
    505   }
    506 }
    507 
    508 void SourceState::SetMemoryLimits(DemuxerStream::Type type, int memory_limit) {
    509   switch (type) {
    510     case DemuxerStream::AUDIO:
    511       if (audio_)
    512         audio_->set_memory_limit(memory_limit);
    513       break;
    514     case DemuxerStream::VIDEO:
    515       if (video_)
    516         video_->set_memory_limit(memory_limit);
    517       break;
    518     case DemuxerStream::TEXT:
    519       for (TextStreamMap::iterator itr = text_stream_map_.begin();
    520            itr != text_stream_map_.end(); ++itr) {
    521         itr->second->set_memory_limit(memory_limit);
    522       }
    523       break;
    524     case DemuxerStream::UNKNOWN:
    525     case DemuxerStream::NUM_TYPES:
    526       NOTREACHED();
    527       break;
    528   }
    529 }
    530 
    531 bool SourceState::IsSeekWaitingForData() const {
    532   if (audio_ && audio_->IsSeekWaitingForData())
    533     return true;
    534 
    535   if (video_ && video_->IsSeekWaitingForData())
    536     return true;
    537 
    538   // NOTE: We are intentionally not checking the text tracks
    539   // because text tracks are discontinuous and may not have data
    540   // for the seek position. This is ok and playback should not be
    541   // stalled because we don't have cues. If cues, with timestamps after
    542   // the seek time, eventually arrive they will be delivered properly
    543   // in response to ChunkDemuxerStream::Read() calls.
    544 
    545   return false;
    546 }
    547 
    548 bool SourceState::OnNewConfigs(
    549     bool allow_audio, bool allow_video,
    550     const AudioDecoderConfig& audio_config,
    551     const VideoDecoderConfig& video_config,
    552     const StreamParser::TextTrackConfigMap& text_configs) {
    553   DVLOG(1) << "OnNewConfigs(" << allow_audio << ", " << allow_video
    554            << ", " << audio_config.IsValidConfig()
    555            << ", " << video_config.IsValidConfig() << ")";
    556   DCHECK(!init_segment_received_cb_.is_null());
    557 
    558   if (!audio_config.IsValidConfig() && !video_config.IsValidConfig()) {
    559     DVLOG(1) << "OnNewConfigs() : Audio & video config are not valid!";
    560     return false;
    561   }
    562 
    563   // Signal an error if we get configuration info for stream types that weren't
    564   // specified in AddId() or more configs after a stream is initialized.
    565   if (allow_audio != audio_config.IsValidConfig()) {
    566     MEDIA_LOG(log_cb_)
    567         << "Initialization segment"
    568         << (audio_config.IsValidConfig() ? " has" : " does not have")
    569         << " an audio track, but the mimetype"
    570         << (allow_audio ? " specifies" : " does not specify")
    571         << " an audio codec.";
    572     return false;
    573   }
    574 
    575   if (allow_video != video_config.IsValidConfig()) {
    576     MEDIA_LOG(log_cb_)
    577         << "Initialization segment"
    578         << (video_config.IsValidConfig() ? " has" : " does not have")
    579         << " a video track, but the mimetype"
    580         << (allow_video ? " specifies" : " does not specify")
    581         << " a video codec.";
    582     return false;
    583   }
    584 
    585   bool success = true;
    586   if (audio_config.IsValidConfig()) {
    587     if (!audio_) {
    588       audio_ = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO);
    589 
    590       if (!audio_) {
    591         DVLOG(1) << "Failed to create an audio stream.";
    592         return false;
    593       }
    594 
    595       if (!frame_processor_->AddTrack(FrameProcessor::kAudioTrackId, audio_)) {
    596         DVLOG(1) << "Failed to add audio track to frame processor.";
    597         return false;
    598       }
    599     }
    600 
    601     frame_processor_->OnPossibleAudioConfigUpdate(audio_config);
    602     success &= audio_->UpdateAudioConfig(audio_config, log_cb_);
    603   }
    604 
    605   if (video_config.IsValidConfig()) {
    606     if (!video_) {
    607       video_ = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO);
    608 
    609       if (!video_) {
    610         DVLOG(1) << "Failed to create a video stream.";
    611         return false;
    612       }
    613 
    614       if (!frame_processor_->AddTrack(FrameProcessor::kVideoTrackId, video_)) {
    615         DVLOG(1) << "Failed to add video track to frame processor.";
    616         return false;
    617       }
    618     }
    619 
    620     success &= video_->UpdateVideoConfig(video_config, log_cb_);
    621   }
    622 
    623   typedef StreamParser::TextTrackConfigMap::const_iterator TextConfigItr;
    624   if (text_stream_map_.empty()) {
    625     for (TextConfigItr itr = text_configs.begin();
    626          itr != text_configs.end(); ++itr) {
    627       ChunkDemuxerStream* const text_stream =
    628           create_demuxer_stream_cb_.Run(DemuxerStream::TEXT);
    629       if (!frame_processor_->AddTrack(itr->first, text_stream)) {
    630         success &= false;
    631         MEDIA_LOG(log_cb_) << "Failed to add text track ID " << itr->first
    632                            << " to frame processor.";
    633         break;
    634       }
    635       text_stream->UpdateTextConfig(itr->second, log_cb_);
    636       text_stream_map_[itr->first] = text_stream;
    637       new_text_track_cb_.Run(text_stream, itr->second);
    638     }
    639   } else {
    640     const size_t text_count = text_stream_map_.size();
    641     if (text_configs.size() != text_count) {
    642       success &= false;
    643       MEDIA_LOG(log_cb_) << "The number of text track configs changed.";
    644     } else if (text_count == 1) {
    645       TextConfigItr config_itr = text_configs.begin();
    646       TextStreamMap::iterator stream_itr = text_stream_map_.begin();
    647       ChunkDemuxerStream* text_stream = stream_itr->second;
    648       TextTrackConfig old_config = text_stream->text_track_config();
    649       TextTrackConfig new_config(config_itr->second.kind(),
    650                                  config_itr->second.label(),
    651                                  config_itr->second.language(),
    652                                  old_config.id());
    653       if (!new_config.Matches(old_config)) {
    654         success &= false;
    655         MEDIA_LOG(log_cb_) << "New text track config does not match old one.";
    656       } else {
    657         StreamParser::TrackId old_id = stream_itr->first;
    658         StreamParser::TrackId new_id = config_itr->first;
    659         if (new_id != old_id) {
    660           if (frame_processor_->UpdateTrack(old_id, new_id)) {
    661             text_stream_map_.clear();
    662             text_stream_map_[config_itr->first] = text_stream;
    663           } else {
    664             success &= false;
    665             MEDIA_LOG(log_cb_) << "Error remapping single text track number";
    666           }
    667         }
    668       }
    669     } else {
    670       for (TextConfigItr config_itr = text_configs.begin();
    671            config_itr != text_configs.end(); ++config_itr) {
    672         TextStreamMap::iterator stream_itr =
    673             text_stream_map_.find(config_itr->first);
    674         if (stream_itr == text_stream_map_.end()) {
    675           success &= false;
    676           MEDIA_LOG(log_cb_) << "Unexpected text track configuration "
    677                                 "for track ID "
    678                              << config_itr->first;
    679           break;
    680         }
    681 
    682         const TextTrackConfig& new_config = config_itr->second;
    683         ChunkDemuxerStream* stream = stream_itr->second;
    684         TextTrackConfig old_config = stream->text_track_config();
    685         if (!new_config.Matches(old_config)) {
    686           success &= false;
    687           MEDIA_LOG(log_cb_) << "New text track config for track ID "
    688                              << config_itr->first
    689                              << " does not match old one.";
    690           break;
    691         }
    692       }
    693     }
    694   }
    695 
    696   frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint();
    697 
    698   DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed");
    699   if (success)
    700     init_segment_received_cb_.Run();
    701 
    702   return success;
    703 }
    704 
    705 void SourceState::OnNewMediaSegment() {
    706   DVLOG(2) << "OnNewMediaSegment()";
    707   parsing_media_segment_ = true;
    708   new_media_segment_ = true;
    709 }
    710 
    711 void SourceState::OnEndOfMediaSegment() {
    712   DVLOG(2) << "OnEndOfMediaSegment()";
    713   parsing_media_segment_ = false;
    714   new_media_segment_ = false;
    715 }
    716 
    717 bool SourceState::OnNewBuffers(
    718     const StreamParser::BufferQueue& audio_buffers,
    719     const StreamParser::BufferQueue& video_buffers,
    720     const StreamParser::TextBufferQueueMap& text_map) {
    721   DVLOG(2) << "OnNewBuffers()";
    722   DCHECK(timestamp_offset_during_append_);
    723   DCHECK(parsing_media_segment_);
    724 
    725   const TimeDelta timestamp_offset_before_processing =
    726       *timestamp_offset_during_append_;
    727 
    728   // Calculate the new timestamp offset for audio/video tracks if the stream
    729   // parser has requested automatic updates.
    730   TimeDelta new_timestamp_offset = timestamp_offset_before_processing;
    731   if (auto_update_timestamp_offset_) {
    732     const bool have_audio_buffers = !audio_buffers.empty();
    733     const bool have_video_buffers = !video_buffers.empty();
    734     if (have_audio_buffers && have_video_buffers) {
    735       new_timestamp_offset +=
    736           std::min(EndTimestamp(audio_buffers), EndTimestamp(video_buffers));
    737     } else if (have_audio_buffers) {
    738       new_timestamp_offset += EndTimestamp(audio_buffers);
    739     } else if (have_video_buffers) {
    740       new_timestamp_offset += EndTimestamp(video_buffers);
    741     }
    742   }
    743 
    744   if (!frame_processor_->ProcessFrames(audio_buffers,
    745                                        video_buffers,
    746                                        text_map,
    747                                        append_window_start_during_append_,
    748                                        append_window_end_during_append_,
    749                                        &new_media_segment_,
    750                                        timestamp_offset_during_append_)) {
    751     return false;
    752   }
    753 
    754   // Only update the timestamp offset if the frame processor hasn't already.
    755   if (auto_update_timestamp_offset_ &&
    756       timestamp_offset_before_processing == *timestamp_offset_during_append_) {
    757     *timestamp_offset_during_append_ = new_timestamp_offset;
    758   }
    759 
    760   return true;
    761 }
    762 
    763 void SourceState::OnSourceInitDone(bool success,
    764                                    const StreamParser::InitParameters& params) {
    765   auto_update_timestamp_offset_ = params.auto_update_timestamp_offset;
    766   base::ResetAndReturn(&init_cb_).Run(success, params);
    767 }
    768 
    769 ChunkDemuxerStream::ChunkDemuxerStream(Type type, bool splice_frames_enabled)
    770     : type_(type),
    771       state_(UNINITIALIZED),
    772       splice_frames_enabled_(splice_frames_enabled),
    773       partial_append_window_trimming_enabled_(false) {
    774 }
    775 
    776 void ChunkDemuxerStream::StartReturningData() {
    777   DVLOG(1) << "ChunkDemuxerStream::StartReturningData()";
    778   base::AutoLock auto_lock(lock_);
    779   DCHECK(read_cb_.is_null());
    780   ChangeState_Locked(RETURNING_DATA_FOR_READS);
    781 }
    782 
    783 void ChunkDemuxerStream::AbortReads() {
    784   DVLOG(1) << "ChunkDemuxerStream::AbortReads()";
    785   base::AutoLock auto_lock(lock_);
    786   ChangeState_Locked(RETURNING_ABORT_FOR_READS);
    787   if (!read_cb_.is_null())
    788     base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
    789 }
    790 
    791 void ChunkDemuxerStream::CompletePendingReadIfPossible() {
    792   base::AutoLock auto_lock(lock_);
    793   if (read_cb_.is_null())
    794     return;
    795 
    796   CompletePendingReadIfPossible_Locked();
    797 }
    798 
    799 void ChunkDemuxerStream::Shutdown() {
    800   DVLOG(1) << "ChunkDemuxerStream::Shutdown()";
    801   base::AutoLock auto_lock(lock_);
    802   ChangeState_Locked(SHUTDOWN);
    803 
    804   // Pass an end of stream buffer to the pending callback to signal that no more
    805   // data will be sent.
    806   if (!read_cb_.is_null()) {
    807     base::ResetAndReturn(&read_cb_).Run(DemuxerStream::kOk,
    808                                         StreamParserBuffer::CreateEOSBuffer());
    809   }
    810 }
    811 
    812 bool ChunkDemuxerStream::IsSeekWaitingForData() const {
    813   base::AutoLock auto_lock(lock_);
    814 
    815   // This method should not be called for text tracks. See the note in
    816   // SourceState::IsSeekWaitingForData().
    817   DCHECK_NE(type_, DemuxerStream::TEXT);
    818 
    819   return stream_->IsSeekPending();
    820 }
    821 
    822 void ChunkDemuxerStream::Seek(TimeDelta time) {
    823   DVLOG(1) << "ChunkDemuxerStream::Seek(" << time.InSecondsF() << ")";
    824   base::AutoLock auto_lock(lock_);
    825   DCHECK(read_cb_.is_null());
    826   DCHECK(state_ == UNINITIALIZED || state_ == RETURNING_ABORT_FOR_READS)
    827       << state_;
    828 
    829   stream_->Seek(time);
    830 }
    831 
    832 bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) {
    833   if (buffers.empty())
    834     return false;
    835 
    836   base::AutoLock auto_lock(lock_);
    837   DCHECK_NE(state_, SHUTDOWN);
    838   if (!stream_->Append(buffers)) {
    839     DVLOG(1) << "ChunkDemuxerStream::Append() : stream append failed";
    840     return false;
    841   }
    842 
    843   if (!read_cb_.is_null())
    844     CompletePendingReadIfPossible_Locked();
    845 
    846   return true;
    847 }
    848 
    849 void ChunkDemuxerStream::Remove(TimeDelta start, TimeDelta end,
    850                                 TimeDelta duration) {
    851   base::AutoLock auto_lock(lock_);
    852   stream_->Remove(start, end, duration);
    853 }
    854 
    855 void ChunkDemuxerStream::OnSetDuration(TimeDelta duration) {
    856   base::AutoLock auto_lock(lock_);
    857   stream_->OnSetDuration(duration);
    858 }
    859 
    860 Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges(
    861     TimeDelta duration) const {
    862   base::AutoLock auto_lock(lock_);
    863 
    864   if (type_ == TEXT) {
    865     // Since text tracks are discontinuous and the lack of cues should not block
    866     // playback, report the buffered range for text tracks as [0, |duration|) so
    867     // that intesections with audio & video tracks are computed correctly when
    868     // no cues are present.
    869     Ranges<TimeDelta> text_range;
    870     text_range.Add(TimeDelta(), duration);
    871     return text_range;
    872   }
    873 
    874   Ranges<TimeDelta> range = stream_->GetBufferedTime();
    875 
    876   if (range.size() == 0u)
    877     return range;
    878 
    879   // Clamp the end of the stream's buffered ranges to fit within the duration.
    880   // This can be done by intersecting the stream's range with the valid time
    881   // range.
    882   Ranges<TimeDelta> valid_time_range;
    883   valid_time_range.Add(range.start(0), duration);
    884   return range.IntersectionWith(valid_time_range);
    885 }
    886 
    887 TimeDelta ChunkDemuxerStream::GetBufferedDuration() const {
    888   return stream_->GetBufferedDuration();
    889 }
    890 
    891 void ChunkDemuxerStream::OnNewMediaSegment(DecodeTimestamp start_timestamp) {
    892   DVLOG(2) << "ChunkDemuxerStream::OnNewMediaSegment("
    893            << start_timestamp.InSecondsF() << ")";
    894   base::AutoLock auto_lock(lock_);
    895   stream_->OnNewMediaSegment(start_timestamp);
    896 }
    897 
    898 bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config,
    899                                            const LogCB& log_cb) {
    900   DCHECK(config.IsValidConfig());
    901   DCHECK_EQ(type_, AUDIO);
    902   base::AutoLock auto_lock(lock_);
    903   if (!stream_) {
    904     DCHECK_EQ(state_, UNINITIALIZED);
    905 
    906     // On platforms which support splice frames, enable splice frames and
    907     // partial append window support for most codecs (notably: not opus).
    908     const bool codec_supported = config.codec() == kCodecMP3 ||
    909                                  config.codec() == kCodecAAC ||
    910                                  config.codec() == kCodecVorbis;
    911     splice_frames_enabled_ = splice_frames_enabled_ && codec_supported;
    912     partial_append_window_trimming_enabled_ =
    913         splice_frames_enabled_ && codec_supported;
    914 
    915     stream_.reset(
    916         new SourceBufferStream(config, log_cb, splice_frames_enabled_));
    917     return true;
    918   }
    919 
    920   return stream_->UpdateAudioConfig(config);
    921 }
    922 
    923 bool ChunkDemuxerStream::UpdateVideoConfig(const VideoDecoderConfig& config,
    924                                            const LogCB& log_cb) {
    925   DCHECK(config.IsValidConfig());
    926   DCHECK_EQ(type_, VIDEO);
    927   base::AutoLock auto_lock(lock_);
    928 
    929   if (!stream_) {
    930     DCHECK_EQ(state_, UNINITIALIZED);
    931     stream_.reset(
    932         new SourceBufferStream(config, log_cb, splice_frames_enabled_));
    933     return true;
    934   }
    935 
    936   return stream_->UpdateVideoConfig(config);
    937 }
    938 
    939 void ChunkDemuxerStream::UpdateTextConfig(const TextTrackConfig& config,
    940                                           const LogCB& log_cb) {
    941   DCHECK_EQ(type_, TEXT);
    942   base::AutoLock auto_lock(lock_);
    943   DCHECK(!stream_);
    944   DCHECK_EQ(state_, UNINITIALIZED);
    945   stream_.reset(new SourceBufferStream(config, log_cb, splice_frames_enabled_));
    946 }
    947 
    948 void ChunkDemuxerStream::MarkEndOfStream() {
    949   base::AutoLock auto_lock(lock_);
    950   stream_->MarkEndOfStream();
    951 }
    952 
    953 void ChunkDemuxerStream::UnmarkEndOfStream() {
    954   base::AutoLock auto_lock(lock_);
    955   stream_->UnmarkEndOfStream();
    956 }
    957 
    958 // DemuxerStream methods.
    959 void ChunkDemuxerStream::Read(const ReadCB& read_cb) {
    960   base::AutoLock auto_lock(lock_);
    961   DCHECK_NE(state_, UNINITIALIZED);
    962   DCHECK(read_cb_.is_null());
    963 
    964   read_cb_ = BindToCurrentLoop(read_cb);
    965   CompletePendingReadIfPossible_Locked();
    966 }
    967 
    968 DemuxerStream::Type ChunkDemuxerStream::type() { return type_; }
    969 
    970 AudioDecoderConfig ChunkDemuxerStream::audio_decoder_config() {
    971   CHECK_EQ(type_, AUDIO);
    972   base::AutoLock auto_lock(lock_);
    973   return stream_->GetCurrentAudioDecoderConfig();
    974 }
    975 
    976 VideoDecoderConfig ChunkDemuxerStream::video_decoder_config() {
    977   CHECK_EQ(type_, VIDEO);
    978   base::AutoLock auto_lock(lock_);
    979   return stream_->GetCurrentVideoDecoderConfig();
    980 }
    981 
    982 bool ChunkDemuxerStream::SupportsConfigChanges() { return true; }
    983 
    984 TextTrackConfig ChunkDemuxerStream::text_track_config() {
    985   CHECK_EQ(type_, TEXT);
    986   base::AutoLock auto_lock(lock_);
    987   return stream_->GetCurrentTextTrackConfig();
    988 }
    989 
    990 VideoRotation ChunkDemuxerStream::video_rotation() {
    991   return VIDEO_ROTATION_0;
    992 }
    993 
    994 void ChunkDemuxerStream::ChangeState_Locked(State state) {
    995   lock_.AssertAcquired();
    996   DVLOG(1) << "ChunkDemuxerStream::ChangeState_Locked() : "
    997            << "type " << type_
    998            << " - " << state_ << " -> " << state;
    999   state_ = state;
   1000 }
   1001 
   1002 ChunkDemuxerStream::~ChunkDemuxerStream() {}
   1003 
   1004 void ChunkDemuxerStream::CompletePendingReadIfPossible_Locked() {
   1005   lock_.AssertAcquired();
   1006   DCHECK(!read_cb_.is_null());
   1007 
   1008   DemuxerStream::Status status;
   1009   scoped_refptr<StreamParserBuffer> buffer;
   1010 
   1011   switch (state_) {
   1012     case UNINITIALIZED:
   1013       NOTREACHED();
   1014       return;
   1015     case RETURNING_DATA_FOR_READS:
   1016       switch (stream_->GetNextBuffer(&buffer)) {
   1017         case SourceBufferStream::kSuccess:
   1018           status = DemuxerStream::kOk;
   1019           break;
   1020         case SourceBufferStream::kNeedBuffer:
   1021           // Return early without calling |read_cb_| since we don't have
   1022           // any data to return yet.
   1023           return;
   1024         case SourceBufferStream::kEndOfStream:
   1025           status = DemuxerStream::kOk;
   1026           buffer = StreamParserBuffer::CreateEOSBuffer();
   1027           break;
   1028         case SourceBufferStream::kConfigChange:
   1029           DVLOG(2) << "Config change reported to ChunkDemuxerStream.";
   1030           status = kConfigChanged;
   1031           buffer = NULL;
   1032           break;
   1033       }
   1034       break;
   1035     case RETURNING_ABORT_FOR_READS:
   1036       // Null buffers should be returned in this state since we are waiting
   1037       // for a seek. Any buffers in the SourceBuffer should NOT be returned
   1038       // because they are associated with the seek.
   1039       status = DemuxerStream::kAborted;
   1040       buffer = NULL;
   1041       break;
   1042     case SHUTDOWN:
   1043       status = DemuxerStream::kOk;
   1044       buffer = StreamParserBuffer::CreateEOSBuffer();
   1045       break;
   1046   }
   1047 
   1048   base::ResetAndReturn(&read_cb_).Run(status, buffer);
   1049 }
   1050 
   1051 ChunkDemuxer::ChunkDemuxer(const base::Closure& open_cb,
   1052                            const NeedKeyCB& need_key_cb,
   1053                            const LogCB& log_cb,
   1054                            bool splice_frames_enabled)
   1055     : state_(WAITING_FOR_INIT),
   1056       cancel_next_seek_(false),
   1057       host_(NULL),
   1058       open_cb_(open_cb),
   1059       need_key_cb_(need_key_cb),
   1060       enable_text_(false),
   1061       log_cb_(log_cb),
   1062       duration_(kNoTimestamp()),
   1063       user_specified_duration_(-1),
   1064       liveness_(LIVENESS_UNKNOWN),
   1065       splice_frames_enabled_(splice_frames_enabled) {
   1066   DCHECK(!open_cb_.is_null());
   1067   DCHECK(!need_key_cb_.is_null());
   1068 }
   1069 
   1070 void ChunkDemuxer::Initialize(
   1071     DemuxerHost* host,
   1072     const PipelineStatusCB& cb,
   1073     bool enable_text_tracks) {
   1074   DVLOG(1) << "Init()";
   1075 
   1076   base::AutoLock auto_lock(lock_);
   1077 
   1078   init_cb_ = BindToCurrentLoop(cb);
   1079   if (state_ == SHUTDOWN) {
   1080     base::ResetAndReturn(&init_cb_).Run(DEMUXER_ERROR_COULD_NOT_OPEN);
   1081     return;
   1082   }
   1083   DCHECK_EQ(state_, WAITING_FOR_INIT);
   1084   host_ = host;
   1085   enable_text_ = enable_text_tracks;
   1086 
   1087   ChangeState_Locked(INITIALIZING);
   1088 
   1089   base::ResetAndReturn(&open_cb_).Run();
   1090 }
   1091 
   1092 void ChunkDemuxer::Stop() {
   1093   DVLOG(1) << "Stop()";
   1094   Shutdown();
   1095 }
   1096 
   1097 void ChunkDemuxer::Seek(TimeDelta time, const PipelineStatusCB& cb) {
   1098   DVLOG(1) << "Seek(" << time.InSecondsF() << ")";
   1099   DCHECK(time >= TimeDelta());
   1100 
   1101   base::AutoLock auto_lock(lock_);
   1102   DCHECK(seek_cb_.is_null());
   1103 
   1104   seek_cb_ = BindToCurrentLoop(cb);
   1105   if (state_ != INITIALIZED && state_ != ENDED) {
   1106     base::ResetAndReturn(&seek_cb_).Run(PIPELINE_ERROR_INVALID_STATE);
   1107     return;
   1108   }
   1109 
   1110   if (cancel_next_seek_) {
   1111     cancel_next_seek_ = false;
   1112     base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
   1113     return;
   1114   }
   1115 
   1116   SeekAllSources(time);
   1117   StartReturningData();
   1118 
   1119   if (IsSeekWaitingForData_Locked()) {
   1120     DVLOG(1) << "Seek() : waiting for more data to arrive.";
   1121     return;
   1122   }
   1123 
   1124   base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
   1125 }
   1126 
   1127 // Demuxer implementation.
   1128 base::Time ChunkDemuxer::GetTimelineOffset() const {
   1129   return timeline_offset_;
   1130 }
   1131 
   1132 DemuxerStream* ChunkDemuxer::GetStream(DemuxerStream::Type type) {
   1133   DCHECK_NE(type, DemuxerStream::TEXT);
   1134   base::AutoLock auto_lock(lock_);
   1135   if (type == DemuxerStream::VIDEO)
   1136     return video_.get();
   1137 
   1138   if (type == DemuxerStream::AUDIO)
   1139     return audio_.get();
   1140 
   1141   return NULL;
   1142 }
   1143 
   1144 TimeDelta ChunkDemuxer::GetStartTime() const {
   1145   return TimeDelta();
   1146 }
   1147 
   1148 Demuxer::Liveness ChunkDemuxer::GetLiveness() const {
   1149   return liveness_;
   1150 }
   1151 
   1152 void ChunkDemuxer::StartWaitingForSeek(TimeDelta seek_time) {
   1153   DVLOG(1) << "StartWaitingForSeek()";
   1154   base::AutoLock auto_lock(lock_);
   1155   DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN ||
   1156          state_ == PARSE_ERROR) << state_;
   1157   DCHECK(seek_cb_.is_null());
   1158 
   1159   if (state_ == SHUTDOWN || state_ == PARSE_ERROR)
   1160     return;
   1161 
   1162   AbortPendingReads();
   1163   SeekAllSources(seek_time);
   1164 
   1165   // Cancel state set in CancelPendingSeek() since we want to
   1166   // accept the next Seek().
   1167   cancel_next_seek_ = false;
   1168 }
   1169 
   1170 void ChunkDemuxer::CancelPendingSeek(TimeDelta seek_time) {
   1171   base::AutoLock auto_lock(lock_);
   1172   DCHECK_NE(state_, INITIALIZING);
   1173   DCHECK(seek_cb_.is_null() || IsSeekWaitingForData_Locked());
   1174 
   1175   if (cancel_next_seek_)
   1176     return;
   1177 
   1178   AbortPendingReads();
   1179   SeekAllSources(seek_time);
   1180 
   1181   if (seek_cb_.is_null()) {
   1182     cancel_next_seek_ = true;
   1183     return;
   1184   }
   1185 
   1186   base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
   1187 }
   1188 
   1189 ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
   1190                                          const std::string& type,
   1191                                          std::vector<std::string>& codecs) {
   1192   base::AutoLock auto_lock(lock_);
   1193 
   1194   if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id))
   1195     return kReachedIdLimit;
   1196 
   1197   bool has_audio = false;
   1198   bool has_video = false;
   1199   scoped_ptr<media::StreamParser> stream_parser(
   1200       StreamParserFactory::Create(type, codecs, log_cb_,
   1201                                   &has_audio, &has_video));
   1202 
   1203   if (!stream_parser)
   1204     return ChunkDemuxer::kNotSupported;
   1205 
   1206   if ((has_audio && !source_id_audio_.empty()) ||
   1207       (has_video && !source_id_video_.empty()))
   1208     return kReachedIdLimit;
   1209 
   1210   if (has_audio)
   1211     source_id_audio_ = id;
   1212 
   1213   if (has_video)
   1214     source_id_video_ = id;
   1215 
   1216   scoped_ptr<FrameProcessor> frame_processor(
   1217       new FrameProcessor(base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary,
   1218                          base::Unretained(this))));
   1219 
   1220   scoped_ptr<SourceState> source_state(
   1221       new SourceState(stream_parser.Pass(),
   1222                       frame_processor.Pass(), log_cb_,
   1223                       base::Bind(&ChunkDemuxer::CreateDemuxerStream,
   1224                                  base::Unretained(this))));
   1225 
   1226   SourceState::NewTextTrackCB new_text_track_cb;
   1227 
   1228   if (enable_text_) {
   1229     new_text_track_cb = base::Bind(&ChunkDemuxer::OnNewTextTrack,
   1230                                    base::Unretained(this));
   1231   }
   1232 
   1233   source_state->Init(
   1234       base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this)),
   1235       has_audio,
   1236       has_video,
   1237       need_key_cb_,
   1238       new_text_track_cb);
   1239 
   1240   source_state_map_[id] = source_state.release();
   1241   return kOk;
   1242 }
   1243 
   1244 void ChunkDemuxer::RemoveId(const std::string& id) {
   1245   base::AutoLock auto_lock(lock_);
   1246   CHECK(IsValidId(id));
   1247 
   1248   delete source_state_map_[id];
   1249   source_state_map_.erase(id);
   1250 
   1251   if (source_id_audio_ == id)
   1252     source_id_audio_.clear();
   1253 
   1254   if (source_id_video_ == id)
   1255     source_id_video_.clear();
   1256 }
   1257 
   1258 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const {
   1259   base::AutoLock auto_lock(lock_);
   1260   DCHECK(!id.empty());
   1261 
   1262   SourceStateMap::const_iterator itr = source_state_map_.find(id);
   1263 
   1264   DCHECK(itr != source_state_map_.end());
   1265   return itr->second->GetBufferedRanges(duration_, state_ == ENDED);
   1266 }
   1267 
   1268 void ChunkDemuxer::AppendData(
   1269     const std::string& id,
   1270     const uint8* data,
   1271     size_t length,
   1272     TimeDelta append_window_start,
   1273     TimeDelta append_window_end,
   1274     TimeDelta* timestamp_offset,
   1275     const InitSegmentReceivedCB& init_segment_received_cb) {
   1276   DVLOG(1) << "AppendData(" << id << ", " << length << ")";
   1277 
   1278   DCHECK(!id.empty());
   1279   DCHECK(timestamp_offset);
   1280   DCHECK(!init_segment_received_cb.is_null());
   1281 
   1282   Ranges<TimeDelta> ranges;
   1283 
   1284   {
   1285     base::AutoLock auto_lock(lock_);
   1286     DCHECK_NE(state_, ENDED);
   1287 
   1288     // Capture if any of the SourceBuffers are waiting for data before we start
   1289     // parsing.
   1290     bool old_waiting_for_data = IsSeekWaitingForData_Locked();
   1291 
   1292     if (length == 0u)
   1293       return;
   1294 
   1295     DCHECK(data);
   1296 
   1297     switch (state_) {
   1298       case INITIALIZING:
   1299         DCHECK(IsValidId(id));
   1300         if (!source_state_map_[id]->Append(data, length,
   1301                                            append_window_start,
   1302                                            append_window_end,
   1303                                            timestamp_offset,
   1304                                            init_segment_received_cb)) {
   1305           ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
   1306           return;
   1307         }
   1308         break;
   1309 
   1310       case INITIALIZED: {
   1311         DCHECK(IsValidId(id));
   1312         if (!source_state_map_[id]->Append(data, length,
   1313                                            append_window_start,
   1314                                            append_window_end,
   1315                                            timestamp_offset,
   1316                                            init_segment_received_cb)) {
   1317           ReportError_Locked(PIPELINE_ERROR_DECODE);
   1318           return;
   1319         }
   1320       } break;
   1321 
   1322       case PARSE_ERROR:
   1323         DVLOG(1) << "AppendData(): Ignoring data after a parse error.";
   1324         return;
   1325 
   1326       case WAITING_FOR_INIT:
   1327       case ENDED:
   1328       case SHUTDOWN:
   1329         DVLOG(1) << "AppendData(): called in unexpected state " << state_;
   1330         return;
   1331     }
   1332 
   1333     // Check to see if data was appended at the pending seek point. This
   1334     // indicates we have parsed enough data to complete the seek.
   1335     if (old_waiting_for_data && !IsSeekWaitingForData_Locked() &&
   1336         !seek_cb_.is_null()) {
   1337       base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
   1338     }
   1339 
   1340     ranges = GetBufferedRanges_Locked();
   1341   }
   1342 
   1343   for (size_t i = 0; i < ranges.size(); ++i)
   1344     host_->AddBufferedTimeRange(ranges.start(i), ranges.end(i));
   1345 }
   1346 
   1347 void ChunkDemuxer::Abort(const std::string& id,
   1348                          TimeDelta append_window_start,
   1349                          TimeDelta append_window_end,
   1350                          TimeDelta* timestamp_offset) {
   1351   DVLOG(1) << "Abort(" << id << ")";
   1352   base::AutoLock auto_lock(lock_);
   1353   DCHECK(!id.empty());
   1354   CHECK(IsValidId(id));
   1355   bool old_waiting_for_data = IsSeekWaitingForData_Locked();
   1356   source_state_map_[id]->Abort(append_window_start,
   1357                                append_window_end,
   1358                                timestamp_offset);
   1359   // Abort can possibly emit some buffers.
   1360   // Need to check whether seeking can be completed.
   1361   if (old_waiting_for_data && !IsSeekWaitingForData_Locked() &&
   1362       !seek_cb_.is_null()) {
   1363     base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
   1364   }
   1365 }
   1366 
   1367 void ChunkDemuxer::Remove(const std::string& id, TimeDelta start,
   1368                           TimeDelta end) {
   1369   DVLOG(1) << "Remove(" << id << ", " << start.InSecondsF()
   1370            << ", " << end.InSecondsF() << ")";
   1371   base::AutoLock auto_lock(lock_);
   1372 
   1373   DCHECK(!id.empty());
   1374   CHECK(IsValidId(id));
   1375   DCHECK(start >= base::TimeDelta()) << start.InSecondsF();
   1376   DCHECK(start < end) << "start " << start.InSecondsF()
   1377                       << " end " << end.InSecondsF();
   1378   DCHECK(duration_ != kNoTimestamp());
   1379   DCHECK(start <= duration_) << "start " << start.InSecondsF()
   1380                              << " duration " << duration_.InSecondsF();
   1381 
   1382   if (start == duration_)
   1383     return;
   1384 
   1385   source_state_map_[id]->Remove(start, end, duration_);
   1386 }
   1387 
   1388 double ChunkDemuxer::GetDuration() {
   1389   base::AutoLock auto_lock(lock_);
   1390   return GetDuration_Locked();
   1391 }
   1392 
   1393 double ChunkDemuxer::GetDuration_Locked() {
   1394   lock_.AssertAcquired();
   1395   if (duration_ == kNoTimestamp())
   1396     return std::numeric_limits<double>::quiet_NaN();
   1397 
   1398   // Return positive infinity if the resource is unbounded.
   1399   // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#dom-media-duration
   1400   if (duration_ == kInfiniteDuration())
   1401     return std::numeric_limits<double>::infinity();
   1402 
   1403   if (user_specified_duration_ >= 0)
   1404     return user_specified_duration_;
   1405 
   1406   return duration_.InSecondsF();
   1407 }
   1408 
   1409 void ChunkDemuxer::SetDuration(double duration) {
   1410   base::AutoLock auto_lock(lock_);
   1411   DVLOG(1) << "SetDuration(" << duration << ")";
   1412   DCHECK_GE(duration, 0);
   1413 
   1414   if (duration == GetDuration_Locked())
   1415     return;
   1416 
   1417   // Compute & bounds check the TimeDelta representation of duration.
   1418   // This can be different if the value of |duration| doesn't fit the range or
   1419   // precision of TimeDelta.
   1420   TimeDelta min_duration = TimeDelta::FromInternalValue(1);
   1421   // Don't use TimeDelta::Max() here, as we want the largest finite time delta.
   1422   TimeDelta max_duration = TimeDelta::FromInternalValue(kint64max - 1);
   1423   double min_duration_in_seconds = min_duration.InSecondsF();
   1424   double max_duration_in_seconds = max_duration.InSecondsF();
   1425 
   1426   TimeDelta duration_td;
   1427   if (duration == std::numeric_limits<double>::infinity()) {
   1428     duration_td = media::kInfiniteDuration();
   1429   } else if (duration < min_duration_in_seconds) {
   1430     duration_td = min_duration;
   1431   } else if (duration > max_duration_in_seconds) {
   1432     duration_td = max_duration;
   1433   } else {
   1434     duration_td = TimeDelta::FromMicroseconds(
   1435         duration * base::Time::kMicrosecondsPerSecond);
   1436   }
   1437 
   1438   DCHECK(duration_td > TimeDelta());
   1439 
   1440   user_specified_duration_ = duration;
   1441   duration_ = duration_td;
   1442   host_->SetDuration(duration_);
   1443 
   1444   for (SourceStateMap::iterator itr = source_state_map_.begin();
   1445        itr != source_state_map_.end(); ++itr) {
   1446     itr->second->OnSetDuration(duration_);
   1447   }
   1448 }
   1449 
   1450 bool ChunkDemuxer::IsParsingMediaSegment(const std::string& id) {
   1451   base::AutoLock auto_lock(lock_);
   1452   DVLOG(1) << "IsParsingMediaSegment(" << id << ")";
   1453   CHECK(IsValidId(id));
   1454 
   1455   return source_state_map_[id]->parsing_media_segment();
   1456 }
   1457 
   1458 void ChunkDemuxer::SetSequenceMode(const std::string& id,
   1459                                    bool sequence_mode) {
   1460   base::AutoLock auto_lock(lock_);
   1461   DVLOG(1) << "SetSequenceMode(" << id << ", " << sequence_mode << ")";
   1462   CHECK(IsValidId(id));
   1463   DCHECK_NE(state_, ENDED);
   1464 
   1465   source_state_map_[id]->SetSequenceMode(sequence_mode);
   1466 }
   1467 
   1468 void ChunkDemuxer::SetGroupStartTimestampIfInSequenceMode(
   1469     const std::string& id,
   1470     base::TimeDelta timestamp_offset) {
   1471   base::AutoLock auto_lock(lock_);
   1472   DVLOG(1) << "SetGroupStartTimestampIfInSequenceMode(" << id << ", "
   1473            << timestamp_offset.InSecondsF() << ")";
   1474   CHECK(IsValidId(id));
   1475   DCHECK_NE(state_, ENDED);
   1476 
   1477   source_state_map_[id]->SetGroupStartTimestampIfInSequenceMode(
   1478       timestamp_offset);
   1479 }
   1480 
   1481 
   1482 void ChunkDemuxer::MarkEndOfStream(PipelineStatus status) {
   1483   DVLOG(1) << "MarkEndOfStream(" << status << ")";
   1484   base::AutoLock auto_lock(lock_);
   1485   DCHECK_NE(state_, WAITING_FOR_INIT);
   1486   DCHECK_NE(state_, ENDED);
   1487 
   1488   if (state_ == SHUTDOWN || state_ == PARSE_ERROR)
   1489     return;
   1490 
   1491   if (state_ == INITIALIZING) {
   1492     ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
   1493     return;
   1494   }
   1495 
   1496   bool old_waiting_for_data = IsSeekWaitingForData_Locked();
   1497   for (SourceStateMap::iterator itr = source_state_map_.begin();
   1498        itr != source_state_map_.end(); ++itr) {
   1499     itr->second->MarkEndOfStream();
   1500   }
   1501 
   1502   CompletePendingReadsIfPossible();
   1503 
   1504   // Give a chance to resume the pending seek process.
   1505   if (status != PIPELINE_OK) {
   1506     ReportError_Locked(status);
   1507     return;
   1508   }
   1509 
   1510   ChangeState_Locked(ENDED);
   1511   DecreaseDurationIfNecessary();
   1512 
   1513   if (old_waiting_for_data && !IsSeekWaitingForData_Locked() &&
   1514       !seek_cb_.is_null()) {
   1515     base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
   1516   }
   1517 }
   1518 
   1519 void ChunkDemuxer::UnmarkEndOfStream() {
   1520   DVLOG(1) << "UnmarkEndOfStream()";
   1521   base::AutoLock auto_lock(lock_);
   1522   DCHECK_EQ(state_, ENDED);
   1523 
   1524   ChangeState_Locked(INITIALIZED);
   1525 
   1526   for (SourceStateMap::iterator itr = source_state_map_.begin();
   1527        itr != source_state_map_.end(); ++itr) {
   1528     itr->second->UnmarkEndOfStream();
   1529   }
   1530 }
   1531 
   1532 void ChunkDemuxer::Shutdown() {
   1533   DVLOG(1) << "Shutdown()";
   1534   base::AutoLock auto_lock(lock_);
   1535 
   1536   if (state_ == SHUTDOWN)
   1537     return;
   1538 
   1539   ShutdownAllStreams();
   1540 
   1541   ChangeState_Locked(SHUTDOWN);
   1542 
   1543   if(!seek_cb_.is_null())
   1544     base::ResetAndReturn(&seek_cb_).Run(PIPELINE_ERROR_ABORT);
   1545 }
   1546 
   1547 void ChunkDemuxer::SetMemoryLimits(DemuxerStream::Type type, int memory_limit) {
   1548   for (SourceStateMap::iterator itr = source_state_map_.begin();
   1549        itr != source_state_map_.end(); ++itr) {
   1550     itr->second->SetMemoryLimits(type, memory_limit);
   1551   }
   1552 }
   1553 
   1554 void ChunkDemuxer::ChangeState_Locked(State new_state) {
   1555   lock_.AssertAcquired();
   1556   DVLOG(1) << "ChunkDemuxer::ChangeState_Locked() : "
   1557            << state_ << " -> " << new_state;
   1558   state_ = new_state;
   1559 }
   1560 
   1561 ChunkDemuxer::~ChunkDemuxer() {
   1562   DCHECK_NE(state_, INITIALIZED);
   1563 
   1564   STLDeleteValues(&source_state_map_);
   1565 }
   1566 
   1567 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) {
   1568   DVLOG(1) << "ReportError_Locked(" << error << ")";
   1569   lock_.AssertAcquired();
   1570   DCHECK_NE(error, PIPELINE_OK);
   1571 
   1572   ChangeState_Locked(PARSE_ERROR);
   1573 
   1574   PipelineStatusCB cb;
   1575 
   1576   if (!init_cb_.is_null()) {
   1577     std::swap(cb, init_cb_);
   1578   } else {
   1579     if (!seek_cb_.is_null())
   1580       std::swap(cb, seek_cb_);
   1581 
   1582     ShutdownAllStreams();
   1583   }
   1584 
   1585   if (!cb.is_null()) {
   1586     cb.Run(error);
   1587     return;
   1588   }
   1589 
   1590   base::AutoUnlock auto_unlock(lock_);
   1591   host_->OnDemuxerError(error);
   1592 }
   1593 
   1594 bool ChunkDemuxer::IsSeekWaitingForData_Locked() const {
   1595   lock_.AssertAcquired();
   1596   for (SourceStateMap::const_iterator itr = source_state_map_.begin();
   1597        itr != source_state_map_.end(); ++itr) {
   1598     if (itr->second->IsSeekWaitingForData())
   1599       return true;
   1600   }
   1601 
   1602   return false;
   1603 }
   1604 
   1605 void ChunkDemuxer::OnSourceInitDone(
   1606     bool success,
   1607     const StreamParser::InitParameters& params) {
   1608   DVLOG(1) << "OnSourceInitDone(" << success << ", "
   1609            << params.duration.InSecondsF() << ")";
   1610   lock_.AssertAcquired();
   1611   DCHECK_EQ(state_, INITIALIZING);
   1612   if (!success || (!audio_ && !video_)) {
   1613     ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
   1614     return;
   1615   }
   1616 
   1617   if (params.duration != TimeDelta() && duration_ == kNoTimestamp())
   1618     UpdateDuration(params.duration);
   1619 
   1620   if (!params.timeline_offset.is_null()) {
   1621     if (!timeline_offset_.is_null() &&
   1622         params.timeline_offset != timeline_offset_) {
   1623       MEDIA_LOG(log_cb_)
   1624           << "Timeline offset is not the same across all SourceBuffers.";
   1625       ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
   1626       return;
   1627     }
   1628 
   1629     timeline_offset_ = params.timeline_offset;
   1630   }
   1631 
   1632   if (params.liveness != LIVENESS_UNKNOWN) {
   1633     if (liveness_ != LIVENESS_UNKNOWN && params.liveness != liveness_) {
   1634       MEDIA_LOG(log_cb_)
   1635           << "Liveness is not the same across all SourceBuffers.";
   1636       ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
   1637       return;
   1638     }
   1639 
   1640     liveness_ = params.liveness;
   1641   }
   1642 
   1643   // Wait until all streams have initialized.
   1644   if ((!source_id_audio_.empty() && !audio_) ||
   1645       (!source_id_video_.empty() && !video_)) {
   1646     return;
   1647   }
   1648 
   1649   SeekAllSources(GetStartTime());
   1650   StartReturningData();
   1651 
   1652   if (duration_ == kNoTimestamp())
   1653     duration_ = kInfiniteDuration();
   1654 
   1655   // The demuxer is now initialized after the |start_timestamp_| was set.
   1656   ChangeState_Locked(INITIALIZED);
   1657   base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
   1658 }
   1659 
   1660 ChunkDemuxerStream*
   1661 ChunkDemuxer::CreateDemuxerStream(DemuxerStream::Type type) {
   1662   switch (type) {
   1663     case DemuxerStream::AUDIO:
   1664       if (audio_)
   1665         return NULL;
   1666       audio_.reset(
   1667           new ChunkDemuxerStream(DemuxerStream::AUDIO, splice_frames_enabled_));
   1668       return audio_.get();
   1669       break;
   1670     case DemuxerStream::VIDEO:
   1671       if (video_)
   1672         return NULL;
   1673       video_.reset(
   1674           new ChunkDemuxerStream(DemuxerStream::VIDEO, splice_frames_enabled_));
   1675       return video_.get();
   1676       break;
   1677     case DemuxerStream::TEXT: {
   1678       return new ChunkDemuxerStream(DemuxerStream::TEXT,
   1679                                     splice_frames_enabled_);
   1680       break;
   1681     }
   1682     case DemuxerStream::UNKNOWN:
   1683     case DemuxerStream::NUM_TYPES:
   1684       NOTREACHED();
   1685       return NULL;
   1686   }
   1687   NOTREACHED();
   1688   return NULL;
   1689 }
   1690 
   1691 void ChunkDemuxer::OnNewTextTrack(ChunkDemuxerStream* text_stream,
   1692                                   const TextTrackConfig& config) {
   1693   lock_.AssertAcquired();
   1694   DCHECK_NE(state_, SHUTDOWN);
   1695   host_->AddTextStream(text_stream, config);
   1696 }
   1697 
   1698 bool ChunkDemuxer::IsValidId(const std::string& source_id) const {
   1699   lock_.AssertAcquired();
   1700   return source_state_map_.count(source_id) > 0u;
   1701 }
   1702 
   1703 void ChunkDemuxer::UpdateDuration(TimeDelta new_duration) {
   1704   DCHECK(duration_ != new_duration);
   1705   user_specified_duration_ = -1;
   1706   duration_ = new_duration;
   1707   host_->SetDuration(new_duration);
   1708 }
   1709 
   1710 void ChunkDemuxer::IncreaseDurationIfNecessary(TimeDelta new_duration) {
   1711   DCHECK(new_duration != kNoTimestamp());
   1712   DCHECK(new_duration != kInfiniteDuration());
   1713 
   1714   // Per April 1, 2014 MSE spec editor's draft:
   1715   // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/
   1716   //     media-source.html#sourcebuffer-coded-frame-processing
   1717   // 5. If the media segment contains data beyond the current duration, then run
   1718   //    the duration change algorithm with new duration set to the maximum of
   1719   //    the current duration and the group end timestamp.
   1720 
   1721   if (new_duration <= duration_)
   1722     return;
   1723 
   1724   DVLOG(2) << __FUNCTION__ << ": Increasing duration: "
   1725            << duration_.InSecondsF() << " -> " << new_duration.InSecondsF();
   1726 
   1727   UpdateDuration(new_duration);
   1728 }
   1729 
   1730 void ChunkDemuxer::DecreaseDurationIfNecessary() {
   1731   lock_.AssertAcquired();
   1732 
   1733   TimeDelta max_duration;
   1734 
   1735   for (SourceStateMap::const_iterator itr = source_state_map_.begin();
   1736        itr != source_state_map_.end(); ++itr) {
   1737     max_duration = std::max(max_duration,
   1738                             itr->second->GetMaxBufferedDuration());
   1739   }
   1740 
   1741   if (max_duration == TimeDelta())
   1742     return;
   1743 
   1744   if (max_duration < duration_)
   1745     UpdateDuration(max_duration);
   1746 }
   1747 
   1748 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges() const {
   1749   base::AutoLock auto_lock(lock_);
   1750   return GetBufferedRanges_Locked();
   1751 }
   1752 
   1753 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges_Locked() const {
   1754   lock_.AssertAcquired();
   1755 
   1756   bool ended = state_ == ENDED;
   1757   // TODO(acolwell): When we start allowing SourceBuffers that are not active,
   1758   // we'll need to update this loop to only add ranges from active sources.
   1759   RangesList ranges_list;
   1760   for (SourceStateMap::const_iterator itr = source_state_map_.begin();
   1761        itr != source_state_map_.end(); ++itr) {
   1762     ranges_list.push_back(itr->second->GetBufferedRanges(duration_, ended));
   1763   }
   1764 
   1765   return ComputeIntersection(ranges_list, ended);
   1766 }
   1767 
   1768 void ChunkDemuxer::StartReturningData() {
   1769   for (SourceStateMap::iterator itr = source_state_map_.begin();
   1770        itr != source_state_map_.end(); ++itr) {
   1771     itr->second->StartReturningData();
   1772   }
   1773 }
   1774 
   1775 void ChunkDemuxer::AbortPendingReads() {
   1776   for (SourceStateMap::iterator itr = source_state_map_.begin();
   1777        itr != source_state_map_.end(); ++itr) {
   1778     itr->second->AbortReads();
   1779   }
   1780 }
   1781 
   1782 void ChunkDemuxer::SeekAllSources(TimeDelta seek_time) {
   1783   for (SourceStateMap::iterator itr = source_state_map_.begin();
   1784        itr != source_state_map_.end(); ++itr) {
   1785     itr->second->Seek(seek_time);
   1786   }
   1787 }
   1788 
   1789 void ChunkDemuxer::CompletePendingReadsIfPossible() {
   1790   for (SourceStateMap::iterator itr = source_state_map_.begin();
   1791        itr != source_state_map_.end(); ++itr) {
   1792     itr->second->CompletePendingReadIfPossible();
   1793   }
   1794 }
   1795 
   1796 void ChunkDemuxer::ShutdownAllStreams() {
   1797   for (SourceStateMap::iterator itr = source_state_map_.begin();
   1798        itr != source_state_map_.end(); ++itr) {
   1799     itr->second->Shutdown();
   1800   }
   1801 }
   1802 
   1803 }  // namespace media
   1804