// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Delegate calls from WebCore::MediaPlayerPrivate to Chrome's video player.
// It contains PipelineImpl which is the actual media player pipeline, it glues
// the media player pipeline, data source, audio renderer and renderer.
// PipelineImpl would creates multiple threads and access some public methods
// of this class, so we need to be extra careful about concurrent access of
// methods and members.
//
// WebMediaPlayerImpl works with multiple objects, the most important ones are:
//
// media::PipelineImpl
// The media playback pipeline.
//
// WebVideoRenderer
// 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
// | | WebVideoRenderer
// | | ^ r
// | | |
// | r | r |
// .------> Proxy <-----.
// |
// |
// v
// WebMediaPlayerClient
//
// Notice that Proxy and WebVideoRenderer 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
// destructed nicely, and there will be some dangling media threads trying to
// the main thread, so we need this class to listen to destruction event of the
// main thread and cleanup the media threads when the even is received. Also
// at destruction of this class we will need to unhook it from destruction event
// list of the main thread.
#ifndef WEBKIT_GLUE_WEBMEDIAPLAYER_IMPL_H_
#define WEBKIT_GLUE_WEBMEDIAPLAYER_IMPL_H_
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/threading/thread.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "media/base/filters.h"
#include "media/base/message_loop_factory.h"
#include "media/base/pipeline.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayer.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayerClient.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
#include "webkit/glue/media/web_data_source.h"
class GURL;
namespace WebKit {
class WebFrame;
}
namespace webkit_glue {
class MediaResourceLoaderBridgeFactory;
class WebVideoRenderer;
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);
// Methods for Filter -> WebMediaPlayerImpl communication.
void Repaint();
void SetVideoRenderer(scoped_refptr<WebVideoRenderer> video_renderer);
WebDataSourceBuildObserverHack* GetBuildObserver();
// Methods for WebMediaPlayerImpl -> Filter communication.
void Paint(SkCanvas* canvas, const gfx::Rect& dest_rect);
void SetSize(const gfx::Rect& rect);
void Detach();
void GetCurrentFrame(scoped_refptr<media::VideoFrame>* frame_out);
void PutCurrentFrame(scoped_refptr<media::VideoFrame> frame);
bool HasSingleOrigin();
void AbortDataSources();
// Methods for PipelineImpl -> WebMediaPlayerImpl communication.
void PipelineInitializationCallback(media::PipelineStatus status);
void PipelineSeekCallback(media::PipelineStatus status);
void PipelineEndedCallback(media::PipelineStatus status);
void PipelineErrorCallback(media::PipelineStatus error);
void NetworkEventCallback(media::PipelineStatus status);
// Returns the message loop used by the proxy.
MessageLoop* message_loop() { return render_loop_; }
private:
friend class base::RefCountedThreadSafe<Proxy>;
virtual ~Proxy();
// Adds a data source to data_sources_.
void AddDataSource(WebDataSource* data_source);
// Invoke |webmediaplayer_| to perform a repaint.
void RepaintTask();
// Notify |webmediaplayer_| that initialization has finished.
void PipelineInitializationTask(media::PipelineStatus status);
// Notify |webmediaplayer_| that a seek has finished.
void PipelineSeekTask(media::PipelineStatus status);
// Notify |webmediaplayer_| that the media has ended.
void PipelineEndedTask(media::PipelineStatus status);
// Notify |webmediaplayer_| that a pipeline error has occurred during
// playback.
void PipelineErrorTask(media::PipelineStatus error);
// Notify |webmediaplayer_| that there's a network event.
void NetworkEventTask(media::PipelineStatus status);
// The render message loop where WebKit lives.
MessageLoop* render_loop_;
WebMediaPlayerImpl* webmediaplayer_;
base::Lock data_sources_lock_;
typedef std::list<scoped_refptr<WebDataSource> > DataSourceList;
DataSourceList data_sources_;
scoped_ptr<WebDataSourceBuildObserverHack> build_observer_;
scoped_refptr<WebVideoRenderer> video_renderer_;
base::Lock lock_;
int outstanding_repaints_;
};
// Construct a WebMediaPlayerImpl with reference to the client, and media
// filter collection. By providing the filter collection the implementor can
// provide more specific media filters that does resource loading and
// rendering. |collection| should contain filter factories for:
// 1. Data source
// 2. Audio renderer
// 3. Video renderer (optional)
//
// There are some default filters provided by this method:
// 1. FFmpeg demuxer
// 2. FFmpeg audio decoder
// 3. FFmpeg video decoder
// 4. Video renderer
// 5. Null audio renderer
// The video renderer provided by this class is using the graphics context
// provided by WebKit to perform renderering. The simple data source does
// resource loading by loading the whole resource object into memory. Null
// audio renderer is a fake audio device that plays silence. Provider of the
// |collection| can override the default filters by adding extra filters to
// |collection| before calling this method.
//
// Callers must call |Initialize()| before they can use the object.
WebMediaPlayerImpl(WebKit::WebMediaPlayerClient* client,
media::FilterCollection* collection,
media::MessageLoopFactory* message_loop_factory);
virtual ~WebMediaPlayerImpl();
// Finalizes initialization of the object.
bool Initialize(
WebKit::WebFrame* frame,
bool use_simple_data_source,
scoped_refptr<WebVideoRenderer> web_video_renderer);
virtual void load(const WebKit::WebURL& url);
virtual void cancelLoad();
// Playback controls.
virtual void play();
virtual void pause();
virtual bool supportsFullscreen() const;
virtual bool supportsSave() const;
virtual void seek(float seconds);
virtual void setEndTime(float seconds);
virtual void setRate(float rate);
virtual void setVolume(float volume);
virtual void setVisible(bool visible);
virtual void setPreload(WebKit::WebMediaPlayer::Preload preload);
virtual bool totalBytesKnown();
virtual const WebKit::WebTimeRanges& buffered();
virtual float maxTimeSeekable() const;
// Methods for painting.
virtual void setSize(const WebKit::WebSize& size);
virtual void paint(WebKit::WebCanvas* canvas, const WebKit::WebRect& rect);
// True if the loaded media has a playable video/audio track.
virtual bool hasVideo() const;
virtual bool hasAudio() const;
// Dimensions of the video.
virtual WebKit::WebSize naturalSize() const;
// Getters of playback state.
virtual bool paused() const;
virtual bool seeking() const;
virtual float duration() const;
virtual float currentTime() const;
// Get rate of loading the resource.
virtual int32 dataRate() const;
// Internal states of loading and network.
// TODO(hclam): Ask the pipeline about the state rather than having reading
// them from members which would cause race conditions.
virtual WebKit::WebMediaPlayer::NetworkState networkState() const;
virtual WebKit::WebMediaPlayer::ReadyState readyState() const;
virtual unsigned long long bytesLoaded() const;
virtual unsigned long long totalBytes() const;
virtual bool hasSingleSecurityOrigin() const;
virtual WebKit::WebMediaPlayer::MovieLoadType movieLoadType() const;
virtual unsigned decodedFrameCount() const;
virtual unsigned droppedFrameCount() const;
virtual unsigned audioDecodedByteCount() const;
virtual unsigned videoDecodedByteCount() const;
virtual WebKit::WebVideoFrame* getCurrentFrame();
virtual void putCurrentFrame(WebKit::WebVideoFrame* web_video_frame);
// As we are closing the tab or even the browser, |main_loop_| is destroyed
// even before this object gets destructed, so we need to know when
// |main_loop_| is being destroyed and we can stop posting repaint task
// to it.
virtual void WillDestroyCurrentMessageLoop();
void Repaint();
void OnPipelineInitialize(media::PipelineStatus status);
void OnPipelineSeek(media::PipelineStatus status);
void OnPipelineEnded(media::PipelineStatus status);
void OnPipelineError(media::PipelineStatus error);
void OnNetworkEvent(media::PipelineStatus status);
private:
// Helpers that set the network/ready state and notifies the client if
// they've changed.
void SetNetworkState(WebKit::WebMediaPlayer::NetworkState state);
void SetReadyState(WebKit::WebMediaPlayer::ReadyState state);
// 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_;
WebKit::WebMediaPlayer::ReadyState ready_state_;
// Keep a list of buffered time ranges.
WebKit::WebTimeRanges buffered_;
// Message loops for posting tasks between Chrome's main thread. Also used
// for DCHECKs so methods calls won't execute in the wrong thread.
MessageLoop* main_loop_;
// A collection of filters.
scoped_ptr<media::FilterCollection> filter_collection_;
// The actual pipeline and the thread it runs on.
scoped_refptr<media::Pipeline> pipeline_;
scoped_ptr<media::MessageLoopFactory> message_loop_factory_;
// Playback state.
//
// TODO(scherkus): we have these because Pipeline favours the simplicity of a
// single "playback rate" over worrying about paused/stopped etc... It forces
// all clients to manage the pause+playback rate externally, but is that
// really a bad thing?
//
// TODO(scherkus): since SetPlaybackRate(0) is asynchronous and we don't want
// to hang the render thread during pause(), we record the time at the same
// time we pause and then return that value in currentTime(). Otherwise our
// clock can creep forward a little bit while the asynchronous
// SetPlaybackRate(0) is being executed.
bool paused_;
bool seeking_;
float playback_rate_;
base::TimeDelta paused_time_;
WebKit::WebMediaPlayerClient* client_;
scoped_refptr<Proxy> proxy_;
#if WEBKIT_USING_CG
scoped_ptr<skia::PlatformCanvas> skia_canvas_;
#endif
DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImpl);
};
} // namespace webkit_glue
#endif // WEBKIT_GLUE_WEBMEDIAPLAYER_IMPL_H_