Direct3D renderer for new VideoEngine API tests.

TEST=Rendered video in video_loopback test.
BUG=
R=stefan@webrtc.org, tommi@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/1573004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4323 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/video_engine/test/common/run_loop.cc b/webrtc/video_engine/test/common/run_loop.cc
new file mode 100644
index 0000000..730071d
--- /dev/null
+++ b/webrtc/video_engine/test/common/run_loop.cc
@@ -0,0 +1,22 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webrtc/video_engine/test/common/run_loop.h"
+
+#include <stdio.h>
+
+namespace webrtc {
+namespace test {
+
+void PressEnterToContinue() {
+  puts(">> Press ENTER to continue...");
+  while (getchar() != '\n' && !feof(stdin));
+}
+}  // namespace test
+}  // namespace webrtc
diff --git a/webrtc/video_engine/test/common/run_loop.h b/webrtc/video_engine/test/common/run_loop.h
new file mode 100644
index 0000000..3101252
--- /dev/null
+++ b/webrtc/video_engine/test/common/run_loop.h
@@ -0,0 +1,22 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef WEBRTC_VIDEO_ENGINE_TEST_COMMON_RUN_LOOP_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_RUN_LOOP_H_
+
+namespace webrtc {
+namespace test {
+
+// Blocks until the user presses enter.
+void PressEnterToContinue();
+
+}  // namespace test
+}  // namespace webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_RUN_LOOP_H_
diff --git a/webrtc/video_engine/test/common/run_tests.cc b/webrtc/video_engine/test/common/run_tests.cc
index 607a1e5..4692ba6 100644
--- a/webrtc/video_engine/test/common/run_tests.cc
+++ b/webrtc/video_engine/test/common/run_tests.cc
@@ -7,7 +7,6 @@
  *  in the file PATENTS.  All contributing project authors may
  *  be found in the AUTHORS file in the root of the source tree.
  */
-
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace webrtc {
diff --git a/webrtc/video_engine/test/common/run_tests.h b/webrtc/video_engine/test/common/run_tests.h
index e7a685a..3f42866 100644
--- a/webrtc/video_engine/test/common/run_tests.h
+++ b/webrtc/video_engine/test/common/run_tests.h
@@ -7,17 +7,18 @@
  *  in the file PATENTS.  All contributing project authors may
  *  be found in the AUTHORS file in the root of the source tree.
  */
-
 #ifndef WEBRTC_VIDEO_ENGINE_TEST_COMMON_TEST_RUNNER_H_
 #define WEBRTC_VIDEO_ENGINE_TEST_COMMON_TEST_RUNNER_H_
 
 namespace webrtc {
 namespace test {
 
+// Blocks until the user presses enter.
+void PressEnterToContinue();
+
 // Performs platform-dependent initializations and calls gtest's
 // RUN_ALL_TESTS().
-  int RunAllTests();
-
+int RunAllTests();
 }  // namespace test
 }  // namespace webrtc
 
diff --git a/webrtc/video_engine/test/common/video_renderer.cc b/webrtc/video_engine/test/common/video_renderer.cc
index 62ede39..84aab89 100644
--- a/webrtc/video_engine/test/common/video_renderer.cc
+++ b/webrtc/video_engine/test/common/video_renderer.cc
@@ -7,10 +7,8 @@
  *  in the file PATENTS.  All contributing project authors may
  *  be found in the AUTHORS file in the root of the source tree.
  */
-
 #include "webrtc/video_engine/test/common/video_renderer.h"
 
-// TODO(pbos): Windows renderer
 // TODO(pbos): Android renderer
 
 #include "webrtc/typedefs.h"
@@ -23,14 +21,16 @@
                            int time_to_render_ms) OVERRIDE {}
 };
 
-VideoRenderer* VideoRenderer::Create(const char* window_title, size_t width,
+VideoRenderer* VideoRenderer::Create(const char* window_title,
+                                     size_t width,
                                      size_t height) {
   VideoRenderer* renderer = CreatePlatformRenderer(window_title, width, height);
   if (renderer != NULL) {
     // TODO(mflodman) Add a warning log.
     return renderer;
   }
+
   return new NullRenderer();
 }
-}  // test
-}  // webrtc
+}  // namespace test
+}  // namespace webrtc
diff --git a/webrtc/video_engine/test/common/video_renderer.h b/webrtc/video_engine/test/common/video_renderer.h
index 2d44103..ac94522 100644
--- a/webrtc/video_engine/test/common/video_renderer.h
+++ b/webrtc/video_engine/test/common/video_renderer.h
@@ -34,7 +34,7 @@
  protected:
   VideoRenderer() {}
 };
-}  // test
-}  // webrtc
+}  // namespace test
+}  // namespace webrtc
 
 #endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_RENDERER_H_
diff --git a/webrtc/video_engine/test/common/win/d3d_renderer.cc b/webrtc/video_engine/test/common/win/d3d_renderer.cc
new file mode 100644
index 0000000..2f9b29f
--- /dev/null
+++ b/webrtc/video_engine/test/common/win/d3d_renderer.cc
@@ -0,0 +1,219 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webrtc/video_engine/test/common/win/d3d_renderer.h"
+
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+
+namespace webrtc {
+namespace test {
+
+#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_TEX1)
+
+struct D3dCustomVertex {
+  float x, y, z;
+  float u, v;
+};
+
+const char kD3DClassName[] = "d3d_renderer";
+
+VideoRenderer* VideoRenderer::CreatePlatformRenderer(const char* window_title,
+                                                     size_t width,
+                                                     size_t height) {
+  return D3dRenderer::Create(window_title, width, height);
+}
+
+D3dRenderer::D3dRenderer(size_t width, size_t height)
+    : width_(width),
+      height_(height),
+      hwnd_(NULL),
+      d3d_(NULL),
+      d3d_device_(NULL),
+      texture_(NULL),
+      vertex_buffer_(NULL) {
+  assert(width > 0);
+  assert(height > 0);
+}
+
+D3dRenderer::~D3dRenderer() { Destroy(); }
+
+LRESULT WINAPI D3dRenderer::WindowProc(HWND hwnd, UINT msg, WPARAM wparam,
+                                       LPARAM lparam) {
+  if (msg == WM_DESTROY || (msg == WM_CHAR && wparam == VK_RETURN)) {
+    PostQuitMessage(0);
+    return 0;
+  }
+
+  return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+void D3dRenderer::Destroy() {
+  texture_ = NULL;
+  vertex_buffer_ = NULL;
+  d3d_device_ = NULL;
+  d3d_ = NULL;
+
+  if (hwnd_ != NULL) {
+    DestroyWindow(hwnd_);
+    assert(!IsWindow(hwnd_));
+    hwnd_ = NULL;
+  }
+}
+
+bool D3dRenderer::Init(const char* window_title) {
+  hwnd_ = CreateWindowA(kD3DClassName,
+                        window_title,
+                        WS_OVERLAPPEDWINDOW,
+                        0,
+                        0,
+                        static_cast<int>(width_),
+                        static_cast<int>(height_),
+                        NULL,
+                        NULL,
+                        NULL,
+                        NULL);
+
+  if (hwnd_ == NULL) {
+    Destroy();
+    return false;
+  }
+
+  d3d_ = Direct3DCreate9(D3D_SDK_VERSION);
+  if (d3d_ == NULL) {
+    Destroy();
+    return false;
+  }
+
+  D3DPRESENT_PARAMETERS d3d_params = {};
+
+  d3d_params.Windowed = TRUE;
+  d3d_params.SwapEffect = D3DSWAPEFFECT_COPY;
+
+  IDirect3DDevice9* d3d_device;
+  if (d3d_->CreateDevice(D3DADAPTER_DEFAULT,
+                         D3DDEVTYPE_HAL,
+                         hwnd_,
+                         D3DCREATE_SOFTWARE_VERTEXPROCESSING,
+                         &d3d_params,
+                         &d3d_device) != D3D_OK) {
+    Destroy();
+    return false;
+  }
+  d3d_device_ = d3d_device;
+  d3d_device->Release();
+
+  IDirect3DVertexBuffer9* vertex_buffer;
+  const int kRectVertices = 4;
+  if (d3d_device_->CreateVertexBuffer(kRectVertices * sizeof(D3dCustomVertex),
+                                      0,
+                                      D3DFVF_CUSTOMVERTEX,
+                                      D3DPOOL_MANAGED,
+                                      &vertex_buffer,
+                                      NULL) != D3D_OK) {
+    Destroy();
+    return false;
+  }
+  vertex_buffer_ = vertex_buffer;
+  vertex_buffer->Release();
+
+  d3d_device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
+  d3d_device_->SetRenderState(D3DRS_LIGHTING, FALSE);
+  Resize(width_, height_);
+
+  ShowWindow(hwnd_, SW_SHOWNOACTIVATE);
+  d3d_device_->Present(NULL, NULL, NULL, NULL);
+
+  return true;
+}
+
+D3dRenderer* D3dRenderer::Create(const char* window_title,
+                                 size_t width,
+                                 size_t height) {
+  static ATOM wc_atom = 0;
+  if (wc_atom == 0) {
+    WNDCLASSA wc = {};
+
+    wc.style = CS_HREDRAW | CS_VREDRAW;
+    wc.lpfnWndProc = WindowProc;
+    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
+    wc.lpszClassName = kD3DClassName;
+
+    wc_atom = RegisterClassA(&wc);
+    if (wc_atom == 0)
+      return false;
+  }
+
+  D3dRenderer* d3d_renderer = new D3dRenderer(width, height);
+  if (!d3d_renderer->Init(window_title)) {
+    delete d3d_renderer;
+    return NULL;
+  }
+
+  return d3d_renderer;
+}
+
+void D3dRenderer::Resize(size_t width, size_t height) {
+  width_ = width;
+  height_ = height;
+  IDirect3DTexture9* texture;
+
+  d3d_device_->CreateTexture(static_cast<UINT>(width_),
+                             static_cast<UINT>(height_),
+                             1,
+                             0,
+                             D3DFMT_A8R8G8B8,
+                             D3DPOOL_MANAGED,
+                             &texture,
+                             NULL);
+  texture_ = texture;
+  texture->Release();
+
+  // Vertices for the video frame to be rendered to.
+  static const D3dCustomVertex rect[] = {
+    {-1.0f, -1.0f, 0.0f, 0.0f, 1.0f},
+    {-1.0f, 1.0f, 0.0f, 0.0f, 0.0f},
+    {1.0f, -1.0f, 0.0f, 1.0f, 1.0f},
+    {1.0f, 1.0f, 0.0f, 1.0f, 0.0f},
+  };
+
+  void* buf_data;
+  if (vertex_buffer_->Lock(0, 0, &buf_data, 0) != D3D_OK)
+    return;
+
+  memcpy(buf_data, &rect, sizeof(rect));
+  vertex_buffer_->Unlock();
+}
+
+void D3dRenderer::RenderFrame(const webrtc::I420VideoFrame& frame,
+                              int /*render_delay_ms*/) {
+  if (static_cast<size_t>(frame.width()) != width_ ||
+      static_cast<size_t>(frame.height()) != height_) {
+    Resize(static_cast<size_t>(frame.width()),
+           static_cast<size_t>(frame.height()));
+  }
+
+  D3DLOCKED_RECT lock_rect;
+  if (texture_->LockRect(0, &lock_rect, NULL, 0) != D3D_OK)
+    return;
+
+  ConvertFromI420(frame, kARGB, 0, static_cast<uint8_t*>(lock_rect.pBits));
+  texture_->UnlockRect(0);
+
+  d3d_device_->BeginScene();
+  d3d_device_->SetFVF(D3DFVF_CUSTOMVERTEX);
+  d3d_device_->SetStreamSource(0, vertex_buffer_, 0, sizeof(D3dCustomVertex));
+  d3d_device_->SetTexture(0, texture_);
+  d3d_device_->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+  d3d_device_->EndScene();
+
+  d3d_device_->Present(NULL, NULL, NULL, NULL);
+}
+}  // namespace test
+}  // namespace webrtc
diff --git a/webrtc/video_engine/test/common/win/d3d_renderer.h b/webrtc/video_engine/test/common/win/d3d_renderer.h
new file mode 100644
index 0000000..38efa46
--- /dev/null
+++ b/webrtc/video_engine/test/common/win/d3d_renderer.h
@@ -0,0 +1,52 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef WEBRTC_VIDEO_ENGINE_TEST_COMMON_WIN_D3D_RENDERER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_WIN_D3D_RENDERER_H_
+
+#include <Windows.h>
+#include <d3d9.h>
+
+#include "webrtc/system_wrappers/interface/scoped_refptr.h"
+#include "webrtc/typedefs.h"
+#include "webrtc/video_engine/test/common/video_renderer.h"
+
+namespace webrtc {
+namespace test {
+
+class D3dRenderer : public VideoRenderer {
+ public:
+  static D3dRenderer* Create(const char* window_title, size_t width,
+                             size_t height);
+  virtual ~D3dRenderer();
+
+  virtual void RenderFrame(const webrtc::I420VideoFrame& frame, int delta)
+      OVERRIDE;
+ private:
+  D3dRenderer(size_t width, size_t height);
+
+  static LRESULT WINAPI WindowProc(HWND hwnd, UINT msg, WPARAM wparam,
+                                   LPARAM lparam);
+  bool Init(const char* window_title);
+  void Resize(size_t width, size_t height);
+  void Destroy();
+
+  size_t width_, height_;
+
+  HWND hwnd_;
+  scoped_refptr<IDirect3D9> d3d_;
+  scoped_refptr<IDirect3DDevice9> d3d_device_;
+
+  scoped_refptr<IDirect3DTexture9> texture_;
+  scoped_refptr<IDirect3DVertexBuffer9> vertex_buffer_;
+};
+}  // namespace test
+}  // namespace webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_WIN_D3D_RENDERER_H_
diff --git a/webrtc/video_engine/test/common/win/run_loop_win.cc b/webrtc/video_engine/test/common/win/run_loop_win.cc
new file mode 100644
index 0000000..c62eeb1
--- /dev/null
+++ b/webrtc/video_engine/test/common/win/run_loop_win.cc
@@ -0,0 +1,33 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webrtc/video_engine/test/common/run_loop.h"
+
+#include <assert.h>
+
+#include <conio.h>
+#include <stdio.h>
+#include <Windows.h>
+
+namespace webrtc {
+namespace test {
+
+void PressEnterToContinue() {
+  puts(">> Press ENTER to continue...");
+
+  MSG msg;
+  BOOL ret;
+  while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0) {
+    assert(ret != -1);
+    TranslateMessage(&msg);
+    DispatchMessage(&msg);
+  }
+}
+}  // namespace test
+}  // namespace webrtc
diff --git a/webrtc/video_engine/test/loopback.cc b/webrtc/video_engine/test/loopback.cc
index 86d6d57..5040799 100644
--- a/webrtc/video_engine/test/loopback.cc
+++ b/webrtc/video_engine/test/loopback.cc
@@ -20,6 +20,8 @@
 #include "webrtc/video_engine/test/common/direct_transport.h"
 #include "webrtc/video_engine/test/common/flags.h"
 #include "webrtc/video_engine/test/common/generate_ssrcs.h"
+#include "webrtc/video_engine/test/common/run_loop.h"
+#include "webrtc/video_engine/test/common/run_tests.h"
 #include "webrtc/video_engine/test/common/video_capturer.h"
 #include "webrtc/video_engine/test/common/video_renderer.h"
 
@@ -82,13 +84,9 @@
 
   receive_stream->StartReceive();
   send_stream->StartSend();
-
   camera->Start();
 
-  // TODO(pbos): Run this time limited (optionally), so it can run automated.
-  puts(">> Press ENTER to continue...");
-  while (getchar() != '\n' && !feof(stdin))
-    ;
+  test::PressEnterToContinue();
 
   camera->Stop();
   send_stream->StopSend();
diff --git a/webrtc/video_engine/test/tests.gypi b/webrtc/video_engine/test/tests.gypi
index 40a5004..250938d 100644
--- a/webrtc/video_engine/test/tests.gypi
+++ b/webrtc/video_engine/test/tests.gypi
@@ -32,6 +32,8 @@
         'common/null_platform_renderer.cc',
         'common/run_tests.cc',
         'common/run_tests.h',
+        'common/run_loop.cc',
+        'common/run_loop.h',
         'common/statistics.cc',
         'common/statistics.h',
         'common/vcm_capturer.cc',
@@ -40,6 +42,9 @@
         'common/video_capturer.h',
         'common/video_renderer.cc',
         'common/video_renderer.h',
+        'common/win/d3d_renderer.cc',
+        'common/win/d3d_renderer.h',
+        'common/win/run_loop_win.cc',
       ],
       'conditions': [
         ['OS=="linux"', {
@@ -59,6 +64,12 @@
             'common/gl/gl_renderer.h',
           ],
         }],
+        ['OS=="win"', {
+          'sources!': [
+            'common/null_platform_renderer.cc',
+            'common/run_loop.cc',
+          ],
+        }],
       ],
       'direct_dependent_settings': {
         'conditions': [