Chromium Code Reviews| Index: chromecast/media/cma/base/balanced_media_task_runner_factory.cc |
| diff --git a/chromecast/media/cma/base/balanced_media_task_runner_factory.cc b/chromecast/media/cma/base/balanced_media_task_runner_factory.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..50f055a25027c16a3b6a53108b162f6d0b9705a2 |
| --- /dev/null |
| +++ b/chromecast/media/cma/base/balanced_media_task_runner_factory.cc |
| @@ -0,0 +1,240 @@ |
| +// Copyright 2014 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 "chromecast/media/cma/base/balanced_media_task_runner_factory.h" |
| + |
| +#include <map> |
| + |
| +#include "base/bind.h" |
| +#include "base/callback_helpers.h" |
| +#include "base/logging.h" |
| +#include "base/single_thread_task_runner.h" |
| +#include "chromecast/media/cma/base/media_task_runner.h" |
| +#include "media/base/buffers.h" |
| + |
| +namespace chromecast { |
| +namespace media { |
| + |
| +// MediaTaskRunnerWithNotification - |
| +// Media task runner which also behaves as a media task runner observer. |
| +class MediaTaskRunnerWithNotification |
| + : public MediaTaskRunner { |
|
xhwang
2014/09/03 17:06:39
fit in one line?
damienv1
2014/09/03 22:01:03
Done.
|
| + public: |
| + MediaTaskRunnerWithNotification( |
| + const scoped_refptr<MediaTaskRunner>& media_task_runner, |
| + const base::Closure new_task_cb, |
| + const base::Closure shutdown_cb); |
|
xhwang
2014/09/03 17:06:39
Document what the two callbacks are and when they
damienv1
2014/09/03 22:01:03
Good catch. That's an unwilling mistake on my side
|
| + |
| + // MediaTaskRunner implementation. |
| + virtual bool PostMediaTask( |
| + const tracked_objects::Location& from_here, |
| + const base::Closure& task, |
| + base::TimeDelta timestamp) OVERRIDE; |
| + |
| + private: |
| + virtual ~MediaTaskRunnerWithNotification(); |
| + |
| + scoped_refptr<MediaTaskRunner> const media_task_runner_; |
| + |
| + const base::Closure new_task_cb_; |
| + const base::Closure shutdown_cb_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(MediaTaskRunnerWithNotification); |
| +}; |
| + |
| +MediaTaskRunnerWithNotification::MediaTaskRunnerWithNotification( |
| + const scoped_refptr<MediaTaskRunner>& media_task_runner, |
| + const base::Closure new_task_cb, |
| + const base::Closure shutdown_cb) |
| + : media_task_runner_(media_task_runner), |
| + new_task_cb_(new_task_cb), |
| + shutdown_cb_(shutdown_cb) { |
| +} |
| + |
| +MediaTaskRunnerWithNotification::~MediaTaskRunnerWithNotification() { |
| + shutdown_cb_.Run(); |
| +} |
| + |
| +bool MediaTaskRunnerWithNotification::PostMediaTask( |
| + const tracked_objects::Location& from_here, |
| + const base::Closure& task, |
| + base::TimeDelta timestamp) { |
| + bool may_run_in_future = |
| + media_task_runner_->PostMediaTask(from_here, task, timestamp); |
| + if (may_run_in_future) |
| + new_task_cb_.Run(); |
| + return may_run_in_future; |
| +} |
| + |
| + |
| +// BalancedMediaTaskRunner - |
| +// Run media tasks whose timestamp is less or equal to a max timestamp. |
| +// |
| +// Restrictions of BalancedMediaTaskRunner: |
| +// - Can have at most one task in the queue. |
| +// - Tasks should be given by increasing timestamps. |
| +class BalancedMediaTaskRunner |
| + : public MediaTaskRunner { |
| + public: |
| + explicit BalancedMediaTaskRunner( |
| + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); |
| + |
| + // Schedule tasks whose timestamp is less than or equal to |max_timestamp|. |
| + void ScheduleWork(base::TimeDelta max_timestamp); |
| + |
| + // Return the timestamp of the last media task. |
| + // Return ::media::kNoTimestamp() if no media task has been posted. |
| + base::TimeDelta GetMediaTimestamp() const; |
| + |
| + // MediaTaskRunner implementation. |
| + virtual bool PostMediaTask( |
| + const tracked_objects::Location& from_here, |
| + const base::Closure& task, |
| + base::TimeDelta timestamp) OVERRIDE; |
| + |
| + private: |
| + virtual ~BalancedMediaTaskRunner(); |
| + |
| + scoped_refptr<base::SingleThreadTaskRunner> const task_runner_; |
| + |
| + // Protects the following variables. |
| + mutable base::Lock lock_; |
| + |
| + // Possible pending media task. |
| + tracked_objects::Location from_here_; |
| + base::Closure pending_task_; |
| + |
| + // Timestamp of the last posted task. |
| + // Is initialized to ::media::kNoTimestamp(). |
| + base::TimeDelta last_timestamp_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BalancedMediaTaskRunner); |
| +}; |
| + |
| +BalancedMediaTaskRunner::BalancedMediaTaskRunner( |
| + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) |
| + : task_runner_(task_runner), |
| + last_timestamp_(::media::kNoTimestamp()) { |
| +} |
| + |
| +BalancedMediaTaskRunner::~BalancedMediaTaskRunner() { |
| +} |
| + |
| +void BalancedMediaTaskRunner::ScheduleWork(base::TimeDelta max_media_time) { |
| + base::Closure task; |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + if (pending_task_.is_null()) |
| + return; |
| + |
| + if (last_timestamp_ != ::media::kNoTimestamp() && |
| + last_timestamp_ >= max_media_time) { |
| + return; |
| + } |
| + |
| + task = base::ResetAndReturn(&pending_task_); |
| + } |
| + task_runner_->PostTask(from_here_, task); |
| +} |
| + |
| +base::TimeDelta BalancedMediaTaskRunner::GetMediaTimestamp() const { |
| + base::AutoLock auto_lock(lock_); |
| + return last_timestamp_; |
| +} |
| + |
| +bool BalancedMediaTaskRunner::PostMediaTask( |
| + const tracked_objects::Location& from_here, |
| + const base::Closure& task, |
| + base::TimeDelta timestamp) { |
| + DCHECK(!task.is_null()); |
| + |
| + // Pass through for a task with no timestamp. |
| + if (timestamp == ::media::kNoTimestamp()) { |
| + return task_runner_->PostTask(from_here, task); |
| + } |
| + |
| + base::AutoLock auto_lock(lock_); |
| + |
| + // Timestamps must be in order. |
| + // Any task that does not meet that condition is simply discarded. |
| + if (last_timestamp_ != ::media::kNoTimestamp() && |
| + timestamp < last_timestamp_) { |
| + return false; |
| + } |
| + |
| + // Only support one pending task at a time. |
| + DCHECK(pending_task_.is_null()); |
| + from_here_ = from_here; |
| + pending_task_ = task; |
| + last_timestamp_ = timestamp; |
| + |
| + return true; |
| +} |
| + |
| + |
| +BalancedMediaTaskRunnerFactory::BalancedMediaTaskRunnerFactory( |
| + base::TimeDelta max_delta) |
| + : max_delta_(max_delta) { |
| +} |
| + |
| +BalancedMediaTaskRunnerFactory::~BalancedMediaTaskRunnerFactory() { |
| +} |
| + |
| +scoped_refptr<MediaTaskRunner> |
| +BalancedMediaTaskRunnerFactory::CreateMediaTaskRunner( |
| + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { |
| + scoped_refptr<BalancedMediaTaskRunner> media_task_runner( |
| + new BalancedMediaTaskRunner(task_runner)); |
| + scoped_refptr<MediaTaskRunnerWithNotification> media_task_runner_wrapper( |
| + new MediaTaskRunnerWithNotification( |
| + media_task_runner, |
| + base::Bind(&BalancedMediaTaskRunnerFactory::OnNewTask, this), |
| + base::Bind( |
| + &BalancedMediaTaskRunnerFactory::UnregisterMediaTaskRunner, |
| + this, media_task_runner))); |
| + base::AutoLock auto_lock(lock_); |
| + task_runners_.insert(media_task_runner); |
| + return media_task_runner_wrapper; |
| +} |
| + |
| +void BalancedMediaTaskRunnerFactory::OnNewTask() { |
| + typedef |
| + std::multimap<base::TimeDelta, scoped_refptr<BalancedMediaTaskRunner> > |
| + TaskRunnerMap; |
| + TaskRunnerMap runnable_task_runner; |
| + |
| + base::AutoLock auto_lock(lock_); |
| + |
| + // Get the minimum timestamp among all streams. |
| + for (MediaTaskRunnerSet::const_iterator it = task_runners_.begin(); |
| + it != task_runners_.end(); ++it) { |
| + base::TimeDelta timestamp((*it)->GetMediaTimestamp()); |
| + if (timestamp == ::media::kNoTimestamp()) |
| + continue; |
| + runnable_task_runner.insert( |
| + std::pair<base::TimeDelta, scoped_refptr<BalancedMediaTaskRunner> >( |
| + timestamp, *it)); |
| + } |
| + |
| + // If there is no media task, just returns. |
| + if (runnable_task_runner.empty()) |
| + return; |
| + |
| + // Run tasks which meet the balancing criteria. |
| + base::TimeDelta min_timestamp(runnable_task_runner.begin()->first); |
| + base::TimeDelta max_timestamp = min_timestamp + max_delta_; |
| + for (TaskRunnerMap::iterator it = runnable_task_runner.begin(); |
| + it != runnable_task_runner.end(); ++it) { |
| + (*it).second->ScheduleWork(max_timestamp); |
| + } |
| +} |
| + |
| +void BalancedMediaTaskRunnerFactory::UnregisterMediaTaskRunner( |
| + const scoped_refptr<BalancedMediaTaskRunner>& media_task_runner) { |
| + base::AutoLock auto_lock(lock_); |
| + task_runners_.erase(media_task_runner); |
| +} |
| + |
| +} // namespace media |
| +} // namespace chromecast |