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