Corrected the TemplateURLTest.ExtractSearchTermsFromUTF8URL test.
[chromium-blink-merge.git] / media / base / pipeline.cc
blob4a397049fc0279f9443de4480d02543ce7135cd4
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.
5 #include "media/base/pipeline.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/callback_helpers.h"
12 #include "base/command_line.h"
13 #include "base/compiler_specific.h"
14 #include "base/location.h"
15 #include "base/metrics/histogram.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/synchronization/condition_variable.h"
21 #include "media/base/media_log.h"
22 #include "media/base/media_switches.h"
23 #include "media/base/renderer.h"
24 #include "media/base/text_renderer.h"
25 #include "media/base/text_track_config.h"
26 #include "media/base/video_decoder_config.h"
28 using base::TimeDelta;
30 namespace media {
32 Pipeline::Pipeline(
33 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
34 MediaLog* media_log)
35 : task_runner_(task_runner),
36 media_log_(media_log),
37 running_(false),
38 did_loading_progress_(false),
39 volume_(1.0f),
40 playback_rate_(0.0),
41 status_(PIPELINE_OK),
42 state_(kCreated),
43 renderer_ended_(false),
44 text_renderer_ended_(false),
45 demuxer_(NULL),
46 pending_cdm_context_(nullptr),
47 weak_factory_(this) {
48 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
49 media_log_->AddEvent(
50 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED));
53 Pipeline::~Pipeline() {
54 DCHECK(thread_checker_.CalledOnValidThread())
55 << "Pipeline must be destroyed on same thread that created it";
56 DCHECK(!running_) << "Stop() must complete before destroying object";
57 DCHECK(stop_cb_.is_null());
58 DCHECK(seek_cb_.is_null());
60 media_log_->AddEvent(
61 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED));
64 void Pipeline::Start(Demuxer* demuxer,
65 scoped_ptr<Renderer> renderer,
66 const base::Closure& ended_cb,
67 const PipelineStatusCB& error_cb,
68 const PipelineStatusCB& seek_cb,
69 const PipelineMetadataCB& metadata_cb,
70 const BufferingStateCB& buffering_state_cb,
71 const base::Closure& duration_change_cb,
72 const AddTextTrackCB& add_text_track_cb,
73 const base::Closure& waiting_for_decryption_key_cb) {
74 DCHECK(!ended_cb.is_null());
75 DCHECK(!error_cb.is_null());
76 DCHECK(!seek_cb.is_null());
77 DCHECK(!metadata_cb.is_null());
78 DCHECK(!buffering_state_cb.is_null());
80 base::AutoLock auto_lock(lock_);
81 CHECK(!running_) << "Media pipeline is already running";
82 running_ = true;
84 demuxer_ = demuxer;
85 renderer_ = renderer.Pass();
86 ended_cb_ = ended_cb;
87 error_cb_ = error_cb;
88 seek_cb_ = seek_cb;
89 metadata_cb_ = metadata_cb;
90 buffering_state_cb_ = buffering_state_cb;
91 duration_change_cb_ = duration_change_cb;
92 add_text_track_cb_ = add_text_track_cb;
93 waiting_for_decryption_key_cb_ = waiting_for_decryption_key_cb;
95 task_runner_->PostTask(
96 FROM_HERE, base::Bind(&Pipeline::StartTask, weak_factory_.GetWeakPtr()));
99 void Pipeline::Stop(const base::Closure& stop_cb) {
100 DVLOG(2) << __FUNCTION__;
101 task_runner_->PostTask(
102 FROM_HERE,
103 base::Bind(&Pipeline::StopTask, weak_factory_.GetWeakPtr(), stop_cb));
106 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) {
107 base::AutoLock auto_lock(lock_);
108 if (!running_) {
109 DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek().";
110 return;
113 task_runner_->PostTask(
114 FROM_HERE,
115 base::Bind(
116 &Pipeline::SeekTask, weak_factory_.GetWeakPtr(), time, seek_cb));
119 bool Pipeline::IsRunning() const {
120 base::AutoLock auto_lock(lock_);
121 return running_;
124 double Pipeline::GetPlaybackRate() const {
125 base::AutoLock auto_lock(lock_);
126 return playback_rate_;
129 void Pipeline::SetPlaybackRate(double playback_rate) {
130 if (playback_rate < 0.0)
131 return;
133 base::AutoLock auto_lock(lock_);
134 playback_rate_ = playback_rate;
135 if (running_) {
136 task_runner_->PostTask(FROM_HERE,
137 base::Bind(&Pipeline::PlaybackRateChangedTask,
138 weak_factory_.GetWeakPtr(),
139 playback_rate));
143 float Pipeline::GetVolume() const {
144 base::AutoLock auto_lock(lock_);
145 return volume_;
148 void Pipeline::SetVolume(float volume) {
149 if (volume < 0.0f || volume > 1.0f)
150 return;
152 base::AutoLock auto_lock(lock_);
153 volume_ = volume;
154 if (running_) {
155 task_runner_->PostTask(
156 FROM_HERE,
157 base::Bind(
158 &Pipeline::VolumeChangedTask, weak_factory_.GetWeakPtr(), volume));
162 TimeDelta Pipeline::GetMediaTime() const {
163 base::AutoLock auto_lock(lock_);
164 return renderer_ ? std::min(renderer_->GetMediaTime(), duration_)
165 : TimeDelta();
168 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() const {
169 base::AutoLock auto_lock(lock_);
170 return buffered_time_ranges_;
173 TimeDelta Pipeline::GetMediaDuration() const {
174 base::AutoLock auto_lock(lock_);
175 return duration_;
178 bool Pipeline::DidLoadingProgress() {
179 base::AutoLock auto_lock(lock_);
180 bool ret = did_loading_progress_;
181 did_loading_progress_ = false;
182 return ret;
185 PipelineStatistics Pipeline::GetStatistics() const {
186 base::AutoLock auto_lock(lock_);
187 return statistics_;
190 void Pipeline::SetCdm(CdmContext* cdm_context,
191 const CdmAttachedCB& cdm_attached_cb) {
192 task_runner_->PostTask(
193 FROM_HERE, base::Bind(&Pipeline::SetCdmTask, weak_factory_.GetWeakPtr(),
194 cdm_context, cdm_attached_cb));
197 void Pipeline::SetErrorForTesting(PipelineStatus status) {
198 OnError(status);
201 bool Pipeline::HasWeakPtrsForTesting() const {
202 DCHECK(task_runner_->BelongsToCurrentThread());
203 return weak_factory_.HasWeakPtrs();
206 void Pipeline::SetState(State next_state) {
207 DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state);
209 state_ = next_state;
210 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
213 #define RETURN_STRING(state) case state: return #state;
215 const char* Pipeline::GetStateString(State state) {
216 switch (state) {
217 RETURN_STRING(kCreated);
218 RETURN_STRING(kInitDemuxer);
219 RETURN_STRING(kInitRenderer);
220 RETURN_STRING(kSeeking);
221 RETURN_STRING(kPlaying);
222 RETURN_STRING(kStopping);
223 RETURN_STRING(kStopped);
225 NOTREACHED();
226 return "INVALID";
229 #undef RETURN_STRING
231 Pipeline::State Pipeline::GetNextState() const {
232 DCHECK(task_runner_->BelongsToCurrentThread());
233 DCHECK(stop_cb_.is_null())
234 << "State transitions don't happen when stopping";
235 DCHECK_EQ(status_, PIPELINE_OK)
236 << "State transitions don't happen when there's an error: " << status_;
238 switch (state_) {
239 case kCreated:
240 return kInitDemuxer;
242 case kInitDemuxer:
243 return kInitRenderer;
245 case kInitRenderer:
246 case kSeeking:
247 return kPlaying;
249 case kPlaying:
250 case kStopping:
251 case kStopped:
252 break;
254 NOTREACHED() << "State has no transition: " << state_;
255 return state_;
258 void Pipeline::OnDemuxerError(PipelineStatus error) {
259 task_runner_->PostTask(FROM_HERE,
260 base::Bind(&Pipeline::ErrorChangedTask,
261 weak_factory_.GetWeakPtr(),
262 error));
265 void Pipeline::AddTextStream(DemuxerStream* text_stream,
266 const TextTrackConfig& config) {
267 task_runner_->PostTask(FROM_HERE,
268 base::Bind(&Pipeline::AddTextStreamTask,
269 weak_factory_.GetWeakPtr(),
270 text_stream,
271 config));
274 void Pipeline::RemoveTextStream(DemuxerStream* text_stream) {
275 task_runner_->PostTask(FROM_HERE,
276 base::Bind(&Pipeline::RemoveTextStreamTask,
277 weak_factory_.GetWeakPtr(),
278 text_stream));
281 void Pipeline::OnError(PipelineStatus error) {
282 DCHECK(task_runner_->BelongsToCurrentThread());
283 DCHECK(IsRunning());
284 DCHECK_NE(PIPELINE_OK, error);
285 VLOG(1) << "Media pipeline error: " << error;
287 task_runner_->PostTask(FROM_HERE, base::Bind(
288 &Pipeline::ErrorChangedTask, weak_factory_.GetWeakPtr(), error));
291 void Pipeline::SetDuration(TimeDelta duration) {
292 DCHECK(IsRunning());
293 media_log_->AddEvent(
294 media_log_->CreateTimeEvent(
295 MediaLogEvent::DURATION_SET, "duration", duration));
296 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
298 base::AutoLock auto_lock(lock_);
299 duration_ = duration;
300 if (!duration_change_cb_.is_null())
301 duration_change_cb_.Run();
304 void Pipeline::StateTransitionTask(PipelineStatus status) {
305 DCHECK(task_runner_->BelongsToCurrentThread());
307 // No-op any state transitions if we're stopping.
308 if (state_ == kStopping || state_ == kStopped)
309 return;
311 // Preserve existing abnormal status, otherwise update based on the result of
312 // the previous operation.
313 status_ = (status_ != PIPELINE_OK ? status_ : status);
315 if (status_ != PIPELINE_OK) {
316 ErrorChangedTask(status_);
317 return;
320 // Guard against accidentally clearing |pending_callbacks_| for states that
321 // use it as well as states that should not be using it.
322 DCHECK_EQ(pending_callbacks_.get() != NULL, state_ == kSeeking);
324 pending_callbacks_.reset();
326 PipelineStatusCB done_cb =
327 base::Bind(&Pipeline::StateTransitionTask, weak_factory_.GetWeakPtr());
329 // Switch states, performing any entrance actions for the new state as well.
330 SetState(GetNextState());
331 switch (state_) {
332 case kInitDemuxer:
333 return InitializeDemuxer(done_cb);
335 case kInitRenderer:
336 // When the state_ transfers to kInitRenderer, it means the demuxer has
337 // finished parsing the init info. It should call ReportMetadata in case
338 // meeting 'decode' error when passing media segment but WebMediaPlayer's
339 // ready_state_ is still ReadyStateHaveNothing. In that case, it will
340 // treat it as NetworkStateFormatError not NetworkStateDecodeError.
341 ReportMetadata();
342 start_timestamp_ = demuxer_->GetStartTime();
344 return InitializeRenderer(done_cb);
346 case kPlaying:
347 DCHECK(start_timestamp_ >= base::TimeDelta());
348 renderer_->StartPlayingFrom(start_timestamp_);
350 if (text_renderer_)
351 text_renderer_->StartPlaying();
353 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
355 PlaybackRateChangedTask(GetPlaybackRate());
356 VolumeChangedTask(GetVolume());
357 return;
359 case kStopping:
360 case kStopped:
361 case kCreated:
362 case kSeeking:
363 NOTREACHED() << "State has no transition: " << state_;
364 return;
368 // Note that the usage of base::Unretained() with the renderers is considered
369 // safe as they are owned by |pending_callbacks_| and share the same lifetime.
371 // That being said, deleting the renderers while keeping |pending_callbacks_|
372 // running on the media thread would result in crashes.
373 void Pipeline::DoSeek(TimeDelta seek_timestamp,
374 const PipelineStatusCB& done_cb) {
375 DCHECK(task_runner_->BelongsToCurrentThread());
376 DCHECK(!pending_callbacks_.get());
377 DCHECK_EQ(state_, kSeeking);
378 SerialRunner::Queue bound_fns;
380 // Pause.
381 if (text_renderer_) {
382 bound_fns.Push(base::Bind(
383 &TextRenderer::Pause, base::Unretained(text_renderer_.get())));
386 // Flush.
387 DCHECK(renderer_);
388 bound_fns.Push(
389 base::Bind(&Renderer::Flush, base::Unretained(renderer_.get())));
391 if (text_renderer_) {
392 bound_fns.Push(base::Bind(
393 &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
396 // Seek demuxer.
397 bound_fns.Push(base::Bind(
398 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
400 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
403 void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
404 DVLOG(2) << __FUNCTION__;
405 DCHECK(task_runner_->BelongsToCurrentThread());
406 DCHECK(!pending_callbacks_.get());
408 // TODO(scherkus): Enforce that Renderer is only called on a single thread,
409 // even for accessing media time http://crbug.com/370634
410 scoped_ptr<Renderer> renderer;
412 base::AutoLock auto_lock(lock_);
413 renderer.swap(renderer_);
415 renderer.reset();
416 text_renderer_.reset();
418 if (demuxer_) {
419 demuxer_->Stop();
420 demuxer_ = NULL;
423 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK));
426 void Pipeline::OnStopCompleted(PipelineStatus status) {
427 DVLOG(2) << __FUNCTION__;
428 DCHECK(task_runner_->BelongsToCurrentThread());
429 DCHECK_EQ(state_, kStopping);
430 DCHECK(!renderer_);
431 DCHECK(!text_renderer_);
434 base::AutoLock auto_lock(lock_);
435 running_ = false;
438 SetState(kStopped);
439 demuxer_ = NULL;
441 // If we stop during initialization/seeking we want to run |seek_cb_|
442 // followed by |stop_cb_| so we don't leave outstanding callbacks around.
443 if (!seek_cb_.is_null()) {
444 base::ResetAndReturn(&seek_cb_).Run(status_);
445 error_cb_.Reset();
447 if (!stop_cb_.is_null()) {
448 error_cb_.Reset();
450 // Invalid all weak pointers so it's safe to destroy |this| on the render
451 // main thread.
452 weak_factory_.InvalidateWeakPtrs();
454 base::ResetAndReturn(&stop_cb_).Run();
456 // NOTE: pipeline may be deleted at this point in time as a result of
457 // executing |stop_cb_|.
458 return;
460 if (!error_cb_.is_null()) {
461 DCHECK_NE(status_, PIPELINE_OK);
462 base::ResetAndReturn(&error_cb_).Run(status_);
466 void Pipeline::AddBufferedTimeRange(TimeDelta start, TimeDelta end) {
467 DCHECK(IsRunning());
468 base::AutoLock auto_lock(lock_);
469 buffered_time_ranges_.Add(start, end);
470 did_loading_progress_ = true;
473 // Called from any thread.
474 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) {
475 base::AutoLock auto_lock(lock_);
476 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded;
477 statistics_.video_bytes_decoded += stats.video_bytes_decoded;
478 statistics_.video_frames_decoded += stats.video_frames_decoded;
479 statistics_.video_frames_dropped += stats.video_frames_dropped;
482 void Pipeline::StartTask() {
483 DCHECK(task_runner_->BelongsToCurrentThread());
485 CHECK_EQ(kCreated, state_)
486 << "Media pipeline cannot be started more than once";
488 text_renderer_ = CreateTextRenderer();
489 if (text_renderer_) {
490 text_renderer_->Initialize(
491 base::Bind(&Pipeline::OnTextRendererEnded, weak_factory_.GetWeakPtr()));
494 // Set CDM early to avoid unnecessary delay in Renderer::Initialize().
495 if (pending_cdm_context_) {
496 renderer_->SetCdm(pending_cdm_context_, base::Bind(&IgnoreCdmAttached));
497 pending_cdm_context_ = nullptr;
500 StateTransitionTask(PIPELINE_OK);
503 void Pipeline::StopTask(const base::Closure& stop_cb) {
504 DCHECK(task_runner_->BelongsToCurrentThread());
505 DCHECK(stop_cb_.is_null());
507 if (state_ == kStopped) {
508 // Invalid all weak pointers so it's safe to destroy |this| on the render
509 // main thread.
510 weak_factory_.InvalidateWeakPtrs();
512 // NOTE: pipeline may be deleted at this point in time as a result of
513 // executing |stop_cb|.
514 stop_cb.Run();
516 return;
519 stop_cb_ = stop_cb;
521 // We may already be stopping due to a runtime error.
522 if (state_ == kStopping)
523 return;
525 // Do not report statistics if the pipeline is not fully initialized.
526 if (state_ == kSeeking || state_ == kPlaying) {
527 PipelineStatistics stats = GetStatistics();
528 if (renderer_->HasVideo() && stats.video_frames_decoded > 0) {
529 UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount",
530 stats.video_frames_dropped);
534 SetState(kStopping);
535 pending_callbacks_.reset();
536 DoStop(base::Bind(&Pipeline::OnStopCompleted, weak_factory_.GetWeakPtr()));
539 void Pipeline::ErrorChangedTask(PipelineStatus error) {
540 DCHECK(task_runner_->BelongsToCurrentThread());
541 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
543 media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error));
545 if (state_ == kStopping || state_ == kStopped)
546 return;
548 SetState(kStopping);
549 pending_callbacks_.reset();
550 status_ = error;
552 DoStop(base::Bind(&Pipeline::OnStopCompleted, weak_factory_.GetWeakPtr()));
555 void Pipeline::PlaybackRateChangedTask(double playback_rate) {
556 DCHECK(task_runner_->BelongsToCurrentThread());
558 // Playback rate changes are only carried out while playing.
559 if (state_ != kPlaying)
560 return;
562 renderer_->SetPlaybackRate(playback_rate);
565 void Pipeline::VolumeChangedTask(float volume) {
566 DCHECK(task_runner_->BelongsToCurrentThread());
568 // Volume changes are only carried out while playing.
569 if (state_ != kPlaying)
570 return;
572 renderer_->SetVolume(volume);
575 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
576 DCHECK(task_runner_->BelongsToCurrentThread());
577 DCHECK(stop_cb_.is_null());
579 // Suppress seeking if we're not fully started.
580 if (state_ != kPlaying) {
581 DCHECK(state_ == kStopping || state_ == kStopped)
582 << "Receive extra seek in unexpected state: " << state_;
584 // TODO(scherkus): should we run the callback? I'm tempted to say the API
585 // will only execute the first Seek() request.
586 DVLOG(1) << "Media pipeline has not started, ignoring seek to "
587 << time.InMicroseconds() << " (current state: " << state_ << ")";
588 return;
591 DCHECK(seek_cb_.is_null());
593 const base::TimeDelta seek_timestamp =
594 std::max(time, demuxer_->GetStartTime());
596 SetState(kSeeking);
597 seek_cb_ = seek_cb;
598 renderer_ended_ = false;
599 text_renderer_ended_ = false;
600 start_timestamp_ = seek_timestamp;
602 DoSeek(seek_timestamp, base::Bind(&Pipeline::StateTransitionTask,
603 weak_factory_.GetWeakPtr()));
606 void Pipeline::SetCdmTask(CdmContext* cdm_context,
607 const CdmAttachedCB& cdm_attached_cb) {
608 base::AutoLock auto_lock(lock_);
609 if (!renderer_) {
610 pending_cdm_context_ = cdm_context;
611 cdm_attached_cb.Run(true);
612 return;
615 renderer_->SetCdm(cdm_context, cdm_attached_cb);
618 void Pipeline::OnRendererEnded() {
619 DCHECK(task_runner_->BelongsToCurrentThread());
620 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED));
622 if (state_ != kPlaying)
623 return;
625 DCHECK(!renderer_ended_);
626 renderer_ended_ = true;
628 RunEndedCallbackIfNeeded();
631 void Pipeline::OnTextRendererEnded() {
632 DCHECK(task_runner_->BelongsToCurrentThread());
633 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
635 if (state_ != kPlaying)
636 return;
638 DCHECK(!text_renderer_ended_);
639 text_renderer_ended_ = true;
641 RunEndedCallbackIfNeeded();
644 void Pipeline::RunEndedCallbackIfNeeded() {
645 DCHECK(task_runner_->BelongsToCurrentThread());
647 if (renderer_ && !renderer_ended_)
648 return;
650 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_)
651 return;
653 DCHECK_EQ(status_, PIPELINE_OK);
654 ended_cb_.Run();
657 scoped_ptr<TextRenderer> Pipeline::CreateTextRenderer() {
658 DCHECK(task_runner_->BelongsToCurrentThread());
660 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
661 if (!cmd_line->HasSwitch(switches::kEnableInbandTextTracks))
662 return scoped_ptr<media::TextRenderer>();
664 return scoped_ptr<media::TextRenderer>(new media::TextRenderer(
665 task_runner_,
666 base::Bind(&Pipeline::OnAddTextTrack, weak_factory_.GetWeakPtr())));
669 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream,
670 const TextTrackConfig& config) {
671 DCHECK(task_runner_->BelongsToCurrentThread());
672 // TODO(matthewjheaney): fix up text_ended_ when text stream
673 // is added (http://crbug.com/321446).
674 if (text_renderer_)
675 text_renderer_->AddTextStream(text_stream, config);
678 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) {
679 DCHECK(task_runner_->BelongsToCurrentThread());
680 if (text_renderer_)
681 text_renderer_->RemoveTextStream(text_stream);
684 void Pipeline::OnAddTextTrack(const TextTrackConfig& config,
685 const AddTextTrackDoneCB& done_cb) {
686 DCHECK(task_runner_->BelongsToCurrentThread());
687 add_text_track_cb_.Run(config, done_cb);
690 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
691 DCHECK(task_runner_->BelongsToCurrentThread());
692 demuxer_->Initialize(this, done_cb, text_renderer_);
695 void Pipeline::InitializeRenderer(const PipelineStatusCB& done_cb) {
696 DCHECK(task_runner_->BelongsToCurrentThread());
698 if (!demuxer_->GetStream(DemuxerStream::AUDIO) &&
699 !demuxer_->GetStream(DemuxerStream::VIDEO)) {
701 base::AutoLock auto_lock(lock_);
702 renderer_.reset();
704 OnError(PIPELINE_ERROR_COULD_NOT_RENDER);
705 return;
708 base::WeakPtr<Pipeline> weak_this = weak_factory_.GetWeakPtr();
709 renderer_->Initialize(
710 demuxer_,
711 done_cb,
712 base::Bind(&Pipeline::OnUpdateStatistics, weak_this),
713 base::Bind(&Pipeline::BufferingStateChanged, weak_this),
714 base::Bind(&Pipeline::OnRendererEnded, weak_this),
715 base::Bind(&Pipeline::OnError, weak_this),
716 waiting_for_decryption_key_cb_);
719 void Pipeline::ReportMetadata() {
720 DCHECK(task_runner_->BelongsToCurrentThread());
721 PipelineMetadata metadata;
722 metadata.timeline_offset = demuxer_->GetTimelineOffset();
723 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
724 if (stream) {
725 metadata.has_video = true;
726 metadata.natural_size = stream->video_decoder_config().natural_size();
727 metadata.video_rotation = stream->video_rotation();
729 if (demuxer_->GetStream(DemuxerStream::AUDIO)) {
730 metadata.has_audio = true;
732 metadata_cb_.Run(metadata);
735 void Pipeline::BufferingStateChanged(BufferingState new_buffering_state) {
736 DVLOG(1) << __FUNCTION__ << "(" << new_buffering_state << ") ";
737 DCHECK(task_runner_->BelongsToCurrentThread());
738 buffering_state_cb_.Run(new_buffering_state);
741 } // namespace media