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_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 media::AddTextTrackDoneCB done_cb
=
130 media::BindToLoop(task_runner_
,
131 base::Bind(&TextRenderer::OnAddTextTrackDone
,
135 add_text_track_cb_
.Run(config
, done_cb
);
138 void TextRenderer::RemoveTextStream(DemuxerStream
* text_stream
) {
139 DCHECK(task_runner_
->BelongsToCurrentThread());
141 TextTrackStateMap::iterator itr
= text_track_state_map_
.find(text_stream
);
142 DCHECK(itr
!= text_track_state_map_
.end());
144 TextTrackState
* state
= itr
->second
;
145 DCHECK_EQ(state
->read_state
, TextTrackState::kReadIdle
);
147 text_track_state_map_
.erase(itr
);
149 pending_eos_set_
.erase(text_stream
);
152 bool TextRenderer::HasTracks() const {
153 DCHECK(task_runner_
->BelongsToCurrentThread());
154 return !text_track_state_map_
.empty();
157 void TextRenderer::BufferReady(
158 DemuxerStream
* stream
,
159 DemuxerStream::Status status
,
160 const scoped_refptr
<DecoderBuffer
>& input
) {
161 DCHECK(task_runner_
->BelongsToCurrentThread());
162 DCHECK_NE(status
, DemuxerStream::kConfigChanged
);
164 if (status
== DemuxerStream::kAborted
) {
166 DCHECK_GT(pending_read_count_
, 0);
167 DCHECK(pending_eos_set_
.find(stream
) != pending_eos_set_
.end());
169 TextTrackStateMap::iterator itr
= text_track_state_map_
.find(stream
);
170 DCHECK(itr
!= text_track_state_map_
.end());
172 TextTrackState
* state
= itr
->second
;
173 DCHECK_EQ(state
->read_state
, TextTrackState::kReadPending
);
175 --pending_read_count_
;
176 state
->read_state
= TextTrackState::kReadIdle
;
183 if (pending_read_count_
== 0) {
185 base::ResetAndReturn(&pause_cb_
).Run();
191 if (pending_read_count_
== 0) {
193 base::ResetAndReturn(&stop_cb_
).Run();
210 if (input
->end_of_stream()) {
211 CueReady(stream
, NULL
);
215 DCHECK_EQ(status
, DemuxerStream::kOk
);
216 DCHECK_GE(input
->side_data_size(), 2);
218 // The side data contains both the cue id and cue settings,
219 // each terminated with a NUL.
220 const char* id_ptr
= reinterpret_cast<const char*>(input
->side_data());
221 size_t id_len
= strlen(id_ptr
);
222 std::string
id(id_ptr
, id_len
);
224 const char* settings_ptr
= id_ptr
+ id_len
+ 1;
225 size_t settings_len
= strlen(settings_ptr
);
226 std::string
settings(settings_ptr
, settings_len
);
228 // The cue payload is stored in the data-part of the input buffer.
229 std::string
text(input
->data(), input
->data() + input
->data_size());
231 scoped_refptr
<TextCue
> text_cue(
232 new TextCue(input
->timestamp(),
238 CueReady(stream
, text_cue
);
241 void TextRenderer::CueReady(
242 DemuxerStream
* text_stream
,
243 const scoped_refptr
<TextCue
>& text_cue
) {
244 DCHECK(task_runner_
->BelongsToCurrentThread());
245 DCHECK(state_
!= kUninitialized
&&
246 state_
!= kStopped
) << "state_ " << state_
;
247 DCHECK_GT(pending_read_count_
, 0);
248 DCHECK(pending_eos_set_
.find(text_stream
) != pending_eos_set_
.end());
250 TextTrackStateMap::iterator itr
= text_track_state_map_
.find(text_stream
);
251 DCHECK(itr
!= text_track_state_map_
.end());
253 TextTrackState
* state
= itr
->second
;
254 DCHECK_EQ(state
->read_state
, TextTrackState::kReadPending
);
255 DCHECK(state
->text_track
);
257 --pending_read_count_
;
258 state
->read_state
= TextTrackState::kReadIdle
;
265 const size_t count
= pending_eos_set_
.erase(text_stream
);
266 DCHECK_EQ(count
, 1U);
268 if (pending_eos_set_
.empty()) {
269 DCHECK_EQ(pending_read_count_
, 0);
275 DCHECK_GT(pending_read_count_
, 0);
278 case kPausePending
: {
282 const size_t count
= pending_eos_set_
.erase(text_stream
);
283 DCHECK_EQ(count
, 1U);
285 if (pending_read_count_
> 0) {
286 DCHECK(!pending_eos_set_
.empty());
291 base::ResetAndReturn(&pause_cb_
).Run();
296 if (pending_read_count_
== 0) {
298 base::ResetAndReturn(&stop_cb_
).Run();
311 base::TimeDelta start
= text_cue
->timestamp();
312 base::TimeDelta end
= start
+ text_cue
->duration();
314 state
->text_track
->addWebVTTCue(start
, end
,
317 text_cue
->settings());
319 if (state_
== kPlaying
) {
320 Read(state
, text_stream
);
324 if (pending_read_count_
== 0) {
325 DCHECK_EQ(state_
, kPausePending
) << "state_ " << state_
;
327 base::ResetAndReturn(&pause_cb_
).Run();
331 void TextRenderer::OnAddTextTrackDone(DemuxerStream
* text_stream
,
332 scoped_ptr
<TextTrack
> text_track
) {
333 DCHECK(task_runner_
->BelongsToCurrentThread());
334 DCHECK(state_
!= kUninitialized
&&
335 state_
!= kStopped
&&
336 state_
!= kStopPending
) << "state_ " << state_
;
340 scoped_ptr
<TextTrackState
> state(new TextTrackState(text_track
.Pass()));
341 text_track_state_map_
[text_stream
] = state
.release();
342 pending_eos_set_
.insert(text_stream
);
344 if (state_
== kPlaying
)
345 Read(text_track_state_map_
[text_stream
], text_stream
);
348 void TextRenderer::Read(
349 TextTrackState
* state
,
350 DemuxerStream
* text_stream
) {
351 DCHECK_NE(state
->read_state
, TextTrackState::kReadPending
);
353 state
->read_state
= TextTrackState::kReadPending
;
354 ++pending_read_count_
;
356 text_stream
->Read(base::Bind(&TextRenderer::BufferReady
,
361 TextRenderer::TextTrackState::TextTrackState(scoped_ptr
<TextTrack
> tt
)
362 : read_state(kReadIdle
),
363 text_track(tt
.Pass()) {
366 TextRenderer::TextTrackState::~TextTrackState() {