OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "media/base/pipeline_impl.h" | 5 #include "media/base/pipeline_impl.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <utility> | |
9 | 8 |
10 #include "base/bind.h" | 9 #include "base/bind.h" |
11 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
12 #include "base/callback.h" | 11 #include "base/callback.h" |
13 #include "base/callback_helpers.h" | 12 #include "base/callback_helpers.h" |
14 #include "base/command_line.h" | 13 #include "base/command_line.h" |
15 #include "base/compiler_specific.h" | |
16 #include "base/location.h" | 14 #include "base/location.h" |
17 #include "base/memory/ptr_util.h" | |
18 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
19 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
20 #include "base/stl_util.h" | 17 #include "base/synchronization/lock.h" |
21 #include "base/strings/string_number_conversions.h" | |
22 #include "base/strings/string_util.h" | |
23 #include "base/synchronization/waitable_event.h" | 18 #include "base/synchronization/waitable_event.h" |
24 #include "base/threading/thread_task_runner_handle.h" | 19 #include "base/threading/thread_task_runner_handle.h" |
25 #include "media/base/bind_to_current_loop.h" | 20 #include "media/base/bind_to_current_loop.h" |
21 #include "media/base/demuxer.h" | |
26 #include "media/base/media_log.h" | 22 #include "media/base/media_log.h" |
27 #include "media/base/media_switches.h" | 23 #include "media/base/media_switches.h" |
28 #include "media/base/renderer.h" | 24 #include "media/base/renderer.h" |
25 #include "media/base/renderer_client.h" | |
26 #include "media/base/serial_runner.h" | |
29 #include "media/base/text_renderer.h" | 27 #include "media/base/text_renderer.h" |
30 #include "media/base/text_track_config.h" | 28 #include "media/base/text_track_config.h" |
31 #include "media/base/timestamp_constants.h" | 29 #include "media/base/timestamp_constants.h" |
32 #include "media/base/video_decoder_config.h" | 30 #include "media/base/video_decoder_config.h" |
33 | 31 |
34 using base::TimeDelta; | 32 namespace { |
33 | |
34 const double kDefaultPlaybackRate = 0.0; | |
35 const float kDefaultVolume = 1.0f; | |
36 | |
37 bool TextTracksEnabled() { | |
38 static bool enabled = base::CommandLine::ForCurrentProcess()->HasSwitch( | |
alokp
2016/06/08 19:59:48
cached here instead of querying the command line f
| |
39 switches::kEnableInbandTextTracks); | |
40 return enabled; | |
41 } | |
42 | |
43 } // namespace | |
35 | 44 |
36 namespace media { | 45 namespace media { |
37 | 46 |
47 class PipelineImpl::RendererWrapper : public DemuxerHost, | |
48 public RendererClient { | |
49 public: | |
50 RendererWrapper(base::WeakPtr<PipelineImpl> weak_pipeline, | |
51 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner, | |
52 scoped_refptr<MediaLog> media_log); | |
53 ~RendererWrapper() final; | |
54 | |
55 void Start(Demuxer* demuxer, | |
56 std::unique_ptr<Renderer> renderer, | |
57 std::unique_ptr<TextRenderer> text_renderer); | |
58 void Stop(const base::Closure& stop_cb); | |
59 void Seek(base::TimeDelta time); | |
60 void Suspend(); | |
61 void Resume(base::TimeDelta time, std::unique_ptr<Renderer> renderer); | |
62 void SetPlaybackRate(double playback_rate); | |
63 void SetVolume(float volume); | |
64 base::TimeDelta GetMediaTime(); | |
65 void SetCdm(CdmContext* cdm_context, const CdmAttachedCB& cdm_attached_cb); | |
66 | |
67 private: | |
68 // DemuxerHost implementaion. | |
69 void OnBufferedTimeRangesChanged(const Ranges<base::TimeDelta>& ranges) final; | |
70 void SetDuration(base::TimeDelta duration) final; | |
71 void OnDemuxerError(PipelineStatus error) final; | |
72 void AddTextStream(DemuxerStream* text_stream, | |
73 const TextTrackConfig& config) final; | |
74 void RemoveTextStream(DemuxerStream* text_stream) final; | |
75 | |
76 // RendererClient implementation. | |
77 void OnError(PipelineStatus error) final; | |
78 void OnEnded() final; | |
79 void OnStatisticsUpdate(const PipelineStatistics& stats) final; | |
80 void OnBufferingStateChange(BufferingState state) final; | |
81 void OnWaitingForDecryptionKey() final; | |
82 void OnVideoNaturalSizeChange(const gfx::Size& size) final; | |
83 void OnVideoOpacityChange(bool opaque) final; | |
84 | |
85 void OnPipelineError(PipelineStatus error); | |
86 void OnTextRendererEnded(); | |
87 void RunEndedCallbackIfNeeded(); | |
88 void SetState(State next_state); | |
89 void OnSeekDone(base::TimeDelta start_time, PipelineStatus status); | |
90 void OnSuspendDone(base::TimeDelta suspend_time, PipelineStatus status); | |
91 | |
92 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, | |
93 CdmContext* cdm_context, | |
94 bool success); | |
95 void AddTextStreamTask(DemuxerStream* text_stream, | |
96 const TextTrackConfig& config); | |
97 void RemoveTextStreamTask(DemuxerStream* text_stream); | |
98 void InitializeDemuxer(const PipelineStatusCB& done_cb); | |
99 void InitializeRenderer(const PipelineStatusCB& done_cb); | |
100 void ReportMetadata(); | |
101 | |
102 base::WeakPtr<PipelineImpl> weak_pipeline_; | |
103 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; | |
104 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; | |
105 const scoped_refptr<MediaLog> media_log_; | |
106 | |
107 Demuxer* demuxer_; | |
108 std::unique_ptr<Renderer> renderer_; | |
109 std::unique_ptr<TextRenderer> text_renderer_; | |
110 double playback_rate_; | |
111 float volume_; | |
112 CdmContext* cdm_context_; | |
113 | |
114 // Lock used to serialize access for |renderer_|. | |
115 mutable base::Lock renderer_lock_; | |
116 | |
117 // Current state of the pipeline. | |
118 State state_; | |
119 | |
120 // Last media time reported to the pipeline. | |
121 base::TimeDelta last_media_time_; | |
122 | |
123 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that | |
124 // the pipeline is operating correctly. Any other value indicates that the | |
125 // pipeline is stopped or is stopping. Clients can call the Stop() method to | |
126 // reset the pipeline state, and restore this to PIPELINE_OK. | |
127 PipelineStatus status_; | |
128 | |
129 // Whether we've received the audio/video/text ended events. | |
130 bool renderer_ended_; | |
131 bool text_renderer_ended_; | |
132 | |
133 // Series of tasks to Start(), Seek(), and Resume(). | |
134 std::unique_ptr<SerialRunner> pending_callbacks_; | |
135 | |
136 base::WeakPtrFactory<RendererWrapper> weak_factory_; | |
137 DISALLOW_COPY_AND_ASSIGN(RendererWrapper); | |
138 }; | |
139 | |
38 PipelineImpl::PipelineImpl( | 140 PipelineImpl::PipelineImpl( |
39 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, | 141 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
40 MediaLog* media_log) | 142 MediaLog* media_log) |
41 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()), | 143 : media_task_runner_(media_task_runner), |
42 media_task_runner_(media_task_runner), | |
43 media_log_(media_log), | 144 media_log_(media_log), |
44 running_(false), | 145 client_(nullptr), |
alokp
2016/06/08 19:59:48
no need for running_. We now use client_ != null a
| |
146 playback_rate_(kDefaultPlaybackRate), | |
147 volume_(kDefaultVolume), | |
148 suspend_time_(kNoTimestamp()), | |
45 did_loading_progress_(false), | 149 did_loading_progress_(false), |
46 volume_(1.0f), | |
47 playback_rate_(0.0), | |
48 status_(PIPELINE_OK), | |
alokp
2016/06/08 19:59:47
state machine has been moved to RendererWrapper.
| |
49 state_(kCreated), | |
50 suspend_timestamp_(kNoTimestamp()), | |
51 renderer_ended_(false), | |
52 text_renderer_ended_(false), | |
53 demuxer_(NULL), | |
54 cdm_context_(nullptr), | |
55 weak_factory_(this) { | 150 weak_factory_(this) { |
56 weak_this_ = weak_factory_.GetWeakPtr(); | 151 DVLOG(2) << __FUNCTION__; |
57 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); | 152 renderer_wrapper_.reset(new RendererWrapper(weak_factory_.GetWeakPtr(), |
153 media_task_runner_, media_log_)); | |
58 } | 154 } |
59 | 155 |
60 PipelineImpl::~PipelineImpl() { | 156 PipelineImpl::~PipelineImpl() { |
61 DCHECK(main_task_runner_->BelongsToCurrentThread()) | 157 DVLOG(2) << __FUNCTION__; |
62 << "Pipeline must be destroyed on same thread that created it"; | 158 DCHECK(thread_checker_.CalledOnValidThread()); |
63 DCHECK(!running_) << "Stop() must complete before destroying object"; | 159 DCHECK(!client_) << "Stop() must complete before destroying object"; |
64 DCHECK(seek_cb_.is_null()); | 160 DCHECK(seek_cb_.is_null()); |
161 DCHECK(suspend_cb_.is_null()); | |
162 | |
163 // Invalidate self weak pointers effectively canceling all pending | |
164 // notifications in the message queue. | |
165 weak_factory_.InvalidateWeakPtrs(); | |
166 | |
167 // RendererWrapper is deleted on the media thread. | |
168 media_task_runner_->DeleteSoon(FROM_HERE, renderer_wrapper_.release()); | |
65 } | 169 } |
66 | 170 |
67 void PipelineImpl::Start(Demuxer* demuxer, | 171 void PipelineImpl::Start(Demuxer* demuxer, |
68 std::unique_ptr<Renderer> renderer, | 172 std::unique_ptr<Renderer> renderer, |
69 Client* client, | 173 Client* client, |
70 const PipelineStatusCB& seek_cb) { | 174 const PipelineStatusCB& seek_cb) { |
71 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 175 DVLOG(2) << __FUNCTION__; |
176 DCHECK(thread_checker_.CalledOnValidThread()); | |
177 DCHECK(demuxer); | |
178 DCHECK(renderer); | |
72 DCHECK(client); | 179 DCHECK(client); |
73 DCHECK(!seek_cb.is_null()); | 180 DCHECK(!seek_cb.is_null()); |
74 | 181 |
75 base::AutoLock auto_lock(lock_); | 182 DCHECK(!client_); |
76 CHECK(!running_) << "Media pipeline is already running"; | 183 DCHECK(seek_cb_.is_null()); |
77 running_ = true; | 184 client_ = client; |
78 | 185 seek_cb_ = seek_cb; |
79 demuxer_ = demuxer; | 186 |
80 renderer_ = std::move(renderer); | 187 std::unique_ptr<TextRenderer> text_renderer; |
alokp
2016/06/08 19:59:46
We create TextRenderer on the main thread because:
| |
81 client_weak_factory_.reset(new base::WeakPtrFactory<Client>(client)); | 188 if (TextTracksEnabled()) { |
82 weak_client_ = client_weak_factory_->GetWeakPtr(); | 189 text_renderer.reset(new TextRenderer( |
83 seek_cb_ = media::BindToCurrentLoop(seek_cb); | 190 media_task_runner_, |
84 media_task_runner_->PostTask( | 191 BindToCurrentLoop(base::Bind(&PipelineImpl::OnAddTextTrack, |
85 FROM_HERE, base::Bind(&PipelineImpl::StartTask, weak_this_)); | 192 weak_factory_.GetWeakPtr())))); |
193 } | |
194 | |
195 media_task_runner_->PostTask( | |
196 FROM_HERE, | |
197 base::Bind(&RendererWrapper::Start, | |
198 base::Unretained(renderer_wrapper_.get()), demuxer, | |
199 base::Passed(&renderer), base::Passed(&text_renderer))); | |
86 } | 200 } |
87 | 201 |
88 void PipelineImpl::Stop() { | 202 void PipelineImpl::Stop() { |
89 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 203 DVLOG(2) << __FUNCTION__; |
90 DVLOG(2) << __FUNCTION__; | 204 DCHECK(thread_checker_.CalledOnValidThread()); |
91 | 205 |
92 if (media_task_runner_ != main_task_runner_) { | 206 if (!IsRunning()) { |
207 DVLOG(2) << "Media pipeline isn't running. Ignoring Stop()"; | |
208 return; | |
209 } | |
210 | |
211 if (media_task_runner_->BelongsToCurrentThread()) { | |
alokp
2016/06/08 19:59:48
no major change here - just swapped the condition
| |
212 // This path is executed by unittests that share media and main threads. | |
213 base::Closure stop_cb = base::Bind(&base::DoNothing); | |
214 media_task_runner_->PostTask( | |
215 FROM_HERE, | |
216 base::Bind(&RendererWrapper::Stop, | |
217 base::Unretained(renderer_wrapper_.get()), stop_cb)); | |
218 } else { | |
93 // This path is executed by production code where the two task runners - | 219 // This path is executed by production code where the two task runners - |
94 // main and media - live on different threads. | 220 // main and media - live on different threads. |
95 // TODO(alokp): It may be possible to not have to wait for StopTask by | 221 // |
96 // moving the members accessed on media thread into a class/struct and | 222 // TODO(alokp): We should not have to wait for the RendererWrapper::Stop. |
97 // DeleteSoon the instance on the media thread. | 223 // RendererWrapper holds a raw reference to Demuxer, which in turn holds a |
224 // raw reference to DataSource. Both Demuxer and DataSource need to live | |
225 // until RendererWrapper is stopped. If RendererWrapper owned Demuxer and | |
226 // Demuxer owned DataSource, we could simply let RendererWrapper get lazily | |
227 // destroyed on the media thread. | |
98 base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::AUTOMATIC, | 228 base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
99 base::WaitableEvent::InitialState::NOT_SIGNALED); | 229 base::WaitableEvent::InitialState::NOT_SIGNALED); |
100 base::Closure stop_cb = | 230 base::Closure stop_cb = |
101 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)); | 231 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)); |
102 // If posting the task fails or the posted task fails to run, | 232 media_task_runner_->PostTask( |
103 // we will wait here forever. So add a CHECK to make sure we do not run | 233 FROM_HERE, |
104 // into those situations. | 234 base::Bind(&RendererWrapper::Stop, |
105 CHECK(weak_factory_.HasWeakPtrs()); | 235 base::Unretained(renderer_wrapper_.get()), stop_cb)); |
106 CHECK(media_task_runner_->PostTask( | |
107 FROM_HERE, base::Bind(&PipelineImpl::StopTask, weak_this_, stop_cb))); | |
108 waiter.Wait(); | 236 waiter.Wait(); |
109 } else { | |
110 // This path is executed by unittests that share media and main threads. | |
111 StopTask(base::Bind(&base::DoNothing)); | |
112 } | 237 } |
113 // Invalidate client weak pointer effectively canceling all pending client | 238 |
114 // notifications in the message queue. | 239 // Once the pipeline is stopped, nothing is reported back to the client. |
115 client_weak_factory_.reset(); | 240 // Reset all callbacks and client handle. |
116 } | 241 seek_cb_.Reset(); |
117 | 242 suspend_cb_.Reset(); |
118 void PipelineImpl::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) { | 243 client_ = nullptr; |
119 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 244 } |
245 | |
246 void PipelineImpl::Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) { | |
247 DVLOG(2) << __FUNCTION__; | |
alokp
2016/06/08 19:59:47
These functions just post tasks to the media threa
| |
248 DCHECK(thread_checker_.CalledOnValidThread()); | |
249 DCHECK(!seek_cb.is_null()); | |
120 | 250 |
121 if (!IsRunning()) { | 251 if (!IsRunning()) { |
122 DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek()."; | 252 DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek()."; |
123 return; | 253 return; |
124 } | 254 } |
125 | 255 |
126 media_task_runner_->PostTask( | 256 DCHECK(seek_cb_.is_null()); |
127 FROM_HERE, base::Bind(&PipelineImpl::SeekTask, weak_this_, time, | 257 seek_cb_ = seek_cb; |
128 media::BindToCurrentLoop(seek_cb))); | 258 media_task_runner_->PostTask( |
259 FROM_HERE, base::Bind(&RendererWrapper::Seek, | |
260 base::Unretained(renderer_wrapper_.get()), time)); | |
261 } | |
262 | |
263 void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) { | |
264 DVLOG(2) << __FUNCTION__; | |
265 DCHECK(!suspend_cb.is_null()); | |
266 | |
267 DCHECK(IsRunning()); | |
268 DCHECK(suspend_cb_.is_null()); | |
269 suspend_cb_ = suspend_cb; | |
270 | |
271 media_task_runner_->PostTask( | |
272 FROM_HERE, base::Bind(&RendererWrapper::Suspend, | |
273 base::Unretained(renderer_wrapper_.get()))); | |
274 } | |
275 | |
276 void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer, | |
277 base::TimeDelta time, | |
278 const PipelineStatusCB& seek_cb) { | |
279 DVLOG(2) << __FUNCTION__; | |
280 DCHECK(thread_checker_.CalledOnValidThread()); | |
281 DCHECK(renderer); | |
282 DCHECK(!seek_cb.is_null()); | |
283 | |
284 DCHECK(IsRunning()); | |
285 DCHECK(seek_cb_.is_null()); | |
286 seek_cb_ = seek_cb; | |
287 | |
288 media_task_runner_->PostTask( | |
289 FROM_HERE, base::Bind(&RendererWrapper::Resume, | |
290 base::Unretained(renderer_wrapper_.get()), time, | |
291 base::Passed(&renderer))); | |
129 } | 292 } |
130 | 293 |
131 bool PipelineImpl::IsRunning() const { | 294 bool PipelineImpl::IsRunning() const { |
132 // TODO(alokp): Add thread DCHECK after removing the internal usage on | 295 DCHECK(thread_checker_.CalledOnValidThread()); |
alokp
2016/06/08 19:59:48
client_ is cleared as soon the pipeline stops. So
| |
133 // media thread. | 296 return !!client_; |
134 base::AutoLock auto_lock(lock_); | |
135 return running_; | |
136 } | 297 } |
137 | 298 |
138 double PipelineImpl::GetPlaybackRate() const { | 299 double PipelineImpl::GetPlaybackRate() const { |
139 // TODO(alokp): Add thread DCHECK after removing the internal usage on | 300 DCHECK(thread_checker_.CalledOnValidThread()); |
140 // media thread. | |
141 base::AutoLock auto_lock(lock_); | |
142 return playback_rate_; | 301 return playback_rate_; |
143 } | 302 } |
144 | 303 |
145 void PipelineImpl::SetPlaybackRate(double playback_rate) { | 304 void PipelineImpl::SetPlaybackRate(double playback_rate) { |
146 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 305 DVLOG(2) << __FUNCTION__ << "(" << playback_rate << ")"; |
306 DCHECK(thread_checker_.CalledOnValidThread()); | |
147 | 307 |
148 if (playback_rate < 0.0) | 308 if (playback_rate < 0.0) |
149 return; | 309 return; |
150 | 310 |
151 base::AutoLock auto_lock(lock_); | |
152 playback_rate_ = playback_rate; | 311 playback_rate_ = playback_rate; |
153 if (running_) { | |
154 media_task_runner_->PostTask( | |
155 FROM_HERE, base::Bind(&PipelineImpl::PlaybackRateChangedTask, | |
156 weak_this_, playback_rate)); | |
157 } | |
158 } | |
159 | |
160 void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) { | |
161 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
162 | |
163 media_task_runner_->PostTask( | |
164 FROM_HERE, base::Bind(&PipelineImpl::SuspendTask, weak_this_, | |
165 media::BindToCurrentLoop(suspend_cb))); | |
166 } | |
167 | |
168 void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer, | |
169 base::TimeDelta timestamp, | |
170 const PipelineStatusCB& seek_cb) { | |
171 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
172 | |
173 media_task_runner_->PostTask( | 312 media_task_runner_->PostTask( |
174 FROM_HERE, | 313 FROM_HERE, |
175 base::Bind(&PipelineImpl::ResumeTask, weak_this_, base::Passed(&renderer), | 314 base::Bind(&RendererWrapper::SetPlaybackRate, |
176 timestamp, media::BindToCurrentLoop(seek_cb))); | 315 base::Unretained(renderer_wrapper_.get()), playback_rate_)); |
177 } | 316 } |
178 | 317 |
179 float PipelineImpl::GetVolume() const { | 318 float PipelineImpl::GetVolume() const { |
180 // TODO(alokp): Add thread DCHECK after removing the internal usage on | 319 DCHECK(thread_checker_.CalledOnValidThread()); |
181 // media thread. | |
182 base::AutoLock auto_lock(lock_); | |
183 return volume_; | 320 return volume_; |
184 } | 321 } |
185 | 322 |
186 void PipelineImpl::SetVolume(float volume) { | 323 void PipelineImpl::SetVolume(float volume) { |
187 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 324 DVLOG(2) << __FUNCTION__ << "(" << volume << ")"; |
325 DCHECK(thread_checker_.CalledOnValidThread()); | |
188 | 326 |
189 if (volume < 0.0f || volume > 1.0f) | 327 if (volume < 0.0f || volume > 1.0f) |
190 return; | 328 return; |
191 | 329 |
192 base::AutoLock auto_lock(lock_); | |
193 volume_ = volume; | 330 volume_ = volume; |
194 if (running_) { | 331 media_task_runner_->PostTask( |
195 media_task_runner_->PostTask( | 332 FROM_HERE, base::Bind(&RendererWrapper::SetVolume, |
196 FROM_HERE, | 333 base::Unretained(renderer_wrapper_.get()), volume)); |
197 base::Bind(&PipelineImpl::VolumeChangedTask, weak_this_, volume)); | 334 } |
198 } | 335 |
199 } | 336 base::TimeDelta PipelineImpl::GetMediaTime() const { |
200 | 337 DCHECK(thread_checker_.CalledOnValidThread()); |
201 TimeDelta PipelineImpl::GetMediaTime() const { | 338 |
202 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 339 return suspend_time_ != kNoTimestamp() ? suspend_time_ |
203 | 340 : renderer_wrapper_->GetMediaTime(); |
204 base::AutoLock auto_lock(lock_); | 341 } |
205 if (suspend_timestamp_ != kNoTimestamp()) | 342 |
206 return suspend_timestamp_; | 343 Ranges<base::TimeDelta> PipelineImpl::GetBufferedTimeRanges() const { |
207 return renderer_ ? std::min(renderer_->GetMediaTime(), duration_) | 344 DCHECK(thread_checker_.CalledOnValidThread()); |
208 : TimeDelta(); | |
209 } | |
210 | |
211 Ranges<TimeDelta> PipelineImpl::GetBufferedTimeRanges() const { | |
212 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
213 | |
214 base::AutoLock auto_lock(lock_); | |
215 return buffered_time_ranges_; | 345 return buffered_time_ranges_; |
216 } | 346 } |
217 | 347 |
218 TimeDelta PipelineImpl::GetMediaDuration() const { | 348 base::TimeDelta PipelineImpl::GetMediaDuration() const { |
219 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 349 DCHECK(thread_checker_.CalledOnValidThread()); |
220 | |
221 base::AutoLock auto_lock(lock_); | |
222 return duration_; | 350 return duration_; |
223 } | 351 } |
224 | 352 |
225 bool PipelineImpl::DidLoadingProgress() { | 353 bool PipelineImpl::DidLoadingProgress() { |
226 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 354 DCHECK(thread_checker_.CalledOnValidThread()); |
227 | |
228 base::AutoLock auto_lock(lock_); | |
229 bool ret = did_loading_progress_; | 355 bool ret = did_loading_progress_; |
230 did_loading_progress_ = false; | 356 did_loading_progress_ = false; |
231 return ret; | 357 return ret; |
232 } | 358 } |
233 | 359 |
234 PipelineStatistics PipelineImpl::GetStatistics() const { | 360 PipelineStatistics PipelineImpl::GetStatistics() const { |
235 // TODO(alokp): Add thread DCHECK after removing the internal usage on | 361 DCHECK(thread_checker_.CalledOnValidThread()); |
236 // media thread. | |
237 base::AutoLock auto_lock(lock_); | |
238 return statistics_; | 362 return statistics_; |
239 } | 363 } |
240 | 364 |
241 void PipelineImpl::SetCdm(CdmContext* cdm_context, | 365 void PipelineImpl::SetCdm(CdmContext* cdm_context, |
242 const CdmAttachedCB& cdm_attached_cb) { | 366 const CdmAttachedCB& cdm_attached_cb) { |
243 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 367 DVLOG(2) << __FUNCTION__; |
244 | 368 DCHECK(thread_checker_.CalledOnValidThread()); |
245 media_task_runner_->PostTask( | 369 DCHECK(cdm_context); |
246 FROM_HERE, base::Bind(&PipelineImpl::SetCdmTask, weak_this_, cdm_context, | 370 DCHECK(!cdm_attached_cb.is_null()); |
247 cdm_attached_cb)); | 371 |
248 } | 372 media_task_runner_->PostTask( |
249 | 373 FROM_HERE, |
250 void PipelineImpl::SetErrorForTesting(PipelineStatus status) { | 374 base::Bind(&RendererWrapper::SetCdm, |
251 OnError(status); | 375 base::Unretained(renderer_wrapper_.get()), cdm_context, |
252 } | 376 media::BindToCurrentLoop(cdm_attached_cb))); |
253 | |
254 bool PipelineImpl::HasWeakPtrsForTesting() const { | |
255 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
256 return weak_factory_.HasWeakPtrs(); | |
257 } | |
258 | |
259 void PipelineImpl::SetState(State next_state) { | |
260 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
261 DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state); | |
262 | |
263 state_ = next_state; | |
264 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); | |
265 } | 377 } |
266 | 378 |
267 #define RETURN_STRING(state) \ | 379 #define RETURN_STRING(state) \ |
268 case state: \ | 380 case state: \ |
269 return #state; | 381 return #state; |
270 | 382 |
383 // static | |
271 const char* PipelineImpl::GetStateString(State state) { | 384 const char* PipelineImpl::GetStateString(State state) { |
272 switch (state) { | 385 switch (state) { |
273 RETURN_STRING(kCreated); | 386 RETURN_STRING(kCreated); |
274 RETURN_STRING(kInitDemuxer); | 387 RETURN_STRING(kStarting); |
275 RETURN_STRING(kInitRenderer); | |
276 RETURN_STRING(kSeeking); | 388 RETURN_STRING(kSeeking); |
277 RETURN_STRING(kPlaying); | 389 RETURN_STRING(kPlaying); |
278 RETURN_STRING(kStopping); | 390 RETURN_STRING(kStopping); |
279 RETURN_STRING(kStopped); | 391 RETURN_STRING(kStopped); |
280 RETURN_STRING(kSuspending); | 392 RETURN_STRING(kSuspending); |
281 RETURN_STRING(kSuspended); | 393 RETURN_STRING(kSuspended); |
282 RETURN_STRING(kResuming); | 394 RETURN_STRING(kResuming); |
283 } | 395 } |
284 NOTREACHED(); | 396 NOTREACHED(); |
285 return "INVALID"; | 397 return "INVALID"; |
286 } | 398 } |
287 | 399 |
288 #undef RETURN_STRING | 400 #undef RETURN_STRING |
289 | 401 |
290 PipelineImpl::State PipelineImpl::GetNextState() const { | 402 void PipelineImpl::OnError(PipelineStatus error) { |
291 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 403 DVLOG(2) << __FUNCTION__; |
292 DCHECK(stop_cb_.is_null()) << "State transitions don't happen when stopping"; | 404 DCHECK(thread_checker_.CalledOnValidThread()); |
293 DCHECK_EQ(status_, PIPELINE_OK) | 405 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; |
294 << "State transitions don't happen when there's an error: " << status_; | 406 |
295 | 407 if (!IsRunning()) |
296 switch (state_) { | 408 return; |
297 case kCreated: | 409 |
298 return kInitDemuxer; | 410 // If the error happens during starting/seeking/suspending/resuming, |
299 | 411 // report the error via the completion callback for those tasks. |
300 case kInitDemuxer: | 412 // Else report error via the client interface. |
301 return kInitRenderer; | 413 if (!seek_cb_.is_null()) { |
302 | 414 base::ResetAndReturn(&seek_cb_).Run(error); |
303 case kInitRenderer: | 415 } else if (!suspend_cb_.is_null()) { |
304 case kSeeking: | 416 base::ResetAndReturn(&suspend_cb_).Run(error); |
305 return kPlaying; | 417 } else { |
306 | 418 DCHECK(client_); |
307 case kSuspending: | 419 client_->OnError(error); |
308 return kSuspended; | 420 } |
309 | 421 |
310 case kSuspended: | 422 // Any kind of error stops the pipeline. |
311 return kResuming; | 423 Stop(); |
312 | 424 } |
313 case kResuming: | 425 |
314 return kPlaying; | 426 void PipelineImpl::OnEnded() { |
315 | 427 DVLOG(2) << __FUNCTION__; |
316 case kPlaying: | 428 DCHECK(thread_checker_.CalledOnValidThread()); |
317 case kStopping: | 429 |
318 case kStopped: | 430 if (IsRunning()) { |
319 break; | 431 DCHECK(client_); |
320 } | 432 client_->OnEnded(); |
321 NOTREACHED() << "State has no transition: " << state_; | 433 } |
322 return state_; | 434 } |
323 } | 435 |
324 | 436 void PipelineImpl::OnMetadata(PipelineMetadata metadata) { |
325 void PipelineImpl::OnDemuxerError(PipelineStatus error) { | 437 DVLOG(2) << __FUNCTION__; |
438 DCHECK(thread_checker_.CalledOnValidThread()); | |
439 | |
440 if (IsRunning()) { | |
441 DCHECK(client_); | |
442 client_->OnMetadata(metadata); | |
443 } | |
444 } | |
445 | |
446 void PipelineImpl::OnBufferingStateChange(BufferingState state) { | |
447 DVLOG(2) << __FUNCTION__ << "(" << state << ")"; | |
448 DCHECK(thread_checker_.CalledOnValidThread()); | |
449 | |
450 if (IsRunning()) { | |
451 DCHECK(client_); | |
452 client_->OnBufferingStateChange(state); | |
453 } | |
454 } | |
455 | |
456 void PipelineImpl::OnDurationChange(base::TimeDelta duration) { | |
457 DVLOG(2) << __FUNCTION__; | |
458 DCHECK(thread_checker_.CalledOnValidThread()); | |
459 | |
460 duration_ = duration; | |
461 | |
462 if (IsRunning()) { | |
463 DCHECK(client_); | |
464 client_->OnDurationChange(); | |
465 } | |
466 } | |
467 | |
468 void PipelineImpl::OnAddTextTrack(const TextTrackConfig& config, | |
469 const AddTextTrackDoneCB& done_cb) { | |
470 DVLOG(2) << __FUNCTION__; | |
471 DCHECK(thread_checker_.CalledOnValidThread()); | |
472 | |
473 if (IsRunning()) { | |
474 DCHECK(client_); | |
475 client_->OnAddTextTrack(config, done_cb); | |
476 } | |
477 } | |
478 | |
479 void PipelineImpl::OnWaitingForDecryptionKey() { | |
480 DVLOG(2) << __FUNCTION__; | |
481 DCHECK(thread_checker_.CalledOnValidThread()); | |
482 | |
483 if (IsRunning()) { | |
484 DCHECK(client_); | |
485 client_->OnWaitingForDecryptionKey(); | |
486 } | |
487 } | |
488 | |
489 void PipelineImpl::OnVideoNaturalSizeChange(const gfx::Size& size) { | |
490 DVLOG(2) << __FUNCTION__; | |
491 DCHECK(thread_checker_.CalledOnValidThread()); | |
492 | |
493 if (IsRunning()) { | |
494 DCHECK(client_); | |
495 client_->OnVideoNaturalSizeChange(size); | |
496 } | |
497 } | |
498 | |
499 void PipelineImpl::OnVideoOpacityChange(bool opaque) { | |
500 DVLOG(2) << __FUNCTION__; | |
501 DCHECK(thread_checker_.CalledOnValidThread()); | |
502 | |
503 if (IsRunning()) { | |
504 DCHECK(client_); | |
505 client_->OnVideoOpacityChange(opaque); | |
506 } | |
507 } | |
508 | |
509 void PipelineImpl::OnBufferedTimeRangesChange( | |
510 const Ranges<base::TimeDelta>& ranges) { | |
511 DVLOG(3) << __FUNCTION__; | |
512 DCHECK(thread_checker_.CalledOnValidThread()); | |
513 | |
514 buffered_time_ranges_ = ranges; | |
515 did_loading_progress_ = true; | |
516 } | |
517 | |
518 void PipelineImpl::OnStatisticsUpdate(const PipelineStatistics& stats) { | |
519 DVLOG(3) << __FUNCTION__; | |
520 DCHECK(thread_checker_.CalledOnValidThread()); | |
521 | |
522 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; | |
523 statistics_.video_bytes_decoded += stats.video_bytes_decoded; | |
524 statistics_.video_frames_decoded += stats.video_frames_decoded; | |
525 statistics_.video_frames_dropped += stats.video_frames_dropped; | |
526 statistics_.audio_memory_usage += stats.audio_memory_usage; | |
527 statistics_.video_memory_usage += stats.video_memory_usage; | |
528 } | |
529 | |
530 void PipelineImpl::OnSeekDone(base::TimeDelta start_time) { | |
531 DVLOG(3) << __FUNCTION__ << "(" << start_time.InMicroseconds() << ")"; | |
532 DCHECK(thread_checker_.CalledOnValidThread()); | |
533 | |
534 // Reset the suspend_time now that the pipeline is playing. | |
535 // Media time will now be reported by renderer. | |
536 suspend_time_ = kNoTimestamp(); | |
537 | |
538 if (IsRunning()) { | |
539 DCHECK(!seek_cb_.is_null()); | |
540 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); | |
541 } | |
542 } | |
543 | |
544 void PipelineImpl::OnSuspendDone(base::TimeDelta suspend_time) { | |
545 DVLOG(3) << __FUNCTION__ << "(" << suspend_time.InMicroseconds() << ")"; | |
546 DCHECK(thread_checker_.CalledOnValidThread()); | |
547 | |
548 // Cache the time at which pipeline was suspended. | |
549 // It will be used to report media time while the pipeline is suspended. | |
550 suspend_time_ = suspend_time; | |
551 | |
552 // Reset audio-video memory usage since renderer has been destroyed. | |
553 statistics_.audio_memory_usage = 0; | |
554 statistics_.video_memory_usage = 0; | |
555 | |
556 if (IsRunning()) { | |
557 DCHECK(!suspend_cb_.is_null()); | |
558 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); | |
559 } | |
560 } | |
561 | |
562 PipelineImpl::RendererWrapper::RendererWrapper( | |
563 base::WeakPtr<PipelineImpl> weak_pipeline, | |
564 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner, | |
565 scoped_refptr<MediaLog> media_log) | |
566 : weak_pipeline_(weak_pipeline), | |
567 media_task_runner_(std::move(media_task_runner)), | |
568 main_task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
569 media_log_(std::move(media_log)), | |
570 demuxer_(nullptr), | |
571 playback_rate_(kDefaultPlaybackRate), | |
572 volume_(kDefaultVolume), | |
573 cdm_context_(nullptr), | |
574 state_(kCreated), | |
575 status_(PIPELINE_OK), | |
576 renderer_ended_(false), | |
577 text_renderer_ended_(false), | |
578 weak_factory_(this) { | |
579 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); | |
580 } | |
581 | |
582 PipelineImpl::RendererWrapper::~RendererWrapper() { | |
583 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
584 DCHECK(state_ == kCreated || state_ == kStopped); | |
585 } | |
586 | |
587 // Note that the usage of base::Unretained() with the renderers and demuxer | |
588 // is safe as they are owned by |pending_callbacks_| and share the same | |
589 // lifetime. That said, deleting the renderers while keeping | |
590 // |pending_callbacks_| running on the media thread would result in crashes. | |
591 | |
592 base::TimeDelta PipelineImpl::RendererWrapper::GetMediaTime() { | |
593 // This is the only member function that gets called on the main thread. | |
594 // TODO(alokp): Enforce that Renderer is only called on a single thread, | |
595 // even for accessing media time http://crbug.com/370634. | |
596 DCHECK(main_task_runner_->BelongsToCurrentThread()); | |
597 | |
598 base::AutoLock auto_lock(renderer_lock_); | |
599 return renderer_ ? renderer_->GetMediaTime() : base::TimeDelta(); | |
600 } | |
601 | |
602 void PipelineImpl::RendererWrapper::SetState(State next_state) { | |
603 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
604 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " | |
605 << PipelineImpl::GetStateString(next_state); | |
606 | |
607 state_ = next_state; | |
608 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); | |
609 } | |
610 | |
611 void PipelineImpl::RendererWrapper::OnDemuxerError(PipelineStatus error) { | |
612 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer | |
613 // implementations call DemuxerHost on the media thread. | |
614 media_task_runner_->PostTask(FROM_HERE, | |
615 base::Bind(&RendererWrapper::OnPipelineError, | |
616 weak_factory_.GetWeakPtr(), error)); | |
617 } | |
618 | |
619 void PipelineImpl::RendererWrapper::AddTextStream( | |
620 DemuxerStream* text_stream, | |
621 const TextTrackConfig& config) { | |
326 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer | 622 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
327 // implementations call DemuxerHost on the media thread. | 623 // implementations call DemuxerHost on the media thread. |
328 media_task_runner_->PostTask( | 624 media_task_runner_->PostTask( |
329 FROM_HERE, | 625 FROM_HERE, base::Bind(&RendererWrapper::AddTextStreamTask, |
330 base::Bind(&PipelineImpl::ErrorChangedTask, weak_this_, error)); | 626 weak_factory_.GetWeakPtr(), text_stream, config)); |
331 } | 627 } |
332 | 628 |
333 void PipelineImpl::AddTextStream(DemuxerStream* text_stream, | 629 void PipelineImpl::RendererWrapper::RemoveTextStream( |
334 const TextTrackConfig& config) { | 630 DemuxerStream* text_stream) { |
335 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer | 631 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
336 // implementations call DemuxerHost on the media thread. | 632 // implementations call DemuxerHost on the media thread. |
337 media_task_runner_->PostTask( | 633 media_task_runner_->PostTask( |
338 FROM_HERE, base::Bind(&PipelineImpl::AddTextStreamTask, weak_this_, | 634 FROM_HERE, base::Bind(&RendererWrapper::RemoveTextStreamTask, |
339 text_stream, config)); | 635 weak_factory_.GetWeakPtr(), text_stream)); |
340 } | 636 } |
341 | 637 |
342 void PipelineImpl::RemoveTextStream(DemuxerStream* text_stream) { | 638 void PipelineImpl::RendererWrapper::OnError(PipelineStatus error) { |
343 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer | 639 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
344 // implementations call DemuxerHost on the media thread. | 640 |
345 media_task_runner_->PostTask( | 641 media_task_runner_->PostTask(FROM_HERE, |
346 FROM_HERE, | 642 base::Bind(&RendererWrapper::OnPipelineError, |
347 base::Bind(&PipelineImpl::RemoveTextStreamTask, weak_this_, text_stream)); | 643 weak_factory_.GetWeakPtr(), error)); |
348 } | 644 } |
349 | 645 |
350 void PipelineImpl::OnError(PipelineStatus error) { | 646 void PipelineImpl::RendererWrapper::OnEnded() { |
351 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
352 DCHECK(IsRunning()); | |
353 DCHECK_NE(PIPELINE_OK, error); | |
354 VLOG(1) << "Media pipeline error: " << error; | |
355 | |
356 media_task_runner_->PostTask( | |
357 FROM_HERE, | |
358 base::Bind(&PipelineImpl::ErrorChangedTask, weak_this_, error)); | |
359 } | |
360 | |
361 void PipelineImpl::OnEnded() { | |
362 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 647 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
363 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); | 648 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); |
364 | 649 |
365 if (state_ != kPlaying) | 650 if (state_ != kPlaying) |
366 return; | 651 return; |
367 | 652 |
368 DCHECK(!renderer_ended_); | 653 DCHECK(!renderer_ended_); |
369 renderer_ended_ = true; | 654 renderer_ended_ = true; |
370 | |
371 RunEndedCallbackIfNeeded(); | 655 RunEndedCallbackIfNeeded(); |
372 } | 656 } |
373 | 657 |
374 void PipelineImpl::OnStatisticsUpdate(const PipelineStatistics& stats) { | 658 void PipelineImpl::RendererWrapper::OnStatisticsUpdate( |
375 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 659 const PipelineStatistics& stats) { |
376 | 660 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
377 base::AutoLock auto_lock(lock_); | 661 |
378 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; | 662 main_task_runner_->PostTask( |
379 statistics_.video_bytes_decoded += stats.video_bytes_decoded; | 663 FROM_HERE, |
380 statistics_.video_frames_decoded += stats.video_frames_decoded; | 664 base::Bind(&PipelineImpl::OnStatisticsUpdate, weak_pipeline_, stats)); |
381 statistics_.video_frames_dropped += stats.video_frames_dropped; | 665 } |
382 statistics_.audio_memory_usage += stats.audio_memory_usage; | 666 |
383 statistics_.video_memory_usage += stats.video_memory_usage; | 667 void PipelineImpl::RendererWrapper::OnBufferingStateChange( |
384 } | 668 BufferingState state) { |
385 | 669 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
386 void PipelineImpl::OnBufferingStateChange(BufferingState state) { | 670 DVLOG(2) << __FUNCTION__ << "(" << state << ") "; |
387 DVLOG(1) << __FUNCTION__ << "(" << state << ") "; | 671 |
388 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 672 main_task_runner_->PostTask( |
389 | 673 FROM_HERE, |
390 main_task_runner_->PostTask( | 674 base::Bind(&PipelineImpl::OnBufferingStateChange, weak_pipeline_, state)); |
391 FROM_HERE, base::Bind(&Pipeline::Client::OnBufferingStateChange, | 675 } |
392 weak_client_, state)); | 676 |
393 } | 677 void PipelineImpl::RendererWrapper::OnWaitingForDecryptionKey() { |
394 | 678 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
395 void PipelineImpl::OnWaitingForDecryptionKey() { | 679 |
396 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 680 main_task_runner_->PostTask( |
397 | 681 FROM_HERE, |
398 main_task_runner_->PostTask( | 682 base::Bind(&PipelineImpl::OnWaitingForDecryptionKey, weak_pipeline_)); |
399 FROM_HERE, | 683 } |
400 base::Bind(&Pipeline::Client::OnWaitingForDecryptionKey, weak_client_)); | 684 |
401 } | 685 void PipelineImpl::RendererWrapper::OnVideoNaturalSizeChange( |
402 | 686 const gfx::Size& size) { |
403 void PipelineImpl::OnVideoNaturalSizeChange(const gfx::Size& size) { | 687 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
404 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 688 |
405 | 689 main_task_runner_->PostTask( |
406 main_task_runner_->PostTask( | 690 FROM_HERE, base::Bind(&PipelineImpl::OnVideoNaturalSizeChange, |
407 FROM_HERE, base::Bind(&Pipeline::Client::OnVideoNaturalSizeChange, | 691 weak_pipeline_, size)); |
408 weak_client_, size)); | 692 } |
409 } | 693 |
410 | 694 void PipelineImpl::RendererWrapper::OnVideoOpacityChange(bool opaque) { |
411 void PipelineImpl::OnVideoOpacityChange(bool opaque) { | 695 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
412 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 696 |
413 | 697 main_task_runner_->PostTask( |
414 main_task_runner_->PostTask( | 698 FROM_HERE, |
415 FROM_HERE, base::Bind(&Pipeline::Client::OnVideoOpacityChange, | 699 base::Bind(&PipelineImpl::OnVideoOpacityChange, weak_pipeline_, opaque)); |
416 weak_client_, opaque)); | 700 } |
417 } | 701 |
418 | 702 void PipelineImpl::RendererWrapper::SetDuration(base::TimeDelta duration) { |
419 void PipelineImpl::SetDuration(TimeDelta duration) { | |
420 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer | 703 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
421 // implementations call DemuxerHost on the media thread. | 704 // implementations call DemuxerHost on the media thread. |
422 DCHECK(IsRunning()); | |
423 media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET, | 705 media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET, |
424 "duration", duration)); | 706 "duration", duration)); |
425 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); | 707 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); |
426 | 708 |
427 base::AutoLock auto_lock(lock_); | 709 main_task_runner_->PostTask( |
428 duration_ = duration; | 710 FROM_HERE, |
429 main_task_runner_->PostTask( | 711 base::Bind(&PipelineImpl::OnDurationChange, weak_pipeline_, duration)); |
430 FROM_HERE, base::Bind(&Pipeline::Client::OnDurationChange, weak_client_)); | 712 } |
431 } | 713 |
432 | 714 void PipelineImpl::RendererWrapper::OnSeekDone(base::TimeDelta start_time, |
433 void PipelineImpl::StateTransitionTask(PipelineStatus status) { | 715 PipelineStatus status) { |
434 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 716 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
435 | 717 |
436 // No-op any state transitions if we're stopping. | 718 DCHECK(pending_callbacks_); |
437 if (state_ == kStopping || state_ == kStopped) | 719 pending_callbacks_.reset(); |
720 | |
721 if (status != PIPELINE_OK) { | |
722 OnPipelineError(status); | |
438 return; | 723 return; |
439 | 724 } |
440 // Report error from the previous operation. | 725 |
441 if (status != PIPELINE_OK) { | 726 start_time = std::max(start_time, demuxer_->GetStartTime()); |
442 ErrorChangedTask(status); | 727 renderer_->StartPlayingFrom(start_time); |
443 return; | 728 |
444 } | 729 if (text_renderer_) |
445 | 730 text_renderer_->StartPlaying(); |
446 // Guard against accidentally clearing |pending_callbacks_| for states that | 731 |
447 // use it as well as states that should not be using it. | 732 renderer_->SetPlaybackRate(playback_rate_); |
448 DCHECK_EQ(pending_callbacks_.get() != NULL, | 733 renderer_->SetVolume(volume_); |
449 state_ == kSeeking || state_ == kSuspending || state_ == kResuming); | 734 |
450 | 735 SetState(kPlaying); |
736 main_task_runner_->PostTask( | |
737 FROM_HERE, | |
738 base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_, start_time)); | |
739 } | |
740 | |
741 void PipelineImpl::RendererWrapper::OnSuspendDone(base::TimeDelta suspend_time, | |
742 PipelineStatus status) { | |
743 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
744 | |
745 // In case we are suspending or suspended, the error may be recoverable, | |
746 // so don't propagate it now, instead let the subsequent seek during resume | |
747 // propagate it if it's unrecoverable. | |
748 LOG_IF(WARNING, status != PIPELINE_OK) | |
749 << "Encountered pipeline error while suspending: " << status; | |
750 | |
751 DCHECK(pending_callbacks_); | |
451 pending_callbacks_.reset(); | 752 pending_callbacks_.reset(); |
452 | 753 |
453 PipelineStatusCB done_cb = | |
454 base::Bind(&PipelineImpl::StateTransitionTask, weak_this_); | |
455 | |
456 // Switch states, performing any entrance actions for the new state as well. | |
457 SetState(GetNextState()); | |
458 switch (state_) { | |
459 case kInitDemuxer: | |
460 return InitializeDemuxer(done_cb); | |
461 | |
462 case kInitRenderer: | |
463 // When the state_ transfers to kInitRenderer, it means the demuxer has | |
464 // finished parsing the init info. It should call ReportMetadata in case | |
465 // meeting 'decode' error when passing media segment but WebMediaPlayer's | |
466 // ready_state_ is still ReadyStateHaveNothing. In that case, it will | |
467 // treat it as NetworkStateFormatError not NetworkStateDecodeError. | |
468 ReportMetadata(); | |
469 start_timestamp_ = demuxer_->GetStartTime(); | |
470 | |
471 return InitializeRenderer(done_cb); | |
472 | |
473 case kPlaying: | |
474 DCHECK(start_timestamp_ >= base::TimeDelta()); | |
475 renderer_->StartPlayingFrom(start_timestamp_); | |
476 { | |
477 base::AutoLock auto_lock(lock_); | |
478 suspend_timestamp_ = kNoTimestamp(); | |
479 } | |
480 | |
481 if (text_renderer_) | |
482 text_renderer_->StartPlaying(); | |
483 | |
484 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); | |
485 | |
486 PlaybackRateChangedTask(GetPlaybackRate()); | |
487 VolumeChangedTask(GetVolume()); | |
488 return; | |
489 | |
490 case kSuspended: | |
491 renderer_.reset(); | |
492 { | |
493 base::AutoLock auto_lock(lock_); | |
494 statistics_.audio_memory_usage = 0; | |
495 statistics_.video_memory_usage = 0; | |
496 } | |
497 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); | |
498 return; | |
499 | |
500 case kStopping: | |
501 case kStopped: | |
502 case kCreated: | |
503 case kSeeking: | |
504 case kSuspending: | |
505 case kResuming: | |
506 NOTREACHED() << "State has no transition: " << state_; | |
507 return; | |
508 } | |
509 } | |
510 | |
511 // Note that the usage of base::Unretained() with the renderers is considered | |
512 // safe as they are owned by |pending_callbacks_| and share the same lifetime. | |
513 // | |
514 // That being said, deleting the renderers while keeping |pending_callbacks_| | |
515 // running on the media thread would result in crashes. | |
516 void PipelineImpl::DoSeek(TimeDelta seek_timestamp, | |
517 const PipelineStatusCB& done_cb) { | |
518 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
519 DCHECK(!pending_callbacks_.get()); | |
520 DCHECK_EQ(state_, kSeeking); | |
521 SerialRunner::Queue bound_fns; | |
522 | |
523 // Pause. | |
524 if (text_renderer_) { | |
525 bound_fns.Push(base::Bind(&TextRenderer::Pause, | |
526 base::Unretained(text_renderer_.get()))); | |
527 } | |
528 | |
529 // Flush. | |
530 DCHECK(renderer_); | |
531 bound_fns.Push( | |
532 base::Bind(&Renderer::Flush, base::Unretained(renderer_.get()))); | |
533 | |
534 if (text_renderer_) { | |
535 bound_fns.Push(base::Bind(&TextRenderer::Flush, | |
536 base::Unretained(text_renderer_.get()))); | |
537 } | |
538 | |
539 // Seek demuxer. | |
540 bound_fns.Push( | |
541 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | |
542 | |
543 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | |
544 } | |
545 | |
546 void PipelineImpl::DoStop() { | |
547 DVLOG(2) << __FUNCTION__; | |
548 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
549 DCHECK_EQ(state_, kStopping); | |
550 DCHECK(!pending_callbacks_.get()); | |
551 | |
552 // TODO(scherkus): Enforce that Renderer is only called on a single thread, | |
553 // even for accessing media time http://crbug.com/370634 | |
554 std::unique_ptr<Renderer> renderer; | 754 std::unique_ptr<Renderer> renderer; |
555 { | 755 { |
556 base::AutoLock auto_lock(lock_); | 756 base::AutoLock auto_lock(renderer_lock_); |
557 renderer.swap(renderer_); | 757 renderer.swap(renderer_); |
558 } | 758 } |
559 renderer.reset(); | 759 renderer.reset(); |
560 text_renderer_.reset(); | 760 |
561 | 761 SetState(kSuspended); |
562 if (demuxer_) { | 762 main_task_runner_->PostTask( |
563 demuxer_->Stop(); | 763 FROM_HERE, |
564 demuxer_ = NULL; | 764 base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_, suspend_time)); |
565 } | 765 } |
566 | 766 |
567 { | 767 void PipelineImpl::RendererWrapper::OnBufferedTimeRangesChanged( |
568 base::AutoLock auto_lock(lock_); | |
569 running_ = false; | |
570 } | |
571 SetState(kStopped); | |
572 | |
573 // If we stop during initialization/seeking/suspending we don't want to leave | |
574 // outstanding callbacks around. The callbacks also do not get run if the | |
575 // pipeline is stopped before it had a chance to complete outstanding tasks. | |
576 seek_cb_.Reset(); | |
577 suspend_cb_.Reset(); | |
578 | |
579 if (!stop_cb_.is_null()) { | |
580 // Invalid all weak pointers so it's safe to destroy |this| on the render | |
581 // main thread. | |
582 weak_factory_.InvalidateWeakPtrs(); | |
583 | |
584 // Post the stop callback to enqueue it after the tasks that may have been | |
585 // Demuxer and Renderer during stopping. | |
586 media_task_runner_->PostTask(FROM_HERE, base::ResetAndReturn(&stop_cb_)); | |
587 } | |
588 } | |
589 | |
590 void PipelineImpl::OnBufferedTimeRangesChanged( | |
591 const Ranges<base::TimeDelta>& ranges) { | 768 const Ranges<base::TimeDelta>& ranges) { |
592 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer | 769 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
593 // implementations call DemuxerHost on the media thread. | 770 // implementations call DemuxerHost on the media thread. |
594 base::AutoLock auto_lock(lock_); | 771 main_task_runner_->PostTask( |
595 buffered_time_ranges_ = ranges; | 772 FROM_HERE, base::Bind(&PipelineImpl::OnBufferedTimeRangesChange, |
596 did_loading_progress_ = true; | 773 weak_pipeline_, ranges)); |
597 } | 774 } |
598 | 775 |
599 void PipelineImpl::StartTask() { | 776 void PipelineImpl::RendererWrapper::Start( |
600 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 777 Demuxer* demuxer, |
601 | 778 std::unique_ptr<Renderer> renderer, |
602 CHECK_EQ(kCreated, state_) | 779 std::unique_ptr<TextRenderer> text_renderer) { |
603 << "Media pipeline cannot be started more than once"; | 780 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
604 | 781 |
605 text_renderer_ = CreateTextRenderer(); | 782 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " |
783 << state_; | |
784 SetState(kStarting); | |
785 | |
786 DCHECK(!demuxer_); | |
787 DCHECK(!renderer_); | |
788 DCHECK(!text_renderer_); | |
789 DCHECK(!renderer_ended_); | |
790 DCHECK(!text_renderer_ended_); | |
791 demuxer_ = demuxer; | |
792 { | |
793 base::AutoLock auto_lock(renderer_lock_); | |
794 renderer_ = std::move(renderer); | |
795 } | |
796 text_renderer_ = std::move(text_renderer); | |
797 | |
798 // Initialize text renderer. | |
606 if (text_renderer_) { | 799 if (text_renderer_) { |
607 text_renderer_->Initialize( | 800 text_renderer_->Initialize(base::Bind(&RendererWrapper::OnTextRendererEnded, |
608 base::Bind(&PipelineImpl::OnTextRendererEnded, weak_this_)); | 801 weak_factory_.GetWeakPtr())); |
609 } | 802 } |
610 | 803 |
611 StateTransitionTask(PIPELINE_OK); | 804 // Queue asynchronous actions required to seek. |
alokp
2016/06/08 19:59:48
Instead of using StateTransitionTask, we use Seria
| |
612 } | 805 DCHECK(!pending_callbacks_); |
613 | 806 SerialRunner::Queue fns; |
614 void PipelineImpl::StopTask(const base::Closure& stop_cb) { | 807 |
615 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 808 // Initialize demuxer. |
616 DCHECK(stop_cb_.is_null()); | 809 fns.Push(base::Bind(&RendererWrapper::InitializeDemuxer, |
617 | 810 weak_factory_.GetWeakPtr())); |
618 if (state_ == kStopped) { | 811 |
619 // Invalid all weak pointers so it's safe to destroy |this| on the render | 812 // Once the demuxer is initialized successfully, media metadata must be |
620 // main thread. | 813 // available - report the metadata to client. |
621 weak_factory_.InvalidateWeakPtrs(); | 814 fns.Push( |
622 | 815 base::Bind(&RendererWrapper::ReportMetadata, weak_factory_.GetWeakPtr())); |
623 // NOTE: pipeline may be deleted at this point in time as a result of | 816 |
624 // executing |stop_cb|. | 817 // Initialize renderer. |
625 stop_cb.Run(); | 818 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, |
626 | 819 weak_factory_.GetWeakPtr())); |
627 return; | 820 |
628 } | 821 // Run tasks. |
629 | 822 pending_callbacks_ = SerialRunner::Run( |
630 stop_cb_ = stop_cb; | 823 fns, base::Bind(&RendererWrapper::OnSeekDone, weak_factory_.GetWeakPtr(), |
631 | 824 base::TimeDelta())); |
632 // We may already be stopping due to a runtime error. | 825 } |
633 if (state_ == kStopping) | 826 |
634 return; | 827 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { |
635 | 828 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
636 // Do not report statistics if the pipeline is not fully initialized. | 829 DCHECK(state_ != kStopping && state_ != kStopped); |
637 if (state_ == kSeeking || state_ == kPlaying || state_ == kSuspending || | |
638 state_ == kSuspended || state_ == kResuming) { | |
639 PipelineStatistics stats = GetStatistics(); | |
640 if (stats.video_frames_decoded > 0) { | |
641 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", | |
642 stats.video_frames_dropped); | |
643 } | |
644 } | |
645 | 830 |
646 SetState(kStopping); | 831 SetState(kStopping); |
832 | |
833 // If we stop during starting/seeking/suspending/resuming we don't want to | |
834 // leave outstanding callbacks around. The callbacks also do not get run if | |
835 // the pipeline is stopped before it had a chance to complete outstanding | |
836 // tasks. | |
647 pending_callbacks_.reset(); | 837 pending_callbacks_.reset(); |
648 DoStop(); | 838 |
649 } | 839 std::unique_ptr<Renderer> renderer; |
650 | 840 { |
651 void PipelineImpl::ErrorChangedTask(PipelineStatus error) { | 841 base::AutoLock auto_lock(renderer_lock_); |
842 renderer.swap(renderer_); | |
843 } | |
844 renderer.reset(); | |
845 text_renderer_.reset(); | |
846 | |
847 if (demuxer_) { | |
848 demuxer_->Stop(); | |
849 demuxer_ = nullptr; | |
850 } | |
851 | |
852 SetState(kStopped); | |
853 | |
854 // Post the stop callback to enqueue it after the tasks that may have been | |
855 // Demuxer and Renderer during stopping. | |
856 media_task_runner_->PostTask(FROM_HERE, stop_cb); | |
857 } | |
858 | |
859 void PipelineImpl::RendererWrapper::OnPipelineError(PipelineStatus error) { | |
652 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 860 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
653 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; | 861 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; |
654 | 862 |
655 // Preserve existing abnormal status. | 863 // Preserve existing abnormal status. |
656 if (status_ != PIPELINE_OK) | 864 if (status_ != PIPELINE_OK) |
657 return; | 865 return; |
658 | 866 |
659 // Don't report pipeline error events to the media log here. The embedder will | 867 // Don't report pipeline error events to the media log here. The embedder |
660 // log this when Client::OnError is called. If the pipeline is already stopped | 868 // will log this when Client::OnError is called. If the pipeline is already |
661 // or stopping we also don't want to log any event. In case we are suspending | 869 // stopped or stopping we also don't want to log any event. In case we are |
662 // or suspended, the error may be recoverable, so don't propagate it now, | 870 // suspending or suspended, the error may be recoverable, so don't propagate |
663 // instead let the subsequent seek during resume propagate it if it's | 871 // it now, instead let the subsequent seek during resume propagate it if |
664 // unrecoverable. | 872 // it's unrecoverable. |
665 if (state_ == kStopping || state_ == kStopped || state_ == kSuspending || | 873 if (state_ == kStopping || state_ == kStopped || state_ == kSuspending || |
666 state_ == kSuspended) { | 874 state_ == kSuspended) { |
667 return; | 875 return; |
668 } | 876 } |
669 | 877 |
670 // Once we enter |kStopping| state, nothing is reported back to the client. | |
671 // If we encounter an error during initialization/seeking/suspending, | |
672 // report the error using the completion callbacks for those tasks. | |
673 status_ = error; | 878 status_ = error; |
674 bool error_reported = false; | 879 main_task_runner_->PostTask( |
675 if (!seek_cb_.is_null()) { | 880 FROM_HERE, base::Bind(&PipelineImpl::OnError, weak_pipeline_, error)); |
676 base::ResetAndReturn(&seek_cb_).Run(status_); | |
677 error_reported = true; | |
678 } | |
679 if (!suspend_cb_.is_null()) { | |
680 base::ResetAndReturn(&suspend_cb_).Run(status_); | |
681 error_reported = true; | |
682 } | |
683 if (!error_reported) { | |
684 DCHECK_NE(status_, PIPELINE_OK); | |
685 main_task_runner_->PostTask( | |
686 FROM_HERE, | |
687 base::Bind(&Pipeline::Client::OnError, weak_client_, status_)); | |
688 } | |
689 | |
690 SetState(kStopping); | |
691 pending_callbacks_.reset(); | |
692 DoStop(); | |
693 } | 881 } |
694 | 882 |
695 void PipelineImpl::PlaybackRateChangedTask(double playback_rate) { | 883 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { |
696 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 884 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
697 | 885 |
698 // Playback rate changes are only carried out while playing. | 886 playback_rate_ = playback_rate; |
699 if (state_ != kPlaying) | 887 if (state_ == kPlaying) |
700 return; | 888 renderer_->SetPlaybackRate(playback_rate_); |
701 | |
702 renderer_->SetPlaybackRate(playback_rate); | |
703 } | 889 } |
704 | 890 |
705 void PipelineImpl::VolumeChangedTask(float volume) { | 891 void PipelineImpl::RendererWrapper::SetVolume(float volume) { |
706 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 892 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
707 | 893 |
708 // Volume changes are only carried out while playing. | 894 volume_ = volume; |
709 if (state_ != kPlaying) | 895 if (state_ == kPlaying) |
710 return; | 896 renderer_->SetVolume(volume_); |
711 | |
712 renderer_->SetVolume(volume); | |
713 } | 897 } |
714 | 898 |
715 void PipelineImpl::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { | 899 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { |
716 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 900 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
717 DCHECK(stop_cb_.is_null()); | |
718 | 901 |
719 // Suppress seeking if we're not fully started. | 902 // Suppress seeking if we're not fully started. |
720 if (state_ != kPlaying) { | 903 if (state_ != kPlaying) { |
721 DCHECK(state_ == kStopping || state_ == kStopped) | 904 DCHECK(state_ == kStopping || state_ == kStopped) |
722 << "Receive seek in unexpected state: " << state_; | 905 << "Receive seek in unexpected state: " << state_; |
723 seek_cb.Run(PIPELINE_ERROR_INVALID_STATE); | 906 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
724 return; | 907 return; |
725 } | 908 } |
726 | 909 DCHECK(renderer_); |
727 DCHECK(seek_cb_.is_null()); | 910 DCHECK(!pending_callbacks_.get()); |
728 | |
729 const base::TimeDelta seek_timestamp = | |
730 std::max(time, demuxer_->GetStartTime()); | |
731 | 911 |
732 SetState(kSeeking); | 912 SetState(kSeeking); |
733 seek_cb_ = seek_cb; | |
734 renderer_ended_ = false; | 913 renderer_ended_ = false; |
735 text_renderer_ended_ = false; | 914 text_renderer_ended_ = false; |
736 start_timestamp_ = seek_timestamp; | |
737 | 915 |
738 DoSeek(seek_timestamp, | 916 // Queue asynchronous actions required to seek. |
739 base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); | 917 SerialRunner::Queue bound_fns; |
918 | |
919 // Pause. | |
920 if (text_renderer_) { | |
921 bound_fns.Push(base::Bind(&TextRenderer::Pause, | |
922 base::Unretained(text_renderer_.get()))); | |
923 } | |
924 | |
925 // Flush. | |
926 bound_fns.Push( | |
927 base::Bind(&Renderer::Flush, base::Unretained(renderer_.get()))); | |
928 if (text_renderer_) { | |
929 bound_fns.Push(base::Bind(&TextRenderer::Flush, | |
930 base::Unretained(text_renderer_.get()))); | |
931 } | |
932 | |
933 // Seek. | |
934 bound_fns.Push(base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), | |
935 std::max(time, demuxer_->GetStartTime()))); | |
936 | |
937 pending_callbacks_ = SerialRunner::Run( | |
938 bound_fns, base::Bind(&RendererWrapper::OnSeekDone, | |
939 weak_factory_.GetWeakPtr(), time)); | |
740 } | 940 } |
741 | 941 |
742 void PipelineImpl::SuspendTask(const PipelineStatusCB& suspend_cb) { | 942 void PipelineImpl::RendererWrapper::Suspend() { |
743 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 943 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
744 | 944 |
745 // Suppress suspending if we're not playing. | 945 // Suppress suspending if we're not playing. |
746 if (state_ != kPlaying) { | 946 if (state_ != kPlaying) { |
747 DCHECK(state_ == kStopping || state_ == kStopped) | 947 DCHECK(state_ == kStopping || state_ == kStopped) |
748 << "Receive suspend in unexpected state: " << state_; | 948 << "Received suspend in unexpected state: " << state_; |
749 suspend_cb.Run(PIPELINE_ERROR_INVALID_STATE); | 949 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
750 return; | 950 return; |
751 } | 951 } |
752 DCHECK(renderer_); | 952 DCHECK(renderer_); |
753 DCHECK(!pending_callbacks_.get()); | 953 DCHECK(!pending_callbacks_.get()); |
754 | 954 |
755 SetState(kSuspending); | 955 SetState(kSuspending); |
756 suspend_cb_ = suspend_cb; | |
757 | 956 |
758 // Freeze playback and record the media time before flushing. (Flushing clears | 957 // Freeze playback and record the media time before flushing. |
759 // the value.) | 958 // (Flushing clears the value.) |
760 renderer_->SetPlaybackRate(0.0); | 959 renderer_->SetPlaybackRate(0.0); |
761 { | 960 base::TimeDelta suspend_time = renderer_->GetMediaTime(); |
762 base::AutoLock auto_lock(lock_); | 961 DCHECK(suspend_time != kNoTimestamp()); |
763 suspend_timestamp_ = renderer_->GetMediaTime(); | |
764 DCHECK(suspend_timestamp_ != kNoTimestamp()); | |
765 } | |
766 | 962 |
767 // Queue the asynchronous actions required to stop playback. (Matches setup in | 963 // Queue asynchronous actions required to suspend playback. |
768 // DoSeek().) | |
769 // TODO(sandersd): Share implementation with DoSeek(). | |
770 SerialRunner::Queue fns; | 964 SerialRunner::Queue fns; |
771 | 965 |
966 // Pause. | |
772 if (text_renderer_) { | 967 if (text_renderer_) { |
773 fns.Push(base::Bind(&TextRenderer::Pause, | 968 fns.Push(base::Bind(&TextRenderer::Pause, |
774 base::Unretained(text_renderer_.get()))); | 969 base::Unretained(text_renderer_.get()))); |
775 } | 970 } |
776 | 971 |
972 // Flush. | |
777 fns.Push(base::Bind(&Renderer::Flush, base::Unretained(renderer_.get()))); | 973 fns.Push(base::Bind(&Renderer::Flush, base::Unretained(renderer_.get()))); |
778 | |
779 if (text_renderer_) { | 974 if (text_renderer_) { |
780 fns.Push(base::Bind(&TextRenderer::Flush, | 975 fns.Push(base::Bind(&TextRenderer::Flush, |
781 base::Unretained(text_renderer_.get()))); | 976 base::Unretained(text_renderer_.get()))); |
782 } | 977 } |
783 | 978 |
784 pending_callbacks_ = SerialRunner::Run( | 979 pending_callbacks_ = SerialRunner::Run( |
785 fns, base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); | 980 fns, base::Bind(&RendererWrapper::OnSuspendDone, |
981 weak_factory_.GetWeakPtr(), suspend_time)); | |
786 } | 982 } |
787 | 983 |
788 void PipelineImpl::ResumeTask(std::unique_ptr<Renderer> renderer, | 984 void PipelineImpl::RendererWrapper::Resume(base::TimeDelta time, |
789 base::TimeDelta timestamp, | 985 std::unique_ptr<Renderer> renderer) { |
790 const PipelineStatusCB& seek_cb) { | |
791 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 986 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
792 | 987 |
793 // Suppress resuming if we're not suspended. | 988 // Suppress resuming if we're not suspended. |
794 if (state_ != kSuspended) { | 989 if (state_ != kSuspended) { |
795 DCHECK(state_ == kStopping || state_ == kStopped) | 990 DCHECK(state_ == kStopping || state_ == kStopped) |
796 << "Receive resume in unexpected state: " << state_; | 991 << "Received resume in unexpected state: " << state_; |
797 seek_cb.Run(PIPELINE_ERROR_INVALID_STATE); | 992 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
798 return; | 993 return; |
799 } | 994 } |
800 DCHECK(!renderer_); | 995 DCHECK(!renderer_); |
801 DCHECK(!pending_callbacks_.get()); | 996 DCHECK(!pending_callbacks_.get()); |
802 | 997 |
803 SetState(kResuming); | 998 SetState(kResuming); |
804 renderer_ = std::move(renderer); | 999 { |
805 | 1000 base::AutoLock auto_lock(renderer_lock_); |
806 // Set up for a seek. (Matches setup in SeekTask().) | 1001 renderer_ = std::move(renderer); |
807 // TODO(sandersd): Share implementation with SeekTask(). | 1002 } |
808 seek_cb_ = seek_cb; | |
809 renderer_ended_ = false; | 1003 renderer_ended_ = false; |
810 text_renderer_ended_ = false; | 1004 text_renderer_ended_ = false; |
811 start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime()); | |
812 | 1005 |
813 // Queue the asynchronous actions required to start playback. Unlike DoSeek(), | 1006 // Queue the asynchronous actions required to resume playback. |
814 // we need to initialize the renderer ourselves (we don't want to enter state | |
815 // kInitDemuxer, and even if we did the current code would seek to the start | |
816 // instead of |timestamp|). | |
817 SerialRunner::Queue fns; | 1007 SerialRunner::Queue fns; |
818 | 1008 |
819 fns.Push( | 1009 // Seek. |
820 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_)); | 1010 fns.Push(base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), |
1011 std::max(time, demuxer_->GetStartTime()))); | |
821 | 1012 |
822 fns.Push(base::Bind(&PipelineImpl::InitializeRenderer, weak_this_)); | 1013 // Initialize the new renderer. |
1014 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, | |
1015 weak_factory_.GetWeakPtr())); | |
823 | 1016 |
824 pending_callbacks_ = SerialRunner::Run( | 1017 // Run tasks. |
825 fns, base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); | 1018 pending_callbacks_ = |
1019 SerialRunner::Run(fns, base::Bind(&RendererWrapper::OnSeekDone, | |
1020 weak_factory_.GetWeakPtr(), time)); | |
826 } | 1021 } |
827 | 1022 |
828 void PipelineImpl::SetCdmTask(CdmContext* cdm_context, | 1023 void PipelineImpl::RendererWrapper::SetCdm( |
829 const CdmAttachedCB& cdm_attached_cb) { | 1024 CdmContext* cdm_context, |
830 base::AutoLock auto_lock(lock_); | 1025 const CdmAttachedCB& cdm_attached_cb) { |
1026 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
1027 | |
831 if (!renderer_) { | 1028 if (!renderer_) { |
832 cdm_context_ = cdm_context; | 1029 cdm_context_ = cdm_context; |
833 cdm_attached_cb.Run(true); | 1030 cdm_attached_cb.Run(true); |
834 return; | 1031 return; |
835 } | 1032 } |
836 | 1033 |
837 renderer_->SetCdm(cdm_context, | 1034 renderer_->SetCdm(cdm_context, base::Bind(&RendererWrapper::OnCdmAttached, |
838 base::Bind(&PipelineImpl::OnCdmAttached, weak_this_, | 1035 weak_factory_.GetWeakPtr(), |
839 cdm_attached_cb, cdm_context)); | 1036 cdm_attached_cb, cdm_context)); |
840 } | 1037 } |
841 | 1038 |
842 void PipelineImpl::OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, | 1039 void PipelineImpl::RendererWrapper::OnCdmAttached( |
843 CdmContext* cdm_context, | 1040 const CdmAttachedCB& cdm_attached_cb, |
844 bool success) { | 1041 CdmContext* cdm_context, |
1042 bool success) { | |
845 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 1043 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
1044 | |
846 if (success) | 1045 if (success) |
847 cdm_context_ = cdm_context; | 1046 cdm_context_ = cdm_context; |
848 cdm_attached_cb.Run(success); | 1047 cdm_attached_cb.Run(success); |
849 } | 1048 } |
850 | 1049 |
851 void PipelineImpl::OnTextRendererEnded() { | 1050 void PipelineImpl::RendererWrapper::OnTextRendererEnded() { |
852 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 1051 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
853 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED)); | 1052 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED)); |
854 | 1053 |
855 if (state_ != kPlaying) | 1054 if (state_ != kPlaying) |
856 return; | 1055 return; |
857 | 1056 |
858 DCHECK(!text_renderer_ended_); | 1057 DCHECK(!text_renderer_ended_); |
859 text_renderer_ended_ = true; | 1058 text_renderer_ended_ = true; |
860 | 1059 |
861 RunEndedCallbackIfNeeded(); | 1060 RunEndedCallbackIfNeeded(); |
862 } | 1061 } |
863 | 1062 |
864 void PipelineImpl::RunEndedCallbackIfNeeded() { | 1063 void PipelineImpl::RendererWrapper::RunEndedCallbackIfNeeded() { |
865 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 1064 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
866 | 1065 |
867 if (renderer_ && !renderer_ended_) | 1066 if (renderer_ && !renderer_ended_) |
868 return; | 1067 return; |
869 | 1068 |
870 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) | 1069 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_) |
871 return; | 1070 return; |
872 | 1071 |
873 DCHECK_EQ(status_, PIPELINE_OK); | 1072 DCHECK_EQ(status_, PIPELINE_OK); |
874 main_task_runner_->PostTask( | 1073 main_task_runner_->PostTask( |
875 FROM_HERE, base::Bind(&Pipeline::Client::OnEnded, weak_client_)); | 1074 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); |
876 } | 1075 } |
877 | 1076 |
878 std::unique_ptr<TextRenderer> PipelineImpl::CreateTextRenderer() { | 1077 void PipelineImpl::RendererWrapper::AddTextStreamTask( |
1078 DemuxerStream* text_stream, | |
1079 const TextTrackConfig& config) { | |
879 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 1080 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
880 | 1081 |
881 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); | |
882 if (!cmd_line->HasSwitch(switches::kEnableInbandTextTracks)) | |
883 return nullptr; | |
884 | |
885 return base::WrapUnique(new media::TextRenderer( | |
886 media_task_runner_, | |
887 base::Bind(&PipelineImpl::OnAddTextTrack, weak_this_))); | |
888 } | |
889 | |
890 void PipelineImpl::AddTextStreamTask(DemuxerStream* text_stream, | |
891 const TextTrackConfig& config) { | |
892 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
893 // TODO(matthewjheaney): fix up text_ended_ when text stream | 1082 // TODO(matthewjheaney): fix up text_ended_ when text stream |
894 // is added (http://crbug.com/321446). | 1083 // is added (http://crbug.com/321446). |
895 if (text_renderer_) | 1084 if (text_renderer_) |
896 text_renderer_->AddTextStream(text_stream, config); | 1085 text_renderer_->AddTextStream(text_stream, config); |
897 } | 1086 } |
898 | 1087 |
899 void PipelineImpl::RemoveTextStreamTask(DemuxerStream* text_stream) { | 1088 void PipelineImpl::RendererWrapper::RemoveTextStreamTask( |
1089 DemuxerStream* text_stream) { | |
900 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 1090 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
901 | 1091 |
902 if (text_renderer_) | 1092 if (text_renderer_) |
903 text_renderer_->RemoveTextStream(text_stream); | 1093 text_renderer_->RemoveTextStream(text_stream); |
904 } | 1094 } |
905 | 1095 |
906 void PipelineImpl::OnAddTextTrack(const TextTrackConfig& config, | 1096 void PipelineImpl::RendererWrapper::InitializeDemuxer( |
907 const AddTextTrackDoneCB& done_cb) { | 1097 const PipelineStatusCB& done_cb) { |
908 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
909 | |
910 main_task_runner_->PostTask( | |
911 FROM_HERE, base::Bind(&Pipeline::Client::OnAddTextTrack, weak_client_, | |
912 config, done_cb)); | |
913 } | |
914 | |
915 void PipelineImpl::InitializeDemuxer(const PipelineStatusCB& done_cb) { | |
916 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 1098 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
917 | 1099 |
918 demuxer_->Initialize(this, done_cb, !!text_renderer_); | 1100 demuxer_->Initialize(this, done_cb, !!text_renderer_); |
919 } | 1101 } |
920 | 1102 |
921 void PipelineImpl::InitializeRenderer(const PipelineStatusCB& done_cb) { | 1103 void PipelineImpl::RendererWrapper::InitializeRenderer( |
1104 const PipelineStatusCB& done_cb) { | |
922 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 1105 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
923 | 1106 |
924 if (!demuxer_->GetStream(DemuxerStream::AUDIO) && | 1107 if (!demuxer_->GetStream(DemuxerStream::AUDIO) && |
925 !demuxer_->GetStream(DemuxerStream::VIDEO)) { | 1108 !demuxer_->GetStream(DemuxerStream::VIDEO)) { |
926 { | 1109 done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER); |
927 base::AutoLock auto_lock(lock_); | |
928 renderer_.reset(); | |
929 } | |
930 OnError(PIPELINE_ERROR_COULD_NOT_RENDER); | |
931 return; | 1110 return; |
932 } | 1111 } |
933 | 1112 |
934 if (cdm_context_) | 1113 if (cdm_context_) |
935 renderer_->SetCdm(cdm_context_, base::Bind(&IgnoreCdmAttached)); | 1114 renderer_->SetCdm(cdm_context_, base::Bind(&IgnoreCdmAttached)); |
936 | 1115 |
937 renderer_->Initialize(demuxer_, this, done_cb); | 1116 renderer_->Initialize(demuxer_, this, done_cb); |
938 } | 1117 } |
939 | 1118 |
940 void PipelineImpl::ReportMetadata() { | 1119 void PipelineImpl::RendererWrapper::ReportMetadata() { |
941 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 1120 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
942 | 1121 |
943 PipelineMetadata metadata; | 1122 PipelineMetadata metadata; |
944 metadata.timeline_offset = demuxer_->GetTimelineOffset(); | 1123 metadata.timeline_offset = demuxer_->GetTimelineOffset(); |
945 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); | 1124 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); |
946 if (stream) { | 1125 if (stream) { |
947 metadata.has_video = true; | 1126 metadata.has_video = true; |
948 metadata.natural_size = stream->video_decoder_config().natural_size(); | 1127 metadata.natural_size = stream->video_decoder_config().natural_size(); |
949 metadata.video_rotation = stream->video_rotation(); | 1128 metadata.video_rotation = stream->video_rotation(); |
950 } | 1129 } |
951 if (demuxer_->GetStream(DemuxerStream::AUDIO)) { | 1130 if (demuxer_->GetStream(DemuxerStream::AUDIO)) { |
952 metadata.has_audio = true; | 1131 metadata.has_audio = true; |
953 } | 1132 } |
954 | 1133 |
955 main_task_runner_->PostTask( | 1134 main_task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::OnMetadata, |
956 FROM_HERE, | 1135 weak_pipeline_, metadata)); |
957 base::Bind(&Pipeline::Client::OnMetadata, weak_client_, metadata)); | |
958 } | 1136 } |
959 | 1137 |
960 } // namespace media | 1138 } // namespace media |
OLD | NEW |