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 |