| 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/buffers.h" | 20 #include "media/base/buffers.h" |
| 21 #include "media/base/clock.h" | 21 #include "media/base/clock.h" |
| 22 #include "media/base/filter_collection.h" | 22 #include "media/base/filter_collection.h" |
| 23 #include "media/base/media_log.h" | 23 #include "media/base/media_log.h" |
| 24 #include "media/base/video_decoder.h" | 24 #include "media/base/video_decoder.h" |
| 25 #include "media/base/video_decoder_config.h" |
| 25 #include "media/base/video_renderer.h" | 26 #include "media/base/video_renderer.h" |
| 26 | 27 |
| 27 using base::TimeDelta; | 28 using base::TimeDelta; |
| 28 | 29 |
| 29 namespace media { | 30 namespace media { |
| 30 | 31 |
| 31 PipelineStatusNotification::PipelineStatusNotification() | 32 PipelineStatusNotification::PipelineStatusNotification() |
| 32 : cv_(&lock_), status_(PIPELINE_OK), notified_(false) { | 33 : cv_(&lock_), status_(PIPELINE_OK), notified_(false) { |
| 33 } | 34 } |
| 34 | 35 |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 101 DCHECK(stop_cb_.is_null()); | 102 DCHECK(stop_cb_.is_null()); |
| 102 DCHECK(!seek_pending_); | 103 DCHECK(!seek_pending_); |
| 103 | 104 |
| 104 media_log_->AddEvent( | 105 media_log_->AddEvent( |
| 105 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED)); | 106 media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED)); |
| 106 } | 107 } |
| 107 | 108 |
| 108 void Pipeline::Start(scoped_ptr<FilterCollection> collection, | 109 void Pipeline::Start(scoped_ptr<FilterCollection> collection, |
| 109 const PipelineStatusCB& ended_cb, | 110 const PipelineStatusCB& ended_cb, |
| 110 const PipelineStatusCB& error_cb, | 111 const PipelineStatusCB& error_cb, |
| 111 const PipelineStatusCB& start_cb) { | 112 const PipelineStatusCB& seek_cb, |
| 113 const ReadyStateCB& ready_state_cb) { |
| 112 base::AutoLock auto_lock(lock_); | 114 base::AutoLock auto_lock(lock_); |
| 113 CHECK(!running_) << "Media pipeline is already running"; | 115 CHECK(!running_) << "Media pipeline is already running"; |
| 114 | 116 |
| 115 running_ = true; | 117 running_ = true; |
| 116 message_loop_->PostTask(FROM_HERE, base::Bind( | 118 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 117 &Pipeline::StartTask, this, base::Passed(&collection), | 119 &Pipeline::StartTask, this, base::Passed(&collection), |
| 118 ended_cb, error_cb, start_cb)); | 120 ended_cb, error_cb, seek_cb, ready_state_cb)); |
| 119 } | 121 } |
| 120 | 122 |
| 121 void Pipeline::Stop(const base::Closure& stop_cb) { | 123 void Pipeline::Stop(const base::Closure& stop_cb) { |
| 122 base::AutoLock auto_lock(lock_); | 124 base::AutoLock auto_lock(lock_); |
| 123 message_loop_->PostTask(FROM_HERE, base::Bind( | 125 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 124 &Pipeline::StopTask, this, stop_cb)); | 126 &Pipeline::StopTask, this, stop_cb)); |
| 125 } | 127 } |
| 126 | 128 |
| 127 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) { | 129 void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) { |
| 128 base::AutoLock auto_lock(lock_); | 130 base::AutoLock auto_lock(lock_); |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 274 void Pipeline::ReportStatus(const PipelineStatusCB& cb, PipelineStatus status) { | 276 void Pipeline::ReportStatus(const PipelineStatusCB& cb, PipelineStatus status) { |
| 275 DCHECK(message_loop_->BelongsToCurrentThread()); | 277 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 276 if (cb.is_null()) | 278 if (cb.is_null()) |
| 277 return; | 279 return; |
| 278 cb.Run(status); | 280 cb.Run(status); |
| 279 // Prevent double-reporting of errors to clients. | 281 // Prevent double-reporting of errors to clients. |
| 280 if (status != PIPELINE_OK) | 282 if (status != PIPELINE_OK) |
| 281 error_cb_.Reset(); | 283 error_cb_.Reset(); |
| 282 } | 284 } |
| 283 | 285 |
| 284 void Pipeline::FinishInitialization() { | 286 void Pipeline::FinishSeek() { |
| 285 DCHECK(message_loop_->BelongsToCurrentThread()); | 287 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 288 seek_timestamp_ = kNoTimestamp(); |
| 289 seek_pending_ = false; |
| 290 |
| 286 // Execute the seek callback, if present. Note that this might be the | 291 // Execute the seek callback, if present. Note that this might be the |
| 287 // initial callback passed into Start(). | 292 // initial callback passed into Start(). |
| 288 ReportStatus(seek_cb_, status_); | 293 ReportStatus(seek_cb_, status_); |
| 289 seek_timestamp_ = kNoTimestamp(); | |
| 290 seek_cb_.Reset(); | 294 seek_cb_.Reset(); |
| 291 } | 295 } |
| 292 | 296 |
| 293 // static | 297 // static |
| 294 bool Pipeline::TransientState(State state) { | 298 bool Pipeline::TransientState(State state) { |
| 295 return state == kPausing || | 299 return state == kPausing || |
| 296 state == kFlushing || | 300 state == kFlushing || |
| 297 state == kSeeking || | 301 state == kSeeking || |
| 298 state == kStarting || | 302 state == kStarting || |
| 299 state == kStopping; | 303 state == kStopping; |
| (...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 544 base::AutoLock auto_lock(lock_); | 548 base::AutoLock auto_lock(lock_); |
| 545 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; | 549 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; |
| 546 statistics_.video_bytes_decoded += stats.video_bytes_decoded; | 550 statistics_.video_bytes_decoded += stats.video_bytes_decoded; |
| 547 statistics_.video_frames_decoded += stats.video_frames_decoded; | 551 statistics_.video_frames_decoded += stats.video_frames_decoded; |
| 548 statistics_.video_frames_dropped += stats.video_frames_dropped; | 552 statistics_.video_frames_dropped += stats.video_frames_dropped; |
| 549 } | 553 } |
| 550 | 554 |
| 551 void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection, | 555 void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection, |
| 552 const PipelineStatusCB& ended_cb, | 556 const PipelineStatusCB& ended_cb, |
| 553 const PipelineStatusCB& error_cb, | 557 const PipelineStatusCB& error_cb, |
| 554 const PipelineStatusCB& start_cb) { | 558 const PipelineStatusCB& seek_cb, |
| 559 const ReadyStateCB& ready_state_cb) { |
| 555 DCHECK(message_loop_->BelongsToCurrentThread()); | 560 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 556 CHECK_EQ(kCreated, state_) | 561 CHECK_EQ(kCreated, state_) |
| 557 << "Media pipeline cannot be started more than once"; | 562 << "Media pipeline cannot be started more than once"; |
| 558 | 563 |
| 559 filter_collection_ = filter_collection.Pass(); | 564 filter_collection_ = filter_collection.Pass(); |
| 560 ended_cb_ = ended_cb; | 565 ended_cb_ = ended_cb; |
| 561 error_cb_ = error_cb; | 566 error_cb_ = error_cb; |
| 562 seek_cb_ = start_cb; | 567 seek_cb_ = seek_cb; |
| 568 ready_state_cb_ = ready_state_cb; |
| 563 | 569 |
| 564 // Kick off initialization. | 570 // Kick off initialization. |
| 565 pipeline_init_state_.reset(new PipelineInitState()); | 571 pipeline_init_state_.reset(new PipelineInitState()); |
| 566 | 572 |
| 567 SetState(kInitDemuxer); | 573 SetState(kInitDemuxer); |
| 568 InitializeDemuxer(); | 574 InitializeDemuxer(); |
| 569 } | 575 } |
| 570 | 576 |
| 571 // Main initialization method called on the pipeline thread. This code attempts | 577 // Main initialization method called on the pipeline thread. This code attempts |
| 572 // to use the specified filter factory to build a pipeline. | 578 // to use the specified filter factory to build a pipeline. |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 618 if (InitializeAudioRenderer(pipeline_init_state_->audio_decoder)) { | 624 if (InitializeAudioRenderer(pipeline_init_state_->audio_decoder)) { |
| 619 base::AutoLock auto_lock(lock_); | 625 base::AutoLock auto_lock(lock_); |
| 620 has_audio_ = true; | 626 has_audio_ = true; |
| 621 return; | 627 return; |
| 622 } | 628 } |
| 623 } | 629 } |
| 624 | 630 |
| 625 // Assuming audio renderer was created, create video renderer. | 631 // Assuming audio renderer was created, create video renderer. |
| 626 if (state_ == kInitAudioRenderer) { | 632 if (state_ == kInitAudioRenderer) { |
| 627 SetState(kInitVideoRenderer); | 633 SetState(kInitVideoRenderer); |
| 628 if (InitializeVideoRenderer(demuxer_->GetStream(DemuxerStream::VIDEO))) { | 634 scoped_refptr<DemuxerStream> video_stream = |
| 635 demuxer_->GetStream(DemuxerStream::VIDEO); |
| 636 if (InitializeVideoRenderer(video_stream)) { |
| 629 base::AutoLock auto_lock(lock_); | 637 base::AutoLock auto_lock(lock_); |
| 630 has_video_ = true; | 638 has_video_ = true; |
| 639 |
| 640 // Get an initial natural size so we have something when we signal |
| 641 // the kHaveMetadata ready state. |
| 642 natural_size_ = video_stream->video_decoder_config().natural_size(); |
| 631 return; | 643 return; |
| 632 } | 644 } |
| 633 } | 645 } |
| 634 | 646 |
| 635 if (state_ == kInitVideoRenderer) { | 647 if (state_ == kInitVideoRenderer) { |
| 636 if (!IsPipelineOk() || !(HasAudio() || HasVideo())) { | 648 if (!IsPipelineOk() || !(HasAudio() || HasVideo())) { |
| 637 SetError(PIPELINE_ERROR_COULD_NOT_RENDER); | 649 SetError(PIPELINE_ERROR_COULD_NOT_RENDER); |
| 638 return; | 650 return; |
| 639 } | 651 } |
| 640 | 652 |
| 641 // Clear initialization state now that we're done. | 653 // Clear initialization state now that we're done. |
| 642 filter_collection_.reset(); | 654 filter_collection_.reset(); |
| 643 pipeline_init_state_.reset(); | 655 pipeline_init_state_.reset(); |
| 644 | 656 |
| 645 // Initialization was successful, we are now considered paused, so it's safe | 657 // Initialization was successful, we are now considered paused, so it's safe |
| 646 // to set the initial playback rate and volume. | 658 // to set the initial playback rate and volume. |
| 647 PlaybackRateChangedTask(GetPlaybackRate()); | 659 PlaybackRateChangedTask(GetPlaybackRate()); |
| 648 VolumeChangedTask(GetVolume()); | 660 VolumeChangedTask(GetVolume()); |
| 649 | 661 |
| 662 if (!ready_state_cb_.is_null()) |
| 663 ready_state_cb_.Run(kHaveMetadata); |
| 664 |
| 650 // Fire a seek request to get the renderers to preroll. We can skip a seek | 665 // Fire a seek request to get the renderers to preroll. We can skip a seek |
| 651 // here as the demuxer should be at the start of the stream. | 666 // here as the demuxer should be at the start of the stream. |
| 652 seek_pending_ = true; | 667 seek_pending_ = true; |
| 653 SetState(kSeeking); | 668 SetState(kSeeking); |
| 654 seek_timestamp_ = demuxer_->GetStartTime(); | 669 seek_timestamp_ = demuxer_->GetStartTime(); |
| 655 DoSeek(seek_timestamp_, true, | 670 DoSeek(seek_timestamp_, true, |
| 656 base::Bind(&Pipeline::OnFilterStateTransition, this)); | 671 base::Bind(&Pipeline::OnFilterStateTransition, this)); |
| 657 } | 672 } |
| 658 } | 673 } |
| 659 | 674 |
| (...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 897 DoSeek(seek_timestamp_, false, | 912 DoSeek(seek_timestamp_, false, |
| 898 base::Bind(&Pipeline::OnFilterStateTransition, this)); | 913 base::Bind(&Pipeline::OnFilterStateTransition, this)); |
| 899 } else if (state_ == kStarting) { | 914 } else if (state_ == kStarting) { |
| 900 DoPlay(base::Bind(&Pipeline::OnFilterStateTransition, this)); | 915 DoPlay(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
| 901 } else if (state_ == kStopping) { | 916 } else if (state_ == kStopping) { |
| 902 DoStop(base::Bind(&Pipeline::OnFilterStateTransition, this)); | 917 DoStop(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
| 903 } else { | 918 } else { |
| 904 NOTREACHED() << "Unexpected state: " << state_; | 919 NOTREACHED() << "Unexpected state: " << state_; |
| 905 } | 920 } |
| 906 } else if (state_ == kStarted) { | 921 } else if (state_ == kStarted) { |
| 907 FinishInitialization(); | |
| 908 | 922 |
| 909 // Finally, complete the seek. | 923 // Fire canplaythrough immediately after playback begins because of |
| 910 seek_pending_ = false; | 924 // crbug.com/106480. |
| 925 // TODO(vrk): set ready state to HaveFutureData when bug above is fixed. |
| 926 if (!ready_state_cb_.is_null() && status_ == PIPELINE_OK) |
| 927 ready_state_cb_.Run(kHaveEnoughData); |
| 928 |
| 929 FinishSeek(); |
| 911 | 930 |
| 912 // If a playback rate change was requested during a seek, do it now that | 931 // If a playback rate change was requested during a seek, do it now that |
| 913 // the seek has compelted. | 932 // the seek has compelted. |
| 914 if (playback_rate_change_pending_) { | 933 if (playback_rate_change_pending_) { |
| 915 playback_rate_change_pending_ = false; | 934 playback_rate_change_pending_ = false; |
| 916 PlaybackRateChangedTask(pending_playback_rate_); | 935 PlaybackRateChangedTask(pending_playback_rate_); |
| 917 } | 936 } |
| 918 | 937 |
| 919 base::AutoLock auto_lock(lock_); | 938 base::AutoLock auto_lock(lock_); |
| 920 // We use audio stream to update the clock. So if there is such a stream, | 939 // We use audio stream to update the clock. So if there is such a stream, |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1136 case kInitAudioDecoder: | 1155 case kInitAudioDecoder: |
| 1137 case kInitAudioRenderer: | 1156 case kInitAudioRenderer: |
| 1138 case kInitVideoRenderer: | 1157 case kInitVideoRenderer: |
| 1139 // Make it look like initialization was successful. | 1158 // Make it look like initialization was successful. |
| 1140 filter_collection_.reset(); | 1159 filter_collection_.reset(); |
| 1141 pipeline_init_state_.reset(); | 1160 pipeline_init_state_.reset(); |
| 1142 | 1161 |
| 1143 SetState(kStopping); | 1162 SetState(kStopping); |
| 1144 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | 1163 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); |
| 1145 | 1164 |
| 1146 FinishInitialization(); | 1165 FinishSeek(); |
| 1147 break; | 1166 break; |
| 1148 | 1167 |
| 1149 case kPausing: | 1168 case kPausing: |
| 1150 case kSeeking: | 1169 case kSeeking: |
| 1151 case kFlushing: | 1170 case kFlushing: |
| 1152 case kStarting: | 1171 case kStarting: |
| 1153 SetState(kStopping); | 1172 SetState(kStopping); |
| 1154 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | 1173 DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); |
| 1155 | 1174 |
| 1156 if (seek_pending_) { | 1175 if (seek_pending_) |
| 1157 seek_pending_ = false; | 1176 FinishSeek(); |
| 1158 FinishInitialization(); | |
| 1159 } | |
| 1160 | 1177 |
| 1161 break; | 1178 break; |
| 1162 | 1179 |
| 1163 case kStarted: | 1180 case kStarted: |
| 1164 SetState(kPausing); | 1181 SetState(kPausing); |
| 1165 DoPause(base::Bind(&Pipeline::OnTeardownStateTransition, this)); | 1182 DoPause(base::Bind(&Pipeline::OnTeardownStateTransition, this)); |
| 1166 break; | 1183 break; |
| 1167 | 1184 |
| 1168 case kStopping: | 1185 case kStopping: |
| 1169 case kStopped: | 1186 case kStopped: |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1216 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { | 1233 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { |
| 1217 lock_.AssertAcquired(); | 1234 lock_.AssertAcquired(); |
| 1218 if (!waiting_for_clock_update_) | 1235 if (!waiting_for_clock_update_) |
| 1219 return; | 1236 return; |
| 1220 | 1237 |
| 1221 waiting_for_clock_update_ = false; | 1238 waiting_for_clock_update_ = false; |
| 1222 clock_->Play(); | 1239 clock_->Play(); |
| 1223 } | 1240 } |
| 1224 | 1241 |
| 1225 } // namespace media | 1242 } // namespace media |
| OLD | NEW |