• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
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/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"
17 
18 namespace media {
19 
TextRenderer(const scoped_refptr<base::SingleThreadTaskRunner> & task_runner,const AddTextTrackCB & add_text_track_cb)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) {}
28 
~TextRenderer()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_);
34 }
35 
Initialize(const base::Closure & ended_cb)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());
44 
45   ended_cb_ = ended_cb;
46   state_ = kPaused;
47 }
48 
Play(const base::Closure & callback)49 void TextRenderer::Play(const base::Closure& callback) {
50   DCHECK(task_runner_->BelongsToCurrentThread());
51   DCHECK_EQ(state_, kPaused) << "state_ " << state_;
52 
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);
58       continue;
59     }
60 
61     Read(state, itr->first);
62   }
63 
64   state_ = kPlaying;
65   callback.Run();
66 }
67 
Pause(const base::Closure & callback)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);
72   pause_cb_ = callback;
73 
74   if (pending_read_count_ == 0) {
75     state_ = kPaused;
76     base::ResetAndReturn(&pause_cb_).Run();
77     return;
78   }
79 
80   state_ = kPausePending;
81 }
82 
Flush(const base::Closure & callback)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_;
87 
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();
92   }
93   DCHECK_EQ(pending_eos_set_.size(), text_track_state_map_.size());
94   callback.Run();
95 }
96 
Stop(const base::Closure & cb)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 ||
102          state_ == kPaused ||
103          state_ == kEnded) << "state_ " << state_;
104   DCHECK_GE(pending_read_count_, 0);
105 
106   stop_cb_ = cb;
107 
108   if (pending_read_count_ == 0) {
109     state_ = kStopped;
110     base::ResetAndReturn(&stop_cb_).Run();
111     return;
112   }
113 
114   state_ = kStopPending;
115 }
116 
AddTextStream(DemuxerStream * text_stream,const TextTrackConfig & config)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());
127 
128   AddTextTrackDoneCB done_cb =
129       BindToCurrentLoop(base::Bind(&TextRenderer::OnAddTextTrackDone,
130                                    weak_factory_.GetWeakPtr(),
131                                    text_stream));
132 
133   add_text_track_cb_.Run(config, done_cb);
134 }
135 
RemoveTextStream(DemuxerStream * text_stream)136 void TextRenderer::RemoveTextStream(DemuxerStream* text_stream) {
137   DCHECK(task_runner_->BelongsToCurrentThread());
138 
139   TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream);
140   DCHECK(itr != text_track_state_map_.end());
141 
142   TextTrackState* state = itr->second;
143   DCHECK_EQ(state->read_state, TextTrackState::kReadIdle);
144   delete state;
145   text_track_state_map_.erase(itr);
146 
147   pending_eos_set_.erase(text_stream);
148 }
149 
HasTracks() const150 bool TextRenderer::HasTracks() const {
151   DCHECK(task_runner_->BelongsToCurrentThread());
152   return !text_track_state_map_.empty();
153 }
154 
BufferReady(DemuxerStream * stream,DemuxerStream::Status status,const scoped_refptr<DecoderBuffer> & input)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);
161 
162   if (status == DemuxerStream::kAborted) {
163     DCHECK(!input);
164     DCHECK_GT(pending_read_count_, 0);
165     DCHECK(pending_eos_set_.find(stream) != pending_eos_set_.end());
166 
167     TextTrackStateMap::iterator itr = text_track_state_map_.find(stream);
168     DCHECK(itr != text_track_state_map_.end());
169 
170     TextTrackState* state = itr->second;
171     DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
172 
173     --pending_read_count_;
174     state->read_state = TextTrackState::kReadIdle;
175 
176     switch (state_) {
177       case kPlaying:
178         return;
179 
180       case kPausePending:
181         if (pending_read_count_ == 0) {
182           state_ = kPaused;
183           base::ResetAndReturn(&pause_cb_).Run();
184         }
185 
186         return;
187 
188       case kStopPending:
189         if (pending_read_count_ == 0) {
190           state_ = kStopped;
191           base::ResetAndReturn(&stop_cb_).Run();
192         }
193 
194         return;
195 
196       case kPaused:
197       case kStopped:
198       case kUninitialized:
199       case kEnded:
200         NOTREACHED();
201         return;
202     }
203 
204     NOTREACHED();
205     return;
206   }
207 
208   if (input->end_of_stream()) {
209     CueReady(stream, NULL);
210     return;
211   }
212 
213   DCHECK_EQ(status, DemuxerStream::kOk);
214   DCHECK_GE(input->side_data_size(), 2);
215 
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);
221 
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);
225 
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());
228 
229   scoped_refptr<TextCue> text_cue(
230       new TextCue(input->timestamp(),
231                   input->duration(),
232                   id,
233                   settings,
234                   text));
235 
236   CueReady(stream, text_cue);
237 }
238 
CueReady(DemuxerStream * text_stream,const scoped_refptr<TextCue> & 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());
247 
248   TextTrackStateMap::iterator itr = text_track_state_map_.find(text_stream);
249   DCHECK(itr != text_track_state_map_.end());
250 
251   TextTrackState* state = itr->second;
252   DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
253   DCHECK(state->text_track);
254 
255   --pending_read_count_;
256   state->read_state = TextTrackState::kReadIdle;
257 
258   switch (state_) {
259     case kPlaying: {
260       if (text_cue)
261         break;
262 
263       const size_t count = pending_eos_set_.erase(text_stream);
264       DCHECK_EQ(count, 1U);
265 
266       if (pending_eos_set_.empty()) {
267         DCHECK_EQ(pending_read_count_, 0);
268         state_ = kEnded;
269         ended_cb_.Run();
270         return;
271       }
272 
273       DCHECK_GT(pending_read_count_, 0);
274       return;
275     }
276     case kPausePending: {
277       if (text_cue)
278         break;
279 
280       const size_t count = pending_eos_set_.erase(text_stream);
281       DCHECK_EQ(count, 1U);
282 
283       if (pending_read_count_ > 0) {
284         DCHECK(!pending_eos_set_.empty());
285         return;
286       }
287 
288       state_ = kPaused;
289       base::ResetAndReturn(&pause_cb_).Run();
290 
291       return;
292     }
293     case kStopPending:
294       if (pending_read_count_ == 0) {
295         state_ = kStopped;
296         base::ResetAndReturn(&stop_cb_).Run();
297       }
298 
299       return;
300 
301     case kPaused:
302     case kStopped:
303     case kUninitialized:
304     case kEnded:
305       NOTREACHED();
306       return;
307   }
308 
309   base::TimeDelta start = text_cue->timestamp();
310 
311   if (state->text_ranges_.AddCue(start)) {
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 
320   if (state_ == kPlaying) {
321     Read(state, text_stream);
322     return;
323   }
324 
325   if (pending_read_count_ == 0) {
326       DCHECK_EQ(state_, kPausePending) << "state_ " << state_;
327       state_ = kPaused;
328       base::ResetAndReturn(&pause_cb_).Run();
329   }
330 }
331 
OnAddTextTrackDone(DemuxerStream * text_stream,scoped_ptr<TextTrack> text_track)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_;
338   DCHECK(text_stream);
339   DCHECK(text_track);
340 
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);
344 
345   if (state_ == kPlaying)
346     Read(text_track_state_map_[text_stream], text_stream);
347 }
348 
Read(TextTrackState * state,DemuxerStream * text_stream)349 void TextRenderer::Read(
350     TextTrackState* state,
351     DemuxerStream* text_stream) {
352   DCHECK_NE(state->read_state, TextTrackState::kReadPending);
353 
354   state->read_state = TextTrackState::kReadPending;
355   ++pending_read_count_;
356 
357   text_stream->Read(base::Bind(
358       &TextRenderer::BufferReady, weak_factory_.GetWeakPtr(), text_stream));
359 }
360 
TextTrackState(scoped_ptr<TextTrack> tt)361 TextRenderer::TextTrackState::TextTrackState(scoped_ptr<TextTrack> tt)
362     : read_state(kReadIdle),
363       text_track(tt.Pass()) {
364 }
365 
~TextTrackState()366 TextRenderer::TextTrackState::~TextTrackState() {
367 }
368 
369 }  // namespace media
370