| 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 <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/callback.h" | 10 #include "base/callback.h" |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 50 state_(kUninitialized), | 50 state_(kUninitialized), |
| 51 sequence_token_(0), | 51 sequence_token_(0), |
| 52 pending_read_(false), | 52 pending_read_(false), |
| 53 drop_frames_(drop_frames), | 53 drop_frames_(drop_frames), |
| 54 buffering_state_(BUFFERING_HAVE_NOTHING), | 54 buffering_state_(BUFFERING_HAVE_NOTHING), |
| 55 frames_decoded_(0), | 55 frames_decoded_(0), |
| 56 frames_dropped_(0), | 56 frames_dropped_(0), |
| 57 tick_clock_(new base::DefaultTickClock()), | 57 tick_clock_(new base::DefaultTickClock()), |
| 58 was_background_rendering_(false), | 58 was_background_rendering_(false), |
| 59 time_progressing_(false), | 59 time_progressing_(false), |
| 60 render_first_frame_and_stop_(false), | |
| 61 posted_maybe_stop_after_first_paint_(false), | |
| 62 last_video_memory_usage_(0), | 60 last_video_memory_usage_(0), |
| 63 have_renderered_frames_(false), | 61 have_renderered_frames_(false), |
| 64 last_frame_opaque_(false), | 62 last_frame_opaque_(false), |
| 65 weak_factory_(this) { | 63 weak_factory_(this) { |
| 66 if (gpu_factories && | 64 if (gpu_factories && |
| 67 gpu_factories->ShouldUseGpuMemoryBuffersForVideoFrames()) { | 65 gpu_factories->ShouldUseGpuMemoryBuffersForVideoFrames()) { |
| 68 gpu_memory_buffer_pool_.reset(new GpuMemoryBufferVideoFramePool( | 66 gpu_memory_buffer_pool_.reset(new GpuMemoryBufferVideoFramePool( |
| 69 media_task_runner, worker_task_runner, gpu_factories)); | 67 media_task_runner, worker_task_runner, gpu_factories)); |
| 70 } | 68 } |
| 71 } | 69 } |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 RendererClient* client, | 130 RendererClient* client, |
| 133 const TimeSource::WallClockTimeCB& wall_clock_time_cb, | 131 const TimeSource::WallClockTimeCB& wall_clock_time_cb, |
| 134 const PipelineStatusCB& init_cb) { | 132 const PipelineStatusCB& init_cb) { |
| 135 DCHECK(task_runner_->BelongsToCurrentThread()); | 133 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 136 base::AutoLock auto_lock(lock_); | 134 base::AutoLock auto_lock(lock_); |
| 137 DCHECK(stream); | 135 DCHECK(stream); |
| 138 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); | 136 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); |
| 139 DCHECK(!init_cb.is_null()); | 137 DCHECK(!init_cb.is_null()); |
| 140 DCHECK(!wall_clock_time_cb.is_null()); | 138 DCHECK(!wall_clock_time_cb.is_null()); |
| 141 DCHECK_EQ(kUninitialized, state_); | 139 DCHECK_EQ(kUninitialized, state_); |
| 142 DCHECK(!render_first_frame_and_stop_); | |
| 143 DCHECK(!posted_maybe_stop_after_first_paint_); | |
| 144 DCHECK(!was_background_rendering_); | 140 DCHECK(!was_background_rendering_); |
| 145 DCHECK(!time_progressing_); | 141 DCHECK(!time_progressing_); |
| 146 DCHECK(!have_renderered_frames_); | 142 DCHECK(!have_renderered_frames_); |
| 147 | 143 |
| 148 low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE); | 144 low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE); |
| 149 UMA_HISTOGRAM_BOOLEAN("Media.VideoRenderer.LowDelay", low_delay_); | 145 UMA_HISTOGRAM_BOOLEAN("Media.VideoRenderer.LowDelay", low_delay_); |
| 150 if (low_delay_) | 146 if (low_delay_) |
| 151 MEDIA_LOG(DEBUG, media_log_) << "Video rendering in low delay mode."; | 147 MEDIA_LOG(DEBUG, media_log_) << "Video rendering in low delay mode."; |
| 152 | 148 |
| 153 // Always post |init_cb_| because |this| could be destroyed if initialization | 149 // Always post |init_cb_| because |this| could be destroyed if initialization |
| (...skipping 21 matching lines...) Expand all Loading... |
| 175 DCHECK_EQ(state_, kPlaying); | 171 DCHECK_EQ(state_, kPlaying); |
| 176 | 172 |
| 177 size_t frames_dropped = 0; | 173 size_t frames_dropped = 0; |
| 178 scoped_refptr<VideoFrame> result = | 174 scoped_refptr<VideoFrame> result = |
| 179 algorithm_->Render(deadline_min, deadline_max, &frames_dropped); | 175 algorithm_->Render(deadline_min, deadline_max, &frames_dropped); |
| 180 | 176 |
| 181 // Due to how the |algorithm_| holds frames, this should never be null if | 177 // Due to how the |algorithm_| holds frames, this should never be null if |
| 182 // we've had a proper startup sequence. | 178 // we've had a proper startup sequence. |
| 183 DCHECK(result); | 179 DCHECK(result); |
| 184 | 180 |
| 185 // Notify client of size and opacity changes if this is the first frame | |
| 186 // or if those have changed from the last frame. | |
| 187 if (!have_renderered_frames_ || | |
| 188 (last_frame_natural_size_ != result->natural_size())) { | |
| 189 last_frame_natural_size_ = result->natural_size(); | |
| 190 task_runner_->PostTask( | |
| 191 FROM_HERE, | |
| 192 base::Bind(&VideoRendererImpl::OnVideoNaturalSizeChange, | |
| 193 weak_factory_.GetWeakPtr(), last_frame_natural_size_)); | |
| 194 } | |
| 195 if (!have_renderered_frames_ || | |
| 196 (last_frame_opaque_ != IsOpaque(result->format()))) { | |
| 197 last_frame_opaque_ = IsOpaque(result->format()); | |
| 198 task_runner_->PostTask( | |
| 199 FROM_HERE, base::Bind(&VideoRendererImpl::OnVideoOpacityChange, | |
| 200 weak_factory_.GetWeakPtr(), last_frame_opaque_)); | |
| 201 } | |
| 202 | |
| 203 // Declare HAVE_NOTHING if we reach a state where we can't progress playback | 181 // Declare HAVE_NOTHING if we reach a state where we can't progress playback |
| 204 // any further. We don't want to do this if we've already done so, reached | 182 // any further. We don't want to do this if we've already done so, reached |
| 205 // end of stream, or have frames available. We also don't want to do this in | 183 // end of stream, or have frames available. We also don't want to do this in |
| 206 // background rendering mode unless this isn't the first background render | 184 // background rendering mode unless this isn't the first background render |
| 207 // tick and we haven't seen any decoded frames since the last one. | 185 // tick and we haven't seen any decoded frames since the last one. |
| 208 // | 186 MaybeFireEndedCallback_Locked(true); |
| 209 // We use the inverse of |render_first_frame_and_stop_| as a proxy for the | |
| 210 // value of |time_progressing_| here since we can't access it from the | |
| 211 // compositor thread. If we're here (in Render()) the sink must have been | |
| 212 // started -- but if it was started only to render the first frame and stop, | |
| 213 // then |time_progressing_| is likely false. If we're still in Render() when | |
| 214 // |render_first_frame_and_stop_| is false, then |time_progressing_| is true. | |
| 215 // If |time_progressing_| is actually true when |render_first_frame_and_stop_| | |
| 216 // is also true, then the ended callback will be harmlessly delayed until | |
| 217 // MaybeStopSinkAfterFirstPaint() runs and the next Render() call comes in. | |
| 218 MaybeFireEndedCallback_Locked(!render_first_frame_and_stop_); | |
| 219 if (buffering_state_ == BUFFERING_HAVE_ENOUGH && !received_end_of_stream_ && | 187 if (buffering_state_ == BUFFERING_HAVE_ENOUGH && !received_end_of_stream_ && |
| 220 !algorithm_->effective_frames_queued() && | 188 !algorithm_->effective_frames_queued() && |
| 221 (!background_rendering || | 189 (!background_rendering || |
| 222 (!frames_decoded_ && was_background_rendering_))) { | 190 (!frames_decoded_ && was_background_rendering_))) { |
| 223 // Do not set |buffering_state_| here as the lock in FrameReady() may be | 191 // Do not set |buffering_state_| here as the lock in FrameReady() may be |
| 224 // held already and it fire the state changes in the wrong order. | 192 // held already and it fire the state changes in the wrong order. |
| 225 task_runner_->PostTask( | 193 task_runner_->PostTask( |
| 226 FROM_HERE, base::Bind(&VideoRendererImpl::TransitionToHaveNothing, | 194 FROM_HERE, base::Bind(&VideoRendererImpl::TransitionToHaveNothing, |
| 227 weak_factory_.GetWeakPtr())); | 195 weak_factory_.GetWeakPtr())); |
| 228 } | 196 } |
| 229 | 197 |
| 230 // We don't count dropped frames in the background to avoid skewing the count | 198 // We don't count dropped frames in the background to avoid skewing the count |
| 231 // and impacting JavaScript visible metrics used by web developers. | 199 // and impacting JavaScript visible metrics used by web developers. |
| 232 // | 200 // |
| 233 // Just after resuming from background rendering, we also don't count the | 201 // Just after resuming from background rendering, we also don't count the |
| 234 // dropped frames since they are likely just dropped due to being too old. | 202 // dropped frames since they are likely just dropped due to being too old. |
| 235 if (!background_rendering && !was_background_rendering_) | 203 if (!background_rendering && !was_background_rendering_) |
| 236 frames_dropped_ += frames_dropped; | 204 frames_dropped_ += frames_dropped; |
| 237 UpdateStats_Locked(); | 205 UpdateStats_Locked(); |
| 238 was_background_rendering_ = background_rendering; | 206 was_background_rendering_ = background_rendering; |
| 239 | 207 |
| 240 // After painting the first frame, if playback hasn't started, we post a | |
| 241 // delayed task to request that the sink be stopped. The task is delayed to | |
| 242 // give videos with autoplay time to start. | |
| 243 // | |
| 244 // OnTimeStateChanged() will clear this flag if time starts before we get here | |
| 245 // and MaybeStopSinkAfterFirstPaint() will ignore this request if time starts | |
| 246 // before the call executes. | |
| 247 if (render_first_frame_and_stop_ && !posted_maybe_stop_after_first_paint_) { | |
| 248 posted_maybe_stop_after_first_paint_ = true; | |
| 249 task_runner_->PostDelayedTask( | |
| 250 FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint, | |
| 251 weak_factory_.GetWeakPtr()), | |
| 252 base::TimeDelta::FromMilliseconds(250)); | |
| 253 } | |
| 254 | |
| 255 // Always post this task, it will acquire new frames if necessary and since it | 208 // Always post this task, it will acquire new frames if necessary and since it |
| 256 // happens on another thread, even if we don't have room in the queue now, by | 209 // happens on another thread, even if we don't have room in the queue now, by |
| 257 // the time it runs (may be delayed up to 50ms for complex decodes!) we might. | 210 // the time it runs (may be delayed up to 50ms for complex decodes!) we might. |
| 258 task_runner_->PostTask(FROM_HERE, base::Bind(&VideoRendererImpl::AttemptRead, | 211 task_runner_->PostTask( |
| 259 weak_factory_.GetWeakPtr())); | 212 FROM_HERE, |
| 213 base::Bind(&VideoRendererImpl::AttemptReadAndCheckForMetadataChanges, |
| 214 weak_factory_.GetWeakPtr(), result->format(), |
| 215 result->natural_size())); |
| 260 | 216 |
| 261 have_renderered_frames_ = true; | |
| 262 return result; | 217 return result; |
| 263 } | 218 } |
| 264 | 219 |
| 265 void VideoRendererImpl::OnFrameDropped() { | 220 void VideoRendererImpl::OnFrameDropped() { |
| 266 base::AutoLock auto_lock(lock_); | 221 base::AutoLock auto_lock(lock_); |
| 267 algorithm_->OnLastFrameDropped(); | 222 algorithm_->OnLastFrameDropped(); |
| 268 } | 223 } |
| 269 | 224 |
| 270 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) { | 225 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) { |
| 271 DCHECK(task_runner_->BelongsToCurrentThread()); | 226 DCHECK(task_runner_->BelongsToCurrentThread()); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 void VideoRendererImpl::OnBufferingStateChange(BufferingState state) { | 264 void VideoRendererImpl::OnBufferingStateChange(BufferingState state) { |
| 310 DCHECK(task_runner_->BelongsToCurrentThread()); | 265 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 311 client_->OnBufferingStateChange(state); | 266 client_->OnBufferingStateChange(state); |
| 312 } | 267 } |
| 313 | 268 |
| 314 void VideoRendererImpl::OnWaitingForDecryptionKey() { | 269 void VideoRendererImpl::OnWaitingForDecryptionKey() { |
| 315 DCHECK(task_runner_->BelongsToCurrentThread()); | 270 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 316 client_->OnWaitingForDecryptionKey(); | 271 client_->OnWaitingForDecryptionKey(); |
| 317 } | 272 } |
| 318 | 273 |
| 319 void VideoRendererImpl::OnVideoNaturalSizeChange(const gfx::Size& size) { | |
| 320 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 321 client_->OnVideoNaturalSizeChange(size); | |
| 322 } | |
| 323 | |
| 324 void VideoRendererImpl::OnVideoOpacityChange(bool opaque) { | |
| 325 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 326 client_->OnVideoOpacityChange(opaque); | |
| 327 } | |
| 328 | |
| 329 void VideoRendererImpl::SetTickClockForTesting( | 274 void VideoRendererImpl::SetTickClockForTesting( |
| 330 std::unique_ptr<base::TickClock> tick_clock) { | 275 std::unique_ptr<base::TickClock> tick_clock) { |
| 331 tick_clock_.swap(tick_clock); | 276 tick_clock_.swap(tick_clock); |
| 332 } | 277 } |
| 333 | 278 |
| 334 void VideoRendererImpl::SetGpuMemoryBufferVideoForTesting( | 279 void VideoRendererImpl::SetGpuMemoryBufferVideoForTesting( |
| 335 std::unique_ptr<GpuMemoryBufferVideoFramePool> gpu_memory_buffer_pool) { | 280 std::unique_ptr<GpuMemoryBufferVideoFramePool> gpu_memory_buffer_pool) { |
| 336 gpu_memory_buffer_pool_.swap(gpu_memory_buffer_pool); | 281 gpu_memory_buffer_pool_.swap(gpu_memory_buffer_pool); |
| 337 } | 282 } |
| 338 | 283 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 383 DCHECK(frame); | 328 DCHECK(frame); |
| 384 gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( | 329 gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
| 385 frame, base::Bind(&VideoRendererImpl::FrameReady, | 330 frame, base::Bind(&VideoRendererImpl::FrameReady, |
| 386 weak_factory_.GetWeakPtr(), sequence_token_, status)); | 331 weak_factory_.GetWeakPtr(), sequence_token_, status)); |
| 387 } | 332 } |
| 388 | 333 |
| 389 void VideoRendererImpl::FrameReady(uint32_t sequence_token, | 334 void VideoRendererImpl::FrameReady(uint32_t sequence_token, |
| 390 VideoFrameStream::Status status, | 335 VideoFrameStream::Status status, |
| 391 const scoped_refptr<VideoFrame>& frame) { | 336 const scoped_refptr<VideoFrame>& frame) { |
| 392 DCHECK(task_runner_->BelongsToCurrentThread()); | 337 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 393 bool start_sink = false; | 338 base::AutoLock auto_lock(lock_); |
| 394 { | |
| 395 base::AutoLock auto_lock(lock_); | |
| 396 // Stream has been reset and this VideoFrame was decoded before the reset | |
| 397 // but the async copy finished after. | |
| 398 if (sequence_token != sequence_token_) | |
| 399 return; | |
| 400 | 339 |
| 401 DCHECK_NE(state_, kUninitialized); | 340 // Stream has been reset and this VideoFrame was decoded before the reset |
| 402 DCHECK_NE(state_, kFlushed); | 341 // but the async copy finished after. |
| 342 if (sequence_token != sequence_token_) |
| 343 return; |
| 403 | 344 |
| 404 CHECK(pending_read_); | 345 DCHECK_NE(state_, kUninitialized); |
| 405 pending_read_ = false; | 346 DCHECK_NE(state_, kFlushed); |
| 406 | 347 |
| 407 if (status == VideoFrameStream::DECODE_ERROR) { | 348 CHECK(pending_read_); |
| 408 DCHECK(!frame); | 349 pending_read_ = false; |
| 409 task_runner_->PostTask( | |
| 410 FROM_HERE, | |
| 411 base::Bind(&VideoRendererImpl::OnPlaybackError, | |
| 412 weak_factory_.GetWeakPtr(), PIPELINE_ERROR_DECODE)); | |
| 413 return; | |
| 414 } | |
| 415 | 350 |
| 416 // Already-queued VideoFrameStream ReadCB's can fire after various state | 351 if (status == VideoFrameStream::DECODE_ERROR) { |
| 417 // transitions have happened; in that case just drop those frames | 352 DCHECK(!frame); |
| 418 // immediately. | 353 task_runner_->PostTask( |
| 419 if (state_ == kFlushing) | 354 FROM_HERE, |
| 420 return; | 355 base::Bind(&VideoRendererImpl::OnPlaybackError, |
| 421 | 356 weak_factory_.GetWeakPtr(), PIPELINE_ERROR_DECODE)); |
| 422 DCHECK_EQ(state_, kPlaying); | 357 return; |
| 423 | |
| 424 // Can happen when demuxers are preparing for a new Seek(). | |
| 425 if (!frame) { | |
| 426 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED); | |
| 427 return; | |
| 428 } | |
| 429 | |
| 430 if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) { | |
| 431 DCHECK(!received_end_of_stream_); | |
| 432 received_end_of_stream_ = true; | |
| 433 | |
| 434 // See if we can fire EOS immediately instead of waiting for Render(). | |
| 435 MaybeFireEndedCallback_Locked(time_progressing_); | |
| 436 } else if ((low_delay_ || !video_frame_stream_->CanReadWithoutStalling()) && | |
| 437 IsBeforeStartTime(frame->timestamp())) { | |
| 438 // Don't accumulate frames that are earlier than the start time if we | |
| 439 // won't have a chance for a better frame, otherwise we could declare | |
| 440 // HAVE_ENOUGH_DATA and start playback prematurely. | |
| 441 AttemptRead_Locked(); | |
| 442 return; | |
| 443 } else { | |
| 444 // If the sink hasn't been started, we still have time to release less | |
| 445 // than ideal frames prior to startup. We don't use IsBeforeStartTime() | |
| 446 // here since it's based on a duration estimate and we can be exact here. | |
| 447 if (!sink_started_ && frame->timestamp() <= start_timestamp_) | |
| 448 algorithm_->Reset(); | |
| 449 | |
| 450 AddReadyFrame_Locked(frame); | |
| 451 } | |
| 452 | |
| 453 // Attempt to purge bad frames in case of underflow or backgrounding. | |
| 454 RemoveFramesForUnderflowOrBackgroundRendering(); | |
| 455 | |
| 456 // Signal buffering state if we've met our conditions. | |
| 457 if (buffering_state_ == BUFFERING_HAVE_NOTHING && HaveEnoughData_Locked()) { | |
| 458 TransitionToHaveEnough_Locked(); | |
| 459 if (!sink_started_ && !rendered_end_of_stream_) { | |
| 460 start_sink = true; | |
| 461 render_first_frame_and_stop_ = true; | |
| 462 posted_maybe_stop_after_first_paint_ = false; | |
| 463 } | |
| 464 } | |
| 465 | |
| 466 // Always request more decoded video if we have capacity. This serves two | |
| 467 // purposes: | |
| 468 // 1) Prerolling while paused | |
| 469 // 2) Keeps decoding going if video rendering thread starts falling behind | |
| 470 AttemptRead_Locked(); | |
| 471 } | 358 } |
| 472 | 359 |
| 473 // If time is progressing, the sink has already been started; this may be true | 360 // Already-queued VideoFrameStream ReadCB's can fire after various state |
| 474 // if we have previously underflowed, yet weren't stopped because of audio. | 361 // transitions have happened; in that case just drop those frames |
| 475 if (start_sink) { | 362 // immediately. |
| 476 DCHECK(!sink_started_); | 363 if (state_ == kFlushing) |
| 477 StartSink(); | 364 return; |
| 365 |
| 366 DCHECK_EQ(state_, kPlaying); |
| 367 |
| 368 // Can happen when demuxers are preparing for a new Seek(). |
| 369 if (!frame) { |
| 370 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED); |
| 371 return; |
| 478 } | 372 } |
| 373 |
| 374 if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) { |
| 375 DCHECK(!received_end_of_stream_); |
| 376 received_end_of_stream_ = true; |
| 377 |
| 378 // See if we can fire EOS immediately instead of waiting for Render(). |
| 379 MaybeFireEndedCallback_Locked(time_progressing_); |
| 380 } else if ((low_delay_ || !video_frame_stream_->CanReadWithoutStalling()) && |
| 381 IsBeforeStartTime(frame->timestamp())) { |
| 382 // Don't accumulate frames that are earlier than the start time if we |
| 383 // won't have a chance for a better frame, otherwise we could declare |
| 384 // HAVE_ENOUGH_DATA and start playback prematurely. |
| 385 AttemptRead_Locked(); |
| 386 return; |
| 387 } else { |
| 388 // If the sink hasn't been started, we still have time to release less |
| 389 // than ideal frames prior to startup. We don't use IsBeforeStartTime() |
| 390 // here since it's based on a duration estimate and we can be exact here. |
| 391 if (!sink_started_ && frame->timestamp() <= start_timestamp_) |
| 392 algorithm_->Reset(); |
| 393 |
| 394 AddReadyFrame_Locked(frame); |
| 395 } |
| 396 |
| 397 // Attempt to purge bad frames in case of underflow or backgrounding. |
| 398 RemoveFramesForUnderflowOrBackgroundRendering(); |
| 399 |
| 400 // Signal buffering state if we've met our conditions. |
| 401 if (buffering_state_ == BUFFERING_HAVE_NOTHING && HaveEnoughData_Locked()) { |
| 402 TransitionToHaveEnough_Locked(); |
| 403 |
| 404 // Paint the first frame if necessary. |
| 405 if (!rendered_end_of_stream_ && !sink_started_) { |
| 406 DCHECK(algorithm_->frames_queued()); |
| 407 scoped_refptr<VideoFrame> frame = algorithm_->first_frame(); |
| 408 CheckForMetadataChanges(frame->format(), frame->natural_size()); |
| 409 sink_->PaintSingleFrame(frame); |
| 410 } |
| 411 } |
| 412 |
| 413 // Always request more decoded video if we have capacity. This serves two |
| 414 // purposes: |
| 415 // 1) Prerolling while paused |
| 416 // 2) Keeps decoding going if video rendering thread starts falling behind |
| 417 AttemptRead_Locked(); |
| 479 } | 418 } |
| 480 | 419 |
| 481 bool VideoRendererImpl::HaveEnoughData_Locked() { | 420 bool VideoRendererImpl::HaveEnoughData_Locked() { |
| 482 DCHECK_EQ(state_, kPlaying); | 421 DCHECK_EQ(state_, kPlaying); |
| 483 lock_.AssertAcquired(); | 422 lock_.AssertAcquired(); |
| 484 | 423 |
| 485 if (received_end_of_stream_) | 424 if (received_end_of_stream_) |
| 486 return true; | 425 return true; |
| 487 | 426 |
| 488 if (HaveReachedBufferingCap()) | 427 if (HaveReachedBufferingCap()) |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 525 const scoped_refptr<VideoFrame>& frame) { | 464 const scoped_refptr<VideoFrame>& frame) { |
| 526 DCHECK(task_runner_->BelongsToCurrentThread()); | 465 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 527 lock_.AssertAcquired(); | 466 lock_.AssertAcquired(); |
| 528 DCHECK(!frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)); | 467 DCHECK(!frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)); |
| 529 | 468 |
| 530 frames_decoded_++; | 469 frames_decoded_++; |
| 531 | 470 |
| 532 algorithm_->EnqueueFrame(frame); | 471 algorithm_->EnqueueFrame(frame); |
| 533 } | 472 } |
| 534 | 473 |
| 535 void VideoRendererImpl::AttemptRead() { | |
| 536 base::AutoLock auto_lock(lock_); | |
| 537 AttemptRead_Locked(); | |
| 538 } | |
| 539 | |
| 540 void VideoRendererImpl::AttemptRead_Locked() { | 474 void VideoRendererImpl::AttemptRead_Locked() { |
| 541 DCHECK(task_runner_->BelongsToCurrentThread()); | 475 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 542 lock_.AssertAcquired(); | 476 lock_.AssertAcquired(); |
| 543 | 477 |
| 544 if (pending_read_ || received_end_of_stream_) | 478 if (pending_read_ || received_end_of_stream_) |
| 545 return; | 479 return; |
| 546 | 480 |
| 547 if (HaveReachedBufferingCap()) | 481 if (HaveReachedBufferingCap()) |
| 548 return; | 482 return; |
| 549 | 483 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 597 | 531 |
| 598 task_runner_->PostTask(FROM_HERE, | 532 task_runner_->PostTask(FROM_HERE, |
| 599 base::Bind(&VideoRendererImpl::OnStatisticsUpdate, | 533 base::Bind(&VideoRendererImpl::OnStatisticsUpdate, |
| 600 weak_factory_.GetWeakPtr(), statistics)); | 534 weak_factory_.GetWeakPtr(), statistics)); |
| 601 frames_decoded_ = 0; | 535 frames_decoded_ = 0; |
| 602 frames_dropped_ = 0; | 536 frames_dropped_ = 0; |
| 603 last_video_memory_usage_ = memory_usage; | 537 last_video_memory_usage_ = memory_usage; |
| 604 } | 538 } |
| 605 } | 539 } |
| 606 | 540 |
| 607 void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() { | |
| 608 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 609 | |
| 610 if (!time_progressing_ && sink_started_) | |
| 611 StopSink(); | |
| 612 | |
| 613 base::AutoLock auto_lock(lock_); | |
| 614 render_first_frame_and_stop_ = false; | |
| 615 } | |
| 616 | |
| 617 bool VideoRendererImpl::HaveReachedBufferingCap() { | 541 bool VideoRendererImpl::HaveReachedBufferingCap() { |
| 618 DCHECK(task_runner_->BelongsToCurrentThread()); | 542 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 619 const size_t kMaxVideoFrames = limits::kMaxVideoFrames; | 543 const size_t kMaxVideoFrames = limits::kMaxVideoFrames; |
| 620 | 544 |
| 621 // When the display rate is less than the frame rate, the effective frames | 545 // When the display rate is less than the frame rate, the effective frames |
| 622 // queued may be much smaller than the actual number of frames queued. Here | 546 // queued may be much smaller than the actual number of frames queued. Here |
| 623 // we ensure that frames_queued() doesn't get excessive. | 547 // we ensure that frames_queued() doesn't get excessive. |
| 624 return algorithm_->effective_frames_queued() >= kMaxVideoFrames || | 548 return algorithm_->effective_frames_queued() >= kMaxVideoFrames || |
| 625 algorithm_->frames_queued() >= 3 * kMaxVideoFrames; | 549 algorithm_->frames_queued() >= 3 * kMaxVideoFrames; |
| 626 } | 550 } |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 724 // If we've paused for underflow, and still have no effective frames, clear | 648 // If we've paused for underflow, and still have no effective frames, clear |
| 725 // the entire queue. Note: this may cause slight inaccuracies in the number | 649 // the entire queue. Note: this may cause slight inaccuracies in the number |
| 726 // of dropped frames since the frame may have been rendered before. | 650 // of dropped frames since the frame may have been rendered before. |
| 727 if (!sink_started_ && !algorithm_->effective_frames_queued()) { | 651 if (!sink_started_ && !algorithm_->effective_frames_queued()) { |
| 728 frames_dropped_ += algorithm_->frames_queued(); | 652 frames_dropped_ += algorithm_->frames_queued(); |
| 729 algorithm_->Reset( | 653 algorithm_->Reset( |
| 730 VideoRendererAlgorithm::ResetFlag::kPreserveNextFrameEstimates); | 654 VideoRendererAlgorithm::ResetFlag::kPreserveNextFrameEstimates); |
| 731 } | 655 } |
| 732 } | 656 } |
| 733 | 657 |
| 658 void VideoRendererImpl::CheckForMetadataChanges(VideoPixelFormat pixel_format, |
| 659 const gfx::Size& natural_size) { |
| 660 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 661 |
| 662 // Notify client of size and opacity changes if this is the first frame |
| 663 // or if those have changed from the last frame. |
| 664 if (!have_renderered_frames_ || last_frame_natural_size_ != natural_size) { |
| 665 last_frame_natural_size_ = natural_size; |
| 666 client_->OnVideoNaturalSizeChange(last_frame_natural_size_); |
| 667 } |
| 668 |
| 669 const bool is_opaque = IsOpaque(pixel_format); |
| 670 if (!have_renderered_frames_ || last_frame_opaque_ != is_opaque) { |
| 671 last_frame_opaque_ = is_opaque; |
| 672 client_->OnVideoOpacityChange(last_frame_opaque_); |
| 673 } |
| 674 |
| 675 have_renderered_frames_ = true; |
| 676 } |
| 677 |
| 678 void VideoRendererImpl::AttemptReadAndCheckForMetadataChanges( |
| 679 VideoPixelFormat pixel_format, |
| 680 const gfx::Size& natural_size) { |
| 681 base::AutoLock auto_lock(lock_); |
| 682 CheckForMetadataChanges(pixel_format, natural_size); |
| 683 AttemptRead_Locked(); |
| 684 } |
| 685 |
| 734 } // namespace media | 686 } // namespace media |
| OLD | NEW |