Update libjingle to 53856368.

R=mallinath@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/2366004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4941 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
wu@webrtc.org 2013-10-07 23:32:02 +00:00
parent e0d55a0782
commit 7818752566
42 changed files with 694 additions and 202 deletions

View File

@ -28,7 +28,7 @@
#include <string>
#include "talk/app/webrtc/webrtcsession.h"
#include "talk/app/webrtc/mediastreamprovider.h"
#include "talk/base/logging.h"
#include "talk/base/refcount.h"
@ -38,24 +38,29 @@ static size_t kMaxQueuedReceivedDataPackets = 100;
static size_t kMaxQueuedSendDataPackets = 100;
talk_base::scoped_refptr<DataChannel> DataChannel::Create(
WebRtcSession* session,
DataChannelProviderInterface* provider,
cricket::DataChannelType dct,
const std::string& label,
const DataChannelInit* config) {
talk_base::scoped_refptr<DataChannel> channel(
new talk_base::RefCountedObject<DataChannel>(session, label));
new talk_base::RefCountedObject<DataChannel>(provider, dct, label));
if (!channel->Init(config)) {
return NULL;
}
return channel;
}
DataChannel::DataChannel(WebRtcSession* session, const std::string& label)
DataChannel::DataChannel(
DataChannelProviderInterface* provider,
cricket::DataChannelType dct,
const std::string& label)
: label_(label),
observer_(NULL),
state_(kConnecting),
was_ever_writable_(false),
session_(session),
data_session_(NULL),
connected_to_provider_(false),
data_channel_type_(dct),
provider_(provider),
send_ssrc_set_(false),
send_ssrc_(0),
receive_ssrc_set_(false),
@ -64,7 +69,7 @@ DataChannel::DataChannel(WebRtcSession* session, const std::string& label)
bool DataChannel::Init(const DataChannelInit* config) {
if (config) {
if (session_->data_channel_type() == cricket::DCT_RTP &&
if (data_channel_type_ == cricket::DCT_RTP &&
(config->reliable ||
config->id != -1 ||
config->maxRetransmits != -1 ||
@ -72,7 +77,7 @@ bool DataChannel::Init(const DataChannelInit* config) {
LOG(LS_ERROR) << "Failed to initialize the RTP data channel due to "
<< "invalid DataChannelInit.";
return false;
} else if (session_->data_channel_type() == cricket::DCT_SCTP) {
} else if (data_channel_type_ == cricket::DCT_SCTP) {
if (config->id < -1 ||
config->maxRetransmits < -1 ||
config->maxRetransmitTime < -1) {
@ -111,7 +116,7 @@ void DataChannel::UnregisterObserver() {
}
bool DataChannel::reliable() const {
if (session_->data_channel_type() == cricket::DCT_RTP) {
if (data_channel_type_ == cricket::DCT_RTP) {
return false;
} else {
return config_.maxRetransmits == -1 &&
@ -165,14 +170,15 @@ void DataChannel::QueueControl(const talk_base::Buffer* buffer) {
}
bool DataChannel::SendControl(const talk_base::Buffer* buffer) {
if (data_channel_type_ == cricket::DCT_RTP) {
delete buffer;
return false;
}
if (state_ != kOpen) {
QueueControl(buffer);
return true;
}
if (session_->data_channel_type() == cricket::DCT_RTP) {
delete buffer;
return false;
}
cricket::SendDataParams send_params;
send_params.ssrc = config_.id;
@ -180,8 +186,7 @@ bool DataChannel::SendControl(const talk_base::Buffer* buffer) {
send_params.type = cricket::DMT_CONTROL;
cricket::SendDataResult send_result;
bool retval = session_->data_channel()->SendData(
send_params, *buffer, &send_result);
bool retval = provider_->SendData(send_params, *buffer, &send_result);
if (!retval && send_result == cricket::SDR_BLOCK) {
// Link is congested. Queue for later.
QueueControl(buffer);
@ -193,7 +198,7 @@ bool DataChannel::SendControl(const talk_base::Buffer* buffer) {
void DataChannel::SetReceiveSsrc(uint32 receive_ssrc) {
if (receive_ssrc_set_) {
ASSERT(session_->data_channel_type() == cricket::DCT_RTP ||
ASSERT(data_channel_type_ == cricket::DCT_RTP ||
!send_ssrc_set_ ||
receive_ssrc_ == send_ssrc_);
return;
@ -210,7 +215,7 @@ void DataChannel::RemotePeerRequestClose() {
void DataChannel::SetSendSsrc(uint32 send_ssrc) {
if (send_ssrc_set_) {
ASSERT(session_->data_channel_type() == cricket::DCT_RTP ||
ASSERT(data_channel_type_ == cricket::DCT_RTP ||
!receive_ssrc_set_ ||
receive_ssrc_ == send_ssrc_);
return;
@ -221,7 +226,7 @@ void DataChannel::SetSendSsrc(uint32 send_ssrc) {
}
// The underlaying data engine is closing.
// This function make sure the DataChannel is disconneced and change state to
// This function makes sure the DataChannel is disconnected and changes state to
// kClosed.
void DataChannel::OnDataEngineClose() {
DoClose();
@ -277,7 +282,7 @@ void DataChannel::UpdateState() {
switch (state_) {
case kConnecting: {
if (HasNegotiationCompleted()) {
if (!IsConnectedToDataSession()) {
if (!connected_to_provider_) {
ConnectToDataSession();
}
if (was_ever_writable_) {
@ -293,7 +298,7 @@ void DataChannel::UpdateState() {
break;
}
case kClosing: {
if (IsConnectedToDataSession()) {
if (connected_to_provider_) {
DisconnectFromDataSession();
}
if (HasNegotiationCompleted()) {
@ -314,28 +319,12 @@ void DataChannel::SetState(DataState state) {
}
void DataChannel::ConnectToDataSession() {
if (!session_->data_channel()) {
LOG(LS_ERROR) << "The DataEngine does not exist.";
ASSERT(session_->data_channel() != NULL);
return;
}
data_session_ = session_->data_channel();
data_session_->SignalReadyToSendData.connect(this,
&DataChannel::OnChannelReady);
data_session_->SignalDataReceived.connect(this, &DataChannel::OnDataReceived);
cricket::StreamParams params =
cricket::StreamParams::CreateLegacy(id());
data_session_->AddRecvStream(params);
data_session_->AddSendStream(params);
connected_to_provider_ = provider_->ConnectDataChannel(this);
}
void DataChannel::DisconnectFromDataSession() {
data_session_->RemoveSendStream(id());
data_session_->RemoveRecvStream(id());
data_session_->SignalReadyToSendData.disconnect(this);
data_session_->SignalDataReceived.disconnect(this);
data_session_ = NULL;
provider_->DisconnectDataChannel(this);
connected_to_provider_ = false;
}
void DataChannel::DeliverQueuedReceivedData() {
@ -409,15 +398,14 @@ bool DataChannel::InternalSendWithoutQueueing(
cricket::SendDataParams send_params;
send_params.ssrc = send_ssrc_;
if (session_->data_channel_type() == cricket::DCT_SCTP) {
if (data_channel_type_ == cricket::DCT_SCTP) {
send_params.ordered = config_.ordered;
send_params.max_rtx_count = config_.maxRetransmits;
send_params.max_rtx_ms = config_.maxRetransmitTime;
}
send_params.type = buffer.binary ? cricket::DMT_BINARY : cricket::DMT_TEXT;
return session_->data_channel()->SendData(send_params, buffer.data,
send_result);
return provider_->SendData(send_params, buffer.data, send_result);
}
bool DataChannel::QueueSendData(const DataBuffer& buffer) {

View File

@ -35,11 +35,24 @@
#include "talk/app/webrtc/proxy.h"
#include "talk/base/scoped_ref_ptr.h"
#include "talk/base/sigslot.h"
#include "talk/media/base/mediachannel.h"
#include "talk/session/media/channel.h"
namespace webrtc {
class WebRtcSession;
class DataChannel;
class DataChannelProviderInterface {
public:
virtual bool SendData(const cricket::SendDataParams& params,
const talk_base::Buffer& payload,
cricket::SendDataResult* result) = 0;
virtual bool ConnectDataChannel(DataChannel* data_channel) = 0;
virtual void DisconnectDataChannel(DataChannel* data_channel) = 0;
protected:
virtual ~DataChannelProviderInterface() {}
};
// DataChannel is a an implementation of the DataChannelInterface based on
// libjingle's data engine. It provides an implementation of unreliable data
@ -60,7 +73,8 @@ class DataChannel : public DataChannelInterface,
public sigslot::has_slots<> {
public:
static talk_base::scoped_refptr<DataChannel> Create(
WebRtcSession* session,
DataChannelProviderInterface* client,
cricket::DataChannelType dct,
const std::string& label,
const DataChannelInit* config);
@ -105,24 +119,26 @@ class DataChannel : public DataChannelInterface,
// underlying DataMediaChannel becomes ready, or when this channel is a new
// stream on an existing DataMediaChannel, and we've finished negotiation.
void OnChannelReady(bool writable);
protected:
DataChannel(WebRtcSession* session, const std::string& label);
virtual ~DataChannel();
bool Init(const DataChannelInit* config);
bool HasNegotiationCompleted();
// Sigslots from cricket::DataChannel
void OnDataReceived(cricket::DataChannel* channel,
const cricket::ReceiveDataParams& params,
const talk_base::Buffer& payload);
protected:
DataChannel(DataChannelProviderInterface* client,
cricket::DataChannelType dct,
const std::string& label);
virtual ~DataChannel();
bool Init(const DataChannelInit* config);
bool HasNegotiationCompleted();
private:
void DoClose();
void UpdateState();
void SetState(DataState state);
void DisconnectFromDataSession();
bool IsConnectedToDataSession() { return data_session_ != NULL; }
void DeliverQueuedControlData();
void QueueControl(const talk_base::Buffer* buffer);
void ClearQueuedControlData();
@ -139,8 +155,9 @@ class DataChannel : public DataChannelInterface,
DataChannelObserver* observer_;
DataState state_;
bool was_ever_writable_;
WebRtcSession* session_;
cricket::DataChannel* data_session_;
bool connected_to_provider_;
cricket::DataChannelType data_channel_type_;
DataChannelProviderInterface* provider_;
bool send_ssrc_set_;
uint32 send_ssrc_;
bool receive_ssrc_set_;

View File

@ -26,118 +26,78 @@
*/
#include "talk/app/webrtc/datachannel.h"
#include "talk/app/webrtc/jsep.h"
#include "talk/app/webrtc/mediastreamsignaling.h"
#include "talk/app/webrtc/test/fakeconstraints.h"
#include "talk/app/webrtc/test/fakedtlsidentityservice.h"
#include "talk/app/webrtc/webrtcsession.h"
#include "talk/base/gunit.h"
#include "talk/media/base/fakemediaengine.h"
#include "talk/media/devices/fakedevicemanager.h"
#include "talk/session/media/channelmanager.h"
using webrtc::CreateSessionDescriptionObserver;
using webrtc::MediaConstraintsInterface;
using webrtc::SessionDescriptionInterface;
using webrtc::DataChannel;
const uint32 kFakeSsrc = 1;
class CreateSessionDescriptionObserverForTest
: public talk_base::RefCountedObject<CreateSessionDescriptionObserver> {
class FakeDataChannelClient : public webrtc::DataChannelProviderInterface {
public:
virtual void OnSuccess(SessionDescriptionInterface* desc) {
description_.reset(desc);
FakeDataChannelClient() : send_blocked_(false) {}
virtual ~FakeDataChannelClient() {}
virtual bool SendData(const cricket::SendDataParams& params,
const talk_base::Buffer& payload,
cricket::SendDataResult* result) OVERRIDE {
if (send_blocked_) {
*result = cricket::SDR_BLOCK;
return false;
}
virtual void OnFailure(const std::string& error) {}
SessionDescriptionInterface* description() { return description_.get(); }
SessionDescriptionInterface* ReleaseDescription() {
return description_.release();
last_send_data_params_ = params;
return true;
}
virtual bool ConnectDataChannel(DataChannel* data_channel) OVERRIDE {
return true;
}
virtual void DisconnectDataChannel(DataChannel* data_channel) OVERRIDE {}
protected:
~CreateSessionDescriptionObserverForTest() {}
void set_send_blocked(bool blocked) { send_blocked_ = blocked; }
cricket::SendDataParams last_send_data_params() {
return last_send_data_params_;
}
private:
talk_base::scoped_ptr<SessionDescriptionInterface> description_;
cricket::SendDataParams last_send_data_params_;
bool send_blocked_;
};
class SctpDataChannelTest : public testing::Test {
protected:
SctpDataChannelTest()
: media_engine_(new cricket::FakeMediaEngine),
data_engine_(new cricket::FakeDataEngine),
channel_manager_(
new cricket::ChannelManager(media_engine_,
data_engine_,
new cricket::FakeDeviceManager(),
new cricket::CaptureManager(),
talk_base::Thread::Current())),
media_stream_signaling_(
new webrtc::MediaStreamSignaling(talk_base::Thread::Current(),
NULL, channel_manager_.get())),
session_(channel_manager_.get(),
talk_base::Thread::Current(),
talk_base::Thread::Current(),
NULL,
media_stream_signaling_.get()),
webrtc_data_channel_(NULL) {}
virtual void SetUp() {
if (!talk_base::SSLStreamAdapter::HaveDtlsSrtp()) {
return;
}
channel_manager_->Init();
webrtc::FakeConstraints constraints;
constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true);
constraints.AddMandatory(MediaConstraintsInterface::kEnableSctpDataChannels,
true);
ASSERT_TRUE(session_.Initialize(&constraints,
new FakeIdentityService()));
webrtc_data_channel_ = webrtc::DataChannel::Create(&session_, "test", NULL);
ASSERT_TRUE(media_stream_signaling_->AddDataChannel(webrtc_data_channel_));
talk_base::scoped_refptr<CreateSessionDescriptionObserverForTest> observer
= new CreateSessionDescriptionObserverForTest();
session_.CreateOffer(observer.get(), NULL);
EXPECT_TRUE_WAIT(observer->description() != NULL, 2000);
ASSERT_TRUE(observer->description() != NULL);
ASSERT_TRUE(session_.SetLocalDescription(observer->ReleaseDescription(),
NULL));
// Connect to the media channel.
webrtc_data_channel_->SetSendSsrc(kFakeSsrc);
webrtc_data_channel_->SetReceiveSsrc(kFakeSsrc);
session_.data_channel()->SignalReadyToSendData(true);
: webrtc_data_channel_(
DataChannel::Create(&client_, cricket::DCT_SCTP, "test", &init_)) {
}
void SetSendBlocked(bool blocked) {
bool was_blocked = data_engine_->GetChannel(0)->is_send_blocked();
data_engine_->GetChannel(0)->set_send_blocked(blocked);
if (!blocked && was_blocked) {
session_.data_channel()->SignalReadyToSendData(true);
void SetChannelReady() {
webrtc_data_channel_->OnChannelReady(true);
}
}
cricket::FakeMediaEngine* media_engine_;
cricket::FakeDataEngine* data_engine_;
talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_;
talk_base::scoped_ptr<webrtc::MediaStreamSignaling> media_stream_signaling_;
webrtc::WebRtcSession session_;
talk_base::scoped_refptr<webrtc::DataChannel> webrtc_data_channel_;
webrtc::DataChannelInit init_;
FakeDataChannelClient client_;
talk_base::scoped_refptr<DataChannel> webrtc_data_channel_;
};
// Tests the state of the data channel.
TEST_F(SctpDataChannelTest, StateTransition) {
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting,
webrtc_data_channel_->state());
SetChannelReady();
EXPECT_EQ(webrtc::DataChannelInterface::kOpen, webrtc_data_channel_->state());
webrtc_data_channel_->Close();
EXPECT_EQ(webrtc::DataChannelInterface::kClosed,
webrtc_data_channel_->state());
}
// Tests that DataChannel::buffered_amount() is correct after the channel is
// blocked.
TEST_F(SctpDataChannelTest, BufferedAmountWhenBlocked) {
if (!talk_base::SSLStreamAdapter::HaveDtlsSrtp()) {
return;
}
SetChannelReady();
webrtc::DataBuffer buffer("abcd");
EXPECT_TRUE(webrtc_data_channel_->Send(buffer));
EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount());
SetSendBlocked(true);
client_.set_send_blocked(true);
const int number_of_packets = 3;
for (int i = 0; i < number_of_packets; ++i) {
EXPECT_TRUE(webrtc_data_channel_->Send(buffer));
@ -149,13 +109,20 @@ TEST_F(SctpDataChannelTest, BufferedAmountWhenBlocked) {
// Tests that the queued data are sent when the channel transitions from blocked
// to unblocked.
TEST_F(SctpDataChannelTest, QueuedDataSentWhenUnblocked) {
if (!talk_base::SSLStreamAdapter::HaveDtlsSrtp()) {
return;
}
SetChannelReady();
webrtc::DataBuffer buffer("abcd");
SetSendBlocked(true);
client_.set_send_blocked(true);
EXPECT_TRUE(webrtc_data_channel_->Send(buffer));
SetSendBlocked(false);
client_.set_send_blocked(false);
SetChannelReady();
EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount());
}
// Tests that the queued control message is sent when channel is ready.
TEST_F(SctpDataChannelTest, QueuedControlMessageSent) {
talk_base::Buffer* buffer = new talk_base::Buffer("abcd", 4);
EXPECT_TRUE(webrtc_data_channel_->SendControl(buffer));
SetChannelReady();
EXPECT_EQ(cricket::DMT_CONTROL, client_.last_send_data_params().type);
}

View File

@ -172,8 +172,10 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration,
}
std::string address = tokens[1];
int port = kDefaultStunPort;
if (service_type == TURNS)
if (service_type == TURNS) {
port = kDefaultStunTlsPort;
turn_transport_type = kTcpTransportType;
}
if (tokens.size() > kMinIceUriTokens) {
if (!talk_base::FromString(tokens[2], &port)) {

View File

@ -61,6 +61,8 @@ static const char kTurnIceServerWithTransport[] =
"turn:test@hello.com?transport=tcp";
static const char kSecureTurnIceServer[] =
"turns:test@hello.com?transport=tcp";
static const char kSecureTurnIceServerWithoutTransportParam[] =
"turns:test_no_transport@hello.com";
static const char kTurnIceServerWithNoUsernameInUri[] =
"turn:test.com:1234";
static const char kTurnPassword[] = "turnpassword";
@ -242,6 +244,9 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) {
ice_server.uri = kSecureTurnIceServer;
ice_server.password = kTurnPassword;
ice_servers.push_back(ice_server);
ice_server.uri = kSecureTurnIceServerWithoutTransportParam;
ice_server.password = kTurnPassword;
ice_servers.push_back(ice_server);
talk_base::scoped_refptr<PeerConnectionInterface> pc(
factory_->CreatePeerConnection(ice_servers, NULL,
allocator_factory_.get(),
@ -249,9 +254,14 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) {
&observer_));
EXPECT_TRUE(pc.get() != NULL);
TurnConfigurations turn_configs;
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn(
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1(
"hello.com", kDefaultStunTlsPort, "test", kTurnPassword, "tcp", true);
turn_configs.push_back(turn);
turn_configs.push_back(turn1);
// TURNS with transport param should be default to tcp.
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn2(
"hello.com", kDefaultStunTlsPort, "test_no_transport",
kTurnPassword, "tcp", true);
turn_configs.push_back(turn2);
VerifyTurnConfigurations(turn_configs);
}

View File

@ -145,6 +145,7 @@ static const char kAttributeFingerprint[] = "fingerprint";
static const char kAttributeSetup[] = "setup";
static const char kAttributeFmtp[] = "fmtp";
static const char kAttributeRtpmap[] = "rtpmap";
static const char kAttributeSctpmap[] = "sctpmap";
static const char kAttributeRtcp[] = "rtcp";
static const char kAttributeIceUfrag[] = "ice-ufrag";
static const char kAttributeIcePwd[] = "ice-pwd";
@ -210,7 +211,7 @@ static const int kIsacWbDefaultRate = 32000; // From acm_common_defs.h
static const int kIsacSwbDefaultRate = 56000; // From acm_common_defs.h
static const int kDefaultSctpFmt = 5000;
static const char kDefaultSctpFmtProtocol[] = "webrtc-datachannel";
static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
struct SsrcInfo {
SsrcInfo()
@ -1268,10 +1269,14 @@ void BuildMediaDescription(const ContentInfo* content_info,
}
void BuildSctpContentAttributes(std::string* message) {
cricket::DataCodec sctp_codec(kDefaultSctpFmt, kDefaultSctpFmtProtocol, 0);
sctp_codec.SetParam(kCodecParamSctpProtocol, kDefaultSctpFmtProtocol);
sctp_codec.SetParam(kCodecParamSctpStreams, cricket::kMaxSctpSid + 1);
AddFmtpLine(sctp_codec, message);
// draft-ietf-mmusic-sctp-sdp-04
// a=sctpmap:sctpmap-number protocol [streams]
std::ostringstream os;
InitAttrLine(kAttributeSctpmap, &os);
os << kSdpDelimiterColon << kDefaultSctpFmt << kSdpDelimiterSpace
<< kDefaultSctpmapProtocol << kSdpDelimiterSpace
<< (cricket::kMaxSctpSid + 1);
AddLine(os.str(), message);
}
void BuildRtpContentAttributes(
@ -2111,10 +2116,25 @@ bool ParseMediaDescription(const std::string& message,
codec_preference,
static_cast<AudioContentDescription*>(content.get()));
} else if (HasAttribute(line, kMediaTypeData)) {
content.reset(ParseContentDescription<DataContentDescription>(
DataContentDescription* desc =
ParseContentDescription<DataContentDescription>(
message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
codec_preference, pos, &content_name,
&transport, candidates, error));
&transport, candidates, error);
if (protocol == cricket::kMediaProtocolDtlsSctp) {
// Add the SCTP Port number as a pseudo-codec "port" parameter
cricket::DataCodec codec_port(
cricket::kGoogleSctpDataCodecId, cricket::kGoogleSctpDataCodecName,
0);
codec_port.SetParam(cricket::kCodecParamPort, fields[3]);
LOG(INFO) << "ParseMediaDescription: Got SCTP Port Number "
<< fields[3];
desc->AddCodec(codec_port);
}
content.reset(desc);
// We should always use the default bandwidth for RTP-based data
// channels. Don't allow SDP to set the bandwidth, because that
// would give JS the opportunity to "break the Internet".

View File

@ -76,6 +76,7 @@ using webrtc::SessionDescriptionInterface;
typedef std::vector<AudioCodec> AudioCodecs;
typedef std::vector<Candidate> Candidates;
static const uint32 kDefaultSctpPort = 5000;
static const char kSessionTime[] = "t=0 0\r\n";
static const uint32 kCandidatePriority = 2130706432U; // pref = 1.0
static const char kCandidateUfragVoice[] = "ufrag_voice";
@ -281,7 +282,7 @@ static const char kSdpSctpDataChannelString[] =
"a=ice-ufrag:ufrag_data\r\n"
"a=ice-pwd:pwd_data\r\n"
"a=mid:data_content_name\r\n"
"a=fmtp:5000 protocol=webrtc-datachannel; streams=65536\r\n";
"a=sctpmap:5000 webrtc-datachannel 65536\r\n";
static const char kSdpSctpDataChannelWithCandidatesString[] =
"m=application 2345 DTLS/SCTP 5000\r\n"
@ -296,7 +297,7 @@ static const char kSdpSctpDataChannelWithCandidatesString[] =
"a=ice-ufrag:ufrag_data\r\n"
"a=ice-pwd:pwd_data\r\n"
"a=mid:data_content_name\r\n"
"a=fmtp:5000 protocol=webrtc-datachannel; streams=65536\r\n";
"a=sctpmap:5000 webrtc-datachannel 65536\r\n";
// One candidate reference string as per W3c spec.
@ -974,6 +975,10 @@ class WebRtcSdpTest : public testing::Test {
new DataContentDescription());
data_desc_ = data.get();
data_desc_->set_protocol(cricket::kMediaProtocolDtlsSctp);
DataCodec codec(cricket::kGoogleSctpDataCodecId,
cricket::kGoogleSctpDataCodecName, 0);
codec.SetParam(cricket::kCodecParamPort, kDefaultSctpPort);
data_desc_->AddCodec(codec);
desc_.AddContent(kDataContentName, NS_JINGLE_DRAFT_SCTP, data.release());
EXPECT_TRUE(desc_.AddTransportInfo(
TransportInfo(kDataContentName,
@ -1761,6 +1766,41 @@ TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannels) {
EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
}
TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndNewPort) {
AddSctpDataChannel();
const uint16 kUnusualSctpPort = 9556;
char default_portstr[16];
char unusual_portstr[16];
talk_base::sprintfn(default_portstr, sizeof(default_portstr), "%d",
kDefaultSctpPort);
talk_base::sprintfn(unusual_portstr, sizeof(unusual_portstr), "%d",
kUnusualSctpPort);
JsepSessionDescription jdesc(kDummyString);
// take our pre-built session description and change the SCTP port.
cricket::SessionDescription* mutant = desc_.Copy();
DataContentDescription* dcdesc = static_cast<DataContentDescription*>(
mutant->GetContentDescriptionByName(kDataContentName));
std::vector<cricket::DataCodec> codecs(dcdesc->codecs());
EXPECT_EQ(codecs.size(), 1UL);
EXPECT_EQ(codecs[0].id, cricket::kGoogleSctpDataCodecId);
codecs[0].SetParam(cricket::kCodecParamPort, kUnusualSctpPort);
// note: mutant's owned by jdesc now.
ASSERT_TRUE(jdesc.Initialize(mutant, kSessionId, kSessionVersion));
mutant = NULL;
std::string sdp_with_data = kSdpString;
sdp_with_data.append(kSdpSctpDataChannelString);
talk_base::replace_substrs(default_portstr, strlen(default_portstr),
unusual_portstr, strlen(unusual_portstr),
&sdp_with_data);
JsepSessionDescription jdesc_output(kDummyString);
EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
}
TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannelsAndBandwidth) {
AddRtpDataChannel();
JsepSessionDescription jdesc(kDummyString);

View File

@ -927,6 +927,40 @@ sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
return &SignalVoiceChannelDestroyed;
}
bool WebRtcSession::SendData(const cricket::SendDataParams& params,
const talk_base::Buffer& payload,
cricket::SendDataResult* result) {
if (!data_channel_.get()) {
LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
return false;
}
return data_channel_->SendData(params, payload, result);
}
bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) {
if (!data_channel_.get()) {
LOG(LS_ERROR) << "ConnectDataChannel called when data_channel_ is NULL.";
return false;
}
data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
&DataChannel::OnChannelReady);
data_channel_->SignalDataReceived.connect(webrtc_data_channel,
&DataChannel::OnDataReceived);
cricket::StreamParams params =
cricket::StreamParams::CreateLegacy(webrtc_data_channel->id());
data_channel_->AddRecvStream(params);
data_channel_->AddSendStream(params);
return true;
}
void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
data_channel_->RemoveSendStream(webrtc_data_channel->id());
data_channel_->RemoveRecvStream(webrtc_data_channel->id());
data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
}
talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
const std::string& label,
const DataChannelInit* config) {
@ -953,7 +987,7 @@ talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
}
talk_base::scoped_refptr<DataChannel> channel(
DataChannel::Create(this, label, &new_config));
DataChannel::Create(this, data_channel_type_, label, &new_config));
if (channel == NULL)
return NULL;
if (!mediastream_signaling_->AddDataChannel(channel))

View File

@ -99,7 +99,8 @@ class WebRtcSession : public cricket::BaseSession,
public AudioProviderInterface,
public DataChannelFactory,
public VideoProviderInterface,
public DtmfProviderInterface {
public DtmfProviderInterface,
public DataChannelProviderInterface {
public:
WebRtcSession(cricket::ChannelManager* channel_manager,
talk_base::Thread* signaling_thread,
@ -181,6 +182,14 @@ class WebRtcSession : public cricket::BaseSession,
int code, int duration);
virtual sigslot::signal0<>* GetOnDestroyedSignal();
// Implements DataChannelProviderInterface.
virtual bool SendData(const cricket::SendDataParams& params,
const talk_base::Buffer& payload,
cricket::SendDataResult* result) OVERRIDE;
virtual bool ConnectDataChannel(DataChannel* webrtc_data_channel) OVERRIDE;
virtual void DisconnectDataChannel(DataChannel* webrtc_data_channel) OVERRIDE;
talk_base::scoped_refptr<DataChannel> CreateDataChannel(
const std::string& label,
const DataChannelInit* config);

View File

@ -67,6 +67,9 @@ class LateBindingSymbolTable {
bool LoadFromPath(const char *dll_path);
void Unload();
// Gets the raw OS handle to the DLL. Be careful what you do with it.
DllHandle GetDllHandle() const { return handle_; }
private:
void ClearSymbols();

View File

@ -123,8 +123,6 @@ LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev,
LogErrorContext err_ctx, int err, const char* module)
: severity_(sev),
warn_slow_logs_delay_(WARN_SLOW_LOGS_DELAY) {
// Android's logging facility keeps track of timestamp and thread.
#ifndef ANDROID
if (timestamp_) {
uint32 time = TimeSince(LogStartTime());
// Also ensure WallClockStartTime is initialized, so that it matches
@ -141,7 +139,6 @@ LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev,
print_stream_ << "[" << std::hex << id << std::dec << "] ";
#endif // WIN32
}
#endif // !ANDROID
if (severity_ >= ctx_sev_) {
print_stream_ << Describe(sev) << "(" << DescribeFile(file)

View File

@ -41,6 +41,8 @@
// LOG_V(sev) Like LOG(), but sev is a run-time variable of the LoggingSeverity
// type (basically, it just doesn't prepend the namespace).
// LOG_F(sev) Like LOG(), but includes the name of the current function.
// LOG_T(sev) Like LOG(), but includes the this pointer.
// LOG_T_F(sev) Like LOG_F(), but includes the this pointer.
// LOG_GLE(M)(sev [, mod]) attempt to add a string description of the
// HRESULT returned by GetLastError. The "M" variant allows searching of a
// DLL's string table for the error description.
@ -328,6 +330,9 @@ inline bool LogCheckLevel(LoggingSeverity sev) {
talk_base::ERRCTX_ ## ctx, err , ##__VA_ARGS__) \
.stream()
#define LOG_T(sev) LOG(sev) << this << ": "
#define LOG_T_F(level) LOG_F(level) << this << ": "
#else // !LOGGING
// Hopefully, the compiler will optimize away some of this code.
@ -348,6 +353,8 @@ inline bool LogCheckLevel(LoggingSeverity sev) {
talk_base::ERRCTX_ ## ctx, err , ##__VA_ARGS__) \
.stream()
#define LOG_T(sev) LOG(sev) << this << ": "
#define LOG_T_F(level) LOG_F(level) << this << " "
#endif // !LOGGING
#define LOG_ERRNO_EX(sev, err) \

View File

@ -38,7 +38,7 @@ std::string Md5(const std::string& input) {
TEST(Md5DigestTest, TestSize) {
Md5Digest md5;
EXPECT_EQ(16U, Md5Digest::kSize);
EXPECT_EQ(16, static_cast<int>(Md5Digest::kSize));
EXPECT_EQ(16U, md5.Size());
}

View File

@ -1,6 +1,6 @@
/*
* libjingle
* Copyright 2004--2011, Google Inc.
* Copyright 2004 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -37,6 +37,9 @@
#endif
#endif
#include "talk/base/gunit.h"
#ifdef WIN32
#include "talk/base/logging.h" // For LOG_GLE
#endif
namespace talk_base {
@ -130,7 +133,7 @@ TEST_F(NetworkTest, TestCreateNetworks) {
IPAddress ip = (*it)->ip();
SocketAddress bindaddress(ip, 0);
bindaddress.SetScopeID((*it)->scope_id());
// TODO: Make this use talk_base::AsyncSocket once it supports IPv6.
// TODO(thaloun): Use talk_base::AsyncSocket once it supports IPv6.
int fd = static_cast<int>(socket(ip.family(), SOCK_STREAM, IPPROTO_TCP));
if (fd > 0) {
size_t ipsize = bindaddress.ToSockAddrStorage(&storage);
@ -138,6 +141,9 @@ TEST_F(NetworkTest, TestCreateNetworks) {
int success = ::bind(fd,
reinterpret_cast<sockaddr*>(&storage),
static_cast<int>(ipsize));
#ifdef WIN32
if (success) LOG_GLE(LS_ERROR) << "Socket bind failed.";
#endif
EXPECT_EQ(0, success);
#ifdef WIN32
closesocket(fd);
@ -486,7 +492,7 @@ TEST_F(NetworkTest, TestIPv6Toggle) {
NetworkManager::NetworkList list;
#ifndef WIN32
// There should be at least one IPv6 network (fe80::/64 should be in there).
// TODO: Disabling this test on windows for the moment as the test
// TODO(thaloun): Disabling this test on windows for the moment as the test
// machines don't seem to have IPv6 installed on them at all.
manager.set_ipv6_enabled(true);
list = GetNetworks(manager, true);
@ -534,5 +540,49 @@ TEST_F(NetworkTest, TestConvertIfAddrsNoAddress) {
}
#endif // defined(POSIX)
#if defined(LINUX)
// If you want to test non-default routes, you can do the following on a linux
// machine:
// 1) Load the dummy network driver:
// sudo modprobe dummy
// sudo ifconfig dummy0 127.0.0.1
// 2) Run this test and confirm the output says it found a dummy route (and
// passes).
// 3) When done:
// sudo rmmmod dummy
TEST_F(NetworkTest, TestIgnoreNonDefaultRoutes) {
BasicNetworkManager manager;
NetworkManager::NetworkList list;
list = GetNetworks(manager, false);
bool found_dummy = false;
LOG(LS_INFO) << "Looking for dummy network: ";
for (NetworkManager::NetworkList::iterator it = list.begin();
it != list.end(); ++it) {
LOG(LS_INFO) << " Network name: " << (*it)->name();
found_dummy |= (*it)->name().find("dummy0") != std::string::npos;
}
for (NetworkManager::NetworkList::iterator it = list.begin();
it != list.end(); ++it) {
delete (*it);
}
if (!found_dummy) {
LOG(LS_INFO) << "No dummy found, quitting.";
return;
}
LOG(LS_INFO) << "Found dummy, running again while ignoring non-default "
<< "routes.";
manager.set_ignore_non_default_routes(true);
list = GetNetworks(manager, false);
for (NetworkManager::NetworkList::iterator it = list.begin();
it != list.end(); ++it) {
LOG(LS_INFO) << " Network name: " << (*it)->name();
EXPECT_TRUE((*it)->name().find("dummy0") == std::string::npos);
}
for (NetworkManager::NetworkList::iterator it = list.begin();
it != list.end(); ++it) {
delete (*it);
}
}
#endif
} // namespace talk_base

View File

@ -234,7 +234,7 @@ bool OpenSSLAdapter::InitializeSSL(VerificationCallback callback) {
if (!InitializeSSLThread() || !SSL_library_init())
return false;
#if !defined(ADDRESS_SANITIZER) || !defined(OSX)
// Loading the error strings crashed mac_asan. Omit this debugging aid there.
// Loading the error strings crashes mac_asan. Omit this debugging aid there.
SSL_load_error_strings();
#endif
ERR_load_BIO_strings();

View File

@ -130,12 +130,7 @@ void Profiler::ReportToLog(const char* file, int line,
for (iterator it = events_.begin(); it != events_.end(); ++it) {
if (event_prefix.empty() || it->first.find(event_prefix) == 0) {
LogMessage(file, line, severity_to_use).stream()
<< it->first << " count=" << it->second.event_count()
<< " total=" << FormattedTime(it->second.total_time())
<< " mean=" << FormattedTime(it->second.mean())
<< " min=" << FormattedTime(it->second.minimum())
<< " max=" << FormattedTime(it->second.maximum())
<< " sd=" << it->second.standard_deviation();
<< it->first << " " << it->second;
}
}
LogMessage(file, line, severity_to_use).stream()
@ -168,4 +163,15 @@ bool Profiler::Clear() {
return result;
}
std::ostream& operator<<(std::ostream& stream,
const ProfilerEvent& profiler_event) {
stream << "count=" << profiler_event.event_count()
<< " total=" << FormattedTime(profiler_event.total_time())
<< " mean=" << FormattedTime(profiler_event.mean())
<< " min=" << FormattedTime(profiler_event.minimum())
<< " max=" << FormattedTime(profiler_event.maximum())
<< " sd=" << profiler_event.standard_deviation();
return stream;
}
} // namespace talk_base

View File

@ -164,6 +164,9 @@ class ProfilerScope {
DISALLOW_COPY_AND_ASSIGN(ProfilerScope);
};
std::ostream& operator<<(std::ostream& stream,
const ProfilerEvent& profiler_event);
} // namespace talk_base
#endif // TALK_BASE_PROFILER_H_

View File

@ -38,7 +38,7 @@ std::string Sha1(const std::string& input) {
TEST(Sha1DigestTest, TestSize) {
Sha1Digest sha1;
EXPECT_EQ(20U, Sha1Digest::kSize);
EXPECT_EQ(20, static_cast<int>(Sha1Digest::kSize));
EXPECT_EQ(20U, sha1.Size());
}

View File

@ -523,6 +523,106 @@ void FileStream::DoClose() {
fclose(file_);
}
CircularFileStream::CircularFileStream(size_t max_size)
: max_write_size_(max_size),
position_(0),
marked_position_(max_size / 2),
last_write_position_(0),
read_segment_(READ_LATEST),
read_segment_available_(0) {
}
bool CircularFileStream::Open(
const std::string& filename, const char* mode, int* error) {
if (!FileStream::Open(filename.c_str(), mode, error))
return false;
if (strchr(mode, "r") != NULL) { // Opened in read mode.
// Check if the buffer has been overwritten and determine how to read the
// log in time sequence.
size_t file_size;
GetSize(&file_size);
if (file_size == position_) {
// The buffer has not been overwritten yet. Read 0 .. file_size
read_segment_ = READ_LATEST;
read_segment_available_ = file_size;
} else {
// The buffer has been over written. There are three segments: The first
// one is 0 .. marked_position_, which is the marked earliest log. The
// second one is position_ .. file_size, which is the middle log. The
// last one is marked_position_ .. position_, which is the latest log.
read_segment_ = READ_MARKED;
read_segment_available_ = marked_position_;
last_write_position_ = position_;
}
// Read from the beginning.
position_ = 0;
SetPosition(position_);
}
return true;
}
StreamResult CircularFileStream::Read(void* buffer, size_t buffer_len,
size_t* read, int* error) {
if (read_segment_available_ == 0) {
size_t file_size;
switch (read_segment_) {
case READ_MARKED: // Finished READ_MARKED and start READ_MIDDLE.
read_segment_ = READ_MIDDLE;
position_ = last_write_position_;
SetPosition(position_);
GetSize(&file_size);
read_segment_available_ = file_size - position_;
break;
case READ_MIDDLE: // Finished READ_MIDDLE and start READ_LATEST.
read_segment_ = READ_LATEST;
position_ = marked_position_;
SetPosition(position_);
read_segment_available_ = last_write_position_ - position_;
break;
default: // Finished READ_LATEST and return EOS.
return talk_base::SR_EOS;
}
}
size_t local_read;
if (!read) read = &local_read;
size_t to_read = talk_base::_min(buffer_len, read_segment_available_);
talk_base::StreamResult result
= talk_base::FileStream::Read(buffer, to_read, read, error);
if (result == talk_base::SR_SUCCESS) {
read_segment_available_ -= *read;
position_ += *read;
}
return result;
}
StreamResult CircularFileStream::Write(const void* data, size_t data_len,
size_t* written, int* error) {
if (position_ >= max_write_size_) {
ASSERT(position_ == max_write_size_);
position_ = marked_position_;
SetPosition(position_);
}
size_t local_written;
if (!written) written = &local_written;
size_t to_eof = max_write_size_ - position_;
size_t to_write = talk_base::_min(data_len, to_eof);
talk_base::StreamResult result
= talk_base::FileStream::Write(data, to_write, written, error);
if (result == talk_base::SR_SUCCESS) {
position_ += *written;
}
return result;
}
AsyncWriteStream::~AsyncWriteStream() {
write_thread_->Clear(this, 0, NULL);
ClearBufferAndWrite();

View File

@ -468,6 +468,35 @@ class FileStream : public StreamInterface {
DISALLOW_EVIL_CONSTRUCTORS(FileStream);
};
// A stream that caps the output at a certain size, dropping content from the
// middle of the logical stream and maintaining equal parts of the start/end of
// the logical stream.
class CircularFileStream : public FileStream {
public:
explicit CircularFileStream(size_t max_size);
virtual bool Open(const std::string& filename, const char* mode, int* error);
virtual StreamResult Read(void* buffer, size_t buffer_len,
size_t* read, int* error);
virtual StreamResult Write(const void* data, size_t data_len,
size_t* written, int* error);
private:
enum ReadSegment {
READ_MARKED, // Read 0 .. marked_position_
READ_MIDDLE, // Read position_ .. file_size
READ_LATEST, // Read marked_position_ .. position_ if the buffer was
// overwritten or 0 .. position_ otherwise.
};
size_t max_write_size_;
size_t position_;
size_t marked_position_;
size_t last_write_position_;
ReadSegment read_segment_;
size_t read_segment_available_;
};
// A stream which pushes writes onto a separate thread and
// returns from the write call immediately.

View File

@ -159,7 +159,6 @@ class Functor2 {
bool* flag_;
};
// See: https://code.google.com/p/webrtc/issues/detail?id=2409
TEST(ThreadTest, DISABLED_Main) {
const SocketAddress addr("127.0.0.1", 0);

View File

@ -85,6 +85,7 @@ const char* kRtcpFbCcmParamFir = "fir";
const char* kCodecParamMaxBitrate = "x-google-max-bitrate";
const char* kCodecParamMinBitrate = "x-google-min-bitrate";
const char* kCodecParamMaxQuantization = "x-google-max-quantization";
const char* kCodecParamPort = "x-google-port";
const int kGoogleRtpDataCodecId = 101;
const char kGoogleRtpDataCodecName[] = "google-data";

View File

@ -99,6 +99,7 @@ extern const char* kRtcpFbCcmParamFir;
extern const char* kCodecParamMaxBitrate;
extern const char* kCodecParamMinBitrate;
extern const char* kCodecParamMaxQuantization;
extern const char* kCodecParamPort;
// We put the data codec names here so callers of
// DataEngine::CreateChannel don't have to import rtpdataengine.h or

View File

@ -837,6 +837,9 @@ class FakeVideoEngine : public FakeBaseEngine {
default_encoder_config_ = config;
return true;
}
VideoEncoderConfig GetDefaultEncoderConfig() const {
return default_encoder_config_;
}
const VideoEncoderConfig& default_encoder_config() const {
return default_encoder_config_;
}

View File

@ -93,6 +93,9 @@ class FileMediaEngine : public MediaEngineInterface {
virtual bool SetDefaultVideoEncoderConfig(const VideoEncoderConfig& config) {
return true;
}
virtual VideoEncoderConfig GetDefaultVideoEncoderConfig() const {
return VideoEncoderConfig();
}
virtual bool SetSoundDevices(const Device* in_dev, const Device* out_dev) {
return true;
}

View File

@ -204,6 +204,20 @@ class HybridVideoEngine : public HybridVideoEngineInterface {
}
return true;
}
VideoEncoderConfig GetDefaultEncoderConfig() const {
// This looks pretty strange, but, in practice, it'll do sane things if
// GetDefaultEncoderConfig is only called after SetDefaultEncoderConfig,
// since both engines should be essentially equivalent at that point. If it
// hasn't been called, though, we'll use the first meaningful encoder
// config, or the config from the second video engine if neither are
// meaningful.
VideoEncoderConfig config = video1_.GetDefaultEncoderConfig();
if (config.max_codec.width != 0) {
return config;
} else {
return video2_.GetDefaultEncoderConfig();
}
}
const std::vector<VideoCodec>& codecs() const {
return codecs_;
}

View File

@ -98,6 +98,10 @@ class MediaEngineInterface {
// and encode video.
virtual bool SetDefaultVideoEncoderConfig(const VideoEncoderConfig& config)
= 0;
// Gets the default (maximum) codec/resolution and encoder option used to
// capture and encode video, as set by SetDefaultVideoEncoderConfig or the
// default from the video engine if not previously set.
virtual VideoEncoderConfig GetDefaultVideoEncoderConfig() const = 0;
// Device selection
// TODO(tschmelcher): Add method for selecting the soundclip device.
@ -203,6 +207,9 @@ class CompositeMediaEngine : public MediaEngineInterface {
virtual bool SetDefaultVideoEncoderConfig(const VideoEncoderConfig& config) {
return video_.SetDefaultEncoderConfig(config);
}
virtual VideoEncoderConfig GetDefaultVideoEncoderConfig() const {
return video_.GetDefaultEncoderConfig();
}
virtual bool SetSoundDevices(const Device* in_device,
const Device* out_device) {
@ -327,6 +334,9 @@ class NullVideoEngine {
return NULL;
}
bool SetOptions(const VideoOptions& options) { return true; }
VideoEncoderConfig GetDefaultEncoderConfig() const {
return VideoEncoderConfig();
}
bool SetDefaultEncoderConfig(const VideoEncoderConfig& config) {
return true;
}

View File

@ -27,6 +27,10 @@
#include "talk/media/devices/libudevsymboltable.h"
#include <dlfcn.h>
#include "talk/base/logging.h"
namespace cricket {
#define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME LIBUDEV_SYMBOLS_CLASS_NAME
@ -37,4 +41,30 @@ namespace cricket {
#undef LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST
#undef LATE_BINDING_SYMBOL_TABLE_DLL_NAME
bool IsWrongLibUDevAbiVersion(talk_base::DllHandle libudev_0) {
talk_base::DllHandle libudev_1 = dlopen("libudev.so.1", RTLD_NOW|RTLD_NOLOAD);
bool unsafe_symlink = (libudev_0 == libudev_1);
if (unsafe_symlink) {
// .0 and .1 are distinct ABIs, so if they point to the same thing then one
// of them must be wrong. Probably the old has been symlinked to the new in
// a misguided attempt at backwards compatibility.
LOG(LS_ERROR) << "libudev.so.0 and libudev.so.1 unsafely point to the"
" same thing; not using libudev";
} else if (libudev_1) {
// If libudev.so.1 is resident but distinct from libudev.so.0, then some
// system library loaded the new ABI separately. This is not a problem for
// LateBindingSymbolTable because its symbol look-ups are restricted to its
// DllHandle, but having libudev.so.0 resident may cause problems for that
// system library because symbol names are not namespaced by DLL.
LOG(LS_WARNING)
<< "libudev.so.1 is resident but distinct from libudev.so.0";
}
if (libudev_1) {
// Release the refcount that we acquired above. (Does not unload the DLL;
// whoever loaded it still needs it.)
dlclose(libudev_1);
}
return unsafe_symlink;
}
} // namespace cricket

View File

@ -66,6 +66,14 @@ namespace cricket {
#undef LATE_BINDING_SYMBOL_TABLE_CLASS_NAME
#undef LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST
// libudev has changed ABIs to libudev.so.1 in recent distros and lots of users
// and/or software (including Google Chrome) are symlinking the old to the new.
// The entire point of ABI versions is that you can't safely do that, and
// it has caused crashes in the wild. This function checks if the DllHandle that
// we got back for libudev.so.0 is actually for libudev.so.1. If so, the library
// cannot safely be used.
bool IsWrongLibUDevAbiVersion(talk_base::DllHandle libudev_0);
} // namespace cricket
#endif // TALK_MEDIA_DEVICES_LIBUDEVSYMBOLTABLE_H_

View File

@ -52,7 +52,8 @@ class ScopedLibUdev {
ScopedLibUdev() {}
bool Init() {
return libudev_.Load();
return libudev_.Load() &&
!IsWrongLibUDevAbiVersion(libudev_.GetDllHandle());
}
LibUDevSymbolTable libudev_;

View File

@ -306,8 +306,9 @@ bool LinuxDeviceWatcher::Start() {
// We deliberately return true in the failure paths here because libudev is
// not a critical component of a Linux system so it may not be present/usable,
// and we don't want to halt LinuxDeviceManager initialization in such a case.
if (!libudev_.Load()) {
LOG(LS_WARNING) << "libudev not present/usable; LinuxDeviceWatcher disabled";
if (!libudev_.Load() || IsWrongLibUDevAbiVersion(libudev_.GetDllHandle())) {
LOG(LS_WARNING)
<< "libudev not present/usable; LinuxDeviceWatcher disabled";
return true;
}
udev_ = libudev_.udev_new()();

View File

@ -242,8 +242,8 @@ DataMediaChannel* SctpDataEngine::CreateChannel(
SctpDataMediaChannel::SctpDataMediaChannel(talk_base::Thread* thread)
: worker_thread_(thread),
local_port_(kSctpDefaultPort),
remote_port_(kSctpDefaultPort),
local_port_(-1),
remote_port_(-1),
sock_(NULL),
sending_(false),
receiving_(false),
@ -344,6 +344,12 @@ void SctpDataMediaChannel::CloseSctpSocket() {
bool SctpDataMediaChannel::Connect() {
LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
if (remote_port_ < 0) {
remote_port_ = kSctpDefaultPort;
}
if (local_port_ < 0) {
local_port_ = kSctpDefaultPort;
}
// If we already have a socket connection, just return.
if (sock_) {
@ -677,6 +683,38 @@ void SctpDataMediaChannel::OnNotificationAssocChange(
}
}
// Puts the specified |param| from the codec identified by |id| into |dest|
// and returns true. Or returns false if it wasn't there, leaving |dest|
// untouched.
static bool GetCodecIntParameter(const std::vector<DataCodec>& codecs,
int id, const std::string& name,
const std::string& param, int* dest) {
std::string value;
Codec match_pattern;
match_pattern.id = id;
match_pattern.name = name;
for (size_t i = 0; i < codecs.size(); ++i) {
if (codecs[i].Matches(match_pattern)) {
if (codecs[i].GetParam(param, &value)) {
*dest = talk_base::FromString<int>(value);
return true;
}
}
}
return false;
}
bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) {
return GetCodecIntParameter(
codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
&remote_port_);
}
bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) {
return GetCodecIntParameter(
codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
&local_port_);
}
void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
talk_base::Buffer* buffer) {

View File

@ -168,12 +168,8 @@ class SctpDataMediaChannel : public DataMediaChannel,
const std::vector<RtpHeaderExtension>& extensions) { return true; }
virtual bool SetSendRtpHeaderExtensions(
const std::vector<RtpHeaderExtension>& extensions) { return true; }
virtual bool SetSendCodecs(const std::vector<DataCodec>& codecs) {
return true;
}
virtual bool SetRecvCodecs(const std::vector<DataCodec>& codecs) {
return true;
}
virtual bool SetSendCodecs(const std::vector<DataCodec>& codecs);
virtual bool SetRecvCodecs(const std::vector<DataCodec>& codecs);
virtual void OnRtcpReceived(talk_base::Buffer* packet) {}
virtual void OnReadyToSend(bool ready) {}
@ -211,7 +207,8 @@ class SctpDataMediaChannel : public DataMediaChannel,
talk_base::Thread* worker_thread_;
// The local and remote SCTP port to use. These are passed along the wire
// and the listener and connector must be using the same port. It is not
// related to the ports at the IP level.
// related to the ports at the IP level. If set to -1, we default to
// kSctpDefaultPort.
int local_port_;
int remote_port_;
struct socket* sock_; // The socket created by usrsctp_socket(...).

View File

@ -318,20 +318,32 @@ class WebRtcDecoderObserver : public webrtc::ViEDecoderObserver {
virtual void IncomingRate(const int videoChannel,
const unsigned int framerate,
const unsigned int bitrate) {
talk_base::CritScope cs(&crit_);
ASSERT(video_channel_ == videoChannel);
framerate_ = framerate;
bitrate_ = bitrate;
}
virtual void RequestNewKeyFrame(const int videoChannel) {
talk_base::CritScope cs(&crit_);
ASSERT(video_channel_ == videoChannel);
++firs_requested_;
}
int framerate() const { return framerate_; }
int bitrate() const { return bitrate_; }
int firs_requested() const { return firs_requested_; }
int framerate() const {
talk_base::CritScope cs(&crit_);
return framerate_;
}
int bitrate() const {
talk_base::CritScope cs(&crit_);
return bitrate_;
}
int firs_requested() const {
talk_base::CritScope cs(&crit_);
return firs_requested_;
}
private:
mutable talk_base::CriticalSection crit_;
int video_channel_;
int framerate_;
int bitrate_;
@ -350,15 +362,23 @@ class WebRtcEncoderObserver : public webrtc::ViEEncoderObserver {
virtual void OutgoingRate(const int videoChannel,
const unsigned int framerate,
const unsigned int bitrate) {
talk_base::CritScope cs(&crit_);
ASSERT(video_channel_ == videoChannel);
framerate_ = framerate;
bitrate_ = bitrate;
}
int framerate() const { return framerate_; }
int bitrate() const { return bitrate_; }
int framerate() const {
talk_base::CritScope cs(&crit_);
return framerate_;
}
int bitrate() const {
talk_base::CritScope cs(&crit_);
return bitrate_;
}
private:
mutable talk_base::CriticalSection crit_;
int video_channel_;
int framerate_;
int bitrate_;
@ -914,6 +934,17 @@ bool WebRtcVideoEngine::SetDefaultEncoderConfig(
return SetDefaultCodec(config.max_codec);
}
VideoEncoderConfig WebRtcVideoEngine::GetDefaultEncoderConfig() const {
ASSERT(!video_codecs_.empty());
VideoCodec max_codec(kVideoCodecPrefs[0].payload_type,
kVideoCodecPrefs[0].name,
video_codecs_[0].width,
video_codecs_[0].height,
video_codecs_[0].framerate,
0);
return VideoEncoderConfig(max_codec);
}
// SetDefaultCodec may be called while the capturer is running. For example, a
// test call is started in a page with QVGA default codec, and then a real call
// is started in another page with VGA default codec. This is the corner case
@ -924,6 +955,7 @@ bool WebRtcVideoEngine::SetDefaultCodec(const VideoCodec& codec) {
return false;
}
ASSERT(!video_codecs_.empty());
default_codec_format_ = VideoFormat(
video_codecs_[0].width,
video_codecs_[0].height,

View File

@ -106,6 +106,7 @@ class WebRtcVideoEngine : public sigslot::has_slots<>,
int GetCapabilities();
bool SetOptions(const VideoOptions &options);
bool SetDefaultEncoderConfig(const VideoEncoderConfig& config);
VideoEncoderConfig GetDefaultEncoderConfig() const;
WebRtcVideoMediaChannel* CreateChannel(VoiceMediaChannel* voice_channel);

View File

@ -2158,6 +2158,11 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
return false;
uint32 ssrc = sp.first_ssrc();
if (ssrc == 0) {
LOG(LS_WARNING) << "AddRecvStream with 0 ssrc is not supported.";
return false;
}
if (receive_channels_.find(ssrc) != receive_channels_.end()) {
LOG(LS_ERROR) << "Stream already exists with ssrc " << ssrc;
return false;
@ -2181,6 +2186,21 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
return false;
}
if (!ConfigureRecvChannel(channel)) {
DeleteChannel(channel);
return false;
}
receive_channels_.insert(
std::make_pair(ssrc, WebRtcVoiceChannelInfo(channel, NULL)));
LOG(LS_INFO) << "New audio stream " << ssrc
<< " registered to VoiceEngine channel #"
<< channel << ".";
return true;
}
bool WebRtcVoiceMediaChannel::ConfigureRecvChannel(int channel) {
// Configure to use external transport, like our default channel.
if (engine()->voe()->network()->RegisterExternalTransport(
channel, *this) == -1) {
@ -2237,13 +2257,6 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
}
SetNack(channel, nack_enabled_);
receive_channels_.insert(
std::make_pair(ssrc, WebRtcVoiceChannelInfo(channel, NULL)));
// TODO(juberti): We should rollback the add if SetPlayout fails.
LOG(LS_INFO) << "New audio stream " << ssrc
<< " registered to VoiceEngine channel #"
<< channel << ".";
return SetPlayout(channel, playout_);
}

View File

@ -381,6 +381,7 @@ class WebRtcVoiceMediaChannel
bool ChangeSend(SendFlags send);
bool ChangeSend(int channel, SendFlags send);
void ConfigureSendChannel(int channel);
bool ConfigureRecvChannel(int channel);
bool DeleteChannel(int channel);
bool InConferenceMode() const {
return options_.conference_mode.GetWithDefaultIfUnset(false);

View File

@ -2044,6 +2044,26 @@ TEST_F(WebRtcVoiceEngineTestFake, StreamCleanup) {
EXPECT_EQ(0, voe_.GetNumChannels());
}
TEST_F(WebRtcVoiceEngineTestFake, TestAddRecvStreamFailWithZeroSsrc) {
EXPECT_TRUE(SetupEngine());
EXPECT_FALSE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(0)));
}
TEST_F(WebRtcVoiceEngineTestFake, TestNoLeakingWhenAddRecvStreamFail) {
EXPECT_TRUE(SetupEngine());
// Stream 1 reuses default channel.
EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1)));
// Manually delete default channel to simulate a failure.
int default_channel = voe_.GetLastChannel();
EXPECT_EQ(0, voe_.DeleteChannel(default_channel));
// Add recv stream 2 should fail because default channel is gone.
EXPECT_FALSE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)));
int new_channel = voe_.GetLastChannel();
EXPECT_NE(default_channel, new_channel);
// The last created channel should have already been deleted.
EXPECT_EQ(-1, voe_.DeleteChannel(new_channel));
}
// Test the InsertDtmf on default send stream as caller.
TEST_F(WebRtcVoiceEngineTestFake, InsertDtmfOnDefaultSendStreamAsCaller) {
TestInsertDtmf(0, true);

View File

@ -411,6 +411,7 @@ BaseChannel::BaseChannel(talk_base::Thread* thread,
BaseChannel::~BaseChannel() {
ASSERT(worker_thread_ == talk_base::Thread::Current());
Deinit();
StopConnectionMonitor();
FlushRtcpMessages(); // Send any outstanding RTCP packets.
Clear(); // eats any outstanding messages or packets
@ -455,6 +456,10 @@ bool BaseChannel::Init(TransportChannel* transport_channel,
return true;
}
void BaseChannel::Deinit() {
media_channel_->SetInterface(NULL);
}
// Can be called from thread other than worker thread
bool BaseChannel::Enable(bool enable) {
Send(enable ? MSG_ENABLE : MSG_DISABLE);
@ -1466,6 +1471,7 @@ VoiceChannel::~VoiceChannel() {
StopMediaMonitor();
// this can't be done in the base class, since it calls a virtual
DisableMedia_w();
Deinit();
}
bool VoiceChannel::Init() {
@ -1961,6 +1967,8 @@ VideoChannel::~VideoChannel() {
StopMediaMonitor();
// this can't be done in the base class, since it calls a virtual
DisableMedia_w();
Deinit();
}
bool VideoChannel::SetRenderer(uint32 ssrc, VideoRenderer* renderer) {
@ -2464,6 +2472,8 @@ DataChannel::~DataChannel() {
StopMediaMonitor();
// this can't be done in the base class, since it calls a virtual
DisableMedia_w();
Deinit();
}
bool DataChannel::Init() {

View File

@ -66,6 +66,12 @@ enum SinkType {
// BaseChannel contains logic common to voice and video, including
// enable/mute, marshaling calls to a worker thread, and
// connection and media monitors.
//
// WARNING! SUBCLASSES MUST CALL Deinit() IN THEIR DESTRUCTORS!
// This is required to avoid a data race between the destructor modifying the
// vtable, and the media channel's thread using BaseChannel as the
// NetworkInterface.
class BaseChannel
: public talk_base::MessageHandler, public sigslot::has_slots<>,
public MediaChannel::NetworkInterface {
@ -76,6 +82,9 @@ class BaseChannel
virtual ~BaseChannel();
bool Init(TransportChannel* transport_channel,
TransportChannel* rtcp_transport_channel);
// Deinit may be called multiple times and is simply ignored if it's alreay
// done.
void Deinit();
talk_base::Thread* worker_thread() const { return worker_thread_; }
BaseSession* session() const { return session_; }

View File

@ -599,6 +599,7 @@ static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
return false;
}
bool common_cryptos_needed = false;
// Get the common cryptos.
const ContentNames& content_names = bundle_group.content_names();
CryptoParamsVec common_cryptos;
@ -607,6 +608,11 @@ static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
if (!IsRtpContent(sdesc, *it)) {
continue;
}
// The common cryptos are needed if any of the content does not have DTLS
// enabled.
if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
common_cryptos_needed = true;
}
if (it == content_names.begin()) {
// Initial the common_cryptos with the first content in the bundle group.
if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
@ -625,7 +631,7 @@ static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
}
}
if (common_cryptos.empty()) {
if (common_cryptos.empty() && common_cryptos_needed) {
return false;
}

View File

@ -536,8 +536,8 @@ TEST_F(MediaSessionDescriptionFactoryTest,
ASSERT_CRYPTO(dcd, 1U, CS_AES_CM_128_HMAC_SHA1_80);
EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), dcd->protocol());
}
// Create a typical data offer, and ensure it matches what we expect.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataOffer) {
// Create a RTP data offer, and ensure it matches what we expect.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateRtpDataOffer) {
MediaSessionOptions opts;
opts.data_channel_type = cricket::DCT_RTP;
f1_.set_secure(SEC_ENABLED);
@ -571,6 +571,18 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataOffer) {
EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), dcd->protocol());
}
// Create an SCTP data offer with bundle without error.
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSctpDataOffer) {
MediaSessionOptions opts;
opts.has_audio = false;
opts.bundle_enabled = true;
opts.data_channel_type = cricket::DCT_SCTP;
f1_.set_secure(SEC_ENABLED);
talk_base::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
EXPECT_TRUE(offer.get() != NULL);
EXPECT_TRUE(offer->GetContentByName("data") != NULL);
}
// Create an audio, video offer without legacy StreamParams.
TEST_F(MediaSessionDescriptionFactoryTest,
TestCreateOfferWithoutLegacyStreams) {