Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/logging.h" | 5 #include "base/logging.h" |
| 6 #include "mojo/public/cpp/application/connect.h" | 6 #include "mojo/public/cpp/application/connect.h" |
| 7 #include "mojo/services/media/common/cpp/timeline.h" | 7 #include "mojo/services/media/common/cpp/timeline.h" |
| 8 #include "services/media/factory_service/media_player_impl.h" | 8 #include "services/media/factory_service/media_player_impl.h" |
| 9 #include "services/media/framework/parts/reader.h" | 9 #include "services/media/framework/parts/reader.h" |
| 10 #include "services/media/framework/util/callback_joiner.h" | 10 #include "services/media/framework/util/callback_joiner.h" |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 76 break; | 76 break; |
| 77 // TODO(dalesat): Enable other stream types. | 77 // TODO(dalesat): Enable other stream types. |
| 78 default: | 78 default: |
| 79 break; | 79 break; |
| 80 } | 80 } |
| 81 } | 81 } |
| 82 | 82 |
| 83 callback_joiner->WhenJoined([this]() { | 83 callback_joiner->WhenJoined([this]() { |
| 84 // The enabled streams are prepared. | 84 // The enabled streams are prepared. |
| 85 factory_.reset(); | 85 factory_.reset(); |
| 86 state_ = State::kPaused; | 86 state_ = State::kFlushed; |
| 87 Update(); | 87 Update(); |
| 88 }); | 88 }); |
| 89 }); | 89 }); |
| 90 } | 90 } |
| 91 | 91 |
| 92 MediaPlayerImpl::~MediaPlayerImpl() {} | 92 MediaPlayerImpl::~MediaPlayerImpl() {} |
| 93 | 93 |
| 94 void MediaPlayerImpl::Update() { | 94 void MediaPlayerImpl::Update() { |
| 95 // This method is called whenever we might want to take action based on the | |
| 96 // current state and recent events. The current state is in |state_|. Recent | |
| 97 // events are recorded in |target_state_|, which indicates what state we'd | |
| 98 // like to transition to, |target_position_|, which can indicate a position | |
| 99 // we'd like to stream to, and |end_of_stream_| which tells us we've reached | |
| 100 // end of stream. | |
| 101 // | |
| 102 // The states are as follows: | |
| 103 // | |
| 104 // |kWaiting| - Indicates that we've done something asynchronous, and no | |
| 105 // further action should be taken by the state machine until that | |
| 106 // something completes (at which point the callback will change | |
| 107 // the state and call |Update|). | |
| 108 // |kFlushed| - Indicates that presentation time is not progressing and that | |
| 109 // the pipeline is not primed with packets. This is the initial | |
| 110 // state and the state we transition to in preparation for | |
| 111 // seeking. A seek is currently only done when when the pipeline | |
| 112 // is clear of packets. | |
| 113 // |kPrimed| - Indicates that presentation time is progressing and that the | |
|
kulakowski
2016/06/21 01:22:54
not progressing?
dalesat
2016/06/21 16:57:35
Done.
| |
| 114 // pipeline is primed with packets. We transition to this state | |
| 115 // when the client calls |Pause|, either from |kFlushed| or | |
| 116 // |kPlaying| state. | |
| 117 // |kPlaying| - Indicates that presentation time is progressing and there are | |
| 118 // packets in the pipeline. We transition to this state when the | |
| 119 // client calls |Play|. If we're in |kFlushed| when |Play| is | |
| 120 // called, we transition through |kPrimed| state. | |
| 121 // | |
| 122 // The while loop that surrounds all the logic below is there because, after | |
| 123 // taking some action and transitioning to a new state, we may want to check | |
| 124 // to see if there's more to do in the new state. You'll also notice that | |
| 125 // the callback lambdas generally call |Update|. | |
| 95 while (true) { | 126 while (true) { |
| 96 switch (state_) { | 127 switch (state_) { |
| 97 case State::kPaused: | 128 case State::kFlushed: |
| 129 // Presentation time is not progressing, and the pipeline is clear of | |
| 130 // packets. | |
| 98 if (target_position_ != kUnspecifiedTime) { | 131 if (target_position_ != kUnspecifiedTime) { |
| 99 WhenPausedAndSeeking(); | 132 // We want to seek. Enter |kWaiting| state until the operation is |
| 133 // complete. | |
| 134 state_ = State::kWaiting; | |
| 135 demux_->Seek(target_position_, [this]() { | |
| 136 transform_subject_time_ = target_position_; | |
| 137 target_position_ = kUnspecifiedTime; | |
| 138 state_ = State::kFlushed; | |
| 139 // Back in |kFlushed|. Call |Update| to see if there's further | |
| 140 // action to be taken. | |
| 141 Update(); | |
| 142 }); | |
| 143 | |
| 144 // Done for now. We're in kWaiting, and the callback will call Update | |
| 145 // when the Seek call is complete. | |
| 146 return; | |
| 147 } | |
| 148 | |
| 149 if (target_state_ == State::kPlaying || | |
| 150 target_state_ == State::kPrimed) { | |
| 151 // We want to transition to |kPrimed| or to |kPlaying|, for which | |
| 152 // |kPrimed| is a prerequisite. We enter |kWaiting| state, issue the | |
| 153 // |Prime| request and transition to |kPrimed| when the operation is | |
| 154 // complete. | |
| 155 state_ = State::kWaiting; | |
| 156 demux_->Prime([this]() { | |
| 157 state_ = State::kPrimed; | |
|
kulakowski
2016/06/21 01:22:54
Ok cool this is much easier for me to follow now.
dalesat
2016/06/21 16:57:34
Acknowledged.
| |
| 158 // Now we're in |kPrimed|. Call |Update| to see if there's further | |
| 159 // action to be taken. | |
| 160 Update(); | |
| 161 }); | |
| 162 | |
| 163 // Done for now. We're in |kWaiting|, and the callback will call | |
| 164 // |Update| when the prime is complete. | |
| 165 return; | |
| 166 } | |
| 167 | |
| 168 // No interesting events to respond to. Done for now. | |
| 169 return; | |
| 170 | |
| 171 case State::kPrimed: | |
| 172 // Presentation time is not progressing, and the pipeline is primed with | |
| 173 // packets. | |
| 174 if (target_position_ != kUnspecifiedTime || | |
| 175 target_state_ == State::kFlushed) { | |
| 176 // Either we want to seek or just want to transition to |kFlushed|. | |
| 177 // We transition to |kWaiting|, issue the |Flush| request and | |
| 178 // transition to |kFlushed| when the operation is complete. | |
| 179 state_ = State::kWaiting; | |
| 180 demux_->Flush([this]() { | |
| 181 state_ = State::kFlushed; | |
| 182 // Now we're in |kFlushed|. Call |Update| to see if there's further | |
| 183 // action to be taken. | |
| 184 Update(); | |
| 185 }); | |
| 186 | |
| 187 // Done for now. We're in |kWaiting|, and the callback will call | |
| 188 // |Update| when the flush is complete. | |
| 189 return; | |
| 190 } | |
| 191 | |
| 192 if (target_state_ == State::kPlaying) { | |
| 193 // We want to transition to |kPlaying|. Start the presentation | |
| 194 // timeline and enter |kWaiting|. |SetTimelineTransform| will | |
|
kulakowski
2016/06/21 01:22:54
Ah cool, this was one other big missing piece when
dalesat
2016/06/21 16:57:35
I noticed this before but left it alone. The way I
| |
| 195 // transition to |kPlaying| and call |Update| when the operation | |
| 196 // completes. | |
| 197 SetTimelineTransform(1, 1); | |
| 198 state_ = State::kWaiting; | |
| 199 return; | |
| 200 } | |
| 201 | |
| 202 // No interesting events to respond to. Done for now. | |
| 203 return; | |
| 204 | |
| 205 case State::kPlaying: | |
| 206 // Presentation time is progressing, and packets are moving through | |
| 207 // the pipeline. | |
| 208 if (target_position_ != kUnspecifiedTime || | |
| 209 target_state_ == State::kFlushed || | |
| 210 target_state_ == State::kPrimed) { | |
| 211 // Either we want to seek or we want to stop playback. In either case, | |
| 212 // we need to stop the presentation timeline and enter kWaiting. | |
| 213 // |SetTimelineTransform| will transition to |kPrimed| and call | |
| 214 // |Update| when the operation completes. | |
| 215 SetTimelineTransform(1, 0); | |
|
kulakowski
2016/06/21 01:22:54
ditto here, and it indeed makes sense that 1, 1 wo
dalesat
2016/06/21 16:57:35
Acknowledged.
| |
| 216 state_ = State::kWaiting; | |
| 217 return; | |
| 218 } | |
| 219 | |
| 220 if (end_of_stream_) { | |
| 221 // We've reached end of stream. The presentation timeline stops by | |
| 222 // itself, so we just need to transition to |kPrimed|. | |
| 223 target_state_ = State::kPrimed; | |
| 224 state_ = State::kPrimed; | |
| 225 // Loop around to check if there's more work to do. | |
| 100 break; | 226 break; |
| 101 } | 227 } |
| 102 | 228 |
| 103 if (target_state_ == State::kPlaying) { | 229 // No interesting events to respond to. Done for now. |
| 104 if (!flushed_) { | |
| 105 SetTimelineTransform(1, 1); | |
| 106 state_ = State::kWaiting; | |
| 107 break; | |
| 108 } | |
| 109 | |
| 110 flushed_ = false; | |
| 111 state_ = State::kWaiting; | |
| 112 demux_->Prime([this]() { | |
| 113 SetTimelineTransform(1, 1); | |
| 114 state_ = State::kWaiting; | |
| 115 Update(); | |
| 116 }); | |
| 117 } | |
| 118 return; | |
| 119 | |
| 120 case State::kPlaying: | |
| 121 if (target_position_ != kUnspecifiedTime || | |
| 122 target_state_ == State::kPaused) { | |
| 123 SetTimelineTransform(1, 0); | |
| 124 state_ = State::kWaiting; | |
| 125 break; | |
| 126 } | |
| 127 | |
| 128 if (end_of_stream_) { | |
| 129 target_state_ = State::kPaused; | |
| 130 state_ = State::kPaused; | |
| 131 break; | |
| 132 } | |
| 133 return; | 230 return; |
| 134 | 231 |
| 135 case State::kWaiting: | 232 case State::kWaiting: |
| 233 // Waiting for some async operation. Nothing to do until it completes. | |
| 136 return; | 234 return; |
| 137 } | 235 } |
| 138 } | 236 } |
| 139 } | 237 } |
| 140 | 238 |
| 141 void MediaPlayerImpl::WhenPausedAndSeeking() { | |
| 142 if (!flushed_) { | |
| 143 state_ = State::kWaiting; | |
| 144 demux_->Flush([this]() { | |
| 145 flushed_ = true; | |
| 146 WhenFlushedAndSeeking(); | |
| 147 }); | |
| 148 } else { | |
| 149 WhenFlushedAndSeeking(); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 void MediaPlayerImpl::WhenFlushedAndSeeking() { | |
| 154 state_ = State::kWaiting; | |
| 155 DCHECK(target_position_ != kUnspecifiedTime); | |
| 156 demux_->Seek(target_position_, [this]() { | |
| 157 transform_subject_time_ = target_position_; | |
| 158 target_position_ = kUnspecifiedTime; | |
| 159 state_ = State::kPaused; | |
| 160 Update(); | |
| 161 }); | |
| 162 } | |
| 163 | |
| 164 void MediaPlayerImpl::SetTimelineTransform(uint32_t reference_delta, | 239 void MediaPlayerImpl::SetTimelineTransform(uint32_t reference_delta, |
| 165 uint32_t subject_delta) { | 240 uint32_t subject_delta) { |
| 166 timeline_consumer_->SetTimelineTransform( | 241 timeline_consumer_->SetTimelineTransform( |
| 167 transform_subject_time_, reference_delta, subject_delta, | 242 transform_subject_time_, reference_delta, subject_delta, |
| 168 Timeline::local_now() + kMinimumLeadTime, kUnspecifiedTime, | 243 Timeline::local_now() + kMinimumLeadTime, kUnspecifiedTime, |
| 169 [this, subject_delta](bool completed) { | 244 [this, subject_delta](bool completed) { |
| 170 RCHECK(state_ == State::kWaiting); | 245 RCHECK(state_ == State::kWaiting); |
| 171 | 246 |
| 172 if (subject_delta == 0) { | 247 if (subject_delta == 0) { |
| 173 state_ = State::kPaused; | 248 state_ = State::kPrimed; |
| 174 } else { | 249 } else { |
| 175 state_ = State::kPlaying; | 250 state_ = State::kPlaying; |
| 176 } | 251 } |
| 177 | 252 |
| 178 Update(); | 253 Update(); |
| 179 }); | 254 }); |
| 255 | |
| 256 transform_subject_time_ = kUnspecifiedTime; | |
| 180 } | 257 } |
| 181 | 258 |
| 182 void MediaPlayerImpl::GetStatus(uint64_t version_last_seen, | 259 void MediaPlayerImpl::GetStatus(uint64_t version_last_seen, |
| 183 const GetStatusCallback& callback) { | 260 const GetStatusCallback& callback) { |
| 184 status_publisher_.Get(version_last_seen, callback); | 261 status_publisher_.Get(version_last_seen, callback); |
| 185 } | 262 } |
| 186 | 263 |
| 187 void MediaPlayerImpl::Play() { | 264 void MediaPlayerImpl::Play() { |
| 188 target_state_ = State::kPlaying; | 265 target_state_ = State::kPlaying; |
| 189 Update(); | 266 Update(); |
| 190 } | 267 } |
| 191 | 268 |
| 192 void MediaPlayerImpl::Pause() { | 269 void MediaPlayerImpl::Pause() { |
| 193 target_state_ = State::kPaused; | 270 target_state_ = State::kPrimed; |
| 194 Update(); | 271 Update(); |
| 195 } | 272 } |
| 196 | 273 |
| 197 void MediaPlayerImpl::Seek(int64_t position) { | 274 void MediaPlayerImpl::Seek(int64_t position) { |
| 198 target_position_ = position; | 275 target_position_ = position; |
| 199 Update(); | 276 Update(); |
| 200 } | 277 } |
| 201 | 278 |
| 202 void MediaPlayerImpl::PrepareStream(Stream* stream, | 279 void MediaPlayerImpl::PrepareStream(Stream* stream, |
| 203 size_t index, | 280 size_t index, |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 298 HandleTimelineControlSiteStatusUpdates(version, status.Pass()); | 375 HandleTimelineControlSiteStatusUpdates(version, status.Pass()); |
| 299 }); | 376 }); |
| 300 } | 377 } |
| 301 | 378 |
| 302 MediaPlayerImpl::Stream::Stream() {} | 379 MediaPlayerImpl::Stream::Stream() {} |
| 303 | 380 |
| 304 MediaPlayerImpl::Stream::~Stream() {} | 381 MediaPlayerImpl::Stream::~Stream() {} |
| 305 | 382 |
| 306 } // namespace media | 383 } // namespace media |
| 307 } // namespace mojo | 384 } // namespace mojo |
| OLD | NEW |