Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 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 "media/blink/pipeline_state.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "build/build_config.h" | |
| 10 #include "media/filters/chunk_demuxer.h" | |
| 11 | |
| 12 namespace media { | |
| 13 | |
| 14 PipelineState::PipelineState(Pipeline* pipeline, | |
| 15 const RendererFactoryCB& renderer_factory_cb, | |
| 16 const SeekedCB& seeked_cb, | |
| 17 const SuspendedCB& suspended_cb, | |
| 18 const PipelineStatusCB& error_cb) | |
| 19 : pipeline_(pipeline), | |
| 20 renderer_factory_cb_(renderer_factory_cb), | |
| 21 seeked_cb_(seeked_cb), | |
| 22 suspended_cb_(suspended_cb), | |
| 23 error_cb_(error_cb) { | |
| 24 DCHECK(pipeline_); | |
| 25 DCHECK(!renderer_factory_cb_.is_null()); | |
| 26 DCHECK(!seeked_cb_.is_null()); | |
| 27 DCHECK(!suspended_cb_.is_null()); | |
| 28 DCHECK(!error_cb_.is_null()); | |
| 29 } | |
| 30 | |
| 31 PipelineState::~PipelineState() {} | |
| 32 | |
| 33 // TODO(sandersd): Move ChunkDemuxer API to Demuxer so that Pipeline can | |
| 34 // implement all of this. | |
| 35 void PipelineState::Start(ChunkDemuxer* chunk_demuxer, | |
| 36 Demuxer* demuxer, | |
| 37 const base::Closure& ended_cb, | |
| 38 const PipelineMetadataCB& metadata_cb, | |
| 39 const BufferingStateCB& buffering_state_cb, | |
| 40 const base::Closure& duration_change_cb, | |
| 41 const AddTextTrackCB& add_text_track_cb, | |
| 42 const base::Closure& waiting_for_decryption_key_cb) { | |
| 43 chunk_demuxer_ = chunk_demuxer; | |
| 44 pipeline_->Start(demuxer, renderer_factory_cb_.Run(), ended_cb, error_cb_, | |
| 45 base::Bind(&PipelineState::OnPipelineStatus, | |
| 46 base::Unretained(this), State::PLAYING), | |
| 47 metadata_cb, buffering_state_cb, duration_change_cb, | |
| 48 add_text_track_cb, waiting_for_decryption_key_cb); | |
| 49 } | |
| 50 | |
| 51 void PipelineState::Seek(base::TimeDelta time, bool time_updated) { | |
| 52 // It would be slightly more clear to set this in Dispatch(), but we want to | |
| 53 // be sure it gets updated even if the seek is elided. | |
| 54 // TODO(sandersd): Is it even possible to have an elided seek during Start()? | |
|
sandersd (OOO until July 31)
2016/02/01 23:19:26
FYI: I removed this TODO, because it turns out tha
| |
| 55 // During this time playback should be paused, and so WMPI would handle the | |
| 56 // elision itself. | |
| 57 if (time_updated) | |
| 58 pending_time_update_ = true; | |
| 59 pending_seeked_ = true; | |
| 60 | |
| 61 // If we are already seeking to |time|, just clear any pending seek. This does | |
| 62 // not apply to MSE because the underlying buffer could have been changed | |
|
wolenetz
2016/01/29 21:42:30
nit: as you mentioned yesterday, this could also a
sandersd (OOO until July 31)
2016/02/01 23:19:26
I added a comment about that for now.
| |
| 63 // between the seek calls. | |
| 64 if ((state_ == State::SEEKING || state_ == State::RESUMING) && | |
| 65 seek_time_ == time && !chunk_demuxer_) { | |
| 66 pending_seek_ = false; | |
| 67 return; | |
| 68 } | |
| 69 | |
| 70 pending_seek_time_ = time; | |
| 71 pending_seek_ = true; | |
| 72 Dispatch(); | |
| 73 } | |
| 74 | |
| 75 void PipelineState::Suspend() { | |
| 76 pending_resume_ = false; | |
| 77 if (state_ != State::SUSPENDING && state_ != State::SUSPENDED) { | |
| 78 pending_suspend_ = true; | |
| 79 Dispatch(); | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 void PipelineState::Resume() { | |
| 84 pending_suspend_ = false; | |
| 85 if (state_ == State::SUSPENDING || state_ == State::SUSPENDED) { | |
| 86 pending_resume_ = true; | |
| 87 Dispatch(); | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 bool PipelineState::IsPlaying() { | |
| 92 return (state_ == State::PLAYING); | |
| 93 } | |
| 94 | |
| 95 bool PipelineState::IsSuspended() { | |
| 96 return (state_ == State::SUSPENDED || state_ == State::RESUMING); | |
| 97 } | |
| 98 | |
| 99 void PipelineState::OnPipelineStatus(State state, | |
| 100 PipelineStatus pipeline_status) { | |
| 101 if (pipeline_status != PIPELINE_OK) { | |
| 102 error_cb_.Run(pipeline_status); | |
| 103 return; | |
| 104 } | |
| 105 | |
| 106 state_ = state; | |
| 107 | |
| 108 // Start(), Seek(), or Resume() completed; we can be sure that | |
| 109 // |chunk_demuxer_| got the seek it was waiting for. | |
| 110 if (state == State::PLAYING) | |
| 111 waiting_for_seek_ = false; | |
| 112 | |
| 113 // Sadly we need to signal this state change via a possibly reentrant | |
| 114 // callback. Keep in mind that the state may change inside the callback! | |
| 115 // (In particular, it must be safe to call Dispatch() twice in a row here.) | |
| 116 if (state == State::SUSPENDED) | |
| 117 suspended_cb_.Run(); | |
| 118 | |
| 119 Dispatch(); | |
| 120 } | |
| 121 | |
| 122 // Note: Dispatch() may be called twice in a row. (See OnPipelineStatus().) | |
|
wolenetz
2016/01/29 21:42:30
nit: Saying it must support re-entrancy by (at lea
sandersd (OOO until July 31)
2016/02/01 23:19:26
Done.
| |
| 123 void PipelineState::Dispatch() { | |
| 124 // Suspend/resume transitions take priority because seeks before a suspend | |
| 125 // are wasted, an seeks after can be merged into the resume operation. | |
| 126 if (pending_suspend_ && state_ == State::PLAYING) { | |
| 127 pending_suspend_ = false; | |
| 128 state_ = State::SUSPENDING; | |
| 129 pipeline_->Suspend(base::Bind(&PipelineState::OnPipelineStatus, | |
|
wolenetz
2016/01/29 21:42:30
In the case of an already-in-progress pipeline_->S
sandersd (OOO until July 31)
2016/02/01 23:19:26
While I tested this manually, and it seemed to wor
| |
| 130 base::Unretained(this), State::SUSPENDED)); | |
| 131 return; | |
| 132 } | |
| 133 | |
| 134 if (pending_resume_ && state_ == State::SUSPENDED) { | |
| 135 // If there is a pending seek, resume to that time instead. | |
| 136 if (pending_seek_) { | |
| 137 seek_time_ = pending_seek_time_; | |
| 138 pending_seek_ = false; | |
| 139 } else { | |
| 140 seek_time_ = pipeline_->GetMediaTime(); | |
| 141 } | |
| 142 | |
| 143 // Tell |chunk_demuxer_| to expect our resume. | |
| 144 if (chunk_demuxer_) { | |
| 145 DCHECK(!waiting_for_seek_); | |
| 146 chunk_demuxer_->StartWaitingForSeek(seek_time_); | |
| 147 waiting_for_seek_ = true; | |
| 148 } | |
| 149 | |
| 150 pending_resume_ = false; | |
| 151 state_ = State::RESUMING; | |
| 152 pipeline_->Resume(renderer_factory_cb_.Run(), seek_time_, | |
| 153 base::Bind(&PipelineState::OnPipelineStatus, | |
| 154 base::Unretained(this), State::PLAYING)); | |
| 155 return; | |
| 156 } | |
| 157 | |
| 158 // |chunk_demuxer_| supports aborting seeks. Make use of that when we have | |
| 159 // iother pending operations. | |
| 160 if ((pending_seek_ || pending_suspend_) && waiting_for_seek_) { | |
| 161 CHECK(chunk_demuxer_); | |
| 162 // CancelPendingSeek() may be reentrant, so update state first and return | |
| 163 // immediately. | |
| 164 waiting_for_seek_ = false; | |
| 165 chunk_demuxer_->CancelPendingSeek(pending_seek_time_); | |
| 166 return; | |
| 167 } | |
| 168 | |
| 169 if (pending_seek_ && state_ == State::PLAYING) { | |
| 170 seek_time_ = pending_seek_time_; | |
| 171 | |
| 172 // Tell |chunk_demuxer_| to expect our seek. | |
| 173 if (chunk_demuxer_) { | |
| 174 DCHECK(!waiting_for_seek_); | |
| 175 waiting_for_seek_ = true; | |
| 176 chunk_demuxer_->StartWaitingForSeek(seek_time_); | |
| 177 } | |
| 178 | |
| 179 pending_seek_ = false; | |
| 180 state_ = State::SEEKING; | |
| 181 pipeline_->Seek(seek_time_, | |
| 182 base::Bind(&PipelineState::OnPipelineStatus, | |
| 183 base::Unretained(this), State::PLAYING)); | |
| 184 return; | |
| 185 } | |
| 186 | |
| 187 // If |state_| is PLAYING and we didn't trigger an operation above then we | |
| 188 // are in a stable state. If there is a seeked callback pending, emit it. | |
| 189 if (state_ == State::PLAYING) { | |
| 190 if (pending_seeked_) { | |
| 191 // |seeked_cb_| may be reentrant, so update state first and return | |
| 192 // immediately. | |
| 193 pending_seeked_ = false; | |
| 194 bool was_pending_time_update = pending_time_update_; | |
| 195 pending_time_update_ = false; | |
| 196 seeked_cb_.Run(was_pending_time_update); | |
| 197 return; | |
| 198 } | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 } // namespace media | |
| OLD | NEW |