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, ¶ms, 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, ¶ms, 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, ¶map);
@@ -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