diff options
Diffstat (limited to 'chrome/renderer')
-rw-r--r-- | chrome/renderer/media/ipc_video_renderer.cc | 183 | ||||
-rw-r--r-- | chrome/renderer/media/ipc_video_renderer.h | 108 | ||||
-rw-r--r-- | chrome/renderer/render_view.cc | 12 | ||||
-rw-r--r-- | chrome/renderer/render_widget.cc | 10 | ||||
-rw-r--r-- | chrome/renderer/render_widget.h | 2 |
5 files changed, 314 insertions, 1 deletions
diff --git a/chrome/renderer/media/ipc_video_renderer.cc b/chrome/renderer/media/ipc_video_renderer.cc new file mode 100644 index 0000000..0a98489 --- /dev/null +++ b/chrome/renderer/media/ipc_video_renderer.cc @@ -0,0 +1,183 @@ +// Copyright (c) 2010 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. + +#include "chrome/renderer/media/ipc_video_renderer.h" + +#include "chrome/common/render_messages.h" +#include "chrome/renderer/render_thread.h" +#include "media/base/buffers.h" +#include "media/base/media_format.h" + +IPCVideoRenderer::IPCVideoRenderer( + webkit_glue::WebMediaPlayerImpl::Proxy* proxy, + int routing_id) + : proxy_(proxy), + created_(false), + routing_id_(routing_id), + stopped_(false, false) { + // TODO(hclam): decide whether to do the following line in this thread or + // in the render thread. + proxy->SetVideoRenderer(this); +} + +IPCVideoRenderer::~IPCVideoRenderer() { +} + +// static +media::FilterFactory* IPCVideoRenderer::CreateFactory( + webkit_glue::WebMediaPlayerImpl::Proxy* proxy, + int routing_id) { + return new media::FilterFactoryImpl2< + IPCVideoRenderer, + webkit_glue::WebMediaPlayerImpl::Proxy*, + int>(proxy, routing_id); +} + +// static +bool IPCVideoRenderer::IsMediaFormatSupported( + const media::MediaFormat& media_format) { + int width = 0; + int height = 0; + return ParseMediaFormat(media_format, &width, &height); +} + +bool IPCVideoRenderer::OnInitialize(media::VideoDecoder* decoder) { + int width = 0; + int height = 0; + if (!ParseMediaFormat(decoder->media_format(), &width, &height)) + return false; + + video_size_.SetSize(width, height); + + // TODO(scherkus): we're assuming YV12 here. + size_t size = (width * height) + ((width * height) >> 1); + uint32 epoch = static_cast<uint32>(reinterpret_cast<size_t>(this)); + transport_dib_.reset(TransportDIB::Create(size, epoch)); + CHECK(transport_dib_.get()); + + return true; +} + +void IPCVideoRenderer::OnStop() { + stopped_.Signal(); + + proxy_->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(this, &IPCVideoRenderer::DoDestroyVideo)); +} + +void IPCVideoRenderer::OnFrameAvailable() { + proxy_->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(this, &IPCVideoRenderer::DoUpdateVideo)); +} + +void IPCVideoRenderer::SetRect(const gfx::Rect& rect) { + DCHECK(MessageLoop::current() == proxy_->message_loop()); + + // TODO(scherkus): this is actually a SetSize() call... there's a pending + // WebKit bug to get this fixed up. It would be nice if this was a real + // SetRect() call so we could get the absolute coordinates instead of relying + // on Paint(). +} + +void IPCVideoRenderer::Paint(skia::PlatformCanvas* canvas, + const gfx::Rect& dest_rect) { + DCHECK(MessageLoop::current() == proxy_->message_loop()); + + // Copy the rect for UpdateVideo messages. + video_rect_ = dest_rect; + + if (!created_) { + created_ = true; + Send(new ViewHostMsg_CreateVideo(routing_id_, video_size_)); + + // Force an update in case the first frame arrived before the first + // Paint() call. + DoUpdateVideo(); + } + + // TODO(scherkus): code to punch a hole through the backing store goes here. + // We don't need it right away since we don't do a proper alpha composite + // between the browser BackingStore and VideoLayer. +} + +void IPCVideoRenderer::Send(IPC::Message* msg) { + DCHECK(routing_id_ != MSG_ROUTING_NONE); + DCHECK(routing_id_ == msg->routing_id()); + + bool result = RenderThread::current()->Send(msg); + LOG_IF(ERROR, !result) << "RenderThread::current()->Send(msg) failed"; +} + +void IPCVideoRenderer::DoUpdateVideo() { + DCHECK(MessageLoop::current() == proxy_->message_loop()); + + // Nothing to do if we don't know where we are positioned on the page. + if (!created_ || video_rect_.IsEmpty() || stopped_.IsSignaled()) { + return; + } + + scoped_refptr<media::VideoFrame> frame; + GetCurrentFrame(&frame); + if (!frame) { + return; + } + + media::VideoSurface surface; + CHECK(frame->Lock(&surface)); + CHECK(surface.width == static_cast<size_t>(video_size_.width())); + CHECK(surface.height == static_cast<size_t>(video_size_.height())); + CHECK(surface.format == media::VideoSurface::YV12); + CHECK(surface.planes == 3); + + uint8* dest = reinterpret_cast<uint8*>(transport_dib_->memory()); + + // Copy Y plane. + const uint8* src = surface.data[media::VideoSurface::kYPlane]; + size_t stride = surface.strides[media::VideoSurface::kYPlane]; + for (size_t row = 0; row < surface.height; ++row) { + memcpy(dest, src, surface.width); + dest += surface.width; + src += stride; + } + + // Copy U plane. + src = surface.data[media::VideoSurface::kUPlane]; + stride = surface.strides[media::VideoSurface::kUPlane]; + for (size_t row = 0; row < surface.height / 2; ++row) { + memcpy(dest, src, surface.width / 2); + dest += surface.width / 2; + src += stride; + } + + // Copy V plane. + src = surface.data[media::VideoSurface::kVPlane]; + stride = surface.strides[media::VideoSurface::kVPlane]; + for (size_t row = 0; row < surface.height / 2; ++row) { + memcpy(dest, src, surface.width / 2); + dest += surface.width / 2; + src += stride; + } + + // Sanity check! + uint8* expected = reinterpret_cast<uint8*>(transport_dib_->memory()) + + transport_dib_->size(); + CHECK(dest == expected); + + Send(new ViewHostMsg_UpdateVideo(routing_id_, + transport_dib_->id(), + video_rect_)); + + frame->Unlock(); +} + +void IPCVideoRenderer::DoDestroyVideo() { + DCHECK(MessageLoop::current() == proxy_->message_loop()); + + // We shouldn't receive any more messages after the browser receives this. + Send(new ViewHostMsg_DestroyVideo(routing_id_)); + + // Detach ourselves from the proxy. + proxy_->SetVideoRenderer(NULL); + proxy_ = NULL; +} diff --git a/chrome/renderer/media/ipc_video_renderer.h b/chrome/renderer/media/ipc_video_renderer.h new file mode 100644 index 0000000..839df03 --- /dev/null +++ b/chrome/renderer/media/ipc_video_renderer.h @@ -0,0 +1,108 @@ +// Copyright (c) 2010 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. + +// This video renderer implementation uses IPC to signal the browser process to +// composite the video as a separate layer underneath the backing store. +// +// Extremely experimental. Use at own risk! + +#ifndef CHROME_RENDERER_MEDIA_IPC_VIDEO_RENDERER_H_ +#define CHROME_RENDERER_MEDIA_IPC_VIDEO_RENDERER_H_ + +#include "base/gfx/rect.h" +#include "base/gfx/size.h" +#include "base/waitable_event.h" +#include "chrome/common/transport_dib.h" +#include "media/filters/video_renderer_base.h" +#include "webkit/glue/media/web_video_renderer.h" +#include "webkit/glue/webmediaplayer_impl.h" + +class IPCVideoRenderer : public webkit_glue::WebVideoRenderer { + public: + // WebVideoRenderer implementation. + virtual void SetRect(const gfx::Rect& rect); + virtual void Paint(skia::PlatformCanvas* canvas, const gfx::Rect& dest_rect); + + void OnUpdateVideo(); + void OnUpdateVideoAck(); + void OnDestroyVideo(); + + // Static method for creating factory for this object. + static media::FilterFactory* CreateFactory( + webkit_glue::WebMediaPlayerImpl::Proxy* proxy, + int routing_id); + + // FilterFactoryImpl2 implementation. + static bool IsMediaFormatSupported(const media::MediaFormat& media_format); + + // TODO(scherkus): remove this mega-hack, see http://crbug.com/28207 + class FactoryFactory : public webkit_glue::WebVideoRendererFactoryFactory { + public: + FactoryFactory(int routing_id) + : webkit_glue::WebVideoRendererFactoryFactory(), + routing_id_(routing_id) { + } + + virtual media::FilterFactory* CreateFactory( + webkit_glue::WebMediaPlayerImpl::Proxy* proxy) { + return IPCVideoRenderer::CreateFactory(proxy, routing_id_); + } + + private: + int routing_id_; + + DISALLOW_COPY_AND_ASSIGN(FactoryFactory); + }; + + protected: + // VideoRendererBase implementation. + virtual bool OnInitialize(media::VideoDecoder* decoder); + virtual void OnStop(); + virtual void OnFrameAvailable(); + + private: + // Only the filter factories can create instances. + friend class media::FilterFactoryImpl2< + IPCVideoRenderer, + webkit_glue::WebMediaPlayerImpl::Proxy*, + int>; + IPCVideoRenderer(webkit_glue::WebMediaPlayerImpl::Proxy* proxy, + int routing_id); + virtual ~IPCVideoRenderer(); + + // Send an IPC message to the browser process. The routing ID of the message + // is assumed to match |routing_id_|. + void Send(IPC::Message* msg); + + // Handles updating the video on the render thread. + void DoUpdateVideo(); + + // Handles destroying the video on the render thread. + void DoDestroyVideo(); + + // Pointer to our parent object that is called to request repaints. + scoped_refptr<webkit_glue::WebMediaPlayerImpl::Proxy> proxy_; + + // The size of the video. + gfx::Size video_size_; + + // The rect of the video. + gfx::Rect video_rect_; + + // Whether we've created the video layer on the browser. + bool created_; + + // Used to transporting YUV to the browser process. + int routing_id_; + scoped_ptr<TransportDIB> transport_dib_; + + // Used to determine whether we've been instructed to stop. + // TODO(scherkus): work around because we don't have asynchronous stopping. + // Refer to http://crbug.com/16059 + base::WaitableEvent stopped_; + + DISALLOW_COPY_AND_ASSIGN(IPCVideoRenderer); +}; + +#endif // CHROME_RENDERER_MEDIA_IPC_VIDEO_RENDERER_H_ diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 03c0850..b3ef04b 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -47,6 +47,7 @@ #include "chrome/renderer/geolocation_dispatcher.h" #include "chrome/renderer/localized_error.h" #include "chrome/renderer/media/audio_renderer_impl.h" +#include "chrome/renderer/media/ipc_video_renderer.h" #include "chrome/renderer/navigation_state.h" #include "chrome/renderer/notification_provider.h" #include "chrome/renderer/plugin_channel_host.h" @@ -110,6 +111,7 @@ #include "webkit/glue/password_form.h" #include "webkit/glue/plugins/plugin_list.h" #include "webkit/glue/plugins/webplugin_delegate_impl.h" +#include "webkit/glue/media/video_renderer_impl.h" #include "webkit/glue/webdropdata.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/webmediaplayer_impl.h" @@ -2140,7 +2142,15 @@ WebMediaPlayer* RenderView::createMediaPlayer( factory->AddFactory(buffered_data_source_factory); factory->AddFactory(simple_data_source_factory); } - return new webkit_glue::WebMediaPlayerImpl(client, factory); + + webkit_glue::WebVideoRendererFactoryFactory* factory_factory = NULL; + if (cmd_line->HasSwitch(switches::kEnableVideoLayering)) { + factory_factory = new IPCVideoRenderer::FactoryFactory(routing_id_); + } else { + factory_factory = new webkit_glue::VideoRendererImpl::FactoryFactory(); + } + + return new webkit_glue::WebMediaPlayerImpl(client, factory, factory_factory); } WebCookieJar* RenderView::cookieJar() { diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc index 5212635..75cd8d2 100644 --- a/chrome/renderer/render_widget.cc +++ b/chrome/renderer/render_widget.cc @@ -140,6 +140,8 @@ IPC_DEFINE_MESSAGE_MAP(RenderWidget) IPC_MESSAGE_HANDLER(ViewMsg_WasHidden, OnWasHidden) IPC_MESSAGE_HANDLER(ViewMsg_WasRestored, OnWasRestored) IPC_MESSAGE_HANDLER(ViewMsg_UpdateRect_ACK, OnUpdateRectAck) + IPC_MESSAGE_HANDLER(ViewMsg_CreateVideo_ACK, OnCreateVideoAck) + IPC_MESSAGE_HANDLER(ViewMsg_UpdateVideo_ACK, OnUpdateVideoAck) IPC_MESSAGE_HANDLER(ViewMsg_HandleInputEvent, OnHandleInputEvent) IPC_MESSAGE_HANDLER(ViewMsg_MouseCaptureLost, OnMouseCaptureLost) IPC_MESSAGE_HANDLER(ViewMsg_SetFocus, OnSetFocus) @@ -277,6 +279,14 @@ void RenderWidget::OnUpdateRectAck() { CallDoDeferredUpdate(); } +void RenderWidget::OnCreateVideoAck(int32 video_id) { + // TODO(scherkus): handle CreateVideo_ACK with a message filter. +} + +void RenderWidget::OnUpdateVideoAck(int32 video_id) { + // TODO(scherkus): handle UpdateVideo_ACK with a message filter. +} + void RenderWidget::OnHandleInputEvent(const IPC::Message& message) { void* iter = NULL; diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h index 571ca8b..feb3efe 100644 --- a/chrome/renderer/render_widget.h +++ b/chrome/renderer/render_widget.h @@ -151,6 +151,8 @@ class RenderWidget : public IPC::Channel::Listener, virtual void OnWasHidden(); virtual void OnWasRestored(bool needs_repainting); void OnUpdateRectAck(); + void OnCreateVideoAck(int32 video_id); + void OnUpdateVideoAck(int32 video_id); void OnRequestMoveAck(); void OnHandleInputEvent(const IPC::Message& message); void OnMouseCaptureLost(); |