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.
5 #include "chromecast/media/cma/base/balanced_media_task_runner_factory.h"
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "base/single_thread_task_runner.h"
13 #include "chromecast/media/cma/base/media_task_runner.h"
14 #include "media/base/timestamp_constants.h"
16 namespace chromecast
{
19 // MediaTaskRunnerWithNotification -
20 // Media task runner which also behaves as a media task runner observer.
21 class MediaTaskRunnerWithNotification
: public MediaTaskRunner
{
23 // Wraps a MediaTaskRunner so that a third party can:
24 // - be notified when a PostMediaTask is performed on this media task runner.
25 // |new_task_cb| is invoked in that case.
26 // - monitor the lifetime of the media task runner, i.e. check when the media
27 // task runner is not needed anymore.
28 // |shutdown_cb| is invoked in that case.
29 MediaTaskRunnerWithNotification(
30 const scoped_refptr
<MediaTaskRunner
>& media_task_runner
,
31 const base::Closure
& new_task_cb
,
32 const base::Closure
& shutdown_cb
);
34 // MediaTaskRunner implementation.
36 const tracked_objects::Location
& from_here
,
37 const base::Closure
& task
,
38 base::TimeDelta timestamp
) override
;
41 ~MediaTaskRunnerWithNotification() override
;
43 scoped_refptr
<MediaTaskRunner
> const media_task_runner_
;
45 const base::Closure new_task_cb_
;
46 const base::Closure shutdown_cb_
;
48 DISALLOW_COPY_AND_ASSIGN(MediaTaskRunnerWithNotification
);
51 MediaTaskRunnerWithNotification::MediaTaskRunnerWithNotification(
52 const scoped_refptr
<MediaTaskRunner
>& media_task_runner
,
53 const base::Closure
& new_task_cb
,
54 const base::Closure
& shutdown_cb
)
55 : media_task_runner_(media_task_runner
),
56 new_task_cb_(new_task_cb
),
57 shutdown_cb_(shutdown_cb
) {
60 MediaTaskRunnerWithNotification::~MediaTaskRunnerWithNotification() {
64 bool MediaTaskRunnerWithNotification::PostMediaTask(
65 const tracked_objects::Location
& from_here
,
66 const base::Closure
& task
,
67 base::TimeDelta timestamp
) {
68 bool may_run_in_future
=
69 media_task_runner_
->PostMediaTask(from_here
, task
, timestamp
);
70 if (may_run_in_future
)
72 return may_run_in_future
;
76 // BalancedMediaTaskRunner -
77 // Run media tasks whose timestamp is less or equal to a max timestamp.
79 // Restrictions of BalancedMediaTaskRunner:
80 // - Can have at most one task in the queue.
81 // - Tasks should be given by increasing timestamps.
82 class BalancedMediaTaskRunner
83 : public MediaTaskRunner
{
85 explicit BalancedMediaTaskRunner(
86 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
);
88 // Schedule tasks whose timestamp is less than or equal to |max_timestamp|.
89 void ScheduleWork(base::TimeDelta max_timestamp
);
91 // Return the timestamp of the last media task.
92 // Return ::media::kNoTimestamp() if no media task has been posted.
93 base::TimeDelta
GetMediaTimestamp() const;
95 // MediaTaskRunner implementation.
97 const tracked_objects::Location
& from_here
,
98 const base::Closure
& task
,
99 base::TimeDelta timestamp
) override
;
102 ~BalancedMediaTaskRunner() override
;
104 scoped_refptr
<base::SingleThreadTaskRunner
> const task_runner_
;
106 // Protects the following variables.
107 mutable base::Lock lock_
;
109 // Possible pending media task.
110 tracked_objects::Location from_here_
;
111 base::Closure pending_task_
;
113 // Timestamp of the last posted task.
114 // Is initialized to ::media::kNoTimestamp().
115 base::TimeDelta last_timestamp_
;
117 DISALLOW_COPY_AND_ASSIGN(BalancedMediaTaskRunner
);
120 BalancedMediaTaskRunner::BalancedMediaTaskRunner(
121 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
)
122 : task_runner_(task_runner
),
123 last_timestamp_(::media::kNoTimestamp()) {
126 BalancedMediaTaskRunner::~BalancedMediaTaskRunner() {
129 void BalancedMediaTaskRunner::ScheduleWork(base::TimeDelta max_media_time
) {
132 base::AutoLock
auto_lock(lock_
);
133 if (pending_task_
.is_null())
136 if (last_timestamp_
!= ::media::kNoTimestamp() &&
137 last_timestamp_
>= max_media_time
) {
141 task
= base::ResetAndReturn(&pending_task_
);
143 task_runner_
->PostTask(from_here_
, task
);
146 base::TimeDelta
BalancedMediaTaskRunner::GetMediaTimestamp() const {
147 base::AutoLock
auto_lock(lock_
);
148 return last_timestamp_
;
151 bool BalancedMediaTaskRunner::PostMediaTask(
152 const tracked_objects::Location
& from_here
,
153 const base::Closure
& task
,
154 base::TimeDelta timestamp
) {
155 DCHECK(!task
.is_null());
157 // Pass through for a task with no timestamp.
158 if (timestamp
== ::media::kNoTimestamp()) {
159 return task_runner_
->PostTask(from_here
, task
);
162 base::AutoLock
auto_lock(lock_
);
164 // Timestamps must be in order.
165 // Any task that does not meet that condition is simply discarded.
166 if (last_timestamp_
!= ::media::kNoTimestamp() &&
167 timestamp
< last_timestamp_
) {
171 // Only support one pending task at a time.
172 DCHECK(pending_task_
.is_null());
173 from_here_
= from_here
;
174 pending_task_
= task
;
175 last_timestamp_
= timestamp
;
181 BalancedMediaTaskRunnerFactory::BalancedMediaTaskRunnerFactory(
182 base::TimeDelta max_delta
)
183 : max_delta_(max_delta
) {
186 BalancedMediaTaskRunnerFactory::~BalancedMediaTaskRunnerFactory() {
189 scoped_refptr
<MediaTaskRunner
>
190 BalancedMediaTaskRunnerFactory::CreateMediaTaskRunner(
191 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
) {
192 scoped_refptr
<BalancedMediaTaskRunner
> media_task_runner(
193 new BalancedMediaTaskRunner(task_runner
));
194 scoped_refptr
<MediaTaskRunnerWithNotification
> media_task_runner_wrapper(
195 new MediaTaskRunnerWithNotification(
197 base::Bind(&BalancedMediaTaskRunnerFactory::OnNewTask
, this),
199 &BalancedMediaTaskRunnerFactory::UnregisterMediaTaskRunner
,
200 this, media_task_runner
)));
201 base::AutoLock
auto_lock(lock_
);
202 // Note that |media_task_runner| is inserted here and
203 // not |media_task_runner_wrapper|. Otherwise, we would always have one
204 // ref on |media_task_runner_wrapper| and would never get the release
206 // When |media_task_runner_wrapper| is going away,
207 // BalancedMediaTaskRunnerFactory will receive a notification and will in
208 // turn remove |media_task_runner|.
209 task_runners_
.insert(media_task_runner
);
210 return media_task_runner_wrapper
;
213 void BalancedMediaTaskRunnerFactory::OnNewTask() {
215 std::multimap
<base::TimeDelta
, scoped_refptr
<BalancedMediaTaskRunner
> >
217 TaskRunnerMap runnable_task_runner
;
219 base::AutoLock
auto_lock(lock_
);
221 // Get the minimum timestamp among all streams.
222 for (MediaTaskRunnerSet::const_iterator it
= task_runners_
.begin();
223 it
!= task_runners_
.end(); ++it
) {
224 base::TimeDelta
timestamp((*it
)->GetMediaTimestamp());
225 if (timestamp
== ::media::kNoTimestamp())
227 runnable_task_runner
.insert(
228 std::pair
<base::TimeDelta
, scoped_refptr
<BalancedMediaTaskRunner
> >(
232 // If there is no media task, just returns.
233 if (runnable_task_runner
.empty())
236 // Run tasks which meet the balancing criteria.
237 base::TimeDelta
min_timestamp(runnable_task_runner
.begin()->first
);
238 base::TimeDelta max_timestamp
= min_timestamp
+ max_delta_
;
239 for (TaskRunnerMap::iterator it
= runnable_task_runner
.begin();
240 it
!= runnable_task_runner
.end(); ++it
) {
241 (*it
).second
->ScheduleWork(max_timestamp
);
245 void BalancedMediaTaskRunnerFactory::UnregisterMediaTaskRunner(
246 const scoped_refptr
<BalancedMediaTaskRunner
>& media_task_runner
) {
248 base::AutoLock
auto_lock(lock_
);
249 task_runners_
.erase(media_task_runner
);
251 // After removing one of the task runners some of the other task runners might
252 // need to be waken up, if they are no longer blocked by the balancing
253 // restrictions with the old stream.
258 } // namespace chromecast