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"
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"
20 TextRenderer::TextRenderer(
21 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
22 const AddTextTrackCB& add_text_track_cb)
23 : task_runner_(task_runner),
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();
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);
63 Read(state, itr->first);
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);
76 if (pending_read_count_ == 0) {
78 base::ResetAndReturn(&pause_cb_).Run();
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);
93 itr->second->text_ranges_.Reset();
95 DCHECK_EQ(pending_eos_set_.size(), text_track_state_map_.size());
99 void TextRenderer::Stop(const base::Closure& cb) {
100 DCHECK(task_runner_->BelongsToCurrentThread());
101 DCHECK(!cb.is_null());
102 DCHECK(state_ == kPlaying ||
103 state_ == kPausePending ||
105 state_ == kEnded) << "state_ " << state_;
106 DCHECK_GE(pending_read_count_, 0);
110 if (pending_read_count_ == 0) {
112 base::ResetAndReturn(&stop_cb_).Run();
116 state_ = kStopPending;
119 void TextRenderer::AddTextStream(DemuxerStream* text_stream,
120 const TextTrackConfig& config) {
121 DCHECK(task_runner_->BelongsToCurrentThread());
122 DCHECK(state_ != kUninitialized) << "state_ " << state_;
123 DCHECK_NE(state_, kStopPending);
124 DCHECK_NE(state_, kStopped);
125 DCHECK(text_track_state_map_.find(text_stream) ==
126 text_track_state_map_.end());
127 DCHECK(pending_eos_set_.find(text_stream) ==
128 pending_eos_set_.end());
130 AddTextTrackDoneCB done_cb = BindToCurrentLoop(
131 base::Bind(&TextRenderer::OnAddTextTrackDone, weak_this_, 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);
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) {
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;
181 if (pending_read_count_ == 0) {
183 base::ResetAndReturn(&pause_cb_).Run();
189 if (pending_read_count_ == 0) {
191 base::ResetAndReturn(&stop_cb_).Run();
208 if (input->end_of_stream()) {
209 CueReady(stream, NULL);
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(),
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;
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);
273 DCHECK_GT(pending_read_count_, 0);
276 case kPausePending: {
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());
289 base::ResetAndReturn(&pause_cb_).Run();
294 if (pending_read_count_ == 0) {
296 base::ResetAndReturn(&stop_cb_).Run();
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,
317 text_cue->settings());
320 if (state_ == kPlaying) {
321 Read(state, text_stream);
325 if (pending_read_count_ == 0) {
326 DCHECK_EQ(state_, kPausePending) << "state_ " << state_;
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_;
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(&TextRenderer::BufferReady,
362 TextRenderer::TextTrackState::TextTrackState(scoped_ptr<TextTrack> tt)
363 : read_state(kReadIdle),
364 text_track(tt.Pass()) {
367 TextRenderer::TextTrackState::~TextTrackState() {