Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(731)

Side by Side Diff: media/filters/video_renderer_impl.cc

Issue 237353007: Refactor VideoRendererImpl to use VideoFrameScheduler. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase over split out CLs Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/filters/video_renderer_impl.h ('k') | media/filters/video_renderer_impl_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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/filters/video_renderer_impl.h" 5 #include "media/filters/video_renderer_impl.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h" 8 #include "base/callback_helpers.h"
10 #include "base/debug/trace_event.h"
11 #include "base/location.h"
12 #include "base/single_thread_task_runner.h" 9 #include "base/single_thread_task_runner.h"
13 #include "base/threading/platform_thread.h" 10 #include "base/time/default_tick_clock.h"
14 #include "media/base/buffers.h"
15 #include "media/base/limits.h" 11 #include "media/base/limits.h"
16 #include "media/base/pipeline.h"
17 #include "media/base/video_frame.h" 12 #include "media/base/video_frame.h"
18 13
19 namespace media { 14 namespace media {
20 15
21 VideoRendererImpl::VideoRendererImpl( 16 VideoRendererImpl::VideoRendererImpl(
22 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 17 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
18 scoped_ptr<VideoFrameScheduler> scheduler,
23 ScopedVector<VideoDecoder> decoders, 19 ScopedVector<VideoDecoder> decoders,
24 const SetDecryptorReadyCB& set_decryptor_ready_cb, 20 const SetDecryptorReadyCB& set_decryptor_ready_cb)
25 const PaintCB& paint_cb,
26 bool drop_frames)
27 : task_runner_(task_runner), 21 : task_runner_(task_runner),
22 scheduler_(scheduler.Pass()),
23 tick_clock_(new base::DefaultTickClock()),
28 video_frame_stream_(task_runner, decoders.Pass(), set_decryptor_ready_cb), 24 video_frame_stream_(task_runner, decoders.Pass(), set_decryptor_ready_cb),
25 next_scheduled_frame_id_(0),
29 received_end_of_stream_(false), 26 received_end_of_stream_(false),
30 frame_available_(&lock_),
31 state_(kUninitialized), 27 state_(kUninitialized),
32 thread_(),
33 pending_read_(false), 28 pending_read_(false),
34 drop_frames_(drop_frames),
35 playback_rate_(0), 29 playback_rate_(0),
36 paint_cb_(paint_cb),
37 last_timestamp_(kNoTimestamp()),
38 frames_decoded_(0), 30 frames_decoded_(0),
39 frames_dropped_(0), 31 frames_dropped_(0),
40 weak_factory_(this) { 32 weak_factory_(this) {
41 DCHECK(!paint_cb_.is_null());
42 } 33 }
43 34
44 VideoRendererImpl::~VideoRendererImpl() { 35 VideoRendererImpl::~VideoRendererImpl() {
45 base::AutoLock auto_lock(lock_);
46 CHECK(state_ == kStopped || state_ == kUninitialized) << state_; 36 CHECK(state_ == kStopped || state_ == kUninitialized) << state_;
47 CHECK(thread_.is_null());
48 } 37 }
49 38
50 void VideoRendererImpl::Play(const base::Closure& callback) { 39 void VideoRendererImpl::Play(const base::Closure& callback) {
51 DCHECK(task_runner_->BelongsToCurrentThread()); 40 DCHECK(task_runner_->BelongsToCurrentThread());
52 base::AutoLock auto_lock(lock_);
53 DCHECK_EQ(kPrerolled, state_); 41 DCHECK_EQ(kPrerolled, state_);
54 state_ = kPlaying; 42 state_ = kPlaying;
43
44 ScheduleAllFrames();
45 AttemptRead();
46
47 // Changing from non-playing to playing state can trigger ended.
48 if (ShouldTransitionToEnded())
49 TransitionToEnded();
50
55 callback.Run(); 51 callback.Run();
56 } 52 }
57 53
58 void VideoRendererImpl::Pause(const base::Closure& callback) { 54 void VideoRendererImpl::Pause(const base::Closure& callback) {
59 DCHECK(task_runner_->BelongsToCurrentThread()); 55 DCHECK(task_runner_->BelongsToCurrentThread());
60 base::AutoLock auto_lock(lock_);
61 DCHECK(state_ != kUninitialized || state_ == kError); 56 DCHECK(state_ != kUninitialized || state_ == kError);
57
62 state_ = kPaused; 58 state_ = kPaused;
59 ResetScheduler();
60
63 callback.Run(); 61 callback.Run();
64 } 62 }
65 63
66 void VideoRendererImpl::Flush(const base::Closure& callback) { 64 void VideoRendererImpl::Flush(const base::Closure& callback) {
67 DCHECK(task_runner_->BelongsToCurrentThread()); 65 DCHECK(task_runner_->BelongsToCurrentThread());
68 base::AutoLock auto_lock(lock_);
69 DCHECK_EQ(state_, kPaused); 66 DCHECK_EQ(state_, kPaused);
67 DCHECK_EQ(scheduled_frames_.size(), 0u);
70 flush_cb_ = callback; 68 flush_cb_ = callback;
71 state_ = kFlushing; 69 state_ = kFlushing;
72 70
73 // This is necessary if the |video_frame_stream_| has already seen an end of 71 unscheduled_frames_.clear();
74 // stream and needs to drain it before flushing it. 72
75 ready_frames_.clear();
76 received_end_of_stream_ = false; 73 received_end_of_stream_ = false;
77 video_frame_stream_.Reset( 74 video_frame_stream_.Reset(
78 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone, 75 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone,
79 weak_factory_.GetWeakPtr())); 76 weak_factory_.GetWeakPtr()));
80 } 77 }
81 78
82 void VideoRendererImpl::Stop(const base::Closure& callback) { 79 void VideoRendererImpl::Stop(const base::Closure& callback) {
83 DCHECK(task_runner_->BelongsToCurrentThread()); 80 DCHECK(task_runner_->BelongsToCurrentThread());
84 base::AutoLock auto_lock(lock_);
85 if (state_ == kUninitialized || state_ == kStopped) { 81 if (state_ == kUninitialized || state_ == kStopped) {
86 callback.Run(); 82 callback.Run();
87 return; 83 return;
88 } 84 }
89 85
90 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing 86 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing
91 // task-running guards that check |state_| with DCHECK(). 87 // task-running guards that check |state_| with DCHECK().
92 88
93 state_ = kStopped; 89 state_ = kStopped;
94 90
95 statistics_cb_.Reset(); 91 statistics_cb_.Reset();
96 max_time_cb_.Reset(); 92 max_time_cb_.Reset();
97 DoStopOrError_Locked();
98 93
99 // Clean up our thread if present. 94 unscheduled_frames_.clear();
100 base::PlatformThreadHandle thread_to_join = base::PlatformThreadHandle(); 95 scheduled_frames_.clear();
101 if (!thread_.is_null()) { 96 scheduler_->Reset();
102 // Signal the thread since it's possible to get stopped with the video
103 // thread waiting for a read to complete.
104 frame_available_.Signal();
105 std::swap(thread_, thread_to_join);
106 }
107
108 if (!thread_to_join.is_null()) {
109 base::AutoUnlock auto_unlock(lock_);
110 base::PlatformThread::Join(thread_to_join);
111 }
112 97
113 video_frame_stream_.Stop(callback); 98 video_frame_stream_.Stop(callback);
114 } 99 }
115 100
116 void VideoRendererImpl::SetPlaybackRate(float playback_rate) { 101 void VideoRendererImpl::SetPlaybackRate(float playback_rate) {
117 DCHECK(task_runner_->BelongsToCurrentThread()); 102 DCHECK(task_runner_->BelongsToCurrentThread());
118 base::AutoLock auto_lock(lock_); 103 if (playback_rate_ == playback_rate)
104 return;
105
119 playback_rate_ = playback_rate; 106 playback_rate_ = playback_rate;
107 ResetScheduler();
108
109 if (state_ == kPlaying)
110 ScheduleAllFrames();
120 } 111 }
121 112
122 void VideoRendererImpl::Preroll(base::TimeDelta time, 113 void VideoRendererImpl::Preroll(base::TimeDelta time,
123 const PipelineStatusCB& cb) { 114 const PipelineStatusCB& cb) {
124 DCHECK(task_runner_->BelongsToCurrentThread()); 115 DCHECK(task_runner_->BelongsToCurrentThread());
125 base::AutoLock auto_lock(lock_);
126 DCHECK(!cb.is_null()); 116 DCHECK(!cb.is_null());
127 DCHECK(preroll_cb_.is_null()); 117 DCHECK(preroll_cb_.is_null());
128 DCHECK(state_ == kFlushed || state_== kPaused) << "state_ " << state_; 118 DCHECK(state_ == kFlushed || state_ == kPaused) << "state_ " << state_;
129 119
130 if (state_ == kFlushed) { 120 if (state_ == kFlushed) {
131 DCHECK(time != kNoTimestamp()); 121 DCHECK(time != kNoTimestamp());
132 DCHECK(!pending_read_); 122 DCHECK(!pending_read_);
133 DCHECK(ready_frames_.empty()); 123 DCHECK(unscheduled_frames_.empty());
124 DCHECK(scheduled_frames_.empty());
134 } else { 125 } else {
126 // Prerolling without a timestamp is used for rebuffering.
135 DCHECK(time == kNoTimestamp()); 127 DCHECK(time == kNoTimestamp());
136 } 128 }
137 129
138 state_ = kPrerolling; 130 state_ = kPrerolling;
139 preroll_cb_ = cb; 131 preroll_cb_ = cb;
140 preroll_timestamp_ = time; 132 preroll_timestamp_ = time;
141 133
142 if (ShouldTransitionToPrerolled_Locked()) { 134 if (ShouldTransitionToPrerolled()) {
143 TransitionToPrerolled_Locked(); 135 TransitionToPrerolled();
144 return; 136 return;
145 } 137 }
146 138
147 AttemptRead_Locked(); 139 AttemptRead();
148 } 140 }
149 141
150 void VideoRendererImpl::Initialize(DemuxerStream* stream, 142 void VideoRendererImpl::Initialize(DemuxerStream* stream,
151 const PipelineStatusCB& init_cb, 143 const PipelineStatusCB& init_cb,
152 const StatisticsCB& statistics_cb, 144 const StatisticsCB& statistics_cb,
153 const TimeCB& max_time_cb, 145 const TimeCB& max_time_cb,
154 const base::Closure& ended_cb, 146 const base::Closure& ended_cb,
155 const PipelineStatusCB& error_cb, 147 const PipelineStatusCB& error_cb,
156 const TimeDeltaCB& get_time_cb, 148 const TimeDeltaCB& get_time_cb,
157 const TimeDeltaCB& get_duration_cb) { 149 const TimeDeltaCB& get_duration_cb) {
158 DCHECK(task_runner_->BelongsToCurrentThread()); 150 DCHECK(task_runner_->BelongsToCurrentThread());
159 base::AutoLock auto_lock(lock_);
160 DCHECK(stream); 151 DCHECK(stream);
161 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); 152 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
162 DCHECK(!init_cb.is_null()); 153 DCHECK(!init_cb.is_null());
163 DCHECK(!statistics_cb.is_null()); 154 DCHECK(!statistics_cb.is_null());
164 DCHECK(!max_time_cb.is_null()); 155 DCHECK(!max_time_cb.is_null());
165 DCHECK(!ended_cb.is_null()); 156 DCHECK(!ended_cb.is_null());
166 DCHECK(!get_time_cb.is_null()); 157 DCHECK(!get_time_cb.is_null());
167 DCHECK(!get_duration_cb.is_null()); 158 DCHECK(!get_duration_cb.is_null());
168 DCHECK_EQ(kUninitialized, state_); 159 DCHECK_EQ(kUninitialized, state_);
169 160
170 init_cb_ = init_cb; 161 init_cb_ = init_cb;
171 statistics_cb_ = statistics_cb; 162 statistics_cb_ = statistics_cb;
172 max_time_cb_ = max_time_cb; 163 max_time_cb_ = max_time_cb;
173 ended_cb_ = ended_cb; 164 ended_cb_ = ended_cb;
174 error_cb_ = error_cb; 165 error_cb_ = error_cb;
175 get_time_cb_ = get_time_cb; 166 get_time_cb_ = get_time_cb;
176 get_duration_cb_ = get_duration_cb; 167 get_duration_cb_ = get_duration_cb;
177 state_ = kInitializing; 168 state_ = kInitializing;
178 169
179 video_frame_stream_.Initialize( 170 video_frame_stream_.Initialize(
180 stream, 171 stream,
181 statistics_cb, 172 statistics_cb,
182 base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized, 173 base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized,
183 weak_factory_.GetWeakPtr())); 174 weak_factory_.GetWeakPtr()));
184 } 175 }
185 176
186 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) { 177 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) {
187 DCHECK(task_runner_->BelongsToCurrentThread()); 178 DCHECK(task_runner_->BelongsToCurrentThread());
188 base::AutoLock auto_lock(lock_);
189 179
190 if (state_ == kStopped) 180 if (state_ == kStopped)
191 return; 181 return;
192 182
193 DCHECK_EQ(state_, kInitializing); 183 DCHECK_EQ(state_, kInitializing);
194 184
195 if (!success) { 185 if (!success) {
196 state_ = kUninitialized; 186 state_ = kUninitialized;
197 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); 187 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
198 return; 188 return;
199 } 189 }
200 190
201 // We're all good! Consider ourselves flushed. (ThreadMain() should never 191 // Consider ourselves flushed as no frames have been received yet.
202 // see us in the kUninitialized state).
203 // Since we had an initial Preroll(), we consider ourself flushed, because we
204 // have not populated any buffers yet.
205 state_ = kFlushed; 192 state_ = kFlushed;
206 193
207 // Create our video thread.
208 if (!base::PlatformThread::Create(0, this, &thread_)) {
209 NOTREACHED() << "Video thread creation failed";
210 state_ = kError;
211 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
212 return;
213 }
214
215 #if defined(OS_WIN)
216 // Bump up our priority so our sleeping is more accurate.
217 // TODO(scherkus): find out if this is necessary, but it seems to help.
218 ::SetThreadPriority(thread_.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL);
219 #endif // defined(OS_WIN)
220 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); 194 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
221 } 195 }
222 196
223 // PlatformThread::Delegate implementation.
224 void VideoRendererImpl::ThreadMain() {
225 base::PlatformThread::SetName("CrVideoRenderer");
226
227 // The number of milliseconds to idle when we do not have anything to do.
228 // Nothing special about the value, other than we're being more OS-friendly
229 // than sleeping for 1 millisecond.
230 //
231 // TODO(scherkus): switch to pure event-driven frame timing instead of this
232 // kIdleTimeDelta business http://crbug.com/106874
233 const base::TimeDelta kIdleTimeDelta =
234 base::TimeDelta::FromMilliseconds(10);
235
236 for (;;) {
237 base::AutoLock auto_lock(lock_);
238
239 // Thread exit condition.
240 if (state_ == kStopped)
241 return;
242
243 // Remain idle as long as we're not playing.
244 if (state_ != kPlaying || playback_rate_ == 0) {
245 UpdateStatsAndWait_Locked(kIdleTimeDelta);
246 continue;
247 }
248
249 // Remain idle until we have the next frame ready for rendering.
250 if (ready_frames_.empty()) {
251 if (received_end_of_stream_) {
252 state_ = kEnded;
253 ended_cb_.Run();
254
255 // No need to sleep here as we idle when |state_ != kPlaying|.
256 continue;
257 }
258
259 UpdateStatsAndWait_Locked(kIdleTimeDelta);
260 continue;
261 }
262
263 base::TimeDelta remaining_time =
264 CalculateSleepDuration(ready_frames_.front(), playback_rate_);
265
266 // Sleep up to a maximum of our idle time until we're within the time to
267 // render the next frame.
268 if (remaining_time.InMicroseconds() > 0) {
269 remaining_time = std::min(remaining_time, kIdleTimeDelta);
270 UpdateStatsAndWait_Locked(remaining_time);
271 continue;
272 }
273
274 // Deadline is defined as the midpoint between this frame and the next
275 // frame, using the delta between this frame and the previous frame as the
276 // assumption for frame duration.
277 //
278 // TODO(scherkus): An improvement over midpoint might be selecting the
279 // minimum and/or maximum between the midpoint and some constants. As a
280 // thought experiment, consider what would be better than the midpoint
281 // for both the 1fps case and 120fps case.
282 //
283 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
284 // the accuracy of our frame timing code. http://crbug.com/149829
285 if (drop_frames_ && last_timestamp_ != kNoTimestamp()) {
286 base::TimeDelta now = get_time_cb_.Run();
287 base::TimeDelta deadline = ready_frames_.front()->timestamp() +
288 (ready_frames_.front()->timestamp() - last_timestamp_) / 2;
289
290 if (now > deadline) {
291 DropNextReadyFrame_Locked();
292 continue;
293 }
294 }
295
296 // Congratulations! You've made it past the video frame timing gauntlet.
297 //
298 // At this point enough time has passed that the next frame that ready for
299 // rendering.
300 PaintNextReadyFrame_Locked();
301 }
302 }
303
304 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
305 lock_.AssertAcquired();
306
307 scoped_refptr<VideoFrame> next_frame = ready_frames_.front();
308 ready_frames_.pop_front();
309 frames_decoded_++;
310
311 last_timestamp_ = next_frame->timestamp();
312
313 paint_cb_.Run(next_frame);
314
315 task_runner_->PostTask(
316 FROM_HERE,
317 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
318 }
319
320 void VideoRendererImpl::DropNextReadyFrame_Locked() {
321 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped");
322
323 lock_.AssertAcquired();
324
325 last_timestamp_ = ready_frames_.front()->timestamp();
326 ready_frames_.pop_front();
327 frames_decoded_++;
328 frames_dropped_++;
329
330 task_runner_->PostTask(
331 FROM_HERE,
332 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
333 }
334
335 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, 197 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
336 const scoped_refptr<VideoFrame>& frame) { 198 const scoped_refptr<VideoFrame>& frame) {
337 base::AutoLock auto_lock(lock_);
338 DCHECK_NE(state_, kUninitialized); 199 DCHECK_NE(state_, kUninitialized);
339 DCHECK_NE(state_, kFlushed); 200 DCHECK_NE(state_, kFlushed);
340 201
341 CHECK(pending_read_); 202 CHECK(pending_read_);
342 pending_read_ = false; 203 pending_read_ = false;
343 204
344 if (status == VideoFrameStream::DECODE_ERROR || 205 if (status == VideoFrameStream::DECODE_ERROR ||
345 status == VideoFrameStream::DECRYPT_ERROR) { 206 status == VideoFrameStream::DECRYPT_ERROR) {
346 DCHECK(!frame.get()); 207 DCHECK(!frame.get());
347 PipelineStatus error = PIPELINE_ERROR_DECODE; 208 PipelineStatus error = PIPELINE_ERROR_DECODE;
(...skipping 12 matching lines...) Expand all
360 // Already-queued VideoFrameStream ReadCB's can fire after various state 221 // Already-queued VideoFrameStream ReadCB's can fire after various state
361 // transitions have happened; in that case just drop those frames immediately. 222 // transitions have happened; in that case just drop those frames immediately.
362 if (state_ == kStopped || state_ == kError || state_ == kFlushing) 223 if (state_ == kStopped || state_ == kError || state_ == kFlushing)
363 return; 224 return;
364 225
365 if (!frame.get()) { 226 if (!frame.get()) {
366 // Abort preroll early for a NULL frame because we won't get more frames. 227 // Abort preroll early for a NULL frame because we won't get more frames.
367 // A new preroll will be requested after this one completes so there is no 228 // A new preroll will be requested after this one completes so there is no
368 // point trying to collect more frames. 229 // point trying to collect more frames.
369 if (state_ == kPrerolling) 230 if (state_ == kPrerolling)
370 TransitionToPrerolled_Locked(); 231 TransitionToPrerolled();
371 232
372 return; 233 return;
373 } 234 }
374 235
375 if (frame->end_of_stream()) { 236 if (frame->end_of_stream()) {
376 DCHECK(!received_end_of_stream_); 237 DCHECK(!received_end_of_stream_);
377 received_end_of_stream_ = true; 238 received_end_of_stream_ = true;
378 max_time_cb_.Run(get_duration_cb_.Run()); 239 max_time_cb_.Run(get_duration_cb_.Run());
379 240
380 if (state_ == kPrerolling) 241 if (state_ == kPrerolling)
381 TransitionToPrerolled_Locked(); 242 TransitionToPrerolled();
243
244 if (ShouldTransitionToEnded())
245 TransitionToEnded();
382 246
383 return; 247 return;
384 } 248 }
385 249
386 // Maintain the latest frame decoded so the correct frame is displayed after 250 // Maintain the latest frame decoded so the correct frame is displayed after
387 // prerolling has completed. 251 // prerolling has completed.
388 if (state_ == kPrerolling && preroll_timestamp_ != kNoTimestamp() && 252 if (state_ == kPrerolling && preroll_timestamp_ != kNoTimestamp() &&
389 frame->timestamp() <= preroll_timestamp_) { 253 frame->timestamp() <= preroll_timestamp_) {
390 ready_frames_.clear(); 254 unscheduled_frames_.clear();
391 } 255 }
392 256
393 AddReadyFrame_Locked(frame);
394
395 if (ShouldTransitionToPrerolled_Locked())
396 TransitionToPrerolled_Locked();
397
398 // Always request more decoded video if we have capacity. This serves two
399 // purposes:
400 // 1) Prerolling while paused
401 // 2) Keeps decoding going if video rendering thread starts falling behind
402 AttemptRead_Locked();
403 }
404
405 bool VideoRendererImpl::ShouldTransitionToPrerolled_Locked() {
406 return state_ == kPrerolling &&
407 (!video_frame_stream_.CanReadWithoutStalling() ||
408 ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames));
409 }
410
411 void VideoRendererImpl::AddReadyFrame_Locked(
412 const scoped_refptr<VideoFrame>& frame) {
413 lock_.AssertAcquired();
414 DCHECK(!frame->end_of_stream());
415
416 // Adjust the incoming frame if its rendering stop time is past the duration 257 // Adjust the incoming frame if its rendering stop time is past the duration
417 // of the video itself. This is typically the last frame of the video and 258 // of the video itself. This is typically the last frame of the video and
418 // occurs if the container specifies a duration that isn't a multiple of the 259 // occurs if the container specifies a duration that isn't a multiple of the
419 // frame rate. Another way for this to happen is for the container to state 260 // frame rate. Another way for this to happen is for the container to state
420 // a smaller duration than the largest packet timestamp. 261 // a smaller duration than the largest packet timestamp.
421 base::TimeDelta duration = get_duration_cb_.Run(); 262 base::TimeDelta duration = get_duration_cb_.Run();
422 if (frame->timestamp() > duration) { 263 if (frame->timestamp() > duration) {
423 frame->set_timestamp(duration); 264 frame->set_timestamp(duration);
424 } 265 }
425 266
426 ready_frames_.push_back(frame); 267 unscheduled_frames_.push_back(frame);
427 DCHECK_LE(ready_frames_.size(), 268 DCHECK_LE(video_frame_count(), limits::kMaxVideoFrames);
428 static_cast<size_t>(limits::kMaxVideoFrames));
429 269
430 max_time_cb_.Run(frame->timestamp()); 270 max_time_cb_.Run(frame->timestamp());
431 271
432 // Avoid needlessly waking up |thread_| unless playing. 272 if (ShouldTransitionToPrerolled())
273 TransitionToPrerolled();
274
433 if (state_ == kPlaying) 275 if (state_ == kPlaying)
434 frame_available_.Signal(); 276 ScheduleAllFrames();
277
278 if (ShouldTransitionToEnded())
279 TransitionToEnded();
280
281 // Always request more decoded video if we have capacity. This serves two
282 // purposes:
283 // 1) Prerolling while paused
284 // 2) Keeps decoding going if video rendering thread starts falling behind
285 AttemptRead();
286 }
287
288 bool VideoRendererImpl::ShouldTransitionToPrerolled() {
289 return state_ == kPrerolling &&
290 (!video_frame_stream_.CanReadWithoutStalling() ||
291 video_frame_count() >= limits::kMaxVideoFrames);
292 }
293
294 bool VideoRendererImpl::ShouldTransitionToEnded() {
295 return state_ == kPlaying && unscheduled_frames_.empty() &&
296 scheduled_frames_.empty() && received_end_of_stream_;
297 }
298
299 void VideoRendererImpl::TransitionToEnded() {
300 DCHECK(ShouldTransitionToEnded());
301 state_ = kEnded;
302 ended_cb_.Run();
435 } 303 }
436 304
437 void VideoRendererImpl::AttemptRead() { 305 void VideoRendererImpl::AttemptRead() {
438 base::AutoLock auto_lock(lock_);
439 AttemptRead_Locked();
440 }
441
442 void VideoRendererImpl::AttemptRead_Locked() {
443 DCHECK(task_runner_->BelongsToCurrentThread()); 306 DCHECK(task_runner_->BelongsToCurrentThread());
444 lock_.AssertAcquired();
445 307
446 if (pending_read_ || received_end_of_stream_ || 308 if (pending_read_ || received_end_of_stream_ ||
447 ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) { 309 video_frame_count() == limits::kMaxVideoFrames) {
448 return; 310 return;
449 } 311 }
450 312
451 switch (state_) { 313 switch (state_) {
452 case kPaused: 314 case kPaused:
453 case kPrerolling: 315 case kPrerolling:
454 case kPlaying: 316 case kPlaying:
455 pending_read_ = true; 317 pending_read_ = true;
456 video_frame_stream_.Read(base::Bind(&VideoRendererImpl::FrameReady, 318 video_frame_stream_.Read(base::Bind(&VideoRendererImpl::FrameReady,
457 weak_factory_.GetWeakPtr())); 319 weak_factory_.GetWeakPtr()));
458 return; 320 return;
459 321
460 case kUninitialized: 322 case kUninitialized:
461 case kInitializing: 323 case kInitializing:
462 case kPrerolled: 324 case kPrerolled:
463 case kFlushing: 325 case kFlushing:
464 case kFlushed: 326 case kFlushed:
465 case kEnded: 327 case kEnded:
466 case kStopped: 328 case kStopped:
467 case kError: 329 case kError:
468 return; 330 return;
469 } 331 }
470 } 332 }
471 333
472 void VideoRendererImpl::OnVideoFrameStreamResetDone() { 334 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
473 base::AutoLock auto_lock(lock_);
474 if (state_ == kStopped) 335 if (state_ == kStopped)
475 return; 336 return;
476 337
477 DCHECK_EQ(kFlushing, state_); 338 DCHECK_EQ(kFlushing, state_);
478 DCHECK(!pending_read_); 339 DCHECK(!pending_read_);
479 DCHECK(ready_frames_.empty()); 340 DCHECK(unscheduled_frames_.empty());
341 DCHECK(scheduled_frames_.empty());
480 DCHECK(!received_end_of_stream_); 342 DCHECK(!received_end_of_stream_);
481 343
482 state_ = kFlushed; 344 state_ = kFlushed;
483 last_timestamp_ = kNoTimestamp();
484 base::ResetAndReturn(&flush_cb_).Run(); 345 base::ResetAndReturn(&flush_cb_).Run();
485 } 346 }
486 347
487 base::TimeDelta VideoRendererImpl::CalculateSleepDuration( 348 void VideoRendererImpl::TransitionToPrerolled() {
488 const scoped_refptr<VideoFrame>& next_frame,
489 float playback_rate) {
490 // Determine the current and next presentation timestamps.
491 base::TimeDelta now = get_time_cb_.Run();
492 base::TimeDelta next_pts = next_frame->timestamp();
493
494 // Scale our sleep based on the playback rate.
495 base::TimeDelta sleep = next_pts - now;
496 return base::TimeDelta::FromMicroseconds(
497 static_cast<int64>(sleep.InMicroseconds() / playback_rate));
498 }
499
500 void VideoRendererImpl::DoStopOrError_Locked() {
501 lock_.AssertAcquired();
502 last_timestamp_ = kNoTimestamp();
503 ready_frames_.clear();
504 }
505
506 void VideoRendererImpl::TransitionToPrerolled_Locked() {
507 lock_.AssertAcquired();
508 DCHECK_EQ(state_, kPrerolling); 349 DCHECK_EQ(state_, kPrerolling);
509 350
510 state_ = kPrerolled; 351 state_ = kPrerolled;
511 352
512 // Because we might remain in the prerolled state for an undetermined amount 353 // Because we might remain in the prerolled state for an undetermined amount
513 // of time (e.g., we seeked while paused), we'll paint the first prerolled 354 // of time (e.g., we seeked while paused), we'll paint the first prerolled
514 // frame. 355 // frame.
515 if (!ready_frames_.empty()) 356 if (!unscheduled_frames_.empty()) {
516 PaintNextReadyFrame_Locked(); 357 ScheduleFirstFrameForImmediateDisplay();
358 AttemptRead();
359 }
517 360
518 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); 361 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK);
519 } 362 }
520 363
521 void VideoRendererImpl::UpdateStatsAndWait_Locked( 364 void VideoRendererImpl::ResetScheduler() {
522 base::TimeDelta wait_duration) { 365 for (VideoFrameIDMap::iterator iter = scheduled_frames_.begin();
523 lock_.AssertAcquired(); 366 iter != scheduled_frames_.end();
524 DCHECK_GE(frames_decoded_, 0); 367 ++iter) {
525 DCHECK_LE(frames_dropped_, frames_decoded_); 368 unscheduled_frames_.push_back(iter->second);
369 }
370 scheduled_frames_.clear();
371 scheduler_->Reset();
372 }
526 373
527 if (frames_decoded_) { 374 void VideoRendererImpl::ScheduleAllFrames() {
528 PipelineStatistics statistics; 375 DCHECK_EQ(state_, kPlaying);
529 statistics.video_frames_decoded = frames_decoded_;
530 statistics.video_frames_dropped = frames_dropped_;
531 statistics_cb_.Run(statistics);
532 376
533 frames_decoded_ = 0; 377 if (playback_rate_ == 0)
534 frames_dropped_ = 0; 378 return;
379
380 base::TimeDelta current_media_time = get_time_cb_.Run();
381 base::TimeTicks current_wall_ticks = tick_clock_->NowTicks();
382
383 while (!unscheduled_frames_.empty()) {
384 // Negative deltas are permitted as it's up to the scheduler to decide
385 // what it wants to do with late frames.
386 base::TimeDelta delta =
387 unscheduled_frames_.front()->timestamp() - current_media_time;
388 base::TimeDelta scaled_delta = base::TimeDelta::FromMicroseconds(
389 static_cast<int64>(delta.InMicroseconds() / playback_rate_));
390 base::TimeTicks target_wall_ticks = current_wall_ticks + scaled_delta;
391
392 scoped_refptr<VideoFrame> frame = unscheduled_frames_.front();
393 int frame_id = next_scheduled_frame_id_++;
394 scheduled_frames_[frame_id] = frame;
395 unscheduled_frames_.pop_front();
396
397 scheduler_->ScheduleVideoFrame(
398 frame,
399 target_wall_ticks,
400 base::Bind(&VideoRendererImpl::OnVideoFrameFinished,
401 weak_factory_.GetWeakPtr(),
402 frame_id));
403 }
404 }
405
406 void VideoRendererImpl::ScheduleFirstFrameForImmediateDisplay() {
407 DCHECK_EQ(state_, kPrerolled);
408
409 scoped_refptr<VideoFrame> frame = unscheduled_frames_.front();
410 int frame_id = next_scheduled_frame_id_++;
411 scheduled_frames_[frame_id] = frame;
412 unscheduled_frames_.pop_front();
413
414 scheduler_->ScheduleVideoFrame(
415 frame,
416 tick_clock_->NowTicks(),
417 base::Bind(&VideoRendererImpl::OnVideoFrameFinished,
418 weak_factory_.GetWeakPtr(),
419 frame_id));
420 }
421
422 void VideoRendererImpl::OnVideoFrameFinished(
423 int frame_id,
424 const scoped_refptr<VideoFrame>& frame,
425 VideoFrameScheduler::Reason reason) {
426 VideoFrameIDMap::iterator iter = scheduled_frames_.find(frame_id);
427 if (iter == scheduled_frames_.end()) {
428 LOG(ERROR) << "Ignoring frame_id=" << frame_id
429 << ", timestamp=" << frame->timestamp().InMicroseconds();
430 return;
535 } 431 }
536 432
537 frame_available_.TimedWait(wait_duration); 433 CHECK_EQ(iter->second, frame);
434 scheduled_frames_.erase(iter);
435
436 switch (reason) {
437 case VideoFrameScheduler::DISPLAYED:
438 ++frames_decoded_;
439 break;
440
441 case VideoFrameScheduler::DROPPED:
442 ++frames_decoded_;
443 ++frames_dropped_;
444 break;
445 }
446
447 PipelineStatistics stats;
448 stats.video_frames_decoded = frames_decoded_;
449 stats.video_frames_dropped = frames_dropped_;
450 statistics_cb_.Run(stats);
451
452 if (ShouldTransitionToEnded()) {
453 TransitionToEnded();
454 return;
455 }
456
457 AttemptRead();
458 }
459
460 void VideoRendererImpl::SetTickClockForTesting(
461 scoped_ptr<base::TickClock> tick_clock) {
462 tick_clock_.swap(tick_clock);
538 } 463 }
539 464
540 } // namespace media 465 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/video_renderer_impl.h ('k') | media/filters/video_renderer_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698