Update talk to 55821645.
TEST=try bots R=mallinath@webrtc.org Review URL: https://webrtc-codereview.appspot.com/3139004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5053 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
ec4cccc6b6
commit
cecfd1832d
@ -31,6 +31,7 @@
|
||||
#include "talk/app/webrtc/mediastreamprovider.h"
|
||||
#include "talk/base/logging.h"
|
||||
#include "talk/base/refcount.h"
|
||||
#include "talk/media/sctp/sctputils.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -68,36 +69,35 @@ DataChannel::DataChannel(
|
||||
}
|
||||
|
||||
bool DataChannel::Init(const DataChannelInit* config) {
|
||||
if (config) {
|
||||
if (data_channel_type_ == cricket::DCT_RTP &&
|
||||
(config->reliable ||
|
||||
config->id != -1 ||
|
||||
config->maxRetransmits != -1 ||
|
||||
config->maxRetransmitTime != -1)) {
|
||||
LOG(LS_ERROR) << "Failed to initialize the RTP data channel due to "
|
||||
if (data_channel_type_ == cricket::DCT_RTP &&
|
||||
(config->reliable ||
|
||||
config->id != -1 ||
|
||||
config->maxRetransmits != -1 ||
|
||||
config->maxRetransmitTime != -1)) {
|
||||
LOG(LS_ERROR) << "Failed to initialize the RTP data channel due to "
|
||||
<< "invalid DataChannelInit.";
|
||||
return false;
|
||||
} else if (data_channel_type_ == cricket::DCT_SCTP) {
|
||||
if (config->id < -1 ||
|
||||
config->maxRetransmits < -1 ||
|
||||
config->maxRetransmitTime < -1) {
|
||||
LOG(LS_ERROR) << "Failed to initialize the SCTP data channel due to "
|
||||
<< "invalid DataChannelInit.";
|
||||
return false;
|
||||
} else if (data_channel_type_ == cricket::DCT_SCTP) {
|
||||
if (config->id < -1 ||
|
||||
config->maxRetransmits < -1 ||
|
||||
config->maxRetransmitTime < -1) {
|
||||
LOG(LS_ERROR) << "Failed to initialize the SCTP data channel due to "
|
||||
<< "invalid DataChannelInit.";
|
||||
return false;
|
||||
}
|
||||
if (config->maxRetransmits != -1 && config->maxRetransmitTime != -1) {
|
||||
LOG(LS_ERROR) <<
|
||||
"maxRetransmits and maxRetransmitTime should not be both set.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (config->maxRetransmits != -1 && config->maxRetransmitTime != -1) {
|
||||
LOG(LS_ERROR) <<
|
||||
"maxRetransmits and maxRetransmitTime should not be both set.";
|
||||
return false;
|
||||
}
|
||||
config_ = *config;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DataChannel::HasNegotiationCompleted() {
|
||||
return send_ssrc_set_ == receive_ssrc_set_;
|
||||
// Try to connect to the transport in case the transport channel already
|
||||
// exists.
|
||||
OnTransportChannelCreated();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DataChannel::~DataChannel() {
|
||||
@ -169,16 +169,13 @@ void DataChannel::QueueControl(const talk_base::Buffer* buffer) {
|
||||
queued_control_data_.push(buffer);
|
||||
}
|
||||
|
||||
bool DataChannel::SendControl(const talk_base::Buffer* buffer) {
|
||||
if (data_channel_type_ == cricket::DCT_RTP) {
|
||||
delete buffer;
|
||||
return false;
|
||||
}
|
||||
bool DataChannel::SendOpenMessage(const talk_base::Buffer* raw_buffer) {
|
||||
ASSERT(data_channel_type_ == cricket::DCT_SCTP &&
|
||||
was_ever_writable_ &&
|
||||
config_.id >= 0 &&
|
||||
!config_.negotiated);
|
||||
|
||||
if (state_ != kOpen) {
|
||||
QueueControl(buffer);
|
||||
return true;
|
||||
}
|
||||
talk_base::scoped_ptr<const talk_base::Buffer> buffer(raw_buffer);
|
||||
|
||||
cricket::SendDataParams send_params;
|
||||
send_params.ssrc = config_.id;
|
||||
@ -189,18 +186,15 @@ bool DataChannel::SendControl(const talk_base::Buffer* buffer) {
|
||||
bool retval = provider_->SendData(send_params, *buffer, &send_result);
|
||||
if (!retval && send_result == cricket::SDR_BLOCK) {
|
||||
// Link is congested. Queue for later.
|
||||
QueueControl(buffer);
|
||||
} else {
|
||||
delete buffer;
|
||||
QueueControl(buffer.release());
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void DataChannel::SetReceiveSsrc(uint32 receive_ssrc) {
|
||||
ASSERT(data_channel_type_ == cricket::DCT_RTP);
|
||||
|
||||
if (receive_ssrc_set_) {
|
||||
ASSERT(data_channel_type_ == cricket::DCT_RTP ||
|
||||
!send_ssrc_set_ ||
|
||||
receive_ssrc_ == send_ssrc_);
|
||||
return;
|
||||
}
|
||||
receive_ssrc_ = receive_ssrc;
|
||||
@ -214,10 +208,8 @@ void DataChannel::RemotePeerRequestClose() {
|
||||
}
|
||||
|
||||
void DataChannel::SetSendSsrc(uint32 send_ssrc) {
|
||||
ASSERT(data_channel_type_ == cricket::DCT_RTP);
|
||||
if (send_ssrc_set_) {
|
||||
ASSERT(data_channel_type_ == cricket::DCT_RTP ||
|
||||
!receive_ssrc_set_ ||
|
||||
receive_ssrc_ == send_ssrc_);
|
||||
return;
|
||||
}
|
||||
send_ssrc_ = send_ssrc;
|
||||
@ -263,8 +255,18 @@ void DataChannel::OnChannelReady(bool writable) {
|
||||
// for sending and now unblocked, so send the queued data now.
|
||||
if (!was_ever_writable_) {
|
||||
was_ever_writable_ = true;
|
||||
|
||||
if (data_channel_type_ == cricket::DCT_SCTP && !config_.negotiated) {
|
||||
talk_base::Buffer* payload = new talk_base::Buffer;
|
||||
if (!cricket::WriteDataChannelOpenMessage(label_, config_, payload)) {
|
||||
// TODO(jiayl): close the data channel on this error.
|
||||
LOG(LS_ERROR) << "Could not write data channel OPEN message";
|
||||
return;
|
||||
}
|
||||
SendOpenMessage(payload);
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
DeliverQueuedControlData();
|
||||
ASSERT(queued_send_data_.empty());
|
||||
} else if (state_ == kOpen) {
|
||||
DeliverQueuedSendData();
|
||||
@ -281,11 +283,15 @@ void DataChannel::DoClose() {
|
||||
void DataChannel::UpdateState() {
|
||||
switch (state_) {
|
||||
case kConnecting: {
|
||||
if (HasNegotiationCompleted()) {
|
||||
if (!connected_to_provider_) {
|
||||
ConnectToDataSession();
|
||||
if (send_ssrc_set_ == receive_ssrc_set_) {
|
||||
if (data_channel_type_ == cricket::DCT_RTP && !connected_to_provider_) {
|
||||
connected_to_provider_ = provider_->ConnectDataChannel(this);
|
||||
provider_->AddRtpDataStream(send_ssrc_, receive_ssrc_);
|
||||
}
|
||||
if (was_ever_writable_) {
|
||||
// TODO(jiayl): Do not transition to kOpen if we failed to send the
|
||||
// OPEN message.
|
||||
DeliverQueuedControlData();
|
||||
SetState(kOpen);
|
||||
// If we have received buffers before the channel got writable.
|
||||
// Deliver them now.
|
||||
@ -298,10 +304,9 @@ void DataChannel::UpdateState() {
|
||||
break;
|
||||
}
|
||||
case kClosing: {
|
||||
if (connected_to_provider_) {
|
||||
DisconnectFromDataSession();
|
||||
}
|
||||
if (HasNegotiationCompleted()) {
|
||||
DisconnectFromTransport();
|
||||
|
||||
if (!send_ssrc_set_ && !receive_ssrc_set_) {
|
||||
SetState(kClosed);
|
||||
}
|
||||
break;
|
||||
@ -318,13 +323,18 @@ void DataChannel::SetState(DataState state) {
|
||||
}
|
||||
}
|
||||
|
||||
void DataChannel::ConnectToDataSession() {
|
||||
connected_to_provider_ = provider_->ConnectDataChannel(this);
|
||||
}
|
||||
void DataChannel::DisconnectFromTransport() {
|
||||
if (!connected_to_provider_)
|
||||
return;
|
||||
|
||||
void DataChannel::DisconnectFromDataSession() {
|
||||
provider_->DisconnectDataChannel(this);
|
||||
connected_to_provider_ = false;
|
||||
|
||||
if (data_channel_type_ == cricket::DCT_RTP) {
|
||||
provider_->RemoveRtpDataStream(send_ssrc_, receive_ssrc_);
|
||||
} else {
|
||||
provider_->RemoveSctpDataStream(config_.id);
|
||||
}
|
||||
}
|
||||
|
||||
void DataChannel::DeliverQueuedReceivedData() {
|
||||
@ -349,10 +359,12 @@ void DataChannel::ClearQueuedReceivedData() {
|
||||
}
|
||||
|
||||
void DataChannel::DeliverQueuedSendData() {
|
||||
ASSERT(was_ever_writable_ && state_ == kOpen);
|
||||
|
||||
// TODO(jiayl): Sending OPEN message here contradicts with the pre-condition
|
||||
// that the readyState is open. According to the standard, the channel should
|
||||
// not become open before the OPEN message is sent.
|
||||
DeliverQueuedControlData();
|
||||
if (!was_ever_writable_) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (!queued_send_data_.empty()) {
|
||||
DataBuffer* buffer = queued_send_data_.front();
|
||||
@ -376,12 +388,11 @@ void DataChannel::ClearQueuedControlData() {
|
||||
}
|
||||
|
||||
void DataChannel::DeliverQueuedControlData() {
|
||||
if (was_ever_writable_) {
|
||||
while (!queued_control_data_.empty()) {
|
||||
const talk_base::Buffer *buf = queued_control_data_.front();
|
||||
queued_control_data_.pop();
|
||||
SendControl(buf);
|
||||
}
|
||||
ASSERT(was_ever_writable_);
|
||||
while (!queued_control_data_.empty()) {
|
||||
const talk_base::Buffer* buf = queued_control_data_.front();
|
||||
queued_control_data_.pop();
|
||||
SendOpenMessage(buf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,4 +428,22 @@ bool DataChannel::QueueSendData(const DataBuffer& buffer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataChannel::SetSctpSid(int sid) {
|
||||
ASSERT(config_.id < 0 && sid >= 0 && data_channel_type_ == cricket::DCT_SCTP);
|
||||
config_.id = sid;
|
||||
provider_->AddSctpDataStream(sid);
|
||||
}
|
||||
|
||||
void DataChannel::OnTransportChannelCreated() {
|
||||
ASSERT(data_channel_type_ == cricket::DCT_SCTP);
|
||||
if (!connected_to_provider_) {
|
||||
connected_to_provider_ = provider_->ConnectDataChannel(this);
|
||||
}
|
||||
// The sid may have been unassigned when provider_->ConnectDataChannel was
|
||||
// done. So always add the streams even if connected_to_provider_ is true.
|
||||
if (config_.id >= 0) {
|
||||
provider_->AddSctpDataStream(config_.id);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -44,24 +44,35 @@ class DataChannel;
|
||||
|
||||
class DataChannelProviderInterface {
|
||||
public:
|
||||
// Sends the data to the transport.
|
||||
virtual bool SendData(const cricket::SendDataParams& params,
|
||||
const talk_base::Buffer& payload,
|
||||
cricket::SendDataResult* result) = 0;
|
||||
// Connects to the transport signals.
|
||||
virtual bool ConnectDataChannel(DataChannel* data_channel) = 0;
|
||||
// Disconnects from the transport signals.
|
||||
virtual void DisconnectDataChannel(DataChannel* data_channel) = 0;
|
||||
// Adds the send and receive stream ssrc to the transport for RTP.
|
||||
virtual void AddRtpDataStream(uint32 send_ssrc, uint32 recv_ssrc) = 0;
|
||||
// Adds the data channel SID to the transport for SCTP.
|
||||
virtual void AddSctpDataStream(uint32 sid) = 0;
|
||||
// Removes the data channel ssrcs from the transport for RTP.
|
||||
virtual void RemoveRtpDataStream(uint32 send_ssrc, uint32 recv_ssrc) = 0;
|
||||
// Removes the data channel SID from the transport for SCTP.
|
||||
virtual void RemoveSctpDataStream(uint32 sid) = 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
|
||||
// channels. Currently this class is specifically designed to use RtpDataEngine,
|
||||
// and will changed to use SCTP in the future.
|
||||
// libjingle's data engine. It provides an implementation of unreliable or
|
||||
// reliabledata channels. Currently this class is specifically designed to use
|
||||
// both RtpDataEngine and SctpDataEngine.
|
||||
|
||||
// DataChannel states:
|
||||
// kConnecting: The channel has been created but SSRC for sending and receiving
|
||||
// has not yet been set and the transport might not yet be ready.
|
||||
// kConnecting: The channel has been created the transport might not yet be
|
||||
// ready.
|
||||
// kOpen: The channel have a local SSRC set by a call to UpdateSendSsrc
|
||||
// and a remote SSRC set by call to UpdateReceiveSsrc and the transport
|
||||
// has been writable once.
|
||||
@ -73,7 +84,7 @@ class DataChannel : public DataChannelInterface,
|
||||
public sigslot::has_slots<> {
|
||||
public:
|
||||
static talk_base::scoped_refptr<DataChannel> Create(
|
||||
DataChannelProviderInterface* client,
|
||||
DataChannelProviderInterface* provider,
|
||||
cricket::DataChannelType dct,
|
||||
const std::string& label,
|
||||
const DataChannelInit* config);
|
||||
@ -97,20 +108,6 @@ class DataChannel : public DataChannelInterface,
|
||||
virtual void Close();
|
||||
virtual DataState state() const { return state_; }
|
||||
virtual bool Send(const DataBuffer& buffer);
|
||||
// Send a control message right now, or queue for later.
|
||||
virtual bool SendControl(const talk_base::Buffer* buffer);
|
||||
void ConnectToDataSession();
|
||||
|
||||
// Set the SSRC this channel should use to receive data from the
|
||||
// underlying data engine.
|
||||
void SetReceiveSsrc(uint32 receive_ssrc);
|
||||
// The remote peer request that this channel should be closed.
|
||||
void RemotePeerRequestClose();
|
||||
|
||||
// Set the SSRC this channel should use to send data on the
|
||||
// underlying data engine. |send_ssrc| == 0 means that the channel is no
|
||||
// longer part of the session negotiation.
|
||||
void SetSendSsrc(uint32 send_ssrc);
|
||||
|
||||
// Called if the underlying data engine is closing.
|
||||
void OnDataEngineClose();
|
||||
@ -125,20 +122,38 @@ class DataChannel : public DataChannelInterface,
|
||||
const cricket::ReceiveDataParams& params,
|
||||
const talk_base::Buffer& payload);
|
||||
|
||||
// The remote peer request that this channel should be closed.
|
||||
void RemotePeerRequestClose();
|
||||
|
||||
// The following methods are for SCTP only.
|
||||
|
||||
// Sets the SCTP sid and adds to transport layer if not set yet.
|
||||
void SetSctpSid(int sid);
|
||||
// Called when the transport channel is created.
|
||||
void OnTransportChannelCreated();
|
||||
|
||||
// The following methods are for RTP only.
|
||||
|
||||
// Set the SSRC this channel should use to send data on the
|
||||
// underlying data engine. |send_ssrc| == 0 means that the channel is no
|
||||
// longer part of the session negotiation.
|
||||
void SetSendSsrc(uint32 send_ssrc);
|
||||
// Set the SSRC this channel should use to receive data from the
|
||||
// underlying data engine.
|
||||
void SetReceiveSsrc(uint32 receive_ssrc);
|
||||
|
||||
protected:
|
||||
DataChannel(DataChannelProviderInterface* client,
|
||||
cricket::DataChannelType dct,
|
||||
const std::string& label);
|
||||
virtual ~DataChannel();
|
||||
|
||||
bool Init(const DataChannelInit* config);
|
||||
bool HasNegotiationCompleted();
|
||||
|
||||
private:
|
||||
bool Init(const DataChannelInit* config);
|
||||
void DoClose();
|
||||
void UpdateState();
|
||||
void SetState(DataState state);
|
||||
void DisconnectFromDataSession();
|
||||
void DisconnectFromTransport();
|
||||
void DeliverQueuedControlData();
|
||||
void QueueControl(const talk_base::Buffer* buffer);
|
||||
void ClearQueuedControlData();
|
||||
@ -149,6 +164,8 @@ class DataChannel : public DataChannelInterface,
|
||||
bool InternalSendWithoutQueueing(const DataBuffer& buffer,
|
||||
cricket::SendDataResult* send_result);
|
||||
bool QueueSendData(const DataBuffer& buffer);
|
||||
bool SendOpenMessage(const talk_base::Buffer* buffer);
|
||||
|
||||
|
||||
std::string label_;
|
||||
DataChannelInit config_;
|
||||
|
@ -26,56 +26,56 @@
|
||||
*/
|
||||
|
||||
#include "talk/app/webrtc/datachannel.h"
|
||||
#include "talk/app/webrtc/test/fakedatachannelprovider.h"
|
||||
#include "talk/base/gunit.h"
|
||||
|
||||
using webrtc::DataChannel;
|
||||
|
||||
class FakeDataChannelClient : public webrtc::DataChannelProviderInterface {
|
||||
public:
|
||||
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;
|
||||
}
|
||||
last_send_data_params_ = params;
|
||||
return true;
|
||||
}
|
||||
virtual bool ConnectDataChannel(DataChannel* data_channel) OVERRIDE {
|
||||
return true;
|
||||
}
|
||||
virtual void DisconnectDataChannel(DataChannel* data_channel) OVERRIDE {}
|
||||
|
||||
void set_send_blocked(bool blocked) { send_blocked_ = blocked; }
|
||||
cricket::SendDataParams last_send_data_params() {
|
||||
return last_send_data_params_;
|
||||
}
|
||||
|
||||
private:
|
||||
cricket::SendDataParams last_send_data_params_;
|
||||
bool send_blocked_;
|
||||
};
|
||||
|
||||
class SctpDataChannelTest : public testing::Test {
|
||||
protected:
|
||||
SctpDataChannelTest()
|
||||
: webrtc_data_channel_(
|
||||
DataChannel::Create(&client_, cricket::DCT_SCTP, "test", &init_)) {
|
||||
DataChannel::Create(&provider_, cricket::DCT_SCTP, "test", &init_)) {
|
||||
}
|
||||
|
||||
void SetChannelReady() {
|
||||
webrtc_data_channel_->OnTransportChannelCreated();
|
||||
if (webrtc_data_channel_->id() < 0) {
|
||||
webrtc_data_channel_->SetSctpSid(0);
|
||||
}
|
||||
webrtc_data_channel_->OnChannelReady(true);
|
||||
}
|
||||
|
||||
webrtc::DataChannelInit init_;
|
||||
FakeDataChannelClient client_;
|
||||
FakeDataChannelProvider provider_;
|
||||
talk_base::scoped_refptr<DataChannel> webrtc_data_channel_;
|
||||
};
|
||||
|
||||
// Verifies that the data channel is connected to the transport after creation.
|
||||
TEST_F(SctpDataChannelTest, ConnectedToTransportOnCreated) {
|
||||
EXPECT_TRUE(provider_.IsConnected(webrtc_data_channel_.get()));
|
||||
// The sid is not set yet, so it should not have added the streams.
|
||||
EXPECT_FALSE(provider_.IsSendStreamAdded(webrtc_data_channel_->id()));
|
||||
EXPECT_FALSE(provider_.IsRecvStreamAdded(webrtc_data_channel_->id()));
|
||||
|
||||
webrtc_data_channel_->SetSctpSid(0);
|
||||
EXPECT_TRUE(provider_.IsSendStreamAdded(webrtc_data_channel_->id()));
|
||||
EXPECT_TRUE(provider_.IsRecvStreamAdded(webrtc_data_channel_->id()));
|
||||
}
|
||||
|
||||
// Verifies that the data channel is connected to the transport if the transport
|
||||
// is not available initially and becomes available later.
|
||||
TEST_F(SctpDataChannelTest, ConnectedAfterTransportBecomesAvailable) {
|
||||
provider_.set_transport_available(false);
|
||||
talk_base::scoped_refptr<DataChannel> dc = DataChannel::Create(
|
||||
&provider_, cricket::DCT_SCTP, "test1", &init_);
|
||||
EXPECT_FALSE(provider_.IsConnected(dc.get()));
|
||||
|
||||
provider_.set_transport_available(true);
|
||||
dc->OnTransportChannelCreated();
|
||||
EXPECT_TRUE(provider_.IsConnected(dc.get()));
|
||||
}
|
||||
|
||||
// Tests the state of the data channel.
|
||||
TEST_F(SctpDataChannelTest, StateTransition) {
|
||||
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting,
|
||||
@ -85,6 +85,8 @@ TEST_F(SctpDataChannelTest, StateTransition) {
|
||||
webrtc_data_channel_->Close();
|
||||
EXPECT_EQ(webrtc::DataChannelInterface::kClosed,
|
||||
webrtc_data_channel_->state());
|
||||
// Verifies that it's disconnected from the transport.
|
||||
EXPECT_FALSE(provider_.IsConnected(webrtc_data_channel_.get()));
|
||||
}
|
||||
|
||||
// Tests that DataChannel::buffered_amount() is correct after the channel is
|
||||
@ -96,7 +98,7 @@ TEST_F(SctpDataChannelTest, BufferedAmountWhenBlocked) {
|
||||
|
||||
EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount());
|
||||
|
||||
client_.set_send_blocked(true);
|
||||
provider_.set_send_blocked(true);
|
||||
|
||||
const int number_of_packets = 3;
|
||||
for (int i = 0; i < number_of_packets; ++i) {
|
||||
@ -111,18 +113,22 @@ TEST_F(SctpDataChannelTest, BufferedAmountWhenBlocked) {
|
||||
TEST_F(SctpDataChannelTest, QueuedDataSentWhenUnblocked) {
|
||||
SetChannelReady();
|
||||
webrtc::DataBuffer buffer("abcd");
|
||||
client_.set_send_blocked(true);
|
||||
provider_.set_send_blocked(true);
|
||||
EXPECT_TRUE(webrtc_data_channel_->Send(buffer));
|
||||
|
||||
client_.set_send_blocked(false);
|
||||
provider_.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));
|
||||
TEST_F(SctpDataChannelTest, OpenMessageSent) {
|
||||
// Initially the id is unassigned.
|
||||
EXPECT_EQ(-1, webrtc_data_channel_->id());
|
||||
|
||||
SetChannelReady();
|
||||
EXPECT_EQ(cricket::DMT_CONTROL, client_.last_send_data_params().type);
|
||||
EXPECT_GE(webrtc_data_channel_->id(), 0);
|
||||
EXPECT_EQ(cricket::DMT_CONTROL, provider_.last_send_data_params().type);
|
||||
EXPECT_EQ(provider_.last_send_data_params().ssrc,
|
||||
static_cast<uint32>(webrtc_data_channel_->id()));
|
||||
}
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "talk/app/webrtc/videosource.h"
|
||||
#include "talk/app/webrtc/videotrack.h"
|
||||
#include "talk/base/bytebuffer.h"
|
||||
#include "talk/base/stringutils.h"
|
||||
#include "talk/media/sctp/sctpdataengine.h"
|
||||
|
||||
static const char kDefaultStreamLabel[] = "default";
|
||||
@ -189,7 +190,8 @@ MediaStreamSignaling::MediaStreamSignaling(
|
||||
remote_streams_(StreamCollection::Create()),
|
||||
remote_stream_factory_(new RemoteMediaStreamFactory(signaling_thread,
|
||||
channel_manager)),
|
||||
last_allocated_sctp_id_(0) {
|
||||
last_allocated_sctp_even_sid_(-2),
|
||||
last_allocated_sctp_odd_sid_(-1) {
|
||||
options_.has_video = false;
|
||||
options_.has_audio = false;
|
||||
}
|
||||
@ -203,36 +205,37 @@ void MediaStreamSignaling::TearDown() {
|
||||
OnDataChannelClose();
|
||||
}
|
||||
|
||||
bool MediaStreamSignaling::IsSctpIdAvailable(int id) const {
|
||||
if (id < 0 || id > static_cast<int>(cricket::kMaxSctpSid))
|
||||
bool MediaStreamSignaling::IsSctpSidAvailable(int sid) const {
|
||||
if (sid < 0 || sid > static_cast<int>(cricket::kMaxSctpSid))
|
||||
return false;
|
||||
for (DataChannels::const_iterator iter = data_channels_.begin();
|
||||
iter != data_channels_.end();
|
||||
++iter) {
|
||||
if (iter->second->id() == id) {
|
||||
if (iter->second->id() == sid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Gets the first id that has not been taken by existing data
|
||||
// channels. Starting from 1.
|
||||
// Returns false if no id can be allocated.
|
||||
// TODO(jiayl): Update to some kind of even/odd random number selection when the
|
||||
// rules are fully standardized.
|
||||
bool MediaStreamSignaling::AllocateSctpId(int* id) {
|
||||
do {
|
||||
last_allocated_sctp_id_++;
|
||||
} while (last_allocated_sctp_id_ <= static_cast<int>(cricket::kMaxSctpSid) &&
|
||||
!IsSctpIdAvailable(last_allocated_sctp_id_));
|
||||
// Gets the first unused odd/even id based on the DTLS role. If |role| is
|
||||
// SSL_CLIENT, the allocated id starts from 0 and takes even numbers; otherwise,
|
||||
// the id starts from 1 and takes odd numbers. Returns false if no id can be
|
||||
// allocated.
|
||||
bool MediaStreamSignaling::AllocateSctpSid(talk_base::SSLRole role, int* sid) {
|
||||
int& last_id = (role == talk_base::SSL_CLIENT) ?
|
||||
last_allocated_sctp_even_sid_ : last_allocated_sctp_odd_sid_;
|
||||
|
||||
if (last_allocated_sctp_id_ > static_cast<int>(cricket::kMaxSctpSid)) {
|
||||
last_allocated_sctp_id_ = cricket::kMaxSctpSid;
|
||||
do {
|
||||
last_id += 2;
|
||||
} while (last_id <= static_cast<int>(cricket::kMaxSctpSid) &&
|
||||
!IsSctpSidAvailable(last_id));
|
||||
|
||||
if (last_id > static_cast<int>(cricket::kMaxSctpSid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*id = last_allocated_sctp_id_;
|
||||
*sid = last_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -392,9 +395,8 @@ void MediaStreamSignaling::OnRemoteDescriptionChanged(
|
||||
const cricket::DataContentDescription* data_desc =
|
||||
static_cast<const cricket::DataContentDescription*>(
|
||||
data_content->description);
|
||||
if (data_desc->protocol() == cricket::kMediaProtocolDtlsSctp) {
|
||||
UpdateRemoteSctpDataChannels();
|
||||
} else {
|
||||
if (talk_base::starts_with(
|
||||
data_desc->protocol().data(), cricket::kMediaProtocolRtpPrefix)) {
|
||||
UpdateRemoteRtpDataChannels(data_desc->streams());
|
||||
}
|
||||
}
|
||||
@ -448,9 +450,8 @@ void MediaStreamSignaling::OnLocalDescriptionChanged(
|
||||
const cricket::DataContentDescription* data_desc =
|
||||
static_cast<const cricket::DataContentDescription*>(
|
||||
data_content->description);
|
||||
if (data_desc->protocol() == cricket::kMediaProtocolDtlsSctp) {
|
||||
UpdateLocalSctpDataChannels();
|
||||
} else {
|
||||
if (talk_base::starts_with(
|
||||
data_desc->protocol().data(), cricket::kMediaProtocolRtpPrefix)) {
|
||||
UpdateLocalRtpDataChannels(data_desc->streams());
|
||||
}
|
||||
}
|
||||
@ -919,20 +920,26 @@ void MediaStreamSignaling::CreateRemoteDataChannel(const std::string& label,
|
||||
stream_observer_->OnAddDataChannel(channel);
|
||||
}
|
||||
|
||||
|
||||
void MediaStreamSignaling::UpdateLocalSctpDataChannels() {
|
||||
void MediaStreamSignaling::OnDataTransportCreatedForSctp() {
|
||||
DataChannels::iterator it = data_channels_.begin();
|
||||
for (; it != data_channels_.end(); ++it) {
|
||||
DataChannel* data_channel = it->second;
|
||||
data_channel->SetSendSsrc(data_channel->id());
|
||||
data_channel->OnTransportChannelCreated();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaStreamSignaling::UpdateRemoteSctpDataChannels() {
|
||||
void MediaStreamSignaling::OnDtlsRoleReadyForSctp(talk_base::SSLRole role) {
|
||||
DataChannels::iterator it = data_channels_.begin();
|
||||
for (; it != data_channels_.end(); ++it) {
|
||||
DataChannel* data_channel = it->second;
|
||||
data_channel->SetReceiveSsrc(data_channel->id());
|
||||
if (data_channel->id() < 0) {
|
||||
int sid;
|
||||
if (!AllocateSctpSid(role, &sid)) {
|
||||
LOG(LS_ERROR) << "Failed to allocate SCTP sid.";
|
||||
continue;
|
||||
}
|
||||
data_channel->SetSctpSid(sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,11 +174,11 @@ class MediaStreamSignaling {
|
||||
}
|
||||
|
||||
// Checks if |id| is available to be assigned to a new SCTP data channel.
|
||||
bool IsSctpIdAvailable(int id) const;
|
||||
bool IsSctpSidAvailable(int sid) const;
|
||||
|
||||
// Gets the first available SCTP id that is not assigned to any existing
|
||||
// data channels.
|
||||
bool AllocateSctpId(int* id);
|
||||
bool AllocateSctpSid(talk_base::SSLRole role, int* sid);
|
||||
|
||||
// Adds |local_stream| to the collection of known MediaStreams that will be
|
||||
// offered in a SessionDescription.
|
||||
@ -249,8 +249,8 @@ class MediaStreamSignaling {
|
||||
StreamCollectionInterface* remote_streams() const {
|
||||
return remote_streams_.get();
|
||||
}
|
||||
void UpdateLocalSctpDataChannels();
|
||||
void UpdateRemoteSctpDataChannels();
|
||||
void OnDataTransportCreatedForSctp();
|
||||
void OnDtlsRoleReadyForSctp(talk_base::SSLRole role);
|
||||
|
||||
private:
|
||||
struct RemotePeerInfo {
|
||||
@ -380,7 +380,9 @@ class MediaStreamSignaling {
|
||||
TrackInfos local_audio_tracks_;
|
||||
TrackInfos local_video_tracks_;
|
||||
|
||||
int last_allocated_sctp_id_;
|
||||
int last_allocated_sctp_even_sid_;
|
||||
int last_allocated_sctp_odd_sid_;
|
||||
|
||||
typedef std::map<std::string, talk_base::scoped_refptr<DataChannel> >
|
||||
DataChannels;
|
||||
DataChannels data_channels_;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "talk/app/webrtc/mediastreamsignaling.h"
|
||||
#include "talk/app/webrtc/streamcollection.h"
|
||||
#include "talk/app/webrtc/test/fakeconstraints.h"
|
||||
#include "talk/app/webrtc/test/fakedatachannelprovider.h"
|
||||
#include "talk/app/webrtc/videotrack.h"
|
||||
#include "talk/base/gunit.h"
|
||||
#include "talk/base/scoped_ptr.h"
|
||||
@ -127,7 +128,7 @@ static const char kSdpStringWithMsidWithoutStreams[] =
|
||||
"o=- 0 0 IN IP4 127.0.0.1\r\n"
|
||||
"s=-\r\n"
|
||||
"t=0 0\r\n"
|
||||
"a:msid-semantic: WMS\r\n"
|
||||
"a=msid-semantic: WMS\r\n"
|
||||
"m=audio 1 RTP/AVPF 103\r\n"
|
||||
"a=mid:audio\r\n"
|
||||
"a=rtpmap:103 ISAC/16000\r\n"
|
||||
@ -1012,4 +1013,43 @@ TEST_F(MediaStreamSignalingTest, ChangeSsrcOnTrackInLocalSessionDescription) {
|
||||
observer_->VerifyLocalVideoTrack(kStreams[0], kVideoTracks[0], 98);
|
||||
}
|
||||
|
||||
// Verifies that an even SCTP id is allocated for SSL_CLIENT and an odd id for
|
||||
// SSL_SERVER.
|
||||
TEST_F(MediaStreamSignalingTest, SctpIdAllocationBasedOnRole) {
|
||||
int id;
|
||||
ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_SERVER, &id));
|
||||
EXPECT_EQ(1, id);
|
||||
ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_CLIENT, &id));
|
||||
EXPECT_EQ(0, id);
|
||||
ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_SERVER, &id));
|
||||
EXPECT_EQ(3, id);
|
||||
ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_CLIENT, &id));
|
||||
EXPECT_EQ(2, id);
|
||||
}
|
||||
|
||||
// Verifies that SCTP ids of existing DataChannels are not reused.
|
||||
TEST_F(MediaStreamSignalingTest, SctpIdAllocationNoReuse) {
|
||||
talk_base::scoped_ptr<FakeDataChannelProvider> provider(
|
||||
new FakeDataChannelProvider());
|
||||
// Creates a DataChannel with id 1.
|
||||
webrtc::DataChannelInit config;
|
||||
config.id = 1;
|
||||
talk_base::scoped_refptr<webrtc::DataChannel> data_channel(
|
||||
webrtc::DataChannel::Create(
|
||||
provider.get(), cricket::DCT_SCTP, "a", &config));
|
||||
ASSERT_TRUE(data_channel.get() != NULL);
|
||||
ASSERT_TRUE(signaling_->AddDataChannel(data_channel.get()));
|
||||
|
||||
int new_id;
|
||||
ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_SERVER, &new_id));
|
||||
EXPECT_NE(config.id, new_id);
|
||||
|
||||
// Creates a DataChannel with id 0.
|
||||
config.id = 0;
|
||||
data_channel = webrtc::DataChannel::Create(
|
||||
provider.get(), cricket::DCT_SCTP, "b", &config);
|
||||
ASSERT_TRUE(data_channel.get() != NULL);
|
||||
ASSERT_TRUE(signaling_->AddDataChannel(data_channel.get()));
|
||||
ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_CLIENT, &new_id));
|
||||
EXPECT_NE(config.id, new_id);
|
||||
}
|
||||
|
@ -427,14 +427,6 @@ PeerConnection::CreateDataChannel(
|
||||
if (!channel.get())
|
||||
return NULL;
|
||||
|
||||
// If we've already passed the underlying channel's setup phase, have the
|
||||
// MediaStreamSignaling update data channels manually.
|
||||
if (session_->data_channel() != NULL &&
|
||||
session_->data_channel_type() == cricket::DCT_SCTP) {
|
||||
mediastream_signaling_->UpdateLocalSctpDataChannels();
|
||||
mediastream_signaling_->UpdateRemoteSctpDataChannels();
|
||||
}
|
||||
|
||||
observer_->OnRenegotiationNeeded();
|
||||
|
||||
return DataChannelProxy::Create(signaling_thread(), channel.get());
|
||||
|
@ -981,31 +981,6 @@ TEST_F(PeerConnectionInterfaceTest,
|
||||
EXPECT_TRUE(channel == NULL);
|
||||
}
|
||||
|
||||
// The test verifies that the first id not used by existing data channels is
|
||||
// assigned to a new data channel if no id is specified.
|
||||
TEST_F(PeerConnectionInterfaceTest, AssignSctpDataChannelId) {
|
||||
FakeConstraints constraints;
|
||||
constraints.SetAllowDtlsSctpDataChannels();
|
||||
CreatePeerConnection(&constraints);
|
||||
|
||||
webrtc::DataChannelInit config;
|
||||
|
||||
scoped_refptr<DataChannelInterface> channel =
|
||||
pc_->CreateDataChannel("1", &config);
|
||||
EXPECT_TRUE(channel != NULL);
|
||||
EXPECT_EQ(1, channel->id());
|
||||
|
||||
config.id = 4;
|
||||
channel = pc_->CreateDataChannel("4", &config);
|
||||
EXPECT_TRUE(channel != NULL);
|
||||
EXPECT_EQ(config.id, channel->id());
|
||||
|
||||
config.id = -1;
|
||||
channel = pc_->CreateDataChannel("2", &config);
|
||||
EXPECT_TRUE(channel != NULL);
|
||||
EXPECT_EQ(2, channel->id());
|
||||
}
|
||||
|
||||
// The test verifies that creating a SCTP data channel with an id already in use
|
||||
// or out of range should fail.
|
||||
TEST_F(PeerConnectionInterfaceTest,
|
||||
@ -1015,13 +990,13 @@ TEST_F(PeerConnectionInterfaceTest,
|
||||
CreatePeerConnection(&constraints);
|
||||
|
||||
webrtc::DataChannelInit config;
|
||||
scoped_refptr<DataChannelInterface> channel;
|
||||
|
||||
scoped_refptr<DataChannelInterface> channel =
|
||||
pc_->CreateDataChannel("1", &config);
|
||||
config.id = 1;
|
||||
channel = pc_->CreateDataChannel("1", &config);
|
||||
EXPECT_TRUE(channel != NULL);
|
||||
EXPECT_EQ(1, channel->id());
|
||||
|
||||
config.id = 1;
|
||||
channel = pc_->CreateDataChannel("x", &config);
|
||||
EXPECT_TRUE(channel == NULL);
|
||||
|
||||
@ -1095,6 +1070,8 @@ TEST_F(PeerConnectionInterfaceTest, TestRejectDataChannelInAnswer) {
|
||||
// Test that we can create a session description from an SDP string from
|
||||
// FireFox, use it as a remote session description, generate an answer and use
|
||||
// the answer as a local description.
|
||||
// TODO(mallinath): re-enable per
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=2574
|
||||
TEST_F(PeerConnectionInterfaceTest, DISABLED_ReceiveFireFoxOffer) {
|
||||
MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
|
||||
FakeConstraints constraints;
|
||||
|
109
talk/app/webrtc/test/fakedatachannelprovider.h
Normal file
109
talk/app/webrtc/test/fakedatachannelprovider.h
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2013, Google Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "talk/app/webrtc/datachannel.h"
|
||||
|
||||
class FakeDataChannelProvider : public webrtc::DataChannelProviderInterface {
|
||||
public:
|
||||
FakeDataChannelProvider()
|
||||
: id_allocation_should_fail_(false),
|
||||
send_blocked_(false),
|
||||
transport_available_(true) {}
|
||||
virtual ~FakeDataChannelProvider() {}
|
||||
|
||||
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;
|
||||
}
|
||||
last_send_data_params_ = params;
|
||||
return true;
|
||||
}
|
||||
virtual bool ConnectDataChannel(webrtc::DataChannel* data_channel) OVERRIDE {
|
||||
ASSERT(connected_channels_.find(data_channel) == connected_channels_.end());
|
||||
if (!transport_available_) {
|
||||
return false;
|
||||
}
|
||||
LOG(LS_INFO) << "DataChannel connected " << data_channel;
|
||||
connected_channels_.insert(data_channel);
|
||||
return true;
|
||||
}
|
||||
virtual void DisconnectDataChannel(
|
||||
webrtc::DataChannel* data_channel) OVERRIDE {
|
||||
ASSERT(connected_channels_.find(data_channel) != connected_channels_.end());
|
||||
LOG(LS_INFO) << "DataChannel disconnected " << data_channel;
|
||||
connected_channels_.erase(data_channel);
|
||||
}
|
||||
virtual void AddRtpDataStream(uint32 send_ssrc, uint32 recv_ssrc) OVERRIDE {
|
||||
send_ssrcs_.insert(send_ssrc);
|
||||
recv_ssrcs_.insert(recv_ssrc);
|
||||
}
|
||||
virtual void AddSctpDataStream(uint32 sid) OVERRIDE {
|
||||
AddRtpDataStream(sid, sid);
|
||||
}
|
||||
virtual void RemoveRtpDataStream(
|
||||
uint32 send_ssrc, uint32 recv_ssrc) OVERRIDE {
|
||||
send_ssrcs_.erase(send_ssrc);
|
||||
recv_ssrcs_.erase(recv_ssrc);
|
||||
}
|
||||
virtual void RemoveSctpDataStream(uint32 sid) OVERRIDE {
|
||||
RemoveRtpDataStream(sid, sid);
|
||||
}
|
||||
|
||||
void set_send_blocked(bool blocked) {
|
||||
send_blocked_ = blocked;
|
||||
}
|
||||
cricket::SendDataParams last_send_data_params() const {
|
||||
return last_send_data_params_;
|
||||
}
|
||||
void set_id_allocaiton_should_fail(bool fail) {
|
||||
id_allocation_should_fail_ = fail;
|
||||
}
|
||||
void set_transport_available(bool available) {
|
||||
transport_available_ = available;
|
||||
}
|
||||
bool IsConnected(webrtc::DataChannel* data_channel) const {
|
||||
return connected_channels_.find(data_channel) != connected_channels_.end();
|
||||
}
|
||||
bool IsSendStreamAdded(uint32 stream) const {
|
||||
return send_ssrcs_.find(stream) != send_ssrcs_.end();
|
||||
}
|
||||
bool IsRecvStreamAdded(uint32 stream) const {
|
||||
return recv_ssrcs_.find(stream) != recv_ssrcs_.end();
|
||||
}
|
||||
|
||||
private:
|
||||
cricket::SendDataParams last_send_data_params_;
|
||||
bool id_allocation_should_fail_;
|
||||
bool send_blocked_;
|
||||
bool transport_available_;
|
||||
std::set<webrtc::DataChannel*> connected_channels_;
|
||||
std::set<uint32> send_ssrcs_;
|
||||
std::set<uint32> recv_ssrcs_;
|
||||
};
|
@ -2188,6 +2188,12 @@ bool ParseMediaDescription(const std::string& message,
|
||||
return ParseFailed("", description.str(), error);
|
||||
}
|
||||
}
|
||||
|
||||
size_t end_of_message = message.size();
|
||||
if (mline_index == -1 && *pos != end_of_message) {
|
||||
ParseFailed(message, *pos, "Expects m line.", error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1484,6 +1484,19 @@ TEST_F(WebRtcSdpTest, DeserializeSessionDescription) {
|
||||
EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMline) {
|
||||
JsepSessionDescription jdesc(kDummyString);
|
||||
const char kSdpWithoutMline[] =
|
||||
"v=0\r\n"
|
||||
"o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
|
||||
"s=-\r\n"
|
||||
"t=0 0\r\n"
|
||||
"a=msid-semantic: WMS local_stream_1 local_stream_2\r\n";
|
||||
// Deserialize
|
||||
EXPECT_TRUE(SdpDeserialize(kSdpWithoutMline, &jdesc));
|
||||
EXPECT_EQ(0u, jdesc.description()->contents().size());
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCarriageReturn) {
|
||||
JsepSessionDescription jdesc(kDummyString);
|
||||
std::string sdp_without_carriage_return = kSdpFullString;
|
||||
@ -1886,6 +1899,7 @@ TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) {
|
||||
ReplaceAndTryToParse("t=", kSdpDestroyer);
|
||||
|
||||
// Broken media description
|
||||
ReplaceAndTryToParse("m=audio", "c=IN IP4 74.125.224.39");
|
||||
ReplaceAndTryToParse("m=video", kSdpDestroyer);
|
||||
|
||||
// Invalid lines
|
||||
|
@ -42,7 +42,6 @@
|
||||
#include "talk/base/stringencode.h"
|
||||
#include "talk/media/base/constants.h"
|
||||
#include "talk/media/base/videocapturer.h"
|
||||
#include "talk/media/sctp/sctputils.h"
|
||||
#include "talk/session/media/channel.h"
|
||||
#include "talk/session/media/channelmanager.h"
|
||||
#include "talk/session/media/mediasession.h"
|
||||
@ -629,6 +628,10 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
||||
// local session description.
|
||||
mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get());
|
||||
|
||||
talk_base::SSLRole role;
|
||||
if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) {
|
||||
mediastream_signaling_->OnDtlsRoleReadyForSctp(role);
|
||||
}
|
||||
if (error() != cricket::BaseSession::ERROR_NONE) {
|
||||
return BadLocalSdp(SessionErrorMsg(error()), err_desc);
|
||||
}
|
||||
@ -681,6 +684,12 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
||||
ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
|
||||
desc);
|
||||
remote_desc_.reset(desc_temp.release());
|
||||
|
||||
talk_base::SSLRole role;
|
||||
if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) {
|
||||
mediastream_signaling_->OnDtlsRoleReadyForSctp(role);
|
||||
}
|
||||
|
||||
if (error() != cricket::BaseSession::ERROR_NONE) {
|
||||
return BadRemoteSdp(SessionErrorMsg(error()), err_desc);
|
||||
}
|
||||
@ -961,25 +970,49 @@ bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) {
|
||||
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());
|
||||
if (!data_channel_.get()) {
|
||||
LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL.";
|
||||
return;
|
||||
}
|
||||
data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
|
||||
data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
|
||||
}
|
||||
|
||||
void WebRtcSession::AddRtpDataStream(uint32 send_ssrc, uint32 recv_ssrc) {
|
||||
if (!data_channel_.get()) {
|
||||
LOG(LS_ERROR) << "AddDataChannelStreams called when data_channel_ is NULL.";
|
||||
return;
|
||||
}
|
||||
data_channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(recv_ssrc));
|
||||
data_channel_->AddSendStream(cricket::StreamParams::CreateLegacy(send_ssrc));
|
||||
}
|
||||
|
||||
void WebRtcSession::AddSctpDataStream(uint32 sid) {
|
||||
AddRtpDataStream(sid, sid);
|
||||
}
|
||||
|
||||
void WebRtcSession::RemoveRtpDataStream(uint32 send_ssrc, uint32 recv_ssrc) {
|
||||
if (!data_channel_.get()) {
|
||||
LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is "
|
||||
<< "NULL.";
|
||||
return;
|
||||
}
|
||||
data_channel_->RemoveRecvStream(recv_ssrc);
|
||||
data_channel_->RemoveSendStream(send_ssrc);
|
||||
}
|
||||
|
||||
void WebRtcSession::RemoveSctpDataStream(uint32 sid) {
|
||||
RemoveRtpDataStream(sid, sid);
|
||||
}
|
||||
|
||||
talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
|
||||
const std::string& label,
|
||||
const DataChannelInit* config) {
|
||||
@ -994,11 +1027,13 @@ talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
|
||||
|
||||
if (data_channel_type_ == cricket::DCT_SCTP) {
|
||||
if (new_config.id < 0) {
|
||||
if (!mediastream_signaling_->AllocateSctpId(&new_config.id)) {
|
||||
talk_base::SSLRole role;
|
||||
if (GetSslRole(&role) &&
|
||||
!mediastream_signaling_->AllocateSctpSid(role, &new_config.id)) {
|
||||
LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
|
||||
return NULL;
|
||||
}
|
||||
} else if (!mediastream_signaling_->IsSctpIdAvailable(new_config.id)) {
|
||||
} else if (!mediastream_signaling_->IsSctpSidAvailable(new_config.id)) {
|
||||
LOG(LS_ERROR) << "Failed to create a SCTP data channel "
|
||||
<< "because the id is already in use or out of range.";
|
||||
return NULL;
|
||||
@ -1007,30 +1042,9 @@ talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
|
||||
|
||||
talk_base::scoped_refptr<DataChannel> channel(
|
||||
DataChannel::Create(this, data_channel_type_, label, &new_config));
|
||||
if (channel == NULL)
|
||||
if (channel && !mediastream_signaling_->AddDataChannel(channel))
|
||||
return NULL;
|
||||
if (!mediastream_signaling_->AddDataChannel(channel))
|
||||
return NULL;
|
||||
if (data_channel_type_ == cricket::DCT_SCTP) {
|
||||
if (config == NULL) {
|
||||
LOG(LS_WARNING) << "Could not send data channel OPEN message"
|
||||
<< " because of NULL config.";
|
||||
return NULL;
|
||||
}
|
||||
if (data_channel_.get()) {
|
||||
channel->SetReceiveSsrc(new_config.id);
|
||||
channel->SetSendSsrc(new_config.id);
|
||||
}
|
||||
if (!config->negotiated) {
|
||||
talk_base::Buffer *payload = new talk_base::Buffer;
|
||||
if (!cricket::WriteDataChannelOpenMessage(label, *config, payload)) {
|
||||
LOG(LS_WARNING) << "Could not write data channel OPEN message";
|
||||
}
|
||||
// SendControl may queue the message until the data channel's set up,
|
||||
// or congestion clears.
|
||||
channel->SendControl(payload);
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
@ -1353,14 +1367,17 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
|
||||
}
|
||||
|
||||
bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
|
||||
bool rtcp = (data_channel_type_ == cricket::DCT_RTP);
|
||||
bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
|
||||
data_channel_.reset(channel_manager_->CreateDataChannel(
|
||||
this, content->name, rtcp, data_channel_type_));
|
||||
this, content->name, !sctp, data_channel_type_));
|
||||
if (!data_channel_.get()) {
|
||||
return false;
|
||||
}
|
||||
data_channel_->SignalNewStreamReceived.connect(
|
||||
this, &WebRtcSession::OnNewDataChannelReceived);
|
||||
if (sctp) {
|
||||
mediastream_signaling_->OnDataTransportCreatedForSctp();
|
||||
data_channel_->SignalNewStreamReceived.connect(
|
||||
this, &WebRtcSession::OnNewDataChannelReceived);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,10 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
cricket::SendDataResult* result) OVERRIDE;
|
||||
virtual bool ConnectDataChannel(DataChannel* webrtc_data_channel) OVERRIDE;
|
||||
virtual void DisconnectDataChannel(DataChannel* webrtc_data_channel) OVERRIDE;
|
||||
|
||||
virtual void AddRtpDataStream(uint32 send_ssrc, uint32 recv_ssrc) OVERRIDE;
|
||||
virtual void AddSctpDataStream(uint32 sid) OVERRIDE;
|
||||
virtual void RemoveRtpDataStream(uint32 send_ssrc, uint32 recv_ssrc) OVERRIDE;
|
||||
virtual void RemoveSctpDataStream(uint32 sid) OVERRIDE;
|
||||
|
||||
talk_base::scoped_refptr<DataChannel> CreateDataChannel(
|
||||
const std::string& label,
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
#include "talk/base/common.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace talk_base {
|
||||
@ -96,7 +95,8 @@ bool Base64::IsBase64Encoded(const std::string& str) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Base64::EncodeFromArray(const void* data, size_t len, string* result) {
|
||||
void Base64::EncodeFromArray(const void* data, size_t len,
|
||||
std::string* result) {
|
||||
ASSERT(NULL != result);
|
||||
result->clear();
|
||||
result->resize(((len + 2) / 3) * 4);
|
||||
@ -190,8 +190,9 @@ size_t Base64::GetNextQuantum(DecodeFlags parse_flags, bool illegal_pads,
|
||||
}
|
||||
|
||||
bool Base64::DecodeFromArray(const char* data, size_t len, DecodeFlags flags,
|
||||
string* result, size_t* data_used) {
|
||||
return DecodeFromArrayTemplate<string>(data, len, flags, result, data_used);
|
||||
std::string* result, size_t* data_used) {
|
||||
return DecodeFromArrayTemplate<std::string>(
|
||||
data, len, flags, result, data_used);
|
||||
}
|
||||
|
||||
bool Base64::DecodeFromArray(const char* data, size_t len, DecodeFlags flags,
|
||||
|
@ -312,8 +312,10 @@ class LogMessageVoidify {
|
||||
// The _F version prefixes the message with the current function name.
|
||||
#if (defined(__GNUC__) && defined(_DEBUG)) || defined(WANT_PRETTY_LOG_F)
|
||||
#define LOG_F(sev) LOG(sev) << __PRETTY_FUNCTION__ << ": "
|
||||
#define LOG_T_F(sev) LOG(sev) << this << ": " << __PRETTY_FUNCTION__ << ": "
|
||||
#else
|
||||
#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": "
|
||||
#define LOG_T_F(sev) LOG(sev) << this << ": " << __FUNCTION__ << ": "
|
||||
#endif
|
||||
|
||||
#define LOG_CHECK_LEVEL(sev) \
|
||||
@ -331,7 +333,6 @@ inline bool LogCheckLevel(LoggingSeverity sev) {
|
||||
.stream()
|
||||
|
||||
#define LOG_T(sev) LOG(sev) << this << ": "
|
||||
#define LOG_T_F(level) LOG_F(level) << this << ": "
|
||||
|
||||
#else // !LOGGING
|
||||
|
||||
@ -354,7 +355,7 @@ inline bool LogCheckLevel(LoggingSeverity sev) {
|
||||
.stream()
|
||||
|
||||
#define LOG_T(sev) LOG(sev) << this << ": "
|
||||
#define LOG_T_F(level) LOG_F(level) << this << " "
|
||||
#define LOG_T_F(sev) LOG(sev) << this << ": " << __FUNCTION__ <<
|
||||
#endif // !LOGGING
|
||||
|
||||
#define LOG_ERRNO_EX(sev, err) \
|
||||
|
@ -466,6 +466,10 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> {
|
||||
ASSERT((0 <= value) && (value <= 65536));
|
||||
*mtu = value;
|
||||
return 0;
|
||||
#elif defined(__native_client__)
|
||||
// Most socket operations, including this, will fail in NaCl's sandbox.
|
||||
error_ = EACCES;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -71,8 +71,7 @@ void ProfilerEvent::Start() {
|
||||
++start_count_;
|
||||
}
|
||||
|
||||
void ProfilerEvent::Stop() {
|
||||
uint64 stop_time = TimeNanos();
|
||||
void ProfilerEvent::Stop(uint64 stop_time) {
|
||||
--start_count_;
|
||||
ASSERT(start_count_ >= 0);
|
||||
if (start_count_ == 0) {
|
||||
@ -94,6 +93,10 @@ void ProfilerEvent::Stop() {
|
||||
}
|
||||
}
|
||||
|
||||
void ProfilerEvent::Stop() {
|
||||
Stop(TimeNanos());
|
||||
}
|
||||
|
||||
double ProfilerEvent::standard_deviation() const {
|
||||
if (event_count_ <= 1) return 0.0;
|
||||
return sqrt(sum_of_squared_differences_ / (event_count_ - 1.0));
|
||||
@ -105,11 +108,29 @@ Profiler* Profiler::Instance() {
|
||||
}
|
||||
|
||||
void Profiler::StartEvent(const std::string& event_name) {
|
||||
events_[event_name].Start();
|
||||
lock_.LockShared();
|
||||
EventMap::iterator it = events_.find(event_name);
|
||||
bool needs_insert = (it == events_.end());
|
||||
lock_.UnlockShared();
|
||||
|
||||
if (needs_insert) {
|
||||
// Need an exclusive lock to modify the map.
|
||||
ExclusiveScope scope(&lock_);
|
||||
it = events_.insert(
|
||||
EventMap::value_type(event_name, ProfilerEvent())).first;
|
||||
}
|
||||
|
||||
it->second.Start();
|
||||
}
|
||||
|
||||
void Profiler::StopEvent(const std::string& event_name) {
|
||||
events_[event_name].Stop();
|
||||
// Get the time ASAP, then wait for the lock.
|
||||
uint64 stop_time = TimeNanos();
|
||||
SharedScope scope(&lock_);
|
||||
EventMap::iterator it = events_.find(event_name);
|
||||
if (it != events_.end()) {
|
||||
it->second.Stop(stop_time);
|
||||
}
|
||||
}
|
||||
|
||||
void Profiler::ReportToLog(const char* file, int line,
|
||||
@ -118,6 +139,9 @@ void Profiler::ReportToLog(const char* file, int line,
|
||||
if (!LogMessage::Loggable(severity_to_use)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SharedScope scope(&lock_);
|
||||
|
||||
{ // Output first line.
|
||||
LogMessage msg(file, line, severity_to_use);
|
||||
msg.stream() << "=== Profile report ";
|
||||
@ -126,8 +150,8 @@ void Profiler::ReportToLog(const char* file, int line,
|
||||
}
|
||||
msg.stream() << "===";
|
||||
}
|
||||
typedef std::map<std::string, ProfilerEvent>::const_iterator iterator;
|
||||
for (iterator it = events_.begin(); it != events_.end(); ++it) {
|
||||
for (EventMap::const_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 << " " << it->second;
|
||||
@ -143,15 +167,17 @@ void Profiler::ReportAllToLog(const char* file, int line,
|
||||
}
|
||||
|
||||
const ProfilerEvent* Profiler::GetEvent(const std::string& event_name) const {
|
||||
std::map<std::string, ProfilerEvent>::const_iterator it =
|
||||
SharedScope scope(&lock_);
|
||||
EventMap::const_iterator it =
|
||||
events_.find(event_name);
|
||||
return (it == events_.end()) ? NULL : &it->second;
|
||||
}
|
||||
|
||||
bool Profiler::Clear() {
|
||||
ExclusiveScope scope(&lock_);
|
||||
bool result = true;
|
||||
// Clear all events that aren't started.
|
||||
std::map<std::string, ProfilerEvent>::iterator it = events_.begin();
|
||||
EventMap::iterator it = events_.begin();
|
||||
while (it != events_.end()) {
|
||||
if (it->second.is_started()) {
|
||||
++it; // Can't clear started events.
|
||||
|
@ -37,7 +37,7 @@
|
||||
// }
|
||||
// Another example:
|
||||
// void StartAsyncProcess() {
|
||||
// PROFILE_START("My event");
|
||||
// PROFILE_START("My async event");
|
||||
// DoSomethingAsyncAndThenCall(&Callback);
|
||||
// }
|
||||
// void Callback() {
|
||||
@ -54,6 +54,7 @@
|
||||
#include "talk/base/basictypes.h"
|
||||
#include "talk/base/common.h"
|
||||
#include "talk/base/logging.h"
|
||||
#include "talk/base/sharedexclusivelock.h"
|
||||
|
||||
// Profiling could be switched via a build flag, but for now, it's always on.
|
||||
#define ENABLE_PROFILING
|
||||
@ -105,6 +106,7 @@ class ProfilerEvent {
|
||||
ProfilerEvent();
|
||||
void Start();
|
||||
void Stop();
|
||||
void Stop(uint64 stop_time);
|
||||
double standard_deviation() const;
|
||||
double total_time() const { return total_time_; }
|
||||
double mean() const { return mean_; }
|
||||
@ -142,7 +144,9 @@ class Profiler {
|
||||
private:
|
||||
Profiler() {}
|
||||
|
||||
std::map<std::string, ProfilerEvent> events_;
|
||||
typedef std::map<std::string, ProfilerEvent> EventMap;
|
||||
EventMap events_;
|
||||
mutable SharedExclusiveLock lock_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Profiler);
|
||||
};
|
||||
|
@ -391,6 +391,7 @@
|
||||
'app/webrtc/test/fakeaudiocapturemodule.h',
|
||||
'app/webrtc/test/fakeaudiocapturemodule_unittest.cc',
|
||||
'app/webrtc/test/fakeconstraints.h',
|
||||
'app/webrtc/test/fakedatachannelprovider.h',
|
||||
'app/webrtc/test/fakedtlsidentityservice.h',
|
||||
'app/webrtc/test/fakemediastreamsignaling.h',
|
||||
'app/webrtc/test/fakeperiodicvideocapturer.h',
|
||||
|
@ -306,6 +306,7 @@ struct VideoOptions {
|
||||
system_high_adaptation_threshhold.SetFrom(
|
||||
change.system_high_adaptation_threshhold);
|
||||
buffered_mode_latency.SetFrom(change.buffered_mode_latency);
|
||||
lower_min_bitrate.SetFrom(change.lower_min_bitrate);
|
||||
}
|
||||
|
||||
bool operator==(const VideoOptions& o) const {
|
||||
@ -329,7 +330,8 @@ struct VideoOptions {
|
||||
o.system_low_adaptation_threshhold &&
|
||||
system_high_adaptation_threshhold ==
|
||||
o.system_high_adaptation_threshhold &&
|
||||
buffered_mode_latency == o.buffered_mode_latency;
|
||||
buffered_mode_latency == o.buffered_mode_latency &&
|
||||
lower_min_bitrate == o.lower_min_bitrate;
|
||||
}
|
||||
|
||||
std::string ToString() const {
|
||||
@ -356,6 +358,7 @@ struct VideoOptions {
|
||||
ost << ToStringIfSet("low", system_low_adaptation_threshhold);
|
||||
ost << ToStringIfSet("high", system_high_adaptation_threshhold);
|
||||
ost << ToStringIfSet("buffered mode latency", buffered_mode_latency);
|
||||
ost << ToStringIfSet("lower min bitrate", lower_min_bitrate);
|
||||
ost << "}";
|
||||
return ost.str();
|
||||
}
|
||||
@ -400,6 +403,8 @@ struct VideoOptions {
|
||||
SettablePercent system_high_adaptation_threshhold;
|
||||
// Specify buffered mode latency in milliseconds.
|
||||
Settable<int> buffered_mode_latency;
|
||||
// Make minimum configured send bitrate even lower than usual, at 30kbit.
|
||||
Settable<bool> lower_min_bitrate;
|
||||
};
|
||||
|
||||
// A class for playing out soundclips.
|
||||
|
@ -33,6 +33,7 @@ namespace cricket {
|
||||
|
||||
const char kFecSsrcGroupSemantics[] = "FEC";
|
||||
const char kFidSsrcGroupSemantics[] = "FID";
|
||||
const char kSimSsrcGroupSemantics[] = "SIM";
|
||||
|
||||
static std::string SsrcsToString(const std::vector<uint32>& ssrcs) {
|
||||
std::ostringstream ost;
|
||||
|
@ -31,6 +31,14 @@
|
||||
// described by one StreamParams object
|
||||
// SsrcGroup is used to describe the relationship between the SSRCs that
|
||||
// are used for this media source.
|
||||
// E.x: Consider a source that is sent as 3 simulcast streams
|
||||
// Let the simulcast elements have SSRC 10, 20, 30.
|
||||
// Let each simulcast element use FEC and let the protection packets have
|
||||
// SSRC 11,21,31.
|
||||
// To describe this 4 SsrcGroups are needed,
|
||||
// StreamParams would then contain ssrc = {10,11,20,21,30,31} and
|
||||
// ssrc_groups = {{SIM,{10,20,30}, {FEC,{10,11}, {FEC, {20,21}, {FEC {30,31}}}
|
||||
// Please see RFC 5576.
|
||||
|
||||
#ifndef TALK_MEDIA_BASE_STREAMPARAMS_H_
|
||||
#define TALK_MEDIA_BASE_STREAMPARAMS_H_
|
||||
@ -46,6 +54,7 @@ namespace cricket {
|
||||
|
||||
extern const char kFecSsrcGroupSemantics[];
|
||||
extern const char kFidSsrcGroupSemantics[];
|
||||
extern const char kSimSsrcGroupSemantics[];
|
||||
|
||||
struct SsrcGroup {
|
||||
SsrcGroup(const std::string& usage, const std::vector<uint32>& ssrcs)
|
||||
|
@ -278,18 +278,14 @@ VideoCapturer* DeviceManager::CreateDesktopCapturer(
|
||||
bool DeviceManager::GetAudioDevices(bool input,
|
||||
std::vector<Device>* devs) {
|
||||
devs->clear();
|
||||
#if defined(IOS) || defined(ANDROID)
|
||||
// Under Android, we don't access the device file directly.
|
||||
// Arbitrary use 0 for the mic and 1 for the output.
|
||||
// These ids are used in MediaEngine::SetSoundDevices(in, out);
|
||||
// The strings are for human consumption.
|
||||
if (input) {
|
||||
devs->push_back(Device("audiorecord", 0));
|
||||
} else {
|
||||
devs->push_back(Device("audiotrack", 1));
|
||||
}
|
||||
#if defined(ANDROID)
|
||||
// Under Android, 0 is always required for the playout device and 0 is the
|
||||
// default for the recording device.
|
||||
devs->push_back(Device("default-device", 0));
|
||||
return true;
|
||||
#else
|
||||
// Other platforms either have their own derived class implementation
|
||||
// (desktop) or don't use device manager for audio devices (iOS).
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
@ -1046,14 +1046,12 @@ class FakeWebRtcVideoEngine
|
||||
return 0;
|
||||
}
|
||||
WEBRTC_STUB(EnableColorEnhancement, (const int, const bool));
|
||||
#ifdef USE_WEBRTC_DEV_BRANCH
|
||||
WEBRTC_VOID_STUB(RegisterPreEncodeCallback,
|
||||
(int, webrtc::I420FrameCallback*));
|
||||
WEBRTC_VOID_STUB(DeRegisterPreEncodeCallback, (int));
|
||||
WEBRTC_VOID_STUB(RegisterPreRenderCallback,
|
||||
(int, webrtc::I420FrameCallback*));
|
||||
WEBRTC_VOID_STUB(DeRegisterPreRenderCallback, (int));
|
||||
#endif
|
||||
// webrtc::ViEExternalCodec
|
||||
WEBRTC_FUNC(RegisterExternalSendCodec,
|
||||
(const int channel, const unsigned char pl_type, webrtc::VideoEncoder*,
|
||||
|
@ -93,6 +93,9 @@ static const int kStartVideoBitrate = 300;
|
||||
static const int kMaxVideoBitrate = 2000;
|
||||
static const int kDefaultConferenceModeMaxVideoBitrate = 500;
|
||||
|
||||
// Controlled by exp, try a super low minimum bitrate for poor connections.
|
||||
static const int kLowerMinBitrate = 30;
|
||||
|
||||
static const int kVideoMtu = 1200;
|
||||
|
||||
static const int kVideoRtpBufferSize = 65536;
|
||||
@ -2568,7 +2571,7 @@ bool WebRtcVideoMediaChannel::SetSendBandwidth(bool autobw, int bps) {
|
||||
int max_bitrate;
|
||||
if (autobw) {
|
||||
// Use the default values for min bitrate.
|
||||
min_bitrate = kMinVideoBitrate;
|
||||
min_bitrate = send_min_bitrate_;
|
||||
// Use the default value or the bps for the max
|
||||
max_bitrate = (bps <= 0) ? send_max_bitrate_ : (bps / 1000);
|
||||
// Maximum start bitrate can be kStartVideoBitrate.
|
||||
@ -2631,6 +2634,17 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) {
|
||||
// Adjust send codec bitrate if needed.
|
||||
int conf_max_bitrate = kDefaultConferenceModeMaxVideoBitrate;
|
||||
|
||||
// Save altered min_bitrate level and apply if necessary.
|
||||
bool adjusted_min_bitrate = false;
|
||||
if (options.lower_min_bitrate.IsSet()) {
|
||||
bool lower;
|
||||
options.lower_min_bitrate.Get(&lower);
|
||||
|
||||
int new_send_min_bitrate = lower ? kLowerMinBitrate : kMinVideoBitrate;
|
||||
adjusted_min_bitrate = (new_send_min_bitrate != send_min_bitrate_);
|
||||
send_min_bitrate_ = new_send_min_bitrate;
|
||||
}
|
||||
|
||||
int expected_bitrate = send_max_bitrate_;
|
||||
if (InConferenceMode()) {
|
||||
expected_bitrate = conf_max_bitrate;
|
||||
@ -2642,7 +2656,8 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) {
|
||||
}
|
||||
|
||||
if (send_codec_ &&
|
||||
(send_max_bitrate_ != expected_bitrate || denoiser_changed)) {
|
||||
(send_max_bitrate_ != expected_bitrate || denoiser_changed ||
|
||||
adjusted_min_bitrate)) {
|
||||
// On success, SetSendCodec() will reset send_max_bitrate_ to
|
||||
// expected_bitrate.
|
||||
if (!SetSendCodec(*send_codec_,
|
||||
|
@ -380,6 +380,25 @@ TEST_F(WebRtcVideoEngineTestFake, SetOptionsWithMaxBitrate) {
|
||||
channel_num, kVP8Codec.width, kVP8Codec.height, 0, 20, 10, 20);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoEngineTestFake, SetOptionsWithLoweredBitrate) {
|
||||
EXPECT_TRUE(SetupEngine());
|
||||
int channel_num = vie_.GetLastChannel();
|
||||
std::vector<cricket::VideoCodec> codecs(engine_.codecs());
|
||||
codecs[0].params[cricket::kCodecParamMinBitrate] = "50";
|
||||
codecs[0].params[cricket::kCodecParamMaxBitrate] = "100";
|
||||
EXPECT_TRUE(channel_->SetSendCodecs(codecs));
|
||||
|
||||
VerifyVP8SendCodec(
|
||||
channel_num, kVP8Codec.width, kVP8Codec.height, 0, 100, 50, 100);
|
||||
|
||||
// Verify that min bitrate changes after SetOptions().
|
||||
cricket::VideoOptions options;
|
||||
options.lower_min_bitrate.Set(true);
|
||||
EXPECT_TRUE(channel_->SetOptions(options));
|
||||
VerifyVP8SendCodec(
|
||||
channel_num, kVP8Codec.width, kVP8Codec.height, 0, 100, 30, 100);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoEngineTestFake, MaxBitrateResetWithConferenceMode) {
|
||||
EXPECT_TRUE(SetupEngine());
|
||||
int channel_num = vie_.GetLastChannel();
|
||||
|
@ -885,7 +885,7 @@ struct ResumeEntry {
|
||||
// soundclip device. At that time, reinstate the soundclip pause/resume code.
|
||||
bool WebRtcVoiceEngine::SetDevices(const Device* in_device,
|
||||
const Device* out_device) {
|
||||
#if !defined(IOS) && !defined(ANDROID)
|
||||
#if !defined(IOS)
|
||||
int in_id = in_device ? talk_base::FromString<int>(in_device->id) :
|
||||
kDefaultAudioDeviceId;
|
||||
int out_id = out_device ? talk_base::FromString<int>(out_device->id) :
|
||||
@ -982,13 +982,13 @@ bool WebRtcVoiceEngine::SetDevices(const Device* in_device,
|
||||
return ret;
|
||||
#else
|
||||
return true;
|
||||
#endif // !IOS && !ANDROID
|
||||
#endif // !IOS
|
||||
}
|
||||
|
||||
bool WebRtcVoiceEngine::FindWebRtcAudioDeviceId(
|
||||
bool is_input, const std::string& dev_name, int dev_id, int* rtc_id) {
|
||||
// In Linux, VoiceEngine uses the same device dev_id as the device manager.
|
||||
#ifdef LINUX
|
||||
#if defined(LINUX) || defined(ANDROID)
|
||||
*rtc_id = dev_id;
|
||||
return true;
|
||||
#else
|
||||
|
@ -242,13 +242,12 @@ static bool GenerateCname(const StreamParamsVec& params_vec,
|
||||
}
|
||||
|
||||
// Generate random SSRC values that are not already present in |params_vec|.
|
||||
// Either 2 or 1 ssrcs will be generated based on |include_rtx_stream| being
|
||||
// true or false. The generated values are added to |ssrcs|.
|
||||
// The generated values are added to |ssrcs|.
|
||||
// |num_ssrcs| is the number of the SSRC will be generated.
|
||||
static void GenerateSsrcs(const StreamParamsVec& params_vec,
|
||||
bool include_rtx_stream,
|
||||
int num_ssrcs,
|
||||
std::vector<uint32>* ssrcs) {
|
||||
unsigned int num_ssrcs = include_rtx_stream ? 2 : 1;
|
||||
for (unsigned int i = 0; i < num_ssrcs; i++) {
|
||||
for (int i = 0; i < num_ssrcs; i++) {
|
||||
uint32 candidate;
|
||||
do {
|
||||
candidate = talk_base::CreateRandomNonZeroId();
|
||||
@ -428,7 +427,8 @@ static bool AddStreamParams(
|
||||
if (IsSctp(content_description)) {
|
||||
GenerateSctpSids(*current_streams, &ssrcs);
|
||||
} else {
|
||||
GenerateSsrcs(*current_streams, include_rtx_stream, &ssrcs);
|
||||
int num_ssrcs = include_rtx_stream ? 2 : 1;
|
||||
GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
|
||||
}
|
||||
if (include_rtx_stream) {
|
||||
content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
|
||||
@ -462,13 +462,23 @@ static bool AddStreamParams(
|
||||
if (IsSctp(content_description)) {
|
||||
GenerateSctpSids(*current_streams, &ssrcs);
|
||||
} else {
|
||||
GenerateSsrcs(*current_streams, include_rtx_stream, &ssrcs);
|
||||
GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
|
||||
}
|
||||
StreamParams stream_param;
|
||||
stream_param.id = stream_it->id;
|
||||
stream_param.ssrcs.push_back(ssrcs[0]);
|
||||
// Add the generated ssrc.
|
||||
for (size_t i = 0; i < ssrcs.size(); ++i) {
|
||||
stream_param.ssrcs.push_back(ssrcs[i]);
|
||||
}
|
||||
if (stream_it->num_sim_layers > 1) {
|
||||
SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
|
||||
stream_param.ssrc_groups.push_back(group);
|
||||
}
|
||||
// Generate an extra ssrc for include_rtx_stream case.
|
||||
if (include_rtx_stream) {
|
||||
stream_param.AddFidSsrc(ssrcs[0], ssrcs[1]);
|
||||
std::vector<uint32> rtx_ssrc;
|
||||
GenerateSsrcs(*current_streams, 1, &rtx_ssrc);
|
||||
stream_param.AddFidSsrc(ssrcs[0], rtx_ssrc[0]);
|
||||
content_description->set_multistream(true);
|
||||
}
|
||||
stream_param.cname = cname;
|
||||
@ -1017,7 +1027,22 @@ static bool IsDtlsActive(
|
||||
void MediaSessionOptions::AddStream(MediaType type,
|
||||
const std::string& id,
|
||||
const std::string& sync_label) {
|
||||
streams.push_back(Stream(type, id, sync_label));
|
||||
AddStreamInternal(type, id, sync_label, 1);
|
||||
}
|
||||
|
||||
void MediaSessionOptions::AddVideoStream(
|
||||
const std::string& id,
|
||||
const std::string& sync_label,
|
||||
int num_sim_layers) {
|
||||
AddStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
|
||||
}
|
||||
|
||||
void MediaSessionOptions::AddStreamInternal(
|
||||
MediaType type,
|
||||
const std::string& id,
|
||||
const std::string& sync_label,
|
||||
int num_sim_layers) {
|
||||
streams.push_back(Stream(type, id, sync_label, num_sim_layers));
|
||||
|
||||
if (type == MEDIA_TYPE_VIDEO)
|
||||
has_video = true;
|
||||
|
@ -105,8 +105,18 @@ struct MediaSessionOptions {
|
||||
void AddStream(MediaType type,
|
||||
const std::string& id,
|
||||
const std::string& sync_label);
|
||||
void AddVideoStream(const std::string& id,
|
||||
const std::string& sync_label,
|
||||
int num_sim_layers);
|
||||
void RemoveStream(MediaType type, const std::string& id);
|
||||
|
||||
|
||||
// Helper function.
|
||||
void AddStreamInternal(MediaType type,
|
||||
const std::string& id,
|
||||
const std::string& sync_label,
|
||||
int num_sim_layers);
|
||||
|
||||
bool has_audio;
|
||||
bool has_video;
|
||||
DataChannelType data_channel_type;
|
||||
@ -122,12 +132,15 @@ struct MediaSessionOptions {
|
||||
struct Stream {
|
||||
Stream(MediaType type,
|
||||
const std::string& id,
|
||||
const std::string& sync_label)
|
||||
: type(type), id(id), sync_label(sync_label) {
|
||||
const std::string& sync_label,
|
||||
int num_sim_layers)
|
||||
: type(type), id(id), sync_label(sync_label),
|
||||
num_sim_layers(num_sim_layers) {
|
||||
}
|
||||
MediaType type;
|
||||
std::string id;
|
||||
std::string sync_label;
|
||||
int num_sim_layers;
|
||||
};
|
||||
|
||||
typedef std::vector<Stream> Streams;
|
||||
|
@ -1162,6 +1162,28 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) {
|
||||
EXPECT_EQ(updated_data_streams[0].cname, updated_data_streams[1].cname);
|
||||
}
|
||||
|
||||
// Create an offer with simulcast video stream.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSimulcastVideoOffer) {
|
||||
MediaSessionOptions opts;
|
||||
const int num_sim_layers = 3;
|
||||
opts.AddVideoStream(kVideoTrack1, kMediaStream1, num_sim_layers);
|
||||
talk_base::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL));
|
||||
|
||||
ASSERT_TRUE(offer.get() != NULL);
|
||||
const ContentInfo* vc = offer->GetContentByName("video");
|
||||
ASSERT_TRUE(vc != NULL);
|
||||
const VideoContentDescription* vcd =
|
||||
static_cast<const VideoContentDescription*>(vc->description);
|
||||
|
||||
const StreamParamsVec& video_streams = vcd->streams();
|
||||
ASSERT_EQ(1U, video_streams.size());
|
||||
EXPECT_EQ(kVideoTrack1, video_streams[0].id);
|
||||
const SsrcGroup* sim_ssrc_group =
|
||||
video_streams[0].get_ssrc_group(cricket::kSimSsrcGroupSemantics);
|
||||
ASSERT_TRUE(sim_ssrc_group != NULL);
|
||||
EXPECT_EQ(static_cast<size_t>(num_sim_layers), sim_ssrc_group->ssrcs.size());
|
||||
}
|
||||
|
||||
// Create an audio and video answer to a standard video offer with:
|
||||
// - one video track
|
||||
// - two audio tracks
|
||||
|
Loading…
x
Reference in New Issue
Block a user