123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- // Copyright 2013 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "media/base/text_renderer.h"
- #include <stddef.h>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/check_op.h"
- #include "base/notreached.h"
- #include "base/task/single_thread_task_runner.h"
- #include "media/base/bind_to_current_loop.h"
- #include "media/base/decoder_buffer.h"
- #include "media/base/demuxer.h"
- #include "media/base/text_cue.h"
- namespace media {
- TextRenderer::TextRenderer(
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
- const AddTextTrackCB& add_text_track_cb)
- : task_runner_(task_runner),
- add_text_track_cb_(add_text_track_cb),
- state_(kUninitialized),
- pending_read_count_(0) {}
- TextRenderer::~TextRenderer() {
- DCHECK(task_runner_->BelongsToCurrentThread());
- text_track_state_map_.clear();
- if (pause_cb_)
- std::move(pause_cb_).Run();
- }
- void TextRenderer::Initialize(const base::RepeatingClosure& ended_cb) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(ended_cb);
- DCHECK_EQ(kUninitialized, state_) << "state_ " << state_;
- DCHECK(text_track_state_map_.empty());
- DCHECK_EQ(pending_read_count_, 0);
- DCHECK(pending_eos_set_.empty());
- DCHECK(!ended_cb_);
- ended_cb_ = ended_cb;
- state_ = kPaused;
- }
- void TextRenderer::StartPlaying() {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK_EQ(state_, kPaused) << "state_ " << state_;
- for (auto itr = text_track_state_map_.begin();
- itr != text_track_state_map_.end(); ++itr) {
- TextTrackState* state = itr->second.get();
- if (state->read_state == TextTrackState::kReadPending) {
- DCHECK_GT(pending_read_count_, 0);
- continue;
- }
- Read(state, itr->first);
- }
- state_ = kPlaying;
- }
- void TextRenderer::Pause(base::OnceClosure callback) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(state_ == kPlaying || state_ == kEnded) << "state_ " << state_;
- DCHECK_GE(pending_read_count_, 0);
- if (pending_read_count_ == 0) {
- state_ = kPaused;
- task_runner_->PostTask(FROM_HERE, std::move(callback));
- return;
- }
- pause_cb_ = std::move(callback);
- state_ = kPausePending;
- }
- void TextRenderer::Flush(base::OnceClosure callback) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK_EQ(pending_read_count_, 0);
- DCHECK(state_ == kPaused) << "state_ " << state_;
- for (auto itr = text_track_state_map_.begin();
- itr != text_track_state_map_.end(); ++itr) {
- pending_eos_set_.insert(itr->first);
- itr->second->text_ranges_.Reset();
- }
- DCHECK_EQ(pending_eos_set_.size(), text_track_state_map_.size());
- task_runner_->PostTask(FROM_HERE, std::move(callback));
- }
- void TextRenderer::AddTextStream(DemuxerStream* text_stream,
- const TextTrackConfig& config) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(state_ != kUninitialized) << "state_ " << state_;
- DCHECK(text_track_state_map_.find(text_stream) ==
- text_track_state_map_.end());
- DCHECK(pending_eos_set_.find(text_stream) ==
- pending_eos_set_.end());
- AddTextTrackDoneCB done_cb = BindToCurrentLoop(
- base::BindOnce(&TextRenderer::OnAddTextTrackDone,
- weak_factory_.GetWeakPtr(), text_stream));
- add_text_track_cb_.Run(config, std::move(done_cb));
- }
- void TextRenderer::RemoveTextStream(DemuxerStream* text_stream) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- auto itr = text_track_state_map_.find(text_stream);
- DCHECK(itr != text_track_state_map_.end());
- TextTrackState* state = itr->second.get();
- DCHECK_EQ(state->read_state, TextTrackState::kReadIdle);
- text_track_state_map_.erase(itr);
- pending_eos_set_.erase(text_stream);
- }
- bool TextRenderer::HasTracks() const {
- DCHECK(task_runner_->BelongsToCurrentThread());
- return !text_track_state_map_.empty();
- }
- void TextRenderer::BufferReady(DemuxerStream* stream,
- DemuxerStream::Status status,
- scoped_refptr<DecoderBuffer> input) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK_NE(status, DemuxerStream::kConfigChanged);
- if (status == DemuxerStream::kAborted) {
- DCHECK(!input);
- DCHECK_GT(pending_read_count_, 0);
- DCHECK(pending_eos_set_.find(stream) != pending_eos_set_.end());
- auto itr = text_track_state_map_.find(stream);
- DCHECK(itr != text_track_state_map_.end());
- TextTrackState* state = itr->second.get();
- DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
- --pending_read_count_;
- state->read_state = TextTrackState::kReadIdle;
- switch (state_) {
- case kPlaying:
- return;
- case kPausePending:
- if (pending_read_count_ == 0) {
- state_ = kPaused;
- std::move(pause_cb_).Run();
- }
- return;
- case kPaused:
- case kUninitialized:
- case kEnded:
- NOTREACHED();
- return;
- }
- NOTREACHED();
- return;
- }
- if (input->end_of_stream()) {
- CueReady(stream, nullptr);
- return;
- }
- DCHECK_EQ(status, DemuxerStream::kOk);
- DCHECK_GE(input->side_data_size(), 2u);
- // The side data contains both the cue id and cue settings,
- // each terminated with a NUL.
- const char* id_ptr = reinterpret_cast<const char*>(input->side_data());
- size_t id_len = strlen(id_ptr);
- std::string id(id_ptr, id_len);
- const char* settings_ptr = id_ptr + id_len + 1;
- size_t settings_len = strlen(settings_ptr);
- std::string settings(settings_ptr, settings_len);
- // The cue payload is stored in the data-part of the input buffer.
- std::string text(input->data(), input->data() + input->data_size());
- scoped_refptr<TextCue> text_cue(
- new TextCue(input->timestamp(),
- input->duration(),
- id,
- settings,
- text));
- CueReady(stream, text_cue);
- }
- void TextRenderer::CueReady(
- DemuxerStream* text_stream,
- const scoped_refptr<TextCue>& text_cue) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK_NE(state_, kUninitialized);
- DCHECK_GT(pending_read_count_, 0);
- DCHECK(pending_eos_set_.find(text_stream) != pending_eos_set_.end());
- auto itr = text_track_state_map_.find(text_stream);
- DCHECK(itr != text_track_state_map_.end());
- TextTrackState* state = itr->second.get();
- DCHECK_EQ(state->read_state, TextTrackState::kReadPending);
- DCHECK(state->text_track);
- --pending_read_count_;
- state->read_state = TextTrackState::kReadIdle;
- switch (state_) {
- case kPlaying: {
- if (text_cue.get())
- break;
- const size_t count = pending_eos_set_.erase(text_stream);
- DCHECK_EQ(count, 1U);
- if (pending_eos_set_.empty()) {
- DCHECK_EQ(pending_read_count_, 0);
- state_ = kEnded;
- task_runner_->PostTask(FROM_HERE, ended_cb_);
- return;
- }
- DCHECK_GT(pending_read_count_, 0);
- return;
- }
- case kPausePending: {
- if (text_cue.get())
- break;
- const size_t count = pending_eos_set_.erase(text_stream);
- DCHECK_EQ(count, 1U);
- if (pending_read_count_ > 0) {
- DCHECK(!pending_eos_set_.empty());
- return;
- }
- state_ = kPaused;
- std::move(pause_cb_).Run();
- return;
- }
- case kPaused:
- case kUninitialized:
- case kEnded:
- NOTREACHED();
- return;
- }
- base::TimeDelta start = text_cue->timestamp();
- if (state->text_ranges_.AddCue(start)) {
- base::TimeDelta end = start + text_cue->duration();
- state->text_track->addWebVTTCue(start, end,
- text_cue->id(),
- text_cue->text(),
- text_cue->settings());
- }
- if (state_ == kPlaying) {
- Read(state, text_stream);
- return;
- }
- if (pending_read_count_ == 0) {
- DCHECK_EQ(state_, kPausePending) << "state_ " << state_;
- state_ = kPaused;
- std::move(pause_cb_).Run();
- }
- }
- void TextRenderer::OnAddTextTrackDone(DemuxerStream* text_stream,
- std::unique_ptr<TextTrack> text_track) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK_NE(state_, kUninitialized);
- DCHECK(text_stream);
- DCHECK(text_track);
- std::unique_ptr<TextTrackState> state(
- new TextTrackState(std::move(text_track)));
- text_track_state_map_[text_stream] = std::move(state);
- pending_eos_set_.insert(text_stream);
- if (state_ == kPlaying)
- Read(text_track_state_map_[text_stream].get(), text_stream);
- }
- void TextRenderer::Read(
- TextTrackState* state,
- DemuxerStream* text_stream) {
- DCHECK_NE(state->read_state, TextTrackState::kReadPending);
- state->read_state = TextTrackState::kReadPending;
- ++pending_read_count_;
- text_stream->Read(base::BindOnce(&TextRenderer::BufferReady,
- weak_factory_.GetWeakPtr(), text_stream));
- }
- TextRenderer::TextTrackState::TextTrackState(std::unique_ptr<TextTrack> tt)
- : read_state(kReadIdle), text_track(std::move(tt)) {}
- TextRenderer::TextTrackState::~TextTrackState() = default;
- } // namespace media
|