Index: webkit/glue/webmediaplayer_impl.cc |
=================================================================== |
--- webkit/glue/webmediaplayer_impl.cc (revision 19826) |
+++ webkit/glue/webmediaplayer_impl.cc (working copy) |
@@ -2,6 +2,8 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include "webkit/glue/webmediaplayer_impl.h" |
+ |
#include "base/command_line.h" |
#include "googleurl/src/gurl.h" |
#include "media/filters/ffmpeg_audio_decoder.h" |
@@ -12,42 +14,149 @@ |
#include "webkit/api/public/WebSize.h" |
#include "webkit/api/public/WebURL.h" |
#include "webkit/glue/media/video_renderer_impl.h" |
-#include "webkit/glue/webmediaplayer_impl.h" |
using WebKit::WebCanvas; |
using WebKit::WebRect; |
using WebKit::WebSize; |
+namespace { |
+ |
+// Limits the maximum outstanding repaints posted on render thread. |
+// This number of 50 is a guess, it does not take too much memory on the task |
+// queue but gives up a pretty good latency on repaint. |
+const int kMaxOutstandingRepaints = 50; |
+ |
+} // namespace |
+ |
namespace webkit_glue { |
///////////////////////////////////////////////////////////////////////////// |
-// Task to be posted on main thread that fire WebMediaPlayerClient methods. |
+// WebMediaPlayerImpl::Proxy implementation |
-class NotifyWebMediaPlayerClientTask : public CancelableTask { |
- public: |
- NotifyWebMediaPlayerClientTask(WebMediaPlayerImpl* media_player, |
- WebMediaPlayerClientMethod method) |
- : media_player_(media_player), |
- method_(method) {} |
+WebMediaPlayerImpl::Proxy::Proxy(MessageLoop* render_loop, |
+ WebMediaPlayerImpl* webmediaplayer) |
+ : render_loop_(render_loop), |
+ webmediaplayer_(webmediaplayer), |
+ outstanding_repaints_(0) { |
+ DCHECK(render_loop_); |
+ DCHECK(webmediaplayer_); |
+} |
- virtual void Run() { |
- if (media_player_) { |
- (media_player_->client()->*(method_))(); |
- media_player_->DidTask(this); |
- } |
+WebMediaPlayerImpl::Proxy::~Proxy() { |
+ Detach(); |
+} |
+ |
+void WebMediaPlayerImpl::Proxy::Repaint() { |
+ AutoLock auto_lock(lock_); |
+ if (outstanding_repaints_ < kMaxOutstandingRepaints) { |
+ ++outstanding_repaints_; |
+ |
+ render_loop_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &WebMediaPlayerImpl::Proxy::RepaintTask)); |
} |
+} |
- virtual void Cancel() { |
- media_player_ = NULL; |
+void WebMediaPlayerImpl::Proxy::TimeChanged() { |
+ render_loop_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &WebMediaPlayerImpl::Proxy::TimeChangedTask)); |
+} |
+ |
+void WebMediaPlayerImpl::Proxy::NetworkStateChanged( |
+ WebKit::WebMediaPlayer::NetworkState state) { |
+ render_loop_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, |
+ &WebMediaPlayerImpl::Proxy::NetworkStateChangedTask, |
+ state)); |
+} |
+ |
+void WebMediaPlayerImpl::Proxy::ReadyStateChanged( |
+ WebKit::WebMediaPlayer::ReadyState state) { |
+ render_loop_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, |
+ &WebMediaPlayerImpl::Proxy::ReadyStateChangedTask, |
+ state)); |
+} |
+ |
+void WebMediaPlayerImpl::Proxy::RepaintTask() { |
+ DCHECK(MessageLoop::current() == render_loop_); |
+ { |
+ AutoLock auto_lock(lock_); |
+ --outstanding_repaints_; |
+ DCHECK_GE(outstanding_repaints_, 0); |
} |
+ if (webmediaplayer_) |
+ webmediaplayer_->Repaint(); |
+} |
- private: |
- WebMediaPlayerImpl* media_player_; |
- WebMediaPlayerClientMethod method_; |
+void WebMediaPlayerImpl::Proxy::TimeChangedTask() { |
+ DCHECK(MessageLoop::current() == render_loop_); |
+ if (webmediaplayer_) |
+ webmediaplayer_->TimeChanged(); |
+} |
- DISALLOW_COPY_AND_ASSIGN(NotifyWebMediaPlayerClientTask); |
-}; |
+void WebMediaPlayerImpl::Proxy::NetworkStateChangedTask( |
+ WebKit::WebMediaPlayer::NetworkState state) { |
+ DCHECK(MessageLoop::current() == render_loop_); |
+ if (webmediaplayer_) |
+ webmediaplayer_->SetNetworkState(state); |
+} |
+void WebMediaPlayerImpl::Proxy::ReadyStateChangedTask( |
+ WebKit::WebMediaPlayer::ReadyState state) { |
+ DCHECK(MessageLoop::current() == render_loop_); |
+ if (webmediaplayer_) |
+ webmediaplayer_->SetReadyState(state); |
+} |
+ |
+void WebMediaPlayerImpl::Proxy::SetVideoRenderer( |
+ VideoRendererImpl* video_renderer) { |
+ video_renderer_ = video_renderer; |
+} |
+ |
+void WebMediaPlayerImpl::Proxy::Paint(skia::PlatformCanvas* canvas, |
+ const gfx::Rect& dest_rect) { |
+ DCHECK(MessageLoop::current() == render_loop_); |
+ if (video_renderer_) { |
+ video_renderer_->Paint(canvas, dest_rect); |
+ } |
+} |
+ |
+void WebMediaPlayerImpl::Proxy::SetSize(const gfx::Rect& rect) { |
+ DCHECK(MessageLoop::current() == render_loop_); |
+ if (video_renderer_) { |
+ video_renderer_->SetRect(rect); |
+ } |
+} |
+ |
+void WebMediaPlayerImpl::Proxy::Detach() { |
+ DCHECK(MessageLoop::current() == render_loop_); |
+ webmediaplayer_ = NULL; |
+ video_renderer_ = NULL; |
+} |
+ |
+void WebMediaPlayerImpl::Proxy::PipelineInitializationCallback(bool success) { |
+ if (success) { |
+ // Since we have initialized the pipeline, say we have everything. |
+ // TODO(hclam): change this to report the correct status. Should also post |
+ // a task to call to |webmediaplayer_|. |
+ ReadyStateChanged(WebKit::WebMediaPlayer::HaveMetadata); |
+ ReadyStateChanged(WebKit::WebMediaPlayer::HaveEnoughData); |
+ NetworkStateChanged(WebKit::WebMediaPlayer::Loaded); |
+ } else { |
+ // TODO(hclam): should use pipeline_.GetError() to determine the state |
+ // properly and reports error using MediaError. |
+ // WebKit uses FormatError to indicate an error for bogus URL or bad file. |
+ // Since we are at the initialization stage we can safely treat every error |
+ // as format error. Should post a task to call to |webmediaplayer_|. |
+ NetworkStateChanged(WebKit::WebMediaPlayer::FormatError); |
+ } |
+} |
+ |
+void WebMediaPlayerImpl::Proxy::PipelineSeekCallback(bool success) { |
+ if (success) |
+ TimeChanged(); |
+} |
+ |
///////////////////////////////////////////////////////////////////////////// |
// WebMediaPlayerImpl implementation |
@@ -57,32 +166,28 @@ |
ready_state_(WebKit::WebMediaPlayer::HaveNothing), |
main_loop_(NULL), |
filter_factory_(factory), |
- video_renderer_(NULL), |
- client_(client), |
- tasks_(kLastTaskIndex) { |
- // Add in the default filter factories. |
- filter_factory_->AddFactory(media::FFmpegDemuxer::CreateFilterFactory()); |
- filter_factory_->AddFactory(media::FFmpegAudioDecoder::CreateFactory()); |
- filter_factory_->AddFactory(media::FFmpegVideoDecoder::CreateFactory()); |
- filter_factory_->AddFactory(media::NullAudioRenderer::CreateFilterFactory()); |
- filter_factory_->AddFactory(VideoRendererImpl::CreateFactory(this)); |
- |
- DCHECK(client_); |
- |
+ client_(client) { |
// Saves the current message loop. |
DCHECK(!main_loop_); |
main_loop_ = MessageLoop::current(); |
// Also we want to be notified of |main_loop_| destruction. |
main_loop_->AddDestructionObserver(this); |
+ |
+ // Creates the proxy. |
+ proxy_ = new Proxy(main_loop_, this); |
+ |
+ // Add in the default filter factories. |
+ filter_factory_->AddFactory(media::FFmpegDemuxer::CreateFilterFactory()); |
+ filter_factory_->AddFactory(media::FFmpegAudioDecoder::CreateFactory()); |
+ filter_factory_->AddFactory(media::FFmpegVideoDecoder::CreateFactory()); |
+ filter_factory_->AddFactory(media::NullAudioRenderer::CreateFilterFactory()); |
+ filter_factory_->AddFactory(VideoRendererImpl::CreateFactory(proxy_)); |
} |
WebMediaPlayerImpl::~WebMediaPlayerImpl() { |
- pipeline_.Stop(); |
+ Destroy(); |
- // Cancel all tasks posted on the |main_loop_|. |
- CancelAllTasks(); |
- |
// Finally tell the |main_loop_| we don't want to be notified of destruction |
// event. |
if (main_loop_) { |
@@ -91,29 +196,25 @@ |
} |
void WebMediaPlayerImpl::load(const WebKit::WebURL& url) { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
+ DCHECK(proxy_); |
// Initialize the pipeline. |
- if (network_state_ != WebKit::WebMediaPlayer::Loading) { |
- network_state_ = WebKit::WebMediaPlayer::Loading; |
- client_->networkStateChanged(); |
- } |
- if (ready_state_ != WebKit::WebMediaPlayer::HaveNothing) { |
- ready_state_ = WebKit::WebMediaPlayer::HaveNothing; |
- client_->readyStateChanged(); |
- } |
- pipeline_.Start(filter_factory_.get(), url.spec(), |
- NewCallback(this, &WebMediaPlayerImpl::OnPipelineInitialize)); |
+ SetNetworkState(WebKit::WebMediaPlayer::Loading); |
+ SetReadyState(WebKit::WebMediaPlayer::HaveNothing); |
+ pipeline_.Start( |
+ filter_factory_.get(), |
+ url.spec(), |
+ NewCallback(proxy_.get(), |
+ &WebMediaPlayerImpl::Proxy::PipelineInitializationCallback)); |
} |
void WebMediaPlayerImpl::cancelLoad() { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
- |
- // TODO(hclam): Calls to render_view_ to stop resource load |
+ DCHECK(MessageLoop::current() == main_loop_); |
} |
void WebMediaPlayerImpl::play() { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
// TODO(hclam): We should restore the previous playback rate rather than |
// having it at 1.0. |
@@ -121,69 +222,63 @@ |
} |
void WebMediaPlayerImpl::pause() { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
pipeline_.SetPlaybackRate(0.0f); |
} |
-void WebMediaPlayerImpl::stop() { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
- |
- // We can fire Stop() multiple times. |
- pipeline_.Stop(); |
-} |
- |
void WebMediaPlayerImpl::seek(float seconds) { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
// Try to preserve as much accuracy as possible. |
float microseconds = seconds * base::Time::kMicrosecondsPerSecond; |
if (seconds != 0) |
pipeline_.Seek( |
base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds)), |
- NewCallback(this, &WebMediaPlayerImpl::OnPipelineSeek)); |
+ NewCallback(proxy_.get(), |
+ &WebMediaPlayerImpl::Proxy::PipelineSeekCallback)); |
} |
void WebMediaPlayerImpl::setEndTime(float seconds) { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
// TODO(hclam): add method call when it has been implemented. |
return; |
} |
void WebMediaPlayerImpl::setRate(float rate) { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
pipeline_.SetPlaybackRate(rate); |
} |
void WebMediaPlayerImpl::setVolume(float volume) { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
pipeline_.SetVolume(volume); |
} |
void WebMediaPlayerImpl::setVisible(bool visible) { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
// TODO(hclam): add appropriate method call when pipeline has it implemented. |
return; |
} |
bool WebMediaPlayerImpl::setAutoBuffer(bool autoBuffer) { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
return false; |
} |
bool WebMediaPlayerImpl::totalBytesKnown() { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
return pipeline_.GetTotalBytes() != 0; |
} |
bool WebMediaPlayerImpl::hasVideo() const { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
size_t width, height; |
pipeline_.GetVideoSize(&width, &height); |
@@ -191,7 +286,7 @@ |
} |
WebKit::WebSize WebMediaPlayerImpl::naturalSize() const { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
size_t width, height; |
pipeline_.GetVideoSize(&width, &height); |
@@ -199,44 +294,44 @@ |
} |
bool WebMediaPlayerImpl::paused() const { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
return pipeline_.GetPlaybackRate() == 0.0f; |
} |
bool WebMediaPlayerImpl::seeking() const { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
- return tasks_[kTimeChangedTaskIndex] != NULL; |
+ return false; |
} |
float WebMediaPlayerImpl::duration() const { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
return static_cast<float>(pipeline_.GetDuration().InSecondsF()); |
} |
float WebMediaPlayerImpl::currentTime() const { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
return static_cast<float>(pipeline_.GetTime().InSecondsF()); |
} |
int WebMediaPlayerImpl::dataRate() const { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
// TODO(hclam): Add this method call if pipeline has it in the interface. |
return 0; |
} |
float WebMediaPlayerImpl::maxTimeBuffered() const { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
return static_cast<float>(pipeline_.GetBufferedTime().InSecondsF()); |
} |
float WebMediaPlayerImpl::maxTimeSeekable() const { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
// TODO(scherkus): move this logic down into the pipeline. |
if (pipeline_.GetTotalBytes() == 0) { |
@@ -249,110 +344,85 @@ |
} |
unsigned long long WebMediaPlayerImpl::bytesLoaded() const { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
return pipeline_.GetBufferedBytes(); |
} |
unsigned long long WebMediaPlayerImpl::totalBytes() const { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
return pipeline_.GetTotalBytes(); |
} |
void WebMediaPlayerImpl::setSize(const WebSize& size) { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
+ DCHECK(proxy_); |
- if (video_renderer_) { |
- // TODO(scherkus): Change API to use SetSize(). |
- video_renderer_->SetRect(gfx::Rect(0, 0, size.width, size.height)); |
- } |
+ proxy_->SetSize(gfx::Rect(0, 0, size.width, size.height)); |
} |
void WebMediaPlayerImpl::paint(WebCanvas* canvas, |
const WebRect& rect) { |
- DCHECK(main_loop_ && MessageLoop::current() == main_loop_); |
+ DCHECK(MessageLoop::current() == main_loop_); |
+ DCHECK(proxy_); |
- if (video_renderer_) { |
- video_renderer_->Paint(canvas, rect); |
- } |
+ proxy_->Paint(canvas, rect); |
} |
void WebMediaPlayerImpl::WillDestroyCurrentMessageLoop() { |
- pipeline_.Stop(); |
+ Destroy(); |
+ main_loop_ = NULL; |
} |
-void WebMediaPlayerImpl::OnPipelineInitialize(bool successful) { |
- WebKit::WebMediaPlayer::ReadyState old_ready_state = ready_state_; |
- WebKit::WebMediaPlayer::NetworkState old_network_state = network_state_; |
- if (successful) { |
- // Since we have initialized the pipeline, say we have everything. |
- // TODO(hclam): change this to report the correct status. |
- ready_state_ = WebKit::WebMediaPlayer::HaveEnoughData; |
- network_state_ = WebKit::WebMediaPlayer::Loaded; |
- } else { |
- // TODO(hclam): should use pipeline_.GetError() to determine the state |
- // properly and reports error using MediaError. |
- // WebKit uses FormatError to indicate an error for bogus URL or bad file. |
- // Since we are at the initialization stage we can safely treat every error |
- // as format error. |
- network_state_ = WebKit::WebMediaPlayer::FormatError; |
- } |
- |
- if (network_state_ != old_network_state) { |
- PostTask(kNetworkStateTaskIndex, |
- &WebKit::WebMediaPlayerClient::networkStateChanged); |
- } |
- if (ready_state_ != old_ready_state) { |
- PostTask(kReadyStateTaskIndex, |
- &WebKit::WebMediaPlayerClient::readyStateChanged); |
- } |
+void WebMediaPlayerImpl::Repaint() { |
+ DCHECK(MessageLoop::current() == main_loop_); |
+ GetClient()->repaint(); |
} |
-void WebMediaPlayerImpl::OnPipelineSeek(bool successful) { |
- PostTask(kTimeChangedTaskIndex, |
- &WebKit::WebMediaPlayerClient::timeChanged); |
+void WebMediaPlayerImpl::TimeChanged() { |
+ DCHECK(MessageLoop::current() == main_loop_); |
+ GetClient()->timeChanged(); |
} |
-void WebMediaPlayerImpl::SetVideoRenderer(VideoRendererImpl* video_renderer) { |
- video_renderer_ = video_renderer; |
-} |
- |
-void WebMediaPlayerImpl::DidTask(CancelableTask* task) { |
- AutoLock auto_lock(task_lock_); |
- |
- for (size_t i = 0; i < tasks_.size(); ++i) { |
- if (tasks_[i] == task) { |
- tasks_[i] = NULL; |
- return; |
- } |
+void WebMediaPlayerImpl::SetNetworkState( |
+ WebKit::WebMediaPlayer::NetworkState state) { |
+ DCHECK(MessageLoop::current() == main_loop_); |
+ if (network_state_ != state) { |
+ network_state_ = state; |
+ GetClient()->networkStateChanged(); |
} |
- NOTREACHED(); |
} |
-void WebMediaPlayerImpl::CancelAllTasks() { |
- AutoLock auto_lock(task_lock_); |
- // Loop through the list of tasks and cancel tasks that are still alive. |
- for (size_t i = 0; i < tasks_.size(); ++i) { |
- if (tasks_[i]) |
- tasks_[i]->Cancel(); |
+void WebMediaPlayerImpl::SetReadyState( |
+ WebKit::WebMediaPlayer::ReadyState state) { |
+ DCHECK(MessageLoop::current() == main_loop_); |
+ if (ready_state_ != state) { |
+ ready_state_ = state; |
+ GetClient()->readyStateChanged(); |
} |
} |
-void WebMediaPlayerImpl::PostTask(int index, |
- WebMediaPlayerClientMethod method) { |
- DCHECK(main_loop_); |
+void WebMediaPlayerImpl::Destroy() { |
+ DCHECK(MessageLoop::current() == main_loop_); |
- AutoLock auto_lock(task_lock_); |
- if (!tasks_[index]) { |
- CancelableTask* task = new NotifyWebMediaPlayerClientTask(this, method); |
- tasks_[index] = task; |
- main_loop_->PostTask(FROM_HERE, task); |
+ // Make sure to kill the pipeline so there's no more media threads running. |
+ // TODO(hclam): stopping the pipeline is synchronous so it might block |
+ // stopping for a long time. |
+ pipeline_.Stop(); |
+ |
+ // And then detach the proxy, it may live on the render thread for a little |
+ // longer until all the tasks are finished. |
+ if (proxy_) { |
+ proxy_->Detach(); |
+ proxy_ = NULL; |
} |
} |
-void WebMediaPlayerImpl::PostRepaintTask() { |
- PostTask(kRepaintTaskIndex, &WebKit::WebMediaPlayerClient::repaint); |
+WebKit::WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() { |
+ DCHECK(MessageLoop::current() == main_loop_); |
+ DCHECK(client_); |
+ return client_; |
} |
} // namespace webkit_glue |