diff options
author | Ben Murdoch <benm@google.com> | 2010-11-25 19:40:10 +0000 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2010-12-03 13:52:53 +0000 |
commit | 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7 (patch) | |
tree | 938665d93a11fe7a6d0124e3c1e020d1f9d3f947 /third_party | |
parent | 7c627d87728a355737862918d144f98f69406954 (diff) | |
download | external_chromium-4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7.zip external_chromium-4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7.tar.gz external_chromium-4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7.tar.bz2 |
Merge Chromium at r66597: Initial merge by git.
Change-Id: I9639f8a997f90ec219573aa22a49f5dbde78cc7b
Diffstat (limited to 'third_party')
39 files changed, 1347 insertions, 617 deletions
diff --git a/third_party/libjingle/source/CHANGELOG b/third_party/libjingle/source/CHANGELOG index 3dee65e..d1574d8 100644 --- a/third_party/libjingle/source/CHANGELOG +++ b/third_party/libjingle/source/CHANGELOG @@ -1,5 +1,17 @@ Libjingle +0.5.1 - Nov 2, 2010 + - Added support for call encryption. + - Added addtional XEP-166 and XEP-167 features: + - Call redirection + - Candidates in session-accept or session-initiate + - Added support for bandwidth control. + - Added features in examples/call: + - bandwidth control on initiate or accept + - turn on/off SSL + - control signaling protocol + - send chat message + 0.5.0 - Sep 16, 2010 - Implemented Jingle protocols XEP-166 and XEP-167. - Backward compatible with Google Talk Call Signaling protocol implemented diff --git a/third_party/libjingle/source/talk/base/fileutils.h b/third_party/libjingle/source/talk/base/fileutils.h index 8ad631f..217cd83 100644 --- a/third_party/libjingle/source/talk/base/fileutils.h +++ b/third_party/libjingle/source/talk/base/fileutils.h @@ -339,6 +339,10 @@ class Filesystem { 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/third_party/libjingle/source/talk/base/helpers.cc b/third_party/libjingle/source/talk/base/helpers.cc index 94ca57e..f8d8a9f 100644 --- a/third_party/libjingle/source/talk/base/helpers.cc +++ b/third_party/libjingle/source/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 @@ class SecureRandomGenerator : public RandomGenerator { RtlGenRandomProc rtl_gen_random_; }; #else -#ifndef USE_OPENSSL +#ifndef SSL_USE_OPENSSL // The old RNG. class SecureRandomGenerator : public RandomGenerator { public: @@ -139,14 +139,14 @@ class SecureRandomGenerator : public RandomGenerator { 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 @@ class SecureRandomGenerator : public RandomGenerator { 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 @@ bool InitRandom(const char* seed, size_t len) { 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/third_party/libjingle/source/talk/base/helpers.h b/third_party/libjingle/source/talk/base/helpers.h index 878efc1..83a3c7f 100644 --- a/third_party/libjingle/source/talk/base/helpers.h +++ b/third_party/libjingle/source/talk/base/helpers.h @@ -42,8 +42,14 @@ bool InitRandom(const char* seed, size_t len); // 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/third_party/libjingle/source/talk/base/linux.cc b/third_party/libjingle/source/talk/base/linux.cc index 2061d73..03d486b 100644 --- a/third_party/libjingle/source/talk/base/linux.cc +++ b/third_party/libjingle/source/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 @@ ProcCpuInfo::~ProcCpuInfo() { 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 @@ std::string ReadLinuxUname() { 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/third_party/libjingle/source/talk/base/linux.h b/third_party/libjingle/source/talk/base/linux.h index 808dc0e..37b845b 100644 --- a/third_party/libjingle/source/talk/base/linux.h +++ b/third_party/libjingle/source/talk/base/linux.h @@ -91,6 +91,11 @@ std::string ReadLinuxLsbRelease(); // 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/third_party/libjingle/source/talk/base/macutils.cc b/third_party/libjingle/source/talk/base/macutils.cc index 69a2f5d..fefe2b9 100644 --- a/third_party/libjingle/source/talk/base/macutils.cc +++ b/third_party/libjingle/source/talk/base/macutils.cc @@ -144,13 +144,77 @@ bool GetQuickTimeVersion(std::string* out) { 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/third_party/libjingle/source/talk/base/macutils.h b/third_party/libjingle/source/talk/base/macutils.h index 1cd64ab..30bb300 100644 --- a/third_party/libjingle/source/talk/base/macutils.h +++ b/third_party/libjingle/source/talk/base/macutils.h @@ -55,6 +55,10 @@ bool GetOSVersion(int* major, int* minor, int* bugfix); 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/third_party/libjingle/source/talk/examples/call/call_main.cc b/third_party/libjingle/source/talk/examples/call/call_main.cc index dde0784..541aca8 100644 --- a/third_party/libjingle/source/talk/examples/call/call_main.cc +++ b/third_party/libjingle/source/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 @@ cricket::MediaEngine* CreateFileMediaEngine(const char* voice_in, // 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 @@ int main(int argc, char **argv) { // 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 @@ int main(int argc, char **argv) { 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 @@ int main(int argc, char **argv) { 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 @@ int main(int argc, char **argv) { 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/third_party/libjingle/source/talk/examples/call/call_main.cc~ b/third_party/libjingle/source/talk/examples/call/call_main.cc~ deleted file mode 100644 index fd7aafd..0000000 --- a/third_party/libjingle/source/talk/examples/call/call_main.cc~ +++ /dev/null @@ -1,348 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <time.h> -#include <iomanip> -#include <cstdio> -#include <cstring> -#include <vector> -#include "talk/base/logging.h" -#include "talk/base/flags.h" -#include "talk/base/pathutils.h" -#include "talk/base/stream.h" -#include "talk/base/ssladapter.h" -#include "talk/base/win32socketserver.h" -#include "talk/xmpp/xmppclientsettings.h" -#include "talk/examples/login/xmppthread.h" -#include "talk/examples/login/xmppauth.h" -#include "talk/examples/login/xmpppump.h" -#include "talk/examples/call/callclient.h" -#include "talk/examples/call/console.h" -#include "talk/session/phone/filemediaengine.h" - -class DebugLog : public sigslot::has_slots<> { - public: - DebugLog() : - debug_input_buf_(NULL), debug_input_len_(0), debug_input_alloc_(0), - debug_output_buf_(NULL), debug_output_len_(0), debug_output_alloc_(0), - censor_password_(false) - {} - char * debug_input_buf_; - int debug_input_len_; - int debug_input_alloc_; - char * debug_output_buf_; - int debug_output_len_; - int debug_output_alloc_; - bool censor_password_; - - void Input(const char * data, int len) { - if (debug_input_len_ + len > debug_input_alloc_) { - char * old_buf = debug_input_buf_; - debug_input_alloc_ = 4096; - while (debug_input_alloc_ < debug_input_len_ + len) { - debug_input_alloc_ *= 2; - } - debug_input_buf_ = new char[debug_input_alloc_]; - memcpy(debug_input_buf_, old_buf, debug_input_len_); - delete[] old_buf; - } - memcpy(debug_input_buf_ + debug_input_len_, data, len); - debug_input_len_ += len; - DebugPrint(debug_input_buf_, &debug_input_len_, false); - } - - void Output(const char * data, int len) { - if (debug_output_len_ + len > debug_output_alloc_) { - char * old_buf = debug_output_buf_; - debug_output_alloc_ = 4096; - while (debug_output_alloc_ < debug_output_len_ + len) { - debug_output_alloc_ *= 2; - } - debug_output_buf_ = new char[debug_output_alloc_]; - memcpy(debug_output_buf_, old_buf, debug_output_len_); - delete[] old_buf; - } - memcpy(debug_output_buf_ + debug_output_len_, data, len); - debug_output_len_ += len; - DebugPrint(debug_output_buf_, &debug_output_len_, true); - } - - static bool IsAuthTag(const char * str, size_t len) { - if (str[0] == '<' && str[1] == 'a' && - str[2] == 'u' && - str[3] == 't' && - str[4] == 'h' && - str[5] <= ' ') { - std::string tag(str, len); - - if (tag.find("mechanism") != std::string::npos) - return true; - } - return false; - } - - void DebugPrint(char * buf, int * plen, bool output) { - int len = *plen; - if (len > 0) { - time_t tim = time(NULL); - struct tm * now = localtime(&tim); - char *time_string = asctime(now); - if (time_string) { - size_t time_len = strlen(time_string); - if (time_len > 0) { - time_string[time_len-1] = 0; // trim off terminating \n - } - } - LOG(INFO) << (output ? "SEND >>>>>>>>>>>>>>>>" : "RECV <<<<<<<<<<<<<<<<") - << " : " << time_string; - - bool indent; - int start = 0, nest = 3; - for (int i = 0; i < len; i += 1) { - if (buf[i] == '>') { - if ((i > 0) && (buf[i-1] == '/')) { - indent = false; - } else if ((start + 1 < len) && (buf[start + 1] == '/')) { - indent = false; - nest -= 2; - } else { - indent = true; - } - - // Output a tag - LOG(INFO) << std::setw(nest) << " " - << std::string(buf + start, i + 1 - start); - - if (indent) - nest += 2; - - // Note if it's a PLAIN auth tag - if (IsAuthTag(buf + start, i + 1 - start)) { - censor_password_ = true; - } - - // incr - start = i + 1; - } - - if (buf[i] == '<' && start < i) { - if (censor_password_) { - LOG(INFO) << std::setw(nest) << " " << "## TEXT REMOVED ##"; - censor_password_ = false; - } else { - LOG(INFO) << std::setw(nest) << " " - << std::string(buf + start, i - start); - } - start = i; - } - } - len = len - start; - memcpy(buf, buf + start, len); - *plen = len; - } - } -}; - -static DebugLog debug_log_; -static const int DEFAULT_PORT = 5222; - - -cricket::MediaEngine* CreateFileMediaEngine(const char* voice_in, - const char* voice_out, - const char* video_in, - const char* video_out) { - cricket::FileMediaEngine* file_media_engine = new cricket::FileMediaEngine; - // Set the RTP dump file names. - if (voice_in) { - file_media_engine->set_voice_input_filename(voice_in); - } - if (voice_out) { - file_media_engine->set_voice_output_filename(voice_out); - } - if (video_in) { - file_media_engine->set_video_input_filename(video_in); - } - if (video_out) { - file_media_engine->set_video_output_filename(video_out); - } - - // Set voice and video codecs. TODO: The codecs actually depend on - // the the input voice and video streams. - std::vector<cricket::AudioCodec> voice_codecs; - voice_codecs.push_back( - 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( - cricket::VideoCodec(97, "H264", 320, 240, 30, 0)); - file_media_engine->set_video_codecs(video_codecs); - - return file_media_engine; -} - -int main(int argc, char **argv) { - // This app has three threads. The main thread will run the XMPP client, - // which will print to the screen in its own thread. A second thread - // will get input from the console, parse it, and pass the appropriate - // message back to the XMPP client's thread. A third thread is used - // by MediaSessionClient as its worker thread. - - // define options - DEFINE_bool(a, false, "Turn on auto accept."); - DEFINE_bool(d, false, "Turn on debugging."); - DEFINE_bool(testserver, false, "Use test server"); - 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."); - DEFINE_string(s, "talk.google.com", "The connection server to use."); - DEFINE_string(voiceinput, NULL, "RTP dump file for voice input."); - DEFINE_string(voiceoutput, NULL, "RTP dump file for voice output."); - DEFINE_string(videoinput, NULL, "RTP dump file for video input."); - DEFINE_string(videooutput, NULL, "RTP dump file for video output."); - DEFINE_bool(help, false, "Prints this message"); - - // parse options - FlagList::SetFlagsFromCommandLine(&argc, argv, true); - if (FLAG_help) { - FlagList::Print(NULL, false); - return 0; - } - - bool auto_accept = FLAG_a; - bool debug = FLAG_d; - bool test_server = FLAG_testserver; - int32 portallocator_flags = FLAG_portallocator; - std::string pmuc_domain = FLAG_pmuc; - std::string server = FLAG_s; - - // parse username and password, if present - buzz::Jid jid; - std::string username; - talk_base::InsecureCryptStringImpl pass; - if (argc > 1) { - username = argv[1]; - if (argc > 2) { - pass.password() = argv[2]; - } - } - - if (debug) - talk_base::LogMessage::LogToDebug(talk_base::LS_VERBOSE); - - if (username.empty()) { - std::cout << "JID: "; - std::cin >> username; - } - if (username.find('@') == std::string::npos) { - username.append("@localhost"); - } - jid = buzz::Jid(username); - if (!jid.IsValid() || jid.node() == "") { - printf("Invalid JID. JIDs should be in the form user@domain\n"); - return 1; - } - if (pass.password().empty() && !test_server) { - Console::SetEcho(false); - std::cout << "Password: "; - std::cin >> pass.password(); - Console::SetEcho(true); - std::cout << std::endl; - } - - buzz::XmppClientSettings xcs; - xcs.set_user(jid.node()); - xcs.set_resource("call"); - xcs.set_host(jid.domain()); - xcs.set_use_tls(!test_server); - - if (test_server) { - pass.password() = jid.node(); - xcs.set_allow_plain(true); - } - xcs.set_pass(talk_base::CryptString(pass)); - - std::string host; - int port; - - int colon = server.find(':'); - if (colon == -1) { - host = server; - port = DEFAULT_PORT; - } else { - host = server.substr(0, colon); - port = atoi(server.substr(colon + 1).c_str()); - } - - xcs.set_server(talk_base::SocketAddress(host, port)); - printf("Logging in to %s as %s\n", server.c_str(), jid.Str().c_str()); - - talk_base::InitializeSSL(); - - -#if WIN32 - // Need to pump messages on our main thread on Windows. - talk_base::Win32Thread w32_thread; - talk_base::ThreadManager::SetCurrent(&w32_thread); -#endif - talk_base::Thread* main_thread = talk_base::Thread::Current(); - - XmppPump pump; - CallClient *client = new CallClient(pump.client()); - - if (FLAG_voiceinput || FLAG_voiceoutput || - FLAG_videoinput || FLAG_videooutput) { - // If any dump file is specified, we use FileMediaEngine. - cricket::MediaEngine* engine = CreateFileMediaEngine(FLAG_voiceinput, - FLAG_voiceoutput, - FLAG_videoinput, - FLAG_videooutput); - // The engine will be released by the client later. - client->SetMediaEngine(engine); - } - - Console *console = new Console(main_thread, client); - client->SetConsole(console); - client->SetAutoAccept(auto_accept); - client->SetPmucDomain(pmuc_domain); - client->SetPortAllocatorFlags(portallocator_flags); - console->Start(); - - if (debug) { - pump.client()->SignalLogInput.connect(&debug_log_, &DebugLog::Input); - pump.client()->SignalLogOutput.connect(&debug_log_, &DebugLog::Output); - } - - pump.DoLogin(xcs, new XmppSocket(true), NULL); - main_thread->Run(); - pump.DoDisconnect(); - - console->Stop(); - delete console; - delete client; - - return 0; -} diff --git a/third_party/libjingle/source/talk/examples/call/callclient.cc b/third_party/libjingle/source/talk/examples/call/callclient.cc index 03edf95..eb761d1 100644 --- a/third_party/libjingle/source/talk/examples/call/callclient.cc +++ b/third_party/libjingle/source/talk/examples/call/callclient.cc @@ -35,6 +35,7 @@ #include "talk/base/network.h" #include "talk/base/socketaddress.h" #include "talk/base/stringutils.h" +#include "talk/base/stringencode.h" #include "talk/p2p/base/sessionmanager.h" #include "talk/p2p/client/basicportallocator.h" #include "talk/p2p/client/sessionmanagertask.h" @@ -82,6 +83,25 @@ const char* DescribeStatus(buzz::Status::Show show, const std::string& desc) { } } +std::string GetWord(const std::vector<std::string>& words, + size_t index, const std::string& def) { + if (words.size() > index) { + return words[index]; + } else { + return def; + } +} + +int GetInt(const std::vector<std::string>& words, size_t index, int def) { + int val; + if (words.size() > index && talk_base::FromString(words[index], &val)) { + return val; + } else { + return def; + } +} + + } // namespace const char* CALL_COMMANDS = @@ -97,7 +117,7 @@ const char* CALL_COMMANDS = const char* RECEIVE_COMMANDS = "Available commands:\n" "\n" -" accept Accepts the incoming call and switches to it.\n" +" accept [bw] Accepts the incoming call and switches to it.\n" " reject Rejects the incoming call and stays with the current call.\n" " quit Quits the application.\n" ""; @@ -107,10 +127,10 @@ const char* CONSOLE_COMMANDS = "\n" " roster Prints the online friends from your roster.\n" " friend user Request to add a user to your roster.\n" -" call [jid] Initiates a call to the user[/room] with the\n" -" given JID.\n" -" vcall [jid] Initiates a video call to the user[/room] with\n" -" the given JID.\n" +" call [jid] [bw] Initiates a call to the user[/room] with the\n" +" given JID and with optional bandwidth.\n" +" vcall [jid] [bw] Initiates a video call to the user[/room] with\n" +" the given JID and with optional bandwidth.\n" " voicemail [jid] Leave a voicemail for the user with the given JID.\n" " join [room] Joins a multi-user-chat.\n" " invite user [room] Invites a friend to a multi-user-chat.\n" @@ -142,53 +162,73 @@ void CallClient::ParseLine(const std::string& line) { } // Global commands - if ((words.size() == 1) && (words[0] == "quit")) { + const std::string& command = GetWord(words, 0, ""); + if (command == "quit") { Quit(); } else if (call_ && incoming_call_) { - if ((words.size() == 1) && (words[0] == "accept")) { - Accept(); - } else if ((words.size() == 1) && (words[0] == "reject")) { + if (command == "accept") { + cricket::CallOptions options; + options.video_bandwidth = GetInt(words, 1, cricket::kAutoBandwidth); + Accept(options); + } else if (command == "reject") { Reject(); } else { console_->Print(RECEIVE_COMMANDS); } } else if (call_) { - if ((words.size() == 1) && (words[0] == "hangup")) { + if (command == "hangup") { // TODO: do more shutdown here, move to Terminate() call_->Terminate(); call_ = NULL; session_ = NULL; console_->SetPrompt(NULL); - } else if ((words.size() == 1) && (words[0] == "mute")) { + } else if (command == "mute") { call_->Mute(true); - } else if ((words.size() == 1) && (words[0] == "unmute")) { + } else if (command == "unmute") { call_->Mute(false); - } else if ((words.size() == 2) && (words[0] == "dtmf")) { + } else if ((command == "dtmf") && (words.size() == 2)) { int ev = std::string("0123456789*#").find(words[1][0]); call_->PressDTMF(ev); } else { console_->Print(CALL_COMMANDS); } } else { - if ((words.size() == 1) && (words[0] == "roster")) { + if (command == "roster") { PrintRoster(); - } else if ((words.size() == 2) && (words[0] == "friend")) { + } else if (command == "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) && (command == "friend")) { InviteFriend(words[1]); - } else if ((words.size() >= 1) && (words[0] == "call")) { - MakeCallTo((words.size() >= 2) ? words[1] : "", false); - } else if ((words.size() >= 1) && (words[0] == "vcall")) { - MakeCallTo((words.size() >= 2) ? words[1] : "", true); - } else if ((words.size() >= 1) && (words[0] == "join")) { - JoinMuc((words.size() >= 2) ? words[1] : ""); - } else if ((words.size() >= 2) && (words[0] == "invite")) { - InviteToMuc(words[1], (words.size() >= 3) ? words[2] : ""); - } else if ((words.size() >= 1) && (words[0] == "leave")) { - LeaveMuc((words.size() >= 2) ? words[1] : ""); - } else if ((words.size() == 1) && (words[0] == "getdevs")) { + } else if (command == "call") { + std::string to = GetWord(words, 1, ""); + MakeCallTo(to, cricket::CallOptions()); + } else if (command == "vcall") { + std::string to = GetWord(words, 1, ""); + int bandwidth = GetInt(words, 2, cricket::kAutoBandwidth); + cricket::CallOptions options; + options.is_video = true; + options.video_bandwidth = bandwidth; + MakeCallTo(to, options); + } else if (command == "join") { + JoinMuc(GetWord(words, 1, "")); + } else if ((words.size() >= 2) && (command == "invite")) { + InviteToMuc(words[1], GetWord(words, 2, "")); + } else if (command == "leave") { + LeaveMuc(GetWord(words, 1, "")); + } else if (command == "getdevs") { GetDevices(); - } else if ((words.size() == 2) && (words[0] == "setvol")) { + } else if ((words.size() == 2) && (command == "setvol")) { SetVolume(words[1]); - } else if ((words.size() >= 1) && (words[0] == "voicemail")) { + } else if (command == "voicemail") { CallVoicemail((words.size() >= 2) ? words[1] : ""); } else { console_->Print(CONSOLE_COMMANDS); @@ -201,7 +241,8 @@ CallClient::CallClient(buzz::XmppClient* xmpp_client) 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 +350,8 @@ void CallClient::InitPhone() { 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 +391,11 @@ void CallClient::OnRequestSignaling() { 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()) { @@ -365,8 +413,9 @@ void CallClient::OnSessionState(cricket::Call* call, call_ = call; session_ = session; incoming_call_ = true; + cricket::CallOptions options; if (auto_accept_) { - Accept(); + Accept(options); } } else if (state == cricket::Session::STATE_SENTINITIATE) { console_->Print("calling..."); @@ -458,6 +507,19 @@ void CallClient::PrintRoster() { 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() == "") { @@ -473,16 +535,20 @@ void CallClient::InviteFriend(const std::string& name) { console_->Printf("Requesting to befriend %s.\n", name.c_str()); } -void CallClient::MakeCallTo(const std::string& name, bool video) { +void CallClient::MakeCallTo(const std::string& name, + const cricket::CallOptions& given_options) { + // Copy so we can change .is_muc. + cricket::CallOptions options = given_options; + bool found = false; - bool is_muc = false; + options.is_muc = false; buzz::Jid callto_jid(name); buzz::Jid found_jid; if (name.length() == 0 && mucs_.size() > 0) { // if no name, and in a MUC, establish audio with the MUC found_jid = mucs_.begin()->first; found = true; - is_muc = true; + options.is_muc = true; } else if (name[0] == '+') { // if the first character is a +, assume it's a phone number found_jid = callto_jid; @@ -507,28 +573,29 @@ void CallClient::MakeCallTo(const std::string& name, bool video) { mucs_[callto_jid]->state() == buzz::Muc::MUC_JOINED) { found = true; found_jid = callto_jid; - is_muc = true; + options.is_muc = true; } } } if (found) { - console_->Printf("Found %s '%s'", is_muc ? "room" : "online friend", + console_->Printf("Found %s '%s'", options.is_muc ? "room" : "online friend", found_jid.Str().c_str()); - PlaceCall(found_jid, is_muc, video); + PlaceCall(found_jid, options); } else { console_->Printf("Could not find online friend '%s'", name.c_str()); } } -void CallClient::PlaceCall(const buzz::Jid& jid, bool is_muc, bool video) { +void CallClient::PlaceCall(const buzz::Jid& jid, + const cricket::CallOptions& options) { media_client_->SignalCallDestroy.connect( this, &CallClient::OnCallDestroy); if (!call_) { - call_ = media_client_->CreateCall(video, is_muc); + call_ = media_client_->CreateCall(); console_->SetPrompt(jid.Str().c_str()); - session_ = call_->InitiateSession(jid); - if (is_muc) { + session_ = call_->InitiateSession(jid, options); + if (options.is_muc) { // If people in this room are already in a call, must add all their // streams. buzz::Muc::MemberMap& members = mucs_[jid]->members(); @@ -566,7 +633,7 @@ void CallClient::CallVoicemail(const std::string& name) { void CallClient::OnFoundVoicemailJid(const buzz::Jid& to, const buzz::Jid& voicemail) { console_->Printf("Calling %s's voicemail.\n", to.Str().c_str()); - PlaceCall(voicemail, false, false); + PlaceCall(voicemail, cricket::CallOptions()); } void CallClient::OnVoicemailJidError(const buzz::Jid& to) { @@ -587,10 +654,10 @@ void CallClient::RemoveStream(uint32 audio_src_id, uint32 video_src_id) { } } -void CallClient::Accept() { +void CallClient::Accept(const cricket::CallOptions& options) { ASSERT(call_ && incoming_call_); ASSERT(call_->sessions().size() == 1); - call_->AcceptSession(call_->sessions()[0]); + call_->AcceptSession(call_->sessions()[0], options); media_client_->SetFocus(call_); if (call_->video()) { call_->SetLocalRenderer(local_renderer_); diff --git a/third_party/libjingle/source/talk/examples/call/callclient.h b/third_party/libjingle/source/talk/examples/call/callclient.h index af27f34..a5686b4 100644 --- a/third_party/libjingle/source/talk/examples/call/callclient.h +++ b/third_party/libjingle/source/talk/examples/call/callclient.h @@ -66,7 +66,9 @@ class MediaEngine; class MediaSessionClient; class Receiver; class Call; +struct CallOptions; class SessionManagerTask; +enum SignalingProtocol; } struct RosterItem { @@ -98,11 +100,20 @@ class CallClient: public sigslot::has_slots<> { 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 +130,7 @@ class CallClient: public sigslot::has_slots<> { 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, @@ -137,10 +149,10 @@ class CallClient: public sigslot::has_slots<> { static const std::string strerror(buzz::XmppEngine::Error err); void PrintRoster(); - void MakeCallTo(const std::string& name, bool video); - void PlaceCall(const buzz::Jid& jid, bool is_muc, bool video); + void MakeCallTo(const std::string& name, const cricket::CallOptions& options); + void PlaceCall(const buzz::Jid& jid, const cricket::CallOptions& options); void CallVoicemail(const std::string& name); - void Accept(); + void Accept(const cricket::CallOptions& options); void Reject(); void Quit(); @@ -178,6 +190,10 @@ class CallClient: public sigslot::has_slots<> { 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/third_party/libjingle/source/talk/main.scons b/third_party/libjingle/source/talk/main.scons index d4163df..a1348ff 100644 --- a/third_party/libjingle/source/talk/main.scons +++ b/third_party/libjingle/source/talk/main.scons @@ -17,6 +17,7 @@ # import talk import os +import platform #------------------------------------------------------------------------------- # The build files/directories to 'build'. @@ -40,6 +41,7 @@ root_env = Environment( 'component_setup', 'replace_strings', 'talk_noops', + #'talk_linux', ], BUILD_SCONSCRIPTS = components, DESTINATION_ROOT = '$MAIN_DIR/build', @@ -60,6 +62,16 @@ root_env = Environment( ] ) +# 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 # @@ -130,6 +142,9 @@ win_env.Append( # this NTDDI version or else the headers # that LMI includes from it won't compile. 'NTDDI_VERSION=NTDDI_WINXP', + + # npapi.h requires the following: + '_WINDOWS', ], CPPPATH = [ '$THIRD_PARTY/wtl_71/include', @@ -211,6 +226,8 @@ posix_env.Append( 'POSIX', 'DISABLE_DYNAMIC_CAST', 'HAVE_OPENSSL_SSL_H=1', + # The POSIX standard says we have to define this. + '_REENTRANT', ], CCFLAGS = [ '-m32', @@ -271,13 +288,9 @@ mac_dbg_env = mac_env.Clone( 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 +301,7 @@ mac_opt_env = mac_env.Clone( 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 +316,92 @@ envs.append(mac_opt_env) #------------------------------------------------------------------------------- # 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/third_party/libjingle/source/talk/p2p/base/constants.cc b/third_party/libjingle/source/talk/p2p/base/constants.cc index 9e45457..1becb39 100644 --- a/third_party/libjingle/source/talk/p2p/base/constants.cc +++ b/third_party/libjingle/source/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 { @@ -61,6 +64,10 @@ const std::string GINGLE_ACTION_REJECT("reject"); const std::string GINGLE_ACTION_TERMINATE("terminate"); const std::string GINGLE_ACTION_CANDIDATES("candidates"); +const std::string LN_ERROR("error"); +const buzz::QName QN_GINGLE_REDIRECT(true, NS_GINGLE, "redirect"); +const std::string STR_REDIRECT_PREFIX("xmpp:"); + // Session Contents (aka Gingle <session><description> // or Jingle <content><description>) const std::string LN_DESCRIPTION("description"); @@ -81,6 +88,7 @@ const std::string PAYLOADTYPE_PARAMETER_BITRATE("bitrate"); const std::string PAYLOADTYPE_PARAMETER_HEIGHT("height"); const std::string PAYLOADTYPE_PARAMETER_WIDTH("width"); const std::string PAYLOADTYPE_PARAMETER_FRAMERATE("framerate"); +const std::string LN_BANDWIDTH("bandwidth"); const std::string CN_AUDIO("audio"); const std::string CN_VIDEO("video"); @@ -91,6 +99,8 @@ const buzz::QName QN_JINGLE_RTP_CONTENT( true, NS_JINGLE_RTP, LN_DESCRIPTION); const buzz::QName QN_JINGLE_RTP_PAYLOADTYPE( true, NS_JINGLE_RTP, LN_PAYLOADTYPE); +const buzz::QName QN_JINGLE_RTP_BANDWIDTH( + true, NS_JINGLE_RTP, LN_BANDWIDTH); const buzz::QName QN_PARAMETER(true, NS_JINGLE_RTP, "parameter"); const std::string NS_GINGLE_AUDIO("http://www.google.com/session/phone"); @@ -105,7 +115,19 @@ const buzz::QName QN_GINGLE_VIDEO_CONTENT( const buzz::QName QN_GINGLE_VIDEO_PAYLOADTYPE( true, NS_GINGLE_VIDEO, LN_PAYLOADTYPE); 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"); +const buzz::QName QN_GINGLE_VIDEO_BANDWIDTH( + true, NS_GINGLE_VIDEO, LN_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"); diff --git a/third_party/libjingle/source/talk/p2p/base/constants.h b/third_party/libjingle/source/talk/p2p/base/constants.h index 7e33c5b..9f93cbc 100644 --- a/third_party/libjingle/source/talk/p2p/base/constants.h +++ b/third_party/libjingle/source/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() @@ -87,6 +80,9 @@ extern const std::string GINGLE_ACTION_REJECT; extern const std::string GINGLE_ACTION_TERMINATE; extern const std::string GINGLE_ACTION_CANDIDATES; +extern const std::string LN_ERROR; +extern const buzz::QName QN_GINGLE_REDIRECT; +extern const std::string STR_REDIRECT_PREFIX; // Session Contents (aka Gingle <session><description> // or Jingle <content><description>) @@ -109,6 +105,7 @@ extern const std::string PAYLOADTYPE_PARAMETER_BITRATE; extern const std::string PAYLOADTYPE_PARAMETER_HEIGHT; extern const std::string PAYLOADTYPE_PARAMETER_WIDTH; extern const std::string PAYLOADTYPE_PARAMETER_FRAMERATE; +extern const std::string LN_BANDWIDTH; // CN_ == "content name". When we initiate a session, we choose the // name, and when we receive a Gingle session, we provide default @@ -122,6 +119,7 @@ extern const std::string CN_OTHER; extern const std::string NS_JINGLE_RTP; extern const buzz::QName QN_JINGLE_RTP_CONTENT; extern const buzz::QName QN_JINGLE_RTP_PAYLOADTYPE; +extern const buzz::QName QN_JINGLE_RTP_BANDWIDTH; extern const std::string NS_GINGLE_AUDIO; extern const buzz::QName QN_GINGLE_AUDIO_CONTENT; @@ -133,6 +131,17 @@ extern const buzz::QName QN_GINGLE_VIDEO_PAYLOADTYPE; 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/third_party/libjingle/source/talk/p2p/base/parsing.cc b/third_party/libjingle/source/talk/p2p/base/parsing.cc index 1ece5d0..da4c31b 100644 --- a/third_party/libjingle/source/talk/p2p/base/parsing.cc +++ b/third_party/libjingle/source/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 @@ std::string GetXmlAttr(const buzz::XmlElement* elem, 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/third_party/libjingle/source/talk/p2p/base/parsing.h b/third_party/libjingle/source/talk/p2p/base/parsing.h index 0ae7403..805372b 100644 --- a/third_party/libjingle/source/talk/p2p/base/parsing.h +++ b/third_party/libjingle/source/talk/p2p/base/parsing.h @@ -77,6 +77,12 @@ bool BadWrite(const std::string& text, WriteError* error); 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/third_party/libjingle/source/talk/p2p/base/session.cc b/third_party/libjingle/source/talk/p2p/base/session.cc index 806c025..38b91ff 100644 --- a/third_party/libjingle/source/talk/p2p/base/session.cc +++ b/third_party/libjingle/source/talk/p2p/base/session.cc @@ -416,6 +416,42 @@ TransportInfos Session::GetEmptyTransportInfos( 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::SpeculativelyConnectAllTransportChannels() { } } -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_; @@ -646,6 +672,20 @@ void Session::OnFailedSend(const buzz::XmlElement* orig_stanza, return; } + // If the error is a session redirect, call OnRedirectError, which will + // continue the session with a new remote JID. + SessionRedirect redirect; + if (FindSessionRedirect(error_stanza, &redirect)) { + SessionError error; + if (!OnRedirectError(redirect, &error)) { + // TODO: Should we send a message back? The standard + // says nothing about it. + LOG(LS_ERROR) << "Failed to redirect: " << error.text; + SetError(ERROR_RESPONSE); + } + return; + } + std::string error_type = "cancel"; const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR); @@ -706,9 +746,8 @@ bool Session::OnInitiateMessage(const SessionMessage& msg, // 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 +767,8 @@ bool Session::OnAcceptMessage(const SessionMessage& msg, MessageError* error) { // 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 +811,8 @@ bool Session::OnTransportInfoMessage(const SessionMessage& msg, 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; } @@ -809,6 +824,32 @@ bool Session::OnTransportAcceptMessage(const SessionMessage& msg, return true; } +bool BareJidsEqual(const std::string& name1, + const std::string& name2) { + buzz::Jid jid1(name1); + buzz::Jid jid2(name2); + + return jid1.IsValid() && jid2.IsValid() && jid1.BareEquals(jid2); +} + +bool Session::OnRedirectError(const SessionRedirect& redirect, + SessionError* error) { + MessageError message_error; + if (!CheckState(STATE_SENTINITIATE, &message_error)) { + return BadWrite(message_error.text, error); + } + + if (!BareJidsEqual(remote_name_, redirect.target)) + return BadWrite("Redirection not allowed: must be the same bare jid.", + error); + + // When we receive a redirect, we point the session at the new JID + // and resend the candidates. + remote_name_ = redirect.target; + return (SendInitiateMessage(local_description(), error) && + ResendAllTransportInfoMessages(error)); +} + bool Session::CheckState(State state, MessageError* error) { ASSERT(state_ == state); if (state_ != state) { @@ -909,6 +950,25 @@ bool Session::WriteSessionAction(SignalingProtocol protocol, elems, error); } +bool Session::ResendAllTransportInfoMessages(SessionError* error) { + for (TransportMap::iterator iter = transports_.begin(); + iter != transports_.end(); ++iter) { + TransportProxy* transproxy = iter->second; + if (transproxy->sent_candidates().size() > 0) { + if (!SendTransportInfoMessage( + TransportInfo( + transproxy->content_name(), + transproxy->type(), + transproxy->sent_candidates()), + error)) { + return false; + } + transproxy->ClearSentCandidates(); + } + } + return true; +} + bool Session::SendMessage(ActionType type, const XmlElements& action_elems, SessionError* error) { talk_base::scoped_ptr<buzz::XmlElement> stanza( diff --git a/third_party/libjingle/source/talk/p2p/base/session.h b/third_party/libjingle/source/talk/p2p/base/session.h index 64bb83a..ddbc663 100644 --- a/third_party/libjingle/source/talk/p2p/base/session.h +++ b/third_party/libjingle/source/talk/p2p/base/session.h @@ -251,6 +251,10 @@ class BaseSession : public sigslot::has_slots<>, // 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 @@ class Session : public BaseSession { 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. @@ -437,6 +439,7 @@ class Session : public BaseSession { bool SendTerminateMessage(const std::string& reason, SessionError* error); bool SendTransportInfoMessage(const TransportInfo& tinfo, SessionError* error); + bool ResendAllTransportInfoMessages(SessionError* error); // Both versions of SendMessage send a message of the given type to // the other client. Can pass either a set of elements or an @@ -507,6 +510,7 @@ class Session : public BaseSession { bool OnTerminateMessage(const SessionMessage& msg, MessageError* error); bool OnTransportInfoMessage(const SessionMessage& msg, MessageError* error); bool OnTransportAcceptMessage(const SessionMessage& msg, MessageError* error); + bool OnRedirectError(const SessionRedirect& redirect, SessionError* error); // Verifies that we are in the appropriate state to receive this message. bool CheckState(State state, MessageError* error); diff --git a/third_party/libjingle/source/talk/p2p/base/sessionmessages.cc b/third_party/libjingle/source/talk/p2p/base/sessionmessages.cc index f7c424e..cfd035d 100644 --- a/third_party/libjingle/source/talk/p2p/base/sessionmessages.cc +++ b/third_party/libjingle/source/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 @@ bool ParseJingleSessionMessage(const buzz::XmlElement* jingle, 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) @@ -818,4 +820,34 @@ bool WriteTransportInfos(SignalingProtocol protocol, } } +bool GetUriTarget(const std::string& prefix, const std::string& str, + std::string* after) { + size_t pos = str.find(prefix); + if (pos == std::string::npos) + return false; + + *after = str.substr(pos + prefix.size(), std::string::npos); + return true; +} + +bool FindSessionRedirect(const buzz::XmlElement* stanza, + SessionRedirect* redirect) { + const buzz::XmlElement* error_elem = GetXmlChild(stanza, LN_ERROR); + if (error_elem == NULL) + return false; + + const buzz::XmlElement* redirect_elem = + error_elem->FirstNamed(QN_GINGLE_REDIRECT); + if (redirect_elem == NULL) + redirect_elem = error_elem->FirstNamed(buzz::QN_STANZA_REDIRECT); + if (redirect_elem == NULL) + return false; + + if (!GetUriTarget(STR_REDIRECT_PREFIX, redirect_elem->BodyText(), + &redirect->target)) + return false; + + return true; +} + } // namespace cricket diff --git a/third_party/libjingle/source/talk/p2p/base/sessionmessages.h b/third_party/libjingle/source/talk/p2p/base/sessionmessages.h index ff8641c..eee8452 100644 --- a/third_party/libjingle/source/talk/p2p/base/sessionmessages.h +++ b/third_party/libjingle/source/talk/p2p/base/sessionmessages.h @@ -149,6 +149,10 @@ struct SessionTerminate { std::string debug_reason; }; +struct SessionRedirect { + std::string target; +}; + bool IsSessionMessage(const buzz::XmlElement* stanza); bool ParseSessionMessage(const buzz::XmlElement* stanza, SessionMessage* msg, @@ -208,6 +212,9 @@ bool WriteTransportInfos(SignalingProtocol protocol, const TransportParserMap& trans_parsers, XmlElements* elems, WriteError* error); +// Handles both Gingle and Jingle syntax. +bool FindSessionRedirect(const buzz::XmlElement* stanza, + SessionRedirect* redirect); } // namespace cricket #endif // TALK_P2P_BASE_SESSIONMESSAGES_H_ diff --git a/third_party/libjingle/source/talk/p2p/base/transport.cc b/third_party/libjingle/source/talk/p2p/base/transport.cc index 663a608..9d8fc32 100644 --- a/third_party/libjingle/source/talk/p2p/base/transport.cc +++ b/third_party/libjingle/source/talk/p2p/base/transport.cc @@ -155,17 +155,20 @@ void Transport::DestroyChannel_w(const std::string& name) { } 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::DestroyAllChannels_w() { 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/third_party/libjingle/source/talk/session/phone/call.cc b/third_party/libjingle/source/talk/session/phone/call.cc index c85be6a..fa4d33d 100644 --- a/third_party/libjingle/source/talk/session/phone/call.cc +++ b/third_party/libjingle/source/talk/session/phone/call.cc @@ -30,6 +30,7 @@ #include "talk/base/logging.h" #include "talk/base/thread.h" #include "talk/session/phone/call.h" +#include "talk/session/phone/mediasessionclient.h" namespace cricket { @@ -45,10 +46,13 @@ const int kNoVoicemailTimeout = 1000*180; const int kMediaMonitorInterval = 1000*15; } -Call::Call(MediaSessionClient *session_client, bool video, bool mux) - : id_(talk_base::CreateRandomId()), session_client_(session_client), - local_renderer_(NULL), video_(video), mux_(mux), - muted_(false), send_to_voicemail_(true), playing_dtmf_(false) { +Call::Call(MediaSessionClient* session_client) + : id_(talk_base::CreateRandomId()), + session_client_(session_client), + local_renderer_(NULL), + muted_(false), + send_to_voicemail_(true), + playing_dtmf_(false) { } Call::~Call() { @@ -60,8 +64,9 @@ Call::~Call() { talk_base::Thread::Current()->Clear(this); } -Session *Call::InitiateSession(const buzz::Jid &jid) { - const SessionDescription* offer = session_client_->CreateOffer(video_, mux_); +Session *Call::InitiateSession(const buzz::Jid &jid, + const CallOptions& options) { + const SessionDescription* offer = session_client_->CreateOffer(options); Session *session = session_client_->CreateSession(this); AddSession(session, offer); @@ -86,13 +91,14 @@ void Call::IncomingSession( SignalSessionState(this, session, Session::STATE_RECEIVEDINITIATE); } -void Call::AcceptSession(BaseSession *session) { +void Call::AcceptSession(BaseSession* session, + const cricket::CallOptions& options) { std::vector<Session *>::iterator it; it = std::find(sessions_.begin(), sessions_.end(), session); ASSERT(it != sessions_.end()); if (it != sessions_.end()) { session->Accept( - session_client_->CreateAnswer(session->remote_description())); + session_client_->CreateAnswer(session->remote_description(), options)); } } @@ -196,6 +202,9 @@ bool Call::AddSession(Session *session, const SessionDescription* offer) { VideoChannel *video_channel = NULL; const ContentInfo* audio_offer = GetFirstAudioContent(offer); + const ContentInfo* video_offer = GetFirstVideoContent(offer); + video_ = (video_offer != NULL); + ASSERT(audio_offer != NULL); // Create voice channel and start a media monitor voice_channel = session_client_->channel_manager()->CreateVoiceChannel( @@ -212,8 +221,6 @@ bool Call::AddSession(Session *session, const SessionDescription* offer) { // If desired, create video channel and start a media monitor if (video_ && succeeded) { - const ContentInfo* video_offer = GetFirstVideoContent(offer); - ASSERT(video_offer != NULL); video_channel = session_client_->channel_manager()->CreateVideoChannel( session, video_offer->name, true, voice_channel); // video_channel can be NULL in case of NullVideoEngine. @@ -441,7 +448,7 @@ void Call::OnConnectionMonitor(VoiceChannel *channel, SignalConnectionMonitor(this, infos); } -void Call::OnMediaMonitor(VoiceChannel *channel, const MediaInfo& info) { +void Call::OnMediaMonitor(VoiceChannel *channel, const VoiceMediaInfo& info) { SignalMediaMonitor(this, info); } @@ -454,7 +461,7 @@ void Call::OnConnectionMonitor(VideoChannel *channel, 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/third_party/libjingle/source/talk/session/phone/call.h b/third_party/libjingle/source/talk/session/phone/call.h index de92086..8e9bcdc 100644 --- a/third_party/libjingle/source/talk/session/phone/call.h +++ b/third_party/libjingle/source/talk/session/phone/call.h @@ -36,22 +36,21 @@ #include "talk/p2p/base/session.h" #include "talk/p2p/client/socketmonitor.h" #include "talk/xmpp/jid.h" -#include "talk/session/phone/mediasessionclient.h" -#include "talk/session/phone/voicechannel.h" #include "talk/session/phone/audiomonitor.h" +#include "talk/session/phone/voicechannel.h" namespace cricket { class MediaSessionClient; +struct CallOptions; class Call : public talk_base::MessageHandler, public sigslot::has_slots<> { public: - Call(MediaSessionClient *session_client, - bool video = false, bool mux = false); + Call(MediaSessionClient* session_client); ~Call(); - Session *InitiateSession(const buzz::Jid &jid); - void AcceptSession(BaseSession *session); + Session *InitiateSession(const buzz::Jid &jid, const CallOptions& options); + void AcceptSession(BaseSession *session, const CallOptions& options); void RejectSession(BaseSession *session); void TerminateSession(BaseSession *session); void Terminate(); @@ -92,11 +91,11 @@ class Call : public talk_base::MessageHandler, public sigslot::has_slots<> { 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 +110,11 @@ class Call : public talk_base::MessageHandler, public sigslot::has_slots<> { 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(); @@ -127,7 +126,6 @@ class Call : public talk_base::MessageHandler, public sigslot::has_slots<> { std::map<std::string, VideoChannel *> video_channel_map_; VideoRenderer* local_renderer_; bool video_; - bool mux_; bool muted_; bool send_to_voicemail_; diff --git a/third_party/libjingle/source/talk/session/phone/channel.cc b/third_party/libjingle/source/talk/session/phone/channel.cc index 5fb1dab..76c1cb4 100644 --- a/third_party/libjingle/source/talk/session/phone/channel.cc +++ b/third_party/libjingle/source/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 @@ BaseChannel::BaseChannel(talk_base::Thread* thread, MediaEngine* media_engine, 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 @@ int BaseChannel::SendPacket(bool rtcp, const void* data, size_t len) { 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 @@ void BaseChannel::HandlePacket(bool rtcp, const char* data, size_t len) { } 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 @@ void BaseChannel::ChannelNotWritable_w() { 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 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content, 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 @@ VideoChannel::VideoChannel(talk_base::Thread* thread, // 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::SetRenderer(uint32 ssrc, VideoRenderer* renderer) { } + +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,12 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, if (ret) { ret = SetRtcpMux_w(video->rtcp_mux(), action, CS_REMOTE); } - // TODO: Set bandwidth appropriately here. + // Set video bandwidth parameters. + if (ret) { + int bandwidth_bps = video->bandwidth(); + bool auto_bandwidth = (bandwidth_bps == kAutoBandwidth); + ret = media_channel()->SetSendBandwidth(auto_bandwidth, bandwidth_bps); + } if (ret) { ret = media_channel()->SetSendCodecs(video->codecs()); } @@ -927,6 +982,13 @@ void VideoChannel::OnMessage(talk_base::Message *pmsg) { 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 +1006,7 @@ void VideoChannel::OnMediaMonitorUpdate( 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/third_party/libjingle/source/talk/session/phone/channel.h b/third_party/libjingle/source/talk/session/phone/channel.h index b7981d9..b82a4bf 100644 --- a/third_party/libjingle/source/talk/session/phone/channel.h +++ b/third_party/libjingle/source/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 @@ enum { 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 @@ class BaseChannel 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 @@ class BaseChannel 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 @@ class BaseChannel 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 @@ class BaseChannel 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 @@ class VideoChannel : public BaseChannel { void StopMediaMonitor(); sigslot::signal2<VideoChannel*, const VideoMediaInfo&> SignalMediaMonitor; + bool SendIntraFrame(); + bool RequestIntraFrame(); + private: // overrides from BaseChannel virtual void ChangeState(); @@ -387,6 +418,13 @@ class VideoChannel : public BaseChannel { 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 @@ class VideoChannel : public BaseChannel { 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/third_party/libjingle/source/talk/session/phone/filemediaengine.h b/third_party/libjingle/source/talk/session/phone/filemediaengine.h index 272449a..cc6788c 100644 --- a/third_party/libjingle/source/talk/session/phone/filemediaengine.h +++ b/third_party/libjingle/source/talk/session/phone/filemediaengine.h @@ -149,7 +149,7 @@ class FileVoiceChannel : public VoiceMediaChannel { 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 @@ class FileVideoChannel : public VideoMediaChannel { 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 @@ class FileVideoChannel : public VideoMediaChannel { 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/third_party/libjingle/source/talk/session/phone/mediachannel.h b/third_party/libjingle/source/talk/session/phone/mediachannel.h index ee9d3c7..01bd8b5 100644 --- a/third_party/libjingle/source/talk/session/phone/mediachannel.h +++ b/third_party/libjingle/source/talk/session/phone/mediachannel.h @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2007, Google Inc. + * 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: @@ -88,7 +88,7 @@ class MediaChannel : public sigslot::has_slots<> { 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 @@ enum SendFlags { SEND_MICROPHONE }; -// TODO: separate into VoiceMediaInfo and VideoMediaInfo struct MediaInfo { int fraction_lost; int cum_lost; @@ -114,8 +113,13 @@ struct MediaInfo { 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 @@ class VideoFrame { 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, @@ -228,7 +237,7 @@ class VideoFrame { // Size of an I420 image of given dimensions when stored as a frame buffer. static size_t SizeOf(size_t w, size_t h) { - return w * h * 3 / 2; + return w * h + ((w + 1) / 2) * ((h + 1) / 2) * 2; } protected: @@ -260,6 +269,10 @@ class NullVideoFrame : public VideoFrame { 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 @@ class VideoMediaChannel : public MediaChannel { 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/third_party/libjingle/source/talk/session/phone/mediasessionclient.cc b/third_party/libjingle/source/talk/session/phone/mediasessionclient.cc index 001cf73..f16b531 100644 --- a/third_party/libjingle/source/talk/session/phone/mediasessionclient.cc +++ b/third_party/libjingle/source/talk/session/phone/mediasessionclient.cc @@ -25,23 +25,37 @@ * 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/base/stringencode.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 +64,8 @@ MediaSessionClient::MediaSessionClient( 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,7 +96,46 @@ MediaSessionClient::~MediaSessionClient() { session_manager_->RemoveClient(NS_JINGLE_RTP); } -SessionDescription* MediaSessionClient::CreateOffer(bool video, bool set_ssrc) { +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( + const CallOptions& options) { SessionDescription* offer = new SessionDescription(); AudioContentDescription* audio = new AudioContentDescription(); @@ -92,14 +146,31 @@ SessionDescription* MediaSessionClient::CreateOffer(bool video, bool set_ssrc) { codec != audio_codecs.end(); ++codec) { audio->AddCodec(*codec); } - if (set_ssrc) { + if (options.is_muc) { 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 - if (video) { + if (options.is_video) { VideoContentDescription* video = new VideoContentDescription(); VideoCodecs video_codecs; channel_manager_->GetSupportedVideoCodecs(&video_codecs); @@ -107,10 +178,28 @@ SessionDescription* MediaSessionClient::CreateOffer(bool video, bool set_ssrc) { codec != video_codecs.end(); ++codec) { video->AddCodec(*codec); } - if (set_ssrc) { + if (options.is_muc) { video->set_ssrc(0); } + video->set_bandwidth(options.video_bandwidth); 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,8 +233,25 @@ const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) { 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) { + const SessionDescription* offer, const CallOptions& options) { // The answer contains the intersection of the codecs in the offer with the // codecs we support, ordered by our local preference. As indicated by // XEP-0167, we retain the same payload ids from the offer in the answer. @@ -171,6 +277,19 @@ SessionDescription* MediaSessionClient::CreateAnswer( } 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); } @@ -193,15 +312,29 @@ SessionDescription* MediaSessionClient::CreateAnswer( } } + video_accept->set_bandwidth(options.video_bandwidth); 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); } return accept; } -Call *MediaSessionClient::CreateCall(bool video, bool mux) { - Call *call = new Call(this, video, mux); +Call *MediaSessionClient::CreateCall() { + Call *call = new Call(this); calls_[call->id()] = call; SignalCallCreate(call); return call; @@ -228,15 +361,14 @@ void MediaSessionClient::OnSessionState(BaseSession* base_session, // If our accept would have no codecs, then we must reject this call. const SessionDescription* offer = session->remote_description(); - const SessionDescription* accept = CreateAnswer(offer); + const SessionDescription* accept = CreateAnswer(offer, CallOptions()); const ContentInfo* audio_content = GetFirstAudioContent(accept); - const ContentInfo* video_content = GetFirstVideoContent(accept); const AudioContentDescription* audio_accept = (!audio_content) ? NULL : static_cast<const AudioContentDescription*>(audio_content->description); // For some reason, we need to create the call even when we // reject. - Call *call = CreateCall(video_content != NULL); + Call *call = CreateCall(); session_map_[session->id()] = call; call->IncomingSession(session, offer); @@ -344,6 +476,70 @@ void ParseGingleSsrc(const buzz::XmlElement* parent_elem, } } +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; +} + +void ParseBandwidth(const buzz::XmlElement* parent_elem, + MediaContentDescription* media) { + const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH); + int bandwidth_kbps; + if (bw_elem && FromString(bw_elem->BodyText(), &bandwidth_kbps)) { + if (bandwidth_kbps >= 0) { + media->set_bandwidth(bandwidth_kbps * 1000); + } + } +} + bool ParseGingleAudioContent(const buzz::XmlElement* content_elem, const ContentDescription** content, ParseError* error) { @@ -368,11 +564,15 @@ bool ParseGingleAudioContent(const buzz::XmlElement* content_elem, 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) { @@ -389,6 +589,12 @@ bool ParseGingleVideoContent(const buzz::XmlElement* content_elem, } ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video); + ParseBandwidth(content_elem, video); + + if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE, + video, error)) { + return false; + } *content = video; return true; @@ -398,8 +604,10 @@ void ParsePayloadTypeParameters(const buzz::XmlElement* element, 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 +620,40 @@ int FindWithDefault(const std::map<std::string, std::string>& map, 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 +670,7 @@ bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) { 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 +697,9 @@ bool ParseJingleAudioContent(const buzz::XmlElement* content_elem, } } + if (!ParseJingleEncryption(content_elem, audio, error)) { + return false; + } // TODO: Figure out how to integrate SSRC into Jingle. *content = audio; return true; @@ -481,6 +720,11 @@ bool ParseJingleVideoContent(const buzz::XmlElement* content_elem, } } + ParseBandwidth(content_elem, video); + + if (!ParseJingleEncryption(content_elem, video, error)) { + return false; + } // TODO: Figure out how to integrate SSRC into Jingle. *content = video; return true; @@ -547,8 +791,58 @@ buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) { return elem; } +buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) { + int kbps = bps / 1000; + buzz::XmlElement* elem = new buzz::XmlElement(name); + elem->AddAttr(buzz::QN_TYPE, "AS"); + SetXmlBody(elem, kbps); + 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 +855,20 @@ buzz::XmlElement* CreateGingleAudioContentElem( 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); @@ -578,6 +880,17 @@ buzz::XmlElement* CreateGingleVideoContentElem( elem->AddElement(CreateGingleSsrcElem( QN_GINGLE_VIDEO_SRCID, video->ssrc())); } + if (video->bandwidth() != kAutoBandwidth) { + elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH, + video->bandwidth())); + } + + const CryptoParamsVec& cryptos = video->cryptos(); + if (!cryptos.empty()) { + elem->AddElement(CreateGingleEncryptionElem(cryptos, + QN_GINGLE_VIDEO_CRYPTO_USAGE, + crypto_required)); + } return elem; } @@ -627,7 +940,7 @@ buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) { } 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 +951,17 @@ buzz::XmlElement* CreateJingleAudioContentElem( 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 +972,16 @@ buzz::XmlElement* CreateJingleVideoContentElem( elem->AddElement(CreateJingleVideoCodecElem(*codec)); } + const CryptoParamsVec& cryptos = video->cryptos(); + if (!cryptos.empty()) { + elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required)); + } + + if (video->bandwidth() != kAutoBandwidth) { + elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH, + video->bandwidth())); + } + // TODO: Figure out how to integrate SSRC into Jingle. return elem; } @@ -664,22 +992,23 @@ bool MediaSessionClient::WriteContent(SignalingProtocol protocol, 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/third_party/libjingle/source/talk/session/phone/mediasessionclient.h b/third_party/libjingle/source/talk/session/phone/mediasessionclient.h index 533341b..a74fe3e 100644 --- a/third_party/libjingle/source/talk/session/phone/mediasessionclient.h +++ b/third_party/libjingle/source/talk/session/phone/mediasessionclient.h @@ -51,8 +51,38 @@ class SessionDescription; 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}; + +const int kAutoBandwidth = -1; + +struct CallOptions { + CallOptions() : + is_video(false), + is_muc(false), + video_bandwidth(kAutoBandwidth) { + } + + bool is_video; + bool is_muc; + // bps. -1 == auto. + int video_bandwidth; +}; + 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. @@ -66,7 +96,7 @@ class MediaSessionClient: public SessionClient, public sigslot::has_slots<> { int GetCapabilities() { return channel_manager_->GetCapabilities(); } - Call *CreateCall(bool video = false, bool mux = false); + Call *CreateCall(); void DestroyCall(Call *call); Call *GetFocus(); @@ -100,8 +130,12 @@ class MediaSessionClient: public SessionClient, public sigslot::has_slots<> { sigslot::signal1<Call *> SignalCallDestroy; sigslot::repeater0<> SignalDevicesChange; - SessionDescription* CreateOffer(bool video = false, bool set_ssrc = false); - SessionDescription* CreateAnswer(const SessionDescription* offer); + SessionDescription* CreateOffer(const CallOptions& options); + SessionDescription* CreateAnswer(const SessionDescription* offer, + const CallOptions& options); + + SecureMediaPolicy secure() const { return secure_; } + void set_secure(SecureMediaPolicy s) { secure_ = s; } private: void Construct(); @@ -124,7 +158,7 @@ class MediaSessionClient: public SessionClient, public sigslot::has_slots<> { ChannelManager *channel_manager_; std::map<uint32, Call *> calls_; std::map<std::string, Call *> session_map_; - + SecureMediaPolicy secure_; friend class Call; }; @@ -135,8 +169,14 @@ enum MediaType { class MediaContentDescription : public ContentDescription { public: - MediaContentDescription() : ssrc_(0), ssrc_set_(false), rtcp_mux_(false), - rtp_headers_disabled_(false) {} + MediaContentDescription() + : ssrc_(0), + ssrc_set_(false), + rtcp_mux_(false), + rtp_headers_disabled_(false), + crypto_required_(false), + bandwidth_(kAutoBandwidth) { + } virtual MediaType type() const = 0; @@ -161,12 +201,22 @@ class MediaContentDescription : public ContentDescription { 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() const { return bandwidth_; } + void set_bandwidth(int bandwidth) { bandwidth_ = bandwidth; } + + protected: uint32 ssrc_; bool ssrc_set_; bool rtcp_mux_; bool rtp_headers_disabled_; std::vector<CryptoParams> cryptos_; + bool crypto_required_; + int bandwidth_; }; template <class C> @@ -190,16 +240,22 @@ class MediaContentDescriptionImpl : public MediaContentDescription { 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/third_party/libjingle/source/talk/session/phone/mediasink.h b/third_party/libjingle/source/talk/session/phone/mediasink.h new file mode 100644 index 0000000..078b534 --- /dev/null +++ b/third_party/libjingle/source/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/third_party/libjingle/source/talk/session/phone/rtpdump.cc b/third_party/libjingle/source/talk/session/phone/rtpdump.cc index 1006015..bd67cad 100644 --- a/third_party/libjingle/source/talk/session/phone/rtpdump.cc +++ b/third_party/libjingle/source/talk/session/phone/rtpdump.cc @@ -35,32 +35,24 @@ namespace cricket { -static const char kRtpDumpFileFirstLine[] = "#!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) { - } - - void WriteToByteBuffer(talk_base::ByteBuffer* buf) { - buf->WriteUInt32(start_sec); - buf->WriteUInt32(start_usec); - buf->WriteUInt32(source); - buf->WriteUInt16(port); - buf->WriteUInt16(padding); - } +const std::string RtpDumpFileHeader::kFirstLine = + "#!rtpplay1.0 0.0.0.0/0\n"; + +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) { +} - 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::WritePacket( 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/third_party/libjingle/source/talk/session/phone/rtpdump.h b/third_party/libjingle/source/talk/session/phone/rtpdump.h index 40c4e03..66275e6 100644 --- a/third_party/libjingle/source/talk/session/phone/rtpdump.h +++ b/third_party/libjingle/source/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 @@ namespace cricket { // 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 @@ class RtpDumpWriter { } 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/third_party/libjingle/source/talk/session/phone/srtpfilter.cc b/third_party/libjingle/source/talk/session/phone/srtpfilter.cc index 63daed6..f8d2dd4 100644 --- a/third_party/libjingle/source/talk/session/phone/srtpfilter.cc +++ b/third_party/libjingle/source/talk/session/phone/srtpfilter.cc @@ -62,6 +62,7 @@ namespace cricket { 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/third_party/libjingle/source/talk/session/phone/srtpfilter.h b/third_party/libjingle/source/talk/session/phone/srtpfilter.h index 71b74e0..3401004 100644 --- a/third_party/libjingle/source/talk/session/phone/srtpfilter.h +++ b/third_party/libjingle/source/talk/session/phone/srtpfilter.h @@ -51,6 +51,8 @@ extern const std::string& CS_DEFAULT; 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/third_party/libjingle/source/talk/session/phone/videocommon.h b/third_party/libjingle/source/talk/session/phone/videocommon.h index 1092b5c..230b1ff 100644 --- a/third_party/libjingle/source/talk/session/phone/videocommon.h +++ b/third_party/libjingle/source/talk/session/phone/videocommon.h @@ -58,24 +58,17 @@ inline std::string GetFourccName(uint32 fourcc) { // 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 @@ enum { 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/third_party/libjingle/source/talk/site_scons/site_tools/talk_noops.pyc b/third_party/libjingle/source/talk/site_scons/site_tools/talk_noops.pyc Binary files differdeleted file mode 100644 index d25fa59..0000000 --- a/third_party/libjingle/source/talk/site_scons/site_tools/talk_noops.pyc +++ /dev/null diff --git a/third_party/libjingle/source/talk/site_scons/talk.py b/third_party/libjingle/source/talk/site_scons/talk.py index e5ede4f..b9e93ef 100644 --- a/third_party/libjingle/source/talk/site_scons/talk.py +++ b/third_party/libjingle/source/talk/site_scons/talk.py @@ -6,27 +6,42 @@ # 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 _GenericLibrary(env, static, **kwargs): + """Extends ComponentLibrary to support multiplatform builds + of dynamic or static libraries. + + Args: + env: The environment object. + kwargs: The keyword arguments. + + Returns: + See swtoolkit ComponentLibrary + """ + params = CombineDicts(kwargs, {'COMPONENT_STATIC': static}) + return ExtendComponent(env, 'ComponentLibrary', **params) + def Library(env, **kwargs): - """Extends ComponentLibrary to support multiplatform builds. + """Extends ComponentLibrary to support multiplatform builds of static + libraries. 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) + Returns: + See swtoolkit ComponentLibrary + """ + return _GenericLibrary(env, True, **kwargs) def DynamicLibrary(env, **kwargs): """Extends ComponentLibrary to support multiplatform builds - of dynmic libraries. This use COMPONENT_STATIC = false. + of dynmic libraries. Args: env: The environment object. @@ -35,8 +50,7 @@ def DynamicLibrary(env, **kwargs): Returns: See swtoolkit ComponentLibrary """ - params = CombineDicts(kwargs, {'COMPONENT_STATIC': False}) - return ExtendComponent(env, 'ComponentLibrary', **params) + return _GenericLibrary(env, False, **kwargs) def Object(env, **kwargs): @@ -73,9 +87,9 @@ def Unittest(env, **kwargs): '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 @@ def AddMediaLibs(env, **kwargs): 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 @@ def AddMediaLibs(env, **kwargs): elif env.Bit('linux'): gips_lib = 'VoiceEngine_Linux_external_gcc' + AddToDict(kwargs, 'libs', [ gips_lib, 'LmiAudioCommon', @@ -367,6 +383,26 @@ def MergeAndFilterByPlatform(env, params): 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. +# ChromeOS (linux) ebuild don't support 64 bit and requires 32 bit build only +# for now. +# TODO: Detect ChromeOS chroot board for ChromeOS x64 build. +def Allow64BitCompile(env): + return (env.Bit('linux') and env.Bit('platform_arch_64bit') and + not env.Bit('linux_chromeos')) + +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 +424,27 @@ def ExtendComponent(env, component, **kwargs): """ 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 +452,7 @@ def ExtendComponent(env, component, **kwargs): 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 +483,7 @@ def ExtendComponent(env, component, **kwargs): 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 +499,7 @@ def ExtendComponent(env, component, **kwargs): 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 +512,8 @@ def ExtendComponent(env, component, **kwargs): # 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 +537,7 @@ def ExtendComponent(env, component, **kwargs): target = '$STAGING_DIR/' + target, ) env.Alias('signed_binaries', signed_node) - return signed + return signed_node return node diff --git a/third_party/libjingle/source/talk/site_scons/talk.pyc b/third_party/libjingle/source/talk/site_scons/talk.pyc Binary files differdeleted file mode 100644 index 308d2e8..0000000 --- a/third_party/libjingle/source/talk/site_scons/talk.pyc +++ /dev/null |