Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(130)

Side by Side Diff: media/filters/pipeline_controller.cc

Issue 1641423002: Re-land extract state management from WebMediaPlayerImpl. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add a message loop to the test. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "media/filters/pipeline_controller.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "build/build_config.h"
10 #include "media/base/bind_to_current_loop.h"
11 #include "media/filters/chunk_demuxer.h"
12
13 namespace media {
14
15 PipelineController::PipelineController(
16 Pipeline* pipeline,
17 const RendererFactoryCB& renderer_factory_cb,
18 const SeekedCB& seeked_cb,
19 const SuspendedCB& suspended_cb,
20 const PipelineStatusCB& error_cb)
21 : pipeline_(pipeline),
22 renderer_factory_cb_(renderer_factory_cb),
23 seeked_cb_(seeked_cb),
24 suspended_cb_(suspended_cb),
25 error_cb_(error_cb),
26 weak_factory_(this) {
27 DCHECK(pipeline_);
28 DCHECK(!renderer_factory_cb_.is_null());
29 DCHECK(!seeked_cb_.is_null());
30 DCHECK(!suspended_cb_.is_null());
31 DCHECK(!error_cb_.is_null());
32 }
33
34 PipelineController::~PipelineController() {}
35
36 // TODO(sandersd): Move ChunkDemuxer API to Demuxer so that Pipeline can
37 // implement all of this.
38 // TODO(sandersd): If there is a pending suspend, don't call pipeline_.Start()
39 // until Resume().
40 void PipelineController::Start(
41 ChunkDemuxer* chunk_demuxer,
42 Demuxer* demuxer,
43 bool is_streaming,
44 const base::Closure& ended_cb,
45 const PipelineMetadataCB& metadata_cb,
46 const BufferingStateCB& buffering_state_cb,
47 const base::Closure& duration_change_cb,
48 const AddTextTrackCB& add_text_track_cb,
49 const base::Closure& waiting_for_decryption_key_cb) {
50 DCHECK(state_ == State::CREATED);
51
wolenetz 2016/02/26 02:30:11 Let's further enforce the contract that, if chunk_
sandersd (OOO until July 31) 2016/02/26 22:10:48 Done.
52 // Once the pipeline is started, we want to call the seeked callback but
53 // without a time update.
54 pending_seeked_ = true;
55 state_ = State::STARTING;
56
57 chunk_demuxer_ = chunk_demuxer;
58 is_streaming_ = is_streaming;
59 pipeline_->Start(
60 demuxer, renderer_factory_cb_.Run(), ended_cb, error_cb_,
61 BindToCurrentLoop(base::Bind(&PipelineController::OnPipelineStatus,
62 weak_factory_.GetWeakPtr(), State::PLAYING)),
63 metadata_cb, buffering_state_cb, duration_change_cb, add_text_track_cb,
64 waiting_for_decryption_key_cb);
65 }
66
67 void PipelineController::Seek(base::TimeDelta time, bool time_updated) {
68 // It would be slightly more clear to set this in Dispatch(), but we want to
69 // be sure it gets updated even if the seek is elided.
70 if (time_updated)
71 pending_time_update_ = true;
72 pending_seeked_ = true;
73
74 // If we are already seeking to |time|, just clear any pending seek. This does
75 // not apply to MSE because the underlying buffer could have been changed
76 // between the seek calls.
77 // TODO(sandersd): The underlying buffer could also have changed for
78 // File objects, but WMPI is also broken in that case (because it caches).
79 if ((state_ == State::SEEKING || state_ == State::RESUMING) &&
80 seek_time_ == time && !chunk_demuxer_) {
81 pending_seek_ = false;
82 return;
83 }
84
85 pending_seek_time_ = time;
86 pending_seek_ = true;
87 Dispatch();
88 }
89
90 // TODO(sandersd): It may be easier to use this interface if |suspended_cb_| is
91 // executed when Suspend() is called while already suspended.
92 void PipelineController::Suspend() {
93 pending_resume_ = false;
94 if (state_ != State::SUSPENDING && state_ != State::SUSPENDED) {
95 pending_suspend_ = true;
96 Dispatch();
97 }
98 }
99
100 void PipelineController::Resume() {
101 pending_suspend_ = false;
102 if (state_ == State::SUSPENDING || state_ == State::SUSPENDED) {
103 pending_resume_ = true;
104 Dispatch();
105 }
106 }
107
108 bool PipelineController::IsStable() {
109 return (state_ == State::PLAYING);
110 }
111
112 bool PipelineController::IsSuspended() {
113 return (state_ == State::SUSPENDED);
114 }
115
116 void PipelineController::OnPipelineStatus(State state,
117 PipelineStatus pipeline_status) {
118 if (pipeline_status != PIPELINE_OK) {
119 error_cb_.Run(pipeline_status);
120 return;
121 }
122
123 state_ = state;
124
125 // Start(), Seek(), or Resume() completed; we can be sure that
126 // |chunk_demuxer_| got the seek it was waiting for.
127 if (state == State::PLAYING)
128 waiting_for_seek_ = false;
129
130 // Sadly we need to signal this state change via a possibly reentrant
131 // callback. Keep in mind that the state may change inside the callback!
132 // (In particular, it must be safe to call Dispatch() twice in a row here.)
133 if (state == State::SUSPENDED)
134 suspended_cb_.Run();
135
136 Dispatch();
137 }
138
139 // Note: Dispatch() may be called re-entrantly (by callbacks internally) or
140 // twice in a row (by OnPipelineStatus()).
141 void PipelineController::Dispatch() {
142 // Suspend/resume transitions take priority because seeks before a suspend
143 // are wasted, and seeks after can be merged into the resume operation.
144 if (pending_suspend_ && state_ == State::PLAYING) {
145 pending_suspend_ = false;
146 state_ = State::SUSPENDING;
147 pipeline_->Suspend(BindToCurrentLoop(
148 base::Bind(&PipelineController::OnPipelineStatus,
149 weak_factory_.GetWeakPtr(), State::SUSPENDED)));
150 return;
151 }
152
153 if (pending_resume_ && state_ == State::SUSPENDED) {
154 // If there is a pending seek, resume to that time instead.
155 if (pending_seek_) {
156 seek_time_ = pending_seek_time_;
157 pending_seek_ = false;
158 } else {
159 seek_time_ = pipeline_->GetMediaTime();
160 }
161
162 // Unless the media is streaming, in which case we resume at the start
163 // because seeking doesn't work well.
164 if (is_streaming_ && !seek_time_.is_zero()) {
165 seek_time_ = base::TimeDelta();
166
167 // In this case we want to make sure that the controls get updated
168 // immediately, so we don't try to hide the seek.
169 pending_time_update_ = true;
170 }
171
172 // Tell |chunk_demuxer_| to expect our resume.
173 if (chunk_demuxer_) {
174 DCHECK(!waiting_for_seek_);
175 chunk_demuxer_->StartWaitingForSeek(seek_time_);
176 waiting_for_seek_ = true;
177 }
178
179 pending_resume_ = false;
180 state_ = State::RESUMING;
181 pipeline_->Resume(renderer_factory_cb_.Run(), seek_time_,
182 BindToCurrentLoop(base::Bind(
183 &PipelineController::OnPipelineStatus,
184 weak_factory_.GetWeakPtr(), State::PLAYING)));
185 return;
186 }
187
188 // |chunk_demuxer_| supports aborting seeks. Make use of that when we have
189 // other pending operations.
190 if ((pending_seek_ || pending_suspend_) && waiting_for_seek_) {
191 CHECK(chunk_demuxer_);
192
193 // If there is no pending seek, return the current seek to pending status.
194 if (!pending_seek_) {
195 pending_seek_time_ = seek_time_;
196 pending_seek_ = true;
197 }
198
199 // CancelPendingSeek() may be reentrant, so update state first and return
200 // immediately.
201 waiting_for_seek_ = false;
202 chunk_demuxer_->CancelPendingSeek(pending_seek_time_);
203 return;
204 }
205
206 // Ordinary seeking.
207 if (pending_seek_ && state_ == State::PLAYING) {
208 seek_time_ = pending_seek_time_;
209
210 // Tell |chunk_demuxer_| to expect our seek.
211 if (chunk_demuxer_) {
212 DCHECK(!waiting_for_seek_);
213 waiting_for_seek_ = true;
214 chunk_demuxer_->StartWaitingForSeek(seek_time_);
215 }
216
217 pending_seek_ = false;
218 state_ = State::SEEKING;
219 pipeline_->Seek(seek_time_,
220 BindToCurrentLoop(base::Bind(
221 &PipelineController::OnPipelineStatus,
222 weak_factory_.GetWeakPtr(), State::PLAYING)));
223 return;
224 }
225
226 // If |state_| is PLAYING and we didn't trigger an operation above then we
227 // are in a stable state. If there is a seeked callback pending, emit it.
228 if (state_ == State::PLAYING) {
229 if (pending_seeked_) {
230 // |seeked_cb_| may be reentrant, so update state first and return
231 // immediately.
232 pending_seeked_ = false;
233 bool was_pending_time_update = pending_time_update_;
234 pending_time_update_ = false;
235 seeked_cb_.Run(was_pending_time_update);
236 return;
237 }
238 }
239 }
240
241 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698