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

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