1// Copyright 2014 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 "chromecast/media/cma/filters/demuxer_stream_adapter.h"
6
7#include "base/bind.h"
8#include "base/callback_helpers.h"
9#include "base/single_thread_task_runner.h"
10#include "chromecast/media/cma/base/balanced_media_task_runner_factory.h"
11#include "chromecast/media/cma/base/cma_logging.h"
12#include "chromecast/media/cma/base/decoder_buffer_adapter.h"
13#include "chromecast/media/cma/base/media_task_runner.h"
14#include "media/base/bind_to_current_loop.h"
15#include "media/base/buffers.h"
16#include "media/base/decoder_buffer.h"
17#include "media/base/demuxer_stream.h"
18
19namespace chromecast {
20namespace media {
21
22namespace {
23
24class DummyMediaTaskRunner : public MediaTaskRunner {
25 public:
26  DummyMediaTaskRunner(
27      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
28
29  // MediaTaskRunner implementation.
30  virtual bool PostMediaTask(
31      const tracked_objects::Location& from_here,
32      const base::Closure& task,
33      base::TimeDelta timestamp) OVERRIDE;
34
35 private:
36  virtual ~DummyMediaTaskRunner();
37
38  scoped_refptr<base::SingleThreadTaskRunner> const task_runner_;
39
40  DISALLOW_COPY_AND_ASSIGN(DummyMediaTaskRunner);
41};
42
43DummyMediaTaskRunner::DummyMediaTaskRunner(
44    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
45  : task_runner_(task_runner) {
46}
47
48DummyMediaTaskRunner::~DummyMediaTaskRunner() {
49}
50
51bool DummyMediaTaskRunner::PostMediaTask(
52    const tracked_objects::Location& from_here,
53    const base::Closure& task,
54    base::TimeDelta timestamp) {
55  return task_runner_->PostTask(from_here, task);
56}
57
58}  // namespace
59
60DemuxerStreamAdapter::DemuxerStreamAdapter(
61    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
62    const scoped_refptr<BalancedMediaTaskRunnerFactory>&
63    media_task_runner_factory,
64    ::media::DemuxerStream* demuxer_stream)
65    : task_runner_(task_runner),
66      media_task_runner_factory_(media_task_runner_factory),
67      media_task_runner_(new DummyMediaTaskRunner(task_runner)),
68      demuxer_stream_(demuxer_stream),
69      is_pending_read_(false),
70      is_pending_demuxer_read_(false),
71      weak_factory_(this),
72      weak_this_(weak_factory_.GetWeakPtr()) {
73  ResetMediaTaskRunner();
74  thread_checker_.DetachFromThread();
75}
76
77DemuxerStreamAdapter::~DemuxerStreamAdapter() {
78  // Needed since we use weak pointers:
79  // weak pointers must be invalidated on the same thread.
80  DCHECK(thread_checker_.CalledOnValidThread());
81}
82
83void DemuxerStreamAdapter::Read(const ReadCB& read_cb) {
84  DCHECK(thread_checker_.CalledOnValidThread());
85
86  DCHECK(flush_cb_.is_null());
87
88  // Support only one read at a time.
89  DCHECK(!is_pending_read_);
90  is_pending_read_ = true;
91  ReadInternal(read_cb);
92}
93
94void DemuxerStreamAdapter::ReadInternal(const ReadCB& read_cb) {
95  bool may_run_in_future = media_task_runner_->PostMediaTask(
96      FROM_HERE,
97      base::Bind(&DemuxerStreamAdapter::RequestBuffer, weak_this_, read_cb),
98      max_pts_);
99  DCHECK(may_run_in_future);
100}
101
102void DemuxerStreamAdapter::Flush(const base::Closure& flush_cb) {
103  DCHECK(thread_checker_.CalledOnValidThread());
104  CMALOG(kLogControl) << __FUNCTION__;
105
106  // Flush cancels any pending read.
107  is_pending_read_ = false;
108
109  // Reset the decoder configurations.
110  audio_config_ = ::media::AudioDecoderConfig();
111  video_config_ = ::media::VideoDecoderConfig();
112
113  // Create a new media task runner for the upcoming media timeline.
114  ResetMediaTaskRunner();
115
116  DCHECK(flush_cb_.is_null());
117  if (is_pending_demuxer_read_) {
118    // If there is a pending demuxer read, the implicit contract
119    // is that the pending read must be completed before invoking the
120    // flush callback.
121    flush_cb_ = flush_cb;
122    return;
123  }
124
125  // At this point, there is no more pending demuxer read,
126  // so all the previous tasks associated with the current timeline
127  // can be cancelled.
128  weak_factory_.InvalidateWeakPtrs();
129  weak_this_ = weak_factory_.GetWeakPtr();
130
131  CMALOG(kLogControl) << "Flush done";
132  flush_cb.Run();
133}
134
135void DemuxerStreamAdapter::ResetMediaTaskRunner() {
136  DCHECK(thread_checker_.CalledOnValidThread());
137
138  max_pts_ = ::media::kNoTimestamp();
139  if (media_task_runner_factory_.get()) {
140    media_task_runner_ =
141        media_task_runner_factory_->CreateMediaTaskRunner(task_runner_);
142  }
143}
144
145void DemuxerStreamAdapter::RequestBuffer(const ReadCB& read_cb) {
146  DCHECK(thread_checker_.CalledOnValidThread());
147  is_pending_demuxer_read_ = true;
148  demuxer_stream_->Read(::media::BindToCurrentLoop(
149      base::Bind(&DemuxerStreamAdapter::OnNewBuffer, weak_this_, read_cb)));
150}
151
152void DemuxerStreamAdapter::OnNewBuffer(
153    const ReadCB& read_cb,
154    ::media::DemuxerStream::Status status,
155    const scoped_refptr< ::media::DecoderBuffer>& input) {
156  DCHECK(thread_checker_.CalledOnValidThread());
157
158  is_pending_demuxer_read_ = false;
159
160  // Just discard the buffer in the flush stage.
161  if (!flush_cb_.is_null()) {
162    CMALOG(kLogControl) << "Flush done";
163    base::ResetAndReturn(&flush_cb_).Run();
164    return;
165  }
166
167  if (status == ::media::DemuxerStream::kAborted) {
168    DCHECK(input.get() == NULL);
169    return;
170  }
171
172  if (status == ::media::DemuxerStream::kConfigChanged) {
173    DCHECK(input.get() == NULL);
174    if (demuxer_stream_->type() == ::media::DemuxerStream::VIDEO)
175      video_config_ = demuxer_stream_->video_decoder_config();
176    if (demuxer_stream_->type() == ::media::DemuxerStream::AUDIO)
177      audio_config_ = demuxer_stream_->audio_decoder_config();
178
179    // Got a new config, but we still need to get a frame.
180    ReadInternal(read_cb);
181    return;
182  }
183
184  DCHECK_EQ(status, ::media::DemuxerStream::kOk);
185
186  // Updates the timestamp used for task scheduling.
187  if (!input->end_of_stream() &&
188      input->timestamp() != ::media::kNoTimestamp() &&
189      (max_pts_ == ::media::kNoTimestamp() || input->timestamp() > max_pts_)) {
190    max_pts_ = input->timestamp();
191  }
192
193  // Provides the buffer as well as possibly valid audio and video configs.
194  is_pending_read_ = false;
195  scoped_refptr<DecoderBufferBase> buffer(new DecoderBufferAdapter(input));
196  read_cb.Run(buffer, audio_config_, video_config_);
197
198  // Back to the default audio/video config:
199  // an invalid audio/video config means there is no config update.
200  if (audio_config_.IsValidConfig())
201    audio_config_ = ::media::AudioDecoderConfig();
202  if (video_config_.IsValidConfig())
203    video_config_ = ::media::VideoDecoderConfig();
204}
205
206}  // namespace media
207}  // namespace chromecast
208