OLD | NEW |
(Empty) | |
| 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/base/balanced_media_task_runner_factory.h" |
| 6 |
| 7 #include <map> |
| 8 |
| 9 #include "base/bind.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/buffers.h" |
| 15 |
| 16 namespace chromecast { |
| 17 namespace media { |
| 18 |
| 19 // MediaTaskRunnerWithNotification - |
| 20 // Media task runner which also behaves as a media task runner observer. |
| 21 class MediaTaskRunnerWithNotification : public MediaTaskRunner { |
| 22 public: |
| 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); |
| 33 |
| 34 // MediaTaskRunner implementation. |
| 35 virtual bool PostMediaTask( |
| 36 const tracked_objects::Location& from_here, |
| 37 const base::Closure& task, |
| 38 base::TimeDelta timestamp) OVERRIDE; |
| 39 |
| 40 private: |
| 41 virtual ~MediaTaskRunnerWithNotification(); |
| 42 |
| 43 scoped_refptr<MediaTaskRunner> const media_task_runner_; |
| 44 |
| 45 const base::Closure new_task_cb_; |
| 46 const base::Closure shutdown_cb_; |
| 47 |
| 48 DISALLOW_COPY_AND_ASSIGN(MediaTaskRunnerWithNotification); |
| 49 }; |
| 50 |
| 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) { |
| 58 } |
| 59 |
| 60 MediaTaskRunnerWithNotification::~MediaTaskRunnerWithNotification() { |
| 61 shutdown_cb_.Run(); |
| 62 } |
| 63 |
| 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) |
| 71 new_task_cb_.Run(); |
| 72 return may_run_in_future; |
| 73 } |
| 74 |
| 75 |
| 76 // BalancedMediaTaskRunner - |
| 77 // Run media tasks whose timestamp is less or equal to a max timestamp. |
| 78 // |
| 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 { |
| 84 public: |
| 85 explicit BalancedMediaTaskRunner( |
| 86 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); |
| 87 |
| 88 // Schedule tasks whose timestamp is less than or equal to |max_timestamp|. |
| 89 void ScheduleWork(base::TimeDelta max_timestamp); |
| 90 |
| 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; |
| 94 |
| 95 // MediaTaskRunner implementation. |
| 96 virtual bool PostMediaTask( |
| 97 const tracked_objects::Location& from_here, |
| 98 const base::Closure& task, |
| 99 base::TimeDelta timestamp) OVERRIDE; |
| 100 |
| 101 private: |
| 102 virtual ~BalancedMediaTaskRunner(); |
| 103 |
| 104 scoped_refptr<base::SingleThreadTaskRunner> const task_runner_; |
| 105 |
| 106 // Protects the following variables. |
| 107 mutable base::Lock lock_; |
| 108 |
| 109 // Possible pending media task. |
| 110 tracked_objects::Location from_here_; |
| 111 base::Closure pending_task_; |
| 112 |
| 113 // Timestamp of the last posted task. |
| 114 // Is initialized to ::media::kNoTimestamp(). |
| 115 base::TimeDelta last_timestamp_; |
| 116 |
| 117 DISALLOW_COPY_AND_ASSIGN(BalancedMediaTaskRunner); |
| 118 }; |
| 119 |
| 120 BalancedMediaTaskRunner::BalancedMediaTaskRunner( |
| 121 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) |
| 122 : task_runner_(task_runner), |
| 123 last_timestamp_(::media::kNoTimestamp()) { |
| 124 } |
| 125 |
| 126 BalancedMediaTaskRunner::~BalancedMediaTaskRunner() { |
| 127 } |
| 128 |
| 129 void BalancedMediaTaskRunner::ScheduleWork(base::TimeDelta max_media_time) { |
| 130 base::Closure task; |
| 131 { |
| 132 base::AutoLock auto_lock(lock_); |
| 133 if (pending_task_.is_null()) |
| 134 return; |
| 135 |
| 136 if (last_timestamp_ != ::media::kNoTimestamp() && |
| 137 last_timestamp_ >= max_media_time) { |
| 138 return; |
| 139 } |
| 140 |
| 141 task = base::ResetAndReturn(&pending_task_); |
| 142 } |
| 143 task_runner_->PostTask(from_here_, task); |
| 144 } |
| 145 |
| 146 base::TimeDelta BalancedMediaTaskRunner::GetMediaTimestamp() const { |
| 147 base::AutoLock auto_lock(lock_); |
| 148 return last_timestamp_; |
| 149 } |
| 150 |
| 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()); |
| 156 |
| 157 // Pass through for a task with no timestamp. |
| 158 if (timestamp == ::media::kNoTimestamp()) { |
| 159 return task_runner_->PostTask(from_here, task); |
| 160 } |
| 161 |
| 162 base::AutoLock auto_lock(lock_); |
| 163 |
| 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_) { |
| 168 return false; |
| 169 } |
| 170 |
| 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; |
| 176 |
| 177 return true; |
| 178 } |
| 179 |
| 180 |
| 181 BalancedMediaTaskRunnerFactory::BalancedMediaTaskRunnerFactory( |
| 182 base::TimeDelta max_delta) |
| 183 : max_delta_(max_delta) { |
| 184 } |
| 185 |
| 186 BalancedMediaTaskRunnerFactory::~BalancedMediaTaskRunnerFactory() { |
| 187 } |
| 188 |
| 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( |
| 196 media_task_runner, |
| 197 base::Bind(&BalancedMediaTaskRunnerFactory::OnNewTask, this), |
| 198 base::Bind( |
| 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 |
| 205 // notification. |
| 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; |
| 211 } |
| 212 |
| 213 void BalancedMediaTaskRunnerFactory::OnNewTask() { |
| 214 typedef |
| 215 std::multimap<base::TimeDelta, scoped_refptr<BalancedMediaTaskRunner> > |
| 216 TaskRunnerMap; |
| 217 TaskRunnerMap runnable_task_runner; |
| 218 |
| 219 base::AutoLock auto_lock(lock_); |
| 220 |
| 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()) |
| 226 continue; |
| 227 runnable_task_runner.insert( |
| 228 std::pair<base::TimeDelta, scoped_refptr<BalancedMediaTaskRunner> >( |
| 229 timestamp, *it)); |
| 230 } |
| 231 |
| 232 // If there is no media task, just returns. |
| 233 if (runnable_task_runner.empty()) |
| 234 return; |
| 235 |
| 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); |
| 242 } |
| 243 } |
| 244 |
| 245 void BalancedMediaTaskRunnerFactory::UnregisterMediaTaskRunner( |
| 246 const scoped_refptr<BalancedMediaTaskRunner>& media_task_runner) { |
| 247 base::AutoLock auto_lock(lock_); |
| 248 task_runners_.erase(media_task_runner); |
| 249 } |
| 250 |
| 251 } // namespace media |
| 252 } // namespace chromecast |
OLD | NEW |