Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/renderers/video_renderer_impl.h" | 5 #include "media/renderers/video_renderer_impl.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback.h" | 8 #include "base/callback.h" |
| 9 #include "base/callback_helpers.h" | 9 #include "base/callback_helpers.h" |
| 10 #include "base/command_line.h" | |
| 10 #include "base/location.h" | 11 #include "base/location.h" |
| 11 #include "base/single_thread_task_runner.h" | 12 #include "base/single_thread_task_runner.h" |
| 12 #include "base/threading/platform_thread.h" | 13 #include "base/threading/platform_thread.h" |
| 13 #include "base/time/default_tick_clock.h" | 14 #include "base/time/default_tick_clock.h" |
| 14 #include "base/trace_event/trace_event.h" | 15 #include "base/trace_event/trace_event.h" |
| 15 #include "media/base/bind_to_current_loop.h" | 16 #include "media/base/bind_to_current_loop.h" |
| 16 #include "media/base/buffers.h" | 17 #include "media/base/buffers.h" |
| 17 #include "media/base/limits.h" | 18 #include "media/base/limits.h" |
| 19 #include "media/base/media_switches.h" | |
| 18 #include "media/base/pipeline.h" | 20 #include "media/base/pipeline.h" |
| 19 #include "media/base/video_frame.h" | 21 #include "media/base/video_frame.h" |
| 20 | 22 |
| 21 namespace media { | 23 namespace media { |
| 22 | 24 |
| 25 // Wait a quarter of a second for Render() callbacks before starting background | |
| 26 // rendering to keep the decode pump moving. | |
| 27 static const int kBackgroundRenderingTimeoutMs = 250; | |
|
xhwang
2015/04/29 06:51:54
nit: s/static//
DaleCurtis
2015/04/30 03:49:37
Done.
| |
| 28 | |
| 23 VideoRendererImpl::VideoRendererImpl( | 29 VideoRendererImpl::VideoRendererImpl( |
| 24 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 30 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 25 VideoRendererSink* sink, | 31 VideoRendererSink* sink, |
| 26 ScopedVector<VideoDecoder> decoders, | 32 ScopedVector<VideoDecoder> decoders, |
| 27 bool drop_frames, | 33 bool drop_frames, |
| 28 const scoped_refptr<MediaLog>& media_log) | 34 const scoped_refptr<MediaLog>& media_log) |
| 29 : task_runner_(task_runner), | 35 : task_runner_(task_runner), |
| 36 use_new_video_renderering_path_( | |
| 37 base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 38 switches::kEnableNewVideoRenderer)), | |
| 30 sink_(sink), | 39 sink_(sink), |
| 31 video_frame_stream_( | 40 video_frame_stream_( |
| 32 new VideoFrameStream(task_runner, decoders.Pass(), media_log)), | 41 new VideoFrameStream(task_runner, decoders.Pass(), media_log)), |
| 33 low_delay_(false), | 42 low_delay_(false), |
| 34 received_end_of_stream_(false), | 43 received_end_of_stream_(false), |
| 35 rendered_end_of_stream_(false), | 44 rendered_end_of_stream_(false), |
| 36 frame_available_(&lock_), | 45 frame_available_(&lock_), |
| 37 state_(kUninitialized), | 46 state_(kUninitialized), |
| 38 thread_(), | 47 thread_(), |
| 39 pending_read_(false), | 48 pending_read_(false), |
| 40 drop_frames_(drop_frames), | 49 drop_frames_(drop_frames), |
| 41 buffering_state_(BUFFERING_HAVE_NOTHING), | 50 buffering_state_(BUFFERING_HAVE_NOTHING), |
| 42 frames_decoded_(0), | 51 frames_decoded_(0), |
| 43 frames_dropped_(0), | 52 frames_dropped_(0), |
| 44 is_shutting_down_(false), | 53 is_shutting_down_(false), |
| 45 tick_clock_(new base::DefaultTickClock()), | 54 tick_clock_(new base::DefaultTickClock()), |
| 55 is_background_rendering_(false), | |
| 56 should_use_background_renderering_(true), | |
| 57 time_progressing_(false), | |
| 58 render_first_frame_and_stop_(false), | |
| 59 background_rendering_timeout_( | |
| 60 base::TimeDelta::FromMilliseconds(kBackgroundRenderingTimeoutMs)), | |
| 46 weak_factory_(this) { | 61 weak_factory_(this) { |
| 47 } | 62 } |
| 48 | 63 |
| 49 VideoRendererImpl::~VideoRendererImpl() { | 64 VideoRendererImpl::~VideoRendererImpl() { |
| 50 DCHECK(task_runner_->BelongsToCurrentThread()); | 65 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 51 | 66 |
| 52 { | 67 if (!use_new_video_renderering_path_) { |
| 53 base::AutoLock auto_lock(lock_); | 68 base::AutoLock auto_lock(lock_); |
| 54 is_shutting_down_ = true; | 69 is_shutting_down_ = true; |
| 55 frame_available_.Signal(); | 70 frame_available_.Signal(); |
| 56 } | 71 } |
| 57 | 72 |
| 58 if (!thread_.is_null()) | 73 if (!thread_.is_null()) |
| 59 base::PlatformThread::Join(thread_); | 74 base::PlatformThread::Join(thread_); |
| 60 | 75 |
| 61 if (!init_cb_.is_null()) | 76 if (!init_cb_.is_null()) |
| 62 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT); | 77 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT); |
| 63 | 78 |
| 64 if (!flush_cb_.is_null()) | 79 if (!flush_cb_.is_null()) |
| 65 base::ResetAndReturn(&flush_cb_).Run(); | 80 base::ResetAndReturn(&flush_cb_).Run(); |
| 81 | |
| 82 if (use_new_video_renderering_path_ && | |
| 83 (time_progressing_ || render_first_frame_and_stop_)) { | |
| 84 StopSink(); | |
| 85 } | |
| 66 } | 86 } |
| 67 | 87 |
| 68 void VideoRendererImpl::Flush(const base::Closure& callback) { | 88 void VideoRendererImpl::Flush(const base::Closure& callback) { |
| 69 DVLOG(1) << __FUNCTION__; | 89 DVLOG(1) << __FUNCTION__; |
| 70 DCHECK(task_runner_->BelongsToCurrentThread()); | 90 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 71 base::AutoLock auto_lock(lock_); | 91 base::AutoLock auto_lock(lock_); |
| 72 DCHECK_EQ(state_, kPlaying); | 92 DCHECK_EQ(state_, kPlaying); |
| 73 flush_cb_ = callback; | 93 flush_cb_ = callback; |
| 74 state_ = kFlushing; | 94 state_ = kFlushing; |
| 75 | 95 |
| 76 // This is necessary if the |video_frame_stream_| has already seen an end of | 96 // This is necessary if the |video_frame_stream_| has already seen an end of |
| 77 // stream and needs to drain it before flushing it. | 97 // stream and needs to drain it before flushing it. |
| 78 ready_frames_.clear(); | 98 ready_frames_.clear(); |
| 79 if (buffering_state_ != BUFFERING_HAVE_NOTHING) { | 99 if (buffering_state_ != BUFFERING_HAVE_NOTHING) { |
| 80 buffering_state_ = BUFFERING_HAVE_NOTHING; | 100 buffering_state_ = BUFFERING_HAVE_NOTHING; |
| 81 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING); | 101 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING); |
| 82 } | 102 } |
| 83 received_end_of_stream_ = false; | 103 received_end_of_stream_ = false; |
| 84 rendered_end_of_stream_ = false; | 104 rendered_end_of_stream_ = false; |
| 85 | 105 |
| 106 if (use_new_video_renderering_path_) | |
| 107 algorithm_->Reset(); | |
| 108 | |
| 86 video_frame_stream_->Reset( | 109 video_frame_stream_->Reset( |
| 87 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone, | 110 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone, |
| 88 weak_factory_.GetWeakPtr())); | 111 weak_factory_.GetWeakPtr())); |
| 89 } | 112 } |
| 90 | 113 |
| 91 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { | 114 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { |
| 92 DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")"; | 115 DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")"; |
| 93 DCHECK(task_runner_->BelongsToCurrentThread()); | 116 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 94 base::AutoLock auto_lock(lock_); | 117 base::AutoLock auto_lock(lock_); |
| 95 DCHECK_EQ(state_, kFlushed); | 118 DCHECK_EQ(state_, kFlushed); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 130 init_cb_ = BindToCurrentLoop(init_cb); | 153 init_cb_ = BindToCurrentLoop(init_cb); |
| 131 | 154 |
| 132 statistics_cb_ = statistics_cb; | 155 statistics_cb_ = statistics_cb; |
| 133 buffering_state_cb_ = buffering_state_cb; | 156 buffering_state_cb_ = buffering_state_cb; |
| 134 paint_cb_ = base::Bind(&VideoRendererSink::PaintFrameUsingOldRenderingPath, | 157 paint_cb_ = base::Bind(&VideoRendererSink::PaintFrameUsingOldRenderingPath, |
| 135 base::Unretained(sink_)); | 158 base::Unretained(sink_)); |
| 136 ended_cb_ = ended_cb; | 159 ended_cb_ = ended_cb; |
| 137 error_cb_ = error_cb; | 160 error_cb_ = error_cb; |
| 138 wall_clock_time_cb_ = wall_clock_time_cb; | 161 wall_clock_time_cb_ = wall_clock_time_cb; |
| 139 state_ = kInitializing; | 162 state_ = kInitializing; |
| 163 render_first_frame_and_stop_ = false; | |
| 164 is_background_rendering_ = false; | |
| 165 time_progressing_ = false; | |
|
xhwang
2015/04/29 06:51:55
Can we DCHECK these three?
DaleCurtis
2015/04/30 03:49:37
Done.
| |
| 140 | 166 |
| 141 video_frame_stream_->Initialize( | 167 video_frame_stream_->Initialize( |
| 142 stream, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized, | 168 stream, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized, |
| 143 weak_factory_.GetWeakPtr()), | 169 weak_factory_.GetWeakPtr()), |
| 144 set_decryptor_ready_cb, statistics_cb, waiting_for_decryption_key_cb); | 170 set_decryptor_ready_cb, statistics_cb, waiting_for_decryption_key_cb); |
| 145 } | 171 } |
| 146 | 172 |
| 147 scoped_refptr<VideoFrame> VideoRendererImpl::Render( | 173 scoped_refptr<VideoFrame> VideoRendererImpl::Render( |
| 148 base::TimeTicks deadline_min, | 174 base::TimeTicks deadline_min, |
| 149 base::TimeTicks deadline_max) { | 175 base::TimeTicks deadline_max) { |
| 150 // TODO(dalecurtis): Hook this up to the new VideoRendererAlgorithm. | 176 base::AutoLock auto_lock(lock_); |
| 151 NOTIMPLEMENTED(); | 177 DCHECK(use_new_video_renderering_path_); |
| 152 return nullptr; | 178 |
| 179 size_t frames_dropped = 0; | |
| 180 scoped_refptr<VideoFrame> result = | |
| 181 algorithm_->Render(deadline_min, deadline_max, &frames_dropped); | |
| 182 | |
| 183 // Due to how the |algorithm_| holds frames, this should never be null if | |
| 184 // we've had a proper startup sequence. | |
| 185 DCHECK(result); | |
| 186 | |
| 187 // See if it's time to fire the ended callback. | |
| 188 const size_t effective_frames = MaybeFireEndedCallback(); | |
| 189 | |
| 190 // Declare HAVE_NOTHING if we have no more effective frames. | |
| 191 if (!effective_frames && !rendered_end_of_stream_ && | |
| 192 buffering_state_ == BUFFERING_HAVE_ENOUGH) { | |
| 193 buffering_state_ = BUFFERING_HAVE_NOTHING; | |
| 194 task_runner_->PostTask( | |
| 195 FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING)); | |
| 196 } | |
| 197 | |
| 198 // As we resume from background rendering, don't count the initial batch of | |
| 199 // dropped frames since they are likely just dropped due to being too old. | |
| 200 if (!is_background_rendering_) { | |
| 201 frames_dropped_ += frames_dropped; | |
| 202 UpdateStatsAndWait_Locked(base::TimeDelta()); | |
| 203 } | |
| 204 is_background_rendering_ = false; | |
| 205 | |
| 206 // After painting the first frame, if playback hasn't started, we request that | |
| 207 // the sink be stopped. OnTimeStateChanged() will clear this flag if time | |
| 208 // starts before we get here and MaybeStopSinkAfterFirstPaint() will ignore | |
| 209 // this request if time starts before the call executes. | |
| 210 if (render_first_frame_and_stop_) { | |
| 211 task_runner_->PostTask( | |
| 212 FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint, | |
| 213 weak_factory_.GetWeakPtr())); | |
| 214 } | |
| 215 | |
| 216 // Always post this task, it will acquire new frames if necessary, reset the | |
| 217 // background rendering timer, and more. | |
| 218 task_runner_->PostTask(FROM_HERE, base::Bind(&VideoRendererImpl::AttemptRead, | |
| 219 weak_factory_.GetWeakPtr())); | |
| 220 | |
| 221 return result; | |
| 153 } | 222 } |
| 154 | 223 |
| 155 void VideoRendererImpl::OnFrameDropped() { | 224 void VideoRendererImpl::OnFrameDropped() { |
| 156 // TODO(dalecurtis): Hook this up to the new VideoRendererAlgorithm. | 225 base::AutoLock auto_lock(lock_); |
| 157 NOTIMPLEMENTED(); | 226 DCHECK(use_new_video_renderering_path_); |
| 227 algorithm_->OnLastFrameDropped(); | |
| 228 } | |
| 229 | |
| 230 void VideoRendererImpl::SetBackgroundRenderingForTesting( | |
| 231 bool enabled, | |
| 232 base::TimeDelta timeout) { | |
| 233 should_use_background_renderering_ = enabled; | |
| 234 background_rendering_timeout_ = timeout; | |
| 158 } | 235 } |
| 159 | 236 |
| 160 void VideoRendererImpl::CreateVideoThread() { | 237 void VideoRendererImpl::CreateVideoThread() { |
| 161 // This may fail and cause a crash if there are too many threads created in | 238 // This may fail and cause a crash if there are too many threads created in |
| 162 // the current process. See http://crbug.com/443291 | 239 // the current process. See http://crbug.com/443291 |
| 163 CHECK(base::PlatformThread::Create(0, this, &thread_)); | 240 CHECK(base::PlatformThread::Create(0, this, &thread_)); |
| 164 | 241 |
| 165 #if defined(OS_WIN) | 242 #if defined(OS_WIN) |
| 166 // Bump up our priority so our sleeping is more accurate. | 243 // Bump up our priority so our sleeping is more accurate. |
| 167 // TODO(scherkus): find out if this is necessary, but it seems to help. | 244 // TODO(scherkus): find out if this is necessary, but it seems to help. |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 179 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); | 256 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); |
| 180 return; | 257 return; |
| 181 } | 258 } |
| 182 | 259 |
| 183 // We're all good! Consider ourselves flushed. (ThreadMain() should never | 260 // We're all good! Consider ourselves flushed. (ThreadMain() should never |
| 184 // see us in the kUninitialized state). | 261 // see us in the kUninitialized state). |
| 185 // Since we had an initial Preroll(), we consider ourself flushed, because we | 262 // Since we had an initial Preroll(), we consider ourself flushed, because we |
| 186 // have not populated any buffers yet. | 263 // have not populated any buffers yet. |
| 187 state_ = kFlushed; | 264 state_ = kFlushed; |
| 188 | 265 |
| 189 CreateVideoThread(); | 266 if (use_new_video_renderering_path_) { |
| 267 algorithm_.reset(new VideoRendererAlgorithm(wall_clock_time_cb_)); | |
| 268 if (!drop_frames_) | |
| 269 algorithm_->disable_frame_dropping(); | |
| 270 } else { | |
| 271 CreateVideoThread(); | |
| 272 } | |
| 190 | 273 |
| 191 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); | 274 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); |
| 192 } | 275 } |
| 193 | 276 |
| 194 // PlatformThread::Delegate implementation. | 277 // PlatformThread::Delegate implementation. |
| 195 void VideoRendererImpl::ThreadMain() { | 278 void VideoRendererImpl::ThreadMain() { |
| 279 DCHECK(!use_new_video_renderering_path_); | |
| 196 base::PlatformThread::SetName("CrVideoRenderer"); | 280 base::PlatformThread::SetName("CrVideoRenderer"); |
| 197 | 281 |
| 198 // The number of milliseconds to idle when we do not have anything to do. | 282 // The number of milliseconds to idle when we do not have anything to do. |
| 199 // Nothing special about the value, other than we're being more OS-friendly | 283 // Nothing special about the value, other than we're being more OS-friendly |
| 200 // than sleeping for 1 millisecond. | 284 // than sleeping for 1 millisecond. |
| 201 // | 285 // |
| 202 // TODO(scherkus): switch to pure event-driven frame timing instead of this | 286 // TODO(scherkus): switch to pure event-driven frame timing instead of this |
| 203 // kIdleTimeDelta business http://crbug.com/106874 | 287 // kIdleTimeDelta business http://crbug.com/106874 |
| 204 const base::TimeDelta kIdleTimeDelta = | 288 const base::TimeDelta kIdleTimeDelta = |
| 205 base::TimeDelta::FromMilliseconds(10); | 289 base::TimeDelta::FromMilliseconds(10); |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 283 PaintNextReadyFrame_Locked(); | 367 PaintNextReadyFrame_Locked(); |
| 284 } | 368 } |
| 285 } | 369 } |
| 286 | 370 |
| 287 void VideoRendererImpl::SetTickClockForTesting( | 371 void VideoRendererImpl::SetTickClockForTesting( |
| 288 scoped_ptr<base::TickClock> tick_clock) { | 372 scoped_ptr<base::TickClock> tick_clock) { |
| 289 tick_clock_.swap(tick_clock); | 373 tick_clock_.swap(tick_clock); |
| 290 } | 374 } |
| 291 | 375 |
| 292 void VideoRendererImpl::OnTimeStateChanged(bool time_progressing) { | 376 void VideoRendererImpl::OnTimeStateChanged(bool time_progressing) { |
| 293 // TODO(dalecurtis): Wire up to the VideoRendererSink once it's implemented. | 377 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 378 const bool was_progressing = time_progressing_; | |
| 379 time_progressing_ = time_progressing; | |
| 380 | |
| 381 // WARNING: Do not attempt to use |lock_| here as this may be a reentrant call | |
| 382 // in response to callbacks firing above. | |
| 383 | |
| 384 if (!use_new_video_renderering_path_ || | |
| 385 was_progressing == time_progressing_ || render_first_frame_and_stop_) { | |
| 386 return; | |
| 387 } | |
| 388 | |
| 389 if (time_progressing_) { | |
| 390 StartSink(); | |
| 391 return; | |
| 392 } | |
| 393 | |
| 394 StopSink(); | |
| 294 } | 395 } |
| 295 | 396 |
| 296 void VideoRendererImpl::PaintNextReadyFrame_Locked() { | 397 void VideoRendererImpl::PaintNextReadyFrame_Locked() { |
| 398 DCHECK(!use_new_video_renderering_path_); | |
| 297 lock_.AssertAcquired(); | 399 lock_.AssertAcquired(); |
| 298 | 400 |
| 299 scoped_refptr<VideoFrame> next_frame = ready_frames_.front(); | 401 scoped_refptr<VideoFrame> next_frame = ready_frames_.front(); |
| 300 ready_frames_.pop_front(); | 402 ready_frames_.pop_front(); |
| 301 frames_decoded_++; | 403 frames_decoded_++; |
| 302 | 404 |
| 303 last_media_time_ = wall_clock_time_cb_.Run(next_frame->timestamp()); | 405 last_media_time_ = wall_clock_time_cb_.Run(next_frame->timestamp()); |
| 304 | 406 |
| 305 paint_cb_.Run(next_frame); | 407 paint_cb_.Run(next_frame); |
| 306 | 408 |
| 307 task_runner_->PostTask( | 409 task_runner_->PostTask( |
| 308 FROM_HERE, | 410 FROM_HERE, |
| 309 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); | 411 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); |
| 310 } | 412 } |
| 311 | 413 |
| 312 void VideoRendererImpl::DropNextReadyFrame_Locked() { | 414 void VideoRendererImpl::DropNextReadyFrame_Locked() { |
| 415 DCHECK(!use_new_video_renderering_path_); | |
| 313 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped"); | 416 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped"); |
| 314 | 417 |
| 315 lock_.AssertAcquired(); | 418 lock_.AssertAcquired(); |
| 316 | 419 |
| 317 last_media_time_ = | 420 last_media_time_ = |
| 318 wall_clock_time_cb_.Run(ready_frames_.front()->timestamp()); | 421 wall_clock_time_cb_.Run(ready_frames_.front()->timestamp()); |
| 319 | 422 |
| 320 ready_frames_.pop_front(); | 423 ready_frames_.pop_front(); |
| 321 frames_decoded_++; | 424 frames_decoded_++; |
| 322 frames_dropped_++; | 425 frames_dropped_++; |
| 323 | 426 |
| 324 task_runner_->PostTask( | 427 task_runner_->PostTask( |
| 325 FROM_HERE, | 428 FROM_HERE, |
| 326 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); | 429 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); |
| 327 } | 430 } |
| 328 | 431 |
| 329 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, | 432 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, |
| 330 const scoped_refptr<VideoFrame>& frame) { | 433 const scoped_refptr<VideoFrame>& frame) { |
| 331 DCHECK(task_runner_->BelongsToCurrentThread()); | 434 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 332 base::AutoLock auto_lock(lock_); | 435 bool start_sink = false; |
| 333 DCHECK_NE(state_, kUninitialized); | 436 { |
| 334 DCHECK_NE(state_, kFlushed); | 437 base::AutoLock auto_lock(lock_); |
| 438 DCHECK_NE(state_, kUninitialized); | |
| 439 DCHECK_NE(state_, kFlushed); | |
| 335 | 440 |
| 336 CHECK(pending_read_); | 441 CHECK(pending_read_); |
| 337 pending_read_ = false; | 442 pending_read_ = false; |
| 338 | 443 |
| 339 if (status == VideoFrameStream::DECODE_ERROR || | 444 if (status == VideoFrameStream::DECODE_ERROR || |
| 340 status == VideoFrameStream::DECRYPT_ERROR) { | 445 status == VideoFrameStream::DECRYPT_ERROR) { |
| 341 DCHECK(!frame.get()); | 446 DCHECK(!frame.get()); |
| 342 PipelineStatus error = PIPELINE_ERROR_DECODE; | 447 PipelineStatus error = PIPELINE_ERROR_DECODE; |
| 343 if (status == VideoFrameStream::DECRYPT_ERROR) | 448 if (status == VideoFrameStream::DECRYPT_ERROR) |
| 344 error = PIPELINE_ERROR_DECRYPT; | 449 error = PIPELINE_ERROR_DECRYPT; |
| 345 task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error)); | 450 task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error)); |
| 346 return; | 451 return; |
| 452 } | |
| 453 | |
| 454 // Already-queued VideoFrameStream ReadCB's can fire after various state | |
| 455 // transitions have happened; in that case just drop those frames | |
| 456 // immediately. | |
| 457 if (state_ == kFlushing) | |
| 458 return; | |
| 459 | |
| 460 DCHECK_EQ(state_, kPlaying); | |
| 461 | |
| 462 // Can happen when demuxers are preparing for a new Seek(). | |
| 463 if (!frame.get()) { | |
| 464 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED); | |
| 465 return; | |
| 466 } | |
| 467 | |
| 468 if (frame->end_of_stream()) { | |
| 469 DCHECK(!received_end_of_stream_); | |
| 470 received_end_of_stream_ = true; | |
| 471 | |
| 472 // See if we can fire EOS immediately instead of waiting for Render() or | |
| 473 // BackgroundRender() to tick. | |
| 474 if (use_new_video_renderering_path_ && time_progressing_) | |
| 475 MaybeFireEndedCallback(); | |
| 476 } else { | |
| 477 // Maintain the latest frame decoded so the correct frame is displayed | |
| 478 // after prerolling has completed. | |
| 479 if (frame->timestamp() <= start_timestamp_) { | |
| 480 if (use_new_video_renderering_path_) | |
| 481 algorithm_->Reset(); | |
| 482 ready_frames_.clear(); | |
| 483 } | |
| 484 AddReadyFrame_Locked(frame); | |
| 485 } | |
| 486 | |
| 487 // Signal buffering state if we've met our conditions for having enough | |
| 488 // data. | |
| 489 if (buffering_state_ != BUFFERING_HAVE_ENOUGH && HaveEnoughData_Locked()) { | |
| 490 TransitionToHaveEnough_Locked(); | |
| 491 start_sink = true; | |
| 492 render_first_frame_and_stop_ = true; | |
| 493 } | |
| 494 | |
| 495 // BackgroundRender may not be ticking fast enough by itself to remove | |
| 496 // expired frames, so give it a boost here by ensuring we don't exit the | |
| 497 // decoding cycle too early. | |
| 498 if (is_background_rendering_) { | |
| 499 DCHECK(use_new_video_renderering_path_); | |
| 500 BackgroundRender_Locked(); | |
| 501 } | |
| 502 | |
| 503 // Always request more decoded video if we have capacity. This serves two | |
| 504 // purposes: | |
| 505 // 1) Prerolling while paused | |
| 506 // 2) Keeps decoding going if video rendering thread starts falling behind | |
| 507 AttemptRead_Locked(); | |
| 347 } | 508 } |
| 348 | 509 |
| 349 // Already-queued VideoFrameStream ReadCB's can fire after various state | 510 // If time is progressing, the sink has already been started; this may be true |
| 350 // transitions have happened; in that case just drop those frames immediately. | 511 // if we have previously underflowed, yet weren't stopped because of audio. |
| 351 if (state_ == kFlushing) | 512 if (use_new_video_renderering_path_ && !time_progressing_ && start_sink) |
| 352 return; | 513 StartSink(); |
| 353 | |
| 354 DCHECK_EQ(state_, kPlaying); | |
| 355 | |
| 356 // Can happen when demuxers are preparing for a new Seek(). | |
| 357 if (!frame.get()) { | |
| 358 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED); | |
| 359 return; | |
| 360 } | |
| 361 | |
| 362 if (frame->end_of_stream()) { | |
| 363 DCHECK(!received_end_of_stream_); | |
| 364 received_end_of_stream_ = true; | |
| 365 } else { | |
| 366 // Maintain the latest frame decoded so the correct frame is displayed after | |
| 367 // prerolling has completed. | |
| 368 if (frame->timestamp() <= start_timestamp_) | |
| 369 ready_frames_.clear(); | |
| 370 AddReadyFrame_Locked(frame); | |
| 371 } | |
| 372 | |
| 373 // Signal buffering state if we've met our conditions for having enough data. | |
| 374 if (buffering_state_ != BUFFERING_HAVE_ENOUGH && HaveEnoughData_Locked()) | |
| 375 TransitionToHaveEnough_Locked(); | |
| 376 | |
| 377 // Always request more decoded video if we have capacity. This serves two | |
| 378 // purposes: | |
| 379 // 1) Prerolling while paused | |
| 380 // 2) Keeps decoding going if video rendering thread starts falling behind | |
| 381 AttemptRead_Locked(); | |
| 382 } | 514 } |
| 383 | 515 |
| 384 bool VideoRendererImpl::HaveEnoughData_Locked() { | 516 bool VideoRendererImpl::HaveEnoughData_Locked() { |
| 385 DCHECK_EQ(state_, kPlaying); | 517 DCHECK_EQ(state_, kPlaying); |
| 386 return received_end_of_stream_ || | 518 |
| 387 !video_frame_stream_->CanReadWithoutStalling() || | 519 if (received_end_of_stream_ || !video_frame_stream_->CanReadWithoutStalling()) |
| 388 ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames) || | 520 return true; |
| 389 (low_delay_ && ready_frames_.size() > 0); | 521 |
| 522 if (HaveReachedBufferingCap()) | |
| 523 return true; | |
| 524 | |
| 525 if (!low_delay_) | |
| 526 return false; | |
| 527 | |
| 528 return ready_frames_.size() > 0 || | |
| 529 (use_new_video_renderering_path_ && algorithm_->frames_queued() > 0); | |
| 390 } | 530 } |
| 391 | 531 |
| 392 void VideoRendererImpl::TransitionToHaveEnough_Locked() { | 532 void VideoRendererImpl::TransitionToHaveEnough_Locked() { |
| 393 DCHECK(task_runner_->BelongsToCurrentThread()); | 533 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 394 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); | 534 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); |
| 395 | 535 |
| 396 if (!ready_frames_.empty()) { | 536 if (!ready_frames_.empty()) { |
| 537 DCHECK(!use_new_video_renderering_path_); | |
| 397 // Because the clock might remain paused in for an undetermined amount | 538 // Because the clock might remain paused in for an undetermined amount |
| 398 // of time (e.g., seeking while paused), paint the first frame. | 539 // of time (e.g., seeking while paused), paint the first frame. |
| 399 PaintNextReadyFrame_Locked(); | 540 PaintNextReadyFrame_Locked(); |
| 400 } | 541 } |
| 401 | 542 |
| 402 buffering_state_ = BUFFERING_HAVE_ENOUGH; | 543 buffering_state_ = BUFFERING_HAVE_ENOUGH; |
| 403 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH); | 544 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH); |
| 404 } | 545 } |
| 405 | 546 |
| 406 void VideoRendererImpl::AddReadyFrame_Locked( | 547 void VideoRendererImpl::AddReadyFrame_Locked( |
| 407 const scoped_refptr<VideoFrame>& frame) { | 548 const scoped_refptr<VideoFrame>& frame) { |
| 408 DCHECK(task_runner_->BelongsToCurrentThread()); | 549 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 409 lock_.AssertAcquired(); | 550 lock_.AssertAcquired(); |
| 410 DCHECK(!frame->end_of_stream()); | 551 DCHECK(!frame->end_of_stream()); |
| 411 | 552 |
| 553 if (use_new_video_renderering_path_) { | |
| 554 algorithm_->EnqueueFrame(frame); | |
| 555 return; | |
| 556 } | |
| 557 | |
| 412 ready_frames_.push_back(frame); | 558 ready_frames_.push_back(frame); |
| 413 DCHECK_LE(ready_frames_.size(), | 559 DCHECK_LE(ready_frames_.size(), |
| 414 static_cast<size_t>(limits::kMaxVideoFrames)); | 560 static_cast<size_t>(limits::kMaxVideoFrames)); |
| 415 | 561 |
| 416 // Avoid needlessly waking up |thread_| unless playing. | 562 // Avoid needlessly waking up |thread_| unless playing. |
| 417 if (state_ == kPlaying) | 563 if (state_ == kPlaying) |
| 418 frame_available_.Signal(); | 564 frame_available_.Signal(); |
| 419 } | 565 } |
| 420 | 566 |
| 421 void VideoRendererImpl::AttemptRead() { | 567 void VideoRendererImpl::AttemptRead() { |
| 422 base::AutoLock auto_lock(lock_); | 568 base::AutoLock auto_lock(lock_); |
| 423 AttemptRead_Locked(); | 569 AttemptRead_Locked(); |
| 424 } | 570 } |
| 425 | 571 |
| 426 void VideoRendererImpl::AttemptRead_Locked() { | 572 void VideoRendererImpl::AttemptRead_Locked() { |
| 427 DCHECK(task_runner_->BelongsToCurrentThread()); | 573 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 428 lock_.AssertAcquired(); | 574 lock_.AssertAcquired(); |
| 429 | 575 |
| 430 if (pending_read_ || received_end_of_stream_ || | 576 if (pending_read_ || received_end_of_stream_) |
| 431 ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) { | |
| 432 return; | 577 return; |
| 433 } | 578 |
| 579 if (use_new_video_renderering_path_ && time_progressing_) | |
| 580 RestartBackgroundRenderTimer(); | |
|
xhwang
2015/04/29 06:51:54
It's a bit unclear to me when we should call this.
DaleCurtis
2015/04/30 03:49:37
Per offline discussion replaced with a repeating t
| |
| 581 | |
| 582 if (HaveReachedBufferingCap()) | |
| 583 return; | |
| 434 | 584 |
| 435 switch (state_) { | 585 switch (state_) { |
| 436 case kPlaying: | 586 case kPlaying: |
| 437 pending_read_ = true; | 587 pending_read_ = true; |
| 438 video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady, | 588 video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady, |
| 439 weak_factory_.GetWeakPtr())); | 589 weak_factory_.GetWeakPtr())); |
| 440 return; | 590 return; |
| 441 | 591 |
| 442 case kUninitialized: | 592 case kUninitialized: |
| 443 case kInitializing: | 593 case kInitializing: |
| 444 case kFlushing: | 594 case kFlushing: |
| 445 case kFlushed: | 595 case kFlushed: |
| 446 return; | 596 return; |
| 447 } | 597 } |
| 448 } | 598 } |
| 449 | 599 |
| 450 void VideoRendererImpl::OnVideoFrameStreamResetDone() { | 600 void VideoRendererImpl::OnVideoFrameStreamResetDone() { |
| 451 base::AutoLock auto_lock(lock_); | 601 base::AutoLock auto_lock(lock_); |
| 452 DCHECK_EQ(kFlushing, state_); | 602 DCHECK_EQ(kFlushing, state_); |
| 453 DCHECK(!pending_read_); | 603 DCHECK(!pending_read_); |
| 454 DCHECK(ready_frames_.empty()); | 604 DCHECK(ready_frames_.empty()); |
| 455 DCHECK(!received_end_of_stream_); | 605 DCHECK(!received_end_of_stream_); |
| 456 DCHECK(!rendered_end_of_stream_); | 606 DCHECK(!rendered_end_of_stream_); |
| 457 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); | 607 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); |
| 458 | 608 |
| 459 state_ = kFlushed; | 609 state_ = kFlushed; |
| 460 latest_possible_paint_time_ = last_media_time_ = base::TimeTicks(); | 610 latest_possible_paint_time_ = last_media_time_ = base::TimeTicks(); |
| 611 | |
| 461 base::ResetAndReturn(&flush_cb_).Run(); | 612 base::ResetAndReturn(&flush_cb_).Run(); |
| 462 } | 613 } |
| 463 | 614 |
| 464 void VideoRendererImpl::UpdateStatsAndWait_Locked( | 615 void VideoRendererImpl::UpdateStatsAndWait_Locked( |
| 465 base::TimeDelta wait_duration) { | 616 base::TimeDelta wait_duration) { |
| 466 lock_.AssertAcquired(); | 617 lock_.AssertAcquired(); |
| 467 DCHECK_GE(frames_decoded_, 0); | 618 DCHECK_GE(frames_decoded_, 0); |
| 468 DCHECK_LE(frames_dropped_, frames_decoded_); | 619 DCHECK_GE(frames_dropped_, 0); |
| 469 | 620 |
| 470 if (frames_decoded_) { | 621 if (frames_decoded_ || frames_dropped_) { |
| 471 PipelineStatistics statistics; | 622 PipelineStatistics statistics; |
| 472 statistics.video_frames_decoded = frames_decoded_; | 623 statistics.video_frames_decoded = frames_decoded_; |
| 473 statistics.video_frames_dropped = frames_dropped_; | 624 statistics.video_frames_dropped = frames_dropped_; |
| 474 task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics)); | 625 task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics)); |
| 475 | 626 |
| 476 frames_decoded_ = 0; | 627 frames_decoded_ = 0; |
| 477 frames_dropped_ = 0; | 628 frames_dropped_ = 0; |
| 478 } | 629 } |
| 479 | 630 |
| 480 frame_available_.TimedWait(wait_duration); | 631 if (wait_duration > base::TimeDelta()) |
| 632 frame_available_.TimedWait(wait_duration); | |
| 633 } | |
| 634 | |
| 635 void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() { | |
| 636 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 637 DCHECK(use_new_video_renderering_path_); | |
| 638 | |
| 639 { | |
| 640 base::AutoLock auto_lock(lock_); | |
| 641 render_first_frame_and_stop_ = false; | |
| 642 } | |
| 643 | |
| 644 if (!time_progressing_) | |
| 645 StopSink(); | |
| 646 } | |
| 647 | |
| 648 void VideoRendererImpl::RestartBackgroundRenderTimer() { | |
| 649 DCHECK(use_new_video_renderering_path_); | |
| 650 if (!drop_frames_ || !should_use_background_renderering_) | |
| 651 return; | |
| 652 | |
| 653 // This controls the amount of time allowed between Render() callbacks before | |
| 654 // we assume they have timed out and it's necessary to start expiring | |
| 655 // unrendered frames. | |
| 656 background_rendering_timer_.Start( | |
| 657 FROM_HERE, background_rendering_timeout_, | |
| 658 base::Bind(&VideoRendererImpl::BackgroundRender, | |
| 659 weak_factory_.GetWeakPtr())); | |
| 660 } | |
| 661 | |
| 662 void VideoRendererImpl::BackgroundRender() { | |
| 663 base::AutoLock auto_lock(lock_); | |
| 664 BackgroundRender_Locked(); | |
| 665 } | |
| 666 | |
| 667 void VideoRendererImpl::BackgroundRender_Locked() { | |
| 668 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 669 lock_.AssertAcquired(); | |
| 670 | |
| 671 // If a Render() call never occurs after starting playback for the first frame | |
| 672 // we need to carry out the duties of Render() and stop the sink. We don't | |
| 673 // call MaybeStopSinkAfterFirstPaint() since it may need to mutate |sink_|, | |
| 674 // which can't be done under lock. | |
| 675 if (render_first_frame_and_stop_) { | |
| 676 task_runner_->PostTask( | |
| 677 FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint, | |
| 678 weak_factory_.GetWeakPtr())); | |
| 679 | |
| 680 // MaybeStopSinkAfterFirstPaint isn't going to stop the sink if time is | |
| 681 // currently progressing, so only bail out if necessary. | |
| 682 if (!time_progressing_) { | |
| 683 RestartBackgroundRenderTimer(); | |
| 684 return; | |
| 685 } | |
| 686 } | |
| 687 | |
| 688 // First clear as many expired frames as we can. | |
| 689 algorithm_->RemoveExpiredFrames(tick_clock_->NowTicks()); | |
| 690 is_background_rendering_ = true; | |
| 691 | |
| 692 // See if we've run out of frames and need to fire the ended callback. | |
| 693 MaybeFireEndedCallback(); | |
| 694 if (rendered_end_of_stream_) | |
| 695 return; | |
| 696 | |
| 697 // Start queuing new frames and scheduled this process again otherwise. | |
| 698 AttemptRead_Locked(); | |
| 699 UpdateStatsAndWait_Locked(base::TimeDelta()); | |
| 700 RestartBackgroundRenderTimer(); | |
| 701 } | |
| 702 | |
| 703 bool VideoRendererImpl::HaveReachedBufferingCap() { | |
| 704 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 705 if (use_new_video_renderering_path_) { | |
| 706 // When the display rate is less than the frame rate, the effective frames | |
| 707 // queued may be much smaller than the actual number of frames queued. Here | |
| 708 // we ensure that frames_queued() doesn't get excessive. | |
| 709 return algorithm_->EffectiveFramesQueued() >= | |
| 710 static_cast<size_t>(limits::kMaxVideoFrames) || | |
| 711 algorithm_->frames_queued() >= | |
| 712 static_cast<size_t>(3 * limits::kMaxVideoFrames); | |
| 713 } | |
| 714 | |
| 715 return ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames); | |
| 716 } | |
| 717 | |
| 718 void VideoRendererImpl::StartSink() { | |
| 719 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 720 sink_->Start(this); | |
| 721 RestartBackgroundRenderTimer(); | |
| 722 } | |
| 723 | |
| 724 void VideoRendererImpl::StopSink() { | |
| 725 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 726 sink_->Stop(); | |
| 727 background_rendering_timer_.Stop(); | |
| 728 is_background_rendering_ = false; | |
| 729 } | |
| 730 | |
| 731 size_t VideoRendererImpl::MaybeFireEndedCallback() { | |
| 732 // If there's only one frame in the video or Render() was never called, the | |
| 733 // algorithm will have one frame linger indefinitely. So in cases where the | |
| 734 // frame duration is unknown nad we've recieved EOS, fire it once we get down | |
|
xhwang
2015/04/29 06:51:54
s/nad/and/
DaleCurtis
2015/04/30 03:49:37
Done.
| |
| 735 // to a single frame. | |
| 736 const size_t effective_frames = algorithm_->EffectiveFramesQueued(); | |
| 737 | |
| 738 if ((!effective_frames || | |
| 739 (algorithm_->frames_queued() == 1u && | |
| 740 algorithm_->average_frame_duration() == base::TimeDelta())) && | |
| 741 received_end_of_stream_ && !rendered_end_of_stream_) { | |
| 742 rendered_end_of_stream_ = true; | |
| 743 task_runner_->PostTask(FROM_HERE, ended_cb_); | |
| 744 } | |
| 745 | |
| 746 return effective_frames; | |
| 481 } | 747 } |
| 482 | 748 |
| 483 } // namespace media | 749 } // namespace media |
| OLD | NEW |