Update to libjingle 0.6.11.
git-svn-id: http://libjingle.googlecode.com/svn/trunk@110 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/CHANGELOG b/CHANGELOG
index 955082a..2e92e53 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,14 +1,19 @@
Libjingle
+0.6.11 - Jan 24, 2012
+ - Improved ipv6 support.
+ - Initial DTLS support.
+ - Initial BUNDLE support.
+
0.6.10 - Jan 11, 2012
- - Support fullscreen screencasting of secondary displays.
- - Add IPv6 support for libjingle's STUN components.
- - Enable SRTP in PeerConnection v1.
- - Bug fixes.
+ - Support fullscreen screencasting of secondary displays.
+ - Add IPv6 support for libjingle's STUN components.
+ - Enable SRTP in PeerConnection v1.
+ - Bug fixes.
0.6.9 - Jan 09, 2012
- - Enable SRTP in PeerConnection.
- - Bug fixes.
+ - Enable SRTP in PeerConnection.
+ - Bug fixes.
0.6.8 - Dec 22, 2011
- Add a lot of unit tests
diff --git a/talk/app/webrtc/peerconnection.h b/talk/app/webrtc/peerconnection.h
index 6a9b18c..24a1cc4 100644
--- a/talk/app/webrtc/peerconnection.h
+++ b/talk/app/webrtc/peerconnection.h
@@ -209,7 +209,7 @@
struct TurnConfiguration {
TurnConfiguration(const std::string& address,
int port,
- const std::string& user_name,
+ const std::string& username,
const std::string& password)
: server(address, port),
username(username),
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index 54f742f..7e8a0a1 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -54,8 +54,8 @@
static const size_t kAllowedCandidates = 4;
// TODO - These are magic string used by cricket::VideoChannel.
// These should be moved to a common place.
-static const std::string kRtpVideoChannelStr = "video_rtp";
-static const std::string kRtcpVideoChannelStr = "video_rtcp";
+static const char kRtpVideoChannelStr[] = "video_rtp";
+static const char kRtcpVideoChannelStr[] = "video_rtcp";
WebRtcSession::WebRtcSession(cricket::ChannelManager* channel_manager,
talk_base::Thread* signaling_thread,
diff --git a/talk/app/webrtcv1/webrtcsession.cc b/talk/app/webrtcv1/webrtcsession.cc
index dc2364d..d30ee43 100644
--- a/talk/app/webrtcv1/webrtcsession.cc
+++ b/talk/app/webrtcv1/webrtcsession.cc
@@ -122,7 +122,7 @@
// Limit the amount of time that setting up a call may take.
StartTransportTimeout(kCallSetupTimeout);
// Set default secure option to SEC_REQUIRED.
- // desc_factory_.set_secure(cricket::SEC_REQUIRED);
+ desc_factory_.set_secure(cricket::SEC_REQUIRED);
return true;
}
diff --git a/talk/base/ipaddress.cc b/talk/base/ipaddress.cc
index 9069269..036584f 100644
--- a/talk/base/ipaddress.cc
+++ b/talk/base/ipaddress.cc
@@ -41,6 +41,7 @@
#include <stdio.h>
#include "talk/base/ipaddress.h"
+#include "talk/base/byteorder.h"
#include "talk/base/nethelpers.h"
#include "talk/base/logging.h"
#include "talk/base/win32.h"
@@ -293,4 +294,95 @@
}
return 0;
}
+
+IPAddress TruncateIP(const IPAddress& ip, int length) {
+ if (length < 0) {
+ return IPAddress();
+ }
+ if (ip.family() == AF_INET) {
+ if (length > 31) {
+ return ip;
+ }
+ if (length == 0) {
+ return IPAddress(INADDR_ANY);
+ }
+ int mask = (0xFFFFFFFF << (32 - length));
+ uint32 host_order_ip = NetworkToHost32(ip.ipv4_address().s_addr);
+ in_addr masked;
+ masked.s_addr = HostToNetwork32(host_order_ip & mask);
+ return IPAddress(masked);
+ } else if (ip.family() == AF_INET6) {
+ if (length > 127) {
+ return ip;
+ }
+ if (length == 0) {
+ return IPAddress(in6addr_any);
+ }
+ in6_addr v6addr = ip.ipv6_address();
+ int position = length / 32;
+ int inner_length = (length - (position * 32));
+ int inner_mask = (0xFFFFFFFF << (32 - inner_length));
+ uint32* v6_as_ints =
+ reinterpret_cast<uint32*>(&v6addr.s6_addr);
+ in6_addr ip_addr = ip.ipv6_address();
+ for (int i = 0; i < 4; ++i) {
+ if (i == position) {
+ uint32 host_order_inner = NetworkToHost32(v6_as_ints[i]);
+ v6_as_ints[i] = HostToNetwork32(host_order_inner & inner_mask);
+ } else if (i > position) {
+ v6_as_ints[i] = 0;
+ }
+ }
+ return IPAddress(v6addr);
+ }
+ return IPAddress();
+}
+
+int CountIPMaskBits(IPAddress mask) {
+ // Doing this the lazy/simple way.
+ // Clever bit tricks welcome but please be careful re: byte order.
+ uint32 word_to_count = 0;
+ int bits = 0;
+ switch (mask.family()) {
+ case AF_INET: {
+ word_to_count = NetworkToHost32(mask.ipv4_address().s_addr);
+ break;
+ }
+ case AF_INET6: {
+ in6_addr v6addr = mask.ipv6_address();
+ const uint32* v6_as_ints =
+ reinterpret_cast<const uint32*>(&v6addr.s6_addr);
+ int i = 0;
+ for (; i < 4; ++i) {
+ if (v6_as_ints[i] != 0xFFFFFFFF) {
+ break;
+ }
+ }
+ if (i < 4) {
+ word_to_count = NetworkToHost32(v6_as_ints[i]);
+ }
+ bits = (i * 32);
+ break;
+ }
+ default: {
+ return 0;
+ }
+ }
+ // Check for byte boundaries before scanning.
+ if (word_to_count == 0) {
+ return bits;
+ } else if (word_to_count == 0xFF000000) {
+ return bits + 8;
+ } else if (word_to_count == 0xFFFF0000) {
+ return bits + 16;
+ } else if (word_to_count == 0xFFFFFF00) {
+ return bits + 24;
+ }
+
+ while (word_to_count & 0x80000000) {
+ word_to_count <<= 1;
+ ++bits;
+ }
+ return bits;
+}
} // Namespace talk base
diff --git a/talk/base/ipaddress.h b/talk/base/ipaddress.h
index a421f16..b1063fd 100644
--- a/talk/base/ipaddress.h
+++ b/talk/base/ipaddress.h
@@ -124,6 +124,13 @@
bool IPIsLoopback(const IPAddress& ip);
bool IPIsPrivate(const IPAddress& ip);
size_t HashIP(const IPAddress& ip);
+
+// Returns 'ip' truncated to be 'length' bits long.
+IPAddress TruncateIP(const IPAddress& ip, int length);
+// Returns the number of contiguously set bits, counting from the MSB in network
+// byte order, in this IPAddress. Bits after the first 0 encountered are not
+// counted.
+int CountIPMaskBits(IPAddress mask);
} // namespace talk_base
#endif // TALK_BASE_IPADDRESS_H_
diff --git a/talk/base/ipaddress_unittest.cc b/talk/base/ipaddress_unittest.cc
index 38a50f9..40cf187 100644
--- a/talk/base/ipaddress_unittest.cc
+++ b/talk/base/ipaddress_unittest.cc
@@ -28,7 +28,6 @@
#include "talk/base/gunit.h"
#include "talk/base/ipaddress.h"
-
namespace talk_base {
static const unsigned int kIPv4AddrSize = 4;
@@ -164,6 +163,21 @@
return addr.family() == AF_UNSPEC;
}
+bool CheckMaskCount(const std::string& mask, int expected_length) {
+ IPAddress addr;
+ return IPFromString(mask, &addr) &&
+ (expected_length == CountIPMaskBits(addr));
+}
+
+bool CheckTruncateIP(const std::string& initial, int truncate_length,
+ const std::string& expected_result) {
+ IPAddress addr, expected;
+ IPFromString(initial, &addr);
+ IPFromString(expected_result, &expected);
+ IPAddress truncated = TruncateIP(addr, truncate_length);
+ return truncated == expected;
+}
+
TEST(IPAddressTest, TestDefaultCtor) {
IPAddress addr;
EXPECT_FALSE(IPIsAny(addr));
@@ -629,4 +643,123 @@
EXPECT_EQ(addr, addr2);
}
+TEST(IPAddressTest, TestCountIPMaskBits) {
+ IPAddress mask;
+ // IPv4 on byte boundaries
+ EXPECT_PRED2(CheckMaskCount, "255.255.255.255", 32);
+ EXPECT_PRED2(CheckMaskCount, "255.255.255.0", 24);
+ EXPECT_PRED2(CheckMaskCount, "255.255.0.0", 16);
+ EXPECT_PRED2(CheckMaskCount, "255.0.0.0", 8);
+ EXPECT_PRED2(CheckMaskCount, "0.0.0.0", 0);
+
+ // IPv4 not on byte boundaries
+ EXPECT_PRED2(CheckMaskCount, "128.0.0.0", 1);
+ EXPECT_PRED2(CheckMaskCount, "224.0.0.0", 3);
+ EXPECT_PRED2(CheckMaskCount, "255.248.0.0", 13);
+ EXPECT_PRED2(CheckMaskCount, "255.255.224.0", 19);
+ EXPECT_PRED2(CheckMaskCount, "255.255.255.252", 30);
+
+ // V6 on byte boundaries
+ EXPECT_PRED2(CheckMaskCount, "::", 0);
+ EXPECT_PRED2(CheckMaskCount, "ff00::", 8);
+ EXPECT_PRED2(CheckMaskCount, "ffff::", 16);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ff00::", 24);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff::", 32);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ff00::", 40);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff::", 48);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ff00::", 56);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff::", 64);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ff00::", 72);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff::", 80);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ff00::", 88);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff::", 96);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ff00:0000", 104);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0000", 112);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00", 120);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128);
+
+ // V6 not on byte boundaries.
+ EXPECT_PRED2(CheckMaskCount, "8000::", 1);
+ EXPECT_PRED2(CheckMaskCount, "ff80::", 9);
+ EXPECT_PRED2(CheckMaskCount, "ffff:fe00::", 23);
+ EXPECT_PRED2(CheckMaskCount, "ffff:fffe::", 31);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:e000::", 35);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffe0::", 43);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:f800::", 53);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:fff8::", 61);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:fc00::", 70);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:fffc::", 78);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:8000::", 81);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ff80::", 89);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:fe00::", 103);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:fffe:0000", 111);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fc00", 118);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc", 126);
+
+ // Non-contiguous ranges. These are kind-of invalid but lets test them
+ // to make sure they don't crash anything or infinite loop or something.
+ EXPECT_PRED2(CheckMaskCount, "217.0.0.0", 2);
+ EXPECT_PRED2(CheckMaskCount, "255.185.0.0", 9);
+ EXPECT_PRED2(CheckMaskCount, "255.255.251.0", 21);
+ EXPECT_PRED2(CheckMaskCount, "255.255.251.255", 21);
+ EXPECT_PRED2(CheckMaskCount, "255.255.254.201", 23);
+ EXPECT_PRED2(CheckMaskCount, "::1", 0);
+ EXPECT_PRED2(CheckMaskCount, "fe80::1", 7);
+ EXPECT_PRED2(CheckMaskCount, "ff80::1", 9);
+ EXPECT_PRED2(CheckMaskCount, "ffff::1", 16);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ff00:1::1", 24);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff::ffff:1", 32);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ff00:1::", 40);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff::ff00", 48);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ff00:1234::", 56);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:0012::ffff", 64);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ff01::", 72);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:7f00::", 80);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ff7a::", 88);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:7f00:0000", 96);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ff70:0000", 104);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0211", 112);
+ EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff7f", 120);
+}
+
+TEST(IPAddressTest, TestTruncateIP) {
+ EXPECT_PRED3(CheckTruncateIP, "255.255.255.255", 24, "255.255.255.0");
+ EXPECT_PRED3(CheckTruncateIP, "255.255.255.255", 16, "255.255.0.0");
+ EXPECT_PRED3(CheckTruncateIP, "255.255.255.255", 8, "255.0.0.0");
+ EXPECT_PRED3(CheckTruncateIP, "202.67.7.255", 24, "202.67.7.0");
+ EXPECT_PRED3(CheckTruncateIP, "202.129.65.205", 16, "202.129.0.0");
+ EXPECT_PRED3(CheckTruncateIP, "55.25.2.77", 8, "55.0.0.0");
+ EXPECT_PRED3(CheckTruncateIP, "74.128.99.254", 1, "0.0.0.0");
+ EXPECT_PRED3(CheckTruncateIP, "106.55.99.254", 3, "96.0.0.0");
+ EXPECT_PRED3(CheckTruncateIP, "172.167.53.222", 13, "172.160.0.0");
+ EXPECT_PRED3(CheckTruncateIP, "255.255.224.0", 18, "255.255.192.0");
+ EXPECT_PRED3(CheckTruncateIP, "255.255.255.252", 28, "255.255.255.240");
+
+ EXPECT_PRED3(CheckTruncateIP, "fe80:1111:2222:3333:4444:5555:6666:7777", 1,
+ "8000::");
+ EXPECT_PRED3(CheckTruncateIP, "fff0:1111:2222:3333:4444:5555:6666:7777", 9,
+ "ff80::");
+ EXPECT_PRED3(CheckTruncateIP, "ffff:ff80:1111:2222:3333:4444:5555:6666", 23,
+ "ffff:fe00::");
+ EXPECT_PRED3(CheckTruncateIP, "2400:f9af:e456:1111:2222:3333:4444:5555", 35,
+ "2400:f9af:e000::");
+ EXPECT_PRED3(CheckTruncateIP, "9999:1111:2233:4444:5555:6666:7777:8888", 53,
+ "9999:1111:2233:4000::");
+ EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 68,
+ "1111:2222:3333:4444:5000::");
+ EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 92,
+ "1111:2222:3333:4444:5555:6660::");
+ EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 105,
+ "1111:2222:3333:4444:5555:6666:7700::");
+ EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 124,
+ "1111:2222:3333:4444:5555:6666:7777:8880");
+
+ // Slightly degenerate cases
+ EXPECT_PRED3(CheckTruncateIP, "202.165.33.127", 32, "202.165.33.127");
+ EXPECT_PRED3(CheckTruncateIP, "235.105.77.12", 0, "0.0.0.0");
+ EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 128,
+ "1111:2222:3333:4444:5555:6666:7777:8888");
+ EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 0,
+ "::");
+}
} // namespace talk_base
diff --git a/talk/base/opensslidentity.cc b/talk/base/opensslidentity.cc
index f18b776..68b113b 100644
--- a/talk/base/opensslidentity.cc
+++ b/talk/base/opensslidentity.cc
@@ -268,11 +268,12 @@
bool OpenSSLCertificate::GetDigestEVP(const std::string &algorithm,
const EVP_MD **mdp) {
-#if defined(HAS_OPENSSL_1_0) && defined(LINUX)
const EVP_MD *md;
if (algorithm == DIGEST_SHA_1) {
md = EVP_sha1();
- } else if (algorithm == DIGEST_SHA_224) {
+ }
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ else if (algorithm == DIGEST_SHA_224) {
md = EVP_sha224();
} else if (algorithm == DIGEST_SHA_256) {
md = EVP_sha256();
@@ -280,7 +281,9 @@
md = EVP_sha384();
} else if (algorithm == DIGEST_SHA_512) {
md = EVP_sha512();
- } else {
+ }
+#endif
+ else {
return false;
}
@@ -288,9 +291,6 @@
ASSERT(EVP_MD_size(md) >= 20);
*mdp = md;
return true;
-#else
- return false;
-#endif
}
OpenSSLCertificate::~OpenSSLCertificate() {
diff --git a/talk/base/opensslstreamadapter.cc b/talk/base/opensslstreamadapter.cc
index 18cef5c..be061ad 100644
--- a/talk/base/opensslstreamadapter.cc
+++ b/talk/base/opensslstreamadapter.cc
@@ -40,6 +40,8 @@
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
+#include <vector>
+
#include "talk/base/common.h"
#include "talk/base/logging.h"
#include "talk/base/stream.h"
@@ -50,6 +52,28 @@
namespace talk_base {
+#if (OPENSSL_VERSION_NUMBER >= 0x10001000L)
+#define HAVE_DTLS_SRTP
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
+#define HAVE_DTLS
+#endif
+
+#ifdef HAVE_DTLS_SRTP
+// SRTP cipher suite table
+struct SrtpCipherMapEntry {
+ const char* external_name;
+ const char* internal_name;
+};
+
+// This isn't elegant, but it's better than an external reference
+static SrtpCipherMapEntry SrtpCipherMap[] = {
+ {"AES_CM_128_HMAC_SHA1_80", "SRTP_AES128_CM_SHA1_80"},
+ {"AES_CM_128_HMAC_SHA1_32", "SRTP_AES128_CM_SHA1_32"},
+ {NULL, NULL}
+};
+#endif
//////////////////////////////////////////////////////////////////////
// StreamBIO
@@ -215,6 +239,95 @@
return true;
}
+// Key Extractor interface
+bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label,
+ const uint8* context,
+ size_t context_len,
+ bool use_context,
+ uint8* result,
+ size_t result_len) {
+#ifdef HAVE_DTLS_SRTP
+ int i;
+
+ i = SSL_export_keying_material(ssl_, result, result_len,
+ label.c_str(), label.length(),
+ const_cast<uint8 *>(context),
+ context_len, use_context);
+
+ if (i != 1)
+ return false;
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool OpenSSLStreamAdapter::SetDtlsSrtpCiphers(
+ const std::vector<std::string>& ciphers) {
+ std::string internal_ciphers;
+
+ if (state_ != SSL_NONE)
+ return false;
+
+#ifdef HAVE_DTLS_SRTP
+ for (std::vector<std::string>::const_iterator cipher = ciphers.begin();
+ cipher != ciphers.end(); ++cipher) {
+ bool found = false;
+ for (SrtpCipherMapEntry *entry = SrtpCipherMap; entry->internal_name;
+ ++entry) {
+ if (*cipher == entry->external_name) {
+ found = true;
+ if (!internal_ciphers.empty())
+ internal_ciphers += ":";
+ internal_ciphers += entry->internal_name;
+ break;
+ }
+ }
+
+ if (!found) {
+ LOG(LS_ERROR) << "Could not find cipher: " << *cipher;
+ return false;
+ }
+ }
+
+ if (internal_ciphers.empty())
+ return false;
+
+ srtp_ciphers_ = internal_ciphers;
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool OpenSSLStreamAdapter::GetDtlsSrtpCipher(std::string* cipher) {
+#ifdef HAVE_DTLS_SRTP
+ ASSERT(state_ == SSL_CONNECTED);
+ if (state_ != SSL_CONNECTED)
+ return false;
+
+ SRTP_PROTECTION_PROFILE *srtp_profile =
+ SSL_get_selected_srtp_profile(ssl_);
+
+ if (!srtp_profile)
+ return NULL;
+
+ for (SrtpCipherMapEntry *entry = SrtpCipherMap;
+ entry->internal_name; ++entry) {
+ if (!strcmp(entry->internal_name, srtp_profile->name)) {
+ *cipher = entry->external_name;
+ return true;
+ }
+ }
+
+ ASSERT(false); // This should never happen
+
+ return false;
+#else
+ return false;
+#endif
+}
int OpenSSLStreamAdapter::StartSSLWithServer(const char* server_name) {
ASSERT(server_name != NULL && server_name[0] != '\0');
@@ -550,9 +663,9 @@
StreamAdapterInterface::OnEvent(stream(), SE_OPEN|SE_READ|SE_WRITE, 0);
break;
- case SSL_ERROR_WANT_READ:{
+ case SSL_ERROR_WANT_READ: {
LOG(LS_INFO) << " -- error want read";
-#if defined(HAS_OPENSSL_1_0) && defined(LINUX)
+#ifdef HAVE_DTLS
struct timeval timeout;
if (DTLSv1_get_timeout(ssl_, &timeout)) {
int delay = timeout.tv_sec * 1000 + timeout.tv_usec/1000;
@@ -614,7 +727,7 @@
// Process our own messages and then pass others to the superclass
if (MSG_TIMEOUT == msg->message_id) {
LOG(LS_INFO) << "DTLS timeout expired";
-#if defined(HAS_OPENSSL_1_0) && defined(LINUX)
+#ifdef HAVE_DTLS
DTLSv1_handle_timeout(ssl_);
#endif
ContinueSSL();
@@ -626,15 +739,21 @@
SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
SSL_CTX *ctx = NULL;
-#if defined(HAS_OPENSSL_1_0) && defined(LINUX)
if (role_ == SSL_CLIENT) {
+#ifdef HAVE_DTLS
ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ?
DTLSv1_client_method() : TLSv1_client_method());
+#else
+ ctx = SSL_CTX_new(TLSv1_client_method());
+#endif
} else {
+#ifdef HAVE_DTLS
ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ?
DTLSv1_server_method() : TLSv1_server_method());
- }
+#else
+ ctx = SSL_CTX_new(TLSv1_server_method());
#endif
+ }
if (ctx == NULL)
return NULL;
@@ -664,6 +783,15 @@
SSL_CTX_set_verify_depth(ctx, 4);
SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+#ifdef HAVE_DTLS_SRTP
+ if (!srtp_ciphers_.empty()) {
+ if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_ciphers_.c_str())) {
+ SSL_CTX_free(ctx);
+ return NULL;
+ }
+ }
+#endif
+
return ctx;
}
@@ -781,8 +909,29 @@
return ok;
}
+bool OpenSSLStreamAdapter::HaveDtls() {
+#ifdef HAVE_DTLS
+ return true;
+#else
+ return false;
+#endif
+}
+bool OpenSSLStreamAdapter::HaveDtlsSrtp() {
+#ifdef HAVE_DTLS_SRTP
+ return true;
+#else
+ return false;
+#endif
+}
+bool OpenSSLStreamAdapter::HaveExporter() {
+#ifdef HAVE_DTLS_SRTP
+ return true;
+#else
+ return false;
+#endif
+}
} // namespace talk_base
diff --git a/talk/base/opensslstreamadapter.h b/talk/base/opensslstreamadapter.h
index 1f94e2b..8e92a10 100644
--- a/talk/base/opensslstreamadapter.h
+++ b/talk/base/opensslstreamadapter.h
@@ -29,6 +29,8 @@
#define TALK_BASE_OPENSSLSTREAMADAPTER_H__
#include <string>
+#include <vector>
+
#include "talk/base/buffer.h"
#include "talk/base/sslstreamadapter.h"
#include "talk/base/opensslidentity.h"
@@ -95,6 +97,24 @@
virtual void Close();
virtual StreamState GetState() const;
+ // Key Extractor interface
+ virtual bool ExportKeyingMaterial(const std::string& label,
+ const uint8* context,
+ size_t context_len,
+ bool use_context,
+ uint8* result,
+ size_t result_len);
+
+
+ // DTLS-SRTP interface
+ virtual bool SetDtlsSrtpCiphers(const std::vector<std::string>& ciphers);
+ virtual bool GetDtlsSrtpCipher(std::string* cipher);
+
+ // Capabilities interfaces
+ static bool HaveDtls();
+ static bool HaveDtlsSrtp();
+ static bool HaveExporter();
+
protected:
virtual void OnEvent(StreamInterface* stream, int events, int err);
@@ -181,6 +201,9 @@
// OpenSSLAdapter::custom_verify_callback_ result
bool custom_verification_succeeded_;
+ // The DtlsSrtp ciphers
+ std::string srtp_ciphers_;
+
// Do DTLS or not
SSLMode ssl_mode_;
};
diff --git a/talk/base/socketaddress.cc b/talk/base/socketaddress.cc
index 9bf713f..a4c7275 100644
--- a/talk/base/socketaddress.cc
+++ b/talk/base/socketaddress.cc
@@ -324,13 +324,14 @@
}
static size_t ToSockAddrStorageHelper(sockaddr_storage* addr,
- IPAddress ip, int port) {
+ IPAddress ip, int port, int scope_id) {
memset(addr, 0, sizeof(sockaddr_storage));
addr->ss_family = ip.family();
if (addr->ss_family == AF_INET6) {
sockaddr_in6* saddr = reinterpret_cast<sockaddr_in6*>(addr);
saddr->sin6_addr = ip.ipv6_address();
saddr->sin6_port = HostToNetwork16(port);
+ saddr->sin6_scope_id = scope_id;
return sizeof(sockaddr_in6);
} else if (addr->ss_family == AF_INET) {
sockaddr_in* saddr = reinterpret_cast<sockaddr_in*>(addr);
@@ -342,11 +343,11 @@
}
size_t SocketAddress::ToDualStackSockAddrStorage(sockaddr_storage *addr) const {
- return ToSockAddrStorageHelper(addr, ip_.AsIPv6Address(), port_);
+ return ToSockAddrStorageHelper(addr, ip_.AsIPv6Address(), port_, scope_id_);
}
size_t SocketAddress::ToSockAddrStorage(sockaddr_storage* addr) const {
- return ToSockAddrStorageHelper(addr, ip_, port_);
+ return ToSockAddrStorageHelper(addr, ip_, port_, scope_id_);
}
std::string SocketAddress::IPToString(uint32 ip_as_host_order_integer) {
@@ -440,6 +441,7 @@
const sockaddr_in6* saddr = reinterpret_cast<const sockaddr_in6*>(&addr);
*out = SocketAddress(IPAddress(saddr->sin6_addr),
NetworkToHost16(saddr->sin6_port));
+ out->SetScopeID(saddr->sin6_scope_id);
return true;
}
return false;
diff --git a/talk/base/socketaddress.h b/talk/base/socketaddress.h
index 575e042..9bd1fad 100644
--- a/talk/base/socketaddress.h
+++ b/talk/base/socketaddress.h
@@ -108,6 +108,14 @@
// Returns the port part of this address.
uint16 port() const;
+ // Returns the scope ID associated with this address. Scope IDs are a
+ // necessary addition to IPv6 link-local addresses, with different network
+ // interfaces having different scope-ids for their link-local addresses.
+ // IPv4 address do not have scope_ids and sockaddr_in structures do not have
+ // a field for them.
+ int scope_id() const {return scope_id_; }
+ void SetScopeID(int id) { scope_id_ = id; }
+
// Returns the IP address (or hostname) in printable form.
std::string IPAsString() const;
@@ -211,6 +219,7 @@
std::string hostname_;
IPAddress ip_;
uint16 port_;
+ int scope_id_;
bool literal_; // Indicates that 'hostname_' contains a literal IP string.
};
diff --git a/talk/base/socketaddress_unittest.cc b/talk/base/socketaddress_unittest.cc
index 6e2ef7f..5ef05b5 100644
--- a/talk/base/socketaddress_unittest.cc
+++ b/talk/base/socketaddress_unittest.cc
@@ -224,6 +224,29 @@
EXPECT_EQ("", addr.hostname());
EXPECT_EQ("[::ffff:1.2.3.4]:5678", addr.ToString());
+ addr.Clear();
+ memset(&addr_storage, 0, sizeof(sockaddr_storage));
+ from = SocketAddress(kTestV6AddrString, 5678);
+ from.SetScopeID(6);
+ from.ToSockAddrStorage(&addr_storage);
+ EXPECT_TRUE(SocketAddressFromSockAddrStorage(addr_storage, &addr));
+ EXPECT_FALSE(addr.IsUnresolvedIP());
+ EXPECT_EQ(IPAddress(kTestV6Addr), addr.ipaddr());
+ EXPECT_EQ(5678, addr.port());
+ EXPECT_EQ("", addr.hostname());
+ EXPECT_EQ(kTestV6AddrFullString, addr.ToString());
+ EXPECT_EQ(6, addr.scope_id());
+
+ addr.Clear();
+ from.ToDualStackSockAddrStorage(&addr_storage);
+ EXPECT_TRUE(SocketAddressFromSockAddrStorage(addr_storage, &addr));
+ EXPECT_FALSE(addr.IsUnresolvedIP());
+ EXPECT_EQ(IPAddress(kTestV6Addr), addr.ipaddr());
+ EXPECT_EQ(5678, addr.port());
+ EXPECT_EQ("", addr.hostname());
+ EXPECT_EQ(kTestV6AddrFullString, addr.ToString());
+ EXPECT_EQ(6, addr.scope_id());
+
addr = from;
addr_storage.ss_family = AF_UNSPEC;
EXPECT_FALSE(SocketAddressFromSockAddrStorage(addr_storage, &addr));
diff --git a/talk/base/sslstreamadapter.cc b/talk/base/sslstreamadapter.cc
index 17121fd..aa0f468 100644
--- a/talk/base/sslstreamadapter.cc
+++ b/talk/base/sslstreamadapter.cc
@@ -65,6 +65,23 @@
#endif // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL
}
+// Note: this matches the logic above with SCHANNEL dominating
+#if SSL_USE_SCHANNEL || !SSL_USE_OPENSSL
+bool SSLStreamAdapter::HaveDtls() { return false; }
+bool SSLStreamAdapter::HaveDtlsSrtp() { return false; }
+bool SSLStreamAdapter::HaveExporter() { return false; }
+#else
+bool SSLStreamAdapter::HaveDtls() {
+ return OpenSSLStreamAdapter::HaveDtls();
+}
+bool SSLStreamAdapter::HaveDtlsSrtp() {
+ return OpenSSLStreamAdapter::HaveDtlsSrtp();
+}
+bool SSLStreamAdapter::HaveExporter() {
+ return OpenSSLStreamAdapter::HaveExporter();
+}
+#endif // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL
+
///////////////////////////////////////////////////////////////////////////////
} // namespace talk_base
diff --git a/talk/base/sslstreamadapter.h b/talk/base/sslstreamadapter.h
index 28870b3..2afe1da 100644
--- a/talk/base/sslstreamadapter.h
+++ b/talk/base/sslstreamadapter.h
@@ -29,6 +29,7 @@
#define TALK_BASE_SSLSTREAMADAPTER_H__
#include <string>
+#include <vector>
#include "talk/base/stream.h"
#include "talk/base/sslidentity.h"
@@ -137,6 +138,42 @@
const unsigned char* digest_val,
size_t digest_len) = 0;
+ // Key Exporter interface from RFC 5705
+ // Arguments are:
+ // label -- the exporter label.
+ // part of the RFC defining each exporter
+ // usage (IN)
+ // context/context_len -- a context to bind to for this connection;
+ // optional, can be NULL, 0 (IN)
+ // use_context -- whether to use the context value
+ // (needed to distinguish no context from
+ // zero-length ones).
+ // result -- where to put the computed value
+ // result_len -- the length of the computed value
+ virtual bool ExportKeyingMaterial(const std::string& label,
+ const uint8* context,
+ size_t context_len,
+ bool use_context,
+ uint8* result,
+ size_t result_len) {
+ return false; // Default is unsupported
+ }
+
+
+ // DTLS-SRTP interface
+ virtual bool SetDtlsSrtpCiphers(const std::vector<std::string>& ciphers) {
+ return false;
+ }
+
+ virtual bool GetDtlsSrtpCipher(std::string* cipher) {
+ return false;
+ }
+
+ // Capabilities testing
+ static bool HaveDtls();
+ static bool HaveDtlsSrtp();
+ static bool HaveExporter();
+
// If true, the server certificate need not match the configured
// server_name, and in fact missing certificate authority and other
// verification errors are ignored.
diff --git a/talk/base/sslstreamadapter_unittest.cc b/talk/base/sslstreamadapter_unittest.cc
index 0206f87..befcdbc 100644
--- a/talk/base/sslstreamadapter_unittest.cc
+++ b/talk/base/sslstreamadapter_unittest.cc
@@ -38,6 +38,17 @@
#include "talk/base/stream.h"
static const int kBlockSize = 4096;
+static const char kAES_CM_HMAC_SHA1_80[] = "AES_CM_128_HMAC_SHA1_80";
+static const char kAES_CM_HMAC_SHA1_32[] = "AES_CM_128_HMAC_SHA1_32";
+static const char kExporterLabel[] = "label";
+static const unsigned char kExporterContext[] = "context";
+static int kExporterContextLen = sizeof(kExporterContext);
+
+#define MAYBE_SKIP_TEST(feature) \
+ if (!(talk_base::SSLStreamAdapter::feature())) { \
+ LOG(LS_INFO) << "Feature disabled... skipping"; \
+ return; \
+ }
class SSLStreamAdapterTestBase;
@@ -300,6 +311,40 @@
handshake_wait_ = wait;
}
+ void SetDtlsSrtpCiphers(const std::vector<std::string> &ciphers,
+ bool client) {
+ if (client)
+ client_ssl_->SetDtlsSrtpCiphers(ciphers);
+ else
+ server_ssl_->SetDtlsSrtpCiphers(ciphers);
+ }
+
+ bool GetDtlsSrtpCipher(bool client, std::string *retval) {
+ if (client)
+ return client_ssl_->GetDtlsSrtpCipher(retval);
+ else
+ return server_ssl_->GetDtlsSrtpCipher(retval);
+ }
+
+ bool ExportKeyingMaterial(const char *label,
+ const unsigned char *context,
+ size_t context_len,
+ bool use_context,
+ bool client,
+ unsigned char *result,
+ size_t result_len) {
+ if (client)
+ return client_ssl_->ExportKeyingMaterial(label,
+ context, context_len,
+ use_context,
+ result, result_len);
+ else
+ return server_ssl_->ExportKeyingMaterial(label,
+ context, context_len,
+ use_context,
+ result, result_len);
+ }
+
// To be implemented by subclasses.
virtual void WriteData() = 0;
virtual void ReadData(talk_base::StreamInterface *stream) = 0;
@@ -438,7 +483,7 @@
unsigned char *packet = new unsigned char[1600];
do {
- memset(packet, sent_ & 0xff, sizeof(packet));
+ memset(packet, sent_ & 0xff, packet_size_);
*(reinterpret_cast<uint32_t *>(packet)) = sent_;
size_t sent;
@@ -459,13 +504,13 @@
}
virtual void ReadData(talk_base::StreamInterface *stream) {
- unsigned char *buffer = new unsigned char[1600];
+ unsigned char *buffer = new unsigned char[2000];
size_t bread;
int err2;
talk_base::StreamResult r;
for (;;) {
- r = stream->Read(buffer, sizeof(buffer),
+ r = stream->Read(buffer, 2000,
&bread, &err2);
if (r == talk_base::SR_ERROR) {
@@ -569,21 +614,24 @@
// Basic tests: DTLS
// Test that we can make a handshake work
-TEST_F(SSLStreamAdapterTestDTLS, DISABLED_TestDTLSConnect) {
+TEST_F(SSLStreamAdapterTestDTLS, TestDTLSConnect) {
+ MAYBE_SKIP_TEST(HaveDtls);
TestHandshake();
};
// Test that we can make a handshake work if the first packet in
// each direction is lost. This gives us predictable loss
// rather than having to tune random
-TEST_F(SSLStreamAdapterTestDTLS, DISABLED_TestDTLSConnectWithLostFirstPacket) {
+TEST_F(SSLStreamAdapterTestDTLS, TestDTLSConnectWithLostFirstPacket) {
+ MAYBE_SKIP_TEST(HaveDtls);
SetLoseFirstPacket(true);
TestHandshake();
};
// Test a handshake with loss and delay
TEST_F(SSLStreamAdapterTestDTLS,
- DISABLED_TestDTLSConnectWithLostFirstPacketDelay2s) {
+ TestDTLSConnectWithLostFirstPacketDelay2s) {
+ MAYBE_SKIP_TEST(HaveDtls);
SetLoseFirstPacket(true);
SetDelay(2000);
SetHandshakeWait(20000);
@@ -592,19 +640,117 @@
// Test a handshake with small MTU
TEST_F(SSLStreamAdapterTestDTLS, TestDTLSConnectWithSmallMtu) {
+ MAYBE_SKIP_TEST(HaveDtls);
SetMtu(700);
TestHandshake();
};
// Test transfer -- trivial
-TEST_F(SSLStreamAdapterTestDTLS, DISABLED_TestDTLSTransfer) {
+TEST_F(SSLStreamAdapterTestDTLS, TestDTLSTransfer) {
+ MAYBE_SKIP_TEST(HaveDtls);
TestHandshake();
TestTransfer(100);
};
-TEST_F(SSLStreamAdapterTestDTLS, DISABLED_TestDTLSTransferWithLoss) {
+TEST_F(SSLStreamAdapterTestDTLS, TestDTLSTransferWithLoss) {
+ MAYBE_SKIP_TEST(HaveDtls);
TestHandshake();
SetLoss(10);
TestTransfer(100);
};
+// Test DTLS-SRTP with all high ciphers
+TEST_F(SSLStreamAdapterTestDTLS, TestDTLSSrtpHigh) {
+ MAYBE_SKIP_TEST(HaveDtlsSrtp);
+ std::vector<std::string> high;
+ high.push_back(kAES_CM_HMAC_SHA1_80);
+ SetDtlsSrtpCiphers(high, true);
+ SetDtlsSrtpCiphers(high, false);
+ TestHandshake();
+
+ std::string client_cipher;
+ ASSERT_TRUE(GetDtlsSrtpCipher(true, &client_cipher));
+ std::string server_cipher;
+ ASSERT_TRUE(GetDtlsSrtpCipher(false, &server_cipher));
+
+ ASSERT_EQ(client_cipher, server_cipher);
+ ASSERT_EQ(client_cipher, kAES_CM_HMAC_SHA1_80);
+};
+
+// Test DTLS-SRTP with all low ciphers
+TEST_F(SSLStreamAdapterTestDTLS, TestDTLSSrtpLow) {
+ MAYBE_SKIP_TEST(HaveDtlsSrtp);
+ std::vector<std::string> low;
+ low.push_back(kAES_CM_HMAC_SHA1_32);
+ SetDtlsSrtpCiphers(low, true);
+ SetDtlsSrtpCiphers(low, false);
+ TestHandshake();
+
+ std::string client_cipher;
+ ASSERT_TRUE(GetDtlsSrtpCipher(true, &client_cipher));
+ std::string server_cipher;
+ ASSERT_TRUE(GetDtlsSrtpCipher(false, &server_cipher));
+
+ ASSERT_EQ(client_cipher, server_cipher);
+ ASSERT_EQ(client_cipher, kAES_CM_HMAC_SHA1_32);
+};
+
+
+// Test DTLS-SRTP with a mismatch -- should not converge
+TEST_F(SSLStreamAdapterTestDTLS, TestDTLSSrtpHighLow) {
+ MAYBE_SKIP_TEST(HaveDtlsSrtp);
+ std::vector<std::string> high;
+ high.push_back(kAES_CM_HMAC_SHA1_80);
+ std::vector<std::string> low;
+ low.push_back(kAES_CM_HMAC_SHA1_32);
+ SetDtlsSrtpCiphers(high, true);
+ SetDtlsSrtpCiphers(low, false);
+ TestHandshake();
+
+ std::string client_cipher;
+ ASSERT_FALSE(GetDtlsSrtpCipher(true, &client_cipher));
+ std::string server_cipher;
+ ASSERT_FALSE(GetDtlsSrtpCipher(false, &server_cipher));
+};
+
+// Test DTLS-SRTP with each side being mixed -- should select high
+TEST_F(SSLStreamAdapterTestDTLS, TestDTLSSrtpMixed) {
+ MAYBE_SKIP_TEST(HaveDtlsSrtp);
+ std::vector<std::string> mixed;
+ mixed.push_back(kAES_CM_HMAC_SHA1_80);
+ mixed.push_back(kAES_CM_HMAC_SHA1_32);
+ SetDtlsSrtpCiphers(mixed, true);
+ SetDtlsSrtpCiphers(mixed, false);
+ TestHandshake();
+
+ std::string client_cipher;
+ ASSERT_TRUE(GetDtlsSrtpCipher(true, &client_cipher));
+ std::string server_cipher;
+ ASSERT_TRUE(GetDtlsSrtpCipher(false, &server_cipher));
+
+ ASSERT_EQ(client_cipher, server_cipher);
+ ASSERT_EQ(client_cipher, kAES_CM_HMAC_SHA1_80);
+};
+
+// Test an exporter
+TEST_F(SSLStreamAdapterTestDTLS, TestDTLSExporter) {
+ MAYBE_SKIP_TEST(HaveExporter);
+ TestHandshake();
+ unsigned char client_out[20];
+ unsigned char server_out[20];
+
+ bool result;
+ result = ExportKeyingMaterial(kExporterLabel,
+ kExporterContext, kExporterContextLen,
+ true, true,
+ client_out, sizeof(client_out));
+ ASSERT_TRUE(result);
+
+ result = ExportKeyingMaterial(kExporterLabel,
+ kExporterContext, kExporterContextLen,
+ true, false,
+ server_out, sizeof(server_out));
+ ASSERT_TRUE(result);
+
+ ASSERT_TRUE(!memcmp(client_out, server_out, sizeof(client_out)));
+}
diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc
index 8ea2eaf..9b8fb1a 100644
--- a/talk/examples/call/callclient.cc
+++ b/talk/examples/call/callclient.cc
@@ -442,8 +442,8 @@
void CallClient::OnCallCreate(cricket::Call* call) {
call->SignalSessionState.connect(this, &CallClient::OnSessionState);
- call->SignalMediaSourcesUpdate.connect(
- this, &CallClient::OnMediaSourcesUpdate);
+ call->SignalMediaStreamsUpdate.connect(
+ this, &CallClient::OnMediaStreamsUpdate);
}
void CallClient::OnSessionState(cricket::Call* call,
@@ -489,17 +489,17 @@
void CallClient::OnSpeakerChanged(cricket::Call* call,
cricket::Session* session,
- const cricket::NamedSource& speaker) {
- if (speaker.ssrc == 0) {
+ const cricket::StreamParams& speaker) {
+ if (!speaker.has_ssrcs()) {
console_->PrintLine("Session %s has no current speaker.",
session->id().c_str());
} else if (speaker.nick.empty()) {
console_->PrintLine("Session %s speaker change to unknown (%u).",
- session->id().c_str(), speaker.ssrc);
+ session->id().c_str(), speaker.first_ssrc());
} else {
console_->PrintLine("Session %s speaker changed to %s (%u).",
session->id().c_str(), speaker.nick.c_str(),
- speaker.ssrc);
+ speaker.first_ssrc());
}
}
@@ -1112,20 +1112,22 @@
media_client_->SetOutputVolume(strtol(level.c_str(), NULL, 10));
}
-void CallClient::OnMediaSourcesUpdate(cricket::Call* call,
+void CallClient::OnMediaStreamsUpdate(cricket::Call* call,
cricket::Session* session,
- const cricket::MediaSources& sources) {
- for (cricket::NamedSources::const_iterator it = sources.video().begin();
- it != sources.video().end(); ++it) {
- if (it->removed) {
- RemoveStaticRenderedView(it->ssrc);
- } else {
- if (render_) {
- // TODO: Make dimensions and positions more configurable.
- int offset = (50 * static_views_accumulated_count_) % 300;
- AddStaticRenderedView(session, it->ssrc, 640, 400, 30,
- offset, offset);
- }
+ const cricket::MediaStreams& added,
+ const cricket::MediaStreams& removed) {
+ for (std::vector<cricket::StreamParams>::const_iterator
+ it = removed.video().begin(); it != removed.video().end(); ++it) {
+ RemoveStaticRenderedView(it->first_ssrc());
+ }
+
+ if (render_) {
+ for (std::vector<cricket::StreamParams>::const_iterator
+ it = added.video().begin(); it != added.video().end(); ++it) {
+ // TODO: Make dimensions and positions more configurable.
+ int offset = (50 * static_views_accumulated_count_) % 300;
+ AddStaticRenderedView(session, it->first_ssrc(), 640, 400, 30,
+ offset, offset);
}
}
diff --git a/talk/examples/call/callclient.h b/talk/examples/call/callclient.h
index 83378b1..76184dc 100644
--- a/talk/examples/call/callclient.h
+++ b/talk/examples/call/callclient.h
@@ -73,7 +73,8 @@
class Call;
class SessionManagerTask;
struct CallOptions;
-struct NamedSource;
+struct MediaStreams;
+struct StreamParams;
}
struct RosterItem {
@@ -201,12 +202,13 @@
const std::string& mutee_nick,
const buzz::XmlElement* stanza);
void OnDevicesChange();
- void OnMediaSourcesUpdate(cricket::Call* call,
+ void OnMediaStreamsUpdate(cricket::Call* call,
cricket::Session* session,
- const cricket::MediaSources& sources);
+ const cricket::MediaStreams& added,
+ const cricket::MediaStreams& removed);
void OnSpeakerChanged(cricket::Call* call,
cricket::Session* session,
- const cricket::NamedSource& speaker_source);
+ const cricket::StreamParams& speaker_stream);
void OnRoomLookupResponse(buzz::MucRoomLookupTask* task,
const buzz::MucRoomInfo& room_info);
void OnRoomLookupError(buzz::IqTask* task,
diff --git a/talk/examples/login/autoportallocator.h b/talk/examples/login/autoportallocator.h
index f1993ca..7e0b062 100644
--- a/talk/examples/login/autoportallocator.h
+++ b/talk/examples/login/autoportallocator.h
@@ -38,8 +38,7 @@
// This class sets the relay and stun servers using XmppClient.
// It enables the client to traverse Proxy and NAT.
-class AutoPortAllocator : public cricket::HttpPortAllocator,
- public sigslot::has_slots<> {
+class AutoPortAllocator : public cricket::HttpPortAllocator {
public:
AutoPortAllocator(talk_base::NetworkManager* network_manager,
const std::string& user_agent)
diff --git a/talk/examples/peerconnection/client/main_wnd.cc b/talk/examples/peerconnection/client/main_wnd.cc
index e9f79e4..e4e0539 100644
--- a/talk/examples/peerconnection/client/main_wnd.cc
+++ b/talk/examples/peerconnection/client/main_wnd.cc
@@ -587,10 +587,11 @@
AutoLock<VideoRenderer> lock(this);
ASSERT(image_.get() != NULL);
- frame->ConvertToRgbBuffer(cricket::FOURCC_ARGB, image_.get(),
- bmi_.bmiHeader.biSizeImage,
- bmi_.bmiHeader.biWidth *
- (bmi_.bmiHeader.biBitCount >> 3));
+ frame->ConvertToRgbBuffer(cricket::FOURCC_ARGB,
+ image_.get(),
+ bmi_.bmiHeader.biSizeImage,
+ bmi_.bmiHeader.biWidth *
+ bmi_.bmiHeader.biBitCount / 8);
}
InvalidateRect(wnd_, NULL, TRUE);
diff --git a/talk/libjingle.scons b/talk/libjingle.scons
index 4cfed14..6cdf5b6 100644
--- a/talk/libjingle.scons
+++ b/talk/libjingle.scons
@@ -201,6 +201,9 @@
"p2p/base/p2ptransportchannel.cc",
"p2p/base/parsing.cc",
"p2p/base/port.cc",
+ "p2p/base/portallocator.cc",
+ "p2p/base/portallocatorsessionproxy.cc",
+ "p2p/base/portproxy.cc",
"p2p/base/pseudotcp.cc",
"p2p/base/relayport.cc",
"p2p/base/relayserver.cc",
diff --git a/talk/main.scons b/talk/main.scons
index e23f481..a58e115 100644
--- a/talk/main.scons
+++ b/talk/main.scons
@@ -311,6 +311,9 @@
#'fill_plist',
],
)
+# Use static OpenSSL on mac so that we can use the latest APIs on all
+# supported mac platforms (10.5+).
+mac_env.SetBits('use_static_openssl')
mac_env.Append(
CPPDEFINES = [
'OSX',
diff --git a/talk/p2p/base/constants.cc b/talk/p2p/base/constants.cc
index 4d65f5b..fe89aa7 100644
--- a/talk/p2p/base/constants.cc
+++ b/talk/p2p/base/constants.cc
@@ -99,7 +99,7 @@
const char CN_VIDEO[] = "video";
const char CN_OTHER[] = "main";
// other SDP related strings
-const char GN_TOGETHER[] = "TOGETHER";
+const char GN_BUNDLE[] = "BUNDLE";
const char NS_JINGLE_RTP[] = "urn:xmpp:jingle:apps:rtp:1";
const buzz::StaticQName QN_JINGLE_RTP_CONTENT =
diff --git a/talk/p2p/base/constants.h b/talk/p2p/base/constants.h
index 0c84dd6..3e85914 100644
--- a/talk/p2p/base/constants.h
+++ b/talk/p2p/base/constants.h
@@ -118,7 +118,7 @@
extern const char CN_OTHER[];
// other SDP related strings
// GN stands for group name
-extern const char GN_TOGETHER[];
+extern const char GN_BUNDLE[];
extern const char NS_JINGLE_RTP[];
extern const buzz::StaticQName QN_JINGLE_RTP_CONTENT;
diff --git a/talk/p2p/base/p2ptransportchannel.cc b/talk/p2p/base/p2ptransportchannel.cc
index 17f52a7..87a0222 100644
--- a/talk/p2p/base/p2ptransportchannel.cc
+++ b/talk/p2p/base/p2ptransportchannel.cc
@@ -301,7 +301,7 @@
// Handle stun packets
void P2PTransportChannel::OnUnknownAddress(
Port *port, const talk_base::SocketAddress &address, StunMessage *stun_msg,
- const std::string &remote_username) {
+ const std::string &remote_username, bool port_muxed) {
ASSERT(worker_thread_ == talk_base::Thread::Current());
// Port has received a valid stun packet from an address that no Connection
@@ -316,11 +316,17 @@
break;
}
}
+
if (candidate == NULL) {
+ if (port_muxed) {
+ // When Ports are muxed, SignalUnknownAddress is delivered to all
+ // P2PTransportChannel belong to a session. Return from here will
+ // save us from sending stun binding error message from incorrect channel.
+ return;
+ }
// Don't know about this username, the request is bogus
// This sometimes happens if a binding response comes in before the ACCEPT
// message. It is totally valid; the retry state machine will try again.
-
port->SendBindingErrorResponse(stun_msg, address,
STUN_ERROR_STALE_CREDENTIALS, STUN_ERROR_REASON_STALE_CREDENTIALS);
delete stun_msg;
@@ -451,6 +457,13 @@
return true;
}
+bool P2PTransportChannel::FindConnection(
+ cricket::Connection* connection) const {
+ std::vector<Connection*>::const_iterator citer =
+ std::find(connections_.begin(), connections_.end(), connection);
+ return citer != connections_.end();
+}
+
// Maintain our remote candidate list, adding this new remote one.
void P2PTransportChannel::RememberRemoteCandidate(
const Candidate& remote_candidate, Port* origin_port) {
@@ -900,8 +913,11 @@
const char *data, size_t len) {
ASSERT(worker_thread_ == talk_base::Thread::Current());
- // Let the client know of an incoming packet
+ // Do not deliver, if packet doesn't belong to the correct transport channel.
+ if (!FindConnection(connection))
+ return;
+ // Let the client know of an incoming packet
SignalReadPacket(this, data, len);
}
@@ -933,7 +949,8 @@
void P2PTransportChannel::OnSignalingReady() {
if (waiting_for_signaling_) {
waiting_for_signaling_ = false;
- AddAllocatorSession(allocator_->CreateSession(name(), content_type()));
+ AddAllocatorSession(allocator_->CreateSession(
+ session_id(), name(), content_type()));
thread()->PostDelayed(kAllocatePeriod, this, MSG_ALLOCATE);
}
}
diff --git a/talk/p2p/base/p2ptransportchannel.h b/talk/p2p/base/p2ptransportchannel.h
index 0083d23..6b4d979 100644
--- a/talk/p2p/base/p2ptransportchannel.h
+++ b/talk/p2p/base/p2ptransportchannel.h
@@ -116,11 +116,12 @@
bool readable);
bool CreateConnection(Port* port, const Candidate& remote_candidate,
Port* origin_port, bool readable);
+ bool FindConnection(cricket::Connection* connection) const;
void RememberRemoteCandidate(const Candidate& remote_candidate,
Port* origin_port);
void OnUnknownAddress(Port *port, const talk_base::SocketAddress &addr,
StunMessage *stun_msg,
- const std::string &remote_username);
+ const std::string &remote_username, bool port_muxed);
void OnPortReady(PortAllocatorSession *session, Port* port);
void OnCandidatesReady(PortAllocatorSession *session,
const std::vector<Candidate>& candidates);
diff --git a/talk/p2p/base/parsing.cc b/talk/p2p/base/parsing.cc
index f302956..ebe0596 100644
--- a/talk/p2p/base/parsing.cc
+++ b/talk/p2p/base/parsing.cc
@@ -91,18 +91,6 @@
return NULL;
}
-const buzz::XmlElement* GetXmlElement(const XmlElements& elems,
- const buzz::QName& name) {
- for (XmlElements::const_iterator iter = elems.begin();
- iter != elems.end(); ++iter) {
- const buzz::XmlElement* elem = *iter;
- if (elem->Name() == name) {
- return elem;
- }
- }
- return NULL;
-}
-
bool RequireXmlChild(const buzz::XmlElement* parent,
const std::string& name,
const buzz::XmlElement** child,
diff --git a/talk/p2p/base/parsing.h b/talk/p2p/base/parsing.h
index 589d9d7..96f4e8b 100644
--- a/talk/p2p/base/parsing.h
+++ b/talk/p2p/base/parsing.h
@@ -112,8 +112,6 @@
const buzz::XmlElement* GetXmlChild(const buzz::XmlElement* parent,
const std::string& name);
-const buzz::XmlElement* GetXmlElement(const XmlElements& elems,
- const buzz::QName& name);
bool RequireXmlChild(const buzz::XmlElement* parent,
const std::string& name,
diff --git a/talk/p2p/base/port.cc b/talk/p2p/base/port.cc
index a016f63..d47e90e 100644
--- a/talk/p2p/base/port.cc
+++ b/talk/p2p/base/port.cc
@@ -212,7 +212,7 @@
} else if (!msg) {
// STUN message handled already
} else if (msg->type() == STUN_BINDING_REQUEST) {
- SignalUnknownAddress(this, addr, msg, remote_username);
+ SignalUnknownAddress(this, addr, msg, remote_username, false);
} else {
// NOTE(tschmelcher): STUN_BINDING_RESPONSE is benign. It occurs if we
// pruned a connection for this port while it had STUN requests in flight,
diff --git a/talk/p2p/base/port.h b/talk/p2p/base/port.h
index 396fce0..1e81b0a 100644
--- a/talk/p2p/base/port.h
+++ b/talk/p2p/base/port.h
@@ -107,7 +107,6 @@
const std::string& password() const { return password_; }
void set_password(const std::string& password) { password_ = password; }
-
// A value in [0,1] that indicates the preference for this port versus other
// ports on this client. (Larger indicates more preference.)
float preference() const { return preference_; }
@@ -141,7 +140,8 @@
const AddressMap& connections() { return connections_; }
// Returns the connection to the given address or NULL if none exists.
- Connection* GetConnection(const talk_base::SocketAddress& remote_addr);
+ virtual Connection* GetConnection(
+ const talk_base::SocketAddress& remote_addr);
// Creates a new connection to the given address.
enum CandidateOrigin { ORIGIN_THIS_PORT, ORIGIN_OTHER_PORT, ORIGIN_MESSAGE };
@@ -160,15 +160,15 @@
// Indicates that we received a successful STUN binding request from an
// address that doesn't correspond to any current connection. To turn this
// into a real connection, call CreateConnection.
- sigslot::signal4<Port*, const talk_base::SocketAddress&, StunMessage*,
- const std::string&> SignalUnknownAddress;
+ sigslot::signal5<Port*, const talk_base::SocketAddress&, StunMessage*,
+ const std::string&, bool> SignalUnknownAddress;
// Sends a response message (normal or error) to the given request. One of
// these methods should be called as a response to SignalUnknownAddress.
// NOTE: You MUST call CreateConnection BEFORE SendBindingResponse.
- void SendBindingResponse(StunMessage* request,
- const talk_base::SocketAddress& addr);
- void SendBindingErrorResponse(
+ virtual void SendBindingResponse(StunMessage* request,
+ const talk_base::SocketAddress& addr);
+ virtual void SendBindingErrorResponse(
StunMessage* request, const talk_base::SocketAddress& addr,
int error_code, const std::string& reason);
@@ -211,6 +211,9 @@
// Debugging description of this port
std::string ToString() const;
+ talk_base::IPAddress& ip() { return ip_; }
+ int min_port() { return min_port_; }
+ int max_port() { return max_port_; }
protected:
// Fills in the local address of the port.
diff --git a/talk/p2p/base/port_unittest.cc b/talk/p2p/base/port_unittest.cc
index 1bdac3d..9a84606 100644
--- a/talk/p2p/base/port_unittest.cc
+++ b/talk/p2p/base/port_unittest.cc
@@ -123,7 +123,8 @@
}
void OnUnknownAddress(Port* port, const SocketAddress& addr,
- StunMessage* msg, const std::string& rf) {
+ StunMessage* msg, const std::string& rf,
+ bool /*port_muxed*/) {
ASSERT_EQ(src_.get(), port);
if (!remote_address_.IsAny()) {
ASSERT_EQ(remote_address_, addr);
diff --git a/talk/p2p/base/portallocator.cc b/talk/p2p/base/portallocator.cc
new file mode 100644
index 0000000..974117f
--- /dev/null
+++ b/talk/p2p/base/portallocator.cc
@@ -0,0 +1,83 @@
+/*
+ * 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 "talk/p2p/base/portallocator.h"
+
+#include "talk/p2p/base/portallocatorsessionproxy.h"
+
+namespace cricket {
+
+PortAllocator::~PortAllocator() {
+ for (SessionMuxerMap::iterator iter = muxers_.begin();
+ iter != muxers_.end(); ++iter) {
+ delete iter->second;
+ }
+}
+
+PortAllocatorSession* PortAllocator::CreateSession(
+ const std::string& sid,
+ const std::string& name,
+ const std::string& session_type) {
+ if (flags_ & PORTALLOCATOR_ENABLE_BUNDLE) {
+ PortAllocatorSessionMuxer* muxer = GetSessionMuxer(sid);
+ if (!muxer) {
+ PortAllocatorSession* session_impl = CreateSession(name, session_type);
+ // Create PortAllocatorSessionMuxer object for |session_impl|.
+ muxer = new PortAllocatorSessionMuxer(session_impl);
+ muxer->SignalDestroyed.connect(
+ this, &PortAllocator::OnSessionMuxerDestroyed);
+ // Add PortAllocatorSession to the map.
+ muxers_[sid] = muxer;
+ }
+ PortAllocatorSessionProxy* proxy =
+ new PortAllocatorSessionProxy(name, session_type, flags_);
+ muxer->RegisterSessionProxy(proxy);
+ return proxy;
+ }
+ return CreateSession(name, session_type);
+}
+
+PortAllocatorSessionMuxer* PortAllocator::GetSessionMuxer(
+ const std::string& sid) const {
+ SessionMuxerMap::const_iterator iter = muxers_.find(sid);
+ if (iter != muxers_.end())
+ return iter->second;
+ return NULL;
+}
+
+void PortAllocator::OnSessionMuxerDestroyed(
+ PortAllocatorSessionMuxer* session) {
+ SessionMuxerMap::iterator iter;
+ for (iter = muxers_.begin(); iter != muxers_.end(); ++iter) {
+ if (iter->second == session)
+ break;
+ }
+ if (iter != muxers_.end())
+ muxers_.erase(iter);
+}
+
+} // namespace cricket
diff --git a/talk/p2p/base/portallocator.h b/talk/p2p/base/portallocator.h
index 175bdbc..79a08c1 100644
--- a/talk/p2p/base/portallocator.h
+++ b/talk/p2p/base/portallocator.h
@@ -47,18 +47,31 @@
const uint32 PORTALLOCATOR_DISABLE_RELAY = 0x04;
const uint32 PORTALLOCATOR_DISABLE_TCP = 0x08;
const uint32 PORTALLOCATOR_ENABLE_SHAKER = 0x10;
+const uint32 PORTALLOCATOR_ENABLE_BUNDLE = 0x20;
const uint32 kDefaultPortAllocatorFlags = 0;
+class PortAllocatorSessionMuxer;
+
class PortAllocatorSession : public sigslot::has_slots<> {
public:
- explicit PortAllocatorSession(uint32 flags) : flags_(flags) {}
+ // TODO Remove session_type argument (and other places), as
+ // its not used.
+ explicit PortAllocatorSession(const std::string& name,
+ const std::string& session_type,
+ uint32 flags) :
+ name_(name),
+ session_type_(session_type),
+ flags_(flags) {
+ }
// Subclasses should clean up any ports created.
virtual ~PortAllocatorSession() {}
uint32 flags() const { return flags_; }
void set_flags(uint32 flags) { flags_ = flags; }
+ const std::string& name() const { return name_; }
+ const std::string& session_type() const { return session_type_; }
// Prepares an initial set of ports to try.
virtual void GetInitialPorts() = 0;
@@ -74,23 +87,33 @@
uint32 generation() { return generation_; }
void set_generation(uint32 generation) { generation_ = generation; }
+ sigslot::signal1<PortAllocatorSession*> SignalDestroyed;
+
+ protected:
+ std::string name_;
+ std::string session_type_;
private:
uint32 flags_;
uint32 generation_;
};
-class PortAllocator {
+class PortAllocator : public sigslot::has_slots<> {
public:
PortAllocator() :
flags_(kDefaultPortAllocatorFlags),
min_port_(0),
max_port_(0) {
}
- virtual ~PortAllocator() {}
+ virtual ~PortAllocator();
- virtual PortAllocatorSession *CreateSession(const std::string &name,
- const std::string &session_type) = 0;
+ PortAllocatorSession* CreateSession(
+ const std::string& sid,
+ const std::string& name,
+ const std::string& session_type);
+
+ PortAllocatorSessionMuxer* GetSessionMuxer(const std::string& sid) const;
+ void OnSessionMuxerDestroyed(PortAllocatorSessionMuxer* session);
uint32 flags() const { return flags_; }
void set_flags(uint32 flags) { flags_ = flags; }
@@ -116,11 +139,18 @@
}
protected:
+ virtual PortAllocatorSession* CreateSession(const std::string &name,
+ const std::string &session_type) = 0;
+
+ typedef std::map<std::string, PortAllocatorSessionMuxer*> SessionMuxerMap;
+
uint32 flags_;
std::string agent_;
talk_base::ProxyInfo proxy_;
int min_port_;
int max_port_;
+
+ SessionMuxerMap muxers_;
};
} // namespace cricket
diff --git a/talk/p2p/base/portallocatorsessionproxy.cc b/talk/p2p/base/portallocatorsessionproxy.cc
new file mode 100644
index 0000000..8305cd3
--- /dev/null
+++ b/talk/p2p/base/portallocatorsessionproxy.cc
@@ -0,0 +1,154 @@
+/*
+ * libjingle
+ * Copyright 2004--2011, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/portallocatorsessionproxy.h"
+
+#include "talk/p2p/base/portallocator.h"
+#include "talk/p2p/base/portproxy.h"
+
+namespace cricket {
+
+PortAllocatorSessionMuxer::PortAllocatorSessionMuxer(
+ PortAllocatorSession* session)
+ : session_(session) {
+ session_->SignalPortReady.connect(
+ this, &PortAllocatorSessionMuxer::OnPortReady);
+}
+
+PortAllocatorSessionMuxer::~PortAllocatorSessionMuxer() {
+ for (size_t i = 0; i < session_proxies_.size(); ++i)
+ delete session_proxies_[i];
+
+ SignalDestroyed(this);
+}
+
+void PortAllocatorSessionMuxer::RegisterSessionProxy(
+ PortAllocatorSessionProxy* session_proxy) {
+ session_proxies_.push_back(session_proxy);
+ session_proxy->SignalDestroyed.connect(
+ this, &PortAllocatorSessionMuxer::OnSessionProxyDestroyed);
+ session_proxy->set_impl(session_.get());
+}
+
+void PortAllocatorSessionMuxer::OnPortReady(PortAllocatorSession* session,
+ Port* port) {
+ ASSERT(session == session_.get());
+ ports_.push_back(port);
+ port->SignalDestroyed.connect(
+ this, &PortAllocatorSessionMuxer::OnPortDestroyed);
+}
+
+void PortAllocatorSessionMuxer::OnPortDestroyed(Port* port) {
+ std::vector<Port*>::iterator it =
+ std::find(ports_.begin(), ports_.end(), port);
+ if (it != ports_.end())
+ ports_.erase(it);
+}
+
+void PortAllocatorSessionMuxer::OnSessionProxyDestroyed(
+ PortAllocatorSession* proxy) {
+ std::vector<PortAllocatorSessionProxy*>::iterator it =
+ std::find(session_proxies_.begin(), session_proxies_.end(), proxy);
+ if (it != session_proxies_.end())
+ session_proxies_.erase(it);
+
+ if (session_proxies_.empty()) {
+ // Destroy PortAllocatorSession and its associated muxer object if all
+ // proxies belonging to this session are already destroyed.
+ delete this;
+ }
+}
+
+PortAllocatorSessionProxy::~PortAllocatorSessionProxy() {
+ std::map<Port*, PortProxy*>::iterator it;
+ for (it = proxy_ports_.begin(); it != proxy_ports_.end(); it++)
+ delete it->second;
+
+ SignalDestroyed(this);
+}
+
+void PortAllocatorSessionProxy::set_impl(
+ PortAllocatorSession* session) {
+ impl_ = session;
+
+ impl_->SignalCandidatesReady.connect(
+ this, &PortAllocatorSessionProxy::OnCandidatesReady);
+ impl_->SignalPortReady.connect(
+ this, &PortAllocatorSessionProxy::OnPortReady);
+}
+
+void PortAllocatorSessionProxy::GetInitialPorts() {
+ ASSERT(impl_ != NULL);
+ impl_->GetInitialPorts();
+}
+
+void PortAllocatorSessionProxy::StartGetAllPorts() {
+ ASSERT(impl_ != NULL);
+ impl_->StartGetAllPorts();
+}
+
+void PortAllocatorSessionProxy::StopGetAllPorts() {
+ ASSERT(impl_ != NULL);
+ impl_->StartGetAllPorts();
+}
+
+bool PortAllocatorSessionProxy::IsGettingAllPorts() {
+ ASSERT(impl_ != NULL);
+ return impl_->IsGettingAllPorts();
+}
+
+void PortAllocatorSessionProxy::OnPortReady(PortAllocatorSession* session,
+ Port* port) {
+ ASSERT(session == impl_);
+
+ PortProxy* proxy_port = new PortProxy(
+ port->thread(), port->type(), port->socket_factory(), port->network(),
+ port->ip(), port->min_port(), port->max_port());
+ proxy_port->set_impl(port);
+ proxy_ports_[port] = proxy_port;
+ SignalPortReady(this, proxy_port);
+}
+
+void PortAllocatorSessionProxy::OnCandidatesReady(
+ PortAllocatorSession* session,
+ const std::vector<Candidate>& candidates) {
+ ASSERT(session == impl_);
+
+ // Since all proxy sessions share a common PortAllocatorSession,
+ // all Candidates will have name associated with the common PAS.
+ // Change Candidate name with the PortAllocatorSessionProxy name.
+ std::vector<Candidate> our_candidates;
+ for (size_t i = 0; i < candidates.size(); ++i) {
+ Candidate new_local_candidate = candidates[i];
+ new_local_candidate.set_name(name_);
+ our_candidates.push_back(new_local_candidate);
+ }
+
+ SignalCandidatesReady(this, our_candidates);
+}
+
+} // namespace cricket
diff --git a/talk/p2p/base/portallocatorsessionproxy.h b/talk/p2p/base/portallocatorsessionproxy.h
new file mode 100644
index 0000000..5f9eaec
--- /dev/null
+++ b/talk/p2p/base/portallocatorsessionproxy.h
@@ -0,0 +1,101 @@
+/*
+ * libjingle
+ * Copyright 2004--2011, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_P2P_BASE_PORTALLOCATORSESSIONPROXY_H_
+#define TALK_P2P_BASE_PORTALLOCATORSESSIONPROXY_H_
+
+#include <string>
+
+#include "talk/p2p/base/candidate.h"
+#include "talk/p2p/base/portallocator.h"
+
+namespace cricket {
+class PortAllocator;
+class PortAllocatorSessionProxy;
+class PortProxy;
+
+// This class maintains the list of cricket::Port* objects. Ports will be
+// deleted upon receiving SignalDestroyed signal. This class is used when
+// PORTALLOCATOR_ENABLE_BUNDLE flag is set.
+
+class PortAllocatorSessionMuxer : public sigslot::has_slots<> {
+ public:
+ explicit PortAllocatorSessionMuxer(PortAllocatorSession* session);
+ virtual ~PortAllocatorSessionMuxer();
+
+ void RegisterSessionProxy(PortAllocatorSessionProxy* session_proxy);
+
+ void OnPortReady(PortAllocatorSession* session, Port* port);
+ void OnPortDestroyed(Port* port);
+
+ const std::vector<Port*>& ports() { return ports_; }
+
+ sigslot::signal1<PortAllocatorSessionMuxer*> SignalDestroyed;
+
+ private:
+ void OnSessionProxyDestroyed(PortAllocatorSession* proxy);
+
+ // Port will be deleted when SignalDestroyed received, otherwise delete
+ // happens when PortAllocatorSession dtor is called.
+ std::vector<Port*> ports_;
+ talk_base::scoped_ptr<PortAllocatorSession> session_;
+ std::vector<PortAllocatorSessionProxy*> session_proxies_;
+};
+
+class PortAllocatorSessionProxy : public PortAllocatorSession {
+ public:
+ PortAllocatorSessionProxy(const std::string& name,
+ const std::string& session_type,
+ uint32 flags)
+ : PortAllocatorSession(name, session_type, flags),
+ impl_(NULL) {}
+
+ virtual ~PortAllocatorSessionProxy();
+
+ PortAllocatorSession* impl() { return impl_; }
+ void set_impl(PortAllocatorSession* session);
+
+ // Forwards call to the actual PortAllocatorSession.
+ virtual void GetInitialPorts();
+ virtual void StartGetAllPorts();
+ virtual void StopGetAllPorts();
+ virtual bool IsGettingAllPorts();
+
+ private:
+ void OnPortReady(PortAllocatorSession* session, Port* port);
+ void OnCandidatesReady(PortAllocatorSession* session,
+ const std::vector<Candidate>& candidates);
+ void OnPortDestroyed(Port* port);
+
+ // This is the actual PortAllocatorSession, owned by PortAllocator.
+ PortAllocatorSession* impl_;
+ std::map<Port*, PortProxy*> proxy_ports_;
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_PORTALLOCATORSESSIONPROXY_H_
diff --git a/talk/p2p/base/portproxy.cc b/talk/p2p/base/portproxy.cc
new file mode 100644
index 0000000..6c20127
--- /dev/null
+++ b/talk/p2p/base/portproxy.cc
@@ -0,0 +1,85 @@
+/*
+ * libjingle
+ * Copyright 2004--2011, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/portproxy.h"
+
+namespace cricket {
+
+void PortProxy::set_impl(Port* port) {
+ impl_ = port;
+ impl_->SignalUnknownAddress.connect(
+ this, &PortProxy::OnUnknownAddress);
+ impl_->SignalDestroyed.connect(this, &PortProxy::OnPortDestroyed);
+}
+
+void PortProxy::PrepareAddress() {
+ impl_->PrepareAddress();
+}
+
+Connection* PortProxy::CreateConnection(const Candidate& remote_candidate,
+ CandidateOrigin origin) {
+ ASSERT(impl_ != NULL);
+ return impl_->CreateConnection(remote_candidate, origin);
+}
+
+int PortProxy::SendTo(const void* data,
+ size_t size,
+ const talk_base::SocketAddress& addr,
+ bool payload) {
+ ASSERT(impl_ != NULL);
+ return impl_->SendTo(data, size, addr, payload);
+}
+
+int PortProxy::SetOption(talk_base::Socket::Option opt,
+ int value) {
+ ASSERT(impl_ != NULL);
+ return impl_->SetOption(opt, value);
+}
+
+int PortProxy::GetError() {
+ ASSERT(impl_ != NULL);
+ return impl_->GetError();
+}
+
+void PortProxy::OnUnknownAddress(
+ Port *port,
+ const talk_base::SocketAddress &addr,
+ StunMessage *stun_msg,
+ const std::string &remote_username,
+ bool port_muxed) {
+ ASSERT(port == impl_);
+ ASSERT(!port_muxed);
+ SignalUnknownAddress(this, addr, stun_msg, remote_username, true);
+}
+
+void PortProxy::OnPortDestroyed(Port* port) {
+ ASSERT(port == impl_);
+ // |port| will be destroyed in PortAllocatorSessionMuxer.
+ SignalDestroyed(this);
+}
+
+} // namespace cricket
diff --git a/talk/p2p/base/portproxy.h b/talk/p2p/base/portproxy.h
new file mode 100644
index 0000000..49707d4
--- /dev/null
+++ b/talk/p2p/base/portproxy.h
@@ -0,0 +1,85 @@
+/*
+ * libjingle
+ * Copyright 2004--2011, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_P2P_BASE_PORTPROXY_H_
+#define TALK_P2P_BASE_PORTPROXY_H_
+
+#include "talk/p2p/base/port.h"
+
+namespace cricket {
+
+class PortProxy : public Port {
+ public:
+ PortProxy(talk_base::Thread* thread, const std::string& type,
+ talk_base::PacketSocketFactory* factory,
+ talk_base::Network* network,
+ const talk_base::IPAddress& ip, int min_port, int max_port)
+ : Port(thread, type, factory, network, ip, min_port, max_port) {
+ }
+ virtual ~PortProxy() {}
+
+ Port* impl() { return impl_; }
+ void set_impl(Port* port);
+
+ // Forwards call to the actual Port.
+ virtual void PrepareAddress();
+ virtual Connection* CreateConnection(const Candidate& remote_candidate,
+ CandidateOrigin origin);
+ virtual int SendTo(
+ const void* data, size_t size, const talk_base::SocketAddress& addr,
+ bool payload);
+ virtual int SetOption(talk_base::Socket::Option opt, int value);
+ virtual int GetError();
+
+ virtual void SendBindingResponse(StunMessage* request,
+ const talk_base::SocketAddress& addr) {
+ impl_->SendBindingResponse(request, addr);
+ }
+
+ virtual Connection* GetConnection(
+ const talk_base::SocketAddress& remote_addr) {
+ return impl_->GetConnection(remote_addr);
+ }
+
+ virtual void SendBindingErrorResponse(
+ StunMessage* request, const talk_base::SocketAddress& addr,
+ int error_code, const std::string& reason) {
+ impl_->SendBindingErrorResponse(request, addr, error_code, reason);
+ }
+
+ private:
+ void OnUnknownAddress(Port *port, const talk_base::SocketAddress &addr,
+ StunMessage *stun_msg,
+ const std::string &remote_username,
+ bool port_muxed);
+ void OnPortDestroyed(Port* port);
+ Port* impl_;
+};
+
+} // namespace cricket
+
+#endif // TALK_P2P_BASE_PORTPROXY_H_
diff --git a/talk/p2p/base/rawtransportchannel.cc b/talk/p2p/base/rawtransportchannel.cc
index 8736053..0c2691c 100644
--- a/talk/p2p/base/rawtransportchannel.cc
+++ b/talk/p2p/base/rawtransportchannel.cc
@@ -95,7 +95,8 @@
void RawTransportChannel::Connect() {
// Create an allocator that only returns stun and relay ports.
- allocator_session_ = allocator_->CreateSession(name(), content_type());
+ allocator_session_ = allocator_->CreateSession(
+ session_id(), name(), content_type());
uint32 flags = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_TCP;
diff --git a/talk/p2p/base/session.cc b/talk/p2p/base/session.cc
index 9891ee2..2439682 100644
--- a/talk/p2p/base/session.cc
+++ b/talk/p2p/base/session.cc
@@ -156,6 +156,7 @@
TransportChannelImpl* impl = transport_->GetChannel(name);
if (impl == NULL) {
impl = transport_->CreateChannel(name, content_type);
+ impl->set_session_id(sid_);
}
return impl;
}
@@ -284,7 +285,7 @@
transport->SignalChannelGone.connect(
this, &BaseSession::OnTransportChannelGone);
- transproxy = new TransportProxy(content_name, transport);
+ transproxy = new TransportProxy(sid_, content_name, transport);
transports_[content_name] = transproxy;
return transproxy;
@@ -363,8 +364,8 @@
// in SDP. It may be necessary to check content_names in groups of both
// local and remote descriptions. Assumption here is that when this method
// returns true, media contents can be muxed.
- if (local_description()->HasGroup(GN_TOGETHER) &&
- remote_description()->HasGroup(GN_TOGETHER)) {
+ if (local_description()->HasGroup(GN_BUNDLE) &&
+ remote_description()->HasGroup(GN_BUNDLE)) {
return true;
}
return false;
@@ -378,7 +379,7 @@
// Always use first content name from the group for muxing. Hence ordering
// of content names in SDP should match to the order in group.
const ContentGroup* muxed_content_group =
- local_description()->GetGroupByName(GN_TOGETHER);
+ local_description()->GetGroupByName(GN_BUNDLE);
const std::string* content_name =
muxed_content_group->FirstContentName();
if (content_name) {
@@ -966,22 +967,22 @@
if (remote_description()->GetContentByName(it->name) == NULL) {
return false;
}
-
- // TODO: We should add a check to ensure that the updated
- // contents are compatible with the original contents.
}
- // Merge the updates into the remote description.
- // TODO: Merge streams instead of overwriting.
- for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
- LOG(LS_INFO) << "Updating content " << it->name;
- remote_description()->RemoveContentByName(it->name);
- remote_description()->AddContent(it->name, it->type, it->description);
- }
+ // TODO: We used to replace contents from an update, but
+ // that no longer works with partial updates. We need to figure out
+ // a way to merge patial updates into contents. For now, users of
+ // Session should listen to SignalRemoteDescriptionUpdate and handle
+ // updates. They should not expect remote_description to be the
+ // latest value.
+ //
+ // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
+ // remote_description()->RemoveContentByName(it->name);
+ // remote_description()->AddContent(it->name, it->type, it->description);
+ // }
+ // }
- // TODO: Add an argument that shows what streams were changed.
- SignalRemoteDescriptionUpdate(this);
-
+ SignalRemoteDescriptionUpdate(this, updated_contents);
return true;
}
diff --git a/talk/p2p/base/session.h b/talk/p2p/base/session.h
index f3b6f25..b57468b 100644
--- a/talk/p2p/base/session.h
+++ b/talk/p2p/base/session.h
@@ -81,8 +81,12 @@
class TransportProxy {
public:
- TransportProxy(const std::string& content_name, Transport* transport)
- : content_name_(content_name),
+ TransportProxy(
+ const std::string& sid,
+ const std::string& content_name,
+ Transport* transport)
+ : sid_(sid),
+ content_name_(content_name),
transport_(transport),
owner_(true),
state_(STATE_INIT),
@@ -126,6 +130,7 @@
const std::string& content_type);
void SetProxyImpl(const std::string& name, TransportChannelProxy* proxy);
+ std::string sid_;
std::string content_name_;
Transport* transport_;
bool owner_;
@@ -241,12 +246,12 @@
// Returns the current state of the session. See the enum above for details.
// Each time the state changes, we will fire this signal.
State state() const { return state_; }
- sigslot::signal2<BaseSession *, State> SignalState;
+ sigslot::signal2<BaseSession* , State> SignalState;
// Returns the last error in the session. See the enum above for details.
// Each time the an error occurs, we will fire this signal.
Error error() const { return error_; }
- sigslot::signal2<BaseSession *, Error> SignalError;
+ sigslot::signal2<BaseSession* , Error> SignalError;
// Updates the state, signaling if necessary.
virtual void SetState(State state);
@@ -254,8 +259,10 @@
// Updates the error state, signaling if necessary.
virtual void SetError(Error error);
- // Fired when the remote description is updated.
- sigslot::signal1<BaseSession *> SignalRemoteDescriptionUpdate;
+ // Fired when the remote description is updated, with the updated
+ // contents.
+ sigslot::signal2<BaseSession* , const ContentInfos&>
+ SignalRemoteDescriptionUpdate;
// Returns the transport that has been negotiated or NULL if
// negotiation is still in progress.
@@ -534,7 +541,7 @@
// sending of each message. When messages are received by the other client,
// they should be handed to OnIncomingMessage.
// (These are called only by SessionManager.)
- sigslot::signal2<Session *, const buzz::XmlElement*> SignalOutgoingMessage;
+ sigslot::signal2<Session* , const buzz::XmlElement*> SignalOutgoingMessage;
void OnIncomingMessage(const SessionMessage& msg);
void OnIncomingResponse(const buzz::XmlElement* orig_stanza,
diff --git a/talk/p2p/base/session_unittest.cc b/talk/p2p/base/session_unittest.cc
index e663113..aec4986 100644
--- a/talk/p2p/base/session_unittest.cc
+++ b/talk/p2p/base/session_unittest.cc
@@ -561,9 +561,9 @@
class TestPortAllocatorSession : public cricket::PortAllocatorSession {
public:
TestPortAllocatorSession(const std::string& name,
+ const std::string& session_type,
const int port_offset)
- : PortAllocatorSession(0),
- name_(name),
+ : PortAllocatorSession(name, session_type, 0),
port_offset_(port_offset),
ports_(kNumPorts),
address_("127.0.0.1", 0),
@@ -618,7 +618,6 @@
}
private:
- std::string name_;
int port_offset_;
std::vector<cricket::Port*> ports_;
talk_base::SocketAddress address_;
@@ -636,7 +635,7 @@
CreateSession(const std::string &name,
const std::string &content_type) {
port_offset_ += 2;
- return new TestPortAllocatorSession(name, port_offset_ - 2);
+ return new TestPortAllocatorSession(name, content_type, port_offset_ - 2);
}
int port_offset_;
@@ -984,7 +983,8 @@
}
}
- void OnSessionRemoteDescriptionUpdate(cricket::BaseSession* session) {
+ void OnSessionRemoteDescriptionUpdate(cricket::BaseSession* session,
+ const cricket::ContentInfos& contents) {
session_remote_description_update_count++;
}
@@ -1338,14 +1338,23 @@
new_content_b->description;
EXPECT_TRUE(new_content_desc_a != NULL);
EXPECT_TRUE(new_content_desc_b != NULL);
- EXPECT_NE(old_content_desc_a, new_content_desc_a);
- if (content_name_a != content_name_b) {
- // If content_name_a != content_name_b, then b's content description
- // should not have changed since the description-info message only
- // contained an update for content_name_a.
- EXPECT_EQ(old_content_desc_b, new_content_desc_b);
- }
+ // TODO: We used to replace contents from an update, but
+ // that no longer works with partial updates. We need to figure out
+ // a way to merge patial updates into contents. For now, users of
+ // Session should listen to SignalRemoteDescriptionUpdate and handle
+ // updates. They should not expect remote_description to be the
+ // latest value.
+ // See session.cc OnDescriptionInfoMessage.
+
+ // EXPECT_NE(old_content_desc_a, new_content_desc_a);
+
+ // if (content_name_a != content_name_b) {
+ // // If content_name_a != content_name_b, then b's content description
+ // // should not have changed since the description-info message only
+ // // contained an update for content_name_a.
+ // EXPECT_EQ(old_content_desc_b, new_content_desc_b);
+ // }
EXPECT_TRUE_WAIT(initiator->sent_stanza_count() > 0, kEventTimeout);
initiator->ExpectSentStanza(
@@ -1998,7 +2007,7 @@
content_name_a, content_type,
content_name_b, content_type);
// Add group information to the offer
- cricket::ContentGroup group(cricket::GN_TOGETHER);
+ cricket::ContentGroup group(cricket::GN_BUNDLE);
group.AddContentName(content_name_a);
group.AddContentName(content_name_b);
EXPECT_TRUE(group.HasContentName(content_name_a));
@@ -2012,12 +2021,12 @@
content_name_b, content_type);
// Check if group "TOGETHER" exists in the offer. If it's present then
// remote supports muxing.
- EXPECT_TRUE(offer->HasGroup(cricket::GN_TOGETHER));
+ EXPECT_TRUE(offer->HasGroup(cricket::GN_BUNDLE));
const cricket::ContentGroup* group_offer =
- offer->GetGroupByName(cricket::GN_TOGETHER);
+ offer->GetGroupByName(cricket::GN_BUNDLE);
// Not creating new copy in answer, for test we can use this for test.
answer->AddGroup(*group_offer);
- EXPECT_TRUE(answer->HasGroup(cricket::GN_TOGETHER));
+ EXPECT_TRUE(answer->HasGroup(cricket::GN_BUNDLE));
initiator->CreateSession();
EXPECT_TRUE(initiator->session->Initiate(
diff --git a/talk/p2p/base/transportchannel.h b/talk/p2p/base/transportchannel.h
index 2672e80..74d0107 100644
--- a/talk/p2p/base/transportchannel.h
+++ b/talk/p2p/base/transportchannel.h
@@ -47,6 +47,8 @@
readable_(false), writable_(false) {}
virtual ~TransportChannel() {}
+ // Returns the session id of this channel.
+ const std::string& session_id() const { return session_id_; }
// Returns the name of this channel.
const std::string& name() const { return name_; }
const std::string& content_type() const { return content_type_; }
@@ -66,6 +68,12 @@
// supported by all transport types.
virtual int SetOption(talk_base::Socket::Option opt, int value) = 0;
+ // Sets session id which created this transport channel.
+ // This is called from TransportProxy::GetOrCreateImpl.
+ void set_session_id(const std::string& session_id) {
+ session_id_ = session_id;
+ }
+
// Returns the most recent error that occurred on this channel.
virtual int GetError() = 0;
@@ -95,7 +103,9 @@
// Sets the writable state, signaling if necessary.
void set_writable(bool writable);
+
private:
+ std::string session_id_;
std::string name_;
std::string content_type_;
bool readable_;
diff --git a/talk/p2p/client/basicportallocator.cc b/talk/p2p/client/basicportallocator.cc
index ec13b86..b77d5ed 100644
--- a/talk/p2p/client/basicportallocator.cc
+++ b/talk/p2p/client/basicportallocator.cc
@@ -229,8 +229,8 @@
BasicPortAllocator *allocator,
const std::string &name,
const std::string &session_type)
- : PortAllocatorSession(allocator->flags()), allocator_(allocator),
- name_(name), session_type_(session_type), network_thread_(NULL),
+ : PortAllocatorSession(name, session_type, allocator->flags()),
+ allocator_(allocator), network_thread_(NULL),
socket_factory_(allocator->socket_factory()), allocation_started_(false),
network_manager_started_(false),
running_(false) {
diff --git a/talk/p2p/client/basicportallocator.h b/talk/p2p/client/basicportallocator.h
index d5fce7e..f38bac6 100644
--- a/talk/p2p/client/basicportallocator.h
+++ b/talk/p2p/client/basicportallocator.h
@@ -114,8 +114,6 @@
~BasicPortAllocatorSession();
virtual BasicPortAllocator* allocator() { return allocator_; }
- const std::string& name() const { return name_; }
- const std::string& session_type() const { return session_type_; }
talk_base::Thread* network_thread() { return network_thread_; }
talk_base::PacketSocketFactory* socket_factory() { return socket_factory_; }
@@ -154,8 +152,6 @@
void OnShake();
BasicPortAllocator* allocator_;
- std::string name_;
- std::string session_type_;
talk_base::Thread* network_thread_;
talk_base::scoped_ptr<talk_base::PacketSocketFactory> owned_socket_factory_;
talk_base::PacketSocketFactory* socket_factory_;
diff --git a/talk/p2p/client/portallocator_unittest.cc b/talk/p2p/client/portallocator_unittest.cc
index be3a4e1..f5bb37c 100644
--- a/talk/p2p/client/portallocator_unittest.cc
+++ b/talk/p2p/client/portallocator_unittest.cc
@@ -35,6 +35,8 @@
#include "talk/base/socketaddress.h"
#include "talk/base/thread.h"
#include "talk/base/virtualsocketserver.h"
+#include "talk/p2p/base/p2ptransportchannel.h"
+#include "talk/p2p/base/portallocatorsessionproxy.h"
#include "talk/p2p/base/testrelayserver.h"
#include "talk/p2p/base/teststunserver.h"
#include "talk/p2p/client/basicportallocator.h"
@@ -44,6 +46,7 @@
using talk_base::Thread;
static const SocketAddress kClientAddr("11.11.11.11", 0);
+static const SocketAddress kRemoteClientAddr("22.22.22.22", 0);
static const SocketAddress kStunAddr("99.99.99.1", cricket::STUN_SERVER_PORT);
static const SocketAddress kRelayUdpIntAddr("99.99.99.2", 5000);
static const SocketAddress kRelayUdpExtAddr("99.99.99.3", 5001);
@@ -99,6 +102,18 @@
return true;
}
+ cricket::PortAllocatorSession* CreateSession(
+ const std::string& sid, const std::string& name,
+ const std::string& type, cricket::PortAllocator* allocator) {
+ cricket::PortAllocatorSession* session =
+ allocator->CreateSession(sid, name, type);
+ session->SignalPortReady.connect(this,
+ &PortAllocatorTest::OnPortReady);
+ session->SignalCandidatesReady.connect(this,
+ &PortAllocatorTest::OnCandidatesReady);
+ return session;
+ }
+
static bool CheckCandidate(const cricket::Candidate& c,
const std::string& name, const std::string& type,
const std::string& proto,
@@ -112,6 +127,17 @@
return (addr.port() >= min_port && addr.port() <= max_port);
}
+ cricket::PortAllocator* CreateAllocator() {
+ return new cricket::BasicPortAllocator(
+ &network_manager_, kStunAddr, SocketAddress(),
+ SocketAddress(), SocketAddress());
+ }
+
+ cricket::P2PTransportChannel* CreateTransportChannel(
+ const std::string& name, cricket::PortAllocator* allocator) {
+ return new cricket::P2PTransportChannel(name, "unittest", NULL, allocator);
+ }
+
protected:
cricket::BasicPortAllocator& allocator() { return allocator_; }
@@ -319,3 +345,52 @@
EXPECT_EQ(2U, alloc.relay_hosts().size());
EXPECT_EQ(2U, alloc.stun_hosts().size());
}
+
+TEST_F(PortAllocatorTest, TestBasicMuxFeatures) {
+ talk_base::scoped_ptr<cricket::PortAllocator> allocator(CreateAllocator());
+ allocator->set_flags(cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+ // Session ID - session1.
+ talk_base::scoped_ptr<cricket::PortAllocatorSession> session1(
+ CreateSession("session1", "rtp", "audio", allocator.get()));
+ talk_base::scoped_ptr<cricket::PortAllocatorSession> session2(
+ CreateSession("session1", "rtcp", "audio", allocator.get()));
+ // We know that PortAllocator is creating a proxy session when bundle flag
+ // is enabled, it's safe to type cast session objects.
+ cricket::PortAllocatorSessionProxy* proxy1 =
+ static_cast<cricket::PortAllocatorSessionProxy*>(session1.get());
+ ASSERT_TRUE(proxy1 != NULL);
+ cricket::PortAllocatorSessionProxy* proxy2 =
+ static_cast<cricket::PortAllocatorSessionProxy*>(session2.get());
+ ASSERT_TRUE(proxy2 != NULL);
+ EXPECT_EQ(proxy1->impl(), proxy2->impl());
+ AddInterface(kClientAddr);
+ session1->GetInitialPorts();
+ session2->GetInitialPorts();
+ // Each session should receive two proxy ports of local and stun.
+ ASSERT_EQ_WAIT(4U, ports_.size(), 1000);
+ EXPECT_EQ(4U, candidates_.size());
+ EXPECT_PRED5(CheckCandidate, candidates_[0],
+ "rtp", "local", "udp", kClientAddr);
+ EXPECT_PRED5(CheckCandidate, candidates_[1],
+ "rtcp", "local", "udp", kClientAddr);
+ EXPECT_PRED5(CheckCandidate, candidates_[2],
+ "rtp", "stun", "udp", kClientAddr);
+ EXPECT_PRED5(CheckCandidate, candidates_[3],
+ "rtcp", "stun", "udp", kClientAddr);
+ talk_base::scoped_ptr<cricket::PortAllocatorSession> session3(
+ CreateSession("session1", "video_rtp", "video", allocator.get()));
+ // ListenToEvents(session3.get());
+ session3->GetInitialPorts();
+ // Since real ports and sessions are already allocated and signal sent, no
+ // new ports will be allocated when new proxy session created.
+ talk_base::Thread::Current()->ProcessMessages(1000);
+ EXPECT_NE(6U, ports_.size());
+ // Creating a PortAllocatorSession with different session name from above.
+ // In this case proxy PAS should have a different PAS.
+ // Session ID - session2.
+ talk_base::scoped_ptr<cricket::PortAllocatorSession> session4(
+ CreateSession("session2", "video_rtp", "video", allocator.get()));
+ cricket::PortAllocatorSessionProxy* proxy4 =
+ static_cast<cricket::PortAllocatorSessionProxy*>(session4.get());
+ EXPECT_NE(proxy4->impl(), proxy1->impl());
+}
diff --git a/talk/session/phone/call.cc b/talk/session/phone/call.cc
index 3c770af..1157519 100644
--- a/talk/session/phone/call.cc
+++ b/talk/session/phone/call.cc
@@ -29,6 +29,7 @@
#include "talk/base/helpers.h"
#include "talk/base/logging.h"
#include "talk/base/thread.h"
+#include "talk/p2p/base/parsing.h"
#include "talk/session/phone/call.h"
#include "talk/session/phone/mediasessionclient.h"
@@ -140,8 +141,8 @@
StaticVideoViews::const_iterator it;
for (it = view_request.static_video_views.begin();
it != view_request.static_video_views.end(); ++it) {
- NamedSource found_source;
- bool found = media_sources_.GetVideoSourceBySsrc(it->ssrc, &found_source);
+ StreamParams found_stream;
+ bool found = recv_streams_.GetVideoStreamBySsrc(it->ssrc, &found_stream);
if (!found) {
LOG(LS_WARNING) <<
"Tried sending view request for bad ssrc: " << it->ssrc;
@@ -178,32 +179,41 @@
}
}
-void Call::AddVoiceStream(Session *session, uint32 voice_ssrc) {
+
+
+
+void Call::AddAudioRecvStream(Session *session, const StreamParams& stream) {
VoiceChannel *voice_channel = GetVoiceChannel(session);
- if (voice_channel && voice_ssrc) {
- voice_channel->AddRecvStream(StreamParams::CreateLegacy(voice_ssrc));
+ if (voice_channel && stream.has_ssrcs()) {
+ voice_channel->AddRecvStream(stream);
}
+ recv_streams_.AddAudioStream(stream);
}
-void Call::AddVideoStream(Session *session, uint32 video_ssrc) {
+void Call::AddVideoRecvStream(Session *session, const StreamParams& stream) {
VideoChannel *video_channel = GetVideoChannel(session);
- if (video_channel && video_ssrc) {
- video_channel->AddRecvStream(StreamParams::CreateLegacy(video_ssrc));
+ if (video_channel && stream.has_ssrcs()) {
+ video_channel->AddRecvStream(stream);
}
+ recv_streams_.AddVideoStream(stream);
}
-void Call::RemoveVoiceStream(Session *session, uint32 voice_ssrc) {
+void Call::RemoveAudioRecvStream(Session *session, const StreamParams& stream) {
VoiceChannel *voice_channel = GetVoiceChannel(session);
- if (voice_channel && voice_ssrc) {
- voice_channel->RemoveRecvStream(voice_ssrc);
+ // TODO: Change RemoveRecvStream to take a stream argument.
+ if (voice_channel && stream.has_ssrcs()) {
+ voice_channel->RemoveRecvStream(stream.first_ssrc());
}
+ recv_streams_.RemoveAudioStreamByNickAndName(stream.nick, stream.name);
}
-void Call::RemoveVideoStream(Session *session, uint32 video_ssrc) {
+void Call::RemoveVideoRecvStream(Session *session, const StreamParams& stream) {
VideoChannel *video_channel = GetVideoChannel(session);
- if (video_channel && video_ssrc) {
- video_channel->RemoveRecvStream(video_ssrc);
+ // TODO: Change RemoveRecvStream to take a stream argument.
+ if (video_channel && stream.has_ssrcs()) {
+ video_channel->RemoveRecvStream(stream.first_ssrc());
}
+ recv_streams_.RemoveVideoStreamByNickAndName(stream.nick, stream.name);
}
void Call::OnMessage(talk_base::Message *message) {
@@ -273,7 +283,10 @@
sessions_.push_back(session);
session->SignalState.connect(this, &Call::OnSessionState);
session->SignalError.connect(this, &Call::OnSessionError);
- session->SignalInfoMessage.connect(this, &Call::OnSessionInfo);
+ session->SignalInfoMessage.connect(
+ this, &Call::OnSessionInfoMessage);
+ session->SignalRemoteDescriptionUpdate.connect(
+ this, &Call::OnRemoteDescriptionUpdate);
session->SignalReceivedTerminateReason
.connect(this, &Call::OnReceivedTerminateReason);
@@ -542,11 +555,10 @@
}
void Call::OnSpeakerMonitor(CurrentSpeakerMonitor* monitor, uint32 ssrc) {
- NamedSource source;
- source.ssrc = ssrc;
- media_sources_.GetAudioSourceBySsrc(ssrc, &source);
+ StreamParams stream;
+ recv_streams_.GetAudioStreamBySsrc(ssrc, &stream);
SignalSpeakerMonitor(this, static_cast<Session *>(monitor->session()),
- source);
+ stream);
}
void Call::OnConnectionMonitor(VideoChannel *channel,
@@ -582,92 +594,168 @@
SignalSessionError(this, static_cast<Session *>(session), error);
}
-void Call::OnSessionInfo(Session *session,
- const buzz::XmlElement* action_elem) {
- // We have a different list of "updates" because we only want to
- // signal the sources that were added or removed. We want to filter
- // out un-changed sources.
- cricket::MediaSources updates;
+void Call::OnSessionInfoMessage(Session *session,
+ const buzz::XmlElement* action_elem) {
+ if (!IsJingleViewRequest(action_elem)) {
+ return;
+ }
- if (IsSourcesNotify(action_elem)) {
- MediaSources sources;
- ParseError error;
- if (!ParseSourcesNotify(action_elem, session->remote_description(),
- &sources, &error)) {
- // TODO: Is there a way we can signal an IQ error
- // back to the sender?
- LOG(LS_WARNING) << "Invalid sources notify message: " << error.text;
- return;
- }
+ ViewRequest view_request;
+ ParseError error;
+ if (!ParseJingleViewRequest(action_elem, &view_request, &error)) {
+ LOG(LS_WARNING) << "Failed to parse view request: " << error.text;
+ return;
+ }
- NamedSources::iterator it;
- for (it = sources.mutable_audio()->begin();
- it != sources.mutable_audio()->end(); ++it) {
- bool found = false;
- NamedSource found_source;
- if (it->ssrc_set) {
- found = media_sources_.GetAudioSourceBySsrc(it->ssrc, &found_source);
- } else {
- // For backwards compatibility, we remove by nick.
- // TODO: Remove once all senders use explicit remove by ssrc.
- found = media_sources_.GetFirstAudioSourceByNick(it->nick,
- &found_source);
- if (found) {
- it->SetSsrc(found_source.ssrc);
- it->removed = true;
- } else {
- continue; // No ssrc to remove.
- }
- }
- if (it->removed && found) {
- RemoveVoiceStream(session, found_source.ssrc);
- media_sources_.RemoveAudioSourceBySsrc(it->ssrc);
- updates.mutable_audio()->push_back(*it);
- LOG(LS_INFO) << "Removed voice stream: " << found_source.ssrc;
- } else if (!it->removed && !found) {
- AddVoiceStream(session, it->ssrc);
- media_sources_.AddAudioSource(*it);
- updates.mutable_audio()->push_back(*it);
- LOG(LS_INFO) << "Added voice stream: " << it->ssrc;
- }
- }
- for (it = sources.mutable_video()->begin();
- it != sources.mutable_video()->end(); ++it) {
- bool found = false;
- NamedSource found_source;
- if (it->ssrc_set) {
- found = media_sources_.GetVideoSourceBySsrc(it->ssrc, &found_source);
- } else {
- // For backwards compatibility, we remove by nick.
- // TODO: Remove once all senders use explicit remove by ssrc.
- found = media_sources_.GetFirstVideoSourceByNick(it->nick,
- &found_source);
- if (found) {
- it->SetSsrc(found_source.ssrc);
- it->removed = true;
- } else {
- continue; // No ssrc to remove.
- }
- }
- if (it->removed && found) {
- RemoveVideoStream(session, found_source.ssrc);
- media_sources_.RemoveVideoSourceBySsrc(it->ssrc);
- updates.mutable_video()->push_back(*it);
- LOG(LS_INFO) << "Removed video stream: " << found_source.ssrc;
- } else if (!it->removed && !found) {
- AddVideoStream(session, it->ssrc);
- media_sources_.AddVideoSource(*it);
- updates.mutable_video()->push_back(*it);
- LOG(LS_INFO) << "Added video stream: " << it->ssrc;
- }
- }
+ if (!SetSendResolutions(session, view_request)) {
+ LOG(LS_WARNING) << "Failed to set send resolutions.";
+ }
+}
- if (!updates.audio().empty() || !updates.video().empty()) {
- SignalMediaSourcesUpdate(this, session, updates);
+bool Call::SetSendResolutions(
+ Session *session, const ViewRequest& view_request) {
+ // TODO: Change resolution depending on ssrc. For now,
+ // assume we have the same resolution for all, and change by setting
+ // the remote content to change the codecs, which changes the
+ // resolution.
+ //
+ // Eventually it should look something like this:
+ // VideoChannel *video_channel = GetVideoChannel(session);
+ // for (StaticVideoViews::const_iterator view =
+ // view_request.static_video_views.begin();
+ // view != view_request.static_video_views.end(); ++view) {
+ // if (!video_channel->SetResolution(
+ // view->ssrc, view->width, view->height, view->framerate)) {
+ // LOG(LS_WARNING) <<
+ // "Failed to set view request resolution of ssrc " << view->ssrc;
+ // }
+ // }
+
+ VideoChannel *video_channel = GetVideoChannel(session);
+ if (video_channel == NULL) {
+ return false;
+ }
+
+ // If there are no views, set resolution to 0x0x0.
+ StaticVideoView target_view(0U, 0, 0, 0);
+ if (!view_request.static_video_views.empty()) {
+ target_view = view_request.static_video_views[0];
+ }
+ VideoContentDescription* target_video = new VideoContentDescription();
+ const VideoContentDescription* current_video =
+ GetFirstVideoContentDescription(session->remote_description());
+ for (VideoCodecs::const_iterator current_codec =
+ current_video->codecs().begin();
+ current_codec != current_video->codecs().end(); ++current_codec) {
+ VideoCodec target_codec = *current_codec;
+ target_codec.width = target_view.width;
+ target_codec.height = target_view.height;
+ target_codec.framerate = target_view.framerate;
+ target_video->AddCodec(target_codec);
+ }
+ return video_channel->SetRemoteContent(target_video, CA_UPDATE);
+}
+
+void FindStreamChanges(const std::vector<StreamParams>& streams,
+ const std::vector<StreamParams>& updates,
+ std::vector<StreamParams>* added_streams,
+ std::vector<StreamParams>* removed_streams) {
+ for (std::vector<StreamParams>::const_iterator update = updates.begin();
+ update != updates.end(); ++update) {
+ StreamParams stream;
+ if (GetStreamByNickAndName(streams, update->nick, update->name, &stream)) {
+ if (!update->has_ssrcs()) {
+ removed_streams->push_back(stream);
+ }
+ } else {
+ added_streams->push_back(*update);
}
}
}
+void Call::OnRemoteDescriptionUpdate(BaseSession *base_session,
+ const ContentInfos& updated_contents) {
+ Session* session = static_cast<Session *>(base_session);
+
+ cricket::MediaStreams added_streams;
+ cricket::MediaStreams removed_streams;
+ std::vector<StreamParams>::const_iterator stream;
+
+ const ContentInfo* audio_content = GetFirstAudioContent(updated_contents);
+ if (audio_content) {
+ const AudioContentDescription* audio_update =
+ static_cast<const AudioContentDescription*>(audio_content->description);
+ if (!audio_update->codecs().empty()) {
+ UpdateVoiceChannelRemoteContent(session, audio_update);
+ }
+
+ FindStreamChanges(recv_streams_.audio(),
+ audio_update->streams(),
+ added_streams.mutable_audio(),
+ removed_streams.mutable_audio());
+ for (stream = added_streams.audio().begin();
+ stream != added_streams.audio().end();
+ ++stream) {
+ AddAudioRecvStream(session, *stream);
+ }
+ for (stream = removed_streams.audio().begin();
+ stream != removed_streams.audio().end();
+ ++stream) {
+ RemoveAudioRecvStream(session, *stream);
+ }
+ }
+
+ const ContentInfo* video_content = GetFirstVideoContent(updated_contents);
+ if (video_content) {
+ const VideoContentDescription* video_update =
+ static_cast<const VideoContentDescription*>(video_content->description);
+ if (!video_update->codecs().empty()) {
+ UpdateVideoChannelRemoteContent(session, video_update);
+ }
+
+ FindStreamChanges(recv_streams_.video(),
+ video_update->streams(),
+ added_streams.mutable_video(),
+ removed_streams.mutable_video());
+ for (stream = added_streams.video().begin();
+ stream != added_streams.video().end();
+ ++stream) {
+ AddVideoRecvStream(session, *stream);
+ }
+ for (stream = removed_streams.video().begin();
+ stream != removed_streams.video().end();
+ ++stream) {
+ RemoveVideoRecvStream(session, *stream);
+ }
+ }
+
+ if (!added_streams.empty() || !removed_streams.empty()) {
+ SignalMediaStreamsUpdate(this, session, added_streams, removed_streams);
+ }
+}
+
+bool Call::UpdateVoiceChannelRemoteContent(
+ Session* session, const AudioContentDescription* audio) {
+ VoiceChannel *voice_channel = GetVoiceChannel(session);
+ if (!voice_channel->SetRemoteContent(audio, CA_UPDATE)) {
+ LOG(LS_ERROR) << "Failure in audio SetRemoteContent with CA_UPDATE";
+ session->SetError(BaseSession::ERROR_CONTENT);
+ return false;
+ }
+ return true;
+}
+
+bool Call::UpdateVideoChannelRemoteContent(
+ Session* session, const VideoContentDescription* video) {
+ VideoChannel *video_channel = GetVideoChannel(session);
+ if (!video_channel->SetRemoteContent(video, CA_UPDATE)) {
+ LOG(LS_ERROR) << "Failure in video SetRemoteContent with CA_UPDATE";
+ session->SetError(BaseSession::ERROR_CONTENT);
+ return false;
+ }
+ return true;
+}
+
void Call::OnReceivedTerminateReason(Session *session,
const std::string &reason) {
session_client_->session_manager()->signaling_thread()->Clear(this,
diff --git a/talk/session/phone/call.h b/talk/session/phone/call.h
index 357f884..69d2941 100644
--- a/talk/session/phone/call.h
+++ b/talk/session/phone/call.h
@@ -40,6 +40,7 @@
#include "talk/session/phone/currentspeakermonitor.h"
#include "talk/session/phone/mediamessages.h"
#include "talk/session/phone/mediasession.h"
+#include "talk/session/phone/streamparams.h"
namespace cricket {
@@ -61,7 +62,7 @@
void RejectSession(Session *session);
void TerminateSession(Session *session);
void Terminate();
- bool SendViewRequest(Session* session,
+ bool SendViewRequest(Session *session,
const ViewRequest& view_request);
void SetLocalRenderer(VideoRenderer* renderer);
void SetVideoRenderer(Session *session, uint32 ssrc,
@@ -105,23 +106,30 @@
SignalConnectionMonitor;
sigslot::signal2<Call *, const VoiceMediaInfo&> SignalMediaMonitor;
sigslot::signal2<Call *, const AudioInfo&> SignalAudioMonitor;
- // Empty nick on NamedSource means "unknown".
- // Ssrc of 0 on NamedSource means "no current speaker".
+ // Empty nick on StreamParams means "unknown".
+ // No ssrcs in StreamParams means "no current speaker".
sigslot::signal3<Call *,
Session *,
- const NamedSource&> SignalSpeakerMonitor;
+ const StreamParams&> SignalSpeakerMonitor;
sigslot::signal2<Call *, const std::vector<ConnectionInfo> &>
SignalVideoConnectionMonitor;
sigslot::signal2<Call *, const VideoMediaInfo&> SignalVideoMediaMonitor;
- sigslot::signal3<Call *,
+ // Gives added streams and removed streams, in that order.
+ sigslot::signal4<Call *,
Session *,
- const MediaSources&> SignalMediaSourcesUpdate;
+ const MediaStreams&,
+ const MediaStreams&> SignalMediaStreamsUpdate;
private:
void OnMessage(talk_base::Message *message);
void OnSessionState(BaseSession *session, BaseSession::State state);
void OnSessionError(BaseSession *session, Session::Error error);
- void OnSessionInfo(Session *session, const buzz::XmlElement* action_elem);
+ void OnSessionInfoMessage(
+ Session *session, const buzz::XmlElement* action_elem);
+ void OnViewRequest(
+ Session *session, const ViewRequest& view_request);
+ void OnRemoteDescriptionUpdate(
+ BaseSession *session, const ContentInfos& updated_contents);
void OnReceivedTerminateReason(Session *session, const std::string &reason);
void IncomingSession(Session *session, const SessionDescription* offer);
// Returns true on success.
@@ -137,18 +145,25 @@
void OnConnectionMonitor(VideoChannel *channel,
const std::vector<ConnectionInfo> &infos);
void OnMediaMonitor(VideoChannel *channel, const VideoMediaInfo& info);
- VoiceChannel* GetVoiceChannel(Session* session);
- VideoChannel* GetVideoChannel(Session* session);
- void AddVoiceStream(Session *session, uint32 voice_ssrc);
- void AddVideoStream(Session *session, uint32 video_ssrc);
- void RemoveVoiceStream(Session *session, uint32 voice_ssrc);
- void RemoveVideoStream(Session *session, uint32 video_ssrc);
+ VoiceChannel* GetVoiceChannel(Session *session);
+ VideoChannel* GetVideoChannel(Session *session);
+ bool SetSendResolutions(Session *session, const ViewRequest& view_request);
+ bool UpdateVoiceChannelRemoteContent(Session *session,
+ const AudioContentDescription* audio);
+ bool UpdateVideoChannelRemoteContent(Session *session,
+ const VideoContentDescription* video);
+ void AddAudioRecvStream(Session *session, const StreamParams& audio_stream);
+ void AddVideoRecvStream(Session *session, const StreamParams& video_stream);
+ void RemoveAudioRecvStream(
+ Session *session, const StreamParams& audio_stream);
+ void RemoveVideoRecvStream(
+ Session *session, const StreamParams& video_stream);
void ContinuePlayDTMF();
uint32 id_;
MediaSessionClient *session_client_;
std::vector<Session *> sessions_;
- MediaSources media_sources_;
+ MediaStreams recv_streams_;
std::map<std::string, VoiceChannel *> voice_channel_map_;
std::map<std::string, VideoChannel *> video_channel_map_;
std::map<std::string, CurrentSpeakerMonitor *> speaker_monitor_map_;
diff --git a/talk/session/phone/channel.cc b/talk/session/phone/channel.cc
index a58734f..9e39088 100644
--- a/talk/session/phone/channel.cc
+++ b/talk/session/phone/channel.cc
@@ -137,8 +137,6 @@
this, &BaseChannel::OnChannelRead);
session_->SignalState.connect(this, &BaseChannel::OnSessionState);
- session_->SignalRemoteDescriptionUpdate.connect(this,
- &BaseChannel::OnRemoteDescriptionUpdate);
OnSessionState(session(), session()->state());
set_rtcp_transport_channel(rtcp_transport_channel);
@@ -450,16 +448,6 @@
}
}
-void BaseChannel::OnRemoteDescriptionUpdate(BaseSession* session) {
- const MediaContentDescription* content =
- GetFirstContent(session->remote_description());
-
- if (content && !SetRemoteContent(content, CA_UPDATE)) {
- LOG(LS_ERROR) << "Failure in SetRemoteContent with CA_UPDATE";
- session->SetError(BaseSession::ERROR_CONTENT);
- }
-}
-
void BaseChannel::EnableMedia_w() {
ASSERT(worker_thread_ == talk_base::Thread::Current());
if (enabled_)
@@ -713,16 +701,12 @@
ContentAction action) {
bool ret = UpdateLocalStreams_w(content->streams(), action);
// Set local SRTP parameters (what we will encrypt with).
- if (ret) {
- ret = SetSrtp_w(content->cryptos(), action, CS_LOCAL);
- }
+ ret &= SetSrtp_w(content->cryptos(), action, CS_LOCAL);
// Set local RTCP mux parameters.
- if (ret) {
- ret = SetRtcpMux_w(content->rtcp_mux(), action, CS_LOCAL);
- }
+ ret &= SetRtcpMux_w(content->rtcp_mux(), action, CS_LOCAL);
// Set local RTP header extensions.
- if (ret && content->rtp_header_extensions_set()) {
- ret = media_channel()->SetRecvRtpHeaderExtensions(
+ if (content->rtp_header_extensions_set()) {
+ ret &= media_channel()->SetRecvRtpHeaderExtensions(
content->rtp_header_extensions());
}
return ret;
@@ -732,17 +716,12 @@
ContentAction action) {
bool ret = UpdateRemoteStreams_w(content->streams(), action);
// Set remote SRTP parameters (what the other side will encrypt with).
- if (ret) {
- ret = SetSrtp_w(content->cryptos(), action, CS_REMOTE);
- }
+ ret &= SetSrtp_w(content->cryptos(), action, CS_REMOTE);
// Set remote RTCP mux parameters.
- if (ret) {
- ret = SetRtcpMux_w(content->rtcp_mux(), action, CS_REMOTE);
- }
-
+ ret &= SetRtcpMux_w(content->rtcp_mux(), action, CS_REMOTE);
// Set remote RTP header extensions.
- if (ret && content->rtp_header_extensions_set()) {
- ret = media_channel()->SetSendRtpHeaderExtensions(
+ if (content->rtp_header_extensions_set()) {
+ ret &= media_channel()->SetSendRtpHeaderExtensions(
content->rtp_header_extensions());
}
return ret;
@@ -995,12 +974,14 @@
const AudioContentDescription* audio =
static_cast<const AudioContentDescription*>(content);
ASSERT(audio != NULL);
+ if (!audio) return false;
bool ret = SetBaseLocalContent_w(content, action);
-
// Set local audio codecs (what we want to receive).
- if (ret) {
- ret = media_channel()->SetRecvCodecs(audio->codecs());
+ // TODO: Change action != CA_UPDATE to !audio->partial() when partial
+ // is set properly.
+ if (action != CA_UPDATE || audio->has_codecs()) {
+ ret &= media_channel()->SetRecvCodecs(audio->codecs());
}
// If everything worked, see if we can start receiving.
@@ -1021,25 +1002,29 @@
const AudioContentDescription* audio =
static_cast<const AudioContentDescription*>(content);
ASSERT(audio != NULL);
+ if (!audio) return false;
+ bool ret = true;
// Set remote video codecs (what the other side wants to receive).
- bool ret = media_channel()->SetSendCodecs(audio->codecs());
-
- if (ret) {
- ret = SetBaseRemoteContent_w(content, action);
+ if (action != CA_UPDATE || audio->has_codecs()) {
+ ret &= media_channel()->SetSendCodecs(audio->codecs());
}
- // Tweak our audio processing settings, if needed.
- int audio_options = 0;
- if (audio->conference_mode()) {
- audio_options |= OPT_CONFERENCE;
- }
- if (audio->agc_minus_10db()) {
- audio_options |= OPT_AGC_MINUS_10DB;
- }
- 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";
+ ret &= SetBaseRemoteContent_w(content, action);
+
+ if (action != CA_UPDATE) {
+ // Tweak our audio processing settings, if needed.
+ int audio_options = 0;
+ if (audio->conference_mode()) {
+ audio_options |= OPT_CONFERENCE;
+ }
+ if (audio->agc_minus_10db()) {
+ audio_options |= OPT_AGC_MINUS_10DB;
+ }
+ 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";
+ }
}
// If everything worked, see if we can start sending.
@@ -1305,12 +1290,12 @@
const VideoContentDescription* video =
static_cast<const VideoContentDescription*>(content);
ASSERT(video != NULL);
+ if (!video) return false;
bool ret = SetBaseLocalContent_w(content, action);
-
// Set local video codecs (what we want to receive).
- if (ret) {
- ret = media_channel()->SetRecvCodecs(video->codecs());
+ if (action != CA_UPDATE || video->has_codecs()) {
+ ret &= media_channel()->SetRecvCodecs(video->codecs());
}
// If everything worked, see if we can start receiving.
@@ -1331,28 +1316,32 @@
const VideoContentDescription* video =
static_cast<const VideoContentDescription*>(content);
ASSERT(video != NULL);
+ if (!video) return false;
+ bool ret = true;
// Set remote video codecs (what the other side wants to receive).
- bool ret = media_channel()->SetSendCodecs(video->codecs());
+ if (action != CA_UPDATE || video->has_codecs()) {
+ ret &= media_channel()->SetSendCodecs(video->codecs());
+ }
- if (ret) {
- ret = SetBaseRemoteContent_w(content, action);
- }
- // Tweak our video processing settings, if needed.
- int video_options = 0;
- if (video->conference_mode()) {
- video_options |= OPT_CONFERENCE;
- }
- if (!media_channel()->SetOptions(video_options)) {
- // Log an error on failure, but don't abort the call.
- LOG(LS_ERROR) << "Failed to set video channel options";
- }
- // Set bandwidth parameters (what the other side wants to get, default=auto)
- if (ret) {
+ ret &= SetBaseRemoteContent_w(content, action);
+
+ if (action != CA_UPDATE) {
+ // Tweak our video processing settings, if needed.
+ int video_options = 0;
+ if (video->conference_mode()) {
+ video_options |= OPT_CONFERENCE;
+ }
+ if (!media_channel()->SetOptions(video_options)) {
+ // Log an error on failure, but don't abort the call.
+ LOG(LS_ERROR) << "Failed to set video channel options";
+ }
+ // Set bandwidth parameters (what the other side wants to get, default=auto)
int bandwidth_bps = video->bandwidth();
bool auto_bandwidth = (bandwidth_bps == kAutoBandwidth);
- ret = media_channel()->SetSendBandwidth(auto_bandwidth, bandwidth_bps);
+ ret &= media_channel()->SetSendBandwidth(auto_bandwidth, bandwidth_bps);
}
+
// If everything worked, see if we can start sending.
if (ret) {
set_has_remote_content(true);
diff --git a/talk/session/phone/channel.h b/talk/session/phone/channel.h
index f262e00..42f3969 100644
--- a/talk/session/phone/channel.h
+++ b/talk/session/phone/channel.h
@@ -168,6 +168,13 @@
SsrcMuxFilter* ssrc_filter() { return &ssrc_filter_; }
+ const std::vector<StreamParams>& local_streams() const {
+ return local_streams_;
+ }
+ const std::vector<StreamParams>& remote_streams() const {
+ return remote_streams_;
+ }
+
protected:
MediaEngineInterface* media_engine() const { return media_engine_; }
virtual MediaChannel* media_channel() const { return media_channel_; }
@@ -208,7 +215,6 @@
// Setting the send codec based on the remote description.
void OnSessionState(BaseSession* session, BaseSession::State state);
- void OnRemoteDescriptionUpdate(BaseSession* session);
void EnableMedia_w();
void DisableMedia_w();
diff --git a/talk/session/phone/channel_unittest.cc b/talk/session/phone/channel_unittest.cc
index b53cca0..a134f45 100644
--- a/talk/session/phone/channel_unittest.cc
+++ b/talk/session/phone/channel_unittest.cc
@@ -458,16 +458,19 @@
media_channel1_->codecs()[0]));
// Now update with other codecs.
typename T::Content update_content;
+ update_content.set_partial(true);
CreateContent(0, kIsacCodec, kH264SvcCodec, &update_content);
EXPECT_TRUE(channel1_->SetRemoteContent(&update_content, CA_UPDATE));
ASSERT_EQ(1U, media_channel1_->codecs().size());
EXPECT_TRUE(CodecMatches(update_content.codecs()[0],
media_channel1_->codecs()[0]));
-
- // Now update without any codec.
+ // Now update without any codecs. This is ignored.
typename T::Content empty_content;
+ empty_content.set_partial(true);
EXPECT_TRUE(channel1_->SetRemoteContent(&empty_content, CA_UPDATE));
- ASSERT_EQ(0U, media_channel1_->codecs().size());
+ ASSERT_EQ(1U, media_channel1_->codecs().size());
+ EXPECT_TRUE(CodecMatches(update_content.codecs()[0],
+ media_channel1_->codecs()[0]));
}
// Test that Add/RemoveStream properly forward to the media channel.
@@ -518,9 +521,9 @@
// Update the local streams by adding another sending stream.
// Use a partial updated session description.
typename T::Content content2;
- CreateContent(0, kPcmuCodec, kH264Codec, &content2);
content2.AddStream(stream2);
content2.AddStream(stream3);
+ content2.set_partial(true);
EXPECT_TRUE(channel1_->SetLocalContent(&content2, CA_UPDATE));
ASSERT_EQ(3u, media_channel1_->send_streams().size());
EXPECT_EQ(stream1, media_channel1_->send_streams()[0]);
@@ -530,10 +533,9 @@
// Update the local streams by removing the first sending stream.
// This is done by removing all SSRCS for this particular stream.
typename T::Content content3;
- CreateContent(0, kPcmuCodec, kH264Codec, &content3);
stream1.ssrcs.clear();
content3.AddStream(stream1);
-
+ content3.set_partial(true);
EXPECT_TRUE(channel1_->SetLocalContent(&content3, CA_UPDATE));
ASSERT_EQ(2u, media_channel1_->send_streams().size());
EXPECT_EQ(stream2, media_channel1_->send_streams()[0]);
@@ -570,15 +572,16 @@
EXPECT_EQ(0u, media_channel1_->recv_streams().size());
EXPECT_TRUE(channel1_->SetRemoteContent(&content1, CA_OFFER));
+ ASSERT_EQ(1u, media_channel1_->codecs().size());
ASSERT_EQ(1u, media_channel1_->recv_streams().size());
EXPECT_EQ(stream1, media_channel1_->recv_streams()[0]);
- // Update the local streams by adding another sending stream.
+ // Update the remote streams by adding another sending stream.
// Use a partial updated session description.
typename T::Content content2;
- CreateContent(0, kPcmuCodec, kH264Codec, &content2);
content2.AddStream(stream2);
content2.AddStream(stream3);
+ content2.set_partial(true);
EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_UPDATE));
ASSERT_EQ(3u, media_channel1_->recv_streams().size());
EXPECT_EQ(stream1, media_channel1_->recv_streams()[0]);
@@ -588,10 +591,9 @@
// Update the remote streams by removing the first stream.
// This is done by removing all SSRCS for this particular stream.
typename T::Content content3;
- CreateContent(0, kPcmuCodec, kH264Codec, &content3);
stream1.ssrcs.clear();
content3.AddStream(stream1);
-
+ content3.set_partial(true);
EXPECT_TRUE(channel1_->SetRemoteContent(&content3, CA_UPDATE));
ASSERT_EQ(2u, media_channel1_->recv_streams().size());
EXPECT_EQ(stream2, media_channel1_->recv_streams()[0]);
diff --git a/talk/session/phone/currentspeakermonitor.cc b/talk/session/phone/currentspeakermonitor.cc
index 1ca6bd9..a148b7a 100644
--- a/talk/session/phone/currentspeakermonitor.cc
+++ b/talk/session/phone/currentspeakermonitor.cc
@@ -56,8 +56,8 @@
if (!started_) {
call_->SignalAudioMonitor.connect(
this, &CurrentSpeakerMonitor::OnAudioMonitor);
- call_->SignalMediaSourcesUpdate.connect(
- this, &CurrentSpeakerMonitor::OnMediaSourcesUpdate);
+ call_->SignalMediaStreamsUpdate.connect(
+ this, &CurrentSpeakerMonitor::OnMediaStreamsUpdate);
started_ = true;
}
@@ -66,7 +66,7 @@
void CurrentSpeakerMonitor::Stop() {
if (started_) {
call_->SignalAudioMonitor.disconnect(this);
- call_->SignalMediaSourcesUpdate.disconnect(this);
+ call_->SignalMediaStreamsUpdate.disconnect(this);
started_ = false;
ssrc_to_speaking_state_map_.clear();
@@ -187,21 +187,20 @@
}
}
-void CurrentSpeakerMonitor::OnMediaSourcesUpdate(Call* call,
+void CurrentSpeakerMonitor::OnMediaStreamsUpdate(Call* call,
Session* session,
- const MediaSources& sources) {
+ const MediaStreams& added,
+ const MediaStreams& removed) {
if (call == call_ && session == session_) {
- // Update the speaking state map based on new or removed sources.
- NamedSources::const_iterator it;
- for (it = sources.audio().begin(); it != sources.audio().end(); it++) {
- if (it->ssrc_set) {
- if (it->removed) {
- ssrc_to_speaking_state_map_.erase(it->ssrc);
- } else if (ssrc_to_speaking_state_map_.find(it->ssrc) ==
- ssrc_to_speaking_state_map_.begin()) {
- ssrc_to_speaking_state_map_[it->ssrc] = SS_NOT_SPEAKING;
- }
- }
+ // Update the speaking state map based on added and removed streams.
+ for (std::vector<cricket::StreamParams>::const_iterator
+ it = removed.video().begin(); it != removed.video().end(); ++it) {
+ ssrc_to_speaking_state_map_.erase(it->first_ssrc());
+ }
+
+ for (std::vector<cricket::StreamParams>::const_iterator
+ it = added.video().begin(); it != added.video().end(); ++it) {
+ ssrc_to_speaking_state_map_[it->first_ssrc()] = SS_NOT_SPEAKING;
}
}
}
diff --git a/talk/session/phone/currentspeakermonitor.h b/talk/session/phone/currentspeakermonitor.h
index 84207fb..7871a82 100644
--- a/talk/session/phone/currentspeakermonitor.h
+++ b/talk/session/phone/currentspeakermonitor.h
@@ -42,7 +42,7 @@
class Call;
class Session;
struct AudioInfo;
-struct MediaSources;
+struct MediaStreams;
// Note that the call's audio monitor must be started before this is started.
// It's recommended that the audio monitor be started with a 100 ms period.
@@ -68,9 +68,10 @@
private:
void OnAudioMonitor(Call* call, const AudioInfo& info);
- void OnMediaSourcesUpdate(Call* call,
+ void OnMediaStreamsUpdate(Call* call,
Session* session,
- const MediaSources& sources);
+ const MediaStreams& added,
+ const MediaStreams& removed);
// These are states that a participant will pass through so that we gradually
// recognize that they have started and stopped speaking. This avoids
diff --git a/talk/session/phone/fakewebrtccommon.h b/talk/session/phone/fakewebrtccommon.h
index 1ade630..a960e8b 100644
--- a/talk/session/phone/fakewebrtccommon.h
+++ b/talk/session/phone/fakewebrtccommon.h
@@ -42,6 +42,9 @@
#define WEBRTC_FUNC_CONST(method, args) \
virtual int method args const
+#define WEBRTC_BOOL_FUNC(method, args) \
+ virtual bool method args
+
#define WEBRTC_CHECK_CHANNEL(channel) \
if (channels_.find(channel) == channels_.end()) return -1;
diff --git a/talk/session/phone/fakewebrtcvideoengine.h b/talk/session/phone/fakewebrtcvideoengine.h
index 3833a44..cbac785 100644
--- a/talk/session/phone/fakewebrtcvideoengine.h
+++ b/talk/session/phone/fakewebrtcvideoengine.h
@@ -84,7 +84,10 @@
rtcp_status_(webrtc::kRtcpNone),
key_frame_request_method_(webrtc::kViEKeyFrameRequestNone),
tmmbr_(false),
- nack_(false) {
+ remb_send_(false),
+ remb_(false),
+ nack_(false),
+ hybrid_nack_fec_(false) {
memset(&send_codec, 0, sizeof(send_codec));
}
int capture_id_;
@@ -96,7 +99,10 @@
webrtc::ViERTCPMode rtcp_status_;
webrtc::ViEKeyFrameRequestMethod key_frame_request_method_;
bool tmmbr_;
+ bool remb_send_; // This channel send REMB packets.
+ bool remb_; // This channel report BWE using remb.
bool nack_;
+ bool hybrid_nack_fec_;
std::vector<webrtc::VideoCodec> recv_codecs;
webrtc::VideoCodec send_codec;
};
@@ -186,10 +192,22 @@
WEBRTC_ASSERT_CHANNEL(channel);
return channels_.find(channel)->second->tmmbr_;
}
+ bool GetRembStatus(int channel) const {
+ WEBRTC_ASSERT_CHANNEL(channel);
+ return channels_.find(channel)->second->remb_;
+ }
+ bool GetRembStatusSend(int channel) const {
+ WEBRTC_ASSERT_CHANNEL(channel);
+ return channels_.find(channel)->second->remb_send_;
+ }
bool GetNackStatus(int channel) const {
WEBRTC_ASSERT_CHANNEL(channel);
return channels_.find(channel)->second->nack_;
}
+ bool GetHybridNackFecStatus(int channel) const {
+ WEBRTC_ASSERT_CHANNEL(channel);
+ return channels_.find(channel)->second->hybrid_nack_fec_;
+ }
bool ReceiveCodecRegistered(int channel,
const webrtc::VideoCodec& codec) const {
@@ -258,9 +276,12 @@
const cricket::VideoCodec& c(*codecs_[list_number]);
if ("I420" == c.name) {
out_codec.codecType = webrtc::kVideoCodecI420;
- }
- else if ("VP8" == c.name) {
+ } else if ("VP8" == c.name) {
out_codec.codecType = webrtc::kVideoCodecVP8;
+ } else if ("red" == c.name) {
+ out_codec.codecType = webrtc::kVideoCodecRED;
+ } else if ("ulpfec" == c.name) {
+ out_codec.codecType = webrtc::kVideoCodecULPFEC;
} else {
out_codec.codecType = webrtc::kVideoCodecUnknown;
}
@@ -493,18 +514,18 @@
channels_[channel]->rtcp_status_ = mode;
return 0;
}
- WEBRTC_STUB(GetRTCPStatus, (const int, webrtc::ViERTCPMode&));
+ WEBRTC_STUB_CONST(GetRTCPStatus, (const int, webrtc::ViERTCPMode&));
WEBRTC_FUNC(SetRTCPCName, (const int channel,
const char rtcp_cname[KMaxRTCPCNameLength])) {
WEBRTC_CHECK_CHANNEL(channel);
channels_[channel]->cname_.assign(rtcp_cname);
return 0;
}
- WEBRTC_FUNC(GetRTCPCName, (const int channel,
- char rtcp_cname[KMaxRTCPCNameLength])) {
+ WEBRTC_FUNC_CONST(GetRTCPCName, (const int channel,
+ char rtcp_cname[KMaxRTCPCNameLength])) {
WEBRTC_CHECK_CHANNEL(channel);
talk_base::strcpyn(rtcp_cname, KMaxRTCPCNameLength,
- channels_[channel]->cname_.c_str());
+ channels_.find(channel)->second->cname_.c_str());
return 0;
}
WEBRTC_STUB_CONST(GetRemoteRTCPCName, (const int, char*));
@@ -513,12 +534,23 @@
WEBRTC_FUNC(SetNACKStatus, (const int channel, const bool enable)) {
WEBRTC_CHECK_CHANNEL(channel);
channels_[channel]->nack_ = enable;
+ channels_[channel]->hybrid_nack_fec_ = false;
return 0;
}
WEBRTC_STUB(SetFECStatus, (const int, const bool, const unsigned char,
const unsigned char));
- WEBRTC_STUB(SetHybridNACKFECStatus, (const int, const bool,
- const unsigned char, const unsigned char));
+ WEBRTC_FUNC(SetHybridNACKFECStatus, (const int channel, const bool enable,
+ const unsigned char red_type, const unsigned char fec_type)) {
+ WEBRTC_CHECK_CHANNEL(channel);
+ if (red_type == fec_type ||
+ red_type == channels_[channel]->send_codec.plType ||
+ fec_type == channels_[channel]->send_codec.plType) {
+ return -1;
+ }
+ channels_[channel]->nack_ = false;
+ channels_[channel]->hybrid_nack_fec_ = true;
+ return 0;
+ }
WEBRTC_FUNC(SetKeyFrameRequestMethod,
(const int channel,
const webrtc::ViEKeyFrameRequestMethod method)) {
@@ -526,6 +558,13 @@
channels_[channel]->key_frame_request_method_ = method;
return 0;
}
+ WEBRTC_BOOL_FUNC(SetRembStatus, (int channel, bool send, bool receive)) {
+ if (channels_.find(channel) == channels_.end())
+ return false;
+ channels_[channel]->remb_send_ = send;
+ channels_[channel]->remb_ = receive;
+ return true;
+ }
WEBRTC_FUNC(SetTMMBRStatus, (const int channel, const bool enable)) {
WEBRTC_CHECK_CHANNEL(channel);
channels_[channel]->tmmbr_ = enable;
@@ -542,7 +581,8 @@
WEBRTC_STUB(SetRTPKeepAliveStatus, (const int, bool, const char,
const unsigned int));
- WEBRTC_STUB(GetRTPKeepAliveStatus, (const int, bool&, char&, unsigned int&));
+ WEBRTC_STUB_CONST(GetRTPKeepAliveStatus,
+ (const int, bool&, char&, unsigned int&));
WEBRTC_STUB(StartRTPDump, (const int, const char*, webrtc::RTPDirections));
WEBRTC_STUB(StopRTPDump, (const int, webrtc::RTPDirections));
WEBRTC_STUB(RegisterRTPObserver, (const int, webrtc::ViERTPObserver&));
diff --git a/talk/session/phone/fakewebrtcvoiceengine.h b/talk/session/phone/fakewebrtcvoiceengine.h
index 5b3ff0c..29735f8 100644
--- a/talk/session/phone/fakewebrtcvoiceengine.h
+++ b/talk/session/phone/fakewebrtcvoiceengine.h
@@ -541,7 +541,6 @@
// webrtc::VoENetEqStats
WEBRTC_STUB(GetNetworkStatistics, (int, webrtc::NetworkStatistics&));
- WEBRTC_STUB(GetJitterStatistics, (int, webrtc::JitterStatistics&));
WEBRTC_STUB(GetPreferredBufferSize, (int, short unsigned int&));
WEBRTC_STUB(ResetJitterStatistics, (int));
diff --git a/talk/session/phone/mediaengine.h b/talk/session/phone/mediaengine.h
index 0f42adb..cc77099 100644
--- a/talk/session/phone/mediaengine.h
+++ b/talk/session/phone/mediaengine.h
@@ -32,6 +32,7 @@
#include <CoreAudio/CoreAudio.h>
#endif
+#include <climits>
#include <string>
#include <vector>
@@ -46,6 +47,13 @@
namespace cricket {
+// TODO: For now, a hard-coded ssrc is used as the video ssrc.
+// This is because when the video frame is passed to the mediaprocessor for
+// processing, it doesn't have the correct ssrc. Since currently only Tx
+// Video processing is supported, this is ok. When we switch over to trigger
+// from capturer, this should be fixed and this const removed.
+const uint32 kDummyVideoSsrc = 0xFFFFFFFF;
+
class VideoCapturer;
// MediaEngineInterface is an abstraction of a media engine which can be
@@ -150,7 +158,6 @@
static MediaEngineInterface* Create();
};
-
// CompositeMediaEngine constructs a MediaEngine from separate
// voice and video engine classes.
template<class VOICE, class VIDEO>
diff --git a/talk/session/phone/mediamessages.cc b/talk/session/phone/mediamessages.cc
index c40b03f..3dd6982 100644
--- a/talk/session/phone/mediamessages.cc
+++ b/talk/session/phone/mediamessages.cc
@@ -43,46 +43,10 @@
namespace {
-bool GetFirstSourceByNick(const NamedSources& sources,
- const std::string& nick,
- NamedSource* source_out) {
- for (NamedSources::const_iterator source = sources.begin();
- source != sources.end(); ++source) {
- if (source->nick == nick) {
- *source_out = *source;
- return true;
- }
- }
- return false;
-}
-
-bool GetSourceBySsrc(const NamedSources& sources, uint32 ssrc,
- NamedSource* source_out) {
- for (NamedSources::const_iterator source = sources.begin();
- source != sources.end(); ++source) {
- if (source->ssrc == ssrc) {
- *source_out = *source;
- return true;
- }
- }
- return false;
-}
-
-// NOTE: There is no check here for duplicate sources, so check before
+// NOTE: There is no check here for duplicate streams, so check before
// adding.
-void AddSource(NamedSources* sources, const NamedSource& source) {
- sources->push_back(source);
-}
-
-void RemoveSourceBySsrc(uint32 ssrc, NamedSources* sources) {
- for (NamedSources::iterator source = sources->begin();
- source != sources->end(); ) {
- if (source->ssrc == ssrc) {
- source = sources->erase(source);
- } else {
- ++source;
- }
- }
+void AddStream(std::vector<StreamParams>* streams, const StreamParams& stream) {
+ streams->push_back(stream);
}
bool ParseSsrc(const std::string& string, uint32* ssrc) {
@@ -96,29 +60,6 @@
return ParseSsrc(element->BodyText(), ssrc);
}
-bool ParseNamedSource(const buzz::XmlElement* source_elem,
- NamedSource* named_source,
- ParseError* error) {
- named_source->nick = source_elem->Attr(QN_NICK);
- if (named_source->nick.empty()) {
- return BadParse("Missing or invalid nick.", error);
- }
-
- named_source->name = source_elem->Attr(QN_NAME);
-
- const buzz::XmlElement* ssrc_elem =
- source_elem->FirstNamed(QN_JINGLE_DRAFT_SSRC);
- if (ssrc_elem != NULL && !ssrc_elem->BodyText().empty()) {
- uint32 ssrc;
- if (!ParseSsrc(ssrc_elem->BodyText(), &ssrc)) {
- return BadParse("Missing or invalid ssrc.", error);
- }
- named_source->SetSsrc(ssrc);
- }
-
- return true;
-}
-
// Builds a <view> element according to the following spec:
// goto/jinglemuc
buzz::XmlElement* CreateViewElem(const std::string& name,
@@ -157,47 +98,49 @@
} // namespace
-bool MediaSources::GetFirstAudioSourceByNick(
- const std::string& nick, NamedSource* source) {
- return GetFirstSourceByNick(audio_, nick, source);
+bool MediaStreams::GetAudioStreamByNickAndName(
+ const std::string& nick, const std::string& name, StreamParams* stream) {
+ return GetStreamByNickAndName(audio_, nick, name, stream);
}
-bool MediaSources::GetFirstVideoSourceByNick(
- const std::string& nick, NamedSource* source) {
- return GetFirstSourceByNick(video_, nick, source);
+bool MediaStreams::GetVideoStreamByNickAndName(
+ const std::string& nick, const std::string& name, StreamParams* stream) {
+ return GetStreamByNickAndName(video_, nick, name, stream);
}
-void MediaSources::CopyFrom(const MediaSources& sources) {
- audio_ = sources.audio_;
- video_ = sources.video_;
+void MediaStreams::CopyFrom(const MediaStreams& streams) {
+ audio_ = streams.audio_;
+ video_ = streams.video_;
}
-bool MediaSources::GetAudioSourceBySsrc(uint32 ssrc, NamedSource* source) {
- return GetSourceBySsrc(audio_, ssrc, source);
+bool MediaStreams::GetAudioStreamBySsrc(uint32 ssrc, StreamParams* stream) {
+ return GetStreamBySsrc(audio_, ssrc, stream);
}
-bool MediaSources::GetVideoSourceBySsrc(uint32 ssrc, NamedSource* source) {
- return GetSourceBySsrc(video_, ssrc, source);
+bool MediaStreams::GetVideoStreamBySsrc(uint32 ssrc, StreamParams* stream) {
+ return GetStreamBySsrc(video_, ssrc, stream);
}
-void MediaSources::AddAudioSource(const NamedSource& source) {
- AddSource(&audio_, source);
+void MediaStreams::AddAudioStream(const StreamParams& stream) {
+ AddStream(&audio_, stream);
}
-void MediaSources::AddVideoSource(const NamedSource& source) {
- AddSource(&video_, source);
+void MediaStreams::AddVideoStream(const StreamParams& stream) {
+ AddStream(&video_, stream);
}
-void MediaSources::RemoveAudioSourceBySsrc(uint32 ssrc) {
- RemoveSourceBySsrc(ssrc, &audio_);
+void MediaStreams::RemoveAudioStreamByNickAndName(
+ const std::string& nick, const std::string& name) {
+ RemoveStreamByNickAndName(&audio_, nick, name);
}
-void MediaSources::RemoveVideoSourceBySsrc(uint32 ssrc) {
- RemoveSourceBySsrc(ssrc, &video_);
+void MediaStreams::RemoveVideoStreamByNickAndName(
+ const std::string& nick, const std::string& name) {
+ RemoveStreamByNickAndName(&video_, nick, name);
}
-bool IsJingleViewRequest(const XmlElements& action_elems) {
- return GetXmlElement(action_elems, QN_JINGLE_DRAFT_VIEW) != NULL;
+bool IsJingleViewRequest(const buzz::XmlElement* action_elem) {
+ return action_elem->FirstNamed(QN_JINGLE_DRAFT_VIEW) != NULL;
}
bool ParseStaticVideoView(const buzz::XmlElement* view_elem,
@@ -221,26 +164,25 @@
return true;
}
-bool ParseJingleViewRequest(const XmlElements& action_elems,
+bool ParseJingleViewRequest(const buzz::XmlElement* action_elem,
ViewRequest* view_request,
ParseError* error) {
- for (XmlElements::const_iterator iter = action_elems.begin();
- iter != action_elems.end(); ++iter) {
- const buzz::XmlElement* view_elem = *iter;
- if (view_elem->Name() == QN_JINGLE_DRAFT_VIEW) {
- std::string type = view_elem->Attr(QN_TYPE);
- if (STR_JINGLE_DRAFT_VIEW_TYPE_NONE == type) {
- view_request->static_video_views.clear();
- return true;
- } else if (STR_JINGLE_DRAFT_VIEW_TYPE_STATIC == type) {
- StaticVideoView static_video_view(0, 0, 0, 0);
- if (!ParseStaticVideoView(view_elem, &static_video_view, error)) {
- return false;
- }
- view_request->static_video_views.push_back(static_video_view);
- } else {
- LOG(LS_INFO) << "Ingnoring unknown view type: " << type;
+ for (const buzz::XmlElement* view_elem =
+ action_elem->FirstNamed(QN_JINGLE_DRAFT_VIEW);
+ view_elem != NULL;
+ view_elem = view_elem->NextNamed(QN_JINGLE_DRAFT_VIEW)) {
+ std::string type = view_elem->Attr(QN_TYPE);
+ if (STR_JINGLE_DRAFT_VIEW_TYPE_NONE == type) {
+ view_request->static_video_views.clear();
+ return true;
+ } else if (STR_JINGLE_DRAFT_VIEW_TYPE_STATIC == type) {
+ StaticVideoView static_video_view(0, 0, 0, 0);
+ if (!ParseStaticVideoView(view_elem, &static_video_view, error)) {
+ return false;
}
+ view_request->static_video_views.push_back(static_video_view);
+ } else {
+ LOG(LS_INFO) << "Ingnoring unknown view type: " << type;
}
}
return true;
@@ -262,48 +204,6 @@
return true;
}
-bool IsSourcesNotify(const buzz::XmlElement* action_elem) {
- return action_elem->FirstNamed(QN_JINGLE_LEGACY_NOTIFY) != NULL;
-}
-
-bool ParseSourcesNotify(const buzz::XmlElement* action_elem,
- const SessionDescription* session_description,
- MediaSources* sources,
- ParseError* error) {
- for (const buzz::XmlElement* notify_elem =
- action_elem->FirstNamed(QN_JINGLE_LEGACY_NOTIFY);
- notify_elem != NULL;
- notify_elem = notify_elem->NextNamed(QN_JINGLE_LEGACY_NOTIFY)) {
- std::string content_name = notify_elem->Attr(QN_NAME);
- for (const buzz::XmlElement* source_elem =
- notify_elem->FirstNamed(QN_JINGLE_LEGACY_SOURCE);
- source_elem != NULL;
- source_elem = source_elem->NextNamed(QN_JINGLE_LEGACY_SOURCE)) {
- NamedSource named_source;
- if (!ParseNamedSource(source_elem, &named_source, error)) {
- return false;
- }
-
- if (session_description == NULL) {
- return BadParse("Unknown content name: " + content_name, error);
- }
- const ContentInfo* content =
- FindContentInfoByName(session_description->contents(), content_name);
- if (content == NULL) {
- return BadParse("Unknown content name: " + content_name, error);
- }
-
- if (IsAudioContent(content)) {
- sources->mutable_audio()->push_back(named_source);
- } else if (IsVideoContent(content)) {
- sources->mutable_video()->push_back(named_source);
- }
- }
- }
-
- return true;
-}
-
bool ParseSsrcAsLegacyStream(const buzz::XmlElement* desc_elem,
std::vector<StreamParams>* streams,
ParseError* error) {
diff --git a/talk/session/phone/mediamessages.h b/talk/session/phone/mediamessages.h
index 11b9e89..8a8a6de 100644
--- a/talk/session/phone/mediamessages.h
+++ b/talk/session/phone/mediamessages.h
@@ -45,63 +45,50 @@
namespace cricket {
-struct StreamParams;
+class StreamParams;
-// In a <notify> message, there are number of sources with names.
-// This represents one such named source.
-struct NamedSource {
- NamedSource() : ssrc(0), ssrc_set(false), removed(false) {}
+// A collection of audio and video streams. Most of the methods are
+// merely for convenience. Many of these methods are keyed by ssrc,
+// which is the source identifier in the RTP spec
+// (http://tools.ietf.org/html/rfc3550).
+struct MediaStreams {
+ public:
+ MediaStreams() {}
+ void CopyFrom(const MediaStreams& sources);
- void SetSsrc(uint32 ssrc) {
- this->ssrc = ssrc;
- this->ssrc_set = true;
+ bool empty() const {
+ return audio_.empty() && video_.empty();
}
- std::string nick;
- std::string name;
- uint32 ssrc;
- bool ssrc_set;
- bool removed;
-};
+ std::vector<StreamParams>* mutable_audio() { return &audio_; }
+ std::vector<StreamParams>* mutable_video() { return &video_; }
+ const std::vector<StreamParams>& audio() const { return audio_; }
+ const std::vector<StreamParams>& video() const { return video_; }
-// TODO: Remove this, according to c++ readability.
-typedef std::vector<NamedSource> NamedSources;
-
-// A collection of named audio sources and named video sources, as
-// would be found in a typical <notify> message. Most of the methods
-// are merely for convenience. Many of these methods are keyed by
-// ssrc, which is the source identifier in the RTP spec
-// (http://tools.ietf.org/html/rfc3550).
-struct MediaSources {
- public:
- MediaSources() {}
- void CopyFrom(const MediaSources& sources);
-
- NamedSources* mutable_audio() { return &audio_; }
- NamedSources* mutable_video() { return &video_; }
- const NamedSources& audio() const { return audio_; }
- const NamedSources& video() const { return video_; }
-
+ // Remove the streams with the given name. Names are only unique to
+ // nicks, so you need the nick as well.
+ bool GetAudioStreamByNickAndName(
+ const std::string& nick, const std::string& name, StreamParams* source);
+ bool GetVideoStreamByNickAndName(
+ const std::string& nick, const std::string& name, StreamParams* source);
// Get the source with the given ssrc. Returns true if found.
- bool GetAudioSourceBySsrc(uint32 ssrc, NamedSource* source);
- bool GetVideoSourceBySsrc(uint32 ssrc, NamedSource* source);
- // Get the first source with the given nick. Returns true if found.
- // TODO: Remove the following two methods once all
- // senders use explicit-remove by ssrc.
- bool GetFirstAudioSourceByNick(const std::string& nick, NamedSource* source);
- bool GetFirstVideoSourceByNick(const std::string& nick, NamedSource* source);
+ bool GetAudioStreamBySsrc(uint32 ssrc, StreamParams* source);
+ bool GetVideoStreamBySsrc(uint32 ssrc, StreamParams* source);
// Add a source.
- void AddAudioSource(const NamedSource& source);
- void AddVideoSource(const NamedSource& source);
- // Remove the source with the given ssrc.
- void RemoveAudioSourceBySsrc(uint32 ssrc);
- void RemoveVideoSourceBySsrc(uint32 ssrc);
+ void AddAudioStream(const StreamParams& source);
+ void AddVideoStream(const StreamParams& source);
+ // Remove the source with the given name. Names are only unique to
+ // nicks, so you need the nick as well.
+ void RemoveAudioStreamByNickAndName(const std::string& nick,
+ const std::string& name);
+ void RemoveVideoStreamByNickAndName(const std::string& nick,
+ const std::string& name);
private:
- NamedSources audio_;
- NamedSources video_;
+ std::vector<StreamParams> audio_;
+ std::vector<StreamParams> video_;
- DISALLOW_COPY_AND_ASSIGN(MediaSources);
+ DISALLOW_COPY_AND_ASSIGN(MediaStreams);
};
// In a <view> message, there are a number of views specified. This
@@ -129,12 +116,13 @@
StaticVideoViews static_video_views;
};
-// If the elems of a parent (usually <jingle>) constitute a view request.
-bool IsJingleViewRequest(const XmlElements& elems);
+// If the parent element (usually <jingle>) is a jingle view.
+bool IsJingleViewRequest(const buzz::XmlElement* action_elem);
-// Parses a view request from jingle contents (<view>s). If it
-// fails, returns false and fills an error message.
-bool ParseJingleViewRequest(const XmlElements& elems,
+// Parses a view request from the parent element (usually
+// <jingle>). If it fails, it returns false and fills an error
+// message.
+bool ParseJingleViewRequest(const buzz::XmlElement* action_elem,
ViewRequest* view_request,
ParseError* error);
@@ -149,14 +137,6 @@
// description-info as soon as reflector is capable of sending it.
bool IsSourcesNotify(const buzz::XmlElement* action_elem);
-// Parses a notify message from XML. If it fails, returns false and
-// fills in an error message.
-// The session_description is needed to map content_name => media type.
-bool ParseSourcesNotify(const buzz::XmlElement* action_elem,
- const SessionDescription* session_description,
- MediaSources* sources,
- ParseError* error);
-
// If the given elem has <streams>.
bool HasJingleStreams(const buzz::XmlElement* desc_elem);
diff --git a/talk/session/phone/mediamessages_unittest.cc b/talk/session/phone/mediamessages_unittest.cc
index 0290c02..a76d296 100644
--- a/talk/session/phone/mediamessages_unittest.cc
+++ b/talk/session/phone/mediamessages_unittest.cc
@@ -48,11 +48,6 @@
" type='none'"
"/>";
-static const char kNotifyEmptyXml[] =
- "<notify xmlns='google:jingle'"
- " name='video1'"
- "/>";
-
class MediaMessagesTest : public testing::Test {
public:
// CreateMediaSessionDescription uses a static variable cricket::NS_JINGLE_RTP
@@ -78,55 +73,6 @@
"</view>";
}
- static std::string NotifyAddXml(const std::string& content_name,
- const std::string& nick,
- const std::string& name,
- const std::string& ssrc) {
- return "<notify xmlns='google:jingle'"
- " name='" + content_name + "'"
- ">"
- " <source"
- " nick='" + nick + "'"
- " name='" + name + "'"
- " >"
- " <ssrc>" + ssrc + "</ssrc>"
- " </source>"
- "</notify>";
- }
-
- static std::string NotifyTwoSourceXml(const std::string& name,
- const std::string& nick1,
- const std::string& ssrc1,
- const std::string& nick2,
- const std::string& ssrc2) {
- return "<notify xmlns='google:jingle'"
- " name='" + name + "'"
- ">"
- " <source"
- " nick='" + nick1 + "'"
- " >"
- " <ssrc>" + ssrc1 + "</ssrc>"
- " </source>"
- " <source"
- " nick='" + nick2 + "'"
- " >"
- " <ssrc>" + ssrc2 + "</ssrc>"
- " </source>"
- "</notify>";
- }
-
- static std::string NotifyImplicitRemoveXml(const std::string& content_name,
- const std::string& nick) {
- return "<notify xmlns='google:jingle'"
- " name='" + content_name + "'"
- ">"
- " <source"
- " nick='" + nick + "'"
- " >"
- " </source>"
- "</notify>";
- }
-
static cricket::StreamParams CreateStream(const std::string& nick,
const std::string& name,
uint32 ssrc1,
@@ -197,14 +143,19 @@
// Test serializing/deserializing an empty <view> message.
TEST_F(MediaMessagesTest, ViewNoneToFromXml) {
- talk_base::scoped_ptr<buzz::XmlElement> expected_view_elem(
- buzz::XmlElement::ForStr(kViewVideoNoneXml));
+ buzz::XmlElement* expected_view_elem =
+ buzz::XmlElement::ForStr(kViewVideoNoneXml);
+ talk_base::scoped_ptr<buzz::XmlElement> action_elem(
+ new buzz::XmlElement(QN_JINGLE));
+
+ EXPECT_FALSE(cricket::IsJingleViewRequest(action_elem.get()));
+ action_elem->AddElement(expected_view_elem);
+ EXPECT_TRUE(cricket::IsJingleViewRequest(action_elem.get()));
cricket::ViewRequest view_request;
cricket::XmlElements actual_view_elems;
cricket::WriteError error;
- EXPECT_FALSE(cricket::IsJingleViewRequest(actual_view_elems));
ASSERT_TRUE(cricket::WriteJingleViewRequest(
"video1", view_request, &actual_view_elems, &error));
@@ -212,18 +163,22 @@
EXPECT_EQ(expected_view_elem->Str(), actual_view_elems[0]->Str());
cricket::ParseError parse_error;
- EXPECT_TRUE(cricket::IsJingleViewRequest(actual_view_elems));
+ EXPECT_TRUE(cricket::IsJingleViewRequest(action_elem.get()));
ASSERT_TRUE(cricket::ParseJingleViewRequest(
- actual_view_elems, &view_request, &parse_error));
+ action_elem.get(), &view_request, &parse_error));
EXPECT_EQ(0U, view_request.static_video_views.size());
}
// Test serializing/deserializing an a simple vga <view> message.
TEST_F(MediaMessagesTest, ViewVgaToFromXml) {
- talk_base::scoped_ptr<buzz::XmlElement> expected_view_elem1(
- buzz::XmlElement::ForStr(ViewVideoStaticVgaXml("1234")));
- talk_base::scoped_ptr<buzz::XmlElement> expected_view_elem2(
- buzz::XmlElement::ForStr(ViewVideoStaticVgaXml("2468")));
+ talk_base::scoped_ptr<buzz::XmlElement> action_elem(
+ new buzz::XmlElement(QN_JINGLE));
+ buzz::XmlElement* expected_view_elem1 =
+ buzz::XmlElement::ForStr(ViewVideoStaticVgaXml("1234"));
+ buzz::XmlElement* expected_view_elem2 =
+ buzz::XmlElement::ForStr(ViewVideoStaticVgaXml("2468"));
+ action_elem->AddElement(expected_view_elem1);
+ action_elem->AddElement(expected_view_elem2);
cricket::ViewRequest view_request;
cricket::XmlElements actual_view_elems;
@@ -243,9 +198,9 @@
view_request.static_video_views.clear();
cricket::ParseError parse_error;
- EXPECT_TRUE(cricket::IsJingleViewRequest(actual_view_elems));
+ EXPECT_TRUE(cricket::IsJingleViewRequest(action_elem.get()));
ASSERT_TRUE(cricket::ParseJingleViewRequest(
- actual_view_elems, &view_request, &parse_error));
+ action_elem.get(), &view_request, &parse_error));
EXPECT_EQ(2U, view_request.static_video_views.size());
EXPECT_EQ(1234U, view_request.static_video_views[0].ssrc);
EXPECT_EQ(640, view_request.static_video_views[0].width);
@@ -256,128 +211,18 @@
// Test deserializing bad view XML.
TEST_F(MediaMessagesTest, ParseBadViewXml) {
- talk_base::scoped_ptr<buzz::XmlElement> view_elem(
- buzz::XmlElement::ForStr(ViewVideoStaticVgaXml("not-an-ssrc")));
- XmlElements view_elems;
- view_elems.push_back(view_elem.get());
+ talk_base::scoped_ptr<buzz::XmlElement> action_elem(
+ new buzz::XmlElement(QN_JINGLE));
+ buzz::XmlElement* view_elem =
+ buzz::XmlElement::ForStr(ViewVideoStaticVgaXml("not-an-ssrc"));
+ action_elem->AddElement(view_elem);
cricket::ViewRequest view_request;
cricket::ParseError parse_error;
ASSERT_FALSE(cricket::ParseJingleViewRequest(
- view_elems, &view_request, &parse_error));
+ action_elem.get(), &view_request, &parse_error));
}
-// Test serializing/deserializing an empty session-info message.
-TEST_F(MediaMessagesTest, NotifyFromEmptyXml) {
- talk_base::scoped_ptr<buzz::XmlElement> action_elem(
- new buzz::XmlElement(cricket::QN_JINGLE));
- EXPECT_FALSE(cricket::IsSourcesNotify(action_elem.get()));
-}
-
-// Test serializing/deserializing an empty <notify> message.
-TEST_F(MediaMessagesTest, NotifyEmptyFromXml) {
- talk_base::scoped_ptr<buzz::XmlElement> action_elem(
- new buzz::XmlElement(cricket::QN_JINGLE));
- action_elem->AddElement(
- buzz::XmlElement::ForStr(kNotifyEmptyXml));
-
- cricket::MediaSources sources;
- cricket::ParseError error;
-
- EXPECT_TRUE(cricket::IsSourcesNotify(action_elem.get()));
- ASSERT_TRUE(cricket::ParseSourcesNotify(action_elem.get(),
- remote_description_.get(),
- &sources, &error));
-
- EXPECT_EQ(0U, sources.audio().size());
- EXPECT_EQ(0U, sources.video().size());
-}
-
-// Test serializing/deserializing a complex <notify> message.
-TEST_F(MediaMessagesTest, NotifyFromXml) {
- talk_base::scoped_ptr<buzz::XmlElement> action_elem(
- new buzz::XmlElement(cricket::QN_JINGLE));
- action_elem->AddElement(
- buzz::XmlElement::ForStr(NotifyAddXml(
- "video1", "Joe", "Facetime", "1234")));
- action_elem->AddElement(
- buzz::XmlElement::ForStr(NotifyAddXml(
- "video1", "Bob", "Microsoft Word", "2468")));
- action_elem->AddElement(
- buzz::XmlElement::ForStr(NotifyAddXml(
- "video1", "Bob", "", "3692")));
- action_elem->AddElement(
- buzz::XmlElement::ForStr(NotifyImplicitRemoveXml(
- "audio1", "Joe")));
- action_elem->AddElement(
- buzz::XmlElement::ForStr(NotifyAddXml(
- "audio1", "Bob", "", "3692")));
- action_elem->AddElement(
- buzz::XmlElement::ForStr(NotifyTwoSourceXml(
- "video1", "Joe", "1234", "Bob", "2468")));
-
- cricket::MediaSources sources;
- cricket::ParseError error;
-
- EXPECT_TRUE(cricket::IsSourcesNotify(action_elem.get()));
- ASSERT_TRUE(cricket::ParseSourcesNotify(action_elem.get(),
- remote_description_.get(),
- &sources, &error));
-
- ASSERT_EQ(5U, sources.video().size());
- ASSERT_EQ(2U, sources.audio().size());
-
- EXPECT_EQ("Joe", sources.video()[0].nick);
- EXPECT_EQ("Facetime", sources.video()[0].name);
- EXPECT_EQ(1234U, sources.video()[0].ssrc);
- EXPECT_TRUE(sources.video()[0].ssrc_set);
- EXPECT_FALSE(sources.video()[0].removed);
-
- EXPECT_EQ("Bob", sources.video()[1].nick);
- EXPECT_EQ("Microsoft Word", sources.video()[1].name);
- EXPECT_EQ(2468U, sources.video()[1].ssrc);
- EXPECT_TRUE(sources.video()[1].ssrc_set);
- EXPECT_FALSE(sources.video()[0].removed);
-
- EXPECT_EQ("Bob", sources.video()[2].nick);
- EXPECT_EQ(3692U, sources.video()[2].ssrc);
- EXPECT_TRUE(sources.video()[2].ssrc_set);
- EXPECT_EQ("", sources.video()[2].name);
- EXPECT_FALSE(sources.video()[0].removed);
-
- EXPECT_EQ("Joe", sources.video()[3].nick);
- EXPECT_EQ(1234U, sources.video()[3].ssrc);
-
- EXPECT_EQ("Bob", sources.video()[4].nick);
- EXPECT_EQ(2468U, sources.video()[4].ssrc);
-
- EXPECT_EQ("Joe", sources.audio()[0].nick);
- EXPECT_FALSE(sources.audio()[0].ssrc_set);
- EXPECT_FALSE(sources.video()[0].removed);
-}
-
-// Test serializing/deserializing a malformed <notify> message.
-TEST_F(MediaMessagesTest, NotifyFromBadXml) {
- MediaSources sources;
- ParseError error;
-
- // Bad ssrc
- talk_base::scoped_ptr<buzz::XmlElement> action_elem(
- new buzz::XmlElement(cricket::QN_JINGLE));
- action_elem->AddElement(
- buzz::XmlElement::ForStr(NotifyAddXml("video1", "Joe", "", "XYZ")));
- EXPECT_TRUE(cricket::IsSourcesNotify(action_elem.get()));
- EXPECT_FALSE(cricket::ParseSourcesNotify(
- action_elem.get(), remote_description_.get(), &sources, &error));
-
- // Bad nick
- action_elem.reset(new buzz::XmlElement(cricket::QN_JINGLE));
- action_elem->AddElement(
- buzz::XmlElement::ForStr(NotifyAddXml("video1", "", "", "1234")));
- EXPECT_TRUE(cricket::IsSourcesNotify(action_elem.get()));
- EXPECT_FALSE(cricket::ParseSourcesNotify(
- action_elem.get(), remote_description_.get(), &sources, &error));
-}
// Test serializing/deserializing typical streams xml.
TEST_F(MediaMessagesTest, StreamsToFromXml) {
diff --git a/talk/session/phone/mediasession.cc b/talk/session/phone/mediasession.cc
index 0784b56..67ac082 100644
--- a/talk/session/phone/mediasession.cc
+++ b/talk/session/phone/mediasession.cc
@@ -288,6 +288,7 @@
// TODO: Remove this legacy stream when all apps use StreamParams.
audio->AddLegacyStream(talk_base::CreateRandomNonZeroId());
}
+ audio->set_multistream(options.is_muc);
audio->set_rtcp_mux(options.rtcp_mux_enabled);
audio->set_lang(lang_);
@@ -340,6 +341,7 @@
// TODO: Remove this legacy stream when all apps use StreamParams.
video->AddLegacyStream(talk_base::CreateRandomNonZeroId());
}
+ video->set_multistream(options.is_muc);
video->set_bandwidth(options.video_bandwidth);
video->set_rtcp_mux(options.rtcp_mux_enabled);
@@ -347,11 +349,9 @@
CryptoParamsVec video_cryptos;
if (current_description) {
// Copy crypto parameters from the previous offer.
- const ContentInfo* info =
- GetFirstVideoContent(current_description);
- if (info) {
- const VideoContentDescription* desc =
- static_cast<const VideoContentDescription*>(info->description);
+ const VideoContentDescription* desc =
+ GetFirstVideoContentDescription(current_description);
+ if (desc) {
video_cryptos = desc->cryptos();
}
}
@@ -491,10 +491,9 @@
if (current_description) {
// Check if this crypto already exist in the previous
// session description. Use it in that case.
- const ContentInfo* info = GetFirstVideoContent(current_description);
- if (info) {
- const VideoContentDescription* desc =
- static_cast<const VideoContentDescription*>(info->description);
+ const VideoContentDescription* desc =
+ GetFirstVideoContentDescription(current_description);
+ if (desc) {
const CryptoParamsVec& cryptos = desc->cryptos();
for (CryptoParamsVec::const_iterator it = cryptos.begin();
it != cryptos.end(); ++it) {
@@ -539,12 +538,8 @@
return IsMediaContent(content, MEDIA_TYPE_VIDEO);
}
-static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
+static const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
MediaType media_type) {
- if (sdesc == NULL)
- return NULL;
-
- const ContentInfos& contents = sdesc->contents();
for (ContentInfos::const_iterator content = contents.begin();
content != contents.end(); content++) {
if (IsMediaContent(&*content, media_type)) {
@@ -554,6 +549,22 @@
return NULL;
}
+const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
+ return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
+}
+
+const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
+ return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
+}
+
+static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
+ MediaType media_type) {
+ if (sdesc == NULL)
+ return NULL;
+
+ return GetFirstMediaContent(sdesc->contents(), media_type);
+}
+
const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
}
@@ -562,4 +573,18 @@
return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
}
+const AudioContentDescription* GetFirstAudioContentDescription(
+ const SessionDescription* sdesc) {
+ const ContentInfo* content = GetFirstAudioContent(sdesc);
+ const ContentDescription* description = content ? content->description : NULL;
+ return static_cast<const AudioContentDescription*>(description);
+}
+
+const VideoContentDescription* GetFirstVideoContentDescription(
+ const SessionDescription* sdesc) {
+ const ContentInfo* content = GetFirstVideoContent(sdesc);
+ const ContentDescription* description = content ? content->description : NULL;
+ return static_cast<const VideoContentDescription*>(description);
+}
+
} // namespace cricket
diff --git a/talk/session/phone/mediasession.h b/talk/session/phone/mediasession.h
index c35a994..c2798c0 100644
--- a/talk/session/phone/mediasession.h
+++ b/talk/session/phone/mediasession.h
@@ -125,6 +125,7 @@
}
virtual MediaType type() const = 0;
+ virtual bool has_codecs() const = 0;
bool rtcp_mux() const { return rtcp_mux_; }
void set_rtcp_mux(bool mux) { rtcp_mux_ = mux; }
@@ -227,6 +228,8 @@
};
const std::vector<C>& codecs() const { return codecs_; }
+ void set_codecs(const std::vector<C>& codecs) { codecs_ = codecs; }
+ virtual bool has_codecs() const { return !codecs_.empty(); }
void AddCodec(const C& codec) {
codecs_.push_back(codec);
}
@@ -302,8 +305,14 @@
// Convenience functions.
bool IsAudioContent(const ContentInfo* content);
bool IsVideoContent(const ContentInfo* content);
+const ContentInfo* GetFirstAudioContent(const ContentInfos& contents);
+const ContentInfo* GetFirstVideoContent(const ContentInfos& contents);
const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc);
const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc);
+const AudioContentDescription* GetFirstAudioContentDescription(
+ const SessionDescription* sdesc);
+const VideoContentDescription* GetFirstVideoContentDescription(
+ const SessionDescription* sdesc);
} // namespace cricket
diff --git a/talk/session/phone/mediasession_unittest.cc b/talk/session/phone/mediasession_unittest.cc
index 4dd88c9..9bb2db2 100644
--- a/talk/session/phone/mediasession_unittest.cc
+++ b/talk/session/phone/mediasession_unittest.cc
@@ -56,8 +56,9 @@
using cricket::ContentInfo;
using cricket::CryptoParamsVec;
using cricket::AudioContentDescription;
-using cricket::MediaContentDescription;
using cricket::VideoContentDescription;
+using cricket::GetFirstAudioContentDescription;
+using cricket::GetFirstVideoContentDescription;
using cricket::kAutoBandwidth;
using cricket::AudioCodec;
using cricket::VideoCodec;
@@ -163,29 +164,6 @@
}
protected:
- const MediaContentDescription* GetMediaDescription(
- const SessionDescription* sdesc,
- const std::string& content_name) {
- const ContentInfo* content = sdesc->GetContentByName(content_name);
- if (content == NULL) {
- return NULL;
- }
- return static_cast<const MediaContentDescription*>(
- content->description);
- }
-
- const AudioContentDescription* GetAudioDescription(
- const SessionDescription* sdesc) {
- return static_cast<const AudioContentDescription*>(
- GetMediaDescription(sdesc, "audio"));
- }
-
- const VideoContentDescription* GetVideoDescription(
- const SessionDescription* sdesc) {
- return static_cast<const VideoContentDescription*>(
- GetMediaDescription(sdesc, "video"));
- }
-
MediaSessionDescriptionFactory f1_;
MediaSessionDescriptionFactory f2_;
};
@@ -342,56 +320,56 @@
offer.reset(f1_.CreateOffer(offer_opts, NULL));
answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL));
- ASSERT_TRUE(NULL != GetAudioDescription(offer.get()));
- ASSERT_TRUE(NULL != GetVideoDescription(offer.get()));
- ASSERT_TRUE(NULL != GetAudioDescription(answer.get()));
- ASSERT_TRUE(NULL != GetVideoDescription(answer.get()));
- EXPECT_TRUE(GetAudioDescription(offer.get())->rtcp_mux());
- EXPECT_TRUE(GetVideoDescription(offer.get())->rtcp_mux());
- EXPECT_TRUE(GetAudioDescription(answer.get())->rtcp_mux());
- EXPECT_TRUE(GetVideoDescription(answer.get())->rtcp_mux());
+ ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
+ ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get()));
+ ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get()));
+ ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get()));
+ EXPECT_TRUE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
+ EXPECT_TRUE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
+ EXPECT_TRUE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
+ EXPECT_TRUE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
offer_opts.rtcp_mux_enabled = true;
answer_opts.rtcp_mux_enabled = false;
offer.reset(f1_.CreateOffer(offer_opts, NULL));
answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL));
- ASSERT_TRUE(NULL != GetAudioDescription(offer.get()));
- ASSERT_TRUE(NULL != GetVideoDescription(offer.get()));
- ASSERT_TRUE(NULL != GetAudioDescription(answer.get()));
- ASSERT_TRUE(NULL != GetVideoDescription(answer.get()));
- EXPECT_TRUE(GetAudioDescription(offer.get())->rtcp_mux());
- EXPECT_TRUE(GetVideoDescription(offer.get())->rtcp_mux());
- EXPECT_FALSE(GetAudioDescription(answer.get())->rtcp_mux());
- EXPECT_FALSE(GetVideoDescription(answer.get())->rtcp_mux());
+ ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
+ ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get()));
+ ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get()));
+ ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get()));
+ EXPECT_TRUE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
+ EXPECT_TRUE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
+ EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
+ EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
offer_opts.rtcp_mux_enabled = false;
answer_opts.rtcp_mux_enabled = true;
offer.reset(f1_.CreateOffer(offer_opts, NULL));
answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL));
- ASSERT_TRUE(NULL != GetAudioDescription(offer.get()));
- ASSERT_TRUE(NULL != GetVideoDescription(offer.get()));
- ASSERT_TRUE(NULL != GetAudioDescription(answer.get()));
- ASSERT_TRUE(NULL != GetVideoDescription(answer.get()));
- EXPECT_FALSE(GetAudioDescription(offer.get())->rtcp_mux());
- EXPECT_FALSE(GetVideoDescription(offer.get())->rtcp_mux());
- EXPECT_FALSE(GetAudioDescription(answer.get())->rtcp_mux());
- EXPECT_FALSE(GetVideoDescription(answer.get())->rtcp_mux());
+ ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
+ ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get()));
+ ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get()));
+ ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get()));
+ EXPECT_FALSE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
+ EXPECT_FALSE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
+ EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
+ EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
offer_opts.rtcp_mux_enabled = false;
answer_opts.rtcp_mux_enabled = false;
offer.reset(f1_.CreateOffer(offer_opts, NULL));
answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL));
- ASSERT_TRUE(NULL != GetAudioDescription(offer.get()));
- ASSERT_TRUE(NULL != GetVideoDescription(offer.get()));
- ASSERT_TRUE(NULL != GetAudioDescription(answer.get()));
- ASSERT_TRUE(NULL != GetVideoDescription(answer.get()));
- EXPECT_FALSE(GetAudioDescription(offer.get())->rtcp_mux());
- EXPECT_FALSE(GetVideoDescription(offer.get())->rtcp_mux());
- EXPECT_FALSE(GetAudioDescription(answer.get())->rtcp_mux());
- EXPECT_FALSE(GetVideoDescription(answer.get())->rtcp_mux());
+ ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
+ ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get()));
+ ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get()));
+ ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get()));
+ EXPECT_FALSE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
+ EXPECT_FALSE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
+ EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
+ EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
}
// Create an audio-only answer to a video offer.
diff --git a/talk/session/phone/mediasessionclient_unittest.cc b/talk/session/phone/mediasessionclient_unittest.cc
index 0b99106..933ef35 100644
--- a/talk/session/phone/mediasessionclient_unittest.cc
+++ b/talk/session/phone/mediasessionclient_unittest.cc
@@ -849,8 +849,9 @@
"</cli:iq>";
}
-std::string JingleNotifyAdd(const std::string& content_name,
+std::string JingleStreamAdd(const std::string& content_name,
const std::string& nick,
+ const std::string& name,
const std::string& ssrc) {
return \
"<iq"
@@ -861,21 +862,30 @@
" id='150'>"
" <jingle"
" xmlns='urn:xmpp:jingle:1'"
- " action='session-info'>"
- " <notify"
- " xmlns='google:jingle'"
+ " action='description-info'>"
+ " <content"
+ " xmlns='urn:xmpp:jingle:1'"
" name='" + content_name + "'>"
- " <source"
- " nick='" + nick + "'>"
- " <ssrc>" + ssrc + "</ssrc>"
- " </source>"
- " </notify>"
+ " <description"
+ " xmlns='urn:xmpp:jingle:apps:rtp:1'"
+ " media='" + content_name + "'>"
+ " <streams"
+ " xmlns='google:jingle'>"
+ " <stream"
+ " nick='" + nick + "'"
+ " name='" + name + "'>"
+ " <ssrc>" + ssrc + "</ssrc>"
+ " </stream>"
+ " </streams>"
+ " </description>"
+ " </content>"
" </jingle>"
"</iq>";
}
-std::string JingleNotifyImplicitRemove(const std::string& content_name,
- const std::string& nick) {
+std::string JingleStreamRemove(const std::string& content_name,
+ const std::string& nick,
+ const std::string& name) {
return \
"<iq"
" xmlns='jabber:client'"
@@ -885,14 +895,21 @@
" id='150'>"
" <jingle"
" xmlns='urn:xmpp:jingle:1'"
- " action='session-info'>"
- " <notify"
- " xmlns='google:jingle'"
+ " action='description-info'>"
+ " <content"
+ " xmlns='urn:xmpp:jingle:1'"
" name='" + content_name + "'>"
- " <source"
- " nick='" + nick + "'>"
- " </source>"
- " </notify>"
+ " <description"
+ " xmlns='urn:xmpp:jingle:apps:rtp:1'"
+ " media='" + content_name + "'>"
+ " <streams"
+ " xmlns='google:jingle'>"
+ " <stream"
+ " nick='" + nick + "'"
+ " name='" + name + "'/>"
+ " </streams>"
+ " </description>"
+ " </content>"
" </jingle>"
"</iq>";
}
@@ -2015,9 +2032,7 @@
buzz::XmlElement* content_desc =
content->FirstNamed(cricket::QN_JINGLE_RTP_CONTENT);
ASSERT_TRUE(content_desc != NULL);
- // TODO: Allow option for intiator to select ssrc.
- // Right now, for MUC, we set it to a random value.
- ASSERT_NE("", content_desc->Attr(cricket::QN_SSRC));
+ ASSERT_EQ("", content_desc->Attr(cricket::QN_SSRC));
}
delete stanzas_[0];
stanzas_.clear();
@@ -2063,7 +2078,7 @@
jingle->SetAttr(cricket::QN_SID, call_->sessions()[0]->id());
}
- void TestSourceNotifiesAndViewRequests() {
+ void TestStreamsUpdateAndViewRequests() {
cricket::CallOptions options;
options.has_video = true;
options.is_muc = true;
@@ -2071,8 +2086,10 @@
client_->CreateCall();
call_->InitiateSession(buzz::Jid("me@mydomain.com"), options);
ASSERT_EQ(1U, ClearStanzas());
- ASSERT_EQ(0U, last_sources_update_.audio().size());
- ASSERT_EQ(0U, last_sources_update_.video().size());
+ ASSERT_EQ(0U, last_streams_added_.audio().size());
+ ASSERT_EQ(0U, last_streams_added_.video().size());
+ ASSERT_EQ(0U, last_streams_removed_.audio().size());
+ ASSERT_EQ(0U, last_streams_removed_.video().size());
talk_base::scoped_ptr<buzz::XmlElement> accept_stanza(
buzz::XmlElement::ForStr(kJingleAcceptWithSsrcs));
@@ -2083,53 +2100,68 @@
ASSERT_EQ(1U, stanzas_.size());
ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[0]->Attr(buzz::QN_TYPE));
ClearStanzas();
+ call_->sessions()[0]->SetState(cricket::Session::STATE_INPROGRESS);
- talk_base::scoped_ptr<buzz::XmlElement> notify_stanza(
- buzz::XmlElement::ForStr(JingleNotifyAdd("video", "Bob", "ABC")));
- SetJingleSid(notify_stanza.get());
- client_->session_manager()->OnIncomingMessage(notify_stanza.get());
+ talk_base::scoped_ptr<buzz::XmlElement> streams_stanza(
+ buzz::XmlElement::ForStr(
+ JingleStreamAdd("video", "Bob", "video1", "ABC")));
+ SetJingleSid(streams_stanza.get());
+ client_->session_manager()->OnIncomingMessage(streams_stanza.get());
// First one is ignored because of bad syntax.
ASSERT_EQ(1U, stanzas_.size());
// TODO: Figure out how to make this an ERROR rather than RESULT.
- ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[0]->Attr(buzz::QN_TYPE));
+ ASSERT_EQ(std::string(buzz::STR_ERROR), stanzas_[0]->Attr(buzz::QN_TYPE));
ClearStanzas();
- ASSERT_EQ(0U, last_sources_update_.audio().size());
- ASSERT_EQ(0U, last_sources_update_.video().size());
+ ASSERT_EQ(0U, last_streams_added_.audio().size());
+ ASSERT_EQ(0U, last_streams_added_.video().size());
+ ASSERT_EQ(0U, last_streams_removed_.audio().size());
+ ASSERT_EQ(0U, last_streams_removed_.video().size());
- notify_stanza.reset(
- buzz::XmlElement::ForStr(JingleNotifyAdd("audio", "Bob", "1234")));
- SetJingleSid(notify_stanza.get());
- client_->session_manager()->OnIncomingMessage(notify_stanza.get());
- ASSERT_EQ(1U, last_sources_update_.audio().size());
- ASSERT_EQ("Bob", last_sources_update_.audio()[0].nick);
- ASSERT_TRUE(last_sources_update_.audio()[0].ssrc_set);
- ASSERT_EQ(1234U, last_sources_update_.audio()[0].ssrc);
+ streams_stanza.reset(buzz::XmlElement::ForStr(
+ JingleStreamAdd("audio", "Bob", "audio1", "1234")));
+ SetJingleSid(streams_stanza.get());
+ client_->session_manager()->OnIncomingMessage(streams_stanza.get());
+ ASSERT_EQ(1U, last_streams_added_.audio().size());
+ ASSERT_EQ("Bob", last_streams_added_.audio()[0].nick);
+ ASSERT_EQ(1U, last_streams_added_.audio()[0].ssrcs.size());
+ ASSERT_EQ(1234U, last_streams_added_.audio()[0].first_ssrc());
- notify_stanza.reset(
- buzz::XmlElement::ForStr(JingleNotifyAdd("audio", "Joe", "2468")));
- SetJingleSid(notify_stanza.get());
- client_->session_manager()->OnIncomingMessage(notify_stanza.get());
- ASSERT_EQ(1U, last_sources_update_.audio().size());
- ASSERT_EQ("Joe", last_sources_update_.audio()[0].nick);
- ASSERT_TRUE(last_sources_update_.audio()[0].ssrc_set);
- ASSERT_EQ(2468U, last_sources_update_.audio()[0].ssrc);
+ streams_stanza.reset(buzz::XmlElement::ForStr(
+ JingleStreamAdd("audio", "Joe", "audio1", "2468")));
+ SetJingleSid(streams_stanza.get());
+ client_->session_manager()->OnIncomingMessage(streams_stanza.get());
+ ASSERT_EQ(1U, last_streams_added_.audio().size());
+ ASSERT_EQ("Joe", last_streams_added_.audio()[0].nick);
+ ASSERT_EQ(1U, last_streams_added_.audio()[0].ssrcs.size());
+ ASSERT_EQ(2468U, last_streams_added_.audio()[0].first_ssrc());
- notify_stanza.reset(
- buzz::XmlElement::ForStr(JingleNotifyAdd("video", "Bob", "5678")));
- SetJingleSid(notify_stanza.get());
- client_->session_manager()->OnIncomingMessage(notify_stanza.get());
- ASSERT_EQ(1U, last_sources_update_.video().size());
- ASSERT_EQ("Bob", last_sources_update_.video()[0].nick);
- ASSERT_TRUE(last_sources_update_.video()[0].ssrc_set);
- ASSERT_EQ(5678U, last_sources_update_.video()[0].ssrc);
+ streams_stanza.reset(buzz::XmlElement::ForStr(
+ JingleStreamAdd("video", "Bob", "video1", "5678")));
+ SetJingleSid(streams_stanza.get());
+ client_->session_manager()->OnIncomingMessage(streams_stanza.get());
+ ASSERT_EQ(1U, last_streams_added_.video().size());
+ ASSERT_EQ("Bob", last_streams_added_.video()[0].nick);
+ ASSERT_EQ(1U, last_streams_added_.video()[0].ssrcs.size());
+ ASSERT_EQ(5678U, last_streams_added_.video()[0].first_ssrc());
// We're testing that a "duplicate" is effectively ignored.
- last_sources_update_.mutable_video()->clear();
- notify_stanza.reset(
- buzz::XmlElement::ForStr(JingleNotifyAdd("video", "Bob", "5678")));
- SetJingleSid(notify_stanza.get());
- client_->session_manager()->OnIncomingMessage(notify_stanza.get());
- ASSERT_EQ(0U, last_sources_update_.video().size());
+ last_streams_added_.mutable_video()->clear();
+ last_streams_removed_.mutable_video()->clear();
+ streams_stanza.reset(buzz::XmlElement::ForStr(
+ JingleStreamAdd("video", "Bob", "video1", "5678")));
+ SetJingleSid(streams_stanza.get());
+ client_->session_manager()->OnIncomingMessage(streams_stanza.get());
+ ASSERT_EQ(0U, last_streams_added_.video().size());
+ ASSERT_EQ(0U, last_streams_removed_.video().size());
+
+ streams_stanza.reset(buzz::XmlElement::ForStr(
+ JingleStreamAdd("video", "Bob", "video2", "5679")));
+ SetJingleSid(streams_stanza.get());
+ client_->session_manager()->OnIncomingMessage(streams_stanza.get());
+ ASSERT_EQ(1U, last_streams_added_.video().size());
+ ASSERT_EQ("Bob", last_streams_added_.video()[0].nick);
+ ASSERT_EQ(1U, last_streams_added_.video()[0].ssrcs.size());
+ ASSERT_EQ(5679U, last_streams_added_.video()[0].first_ssrc());
cricket::FakeVoiceMediaChannel* voice_channel = fme_->GetVoiceChannel(0);
ASSERT_TRUE(voice_channel != NULL);
@@ -2153,41 +2185,45 @@
ASSERT_EQ(expected_view_elem->Str(), stanzas_[0]->Str());
ClearStanzas();
- // Implicit removal of audio ssrc.
- notify_stanza.reset(
- buzz::XmlElement::ForStr(JingleNotifyImplicitRemove("audio", "Bob")));
- SetJingleSid(notify_stanza.get());
- client_->session_manager()->OnIncomingMessage(notify_stanza.get());
- ASSERT_EQ(1U, last_sources_update_.audio().size());
- ASSERT_TRUE(last_sources_update_.audio()[0].removed);
- ASSERT_TRUE(last_sources_update_.audio()[0].ssrc_set);
- ASSERT_EQ(1234U, last_sources_update_.audio()[0].ssrc);
+ streams_stanza.reset(buzz::XmlElement::ForStr(
+ JingleStreamRemove("audio", "Bob", "audio1")));
+ SetJingleSid(streams_stanza.get());
+ client_->session_manager()->OnIncomingMessage(streams_stanza.get());
+ ASSERT_EQ(1U, last_streams_removed_.audio().size());
+ ASSERT_EQ(1U, last_streams_removed_.audio()[0].ssrcs.size());
+ ASSERT_EQ(1234U, last_streams_removed_.audio()[0].first_ssrc());
- // Implicit removal of video ssrc.
- notify_stanza.reset(
- buzz::XmlElement::ForStr(JingleNotifyImplicitRemove("video", "Bob")));
- SetJingleSid(notify_stanza.get());
- client_->session_manager()->OnIncomingMessage(notify_stanza.get());
- ASSERT_EQ(1U, last_sources_update_.video().size());
- ASSERT_TRUE(last_sources_update_.video()[0].removed);
- ASSERT_TRUE(last_sources_update_.video()[0].ssrc_set);
- ASSERT_EQ(5678U, last_sources_update_.video()[0].ssrc);
+ streams_stanza.reset(buzz::XmlElement::ForStr(
+ JingleStreamRemove("video", "Bob", "video1")));
+ SetJingleSid(streams_stanza.get());
+ client_->session_manager()->OnIncomingMessage(streams_stanza.get());
+ ASSERT_EQ(1U, last_streams_removed_.video().size());
+ ASSERT_EQ(1U, last_streams_removed_.audio()[0].ssrcs.size());
+ ASSERT_EQ(5678U, last_streams_removed_.audio()[0].first_ssrc());
- // Implicit removal of non-existent audio ssrc: should be ignored.
- last_sources_update_.mutable_audio()->clear();
- notify_stanza.reset(
- buzz::XmlElement::ForStr(JingleNotifyImplicitRemove("audio", "Bob")));
- SetJingleSid(notify_stanza.get());
- client_->session_manager()->OnIncomingMessage(notify_stanza.get());
- ASSERT_EQ(0U, last_sources_update_.audio().size());
+ streams_stanza.reset(buzz::XmlElement::ForStr(
+ JingleStreamRemove("video", "Bob", "video2")));
+ SetJingleSid(streams_stanza.get());
+ client_->session_manager()->OnIncomingMessage(streams_stanza.get());
+ ASSERT_EQ(1U, last_streams_removed_.video().size());
+ ASSERT_EQ(1U, last_streams_removed_.audio()[0].ssrcs.size());
+ ASSERT_EQ(5679U, last_streams_removed_.audio()[0].first_ssrc());
- // Implicit removal of non-existent video ssrc: should be ignored.
- last_sources_update_.mutable_video()->clear();
- notify_stanza.reset(
- buzz::XmlElement::ForStr(JingleNotifyImplicitRemove("video", "Bob")));
- SetJingleSid(notify_stanza.get());
- client_->session_manager()->OnIncomingMessage(notify_stanza.get());
- ASSERT_EQ(0U, last_sources_update_.video().size());
+ // Duplicate removal: should be ignored.
+ last_streams_removed_.mutable_audio()->clear();
+ streams_stanza.reset(buzz::XmlElement::ForStr(
+ JingleStreamRemove("audio", "Bob", "audio1")));
+ SetJingleSid(streams_stanza.get());
+ client_->session_manager()->OnIncomingMessage(streams_stanza.get());
+ ASSERT_EQ(0U, last_streams_removed_.audio().size());
+
+ // Duplicate removal: should be ignored.
+ last_streams_removed_.mutable_video()->clear();
+ streams_stanza.reset(buzz::XmlElement::ForStr(
+ JingleStreamRemove("video", "Bob", "video1")));
+ SetJingleSid(streams_stanza.get());
+ client_->session_manager()->OnIncomingMessage(streams_stanza.get());
+ ASSERT_EQ(0U, last_streams_removed_.video().size());
voice_channel = fme_->GetVoiceChannel(0);
ASSERT_TRUE(voice_channel != NULL);
@@ -2237,18 +2273,20 @@
void OnCallCreate(cricket::Call *call) {
call_ = call;
- call->SignalMediaSourcesUpdate.connect(
- this, &MediaSessionClientTest::OnMediaSourcesUpdate);
+ call->SignalMediaStreamsUpdate.connect(
+ this, &MediaSessionClientTest::OnMediaStreamsUpdate);
}
void OnCallDestroy(cricket::Call *call) {
call_ = NULL;
}
- void OnMediaSourcesUpdate(cricket::Call *call,
+ void OnMediaStreamsUpdate(cricket::Call *call,
cricket::Session *session,
- const cricket::MediaSources& sources) {
- last_sources_update_.CopyFrom(sources);
+ const cricket::MediaStreams& added,
+ const cricket::MediaStreams& removed) {
+ last_streams_added_.CopyFrom(added);
+ last_streams_removed_.CopyFrom(removed);
}
talk_base::NetworkManager* nm_;
@@ -2265,7 +2303,8 @@
bool expect_outgoing_crypto_;
int expected_video_bandwidth_;
bool expected_video_rtcp_mux_;
- cricket::MediaSources last_sources_update_;
+ cricket::MediaStreams last_streams_added_;
+ cricket::MediaStreams last_streams_removed_;
};
MediaSessionClientTest* GingleTest() {
@@ -2482,9 +2521,9 @@
test->TestIncomingAcceptWithSsrcs(kJingleAcceptWithSsrcs);
}
-TEST(MediaSessionTest, JingleNotifyAndView) {
+TEST(MediaSessionTest, JingleStreamsUpdateAndView) {
talk_base::scoped_ptr<MediaSessionClientTest> test(JingleTest());
- test->TestSourceNotifiesAndViewRequests();
+ test->TestStreamsUpdateAndViewRequests();
}
// Gingle tests
diff --git a/talk/session/phone/srtpfilter.cc b/talk/session/phone/srtpfilter.cc
index af02a7c..7a1cb78 100644
--- a/talk/session/phone/srtpfilter.cc
+++ b/talk/session/phone/srtpfilter.cc
@@ -369,7 +369,6 @@
#ifdef HAVE_SRTP
bool SrtpSession::inited_ = false;
-std::list<SrtpSession*> SrtpSession::sessions_;
SrtpSession::SrtpSession()
: session_(NULL),
@@ -377,12 +376,12 @@
rtcp_auth_tag_len_(0),
srtp_stat_(new SrtpStat()),
last_send_seq_num_(-1) {
- sessions_.push_back(this);
+ sessions()->push_back(this);
SignalSrtpError.repeat(srtp_stat_->SignalSrtpError);
}
SrtpSession::~SrtpSession() {
- sessions_.erase(std::find(sessions_.begin(), sessions_.end(), this));
+ sessions()->erase(std::find(sessions()->begin(), sessions()->end(), this));
if (session_) {
srtp_dealloc(session_);
}
@@ -582,8 +581,8 @@
}
void SrtpSession::HandleEventThunk(srtp_event_data_t* ev) {
- for (std::list<SrtpSession*>::iterator it = sessions_.begin();
- it != sessions_.end(); ++it) {
+ for (std::list<SrtpSession*>::iterator it = sessions()->begin();
+ it != sessions()->end(); ++it) {
if ((*it)->session_ == ev->session) {
(*it)->HandleEvent(ev);
break;
@@ -591,6 +590,11 @@
}
}
+std::list<SrtpSession*>* SrtpSession::sessions() {
+ LIBJINGLE_DEFINE_STATIC_LOCAL(std::list<SrtpSession*>, sessions, ());
+ return &sessions;
+}
+
#else // !HAVE_SRTP
// On some systems, SRTP is not (yet) available.
diff --git a/talk/session/phone/srtpfilter.h b/talk/session/phone/srtpfilter.h
index da7a236..991d4bf 100644
--- a/talk/session/phone/srtpfilter.h
+++ b/talk/session/phone/srtpfilter.h
@@ -194,13 +194,13 @@
static bool Init();
void HandleEvent(const srtp_event_data_t* ev);
static void HandleEventThunk(srtp_event_data_t* ev);
+ static std::list<SrtpSession*>* sessions();
srtp_t session_;
int rtp_auth_tag_len_;
int rtcp_auth_tag_len_;
talk_base::scoped_ptr<SrtpStat> srtp_stat_;
static bool inited_;
- static std::list<SrtpSession*> sessions_;
int last_send_seq_num_;
DISALLOW_COPY_AND_ASSIGN(SrtpSession);
};
diff --git a/talk/session/phone/streamparams.h b/talk/session/phone/streamparams.h
index 3e2bd94..e202f41 100644
--- a/talk/session/phone/streamparams.h
+++ b/talk/session/phone/streamparams.h
@@ -100,6 +100,9 @@
bool has_ssrc(uint32 ssrc) const {
return std::find(ssrcs.begin(), ssrcs.end(), ssrc) != ssrcs.end();
}
+ void add_ssrc(uint32 ssrc) {
+ ssrcs.push_back(ssrc);
+ }
// Resource of the MUC jid of the participant of with this stream.
// For 1:1 calls, should be left empty (which means remote streams
diff --git a/talk/session/phone/videoframe.cc b/talk/session/phone/videoframe.cc
index 0db3352..dd02a9a 100644
--- a/talk/session/phone/videoframe.cc
+++ b/talk/session/phone/videoframe.cc
@@ -87,8 +87,7 @@
size_t VideoFrame::StretchToBuffer(size_t w, size_t h,
uint8* buffer, size_t size,
- bool interpolate,
- bool vert_crop) const {
+ bool interpolate, bool vert_crop) const {
if (!buffer) return 0;
size_t needed = SizeOf(w, h);
@@ -103,8 +102,7 @@
}
void VideoFrame::StretchToFrame(VideoFrame *target,
- bool interpolate,
- bool vert_crop) const {
+ bool interpolate, bool vert_crop) const {
if (!target) return;
StretchToPlanes(target->GetYPlane(),
@@ -120,6 +118,16 @@
target->SetTimeStamp(GetTimeStamp());
}
+VideoFrame* VideoFrame::Stretch(size_t w, size_t h,
+ bool interpolate, bool vert_crop) const {
+ VideoFrame* dest = CreateEmptyFrame(w, h, GetPixelWidth(), GetPixelHeight(),
+ GetElapsedTime(), GetTimeStamp());
+ if (dest) {
+ StretchToFrame(dest, interpolate, vert_crop);
+ }
+ return dest;
+}
+
bool VideoFrame::SetToBlack() {
#ifdef HAVE_YUV
return libyuv::I420Rect(GetYPlane(), GetYPitch(),
diff --git a/talk/session/phone/videoframe.h b/talk/session/phone/videoframe.h
index 1c5ae5c..c34db88 100644
--- a/talk/session/phone/videoframe.h
+++ b/talk/session/phone/videoframe.h
@@ -50,13 +50,24 @@
public:
VideoFrame() : rendered_(false) {}
-
virtual ~VideoFrame() {}
+ // Creates a frame from a raw sample with FourCC |format| and size |w| x |h|.
+ // |h| can be negative indicating a vertically flipped image.
+ // |dw| is destination width; can be less than |w| if cropping is desired.
+ // |dh| is destination height, like |dw|, but must be a positive number.
+ // Returns whether the function succeeded or failed.
+ virtual bool Reset(uint32 fourcc, int w, int h, int dw, int dh,
+ uint8 *sample, size_t sample_size,
+ size_t pixel_width, size_t pixel_height,
+ int64 elapsed_time, int64 time_stamp, int rotation) = 0;
+
+ // Basic accessors.
virtual size_t GetWidth() const = 0;
virtual size_t GetHeight() const = 0;
size_t GetChromaWidth() const { return (GetWidth() + 1) / 2; }
size_t GetChromaHeight() const { return (GetHeight() + 1) / 2; }
+ size_t GetChromaSize() const { return GetUPitch() * GetChromaHeight(); }
virtual const uint8 *GetYPlane() const = 0;
virtual const uint8 *GetUPlane() const = 0;
virtual const uint8 *GetVPlane() const = 0;
@@ -72,8 +83,6 @@
virtual size_t GetPixelWidth() const = 0;
virtual size_t GetPixelHeight() const = 0;
- // TODO: Add a fourcc format here and probably combine VideoFrame
- // with CapturedFrame.
virtual int64 GetElapsedTime() const = 0;
virtual int64 GetTimeStamp() const = 0;
virtual void SetElapsedTime(int64 elapsed_time) = 0;
@@ -102,10 +111,10 @@
// Converts the I420 data to RGB of a certain type such as ARGB and ABGR.
// Returns the frame's actual size, regardless of whether it was written or
- // not (like snprintf). Parameters size and pitch_rgb are in units of bytes.
+ // not (like snprintf). Parameters size and stride_rgb are in units of bytes.
// If there is insufficient space, nothing is written.
virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, uint8 *buffer,
- size_t size, size_t pitch_rgb) const = 0;
+ size_t size, int stride_rgb) const = 0;
// Writes the frame into the given planes, stretched to the given width and
// height. The parameter "interpolate" controls whether to interpolate or just
@@ -138,9 +147,9 @@
// just take the nearest-point. The parameter "crop" controls whether to crop
// this frame to the aspect ratio of the given dimensions before stretching.
virtual VideoFrame *Stretch(size_t w, size_t h, bool interpolate,
- bool crop) const = 0;
+ bool crop) const;
- // Set the video frame to black.
+ // Sets the video frame to black.
bool SetToBlack();
// Size of an I420 image of given dimensions when stored as a frame buffer.
@@ -149,6 +158,12 @@
}
protected:
+ // Creates an empty frame.
+ virtual VideoFrame* CreateEmptyFrame(int w, int h,
+ size_t pixel_width, size_t pixel_height,
+ int64 elapsed_time,
+ int64 time_stamp) const = 0;
+
// The frame needs to be rendered to magiccam only once.
// TODO: Remove this flag once magiccam rendering is fully replaced
// by client3d rendering.
diff --git a/talk/session/phone/videorenderer.h b/talk/session/phone/videorenderer.h
index f1fe547..c428d57 100644
--- a/talk/session/phone/videorenderer.h
+++ b/talk/session/phone/videorenderer.h
@@ -28,6 +28,10 @@
#ifndef TALK_SESSION_PHONE_VIDEORENDERER_H_
#define TALK_SESSION_PHONE_VIDEORENDERER_H_
+#ifdef _DEBUG
+#include <string>
+#endif // _DEBUG
+
namespace cricket {
class VideoFrame;
@@ -40,6 +44,11 @@
virtual bool SetSize(int width, int height, int reserved) = 0;
// Called when a new frame is available for display.
virtual bool RenderFrame(const VideoFrame *frame) = 0;
+
+#ifdef _DEBUG
+ // Allow renderer dumping out rendered frames.
+ virtual bool SetDumpPath(const std::string &path) { return true; }
+#endif // _DEBUG
};
} // namespace cricket
diff --git a/talk/session/phone/webrtcvideoengine.cc b/talk/session/phone/webrtcvideoengine.cc
index 9667538..c3896a2 100644
--- a/talk/session/phone/webrtcvideoengine.cc
+++ b/talk/session/phone/webrtcvideoengine.cc
@@ -49,9 +49,6 @@
#include "talk/session/phone/webrtcvie.h"
#include "talk/session/phone/webrtcvoe.h"
-// TODO Change video protection calls when WebRTC API has changed.
-#define WEBRTC_VIDEO_AVPF_NACK_ONLY
-
namespace cricket {
static const int kDefaultLogSeverity = talk_base::LS_WARNING;
@@ -272,10 +269,8 @@
const WebRtcVideoEngine::VideoCodecPref
WebRtcVideoEngine::kVideoCodecPrefs[] = {
{kVp8PayloadName, 100, 0},
-#ifndef WEBRTC_VIDEO_AVPF_NACK_ONLY
{kRedPayloadName, 101, 1},
{kFecPayloadName, 102, 2},
-#endif
};
static const int64 kNsPerFrame = 33333333; // 30fps
@@ -339,7 +334,7 @@
video_capturer_ = NULL;
capture_started_ = false;
- ApplyLogging();
+ ApplyLogging("");
if (tracing_->SetTraceCallback(this) != 0) {
LOG_RTCERR1(SetTraceCallback, this);
}
@@ -366,6 +361,8 @@
Terminate();
}
tracing_->SetTraceCallback(NULL);
+ // Test to see if the media processor was deregistered properly.
+ ASSERT(SignalMediaFrame.is_empty());
}
bool WebRtcVideoEngine::Init() {
@@ -643,6 +640,18 @@
return;
}
+ // TODO: This is the trigger point for Tx video processing.
+ // Once the capturer refactoring is done, we will move this into the
+ // capturer...it's not there right now because that image is in not in the
+ // I420 color space.
+ // The clients that subscribe will obtain meta info from the frame.
+ // When this trigger is switched over to capturer, need to pass in the real
+ // ssrc.
+ {
+ talk_base::CritScope cs(&signal_media_critical_);
+ SignalMediaFrame(kDummyVideoSsrc, &i420_frame);
+ }
+
// Send I420 frame to the local renderer.
if (local_renderer_) {
if (local_renderer_w_ != static_cast<int>(i420_frame.GetWidth()) ||
@@ -666,8 +675,11 @@
}
void WebRtcVideoEngine::SetLogging(int min_sev, const char* filter) {
- log_level_ = min_sev;
- ApplyLogging();
+ // if min_sev == -1, we keep the current log level.
+ if (min_sev >= 0) {
+ log_level_ = min_sev;
+ }
+ ApplyLogging(filter);
}
int WebRtcVideoEngine::GetLastEngineError() {
@@ -785,7 +797,7 @@
int ncodecs = vie_wrapper_->codec()->NumberOfCodecs();
for (int i = 0; i < ncodecs; ++i) {
if (vie_wrapper_->codec()->GetCodec(i, *out_codec) == 0 &&
- in_codec.name == out_codec->plName) {
+ _stricmp(in_codec.name.c_str(), out_codec->plName) == 0) {
found = true;
break;
}
@@ -846,7 +858,10 @@
return true;
}
-void WebRtcVideoEngine::ApplyLogging() {
+// See https://sites.google.com/a/google.com/wavelet/
+// Home/Magic-Flute--RTC-Engine-/Magic-Flute-Command-Line-Parameters
+// for all supported command line setttings.
+void WebRtcVideoEngine::ApplyLogging(const std::string& log_filter) {
int filter = 0;
switch (log_level_) {
case talk_base::LS_VERBOSE: filter |= webrtc::kTraceAll;
@@ -856,6 +871,18 @@
webrtc::kTraceError | webrtc::kTraceCritical;
}
tracing_->SetTraceFilter(filter);
+
+ // Set WebRTC trace file.
+ std::vector<std::string> opts;
+ talk_base::tokenize(log_filter, ' ', '"', '"', &opts);
+ std::vector<std::string>::iterator tracefile =
+ std::find(opts.begin(), opts.end(), "tracefile");
+ if (tracefile != opts.end() && ++tracefile != opts.end()) {
+ // Write WebRTC debug output (at same loglevel) to file
+ if (tracing_->SetTraceFile(tracefile->c_str()) == -1) {
+ LOG_RTCERR1(SetTraceFile, *tracefile);
+ }
+ }
}
// Rebuilds the codec list to be only those that are less intensive
@@ -958,13 +985,17 @@
}
}
-// TODO: stubs for now
bool WebRtcVideoEngine::RegisterProcessor(
VideoProcessor* video_processor) {
+ talk_base::CritScope cs(&signal_media_critical_);
+ SignalMediaFrame.connect(video_processor,
+ &VideoProcessor::OnFrame);
return true;
}
bool WebRtcVideoEngine::UnregisterProcessor(
VideoProcessor* video_processor) {
+ talk_base::CritScope cs(&signal_media_critical_);
+ SignalMediaFrame.disconnect(video_processor);
return true;
}
@@ -1131,12 +1162,10 @@
return false;
}
-#ifndef WEBRTC_VIDEO_AVPF_NACK_ONLY
- // Configure FEC if enabled.
- if (!SetNackFec(red_type, fec_type)) {
+ // Configure video protection.
+ if (!SetNackFec(vie_channel_, red_type, fec_type)) {
return false;
}
-#endif
// Select the first matched codec.
webrtc::VideoCodec& codec(send_codecs[0]);
@@ -1714,14 +1743,6 @@
channel_id, webrtc::kViEKeyFrameRequestPliRtcp);
return false;
}
-
-#ifdef WEBRTC_VIDEO_AVPF_NACK_ONLY
- // Turn on NACK-only loss handling.
- if (engine_->vie()->rtp()->SetNACKStatus(channel_id, true) != 0) {
- LOG_RTCERR1(SetNACKStatus, channel_id);
- return false;
- }
-#endif
return true;
}
@@ -1753,10 +1774,14 @@
return false;
}
- // Turn on TMMBR-based BWE reporting.
- // TODO: We should use REMB per default when it is implemented.
- if (engine_->vie()->rtp()->SetTMMBRStatus(channel_id, true) != 0) {
- LOG_RTCERR1(SetTMMBRStatus, channel_id);
+ // Turn on REMB-based BWE reporting.
+ // First parameter is channel id, second is if this channel should send REMB
+ // packets, last parameter is if it should report BWE using REMB.
+ // TODO: |send_remb| should be channel id so we can have several
+ // REMB groups.
+ bool send_remb = (remote_ssrc == 0); // SSRC 0 is our default channel.
+ if (!engine_->vie()->rtp()->SetRembStatus(channel_id, send_remb, true)) {
+ LOG_RTCERR3(SetRembStatus, channel_id, send_remb, true);
return false;
}
@@ -1805,14 +1830,27 @@
return true;
}
-bool WebRtcVideoMediaChannel::SetNackFec(int red_payload_type,
+bool WebRtcVideoMediaChannel::SetNackFec(int channel_id,
+ int red_payload_type,
int fec_payload_type) {
- bool enable = (red_payload_type != -1 && fec_payload_type != -1);
- if (engine_->vie()->rtp()->SetHybridNACKFECStatus(
- vie_channel_, enable, red_payload_type, fec_payload_type) != 0) {
- LOG_RTCERR4(SetHybridNACKFECStatus,
- vie_channel_, enable, red_payload_type, fec_payload_type);
- return false;
+ // Enable hybrid NACK/FEC if negotiated and not in a conference, use only NACK
+ // otherwise.
+ bool enable = (red_payload_type != -1 && fec_payload_type != -1 &&
+ !(channel_options_ & OPT_CONFERENCE));
+ if (enable) {
+ if (engine_->vie()->rtp()->SetHybridNACKFECStatus(
+ channel_id, enable, red_payload_type, fec_payload_type) != 0) {
+ LOG_RTCERR4(SetHybridNACKFECStatus,
+ channel_id, enable, red_payload_type, fec_payload_type);
+ return false;
+ }
+ LOG(LS_INFO) << "Hybrid NACK/FEC enabled for channel " << channel_id;
+ } else {
+ if (engine_->vie()->rtp()->SetNACKStatus(channel_id, true) != 0) {
+ LOG_RTCERR1(SetNACKStatus, channel_id);
+ return false;
+ }
+ LOG(LS_INFO) << "NACK enabled for channel " << channel_id;
}
return true;
}
@@ -1842,14 +1880,29 @@
}
bool WebRtcVideoMediaChannel::SetReceiveCodecs(int channel_id) {
+ int red_type = -1;
+ int fec_type = -1;
for (std::vector<webrtc::VideoCodec>::iterator it = receive_codecs_.begin();
it != receive_codecs_.end(); ++it) {
+ if (it->codecType == webrtc::kVideoCodecRED) {
+ red_type = it->plType;
+ } else if (it->codecType == webrtc::kVideoCodecULPFEC) {
+ fec_type = it->plType;
+ }
if (engine()->vie()->codec()->SetReceiveCodec(channel_id, *it) != 0) {
LOG_RTCERR2(SetReceiveCodec, channel_id, it->plName);
return false;
}
}
+ // Enable video protection. For a sending channel, this will be taken care of
+ // in SetSendCodecs.
+ if (channel_id != vie_channel_) {
+ if (!SetNackFec(channel_id, red_type, fec_type)) {
+ return false;
+ }
+ }
+
// Start receiving packets if at least one receive codec has been set.
if (!receive_codecs_.empty()) {
if (engine()->vie()->base()->StartReceive(channel_id) != 0) {
diff --git a/talk/session/phone/webrtcvideoengine.h b/talk/session/phone/webrtcvideoengine.h
index c25c4da..0e65a34 100644
--- a/talk/session/phone/webrtcvideoengine.h
+++ b/talk/session/phone/webrtcvideoengine.h
@@ -158,7 +158,7 @@
WebRtcVoiceEngine* voice_engine);
bool SetDefaultCodec(const VideoCodec& codec);
bool RebuildCodecList(const VideoCodec& max_codec);
- void ApplyLogging();
+ void ApplyLogging(const std::string& log_filter);
bool InitVideoEngine();
bool SetCapturer(VideoCapturer* capturer, bool own_capturer);
@@ -266,7 +266,7 @@
// Creates and initializes a WebRtc video channel.
bool ConfigureChannel(int channel_id);
bool ConfigureReceiving(int channel_id, uint32 remote_ssrc);
- bool SetNackFec(int red_payload_type, int fec_payload_type);
+ bool SetNackFec(int channel_id, int red_payload_type, int fec_payload_type);
bool SetSendCodec(const webrtc::VideoCodec& codec,
int min_bitrate,
int start_bitrate,
diff --git a/talk/session/phone/webrtcvideoengine_unittest.cc b/talk/session/phone/webrtcvideoengine_unittest.cc
index 88d1505..b4f608a 100644
--- a/talk/session/phone/webrtcvideoengine_unittest.cc
+++ b/talk/session/phone/webrtcvideoengine_unittest.cc
@@ -27,6 +27,7 @@
#include "talk/base/gunit.h"
#include "talk/base/scoped_ptr.h"
+#include "talk/session/phone/fakemediaprocessor.h"
#include "talk/session/phone/fakewebrtcvideocapturemodule.h"
#include "talk/session/phone/fakewebrtcvideoengine.h"
#include "talk/session/phone/fakewebrtcvoiceengine.h"
@@ -174,12 +175,11 @@
EXPECT_TRUE(channel_ == NULL);
}
-// Test that we apply plain old VP8 codecs properly.
+// Test that we apply our default codecs properly.
TEST_F(WebRtcVideoEngineTestFake, SetSendCodecs) {
EXPECT_TRUE(SetupEngine());
int channel_num = vie_.GetLastChannel();
std::vector<cricket::VideoCodec> codecs(engine_.codecs());
- codecs.resize(1); // toss out red and ulpfec
EXPECT_TRUE(channel_->SetSendCodecs(codecs));
webrtc::VideoCodec gcodec;
EXPECT_EQ(0, vie_.GetSendCodec(channel_num, gcodec));
@@ -190,7 +190,8 @@
EXPECT_EQ(kMinBandwidthKbps, gcodec.minBitrate);
EXPECT_EQ(kStartBandwidthKbps, gcodec.startBitrate);
EXPECT_EQ(kMaxBandwidthKbps, gcodec.maxBitrate);
- // TODO: Check HybridNackFecStatus.
+ EXPECT_TRUE(vie_.GetHybridNackFecStatus(channel_num));
+ EXPECT_FALSE(vie_.GetNackStatus(channel_num));
// TODO: Check RTCP, PLI, TMMBR.
}
@@ -349,20 +350,103 @@
vie_.GetKeyFrameRequestMethod(channel_num));
}
-// Test that tmmmbr is enabled on the channel.
-TEST_F(WebRtcVideoEngineTestFake, TmmbrEnabled) {
+// Test that remb is enabled on the default channel.
+TEST_F(WebRtcVideoEngineTestFake, RembEnabled) {
EXPECT_TRUE(SetupEngine());
int channel_num = vie_.GetLastChannel();
- EXPECT_TRUE(vie_.GetTmmbrStatus(channel_num));
+ EXPECT_TRUE(vie_.GetRembStatus(channel_num));
+ EXPECT_TRUE(vie_.GetRembStatusSend(channel_num));
}
-// Test that nack is enabled on the channel.
+// Test that remb is enabled on a receive channel but it uses the default
+// channel for sending remb packets.
+TEST_F(WebRtcVideoEngineTestFake, RembEnabledOnReceiveChannels) {
+ EXPECT_TRUE(SetupEngine());
+ EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1)));
+ int channel_num = vie_.GetLastChannel();
+ EXPECT_TRUE(vie_.GetRembStatus(channel_num));
+ EXPECT_FALSE(vie_.GetRembStatusSend(channel_num));
+}
+
+// Test that nack is enabled on the channel if we don't offer red/fec.
TEST_F(WebRtcVideoEngineTestFake, NackEnabled) {
EXPECT_TRUE(SetupEngine());
int channel_num = vie_.GetLastChannel();
+ std::vector<cricket::VideoCodec> codecs(engine_.codecs());
+ codecs.resize(1); // toss out red and ulpfec
+ EXPECT_TRUE(channel_->SetSendCodecs(codecs));
EXPECT_TRUE(vie_.GetNackStatus(channel_num));
}
+// Test that we enable hybrid NACK FEC mode.
+TEST_F(WebRtcVideoEngineTestFake, HybridNackFec) {
+ EXPECT_TRUE(SetupEngine());
+ int channel_num = vie_.GetLastChannel();
+ EXPECT_TRUE(channel_->SetRecvCodecs(engine_.codecs()));
+ EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs()));
+ EXPECT_TRUE(vie_.GetHybridNackFecStatus(channel_num));
+ EXPECT_FALSE(vie_.GetNackStatus(channel_num));
+}
+
+// Test that we enable hybrid NACK FEC mode when calling SetSendCodecs and
+// SetReceiveCodecs in reversed order.
+TEST_F(WebRtcVideoEngineTestFake, HybridNackFecReversedOrder) {
+ EXPECT_TRUE(SetupEngine());
+ int channel_num = vie_.GetLastChannel();
+ EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs()));
+ EXPECT_TRUE(channel_->SetRecvCodecs(engine_.codecs()));
+ EXPECT_TRUE(vie_.GetHybridNackFecStatus(channel_num));
+ EXPECT_FALSE(vie_.GetNackStatus(channel_num));
+}
+
+// Test NACK vs Hybrid NACK/FEC interop call setup, i.e. only use NACK even if
+// red/fec is offered as receive codec.
+TEST_F(WebRtcVideoEngineTestFake, VideoProtectionInterop) {
+ EXPECT_TRUE(SetupEngine());
+ int channel_num = vie_.GetLastChannel();
+ std::vector<cricket::VideoCodec> recv_codecs(engine_.codecs());
+ std::vector<cricket::VideoCodec> send_codecs(engine_.codecs());
+ // Only add VP8 as send codec.
+ send_codecs.resize(1);
+ EXPECT_TRUE(channel_->SetRecvCodecs(recv_codecs));
+ EXPECT_TRUE(channel_->SetSendCodecs(send_codecs));
+ EXPECT_FALSE(vie_.GetHybridNackFecStatus(channel_num));
+ EXPECT_TRUE(vie_.GetNackStatus(channel_num));
+}
+
+// Test NACK vs Hybrid NACK/FEC interop call setup, i.e. only use NACK even if
+// red/fec is offered as receive codec. Call order reversed compared to
+// VideoProtectionInterop.
+TEST_F(WebRtcVideoEngineTestFake, VideoProtectionInteropReversed) {
+ EXPECT_TRUE(SetupEngine());
+ int channel_num = vie_.GetLastChannel();
+ std::vector<cricket::VideoCodec> recv_codecs(engine_.codecs());
+ std::vector<cricket::VideoCodec> send_codecs(engine_.codecs());
+ // Only add VP8 as send codec.
+ send_codecs.resize(1);
+ EXPECT_TRUE(channel_->SetSendCodecs(send_codecs));
+ EXPECT_TRUE(channel_->SetRecvCodecs(recv_codecs));
+ EXPECT_FALSE(vie_.GetHybridNackFecStatus(channel_num));
+ EXPECT_TRUE(vie_.GetNackStatus(channel_num));
+}
+
+// Test that NACK, not hybrid mode, is enabled in conference mode.
+TEST_F(WebRtcVideoEngineTestFake, HybridNackFecConference) {
+ EXPECT_TRUE(SetupEngine());
+ // Setup the send channel.
+ int send_channel_num = vie_.GetLastChannel();
+ EXPECT_TRUE(channel_->SetOptions(cricket::OPT_CONFERENCE));
+ EXPECT_TRUE(channel_->SetRecvCodecs(engine_.codecs()));
+ EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs()));
+ EXPECT_FALSE(vie_.GetHybridNackFecStatus(send_channel_num));
+ EXPECT_TRUE(vie_.GetNackStatus(send_channel_num));
+ // Add a receive stream.
+ EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1)));
+ int receive_channel_num = vie_.GetLastChannel();
+ EXPECT_FALSE(vie_.GetHybridNackFecStatus(receive_channel_num));
+ EXPECT_TRUE(vie_.GetNackStatus(receive_channel_num));
+}
+
// Test that we can create a channel and start/stop rendering out on it.
TEST_F(WebRtcVideoEngineTestFake, SetRender) {
EXPECT_TRUE(SetupEngine());
@@ -402,8 +486,6 @@
EXPECT_FALSE(vie_.GetSend(channel_num));
}
-// TODO: Add test for FEC.
-
// Test that we set bandwidth properly when using full auto bandwidth mode.
TEST_F(WebRtcVideoEngineTestFake, SetBandwidthAuto) {
EXPECT_TRUE(SetupEngine());
@@ -514,7 +596,7 @@
TEST_F(WebRtcVideoEngineTest, FindCodec) {
// We should not need to init engine in order to get codecs.
const std::vector<cricket::VideoCodec>& c = engine_.codecs();
- EXPECT_EQ(1U, c.size());
+ EXPECT_EQ(3U, c.size());
cricket::VideoCodec vp8(104, "VP8", 320, 200, 30, 0);
EXPECT_TRUE(engine_.FindCodec(vp8));
@@ -538,8 +620,6 @@
cricket::VideoCodec vp8_zero_res(104, "VP8", 0, 0, 30, 0);
EXPECT_TRUE(engine_.FindCodec(vp8_zero_res));
- // TODO: Re-enable when we re-enable FEC.
-#if 0
cricket::VideoCodec red(101, "RED", 0, 0, 30, 0);
EXPECT_TRUE(engine_.FindCodec(red));
@@ -551,7 +631,6 @@
cricket::VideoCodec fec_ci(102, "ulpfec", 0, 0, 30, 0);
EXPECT_TRUE(engine_.FindCodec(fec));
-#endif
}
TEST_F(WebRtcVideoEngineTest, StartupShutdown) {
@@ -643,6 +722,21 @@
EXPECT_FALSE(engine_.IsCapturing());
}
+TEST_F(WebRtcVideoEngineTest, TestRegisterVideoProcessor) {
+ cricket::FakeMediaProcessor vp;
+ EXPECT_TRUE(engine_.Init());
+
+ EXPECT_TRUE(engine_.RegisterProcessor(&vp));
+ engine_.TriggerMediaFrame(0, NULL);
+ EXPECT_EQ(1, vp.video_frame_count());
+
+ EXPECT_TRUE(engine_.UnregisterProcessor(&vp));
+ engine_.TriggerMediaFrame(0, NULL);
+ EXPECT_EQ(1, vp.video_frame_count());
+
+ engine_.Terminate();
+}
+
TEST_F(WebRtcVideoMediaChannelTest, SetRecvCodecs) {
std::vector<cricket::VideoCodec> codecs;
codecs.push_back(kVP8Codec);
diff --git a/talk/session/phone/webrtcvideoframe.cc b/talk/session/phone/webrtcvideoframe.cc
index 1585e09..5eb849d 100644
--- a/talk/session/phone/webrtcvideoframe.cc
+++ b/talk/session/phone/webrtcvideoframe.cc
@@ -27,6 +27,7 @@
#include "talk/session/phone/webrtcvideoframe.h"
+#include "libyuv/convert.h"
#include "libyuv/planar_functions.h"
#include "talk/base/logging.h"
#include "talk/session/phone/videocapturer.h"
@@ -51,52 +52,21 @@
size_t pixel_width, size_t pixel_height,
int64 elapsed_time, int64 time_stamp,
int rotation) {
- // WebRtcVideoFrame currently doesn't support color conversion or rotation.
- // TODO: Add horizontal cropping support.
- if (format != FOURCC_I420 || dw != w || dh < 0 || dh > abs(h) ||
- rotation != 0) {
- return false;
- }
-
- size_t desired_size = SizeOf(dw, dh);
- uint8* buffer = new uint8[desired_size];
- Attach(buffer, desired_size, dw, dh, pixel_width, pixel_height,
- elapsed_time, time_stamp, rotation);
- if (dh == h) {
- // Uncropped
- memcpy(buffer, sample, desired_size);
- } else {
- // Cropped
- // TODO: use I420Copy which supports horizontal crop and vertical
- // flip.
- int horiz_crop = ((w - dw) / 2) & ~1;
- int vert_crop = ((abs(h) - dh) / 2) & ~1;
- int y_crop_offset = w * vert_crop + horiz_crop;
- int halfwidth = (w + 1) / 2;
- int halfheight = (h + 1) / 2;
- int uv_size = GetChromaSize();
- int uv_crop_offset = (halfwidth * vert_crop + horiz_crop) / 2;
- uint8* src_y = sample + y_crop_offset;
- uint8* src_u = sample + w * h + uv_crop_offset;
- uint8* src_v = sample + w * h + halfwidth * halfheight + uv_crop_offset;
- memcpy(GetYPlane(), src_y, dw * dh);
- memcpy(GetUPlane(), src_u, uv_size);
- memcpy(GetVPlane(), src_v, uv_size);
- }
- return true;
+ return Reset(format, w, h, dw, dh, sample, sample_size,
+ pixel_width, pixel_height, elapsed_time, time_stamp, rotation);
}
bool WebRtcVideoFrame::Init(const CapturedFrame* frame, int dw, int dh) {
- return Init(frame->fourcc, frame->width, frame->height, dw, dh,
- static_cast<uint8*>(frame->data), frame->data_size,
- frame->pixel_width, frame->pixel_height,
- frame->elapsed_time, frame->time_stamp, frame->rotation);
+ return Reset(frame->fourcc, frame->width, frame->height, dw, dh,
+ static_cast<uint8*>(frame->data), frame->data_size,
+ frame->pixel_width, frame->pixel_height,
+ frame->elapsed_time, frame->time_stamp, frame->rotation);
}
bool WebRtcVideoFrame::InitToBlack(int w, int h,
size_t pixel_width, size_t pixel_height,
int64 elapsed_time, int64 time_stamp) {
- CreateBuffer(w, h, pixel_width, pixel_height, elapsed_time, time_stamp);
+ InitToEmptyBuffer(w, h, pixel_width, pixel_height, elapsed_time, time_stamp);
return SetToBlack();
}
@@ -217,70 +187,30 @@
size_t WebRtcVideoFrame::ConvertToRgbBuffer(uint32 to_fourcc,
uint8* buffer,
size_t size,
- size_t stride_rgb) const {
+ int stride_rgb) const {
if (!video_frame_.Buffer()) {
return 0;
}
size_t width = video_frame_.Width();
size_t height = video_frame_.Height();
- size_t needed = stride_rgb * height;
+ size_t needed = (stride_rgb >= 0 ? stride_rgb : -stride_rgb) * height;
if (size < needed) {
LOG(LS_WARNING) << "RGB buffer is not large enough";
return needed;
}
- // TODO: Use libyuv::ConvertFromI420
- switch (to_fourcc) {
- case FOURCC_ARGB:
- libyuv::I420ToARGB(
- GetYPlane(), GetYPitch(),
- GetUPlane(), GetUPitch(),
- GetVPlane(), GetVPitch(),
- buffer, stride_rgb, width, height);
- break;
-
- case FOURCC_BGRA:
- libyuv::I420ToBGRA(
- GetYPlane(), GetYPitch(),
- GetUPlane(), GetUPitch(),
- GetVPlane(), GetVPitch(),
- buffer, stride_rgb, width, height);
- break;
-
- case FOURCC_ABGR:
- libyuv::I420ToABGR(
- GetYPlane(), GetYPitch(),
- GetUPlane(), GetUPitch(),
- GetVPlane(), GetVPitch(),
- buffer, stride_rgb, width, height);
- break;
-
- default:
- needed = 0;
- LOG(LS_WARNING) << "RGB type not supported: " << to_fourcc;
- break;
+ if (libyuv::ConvertFromI420(GetYPlane(), GetYPitch(),
+ GetUPlane(), GetUPitch(),
+ GetVPlane(), GetVPitch(),
+ buffer, stride_rgb,
+ width, height,
+ to_fourcc)) {
+ LOG(LS_WARNING) << "RGB type not supported: " << to_fourcc;
+ return 0; // 0 indicates error
}
return needed;
}
-VideoFrame* WebRtcVideoFrame::Stretch(size_t w, size_t h,
- bool interpolate, bool vert_crop) const {
- WebRtcVideoFrame* frame = new WebRtcVideoFrame();
- frame->CreateBuffer(w, h, 1, 1, 0, 0);
- StretchToFrame(frame, interpolate, vert_crop);
-
- return frame;
-}
-
-void WebRtcVideoFrame::CreateBuffer(int w, int h,
- size_t pixel_width, size_t pixel_height,
- int64 elapsed_time, int64 time_stamp) {
- size_t buffer_size = VideoFrame::SizeOf(w, h);
- uint8* buffer = new uint8[buffer_size];
- Attach(buffer, buffer_size, w, h, pixel_width, pixel_height,
- elapsed_time, time_stamp, 0);
-}
-
// Add a square watermark near the left-low corner. clamp Y.
// Returns false on error.
bool WebRtcVideoFrame::AddWatermark() {
@@ -304,4 +234,75 @@
return true;
}
+bool WebRtcVideoFrame::Reset(uint32 format, int w, int h, int dw, int dh,
+ uint8* sample, size_t sample_size,
+ size_t pixel_width, size_t pixel_height,
+ int64 elapsed_time, int64 time_stamp,
+ int rotation) {
+ // WebRtcVideoFrame currently doesn't support color conversion or rotation.
+ // TODO: Add horizontal cropping support.
+ if (format != FOURCC_I420 || dw != w || dh < 0 || dh > abs(h) ||
+ rotation != 0) {
+ return false;
+ }
+
+ // Discard the existing buffer.
+ uint8* old_buffer;
+ size_t old_buffer_size;
+ Detach(&old_buffer, &old_buffer_size);
+ delete[] old_buffer;
+
+ // Set up a new buffer.
+ size_t desired_size = SizeOf(dw, dh);
+ uint8* buffer = new uint8[desired_size];
+ Attach(buffer, desired_size, dw, dh, pixel_width, pixel_height,
+ elapsed_time, time_stamp, rotation);
+
+ if (dh == h) {
+ // Uncropped
+ memcpy(buffer, sample, desired_size);
+ } else {
+ // Cropped
+ // TODO: use I420Copy which supports horizontal crop and vertical
+ // flip.
+ int horiz_crop = ((w - dw) / 2) & ~1;
+ int vert_crop = ((abs(h) - dh) / 2) & ~1;
+ int y_crop_offset = w * vert_crop + horiz_crop;
+ int halfwidth = (w + 1) / 2;
+ int halfheight = (h + 1) / 2;
+ int uv_size = GetChromaSize();
+ int uv_crop_offset = (halfwidth * vert_crop + horiz_crop) / 2;
+ uint8* src_y = sample + y_crop_offset;
+ uint8* src_u = sample + w * h + uv_crop_offset;
+ uint8* src_v = sample + w * h + halfwidth * halfheight + uv_crop_offset;
+ memcpy(GetYPlane(), src_y, dw * dh);
+ memcpy(GetUPlane(), src_u, uv_size);
+ memcpy(GetVPlane(), src_v, uv_size);
+ }
+
+ return true;
+}
+
+VideoFrame* WebRtcVideoFrame::CreateEmptyFrame(int w, int h,
+ size_t pixel_width,
+ size_t pixel_height,
+ int64 elapsed_time,
+ int64 time_stamp) const {
+ WebRtcVideoFrame* frame = new WebRtcVideoFrame();
+ frame->InitToEmptyBuffer(w, h, pixel_width, pixel_height,
+ elapsed_time, time_stamp);
+ return frame;
+}
+
+void WebRtcVideoFrame::InitToEmptyBuffer(int w, int h,
+ size_t pixel_width,
+ size_t pixel_height,
+ int64 elapsed_time,
+ int64 time_stamp) {
+ size_t buffer_size = VideoFrame::SizeOf(w, h);
+ uint8* buffer = new uint8[buffer_size];
+ Attach(buffer, buffer_size, w, h, pixel_width, pixel_height,
+ elapsed_time, time_stamp, 0);
+}
+
} // namespace cricket
diff --git a/talk/session/phone/webrtcvideoframe.h b/talk/session/phone/webrtcvideoframe.h
index 6519543..e041003 100644
--- a/talk/session/phone/webrtcvideoframe.h
+++ b/talk/session/phone/webrtcvideoframe.h
@@ -68,6 +68,11 @@
webrtc::VideoFrame* frame() { return &video_frame_; }
// From base class VideoFrame.
+ virtual bool Reset(uint32 format, int w, int h, int dw, int dh,
+ uint8* sample, size_t sample_size,
+ size_t pixel_width, size_t pixel_height,
+ int64 elapsed_time, int64 time_stamp, int rotation);
+
virtual size_t GetWidth() const;
virtual size_t GetHeight() const;
virtual const uint8* GetYPlane() const;
@@ -97,14 +102,17 @@
virtual bool MakeExclusive();
virtual size_t CopyToBuffer(uint8* buffer, size_t size) const;
virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, uint8* buffer,
- size_t size, size_t pitch_rgb) const;
- virtual VideoFrame* Stretch(size_t w, size_t h, bool interpolate,
- bool vert_crop) const;
+ size_t size, int stride_rgb) const;
private:
- size_t GetChromaSize() const { return GetUPitch() * GetChromaHeight(); }
- void CreateBuffer(int w, int h, size_t pixel_width, size_t pixel_height,
- int64 elapsed_time, int64 time_stamp);
+ virtual VideoFrame* CreateEmptyFrame(int w, int h,
+ size_t pixel_width, size_t pixel_height,
+ int64 elapsed_time,
+ int64 time_stamp) const;
+ void InitToEmptyBuffer(int w, int h,
+ size_t pixel_width, size_t pixel_height,
+ int64 elapsed_time, int64 time_stamp);
+
webrtc::VideoFrame video_frame_;
size_t pixel_width_;
size_t pixel_height_;
diff --git a/talk/session/phone/webrtcvideoframe_unittest.cc b/talk/session/phone/webrtcvideoframe_unittest.cc
index b115625..ea47a72 100644
--- a/talk/session/phone/webrtcvideoframe_unittest.cc
+++ b/talk/session/phone/webrtcvideoframe_unittest.cc
@@ -54,25 +54,44 @@
// TEST_WEBRTCVIDEOFRAME(ConstructCopyIsRef)
TEST_WEBRTCVIDEOFRAME(ConstructBlack)
// TODO: Implement Jpeg
-// TEST_LMIVIDEOFRAME(ConstructMjpgI420)
-// TEST_LMIVIDEOFRAME(ConstructMjpgI422)
-// TEST_LMIVIDEOFRAME(ConstructMjpgI444)
-// TEST_LMIVIDEOFRAME(ConstructMjpgI400)
+// TEST_WEBRTCVIDEOFRAME(ConstructMjpgI420)
+// TEST_WEBRTCVIDEOFRAME(ConstructMjpgI422)
+// TEST_WEBRTCVIDEOFRAME(ConstructMjpgI444)
+// TEST_WEBRTCVIDEOFRAME(ConstructMjpgI400)
// TODO: WebRtcVideoFrame does not support odd sizes.
// Re-evaluate once WebRTC switches to libyuv
-// TEST_LMIVIDEOFRAME(ConstructYuy2AllSizes)
-// TODO: WebRtcVideoFrame currently only supports ARGB output.
-#ifdef HAVE_YUV
-TEST_WEBRTCVIDEOFRAME(ConvertToBGRABuffer)
+// TEST_WEBRTCVIDEOFRAME(ConstructYuy2AllSizes)
+TEST_WEBRTCVIDEOFRAME(Reset)
TEST_WEBRTCVIDEOFRAME(ConvertToABGRBuffer)
-#endif
+TEST_WEBRTCVIDEOFRAME(ConvertToABGRBufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertToABGRBufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertToARGB1555Buffer)
+TEST_WEBRTCVIDEOFRAME(ConvertToARGB1555BufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertToARGB1555BufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertToARGB4444Buffer)
+TEST_WEBRTCVIDEOFRAME(ConvertToARGB4444BufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertToARGB4444BufferInverted)
TEST_WEBRTCVIDEOFRAME(ConvertToARGBBuffer)
-//TEST_LMIVIDEOFRAME(ConvertToRGB24Buffer)
-//TEST_LMIVIDEOFRAME(ConvertToRAWBuffer)
-//TEST_LMIVIDEOFRAME(ConvertToRGB565Buffer)
-//TEST_LMIVIDEOFRAME(ConvertToARGB1555Buffer)
-//TEST_LMIVIDEOFRAME(ConvertToARGB4444Buffer)
-//TEST_WEBRTCVIDEOFRAME(ConvertToYUY2Buffer)
+TEST_WEBRTCVIDEOFRAME(ConvertToARGBBufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertToARGBBufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertToBGRABuffer)
+TEST_WEBRTCVIDEOFRAME(ConvertToBGRABufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertToBGRABufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertToRAWBuffer)
+TEST_WEBRTCVIDEOFRAME(ConvertToRAWBufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertToRAWBufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertToRGB24Buffer)
+TEST_WEBRTCVIDEOFRAME(ConvertToRGB24BufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertToRGB24BufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertToRGB565Buffer)
+TEST_WEBRTCVIDEOFRAME(ConvertToRGB565BufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertToRGB565BufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertToYUY2Buffer)
+TEST_WEBRTCVIDEOFRAME(ConvertToYUY2BufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertToYUY2BufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertToUYVYBuffer)
+TEST_WEBRTCVIDEOFRAME(ConvertToUYVYBufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertToUYVYBufferInverted)
//TEST_WEBRTCVIDEOFRAME(ConvertToI422Buffer)
TEST_WEBRTCVIDEOFRAME(ConvertARGBToBayerGRBG)
TEST_WEBRTCVIDEOFRAME(ConvertARGBToBayerGBRG)
@@ -80,6 +99,8 @@
TEST_WEBRTCVIDEOFRAME(ConvertARGBToBayerRGGB)
TEST_WEBRTCVIDEOFRAME(CopyToBuffer)
TEST_WEBRTCVIDEOFRAME(CopyToBuffer1Pixel)
+//TEST_WEBRTCVIDEOFRAME(ConstructARGBBlackWhitePixel)
+
TEST_WEBRTCVIDEOFRAME(StretchToFrame)
TEST_WEBRTCVIDEOFRAME(Copy)
// TODO: WebRtcVideoFrame is not currently refcounted.
diff --git a/talk/session/phone/webrtcvoiceengine.cc b/talk/session/phone/webrtcvoiceengine.cc
index da9829a..58706f9 100644
--- a/talk/session/phone/webrtcvoiceengine.cc
+++ b/talk/session/phone/webrtcvoiceengine.cc
@@ -382,17 +382,23 @@
// First check whether there is a valid sound device for playback.
// TODO: Clean this up when we support setting the soundclip device.
#ifdef WIN32
- int num_of_devices = 0;
- if (voe_wrapper_sc_->hw()->GetNumOfPlayoutDevices(num_of_devices) != -1 &&
- num_of_devices > 0) {
- if (voe_wrapper_sc_->hw()->SetPlayoutDevice(kDefaultSoundclipDeviceId)
- == -1) {
- LOG_RTCERR1_EX(SetPlayoutDevice, kDefaultSoundclipDeviceId,
- voe_wrapper_sc_->error());
- return false;
+ // The SetPlayoutDevice may not be implemented in the case of external ADM.
+ // TODO: We should only check the adm_sc_ here, but current
+ // PeerConnection interface never set the adm_sc_, so need to check both
+ // in order to determine if the external adm is used.
+ if (!adm_ && !adm_sc_) {
+ int num_of_devices = 0;
+ if (voe_wrapper_sc_->hw()->GetNumOfPlayoutDevices(num_of_devices) != -1 &&
+ num_of_devices > 0) {
+ if (voe_wrapper_sc_->hw()->SetPlayoutDevice(kDefaultSoundclipDeviceId)
+ == -1) {
+ LOG_RTCERR1_EX(SetPlayoutDevice, kDefaultSoundclipDeviceId,
+ voe_wrapper_sc_->error());
+ return false;
+ }
+ } else {
+ LOG(LS_WARNING) << "No valid sound playout device found.";
}
- } else {
- LOG(LS_WARNING) << "No valid sound playout device found.";
}
#endif
diff --git a/talk/xmpp/hangoutpubsubclient.cc b/talk/xmpp/hangoutpubsubclient.cc
index 8f637fd..8db2d45 100644
--- a/talk/xmpp/hangoutpubsubclient.cc
+++ b/talk/xmpp/hangoutpubsubclient.cc
@@ -341,6 +341,10 @@
QN_PRESENTER_PRESENTATION_TYPE) != kNotPresenting);
return true;
}
+
+ virtual bool StatesEqual(bool state1, bool state2) {
+ return false; // Make every item trigger an event, even if state doesn't change.
+ }
};
HangoutPubSubClient::HangoutPubSubClient(XmppTaskParentInterface* parent,
diff --git a/talk/xmpp/hangoutpubsubclient_unittest.cc b/talk/xmpp/hangoutpubsubclient_unittest.cc
index f994992..a0c2439 100644
--- a/talk/xmpp/hangoutpubsubclient_unittest.cc
+++ b/talk/xmpp/hangoutpubsubclient_unittest.cc
@@ -278,7 +278,7 @@
xmpp_client->HandleStanza(
buzz::XmlElement::ForStr(incoming_presenter_resets_message));
EXPECT_EQ("presenting-nick", listener->last_presenter_nick);
- EXPECT_TRUE(listener->last_was_presenting);
+ //EXPECT_TRUE(listener->last_was_presenting);
EXPECT_FALSE(listener->last_is_presenting);
std::string incoming_presenter_retracts_message =
@@ -335,6 +335,12 @@
EXPECT_FALSE(listener->last_was_presenting);
EXPECT_TRUE(listener->last_is_presenting);
+ xmpp_client->HandleStanza(
+ buzz::XmlElement::ForStr(incoming_presenter_changes_message));
+ EXPECT_EQ("presenting-nick2", listener->last_presenter_nick);
+ EXPECT_TRUE(listener->last_was_presenting);
+ EXPECT_TRUE(listener->last_is_presenting);
+
std::string incoming_media_changes_message =
"<message xmlns='jabber:client' from='room@domain.com'>"
" <event xmlns='http://jabber.org/protocol/pubsub#event'>"