Work around WebKeyboardEvent.domCode not being set on OS X.
[chromium-blink-merge.git] / media / base / text_renderer.cc
blob3648f8a0a8f03fbe45fc925df74d7ad70591650a
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/text_cue.h"
17 namespace media {
19 TextRenderer::TextRenderer(
20 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
21 const AddTextTrackCB& add_text_track_cb)
22 : task_runner_(task_runner),
23 add_text_track_cb_(add_text_track_cb),
24 state_(kUninitialized),
25 pending_read_count_(0),
26 weak_factory_(this) {}
28 TextRenderer::~TextRenderer() {
29 DCHECK(task_runner_->BelongsToCurrentThread());
30 STLDeleteValues(&text_track_state_map_);
31 if (!pause_cb_.is_null())
32 base::ResetAndReturn(&pause_cb_).Run();
35 void TextRenderer::Initialize(const base::Closure& ended_cb) {
36 DCHECK(task_runner_->BelongsToCurrentThread());
37 DCHECK(!ended_cb.is_null());
38 DCHECK_EQ(kUninitialized, state_) << "state_ " << state_;
39 DCHECK(text_track_state_map_.empty());
40 DCHECK_EQ(pending_read_count_, 0);
41 DCHECK(pending_eos_set_.empty());
42 DCHECK(ended_cb_.is_null());
44 ended_cb_ = ended_cb;
45 state_ = kPaused;
48 void TextRenderer::StartPlaying() {
49 DCHECK(task_runner_->BelongsToCurrentThread());
50 DCHECK_EQ(state_, kPaused) << "state_ " << state_;
52 for (TextTrackStateMap::iterator itr = text_track_state_map_.begin();
53 itr != text_track_state_map_.end(); ++itr) {
54 TextTrackState* state = itr->second;
55 if (state->read_state == TextTrackState::kReadPending) {
56 DCHECK_GT(pending_read_count_, 0);
57 continue;
60 Read(state, itr->first);
63 state_ = kPlaying;
66 void TextRenderer::Pause(const base::Closure& callback) {
67 DCHECK(task_runner_->BelongsToCurrentThread());
68 DCHECK(state_ == kPlaying || state_ == kEnded) << "state_ " << state_;
69 DCHECK_GE(pending_read_count_, 0);
71 if (pending_read_count_ == 0) {
72 state_ = kPaused;
73 task_runner_->PostTask(FROM_HERE, callback);
74 return;
77 pause_cb_ = callback;
78 state_ = kPausePending;
81 void TextRenderer::Flush(const base::Closure& callback) {
82 DCHECK(task_runner_->BelongsToCurrentThread());
83 DCHECK_EQ(pending_read_count_, 0);
84 DCHECK(state_ == kPaused) << "state_ " << state_;
86 for (TextTrackStateMap::iterator itr = text_track_state_map_.begin();
87 itr != text_track_state_map_.end(); ++itr) {
88 pending_eos_set_.insert(itr->first);
89 itr->second->text_ranges_.Reset();
91 DCHECK_EQ(pending_eos_set_.size(), text_track_state_map_.size());
92 task_runner_->PostTask(FROM_HERE, callback);
95 void TextRenderer::AddTextStream(DemuxerStream* text_stream,
96 const TextTrackConfig& config) {
97 DCHECK(task_runner_->BelongsToCurrentThread());
98 DCHECK(state_ != kUninitialized) << "state_ " << state_;
99 DCHECK(text_track_state_map_.find(text_stream) ==
100 text_track_state_map_.end());
101 DCHECK(pending_eos_set_.find(text_stream) ==
102 pending_eos_set_.end());
104 AddTextTrackDoneCB done_cb =
105 BindToCurrentLoop(base::Bind(&TextRenderer::OnAddTextTrackDone,
106 weak_factory_.GetWeakPtr(),
107 text_stream));
109 add_text_track_cb_.Run(config, done_cb);
112 void TextRenderer::RemoveTextStream(DemuxerStream* text_stream) {
113 DCHECK(task_runner_->BelongsToCurrentThread());
115 TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream);
116 DCHECK(itr != text_track_state_map_.end());
118 TextTrackState* state = itr->second;
119 DCHECK_EQ(state->read_state, TextTrackState::kReadIdle);
120 delete state;
121 text_track_state_map_.erase(itr);
123 pending_eos_set_.erase(text_stream);
126 bool TextRenderer::HasTracks() const {
127 DCHECK(task_runner_->BelongsToCurrentThread());
128 return !text_track_state_map_.empty();
131 void TextRenderer::BufferReady(
132 DemuxerStream* stream,
133 DemuxerStream::Status status,
134 const scoped_refptr<DecoderBuffer>& input) {
135 DCHECK(task_runner_->BelongsToCurrentThread());
136 DCHECK_NE(status, DemuxerStream::kConfigChanged);
138 if (status == DemuxerStream::kAborted) {
139 DCHECK(!input.get());
140 DCHECK_GT(pending_read_count_, 0);
141 DCHECK(pending_eos_set_.find(stream) != pending_eos_set_.end());
143 TextTrackStateMap::iterator itr = text_track_state_map_.find(stream);
144 DCHECK(itr != text_track_state_map_.end());
146 TextTrackState* state = itr->second;
147 DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
149 --pending_read_count_;
150 state->read_state = TextTrackState::kReadIdle;
152 switch (state_) {
153 case kPlaying:
154 return;
156 case kPausePending:
157 if (pending_read_count_ == 0) {
158 state_ = kPaused;
159 base::ResetAndReturn(&pause_cb_).Run();
162 return;
164 case kPaused:
165 case kUninitialized:
166 case kEnded:
167 NOTREACHED();
168 return;
171 NOTREACHED();
172 return;
175 if (input->end_of_stream()) {
176 CueReady(stream, NULL);
177 return;
180 DCHECK_EQ(status, DemuxerStream::kOk);
181 DCHECK_GE(input->side_data_size(), 2);
183 // The side data contains both the cue id and cue settings,
184 // each terminated with a NUL.
185 const char* id_ptr = reinterpret_cast<const char*>(input->side_data());
186 size_t id_len = strlen(id_ptr);
187 std::string id(id_ptr, id_len);
189 const char* settings_ptr = id_ptr + id_len + 1;
190 size_t settings_len = strlen(settings_ptr);
191 std::string settings(settings_ptr, settings_len);
193 // The cue payload is stored in the data-part of the input buffer.
194 std::string text(input->data(), input->data() + input->data_size());
196 scoped_refptr<TextCue> text_cue(
197 new TextCue(input->timestamp(),
198 input->duration(),
200 settings,
201 text));
203 CueReady(stream, text_cue);
206 void TextRenderer::CueReady(
207 DemuxerStream* text_stream,
208 const scoped_refptr<TextCue>& text_cue) {
209 DCHECK(task_runner_->BelongsToCurrentThread());
210 DCHECK_NE(state_, kUninitialized);
211 DCHECK_GT(pending_read_count_, 0);
212 DCHECK(pending_eos_set_.find(text_stream) != pending_eos_set_.end());
214 TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream);
215 DCHECK(itr != text_track_state_map_.end());
217 TextTrackState* state = itr->second;
218 DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
219 DCHECK(state->text_track);
221 --pending_read_count_;
222 state->read_state = TextTrackState::kReadIdle;
224 switch (state_) {
225 case kPlaying: {
226 if (text_cue.get())
227 break;
229 const size_t count = pending_eos_set_.erase(text_stream);
230 DCHECK_EQ(count, 1U);
232 if (pending_eos_set_.empty()) {
233 DCHECK_EQ(pending_read_count_, 0);
234 state_ = kEnded;
235 task_runner_->PostTask(FROM_HERE, ended_cb_);
236 return;
239 DCHECK_GT(pending_read_count_, 0);
240 return;
242 case kPausePending: {
243 if (text_cue.get())
244 break;
246 const size_t count = pending_eos_set_.erase(text_stream);
247 DCHECK_EQ(count, 1U);
249 if (pending_read_count_ > 0) {
250 DCHECK(!pending_eos_set_.empty());
251 return;
254 state_ = kPaused;
255 base::ResetAndReturn(&pause_cb_).Run();
257 return;
260 case kPaused:
261 case kUninitialized:
262 case kEnded:
263 NOTREACHED();
264 return;
267 base::TimeDelta start = text_cue->timestamp();
269 if (state->text_ranges_.AddCue(start)) {
270 base::TimeDelta end = start + text_cue->duration();
272 state->text_track->addWebVTTCue(start, end,
273 text_cue->id(),
274 text_cue->text(),
275 text_cue->settings());
278 if (state_ == kPlaying) {
279 Read(state, text_stream);
280 return;
283 if (pending_read_count_ == 0) {
284 DCHECK_EQ(state_, kPausePending) << "state_ " << state_;
285 state_ = kPaused;
286 base::ResetAndReturn(&pause_cb_).Run();
290 void TextRenderer::OnAddTextTrackDone(DemuxerStream* text_stream,
291 scoped_ptr<TextTrack> text_track) {
292 DCHECK(task_runner_->BelongsToCurrentThread());
293 DCHECK_NE(state_, kUninitialized);
294 DCHECK(text_stream);
295 DCHECK(text_track);
297 scoped_ptr<TextTrackState> state(new TextTrackState(text_track.Pass()));
298 text_track_state_map_[text_stream] = state.release();
299 pending_eos_set_.insert(text_stream);
301 if (state_ == kPlaying)
302 Read(text_track_state_map_[text_stream], text_stream);
305 void TextRenderer::Read(
306 TextTrackState* state,
307 DemuxerStream* text_stream) {
308 DCHECK_NE(state->read_state, TextTrackState::kReadPending);
310 state->read_state = TextTrackState::kReadPending;
311 ++pending_read_count_;
313 text_stream->Read(base::Bind(
314 &TextRenderer::BufferReady, weak_factory_.GetWeakPtr(), text_stream));
317 TextRenderer::TextTrackState::TextTrackState(scoped_ptr<TextTrack> tt)
318 : read_state(kReadIdle),
319 text_track(tt.Pass()) {
322 TextRenderer::TextTrackState::~TextTrackState() {
325 } // namespace media