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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (receive_ssrc_set_) {
|
||||
ASSERT(session_->data_channel_type() == cricket::DCT_RTP ||
|
||||
@ -278,9 +309,9 @@ void DataChannel::SetState(DataState state) {
|
||||
}
|
||||
|
||||
void DataChannel::ConnectToDataSession() {
|
||||
ASSERT(session_->data_channel() != NULL);
|
||||
if (!session_->data_channel()) {
|
||||
LOG(LS_ERROR) << "The DataEngine does not exist.";
|
||||
ASSERT(session_->data_channel() != NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -288,9 +319,17 @@ void DataChannel::ConnectToDataSession() {
|
||||
data_session_->SignalReadyToSendData.connect(this,
|
||||
&DataChannel::OnChannelReady);
|
||||
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() {
|
||||
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_->SignalDataReceived.disconnect(this);
|
||||
data_session_ = NULL;
|
||||
@ -318,6 +357,7 @@ void DataChannel::ClearQueuedReceivedData() {
|
||||
}
|
||||
|
||||
void DataChannel::SendQueuedSendData() {
|
||||
DeliverQueuedControlData();
|
||||
if (!was_ever_writable_) {
|
||||
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() {
|
||||
while (!queued_send_data_.empty()) {
|
||||
DataBuffer* buffer = queued_send_data_.front();
|
||||
|
@ -74,6 +74,9 @@ class DataChannel : public DataChannelInterface,
|
||||
virtual void Close();
|
||||
virtual DataState state() const { return state_; }
|
||||
virtual bool Send(const DataBuffer& buffer);
|
||||
// Send a control message right now, or queue for later.
|
||||
virtual bool SendControl(const talk_base::Buffer* buffer);
|
||||
void ConnectToDataSession();
|
||||
|
||||
// Set the SSRC this channel should use to receive data from the
|
||||
// underlying data engine.
|
||||
@ -89,6 +92,10 @@ class DataChannel : public DataChannelInterface,
|
||||
// Called if the underlying data engine is closing.
|
||||
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:
|
||||
DataChannel(WebRtcSession* session, const std::string& label);
|
||||
virtual ~DataChannel();
|
||||
@ -100,15 +107,15 @@ class DataChannel : public DataChannelInterface,
|
||||
void OnDataReceived(cricket::DataChannel* channel,
|
||||
const cricket::ReceiveDataParams& params,
|
||||
const talk_base::Buffer& payload);
|
||||
void OnChannelReady(bool writable);
|
||||
|
||||
private:
|
||||
void DoClose();
|
||||
void UpdateState();
|
||||
void SetState(DataState state);
|
||||
void ConnectToDataSession();
|
||||
void DisconnectFromDataSession();
|
||||
bool IsConnectedToDataSession() { return data_session_ != NULL; }
|
||||
void DeliverQueuedControlData();
|
||||
void QueueControl(const talk_base::Buffer* buffer);
|
||||
void DeliverQueuedReceivedData();
|
||||
void ClearQueuedReceivedData();
|
||||
void SendQueuedSendData();
|
||||
@ -128,6 +135,9 @@ class DataChannel : public DataChannelInterface,
|
||||
uint32 send_ssrc_;
|
||||
bool receive_ssrc_set_;
|
||||
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::deque<DataBuffer*> queued_send_data_;
|
||||
};
|
||||
|
@ -26,6 +26,7 @@
|
||||
*/
|
||||
|
||||
#include "talk/app/webrtc/datachannel.h"
|
||||
#include "talk/app/webrtc/jsep.h"
|
||||
#include "talk/app/webrtc/mediastreamsignaling.h"
|
||||
#include "talk/app/webrtc/test/fakeconstraints.h"
|
||||
#include "talk/app/webrtc/webrtcsession.h"
|
||||
@ -34,10 +35,31 @@
|
||||
#include "talk/media/devices/fakedevicemanager.h"
|
||||
#include "talk/session/media/channelmanager.h"
|
||||
|
||||
using webrtc::CreateSessionDescriptionObserver;
|
||||
using webrtc::MediaConstraintsInterface;
|
||||
using webrtc::SessionDescriptionInterface;
|
||||
|
||||
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 {
|
||||
protected:
|
||||
SctpDataChannelTest()
|
||||
@ -49,13 +71,14 @@ class SctpDataChannelTest : public testing::Test {
|
||||
new cricket::FakeDeviceManager(),
|
||||
new cricket::CaptureManager(),
|
||||
talk_base::Thread::Current())),
|
||||
ms_signaling_(new webrtc::MediaStreamSignaling(
|
||||
talk_base::Thread::Current(), NULL)),
|
||||
media_stream_signaling_(
|
||||
new webrtc::MediaStreamSignaling(talk_base::Thread::Current(),
|
||||
NULL)),
|
||||
session_(channel_manager_.get(),
|
||||
talk_base::Thread::Current(),
|
||||
talk_base::Thread::Current(),
|
||||
NULL,
|
||||
ms_signaling_.get()),
|
||||
media_stream_signaling_.get()),
|
||||
webrtc_data_channel_(NULL) {}
|
||||
|
||||
virtual void SetUp() {
|
||||
@ -67,10 +90,13 @@ class SctpDataChannelTest : public testing::Test {
|
||||
constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true);
|
||||
constraints.AddMandatory(MediaConstraintsInterface::kEnableSctpDataChannels,
|
||||
true);
|
||||
ASSERT_TRUE(session_.Initialize(&constraints));
|
||||
webrtc::SessionDescriptionInterface* offer = session_.CreateOffer(NULL);
|
||||
ASSERT_TRUE(offer != NULL);
|
||||
ASSERT_TRUE(session_.SetLocalDescription(offer, NULL));
|
||||
ASSERT_TRUE(session_.Initialize(&constraints, NULL));
|
||||
talk_base::scoped_refptr<CreateSessionDescriptionObserverForTest> observer
|
||||
= new CreateSessionDescriptionObserverForTest();
|
||||
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);
|
||||
// Connect to the media channel.
|
||||
@ -91,7 +117,7 @@ class SctpDataChannelTest : public testing::Test {
|
||||
cricket::FakeMediaEngine* media_engine_;
|
||||
cricket::FakeDataEngine* data_engine_;
|
||||
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_;
|
||||
talk_base::scoped_refptr<webrtc::DataChannel> webrtc_data_channel_;
|
||||
};
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "talk/app/webrtc/mediaconstraintsinterface.h"
|
||||
#include "talk/app/webrtc/mediastreamtrackproxy.h"
|
||||
#include "talk/app/webrtc/videotrack.h"
|
||||
#include "talk/base/bytebuffer.h"
|
||||
|
||||
static const char kDefaultStreamLabel[] = "default";
|
||||
static const char kDefaultAudioTrackLabel[] = "defaulta0";
|
||||
@ -235,6 +236,29 @@ bool MediaStreamSignaling::AddDataChannel(DataChannel* data_channel) {
|
||||
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) {
|
||||
if (local_streams_->find(local_stream->label()) != NULL) {
|
||||
LOG(LS_WARNING) << "MediaStream with label " << local_stream->label()
|
||||
@ -864,6 +888,145 @@ void MediaStreamSignaling::CreateRemoteDataChannel(const std::string& label,
|
||||
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() {
|
||||
DataChannels::iterator it = data_channels_.begin();
|
||||
for (; it != data_channels_.end(); ++it) {
|
||||
|
@ -190,6 +190,15 @@ class MediaStreamSignaling {
|
||||
// Adds |data_channel| to the collection of DataChannels that will be
|
||||
// be offered in a SessionDescription.
|
||||
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|,
|
||||
// the local MediaStreams and DataChannels.
|
||||
@ -243,6 +252,8 @@ class MediaStreamSignaling {
|
||||
StreamCollectionInterface* remote_streams() const {
|
||||
return remote_streams_.get();
|
||||
}
|
||||
void UpdateLocalSctpDataChannels();
|
||||
void UpdateRemoteSctpDataChannels();
|
||||
|
||||
private:
|
||||
struct RemotePeerInfo {
|
||||
@ -357,8 +368,6 @@ class MediaStreamSignaling {
|
||||
void UpdateClosingDataChannels(
|
||||
const std::vector<std::string>& active_channels, bool is_local_update);
|
||||
void CreateRemoteDataChannel(const std::string& label, uint32 remote_ssrc);
|
||||
void UpdateLocalSctpDataChannels();
|
||||
void UpdateRemoteSctpDataChannels();
|
||||
|
||||
RemotePeerInfo remote_info_;
|
||||
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.
|
||||
static const size_t kTurnTransportTokensNum = 2;
|
||||
// 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 kDefaultTransportType[] = "udp";
|
||||
static const char kUdpTransportType[] = "udp";
|
||||
static const char kTcpTransportType[] = "tcp";
|
||||
|
||||
// NOTE: Must be in the same order as the ServiceType enum.
|
||||
static const char* kValidIceServiceTypes[] = {
|
||||
@ -67,9 +69,7 @@ enum ServiceType {
|
||||
};
|
||||
|
||||
enum {
|
||||
MSG_CREATE_SESSIONDESCRIPTION_SUCCESS = 0,
|
||||
MSG_CREATE_SESSIONDESCRIPTION_FAILED,
|
||||
MSG_SET_SESSIONDESCRIPTION_SUCCESS,
|
||||
MSG_SET_SESSIONDESCRIPTION_SUCCESS = 0,
|
||||
MSG_SET_SESSIONDESCRIPTION_FAILED,
|
||||
MSG_GETSTATS,
|
||||
MSG_ICECONNECTIONCHANGE,
|
||||
@ -85,17 +85,6 @@ struct CandidateMsg : public talk_base::MessageData {
|
||||
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 {
|
||||
explicit SetSessionDescriptionMsg(
|
||||
webrtc::SetSessionDescriptionObserver* observer)
|
||||
@ -145,7 +134,7 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration,
|
||||
continue;
|
||||
}
|
||||
std::vector<std::string> tokens;
|
||||
std::string turn_transport_type = kDefaultTransportType;
|
||||
std::string turn_transport_type = kUdpTransportType;
|
||||
talk_base::tokenize(server.uri, '?', &tokens);
|
||||
std::string uri_without_transport = tokens[0];
|
||||
// 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];
|
||||
talk_base::tokenize(uri_transport_param, '=', &tokens);
|
||||
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];
|
||||
}
|
||||
}
|
||||
@ -176,7 +171,10 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration,
|
||||
continue;
|
||||
}
|
||||
std::string address = tokens[1];
|
||||
int port = kDefaultPort;
|
||||
int port = kDefaultStunPort;
|
||||
if (service_type == TURNS)
|
||||
port = kDefaultStunTlsPort;
|
||||
|
||||
if (tokens.size() > kMinIceUriTokens) {
|
||||
if (!talk_base::FromString(tokens[2], &port)) {
|
||||
LOG(LS_WARNING) << "Failed to parse port string: " << tokens[2];
|
||||
@ -194,7 +192,8 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration,
|
||||
case STUNS:
|
||||
stun_config->push_back(StunConfiguration(address, port));
|
||||
break;
|
||||
case TURN: {
|
||||
case TURN:
|
||||
case TURNS: {
|
||||
if (server.username.empty()) {
|
||||
// Turn url example from the spec |url:"turn:user@turn.example.org"|.
|
||||
std::vector<std::string> turn_tokens;
|
||||
@ -204,15 +203,23 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration,
|
||||
address = turn_tokens[1];
|
||||
}
|
||||
}
|
||||
|
||||
bool secure = (service_type == TURNS);
|
||||
|
||||
turn_config->push_back(TurnConfiguration(address, port,
|
||||
server.username,
|
||||
server.password,
|
||||
turn_transport_type));
|
||||
turn_transport_type,
|
||||
secure));
|
||||
// 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));
|
||||
break;
|
||||
}
|
||||
case TURNS:
|
||||
case INVALID:
|
||||
default:
|
||||
LOG(WARNING) << "Configuration not supported: " << server.uri;
|
||||
@ -261,7 +268,8 @@ PeerConnection::~PeerConnection() {
|
||||
bool PeerConnection::Initialize(
|
||||
const PeerConnectionInterface::IceServers& configuration,
|
||||
const MediaConstraintsInterface* constraints,
|
||||
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
||||
PortAllocatorFactoryInterface* allocator_factory,
|
||||
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||
PeerConnectionObserver* observer) {
|
||||
std::vector<PortAllocatorFactoryInterface::StunConfiguration> stun_config;
|
||||
std::vector<PortAllocatorFactoryInterface::TurnConfiguration> turn_config;
|
||||
@ -270,7 +278,7 @@ bool PeerConnection::Initialize(
|
||||
}
|
||||
|
||||
return DoInitialize(stun_config, turn_config, constraints,
|
||||
allocator_factory, observer);
|
||||
allocator_factory, dtls_identity_service, observer);
|
||||
}
|
||||
|
||||
bool PeerConnection::DoInitialize(
|
||||
@ -278,6 +286,7 @@ bool PeerConnection::DoInitialize(
|
||||
const TurnConfigurations& turn_config,
|
||||
const MediaConstraintsInterface* constraints,
|
||||
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
||||
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||
PeerConnectionObserver* observer) {
|
||||
ASSERT(observer != NULL);
|
||||
if (!observer)
|
||||
@ -306,10 +315,9 @@ bool PeerConnection::DoInitialize(
|
||||
stats_.set_session(session_.get());
|
||||
|
||||
// Initialize the WebRtcSession. It creates transport channels etc.
|
||||
if (!session_->Initialize(constraints))
|
||||
if (!session_->Initialize(constraints, dtls_identity_service))
|
||||
return false;
|
||||
|
||||
|
||||
// Register PeerConnection as receiver of local ice candidates.
|
||||
// All the callbacks will be posted to the application from PeerConnection.
|
||||
session_->RegisterIceObserver(this);
|
||||
@ -416,7 +424,16 @@ PeerConnection::CreateDataChannel(
|
||||
if (!channel.get())
|
||||
return NULL;
|
||||
|
||||
// If we've already passed the underlying channel's setup phase, have the
|
||||
// MediaStreamSignaling update data channels manually.
|
||||
if (session_->data_channel() != NULL &&
|
||||
session_->data_channel_type() == cricket::DCT_SCTP) {
|
||||
mediastream_signaling_->UpdateLocalSctpDataChannels();
|
||||
mediastream_signaling_->UpdateRemoteSctpDataChannels();
|
||||
}
|
||||
|
||||
observer_->OnRenegotiationNeeded();
|
||||
|
||||
return DataChannelProxy::Create(signaling_thread(), channel.get());
|
||||
}
|
||||
|
||||
@ -426,17 +443,7 @@ void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
|
||||
LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
|
||||
return;
|
||||
}
|
||||
CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
|
||||
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);
|
||||
session_->CreateOffer(observer, constraints);
|
||||
}
|
||||
|
||||
void PeerConnection::CreateAnswer(
|
||||
@ -446,15 +453,7 @@ void PeerConnection::CreateAnswer(
|
||||
LOG(LS_ERROR) << "CreateAnswer - observer is NULL.";
|
||||
return;
|
||||
}
|
||||
CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
|
||||
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);
|
||||
session_->CreateAnswer(observer, constraints);
|
||||
}
|
||||
|
||||
void PeerConnection::SetLocalDescription(
|
||||
@ -468,7 +467,6 @@ void PeerConnection::SetLocalDescription(
|
||||
PostSetSessionDescriptionFailure(observer, "SessionDescription is NULL.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update stats here so that we have the most recent stats for tracks and
|
||||
// streams that might be removed by updating the session description.
|
||||
stats_.UpdateStats();
|
||||
@ -488,7 +486,6 @@ void PeerConnection::SetRemoteDescription(
|
||||
LOG(LS_ERROR) << "SetRemoteDescription - observer is NULL.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!desc) {
|
||||
PostSetSessionDescriptionFailure(observer, "SessionDescription is NULL.");
|
||||
return;
|
||||
@ -572,20 +569,6 @@ void PeerConnection::OnSessionStateChange(cricket::BaseSession* /*session*/,
|
||||
|
||||
void PeerConnection::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_SET_SESSIONDESCRIPTION_SUCCESS: {
|
||||
SetSessionDescriptionMsg* param =
|
||||
static_cast<SetSessionDescriptionMsg*>(msg->pdata);
|
||||
|
@ -59,7 +59,8 @@ class PeerConnection : public PeerConnectionInterface,
|
||||
|
||||
bool Initialize(const PeerConnectionInterface::IceServers& configuration,
|
||||
const MediaConstraintsInterface* constraints,
|
||||
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
||||
PortAllocatorFactoryInterface* allocator_factory,
|
||||
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||
PeerConnectionObserver* observer);
|
||||
virtual talk_base::scoped_refptr<StreamCollectionInterface> local_streams();
|
||||
virtual talk_base::scoped_refptr<StreamCollectionInterface> remote_streams();
|
||||
@ -152,7 +153,8 @@ class PeerConnection : public PeerConnectionInterface,
|
||||
bool DoInitialize(const StunConfigurations& stun_config,
|
||||
const TurnConfigurations& turn_config,
|
||||
const MediaConstraintsInterface* constraints,
|
||||
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
||||
PortAllocatorFactoryInterface* allocator_factory,
|
||||
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||
PeerConnectionObserver* observer);
|
||||
|
||||
talk_base::Thread* signaling_thread() const {
|
||||
|
@ -54,16 +54,19 @@ struct CreatePeerConnectionParams : public talk_base::MessageData {
|
||||
const webrtc::PeerConnectionInterface::IceServers& configuration,
|
||||
const webrtc::MediaConstraintsInterface* constraints,
|
||||
webrtc::PortAllocatorFactoryInterface* allocator_factory,
|
||||
webrtc::DTLSIdentityServiceInterface* dtls_identity_service,
|
||||
webrtc::PeerConnectionObserver* observer)
|
||||
: configuration(configuration),
|
||||
constraints(constraints),
|
||||
allocator_factory(allocator_factory),
|
||||
dtls_identity_service(dtls_identity_service),
|
||||
observer(observer) {
|
||||
}
|
||||
scoped_refptr<webrtc::PeerConnectionInterface> peerconnection;
|
||||
const webrtc::PeerConnectionInterface::IceServers& configuration;
|
||||
const webrtc::MediaConstraintsInterface* constraints;
|
||||
scoped_refptr<webrtc::PortAllocatorFactoryInterface> allocator_factory;
|
||||
webrtc::DTLSIdentityServiceInterface* dtls_identity_service;
|
||||
webrtc::PeerConnectionObserver* observer;
|
||||
};
|
||||
|
||||
@ -199,11 +202,13 @@ void PeerConnectionFactory::OnMessage(talk_base::Message* msg) {
|
||||
}
|
||||
case MSG_CREATE_PEERCONNECTION: {
|
||||
CreatePeerConnectionParams* pdata =
|
||||
static_cast<CreatePeerConnectionParams*>(msg->pdata);
|
||||
pdata->peerconnection = CreatePeerConnection_s(pdata->configuration,
|
||||
pdata->constraints,
|
||||
pdata->allocator_factory,
|
||||
pdata->observer);
|
||||
static_cast<CreatePeerConnectionParams*> (msg->pdata);
|
||||
pdata->peerconnection = CreatePeerConnection_s(
|
||||
pdata->configuration,
|
||||
pdata->constraints,
|
||||
pdata->allocator_factory,
|
||||
pdata->dtls_identity_service,
|
||||
pdata->observer);
|
||||
break;
|
||||
}
|
||||
case MSG_CREATE_AUDIOSOURCE: {
|
||||
@ -278,7 +283,8 @@ PeerConnectionFactory::CreatePeerConnection(
|
||||
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||
PeerConnectionObserver* observer) {
|
||||
CreatePeerConnectionParams params(configuration, constraints,
|
||||
allocator_factory, observer);
|
||||
allocator_factory, dtls_identity_service,
|
||||
observer);
|
||||
signaling_thread_->Send(this, MSG_CREATE_PEERCONNECTION, ¶ms);
|
||||
return params.peerconnection;
|
||||
}
|
||||
@ -298,6 +304,7 @@ PeerConnectionFactory::CreatePeerConnection_s(
|
||||
const PeerConnectionInterface::IceServers& configuration,
|
||||
const MediaConstraintsInterface* constraints,
|
||||
PortAllocatorFactoryInterface* allocator_factory,
|
||||
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||
PeerConnectionObserver* observer) {
|
||||
ASSERT(allocator_factory || allocator_factory_);
|
||||
talk_base::scoped_refptr<PeerConnection> pc(
|
||||
@ -306,6 +313,7 @@ PeerConnectionFactory::CreatePeerConnection_s(
|
||||
configuration,
|
||||
constraints,
|
||||
allocator_factory ? allocator_factory : allocator_factory_.get(),
|
||||
dtls_identity_service,
|
||||
observer)) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface,
|
||||
const PeerConnectionInterface::IceServers& configuration,
|
||||
const MediaConstraintsInterface* constraints,
|
||||
PortAllocatorFactoryInterface* allocator_factory,
|
||||
DTLSIdentityServiceInterface* dtls_identity_service,
|
||||
PeerConnectionObserver* observer);
|
||||
// Implements talk_base::MessageHandler.
|
||||
void OnMessage(talk_base::Message* msg);
|
||||
|
@ -64,7 +64,8 @@ static const char kSecureTurnIceServer[] =
|
||||
static const char kTurnIceServerWithNoUsernameInUri[] =
|
||||
"turn:test.com:1234";
|
||||
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";
|
||||
|
||||
class NullPeerConnectionObserver : public PeerConnectionObserver {
|
||||
@ -174,15 +175,15 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) {
|
||||
"test.com", 1234);
|
||||
stun_configs.push_back(stun2);
|
||||
webrtc::PortAllocatorFactoryInterface::StunConfiguration stun3(
|
||||
"hello.com", kDefaultPort);
|
||||
"hello.com", kDefaultStunPort);
|
||||
stun_configs.push_back(stun3);
|
||||
VerifyStunConfigurations(stun_configs);
|
||||
TurnConfigurations turn_configs;
|
||||
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);
|
||||
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn2(
|
||||
"hello.com", kDefaultPort, "test", kTurnPassword, "tcp");
|
||||
"hello.com", kDefaultStunPort, "test", kTurnPassword, "tcp", false);
|
||||
turn_configs.push_back(turn2);
|
||||
VerifyTurnConfigurations(turn_configs);
|
||||
}
|
||||
@ -204,7 +205,7 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingNoUsernameInUri) {
|
||||
EXPECT_TRUE(pc.get() != NULL);
|
||||
TurnConfigurations turn_configs;
|
||||
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn(
|
||||
"test.com", 1234, kTurnUsername, kTurnPassword, "udp");
|
||||
"test.com", 1234, kTurnUsername, kTurnPassword, "udp", false);
|
||||
turn_configs.push_back(turn);
|
||||
VerifyTurnConfigurations(turn_configs);
|
||||
}
|
||||
@ -225,19 +226,16 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) {
|
||||
EXPECT_TRUE(pc.get() != NULL);
|
||||
TurnConfigurations turn_configs;
|
||||
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn(
|
||||
"hello.com", kDefaultPort, "test", kTurnPassword, "tcp");
|
||||
"hello.com", kDefaultStunPort, "test", kTurnPassword, "tcp", false);
|
||||
turn_configs.push_back(turn);
|
||||
VerifyTurnConfigurations(turn_configs);
|
||||
StunConfigurations stun_configs;
|
||||
webrtc::PortAllocatorFactoryInterface::StunConfiguration stun(
|
||||
"hello.com", kDefaultPort);
|
||||
"hello.com", kDefaultStunPort);
|
||||
stun_configs.push_back(stun);
|
||||
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) {
|
||||
webrtc::PeerConnectionInterface::IceServers ice_servers;
|
||||
webrtc::PeerConnectionInterface::IceServer ice_server;
|
||||
@ -249,8 +247,11 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) {
|
||||
allocator_factory_.get(),
|
||||
NULL,
|
||||
&observer_));
|
||||
EXPECT_TRUE(pc.get() == NULL);
|
||||
EXPECT_TRUE(pc.get() != NULL);
|
||||
TurnConfigurations turn_configs;
|
||||
webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn(
|
||||
"hello.com", kDefaultStunTlsPort, "test", kTurnPassword, "tcp", true);
|
||||
turn_configs.push_back(turn);
|
||||
VerifyTurnConfigurations(turn_configs);
|
||||
}
|
||||
|
||||
|
@ -315,15 +315,18 @@ class PortAllocatorFactoryInterface : public talk_base::RefCountInterface {
|
||||
int port,
|
||||
const std::string& username,
|
||||
const std::string& password,
|
||||
const std::string& transport_type)
|
||||
const std::string& transport_type,
|
||||
bool secure)
|
||||
: server(address, port),
|
||||
username(username),
|
||||
password(password),
|
||||
transport_type(transport_type) {}
|
||||
transport_type(transport_type),
|
||||
secure(secure) {}
|
||||
talk_base::SocketAddress server;
|
||||
std::string username;
|
||||
std::string password;
|
||||
std::string transport_type;
|
||||
bool secure;
|
||||
};
|
||||
|
||||
virtual cricket::PortAllocator* CreatePortAllocator(
|
||||
@ -339,8 +342,8 @@ class PortAllocatorFactoryInterface : public talk_base::RefCountInterface {
|
||||
class DTLSIdentityRequestObserver : public talk_base::RefCountInterface {
|
||||
public:
|
||||
virtual void OnFailure(int error) = 0;
|
||||
virtual void OnSuccess(const std::string& certificate,
|
||||
const std::string& private_key) = 0;
|
||||
virtual void OnSuccess(const std::string& der_cert,
|
||||
const std::string& der_private_key) = 0;
|
||||
protected:
|
||||
virtual ~DTLSIdentityRequestObserver() {}
|
||||
};
|
||||
@ -372,6 +375,8 @@ class DTLSIdentityServiceInterface {
|
||||
const std::string& identity_name,
|
||||
const std::string& common_name,
|
||||
DTLSIdentityRequestObserver* observer) = 0;
|
||||
|
||||
virtual ~DTLSIdentityServiceInterface() {}
|
||||
};
|
||||
|
||||
// PeerConnectionFactoryInterface is the factory interface use for creating
|
||||
|
@ -77,7 +77,7 @@ cricket::PortAllocator* PortAllocatorFactory::CreatePortAllocator(
|
||||
cricket::ProtocolType protocol;
|
||||
if (cricket::StringToProto(turn[i].transport_type.c_str(), &protocol)) {
|
||||
relay_server.ports.push_back(cricket::ProtocolAddress(
|
||||
turn[i].server, protocol));
|
||||
turn[i].server, protocol, turn[i].secure));
|
||||
relay_server.credentials = credentials;
|
||||
allocator->AddRelay(relay_server);
|
||||
} 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',
|
||||
'webrtcsdp.cc',
|
||||
'webrtcsession.cc',
|
||||
'webrtcsessiondescriptionfactory.cc',
|
||||
],
|
||||
lin_ccflags = peerconnection_lin_ccflags
|
||||
)
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "talk/app/webrtc/mediaconstraintsinterface.h"
|
||||
#include "talk/app/webrtc/mediastreamsignaling.h"
|
||||
#include "talk/app/webrtc/peerconnectioninterface.h"
|
||||
#include "talk/app/webrtc/webrtcsessiondescriptionfactory.h"
|
||||
#include "talk/base/helpers.h"
|
||||
#include "talk/base/logging.h"
|
||||
#include "talk/base/stringencode.h"
|
||||
@ -51,13 +52,8 @@ using cricket::MediaContentDescription;
|
||||
using cricket::SessionDescription;
|
||||
using cricket::TransportInfo;
|
||||
|
||||
typedef cricket::MediaSessionOptions::Stream Stream;
|
||||
typedef cricket::MediaSessionOptions::Streams Streams;
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
static const uint64 kInitSessionVersion = 2;
|
||||
|
||||
const char kInternalConstraintPrefix[] = "internal";
|
||||
|
||||
// Supported MediaConstraints.
|
||||
@ -73,10 +69,6 @@ const char MediaConstraintsInterface::kEnableRtpDataChannels[] =
|
||||
const char MediaConstraintsInterface::kEnableSctpDataChannels[] =
|
||||
"internalSctpDataChannels";
|
||||
|
||||
// Arbitrary constant used as prefix for the identity.
|
||||
// Chosen to make the certificates more readable.
|
||||
const char kWebRTCIdentityPrefix[] = "WebRTC";
|
||||
|
||||
// Error messages
|
||||
const char kSetLocalSdpFailed[] = "SetLocalDescription failed: ";
|
||||
const char kSetRemoteSdpFailed[] = "SetRemoteDescription failed: ";
|
||||
@ -114,24 +106,6 @@ static bool VerifyMediaDescriptions(
|
||||
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
|
||||
// fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES
|
||||
// keys, will be caught in Transport negotiation, and backstopped by Channel's
|
||||
@ -167,22 +141,29 @@ static bool VerifyCrypto(const SessionDescription* desc) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CompareStream(const Stream& stream1, const Stream& stream2) {
|
||||
return (stream1.id < stream2.id);
|
||||
}
|
||||
// Forces |sdesc->crypto_required| to the appropriate state based on the
|
||||
// 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) {
|
||||
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());
|
||||
// Updating the |crypto_required_| in MediaContentDescription to the
|
||||
// appropriate state based on the current security policy.
|
||||
for (cricket::ContentInfos::iterator iter = sdesc->contents().begin();
|
||||
iter != sdesc->contents().end(); ++iter) {
|
||||
if (cricket::IsMediaContent(&*iter)) {
|
||||
MediaContentDescription* mdesc =
|
||||
static_cast<MediaContentDescription*> (iter->description);
|
||||
if (mdesc) {
|
||||
mdesc->set_crypto_required(secure_policy == cricket::SEC_REQUIRED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetAudioSsrcByTrackId(
|
||||
@ -343,15 +324,17 @@ class IceRestartAnswerLatch {
|
||||
public:
|
||||
IceRestartAnswerLatch() : ice_restart_(false) { }
|
||||
|
||||
// Returns true if CheckForRemoteIceRestart has been called since last
|
||||
// time this method was called with a new session description where
|
||||
// ice password and ufrag has changed.
|
||||
bool AnswerWithIceRestartLatch() {
|
||||
// Returns true if CheckForRemoteIceRestart has been called with a new session
|
||||
// description where ice password and ufrag has changed since last time
|
||||
// Reset() was called.
|
||||
bool Get() const {
|
||||
return ice_restart_;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
if (ice_restart_) {
|
||||
ice_restart_ = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CheckForRemoteIceRestart(
|
||||
@ -391,11 +374,12 @@ class IceRestartAnswerLatch {
|
||||
bool ice_restart_;
|
||||
};
|
||||
|
||||
WebRtcSession::WebRtcSession(cricket::ChannelManager* channel_manager,
|
||||
talk_base::Thread* signaling_thread,
|
||||
talk_base::Thread* worker_thread,
|
||||
cricket::PortAllocator* port_allocator,
|
||||
MediaStreamSignaling* mediastream_signaling)
|
||||
WebRtcSession::WebRtcSession(
|
||||
cricket::ChannelManager* channel_manager,
|
||||
talk_base::Thread* signaling_thread,
|
||||
talk_base::Thread* worker_thread,
|
||||
cricket::PortAllocator* port_allocator,
|
||||
MediaStreamSignaling* mediastream_signaling)
|
||||
: cricket::BaseSession(signaling_thread, worker_thread, port_allocator,
|
||||
talk_base::ToString(talk_base::CreateRandomId64() &
|
||||
LLONG_MAX),
|
||||
@ -404,19 +388,12 @@ WebRtcSession::WebRtcSession(cricket::ChannelManager* channel_manager,
|
||||
// o line MUST be representable with a "64 bit signed integer".
|
||||
// Due to this constraint session id |sid_| is max limited to LLONG_MAX.
|
||||
channel_manager_(channel_manager),
|
||||
session_desc_factory_(channel_manager, &transport_desc_factory_),
|
||||
mediastream_signaling_(mediastream_signaling),
|
||||
ice_observer_(NULL),
|
||||
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),
|
||||
data_channel_type_(cricket::DCT_NONE),
|
||||
ice_restart_latch_(new IceRestartAnswerLatch) {
|
||||
transport_desc_factory_.set_protocol(cricket::ICEPROTO_HYBRID);
|
||||
}
|
||||
|
||||
WebRtcSession::~WebRtcSession() {
|
||||
@ -436,34 +413,15 @@ WebRtcSession::~WebRtcSession() {
|
||||
delete saved_candidates_[i];
|
||||
}
|
||||
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
|
||||
// mandatory constraints can be fulfilled. Note that |constraints|
|
||||
// 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;
|
||||
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.
|
||||
// It takes precendence over the kEnableSctpDataChannels constraint.
|
||||
if (FindConstraint(
|
||||
@ -471,23 +429,26 @@ bool WebRtcSession::Initialize(const MediaConstraintsInterface* constraints) {
|
||||
&value, NULL) && value) {
|
||||
LOG(LS_INFO) << "Allowing RTP data engine.";
|
||||
data_channel_type_ = cricket::DCT_RTP;
|
||||
} else if (
|
||||
FindConstraint(
|
||||
constraints,
|
||||
MediaConstraintsInterface::kEnableSctpDataChannels,
|
||||
&value, NULL) && value &&
|
||||
// DTLS has to be enabled to use SCTP.
|
||||
(transport_desc_factory_.secure() == cricket::SEC_ENABLED)) {
|
||||
LOG(LS_INFO) << "Allowing SCTP data engine.";
|
||||
data_channel_type_ = cricket::DCT_SCTP;
|
||||
} else {
|
||||
bool sctp_enabled = FindConstraint(
|
||||
constraints,
|
||||
MediaConstraintsInterface::kEnableSctpDataChannels,
|
||||
&value, NULL) && value;
|
||||
bool dtls_enabled = FindConstraint(
|
||||
constraints,
|
||||
MediaConstraintsInterface::kEnableDtlsSrtp,
|
||||
&value, NULL) && value;
|
||||
|
||||
// DTLS has to be enabled to use SCTP.
|
||||
if (sctp_enabled && dtls_enabled) {
|
||||
LOG(LS_INFO) << "Allowing SCTP data engine.";
|
||||
data_channel_type_ = cricket::DCT_SCTP;
|
||||
}
|
||||
}
|
||||
if (data_channel_type_ != cricket::DCT_NONE) {
|
||||
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(
|
||||
JsepSessionDescription::kDefaultVideoCodecId,
|
||||
JsepSessionDescription::kDefaultVideoCodecName,
|
||||
@ -497,6 +458,19 @@ bool WebRtcSession::Initialize(const MediaConstraintsInterface* constraints) {
|
||||
JsepSessionDescription::kDefaultVideoCodecPreference);
|
||||
channel_manager_->SetDefaultVideoEncoderConfig(
|
||||
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;
|
||||
}
|
||||
|
||||
@ -524,113 +498,27 @@ bool WebRtcSession::StartCandidatesAllocation() {
|
||||
|
||||
void WebRtcSession::set_secure_policy(
|
||||
cricket::SecureMediaPolicy secure_policy) {
|
||||
session_desc_factory_.set_secure(secure_policy);
|
||||
webrtc_session_desc_factory_->set_secure(secure_policy);
|
||||
}
|
||||
|
||||
SessionDescriptionInterface* WebRtcSession::CreateOffer(
|
||||
const MediaConstraintsInterface* constraints) {
|
||||
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;
|
||||
cricket::SecureMediaPolicy WebRtcSession::secure_policy() const {
|
||||
return webrtc_session_desc_factory_->secure();
|
||||
}
|
||||
|
||||
SessionDescriptionInterface* WebRtcSession::CreateAnswer(
|
||||
const MediaConstraintsInterface* constraints) {
|
||||
if (!remote_description()) {
|
||||
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;
|
||||
}
|
||||
void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer,
|
||||
const MediaConstraintsInterface* constraints) {
|
||||
webrtc_session_desc_factory_->CreateOffer(observer, constraints);
|
||||
}
|
||||
|
||||
cricket::MediaSessionOptions options;
|
||||
if (!mediastream_signaling_->GetOptionsForAnswer(constraints, &options)) {
|
||||
LOG(LS_ERROR) << "CreateAnswer called with invalid 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;
|
||||
void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer,
|
||||
const MediaConstraintsInterface* constraints) {
|
||||
webrtc_session_desc_factory_->CreateAnswer(observer, constraints);
|
||||
}
|
||||
|
||||
bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
||||
std::string* err_desc) {
|
||||
cricket::SecureMediaPolicy secure_policy =
|
||||
webrtc_session_desc_factory_->secure();
|
||||
// Takes the ownership of |desc| regardless of the result.
|
||||
talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
|
||||
|
||||
@ -651,12 +539,10 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
||||
std::string type = desc->type();
|
||||
return BadLocalSdp(BadStateErrMsg(type, state()), err_desc);
|
||||
}
|
||||
|
||||
if (session_desc_factory_.secure() == cricket::SEC_REQUIRED &&
|
||||
if (secure_policy == cricket::SEC_REQUIRED &&
|
||||
!VerifyCrypto(desc->description())) {
|
||||
return BadLocalSdp(kSdpWithoutCrypto, err_desc);
|
||||
}
|
||||
|
||||
if (action == kAnswer && !VerifyMediaDescriptions(
|
||||
desc->description(), remote_description()->description())) {
|
||||
return BadLocalSdp(kMlineMismatch, err_desc);
|
||||
@ -668,7 +554,7 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
||||
}
|
||||
|
||||
// Update the MediaContentDescription crypto settings as per the policy set.
|
||||
UpdateSessionDescriptionSecurePolicy(desc->description());
|
||||
UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description());
|
||||
|
||||
set_local_description(desc->description()->Copy());
|
||||
local_desc_.reset(desc_temp.release());
|
||||
@ -703,6 +589,8 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
||||
|
||||
bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
||||
std::string* err_desc) {
|
||||
cricket::SecureMediaPolicy secure_policy =
|
||||
webrtc_session_desc_factory_->secure();
|
||||
// Takes the ownership of |desc| regardless of the result.
|
||||
talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
|
||||
|
||||
@ -729,7 +617,7 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
||||
return BadRemoteSdp(kMlineMismatch, err_desc);
|
||||
}
|
||||
|
||||
if (session_desc_factory_.secure() == cricket::SEC_REQUIRED &&
|
||||
if (secure_policy == cricket::SEC_REQUIRED &&
|
||||
!VerifyCrypto(desc->description())) {
|
||||
return BadRemoteSdp(kSdpWithoutCrypto, err_desc);
|
||||
}
|
||||
@ -762,7 +650,8 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
||||
// Copy all saved candidates.
|
||||
CopySavedCandidates(desc);
|
||||
// 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
|
||||
// that indicates the remote peer requests ice restart.
|
||||
ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
|
||||
@ -1057,12 +946,35 @@ talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
talk_base::scoped_refptr<DataChannel> channel(
|
||||
DataChannel::Create(this, label, &new_config));
|
||||
if (channel == NULL)
|
||||
return NULL;
|
||||
if (!mediastream_signaling_->AddDataChannel(channel))
|
||||
return NULL;
|
||||
if (data_channel_type_ == cricket::DCT_SCTP) {
|
||||
if (config == NULL) {
|
||||
LOG(LS_WARNING) << "Could not send data channel OPEN message"
|
||||
<< " because of NULL config.";
|
||||
return NULL;
|
||||
}
|
||||
if (data_channel_.get()) {
|
||||
channel->SetReceiveSsrc(new_config.id);
|
||||
channel->SetSendSsrc(new_config.id);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1070,6 +982,22 @@ cricket::DataChannelType WebRtcSession::data_channel_type() const {
|
||||
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(
|
||||
PeerConnectionInterface::IceConnectionState state) {
|
||||
if (ice_connection_state_ == state) {
|
||||
@ -1401,8 +1329,13 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
|
||||
bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
|
||||
bool rtcp = (data_channel_type_ == cricket::DCT_RTP);
|
||||
data_channel_.reset(channel_manager_->CreateDataChannel(
|
||||
this, content->name, rtcp, data_channel_type_));
|
||||
return (data_channel_ != NULL);
|
||||
this, content->name, rtcp, data_channel_type_));
|
||||
if (!data_channel_.get()) {
|
||||
return false;
|
||||
}
|
||||
data_channel_->SignalDataReceived.connect(
|
||||
this, &WebRtcSession::OnDataReceived);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebRtcSession::CopySavedCandidates(
|
||||
@ -1418,24 +1351,29 @@ void WebRtcSession::CopySavedCandidates(
|
||||
saved_candidates_.clear();
|
||||
}
|
||||
|
||||
void WebRtcSession::UpdateSessionDescriptionSecurePolicy(
|
||||
SessionDescription* sdesc) {
|
||||
if (!sdesc) {
|
||||
// Look for OPEN messages and set up data channels in response.
|
||||
void WebRtcSession::OnDataReceived(
|
||||
cricket::DataChannel* channel,
|
||||
const cricket::ReceiveDataParams& params,
|
||||
const talk_base::Buffer& payload) {
|
||||
if (params.type != cricket::DMT_CONTROL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Updating the |crypto_required_| in MediaContentDescription to the
|
||||
// appropriate state based on the current security policy.
|
||||
for (cricket::ContentInfos::iterator iter = sdesc->contents().begin();
|
||||
iter != sdesc->contents().end(); ++iter) {
|
||||
if (cricket::IsMediaContent(&*iter)) {
|
||||
MediaContentDescription* mdesc =
|
||||
static_cast<MediaContentDescription*>(iter->description);
|
||||
if (mdesc) {
|
||||
mdesc->set_crypto_required(
|
||||
session_desc_factory_.secure() == cricket::SEC_REQUIRED);
|
||||
}
|
||||
}
|
||||
std::string label;
|
||||
DataChannelInit config;
|
||||
if (!mediastream_signaling_->ParseDataChannelOpenMessage(
|
||||
payload, &label, &config)) {
|
||||
LOG(LS_WARNING) << "Failed to parse data channel OPEN message.";
|
||||
return;
|
||||
}
|
||||
|
||||
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/media/base/mediachannel.h"
|
||||
#include "talk/p2p/base/session.h"
|
||||
#include "talk/p2p/base/transportdescriptionfactory.h"
|
||||
#include "talk/session/media/mediasession.h"
|
||||
|
||||
namespace cricket {
|
||||
@ -59,6 +58,7 @@ namespace webrtc {
|
||||
|
||||
class IceRestartAnswerLatch;
|
||||
class MediaStreamSignaling;
|
||||
class WebRtcSessionDescriptionFactory;
|
||||
|
||||
extern const char kSetLocalSdpFailed[];
|
||||
extern const char kSetRemoteSdpFailed[];
|
||||
@ -107,7 +107,8 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
MediaStreamSignaling* mediastream_signaling);
|
||||
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
|
||||
// to STATE_RECEIVEDTERMINATE.
|
||||
void Terminate();
|
||||
@ -127,20 +128,16 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
}
|
||||
|
||||
void set_secure_policy(cricket::SecureMediaPolicy secure_policy);
|
||||
cricket::SecureMediaPolicy secure_policy() const {
|
||||
return session_desc_factory_.secure();
|
||||
}
|
||||
cricket::SecureMediaPolicy secure_policy() const;
|
||||
|
||||
// Generic error message callback from WebRtcSession.
|
||||
// TODO - It may be necessary to supply error code as well.
|
||||
sigslot::signal0<> SignalError;
|
||||
|
||||
SessionDescriptionInterface* CreateOffer(
|
||||
const MediaConstraintsInterface* constraints);
|
||||
|
||||
SessionDescriptionInterface* CreateAnswer(
|
||||
const MediaConstraintsInterface* constraints);
|
||||
|
||||
void CreateOffer(CreateSessionDescriptionObserver* observer,
|
||||
const MediaConstraintsInterface* constraints);
|
||||
void CreateAnswer(CreateSessionDescriptionObserver* observer,
|
||||
const MediaConstraintsInterface* constraints);
|
||||
// The ownership of |desc| will be transferred after this call.
|
||||
bool SetLocalDescription(SessionDescriptionInterface* desc,
|
||||
std::string* err_desc);
|
||||
@ -155,6 +152,9 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
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.
|
||||
virtual bool GetTrackIdBySsrc(uint32 ssrc, std::string* id);
|
||||
|
||||
@ -186,6 +186,17 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
|
||||
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:
|
||||
// Indicates the type of SessionDescription in a call to SetLocalDescription
|
||||
// and SetRemoteDescription.
|
||||
@ -194,6 +205,7 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
kPrAnswer,
|
||||
kAnswer,
|
||||
};
|
||||
|
||||
// Invokes ConnectChannels() on transport proxies, which initiates ice
|
||||
// candidates allocation.
|
||||
bool StartCandidatesAllocation();
|
||||
@ -252,11 +264,10 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
// The |saved_candidates_| will be cleared after this function call.
|
||||
void CopySavedCandidates(SessionDescriptionInterface* dest_desc);
|
||||
|
||||
// Forces |desc->crypto_required| to the appropriate state based on the
|
||||
// current security policy, to ensure a failure occurs if there is an error
|
||||
// in crypto negotiation.
|
||||
// Called when processing the local session description.
|
||||
void UpdateSessionDescriptionSecurePolicy(cricket::SessionDescription* desc);
|
||||
void OnDataReceived(
|
||||
cricket::DataChannel* channel,
|
||||
const cricket::ReceiveDataParams& params,
|
||||
const talk_base::Buffer& payload);
|
||||
|
||||
bool GetLocalTrackId(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::DataChannel> data_channel_;
|
||||
cricket::ChannelManager* channel_manager_;
|
||||
cricket::TransportDescriptionFactory transport_desc_factory_;
|
||||
cricket::MediaSessionDescriptionFactory session_desc_factory_;
|
||||
MediaStreamSignaling* mediastream_signaling_;
|
||||
IceObserver* ice_observer_;
|
||||
PeerConnectionInterface::IceConnectionState ice_connection_state_;
|
||||
@ -280,7 +289,6 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
talk_base::scoped_ptr<SessionDescriptionInterface> remote_desc_;
|
||||
// Candidates that arrived before the remote description was set.
|
||||
std::vector<IceCandidateInterface*> saved_candidates_;
|
||||
uint64 session_version_;
|
||||
// If the remote peer is using a older version of implementation.
|
||||
bool older_version_remote_peer_;
|
||||
// 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).
|
||||
cricket::DataChannelType data_channel_type_;
|
||||
talk_base::scoped_ptr<IceRestartAnswerLatch> ice_restart_latch_;
|
||||
|
||||
talk_base::scoped_ptr<WebRtcSessionDescriptionFactory>
|
||||
webrtc_session_desc_factory_;
|
||||
|
||||
sigslot::signal0<> SignalVoiceChannelDestroyed;
|
||||
sigslot::signal0<> SignalVideoChannelDestroyed;
|
||||
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 "sechash.h"
|
||||
|
||||
#include "talk/base/base64.h"
|
||||
#include "talk/base/logging.h"
|
||||
#include "talk/base/helpers.h"
|
||||
#include "talk/base/nssstreamadapter.h"
|
||||
|
||||
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() {
|
||||
if (privkey_)
|
||||
SECKEY_DestroyPrivateKey(privkey_);
|
||||
@ -135,7 +92,7 @@ NSSKeyPair *NSSKeyPair::GetReference() {
|
||||
|
||||
NSSCertificate *NSSCertificate::FromPEMString(const std::string &pem_string) {
|
||||
std::string der;
|
||||
if (!PemToDer("CERTIFICATE", pem_string, &der))
|
||||
if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der))
|
||||
return NULL;
|
||||
|
||||
SECItem der_cert;
|
||||
@ -160,9 +117,9 @@ NSSCertificate *NSSCertificate::GetReference() const {
|
||||
}
|
||||
|
||||
std::string NSSCertificate::ToPEMString() const {
|
||||
return DerToPem("CERTIFICATE",
|
||||
certificate_->derCert.data,
|
||||
certificate_->derCert.len);
|
||||
return SSLIdentity::DerToPem(kPemTypeCertificate,
|
||||
certificate_->derCert.data,
|
||||
certificate_->derCert.len);
|
||||
}
|
||||
|
||||
bool NSSCertificate::GetDigestLength(const std::string &algorithm,
|
||||
@ -357,8 +314,8 @@ NSSIdentity *NSSIdentity::Generate(const std::string &common_name) {
|
||||
SSLIdentity* NSSIdentity::FromPEMStrings(const std::string& private_key,
|
||||
const std::string& certificate) {
|
||||
std::string private_key_der;
|
||||
if (!PemToDer(
|
||||
"RSA PRIVATE KEY", private_key, &private_key_der))
|
||||
if (!SSLIdentity::PemToDer(
|
||||
kPemTypeRsaPrivateKey, private_key, &private_key_der))
|
||||
return NULL;
|
||||
|
||||
SECItem private_key_item;
|
||||
|
@ -363,6 +363,15 @@ void SocketTest::ConnectWithDnsLookupFailInternal(const IPAddress& loopback) {
|
||||
EXPECT_EQ(0, client->Connect(bogus_dns_addr));
|
||||
|
||||
// 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_FALSE(sink.Check(client.get(), testing::SSE_OPEN));
|
||||
EXPECT_TRUE(sink.Check(client.get(), testing::SSE_ERROR));
|
||||
|
@ -34,6 +34,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "talk/base/base64.h"
|
||||
#include "talk/base/logging.h"
|
||||
#include "talk/base/sslconfig.h"
|
||||
|
||||
#if SSL_USE_SCHANNEL
|
||||
@ -50,6 +52,59 @@
|
||||
|
||||
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
|
||||
|
||||
SSLCertificate* SSLCertificate::FromPEMString(const std::string& pem_string) {
|
||||
|
@ -64,8 +64,8 @@ class SSLCertificate {
|
||||
|
||||
// Compute the digest of the certificate given algorithm
|
||||
virtual bool ComputeDigest(const std::string &algorithm,
|
||||
unsigned char *digest, std::size_t size,
|
||||
std::size_t *length) const = 0;
|
||||
unsigned char* digest, std::size_t size,
|
||||
std::size_t* length) const = 0;
|
||||
};
|
||||
|
||||
// Our identity in an SSL negotiation: a keypair and certificate (both
|
||||
@ -93,8 +93,19 @@ class SSLIdentity {
|
||||
|
||||
// Returns a temporary reference to the certificate.
|
||||
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
|
||||
|
||||
#endif // TALK_BASE_SSLIDENTITY_H__
|
||||
|
@ -32,6 +32,8 @@
|
||||
#include "talk/base/ssladapter.h"
|
||||
#include "talk/base/sslidentity.h"
|
||||
|
||||
using talk_base::SSLIdentity;
|
||||
|
||||
const char kTestCertificate[] = "-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIB6TCCAVICAQYwDQYJKoZIhvcNAQEEBQAwWzELMAkGA1UEBhMCQVUxEzARBgNV\n"
|
||||
"BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRswGQYD\n"
|
||||
@ -70,8 +72,8 @@ class SSLIdentityTest : public testing::Test {
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
identity1_.reset(talk_base::SSLIdentity::Generate("test1"));
|
||||
identity2_.reset(talk_base::SSLIdentity::Generate("test2"));
|
||||
identity1_.reset(SSLIdentity::Generate("test1"));
|
||||
identity2_.reset(SSLIdentity::Generate("test2"));
|
||||
|
||||
ASSERT_TRUE(identity1_);
|
||||
ASSERT_TRUE(identity2_);
|
||||
@ -126,8 +128,8 @@ class SSLIdentityTest : public testing::Test {
|
||||
}
|
||||
|
||||
private:
|
||||
talk_base::scoped_ptr<talk_base::SSLIdentity> identity1_;
|
||||
talk_base::scoped_ptr<talk_base::SSLIdentity> identity2_;
|
||||
talk_base::scoped_ptr<SSLIdentity> identity1_;
|
||||
talk_base::scoped_ptr<SSLIdentity> identity2_;
|
||||
talk_base::scoped_ptr<talk_base::SSLCertificate> test_cert_;
|
||||
};
|
||||
|
||||
@ -187,8 +189,17 @@ TEST_F(SSLIdentityTest, FromPEMStrings) {
|
||||
"qCV42aXS3onOXDQ1ibuWq0fr0//aj0wo4KV474c=\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
talk_base::scoped_ptr<talk_base::SSLIdentity> identity(
|
||||
talk_base::SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kCERT_PEM));
|
||||
talk_base::scoped_ptr<SSLIdentity> identity(
|
||||
SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kCERT_PEM));
|
||||
EXPECT_TRUE(identity);
|
||||
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/webrtcsession.cc',
|
||||
'app/webrtc/webrtcsession.h',
|
||||
'app/webrtc/webrtcsessiondescriptionfactory.cc',
|
||||
'app/webrtc/webrtcsessiondescriptionfactory.h',
|
||||
],
|
||||
}, # target libjingle_peerconnection
|
||||
],
|
||||
|
@ -391,6 +391,8 @@
|
||||
'app/webrtc/test/fakeaudiocapturemodule.h',
|
||||
'app/webrtc/test/fakeaudiocapturemodule_unittest.cc',
|
||||
'app/webrtc/test/fakeconstraints.h',
|
||||
'app/webrtc/test/fakedtlsidentityservice.h',
|
||||
'app/webrtc/test/fakemediastreamsignaling.h',
|
||||
'app/webrtc/test/fakeperiodicvideocapturer.h',
|
||||
'app/webrtc/test/fakevideotrackrenderer.h',
|
||||
'app/webrtc/test/mockpeerconnectionobservers.h',
|
||||
|
@ -45,6 +45,10 @@ class FeedbackParam {
|
||||
: id_(id),
|
||||
param_(param) {
|
||||
}
|
||||
explicit FeedbackParam(const std::string& id)
|
||||
: id_(id),
|
||||
param_(kParamValueEmpty) {
|
||||
}
|
||||
bool operator==(const FeedbackParam& other) const;
|
||||
|
||||
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).
|
||||
// TODO(ldixon): Unclear if copy is needed as this method is responsible for
|
||||
// memory cleanup. But this does simplify code.
|
||||
const uint32 native_ppid = talk_base::HostToNetwork32(rcv.rcv_ppid);
|
||||
SctpInboundPacket* packet = new SctpInboundPacket();
|
||||
packet->buffer.SetData(data, length);
|
||||
packet->params.ssrc = rcv.rcv_sid;
|
||||
packet->params.seq_num = rcv.rcv_ssn;
|
||||
packet->params.timestamp = rcv.rcv_tsn;
|
||||
packet->params.type =
|
||||
static_cast<cricket::DataMessageType>(native_ppid);
|
||||
packet->flags = flags;
|
||||
channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET,
|
||||
talk_base::WrapMessageData(packet));
|
||||
@ -371,7 +374,8 @@ bool SctpDataMediaChannel::AddSendStream(const StreamParams& 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(...): "
|
||||
<< "Not adding data send stream '" << stream.id
|
||||
<< "' with ssrc=" << stream.first_ssrc()
|
||||
@ -379,17 +383,17 @@ bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
send_streams_.push_back(stream);
|
||||
streams_.push_back(stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SctpDataMediaChannel::RemoveSendStream(uint32 ssrc) {
|
||||
StreamParams found_stream;
|
||||
if (!GetStreamBySsrc(send_streams_, ssrc, &found_stream)) {
|
||||
if (!GetStreamBySsrc(streams_, ssrc, &found_stream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RemoveStreamBySsrc(&send_streams_, ssrc);
|
||||
RemoveStreamBySsrc(&streams_, ssrc);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -401,7 +405,7 @@ bool SctpDataMediaChannel::AddRecvStream(const StreamParams& 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(...): "
|
||||
<< "Not adding data recv stream '" << stream.id
|
||||
<< "' with ssrc=" << stream.first_ssrc()
|
||||
@ -409,7 +413,7 @@ bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
recv_streams_.push_back(stream);
|
||||
streams_.push_back(stream);
|
||||
LOG(LS_VERBOSE) << debug_name_ << "->AddRecvStream(...): "
|
||||
<< "Added data recv stream '" << stream.id
|
||||
<< "' with ssrc=" << stream.first_ssrc();
|
||||
@ -417,7 +421,7 @@ bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
|
||||
}
|
||||
|
||||
bool SctpDataMediaChannel::RemoveRecvStream(uint32 ssrc) {
|
||||
RemoveStreamBySsrc(&recv_streams_, ssrc);
|
||||
RemoveStreamBySsrc(&streams_, ssrc);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -438,7 +442,8 @@ bool SctpDataMediaChannel::SendData(
|
||||
}
|
||||
|
||||
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(...): "
|
||||
<< "Not sending data because ssrc is unknown: "
|
||||
<< params.ssrc;
|
||||
@ -471,7 +476,7 @@ bool SctpDataMediaChannel::SendData(
|
||||
sndinfo.snd_flags = 0;
|
||||
// TODO(pthatcher): Once data types are added to SendParams, this can be set
|
||||
// from SendParams.
|
||||
sndinfo.snd_ppid = talk_base::HostToNetwork32(PPID_NONE);
|
||||
sndinfo.snd_ppid = talk_base::HostToNetwork32(params.type);
|
||||
sndinfo.snd_context = 0;
|
||||
sndinfo.snd_assoc_id = 0;
|
||||
ssize_t res = usrsctp_sendv(sock_, payload.data(),
|
||||
@ -548,9 +553,13 @@ void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
|
||||
void SctpDataMediaChannel::OnDataFromSctpToChannel(
|
||||
const ReceiveDataParams& params, talk_base::Buffer* buffer) {
|
||||
StreamParams found_stream;
|
||||
if (!GetStreamBySsrc(recv_streams_, params.ssrc, &found_stream)) {
|
||||
LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
|
||||
<< "Received packet for unknown ssrc: " << params.ssrc;
|
||||
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(...): "
|
||||
<< "Received packet for unknown ssrc: " << params.ssrc;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -218,8 +218,8 @@ class SctpDataMediaChannel : public DataMediaChannel,
|
||||
bool sending_;
|
||||
// receiving_ controls whether inbound packets are thrown away.
|
||||
bool receiving_;
|
||||
std::vector<StreamParams> send_streams_;
|
||||
std::vector<StreamParams> recv_streams_;
|
||||
// Unified send/receive streams, as each is bidirectional.
|
||||
std::vector<StreamParams> streams_;
|
||||
|
||||
// A human-readable name for debugging messages.
|
||||
std::string debug_name_;
|
||||
|
@ -266,7 +266,9 @@ class FakeWebRtcVoiceEngine
|
||||
virtual webrtc::AudioProcessing* audio_processing() OVERRIDE {
|
||||
return NULL;
|
||||
}
|
||||
#ifndef USE_WEBRTC_DEV_BRANCH
|
||||
WEBRTC_STUB(MaxNumOfChannels, ());
|
||||
#endif
|
||||
WEBRTC_FUNC(CreateChannel, ()) {
|
||||
if (fail_create_channel_) {
|
||||
return -1;
|
||||
|
@ -76,6 +76,13 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateUdpSocket(
|
||||
|
||||
AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket(
|
||||
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 =
|
||||
socket_factory()->CreateAsyncSocket(local_address.family(),
|
||||
SOCK_STREAM);
|
||||
@ -92,6 +99,7 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket(
|
||||
|
||||
// If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket.
|
||||
if (opts & PacketSocketFactory::OPT_SSLTCP) {
|
||||
ASSERT(!(opts & PacketSocketFactory::OPT_TLS));
|
||||
socket = new talk_base::AsyncSSLSocket(socket);
|
||||
}
|
||||
|
||||
@ -108,6 +116,13 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket(
|
||||
AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
|
||||
const SocketAddress& local_address, const SocketAddress& remote_address,
|
||||
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 =
|
||||
socket_factory()->CreateAsyncSocket(local_address.family(), SOCK_STREAM);
|
||||
if (!socket) {
|
||||
@ -133,6 +148,7 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
|
||||
|
||||
// If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket.
|
||||
if (opts & PacketSocketFactory::OPT_SSLTCP) {
|
||||
ASSERT(!(opts & PacketSocketFactory::OPT_TLS));
|
||||
socket = new talk_base::AsyncSSLSocket(socket);
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,8 @@ const buzz::StaticQName QN_JINGLE_RTP_PAYLOADTYPE =
|
||||
const buzz::StaticQName QN_JINGLE_RTP_BANDWIDTH =
|
||||
{ NS_JINGLE_RTP, LN_BANDWIDTH };
|
||||
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_JINGLE_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_BANDWIDTH;
|
||||
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_URI;
|
||||
|
||||
|
@ -56,6 +56,10 @@ class DtlsTransport : public Base {
|
||||
Base::DestroyAllChannels();
|
||||
}
|
||||
|
||||
virtual void SetIdentity_w(talk_base::SSLIdentity* identity) {
|
||||
identity_ = identity;
|
||||
}
|
||||
|
||||
virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl*
|
||||
channel) {
|
||||
talk_base::SSLFingerprint* local_fp =
|
||||
|
@ -226,6 +226,9 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
void SetAllocationStepDelay(uint32 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::scoped_ptr<cricket::PortAllocator> allocator_;
|
||||
@ -398,6 +401,9 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
void SetAllocationStepDelay(int endpoint, uint32 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) {
|
||||
int32 connect_start = talk_base::Time(), connect_time;
|
||||
@ -1222,6 +1228,44 @@ TEST_F(P2PTransportChannelTest, IncomingOnlyOpen) {
|
||||
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
|
||||
// to interesting behavior because the STUN server will only give out the
|
||||
// address of the outermost NAT.
|
||||
|
@ -37,8 +37,9 @@ class AsyncPacketSocket;
|
||||
class PacketSocketFactory {
|
||||
public:
|
||||
enum Options {
|
||||
OPT_SSLTCP = 0x01,
|
||||
OPT_STUN = 0x02,
|
||||
OPT_SSLTCP = 0x01, // Pseudo-TLS.
|
||||
OPT_TLS = 0x02,
|
||||
OPT_STUN = 0x04,
|
||||
};
|
||||
|
||||
PacketSocketFactory() { }
|
||||
|
@ -104,9 +104,12 @@ bool StringToProto(const char* value, ProtocolType* proto);
|
||||
struct ProtocolAddress {
|
||||
talk_base::SocketAddress address;
|
||||
ProtocolType proto;
|
||||
bool secure;
|
||||
|
||||
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
|
||||
|
@ -117,7 +117,8 @@ class PortAllocator : public sigslot::has_slots<> {
|
||||
flags_(kDefaultPortAllocatorFlags),
|
||||
min_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.
|
||||
}
|
||||
virtual ~PortAllocator();
|
||||
@ -155,11 +156,16 @@ class PortAllocator : public sigslot::has_slots<> {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 step_delay() const { return step_delay_; }
|
||||
void set_step_delay(uint32 delay) {
|
||||
ASSERT(delay >= kMinimumStepDelay);
|
||||
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:
|
||||
virtual PortAllocatorSession* CreateSessionInternal(
|
||||
@ -177,6 +183,7 @@ class PortAllocator : public sigslot::has_slots<> {
|
||||
int max_port_;
|
||||
uint32 step_delay_;
|
||||
SessionMuxerMap muxers_;
|
||||
bool allow_tcp_listen_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
@ -288,6 +288,11 @@ bool TransportProxy::OnRemoteCandidates(const Candidates& candidates,
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransportProxy::SetIdentity(
|
||||
talk_base::SSLIdentity* identity) {
|
||||
transport_->get()->SetIdentity(identity);
|
||||
}
|
||||
|
||||
std::string BaseSession::StateToString(State state) {
|
||||
switch (state) {
|
||||
case Session::STATE_INIT:
|
||||
@ -368,6 +373,17 @@ BaseSession::~BaseSession() {
|
||||
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,
|
||||
ContentAction action) {
|
||||
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.
|
||||
void SetRole(TransportRole role);
|
||||
void SetIdentity(talk_base::SSLIdentity* identity);
|
||||
bool SetLocalTransportDescription(const TransportDescription& description,
|
||||
ContentAction action);
|
||||
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.
|
||||
virtual bool GetStats(SessionStats* stats);
|
||||
|
||||
talk_base::SSLIdentity* identity() { return identity_; }
|
||||
|
||||
protected:
|
||||
// Specifies the identity to use in this session.
|
||||
bool SetIdentity(talk_base::SSLIdentity* identity);
|
||||
|
||||
bool PushdownTransportDescription(ContentSource source,
|
||||
ContentAction action);
|
||||
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_; }
|
||||
// Get a TransportProxy by content_name or transport. NULL if not found.
|
||||
TransportProxy* GetTransportProxy(const std::string& content_name);
|
||||
|
@ -100,7 +100,7 @@ Session* SessionManager::CreateSession(
|
||||
|
||||
Session* session = new Session(this, local_name, initiator_name,
|
||||
sid, content_type, client);
|
||||
session->set_identity(transport_desc_factory_.identity());
|
||||
session->SetIdentity(transport_desc_factory_.identity());
|
||||
session_map_[session->id()] = session;
|
||||
session->SignalRequestSignaling.connect(
|
||||
this, &SessionManager::OnRequestSignaling);
|
||||
|
@ -58,7 +58,8 @@ enum {
|
||||
MSG_SETROLE = 16,
|
||||
MSG_SETLOCALDESCRIPTION = 17,
|
||||
MSG_SETREMOTEDESCRIPTION = 18,
|
||||
MSG_GETSTATS = 19
|
||||
MSG_GETSTATS = 19,
|
||||
MSG_SETIDENTITY = 20,
|
||||
};
|
||||
|
||||
struct ChannelParams : public talk_base::MessageData {
|
||||
@ -102,6 +103,13 @@ struct StatsParam : public talk_base::MessageData {
|
||||
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,
|
||||
talk_base::Thread* worker_thread,
|
||||
const std::string& content_name,
|
||||
@ -133,6 +141,11 @@ void Transport::SetRole(TransportRole role) {
|
||||
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(
|
||||
const TransportDescription& description, ContentAction action) {
|
||||
TransportDescriptionParams params(description, action);
|
||||
@ -801,6 +814,11 @@ void Transport::OnMessage(talk_base::Message* msg) {
|
||||
params->result = GetStats_w(params->stats);
|
||||
}
|
||||
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; }
|
||||
uint64 tiebreaker() { return tiebreaker_; }
|
||||
|
||||
// Must be called before applying local session description.
|
||||
void SetIdentity(talk_base::SSLIdentity* identity);
|
||||
|
||||
TransportProtocol protocol() const { return protocol_; }
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
virtual void SetIdentity_w(talk_base::SSLIdentity* identity) {}
|
||||
|
||||
// Pushes down the transport parameters from the local description, such
|
||||
// as the ICE ufrag and pwd.
|
||||
// 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
|
||||
}
|
||||
|
||||
static int GetRelayPreference(cricket::ProtocolType proto) {
|
||||
static int GetRelayPreference(cricket::ProtocolType proto, bool secure) {
|
||||
int relay_preference = ICE_TYPE_PREFERENCE_RELAY;
|
||||
if (proto == cricket::PROTO_TCP)
|
||||
if (proto == cricket::PROTO_TCP) {
|
||||
relay_preference -= 1;
|
||||
else if (proto == cricket::PROTO_SSLTCP)
|
||||
relay_preference -= 2;
|
||||
if (secure)
|
||||
relay_preference -= 1;
|
||||
}
|
||||
|
||||
ASSERT(relay_preference >= 0);
|
||||
return relay_preference;
|
||||
@ -223,9 +224,15 @@ void TurnPort::PrepareAddress() {
|
||||
socket_.reset(socket_factory()->CreateUdpSocket(
|
||||
talk_base::SocketAddress(ip(), 0), min_port(), max_port()));
|
||||
} 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(
|
||||
talk_base::SocketAddress(ip(), 0), server_address_.address,
|
||||
proxy(), user_agent(), talk_base::PacketSocketFactory::OPT_STUN));
|
||||
proxy(), user_agent(), opts));
|
||||
}
|
||||
|
||||
if (!socket_) {
|
||||
@ -412,8 +419,12 @@ void TurnPort::OnStunAddress(const talk_base::SocketAddress& address) {
|
||||
|
||||
void TurnPort::OnAllocateSuccess(const talk_base::SocketAddress& address) {
|
||||
connected_ = true;
|
||||
AddAddress(address, socket_->GetLocalAddress(), "udp",
|
||||
RELAY_PORT_TYPE, GetRelayPreference(server_address_.proto), true);
|
||||
AddAddress(address,
|
||||
socket_->GetLocalAddress(),
|
||||
"udp",
|
||||
RELAY_PORT_TYPE,
|
||||
GetRelayPreference(server_address_.proto, server_address_.secure),
|
||||
true);
|
||||
}
|
||||
|
||||
void TurnPort::OnAllocateError() {
|
||||
|
@ -66,10 +66,10 @@ static const char kTurnUsername[] = "test";
|
||||
static const char kTurnPassword[] = "test";
|
||||
static const int kTimeout = 1000;
|
||||
|
||||
static const cricket::ProtocolAddress kTurnUdpProtoAddr(kTurnUdpIntAddr,
|
||||
cricket::PROTO_UDP);
|
||||
static const cricket::ProtocolAddress kTurnTcpProtoAddr(kTurnTcpIntAddr,
|
||||
cricket::PROTO_TCP);
|
||||
static const cricket::ProtocolAddress kTurnUdpProtoAddr(
|
||||
kTurnUdpIntAddr, cricket::PROTO_UDP);
|
||||
static const cricket::ProtocolAddress kTurnTcpProtoAddr(
|
||||
kTurnTcpIntAddr, cricket::PROTO_TCP);
|
||||
|
||||
class TurnPortTest : public testing::Test,
|
||||
public sigslot::has_slots<> {
|
||||
@ -295,6 +295,7 @@ TEST_F(TurnPortTest, TestTurnConnection) {
|
||||
TestTurnConnection();
|
||||
}
|
||||
|
||||
// Test that we can establish a TCP connection with TURN server.
|
||||
TEST_F(TurnPortTest, TestTurnTcpConnection) {
|
||||
talk_base::AsyncSocket* tcp_server_socket =
|
||||
CreateServerSocket(kTurnTcpIntAddr);
|
||||
@ -304,6 +305,18 @@ TEST_F(TurnPortTest, TestTurnTcpConnection) {
|
||||
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.
|
||||
// Here server will send a 438 STALE_NONCE error message for
|
||||
// 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);
|
||||
if (accepted_socket != NULL) {
|
||||
ProtocolType proto = server_listen_sockets_[server_socket];
|
||||
if (proto == PROTO_SSLTCP) {
|
||||
accepted_socket = new talk_base::AsyncSSLServerSocket(accepted_socket);
|
||||
}
|
||||
cricket::AsyncStunTCPSocket* tcp_socket =
|
||||
new cricket::AsyncStunTCPSocket(accepted_socket, false);
|
||||
|
||||
|
@ -99,13 +99,6 @@ class BasicPortAllocator : public PortAllocator {
|
||||
const std::string& ice_ufrag,
|
||||
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:
|
||||
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,
|
||||
const std::string& key, const int def) {
|
||||
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);
|
||||
|
||||
*codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
|
||||
ParseFeedbackParams(elem, &codec->feedback_params);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -506,6 +526,7 @@ bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) {
|
||||
|
||||
*codec = VideoCodec(id, name, width, height, framerate, 0);
|
||||
codec->params = paramap;
|
||||
ParseFeedbackParams(elem, &codec->feedback_params);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -517,6 +538,7 @@ bool ParseJingleDataCodec(const buzz::XmlElement* elem, DataCodec* codec) {
|
||||
std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
|
||||
|
||||
*codec = DataCodec(id, name, 0);
|
||||
ParseFeedbackParams(elem, &codec->feedback_params);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -543,12 +565,16 @@ bool ParseJingleAudioContent(const buzz::XmlElement* content_elem,
|
||||
talk_base::scoped_ptr<AudioContentDescription> audio(
|
||||
new AudioContentDescription());
|
||||
|
||||
FeedbackParams content_feedback_params;
|
||||
ParseFeedbackParams(content_elem, &content_feedback_params);
|
||||
|
||||
for (const buzz::XmlElement* payload_elem =
|
||||
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
|
||||
payload_elem != NULL;
|
||||
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
|
||||
AudioCodec codec;
|
||||
if (ParseJingleAudioCodec(payload_elem, &codec)) {
|
||||
AddFeedbackParams(content_feedback_params, &codec.feedback_params);
|
||||
audio->AddCodec(codec);
|
||||
}
|
||||
}
|
||||
@ -579,12 +605,16 @@ bool ParseJingleVideoContent(const buzz::XmlElement* content_elem,
|
||||
talk_base::scoped_ptr<VideoContentDescription> video(
|
||||
new VideoContentDescription());
|
||||
|
||||
FeedbackParams content_feedback_params;
|
||||
ParseFeedbackParams(content_elem, &content_feedback_params);
|
||||
|
||||
for (const buzz::XmlElement* payload_elem =
|
||||
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
|
||||
payload_elem != NULL;
|
||||
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
|
||||
VideoCodec codec;
|
||||
if (ParseJingleVideoCodec(payload_elem, &codec)) {
|
||||
AddFeedbackParams(content_feedback_params, &codec.feedback_params);
|
||||
video->AddCodec(codec);
|
||||
}
|
||||
}
|
||||
@ -645,12 +675,16 @@ bool ParseJingleRtpDataContent(const buzz::XmlElement* content_elem,
|
||||
ParseError* error) {
|
||||
DataContentDescription* data = new DataContentDescription();
|
||||
|
||||
FeedbackParams content_feedback_params;
|
||||
ParseFeedbackParams(content_elem, &content_feedback_params);
|
||||
|
||||
for (const buzz::XmlElement* payload_elem =
|
||||
content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
|
||||
payload_elem != NULL;
|
||||
payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
|
||||
DataCodec codec;
|
||||
if (ParseJingleDataCodec(payload_elem, &codec)) {
|
||||
AddFeedbackParams(content_feedback_params, &codec.feedback_params);
|
||||
data->AddCodec(codec);
|
||||
}
|
||||
}
|
||||
@ -853,6 +887,18 @@ buzz::XmlElement* CreatePayloadTypeParameterElem(
|
||||
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* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
|
||||
|
||||
@ -869,6 +915,8 @@ buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
|
||||
AddXmlAttr(elem, QN_CHANNELS, codec.channels);
|
||||
}
|
||||
|
||||
AddRtcpFeedbackElem(elem, codec.feedback_params);
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
@ -883,6 +931,9 @@ buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) {
|
||||
PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
|
||||
elem->AddElement(CreatePayloadTypeParameterElem(
|
||||
PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
|
||||
|
||||
AddRtcpFeedbackElem(elem, codec.feedback_params);
|
||||
|
||||
CodecParameterMap::const_iterator param_iter;
|
||||
for (param_iter = codec.params.begin(); param_iter != codec.params.end();
|
||||
++param_iter) {
|
||||
@ -899,6 +950,8 @@ buzz::XmlElement* CreateJingleDataCodecElem(const DataCodec& codec) {
|
||||
AddXmlAttr(elem, QN_ID, codec.id);
|
||||
elem->AddAttr(QN_NAME, codec.name);
|
||||
|
||||
AddRtcpFeedbackElem(elem, codec.feedback_params);
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,16 @@
|
||||
#include "talk/xmllite/xmlprinter.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 tests check that our messages have codecs in the correct order.
|
||||
static const cricket::AudioCodec kAudioCodecs[] = {
|
||||
@ -377,6 +387,42 @@ const std::string kJingleInitiateDifferentPreference(
|
||||
" </jingle> " \
|
||||
"</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(
|
||||
"<iq xmlns='jabber:client' from='me@domain.com/resource' " \
|
||||
" to='user@domain.com/resource' type='set' id='123'> " \
|
||||
@ -1151,7 +1197,7 @@ static cricket::CallOptions VideoCallOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
buzz::XmlElement* CopyElement(const buzz::XmlElement* elem) {
|
||||
static buzz::XmlElement* CopyElement(const buzz::XmlElement* elem) {
|
||||
return new buzz::XmlElement(*elem);
|
||||
}
|
||||
|
||||
@ -1164,8 +1210,8 @@ static std::string AddEncryption(std::string stanza, std::string encryption) {
|
||||
return stanza;
|
||||
}
|
||||
|
||||
int IntFromJingleCodecParameter(const buzz::XmlElement* parameter,
|
||||
const std::string& expected_name) {
|
||||
static int IntFromJingleCodecParameter(const buzz::XmlElement* parameter,
|
||||
const std::string& expected_name) {
|
||||
if (parameter) {
|
||||
const std::string& actual_name =
|
||||
parameter->Attr(cricket::QN_PAYLOADTYPE_PARAMETER_NAME);
|
||||
@ -1181,6 +1227,18 @@ int IntFromJingleCodecParameter(const buzz::XmlElement* parameter,
|
||||
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
|
||||
// that XML will be in various contents (Gingle and Jingle), we need an
|
||||
// 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);
|
||||
}
|
||||
|
||||
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(
|
||||
const buzz::XmlElement* payload_type) {
|
||||
int id = 0;
|
||||
@ -1272,7 +1344,9 @@ class JingleSessionTestParser : public MediaSessionTestParser {
|
||||
channels = atoi(payload_type->Attr(
|
||||
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(
|
||||
@ -1301,8 +1375,9 @@ class JingleSessionTestParser : public MediaSessionTestParser {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cricket::VideoCodec(id, name, width, height, framerate, 0);
|
||||
VideoCodec codec = VideoCodec(id, name, width, height, framerate, 0);
|
||||
ParsePayloadTypeFeedbackParameters(payload_type, &codec.feedback_params);
|
||||
return codec;
|
||||
}
|
||||
|
||||
cricket::DataCodec DataCodecFromPayloadType(
|
||||
@ -1315,7 +1390,9 @@ class JingleSessionTestParser : public MediaSessionTestParser {
|
||||
if (payload_type->HasAttr(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) {
|
||||
@ -1482,14 +1559,24 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
fme_ = new cricket::FakeMediaEngine();
|
||||
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>
|
||||
audio_codecs(kAudioCodecs, kAudioCodecs + ARRAY_SIZE(kAudioCodecs));
|
||||
SetCodecFeedbackParams(&audio_codecs, params_nack);
|
||||
fme_->SetAudioCodecs(audio_codecs);
|
||||
std::vector<cricket::VideoCodec>
|
||||
video_codecs(kVideoCodecs, kVideoCodecs + ARRAY_SIZE(kVideoCodecs));
|
||||
SetCodecFeedbackParams(&video_codecs, params_nack_fir);
|
||||
fme_->SetVideoCodecs(video_codecs);
|
||||
std::vector<cricket::DataCodec>
|
||||
data_codecs(kDataCodecs, kDataCodecs + ARRAY_SIZE(kDataCodecs));
|
||||
SetCodecFeedbackParams(&data_codecs, params_nack);
|
||||
fdme_->SetDataCodecs(data_codecs);
|
||||
|
||||
client_ = new cricket::MediaSessionClient(
|
||||
@ -1551,14 +1638,23 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
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::ContentInfo* content =
|
||||
cricket::GetFirstAudioContent(sdesc);
|
||||
if (content == NULL)
|
||||
return NULL;
|
||||
return static_cast<const cricket::AudioContentDescription*>(
|
||||
content->description);
|
||||
return static_cast<const AudioContentDescription*>(content->description);
|
||||
}
|
||||
|
||||
const cricket::VideoContentDescription* GetFirstVideoContentDescription(
|
||||
@ -1573,7 +1669,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
|
||||
void CheckCryptoFromGoodIncomingInitiate(const cricket::Session* session) {
|
||||
ASSERT_TRUE(session != NULL);
|
||||
const cricket::AudioContentDescription* content =
|
||||
const AudioContentDescription* content =
|
||||
GetFirstAudioContentDescription(session->remote_description());
|
||||
ASSERT_TRUE(content != NULL);
|
||||
ASSERT_EQ(2U, content->cryptos().size());
|
||||
@ -1588,7 +1684,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
}
|
||||
|
||||
void CheckCryptoForGoodOutgoingAccept(const cricket::Session* session) {
|
||||
const cricket::AudioContentDescription* content =
|
||||
const AudioContentDescription* content =
|
||||
GetFirstAudioContentDescription(session->local_description());
|
||||
ASSERT_EQ(1U, content->cryptos().size());
|
||||
ASSERT_EQ(145, content->cryptos()[0].tag);
|
||||
@ -1597,7 +1693,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
}
|
||||
|
||||
void CheckBadCryptoFromIncomingInitiate(const cricket::Session* session) {
|
||||
const cricket::AudioContentDescription* content =
|
||||
const AudioContentDescription* content =
|
||||
GetFirstAudioContentDescription(session->remote_description());
|
||||
ASSERT_EQ(1U, content->cryptos().size());
|
||||
ASSERT_EQ(145, content->cryptos()[0].tag);
|
||||
@ -1607,11 +1703,22 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
}
|
||||
|
||||
void CheckNoCryptoForOutgoingAccept(const cricket::Session* session) {
|
||||
const cricket::AudioContentDescription* content =
|
||||
const AudioContentDescription* content =
|
||||
GetFirstAudioContentDescription(session->local_description());
|
||||
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,
|
||||
const cricket::SessionDescription* sdesc) {
|
||||
const cricket::VideoContentDescription* video =
|
||||
@ -1638,9 +1745,10 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
|
||||
buzz::XmlElement* e = PayloadTypeFromContent(content);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
cricket::DataCodec codec = parser_->DataCodecFromPayloadType(e);
|
||||
cricket::DataCodec codec = DataCodecFromPayloadType(e);
|
||||
EXPECT_EQ(127, codec.id);
|
||||
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]->remote_description());
|
||||
@ -1675,7 +1783,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
}
|
||||
|
||||
void CheckAudioSsrcForIncomingAccept(const cricket::Session* session) {
|
||||
const cricket::AudioContentDescription* audio =
|
||||
const AudioContentDescription* audio =
|
||||
GetFirstAudioContentDescription(session->remote_description());
|
||||
ASSERT_TRUE(audio != NULL);
|
||||
ASSERT_EQ(kAudioSsrc, audio->first_ssrc());
|
||||
@ -1716,6 +1824,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
call_->sessions()[0]->remote_description());
|
||||
CheckVideoRtcpMux(expected_video_rtcp_mux_,
|
||||
call_->sessions()[0]->remote_description());
|
||||
CheckRtcpFb(call_->sessions()[0]->remote_description());
|
||||
if (expect_incoming_crypto_) {
|
||||
CheckCryptoFromGoodIncomingInitiate(call_->sessions()[0]);
|
||||
}
|
||||
@ -1739,7 +1848,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
CheckCryptoForGoodOutgoingAccept(call_->sessions()[0]);
|
||||
}
|
||||
|
||||
if (options.data_channel_type == cricket::DCT_RTP) {
|
||||
if (options.data_channel_type == cricket::DCT_RTP) {
|
||||
CheckDataRtcpMux(true, call_->sessions()[0]->local_description());
|
||||
CheckDataRtcpMux(true, call_->sessions()[0]->remote_description());
|
||||
// TODO(pthatcher): Check rtcpmux and crypto?
|
||||
@ -1841,7 +1950,23 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
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) {
|
||||
if (initial_protocol_ == cricket::PROTOCOL_JINGLE) {
|
||||
// rtcp fb is only implemented for jingle.
|
||||
ExpectRtcpFb();
|
||||
}
|
||||
|
||||
client_->CreateCall();
|
||||
ASSERT_TRUE(call_ != NULL);
|
||||
call_->InitiateSession(buzz::Jid("me@mydomain.com"),
|
||||
@ -1861,159 +1986,92 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
buzz::XmlElement* e = PayloadTypeFromContent(content);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
cricket::AudioCodec codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(103, codec.id);
|
||||
ASSERT_EQ("ISAC", codec.name);
|
||||
ASSERT_EQ(16000, codec.clockrate);
|
||||
ASSERT_EQ(0, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 103, "ISAC", 16000, 0, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(104, codec.id);
|
||||
ASSERT_EQ("ISAC", codec.name);
|
||||
ASSERT_EQ(32000, codec.clockrate);
|
||||
ASSERT_EQ(0, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 104, "ISAC", 32000, 0, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(119, codec.id);
|
||||
ASSERT_EQ("ISACLC", codec.name);
|
||||
ASSERT_EQ(16000, codec.clockrate);
|
||||
ASSERT_EQ(40000, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 119, "ISACLC", 16000, 40000, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(99, codec.id);
|
||||
ASSERT_EQ("speex", codec.name);
|
||||
ASSERT_EQ(16000, codec.clockrate);
|
||||
ASSERT_EQ(22000, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 99, "speex", 16000, 22000, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(97, codec.id);
|
||||
ASSERT_EQ("IPCMWB", codec.name);
|
||||
ASSERT_EQ(16000, codec.clockrate);
|
||||
ASSERT_EQ(80000, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 97, "IPCMWB", 16000, 80000, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(9, codec.id);
|
||||
ASSERT_EQ("G722", codec.name);
|
||||
ASSERT_EQ(16000, codec.clockrate);
|
||||
ASSERT_EQ(64000, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 9, "G722", 16000, 64000, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(102, codec.id);
|
||||
ASSERT_EQ("iLBC", codec.name);
|
||||
ASSERT_EQ(8000, codec.clockrate);
|
||||
ASSERT_EQ(13300, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 102, "iLBC", 8000, 13300, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(98, codec.id);
|
||||
ASSERT_EQ("speex", codec.name);
|
||||
ASSERT_EQ(8000, codec.clockrate);
|
||||
ASSERT_EQ(11000, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 98, "speex", 8000, 11000, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(3, codec.id);
|
||||
ASSERT_EQ("GSM", codec.name);
|
||||
ASSERT_EQ(8000, codec.clockrate);
|
||||
ASSERT_EQ(13000, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 3, "GSM", 8000, 13000, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(100, codec.id);
|
||||
ASSERT_EQ("EG711U", codec.name);
|
||||
ASSERT_EQ(8000, codec.clockrate);
|
||||
ASSERT_EQ(64000, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 100, "EG711U", 8000, 64000, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(101, codec.id);
|
||||
ASSERT_EQ("EG711A", codec.name);
|
||||
ASSERT_EQ(8000, codec.clockrate);
|
||||
ASSERT_EQ(64000, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 101, "EG711A", 8000, 64000, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(0, codec.id);
|
||||
ASSERT_EQ("PCMU", codec.name);
|
||||
ASSERT_EQ(8000, codec.clockrate);
|
||||
ASSERT_EQ(64000, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 0, "PCMU", 8000, 64000, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(8, codec.id);
|
||||
ASSERT_EQ("PCMA", codec.name);
|
||||
ASSERT_EQ(8000, codec.clockrate);
|
||||
ASSERT_EQ(64000, codec.bitrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 8, "PCMA", 8000, 64000, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(126, codec.id);
|
||||
ASSERT_EQ("CN", codec.name);
|
||||
ASSERT_EQ(32000, codec.clockrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 126, "CN", 32000, 0, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(105, codec.id);
|
||||
ASSERT_EQ("CN", codec.name);
|
||||
ASSERT_EQ(16000, codec.clockrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 105, "CN", 16000, 0, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(13, codec.id);
|
||||
ASSERT_EQ("CN", codec.name);
|
||||
ASSERT_EQ(8000, codec.clockrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 13, "CN", 8000, 0, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(117, codec.id);
|
||||
ASSERT_EQ("red", codec.name);
|
||||
ASSERT_EQ(8000, codec.clockrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 117, "red", 8000, 0, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e != NULL);
|
||||
codec = AudioCodecFromPayloadType(e);
|
||||
ASSERT_EQ(106, codec.id);
|
||||
ASSERT_EQ("telephone-event", codec.name);
|
||||
ASSERT_EQ(8000, codec.clockrate);
|
||||
ASSERT_EQ(1, codec.channels);
|
||||
VerifyAudioCodec(codec, 106, "telephone-event", 8000, 0, 1);
|
||||
|
||||
e = NextFromPayloadType(e);
|
||||
ASSERT_TRUE(e == NULL);
|
||||
@ -2073,6 +2131,14 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
ASSERT_EQ(talk_base::ToString(options.video_bandwidth / 1000),
|
||||
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) {
|
||||
@ -2705,6 +2771,28 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
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:
|
||||
void OnSendStanza(cricket::SessionManager* manager,
|
||||
const buzz::XmlElement* stanza) {
|
||||
@ -2749,6 +2837,9 @@ class MediaSessionClientTest : public sigslot::has_slots<> {
|
||||
bool expect_outgoing_crypto_;
|
||||
int expected_video_bandwidth_;
|
||||
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_removed_;
|
||||
};
|
||||
@ -2763,6 +2854,17 @@ MediaSessionClientTest* JingleTest() {
|
||||
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) {
|
||||
talk_base::scoped_ptr<MediaSessionClientTest> test(JingleTest());
|
||||
talk_base::scoped_ptr<buzz::XmlElement> elem;
|
||||
|
Loading…
x
Reference in New Issue
Block a user