Chromium Code Reviews| 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 |