diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-07 17:31:49 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-07 17:31:49 +0000 |
commit | 8931c41ae5392aab1c70f9c9b4e4c64061fdd9af (patch) | |
tree | be0d4f804f0e5964ffbd093b26d8c3fe96f443c3 | |
parent | 7de37fb6af07c84deee63f6a1baec91e0290c830 (diff) | |
download | chromium_src-8931c41ae5392aab1c70f9c9b4e4c64061fdd9af.zip chromium_src-8931c41ae5392aab1c70f9c9b4e4c64061fdd9af.tar.gz chromium_src-8931c41ae5392aab1c70f9c9b4e4c64061fdd9af.tar.bz2 |
Refactoring to introduce refcount to WebMediaPlayerImpl
WebMediaPlayerImpl interacts with multiple threads that it
becomes necessary to make it refcounted so we can post task
on different threads' message loop.
Review URL: http://codereview.chromium.org/147225
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20038 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | webkit/api/public/WebMediaPlayer.h | 9 | ||||
-rw-r--r-- | webkit/glue/media/video_renderer_impl.cc | 14 | ||||
-rw-r--r-- | webkit/glue/media/video_renderer_impl.h | 15 | ||||
-rw-r--r-- | webkit/glue/webmediaplayer_impl.cc | 366 | ||||
-rw-r--r-- | webkit/glue/webmediaplayer_impl.h | 179 |
5 files changed, 346 insertions, 237 deletions
diff --git a/webkit/api/public/WebMediaPlayer.h b/webkit/api/public/WebMediaPlayer.h index 56d01e5..740b894 100644 --- a/webkit/api/public/WebMediaPlayer.h +++ b/webkit/api/public/WebMediaPlayer.h @@ -1,10 +1,10 @@ /* * Copyright (C) 2009 Google Inc. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above @@ -14,7 +14,7 @@ * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -67,7 +67,6 @@ namespace WebKit { // Playback controls. virtual void play() = 0; virtual void pause() = 0; - virtual void stop() = 0; virtual void seek(float seconds) = 0; virtual void setEndTime(float seconds) = 0; virtual void setRate(float) = 0; @@ -88,7 +87,7 @@ namespace WebKit { // Dimension of the video. virtual WebSize naturalSize() const = 0; - // Getters fo playback state. + // Getters of playback state. virtual bool paused() const = 0; virtual bool seeking() const = 0; virtual float duration() const = 0; diff --git a/webkit/glue/media/video_renderer_impl.cc b/webkit/glue/media/video_renderer_impl.cc index 9137fb3..a7dc980 100644 --- a/webkit/glue/media/video_renderer_impl.cc +++ b/webkit/glue/media/video_renderer_impl.cc @@ -9,12 +9,12 @@ namespace webkit_glue { -VideoRendererImpl::VideoRendererImpl(WebMediaPlayerImpl* delegate) - : delegate_(delegate), + VideoRendererImpl::VideoRendererImpl(WebMediaPlayerImpl::Proxy* proxy) + : proxy_(proxy), last_converted_frame_(NULL) { // TODO(hclam): decide whether to do the following line in this thread or // in the render thread. - delegate_->SetVideoRenderer(this); + proxy->SetVideoRenderer(this); } // static @@ -25,7 +25,6 @@ bool VideoRendererImpl::IsMediaFormatSupported( return ParseMediaFormat(media_format, &width, &height); } - bool VideoRendererImpl::OnInitialize(media::VideoDecoder* decoder) { int width = 0; int height = 0; @@ -44,11 +43,14 @@ bool VideoRendererImpl::OnInitialize(media::VideoDecoder* decoder) { } void VideoRendererImpl::OnStop() { - delegate_->SetVideoRenderer(NULL); + DCHECK(proxy_); + proxy_->SetVideoRenderer(NULL); + proxy_ = NULL; } void VideoRendererImpl::OnFrameAvailable() { - delegate_->PostRepaintTask(); + DCHECK(proxy_); + proxy_->Repaint(); } void VideoRendererImpl::SetRect(const gfx::Rect& rect) { diff --git a/webkit/glue/media/video_renderer_impl.h b/webkit/glue/media/video_renderer_impl.h index ee251a5..d57223e 100644 --- a/webkit/glue/media/video_renderer_impl.h +++ b/webkit/glue/media/video_renderer_impl.h @@ -22,14 +22,13 @@ #include "media/base/filters.h" #include "media/filters/video_renderer_base.h" #include "webkit/api/public/WebMediaPlayer.h" +#include "webkit/glue/webmediaplayer_impl.h" namespace webkit_glue { -class WebMediaPlayerImpl; - class VideoRendererImpl : public media::VideoRendererBase { public: - // Methods for painting called by the WebMediaPlayerDelegateImpl + // Methods for painting called by the WebMediaPlayerImpl::Proxy // This method is called with the same rect as the Paint method and could // be used by future implementations to implement an improved color space + @@ -42,9 +41,9 @@ class VideoRendererImpl : public media::VideoRendererBase { virtual void Paint(skia::PlatformCanvas* canvas, const gfx::Rect& dest_rect); // Static method for creating factory for this object. - static media::FilterFactory* CreateFactory(WebMediaPlayerImpl* delegate) { + static media::FilterFactory* CreateFactory(WebMediaPlayerImpl::Proxy* proxy) { return new media::FilterFactoryImpl1<VideoRendererImpl, - WebMediaPlayerImpl*>(delegate); + WebMediaPlayerImpl::Proxy*>(proxy); } // FilterFactoryImpl1 implementation. @@ -63,8 +62,8 @@ class VideoRendererImpl : public media::VideoRendererBase { private: // Only the filter factories can create instances. friend class media::FilterFactoryImpl1<VideoRendererImpl, - WebMediaPlayerImpl*>; - explicit VideoRendererImpl(WebMediaPlayerImpl* delegate); + WebMediaPlayerImpl::Proxy*>; + explicit VideoRendererImpl(WebMediaPlayerImpl::Proxy* proxy); virtual ~VideoRendererImpl() {} // Determine the conditions to perform fast paint. Returns true if we can do @@ -87,7 +86,7 @@ class VideoRendererImpl : public media::VideoRendererBase { SkIRect* dest_rect); // Pointer to our parent object that is called to request repaints. - WebMediaPlayerImpl* delegate_; + scoped_refptr<WebMediaPlayerImpl::Proxy> proxy_; // An RGB bitmap used to convert the video frames. SkBitmap bitmap_; diff --git a/webkit/glue/webmediaplayer_impl.cc b/webkit/glue/webmediaplayer_impl.cc index d538a72..452a9e6 100644 --- a/webkit/glue/webmediaplayer_impl.cc +++ b/webkit/glue/webmediaplayer_impl.cc @@ -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,41 +14,148 @@ #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. - -class NotifyWebMediaPlayerClientTask : public CancelableTask { - public: - NotifyWebMediaPlayerClientTask(WebMediaPlayerImpl* media_player, - WebMediaPlayerClientMethod method) - : media_player_(media_player), - method_(method) {} - - virtual void Run() { - if (media_player_) { - (media_player_->client()->*(method_))(); - media_player_->DidTask(this); - } +// WebMediaPlayerImpl::Proxy implementation + +WebMediaPlayerImpl::Proxy::Proxy(MessageLoop* render_loop, + WebMediaPlayerImpl* webmediaplayer) + : render_loop_(render_loop), + webmediaplayer_(webmediaplayer), + outstanding_repaints_(0) { + DCHECK(render_loop_); + DCHECK(webmediaplayer_); +} + +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)); + } +} + +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(); +} + +void WebMediaPlayerImpl::Proxy::TimeChangedTask() { + DCHECK(MessageLoop::current() == render_loop_); + if (webmediaplayer_) + webmediaplayer_->TimeChanged(); +} + +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); } +} - virtual void Cancel() { - media_player_ = NULL; +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; +} - private: - WebMediaPlayerImpl* media_player_; - WebMediaPlayerClientMethod method_; +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); + } +} - DISALLOW_COPY_AND_ASSIGN(NotifyWebMediaPlayerClientTask); -}; +void WebMediaPlayerImpl::Proxy::PipelineSeekCallback(bool success) { + if (success) + TimeChanged(); +} ///////////////////////////////////////////////////////////////////////////// // WebMediaPlayerImpl implementation @@ -57,31 +166,27 @@ WebMediaPlayerImpl::WebMediaPlayerImpl(WebKit::WebMediaPlayerClient* client, 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(); - - // Cancel all tasks posted on the |main_loop_|. - CancelAllTasks(); + Destroy(); // Finally tell the |main_loop_| we don't want to be notified of destruction // event. @@ -91,29 +196,25 @@ WebMediaPlayerImpl::~WebMediaPlayerImpl() { } 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::play() { } 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 @@ bool WebMediaPlayerImpl::hasVideo() const { } 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 @@ WebKit::WebSize WebMediaPlayerImpl::naturalSize() const { } 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 @@ float WebMediaPlayerImpl::maxTimeSeekable() const { } 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 diff --git a/webkit/glue/webmediaplayer_impl.h b/webkit/glue/webmediaplayer_impl.h index d8a365c..604188c 100644 --- a/webkit/glue/webmediaplayer_impl.h +++ b/webkit/glue/webmediaplayer_impl.h @@ -9,28 +9,38 @@ // of this class, so we need to be extra careful about concurrent access of // methods and members. // -// Properties that are shared by main thread and media threads: -// CancelableTaskList tasks_; -// ^--- This property is shared for keeping records of the tasks posted to -// make sure there will be only one task for each task type that can -// exist in the main thread. +// WebMediaPlayerImpl works with multiple objects, the most important ones are: // -// Methods that are accessed in media threads: -// SetAudioRenderer() -// ^--- Called during the initialization of the pipeline, essentially from the -// the pipeline thread. -// SetVideoRenderer() -// ^--- Called during the initialization of the pipeline, essentially from the -// the pipeline thread. -// PostRepaintTask() -// ^--- Called from the video renderer thread to notify a video frame has -// been prepared. -// PostTask() -// ^--- A method that helps posting tasks to the main thread, it is -// accessed from main thread and media threads, it access the |tasks_| -// internally. Needs locking inside to avoid concurrent access to -// |tasks_|. +// media::PipelineImpl +// The media playback pipeline. // +// VideoRendererImpl +// Video renderer object. +// +// WebMediaPlayerImpl::Proxy +// Proxies methods calls from the media pipeline to WebKit. +// +// WebKit::WebMediaPlayerClient +// WebKit client of this media player object. +// +// The following diagram shows the relationship of these objects: +// (note: ref-counted reference is marked by a "r".) +// +// WebMediaPlayerImpl ------> PipelineImpl +// | ^ | r +// | | v +// | | VideoRendererImpl +// | | ^ r +// | | | +// | r | r | +// .------> Proxy <-----. +// | +// | +// v +// WebMediaPlayerClient +// +// Notice that Proxy and VideoRendererImpl are referencing each other. This +// interdependency has to be treated carefully. // // Other issues: // During tear down of the whole browser or a tab, the DOM tree may not be @@ -41,23 +51,22 @@ // list of the main thread. #ifndef WEBKIT_GLUE_WEBMEDIAPLAYER_IMPL_H_ -#define WEBKTI_GLUE_WEBMEDIAPLAYER_IMPL_H_ +#define WEBKIT_GLUE_WEBMEDIAPLAYER_IMPL_H_ #include <vector> #include "base/gfx/platform_canvas.h" +#include "base/gfx/rect.h" +#include "base/gfx/size.h" #include "base/lock.h" #include "base/message_loop.h" +#include "base/ref_counted.h" #include "media/base/filters.h" #include "media/base/pipeline_impl.h" #include "webkit/api/public/WebMediaPlayer.h" #include "webkit/api/public/WebMediaPlayerClient.h" -class AudioRendererImpl; -class DataSourceImpl; class GURL; -class RenderView; -class VideoRendererImpl; namespace media { class FilterFactoryCollection; @@ -65,13 +74,75 @@ class FilterFactoryCollection; namespace webkit_glue { -// This typedef is used for WebMediaPlayerImpl::PostTask() and -// NotifyWebMediaPlayerTask in the source file. -typedef void (WebKit::WebMediaPlayerClient::*WebMediaPlayerClientMethod)(); +class VideoRendererImpl; class WebMediaPlayerImpl : public WebKit::WebMediaPlayer, public MessageLoop::DestructionObserver { public: + // A proxy class that dispatches method calls from the media pipeline to + // WebKit. Since there are multiple threads in the media pipeline and there's + // need for the media pipeline to call to WebKit, e.g. repaint requests, + // initialization events, etc, we have this class to bridge all method calls + // from the media pipeline on different threads and serialize these calls + // on the render thread. + // Because of the nature of this object that it works with different threads, + // it is made ref-counted. + class Proxy : public base::RefCountedThreadSafe<Proxy> { + public: + Proxy(MessageLoop* render_loop, + WebMediaPlayerImpl* webmediaplayer); + virtual ~Proxy(); + + // Fire a repaint event to WebKit. + void Repaint(); + + // Report to WebKit that time has changed. + void TimeChanged(); + + // Report to WebKit that network state has changed. + void NetworkStateChanged(WebKit::WebMediaPlayer::NetworkState state); + + // Report the WebKit that ready state has changed. + void ReadyStateChanged(WebKit::WebMediaPlayer::ReadyState state); + + // Public methods to be called from video renderer. + void SetVideoRenderer(VideoRendererImpl* video_renderer); + + private: + friend class WebMediaPlayerImpl; + + // Invoke |webmediaplayer_| to perform a repaint. + void RepaintTask(); + + // Invoke |webmediaplayer_| to notify a time change event. + void TimeChangedTask(); + + // Saves the internal network state and notify WebKit to pick up the change. + void NetworkStateChangedTask(WebKit::WebMediaPlayer::NetworkState state); + + // Saves the internal ready state and notify WebKit to pick the change. + void ReadyStateChangedTask(WebKit::WebMediaPlayer::ReadyState state); + + void Paint(skia::PlatformCanvas* canvas, const gfx::Rect& dest_rect); + + void SetSize(const gfx::Rect& rect); + + // Detach from |webmediaplayer_|. + void Detach(); + + void PipelineInitializationCallback(bool success); + + void PipelineSeekCallback(bool success); + + // The render message loop where WebKit lives. + MessageLoop* render_loop_; + WebMediaPlayerImpl* webmediaplayer_; + scoped_refptr<VideoRendererImpl> video_renderer_; + + Lock lock_; + int outstanding_repaints_; + }; + // Construct a WebMediaPlayerImpl with reference to the client, and media // filter factory collection. By providing the filter factory collection // the implementor can provide more specific media filters that does resource @@ -102,7 +173,6 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer, // Playback controls. virtual void play(); virtual void pause(); - virtual void stop(); virtual void seek(float seconds); virtual void setEndTime(float seconds); virtual void setRate(float rate); @@ -152,42 +222,20 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer, // to it. virtual void WillDestroyCurrentMessageLoop(); - // Notification from |pipeline_| when initialization has finished. - void OnPipelineInitialize(bool successful); - - // Notification from |pipeline_| when a seek has finished. - void OnPipelineSeek(bool successful); - - // Called from tasks posted to |main_loop_| from this object to remove - // reference of them. - void DidTask(CancelableTask* task); + void Repaint(); - // Public methods to be called from renderers and data source so that - // WebMediaPlayerImpl has references to them. - void SetVideoRenderer(VideoRendererImpl* video_renderer); + void TimeChanged(); - // Called from VideoRenderer to fire a repaint task to |main_loop_|. - void PostRepaintTask(); + void SetNetworkState(WebKit::WebMediaPlayer::NetworkState state); - // Inline getters. - WebKit::WebMediaPlayerClient* client() { return client_; } + void SetReadyState(WebKit::WebMediaPlayer::ReadyState state); private: - // Methods for posting tasks and cancelling tasks. This method may lives in - // the main thread or the media threads. - void PostTask(int index, WebMediaPlayerClientMethod method); - - // Cancel all tasks currently live in |main_loop_|. - void CancelAllTasks(); - - // Indexes for tasks. - enum { - kRepaintTaskIndex = 0, - kReadyStateTaskIndex, - kNetworkStateTaskIndex, - kTimeChangedTaskIndex, - kLastTaskIndex - }; + // Destroy resources held. + void Destroy(); + + // Getter method to |client_|. + WebKit::WebMediaPlayerClient* GetClient(); // TODO(hclam): get rid of these members and read from the pipeline directly. WebKit::WebMediaPlayer::NetworkState network_state_; @@ -204,18 +252,9 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer, // the same lifetime as the pipeline. media::PipelineImpl pipeline_; - // We have the interface to VideoRenderer to delegate paint messages to it - // from WebKit. - scoped_refptr<VideoRendererImpl> video_renderer_; - WebKit::WebMediaPlayerClient* client_; - // List of tasks for holding pointers to all tasks currently in the - // |main_loop_|. |tasks_| can be access from main thread or the media threads - // we need a lock for protecting it. - Lock task_lock_; - typedef std::vector<CancelableTask*> CancelableTaskList; - CancelableTaskList tasks_; + scoped_refptr<Proxy> proxy_; DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImpl); }; |