summaryrefslogtreecommitdiffstats
path: root/ui/surface/accelerated_surface_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ui/surface/accelerated_surface_win.cc')
-rw-r--r--ui/surface/accelerated_surface_win.cc182
1 files changed, 170 insertions, 12 deletions
diff --git a/ui/surface/accelerated_surface_win.cc b/ui/surface/accelerated_surface_win.cc
index 8523493..a3ecb03 100644
--- a/ui/surface/accelerated_surface_win.cc
+++ b/ui/surface/accelerated_surface_win.cc
@@ -21,8 +21,9 @@
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
-#include "base/time.h"
#include "base/win/wrapped_window_proc.h"
+#include "media/base/video_frame.h"
+#include "media/base/video_util.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/win/dpi.h"
#include "ui/base/win/hwnd_util.h"
@@ -54,6 +55,49 @@ bool DoAllShowPresentWithGDI() {
switches::kDoAllShowPresentWithGDI);
}
+// Lock a D3D surface, and invoke a VideoFrame copier on the result.
+bool LockAndCopyPlane(IDirect3DSurface9* src_surface,
+ media::VideoFrame* dst_frame,
+ size_t plane_id) {
+ gfx::Size src_size = d3d_utils::GetSize(src_surface);
+
+ D3DLOCKED_RECT locked_rect;
+ {
+ TRACE_EVENT0("gpu", "LockRect");
+ HRESULT hr = src_surface->LockRect(&locked_rect, NULL,
+ D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK);
+ if (FAILED(hr))
+ return false;
+ }
+
+ {
+ TRACE_EVENT0("gpu", "memcpy");
+ uint8* src = reinterpret_cast<uint8*>(locked_rect.pBits);
+ int src_stride = locked_rect.Pitch;
+ media::CopyPlane(plane_id, src, src_stride, src_size.height(), dst_frame);
+ }
+ src_surface->UnlockRect();
+ return true;
+}
+
+// Return the largest centered rectangle with the same aspect ratio of |content|
+// that fits entirely inside of |bounds|.
+gfx::Rect ComputeLetterboxRegion(
+ const gfx::Rect& bounds,
+ const gfx::Size& content) {
+ int64 x = static_cast<int64>(content.width()) * bounds.height();
+ int64 y = static_cast<int64>(bounds.width()) * content.height();
+
+ gfx::Size letterbox(bounds.width(), bounds.height());
+ if (y < x)
+ letterbox.set_height(static_cast<int>(y / content.width()));
+ else
+ letterbox.set_width(static_cast<int>(x / content.height()));
+ gfx::Rect result = bounds;
+ result.ClampToCenteredSize(letterbox);
+ return result;
+}
+
} // namespace
// A PresentThread is a thread that is dedicated to presenting surfaces to a
@@ -318,24 +362,45 @@ void AcceleratedPresenter::AsyncCopyTo(
callback));
}
+void AcceleratedPresenter::AsyncCopyToVideoFrame(
+ const gfx::Rect& requested_src_subrect,
+ const scoped_refptr<media::VideoFrame>& target,
+ const base::Callback<void(bool)>& callback) {
+ present_thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge,
+ this,
+ requested_src_subrect,
+ target,
+ base::MessageLoopProxy::current(),
+ callback));
+}
+
void AcceleratedPresenter::DoCopyToAndAcknowledge(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
scoped_refptr<base::SingleThreadTaskRunner> callback_runner,
const base::Callback<void(bool, const SkBitmap&)>& callback) {
-
SkBitmap target;
- bool result = DoCopyTo(src_subrect, dst_size, &target);
+ bool result = DoCopyToARGB(src_subrect, dst_size, &target);
if (!result)
target.reset();
- callback_runner->PostTask(
- FROM_HERE,
- base::Bind(callback, result, target));
+ callback_runner->PostTask(FROM_HERE, base::Bind(callback, result, target));
}
-bool AcceleratedPresenter::DoCopyTo(const gfx::Rect& requested_src_subrect,
- const gfx::Size& dst_size,
- SkBitmap* bitmap) {
+void AcceleratedPresenter::DoCopyToVideoFrameAndAcknowledge(
+ const gfx::Rect& src_subrect,
+ const scoped_refptr<media::VideoFrame>& target,
+ const scoped_refptr<base::SingleThreadTaskRunner>& callback_runner,
+ const base::Callback<void(bool)>& callback) {
+
+ bool result = DoCopyToYUV(src_subrect, target);
+ callback_runner->PostTask(FROM_HERE, base::Bind(callback, result));
+}
+
+bool AcceleratedPresenter::DoCopyToARGB(const gfx::Rect& requested_src_subrect,
+ const gfx::Size& dst_size,
+ SkBitmap* bitmap) {
TRACE_EVENT2(
"gpu", "CopyTo",
"width", dst_size.width(),
@@ -343,8 +408,6 @@ bool AcceleratedPresenter::DoCopyTo(const gfx::Rect& requested_src_subrect,
base::AutoLock locked(lock_);
- TRACE_EVENT0("gpu", "CopyTo_locked");
-
if (!swap_chain_)
return false;
@@ -389,7 +452,8 @@ bool AcceleratedPresenter::DoCopyTo(const gfx::Rect& requested_src_subrect,
{
// Let the surface transformer start the resize into |final_surface|.
TRACE_EVENT0("gpu", "ResizeBilinear");
- if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, final_surface)) {
+ if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect,
+ final_surface, gfx::Rect(dst_size))) {
LOG(ERROR) << "Failed to resize bilinear";
return false;
}
@@ -430,6 +494,93 @@ bool AcceleratedPresenter::DoCopyTo(const gfx::Rect& requested_src_subrect,
return true;
}
+bool AcceleratedPresenter::DoCopyToYUV(
+ const gfx::Rect& requested_src_subrect,
+ const scoped_refptr<media::VideoFrame>& frame) {
+ gfx::Size dst_size = frame->coded_size();
+ TRACE_EVENT2(
+ "gpu", "CopyToYUV",
+ "width", dst_size.width(),
+ "height", dst_size.height());
+
+ base::AutoLock locked(lock_);
+
+ if (!swap_chain_)
+ return false;
+
+ AcceleratedSurfaceTransformer* gpu_ops =
+ present_thread_->surface_transformer();
+
+ base::win::ScopedComPtr<IDirect3DSurface9> back_buffer;
+ HRESULT hr = swap_chain_->GetBackBuffer(0,
+ D3DBACKBUFFER_TYPE_MONO,
+ back_buffer.Receive());
+ if (FAILED(hr))
+ return false;
+
+ D3DSURFACE_DESC desc;
+ hr = back_buffer->GetDesc(&desc);
+ if (FAILED(hr))
+ return false;
+
+ const gfx::Size back_buffer_size(desc.Width, desc.Height);
+ if (back_buffer_size.IsEmpty())
+ return false;
+
+ // With window resizing, it's possible that the back buffer is smaller than
+ // the requested src subset. Clip to the actual back buffer.
+ gfx::Rect src_subrect = requested_src_subrect;
+ src_subrect.Intersect(gfx::Rect(back_buffer_size));
+
+ base::win::ScopedComPtr<IDirect3DTexture9> resized_as_texture;
+ base::win::ScopedComPtr<IDirect3DSurface9> resized;
+ {
+ TRACE_EVENT0("gpu", "CreateTemporaryRenderTargetTexture");
+ if (!d3d_utils::CreateTemporaryRenderTargetTexture(
+ present_thread_->device(),
+ dst_size,
+ resized_as_texture.Receive(),
+ resized.Receive())) {
+ return false;
+ }
+ }
+
+ // Shrink the source to fit entirely in the destination while preserving
+ // aspect ratio. Fill in any margin with black.
+ // TODO(nick): It would be more efficient all around to implement
+ // letterboxing as a memset() on the dst.
+ gfx::Rect letterbox = ComputeLetterboxRegion(gfx::Rect(dst_size),
+ src_subrect.size());
+ if (letterbox != gfx::Rect(dst_size)) {
+ TRACE_EVENT0("gpu", "Letterbox");
+ present_thread_->device()->ColorFill(resized, NULL, 0xFF000000);
+ }
+
+ {
+ TRACE_EVENT0("gpu", "ResizeBilinear");
+ if (!gpu_ops->ResizeBilinear(back_buffer, src_subrect, resized, letterbox))
+ return false;
+ }
+
+ base::win::ScopedComPtr<IDirect3DSurface9> y, u, v;
+ {
+ TRACE_EVENT0("gpu", "TransformRGBToYV12");
+ if (!gpu_ops->TransformRGBToYV12(resized_as_texture,
+ dst_size,
+ y.Receive(), u.Receive(), v.Receive())) {
+ return false;
+ }
+ }
+
+ if (!LockAndCopyPlane(y, frame, media::VideoFrame::kYPlane))
+ return false;
+ if (!LockAndCopyPlane(u, frame, media::VideoFrame::kUPlane))
+ return false;
+ if (!LockAndCopyPlane(v, frame, media::VideoFrame::kVPlane))
+ return false;
+ return true;
+}
+
void AcceleratedPresenter::Suspend() {
present_thread_->message_loop()->PostTask(
FROM_HERE,
@@ -859,6 +1010,13 @@ void AcceleratedSurface::AsyncCopyTo(
presenter_->AsyncCopyTo(src_subrect, dst_size, callback);
}
+void AcceleratedSurface::AsyncCopyToVideoFrame(
+ const gfx::Rect& src_subrect,
+ const scoped_refptr<media::VideoFrame>& target,
+ const base::Callback<void(bool)>& callback) {
+ presenter_->AsyncCopyToVideoFrame(src_subrect, target, callback);
+}
+
void AcceleratedSurface::Suspend() {
presenter_->Suspend();
}