blob: 3a6046e817e665b708329b9fa921871f5c228af6 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2012, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27#include "talk/app/webrtc/datachannel.h"
28
29#include <string>
30
wu@webrtc.org78187522013-10-07 23:32:02 +000031#include "talk/app/webrtc/mediastreamprovider.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000032#include "talk/base/logging.h"
33#include "talk/base/refcount.h"
wu@webrtc.orgcecfd182013-10-30 05:18:12 +000034#include "talk/media/sctp/sctputils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035
36namespace webrtc {
37
wu@webrtc.orgd64719d2013-08-01 00:00:07 +000038static size_t kMaxQueuedReceivedDataPackets = 100;
39static size_t kMaxQueuedSendDataPackets = 100;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000040
41talk_base::scoped_refptr<DataChannel> DataChannel::Create(
wu@webrtc.org78187522013-10-07 23:32:02 +000042 DataChannelProviderInterface* provider,
43 cricket::DataChannelType dct,
henrike@webrtc.org28e20752013-07-10 00:45:36 +000044 const std::string& label,
45 const DataChannelInit* config) {
46 talk_base::scoped_refptr<DataChannel> channel(
wu@webrtc.org78187522013-10-07 23:32:02 +000047 new talk_base::RefCountedObject<DataChannel>(provider, dct, label));
henrike@webrtc.org28e20752013-07-10 00:45:36 +000048 if (!channel->Init(config)) {
49 return NULL;
50 }
51 return channel;
52}
53
wu@webrtc.org78187522013-10-07 23:32:02 +000054DataChannel::DataChannel(
55 DataChannelProviderInterface* provider,
56 cricket::DataChannelType dct,
57 const std::string& label)
henrike@webrtc.org28e20752013-07-10 00:45:36 +000058 : label_(label),
59 observer_(NULL),
60 state_(kConnecting),
61 was_ever_writable_(false),
wu@webrtc.org78187522013-10-07 23:32:02 +000062 connected_to_provider_(false),
63 data_channel_type_(dct),
64 provider_(provider),
henrike@webrtc.org28e20752013-07-10 00:45:36 +000065 send_ssrc_set_(false),
66 send_ssrc_(0),
67 receive_ssrc_set_(false),
68 receive_ssrc_(0) {
69}
70
71bool DataChannel::Init(const DataChannelInit* config) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +000072 if (data_channel_type_ == cricket::DCT_RTP &&
73 (config->reliable ||
74 config->id != -1 ||
75 config->maxRetransmits != -1 ||
76 config->maxRetransmitTime != -1)) {
77 LOG(LS_ERROR) << "Failed to initialize the RTP data channel due to "
78 << "invalid DataChannelInit.";
79 return false;
80 } else if (data_channel_type_ == cricket::DCT_SCTP) {
81 if (config->id < -1 ||
82 config->maxRetransmits < -1 ||
83 config->maxRetransmitTime < -1) {
84 LOG(LS_ERROR) << "Failed to initialize the SCTP data channel due to "
henrike@webrtc.org28e20752013-07-10 00:45:36 +000085 << "invalid DataChannelInit.";
86 return false;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +000087 }
88 if (config->maxRetransmits != -1 && config->maxRetransmitTime != -1) {
89 LOG(LS_ERROR) <<
90 "maxRetransmits and maxRetransmitTime should not be both set.";
91 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000092 }
93 config_ = *config;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000094
wu@webrtc.orgcecfd182013-10-30 05:18:12 +000095 // Try to connect to the transport in case the transport channel already
96 // exists.
97 OnTransportChannelCreated();
98 }
99
100 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000101}
102
103DataChannel::~DataChannel() {
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000104 ClearQueuedReceivedData();
105 ClearQueuedSendData();
wu@webrtc.org822fbd82013-08-15 23:38:54 +0000106 ClearQueuedControlData();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000107}
108
109void DataChannel::RegisterObserver(DataChannelObserver* observer) {
110 observer_ = observer;
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000111 DeliverQueuedReceivedData();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000112}
113
114void DataChannel::UnregisterObserver() {
115 observer_ = NULL;
116}
117
118bool DataChannel::reliable() const {
wu@webrtc.org78187522013-10-07 23:32:02 +0000119 if (data_channel_type_ == cricket::DCT_RTP) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000120 return false;
121 } else {
122 return config_.maxRetransmits == -1 &&
123 config_.maxRetransmitTime == -1;
124 }
125}
126
127uint64 DataChannel::buffered_amount() const {
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000128 uint64 buffered_amount = 0;
129 for (std::deque<DataBuffer*>::const_iterator it = queued_send_data_.begin();
130 it != queued_send_data_.end();
131 ++it) {
132 buffered_amount += (*it)->size();
133 }
134 return buffered_amount;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000135}
136
137void DataChannel::Close() {
138 if (state_ == kClosed)
139 return;
140 send_ssrc_ = 0;
141 send_ssrc_set_ = false;
142 SetState(kClosing);
143 UpdateState();
144}
145
146bool DataChannel::Send(const DataBuffer& buffer) {
147 if (state_ != kOpen) {
148 return false;
149 }
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000150 // If the queue is non-empty, we're waiting for SignalReadyToSend,
151 // so just add to the end of the queue and keep waiting.
152 if (!queued_send_data_.empty()) {
153 return QueueSendData(buffer);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000154 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000155
156 cricket::SendDataResult send_result;
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000157 if (!InternalSendWithoutQueueing(buffer, &send_result)) {
158 if (send_result == cricket::SDR_BLOCK) {
159 return QueueSendData(buffer);
160 }
161 // Fail for other results.
162 // TODO(jiayl): We should close the data channel in this case.
163 return false;
164 }
165 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000166}
167
wu@webrtc.org91053e72013-08-10 07:18:04 +0000168void DataChannel::QueueControl(const talk_base::Buffer* buffer) {
169 queued_control_data_.push(buffer);
170}
171
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000172bool DataChannel::SendOpenMessage(const talk_base::Buffer* raw_buffer) {
173 ASSERT(data_channel_type_ == cricket::DCT_SCTP &&
174 was_ever_writable_ &&
175 config_.id >= 0 &&
176 !config_.negotiated);
wu@webrtc.org78187522013-10-07 23:32:02 +0000177
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000178 talk_base::scoped_ptr<const talk_base::Buffer> buffer(raw_buffer);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000179
180 cricket::SendDataParams send_params;
181 send_params.ssrc = config_.id;
182 send_params.ordered = true;
183 send_params.type = cricket::DMT_CONTROL;
184
185 cricket::SendDataResult send_result;
wu@webrtc.org78187522013-10-07 23:32:02 +0000186 bool retval = provider_->SendData(send_params, *buffer, &send_result);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000187 if (!retval && send_result == cricket::SDR_BLOCK) {
188 // Link is congested. Queue for later.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000189 QueueControl(buffer.release());
wu@webrtc.org91053e72013-08-10 07:18:04 +0000190 }
191 return retval;
192}
193
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000194void DataChannel::SetReceiveSsrc(uint32 receive_ssrc) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000195 ASSERT(data_channel_type_ == cricket::DCT_RTP);
196
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000197 if (receive_ssrc_set_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000198 return;
199 }
200 receive_ssrc_ = receive_ssrc;
201 receive_ssrc_set_ = true;
202 UpdateState();
203}
204
205// The remote peer request that this channel shall be closed.
206void DataChannel::RemotePeerRequestClose() {
207 DoClose();
208}
209
210void DataChannel::SetSendSsrc(uint32 send_ssrc) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000211 ASSERT(data_channel_type_ == cricket::DCT_RTP);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000212 if (send_ssrc_set_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000213 return;
214 }
215 send_ssrc_ = send_ssrc;
216 send_ssrc_set_ = true;
217 UpdateState();
218}
219
220// The underlaying data engine is closing.
wu@webrtc.org78187522013-10-07 23:32:02 +0000221// This function makes sure the DataChannel is disconnected and changes state to
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000222// kClosed.
223void DataChannel::OnDataEngineClose() {
224 DoClose();
225}
226
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000227void DataChannel::OnDataReceived(cricket::DataChannel* channel,
228 const cricket::ReceiveDataParams& params,
229 const talk_base::Buffer& payload) {
230 if (params.ssrc != receive_ssrc_) {
231 return;
232 }
233
234 bool binary = (params.type == cricket::DMT_BINARY);
235 talk_base::scoped_ptr<DataBuffer> buffer(new DataBuffer(payload, binary));
236 if (was_ever_writable_ && observer_) {
237 observer_->OnMessage(*buffer.get());
238 } else {
239 if (queued_received_data_.size() > kMaxQueuedReceivedDataPackets) {
240 // TODO(jiayl): We should close the data channel in this case.
241 LOG(LS_ERROR)
242 << "Queued received data exceeds the max number of packes.";
243 ClearQueuedReceivedData();
244 }
245 queued_received_data_.push(buffer.release());
246 }
247}
248
249void DataChannel::OnChannelReady(bool writable) {
250 if (!writable) {
251 return;
252 }
wu@webrtc.org822fbd82013-08-15 23:38:54 +0000253 // Update the readyState and send the queued control message if the channel
254 // is writable for the first time; otherwise it means the channel was blocked
255 // for sending and now unblocked, so send the queued data now.
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000256 if (!was_ever_writable_) {
257 was_ever_writable_ = true;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000258
259 if (data_channel_type_ == cricket::DCT_SCTP && !config_.negotiated) {
260 talk_base::Buffer* payload = new talk_base::Buffer;
261 if (!cricket::WriteDataChannelOpenMessage(label_, config_, payload)) {
262 // TODO(jiayl): close the data channel on this error.
263 LOG(LS_ERROR) << "Could not write data channel OPEN message";
264 return;
265 }
266 SendOpenMessage(payload);
267 }
268
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000269 UpdateState();
wu@webrtc.org822fbd82013-08-15 23:38:54 +0000270 ASSERT(queued_send_data_.empty());
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000271 } else if (state_ == kOpen) {
wu@webrtc.org822fbd82013-08-15 23:38:54 +0000272 DeliverQueuedSendData();
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000273 }
274}
275
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000276void DataChannel::DoClose() {
277 receive_ssrc_set_ = false;
278 send_ssrc_set_ = false;
279 SetState(kClosing);
280 UpdateState();
281}
282
283void DataChannel::UpdateState() {
284 switch (state_) {
285 case kConnecting: {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000286 if (send_ssrc_set_ == receive_ssrc_set_) {
287 if (data_channel_type_ == cricket::DCT_RTP && !connected_to_provider_) {
288 connected_to_provider_ = provider_->ConnectDataChannel(this);
289 provider_->AddRtpDataStream(send_ssrc_, receive_ssrc_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000290 }
291 if (was_ever_writable_) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000292 // TODO(jiayl): Do not transition to kOpen if we failed to send the
293 // OPEN message.
294 DeliverQueuedControlData();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000295 SetState(kOpen);
296 // If we have received buffers before the channel got writable.
297 // Deliver them now.
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000298 DeliverQueuedReceivedData();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000299 }
300 }
301 break;
302 }
303 case kOpen: {
304 break;
305 }
306 case kClosing: {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000307 DisconnectFromTransport();
308
309 if (!send_ssrc_set_ && !receive_ssrc_set_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000310 SetState(kClosed);
311 }
312 break;
313 }
314 case kClosed:
315 break;
316 }
317}
318
319void DataChannel::SetState(DataState state) {
320 state_ = state;
321 if (observer_) {
322 observer_->OnStateChange();
323 }
324}
325
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000326void DataChannel::DisconnectFromTransport() {
327 if (!connected_to_provider_)
328 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000329
wu@webrtc.org78187522013-10-07 23:32:02 +0000330 provider_->DisconnectDataChannel(this);
331 connected_to_provider_ = false;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000332
333 if (data_channel_type_ == cricket::DCT_RTP) {
334 provider_->RemoveRtpDataStream(send_ssrc_, receive_ssrc_);
335 } else {
336 provider_->RemoveSctpDataStream(config_.id);
337 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000338}
339
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000340void DataChannel::DeliverQueuedReceivedData() {
341 if (!was_ever_writable_ || !observer_) {
342 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000343 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000344
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000345 while (!queued_received_data_.empty()) {
346 DataBuffer* buffer = queued_received_data_.front();
347 observer_->OnMessage(*buffer);
348 queued_received_data_.pop();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000349 delete buffer;
350 }
351}
352
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000353void DataChannel::ClearQueuedReceivedData() {
354 while (!queued_received_data_.empty()) {
355 DataBuffer* buffer = queued_received_data_.front();
356 queued_received_data_.pop();
357 delete buffer;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000358 }
359}
360
wu@webrtc.org822fbd82013-08-15 23:38:54 +0000361void DataChannel::DeliverQueuedSendData() {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000362 ASSERT(was_ever_writable_ && state_ == kOpen);
363
364 // TODO(jiayl): Sending OPEN message here contradicts with the pre-condition
365 // that the readyState is open. According to the standard, the channel should
366 // not become open before the OPEN message is sent.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000367 DeliverQueuedControlData();
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000368
369 while (!queued_send_data_.empty()) {
370 DataBuffer* buffer = queued_send_data_.front();
371 cricket::SendDataResult send_result;
372 if (!InternalSendWithoutQueueing(*buffer, &send_result)) {
wu@webrtc.org822fbd82013-08-15 23:38:54 +0000373 LOG(LS_WARNING) << "DeliverQueuedSendData aborted due to send_result "
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000374 << send_result;
375 break;
376 }
377 queued_send_data_.pop_front();
378 delete buffer;
379 }
380}
381
wu@webrtc.org822fbd82013-08-15 23:38:54 +0000382void DataChannel::ClearQueuedControlData() {
383 while (!queued_control_data_.empty()) {
384 const talk_base::Buffer *buf = queued_control_data_.front();
385 queued_control_data_.pop();
386 delete buf;
387 }
388}
389
wu@webrtc.org91053e72013-08-10 07:18:04 +0000390void DataChannel::DeliverQueuedControlData() {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000391 ASSERT(was_ever_writable_);
392 while (!queued_control_data_.empty()) {
393 const talk_base::Buffer* buf = queued_control_data_.front();
394 queued_control_data_.pop();
395 SendOpenMessage(buf);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000396 }
397}
398
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000399void DataChannel::ClearQueuedSendData() {
wu@webrtc.orga0545692013-08-01 22:08:14 +0000400 while (!queued_send_data_.empty()) {
401 DataBuffer* buffer = queued_send_data_.front();
402 queued_send_data_.pop_front();
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000403 delete buffer;
404 }
405}
406
407bool DataChannel::InternalSendWithoutQueueing(
408 const DataBuffer& buffer, cricket::SendDataResult* send_result) {
409 cricket::SendDataParams send_params;
410
411 send_params.ssrc = send_ssrc_;
wu@webrtc.org78187522013-10-07 23:32:02 +0000412 if (data_channel_type_ == cricket::DCT_SCTP) {
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000413 send_params.ordered = config_.ordered;
414 send_params.max_rtx_count = config_.maxRetransmits;
415 send_params.max_rtx_ms = config_.maxRetransmitTime;
416 }
417 send_params.type = buffer.binary ? cricket::DMT_BINARY : cricket::DMT_TEXT;
418
wu@webrtc.org78187522013-10-07 23:32:02 +0000419 return provider_->SendData(send_params, buffer.data, send_result);
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000420}
421
422bool DataChannel::QueueSendData(const DataBuffer& buffer) {
423 if (queued_send_data_.size() > kMaxQueuedSendDataPackets) {
424 LOG(LS_ERROR) << "Can't buffer any more data in the data channel.";
425 return false;
426 }
427 queued_send_data_.push_back(new DataBuffer(buffer));
428 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000429}
430
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000431void DataChannel::SetSctpSid(int sid) {
432 ASSERT(config_.id < 0 && sid >= 0 && data_channel_type_ == cricket::DCT_SCTP);
433 config_.id = sid;
434 provider_->AddSctpDataStream(sid);
435}
436
437void DataChannel::OnTransportChannelCreated() {
438 ASSERT(data_channel_type_ == cricket::DCT_SCTP);
439 if (!connected_to_provider_) {
440 connected_to_provider_ = provider_->ConnectDataChannel(this);
441 }
442 // The sid may have been unassigned when provider_->ConnectDataChannel was
443 // done. So always add the streams even if connected_to_provider_ is true.
444 if (config_.id >= 0) {
445 provider_->AddSctpDataStream(config_.id);
446 }
447}
448
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000449} // namespace webrtc