| OLD | NEW |
| 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(¤t_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(¤t_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 |
| OLD | NEW |