Update libjingle to 50654631.
R=mallinath@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2000006 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4519 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
bf853f2732
commit
91053e7c5a
@ -159,6 +159,37 @@ bool DataChannel::Send(const DataBuffer& buffer) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DataChannel::QueueControl(const talk_base::Buffer* buffer) {
|
||||||
|
queued_control_data_.push(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DataChannel::SendControl(const talk_base::Buffer* buffer) {
|
||||||
|
if (state_ != kOpen) {
|
||||||
|
QueueControl(buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (session_->data_channel_type() == cricket::DCT_RTP) {
|
||||||
|
delete buffer;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cricket::SendDataParams send_params;
|
||||||
|
send_params.ssrc = config_.id;
|
||||||
|
send_params.ordered = true;
|
||||||
|
send_params.type = cricket::DMT_CONTROL;
|
||||||
|
|
||||||
|
cricket::SendDataResult send_result;
|
||||||
|
bool retval = session_->data_channel()->SendData(
|
||||||
|
send_params, *buffer, &send_result);
|
||||||
|
if (!retval && send_result == cricket::SDR_BLOCK) {
|
||||||
|
// Link is congested. Queue for later.
|
||||||
|
QueueControl(buffer);
|
||||||
|
} else {
|
||||||
|
delete buffer;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
void DataChannel::SetReceiveSsrc(uint32 receive_ssrc) {
|
void DataChannel::SetReceiveSsrc(uint32 receive_ssrc) {
|
||||||
if (receive_ssrc_set_) {
|
if (receive_ssrc_set_) {
|
||||||
ASSERT(session_->data_channel_type() == cricket::DCT_RTP ||
|
ASSERT(session_->data_channel_type() == cricket::DCT_RTP ||
|
||||||
@ -278,9 +309,9 @@ void DataChannel::SetState(DataState state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DataChannel::ConnectToDataSession() {
|
void DataChannel::ConnectToDataSession() {
|
||||||
ASSERT(session_->data_channel() != NULL);
|
|
||||||
if (!session_->data_channel()) {
|
if (!session_->data_channel()) {
|
||||||
LOG(LS_ERROR) << "The DataEngine does not exist.";
|
LOG(LS_ERROR) << "The DataEngine does not exist.";
|
||||||
|
ASSERT(session_->data_channel() != NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,9 +319,17 @@ void DataChannel::ConnectToDataSession() {
|
|||||||
data_session_->SignalReadyToSendData.connect(this,
|
data_session_->SignalReadyToSendData.connect(this,
|
||||||
&DataChannel::OnChannelReady);
|
&DataChannel::OnChannelReady);
|
||||||
data_session_->SignalDataReceived.connect(this, &DataChannel::OnDataReceived);
|
data_session_->SignalDataReceived.connect(this, &DataChannel::OnDataReceived);
|
||||||
|
cricket::StreamParams params =
|
||||||
|
cricket::StreamParams::CreateLegacy(id());
|
||||||
|
data_session_->media_channel()->AddSendStream(params);
|
||||||
|
data_session_->media_channel()->AddRecvStream(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataChannel::DisconnectFromDataSession() {
|
void DataChannel::DisconnectFromDataSession() {
|
||||||
|
if (data_session_->media_channel() != NULL) {
|
||||||
|
data_session_->media_channel()->RemoveSendStream(id());
|
||||||
|
data_session_->media_channel()->RemoveRecvStream(id());
|
||||||
|
}
|
||||||
data_session_->SignalReadyToSendData.disconnect(this);
|
data_session_->SignalReadyToSendData.disconnect(this);
|
||||||
data_session_->SignalDataReceived.disconnect(this);
|
data_session_->SignalDataReceived.disconnect(this);
|
||||||
data_session_ = NULL;
|
data_session_ = NULL;
|
||||||
@ -318,6 +357,7 @@ void DataChannel::ClearQueuedReceivedData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DataChannel::SendQueuedSendData() {
|
void DataChannel::SendQueuedSendData() {
|
||||||
|
DeliverQueuedControlData();
|
||||||
if (!was_ever_writable_) {
|
if (!was_ever_writable_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -335,6 +375,16 @@ void DataChannel::SendQueuedSendData() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DataChannel::DeliverQueuedControlData() {
|
||||||
|
if (was_ever_writable_) {
|
||||||
|
while (!queued_control_data_.empty()) {
|
||||||
|
const talk_base::Buffer *buf = queued_control_data_.front();
|
||||||
|
queued_control_data_.pop();
|
||||||
|
SendControl(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DataChannel::ClearQueuedSendData() {
|
void DataChannel::ClearQueuedSendData() {
|
||||||
while (!queued_send_data_.empty()) {
|
while (!queued_send_data_.empty()) {
|
||||||
DataBuffer* buffer = queued_send_data_.front();
|
DataBuffer* buffer = queued_send_data_.front();
|
||||||
|
@ -74,6 +74,9 @@ 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
|
// Set the SSRC this channel should use to receive data from the
|
||||||
// underlying data engine.
|
// underlying data engine.
|
||||||
@ -89,6 +92,10 @@ class DataChannel : public DataChannelInterface,
|
|||||||
// Called if the underlying data engine is closing.
|
// Called if the underlying data engine is closing.
|
||||||
void OnDataEngineClose();
|
void OnDataEngineClose();
|
||||||
|
|
||||||
|
// Called when the channel's ready to use. That can happen when the
|
||||||
|
// underlying DataMediaChannel becomes ready, or when this channel is a new
|
||||||
|
// stream on an existing DataMediaChannel, and we've finished negotiation.
|
||||||
|
void OnChannelReady(bool writable);
|
||||||
protected:
|
protected:
|
||||||
DataChannel(WebRtcSession* session, const std::string& label);
|
DataChannel(WebRtcSession* session, const std::string& label);
|
||||||
virtual ~DataChannel();
|
virtual ~DataChannel();
|
||||||
@ -100,15 +107,15 @@ class DataChannel : public DataChannelInterface,
|
|||||||
void OnDataReceived(cricket::DataChannel* channel,
|
void OnDataReceived(cricket::DataChannel* channel,
|
||||||
const cricket::ReceiveDataParams& params,
|
const cricket::ReceiveDataParams& params,
|
||||||
const talk_base::Buffer& payload);
|
const talk_base::Buffer& payload);
|
||||||
void OnChannelReady(bool writable);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DoClose();
|
void DoClose();
|
||||||
void UpdateState();
|
void UpdateState();
|
||||||
void SetState(DataState state);
|
void SetState(DataState state);
|
||||||
void ConnectToDataSession();
|
|
||||||
void DisconnectFromDataSession();
|
void DisconnectFromDataSession();
|
||||||
bool IsConnectedToDataSession() { return data_session_ != NULL; }
|
bool IsConnectedToDataSession() { return data_session_ != NULL; }
|
||||||
|
void DeliverQueuedControlData();
|
||||||
|
void QueueControl(const talk_base::Buffer* buffer);
|
||||||
void DeliverQueuedReceivedData();
|
void DeliverQueuedReceivedData();
|
||||||
void ClearQueuedReceivedData();
|
void ClearQueuedReceivedData();
|
||||||
void SendQueuedSendData();
|
void SendQueuedSendData();
|
||||||
@ -128,6 +135,9 @@ class DataChannel : public DataChannelInterface,
|
|||||||
uint32 send_ssrc_;
|
uint32 send_ssrc_;
|
||||||
bool receive_ssrc_set_;
|
bool receive_ssrc_set_;
|
||||||
uint32 receive_ssrc_;
|
uint32 receive_ssrc_;
|
||||||
|
// Control messages that always have to get sent out before any queued
|
||||||
|
// data.
|
||||||
|
std::queue<const talk_base::Buffer*> queued_control_data_;
|
||||||
std::queue<DataBuffer*> queued_received_data_;
|
std::queue<DataBuffer*> queued_received_data_;
|
||||||
std::deque<DataBuffer*> queued_send_data_;
|
std::deque<DataBuffer*> queued_send_data_;
|
||||||
};
|
};
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "talk/app/webrtc/datachannel.h"
|
#include "talk/app/webrtc/datachannel.h"
|
||||||
|
#include "talk/app/webrtc/jsep.h"
|
||||||
#include "talk/app/webrtc/mediastreamsignaling.h"
|
#include "talk/app/webrtc/mediastreamsignaling.h"
|
||||||
#include "talk/app/webrtc/test/fakeconstraints.h"
|
#include "talk/app/webrtc/test/fakeconstraints.h"
|
||||||
#include "talk/app/webrtc/webrtcsession.h"
|
#include "talk/app/webrtc/webrtcsession.h"
|
||||||
@ -34,10 +35,31 @@
|
|||||||
#include "talk/media/devices/fakedevicemanager.h"
|
#include "talk/media/devices/fakedevicemanager.h"
|
||||||
#include "talk/session/media/channelmanager.h"
|
#include "talk/session/media/channelmanager.h"
|
||||||
|
|
||||||
|
using webrtc::CreateSessionDescriptionObserver;
|
||||||
using webrtc::MediaConstraintsInterface;
|
using webrtc::MediaConstraintsInterface;
|
||||||
|
using webrtc::SessionDescriptionInterface;
|
||||||
|
|
||||||
const uint32 kFakeSsrc = 1;
|
const uint32 kFakeSsrc = 1;
|
||||||
|
|
||||||
|
class CreateSessionDescriptionObserverForTest
|
||||||
|
: public talk_base::RefCountedObject<CreateSessionDescriptionObserver> {
|
||||||
|
public:
|
||||||
|
CreateSessionDescriptionObserverForTest() : description_(NULL) {}
|
||||||
|
|
||||||
|
virtual void OnSuccess(SessionDescriptionInterface* desc) {
|
||||||
|
description_ = desc;
|
||||||
|
}
|
||||||
|
virtual void OnFailure(const std::string& error) {}
|
||||||
|
|
||||||
|
SessionDescriptionInterface* description() { return description_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~CreateSessionDescriptionObserverForTest() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SessionDescriptionInterface* description_;
|
||||||
|
};
|
||||||
|
|
||||||
class SctpDataChannelTest : public testing::Test {
|
class SctpDataChannelTest : public testing::Test {
|
||||||
protected:
|
protected:
|
||||||
SctpDataChannelTest()
|
SctpDataChannelTest()
|
||||||
@ -49,13 +71,14 @@ class SctpDataChannelTest : public testing::Test {
|
|||||||
new cricket::FakeDeviceManager(),
|
new cricket::FakeDeviceManager(),
|
||||||
new cricket::CaptureManager(),
|
new cricket::CaptureManager(),
|
||||||
talk_base::Thread::Current())),
|
talk_base::Thread::Current())),
|
||||||
ms_signaling_(new webrtc::MediaStreamSignaling(
|
media_stream_signaling_(
|
||||||
talk_base::Thread::Current(), NULL)),
|
new webrtc::MediaStreamSignaling(talk_base::Thread::Current(),
|
||||||
|
NULL)),
|
||||||
session_(channel_manager_.get(),
|
session_(channel_manager_.get(),
|
||||||
talk_base::Thread::Current(),
|
talk_base::Thread::Current(),
|
||||||
talk_base::Thread::Current(),
|
talk_base::Thread::Current(),
|
||||||
NULL,
|
NULL,
|
||||||
ms_signaling_.get()),
|
media_stream_signaling_.get()),
|
||||||
webrtc_data_channel_(NULL) {}
|
webrtc_data_channel_(NULL) {}
|
||||||
|
|
||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
@ -67,10 +90,13 @@ class SctpDataChannelTest : public testing::Test {
|
|||||||
constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true);
|
constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true);
|
||||||
constraints.AddMandatory(MediaConstraintsInterface::kEnableSctpDataChannels,
|
constraints.AddMandatory(MediaConstraintsInterface::kEnableSctpDataChannels,
|
||||||
true);
|
true);
|
||||||
ASSERT_TRUE(session_.Initialize(&constraints));
|
ASSERT_TRUE(session_.Initialize(&constraints, NULL));
|
||||||
webrtc::SessionDescriptionInterface* offer = session_.CreateOffer(NULL);
|
talk_base::scoped_refptr<CreateSessionDescriptionObserverForTest> observer
|
||||||
ASSERT_TRUE(offer != NULL);
|
= new CreateSessionDescriptionObserverForTest();
|
||||||
ASSERT_TRUE(session_.SetLocalDescription(offer, NULL));
|
session_.CreateOffer(observer.get(), NULL);
|
||||||
|
EXPECT_TRUE_WAIT(observer->description() != NULL, 1000);
|
||||||
|
ASSERT_TRUE(observer->description() != NULL);
|
||||||
|
ASSERT_TRUE(session_.SetLocalDescription(observer->description(), NULL));
|
||||||
|
|
||||||
webrtc_data_channel_ = webrtc::DataChannel::Create(&session_, "test", NULL);
|
webrtc_data_channel_ = webrtc::DataChannel::Create(&session_, "test", NULL);
|
||||||
// Connect to the media channel.
|
// Connect to the media channel.
|
||||||
@ -91,7 +117,7 @@ class SctpDataChannelTest : public testing::Test {
|
|||||||
cricket::FakeMediaEngine* media_engine_;
|
cricket::FakeMediaEngine* media_engine_;
|
||||||
cricket::FakeDataEngine* data_engine_;
|
cricket::FakeDataEngine* data_engine_;
|
||||||
talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_;
|
talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_;
|
||||||
talk_base::scoped_ptr<webrtc::MediaStreamSignaling> ms_signaling_;
|
talk_base::scoped_ptr<webrtc::MediaStreamSignaling> media_stream_signaling_;
|
||||||
webrtc::WebRtcSession session_;
|
webrtc::WebRtcSession session_;
|
||||||
talk_base::scoped_refptr<webrtc::DataChannel> webrtc_data_channel_;
|
talk_base::scoped_refptr<webrtc::DataChannel> webrtc_data_channel_;
|
||||||
};
|
};
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "talk/app/webrtc/mediaconstraintsinterface.h"
|
#include "talk/app/webrtc/mediaconstraintsinterface.h"
|
||||||
#include "talk/app/webrtc/mediastreamtrackproxy.h"
|
#include "talk/app/webrtc/mediastreamtrackproxy.h"
|
||||||
#include "talk/app/webrtc/videotrack.h"
|
#include "talk/app/webrtc/videotrack.h"
|
||||||
|
#include "talk/base/bytebuffer.h"
|
||||||
|
|
||||||
static const char kDefaultStreamLabel[] = "default";
|
static const char kDefaultStreamLabel[] = "default";
|
||||||
static const char kDefaultAudioTrackLabel[] = "defaulta0";
|
static const char kDefaultAudioTrackLabel[] = "defaulta0";
|
||||||
@ -235,6 +236,29 @@ bool MediaStreamSignaling::AddDataChannel(DataChannel* data_channel) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MediaStreamSignaling::AddDataChannelFromOpenMessage(
|
||||||
|
const std::string& label,
|
||||||
|
const DataChannelInit& config) {
|
||||||
|
if (!data_channel_factory_) {
|
||||||
|
LOG(LS_WARNING) << "Remote peer requested a DataChannel but DataChannels "
|
||||||
|
<< "are not supported.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_channels_.find(label) != data_channels_.end()) {
|
||||||
|
LOG(LS_ERROR) << "DataChannel with label " << label
|
||||||
|
<< " already exists.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
scoped_refptr<DataChannel> channel(
|
||||||
|
data_channel_factory_->CreateDataChannel(label, &config));
|
||||||
|
data_channels_[label] = channel;
|
||||||
|
stream_observer_->OnAddDataChannel(channel);
|
||||||
|
// It's immediately ready to use.
|
||||||
|
channel->OnChannelReady(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool MediaStreamSignaling::AddLocalStream(MediaStreamInterface* local_stream) {
|
bool MediaStreamSignaling::AddLocalStream(MediaStreamInterface* local_stream) {
|
||||||
if (local_streams_->find(local_stream->label()) != NULL) {
|
if (local_streams_->find(local_stream->label()) != NULL) {
|
||||||
LOG(LS_WARNING) << "MediaStream with label " << local_stream->label()
|
LOG(LS_WARNING) << "MediaStream with label " << local_stream->label()
|
||||||
@ -864,6 +888,145 @@ void MediaStreamSignaling::CreateRemoteDataChannel(const std::string& label,
|
|||||||
stream_observer_->OnAddDataChannel(channel);
|
stream_observer_->OnAddDataChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Format defined at
|
||||||
|
// http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04
|
||||||
|
const uint8 DATA_CHANNEL_OPEN_MESSAGE_TYPE = 0x03;
|
||||||
|
|
||||||
|
enum DataChannelOpenMessageChannelType {
|
||||||
|
DCOMCT_ORDERED_RELIABLE = 0x00,
|
||||||
|
DCOMCT_ORDERED_PARTIAL_RTXS = 0x01,
|
||||||
|
DCOMCT_ORDERED_PARTIAL_TIME = 0x02,
|
||||||
|
DCOMCT_UNORDERED_RELIABLE = 0x80,
|
||||||
|
DCOMCT_UNORDERED_PARTIAL_RTXS = 0x81,
|
||||||
|
DCOMCT_UNORDERED_PARTIAL_TIME = 0x82,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool MediaStreamSignaling::ParseDataChannelOpenMessage(
|
||||||
|
const talk_base::Buffer& payload,
|
||||||
|
std::string* label,
|
||||||
|
DataChannelInit* config) {
|
||||||
|
// Format defined at
|
||||||
|
// http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04
|
||||||
|
|
||||||
|
talk_base::ByteBuffer buffer(payload.data(), payload.length());
|
||||||
|
|
||||||
|
uint8 message_type;
|
||||||
|
if (!buffer.ReadUInt8(&message_type)) {
|
||||||
|
LOG(LS_WARNING) << "Could not read OPEN message type.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (message_type != DATA_CHANNEL_OPEN_MESSAGE_TYPE) {
|
||||||
|
LOG(LS_WARNING) << "Data Channel OPEN message of unexpected type: "
|
||||||
|
<< message_type;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 channel_type;
|
||||||
|
if (!buffer.ReadUInt8(&channel_type)) {
|
||||||
|
LOG(LS_WARNING) << "Could not read OPEN message channel type.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint16 priority;
|
||||||
|
if (!buffer.ReadUInt16(&priority)) {
|
||||||
|
LOG(LS_WARNING) << "Could not read OPEN message reliabilility prioirty.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32 reliability_param;
|
||||||
|
if (!buffer.ReadUInt32(&reliability_param)) {
|
||||||
|
LOG(LS_WARNING) << "Could not read OPEN message reliabilility param.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint16 label_length;
|
||||||
|
if (!buffer.ReadUInt16(&label_length)) {
|
||||||
|
LOG(LS_WARNING) << "Could not read OPEN message label length.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint16 protocol_length;
|
||||||
|
if (!buffer.ReadUInt16(&protocol_length)) {
|
||||||
|
LOG(LS_WARNING) << "Could not read OPEN message protocol length.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!buffer.ReadString(label, (size_t) label_length)) {
|
||||||
|
LOG(LS_WARNING) << "Could not read OPEN message label";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!buffer.ReadString(&config->protocol, protocol_length)) {
|
||||||
|
LOG(LS_WARNING) << "Could not read OPEN message protocol.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
config->ordered = true;
|
||||||
|
switch (channel_type) {
|
||||||
|
case DCOMCT_UNORDERED_RELIABLE:
|
||||||
|
case DCOMCT_UNORDERED_PARTIAL_RTXS:
|
||||||
|
case DCOMCT_UNORDERED_PARTIAL_TIME:
|
||||||
|
config->ordered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
config->maxRetransmits = -1;
|
||||||
|
config->maxRetransmitTime = -1;
|
||||||
|
switch (channel_type) {
|
||||||
|
case DCOMCT_ORDERED_PARTIAL_RTXS:
|
||||||
|
case DCOMCT_UNORDERED_PARTIAL_RTXS:
|
||||||
|
config->maxRetransmits = reliability_param;
|
||||||
|
|
||||||
|
case DCOMCT_ORDERED_PARTIAL_TIME:
|
||||||
|
case DCOMCT_UNORDERED_PARTIAL_TIME:
|
||||||
|
config->maxRetransmitTime = reliability_param;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MediaStreamSignaling::WriteDataChannelOpenMessage(
|
||||||
|
const std::string& label,
|
||||||
|
const DataChannelInit& config,
|
||||||
|
talk_base::Buffer* payload) {
|
||||||
|
// Format defined at
|
||||||
|
// http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04
|
||||||
|
// TODO(pthatcher)
|
||||||
|
|
||||||
|
uint8 channel_type = 0;
|
||||||
|
uint32 reliability_param = 0;
|
||||||
|
uint16 priority = 0;
|
||||||
|
if (config.ordered) {
|
||||||
|
if (config.maxRetransmits > -1) {
|
||||||
|
channel_type = DCOMCT_ORDERED_PARTIAL_RTXS;
|
||||||
|
reliability_param = config.maxRetransmits;
|
||||||
|
} else if (config.maxRetransmitTime > -1) {
|
||||||
|
channel_type = DCOMCT_ORDERED_PARTIAL_TIME;
|
||||||
|
reliability_param = config.maxRetransmitTime;
|
||||||
|
} else {
|
||||||
|
channel_type = DCOMCT_ORDERED_RELIABLE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (config.maxRetransmits > -1) {
|
||||||
|
channel_type = DCOMCT_UNORDERED_PARTIAL_RTXS;
|
||||||
|
reliability_param = config.maxRetransmits;
|
||||||
|
} else if (config.maxRetransmitTime > -1) {
|
||||||
|
channel_type = DCOMCT_UNORDERED_PARTIAL_TIME;
|
||||||
|
reliability_param = config.maxRetransmitTime;
|
||||||
|
} else {
|
||||||
|
channel_type = DCOMCT_UNORDERED_RELIABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
talk_base::ByteBuffer buffer(
|
||||||
|
NULL, 20 + label.length() + config.protocol.length(),
|
||||||
|
talk_base::ByteBuffer::ORDER_NETWORK);
|
||||||
|
buffer.WriteUInt8(DATA_CHANNEL_OPEN_MESSAGE_TYPE);
|
||||||
|
buffer.WriteUInt8(channel_type);
|
||||||
|
buffer.WriteUInt16(priority);
|
||||||
|
buffer.WriteUInt32(reliability_param);
|
||||||
|
buffer.WriteUInt16(static_cast<uint16>(label.length()));
|
||||||
|
buffer.WriteUInt16(static_cast<uint16>(config.protocol.length()));
|
||||||
|
buffer.WriteString(label);
|
||||||
|
buffer.WriteString(config.protocol);
|
||||||
|
payload->SetData(buffer.Data(), buffer.Length());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void MediaStreamSignaling::UpdateLocalSctpDataChannels() {
|
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) {
|
||||||
|
@ -190,6 +190,15 @@ class MediaStreamSignaling {
|
|||||||
// Adds |data_channel| to the collection of DataChannels that will be
|
// Adds |data_channel| to the collection of DataChannels that will be
|
||||||
// be offered in a SessionDescription.
|
// be offered in a SessionDescription.
|
||||||
bool AddDataChannel(DataChannel* data_channel);
|
bool AddDataChannel(DataChannel* data_channel);
|
||||||
|
// After we receive an OPEN message, create a data channel and add it.
|
||||||
|
bool AddDataChannelFromOpenMessage(
|
||||||
|
const std::string& label, const DataChannelInit& config);
|
||||||
|
bool ParseDataChannelOpenMessage(
|
||||||
|
const talk_base::Buffer& payload, std::string* label,
|
||||||
|
DataChannelInit* config);
|
||||||
|
bool WriteDataChannelOpenMessage(
|
||||||
|
const std::string& label, const DataChannelInit& config,
|
||||||
|
talk_base::Buffer* payload);
|
||||||
|
|
||||||
// Returns a MediaSessionOptions struct with options decided by |constraints|,
|
// Returns a MediaSessionOptions struct with options decided by |constraints|,
|
||||||
// the local MediaStreams and DataChannels.
|
// the local MediaStreams and DataChannels.
|
||||||
@ -243,6 +252,8 @@ class MediaStreamSignaling {
|
|||||||
StreamCollectionInterface* remote_streams() const {
|
StreamCollectionInterface* remote_streams() const {
|
||||||
return remote_streams_.get();
|
return remote_streams_.get();
|
||||||
}
|
}
|
||||||
|
void UpdateLocalSctpDataChannels();
|
||||||
|
void UpdateRemoteSctpDataChannels();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct RemotePeerInfo {
|
struct RemotePeerInfo {
|
||||||
@ -357,8 +368,6 @@ class MediaStreamSignaling {
|
|||||||
void UpdateClosingDataChannels(
|
void UpdateClosingDataChannels(
|
||||||
const std::vector<std::string>& active_channels, bool is_local_update);
|
const std::vector<std::string>& active_channels, bool is_local_update);
|
||||||
void CreateRemoteDataChannel(const std::string& label, uint32 remote_ssrc);
|
void CreateRemoteDataChannel(const std::string& label, uint32 remote_ssrc);
|
||||||
void UpdateLocalSctpDataChannels();
|
|
||||||
void UpdateRemoteSctpDataChannels();
|
|
||||||
|
|
||||||
RemotePeerInfo remote_info_;
|
RemotePeerInfo remote_info_;
|
||||||
talk_base::Thread* signaling_thread_;
|
talk_base::Thread* signaling_thread_;
|
||||||
|
@ -50,9 +50,11 @@ static const size_t kTurnHostTokensNum = 2;
|
|||||||
// Number of tokens must be preset when TURN uri has transport param.
|
// Number of tokens must be preset when TURN uri has transport param.
|
||||||
static const size_t kTurnTransportTokensNum = 2;
|
static const size_t kTurnTransportTokensNum = 2;
|
||||||
// The default stun port.
|
// The default stun port.
|
||||||
static const int kDefaultPort = 3478;
|
static const int kDefaultStunPort = 3478;
|
||||||
|
static const int kDefaultStunTlsPort = 5349;
|
||||||
static const char kTransport[] = "transport";
|
static const char kTransport[] = "transport";
|
||||||
static const char kDefaultTransportType[] = "udp";
|
static const char kUdpTransportType[] = "udp";
|
||||||
|
static const char kTcpTransportType[] = "tcp";
|
||||||
|
|
||||||
// NOTE: Must be in the same order as the ServiceType enum.
|
// NOTE: Must be in the same order as the ServiceType enum.
|
||||||
static const char* kValidIceServiceTypes[] = {
|
static const char* kValidIceServiceTypes[] = {
|
||||||
@ -67,9 +69,7 @@ enum ServiceType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MSG_CREATE_SESSIONDESCRIPTION_SUCCESS = 0,
|
MSG_SET_SESSIONDESCRIPTION_SUCCESS = 0,
|
||||||
MSG_CREATE_SESSIONDESCRIPTION_FAILED,
|
|
||||||
MSG_SET_SESSIONDESCRIPTION_SUCCESS,
|
|
||||||
MSG_SET_SESSIONDESCRIPTION_FAILED,
|
MSG_SET_SESSIONDESCRIPTION_FAILED,
|
||||||
MSG_GETSTATS,
|
MSG_GETSTATS,
|
||||||
MSG_ICECONNECTIONCHANGE,
|
MSG_ICECONNECTIONCHANGE,
|
||||||
@ -85,17 +85,6 @@ struct CandidateMsg : public talk_base::MessageData {
|
|||||||
talk_base::scoped_ptr<const webrtc::JsepIceCandidate> candidate;
|
talk_base::scoped_ptr<const webrtc::JsepIceCandidate> candidate;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CreateSessionDescriptionMsg : public talk_base::MessageData {
|
|
||||||
explicit CreateSessionDescriptionMsg(
|
|
||||||
webrtc::CreateSessionDescriptionObserver* observer)
|
|
||||||
: observer(observer) {
|
|
||||||
}
|
|
||||||
|
|
||||||
talk_base::scoped_refptr<webrtc::CreateSessionDescriptionObserver> observer;
|
|
||||||
std::string error;
|
|
||||||
talk_base::scoped_ptr<webrtc::SessionDescriptionInterface> description;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SetSessionDescriptionMsg : public talk_base::MessageData {
|
struct SetSessionDescriptionMsg : public talk_base::MessageData {
|
||||||
explicit SetSessionDescriptionMsg(
|
explicit SetSessionDescriptionMsg(
|
||||||
webrtc::SetSessionDescriptionObserver* observer)
|
webrtc::SetSessionDescriptionObserver* observer)
|
||||||
@ -145,7 +134,7 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::vector<std::string> tokens;
|
std::vector<std::string> tokens;
|
||||||
std::string turn_transport_type = kDefaultTransportType;
|
std::string turn_transport_type = kUdpTransportType;
|
||||||
talk_base::tokenize(server.uri, '?', &tokens);
|
talk_base::tokenize(server.uri, '?', &tokens);
|
||||||
std::string uri_without_transport = tokens[0];
|
std::string uri_without_transport = tokens[0];
|
||||||
// Let's look into transport= param, if it exists.
|
// Let's look into transport= param, if it exists.
|
||||||
@ -153,6 +142,12 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration,
|
|||||||
std::string uri_transport_param = tokens[1];
|
std::string uri_transport_param = tokens[1];
|
||||||
talk_base::tokenize(uri_transport_param, '=', &tokens);
|
talk_base::tokenize(uri_transport_param, '=', &tokens);
|
||||||
if (tokens[0] == kTransport) {
|
if (tokens[0] == kTransport) {
|
||||||
|
// As per above grammar transport param will be consist of lower case
|
||||||
|
// letters.
|
||||||
|
if (tokens[1] != kUdpTransportType && tokens[1] != kTcpTransportType) {
|
||||||
|
LOG(LS_WARNING) << "Transport param should always be udp or tcp.";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
turn_transport_type = tokens[1];
|
turn_transport_type = tokens[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,7 +171,10 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::string address = tokens[1];
|
std::string address = tokens[1];
|
||||||
int port = kDefaultPort;
|
int port = kDefaultStunPort;
|
||||||
|
if (service_type == TURNS)
|
||||||
|
port = kDefaultStunTlsPort;
|
||||||
|
|
||||||
if (tokens.size() > kMinIceUriTokens) {
|
if (tokens.size() > kMinIceUriTokens) {
|
||||||
if (!talk_base::FromString(tokens[2], &port)) {
|
if (!talk_base::FromString(tokens[2], &port)) {
|
||||||
LOG(LS_WARNING) << "Failed to parse port string: " << tokens[2];
|
LOG(LS_WARNING) << "Failed to parse port string: " << tokens[2];
|
||||||
@ -194,7 +192,8 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration,
|
|||||||
case STUNS:
|
case STUNS:
|
||||||
stun_config->push_back(StunConfiguration(address, port));
|
stun_config->push_back(StunConfiguration(address, port));
|
||||||
break;
|
break;
|
||||||
case TURN: {
|
case TURN:
|
||||||
|
case TURNS: {
|
||||||
if (server.username.empty()) {
|
if (server.username.empty()) {
|
||||||
// Turn url example from the spec |url:"turn:user@turn.example.org"|.
|
// Turn url example from the spec |url:"turn:user@turn.example.org"|.
|
||||||
std::vector<std::string> turn_tokens;
|
std::vector<std::string> turn_tokens;
|
||||||
@ -204,15 +203,23 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration,
|
|||||||
address = turn_tokens[1];
|
address = turn_tokens[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool secure = (service_type == TURNS);
|
||||||
|
|
||||||
turn_config->push_back(TurnConfiguration(address, port,
|
turn_config->push_back(TurnConfiguration(address, port,
|
||||||
server.username,
|
server.username,
|
||||||
server.password,
|
server.password,
|
||||||
turn_transport_type));
|
turn_transport_type,
|
||||||
|
secure));
|
||||||
// STUN functionality is part of TURN.
|
// STUN functionality is part of TURN.
|
||||||
|
// Note: If there is only TURNS is supplied as part of configuration,
|
||||||
|
// we will have problem in fetching server reflexive candidate, as
|
||||||
|
// currently we don't have support of TCP/TLS in stunport.cc.
|
||||||
|
// In that case we should fetch server reflexive addess from
|
||||||
|
// TURN allocate response.
|
||||||
stun_config->push_back(StunConfiguration(address, port));
|
stun_config->push_back(StunConfiguration(address, port));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TURNS:
|
|
||||||
case INVALID:
|
case INVALID:
|
||||||
default:
|
default:
|
||||||
LOG(WARNING) << "Configuration not supported: " << server.uri;
|
LOG(WARNING) << "Configuration not supported: " << server.uri;
|
||||||
@ -261,7 +268,8 @@ PeerConnection::~PeerConnection() {
|
|||||||
bool PeerConnection::Initialize(
|
bool PeerConnection::Initialize(
|
||||||
const PeerConnectionInterface::IceServers& configuration,
|
const PeerConnectionInterface::IceServers& configuration,
|
||||||
const MediaConstraintsInterface* constraints,
|
const MediaConstraintsInterface* constraints,
|
||||||
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
PortAllocatorFactoryInterface* allocator_factory,
|
||||||
|
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||||
PeerConnectionObserver* observer) {
|
PeerConnectionObserver* observer) {
|
||||||
std::vector<PortAllocatorFactoryInterface::StunConfiguration> stun_config;
|
std::vector<PortAllocatorFactoryInterface::StunConfiguration> stun_config;
|
||||||
std::vector<PortAllocatorFactoryInterface::TurnConfiguration> turn_config;
|
std::vector<PortAllocatorFactoryInterface::TurnConfiguration> turn_config;
|
||||||
@ -270,7 +278,7 @@ bool PeerConnection::Initialize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return DoInitialize(stun_config, turn_config, constraints,
|
return DoInitialize(stun_config, turn_config, constraints,
|
||||||
allocator_factory, observer);
|
allocator_factory, dtls_identity_service, observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PeerConnection::DoInitialize(
|
bool PeerConnection::DoInitialize(
|
||||||
@ -278,6 +286,7 @@ bool PeerConnection::DoInitialize(
|
|||||||
const TurnConfigurations& turn_config,
|
const TurnConfigurations& turn_config,
|
||||||
const MediaConstraintsInterface* constraints,
|
const MediaConstraintsInterface* constraints,
|
||||||
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
||||||
|
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||||
PeerConnectionObserver* observer) {
|
PeerConnectionObserver* observer) {
|
||||||
ASSERT(observer != NULL);
|
ASSERT(observer != NULL);
|
||||||
if (!observer)
|
if (!observer)
|
||||||
@ -306,10 +315,9 @@ bool PeerConnection::DoInitialize(
|
|||||||
stats_.set_session(session_.get());
|
stats_.set_session(session_.get());
|
||||||
|
|
||||||
// Initialize the WebRtcSession. It creates transport channels etc.
|
// Initialize the WebRtcSession. It creates transport channels etc.
|
||||||
if (!session_->Initialize(constraints))
|
if (!session_->Initialize(constraints, dtls_identity_service))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
// Register PeerConnection as receiver of local ice candidates.
|
// Register PeerConnection as receiver of local ice candidates.
|
||||||
// All the callbacks will be posted to the application from PeerConnection.
|
// All the callbacks will be posted to the application from PeerConnection.
|
||||||
session_->RegisterIceObserver(this);
|
session_->RegisterIceObserver(this);
|
||||||
@ -416,7 +424,16 @@ 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,17 +443,7 @@ void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
|
|||||||
LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
|
LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
|
session_->CreateOffer(observer, constraints);
|
||||||
msg->description.reset(
|
|
||||||
session_->CreateOffer(constraints));
|
|
||||||
|
|
||||||
if (!msg->description) {
|
|
||||||
msg->error = "CreateOffer failed.";
|
|
||||||
signaling_thread()->Post(this, MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
signaling_thread()->Post(this, MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::CreateAnswer(
|
void PeerConnection::CreateAnswer(
|
||||||
@ -446,15 +453,7 @@ void PeerConnection::CreateAnswer(
|
|||||||
LOG(LS_ERROR) << "CreateAnswer - observer is NULL.";
|
LOG(LS_ERROR) << "CreateAnswer - observer is NULL.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
|
session_->CreateAnswer(observer, constraints);
|
||||||
msg->description.reset(session_->CreateAnswer(constraints));
|
|
||||||
if (!msg->description) {
|
|
||||||
msg->error = "CreateAnswer failed.";
|
|
||||||
signaling_thread()->Post(this, MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
signaling_thread()->Post(this, MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::SetLocalDescription(
|
void PeerConnection::SetLocalDescription(
|
||||||
@ -468,7 +467,6 @@ void PeerConnection::SetLocalDescription(
|
|||||||
PostSetSessionDescriptionFailure(observer, "SessionDescription is NULL.");
|
PostSetSessionDescriptionFailure(observer, "SessionDescription is NULL.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update stats here so that we have the most recent stats for tracks and
|
// Update stats here so that we have the most recent stats for tracks and
|
||||||
// streams that might be removed by updating the session description.
|
// streams that might be removed by updating the session description.
|
||||||
stats_.UpdateStats();
|
stats_.UpdateStats();
|
||||||
@ -488,7 +486,6 @@ void PeerConnection::SetRemoteDescription(
|
|||||||
LOG(LS_ERROR) << "SetRemoteDescription - observer is NULL.";
|
LOG(LS_ERROR) << "SetRemoteDescription - observer is NULL.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!desc) {
|
if (!desc) {
|
||||||
PostSetSessionDescriptionFailure(observer, "SessionDescription is NULL.");
|
PostSetSessionDescriptionFailure(observer, "SessionDescription is NULL.");
|
||||||
return;
|
return;
|
||||||
@ -572,20 +569,6 @@ void PeerConnection::OnSessionStateChange(cricket::BaseSession* /*session*/,
|
|||||||
|
|
||||||
void PeerConnection::OnMessage(talk_base::Message* msg) {
|
void PeerConnection::OnMessage(talk_base::Message* msg) {
|
||||||
switch (msg->message_id) {
|
switch (msg->message_id) {
|
||||||
case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: {
|
|
||||||
CreateSessionDescriptionMsg* param =
|
|
||||||
static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
|
|
||||||
param->observer->OnSuccess(param->description.release());
|
|
||||||
delete param;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MSG_CREATE_SESSIONDESCRIPTION_FAILED: {
|
|
||||||
CreateSessionDescriptionMsg* param =
|
|
||||||
static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
|
|
||||||
param->observer->OnFailure(param->error);
|
|
||||||
delete param;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MSG_SET_SESSIONDESCRIPTION_SUCCESS: {
|
case MSG_SET_SESSIONDESCRIPTION_SUCCESS: {
|
||||||
SetSessionDescriptionMsg* param =
|
SetSessionDescriptionMsg* param =
|
||||||
static_cast<SetSessionDescriptionMsg*>(msg->pdata);
|
static_cast<SetSessionDescriptionMsg*>(msg->pdata);
|
||||||
|
@ -59,7 +59,8 @@ class PeerConnection : public PeerConnectionInterface,
|
|||||||
|
|
||||||
bool Initialize(const PeerConnectionInterface::IceServers& configuration,
|
bool Initialize(const PeerConnectionInterface::IceServers& configuration,
|
||||||
const MediaConstraintsInterface* constraints,
|
const MediaConstraintsInterface* constraints,
|
||||||
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
PortAllocatorFactoryInterface* allocator_factory,
|
||||||
|
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||||
PeerConnectionObserver* observer);
|
PeerConnectionObserver* observer);
|
||||||
virtual talk_base::scoped_refptr<StreamCollectionInterface> local_streams();
|
virtual talk_base::scoped_refptr<StreamCollectionInterface> local_streams();
|
||||||
virtual talk_base::scoped_refptr<StreamCollectionInterface> remote_streams();
|
virtual talk_base::scoped_refptr<StreamCollectionInterface> remote_streams();
|
||||||
@ -152,7 +153,8 @@ class PeerConnection : public PeerConnectionInterface,
|
|||||||
bool DoInitialize(const StunConfigurations& stun_config,
|
bool DoInitialize(const StunConfigurations& stun_config,
|
||||||
const TurnConfigurations& turn_config,
|
const TurnConfigurations& turn_config,
|
||||||
const MediaConstraintsInterface* constraints,
|
const MediaConstraintsInterface* constraints,
|
||||||
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
PortAllocatorFactoryInterface* allocator_factory,
|
||||||
|
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||||
PeerConnectionObserver* observer);
|
PeerConnectionObserver* observer);
|
||||||
|
|
||||||
talk_base::Thread* signaling_thread() const {
|
talk_base::Thread* signaling_thread() const {
|
||||||
|
@ -54,16 +54,19 @@ struct CreatePeerConnectionParams : public talk_base::MessageData {
|
|||||||
const webrtc::PeerConnectionInterface::IceServers& configuration,
|
const webrtc::PeerConnectionInterface::IceServers& configuration,
|
||||||
const webrtc::MediaConstraintsInterface* constraints,
|
const webrtc::MediaConstraintsInterface* constraints,
|
||||||
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
||||||
|
webrtc::DTLSIdentityServiceInterface* dtls_identity_service,
|
||||||
webrtc::PeerConnectionObserver* observer)
|
webrtc::PeerConnectionObserver* observer)
|
||||||
: configuration(configuration),
|
: configuration(configuration),
|
||||||
constraints(constraints),
|
constraints(constraints),
|
||||||
allocator_factory(allocator_factory),
|
allocator_factory(allocator_factory),
|
||||||
|
dtls_identity_service(dtls_identity_service),
|
||||||
observer(observer) {
|
observer(observer) {
|
||||||
}
|
}
|
||||||
scoped_refptr<webrtc::PeerConnectionInterface> peerconnection;
|
scoped_refptr<webrtc::PeerConnectionInterface> peerconnection;
|
||||||
const webrtc::PeerConnectionInterface::IceServers& configuration;
|
const webrtc::PeerConnectionInterface::IceServers& configuration;
|
||||||
const webrtc::MediaConstraintsInterface* constraints;
|
const webrtc::MediaConstraintsInterface* constraints;
|
||||||
scoped_refptr<webrtc::PortAllocatorFactoryInterface> allocator_factory;
|
scoped_refptr<webrtc::PortAllocatorFactoryInterface> allocator_factory;
|
||||||
|
webrtc::DTLSIdentityServiceInterface* dtls_identity_service;
|
||||||
webrtc::PeerConnectionObserver* observer;
|
webrtc::PeerConnectionObserver* observer;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -199,10 +202,12 @@ void PeerConnectionFactory::OnMessage(talk_base::Message* msg) {
|
|||||||
}
|
}
|
||||||
case MSG_CREATE_PEERCONNECTION: {
|
case MSG_CREATE_PEERCONNECTION: {
|
||||||
CreatePeerConnectionParams* pdata =
|
CreatePeerConnectionParams* pdata =
|
||||||
static_cast<CreatePeerConnectionParams*>(msg->pdata);
|
static_cast<CreatePeerConnectionParams*> (msg->pdata);
|
||||||
pdata->peerconnection = CreatePeerConnection_s(pdata->configuration,
|
pdata->peerconnection = CreatePeerConnection_s(
|
||||||
|
pdata->configuration,
|
||||||
pdata->constraints,
|
pdata->constraints,
|
||||||
pdata->allocator_factory,
|
pdata->allocator_factory,
|
||||||
|
pdata->dtls_identity_service,
|
||||||
pdata->observer);
|
pdata->observer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -278,7 +283,8 @@ PeerConnectionFactory::CreatePeerConnection(
|
|||||||
DTLSIdentityServiceInterface* dtls_identity_service,
|
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||||
PeerConnectionObserver* observer) {
|
PeerConnectionObserver* observer) {
|
||||||
CreatePeerConnectionParams params(configuration, constraints,
|
CreatePeerConnectionParams params(configuration, constraints,
|
||||||
allocator_factory, observer);
|
allocator_factory, dtls_identity_service,
|
||||||
|
observer);
|
||||||
signaling_thread_->Send(this, MSG_CREATE_PEERCONNECTION, ¶ms);
|
signaling_thread_->Send(this, MSG_CREATE_PEERCONNECTION, ¶ms);
|
||||||
return params.peerconnection;
|
return params.peerconnection;
|
||||||
}
|
}
|
||||||
@ -298,6 +304,7 @@ PeerConnectionFactory::CreatePeerConnection_s(
|
|||||||
const PeerConnectionInterface::IceServers& configuration,
|
const PeerConnectionInterface::IceServers& configuration,
|
||||||
const MediaConstraintsInterface* constraints,
|
const MediaConstraintsInterface* constraints,
|
||||||
PortAllocatorFactoryInterface* allocator_factory,
|
PortAllocatorFactoryInterface* allocator_factory,
|
||||||
|
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||||
PeerConnectionObserver* observer) {
|
PeerConnectionObserver* observer) {
|
||||||
ASSERT(allocator_factory || allocator_factory_);
|
ASSERT(allocator_factory || allocator_factory_);
|
||||||
talk_base::scoped_refptr<PeerConnection> pc(
|
talk_base::scoped_refptr<PeerConnection> pc(
|
||||||
@ -306,6 +313,7 @@ PeerConnectionFactory::CreatePeerConnection_s(
|
|||||||
configuration,
|
configuration,
|
||||||
constraints,
|
constraints,
|
||||||
allocator_factory ? allocator_factory : allocator_factory_.get(),
|
allocator_factory ? allocator_factory : allocator_factory_.get(),
|
||||||
|
dtls_identity_service,
|
||||||
observer)) {
|
observer)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface,
|
|||||||
const PeerConnectionInterface::IceServers& configuration,
|
const PeerConnectionInterface::IceServers& configuration,
|
||||||
const MediaConstraintsInterface* constraints,
|
const MediaConstraintsInterface* constraints,
|
||||||
PortAllocatorFactoryInterface* allocator_factory,
|
PortAllocatorFactoryInterface* allocator_factory,
|
||||||
|
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||||
PeerConnectionObserver* observer);
|
PeerConnectionObserver* observer);
|
||||||
// Implements talk_base::MessageHandler.
|
// Implements talk_base::MessageHandler.
|
||||||
void OnMessage(talk_base::Message* msg);
|
void OnMessage(talk_base::Message* msg);
|
||||||
|
@ -64,7 +64,8 @@ static const char kSecureTurnIceServer[] =
|
|||||||
static const char kTurnIceServerWithNoUsernameInUri[] =
|
static const char kTurnIceServerWithNoUsernameInUri[] =
|
||||||
"turn:test.com:1234";
|
"turn:test.com:1234";
|
||||||
static const char kTurnPassword[] = "turnpassword";
|
static const char kTurnPassword[] = "turnpassword";
|
||||||
static const int kDefaultPort = 3478;
|
static const int kDefaultStunPort = 3478;
|
||||||
|
static const int kDefaultStunTlsPort = 5349;
|
||||||
static const char kTurnUsername[] = "test";
|
static const char kTurnUsername[] = "test";
|
||||||
|
|
||||||
class NullPeerConnectionObserver : public PeerConnectionObserver {
|
class NullPeerConnectionObserver : public PeerConnectionObserver {
|
||||||
@ -174,15 +175,15 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) {
|
|||||||
"test.com", 1234);
|
"test.com", 1234);
|
||||||
stun_configs.push_back(stun2);
|
stun_configs.push_back(stun2);
|
||||||
webrtc::PortAllocatorFactoryInterface::StunConfiguration stun3(
|
webrtc::PortAllocatorFactoryInterface::StunConfiguration stun3(
|
||||||
"hello.com", kDefaultPort);
|
"hello.com", kDefaultStunPort);
|
||||||
stun_configs.push_back(stun3);
|
stun_configs.push_back(stun3);
|
||||||
VerifyStunConfigurations(stun_configs);
|
VerifyStunConfigurations(stun_configs);
|
||||||
TurnConfigurations turn_configs;
|
TurnConfigurations turn_configs;
|
||||||
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1(
|
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1(
|
||||||
"test.com", 1234, "test@hello.com", kTurnPassword, "udp");
|
"test.com", 1234, "test@hello.com", kTurnPassword, "udp", false);
|
||||||
turn_configs.push_back(turn1);
|
turn_configs.push_back(turn1);
|
||||||
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn2(
|
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn2(
|
||||||
"hello.com", kDefaultPort, "test", kTurnPassword, "tcp");
|
"hello.com", kDefaultStunPort, "test", kTurnPassword, "tcp", false);
|
||||||
turn_configs.push_back(turn2);
|
turn_configs.push_back(turn2);
|
||||||
VerifyTurnConfigurations(turn_configs);
|
VerifyTurnConfigurations(turn_configs);
|
||||||
}
|
}
|
||||||
@ -204,7 +205,7 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingNoUsernameInUri) {
|
|||||||
EXPECT_TRUE(pc.get() != NULL);
|
EXPECT_TRUE(pc.get() != NULL);
|
||||||
TurnConfigurations turn_configs;
|
TurnConfigurations turn_configs;
|
||||||
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn(
|
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn(
|
||||||
"test.com", 1234, kTurnUsername, kTurnPassword, "udp");
|
"test.com", 1234, kTurnUsername, kTurnPassword, "udp", false);
|
||||||
turn_configs.push_back(turn);
|
turn_configs.push_back(turn);
|
||||||
VerifyTurnConfigurations(turn_configs);
|
VerifyTurnConfigurations(turn_configs);
|
||||||
}
|
}
|
||||||
@ -225,19 +226,16 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) {
|
|||||||
EXPECT_TRUE(pc.get() != NULL);
|
EXPECT_TRUE(pc.get() != NULL);
|
||||||
TurnConfigurations turn_configs;
|
TurnConfigurations turn_configs;
|
||||||
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn(
|
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn(
|
||||||
"hello.com", kDefaultPort, "test", kTurnPassword, "tcp");
|
"hello.com", kDefaultStunPort, "test", kTurnPassword, "tcp", false);
|
||||||
turn_configs.push_back(turn);
|
turn_configs.push_back(turn);
|
||||||
VerifyTurnConfigurations(turn_configs);
|
VerifyTurnConfigurations(turn_configs);
|
||||||
StunConfigurations stun_configs;
|
StunConfigurations stun_configs;
|
||||||
webrtc::PortAllocatorFactoryInterface::StunConfiguration stun(
|
webrtc::PortAllocatorFactoryInterface::StunConfiguration stun(
|
||||||
"hello.com", kDefaultPort);
|
"hello.com", kDefaultStunPort);
|
||||||
stun_configs.push_back(stun);
|
stun_configs.push_back(stun);
|
||||||
VerifyStunConfigurations(stun_configs);
|
VerifyStunConfigurations(stun_configs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test verifies factory failed to create a peerconneciton object when
|
|
||||||
// a valid secure TURN url passed. Connecting to a secure TURN server is not
|
|
||||||
// supported currently.
|
|
||||||
TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) {
|
TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) {
|
||||||
webrtc::PeerConnectionInterface::IceServers ice_servers;
|
webrtc::PeerConnectionInterface::IceServers ice_servers;
|
||||||
webrtc::PeerConnectionInterface::IceServer ice_server;
|
webrtc::PeerConnectionInterface::IceServer ice_server;
|
||||||
@ -249,8 +247,11 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) {
|
|||||||
allocator_factory_.get(),
|
allocator_factory_.get(),
|
||||||
NULL,
|
NULL,
|
||||||
&observer_));
|
&observer_));
|
||||||
EXPECT_TRUE(pc.get() == NULL);
|
EXPECT_TRUE(pc.get() != NULL);
|
||||||
TurnConfigurations turn_configs;
|
TurnConfigurations turn_configs;
|
||||||
|
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn(
|
||||||
|
"hello.com", kDefaultStunTlsPort, "test", kTurnPassword, "tcp", true);
|
||||||
|
turn_configs.push_back(turn);
|
||||||
VerifyTurnConfigurations(turn_configs);
|
VerifyTurnConfigurations(turn_configs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,15 +315,18 @@ class PortAllocatorFactoryInterface : public talk_base::RefCountInterface {
|
|||||||
int port,
|
int port,
|
||||||
const std::string& username,
|
const std::string& username,
|
||||||
const std::string& password,
|
const std::string& password,
|
||||||
const std::string& transport_type)
|
const std::string& transport_type,
|
||||||
|
bool secure)
|
||||||
: server(address, port),
|
: server(address, port),
|
||||||
username(username),
|
username(username),
|
||||||
password(password),
|
password(password),
|
||||||
transport_type(transport_type) {}
|
transport_type(transport_type),
|
||||||
|
secure(secure) {}
|
||||||
talk_base::SocketAddress server;
|
talk_base::SocketAddress server;
|
||||||
std::string username;
|
std::string username;
|
||||||
std::string password;
|
std::string password;
|
||||||
std::string transport_type;
|
std::string transport_type;
|
||||||
|
bool secure;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual cricket::PortAllocator* CreatePortAllocator(
|
virtual cricket::PortAllocator* CreatePortAllocator(
|
||||||
@ -339,8 +342,8 @@ class PortAllocatorFactoryInterface : public talk_base::RefCountInterface {
|
|||||||
class DTLSIdentityRequestObserver : public talk_base::RefCountInterface {
|
class DTLSIdentityRequestObserver : public talk_base::RefCountInterface {
|
||||||
public:
|
public:
|
||||||
virtual void OnFailure(int error) = 0;
|
virtual void OnFailure(int error) = 0;
|
||||||
virtual void OnSuccess(const std::string& certificate,
|
virtual void OnSuccess(const std::string& der_cert,
|
||||||
const std::string& private_key) = 0;
|
const std::string& der_private_key) = 0;
|
||||||
protected:
|
protected:
|
||||||
virtual ~DTLSIdentityRequestObserver() {}
|
virtual ~DTLSIdentityRequestObserver() {}
|
||||||
};
|
};
|
||||||
@ -372,6 +375,8 @@ class DTLSIdentityServiceInterface {
|
|||||||
const std::string& identity_name,
|
const std::string& identity_name,
|
||||||
const std::string& common_name,
|
const std::string& common_name,
|
||||||
DTLSIdentityRequestObserver* observer) = 0;
|
DTLSIdentityRequestObserver* observer) = 0;
|
||||||
|
|
||||||
|
virtual ~DTLSIdentityServiceInterface() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// PeerConnectionFactoryInterface is the factory interface use for creating
|
// PeerConnectionFactoryInterface is the factory interface use for creating
|
||||||
|
@ -77,7 +77,7 @@ cricket::PortAllocator* PortAllocatorFactory::CreatePortAllocator(
|
|||||||
cricket::ProtocolType protocol;
|
cricket::ProtocolType protocol;
|
||||||
if (cricket::StringToProto(turn[i].transport_type.c_str(), &protocol)) {
|
if (cricket::StringToProto(turn[i].transport_type.c_str(), &protocol)) {
|
||||||
relay_server.ports.push_back(cricket::ProtocolAddress(
|
relay_server.ports.push_back(cricket::ProtocolAddress(
|
||||||
turn[i].server, protocol));
|
turn[i].server, protocol, turn[i].secure));
|
||||||
relay_server.credentials = credentials;
|
relay_server.credentials = credentials;
|
||||||
allocator->AddRelay(relay_server);
|
allocator->AddRelay(relay_server);
|
||||||
} else {
|
} else {
|
||||||
|
135
talk/app/webrtc/test/fakedtlsidentityservice.h
Normal file
135
talk/app/webrtc/test/fakedtlsidentityservice.h
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TALK_APP_WEBRTC_TEST_FAKEDTLSIDENTITYSERVICE_H_
|
||||||
|
#define TALK_APP_WEBRTC_TEST_FAKEDTLSIDENTITYSERVICE_H_
|
||||||
|
|
||||||
|
#include "talk/app/webrtc/peerconnectioninterface.h"
|
||||||
|
|
||||||
|
static const char kRSA_PRIVATE_KEY_PEM[] =
|
||||||
|
"-----BEGIN RSA PRIVATE KEY-----\n"
|
||||||
|
"MIICXQIBAAKBgQDCueE4a9hDMZ3sbVZdlXOz9ZA+cvzie3zJ9gXnT/BCt9P4b9HE\n"
|
||||||
|
"vD/tr73YBqD3Wr5ZWScmyGYF9EMn0r3rzBxv6oooLU5TdUvOm4rzUjkCLQaQML8o\n"
|
||||||
|
"NxXq+qW/j3zUKGikLhaaAl/amaX2zSWUsRQ1CpngQ3+tmDNH4/25TncNmQIDAQAB\n"
|
||||||
|
"AoGAUcuU0Id0k10fMjYHZk4mCPzot2LD2Tr4Aznl5vFMQipHzv7hhZtx2xzMSRcX\n"
|
||||||
|
"vG+Qr6VkbcUWHgApyWubvZXCh3+N7Vo2aYdMAQ8XqmFpBdIrL5CVdVfqFfEMlgEy\n"
|
||||||
|
"LSZNG5klnrIfl3c7zQVovLr4eMqyl2oGfAqPQz75+fecv1UCQQD6wNHch9NbAG1q\n"
|
||||||
|
"yuFEhMARB6gDXb+5SdzFjjtTWW5uJfm4DcZLoYyaIZm0uxOwsUKd0Rsma+oGitS1\n"
|
||||||
|
"CXmuqfpPAkEAxszyN3vIdpD44SREEtyKZBMNOk5pEIIGdbeMJC5/XHvpxww9xkoC\n"
|
||||||
|
"+39NbvUZYd54uT+rafbx4QZKc0h9xA/HlwJBAL37lYVWy4XpPv1olWCKi9LbUCqs\n"
|
||||||
|
"vvQtyD1N1BkEayy9TQRsO09WKOcmigRqsTJwOx7DLaTgokEuspYvhagWVPUCQE/y\n"
|
||||||
|
"0+YkTbYBD1Xbs9SyBKXCU6uDJRWSdO6aZi2W1XloC9gUwDMiSJjD1Wwt/YsyYPJ+\n"
|
||||||
|
"/Hyc5yFL2l0KZimW/vkCQQCjuZ/lPcH46EuzhdbRfumDOG5N3ld7UhGI1TIRy17W\n"
|
||||||
|
"dGF90cG33/L6BfS8Ll+fkkW/2AMRk8FDvF4CZi2nfW4L\n"
|
||||||
|
"-----END RSA PRIVATE KEY-----\n";
|
||||||
|
|
||||||
|
static const char kCERT_PEM[] =
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
"MIIBmTCCAQICCQCPNJORW/M13DANBgkqhkiG9w0BAQUFADARMQ8wDQYDVQQDDAZ3\n"
|
||||||
|
"ZWJydGMwHhcNMTMwNjE0MjIzMDAxWhcNMTQwNjE0MjIzMDAxWjARMQ8wDQYDVQQD\n"
|
||||||
|
"DAZ3ZWJydGMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMK54Thr2EMxnext\n"
|
||||||
|
"Vl2Vc7P1kD5y/OJ7fMn2BedP8EK30/hv0cS8P+2vvdgGoPdavllZJybIZgX0QyfS\n"
|
||||||
|
"vevMHG/qiigtTlN1S86bivNSOQItBpAwvyg3Fer6pb+PfNQoaKQuFpoCX9qZpfbN\n"
|
||||||
|
"JZSxFDUKmeBDf62YM0fj/blOdw2ZAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAECMt\n"
|
||||||
|
"UZb35H8TnjGx4XPzco/kbnurMLFFWcuve/DwTsuf10Ia9N4md8LY0UtgIgtyNqWc\n"
|
||||||
|
"ZwyRMwxONF6ty3wcaIiPbGqiAa55T3YRuPibkRmck9CjrmM9JAtyvqHnpHd2TsBD\n"
|
||||||
|
"qCV42aXS3onOXDQ1ibuWq0fr0//aj0wo4KV474c=\n"
|
||||||
|
"-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
|
using webrtc::DTLSIdentityRequestObserver;
|
||||||
|
|
||||||
|
class FakeIdentityService : public webrtc::DTLSIdentityServiceInterface,
|
||||||
|
public talk_base::MessageHandler {
|
||||||
|
public:
|
||||||
|
struct Request {
|
||||||
|
Request(const std::string& common_name,
|
||||||
|
DTLSIdentityRequestObserver* observer)
|
||||||
|
: common_name(common_name), observer(observer) {}
|
||||||
|
|
||||||
|
std::string common_name;
|
||||||
|
talk_base::scoped_refptr<DTLSIdentityRequestObserver> observer;
|
||||||
|
};
|
||||||
|
typedef talk_base::TypedMessageData<Request> MessageData;
|
||||||
|
|
||||||
|
FakeIdentityService() : should_fail_(false) {}
|
||||||
|
|
||||||
|
void set_should_fail(bool should_fail) {
|
||||||
|
should_fail_ = should_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DTLSIdentityServiceInterface implemenation.
|
||||||
|
virtual bool RequestIdentity(const std::string& identity_name,
|
||||||
|
const std::string& common_name,
|
||||||
|
DTLSIdentityRequestObserver* observer) {
|
||||||
|
MessageData* msg = new MessageData(Request(common_name, observer));
|
||||||
|
if (should_fail_) {
|
||||||
|
talk_base::Thread::Current()->Post(this, MSG_FAILURE, msg);
|
||||||
|
} else {
|
||||||
|
talk_base::Thread::Current()->Post(this, MSG_SUCCESS, msg);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum {
|
||||||
|
MSG_SUCCESS,
|
||||||
|
MSG_FAILURE,
|
||||||
|
};
|
||||||
|
|
||||||
|
// talk_base::MessageHandler implementation.
|
||||||
|
void OnMessage(talk_base::Message* msg) {
|
||||||
|
FakeIdentityService::MessageData* message_data =
|
||||||
|
static_cast<FakeIdentityService::MessageData*>(msg->pdata);
|
||||||
|
DTLSIdentityRequestObserver* observer = message_data->data().observer.get();
|
||||||
|
switch (msg->message_id) {
|
||||||
|
case MSG_SUCCESS: {
|
||||||
|
std::string cert, key;
|
||||||
|
GenerateIdentity(message_data->data().common_name, &cert, &key);
|
||||||
|
observer->OnSuccess(cert, key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MSG_FAILURE:
|
||||||
|
observer->OnFailure(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateIdentity(
|
||||||
|
const std::string& common_name,
|
||||||
|
std::string* der_cert,
|
||||||
|
std::string* der_key) {
|
||||||
|
talk_base::SSLIdentity::PemToDer("CERTIFICATE", kCERT_PEM, der_cert);
|
||||||
|
talk_base::SSLIdentity::PemToDer("RSA PRIVATE KEY",
|
||||||
|
kRSA_PRIVATE_KEY_PEM,
|
||||||
|
der_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_fail_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TALK_APP_WEBRTC_TEST_FAKEDTLSIDENTITYSERVICE_H_
|
156
talk/app/webrtc/test/fakemediastreamsignaling.h
Normal file
156
talk/app/webrtc/test/fakemediastreamsignaling.h
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TALK_APP_WEBRTC_TEST_FAKEMEDIASTREAMSIGNALING_H_
|
||||||
|
#define TALK_APP_WEBRTC_TEST_FAKEMEDIASTREAMSIGNALING_H_
|
||||||
|
|
||||||
|
#include "talk/app/webrtc/audiotrack.h"
|
||||||
|
#include "talk/app/webrtc/mediastreamsignaling.h"
|
||||||
|
#include "talk/app/webrtc/videotrack.h"
|
||||||
|
|
||||||
|
static const char kStream1[] = "stream1";
|
||||||
|
static const char kVideoTrack1[] = "video1";
|
||||||
|
static const char kAudioTrack1[] = "audio1";
|
||||||
|
|
||||||
|
static const char kStream2[] = "stream2";
|
||||||
|
static const char kVideoTrack2[] = "video2";
|
||||||
|
static const char kAudioTrack2[] = "audio2";
|
||||||
|
|
||||||
|
class FakeMediaStreamSignaling : public webrtc::MediaStreamSignaling,
|
||||||
|
public webrtc::MediaStreamSignalingObserver {
|
||||||
|
public:
|
||||||
|
FakeMediaStreamSignaling() :
|
||||||
|
webrtc::MediaStreamSignaling(talk_base::Thread::Current(), this) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendAudioVideoStream1() {
|
||||||
|
ClearLocalStreams();
|
||||||
|
AddLocalStream(CreateStream(kStream1, kAudioTrack1, kVideoTrack1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendAudioVideoStream2() {
|
||||||
|
ClearLocalStreams();
|
||||||
|
AddLocalStream(CreateStream(kStream2, kAudioTrack2, kVideoTrack2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendAudioVideoStream1And2() {
|
||||||
|
ClearLocalStreams();
|
||||||
|
AddLocalStream(CreateStream(kStream1, kAudioTrack1, kVideoTrack1));
|
||||||
|
AddLocalStream(CreateStream(kStream2, kAudioTrack2, kVideoTrack2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendNothing() {
|
||||||
|
ClearLocalStreams();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UseOptionsAudioOnly() {
|
||||||
|
ClearLocalStreams();
|
||||||
|
AddLocalStream(CreateStream(kStream2, kAudioTrack2, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UseOptionsVideoOnly() {
|
||||||
|
ClearLocalStreams();
|
||||||
|
AddLocalStream(CreateStream(kStream2, "", kVideoTrack2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearLocalStreams() {
|
||||||
|
while (local_streams()->count() != 0) {
|
||||||
|
RemoveLocalStream(local_streams()->at(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements MediaStreamSignalingObserver.
|
||||||
|
virtual void OnAddRemoteStream(webrtc::MediaStreamInterface* stream) {
|
||||||
|
}
|
||||||
|
virtual void OnRemoveRemoteStream(webrtc::MediaStreamInterface* stream) {
|
||||||
|
}
|
||||||
|
virtual void OnAddDataChannel(webrtc::DataChannelInterface* data_channel) {
|
||||||
|
}
|
||||||
|
virtual void OnAddLocalAudioTrack(webrtc::MediaStreamInterface* stream,
|
||||||
|
webrtc::AudioTrackInterface* audio_track,
|
||||||
|
uint32 ssrc) {
|
||||||
|
}
|
||||||
|
virtual void OnAddLocalVideoTrack(webrtc::MediaStreamInterface* stream,
|
||||||
|
webrtc::VideoTrackInterface* video_track,
|
||||||
|
uint32 ssrc) {
|
||||||
|
}
|
||||||
|
virtual void OnAddRemoteAudioTrack(webrtc::MediaStreamInterface* stream,
|
||||||
|
webrtc::AudioTrackInterface* audio_track,
|
||||||
|
uint32 ssrc) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnAddRemoteVideoTrack(webrtc::MediaStreamInterface* stream,
|
||||||
|
webrtc::VideoTrackInterface* video_track,
|
||||||
|
uint32 ssrc) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnRemoveRemoteAudioTrack(
|
||||||
|
webrtc::MediaStreamInterface* stream,
|
||||||
|
webrtc::AudioTrackInterface* audio_track) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnRemoveRemoteVideoTrack(
|
||||||
|
webrtc::MediaStreamInterface* stream,
|
||||||
|
webrtc::VideoTrackInterface* video_track) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnRemoveLocalAudioTrack(
|
||||||
|
webrtc::MediaStreamInterface* stream,
|
||||||
|
webrtc::AudioTrackInterface* audio_track) {
|
||||||
|
}
|
||||||
|
virtual void OnRemoveLocalVideoTrack(
|
||||||
|
webrtc::MediaStreamInterface* stream,
|
||||||
|
webrtc::VideoTrackInterface* video_track) {
|
||||||
|
}
|
||||||
|
virtual void OnRemoveLocalStream(webrtc::MediaStreamInterface* stream) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
talk_base::scoped_refptr<webrtc::MediaStreamInterface> CreateStream(
|
||||||
|
const std::string& stream_label,
|
||||||
|
const std::string& audio_track_id,
|
||||||
|
const std::string& video_track_id) {
|
||||||
|
talk_base::scoped_refptr<webrtc::MediaStreamInterface> stream(
|
||||||
|
webrtc::MediaStream::Create(stream_label));
|
||||||
|
|
||||||
|
if (!audio_track_id.empty()) {
|
||||||
|
talk_base::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
|
||||||
|
webrtc::AudioTrack::Create(audio_track_id, NULL));
|
||||||
|
stream->AddTrack(audio_track);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!video_track_id.empty()) {
|
||||||
|
talk_base::scoped_refptr<webrtc::VideoTrackInterface> video_track(
|
||||||
|
webrtc::VideoTrack::Create(video_track_id, NULL));
|
||||||
|
stream->AddTrack(video_track);
|
||||||
|
}
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TALK_APP_WEBRTC_TEST_FAKEMEDIASTREAMSIGNALING_H_
|
@ -38,6 +38,7 @@ if env.Bit('have_webrtc_voice') and env.Bit('have_webrtc_video'):
|
|||||||
'videotrack.cc',
|
'videotrack.cc',
|
||||||
'webrtcsdp.cc',
|
'webrtcsdp.cc',
|
||||||
'webrtcsession.cc',
|
'webrtcsession.cc',
|
||||||
|
'webrtcsessiondescriptionfactory.cc',
|
||||||
],
|
],
|
||||||
lin_ccflags = peerconnection_lin_ccflags
|
lin_ccflags = peerconnection_lin_ccflags
|
||||||
)
|
)
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "talk/app/webrtc/mediaconstraintsinterface.h"
|
#include "talk/app/webrtc/mediaconstraintsinterface.h"
|
||||||
#include "talk/app/webrtc/mediastreamsignaling.h"
|
#include "talk/app/webrtc/mediastreamsignaling.h"
|
||||||
#include "talk/app/webrtc/peerconnectioninterface.h"
|
#include "talk/app/webrtc/peerconnectioninterface.h"
|
||||||
|
#include "talk/app/webrtc/webrtcsessiondescriptionfactory.h"
|
||||||
#include "talk/base/helpers.h"
|
#include "talk/base/helpers.h"
|
||||||
#include "talk/base/logging.h"
|
#include "talk/base/logging.h"
|
||||||
#include "talk/base/stringencode.h"
|
#include "talk/base/stringencode.h"
|
||||||
@ -51,13 +52,8 @@ using cricket::MediaContentDescription;
|
|||||||
using cricket::SessionDescription;
|
using cricket::SessionDescription;
|
||||||
using cricket::TransportInfo;
|
using cricket::TransportInfo;
|
||||||
|
|
||||||
typedef cricket::MediaSessionOptions::Stream Stream;
|
|
||||||
typedef cricket::MediaSessionOptions::Streams Streams;
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
static const uint64 kInitSessionVersion = 2;
|
|
||||||
|
|
||||||
const char kInternalConstraintPrefix[] = "internal";
|
const char kInternalConstraintPrefix[] = "internal";
|
||||||
|
|
||||||
// Supported MediaConstraints.
|
// Supported MediaConstraints.
|
||||||
@ -73,10 +69,6 @@ const char MediaConstraintsInterface::kEnableRtpDataChannels[] =
|
|||||||
const char MediaConstraintsInterface::kEnableSctpDataChannels[] =
|
const char MediaConstraintsInterface::kEnableSctpDataChannels[] =
|
||||||
"internalSctpDataChannels";
|
"internalSctpDataChannels";
|
||||||
|
|
||||||
// Arbitrary constant used as prefix for the identity.
|
|
||||||
// Chosen to make the certificates more readable.
|
|
||||||
const char kWebRTCIdentityPrefix[] = "WebRTC";
|
|
||||||
|
|
||||||
// Error messages
|
// Error messages
|
||||||
const char kSetLocalSdpFailed[] = "SetLocalDescription failed: ";
|
const char kSetLocalSdpFailed[] = "SetLocalDescription failed: ";
|
||||||
const char kSetRemoteSdpFailed[] = "SetRemoteDescription failed: ";
|
const char kSetRemoteSdpFailed[] = "SetRemoteDescription failed: ";
|
||||||
@ -114,24 +106,6 @@ static bool VerifyMediaDescriptions(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CopyCandidatesFromSessionDescription(
|
|
||||||
const SessionDescriptionInterface* source_desc,
|
|
||||||
SessionDescriptionInterface* dest_desc) {
|
|
||||||
if (!source_desc)
|
|
||||||
return;
|
|
||||||
for (size_t m = 0; m < source_desc->number_of_mediasections() &&
|
|
||||||
m < dest_desc->number_of_mediasections(); ++m) {
|
|
||||||
const IceCandidateCollection* source_candidates =
|
|
||||||
source_desc->candidates(m);
|
|
||||||
const IceCandidateCollection* dest_candidates = dest_desc->candidates(m);
|
|
||||||
for (size_t n = 0; n < source_candidates->count(); ++n) {
|
|
||||||
const IceCandidateInterface* new_candidate = source_candidates->at(n);
|
|
||||||
if (!dest_candidates->HasCandidate(new_candidate))
|
|
||||||
dest_desc->AddCandidate(source_candidates->at(n));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks that each non-rejected content has SDES crypto keys or a DTLS
|
// Checks that each non-rejected content has SDES crypto keys or a DTLS
|
||||||
// fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES
|
// fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES
|
||||||
// keys, will be caught in Transport negotiation, and backstopped by Channel's
|
// keys, will be caught in Transport negotiation, and backstopped by Channel's
|
||||||
@ -167,22 +141,29 @@ static bool VerifyCrypto(const SessionDescription* desc) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CompareStream(const Stream& stream1, const Stream& stream2) {
|
// Forces |sdesc->crypto_required| to the appropriate state based on the
|
||||||
return (stream1.id < stream2.id);
|
// current security policy, to ensure a failure occurs if there is an error
|
||||||
}
|
// in crypto negotiation.
|
||||||
|
// Called when processing the local session description.
|
||||||
|
static void UpdateSessionDescriptionSecurePolicy(
|
||||||
|
cricket::SecureMediaPolicy secure_policy,
|
||||||
|
SessionDescription* sdesc) {
|
||||||
|
if (!sdesc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static bool SameId(const Stream& stream1, const Stream& stream2) {
|
// Updating the |crypto_required_| in MediaContentDescription to the
|
||||||
return (stream1.id == stream2.id);
|
// appropriate state based on the current security policy.
|
||||||
}
|
for (cricket::ContentInfos::iterator iter = sdesc->contents().begin();
|
||||||
|
iter != sdesc->contents().end(); ++iter) {
|
||||||
// Checks if each Stream within the |streams| has unique id.
|
if (cricket::IsMediaContent(&*iter)) {
|
||||||
static bool ValidStreams(const Streams& streams) {
|
MediaContentDescription* mdesc =
|
||||||
Streams sorted_streams = streams;
|
static_cast<MediaContentDescription*> (iter->description);
|
||||||
std::sort(sorted_streams.begin(), sorted_streams.end(), CompareStream);
|
if (mdesc) {
|
||||||
Streams::iterator it =
|
mdesc->set_crypto_required(secure_policy == cricket::SEC_REQUIRED);
|
||||||
std::adjacent_find(sorted_streams.begin(), sorted_streams.end(),
|
}
|
||||||
SameId);
|
}
|
||||||
return (it == sorted_streams.end());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GetAudioSsrcByTrackId(
|
static bool GetAudioSsrcByTrackId(
|
||||||
@ -343,15 +324,17 @@ class IceRestartAnswerLatch {
|
|||||||
public:
|
public:
|
||||||
IceRestartAnswerLatch() : ice_restart_(false) { }
|
IceRestartAnswerLatch() : ice_restart_(false) { }
|
||||||
|
|
||||||
// Returns true if CheckForRemoteIceRestart has been called since last
|
// Returns true if CheckForRemoteIceRestart has been called with a new session
|
||||||
// time this method was called with a new session description where
|
// description where ice password and ufrag has changed since last time
|
||||||
// ice password and ufrag has changed.
|
// Reset() was called.
|
||||||
bool AnswerWithIceRestartLatch() {
|
bool Get() const {
|
||||||
|
return ice_restart_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset() {
|
||||||
if (ice_restart_) {
|
if (ice_restart_) {
|
||||||
ice_restart_ = false;
|
ice_restart_ = false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckForRemoteIceRestart(
|
void CheckForRemoteIceRestart(
|
||||||
@ -391,7 +374,8 @@ class IceRestartAnswerLatch {
|
|||||||
bool ice_restart_;
|
bool ice_restart_;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebRtcSession::WebRtcSession(cricket::ChannelManager* channel_manager,
|
WebRtcSession::WebRtcSession(
|
||||||
|
cricket::ChannelManager* channel_manager,
|
||||||
talk_base::Thread* signaling_thread,
|
talk_base::Thread* signaling_thread,
|
||||||
talk_base::Thread* worker_thread,
|
talk_base::Thread* worker_thread,
|
||||||
cricket::PortAllocator* port_allocator,
|
cricket::PortAllocator* port_allocator,
|
||||||
@ -404,19 +388,12 @@ WebRtcSession::WebRtcSession(cricket::ChannelManager* channel_manager,
|
|||||||
// o line MUST be representable with a "64 bit signed integer".
|
// o line MUST be representable with a "64 bit signed integer".
|
||||||
// Due to this constraint session id |sid_| is max limited to LLONG_MAX.
|
// Due to this constraint session id |sid_| is max limited to LLONG_MAX.
|
||||||
channel_manager_(channel_manager),
|
channel_manager_(channel_manager),
|
||||||
session_desc_factory_(channel_manager, &transport_desc_factory_),
|
|
||||||
mediastream_signaling_(mediastream_signaling),
|
mediastream_signaling_(mediastream_signaling),
|
||||||
ice_observer_(NULL),
|
ice_observer_(NULL),
|
||||||
ice_connection_state_(PeerConnectionInterface::kIceConnectionNew),
|
ice_connection_state_(PeerConnectionInterface::kIceConnectionNew),
|
||||||
// RFC 4566 suggested a Network Time Protocol (NTP) format timestamp
|
|
||||||
// as the session id and session version. To simplify, it should be fine
|
|
||||||
// to just use a random number as session id and start version from
|
|
||||||
// |kInitSessionVersion|.
|
|
||||||
session_version_(kInitSessionVersion),
|
|
||||||
older_version_remote_peer_(false),
|
older_version_remote_peer_(false),
|
||||||
data_channel_type_(cricket::DCT_NONE),
|
data_channel_type_(cricket::DCT_NONE),
|
||||||
ice_restart_latch_(new IceRestartAnswerLatch) {
|
ice_restart_latch_(new IceRestartAnswerLatch) {
|
||||||
transport_desc_factory_.set_protocol(cricket::ICEPROTO_HYBRID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebRtcSession::~WebRtcSession() {
|
WebRtcSession::~WebRtcSession() {
|
||||||
@ -436,34 +413,15 @@ WebRtcSession::~WebRtcSession() {
|
|||||||
delete saved_candidates_[i];
|
delete saved_candidates_[i];
|
||||||
}
|
}
|
||||||
delete identity();
|
delete identity();
|
||||||
set_identity(NULL);
|
|
||||||
transport_desc_factory_.set_identity(NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebRtcSession::Initialize(const MediaConstraintsInterface* constraints) {
|
bool WebRtcSession::Initialize(
|
||||||
|
const MediaConstraintsInterface* constraints,
|
||||||
|
DTLSIdentityServiceInterface* dtls_identity_service) {
|
||||||
// TODO(perkj): Take |constraints| into consideration. Return false if not all
|
// TODO(perkj): Take |constraints| into consideration. Return false if not all
|
||||||
// mandatory constraints can be fulfilled. Note that |constraints|
|
// mandatory constraints can be fulfilled. Note that |constraints|
|
||||||
// can be null.
|
// can be null.
|
||||||
|
|
||||||
// By default SRTP-SDES is enabled in WebRtc.
|
|
||||||
set_secure_policy(cricket::SEC_REQUIRED);
|
|
||||||
|
|
||||||
// Enable DTLS-SRTP if the constraint is set.
|
|
||||||
bool value;
|
bool value;
|
||||||
if (FindConstraint(constraints, MediaConstraintsInterface::kEnableDtlsSrtp,
|
|
||||||
&value, NULL) && value) {
|
|
||||||
LOG(LS_INFO) << "DTLS-SRTP enabled; generating identity";
|
|
||||||
std::string identity_name = kWebRTCIdentityPrefix +
|
|
||||||
talk_base::ToString(talk_base::CreateRandomId());
|
|
||||||
transport_desc_factory_.set_identity(talk_base::SSLIdentity::Generate(
|
|
||||||
identity_name));
|
|
||||||
LOG(LS_INFO) << "Finished generating identity";
|
|
||||||
set_identity(transport_desc_factory_.identity());
|
|
||||||
transport_desc_factory_.set_digest_algorithm(talk_base::DIGEST_SHA_256);
|
|
||||||
|
|
||||||
transport_desc_factory_.set_secure(cricket::SEC_ENABLED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable creation of RTP data channels if the kEnableRtpDataChannels is set.
|
// Enable creation of RTP data channels if the kEnableRtpDataChannels is set.
|
||||||
// It takes precendence over the kEnableSctpDataChannels constraint.
|
// It takes precendence over the kEnableSctpDataChannels constraint.
|
||||||
if (FindConstraint(
|
if (FindConstraint(
|
||||||
@ -471,23 +429,26 @@ bool WebRtcSession::Initialize(const MediaConstraintsInterface* constraints) {
|
|||||||
&value, NULL) && value) {
|
&value, NULL) && value) {
|
||||||
LOG(LS_INFO) << "Allowing RTP data engine.";
|
LOG(LS_INFO) << "Allowing RTP data engine.";
|
||||||
data_channel_type_ = cricket::DCT_RTP;
|
data_channel_type_ = cricket::DCT_RTP;
|
||||||
} else if (
|
} else {
|
||||||
FindConstraint(
|
bool sctp_enabled = FindConstraint(
|
||||||
constraints,
|
constraints,
|
||||||
MediaConstraintsInterface::kEnableSctpDataChannels,
|
MediaConstraintsInterface::kEnableSctpDataChannels,
|
||||||
&value, NULL) && value &&
|
&value, NULL) && value;
|
||||||
|
bool dtls_enabled = FindConstraint(
|
||||||
|
constraints,
|
||||||
|
MediaConstraintsInterface::kEnableDtlsSrtp,
|
||||||
|
&value, NULL) && value;
|
||||||
|
|
||||||
// DTLS has to be enabled to use SCTP.
|
// DTLS has to be enabled to use SCTP.
|
||||||
(transport_desc_factory_.secure() == cricket::SEC_ENABLED)) {
|
if (sctp_enabled && dtls_enabled) {
|
||||||
LOG(LS_INFO) << "Allowing SCTP data engine.";
|
LOG(LS_INFO) << "Allowing SCTP data engine.";
|
||||||
data_channel_type_ = cricket::DCT_SCTP;
|
data_channel_type_ = cricket::DCT_SCTP;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (data_channel_type_ != cricket::DCT_NONE) {
|
if (data_channel_type_ != cricket::DCT_NONE) {
|
||||||
mediastream_signaling_->SetDataChannelFactory(this);
|
mediastream_signaling_->SetDataChannelFactory(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure SessionDescriptions only contains the StreamParams we negotiate.
|
|
||||||
session_desc_factory_.set_add_legacy_streams(false);
|
|
||||||
|
|
||||||
const cricket::VideoCodec default_codec(
|
const cricket::VideoCodec default_codec(
|
||||||
JsepSessionDescription::kDefaultVideoCodecId,
|
JsepSessionDescription::kDefaultVideoCodecId,
|
||||||
JsepSessionDescription::kDefaultVideoCodecName,
|
JsepSessionDescription::kDefaultVideoCodecName,
|
||||||
@ -497,6 +458,19 @@ bool WebRtcSession::Initialize(const MediaConstraintsInterface* constraints) {
|
|||||||
JsepSessionDescription::kDefaultVideoCodecPreference);
|
JsepSessionDescription::kDefaultVideoCodecPreference);
|
||||||
channel_manager_->SetDefaultVideoEncoderConfig(
|
channel_manager_->SetDefaultVideoEncoderConfig(
|
||||||
cricket::VideoEncoderConfig(default_codec));
|
cricket::VideoEncoderConfig(default_codec));
|
||||||
|
|
||||||
|
webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
|
||||||
|
signaling_thread(),
|
||||||
|
channel_manager_,
|
||||||
|
mediastream_signaling_,
|
||||||
|
dtls_identity_service,
|
||||||
|
this,
|
||||||
|
id(),
|
||||||
|
data_channel_type_,
|
||||||
|
constraints));
|
||||||
|
|
||||||
|
webrtc_session_desc_factory_->SignalIdentityReady.connect(
|
||||||
|
this, &WebRtcSession::OnIdentityReady);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,113 +498,27 @@ bool WebRtcSession::StartCandidatesAllocation() {
|
|||||||
|
|
||||||
void WebRtcSession::set_secure_policy(
|
void WebRtcSession::set_secure_policy(
|
||||||
cricket::SecureMediaPolicy secure_policy) {
|
cricket::SecureMediaPolicy secure_policy) {
|
||||||
session_desc_factory_.set_secure(secure_policy);
|
webrtc_session_desc_factory_->set_secure(secure_policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionDescriptionInterface* WebRtcSession::CreateOffer(
|
cricket::SecureMediaPolicy WebRtcSession::secure_policy() const {
|
||||||
const MediaConstraintsInterface* constraints) {
|
return webrtc_session_desc_factory_->secure();
|
||||||
cricket::MediaSessionOptions options;
|
|
||||||
|
|
||||||
if (!mediastream_signaling_->GetOptionsForOffer(constraints, &options)) {
|
|
||||||
LOG(LS_ERROR) << "CreateOffer called with invalid constraints.";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ValidStreams(options.streams)) {
|
|
||||||
LOG(LS_ERROR) << "CreateOffer called with invalid media streams.";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data_channel_type_ == cricket::DCT_SCTP) {
|
|
||||||
options.data_channel_type = cricket::DCT_SCTP;
|
|
||||||
}
|
|
||||||
SessionDescription* desc(
|
|
||||||
session_desc_factory_.CreateOffer(options,
|
|
||||||
BaseSession::local_description()));
|
|
||||||
// RFC 3264
|
|
||||||
// When issuing an offer that modifies the session,
|
|
||||||
// the "o=" line of the new SDP MUST be identical to that in the
|
|
||||||
// previous SDP, except that the version in the origin field MUST
|
|
||||||
// increment by one from the previous SDP.
|
|
||||||
|
|
||||||
// Just increase the version number by one each time when a new offer
|
|
||||||
// is created regardless if it's identical to the previous one or not.
|
|
||||||
// The |session_version_| is a uint64, the wrap around should not happen.
|
|
||||||
ASSERT(session_version_ + 1 > session_version_);
|
|
||||||
JsepSessionDescription* offer(new JsepSessionDescription(
|
|
||||||
JsepSessionDescription::kOffer));
|
|
||||||
if (!offer->Initialize(desc, id(),
|
|
||||||
talk_base::ToString(session_version_++))) {
|
|
||||||
delete offer;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (local_description() && !options.transport_options.ice_restart) {
|
|
||||||
// Include all local ice candidates in the SessionDescription unless
|
|
||||||
// the an ice restart has been requested.
|
|
||||||
CopyCandidatesFromSessionDescription(local_description(), offer);
|
|
||||||
}
|
|
||||||
return offer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionDescriptionInterface* WebRtcSession::CreateAnswer(
|
void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer,
|
||||||
const MediaConstraintsInterface* constraints) {
|
const MediaConstraintsInterface* constraints) {
|
||||||
if (!remote_description()) {
|
webrtc_session_desc_factory_->CreateOffer(observer, constraints);
|
||||||
LOG(LS_ERROR) << "CreateAnswer can't be called before"
|
}
|
||||||
<< " SetRemoteDescription.";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (remote_description()->type() != JsepSessionDescription::kOffer) {
|
|
||||||
LOG(LS_ERROR) << "CreateAnswer failed because remote_description is not an"
|
|
||||||
<< " offer.";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
cricket::MediaSessionOptions options;
|
void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer,
|
||||||
if (!mediastream_signaling_->GetOptionsForAnswer(constraints, &options)) {
|
const MediaConstraintsInterface* constraints) {
|
||||||
LOG(LS_ERROR) << "CreateAnswer called with invalid constraints.";
|
webrtc_session_desc_factory_->CreateAnswer(observer, constraints);
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!ValidStreams(options.streams)) {
|
|
||||||
LOG(LS_ERROR) << "CreateAnswer called with invalid media streams.";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (data_channel_type_ == cricket::DCT_SCTP) {
|
|
||||||
options.data_channel_type = cricket::DCT_SCTP;
|
|
||||||
}
|
|
||||||
// According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
|
|
||||||
// an answer should also contain new ice ufrag and password if an offer has
|
|
||||||
// been received with new ufrag and password.
|
|
||||||
options.transport_options.ice_restart =
|
|
||||||
ice_restart_latch_->AnswerWithIceRestartLatch();
|
|
||||||
SessionDescription* desc(
|
|
||||||
session_desc_factory_.CreateAnswer(BaseSession::remote_description(),
|
|
||||||
options,
|
|
||||||
BaseSession::local_description()));
|
|
||||||
// RFC 3264
|
|
||||||
// If the answer is different from the offer in any way (different IP
|
|
||||||
// addresses, ports, etc.), the origin line MUST be different in the answer.
|
|
||||||
// In that case, the version number in the "o=" line of the answer is
|
|
||||||
// unrelated to the version number in the o line of the offer.
|
|
||||||
// Get a new version number by increasing the |session_version_answer_|.
|
|
||||||
// The |session_version_| is a uint64, the wrap around should not happen.
|
|
||||||
ASSERT(session_version_ + 1 > session_version_);
|
|
||||||
JsepSessionDescription* answer(new JsepSessionDescription(
|
|
||||||
JsepSessionDescription::kAnswer));
|
|
||||||
if (!answer->Initialize(desc, id(),
|
|
||||||
talk_base::ToString(session_version_++))) {
|
|
||||||
delete answer;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (local_description() && !options.transport_options.ice_restart) {
|
|
||||||
// Include all local ice candidates in the SessionDescription unless
|
|
||||||
// the remote peer has requested an ice restart.
|
|
||||||
CopyCandidatesFromSessionDescription(local_description(), answer);
|
|
||||||
}
|
|
||||||
return answer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
||||||
std::string* err_desc) {
|
std::string* err_desc) {
|
||||||
|
cricket::SecureMediaPolicy secure_policy =
|
||||||
|
webrtc_session_desc_factory_->secure();
|
||||||
// Takes the ownership of |desc| regardless of the result.
|
// Takes the ownership of |desc| regardless of the result.
|
||||||
talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
|
talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
|
||||||
|
|
||||||
@ -651,12 +539,10 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
|||||||
std::string type = desc->type();
|
std::string type = desc->type();
|
||||||
return BadLocalSdp(BadStateErrMsg(type, state()), err_desc);
|
return BadLocalSdp(BadStateErrMsg(type, state()), err_desc);
|
||||||
}
|
}
|
||||||
|
if (secure_policy == cricket::SEC_REQUIRED &&
|
||||||
if (session_desc_factory_.secure() == cricket::SEC_REQUIRED &&
|
|
||||||
!VerifyCrypto(desc->description())) {
|
!VerifyCrypto(desc->description())) {
|
||||||
return BadLocalSdp(kSdpWithoutCrypto, err_desc);
|
return BadLocalSdp(kSdpWithoutCrypto, err_desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action == kAnswer && !VerifyMediaDescriptions(
|
if (action == kAnswer && !VerifyMediaDescriptions(
|
||||||
desc->description(), remote_description()->description())) {
|
desc->description(), remote_description()->description())) {
|
||||||
return BadLocalSdp(kMlineMismatch, err_desc);
|
return BadLocalSdp(kMlineMismatch, err_desc);
|
||||||
@ -668,7 +554,7 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the MediaContentDescription crypto settings as per the policy set.
|
// Update the MediaContentDescription crypto settings as per the policy set.
|
||||||
UpdateSessionDescriptionSecurePolicy(desc->description());
|
UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description());
|
||||||
|
|
||||||
set_local_description(desc->description()->Copy());
|
set_local_description(desc->description()->Copy());
|
||||||
local_desc_.reset(desc_temp.release());
|
local_desc_.reset(desc_temp.release());
|
||||||
@ -703,6 +589,8 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
|||||||
|
|
||||||
bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
||||||
std::string* err_desc) {
|
std::string* err_desc) {
|
||||||
|
cricket::SecureMediaPolicy secure_policy =
|
||||||
|
webrtc_session_desc_factory_->secure();
|
||||||
// Takes the ownership of |desc| regardless of the result.
|
// Takes the ownership of |desc| regardless of the result.
|
||||||
talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
|
talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
|
||||||
|
|
||||||
@ -729,7 +617,7 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
|||||||
return BadRemoteSdp(kMlineMismatch, err_desc);
|
return BadRemoteSdp(kMlineMismatch, err_desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session_desc_factory_.secure() == cricket::SEC_REQUIRED &&
|
if (secure_policy == cricket::SEC_REQUIRED &&
|
||||||
!VerifyCrypto(desc->description())) {
|
!VerifyCrypto(desc->description())) {
|
||||||
return BadRemoteSdp(kSdpWithoutCrypto, err_desc);
|
return BadRemoteSdp(kSdpWithoutCrypto, err_desc);
|
||||||
}
|
}
|
||||||
@ -762,7 +650,8 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
|||||||
// Copy all saved candidates.
|
// Copy all saved candidates.
|
||||||
CopySavedCandidates(desc);
|
CopySavedCandidates(desc);
|
||||||
// We retain all received candidates.
|
// We retain all received candidates.
|
||||||
CopyCandidatesFromSessionDescription(remote_desc_.get(), desc);
|
WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
|
||||||
|
remote_desc_.get(), desc);
|
||||||
// Check if this new SessionDescription contains new ice ufrag and password
|
// Check if this new SessionDescription contains new ice ufrag and password
|
||||||
// that indicates the remote peer requests ice restart.
|
// that indicates the remote peer requests ice restart.
|
||||||
ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
|
ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
|
||||||
@ -1057,12 +946,35 @@ talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
talk_base::scoped_refptr<DataChannel> channel(
|
talk_base::scoped_refptr<DataChannel> channel(
|
||||||
DataChannel::Create(this, label, &new_config));
|
DataChannel::Create(this, label, &new_config));
|
||||||
if (channel == NULL)
|
if (channel == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (!mediastream_signaling_->AddDataChannel(channel))
|
if (!mediastream_signaling_->AddDataChannel(channel))
|
||||||
return NULL;
|
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);
|
||||||
|
channel->ConnectToDataSession();
|
||||||
|
}
|
||||||
|
if (!config->negotiated) {
|
||||||
|
talk_base::Buffer *payload = new talk_base::Buffer;
|
||||||
|
if (!mediastream_signaling_->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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1070,6 +982,22 @@ cricket::DataChannelType WebRtcSession::data_channel_type() const {
|
|||||||
return data_channel_type_;
|
return data_channel_type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WebRtcSession::IceRestartPending() const {
|
||||||
|
return ice_restart_latch_->Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSession::ResetIceRestartLatch() {
|
||||||
|
ice_restart_latch_->Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSession::OnIdentityReady(talk_base::SSLIdentity* identity) {
|
||||||
|
SetIdentity(identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebRtcSession::waiting_for_identity() const {
|
||||||
|
return webrtc_session_desc_factory_->waiting_for_identity();
|
||||||
|
}
|
||||||
|
|
||||||
void WebRtcSession::SetIceConnectionState(
|
void WebRtcSession::SetIceConnectionState(
|
||||||
PeerConnectionInterface::IceConnectionState state) {
|
PeerConnectionInterface::IceConnectionState state) {
|
||||||
if (ice_connection_state_ == state) {
|
if (ice_connection_state_ == state) {
|
||||||
@ -1402,7 +1330,12 @@ bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
|
|||||||
bool rtcp = (data_channel_type_ == cricket::DCT_RTP);
|
bool rtcp = (data_channel_type_ == cricket::DCT_RTP);
|
||||||
data_channel_.reset(channel_manager_->CreateDataChannel(
|
data_channel_.reset(channel_manager_->CreateDataChannel(
|
||||||
this, content->name, rtcp, data_channel_type_));
|
this, content->name, rtcp, data_channel_type_));
|
||||||
return (data_channel_ != NULL);
|
if (!data_channel_.get()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
data_channel_->SignalDataReceived.connect(
|
||||||
|
this, &WebRtcSession::OnDataReceived);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebRtcSession::CopySavedCandidates(
|
void WebRtcSession::CopySavedCandidates(
|
||||||
@ -1418,24 +1351,29 @@ void WebRtcSession::CopySavedCandidates(
|
|||||||
saved_candidates_.clear();
|
saved_candidates_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebRtcSession::UpdateSessionDescriptionSecurePolicy(
|
// Look for OPEN messages and set up data channels in response.
|
||||||
SessionDescription* sdesc) {
|
void WebRtcSession::OnDataReceived(
|
||||||
if (!sdesc) {
|
cricket::DataChannel* channel,
|
||||||
|
const cricket::ReceiveDataParams& params,
|
||||||
|
const talk_base::Buffer& payload) {
|
||||||
|
if (params.type != cricket::DMT_CONTROL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updating the |crypto_required_| in MediaContentDescription to the
|
std::string label;
|
||||||
// appropriate state based on the current security policy.
|
DataChannelInit config;
|
||||||
for (cricket::ContentInfos::iterator iter = sdesc->contents().begin();
|
if (!mediastream_signaling_->ParseDataChannelOpenMessage(
|
||||||
iter != sdesc->contents().end(); ++iter) {
|
payload, &label, &config)) {
|
||||||
if (cricket::IsMediaContent(&*iter)) {
|
LOG(LS_WARNING) << "Failed to parse data channel OPEN message.";
|
||||||
MediaContentDescription* mdesc =
|
return;
|
||||||
static_cast<MediaContentDescription*>(iter->description);
|
|
||||||
if (mdesc) {
|
|
||||||
mdesc->set_crypto_required(
|
|
||||||
session_desc_factory_.secure() == cricket::SEC_REQUIRED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.negotiated = true; // This is the negotiation.
|
||||||
|
|
||||||
|
if (!mediastream_signaling_->AddDataChannelFromOpenMessage(
|
||||||
|
label, config)) {
|
||||||
|
LOG(LS_WARNING) << "Failed to create data channel from OPEN message.";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@
|
|||||||
#include "talk/base/thread.h"
|
#include "talk/base/thread.h"
|
||||||
#include "talk/media/base/mediachannel.h"
|
#include "talk/media/base/mediachannel.h"
|
||||||
#include "talk/p2p/base/session.h"
|
#include "talk/p2p/base/session.h"
|
||||||
#include "talk/p2p/base/transportdescriptionfactory.h"
|
|
||||||
#include "talk/session/media/mediasession.h"
|
#include "talk/session/media/mediasession.h"
|
||||||
|
|
||||||
namespace cricket {
|
namespace cricket {
|
||||||
@ -59,6 +58,7 @@ namespace webrtc {
|
|||||||
|
|
||||||
class IceRestartAnswerLatch;
|
class IceRestartAnswerLatch;
|
||||||
class MediaStreamSignaling;
|
class MediaStreamSignaling;
|
||||||
|
class WebRtcSessionDescriptionFactory;
|
||||||
|
|
||||||
extern const char kSetLocalSdpFailed[];
|
extern const char kSetLocalSdpFailed[];
|
||||||
extern const char kSetRemoteSdpFailed[];
|
extern const char kSetRemoteSdpFailed[];
|
||||||
@ -107,7 +107,8 @@ class WebRtcSession : public cricket::BaseSession,
|
|||||||
MediaStreamSignaling* mediastream_signaling);
|
MediaStreamSignaling* mediastream_signaling);
|
||||||
virtual ~WebRtcSession();
|
virtual ~WebRtcSession();
|
||||||
|
|
||||||
bool Initialize(const MediaConstraintsInterface* constraints);
|
bool Initialize(const MediaConstraintsInterface* constraints,
|
||||||
|
DTLSIdentityServiceInterface* dtls_identity_service);
|
||||||
// Deletes the voice, video and data channel and changes the session state
|
// Deletes the voice, video and data channel and changes the session state
|
||||||
// to STATE_RECEIVEDTERMINATE.
|
// to STATE_RECEIVEDTERMINATE.
|
||||||
void Terminate();
|
void Terminate();
|
||||||
@ -127,20 +128,16 @@ class WebRtcSession : public cricket::BaseSession,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void set_secure_policy(cricket::SecureMediaPolicy secure_policy);
|
void set_secure_policy(cricket::SecureMediaPolicy secure_policy);
|
||||||
cricket::SecureMediaPolicy secure_policy() const {
|
cricket::SecureMediaPolicy secure_policy() const;
|
||||||
return session_desc_factory_.secure();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic error message callback from WebRtcSession.
|
// Generic error message callback from WebRtcSession.
|
||||||
// TODO - It may be necessary to supply error code as well.
|
// TODO - It may be necessary to supply error code as well.
|
||||||
sigslot::signal0<> SignalError;
|
sigslot::signal0<> SignalError;
|
||||||
|
|
||||||
SessionDescriptionInterface* CreateOffer(
|
void CreateOffer(CreateSessionDescriptionObserver* observer,
|
||||||
const MediaConstraintsInterface* constraints);
|
const MediaConstraintsInterface* constraints);
|
||||||
|
void CreateAnswer(CreateSessionDescriptionObserver* observer,
|
||||||
SessionDescriptionInterface* CreateAnswer(
|
|
||||||
const MediaConstraintsInterface* constraints);
|
const MediaConstraintsInterface* constraints);
|
||||||
|
|
||||||
// The ownership of |desc| will be transferred after this call.
|
// The ownership of |desc| will be transferred after this call.
|
||||||
bool SetLocalDescription(SessionDescriptionInterface* desc,
|
bool SetLocalDescription(SessionDescriptionInterface* desc,
|
||||||
std::string* err_desc);
|
std::string* err_desc);
|
||||||
@ -155,6 +152,9 @@ class WebRtcSession : public cricket::BaseSession,
|
|||||||
return remote_desc_.get();
|
return remote_desc_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_secure(cricket::SecureMediaPolicy secure_policy);
|
||||||
|
cricket::SecureMediaPolicy secure();
|
||||||
|
|
||||||
// Get the id used as a media stream track's "id" field from ssrc.
|
// Get the id used as a media stream track's "id" field from ssrc.
|
||||||
virtual bool GetTrackIdBySsrc(uint32 ssrc, std::string* id);
|
virtual bool GetTrackIdBySsrc(uint32 ssrc, std::string* id);
|
||||||
|
|
||||||
@ -186,6 +186,17 @@ class WebRtcSession : public cricket::BaseSession,
|
|||||||
|
|
||||||
cricket::DataChannelType data_channel_type() const;
|
cricket::DataChannelType data_channel_type() const;
|
||||||
|
|
||||||
|
bool IceRestartPending() const;
|
||||||
|
|
||||||
|
void ResetIceRestartLatch();
|
||||||
|
|
||||||
|
// Called when an SSLIdentity is generated or retrieved by
|
||||||
|
// WebRTCSessionDescriptionFactory. Should happen before setLocalDescription.
|
||||||
|
void OnIdentityReady(talk_base::SSLIdentity* identity);
|
||||||
|
|
||||||
|
// For unit test.
|
||||||
|
bool waiting_for_identity() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Indicates the type of SessionDescription in a call to SetLocalDescription
|
// Indicates the type of SessionDescription in a call to SetLocalDescription
|
||||||
// and SetRemoteDescription.
|
// and SetRemoteDescription.
|
||||||
@ -194,6 +205,7 @@ class WebRtcSession : public cricket::BaseSession,
|
|||||||
kPrAnswer,
|
kPrAnswer,
|
||||||
kAnswer,
|
kAnswer,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Invokes ConnectChannels() on transport proxies, which initiates ice
|
// Invokes ConnectChannels() on transport proxies, which initiates ice
|
||||||
// candidates allocation.
|
// candidates allocation.
|
||||||
bool StartCandidatesAllocation();
|
bool StartCandidatesAllocation();
|
||||||
@ -252,11 +264,10 @@ class WebRtcSession : public cricket::BaseSession,
|
|||||||
// The |saved_candidates_| will be cleared after this function call.
|
// The |saved_candidates_| will be cleared after this function call.
|
||||||
void CopySavedCandidates(SessionDescriptionInterface* dest_desc);
|
void CopySavedCandidates(SessionDescriptionInterface* dest_desc);
|
||||||
|
|
||||||
// Forces |desc->crypto_required| to the appropriate state based on the
|
void OnDataReceived(
|
||||||
// current security policy, to ensure a failure occurs if there is an error
|
cricket::DataChannel* channel,
|
||||||
// in crypto negotiation.
|
const cricket::ReceiveDataParams& params,
|
||||||
// Called when processing the local session description.
|
const talk_base::Buffer& payload);
|
||||||
void UpdateSessionDescriptionSecurePolicy(cricket::SessionDescription* desc);
|
|
||||||
|
|
||||||
bool GetLocalTrackId(uint32 ssrc, std::string* track_id);
|
bool GetLocalTrackId(uint32 ssrc, std::string* track_id);
|
||||||
bool GetRemoteTrackId(uint32 ssrc, std::string* track_id);
|
bool GetRemoteTrackId(uint32 ssrc, std::string* track_id);
|
||||||
@ -271,8 +282,6 @@ class WebRtcSession : public cricket::BaseSession,
|
|||||||
talk_base::scoped_ptr<cricket::VideoChannel> video_channel_;
|
talk_base::scoped_ptr<cricket::VideoChannel> video_channel_;
|
||||||
talk_base::scoped_ptr<cricket::DataChannel> data_channel_;
|
talk_base::scoped_ptr<cricket::DataChannel> data_channel_;
|
||||||
cricket::ChannelManager* channel_manager_;
|
cricket::ChannelManager* channel_manager_;
|
||||||
cricket::TransportDescriptionFactory transport_desc_factory_;
|
|
||||||
cricket::MediaSessionDescriptionFactory session_desc_factory_;
|
|
||||||
MediaStreamSignaling* mediastream_signaling_;
|
MediaStreamSignaling* mediastream_signaling_;
|
||||||
IceObserver* ice_observer_;
|
IceObserver* ice_observer_;
|
||||||
PeerConnectionInterface::IceConnectionState ice_connection_state_;
|
PeerConnectionInterface::IceConnectionState ice_connection_state_;
|
||||||
@ -280,7 +289,6 @@ class WebRtcSession : public cricket::BaseSession,
|
|||||||
talk_base::scoped_ptr<SessionDescriptionInterface> remote_desc_;
|
talk_base::scoped_ptr<SessionDescriptionInterface> remote_desc_;
|
||||||
// Candidates that arrived before the remote description was set.
|
// Candidates that arrived before the remote description was set.
|
||||||
std::vector<IceCandidateInterface*> saved_candidates_;
|
std::vector<IceCandidateInterface*> saved_candidates_;
|
||||||
uint64 session_version_;
|
|
||||||
// If the remote peer is using a older version of implementation.
|
// If the remote peer is using a older version of implementation.
|
||||||
bool older_version_remote_peer_;
|
bool older_version_remote_peer_;
|
||||||
// Specifies which kind of data channel is allowed. This is controlled
|
// Specifies which kind of data channel is allowed. This is controlled
|
||||||
@ -292,6 +300,10 @@ class WebRtcSession : public cricket::BaseSession,
|
|||||||
// 3. If both 1&2 are false, data channel is not allowed (DCT_NONE).
|
// 3. If both 1&2 are false, data channel is not allowed (DCT_NONE).
|
||||||
cricket::DataChannelType data_channel_type_;
|
cricket::DataChannelType data_channel_type_;
|
||||||
talk_base::scoped_ptr<IceRestartAnswerLatch> ice_restart_latch_;
|
talk_base::scoped_ptr<IceRestartAnswerLatch> ice_restart_latch_;
|
||||||
|
|
||||||
|
talk_base::scoped_ptr<WebRtcSessionDescriptionFactory>
|
||||||
|
webrtc_session_desc_factory_;
|
||||||
|
|
||||||
sigslot::signal0<> SignalVoiceChannelDestroyed;
|
sigslot::signal0<> SignalVoiceChannelDestroyed;
|
||||||
sigslot::signal0<> SignalVideoChannelDestroyed;
|
sigslot::signal0<> SignalVideoChannelDestroyed;
|
||||||
sigslot::signal0<> SignalDataChannelDestroyed;
|
sigslot::signal0<> SignalDataChannelDestroyed;
|
||||||
|
File diff suppressed because it is too large
Load Diff
451
talk/app/webrtc/webrtcsessiondescriptionfactory.cc
Normal file
451
talk/app/webrtc/webrtcsessiondescriptionfactory.cc
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
/*
|
||||||
|
* 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/webrtcsessiondescriptionfactory.h"
|
||||||
|
|
||||||
|
#include "talk/app/webrtc/jsep.h"
|
||||||
|
#include "talk/app/webrtc/jsepsessiondescription.h"
|
||||||
|
#include "talk/app/webrtc/mediaconstraintsinterface.h"
|
||||||
|
#include "talk/app/webrtc/mediastreamsignaling.h"
|
||||||
|
#include "talk/app/webrtc/webrtcsession.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static const char kFailedDueToIdentityFailed[] =
|
||||||
|
" failed because DTLS identity request failed";
|
||||||
|
|
||||||
|
// Arbitrary constant used as common name for the identity.
|
||||||
|
// Chosen to make the certificates more readable.
|
||||||
|
static const char kWebRTCIdentityName[] = "WebRTC";
|
||||||
|
|
||||||
|
static const uint64 kInitSessionVersion = 2;
|
||||||
|
|
||||||
|
typedef cricket::MediaSessionOptions::Stream Stream;
|
||||||
|
typedef cricket::MediaSessionOptions::Streams Streams;
|
||||||
|
|
||||||
|
static bool CompareStream(const Stream& stream1, const Stream& stream2) {
|
||||||
|
return (stream1.id < stream2.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool SameId(const Stream& stream1, const Stream& stream2) {
|
||||||
|
return (stream1.id == stream2.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if each Stream within the |streams| has unique id.
|
||||||
|
static bool ValidStreams(const Streams& streams) {
|
||||||
|
Streams sorted_streams = streams;
|
||||||
|
std::sort(sorted_streams.begin(), sorted_streams.end(), CompareStream);
|
||||||
|
Streams::iterator it =
|
||||||
|
std::adjacent_find(sorted_streams.begin(), sorted_streams.end(),
|
||||||
|
SameId);
|
||||||
|
return (it == sorted_streams.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MSG_CREATE_SESSIONDESCRIPTION_SUCCESS,
|
||||||
|
MSG_CREATE_SESSIONDESCRIPTION_FAILED,
|
||||||
|
MSG_GENERATE_IDENTITY,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CreateSessionDescriptionMsg : public talk_base::MessageData {
|
||||||
|
explicit CreateSessionDescriptionMsg(
|
||||||
|
webrtc::CreateSessionDescriptionObserver* observer)
|
||||||
|
: observer(observer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
talk_base::scoped_refptr<webrtc::CreateSessionDescriptionObserver> observer;
|
||||||
|
std::string error;
|
||||||
|
talk_base::scoped_ptr<webrtc::SessionDescriptionInterface> description;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// static
|
||||||
|
void WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
|
||||||
|
const SessionDescriptionInterface* source_desc,
|
||||||
|
SessionDescriptionInterface* dest_desc) {
|
||||||
|
if (!source_desc)
|
||||||
|
return;
|
||||||
|
for (size_t m = 0; m < source_desc->number_of_mediasections() &&
|
||||||
|
m < dest_desc->number_of_mediasections(); ++m) {
|
||||||
|
const IceCandidateCollection* source_candidates =
|
||||||
|
source_desc->candidates(m);
|
||||||
|
const IceCandidateCollection* dest_candidates = dest_desc->candidates(m);
|
||||||
|
for (size_t n = 0; n < source_candidates->count(); ++n) {
|
||||||
|
const IceCandidateInterface* new_candidate = source_candidates->at(n);
|
||||||
|
if (!dest_candidates->HasCandidate(new_candidate))
|
||||||
|
dest_desc->AddCandidate(source_candidates->at(n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
|
||||||
|
talk_base::Thread* signaling_thread,
|
||||||
|
cricket::ChannelManager* channel_manager,
|
||||||
|
MediaStreamSignaling* mediastream_signaling,
|
||||||
|
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||||
|
WebRtcSession* session,
|
||||||
|
const std::string& session_id,
|
||||||
|
cricket::DataChannelType dct,
|
||||||
|
const MediaConstraintsInterface* constraints)
|
||||||
|
: signaling_thread_(signaling_thread),
|
||||||
|
mediastream_signaling_(mediastream_signaling),
|
||||||
|
session_desc_factory_(channel_manager, &transport_desc_factory_),
|
||||||
|
// RFC 4566 suggested a Network Time Protocol (NTP) format timestamp
|
||||||
|
// as the session id and session version. To simplify, it should be fine
|
||||||
|
// to just use a random number as session id and start version from
|
||||||
|
// |kInitSessionVersion|.
|
||||||
|
session_version_(kInitSessionVersion),
|
||||||
|
identity_service_(dtls_identity_service),
|
||||||
|
session_(session),
|
||||||
|
session_id_(session_id),
|
||||||
|
data_channel_type_(dct),
|
||||||
|
identity_request_state_(IDENTITY_NOT_NEEDED) {
|
||||||
|
transport_desc_factory_.set_protocol(cricket::ICEPROTO_HYBRID);
|
||||||
|
session_desc_factory_.set_add_legacy_streams(false);
|
||||||
|
// By default SRTP-SDES is enabled in WebRtc.
|
||||||
|
set_secure(cricket::SEC_REQUIRED);
|
||||||
|
|
||||||
|
// Enable DTLS-SRTP if the constraint is set.
|
||||||
|
bool dtls_enabled = false;
|
||||||
|
if (!FindConstraint(
|
||||||
|
constraints, MediaConstraintsInterface::kEnableDtlsSrtp,
|
||||||
|
&dtls_enabled, NULL) ||
|
||||||
|
!dtls_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// DTLS is enabled.
|
||||||
|
if (identity_service_.get()) {
|
||||||
|
identity_request_observer_ =
|
||||||
|
new talk_base::RefCountedObject<WebRtcIdentityRequestObserver>();
|
||||||
|
|
||||||
|
identity_request_observer_->SignalRequestFailed.connect(
|
||||||
|
this, &WebRtcSessionDescriptionFactory::OnIdentityRequestFailed);
|
||||||
|
identity_request_observer_->SignalIdentityReady.connect(
|
||||||
|
this, &WebRtcSessionDescriptionFactory::OnIdentityReady);
|
||||||
|
|
||||||
|
if (identity_service_->RequestIdentity(kWebRTCIdentityName,
|
||||||
|
kWebRTCIdentityName,
|
||||||
|
identity_request_observer_)) {
|
||||||
|
LOG(LS_VERBOSE) << "DTLS-SRTP enabled; sent DTLS identity request.";
|
||||||
|
identity_request_state_ = IDENTITY_WAITING;
|
||||||
|
} else {
|
||||||
|
LOG(LS_ERROR) << "Failed to send DTLS identity request.";
|
||||||
|
identity_request_state_ = IDENTITY_FAILED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
identity_request_state_ = IDENTITY_WAITING;
|
||||||
|
// Do not generate the identity in the constructor since the caller has
|
||||||
|
// not got a chance to connect to SignalIdentityReady.
|
||||||
|
signaling_thread_->Post(this, MSG_GENERATE_IDENTITY, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() {
|
||||||
|
transport_desc_factory_.set_identity(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSessionDescriptionFactory::CreateOffer(
|
||||||
|
CreateSessionDescriptionObserver* observer,
|
||||||
|
const MediaConstraintsInterface* constraints) {
|
||||||
|
cricket::MediaSessionOptions options;
|
||||||
|
std::string error = "CreateOffer";
|
||||||
|
if (identity_request_state_ == IDENTITY_FAILED) {
|
||||||
|
error += kFailedDueToIdentityFailed;
|
||||||
|
LOG(LS_ERROR) << error;
|
||||||
|
PostCreateSessionDescriptionFailed(observer, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mediastream_signaling_->GetOptionsForOffer(constraints, &options)) {
|
||||||
|
error += " called with invalid constraints.";
|
||||||
|
LOG(LS_ERROR) << error;
|
||||||
|
PostCreateSessionDescriptionFailed(observer, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidStreams(options.streams)) {
|
||||||
|
error += " called with invalid media streams.";
|
||||||
|
LOG(LS_ERROR) << error;
|
||||||
|
PostCreateSessionDescriptionFailed(observer, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_channel_type_ == cricket::DCT_SCTP) {
|
||||||
|
options.data_channel_type = cricket::DCT_SCTP;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateSessionDescriptionRequest request(
|
||||||
|
CreateSessionDescriptionRequest::kOffer, observer, options);
|
||||||
|
if (identity_request_state_ == IDENTITY_WAITING) {
|
||||||
|
create_session_description_requests_.push(request);
|
||||||
|
} else {
|
||||||
|
ASSERT(identity_request_state_ == IDENTITY_SUCCEEDED ||
|
||||||
|
identity_request_state_ == IDENTITY_NOT_NEEDED);
|
||||||
|
InternalCreateOffer(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSessionDescriptionFactory::CreateAnswer(
|
||||||
|
CreateSessionDescriptionObserver* observer,
|
||||||
|
const MediaConstraintsInterface* constraints) {
|
||||||
|
std::string error = "CreateAnswer";
|
||||||
|
if (identity_request_state_ == IDENTITY_FAILED) {
|
||||||
|
error += kFailedDueToIdentityFailed;
|
||||||
|
LOG(LS_ERROR) << error;
|
||||||
|
PostCreateSessionDescriptionFailed(observer, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!session_->remote_description()) {
|
||||||
|
error += " can't be called before SetRemoteDescription.";
|
||||||
|
LOG(LS_ERROR) << error;
|
||||||
|
PostCreateSessionDescriptionFailed(observer, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (session_->remote_description()->type() !=
|
||||||
|
JsepSessionDescription::kOffer) {
|
||||||
|
error += " failed because remote_description is not an offer.";
|
||||||
|
LOG(LS_ERROR) << error;
|
||||||
|
PostCreateSessionDescriptionFailed(observer, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cricket::MediaSessionOptions options;
|
||||||
|
if (!mediastream_signaling_->GetOptionsForAnswer(constraints, &options)) {
|
||||||
|
error += " called with invalid constraints.";
|
||||||
|
LOG(LS_ERROR) << error;
|
||||||
|
PostCreateSessionDescriptionFailed(observer, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ValidStreams(options.streams)) {
|
||||||
|
error += " called with invalid media streams.";
|
||||||
|
LOG(LS_ERROR) << error;
|
||||||
|
PostCreateSessionDescriptionFailed(observer, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data_channel_type_ == cricket::DCT_SCTP) {
|
||||||
|
options.data_channel_type = cricket::DCT_SCTP;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateSessionDescriptionRequest request(
|
||||||
|
CreateSessionDescriptionRequest::kAnswer, observer, options);
|
||||||
|
if (identity_request_state_ == IDENTITY_WAITING) {
|
||||||
|
create_session_description_requests_.push(request);
|
||||||
|
} else {
|
||||||
|
ASSERT(identity_request_state_ == IDENTITY_SUCCEEDED ||
|
||||||
|
identity_request_state_ == IDENTITY_NOT_NEEDED);
|
||||||
|
InternalCreateAnswer(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSessionDescriptionFactory::set_secure(
|
||||||
|
cricket::SecureMediaPolicy secure_policy) {
|
||||||
|
session_desc_factory_.set_secure(secure_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
cricket::SecureMediaPolicy WebRtcSessionDescriptionFactory::secure() const {
|
||||||
|
return session_desc_factory_.secure();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebRtcSessionDescriptionFactory::waiting_for_identity() const {
|
||||||
|
return identity_request_state_ == IDENTITY_WAITING;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSessionDescriptionFactory::OnMessage(talk_base::Message* msg) {
|
||||||
|
switch (msg->message_id) {
|
||||||
|
case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: {
|
||||||
|
CreateSessionDescriptionMsg* param =
|
||||||
|
static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
|
||||||
|
param->observer->OnSuccess(param->description.release());
|
||||||
|
delete param;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MSG_CREATE_SESSIONDESCRIPTION_FAILED: {
|
||||||
|
CreateSessionDescriptionMsg* param =
|
||||||
|
static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
|
||||||
|
param->observer->OnFailure(param->error);
|
||||||
|
delete param;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MSG_GENERATE_IDENTITY: {
|
||||||
|
LOG(LS_INFO) << "Generating identity.";
|
||||||
|
SetIdentity(talk_base::SSLIdentity::Generate(kWebRTCIdentityName));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSessionDescriptionFactory::InternalCreateOffer(
|
||||||
|
CreateSessionDescriptionRequest request) {
|
||||||
|
cricket::SessionDescription* desc(
|
||||||
|
session_desc_factory_.CreateOffer(
|
||||||
|
request.options,
|
||||||
|
static_cast<cricket::BaseSession*>(session_)->local_description()));
|
||||||
|
// RFC 3264
|
||||||
|
// When issuing an offer that modifies the session,
|
||||||
|
// the "o=" line of the new SDP MUST be identical to that in the
|
||||||
|
// previous SDP, except that the version in the origin field MUST
|
||||||
|
// increment by one from the previous SDP.
|
||||||
|
|
||||||
|
// Just increase the version number by one each time when a new offer
|
||||||
|
// is created regardless if it's identical to the previous one or not.
|
||||||
|
// The |session_version_| is a uint64, the wrap around should not happen.
|
||||||
|
ASSERT(session_version_ + 1 > session_version_);
|
||||||
|
JsepSessionDescription* offer(new JsepSessionDescription(
|
||||||
|
JsepSessionDescription::kOffer));
|
||||||
|
if (!offer->Initialize(desc, session_id_,
|
||||||
|
talk_base::ToString(session_version_++))) {
|
||||||
|
delete offer;
|
||||||
|
PostCreateSessionDescriptionFailed(request.observer, "CreateOffer failed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (session_->local_description() &&
|
||||||
|
!request.options.transport_options.ice_restart) {
|
||||||
|
// Include all local ice candidates in the SessionDescription unless
|
||||||
|
// the an ice restart has been requested.
|
||||||
|
CopyCandidatesFromSessionDescription(session_->local_description(), offer);
|
||||||
|
}
|
||||||
|
PostCreateSessionDescriptionSucceeded(request.observer, offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
|
||||||
|
CreateSessionDescriptionRequest request) {
|
||||||
|
// According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
|
||||||
|
// an answer should also contain new ice ufrag and password if an offer has
|
||||||
|
// been received with new ufrag and password.
|
||||||
|
request.options.transport_options.ice_restart = session_->IceRestartPending();
|
||||||
|
|
||||||
|
cricket::SessionDescription* desc(session_desc_factory_.CreateAnswer(
|
||||||
|
static_cast<cricket::BaseSession*>(session_)->remote_description(),
|
||||||
|
request.options,
|
||||||
|
static_cast<cricket::BaseSession*>(session_)->local_description()));
|
||||||
|
// RFC 3264
|
||||||
|
// If the answer is different from the offer in any way (different IP
|
||||||
|
// addresses, ports, etc.), the origin line MUST be different in the answer.
|
||||||
|
// In that case, the version number in the "o=" line of the answer is
|
||||||
|
// unrelated to the version number in the o line of the offer.
|
||||||
|
// Get a new version number by increasing the |session_version_answer_|.
|
||||||
|
// The |session_version_| is a uint64, the wrap around should not happen.
|
||||||
|
ASSERT(session_version_ + 1 > session_version_);
|
||||||
|
JsepSessionDescription* answer(new JsepSessionDescription(
|
||||||
|
JsepSessionDescription::kAnswer));
|
||||||
|
if (!answer->Initialize(desc, session_id_,
|
||||||
|
talk_base::ToString(session_version_++))) {
|
||||||
|
delete answer;
|
||||||
|
PostCreateSessionDescriptionFailed(request.observer,
|
||||||
|
"CreateAnswer failed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (session_->local_description() &&
|
||||||
|
!request.options.transport_options.ice_restart) {
|
||||||
|
// Include all local ice candidates in the SessionDescription unless
|
||||||
|
// the remote peer has requested an ice restart.
|
||||||
|
CopyCandidatesFromSessionDescription(session_->local_description(), answer);
|
||||||
|
}
|
||||||
|
session_->ResetIceRestartLatch();
|
||||||
|
PostCreateSessionDescriptionSucceeded(request.observer, answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionFailed(
|
||||||
|
CreateSessionDescriptionObserver* observer, const std::string& error) {
|
||||||
|
CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
|
||||||
|
msg->error = error;
|
||||||
|
signaling_thread_->Post(this, MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded(
|
||||||
|
CreateSessionDescriptionObserver* observer,
|
||||||
|
SessionDescriptionInterface* description) {
|
||||||
|
CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
|
||||||
|
msg->description.reset(description);
|
||||||
|
signaling_thread_->Post(this, MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSessionDescriptionFactory::OnIdentityRequestFailed(int error) {
|
||||||
|
ASSERT(signaling_thread_->IsCurrent());
|
||||||
|
|
||||||
|
LOG(LS_ERROR) << "Async identity request failed: error = " << error;
|
||||||
|
identity_request_state_ = IDENTITY_FAILED;
|
||||||
|
|
||||||
|
std::string msg = kFailedDueToIdentityFailed;
|
||||||
|
while (!create_session_description_requests_.empty()) {
|
||||||
|
const CreateSessionDescriptionRequest& request =
|
||||||
|
create_session_description_requests_.front();
|
||||||
|
PostCreateSessionDescriptionFailed(
|
||||||
|
request.observer,
|
||||||
|
((request.type == CreateSessionDescriptionRequest::kOffer) ?
|
||||||
|
"CreateOffer" : "CreateAnswer") + msg);
|
||||||
|
create_session_description_requests_.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSessionDescriptionFactory::OnIdentityReady(
|
||||||
|
const std::string& der_cert,
|
||||||
|
const std::string& der_private_key) {
|
||||||
|
ASSERT(signaling_thread_->IsCurrent());
|
||||||
|
LOG(LS_VERBOSE) << "Identity is successfully generated.";
|
||||||
|
|
||||||
|
std::string pem_cert = talk_base::SSLIdentity::DerToPem(
|
||||||
|
talk_base::kPemTypeCertificate,
|
||||||
|
reinterpret_cast<const unsigned char*>(der_cert.data()),
|
||||||
|
der_cert.length());
|
||||||
|
std::string pem_key = talk_base::SSLIdentity::DerToPem(
|
||||||
|
talk_base::kPemTypeRsaPrivateKey,
|
||||||
|
reinterpret_cast<const unsigned char*>(der_private_key.data()),
|
||||||
|
der_private_key.length());
|
||||||
|
|
||||||
|
talk_base::SSLIdentity* identity =
|
||||||
|
talk_base::SSLIdentity::FromPEMStrings(pem_key, pem_cert);
|
||||||
|
SetIdentity(identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcSessionDescriptionFactory::SetIdentity(
|
||||||
|
talk_base::SSLIdentity* identity) {
|
||||||
|
identity_request_state_ = IDENTITY_SUCCEEDED;
|
||||||
|
SignalIdentityReady(identity);
|
||||||
|
|
||||||
|
transport_desc_factory_.set_identity(identity);
|
||||||
|
transport_desc_factory_.set_digest_algorithm(talk_base::DIGEST_SHA_256);
|
||||||
|
transport_desc_factory_.set_secure(cricket::SEC_ENABLED);
|
||||||
|
|
||||||
|
while (!create_session_description_requests_.empty()) {
|
||||||
|
if (create_session_description_requests_.front().type ==
|
||||||
|
CreateSessionDescriptionRequest::kOffer) {
|
||||||
|
InternalCreateOffer(create_session_description_requests_.front());
|
||||||
|
} else {
|
||||||
|
InternalCreateAnswer(create_session_description_requests_.front());
|
||||||
|
}
|
||||||
|
create_session_description_requests_.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
172
talk/app/webrtc/webrtcsessiondescriptionfactory.h
Normal file
172
talk/app/webrtc/webrtcsessiondescriptionfactory.h
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TALK_APP_WEBRTC_WEBRTCSESSIONDESCRIPTIONFACTORY_H_
|
||||||
|
#define TALK_APP_WEBRTC_WEBRTCSESSIONDESCRIPTIONFACTORY_H_
|
||||||
|
|
||||||
|
#include "talk/app/webrtc/peerconnectioninterface.h"
|
||||||
|
#include "talk/base/messagehandler.h"
|
||||||
|
#include "talk/p2p/base/transportdescriptionfactory.h"
|
||||||
|
#include "talk/session/media/mediasession.h"
|
||||||
|
|
||||||
|
namespace cricket {
|
||||||
|
|
||||||
|
class ChannelManager;
|
||||||
|
class TransportDescriptionFactory;
|
||||||
|
|
||||||
|
} // namespace cricket
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class CreateSessionDescriptionObserver;
|
||||||
|
class MediaConstraintsInterface;
|
||||||
|
class MediaStreamSignaling;
|
||||||
|
class SessionDescriptionInterface;
|
||||||
|
class WebRtcSession;
|
||||||
|
|
||||||
|
|
||||||
|
// DTLS identity request callback class.
|
||||||
|
class WebRtcIdentityRequestObserver : public DTLSIdentityRequestObserver,
|
||||||
|
public sigslot::has_slots<> {
|
||||||
|
public:
|
||||||
|
// DTLSIdentityRequestObserver overrides.
|
||||||
|
virtual void OnFailure(int error) {
|
||||||
|
SignalRequestFailed(error);
|
||||||
|
}
|
||||||
|
virtual void OnSuccess(const std::string& der_cert,
|
||||||
|
const std::string& der_private_key) {
|
||||||
|
SignalIdentityReady(der_cert, der_private_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
sigslot::signal1<int> SignalRequestFailed;
|
||||||
|
sigslot::signal2<const std::string&, const std::string&> SignalIdentityReady;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CreateSessionDescriptionRequest {
|
||||||
|
enum Type {
|
||||||
|
kOffer,
|
||||||
|
kAnswer,
|
||||||
|
};
|
||||||
|
|
||||||
|
CreateSessionDescriptionRequest(
|
||||||
|
Type type,
|
||||||
|
CreateSessionDescriptionObserver* observer,
|
||||||
|
const cricket::MediaSessionOptions& options)
|
||||||
|
: type(type),
|
||||||
|
observer(observer),
|
||||||
|
options(options) {}
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
talk_base::scoped_refptr<CreateSessionDescriptionObserver> observer;
|
||||||
|
cricket::MediaSessionOptions options;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This class is used to create offer/answer session description with regards to
|
||||||
|
// the async DTLS identity generation for WebRtcSession.
|
||||||
|
// It queues the create offer/answer request until the DTLS identity
|
||||||
|
// request has completed, i.e. when OnIdentityRequestFailed or OnIdentityReady
|
||||||
|
// is called.
|
||||||
|
class WebRtcSessionDescriptionFactory : public talk_base::MessageHandler,
|
||||||
|
public sigslot::has_slots<> {
|
||||||
|
public:
|
||||||
|
WebRtcSessionDescriptionFactory(
|
||||||
|
talk_base::Thread* signaling_thread,
|
||||||
|
cricket::ChannelManager* channel_manager,
|
||||||
|
MediaStreamSignaling* mediastream_signaling,
|
||||||
|
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||||
|
// TODO(jiayl): remove the dependency on session once b/10226852 is fixed.
|
||||||
|
WebRtcSession* session,
|
||||||
|
const std::string& session_id,
|
||||||
|
cricket::DataChannelType dct,
|
||||||
|
const MediaConstraintsInterface* constraints);
|
||||||
|
virtual ~WebRtcSessionDescriptionFactory();
|
||||||
|
|
||||||
|
static void CopyCandidatesFromSessionDescription(
|
||||||
|
const SessionDescriptionInterface* source_desc,
|
||||||
|
SessionDescriptionInterface* dest_desc);
|
||||||
|
|
||||||
|
void CreateOffer(
|
||||||
|
CreateSessionDescriptionObserver* observer,
|
||||||
|
const MediaConstraintsInterface* constraints);
|
||||||
|
void CreateAnswer(
|
||||||
|
CreateSessionDescriptionObserver* observer,
|
||||||
|
const MediaConstraintsInterface* constraints);
|
||||||
|
|
||||||
|
void set_secure(cricket::SecureMediaPolicy secure_policy);
|
||||||
|
cricket::SecureMediaPolicy secure() const;
|
||||||
|
|
||||||
|
sigslot::signal1<talk_base::SSLIdentity*> SignalIdentityReady;
|
||||||
|
|
||||||
|
// For testing.
|
||||||
|
bool waiting_for_identity() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum IdentityRequestState {
|
||||||
|
IDENTITY_NOT_NEEDED,
|
||||||
|
IDENTITY_WAITING,
|
||||||
|
IDENTITY_SUCCEEDED,
|
||||||
|
IDENTITY_FAILED,
|
||||||
|
};
|
||||||
|
|
||||||
|
// MessageHandler implementation.
|
||||||
|
virtual void OnMessage(talk_base::Message* msg);
|
||||||
|
|
||||||
|
void InternalCreateOffer(CreateSessionDescriptionRequest request);
|
||||||
|
void InternalCreateAnswer(CreateSessionDescriptionRequest request);
|
||||||
|
void PostCreateSessionDescriptionFailed(
|
||||||
|
CreateSessionDescriptionObserver* observer,
|
||||||
|
const std::string& error);
|
||||||
|
void PostCreateSessionDescriptionSucceeded(
|
||||||
|
CreateSessionDescriptionObserver* observer,
|
||||||
|
SessionDescriptionInterface* description);
|
||||||
|
|
||||||
|
void OnIdentityRequestFailed(int error);
|
||||||
|
void OnIdentityReady(const std::string& der_cert,
|
||||||
|
const std::string& der_private_key);
|
||||||
|
void SetIdentity(talk_base::SSLIdentity* identity);
|
||||||
|
|
||||||
|
std::queue<CreateSessionDescriptionRequest>
|
||||||
|
create_session_description_requests_;
|
||||||
|
talk_base::Thread* signaling_thread_;
|
||||||
|
MediaStreamSignaling* mediastream_signaling_;
|
||||||
|
cricket::TransportDescriptionFactory transport_desc_factory_;
|
||||||
|
cricket::MediaSessionDescriptionFactory session_desc_factory_;
|
||||||
|
uint64 session_version_;
|
||||||
|
talk_base::scoped_ptr<DTLSIdentityServiceInterface> identity_service_;
|
||||||
|
talk_base::scoped_refptr<WebRtcIdentityRequestObserver>
|
||||||
|
identity_request_observer_;
|
||||||
|
WebRtcSession* session_;
|
||||||
|
std::string session_id_;
|
||||||
|
cricket::DataChannelType data_channel_type_;
|
||||||
|
IdentityRequestState identity_request_state_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(WebRtcSessionDescriptionFactory);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // TALK_APP_WEBRTC_WEBRTCSESSIONDESCRIPTIONFACTORY_H_
|
@ -43,55 +43,12 @@
|
|||||||
#include "pk11pub.h"
|
#include "pk11pub.h"
|
||||||
#include "sechash.h"
|
#include "sechash.h"
|
||||||
|
|
||||||
#include "talk/base/base64.h"
|
|
||||||
#include "talk/base/logging.h"
|
#include "talk/base/logging.h"
|
||||||
#include "talk/base/helpers.h"
|
#include "talk/base/helpers.h"
|
||||||
#include "talk/base/nssstreamadapter.h"
|
#include "talk/base/nssstreamadapter.h"
|
||||||
|
|
||||||
namespace talk_base {
|
namespace talk_base {
|
||||||
|
|
||||||
// Helper function to parse PEM-encoded DER.
|
|
||||||
static bool PemToDer(const std::string& pem_type,
|
|
||||||
const std::string& pem_string,
|
|
||||||
std::string* der) {
|
|
||||||
// Find the inner body. We need this to fulfill the contract of
|
|
||||||
// returning pem_length.
|
|
||||||
size_t header = pem_string.find("-----BEGIN " + pem_type + "-----");
|
|
||||||
if (header == std::string::npos)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
size_t body = pem_string.find("\n", header);
|
|
||||||
if (body == std::string::npos)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
size_t trailer = pem_string.find("-----END " + pem_type + "-----");
|
|
||||||
if (trailer == std::string::npos)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string inner = pem_string.substr(body + 1, trailer - (body + 1));
|
|
||||||
|
|
||||||
*der = Base64::Decode(inner, Base64::DO_PARSE_WHITE |
|
|
||||||
Base64::DO_PAD_ANY |
|
|
||||||
Base64::DO_TERM_BUFFER);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string DerToPem(const std::string& pem_type,
|
|
||||||
const unsigned char *data,
|
|
||||||
size_t length) {
|
|
||||||
std::stringstream result;
|
|
||||||
|
|
||||||
result << "-----BEGIN " << pem_type << "-----\n";
|
|
||||||
|
|
||||||
std::string tmp;
|
|
||||||
Base64::EncodeFromArray(data, length, &tmp);
|
|
||||||
result << tmp;
|
|
||||||
|
|
||||||
result << "-----END " << pem_type << "-----\n";
|
|
||||||
|
|
||||||
return result.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
NSSKeyPair::~NSSKeyPair() {
|
NSSKeyPair::~NSSKeyPair() {
|
||||||
if (privkey_)
|
if (privkey_)
|
||||||
SECKEY_DestroyPrivateKey(privkey_);
|
SECKEY_DestroyPrivateKey(privkey_);
|
||||||
@ -135,7 +92,7 @@ NSSKeyPair *NSSKeyPair::GetReference() {
|
|||||||
|
|
||||||
NSSCertificate *NSSCertificate::FromPEMString(const std::string &pem_string) {
|
NSSCertificate *NSSCertificate::FromPEMString(const std::string &pem_string) {
|
||||||
std::string der;
|
std::string der;
|
||||||
if (!PemToDer("CERTIFICATE", pem_string, &der))
|
if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
SECItem der_cert;
|
SECItem der_cert;
|
||||||
@ -160,7 +117,7 @@ NSSCertificate *NSSCertificate::GetReference() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string NSSCertificate::ToPEMString() const {
|
std::string NSSCertificate::ToPEMString() const {
|
||||||
return DerToPem("CERTIFICATE",
|
return SSLIdentity::DerToPem(kPemTypeCertificate,
|
||||||
certificate_->derCert.data,
|
certificate_->derCert.data,
|
||||||
certificate_->derCert.len);
|
certificate_->derCert.len);
|
||||||
}
|
}
|
||||||
@ -357,8 +314,8 @@ NSSIdentity *NSSIdentity::Generate(const std::string &common_name) {
|
|||||||
SSLIdentity* NSSIdentity::FromPEMStrings(const std::string& private_key,
|
SSLIdentity* NSSIdentity::FromPEMStrings(const std::string& private_key,
|
||||||
const std::string& certificate) {
|
const std::string& certificate) {
|
||||||
std::string private_key_der;
|
std::string private_key_der;
|
||||||
if (!PemToDer(
|
if (!SSLIdentity::PemToDer(
|
||||||
"RSA PRIVATE KEY", private_key, &private_key_der))
|
kPemTypeRsaPrivateKey, private_key, &private_key_der))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
SECItem private_key_item;
|
SECItem private_key_item;
|
||||||
|
@ -363,6 +363,15 @@ void SocketTest::ConnectWithDnsLookupFailInternal(const IPAddress& loopback) {
|
|||||||
EXPECT_EQ(0, client->Connect(bogus_dns_addr));
|
EXPECT_EQ(0, client->Connect(bogus_dns_addr));
|
||||||
|
|
||||||
// Wait for connection to fail (EHOSTNOTFOUND).
|
// Wait for connection to fail (EHOSTNOTFOUND).
|
||||||
|
bool dns_lookup_finished = false;
|
||||||
|
WAIT_(client->GetState() == AsyncSocket::CS_CLOSED, kTimeout,
|
||||||
|
dns_lookup_finished);
|
||||||
|
if (!dns_lookup_finished) {
|
||||||
|
LOG(LS_WARNING) << "Skipping test; DNS resolution took longer than 5 "
|
||||||
|
<< "seconds.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
|
||||||
EXPECT_FALSE(sink.Check(client.get(), testing::SSE_OPEN));
|
EXPECT_FALSE(sink.Check(client.get(), testing::SSE_OPEN));
|
||||||
EXPECT_TRUE(sink.Check(client.get(), testing::SSE_ERROR));
|
EXPECT_TRUE(sink.Check(client.get(), testing::SSE_ERROR));
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "talk/base/base64.h"
|
||||||
|
#include "talk/base/logging.h"
|
||||||
#include "talk/base/sslconfig.h"
|
#include "talk/base/sslconfig.h"
|
||||||
|
|
||||||
#if SSL_USE_SCHANNEL
|
#if SSL_USE_SCHANNEL
|
||||||
@ -50,6 +52,59 @@
|
|||||||
|
|
||||||
namespace talk_base {
|
namespace talk_base {
|
||||||
|
|
||||||
|
const char kPemTypeCertificate[] = "CERTIFICATE";
|
||||||
|
const char kPemTypeRsaPrivateKey[] = "RSA PRIVATE KEY";
|
||||||
|
|
||||||
|
bool SSLIdentity::PemToDer(const std::string& pem_type,
|
||||||
|
const std::string& pem_string,
|
||||||
|
std::string* der) {
|
||||||
|
// Find the inner body. We need this to fulfill the contract of
|
||||||
|
// returning pem_length.
|
||||||
|
size_t header = pem_string.find("-----BEGIN " + pem_type + "-----");
|
||||||
|
if (header == std::string::npos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t body = pem_string.find("\n", header);
|
||||||
|
if (body == std::string::npos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t trailer = pem_string.find("-----END " + pem_type + "-----");
|
||||||
|
if (trailer == std::string::npos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string inner = pem_string.substr(body + 1, trailer - (body + 1));
|
||||||
|
|
||||||
|
*der = Base64::Decode(inner, Base64::DO_PARSE_WHITE |
|
||||||
|
Base64::DO_PAD_ANY |
|
||||||
|
Base64::DO_TERM_BUFFER);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SSLIdentity::DerToPem(const std::string& pem_type,
|
||||||
|
const unsigned char* data,
|
||||||
|
size_t length) {
|
||||||
|
std::stringstream result;
|
||||||
|
|
||||||
|
result << "-----BEGIN " << pem_type << "-----\n";
|
||||||
|
|
||||||
|
std::string b64_encoded;
|
||||||
|
Base64::EncodeFromArray(data, length, &b64_encoded);
|
||||||
|
|
||||||
|
// Divide the Base-64 encoded data into 64-character chunks, as per
|
||||||
|
// 4.3.2.4 of RFC 1421.
|
||||||
|
static const size_t kChunkSize = 64;
|
||||||
|
size_t chunks = (b64_encoded.size() + (kChunkSize - 1)) / kChunkSize;
|
||||||
|
for (size_t i = 0, chunk_offset = 0; i < chunks;
|
||||||
|
++i, chunk_offset += kChunkSize) {
|
||||||
|
result << b64_encoded.substr(chunk_offset, kChunkSize);
|
||||||
|
result << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
result << "-----END " << pem_type << "-----\n";
|
||||||
|
|
||||||
|
return result.str();
|
||||||
|
}
|
||||||
|
|
||||||
#if SSL_USE_SCHANNEL
|
#if SSL_USE_SCHANNEL
|
||||||
|
|
||||||
SSLCertificate* SSLCertificate::FromPEMString(const std::string& pem_string) {
|
SSLCertificate* SSLCertificate::FromPEMString(const std::string& pem_string) {
|
||||||
|
@ -64,8 +64,8 @@ class SSLCertificate {
|
|||||||
|
|
||||||
// Compute the digest of the certificate given algorithm
|
// Compute the digest of the certificate given algorithm
|
||||||
virtual bool ComputeDigest(const std::string &algorithm,
|
virtual bool ComputeDigest(const std::string &algorithm,
|
||||||
unsigned char *digest, std::size_t size,
|
unsigned char* digest, std::size_t size,
|
||||||
std::size_t *length) const = 0;
|
std::size_t* length) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Our identity in an SSL negotiation: a keypair and certificate (both
|
// Our identity in an SSL negotiation: a keypair and certificate (both
|
||||||
@ -93,8 +93,19 @@ class SSLIdentity {
|
|||||||
|
|
||||||
// Returns a temporary reference to the certificate.
|
// Returns a temporary reference to the certificate.
|
||||||
virtual const SSLCertificate& certificate() const = 0;
|
virtual const SSLCertificate& certificate() const = 0;
|
||||||
|
|
||||||
|
// Helpers for parsing converting between PEM and DER format.
|
||||||
|
static bool PemToDer(const std::string& pem_type,
|
||||||
|
const std::string& pem_string,
|
||||||
|
std::string* der);
|
||||||
|
static std::string DerToPem(const std::string& pem_type,
|
||||||
|
const unsigned char* data,
|
||||||
|
size_t length);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern const char kPemTypeCertificate[];
|
||||||
|
extern const char kPemTypeRsaPrivateKey[];
|
||||||
|
|
||||||
} // namespace talk_base
|
} // namespace talk_base
|
||||||
|
|
||||||
#endif // TALK_BASE_SSLIDENTITY_H__
|
#endif // TALK_BASE_SSLIDENTITY_H__
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
#include "talk/base/ssladapter.h"
|
#include "talk/base/ssladapter.h"
|
||||||
#include "talk/base/sslidentity.h"
|
#include "talk/base/sslidentity.h"
|
||||||
|
|
||||||
|
using talk_base::SSLIdentity;
|
||||||
|
|
||||||
const char kTestCertificate[] = "-----BEGIN CERTIFICATE-----\n"
|
const char kTestCertificate[] = "-----BEGIN CERTIFICATE-----\n"
|
||||||
"MIIB6TCCAVICAQYwDQYJKoZIhvcNAQEEBQAwWzELMAkGA1UEBhMCQVUxEzARBgNV\n"
|
"MIIB6TCCAVICAQYwDQYJKoZIhvcNAQEEBQAwWzELMAkGA1UEBhMCQVUxEzARBgNV\n"
|
||||||
"BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRswGQYD\n"
|
"BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRswGQYD\n"
|
||||||
@ -70,8 +72,8 @@ class SSLIdentityTest : public testing::Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
identity1_.reset(talk_base::SSLIdentity::Generate("test1"));
|
identity1_.reset(SSLIdentity::Generate("test1"));
|
||||||
identity2_.reset(talk_base::SSLIdentity::Generate("test2"));
|
identity2_.reset(SSLIdentity::Generate("test2"));
|
||||||
|
|
||||||
ASSERT_TRUE(identity1_);
|
ASSERT_TRUE(identity1_);
|
||||||
ASSERT_TRUE(identity2_);
|
ASSERT_TRUE(identity2_);
|
||||||
@ -126,8 +128,8 @@ class SSLIdentityTest : public testing::Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
talk_base::scoped_ptr<talk_base::SSLIdentity> identity1_;
|
talk_base::scoped_ptr<SSLIdentity> identity1_;
|
||||||
talk_base::scoped_ptr<talk_base::SSLIdentity> identity2_;
|
talk_base::scoped_ptr<SSLIdentity> identity2_;
|
||||||
talk_base::scoped_ptr<talk_base::SSLCertificate> test_cert_;
|
talk_base::scoped_ptr<talk_base::SSLCertificate> test_cert_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -187,8 +189,17 @@ TEST_F(SSLIdentityTest, FromPEMStrings) {
|
|||||||
"qCV42aXS3onOXDQ1ibuWq0fr0//aj0wo4KV474c=\n"
|
"qCV42aXS3onOXDQ1ibuWq0fr0//aj0wo4KV474c=\n"
|
||||||
"-----END CERTIFICATE-----\n";
|
"-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
talk_base::scoped_ptr<talk_base::SSLIdentity> identity(
|
talk_base::scoped_ptr<SSLIdentity> identity(
|
||||||
talk_base::SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kCERT_PEM));
|
SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kCERT_PEM));
|
||||||
EXPECT_TRUE(identity);
|
EXPECT_TRUE(identity);
|
||||||
EXPECT_EQ(kCERT_PEM, identity->certificate().ToPEMString());
|
EXPECT_EQ(kCERT_PEM, identity->certificate().ToPEMString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SSLIdentityTest, PemDerConversion) {
|
||||||
|
std::string der;
|
||||||
|
EXPECT_TRUE(SSLIdentity::PemToDer("CERTIFICATE", kTestCertificate, &der));
|
||||||
|
|
||||||
|
EXPECT_EQ(kTestCertificate, SSLIdentity::DerToPem(
|
||||||
|
"CERTIFICATE",
|
||||||
|
reinterpret_cast<const unsigned char*>(der.data()), der.length()));
|
||||||
|
}
|
||||||
|
@ -1158,6 +1158,8 @@
|
|||||||
'app/webrtc/webrtcsdp.h',
|
'app/webrtc/webrtcsdp.h',
|
||||||
'app/webrtc/webrtcsession.cc',
|
'app/webrtc/webrtcsession.cc',
|
||||||
'app/webrtc/webrtcsession.h',
|
'app/webrtc/webrtcsession.h',
|
||||||
|
'app/webrtc/webrtcsessiondescriptionfactory.cc',
|
||||||
|
'app/webrtc/webrtcsessiondescriptionfactory.h',
|
||||||
],
|
],
|
||||||
}, # target libjingle_peerconnection
|
}, # target libjingle_peerconnection
|
||||||
],
|
],
|
||||||
|
@ -391,6 +391,8 @@
|
|||||||
'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/fakedtlsidentityservice.h',
|
||||||
|
'app/webrtc/test/fakemediastreamsignaling.h',
|
||||||
'app/webrtc/test/fakeperiodicvideocapturer.h',
|
'app/webrtc/test/fakeperiodicvideocapturer.h',
|
||||||
'app/webrtc/test/fakevideotrackrenderer.h',
|
'app/webrtc/test/fakevideotrackrenderer.h',
|
||||||
'app/webrtc/test/mockpeerconnectionobservers.h',
|
'app/webrtc/test/mockpeerconnectionobservers.h',
|
||||||
|
@ -45,6 +45,10 @@ class FeedbackParam {
|
|||||||
: id_(id),
|
: id_(id),
|
||||||
param_(param) {
|
param_(param) {
|
||||||
}
|
}
|
||||||
|
explicit FeedbackParam(const std::string& id)
|
||||||
|
: id_(id),
|
||||||
|
param_(kParamValueEmpty) {
|
||||||
|
}
|
||||||
bool operator==(const FeedbackParam& other) const;
|
bool operator==(const FeedbackParam& other) const;
|
||||||
|
|
||||||
const std::string& id() const { return id_; }
|
const std::string& id() const { return id_; }
|
||||||
|
@ -134,11 +134,14 @@ static int OnSctpInboundPacket(struct socket* sock, union sctp_sockstore addr,
|
|||||||
// Post data to the channel's receiver thread (copying it).
|
// Post data to the channel's receiver thread (copying it).
|
||||||
// TODO(ldixon): Unclear if copy is needed as this method is responsible for
|
// TODO(ldixon): Unclear if copy is needed as this method is responsible for
|
||||||
// memory cleanup. But this does simplify code.
|
// memory cleanup. But this does simplify code.
|
||||||
|
const uint32 native_ppid = talk_base::HostToNetwork32(rcv.rcv_ppid);
|
||||||
SctpInboundPacket* packet = new SctpInboundPacket();
|
SctpInboundPacket* packet = new SctpInboundPacket();
|
||||||
packet->buffer.SetData(data, length);
|
packet->buffer.SetData(data, length);
|
||||||
packet->params.ssrc = rcv.rcv_sid;
|
packet->params.ssrc = rcv.rcv_sid;
|
||||||
packet->params.seq_num = rcv.rcv_ssn;
|
packet->params.seq_num = rcv.rcv_ssn;
|
||||||
packet->params.timestamp = rcv.rcv_tsn;
|
packet->params.timestamp = rcv.rcv_tsn;
|
||||||
|
packet->params.type =
|
||||||
|
static_cast<cricket::DataMessageType>(native_ppid);
|
||||||
packet->flags = flags;
|
packet->flags = flags;
|
||||||
channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET,
|
channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET,
|
||||||
talk_base::WrapMessageData(packet));
|
talk_base::WrapMessageData(packet));
|
||||||
@ -371,7 +374,8 @@ bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StreamParams found_stream;
|
StreamParams found_stream;
|
||||||
if (GetStreamBySsrc(send_streams_, stream.first_ssrc(), &found_stream)) {
|
// TODO(lally): Consider keeping this sorted.
|
||||||
|
if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
|
||||||
LOG(LS_WARNING) << debug_name_ << "->AddSendStream(...): "
|
LOG(LS_WARNING) << debug_name_ << "->AddSendStream(...): "
|
||||||
<< "Not adding data send stream '" << stream.id
|
<< "Not adding data send stream '" << stream.id
|
||||||
<< "' with ssrc=" << stream.first_ssrc()
|
<< "' with ssrc=" << stream.first_ssrc()
|
||||||
@ -379,17 +383,17 @@ bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
send_streams_.push_back(stream);
|
streams_.push_back(stream);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SctpDataMediaChannel::RemoveSendStream(uint32 ssrc) {
|
bool SctpDataMediaChannel::RemoveSendStream(uint32 ssrc) {
|
||||||
StreamParams found_stream;
|
StreamParams found_stream;
|
||||||
if (!GetStreamBySsrc(send_streams_, ssrc, &found_stream)) {
|
if (!GetStreamBySsrc(streams_, ssrc, &found_stream)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveStreamBySsrc(&send_streams_, ssrc);
|
RemoveStreamBySsrc(&streams_, ssrc);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,7 +405,7 @@ bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StreamParams found_stream;
|
StreamParams found_stream;
|
||||||
if (GetStreamBySsrc(recv_streams_, stream.first_ssrc(), &found_stream)) {
|
if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
|
||||||
LOG(LS_WARNING) << debug_name_ << "->AddRecvStream(...): "
|
LOG(LS_WARNING) << debug_name_ << "->AddRecvStream(...): "
|
||||||
<< "Not adding data recv stream '" << stream.id
|
<< "Not adding data recv stream '" << stream.id
|
||||||
<< "' with ssrc=" << stream.first_ssrc()
|
<< "' with ssrc=" << stream.first_ssrc()
|
||||||
@ -409,7 +413,7 @@ bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
recv_streams_.push_back(stream);
|
streams_.push_back(stream);
|
||||||
LOG(LS_VERBOSE) << debug_name_ << "->AddRecvStream(...): "
|
LOG(LS_VERBOSE) << debug_name_ << "->AddRecvStream(...): "
|
||||||
<< "Added data recv stream '" << stream.id
|
<< "Added data recv stream '" << stream.id
|
||||||
<< "' with ssrc=" << stream.first_ssrc();
|
<< "' with ssrc=" << stream.first_ssrc();
|
||||||
@ -417,7 +421,7 @@ bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool SctpDataMediaChannel::RemoveRecvStream(uint32 ssrc) {
|
bool SctpDataMediaChannel::RemoveRecvStream(uint32 ssrc) {
|
||||||
RemoveStreamBySsrc(&recv_streams_, ssrc);
|
RemoveStreamBySsrc(&streams_, ssrc);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,7 +442,8 @@ bool SctpDataMediaChannel::SendData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
StreamParams found_stream;
|
StreamParams found_stream;
|
||||||
if (!GetStreamBySsrc(send_streams_, params.ssrc, &found_stream)) {
|
if (params.type != cricket::DMT_CONTROL &&
|
||||||
|
!GetStreamBySsrc(streams_, params.ssrc, &found_stream)) {
|
||||||
LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
|
LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
|
||||||
<< "Not sending data because ssrc is unknown: "
|
<< "Not sending data because ssrc is unknown: "
|
||||||
<< params.ssrc;
|
<< params.ssrc;
|
||||||
@ -471,7 +476,7 @@ bool SctpDataMediaChannel::SendData(
|
|||||||
sndinfo.snd_flags = 0;
|
sndinfo.snd_flags = 0;
|
||||||
// TODO(pthatcher): Once data types are added to SendParams, this can be set
|
// TODO(pthatcher): Once data types are added to SendParams, this can be set
|
||||||
// from SendParams.
|
// from SendParams.
|
||||||
sndinfo.snd_ppid = talk_base::HostToNetwork32(PPID_NONE);
|
sndinfo.snd_ppid = talk_base::HostToNetwork32(params.type);
|
||||||
sndinfo.snd_context = 0;
|
sndinfo.snd_context = 0;
|
||||||
sndinfo.snd_assoc_id = 0;
|
sndinfo.snd_assoc_id = 0;
|
||||||
ssize_t res = usrsctp_sendv(sock_, payload.data(),
|
ssize_t res = usrsctp_sendv(sock_, payload.data(),
|
||||||
@ -548,9 +553,13 @@ void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
|
|||||||
void SctpDataMediaChannel::OnDataFromSctpToChannel(
|
void SctpDataMediaChannel::OnDataFromSctpToChannel(
|
||||||
const ReceiveDataParams& params, talk_base::Buffer* buffer) {
|
const ReceiveDataParams& params, talk_base::Buffer* buffer) {
|
||||||
StreamParams found_stream;
|
StreamParams found_stream;
|
||||||
if (!GetStreamBySsrc(recv_streams_, params.ssrc, &found_stream)) {
|
if (!GetStreamBySsrc(streams_, params.ssrc, &found_stream)) {
|
||||||
|
if (params.type == DMT_CONTROL) {
|
||||||
|
SignalDataReceived(params, buffer->data(), buffer->length());
|
||||||
|
} else {
|
||||||
LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
|
LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
|
||||||
<< "Received packet for unknown ssrc: " << params.ssrc;
|
<< "Received packet for unknown ssrc: " << params.ssrc;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,8 +218,8 @@ class SctpDataMediaChannel : public DataMediaChannel,
|
|||||||
bool sending_;
|
bool sending_;
|
||||||
// receiving_ controls whether inbound packets are thrown away.
|
// receiving_ controls whether inbound packets are thrown away.
|
||||||
bool receiving_;
|
bool receiving_;
|
||||||
std::vector<StreamParams> send_streams_;
|
// Unified send/receive streams, as each is bidirectional.
|
||||||
std::vector<StreamParams> recv_streams_;
|
std::vector<StreamParams> streams_;
|
||||||
|
|
||||||
// A human-readable name for debugging messages.
|
// A human-readable name for debugging messages.
|
||||||
std::string debug_name_;
|
std::string debug_name_;
|
||||||
|
@ -266,7 +266,9 @@ class FakeWebRtcVoiceEngine
|
|||||||
virtual webrtc::AudioProcessing* audio_processing() OVERRIDE {
|
virtual webrtc::AudioProcessing* audio_processing() OVERRIDE {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
#ifndef USE_WEBRTC_DEV_BRANCH
|
||||||
WEBRTC_STUB(MaxNumOfChannels, ());
|
WEBRTC_STUB(MaxNumOfChannels, ());
|
||||||
|
#endif
|
||||||
WEBRTC_FUNC(CreateChannel, ()) {
|
WEBRTC_FUNC(CreateChannel, ()) {
|
||||||
if (fail_create_channel_) {
|
if (fail_create_channel_) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -76,6 +76,13 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateUdpSocket(
|
|||||||
|
|
||||||
AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket(
|
AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket(
|
||||||
const SocketAddress& local_address, int min_port, int max_port, int opts) {
|
const SocketAddress& local_address, int min_port, int max_port, int opts) {
|
||||||
|
|
||||||
|
// Fail if TLS is required.
|
||||||
|
if (opts & PacketSocketFactory::OPT_TLS) {
|
||||||
|
LOG(LS_ERROR) << "TLS support currently is not available.";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
talk_base::AsyncSocket* socket =
|
talk_base::AsyncSocket* socket =
|
||||||
socket_factory()->CreateAsyncSocket(local_address.family(),
|
socket_factory()->CreateAsyncSocket(local_address.family(),
|
||||||
SOCK_STREAM);
|
SOCK_STREAM);
|
||||||
@ -92,6 +99,7 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket(
|
|||||||
|
|
||||||
// If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket.
|
// If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket.
|
||||||
if (opts & PacketSocketFactory::OPT_SSLTCP) {
|
if (opts & PacketSocketFactory::OPT_SSLTCP) {
|
||||||
|
ASSERT(!(opts & PacketSocketFactory::OPT_TLS));
|
||||||
socket = new talk_base::AsyncSSLSocket(socket);
|
socket = new talk_base::AsyncSSLSocket(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +116,13 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket(
|
|||||||
AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
|
AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
|
||||||
const SocketAddress& local_address, const SocketAddress& remote_address,
|
const SocketAddress& local_address, const SocketAddress& remote_address,
|
||||||
const ProxyInfo& proxy_info, const std::string& user_agent, int opts) {
|
const ProxyInfo& proxy_info, const std::string& user_agent, int opts) {
|
||||||
|
|
||||||
|
// Fail if TLS is required.
|
||||||
|
if (opts & PacketSocketFactory::OPT_TLS) {
|
||||||
|
LOG(LS_ERROR) << "TLS support currently is not available.";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
talk_base::AsyncSocket* socket =
|
talk_base::AsyncSocket* socket =
|
||||||
socket_factory()->CreateAsyncSocket(local_address.family(), SOCK_STREAM);
|
socket_factory()->CreateAsyncSocket(local_address.family(), SOCK_STREAM);
|
||||||
if (!socket) {
|
if (!socket) {
|
||||||
@ -133,6 +148,7 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
|
|||||||
|
|
||||||
// If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket.
|
// If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket.
|
||||||
if (opts & PacketSocketFactory::OPT_SSLTCP) {
|
if (opts & PacketSocketFactory::OPT_SSLTCP) {
|
||||||
|
ASSERT(!(opts & PacketSocketFactory::OPT_TLS));
|
||||||
socket = new talk_base::AsyncSSLSocket(socket);
|
socket = new talk_base::AsyncSSLSocket(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +114,8 @@ const buzz::StaticQName QN_JINGLE_RTP_PAYLOADTYPE =
|
|||||||
const buzz::StaticQName QN_JINGLE_RTP_BANDWIDTH =
|
const buzz::StaticQName QN_JINGLE_RTP_BANDWIDTH =
|
||||||
{ NS_JINGLE_RTP, LN_BANDWIDTH };
|
{ NS_JINGLE_RTP, LN_BANDWIDTH };
|
||||||
const buzz::StaticQName QN_JINGLE_RTCP_MUX = { NS_JINGLE_RTP, "rtcp-mux" };
|
const buzz::StaticQName QN_JINGLE_RTCP_MUX = { NS_JINGLE_RTP, "rtcp-mux" };
|
||||||
|
const buzz::StaticQName QN_JINGLE_RTCP_FB = { NS_JINGLE_RTP, "rtcp-fb" };
|
||||||
|
const buzz::StaticQName QN_SUBTYPE = { NS_EMPTY, "subtype" };
|
||||||
const buzz::StaticQName QN_PARAMETER = { NS_JINGLE_RTP, "parameter" };
|
const buzz::StaticQName QN_PARAMETER = { NS_JINGLE_RTP, "parameter" };
|
||||||
const buzz::StaticQName QN_JINGLE_RTP_HDREXT =
|
const buzz::StaticQName QN_JINGLE_RTP_HDREXT =
|
||||||
{ NS_JINGLE_RTP, "rtp-hdrext" };
|
{ NS_JINGLE_RTP, "rtp-hdrext" };
|
||||||
|
@ -130,6 +130,8 @@ extern const buzz::StaticQName QN_SSRC;
|
|||||||
extern const buzz::StaticQName QN_JINGLE_RTP_PAYLOADTYPE;
|
extern const buzz::StaticQName QN_JINGLE_RTP_PAYLOADTYPE;
|
||||||
extern const buzz::StaticQName QN_JINGLE_RTP_BANDWIDTH;
|
extern const buzz::StaticQName QN_JINGLE_RTP_BANDWIDTH;
|
||||||
extern const buzz::StaticQName QN_JINGLE_RTCP_MUX;
|
extern const buzz::StaticQName QN_JINGLE_RTCP_MUX;
|
||||||
|
extern const buzz::StaticQName QN_JINGLE_RTCP_FB;
|
||||||
|
extern const buzz::StaticQName QN_SUBTYPE;
|
||||||
extern const buzz::StaticQName QN_JINGLE_RTP_HDREXT;
|
extern const buzz::StaticQName QN_JINGLE_RTP_HDREXT;
|
||||||
extern const buzz::StaticQName QN_URI;
|
extern const buzz::StaticQName QN_URI;
|
||||||
|
|
||||||
|
@ -56,6 +56,10 @@ class DtlsTransport : public Base {
|
|||||||
Base::DestroyAllChannels();
|
Base::DestroyAllChannels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void SetIdentity_w(talk_base::SSLIdentity* identity) {
|
||||||
|
identity_ = identity;
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl*
|
virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl*
|
||||||
channel) {
|
channel) {
|
||||||
talk_base::SSLFingerprint* local_fp =
|
talk_base::SSLFingerprint* local_fp =
|
||||||
|
@ -226,6 +226,9 @@ class P2PTransportChannelTestBase : public testing::Test,
|
|||||||
void SetAllocationStepDelay(uint32 delay) {
|
void SetAllocationStepDelay(uint32 delay) {
|
||||||
allocator_->set_step_delay(delay);
|
allocator_->set_step_delay(delay);
|
||||||
}
|
}
|
||||||
|
void SetAllowTcpListen(bool allow_tcp_listen) {
|
||||||
|
allocator_->set_allow_tcp_listen(allow_tcp_listen);
|
||||||
|
}
|
||||||
|
|
||||||
talk_base::FakeNetworkManager network_manager_;
|
talk_base::FakeNetworkManager network_manager_;
|
||||||
talk_base::scoped_ptr<cricket::PortAllocator> allocator_;
|
talk_base::scoped_ptr<cricket::PortAllocator> allocator_;
|
||||||
@ -398,6 +401,9 @@ class P2PTransportChannelTestBase : public testing::Test,
|
|||||||
void SetAllocationStepDelay(int endpoint, uint32 delay) {
|
void SetAllocationStepDelay(int endpoint, uint32 delay) {
|
||||||
return GetEndpoint(endpoint)->SetAllocationStepDelay(delay);
|
return GetEndpoint(endpoint)->SetAllocationStepDelay(delay);
|
||||||
}
|
}
|
||||||
|
void SetAllowTcpListen(int endpoint, bool allow_tcp_listen) {
|
||||||
|
return GetEndpoint(endpoint)->SetAllowTcpListen(allow_tcp_listen);
|
||||||
|
}
|
||||||
|
|
||||||
void Test(const Result& expected) {
|
void Test(const Result& expected) {
|
||||||
int32 connect_start = talk_base::Time(), connect_time;
|
int32 connect_start = talk_base::Time(), connect_time;
|
||||||
@ -1222,6 +1228,44 @@ TEST_F(P2PTransportChannelTest, IncomingOnlyOpen) {
|
|||||||
DestroyChannels();
|
DestroyChannels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(P2PTransportChannelTest, TestTcpConnectionsFromActiveToPassive) {
|
||||||
|
AddAddress(0, kPublicAddrs[0]);
|
||||||
|
AddAddress(1, kPublicAddrs[1]);
|
||||||
|
|
||||||
|
SetAllocationStepDelay(0, kMinimumStepDelay);
|
||||||
|
SetAllocationStepDelay(1, kMinimumStepDelay);
|
||||||
|
|
||||||
|
int kOnlyLocalTcpPorts = cricket::PORTALLOCATOR_DISABLE_UDP |
|
||||||
|
cricket::PORTALLOCATOR_DISABLE_STUN |
|
||||||
|
cricket::PORTALLOCATOR_DISABLE_RELAY |
|
||||||
|
cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG;
|
||||||
|
// Disable all protocols except TCP.
|
||||||
|
SetAllocatorFlags(0, kOnlyLocalTcpPorts);
|
||||||
|
SetAllocatorFlags(1, kOnlyLocalTcpPorts);
|
||||||
|
|
||||||
|
SetAllowTcpListen(0, true); // actpass.
|
||||||
|
SetAllowTcpListen(1, false); // active.
|
||||||
|
|
||||||
|
CreateChannels(1);
|
||||||
|
|
||||||
|
EXPECT_TRUE_WAIT(ep1_ch1()->readable() && ep1_ch1()->writable() &&
|
||||||
|
ep2_ch1()->readable() && ep2_ch1()->writable(),
|
||||||
|
1000);
|
||||||
|
EXPECT_TRUE(
|
||||||
|
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
|
||||||
|
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
|
||||||
|
RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1]));
|
||||||
|
|
||||||
|
std::string kTcpProtocol = "tcp";
|
||||||
|
EXPECT_EQ(kTcpProtocol, RemoteCandidate(ep1_ch1())->protocol());
|
||||||
|
EXPECT_EQ(kTcpProtocol, LocalCandidate(ep1_ch1())->protocol());
|
||||||
|
EXPECT_EQ(kTcpProtocol, RemoteCandidate(ep2_ch1())->protocol());
|
||||||
|
EXPECT_EQ(kTcpProtocol, LocalCandidate(ep2_ch1())->protocol());
|
||||||
|
|
||||||
|
TestSendRecv(1);
|
||||||
|
DestroyChannels();
|
||||||
|
}
|
||||||
|
|
||||||
// Test what happens when we have 2 users behind the same NAT. This can lead
|
// Test what happens when we have 2 users behind the same NAT. This can lead
|
||||||
// to interesting behavior because the STUN server will only give out the
|
// to interesting behavior because the STUN server will only give out the
|
||||||
// address of the outermost NAT.
|
// address of the outermost NAT.
|
||||||
|
@ -37,8 +37,9 @@ class AsyncPacketSocket;
|
|||||||
class PacketSocketFactory {
|
class PacketSocketFactory {
|
||||||
public:
|
public:
|
||||||
enum Options {
|
enum Options {
|
||||||
OPT_SSLTCP = 0x01,
|
OPT_SSLTCP = 0x01, // Pseudo-TLS.
|
||||||
OPT_STUN = 0x02,
|
OPT_TLS = 0x02,
|
||||||
|
OPT_STUN = 0x04,
|
||||||
};
|
};
|
||||||
|
|
||||||
PacketSocketFactory() { }
|
PacketSocketFactory() { }
|
||||||
|
@ -104,9 +104,12 @@ bool StringToProto(const char* value, ProtocolType* proto);
|
|||||||
struct ProtocolAddress {
|
struct ProtocolAddress {
|
||||||
talk_base::SocketAddress address;
|
talk_base::SocketAddress address;
|
||||||
ProtocolType proto;
|
ProtocolType proto;
|
||||||
|
bool secure;
|
||||||
|
|
||||||
ProtocolAddress(const talk_base::SocketAddress& a, ProtocolType p)
|
ProtocolAddress(const talk_base::SocketAddress& a, ProtocolType p)
|
||||||
: address(a), proto(p) { }
|
: address(a), proto(p), secure(false) { }
|
||||||
|
ProtocolAddress(const talk_base::SocketAddress& a, ProtocolType p, bool sec)
|
||||||
|
: address(a), proto(p), secure(sec) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Represents a local communication mechanism that can be used to create
|
// Represents a local communication mechanism that can be used to create
|
||||||
|
@ -117,7 +117,8 @@ class PortAllocator : public sigslot::has_slots<> {
|
|||||||
flags_(kDefaultPortAllocatorFlags),
|
flags_(kDefaultPortAllocatorFlags),
|
||||||
min_port_(0),
|
min_port_(0),
|
||||||
max_port_(0),
|
max_port_(0),
|
||||||
step_delay_(kDefaultStepDelay) {
|
step_delay_(kDefaultStepDelay),
|
||||||
|
allow_tcp_listen_(true) {
|
||||||
// This will allow us to have old behavior on non webrtc clients.
|
// This will allow us to have old behavior on non webrtc clients.
|
||||||
}
|
}
|
||||||
virtual ~PortAllocator();
|
virtual ~PortAllocator();
|
||||||
@ -155,11 +156,16 @@ class PortAllocator : public sigslot::has_slots<> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 step_delay() const { return step_delay_; }
|
||||||
void set_step_delay(uint32 delay) {
|
void set_step_delay(uint32 delay) {
|
||||||
ASSERT(delay >= kMinimumStepDelay);
|
ASSERT(delay >= kMinimumStepDelay);
|
||||||
step_delay_ = delay;
|
step_delay_ = delay;
|
||||||
}
|
}
|
||||||
uint32 step_delay() const { return step_delay_; }
|
|
||||||
|
bool allow_tcp_listen() const { return allow_tcp_listen_; }
|
||||||
|
void set_allow_tcp_listen(bool allow_tcp_listen) {
|
||||||
|
allow_tcp_listen_ = allow_tcp_listen;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual PortAllocatorSession* CreateSessionInternal(
|
virtual PortAllocatorSession* CreateSessionInternal(
|
||||||
@ -177,6 +183,7 @@ class PortAllocator : public sigslot::has_slots<> {
|
|||||||
int max_port_;
|
int max_port_;
|
||||||
uint32 step_delay_;
|
uint32 step_delay_;
|
||||||
SessionMuxerMap muxers_;
|
SessionMuxerMap muxers_;
|
||||||
|
bool allow_tcp_listen_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cricket
|
} // namespace cricket
|
||||||
|
@ -288,6 +288,11 @@ bool TransportProxy::OnRemoteCandidates(const Candidates& candidates,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransportProxy::SetIdentity(
|
||||||
|
talk_base::SSLIdentity* identity) {
|
||||||
|
transport_->get()->SetIdentity(identity);
|
||||||
|
}
|
||||||
|
|
||||||
std::string BaseSession::StateToString(State state) {
|
std::string BaseSession::StateToString(State state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Session::STATE_INIT:
|
case Session::STATE_INIT:
|
||||||
@ -368,6 +373,17 @@ BaseSession::~BaseSession() {
|
|||||||
delete local_description_;
|
delete local_description_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BaseSession::SetIdentity(talk_base::SSLIdentity* identity) {
|
||||||
|
if (identity_)
|
||||||
|
return false;
|
||||||
|
identity_ = identity;
|
||||||
|
for (TransportMap::iterator iter = transports_.begin();
|
||||||
|
iter != transports_.end(); ++iter) {
|
||||||
|
iter->second->SetIdentity(identity_);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool BaseSession::PushdownTransportDescription(ContentSource source,
|
bool BaseSession::PushdownTransportDescription(ContentSource source,
|
||||||
ContentAction action) {
|
ContentAction action) {
|
||||||
if (source == CS_LOCAL) {
|
if (source == CS_LOCAL) {
|
||||||
|
@ -141,6 +141,7 @@ class TransportProxy : public sigslot::has_slots<>,
|
|||||||
|
|
||||||
// Simple functions that thunk down to the same functions on Transport.
|
// Simple functions that thunk down to the same functions on Transport.
|
||||||
void SetRole(TransportRole role);
|
void SetRole(TransportRole role);
|
||||||
|
void SetIdentity(talk_base::SSLIdentity* identity);
|
||||||
bool SetLocalTransportDescription(const TransportDescription& description,
|
bool SetLocalTransportDescription(const TransportDescription& description,
|
||||||
ContentAction action);
|
ContentAction action);
|
||||||
bool SetRemoteTransportDescription(const TransportDescription& description,
|
bool SetRemoteTransportDescription(const TransportDescription& description,
|
||||||
@ -365,15 +366,16 @@ class BaseSession : public sigslot::has_slots<>,
|
|||||||
// This avoids exposing the internal structures used to track them.
|
// This avoids exposing the internal structures used to track them.
|
||||||
virtual bool GetStats(SessionStats* stats);
|
virtual bool GetStats(SessionStats* stats);
|
||||||
|
|
||||||
|
talk_base::SSLIdentity* identity() { return identity_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// Specifies the identity to use in this session.
|
||||||
|
bool SetIdentity(talk_base::SSLIdentity* identity);
|
||||||
|
|
||||||
bool PushdownTransportDescription(ContentSource source,
|
bool PushdownTransportDescription(ContentSource source,
|
||||||
ContentAction action);
|
ContentAction action);
|
||||||
void set_initiator(bool initiator) { initiator_ = initiator; }
|
void set_initiator(bool initiator) { initiator_ = initiator; }
|
||||||
|
|
||||||
talk_base::SSLIdentity* identity() { return identity_; }
|
|
||||||
// Specifies the identity to use in this session.
|
|
||||||
void set_identity(talk_base::SSLIdentity* identity) { identity_ = identity; }
|
|
||||||
|
|
||||||
const TransportMap& transport_proxies() const { return transports_; }
|
const TransportMap& transport_proxies() const { return transports_; }
|
||||||
// Get a TransportProxy by content_name or transport. NULL if not found.
|
// Get a TransportProxy by content_name or transport. NULL if not found.
|
||||||
TransportProxy* GetTransportProxy(const std::string& content_name);
|
TransportProxy* GetTransportProxy(const std::string& content_name);
|
||||||
|
@ -100,7 +100,7 @@ Session* SessionManager::CreateSession(
|
|||||||
|
|
||||||
Session* session = new Session(this, local_name, initiator_name,
|
Session* session = new Session(this, local_name, initiator_name,
|
||||||
sid, content_type, client);
|
sid, content_type, client);
|
||||||
session->set_identity(transport_desc_factory_.identity());
|
session->SetIdentity(transport_desc_factory_.identity());
|
||||||
session_map_[session->id()] = session;
|
session_map_[session->id()] = session;
|
||||||
session->SignalRequestSignaling.connect(
|
session->SignalRequestSignaling.connect(
|
||||||
this, &SessionManager::OnRequestSignaling);
|
this, &SessionManager::OnRequestSignaling);
|
||||||
|
@ -58,7 +58,8 @@ enum {
|
|||||||
MSG_SETROLE = 16,
|
MSG_SETROLE = 16,
|
||||||
MSG_SETLOCALDESCRIPTION = 17,
|
MSG_SETLOCALDESCRIPTION = 17,
|
||||||
MSG_SETREMOTEDESCRIPTION = 18,
|
MSG_SETREMOTEDESCRIPTION = 18,
|
||||||
MSG_GETSTATS = 19
|
MSG_GETSTATS = 19,
|
||||||
|
MSG_SETIDENTITY = 20,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ChannelParams : public talk_base::MessageData {
|
struct ChannelParams : public talk_base::MessageData {
|
||||||
@ -102,6 +103,13 @@ struct StatsParam : public talk_base::MessageData {
|
|||||||
bool result;
|
bool result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct IdentityParam : public talk_base::MessageData {
|
||||||
|
explicit IdentityParam(talk_base::SSLIdentity* identity)
|
||||||
|
: identity(identity) {}
|
||||||
|
|
||||||
|
talk_base::SSLIdentity* identity;
|
||||||
|
};
|
||||||
|
|
||||||
Transport::Transport(talk_base::Thread* signaling_thread,
|
Transport::Transport(talk_base::Thread* signaling_thread,
|
||||||
talk_base::Thread* worker_thread,
|
talk_base::Thread* worker_thread,
|
||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
@ -133,6 +141,11 @@ void Transport::SetRole(TransportRole role) {
|
|||||||
worker_thread()->Send(this, MSG_SETROLE, ¶m);
|
worker_thread()->Send(this, MSG_SETROLE, ¶m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Transport::SetIdentity(talk_base::SSLIdentity* identity) {
|
||||||
|
IdentityParam params(identity);
|
||||||
|
worker_thread()->Send(this, MSG_SETIDENTITY, ¶ms);
|
||||||
|
}
|
||||||
|
|
||||||
bool Transport::SetLocalTransportDescription(
|
bool Transport::SetLocalTransportDescription(
|
||||||
const TransportDescription& description, ContentAction action) {
|
const TransportDescription& description, ContentAction action) {
|
||||||
TransportDescriptionParams params(description, action);
|
TransportDescriptionParams params(description, action);
|
||||||
@ -801,6 +814,11 @@ void Transport::OnMessage(talk_base::Message* msg) {
|
|||||||
params->result = GetStats_w(params->stats);
|
params->result = GetStats_w(params->stats);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MSG_SETIDENTITY: {
|
||||||
|
IdentityParam* params = static_cast<IdentityParam*>(msg->pdata);
|
||||||
|
SetIdentity_w(params->identity);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,6 +250,9 @@ class Transport : public talk_base::MessageHandler,
|
|||||||
void SetTiebreaker(uint64 tiebreaker) { tiebreaker_ = tiebreaker; }
|
void SetTiebreaker(uint64 tiebreaker) { tiebreaker_ = tiebreaker; }
|
||||||
uint64 tiebreaker() { return tiebreaker_; }
|
uint64 tiebreaker() { return tiebreaker_; }
|
||||||
|
|
||||||
|
// Must be called before applying local session description.
|
||||||
|
void SetIdentity(talk_base::SSLIdentity* identity);
|
||||||
|
|
||||||
TransportProtocol protocol() const { return protocol_; }
|
TransportProtocol protocol() const { return protocol_; }
|
||||||
|
|
||||||
// Create, destroy, and lookup the channels of this type by their components.
|
// Create, destroy, and lookup the channels of this type by their components.
|
||||||
@ -348,6 +351,8 @@ class Transport : public talk_base::MessageHandler,
|
|||||||
return remote_description_.get();
|
return remote_description_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void SetIdentity_w(talk_base::SSLIdentity* identity) {}
|
||||||
|
|
||||||
// Pushes down the transport parameters from the local description, such
|
// Pushes down the transport parameters from the local description, such
|
||||||
// as the ICE ufrag and pwd.
|
// as the ICE ufrag and pwd.
|
||||||
// Derived classes can override, but must call the base as well.
|
// Derived classes can override, but must call the base as well.
|
||||||
|
@ -60,12 +60,13 @@ inline bool IsTurnChannelData(uint16 msg_type) {
|
|||||||
return ((msg_type & 0xC000) == 0x4000); // MSB are 0b01
|
return ((msg_type & 0xC000) == 0x4000); // MSB are 0b01
|
||||||
}
|
}
|
||||||
|
|
||||||
static int GetRelayPreference(cricket::ProtocolType proto) {
|
static int GetRelayPreference(cricket::ProtocolType proto, bool secure) {
|
||||||
int relay_preference = ICE_TYPE_PREFERENCE_RELAY;
|
int relay_preference = ICE_TYPE_PREFERENCE_RELAY;
|
||||||
if (proto == cricket::PROTO_TCP)
|
if (proto == cricket::PROTO_TCP) {
|
||||||
relay_preference -= 1;
|
relay_preference -= 1;
|
||||||
else if (proto == cricket::PROTO_SSLTCP)
|
if (secure)
|
||||||
relay_preference -= 2;
|
relay_preference -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
ASSERT(relay_preference >= 0);
|
ASSERT(relay_preference >= 0);
|
||||||
return relay_preference;
|
return relay_preference;
|
||||||
@ -223,9 +224,15 @@ void TurnPort::PrepareAddress() {
|
|||||||
socket_.reset(socket_factory()->CreateUdpSocket(
|
socket_.reset(socket_factory()->CreateUdpSocket(
|
||||||
talk_base::SocketAddress(ip(), 0), min_port(), max_port()));
|
talk_base::SocketAddress(ip(), 0), min_port(), max_port()));
|
||||||
} else if (server_address_.proto == PROTO_TCP) {
|
} else if (server_address_.proto == PROTO_TCP) {
|
||||||
|
int opts = talk_base::PacketSocketFactory::OPT_STUN;
|
||||||
|
// If secure bit is enabled in server address, use TLS over TCP.
|
||||||
|
if (server_address_.secure) {
|
||||||
|
opts |= talk_base::PacketSocketFactory::OPT_TLS;
|
||||||
|
}
|
||||||
|
|
||||||
socket_.reset(socket_factory()->CreateClientTcpSocket(
|
socket_.reset(socket_factory()->CreateClientTcpSocket(
|
||||||
talk_base::SocketAddress(ip(), 0), server_address_.address,
|
talk_base::SocketAddress(ip(), 0), server_address_.address,
|
||||||
proxy(), user_agent(), talk_base::PacketSocketFactory::OPT_STUN));
|
proxy(), user_agent(), opts));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!socket_) {
|
if (!socket_) {
|
||||||
@ -412,8 +419,12 @@ void TurnPort::OnStunAddress(const talk_base::SocketAddress& address) {
|
|||||||
|
|
||||||
void TurnPort::OnAllocateSuccess(const talk_base::SocketAddress& address) {
|
void TurnPort::OnAllocateSuccess(const talk_base::SocketAddress& address) {
|
||||||
connected_ = true;
|
connected_ = true;
|
||||||
AddAddress(address, socket_->GetLocalAddress(), "udp",
|
AddAddress(address,
|
||||||
RELAY_PORT_TYPE, GetRelayPreference(server_address_.proto), true);
|
socket_->GetLocalAddress(),
|
||||||
|
"udp",
|
||||||
|
RELAY_PORT_TYPE,
|
||||||
|
GetRelayPreference(server_address_.proto, server_address_.secure),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TurnPort::OnAllocateError() {
|
void TurnPort::OnAllocateError() {
|
||||||
|
@ -66,10 +66,10 @@ static const char kTurnUsername[] = "test";
|
|||||||
static const char kTurnPassword[] = "test";
|
static const char kTurnPassword[] = "test";
|
||||||
static const int kTimeout = 1000;
|
static const int kTimeout = 1000;
|
||||||
|
|
||||||
static const cricket::ProtocolAddress kTurnUdpProtoAddr(kTurnUdpIntAddr,
|
static const cricket::ProtocolAddress kTurnUdpProtoAddr(
|
||||||
cricket::PROTO_UDP);
|
kTurnUdpIntAddr, cricket::PROTO_UDP);
|
||||||
static const cricket::ProtocolAddress kTurnTcpProtoAddr(kTurnTcpIntAddr,
|
static const cricket::ProtocolAddress kTurnTcpProtoAddr(
|
||||||
cricket::PROTO_TCP);
|
kTurnTcpIntAddr, cricket::PROTO_TCP);
|
||||||
|
|
||||||
class TurnPortTest : public testing::Test,
|
class TurnPortTest : public testing::Test,
|
||||||
public sigslot::has_slots<> {
|
public sigslot::has_slots<> {
|
||||||
@ -295,6 +295,7 @@ TEST_F(TurnPortTest, TestTurnConnection) {
|
|||||||
TestTurnConnection();
|
TestTurnConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that we can establish a TCP connection with TURN server.
|
||||||
TEST_F(TurnPortTest, TestTurnTcpConnection) {
|
TEST_F(TurnPortTest, TestTurnTcpConnection) {
|
||||||
talk_base::AsyncSocket* tcp_server_socket =
|
talk_base::AsyncSocket* tcp_server_socket =
|
||||||
CreateServerSocket(kTurnTcpIntAddr);
|
CreateServerSocket(kTurnTcpIntAddr);
|
||||||
@ -304,6 +305,18 @@ TEST_F(TurnPortTest, TestTurnTcpConnection) {
|
|||||||
TestTurnConnection();
|
TestTurnConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that we fail to create a connection when we want to use TLS over TCP.
|
||||||
|
// This test should be removed once we have TLS support.
|
||||||
|
TEST_F(TurnPortTest, TestTurnTlsTcpConnectionFails) {
|
||||||
|
cricket::ProtocolAddress secure_addr(kTurnTcpProtoAddr.address,
|
||||||
|
kTurnTcpProtoAddr.proto,
|
||||||
|
true);
|
||||||
|
CreateTurnPort(kTurnUsername, kTurnPassword, secure_addr);
|
||||||
|
turn_port_->PrepareAddress();
|
||||||
|
EXPECT_TRUE_WAIT(turn_error_, kTimeout);
|
||||||
|
ASSERT_EQ(0U, turn_port_->Candidates().size());
|
||||||
|
}
|
||||||
|
|
||||||
// Run TurnConnectionTest with one-time-use nonce feature.
|
// Run TurnConnectionTest with one-time-use nonce feature.
|
||||||
// Here server will send a 438 STALE_NONCE error message for
|
// Here server will send a 438 STALE_NONCE error message for
|
||||||
// every TURN transaction.
|
// every TURN transaction.
|
||||||
|
@ -264,9 +264,6 @@ void TurnServer::AcceptConnection(talk_base::AsyncSocket* server_socket) {
|
|||||||
talk_base::AsyncSocket* accepted_socket = server_socket->Accept(&accept_addr);
|
talk_base::AsyncSocket* accepted_socket = server_socket->Accept(&accept_addr);
|
||||||
if (accepted_socket != NULL) {
|
if (accepted_socket != NULL) {
|
||||||
ProtocolType proto = server_listen_sockets_[server_socket];
|
ProtocolType proto = server_listen_sockets_[server_socket];
|
||||||
if (proto == PROTO_SSLTCP) {
|
|
||||||
accepted_socket = new talk_base::AsyncSSLServerSocket(accepted_socket);
|
|
||||||
}
|
|
||||||
cricket::AsyncStunTCPSocket* tcp_socket =
|
cricket::AsyncStunTCPSocket* tcp_socket =
|
||||||
new cricket::AsyncStunTCPSocket(accepted_socket, false);
|
new cricket::AsyncStunTCPSocket(accepted_socket, false);
|
||||||
|
|
||||||
|
@ -99,13 +99,6 @@ class BasicPortAllocator : public PortAllocator {
|
|||||||
const std::string& ice_ufrag,
|
const std::string& ice_ufrag,
|
||||||
const std::string& ice_pwd);
|
const std::string& ice_pwd);
|
||||||
|
|
||||||
bool allow_tcp_listen() const {
|
|
||||||
return allow_tcp_listen_;
|
|
||||||
}
|
|
||||||
void set_allow_tcp_listen(bool allow_tcp_listen) {
|
|
||||||
allow_tcp_listen_ = allow_tcp_listen;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Construct();
|
void Construct();
|
||||||
|
|
||||||
|
@ -440,6 +440,25 @@ void ParsePayloadTypeParameters(const buzz::XmlElement* element,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParseFeedbackParams(const buzz::XmlElement* element,
|
||||||
|
FeedbackParams* params) {
|
||||||
|
for (const buzz::XmlElement* param = element->FirstNamed(QN_JINGLE_RTCP_FB);
|
||||||
|
param != NULL; param = param->NextNamed(QN_JINGLE_RTCP_FB)) {
|
||||||
|
std::string type = GetXmlAttr(param, QN_TYPE, buzz::STR_EMPTY);
|
||||||
|
std::string subtype = GetXmlAttr(param, QN_SUBTYPE, buzz::STR_EMPTY);
|
||||||
|
if (!type.empty()) {
|
||||||
|
params->Add(FeedbackParam(type, subtype));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddFeedbackParams(const FeedbackParams& additional_params,
|
||||||
|
FeedbackParams* params) {
|
||||||
|
for (size_t i = 0; i < additional_params.params().size(); ++i) {
|
||||||
|
params->Add(additional_params.params()[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int FindWithDefault(const std::map<std::string, std::string>& map,
|
int FindWithDefault(const std::map<std::string, std::string>& map,
|
||||||
const std::string& key, const int def) {
|
const std::string& key, const int def) {
|
||||||
std::map<std::string, std::string>::const_iterator iter = map.find(key);
|
std::map<std::string, std::string>::const_iterator iter = map.find(key);
|
||||||
@ -488,6 +507,7 @@ bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) {
|
|||||||
int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0);
|
int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0);
|
||||||
|
|
||||||
*codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
|
*codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
|
||||||
|
ParseFeedbackParams(elem, &codec->feedback_params);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,6 +526,7 @@ bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) {
|
|||||||
|
|
||||||
*codec = VideoCodec(id, name, width, height, framerate, 0);
|
*codec = VideoCodec(id, name, width, height, framerate, 0);
|
||||||
codec->params = paramap;
|
codec->params = paramap;
|
||||||
|
ParseFeedbackParams(elem, &codec->feedback_params);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,6 +538,7 @@ bool ParseJingleDataCodec(const buzz::XmlElement* elem, DataCodec* codec) {
|
|||||||
std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
|
std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
|
||||||
|
|
||||||
*codec = DataCodec(id, name, 0);
|
*codec = DataCodec(id, name, 0);
|
||||||
|
ParseFeedbackParams(elem, &codec->feedback_params);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,12 +565,16 @@ bool ParseJingleAudioContent(const buzz::XmlElement* content_elem,
|
|||||||
talk_base::scoped_ptr<AudioContentDescription> audio(
|
talk_base::scoped_ptr<AudioContentDescription> audio(
|
||||||
new AudioContentDescription());
|
new AudioContentDescription());
|
||||||
|
|
||||||
|
FeedbackParams content_feedback_params;
|
||||||
|
ParseFeedbackParams(content_elem, &content_feedback_params);
|
||||||
|
|
||||||
for (const buzz::XmlElement* payload_elem =
|
for (const buzz::XmlElement* payload_elem =
|
||||||
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
|
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
|
||||||
payload_elem != NULL;
|
payload_elem != NULL;
|
||||||
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
|
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
|
||||||
AudioCodec codec;
|
AudioCodec codec;
|
||||||
if (ParseJingleAudioCodec(payload_elem, &codec)) {
|
if (ParseJingleAudioCodec(payload_elem, &codec)) {
|
||||||
|
AddFeedbackParams(content_feedback_params, &codec.feedback_params);
|
||||||
audio->AddCodec(codec);
|
audio->AddCodec(codec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -579,12 +605,16 @@ bool ParseJingleVideoContent(const buzz::XmlElement* content_elem,
|
|||||||
talk_base::scoped_ptr<VideoContentDescription> video(
|
talk_base::scoped_ptr<VideoContentDescription> video(
|
||||||
new VideoContentDescription());
|
new VideoContentDescription());
|
||||||
|
|
||||||
|
FeedbackParams content_feedback_params;
|
||||||
|
ParseFeedbackParams(content_elem, &content_feedback_params);
|
||||||
|
|
||||||
for (const buzz::XmlElement* payload_elem =
|
for (const buzz::XmlElement* payload_elem =
|
||||||
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
|
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
|
||||||
payload_elem != NULL;
|
payload_elem != NULL;
|
||||||
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
|
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
|
||||||
VideoCodec codec;
|
VideoCodec codec;
|
||||||
if (ParseJingleVideoCodec(payload_elem, &codec)) {
|
if (ParseJingleVideoCodec(payload_elem, &codec)) {
|
||||||
|
AddFeedbackParams(content_feedback_params, &codec.feedback_params);
|
||||||
video->AddCodec(codec);
|
video->AddCodec(codec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -645,12 +675,16 @@ bool ParseJingleRtpDataContent(const buzz::XmlElement* content_elem,
|
|||||||
ParseError* error) {
|
ParseError* error) {
|
||||||
DataContentDescription* data = new DataContentDescription();
|
DataContentDescription* data = new DataContentDescription();
|
||||||
|
|
||||||
|
FeedbackParams content_feedback_params;
|
||||||
|
ParseFeedbackParams(content_elem, &content_feedback_params);
|
||||||
|
|
||||||
for (const buzz::XmlElement* payload_elem =
|
for (const buzz::XmlElement* payload_elem =
|
||||||
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
|
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
|
||||||
payload_elem != NULL;
|
payload_elem != NULL;
|
||||||
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
|
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
|
||||||
DataCodec codec;
|
DataCodec codec;
|
||||||
if (ParseJingleDataCodec(payload_elem, &codec)) {
|
if (ParseJingleDataCodec(payload_elem, &codec)) {
|
||||||
|
AddFeedbackParams(content_feedback_params, &codec.feedback_params);
|
||||||
data->AddCodec(codec);
|
data->AddCodec(codec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -853,6 +887,18 @@ buzz::XmlElement* CreatePayloadTypeParameterElem(
|
|||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddRtcpFeedbackElem(buzz::XmlElement* elem,
|
||||||
|
const FeedbackParams& feedback_params) {
|
||||||
|
std::vector<FeedbackParam>::const_iterator it;
|
||||||
|
for (it = feedback_params.params().begin();
|
||||||
|
it != feedback_params.params().end(); ++it) {
|
||||||
|
buzz::XmlElement* fb_elem = new buzz::XmlElement(QN_JINGLE_RTCP_FB);
|
||||||
|
fb_elem->AddAttr(QN_TYPE, it->id());
|
||||||
|
fb_elem->AddAttr(QN_SUBTYPE, it->param());
|
||||||
|
elem->AddElement(fb_elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
|
buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
|
||||||
buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
|
buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
|
||||||
|
|
||||||
@ -869,6 +915,8 @@ buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
|
|||||||
AddXmlAttr(elem, QN_CHANNELS, codec.channels);
|
AddXmlAttr(elem, QN_CHANNELS, codec.channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddRtcpFeedbackElem(elem, codec.feedback_params);
|
||||||
|
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -883,6 +931,9 @@ buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) {
|
|||||||
PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
|
PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
|
||||||
elem->AddElement(CreatePayloadTypeParameterElem(
|
elem->AddElement(CreatePayloadTypeParameterElem(
|
||||||
PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
|
PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
|
||||||
|
|
||||||
|
AddRtcpFeedbackElem(elem, codec.feedback_params);
|
||||||
|
|
||||||
CodecParameterMap::const_iterator param_iter;
|
CodecParameterMap::const_iterator param_iter;
|
||||||
for (param_iter = codec.params.begin(); param_iter != codec.params.end();
|
for (param_iter = codec.params.begin(); param_iter != codec.params.end();
|
||||||
++param_iter) {
|
++param_iter) {
|
||||||
@ -899,6 +950,8 @@ buzz::XmlElement* CreateJingleDataCodecElem(const DataCodec& codec) {
|
|||||||
AddXmlAttr(elem, QN_ID, codec.id);
|
AddXmlAttr(elem, QN_ID, codec.id);
|
||||||
elem->AddAttr(QN_NAME, codec.name);
|
elem->AddAttr(QN_NAME, codec.name);
|
||||||
|
|
||||||
|
AddRtcpFeedbackElem(elem, codec.feedback_params);
|
||||||
|
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,16 @@
|
|||||||
#include "talk/xmllite/xmlprinter.h"
|
#include "talk/xmllite/xmlprinter.h"
|
||||||
#include "talk/xmpp/constants.h"
|
#include "talk/xmpp/constants.h"
|
||||||
|
|
||||||
|
using cricket::AudioCodec;
|
||||||
|
using cricket::AudioContentDescription;
|
||||||
|
using cricket::Codec;
|
||||||
|
using cricket::DataCodec;
|
||||||
|
using cricket::DataContentDescription;
|
||||||
|
using cricket::FeedbackParam;
|
||||||
|
using cricket::FeedbackParams;
|
||||||
|
using cricket::VideoCodec;
|
||||||
|
using cricket::VideoContentDescription;
|
||||||
|
|
||||||
// The codecs that our FakeMediaEngine will support. Order is important, since
|
// The codecs that our FakeMediaEngine will support. Order is important, since
|
||||||
// the tests check that our messages have codecs in the correct order.
|
// the tests check that our messages have codecs in the correct order.
|
||||||
static const cricket::AudioCodec kAudioCodecs[] = {
|
static const cricket::AudioCodec kAudioCodecs[] = {
|
||||||
@ -377,6 +387,42 @@ const std::string kJingleInitiateDifferentPreference(
|
|||||||
" </jingle> " \
|
" </jingle> " \
|
||||||
"</iq> ");
|
"</iq> ");
|
||||||
|
|
||||||
|
const std::string kJingleInitiateWithRtcpFb(
|
||||||
|
"<iq xmlns='jabber:client' from='me@domain.com/resource' " \
|
||||||
|
" to='user@domain.com/resource' type='set' id='123'> " \
|
||||||
|
" <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \
|
||||||
|
" sid='abcdef' initiator='me@domain.com/resource'> " \
|
||||||
|
" <content name='test audio'> " \
|
||||||
|
" <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'> " \
|
||||||
|
" <payload-type id='103' name='ISAC' clockrate='16000'> " \
|
||||||
|
" <rtcp-fb type='nack'/> " \
|
||||||
|
" </payload-type> " \
|
||||||
|
" </description> " \
|
||||||
|
" <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \
|
||||||
|
" </content> " \
|
||||||
|
" <content name='test video'> " \
|
||||||
|
" <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='video'> " \
|
||||||
|
" <rtcp-fb type='nack'/> " \
|
||||||
|
" <payload-type id='99' name='H264-SVC'> " \
|
||||||
|
" <rtcp-fb type='ccm' subtype='fir'/> " \
|
||||||
|
" <parameter name='height' value='200'/> " \
|
||||||
|
" <parameter name='width' value='320'/> " \
|
||||||
|
" <parameter name='framerate' value='30'/> " \
|
||||||
|
" </payload-type> " \
|
||||||
|
" </description> " \
|
||||||
|
" <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \
|
||||||
|
" </content> " \
|
||||||
|
" <content name='test data'> " \
|
||||||
|
" <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='data'> " \
|
||||||
|
" <rtcp-fb type='nack'/> " \
|
||||||
|
" <payload-type id='127' name='google-data'> " \
|
||||||
|
" </payload-type> " \
|
||||||
|
" </description> " \
|
||||||
|
" <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \
|
||||||
|
" </content> " \
|
||||||
|
" </jingle> " \
|
||||||
|
"</iq> ");
|
||||||
|
|
||||||
const std::string kGingleVideoInitiate(
|
const std::string kGingleVideoInitiate(
|
||||||
"<iq xmlns='jabber:client' from='me@domain.com/resource' " \
|
"<iq xmlns='jabber:client' from='me@domain.com/resource' " \
|
||||||
" to='user@domain.com/resource' type='set' id='123'> " \
|
" to='user@domain.com/resource' type='set' id='123'> " \
|
||||||
@ -1151,7 +1197,7 @@ static cricket::CallOptions VideoCallOptions() {
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
buzz::XmlElement* CopyElement(const buzz::XmlElement* elem) {
|
static buzz::XmlElement* CopyElement(const buzz::XmlElement* elem) {
|
||||||
return new buzz::XmlElement(*elem);
|
return new buzz::XmlElement(*elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1164,7 +1210,7 @@ static std::string AddEncryption(std::string stanza, std::string encryption) {
|
|||||||
return stanza;
|
return stanza;
|
||||||
}
|
}
|
||||||
|
|
||||||
int IntFromJingleCodecParameter(const buzz::XmlElement* parameter,
|
static int IntFromJingleCodecParameter(const buzz::XmlElement* parameter,
|
||||||
const std::string& expected_name) {
|
const std::string& expected_name) {
|
||||||
if (parameter) {
|
if (parameter) {
|
||||||
const std::string& actual_name =
|
const std::string& actual_name =
|
||||||
@ -1181,6 +1227,18 @@ int IntFromJingleCodecParameter(const buzz::XmlElement* parameter,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class CodecClass, class DescriptionClass>
|
||||||
|
static void VerifyCodecFbParams(const FeedbackParams& expected,
|
||||||
|
const DescriptionClass* desc) {
|
||||||
|
if (!expected.params().empty()) {
|
||||||
|
ASSERT_TRUE(desc != NULL);
|
||||||
|
const std::vector<CodecClass> codecs = desc->codecs();
|
||||||
|
for (size_t i = 0; i < codecs.size(); ++i) {
|
||||||
|
EXPECT_EQ(expected, codecs[i].feedback_params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parses and extracts payload and codec info from test XML. Since
|
// Parses and extracts payload and codec info from test XML. Since
|
||||||
// that XML will be in various contents (Gingle and Jingle), we need an
|
// that XML will be in various contents (Gingle and Jingle), we need an
|
||||||
// abstract parser with one concrete implementation per XML content.
|
// abstract parser with one concrete implementation per XML content.
|
||||||
@ -1250,6 +1308,20 @@ class JingleSessionTestParser : public MediaSessionTestParser {
|
|||||||
return payload_type->NextNamed(cricket::QN_JINGLE_RTP_PAYLOADTYPE);
|
return payload_type->NextNamed(cricket::QN_JINGLE_RTP_PAYLOADTYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParsePayloadTypeFeedbackParameters(const buzz::XmlElement* element,
|
||||||
|
FeedbackParams* params) {
|
||||||
|
const buzz::XmlElement* param =
|
||||||
|
element->FirstNamed(cricket::QN_JINGLE_RTCP_FB);
|
||||||
|
for (; param != NULL;
|
||||||
|
param = param->NextNamed(cricket::QN_JINGLE_RTCP_FB)) {
|
||||||
|
std::string type = param->Attr(cricket::QN_TYPE);
|
||||||
|
std::string subtype = param->Attr(cricket::QN_SUBTYPE);
|
||||||
|
if (!type.empty()) {
|
||||||
|
params->Add(FeedbackParam(type, subtype));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cricket::AudioCodec AudioCodecFromPayloadType(
|
cricket::AudioCodec AudioCodecFromPayloadType(
|
||||||
const buzz::XmlElement* payload_type) {
|
const buzz::XmlElement* payload_type) {
|
||||||
int id = 0;
|
int id = 0;
|
||||||
@ -1272,7 +1344,9 @@ class JingleSessionTestParser : public MediaSessionTestParser {
|
|||||||
channels = atoi(payload_type->Attr(
|
channels = atoi(payload_type->Attr(
|
||||||
cricket::QN_CHANNELS).c_str());
|
cricket::QN_CHANNELS).c_str());
|
||||||
|
|
||||||
return cricket::AudioCodec(id, name, clockrate, bitrate, channels, 0);
|
AudioCodec codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
|
||||||
|
ParsePayloadTypeFeedbackParameters(payload_type, &codec.feedback_params);
|
||||||
|
return codec;
|
||||||
}
|
}
|
||||||
|
|
||||||
cricket::VideoCodec VideoCodecFromPayloadType(
|
cricket::VideoCodec VideoCodecFromPayloadType(
|
||||||
@ -1301,8 +1375,9 @@ class JingleSessionTestParser : public MediaSessionTestParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
VideoCodec codec = VideoCodec(id, name, width, height, framerate, 0);
|
||||||
return cricket::VideoCodec(id, name, width, height, framerate, 0);
|
ParsePayloadTypeFeedbackParameters(payload_type, &codec.feedback_params);
|
||||||
|
return codec;
|
||||||
}
|
}
|
||||||
|
|
||||||
cricket::DataCodec DataCodecFromPayloadType(
|
cricket::DataCodec DataCodecFromPayloadType(
|
||||||
@ -1315,7 +1390,9 @@ class JingleSessionTestParser : public MediaSessionTestParser {
|
|||||||
if (payload_type->HasAttr(cricket::QN_NAME))
|
if (payload_type->HasAttr(cricket::QN_NAME))
|
||||||
name = payload_type->Attr(cricket::QN_NAME);
|
name = payload_type->Attr(cricket::QN_NAME);
|
||||||
|
|
||||||
return cricket::DataCodec(id, name, 0);
|
DataCodec codec = DataCodec(id, name, 0);
|
||||||
|
ParsePayloadTypeFeedbackParameters(payload_type, &codec.feedback_params);
|
||||||
|
return codec;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ActionIsTerminate(const buzz::XmlElement* action) {
|
bool ActionIsTerminate(const buzz::XmlElement* action) {
|
||||||
@ -1482,14 +1559,24 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
fme_ = new cricket::FakeMediaEngine();
|
fme_ = new cricket::FakeMediaEngine();
|
||||||
fdme_ = new cricket::FakeDataEngine();
|
fdme_ = new cricket::FakeDataEngine();
|
||||||
|
|
||||||
|
FeedbackParams params_nack_fir;
|
||||||
|
params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamCcm,
|
||||||
|
cricket::kRtcpFbCcmParamFir));
|
||||||
|
params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamNack));
|
||||||
|
FeedbackParams params_nack;
|
||||||
|
params_nack.Add(FeedbackParam(cricket::kRtcpFbParamNack));
|
||||||
|
|
||||||
std::vector<cricket::AudioCodec>
|
std::vector<cricket::AudioCodec>
|
||||||
audio_codecs(kAudioCodecs, kAudioCodecs + ARRAY_SIZE(kAudioCodecs));
|
audio_codecs(kAudioCodecs, kAudioCodecs + ARRAY_SIZE(kAudioCodecs));
|
||||||
|
SetCodecFeedbackParams(&audio_codecs, params_nack);
|
||||||
fme_->SetAudioCodecs(audio_codecs);
|
fme_->SetAudioCodecs(audio_codecs);
|
||||||
std::vector<cricket::VideoCodec>
|
std::vector<cricket::VideoCodec>
|
||||||
video_codecs(kVideoCodecs, kVideoCodecs + ARRAY_SIZE(kVideoCodecs));
|
video_codecs(kVideoCodecs, kVideoCodecs + ARRAY_SIZE(kVideoCodecs));
|
||||||
|
SetCodecFeedbackParams(&video_codecs, params_nack_fir);
|
||||||
fme_->SetVideoCodecs(video_codecs);
|
fme_->SetVideoCodecs(video_codecs);
|
||||||
std::vector<cricket::DataCodec>
|
std::vector<cricket::DataCodec>
|
||||||
data_codecs(kDataCodecs, kDataCodecs + ARRAY_SIZE(kDataCodecs));
|
data_codecs(kDataCodecs, kDataCodecs + ARRAY_SIZE(kDataCodecs));
|
||||||
|
SetCodecFeedbackParams(&data_codecs, params_nack);
|
||||||
fdme_->SetDataCodecs(data_codecs);
|
fdme_->SetDataCodecs(data_codecs);
|
||||||
|
|
||||||
client_ = new cricket::MediaSessionClient(
|
client_ = new cricket::MediaSessionClient(
|
||||||
@ -1551,14 +1638,23 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
return parser_->AudioCodecFromPayloadType(payload_type);
|
return parser_->AudioCodecFromPayloadType(payload_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cricket::AudioContentDescription* GetFirstAudioContentDescription(
|
cricket::VideoCodec VideoCodecFromPayloadType(
|
||||||
|
const buzz::XmlElement* payload_type) {
|
||||||
|
return parser_->VideoCodecFromPayloadType(payload_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
cricket::DataCodec DataCodecFromPayloadType(
|
||||||
|
const buzz::XmlElement* payload_type) {
|
||||||
|
return parser_->DataCodecFromPayloadType(payload_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
const AudioContentDescription* GetFirstAudioContentDescription(
|
||||||
const cricket::SessionDescription* sdesc) {
|
const cricket::SessionDescription* sdesc) {
|
||||||
const cricket::ContentInfo* content =
|
const cricket::ContentInfo* content =
|
||||||
cricket::GetFirstAudioContent(sdesc);
|
cricket::GetFirstAudioContent(sdesc);
|
||||||
if (content == NULL)
|
if (content == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
return static_cast<const cricket::AudioContentDescription*>(
|
return static_cast<const AudioContentDescription*>(content->description);
|
||||||
content->description);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cricket::VideoContentDescription* GetFirstVideoContentDescription(
|
const cricket::VideoContentDescription* GetFirstVideoContentDescription(
|
||||||
@ -1573,7 +1669,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
|
|
||||||
void CheckCryptoFromGoodIncomingInitiate(const cricket::Session* session) {
|
void CheckCryptoFromGoodIncomingInitiate(const cricket::Session* session) {
|
||||||
ASSERT_TRUE(session != NULL);
|
ASSERT_TRUE(session != NULL);
|
||||||
const cricket::AudioContentDescription* content =
|
const AudioContentDescription* content =
|
||||||
GetFirstAudioContentDescription(session->remote_description());
|
GetFirstAudioContentDescription(session->remote_description());
|
||||||
ASSERT_TRUE(content != NULL);
|
ASSERT_TRUE(content != NULL);
|
||||||
ASSERT_EQ(2U, content->cryptos().size());
|
ASSERT_EQ(2U, content->cryptos().size());
|
||||||
@ -1588,7 +1684,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CheckCryptoForGoodOutgoingAccept(const cricket::Session* session) {
|
void CheckCryptoForGoodOutgoingAccept(const cricket::Session* session) {
|
||||||
const cricket::AudioContentDescription* content =
|
const AudioContentDescription* content =
|
||||||
GetFirstAudioContentDescription(session->local_description());
|
GetFirstAudioContentDescription(session->local_description());
|
||||||
ASSERT_EQ(1U, content->cryptos().size());
|
ASSERT_EQ(1U, content->cryptos().size());
|
||||||
ASSERT_EQ(145, content->cryptos()[0].tag);
|
ASSERT_EQ(145, content->cryptos()[0].tag);
|
||||||
@ -1597,7 +1693,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CheckBadCryptoFromIncomingInitiate(const cricket::Session* session) {
|
void CheckBadCryptoFromIncomingInitiate(const cricket::Session* session) {
|
||||||
const cricket::AudioContentDescription* content =
|
const AudioContentDescription* content =
|
||||||
GetFirstAudioContentDescription(session->remote_description());
|
GetFirstAudioContentDescription(session->remote_description());
|
||||||
ASSERT_EQ(1U, content->cryptos().size());
|
ASSERT_EQ(1U, content->cryptos().size());
|
||||||
ASSERT_EQ(145, content->cryptos()[0].tag);
|
ASSERT_EQ(145, content->cryptos()[0].tag);
|
||||||
@ -1607,11 +1703,22 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CheckNoCryptoForOutgoingAccept(const cricket::Session* session) {
|
void CheckNoCryptoForOutgoingAccept(const cricket::Session* session) {
|
||||||
const cricket::AudioContentDescription* content =
|
const AudioContentDescription* content =
|
||||||
GetFirstAudioContentDescription(session->local_description());
|
GetFirstAudioContentDescription(session->local_description());
|
||||||
ASSERT_TRUE(content->cryptos().empty());
|
ASSERT_TRUE(content->cryptos().empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckRtcpFb(const cricket::SessionDescription* sdesc) {
|
||||||
|
VerifyCodecFbParams<AudioCodec>(expected_audio_fb_params_,
|
||||||
|
GetFirstAudioContentDescription(sdesc));
|
||||||
|
|
||||||
|
VerifyCodecFbParams<VideoCodec>(expected_video_fb_params_,
|
||||||
|
GetFirstVideoContentDescription(sdesc));
|
||||||
|
|
||||||
|
VerifyCodecFbParams<DataCodec>(expected_data_fb_params_,
|
||||||
|
GetFirstDataContentDescription(sdesc));
|
||||||
|
}
|
||||||
|
|
||||||
void CheckVideoBandwidth(int expected_bandwidth,
|
void CheckVideoBandwidth(int expected_bandwidth,
|
||||||
const cricket::SessionDescription* sdesc) {
|
const cricket::SessionDescription* sdesc) {
|
||||||
const cricket::VideoContentDescription* video =
|
const cricket::VideoContentDescription* video =
|
||||||
@ -1638,9 +1745,10 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
|
|
||||||
buzz::XmlElement* e = PayloadTypeFromContent(content);
|
buzz::XmlElement* e = PayloadTypeFromContent(content);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
cricket::DataCodec codec = parser_->DataCodecFromPayloadType(e);
|
cricket::DataCodec codec = DataCodecFromPayloadType(e);
|
||||||
EXPECT_EQ(127, codec.id);
|
EXPECT_EQ(127, codec.id);
|
||||||
EXPECT_EQ("google-data", codec.name);
|
EXPECT_EQ("google-data", codec.name);
|
||||||
|
EXPECT_EQ(expected_data_fb_params_, codec.feedback_params);
|
||||||
|
|
||||||
CheckDataRtcpMux(true, call_->sessions()[0]->local_description());
|
CheckDataRtcpMux(true, call_->sessions()[0]->local_description());
|
||||||
CheckDataRtcpMux(true, call_->sessions()[0]->remote_description());
|
CheckDataRtcpMux(true, call_->sessions()[0]->remote_description());
|
||||||
@ -1675,7 +1783,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CheckAudioSsrcForIncomingAccept(const cricket::Session* session) {
|
void CheckAudioSsrcForIncomingAccept(const cricket::Session* session) {
|
||||||
const cricket::AudioContentDescription* audio =
|
const AudioContentDescription* audio =
|
||||||
GetFirstAudioContentDescription(session->remote_description());
|
GetFirstAudioContentDescription(session->remote_description());
|
||||||
ASSERT_TRUE(audio != NULL);
|
ASSERT_TRUE(audio != NULL);
|
||||||
ASSERT_EQ(kAudioSsrc, audio->first_ssrc());
|
ASSERT_EQ(kAudioSsrc, audio->first_ssrc());
|
||||||
@ -1716,6 +1824,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
call_->sessions()[0]->remote_description());
|
call_->sessions()[0]->remote_description());
|
||||||
CheckVideoRtcpMux(expected_video_rtcp_mux_,
|
CheckVideoRtcpMux(expected_video_rtcp_mux_,
|
||||||
call_->sessions()[0]->remote_description());
|
call_->sessions()[0]->remote_description());
|
||||||
|
CheckRtcpFb(call_->sessions()[0]->remote_description());
|
||||||
if (expect_incoming_crypto_) {
|
if (expect_incoming_crypto_) {
|
||||||
CheckCryptoFromGoodIncomingInitiate(call_->sessions()[0]);
|
CheckCryptoFromGoodIncomingInitiate(call_->sessions()[0]);
|
||||||
}
|
}
|
||||||
@ -1841,7 +1950,23 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
ClearStanzas();
|
ClearStanzas();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VerifyAudioCodec(const AudioCodec& codec, int id,
|
||||||
|
const std::string& name, int clockrate,
|
||||||
|
int bitrate, int channels) {
|
||||||
|
ASSERT_EQ(id, codec.id);
|
||||||
|
ASSERT_EQ(name, codec.name);
|
||||||
|
ASSERT_EQ(clockrate, codec.clockrate);
|
||||||
|
ASSERT_EQ(bitrate, codec.bitrate);
|
||||||
|
ASSERT_EQ(channels, codec.channels);
|
||||||
|
ASSERT_EQ(expected_audio_fb_params_, codec.feedback_params);
|
||||||
|
}
|
||||||
|
|
||||||
void TestGoodOutgoingInitiate(const cricket::CallOptions& options) {
|
void TestGoodOutgoingInitiate(const cricket::CallOptions& options) {
|
||||||
|
if (initial_protocol_ == cricket::PROTOCOL_JINGLE) {
|
||||||
|
// rtcp fb is only implemented for jingle.
|
||||||
|
ExpectRtcpFb();
|
||||||
|
}
|
||||||
|
|
||||||
client_->CreateCall();
|
client_->CreateCall();
|
||||||
ASSERT_TRUE(call_ != NULL);
|
ASSERT_TRUE(call_ != NULL);
|
||||||
call_->InitiateSession(buzz::Jid("me@mydomain.com"),
|
call_->InitiateSession(buzz::Jid("me@mydomain.com"),
|
||||||
@ -1861,159 +1986,92 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
buzz::XmlElement* e = PayloadTypeFromContent(content);
|
buzz::XmlElement* e = PayloadTypeFromContent(content);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
cricket::AudioCodec codec = AudioCodecFromPayloadType(e);
|
cricket::AudioCodec codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(103, codec.id);
|
VerifyAudioCodec(codec, 103, "ISAC", 16000, 0, 1);
|
||||||
ASSERT_EQ("ISAC", codec.name);
|
|
||||||
ASSERT_EQ(16000, codec.clockrate);
|
|
||||||
ASSERT_EQ(0, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(104, codec.id);
|
VerifyAudioCodec(codec, 104, "ISAC", 32000, 0, 1);
|
||||||
ASSERT_EQ("ISAC", codec.name);
|
|
||||||
ASSERT_EQ(32000, codec.clockrate);
|
|
||||||
ASSERT_EQ(0, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(119, codec.id);
|
VerifyAudioCodec(codec, 119, "ISACLC", 16000, 40000, 1);
|
||||||
ASSERT_EQ("ISACLC", codec.name);
|
|
||||||
ASSERT_EQ(16000, codec.clockrate);
|
|
||||||
ASSERT_EQ(40000, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(99, codec.id);
|
VerifyAudioCodec(codec, 99, "speex", 16000, 22000, 1);
|
||||||
ASSERT_EQ("speex", codec.name);
|
|
||||||
ASSERT_EQ(16000, codec.clockrate);
|
|
||||||
ASSERT_EQ(22000, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(97, codec.id);
|
VerifyAudioCodec(codec, 97, "IPCMWB", 16000, 80000, 1);
|
||||||
ASSERT_EQ("IPCMWB", codec.name);
|
|
||||||
ASSERT_EQ(16000, codec.clockrate);
|
|
||||||
ASSERT_EQ(80000, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(9, codec.id);
|
VerifyAudioCodec(codec, 9, "G722", 16000, 64000, 1);
|
||||||
ASSERT_EQ("G722", codec.name);
|
|
||||||
ASSERT_EQ(16000, codec.clockrate);
|
|
||||||
ASSERT_EQ(64000, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(102, codec.id);
|
VerifyAudioCodec(codec, 102, "iLBC", 8000, 13300, 1);
|
||||||
ASSERT_EQ("iLBC", codec.name);
|
|
||||||
ASSERT_EQ(8000, codec.clockrate);
|
|
||||||
ASSERT_EQ(13300, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(98, codec.id);
|
VerifyAudioCodec(codec, 98, "speex", 8000, 11000, 1);
|
||||||
ASSERT_EQ("speex", codec.name);
|
|
||||||
ASSERT_EQ(8000, codec.clockrate);
|
|
||||||
ASSERT_EQ(11000, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(3, codec.id);
|
VerifyAudioCodec(codec, 3, "GSM", 8000, 13000, 1);
|
||||||
ASSERT_EQ("GSM", codec.name);
|
|
||||||
ASSERT_EQ(8000, codec.clockrate);
|
|
||||||
ASSERT_EQ(13000, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(100, codec.id);
|
VerifyAudioCodec(codec, 100, "EG711U", 8000, 64000, 1);
|
||||||
ASSERT_EQ("EG711U", codec.name);
|
|
||||||
ASSERT_EQ(8000, codec.clockrate);
|
|
||||||
ASSERT_EQ(64000, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(101, codec.id);
|
VerifyAudioCodec(codec, 101, "EG711A", 8000, 64000, 1);
|
||||||
ASSERT_EQ("EG711A", codec.name);
|
|
||||||
ASSERT_EQ(8000, codec.clockrate);
|
|
||||||
ASSERT_EQ(64000, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(0, codec.id);
|
VerifyAudioCodec(codec, 0, "PCMU", 8000, 64000, 1);
|
||||||
ASSERT_EQ("PCMU", codec.name);
|
|
||||||
ASSERT_EQ(8000, codec.clockrate);
|
|
||||||
ASSERT_EQ(64000, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(8, codec.id);
|
VerifyAudioCodec(codec, 8, "PCMA", 8000, 64000, 1);
|
||||||
ASSERT_EQ("PCMA", codec.name);
|
|
||||||
ASSERT_EQ(8000, codec.clockrate);
|
|
||||||
ASSERT_EQ(64000, codec.bitrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(126, codec.id);
|
VerifyAudioCodec(codec, 126, "CN", 32000, 0, 1);
|
||||||
ASSERT_EQ("CN", codec.name);
|
|
||||||
ASSERT_EQ(32000, codec.clockrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(105, codec.id);
|
VerifyAudioCodec(codec, 105, "CN", 16000, 0, 1);
|
||||||
ASSERT_EQ("CN", codec.name);
|
|
||||||
ASSERT_EQ(16000, codec.clockrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(13, codec.id);
|
VerifyAudioCodec(codec, 13, "CN", 8000, 0, 1);
|
||||||
ASSERT_EQ("CN", codec.name);
|
|
||||||
ASSERT_EQ(8000, codec.clockrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(117, codec.id);
|
VerifyAudioCodec(codec, 117, "red", 8000, 0, 1);
|
||||||
ASSERT_EQ("red", codec.name);
|
|
||||||
ASSERT_EQ(8000, codec.clockrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e != NULL);
|
ASSERT_TRUE(e != NULL);
|
||||||
codec = AudioCodecFromPayloadType(e);
|
codec = AudioCodecFromPayloadType(e);
|
||||||
ASSERT_EQ(106, codec.id);
|
VerifyAudioCodec(codec, 106, "telephone-event", 8000, 0, 1);
|
||||||
ASSERT_EQ("telephone-event", codec.name);
|
|
||||||
ASSERT_EQ(8000, codec.clockrate);
|
|
||||||
ASSERT_EQ(1, codec.channels);
|
|
||||||
|
|
||||||
e = NextFromPayloadType(e);
|
e = NextFromPayloadType(e);
|
||||||
ASSERT_TRUE(e == NULL);
|
ASSERT_TRUE(e == NULL);
|
||||||
@ -2073,6 +2131,14 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
ASSERT_EQ(talk_base::ToString(options.video_bandwidth / 1000),
|
ASSERT_EQ(talk_base::ToString(options.video_bandwidth / 1000),
|
||||||
bandwidth->BodyText());
|
bandwidth->BodyText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buzz::XmlElement* e = PayloadTypeFromContent(content);
|
||||||
|
ASSERT_TRUE(e != NULL);
|
||||||
|
VideoCodec codec = VideoCodecFromPayloadType(e);
|
||||||
|
VideoCodec expected_codec = kVideoCodecs[0];
|
||||||
|
expected_codec.preference = codec.preference;
|
||||||
|
expected_codec.feedback_params = expected_video_fb_params_;
|
||||||
|
EXPECT_EQ(expected_codec, codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.data_channel_type == cricket::DCT_RTP) {
|
if (options.data_channel_type == cricket::DCT_RTP) {
|
||||||
@ -2705,6 +2771,28 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
expected_video_rtcp_mux_ = rtcp_mux;
|
expected_video_rtcp_mux_ = rtcp_mux;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class C>
|
||||||
|
void SetCodecFeedbackParams(std::vector<C>* codecs,
|
||||||
|
const FeedbackParams& fb_params) {
|
||||||
|
for (size_t i = 0; i < codecs->size(); ++i) {
|
||||||
|
codecs->at(i).feedback_params = fb_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpectRtcpFb() {
|
||||||
|
FeedbackParams params_nack_fir;
|
||||||
|
params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamCcm,
|
||||||
|
cricket::kRtcpFbCcmParamFir));
|
||||||
|
params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamNack));
|
||||||
|
|
||||||
|
FeedbackParams params_nack;
|
||||||
|
params_nack.Add(FeedbackParam(cricket::kRtcpFbParamNack));
|
||||||
|
|
||||||
|
expected_audio_fb_params_ = params_nack;
|
||||||
|
expected_video_fb_params_ = params_nack_fir;
|
||||||
|
expected_data_fb_params_ = params_nack;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnSendStanza(cricket::SessionManager* manager,
|
void OnSendStanza(cricket::SessionManager* manager,
|
||||||
const buzz::XmlElement* stanza) {
|
const buzz::XmlElement* stanza) {
|
||||||
@ -2749,6 +2837,9 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
|||||||
bool expect_outgoing_crypto_;
|
bool expect_outgoing_crypto_;
|
||||||
int expected_video_bandwidth_;
|
int expected_video_bandwidth_;
|
||||||
bool expected_video_rtcp_mux_;
|
bool expected_video_rtcp_mux_;
|
||||||
|
FeedbackParams expected_audio_fb_params_;
|
||||||
|
FeedbackParams expected_video_fb_params_;
|
||||||
|
FeedbackParams expected_data_fb_params_;
|
||||||
cricket::MediaStreams last_streams_added_;
|
cricket::MediaStreams last_streams_added_;
|
||||||
cricket::MediaStreams last_streams_removed_;
|
cricket::MediaStreams last_streams_removed_;
|
||||||
};
|
};
|
||||||
@ -2763,6 +2854,17 @@ MediaSessionClientTest* JingleTest() {
|
|||||||
cricket::PROTOCOL_JINGLE);
|
cricket::PROTOCOL_JINGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(MediaSessionTest, JingleGoodInitiateWithRtcpFb) {
|
||||||
|
talk_base::scoped_ptr<MediaSessionClientTest> test(JingleTest());
|
||||||
|
talk_base::scoped_ptr<buzz::XmlElement> elem;
|
||||||
|
|
||||||
|
cricket::CallOptions options = VideoCallOptions();
|
||||||
|
options.data_channel_type = cricket::DCT_SCTP;
|
||||||
|
test->ExpectRtcpFb();
|
||||||
|
test->TestGoodIncomingInitiate(
|
||||||
|
kJingleInitiateWithRtcpFb, options, elem.use());
|
||||||
|
}
|
||||||
|
|
||||||
TEST(MediaSessionTest, JingleGoodVideoInitiate) {
|
TEST(MediaSessionTest, JingleGoodVideoInitiate) {
|
||||||
talk_base::scoped_ptr<MediaSessionClientTest> test(JingleTest());
|
talk_base::scoped_ptr<MediaSessionClientTest> test(JingleTest());
|
||||||
talk_base::scoped_ptr<buzz::XmlElement> elem;
|
talk_base::scoped_ptr<buzz::XmlElement> elem;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user