New VideoEngine API implementation on top of old one, first steps.

BUG=1668
R=mflodman@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@4044 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/video_engine/test/common/direct_transport.h b/video_engine/test/common/direct_transport.h
new file mode 100644
index 0000000..69de83e
--- /dev/null
+++ b/video_engine/test/common/direct_transport.h
@@ -0,0 +1,39 @@
+/*
+ *  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_DIRECT_TRANSPORT_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_DIRECT_TRANSPORT_H_
+
+#include "webrtc/video_engine/new_include/common.h"
+
+namespace webrtc {
+namespace test {
+
+class DirectTransport : public newapi::Transport {
+ public:
+  explicit DirectTransport(newapi::PacketReceiver* receiver)
+      : receiver_(receiver) {}
+
+  void SetReceiver(newapi::PacketReceiver* receiver) { receiver_ = receiver; }
+
+  bool SendRTP(const void* data, size_t length) OVERRIDE {
+    return receiver_->DeliverPacket(data, length);
+  }
+
+  bool SendRTCP(const void* data, size_t length) OVERRIDE {
+    return SendRTP(data, length);
+  }
+
+ private:
+  newapi::PacketReceiver* receiver_;
+};
+}  // test
+}  // webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_DIRECT_TRANSPORT_H_
diff --git a/video_engine/test/common/generate_ssrcs.h b/video_engine/test/common/generate_ssrcs.h
new file mode 100644
index 0000000..db6c660
--- /dev/null
+++ b/video_engine/test/common/generate_ssrcs.h
@@ -0,0 +1,51 @@
+/*
+ *  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_GENERATE_SSRCS_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_GENERATE_SSRCS_H_
+
+#include <map>
+#include <vector>
+
+#include "webrtc/video_engine/new_include/video_send_stream.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace test {
+
+void GenerateRandomSsrcs(newapi::VideoSendStreamConfig* config,
+                         std::map<uint32_t, bool>* reserved_ssrcs) {
+  size_t num_ssrcs = config->codec.numberOfSimulcastStreams;
+  std::vector<uint32_t>* ssrcs = &config->rtp.ssrcs;
+  assert(ssrcs->size() == 0);
+
+  if (num_ssrcs == 0) {
+    num_ssrcs = 1;
+  }
+
+  while (ssrcs->size() < num_ssrcs) {
+    uint32_t rand_ssrc = static_cast<uint32_t>(rand() + 1);  // NOLINT
+
+    // Make sure the ssrc is unique per-call
+    while (true) {
+      if (!(*reserved_ssrcs)[rand_ssrc]) {
+        (*reserved_ssrcs)[rand_ssrc] = true;
+        break;
+      }
+      ++rand_ssrc;
+    }
+
+    ssrcs->push_back(rand_ssrc);
+  }
+}
+}  // test
+}  // webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_GENERATE_SSRCS_H_
diff --git a/video_engine/test/common/gl/gl_renderer.cc b/video_engine/test/common/gl/gl_renderer.cc
new file mode 100644
index 0000000..3345fac
--- /dev/null
+++ b/video_engine/test/common/gl/gl_renderer.cc
@@ -0,0 +1,112 @@
+/*
+ *  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/gl/gl_renderer.h"
+
+#include <cstring>
+
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+
+namespace webrtc {
+namespace test {
+
+GlRenderer::GlRenderer()
+    : is_init_(false), buffer_(NULL), width_(0), height_(0) {}
+
+void GlRenderer::Init() {
+  assert(!is_init_);
+  is_init_ = true;
+
+  glGenTextures(1, &texture_);
+}
+
+void GlRenderer::Destroy() {
+  if (!is_init_) {
+    return;
+  }
+
+  is_init_ = false;
+
+  delete[] buffer_;
+  buffer_ = NULL;
+
+  glDeleteTextures(1, &texture_);
+}
+
+void GlRenderer::ResizeViewport(size_t width, size_t height) {
+  // TODO(pbos): Aspect ratio, letterbox the video.
+  glViewport(0, 0, width, height);
+
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+  glOrtho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f);
+  glMatrixMode(GL_MODELVIEW);
+}
+
+void GlRenderer::ResizeVideo(size_t width, size_t height) {
+  assert(is_init_);
+  width_ = width;
+  height_ = height;
+
+  buffer_size_ = width * height * 4;  // BGRA
+
+  delete[] buffer_;
+  buffer_ = new uint8_t[buffer_size_];
+  assert(buffer_ != NULL);
+  memset(buffer_, 0, buffer_size_);
+  glBindTexture(GL_TEXTURE_2D, texture_);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGRA,
+               GL_UNSIGNED_INT_8_8_8_8, static_cast<GLvoid*>(buffer_));
+}
+
+void GlRenderer::RenderFrame(const webrtc::I420VideoFrame& frame,
+                             int /*render_delay_ms*/) {
+  assert(is_init_);
+
+  if (static_cast<size_t>(frame.width()) != width_ ||
+      static_cast<size_t>(frame.height()) != height_) {
+    ResizeVideo(frame.width(), frame.height());
+  }
+
+  webrtc::ConvertFromI420(frame, kBGRA, 0, buffer_);
+
+  glEnable(GL_TEXTURE_2D);
+  glBindTexture(GL_TEXTURE_2D, texture_);
+  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_BGRA,
+                  GL_UNSIGNED_INT_8_8_8_8, buffer_);
+
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glLoadIdentity();
+
+  glBegin(GL_QUADS);
+  {
+    glTexCoord2f(0.0f, 0.0f);
+    glVertex3f(0.0f, 0.0f, 0.0f);
+
+    glTexCoord2f(0.0f, 1.0f);
+    glVertex3f(0.0f, 1.0f, 0.0f);
+
+    glTexCoord2f(1.0f, 1.0f);
+    glVertex3f(1.0f, 1.0f, 0.0f);
+
+    glTexCoord2f(1.0f, 0.0f);
+    glVertex3f(1.0f, 0.0f, 0.0f);
+  }
+  glEnd();
+
+  glBindTexture(GL_TEXTURE_2D, 0);
+  glFlush();
+}
+}  // test
+}  // webrtc
diff --git a/video_engine/test/common/gl/gl_renderer.h b/video_engine/test/common/gl/gl_renderer.h
new file mode 100644
index 0000000..6a817ee
--- /dev/null
+++ b/video_engine/test/common/gl/gl_renderer.h
@@ -0,0 +1,45 @@
+/*
+ *  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_GL_GL_RENDERER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_GL_GL_RENDERER_H_
+
+#include <GL/gl.h>
+
+#include "webrtc/video_engine/test/common/video_renderer.h"
+
+namespace webrtc {
+namespace test {
+
+class GlRenderer : public VideoRenderer {
+ public:
+  virtual void RenderFrame(const webrtc::I420VideoFrame& frame,
+                           int time_to_render_ms) OVERRIDE;
+
+ protected:
+  GlRenderer();
+
+  void Init();
+  void Destroy();
+
+  void ResizeViewport(size_t width, size_t height);
+
+ private:
+  bool is_init_;
+  uint8_t* buffer_;
+  GLuint texture_;
+  size_t width_, height_, buffer_size_;
+
+  void ResizeVideo(size_t width, size_t height);
+};
+}  // test
+}  // webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_GL_GL_RENDERER_H_
diff --git a/video_engine/test/common/linux/glx_renderer.cc b/video_engine/test/common/linux/glx_renderer.cc
new file mode 100644
index 0000000..4f7c3d2
--- /dev/null
+++ b/video_engine/test/common/linux/glx_renderer.cc
@@ -0,0 +1,180 @@
+/*
+ *  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/linux/glx_renderer.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include <cassert>
+
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+
+namespace webrtc {
+namespace test {
+
+GlxRenderer::GlxRenderer(size_t width, size_t height)
+    : is_init_(false),
+      width_(width),
+      height_(height),
+      display_(NULL),
+      context_(NULL) {
+  assert(width > 0);
+  assert(height > 0);
+}
+
+bool GlxRenderer::Init(const char* window_title) {
+  if ((display_ = XOpenDisplay(NULL)) == NULL) {
+    Destroy();
+    return false;
+  }
+
+  int screen = DefaultScreen(display_);
+
+  XVisualInfo* vi;
+  int attr_list[] = { GLX_DOUBLEBUFFER, GLX_RGBA, GLX_RED_SIZE, 4,
+                      GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4, GLX_DEPTH_SIZE, 16,
+                      None, };
+
+  if ((vi = glXChooseVisual(display_, screen, attr_list)) == NULL) {
+    Destroy();
+    return false;
+  }
+
+  context_ = glXCreateContext(display_, vi, 0, true);
+  if (context_ == NULL) {
+    Destroy();
+    return false;
+  }
+
+  XSetWindowAttributes window_attributes;
+  window_attributes.colormap = XCreateColormap(
+      display_, RootWindow(display_, vi->screen), vi->visual, AllocNone);
+  window_attributes.border_pixel = 0;
+  window_attributes.event_mask = StructureNotifyMask | ExposureMask;
+  window_ = XCreateWindow(display_, RootWindow(display_, vi->screen), 0, 0,
+                          width_, height_, 0, vi->depth, InputOutput,
+                          vi->visual, CWBorderPixel | CWColormap | CWEventMask,
+                          &window_attributes);
+  XFree(vi);
+
+  XSetStandardProperties(display_, window_, window_title, window_title, None,
+                         NULL, 0, NULL);
+
+  Atom wm_delete = XInternAtom(display_, "WM_DELETE_WINDOW", True);
+  if (wm_delete != None) {
+    XSetWMProtocols(display_, window_, &wm_delete, 1);
+  }
+
+  XMapRaised(display_, window_);
+
+  if (!glXMakeCurrent(display_, window_, context_)) {
+    Destroy();
+    return false;
+  }
+  GlRenderer::Init();
+  if (!glXMakeCurrent(display_, None, NULL)) {
+    Destroy();
+    return false;
+  }
+
+  Resize(width_, height_);
+  return true;
+}
+
+void GlxRenderer::Destroy() {
+  if (!is_init_) {
+    return;
+  }
+
+  is_init_ = false;
+
+  if (context_ != NULL) {
+    glXMakeCurrent(display_, window_, context_);
+    GlRenderer::Destroy();
+    glXMakeCurrent(display_, None, NULL);
+    glXDestroyContext(display_, context_);
+    context_ = NULL;
+  }
+
+  if (display_ != NULL) {
+    XCloseDisplay(display_);
+    display_ = NULL;
+  }
+}
+
+GlxRenderer* GlxRenderer::Create(const char* window_title, size_t width,
+                                 size_t height) {
+  GlxRenderer* glx_renderer = new GlxRenderer(width, height);
+  if (!glx_renderer->Init(window_title)) {
+    // TODO(pbos): Add GLX-failed warning here?
+    delete glx_renderer;
+    return NULL;
+  }
+  return glx_renderer;
+}
+
+GlxRenderer::~GlxRenderer() { Destroy(); }
+
+void GlxRenderer::Resize(size_t width, size_t height) {
+  width_ = width;
+  height_ = height;
+  if (!glXMakeCurrent(display_, window_, context_)) {
+    abort();
+  }
+  GlRenderer::ResizeViewport(width_, height_);
+  if (!glXMakeCurrent(display_, None, NULL)) {
+    abort();
+  }
+
+  XSizeHints* size_hints = XAllocSizeHints();
+  if (size_hints == NULL) {
+    abort();
+  }
+  size_hints->flags = PAspect;
+  size_hints->min_aspect.x = size_hints->max_aspect.x = width_;
+  size_hints->min_aspect.y = size_hints->max_aspect.y = height_;
+  XSetWMNormalHints(display_, window_, size_hints);
+  XFree(size_hints);
+}
+
+void GlxRenderer::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()));
+  }
+
+  XEvent event;
+  if (!glXMakeCurrent(display_, window_, context_)) {
+    abort();
+  }
+  while (XPending(display_)) {
+    XNextEvent(display_, &event);
+    switch (event.type) {
+      case ConfigureNotify:
+        GlRenderer::ResizeViewport(event.xconfigure.width,
+                                   event.xconfigure.height);
+        break;
+      default:
+        break;
+    }
+  }
+
+  GlRenderer::RenderFrame(frame, 0);
+  glXSwapBuffers(display_, window_);
+
+  if (!glXMakeCurrent(display_, None, NULL)) {
+    abort();
+  }
+}
+}  // test
+}  // webrtc
diff --git a/video_engine/test/common/linux/glx_renderer.h b/video_engine/test/common/linux/glx_renderer.h
new file mode 100644
index 0000000..30fc8df
--- /dev/null
+++ b/video_engine/test/common/linux/glx_renderer.h
@@ -0,0 +1,47 @@
+/*
+ *  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_LINUX_GLX_RENDERER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_GLX_RENDERER_H_
+
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+
+#include "webrtc/video_engine/test/common/gl/gl_renderer.h"
+
+namespace webrtc {
+namespace test {
+
+class GlxRenderer : public GlRenderer {
+ public:
+  static GlxRenderer* Create(const char* window_title, size_t width,
+                             size_t height);
+  virtual ~GlxRenderer();
+
+  virtual void RenderFrame(const webrtc::I420VideoFrame& frame, int delta)
+      OVERRIDE;
+ private:
+  GlxRenderer(size_t width, size_t height);
+
+  bool Init(const char* window_title);
+  void Resize(size_t width, size_t height);
+  void Destroy();
+
+  bool is_init_;
+  size_t width_, height_;
+
+  Display* display_;
+  Window window_;
+  GLXContext context_;
+};
+}  // test
+}  // webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_GLX_RENDERER_H_
diff --git a/video_engine/test/common/linux/xv_renderer.cc b/video_engine/test/common/linux/xv_renderer.cc
new file mode 100644
index 0000000..ba0b437
--- /dev/null
+++ b/video_engine/test/common/linux/xv_renderer.cc
@@ -0,0 +1,210 @@
+/*
+ *  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/linux/xv_renderer.h"
+
+#include <sys/shm.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xvlib.h>
+
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+
+#define GUID_I420_PLANAR 0x30323449
+
+namespace webrtc {
+namespace test {
+
+XvRenderer::XvRenderer(size_t width, size_t height)
+    : width(width),
+      height(height),
+      is_init(false),
+      display(NULL),
+      gc(NULL),
+      image(NULL) {
+  assert(width > 0);
+  assert(height > 0);
+}
+
+bool XvRenderer::Init(const char* window_title) {
+  assert(!is_init);
+  is_init = true;
+  if ((display = XOpenDisplay(NULL)) == NULL) {
+    Destroy();
+    return false;
+  }
+
+  int screen = DefaultScreen(display);
+
+  XVisualInfo vinfo;
+  if (!XMatchVisualInfo(display, screen, 24, TrueColor, &vinfo)) {
+    Destroy();
+    return false;
+  }
+
+  XSetWindowAttributes xswa;
+  xswa.colormap = XCreateColormap(display, DefaultRootWindow(display),
+                                  vinfo.visual, AllocNone);
+  xswa.event_mask = StructureNotifyMask | ExposureMask;
+  xswa.background_pixel = 0;
+  xswa.border_pixel = 0;
+
+  window = XCreateWindow(display, DefaultRootWindow(display), 0, 0, width,
+                         height, 0, vinfo.depth, InputOutput, vinfo.visual,
+                         CWBackPixel | CWBorderPixel | CWColormap | CWEventMask,
+                         &xswa);
+
+  XStoreName(display, window, window_title);
+  XSetIconName(display, window, window_title);
+
+  XSelectInput(display, window, StructureNotifyMask);
+
+  XMapRaised(display, window);
+
+  XEvent event;
+  do {
+    XNextEvent(display, &event);
+  } while (event.type != MapNotify || event.xmap.event != window);
+
+  if (!XShmQueryExtension(display)) {
+    Destroy();
+    return false;
+  }
+
+  xv_complete = XShmGetEventBase(display) + ShmCompletion;
+
+  XvAdaptorInfo* ai;
+  unsigned int p_num_adaptors;
+
+  if (XvQueryAdaptors(display, DefaultRootWindow(display), &p_num_adaptors,
+                      &ai) !=
+      Success) {
+    Destroy();
+    return false;
+  }
+  if (p_num_adaptors <= 0) {
+    XvFreeAdaptorInfo(ai);
+    Destroy();
+    return false;
+  }
+
+  xv_port = ai[p_num_adaptors - 1].base_id;
+  XvFreeAdaptorInfo(ai);
+
+  if (xv_port == -1) {
+    Destroy();
+    return false;
+  }
+
+  gc = XCreateGC(display, window, 0, 0);
+  if (gc == NULL) {
+    Destroy();
+    return false;
+  }
+
+  Resize(width, height);
+
+  return true;
+}
+
+void XvRenderer::Destroy() {
+  if (image != NULL) {
+    XFree(image);
+    image = NULL;
+  }
+
+  if (gc != NULL) {
+    XFreeGC(display, gc);
+    gc = NULL;
+  }
+
+  if (display != NULL) {
+    XCloseDisplay(display);
+    display = NULL;
+  }
+}
+
+XvRenderer* XvRenderer::Create(const char* window_title, size_t width,
+                               size_t height) {
+  XvRenderer* xv_renderer = new XvRenderer(width, height);
+  if (!xv_renderer->Init(window_title)) {
+    // TODO(pbos): Add Xv-failed warning here?
+    delete xv_renderer;
+    return NULL;
+  }
+  return xv_renderer;
+}
+
+XvRenderer::~XvRenderer() { Destroy(); }
+
+void XvRenderer::Resize(size_t width, size_t height) {
+  this->width = width;
+  this->height = height;
+
+  if (image != NULL) {
+    XFree(image);
+  }
+  image = XvShmCreateImage(display, xv_port, GUID_I420_PLANAR, 0, width, height,
+                           &shm_info);
+  assert(image != NULL);
+
+  shm_info.shmid = shmget(IPC_PRIVATE, image->data_size, IPC_CREAT | 0777);
+  shm_info.shmaddr = image->data =
+      reinterpret_cast<char*>(shmat(shm_info.shmid, 0, 0));
+  shm_info.readOnly = False;
+
+  if (!XShmAttach(display, &shm_info)) {
+    abort();
+  }
+
+  XSizeHints* size_hints = XAllocSizeHints();
+  if (size_hints == NULL) {
+    abort();
+  }
+  size_hints->flags = PAspect;
+  size_hints->min_aspect.x = size_hints->max_aspect.x = width;
+  size_hints->min_aspect.y = size_hints->max_aspect.y = height;
+  XSetWMNormalHints(display, window, size_hints);
+  XFree(size_hints);
+
+  XWindowChanges wc;
+  wc.width = width;
+  wc.height = height;
+  XConfigureWindow(display, window, CWWidth | CWHeight, &wc);
+}
+
+void XvRenderer::RenderFrame(const webrtc::I420VideoFrame& frame,
+                             int /*render_delay_ms*/) {
+  int size = webrtc::ExtractBuffer(frame, image->data_size,
+                                   reinterpret_cast<uint8_t*>(image->data));
+  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()));
+  }
+  assert(size > 0);
+  Window root;
+  int temp;
+  unsigned int window_width, window_height, u_temp;
+
+  XGetGeometry(display, window, &root, &temp, &temp, &window_width,
+               &window_height, &u_temp, &u_temp);
+
+  XvShmPutImage(display, xv_port, window, gc, image, 0, 0, image->width,
+                image->height, 0, 0, window_width, window_height, True);
+
+  XFlush(display);
+
+  XEvent event;
+  while (XPending(display)) {
+    XNextEvent(display, &event);
+  }
+}
+}  // test
+}  // webrtc
diff --git a/video_engine/test/common/linux/xv_renderer.h b/video_engine/test/common/linux/xv_renderer.h
new file mode 100644
index 0000000..df6b050
--- /dev/null
+++ b/video_engine/test/common/linux/xv_renderer.h
@@ -0,0 +1,54 @@
+/*
+ *  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_LINUX_XV_RENDERER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_XV_RENDERER_H_
+
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+#include <X11/extensions/Xvlib.h>
+
+#include "webrtc/video_engine/new_include/common.h"
+#include "webrtc/video_engine/test/common/video_renderer.h"
+
+namespace webrtc {
+namespace test {
+
+class XvRenderer : public VideoRenderer {
+ public:
+  ~XvRenderer();
+
+  virtual void RenderFrame(const webrtc::I420VideoFrame& frame, int delta)
+      OVERRIDE;
+
+  static XvRenderer* Create(const char *window_title, size_t width,
+                            size_t height);
+
+ private:
+  XvRenderer(size_t width, size_t height);
+
+  bool Init(const char *window_title);
+  void Resize(size_t width, size_t height);
+  void Destroy();
+
+  size_t width, height;
+  bool is_init;
+
+  Display* display;
+  Window window;
+  GC gc;
+  XvImage* image;
+  XShmSegmentInfo shm_info;
+  int xv_port, xv_complete;
+};
+}  // test
+}  // webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_XV_RENDERER_H_
diff --git a/video_engine/test/common/vcm_capturer.cc b/video_engine/test/common/vcm_capturer.cc
new file mode 100644
index 0000000..62f0a85
--- /dev/null
+++ b/video_engine/test/common/vcm_capturer.cc
@@ -0,0 +1,108 @@
+/*
+ *  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/vcm_capturer.h"
+
+#include "webrtc/modules/video_capture/include/video_capture_factory.h"
+#include "webrtc/video_engine/new_include/video_send_stream.h"
+
+namespace webrtc {
+namespace test {
+
+VcmCapturer::VcmCapturer(webrtc::newapi::VideoSendStreamInput* input)
+    : VideoCapturer(input), started_(false), vcm(NULL), last_timestamp(0) {}
+
+bool VcmCapturer::Init(size_t width, size_t height, size_t target_fps) {
+  VideoCaptureModule::DeviceInfo* device_info =
+      VideoCaptureFactory::CreateDeviceInfo(42);  // Any ID (42) will do.
+
+  char device_name[256];
+  char unique_name[256];
+  if (device_info->GetDeviceName(0, device_name, sizeof(device_name),
+                                 unique_name, sizeof(unique_name)) !=
+      0) {
+    Destroy();
+    return false;
+  }
+
+  vcm = webrtc::VideoCaptureFactory::Create(0, unique_name);
+  vcm->RegisterCaptureDataCallback(*this);
+
+  device_info->GetCapability(vcm->CurrentDeviceName(), 0, capability_);
+  delete device_info;
+
+  capability_.width = static_cast<int32_t>(width);
+  capability_.height = static_cast<int32_t>(height);
+  capability_.maxFPS = static_cast<int32_t>(target_fps);
+  capability_.rawType = kVideoI420;
+
+  if (vcm->StartCapture(capability_) != 0) {
+    Destroy();
+    return false;
+  }
+
+  assert(vcm->CaptureStarted());
+
+  return true;
+}
+
+VcmCapturer* VcmCapturer::Create(newapi::VideoSendStreamInput* input,
+                                 size_t width, size_t height,
+                                 size_t target_fps) {
+  VcmCapturer* vcm_capturer = new VcmCapturer(input);
+  if (!vcm_capturer->Init(width, height, target_fps)) {
+    // TODO(pbos): Log a warning that this failed.
+    delete vcm_capturer;
+    return NULL;
+  }
+  return vcm_capturer;
+}
+
+
+void VcmCapturer::Start() { started_ = true; }
+
+void VcmCapturer::Stop() { started_ = false; }
+
+void VcmCapturer::Destroy() {
+  if (vcm == NULL) {
+    return;
+  }
+
+  vcm->StopCapture();
+  vcm->DeRegisterCaptureDataCallback();
+  vcm->Release();
+
+  // TODO(pbos): How do I destroy the VideoCaptureModule? This still leaves
+  //             non-freed memory.
+  vcm = NULL;
+}
+
+VcmCapturer::~VcmCapturer() { Destroy(); }
+
+void VcmCapturer::OnIncomingCapturedFrame(const int32_t id,
+                                          I420VideoFrame& frame) {
+  if (last_timestamp == 0 || frame.timestamp() < last_timestamp) {
+    last_timestamp = frame.timestamp();
+  }
+
+  if (started_) {
+    input_->PutFrame(frame, frame.timestamp() - last_timestamp);
+  }
+  last_timestamp = frame.timestamp();
+}
+
+void VcmCapturer::OnIncomingCapturedEncodedFrame(const int32_t id,
+                                                 VideoFrame& frame,
+                                                 VideoCodecType codec_type) {}
+
+void VcmCapturer::OnCaptureDelayChanged(const int32_t id, const int32_t delay) {
+}
+}  // test
+}  // webrtc
diff --git a/video_engine/test/common/vcm_capturer.h b/video_engine/test/common/vcm_capturer.h
new file mode 100644
index 0000000..ac31825
--- /dev/null
+++ b/video_engine/test/common/vcm_capturer.h
@@ -0,0 +1,53 @@
+/*
+ *  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_VCM_CAPTURER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_VCM_CAPTURER_H_
+
+#include "webrtc/common_types.h"
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/modules/video_capture/include/video_capture.h"
+#include "webrtc/video_engine/test/common/video_capturer.h"
+
+namespace webrtc {
+namespace test {
+
+class VcmCapturer : public VideoCapturer, public VideoCaptureDataCallback {
+ public:
+  static VcmCapturer* Create(newapi::VideoSendStreamInput* input, size_t width,
+                             size_t height, size_t target_fps);
+  virtual ~VcmCapturer();
+
+  virtual void Start() OVERRIDE;
+  virtual void Stop() OVERRIDE;
+
+  virtual void OnIncomingCapturedFrame(const int32_t id, I420VideoFrame& frame)
+      OVERRIDE;
+  virtual void OnIncomingCapturedEncodedFrame(const int32_t id,
+                                              VideoFrame& frame,
+                                              VideoCodecType codec_type)
+      OVERRIDE;
+  virtual void OnCaptureDelayChanged(const int32_t id, const int32_t delay)
+      OVERRIDE;
+
+ private:
+  explicit VcmCapturer(newapi::VideoSendStreamInput* input);
+  bool Init(size_t width, size_t height, size_t target_fps);
+  void Destroy();
+
+  bool started_;
+  VideoCaptureModule* vcm;
+  VideoCaptureCapability capability_;
+
+  uint32_t last_timestamp;
+};
+}  // test
+}  // webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_VCM_CAPTURER_H_
diff --git a/video_engine/test/common/video_capturer.cc b/video_engine/test/common/video_capturer.cc
new file mode 100644
index 0000000..c8e5477
--- /dev/null
+++ b/video_engine/test/common/video_capturer.cc
@@ -0,0 +1,47 @@
+/*
+ *  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/video_capturer.h"
+#include "webrtc/video_engine/test/common/vcm_capturer.h"
+
+namespace webrtc {
+namespace test {
+
+class NullCapturer : public VideoCapturer {
+ public:
+  NullCapturer() : VideoCapturer(NULL) {}
+  virtual ~NullCapturer() {}
+
+  virtual void Start() {}
+  virtual void Stop() {}
+};
+
+VideoCapturer::VideoCapturer(newapi::VideoSendStreamInput* input)
+    : input_(input) {}
+
+VideoCapturer* VideoCapturer::Create(newapi::VideoSendStreamInput* input) {
+  // TODO(pbos): These should be specified by command-line parameters.
+  size_t width = 640;
+  size_t height = 480;
+  size_t target_fps = 30;
+
+  VcmCapturer* vcm_capturer = VcmCapturer::Create(input, width, height,
+                                                  target_fps);
+  if (vcm_capturer != NULL) {
+    return vcm_capturer;
+  }
+  // TODO(pbos): Log a warning that this failed.
+
+  // TODO(pbos): Add a pseudocapturer which generates frames.
+
+  return new NullCapturer();
+}
+}  // test
+}  // webrtc
diff --git a/video_engine/test/common/video_capturer.h b/video_engine/test/common/video_capturer.h
new file mode 100644
index 0000000..c142998
--- /dev/null
+++ b/video_engine/test/common/video_capturer.h
@@ -0,0 +1,36 @@
+/*
+ *  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_VIDEO_CAPTURER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_CAPTURER_H_
+
+namespace webrtc {
+
+namespace newapi {
+class VideoSendStreamInput;
+}  // newapi
+
+namespace test {
+
+class VideoCapturer {
+ public:
+  static VideoCapturer* Create(newapi::VideoSendStreamInput* input);
+  virtual ~VideoCapturer() {}
+
+  virtual void Start() = 0;
+  virtual void Stop() = 0;
+
+ protected:
+  explicit VideoCapturer(newapi::VideoSendStreamInput* input);
+  newapi::VideoSendStreamInput* input_;
+};
+}  // test
+}  // webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_CAPTURER_H
diff --git a/video_engine/test/common/video_renderer.cc b/video_engine/test/common/video_renderer.cc
new file mode 100644
index 0000000..bc53fa7
--- /dev/null
+++ b/video_engine/test/common/video_renderer.cc
@@ -0,0 +1,63 @@
+/*
+ *  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/video_renderer.h"
+
+#include "webrtc/modules/video_capture/include/video_capture_factory.h"
+#include "webrtc/video_engine/new_include/video_send_stream.h"
+
+#ifdef WEBRTC_TEST_XV
+#include "webrtc/video_engine/test/common/linux/xv_renderer.h"
+#endif  // WEBRTC_TEST_XV
+
+// Platform-specific renderers preferred over NullRenderer
+#ifdef WEBRTC_TEST_GLX
+#include "webrtc/video_engine/test/common/linux/glx_renderer.h"
+#endif  // WEBRTC_TEST_GLX
+
+// TODO(pbos): Mac renderer
+// TODO(pbos): Windows renderer
+// TODO(pbos): Android renderer
+
+namespace webrtc {
+namespace test {
+
+class NullRenderer : public VideoRenderer {
+  virtual void RenderFrame(const I420VideoFrame& video_frame,
+                           int time_to_render_ms) OVERRIDE {}
+};
+
+VideoRenderer* VideoRenderer::Create(const char* window_title) {
+  // TODO(pbos): Get these from command-line parameters.
+  int width = 640;
+  int height = 480;
+
+#ifdef WEBRTC_TEST_XV
+  XvRenderer* xv_renderer = XvRenderer::Create(window_title, width, height);
+  if (xv_renderer != NULL) {
+    return xv_renderer;
+  }
+#endif  // WEBRTC_TEST_XV
+#ifdef WEBRTC_TEST_GLX
+  GlxRenderer* glx_renderer = GlxRenderer::Create(window_title, width, height);
+  if (glx_renderer != NULL) {
+    return glx_renderer;
+  }
+#endif  // WEBRTC_TEST_GLX
+
+  // Avoid initialized-but-not-referenced errors when only building a
+  // NullRenderer
+  (void) width;
+  (void) height;
+
+  return new NullRenderer();
+}
+}  // test
+}  // webrtc
diff --git a/video_engine/test/common/video_renderer.h b/video_engine/test/common/video_renderer.h
new file mode 100644
index 0000000..60ca786
--- /dev/null
+++ b/video_engine/test/common/video_renderer.h
@@ -0,0 +1,26 @@
+/*
+ *  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_VIDEO_RENDERER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_RENDERER_H_
+
+#include "webrtc/video_engine/new_include/common.h"
+
+namespace webrtc {
+namespace test {
+
+class VideoRenderer : public newapi::VideoRenderer {
+ public:
+  static VideoRenderer* Create(const char* window_title);
+  virtual ~VideoRenderer() {}
+};
+}  // test
+}  // webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_RENDERER_H_
diff --git a/video_engine/test/loopback.cc b/video_engine/test/loopback.cc
new file mode 100644
index 0000000..c8c3ae3
--- /dev/null
+++ b/video_engine/test/loopback.cc
@@ -0,0 +1,103 @@
+/*
+ *  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 <gtest/gtest.h>
+
+#include <iostream>
+#include <map>
+
+#include "webrtc/video_engine/new_include/video_engine.h"
+#include "webrtc/video_engine/test/common/direct_transport.h"
+#include "webrtc/video_engine/test/common/generate_ssrcs.h"
+#include "webrtc/video_engine/test/common/video_capturer.h"
+#include "webrtc/video_engine/test/common/video_renderer.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class LoopbackTest : public ::testing::Test {
+ protected:
+  std::map<uint32_t, bool> reserved_ssrcs;
+};
+
+TEST_F(LoopbackTest, Test) {
+  test::VideoRenderer* local_preview =
+      test::VideoRenderer::Create("Local Preview");
+  test::VideoRenderer* loopback_video =
+      test::VideoRenderer::Create("Loopback Video");
+
+  newapi::VideoEngine* video_engine =
+      newapi::VideoEngine::Create(webrtc::newapi::VideoEngineConfig());
+
+  test::DirectTransport transport(NULL);
+  newapi::VideoCall* call = video_engine->CreateCall(&transport);
+
+  // Loopback, call sends to itself.
+  transport.SetReceiver(call->Receiver());
+
+  newapi::VideoSendStreamConfig send_config;
+  call->GetDefaultSendConfig(&send_config);
+  test::GenerateRandomSsrcs(&send_config, &reserved_ssrcs);
+
+  send_config.local_renderer = local_preview;
+
+  // TODO(pbos): Should be specified by command-line parameters. And not even
+  //             visible in the test. Break it out to some get-test-defaults
+  //             class
+  send_config.codec.width = 640;
+  send_config.codec.height = 480;
+  send_config.codec.minBitrate = 1000;
+  send_config.codec.startBitrate = 1500;
+  send_config.codec.maxBitrate = 2000;
+
+  newapi::VideoSendStream* send_stream = call->CreateSendStream(send_config);
+
+  test::VideoCapturer* camera =
+      test::VideoCapturer::Create(send_stream->Input());
+
+  newapi::VideoReceiveStreamConfig receive_config;
+  call->GetDefaultReceiveConfig(&receive_config);
+  receive_config.rtp.ssrc = send_config.rtp.ssrcs[0];
+  receive_config.renderer = loopback_video;
+
+  newapi::VideoReceiveStream* receive_stream =
+      call->CreateReceiveStream(receive_config);
+
+  receive_stream->StartReceive();
+  send_stream->StartSend();
+
+  camera->Start();
+
+  // TODO(pbos): Run this time limited (optionally), so it can run automated.
+  std::cout << ">> Press ENTER to continue..." << std::endl;
+  std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
+
+  receive_stream->StopReceive();
+  send_stream->StopSend();
+
+  // Stop sending
+  delete camera;
+
+  call->DestroyReceiveStream(receive_stream);
+  call->DestroySendStream(send_stream);
+
+  delete call;
+  delete video_engine;
+
+  delete loopback_video;
+  delete local_preview;
+}
+}  // webrtc
+
+int main(int argc, char* argv[]) {
+  ::testing::InitGoogleTest(&argc, argv);
+
+  return RUN_ALL_TESTS();
+}
diff --git a/video_engine/test/tests.gypi b/video_engine/test/tests.gypi
new file mode 100644
index 0000000..d74881a
--- /dev/null
+++ b/video_engine/test/tests.gypi
@@ -0,0 +1,115 @@
+# 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.
+
+{
+  'variables': {
+    'xv_renderer%': 0,
+  },
+  'conditions': [
+    ['OS=="linux"', {
+      'variables': {
+       'glx_renderer%': 1,
+      },
+    }, {
+      # OS != "linux"
+      'variables': {
+        'glx_renderer%': 0,
+      },
+    }],
+  ],
+  'targets': [
+    {
+      'target_name': 'video_tests_common',
+      'type': 'static_library',
+      'sources': [
+        'common/generate_ssrcs.h',
+        'common/vcm_capturer.h',
+        'common/vcm_capturer.cc',
+        'common/video_capturer.cc',
+        'common/video_capturer.h',
+        'common/video_renderer.cc',
+        'common/video_renderer.h',
+      ],
+      'conditions': [
+        ['glx_renderer==1', {
+          'defines': [
+            'WEBRTC_TEST_GLX',
+          ],
+          'sources' : [
+            'common/gl/gl_renderer.cc',
+            'common/gl/gl_renderer.h',
+            'common/linux/glx_renderer.cc',
+            'common/linux/glx_renderer.h',
+          ],
+        }],
+        ['xv_renderer==1', {
+          'defines': [
+            'WEBRTC_TEST_XV',
+          ],
+          'sources': [
+            'common/linux/xv_renderer.cc',
+            'common/linux/xv_renderer.h',
+          ],
+        }],
+      ],
+      'direct_dependent_settings': {
+        'conditions': [
+          ['OS=="linux"', {
+            'libraries': [
+              '-lXext',
+              '-lX11',
+              '-lGL',
+            ],
+          }],
+          ['xv_renderer==1', {
+            'libraries': [
+              '-lXv',
+            ],
+          }],
+          #TODO(pbos) : These dependencies should not have to be here, they
+          #             aren't used by test code directly, only by components
+          #             used by the tests.
+          ['OS=="android"', {
+            'libraries' : [
+              '-lGLESv2', '-llog',
+            ],
+          }],
+          ['OS=="mac"', {
+            'xcode_settings' : {
+              'OTHER_LDFLAGS' : [
+                '-framework Foundation',
+                '-framework AppKit',
+                '-framework Cocoa',
+                '-framework OpenGL',
+                '-framework CoreVideo',
+                '-framework CoreAudio',
+                '-framework AudioToolbox',
+              ],
+            },
+          }],
+        ],
+      },
+      'dependencies': [
+        '<(webrtc_root)/modules/modules.gyp:video_capture_module',
+        'video_engine_core',
+      ],
+    },
+    {
+      'target_name': 'video_loopback',
+      'type': 'executable',
+      'sources': [
+        'loopback.cc',
+      ],
+      'dependencies': [
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+        '<(webrtc_root)/modules/modules.gyp:video_capture_module',
+        'video_tests_common',
+      ],
+    },
+  ],
+}