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

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

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

Powered by Google App Engine
This is Rietveld 408576698