cc: Fix logic for detecting when raster tasks were throttled
[chromium-blink-merge.git] / media / base / text_renderer.cc
blobcee6fd99e73d86e12ce18b33f63bc220a323bfff
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.
5 #include "media/base/text_renderer.h"
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_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"
18 namespace media {
20 TextRenderer::TextRenderer(
21 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
22 const AddTextTrackCB& add_text_track_cb)
23 : task_runner_(task_runner),
24 weak_factory_(this),
25 add_text_track_cb_(add_text_track_cb),
26 state_(kUninitialized),
27 pending_read_count_(0) {
30 TextRenderer::~TextRenderer() {
31 DCHECK(state_ == kUninitialized ||
32 state_ == kStopped) << "state_ " << state_;
33 DCHECK_EQ(pending_read_count_, 0);
34 STLDeleteValues(&text_track_state_map_);
37 void TextRenderer::Initialize(const base::Closure& ended_cb) {
38 DCHECK(task_runner_->BelongsToCurrentThread());
39 DCHECK(!ended_cb.is_null());
40 DCHECK_EQ(kUninitialized, state_) << "state_ " << state_;
41 DCHECK(text_track_state_map_.empty());
42 DCHECK_EQ(pending_read_count_, 0);
43 DCHECK(pending_eos_set_.empty());
44 DCHECK(ended_cb_.is_null());
46 weak_this_ = weak_factory_.GetWeakPtr();
47 ended_cb_ = ended_cb;
48 state_ = kPaused;
51 void TextRenderer::Play(const base::Closure& callback) {
52 DCHECK(task_runner_->BelongsToCurrentThread());
53 DCHECK_EQ(state_, kPaused) << "state_ " << state_;
55 for (TextTrackStateMap::iterator itr = text_track_state_map_.begin();
56 itr != text_track_state_map_.end(); ++itr) {
57 TextTrackState* state = itr->second;
58 if (state->read_state == TextTrackState::kReadPending) {
59 DCHECK_GT(pending_read_count_, 0);
60 continue;
63 Read(state, itr->first);
66 state_ = kPlaying;
67 callback.Run();
70 void TextRenderer::Pause(const base::Closure& callback) {
71 DCHECK(task_runner_->BelongsToCurrentThread());
72 DCHECK(state_ == kPlaying || state_ == kEnded) << "state_ " << state_;
73 DCHECK_GE(pending_read_count_, 0);
74 pause_cb_ = callback;
76 if (pending_read_count_ == 0) {
77 state_ = kPaused;
78 base::ResetAndReturn(&pause_cb_).Run();
79 return;
82 state_ = kPausePending;
85 void TextRenderer::Flush(const base::Closure& callback) {
86 DCHECK(task_runner_->BelongsToCurrentThread());
87 DCHECK_EQ(pending_read_count_, 0);
88 DCHECK(state_ == kPaused) << "state_ " << state_;
90 for (TextTrackStateMap::iterator itr = text_track_state_map_.begin();
91 itr != text_track_state_map_.end(); ++itr) {
92 pending_eos_set_.insert(itr->first);
94 DCHECK_EQ(pending_eos_set_.size(), text_track_state_map_.size());
95 callback.Run();
98 void TextRenderer::Stop(const base::Closure& cb) {
99 DCHECK(task_runner_->BelongsToCurrentThread());
100 DCHECK(!cb.is_null());
101 DCHECK(state_ == kPlaying ||
102 state_ == kPausePending ||
103 state_ == kPaused ||
104 state_ == kEnded) << "state_ " << state_;
105 DCHECK_GE(pending_read_count_, 0);
107 stop_cb_ = cb;
109 if (pending_read_count_ == 0) {
110 state_ = kStopped;
111 base::ResetAndReturn(&stop_cb_).Run();
112 return;
115 state_ = kStopPending;
118 void TextRenderer::AddTextStream(DemuxerStream* text_stream,
119 const TextTrackConfig& config) {
120 DCHECK(task_runner_->BelongsToCurrentThread());
121 DCHECK(state_ != kUninitialized) << "state_ " << state_;
122 DCHECK_NE(state_, kStopPending);
123 DCHECK_NE(state_, kStopped);
124 DCHECK(text_track_state_map_.find(text_stream) ==
125 text_track_state_map_.end());
126 DCHECK(pending_eos_set_.find(text_stream) ==
127 pending_eos_set_.end());
129 media::AddTextTrackDoneCB done_cb =
130 media::BindToLoop(task_runner_,
131 base::Bind(&TextRenderer::OnAddTextTrackDone,
132 weak_this_,
133 text_stream));
135 add_text_track_cb_.Run(config, done_cb);
138 void TextRenderer::RemoveTextStream(DemuxerStream* text_stream) {
139 DCHECK(task_runner_->BelongsToCurrentThread());
141 TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream);
142 DCHECK(itr != text_track_state_map_.end());
144 TextTrackState* state = itr->second;
145 DCHECK_EQ(state->read_state, TextTrackState::kReadIdle);
146 delete state;
147 text_track_state_map_.erase(itr);
149 pending_eos_set_.erase(text_stream);
152 bool TextRenderer::HasTracks() const {
153 DCHECK(task_runner_->BelongsToCurrentThread());
154 return !text_track_state_map_.empty();
157 void TextRenderer::BufferReady(
158 DemuxerStream* stream,
159 DemuxerStream::Status status,
160 const scoped_refptr<DecoderBuffer>& input) {
161 DCHECK(task_runner_->BelongsToCurrentThread());
162 DCHECK_NE(status, DemuxerStream::kConfigChanged);
164 if (status == DemuxerStream::kAborted) {
165 DCHECK(!input);
166 DCHECK_GT(pending_read_count_, 0);
167 DCHECK(pending_eos_set_.find(stream) != pending_eos_set_.end());
169 TextTrackStateMap::iterator itr = text_track_state_map_.find(stream);
170 DCHECK(itr != text_track_state_map_.end());
172 TextTrackState* state = itr->second;
173 DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
175 --pending_read_count_;
176 state->read_state = TextTrackState::kReadIdle;
178 switch (state_) {
179 case kPlaying:
180 return;
182 case kPausePending:
183 if (pending_read_count_ == 0) {
184 state_ = kPaused;
185 base::ResetAndReturn(&pause_cb_).Run();
188 return;
190 case kStopPending:
191 if (pending_read_count_ == 0) {
192 state_ = kStopped;
193 base::ResetAndReturn(&stop_cb_).Run();
196 return;
198 case kPaused:
199 case kStopped:
200 case kUninitialized:
201 case kEnded:
202 NOTREACHED();
203 return;
206 NOTREACHED();
207 return;
210 if (input->end_of_stream()) {
211 CueReady(stream, NULL);
212 return;
215 DCHECK_EQ(status, DemuxerStream::kOk);
216 DCHECK_GE(input->side_data_size(), 2);
218 // The side data contains both the cue id and cue settings,
219 // each terminated with a NUL.
220 const char* id_ptr = reinterpret_cast<const char*>(input->side_data());
221 size_t id_len = strlen(id_ptr);
222 std::string id(id_ptr, id_len);
224 const char* settings_ptr = id_ptr + id_len + 1;
225 size_t settings_len = strlen(settings_ptr);
226 std::string settings(settings_ptr, settings_len);
228 // The cue payload is stored in the data-part of the input buffer.
229 std::string text(input->data(), input->data() + input->data_size());
231 scoped_refptr<TextCue> text_cue(
232 new TextCue(input->timestamp(),
233 input->duration(),
235 settings,
236 text));
238 CueReady(stream, text_cue);
241 void TextRenderer::CueReady(
242 DemuxerStream* text_stream,
243 const scoped_refptr<TextCue>& text_cue) {
244 DCHECK(task_runner_->BelongsToCurrentThread());
245 DCHECK(state_ != kUninitialized &&
246 state_ != kStopped) << "state_ " << state_;
247 DCHECK_GT(pending_read_count_, 0);
248 DCHECK(pending_eos_set_.find(text_stream) != pending_eos_set_.end());
250 TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream);
251 DCHECK(itr != text_track_state_map_.end());
253 TextTrackState* state = itr->second;
254 DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
255 DCHECK(state->text_track);
257 --pending_read_count_;
258 state->read_state = TextTrackState::kReadIdle;
260 switch (state_) {
261 case kPlaying: {
262 if (text_cue)
263 break;
265 const size_t count = pending_eos_set_.erase(text_stream);
266 DCHECK_EQ(count, 1U);
268 if (pending_eos_set_.empty()) {
269 DCHECK_EQ(pending_read_count_, 0);
270 state_ = kEnded;
271 ended_cb_.Run();
272 return;
275 DCHECK_GT(pending_read_count_, 0);
276 return;
278 case kPausePending: {
279 if (text_cue)
280 break;
282 const size_t count = pending_eos_set_.erase(text_stream);
283 DCHECK_EQ(count, 1U);
285 if (pending_read_count_ > 0) {
286 DCHECK(!pending_eos_set_.empty());
287 return;
290 state_ = kPaused;
291 base::ResetAndReturn(&pause_cb_).Run();
293 return;
295 case kStopPending:
296 if (pending_read_count_ == 0) {
297 state_ = kStopped;
298 base::ResetAndReturn(&stop_cb_).Run();
301 return;
303 case kPaused:
304 case kStopped:
305 case kUninitialized:
306 case kEnded:
307 NOTREACHED();
308 return;
311 base::TimeDelta start = text_cue->timestamp();
312 base::TimeDelta end = start + text_cue->duration();
314 state->text_track->addWebVTTCue(start, end,
315 text_cue->id(),
316 text_cue->text(),
317 text_cue->settings());
319 if (state_ == kPlaying) {
320 Read(state, text_stream);
321 return;
324 if (pending_read_count_ == 0) {
325 DCHECK_EQ(state_, kPausePending) << "state_ " << state_;
326 state_ = kPaused;
327 base::ResetAndReturn(&pause_cb_).Run();
331 void TextRenderer::OnAddTextTrackDone(DemuxerStream* text_stream,
332 scoped_ptr<TextTrack> text_track) {
333 DCHECK(task_runner_->BelongsToCurrentThread());
334 DCHECK(state_ != kUninitialized &&
335 state_ != kStopped &&
336 state_ != kStopPending) << "state_ " << state_;
337 DCHECK(text_stream);
338 DCHECK(text_track);
340 scoped_ptr<TextTrackState> state(new TextTrackState(text_track.Pass()));
341 text_track_state_map_[text_stream] = state.release();
342 pending_eos_set_.insert(text_stream);
344 if (state_ == kPlaying)
345 Read(text_track_state_map_[text_stream], text_stream);
348 void TextRenderer::Read(
349 TextTrackState* state,
350 DemuxerStream* text_stream) {
351 DCHECK_NE(state->read_state, TextTrackState::kReadPending);
353 state->read_state = TextTrackState::kReadPending;
354 ++pending_read_count_;
356 text_stream->Read(base::Bind(&TextRenderer::BufferReady,
357 weak_this_,
358 text_stream));
361 TextRenderer::TextTrackState::TextTrackState(scoped_ptr<TextTrack> tt)
362 : read_state(kReadIdle),
363 text_track(tt.Pass()) {
366 TextRenderer::TextTrackState::~TextTrackState() {
369 } // namespace media