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..93132603cf3952e34c6fcec7f297e02b2b93e96f |
--- /dev/null |
+++ b/chromecast/media/cma/base/balanced_media_task_runner_factory.cc |
@@ -0,0 +1,252 @@ |
+// 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 { |
+ public: |
+ // Wraps a MediaTaskRunner so that a third party can: |
+ // - be notified when a PostMediaTask is performed on this media task runner. |
+ // |new_task_cb| is invoked in that case. |
+ // - monitor the lifetime of the media task runner, i.e. check when the media |
+ // task runner is not needed anymore. |
+ // |shutdown_cb| is invoked in that case. |
+ MediaTaskRunnerWithNotification( |
+ const scoped_refptr<MediaTaskRunner>& media_task_runner, |
+ const base::Closure& new_task_cb, |
+ const base::Closure& shutdown_cb); |
+ |
+ // 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_); |
+ // Note that |media_task_runner| is inserted here and |
+ // not |media_task_runner_wrapper|. Otherwise, we would always have one |
+ // ref on |media_task_runner_wrapper| and would never get the release |
+ // notification. |
+ // When |media_task_runner_wrapper| is going away, |
+ // BalancedMediaTaskRunnerFactory will receive a notification and will in |
+ // turn remove |media_task_runner|. |
+ 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 |