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

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

Issue 159476: Merge 21611 - Implemented proper pausethenseek behaviour for the media pipeli... (Closed) Base URL: svn://chrome-svn/chrome/branches/195/src/
Patch Set: Created 11 years, 5 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_base.h ('k') | media/filters/video_renderer_base_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:mergeinfo
Merged /branches/chrome_webkit_merge_branch/media/filters/video_renderer_base.cc:r69-2775
Merged /trunk/src/media/filters/video_renderer_base.cc:r21611
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
2 // source code is governed by a BSD-style license that can be found in the 2 // source code is governed by a BSD-style license that can be found in the
3 // LICENSE file. 3 // LICENSE file.
4 4
5 #include "media/base/buffers.h" 5 #include "media/base/buffers.h"
6 #include "media/base/filter_host.h" 6 #include "media/base/filter_host.h"
7 #include "media/base/video_frame_impl.h"
7 #include "media/filters/video_renderer_base.h" 8 #include "media/filters/video_renderer_base.h"
8 9
9 namespace media { 10 namespace media {
10 11
11 // Limit our read ahead to three frames. One frame is typically in flux at all 12 // Limit our read ahead to three frames. One frame is typically in flux at all
12 // times, as in frame n is discarded at the top of ThreadMain() while frame 13 // times, as in frame n is discarded at the top of ThreadMain() while frame
13 // (n + kMaxFrames) is being asynchronously fetched. The remaining two frames 14 // (n + kMaxFrames) is being asynchronously fetched. The remaining two frames
14 // allow us to advance the current frame as well as read the timestamp of the 15 // allow us to advance the current frame as well as read the timestamp of the
15 // following frame for more accurate timing. 16 // following frame for more accurate timing.
16 // 17 //
17 // Increasing this number beyond 3 simply creates a larger buffer to work with 18 // Increasing this number beyond 3 simply creates a larger buffer to work with
18 // at the expense of memory (~0.5MB and ~1.3MB per frame for 480p and 720p 19 // at the expense of memory (~0.5MB and ~1.3MB per frame for 480p and 720p
19 // resolutions, respectively). This can help on lower-end systems if there are 20 // resolutions, respectively). This can help on lower-end systems if there are
20 // difficult sections in the movie and decoding slows down. 21 // difficult sections in the movie and decoding slows down.
21 static const size_t kMaxFrames = 3; 22 static const size_t kMaxFrames = 3;
22 23
23 // Sleeping for negative amounts actually hangs your thread on Windows! 24 // Sleeping for negative amounts actually hangs your thread on Windows!
24 static const int64 kMinSleepMilliseconds = 0; 25 static const int64 kMinSleepMilliseconds = 0;
25 26
26 // This equates to ~13.33 fps, which is just under the typical 15 fps that 27 // This equates to ~13.33 fps, which is just under the typical 15 fps that
27 // lower quality cameras or shooting modes usually use for video encoding. 28 // lower quality cameras or shooting modes usually use for video encoding.
28 static const int64 kMaxSleepMilliseconds = 75; 29 static const int64 kMaxSleepMilliseconds = 75;
29 30
30 VideoRendererBase::VideoRendererBase() 31 VideoRendererBase::VideoRendererBase()
31 : frame_available_(&lock_), 32 : width_(0),
32 state_(UNINITIALIZED), 33 height_(0),
34 frame_available_(&lock_),
35 state_(kUninitialized),
33 thread_(NULL), 36 thread_(NULL),
37 pending_reads_(0),
34 playback_rate_(0) { 38 playback_rate_(0) {
35 } 39 }
36 40
37 VideoRendererBase::~VideoRendererBase() { 41 VideoRendererBase::~VideoRendererBase() {
38 AutoLock auto_lock(lock_); 42 AutoLock auto_lock(lock_);
39 DCHECK(state_ == UNINITIALIZED || state_ == STOPPED); 43 DCHECK(state_ == kUninitialized || state_ == kStopped);
40 } 44 }
41 45
42 // static 46 // static
43 bool VideoRendererBase::ParseMediaFormat(const MediaFormat& media_format, 47 bool VideoRendererBase::ParseMediaFormat(const MediaFormat& media_format,
44 int* width_out, int* height_out) { 48 int* width_out, int* height_out) {
45 std::string mime_type; 49 std::string mime_type;
46 if (!media_format.GetAsString(MediaFormat::kMimeType, &mime_type)) 50 if (!media_format.GetAsString(MediaFormat::kMimeType, &mime_type))
47 return false; 51 return false;
48 if (mime_type.compare(mime_type::kUncompressedVideo) != 0) 52 if (mime_type.compare(mime_type::kUncompressedVideo) != 0)
49 return false; 53 return false;
50 if (!media_format.GetAsInteger(MediaFormat::kWidth, width_out)) 54 if (!media_format.GetAsInteger(MediaFormat::kWidth, width_out))
51 return false; 55 return false;
52 if (!media_format.GetAsInteger(MediaFormat::kHeight, height_out)) 56 if (!media_format.GetAsInteger(MediaFormat::kHeight, height_out))
53 return false; 57 return false;
54 return true; 58 return true;
55 } 59 }
56 60
61 void VideoRendererBase::Play(FilterCallback* callback) {
62 AutoLock auto_lock(lock_);
63 DCHECK_EQ(kPaused, state_);
64 scoped_ptr<FilterCallback> c(callback);
65 state_ = kPlaying;
66 callback->Run();
67 }
68
69 void VideoRendererBase::Pause(FilterCallback* callback) {
70 AutoLock auto_lock(lock_);
71 DCHECK_EQ(kPlaying, state_);
72 pause_callback_.reset(callback);
73 state_ = kPaused;
74
75 // We'll only pause when we've finished all pending reads.
76 if (pending_reads_ == 0) {
77 pause_callback_->Run();
78 pause_callback_.reset();
79 } else {
80 state_ = kPaused;
81 }
82 }
83
57 void VideoRendererBase::Stop() { 84 void VideoRendererBase::Stop() {
58 AutoLock auto_lock(lock_); 85 AutoLock auto_lock(lock_);
59 state_ = STOPPED; 86 state_ = kStopped;
60 87
61 // Signal the subclass we're stopping. 88 // Signal the subclass we're stopping.
62 // TODO(scherkus): do we trust subclasses not to do something silly while 89 // TODO(scherkus): do we trust subclasses not to do something silly while
63 // we're holding the lock? 90 // we're holding the lock?
64 OnStop(); 91 OnStop();
65 92
66 // Clean up our thread if present. 93 // Clean up our thread if present.
67 if (thread_) { 94 if (thread_) {
68 // Signal the thread since it's possible to get stopped with the video 95 // Signal the thread since it's possible to get stopped with the video
69 // thread waiting for a read to complete. 96 // thread waiting for a read to complete.
70 frame_available_.Signal(); 97 frame_available_.Signal();
71 { 98 {
72 AutoUnlock auto_unlock(lock_); 99 AutoUnlock auto_unlock(lock_);
73 PlatformThread::Join(thread_); 100 PlatformThread::Join(thread_);
74 } 101 }
75 thread_ = NULL; 102 thread_ = NULL;
76 } 103 }
77 } 104 }
78 105
79 void VideoRendererBase::SetPlaybackRate(float playback_rate) { 106 void VideoRendererBase::SetPlaybackRate(float playback_rate) {
80 AutoLock auto_lock(lock_); 107 AutoLock auto_lock(lock_);
81 playback_rate_ = playback_rate; 108 playback_rate_ = playback_rate;
82 } 109 }
83 110
84 void VideoRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) { 111 void VideoRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) {
85 AutoLock auto_lock(lock_); 112 AutoLock auto_lock(lock_);
86 // We need the first frame in |frames_| to run the VideoRendererBase main 113 DCHECK_EQ(kPaused, state_);
87 // loop, but we don't need decoded frames after the first frame since we are 114 DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed";
88 // at a new time. We should get some new frames so issue reads to compensate 115 state_ = kSeeking;
89 // for those discarded. 116 seek_callback_.reset(callback);
90 while (frames_.size() > 1) { 117
91 frames_.pop_back(); 118 // Throw away everything and schedule our reads.
92 ScheduleRead(); 119 frames_.clear();
120 for (size_t i = 0; i < kMaxFrames; ++i) {
121 ScheduleRead_Locked();
93 } 122 }
94 } 123 }
95 124
96 void VideoRendererBase::Initialize(VideoDecoder* decoder, 125 void VideoRendererBase::Initialize(VideoDecoder* decoder,
97 FilterCallback* callback) { 126 FilterCallback* callback) {
98 AutoLock auto_lock(lock_); 127 AutoLock auto_lock(lock_);
99 DCHECK(decoder); 128 DCHECK(decoder);
100 DCHECK(callback); 129 DCHECK(callback);
101 DCHECK_EQ(state_, UNINITIALIZED); 130 DCHECK_EQ(kUninitialized, state_);
102 state_ = INITIALIZING;
103 decoder_ = decoder; 131 decoder_ = decoder;
104 initialize_callback_.reset(callback); 132 scoped_ptr<FilterCallback> c(callback);
105 133
106 // Notify the pipeline of the video dimensions. 134 // Notify the pipeline of the video dimensions.
107 int width = 0; 135 if (!ParseMediaFormat(decoder->media_format(), &width_, &height_)) {
108 int height = 0;
109 if (!ParseMediaFormat(decoder->media_format(), &width, &height)) {
110 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); 136 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED);
111 initialize_callback_->Run(); 137 callback->Run();
112 initialize_callback_.reset();
113 return; 138 return;
114 } 139 }
115 host()->SetVideoSize(width, height); 140 host()->SetVideoSize(width_, height_);
116 141
117 // Initialize the subclass. 142 // Initialize the subclass.
118 // TODO(scherkus): do we trust subclasses not to do something silly while 143 // TODO(scherkus): do we trust subclasses not to do something silly while
119 // we're holding the lock? 144 // we're holding the lock?
120 if (!OnInitialize(decoder)) { 145 if (!OnInitialize(decoder)) {
121 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); 146 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED);
122 initialize_callback_->Run(); 147 callback->Run();
123 initialize_callback_.reset();
124 return; 148 return;
125 } 149 }
126 150
151 // Create a black frame so clients have something to render before we finish
152 // prerolling.
153 CreateBlackFrame(&current_frame_);
154
155 // We're all good! Consider ourselves paused (ThreadMain() should never
156 // see us in the kUninitialized state).
157 state_ = kPaused;
158
127 // Create our video thread. 159 // Create our video thread.
128 if (!PlatformThread::Create(0, this, &thread_)) { 160 if (!PlatformThread::Create(0, this, &thread_)) {
129 NOTREACHED() << "Video thread creation failed"; 161 NOTREACHED() << "Video thread creation failed";
130 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); 162 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED);
131 initialize_callback_->Run(); 163 callback->Run();
132 initialize_callback_.reset();
133 return; 164 return;
134 } 165 }
135 166
136 #if defined(OS_WIN) 167 #if defined(OS_WIN)
137 // Bump up our priority so our sleeping is more accurate. 168 // Bump up our priority so our sleeping is more accurate.
138 // TODO(scherkus): find out if this is necessary, but it seems to help. 169 // TODO(scherkus): find out if this is necessary, but it seems to help.
139 ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL); 170 ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL);
140 #endif // defined(OS_WIN) 171 #endif // defined(OS_WIN)
141 172
142 // Queue initial reads. 173 // Finally, execute the start callback.
143 for (size_t i = 0; i < kMaxFrames; ++i) { 174 callback->Run();
144 ScheduleRead();
145 }
146 } 175 }
147 176
148 // PlatformThread::Delegate implementation. 177 // PlatformThread::Delegate implementation.
149 void VideoRendererBase::ThreadMain() { 178 void VideoRendererBase::ThreadMain() {
150 PlatformThread::SetName("VideoThread"); 179 PlatformThread::SetName("VideoThread");
151
152 // Wait to be initialized so we can notify the first frame is available.
153 if (!WaitForInitialized())
154 return;
155 OnFrameAvailable();
156
157 for (;;) { 180 for (;;) {
158 // State and playback rate to assume for this iteration of the loop. 181 // State and playback rate to assume for this iteration of the loop.
159 State state; 182 State state;
160 float playback_rate; 183 float playback_rate;
161 { 184 {
162 AutoLock auto_lock(lock_); 185 AutoLock auto_lock(lock_);
163 state = state_; 186 state = state_;
164 playback_rate = playback_rate_; 187 playback_rate = playback_rate_;
165 } 188 }
166 if (state == STOPPED) { 189 if (state == kStopped) {
167 return; 190 return;
168 } 191 }
169 DCHECK_EQ(state, INITIALIZED);
170 192
171 // Sleep for 10 milliseconds while paused. 193 // Sleep for 10 milliseconds while paused. Nothing special about the value,
172 if (playback_rate == 0) { 194 // other than we're being more OS-friendly than sleeping for 1 millisecond.
195 if (state == kPaused || state == kSeeking || playback_rate == 0) {
173 PlatformThread::Sleep(10); 196 PlatformThread::Sleep(10);
174 continue; 197 continue;
175 } 198 }
176 199
177 // Advance |current_frame_| and try to determine |next_frame|. 200 // Advance |current_frame_| and try to determine |next_frame|. Note that
201 // this loop executes our "playing" logic.
202 DCHECK_EQ(kPlaying, state);
178 scoped_refptr<VideoFrame> next_frame; 203 scoped_refptr<VideoFrame> next_frame;
179 { 204 {
180 AutoLock auto_lock(lock_); 205 AutoLock auto_lock(lock_);
181 DCHECK(!frames_.empty()); 206 // Check the actual state to see if we're trying to stop playing.
182 DCHECK_EQ(current_frame_, frames_.front()); 207 if (state_ != kPlaying) {
183 frames_.pop_front(); 208 continue;
184 ScheduleRead(); 209 }
185 while (frames_.empty()) { 210
211 // Otherwise we're playing, so advance the frame and keep reading from the
212 // decoder. |frames_| might be empty if we seeked to the very end of the
213 // media where no frames were available.
214 if (!frames_.empty()) {
215 DCHECK_EQ(current_frame_, frames_.front());
216 frames_.pop_front();
217 ScheduleRead_Locked();
218 }
219
220 // While playing, we'll wait until a new frame arrives before updating
221 // |current_frame_|.
222 while (frames_.empty() && state_ == kPlaying) {
186 frame_available_.Wait(); 223 frame_available_.Wait();
224 }
187 225
188 // We have the lock again, check the actual state to see if we're trying 226 // If we ended up transitioning out of playing while waiting for a new
189 // to stop. 227 // frame, restart the iteration.
190 if (state_ == STOPPED) { 228 if (state_ != kPlaying) {
191 return; 229 continue;
192 }
193 } 230 }
231
232 // Update our current frame and attempt to grab the next frame.
194 current_frame_ = frames_.front(); 233 current_frame_ = frames_.front();
195 if (frames_.size() >= 2) { 234 if (frames_.size() >= 2) {
196 next_frame = frames_[1]; 235 next_frame = frames_[1];
197 } 236 }
198 } 237 }
199 238
200 // Notify subclass that |current_frame_| has been updated. 239 // Notify subclass that |current_frame_| has been updated.
201 OnFrameAvailable(); 240 OnFrameAvailable();
202 241
203 // Determine the current and next presentation timestamps. 242 // Calculate our sleep duration.
204 base::TimeDelta now = host()->GetTime(); 243 base::TimeDelta sleep = CalculateSleepDuration(next_frame, playback_rate);
205 base::TimeDelta this_pts = current_frame_->GetTimestamp();
206 base::TimeDelta next_pts;
207 if (next_frame) {
208 next_pts = next_frame->GetTimestamp();
209 } else {
210 next_pts = this_pts + current_frame_->GetDuration();
211 }
212
213 // Determine our sleep duration based on whether time advanced.
214 base::TimeDelta sleep;
215 if (now == previous_time_) {
216 // Time has not changed, assume we sleep for the frame's duration.
217 sleep = next_pts - this_pts;
218 } else {
219 // Time has changed, figure out real sleep duration.
220 sleep = next_pts - now;
221 previous_time_ = now;
222 }
223
224 // Scale our sleep based on the playback rate.
225 // TODO(scherkus): floating point badness and degrade gracefully.
226 int sleep_ms = static_cast<int>(sleep.InMicroseconds() / playback_rate /
227 base::Time::kMicrosecondsPerMillisecond);
228 244
229 // To be safe, limit our sleep duration. 245 // To be safe, limit our sleep duration.
230 // TODO(scherkus): handle seeking gracefully.. right now a seek backwards 246 // TODO(scherkus): handle seeking gracefully.. right now a seek backwards
231 // will hit kMinSleepMilliseconds whereas a seek forward will hit 247 // will hit kMinSleepMilliseconds whereas a seek forward will hit
232 // kMaxSleepMilliseconds. 248 // kMaxSleepMilliseconds.
249 int sleep_ms = static_cast<int>(sleep.InMilliseconds());
233 if (sleep_ms < kMinSleepMilliseconds) 250 if (sleep_ms < kMinSleepMilliseconds)
234 sleep_ms = kMinSleepMilliseconds; 251 sleep_ms = kMinSleepMilliseconds;
235 else if (sleep_ms > kMaxSleepMilliseconds) 252 else if (sleep_ms > kMaxSleepMilliseconds)
236 sleep_ms = kMaxSleepMilliseconds; 253 sleep_ms = kMaxSleepMilliseconds;
237 254
238 PlatformThread::Sleep(sleep_ms); 255 PlatformThread::Sleep(sleep_ms);
239 } 256 }
240 } 257 }
241 258
242 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { 259 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) {
243 AutoLock auto_lock(lock_); 260 AutoLock auto_lock(lock_);
244 // Either we have initialized or we have the current frame. 261 // We should have initialized and have the current frame.
245 DCHECK(state_ != INITIALIZED || current_frame_); 262 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying);
263 DCHECK(current_frame_);
246 *frame_out = current_frame_; 264 *frame_out = current_frame_;
247 } 265 }
248 266
249 void VideoRendererBase::OnReadComplete(VideoFrame* frame) { 267 void VideoRendererBase::OnReadComplete(VideoFrame* frame) {
250 AutoLock auto_lock(lock_); 268 AutoLock auto_lock(lock_);
269 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying);
270 DCHECK_GT(pending_reads_, 0u);
271 --pending_reads_;
272
251 // If this is an end of stream frame, don't enqueue it since it has no data. 273 // If this is an end of stream frame, don't enqueue it since it has no data.
252 if (!frame->IsEndOfStream()) { 274 if (!frame->IsEndOfStream()) {
253 frames_.push_back(frame); 275 frames_.push_back(frame);
254 DCHECK_LE(frames_.size(), kMaxFrames); 276 DCHECK_LE(frames_.size(), kMaxFrames);
255 frame_available_.Signal(); 277 frame_available_.Signal();
256 } 278 }
257 279
258 // Check for our initialization condition. 280 // Check for our preroll complete condition.
259 if (state_ == INITIALIZING && 281 if (state_ == kSeeking) {
260 (frames_.size() == kMaxFrames || frame->IsEndOfStream())) { 282 DCHECK(seek_callback_.get());
261 if (frames_.empty()) { 283 if (frames_.size() == kMaxFrames || frame->IsEndOfStream()) {
262 // We should have initialized but there's no decoded frames in the queue. 284 if (frames_.empty()) {
263 // Raise an error. 285 // Eeep.. we seeked to somewhere where there's no video data (most
264 state_ = ERRORED; 286 // likely the very end of the file). For user-friendliness, we'll
265 host()->SetError(PIPELINE_ERROR_NO_DATA); 287 // create a black frame just in case |current_frame_| is old or garbage.
266 initialize_callback_->Run(); 288 CreateBlackFrame(&current_frame_);
267 initialize_callback_.reset(); 289 } else {
268 } else { 290 // Update our current frame.
269 state_ = INITIALIZED; 291 current_frame_ = frames_.front();
270 current_frame_ = frames_.front(); 292 }
271 initialize_callback_->Run(); 293 // Because we might remain paused, we can't rely on ThreadMain() to
272 initialize_callback_.reset(); 294 // notify the subclass the frame has been updated.
295 DCHECK(current_frame_);
296 state_ = kPaused;
297 OnFrameAvailable();
298
299 seek_callback_->Run();
300 seek_callback_.reset();
301 }
302 } else if (state_ == kPaused && pending_reads_ == 0) {
303 // No more pending reads! We're now officially "paused".
304 if (pause_callback_.get()) {
305 pause_callback_->Run();
306 pause_callback_.reset();
273 } 307 }
274 } 308 }
275 } 309 }
276 310
277 void VideoRendererBase::ScheduleRead() { 311 void VideoRendererBase::ScheduleRead_Locked() {
312 lock_.AssertAcquired();
313 DCHECK_LT(pending_reads_, kMaxFrames);
314 ++pending_reads_;
278 decoder_->Read(NewCallback(this, &VideoRendererBase::OnReadComplete)); 315 decoder_->Read(NewCallback(this, &VideoRendererBase::OnReadComplete));
279 } 316 }
280 317
281 bool VideoRendererBase::WaitForInitialized() { 318 base::TimeDelta VideoRendererBase::CalculateSleepDuration(
282 // This loop essentially handles preroll. We wait until we've been fully 319 VideoFrame* next_frame, float playback_rate) {
283 // initialized so we can call OnFrameAvailable() to provide subclasses with 320 // Determine the current and next presentation timestamps.
284 // the first frame. 321 base::TimeDelta now = host()->GetTime();
285 AutoLock auto_lock(lock_); 322 base::TimeDelta this_pts = current_frame_->GetTimestamp();
286 while (state_ == INITIALIZING) { 323 base::TimeDelta next_pts;
287 frame_available_.Wait(); 324 if (next_frame) {
325 next_pts = next_frame->GetTimestamp();
326 } else {
327 next_pts = this_pts + current_frame_->GetDuration();
288 } 328 }
289 if (state_ == STOPPED || state_ == ERRORED) { 329
290 return false; 330 // Determine our sleep duration based on whether time advanced.
331 base::TimeDelta sleep;
332 if (now == previous_time_) {
333 // Time has not changed, assume we sleep for the frame's duration.
334 sleep = next_pts - this_pts;
335 } else {
336 // Time has changed, figure out real sleep duration.
337 sleep = next_pts - now;
338 previous_time_ = now;
291 } 339 }
292 DCHECK_EQ(state_, INITIALIZED); 340
293 DCHECK(current_frame_); 341 // Scale our sleep based on the playback rate.
294 return true; 342 // TODO(scherkus): floating point badness and degrade gracefully.
343 return base::TimeDelta::FromMicroseconds(
344 static_cast<int64>(sleep.InMicroseconds() / playback_rate));
345 }
346
347 void VideoRendererBase::CreateBlackFrame(scoped_refptr<VideoFrame>* frame_out) {
348 DCHECK_GT(width_, 0);
349 DCHECK_GT(height_, 0);
350 *frame_out = NULL;
351
352 // Create our frame.
353 scoped_refptr<VideoFrame> frame;
354 const base::TimeDelta kZero;
355 VideoFrameImpl::CreateFrame(VideoSurface::YV12, width_, height_, kZero, kZero,
356 &frame);
357 DCHECK(frame);
358
359 // Now set the data to YUV(0,128,128).
360 VideoSurface surface;
361 frame->Lock(&surface);
362 DCHECK_EQ(VideoSurface::YV12, surface.format) << "Expected YV12 surface";
363
364 // Fill the Y plane.
365 for (size_t i = 0; i < surface.height; ++i) {
366 memset(surface.data[VideoSurface::kYPlane], 0x00, surface.width);
367 surface.data[VideoSurface::kYPlane]
368 += surface.strides[VideoSurface::kYPlane];
369 }
370
371 // Fill the U and V planes.
372 for (size_t i = 0; i < (surface.height / 2); ++i) {
373 memset(surface.data[VideoSurface::kUPlane], 0x80, surface.width / 2);
374 memset(surface.data[VideoSurface::kVPlane], 0x80, surface.width / 2);
375 surface.data[VideoSurface::kUPlane]
376 += surface.strides[VideoSurface::kUPlane];
377 surface.data[VideoSurface::kVPlane]
378 += surface.strides[VideoSurface::kVPlane];
379 }
380 frame->Unlock();
381
382 // Success!
383 *frame_out = frame;
295 } 384 }
296 385
297 } // namespace media 386 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/video_renderer_base.h ('k') | media/filters/video_renderer_base_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698