Fix build for Windows and Linux.

git-svn-id: http://libjingle.googlecode.com/svn/trunk@57 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/talk/base/basicpacketsocketfactory.cc b/talk/base/basicpacketsocketfactory.cc
new file mode 100644
index 0000000..42721ba
--- /dev/null
+++ b/talk/base/basicpacketsocketfactory.cc
@@ -0,0 +1,175 @@
+/*
+ * libjingle
+ * Copyright 2011, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/basicpacketsocketfactory.h"
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/asynctcpsocket.h"
+#include "talk/base/logging.h"
+#include "talk/base/socketadapters.h"
+#include "talk/base/thread.h"
+
+namespace talk_base {
+
+BasicPacketSocketFactory::BasicPacketSocketFactory()
+    : thread_(Thread::Current()),
+      socket_factory_(NULL) {
+}
+
+BasicPacketSocketFactory::BasicPacketSocketFactory(Thread* thread)
+    : thread_(thread),
+      socket_factory_(NULL) {
+}
+
+BasicPacketSocketFactory::BasicPacketSocketFactory(
+    SocketFactory* socket_factory)
+    : thread_(NULL),
+      socket_factory_(socket_factory) {
+}
+
+BasicPacketSocketFactory::~BasicPacketSocketFactory() {
+}
+
+AsyncPacketSocket* BasicPacketSocketFactory::CreateUdpSocket(
+    const SocketAddress& address, int min_port, int max_port) {
+  // UDP sockets are simple.
+  talk_base::AsyncSocket* socket =
+      socket_factory()->CreateAsyncSocket(SOCK_DGRAM);
+  if (!socket) {
+    return NULL;
+  }
+  if (BindSocket(socket, address, min_port, max_port) < 0) {
+    LOG(LS_ERROR) << "UDP bind failed with error "
+                    << socket->GetError();
+    delete socket;
+    return NULL;
+  }
+  return new talk_base::AsyncUDPSocket(socket);
+}
+
+AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket(
+    const SocketAddress& local_address, int min_port, int max_port, bool ssl) {
+  talk_base::AsyncSocket* socket =
+      socket_factory()->CreateAsyncSocket(SOCK_STREAM);
+  if (!socket) {
+    return NULL;
+  }
+
+  if (BindSocket(socket, local_address, min_port, max_port) < 0) {
+    LOG(LS_ERROR) << "TCP bind failed with error "
+                  << socket->GetError();
+    delete socket;
+    return NULL;
+  }
+
+  // If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket.
+  if (ssl) {
+    socket = new talk_base::AsyncSSLSocket(socket);
+  }
+
+  // Set TCP_NODELAY (via OPT_NODELAY) for improved performance.
+  // See http://go/gtalktcpnodelayexperiment
+  socket->SetOption(talk_base::Socket::OPT_NODELAY, 1);
+
+  return new talk_base::AsyncTCPSocket(socket, true);
+}
+
+AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
+    const SocketAddress& local_address, const SocketAddress& remote_address,
+    const ProxyInfo& proxy_info, const std::string& user_agent, bool ssl) {
+  talk_base::AsyncSocket* socket =
+      socket_factory()->CreateAsyncSocket(SOCK_STREAM);
+  if (!socket) {
+    return NULL;
+  }
+
+  if (BindSocket(socket, local_address, 0, 0) < 0) {
+    LOG(LS_ERROR) << "TCP bind failed with error "
+                  << socket->GetError();
+    delete socket;
+    return NULL;
+  }
+
+  // If using a proxy, wrap the socket in a proxy socket.
+  if (proxy_info.type == talk_base::PROXY_SOCKS5) {
+    socket = new talk_base::AsyncSocksProxySocket(
+        socket, proxy_info.address, proxy_info.username, proxy_info.password);
+  } else if (proxy_info.type == talk_base::PROXY_HTTPS) {
+    socket = new talk_base::AsyncHttpsProxySocket(
+        socket, user_agent, proxy_info.address,
+        proxy_info.username, proxy_info.password);
+  }
+
+  // If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket.
+  if (ssl) {
+    socket = new talk_base::AsyncSSLSocket(socket);
+  }
+
+  if (socket->Connect(remote_address) < 0) {
+    LOG(LS_ERROR) << "TCP connect failed with error "
+                  << socket->GetError();
+    delete socket;
+    return NULL;
+  }
+
+  // Finally, wrap that socket in a TCP packet socket.
+  talk_base::AsyncTCPSocket* tcp_socket =
+      new talk_base::AsyncTCPSocket(socket, false);
+
+  // Set TCP_NODELAY (via OPT_NODELAY) for improved performance.
+  // See http://go/gtalktcpnodelayexperiment
+  tcp_socket->SetOption(talk_base::Socket::OPT_NODELAY, 1);
+
+  return tcp_socket;
+}
+
+int BasicPacketSocketFactory::BindSocket(
+    AsyncSocket* socket, const SocketAddress& local_address,
+    int min_port, int max_port) {
+  int ret = -1;
+  if (min_port == 0 && max_port == 0) {
+    // If there's no port range, let the OS pick a port for us.
+    ret = socket->Bind(local_address);
+  } else {
+    // Otherwise, try to find a port in the provided range.
+    for (int port = min_port; ret < 0 && port <= max_port; ++port) {
+      ret = socket->Bind(talk_base::SocketAddress(local_address.ip(), port));
+    }
+  }
+  return ret;
+}
+
+SocketFactory* BasicPacketSocketFactory::socket_factory() {
+  if (thread_) {
+    ASSERT(thread_ == Thread::Current());
+    return thread_->socketserver();
+  } else {
+    return socket_factory_;
+  }
+}
+
+}  // namespace talk_base
diff --git a/talk/base/basicpacketsocketfactory.h b/talk/base/basicpacketsocketfactory.h
new file mode 100644
index 0000000..bf66bc8
--- /dev/null
+++ b/talk/base/basicpacketsocketfactory.h
@@ -0,0 +1,66 @@
+/*
+ * libjingle
+ * Copyright 2011, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_BASICPACKETSOCKETFACTORY_H_
+#define TALK_BASE_BASICPACKETSOCKETFACTORY_H_
+
+#include "talk/base/packetsocketfactory.h"
+
+namespace talk_base {
+
+class AsyncSocket;
+class SocketFactory;
+class Thread;
+
+class BasicPacketSocketFactory : public PacketSocketFactory {
+ public:
+  BasicPacketSocketFactory();
+  explicit BasicPacketSocketFactory(Thread* thread);
+  explicit BasicPacketSocketFactory(SocketFactory* socket_factory);
+  virtual ~BasicPacketSocketFactory();
+
+  virtual AsyncPacketSocket* CreateUdpSocket(
+      const SocketAddress& local_address, int min_port, int max_port);
+  virtual AsyncPacketSocket* CreateServerTcpSocket(
+      const SocketAddress& local_address, int min_port, int max_port, bool ssl);
+  virtual AsyncPacketSocket* CreateClientTcpSocket(
+      const SocketAddress& local_address, const SocketAddress& remote_address,
+      const ProxyInfo& proxy_info, const std::string& user_agent, bool ssl);
+
+ private:
+  int BindSocket(AsyncSocket* socket, const SocketAddress& local_address,
+                 int min_port, int max_port);
+
+  SocketFactory* socket_factory();
+
+  Thread* thread_;
+  SocketFactory* socket_factory_;
+};
+
+}  // namespace talk_base
+
+#endif  // TALK_BASE_BASICPACKETSOCKETFACTORY_H_
diff --git a/talk/base/packetsocketfactory.h b/talk/base/packetsocketfactory.h
new file mode 100644
index 0000000..7c74e86
--- /dev/null
+++ b/talk/base/packetsocketfactory.h
@@ -0,0 +1,60 @@
+/*
+ * libjingle
+ * Copyright 2011, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_PACKETSOCKETFACTORY_H_
+#define TALK_BASE_PACKETSOCKETFACTORY_H_
+
+#include "talk/base/proxyinfo.h"
+
+namespace talk_base {
+
+class AsyncPacketSocket;
+
+class PacketSocketFactory {
+ public:
+  PacketSocketFactory() { }
+  virtual ~PacketSocketFactory() { }
+
+  virtual AsyncPacketSocket* CreateUdpSocket(
+      const SocketAddress& address, int min_port, int max_port) = 0;
+  virtual AsyncPacketSocket* CreateServerTcpSocket(
+      const SocketAddress& local_address, int min_port, int max_port,
+      bool ssl) = 0;
+
+  // TODO: |proxy_info| and |user_agent| should be set
+  // per-factory and not when socket is created.
+  virtual AsyncPacketSocket* CreateClientTcpSocket(
+      const SocketAddress& local_address, const SocketAddress& remote_address,
+      const ProxyInfo& proxy_info, const std::string& user_agent, bool ssl) = 0;
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(PacketSocketFactory);
+};
+
+}  // namespace talk_base
+
+#endif  // TALK_BASE_PACKETSOCKETFACTORY_H_
diff --git a/talk/libjingle.scons b/talk/libjingle.scons
index b48fc65..5d77429 100644
--- a/talk/libjingle.scons
+++ b/talk/libjingle.scons
@@ -46,8 +46,8 @@
                "third_party/srtp/srtp/srtp.c",
              ],
              includedirs = [
-               "third_party/srtpinclude",
-               "third_party/srtpcrypto/include",
+               "third_party/srtp/include",
+               "third_party/srtp/crypto/include",
              ],
              win_ccflags = [
                "/wd4701",
@@ -64,6 +64,7 @@
                "sound/alsasymboltable.cc",
                "sound/linuxsoundsystem.cc",
                "sound/pulseaudiosoundsystem.cc",
+               "sound/pulseaudiosymboltable.cc",
                "sound/platformsoundsystem.cc",
                "sound/platformsoundsystemfactory.cc",
                "sound/soundsysteminterface.cc",
@@ -99,6 +100,7 @@
                "base/asyncudpsocket.cc",
                "base/autodetectproxy.cc",
                "base/base64.cc",
+               "base/basicpacketsocketfactory.cc",
                "base/bytebuffer.cc",
                "base/checks.cc",
                "base/common.cc",
@@ -209,8 +211,8 @@
              includedirs = [
                "third_party/libudev",
                "third_party/expat-2.0.1",
-               "third_party/srtpinclude",
-               "third_party/srtpcrypto/include",
+               "third_party/srtp/include",
+               "third_party/srtp/crypto/include",
              ],
              win_srcs = [
                "base/schanneladapter.cc",
@@ -222,6 +224,7 @@
                "base/win32window.cc",
                "base/winfirewall.cc",
                "base/winping.cc",
+               "session/phone/gdivideorenderer.cc",
              ],
 )
 talk.App(env, name = "login",
@@ -241,8 +244,28 @@
          lin_libs = [
            "libpthread",
            ":libssl.so.0.9.8",
+           "gdk_pixbuf-2.0",
+           "gdk-x11-2.0",
+           "glib-2.0",
+           "gobject-2.0",
+           "gtk-x11-2.0",
+           "videorenderer",
          ],
 )
+talk.Library(env, name = "videorenderer",
+             lin_srcs = [
+               "session/phone/gtkvideorenderer.cc",
+             ],
+             lin_includedirs = [
+               "/usr/include/atk-1.0",
+               "/usr/include/cairo",
+               "/usr/include/glib-2.0",
+               "/usr/include/gtk-2.0",
+               "/usr/include/pango-1.0",
+               "/usr/lib/glib-2.0/include",
+               "/usr/lib/gtk-2.0/include",
+             ],
+)
 talk.Library(env, name = "libxmpphelp",
              libs = [
                "libjingle",
@@ -279,10 +302,15 @@
            "FEATURE_ENABLE_VOICEMAIL",
          ],
          lin_libs = [
+           "gdk_pixbuf-2.0",
+           "gdk-x11-2.0",
+           "glib-2.0",
+           "gobject-2.0",
            "gtk-x11-2.0",
            "libasound",
            "libpthread",
            ":libssl.so.0.9.8",
+           "videorenderer",
          ],
          srcs = [
            "examples/call/call_main.cc",
diff --git a/talk/main.scons b/talk/main.scons
index 58c3bae..349c317 100644
--- a/talk/main.scons
+++ b/talk/main.scons
@@ -347,6 +347,7 @@
 linux_common_env = posix_env.Clone(
   tools = [
     'target_platform_linux',
+    'talk_libjingle',
     #'talk_linux',
   ],
 )
diff --git a/talk/session/phone/devicemanager.cc b/talk/session/phone/devicemanager.cc
index 283b84e..87cd8c9 100644
--- a/talk/session/phone/devicemanager.cc
+++ b/talk/session/phone/devicemanager.cc
@@ -113,14 +113,6 @@
   DeviceManager* manager_;
   void* impl_;
 };
-#elif defined(IOS) || defined(ANDROID)
-// We don't use DeviceWatcher on iOS or Android, so just stub out a noop class.
-class DeviceWatcher {
- public:
-  explicit DeviceWatcher(DeviceManager* dm) {}
-  bool Start() { return true; }
-  void Stop() {}
-};
 #endif
 
 #if !defined(LINUX) && !defined(IOS)
diff --git a/talk/session/phone/gdivideorenderer.cc b/talk/session/phone/gdivideorenderer.cc
new file mode 100644
index 0000000..6afb764
--- /dev/null
+++ b/talk/session/phone/gdivideorenderer.cc
@@ -0,0 +1,265 @@
+// libjingle
+// Copyright 2004 Google Inc.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//  1. Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//  2. Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//  3. The name of the author may not be used to endorse or promote products
+//     derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Implementation of GdiVideoRenderer on Windows
+
+#ifdef WIN32
+
+#include "talk/session/phone/gdivideorenderer.h"
+
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/thread.h"
+#include "talk/base/win32window.h"
+#include "talk/session/phone/videocommon.h"
+
+namespace cricket {
+
+/////////////////////////////////////////////////////////////////////////////
+// Definition of private class VideoWindow. We use a worker thread to manage
+// the window.
+/////////////////////////////////////////////////////////////////////////////
+class GdiVideoRenderer::VideoWindow : public talk_base::Win32Window {
+ public:
+  VideoWindow(int x, int y, int width, int height);
+  virtual ~VideoWindow();
+
+  // Called when the video size changes. If it is called the first time, we
+  // create and start the thread. Otherwise, we send kSetSizeMsg to the thread.
+  // Context: non-worker thread.
+  bool SetSize(int width, int height);
+
+  // Called when a new frame is available. Upon this call, we send
+  // kRenderFrameMsg to the window thread. Context: non-worker thread. It may be
+  // better to pass RGB bytes to VideoWindow. However, we pass VideoFrame to put
+  // all the thread synchronization within VideoWindow.
+  bool RenderFrame(const VideoFrame* frame);
+
+ protected:
+  // Override virtual method of talk_base::Win32Window. Context: worker Thread.
+  virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+                         LRESULT& result);
+
+ private:
+  enum { kSetSizeMsg = WM_USER, kRenderFrameMsg};
+
+  class WindowThread : public talk_base::Thread {
+   public:
+    explicit WindowThread(VideoWindow* window) : window_(window) {}
+
+    // Override virtual method of talk_base::Thread. Context: worker Thread.
+    virtual void Run() {
+      // Initialize the window
+      if (!window_ || !window_->Initialize()) {
+        return;
+      }
+      // Run the message loop
+      MSG msg;
+      while (GetMessage(&msg, NULL, 0, 0) > 0) {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+      }
+    }
+
+  private:
+    VideoWindow* window_;
+  };
+
+  // Context: worker Thread.
+  bool Initialize();
+  void OnPaint();
+  void OnSize(int width, int height, bool frame_changed);
+  void OnRenderFrame(const VideoFrame* frame);
+
+  BITMAPINFO bmi_;
+  talk_base::scoped_array<uint8> image_;
+  talk_base::scoped_ptr<WindowThread> window_thread_;
+  // The initial position of the window.
+  int initial_x_;
+  int initial_y_;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// Implementation of class VideoWindow
+/////////////////////////////////////////////////////////////////////////////
+GdiVideoRenderer::VideoWindow::VideoWindow(
+    int x, int y, int width, int height)
+    : initial_x_(x),
+      initial_y_(y) {
+  memset(&bmi_.bmiHeader, 0, sizeof(bmi_.bmiHeader));
+  bmi_.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+  bmi_.bmiHeader.biPlanes = 1;
+  bmi_.bmiHeader.biBitCount = 32;
+  bmi_.bmiHeader.biCompression = BI_RGB;
+  bmi_.bmiHeader.biWidth = width;
+  bmi_.bmiHeader.biHeight = -height;
+  bmi_.bmiHeader.biSizeImage = width * height * 4;
+
+  image_.reset(new uint8[bmi_.bmiHeader.biSizeImage]);
+}
+
+GdiVideoRenderer::VideoWindow::~VideoWindow() {
+  // Context: caller Thread. We cannot call Destroy() since the window was
+  // created by another thread. Instead, we send WM_CLOSE message.
+  if (handle()) {
+    SendMessage(handle(), WM_CLOSE, 0, 0);
+  }
+}
+
+bool GdiVideoRenderer::VideoWindow::SetSize(int width, int height) {
+  if (!window_thread_.get()) {
+    // Create and start the window thread.
+    window_thread_.reset(new WindowThread(this));
+    return window_thread_->Start();
+  } else if (width != bmi_.bmiHeader.biWidth ||
+      height != -bmi_.bmiHeader.biHeight) {
+    SendMessage(handle(), kSetSizeMsg, 0, MAKELPARAM(width, height));
+  }
+  return true;
+}
+
+bool GdiVideoRenderer::VideoWindow::RenderFrame(const VideoFrame* frame) {
+  if (!handle()) {
+    return false;
+  }
+
+  SendMessage(handle(), kRenderFrameMsg, reinterpret_cast<WPARAM>(frame), 0);
+  return true;
+}
+
+bool GdiVideoRenderer::VideoWindow::OnMessage(UINT uMsg, WPARAM wParam,
+                                              LPARAM lParam, LRESULT& result) {
+  switch (uMsg) {
+    case WM_PAINT:
+      OnPaint();
+      return true;
+
+    case WM_DESTROY:
+      PostQuitMessage(0);  // post WM_QUIT to end the message loop in Run()
+      return false;
+
+    case WM_SIZE:  // The window UI was resized.
+      OnSize(LOWORD(lParam), HIWORD(lParam), false);
+      return true;
+
+    case kSetSizeMsg:  // The video resolution changed.
+      OnSize(LOWORD(lParam), HIWORD(lParam), true);
+      return true;
+
+    case kRenderFrameMsg:
+      OnRenderFrame(reinterpret_cast<const VideoFrame*>(wParam));
+      return true;
+  }
+  return false;
+}
+
+bool GdiVideoRenderer::VideoWindow::Initialize() {
+  if (!talk_base::Win32Window::Create(
+      NULL, L"Video Renderer",
+      WS_OVERLAPPEDWINDOW | WS_SIZEBOX,
+      WS_EX_APPWINDOW,
+      initial_x_, initial_y_,
+      bmi_.bmiHeader.biWidth, -bmi_.bmiHeader.biHeight)) {
+        return false;
+  }
+  OnSize(bmi_.bmiHeader.biWidth, -bmi_.bmiHeader.biHeight, false);
+  return true;
+}
+
+void GdiVideoRenderer::VideoWindow::OnPaint() {
+  RECT rcClient;
+  GetClientRect(handle(), &rcClient);
+  PAINTSTRUCT ps;
+  HDC hdc = BeginPaint(handle(), &ps);
+  StretchDIBits(hdc,
+    0, 0, rcClient.right, rcClient.bottom,  // destination rect
+    0, 0, bmi_.bmiHeader.biWidth, -bmi_.bmiHeader.biHeight,  // source rect
+    image_.get(), &bmi_, DIB_RGB_COLORS, SRCCOPY);
+  EndPaint(handle(), &ps);
+}
+
+void GdiVideoRenderer::VideoWindow::OnSize(int width, int height,
+                                           bool frame_changed) {
+  // Get window and client sizes
+  RECT rcClient, rcWindow;
+  GetClientRect(handle(), &rcClient);
+  GetWindowRect(handle(), &rcWindow);
+
+  // Find offset between window size and client size
+  POINT ptDiff;
+  ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
+  ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
+
+  // Resize client
+  MoveWindow(handle(), rcWindow.left, rcWindow.top,
+             width + ptDiff.x, height + ptDiff.y, false);
+  UpdateWindow(handle());
+  ShowWindow(handle(), SW_SHOW);
+
+  if (frame_changed && (width != bmi_.bmiHeader.biWidth ||
+    height != -bmi_.bmiHeader.biHeight)) {
+    // Update the bmi and image buffer
+    bmi_.bmiHeader.biWidth = width;
+    bmi_.bmiHeader.biHeight = -height;
+    bmi_.bmiHeader.biSizeImage = width * height * 4;
+    image_.reset(new uint8[bmi_.bmiHeader.biSizeImage]);
+  }
+}
+
+void GdiVideoRenderer::VideoWindow::OnRenderFrame(const VideoFrame* frame) {
+  if (!frame) {
+    return;
+  }
+  // Convert frame to ARGB format, which is accepted by GDI
+  frame->ConvertToRgbBuffer(cricket::FOURCC_ARGB, image_.get(),
+                            bmi_.bmiHeader.biSizeImage,
+                            bmi_.bmiHeader.biWidth * 4);
+  InvalidateRect(handle(), 0, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Implementation of class GdiVideoRenderer
+/////////////////////////////////////////////////////////////////////////////
+GdiVideoRenderer::GdiVideoRenderer(int x, int y)
+    : initial_x_(x),
+      initial_y_(y) {
+}
+GdiVideoRenderer::~GdiVideoRenderer() {}
+
+bool GdiVideoRenderer::SetSize(int width, int height, int reserved) {
+  if (!window_.get()) {  // Create the window for the first frame
+    window_.reset(new VideoWindow(initial_x_, initial_y_, width, height));
+  }
+  return window_->SetSize(width, height);
+}
+
+bool GdiVideoRenderer::RenderFrame(const VideoFrame* frame) {
+  if (!frame || !window_.get()) {
+    return false;
+  }
+  return window_->RenderFrame(frame);
+}
+
+}  // namespace cricket
+#endif  // WIN32
diff --git a/talk/session/phone/gtkvideorenderer.cc b/talk/session/phone/gtkvideorenderer.cc
new file mode 100644
index 0000000..4e356b3
--- /dev/null
+++ b/talk/session/phone/gtkvideorenderer.cc
@@ -0,0 +1,114 @@
+// libjingle
+// Copyright 2004 Google Inc.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//  1. Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//  2. Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//  3. The name of the author may not be used to endorse or promote products
+//     derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Implementation of GtkVideoRenderer
+
+#include "talk/session/phone/gtkvideorenderer.h"
+
+#include <gtk/gtk.h>
+
+#include "talk/session/phone/videocommon.h"
+
+namespace cricket {
+
+GtkVideoRenderer::~GtkVideoRenderer() {
+  if (window_) {
+    gtk_widget_destroy(window_);
+    // Run the Gtk main loop to tear down the window.
+    Pump();
+  }
+  // Don't need to destroy draw_area_ because it is not top-level, so it is
+  // implicitly destroyed by the above.
+}
+
+bool GtkVideoRenderer::SetSize(int width, int height, int reserved) {
+  // For the first frame, initialize the GTK window
+  if (!window_ && !Initialize(width, height)) {
+    return false;
+  }
+
+  image_.reset(new uint8[width * height * 4]);
+  gtk_window_resize(GTK_WINDOW(window_), width, height);
+  return true;
+}
+
+bool GtkVideoRenderer::RenderFrame(const VideoFrame* frame) {
+  if (!frame) {
+    return false;
+  }
+
+  if (!GTK_IS_WINDOW(window_) || !GTK_IS_DRAWING_AREA(draw_area_)) {
+    // window was closed
+    return false;
+  }
+
+  // convert I420 frame to ABGR format, which is accepted by GTK
+  frame->ConvertToRgbBuffer(cricket::FOURCC_ABGR,
+                            image_.get(),
+                            frame->GetWidth() * frame->GetHeight() * 4,
+                            frame->GetWidth() * 4);
+
+  // draw the ABGR image
+  gdk_draw_rgb_32_image(draw_area_->window,
+                        draw_area_->style->fg_gc[GTK_STATE_NORMAL],
+                        0,
+                        0,
+                        frame->GetWidth(),
+                        frame->GetHeight(),
+                        GDK_RGB_DITHER_MAX,
+                        image_.get(),
+                        frame->GetWidth() * 4);
+
+  // Run the Gtk main loop to refresh the window.
+  Pump();
+  return true;
+}
+
+bool GtkVideoRenderer::Initialize(int width, int height) {
+  gtk_init(NULL, NULL);
+  window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  draw_area_ = gtk_drawing_area_new();
+  if (!window_ || !draw_area_) {
+    return false;
+  }
+
+  gtk_window_set_position(GTK_WINDOW(window_), GTK_WIN_POS_CENTER);
+  gtk_window_set_title(GTK_WINDOW(window_), "Video Renderer");
+  gtk_widget_set_size_request(draw_area_, width, height);
+  gtk_container_add(GTK_CONTAINER(window_), draw_area_);
+  gtk_widget_show_all(window_);
+  gtk_window_move(GTK_WINDOW(window_), initial_x_, initial_y_);
+
+  image_.reset(new uint8[width * height * 4]);
+  return true;
+}
+
+void GtkVideoRenderer::Pump() {
+  while (gtk_events_pending()) {
+    gtk_main_iteration();
+  }
+}
+
+}  // namespace cricket
diff --git a/talk/site_scons/site_tools/talk_libjingle.py b/talk/site_scons/site_tools/talk_libjingle.py
new file mode 100644
index 0000000..3086614
--- /dev/null
+++ b/talk/site_scons/site_tools/talk_libjingle.py
@@ -0,0 +1,45 @@
+# Copyright 2010 Google Inc.
+# All Rights Reserved.
+# Author: thaloun@google.com (Tim Haloun)
+
+"""Tools that we need to include with libjingle."""
+
+import subprocess
+
+# We need this in libjingle because main.scons depends on it and
+# libjingle depends on main.scons.
+def EnableFeatureWherePackagePresent(env, bit, cpp_flag, package):
+  """Enable a feature if a required pkg-config package is present.
+
+  Args:
+    env: The current SCons environment.
+    bit: The name of the Bit to enable when the package is present.
+    cpp_flag: The CPP flag to enable when the package is present.
+    package: The name of the package.
+  """
+  if not env.Bit('host_linux'):
+    return
+  if _HavePackage(package):
+    env.SetBits(bit)
+    env.Append(CPPDEFINES = [cpp_flag])
+  else:
+    print ("Warning: Package \"%s\" not found. Feature \"%s\" will not be "
+           "built. To build with this feature, install the package that "
+           "provides the \"%s.pc\" file.") % (package, bit, package)
+
+def _HavePackage(package):
+  """Whether the given pkg-config package name is present on the build system.
+
+  Args:
+    package: The name of the package.
+
+  Return:
+    True if the package is present, else False
+  """
+  return subprocess.call(["pkg-config", "--exists", package]) == 0
+
+def generate(env):
+  env.AddMethod(EnableFeatureWherePackagePresent)
+
+def exists(env):
+  return 1
diff --git a/talk/sound/pulseaudiosymboltable.cc b/talk/sound/pulseaudiosymboltable.cc
new file mode 100644
index 0000000..008b003
--- /dev/null
+++ b/talk/sound/pulseaudiosymboltable.cc
@@ -0,0 +1,39 @@
+/*
+ * libjingle
+ * Copyright 2004--2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/sound/pulseaudiosymboltable.h"
+
+namespace cricket {
+
+LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(PulseAudioSymbolTable, "libpulse.so.0")
+#define X(sym) \
+    LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(PulseAudioSymbolTable, sym)
+PULSE_AUDIO_SYMBOLS_LIST
+#undef X
+LATE_BINDING_SYMBOL_TABLE_DEFINE_END(PulseAudioSymbolTable)
+
+}  // namespace cricket
diff --git a/talk/sound/pulseaudiosymboltable.h b/talk/sound/pulseaudiosymboltable.h
new file mode 100644
index 0000000..21be35e
--- /dev/null
+++ b/talk/sound/pulseaudiosymboltable.h
@@ -0,0 +1,99 @@
+/*
+ * libjingle
+ * Copyright 2004--2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_SOUND_PULSEAUDIOSYMBOLTABLE_H_
+#define TALK_SOUND_PULSEAUDIOSYMBOLTABLE_H_
+
+#include "talk/base/latebindingsymboltable.h"
+
+namespace cricket {
+
+// The PulseAudio symbols we need, as an X-Macro list.
+// This list must contain precisely every libpulse function that is used in
+// pulseaudiosoundsystem.cc.
+#define PULSE_AUDIO_SYMBOLS_LIST \
+  X(pa_bytes_per_second) \
+  X(pa_context_connect) \
+  X(pa_context_disconnect) \
+  X(pa_context_errno) \
+  X(pa_context_get_protocol_version) \
+  X(pa_context_get_server_info) \
+  X(pa_context_get_sink_info_list) \
+  X(pa_context_get_sink_input_info) \
+  X(pa_context_get_source_info_by_index) \
+  X(pa_context_get_source_info_list) \
+  X(pa_context_get_state) \
+  X(pa_context_new) \
+  X(pa_context_set_sink_input_volume) \
+  X(pa_context_set_source_volume_by_index) \
+  X(pa_context_set_state_callback) \
+  X(pa_context_unref) \
+  X(pa_cvolume_set) \
+  X(pa_operation_get_state) \
+  X(pa_operation_unref) \
+  X(pa_stream_connect_playback) \
+  X(pa_stream_connect_record) \
+  X(pa_stream_disconnect) \
+  X(pa_stream_drop) \
+  X(pa_stream_get_device_index) \
+  X(pa_stream_get_index) \
+  X(pa_stream_get_latency) \
+  X(pa_stream_get_sample_spec) \
+  X(pa_stream_get_state) \
+  X(pa_stream_new) \
+  X(pa_stream_peek) \
+  X(pa_stream_readable_size) \
+  X(pa_stream_set_buffer_attr) \
+  X(pa_stream_set_overflow_callback) \
+  X(pa_stream_set_read_callback) \
+  X(pa_stream_set_state_callback) \
+  X(pa_stream_set_underflow_callback) \
+  X(pa_stream_set_write_callback) \
+  X(pa_stream_unref) \
+  X(pa_stream_writable_size) \
+  X(pa_stream_write) \
+  X(pa_strerror) \
+  X(pa_threaded_mainloop_free) \
+  X(pa_threaded_mainloop_get_api) \
+  X(pa_threaded_mainloop_lock) \
+  X(pa_threaded_mainloop_new) \
+  X(pa_threaded_mainloop_signal) \
+  X(pa_threaded_mainloop_start) \
+  X(pa_threaded_mainloop_stop) \
+  X(pa_threaded_mainloop_unlock) \
+  X(pa_threaded_mainloop_wait)
+
+LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(PulseAudioSymbolTable)
+#define X(sym) \
+    LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(PulseAudioSymbolTable, sym)
+PULSE_AUDIO_SYMBOLS_LIST
+#undef X
+LATE_BINDING_SYMBOL_TABLE_DECLARE_END(PulseAudioSymbolTable)
+
+}  // namespace cricket
+
+#endif  // TALK_SOUND_PULSEAUDIOSYMBOLTABLE_H_