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
);
94 DCHECK_EQ(pending_eos_set_
.size(), text_track_state_map_
.size());
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
||
104 state_
== kEnded
) << "state_ " << state_
;
105 DCHECK_GE(pending_read_count_
, 0);
109 if (pending_read_count_
== 0) {
111 base::ResetAndReturn(&stop_cb_
).Run();
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 AddTextTrackDoneCB done_cb
= BindToCurrentLoop(
130 base::Bind(&TextRenderer::OnAddTextTrackDone
, weak_this_
, text_stream
));
132 add_text_track_cb_
.Run(config
, done_cb
);
135 void TextRenderer::RemoveTextStream(DemuxerStream
* text_stream
) {
136 DCHECK(task_runner_
->BelongsToCurrentThread());
138 TextTrackStateMap::iterator itr
= text_track_state_map_
.find(text_stream
);
139 DCHECK(itr
!= text_track_state_map_
.end());
141 TextTrackState
* state
= itr
->second
;
142 DCHECK_EQ(state
->read_state
, TextTrackState::kReadIdle
);
144 text_track_state_map_
.erase(itr
);
146 pending_eos_set_
.erase(text_stream
);
149 bool TextRenderer::HasTracks() const {
150 DCHECK(task_runner_
->BelongsToCurrentThread());
151 return !text_track_state_map_
.empty();
154 void TextRenderer::BufferReady(
155 DemuxerStream
* stream
,
156 DemuxerStream::Status status
,
157 const scoped_refptr
<DecoderBuffer
>& input
) {
158 DCHECK(task_runner_
->BelongsToCurrentThread());
159 DCHECK_NE(status
, DemuxerStream::kConfigChanged
);
161 if (status
== DemuxerStream::kAborted
) {
163 DCHECK_GT(pending_read_count_
, 0);
164 DCHECK(pending_eos_set_
.find(stream
) != pending_eos_set_
.end());
166 TextTrackStateMap::iterator itr
= text_track_state_map_
.find(stream
);
167 DCHECK(itr
!= text_track_state_map_
.end());
169 TextTrackState
* state
= itr
->second
;
170 DCHECK_EQ(state
->read_state
, TextTrackState::kReadPending
);
172 --pending_read_count_
;
173 state
->read_state
= TextTrackState::kReadIdle
;
180 if (pending_read_count_
== 0) {
182 base::ResetAndReturn(&pause_cb_
).Run();
188 if (pending_read_count_
== 0) {
190 base::ResetAndReturn(&stop_cb_
).Run();
207 if (input
->end_of_stream()) {
208 CueReady(stream
, NULL
);
212 DCHECK_EQ(status
, DemuxerStream::kOk
);
213 DCHECK_GE(input
->side_data_size(), 2);
215 // The side data contains both the cue id and cue settings,
216 // each terminated with a NUL.
217 const char* id_ptr
= reinterpret_cast<const char*>(input
->side_data());
218 size_t id_len
= strlen(id_ptr
);
219 std::string
id(id_ptr
, id_len
);
221 const char* settings_ptr
= id_ptr
+ id_len
+ 1;
222 size_t settings_len
= strlen(settings_ptr
);
223 std::string
settings(settings_ptr
, settings_len
);
225 // The cue payload is stored in the data-part of the input buffer.
226 std::string
text(input
->data(), input
->data() + input
->data_size());
228 scoped_refptr
<TextCue
> text_cue(
229 new TextCue(input
->timestamp(),
235 CueReady(stream
, text_cue
);
238 void TextRenderer::CueReady(
239 DemuxerStream
* text_stream
,
240 const scoped_refptr
<TextCue
>& text_cue
) {
241 DCHECK(task_runner_
->BelongsToCurrentThread());
242 DCHECK(state_
!= kUninitialized
&&
243 state_
!= kStopped
) << "state_ " << state_
;
244 DCHECK_GT(pending_read_count_
, 0);
245 DCHECK(pending_eos_set_
.find(text_stream
) != pending_eos_set_
.end());
247 TextTrackStateMap::iterator itr
= text_track_state_map_
.find(text_stream
);
248 DCHECK(itr
!= text_track_state_map_
.end());
250 TextTrackState
* state
= itr
->second
;
251 DCHECK_EQ(state
->read_state
, TextTrackState::kReadPending
);
252 DCHECK(state
->text_track
);
254 --pending_read_count_
;
255 state
->read_state
= TextTrackState::kReadIdle
;
262 const size_t count
= pending_eos_set_
.erase(text_stream
);
263 DCHECK_EQ(count
, 1U);
265 if (pending_eos_set_
.empty()) {
266 DCHECK_EQ(pending_read_count_
, 0);
272 DCHECK_GT(pending_read_count_
, 0);
275 case kPausePending
: {
279 const size_t count
= pending_eos_set_
.erase(text_stream
);
280 DCHECK_EQ(count
, 1U);
282 if (pending_read_count_
> 0) {
283 DCHECK(!pending_eos_set_
.empty());
288 base::ResetAndReturn(&pause_cb_
).Run();
293 if (pending_read_count_
== 0) {
295 base::ResetAndReturn(&stop_cb_
).Run();
308 base::TimeDelta start
= text_cue
->timestamp();
309 base::TimeDelta end
= start
+ text_cue
->duration();
311 state
->text_track
->addWebVTTCue(start
, end
,
314 text_cue
->settings());
316 if (state_
== kPlaying
) {
317 Read(state
, text_stream
);
321 if (pending_read_count_
== 0) {
322 DCHECK_EQ(state_
, kPausePending
) << "state_ " << state_
;
324 base::ResetAndReturn(&pause_cb_
).Run();
328 void TextRenderer::OnAddTextTrackDone(DemuxerStream
* text_stream
,
329 scoped_ptr
<TextTrack
> text_track
) {
330 DCHECK(task_runner_
->BelongsToCurrentThread());
331 DCHECK(state_
!= kUninitialized
&&
332 state_
!= kStopped
&&
333 state_
!= kStopPending
) << "state_ " << state_
;
337 scoped_ptr
<TextTrackState
> state(new TextTrackState(text_track
.Pass()));
338 text_track_state_map_
[text_stream
] = state
.release();
339 pending_eos_set_
.insert(text_stream
);
341 if (state_
== kPlaying
)
342 Read(text_track_state_map_
[text_stream
], text_stream
);
345 void TextRenderer::Read(
346 TextTrackState
* state
,
347 DemuxerStream
* text_stream
) {
348 DCHECK_NE(state
->read_state
, TextTrackState::kReadPending
);
350 state
->read_state
= TextTrackState::kReadPending
;
351 ++pending_read_count_
;
353 text_stream
->Read(base::Bind(&TextRenderer::BufferReady
,
358 TextRenderer::TextTrackState::TextTrackState(scoped_ptr
<TextTrack
> tt
)
359 : read_state(kReadIdle
),
360 text_track(tt
.Pass()) {
363 TextRenderer::TextTrackState::~TextTrackState() {