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
),
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());
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);
61 Read(state
, itr
->first
);
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);
74 if (pending_read_count_
== 0) {
76 base::ResetAndReturn(&pause_cb_
).Run();
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());
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
||
103 state_
== kEnded
) << "state_ " << state_
;
104 DCHECK_GE(pending_read_count_
, 0);
108 if (pending_read_count_
== 0) {
110 base::ResetAndReturn(&stop_cb_
).Run();
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(),
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(
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() {