Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / media / base / text_renderer.cc
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
20 TextRenderer::TextRenderer(
21     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
22     const AddTextTrackCB& add_text_track_cb)
23     : task_runner_(task_runner),
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(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());
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(task_runner_->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(task_runner_->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(task_runner_->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     itr->second->text_ranges_.Reset();
94   }
95   DCHECK_EQ(pending_eos_set_.size(), text_track_state_map_.size());
96   callback.Run();
97 }
98
99 void TextRenderer::Stop(const base::Closure& cb) {
100   DCHECK(task_runner_->BelongsToCurrentThread());
101   DCHECK(!cb.is_null());
102   DCHECK(state_ == kPlaying ||
103          state_ == kPausePending ||
104          state_ == kPaused ||
105          state_ == kEnded) << "state_ " << state_;
106   DCHECK_GE(pending_read_count_, 0);
107
108   stop_cb_ = cb;
109
110   if (pending_read_count_ == 0) {
111     state_ = kStopped;
112     base::ResetAndReturn(&stop_cb_).Run();
113     return;
114   }
115
116   state_ = kStopPending;
117 }
118
119 void TextRenderer::AddTextStream(DemuxerStream* text_stream,
120                                  const TextTrackConfig& config) {
121   DCHECK(task_runner_->BelongsToCurrentThread());
122   DCHECK(state_ != kUninitialized) << "state_ " << state_;
123   DCHECK_NE(state_, kStopPending);
124   DCHECK_NE(state_, kStopped);
125   DCHECK(text_track_state_map_.find(text_stream) ==
126          text_track_state_map_.end());
127   DCHECK(pending_eos_set_.find(text_stream) ==
128          pending_eos_set_.end());
129
130   AddTextTrackDoneCB done_cb = BindToCurrentLoop(
131       base::Bind(&TextRenderer::OnAddTextTrackDone, weak_this_, text_stream));
132
133   add_text_track_cb_.Run(config, done_cb);
134 }
135
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
150 bool TextRenderer::HasTracks() const {
151   DCHECK(task_runner_->BelongsToCurrentThread());
152   return !text_track_state_map_.empty();
153 }
154
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
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
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
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(&TextRenderer::BufferReady,
358                                 weak_this_,
359                                 text_stream));
360 }
361
362 TextRenderer::TextTrackState::TextTrackState(scoped_ptr<TextTrack> tt)
363     : read_state(kReadIdle),
364       text_track(tt.Pass()) {
365 }
366
367 TextRenderer::TextTrackState::~TextTrackState() {
368 }
369
370 }  // namespace media