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