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

Side by Side Diff: media/base/pipeline_impl.cc

Issue 2097623002: Rearrange function definitions in the same order as declared. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/bind_helpers.h" 10 #include "base/bind_helpers.h"
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
98 98
99 // RendererClient implementation. 99 // RendererClient implementation.
100 void OnError(PipelineStatus error) final; 100 void OnError(PipelineStatus error) final;
101 void OnEnded() final; 101 void OnEnded() final;
102 void OnStatisticsUpdate(const PipelineStatistics& stats) final; 102 void OnStatisticsUpdate(const PipelineStatistics& stats) final;
103 void OnBufferingStateChange(BufferingState state) final; 103 void OnBufferingStateChange(BufferingState state) final;
104 void OnWaitingForDecryptionKey() final; 104 void OnWaitingForDecryptionKey() final;
105 void OnVideoNaturalSizeChange(const gfx::Size& size) final; 105 void OnVideoNaturalSizeChange(const gfx::Size& size) final;
106 void OnVideoOpacityChange(bool opaque) final; 106 void OnVideoOpacityChange(bool opaque) final;
107 107
108 // TextRenderer tasks and notifications.
109 void OnTextRendererEnded();
110 void AddTextStreamTask(DemuxerStream* text_stream,
111 const TextTrackConfig& config);
112 void RemoveTextStreamTask(DemuxerStream* text_stream);
113
114 // Common handlers for notifications from renderers and demuxer.
115 void OnPipelineError(PipelineStatus error);
116 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb,
117 CdmContext* cdm_context,
118 bool success);
119 void CheckPlaybackEnded();
120
121 // State transition tasks.
108 void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb); 122 void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb);
109 void DoStop(const base::Closure& done_cb); 123 void DoStop(const base::Closure& done_cb);
110 void OnPipelineError(PipelineStatus error);
111 void OnTextRendererEnded();
112 void RunEndedCallbackIfNeeded();
113 void SetState(State next_state); 124 void SetState(State next_state);
114 State GetNextState() const; 125 State GetNextState() const;
115 void StateTransitionTask(PipelineStatus status); 126 void StateTransitionTask(PipelineStatus status);
116 void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb,
117 CdmContext* cdm_context,
118 bool success);
119 void AddTextStreamTask(DemuxerStream* text_stream,
120 const TextTrackConfig& config);
121 void RemoveTextStreamTask(DemuxerStream* text_stream);
122 void InitializeDemuxer(const PipelineStatusCB& done_cb); 127 void InitializeDemuxer(const PipelineStatusCB& done_cb);
123 void InitializeRenderer(const PipelineStatusCB& done_cb); 128 void InitializeRenderer(const PipelineStatusCB& done_cb);
124 void DestroyRenderer(); 129 void DestroyRenderer();
125 void ReportMetadata(); 130 void ReportMetadata();
126 131
127 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; 132 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
128 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; 133 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
129 const scoped_refptr<MediaLog> media_log_; 134 const scoped_refptr<MediaLog> media_log_;
130 135
131 base::WeakPtr<PipelineImpl> weak_pipeline_; 136 base::WeakPtr<PipelineImpl> weak_pipeline_;
(...skipping 27 matching lines...) Expand all
159 bool text_renderer_ended_; 164 bool text_renderer_ended_;
160 165
161 // Series of tasks to Start(), Seek(), and Resume(). 166 // Series of tasks to Start(), Seek(), and Resume().
162 std::unique_ptr<SerialRunner> pending_callbacks_; 167 std::unique_ptr<SerialRunner> pending_callbacks_;
163 168
164 base::WeakPtr<RendererWrapper> weak_this_; 169 base::WeakPtr<RendererWrapper> weak_this_;
165 base::WeakPtrFactory<RendererWrapper> weak_factory_; 170 base::WeakPtrFactory<RendererWrapper> weak_factory_;
166 DISALLOW_COPY_AND_ASSIGN(RendererWrapper); 171 DISALLOW_COPY_AND_ASSIGN(RendererWrapper);
167 }; 172 };
168 173
169 PipelineImpl::PipelineImpl( 174 PipelineImpl::RendererWrapper::RendererWrapper(
170 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, 175 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
171 MediaLog* media_log) 176 scoped_refptr<MediaLog> media_log)
172 : media_task_runner_(media_task_runner), 177 : media_task_runner_(std::move(media_task_runner)),
173 media_log_(media_log), 178 main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
174 client_(nullptr), 179 media_log_(std::move(media_log)),
180 demuxer_(nullptr),
175 playback_rate_(kDefaultPlaybackRate), 181 playback_rate_(kDefaultPlaybackRate),
176 volume_(kDefaultVolume), 182 volume_(kDefaultVolume),
183 cdm_context_(nullptr),
184 state_(kCreated),
185 status_(PIPELINE_OK),
186 renderer_ended_(false),
187 text_renderer_ended_(false),
177 weak_factory_(this) { 188 weak_factory_(this) {
178 DVLOG(2) << __FUNCTION__; 189 weak_this_ = weak_factory_.GetWeakPtr();
179 renderer_wrapper_.reset(new RendererWrapper(media_task_runner_, media_log_)); 190 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
180 } 191 }
181 192
182 PipelineImpl::~PipelineImpl() { 193 PipelineImpl::RendererWrapper::~RendererWrapper() {
183 DVLOG(2) << __FUNCTION__; 194 DCHECK(media_task_runner_->BelongsToCurrentThread());
184 DCHECK(thread_checker_.CalledOnValidThread()); 195 DCHECK(state_ == kCreated || state_ == kStopped);
185 DCHECK(!client_) << "Stop() must complete before destroying object"; 196 }
186 DCHECK(seek_cb_.is_null()); 197
187 DCHECK(suspend_cb_.is_null()); 198 void PipelineImpl::RendererWrapper::Start(
188 DCHECK(!weak_factory_.HasWeakPtrs()); 199 Demuxer* demuxer,
189 200 std::unique_ptr<Renderer> renderer,
190 // RendererWrapper is deleted on the media thread. 201 std::unique_ptr<TextRenderer> text_renderer,
191 media_task_runner_->DeleteSoon(FROM_HERE, renderer_wrapper_.release()); 202 base::WeakPtr<PipelineImpl> weak_pipeline) {
192 } 203 DCHECK(media_task_runner_->BelongsToCurrentThread());
193 204 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: "
194 void PipelineImpl::Start(Demuxer* demuxer, 205 << state_;
195 std::unique_ptr<Renderer> renderer, 206
196 Client* client, 207 DCHECK(!demuxer_);
197 const PipelineStatusCB& seek_cb) { 208 DCHECK(!shared_state_.renderer);
198 DVLOG(2) << __FUNCTION__; 209 DCHECK(!text_renderer_);
199 DCHECK(thread_checker_.CalledOnValidThread()); 210 DCHECK(!renderer_ended_);
200 DCHECK(demuxer); 211 DCHECK(!text_renderer_ended_);
201 DCHECK(renderer); 212 DCHECK(!weak_pipeline_);
202 DCHECK(client); 213 demuxer_ = demuxer;
203 DCHECK(!seek_cb.is_null()); 214 {
204 215 base::AutoLock auto_lock(shared_state_lock_);
205 DCHECK(!client_); 216 shared_state_.renderer = std::move(renderer);
206 DCHECK(seek_cb_.is_null()); 217 }
207 client_ = client; 218 text_renderer_ = std::move(text_renderer);
208 seek_cb_ = seek_cb; 219 if (text_renderer_) {
209 220 text_renderer_->Initialize(
210 std::unique_ptr<TextRenderer> text_renderer; 221 base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_));
211 if (base::CommandLine::ForCurrentProcess()->HasSwitch( 222 }
212 switches::kEnableInbandTextTracks)) { 223 weak_pipeline_ = weak_pipeline;
213 text_renderer.reset(new TextRenderer( 224
214 media_task_runner_, 225 StateTransitionTask(PIPELINE_OK);
215 BindToCurrentLoop(base::Bind(&PipelineImpl::OnAddTextTrack, 226 }
216 weak_factory_.GetWeakPtr())))); 227
217 } 228 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) {
218 229 DCHECK(media_task_runner_->BelongsToCurrentThread());
219 media_task_runner_->PostTask( 230 DCHECK(state_ != kStopping && state_ != kStopped);
231
232 SetState(kStopping);
233
234 if (shared_state_.statistics.video_frames_decoded > 0) {
235 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount",
236 shared_state_.statistics.video_frames_dropped);
237 }
238
239 // If we stop during starting/seeking/suspending/resuming we don't want to
240 // leave outstanding callbacks around. The callbacks also do not get run if
241 // the pipeline is stopped before it had a chance to complete outstanding
242 // tasks.
243 pending_callbacks_.reset();
244
245 DoStop(stop_cb);
246 }
247
248 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) {
249 DCHECK(media_task_runner_->BelongsToCurrentThread());
250
251 // Suppress seeking if we're not fully started.
252 if (state_ != kPlaying) {
253 DCHECK(state_ == kStopping || state_ == kStopped)
254 << "Receive seek in unexpected state: " << state_;
255 OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
256 return;
257 }
258
259 const base::TimeDelta seek_timestamp =
260 std::max(time, demuxer_->GetStartTime());
261
262 SetState(kSeeking);
263 renderer_ended_ = false;
264 text_renderer_ended_ = false;
265 start_timestamp_ = seek_timestamp;
266
267 DoSeek(seek_timestamp,
268 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
269 }
270
271 void PipelineImpl::RendererWrapper::Suspend() {
272 DCHECK(media_task_runner_->BelongsToCurrentThread());
273
274 // Suppress suspending if we're not playing.
275 if (state_ != kPlaying) {
276 DCHECK(state_ == kStopping || state_ == kStopped)
277 << "Receive suspend in unexpected state: " << state_;
278 OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
279 return;
280 }
281 DCHECK(shared_state_.renderer);
282 DCHECK(!pending_callbacks_.get());
283
284 SetState(kSuspending);
285
286 // Freeze playback and record the media time before flushing. (Flushing clears
287 // the value.)
288 shared_state_.renderer->SetPlaybackRate(0.0);
289 {
290 base::AutoLock auto_lock(shared_state_lock_);
291 shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime();
292 DCHECK(shared_state_.suspend_timestamp != kNoTimestamp());
293 }
294
295 // Queue the asynchronous actions required to stop playback. (Matches setup in
296 // DoSeek().)
297 // TODO(sandersd): Share implementation with DoSeek().
298 SerialRunner::Queue fns;
299
300 if (text_renderer_) {
301 fns.Push(base::Bind(&TextRenderer::Pause,
302 base::Unretained(text_renderer_.get())));
303 }
304
305 fns.Push(base::Bind(&Renderer::Flush,
306 base::Unretained(shared_state_.renderer.get())));
307
308 if (text_renderer_) {
309 fns.Push(base::Bind(&TextRenderer::Flush,
310 base::Unretained(text_renderer_.get())));
311 }
312
313 pending_callbacks_ = SerialRunner::Run(
314 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
315 }
316
317 void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer,
318 base::TimeDelta timestamp) {
319 DCHECK(media_task_runner_->BelongsToCurrentThread());
320
321 // Suppress resuming if we're not suspended.
322 if (state_ != kSuspended) {
323 DCHECK(state_ == kStopping || state_ == kStopped)
324 << "Receive resume in unexpected state: " << state_;
325 OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
326 return;
327 }
328 DCHECK(!shared_state_.renderer);
329 DCHECK(!pending_callbacks_.get());
330
331 SetState(kResuming);
332
333 {
334 base::AutoLock auto_lock(shared_state_lock_);
335 shared_state_.renderer = std::move(renderer);
336 }
337
338 // Set up for a seek. (Matches setup in SeekTask().)
339 // TODO(sandersd): Share implementation with SeekTask().
340 renderer_ended_ = false;
341 text_renderer_ended_ = false;
342 start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime());
343
344 // Queue the asynchronous actions required to start playback. Unlike DoSeek(),
345 // we need to initialize the renderer ourselves (we don't want to enter state
346 // kInitDemuxer, and even if we did the current code would seek to the start
347 // instead of |timestamp|).
348 SerialRunner::Queue fns;
349
350 fns.Push(
351 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_));
352
353 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_));
354
355 pending_callbacks_ = SerialRunner::Run(
356 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
357 }
358
359 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) {
360 DCHECK(media_task_runner_->BelongsToCurrentThread());
361
362 playback_rate_ = playback_rate;
363 if (state_ == kPlaying)
364 shared_state_.renderer->SetPlaybackRate(playback_rate_);
365 }
366
367 void PipelineImpl::RendererWrapper::SetVolume(float volume) {
368 DCHECK(media_task_runner_->BelongsToCurrentThread());
369
370 volume_ = volume;
371 if (state_ == kPlaying)
372 shared_state_.renderer->SetVolume(volume_);
373 }
374
375 base::TimeDelta PipelineImpl::RendererWrapper::GetMediaTime() const {
376 DCHECK(main_task_runner_->BelongsToCurrentThread());
377
378 base::AutoLock auto_lock(shared_state_lock_);
379 if (shared_state_.suspend_timestamp != kNoTimestamp())
380 return shared_state_.suspend_timestamp;
381 return shared_state_.renderer ? shared_state_.renderer->GetMediaTime()
382 : base::TimeDelta();
383 }
384
385 Ranges<base::TimeDelta> PipelineImpl::RendererWrapper::GetBufferedTimeRanges()
386 const {
387 DCHECK(main_task_runner_->BelongsToCurrentThread());
388
389 base::AutoLock auto_lock(shared_state_lock_);
390 return shared_state_.buffered_time_ranges;
391 }
392
393 bool PipelineImpl::RendererWrapper::DidLoadingProgress() {
394 DCHECK(main_task_runner_->BelongsToCurrentThread());
395
396 base::AutoLock auto_lock(shared_state_lock_);
397 bool did_progress = shared_state_.did_loading_progress;
398 shared_state_.did_loading_progress = false;
399 return did_progress;
400 }
401
402 PipelineStatistics PipelineImpl::RendererWrapper::GetStatistics() const {
403 DCHECK(main_task_runner_->BelongsToCurrentThread());
404
405 base::AutoLock auto_lock(shared_state_lock_);
406 return shared_state_.statistics;
407 }
408
409 void PipelineImpl::RendererWrapper::SetCdm(
410 CdmContext* cdm_context,
411 const CdmAttachedCB& cdm_attached_cb) {
412 DCHECK(media_task_runner_->BelongsToCurrentThread());
413
414 if (!shared_state_.renderer) {
415 cdm_context_ = cdm_context;
416 cdm_attached_cb.Run(true);
417 return;
418 }
419
420 shared_state_.renderer->SetCdm(
421 cdm_context, base::Bind(&RendererWrapper::OnCdmAttached, weak_this_,
422 cdm_attached_cb, cdm_context));
423 }
424
425 void PipelineImpl::RendererWrapper::OnBufferedTimeRangesChanged(
426 const Ranges<base::TimeDelta>& ranges) {
427 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer
428 // implementations call DemuxerHost on the media thread.
429 base::AutoLock auto_lock(shared_state_lock_);
430 shared_state_.did_loading_progress = true;
431 shared_state_.buffered_time_ranges = ranges;
432 }
433
434 void PipelineImpl::RendererWrapper::SetDuration(base::TimeDelta duration) {
435 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer
436 // implementations call DemuxerHost on the media thread.
437 media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET,
438 "duration", duration));
439 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
440
441 main_task_runner_->PostTask(
220 FROM_HERE, 442 FROM_HERE,
221 base::Bind(&RendererWrapper::Start, 443 base::Bind(&PipelineImpl::OnDurationChange, weak_pipeline_, duration));
222 base::Unretained(renderer_wrapper_.get()), demuxer,
223 base::Passed(&renderer), base::Passed(&text_renderer),
224 weak_factory_.GetWeakPtr()));
225 }
226
227 void PipelineImpl::Stop() {
228 DVLOG(2) << __FUNCTION__;
229 DCHECK(thread_checker_.CalledOnValidThread());
230
231 if (!IsRunning()) {
232 DVLOG(2) << "Media pipeline isn't running. Ignoring Stop()";
233 return;
234 }
235
236 if (media_task_runner_->BelongsToCurrentThread()) {
237 // This path is executed by unittests that share media and main threads.
238 base::Closure stop_cb = base::Bind(&base::DoNothing);
239 media_task_runner_->PostTask(
240 FROM_HERE,
241 base::Bind(&RendererWrapper::Stop,
242 base::Unretained(renderer_wrapper_.get()), stop_cb));
243 } else {
244 // This path is executed by production code where the two task runners -
245 // main and media - live on different threads.
246 //
247 // TODO(alokp): We should not have to wait for the RendererWrapper::Stop.
248 // RendererWrapper holds a raw reference to Demuxer, which in turn holds a
249 // raw reference to DataSource. Both Demuxer and DataSource need to live
250 // until RendererWrapper is stopped. If RendererWrapper owned Demuxer and
251 // Demuxer owned DataSource, we could simply let RendererWrapper get lazily
252 // destroyed on the media thread.
253 base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::AUTOMATIC,
254 base::WaitableEvent::InitialState::NOT_SIGNALED);
255 base::Closure stop_cb =
256 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter));
257 // If posting the task fails or the posted task fails to run,
258 // we will wait here forever. So add a CHECK to make sure we do not run
259 // into those situations.
260 CHECK(media_task_runner_->PostTask(
261 FROM_HERE,
262 base::Bind(&RendererWrapper::Stop,
263 base::Unretained(renderer_wrapper_.get()), stop_cb)));
264 waiter.Wait();
265 }
266
267 // Once the pipeline is stopped, nothing is reported back to the client.
268 // Reset all callbacks and client handle.
269 seek_cb_.Reset();
270 suspend_cb_.Reset();
271 client_ = nullptr;
272
273 // Invalidate self weak pointers effectively canceling all pending
274 // notifications in the message queue.
275 weak_factory_.InvalidateWeakPtrs();
276 }
277
278 void PipelineImpl::Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) {
279 DVLOG(2) << __FUNCTION__;
280 DCHECK(thread_checker_.CalledOnValidThread());
281 DCHECK(!seek_cb.is_null());
282
283 if (!IsRunning()) {
284 DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek().";
285 return;
286 }
287
288 DCHECK(seek_cb_.is_null());
289 seek_cb_ = seek_cb;
290 media_task_runner_->PostTask(
291 FROM_HERE, base::Bind(&RendererWrapper::Seek,
292 base::Unretained(renderer_wrapper_.get()), time));
293 }
294
295 bool PipelineImpl::IsRunning() const {
296 DCHECK(thread_checker_.CalledOnValidThread());
297 return !!client_;
298 }
299
300 double PipelineImpl::GetPlaybackRate() const {
301 DCHECK(thread_checker_.CalledOnValidThread());
302 return playback_rate_;
303 }
304
305 void PipelineImpl::SetPlaybackRate(double playback_rate) {
306 DVLOG(2) << __FUNCTION__ << "(" << playback_rate << ")";
307 DCHECK(thread_checker_.CalledOnValidThread());
308
309 if (playback_rate < 0.0)
310 return;
311
312 playback_rate_ = playback_rate;
313 media_task_runner_->PostTask(
314 FROM_HERE,
315 base::Bind(&RendererWrapper::SetPlaybackRate,
316 base::Unretained(renderer_wrapper_.get()), playback_rate_));
317 }
318
319 void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) {
320 DVLOG(2) << __FUNCTION__;
321 DCHECK(!suspend_cb.is_null());
322
323 DCHECK(IsRunning());
324 DCHECK(suspend_cb_.is_null());
325 suspend_cb_ = suspend_cb;
326
327 media_task_runner_->PostTask(
328 FROM_HERE, base::Bind(&RendererWrapper::Suspend,
329 base::Unretained(renderer_wrapper_.get())));
330 }
331
332 void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer,
333 base::TimeDelta time,
334 const PipelineStatusCB& seek_cb) {
335 DVLOG(2) << __FUNCTION__;
336 DCHECK(thread_checker_.CalledOnValidThread());
337 DCHECK(renderer);
338 DCHECK(!seek_cb.is_null());
339
340 DCHECK(IsRunning());
341 DCHECK(seek_cb_.is_null());
342 seek_cb_ = seek_cb;
343
344 media_task_runner_->PostTask(
345 FROM_HERE, base::Bind(&RendererWrapper::Resume,
346 base::Unretained(renderer_wrapper_.get()),
347 base::Passed(&renderer), time));
348 }
349
350 float PipelineImpl::GetVolume() const {
351 DCHECK(thread_checker_.CalledOnValidThread());
352 return volume_;
353 }
354
355 void PipelineImpl::SetVolume(float volume) {
356 DVLOG(2) << __FUNCTION__ << "(" << volume << ")";
357 DCHECK(thread_checker_.CalledOnValidThread());
358
359 if (volume < 0.0f || volume > 1.0f)
360 return;
361
362 volume_ = volume;
363 media_task_runner_->PostTask(
364 FROM_HERE,
365 base::Bind(&RendererWrapper::SetVolume,
366 base::Unretained(renderer_wrapper_.get()), volume_));
367 }
368
369 base::TimeDelta PipelineImpl::GetMediaTime() const {
370 DCHECK(thread_checker_.CalledOnValidThread());
371 return renderer_wrapper_->GetMediaTime();
372 }
373
374 Ranges<base::TimeDelta> PipelineImpl::GetBufferedTimeRanges() const {
375 DCHECK(thread_checker_.CalledOnValidThread());
376 return renderer_wrapper_->GetBufferedTimeRanges();
377 }
378
379 base::TimeDelta PipelineImpl::GetMediaDuration() const {
380 DCHECK(thread_checker_.CalledOnValidThread());
381 return duration_;
382 }
383
384 bool PipelineImpl::DidLoadingProgress() {
385 DCHECK(thread_checker_.CalledOnValidThread());
386 return renderer_wrapper_->DidLoadingProgress();
387 }
388
389 PipelineStatistics PipelineImpl::GetStatistics() const {
390 DCHECK(thread_checker_.CalledOnValidThread());
391 return renderer_wrapper_->GetStatistics();
392 }
393
394 void PipelineImpl::SetCdm(CdmContext* cdm_context,
395 const CdmAttachedCB& cdm_attached_cb) {
396 DVLOG(2) << __FUNCTION__;
397 DCHECK(thread_checker_.CalledOnValidThread());
398 DCHECK(cdm_context);
399 DCHECK(!cdm_attached_cb.is_null());
400
401 media_task_runner_->PostTask(
402 FROM_HERE,
403 base::Bind(&RendererWrapper::SetCdm,
404 base::Unretained(renderer_wrapper_.get()), cdm_context,
405 media::BindToCurrentLoop(cdm_attached_cb)));
406 }
407
408 void PipelineImpl::RendererWrapper::SetState(State next_state) {
409 DCHECK(media_task_runner_->BelongsToCurrentThread());
410 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> "
411 << PipelineImpl::GetStateString(next_state);
412
413 state_ = next_state;
414 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
415 }
416
417 #define RETURN_STRING(state) \
418 case state: \
419 return #state;
420
421 // static
422 const char* PipelineImpl::GetStateString(State state) {
423 switch (state) {
424 RETURN_STRING(kCreated);
425 RETURN_STRING(kInitDemuxer);
426 RETURN_STRING(kInitRenderer);
427 RETURN_STRING(kSeeking);
428 RETURN_STRING(kPlaying);
429 RETURN_STRING(kStopping);
430 RETURN_STRING(kStopped);
431 RETURN_STRING(kSuspending);
432 RETURN_STRING(kSuspended);
433 RETURN_STRING(kResuming);
434 }
435 NOTREACHED();
436 return "INVALID";
437 }
438
439 #undef RETURN_STRING
440
441 PipelineImpl::State PipelineImpl::RendererWrapper::GetNextState() const {
442 DCHECK(media_task_runner_->BelongsToCurrentThread());
443 DCHECK_EQ(status_, PIPELINE_OK)
444 << "State transitions don't happen when there's an error: " << status_;
445
446 switch (state_) {
447 case kCreated:
448 return kInitDemuxer;
449
450 case kInitDemuxer:
451 return kInitRenderer;
452
453 case kInitRenderer:
454 case kSeeking:
455 return kPlaying;
456
457 case kSuspending:
458 return kSuspended;
459
460 case kSuspended:
461 return kResuming;
462
463 case kResuming:
464 return kPlaying;
465
466 case kPlaying:
467 case kStopping:
468 case kStopped:
469 break;
470 }
471 NOTREACHED() << "State has no transition: " << state_;
472 return state_;
473 } 444 }
474 445
475 void PipelineImpl::RendererWrapper::OnDemuxerError(PipelineStatus error) { 446 void PipelineImpl::RendererWrapper::OnDemuxerError(PipelineStatus error) {
476 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer 447 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer
477 // implementations call DemuxerHost on the media thread. 448 // implementations call DemuxerHost on the media thread.
478 media_task_runner_->PostTask( 449 media_task_runner_->PostTask(
479 FROM_HERE, 450 FROM_HERE,
480 base::Bind(&RendererWrapper::OnPipelineError, weak_this_, error)); 451 base::Bind(&RendererWrapper::OnPipelineError, weak_this_, error));
481 } 452 }
482 453
(...skipping 26 matching lines...) Expand all
509 480
510 void PipelineImpl::RendererWrapper::OnEnded() { 481 void PipelineImpl::RendererWrapper::OnEnded() {
511 DCHECK(media_task_runner_->BelongsToCurrentThread()); 482 DCHECK(media_task_runner_->BelongsToCurrentThread());
512 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); 483 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED));
513 484
514 if (state_ != kPlaying) 485 if (state_ != kPlaying)
515 return; 486 return;
516 487
517 DCHECK(!renderer_ended_); 488 DCHECK(!renderer_ended_);
518 renderer_ended_ = true; 489 renderer_ended_ = true;
519 RunEndedCallbackIfNeeded(); 490 CheckPlaybackEnded();
520 } 491 }
521 492
522 void PipelineImpl::RendererWrapper::OnStatisticsUpdate( 493 void PipelineImpl::RendererWrapper::OnStatisticsUpdate(
523 const PipelineStatistics& stats) { 494 const PipelineStatistics& stats) {
524 DVLOG(3) << __FUNCTION__; 495 DVLOG(3) << __FUNCTION__;
525 DCHECK(media_task_runner_->BelongsToCurrentThread()); 496 DCHECK(media_task_runner_->BelongsToCurrentThread());
526 497
527 base::AutoLock auto_lock(shared_state_lock_); 498 base::AutoLock auto_lock(shared_state_lock_);
528 shared_state_.statistics.audio_bytes_decoded += stats.audio_bytes_decoded; 499 shared_state_.statistics.audio_bytes_decoded += stats.audio_bytes_decoded;
529 shared_state_.statistics.video_bytes_decoded += stats.video_bytes_decoded; 500 shared_state_.statistics.video_bytes_decoded += stats.video_bytes_decoded;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
561 } 532 }
562 533
563 void PipelineImpl::RendererWrapper::OnVideoOpacityChange(bool opaque) { 534 void PipelineImpl::RendererWrapper::OnVideoOpacityChange(bool opaque) {
564 DCHECK(media_task_runner_->BelongsToCurrentThread()); 535 DCHECK(media_task_runner_->BelongsToCurrentThread());
565 536
566 main_task_runner_->PostTask( 537 main_task_runner_->PostTask(
567 FROM_HERE, 538 FROM_HERE,
568 base::Bind(&PipelineImpl::OnVideoOpacityChange, weak_pipeline_, opaque)); 539 base::Bind(&PipelineImpl::OnVideoOpacityChange, weak_pipeline_, opaque));
569 } 540 }
570 541
571 void PipelineImpl::RendererWrapper::SetDuration(base::TimeDelta duration) { 542 void PipelineImpl::RendererWrapper::OnTextRendererEnded() {
572 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer 543 DCHECK(media_task_runner_->BelongsToCurrentThread());
573 // implementations call DemuxerHost on the media thread. 544 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
574 media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET,
575 "duration", duration));
576 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
577 545
578 main_task_runner_->PostTask( 546 if (state_ != kPlaying)
579 FROM_HERE, 547 return;
580 base::Bind(&PipelineImpl::OnDurationChange, weak_pipeline_, duration)); 548
549 DCHECK(!text_renderer_ended_);
550 text_renderer_ended_ = true;
551 CheckPlaybackEnded();
581 } 552 }
582 553
583 void PipelineImpl::RendererWrapper::StateTransitionTask(PipelineStatus status) { 554 void PipelineImpl::RendererWrapper::AddTextStreamTask(
555 DemuxerStream* text_stream,
556 const TextTrackConfig& config) {
584 DCHECK(media_task_runner_->BelongsToCurrentThread()); 557 DCHECK(media_task_runner_->BelongsToCurrentThread());
585 558
586 // No-op any state transitions if we're stopping or already encountered error. 559 // TODO(matthewjheaney): fix up text_ended_ when text stream
587 if (state_ == kStopping || state_ == kStopped || status_ != PIPELINE_OK) 560 // is added (http://crbug.com/321446).
561 if (text_renderer_)
562 text_renderer_->AddTextStream(text_stream, config);
563 }
564
565 void PipelineImpl::RendererWrapper::RemoveTextStreamTask(
566 DemuxerStream* text_stream) {
567 DCHECK(media_task_runner_->BelongsToCurrentThread());
568
569 if (text_renderer_)
570 text_renderer_->RemoveTextStream(text_stream);
571 }
572
573 void PipelineImpl::RendererWrapper::OnPipelineError(PipelineStatus error) {
574 DCHECK(media_task_runner_->BelongsToCurrentThread());
575 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
576
577 // Preserve existing abnormal status.
578 if (status_ != PIPELINE_OK)
588 return; 579 return;
589 580
590 // Report error from the previous operation. 581 // Don't report pipeline error events to the media log here. The embedder
591 if (status != PIPELINE_OK) { 582 // will log this when Client::OnError is called. If the pipeline is already
592 OnPipelineError(status); 583 // stopped or stopping we also don't want to log any event. In case we are
584 // suspending or suspended, the error may be recoverable, so don't propagate
585 // it now, instead let the subsequent seek during resume propagate it if
586 // it's unrecoverable.
587 if (state_ == kStopping || state_ == kStopped || state_ == kSuspending ||
588 state_ == kSuspended) {
593 return; 589 return;
594 } 590 }
595 591
596 // Guard against accidentally clearing |pending_callbacks_| for states that 592 status_ = error;
597 // use it as well as states that should not be using it. 593 main_task_runner_->PostTask(
598 DCHECK_EQ(pending_callbacks_.get() != NULL, 594 FROM_HERE, base::Bind(&PipelineImpl::OnError, weak_pipeline_, error));
599 state_ == kSeeking || state_ == kSuspending || state_ == kResuming); 595 }
600 596
601 pending_callbacks_.reset(); 597 void PipelineImpl::RendererWrapper::OnCdmAttached(
598 const CdmAttachedCB& cdm_attached_cb,
599 CdmContext* cdm_context,
600 bool success) {
601 DCHECK(media_task_runner_->BelongsToCurrentThread());
602 602
603 PipelineStatusCB done_cb = 603 if (success)
604 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_); 604 cdm_context_ = cdm_context;
605 cdm_attached_cb.Run(success);
606 }
605 607
606 // Switch states, performing any entrance actions for the new state as well. 608 void PipelineImpl::RendererWrapper::CheckPlaybackEnded() {
607 SetState(GetNextState()); 609 DCHECK(media_task_runner_->BelongsToCurrentThread());
608 switch (state_) {
609 case kInitDemuxer:
610 return InitializeDemuxer(done_cb);
611 610
612 case kInitRenderer: 611 if (shared_state_.renderer && !renderer_ended_)
613 // When the state_ transfers to kInitRenderer, it means the demuxer has 612 return;
614 // finished parsing the init info. It should call ReportMetadata in case
615 // meeting 'decode' error when passing media segment but WebMediaPlayer's
616 // ready_state_ is still ReadyStateHaveNothing. In that case, it will
617 // treat it as NetworkStateFormatError not NetworkStateDecodeError.
618 ReportMetadata();
619 start_timestamp_ = demuxer_->GetStartTime();
620 613
621 return InitializeRenderer(done_cb); 614 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_)
615 return;
622 616
623 case kPlaying: 617 DCHECK_EQ(status_, PIPELINE_OK);
624 DCHECK(start_timestamp_ >= base::TimeDelta()); 618 main_task_runner_->PostTask(
625 shared_state_.renderer->StartPlayingFrom(start_timestamp_); 619 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_));
626 {
627 base::AutoLock auto_lock(shared_state_lock_);
628 shared_state_.suspend_timestamp = kNoTimestamp();
629 }
630
631 if (text_renderer_)
632 text_renderer_->StartPlaying();
633
634 main_task_runner_->PostTask(
635 FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_,
636 start_timestamp_));
637
638 shared_state_.renderer->SetPlaybackRate(playback_rate_);
639 shared_state_.renderer->SetVolume(volume_);
640 return;
641
642 case kSuspended:
643 DestroyRenderer();
644 {
645 base::AutoLock auto_lock(shared_state_lock_);
646 shared_state_.statistics.audio_memory_usage = 0;
647 shared_state_.statistics.video_memory_usage = 0;
648 }
649 main_task_runner_->PostTask(
650 FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_,
651 shared_state_.suspend_timestamp));
652 return;
653
654 case kStopping:
655 case kStopped:
656 case kCreated:
657 case kSeeking:
658 case kSuspending:
659 case kResuming:
660 NOTREACHED() << "State has no transition: " << state_;
661 return;
662 }
663 } 620 }
664 621
665 // Note that the usage of base::Unretained() with the renderers is considered 622 // Note that the usage of base::Unretained() with the renderers is considered
666 // safe as they are owned by |pending_callbacks_| and share the same lifetime. 623 // safe as they are owned by |pending_callbacks_| and share the same lifetime.
667 // 624 //
668 // That being said, deleting the renderers while keeping |pending_callbacks_| 625 // That being said, deleting the renderers while keeping |pending_callbacks_|
669 // running on the media thread would result in crashes. 626 // running on the media thread would result in crashes.
670 void PipelineImpl::RendererWrapper::DoSeek(base::TimeDelta seek_timestamp, 627 void PipelineImpl::RendererWrapper::DoSeek(base::TimeDelta seek_timestamp,
671 const PipelineStatusCB& done_cb) { 628 const PipelineStatusCB& done_cb) {
672 DCHECK(media_task_runner_->BelongsToCurrentThread()); 629 DCHECK(media_task_runner_->BelongsToCurrentThread());
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
715 672
716 // Post the stop callback to enqueue it after the tasks that may have been 673 // Post the stop callback to enqueue it after the tasks that may have been
717 // posted by Demuxer and Renderer during stopping. Note that in theory the 674 // posted by Demuxer and Renderer during stopping. Note that in theory the
718 // tasks posted by Demuxer/Renderer may post even more tasks that will get 675 // tasks posted by Demuxer/Renderer may post even more tasks that will get
719 // enqueued after |done_cb|. This may be problematic because Demuxer may 676 // enqueued after |done_cb|. This may be problematic because Demuxer may
720 // get destroyed as soon as |done_cb| is run. In practice this is not a 677 // get destroyed as soon as |done_cb| is run. In practice this is not a
721 // problem, but ideally Demuxer should be destroyed on the media thread. 678 // problem, but ideally Demuxer should be destroyed on the media thread.
722 media_task_runner_->PostTask(FROM_HERE, done_cb); 679 media_task_runner_->PostTask(FROM_HERE, done_cb);
723 } 680 }
724 681
725 void PipelineImpl::RendererWrapper::OnBufferedTimeRangesChanged( 682 void PipelineImpl::RendererWrapper::SetState(State next_state) {
726 const Ranges<base::TimeDelta>& ranges) { 683 DCHECK(media_task_runner_->BelongsToCurrentThread());
727 // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer 684 DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> "
728 // implementations call DemuxerHost on the media thread. 685 << PipelineImpl::GetStateString(next_state);
729 base::AutoLock auto_lock(shared_state_lock_); 686
730 shared_state_.did_loading_progress = true; 687 state_ = next_state;
731 shared_state_.buffered_time_ranges = ranges; 688 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
732 } 689 }
733 690
734 void PipelineImpl::RendererWrapper::Start( 691 PipelineImpl::State PipelineImpl::RendererWrapper::GetNextState() const {
735 Demuxer* demuxer, 692 DCHECK(media_task_runner_->BelongsToCurrentThread());
736 std::unique_ptr<Renderer> renderer, 693 DCHECK_EQ(status_, PIPELINE_OK)
737 std::unique_ptr<TextRenderer> text_renderer, 694 << "State transitions don't happen when there's an error: " << status_;
738 base::WeakPtr<PipelineImpl> weak_pipeline) { 695
739 DCHECK(media_task_runner_->BelongsToCurrentThread()); 696 switch (state_) {
740 DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " 697 case kCreated:
741 << state_; 698 return kInitDemuxer;
742 699
743 DCHECK(!demuxer_); 700 case kInitDemuxer:
744 DCHECK(!shared_state_.renderer); 701 return kInitRenderer;
745 DCHECK(!text_renderer_); 702
746 DCHECK(!renderer_ended_); 703 case kInitRenderer:
747 DCHECK(!text_renderer_ended_); 704 case kSeeking:
748 DCHECK(!weak_pipeline_); 705 return kPlaying;
749 demuxer_ = demuxer; 706
750 { 707 case kSuspending:
751 base::AutoLock auto_lock(shared_state_lock_); 708 return kSuspended;
752 shared_state_.renderer = std::move(renderer); 709
753 } 710 case kSuspended:
754 text_renderer_ = std::move(text_renderer); 711 return kResuming;
755 if (text_renderer_) { 712
756 text_renderer_->Initialize( 713 case kResuming:
757 base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_)); 714 return kPlaying;
758 } 715
759 weak_pipeline_ = weak_pipeline; 716 case kPlaying:
760 717 case kStopping:
761 StateTransitionTask(PIPELINE_OK); 718 case kStopped:
762 } 719 break;
763 720 }
764 void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { 721 NOTREACHED() << "State has no transition: " << state_;
765 DCHECK(media_task_runner_->BelongsToCurrentThread()); 722 return state_;
766 DCHECK(state_ != kStopping && state_ != kStopped); 723 }
767 724 void PipelineImpl::RendererWrapper::StateTransitionTask(PipelineStatus status) {
768 SetState(kStopping); 725 DCHECK(media_task_runner_->BelongsToCurrentThread());
769 726
770 if (shared_state_.statistics.video_frames_decoded > 0) { 727 // No-op any state transitions if we're stopping or already encountered error.
771 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", 728 if (state_ == kStopping || state_ == kStopped || status_ != PIPELINE_OK)
772 shared_state_.statistics.video_frames_dropped); 729 return;
773 } 730
774 731 // Report error from the previous operation.
775 // If we stop during starting/seeking/suspending/resuming we don't want to 732 if (status != PIPELINE_OK) {
776 // leave outstanding callbacks around. The callbacks also do not get run if 733 OnPipelineError(status);
777 // the pipeline is stopped before it had a chance to complete outstanding 734 return;
778 // tasks. 735 }
736
737 // Guard against accidentally clearing |pending_callbacks_| for states that
738 // use it as well as states that should not be using it.
739 DCHECK_EQ(pending_callbacks_.get() != NULL,
740 state_ == kSeeking || state_ == kSuspending || state_ == kResuming);
741
779 pending_callbacks_.reset(); 742 pending_callbacks_.reset();
780 743
781 DoStop(stop_cb); 744 PipelineStatusCB done_cb =
782 } 745 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_);
783 746
784 void PipelineImpl::RendererWrapper::OnPipelineError(PipelineStatus error) { 747 // Switch states, performing any entrance actions for the new state as well.
785 DCHECK(media_task_runner_->BelongsToCurrentThread()); 748 SetState(GetNextState());
786 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; 749 switch (state_) {
787 750 case kInitDemuxer:
788 // Preserve existing abnormal status. 751 return InitializeDemuxer(done_cb);
789 if (status_ != PIPELINE_OK) 752
790 return; 753 case kInitRenderer:
791 754 // When the state_ transfers to kInitRenderer, it means the demuxer has
792 // Don't report pipeline error events to the media log here. The embedder 755 // finished parsing the init info. It should call ReportMetadata in case
793 // will log this when Client::OnError is called. If the pipeline is already 756 // meeting 'decode' error when passing media segment but WebMediaPlayer's
794 // stopped or stopping we also don't want to log any event. In case we are 757 // ready_state_ is still ReadyStateHaveNothing. In that case, it will
795 // suspending or suspended, the error may be recoverable, so don't propagate 758 // treat it as NetworkStateFormatError not NetworkStateDecodeError.
796 // it now, instead let the subsequent seek during resume propagate it if 759 ReportMetadata();
797 // it's unrecoverable. 760 start_timestamp_ = demuxer_->GetStartTime();
798 if (state_ == kStopping || state_ == kStopped || state_ == kSuspending || 761
799 state_ == kSuspended) { 762 return InitializeRenderer(done_cb);
800 return; 763
801 } 764 case kPlaying:
802 765 DCHECK(start_timestamp_ >= base::TimeDelta());
803 status_ = error; 766 shared_state_.renderer->StartPlayingFrom(start_timestamp_);
804 main_task_runner_->PostTask( 767 {
805 FROM_HERE, base::Bind(&PipelineImpl::OnError, weak_pipeline_, error)); 768 base::AutoLock auto_lock(shared_state_lock_);
806 } 769 shared_state_.suspend_timestamp = kNoTimestamp();
807 770 }
808 void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { 771
809 DCHECK(media_task_runner_->BelongsToCurrentThread()); 772 if (text_renderer_)
810 773 text_renderer_->StartPlaying();
811 playback_rate_ = playback_rate; 774
812 if (state_ == kPlaying) 775 main_task_runner_->PostTask(
813 shared_state_.renderer->SetPlaybackRate(playback_rate_); 776 FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_,
814 } 777 start_timestamp_));
815 778
816 void PipelineImpl::RendererWrapper::SetVolume(float volume) { 779 shared_state_.renderer->SetPlaybackRate(playback_rate_);
817 DCHECK(media_task_runner_->BelongsToCurrentThread()); 780 shared_state_.renderer->SetVolume(volume_);
818 781 return;
819 volume_ = volume; 782
820 if (state_ == kPlaying) 783 case kSuspended:
821 shared_state_.renderer->SetVolume(volume_); 784 DestroyRenderer();
822 } 785 {
823 786 base::AutoLock auto_lock(shared_state_lock_);
824 void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { 787 shared_state_.statistics.audio_memory_usage = 0;
825 DCHECK(media_task_runner_->BelongsToCurrentThread()); 788 shared_state_.statistics.video_memory_usage = 0;
826 789 }
827 // Suppress seeking if we're not fully started. 790 main_task_runner_->PostTask(
828 if (state_ != kPlaying) { 791 FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_,
829 DCHECK(state_ == kStopping || state_ == kStopped) 792 shared_state_.suspend_timestamp));
830 << "Receive seek in unexpected state: " << state_; 793 return;
831 OnPipelineError(PIPELINE_ERROR_INVALID_STATE); 794
832 return; 795 case kStopping:
833 } 796 case kStopped:
834 797 case kCreated:
835 const base::TimeDelta seek_timestamp = 798 case kSeeking:
836 std::max(time, demuxer_->GetStartTime()); 799 case kSuspending:
837 800 case kResuming:
838 SetState(kSeeking); 801 NOTREACHED() << "State has no transition: " << state_;
839 renderer_ended_ = false; 802 return;
840 text_renderer_ended_ = false; 803 }
841 start_timestamp_ = seek_timestamp;
842
843 DoSeek(seek_timestamp,
844 base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
845 }
846
847 void PipelineImpl::RendererWrapper::Suspend() {
848 DCHECK(media_task_runner_->BelongsToCurrentThread());
849
850 // Suppress suspending if we're not playing.
851 if (state_ != kPlaying) {
852 DCHECK(state_ == kStopping || state_ == kStopped)
853 << "Receive suspend in unexpected state: " << state_;
854 OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
855 return;
856 }
857 DCHECK(shared_state_.renderer);
858 DCHECK(!pending_callbacks_.get());
859
860 SetState(kSuspending);
861
862 // Freeze playback and record the media time before flushing. (Flushing clears
863 // the value.)
864 shared_state_.renderer->SetPlaybackRate(0.0);
865 {
866 base::AutoLock auto_lock(shared_state_lock_);
867 shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime();
868 DCHECK(shared_state_.suspend_timestamp != kNoTimestamp());
869 }
870
871 // Queue the asynchronous actions required to stop playback. (Matches setup in
872 // DoSeek().)
873 // TODO(sandersd): Share implementation with DoSeek().
874 SerialRunner::Queue fns;
875
876 if (text_renderer_) {
877 fns.Push(base::Bind(&TextRenderer::Pause,
878 base::Unretained(text_renderer_.get())));
879 }
880
881 fns.Push(base::Bind(&Renderer::Flush,
882 base::Unretained(shared_state_.renderer.get())));
883
884 if (text_renderer_) {
885 fns.Push(base::Bind(&TextRenderer::Flush,
886 base::Unretained(text_renderer_.get())));
887 }
888
889 pending_callbacks_ = SerialRunner::Run(
890 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
891 }
892
893 void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer,
894 base::TimeDelta timestamp) {
895 DCHECK(media_task_runner_->BelongsToCurrentThread());
896
897 // Suppress resuming if we're not suspended.
898 if (state_ != kSuspended) {
899 DCHECK(state_ == kStopping || state_ == kStopped)
900 << "Receive resume in unexpected state: " << state_;
901 OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
902 return;
903 }
904 DCHECK(!shared_state_.renderer);
905 DCHECK(!pending_callbacks_.get());
906
907 SetState(kResuming);
908
909 {
910 base::AutoLock auto_lock(shared_state_lock_);
911 shared_state_.renderer = std::move(renderer);
912 }
913
914 // Set up for a seek. (Matches setup in SeekTask().)
915 // TODO(sandersd): Share implementation with SeekTask().
916 renderer_ended_ = false;
917 text_renderer_ended_ = false;
918 start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime());
919
920 // Queue the asynchronous actions required to start playback. Unlike DoSeek(),
921 // we need to initialize the renderer ourselves (we don't want to enter state
922 // kInitDemuxer, and even if we did the current code would seek to the start
923 // instead of |timestamp|).
924 SerialRunner::Queue fns;
925
926 fns.Push(
927 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_));
928
929 fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_));
930
931 pending_callbacks_ = SerialRunner::Run(
932 fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
933 }
934
935 Ranges<base::TimeDelta> PipelineImpl::RendererWrapper::GetBufferedTimeRanges()
936 const {
937 DCHECK(main_task_runner_->BelongsToCurrentThread());
938
939 base::AutoLock auto_lock(shared_state_lock_);
940 return shared_state_.buffered_time_ranges;
941 }
942
943 bool PipelineImpl::RendererWrapper::DidLoadingProgress() {
944 DCHECK(main_task_runner_->BelongsToCurrentThread());
945
946 base::AutoLock auto_lock(shared_state_lock_);
947 bool did_progress = shared_state_.did_loading_progress;
948 shared_state_.did_loading_progress = false;
949 return did_progress;
950 }
951
952 PipelineStatistics PipelineImpl::RendererWrapper::GetStatistics() const {
953 DCHECK(main_task_runner_->BelongsToCurrentThread());
954
955 base::AutoLock auto_lock(shared_state_lock_);
956 return shared_state_.statistics;
957 }
958
959 void PipelineImpl::RendererWrapper::SetCdm(
960 CdmContext* cdm_context,
961 const CdmAttachedCB& cdm_attached_cb) {
962 DCHECK(media_task_runner_->BelongsToCurrentThread());
963
964 if (!shared_state_.renderer) {
965 cdm_context_ = cdm_context;
966 cdm_attached_cb.Run(true);
967 return;
968 }
969
970 shared_state_.renderer->SetCdm(
971 cdm_context, base::Bind(&RendererWrapper::OnCdmAttached, weak_this_,
972 cdm_attached_cb, cdm_context));
973 }
974
975 void PipelineImpl::RendererWrapper::OnCdmAttached(
976 const CdmAttachedCB& cdm_attached_cb,
977 CdmContext* cdm_context,
978 bool success) {
979 DCHECK(media_task_runner_->BelongsToCurrentThread());
980
981 if (success)
982 cdm_context_ = cdm_context;
983 cdm_attached_cb.Run(success);
984 }
985
986 void PipelineImpl::RendererWrapper::OnTextRendererEnded() {
987 DCHECK(media_task_runner_->BelongsToCurrentThread());
988 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
989
990 if (state_ != kPlaying)
991 return;
992
993 DCHECK(!text_renderer_ended_);
994 text_renderer_ended_ = true;
995
996 RunEndedCallbackIfNeeded();
997 }
998
999 void PipelineImpl::RendererWrapper::RunEndedCallbackIfNeeded() {
1000 DCHECK(media_task_runner_->BelongsToCurrentThread());
1001
1002 if (shared_state_.renderer && !renderer_ended_)
1003 return;
1004
1005 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_)
1006 return;
1007
1008 DCHECK_EQ(status_, PIPELINE_OK);
1009 main_task_runner_->PostTask(
1010 FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_));
1011 }
1012
1013 void PipelineImpl::RendererWrapper::AddTextStreamTask(
1014 DemuxerStream* text_stream,
1015 const TextTrackConfig& config) {
1016 DCHECK(media_task_runner_->BelongsToCurrentThread());
1017
1018 // TODO(matthewjheaney): fix up text_ended_ when text stream
1019 // is added (http://crbug.com/321446).
1020 if (text_renderer_)
1021 text_renderer_->AddTextStream(text_stream, config);
1022 }
1023
1024 void PipelineImpl::RendererWrapper::RemoveTextStreamTask(
1025 DemuxerStream* text_stream) {
1026 DCHECK(media_task_runner_->BelongsToCurrentThread());
1027
1028 if (text_renderer_)
1029 text_renderer_->RemoveTextStream(text_stream);
1030 } 804 }
1031 805
1032 void PipelineImpl::RendererWrapper::InitializeDemuxer( 806 void PipelineImpl::RendererWrapper::InitializeDemuxer(
1033 const PipelineStatusCB& done_cb) { 807 const PipelineStatusCB& done_cb) {
1034 DCHECK(media_task_runner_->BelongsToCurrentThread()); 808 DCHECK(media_task_runner_->BelongsToCurrentThread());
1035 809
1036 demuxer_->Initialize(this, done_cb, !!text_renderer_); 810 demuxer_->Initialize(this, done_cb, !!text_renderer_);
1037 } 811 }
1038 812
1039 void PipelineImpl::RendererWrapper::InitializeRenderer( 813 void PipelineImpl::RendererWrapper::InitializeRenderer(
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
1077 metadata.video_rotation = stream->video_rotation(); 851 metadata.video_rotation = stream->video_rotation();
1078 } 852 }
1079 if (demuxer_->GetStream(DemuxerStream::AUDIO)) { 853 if (demuxer_->GetStream(DemuxerStream::AUDIO)) {
1080 metadata.has_audio = true; 854 metadata.has_audio = true;
1081 } 855 }
1082 856
1083 main_task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::OnMetadata, 857 main_task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::OnMetadata,
1084 weak_pipeline_, metadata)); 858 weak_pipeline_, metadata));
1085 } 859 }
1086 860
861 PipelineImpl::PipelineImpl(
862 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
863 MediaLog* media_log)
864 : media_task_runner_(media_task_runner),
865 media_log_(media_log),
866 client_(nullptr),
867 playback_rate_(kDefaultPlaybackRate),
868 volume_(kDefaultVolume),
869 weak_factory_(this) {
870 DVLOG(2) << __FUNCTION__;
871 renderer_wrapper_.reset(new RendererWrapper(media_task_runner_, media_log_));
872 }
873
874 PipelineImpl::~PipelineImpl() {
875 DVLOG(2) << __FUNCTION__;
876 DCHECK(thread_checker_.CalledOnValidThread());
877 DCHECK(!client_) << "Stop() must complete before destroying object";
878 DCHECK(seek_cb_.is_null());
879 DCHECK(suspend_cb_.is_null());
880 DCHECK(!weak_factory_.HasWeakPtrs());
881
882 // RendererWrapper is deleted on the media thread.
883 media_task_runner_->DeleteSoon(FROM_HERE, renderer_wrapper_.release());
884 }
885
886 void PipelineImpl::Start(Demuxer* demuxer,
887 std::unique_ptr<Renderer> renderer,
888 Client* client,
889 const PipelineStatusCB& seek_cb) {
890 DVLOG(2) << __FUNCTION__;
891 DCHECK(thread_checker_.CalledOnValidThread());
892 DCHECK(demuxer);
893 DCHECK(renderer);
894 DCHECK(client);
895 DCHECK(!seek_cb.is_null());
896
897 DCHECK(!client_);
898 DCHECK(seek_cb_.is_null());
899 client_ = client;
900 seek_cb_ = seek_cb;
901
902 std::unique_ptr<TextRenderer> text_renderer;
903 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
904 switches::kEnableInbandTextTracks)) {
905 text_renderer.reset(new TextRenderer(
906 media_task_runner_,
907 BindToCurrentLoop(base::Bind(&PipelineImpl::OnAddTextTrack,
908 weak_factory_.GetWeakPtr()))));
909 }
910
911 media_task_runner_->PostTask(
912 FROM_HERE,
913 base::Bind(&RendererWrapper::Start,
914 base::Unretained(renderer_wrapper_.get()), demuxer,
915 base::Passed(&renderer), base::Passed(&text_renderer),
916 weak_factory_.GetWeakPtr()));
917 }
918
919 void PipelineImpl::Stop() {
920 DVLOG(2) << __FUNCTION__;
921 DCHECK(thread_checker_.CalledOnValidThread());
922
923 if (!IsRunning()) {
924 DVLOG(2) << "Media pipeline isn't running. Ignoring Stop()";
925 return;
926 }
927
928 if (media_task_runner_->BelongsToCurrentThread()) {
929 // This path is executed by unittests that share media and main threads.
930 base::Closure stop_cb = base::Bind(&base::DoNothing);
931 media_task_runner_->PostTask(
932 FROM_HERE,
933 base::Bind(&RendererWrapper::Stop,
934 base::Unretained(renderer_wrapper_.get()), stop_cb));
935 } else {
936 // This path is executed by production code where the two task runners -
937 // main and media - live on different threads.
938 //
939 // TODO(alokp): We should not have to wait for the RendererWrapper::Stop.
940 // RendererWrapper holds a raw reference to Demuxer, which in turn holds a
941 // raw reference to DataSource. Both Demuxer and DataSource need to live
942 // until RendererWrapper is stopped. If RendererWrapper owned Demuxer and
943 // Demuxer owned DataSource, we could simply let RendererWrapper get lazily
944 // destroyed on the media thread.
945 base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::AUTOMATIC,
946 base::WaitableEvent::InitialState::NOT_SIGNALED);
947 base::Closure stop_cb =
948 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter));
949 // If posting the task fails or the posted task fails to run,
950 // we will wait here forever. So add a CHECK to make sure we do not run
951 // into those situations.
952 CHECK(media_task_runner_->PostTask(
953 FROM_HERE,
954 base::Bind(&RendererWrapper::Stop,
955 base::Unretained(renderer_wrapper_.get()), stop_cb)));
956 waiter.Wait();
957 }
958
959 // Once the pipeline is stopped, nothing is reported back to the client.
960 // Reset all callbacks and client handle.
961 seek_cb_.Reset();
962 suspend_cb_.Reset();
963 client_ = nullptr;
964
965 // Invalidate self weak pointers effectively canceling all pending
966 // notifications in the message queue.
967 weak_factory_.InvalidateWeakPtrs();
968 }
969
970 void PipelineImpl::Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) {
971 DVLOG(2) << __FUNCTION__;
972 DCHECK(thread_checker_.CalledOnValidThread());
973 DCHECK(!seek_cb.is_null());
974
975 if (!IsRunning()) {
976 DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek().";
977 return;
978 }
979
980 DCHECK(seek_cb_.is_null());
981 seek_cb_ = seek_cb;
982 media_task_runner_->PostTask(
983 FROM_HERE, base::Bind(&RendererWrapper::Seek,
984 base::Unretained(renderer_wrapper_.get()), time));
985 }
986
987 void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) {
988 DVLOG(2) << __FUNCTION__;
989 DCHECK(!suspend_cb.is_null());
990
991 DCHECK(IsRunning());
992 DCHECK(suspend_cb_.is_null());
993 suspend_cb_ = suspend_cb;
994
995 media_task_runner_->PostTask(
996 FROM_HERE, base::Bind(&RendererWrapper::Suspend,
997 base::Unretained(renderer_wrapper_.get())));
998 }
999
1000 void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer,
1001 base::TimeDelta time,
1002 const PipelineStatusCB& seek_cb) {
1003 DVLOG(2) << __FUNCTION__;
1004 DCHECK(thread_checker_.CalledOnValidThread());
1005 DCHECK(renderer);
1006 DCHECK(!seek_cb.is_null());
1007
1008 DCHECK(IsRunning());
1009 DCHECK(seek_cb_.is_null());
1010 seek_cb_ = seek_cb;
1011
1012 media_task_runner_->PostTask(
1013 FROM_HERE, base::Bind(&RendererWrapper::Resume,
1014 base::Unretained(renderer_wrapper_.get()),
1015 base::Passed(&renderer), time));
1016 }
1017
1018 bool PipelineImpl::IsRunning() const {
1019 DCHECK(thread_checker_.CalledOnValidThread());
1020 return !!client_;
1021 }
1022
1023 double PipelineImpl::GetPlaybackRate() const {
1024 DCHECK(thread_checker_.CalledOnValidThread());
1025 return playback_rate_;
1026 }
1027
1028 void PipelineImpl::SetPlaybackRate(double playback_rate) {
1029 DVLOG(2) << __FUNCTION__ << "(" << playback_rate << ")";
1030 DCHECK(thread_checker_.CalledOnValidThread());
1031
1032 if (playback_rate < 0.0)
1033 return;
1034
1035 playback_rate_ = playback_rate;
1036 media_task_runner_->PostTask(
1037 FROM_HERE,
1038 base::Bind(&RendererWrapper::SetPlaybackRate,
1039 base::Unretained(renderer_wrapper_.get()), playback_rate_));
1040 }
1041
1042 float PipelineImpl::GetVolume() const {
1043 DCHECK(thread_checker_.CalledOnValidThread());
1044 return volume_;
1045 }
1046
1047 void PipelineImpl::SetVolume(float volume) {
1048 DVLOG(2) << __FUNCTION__ << "(" << volume << ")";
1049 DCHECK(thread_checker_.CalledOnValidThread());
1050
1051 if (volume < 0.0f || volume > 1.0f)
1052 return;
1053
1054 volume_ = volume;
1055 media_task_runner_->PostTask(
1056 FROM_HERE,
1057 base::Bind(&RendererWrapper::SetVolume,
1058 base::Unretained(renderer_wrapper_.get()), volume_));
1059 }
1060
1061 base::TimeDelta PipelineImpl::GetMediaTime() const {
1062 DCHECK(thread_checker_.CalledOnValidThread());
1063 return renderer_wrapper_->GetMediaTime();
1064 }
1065
1066 Ranges<base::TimeDelta> PipelineImpl::GetBufferedTimeRanges() const {
1067 DCHECK(thread_checker_.CalledOnValidThread());
1068 return renderer_wrapper_->GetBufferedTimeRanges();
1069 }
1070
1071 base::TimeDelta PipelineImpl::GetMediaDuration() const {
1072 DCHECK(thread_checker_.CalledOnValidThread());
1073 return duration_;
1074 }
1075
1076 bool PipelineImpl::DidLoadingProgress() {
1077 DCHECK(thread_checker_.CalledOnValidThread());
1078 return renderer_wrapper_->DidLoadingProgress();
1079 }
1080
1081 PipelineStatistics PipelineImpl::GetStatistics() const {
1082 DCHECK(thread_checker_.CalledOnValidThread());
1083 return renderer_wrapper_->GetStatistics();
1084 }
1085
1086 void PipelineImpl::SetCdm(CdmContext* cdm_context,
1087 const CdmAttachedCB& cdm_attached_cb) {
1088 DVLOG(2) << __FUNCTION__;
1089 DCHECK(thread_checker_.CalledOnValidThread());
1090 DCHECK(cdm_context);
1091 DCHECK(!cdm_attached_cb.is_null());
1092
1093 media_task_runner_->PostTask(
1094 FROM_HERE,
1095 base::Bind(&RendererWrapper::SetCdm,
1096 base::Unretained(renderer_wrapper_.get()), cdm_context,
1097 media::BindToCurrentLoop(cdm_attached_cb)));
1098 }
1099
1100 #define RETURN_STRING(state) \
1101 case state: \
1102 return #state;
1103
1104 // static
1105 const char* PipelineImpl::GetStateString(State state) {
1106 switch (state) {
1107 RETURN_STRING(kCreated);
1108 RETURN_STRING(kInitDemuxer);
1109 RETURN_STRING(kInitRenderer);
1110 RETURN_STRING(kSeeking);
1111 RETURN_STRING(kPlaying);
1112 RETURN_STRING(kStopping);
1113 RETURN_STRING(kStopped);
1114 RETURN_STRING(kSuspending);
1115 RETURN_STRING(kSuspended);
1116 RETURN_STRING(kResuming);
1117 }
1118 NOTREACHED();
1119 return "INVALID";
1120 }
1121
1122 #undef RETURN_STRING
1123
1087 void PipelineImpl::OnError(PipelineStatus error) { 1124 void PipelineImpl::OnError(PipelineStatus error) {
1088 DVLOG(2) << __FUNCTION__; 1125 DVLOG(2) << __FUNCTION__;
1089 DCHECK(thread_checker_.CalledOnValidThread()); 1126 DCHECK(thread_checker_.CalledOnValidThread());
1090 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; 1127 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
1091 DCHECK(IsRunning()); 1128 DCHECK(IsRunning());
1092 1129
1093 // If the error happens during starting/seeking/suspending/resuming, 1130 // If the error happens during starting/seeking/suspending/resuming,
1094 // report the error via the completion callback for those tasks. 1131 // report the error via the completion callback for those tasks.
1095 // Else report error via the client interface. 1132 // Else report error via the client interface.
1096 if (!seek_cb_.is_null()) { 1133 if (!seek_cb_.is_null()) {
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
1192 1229
1193 void PipelineImpl::OnSuspendDone(base::TimeDelta suspend_time) { 1230 void PipelineImpl::OnSuspendDone(base::TimeDelta suspend_time) {
1194 DVLOG(3) << __FUNCTION__ << "(" << suspend_time.InMicroseconds() << ")"; 1231 DVLOG(3) << __FUNCTION__ << "(" << suspend_time.InMicroseconds() << ")";
1195 DCHECK(thread_checker_.CalledOnValidThread()); 1232 DCHECK(thread_checker_.CalledOnValidThread());
1196 DCHECK(IsRunning()); 1233 DCHECK(IsRunning());
1197 1234
1198 DCHECK(!suspend_cb_.is_null()); 1235 DCHECK(!suspend_cb_.is_null());
1199 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); 1236 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK);
1200 } 1237 }
1201 1238
1202 PipelineImpl::RendererWrapper::RendererWrapper(
1203 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
1204 scoped_refptr<MediaLog> media_log)
1205 : media_task_runner_(std::move(media_task_runner)),
1206 main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
1207 media_log_(std::move(media_log)),
1208 demuxer_(nullptr),
1209 playback_rate_(kDefaultPlaybackRate),
1210 volume_(kDefaultVolume),
1211 cdm_context_(nullptr),
1212 state_(kCreated),
1213 status_(PIPELINE_OK),
1214 renderer_ended_(false),
1215 text_renderer_ended_(false),
1216 weak_factory_(this) {
1217 weak_this_ = weak_factory_.GetWeakPtr();
1218 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
1219 }
1220
1221 PipelineImpl::RendererWrapper::~RendererWrapper() {
1222 DCHECK(media_task_runner_->BelongsToCurrentThread());
1223 DCHECK(state_ == kCreated || state_ == kStopped);
1224 }
1225
1226 base::TimeDelta PipelineImpl::RendererWrapper::GetMediaTime() const {
1227 DCHECK(main_task_runner_->BelongsToCurrentThread());
1228
1229 base::AutoLock auto_lock(shared_state_lock_);
1230 if (shared_state_.suspend_timestamp != kNoTimestamp())
1231 return shared_state_.suspend_timestamp;
1232 return shared_state_.renderer ? shared_state_.renderer->GetMediaTime()
1233 : base::TimeDelta();
1234 }
1235
1236 } // namespace media 1239 } // namespace media
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698