Commit 7a2b1cb8 authored by boliu's avatar boliu Committed by Commit bot

Handle context loss in WebMediaPlayerPlayer in-process

Add plumbing for a ResetStreamTextureProxy which should be
called on the event of a context loss. Only hooked up the
call for the in-process implementation in this CL when the
contex is restored.

ResetStreamTextureProxy currently handles "onContextLoss" as
well as "onContextRestored" concepts. It deletes the old
stream texture id, and recreates and binds a new
StreamTextureProxy.

BUG=412578

Review URL: https://codereview.chromium.org/532993002

Cr-Commit-Position: refs/heads/master@{#295219}
parent dd4b8a50
......@@ -4,6 +4,7 @@
#include "content/browser/android/in_process/synchronous_compositor_factory_impl.h"
#include "base/observer_list.h"
#include "content/browser/android/in_process/synchronous_compositor_output_surface.h"
#include "content/public/browser/browser_thread.h"
#include "content/renderer/gpu/frame_swap_message_queue.h"
......@@ -103,7 +104,9 @@ WrapContextWithAttributes(
context.Pass(), attributes));
}
class VideoContextProvider
} // namespace
class SynchronousCompositorFactoryImpl::VideoContextProvider
: public StreamTextureFactorySynchronousImpl::ContextProvider {
public:
VideoContextProvider(
......@@ -125,18 +128,32 @@ class VideoContextProvider
return context_provider_->ContextGL();
}
virtual void AddObserver(StreamTextureFactoryContextObserver* obs) OVERRIDE {
observer_list_.AddObserver(obs);
}
virtual void RemoveObserver(
StreamTextureFactoryContextObserver* obs) OVERRIDE {
observer_list_.RemoveObserver(obs);
}
void RestoreContext() {
FOR_EACH_OBSERVER(StreamTextureFactoryContextObserver,
observer_list_,
ResetStreamTextureProxy());
}
private:
friend class base::RefCountedThreadSafe<VideoContextProvider>;
virtual ~VideoContextProvider() {}
scoped_refptr<cc::ContextProvider> context_provider_;
gpu::GLInProcessContext* gl_in_process_context_;
ObserverList<StreamTextureFactoryContextObserver> observer_list_;
DISALLOW_COPY_AND_ASSIGN(VideoContextProvider);
};
} // namespace
using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
SynchronousCompositorFactoryImpl::SynchronousCompositorFactoryImpl()
......@@ -216,6 +233,13 @@ SynchronousCompositorFactoryImpl::CreateOffscreenGraphicsContext3D(
void SynchronousCompositorFactoryImpl::CompositorInitializedHardwareDraw() {
base::AutoLock lock(num_hardware_compositor_lock_);
num_hardware_compositors_++;
if (num_hardware_compositors_ == 1 && main_thread_proxy_) {
main_thread_proxy_->PostTask(
FROM_HERE,
base::Bind(
&SynchronousCompositorFactoryImpl::RestoreContextOnMainThread,
base::Unretained(this)));
}
}
void SynchronousCompositorFactoryImpl::CompositorReleasedHardwareDraw() {
......@@ -224,6 +248,11 @@ void SynchronousCompositorFactoryImpl::CompositorReleasedHardwareDraw() {
num_hardware_compositors_--;
}
void SynchronousCompositorFactoryImpl::RestoreContextOnMainThread() {
if (CanCreateMainThreadContext() && video_context_provider_ )
video_context_provider_->RestoreContext();
}
bool SynchronousCompositorFactoryImpl::CanCreateMainThreadContext() {
base::AutoLock lock(num_hardware_compositor_lock_);
return num_hardware_compositors_ > 0;
......@@ -231,6 +260,11 @@ bool SynchronousCompositorFactoryImpl::CanCreateMainThreadContext() {
scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
SynchronousCompositorFactoryImpl::TryCreateStreamTextureFactory() {
{
base::AutoLock lock(num_hardware_compositor_lock_);
main_thread_proxy_ = base::MessageLoopProxy::current();
}
// Always fail creation even if |video_context_provider_| is not NULL.
// This is to avoid synchronous calls that may deadlock. Setting
// |video_context_provider_| to null is also not safe since it makes
......
......@@ -65,12 +65,14 @@ class SynchronousCompositorFactoryImpl : public SynchronousCompositorFactory {
bool CanCreateMainThreadContext();
scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
TryCreateStreamTextureFactory();
void RestoreContextOnMainThread();
SynchronousInputEventFilter synchronous_input_event_filter_;
scoped_refptr<gpu::InProcessCommandBuffer::Service> service_;
scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
video_context_provider_;
class VideoContextProvider;
scoped_refptr<VideoContextProvider> video_context_provider_;
bool record_full_layer_;
......@@ -78,6 +80,7 @@ class SynchronousCompositorFactoryImpl : public SynchronousCompositorFactory {
// read on renderer main thread.
base::Lock num_hardware_compositor_lock_;
unsigned int num_hardware_compositors_;
scoped_refptr<base::MessageLoopProxy> main_thread_proxy_;
};
} // namespace content
......
......@@ -7,6 +7,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop_proxy.h"
#include "cc/layers/video_frame_provider.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "ui/gfx/size.h"
......@@ -25,13 +26,12 @@ class StreamTextureProxy {
public:
virtual ~StreamTextureProxy() {}
// Initialize and bind to the current thread, which becomes the thread that
// a connected client will receive callbacks on.
virtual void BindToCurrentThread(int32 stream_id) = 0;
// Setting the target for callback when a frame is available. This function
// could be called on both the main thread and the compositor thread.
virtual void SetClient(cc::VideoFrameProvider::Client* client) = 0;
// Initialize and bind to the loop, which becomes the thread that
// a connected client will receive callbacks on. This can be called
// on any thread, but must be called with the same loop every time.
virtual void BindToLoop(int32 stream_id,
cc::VideoFrameProvider::Client* client,
scoped_refptr<base::MessageLoopProxy> loop) = 0;
// Causes this instance to be deleted on the thread it is bound to.
virtual void Release() = 0;
......@@ -44,6 +44,12 @@ class StreamTextureProxy {
typedef scoped_ptr<StreamTextureProxy, StreamTextureProxy::Deleter>
ScopedStreamTextureProxy;
class StreamTextureFactoryContextObserver {
public:
virtual ~StreamTextureFactoryContextObserver() {}
virtual void ResetStreamTextureProxy() = 0;
};
// Factory class for managing stream textures.
class StreamTextureFactory : public base::RefCounted<StreamTextureFactory> {
public:
......@@ -69,6 +75,9 @@ class StreamTextureFactory : public base::RefCounted<StreamTextureFactory> {
virtual gpu::gles2::GLES2Interface* ContextGL() = 0;
virtual void AddObserver(StreamTextureFactoryContextObserver* obs) = 0;
virtual void RemoveObserver(StreamTextureFactoryContextObserver* obs) = 0;
protected:
friend class base::RefCounted<StreamTextureFactory>;
virtual ~StreamTextureFactory() {}
......
......@@ -22,8 +22,9 @@ class StreamTextureProxyImpl : public StreamTextureProxy,
virtual ~StreamTextureProxyImpl();
// StreamTextureProxy implementation:
virtual void BindToCurrentThread(int32 stream_id) OVERRIDE;
virtual void SetClient(cc::VideoFrameProvider::Client* client) OVERRIDE;
virtual void BindToLoop(int32 stream_id,
cc::VideoFrameProvider::Client* client,
scoped_refptr<base::MessageLoopProxy> loop) OVERRIDE;
virtual void Release() OVERRIDE;
// StreamTextureHost::Listener implementation:
......@@ -31,7 +32,11 @@ class StreamTextureProxyImpl : public StreamTextureProxy,
virtual void OnMatrixChanged(const float matrix[16]) OVERRIDE;
private:
scoped_ptr<StreamTextureHost> host_;
void SetClient(cc::VideoFrameProvider::Client* client);
void BindOnThread(int32 stream_id,
scoped_refptr<base::MessageLoopProxy> loop);
const scoped_ptr<StreamTextureHost> host_;
scoped_refptr<base::MessageLoopProxy> loop_;
base::Lock client_lock_;
......@@ -46,11 +51,13 @@ StreamTextureProxyImpl::StreamTextureProxyImpl(StreamTextureHost* host)
StreamTextureProxyImpl::~StreamTextureProxyImpl() {}
void StreamTextureProxyImpl::Release() {
// Assumes this is the last reference to this object. So no need to acquire
// lock.
SetClient(NULL);
if (loop_.get() && loop_.get() != base::MessageLoopProxy::current())
loop_->DeleteSoon(FROM_HERE, this);
else
if (!loop_.get() || loop_->BelongsToCurrentThread() ||
!loop_->DeleteSoon(FROM_HERE, this)) {
delete this;
}
}
void StreamTextureProxyImpl::SetClient(cc::VideoFrameProvider::Client* client) {
......@@ -58,8 +65,30 @@ void StreamTextureProxyImpl::SetClient(cc::VideoFrameProvider::Client* client) {
client_ = client;
}
void StreamTextureProxyImpl::BindToCurrentThread(int stream_id) {
loop_ = base::MessageLoopProxy::current();
void StreamTextureProxyImpl::BindToLoop(
int32 stream_id,
cc::VideoFrameProvider::Client* client,
scoped_refptr<base::MessageLoopProxy> loop) {
DCHECK(loop);
SetClient(client);
if (loop->BelongsToCurrentThread()) {
BindOnThread(stream_id, loop);
return;
}
// Unretained is safe here only because the object is deleted on |loop_|
// thread.
loop->PostTask(FROM_HERE,
base::Bind(&StreamTextureProxyImpl::BindOnThread,
base::Unretained(this),
stream_id,
loop));
}
void StreamTextureProxyImpl::BindOnThread(
int32 stream_id,
scoped_refptr<base::MessageLoopProxy> loop) {
DCHECK(!loop_ || (loop == loop_));
loop_ = loop;
host_->BindToCurrentThread(stream_id, this);
}
......@@ -134,4 +163,12 @@ gpu::gles2::GLES2Interface* StreamTextureFactoryImpl::ContextGL() {
return context_provider_->ContextGL();
}
void StreamTextureFactoryImpl::AddObserver(
StreamTextureFactoryContextObserver* obs) {
}
void StreamTextureFactoryImpl::RemoveObserver(
StreamTextureFactoryContextObserver* obs) {
}
} // namespace content
......@@ -37,6 +37,9 @@ class StreamTextureFactoryImpl : public StreamTextureFactory {
virtual void SetStreamTextureSize(int32 texture_id,
const gfx::Size& size) OVERRIDE;
virtual gpu::gles2::GLES2Interface* ContextGL() OVERRIDE;
virtual void AddObserver(StreamTextureFactoryContextObserver* obs) OVERRIDE;
virtual void RemoveObserver(
StreamTextureFactoryContextObserver* obs) OVERRIDE;
private:
friend class base::RefCounted<StreamTextureFactoryImpl>;
......
......@@ -34,22 +34,26 @@ class StreamTextureProxyImpl
virtual ~StreamTextureProxyImpl();
// StreamTextureProxy implementation:
virtual void BindToCurrentThread(int32 stream_id) OVERRIDE;
virtual void SetClient(cc::VideoFrameProvider::Client* client) OVERRIDE;
virtual void BindToLoop(int32 stream_id,
cc::VideoFrameProvider::Client* client,
scoped_refptr<base::MessageLoopProxy> loop) OVERRIDE;
virtual void Release() OVERRIDE;
private:
void SetClient(cc::VideoFrameProvider::Client* client);
void BindOnThread(int32 stream_id,
scoped_refptr<base::MessageLoopProxy> loop);
void OnFrameAvailable();
scoped_refptr<base::MessageLoopProxy> loop_;
base::Lock client_lock_;
cc::VideoFrameProvider::Client* client_;
base::Closure callback_;
// Accessed on the |loop_| thread only.
scoped_refptr<base::MessageLoopProxy> loop_;
base::Closure callback_;
scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
context_provider_;
scoped_refptr<gfx::SurfaceTexture> surface_texture_;
float current_matrix_[16];
bool has_updated_;
......@@ -58,18 +62,20 @@ class StreamTextureProxyImpl
StreamTextureProxyImpl::StreamTextureProxyImpl(
StreamTextureFactorySynchronousImpl::ContextProvider* provider)
: context_provider_(provider), has_updated_(false) {
: client_(NULL), context_provider_(provider), has_updated_(false) {
std::fill(current_matrix_, current_matrix_ + 16, 0);
}
StreamTextureProxyImpl::~StreamTextureProxyImpl() {}
void StreamTextureProxyImpl::Release() {
// Assumes this is the last reference to this object. So no need to acquire
// lock.
SetClient(NULL);
if (loop_.get() && !loop_->BelongsToCurrentThread())
loop_->DeleteSoon(FROM_HERE, this);
else
if (!loop_.get() || loop_->BelongsToCurrentThread() ||
!loop_->DeleteSoon(FROM_HERE, this)) {
delete this;
}
}
void StreamTextureProxyImpl::SetClient(cc::VideoFrameProvider::Client* client) {
......@@ -77,8 +83,31 @@ void StreamTextureProxyImpl::SetClient(cc::VideoFrameProvider::Client* client) {
client_ = client;
}
void StreamTextureProxyImpl::BindToCurrentThread(int stream_id) {
loop_ = base::MessageLoopProxy::current();
void StreamTextureProxyImpl::BindToLoop(
int32 stream_id,
cc::VideoFrameProvider::Client* client,
scoped_refptr<base::MessageLoopProxy> loop) {
DCHECK(loop);
SetClient(client);
if (loop->BelongsToCurrentThread()) {
BindOnThread(stream_id, loop);
return;
}
// Unretained is safe here only because the object is deleted on |loop_|
// thread.
loop->PostTask(FROM_HERE,
base::Bind(&StreamTextureProxyImpl::BindOnThread,
base::Unretained(this),
stream_id,
loop));
}
void StreamTextureProxyImpl::BindOnThread(
int32 stream_id,
scoped_refptr<base::MessageLoopProxy> loop) {
DCHECK(!loop_ || (loop == loop_));
loop_ = loop;
surface_texture_ = context_provider_->GetSurfaceTexture(stream_id);
if (!surface_texture_) {
LOG(ERROR) << "Failed to get SurfaceTexture for stream.";
......@@ -130,7 +159,8 @@ StreamTextureFactorySynchronousImpl::StreamTextureFactorySynchronousImpl(
int frame_id)
: create_context_provider_callback_(try_create_callback),
context_provider_(create_context_provider_callback_.Run()),
frame_id_(frame_id) {}
frame_id_(frame_id),
observer_(NULL) {}
StreamTextureFactorySynchronousImpl::~StreamTextureFactorySynchronousImpl() {}
......@@ -140,6 +170,9 @@ StreamTextureProxy* StreamTextureFactorySynchronousImpl::CreateProxy() {
if (!context_provider_)
return NULL;
if (observer_)
context_provider_->AddObserver(observer_);
return new StreamTextureProxyImpl(context_provider_);
}
......@@ -182,4 +215,20 @@ gpu::gles2::GLES2Interface* StreamTextureFactorySynchronousImpl::ContextGL() {
return context_provider_->ContextGL();
}
void StreamTextureFactorySynchronousImpl::AddObserver(
StreamTextureFactoryContextObserver* obs) {
DCHECK(!observer_);
observer_ = obs;
if (context_provider_)
context_provider_->AddObserver(obs);
}
void StreamTextureFactorySynchronousImpl::RemoveObserver(
StreamTextureFactoryContextObserver* obs) {
DCHECK_EQ(observer_, obs);
observer_ = NULL;
if (context_provider_)
context_provider_->AddObserver(obs);
}
} // namespace content
......@@ -7,6 +7,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
#include "content/renderer/media/android/stream_texture_factory.h"
namespace gfx {
......@@ -31,6 +32,9 @@ class StreamTextureFactorySynchronousImpl : public StreamTextureFactory {
virtual gpu::gles2::GLES2Interface* ContextGL() = 0;
virtual void AddObserver(StreamTextureFactoryContextObserver* obs) = 0;
virtual void RemoveObserver(StreamTextureFactoryContextObserver* obs) = 0;
protected:
friend class base::RefCountedThreadSafe<ContextProvider>;
virtual ~ContextProvider() {}
......@@ -51,6 +55,9 @@ class StreamTextureFactorySynchronousImpl : public StreamTextureFactory {
virtual void SetStreamTextureSize(int32 stream_id,
const gfx::Size& size) OVERRIDE;
virtual gpu::gles2::GLES2Interface* ContextGL() OVERRIDE;
virtual void AddObserver(StreamTextureFactoryContextObserver* obs) OVERRIDE;
virtual void RemoveObserver(
StreamTextureFactoryContextObserver* obs) OVERRIDE;
private:
friend class base::RefCounted<StreamTextureFactorySynchronousImpl>;
......@@ -62,6 +69,7 @@ class StreamTextureFactorySynchronousImpl : public StreamTextureFactory {
CreateContextProviderCallback create_context_provider_callback_;
scoped_refptr<ContextProvider> context_provider_;
int frame_id_;
StreamTextureFactoryContextObserver* observer_;
DISALLOW_IMPLICIT_CONSTRUCTORS(StreamTextureFactorySynchronousImpl);
};
......
......@@ -136,8 +136,9 @@ WebMediaPlayerAndroid::WebMediaPlayerAndroid(
stream_id_(0),
is_playing_(false),
needs_establish_peer_(true),
stream_texture_proxy_initialized_(false),
has_size_info_(false),
compositor_loop_(
RenderThreadImpl::current()->compositor_message_loop_proxy()),
stream_texture_factory_(factory),
needs_external_surface_(false),
has_valid_metadata_(false),
......@@ -155,6 +156,7 @@ WebMediaPlayerAndroid::WebMediaPlayerAndroid(
DCHECK(cdm_manager_);
DCHECK(main_thread_checker_.CalledOnValidThread());
stream_texture_factory_->AddObserver(this);
player_id_ = player_manager_->RegisterMediaPlayer(this);
......@@ -197,6 +199,8 @@ WebMediaPlayerAndroid::~WebMediaPlayerAndroid() {
if (delegate_)
delegate_->PlayerGone(this);
stream_texture_factory_->RemoveObserver(this);
}
void WebMediaPlayerAndroid::load(LoadType load_type,
......@@ -1214,22 +1218,15 @@ void WebMediaPlayerAndroid::SetVideoFrameProviderClient(
cc::VideoFrameProvider::Client* client) {
// This is called from both the main renderer thread and the compositor
// thread (when the main thread is blocked).
if (video_frame_provider_client_)
// Set the callback target when a frame is produced. Need to do this before
// StopUsingProvider to ensure we really stop using the client.
if (stream_texture_proxy_)
stream_texture_proxy_->BindToLoop(stream_id_, client, compositor_loop_);
if (video_frame_provider_client_ && video_frame_provider_client_ != client)
video_frame_provider_client_->StopUsingProvider();
video_frame_provider_client_ = client;
// Set the callback target when a frame is produced.
if (stream_texture_proxy_) {
stream_texture_proxy_->SetClient(client);
// If client exists, the compositor thread calls it. At that time,
// stream_id_, needs_external_surface_, is_remote_ can be accessed because
// the main thread is blocked.
if (client && !stream_texture_proxy_initialized_ && stream_id_ &&
!needs_external_surface_ && !is_remote_) {
stream_texture_proxy_->BindToCurrentThread(stream_id_);
stream_texture_proxy_initialized_ = true;
}
}
}
void WebMediaPlayerAndroid::SetCurrentFrameInternal(
......@@ -1253,6 +1250,26 @@ void WebMediaPlayerAndroid::PutCurrentFrame(
const scoped_refptr<media::VideoFrame>& frame) {
}
void WebMediaPlayerAndroid::ResetStreamTextureProxy() {
DCHECK(main_thread_checker_.CalledOnValidThread());
if (stream_id_) {
GLES2Interface* gl = stream_texture_factory_->ContextGL();
gl->DeleteTextures(1, &texture_id_);
texture_id_ = 0;
texture_mailbox_ = gpu::Mailbox();
stream_id_ = 0;
}
stream_texture_proxy_.reset();
needs_establish_peer_ = !needs_external_surface_ && !is_remote_ &&
!player_manager_->IsInFullscreen(frame_) &&
(hasVideo() || IsHLSStream());
TryCreateStreamTextureProxyIfNeeded();
if (needs_establish_peer_ && is_playing_)
EstablishSurfaceTexturePeer();
}
void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() {
DCHECK(main_thread_checker_.CalledOnValidThread());
// Already created.
......@@ -1263,14 +1280,19 @@ void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() {
if (!stream_texture_factory_)
return;
// Not needed for hole punching.
if (!needs_establish_peer_)
return;
stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy());
if (needs_establish_peer_ && stream_texture_proxy_) {
if (stream_texture_proxy_) {
DoCreateStreamTexture();
ReallocateVideoFrame();
if (video_frame_provider_client_) {
stream_texture_proxy_->BindToLoop(
stream_id_, video_frame_provider_client_, compositor_loop_);
}
}
if (stream_texture_proxy_ && video_frame_provider_client_)
stream_texture_proxy_->SetClient(video_frame_provider_client_);
}
void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() {
......
......@@ -72,7 +72,8 @@ class WebContentDecryptionModuleImpl;
// player.
class WebMediaPlayerAndroid : public blink::WebMediaPlayer,
public cc::VideoFrameProvider,
public RenderFrameObserver {
public RenderFrameObserver,
public StreamTextureFactoryContextObserver {
public:
// Construct a WebMediaPlayerAndroid object. This class communicates with the
// MediaPlayerAndroid object in the browser process through |proxy|.
......@@ -199,6 +200,9 @@ class WebMediaPlayerAndroid : public blink::WebMediaPlayer,
void OnMediaPlayerPause();
void OnRequestFullscreen();
// StreamTextureFactoryContextObserver implementation.
virtual void ResetStreamTextureProxy() OVERRIDE;
// Called when the player is released.
virtual void OnPlayerReleased();
......@@ -413,17 +417,18 @@ class WebMediaPlayerAndroid : public blink::WebMediaPlayer,
// Whether media player needs to re-establish the surface texture peer.
bool needs_establish_peer_;
// Whether |stream_texture_proxy_| is initialized.
bool stream_texture_proxy_initialized_;
// Whether the video size info is available.
bool has_size_info_;
const scoped_refptr<base::MessageLoopProxy> compositor_loop_;
// Object for allocating stream textures.
scoped_refptr<StreamTextureFactory> stream_texture_factory_;
// Object for calling back the compositor thread to repaint the video when a
// frame available. It should be initialized on the compositor thread.
// Accessed on main thread and on compositor thread when main thread is
// blocked.
ScopedStreamTextureProxy stream_texture_proxy_;
// Whether media player needs external surface.
......@@ -435,6 +440,8 @@ class WebMediaPlayerAndroid : public blink::WebMediaPlayer,
// A pointer back to the compositor to inform it about state changes. This is
// not NULL while the compositor is actively using this webmediaplayer.
// Accessed on main thread and on compositor thread when main thread is
// blocked.
cc::VideoFrameProvider::Client* video_frame_provider_client_;
scoped_ptr<cc_blink::WebLayerImpl> video_weblayer_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment