OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "media/base/text_renderer.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/callback_helpers.h" |
| 9 #include "base/logging.h" |
| 10 #include "base/message_loop/message_loop_proxy.h" |
| 11 #include "media/base/demuxer.h" |
| 12 #include "media/base/demuxer_stream.h" |
| 13 #include "media/base/text_cue.h" |
| 14 #include "media/base/text_decoder.h" |
| 15 |
| 16 namespace media { |
| 17 |
| 18 TextRenderer::TextRenderer( |
| 19 const scoped_refptr<base::MessageLoopProxy>& message_loop, |
| 20 scoped_ptr<TextDecoder> decoder, |
| 21 const AddTextStreamCB& add_text_stream_cb, |
| 22 const CueReadyCB& cue_ready_cb) |
| 23 : message_loop_(message_loop), |
| 24 weak_factory_(this), |
| 25 decoder_(decoder.Pass()), |
| 26 add_text_stream_cb_(add_text_stream_cb), |
| 27 cue_ready_cb_(cue_ready_cb), |
| 28 state_(kUninitialized), |
| 29 pending_read_count_(0), |
| 30 pending_eos_count_(0) { |
| 31 } |
| 32 |
| 33 TextRenderer::~TextRenderer() { |
| 34 DCHECK(state_ == kUninitialized || state_ == kStopped); |
| 35 DCHECK_EQ(pending_read_count_, 0); |
| 36 } |
| 37 |
| 38 void TextRenderer::Initialize(const base::Closure& ended_cb) { |
| 39 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 40 DCHECK(!ended_cb.is_null()); |
| 41 DCHECK_EQ(kUninitialized, state_); |
| 42 DCHECK(read_state_.empty()); |
| 43 DCHECK_EQ(pending_read_count_, 0); |
| 44 DCHECK_EQ(pending_eos_count_, 0); |
| 45 DCHECK(ended_cb_.is_null()); |
| 46 |
| 47 weak_this_ = weak_factory_.GetWeakPtr(); |
| 48 ended_cb_ = ended_cb; |
| 49 state_ = kPaused; |
| 50 decoder_->Initialize(); |
| 51 } |
| 52 |
| 53 void TextRenderer::Play(const base::Closure& callback) { |
| 54 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 55 DCHECK(state_ == kPaused); |
| 56 |
| 57 state_ = kPlaying; |
| 58 callback.Run(); |
| 59 |
| 60 for (ReadStateMap::iterator itr = read_state_.begin(); |
| 61 itr != read_state_.end(); ++itr) { |
| 62 ReadStateMap::value_type& val = *itr; |
| 63 |
| 64 if (val.second == kReadPending) { |
| 65 DCHECK_GT(pending_read_count_, 0); |
| 66 continue; |
| 67 } |
| 68 |
| 69 val.second = kReadPending; |
| 70 ++pending_read_count_; |
| 71 decoder_->Read(val.first, |
| 72 base::Bind(&TextRenderer::CueReady, weak_this_)); |
| 73 } |
| 74 } |
| 75 |
| 76 void TextRenderer::Pause(const base::Closure& callback) { |
| 77 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 78 DCHECK(state_ == kPlaying || state_ == kEnded); |
| 79 pause_cb_ = callback; |
| 80 |
| 81 if (pending_read_count_ <= 0) { |
| 82 state_ = kPaused; |
| 83 base::ResetAndReturn(&pause_cb_).Run(); |
| 84 return; |
| 85 } |
| 86 |
| 87 state_ = kPausePending; |
| 88 } |
| 89 |
| 90 void TextRenderer::Flush(const base::Closure& callback) { |
| 91 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 92 DCHECK_EQ(pending_read_count_, 0); |
| 93 DCHECK(state_ == kPaused || state_ == kEnded); |
| 94 |
| 95 pending_eos_count_ = read_state_.size(); |
| 96 |
| 97 callback.Run(); |
| 98 } |
| 99 |
| 100 void TextRenderer::Stop(const base::Closure& cb) { |
| 101 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 102 DCHECK(!cb.is_null()); |
| 103 DCHECK(state_ == kPlaying || state_ == kPaused || state_ == kEnded); |
| 104 |
| 105 stop_cb_ = cb; |
| 106 |
| 107 if (pending_read_count_ <= 0) { |
| 108 state_ = kStopped; |
| 109 base::ResetAndReturn(&stop_cb_).Run(); |
| 110 return; |
| 111 } |
| 112 |
| 113 state_ = kStopPending; |
| 114 } |
| 115 |
| 116 void TextRenderer::AddTextStream(DemuxerStream* text_stream, |
| 117 TextKind kind, |
| 118 const std::string& label, |
| 119 const std::string& language) { |
| 120 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 121 DCHECK(state_ != kUninitialized && state_ != kStopped); |
| 122 add_text_stream_cb_.Run(text_stream, kind, label, language); |
| 123 ++pending_eos_count_; |
| 124 |
| 125 if (state_ != kPlaying) { |
| 126 read_state_[text_stream] = kReadIdle; |
| 127 } else { |
| 128 read_state_[text_stream] = kReadPending; |
| 129 ++pending_read_count_; |
| 130 |
| 131 decoder_->Read(text_stream, |
| 132 base::Bind(&TextRenderer::CueReady, weak_this_)); |
| 133 } |
| 134 } |
| 135 |
| 136 void TextRenderer::CueReady( |
| 137 DemuxerStream* text_stream, |
| 138 const scoped_refptr<TextCue>& text_cue) { |
| 139 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 140 DCHECK_NE(state_, kUninitialized); |
| 141 DCHECK_NE(state_, kStopped); |
| 142 DCHECK_GT(pending_read_count_, 0); |
| 143 DCHECK_GT(pending_eos_count_, 0); |
| 144 DCHECK_EQ(read_state_[text_stream], kReadPending); |
| 145 |
| 146 --pending_read_count_; |
| 147 read_state_[text_stream] = kReadIdle; |
| 148 |
| 149 switch (state_) { |
| 150 case kPlaying: |
| 151 if (text_cue) |
| 152 break; |
| 153 |
| 154 // A NULL buffer means that we have reached EOS (or there's an error). |
| 155 |
| 156 --pending_eos_count_; |
| 157 |
| 158 if (pending_read_count_ > 0) |
| 159 return; |
| 160 |
| 161 if (pending_eos_count_ <= 0) { |
| 162 state_ = kEnded; |
| 163 ended_cb_.Run(); |
| 164 } |
| 165 |
| 166 return; |
| 167 |
| 168 case kPausePending: |
| 169 if (pending_read_count_ <= 0) { |
| 170 state_ = kPaused; |
| 171 pause_cb_.Run(); |
| 172 } |
| 173 |
| 174 return; |
| 175 |
| 176 case kStopPending: |
| 177 if (pending_read_count_ <= 0) { |
| 178 state_ = kStopped; |
| 179 stop_cb_.Run(); |
| 180 } |
| 181 |
| 182 return; |
| 183 |
| 184 case kPaused: |
| 185 case kStopped: |
| 186 case kUninitialized: |
| 187 default: |
| 188 NOTREACHED(); |
| 189 return; |
| 190 } |
| 191 |
| 192 if (!text_cue->text().empty()) |
| 193 cue_ready_cb_.Run(text_stream, text_cue); |
| 194 |
| 195 read_state_[text_stream] = kReadPending; |
| 196 ++pending_read_count_; |
| 197 decoder_->Read(text_stream, |
| 198 base::Bind(&TextRenderer::CueReady, weak_this_)); |
| 199 } |
| 200 |
| 201 } // namespace media |
OLD | NEW |