OLD | NEW |
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/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
14 #include "base/message_loop.h" | 14 #include "base/message_loop.h" |
15 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
16 #include "base/string_util.h" | 16 #include "base/string_util.h" |
17 #include "base/synchronization/condition_variable.h" | 17 #include "base/synchronization/condition_variable.h" |
18 #include "media/base/audio_decoder.h" | 18 #include "media/base/audio_decoder.h" |
19 #include "media/base/audio_renderer.h" | 19 #include "media/base/audio_renderer.h" |
20 #include "media/base/callback_util.h" | |
21 #include "media/base/clock.h" | 20 #include "media/base/clock.h" |
22 #include "media/base/filter_collection.h" | 21 #include "media/base/filter_collection.h" |
23 #include "media/base/media_log.h" | 22 #include "media/base/media_log.h" |
24 #include "media/base/video_decoder.h" | 23 #include "media/base/video_decoder.h" |
25 #include "media/base/video_renderer.h" | 24 #include "media/base/video_renderer.h" |
26 | 25 |
27 using base::TimeDelta; | 26 using base::TimeDelta; |
28 | 27 |
29 namespace media { | 28 namespace media { |
30 | 29 |
(...skipping 413 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
444 // Since the byte->time calculation is approximate, fudge the beginning & | 443 // Since the byte->time calculation is approximate, fudge the beginning & |
445 // ending areas to look better. | 444 // ending areas to look better. |
446 TimeDelta epsilon = clock_->Duration() / 100; | 445 TimeDelta epsilon = clock_->Duration() / 100; |
447 if (time_offset < epsilon) | 446 if (time_offset < epsilon) |
448 return TimeDelta(); | 447 return TimeDelta(); |
449 if (time_offset + epsilon > clock_->Duration()) | 448 if (time_offset + epsilon > clock_->Duration()) |
450 return clock_->Duration(); | 449 return clock_->Duration(); |
451 return time_offset; | 450 return time_offset; |
452 } | 451 } |
453 | 452 |
454 void Pipeline::DoPause(const base::Closure& done_cb) { | 453 void Pipeline::DoPause(const PipelineStatusCB& done_cb) { |
455 DCHECK(message_loop_->BelongsToCurrentThread()); | 454 DCHECK(message_loop_->BelongsToCurrentThread()); |
456 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); | 455 DCHECK(!pending_callbacks_.get()); |
| 456 SerialRunner::Queue bound_fns; |
457 | 457 |
458 if (audio_renderer_) | 458 if (audio_renderer_) |
459 closures->push(base::Bind(&AudioRenderer::Pause, audio_renderer_)); | 459 bound_fns.Push(base::Bind(&AudioRenderer::Pause, audio_renderer_)); |
460 | 460 |
461 if (video_renderer_) | 461 if (video_renderer_) |
462 closures->push(base::Bind(&VideoRenderer::Pause, video_renderer_)); | 462 bound_fns.Push(base::Bind(&VideoRenderer::Pause, video_renderer_)); |
463 | 463 |
464 RunInSeries(closures.Pass(), done_cb); | 464 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
465 } | 465 } |
466 | 466 |
467 void Pipeline::DoFlush(const base::Closure& done_cb) { | 467 void Pipeline::DoFlush(const PipelineStatusCB& done_cb) { |
468 DCHECK(message_loop_->BelongsToCurrentThread()); | 468 DCHECK(message_loop_->BelongsToCurrentThread()); |
469 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); | 469 DCHECK(!pending_callbacks_.get()); |
| 470 SerialRunner::Queue bound_fns; |
470 | 471 |
471 if (audio_renderer_) | 472 if (audio_renderer_) |
472 closures->push(base::Bind(&AudioRenderer::Flush, audio_renderer_)); | 473 bound_fns.Push(base::Bind(&AudioRenderer::Flush, audio_renderer_)); |
473 | 474 |
474 if (video_renderer_) | 475 if (video_renderer_) |
475 closures->push(base::Bind(&VideoRenderer::Flush, video_renderer_)); | 476 bound_fns.Push(base::Bind(&VideoRenderer::Flush, video_renderer_)); |
476 | 477 |
477 RunInParallel(closures.Pass(), done_cb); | 478 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
478 } | 479 } |
479 | 480 |
480 void Pipeline::DoPlay(const base::Closure& done_cb) { | 481 void Pipeline::DoPlay(const PipelineStatusCB& done_cb) { |
481 DCHECK(message_loop_->BelongsToCurrentThread()); | 482 DCHECK(message_loop_->BelongsToCurrentThread()); |
482 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); | 483 DCHECK(!pending_callbacks_.get()); |
| 484 SerialRunner::Queue bound_fns; |
483 | 485 |
484 if (audio_renderer_) | 486 if (audio_renderer_) |
485 closures->push(base::Bind(&AudioRenderer::Play, audio_renderer_)); | 487 bound_fns.Push(base::Bind(&AudioRenderer::Play, audio_renderer_)); |
486 | 488 |
487 if (video_renderer_) | 489 if (video_renderer_) |
488 closures->push(base::Bind(&VideoRenderer::Play, video_renderer_)); | 490 bound_fns.Push(base::Bind(&VideoRenderer::Play, video_renderer_)); |
489 | 491 |
490 RunInSeries(closures.Pass(), done_cb); | 492 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
491 } | 493 } |
492 | 494 |
493 void Pipeline::DoStop(const base::Closure& done_cb) { | 495 void Pipeline::DoStop(const PipelineStatusCB& done_cb) { |
494 DCHECK(message_loop_->BelongsToCurrentThread()); | 496 DCHECK(message_loop_->BelongsToCurrentThread()); |
495 scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); | 497 DCHECK(!pending_callbacks_.get()); |
| 498 SerialRunner::Queue bound_fns; |
496 | 499 |
497 if (demuxer_) | 500 if (demuxer_) |
498 closures->push(base::Bind(&Demuxer::Stop, demuxer_)); | 501 bound_fns.Push(base::Bind(&Demuxer::Stop, demuxer_)); |
499 | 502 |
500 if (audio_renderer_) | 503 if (audio_renderer_) |
501 closures->push(base::Bind(&AudioRenderer::Stop, audio_renderer_)); | 504 bound_fns.Push(base::Bind(&AudioRenderer::Stop, audio_renderer_)); |
502 | 505 |
503 if (video_renderer_) | 506 if (video_renderer_) |
504 closures->push(base::Bind(&VideoRenderer::Stop, video_renderer_)); | 507 bound_fns.Push(base::Bind(&VideoRenderer::Stop, video_renderer_)); |
505 | 508 |
506 RunInSeries(closures.Pass(), done_cb); | 509 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
507 } | 510 } |
508 | 511 |
509 void Pipeline::AddBufferedByteRange(int64 start, int64 end) { | 512 void Pipeline::AddBufferedByteRange(int64 start, int64 end) { |
510 DCHECK(IsRunning()); | 513 DCHECK(IsRunning()); |
511 base::AutoLock auto_lock(lock_); | 514 base::AutoLock auto_lock(lock_); |
512 buffered_byte_ranges_.Add(start, end); | 515 buffered_byte_ranges_.Add(start, end); |
513 did_loading_progress_ = true; | 516 did_loading_progress_ = true; |
514 } | 517 } |
515 | 518 |
516 void Pipeline::AddBufferedTimeRange(base::TimeDelta start, | 519 void Pipeline::AddBufferedTimeRange(base::TimeDelta start, |
(...skipping 21 matching lines...) Expand all Loading... |
538 } | 541 } |
539 | 542 |
540 // Called from any thread. | 543 // Called from any thread. |
541 void Pipeline::OnFilterInitialize(PipelineStatus status) { | 544 void Pipeline::OnFilterInitialize(PipelineStatus status) { |
542 // Continue the initialize task by proceeding to the next stage. | 545 // Continue the initialize task by proceeding to the next stage. |
543 message_loop_->PostTask(FROM_HERE, base::Bind( | 546 message_loop_->PostTask(FROM_HERE, base::Bind( |
544 &Pipeline::InitializeTask, this, status)); | 547 &Pipeline::InitializeTask, this, status)); |
545 } | 548 } |
546 | 549 |
547 // Called from any thread. | 550 // Called from any thread. |
548 void Pipeline::OnFilterStateTransition() { | |
549 message_loop_->PostTask(FROM_HERE, base::Bind( | |
550 &Pipeline::FilterStateTransitionTask, this)); | |
551 } | |
552 | |
553 // Called from any thread. | |
554 // This method makes the PipelineStatusCB behave like a Closure. It | 551 // This method makes the PipelineStatusCB behave like a Closure. It |
555 // makes it look like a host()->SetError() call followed by a call to | 552 // makes it look like a host()->SetError() call followed by a call to |
556 // OnFilterStateTransition() when errors occur. | 553 // OnFilterStateTransition() when errors occur. |
557 // | 554 // |
558 // TODO: Revisit this code when SetError() is removed from FilterHost and | 555 // TODO: Revisit this code when SetError() is removed from FilterHost and |
559 // all the Closures are converted to PipelineStatusCB. | 556 // all the Closures are converted to PipelineStatusCB. |
560 void Pipeline::OnFilterStateTransitionWithStatus(PipelineStatus status) { | 557 void Pipeline::OnFilterStateTransition(PipelineStatus status) { |
561 if (status != PIPELINE_OK) | 558 if (status != PIPELINE_OK) |
562 SetError(status); | 559 SetError(status); |
563 OnFilterStateTransition(); | 560 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 561 &Pipeline::FilterStateTransitionTask, this)); |
564 } | 562 } |
565 | 563 |
566 void Pipeline::OnTeardownStateTransition() { | 564 void Pipeline::OnTeardownStateTransition(PipelineStatus status) { |
| 565 // Ignore any errors during teardown. |
567 message_loop_->PostTask(FROM_HERE, base::Bind( | 566 message_loop_->PostTask(FROM_HERE, base::Bind( |
568 &Pipeline::TeardownStateTransitionTask, this)); | 567 &Pipeline::TeardownStateTransitionTask, this)); |
569 } | 568 } |
570 | 569 |
571 // Called from any thread. | 570 // Called from any thread. |
572 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { | 571 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { |
573 base::AutoLock auto_lock(lock_); | 572 base::AutoLock auto_lock(lock_); |
574 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; | 573 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; |
575 statistics_.video_bytes_decoded += stats.video_bytes_decoded; | 574 statistics_.video_bytes_decoded += stats.video_bytes_decoded; |
576 statistics_.video_frames_decoded += stats.video_frames_decoded; | 575 statistics_.video_frames_decoded += stats.video_frames_decoded; |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
687 // to set the initial playback rate and volume. | 686 // to set the initial playback rate and volume. |
688 PlaybackRateChangedTask(GetPlaybackRate()); | 687 PlaybackRateChangedTask(GetPlaybackRate()); |
689 VolumeChangedTask(GetVolume()); | 688 VolumeChangedTask(GetVolume()); |
690 | 689 |
691 // Fire a seek request to get the renderers to preroll. We can skip a seek | 690 // Fire a seek request to get the renderers to preroll. We can skip a seek |
692 // here as the demuxer should be at the start of the stream. | 691 // here as the demuxer should be at the start of the stream. |
693 seek_pending_ = true; | 692 seek_pending_ = true; |
694 SetState(kSeeking); | 693 SetState(kSeeking); |
695 seek_timestamp_ = demuxer_->GetStartTime(); | 694 seek_timestamp_ = demuxer_->GetStartTime(); |
696 DoSeek(seek_timestamp_, true, | 695 DoSeek(seek_timestamp_, true, |
697 base::Bind(&Pipeline::OnFilterStateTransitionWithStatus, this)); | 696 base::Bind(&Pipeline::OnFilterStateTransition, this)); |
698 } | 697 } |
699 } | 698 } |
700 | 699 |
701 // This method is called as a result of the client calling Pipeline::Stop() or | 700 // This method is called as a result of the client calling Pipeline::Stop() or |
702 // as the result of an error condition. | 701 // as the result of an error condition. |
703 // We stop the filters in the reverse order. | 702 // We stop the filters in the reverse order. |
704 // | 703 // |
705 // TODO(scherkus): beware! this can get posted multiple times since we post | 704 // TODO(scherkus): beware! this can get posted multiple times since we post |
706 // Stop() tasks even if we've already stopped. Perhaps this should no-op for | 705 // Stop() tasks even if we've already stopped. Perhaps this should no-op for |
707 // additional calls, however most of this logic will be changing. | 706 // additional calls, however most of this logic will be changing. |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
882 demuxer_->OnAudioRendererDisabled(); | 881 demuxer_->OnAudioRendererDisabled(); |
883 | 882 |
884 // Start clock since there is no more audio to | 883 // Start clock since there is no more audio to |
885 // trigger clock updates. | 884 // trigger clock updates. |
886 clock_->SetMaxTime(clock_->Duration()); | 885 clock_->SetMaxTime(clock_->Duration()); |
887 StartClockIfWaitingForTimeUpdate_Locked(); | 886 StartClockIfWaitingForTimeUpdate_Locked(); |
888 } | 887 } |
889 | 888 |
890 void Pipeline::FilterStateTransitionTask() { | 889 void Pipeline::FilterStateTransitionTask() { |
891 DCHECK(message_loop_->BelongsToCurrentThread()); | 890 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 891 DCHECK(pending_callbacks_.get()) |
| 892 << "Filter state transitions must be completed via pending_callbacks_"; |
| 893 pending_callbacks_.reset(); |
892 | 894 |
893 // No reason transitioning if we've errored or have stopped. | 895 // No reason transitioning if we've errored or have stopped. |
894 if (IsPipelineStopped()) { | 896 if (IsPipelineStopped()) { |
895 return; | 897 return; |
896 } | 898 } |
897 | 899 |
898 // If we are tearing down, don't allow any state changes. Teardown | 900 // If we are tearing down, don't allow any state changes. Teardown |
899 // state changes will come in via TeardownStateTransitionTask(). | 901 // state changes will come in via TeardownStateTransitionTask(). |
900 if (IsPipelineTearingDown()) { | 902 if (IsPipelineTearingDown()) { |
901 return; | 903 return; |
(...skipping 14 matching lines...) Expand all Loading... |
916 } | 918 } |
917 | 919 |
918 // Carry out the action for the current state. | 920 // Carry out the action for the current state. |
919 if (TransientState(state_)) { | 921 if (TransientState(state_)) { |
920 if (state_ == kPausing) { | 922 if (state_ == kPausing) { |
921 DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this)); | 923 DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
922 } else if (state_ == kFlushing) { | 924 } else if (state_ == kFlushing) { |
923 DoFlush(base::Bind(&Pipeline::OnFilterStateTransition, this)); | 925 DoFlush(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
924 } else if (state_ == kSeeking) { | 926 } else if (state_ == kSeeking) { |
925 DoSeek(seek_timestamp_, false, | 927 DoSeek(seek_timestamp_, false, |
926 base::Bind(&Pipeline::OnFilterStateTransitionWithStatus, this)); | 928 base::Bind(&Pipeline::OnFilterStateTransition, this)); |
927 } else if (state_ == kStarting) { | 929 } else if (state_ == kStarting) { |
928 DoPlay(base::Bind(&Pipeline::OnFilterStateTransition, this)); | 930 DoPlay(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
929 } else if (state_ == kStopping) { | 931 } else if (state_ == kStopping) { |
930 DoStop(base::Bind(&Pipeline::OnFilterStateTransition, this)); | 932 DoStop(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
931 } else { | 933 } else { |
932 NOTREACHED() << "Unexpected state: " << state_; | 934 NOTREACHED() << "Unexpected state: " << state_; |
933 } | 935 } |
934 } else if (state_ == kStarted) { | 936 } else if (state_ == kStarted) { |
935 FinishInitialization(); | 937 FinishInitialization(); |
936 | 938 |
(...skipping 20 matching lines...) Expand all Loading... |
957 // We had a pending stop request need to be honored right now. | 959 // We had a pending stop request need to be honored right now. |
958 TearDownPipeline(); | 960 TearDownPipeline(); |
959 } | 961 } |
960 } else { | 962 } else { |
961 NOTREACHED() << "Unexpected state: " << state_; | 963 NOTREACHED() << "Unexpected state: " << state_; |
962 } | 964 } |
963 } | 965 } |
964 | 966 |
965 void Pipeline::TeardownStateTransitionTask() { | 967 void Pipeline::TeardownStateTransitionTask() { |
966 DCHECK(IsPipelineTearingDown()); | 968 DCHECK(IsPipelineTearingDown()); |
| 969 DCHECK(pending_callbacks_.get()) |
| 970 << "Teardown state transitions must be completed via pending_callbacks_"; |
| 971 pending_callbacks_.reset(); |
| 972 |
967 switch (state_) { | 973 switch (state_) { |
968 case kStopping: | 974 case kStopping: |
969 SetState(error_caused_teardown_ ? kError : kStopped); | 975 SetState(error_caused_teardown_ ? kError : kStopped); |
970 FinishDestroyingFiltersTask(); | 976 FinishDestroyingFiltersTask(); |
971 break; | 977 break; |
972 case kPausing: | 978 case kPausing: |
973 SetState(kFlushing); | 979 SetState(kFlushing); |
974 DoFlush(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | 980 DoFlush(base::Bind(&Pipeline::OnTeardownStateTransition, this)); |
975 break; | 981 break; |
976 case kFlushing: | 982 case kFlushing: |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1164 DCHECK(message_loop_->BelongsToCurrentThread()); | 1170 DCHECK(message_loop_->BelongsToCurrentThread()); |
1165 DCHECK_NE(kStopped, state_); | 1171 DCHECK_NE(kStopped, state_); |
1166 | 1172 |
1167 DCHECK(!tearing_down_ || // Teardown on Stop(). | 1173 DCHECK(!tearing_down_ || // Teardown on Stop(). |
1168 (tearing_down_ && error_caused_teardown_) || // Teardown on error. | 1174 (tearing_down_ && error_caused_teardown_) || // Teardown on error. |
1169 (tearing_down_ && stop_pending_)); // Stop during teardown by error. | 1175 (tearing_down_ && stop_pending_)); // Stop during teardown by error. |
1170 | 1176 |
1171 // Mark that we already start tearing down operation. | 1177 // Mark that we already start tearing down operation. |
1172 tearing_down_ = true; | 1178 tearing_down_ = true; |
1173 | 1179 |
| 1180 // Cancel any pending operation so we can proceed with teardown. |
| 1181 pending_callbacks_.reset(); |
| 1182 |
1174 switch (state_) { | 1183 switch (state_) { |
1175 case kCreated: | 1184 case kCreated: |
1176 case kError: | 1185 case kError: |
1177 SetState(kStopped); | 1186 SetState(kStopped); |
1178 // Need to put this in the message loop to make sure that it comes | 1187 // Need to put this in the message loop to make sure that it comes |
1179 // after any pending callback tasks that are already queued. | 1188 // after any pending callback tasks that are already queued. |
1180 message_loop_->PostTask(FROM_HERE, base::Bind( | 1189 message_loop_->PostTask(FROM_HERE, base::Bind( |
1181 &Pipeline::FinishDestroyingFiltersTask, this)); | 1190 &Pipeline::FinishDestroyingFiltersTask, this)); |
1182 break; | 1191 break; |
1183 | 1192 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1222 break; | 1231 break; |
1223 // default: intentionally left out to force new states to cause compiler | 1232 // default: intentionally left out to force new states to cause compiler |
1224 // errors. | 1233 // errors. |
1225 }; | 1234 }; |
1226 } | 1235 } |
1227 | 1236 |
1228 void Pipeline::DoSeek(base::TimeDelta seek_timestamp, | 1237 void Pipeline::DoSeek(base::TimeDelta seek_timestamp, |
1229 bool skip_demuxer_seek, | 1238 bool skip_demuxer_seek, |
1230 const PipelineStatusCB& done_cb) { | 1239 const PipelineStatusCB& done_cb) { |
1231 DCHECK(message_loop_->BelongsToCurrentThread()); | 1240 DCHECK(message_loop_->BelongsToCurrentThread()); |
1232 scoped_ptr<std::queue<PipelineStatusCBFunc> > status_cbs( | 1241 DCHECK(!pending_callbacks_.get()); |
1233 new std::queue<PipelineStatusCBFunc>()); | 1242 SerialRunner::Queue bound_fns; |
1234 | 1243 |
1235 if (!skip_demuxer_seek) | 1244 if (!skip_demuxer_seek) { |
1236 status_cbs->push(base::Bind(&Demuxer::Seek, demuxer_, seek_timestamp)); | 1245 bound_fns.Push(base::Bind( |
| 1246 &Demuxer::Seek, demuxer_, seek_timestamp)); |
| 1247 } |
1237 | 1248 |
1238 if (audio_renderer_) | 1249 if (audio_renderer_) { |
1239 status_cbs->push(base::Bind( | 1250 bound_fns.Push(base::Bind( |
1240 &AudioRenderer::Preroll, audio_renderer_, seek_timestamp)); | 1251 &AudioRenderer::Preroll, audio_renderer_, seek_timestamp)); |
| 1252 } |
1241 | 1253 |
1242 if (video_renderer_) | 1254 if (video_renderer_) { |
1243 status_cbs->push(base::Bind( | 1255 bound_fns.Push(base::Bind( |
1244 &VideoRenderer::Preroll, video_renderer_, seek_timestamp)); | 1256 &VideoRenderer::Preroll, video_renderer_, seek_timestamp)); |
| 1257 } |
1245 | 1258 |
1246 RunInSeriesWithStatus(status_cbs.Pass(), base::Bind( | 1259 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
1247 &Pipeline::ReportStatus, this, done_cb)); | |
1248 } | 1260 } |
1249 | 1261 |
1250 void Pipeline::OnAudioUnderflow() { | 1262 void Pipeline::OnAudioUnderflow() { |
1251 if (!message_loop_->BelongsToCurrentThread()) { | 1263 if (!message_loop_->BelongsToCurrentThread()) { |
1252 message_loop_->PostTask(FROM_HERE, base::Bind( | 1264 message_loop_->PostTask(FROM_HERE, base::Bind( |
1253 &Pipeline::OnAudioUnderflow, this)); | 1265 &Pipeline::OnAudioUnderflow, this)); |
1254 return; | 1266 return; |
1255 } | 1267 } |
1256 | 1268 |
1257 if (state_ != kStarted) | 1269 if (state_ != kStarted) |
1258 return; | 1270 return; |
1259 | 1271 |
1260 if (audio_renderer_) | 1272 if (audio_renderer_) |
1261 audio_renderer_->ResumeAfterUnderflow(true); | 1273 audio_renderer_->ResumeAfterUnderflow(true); |
1262 } | 1274 } |
1263 | 1275 |
1264 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { | 1276 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { |
1265 lock_.AssertAcquired(); | 1277 lock_.AssertAcquired(); |
1266 if (!waiting_for_clock_update_) | 1278 if (!waiting_for_clock_update_) |
1267 return; | 1279 return; |
1268 | 1280 |
1269 waiting_for_clock_update_ = false; | 1281 waiting_for_clock_update_ = false; |
1270 clock_->Play(); | 1282 clock_->Play(); |
1271 } | 1283 } |
1272 | 1284 |
1273 } // namespace media | 1285 } // namespace media |
OLD | NEW |