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 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/callback_helpers.h" | 12 #include "base/callback_helpers.h" |
13 #include "base/location.h" | 13 #include "base/location.h" |
14 #include "base/message_loop/message_loop_proxy.h" | 14 #include "base/message_loop/message_loop_proxy.h" |
15 #include "media/base/audio_decoder_config.h" | 15 #include "media/base/audio_decoder_config.h" |
16 #include "media/base/bind_to_loop.h" | 16 #include "media/base/bind_to_loop.h" |
17 #include "media/base/stream_parser_buffer.h" | 17 #include "media/base/stream_parser_buffer.h" |
18 #include "media/base/video_decoder_config.h" | 18 #include "media/base/video_decoder_config.h" |
19 #include "media/filters/stream_parser_factory.h" | 19 #include "media/filters/stream_parser_factory.h" |
20 #include "media/webm/webm_webvtt_parser.h" | |
21 | 20 |
22 using base::TimeDelta; | 21 using base::TimeDelta; |
23 | 22 |
24 namespace media { | 23 namespace media { |
25 | 24 |
26 // Contains state belonging to a source id. | 25 // Contains state belonging to a source id. |
27 class SourceState { | 26 class SourceState { |
28 public: | 27 public: |
29 // Callback signature used to create ChunkDemuxerStreams. | 28 // Callback signature used to create ChunkDemuxerStreams. |
30 typedef base::Callback<ChunkDemuxerStream*( | 29 typedef base::Callback<ChunkDemuxerStream*( |
31 DemuxerStream::Type)> CreateDemuxerStreamCB; | 30 DemuxerStream::Type)> CreateDemuxerStreamCB; |
32 | 31 |
33 // Callback signature used to notify ChunkDemuxer of timestamps | 32 // Callback signature used to notify ChunkDemuxer of timestamps |
34 // that may cause the duration to be updated. | 33 // that may cause the duration to be updated. |
35 typedef base::Callback<void( | 34 typedef base::Callback<void( |
36 TimeDelta, ChunkDemuxerStream*)> IncreaseDurationCB; | 35 TimeDelta, ChunkDemuxerStream*)> IncreaseDurationCB; |
37 | 36 |
38 SourceState(scoped_ptr<StreamParser> stream_parser, const LogCB& log_cb, | 37 SourceState(scoped_ptr<StreamParser> stream_parser, const LogCB& log_cb, |
39 const CreateDemuxerStreamCB& create_demuxer_stream_cb, | 38 const CreateDemuxerStreamCB& create_demuxer_stream_cb, |
40 const IncreaseDurationCB& increase_duration_cb); | 39 const IncreaseDurationCB& increase_duration_cb); |
41 | 40 |
41 // Called when a new text track has been parsed, so the demuxer can alert | |
42 // the demuxer host. | |
43 typedef base::Callback<void(ChunkDemuxerStream* stream, | |
44 TextKind kind, | |
45 const std::string& name, | |
46 const std::string& language)> NewTextTrackCB; | |
47 | |
42 void Init(const StreamParser::InitCB& init_cb, | 48 void Init(const StreamParser::InitCB& init_cb, |
43 bool allow_audio, | 49 bool allow_audio, |
44 bool allow_video, | 50 bool allow_video, |
45 const StreamParser::NewTextBuffersCB& text_cb, | |
46 const StreamParser::NeedKeyCB& need_key_cb, | 51 const StreamParser::NeedKeyCB& need_key_cb, |
47 const AddTextTrackCB& add_text_track_cb); | 52 const NewTextTrackCB& new_text_track_cb); |
48 | 53 |
49 // Appends new data to the StreamParser. | 54 // Appends new data to the StreamParser. |
50 // Returns true if the data was successfully appended. Returns false if an | 55 // Returns true if the data was successfully appended. Returns false if an |
51 // error occurred. | 56 // error occurred. |
52 bool Append(const uint8* data, size_t length); | 57 bool Append(const uint8* data, size_t length); |
53 | 58 |
54 // Aborts the current append sequence and resets the parser. | 59 // Aborts the current append sequence and resets the parser. |
55 void Abort(); | 60 void Abort(); |
56 | 61 |
57 // Sets |timestamp_offset_| if possible. | 62 // Sets |timestamp_offset_| if possible. |
(...skipping 25 matching lines...) Expand all Loading... | |
83 | 88 |
84 // Called by the |stream_parser_| when new buffers have been parsed. It | 89 // Called by the |stream_parser_| when new buffers have been parsed. It |
85 // applies |timestamp_offset_| to all buffers in |audio_buffers| and | 90 // applies |timestamp_offset_| to all buffers in |audio_buffers| and |
86 // |video_buffers| and then calls Append() on |audio_| and/or | 91 // |video_buffers| and then calls Append() on |audio_| and/or |
87 // |video_| with the modified buffers. | 92 // |video_| with the modified buffers. |
88 // Returns true on a successful call. Returns false if an error occured while | 93 // Returns true on a successful call. Returns false if an error occured while |
89 // processing the buffers. | 94 // processing the buffers. |
90 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, | 95 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, |
91 const StreamParser::BufferQueue& video_buffers); | 96 const StreamParser::BufferQueue& video_buffers); |
92 | 97 |
98 void OnNewTextTrack(int text_track_num, | |
99 TextKind kind, | |
100 const std::string& name, | |
101 const std::string& language); | |
102 | |
93 // Called by the |stream_parser_| when new text buffers have been parsed. It | 103 // 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 | 104 // applies |timestamp_offset_| to all buffers in |buffers| and then appends |
95 // |new_buffers_cb| with the modified buffers. | 105 // the (modified) buffers to the demuxer stream associated with |
106 // the track having |text_track_number|. | |
96 // Returns true on a successful call. Returns false if an error occured while | 107 // Returns true on a successful call. Returns false if an error occured while |
97 // processing the buffers. | 108 // processing the buffers. |
98 bool OnTextBuffers(const StreamParser::NewTextBuffersCB& new_buffers_cb, | 109 bool OnTextBuffers(int text_track_number, |
99 TextTrack* text_track, | |
100 const StreamParser::BufferQueue& buffers); | 110 const StreamParser::BufferQueue& buffers); |
101 | 111 |
102 // Helper function that adds |timestamp_offset_| to each buffer in |buffers|. | 112 // Helper function that adds |timestamp_offset_| to each buffer in |buffers|. |
103 void AdjustBufferTimestamps(const StreamParser::BufferQueue& buffers); | 113 void AdjustBufferTimestamps(const StreamParser::BufferQueue& buffers); |
104 | 114 |
105 // Filters out buffers that are outside of the append window | 115 // Filters out buffers that are outside of the append window |
106 // [|append_window_start_|, |append_window_end_|). | 116 // [|append_window_start_|, |append_window_end_|). |
107 // |needs_keyframe| is a pointer to the |xxx_need_keyframe_| flag | 117 // |needs_keyframe| is a pointer to the |xxx_need_keyframe_| flag |
108 // associated with the |buffers|. Its state is read an updated as | 118 // associated with the |buffers|. Its state is read an updated as |
109 // this method filters |buffers|. | 119 // this method filters |buffers|. |
(...skipping 25 matching lines...) Expand all Loading... | |
135 | 145 |
136 // The object used to parse appended data. | 146 // The object used to parse appended data. |
137 scoped_ptr<StreamParser> stream_parser_; | 147 scoped_ptr<StreamParser> stream_parser_; |
138 | 148 |
139 ChunkDemuxerStream* audio_; | 149 ChunkDemuxerStream* audio_; |
140 bool audio_needs_keyframe_; | 150 bool audio_needs_keyframe_; |
141 | 151 |
142 ChunkDemuxerStream* video_; | 152 ChunkDemuxerStream* video_; |
143 bool video_needs_keyframe_; | 153 bool video_needs_keyframe_; |
144 | 154 |
155 NewTextTrackCB new_text_track_cb_; | |
156 | |
157 typedef std::map<int, ChunkDemuxerStream*> TextStreamMap; | |
158 TextStreamMap text_stream_map_; | |
159 | |
145 LogCB log_cb_; | 160 LogCB log_cb_; |
146 | 161 |
147 DISALLOW_COPY_AND_ASSIGN(SourceState); | 162 DISALLOW_COPY_AND_ASSIGN(SourceState); |
148 }; | 163 }; |
149 | 164 |
150 class ChunkDemuxerStream : public DemuxerStream { | 165 class ChunkDemuxerStream : public DemuxerStream { |
151 public: | 166 public: |
152 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; | 167 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; |
153 | 168 |
154 explicit ChunkDemuxerStream(Type type); | 169 explicit ChunkDemuxerStream(Type type); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
186 | 201 |
187 // Signal to the stream that buffers handed in through subsequent calls to | 202 // Signal to the stream that buffers handed in through subsequent calls to |
188 // Append() belong to a media segment that starts at |start_timestamp|. | 203 // Append() belong to a media segment that starts at |start_timestamp|. |
189 void OnNewMediaSegment(TimeDelta start_timestamp); | 204 void OnNewMediaSegment(TimeDelta start_timestamp); |
190 | 205 |
191 // Called when midstream config updates occur. | 206 // Called when midstream config updates occur. |
192 // Returns true if the new config is accepted. | 207 // Returns true if the new config is accepted. |
193 // Returns false if the new config should trigger an error. | 208 // Returns false if the new config should trigger an error. |
194 bool UpdateAudioConfig(const AudioDecoderConfig& config, const LogCB& log_cb); | 209 bool UpdateAudioConfig(const AudioDecoderConfig& config, const LogCB& log_cb); |
195 bool UpdateVideoConfig(const VideoDecoderConfig& config, const LogCB& log_cb); | 210 bool UpdateVideoConfig(const VideoDecoderConfig& config, const LogCB& log_cb); |
211 void UpdateTextConfig(const LogCB& log_cb); | |
196 | 212 |
197 void MarkEndOfStream(); | 213 void MarkEndOfStream(); |
198 void UnmarkEndOfStream(); | 214 void UnmarkEndOfStream(); |
199 | 215 |
200 // DemuxerStream methods. | 216 // DemuxerStream methods. |
201 virtual void Read(const ReadCB& read_cb) OVERRIDE; | 217 virtual void Read(const ReadCB& read_cb) OVERRIDE; |
202 virtual Type type() OVERRIDE; | 218 virtual Type type() OVERRIDE; |
203 virtual void EnableBitstreamConverter() OVERRIDE; | 219 virtual void EnableBitstreamConverter() OVERRIDE; |
204 virtual AudioDecoderConfig audio_decoder_config() OVERRIDE; | 220 virtual AudioDecoderConfig audio_decoder_config() OVERRIDE; |
205 virtual VideoDecoderConfig video_decoder_config() OVERRIDE; | 221 virtual VideoDecoderConfig video_decoder_config() OVERRIDE; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
254 video_(NULL), | 270 video_(NULL), |
255 video_needs_keyframe_(true), | 271 video_needs_keyframe_(true), |
256 log_cb_(log_cb) { | 272 log_cb_(log_cb) { |
257 DCHECK(!create_demuxer_stream_cb_.is_null()); | 273 DCHECK(!create_demuxer_stream_cb_.is_null()); |
258 DCHECK(!increase_duration_cb_.is_null()); | 274 DCHECK(!increase_duration_cb_.is_null()); |
259 } | 275 } |
260 | 276 |
261 void SourceState::Init(const StreamParser::InitCB& init_cb, | 277 void SourceState::Init(const StreamParser::InitCB& init_cb, |
262 bool allow_audio, | 278 bool allow_audio, |
263 bool allow_video, | 279 bool allow_video, |
264 const StreamParser::NewTextBuffersCB& text_cb, | |
265 const StreamParser::NeedKeyCB& need_key_cb, | 280 const StreamParser::NeedKeyCB& need_key_cb, |
266 const AddTextTrackCB& add_text_track_cb) { | 281 const NewTextTrackCB& new_text_track_cb) { |
267 StreamParser::NewBuffersCB audio_cb; | 282 StreamParser::NewBuffersCB audio_cb; |
283 new_text_track_cb_ = new_text_track_cb; | |
268 | 284 |
269 stream_parser_->Init(init_cb, | 285 stream_parser_->Init(init_cb, |
270 base::Bind(&SourceState::OnNewConfigs, | 286 base::Bind(&SourceState::OnNewConfigs, |
271 base::Unretained(this), | 287 base::Unretained(this), |
272 allow_audio, | 288 allow_audio, |
273 allow_video), | 289 allow_video), |
274 base::Bind(&SourceState::OnNewBuffers, | 290 base::Bind(&SourceState::OnNewBuffers, |
275 base::Unretained(this)), | 291 base::Unretained(this)), |
276 base::Bind(&SourceState::OnTextBuffers, | 292 base::Bind(&SourceState::OnTextBuffers, |
277 base::Unretained(this), text_cb), | 293 base::Unretained(this)), |
278 need_key_cb, | 294 need_key_cb, |
279 add_text_track_cb, | 295 base::Bind(&SourceState::OnNewTextTrack, |
296 base::Unretained(this)), | |
280 base::Bind(&SourceState::OnNewMediaSegment, | 297 base::Bind(&SourceState::OnNewMediaSegment, |
281 base::Unretained(this)), | 298 base::Unretained(this)), |
282 base::Bind(&SourceState::OnEndOfMediaSegment, | 299 base::Bind(&SourceState::OnEndOfMediaSegment, |
283 base::Unretained(this)), | 300 base::Unretained(this)), |
284 log_cb_); | 301 log_cb_); |
285 } | 302 } |
286 | 303 |
287 bool SourceState::SetTimestampOffset(TimeDelta timestamp_offset) { | 304 bool SourceState::SetTimestampOffset(TimeDelta timestamp_offset) { |
288 if (!can_update_offset_) | 305 if (!can_update_offset_) |
289 return false; | 306 return false; |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
425 segment_timestamp = filtered_video.front()->GetDecodeTimestamp(); | 442 segment_timestamp = filtered_video.front()->GetDecodeTimestamp(); |
426 } | 443 } |
427 | 444 |
428 new_media_segment_ = false; | 445 new_media_segment_ = false; |
429 | 446 |
430 if (audio_) | 447 if (audio_) |
431 audio_->OnNewMediaSegment(segment_timestamp); | 448 audio_->OnNewMediaSegment(segment_timestamp); |
432 | 449 |
433 if (video_) | 450 if (video_) |
434 video_->OnNewMediaSegment(segment_timestamp); | 451 video_->OnNewMediaSegment(segment_timestamp); |
452 | |
453 for (TextStreamMap::iterator itr = text_stream_map_.begin(); | |
454 itr != text_stream_map_.end(); ++itr) { | |
455 itr->second->OnNewMediaSegment(segment_timestamp); | |
456 } | |
435 } | 457 } |
436 | 458 |
437 if (!filtered_audio.empty()) { | 459 if (!filtered_audio.empty()) { |
438 if (!audio_ || !audio_->Append(filtered_audio)) | 460 if (!audio_ || !audio_->Append(filtered_audio)) |
439 return false; | 461 return false; |
440 increase_duration_cb_.Run(filtered_audio.back()->timestamp(), audio_); | 462 increase_duration_cb_.Run(filtered_audio.back()->timestamp(), audio_); |
441 } | 463 } |
442 | 464 |
443 if (!filtered_video.empty()) { | 465 if (!filtered_video.empty()) { |
444 if (!video_ || !video_->Append(filtered_video)) | 466 if (!video_ || !video_->Append(filtered_video)) |
445 return false; | 467 return false; |
446 increase_duration_cb_.Run(filtered_video.back()->timestamp(), video_); | 468 increase_duration_cb_.Run(filtered_video.back()->timestamp(), video_); |
447 } | 469 } |
448 | 470 |
449 return true; | 471 return true; |
450 } | 472 } |
451 | 473 |
452 bool SourceState::OnTextBuffers( | 474 bool SourceState::OnTextBuffers( |
453 const StreamParser::NewTextBuffersCB& new_buffers_cb, | 475 int text_track_number, |
454 TextTrack* text_track, | |
455 const StreamParser::BufferQueue& buffers) { | 476 const StreamParser::BufferQueue& buffers) { |
456 if (new_buffers_cb.is_null()) | 477 if (buffers.empty()) |
acolwell GONE FROM CHROMIUM
2013/10/14 20:42:24
nit: DCHECK(!buffers.empty()) instead.
Matthew Heaney (Chromium)
2013/10/17 05:46:44
Done.
Matthew Heaney (Chromium)
2013/10/17 05:46:44
Done.
| |
457 return false; | 478 return true; |
479 | |
480 TextStreamMap::iterator itr = text_stream_map_.find(text_track_number); | |
481 if (itr == text_stream_map_.end()) | |
482 return true; | |
acolwell GONE FROM CHROMIUM
2013/10/14 20:42:24
nit: I think this should return false. Doesn't thi
Matthew Heaney (Chromium)
2013/10/17 05:46:44
Done.
| |
458 | 483 |
459 AdjustBufferTimestamps(buffers); | 484 AdjustBufferTimestamps(buffers); |
460 | 485 |
461 return new_buffers_cb.Run(text_track, buffers); | 486 return itr->second->Append(buffers); |
487 } | |
488 | |
489 void SourceState::OnNewTextTrack(int text_track_number, | |
acolwell GONE FROM CHROMIUM
2013/10/14 20:42:24
I think this may need to be folded into OnNewConfi
Matthew Heaney (Chromium)
2013/10/17 05:46:44
I did an incomplete implementation. You can check
| |
490 TextKind kind, | |
491 const std::string& name, | |
492 const std::string& language) { | |
493 ChunkDemuxerStream* const text_stream = | |
494 create_demuxer_stream_cb_.Run(DemuxerStream::TEXT); | |
495 | |
496 // Interpret NULL as meaning that inband text tracks are disabled. | |
497 if (text_stream) { | |
498 text_stream->UpdateTextConfig(log_cb_); | |
499 text_stream_map_[text_track_number] = text_stream; | |
500 new_text_track_cb_.Run(text_stream, kind, name, language); | |
501 } | |
462 } | 502 } |
463 | 503 |
464 void SourceState::FilterWithAppendWindow( | 504 void SourceState::FilterWithAppendWindow( |
465 const StreamParser::BufferQueue& buffers, bool* needs_keyframe, | 505 const StreamParser::BufferQueue& buffers, bool* needs_keyframe, |
466 StreamParser::BufferQueue* filtered_buffers) { | 506 StreamParser::BufferQueue* filtered_buffers) { |
467 DCHECK(needs_keyframe); | 507 DCHECK(needs_keyframe); |
468 DCHECK(filtered_buffers); | 508 DCHECK(filtered_buffers); |
469 | 509 |
470 // This loop implements steps 1.9, 1.10, & 1.11 of the "Coded frame | 510 // This loop implements steps 1.9, 1.10, & 1.11 of the "Coded frame |
471 // processing loop" in the Media Source Extensions spec. | 511 // processing loop" in the Media Source Extensions spec. |
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
642 | 682 |
643 if (!stream_) { | 683 if (!stream_) { |
644 DCHECK_EQ(state_, UNINITIALIZED); | 684 DCHECK_EQ(state_, UNINITIALIZED); |
645 stream_.reset(new SourceBufferStream(config, log_cb)); | 685 stream_.reset(new SourceBufferStream(config, log_cb)); |
646 return true; | 686 return true; |
647 } | 687 } |
648 | 688 |
649 return stream_->UpdateVideoConfig(config); | 689 return stream_->UpdateVideoConfig(config); |
650 } | 690 } |
651 | 691 |
692 void ChunkDemuxerStream::UpdateTextConfig(const LogCB& log_cb) { | |
693 DCHECK_EQ(type_, TEXT); | |
694 base::AutoLock auto_lock(lock_); | |
695 | |
696 if (!stream_) { | |
697 DCHECK_EQ(state_, UNINITIALIZED); | |
698 stream_.reset(new SourceBufferStream(log_cb)); | |
699 } | |
acolwell GONE FROM CHROMIUM
2013/10/14 20:42:24
This needs to verify that kind and language haven'
Matthew Heaney (Chromium)
2013/10/17 05:46:44
See if I got OnNewConfigs right, then I can adjust
| |
700 } | |
701 | |
652 void ChunkDemuxerStream::MarkEndOfStream() { | 702 void ChunkDemuxerStream::MarkEndOfStream() { |
653 base::AutoLock auto_lock(lock_); | 703 base::AutoLock auto_lock(lock_); |
654 stream_->MarkEndOfStream(); | 704 stream_->MarkEndOfStream(); |
655 } | 705 } |
656 | 706 |
657 void ChunkDemuxerStream::UnmarkEndOfStream() { | 707 void ChunkDemuxerStream::UnmarkEndOfStream() { |
658 base::AutoLock auto_lock(lock_); | 708 base::AutoLock auto_lock(lock_); |
659 stream_->UnmarkEndOfStream(); | 709 stream_->UnmarkEndOfStream(); |
660 } | 710 } |
661 | 711 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
737 status = DemuxerStream::kOk; | 787 status = DemuxerStream::kOk; |
738 buffer = StreamParserBuffer::CreateEOSBuffer(); | 788 buffer = StreamParserBuffer::CreateEOSBuffer(); |
739 break; | 789 break; |
740 } | 790 } |
741 | 791 |
742 base::ResetAndReturn(&read_cb_).Run(status, buffer); | 792 base::ResetAndReturn(&read_cb_).Run(status, buffer); |
743 } | 793 } |
744 | 794 |
745 ChunkDemuxer::ChunkDemuxer(const base::Closure& open_cb, | 795 ChunkDemuxer::ChunkDemuxer(const base::Closure& open_cb, |
746 const NeedKeyCB& need_key_cb, | 796 const NeedKeyCB& need_key_cb, |
747 const AddTextTrackCB& add_text_track_cb, | 797 bool enable_text, |
748 const LogCB& log_cb) | 798 const LogCB& log_cb) |
749 : state_(WAITING_FOR_INIT), | 799 : state_(WAITING_FOR_INIT), |
750 cancel_next_seek_(false), | 800 cancel_next_seek_(false), |
751 host_(NULL), | 801 host_(NULL), |
752 open_cb_(open_cb), | 802 open_cb_(open_cb), |
753 need_key_cb_(need_key_cb), | 803 need_key_cb_(need_key_cb), |
754 add_text_track_cb_(add_text_track_cb), | 804 enable_text_(enable_text), |
755 log_cb_(log_cb), | 805 log_cb_(log_cb), |
756 duration_(kNoTimestamp()), | 806 duration_(kNoTimestamp()), |
757 user_specified_duration_(-1) { | 807 user_specified_duration_(-1) { |
758 DCHECK(!open_cb_.is_null()); | 808 DCHECK(!open_cb_.is_null()); |
759 DCHECK(!need_key_cb_.is_null()); | 809 DCHECK(!need_key_cb_.is_null()); |
760 } | 810 } |
761 | 811 |
762 void ChunkDemuxer::Initialize(DemuxerHost* host, const PipelineStatusCB& cb) { | 812 void ChunkDemuxer::Initialize(DemuxerHost* host, const PipelineStatusCB& cb) { |
763 DVLOG(1) << "Init()"; | 813 DVLOG(1) << "Init()"; |
764 | 814 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
821 | 871 |
822 // Demuxer implementation. | 872 // Demuxer implementation. |
823 DemuxerStream* ChunkDemuxer::GetStream(DemuxerStream::Type type) { | 873 DemuxerStream* ChunkDemuxer::GetStream(DemuxerStream::Type type) { |
824 base::AutoLock auto_lock(lock_); | 874 base::AutoLock auto_lock(lock_); |
825 if (type == DemuxerStream::VIDEO) | 875 if (type == DemuxerStream::VIDEO) |
826 return video_.get(); | 876 return video_.get(); |
827 | 877 |
828 if (type == DemuxerStream::AUDIO) | 878 if (type == DemuxerStream::AUDIO) |
829 return audio_.get(); | 879 return audio_.get(); |
830 | 880 |
831 return NULL; | 881 if (!enable_text_ || text_stream_set_.empty()) |
acolwell GONE FROM CHROMIUM
2013/10/14 20:42:24
nit: I'd prefer to not return text tracks through
Matthew Heaney (Chromium)
2013/10/17 05:46:44
Done.
| |
882 return NULL; | |
883 | |
884 TextStreamSet::iterator itr = text_stream_set_.begin(); | |
885 return *itr; | |
832 } | 886 } |
833 | 887 |
834 TimeDelta ChunkDemuxer::GetStartTime() const { | 888 TimeDelta ChunkDemuxer::GetStartTime() const { |
835 return TimeDelta(); | 889 return TimeDelta(); |
836 } | 890 } |
837 | 891 |
838 void ChunkDemuxer::StartWaitingForSeek(TimeDelta seek_time) { | 892 void ChunkDemuxer::StartWaitingForSeek(TimeDelta seek_time) { |
839 DVLOG(1) << "StartWaitingForSeek()"; | 893 DVLOG(1) << "StartWaitingForSeek()"; |
840 base::AutoLock auto_lock(lock_); | 894 base::AutoLock auto_lock(lock_); |
841 DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN || | 895 DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN || |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
903 new SourceState(stream_parser.Pass(), log_cb_, | 957 new SourceState(stream_parser.Pass(), log_cb_, |
904 base::Bind(&ChunkDemuxer::CreateDemuxerStream, | 958 base::Bind(&ChunkDemuxer::CreateDemuxerStream, |
905 base::Unretained(this)), | 959 base::Unretained(this)), |
906 base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary, | 960 base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary, |
907 base::Unretained(this)))); | 961 base::Unretained(this)))); |
908 | 962 |
909 source_state->Init( | 963 source_state->Init( |
910 base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this)), | 964 base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this)), |
911 has_audio, | 965 has_audio, |
912 has_video, | 966 has_video, |
913 base::Bind(&ChunkDemuxer::OnTextBuffers, base::Unretained(this)), | |
914 need_key_cb_, | 967 need_key_cb_, |
915 add_text_track_cb_); | 968 base::Bind(&ChunkDemuxer::OnNewTextTrack, base::Unretained(this))); |
916 | 969 |
917 source_state_map_[id] = source_state.release(); | 970 source_state_map_[id] = source_state.release(); |
918 return kOk; | 971 return kOk; |
919 } | 972 } |
920 | 973 |
921 void ChunkDemuxer::RemoveId(const std::string& id) { | 974 void ChunkDemuxer::RemoveId(const std::string& id) { |
922 base::AutoLock auto_lock(lock_); | 975 base::AutoLock auto_lock(lock_); |
923 CHECK(IsValidId(id)); | 976 CHECK(IsValidId(id)); |
924 | 977 |
925 delete source_state_map_[id]; | 978 delete source_state_map_[id]; |
926 source_state_map_.erase(id); | 979 source_state_map_.erase(id); |
927 | 980 |
928 if (source_id_audio_ == id) { | 981 if (source_id_audio_ == id) { |
929 if (audio_) | 982 if (audio_) |
930 audio_->Shutdown(); | 983 audio_->Shutdown(); |
931 source_id_audio_.clear(); | 984 source_id_audio_.clear(); |
932 } | 985 } |
933 | 986 |
934 if (source_id_video_ == id) { | 987 if (source_id_video_ == id) { |
935 if (video_) | 988 if (video_) |
936 video_->Shutdown(); | 989 video_->Shutdown(); |
937 source_id_video_.clear(); | 990 source_id_video_.clear(); |
938 } | 991 } |
992 | |
993 // TODO(matthewjheaney): delete the text streams associated with this id | |
acolwell GONE FROM CHROMIUM
2013/10/14 20:42:24
This needs to be addressed in this CL. If you let
Matthew Heaney (Chromium)
2013/10/17 05:46:44
I did a partial implementation. You can see if I'
| |
939 } | 994 } |
940 | 995 |
941 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { | 996 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { |
942 base::AutoLock auto_lock(lock_); | 997 base::AutoLock auto_lock(lock_); |
943 DCHECK(!id.empty()); | 998 DCHECK(!id.empty()); |
944 DCHECK(IsValidId(id)); | 999 DCHECK(IsValidId(id)); |
945 DCHECK(id == source_id_audio_ || id == source_id_video_); | 1000 DCHECK(id == source_id_audio_ || id == source_id_video_); |
946 | 1001 |
947 if (id == source_id_audio_ && id != source_id_video_) { | 1002 if (id == source_id_audio_ && id != source_id_video_) { |
948 // Only include ranges that have been buffered in |audio_| | 1003 // Only include ranges that have been buffered in |audio_| |
(...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1245 state_ = new_state; | 1300 state_ = new_state; |
1246 } | 1301 } |
1247 | 1302 |
1248 ChunkDemuxer::~ChunkDemuxer() { | 1303 ChunkDemuxer::~ChunkDemuxer() { |
1249 DCHECK_NE(state_, INITIALIZED); | 1304 DCHECK_NE(state_, INITIALIZED); |
1250 for (SourceStateMap::iterator it = source_state_map_.begin(); | 1305 for (SourceStateMap::iterator it = source_state_map_.begin(); |
1251 it != source_state_map_.end(); ++it) { | 1306 it != source_state_map_.end(); ++it) { |
1252 delete it->second; | 1307 delete it->second; |
1253 } | 1308 } |
1254 source_state_map_.clear(); | 1309 source_state_map_.clear(); |
1310 | |
1311 TextStreamSet::iterator it = text_stream_set_.begin(); | |
acolwell GONE FROM CHROMIUM
2013/10/14 20:42:24
This would go away if you let SourceState own the
Matthew Heaney (Chromium)
2013/10/17 05:46:44
Done.
| |
1312 while (it != text_stream_set_.end()) { | |
1313 ChunkDemuxerStream* text_stream = *it; | |
1314 text_stream_set_.erase(it++); | |
1315 delete text_stream; | |
1316 } | |
1255 } | 1317 } |
1256 | 1318 |
1257 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { | 1319 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { |
1258 DVLOG(1) << "ReportError_Locked(" << error << ")"; | 1320 DVLOG(1) << "ReportError_Locked(" << error << ")"; |
1259 lock_.AssertAcquired(); | 1321 lock_.AssertAcquired(); |
1260 DCHECK_NE(error, PIPELINE_OK); | 1322 DCHECK_NE(error, PIPELINE_OK); |
1261 | 1323 |
1262 ChangeState_Locked(PARSE_ERROR); | 1324 ChangeState_Locked(PARSE_ERROR); |
1263 | 1325 |
1264 PipelineStatusCB cb; | 1326 PipelineStatusCB cb; |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1335 return NULL; | 1397 return NULL; |
1336 audio_.reset(new ChunkDemuxerStream(DemuxerStream::AUDIO)); | 1398 audio_.reset(new ChunkDemuxerStream(DemuxerStream::AUDIO)); |
1337 return audio_.get(); | 1399 return audio_.get(); |
1338 break; | 1400 break; |
1339 case DemuxerStream::VIDEO: | 1401 case DemuxerStream::VIDEO: |
1340 if (video_) | 1402 if (video_) |
1341 return NULL; | 1403 return NULL; |
1342 video_.reset(new ChunkDemuxerStream(DemuxerStream::VIDEO)); | 1404 video_.reset(new ChunkDemuxerStream(DemuxerStream::VIDEO)); |
1343 return video_.get(); | 1405 return video_.get(); |
1344 break; | 1406 break; |
1407 case DemuxerStream::TEXT: { | |
1408 // TODO(matthewjheaney): we need to destroy this text stream | |
1409 ChunkDemuxerStream* text_stream = | |
1410 new ChunkDemuxerStream(DemuxerStream::TEXT); | |
1411 text_stream_set_.insert(text_stream); | |
1412 return text_stream; | |
1413 break; | |
1414 } | |
1345 case DemuxerStream::UNKNOWN: | 1415 case DemuxerStream::UNKNOWN: |
1346 case DemuxerStream::NUM_TYPES: | 1416 case DemuxerStream::NUM_TYPES: |
1347 NOTREACHED(); | 1417 NOTREACHED(); |
1348 return NULL; | 1418 return NULL; |
1349 } | 1419 } |
1350 NOTREACHED(); | 1420 NOTREACHED(); |
1351 return NULL; | 1421 return NULL; |
1352 } | 1422 } |
1353 | 1423 |
1354 bool ChunkDemuxer::OnTextBuffers( | 1424 void ChunkDemuxer::OnNewTextTrack(ChunkDemuxerStream* text_stream, |
1355 TextTrack* text_track, | 1425 TextKind kind, |
1356 const StreamParser::BufferQueue& buffers) { | 1426 const std::string& name, |
1427 const std::string& language) { | |
1357 lock_.AssertAcquired(); | 1428 lock_.AssertAcquired(); |
1358 DCHECK_NE(state_, SHUTDOWN); | 1429 DCHECK_NE(state_, SHUTDOWN); |
1359 | 1430 DCHECK(text_stream_set_.find(text_stream) != text_stream_set_.end()); |
1360 // TODO(matthewjheaney): IncreaseDurationIfNecessary | 1431 host_->AddTextStream(text_stream, kind, name, language); |
1361 | |
1362 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin(); | |
1363 itr != buffers.end(); ++itr) { | |
1364 const StreamParserBuffer* const buffer = itr->get(); | |
1365 const TimeDelta start = buffer->timestamp(); | |
1366 const TimeDelta end = start + buffer->duration(); | |
1367 | |
1368 std::string id, settings, content; | |
1369 | |
1370 WebMWebVTTParser::Parse(buffer->data(), | |
1371 buffer->data_size(), | |
1372 &id, &settings, &content); | |
1373 | |
1374 text_track->addWebVTTCue(start, end, id, content, settings); | |
1375 } | |
1376 | |
1377 return true; | |
1378 } | 1432 } |
1379 | 1433 |
1380 bool ChunkDemuxer::IsValidId(const std::string& source_id) const { | 1434 bool ChunkDemuxer::IsValidId(const std::string& source_id) const { |
1381 lock_.AssertAcquired(); | 1435 lock_.AssertAcquired(); |
1382 return source_state_map_.count(source_id) > 0u; | 1436 return source_state_map_.count(source_id) > 0u; |
1383 } | 1437 } |
1384 | 1438 |
1385 void ChunkDemuxer::UpdateDuration(TimeDelta new_duration) { | 1439 void ChunkDemuxer::UpdateDuration(TimeDelta new_duration) { |
1386 DCHECK(duration_ != new_duration); | 1440 DCHECK(duration_ != new_duration); |
1387 user_specified_duration_ = -1; | 1441 user_specified_duration_ = -1; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1421 return video_->GetBufferedRanges(duration_); | 1475 return video_->GetBufferedRanges(duration_); |
1422 return ComputeIntersection(); | 1476 return ComputeIntersection(); |
1423 } | 1477 } |
1424 | 1478 |
1425 void ChunkDemuxer::StartReturningData() { | 1479 void ChunkDemuxer::StartReturningData() { |
1426 if (audio_) | 1480 if (audio_) |
1427 audio_->StartReturningData(); | 1481 audio_->StartReturningData(); |
1428 | 1482 |
1429 if (video_) | 1483 if (video_) |
1430 video_->StartReturningData(); | 1484 video_->StartReturningData(); |
1485 | |
1486 for (TextStreamSet::iterator itr = text_stream_set_.begin(); | |
acolwell GONE FROM CHROMIUM
2013/10/14 20:42:24
nit: Consider iterating over the SourceState objec
Matthew Heaney (Chromium)
2013/10/17 05:46:44
Done.
| |
1487 itr != text_stream_set_.end(); ++itr) { | |
1488 (*itr)->StartReturningData(); | |
1489 } | |
1431 } | 1490 } |
1432 | 1491 |
1433 void ChunkDemuxer::AbortPendingReads() { | 1492 void ChunkDemuxer::AbortPendingReads() { |
1434 if (audio_) | 1493 if (audio_) |
1435 audio_->AbortReads(); | 1494 audio_->AbortReads(); |
1436 | 1495 |
1437 if (video_) | 1496 if (video_) |
1438 video_->AbortReads(); | 1497 video_->AbortReads(); |
1498 | |
1499 for (TextStreamSet::iterator itr = text_stream_set_.begin(); | |
acolwell GONE FROM CHROMIUM
2013/10/14 20:42:24
ditto for this & the other 2 methods below.
Matthew Heaney (Chromium)
2013/10/17 05:46:44
Done.
| |
1500 itr != text_stream_set_.end(); ++itr) { | |
1501 (*itr)->AbortReads(); | |
1502 } | |
1439 } | 1503 } |
1440 | 1504 |
1441 void ChunkDemuxer::SeekAllSources(TimeDelta seek_time) { | 1505 void ChunkDemuxer::SeekAllSources(TimeDelta seek_time) { |
1442 if (audio_) | 1506 if (audio_) |
1443 audio_->Seek(seek_time); | 1507 audio_->Seek(seek_time); |
1444 | 1508 |
1445 if (video_) | 1509 if (video_) |
1446 video_->Seek(seek_time); | 1510 video_->Seek(seek_time); |
1511 | |
1512 for (TextStreamSet::iterator itr = text_stream_set_.begin(); | |
1513 itr != text_stream_set_.end(); ++itr) { | |
1514 (*itr)->Seek(seek_time); | |
1515 } | |
1447 } | 1516 } |
1448 | 1517 |
1449 void ChunkDemuxer::CompletePendingReadsIfPossible() { | 1518 void ChunkDemuxer::CompletePendingReadsIfPossible() { |
1450 if (audio_) | 1519 if (audio_) |
1451 audio_->CompletePendingReadIfPossible(); | 1520 audio_->CompletePendingReadIfPossible(); |
1452 | 1521 |
1453 if (video_) | 1522 if (video_) |
1454 video_->CompletePendingReadIfPossible(); | 1523 video_->CompletePendingReadIfPossible(); |
1524 | |
1525 for (TextStreamSet::iterator itr = text_stream_set_.begin(); | |
1526 itr != text_stream_set_.end(); ++itr) { | |
1527 (*itr)->CompletePendingReadIfPossible(); | |
1528 } | |
1455 } | 1529 } |
1456 | 1530 |
1457 } // namespace media | 1531 } // namespace media |
OLD | NEW |