Make new snapshot with a number of updates:

- new features for examples/call
  - request bandwidth
  - turn on/off SSL
  - control signaling protocol
  - send chat message
- add support for call encryption
- handle jingle candidates in session-initiate or session-accept

git-svn-id: http://libjingle.googlecode.com/svn/trunk@37 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/talk/base/fileutils.h b/talk/base/fileutils.h
index 8ad631f..217cd83 100644
--- a/talk/base/fileutils.h
+++ b/talk/base/fileutils.h
@@ -339,6 +339,10 @@
     return EnsureDefaultFilesystem()->MoveFile(old_path, new_path);
   }
 
+  static bool CopyFolder(const Pathname &old_path, const Pathname &new_path) {
+    return EnsureDefaultFilesystem()->CopyFolder(old_path, new_path);
+  }
+
   static bool CopyFile(const Pathname &old_path, const Pathname &new_path) {
     return EnsureDefaultFilesystem()->CopyFile(old_path, new_path);
   }
diff --git a/talk/base/helpers.cc b/talk/base/helpers.cc
index 94ca57e..f8d8a9f 100644
--- a/talk/base/helpers.cc
+++ b/talk/base/helpers.cc
@@ -32,7 +32,7 @@
 #include <windows.h>
 #include <ntsecapi.h>
 #else
-#ifdef USE_OPENSSL
+#ifdef SSL_USE_OPENSSL
 #include <openssl/rand.h>
 #endif
 #endif
@@ -98,7 +98,7 @@
   RtlGenRandomProc rtl_gen_random_;
 };
 #else
-#ifndef USE_OPENSSL
+#ifndef SSL_USE_OPENSSL
 // The old RNG.
 class SecureRandomGenerator : public RandomGenerator {
  public:
@@ -139,14 +139,14 @@
   virtual bool Init(const void* seed, size_t len) {
     // By default, seed from the system state.
     if (!inited_) {
-      if (RAND_poll() != 0) {
+      if (RAND_poll() <= 0) {
         return false;
       }
       inited_ = true;
     }
     // Allow app data to be mixed in, if provided.
     if (seed) {
-      RAND_add(seed, len);
+      RAND_seed(seed, len);
     }
     return true;
   }
@@ -154,13 +154,13 @@
     if (!inited_ && !Init(NULL, 0)) {
       return false;
     }
-    return (RAND_bytes(buf, len) == 0);
+    return (RAND_bytes(reinterpret_cast<unsigned char*>(buf), len) > 0);
   }
 
  private:
   bool inited_;
 };
-#endif  // USE_OPENSSL
+#endif  // SSL_USE_OPENSSL
 #endif  // WIN32
 
 // A test random generator, for predictable output.
@@ -220,14 +220,22 @@
 
 std::string CreateRandomString(size_t len) {
   std::string str;
+  CreateRandomString(len, &str);
+  return str;
+}
+
+bool CreateRandomString(size_t len, std::string* str) {
+  str->clear();
   scoped_array<uint8> bytes(new uint8[len]);
   if (!g_rng->Generate(bytes.get(), len)) {
     LOG(LS_ERROR) << "Failed to generate random string!";
+    return false;
   }
-  for (size_t i = 0; i < len; i++) {
-    str.push_back(BASE64[bytes[i] & 63]);
+  str->reserve(len);
+  for (size_t i = 0; i < len; ++i) {
+    str->push_back(BASE64[bytes[i] & 63]);
   }
-  return str;
+  return true;
 }
 
 uint32 CreateRandomId() {
diff --git a/talk/base/helpers.h b/talk/base/helpers.h
index 878efc1..83a3c7f 100644
--- a/talk/base/helpers.h
+++ b/talk/base/helpers.h
@@ -42,8 +42,14 @@
 
 // Generates a (cryptographically) random string of the given length.
 // We generate base64 values so that they will be printable.
+// WARNING: could silently fail. Use the version below instead.
 std::string CreateRandomString(size_t length);
 
+// Generates a (cryptographically) random string of the given length.
+// We generate base64 values so that they will be printable.
+// Return false if the random number generator failed.
+bool CreateRandomString(size_t length, std::string* str);
+
 // Generates a random id.
 uint32 CreateRandomId();
 
diff --git a/talk/base/linux.cc b/talk/base/linux.cc
index 2061d73..03d486b 100644
--- a/talk/base/linux.cc
+++ b/talk/base/linux.cc
@@ -14,6 +14,10 @@
 
 namespace talk_base {
 
+static const char kCpuInfoFile[] = "/proc/cpuinfo";
+static const char kCpuMaxFreqFile[] =
+    "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
+
 ProcCpuInfo::ProcCpuInfo() {
 }
 
@@ -22,7 +26,7 @@
 
 bool ProcCpuInfo::LoadFromSystem() {
   ConfigParser procfs;
-  if (!procfs.Open("/proc/cpuinfo"))
+  if (!procfs.Open(kCpuInfoFile))
     return false;
   return procfs.Parse(&cpu_info_);
 };
@@ -214,6 +218,15 @@
   return sstr.str();
 }
 
+int ReadCpuMaxFreq() {
+  FileStream fs;
+  std::string str;
+  if (!fs.Open(kCpuMaxFreqFile, "r") || SR_SUCCESS != fs.ReadLine(&str)) {
+    return -1;
+  }
+  return atoi(str.c_str());
+}
+
 }  // namespace talk_base
 
 #endif  // LINUX
diff --git a/talk/base/linux.h b/talk/base/linux.h
index 808dc0e..37b845b 100644
--- a/talk/base/linux.h
+++ b/talk/base/linux.h
@@ -91,6 +91,11 @@
 // Returns the output of "uname".
 std::string ReadLinuxUname();
 
+// Returns the content (int) of
+// /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
+// Returns -1 on error.
+int ReadCpuMaxFreq();
+
 }  // namespace talk_base
 
 #endif  // LINUX
diff --git a/talk/base/macutils.cc b/talk/base/macutils.cc
index 69a2f5d..fefe2b9 100644
--- a/talk/base/macutils.cc
+++ b/talk/base/macutils.cc
@@ -144,13 +144,77 @@
   int ver;
   if (!GetGestalt(gestaltQuickTimeVersion, &ver))
     return false;
-  
+
   std::stringstream ss;
   ss << std::hex << ver;
   *out = ss.str();
   return true;
 }
 
+bool RunAppleScript(const std::string& script) {
+  ComponentInstance component = NULL;
+  AEDesc script_desc;
+  AEDesc result_data;
+  OSStatus err;
+  OSAID script_id, result_id;
+
+  AECreateDesc(typeNull, NULL, 0, &script_desc);
+  AECreateDesc(typeNull, NULL, 0, &result_data);
+  script_id = kOSANullScript;
+  result_id = kOSANullScript;
+
+  component = OpenDefaultComponent(kOSAComponentType, typeAppleScript);
+  if (component == NULL) {
+    LOG(LS_ERROR) << "Failed opening Apple Script component";
+    return false;
+  }
+  err = AECreateDesc(typeUTF8Text, script.data(), script.size(), &script_desc);
+  if (err != noErr) {
+    CloseComponent(component);
+    LOG(LS_ERROR) << "Failed creating Apple Script description";
+    return false;
+  }
+
+  err = OSACompile(component, &script_desc, kOSAModeCanInteract, &script_id);
+  if (err != noErr) {
+    AEDisposeDesc(&script_desc);
+    if (script_id != kOSANullScript) {
+      OSADispose(component, script_id);
+    }
+    CloseComponent(component);
+    LOG(LS_ERROR) << "Error compiling Apple Script";
+    return false;
+  }
+
+  err = OSAExecute(component, script_id, kOSANullScript, kOSAModeCanInteract,
+		   &result_id);
+
+  if (err == errOSAScriptError) {
+    LOG(LS_ERROR) << "Error when executing Apple Script: " << script;
+    AECreateDesc(typeNull, NULL, 0, &result_data);
+    OSAScriptError(component, kOSAErrorMessage, typeChar, &result_data);
+    int len = AEGetDescDataSize(&result_data);
+    char* data = (char*) malloc(len);
+    if (data != NULL) {
+      err = AEGetDescData(&result_data, data, len);
+      LOG(LS_ERROR) << "Script error: " << data;
+    }
+    AEDisposeDesc(&script_desc);
+    AEDisposeDesc(&result_data);
+    return false;
+  }
+  AEDisposeDesc(&script_desc);
+  if (script_id != kOSANullScript) {
+    OSADispose(component, script_id);
+  }
+  if (result_id != kOSANullScript) {
+    OSADispose(component, result_id);
+  }
+  CloseComponent(component);
+  return true;
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////
 
 }  // namespace talk_base
diff --git a/talk/base/macutils.h b/talk/base/macutils.h
index 1cd64ab..30bb300 100644
--- a/talk/base/macutils.h
+++ b/talk/base/macutils.h
@@ -55,6 +55,10 @@
 MacOSVersionName GetOSVersionName();
 bool GetQuickTimeVersion(std::string* version);
 
+// Runs the given apple script. Only supports scripts that does not
+// require user interaction.
+bool RunAppleScript(const std::string& script);
+
 ///////////////////////////////////////////////////////////////////////////////
 
 }  // namespace talk_base
diff --git a/talk/examples/call/call_main.cc b/talk/examples/call/call_main.cc
index dde0784..541aca8 100644
--- a/talk/examples/call/call_main.cc
+++ b/talk/examples/call/call_main.cc
@@ -36,6 +36,7 @@
 #include "talk/base/stream.h"
 #include "talk/base/ssladapter.h"
 #include "talk/base/win32socketserver.h"
+#include "talk/p2p/base/constants.h"
 #include "talk/xmpp/xmppclientsettings.h"
 #include "talk/examples/login/xmppthread.h"
 #include "talk/examples/login/xmppauth.h"
@@ -194,7 +195,7 @@
   // the the input voice and video streams.
   std::vector<cricket::AudioCodec> voice_codecs;
   voice_codecs.push_back(
-      cricket::AudioCodec(9, "G722", 16000, 64000, 1, 0));
+      cricket::AudioCodec(9, "G722", 16000, 0, 1, 0));
   file_media_engine->set_voice_codecs(voice_codecs);
   std::vector<cricket::VideoCodec> video_codecs;
   video_codecs.push_back(
@@ -214,7 +215,11 @@
   // define options
   DEFINE_bool(a, false, "Turn on auto accept.");
   DEFINE_bool(d, false, "Turn on debugging.");
+  DEFINE_string(
+      protocol, "hybrid",
+      "Initial signaling protocol to use: jingle, gingle, or hybrid.");
   DEFINE_bool(testserver, false, "Use test server");
+  DEFINE_bool(plainserver, false, "Turn off tls and allow plain password.");
   DEFINE_int(portallocator, 0, "Filter out unwanted connection types.");
   DEFINE_string(filterhost, NULL, "Filter out the host from all candidates.");
   DEFINE_string(pmuc, "groupchat.google.com", "The persistant muc domain.");
@@ -234,11 +239,25 @@
 
   bool auto_accept = FLAG_a;
   bool debug = FLAG_d;
+  std::string protocol = FLAG_protocol;
   bool test_server = FLAG_testserver;
+  bool plain_server = FLAG_plainserver;
   int32 portallocator_flags = FLAG_portallocator;
   std::string pmuc_domain = FLAG_pmuc;
   std::string server = FLAG_s;
 
+  cricket::SignalingProtocol initial_protocol = cricket::PROTOCOL_HYBRID;
+  if (protocol == "jingle") {
+    initial_protocol = cricket::PROTOCOL_JINGLE;
+  } else if (protocol == "gingle") {
+    initial_protocol = cricket::PROTOCOL_GINGLE;
+  } else if (protocol == "hybrid") {
+    initial_protocol = cricket::PROTOCOL_HYBRID;
+  } else {
+    printf("Invalid protocol.  Must be jingle, gingle, or hybrid.");
+    return 1;
+  }
+
   // parse username and password, if present
   buzz::Jid jid;
   std::string username;
@@ -279,6 +298,10 @@
   xcs.set_host(jid.domain());
   xcs.set_use_tls(!test_server);
 
+  if (plain_server) {
+    xcs.set_use_tls(false);
+    xcs.set_allow_plain(true);
+  }
   if (test_server) {
     pass.password() = jid.node();
     xcs.set_allow_plain(true);
@@ -329,6 +352,8 @@
   client->SetAutoAccept(auto_accept);
   client->SetPmucDomain(pmuc_domain);
   client->SetPortAllocatorFlags(portallocator_flags);
+  client->SetAllowLocalIps(true);
+  client->SetInitialProtocol(initial_protocol);
   console->Start();
 
   if (debug) {
diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc
index 03edf95..3657420 100644
--- a/talk/examples/call/callclient.cc
+++ b/talk/examples/call/callclient.cc
@@ -172,6 +172,17 @@
   } else {
     if ((words.size() == 1) && (words[0] == "roster")) {
       PrintRoster();
+    } else if ((words.size() >= 2) && (words[0] == "send")) {
+      buzz::Jid jid(words[1]);
+      if (jid.IsValid()) {
+        last_sent_to_ = words[1];
+        SendChat(words[1], words[2]);
+      } else if (!last_sent_to_.empty()) {
+        SendChat(last_sent_to_, words[1]);
+      } else {
+        console_->Printf(
+            "Invalid JID. JIDs should be in the form user@domain\n");
+      }
     } else if ((words.size() == 2) && (words[0] == "friend")) {
       InviteFriend(words[1]);
     } else if ((words.size() >= 1) && (words[0] == "call")) {
@@ -201,7 +212,8 @@
       call_(NULL), incoming_call_(false),
       auto_accept_(false), pmuc_domain_("groupchat.google.com"),
       local_renderer_(NULL), remote_renderer_(NULL),
-      roster_(new RosterMap), portallocator_flags_(0)
+      roster_(new RosterMap), portallocator_flags_(0),
+      allow_local_ips_(false), initial_protocol_(cricket::PROTOCOL_HYBRID)
 #ifdef USE_TALK_SOUND
       , sound_system_factory_(NULL)
 #endif
@@ -309,6 +321,8 @@
       port_allocator_, worker_thread_);
   session_manager_->SignalRequestSignaling.connect(
       this, &CallClient::OnRequestSignaling);
+  session_manager_->SignalSessionCreate.connect(
+      this, &CallClient::OnSessionCreate);
   session_manager_->OnSignalingReady();
 
   session_manager_task_ =
@@ -348,6 +362,11 @@
   session_manager_->OnSignalingReady();
 }
 
+void CallClient::OnSessionCreate(cricket::Session* session, bool initiate) {
+  session->set_allow_local_ips(allow_local_ips_);
+  session->set_current_protocol(initial_protocol_);
+}
+
 void CallClient::OnCallCreate(cricket::Call* call) {
   call->SignalSessionState.connect(this, &CallClient::OnSessionState);
   if (call->video()) {
@@ -458,6 +477,19 @@
   console_->SetPrompting(true);
 }
 
+void CallClient::SendChat(const std::string& to, const std::string msg) {
+  buzz::XmlElement* stanza = new buzz::XmlElement(buzz::QN_MESSAGE);
+  stanza->AddAttr(buzz::QN_TO, to);
+  stanza->AddAttr(buzz::QN_ID, talk_base::CreateRandomString(16));
+  stanza->AddAttr(buzz::QN_TYPE, "chat");
+  buzz::XmlElement* body = new buzz::XmlElement(buzz::QN_BODY);
+  body->SetBodyText(msg);
+  stanza->AddElement(body);
+
+  xmpp_client_->SendStanza(stanza);
+  delete stanza;
+}
+
 void CallClient::InviteFriend(const std::string& name) {
   buzz::Jid jid(name);
   if (!jid.IsValid() || jid.node() == "") {
diff --git a/talk/examples/call/callclient.h b/talk/examples/call/callclient.h
index af27f34..b7dc716 100644
--- a/talk/examples/call/callclient.h
+++ b/talk/examples/call/callclient.h
@@ -67,6 +67,7 @@
 class Receiver;
 class Call;
 class SessionManagerTask;
+enum SignalingProtocol;
 }
 
 struct RosterItem {
@@ -98,11 +99,20 @@
 
   void ParseLine(const std::string &str);
 
+  void SendChat(const std::string& to, const std::string msg);
   void InviteFriend(const std::string& user);
   void JoinMuc(const std::string& room);
   void InviteToMuc(const std::string& user, const std::string& room);
   void LeaveMuc(const std::string& room);
   void SetPortAllocatorFlags(uint32 flags) { portallocator_flags_ = flags; }
+  void SetAllowLocalIps(bool allow_local_ips) {
+    allow_local_ips_ = allow_local_ips;
+  }
+
+  void SetInitialProtocol(cricket::SignalingProtocol initial_protocol) {
+    initial_protocol_ = initial_protocol;
+  }
+
 
   typedef std::map<buzz::Jid, buzz::Muc*> MucMap;
 
@@ -119,6 +129,7 @@
   void InitPresence();
   void RefreshStatus();
   void OnRequestSignaling();
+  void OnSessionCreate(cricket::Session* session, bool initiate);
   void OnCallCreate(cricket::Call* call);
   void OnCallDestroy(cricket::Call* call);
   void OnSessionState(cricket::Call* call,
@@ -178,6 +189,10 @@
   buzz::FriendInviteSendTask* friend_invite_send_;
   RosterMap* roster_;
   uint32 portallocator_flags_;
+
+  bool allow_local_ips_;
+  cricket::SignalingProtocol initial_protocol_;
+  std::string last_sent_to_;
 #ifdef USE_TALK_SOUND
   cricket::SoundSystemFactory* sound_system_factory_;
 #endif
diff --git a/talk/main.scons b/talk/main.scons
index d4163df..54b863a 100644
--- a/talk/main.scons
+++ b/talk/main.scons
@@ -17,6 +17,7 @@
 #
 import talk
 import os
+import platform
 
 #-------------------------------------------------------------------------------
 # The build files/directories to 'build'.
@@ -40,6 +41,7 @@
     'component_setup',
     'replace_strings',
     'talk_noops',
+    #'talk_linux',
   ],
   BUILD_SCONSCRIPTS = components,
   DESTINATION_ROOT = '$MAIN_DIR/build',
@@ -60,6 +62,16 @@
   ]
 )
 
+# This is where we set common environments
+#
+# Detect if running on 64 bit or 32 bit host.
+DeclareBit('platform_arch_64bit', 'Host Platform is 64 Bit')
+if platform.architecture()[0] == "64bit":
+  root_env.SetBits('platform_arch_64bit')
+
+DeclareBit('use_static_openssl', 'Build OpenSSL as a static library')
+
+
 #-------------------------------------------------------------------------------
 # W I N D O W S
 #
@@ -211,6 +223,8 @@
     'POSIX',
     'DISABLE_DYNAMIC_CAST',
     'HAVE_OPENSSL_SSL_H=1',
+    # The POSIX standard says we have to define this.
+    '_REENTRANT',
   ],
   CCFLAGS = [
     '-m32',
@@ -271,13 +285,9 @@
   BUILD_TYPE = 'dbg',
   BUILD_TYPE_DESCRIPTION = 'Mac debug build',
   BUILD_GROUPS = ['default', 'all'],
-  tools = ['target_debug']
+  tools = ['target_debug'],
 )
 mac_dbg_env.Append(
-  CPPDEFINES = [
-    'FLAVOR_DBG',
-    'ENABLE_DEBUG',
-  ],
   CCFLAGS = [
     '-O0',
   ]
@@ -288,7 +298,7 @@
   BUILD_TYPE = 'opt',
   BUILD_TYPE_DESCRIPTION = 'Mac opt build',
   BUILD_GROUPS = ['all'],
-  tools = ['target_optimized']
+  tools = ['target_optimized'],
 )
 mac_opt_env.Append(
   CCFLAGS = [
@@ -303,45 +313,92 @@
 #-------------------------------------------------------------------------------
 # L I N U X
 #
-linux_env = posix_env.Clone(
-  tools = ['target_platform_linux'],
+linux_common_env = posix_env.Clone(
+  tools = [
+    'target_platform_linux',
+    #'talk_linux',
+  ],
 )
 
-linux_env.Append(
+linux_common_env.Append(
   CPPDEFINES = [
     'LINUX',
     'HAVE_GLIB',
     # TODO() Enable once we figure out multiple defines with gips lib
     # Also consider other linux flags:  64bit, no-strict-aliasing, wrap, etc
+    #'USE_TALK_SOUND',
+  ],
+  CCFLAGS = [
+    # TODO: Some or all of this may be desirable for Mac too.
+    # Needed for link-time dead-code removal to work properly.
+    '-ffunction-sections',
+    '-fdata-sections',
+    # Needed for a clean ABI and for link-time dead-code removal to work
+    # properly.
+    '-fvisibility=hidden',
+    # Generate debugging info in the DWARF2 format.
+    '-gdwarf-2',
+    # Generate maximal debugging information. (It is stripped from what we ship
+    # to users, so we want it for both dbg and opt.)
+    '-g3',
   ],
   LINKFLAGS = [
-    # TODO consider enabling gc-sections.  Perhaps only in opt.
-    #'-Wl,--gc-sections',
+    # Enable dead-code removal.
+    '-Wl,--gc-sections',
     '-Wl,--start-group',
   ],
   _LIBFLAGS = ['-Wl,--end-group'],
 )
 
+# Remove default rpath set by Hammer. Hammer sets it to LIB_DIR, which is wrong.
+# The rpath is the _run-time_ library search path for the resulting binary, i.e.
+# the one used by ld.so at load time. Setting it equal to the path to build
+# output on the build machine is nonsense.
+linux_common_env.Replace(
+  RPATH = [],
+)
+
+#-------------------------------------------------------------------------------
+# L I N U X -- T R A D I T I O N A L
+#
+# Settings that are specific to our desktop Linux targets.
+linux_env = linux_common_env.Clone()
+# OpenSSL has infamously poor ABI stability, so that building against one
+# version and running against a different one often will not work. Since our
+# non-ChromeOS Linux builds are used on many different distros and distro
+# versions, this means we can't safely dynamically link to OpenSSL because the
+# product would end up being broken on any computer with a different version
+# installed. So instead we build it ourself and statically link to it.
+linux_env.SetBits('use_static_openssl')
+
 linux_dbg_env = linux_env.Clone(
   BUILD_TYPE = 'dbg',
   BUILD_TYPE_DESCRIPTION = 'Linux debug build',
   BUILD_GROUPS = ['default', 'all'],
-  tools = ['target_debug']
+  tools = ['target_debug'],
 )
+# Remove -g set by hammer, which is not what we want (we have set -g3 above).
+linux_dbg_env.FilterOut(CCFLAGS = ['-g'])
 envs.append(linux_dbg_env)
 
 linux_opt_env = linux_env.Clone(
   BUILD_TYPE = 'opt',
   BUILD_TYPE_DESCRIPTION = 'Linux optimized build',
   BUILD_GROUPS = ['all'],
-  tools = ['target_optimized']
+  tools = ['target_optimized'],
 )
+# Remove -O2 set by hammer, which is not what we want.
+linux_opt_env.FilterOut(CCFLAGS = ['-O2'])
+linux_opt_env.Append(CCFLAGS = ['-Os'])
 envs.append(linux_opt_env)
 
 
 
 # TODO(): Clone linux envs for 64bit.  See 'variant' documentation.
 
+# Create a group for installers
+AddTargetGroup('all_installers', 'installers that can be built')
+
 # Parse child .scons files
 BuildEnvironments(envs)
 
diff --git a/talk/p2p/base/constants.cc b/talk/p2p/base/constants.cc
index 9e45457..7430140 100644
--- a/talk/p2p/base/constants.cc
+++ b/talk/p2p/base/constants.cc
@@ -25,7 +25,10 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <string>
+
 #include "talk/p2p/base/constants.h"
+#include "talk/xmllite/qname.h"
 
 namespace cricket {
 
@@ -107,6 +110,17 @@
 const buzz::QName QN_GINGLE_VIDEO_SRCID(true, NS_GINGLE_VIDEO, "src-id");
 const buzz::QName QN_GINGLE_VIDEO_BANDWIDTH(true, NS_GINGLE_VIDEO, "bandwidth");
 
+// Crypto support.
+const buzz::QName QN_ENCRYPTION(true, NS_JINGLE_RTP, "encryption");
+const buzz::QName QN_ENCRYPTION_REQUIRED(true, NS_EMPTY, "required");
+const buzz::QName QN_CRYPTO(true, NS_JINGLE_RTP, "crypto");
+const buzz::QName QN_GINGLE_AUDIO_CRYPTO_USAGE(true, NS_GINGLE_AUDIO, "usage");
+const buzz::QName QN_GINGLE_VIDEO_CRYPTO_USAGE(true, NS_GINGLE_VIDEO, "usage");
+const buzz::QName QN_CRYPTO_SUITE(true, NS_EMPTY, "crypto-suite");
+const buzz::QName QN_CRYPTO_KEY_PARAMS(true, NS_EMPTY, "key-params");
+const buzz::QName QN_CRYPTO_TAG(true, NS_EMPTY, "tag");
+const buzz::QName QN_CRYPTO_SESSION_PARAMS(true, NS_EMPTY, "session-params");
+
 // transports and candidates
 const std::string LN_TRANSPORT("transport");
 const std::string LN_CANDIDATE("candidate");
diff --git a/talk/p2p/base/constants.h b/talk/p2p/base/constants.h
index 7e33c5b..d4e600b 100644
--- a/talk/p2p/base/constants.h
+++ b/talk/p2p/base/constants.h
@@ -36,13 +36,6 @@
 
 namespace cricket {
 
-// There are 3 different types of Jingle messages or protocols: Jingle
-// (the spec in XEP-166, etc), Gingle (the legacy protocol) and hybrid
-// (both at the same time).  Gingle2 is a temporary protocol that we
-// are only keeping around right now during this refactoring phase.
-// Once we finish refactoring and start implementing Jingle, we will
-// remove Gingle2.
-
 // NS_ == namespace
 // QN_ == buzz::QName (namespace + name)
 // LN_ == "local name" == QName::LocalPart()
@@ -133,6 +126,17 @@
 extern const buzz::QName QN_GINGLE_VIDEO_SRCID;
 extern const buzz::QName QN_GINGLE_VIDEO_BANDWIDTH;
 
+// Crypto support.
+extern const buzz::QName QN_ENCRYPTION;
+extern const buzz::QName QN_ENCRYPTION_REQUIRED;
+extern const buzz::QName QN_CRYPTO;
+extern const buzz::QName QN_GINGLE_AUDIO_CRYPTO_USAGE;
+extern const buzz::QName QN_GINGLE_VIDEO_CRYPTO_USAGE;
+extern const buzz::QName QN_CRYPTO_SUITE;
+extern const buzz::QName QN_CRYPTO_KEY_PARAMS;
+extern const buzz::QName QN_CRYPTO_TAG;
+extern const buzz::QName QN_CRYPTO_SESSION_PARAMS;
+
 // transports and candidates
 extern const std::string LN_TRANSPORT;
 extern const std::string LN_CANDIDATE;
diff --git a/talk/p2p/base/parsing.cc b/talk/p2p/base/parsing.cc
index 1ece5d0..da4c31b 100644
--- a/talk/p2p/base/parsing.cc
+++ b/talk/p2p/base/parsing.cc
@@ -27,9 +27,15 @@
 
 #include "talk/p2p/base/parsing.h"
 
+#include <algorithm>
 #include <stdlib.h>
 #include "talk/base/stringutils.h"
 
+namespace {
+std::string kTrue = "true";
+std::string kOne = "1";
+}
+
 namespace cricket {
 
 bool BadParse(const std::string& text, ParseError* err) {
@@ -53,6 +59,20 @@
   return val.empty() ? def : val;
 }
 
+std::string GetXmlAttr(const buzz::XmlElement* elem,
+                       const buzz::QName& name,
+                       const char* def) {
+    return GetXmlAttr(elem, name, std::string(def));
+}
+
+bool GetXmlAttr(const buzz::XmlElement* elem,
+                const buzz::QName& name, bool def) {
+  std::string val = elem->Attr(name);
+  std::transform(val.begin(), val.end(), val.begin(), tolower);
+
+  return val.empty() ? def : (val == kTrue || val == kOne);
+}
+
 int GetXmlAttr(const buzz::XmlElement* elem,
                const buzz::QName& name, int def) {
   std::string val = elem->Attr(name);
diff --git a/talk/p2p/base/parsing.h b/talk/p2p/base/parsing.h
index 0ae7403..805372b 100644
--- a/talk/p2p/base/parsing.h
+++ b/talk/p2p/base/parsing.h
@@ -77,6 +77,12 @@
 std::string GetXmlAttr(const buzz::XmlElement* elem,
                        const buzz::QName& name,
                        const std::string& def);
+std::string GetXmlAttr(const buzz::XmlElement* elem,
+                       const buzz::QName& name,
+                       const char* def);
+// Return true if the value is "true" or "1".
+bool GetXmlAttr(const buzz::XmlElement* elem,
+                const buzz::QName& name, bool def);
 int GetXmlAttr(const buzz::XmlElement* elem,
                const buzz::QName& name, int def);
 void AddXmlAttr(buzz::XmlElement* elem,
diff --git a/talk/p2p/base/session.cc b/talk/p2p/base/session.cc
index 806c025..a7bb92d 100644
--- a/talk/p2p/base/session.cc
+++ b/talk/p2p/base/session.cc
@@ -416,6 +416,42 @@
   return tinfos;
 }
 
+
+bool Session::OnRemoteCandidates(
+    const TransportInfos& tinfos, ParseError* error) {
+  for (TransportInfos::const_iterator tinfo = tinfos.begin();
+       tinfo != tinfos.end(); ++tinfo) {
+    TransportProxy* transproxy = GetTransportProxy(tinfo->content_name);
+    if (transproxy == NULL) {
+      return BadParse("Unknown content name: " + tinfo->content_name, error);
+    }
+
+    // Must complete negotiation before sending remote candidates, or
+    // there won't be any channel impls.
+    transproxy->CompleteNegotiation();
+    for (Candidates::const_iterator cand = tinfo->candidates.begin();
+         cand != tinfo->candidates.end(); ++cand) {
+      if (!transproxy->impl()->VerifyCandidate(*cand, error))
+        return false;
+
+      if (!transproxy->impl()->HasChannel(cand->name())) {
+        buzz::XmlElement* extra_info =
+            new buzz::XmlElement(QN_GINGLE_P2P_UNKNOWN_CHANNEL_NAME);
+        extra_info->AddAttr(buzz::QN_NAME, cand->name());
+        error->extra = extra_info;
+
+        return BadParse("channel named in candidate does not exist: " +
+                        cand->name() + " for content: "+ tinfo->content_name,
+                        error);
+      }
+    }
+    transproxy->impl()->OnRemoteCandidates(tinfo->candidates);
+  }
+
+  return true;
+}
+
+
 TransportProxy* Session::GetOrCreateTransportProxy(
     const std::string& content_name) {
   TransportProxy* transproxy = GetTransportProxy(content_name);
@@ -467,16 +503,6 @@
   }
 }
 
-void Session::CompleteTransportNegotiations(const TransportInfos& transports) {
-  for (TransportInfos::const_iterator transport = transports.begin();
-       transport != transports.end(); ++transport) {
-    TransportProxy* transproxy = GetTransportProxy(transport->content_name);
-    if (transproxy) {
-      transproxy->CompleteNegotiation();
-    }
-  }
-}
-
 TransportParserMap Session::GetTransportParsers() {
   TransportParserMap parsers;
   parsers[transport_type_] = transport_parser_;
@@ -706,9 +732,8 @@
 
   // Users of Session may listen to state change and call Reject().
   if (state_ != STATE_SENTREJECT) {
-    // TODO: Jingle spec allows candidates to be in the
-    // initiate.  We should support receiving them.
-    CompleteTransportNegotiations(init.transports);
+    if (!OnRemoteCandidates(init.transports, error))
+      return false;
   }
   return true;
 }
@@ -728,9 +753,8 @@
 
   // Users of Session may listen to state change and call Reject().
   if (state_ != STATE_SENTREJECT) {
-    // TODO: Jingle spec allows candidates to be in the
-    // accept.  We should support receiving them.
-    CompleteTransportNegotiations(accept.transports);
+    if (!OnRemoteCandidates(accept.transports, error))
+      return false;
   }
 
   return true;
@@ -773,31 +797,8 @@
                            GetTransportParsers(), &tinfos, error))
     return false;
 
-  for (TransportInfos::iterator tinfo = tinfos.begin();
-       tinfo != tinfos.end(); ++tinfo) {
-    TransportProxy* transproxy = GetTransportProxy(tinfo->content_name);
-    if (transproxy == NULL)
-      return BadParse("Unknown content name: " + tinfo->content_name, error);
-
-    for (Candidates::const_iterator cand = tinfo->candidates.begin();
-         cand != tinfo->candidates.end(); ++cand) {
-      if (!transproxy->impl()->VerifyCandidate(*cand, error))
-        return false;
-
-      if (!transproxy->impl()->HasChannel(cand->name())) {
-        buzz::XmlElement* extra_info =
-            new buzz::XmlElement(QN_GINGLE_P2P_UNKNOWN_CHANNEL_NAME);
-        extra_info->AddAttr(buzz::QN_NAME, cand->name());
-        error->extra = extra_info;
-        return BadParse("channel named in candidate does not exist: " +
-                        cand->name() + " for content: "+ tinfo->content_name,
-                        error);
-      }
-    }
-
-    transproxy->impl()->OnRemoteCandidates(tinfo->candidates);
-    transproxy->CompleteNegotiation();
-  }
+  if (!OnRemoteCandidates(tinfos, error))
+    return false;
 
   return true;
 }
diff --git a/talk/p2p/base/session.h b/talk/p2p/base/session.h
index 64bb83a..a72f751 100644
--- a/talk/p2p/base/session.h
+++ b/talk/p2p/base/session.h
@@ -251,6 +251,10 @@
   // The worker thread used by the session manager
   virtual talk_base::Thread *worker_thread() = 0;
 
+  talk_base::Thread *signaling_thread() {
+    return signaling_thread_;
+  }
+
   // Returns the JID of this client.
   const std::string& local_name() const { return local_name_; }
 
@@ -382,14 +386,12 @@
   bool CreateTransportProxies(const TransportInfos& tinfos,
                               SessionError* error);
   void SpeculativelyConnectAllTransportChannels();
-  // For each transport proxy with a matching content name, complete
-  // the transport negotiation.
-  void CompleteTransportNegotiations(const TransportInfos& tinfos);
+  bool OnRemoteCandidates(const TransportInfos& tinfos,
+                          ParseError* error);
   // Returns a TransportInfo without candidates for each content name.
   // Uses the transport_type_ of the session.
   TransportInfos GetEmptyTransportInfos(const ContentInfos& contents) const;
 
-
   // Called when the first channel of a transport begins connecting.  We use
   // this to start a timer, to make sure that the connection completes in a
   // reasonable amount of time.
diff --git a/talk/p2p/base/sessionmessages.cc b/talk/p2p/base/sessionmessages.cc
index f7c424e..e65d826 100644
--- a/talk/p2p/base/sessionmessages.cc
+++ b/talk/p2p/base/sessionmessages.cc
@@ -25,10 +25,12 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <string>
 #include "talk/p2p/base/sessionmessages.h"
 
 #include "talk/base/logging.h"
 #include "talk/base/scoped_ptr.h"
+#include "talk/xmllite/xmlconstants.h"
 #include "talk/xmpp/constants.h"
 #include "talk/p2p/base/constants.h"
 #include "talk/p2p/base/p2ptransport.h"
@@ -163,7 +165,7 @@
   std::string type_string = jingle->Attr(buzz::QN_ACTION);
   msg->type = ToActionType(type_string);
   msg->sid = jingle->Attr(buzz::QN_ID);
-  msg->initiator = GetXmlAttr(jingle, QN_INITIATOR, "");
+  msg->initiator = GetXmlAttr(jingle, QN_INITIATOR, buzz::STR_EMPTY);
   msg->action_elem = jingle;
 
   if (msg->type == ACTION_UNKNOWN)
diff --git a/talk/p2p/base/transport.cc b/talk/p2p/base/transport.cc
index 663a608..9d8fc32 100644
--- a/talk/p2p/base/transport.cc
+++ b/talk/p2p/base/transport.cc
@@ -155,17 +155,20 @@
   }
 
   if (connect_requested_ && channels_.empty()) {
-    // We're not longer attempting to connect.
+    // We're no longer attempting to connect.
     signaling_thread()->Post(this, MSG_CONNECTING, NULL);
   }
 
-  if (impl)
+  if (impl) {
+    // Check in case the deleted channel was the only non-writable channel.
+    OnChannelWritableState(impl);
     DestroyTransportChannel(impl);
+  }
 }
 
 void Transport::ConnectChannels() {
   ASSERT(signaling_thread()->IsCurrent());
-  worker_thread()->Post(this, MSG_CONNECTCHANNELS, NULL);
+  worker_thread()->Send(this, MSG_CONNECTCHANNELS, NULL);
 }
 
 void Transport::ConnectChannels_w() {
@@ -211,7 +214,7 @@
 
 void Transport::ResetChannels() {
   ASSERT(signaling_thread()->IsCurrent());
-  worker_thread()->Post(this, MSG_RESETCHANNELS, NULL);
+  worker_thread()->Send(this, MSG_RESETCHANNELS, NULL);
 }
 
 void Transport::ResetChannels_w() {
diff --git a/talk/session/phone/call.cc b/talk/session/phone/call.cc
index c85be6a..fbd7a85 100644
--- a/talk/session/phone/call.cc
+++ b/talk/session/phone/call.cc
@@ -441,7 +441,7 @@
   SignalConnectionMonitor(this, infos);
 }
 
-void Call::OnMediaMonitor(VoiceChannel *channel, const MediaInfo& info) {
+void Call::OnMediaMonitor(VoiceChannel *channel, const VoiceMediaInfo& info) {
   SignalMediaMonitor(this, info);
 }
 
@@ -454,7 +454,7 @@
   SignalVideoConnectionMonitor(this, infos);
 }
 
-void Call::OnMediaMonitor(VideoChannel *channel, const MediaInfo& info) {
+void Call::OnMediaMonitor(VideoChannel *channel, const VideoMediaInfo& info) {
   SignalVideoMediaMonitor(this, info);
 }
 
diff --git a/talk/session/phone/call.h b/talk/session/phone/call.h
index de92086..8500fc6 100644
--- a/talk/session/phone/call.h
+++ b/talk/session/phone/call.h
@@ -92,11 +92,11 @@
       SignalReceivedTerminateReason;
   sigslot::signal2<Call *, const std::vector<ConnectionInfo> &>
       SignalConnectionMonitor;
-  sigslot::signal2<Call *, const MediaInfo&> SignalMediaMonitor;
+  sigslot::signal2<Call *, const VoiceMediaInfo&> SignalMediaMonitor;
   sigslot::signal2<Call *, const AudioInfo&> SignalAudioMonitor;
   sigslot::signal2<Call *, const std::vector<ConnectionInfo> &>
       SignalVideoConnectionMonitor;
-  sigslot::signal2<Call *, const MediaInfo&> SignalVideoMediaMonitor;
+  sigslot::signal2<Call *, const VideoMediaInfo&> SignalVideoMediaMonitor;
 
  private:
   void OnMessage(talk_base::Message *message);
@@ -111,11 +111,11 @@
   void Join(Call *call, bool enable);
   void OnConnectionMonitor(VoiceChannel *channel,
                            const std::vector<ConnectionInfo> &infos);
-  void OnMediaMonitor(VoiceChannel *channel, const MediaInfo& info);
+  void OnMediaMonitor(VoiceChannel *channel, const VoiceMediaInfo& info);
   void OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info);
   void OnConnectionMonitor(VideoChannel *channel,
                            const std::vector<ConnectionInfo> &infos);
-  void OnMediaMonitor(VideoChannel *channel, const MediaInfo& info);
+  void OnMediaMonitor(VideoChannel *channel, const VideoMediaInfo& info);
   VoiceChannel* GetVoiceChannel(BaseSession* session);
   VideoChannel* GetVideoChannel(BaseSession* session);
   void ContinuePlayDTMF();
diff --git a/talk/session/phone/channel.cc b/talk/session/phone/channel.cc
index 5fb1dab..526dbec 100644
--- a/talk/session/phone/channel.cc
+++ b/talk/session/phone/channel.cc
@@ -32,6 +32,7 @@
 #include "talk/p2p/base/transportchannel.h"
 #include "talk/session/phone/channelmanager.h"
 #include "talk/session/phone/mediasessionclient.h"
+#include "talk/session/phone/mediasink.h"
 
 namespace cricket {
 
@@ -45,11 +46,19 @@
                          MediaChannel* media_channel, BaseSession* session,
                          const std::string& content_name,
                          TransportChannel* transport_channel)
-    : worker_thread_(thread), media_engine_(media_engine),
-      session_(session), media_channel_(media_channel),
+    : worker_thread_(thread),
+      media_engine_(media_engine),
+      session_(session),
+      media_channel_(media_channel),
+      received_media_sink_(NULL),
+      sent_media_sink_(NULL),
       content_name_(content_name),
-      transport_channel_(transport_channel), rtcp_transport_channel_(NULL),
-      enabled_(false), writable_(false), has_codec_(false), muted_(false) {
+      transport_channel_(transport_channel),
+      rtcp_transport_channel_(NULL),
+      enabled_(false),
+      writable_(false),
+      has_codec_(false),
+      muted_(false) {
   ASSERT(worker_thread_ == talk_base::Thread::Current());
   media_channel_->SetInterface(this);
   transport_channel_->SignalWritableState.connect(
@@ -233,6 +242,18 @@
     real_data = reinterpret_cast<const char*>(work);
   }
 
+  {
+    talk_base::CritScope cs(&sink_critical_section_);
+    if (sent_media_sink_) {
+      // Put the sent RTP or RTCP packet to the sink.
+      if (!rtcp) {
+        sent_media_sink_->OnRtpPacket(real_data, real_len);
+      } else {
+        sent_media_sink_->OnRtcpPacket(real_data, real_len);
+      }
+    }
+  }
+
   // Bon voyage. Return a number that the caller can understand.
   return (channel->SendPacket(real_data, real_len) == real_len) ? len : -1;
 }
@@ -271,6 +292,18 @@
   } else {
     media_channel_->OnRtcpReceived(real_data, real_len);
   }
+
+  {
+    talk_base::CritScope cs(&sink_critical_section_);
+    if (received_media_sink_) {
+      // Put the received RTP or RTCP packet to the sink.
+      if (!rtcp) {
+        received_media_sink_->OnRtpPacket(real_data, real_len);
+      } else {
+        received_media_sink_->OnRtcpPacket(real_data, real_len);
+      }
+    }
+  }
 }
 
 void BaseChannel::OnSessionState(BaseSession* session,
@@ -371,8 +404,9 @@
   ChangeState();
 }
 
+// Sets the maximum video bandwidth for automatic bandwidth adjustment.
 bool BaseChannel::SetMaxSendBandwidth_w(int max_bandwidth) {
-  return media_channel()->SetMaxSendBandwidth(max_bandwidth);
+  return media_channel()->SetSendBandwidth(true, max_bandwidth);
 }
 
 bool BaseChannel::SetRtcpCName_w(const std::string& cname) {
@@ -670,6 +704,11 @@
     ret = media_channel()->SetSendCodecs(audio->codecs());
   }
 
+  int audio_options = audio->conference_mode() ? OPT_CONFERENCE : 0;
+  if (!media_channel()->SetOptions(audio_options)) {
+    // Log an error on failure, but don't abort the call.
+    LOG(LS_ERROR) << "Failed to set voice channel options";
+  }
 
   // update state
   if (ret) {
@@ -786,6 +825,7 @@
   // Can't go in BaseChannel because certain session states will
   // trigger pure virtual functions, such as GetFirstContent()
   OnSessionState(session, session->state());
+
 }
 
 VideoChannel::~VideoChannel() {
@@ -807,6 +847,16 @@
 }
 
 
+
+bool VideoChannel::SendIntraFrame() {
+  Send(MSG_SENDINTRAFRAME);
+  return true;
+}
+bool VideoChannel::RequestIntraFrame() {
+  Send(MSG_REQUESTINTRAFRAME);
+  return true;
+}
+
 void VideoChannel::ChangeState() {
   // render incoming data if we are the active call
   // we receive data on the default channel and multiplexed streams
@@ -890,7 +940,11 @@
   if (ret) {
     ret = SetRtcpMux_w(video->rtcp_mux(), action, CS_REMOTE);
   }
-  // TODO: Set bandwidth appropriately here.
+  // Set video bandwidth parameters.
+  if (ret) {
+    ret = media_channel()->SetSendBandwidth(video->auto_bandwidth(),
+                                            video->bandwidth_bps());
+  }
   if (ret) {
     ret = media_channel()->SetSendCodecs(video->codecs());
   }
@@ -927,6 +981,13 @@
       SetRenderer_w(data->ssrc, data->renderer);
       break;
     }
+    case MSG_SENDINTRAFRAME:
+      SendIntraFrame_w();
+      break;
+    case MSG_REQUESTINTRAFRAME:
+      RequestIntraFrame_w();
+      break;
+
   default:
     BaseChannel::OnMessage(pmsg);
     break;
@@ -944,6 +1005,7 @@
   SignalMediaMonitor(this, info);
 }
 
+
 // TODO: Move to own file in a future CL.
 // Leaving here for now to avoid having to mess with the Mac build.
 RtcpMuxFilter::RtcpMuxFilter() : state_(ST_INIT), offer_enable_(false) {
diff --git a/talk/session/phone/channel.h b/talk/session/phone/channel.h
index b7981d9..b82a4bf 100644
--- a/talk/session/phone/channel.h
+++ b/talk/session/phone/channel.h
@@ -32,6 +32,7 @@
 #include <vector>
 
 #include "talk/base/asyncudpsocket.h"
+#include "talk/base/criticalsection.h"
 #include "talk/base/network.h"
 #include "talk/base/sigslot.h"
 #include "talk/p2p/client/socketmonitor.h"
@@ -45,6 +46,7 @@
 namespace cricket {
 
 class MediaContentDescription;
+class MediaSinkInterface;
 struct CryptoParams;
 
 enum {
@@ -62,9 +64,9 @@
   MSG_SETRINGBACKTONE = 13,
   MSG_PLAYRINGBACKTONE = 14,
   MSG_SETMAXSENDBANDWIDTH = 15,
-  MSG_ADDSCREENCAST = 16,
-  MSG_REMOVESCREENCAST = 17,
-  MSG_SETRTCPCNAME = 18
+  MSG_SETRTCPCNAME = 18,
+  MSG_SENDINTRAFRAME = 19,
+  MSG_REQUESTINTRAFRAME = 20,
 };
 
 // TODO: Move to own file.
@@ -108,6 +110,7 @@
 
   talk_base::Thread* worker_thread() const { return worker_thread_; }
   BaseSession* session() const { return session_; }
+  const std::string& content_name() { return content_name_; }
   TransportChannel* transport_channel() const {
     return transport_channel_;
   }
@@ -134,6 +137,24 @@
   void StartConnectionMonitor(int cms);
   void StopConnectionMonitor();
 
+  // Set and get media sinks for recording media.
+  void set_received_media_sink(MediaSinkInterface* sink) {
+    talk_base::CritScope cs(&sink_critical_section_);
+    received_media_sink_ = sink;
+  }
+  const MediaSinkInterface* received_media_sink() {
+    talk_base::CritScope cs(&sink_critical_section_);
+    return received_media_sink_;
+  }
+  void set_sent_media_sink(MediaSinkInterface* sink) {
+    talk_base::CritScope cs(&sink_critical_section_);
+    sent_media_sink_ = sink;
+  }
+  const MediaSinkInterface* sent_media_sink() {
+    talk_base::CritScope cs(&sink_critical_section_);
+    return sent_media_sink_;
+  }
+
  protected:
   MediaEngine* media_engine() const { return media_engine_; }
   virtual MediaChannel* media_channel() const { return media_channel_; }
@@ -143,6 +164,7 @@
   bool has_codec() const { return has_codec_; }
   void set_has_codec(bool has_codec) { has_codec_ = has_codec; }
   bool muted() const { return muted_; }
+  talk_base::Thread* signaling_thread() { return session_->signaling_thread(); }
 
   void Send(uint32 id, talk_base::MessageData *pdata = NULL);
   void Post(uint32 id, talk_base::MessageData *pdata = NULL);
@@ -230,6 +252,12 @@
   MediaEngine *media_engine_;
   BaseSession *session_;
   MediaChannel *media_channel_;
+  // Media sinks to handle the received or sent RTP/RTCP packets. These are
+  // reference to the objects owned by the media recorder.
+  MediaSinkInterface* received_media_sink_;
+  MediaSinkInterface* sent_media_sink_;
+  talk_base::CriticalSection sink_critical_section_;
+
   std::string content_name_;
   TransportChannel *transport_channel_;
   TransportChannel *rtcp_transport_channel_;
@@ -374,6 +402,9 @@
   void StopMediaMonitor();
   sigslot::signal2<VideoChannel*, const VideoMediaInfo&> SignalMediaMonitor;
 
+  bool SendIntraFrame();
+  bool RequestIntraFrame();
+
  private:
   // overrides from BaseChannel
   virtual void ChangeState();
@@ -387,6 +418,13 @@
   void AddStream_w(uint32 ssrc, uint32 voice_ssrc);
   void RemoveStream_w(uint32 ssrc);
 
+  void SendIntraFrame_w() {
+    media_channel()->SendIntraFrame();
+  }
+  void RequestIntraFrame_w() {
+    media_channel()->RequestIntraFrame();
+  }
+
   struct RenderMessageData : public talk_base::MessageData {
     RenderMessageData(uint32 s, VideoRenderer* r) : ssrc(s), renderer(r) {}
     uint32 ssrc;
@@ -402,7 +440,6 @@
       SocketMonitor *monitor, const std::vector<ConnectionInfo> &infos);
   virtual void OnMediaMonitorUpdate(
       VideoMediaChannel *media_channel, const VideoMediaInfo& info);
-
   VoiceChannel *voice_channel_;
   VideoRenderer *renderer_;
   talk_base::scoped_ptr<VideoMediaMonitor> media_monitor_;
diff --git a/talk/session/phone/filemediaengine.h b/talk/session/phone/filemediaengine.h
index 272449a..cc6788c 100644
--- a/talk/session/phone/filemediaengine.h
+++ b/talk/session/phone/filemediaengine.h
@@ -149,7 +149,7 @@
   virtual void SetSendSsrc(uint32 id) {}  // TODO: change RTP packet?
   virtual bool SetRtcpCName(const std::string& cname) { return true; }
   virtual bool Mute(bool on) { return false; }
-  virtual bool SetMaxSendBandwidth(int max_bandwidth) { return true; }
+  virtual bool SetSendBandwidth(bool autobw, int bps) { return true; }
   virtual bool SetOptions(int options) { return true; }
 
  private:
@@ -174,7 +174,9 @@
   virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer) {
     return true;
   }
-  virtual bool GetStats(VoiceMediaInfo* info) { return true; }
+  virtual bool GetStats(VideoMediaInfo* info) { return true; }
+  virtual bool SendIntraFrame() { return false; }
+  virtual bool RequestIntraFrame() { return false; }
 
   // Implement pure virtual methods of MediaChannel.
   virtual void OnPacketReceived(const void* data, int len);
@@ -182,7 +184,7 @@
   virtual void SetSendSsrc(uint32 id) {}  // TODO: change RTP packet?
   virtual bool SetRtcpCName(const std::string& cname) { return true; }
   virtual bool Mute(bool on) { return false; }
-  virtual bool SetMaxSendBandwidth(int max_bandwidth) { return true; }
+  virtual bool SetSendBandwidth(bool autobw, int bps) { return true; }
   virtual bool SetOptions(int options) { return true; }
 
  private:
diff --git a/talk/session/phone/mediachannel.h b/talk/session/phone/mediachannel.h
index ee9d3c7..2650ea7 100644
--- a/talk/session/phone/mediachannel.h
+++ b/talk/session/phone/mediachannel.h
@@ -88,7 +88,7 @@
   virtual bool Mute(bool on) = 0;
 
   virtual bool SetRtpExtensionHeaders(bool enable_all) { return true; }
-  virtual bool SetMaxSendBandwidth(int max_bandwidth) = 0;
+  virtual bool SetSendBandwidth(bool autobw, int bps) = 0;
   virtual bool SetOptions(int options) = 0;
 
  protected:
@@ -101,7 +101,6 @@
   SEND_MICROPHONE
 };
 
-// TODO: separate into VoiceMediaInfo and VideoMediaInfo
 struct MediaInfo {
   int fraction_lost;
   int cum_lost;
@@ -114,8 +113,13 @@
   int packetsReceived;
 };
 
-typedef MediaInfo VoiceMediaInfo;
-typedef MediaInfo VideoMediaInfo;
+struct VoiceMediaInfo : MediaInfo {
+};
+
+struct VideoMediaInfo : MediaInfo {
+  int receive_framerate;
+  int send_framerate;
+};
 
 class VoiceMediaChannel : public MediaChannel {
  public:
@@ -180,6 +184,11 @@
   virtual void SetElapsedTime(int64 elapsed_time) = 0;
   virtual void SetTimeStamp(int64 time_stamp) = 0;
 
+  // Make a copy of the frame. The frame buffer itself may not be copied,
+  // in which case both the current and new VideoFrame will share a single
+  // reference-counted frame buffer.
+  virtual VideoFrame *Copy() const = 0;
+
   // Writes the frame into the given frame buffer, provided that it is of
   // sufficient size. Returns the frame's actual size, regardless of whether
   // it was written or not (like snprintf). If there is insufficient space,
@@ -260,6 +269,10 @@
   virtual void SetElapsedTime(int64 elapsed_time) {}
   virtual void SetTimeStamp(int64 time_stamp) {}
 
+  virtual VideoFrame *Copy() const {
+    return NULL;
+  }
+
   virtual size_t CopyToBuffer(uint8 *buffer, size_t size) const {
     return 0;
   }
@@ -332,6 +345,13 @@
   virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer) = 0;
   // Gets quality stats for the channel.
   virtual bool GetStats(VideoMediaInfo* info) = 0;
+
+  // Send an intra frame to the receivers.
+  virtual bool SendIntraFrame() = 0;
+  // Reuqest each of the remote senders to send an intra frame.
+  virtual bool RequestIntraFrame() = 0;
+
+
  protected:
   VideoRenderer *renderer_;
 };
diff --git a/talk/session/phone/mediasessionclient.cc b/talk/session/phone/mediasessionclient.cc
index 001cf73..6e9d8b6 100644
--- a/talk/session/phone/mediasessionclient.cc
+++ b/talk/session/phone/mediasessionclient.cc
@@ -25,23 +25,36 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <string>
+
 #include "talk/session/phone/mediasessionclient.h"
 
+#include "talk/base/helpers.h"
 #include "talk/base/logging.h"
 #include "talk/base/stringutils.h"
 #include "talk/p2p/base/constants.h"
 #include "talk/p2p/base/parsing.h"
+#include "talk/session/phone/cryptoparams.h"
+#include "talk/session/phone/srtpfilter.h"
 #include "talk/xmpp/constants.h"
 #include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlconstants.h"
 
 using namespace talk_base;
 
+namespace {
+const std::string kInline = "inline:";
+}
+
 namespace cricket {
 
+typedef std::vector<CryptoParams> CryptoParamsVec;
+
 MediaSessionClient::MediaSessionClient(
     const buzz::Jid& jid, SessionManager *manager)
     : jid_(jid), session_manager_(manager), focus_call_(NULL),
-      channel_manager_(new ChannelManager(session_manager_->worker_thread())) {
+      channel_manager_(new ChannelManager(session_manager_->worker_thread())),
+      secure_(SEC_DISABLED) {
   Construct();
 }
 
@@ -50,7 +63,8 @@
     MediaEngine* media_engine, DeviceManager* device_manager)
     : jid_(jid), session_manager_(manager), focus_call_(NULL),
       channel_manager_(new ChannelManager(
-          media_engine, device_manager, session_manager_->worker_thread())) {
+          media_engine, device_manager, session_manager_->worker_thread())),
+      secure_(SEC_DISABLED) {
   Construct();
 }
 
@@ -81,6 +95,44 @@
   session_manager_->RemoveClient(NS_JINGLE_RTP);
 }
 
+bool CreateCryptoParams(int tag, const std::string& cipher, CryptoParams *out) {
+  std::string key;
+  key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
+
+  if (!CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
+    return false;
+  }
+  out->tag = tag;
+  out->cipher_suite = cipher;
+  out->key_params = kInline + key;
+  return true;
+}
+
+bool AddCryptoParams(const std::string& cipher_suite, CryptoParamsVec *out) {
+  int size = out->size();
+
+  out->resize(size + 1);
+  return CreateCryptoParams(size, cipher_suite, &out->at(size));
+}
+
+// For audio, HMAC 32 is prefered because of the low overhead.
+bool GetSupportedAudioCryptos(CryptoParamsVec* cryptos) {
+#ifdef HAVE_SRTP
+  return AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_32, cryptos) &&
+      AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_80, cryptos);
+#else
+  return false;
+#endif
+}
+
+bool GetSupportedVideoCryptos(CryptoParamsVec* cryptos) {
+#ifdef HAVE_SRTP
+  return AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_80, cryptos);
+#else
+  return false;
+#endif
+}
+
 SessionDescription* MediaSessionClient::CreateOffer(bool video, bool set_ssrc) {
   SessionDescription* offer = new SessionDescription();
   AudioContentDescription* audio = new AudioContentDescription();
@@ -96,6 +148,23 @@
     audio->set_ssrc(0);
   }
   audio->SortCodecs();
+
+  if (secure() != SEC_DISABLED) {
+    CryptoParamsVec audio_cryptos;
+    if (GetSupportedAudioCryptos(&audio_cryptos)) {
+      for (CryptoParamsVec::const_iterator crypto = audio_cryptos.begin();
+           crypto != audio_cryptos.end(); ++crypto) {
+        audio->AddCrypto(*crypto);
+      }
+    }
+    if (secure() == SEC_REQUIRED) {
+      if (audio->cryptos().empty()) {
+        return NULL;  // Abort, crypto required but none found.
+      }
+      audio->set_crypto_required(true);
+    }
+  }
+
   offer->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio);
 
   // add video codecs, if this is a video call
@@ -111,6 +180,23 @@
       video->set_ssrc(0);
     }
     video->SortCodecs();
+
+    if (secure() != SEC_DISABLED) {
+      CryptoParamsVec video_cryptos;
+      if (GetSupportedVideoCryptos(&video_cryptos)) {
+        for (CryptoParamsVec::const_iterator crypto = video_cryptos.begin();
+             crypto != video_cryptos.end(); ++crypto) {
+          video->AddCrypto(*crypto);
+        }
+      }
+      if (secure() == SEC_REQUIRED) {
+        if (video->cryptos().empty()) {
+          return NULL;  // Abort, crypto required but none found.
+        }
+        video->set_crypto_required(true);
+      }
+    }
+
     offer->AddContent(CN_VIDEO, NS_JINGLE_RTP, video);
   }
 
@@ -144,6 +230,23 @@
   return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
 }
 
+// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
+// tolerated because it is low overhead. Pick the crypto in the list
+// that is supported.
+bool SelectCrypto(const MediaContentDescription* offer, CryptoParams *crypto) {
+  bool audio = offer->type() == MEDIA_TYPE_AUDIO;
+  const CryptoParamsVec& cryptos = offer->cryptos();
+
+  for (CryptoParamsVec::const_iterator i = cryptos.begin();
+       i != cryptos.end(); ++i) {
+    if (CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
+        (CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio)) {
+      return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
+    }
+  }
+  return false;
+}
+
 SessionDescription* MediaSessionClient::CreateAnswer(
     const SessionDescription* offer) {
   // The answer contains the intersection of the codecs in the offer with the
@@ -171,6 +274,19 @@
     }
 
     audio_accept->SortCodecs();
+
+    if (secure() != SEC_DISABLED) {
+      CryptoParams crypto;
+
+      if (SelectCrypto(audio_offer, &crypto)) {
+        audio_accept->AddCrypto(crypto);
+      }
+    }
+
+    if (audio_accept->cryptos().empty() &&
+        (audio_offer->crypto_required() || secure() == SEC_REQUIRED)) {
+      return NULL;  // Fails the session setup.
+    }
     accept->AddContent(audio_content->name, audio_content->type, audio_accept);
   }
 
@@ -194,6 +310,19 @@
     }
 
     video_accept->SortCodecs();
+
+    if (secure() != SEC_DISABLED) {
+      CryptoParams crypto;
+
+      if (SelectCrypto(video_offer, &crypto)) {
+        video_accept->AddCrypto(crypto);
+      }
+    }
+
+    if (video_accept->cryptos().empty() &&
+        (video_offer->crypto_required() || secure() == SEC_REQUIRED)) {
+      return NULL;  // Fails the session setup.
+    }
     accept->AddContent(video_content->name, video_content->type, video_accept);
   }
 
@@ -344,6 +473,59 @@
   }
 }
 
+bool ParseCryptoParams(const buzz::XmlElement* element,
+                       CryptoParams* out,
+                       ParseError* error) {
+  if (!element->HasAttr(QN_CRYPTO_SUITE)) {
+    return BadParse("crypto: crypto-suite attribute missing ", error);
+  } else if (!element->HasAttr(QN_CRYPTO_KEY_PARAMS)) {
+    return BadParse("crypto: key-params attribute missing ", error);
+  } else if (!element->HasAttr(QN_CRYPTO_TAG)) {
+    return BadParse("crypto: tag attribute missing ", error);
+  }
+
+  const std::string& crypto_suite = element->Attr(QN_CRYPTO_SUITE);
+  const std::string& key_params = element->Attr(QN_CRYPTO_KEY_PARAMS);
+  const int tag = GetXmlAttr(element, QN_CRYPTO_TAG, 0);
+  const std::string& session_params =
+      element->Attr(QN_CRYPTO_SESSION_PARAMS);  // Optional.
+
+  *out = CryptoParams(tag, crypto_suite, key_params, session_params);
+  return true;
+}
+
+
+// Parse the first encryption element found with a matching 'usage'
+// element.
+// <usage/> is specific to Gingle. In Jingle, <crypto/> is already
+// scoped to a content.
+// Return false if there was an encryption element and it could not be
+// parsed.
+bool ParseGingleEncryption(const buzz::XmlElement* desc,
+                           const buzz::QName& usage,
+                           MediaContentDescription* media,
+                           ParseError* error) {
+  for (const buzz::XmlElement* encryption = desc->FirstNamed(QN_ENCRYPTION);
+       encryption != NULL;
+       encryption = encryption->NextNamed(QN_ENCRYPTION)) {
+    if (encryption->FirstNamed(usage) != NULL) {
+      media->set_crypto_required(
+          GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
+      for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
+           crypto != NULL;
+           crypto = crypto->NextNamed(QN_CRYPTO)) {
+        CryptoParams params;
+        if (!ParseCryptoParams(crypto, &params, error)) {
+          return false;
+        }
+        media->AddCrypto(params);
+      }
+      break;
+    }
+  }
+  return true;
+}
+
 bool ParseGingleAudioContent(const buzz::XmlElement* content_elem,
                              const ContentDescription** content,
                              ParseError* error) {
@@ -368,11 +550,15 @@
 
   ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, audio);
 
+  if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE,
+                             audio, error)) {
+    return false;
+  }
+
   *content = audio;
   return true;
 }
 
-
 bool ParseGingleVideoContent(const buzz::XmlElement* content_elem,
                              const ContentDescription** content,
                              ParseError* error) {
@@ -390,6 +576,11 @@
 
   ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video);
 
+  if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE,
+                             video, error)) {
+    return false;
+  }
+
   *content = video;
   return true;
 }
@@ -398,8 +589,10 @@
                                 std::map<std::string, std::string>* paramap) {
   for (const buzz::XmlElement* param = element->FirstNamed(QN_PARAMETER);
        param != NULL; param = param->NextNamed(QN_PARAMETER)) {
-    std::string name  = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME, "");
-    std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE, "");
+    std::string name  = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME,
+                                   buzz::STR_EMPTY);
+    std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE,
+                                   buzz::STR_EMPTY);
     if (!name.empty() && !value.empty()) {
       paramap->insert(make_pair(name, value));
     }
@@ -412,12 +605,40 @@
   return (iter == map.end()) ? def : atoi(iter->second.c_str());
 }
 
+
+// Parse the first encryption element found.
+// Return false if there was an encryption element and it could not be
+// parsed.
+bool ParseJingleEncryption(const buzz::XmlElement* content_elem,
+                           MediaContentDescription* media,
+                           ParseError* error) {
+  const buzz::XmlElement* encryption =
+          content_elem->FirstNamed(QN_ENCRYPTION);
+  if (encryption == NULL) {
+      return true;
+  }
+
+  media->set_crypto_required(
+      GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
+
+  for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
+       crypto != NULL;
+       crypto = crypto->NextNamed(QN_CRYPTO)) {
+    CryptoParams params;
+    if (!ParseCryptoParams(crypto, &params, error)) {
+      return false;
+    }
+    media->AddCrypto(params);
+  }
+  return true;
+}
+
 bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) {
   int id = GetXmlAttr(elem, QN_ID, -1);
   if (id < 0)
     return false;
 
-  std::string name = GetXmlAttr(elem, QN_NAME, "");
+  std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
   int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0);
   int channels = GetXmlAttr(elem, QN_CHANNELS, 1);
 
@@ -434,7 +655,7 @@
   if (id < 0)
     return false;
 
-  std::string name = GetXmlAttr(elem, QN_NAME, "");
+  std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
 
   std::map<std::string, std::string> paramap;
   ParsePayloadTypeParameters(elem, &paramap);
@@ -461,6 +682,9 @@
     }
   }
 
+  if (!ParseJingleEncryption(content_elem, audio, error)) {
+    return false;
+  }
   // TODO: Figure out how to integrate SSRC into Jingle.
   *content = audio;
   return true;
@@ -481,6 +705,9 @@
     }
   }
 
+  if (!ParseJingleEncryption(content_elem, video, error)) {
+    return false;
+  }
   // TODO: Figure out how to integrate SSRC into Jingle.
   *content = video;
   return true;
@@ -547,8 +774,51 @@
   return elem;
 }
 
+// For Jingle, usage_qname is empty.
+buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos,
+                                             bool required) {
+  buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION);
+
+  if (required) {
+    encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
+  }
+
+  for (CryptoParamsVec::const_iterator i = cryptos.begin();
+       i != cryptos.end();
+       ++i) {
+    buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO);
+
+    AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag);
+    crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite);
+    crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params);
+    if (!i->session_params.empty()) {
+      crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params);
+    }
+    encryption_elem->AddElement(crypto_elem);
+  }
+  return encryption_elem;
+}
+
+buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos,
+                                             const buzz::QName& usage_qname,
+                                             bool required) {
+  buzz::XmlElement* encryption_elem =
+      CreateJingleEncryptionElem(cryptos, required);
+
+  if (required) {
+    encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
+  }
+
+  buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname);
+  encryption_elem->AddElement(usage_elem);
+
+  return encryption_elem;
+}
+
+
 buzz::XmlElement* CreateGingleAudioContentElem(
-    const AudioContentDescription* audio) {
+    const AudioContentDescription* audio,
+    bool crypto_required) {
   buzz::XmlElement* elem =
       new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true);
 
@@ -561,12 +831,20 @@
         QN_GINGLE_AUDIO_SRCID, audio->ssrc()));
   }
 
+  const CryptoParamsVec& cryptos = audio->cryptos();
+  if (!cryptos.empty()) {
+    elem->AddElement(CreateGingleEncryptionElem(cryptos,
+                                                QN_GINGLE_AUDIO_CRYPTO_USAGE,
+                                                crypto_required));
+  }
+
 
   return elem;
 }
 
 buzz::XmlElement* CreateGingleVideoContentElem(
-    const VideoContentDescription* video) {
+    const VideoContentDescription* video,
+    bool crypto_required) {
   buzz::XmlElement* elem =
       new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true);
 
@@ -579,6 +857,13 @@
         QN_GINGLE_VIDEO_SRCID, video->ssrc()));
   }
 
+  const CryptoParamsVec& cryptos = video->cryptos();
+  if (!cryptos.empty()) {
+    elem->AddElement(CreateGingleEncryptionElem(cryptos,
+                                                QN_GINGLE_VIDEO_CRYPTO_USAGE,
+                                                crypto_required));
+  }
+
   return elem;
 }
 
@@ -627,7 +912,7 @@
 }
 
 buzz::XmlElement* CreateJingleAudioContentElem(
-    const AudioContentDescription* audio) {
+    const AudioContentDescription* audio, bool crypto_required) {
   buzz::XmlElement* elem =
       new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
 
@@ -638,12 +923,17 @@
     elem->AddElement(CreateJingleAudioCodecElem(*codec));
   }
 
+  const CryptoParamsVec& cryptos = audio->cryptos();
+  if (!cryptos.empty()) {
+    elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
+  }
+
   // TODO: Figure out how to integrate SSRC into Jingle.
   return elem;
 }
 
 buzz::XmlElement* CreateJingleVideoContentElem(
-    const VideoContentDescription* video) {
+    const VideoContentDescription* video, bool crypto_required) {
   buzz::XmlElement* elem =
       new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
 
@@ -654,6 +944,11 @@
     elem->AddElement(CreateJingleVideoCodecElem(*codec));
   }
 
+  const CryptoParamsVec& cryptos = video->cryptos();
+  if (!cryptos.empty()) {
+    elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
+  }
+
   // TODO: Figure out how to integrate SSRC into Jingle.
   return elem;
 }
@@ -664,22 +959,23 @@
                                       WriteError* error) {
   const MediaContentDescription* media =
       static_cast<const MediaContentDescription*>(content);
+  bool crypto_required = secure() == SEC_REQUIRED;
 
   if (media->type() == MEDIA_TYPE_AUDIO) {
     const AudioContentDescription* audio =
         static_cast<const AudioContentDescription*>(media);
     if (protocol == PROTOCOL_GINGLE) {
-      *elem = CreateGingleAudioContentElem(audio);
+      *elem = CreateGingleAudioContentElem(audio, crypto_required);
     } else {
-      *elem = CreateJingleAudioContentElem(audio);
+      *elem = CreateJingleAudioContentElem(audio, crypto_required);
     }
   } else if (media->type() == MEDIA_TYPE_VIDEO) {
     const VideoContentDescription* video =
         static_cast<const VideoContentDescription*>(media);
     if (protocol == PROTOCOL_GINGLE) {
-      *elem = CreateGingleVideoContentElem(video);
+      *elem = CreateGingleVideoContentElem(video, crypto_required);
     } else {
-      *elem = CreateJingleVideoContentElem(video);
+      *elem = CreateJingleVideoContentElem(video, crypto_required);
     }
   } else {
     return BadWrite("Unknown content type: " + media->type(), error);
diff --git a/talk/session/phone/mediasessionclient.h b/talk/session/phone/mediasessionclient.h
index 533341b..6534259 100644
--- a/talk/session/phone/mediasessionclient.h
+++ b/talk/session/phone/mediasessionclient.h
@@ -51,8 +51,23 @@
 typedef std::vector<AudioCodec> AudioCodecs;
 typedef std::vector<VideoCodec> VideoCodecs;
 
+// SEC_ENABLED and SEC_REQUIRED should only be used if the session
+// was negotiated over TLS, to protect the inline crypto material
+// exchange.
+// SEC_DISABLED: No crypto in outgoing offer and answer. Fail any
+//               offer with crypto required.
+// SEC_ENABLED: Crypto in outgoing offer and answer. Fail any offer
+//              with unsupported required crypto. Crypto set but not
+//              required in outgoing offer.
+// SEC_REQUIRED: Crypto in outgoing offer and answer with
+//               required='true'. Fail any offer with no or
+//               unsupported crypto (implicit crypto required='true'
+//               in the offer.)
+enum SecureMediaPolicy {SEC_DISABLED, SEC_ENABLED, SEC_REQUIRED};
+
 class MediaSessionClient: public SessionClient, public sigslot::has_slots<> {
  public:
+
   MediaSessionClient(const buzz::Jid& jid, SessionManager *manager);
   // Alternative constructor, allowing injection of media_engine
   // and device_manager.
@@ -103,6 +118,9 @@
   SessionDescription* CreateOffer(bool video = false, bool set_ssrc = false);
   SessionDescription* CreateAnswer(const SessionDescription* offer);
 
+  SecureMediaPolicy secure() const { return secure_; }
+  void set_secure(SecureMediaPolicy s) { secure_ = s; }
+
  private:
   void Construct();
   void OnSessionCreate(Session *session, bool received_initiate);
@@ -124,7 +142,7 @@
   ChannelManager *channel_manager_;
   std::map<uint32, Call *> calls_;
   std::map<std::string, Call *> session_map_;
-
+  SecureMediaPolicy secure_;
   friend class Call;
 };
 
@@ -135,8 +153,10 @@
 
 class MediaContentDescription : public ContentDescription {
  public:
-  MediaContentDescription() : ssrc_(0), ssrc_set_(false), rtcp_mux_(false),
-                              rtp_headers_disabled_(false) {}
+  MediaContentDescription() : ssrc_(0), bandwidth_bps_(-1),
+                              ssrc_set_(false), auto_bandwidth_(true),
+                              rtcp_mux_(false), rtp_headers_disabled_(false),
+                              crypto_required_(false) {}
 
   virtual MediaType type() const = 0;
 
@@ -161,12 +181,26 @@
   void AddCrypto(const CryptoParams& params) {
     cryptos_.push_back(params);
   }
+  bool crypto_required() const { return crypto_required_; }
+  void set_crypto_required(bool crypto) {
+    crypto_required_ = crypto;
+  }
 
+  int bandwidth_bps() const { return bandwidth_bps_; }
+  void set_bandwidth_bps(int bps) { bandwidth_bps_ = bps; }
+
+  bool auto_bandwidth() const { return auto_bandwidth_; }
+  void set_auto_bandwidth(bool enable) { auto_bandwidth_ = enable; }
+
+ protected:
   uint32 ssrc_;
+  int bandwidth_bps_;    // fixed or max video bandwidth
   bool ssrc_set_;
+  bool auto_bandwidth_;    // if true, bandwidth_bps_ < 0 flags default limits
   bool rtcp_mux_;
   bool rtp_headers_disabled_;
   std::vector<CryptoParams> cryptos_;
+  bool crypto_required_;
 };
 
 template <class C>
@@ -190,16 +224,22 @@
 
 class AudioContentDescription : public MediaContentDescriptionImpl<AudioCodec> {
  public:
-  AudioContentDescription()
-      {}
+  AudioContentDescription() :
+      conference_mode_(false) {}
 
   virtual MediaType type() const { return MEDIA_TYPE_AUDIO; }
 
+  bool conference_mode() const { return conference_mode_; }
+  void set_conference_mode(bool enable) {
+    conference_mode_ = enable;
+  }
+
   const std::string &lang() const { return lang_; }
   void set_lang(const std::string &lang) { lang_ = lang; }
 
 
  private:
+  bool conference_mode_;
   std::string lang_;
 };
 
diff --git a/talk/session/phone/mediasink.h b/talk/session/phone/mediasink.h
new file mode 100644
index 0000000..078b534
--- /dev/null
+++ b/talk/session/phone/mediasink.h
@@ -0,0 +1,49 @@
+/*
+ * 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_SESSION_PHONE_MEDIASINK_H_
+#define TALK_SESSION_PHONE_MEDIASINK_H_
+
+namespace cricket {
+
+// MediaSinkInterface is a sink to handle RTP and RTCP packets that are sent or
+// received by a channel. Each channel needs two MediaSinkInterface, one for
+// the sent packets and the other for the received packets.
+class MediaSinkInterface {
+ public:
+  virtual ~MediaSinkInterface() {}
+
+  virtual void SetMaxSize(size_t size) = 0;
+  virtual bool Enable(bool enable) = 0;
+  virtual bool IsEnabled() const = 0;
+  virtual void OnRtpPacket(const void* data, size_t size) = 0;
+  virtual void OnRtcpPacket(const void* data, size_t size) = 0;
+};
+
+}  // namespace cricket
+
+#endif  // TALK_SESSION_PHONE_MEDIASINK_H_
diff --git a/talk/session/phone/rtpdump.cc b/talk/session/phone/rtpdump.cc
index 1006015..bd67cad 100644
--- a/talk/session/phone/rtpdump.cc
+++ b/talk/session/phone/rtpdump.cc
@@ -35,32 +35,24 @@
 
 namespace cricket {
 
-static const char kRtpDumpFileFirstLine[] = "#!rtpplay1.0 0.0.0.0/0\n";
+const std::string RtpDumpFileHeader::kFirstLine =
+    "#!rtpplay1.0 0.0.0.0/0\n";
 
-struct RtpDumpFileHeader {
-  RtpDumpFileHeader(uint32 start_ms, uint32 s, uint16 p)
-      : start_sec(start_ms / 1000),
-        start_usec(start_ms % 1000 * 1000),
-        source(s),
-        port(p),
-        padding(0) {
-  }
+RtpDumpFileHeader::RtpDumpFileHeader(uint32 start_ms, uint32 s, uint16 p)
+    : start_sec(start_ms / 1000),
+      start_usec(start_ms % 1000 * 1000),
+      source(s),
+      port(p),
+      padding(0) {
+}
 
-  void WriteToByteBuffer(talk_base::ByteBuffer* buf) {
-    buf->WriteUInt32(start_sec);
-    buf->WriteUInt32(start_usec);
-    buf->WriteUInt32(source);
-    buf->WriteUInt16(port);
-    buf->WriteUInt16(padding);
-  }
-
-  static const size_t kHeaderLength = 16;
-  uint32 start_sec;   // start of recording, the seconds part.
-  uint32 start_usec;  // start of recording, the microseconds part.
-  uint32 source;      // network source (multicast address).
-  uint16 port;        // UDP port.
-  uint16 padding;     // 2 bytes padding.
-};
+void RtpDumpFileHeader::WriteToByteBuffer(talk_base::ByteBuffer* buf) {
+  buf->WriteUInt32(start_sec);
+  buf->WriteUInt32(start_usec);
+  buf->WriteUInt32(source);
+  buf->WriteUInt16(port);
+  buf->WriteUInt16(padding);
+}
 
 // RTP packet format (http://www.networksorcery.com/enp/protocol/rtp.htm).
 static const int kRtpSeqNumOffset = 2;
@@ -308,7 +300,8 @@
 
 talk_base::StreamResult RtpDumpWriter::WriteFileHeader() {
   talk_base::StreamResult res = stream_->WriteAll(
-      kRtpDumpFileFirstLine, strlen(kRtpDumpFileFirstLine), NULL, NULL);
+      RtpDumpFileHeader::kFirstLine.c_str(),
+      RtpDumpFileHeader::kFirstLine.size(), NULL, NULL);
   if (res != talk_base::SR_SUCCESS) {
     return res;
   }
diff --git a/talk/session/phone/rtpdump.h b/talk/session/phone/rtpdump.h
index 40c4e03..66275e6 100644
--- a/talk/session/phone/rtpdump.h
+++ b/talk/session/phone/rtpdump.h
@@ -35,6 +35,10 @@
 #include "talk/base/basictypes.h"
 #include "talk/base/stream.h"
 
+namespace talk_base {
+class ByteBuffer;
+}
+
 namespace cricket {
 
 // We use the RTP dump file format compatible to the format used by rtptools
@@ -44,6 +48,19 @@
 // For each packet, the file contains a 8 byte dump packet header, followed by
 // the actual RTP or RTCP packet.
 
+struct RtpDumpFileHeader {
+  RtpDumpFileHeader(uint32 start_ms, uint32 s, uint16 p);
+  void WriteToByteBuffer(talk_base::ByteBuffer* buf);
+
+  static const std::string kFirstLine;
+  static const size_t kHeaderLength = 16;
+  uint32 start_sec;   // start of recording, the seconds part.
+  uint32 start_usec;  // start of recording, the microseconds part.
+  uint32 source;      // network source (multicast address).
+  uint16 port;        // UDP port.
+  uint16 padding;     // 2 bytes padding.
+};
+
 struct RtpDumpPacket {
   RtpDumpPacket() {}
 
@@ -166,6 +183,12 @@
   }
   uint32 GetElapsedTime() const;
 
+  bool GetDumpSize(size_t* size) {
+    // Note that we use GetPosition(), rather than GetSize(), to avoid flush the
+    // stream per write.
+    return stream_ && size && stream_->GetPosition(size);
+  }
+
  protected:
   talk_base::StreamResult WriteFileHeader();
 
diff --git a/talk/session/phone/srtpfilter.cc b/talk/session/phone/srtpfilter.cc
index 63daed6..f8d2dd4 100644
--- a/talk/session/phone/srtpfilter.cc
+++ b/talk/session/phone/srtpfilter.cc
@@ -62,6 +62,7 @@
 const std::string& CS_DEFAULT = CS_AES_CM_128_HMAC_SHA1_80;
 const std::string CS_AES_CM_128_HMAC_SHA1_80 = "AES_CM_128_HMAC_SHA1_80";
 const std::string CS_AES_CM_128_HMAC_SHA1_32 = "AES_CM_128_HMAC_SHA1_32";
+const int SRTP_MASTER_KEY_BASE64_LEN = SRTP_MASTER_KEY_LEN * 4 / 3;
 
 SrtpFilter::SrtpFilter() : state_(ST_INIT) {
 }
diff --git a/talk/session/phone/srtpfilter.h b/talk/session/phone/srtpfilter.h
index 71b74e0..3401004 100644
--- a/talk/session/phone/srtpfilter.h
+++ b/talk/session/phone/srtpfilter.h
@@ -51,6 +51,8 @@
 extern const std::string CS_AES_CM_128_HMAC_SHA1_80;
 // 128-bit AES with 32-bit SHA-1 HMAC.
 extern const std::string CS_AES_CM_128_HMAC_SHA1_32;
+// Key is 128 bits and salt is 112 bits == 30 bytes. B64 bloat => 40 bytes.
+extern const int SRTP_MASTER_KEY_BASE64_LEN;
 
 // Class that wraps a libSRTP session. Used internally by SrtpFilter, below.
 class SrtpSession {
diff --git a/talk/session/phone/videocommon.h b/talk/session/phone/videocommon.h
index 1092b5c..230b1ff 100644
--- a/talk/session/phone/videocommon.h
+++ b/talk/session/phone/videocommon.h
@@ -58,24 +58,17 @@
 // Some good pages discussing FourCC codes:
 //   http://developer.apple.com/quicktime/icefloe/dispatch020.html
 //   http://www.fourcc.org/yuv.php
-enum {
+enum FourCC {
+  // Canonical fourccs used in our code.
   FOURCC_I420 = FOURCC('I', '4', '2', '0'),
-  FOURCC_IYUV = FOURCC('I', 'Y', 'U', 'V'),  // Alias for I420
-  FOURCC_YU12 = FOURCC('Y', 'U', '1', '2'),  // Alias for I420
   FOURCC_YUY2 = FOURCC('Y', 'U', 'Y', '2'),
   FOURCC_UYVY = FOURCC('U', 'Y', 'V', 'Y'),
-  FOURCC_HDYC = FOURCC('H', 'D', 'Y', 'C'),
   FOURCC_24BG = FOURCC('2', '4', 'B', 'G'),
-  FOURCC_RGB1 = FOURCC('R', 'G', 'B', '1'),
   FOURCC_RGBA = FOURCC('R', 'G', 'B', 'A'),
-  FOURCC_RGB2 = FOURCC('R', 'G', 'B', '2'),
-  FOURCC_ARGB = FOURCC('A', 'R', 'G', 'B'),
   FOURCC_BGRA = FOURCC('B', 'G', 'R', 'A'),
+  FOURCC_ARGB = FOURCC('A', 'R', 'G', 'B'),
   FOURCC_MJPG = FOURCC('M', 'J', 'P', 'G'),
   FOURCC_JPEG = FOURCC('J', 'P', 'E', 'G'),
-  FOURCC_2VUY = FOURCC('2', 'v', 'u', 'y'),  // Alias for UYVY
-  FOURCC_YUVS = FOURCC('y', 'u', 'v', 's'),  // Alias for YUY2
-  FOURCC_YUYV = FOURCC('Y', 'U', 'Y', 'V'),  // Alias for YUY2
   FOURCC_RAW  = FOURCC('r', 'a', 'w', ' '),
   // Next five are Bayer RGB formats. The four characters define the order of
   // the colours in each 2x2 pixel grid, going left-to-right and top-to-bottom.
@@ -83,10 +76,26 @@
   FOURCC_BGGR = FOURCC('B', 'G', 'G', 'R'),
   FOURCC_GRBG = FOURCC('G', 'R', 'B', 'G'),
   FOURCC_GBRG = FOURCC('G', 'B', 'R', 'G'),
+
+  // Aliases for canonical fourccs, replaced with their canonical equivalents by
+  // CanonicalFourCC().
+  FOURCC_IYUV = FOURCC('I', 'Y', 'U', 'V'),  // Alias for I420
+  FOURCC_YU12 = FOURCC('Y', 'U', '1', '2'),  // Alias for I420
+  FOURCC_YUYV = FOURCC('Y', 'U', 'Y', 'V'),  // Alias for YUY2
+  FOURCC_YUVS = FOURCC('y', 'u', 'v', 's'),  // Alias for YUY2
+  FOURCC_HDYC = FOURCC('H', 'D', 'Y', 'C'),  // Alias for UYVY
+  FOURCC_2VUY = FOURCC('2', 'v', 'u', 'y'),  // Alias for UYVY
+  FOURCC_RGB1 = FOURCC('R', 'G', 'B', '1'),  // Alias for RGBA
+  FOURCC_RGB2 = FOURCC('R', 'G', 'B', '2'),  // Alias for BGRA
   FOURCC_BA81 = FOURCC('B', 'A', '8', '1'),  // Alias for BGGR
-  FOURCC_ANY  = 0xFFFFFFFF,  // Match any fourcc.
+
+  // Match any fourcc.
+  FOURCC_ANY  = 0xFFFFFFFF,
 };
 
+// Converts fourcc aliases into canonical ones.
+uint32 CanonicalFourCC(uint32 fourcc);
+
 //////////////////////////////////////////////////////////////////////////////
 // Definition of VideoFormat.
 //////////////////////////////////////////////////////////////////////////////
diff --git a/talk/site_scons/talk.py b/talk/site_scons/talk.py
index e5ede4f..fda5417 100644
--- a/talk/site_scons/talk.py
+++ b/talk/site_scons/talk.py
@@ -6,27 +6,13 @@
 #
 import os
 
-# Keep a global list of what libraries have a 64 bit version.  We use it for
-# replacements when linking a 64 bit dylib or executable.
-libs64 = []
+# Keep a global dictionary of library target params for lookups in
+# ExtendComponent().
+_all_lib_targets = {}
 
-def Library(env, **kwargs):
-  """Extends ComponentLibrary to support multiplatform builds.
-
-  Args:
-    env: The current environment.
-    kwargs: The keyword arguments.
-  """
-  params = CombineDicts(kwargs, {'COMPONENT_STATIC': True})
-  if params.has_key('also64bit'):
-    libs64.append(params['name'])
-
-  return ExtendComponent(env, 'ComponentLibrary', **params)
-
-
-def DynamicLibrary(env, **kwargs):
+def _GenericLibrary(env, static, **kwargs):
   """Extends ComponentLibrary to support multiplatform builds
-     of dynmic libraries. This use COMPONENT_STATIC = false.
+     of dynamic or static libraries.
 
   Args:
     env: The environment object.
@@ -35,10 +21,38 @@
   Returns:
     See swtoolkit ComponentLibrary
   """
-  params = CombineDicts(kwargs, {'COMPONENT_STATIC': False})
+  params = CombineDicts(kwargs, {'COMPONENT_STATIC': static})
   return ExtendComponent(env, 'ComponentLibrary', **params)
 
 
+def Library(env, **kwargs):
+  """Extends ComponentLibrary to support multiplatform builds of static
+     libraries.
+
+  Args:
+    env: The current environment.
+    kwargs: The keyword arguments.
+
+  Returns:
+    See swtoolkit ComponentLibrary
+  """
+  return _GenericLibrary(env, True, **kwargs)
+
+
+def DynamicLibrary(env, **kwargs):
+  """Extends ComponentLibrary to support multiplatform builds
+     of dynmic libraries.
+
+  Args:
+    env: The environment object.
+    kwargs: The keyword arguments.
+
+  Returns:
+    See swtoolkit ComponentLibrary
+  """
+  return _GenericLibrary(env, False, **kwargs)
+
+
 def Object(env, **kwargs):
   return ExtendComponent(env, 'ComponentObject', **kwargs)
 
@@ -73,9 +87,9 @@
       'ws2_32'
     ]
     common_test_params['lin_libs'] = [
+      'crypto',
       'pthread',
-      ':libssl.so.0.9.8',
-      ':libcrypto.so.0.9.8',
+      'ssl',
     ]
 
   params = CombineDicts(kwargs, common_test_params)
@@ -190,6 +204,7 @@
   elif env.Bit('linux'):
     ipp_libdir %= 'v_5_2_linux'
 
+
   AddToDict(kwargs, 'libdirs', [
     '$MAIN_DIR/third_party/gips/Libraries/',
     ipp_libdir,
@@ -207,6 +222,7 @@
   elif env.Bit('linux'):
     gips_lib = 'VoiceEngine_Linux_external_gcc'
 
+
   AddToDict(kwargs, 'libs', [
     gips_lib,
     'LmiAudioCommon',
@@ -367,6 +383,23 @@
 
   return merged
 
+# Linux can build both 32 and 64 bit on 64 bit host, but 32 bit host can
+# only build 32 bit.  For 32 bit debian installer a 32 bit host is required.
+
+def Allow64BitCompile(env):
+  return env.Bit('linux') and env.Bit('platform_arch_64bit')
+
+def MergeSettingsFromLibraryDependencies(env, params):
+  if params.has_key('libs'):
+    for lib in params['libs']:
+      if (_all_lib_targets.has_key(lib) and
+          _all_lib_targets[lib].has_key('dependent_target_settings')):
+        params = CombineDicts(
+            params,
+            MergeAndFilterByPlatform(
+                env,
+                _all_lib_targets[lib]['dependent_target_settings']))
+  return params
 
 def ExtendComponent(env, component, **kwargs):
   """A wrapper around a scons builder function that preprocesses and post-
@@ -388,22 +421,27 @@
   """
   env = env.Clone()
 
+  # prune parameters intended for other platforms, then merge
+  params = MergeAndFilterByPlatform(env, kwargs)
+
   # get the 'target' field
-  name = GetEntry(kwargs, 'name')
+  name = GetEntry(params, 'name')
+
+  # save pristine params of lib targets for future reference
+  if 'ComponentLibrary' == component:
+    _all_lib_targets[name] = dict(params)
+
+  # add any dependent target settings from library dependencies
+  params = MergeSettingsFromLibraryDependencies(env, params)
 
   # if this is a signed binary we need to make an unsigned version first
-  signed = env.Bit('windows') and GetEntry(kwargs, 'signed')
+  signed = env.Bit('windows') and GetEntry(params, 'signed')
   if signed:
     name = 'unsigned_' + name
 
-  also64bit = env.Bit('linux') and GetEntry(kwargs, 'also64bit')
-
   # add default values
-  if GetEntry(kwargs, 'include_talk_media_libs'):
-    kwargs = AddMediaLibs(env, **kwargs)
-
-  # prune parameters intended for other platforms, then merge
-  params = MergeAndFilterByPlatform(env, kwargs)
+  if GetEntry(params, 'include_talk_media_libs'):
+    params = AddMediaLibs(env, **params)
 
   # potentially exit now
   srcs = GetEntry(params, 'srcs')
@@ -411,7 +449,7 @@
     return None
 
   # apply any explicit dependencies
-  dependencies = GetEntry(kwargs, 'depends')
+  dependencies = GetEntry(params, 'depends')
   if dependencies is not None:
     env.Depends(name, dependencies)
 
@@ -442,7 +480,7 @@
       env.Prepend(**{var : values})
 
   # workaround for pulse stripping link flag for unknown reason
-  if env.Bit('linux'):
+  if Allow64BitCompile(env):
     env['SHLINKCOM'] = ('$SHLINK -o $TARGET -m32 $SHLINKFLAGS $SOURCES '
                         '$_LIBDIRFLAGS $_LIBFLAGS')
     env['LINKCOM'] = ('$LINK -o $TARGET -m32 $LINKFLAGS $SOURCES '
@@ -458,7 +496,7 @@
   node = builder(name, srcs)
 
   # make a parallel 64bit version if requested
-  if also64bit:
+  if Allow64BitCompile(env) and GetEntry(params, 'also64bit'):
     env_64bit = env.Clone()
     env_64bit.FilterOut(CCFLAGS = ['-m32'], LINKFLAGS = ['-m32'])
     env_64bit.Prepend(CCFLAGS = ['-m64', '-fPIC'], LINKFLAGS = ['-m64'])
@@ -471,7 +509,8 @@
       # link 64 bit versions of libraries
       libs = []
       for lib in env_64bit['LIBS']:
-        if lib in set(libs64):
+        if (_all_lib_targets.has_key(lib) and
+            _all_lib_targets[lib].has_key('also64bit')):
           libs.append(lib + '64')
         else:
           libs.append(lib)
@@ -495,7 +534,7 @@
       target = '$STAGING_DIR/' + target,
     )
     env.Alias('signed_binaries', signed_node)
-    return signed
+    return signed_node
 
   return node