Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(96)

Side by Side Diff: media/base/text_renderer.cc

Issue 23702007: Render inband text tracks in the media pipeline (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: incorporate aaron's comments (11/12) Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 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.
4
5 #include "media/base/text_renderer.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop_proxy.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"
17
18 namespace media {
19
20 TextRenderer::TextRenderer(
21 const scoped_refptr<base::MessageLoopProxy>& message_loop,
22 const AddTextTrackCB& add_text_track_cb)
23 : message_loop_(message_loop),
24 weak_factory_(this),
25 add_text_track_cb_(add_text_track_cb),
26 state_(kUninitialized),
27 pending_read_count_(0) {
28 }
29
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_);
35 }
36
37 void TextRenderer::Initialize(const base::Closure& ended_cb) {
38 DCHECK(message_loop_->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());
45
46 weak_this_ = weak_factory_.GetWeakPtr();
47 ended_cb_ = ended_cb;
48 state_ = kPaused;
49 }
50
51 void TextRenderer::Play(const base::Closure& callback) {
52 DCHECK(message_loop_->BelongsToCurrentThread());
53 DCHECK_EQ(state_, kPaused) << "state_ " << state_;
54
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);
60 continue;
61 }
62
63 Read(state, itr->first);
64 }
65
66 state_ = kPlaying;
67 callback.Run();
68 }
69
70 void TextRenderer::Pause(const base::Closure& callback) {
71 DCHECK(message_loop_->BelongsToCurrentThread());
72 DCHECK(state_ == kPlaying || state_ == kEnded) << "state_ " << state_;
73 DCHECK_GE(pending_read_count_, 0);
74 pause_cb_ = callback;
75
76 if (pending_read_count_ == 0) {
77 state_ = kPaused;
78 base::ResetAndReturn(&pause_cb_).Run();
79 return;
80 }
81
82 state_ = kPausePending;
83 }
84
85 void TextRenderer::Flush(const base::Closure& callback) {
86 DCHECK(message_loop_->BelongsToCurrentThread());
87 DCHECK_EQ(pending_read_count_, 0);
88 DCHECK(state_ == kPaused) << "state_ " << state_;
89
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 }
94 DCHECK_EQ(pending_eos_set_.size(), text_track_state_map_.size());
95 callback.Run();
96 }
97
98 void TextRenderer::Stop(const base::Closure& cb) {
99 DCHECK(message_loop_->BelongsToCurrentThread());
100 DCHECK(!cb.is_null());
101 DCHECK(state_ == kPlaying ||
102 state_ == kPausePending ||
103 state_ == kPaused ||
104 state_ == kEnded) << "state_ " << state_;
105 DCHECK_GE(pending_read_count_, 0);
106
107 stop_cb_ = cb;
108
109 if (pending_read_count_ == 0) {
110 state_ = kStopped;
111 base::ResetAndReturn(&stop_cb_).Run();
112 return;
113 }
114
115 state_ = kStopPending;
116 }
117
118 void TextRenderer::AddTextStream(DemuxerStream* text_stream,
119 const TextTrackConfig& config) {
120 DCHECK(message_loop_->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());
128
129 media::AddTextTrackDoneCB done_cb =
130 media::BindToLoop(message_loop_,
131 base::Bind(&TextRenderer::OnAddTextTrackDone,
132 weak_this_,
133 text_stream));
134
135 add_text_track_cb_.Run(config, done_cb);
136 }
137
138 void TextRenderer::RemoveTextStream(DemuxerStream* text_stream) {
139 DCHECK(message_loop_->BelongsToCurrentThread());
140
141 TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream);
142 DCHECK(itr != text_track_state_map_.end());
143
144 TextTrackState* state = itr->second;
145 DCHECK_EQ(state->read_state, TextTrackState::kReadIdle);
146 delete state;
147 text_track_state_map_.erase(itr);
148
149 pending_eos_set_.erase(text_stream);
150 }
151
152 bool TextRenderer::HasTracks() const {
153 DCHECK(message_loop_->BelongsToCurrentThread());
154 return !text_track_state_map_.empty();
155 }
156
157 void TextRenderer::BufferReady(
158 DemuxerStream* stream,
159 DemuxerStream::Status status,
160 const scoped_refptr<DecoderBuffer>& input) {
161 DCHECK(message_loop_->BelongsToCurrentThread());
162 DCHECK_NE(status, DemuxerStream::kConfigChanged);
163
164 if (status == DemuxerStream::kAborted) {
165 DCHECK(!input);
166 DCHECK_GT(pending_read_count_, 0);
167 DCHECK(pending_eos_set_.find(stream) != pending_eos_set_.end());
168
169 TextTrackStateMap::iterator itr = text_track_state_map_.find(stream);
170 DCHECK(itr != text_track_state_map_.end());
171
172 TextTrackState* state = itr->second;
173 DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
174
175 --pending_read_count_;
176 state->read_state = TextTrackState::kReadIdle;
177
178 switch (state_) {
179 case kPlaying:
180 return;
181
182 case kPausePending:
183 if (pending_read_count_ == 0) {
184 state_ = kPaused;
185 base::ResetAndReturn(&pause_cb_).Run();
186 }
187
188 return;
189
190 case kStopPending:
191 if (pending_read_count_ == 0) {
192 state_ = kStopped;
193 stop_cb_.Run();
acolwell GONE FROM CHROMIUM 2013/11/20 01:15:24 This should be base::ResetAndReturn(&stop_cb_).Run
Matthew Heaney (Chromium) 2013/11/20 19:23:16 Done.
194 }
195
196 return;
197
198 case kPaused:
199 case kStopped:
200 case kUninitialized:
201 case kEnded:
202 NOTREACHED();
203 return;
204 }
205
206 NOTREACHED();
207 return;
208 }
209
210 if (input->end_of_stream()) {
211 CueReady(stream, NULL);
212 return;
213 }
214
215 DCHECK_EQ(status, DemuxerStream::kOk);
216 DCHECK_GE(input->side_data_size(), 2);
217
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);
223
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);
227
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());
230
231 scoped_refptr<TextCue> text_cue(
232 new TextCue(input->timestamp(),
233 input->duration(),
234 id,
235 settings,
236 text));
237
238 CueReady(stream, text_cue);
239 }
240
241 void TextRenderer::CueReady(
242 DemuxerStream* text_stream,
243 const scoped_refptr<TextCue>& text_cue) {
244 DCHECK(message_loop_->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());
249
250 TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream);
251 DCHECK(itr != text_track_state_map_.end());
252
253 TextTrackState* state = itr->second;
254 DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
255 DCHECK(state->text_track);
256
257 --pending_read_count_;
258 state->read_state = TextTrackState::kReadIdle;
259
260 switch (state_) {
261 case kPlaying: {
262 if (text_cue)
263 break;
264
265 const size_t count = pending_eos_set_.erase(text_stream);
266 DCHECK_EQ(count, 1U);
267
268 if (pending_eos_set_.empty()) {
269 DCHECK_EQ(pending_read_count_, 0);
270 state_ = kEnded;
271 ended_cb_.Run();
272 return;
273 }
274
275 DCHECK_GT(pending_read_count_, 0);
276 return;
277 }
278 case kPausePending: {
279 if (text_cue)
280 break;
281
282 const size_t count = pending_eos_set_.erase(text_stream);
283 DCHECK_EQ(count, 1U);
284
285 if (pending_read_count_ > 0) {
286 DCHECK(!pending_eos_set_.empty());
287 return;
288 }
289
290 state_ = kPaused;
291 base::ResetAndReturn(&pause_cb_).Run();
292
293 return;
294 }
295 case kStopPending:
296 if (pending_read_count_ == 0) {
297 state_ = kStopped;
298 stop_cb_.Run();
acolwell GONE FROM CHROMIUM 2013/11/20 01:15:24 base::ResetAndReturn(&stop_cb_).Run();
Matthew Heaney (Chromium) 2013/11/20 19:23:16 Done.
299 }
300
301 return;
302
303 case kPaused:
304 case kStopped:
305 case kUninitialized:
306 case kEnded:
307 NOTREACHED();
308 return;
309 }
310
311 base::TimeDelta start = text_cue->timestamp();
312 base::TimeDelta end = start + text_cue->duration();
313
314 state->text_track->addWebVTTCue(start, end,
315 text_cue->id(),
316 text_cue->text(),
317 text_cue->settings());
318
319 if (state_ == kPlaying) {
320 Read(state, text_stream);
321 return;
322 }
323
324 if (pending_read_count_ == 0) {
325 DCHECK_EQ(state_, kPausePending) << "state_ " << state_;
326 state_ = kPaused;
327 base::ResetAndReturn(&pause_cb_).Run();
328 }
329 }
330
331 void TextRenderer::OnAddTextTrackDone(DemuxerStream* text_stream,
332 scoped_ptr<TextTrack> text_track) {
333 DCHECK(message_loop_->BelongsToCurrentThread());
334 DCHECK(state_ != kUninitialized &&
335 state_ != kStopped &&
336 state_ != kStopPending) << "state_ " << state_;
337 DCHECK(text_stream);
338 DCHECK(text_track);
339
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);
343
344 if (state_ == kPlaying)
345 Read(text_track_state_map_[text_stream], text_stream);
346 }
347
348 void TextRenderer::Read(
349 TextTrackState* state,
350 DemuxerStream* text_stream) {
351 DCHECK_NE(state->read_state, TextTrackState::kReadPending);
352
353 state->read_state = TextTrackState::kReadPending;
354 ++pending_read_count_;
355
356 text_stream->Read(base::Bind(&TextRenderer::BufferReady,
357 weak_this_,
358 text_stream));
359 }
360
361 TextRenderer::TextTrackState::TextTrackState(scoped_ptr<TextTrack> tt)
362 : read_state(kReadIdle),
363 text_track(tt.Pass()) {
364 }
365
366 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698