1 /*
2 * libjingle
3 * Copyright 2011, 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
28 #include "talk/app/webrtc/roapsignaling.h"
29
30 #include <utility>
31
32 #include "talk/app/webrtc/jsepsessiondescription.h"
33 #include "talk/app/webrtc/mediastreamsignaling.h"
34 #include "talk/app/webrtc/streamcollectionimpl.h"
35 #include "talk/base/helpers.h"
36 #include "talk/base/logging.h"
37 #include "talk/base/messagequeue.h"
38 #include "talk/session/phone/channelmanager.h"
39
40 using talk_base::scoped_refptr;
41
42 namespace webrtc {
43
44 enum {
45 MSG_SEND_QUEUED_OFFER = 1,
46 MSG_GENERATE_ANSWER = 2,
47 };
48
RoapSignaling(MediaStreamSignaling * mediastream_signaling,JsepInterface * provider)49 RoapSignaling::RoapSignaling(
50 MediaStreamSignaling* mediastream_signaling,
51 JsepInterface* provider)
52 : stream_signaling_(mediastream_signaling),
53 provider_(provider),
54 state_(kNew),
55 received_pre_offer_(false),
56 local_streams_(StreamCollection::Create()) {
57 }
58
~RoapSignaling()59 RoapSignaling::~RoapSignaling() {}
60
OnIceComplete()61 void RoapSignaling::OnIceComplete() {
62 if (!VERIFY(state_ == kInitializing))
63 return;
64 // If we have a queued remote offer we need to handle this first.
65 if (received_pre_offer_) {
66 received_pre_offer_ = false;
67 SendAnswer();
68 } else if (!queued_local_streams_.empty()) {
69 // Else CreateOffer have been called.
70 SendOffer(provider_->local_description());
71 } else {
72 ChangeState(kIdle);
73 }
74 }
75
OnIceCandidate(const IceCandidateInterface *)76 void RoapSignaling::OnIceCandidate(
77 const IceCandidateInterface* /*candidate*/) {
78 // Ignore all candidates. We only care about when all
79 // candidates have been collected.
80 }
81
ChangeState(State new_state)82 void RoapSignaling::ChangeState(State new_state) {
83 state_ = new_state;
84 SignalStateChange(state_);
85 }
86
ProcessSignalingMessage(const std::string & message,StreamCollectionInterface * local_streams)87 void RoapSignaling::ProcessSignalingMessage(
88 const std::string& message,
89 StreamCollectionInterface* local_streams) {
90 RoapSession::ParseResult result = roap_session_.Parse(message);
91
92 // Signal an error message and return if a message is received after shutdown
93 // or it is not an ok message that is received during shutdown.
94 // No other messages from the remote peer can be processed in these states.
95 if (state_ == kShutdownComplete ||
96 (state_ == kShutingDown && result != RoapSession::kOk)) {
97 SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(kNoMatch));
98 return;
99 }
100
101 switch (result) {
102 case RoapSession::kOffer: {
103 if (state_ == kWaitingForAnswer) {
104 // Message received out of order or Glare occurred and the decision was
105 // to use the incoming offer.
106 LOG(LS_INFO) << "Received offer while waiting for answer.";
107 }
108
109 // Provide the remote session description and the remote candidates from
110 // the parsed ROAP message to the |provider_|.
111 if (!ProcessRemoteDescription(roap_session_.RemoteDescription(),
112 JsepInterface::kOffer)) {
113 break;
114 }
115
116 InitializeSendingAnswer(local_streams);
117 break;
118 }
119 case RoapSession::kAnswerMoreComing: {
120 // We ignore this message for now and wait for the complete result.
121 LOG(LS_INFO) << "Received answer more coming.";
122 break;
123 }
124 case RoapSession::kAnswer: {
125 if (state_ != kWaitingForAnswer) {
126 LOG(LS_WARNING) << "Received an unexpected answer.";
127 return;
128 }
129
130 // Pop the first item of queued StreamCollections containing local
131 // MediaStreams that just have been negotiated.
132 scoped_refptr<StreamCollectionInterface> streams(
133 queued_local_streams_.front());
134 queued_local_streams_.pop_front();
135
136 // If this is an answer to an initial offer SetLocalDescription have
137 // already been called.
138 if (local_desc_.get()) {
139 // Hand the ownership of the local session description to |provider_|.
140 provider_->SetLocalDescription(JsepInterface::kOffer,
141 local_desc_.release());
142 }
143
144 // Provide the remote session description and the remote candidates from
145 // the parsed ROAP message to the |provider_|.
146 if (!ProcessRemoteDescription(roap_session_.RemoteDescription(),
147 JsepInterface::kAnswer)) {
148 break;
149 }
150
151 // Let the remote peer know we have received the answer.
152 SignalNewPeerConnectionMessage(roap_session_.CreateOk());
153 // Check if we have more offers waiting in the queue.
154 if (!queued_local_streams_.empty()) {
155 // Send the next offer.
156 InitializeSendingOffer();
157 } else {
158 ChangeState(kIdle);
159 }
160 break;
161 }
162 case RoapSession::kOk: {
163 if (state_ == kWaitingForOK) {
164 ChangeState(kIdle);
165 // Check if we have an updated offer waiting in the queue.
166 if (!queued_local_streams_.empty())
167 InitializeSendingOffer();
168 } else if (state_ == kShutingDown) {
169 ChangeState(kShutdownComplete);
170 }
171 break;
172 }
173 case RoapSession::kParseConflict: {
174 SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(
175 kConflict));
176 break;
177 }
178 case RoapSession::kParseDoubleConflict: {
179 SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(
180 kDoubleConflict));
181
182 // Recreate the offer with new sequence values etc.
183 InitializeSendingOffer();
184 break;
185 }
186 case RoapSession::kError: {
187 if (roap_session_.RemoteError() != kConflict &&
188 roap_session_.RemoteError() != kDoubleConflict) {
189 SignalErrorMessageReceived(roap_session_.RemoteError());
190 // An error have occurred that we can't do anything about.
191 // Reset the state and wait for user action.
192 queued_local_streams_.clear();
193 ChangeState(kIdle);
194 }
195 break;
196 }
197 case RoapSession::kShutDown: {
198 DoShutDown();
199 ChangeState(kShutdownComplete);
200 SignalNewPeerConnectionMessage(roap_session_.CreateOk());
201 break;
202 }
203 case RoapSession::kInvalidMessage: {
204 SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(
205 kNoMatch));
206 return;
207 }
208 }
209 }
210
CreateOffer(StreamCollectionInterface * local_streams)211 void RoapSignaling::CreateOffer(StreamCollectionInterface* local_streams) {
212 if (!VERIFY(state_ != kShutingDown && state_ != kShutdownComplete)) {
213 return;
214 }
215
216 queued_local_streams_.push_back(local_streams);
217 if (state_ == kNew || state_ == kIdle) {
218 InitializeSendingOffer();
219 }
220 return;
221 }
222
SendShutDown()223 void RoapSignaling::SendShutDown() {
224 DoShutDown();
225 SignalNewPeerConnectionMessage(roap_session_.CreateShutDown());
226 }
227
InitializeSendingOffer()228 void RoapSignaling::InitializeSendingOffer() {
229 ASSERT(!queued_local_streams_.empty());
230
231 scoped_refptr<StreamCollectionInterface> local_streams(
232 queued_local_streams_.front());
233
234 stream_signaling_->SetLocalStreams(local_streams);
235 local_desc_.reset(provider_->CreateOffer(MediaHints()));
236
237 // If we are still in state kNew, we need to start the ice negotiation and
238 // wait until we have our local candidates before we can send the offer.
239 // The offer is sent in OnIceComplete.
240 if (state_ == kNew) {
241 ChangeState(kInitializing);
242 provider_->SetLocalDescription(JsepInterface::kOffer,
243 local_desc_.release());
244 // Start Ice and set the local session description for the first time.
245 provider_->StartIce(JsepInterface::kUseAll);
246 return;
247 }
248 SendOffer(local_desc_.get());
249 }
250
SendOffer(const SessionDescriptionInterface * local_desc)251 void RoapSignaling::SendOffer(const SessionDescriptionInterface* local_desc) {
252 ChangeState(kWaitingForAnswer);
253 std::string sdp;
254 local_desc->ToString(&sdp);
255 SignalNewPeerConnectionMessage(roap_session_.CreateOffer(sdp));
256 }
257
InitializeSendingAnswer(StreamCollectionInterface * local_streams)258 void RoapSignaling::InitializeSendingAnswer(
259 StreamCollectionInterface* local_streams) {
260 if (state_ == kInitializing) {
261 LOG(LS_WARNING) << "Unexpected offer received";
262 SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(kRefused));
263 return;
264 }
265
266 // If we are still in state kNew, we need to start the ice negotiation and
267 // wait until we have our local candidates before we can send the answer.
268 // We need to change the state here since
269 // |provider_->SetLocalDescription| Sends tasks to other threads and we
270 // therefore run the risk of receiving tasks from other threads while doing so.
271 bool start_ice = false;
272 if (state_ == kNew) {
273 start_ice = true;
274 ChangeState(kInitializing);
275 }
276 // Clean up all queued collections of local streams.
277 queued_local_streams_.clear();
278
279 stream_signaling_->SetLocalStreams(local_streams);
280 // Create an local session description based on |local_streams|.
281 SessionDescriptionInterface* local_desc(provider_->CreateAnswer(
282 MediaHints(), provider_->remote_description()));
283 if (!local_desc || !provider_->SetLocalDescription(JsepInterface::kAnswer,
284 local_desc)) {
285 LOG(LS_WARNING) << "Answer to Roap offer failed";
286 SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(kRefused));
287 return;
288 }
289
290 if (start_ice) {
291 if (!provider_->StartIce(JsepInterface::kUseAll)) {
292 SignalNewPeerConnectionMessage(
293 roap_session_.CreateErrorMessage(kRefused));
294 return;
295 }
296 received_pre_offer_ = true;
297 // The answer is sent in OnIceComplete.
298 return;
299 }
300 SendAnswer();
301 }
302
SendAnswer()303 void RoapSignaling::SendAnswer() {
304 std::string sdp;
305 provider_->local_description()->ToString(&sdp);
306 received_pre_offer_ = false;
307 ChangeState(kWaitingForOK);
308 SignalNewPeerConnectionMessage(roap_session_.CreateAnswer(sdp));
309 }
310
DoShutDown()311 void RoapSignaling::DoShutDown() {
312 ChangeState(kShutingDown);
313 queued_local_streams_.clear();
314
315 stream_signaling_->SetLocalStreams(NULL);
316 // Create new empty session descriptions without StreamParams.
317 // By applying these descriptions we don't send or receive any streams.
318 SessionDescriptionInterface* local_desc =
319 provider_->CreateOffer(MediaHints());
320 SessionDescriptionInterface* remote_desc =
321 provider_->CreateAnswer(MediaHints(), local_desc);
322
323 provider_->SetRemoteDescription(JsepInterface::kOffer, remote_desc);
324 provider_->SetLocalDescription(JsepInterface::kAnswer, local_desc);
325 }
326
ProcessRemoteDescription(const std::string & sdp,JsepInterface::Action action)327 bool RoapSignaling::ProcessRemoteDescription(const std::string& sdp,
328 JsepInterface::Action action) {
329 // Provide the remote session description and the remote candidates from
330 // the parsed ROAP message to the |provider_|.
331 SessionDescriptionInterface* desc = CreateSessionDescription(sdp);
332 bool ret = provider_->SetRemoteDescription(action, desc);
333 if (!ret)
334 SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(kRefused));
335 return ret;
336 }
337
338 } // namespace webrtc
339