Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / media / base / text_renderer.cc
blob6f88ef71a697f64617e7dc87f47699fc0dcb24b2
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_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"
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 add_text_track_cb_(add_text_track_cb),
25 state_(kUninitialized),
26 pending_read_count_(0),
27 weak_factory_(this) {}
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_);
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());
45 ended_cb_ = ended_cb;
46 state_ = kPaused;
49 void TextRenderer::Play(const base::Closure& callback) {
50 DCHECK(task_runner_->BelongsToCurrentThread());
51 DCHECK_EQ(state_, kPaused) << "state_ " << state_;
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;
61 Read(state, itr->first);
64 state_ = kPlaying;
65 callback.Run();
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;
74 if (pending_read_count_ == 0) {
75 state_ = kPaused;
76 base::ResetAndReturn(&pause_cb_).Run();
77 return;
80 state_ = kPausePending;
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_;
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();
93 DCHECK_EQ(pending_eos_set_.size(), text_track_state_map_.size());
94 callback.Run();
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);
106 stop_cb_ = cb;
108 if (pending_read_count_ == 0) {
109 state_ = kStopped;
110 base::ResetAndReturn(&stop_cb_).Run();
111 return;
114 state_ = kStopPending;
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());
128 AddTextTrackDoneCB done_cb =
129 BindToCurrentLoop(base::Bind(&TextRenderer::OnAddTextTrackDone,
130 weak_factory_.GetWeakPtr(),
131 text_stream));
133 add_text_track_cb_.Run(config, done_cb);
136 void TextRenderer::RemoveTextStream(DemuxerStream* text_stream) {
137 DCHECK(task_runner_->BelongsToCurrentThread());
139 TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream);
140 DCHECK(itr != text_track_state_map_.end());
142 TextTrackState* state = itr->second;
143 DCHECK_EQ(state->read_state, TextTrackState::kReadIdle);
144 delete state;
145 text_track_state_map_.erase(itr);
147 pending_eos_set_.erase(text_stream);
150 bool TextRenderer::HasTracks() const {
151 DCHECK(task_runner_->BelongsToCurrentThread());
152 return !text_track_state_map_.empty();
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);
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());
167 TextTrackStateMap::iterator itr = text_track_state_map_.find(stream);
168 DCHECK(itr != text_track_state_map_.end());
170 TextTrackState* state = itr->second;
171 DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
173 --pending_read_count_;
174 state->read_state = TextTrackState::kReadIdle;
176 switch (state_) {
177 case kPlaying:
178 return;
180 case kPausePending:
181 if (pending_read_count_ == 0) {
182 state_ = kPaused;
183 base::ResetAndReturn(&pause_cb_).Run();
186 return;
188 case kStopPending:
189 if (pending_read_count_ == 0) {
190 state_ = kStopped;
191 base::ResetAndReturn(&stop_cb_).Run();
194 return;
196 case kPaused:
197 case kStopped:
198 case kUninitialized:
199 case kEnded:
200 NOTREACHED();
201 return;
204 NOTREACHED();
205 return;
208 if (input->end_of_stream()) {
209 CueReady(stream, NULL);
210 return;
213 DCHECK_EQ(status, DemuxerStream::kOk);
214 DCHECK_GE(input->side_data_size(), 2);
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);
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);
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());
229 scoped_refptr<TextCue> text_cue(
230 new TextCue(input->timestamp(),
231 input->duration(),
233 settings,
234 text));
236 CueReady(stream, text_cue);
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());
248 TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream);
249 DCHECK(itr != text_track_state_map_.end());
251 TextTrackState* state = itr->second;
252 DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
253 DCHECK(state->text_track);
255 --pending_read_count_;
256 state->read_state = TextTrackState::kReadIdle;
258 switch (state_) {
259 case kPlaying: {
260 if (text_cue)
261 break;
263 const size_t count = pending_eos_set_.erase(text_stream);
264 DCHECK_EQ(count, 1U);
266 if (pending_eos_set_.empty()) {
267 DCHECK_EQ(pending_read_count_, 0);
268 state_ = kEnded;
269 ended_cb_.Run();
270 return;
273 DCHECK_GT(pending_read_count_, 0);
274 return;
276 case kPausePending: {
277 if (text_cue)
278 break;
280 const size_t count = pending_eos_set_.erase(text_stream);
281 DCHECK_EQ(count, 1U);
283 if (pending_read_count_ > 0) {
284 DCHECK(!pending_eos_set_.empty());
285 return;
288 state_ = kPaused;
289 base::ResetAndReturn(&pause_cb_).Run();
291 return;
293 case kStopPending:
294 if (pending_read_count_ == 0) {
295 state_ = kStopped;
296 base::ResetAndReturn(&stop_cb_).Run();
299 return;
301 case kPaused:
302 case kStopped:
303 case kUninitialized:
304 case kEnded:
305 NOTREACHED();
306 return;
309 base::TimeDelta start = text_cue->timestamp();
311 if (state->text_ranges_.AddCue(start)) {
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());
320 if (state_ == kPlaying) {
321 Read(state, text_stream);
322 return;
325 if (pending_read_count_ == 0) {
326 DCHECK_EQ(state_, kPausePending) << "state_ " << state_;
327 state_ = kPaused;
328 base::ResetAndReturn(&pause_cb_).Run();
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);
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);
345 if (state_ == kPlaying)
346 Read(text_track_state_map_[text_stream], text_stream);
349 void TextRenderer::Read(
350 TextTrackState* state,
351 DemuxerStream* text_stream) {
352 DCHECK_NE(state->read_state, TextTrackState::kReadPending);
354 state->read_state = TextTrackState::kReadPending;
355 ++pending_read_count_;
357 text_stream->Read(base::Bind(
358 &TextRenderer::BufferReady, weak_factory_.GetWeakPtr(), 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