blob: 497242422f3cb3f2fc16e6427bd2fc1608ac60d4 [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
31#include "talk/app/webrtc/webrtcsession.h"
32#include "talk/base/logging.h"
33#include "talk/base/refcount.h"
34
35namespace webrtc {
36
wu@webrtc.orgd64719d2013-08-01 00:00:07 +000037static size_t kMaxQueuedReceivedDataPackets = 100;
38static size_t kMaxQueuedSendDataPackets = 100;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000039
40talk_base::scoped_refptr<DataChannel> DataChannel::Create(
41 WebRtcSession* session,
42 const std::string& label,
43 const DataChannelInit* config) {
44 talk_base::scoped_refptr<DataChannel> channel(
45 new talk_base::RefCountedObject<DataChannel>(session, label));
46 if (!channel->Init(config)) {
47 return NULL;
48 }
49 return channel;
50}
51
52DataChannel::DataChannel(WebRtcSession* session, const std::string& label)
53 : label_(label),
54 observer_(NULL),
55 state_(kConnecting),
56 was_ever_writable_(false),
57 session_(session),
58 data_session_(NULL),
59 send_ssrc_set_(false),
60 send_ssrc_(0),
61 receive_ssrc_set_(false),
62 receive_ssrc_(0) {
63}
64
65bool DataChannel::Init(const DataChannelInit* config) {
66 if (config) {
67 if (session_->data_channel_type() == cricket::DCT_RTP &&
68 (config->reliable ||
69 config->id != -1 ||
70 config->maxRetransmits != -1 ||
71 config->maxRetransmitTime != -1)) {
72 LOG(LS_ERROR) << "Failed to initialize the RTP data channel due to "
73 << "invalid DataChannelInit.";
74 return false;
75 } else if (session_->data_channel_type() == cricket::DCT_SCTP) {
76 if (config->id < -1 ||
77 config->maxRetransmits < -1 ||
78 config->maxRetransmitTime < -1) {
79 LOG(LS_ERROR) << "Failed to initialize the SCTP data channel due to "
80 << "invalid DataChannelInit.";
81 return false;
82 }
83 if (config->maxRetransmits != -1 && config->maxRetransmitTime != -1) {
84 LOG(LS_ERROR) <<
85 "maxRetransmits and maxRetransmitTime should not be both set.";
86 return false;
87 }
88 }
89 config_ = *config;
90 }
91 return true;
92}
93
94bool DataChannel::HasNegotiationCompleted() {
95 return send_ssrc_set_ == receive_ssrc_set_;
96}
97
98DataChannel::~DataChannel() {
wu@webrtc.orgd64719d2013-08-01 00:00:07 +000099 ClearQueuedReceivedData();
100 ClearQueuedSendData();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000101}
102
103void DataChannel::RegisterObserver(DataChannelObserver* observer) {
104 observer_ = observer;
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000105 DeliverQueuedReceivedData();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000106}
107
108void DataChannel::UnregisterObserver() {
109 observer_ = NULL;
110}
111
112bool DataChannel::reliable() const {
113 if (session_->data_channel_type() == cricket::DCT_RTP) {
114 return false;
115 } else {
116 return config_.maxRetransmits == -1 &&
117 config_.maxRetransmitTime == -1;
118 }
119}
120
121uint64 DataChannel::buffered_amount() const {
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000122 uint64 buffered_amount = 0;
123 for (std::deque<DataBuffer*>::const_iterator it = queued_send_data_.begin();
124 it != queued_send_data_.end();
125 ++it) {
126 buffered_amount += (*it)->size();
127 }
128 return buffered_amount;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000129}
130
131void DataChannel::Close() {
132 if (state_ == kClosed)
133 return;
134 send_ssrc_ = 0;
135 send_ssrc_set_ = false;
136 SetState(kClosing);
137 UpdateState();
138}
139
140bool DataChannel::Send(const DataBuffer& buffer) {
141 if (state_ != kOpen) {
142 return false;
143 }
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000144 // If the queue is non-empty, we're waiting for SignalReadyToSend,
145 // so just add to the end of the queue and keep waiting.
146 if (!queued_send_data_.empty()) {
147 return QueueSendData(buffer);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000148 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000149
150 cricket::SendDataResult send_result;
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000151 if (!InternalSendWithoutQueueing(buffer, &send_result)) {
152 if (send_result == cricket::SDR_BLOCK) {
153 return QueueSendData(buffer);
154 }
155 // Fail for other results.
156 // TODO(jiayl): We should close the data channel in this case.
157 return false;
158 }
159 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000160}
161
162void DataChannel::SetReceiveSsrc(uint32 receive_ssrc) {
163 if (receive_ssrc_set_) {
164 ASSERT(session_->data_channel_type() == cricket::DCT_RTP ||
165 receive_ssrc_ == send_ssrc_);
166 return;
167 }
168 receive_ssrc_ = receive_ssrc;
169 receive_ssrc_set_ = true;
170 UpdateState();
171}
172
173// The remote peer request that this channel shall be closed.
174void DataChannel::RemotePeerRequestClose() {
175 DoClose();
176}
177
178void DataChannel::SetSendSsrc(uint32 send_ssrc) {
179 if (send_ssrc_set_) {
180 ASSERT(session_->data_channel_type() == cricket::DCT_RTP ||
181 receive_ssrc_ == send_ssrc_);
182 return;
183 }
184 send_ssrc_ = send_ssrc;
185 send_ssrc_set_ = true;
186 UpdateState();
187}
188
189// The underlaying data engine is closing.
190// This function make sure the DataChannel is disconneced and change state to
191// kClosed.
192void DataChannel::OnDataEngineClose() {
193 DoClose();
194}
195
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000196void DataChannel::OnDataReceived(cricket::DataChannel* channel,
197 const cricket::ReceiveDataParams& params,
198 const talk_base::Buffer& payload) {
199 if (params.ssrc != receive_ssrc_) {
200 return;
201 }
202
203 bool binary = (params.type == cricket::DMT_BINARY);
204 talk_base::scoped_ptr<DataBuffer> buffer(new DataBuffer(payload, binary));
205 if (was_ever_writable_ && observer_) {
206 observer_->OnMessage(*buffer.get());
207 } else {
208 if (queued_received_data_.size() > kMaxQueuedReceivedDataPackets) {
209 // TODO(jiayl): We should close the data channel in this case.
210 LOG(LS_ERROR)
211 << "Queued received data exceeds the max number of packes.";
212 ClearQueuedReceivedData();
213 }
214 queued_received_data_.push(buffer.release());
215 }
216}
217
218void DataChannel::OnChannelReady(bool writable) {
219 if (!writable) {
220 return;
221 }
222 // Update the readyState if the channel is writable for the first time;
223 // otherwise it means the channel was blocked for sending and now unblocked,
224 // so send the queued data now.
225 if (!was_ever_writable_) {
226 was_ever_writable_ = true;
227 UpdateState();
228 } else if (state_ == kOpen) {
229 SendQueuedSendData();
230 }
231}
232
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000233void DataChannel::DoClose() {
234 receive_ssrc_set_ = false;
235 send_ssrc_set_ = false;
236 SetState(kClosing);
237 UpdateState();
238}
239
240void DataChannel::UpdateState() {
241 switch (state_) {
242 case kConnecting: {
243 if (HasNegotiationCompleted()) {
244 if (!IsConnectedToDataSession()) {
245 ConnectToDataSession();
246 }
247 if (was_ever_writable_) {
248 SetState(kOpen);
249 // If we have received buffers before the channel got writable.
250 // Deliver them now.
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000251 DeliverQueuedReceivedData();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000252 }
253 }
254 break;
255 }
256 case kOpen: {
257 break;
258 }
259 case kClosing: {
260 if (IsConnectedToDataSession()) {
261 DisconnectFromDataSession();
262 }
263 if (HasNegotiationCompleted()) {
264 SetState(kClosed);
265 }
266 break;
267 }
268 case kClosed:
269 break;
270 }
271}
272
273void DataChannel::SetState(DataState state) {
274 state_ = state;
275 if (observer_) {
276 observer_->OnStateChange();
277 }
278}
279
280void DataChannel::ConnectToDataSession() {
281 ASSERT(session_->data_channel() != NULL);
282 if (!session_->data_channel()) {
283 LOG(LS_ERROR) << "The DataEngine does not exist.";
284 return;
285 }
286
287 data_session_ = session_->data_channel();
288 data_session_->SignalReadyToSendData.connect(this,
289 &DataChannel::OnChannelReady);
290 data_session_->SignalDataReceived.connect(this, &DataChannel::OnDataReceived);
291}
292
293void DataChannel::DisconnectFromDataSession() {
294 data_session_->SignalReadyToSendData.disconnect(this);
295 data_session_->SignalDataReceived.disconnect(this);
296 data_session_ = NULL;
297}
298
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000299void DataChannel::DeliverQueuedReceivedData() {
300 if (!was_ever_writable_ || !observer_) {
301 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000302 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000303
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000304 while (!queued_received_data_.empty()) {
305 DataBuffer* buffer = queued_received_data_.front();
306 observer_->OnMessage(*buffer);
307 queued_received_data_.pop();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000308 delete buffer;
309 }
310}
311
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000312void DataChannel::ClearQueuedReceivedData() {
313 while (!queued_received_data_.empty()) {
314 DataBuffer* buffer = queued_received_data_.front();
315 queued_received_data_.pop();
316 delete buffer;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000317 }
318}
319
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000320void DataChannel::SendQueuedSendData() {
321 if (!was_ever_writable_) {
322 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000323 }
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000324
325 while (!queued_send_data_.empty()) {
326 DataBuffer* buffer = queued_send_data_.front();
327 cricket::SendDataResult send_result;
328 if (!InternalSendWithoutQueueing(*buffer, &send_result)) {
329 LOG(LS_WARNING) << "SendQueuedSendData aborted due to send_result "
330 << send_result;
331 break;
332 }
333 queued_send_data_.pop_front();
334 delete buffer;
335 }
336}
337
338void DataChannel::ClearQueuedSendData() {
339 while (!queued_received_data_.empty()) {
340 DataBuffer* buffer = queued_received_data_.front();
341 queued_received_data_.pop();
342 delete buffer;
343 }
344}
345
346bool DataChannel::InternalSendWithoutQueueing(
347 const DataBuffer& buffer, cricket::SendDataResult* send_result) {
348 cricket::SendDataParams send_params;
349
350 send_params.ssrc = send_ssrc_;
351 if (session_->data_channel_type() == cricket::DCT_SCTP) {
352 send_params.ordered = config_.ordered;
353 send_params.max_rtx_count = config_.maxRetransmits;
354 send_params.max_rtx_ms = config_.maxRetransmitTime;
355 }
356 send_params.type = buffer.binary ? cricket::DMT_BINARY : cricket::DMT_TEXT;
357
358 return session_->data_channel()->SendData(send_params, buffer.data,
359 send_result);
360}
361
362bool DataChannel::QueueSendData(const DataBuffer& buffer) {
363 if (queued_send_data_.size() > kMaxQueuedSendDataPackets) {
364 LOG(LS_ERROR) << "Can't buffer any more data in the data channel.";
365 return false;
366 }
367 queued_send_data_.push_back(new DataBuffer(buffer));
368 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000369}
370
371} // namespace webrtc