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:
parent
e0d55a0782
commit
7818752566
@ -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) {
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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".
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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) \
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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_
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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_
|
||||
|
@ -52,7 +52,8 @@ class ScopedLibUdev {
|
||||
ScopedLibUdev() {}
|
||||
|
||||
bool Init() {
|
||||
return libudev_.Load();
|
||||
return libudev_.Load() &&
|
||||
!IsWrongLibUDevAbiVersion(libudev_.GetDllHandle());
|
||||
}
|
||||
|
||||
LibUDevSymbolTable libudev_;
|
||||
|
@ -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()();
|
||||
|
@ -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) {
|
||||
|
@ -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(...).
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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_);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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_; }
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user