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

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

Issue 418143005: media: Introduce Renderer interface and RendererImpl. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase only Created 6 years, 4 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/base/pipeline.h" 5 #include "media/base/pipeline.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback.h" 10 #include "base/callback.h"
11 #include "base/callback_helpers.h" 11 #include "base/callback_helpers.h"
12 #include "base/compiler_specific.h" 12 #include "base/compiler_specific.h"
13 #include "base/location.h" 13 #include "base/location.h"
14 #include "base/metrics/histogram.h" 14 #include "base/metrics/histogram.h"
15 #include "base/single_thread_task_runner.h" 15 #include "base/single_thread_task_runner.h"
16 #include "base/stl_util.h" 16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h" 17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h" 18 #include "base/strings/string_util.h"
19 #include "base/synchronization/condition_variable.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/filter_collection.h" 20 #include "media/base/filter_collection.h"
23 #include "media/base/media_log.h" 21 #include "media/base/media_log.h"
22 #include "media/base/renderer.h"
24 #include "media/base/text_renderer.h" 23 #include "media/base/text_renderer.h"
25 #include "media/base/text_track_config.h" 24 #include "media/base/text_track_config.h"
26 #include "media/base/time_delta_interpolator.h"
27 #include "media/base/time_source.h"
28 #include "media/base/video_decoder.h"
29 #include "media/base/video_decoder_config.h" 25 #include "media/base/video_decoder_config.h"
30 #include "media/base/video_renderer.h"
31 26
32 using base::TimeDelta; 27 using base::TimeDelta;
33 28
34 namespace media { 29 namespace media {
35 30
36 Pipeline::Pipeline( 31 Pipeline::Pipeline(
37 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 32 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
38 MediaLog* media_log) 33 MediaLog* media_log)
39 : task_runner_(task_runner), 34 : task_runner_(task_runner),
40 media_log_(media_log), 35 media_log_(media_log),
41 running_(false), 36 running_(false),
42 did_loading_progress_(false), 37 did_loading_progress_(false),
43 volume_(1.0f), 38 volume_(1.0f),
44 playback_rate_(0.0f), 39 playback_rate_(0.0f),
45 interpolator_(new TimeDeltaInterpolator(&default_tick_clock_)),
46 interpolation_state_(INTERPOLATION_STOPPED),
47 status_(PIPELINE_OK), 40 status_(PIPELINE_OK),
41 is_initialized_(false),
48 state_(kCreated), 42 state_(kCreated),
49 audio_ended_(false), 43 renderer_ended_(false),
50 video_ended_(false), 44 text_renderer_ended_(false),
51 text_ended_(false),
52 audio_buffering_state_(BUFFERING_HAVE_NOTHING),
53 video_buffering_state_(BUFFERING_HAVE_NOTHING),
54 demuxer_(NULL), 45 demuxer_(NULL),
55 time_source_(NULL),
56 underflow_disabled_for_testing_(false), 46 underflow_disabled_for_testing_(false),
47 test_interpolator_(NULL),
57 weak_factory_(this) { 48 weak_factory_(this) {
58 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); 49 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
59 media_log_->AddEvent( 50 media_log_->AddEvent(
60 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED)); 51 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED));
61 interpolator_->SetBounds(base::TimeDelta(), base::TimeDelta());
62 } 52 }
63 53
64 Pipeline::~Pipeline() { 54 Pipeline::~Pipeline() {
65 DCHECK(thread_checker_.CalledOnValidThread()) 55 DCHECK(thread_checker_.CalledOnValidThread())
66 << "Pipeline must be destroyed on same thread that created it"; 56 << "Pipeline must be destroyed on same thread that created it";
67 DCHECK(!running_) << "Stop() must complete before destroying object"; 57 DCHECK(!running_) << "Stop() must complete before destroying object";
68 DCHECK(stop_cb_.is_null()); 58 DCHECK(stop_cb_.is_null());
69 DCHECK(seek_cb_.is_null()); 59 DCHECK(seek_cb_.is_null());
70 60
71 media_log_->AddEvent( 61 media_log_->AddEvent(
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 if (running_) { 149 if (running_) {
160 task_runner_->PostTask( 150 task_runner_->PostTask(
161 FROM_HERE, 151 FROM_HERE,
162 base::Bind( 152 base::Bind(
163 &Pipeline::VolumeChangedTask, weak_factory_.GetWeakPtr(), volume)); 153 &Pipeline::VolumeChangedTask, weak_factory_.GetWeakPtr(), volume));
164 } 154 }
165 } 155 }
166 156
167 TimeDelta Pipeline::GetMediaTime() const { 157 TimeDelta Pipeline::GetMediaTime() const {
168 base::AutoLock auto_lock(lock_); 158 base::AutoLock auto_lock(lock_);
169 return std::min(interpolator_->GetInterpolatedTime(), duration_); 159 return renderer_ ? std::min(renderer_->GetMediaTime(), duration_)
160 : TimeDelta();
170 } 161 }
171 162
172 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() const { 163 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() const {
173 base::AutoLock auto_lock(lock_); 164 base::AutoLock auto_lock(lock_);
174 return buffered_time_ranges_; 165 return buffered_time_ranges_;
175 } 166 }
176 167
177 TimeDelta Pipeline::GetMediaDuration() const { 168 TimeDelta Pipeline::GetMediaDuration() const {
178 base::AutoLock auto_lock(lock_); 169 base::AutoLock auto_lock(lock_);
179 return duration_; 170 return duration_;
180 } 171 }
181 172
182 bool Pipeline::DidLoadingProgress() { 173 bool Pipeline::DidLoadingProgress() {
183 base::AutoLock auto_lock(lock_); 174 base::AutoLock auto_lock(lock_);
184 bool ret = did_loading_progress_; 175 bool ret = did_loading_progress_;
185 did_loading_progress_ = false; 176 did_loading_progress_ = false;
186 return ret; 177 return ret;
187 } 178 }
188 179
189 PipelineStatistics Pipeline::GetStatistics() const { 180 PipelineStatistics Pipeline::GetStatistics() const {
190 base::AutoLock auto_lock(lock_); 181 base::AutoLock auto_lock(lock_);
191 return statistics_; 182 return statistics_;
192 } 183 }
193 184
185 void Pipeline::DisableUnderflowForTesting() {
186 DCHECK(!renderer_);
187 underflow_disabled_for_testing_ = true;
188 }
189
194 void Pipeline::SetTimeDeltaInterpolatorForTesting( 190 void Pipeline::SetTimeDeltaInterpolatorForTesting(
195 TimeDeltaInterpolator* interpolator) { 191 TimeDeltaInterpolator* interpolator) {
196 interpolator_.reset(interpolator); 192 DCHECK(!renderer_);
193 DCHECK(interpolator);
194 test_interpolator_ = interpolator;
197 } 195 }
198 196
199 void Pipeline::SetErrorForTesting(PipelineStatus status) { 197 void Pipeline::SetErrorForTesting(PipelineStatus status) {
200 OnError(status); 198 OnError(status);
201 } 199 }
202 200
203 bool Pipeline::HasWeakPtrsForTesting() const { 201 bool Pipeline::HasWeakPtrsForTesting() const {
204 DCHECK(task_runner_->BelongsToCurrentThread()); 202 DCHECK(task_runner_->BelongsToCurrentThread());
205 return weak_factory_.HasWeakPtrs(); 203 return weak_factory_.HasWeakPtrs();
206 } 204 }
207 205
208 void Pipeline::SetState(State next_state) { 206 void Pipeline::SetState(State next_state) {
209 DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state); 207 DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state);
210 208
211 state_ = next_state; 209 state_ = next_state;
212 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); 210 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
213 } 211 }
214 212
215 #define RETURN_STRING(state) case state: return #state; 213 #define RETURN_STRING(state) case state: return #state;
216 214
217 const char* Pipeline::GetStateString(State state) { 215 const char* Pipeline::GetStateString(State state) {
218 switch (state) { 216 switch (state) {
219 RETURN_STRING(kCreated); 217 RETURN_STRING(kCreated);
220 RETURN_STRING(kInitDemuxer); 218 RETURN_STRING(kInitDemuxer);
221 RETURN_STRING(kInitAudioRenderer); 219 RETURN_STRING(kInitRenderer);
222 RETURN_STRING(kInitVideoRenderer);
223 RETURN_STRING(kSeeking); 220 RETURN_STRING(kSeeking);
224 RETURN_STRING(kPlaying); 221 RETURN_STRING(kPlaying);
225 RETURN_STRING(kStopping); 222 RETURN_STRING(kStopping);
226 RETURN_STRING(kStopped); 223 RETURN_STRING(kStopped);
227 } 224 }
228 NOTREACHED(); 225 NOTREACHED();
229 return "INVALID"; 226 return "INVALID";
230 } 227 }
231 228
232 #undef RETURN_STRING 229 #undef RETURN_STRING
233 230
234 Pipeline::State Pipeline::GetNextState() const { 231 Pipeline::State Pipeline::GetNextState() const {
235 DCHECK(task_runner_->BelongsToCurrentThread()); 232 DCHECK(task_runner_->BelongsToCurrentThread());
236 DCHECK(stop_cb_.is_null()) 233 DCHECK(stop_cb_.is_null())
237 << "State transitions don't happen when stopping"; 234 << "State transitions don't happen when stopping";
238 DCHECK_EQ(status_, PIPELINE_OK) 235 DCHECK_EQ(status_, PIPELINE_OK)
239 << "State transitions don't happen when there's an error: " << status_; 236 << "State transitions don't happen when there's an error: " << status_;
240 237
241 switch (state_) { 238 switch (state_) {
242 case kCreated: 239 case kCreated:
243 return kInitDemuxer; 240 return kInitDemuxer;
244 241
245 case kInitDemuxer: 242 case kInitDemuxer:
246 if (demuxer_->GetStream(DemuxerStream::AUDIO)) 243 if (demuxer_->GetStream(DemuxerStream::AUDIO) ||
247 return kInitAudioRenderer; 244 demuxer_->GetStream(DemuxerStream::VIDEO)) {
248 if (demuxer_->GetStream(DemuxerStream::VIDEO)) 245 return kInitRenderer;
249 return kInitVideoRenderer; 246 }
250 return kPlaying; 247 return kPlaying;
251 248
252 case kInitAudioRenderer: 249 case kInitRenderer:
253 if (demuxer_->GetStream(DemuxerStream::VIDEO))
254 return kInitVideoRenderer;
255 return kPlaying;
256
257 case kInitVideoRenderer:
258 return kPlaying;
259
260 case kSeeking: 250 case kSeeking:
261 return kPlaying; 251 return kPlaying;
262 252
263 case kPlaying: 253 case kPlaying:
264 case kStopping: 254 case kStopping:
265 case kStopped: 255 case kStopped:
266 break; 256 break;
267 } 257 }
268 NOTREACHED() << "State has no transition: " << state_; 258 NOTREACHED() << "State has no transition: " << state_;
269 return state_; 259 return state_;
(...skipping 25 matching lines...) Expand all
295 void Pipeline::OnError(PipelineStatus error) { 285 void Pipeline::OnError(PipelineStatus error) {
296 DCHECK(task_runner_->BelongsToCurrentThread()); 286 DCHECK(task_runner_->BelongsToCurrentThread());
297 DCHECK(IsRunning()); 287 DCHECK(IsRunning());
298 DCHECK_NE(PIPELINE_OK, error); 288 DCHECK_NE(PIPELINE_OK, error);
299 VLOG(1) << "Media pipeline error: " << error; 289 VLOG(1) << "Media pipeline error: " << error;
300 290
301 task_runner_->PostTask(FROM_HERE, base::Bind( 291 task_runner_->PostTask(FROM_HERE, base::Bind(
302 &Pipeline::ErrorChangedTask, weak_factory_.GetWeakPtr(), error)); 292 &Pipeline::ErrorChangedTask, weak_factory_.GetWeakPtr(), error));
303 } 293 }
304 294
305 void Pipeline::OnAudioTimeUpdate(TimeDelta time, TimeDelta max_time) {
306 DCHECK(task_runner_->BelongsToCurrentThread());
307 DCHECK_LE(time.InMicroseconds(), max_time.InMicroseconds());
308 base::AutoLock auto_lock(lock_);
309
310 if (interpolation_state_ == INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE &&
311 time < interpolator_->GetInterpolatedTime()) {
312 return;
313 }
314
315 if (state_ == kSeeking)
316 return;
317
318 interpolator_->SetBounds(time, max_time);
319 StartClockIfWaitingForTimeUpdate_Locked();
320 }
321
322 void Pipeline::OnVideoTimeUpdate(TimeDelta max_time) {
323 DCHECK(task_runner_->BelongsToCurrentThread());
324
325 if (audio_renderer_)
326 return;
327
328 if (state_ == kSeeking)
329 return;
330
331 base::AutoLock auto_lock(lock_);
332 DCHECK_NE(interpolation_state_, INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE);
333 interpolator_->SetUpperBound(max_time);
334 }
335
336 void Pipeline::SetDuration(TimeDelta duration) { 295 void Pipeline::SetDuration(TimeDelta duration) {
337 DCHECK(IsRunning()); 296 DCHECK(IsRunning());
338 media_log_->AddEvent( 297 media_log_->AddEvent(
339 media_log_->CreateTimeEvent( 298 media_log_->CreateTimeEvent(
340 MediaLogEvent::DURATION_SET, "duration", duration)); 299 MediaLogEvent::DURATION_SET, "duration", duration));
341 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); 300 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
342 301
343 base::AutoLock auto_lock(lock_); 302 base::AutoLock auto_lock(lock_);
344 duration_ = duration; 303 duration_ = duration;
345 if (!duration_change_cb_.is_null()) 304 if (!duration_change_cb_.is_null())
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
379 338
380 PipelineStatusCB done_cb = 339 PipelineStatusCB done_cb =
381 base::Bind(&Pipeline::OnStateTransition, weak_factory_.GetWeakPtr()); 340 base::Bind(&Pipeline::OnStateTransition, weak_factory_.GetWeakPtr());
382 341
383 // Switch states, performing any entrance actions for the new state as well. 342 // Switch states, performing any entrance actions for the new state as well.
384 SetState(GetNextState()); 343 SetState(GetNextState());
385 switch (state_) { 344 switch (state_) {
386 case kInitDemuxer: 345 case kInitDemuxer:
387 return InitializeDemuxer(done_cb); 346 return InitializeDemuxer(done_cb);
388 347
389 case kInitAudioRenderer: 348 case kInitRenderer:
390 return InitializeAudioRenderer(done_cb); 349 return InitializeRenderer(done_cb);
391
392 case kInitVideoRenderer:
393 return InitializeVideoRenderer(done_cb);
394 350
395 case kPlaying: 351 case kPlaying:
396 // Finish initial start sequence the first time we enter the playing 352 // Finish initial start sequence the first time we enter the playing
397 // state. 353 // state.
398 if (filter_collection_) { 354 if (!is_initialized_) {
399 filter_collection_.reset(); 355 if (!renderer_) {
400 if (!audio_renderer_ && !video_renderer_) {
401 ErrorChangedTask(PIPELINE_ERROR_COULD_NOT_RENDER); 356 ErrorChangedTask(PIPELINE_ERROR_COULD_NOT_RENDER);
402 return; 357 return;
403 } 358 }
404 359
405 if (audio_renderer_) 360 is_initialized_ = true;
406 time_source_ = audio_renderer_->GetTimeSource();
407 361
408 { 362 {
409 PipelineMetadata metadata; 363 PipelineMetadata metadata;
410 metadata.has_audio = audio_renderer_; 364 metadata.has_audio = renderer_->HasAudio();
411 metadata.has_video = video_renderer_; 365 metadata.has_video = renderer_->HasVideo();
412 metadata.timeline_offset = demuxer_->GetTimelineOffset(); 366 metadata.timeline_offset = demuxer_->GetTimelineOffset();
413 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); 367 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
414 if (stream) { 368 if (stream) {
415 metadata.natural_size = 369 metadata.natural_size =
416 stream->video_decoder_config().natural_size(); 370 stream->video_decoder_config().natural_size();
417 metadata.video_rotation = stream->video_rotation(); 371 metadata.video_rotation = stream->video_rotation();
418 } 372 }
419 metadata_cb_.Run(metadata); 373 metadata_cb_.Run(metadata);
420 } 374 }
421 } 375 }
422 376
423 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); 377 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
424 378
425 { 379 if (renderer_)
426 base::AutoLock auto_lock(lock_); 380 renderer_->StartPlayingFrom(start_timestamp_);
427 interpolator_->SetBounds(start_timestamp_, start_timestamp_);
428 }
429
430 if (time_source_)
431 time_source_->SetMediaTime(start_timestamp_);
432 if (audio_renderer_)
433 audio_renderer_->StartPlaying();
434 if (video_renderer_)
435 video_renderer_->StartPlaying();
436 if (text_renderer_) 381 if (text_renderer_)
437 text_renderer_->StartPlaying(); 382 text_renderer_->StartPlaying();
438 383
439 PlaybackRateChangedTask(GetPlaybackRate()); 384 PlaybackRateChangedTask(GetPlaybackRate());
440 VolumeChangedTask(GetVolume()); 385 VolumeChangedTask(GetVolume());
441 return; 386 return;
442 387
443 case kStopping: 388 case kStopping:
444 case kStopped: 389 case kStopped:
445 case kCreated: 390 case kCreated:
446 case kSeeking: 391 case kSeeking:
447 NOTREACHED() << "State has no transition: " << state_; 392 NOTREACHED() << "State has no transition: " << state_;
448 return; 393 return;
449 } 394 }
450 } 395 }
451 396
452 // Note that the usage of base::Unretained() with the audio/video renderers 397 // Note that the usage of base::Unretained() with the renderers in the following
453 // in the following DoXXX() functions is considered safe as they are owned by 398 // DoXXX() functions is considered safe as they are owned by
454 // |pending_callbacks_| and share the same lifetime. 399 // |pending_callbacks_| and share the same lifetime.
455 // 400 //
456 // That being said, deleting the renderers while keeping |pending_callbacks_| 401 // That being said, deleting the renderers while keeping |pending_callbacks_|
457 // running on the media thread would result in crashes. 402 // running on the media thread would result in crashes.
458 403
459 #if DCHECK_IS_ON 404 void Pipeline::DoSeek(TimeDelta seek_timestamp,
460 static void VerifyBufferingStates(BufferingState* audio_buffering_state, 405 const PipelineStatusCB& done_cb) {
461 BufferingState* video_buffering_state) {
462 DCHECK_EQ(*audio_buffering_state, BUFFERING_HAVE_NOTHING);
463 DCHECK_EQ(*video_buffering_state, BUFFERING_HAVE_NOTHING);
464 }
465 #endif
466
467 void Pipeline::DoSeek(
468 base::TimeDelta seek_timestamp,
469 const PipelineStatusCB& done_cb) {
470 DCHECK(task_runner_->BelongsToCurrentThread()); 406 DCHECK(task_runner_->BelongsToCurrentThread());
471 DCHECK(!pending_callbacks_.get()); 407 DCHECK(!pending_callbacks_.get());
472 SerialRunner::Queue bound_fns; 408 SerialRunner::Queue bound_fns;
473 {
474 base::AutoLock auto_lock(lock_);
475 PauseClockAndStopTicking_Locked();
476 }
477 409
478 // Pause. 410 // Pause.
479 if (text_renderer_) { 411 if (text_renderer_) {
480 bound_fns.Push(base::Bind( 412 bound_fns.Push(base::Bind(
481 &TextRenderer::Pause, base::Unretained(text_renderer_.get()))); 413 &TextRenderer::Pause, base::Unretained(text_renderer_.get())));
482 } 414 }
483 415
484 // Flush. 416 // Flush.
485 if (audio_renderer_) { 417 if (renderer_) {
486 bound_fns.Push(base::Bind( 418 bound_fns.Push(base::Bind(
487 &AudioRenderer::Flush, base::Unretained(audio_renderer_.get()))); 419 &Renderer::Flush, base::Unretained(renderer_.get())));
488 } 420 }
489 421
490 if (video_renderer_) {
491 bound_fns.Push(base::Bind(
492 &VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
493 }
494
495 #if DCHECK_IS_ON
496 // Verify renderers reset their buffering states.
497 bound_fns.Push(base::Bind(&VerifyBufferingStates,
498 &audio_buffering_state_,
499 &video_buffering_state_));
500 #endif
501
502 if (text_renderer_) { 422 if (text_renderer_) {
503 bound_fns.Push(base::Bind( 423 bound_fns.Push(base::Bind(
504 &TextRenderer::Flush, base::Unretained(text_renderer_.get()))); 424 &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
505 } 425 }
506 426
507 // Seek demuxer. 427 // Seek demuxer.
508 bound_fns.Push(base::Bind( 428 bound_fns.Push(base::Bind(
509 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); 429 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
510 430
511 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); 431 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
512 } 432 }
513 433
514 void Pipeline::DoStop(const PipelineStatusCB& done_cb) { 434 void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
515 DVLOG(2) << __FUNCTION__; 435 DVLOG(2) << __FUNCTION__;
516 DCHECK(task_runner_->BelongsToCurrentThread()); 436 DCHECK(task_runner_->BelongsToCurrentThread());
517 DCHECK(!pending_callbacks_.get()); 437 DCHECK(!pending_callbacks_.get());
518 438
519 audio_renderer_.reset(); 439 renderer_.reset();
520 video_renderer_.reset();
521 text_renderer_.reset(); 440 text_renderer_.reset();
522 441
523 if (demuxer_) { 442 if (demuxer_) {
524 demuxer_->Stop(base::Bind(done_cb, PIPELINE_OK)); 443 demuxer_->Stop(base::Bind(done_cb, PIPELINE_OK));
525 return; 444 return;
526 } 445 }
527 446
528 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK)); 447 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK));
529 } 448 }
530 449
531 void Pipeline::OnStopCompleted(PipelineStatus status) { 450 void Pipeline::OnStopCompleted(PipelineStatus status) {
532 DVLOG(2) << __FUNCTION__; 451 DVLOG(2) << __FUNCTION__;
533 DCHECK(task_runner_->BelongsToCurrentThread()); 452 DCHECK(task_runner_->BelongsToCurrentThread());
534 DCHECK_EQ(state_, kStopping); 453 DCHECK_EQ(state_, kStopping);
535 DCHECK(!audio_renderer_); 454 DCHECK(!renderer_);
536 DCHECK(!video_renderer_);
537 DCHECK(!text_renderer_); 455 DCHECK(!text_renderer_);
456
538 { 457 {
539 base::AutoLock l(lock_); 458 base::AutoLock l(lock_);
540 running_ = false; 459 running_ = false;
541 } 460 }
542 461
543 SetState(kStopped); 462 SetState(kStopped);
544 filter_collection_.reset(); 463 filter_collection_.reset();
545 demuxer_ = NULL; 464 demuxer_ = NULL;
546 465
547 // If we stop during initialization/seeking we want to run |seek_cb_| 466 // If we stop during initialization/seeking we want to run |seek_cb_|
(...skipping 14 matching lines...) Expand all
562 // NOTE: pipeline may be deleted at this point in time as a result of 481 // NOTE: pipeline may be deleted at this point in time as a result of
563 // executing |stop_cb_|. 482 // executing |stop_cb_|.
564 return; 483 return;
565 } 484 }
566 if (!error_cb_.is_null()) { 485 if (!error_cb_.is_null()) {
567 DCHECK_NE(status_, PIPELINE_OK); 486 DCHECK_NE(status_, PIPELINE_OK);
568 base::ResetAndReturn(&error_cb_).Run(status_); 487 base::ResetAndReturn(&error_cb_).Run(status_);
569 } 488 }
570 } 489 }
571 490
572 void Pipeline::AddBufferedTimeRange(base::TimeDelta start, 491 void Pipeline::AddBufferedTimeRange(TimeDelta start,
573 base::TimeDelta end) { 492 TimeDelta end) {
574 DCHECK(IsRunning()); 493 DCHECK(IsRunning());
575 base::AutoLock auto_lock(lock_); 494 base::AutoLock auto_lock(lock_);
576 buffered_time_ranges_.Add(start, end); 495 buffered_time_ranges_.Add(start, end);
577 did_loading_progress_ = true; 496 did_loading_progress_ = true;
578 } 497 }
579 498
580 // Called from any thread. 499 // Called from any thread.
581 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { 500 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) {
582 base::AutoLock auto_lock(lock_); 501 base::AutoLock auto_lock(lock_);
583 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; 502 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded;
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
645 DoStop(base::Bind(&Pipeline::OnStopCompleted, weak_factory_.GetWeakPtr())); 564 DoStop(base::Bind(&Pipeline::OnStopCompleted, weak_factory_.GetWeakPtr()));
646 } 565 }
647 566
648 void Pipeline::PlaybackRateChangedTask(float playback_rate) { 567 void Pipeline::PlaybackRateChangedTask(float playback_rate) {
649 DCHECK(task_runner_->BelongsToCurrentThread()); 568 DCHECK(task_runner_->BelongsToCurrentThread());
650 569
651 // Playback rate changes are only carried out while playing. 570 // Playback rate changes are only carried out while playing.
652 if (state_ != kPlaying) 571 if (state_ != kPlaying)
653 return; 572 return;
654 573
655 { 574 if (renderer_)
656 base::AutoLock auto_lock(lock_); 575 renderer_->SetPlaybackRate(playback_rate_);
657 interpolator_->SetPlaybackRate(playback_rate);
658 }
659
660 if (time_source_)
661 time_source_->SetPlaybackRate(playback_rate_);
662 } 576 }
663 577
664 void Pipeline::VolumeChangedTask(float volume) { 578 void Pipeline::VolumeChangedTask(float volume) {
665 DCHECK(task_runner_->BelongsToCurrentThread()); 579 DCHECK(task_runner_->BelongsToCurrentThread());
666 580
667 // Volume changes are only carried out while playing. 581 // Volume changes are only carried out while playing.
668 if (state_ != kPlaying) 582 if (state_ != kPlaying)
669 return; 583 return;
670 584
671 if (audio_renderer_) 585 if (renderer_)
672 audio_renderer_->SetVolume(volume); 586 renderer_->SetVolume(volume);
673 } 587 }
674 588
675 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { 589 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
676 DCHECK(task_runner_->BelongsToCurrentThread()); 590 DCHECK(task_runner_->BelongsToCurrentThread());
677 DCHECK(stop_cb_.is_null()); 591 DCHECK(stop_cb_.is_null());
678 592
679 // Suppress seeking if we're not fully started. 593 // Suppress seeking if we're not fully started.
680 if (state_ != kPlaying) { 594 if (state_ != kPlaying) {
681 DCHECK(state_ == kStopping || state_ == kStopped) 595 DCHECK(state_ == kStopping || state_ == kStopped)
682 << "Receive extra seek in unexpected state: " << state_; 596 << "Receive extra seek in unexpected state: " << state_;
683 597
684 // TODO(scherkus): should we run the callback? I'm tempted to say the API 598 // TODO(scherkus): should we run the callback? I'm tempted to say the API
685 // will only execute the first Seek() request. 599 // will only execute the first Seek() request.
686 DVLOG(1) << "Media pipeline has not started, ignoring seek to " 600 DVLOG(1) << "Media pipeline has not started, ignoring seek to "
687 << time.InMicroseconds() << " (current state: " << state_ << ")"; 601 << time.InMicroseconds() << " (current state: " << state_ << ")";
688 return; 602 return;
689 } 603 }
690 604
691 DCHECK(seek_cb_.is_null()); 605 DCHECK(seek_cb_.is_null());
692 606
693 SetState(kSeeking); 607 SetState(kSeeking);
694 seek_cb_ = seek_cb; 608 seek_cb_ = seek_cb;
695 audio_ended_ = false; 609 renderer_ended_ = false;
696 video_ended_ = false; 610 text_renderer_ended_ = false;
697 text_ended_ = false;
698 start_timestamp_ = time; 611 start_timestamp_ = time;
699 612
700 DoSeek(time, 613 DoSeek(time,
701 base::Bind(&Pipeline::OnStateTransition, weak_factory_.GetWeakPtr())); 614 base::Bind(&Pipeline::OnStateTransition, weak_factory_.GetWeakPtr()));
702 } 615 }
703 616
704 void Pipeline::OnAudioRendererEnded() { 617 void Pipeline::OnRendererEnded() {
705 DCHECK(task_runner_->BelongsToCurrentThread()); 618 DCHECK(task_runner_->BelongsToCurrentThread());
706 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::AUDIO_ENDED)); 619 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED));
707 620
708 if (state_ != kPlaying) 621 if (state_ != kPlaying)
709 return; 622 return;
710 623
711 DCHECK(!audio_ended_); 624 DCHECK(!renderer_ended_);
712 audio_ended_ = true; 625 renderer_ended_ = true;
713
714 // Start clock since there is no more audio to trigger clock updates.
715 {
716 base::AutoLock auto_lock(lock_);
717 interpolator_->SetUpperBound(duration_);
718 StartClockIfWaitingForTimeUpdate_Locked();
719 }
720 626
721 RunEndedCallbackIfNeeded(); 627 RunEndedCallbackIfNeeded();
722 } 628 }
723
724 void Pipeline::OnVideoRendererEnded() {
725 DCHECK(task_runner_->BelongsToCurrentThread());
726 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED));
727
728 if (state_ != kPlaying)
729 return;
730
731 DCHECK(!video_ended_);
732 video_ended_ = true;
733
734 RunEndedCallbackIfNeeded();
735 }
736 629
737 void Pipeline::OnTextRendererEnded() { 630 void Pipeline::OnTextRendererEnded() {
738 DCHECK(task_runner_->BelongsToCurrentThread()); 631 DCHECK(task_runner_->BelongsToCurrentThread());
739 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED)); 632 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
740 633
741 if (state_ != kPlaying) 634 if (state_ != kPlaying)
742 return; 635 return;
743 636
744 DCHECK(!text_ended_); 637 DCHECK(!text_renderer_ended_);
745 text_ended_ = true; 638 text_renderer_ended_ = true;
746 639
747 RunEndedCallbackIfNeeded(); 640 RunEndedCallbackIfNeeded();
748 } 641 }
749 642
750 void Pipeline::RunEndedCallbackIfNeeded() { 643 void Pipeline::RunEndedCallbackIfNeeded() {
751 DCHECK(task_runner_->BelongsToCurrentThread()); 644 DCHECK(task_runner_->BelongsToCurrentThread());
752 645
753 if (audio_renderer_ && !audio_ended_) 646 if (renderer_ && !renderer_ended_)
754 return; 647 return;
755 648
756 if (video_renderer_ && !video_ended_) 649 if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_)
757 return; 650 return;
758 651
759 if (text_renderer_ && text_renderer_->HasTracks() && !text_ended_)
760 return;
761
762 {
763 base::AutoLock auto_lock(lock_);
764 PauseClockAndStopTicking_Locked();
765 interpolator_->SetBounds(duration_, duration_);
766 }
767
768 DCHECK_EQ(status_, PIPELINE_OK); 652 DCHECK_EQ(status_, PIPELINE_OK);
769 ended_cb_.Run(); 653 ended_cb_.Run();
770 } 654 }
771 655
772 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream, 656 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream,
773 const TextTrackConfig& config) { 657 const TextTrackConfig& config) {
774 DCHECK(task_runner_->BelongsToCurrentThread()); 658 DCHECK(task_runner_->BelongsToCurrentThread());
775 // TODO(matthewjheaney): fix up text_ended_ when text stream 659 // TODO(matthewjheaney): fix up text_ended_ when text stream
776 // is added (http://crbug.com/321446). 660 // is added (http://crbug.com/321446).
777 text_renderer_->AddTextStream(text_stream, config); 661 text_renderer_->AddTextStream(text_stream, config);
778 } 662 }
779 663
780 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) { 664 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) {
781 DCHECK(task_runner_->BelongsToCurrentThread()); 665 DCHECK(task_runner_->BelongsToCurrentThread());
782 text_renderer_->RemoveTextStream(text_stream); 666 text_renderer_->RemoveTextStream(text_stream);
783 } 667 }
784 668
785 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) { 669 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
786 DCHECK(task_runner_->BelongsToCurrentThread()); 670 DCHECK(task_runner_->BelongsToCurrentThread());
787 671
788 demuxer_ = filter_collection_->GetDemuxer(); 672 demuxer_ = filter_collection_->GetDemuxer();
789 demuxer_->Initialize(this, done_cb, text_renderer_); 673 demuxer_->Initialize(this, done_cb, text_renderer_);
790 } 674 }
791 675
792 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) { 676 void Pipeline::InitializeRenderer(const PipelineStatusCB& done_cb) {
793 DCHECK(task_runner_->BelongsToCurrentThread()); 677 DCHECK(task_runner_->BelongsToCurrentThread());
794 678
795 audio_renderer_ = filter_collection_->GetAudioRenderer(); 679 renderer_ = filter_collection_->GetRenderer();
680
681 if (test_interpolator_)
682 renderer_->SetTimeDeltaInterpolatorForTesting(test_interpolator_);
683 if (underflow_disabled_for_testing_)
684 renderer_->DisableUnderflowForTesting();
685
796 base::WeakPtr<Pipeline> weak_this = weak_factory_.GetWeakPtr(); 686 base::WeakPtr<Pipeline> weak_this = weak_factory_.GetWeakPtr();
797 audio_renderer_->Initialize( 687 renderer_->Initialize(
798 demuxer_->GetStream(DemuxerStream::AUDIO),
799 done_cb, 688 done_cb,
800 base::Bind(&Pipeline::OnUpdateStatistics, weak_this), 689 base::Bind(&Pipeline::OnUpdateStatistics, weak_this),
801 base::Bind(&Pipeline::OnAudioTimeUpdate, weak_this), 690 base::Bind(&Pipeline::OnRendererEnded, weak_this),
802 base::Bind(&Pipeline::BufferingStateChanged, weak_this,
803 &audio_buffering_state_),
804 base::Bind(&Pipeline::OnAudioRendererEnded, weak_this),
805 base::Bind(&Pipeline::OnError, weak_this));
806 }
807
808 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) {
809 DCHECK(task_runner_->BelongsToCurrentThread());
810
811 video_renderer_ = filter_collection_->GetVideoRenderer();
812 base::WeakPtr<Pipeline> weak_this = weak_factory_.GetWeakPtr();
813 video_renderer_->Initialize(
814 demuxer_->GetStream(DemuxerStream::VIDEO),
815 demuxer_->GetLiveness() == Demuxer::LIVENESS_LIVE,
816 done_cb,
817 base::Bind(&Pipeline::OnUpdateStatistics, weak_this),
818 base::Bind(&Pipeline::OnVideoTimeUpdate, weak_this),
819 base::Bind(&Pipeline::BufferingStateChanged, weak_this,
820 &video_buffering_state_),
821 base::Bind(&Pipeline::OnVideoRendererEnded, weak_this),
822 base::Bind(&Pipeline::OnError, weak_this), 691 base::Bind(&Pipeline::OnError, weak_this),
823 base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)), 692 base::Bind(&Pipeline::BufferingStateChanged, weak_this),
824 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this))); 693 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this)));
825 } 694 }
826 695
827 void Pipeline::BufferingStateChanged(BufferingState* buffering_state, 696 void Pipeline::BufferingStateChanged(BufferingState new_buffering_state) {
828 BufferingState new_buffering_state) { 697 DVLOG(1) << __FUNCTION__ << "(" << new_buffering_state << ") ";
829 DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", "
830 << " " << new_buffering_state << ") "
831 << (buffering_state == &audio_buffering_state_ ? "audio" : "video");
832 DCHECK(task_runner_->BelongsToCurrentThread()); 698 DCHECK(task_runner_->BelongsToCurrentThread());
833 bool was_waiting_for_enough_data = WaitingForEnoughData(); 699 buffering_state_cb_.Run(new_buffering_state);
834
835 *buffering_state = new_buffering_state;
836
837 // Disable underflow by ignoring updates that renderers have ran out of data
838 // after we have started the clock.
839 if (state_ == kPlaying && underflow_disabled_for_testing_ &&
840 interpolation_state_ != INTERPOLATION_STOPPED) {
841 return;
842 }
843
844 // Renderer underflowed.
845 if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
846 PausePlayback();
847
848 // TODO(scherkus): Fire BUFFERING_HAVE_NOTHING callback to alert clients of
849 // underflow state http://crbug.com/144683
850 return;
851 }
852
853 // Renderer prerolled.
854 if (was_waiting_for_enough_data && !WaitingForEnoughData()) {
855 StartPlayback();
856 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
857 return;
858 }
859 }
860
861 bool Pipeline::WaitingForEnoughData() const {
862 DCHECK(task_runner_->BelongsToCurrentThread());
863 if (state_ != kPlaying)
864 return false;
865 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH)
866 return true;
867 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH)
868 return true;
869 return false;
870 }
871
872 void Pipeline::PausePlayback() {
873 DVLOG(1) << __FUNCTION__;
874 DCHECK_EQ(state_, kPlaying);
875 DCHECK(WaitingForEnoughData());
876 DCHECK(task_runner_->BelongsToCurrentThread());
877
878 base::AutoLock auto_lock(lock_);
879 PauseClockAndStopTicking_Locked();
880 }
881
882 void Pipeline::StartPlayback() {
883 DVLOG(1) << __FUNCTION__;
884 DCHECK_EQ(state_, kPlaying);
885 DCHECK_EQ(interpolation_state_, INTERPOLATION_STOPPED);
886 DCHECK(!WaitingForEnoughData());
887 DCHECK(task_runner_->BelongsToCurrentThread());
888
889 if (time_source_) {
890 // We use audio stream to update the clock. So if there is such a
891 // stream, we pause the clock until we receive a valid timestamp.
892 base::AutoLock auto_lock(lock_);
893 interpolation_state_ = INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE;
894 time_source_->StartTicking();
895 } else {
896 base::AutoLock auto_lock(lock_);
897 interpolation_state_ = INTERPOLATION_STARTED;
898 interpolator_->SetUpperBound(duration_);
899 interpolator_->StartInterpolating();
900 }
901 }
902
903 void Pipeline::PauseClockAndStopTicking_Locked() {
904 lock_.AssertAcquired();
905 switch (interpolation_state_) {
906 case INTERPOLATION_STOPPED:
907 return;
908
909 case INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE:
910 time_source_->StopTicking();
911 break;
912
913 case INTERPOLATION_STARTED:
914 if (time_source_)
915 time_source_->StopTicking();
916 interpolator_->StopInterpolating();
917 break;
918 }
919
920 interpolation_state_ = INTERPOLATION_STOPPED;
921 }
922
923 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
924 lock_.AssertAcquired();
925 if (interpolation_state_ != INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE)
926 return;
927
928 interpolation_state_ = INTERPOLATION_STARTED;
929 interpolator_->StartInterpolating();
930 } 700 }
931 701
932 } // namespace media 702 } // namespace media
OLDNEW
« no previous file with comments | « media/base/pipeline.h ('k') | media/base/pipeline_unittest.cc » ('j') | media/base/renderer.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698