1 // Copyright 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/single_thread_task_runner.h" 11 #include "base/stl_util.h" 12 #include "media/base/bind_to_current_loop.h" 13 #include "media/base/decoder_buffer.h" 14 #include "media/base/demuxer.h" 15 #include "media/base/demuxer_stream.h" 16 #include "media/base/text_cue.h" 17 18 namespace media { 19 20 TextRenderer::TextRenderer( 21 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 22 const AddTextTrackCB& add_text_track_cb) 23 : task_runner_(task_runner), 24 add_text_track_cb_(add_text_track_cb), 25 state_(kUninitialized), 26 pending_read_count_(0), 27 weak_factory_(this) {} 28 29 TextRenderer::~TextRenderer() { 30 DCHECK(state_ == kUninitialized || 31 state_ == kStopped) << "state_ " << state_; 32 DCHECK_EQ(pending_read_count_, 0); 33 STLDeleteValues(&text_track_state_map_); 34 } 35 36 void TextRenderer::Initialize(const base::Closure& ended_cb) { 37 DCHECK(task_runner_->BelongsToCurrentThread()); 38 DCHECK(!ended_cb.is_null()); 39 DCHECK_EQ(kUninitialized, state_) << "state_ " << state_; 40 DCHECK(text_track_state_map_.empty()); 41 DCHECK_EQ(pending_read_count_, 0); 42 DCHECK(pending_eos_set_.empty()); 43 DCHECK(ended_cb_.is_null()); 44 45 ended_cb_ = ended_cb; 46 state_ = kPaused; 47 } 48 49 void TextRenderer::Play(const base::Closure& callback) { 50 DCHECK(task_runner_->BelongsToCurrentThread()); 51 DCHECK_EQ(state_, kPaused) << "state_ " << state_; 52 53 for (TextTrackStateMap::iterator itr = text_track_state_map_.begin(); 54 itr != text_track_state_map_.end(); ++itr) { 55 TextTrackState* state = itr->second; 56 if (state->read_state == TextTrackState::kReadPending) { 57 DCHECK_GT(pending_read_count_, 0); 58 continue; 59 } 60 61 Read(state, itr->first); 62 } 63 64 state_ = kPlaying; 65 callback.Run(); 66 } 67 68 void TextRenderer::Pause(const base::Closure& callback) { 69 DCHECK(task_runner_->BelongsToCurrentThread()); 70 DCHECK(state_ == kPlaying || state_ == kEnded) << "state_ " << state_; 71 DCHECK_GE(pending_read_count_, 0); 72 pause_cb_ = callback; 73 74 if (pending_read_count_ == 0) { 75 state_ = kPaused; 76 base::ResetAndReturn(&pause_cb_).Run(); 77 return; 78 } 79 80 state_ = kPausePending; 81 } 82 83 void TextRenderer::Flush(const base::Closure& callback) { 84 DCHECK(task_runner_->BelongsToCurrentThread()); 85 DCHECK_EQ(pending_read_count_, 0); 86 DCHECK(state_ == kPaused) << "state_ " << state_; 87 88 for (TextTrackStateMap::iterator itr = text_track_state_map_.begin(); 89 itr != text_track_state_map_.end(); ++itr) { 90 pending_eos_set_.insert(itr->first); 91 itr->second->text_ranges_.Reset(); 92 } 93 DCHECK_EQ(pending_eos_set_.size(), text_track_state_map_.size()); 94 callback.Run(); 95 } 96 97 void TextRenderer::Stop(const base::Closure& cb) { 98 DCHECK(task_runner_->BelongsToCurrentThread()); 99 DCHECK(!cb.is_null()); 100 DCHECK(state_ == kPlaying || 101 state_ == kPausePending || 102 state_ == kPaused || 103 state_ == kEnded) << "state_ " << state_; 104 DCHECK_GE(pending_read_count_, 0); 105 106 stop_cb_ = cb; 107 108 if (pending_read_count_ == 0) { 109 state_ = kStopped; 110 base::ResetAndReturn(&stop_cb_).Run(); 111 return; 112 } 113 114 state_ = kStopPending; 115 } 116 117 void TextRenderer::AddTextStream(DemuxerStream* text_stream, 118 const TextTrackConfig& config) { 119 DCHECK(task_runner_->BelongsToCurrentThread()); 120 DCHECK(state_ != kUninitialized) << "state_ " << state_; 121 DCHECK_NE(state_, kStopPending); 122 DCHECK_NE(state_, kStopped); 123 DCHECK(text_track_state_map_.find(text_stream) == 124 text_track_state_map_.end()); 125 DCHECK(pending_eos_set_.find(text_stream) == 126 pending_eos_set_.end()); 127 128 AddTextTrackDoneCB done_cb = 129 BindToCurrentLoop(base::Bind(&TextRenderer::OnAddTextTrackDone, 130 weak_factory_.GetWeakPtr(), 131 text_stream)); 132 133 add_text_track_cb_.Run(config, done_cb); 134 } 135 136 void TextRenderer::RemoveTextStream(DemuxerStream* text_stream) { 137 DCHECK(task_runner_->BelongsToCurrentThread()); 138 139 TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream); 140 DCHECK(itr != text_track_state_map_.end()); 141 142 TextTrackState* state = itr->second; 143 DCHECK_EQ(state->read_state, TextTrackState::kReadIdle); 144 delete state; 145 text_track_state_map_.erase(itr); 146 147 pending_eos_set_.erase(text_stream); 148 } 149 150 bool TextRenderer::HasTracks() const { 151 DCHECK(task_runner_->BelongsToCurrentThread()); 152 return !text_track_state_map_.empty(); 153 } 154 155 void TextRenderer::BufferReady( 156 DemuxerStream* stream, 157 DemuxerStream::Status status, 158 const scoped_refptr<DecoderBuffer>& input) { 159 DCHECK(task_runner_->BelongsToCurrentThread()); 160 DCHECK_NE(status, DemuxerStream::kConfigChanged); 161 162 if (status == DemuxerStream::kAborted) { 163 DCHECK(!input); 164 DCHECK_GT(pending_read_count_, 0); 165 DCHECK(pending_eos_set_.find(stream) != pending_eos_set_.end()); 166 167 TextTrackStateMap::iterator itr = text_track_state_map_.find(stream); 168 DCHECK(itr != text_track_state_map_.end()); 169 170 TextTrackState* state = itr->second; 171 DCHECK_EQ(state->read_state, TextTrackState::kReadPending); 172 173 --pending_read_count_; 174 state->read_state = TextTrackState::kReadIdle; 175 176 switch (state_) { 177 case kPlaying: 178 return; 179 180 case kPausePending: 181 if (pending_read_count_ == 0) { 182 state_ = kPaused; 183 base::ResetAndReturn(&pause_cb_).Run(); 184 } 185 186 return; 187 188 case kStopPending: 189 if (pending_read_count_ == 0) { 190 state_ = kStopped; 191 base::ResetAndReturn(&stop_cb_).Run(); 192 } 193 194 return; 195 196 case kPaused: 197 case kStopped: 198 case kUninitialized: 199 case kEnded: 200 NOTREACHED(); 201 return; 202 } 203 204 NOTREACHED(); 205 return; 206 } 207 208 if (input->end_of_stream()) { 209 CueReady(stream, NULL); 210 return; 211 } 212 213 DCHECK_EQ(status, DemuxerStream::kOk); 214 DCHECK_GE(input->side_data_size(), 2); 215 216 // The side data contains both the cue id and cue settings, 217 // each terminated with a NUL. 218 const char* id_ptr = reinterpret_cast<const char*>(input->side_data()); 219 size_t id_len = strlen(id_ptr); 220 std::string id(id_ptr, id_len); 221 222 const char* settings_ptr = id_ptr + id_len + 1; 223 size_t settings_len = strlen(settings_ptr); 224 std::string settings(settings_ptr, settings_len); 225 226 // The cue payload is stored in the data-part of the input buffer. 227 std::string text(input->data(), input->data() + input->data_size()); 228 229 scoped_refptr<TextCue> text_cue( 230 new TextCue(input->timestamp(), 231 input->duration(), 232 id, 233 settings, 234 text)); 235 236 CueReady(stream, text_cue); 237 } 238 239 void TextRenderer::CueReady( 240 DemuxerStream* text_stream, 241 const scoped_refptr<TextCue>& text_cue) { 242 DCHECK(task_runner_->BelongsToCurrentThread()); 243 DCHECK(state_ != kUninitialized && 244 state_ != kStopped) << "state_ " << state_; 245 DCHECK_GT(pending_read_count_, 0); 246 DCHECK(pending_eos_set_.find(text_stream) != pending_eos_set_.end()); 247 248 TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream); 249 DCHECK(itr != text_track_state_map_.end()); 250 251 TextTrackState* state = itr->second; 252 DCHECK_EQ(state->read_state, TextTrackState::kReadPending); 253 DCHECK(state->text_track); 254 255 --pending_read_count_; 256 state->read_state = TextTrackState::kReadIdle; 257 258 switch (state_) { 259 case kPlaying: { 260 if (text_cue) 261 break; 262 263 const size_t count = pending_eos_set_.erase(text_stream); 264 DCHECK_EQ(count, 1U); 265 266 if (pending_eos_set_.empty()) { 267 DCHECK_EQ(pending_read_count_, 0); 268 state_ = kEnded; 269 ended_cb_.Run(); 270 return; 271 } 272 273 DCHECK_GT(pending_read_count_, 0); 274 return; 275 } 276 case kPausePending: { 277 if (text_cue) 278 break; 279 280 const size_t count = pending_eos_set_.erase(text_stream); 281 DCHECK_EQ(count, 1U); 282 283 if (pending_read_count_ > 0) { 284 DCHECK(!pending_eos_set_.empty()); 285 return; 286 } 287 288 state_ = kPaused; 289 base::ResetAndReturn(&pause_cb_).Run(); 290 291 return; 292 } 293 case kStopPending: 294 if (pending_read_count_ == 0) { 295 state_ = kStopped; 296 base::ResetAndReturn(&stop_cb_).Run(); 297 } 298 299 return; 300 301 case kPaused: 302 case kStopped: 303 case kUninitialized: 304 case kEnded: 305 NOTREACHED(); 306 return; 307 } 308 309 base::TimeDelta start = text_cue->timestamp(); 310 311 if (state->text_ranges_.AddCue(start)) { 312 base::TimeDelta end = start + text_cue->duration(); 313 314 state->text_track->addWebVTTCue(start, end, 315 text_cue->id(), 316 text_cue->text(), 317 text_cue->settings()); 318 } 319 320 if (state_ == kPlaying) { 321 Read(state, text_stream); 322 return; 323 } 324 325 if (pending_read_count_ == 0) { 326 DCHECK_EQ(state_, kPausePending) << "state_ " << state_; 327 state_ = kPaused; 328 base::ResetAndReturn(&pause_cb_).Run(); 329 } 330 } 331 332 void TextRenderer::OnAddTextTrackDone(DemuxerStream* text_stream, 333 scoped_ptr<TextTrack> text_track) { 334 DCHECK(task_runner_->BelongsToCurrentThread()); 335 DCHECK(state_ != kUninitialized && 336 state_ != kStopped && 337 state_ != kStopPending) << "state_ " << state_; 338 DCHECK(text_stream); 339 DCHECK(text_track); 340 341 scoped_ptr<TextTrackState> state(new TextTrackState(text_track.Pass())); 342 text_track_state_map_[text_stream] = state.release(); 343 pending_eos_set_.insert(text_stream); 344 345 if (state_ == kPlaying) 346 Read(text_track_state_map_[text_stream], text_stream); 347 } 348 349 void TextRenderer::Read( 350 TextTrackState* state, 351 DemuxerStream* text_stream) { 352 DCHECK_NE(state->read_state, TextTrackState::kReadPending); 353 354 state->read_state = TextTrackState::kReadPending; 355 ++pending_read_count_; 356 357 text_stream->Read(base::Bind( 358 &TextRenderer::BufferReady, weak_factory_.GetWeakPtr(), text_stream)); 359 } 360 361 TextRenderer::TextTrackState::TextTrackState(scoped_ptr<TextTrack> tt) 362 : read_state(kReadIdle), 363 text_track(tt.Pass()) { 364 } 365 366 TextRenderer::TextTrackState::~TextTrackState() { 367 } 368 369 } // namespace media 370