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/app/webrtc/mediastreamprovider.h"
|
||||||
#include "talk/base/logging.h"
|
#include "talk/base/logging.h"
|
||||||
#include "talk/base/refcount.h"
|
#include "talk/base/refcount.h"
|
||||||
|
#include "talk/media/sctp/sctputils.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -68,36 +69,35 @@ DataChannel::DataChannel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool DataChannel::Init(const DataChannelInit* config) {
|
bool DataChannel::Init(const DataChannelInit* config) {
|
||||||
if (config) {
|
if (data_channel_type_ == cricket::DCT_RTP &&
|
||||||
if (data_channel_type_ == cricket::DCT_RTP &&
|
(config->reliable ||
|
||||||
(config->reliable ||
|
config->id != -1 ||
|
||||||
config->id != -1 ||
|
config->maxRetransmits != -1 ||
|
||||||
config->maxRetransmits != -1 ||
|
config->maxRetransmitTime != -1)) {
|
||||||
config->maxRetransmitTime != -1)) {
|
LOG(LS_ERROR) << "Failed to initialize the RTP data channel due to "
|
||||||
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.";
|
<< "invalid DataChannelInit.";
|
||||||
return false;
|
return false;
|
||||||
} else if (data_channel_type_ == cricket::DCT_SCTP) {
|
}
|
||||||
if (config->id < -1 ||
|
if (config->maxRetransmits != -1 && config->maxRetransmitTime != -1) {
|
||||||
config->maxRetransmits < -1 ||
|
LOG(LS_ERROR) <<
|
||||||
config->maxRetransmitTime < -1) {
|
"maxRetransmits and maxRetransmitTime should not be both set.";
|
||||||
LOG(LS_ERROR) << "Failed to initialize the SCTP data channel due to "
|
return false;
|
||||||
<< "invalid DataChannelInit.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (config->maxRetransmits != -1 && config->maxRetransmitTime != -1) {
|
|
||||||
LOG(LS_ERROR) <<
|
|
||||||
"maxRetransmits and maxRetransmitTime should not be both set.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
config_ = *config;
|
config_ = *config;
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DataChannel::HasNegotiationCompleted() {
|
// Try to connect to the transport in case the transport channel already
|
||||||
return send_ssrc_set_ == receive_ssrc_set_;
|
// exists.
|
||||||
|
OnTransportChannelCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataChannel::~DataChannel() {
|
DataChannel::~DataChannel() {
|
||||||
@ -169,16 +169,13 @@ void DataChannel::QueueControl(const talk_base::Buffer* buffer) {
|
|||||||
queued_control_data_.push(buffer);
|
queued_control_data_.push(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DataChannel::SendControl(const talk_base::Buffer* buffer) {
|
bool DataChannel::SendOpenMessage(const talk_base::Buffer* raw_buffer) {
|
||||||
if (data_channel_type_ == cricket::DCT_RTP) {
|
ASSERT(data_channel_type_ == cricket::DCT_SCTP &&
|
||||||
delete buffer;
|
was_ever_writable_ &&
|
||||||
return false;
|
config_.id >= 0 &&
|
||||||
}
|
!config_.negotiated);
|
||||||
|
|
||||||
if (state_ != kOpen) {
|
talk_base::scoped_ptr<const talk_base::Buffer> buffer(raw_buffer);
|
||||||
QueueControl(buffer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
cricket::SendDataParams send_params;
|
cricket::SendDataParams send_params;
|
||||||
send_params.ssrc = config_.id;
|
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);
|
bool retval = provider_->SendData(send_params, *buffer, &send_result);
|
||||||
if (!retval && send_result == cricket::SDR_BLOCK) {
|
if (!retval && send_result == cricket::SDR_BLOCK) {
|
||||||
// Link is congested. Queue for later.
|
// Link is congested. Queue for later.
|
||||||
QueueControl(buffer);
|
QueueControl(buffer.release());
|
||||||
} else {
|
|
||||||
delete buffer;
|
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataChannel::SetReceiveSsrc(uint32 receive_ssrc) {
|
void DataChannel::SetReceiveSsrc(uint32 receive_ssrc) {
|
||||||
|
ASSERT(data_channel_type_ == cricket::DCT_RTP);
|
||||||
|
|
||||||
if (receive_ssrc_set_) {
|
if (receive_ssrc_set_) {
|
||||||
ASSERT(data_channel_type_ == cricket::DCT_RTP ||
|
|
||||||
!send_ssrc_set_ ||
|
|
||||||
receive_ssrc_ == send_ssrc_);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
receive_ssrc_ = receive_ssrc;
|
receive_ssrc_ = receive_ssrc;
|
||||||
@ -214,10 +208,8 @@ void DataChannel::RemotePeerRequestClose() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DataChannel::SetSendSsrc(uint32 send_ssrc) {
|
void DataChannel::SetSendSsrc(uint32 send_ssrc) {
|
||||||
|
ASSERT(data_channel_type_ == cricket::DCT_RTP);
|
||||||
if (send_ssrc_set_) {
|
if (send_ssrc_set_) {
|
||||||
ASSERT(data_channel_type_ == cricket::DCT_RTP ||
|
|
||||||
!receive_ssrc_set_ ||
|
|
||||||
receive_ssrc_ == send_ssrc_);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
send_ssrc_ = send_ssrc;
|
send_ssrc_ = send_ssrc;
|
||||||
@ -263,8 +255,18 @@ void DataChannel::OnChannelReady(bool writable) {
|
|||||||
// for sending and now unblocked, so send the queued data now.
|
// for sending and now unblocked, so send the queued data now.
|
||||||
if (!was_ever_writable_) {
|
if (!was_ever_writable_) {
|
||||||
was_ever_writable_ = true;
|
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();
|
UpdateState();
|
||||||
DeliverQueuedControlData();
|
|
||||||
ASSERT(queued_send_data_.empty());
|
ASSERT(queued_send_data_.empty());
|
||||||
} else if (state_ == kOpen) {
|
} else if (state_ == kOpen) {
|
||||||
DeliverQueuedSendData();
|
DeliverQueuedSendData();
|
||||||
@ -281,11 +283,15 @@ void DataChannel::DoClose() {
|
|||||||
void DataChannel::UpdateState() {
|
void DataChannel::UpdateState() {
|
||||||
switch (state_) {
|
switch (state_) {
|
||||||
case kConnecting: {
|
case kConnecting: {
|
||||||
if (HasNegotiationCompleted()) {
|
if (send_ssrc_set_ == receive_ssrc_set_) {
|
||||||
if (!connected_to_provider_) {
|
if (data_channel_type_ == cricket::DCT_RTP && !connected_to_provider_) {
|
||||||
ConnectToDataSession();
|
connected_to_provider_ = provider_->ConnectDataChannel(this);
|
||||||
|
provider_->AddRtpDataStream(send_ssrc_, receive_ssrc_);
|
||||||
}
|
}
|
||||||
if (was_ever_writable_) {
|
if (was_ever_writable_) {
|
||||||
|
// TODO(jiayl): Do not transition to kOpen if we failed to send the
|
||||||
|
// OPEN message.
|
||||||
|
DeliverQueuedControlData();
|
||||||
SetState(kOpen);
|
SetState(kOpen);
|
||||||
// If we have received buffers before the channel got writable.
|
// If we have received buffers before the channel got writable.
|
||||||
// Deliver them now.
|
// Deliver them now.
|
||||||
@ -298,10 +304,9 @@ void DataChannel::UpdateState() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kClosing: {
|
case kClosing: {
|
||||||
if (connected_to_provider_) {
|
DisconnectFromTransport();
|
||||||
DisconnectFromDataSession();
|
|
||||||
}
|
if (!send_ssrc_set_ && !receive_ssrc_set_) {
|
||||||
if (HasNegotiationCompleted()) {
|
|
||||||
SetState(kClosed);
|
SetState(kClosed);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -318,13 +323,18 @@ void DataChannel::SetState(DataState state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataChannel::ConnectToDataSession() {
|
void DataChannel::DisconnectFromTransport() {
|
||||||
connected_to_provider_ = provider_->ConnectDataChannel(this);
|
if (!connected_to_provider_)
|
||||||
}
|
return;
|
||||||
|
|
||||||
void DataChannel::DisconnectFromDataSession() {
|
|
||||||
provider_->DisconnectDataChannel(this);
|
provider_->DisconnectDataChannel(this);
|
||||||
connected_to_provider_ = false;
|
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() {
|
void DataChannel::DeliverQueuedReceivedData() {
|
||||||
@ -349,10 +359,12 @@ void DataChannel::ClearQueuedReceivedData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DataChannel::DeliverQueuedSendData() {
|
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();
|
DeliverQueuedControlData();
|
||||||
if (!was_ever_writable_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!queued_send_data_.empty()) {
|
while (!queued_send_data_.empty()) {
|
||||||
DataBuffer* buffer = queued_send_data_.front();
|
DataBuffer* buffer = queued_send_data_.front();
|
||||||
@ -376,12 +388,11 @@ void DataChannel::ClearQueuedControlData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DataChannel::DeliverQueuedControlData() {
|
void DataChannel::DeliverQueuedControlData() {
|
||||||
if (was_ever_writable_) {
|
ASSERT(was_ever_writable_);
|
||||||
while (!queued_control_data_.empty()) {
|
while (!queued_control_data_.empty()) {
|
||||||
const talk_base::Buffer *buf = queued_control_data_.front();
|
const talk_base::Buffer* buf = queued_control_data_.front();
|
||||||
queued_control_data_.pop();
|
queued_control_data_.pop();
|
||||||
SendControl(buf);
|
SendOpenMessage(buf);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,4 +428,22 @@ bool DataChannel::QueueSendData(const DataBuffer& buffer) {
|
|||||||
return true;
|
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
|
} // namespace webrtc
|
||||||
|
@ -44,24 +44,35 @@ class DataChannel;
|
|||||||
|
|
||||||
class DataChannelProviderInterface {
|
class DataChannelProviderInterface {
|
||||||
public:
|
public:
|
||||||
|
// Sends the data to the transport.
|
||||||
virtual bool SendData(const cricket::SendDataParams& params,
|
virtual bool SendData(const cricket::SendDataParams& params,
|
||||||
const talk_base::Buffer& payload,
|
const talk_base::Buffer& payload,
|
||||||
cricket::SendDataResult* result) = 0;
|
cricket::SendDataResult* result) = 0;
|
||||||
|
// Connects to the transport signals.
|
||||||
virtual bool ConnectDataChannel(DataChannel* data_channel) = 0;
|
virtual bool ConnectDataChannel(DataChannel* data_channel) = 0;
|
||||||
|
// Disconnects from the transport signals.
|
||||||
virtual void DisconnectDataChannel(DataChannel* data_channel) = 0;
|
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:
|
protected:
|
||||||
virtual ~DataChannelProviderInterface() {}
|
virtual ~DataChannelProviderInterface() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// DataChannel is a an implementation of the DataChannelInterface based on
|
// DataChannel is a an implementation of the DataChannelInterface based on
|
||||||
// libjingle's data engine. It provides an implementation of unreliable data
|
// libjingle's data engine. It provides an implementation of unreliable or
|
||||||
// channels. Currently this class is specifically designed to use RtpDataEngine,
|
// reliabledata channels. Currently this class is specifically designed to use
|
||||||
// and will changed to use SCTP in the future.
|
// both RtpDataEngine and SctpDataEngine.
|
||||||
|
|
||||||
// DataChannel states:
|
// DataChannel states:
|
||||||
// kConnecting: The channel has been created but SSRC for sending and receiving
|
// kConnecting: The channel has been created the transport might not yet be
|
||||||
// has not yet been set and the transport might not yet be ready.
|
// ready.
|
||||||
// kOpen: The channel have a local SSRC set by a call to UpdateSendSsrc
|
// 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
|
// and a remote SSRC set by call to UpdateReceiveSsrc and the transport
|
||||||
// has been writable once.
|
// has been writable once.
|
||||||
@ -73,7 +84,7 @@ class DataChannel : public DataChannelInterface,
|
|||||||
public sigslot::has_slots<> {
|
public sigslot::has_slots<> {
|
||||||
public:
|
public:
|
||||||
static talk_base::scoped_refptr<DataChannel> Create(
|
static talk_base::scoped_refptr<DataChannel> Create(
|
||||||
DataChannelProviderInterface* client,
|
DataChannelProviderInterface* provider,
|
||||||
cricket::DataChannelType dct,
|
cricket::DataChannelType dct,
|
||||||
const std::string& label,
|
const std::string& label,
|
||||||
const DataChannelInit* config);
|
const DataChannelInit* config);
|
||||||
@ -97,20 +108,6 @@ class DataChannel : public DataChannelInterface,
|
|||||||
virtual void Close();
|
virtual void Close();
|
||||||
virtual DataState state() const { return state_; }
|
virtual DataState state() const { return state_; }
|
||||||
virtual bool Send(const DataBuffer& buffer);
|
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.
|
// Called if the underlying data engine is closing.
|
||||||
void OnDataEngineClose();
|
void OnDataEngineClose();
|
||||||
@ -125,20 +122,38 @@ class DataChannel : public DataChannelInterface,
|
|||||||
const cricket::ReceiveDataParams& params,
|
const cricket::ReceiveDataParams& params,
|
||||||
const talk_base::Buffer& payload);
|
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:
|
protected:
|
||||||
DataChannel(DataChannelProviderInterface* client,
|
DataChannel(DataChannelProviderInterface* client,
|
||||||
cricket::DataChannelType dct,
|
cricket::DataChannelType dct,
|
||||||
const std::string& label);
|
const std::string& label);
|
||||||
virtual ~DataChannel();
|
virtual ~DataChannel();
|
||||||
|
|
||||||
bool Init(const DataChannelInit* config);
|
|
||||||
bool HasNegotiationCompleted();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool Init(const DataChannelInit* config);
|
||||||
void DoClose();
|
void DoClose();
|
||||||
void UpdateState();
|
void UpdateState();
|
||||||
void SetState(DataState state);
|
void SetState(DataState state);
|
||||||
void DisconnectFromDataSession();
|
void DisconnectFromTransport();
|
||||||
void DeliverQueuedControlData();
|
void DeliverQueuedControlData();
|
||||||
void QueueControl(const talk_base::Buffer* buffer);
|
void QueueControl(const talk_base::Buffer* buffer);
|
||||||
void ClearQueuedControlData();
|
void ClearQueuedControlData();
|
||||||
@ -149,6 +164,8 @@ class DataChannel : public DataChannelInterface,
|
|||||||
bool InternalSendWithoutQueueing(const DataBuffer& buffer,
|
bool InternalSendWithoutQueueing(const DataBuffer& buffer,
|
||||||
cricket::SendDataResult* send_result);
|
cricket::SendDataResult* send_result);
|
||||||
bool QueueSendData(const DataBuffer& buffer);
|
bool QueueSendData(const DataBuffer& buffer);
|
||||||
|
bool SendOpenMessage(const talk_base::Buffer* buffer);
|
||||||
|
|
||||||
|
|
||||||
std::string label_;
|
std::string label_;
|
||||||
DataChannelInit config_;
|
DataChannelInit config_;
|
||||||
|
@ -26,56 +26,56 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "talk/app/webrtc/datachannel.h"
|
#include "talk/app/webrtc/datachannel.h"
|
||||||
|
#include "talk/app/webrtc/test/fakedatachannelprovider.h"
|
||||||
#include "talk/base/gunit.h"
|
#include "talk/base/gunit.h"
|
||||||
|
|
||||||
using webrtc::DataChannel;
|
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 {
|
class SctpDataChannelTest : public testing::Test {
|
||||||
protected:
|
protected:
|
||||||
SctpDataChannelTest()
|
SctpDataChannelTest()
|
||||||
: webrtc_data_channel_(
|
: webrtc_data_channel_(
|
||||||
DataChannel::Create(&client_, cricket::DCT_SCTP, "test", &init_)) {
|
DataChannel::Create(&provider_, cricket::DCT_SCTP, "test", &init_)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetChannelReady() {
|
void SetChannelReady() {
|
||||||
|
webrtc_data_channel_->OnTransportChannelCreated();
|
||||||
|
if (webrtc_data_channel_->id() < 0) {
|
||||||
|
webrtc_data_channel_->SetSctpSid(0);
|
||||||
|
}
|
||||||
webrtc_data_channel_->OnChannelReady(true);
|
webrtc_data_channel_->OnChannelReady(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
webrtc::DataChannelInit init_;
|
webrtc::DataChannelInit init_;
|
||||||
FakeDataChannelClient client_;
|
FakeDataChannelProvider provider_;
|
||||||
talk_base::scoped_refptr<DataChannel> webrtc_data_channel_;
|
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.
|
// Tests the state of the data channel.
|
||||||
TEST_F(SctpDataChannelTest, StateTransition) {
|
TEST_F(SctpDataChannelTest, StateTransition) {
|
||||||
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting,
|
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting,
|
||||||
@ -85,6 +85,8 @@ TEST_F(SctpDataChannelTest, StateTransition) {
|
|||||||
webrtc_data_channel_->Close();
|
webrtc_data_channel_->Close();
|
||||||
EXPECT_EQ(webrtc::DataChannelInterface::kClosed,
|
EXPECT_EQ(webrtc::DataChannelInterface::kClosed,
|
||||||
webrtc_data_channel_->state());
|
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
|
// 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());
|
EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount());
|
||||||
|
|
||||||
client_.set_send_blocked(true);
|
provider_.set_send_blocked(true);
|
||||||
|
|
||||||
const int number_of_packets = 3;
|
const int number_of_packets = 3;
|
||||||
for (int i = 0; i < number_of_packets; ++i) {
|
for (int i = 0; i < number_of_packets; ++i) {
|
||||||
@ -111,18 +113,22 @@ TEST_F(SctpDataChannelTest, BufferedAmountWhenBlocked) {
|
|||||||
TEST_F(SctpDataChannelTest, QueuedDataSentWhenUnblocked) {
|
TEST_F(SctpDataChannelTest, QueuedDataSentWhenUnblocked) {
|
||||||
SetChannelReady();
|
SetChannelReady();
|
||||||
webrtc::DataBuffer buffer("abcd");
|
webrtc::DataBuffer buffer("abcd");
|
||||||
client_.set_send_blocked(true);
|
provider_.set_send_blocked(true);
|
||||||
EXPECT_TRUE(webrtc_data_channel_->Send(buffer));
|
EXPECT_TRUE(webrtc_data_channel_->Send(buffer));
|
||||||
|
|
||||||
client_.set_send_blocked(false);
|
provider_.set_send_blocked(false);
|
||||||
SetChannelReady();
|
SetChannelReady();
|
||||||
EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount());
|
EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that the queued control message is sent when channel is ready.
|
// Tests that the queued control message is sent when channel is ready.
|
||||||
TEST_F(SctpDataChannelTest, QueuedControlMessageSent) {
|
TEST_F(SctpDataChannelTest, OpenMessageSent) {
|
||||||
talk_base::Buffer* buffer = new talk_base::Buffer("abcd", 4);
|
// Initially the id is unassigned.
|
||||||
EXPECT_TRUE(webrtc_data_channel_->SendControl(buffer));
|
EXPECT_EQ(-1, webrtc_data_channel_->id());
|
||||||
|
|
||||||
SetChannelReady();
|
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/videosource.h"
|
||||||
#include "talk/app/webrtc/videotrack.h"
|
#include "talk/app/webrtc/videotrack.h"
|
||||||
#include "talk/base/bytebuffer.h"
|
#include "talk/base/bytebuffer.h"
|
||||||
|
#include "talk/base/stringutils.h"
|
||||||
#include "talk/media/sctp/sctpdataengine.h"
|
#include "talk/media/sctp/sctpdataengine.h"
|
||||||
|
|
||||||
static const char kDefaultStreamLabel[] = "default";
|
static const char kDefaultStreamLabel[] = "default";
|
||||||
@ -189,7 +190,8 @@ MediaStreamSignaling::MediaStreamSignaling(
|
|||||||
remote_streams_(StreamCollection::Create()),
|
remote_streams_(StreamCollection::Create()),
|
||||||
remote_stream_factory_(new RemoteMediaStreamFactory(signaling_thread,
|
remote_stream_factory_(new RemoteMediaStreamFactory(signaling_thread,
|
||||||
channel_manager)),
|
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_video = false;
|
||||||
options_.has_audio = false;
|
options_.has_audio = false;
|
||||||
}
|
}
|
||||||
@ -203,36 +205,37 @@ void MediaStreamSignaling::TearDown() {
|
|||||||
OnDataChannelClose();
|
OnDataChannelClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaStreamSignaling::IsSctpIdAvailable(int id) const {
|
bool MediaStreamSignaling::IsSctpSidAvailable(int sid) const {
|
||||||
if (id < 0 || id > static_cast<int>(cricket::kMaxSctpSid))
|
if (sid < 0 || sid > static_cast<int>(cricket::kMaxSctpSid))
|
||||||
return false;
|
return false;
|
||||||
for (DataChannels::const_iterator iter = data_channels_.begin();
|
for (DataChannels::const_iterator iter = data_channels_.begin();
|
||||||
iter != data_channels_.end();
|
iter != data_channels_.end();
|
||||||
++iter) {
|
++iter) {
|
||||||
if (iter->second->id() == id) {
|
if (iter->second->id() == sid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the first id that has not been taken by existing data
|
// Gets the first unused odd/even id based on the DTLS role. If |role| is
|
||||||
// channels. Starting from 1.
|
// SSL_CLIENT, the allocated id starts from 0 and takes even numbers; otherwise,
|
||||||
// Returns false if no id can be allocated.
|
// the id starts from 1 and takes odd numbers. Returns false if no id can be
|
||||||
// TODO(jiayl): Update to some kind of even/odd random number selection when the
|
// allocated.
|
||||||
// rules are fully standardized.
|
bool MediaStreamSignaling::AllocateSctpSid(talk_base::SSLRole role, int* sid) {
|
||||||
bool MediaStreamSignaling::AllocateSctpId(int* id) {
|
int& last_id = (role == talk_base::SSL_CLIENT) ?
|
||||||
do {
|
last_allocated_sctp_even_sid_ : last_allocated_sctp_odd_sid_;
|
||||||
last_allocated_sctp_id_++;
|
|
||||||
} while (last_allocated_sctp_id_ <= static_cast<int>(cricket::kMaxSctpSid) &&
|
|
||||||
!IsSctpIdAvailable(last_allocated_sctp_id_));
|
|
||||||
|
|
||||||
if (last_allocated_sctp_id_ > static_cast<int>(cricket::kMaxSctpSid)) {
|
do {
|
||||||
last_allocated_sctp_id_ = cricket::kMaxSctpSid;
|
last_id += 2;
|
||||||
|
} while (last_id <= static_cast<int>(cricket::kMaxSctpSid) &&
|
||||||
|
!IsSctpSidAvailable(last_id));
|
||||||
|
|
||||||
|
if (last_id > static_cast<int>(cricket::kMaxSctpSid)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
*id = last_allocated_sctp_id_;
|
*sid = last_id;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,9 +395,8 @@ void MediaStreamSignaling::OnRemoteDescriptionChanged(
|
|||||||
const cricket::DataContentDescription* data_desc =
|
const cricket::DataContentDescription* data_desc =
|
||||||
static_cast<const cricket::DataContentDescription*>(
|
static_cast<const cricket::DataContentDescription*>(
|
||||||
data_content->description);
|
data_content->description);
|
||||||
if (data_desc->protocol() == cricket::kMediaProtocolDtlsSctp) {
|
if (talk_base::starts_with(
|
||||||
UpdateRemoteSctpDataChannels();
|
data_desc->protocol().data(), cricket::kMediaProtocolRtpPrefix)) {
|
||||||
} else {
|
|
||||||
UpdateRemoteRtpDataChannels(data_desc->streams());
|
UpdateRemoteRtpDataChannels(data_desc->streams());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -448,9 +450,8 @@ void MediaStreamSignaling::OnLocalDescriptionChanged(
|
|||||||
const cricket::DataContentDescription* data_desc =
|
const cricket::DataContentDescription* data_desc =
|
||||||
static_cast<const cricket::DataContentDescription*>(
|
static_cast<const cricket::DataContentDescription*>(
|
||||||
data_content->description);
|
data_content->description);
|
||||||
if (data_desc->protocol() == cricket::kMediaProtocolDtlsSctp) {
|
if (talk_base::starts_with(
|
||||||
UpdateLocalSctpDataChannels();
|
data_desc->protocol().data(), cricket::kMediaProtocolRtpPrefix)) {
|
||||||
} else {
|
|
||||||
UpdateLocalRtpDataChannels(data_desc->streams());
|
UpdateLocalRtpDataChannels(data_desc->streams());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -919,20 +920,26 @@ void MediaStreamSignaling::CreateRemoteDataChannel(const std::string& label,
|
|||||||
stream_observer_->OnAddDataChannel(channel);
|
stream_observer_->OnAddDataChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MediaStreamSignaling::OnDataTransportCreatedForSctp() {
|
||||||
void MediaStreamSignaling::UpdateLocalSctpDataChannels() {
|
|
||||||
DataChannels::iterator it = data_channels_.begin();
|
DataChannels::iterator it = data_channels_.begin();
|
||||||
for (; it != data_channels_.end(); ++it) {
|
for (; it != data_channels_.end(); ++it) {
|
||||||
DataChannel* data_channel = it->second;
|
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();
|
DataChannels::iterator it = data_channels_.begin();
|
||||||
for (; it != data_channels_.end(); ++it) {
|
for (; it != data_channels_.end(); ++it) {
|
||||||
DataChannel* data_channel = it->second;
|
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.
|
// 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
|
// Gets the first available SCTP id that is not assigned to any existing
|
||||||
// data channels.
|
// 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
|
// Adds |local_stream| to the collection of known MediaStreams that will be
|
||||||
// offered in a SessionDescription.
|
// offered in a SessionDescription.
|
||||||
@ -249,8 +249,8 @@ class MediaStreamSignaling {
|
|||||||
StreamCollectionInterface* remote_streams() const {
|
StreamCollectionInterface* remote_streams() const {
|
||||||
return remote_streams_.get();
|
return remote_streams_.get();
|
||||||
}
|
}
|
||||||
void UpdateLocalSctpDataChannels();
|
void OnDataTransportCreatedForSctp();
|
||||||
void UpdateRemoteSctpDataChannels();
|
void OnDtlsRoleReadyForSctp(talk_base::SSLRole role);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct RemotePeerInfo {
|
struct RemotePeerInfo {
|
||||||
@ -380,7 +380,9 @@ class MediaStreamSignaling {
|
|||||||
TrackInfos local_audio_tracks_;
|
TrackInfos local_audio_tracks_;
|
||||||
TrackInfos local_video_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> >
|
typedef std::map<std::string, talk_base::scoped_refptr<DataChannel> >
|
||||||
DataChannels;
|
DataChannels;
|
||||||
DataChannels data_channels_;
|
DataChannels data_channels_;
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "talk/app/webrtc/mediastreamsignaling.h"
|
#include "talk/app/webrtc/mediastreamsignaling.h"
|
||||||
#include "talk/app/webrtc/streamcollection.h"
|
#include "talk/app/webrtc/streamcollection.h"
|
||||||
#include "talk/app/webrtc/test/fakeconstraints.h"
|
#include "talk/app/webrtc/test/fakeconstraints.h"
|
||||||
|
#include "talk/app/webrtc/test/fakedatachannelprovider.h"
|
||||||
#include "talk/app/webrtc/videotrack.h"
|
#include "talk/app/webrtc/videotrack.h"
|
||||||
#include "talk/base/gunit.h"
|
#include "talk/base/gunit.h"
|
||||||
#include "talk/base/scoped_ptr.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"
|
"o=- 0 0 IN IP4 127.0.0.1\r\n"
|
||||||
"s=-\r\n"
|
"s=-\r\n"
|
||||||
"t=0 0\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"
|
"m=audio 1 RTP/AVPF 103\r\n"
|
||||||
"a=mid:audio\r\n"
|
"a=mid:audio\r\n"
|
||||||
"a=rtpmap:103 ISAC/16000\r\n"
|
"a=rtpmap:103 ISAC/16000\r\n"
|
||||||
@ -1012,4 +1013,43 @@ TEST_F(MediaStreamSignalingTest, ChangeSsrcOnTrackInLocalSessionDescription) {
|
|||||||
observer_->VerifyLocalVideoTrack(kStreams[0], kVideoTracks[0], 98);
|
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())
|
if (!channel.get())
|
||||||
return NULL;
|
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();
|
observer_->OnRenegotiationNeeded();
|
||||||
|
|
||||||
return DataChannelProxy::Create(signaling_thread(), channel.get());
|
return DataChannelProxy::Create(signaling_thread(), channel.get());
|
||||||
|
@ -981,31 +981,6 @@ TEST_F(PeerConnectionInterfaceTest,
|
|||||||
EXPECT_TRUE(channel == NULL);
|
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
|
// The test verifies that creating a SCTP data channel with an id already in use
|
||||||
// or out of range should fail.
|
// or out of range should fail.
|
||||||
TEST_F(PeerConnectionInterfaceTest,
|
TEST_F(PeerConnectionInterfaceTest,
|
||||||
@ -1015,13 +990,13 @@ TEST_F(PeerConnectionInterfaceTest,
|
|||||||
CreatePeerConnection(&constraints);
|
CreatePeerConnection(&constraints);
|
||||||
|
|
||||||
webrtc::DataChannelInit config;
|
webrtc::DataChannelInit config;
|
||||||
|
scoped_refptr<DataChannelInterface> channel;
|
||||||
|
|
||||||
scoped_refptr<DataChannelInterface> channel =
|
config.id = 1;
|
||||||
pc_->CreateDataChannel("1", &config);
|
channel = pc_->CreateDataChannel("1", &config);
|
||||||
EXPECT_TRUE(channel != NULL);
|
EXPECT_TRUE(channel != NULL);
|
||||||
EXPECT_EQ(1, channel->id());
|
EXPECT_EQ(1, channel->id());
|
||||||
|
|
||||||
config.id = 1;
|
|
||||||
channel = pc_->CreateDataChannel("x", &config);
|
channel = pc_->CreateDataChannel("x", &config);
|
||||||
EXPECT_TRUE(channel == NULL);
|
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
|
// 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
|
// FireFox, use it as a remote session description, generate an answer and use
|
||||||
// the answer as a local description.
|
// 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) {
|
TEST_F(PeerConnectionInterfaceTest, DISABLED_ReceiveFireFoxOffer) {
|
||||||
MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
|
MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
|
||||||
FakeConstraints constraints;
|
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);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1484,6 +1484,19 @@ TEST_F(WebRtcSdpTest, DeserializeSessionDescription) {
|
|||||||
EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
|
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) {
|
TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCarriageReturn) {
|
||||||
JsepSessionDescription jdesc(kDummyString);
|
JsepSessionDescription jdesc(kDummyString);
|
||||||
std::string sdp_without_carriage_return = kSdpFullString;
|
std::string sdp_without_carriage_return = kSdpFullString;
|
||||||
@ -1886,6 +1899,7 @@ TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) {
|
|||||||
ReplaceAndTryToParse("t=", kSdpDestroyer);
|
ReplaceAndTryToParse("t=", kSdpDestroyer);
|
||||||
|
|
||||||
// Broken media description
|
// Broken media description
|
||||||
|
ReplaceAndTryToParse("m=audio", "c=IN IP4 74.125.224.39");
|
||||||
ReplaceAndTryToParse("m=video", kSdpDestroyer);
|
ReplaceAndTryToParse("m=video", kSdpDestroyer);
|
||||||
|
|
||||||
// Invalid lines
|
// Invalid lines
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
#include "talk/base/stringencode.h"
|
#include "talk/base/stringencode.h"
|
||||||
#include "talk/media/base/constants.h"
|
#include "talk/media/base/constants.h"
|
||||||
#include "talk/media/base/videocapturer.h"
|
#include "talk/media/base/videocapturer.h"
|
||||||
#include "talk/media/sctp/sctputils.h"
|
|
||||||
#include "talk/session/media/channel.h"
|
#include "talk/session/media/channel.h"
|
||||||
#include "talk/session/media/channelmanager.h"
|
#include "talk/session/media/channelmanager.h"
|
||||||
#include "talk/session/media/mediasession.h"
|
#include "talk/session/media/mediasession.h"
|
||||||
@ -629,6 +628,10 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
|||||||
// local session description.
|
// local session description.
|
||||||
mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get());
|
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) {
|
if (error() != cricket::BaseSession::ERROR_NONE) {
|
||||||
return BadLocalSdp(SessionErrorMsg(error()), err_desc);
|
return BadLocalSdp(SessionErrorMsg(error()), err_desc);
|
||||||
}
|
}
|
||||||
@ -681,6 +684,12 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
|||||||
ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
|
ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
|
||||||
desc);
|
desc);
|
||||||
remote_desc_.reset(desc_temp.release());
|
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) {
|
if (error() != cricket::BaseSession::ERROR_NONE) {
|
||||||
return BadRemoteSdp(SessionErrorMsg(error()), err_desc);
|
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.";
|
LOG(LS_ERROR) << "ConnectDataChannel called when data_channel_ is NULL.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
|
data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
|
||||||
&DataChannel::OnChannelReady);
|
&DataChannel::OnChannelReady);
|
||||||
data_channel_->SignalDataReceived.connect(webrtc_data_channel,
|
data_channel_->SignalDataReceived.connect(webrtc_data_channel,
|
||||||
&DataChannel::OnDataReceived);
|
&DataChannel::OnDataReceived);
|
||||||
cricket::StreamParams params =
|
|
||||||
cricket::StreamParams::CreateLegacy(webrtc_data_channel->id());
|
|
||||||
data_channel_->AddRecvStream(params);
|
|
||||||
data_channel_->AddSendStream(params);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
|
void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
|
||||||
data_channel_->RemoveSendStream(webrtc_data_channel->id());
|
if (!data_channel_.get()) {
|
||||||
data_channel_->RemoveRecvStream(webrtc_data_channel->id());
|
LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
|
data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
|
||||||
data_channel_->SignalDataReceived.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(
|
talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
|
||||||
const std::string& label,
|
const std::string& label,
|
||||||
const DataChannelInit* config) {
|
const DataChannelInit* config) {
|
||||||
@ -994,11 +1027,13 @@ talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
|
|||||||
|
|
||||||
if (data_channel_type_ == cricket::DCT_SCTP) {
|
if (data_channel_type_ == cricket::DCT_SCTP) {
|
||||||
if (new_config.id < 0) {
|
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.";
|
LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
|
||||||
return NULL;
|
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 "
|
LOG(LS_ERROR) << "Failed to create a SCTP data channel "
|
||||||
<< "because the id is already in use or out of range.";
|
<< "because the id is already in use or out of range.";
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1007,30 +1042,9 @@ talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
|
|||||||
|
|
||||||
talk_base::scoped_refptr<DataChannel> channel(
|
talk_base::scoped_refptr<DataChannel> channel(
|
||||||
DataChannel::Create(this, data_channel_type_, label, &new_config));
|
DataChannel::Create(this, data_channel_type_, label, &new_config));
|
||||||
if (channel == NULL)
|
if (channel && !mediastream_signaling_->AddDataChannel(channel))
|
||||||
return NULL;
|
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;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1353,14 +1367,17 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WebRtcSession::CreateDataChannel(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(
|
data_channel_.reset(channel_manager_->CreateDataChannel(
|
||||||
this, content->name, rtcp, data_channel_type_));
|
this, content->name, !sctp, data_channel_type_));
|
||||||
if (!data_channel_.get()) {
|
if (!data_channel_.get()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
data_channel_->SignalNewStreamReceived.connect(
|
if (sctp) {
|
||||||
this, &WebRtcSession::OnNewDataChannelReceived);
|
mediastream_signaling_->OnDataTransportCreatedForSctp();
|
||||||
|
data_channel_->SignalNewStreamReceived.connect(
|
||||||
|
this, &WebRtcSession::OnNewDataChannelReceived);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,10 @@ class WebRtcSession : public cricket::BaseSession,
|
|||||||
cricket::SendDataResult* result) OVERRIDE;
|
cricket::SendDataResult* result) OVERRIDE;
|
||||||
virtual bool ConnectDataChannel(DataChannel* webrtc_data_channel) OVERRIDE;
|
virtual bool ConnectDataChannel(DataChannel* webrtc_data_channel) OVERRIDE;
|
||||||
virtual void DisconnectDataChannel(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(
|
talk_base::scoped_refptr<DataChannel> CreateDataChannel(
|
||||||
const std::string& label,
|
const std::string& label,
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
#include "talk/base/common.h"
|
#include "talk/base/common.h"
|
||||||
|
|
||||||
using std::string;
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace talk_base {
|
namespace talk_base {
|
||||||
@ -96,7 +95,8 @@ bool Base64::IsBase64Encoded(const std::string& str) {
|
|||||||
return true;
|
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);
|
ASSERT(NULL != result);
|
||||||
result->clear();
|
result->clear();
|
||||||
result->resize(((len + 2) / 3) * 4);
|
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,
|
bool Base64::DecodeFromArray(const char* data, size_t len, DecodeFlags flags,
|
||||||
string* result, size_t* data_used) {
|
std::string* result, size_t* data_used) {
|
||||||
return DecodeFromArrayTemplate<string>(data, len, flags, result, data_used);
|
return DecodeFromArrayTemplate<std::string>(
|
||||||
|
data, len, flags, result, data_used);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Base64::DecodeFromArray(const char* data, size_t len, DecodeFlags flags,
|
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.
|
// The _F version prefixes the message with the current function name.
|
||||||
#if (defined(__GNUC__) && defined(_DEBUG)) || defined(WANT_PRETTY_LOG_F)
|
#if (defined(__GNUC__) && defined(_DEBUG)) || defined(WANT_PRETTY_LOG_F)
|
||||||
#define LOG_F(sev) LOG(sev) << __PRETTY_FUNCTION__ << ": "
|
#define LOG_F(sev) LOG(sev) << __PRETTY_FUNCTION__ << ": "
|
||||||
|
#define LOG_T_F(sev) LOG(sev) << this << ": " << __PRETTY_FUNCTION__ << ": "
|
||||||
#else
|
#else
|
||||||
#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": "
|
#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": "
|
||||||
|
#define LOG_T_F(sev) LOG(sev) << this << ": " << __FUNCTION__ << ": "
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LOG_CHECK_LEVEL(sev) \
|
#define LOG_CHECK_LEVEL(sev) \
|
||||||
@ -331,7 +333,6 @@ inline bool LogCheckLevel(LoggingSeverity sev) {
|
|||||||
.stream()
|
.stream()
|
||||||
|
|
||||||
#define LOG_T(sev) LOG(sev) << this << ": "
|
#define LOG_T(sev) LOG(sev) << this << ": "
|
||||||
#define LOG_T_F(level) LOG_F(level) << this << ": "
|
|
||||||
|
|
||||||
#else // !LOGGING
|
#else // !LOGGING
|
||||||
|
|
||||||
@ -354,7 +355,7 @@ inline bool LogCheckLevel(LoggingSeverity sev) {
|
|||||||
.stream()
|
.stream()
|
||||||
|
|
||||||
#define LOG_T(sev) LOG(sev) << this << ": "
|
#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
|
#endif // !LOGGING
|
||||||
|
|
||||||
#define LOG_ERRNO_EX(sev, err) \
|
#define LOG_ERRNO_EX(sev, err) \
|
||||||
|
@ -466,6 +466,10 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> {
|
|||||||
ASSERT((0 <= value) && (value <= 65536));
|
ASSERT((0 <= value) && (value <= 65536));
|
||||||
*mtu = value;
|
*mtu = value;
|
||||||
return 0;
|
return 0;
|
||||||
|
#elif defined(__native_client__)
|
||||||
|
// Most socket operations, including this, will fail in NaCl's sandbox.
|
||||||
|
error_ = EACCES;
|
||||||
|
return -1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,8 +71,7 @@ void ProfilerEvent::Start() {
|
|||||||
++start_count_;
|
++start_count_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProfilerEvent::Stop() {
|
void ProfilerEvent::Stop(uint64 stop_time) {
|
||||||
uint64 stop_time = TimeNanos();
|
|
||||||
--start_count_;
|
--start_count_;
|
||||||
ASSERT(start_count_ >= 0);
|
ASSERT(start_count_ >= 0);
|
||||||
if (start_count_ == 0) {
|
if (start_count_ == 0) {
|
||||||
@ -94,6 +93,10 @@ void ProfilerEvent::Stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProfilerEvent::Stop() {
|
||||||
|
Stop(TimeNanos());
|
||||||
|
}
|
||||||
|
|
||||||
double ProfilerEvent::standard_deviation() const {
|
double ProfilerEvent::standard_deviation() const {
|
||||||
if (event_count_ <= 1) return 0.0;
|
if (event_count_ <= 1) return 0.0;
|
||||||
return sqrt(sum_of_squared_differences_ / (event_count_ - 1.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) {
|
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) {
|
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,
|
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)) {
|
if (!LogMessage::Loggable(severity_to_use)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharedScope scope(&lock_);
|
||||||
|
|
||||||
{ // Output first line.
|
{ // Output first line.
|
||||||
LogMessage msg(file, line, severity_to_use);
|
LogMessage msg(file, line, severity_to_use);
|
||||||
msg.stream() << "=== Profile report ";
|
msg.stream() << "=== Profile report ";
|
||||||
@ -126,8 +150,8 @@ void Profiler::ReportToLog(const char* file, int line,
|
|||||||
}
|
}
|
||||||
msg.stream() << "===";
|
msg.stream() << "===";
|
||||||
}
|
}
|
||||||
typedef std::map<std::string, ProfilerEvent>::const_iterator iterator;
|
for (EventMap::const_iterator it = events_.begin();
|
||||||
for (iterator it = events_.begin(); it != events_.end(); ++it) {
|
it != events_.end(); ++it) {
|
||||||
if (event_prefix.empty() || it->first.find(event_prefix) == 0) {
|
if (event_prefix.empty() || it->first.find(event_prefix) == 0) {
|
||||||
LogMessage(file, line, severity_to_use).stream()
|
LogMessage(file, line, severity_to_use).stream()
|
||||||
<< it->first << " " << it->second;
|
<< 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 {
|
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);
|
events_.find(event_name);
|
||||||
return (it == events_.end()) ? NULL : &it->second;
|
return (it == events_.end()) ? NULL : &it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Profiler::Clear() {
|
bool Profiler::Clear() {
|
||||||
|
ExclusiveScope scope(&lock_);
|
||||||
bool result = true;
|
bool result = true;
|
||||||
// Clear all events that aren't started.
|
// 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()) {
|
while (it != events_.end()) {
|
||||||
if (it->second.is_started()) {
|
if (it->second.is_started()) {
|
||||||
++it; // Can't clear started events.
|
++it; // Can't clear started events.
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
// }
|
// }
|
||||||
// Another example:
|
// Another example:
|
||||||
// void StartAsyncProcess() {
|
// void StartAsyncProcess() {
|
||||||
// PROFILE_START("My event");
|
// PROFILE_START("My async event");
|
||||||
// DoSomethingAsyncAndThenCall(&Callback);
|
// DoSomethingAsyncAndThenCall(&Callback);
|
||||||
// }
|
// }
|
||||||
// void Callback() {
|
// void Callback() {
|
||||||
@ -54,6 +54,7 @@
|
|||||||
#include "talk/base/basictypes.h"
|
#include "talk/base/basictypes.h"
|
||||||
#include "talk/base/common.h"
|
#include "talk/base/common.h"
|
||||||
#include "talk/base/logging.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.
|
// Profiling could be switched via a build flag, but for now, it's always on.
|
||||||
#define ENABLE_PROFILING
|
#define ENABLE_PROFILING
|
||||||
@ -105,6 +106,7 @@ class ProfilerEvent {
|
|||||||
ProfilerEvent();
|
ProfilerEvent();
|
||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
|
void Stop(uint64 stop_time);
|
||||||
double standard_deviation() const;
|
double standard_deviation() const;
|
||||||
double total_time() const { return total_time_; }
|
double total_time() const { return total_time_; }
|
||||||
double mean() const { return mean_; }
|
double mean() const { return mean_; }
|
||||||
@ -142,7 +144,9 @@ class Profiler {
|
|||||||
private:
|
private:
|
||||||
Profiler() {}
|
Profiler() {}
|
||||||
|
|
||||||
std::map<std::string, ProfilerEvent> events_;
|
typedef std::map<std::string, ProfilerEvent> EventMap;
|
||||||
|
EventMap events_;
|
||||||
|
mutable SharedExclusiveLock lock_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Profiler);
|
DISALLOW_COPY_AND_ASSIGN(Profiler);
|
||||||
};
|
};
|
||||||
|
@ -391,6 +391,7 @@
|
|||||||
'app/webrtc/test/fakeaudiocapturemodule.h',
|
'app/webrtc/test/fakeaudiocapturemodule.h',
|
||||||
'app/webrtc/test/fakeaudiocapturemodule_unittest.cc',
|
'app/webrtc/test/fakeaudiocapturemodule_unittest.cc',
|
||||||
'app/webrtc/test/fakeconstraints.h',
|
'app/webrtc/test/fakeconstraints.h',
|
||||||
|
'app/webrtc/test/fakedatachannelprovider.h',
|
||||||
'app/webrtc/test/fakedtlsidentityservice.h',
|
'app/webrtc/test/fakedtlsidentityservice.h',
|
||||||
'app/webrtc/test/fakemediastreamsignaling.h',
|
'app/webrtc/test/fakemediastreamsignaling.h',
|
||||||
'app/webrtc/test/fakeperiodicvideocapturer.h',
|
'app/webrtc/test/fakeperiodicvideocapturer.h',
|
||||||
|
@ -306,6 +306,7 @@ struct VideoOptions {
|
|||||||
system_high_adaptation_threshhold.SetFrom(
|
system_high_adaptation_threshhold.SetFrom(
|
||||||
change.system_high_adaptation_threshhold);
|
change.system_high_adaptation_threshhold);
|
||||||
buffered_mode_latency.SetFrom(change.buffered_mode_latency);
|
buffered_mode_latency.SetFrom(change.buffered_mode_latency);
|
||||||
|
lower_min_bitrate.SetFrom(change.lower_min_bitrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const VideoOptions& o) const {
|
bool operator==(const VideoOptions& o) const {
|
||||||
@ -329,7 +330,8 @@ struct VideoOptions {
|
|||||||
o.system_low_adaptation_threshhold &&
|
o.system_low_adaptation_threshhold &&
|
||||||
system_high_adaptation_threshhold ==
|
system_high_adaptation_threshhold ==
|
||||||
o.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 {
|
std::string ToString() const {
|
||||||
@ -356,6 +358,7 @@ struct VideoOptions {
|
|||||||
ost << ToStringIfSet("low", system_low_adaptation_threshhold);
|
ost << ToStringIfSet("low", system_low_adaptation_threshhold);
|
||||||
ost << ToStringIfSet("high", system_high_adaptation_threshhold);
|
ost << ToStringIfSet("high", system_high_adaptation_threshhold);
|
||||||
ost << ToStringIfSet("buffered mode latency", buffered_mode_latency);
|
ost << ToStringIfSet("buffered mode latency", buffered_mode_latency);
|
||||||
|
ost << ToStringIfSet("lower min bitrate", lower_min_bitrate);
|
||||||
ost << "}";
|
ost << "}";
|
||||||
return ost.str();
|
return ost.str();
|
||||||
}
|
}
|
||||||
@ -400,6 +403,8 @@ struct VideoOptions {
|
|||||||
SettablePercent system_high_adaptation_threshhold;
|
SettablePercent system_high_adaptation_threshhold;
|
||||||
// Specify buffered mode latency in milliseconds.
|
// Specify buffered mode latency in milliseconds.
|
||||||
Settable<int> buffered_mode_latency;
|
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.
|
// A class for playing out soundclips.
|
||||||
|
@ -33,6 +33,7 @@ namespace cricket {
|
|||||||
|
|
||||||
const char kFecSsrcGroupSemantics[] = "FEC";
|
const char kFecSsrcGroupSemantics[] = "FEC";
|
||||||
const char kFidSsrcGroupSemantics[] = "FID";
|
const char kFidSsrcGroupSemantics[] = "FID";
|
||||||
|
const char kSimSsrcGroupSemantics[] = "SIM";
|
||||||
|
|
||||||
static std::string SsrcsToString(const std::vector<uint32>& ssrcs) {
|
static std::string SsrcsToString(const std::vector<uint32>& ssrcs) {
|
||||||
std::ostringstream ost;
|
std::ostringstream ost;
|
||||||
|
@ -31,6 +31,14 @@
|
|||||||
// described by one StreamParams object
|
// described by one StreamParams object
|
||||||
// SsrcGroup is used to describe the relationship between the SSRCs that
|
// SsrcGroup is used to describe the relationship between the SSRCs that
|
||||||
// are used for this media source.
|
// 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_
|
#ifndef TALK_MEDIA_BASE_STREAMPARAMS_H_
|
||||||
#define TALK_MEDIA_BASE_STREAMPARAMS_H_
|
#define TALK_MEDIA_BASE_STREAMPARAMS_H_
|
||||||
@ -46,6 +54,7 @@ namespace cricket {
|
|||||||
|
|
||||||
extern const char kFecSsrcGroupSemantics[];
|
extern const char kFecSsrcGroupSemantics[];
|
||||||
extern const char kFidSsrcGroupSemantics[];
|
extern const char kFidSsrcGroupSemantics[];
|
||||||
|
extern const char kSimSsrcGroupSemantics[];
|
||||||
|
|
||||||
struct SsrcGroup {
|
struct SsrcGroup {
|
||||||
SsrcGroup(const std::string& usage, const std::vector<uint32>& ssrcs)
|
SsrcGroup(const std::string& usage, const std::vector<uint32>& ssrcs)
|
||||||
|
@ -278,18 +278,14 @@ VideoCapturer* DeviceManager::CreateDesktopCapturer(
|
|||||||
bool DeviceManager::GetAudioDevices(bool input,
|
bool DeviceManager::GetAudioDevices(bool input,
|
||||||
std::vector<Device>* devs) {
|
std::vector<Device>* devs) {
|
||||||
devs->clear();
|
devs->clear();
|
||||||
#if defined(IOS) || defined(ANDROID)
|
#if defined(ANDROID)
|
||||||
// Under Android, we don't access the device file directly.
|
// Under Android, 0 is always required for the playout device and 0 is the
|
||||||
// Arbitrary use 0 for the mic and 1 for the output.
|
// default for the recording device.
|
||||||
// These ids are used in MediaEngine::SetSoundDevices(in, out);
|
devs->push_back(Device("default-device", 0));
|
||||||
// The strings are for human consumption.
|
|
||||||
if (input) {
|
|
||||||
devs->push_back(Device("audiorecord", 0));
|
|
||||||
} else {
|
|
||||||
devs->push_back(Device("audiotrack", 1));
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
|
// Other platforms either have their own derived class implementation
|
||||||
|
// (desktop) or don't use device manager for audio devices (iOS).
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -1046,14 +1046,12 @@ class FakeWebRtcVideoEngine
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
WEBRTC_STUB(EnableColorEnhancement, (const int, const bool));
|
WEBRTC_STUB(EnableColorEnhancement, (const int, const bool));
|
||||||
#ifdef USE_WEBRTC_DEV_BRANCH
|
|
||||||
WEBRTC_VOID_STUB(RegisterPreEncodeCallback,
|
WEBRTC_VOID_STUB(RegisterPreEncodeCallback,
|
||||||
(int, webrtc::I420FrameCallback*));
|
(int, webrtc::I420FrameCallback*));
|
||||||
WEBRTC_VOID_STUB(DeRegisterPreEncodeCallback, (int));
|
WEBRTC_VOID_STUB(DeRegisterPreEncodeCallback, (int));
|
||||||
WEBRTC_VOID_STUB(RegisterPreRenderCallback,
|
WEBRTC_VOID_STUB(RegisterPreRenderCallback,
|
||||||
(int, webrtc::I420FrameCallback*));
|
(int, webrtc::I420FrameCallback*));
|
||||||
WEBRTC_VOID_STUB(DeRegisterPreRenderCallback, (int));
|
WEBRTC_VOID_STUB(DeRegisterPreRenderCallback, (int));
|
||||||
#endif
|
|
||||||
// webrtc::ViEExternalCodec
|
// webrtc::ViEExternalCodec
|
||||||
WEBRTC_FUNC(RegisterExternalSendCodec,
|
WEBRTC_FUNC(RegisterExternalSendCodec,
|
||||||
(const int channel, const unsigned char pl_type, webrtc::VideoEncoder*,
|
(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 kMaxVideoBitrate = 2000;
|
||||||
static const int kDefaultConferenceModeMaxVideoBitrate = 500;
|
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 kVideoMtu = 1200;
|
||||||
|
|
||||||
static const int kVideoRtpBufferSize = 65536;
|
static const int kVideoRtpBufferSize = 65536;
|
||||||
@ -2568,7 +2571,7 @@ bool WebRtcVideoMediaChannel::SetSendBandwidth(bool autobw, int bps) {
|
|||||||
int max_bitrate;
|
int max_bitrate;
|
||||||
if (autobw) {
|
if (autobw) {
|
||||||
// Use the default values for min bitrate.
|
// 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
|
// Use the default value or the bps for the max
|
||||||
max_bitrate = (bps <= 0) ? send_max_bitrate_ : (bps / 1000);
|
max_bitrate = (bps <= 0) ? send_max_bitrate_ : (bps / 1000);
|
||||||
// Maximum start bitrate can be kStartVideoBitrate.
|
// Maximum start bitrate can be kStartVideoBitrate.
|
||||||
@ -2631,6 +2634,17 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) {
|
|||||||
// Adjust send codec bitrate if needed.
|
// Adjust send codec bitrate if needed.
|
||||||
int conf_max_bitrate = kDefaultConferenceModeMaxVideoBitrate;
|
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_;
|
int expected_bitrate = send_max_bitrate_;
|
||||||
if (InConferenceMode()) {
|
if (InConferenceMode()) {
|
||||||
expected_bitrate = conf_max_bitrate;
|
expected_bitrate = conf_max_bitrate;
|
||||||
@ -2642,7 +2656,8 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (send_codec_ &&
|
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
|
// On success, SetSendCodec() will reset send_max_bitrate_ to
|
||||||
// expected_bitrate.
|
// expected_bitrate.
|
||||||
if (!SetSendCodec(*send_codec_,
|
if (!SetSendCodec(*send_codec_,
|
||||||
|
@ -380,6 +380,25 @@ TEST_F(WebRtcVideoEngineTestFake, SetOptionsWithMaxBitrate) {
|
|||||||
channel_num, kVP8Codec.width, kVP8Codec.height, 0, 20, 10, 20);
|
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) {
|
TEST_F(WebRtcVideoEngineTestFake, MaxBitrateResetWithConferenceMode) {
|
||||||
EXPECT_TRUE(SetupEngine());
|
EXPECT_TRUE(SetupEngine());
|
||||||
int channel_num = vie_.GetLastChannel();
|
int channel_num = vie_.GetLastChannel();
|
||||||
|
@ -885,7 +885,7 @@ struct ResumeEntry {
|
|||||||
// soundclip device. At that time, reinstate the soundclip pause/resume code.
|
// soundclip device. At that time, reinstate the soundclip pause/resume code.
|
||||||
bool WebRtcVoiceEngine::SetDevices(const Device* in_device,
|
bool WebRtcVoiceEngine::SetDevices(const Device* in_device,
|
||||||
const Device* out_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) :
|
int in_id = in_device ? talk_base::FromString<int>(in_device->id) :
|
||||||
kDefaultAudioDeviceId;
|
kDefaultAudioDeviceId;
|
||||||
int out_id = out_device ? talk_base::FromString<int>(out_device->id) :
|
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;
|
return ret;
|
||||||
#else
|
#else
|
||||||
return true;
|
return true;
|
||||||
#endif // !IOS && !ANDROID
|
#endif // !IOS
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebRtcVoiceEngine::FindWebRtcAudioDeviceId(
|
bool WebRtcVoiceEngine::FindWebRtcAudioDeviceId(
|
||||||
bool is_input, const std::string& dev_name, int dev_id, int* rtc_id) {
|
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.
|
// In Linux, VoiceEngine uses the same device dev_id as the device manager.
|
||||||
#ifdef LINUX
|
#if defined(LINUX) || defined(ANDROID)
|
||||||
*rtc_id = dev_id;
|
*rtc_id = dev_id;
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
|
@ -242,13 +242,12 @@ static bool GenerateCname(const StreamParamsVec& params_vec,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate random SSRC values that are not already present in |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
|
// The generated values are added to |ssrcs|.
|
||||||
// true or false. 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,
|
static void GenerateSsrcs(const StreamParamsVec& params_vec,
|
||||||
bool include_rtx_stream,
|
int num_ssrcs,
|
||||||
std::vector<uint32>* ssrcs) {
|
std::vector<uint32>* ssrcs) {
|
||||||
unsigned int num_ssrcs = include_rtx_stream ? 2 : 1;
|
for (int i = 0; i < num_ssrcs; i++) {
|
||||||
for (unsigned int i = 0; i < num_ssrcs; i++) {
|
|
||||||
uint32 candidate;
|
uint32 candidate;
|
||||||
do {
|
do {
|
||||||
candidate = talk_base::CreateRandomNonZeroId();
|
candidate = talk_base::CreateRandomNonZeroId();
|
||||||
@ -428,7 +427,8 @@ static bool AddStreamParams(
|
|||||||
if (IsSctp(content_description)) {
|
if (IsSctp(content_description)) {
|
||||||
GenerateSctpSids(*current_streams, &ssrcs);
|
GenerateSctpSids(*current_streams, &ssrcs);
|
||||||
} else {
|
} 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) {
|
if (include_rtx_stream) {
|
||||||
content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
|
content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
|
||||||
@ -462,13 +462,23 @@ static bool AddStreamParams(
|
|||||||
if (IsSctp(content_description)) {
|
if (IsSctp(content_description)) {
|
||||||
GenerateSctpSids(*current_streams, &ssrcs);
|
GenerateSctpSids(*current_streams, &ssrcs);
|
||||||
} else {
|
} else {
|
||||||
GenerateSsrcs(*current_streams, include_rtx_stream, &ssrcs);
|
GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
|
||||||
}
|
}
|
||||||
StreamParams stream_param;
|
StreamParams stream_param;
|
||||||
stream_param.id = stream_it->id;
|
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) {
|
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);
|
content_description->set_multistream(true);
|
||||||
}
|
}
|
||||||
stream_param.cname = cname;
|
stream_param.cname = cname;
|
||||||
@ -1017,7 +1027,22 @@ static bool IsDtlsActive(
|
|||||||
void MediaSessionOptions::AddStream(MediaType type,
|
void MediaSessionOptions::AddStream(MediaType type,
|
||||||
const std::string& id,
|
const std::string& id,
|
||||||
const std::string& sync_label) {
|
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)
|
if (type == MEDIA_TYPE_VIDEO)
|
||||||
has_video = true;
|
has_video = true;
|
||||||
|
@ -105,8 +105,18 @@ struct MediaSessionOptions {
|
|||||||
void AddStream(MediaType type,
|
void AddStream(MediaType type,
|
||||||
const std::string& id,
|
const std::string& id,
|
||||||
const std::string& sync_label);
|
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);
|
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_audio;
|
||||||
bool has_video;
|
bool has_video;
|
||||||
DataChannelType data_channel_type;
|
DataChannelType data_channel_type;
|
||||||
@ -122,12 +132,15 @@ struct MediaSessionOptions {
|
|||||||
struct Stream {
|
struct Stream {
|
||||||
Stream(MediaType type,
|
Stream(MediaType type,
|
||||||
const std::string& id,
|
const std::string& id,
|
||||||
const std::string& sync_label)
|
const std::string& sync_label,
|
||||||
: type(type), id(id), sync_label(sync_label) {
|
int num_sim_layers)
|
||||||
|
: type(type), id(id), sync_label(sync_label),
|
||||||
|
num_sim_layers(num_sim_layers) {
|
||||||
}
|
}
|
||||||
MediaType type;
|
MediaType type;
|
||||||
std::string id;
|
std::string id;
|
||||||
std::string sync_label;
|
std::string sync_label;
|
||||||
|
int num_sim_layers;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<Stream> Streams;
|
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);
|
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:
|
// Create an audio and video answer to a standard video offer with:
|
||||||
// - one video track
|
// - one video track
|
||||||
// - two audio tracks
|
// - two audio tracks
|
||||||
|
Loading…
x
Reference in New Issue
Block a user