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