Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(146)

Side by Side Diff: media/filters/chunk_demuxer.cc

Issue 191513002: Extract coded frame processing from SourceState into LegacyFrameProcessor (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase and address PS2 comments and nits Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/filters/chunk_demuxer.h" 5 #include "media/filters/chunk_demuxer.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <deque>
9 #include <limits> 8 #include <limits>
10 #include <list> 9 #include <list>
11 10
12 #include "base/bind.h" 11 #include "base/bind.h"
13 #include "base/callback_helpers.h" 12 #include "base/callback_helpers.h"
14 #include "base/location.h" 13 #include "base/location.h"
15 #include "base/message_loop/message_loop_proxy.h" 14 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/stl_util.h" 15 #include "base/stl_util.h"
17 #include "media/base/audio_decoder_config.h" 16 #include "media/base/audio_decoder_config.h"
18 #include "media/base/bind_to_current_loop.h" 17 #include "media/base/bind_to_current_loop.h"
19 #include "media/base/stream_parser_buffer.h" 18 #include "media/base/stream_parser_buffer.h"
20 #include "media/base/video_decoder_config.h" 19 #include "media/base/video_decoder_config.h"
20 #include "media/filters/legacy_frame_processor.h"
21 #include "media/filters/stream_parser_factory.h" 21 #include "media/filters/stream_parser_factory.h"
22 22
23 using base::TimeDelta; 23 using base::TimeDelta;
24 24
25 namespace media { 25 namespace media {
26 26
27 // List of time ranges for each SourceBuffer. 27 // List of time ranges for each SourceBuffer.
28 typedef std::list<Ranges<TimeDelta> > RangesList; 28 typedef std::list<Ranges<TimeDelta> > RangesList;
29 static Ranges<TimeDelta> ComputeIntersection(const RangesList& activeRanges, 29 static Ranges<TimeDelta> ComputeIntersection(const RangesList& activeRanges,
30 bool ended) { 30 bool ended) {
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 return intersection_ranges; 79 return intersection_ranges;
80 } 80 }
81 81
82 // Contains state belonging to a source id. 82 // Contains state belonging to a source id.
83 class SourceState { 83 class SourceState {
84 public: 84 public:
85 // Callback signature used to create ChunkDemuxerStreams. 85 // Callback signature used to create ChunkDemuxerStreams.
86 typedef base::Callback<ChunkDemuxerStream*( 86 typedef base::Callback<ChunkDemuxerStream*(
87 DemuxerStream::Type)> CreateDemuxerStreamCB; 87 DemuxerStream::Type)> CreateDemuxerStreamCB;
88 88
89 // Callback signature used to notify ChunkDemuxer of timestamps
90 // that may cause the duration to be updated.
91 typedef base::Callback<void(
92 TimeDelta, ChunkDemuxerStream*)> IncreaseDurationCB;
93
94 typedef base::Callback<void( 89 typedef base::Callback<void(
95 ChunkDemuxerStream*, const TextTrackConfig&)> NewTextTrackCB; 90 ChunkDemuxerStream*, const TextTrackConfig&)> NewTextTrackCB;
96 91
97 SourceState(scoped_ptr<StreamParser> stream_parser, const LogCB& log_cb, 92 SourceState(
98 const CreateDemuxerStreamCB& create_demuxer_stream_cb, 93 scoped_ptr<StreamParser> stream_parser,
99 const IncreaseDurationCB& increase_duration_cb); 94 scoped_ptr<FrameProcessorBase> frame_processor, const LogCB& log_cb,
95 const CreateDemuxerStreamCB& create_demuxer_stream_cb);
100 96
101 ~SourceState(); 97 ~SourceState();
102 98
103 void Init(const StreamParser::InitCB& init_cb, 99 void Init(const StreamParser::InitCB& init_cb,
104 bool allow_audio, 100 bool allow_audio,
105 bool allow_video, 101 bool allow_video,
106 const StreamParser::NeedKeyCB& need_key_cb, 102 const StreamParser::NeedKeyCB& need_key_cb,
107 const NewTextTrackCB& new_text_track_cb); 103 const NewTextTrackCB& new_text_track_cb);
108 104
109 // Appends new data to the StreamParser. 105 // Appends new data to the StreamParser.
110 // Returns true if the data was successfully appended. Returns false if an 106 // Returns true if the data was successfully appended. Returns false if an
111 // error occurred. Appending uses cached |timestamp_offset_| and may update 107 // error occurred. |*timestamp_offset| is used and possibly updated by the
112 // |*timestamp_offset| if |timestamp_offset| is not NULL. 108 // append. |append_window_start| and |append_window_end| correspond to the MSE
113 // TODO(wolenetz): Rework so |timestamp_offset_| is only valid during 109 // spec's similarly named source buffer attributes that are used in coded
114 // Append(). See http://crbug.com/347623. 110 // frame processing.
115 bool Append(const uint8* data, size_t length, double* timestamp_offset); 111 bool Append(const uint8* data, size_t length,
112 const base::TimeDelta& append_window_start,
acolwell GONE FROM CHROMIUM 2014/03/11 20:00:37 ditto
wolenetz 2014/03/12 00:46:14 Done in the prereq CL I split out of this.
113 const base::TimeDelta& append_window_end,
114 base::TimeDelta* timestamp_offset);
116 115
117 // Aborts the current append sequence and resets the parser. 116 // Aborts the current append sequence and resets the parser.
118 void Abort(); 117 void Abort();
119 118
120 // Calls Remove(|start|, |end|, |duration|) on all 119 // Calls Remove(|start|, |end|, |duration|) on all
121 // ChunkDemuxerStreams managed by this object. 120 // ChunkDemuxerStreams managed by this object.
122 void Remove(TimeDelta start, TimeDelta end, TimeDelta duration); 121 void Remove(TimeDelta start, TimeDelta end, TimeDelta duration);
123 122
124 // Sets user-specified |timestamp_offset_| if possible. 123 // Returns true if currently parsing a media segment, or false otherwise.
125 // Returns true if the offset was set. Returns false if the offset could not 124 bool IsParsingMediaSegment() const { return parsing_media_segment_; }
acolwell GONE FROM CHROMIUM 2014/03/11 20:00:37 nit: unix_hacker_style since this is a simple acce
wolenetz 2014/03/12 00:46:14 Done in the prereq CL I split out of this.
126 // be set at this time.
127 bool SetTimestampOffset(TimeDelta timestamp_offset);
128 125
129 // Sets |sequence_mode_| to |sequence_mode| if possible. 126 // Sets |frame_processor_|'s sequence mode to |sequence_mode|.
130 // Returns true if the mode update was allowed. Returns false if the mode 127 void SetSequenceMode(bool sequence_mode);
131 // could not be updated at this time.
132 bool SetSequenceMode(bool sequence_mode);
133
134 void set_append_window_start(TimeDelta start) {
135 append_window_start_ = start;
136 }
137 void set_append_window_end(TimeDelta end) { append_window_end_ = end; }
138 128
139 // Returns the range of buffered data in this source, capped at |duration|. 129 // Returns the range of buffered data in this source, capped at |duration|.
140 // |ended| - Set to true if end of stream has been signalled and the special 130 // |ended| - Set to true if end of stream has been signalled and the special
141 // end of stream range logic needs to be executed. 131 // end of stream range logic needs to be executed.
142 Ranges<TimeDelta> GetBufferedRanges(TimeDelta duration, bool ended) const; 132 Ranges<TimeDelta> GetBufferedRanges(TimeDelta duration, bool ended) const;
143 133
144 // Returns the highest buffered duration across all streams managed 134 // Returns the highest buffered duration across all streams managed
145 // by this object. 135 // by this object.
146 // Returns TimeDelta() if none of the streams contain buffered data. 136 // Returns TimeDelta() if none of the streams contain buffered data.
147 TimeDelta GetMaxBufferedDuration() const; 137 TimeDelta GetMaxBufferedDuration() const;
(...skipping 22 matching lines...) Expand all
170 const AudioDecoderConfig& audio_config, 160 const AudioDecoderConfig& audio_config,
171 const VideoDecoderConfig& video_config, 161 const VideoDecoderConfig& video_config,
172 const StreamParser::TextTrackConfigMap& text_configs); 162 const StreamParser::TextTrackConfigMap& text_configs);
173 163
174 // Called by the |stream_parser_| at the beginning of a new media segment. 164 // Called by the |stream_parser_| at the beginning of a new media segment.
175 void OnNewMediaSegment(); 165 void OnNewMediaSegment();
176 166
177 // Called by the |stream_parser_| at the end of a media segment. 167 // Called by the |stream_parser_| at the end of a media segment.
178 void OnEndOfMediaSegment(); 168 void OnEndOfMediaSegment();
179 169
180 // Called by the |stream_parser_| when new buffers have been parsed. It 170 // Called by the |stream_parser_| when new buffers have been parsed.
181 // applies |timestamp_offset_| to all buffers in |audio_buffers|, 171 // It processes the new buffers using |frame_processor_|, which includes
182 // |video_buffers| and |text_map| and then calls Append() with the modified 172 // appending the processed frames to associated demuxer streams for each
183 // buffers on |audio_|, |video_| and/or the text demuxer streams associated 173 // frame's track.
184 // with the track numbers in |text_map|.
185 // Returns true on a successful call. Returns false if an error occurred while 174 // Returns true on a successful call. Returns false if an error occurred while
186 // processing the buffers. 175 // processing the buffers.
187 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, 176 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers,
188 const StreamParser::BufferQueue& video_buffers, 177 const StreamParser::BufferQueue& video_buffers,
189 const StreamParser::TextBufferQueueMap& text_map); 178 const StreamParser::TextBufferQueueMap& text_map);
190 179
191 // Helper function for OnNewBuffers() when new text buffers have been parsed.
192 // It applies |timestamp_offset_| to all buffers in |buffers| and then appends
193 // the (modified) buffers to the demuxer stream associated with
194 // the track having |text_track_id|.
195 // Returns true on a successful call. Returns false if an error occurred while
196 // processing the buffers.
197 bool OnTextBuffers(StreamParser::TrackId text_track_id,
198 const StreamParser::BufferQueue& buffers);
199
200 // Helper function that appends |buffers| to |stream| and calls
201 // |increase_duration_cb_| to potentially update the duration.
202 // Returns true if the append was successful. Returns false if
203 // |stream| is NULL or something in |buffers| caused the append to fail.
204 bool AppendAndUpdateDuration(ChunkDemuxerStream* stream,
205 const StreamParser::BufferQueue& buffers);
206
207 // Helper function that adds |timestamp_offset_| to each buffer in |buffers|.
208 void AdjustBufferTimestamps(const StreamParser::BufferQueue& buffers);
209
210 // Filters out buffers that are outside of the append window
211 // [|append_window_start_|, |append_window_end_|).
212 // |needs_keyframe| is a pointer to the |xxx_need_keyframe_| flag
213 // associated with the |buffers|. Its state is read an updated as
214 // this method filters |buffers|.
215 // Buffers that are inside the append window are appended to the end
216 // of |filtered_buffers|.
217 void FilterWithAppendWindow(const StreamParser::BufferQueue& buffers,
218 bool* needs_keyframe,
219 StreamParser::BufferQueue* filtered_buffers);
220
221 CreateDemuxerStreamCB create_demuxer_stream_cb_; 180 CreateDemuxerStreamCB create_demuxer_stream_cb_;
222 IncreaseDurationCB increase_duration_cb_;
223 NewTextTrackCB new_text_track_cb_; 181 NewTextTrackCB new_text_track_cb_;
224 182
225 // The offset to apply to media segment timestamps. 183 // During Append(), if OnNewBuffers() coded frame processing updates the
226 TimeDelta timestamp_offset_; 184 // timestamp offset then |*timestamp_offset_during_append_| is also updated
185 // so Append()'s caller can know the new offset. This pointer is only non-NULL
186 // during the lifetime of an Append() call.
187 base::TimeDelta* timestamp_offset_during_append_;
227 188
228 // Flag that tracks whether or not the current Append() operation changed 189 // During Append(), coded frame processing triggered by OnNewBuffers()
229 // |timestamp_offset_|. 190 // requires these two attributes. These are only valid during the lifetime of
230 bool timestamp_offset_updated_by_append_; 191 // an Append() call.
231 192 base::TimeDelta append_window_start_during_append_;
232 // Tracks the mode by which appended media is processed. If true, then 193 base::TimeDelta append_window_end_during_append_;
233 // appended media is processed using "sequence" mode. Otherwise, appended
234 // media is processed using "segments" mode.
235 // TODO(wolenetz): Enable "sequence" mode logic. See http://crbug.com/249422
236 // and http://crbug.com/333437.
237 bool sequence_mode_;
238
239 TimeDelta append_window_start_;
240 TimeDelta append_window_end_;
241 194
242 // Set to true if the next buffers appended within the append window 195 // Set to true if the next buffers appended within the append window
243 // represent the start of a new media segment. This flag being set 196 // represent the start of a new media segment. This flag being set
244 // triggers a call to |new_segment_cb_| when the new buffers are 197 // triggers a call to |new_segment_cb_| when the new buffers are
245 // appended. The flag is set on actual media segment boundaries and 198 // appended. The flag is set on actual media segment boundaries and
246 // when the "append window" filtering causes discontinuities in the 199 // when the "append window" filtering causes discontinuities in the
247 // appended data. 200 // appended data.
248 bool new_media_segment_; 201 bool new_media_segment_;
249 202
250 // Keeps track of whether |timestamp_offset_| or |sequence_mode_| can be 203 // Keeps track of whether a media segment is being parsed.
251 // updated. These cannot be updated if a media segment is being parsed.
252 bool parsing_media_segment_; 204 bool parsing_media_segment_;
253 205
254 // The object used to parse appended data. 206 // The object used to parse appended data.
255 scoped_ptr<StreamParser> stream_parser_; 207 scoped_ptr<StreamParser> stream_parser_;
256 208
257 ChunkDemuxerStream* audio_; 209 ChunkDemuxerStream* audio_; // Not owned by |this|.
258 bool audio_needs_keyframe_; 210 ChunkDemuxerStream* video_; // Not owned by |this|.
259
260 ChunkDemuxerStream* video_;
261 bool video_needs_keyframe_;
262 211
263 typedef std::map<StreamParser::TrackId, ChunkDemuxerStream*> TextStreamMap; 212 typedef std::map<StreamParser::TrackId, ChunkDemuxerStream*> TextStreamMap;
264 TextStreamMap text_stream_map_; 213 TextStreamMap text_stream_map_; // |this| owns the map's stream pointers.
265 214
215 scoped_ptr<FrameProcessorBase> frame_processor_;
266 LogCB log_cb_; 216 LogCB log_cb_;
267 217
268 DISALLOW_COPY_AND_ASSIGN(SourceState); 218 DISALLOW_COPY_AND_ASSIGN(SourceState);
269 }; 219 };
270 220
271 class ChunkDemuxerStream : public DemuxerStream { 221 SourceState::SourceState(
272 public: 222 scoped_ptr<StreamParser> stream_parser,
273 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; 223 scoped_ptr<FrameProcessorBase> frame_processor,
274 224 const LogCB& log_cb,
275 explicit ChunkDemuxerStream(Type type); 225 const CreateDemuxerStreamCB& create_demuxer_stream_cb)
276 virtual ~ChunkDemuxerStream();
277
278 // ChunkDemuxerStream control methods.
279 void StartReturningData();
280 void AbortReads();
281 void CompletePendingReadIfPossible();
282 void Shutdown();
283
284 // SourceBufferStream manipulation methods.
285 void Seek(TimeDelta time);
286 bool IsSeekWaitingForData() const;
287
288 // Add buffers to this stream. Buffers are stored in SourceBufferStreams,
289 // which handle ordering and overlap resolution.
290 // Returns true if buffers were successfully added.
291 bool Append(const StreamParser::BufferQueue& buffers);
292
293 // Removes buffers between |start| and |end| according to the steps
294 // in the "Coded Frame Removal Algorithm" in the Media Source
295 // Extensions Spec.
296 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-sourc e.html#sourcebuffer-coded-frame-removal
297 //
298 // |duration| is the current duration of the presentation. It is
299 // required by the computation outlined in the spec.
300 void Remove(TimeDelta start, TimeDelta end, TimeDelta duration);
301
302 // Signal to the stream that duration has changed to |duration|.
303 void OnSetDuration(TimeDelta duration);
304
305 // Returns the range of buffered data in this stream, capped at |duration|.
306 Ranges<TimeDelta> GetBufferedRanges(TimeDelta duration) const;
307
308 // Returns the duration of the buffered data.
309 // Returns TimeDelta() if the stream has no buffered data.
310 TimeDelta GetBufferedDuration() const;
311
312 // Signal to the stream that buffers handed in through subsequent calls to
313 // Append() belong to a media segment that starts at |start_timestamp|.
314 void OnNewMediaSegment(TimeDelta start_timestamp);
315
316 // Called when midstream config updates occur.
317 // Returns true if the new config is accepted.
318 // Returns false if the new config should trigger an error.
319 bool UpdateAudioConfig(const AudioDecoderConfig& config, const LogCB& log_cb);
320 bool UpdateVideoConfig(const VideoDecoderConfig& config, const LogCB& log_cb);
321 void UpdateTextConfig(const TextTrackConfig& config, const LogCB& log_cb);
322
323 void MarkEndOfStream();
324 void UnmarkEndOfStream();
325
326 // DemuxerStream methods.
327 virtual void Read(const ReadCB& read_cb) OVERRIDE;
328 virtual Type type() OVERRIDE;
329 virtual void EnableBitstreamConverter() OVERRIDE;
330 virtual AudioDecoderConfig audio_decoder_config() OVERRIDE;
331 virtual VideoDecoderConfig video_decoder_config() OVERRIDE;
332
333 // Returns the text track configuration. It is an error to call this method
334 // if type() != TEXT.
335 TextTrackConfig text_track_config();
336
337 // Sets the memory limit, in bytes, on the SourceBufferStream.
338 void set_memory_limit_for_testing(int memory_limit) {
339 stream_->set_memory_limit_for_testing(memory_limit);
340 }
341
342 private:
343 enum State {
344 UNINITIALIZED,
345 RETURNING_DATA_FOR_READS,
346 RETURNING_ABORT_FOR_READS,
347 SHUTDOWN,
348 };
349
350 // Assigns |state_| to |state|
351 void ChangeState_Locked(State state);
352
353 void CompletePendingReadIfPossible_Locked();
354
355 // Specifies the type of the stream.
356 Type type_;
357
358 scoped_ptr<SourceBufferStream> stream_;
359
360 mutable base::Lock lock_;
361 State state_;
362 ReadCB read_cb_;
363
364 DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream);
365 };
366
367 SourceState::SourceState(scoped_ptr<StreamParser> stream_parser,
368 const LogCB& log_cb,
369 const CreateDemuxerStreamCB& create_demuxer_stream_cb,
370 const IncreaseDurationCB& increase_duration_cb)
371 : create_demuxer_stream_cb_(create_demuxer_stream_cb), 226 : create_demuxer_stream_cb_(create_demuxer_stream_cb),
372 increase_duration_cb_(increase_duration_cb), 227 timestamp_offset_during_append_(NULL),
373 timestamp_offset_updated_by_append_(false),
374 sequence_mode_(false),
375 append_window_end_(kInfiniteDuration()),
376 new_media_segment_(false), 228 new_media_segment_(false),
377 parsing_media_segment_(false), 229 parsing_media_segment_(false),
378 stream_parser_(stream_parser.release()), 230 stream_parser_(stream_parser.release()),
379 audio_(NULL), 231 audio_(NULL),
380 audio_needs_keyframe_(true),
381 video_(NULL), 232 video_(NULL),
382 video_needs_keyframe_(true), 233 frame_processor_(frame_processor.release()),
383 log_cb_(log_cb) { 234 log_cb_(log_cb) {
384 DCHECK(!create_demuxer_stream_cb_.is_null()); 235 DCHECK(!create_demuxer_stream_cb_.is_null());
385 DCHECK(!increase_duration_cb_.is_null()); 236 DCHECK(frame_processor_);
386 } 237 }
387 238
388 SourceState::~SourceState() { 239 SourceState::~SourceState() {
389 Shutdown(); 240 Shutdown();
390 241
391 STLDeleteValues(&text_stream_map_); 242 STLDeleteValues(&text_stream_map_);
392 } 243 }
393 244
394 void SourceState::Init(const StreamParser::InitCB& init_cb, 245 void SourceState::Init(const StreamParser::InitCB& init_cb,
395 bool allow_audio, 246 bool allow_audio,
(...skipping 11 matching lines...) Expand all
407 base::Unretained(this)), 258 base::Unretained(this)),
408 new_text_track_cb_.is_null(), 259 new_text_track_cb_.is_null(),
409 need_key_cb, 260 need_key_cb,
410 base::Bind(&SourceState::OnNewMediaSegment, 261 base::Bind(&SourceState::OnNewMediaSegment,
411 base::Unretained(this)), 262 base::Unretained(this)),
412 base::Bind(&SourceState::OnEndOfMediaSegment, 263 base::Bind(&SourceState::OnEndOfMediaSegment,
413 base::Unretained(this)), 264 base::Unretained(this)),
414 log_cb_); 265 log_cb_);
415 } 266 }
416 267
417 bool SourceState::SetTimestampOffset(TimeDelta timestamp_offset) { 268 void SourceState::SetSequenceMode(bool sequence_mode) {
418 if (parsing_media_segment_) 269 DCHECK(!parsing_media_segment_);
419 return false;
420 270
421 timestamp_offset_ = timestamp_offset; 271 frame_processor_->SetSequenceMode(sequence_mode);
422 return true;
423 }
424
425 bool SourceState::SetSequenceMode(bool sequence_mode) {
426 if (parsing_media_segment_)
427 return false;
428
429 sequence_mode_ = sequence_mode;
430 return true;
431 } 272 }
432 273
433 bool SourceState::Append(const uint8* data, size_t length, 274 bool SourceState::Append(const uint8* data, size_t length,
434 double* timestamp_offset) { 275 const base::TimeDelta& append_window_start,
435 timestamp_offset_updated_by_append_ = false; 276 const base::TimeDelta& append_window_end,
277 base::TimeDelta* timestamp_offset) {
278 DCHECK(timestamp_offset);
279 DCHECK(!timestamp_offset_during_append_);
280 timestamp_offset_during_append_ = timestamp_offset;
281 append_window_start_during_append_ = append_window_start;
282 append_window_end_during_append_ = append_window_end;
436 bool err = stream_parser_->Parse(data, length); 283 bool err = stream_parser_->Parse(data, length);
acolwell GONE FROM CHROMIUM 2014/03/11 20:00:37 nit: Add a TODO here to change the StreamParser::P
wolenetz 2014/03/12 00:46:14 Done in prereq CL I split out of this. I assigned
437 284 timestamp_offset_during_append_ = NULL;
438 if (timestamp_offset_updated_by_append_ && timestamp_offset)
439 *timestamp_offset = timestamp_offset_.InSecondsF();
440
441 return err; 285 return err;
442 } 286 }
443 287
444 void SourceState::Abort() { 288 void SourceState::Abort() {
445 stream_parser_->Flush(); 289 stream_parser_->Flush();
446 audio_needs_keyframe_ = true; 290 frame_processor_->Reset();
447 video_needs_keyframe_ = true;
448 parsing_media_segment_ = false; 291 parsing_media_segment_ = false;
449 } 292 }
450 293
451 void SourceState::Remove(TimeDelta start, TimeDelta end, TimeDelta duration) { 294 void SourceState::Remove(TimeDelta start, TimeDelta end, TimeDelta duration) {
452 if (audio_) 295 if (audio_)
453 audio_->Remove(start, end, duration); 296 audio_->Remove(start, end, duration);
454 297
455 if (video_) 298 if (video_)
456 video_->Remove(start, end, duration); 299 video_->Remove(start, end, duration);
457 300
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after
624 // NOTE: We are intentionally not checking the text tracks 467 // NOTE: We are intentionally not checking the text tracks
625 // because text tracks are discontinuous and may not have data 468 // because text tracks are discontinuous and may not have data
626 // for the seek position. This is ok and playback should not be 469 // for the seek position. This is ok and playback should not be
627 // stalled because we don't have cues. If cues, with timestamps after 470 // stalled because we don't have cues. If cues, with timestamps after
628 // the seek time, eventually arrive they will be delivered properly 471 // the seek time, eventually arrive they will be delivered properly
629 // in response to ChunkDemuxerStream::Read() calls. 472 // in response to ChunkDemuxerStream::Read() calls.
630 473
631 return false; 474 return false;
632 } 475 }
633 476
634 void SourceState::AdjustBufferTimestamps(
635 const StreamParser::BufferQueue& buffers) {
636 if (timestamp_offset_ == TimeDelta())
637 return;
638
639 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
640 itr != buffers.end(); ++itr) {
641 (*itr)->SetDecodeTimestamp(
642 (*itr)->GetDecodeTimestamp() + timestamp_offset_);
643 (*itr)->set_timestamp((*itr)->timestamp() + timestamp_offset_);
644 }
645 }
646
647 bool SourceState::OnNewConfigs( 477 bool SourceState::OnNewConfigs(
648 bool allow_audio, bool allow_video, 478 bool allow_audio, bool allow_video,
649 const AudioDecoderConfig& audio_config, 479 const AudioDecoderConfig& audio_config,
650 const VideoDecoderConfig& video_config, 480 const VideoDecoderConfig& video_config,
651 const StreamParser::TextTrackConfigMap& text_configs) { 481 const StreamParser::TextTrackConfigMap& text_configs) {
652 DVLOG(1) << "OnNewConfigs(" << allow_audio << ", " << allow_video 482 DVLOG(1) << "OnNewConfigs(" << allow_audio << ", " << allow_video
653 << ", " << audio_config.IsValidConfig() 483 << ", " << audio_config.IsValidConfig()
654 << ", " << video_config.IsValidConfig() << ")"; 484 << ", " << video_config.IsValidConfig() << ")";
655 485
656 if (!audio_config.IsValidConfig() && !video_config.IsValidConfig()) { 486 if (!audio_config.IsValidConfig() && !video_config.IsValidConfig()) {
(...skipping 25 matching lines...) Expand all
682 512
683 bool success = true; 513 bool success = true;
684 if (audio_config.IsValidConfig()) { 514 if (audio_config.IsValidConfig()) {
685 if (!audio_) { 515 if (!audio_) {
686 audio_ = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO); 516 audio_ = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO);
687 517
688 if (!audio_) { 518 if (!audio_) {
689 DVLOG(1) << "Failed to create an audio stream."; 519 DVLOG(1) << "Failed to create an audio stream.";
690 return false; 520 return false;
691 } 521 }
522
523 if (!frame_processor_->AddTrack(FrameProcessorBase::kAudioTrackId,
524 audio_)) {
525 DVLOG(1) << "Failed to add audio track to frame processor.";
526 return false;
527 }
692 } 528 }
693 529
694 success &= audio_->UpdateAudioConfig(audio_config, log_cb_); 530 success &= audio_->UpdateAudioConfig(audio_config, log_cb_);
695 } 531 }
696 532
697 if (video_config.IsValidConfig()) { 533 if (video_config.IsValidConfig()) {
698 if (!video_) { 534 if (!video_) {
699 video_ = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO); 535 video_ = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO);
700 536
701 if (!video_) { 537 if (!video_) {
702 DVLOG(1) << "Failed to create a video stream."; 538 DVLOG(1) << "Failed to create a video stream.";
703 return false; 539 return false;
704 } 540 }
541
542 if (!frame_processor_->AddTrack(FrameProcessorBase::kVideoTrackId,
543 video_)) {
544 DVLOG(1) << "Failed to add video track to frame processor.";
545 return false;
546 }
705 } 547 }
706 548
707 success &= video_->UpdateVideoConfig(video_config, log_cb_); 549 success &= video_->UpdateVideoConfig(video_config, log_cb_);
708 } 550 }
709 551
710 typedef StreamParser::TextTrackConfigMap::const_iterator TextConfigItr; 552 typedef StreamParser::TextTrackConfigMap::const_iterator TextConfigItr;
711 if (text_stream_map_.empty()) { 553 if (text_stream_map_.empty()) {
712 for (TextConfigItr itr = text_configs.begin(); 554 for (TextConfigItr itr = text_configs.begin();
713 itr != text_configs.end(); ++itr) { 555 itr != text_configs.end(); ++itr) {
714 ChunkDemuxerStream* const text_stream = 556 ChunkDemuxerStream* const text_stream =
715 create_demuxer_stream_cb_.Run(DemuxerStream::TEXT); 557 create_demuxer_stream_cb_.Run(DemuxerStream::TEXT);
558 if (!frame_processor_->AddTrack(itr->first, text_stream)) {
559 success &= false;
560 MEDIA_LOG(log_cb_) << "Failed to add text track ID " << itr->first
561 << " to frame processor.";
562 break;
563 }
716 text_stream->UpdateTextConfig(itr->second, log_cb_); 564 text_stream->UpdateTextConfig(itr->second, log_cb_);
717 text_stream_map_[itr->first] = text_stream; 565 text_stream_map_[itr->first] = text_stream;
718 new_text_track_cb_.Run(text_stream, itr->second); 566 new_text_track_cb_.Run(text_stream, itr->second);
719 } 567 }
720 } else { 568 } else {
721 const size_t text_count = text_stream_map_.size(); 569 const size_t text_count = text_stream_map_.size();
722 if (text_configs.size() != text_count) { 570 if (text_configs.size() != text_count) {
723 success &= false; 571 success &= false;
724 MEDIA_LOG(log_cb_) << "The number of text track configs changed."; 572 MEDIA_LOG(log_cb_) << "The number of text track configs changed.";
725 } else if (text_count == 1) { 573 } else if (text_count == 1) {
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
775 void SourceState::OnEndOfMediaSegment() { 623 void SourceState::OnEndOfMediaSegment() {
776 DVLOG(2) << "OnEndOfMediaSegment()"; 624 DVLOG(2) << "OnEndOfMediaSegment()";
777 parsing_media_segment_ = false; 625 parsing_media_segment_ = false;
778 new_media_segment_ = false; 626 new_media_segment_ = false;
779 } 627 }
780 628
781 bool SourceState::OnNewBuffers( 629 bool SourceState::OnNewBuffers(
782 const StreamParser::BufferQueue& audio_buffers, 630 const StreamParser::BufferQueue& audio_buffers,
783 const StreamParser::BufferQueue& video_buffers, 631 const StreamParser::BufferQueue& video_buffers,
784 const StreamParser::TextBufferQueueMap& text_map) { 632 const StreamParser::TextBufferQueueMap& text_map) {
785 DCHECK(!audio_buffers.empty() || !video_buffers.empty() || 633 DVLOG(2) << "OnNewBuffers()";
786 !text_map.empty()); 634 DCHECK(timestamp_offset_during_append_);
787 635
788 // TODO(wolenetz): DCHECK + return false if any of these buffers have UNKNOWN 636 return frame_processor_->ProcessFrames(
789 // type() in upcoming coded frame processing compliant implementation. See 637 audio_buffers, video_buffers, text_map,
790 // http://crbug.com/249422. 638 append_window_start_during_append_, append_window_end_during_append_,
791 639 &new_media_segment_, timestamp_offset_during_append_);
792 AdjustBufferTimestamps(audio_buffers);
793 AdjustBufferTimestamps(video_buffers);
794
795 StreamParser::BufferQueue filtered_audio;
796 StreamParser::BufferQueue filtered_video;
797
798 FilterWithAppendWindow(audio_buffers, &audio_needs_keyframe_,
799 &filtered_audio);
800
801 FilterWithAppendWindow(video_buffers, &video_needs_keyframe_,
802 &filtered_video);
803
804 if ((!filtered_audio.empty() || !filtered_video.empty()) &&
805 new_media_segment_) {
806 // Find the earliest timestamp in the filtered buffers and use that for the
807 // segment start timestamp.
808 TimeDelta segment_timestamp = kNoTimestamp();
809
810 if (!filtered_audio.empty())
811 segment_timestamp = filtered_audio.front()->GetDecodeTimestamp();
812
813 if (!filtered_video.empty() &&
814 (segment_timestamp == kNoTimestamp() ||
815 filtered_video.front()->GetDecodeTimestamp() < segment_timestamp)) {
816 segment_timestamp = filtered_video.front()->GetDecodeTimestamp();
817 }
818
819 new_media_segment_ = false;
820
821 if (audio_)
822 audio_->OnNewMediaSegment(segment_timestamp);
823
824 if (video_)
825 video_->OnNewMediaSegment(segment_timestamp);
826
827 for (TextStreamMap::iterator itr = text_stream_map_.begin();
828 itr != text_stream_map_.end(); ++itr) {
829 itr->second->OnNewMediaSegment(segment_timestamp);
830 }
831 }
832
833 if (!filtered_audio.empty() &&
834 !AppendAndUpdateDuration(audio_, filtered_audio)) {
835 return false;
836 }
837
838 if (!filtered_video.empty() &&
839 !AppendAndUpdateDuration(video_, filtered_video)) {
840 return false;
841 }
842
843 if (text_map.empty())
844 return true;
845
846 // Process any buffers for each of the text tracks in the map.
847 bool all_text_buffers_empty = true;
848 for (StreamParser::TextBufferQueueMap::const_iterator itr = text_map.begin();
849 itr != text_map.end();
850 ++itr) {
851 const StreamParser::BufferQueue text_buffers = itr->second;
852 if (!text_buffers.empty()) {
853 all_text_buffers_empty = false;
854 if (!OnTextBuffers(itr->first, text_buffers))
855 return false;
856 }
857 }
858
859 DCHECK(!all_text_buffers_empty);
860 return true;
861 }
862
863 bool SourceState::OnTextBuffers(
864 StreamParser::TrackId text_track_id,
865 const StreamParser::BufferQueue& buffers) {
866 DCHECK(!buffers.empty());
867
868 TextStreamMap::iterator itr = text_stream_map_.find(text_track_id);
869 if (itr == text_stream_map_.end())
870 return false;
871
872 AdjustBufferTimestamps(buffers);
873
874 StreamParser::BufferQueue filtered_buffers;
875 bool needs_keyframe = false;
876 FilterWithAppendWindow(buffers, &needs_keyframe, &filtered_buffers);
877
878 if (filtered_buffers.empty())
879 return true;
880
881 return AppendAndUpdateDuration(itr->second, filtered_buffers);
882 }
883
884 bool SourceState::AppendAndUpdateDuration(
885 ChunkDemuxerStream* stream,
886 const StreamParser::BufferQueue& buffers) {
887 DCHECK(!buffers.empty());
888
889 if (!stream || !stream->Append(buffers))
890 return false;
891
892 increase_duration_cb_.Run(buffers.back()->timestamp(), stream);
893 return true;
894 }
895
896 void SourceState::FilterWithAppendWindow(
897 const StreamParser::BufferQueue& buffers, bool* needs_keyframe,
898 StreamParser::BufferQueue* filtered_buffers) {
899 DCHECK(needs_keyframe);
900 DCHECK(filtered_buffers);
901
902 // This loop implements steps 1.9, 1.10, & 1.11 of the "Coded frame
903 // processing loop" in the Media Source Extensions spec.
904 // These steps filter out buffers that are not within the "append
905 // window" and handles resyncing on the next random access point
906 // (i.e., next keyframe) if a buffer gets dropped.
907 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
908 itr != buffers.end(); ++itr) {
909 // Filter out buffers that are outside the append window. Anytime
910 // a buffer gets dropped we need to set |*needs_keyframe| to true
911 // because we can only resume decoding at keyframes.
912 TimeDelta presentation_timestamp = (*itr)->timestamp();
913
914 // TODO(acolwell): Change |frame_end_timestamp| value to
915 // |presentation_timestamp + (*itr)->duration()|, like the spec
916 // requires, once frame durations are actually present in all buffers.
917 TimeDelta frame_end_timestamp = presentation_timestamp;
918 if (presentation_timestamp < append_window_start_ ||
919 frame_end_timestamp > append_window_end_) {
920 DVLOG(1) << "Dropping buffer outside append window."
921 << " presentation_timestamp "
922 << presentation_timestamp.InSecondsF();
923 *needs_keyframe = true;
924
925 // This triggers a discontinuity so we need to treat the next frames
926 // appended within the append window as if they were the beginning of a
927 // new segment.
928 new_media_segment_ = true;
929 continue;
930 }
931
932 // If |*needs_keyframe| is true then filter out buffers until we
933 // encounter the next keyframe.
934 if (*needs_keyframe) {
935 if (!(*itr)->IsKeyframe()) {
936 DVLOG(1) << "Dropping non-keyframe. presentation_timestamp "
937 << presentation_timestamp.InSecondsF();
938 continue;
939 }
940
941 *needs_keyframe = false;
942 }
943
944 filtered_buffers->push_back(*itr);
945 }
946 } 640 }
947 641
948 ChunkDemuxerStream::ChunkDemuxerStream(Type type) 642 ChunkDemuxerStream::ChunkDemuxerStream(Type type)
949 : type_(type), 643 : type_(type),
950 state_(UNINITIALIZED) { 644 state_(UNINITIALIZED) {
951 } 645 }
952 646
953 void ChunkDemuxerStream::StartReturningData() { 647 void ChunkDemuxerStream::StartReturningData() {
954 DVLOG(1) << "ChunkDemuxerStream::StartReturningData()"; 648 DVLOG(1) << "ChunkDemuxerStream::StartReturningData()";
955 base::AutoLock auto_lock(lock_); 649 base::AutoLock auto_lock(lock_);
(...skipping 407 matching lines...) Expand 10 before | Expand all | Expand 10 after
1363 if ((has_audio && !source_id_audio_.empty()) || 1057 if ((has_audio && !source_id_audio_.empty()) ||
1364 (has_video && !source_id_video_.empty())) 1058 (has_video && !source_id_video_.empty()))
1365 return kReachedIdLimit; 1059 return kReachedIdLimit;
1366 1060
1367 if (has_audio) 1061 if (has_audio)
1368 source_id_audio_ = id; 1062 source_id_audio_ = id;
1369 1063
1370 if (has_video) 1064 if (has_video)
1371 source_id_video_ = id; 1065 source_id_video_ = id;
1372 1066
1067 scoped_ptr<FrameProcessorBase> frame_processor(new LegacyFrameProcessor(
1068 base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary,
1069 base::Unretained(this))));
1070
1373 scoped_ptr<SourceState> source_state( 1071 scoped_ptr<SourceState> source_state(
1374 new SourceState(stream_parser.Pass(), log_cb_, 1072 new SourceState(stream_parser.Pass(),
1073 frame_processor.Pass(), log_cb_,
1375 base::Bind(&ChunkDemuxer::CreateDemuxerStream, 1074 base::Bind(&ChunkDemuxer::CreateDemuxerStream,
1376 base::Unretained(this)),
1377 base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary,
1378 base::Unretained(this)))); 1075 base::Unretained(this))));
1379 1076
1380 SourceState::NewTextTrackCB new_text_track_cb; 1077 SourceState::NewTextTrackCB new_text_track_cb;
1381 1078
1382 if (enable_text_) { 1079 if (enable_text_) {
1383 new_text_track_cb = base::Bind(&ChunkDemuxer::OnNewTextTrack, 1080 new_text_track_cb = base::Bind(&ChunkDemuxer::OnNewTextTrack,
1384 base::Unretained(this)); 1081 base::Unretained(this));
1385 } 1082 }
1386 1083
1387 source_state->Init( 1084 source_state->Init(
(...skipping 26 matching lines...) Expand all
1414 DCHECK(!id.empty()); 1111 DCHECK(!id.empty());
1415 1112
1416 SourceStateMap::const_iterator itr = source_state_map_.find(id); 1113 SourceStateMap::const_iterator itr = source_state_map_.find(id);
1417 1114
1418 DCHECK(itr != source_state_map_.end()); 1115 DCHECK(itr != source_state_map_.end());
1419 return itr->second->GetBufferedRanges(duration_, state_ == ENDED); 1116 return itr->second->GetBufferedRanges(duration_, state_ == ENDED);
1420 } 1117 }
1421 1118
1422 void ChunkDemuxer::AppendData(const std::string& id, 1119 void ChunkDemuxer::AppendData(const std::string& id,
1423 const uint8* data, size_t length, 1120 const uint8* data, size_t length,
1424 double* timestamp_offset) { 1121 const base::TimeDelta& append_window_start,
1122 const base::TimeDelta& append_window_end,
1123 base::TimeDelta* timestamp_offset) {
1425 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; 1124 DVLOG(1) << "AppendData(" << id << ", " << length << ")";
1426 1125
1427 DCHECK(!id.empty()); 1126 DCHECK(!id.empty());
1127 DCHECK(timestamp_offset);
1428 1128
1429 Ranges<TimeDelta> ranges; 1129 Ranges<TimeDelta> ranges;
1430 1130
1431 { 1131 {
1432 base::AutoLock auto_lock(lock_); 1132 base::AutoLock auto_lock(lock_);
1433 DCHECK_NE(state_, ENDED); 1133 DCHECK_NE(state_, ENDED);
1434 1134
1435 // Capture if any of the SourceBuffers are waiting for data before we start 1135 // Capture if any of the SourceBuffers are waiting for data before we start
1436 // parsing. 1136 // parsing.
1437 bool old_waiting_for_data = IsSeekWaitingForData_Locked(); 1137 bool old_waiting_for_data = IsSeekWaitingForData_Locked();
1438 1138
1439 if (length == 0u) 1139 if (length == 0u)
1440 return; 1140 return;
1441 1141
1442 DCHECK(data); 1142 DCHECK(data);
1443 1143
1444 switch (state_) { 1144 switch (state_) {
1445 case INITIALIZING: 1145 case INITIALIZING:
1446 DCHECK(IsValidId(id)); 1146 DCHECK(IsValidId(id));
1447 if (!source_state_map_[id]->Append(data, length, 1147 if (!source_state_map_[id]->Append(data, length,
1148 append_window_start,
1149 append_window_end,
1448 timestamp_offset)) { 1150 timestamp_offset)) {
1449 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); 1151 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
1450 return; 1152 return;
1451 } 1153 }
1452 break; 1154 break;
1453 1155
1454 case INITIALIZED: { 1156 case INITIALIZED: {
1455 DCHECK(IsValidId(id)); 1157 DCHECK(IsValidId(id));
1456 if (!source_state_map_[id]->Append(data, length, 1158 if (!source_state_map_[id]->Append(data, length,
1159 append_window_start,
1160 append_window_end,
1457 timestamp_offset)) { 1161 timestamp_offset)) {
1458 ReportError_Locked(PIPELINE_ERROR_DECODE); 1162 ReportError_Locked(PIPELINE_ERROR_DECODE);
1459 return; 1163 return;
1460 } 1164 }
1461 } break; 1165 } break;
1462 1166
1463 case PARSE_ERROR: 1167 case PARSE_ERROR:
1464 DVLOG(1) << "AppendData(): Ignoring data after a parse error."; 1168 DVLOG(1) << "AppendData(): Ignoring data after a parse error.";
1465 return; 1169 return;
1466 1170
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
1559 user_specified_duration_ = duration; 1263 user_specified_duration_ = duration;
1560 duration_ = duration_td; 1264 duration_ = duration_td;
1561 host_->SetDuration(duration_); 1265 host_->SetDuration(duration_);
1562 1266
1563 for (SourceStateMap::iterator itr = source_state_map_.begin(); 1267 for (SourceStateMap::iterator itr = source_state_map_.begin();
1564 itr != source_state_map_.end(); ++itr) { 1268 itr != source_state_map_.end(); ++itr) {
1565 itr->second->OnSetDuration(duration_); 1269 itr->second->OnSetDuration(duration_);
1566 } 1270 }
1567 } 1271 }
1568 1272
1569 bool ChunkDemuxer::SetTimestampOffset(const std::string& id, TimeDelta offset) { 1273 bool ChunkDemuxer::IsParsingMediaSegment(const std::string& id) {
1570 base::AutoLock auto_lock(lock_); 1274 base::AutoLock auto_lock(lock_);
1571 DVLOG(1) << "SetTimestampOffset(" << id << ", " << offset.InSecondsF() << ")"; 1275 DVLOG(1) << "IsParsingMediaSegment(" << id << ")";
1572 CHECK(IsValidId(id)); 1276 CHECK(IsValidId(id));
1573 1277
1574 return source_state_map_[id]->SetTimestampOffset(offset); 1278 return source_state_map_[id]->IsParsingMediaSegment();
1575 } 1279 }
1576 1280
1577 bool ChunkDemuxer::SetSequenceMode(const std::string& id, 1281 void ChunkDemuxer::SetSequenceMode(const std::string& id,
1578 bool sequence_mode) { 1282 bool sequence_mode) {
1579 base::AutoLock auto_lock(lock_); 1283 base::AutoLock auto_lock(lock_);
1580 DVLOG(1) << "SetSequenceMode(" << id << ", " << sequence_mode << ")"; 1284 DVLOG(1) << "SetSequenceMode(" << id << ", " << sequence_mode << ")";
1581 CHECK(IsValidId(id)); 1285 CHECK(IsValidId(id));
1582 DCHECK_NE(state_, ENDED); 1286 DCHECK_NE(state_, ENDED);
1583 1287
1584 return source_state_map_[id]->SetSequenceMode(sequence_mode); 1288 source_state_map_[id]->SetSequenceMode(sequence_mode);
1585 } 1289 }
1586 1290
1587 void ChunkDemuxer::MarkEndOfStream(PipelineStatus status) { 1291 void ChunkDemuxer::MarkEndOfStream(PipelineStatus status) {
1588 DVLOG(1) << "MarkEndOfStream(" << status << ")"; 1292 DVLOG(1) << "MarkEndOfStream(" << status << ")";
1589 base::AutoLock auto_lock(lock_); 1293 base::AutoLock auto_lock(lock_);
1590 DCHECK_NE(state_, WAITING_FOR_INIT); 1294 DCHECK_NE(state_, WAITING_FOR_INIT);
1591 DCHECK_NE(state_, ENDED); 1295 DCHECK_NE(state_, ENDED);
1592 1296
1593 if (state_ == SHUTDOWN || state_ == PARSE_ERROR) 1297 if (state_ == SHUTDOWN || state_ == PARSE_ERROR)
1594 return; 1298 return;
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1627 DCHECK_EQ(state_, ENDED); 1331 DCHECK_EQ(state_, ENDED);
1628 1332
1629 ChangeState_Locked(INITIALIZED); 1333 ChangeState_Locked(INITIALIZED);
1630 1334
1631 for (SourceStateMap::iterator itr = source_state_map_.begin(); 1335 for (SourceStateMap::iterator itr = source_state_map_.begin();
1632 itr != source_state_map_.end(); ++itr) { 1336 itr != source_state_map_.end(); ++itr) {
1633 itr->second->UnmarkEndOfStream(); 1337 itr->second->UnmarkEndOfStream();
1634 } 1338 }
1635 } 1339 }
1636 1340
1637 void ChunkDemuxer::SetAppendWindowStart(const std::string& id,
1638 TimeDelta start) {
1639 base::AutoLock auto_lock(lock_);
1640 DVLOG(1) << "SetAppendWindowStart(" << id << ", "
1641 << start.InSecondsF() << ")";
1642 CHECK(IsValidId(id));
1643 source_state_map_[id]->set_append_window_start(start);
1644 }
1645
1646 void ChunkDemuxer::SetAppendWindowEnd(const std::string& id, TimeDelta end) {
1647 base::AutoLock auto_lock(lock_);
1648 DVLOG(1) << "SetAppendWindowEnd(" << id << ", " << end.InSecondsF() << ")";
1649 CHECK(IsValidId(id));
1650 source_state_map_[id]->set_append_window_end(end);
1651 }
1652
1653 void ChunkDemuxer::Shutdown() { 1341 void ChunkDemuxer::Shutdown() {
1654 DVLOG(1) << "Shutdown()"; 1342 DVLOG(1) << "Shutdown()";
1655 base::AutoLock auto_lock(lock_); 1343 base::AutoLock auto_lock(lock_);
1656 1344
1657 if (state_ == SHUTDOWN) 1345 if (state_ == SHUTDOWN)
1658 return; 1346 return;
1659 1347
1660 ShutdownAllStreams(); 1348 ShutdownAllStreams();
1661 1349
1662 ChangeState_Locked(SHUTDOWN); 1350 ChangeState_Locked(SHUTDOWN);
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
1880 } 1568 }
1881 1569
1882 void ChunkDemuxer::ShutdownAllStreams() { 1570 void ChunkDemuxer::ShutdownAllStreams() {
1883 for (SourceStateMap::iterator itr = source_state_map_.begin(); 1571 for (SourceStateMap::iterator itr = source_state_map_.begin();
1884 itr != source_state_map_.end(); ++itr) { 1572 itr != source_state_map_.end(); ++itr) {
1885 itr->second->Shutdown(); 1573 itr->second->Shutdown();
1886 } 1574 }
1887 } 1575 }
1888 1576
1889 } // namespace media 1577 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698