diff options
-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); }; |