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
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/callback_helpers.h"
12 #include "base/compiler_specific.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/synchronization/condition_variable.h"
19 #include "media/base/audio_decoder.h"
20 #include "media/base/audio_renderer.h"
21 #include "media/base/clock.h"
22 #include "media/base/filter_collection.h"
23 #include "media/base/media_log.h"
24 #include "media/base/text_renderer.h"
25 #include "media/base/text_track_config.h"
26 #include "media/base/video_decoder.h"
27 #include "media/base/video_decoder_config.h"
28 #include "media/base/video_renderer.h"
29
30 using base::TimeDelta;
31
32 namespace media {
33
Pipeline(const scoped_refptr<base::MessageLoopProxy> & message_loop,MediaLog * media_log)34 Pipeline::Pipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop,
35 MediaLog* media_log)
36 : message_loop_(message_loop),
37 media_log_(media_log),
38 running_(false),
39 did_loading_progress_(false),
40 total_bytes_(0),
41 natural_size_(0, 0),
42 volume_(1.0f),
43 playback_rate_(0.0f),
44 clock_(new Clock(&default_tick_clock_)),
45 waiting_for_clock_update_(false),
46 status_(PIPELINE_OK),
47 has_audio_(false),
48 has_video_(false),
49 state_(kCreated),
50 audio_ended_(false),
51 video_ended_(false),
52 text_ended_(false),
53 audio_disabled_(false),
54 demuxer_(NULL),
55 creation_time_(default_tick_clock_.NowTicks()) {
56 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
57 media_log_->AddEvent(
58 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED));
59 }
60
~Pipeline()61 Pipeline::~Pipeline() {
62 DCHECK(thread_checker_.CalledOnValidThread())
63 << "Pipeline must be destroyed on same thread that created it";
64 DCHECK(!running_) << "Stop() must complete before destroying object";
65 DCHECK(stop_cb_.is_null());
66 DCHECK(seek_cb_.is_null());
67
68 media_log_->AddEvent(
69 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED));
70 }
71
Start(scoped_ptr<FilterCollection> collection,const base::Closure & ended_cb,const PipelineStatusCB & error_cb,const PipelineStatusCB & seek_cb,const BufferingStateCB & buffering_state_cb,const base::Closure & duration_change_cb)72 void Pipeline::Start(scoped_ptr<FilterCollection> collection,
73 const base::Closure& ended_cb,
74 const PipelineStatusCB& error_cb,
75 const PipelineStatusCB& seek_cb,
76 const BufferingStateCB& buffering_state_cb,
77 const base::Closure& duration_change_cb) {
78 base::AutoLock auto_lock(lock_);
79 CHECK(!running_) << "Media pipeline is already running";
80 DCHECK(!buffering_state_cb.is_null());
81
82 running_ = true;
83 message_loop_->PostTask(FROM_HERE, base::Bind(
84 &Pipeline::StartTask, base::Unretained(this), base::Passed(&collection),
85 ended_cb, error_cb, seek_cb, buffering_state_cb, duration_change_cb));
86 }
87
Stop(const base::Closure & stop_cb)88 void Pipeline::Stop(const base::Closure& stop_cb) {
89 base::AutoLock auto_lock(lock_);
90 message_loop_->PostTask(FROM_HERE, base::Bind(
91 &Pipeline::StopTask, base::Unretained(this), stop_cb));
92 }
93
Seek(TimeDelta time,const PipelineStatusCB & seek_cb)94 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) {
95 base::AutoLock auto_lock(lock_);
96 if (!running_) {
97 NOTREACHED() << "Media pipeline isn't running";
98 return;
99 }
100
101 message_loop_->PostTask(FROM_HERE, base::Bind(
102 &Pipeline::SeekTask, base::Unretained(this), time, seek_cb));
103 }
104
IsRunning() const105 bool Pipeline::IsRunning() const {
106 base::AutoLock auto_lock(lock_);
107 return running_;
108 }
109
HasAudio() const110 bool Pipeline::HasAudio() const {
111 base::AutoLock auto_lock(lock_);
112 return has_audio_;
113 }
114
HasVideo() const115 bool Pipeline::HasVideo() const {
116 base::AutoLock auto_lock(lock_);
117 return has_video_;
118 }
119
GetPlaybackRate() const120 float Pipeline::GetPlaybackRate() const {
121 base::AutoLock auto_lock(lock_);
122 return playback_rate_;
123 }
124
SetPlaybackRate(float playback_rate)125 void Pipeline::SetPlaybackRate(float playback_rate) {
126 if (playback_rate < 0.0f)
127 return;
128
129 base::AutoLock auto_lock(lock_);
130 playback_rate_ = playback_rate;
131 if (running_) {
132 message_loop_->PostTask(FROM_HERE, base::Bind(
133 &Pipeline::PlaybackRateChangedTask, base::Unretained(this),
134 playback_rate));
135 }
136 }
137
GetVolume() const138 float Pipeline::GetVolume() const {
139 base::AutoLock auto_lock(lock_);
140 return volume_;
141 }
142
SetVolume(float volume)143 void Pipeline::SetVolume(float volume) {
144 if (volume < 0.0f || volume > 1.0f)
145 return;
146
147 base::AutoLock auto_lock(lock_);
148 volume_ = volume;
149 if (running_) {
150 message_loop_->PostTask(FROM_HERE, base::Bind(
151 &Pipeline::VolumeChangedTask, base::Unretained(this), volume));
152 }
153 }
154
GetMediaTime() const155 TimeDelta Pipeline::GetMediaTime() const {
156 base::AutoLock auto_lock(lock_);
157 return clock_->Elapsed();
158 }
159
GetBufferedTimeRanges()160 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() {
161 base::AutoLock auto_lock(lock_);
162 Ranges<TimeDelta> time_ranges;
163 for (size_t i = 0; i < buffered_time_ranges_.size(); ++i) {
164 time_ranges.Add(buffered_time_ranges_.start(i),
165 buffered_time_ranges_.end(i));
166 }
167 if (clock_->Duration() == TimeDelta() || total_bytes_ == 0)
168 return time_ranges;
169 for (size_t i = 0; i < buffered_byte_ranges_.size(); ++i) {
170 TimeDelta start = TimeForByteOffset_Locked(buffered_byte_ranges_.start(i));
171 TimeDelta end = TimeForByteOffset_Locked(buffered_byte_ranges_.end(i));
172 // Cap approximated buffered time at the length of the video.
173 end = std::min(end, clock_->Duration());
174 time_ranges.Add(start, end);
175 }
176
177 return time_ranges;
178 }
179
GetMediaDuration() const180 TimeDelta Pipeline::GetMediaDuration() const {
181 base::AutoLock auto_lock(lock_);
182 return clock_->Duration();
183 }
184
GetTotalBytes() const185 int64 Pipeline::GetTotalBytes() const {
186 base::AutoLock auto_lock(lock_);
187 return total_bytes_;
188 }
189
GetNaturalVideoSize(gfx::Size * out_size) const190 void Pipeline::GetNaturalVideoSize(gfx::Size* out_size) const {
191 CHECK(out_size);
192 base::AutoLock auto_lock(lock_);
193 *out_size = natural_size_;
194 }
195
DidLoadingProgress() const196 bool Pipeline::DidLoadingProgress() const {
197 base::AutoLock auto_lock(lock_);
198 bool ret = did_loading_progress_;
199 did_loading_progress_ = false;
200 return ret;
201 }
202
GetStatistics() const203 PipelineStatistics Pipeline::GetStatistics() const {
204 base::AutoLock auto_lock(lock_);
205 return statistics_;
206 }
207
SetClockForTesting(Clock * clock)208 void Pipeline::SetClockForTesting(Clock* clock) {
209 clock_.reset(clock);
210 }
211
SetErrorForTesting(PipelineStatus status)212 void Pipeline::SetErrorForTesting(PipelineStatus status) {
213 SetError(status);
214 }
215
SetState(State next_state)216 void Pipeline::SetState(State next_state) {
217 if (state_ != kStarted && next_state == kStarted &&
218 !creation_time_.is_null()) {
219 UMA_HISTOGRAM_TIMES("Media.TimeToPipelineStarted",
220 default_tick_clock_.NowTicks() - creation_time_);
221 creation_time_ = base::TimeTicks();
222 }
223
224 DVLOG(2) << GetStateString(state_) << " -> " << GetStateString(next_state);
225
226 state_ = next_state;
227 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
228 }
229
230 #define RETURN_STRING(state) case state: return #state;
231
GetStateString(State state)232 const char* Pipeline::GetStateString(State state) {
233 switch (state) {
234 RETURN_STRING(kCreated);
235 RETURN_STRING(kInitDemuxer);
236 RETURN_STRING(kInitAudioRenderer);
237 RETURN_STRING(kInitVideoRenderer);
238 RETURN_STRING(kInitPrerolling);
239 RETURN_STRING(kSeeking);
240 RETURN_STRING(kStarting);
241 RETURN_STRING(kStarted);
242 RETURN_STRING(kStopping);
243 RETURN_STRING(kStopped);
244 }
245 NOTREACHED();
246 return "INVALID";
247 }
248
249 #undef RETURN_STRING
250
GetNextState() const251 Pipeline::State Pipeline::GetNextState() const {
252 DCHECK(message_loop_->BelongsToCurrentThread());
253 DCHECK(stop_cb_.is_null())
254 << "State transitions don't happen when stopping";
255 DCHECK_EQ(status_, PIPELINE_OK)
256 << "State transitions don't happen when there's an error: " << status_;
257
258 switch (state_) {
259 case kCreated:
260 return kInitDemuxer;
261
262 case kInitDemuxer:
263 if (demuxer_->GetStream(DemuxerStream::AUDIO))
264 return kInitAudioRenderer;
265 if (demuxer_->GetStream(DemuxerStream::VIDEO))
266 return kInitVideoRenderer;
267 return kInitPrerolling;
268
269 case kInitAudioRenderer:
270 if (demuxer_->GetStream(DemuxerStream::VIDEO))
271 return kInitVideoRenderer;
272 return kInitPrerolling;
273
274 case kInitVideoRenderer:
275 return kInitPrerolling;
276
277 case kInitPrerolling:
278 return kStarting;
279
280 case kSeeking:
281 return kStarting;
282
283 case kStarting:
284 return kStarted;
285
286 case kStarted:
287 case kStopping:
288 case kStopped:
289 break;
290 }
291 NOTREACHED() << "State has no transition: " << state_;
292 return state_;
293 }
294
OnDemuxerError(PipelineStatus error)295 void Pipeline::OnDemuxerError(PipelineStatus error) {
296 SetError(error);
297 }
298
AddTextStream(DemuxerStream * text_stream,const TextTrackConfig & config)299 void Pipeline::AddTextStream(DemuxerStream* text_stream,
300 const TextTrackConfig& config) {
301 message_loop_->PostTask(FROM_HERE, base::Bind(
302 &Pipeline::AddTextStreamTask, base::Unretained(this),
303 text_stream, config));
304 }
305
RemoveTextStream(DemuxerStream * text_stream)306 void Pipeline::RemoveTextStream(DemuxerStream* text_stream) {
307 message_loop_->PostTask(FROM_HERE, base::Bind(
308 &Pipeline::RemoveTextStreamTask, base::Unretained(this),
309 text_stream));
310 }
311
SetError(PipelineStatus error)312 void Pipeline::SetError(PipelineStatus error) {
313 DCHECK(IsRunning());
314 DCHECK_NE(PIPELINE_OK, error);
315 VLOG(1) << "Media pipeline error: " << error;
316
317 message_loop_->PostTask(FROM_HERE, base::Bind(
318 &Pipeline::ErrorChangedTask, base::Unretained(this), error));
319
320 media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error));
321 }
322
OnAudioDisabled()323 void Pipeline::OnAudioDisabled() {
324 DCHECK(IsRunning());
325 message_loop_->PostTask(FROM_HERE, base::Bind(
326 &Pipeline::AudioDisabledTask, base::Unretained(this)));
327 media_log_->AddEvent(
328 media_log_->CreateEvent(MediaLogEvent::AUDIO_RENDERER_DISABLED));
329 }
330
OnAudioTimeUpdate(TimeDelta time,TimeDelta max_time)331 void Pipeline::OnAudioTimeUpdate(TimeDelta time, TimeDelta max_time) {
332 DCHECK_LE(time.InMicroseconds(), max_time.InMicroseconds());
333 DCHECK(IsRunning());
334 base::AutoLock auto_lock(lock_);
335
336 if (!has_audio_)
337 return;
338 if (waiting_for_clock_update_ && time < clock_->Elapsed())
339 return;
340
341 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see
342 // http://crbug.com/137973
343 if (state_ == kSeeking)
344 return;
345
346 clock_->SetTime(time, max_time);
347 StartClockIfWaitingForTimeUpdate_Locked();
348 }
349
OnVideoTimeUpdate(TimeDelta max_time)350 void Pipeline::OnVideoTimeUpdate(TimeDelta max_time) {
351 DCHECK(IsRunning());
352 base::AutoLock auto_lock(lock_);
353
354 if (has_audio_)
355 return;
356
357 // TODO(scherkus): |state_| should only be accessed on pipeline thread, see
358 // http://crbug.com/137973
359 if (state_ == kSeeking)
360 return;
361
362 DCHECK(!waiting_for_clock_update_);
363 clock_->SetMaxTime(max_time);
364 }
365
SetDuration(TimeDelta duration)366 void Pipeline::SetDuration(TimeDelta duration) {
367 DCHECK(IsRunning());
368 media_log_->AddEvent(
369 media_log_->CreateTimeEvent(
370 MediaLogEvent::DURATION_SET, "duration", duration));
371 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
372
373 base::AutoLock auto_lock(lock_);
374 clock_->SetDuration(duration);
375 if (!duration_change_cb_.is_null())
376 duration_change_cb_.Run();
377 }
378
SetTotalBytes(int64 total_bytes)379 void Pipeline::SetTotalBytes(int64 total_bytes) {
380 DCHECK(IsRunning());
381 media_log_->AddEvent(
382 media_log_->CreateStringEvent(
383 MediaLogEvent::TOTAL_BYTES_SET, "total_bytes",
384 base::Int64ToString(total_bytes)));
385 int64 total_mbytes = total_bytes >> 20;
386 if (total_mbytes > kint32max)
387 total_mbytes = kint32max;
388 UMA_HISTOGRAM_CUSTOM_COUNTS(
389 "Media.TotalMBytes", static_cast<int32>(total_mbytes), 1, kint32max, 50);
390
391 base::AutoLock auto_lock(lock_);
392 total_bytes_ = total_bytes;
393 }
394
TimeForByteOffset_Locked(int64 byte_offset) const395 TimeDelta Pipeline::TimeForByteOffset_Locked(int64 byte_offset) const {
396 lock_.AssertAcquired();
397 // Use floating point to avoid potential overflow when using 64 bit integers.
398 double time_offset_in_ms = clock_->Duration().InMilliseconds() *
399 (static_cast<double>(byte_offset) / total_bytes_);
400 TimeDelta time_offset(TimeDelta::FromMilliseconds(
401 static_cast<int64>(time_offset_in_ms)));
402 // Since the byte->time calculation is approximate, fudge the beginning &
403 // ending areas to look better.
404 TimeDelta epsilon = clock_->Duration() / 100;
405 if (time_offset < epsilon)
406 return TimeDelta();
407 if (time_offset + epsilon > clock_->Duration())
408 return clock_->Duration();
409 return time_offset;
410 }
411
OnStateTransition(PipelineStatus status)412 void Pipeline::OnStateTransition(PipelineStatus status) {
413 // Force post to process state transitions after current execution frame.
414 message_loop_->PostTask(FROM_HERE, base::Bind(
415 &Pipeline::StateTransitionTask, base::Unretained(this), status));
416 }
417
StateTransitionTask(PipelineStatus status)418 void Pipeline::StateTransitionTask(PipelineStatus status) {
419 DCHECK(message_loop_->BelongsToCurrentThread());
420
421 // No-op any state transitions if we're stopping.
422 if (state_ == kStopping || state_ == kStopped)
423 return;
424
425 // Preserve existing abnormal status, otherwise update based on the result of
426 // the previous operation.
427 status_ = (status_ != PIPELINE_OK ? status_ : status);
428
429 if (status_ != PIPELINE_OK) {
430 ErrorChangedTask(status_);
431 return;
432 }
433
434 // Guard against accidentally clearing |pending_callbacks_| for states that
435 // use it as well as states that should not be using it.
436 //
437 // TODO(scherkus): Make every state transition use |pending_callbacks_|.
438 DCHECK_EQ(pending_callbacks_.get() != NULL,
439 (state_ == kInitPrerolling || state_ == kStarting ||
440 state_ == kSeeking));
441 pending_callbacks_.reset();
442
443 PipelineStatusCB done_cb = base::Bind(
444 &Pipeline::OnStateTransition, base::Unretained(this));
445
446 // Switch states, performing any entrance actions for the new state as well.
447 SetState(GetNextState());
448 switch (state_) {
449 case kInitDemuxer:
450 return InitializeDemuxer(done_cb);
451
452 case kInitAudioRenderer:
453 return InitializeAudioRenderer(done_cb);
454
455 case kInitVideoRenderer:
456 return InitializeVideoRenderer(done_cb);
457
458 case kInitPrerolling:
459 filter_collection_.reset();
460 {
461 base::AutoLock l(lock_);
462 // We do not want to start the clock running. We only want to set the
463 // base media time so our timestamp calculations will be correct.
464 clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime());
465
466 // TODO(scherkus): |has_audio_| should be true no matter what --
467 // otherwise people with muted/disabled sound cards will make our
468 // default controls look as if every video doesn't contain an audio
469 // track.
470 has_audio_ = audio_renderer_ != NULL && !audio_disabled_;
471 has_video_ = video_renderer_ != NULL;
472 }
473 if (!audio_renderer_ && !video_renderer_) {
474 done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER);
475 return;
476 }
477
478 buffering_state_cb_.Run(kHaveMetadata);
479
480 return DoInitialPreroll(done_cb);
481
482 case kStarting:
483 return DoPlay(done_cb);
484
485 case kStarted:
486 {
487 base::AutoLock l(lock_);
488 // We use audio stream to update the clock. So if there is such a
489 // stream, we pause the clock until we receive a valid timestamp.
490 waiting_for_clock_update_ = true;
491 if (!has_audio_) {
492 clock_->SetMaxTime(clock_->Duration());
493 StartClockIfWaitingForTimeUpdate_Locked();
494 }
495 }
496
497 DCHECK(!seek_cb_.is_null());
498 DCHECK_EQ(status_, PIPELINE_OK);
499
500 // Fire canplaythrough immediately after playback begins because of
501 // crbug.com/106480.
502 // TODO(vrk): set ready state to HaveFutureData when bug above is fixed.
503 buffering_state_cb_.Run(kPrerollCompleted);
504 return base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
505
506 case kStopping:
507 case kStopped:
508 case kCreated:
509 case kSeeking:
510 NOTREACHED() << "State has no transition: " << state_;
511 return;
512 }
513 }
514
515 // Note that the usage of base::Unretained() with the audio/video renderers
516 // in the following DoXXX() functions is considered safe as they are owned by
517 // |pending_callbacks_| and share the same lifetime.
518 //
519 // That being said, deleting the renderers while keeping |pending_callbacks_|
520 // running on the media thread would result in crashes.
DoInitialPreroll(const PipelineStatusCB & done_cb)521 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
522 DCHECK(message_loop_->BelongsToCurrentThread());
523 DCHECK(!pending_callbacks_.get());
524 SerialRunner::Queue bound_fns;
525
526 base::TimeDelta seek_timestamp = demuxer_->GetStartTime();
527
528 // Preroll renderers.
529 if (audio_renderer_) {
530 bound_fns.Push(base::Bind(
531 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
532 seek_timestamp));
533 }
534
535 if (video_renderer_) {
536 bound_fns.Push(base::Bind(
537 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
538 seek_timestamp));
539 }
540
541 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
542 }
543
DoSeek(base::TimeDelta seek_timestamp,const PipelineStatusCB & done_cb)544 void Pipeline::DoSeek(
545 base::TimeDelta seek_timestamp,
546 const PipelineStatusCB& done_cb) {
547 DCHECK(message_loop_->BelongsToCurrentThread());
548 DCHECK(!pending_callbacks_.get());
549 SerialRunner::Queue bound_fns;
550
551 // Pause.
552 if (audio_renderer_) {
553 bound_fns.Push(base::Bind(
554 &AudioRenderer::Pause, base::Unretained(audio_renderer_.get())));
555 }
556 if (video_renderer_) {
557 bound_fns.Push(base::Bind(
558 &VideoRenderer::Pause, base::Unretained(video_renderer_.get())));
559 }
560 if (text_renderer_) {
561 bound_fns.Push(base::Bind(
562 &TextRenderer::Pause, base::Unretained(text_renderer_.get())));
563 }
564
565 // Flush.
566 if (audio_renderer_) {
567 bound_fns.Push(base::Bind(
568 &AudioRenderer::Flush, base::Unretained(audio_renderer_.get())));
569 }
570 if (video_renderer_) {
571 bound_fns.Push(base::Bind(
572 &VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
573 }
574 if (text_renderer_) {
575 bound_fns.Push(base::Bind(
576 &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
577 }
578
579 // Seek demuxer.
580 bound_fns.Push(base::Bind(
581 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
582
583 // Preroll renderers.
584 if (audio_renderer_) {
585 bound_fns.Push(base::Bind(
586 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
587 seek_timestamp));
588 }
589
590 if (video_renderer_) {
591 bound_fns.Push(base::Bind(
592 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
593 seek_timestamp));
594 }
595
596 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
597 }
598
DoPlay(const PipelineStatusCB & done_cb)599 void Pipeline::DoPlay(const PipelineStatusCB& done_cb) {
600 DCHECK(message_loop_->BelongsToCurrentThread());
601 DCHECK(!pending_callbacks_.get());
602 SerialRunner::Queue bound_fns;
603
604 PlaybackRateChangedTask(GetPlaybackRate());
605 VolumeChangedTask(GetVolume());
606
607 if (audio_renderer_) {
608 bound_fns.Push(base::Bind(
609 &AudioRenderer::Play, base::Unretained(audio_renderer_.get())));
610 }
611
612 if (video_renderer_) {
613 bound_fns.Push(base::Bind(
614 &VideoRenderer::Play, base::Unretained(video_renderer_.get())));
615 }
616
617 if (text_renderer_) {
618 bound_fns.Push(base::Bind(
619 &TextRenderer::Play, base::Unretained(text_renderer_.get())));
620 }
621
622 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
623 }
624
DoStop(const PipelineStatusCB & done_cb)625 void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
626 DCHECK(message_loop_->BelongsToCurrentThread());
627 DCHECK(!pending_callbacks_.get());
628 SerialRunner::Queue bound_fns;
629
630 if (demuxer_) {
631 bound_fns.Push(base::Bind(
632 &Demuxer::Stop, base::Unretained(demuxer_)));
633 }
634
635 if (audio_renderer_) {
636 bound_fns.Push(base::Bind(
637 &AudioRenderer::Stop, base::Unretained(audio_renderer_.get())));
638 }
639
640 if (video_renderer_) {
641 bound_fns.Push(base::Bind(
642 &VideoRenderer::Stop, base::Unretained(video_renderer_.get())));
643 }
644
645 if (text_renderer_) {
646 bound_fns.Push(base::Bind(
647 &TextRenderer::Stop, base::Unretained(text_renderer_.get())));
648 }
649
650 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
651 }
652
OnStopCompleted(PipelineStatus status)653 void Pipeline::OnStopCompleted(PipelineStatus status) {
654 DCHECK(message_loop_->BelongsToCurrentThread());
655 DCHECK_EQ(state_, kStopping);
656 {
657 base::AutoLock l(lock_);
658 running_ = false;
659 }
660
661 SetState(kStopped);
662 pending_callbacks_.reset();
663 filter_collection_.reset();
664 audio_renderer_.reset();
665 video_renderer_.reset();
666 text_renderer_.reset();
667 demuxer_ = NULL;
668
669 // If we stop during initialization/seeking we want to run |seek_cb_|
670 // followed by |stop_cb_| so we don't leave outstanding callbacks around.
671 if (!seek_cb_.is_null()) {
672 base::ResetAndReturn(&seek_cb_).Run(status_);
673 error_cb_.Reset();
674 }
675 if (!stop_cb_.is_null()) {
676 error_cb_.Reset();
677 base::ResetAndReturn(&stop_cb_).Run();
678
679 // NOTE: pipeline may be deleted at this point in time as a result of
680 // executing |stop_cb_|.
681 return;
682 }
683 if (!error_cb_.is_null()) {
684 DCHECK_NE(status_, PIPELINE_OK);
685 base::ResetAndReturn(&error_cb_).Run(status_);
686 }
687 }
688
AddBufferedByteRange(int64 start,int64 end)689 void Pipeline::AddBufferedByteRange(int64 start, int64 end) {
690 DCHECK(IsRunning());
691 base::AutoLock auto_lock(lock_);
692 buffered_byte_ranges_.Add(start, end);
693 did_loading_progress_ = true;
694 }
695
AddBufferedTimeRange(base::TimeDelta start,base::TimeDelta end)696 void Pipeline::AddBufferedTimeRange(base::TimeDelta start,
697 base::TimeDelta end) {
698 DCHECK(IsRunning());
699 base::AutoLock auto_lock(lock_);
700 buffered_time_ranges_.Add(start, end);
701 did_loading_progress_ = true;
702 }
703
OnNaturalVideoSizeChanged(const gfx::Size & size)704 void Pipeline::OnNaturalVideoSizeChanged(const gfx::Size& size) {
705 DCHECK(IsRunning());
706 media_log_->AddEvent(media_log_->CreateVideoSizeSetEvent(
707 size.width(), size.height()));
708
709 base::AutoLock auto_lock(lock_);
710 natural_size_ = size;
711 }
712
OnAudioRendererEnded()713 void Pipeline::OnAudioRendererEnded() {
714 // Force post to process ended messages after current execution frame.
715 message_loop_->PostTask(FROM_HERE, base::Bind(
716 &Pipeline::DoAudioRendererEnded, base::Unretained(this)));
717 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::AUDIO_ENDED));
718 }
719
OnVideoRendererEnded()720 void Pipeline::OnVideoRendererEnded() {
721 // Force post to process ended messages after current execution frame.
722 message_loop_->PostTask(FROM_HERE, base::Bind(
723 &Pipeline::DoVideoRendererEnded, base::Unretained(this)));
724 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED));
725 }
726
OnTextRendererEnded()727 void Pipeline::OnTextRendererEnded() {
728 // Force post to process ended messages after current execution frame.
729 message_loop_->PostTask(FROM_HERE, base::Bind(
730 &Pipeline::DoTextRendererEnded, base::Unretained(this)));
731 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
732 }
733
734 // Called from any thread.
OnUpdateStatistics(const PipelineStatistics & stats)735 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) {
736 base::AutoLock auto_lock(lock_);
737 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded;
738 statistics_.video_bytes_decoded += stats.video_bytes_decoded;
739 statistics_.video_frames_decoded += stats.video_frames_decoded;
740 statistics_.video_frames_dropped += stats.video_frames_dropped;
741 }
742
StartTask(scoped_ptr<FilterCollection> filter_collection,const base::Closure & ended_cb,const PipelineStatusCB & error_cb,const PipelineStatusCB & seek_cb,const BufferingStateCB & buffering_state_cb,const base::Closure & duration_change_cb)743 void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection,
744 const base::Closure& ended_cb,
745 const PipelineStatusCB& error_cb,
746 const PipelineStatusCB& seek_cb,
747 const BufferingStateCB& buffering_state_cb,
748 const base::Closure& duration_change_cb) {
749 DCHECK(message_loop_->BelongsToCurrentThread());
750 CHECK_EQ(kCreated, state_)
751 << "Media pipeline cannot be started more than once";
752
753 filter_collection_ = filter_collection.Pass();
754 ended_cb_ = ended_cb;
755 error_cb_ = error_cb;
756 seek_cb_ = seek_cb;
757 buffering_state_cb_ = buffering_state_cb;
758 duration_change_cb_ = duration_change_cb;
759
760 text_renderer_ = filter_collection_->GetTextRenderer();
761
762 if (text_renderer_) {
763 text_renderer_->Initialize(
764 base::Bind(&Pipeline::OnTextRendererEnded, base::Unretained(this)));
765 }
766
767 StateTransitionTask(PIPELINE_OK);
768 }
769
StopTask(const base::Closure & stop_cb)770 void Pipeline::StopTask(const base::Closure& stop_cb) {
771 DCHECK(message_loop_->BelongsToCurrentThread());
772 DCHECK(stop_cb_.is_null());
773
774 if (state_ == kStopped) {
775 stop_cb.Run();
776 return;
777 }
778
779 stop_cb_ = stop_cb;
780
781 // We may already be stopping due to a runtime error.
782 if (state_ == kStopping)
783 return;
784
785 SetState(kStopping);
786 pending_callbacks_.reset();
787 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
788 }
789
ErrorChangedTask(PipelineStatus error)790 void Pipeline::ErrorChangedTask(PipelineStatus error) {
791 DCHECK(message_loop_->BelongsToCurrentThread());
792 DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
793
794 if (state_ == kStopping || state_ == kStopped)
795 return;
796
797 SetState(kStopping);
798 pending_callbacks_.reset();
799 status_ = error;
800
801 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this)));
802 }
803
PlaybackRateChangedTask(float playback_rate)804 void Pipeline::PlaybackRateChangedTask(float playback_rate) {
805 DCHECK(message_loop_->BelongsToCurrentThread());
806
807 // Playback rate changes are only carried out while playing.
808 if (state_ != kStarting && state_ != kStarted)
809 return;
810
811 {
812 base::AutoLock auto_lock(lock_);
813 clock_->SetPlaybackRate(playback_rate);
814 }
815
816 if (audio_renderer_)
817 audio_renderer_->SetPlaybackRate(playback_rate_);
818 if (video_renderer_)
819 video_renderer_->SetPlaybackRate(playback_rate_);
820 }
821
VolumeChangedTask(float volume)822 void Pipeline::VolumeChangedTask(float volume) {
823 DCHECK(message_loop_->BelongsToCurrentThread());
824
825 // Volume changes are only carried out while playing.
826 if (state_ != kStarting && state_ != kStarted)
827 return;
828
829 if (audio_renderer_)
830 audio_renderer_->SetVolume(volume);
831 }
832
SeekTask(TimeDelta time,const PipelineStatusCB & seek_cb)833 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
834 DCHECK(message_loop_->BelongsToCurrentThread());
835 DCHECK(stop_cb_.is_null());
836
837 // Suppress seeking if we're not fully started.
838 if (state_ != kStarted) {
839 DCHECK(state_ == kStopping || state_ == kStopped)
840 << "Receive extra seek in unexpected state: " << state_;
841
842 // TODO(scherkus): should we run the callback? I'm tempted to say the API
843 // will only execute the first Seek() request.
844 DVLOG(1) << "Media pipeline has not started, ignoring seek to "
845 << time.InMicroseconds() << " (current state: " << state_ << ")";
846 return;
847 }
848
849 DCHECK(seek_cb_.is_null());
850
851 SetState(kSeeking);
852 base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime());
853 seek_cb_ = seek_cb;
854 audio_ended_ = false;
855 video_ended_ = false;
856 text_ended_ = false;
857
858 // Kick off seeking!
859 {
860 base::AutoLock auto_lock(lock_);
861 if (clock_->IsPlaying())
862 clock_->Pause();
863 clock_->SetTime(seek_timestamp, seek_timestamp);
864 }
865 DoSeek(seek_timestamp, base::Bind(
866 &Pipeline::OnStateTransition, base::Unretained(this)));
867 }
868
DoAudioRendererEnded()869 void Pipeline::DoAudioRendererEnded() {
870 DCHECK(message_loop_->BelongsToCurrentThread());
871
872 if (state_ != kStarted)
873 return;
874
875 DCHECK(!audio_ended_);
876 audio_ended_ = true;
877
878 // Start clock since there is no more audio to trigger clock updates.
879 if (!audio_disabled_) {
880 base::AutoLock auto_lock(lock_);
881 clock_->SetMaxTime(clock_->Duration());
882 StartClockIfWaitingForTimeUpdate_Locked();
883 }
884
885 RunEndedCallbackIfNeeded();
886 }
887
DoVideoRendererEnded()888 void Pipeline::DoVideoRendererEnded() {
889 DCHECK(message_loop_->BelongsToCurrentThread());
890
891 if (state_ != kStarted)
892 return;
893
894 DCHECK(!video_ended_);
895 video_ended_ = true;
896
897 RunEndedCallbackIfNeeded();
898 }
899
DoTextRendererEnded()900 void Pipeline::DoTextRendererEnded() {
901 DCHECK(message_loop_->BelongsToCurrentThread());
902
903 if (state_ != kStarted)
904 return;
905
906 DCHECK(!text_ended_);
907 text_ended_ = true;
908
909 RunEndedCallbackIfNeeded();
910 }
911
RunEndedCallbackIfNeeded()912 void Pipeline::RunEndedCallbackIfNeeded() {
913 DCHECK(message_loop_->BelongsToCurrentThread());
914
915 if (audio_renderer_ && !audio_ended_ && !audio_disabled_)
916 return;
917
918 if (video_renderer_ && !video_ended_)
919 return;
920
921 if (text_renderer_ && text_renderer_->HasTracks() && !text_ended_)
922 return;
923
924 {
925 base::AutoLock auto_lock(lock_);
926 clock_->EndOfStream();
927 }
928
929 DCHECK_EQ(status_, PIPELINE_OK);
930 ended_cb_.Run();
931 }
932
AudioDisabledTask()933 void Pipeline::AudioDisabledTask() {
934 DCHECK(message_loop_->BelongsToCurrentThread());
935
936 base::AutoLock auto_lock(lock_);
937 has_audio_ = false;
938 audio_disabled_ = true;
939
940 // Notify our demuxer that we're no longer rendering audio.
941 demuxer_->OnAudioRendererDisabled();
942
943 // Start clock since there is no more audio to trigger clock updates.
944 clock_->SetMaxTime(clock_->Duration());
945 StartClockIfWaitingForTimeUpdate_Locked();
946 }
947
AddTextStreamTask(DemuxerStream * text_stream,const TextTrackConfig & config)948 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream,
949 const TextTrackConfig& config) {
950 DCHECK(message_loop_->BelongsToCurrentThread());
951 // TODO(matthewjheaney): fix up text_ended_ when text stream
952 // is added (http://crbug.com/321446).
953 text_renderer_->AddTextStream(text_stream, config);
954 }
955
RemoveTextStreamTask(DemuxerStream * text_stream)956 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) {
957 DCHECK(message_loop_->BelongsToCurrentThread());
958 text_renderer_->RemoveTextStream(text_stream);
959 }
960
InitializeDemuxer(const PipelineStatusCB & done_cb)961 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
962 DCHECK(message_loop_->BelongsToCurrentThread());
963
964 demuxer_ = filter_collection_->GetDemuxer();
965 demuxer_->Initialize(this, done_cb, text_renderer_);
966 }
967
InitializeAudioRenderer(const PipelineStatusCB & done_cb)968 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) {
969 DCHECK(message_loop_->BelongsToCurrentThread());
970
971 audio_renderer_ = filter_collection_->GetAudioRenderer();
972 audio_renderer_->Initialize(
973 demuxer_->GetStream(DemuxerStream::AUDIO),
974 done_cb,
975 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
976 base::Bind(&Pipeline::OnAudioUnderflow, base::Unretained(this)),
977 base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)),
978 base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)),
979 base::Bind(&Pipeline::OnAudioDisabled, base::Unretained(this)),
980 base::Bind(&Pipeline::SetError, base::Unretained(this)));
981 }
982
InitializeVideoRenderer(const PipelineStatusCB & done_cb)983 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) {
984 DCHECK(message_loop_->BelongsToCurrentThread());
985
986 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
987
988 {
989 // Get an initial natural size so we have something when we signal
990 // the kHaveMetadata buffering state.
991 base::AutoLock l(lock_);
992 natural_size_ = stream->video_decoder_config().natural_size();
993 }
994
995 video_renderer_ = filter_collection_->GetVideoRenderer();
996 video_renderer_->Initialize(
997 stream,
998 done_cb,
999 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
1000 base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)),
1001 base::Bind(&Pipeline::OnNaturalVideoSizeChanged, base::Unretained(this)),
1002 base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)),
1003 base::Bind(&Pipeline::SetError, base::Unretained(this)),
1004 base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)),
1005 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this)));
1006 }
1007
OnAudioUnderflow()1008 void Pipeline::OnAudioUnderflow() {
1009 if (!message_loop_->BelongsToCurrentThread()) {
1010 message_loop_->PostTask(FROM_HERE, base::Bind(
1011 &Pipeline::OnAudioUnderflow, base::Unretained(this)));
1012 return;
1013 }
1014
1015 if (state_ != kStarted)
1016 return;
1017
1018 if (audio_renderer_)
1019 audio_renderer_->ResumeAfterUnderflow();
1020 }
1021
StartClockIfWaitingForTimeUpdate_Locked()1022 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
1023 lock_.AssertAcquired();
1024 if (!waiting_for_clock_update_)
1025 return;
1026
1027 waiting_for_clock_update_ = false;
1028 clock_->Play();
1029 }
1030
1031 } // namespace media
1032