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 "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
11 #include "media/base/audio_decoder_config.h" | 11 #include "media/base/audio_decoder_config.h" |
12 #include "media/base/stream_parser_buffer.h" | 12 #include "media/base/stream_parser_buffer.h" |
13 #include "media/base/video_decoder_config.h" | 13 #include "media/base/video_decoder_config.h" |
14 #include "media/filters/chunk_demuxer_client.h" | 14 #include "media/filters/chunk_demuxer_client.h" |
15 #include "media/mp4/mp4_stream_parser.h" | 15 #include "media/mp4/mp4_stream_parser.h" |
16 #include "media/webm/webm_stream_parser.h" | 16 #include "media/webm/webm_stream_parser.h" |
17 | 17 |
| 18 using base::TimeDelta; |
| 19 |
18 namespace media { | 20 namespace media { |
19 | 21 |
20 struct CodecInfo { | 22 struct CodecInfo { |
21 const char* pattern; | 23 const char* pattern; |
22 DemuxerStream::Type type; | 24 DemuxerStream::Type type; |
23 }; | 25 }; |
24 | 26 |
25 typedef StreamParser* (*ParserFactoryFunction)(); | 27 typedef StreamParser* (*ParserFactoryFunction)(); |
26 | 28 |
27 struct SupportedTypeInfo { | 29 struct SupportedTypeInfo { |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
66 return new mp4::MP4StreamParser(); | 68 return new mp4::MP4StreamParser(); |
67 } | 69 } |
68 | 70 |
69 static const SupportedTypeInfo kSupportedTypeInfo[] = { | 71 static const SupportedTypeInfo kSupportedTypeInfo[] = { |
70 { "video/webm", &BuildWebMParser, kVideoWebMCodecs }, | 72 { "video/webm", &BuildWebMParser, kVideoWebMCodecs }, |
71 { "audio/webm", &BuildWebMParser, kAudioWebMCodecs }, | 73 { "audio/webm", &BuildWebMParser, kAudioWebMCodecs }, |
72 { "video/mp4", &BuildMP4Parser, kVideoMP4Codecs }, | 74 { "video/mp4", &BuildMP4Parser, kVideoMP4Codecs }, |
73 { "audio/mp4", &BuildMP4Parser, kAudioMP4Codecs }, | 75 { "audio/mp4", &BuildMP4Parser, kAudioMP4Codecs }, |
74 }; | 76 }; |
75 | 77 |
| 78 |
| 79 // The fake total size we use for converting times to bytes |
| 80 // for AddBufferedByteRange() calls. |
| 81 // TODO(acolwell): Remove this once Pipeline accepts buffered times |
| 82 // instead of only buffered bytes. |
| 83 enum { kFakeTotalBytes = 1000000 }; |
| 84 |
76 // Checks to see if the specified |type| and |codecs| list are supported. | 85 // Checks to see if the specified |type| and |codecs| list are supported. |
77 // Returns true if |type| and all codecs listed in |codecs| are supported. | 86 // Returns true if |type| and all codecs listed in |codecs| are supported. |
78 // |factory_function| contains a function that can build a StreamParser | 87 // |factory_function| contains a function that can build a StreamParser |
79 // for this type. | 88 // for this type. |
80 // |has_audio| is true if an audio codec was specified. | 89 // |has_audio| is true if an audio codec was specified. |
81 // |has_video| is true if a video codec was specified. | 90 // |has_video| is true if a video codec was specified. |
82 // Returns false otherwise. The values of |factory_function|, |has_audio|, | 91 // Returns false otherwise. The values of |factory_function|, |has_audio|, |
83 // and |has_video| are undefined. | 92 // and |has_video| are undefined. |
84 static bool IsSupported(const std::string& type, | 93 static bool IsSupported(const std::string& type, |
85 std::vector<std::string>& codecs, | 94 std::vector<std::string>& codecs, |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
140 class ChunkDemuxerStream : public DemuxerStream { | 149 class ChunkDemuxerStream : public DemuxerStream { |
141 public: | 150 public: |
142 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; | 151 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; |
143 typedef std::deque<ReadCB> ReadCBQueue; | 152 typedef std::deque<ReadCB> ReadCBQueue; |
144 typedef std::deque<base::Closure> ClosureQueue; | 153 typedef std::deque<base::Closure> ClosureQueue; |
145 | 154 |
146 explicit ChunkDemuxerStream(const AudioDecoderConfig& audio_config); | 155 explicit ChunkDemuxerStream(const AudioDecoderConfig& audio_config); |
147 explicit ChunkDemuxerStream(const VideoDecoderConfig& video_config); | 156 explicit ChunkDemuxerStream(const VideoDecoderConfig& video_config); |
148 | 157 |
149 void StartWaitingForSeek(); | 158 void StartWaitingForSeek(); |
150 void Seek(base::TimeDelta time); | 159 void Seek(TimeDelta time); |
151 bool IsSeekPending() const; | 160 bool IsSeekPending() const; |
152 void Flush(); | 161 void Flush(); |
153 | 162 |
154 // Add buffers to this stream. Buffers are stored in SourceBufferStreams, | 163 // Add buffers to this stream. Buffers are stored in SourceBufferStreams, |
155 // which handle ordering and overlap resolution. | 164 // which handle ordering and overlap resolution. |
156 // Returns true if buffers were successfully added. | 165 // Returns true if buffers were successfully added. |
157 bool Append(const StreamParser::BufferQueue& buffers); | 166 bool Append(const StreamParser::BufferQueue& buffers); |
158 | 167 |
159 // Returns a list of the buffered time ranges. | 168 // Returns a list of the buffered time ranges. |
160 SourceBufferStream::TimespanList GetBufferedTime() const; | 169 Ranges<TimeDelta> GetBufferedTime() const; |
161 | 170 |
162 // Signal to the stream that buffers handed in through subsequent calls to | 171 // Signal to the stream that buffers handed in through subsequent calls to |
163 // Append() belong to a media segment that starts at |start_timestamp|. | 172 // Append() belong to a media segment that starts at |start_timestamp|. |
164 void OnNewMediaSegment(base::TimeDelta start_timestamp); | 173 void OnNewMediaSegment(TimeDelta start_timestamp); |
165 | 174 |
166 // Called when mid-stream config updates occur. | 175 // Called when mid-stream config updates occur. |
167 // Returns true if the new config is accepted. | 176 // Returns true if the new config is accepted. |
168 // Returns false if the new config should trigger an error. | 177 // Returns false if the new config should trigger an error. |
169 bool UpdateAudioConfig(const AudioDecoderConfig& config); | 178 bool UpdateAudioConfig(const AudioDecoderConfig& config); |
170 bool UpdateVideoConfig(const VideoDecoderConfig& config); | 179 bool UpdateVideoConfig(const VideoDecoderConfig& config); |
171 | 180 |
172 void EndOfStream(); | 181 void EndOfStream(); |
173 bool CanEndOfStream() const; | 182 bool CanEndOfStream() const; |
174 void Shutdown(); | 183 void Shutdown(); |
(...skipping 30 matching lines...) Expand all Loading... |
205 Type type_; | 214 Type type_; |
206 | 215 |
207 scoped_ptr<SourceBufferStream> stream_; | 216 scoped_ptr<SourceBufferStream> stream_; |
208 | 217 |
209 mutable base::Lock lock_; | 218 mutable base::Lock lock_; |
210 State state_; | 219 State state_; |
211 ReadCBQueue read_cbs_; | 220 ReadCBQueue read_cbs_; |
212 | 221 |
213 // The timestamp of the current media segment being parsed by | 222 // The timestamp of the current media segment being parsed by |
214 // |stream_parser_|. | 223 // |stream_parser_|. |
215 base::TimeDelta media_segment_start_time_; | 224 TimeDelta media_segment_start_time_; |
216 | 225 |
217 DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream); | 226 DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream); |
218 }; | 227 }; |
219 | 228 |
220 ChunkDemuxerStream::ChunkDemuxerStream(const AudioDecoderConfig& audio_config) | 229 ChunkDemuxerStream::ChunkDemuxerStream(const AudioDecoderConfig& audio_config) |
221 : type_(AUDIO), | 230 : type_(AUDIO), |
222 state_(RETURNING_DATA_FOR_READS), | 231 state_(RETURNING_DATA_FOR_READS), |
223 media_segment_start_time_(kNoTimestamp()) { | 232 media_segment_start_time_(kNoTimestamp()) { |
224 stream_.reset(new SourceBufferStream(audio_config)); | 233 stream_.reset(new SourceBufferStream(audio_config)); |
225 } | 234 } |
(...skipping 11 matching lines...) Expand all Loading... |
237 base::AutoLock auto_lock(lock_); | 246 base::AutoLock auto_lock(lock_); |
238 ChangeState_Locked(WAITING_FOR_SEEK); | 247 ChangeState_Locked(WAITING_FOR_SEEK); |
239 | 248 |
240 std::swap(read_cbs_, read_cbs); | 249 std::swap(read_cbs_, read_cbs); |
241 } | 250 } |
242 | 251 |
243 for (ReadCBQueue::iterator it = read_cbs.begin(); it != read_cbs.end(); ++it) | 252 for (ReadCBQueue::iterator it = read_cbs.begin(); it != read_cbs.end(); ++it) |
244 it->Run(NULL); | 253 it->Run(NULL); |
245 } | 254 } |
246 | 255 |
247 void ChunkDemuxerStream::Seek(base::TimeDelta time) { | 256 void ChunkDemuxerStream::Seek(TimeDelta time) { |
248 base::AutoLock auto_lock(lock_); | 257 base::AutoLock auto_lock(lock_); |
249 | 258 |
250 DCHECK(read_cbs_.empty()); | 259 DCHECK(read_cbs_.empty()); |
251 | 260 |
252 stream_->Seek(time); | 261 stream_->Seek(time); |
253 | 262 |
254 if (state_ == WAITING_FOR_SEEK) | 263 if (state_ == WAITING_FOR_SEEK) |
255 ChangeState_Locked(RETURNING_DATA_FOR_READS); | 264 ChangeState_Locked(RETURNING_DATA_FOR_READS); |
256 } | 265 } |
257 | 266 |
258 bool ChunkDemuxerStream::IsSeekPending() const { | 267 bool ChunkDemuxerStream::IsSeekPending() const { |
259 base::AutoLock auto_lock(lock_); | 268 base::AutoLock auto_lock(lock_); |
260 return stream_->IsSeekPending(); | 269 return stream_->IsSeekPending(); |
261 } | 270 } |
262 | 271 |
263 void ChunkDemuxerStream::Flush() { | 272 void ChunkDemuxerStream::Flush() { |
264 media_segment_start_time_ = kNoTimestamp(); | 273 media_segment_start_time_ = kNoTimestamp(); |
265 } | 274 } |
266 | 275 |
267 void ChunkDemuxerStream::OnNewMediaSegment(base::TimeDelta start_timestamp) { | 276 void ChunkDemuxerStream::OnNewMediaSegment(TimeDelta start_timestamp) { |
268 media_segment_start_time_ = start_timestamp; | 277 media_segment_start_time_ = start_timestamp; |
269 } | 278 } |
270 | 279 |
271 bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) { | 280 bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) { |
272 if (buffers.empty()) | 281 if (buffers.empty()) |
273 return false; | 282 return false; |
274 | 283 |
275 ClosureQueue closures; | 284 ClosureQueue closures; |
276 { | 285 { |
277 base::AutoLock auto_lock(lock_); | 286 base::AutoLock auto_lock(lock_); |
278 DCHECK_NE(state_, SHUTDOWN); | 287 DCHECK_NE(state_, SHUTDOWN); |
279 DCHECK(media_segment_start_time_ != kNoTimestamp()); | 288 DCHECK(media_segment_start_time_ != kNoTimestamp()); |
280 if (!stream_->Append(buffers, media_segment_start_time_)) { | 289 if (!stream_->Append(buffers, media_segment_start_time_)) { |
281 DVLOG(1) << "ChunkDemuxerStream::Append() : stream append failed"; | 290 DVLOG(1) << "ChunkDemuxerStream::Append() : stream append failed"; |
282 return false; | 291 return false; |
283 } | 292 } |
284 CreateReadDoneClosures_Locked(&closures); | 293 CreateReadDoneClosures_Locked(&closures); |
285 } | 294 } |
286 | 295 |
287 for (ClosureQueue::iterator it = closures.begin(); it != closures.end(); ++it) | 296 for (ClosureQueue::iterator it = closures.begin(); it != closures.end(); ++it) |
288 it->Run(); | 297 it->Run(); |
289 | 298 |
290 return true; | 299 return true; |
291 } | 300 } |
292 | 301 |
293 SourceBufferStream::TimespanList ChunkDemuxerStream::GetBufferedTime() const { | 302 Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedTime() const { |
294 base::AutoLock auto_lock(lock_); | 303 base::AutoLock auto_lock(lock_); |
295 return stream_->GetBufferedTime(); | 304 return stream_->GetBufferedTime(); |
296 } | 305 } |
297 | 306 |
298 bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config) { | 307 bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config) { |
299 DCHECK(config.IsValidConfig()); | 308 DCHECK(config.IsValidConfig()); |
300 DCHECK_EQ(type_, AUDIO); | 309 DCHECK_EQ(type_, AUDIO); |
301 | 310 |
302 const AudioDecoderConfig& current_config = | 311 const AudioDecoderConfig& current_config = |
303 stream_->GetCurrentAudioDecoderConfig(); | 312 stream_->GetCurrentAudioDecoderConfig(); |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
456 if (!stream_->GetNextBuffer(&buffer)) | 465 if (!stream_->GetNextBuffer(&buffer)) |
457 return; | 466 return; |
458 closures->push_back(base::Bind(read_cbs_.front(), buffer)); | 467 closures->push_back(base::Bind(read_cbs_.front(), buffer)); |
459 read_cbs_.pop_front(); | 468 read_cbs_.pop_front(); |
460 } | 469 } |
461 } | 470 } |
462 | 471 |
463 ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) | 472 ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) |
464 : state_(WAITING_FOR_INIT), | 473 : state_(WAITING_FOR_INIT), |
465 host_(NULL), | 474 host_(NULL), |
466 client_(client), | 475 client_(client) { |
467 buffered_bytes_(0) { | |
468 DCHECK(client); | 476 DCHECK(client); |
469 } | 477 } |
470 | 478 |
471 void ChunkDemuxer::Initialize(DemuxerHost* host, | 479 void ChunkDemuxer::Initialize(DemuxerHost* host, |
472 const PipelineStatusCB& cb) { | 480 const PipelineStatusCB& cb) { |
473 DVLOG(1) << "Init()"; | 481 DVLOG(1) << "Init()"; |
474 { | 482 { |
475 base::AutoLock auto_lock(lock_); | 483 base::AutoLock auto_lock(lock_); |
476 DCHECK_EQ(state_, WAITING_FOR_INIT); | 484 DCHECK_EQ(state_, WAITING_FOR_INIT); |
477 host_ = host; | 485 host_ = host; |
478 | 486 |
479 ChangeState_Locked(INITIALIZING); | 487 ChangeState_Locked(INITIALIZING); |
480 init_cb_ = cb; | 488 init_cb_ = cb; |
481 } | 489 } |
482 | 490 |
483 client_->DemuxerOpened(this); | 491 client_->DemuxerOpened(this); |
484 } | 492 } |
485 | 493 |
486 void ChunkDemuxer::Stop(const base::Closure& callback) { | 494 void ChunkDemuxer::Stop(const base::Closure& callback) { |
487 DVLOG(1) << "Stop()"; | 495 DVLOG(1) << "Stop()"; |
488 Shutdown(); | 496 Shutdown(); |
489 callback.Run(); | 497 callback.Run(); |
490 } | 498 } |
491 | 499 |
492 void ChunkDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { | 500 void ChunkDemuxer::Seek(TimeDelta time, const PipelineStatusCB& cb) { |
493 DVLOG(1) << "Seek(" << time.InSecondsF() << ")"; | 501 DVLOG(1) << "Seek(" << time.InSecondsF() << ")"; |
494 | 502 |
495 PipelineStatus status = PIPELINE_ERROR_INVALID_STATE; | 503 PipelineStatus status = PIPELINE_ERROR_INVALID_STATE; |
496 { | 504 { |
497 base::AutoLock auto_lock(lock_); | 505 base::AutoLock auto_lock(lock_); |
498 | 506 |
499 if (state_ == INITIALIZED || state_ == ENDED) { | 507 if (state_ == INITIALIZED || state_ == ENDED) { |
500 if (audio_) | 508 if (audio_) |
501 audio_->Seek(time); | 509 audio_->Seek(time); |
502 | 510 |
(...skipping 28 matching lines...) Expand all Loading... |
531 DemuxerStream::Type type) { | 539 DemuxerStream::Type type) { |
532 if (type == DemuxerStream::VIDEO) | 540 if (type == DemuxerStream::VIDEO) |
533 return video_; | 541 return video_; |
534 | 542 |
535 if (type == DemuxerStream::AUDIO) | 543 if (type == DemuxerStream::AUDIO) |
536 return audio_; | 544 return audio_; |
537 | 545 |
538 return NULL; | 546 return NULL; |
539 } | 547 } |
540 | 548 |
541 base::TimeDelta ChunkDemuxer::GetStartTime() const { | 549 TimeDelta ChunkDemuxer::GetStartTime() const { |
542 DVLOG(1) << "GetStartTime()"; | 550 DVLOG(1) << "GetStartTime()"; |
543 // TODO(acolwell) : Fix this so it uses the time on the first packet. | 551 // TODO(acolwell) : Fix this so it uses the time on the first packet. |
544 // (crbug.com/132815) | 552 // (crbug.com/132815) |
545 return base::TimeDelta(); | 553 return TimeDelta(); |
546 } | 554 } |
547 | 555 |
548 void ChunkDemuxer::StartWaitingForSeek() { | 556 void ChunkDemuxer::StartWaitingForSeek() { |
549 DVLOG(1) << "StartWaitingForSeek()"; | 557 DVLOG(1) << "StartWaitingForSeek()"; |
550 base::AutoLock auto_lock(lock_); | 558 base::AutoLock auto_lock(lock_); |
551 DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN); | 559 DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN); |
552 | 560 |
553 if (state_ == SHUTDOWN) | 561 if (state_ == SHUTDOWN) |
554 return; | 562 return; |
555 | 563 |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
620 delete stream_parser_map_[id]; | 628 delete stream_parser_map_[id]; |
621 stream_parser_map_.erase(id); | 629 stream_parser_map_.erase(id); |
622 | 630 |
623 if (source_id_audio_ == id && audio_) | 631 if (source_id_audio_ == id && audio_) |
624 audio_->Shutdown(); | 632 audio_->Shutdown(); |
625 | 633 |
626 if (source_id_video_ == id && video_) | 634 if (source_id_video_ == id && video_) |
627 video_->Shutdown(); | 635 video_->Shutdown(); |
628 } | 636 } |
629 | 637 |
630 bool ChunkDemuxer::GetBufferedRanges(const std::string& id, | 638 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { |
631 Ranges* ranges_out) const { | |
632 DCHECK(!id.empty()); | 639 DCHECK(!id.empty()); |
633 DCHECK_GT(stream_parser_map_.count(id), 0u); | 640 DCHECK_GT(stream_parser_map_.count(id), 0u); |
634 DCHECK(id == source_id_audio_ || id == source_id_video_); | 641 DCHECK(id == source_id_audio_ || id == source_id_video_); |
635 DCHECK(ranges_out); | |
636 | 642 |
637 base::AutoLock auto_lock(lock_); | 643 base::AutoLock auto_lock(lock_); |
638 | 644 |
639 if (id == source_id_audio_ && id != source_id_video_) { | 645 if (id == source_id_audio_ && id != source_id_video_) { |
640 // Only include ranges that have been buffered in |audio_| | 646 // Only include ranges that have been buffered in |audio_| |
641 return CopyIntoRanges(audio_->GetBufferedTime(), ranges_out); | 647 return audio_->GetBufferedTime(); |
642 } | 648 } |
643 | 649 |
644 if (id != source_id_audio_ && id == source_id_video_) { | 650 if (id != source_id_audio_ && id == source_id_video_) { |
645 // Only include ranges that have been buffered in |video_| | 651 // Only include ranges that have been buffered in |video_| |
646 return CopyIntoRanges(video_->GetBufferedTime(), ranges_out); | 652 return video_->GetBufferedTime(); |
647 } | 653 } |
648 | 654 |
| 655 return ComputeIntersection(); |
| 656 } |
| 657 |
| 658 Ranges<TimeDelta> ChunkDemuxer::ComputeIntersection() const { |
| 659 lock_.AssertAcquired(); |
| 660 |
| 661 if (!audio_ || !video_) |
| 662 return Ranges<TimeDelta>(); |
| 663 |
649 // Include ranges that have been buffered in both |audio_| and |video_|. | 664 // Include ranges that have been buffered in both |audio_| and |video_|. |
650 SourceBufferStream::TimespanList audio_ranges = audio_->GetBufferedTime(); | 665 Ranges<TimeDelta> audio_ranges = audio_->GetBufferedTime(); |
651 SourceBufferStream::TimespanList video_ranges = video_->GetBufferedTime(); | 666 Ranges<TimeDelta> video_ranges = video_->GetBufferedTime(); |
652 SourceBufferStream::TimespanList::const_iterator video_ranges_itr = | 667 Ranges<TimeDelta> result = audio_ranges.IntersectionWith(video_ranges); |
653 video_ranges.begin(); | |
654 SourceBufferStream::TimespanList::const_iterator audio_ranges_itr = | |
655 audio_ranges.begin(); | |
656 bool success = false; | |
657 | 668 |
658 while (audio_ranges_itr != audio_ranges.end() && | 669 if (state_ == ENDED && result.size() > 0) { |
659 video_ranges_itr != video_ranges.end()) { | 670 // If appending has ended, extend the last intersection range to include the |
660 // If this is the last range and EndOfStream() was called (i.e. all data | 671 // max end time of the last audio/video range. This allows the buffered |
661 // has been appended), choose the max end point of the ranges. | 672 // information to match the actual time range that will get played out if |
662 bool last_range_after_ended = | 673 // the streams have slightly different lengths. |
663 state_ == ENDED && | 674 TimeDelta audio_start = audio_ranges.start(audio_ranges.size() - 1); |
664 (audio_ranges_itr + 1) == audio_ranges.end() && | 675 TimeDelta audio_end = audio_ranges.end(audio_ranges.size() - 1); |
665 (video_ranges_itr + 1) == video_ranges.end(); | 676 TimeDelta video_start = video_ranges.start(video_ranges.size() - 1); |
| 677 TimeDelta video_end = video_ranges.end(video_ranges.size() - 1); |
666 | 678 |
667 // Audio range start time is within the video range. | 679 // Verify the last audio range overlaps with the last video range. |
668 if ((*audio_ranges_itr).first >= (*video_ranges_itr).first && | 680 // This is enforced by the logic that controls the transition to ENDED. |
669 (*audio_ranges_itr).first <= (*video_ranges_itr).second) { | 681 DCHECK((audio_start <= video_start && video_start <= audio_end) || |
670 AddIntersectionRange(*audio_ranges_itr, *video_ranges_itr, | 682 (video_start <= audio_start && audio_start <= video_end)); |
671 last_range_after_ended, ranges_out); | 683 result.Add(result.end(result.size()-1), std::max(audio_end, video_end)); |
672 audio_ranges_itr++; | |
673 success = true; | |
674 continue; | |
675 } | |
676 | |
677 // Video range start time is within the audio range. | |
678 if ((*video_ranges_itr).first >= (*audio_ranges_itr).first && | |
679 (*video_ranges_itr).first <= (*audio_ranges_itr).second) { | |
680 AddIntersectionRange(*video_ranges_itr, *audio_ranges_itr, | |
681 last_range_after_ended, ranges_out); | |
682 video_ranges_itr++; | |
683 success = true; | |
684 continue; | |
685 } | |
686 | |
687 // No overlap was found. Increment the earliest one and keep looking. | |
688 if ((*audio_ranges_itr).first < (*video_ranges_itr).first) | |
689 audio_ranges_itr++; | |
690 else | |
691 video_ranges_itr++; | |
692 } | 684 } |
693 | 685 |
694 return success; | 686 return result; |
695 } | |
696 | |
697 bool ChunkDemuxer::CopyIntoRanges( | |
698 const SourceBufferStream::TimespanList& timespans, | |
699 Ranges* ranges_out) const { | |
700 for (SourceBufferStream::TimespanList::const_iterator itr = timespans.begin(); | |
701 itr != timespans.end(); ++itr) { | |
702 ranges_out->push_back(*itr); | |
703 } | |
704 return !timespans.empty(); | |
705 } | |
706 | |
707 void ChunkDemuxer::AddIntersectionRange( | |
708 SourceBufferStream::Timespan timespan_a, | |
709 SourceBufferStream::Timespan timespan_b, | |
710 bool last_range_after_ended, | |
711 Ranges* ranges_out) const { | |
712 base::TimeDelta start = timespan_a.first; | |
713 | |
714 // If this is the last range after EndOfStream() was called, choose the later | |
715 // end point of the ranges, otherwise choose the earlier. | |
716 base::TimeDelta end; | |
717 if (last_range_after_ended) | |
718 end = std::max(timespan_a.second, timespan_b.second); | |
719 else | |
720 end = std::min(timespan_a.second, timespan_b.second); | |
721 | |
722 ranges_out->push_back(std::make_pair(start, end)); | |
723 } | 687 } |
724 | 688 |
725 bool ChunkDemuxer::AppendData(const std::string& id, | 689 bool ChunkDemuxer::AppendData(const std::string& id, |
726 const uint8* data, | 690 const uint8* data, |
727 size_t length) { | 691 size_t length) { |
728 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; | 692 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; |
729 | 693 |
730 DCHECK(!id.empty()); | 694 DCHECK(!id.empty()); |
731 DCHECK(data); | 695 DCHECK(data); |
732 DCHECK_GT(length, 0u); | 696 DCHECK_GT(length, 0u); |
733 | 697 |
734 int64 buffered_bytes = 0; | 698 Ranges<TimeDelta> ranges; |
735 | 699 |
736 PipelineStatusCB cb; | 700 PipelineStatusCB cb; |
737 { | 701 { |
738 base::AutoLock auto_lock(lock_); | 702 base::AutoLock auto_lock(lock_); |
739 | 703 |
740 // Capture if the SourceBuffer has a pending seek before we start parsing. | 704 // Capture if the SourceBuffer has a pending seek before we start parsing. |
741 bool old_seek_pending = IsSeekPending_Locked(); | 705 bool old_seek_pending = IsSeekPending_Locked(); |
742 | 706 |
743 switch (state_) { | 707 switch (state_) { |
744 case INITIALIZING: | 708 case INITIALIZING: |
(...skipping 20 matching lines...) Expand all Loading... |
765 DVLOG(1) << "AppendData(): called in unexpected state " << state_; | 729 DVLOG(1) << "AppendData(): called in unexpected state " << state_; |
766 return false; | 730 return false; |
767 } | 731 } |
768 | 732 |
769 // Check to see if data was appended at the pending seek point. This | 733 // Check to see if data was appended at the pending seek point. This |
770 // indicates we have parsed enough data to complete the seek. | 734 // indicates we have parsed enough data to complete the seek. |
771 if (old_seek_pending && !IsSeekPending_Locked() && !seek_cb_.is_null()) { | 735 if (old_seek_pending && !IsSeekPending_Locked() && !seek_cb_.is_null()) { |
772 std::swap(cb, seek_cb_); | 736 std::swap(cb, seek_cb_); |
773 } | 737 } |
774 | 738 |
775 buffered_bytes_ += length; | 739 if (duration_ > TimeDelta() && duration_ != kInfiniteDuration()) { |
776 buffered_bytes = buffered_bytes_; | 740 if (audio_ && !video_) { |
| 741 ranges = audio_->GetBufferedTime(); |
| 742 } else if (!audio_ && video_) { |
| 743 ranges = video_->GetBufferedTime(); |
| 744 } else { |
| 745 ranges = ComputeIntersection(); |
| 746 } |
| 747 } |
777 } | 748 } |
778 | 749 |
779 // Notify the host of 'network activity' because we got data, using a bogus | 750 DCHECK(!ranges.size() || duration_ > TimeDelta()); |
780 // range. | 751 for (size_t i = 0; i < ranges.size(); ++i) { |
781 host_->AddBufferedByteRange(0, buffered_bytes); | 752 // Notify the host of 'network activity' because we got data. |
| 753 int64 start = |
| 754 kFakeTotalBytes * ranges.start(i).InSecondsF() / duration_.InSecondsF(); |
| 755 int64 end = |
| 756 kFakeTotalBytes * ranges.end(i).InSecondsF() / duration_.InSecondsF(); |
| 757 host_->AddBufferedByteRange(start, end); |
| 758 } |
782 | 759 |
783 if (!cb.is_null()) | 760 if (!cb.is_null()) |
784 cb.Run(PIPELINE_OK); | 761 cb.Run(PIPELINE_OK); |
785 | 762 |
786 return true; | 763 return true; |
787 } | 764 } |
788 | 765 |
789 void ChunkDemuxer::Abort(const std::string& id) { | 766 void ChunkDemuxer::Abort(const std::string& id) { |
790 DVLOG(1) << "Abort(" << id << ")"; | 767 DVLOG(1) << "Abort(" << id << ")"; |
791 DCHECK(!id.empty()); | 768 DCHECK(!id.empty()); |
(...skipping 22 matching lines...) Expand all Loading... |
814 | 791 |
815 if (!CanEndOfStream_Locked() && status == PIPELINE_OK) | 792 if (!CanEndOfStream_Locked() && status == PIPELINE_OK) |
816 return false; | 793 return false; |
817 | 794 |
818 if (audio_) | 795 if (audio_) |
819 audio_->EndOfStream(); | 796 audio_->EndOfStream(); |
820 | 797 |
821 if (video_) | 798 if (video_) |
822 video_->EndOfStream(); | 799 video_->EndOfStream(); |
823 | 800 |
824 ChangeState_Locked(ENDED); | |
825 | |
826 if (status != PIPELINE_OK) | 801 if (status != PIPELINE_OK) |
827 ReportError_Locked(status); | 802 ReportError_Locked(status); |
| 803 else |
| 804 ChangeState_Locked(ENDED); |
828 | 805 |
829 return true; | 806 return true; |
830 } | 807 } |
831 | 808 |
832 void ChunkDemuxer::Shutdown() { | 809 void ChunkDemuxer::Shutdown() { |
833 DVLOG(1) << "Shutdown()"; | 810 DVLOG(1) << "Shutdown()"; |
834 PipelineStatusCB cb; | 811 PipelineStatusCB cb; |
835 { | 812 { |
836 base::AutoLock auto_lock(lock_); | 813 base::AutoLock auto_lock(lock_); |
837 | 814 |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
921 | 898 |
922 return seek_pending; | 899 return seek_pending; |
923 } | 900 } |
924 | 901 |
925 bool ChunkDemuxer::CanEndOfStream_Locked() const { | 902 bool ChunkDemuxer::CanEndOfStream_Locked() const { |
926 lock_.AssertAcquired(); | 903 lock_.AssertAcquired(); |
927 return (!audio_ || audio_->CanEndOfStream()) && | 904 return (!audio_ || audio_->CanEndOfStream()) && |
928 (!video_ || video_->CanEndOfStream()); | 905 (!video_ || video_->CanEndOfStream()); |
929 } | 906 } |
930 | 907 |
931 void ChunkDemuxer::OnStreamParserInitDone(bool success, | 908 void ChunkDemuxer::OnStreamParserInitDone(bool success, TimeDelta duration) { |
932 base::TimeDelta duration) { | |
933 DVLOG(1) << "OnSourceBufferInitDone(" << success << ", " | 909 DVLOG(1) << "OnSourceBufferInitDone(" << success << ", " |
934 << duration.InSecondsF() << ")"; | 910 << duration.InSecondsF() << ")"; |
935 lock_.AssertAcquired(); | 911 lock_.AssertAcquired(); |
936 DCHECK_EQ(state_, INITIALIZING); | 912 DCHECK_EQ(state_, INITIALIZING); |
937 if (!success || (!audio_ && !video_)) { | 913 if (!success || (!audio_ && !video_)) { |
938 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); | 914 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
939 return; | 915 return; |
940 } | 916 } |
941 | 917 |
942 if (duration > duration_) | 918 if (duration > duration_) |
943 duration_ = duration; | 919 duration_ = duration; |
944 | 920 |
945 // Wait until all streams have initialized. | 921 // Wait until all streams have initialized. |
946 if ((!source_id_audio_.empty() && !audio_) || | 922 if ((!source_id_audio_.empty() && !audio_) || |
947 (!source_id_video_.empty() && !video_)) | 923 (!source_id_video_.empty() && !video_)) |
948 return; | 924 return; |
949 | 925 |
| 926 if (duration_ > TimeDelta() && duration_ != kInfiniteDuration()) |
| 927 host_->SetTotalBytes(kFakeTotalBytes); |
950 host_->SetDuration(duration_); | 928 host_->SetDuration(duration_); |
951 | 929 |
952 ChangeState_Locked(INITIALIZED); | 930 ChangeState_Locked(INITIALIZED); |
953 PipelineStatusCB cb; | 931 PipelineStatusCB cb; |
954 std::swap(cb, init_cb_); | 932 std::swap(cb, init_cb_); |
955 cb.Run(PIPELINE_OK); | 933 cb.Run(PIPELINE_OK); |
956 } | 934 } |
957 | 935 |
958 bool ChunkDemuxer::OnNewConfigs(bool has_audio, bool has_video, | 936 bool ChunkDemuxer::OnNewConfigs(bool has_audio, bool has_video, |
959 const AudioDecoderConfig& audio_config, | 937 const AudioDecoderConfig& audio_config, |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1017 return video_->Append(buffers); | 995 return video_->Append(buffers); |
1018 } | 996 } |
1019 | 997 |
1020 bool ChunkDemuxer::OnNeedKey(scoped_array<uint8> init_data, | 998 bool ChunkDemuxer::OnNeedKey(scoped_array<uint8> init_data, |
1021 int init_data_size) { | 999 int init_data_size) { |
1022 client_->DemuxerNeedKey(init_data.Pass(), init_data_size); | 1000 client_->DemuxerNeedKey(init_data.Pass(), init_data_size); |
1023 return true; | 1001 return true; |
1024 } | 1002 } |
1025 | 1003 |
1026 void ChunkDemuxer::OnNewMediaSegment(const std::string& source_id, | 1004 void ChunkDemuxer::OnNewMediaSegment(const std::string& source_id, |
1027 base::TimeDelta start_timestamp) { | 1005 TimeDelta start_timestamp) { |
1028 // TODO(vrk): There should be a special case for the first appends where all | 1006 // TODO(vrk): There should be a special case for the first appends where all |
1029 // streams (for both demuxed and muxed case) begin at the earliest stream | 1007 // streams (for both demuxed and muxed case) begin at the earliest stream |
1030 // timestamp. (crbug.com/132815) | 1008 // timestamp. (crbug.com/132815) |
1031 if (audio_ && source_id == source_id_audio_) | 1009 if (audio_ && source_id == source_id_audio_) |
1032 audio_->OnNewMediaSegment(start_timestamp); | 1010 audio_->OnNewMediaSegment(start_timestamp); |
1033 if (video_ && source_id == source_id_video_) | 1011 if (video_ && source_id == source_id_video_) |
1034 video_->OnNewMediaSegment(start_timestamp); | 1012 video_->OnNewMediaSegment(start_timestamp); |
1035 } | 1013 } |
1036 | 1014 |
1037 } // namespace media | 1015 } // namespace media |
OLD | NEW |