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