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

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

Issue 1658303002: Create abstract interface for media::Pipeline. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix FakeMediaSource includes. Created 4 years, 10 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 | « media/base/pipeline.h ('k') | media/base/pipeline_impl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "media/base/pipeline.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/callback.h"
13 #include "base/callback_helpers.h"
14 #include "base/command_line.h"
15 #include "base/compiler_specific.h"
16 #include "base/location.h"
17 #include "base/metrics/histogram.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/stl_util.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/synchronization/condition_variable.h"
23 #include "media/base/media_log.h"
24 #include "media/base/media_switches.h"
25 #include "media/base/renderer.h"
26 #include "media/base/text_renderer.h"
27 #include "media/base/text_track_config.h"
28 #include "media/base/timestamp_constants.h"
29 #include "media/base/video_decoder_config.h"
30
31 using base::TimeDelta;
32
33 namespace media {
34
35 Pipeline::Pipeline(
36 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
37 MediaLog* media_log)
38 : task_runner_(task_runner),
39 media_log_(media_log),
40 running_(false),
41 did_loading_progress_(false),
42 volume_(1.0f),
43 playback_rate_(0.0),
44 status_(PIPELINE_OK),
45 state_(kCreated),
46 suspend_timestamp_(kNoTimestamp()),
47 renderer_ended_(false),
48 text_renderer_ended_(false),
49 demuxer_(NULL),
50 pending_cdm_context_(nullptr),
51 weak_factory_(this) {
52 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
53 }
54
55 Pipeline::~Pipeline() {
56 DCHECK(thread_checker_.CalledOnValidThread())
57 << "Pipeline must be destroyed on same thread that created it";
58 DCHECK(!running_) << "Stop() must complete before destroying object";
59 DCHECK(stop_cb_.is_null());
60 DCHECK(seek_cb_.is_null());
61 }
62
63 void Pipeline::Start(Demuxer* demuxer,
64 scoped_ptr<Renderer> renderer,
65 const base::Closure& ended_cb,
66 const PipelineStatusCB& error_cb,
67 const PipelineStatusCB& seek_cb,
68 const PipelineMetadataCB& metadata_cb,
69 const BufferingStateCB& buffering_state_cb,
70 const base::Closure& duration_change_cb,
71 const AddTextTrackCB& add_text_track_cb,
72 const base::Closure& waiting_for_decryption_key_cb) {
73 DCHECK(!ended_cb.is_null());
74 DCHECK(!error_cb.is_null());
75 DCHECK(!seek_cb.is_null());
76 DCHECK(!metadata_cb.is_null());
77 DCHECK(!buffering_state_cb.is_null());
78
79 base::AutoLock auto_lock(lock_);
80 CHECK(!running_) << "Media pipeline is already running";
81 running_ = true;
82
83 demuxer_ = demuxer;
84 renderer_ = std::move(renderer);
85 ended_cb_ = ended_cb;
86 error_cb_ = error_cb;
87 seek_cb_ = seek_cb;
88 metadata_cb_ = metadata_cb;
89 buffering_state_cb_ = buffering_state_cb;
90 duration_change_cb_ = duration_change_cb;
91 add_text_track_cb_ = add_text_track_cb;
92 waiting_for_decryption_key_cb_ = waiting_for_decryption_key_cb;
93
94 task_runner_->PostTask(
95 FROM_HERE, base::Bind(&Pipeline::StartTask, weak_factory_.GetWeakPtr()));
96 }
97
98 void Pipeline::Stop(const base::Closure& stop_cb) {
99 DVLOG(2) << __FUNCTION__;
100 task_runner_->PostTask(
101 FROM_HERE,
102 base::Bind(&Pipeline::StopTask, weak_factory_.GetWeakPtr(), stop_cb));
103 }
104
105 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) {
106 base::AutoLock auto_lock(lock_);
107 if (!running_) {
108 DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek().";
109 return;
110 }
111
112 task_runner_->PostTask(
113 FROM_HERE,
114 base::Bind(
115 &Pipeline::SeekTask, weak_factory_.GetWeakPtr(), time, seek_cb));
116 }
117
118 bool Pipeline::IsRunning() const {
119 base::AutoLock auto_lock(lock_);
120 return running_;
121 }
122
123 double Pipeline::GetPlaybackRate() const {
124 base::AutoLock auto_lock(lock_);
125 return playback_rate_;
126 }
127
128 void Pipeline::SetPlaybackRate(double playback_rate) {
129 if (playback_rate < 0.0)
130 return;
131
132 base::AutoLock auto_lock(lock_);
133 playback_rate_ = playback_rate;
134 if (running_) {
135 task_runner_->PostTask(FROM_HERE,
136 base::Bind(&Pipeline::PlaybackRateChangedTask,
137 weak_factory_.GetWeakPtr(),
138 playback_rate));
139 }
140 }
141
142 void Pipeline::Suspend(const PipelineStatusCB& suspend_cb) {
143 task_runner_->PostTask(
144 FROM_HERE, base::Bind(&Pipeline::SuspendTask, weak_factory_.GetWeakPtr(),
145 suspend_cb));
146 }
147
148 void Pipeline::Resume(scoped_ptr<Renderer> renderer,
149 base::TimeDelta timestamp,
150 const PipelineStatusCB& seek_cb) {
151 task_runner_->PostTask(
152 FROM_HERE,
153 base::Bind(&Pipeline::ResumeTask, weak_factory_.GetWeakPtr(),
154 base::Passed(std::move(renderer)), timestamp, seek_cb));
155 }
156
157 float Pipeline::GetVolume() const {
158 base::AutoLock auto_lock(lock_);
159 return volume_;
160 }
161
162 void Pipeline::SetVolume(float volume) {
163 if (volume < 0.0f || volume > 1.0f)
164 return;
165
166 base::AutoLock auto_lock(lock_);
167 volume_ = volume;
168 if (running_) {
169 task_runner_->PostTask(
170 FROM_HERE,
171 base::Bind(
172 &Pipeline::VolumeChangedTask, weak_factory_.GetWeakPtr(), volume));
173 }
174 }
175
176 TimeDelta Pipeline::GetMediaTime() const {
177 base::AutoLock auto_lock(lock_);
178 if (suspend_timestamp_ != kNoTimestamp())
179 return suspend_timestamp_;
180 return renderer_ ? std::min(renderer_->GetMediaTime(), duration_)
181 : TimeDelta();
182 }
183
184 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() const {
185 base::AutoLock auto_lock(lock_);
186 return buffered_time_ranges_;
187 }
188
189 TimeDelta Pipeline::GetMediaDuration() const {
190 base::AutoLock auto_lock(lock_);
191 return duration_;
192 }
193
194 bool Pipeline::DidLoadingProgress() {
195 base::AutoLock auto_lock(lock_);
196 bool ret = did_loading_progress_;
197 did_loading_progress_ = false;
198 return ret;
199 }
200
201 PipelineStatistics Pipeline::GetStatistics() const {
202 base::AutoLock auto_lock(lock_);
203 return statistics_;
204 }
205
206 void Pipeline::SetCdm(CdmContext* cdm_context,
207 const CdmAttachedCB& cdm_attached_cb) {
208 task_runner_->PostTask(
209 FROM_HERE, base::Bind(&Pipeline::SetCdmTask, weak_factory_.GetWeakPtr(),
210 cdm_context, cdm_attached_cb));
211 }
212
213 void Pipeline::SetErrorForTesting(PipelineStatus status) {
214 OnError(status);
215 }
216
217 bool Pipeline::HasWeakPtrsForTesting() const {
218 DCHECK(task_runner_->BelongsToCurrentThread());
219 return weak_factory_.HasWeakPtrs();
220 }
221
222 void Pipeline::SetState(State next_state) {
223 DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state);
224
225 state_ = next_state;
226 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
227 }
228
229 #define RETURN_STRING(state) case state: return #state;
230
231 const char* Pipeline::GetStateString(State state) {
232 switch (state) {
233 RETURN_STRING(kCreated);
234 RETURN_STRING(kInitDemuxer);
235 RETURN_STRING(kInitRenderer);
236 RETURN_STRING(kSeeking);
237 RETURN_STRING(kPlaying);
238 RETURN_STRING(kStopping);
239 RETURN_STRING(kStopped);
240 RETURN_STRING(kSuspending);
241 RETURN_STRING(kSuspended);
242 RETURN_STRING(kResuming);
243 }
244 NOTREACHED();
245 return "INVALID";
246 }
247
248 #undef RETURN_STRING
249
250 Pipeline::State Pipeline::GetNextState() const {
251 DCHECK(task_runner_->BelongsToCurrentThread());
252 DCHECK(stop_cb_.is_null())
253 << "State transitions don't happen when stopping";
254 DCHECK_EQ(status_, PIPELINE_OK)
255 << "State transitions don't happen when there's an error: " << status_;
256
257 switch (state_) {
258 case kCreated:
259 return kInitDemuxer;
260
261 case kInitDemuxer:
262 return kInitRenderer;
263
264 case kInitRenderer:
265 case kSeeking:
266 return kPlaying;
267
268 case kSuspending:
269 return kSuspended;
270
271 case kSuspended:
272 return kResuming;
273
274 case kResuming:
275 return kPlaying;
276
277 case kPlaying:
278 case kStopping:
279 case kStopped:
280 break;
281 }
282 NOTREACHED() << "State has no transition: " << state_;
283 return state_;
284 }
285
286 void Pipeline::OnDemuxerError(PipelineStatus error) {
287 task_runner_->PostTask(FROM_HERE,
288 base::Bind(&Pipeline::ErrorChangedTask,
289 weak_factory_.GetWeakPtr(),
290 error));
291 }
292
293 void Pipeline::AddTextStream(DemuxerStream* text_stream,
294 const TextTrackConfig& config) {
295 task_runner_->PostTask(FROM_HERE,
296 base::Bind(&Pipeline::AddTextStreamTask,
297 weak_factory_.GetWeakPtr(),
298 text_stream,
299 config));
300 }
301
302 void Pipeline::RemoveTextStream(DemuxerStream* text_stream) {
303 task_runner_->PostTask(FROM_HERE,
304 base::Bind(&Pipeline::RemoveTextStreamTask,
305 weak_factory_.GetWeakPtr(),
306 text_stream));
307 }
308
309 void Pipeline::OnError(PipelineStatus error) {
310 DCHECK(task_runner_->BelongsToCurrentThread());
311 DCHECK(IsRunning());
312 DCHECK_NE(PIPELINE_OK, error);
313 VLOG(1) << "Media pipeline error: " << error;
314
315 task_runner_->PostTask(FROM_HERE, base::Bind(
316 &Pipeline::ErrorChangedTask, weak_factory_.GetWeakPtr(), error));
317 }
318
319 void Pipeline::SetDuration(TimeDelta duration) {
320 DCHECK(IsRunning());
321 media_log_->AddEvent(
322 media_log_->CreateTimeEvent(
323 MediaLogEvent::DURATION_SET, "duration", duration));
324 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
325
326 base::AutoLock auto_lock(lock_);
327 duration_ = duration;
328 if (!duration_change_cb_.is_null())
329 duration_change_cb_.Run();
330 }
331
332 void Pipeline::StateTransitionTask(PipelineStatus status) {
333 DCHECK(task_runner_->BelongsToCurrentThread());
334
335 // No-op any state transitions if we're stopping.
336 if (state_ == kStopping || state_ == kStopped)
337 return;
338
339 // Preserve existing abnormal status, otherwise update based on the result of
340 // the previous operation.
341 status_ = (status_ != PIPELINE_OK ? status_ : status);
342
343 if (status_ != PIPELINE_OK) {
344 ErrorChangedTask(status_);
345 return;
346 }
347
348 // Guard against accidentally clearing |pending_callbacks_| for states that
349 // use it as well as states that should not be using it.
350 DCHECK_EQ(pending_callbacks_.get() != NULL,
351 state_ == kSeeking || state_ == kSuspending || state_ == kResuming);
352
353 pending_callbacks_.reset();
354
355 PipelineStatusCB done_cb =
356 base::Bind(&Pipeline::StateTransitionTask, weak_factory_.GetWeakPtr());
357
358 // Switch states, performing any entrance actions for the new state as well.
359 SetState(GetNextState());
360 switch (state_) {
361 case kInitDemuxer:
362 return InitializeDemuxer(done_cb);
363
364 case kInitRenderer:
365 // When the state_ transfers to kInitRenderer, it means the demuxer has
366 // finished parsing the init info. It should call ReportMetadata in case
367 // meeting 'decode' error when passing media segment but WebMediaPlayer's
368 // ready_state_ is still ReadyStateHaveNothing. In that case, it will
369 // treat it as NetworkStateFormatError not NetworkStateDecodeError.
370 ReportMetadata();
371 start_timestamp_ = demuxer_->GetStartTime();
372
373 return InitializeRenderer(done_cb);
374
375 case kPlaying:
376 DCHECK(start_timestamp_ >= base::TimeDelta());
377 renderer_->StartPlayingFrom(start_timestamp_);
378 {
379 base::AutoLock auto_lock(lock_);
380 suspend_timestamp_ = kNoTimestamp();
381 }
382
383 if (text_renderer_)
384 text_renderer_->StartPlaying();
385
386 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
387
388 PlaybackRateChangedTask(GetPlaybackRate());
389 VolumeChangedTask(GetVolume());
390 return;
391
392 case kSuspended:
393 renderer_.reset();
394 base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK);
395 return;
396
397 case kStopping:
398 case kStopped:
399 case kCreated:
400 case kSeeking:
401 case kSuspending:
402 case kResuming:
403 NOTREACHED() << "State has no transition: " << state_;
404 return;
405 }
406 }
407
408 // Note that the usage of base::Unretained() with the renderers is considered
409 // safe as they are owned by |pending_callbacks_| and share the same lifetime.
410 //
411 // That being said, deleting the renderers while keeping |pending_callbacks_|
412 // running on the media thread would result in crashes.
413 void Pipeline::DoSeek(TimeDelta seek_timestamp,
414 const PipelineStatusCB& done_cb) {
415 DCHECK(task_runner_->BelongsToCurrentThread());
416 DCHECK(!pending_callbacks_.get());
417 DCHECK_EQ(state_, kSeeking);
418 SerialRunner::Queue bound_fns;
419
420 // Pause.
421 if (text_renderer_) {
422 bound_fns.Push(base::Bind(
423 &TextRenderer::Pause, base::Unretained(text_renderer_.get())));
424 }
425
426 // Flush.
427 DCHECK(renderer_);
428 bound_fns.Push(
429 base::Bind(&Renderer::Flush, base::Unretained(renderer_.get())));
430
431 if (text_renderer_) {
432 bound_fns.Push(base::Bind(
433 &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
434 }
435
436 // Seek demuxer.
437 bound_fns.Push(base::Bind(
438 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
439
440 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
441 }
442
443 void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
444 DVLOG(2) << __FUNCTION__;
445 DCHECK(task_runner_->BelongsToCurrentThread());
446 DCHECK(!pending_callbacks_.get());
447
448 // TODO(scherkus): Enforce that Renderer is only called on a single thread,
449 // even for accessing media time http://crbug.com/370634
450 scoped_ptr<Renderer> renderer;
451 {
452 base::AutoLock auto_lock(lock_);
453 renderer.swap(renderer_);
454 }
455 renderer.reset();
456 text_renderer_.reset();
457
458 if (demuxer_) {
459 demuxer_->Stop();
460 demuxer_ = NULL;
461 }
462
463 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK));
464 }
465
466 void Pipeline::OnStopCompleted(PipelineStatus status) {
467 DVLOG(2) << __FUNCTION__;
468 DCHECK(task_runner_->BelongsToCurrentThread());
469 DCHECK_EQ(state_, kStopping);
470 DCHECK(!renderer_);
471 DCHECK(!text_renderer_);
472
473 {
474 base::AutoLock auto_lock(lock_);
475 running_ = false;
476 }
477
478 SetState(kStopped);
479 demuxer_ = NULL;
480
481 // If we stop during initialization/seeking/suspending we don't want to leave
482 // outstanding callbacks around.
483 if (!seek_cb_.is_null()) {
484 base::ResetAndReturn(&seek_cb_).Run(status_);
485 error_cb_.Reset();
486 }
487 if (!suspend_cb_.is_null()) {
488 base::ResetAndReturn(&suspend_cb_).Run(status_);
489 error_cb_.Reset();
490 }
491 if (!stop_cb_.is_null()) {
492 error_cb_.Reset();
493
494 // Invalid all weak pointers so it's safe to destroy |this| on the render
495 // main thread.
496 weak_factory_.InvalidateWeakPtrs();
497
498 base::ResetAndReturn(&stop_cb_).Run();
499
500 // NOTE: pipeline may be deleted at this point in time as a result of
501 // executing |stop_cb_|.
502 return;
503 }
504 if (!error_cb_.is_null()) {
505 DCHECK_NE(status_, PIPELINE_OK);
506 base::ResetAndReturn(&error_cb_).Run(status_);
507 }
508 }
509
510 void Pipeline::OnBufferedTimeRangesChanged(
511 const Ranges<base::TimeDelta>& ranges) {
512 DCHECK(IsRunning());
513 base::AutoLock auto_lock(lock_);
514 buffered_time_ranges_ = ranges;
515 did_loading_progress_ = true;
516 }
517
518 // Called from any thread.
519 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats_delta) {
520 base::AutoLock auto_lock(lock_);
521 statistics_.audio_bytes_decoded += stats_delta.audio_bytes_decoded;
522 statistics_.video_bytes_decoded += stats_delta.video_bytes_decoded;
523 statistics_.video_frames_decoded += stats_delta.video_frames_decoded;
524 statistics_.video_frames_dropped += stats_delta.video_frames_dropped;
525 statistics_.audio_memory_usage += stats_delta.audio_memory_usage;
526 statistics_.video_memory_usage += stats_delta.video_memory_usage;
527 }
528
529 void Pipeline::StartTask() {
530 DCHECK(task_runner_->BelongsToCurrentThread());
531
532 CHECK_EQ(kCreated, state_)
533 << "Media pipeline cannot be started more than once";
534
535 text_renderer_ = CreateTextRenderer();
536 if (text_renderer_) {
537 text_renderer_->Initialize(
538 base::Bind(&Pipeline::OnTextRendererEnded, weak_factory_.GetWeakPtr()));
539 }
540
541 // Set CDM early to avoid unnecessary delay in Renderer::Initialize().
542 if (pending_cdm_context_) {
543 renderer_->SetCdm(pending_cdm_context_, base::Bind(&IgnoreCdmAttached));
544 pending_cdm_context_ = nullptr;
545 }
546
547 StateTransitionTask(PIPELINE_OK);
548 }
549
550 void Pipeline::StopTask(const base::Closure& stop_cb) {
551 DCHECK(task_runner_->BelongsToCurrentThread());
552 DCHECK(stop_cb_.is_null());
553
554 if (state_ == kStopped) {
555 // Invalid all weak pointers so it's safe to destroy |this| on the render
556 // main thread.
557 weak_factory_.InvalidateWeakPtrs();
558
559 // NOTE: pipeline may be deleted at this point in time as a result of
560 // executing |stop_cb|.
561 stop_cb.Run();
562
563 return;
564 }
565
566 stop_cb_ = stop_cb;
567
568 // We may already be stopping due to a runtime error.
569 if (state_ == kStopping)
570 return;
571
572 // Do not report statistics if the pipeline is not fully initialized.
573 if (state_ == kSeeking || state_ == kPlaying || state_ == kSuspending ||
574 state_ == kSuspended || state_ == kResuming) {
575 PipelineStatistics stats = GetStatistics();
576 if (stats.video_frames_decoded > 0) {
577 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount",
578 stats.video_frames_dropped);
579 }
580 }
581
582 SetState(kStopping);
583 pending_callbacks_.reset();
584 DoStop(base::Bind(&Pipeline::OnStopCompleted, weak_factory_.GetWeakPtr()));
585 }
586
587 void Pipeline::ErrorChangedTask(PipelineStatus error) {
588 DCHECK(task_runner_->BelongsToCurrentThread());
589 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
590
591 media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error));
592
593 if (state_ == kStopping || state_ == kStopped)
594 return;
595
596 SetState(kStopping);
597 pending_callbacks_.reset();
598 status_ = error;
599
600 DoStop(base::Bind(&Pipeline::OnStopCompleted, weak_factory_.GetWeakPtr()));
601 }
602
603 void Pipeline::PlaybackRateChangedTask(double playback_rate) {
604 DCHECK(task_runner_->BelongsToCurrentThread());
605
606 // Playback rate changes are only carried out while playing.
607 if (state_ != kPlaying)
608 return;
609
610 renderer_->SetPlaybackRate(playback_rate);
611 }
612
613 void Pipeline::VolumeChangedTask(float volume) {
614 DCHECK(task_runner_->BelongsToCurrentThread());
615
616 // Volume changes are only carried out while playing.
617 if (state_ != kPlaying)
618 return;
619
620 renderer_->SetVolume(volume);
621 }
622
623 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
624 DCHECK(task_runner_->BelongsToCurrentThread());
625 DCHECK(stop_cb_.is_null());
626
627 // Suppress seeking if we're not fully started.
628 if (state_ != kPlaying) {
629 DCHECK(state_ == kStopping || state_ == kStopped)
630 << "Receive seek in unexpected state: " << state_;
631 seek_cb.Run(PIPELINE_ERROR_INVALID_STATE);
632 return;
633 }
634
635 DCHECK(seek_cb_.is_null());
636
637 const base::TimeDelta seek_timestamp =
638 std::max(time, demuxer_->GetStartTime());
639
640 SetState(kSeeking);
641 seek_cb_ = seek_cb;
642 renderer_ended_ = false;
643 text_renderer_ended_ = false;
644 start_timestamp_ = seek_timestamp;
645
646 DoSeek(seek_timestamp, base::Bind(&Pipeline::StateTransitionTask,
647 weak_factory_.GetWeakPtr()));
648 }
649
650 void Pipeline::SuspendTask(const PipelineStatusCB& suspend_cb) {
651 DCHECK(task_runner_->BelongsToCurrentThread());
652
653 // Suppress suspending if we're not playing.
654 if (state_ != kPlaying) {
655 DCHECK(state_ == kStopping || state_ == kStopped)
656 << "Receive suspend in unexpected state: " << state_;
657 suspend_cb.Run(PIPELINE_ERROR_INVALID_STATE);
658 return;
659 }
660 DCHECK(renderer_);
661 DCHECK(!pending_callbacks_.get());
662
663 SetState(kSuspending);
664 suspend_cb_ = suspend_cb;
665
666 // Freeze playback and record the media time before flushing. (Flushing clears
667 // the value.)
668 renderer_->SetPlaybackRate(0.0);
669 {
670 base::AutoLock auto_lock(lock_);
671 suspend_timestamp_ = renderer_->GetMediaTime();
672 DCHECK(suspend_timestamp_ != kNoTimestamp());
673 }
674
675 // Queue the asynchronous actions required to stop playback. (Matches setup in
676 // DoSeek().)
677 // TODO(sandersd): Share implementation with DoSeek().
678 SerialRunner::Queue fns;
679
680 if (text_renderer_) {
681 fns.Push(base::Bind(&TextRenderer::Pause,
682 base::Unretained(text_renderer_.get())));
683 }
684
685 fns.Push(base::Bind(&Renderer::Flush, base::Unretained(renderer_.get())));
686
687 if (text_renderer_) {
688 fns.Push(base::Bind(&TextRenderer::Flush,
689 base::Unretained(text_renderer_.get())));
690 }
691
692 pending_callbacks_ = SerialRunner::Run(
693 fns,
694 base::Bind(&Pipeline::StateTransitionTask, weak_factory_.GetWeakPtr()));
695 }
696
697 void Pipeline::ResumeTask(scoped_ptr<Renderer> renderer,
698 base::TimeDelta timestamp,
699 const PipelineStatusCB& seek_cb) {
700 DCHECK(task_runner_->BelongsToCurrentThread());
701
702 // Suppress resuming if we're not suspended.
703 if (state_ != kSuspended) {
704 DCHECK(state_ == kStopping || state_ == kStopped)
705 << "Receive resume in unexpected state: " << state_;
706 seek_cb.Run(PIPELINE_ERROR_INVALID_STATE);
707 return;
708 }
709 DCHECK(!renderer_);
710 DCHECK(!pending_callbacks_.get());
711
712 SetState(kResuming);
713 renderer_ = std::move(renderer);
714
715 // Set up for a seek. (Matches setup in SeekTask().)
716 // TODO(sandersd): Share implementation with SeekTask().
717 seek_cb_ = seek_cb;
718 renderer_ended_ = false;
719 text_renderer_ended_ = false;
720 start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime());
721
722 // Queue the asynchronous actions required to start playback. Unlike DoSeek(),
723 // we need to initialize the renderer ourselves (we don't want to enter state
724 // kInitDemuxer, and even if we did the current code would seek to the start
725 // instead of |timestamp|).
726 SerialRunner::Queue fns;
727 base::WeakPtr<Pipeline> weak_this = weak_factory_.GetWeakPtr();
728
729 fns.Push(
730 base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_));
731
732 fns.Push(base::Bind(&Pipeline::InitializeRenderer, weak_this));
733
734 pending_callbacks_ = SerialRunner::Run(
735 fns, base::Bind(&Pipeline::StateTransitionTask, weak_this));
736 }
737
738 void Pipeline::SetCdmTask(CdmContext* cdm_context,
739 const CdmAttachedCB& cdm_attached_cb) {
740 base::AutoLock auto_lock(lock_);
741 if (!renderer_) {
742 pending_cdm_context_ = cdm_context;
743 cdm_attached_cb.Run(true);
744 return;
745 }
746
747 renderer_->SetCdm(cdm_context, cdm_attached_cb);
748 }
749
750 void Pipeline::OnRendererEnded() {
751 DCHECK(task_runner_->BelongsToCurrentThread());
752 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED));
753
754 if (state_ != kPlaying)
755 return;
756
757 DCHECK(!renderer_ended_);
758 renderer_ended_ = true;
759
760 RunEndedCallbackIfNeeded();
761 }
762
763 void Pipeline::OnTextRendererEnded() {
764 DCHECK(task_runner_->BelongsToCurrentThread());
765 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
766
767 if (state_ != kPlaying)
768 return;
769
770 DCHECK(!text_renderer_ended_);
771 text_renderer_ended_ = true;
772
773 RunEndedCallbackIfNeeded();
774 }
775
776 void Pipeline::RunEndedCallbackIfNeeded() {
777 DCHECK(task_runner_->BelongsToCurrentThread());
778
779 if (renderer_ && !renderer_ended_)
780 return;
781
782 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_)
783 return;
784
785 DCHECK_EQ(status_, PIPELINE_OK);
786 ended_cb_.Run();
787 }
788
789 scoped_ptr<TextRenderer> Pipeline::CreateTextRenderer() {
790 DCHECK(task_runner_->BelongsToCurrentThread());
791
792 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
793 if (!cmd_line->HasSwitch(switches::kEnableInbandTextTracks))
794 return scoped_ptr<media::TextRenderer>();
795
796 return scoped_ptr<media::TextRenderer>(new media::TextRenderer(
797 task_runner_,
798 base::Bind(&Pipeline::OnAddTextTrack, weak_factory_.GetWeakPtr())));
799 }
800
801 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream,
802 const TextTrackConfig& config) {
803 DCHECK(task_runner_->BelongsToCurrentThread());
804 // TODO(matthewjheaney): fix up text_ended_ when text stream
805 // is added (http://crbug.com/321446).
806 if (text_renderer_)
807 text_renderer_->AddTextStream(text_stream, config);
808 }
809
810 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) {
811 DCHECK(task_runner_->BelongsToCurrentThread());
812 if (text_renderer_)
813 text_renderer_->RemoveTextStream(text_stream);
814 }
815
816 void Pipeline::OnAddTextTrack(const TextTrackConfig& config,
817 const AddTextTrackDoneCB& done_cb) {
818 DCHECK(task_runner_->BelongsToCurrentThread());
819 add_text_track_cb_.Run(config, done_cb);
820 }
821
822 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
823 DCHECK(task_runner_->BelongsToCurrentThread());
824 demuxer_->Initialize(this, done_cb, !!text_renderer_);
825 }
826
827 void Pipeline::InitializeRenderer(const PipelineStatusCB& done_cb) {
828 DCHECK(task_runner_->BelongsToCurrentThread());
829
830 if (!demuxer_->GetStream(DemuxerStream::AUDIO) &&
831 !demuxer_->GetStream(DemuxerStream::VIDEO)) {
832 {
833 base::AutoLock auto_lock(lock_);
834 renderer_.reset();
835 }
836 OnError(PIPELINE_ERROR_COULD_NOT_RENDER);
837 return;
838 }
839
840 base::WeakPtr<Pipeline> weak_this = weak_factory_.GetWeakPtr();
841 renderer_->Initialize(
842 demuxer_,
843 done_cb,
844 base::Bind(&Pipeline::OnUpdateStatistics, weak_this),
845 base::Bind(&Pipeline::BufferingStateChanged, weak_this),
846 base::Bind(&Pipeline::OnRendererEnded, weak_this),
847 base::Bind(&Pipeline::OnError, weak_this),
848 waiting_for_decryption_key_cb_);
849 }
850
851 void Pipeline::ReportMetadata() {
852 DCHECK(task_runner_->BelongsToCurrentThread());
853 PipelineMetadata metadata;
854 metadata.timeline_offset = demuxer_->GetTimelineOffset();
855 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
856 if (stream) {
857 metadata.has_video = true;
858 metadata.natural_size = stream->video_decoder_config().natural_size();
859 metadata.video_rotation = stream->video_rotation();
860 }
861 if (demuxer_->GetStream(DemuxerStream::AUDIO)) {
862 metadata.has_audio = true;
863 }
864 metadata_cb_.Run(metadata);
865 }
866
867 void Pipeline::BufferingStateChanged(BufferingState new_buffering_state) {
868 DVLOG(1) << __FUNCTION__ << "(" << new_buffering_state << ") ";
869 DCHECK(task_runner_->BelongsToCurrentThread());
870 buffering_state_cb_.Run(new_buffering_state);
871 }
872
873 } // namespace media
OLDNEW
« no previous file with comments | « media/base/pipeline.h ('k') | media/base/pipeline_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698