OLD | NEW |
---|---|
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> | 8 #include <deque> |
9 #include <limits> | 9 #include <limits> |
10 | 10 |
(...skipping 19 matching lines...) Expand all Loading... | |
30 typedef base::Callback<ChunkDemuxerStream*( | 30 typedef base::Callback<ChunkDemuxerStream*( |
31 DemuxerStream::Type)> CreateDemuxerStreamCB; | 31 DemuxerStream::Type)> CreateDemuxerStreamCB; |
32 | 32 |
33 // Callback signature used to notify ChunkDemuxer of timestamps | 33 // Callback signature used to notify ChunkDemuxer of timestamps |
34 // that may cause the duration to be updated. | 34 // that may cause the duration to be updated. |
35 typedef base::Callback<void( | 35 typedef base::Callback<void( |
36 TimeDelta, ChunkDemuxerStream*)> IncreaseDurationCB; | 36 TimeDelta, ChunkDemuxerStream*)> IncreaseDurationCB; |
37 | 37 |
38 SourceState(scoped_ptr<StreamParser> stream_parser, const LogCB& log_cb, | 38 SourceState(scoped_ptr<StreamParser> stream_parser, const LogCB& log_cb, |
39 const CreateDemuxerStreamCB& create_demuxer_stream_cb, | 39 const CreateDemuxerStreamCB& create_demuxer_stream_cb, |
40 const IncreaseDurationCB& increase_duration_cb); | 40 const IncreaseDurationCB& increase_duration_cb, |
41 const StreamParser::NewMediaSegmentCB& new_segment_cb); | |
41 | 42 |
42 void Init(const StreamParser::InitCB& init_cb, | 43 void Init(const StreamParser::InitCB& init_cb, |
43 bool allow_audio, | 44 bool allow_audio, |
44 bool allow_video, | 45 bool allow_video, |
45 const StreamParser::NewTextBuffersCB& text_cb, | 46 const StreamParser::NewTextBuffersCB& text_cb, |
46 const StreamParser::NeedKeyCB& need_key_cb, | 47 const StreamParser::NeedKeyCB& need_key_cb, |
47 const AddTextTrackCB& add_text_track_cb, | 48 const AddTextTrackCB& add_text_track_cb); |
48 const StreamParser::NewMediaSegmentCB& new_segment_cb); | |
49 | 49 |
50 // Appends new data to the StreamParser. | 50 // Appends new data to the StreamParser. |
51 // Returns true if the data was successfully appended. Returns false if an | 51 // Returns true if the data was successfully appended. Returns false if an |
52 // error occurred. | 52 // error occurred. |
53 bool Append(const uint8* data, size_t length); | 53 bool Append(const uint8* data, size_t length); |
54 | 54 |
55 // Aborts the current append sequence and resets the parser. | 55 // Aborts the current append sequence and resets the parser. |
56 void Abort(); | 56 void Abort(); |
57 | 57 |
58 // Sets |timestamp_offset_| if possible. | 58 // Sets |timestamp_offset_| if possible. |
59 // Returns if the offset was set. Returns false if the offset could not be | 59 // Returns if the offset was set. Returns false if the offset could not be |
60 // updated at this time. | 60 // updated at this time. |
61 bool SetTimestampOffset(TimeDelta timestamp_offset); | 61 bool SetTimestampOffset(TimeDelta timestamp_offset); |
62 | 62 |
63 TimeDelta timestamp_offset() const { return timestamp_offset_; } | 63 TimeDelta timestamp_offset() const { return timestamp_offset_; } |
64 | 64 |
65 void SetAppendWindowStart(TimeDelta start) { append_window_start_ = start; } | |
scherkus (not reviewing)
2013/07/24 22:47:41
these should be unix_hacker style as they're inlin
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Done.
| |
66 void SetAppendWindowEnd(TimeDelta end) { append_window_end_ = end; } | |
67 | |
65 private: | 68 private: |
66 // Called by the |stream_parser_| when a new initialization segment is | 69 // Called by the |stream_parser_| when a new initialization segment is |
67 // encountered. | 70 // encountered. |
68 // Returns true on a successful call. Returns false if an error occured while | 71 // Returns true on a successful call. Returns false if an error occured while |
69 // processing decoder configurations. | 72 // processing decoder configurations. |
70 bool OnNewConfigs(bool allow_audio, bool allow_video, | 73 bool OnNewConfigs(bool allow_audio, bool allow_video, |
71 const AudioDecoderConfig& audio_config, | 74 const AudioDecoderConfig& audio_config, |
72 const VideoDecoderConfig& video_config); | 75 const VideoDecoderConfig& video_config); |
73 | 76 |
74 // Called by the |stream_parser_| at the beginning of a new media segment. | 77 // Called by the |stream_parser_| at the beginning of a new media segment. |
75 // |timestamp| is the timestamp on the first buffer in the segment. | 78 // |timestamp| is the timestamp on the first buffer in the segment. |
76 // It modifies the state of this object and then calls |new_segment_cb| with | 79 void OnNewMediaSegment(TimeDelta timestamp); |
77 // modified version of |timestamp|. | |
78 void OnNewMediaSegment(const StreamParser::NewMediaSegmentCB& new_segment_cb, | |
79 TimeDelta timestamp); | |
80 | 80 |
81 // Called by the |stream_parser_| at the end of a media segment. | 81 // Called by the |stream_parser_| at the end of a media segment. |
82 void OnEndOfMediaSegment(); | 82 void OnEndOfMediaSegment(); |
83 | 83 |
84 // Called by the |stream_parser_| when new buffers have been parsed. It | 84 // Called by the |stream_parser_| when new buffers have been parsed. It |
85 // applies |timestamp_offset_| to all buffers in |buffers| and then calls | 85 // applies |timestamp_offset_| to all buffers in |audio_buffers| and |
86 // Append() on either |audio_| or |video_| with the modified buffers based on | 86 // |video_buffers| and then calls Append() on |audio_| and/or |
87 // the value of |type|. | 87 // |video_| with the modified buffers. |
88 // Returns true on a successful call. Returns false if an error occured while | 88 // Returns true on a successful call. Returns false if an error occured while |
89 // processing the buffers. | 89 // processing the buffers. |
90 bool OnBuffers(DemuxerStream::Type type, | 90 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, |
91 const StreamParser::BufferQueue& buffers); | 91 const StreamParser::BufferQueue& video_buffers); |
92 | 92 |
93 // Called by the |stream_parser_| when new text buffers have been parsed. It | 93 // Called by the |stream_parser_| when new text buffers have been parsed. It |
94 // applies |timestamp_offset_| to all buffers in |buffers| and then calls | 94 // applies |timestamp_offset_| to all buffers in |buffers| and then calls |
95 // |new_buffers_cb| with the modified buffers. | 95 // |new_buffers_cb| with the modified buffers. |
96 // Returns true on a successful call. Returns false if an error occured while | 96 // Returns true on a successful call. Returns false if an error occured while |
97 // processing the buffers. | 97 // processing the buffers. |
98 bool OnTextBuffers(const StreamParser::NewTextBuffersCB& new_buffers_cb, | 98 bool OnTextBuffers(const StreamParser::NewTextBuffersCB& new_buffers_cb, |
99 TextTrack* text_track, | 99 TextTrack* text_track, |
100 const StreamParser::BufferQueue& buffers); | 100 const StreamParser::BufferQueue& buffers); |
101 | 101 |
102 // Helper function that adds |timestamp_offset_| to each buffer in |buffers|. | 102 // Helper function that adds |timestamp_offset_| to each buffer in |buffers|. |
103 void AdjustBufferTimestamps(const StreamParser::BufferQueue& buffers); | 103 void AdjustBufferTimestamps(const StreamParser::BufferQueue& buffers); |
104 | 104 |
105 // Filters out buffers that are outside of the append window | |
106 // [|append_window_start_|, |append_window_end_|). | |
107 // |needs_keyframe| is a pointer to the |xxx_need_keyframe_| flag | |
108 // associated with the |buffers|. Its state is read an updated as | |
109 // this method filters |buffers|. | |
110 // Returns a filtered queue of buffers that are inside the append window. | |
111 StreamParser::BufferQueue FilterWithAppendWindow( | |
112 const StreamParser::BufferQueue& buffers, bool* needs_keyframe); | |
113 | |
105 CreateDemuxerStreamCB create_demuxer_stream_cb_; | 114 CreateDemuxerStreamCB create_demuxer_stream_cb_; |
106 IncreaseDurationCB increase_duration_cb_; | 115 IncreaseDurationCB increase_duration_cb_; |
116 StreamParser::NewMediaSegmentCB new_segment_cb_; | |
107 | 117 |
108 // The offset to apply to media segment timestamps. | 118 // The offset to apply to media segment timestamps. |
109 TimeDelta timestamp_offset_; | 119 TimeDelta timestamp_offset_; |
110 | 120 |
121 TimeDelta append_window_start_; | |
122 TimeDelta append_window_end_; | |
123 | |
124 // Set to true if the next buffers appended within the append window | |
125 // represent the start of a new media segment. This flag being set | |
126 // triggers a call to |new_segment_cb_| when the new buffers are | |
127 // appended. The flag is set on actual media segment boundaries and | |
128 // when the "append window" filtering causes discontinuities in the | |
129 // appended data. | |
130 bool new_media_segment_; | |
131 | |
111 // Keeps track of whether |timestamp_offset_| can be modified. | 132 // Keeps track of whether |timestamp_offset_| can be modified. |
112 bool can_update_offset_; | 133 bool can_update_offset_; |
113 | 134 |
114 // The object used to parse appended data. | 135 // The object used to parse appended data. |
115 scoped_ptr<StreamParser> stream_parser_; | 136 scoped_ptr<StreamParser> stream_parser_; |
116 | 137 |
117 ChunkDemuxerStream* audio_; | 138 ChunkDemuxerStream* audio_; |
139 bool audio_needs_keyframe_; | |
140 | |
118 ChunkDemuxerStream* video_; | 141 ChunkDemuxerStream* video_; |
142 bool video_needs_keyframe_; | |
119 | 143 |
120 LogCB log_cb_; | 144 LogCB log_cb_; |
121 | 145 |
122 DISALLOW_COPY_AND_ASSIGN(SourceState); | 146 DISALLOW_COPY_AND_ASSIGN(SourceState); |
123 }; | 147 }; |
124 | 148 |
125 class ChunkDemuxerStream : public DemuxerStream { | 149 class ChunkDemuxerStream : public DemuxerStream { |
126 public: | 150 public: |
127 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; | 151 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; |
128 | 152 |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
210 mutable base::Lock lock_; | 234 mutable base::Lock lock_; |
211 State state_; | 235 State state_; |
212 ReadCB read_cb_; | 236 ReadCB read_cb_; |
213 | 237 |
214 DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream); | 238 DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream); |
215 }; | 239 }; |
216 | 240 |
217 SourceState::SourceState(scoped_ptr<StreamParser> stream_parser, | 241 SourceState::SourceState(scoped_ptr<StreamParser> stream_parser, |
218 const LogCB& log_cb, | 242 const LogCB& log_cb, |
219 const CreateDemuxerStreamCB& create_demuxer_stream_cb, | 243 const CreateDemuxerStreamCB& create_demuxer_stream_cb, |
220 const IncreaseDurationCB& increase_duration_cb) | 244 const IncreaseDurationCB& increase_duration_cb, |
245 const StreamParser::NewMediaSegmentCB& new_segment_cb) | |
221 : create_demuxer_stream_cb_(create_demuxer_stream_cb), | 246 : create_demuxer_stream_cb_(create_demuxer_stream_cb), |
222 increase_duration_cb_(increase_duration_cb), | 247 increase_duration_cb_(increase_duration_cb), |
248 new_segment_cb_(new_segment_cb), | |
249 append_window_start_(TimeDelta()), | |
scherkus (not reviewing)
2013/07/24 22:47:41
do you have this here to make it more explicit?
i
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Yeah that was the original idea, but it's a pretty
| |
250 append_window_end_(kInfiniteDuration()), | |
251 new_media_segment_(false), | |
223 can_update_offset_(true), | 252 can_update_offset_(true), |
224 stream_parser_(stream_parser.release()), | 253 stream_parser_(stream_parser.release()), |
225 audio_(NULL), | 254 audio_(NULL), |
255 audio_needs_keyframe_(true), | |
226 video_(NULL), | 256 video_(NULL), |
257 video_needs_keyframe_(true), | |
227 log_cb_(log_cb) { | 258 log_cb_(log_cb) { |
228 DCHECK(!create_demuxer_stream_cb_.is_null()); | 259 DCHECK(!create_demuxer_stream_cb_.is_null()); |
229 DCHECK(!increase_duration_cb_.is_null()); | 260 DCHECK(!increase_duration_cb_.is_null()); |
261 DCHECK(!new_segment_cb_.is_null()); | |
230 } | 262 } |
231 | 263 |
232 void SourceState::Init(const StreamParser::InitCB& init_cb, | 264 void SourceState::Init(const StreamParser::InitCB& init_cb, |
233 bool allow_audio, | 265 bool allow_audio, |
234 bool allow_video, | 266 bool allow_video, |
235 const StreamParser::NewTextBuffersCB& text_cb, | 267 const StreamParser::NewTextBuffersCB& text_cb, |
236 const StreamParser::NeedKeyCB& need_key_cb, | 268 const StreamParser::NeedKeyCB& need_key_cb, |
237 const AddTextTrackCB& add_text_track_cb, | 269 const AddTextTrackCB& add_text_track_cb) { |
238 const StreamParser::NewMediaSegmentCB& new_segment_cb) { | |
239 StreamParser::NewBuffersCB audio_cb; | 270 StreamParser::NewBuffersCB audio_cb; |
240 StreamParser::NewBuffersCB video_cb; | |
241 | |
242 if (allow_audio) { | |
243 audio_cb = base::Bind(&SourceState::OnBuffers, | |
244 base::Unretained(this), DemuxerStream::AUDIO); | |
245 } | |
246 | |
247 if (allow_video) { | |
248 video_cb = base::Bind(&SourceState::OnBuffers, | |
249 base::Unretained(this), DemuxerStream::VIDEO); | |
250 } | |
251 | 271 |
252 stream_parser_->Init(init_cb, | 272 stream_parser_->Init(init_cb, |
253 base::Bind(&SourceState::OnNewConfigs, | 273 base::Bind(&SourceState::OnNewConfigs, |
254 base::Unretained(this), | 274 base::Unretained(this), |
255 allow_audio, | 275 allow_audio, |
256 allow_video), | 276 allow_video), |
257 audio_cb, | 277 base::Bind(&SourceState::OnNewBuffers, |
258 video_cb, | 278 base::Unretained(this)), |
259 base::Bind(&SourceState::OnTextBuffers, | 279 base::Bind(&SourceState::OnTextBuffers, |
260 base::Unretained(this), text_cb), | 280 base::Unretained(this), text_cb), |
261 need_key_cb, | 281 need_key_cb, |
262 add_text_track_cb, | 282 add_text_track_cb, |
263 base::Bind(&SourceState::OnNewMediaSegment, | 283 base::Bind(&SourceState::OnNewMediaSegment, |
264 base::Unretained(this), new_segment_cb), | 284 base::Unretained(this)), |
265 base::Bind(&SourceState::OnEndOfMediaSegment, | 285 base::Bind(&SourceState::OnEndOfMediaSegment, |
266 base::Unretained(this)), | 286 base::Unretained(this)), |
267 log_cb_); | 287 log_cb_); |
268 } | 288 } |
269 | 289 |
270 bool SourceState::SetTimestampOffset(TimeDelta timestamp_offset) { | 290 bool SourceState::SetTimestampOffset(TimeDelta timestamp_offset) { |
271 if (!can_update_offset_) | 291 if (!can_update_offset_) |
272 return false; | 292 return false; |
273 | 293 |
274 timestamp_offset_ = timestamp_offset; | 294 timestamp_offset_ = timestamp_offset; |
275 return true; | 295 return true; |
276 } | 296 } |
277 | 297 |
278 bool SourceState::Append(const uint8* data, size_t length) { | 298 bool SourceState::Append(const uint8* data, size_t length) { |
279 return stream_parser_->Parse(data, length); | 299 return stream_parser_->Parse(data, length); |
280 } | 300 } |
281 | 301 |
282 void SourceState::Abort() { | 302 void SourceState::Abort() { |
283 stream_parser_->Flush(); | 303 stream_parser_->Flush(); |
304 audio_needs_keyframe_ = true; | |
305 video_needs_keyframe_ = true; | |
284 can_update_offset_ = true; | 306 can_update_offset_ = true; |
scherkus (not reviewing)
2013/07/24 22:47:41
sanity check: does new_media_segment_ need to be u
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
No. We will always get an OnNewMediaSegment() call
| |
285 } | 307 } |
286 | 308 |
287 void SourceState::AdjustBufferTimestamps( | 309 void SourceState::AdjustBufferTimestamps( |
288 const StreamParser::BufferQueue& buffers) { | 310 const StreamParser::BufferQueue& buffers) { |
289 if (timestamp_offset_ == TimeDelta()) | 311 if (timestamp_offset_ == TimeDelta()) |
290 return; | 312 return; |
291 | 313 |
292 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin(); | 314 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin(); |
293 itr != buffers.end(); ++itr) { | 315 itr != buffers.end(); ++itr) { |
294 (*itr)->SetDecodeTimestamp( | 316 (*itr)->SetDecodeTimestamp( |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
355 } | 377 } |
356 } | 378 } |
357 | 379 |
358 success &= video_->UpdateVideoConfig(video_config, log_cb_); | 380 success &= video_->UpdateVideoConfig(video_config, log_cb_); |
359 } | 381 } |
360 | 382 |
361 DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed"); | 383 DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed"); |
362 return success; | 384 return success; |
363 } | 385 } |
364 | 386 |
365 void SourceState::OnNewMediaSegment( | 387 void SourceState::OnNewMediaSegment(TimeDelta timestamp) { |
scherkus (not reviewing)
2013/07/24 22:47:41
|timestamp| isn't used anymore other than for DCHE
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Done.
| |
366 const StreamParser::NewMediaSegmentCB& new_segment_cb, | |
367 TimeDelta timestamp) { | |
368 DCHECK(timestamp != kNoTimestamp()); | 388 DCHECK(timestamp != kNoTimestamp()); |
369 DVLOG(2) << "OnNewMediaSegment(" << timestamp.InSecondsF() << ")"; | 389 DVLOG(2) << "SourceState::OnNewMediaSegment(" |
370 | 390 << timestamp.InSecondsF() << ")"; |
371 can_update_offset_ = false; | 391 can_update_offset_ = false; |
372 new_segment_cb.Run(timestamp + timestamp_offset_); | 392 new_media_segment_ = true; |
373 } | 393 } |
374 | 394 |
375 void SourceState::OnEndOfMediaSegment() { | 395 void SourceState::OnEndOfMediaSegment() { |
376 DVLOG(2) << "OnEndOfMediaSegment()"; | 396 DVLOG(2) << "OnEndOfMediaSegment()"; |
377 can_update_offset_ = true; | 397 can_update_offset_ = true; |
398 new_media_segment_ = false; | |
378 } | 399 } |
379 | 400 |
380 bool SourceState::OnBuffers(DemuxerStream::Type type, | 401 bool SourceState::OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, |
381 const StreamParser::BufferQueue& buffers) { | 402 const StreamParser::BufferQueue& video_buffers) { |
382 DCHECK(!buffers.empty()); | 403 DCHECK(!audio_buffers.empty() || !video_buffers.empty()); |
383 AdjustBufferTimestamps(buffers); | 404 AdjustBufferTimestamps(audio_buffers); |
405 AdjustBufferTimestamps(video_buffers); | |
384 | 406 |
385 ChunkDemuxerStream* stream = NULL; | 407 StreamParser::BufferQueue filtered_audio = |
386 switch (type) { | 408 FilterWithAppendWindow(audio_buffers, &audio_needs_keyframe_); |
387 case DemuxerStream::AUDIO: | 409 |
388 stream = audio_; | 410 StreamParser::BufferQueue filtered_video = |
389 break; | 411 FilterWithAppendWindow(video_buffers, &video_needs_keyframe_); |
390 case DemuxerStream::VIDEO: | 412 |
391 stream = video_; | 413 if (filtered_audio.empty() && filtered_video.empty()) |
392 break; | 414 return true; |
393 case DemuxerStream::UNKNOWN: | 415 |
394 case DemuxerStream::NUM_TYPES: | 416 if (new_media_segment_) { |
395 NOTREACHED(); | 417 // Find the earliest timestamp in the filtered buffers and use |
scherkus (not reviewing)
2013/07/24 22:47:41
nit: you can fit a few more words on this line
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Done.
| |
396 return false; | 418 // that for the segment start timestamp. |
419 TimeDelta segment_timestamp = kNoTimestamp(); | |
420 | |
421 if (!filtered_audio.empty()) | |
422 segment_timestamp = filtered_audio.front()->GetDecodeTimestamp(); | |
423 | |
424 if (!filtered_video.empty() && | |
425 (segment_timestamp == kNoTimestamp() || | |
426 filtered_video.front()->GetDecodeTimestamp() < segment_timestamp)) { | |
427 segment_timestamp = filtered_video.front()->GetDecodeTimestamp(); | |
428 } | |
429 | |
430 new_media_segment_ = false; | |
scherkus (not reviewing)
2013/07/24 22:47:41
sanity check: does can_update_offset_ need to be u
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
No. can_update_offset_ tracks the actual media seg
| |
431 new_segment_cb_.Run(segment_timestamp); | |
397 } | 432 } |
398 | 433 |
399 if (!stream->Append(buffers)) | 434 if (!filtered_audio.empty()) { |
400 return false; | 435 if (!audio_ || !audio_->Append(filtered_audio)) |
401 increase_duration_cb_.Run(buffers.back()->timestamp(), stream); | 436 return false; |
437 increase_duration_cb_.Run(filtered_audio.back()->timestamp(), audio_); | |
438 } | |
439 | |
440 if (!filtered_video.empty()) { | |
441 if (!video_ || !video_->Append(filtered_video)) | |
442 return false; | |
443 increase_duration_cb_.Run(filtered_video.back()->timestamp(), video_); | |
444 } | |
445 | |
402 return true; | 446 return true; |
403 } | 447 } |
404 | 448 |
405 bool SourceState::OnTextBuffers( | 449 bool SourceState::OnTextBuffers( |
406 const StreamParser::NewTextBuffersCB& new_buffers_cb, | 450 const StreamParser::NewTextBuffersCB& new_buffers_cb, |
407 TextTrack* text_track, | 451 TextTrack* text_track, |
408 const StreamParser::BufferQueue& buffers) { | 452 const StreamParser::BufferQueue& buffers) { |
409 if (new_buffers_cb.is_null()) | 453 if (new_buffers_cb.is_null()) |
410 return false; | 454 return false; |
411 | 455 |
412 AdjustBufferTimestamps(buffers); | 456 AdjustBufferTimestamps(buffers); |
413 | 457 |
414 return new_buffers_cb.Run(text_track, buffers); | 458 return new_buffers_cb.Run(text_track, buffers); |
415 } | 459 } |
416 | 460 |
461 StreamParser::BufferQueue SourceState::FilterWithAppendWindow( | |
scherkus (not reviewing)
2013/07/24 22:47:41
do we know how large this queue can get?
any reas
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Nope. Done.
| |
462 const StreamParser::BufferQueue& buffers, bool* needs_keyframe) { | |
463 StreamParser::BufferQueue filtered_buffers; | |
464 DCHECK(needs_keyframe); | |
465 | |
466 // This loop implements steps 1.9, 1.10, & 1.11 of the "Coded frame | |
467 // processing loop" in the Media Source Extensions spec. | |
468 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-sourc e.html#sourcebuffer-coded-frame-processing | |
469 // These steps filter out buffers that are not within the "append | |
470 // window" and handles resyncing on the next random access point | |
471 // (i.e., next keyframe) if a buffer gets dropped. | |
472 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin(); | |
473 itr != buffers.end(); ++itr) { | |
474 // Filter out buffers that are outside the append window. Anytime | |
475 // a buffer gets dropped we need to set |*needs_keyframe| to true | |
476 // because we can only resume decoding at keyframes. | |
477 TimeDelta presentation_timestamp = (*itr)->timestamp(); | |
478 | |
479 // TODO(acolwell): Change |frame_end_timestamp| value to | |
480 // |presentation_timestamp + (*itr)->duration()|, like the spec | |
481 // requires, once frame durations are actually present in all buffers. | |
482 TimeDelta frame_end_timestamp = presentation_timestamp; | |
483 if (presentation_timestamp < append_window_start_ || | |
484 frame_end_timestamp > append_window_end_) { | |
485 DVLOG(1) << "Dropping buffer outside append window." | |
486 << " presentation_timestamp " | |
487 << presentation_timestamp.InSecondsF(); | |
488 *needs_keyframe = true; | |
489 | |
490 // This triggers a discontinuity so we need to treat the next frames | |
491 // appended within the append window as if they were the beginning of a | |
492 // new segment. | |
493 new_media_segment_ = true; | |
scherkus (not reviewing)
2013/07/24 22:47:41
sanity check: does can_update_offset_ need to be u
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
No. This is a "virtual" media segment (i.e., a new
| |
494 continue; | |
495 } | |
496 | |
497 // If |*needs_keyframe| is true then filter out buffers until we | |
498 // encounter the next keyframe. | |
499 if (*needs_keyframe) { | |
500 if (!(*itr)->IsKeyframe()) { | |
501 DVLOG(1) << "Dropping non-keyframe. presentation_timestamp " | |
502 << presentation_timestamp.InSecondsF(); | |
503 continue; | |
504 } | |
505 | |
506 *needs_keyframe = false; | |
507 } | |
508 | |
509 filtered_buffers.push_back(*itr); | |
510 } | |
511 | |
512 return filtered_buffers; | |
513 } | |
514 | |
417 ChunkDemuxerStream::ChunkDemuxerStream(Type type) | 515 ChunkDemuxerStream::ChunkDemuxerStream(Type type) |
418 : type_(type), | 516 : type_(type), |
419 state_(UNINITIALIZED) { | 517 state_(UNINITIALIZED) { |
420 } | 518 } |
421 | 519 |
422 void ChunkDemuxerStream::StartReturningData() { | 520 void ChunkDemuxerStream::StartReturningData() { |
423 DVLOG(1) << "ChunkDemuxerStream::StartReturningData()"; | 521 DVLOG(1) << "ChunkDemuxerStream::StartReturningData()"; |
424 base::AutoLock auto_lock(lock_); | 522 base::AutoLock auto_lock(lock_); |
425 DCHECK(read_cb_.is_null()); | 523 DCHECK(read_cb_.is_null()); |
426 ChangeState_Locked(RETURNING_DATA_FOR_READS); | 524 ChangeState_Locked(RETURNING_DATA_FOR_READS); |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
507 | 605 |
508 // Clamp the end of the stream's buffered ranges to fit within the duration. | 606 // Clamp the end of the stream's buffered ranges to fit within the duration. |
509 // This can be done by intersecting the stream's range with the valid time | 607 // This can be done by intersecting the stream's range with the valid time |
510 // range. | 608 // range. |
511 Ranges<TimeDelta> valid_time_range; | 609 Ranges<TimeDelta> valid_time_range; |
512 valid_time_range.Add(range.start(0), duration); | 610 valid_time_range.Add(range.start(0), duration); |
513 return range.IntersectionWith(valid_time_range); | 611 return range.IntersectionWith(valid_time_range); |
514 } | 612 } |
515 | 613 |
516 void ChunkDemuxerStream::OnNewMediaSegment(TimeDelta start_timestamp) { | 614 void ChunkDemuxerStream::OnNewMediaSegment(TimeDelta start_timestamp) { |
615 DVLOG(2) << "ChunkDemuxerStream::OnNewMediaSegment(" | |
616 << start_timestamp.InSecondsF() << ")"; | |
517 base::AutoLock auto_lock(lock_); | 617 base::AutoLock auto_lock(lock_); |
518 stream_->OnNewMediaSegment(start_timestamp); | 618 stream_->OnNewMediaSegment(start_timestamp); |
519 } | 619 } |
520 | 620 |
521 bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config, | 621 bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config, |
522 const LogCB& log_cb) { | 622 const LogCB& log_cb) { |
523 DCHECK(config.IsValidConfig()); | 623 DCHECK(config.IsValidConfig()); |
524 DCHECK_EQ(type_, AUDIO); | 624 DCHECK_EQ(type_, AUDIO); |
525 base::AutoLock auto_lock(lock_); | 625 base::AutoLock auto_lock(lock_); |
526 if (!stream_) { | 626 if (!stream_) { |
(...skipping 268 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
795 source_id_audio_ = id; | 895 source_id_audio_ = id; |
796 | 896 |
797 if (has_video) | 897 if (has_video) |
798 source_id_video_ = id; | 898 source_id_video_ = id; |
799 | 899 |
800 scoped_ptr<SourceState> source_state( | 900 scoped_ptr<SourceState> source_state( |
801 new SourceState(stream_parser.Pass(), log_cb_, | 901 new SourceState(stream_parser.Pass(), log_cb_, |
802 base::Bind(&ChunkDemuxer::CreateDemuxerStream, | 902 base::Bind(&ChunkDemuxer::CreateDemuxerStream, |
803 base::Unretained(this)), | 903 base::Unretained(this)), |
804 base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary, | 904 base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary, |
805 base::Unretained(this)))); | 905 base::Unretained(this)), |
906 base::Bind(&ChunkDemuxer::OnNewMediaSegment, | |
907 base::Unretained(this), id))); | |
908 | |
806 source_state->Init( | 909 source_state->Init( |
807 base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this)), | 910 base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this)), |
808 has_audio, | 911 has_audio, |
809 has_video, | 912 has_video, |
810 base::Bind(&ChunkDemuxer::OnTextBuffers, base::Unretained(this)), | 913 base::Bind(&ChunkDemuxer::OnTextBuffers, base::Unretained(this)), |
811 need_key_cb_, | 914 need_key_cb_, |
812 add_text_track_cb_, | 915 add_text_track_cb_); |
813 base::Bind(&ChunkDemuxer::OnNewMediaSegment, base::Unretained(this), id)); | |
814 | 916 |
815 source_state_map_[id] = source_state.release(); | 917 source_state_map_[id] = source_state.release(); |
816 return kOk; | 918 return kOk; |
817 } | 919 } |
818 | 920 |
819 void ChunkDemuxer::RemoveId(const std::string& id) { | 921 void ChunkDemuxer::RemoveId(const std::string& id) { |
820 base::AutoLock auto_lock(lock_); | 922 base::AutoLock auto_lock(lock_); |
821 CHECK(IsValidId(id)); | 923 CHECK(IsValidId(id)); |
822 | 924 |
823 delete source_state_map_[id]; | 925 delete source_state_map_[id]; |
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1086 | 1188 |
1087 ChangeState_Locked(INITIALIZED); | 1189 ChangeState_Locked(INITIALIZED); |
1088 | 1190 |
1089 if (audio_) | 1191 if (audio_) |
1090 audio_->UnmarkEndOfStream(); | 1192 audio_->UnmarkEndOfStream(); |
1091 | 1193 |
1092 if (video_) | 1194 if (video_) |
1093 video_->UnmarkEndOfStream(); | 1195 video_->UnmarkEndOfStream(); |
1094 } | 1196 } |
1095 | 1197 |
1198 void ChunkDemuxer::SetAppendWindowStart(const std::string& id, | |
1199 TimeDelta start) { | |
1200 base::AutoLock auto_lock(lock_); | |
1201 DVLOG(1) << "SetAppendWindowStart(" << id << ", " | |
1202 << start.InSecondsF() << ")"; | |
1203 CHECK(IsValidId(id)); | |
1204 source_state_map_[id]->SetAppendWindowStart(start); | |
1205 } | |
1206 | |
1207 void ChunkDemuxer::SetAppendWindowEnd(const std::string& id, TimeDelta end) { | |
1208 base::AutoLock auto_lock(lock_); | |
1209 DVLOG(1) << "SetAppendWindowEnd(" << id << ", " << end.InSecondsF() << ")"; | |
1210 CHECK(IsValidId(id)); | |
1211 source_state_map_[id]->SetAppendWindowEnd(end); | |
1212 } | |
1213 | |
1096 void ChunkDemuxer::Shutdown() { | 1214 void ChunkDemuxer::Shutdown() { |
1097 DVLOG(1) << "Shutdown()"; | 1215 DVLOG(1) << "Shutdown()"; |
1098 base::AutoLock auto_lock(lock_); | 1216 base::AutoLock auto_lock(lock_); |
1099 | 1217 |
1100 if (state_ == SHUTDOWN) | 1218 if (state_ == SHUTDOWN) |
1101 return; | 1219 return; |
1102 | 1220 |
1103 if (audio_) | 1221 if (audio_) |
1104 audio_->Shutdown(); | 1222 audio_->Shutdown(); |
1105 | 1223 |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1255 | 1373 |
1256 text_track->addWebVTTCue(start, end, id, content, settings); | 1374 text_track->addWebVTTCue(start, end, id, content, settings); |
1257 } | 1375 } |
1258 | 1376 |
1259 return true; | 1377 return true; |
1260 } | 1378 } |
1261 | 1379 |
1262 void ChunkDemuxer::OnNewMediaSegment(const std::string& source_id, | 1380 void ChunkDemuxer::OnNewMediaSegment(const std::string& source_id, |
1263 TimeDelta timestamp) { | 1381 TimeDelta timestamp) { |
1264 DCHECK(timestamp != kNoTimestamp()); | 1382 DCHECK(timestamp != kNoTimestamp()); |
1265 DVLOG(2) << "OnNewMediaSegment(" << source_id << ", " | 1383 DVLOG(2) << "ChunkDemuxer::OnNewMediaSegment(" << source_id << ", " |
scherkus (not reviewing)
2013/07/24 22:47:41
FYI the DVLOGs in this class are inconsistent re:
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Actually this method doesn't need to exist anymore
| |
1266 << timestamp.InSecondsF() << ")"; | 1384 << timestamp.InSecondsF() << ")"; |
1267 lock_.AssertAcquired(); | 1385 lock_.AssertAcquired(); |
1268 | 1386 |
1269 CHECK(IsValidId(source_id)); | 1387 CHECK(IsValidId(source_id)); |
1270 if (audio_ && source_id == source_id_audio_) | 1388 if (audio_ && source_id == source_id_audio_) |
1271 audio_->OnNewMediaSegment(timestamp); | 1389 audio_->OnNewMediaSegment(timestamp); |
1272 if (video_ && source_id == source_id_video_) | 1390 if (video_ && source_id == source_id_video_) |
1273 video_->OnNewMediaSegment(timestamp); | 1391 video_->OnNewMediaSegment(timestamp); |
1274 } | 1392 } |
1275 | 1393 |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1344 | 1462 |
1345 void ChunkDemuxer::CompletePendingReadsIfPossible() { | 1463 void ChunkDemuxer::CompletePendingReadsIfPossible() { |
1346 if (audio_) | 1464 if (audio_) |
1347 audio_->CompletePendingReadIfPossible(); | 1465 audio_->CompletePendingReadIfPossible(); |
1348 | 1466 |
1349 if (video_) | 1467 if (video_) |
1350 video_->CompletePendingReadIfPossible(); | 1468 video_->CompletePendingReadIfPossible(); |
1351 } | 1469 } |
1352 | 1470 |
1353 } // namespace media | 1471 } // namespace media |
OLD | NEW |