/src/mozilla-central/media/mtransport/nricemediastream.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
5 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | // Original author: ekr@rtfm.com |
8 | | |
9 | | // Some of this code is cut-and-pasted from nICEr. Copyright is: |
10 | | |
11 | | /* |
12 | | Copyright (c) 2007, Adobe Systems, Incorporated |
13 | | All rights reserved. |
14 | | |
15 | | Redistribution and use in source and binary forms, with or without |
16 | | modification, are permitted provided that the following conditions are |
17 | | met: |
18 | | |
19 | | * Redistributions of source code must retain the above copyright |
20 | | notice, this list of conditions and the following disclaimer. |
21 | | |
22 | | * Redistributions in binary form must reproduce the above copyright |
23 | | notice, this list of conditions and the following disclaimer in the |
24 | | documentation and/or other materials provided with the distribution. |
25 | | |
26 | | * Neither the name of Adobe Systems, Network Resonance nor the names of its |
27 | | contributors may be used to endorse or promote products derived from |
28 | | this software without specific prior written permission. |
29 | | |
30 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
31 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
32 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
33 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
34 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
35 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
36 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
37 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
38 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
39 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
40 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
41 | | */ |
42 | | |
43 | | |
44 | | #include <string> |
45 | | #include <vector> |
46 | | |
47 | | #include "logging.h" |
48 | | #include "nsError.h" |
49 | | |
50 | | // nICEr includes |
51 | | extern "C" { |
52 | | #include "nr_api.h" |
53 | | #include "registry.h" |
54 | | #include "async_timer.h" |
55 | | #include "ice_util.h" |
56 | | #include "transport_addr.h" |
57 | | #include "nr_crypto.h" |
58 | | #include "nr_socket.h" |
59 | | #include "nr_socket_local.h" |
60 | | #include "stun_client_ctx.h" |
61 | | #include "stun_server_ctx.h" |
62 | | #include "ice_ctx.h" |
63 | | #include "ice_candidate.h" |
64 | | #include "ice_handler.h" |
65 | | } |
66 | | |
67 | | // Local includes |
68 | | #include "nricectx.h" |
69 | | #include "nricemediastream.h" |
70 | | |
71 | | namespace mozilla { |
72 | | |
73 | | MOZ_MTLOG_MODULE("mtransport") |
74 | | |
75 | | static bool ToNrIceAddr(nr_transport_addr &addr, |
76 | 0 | NrIceAddr *out) { |
77 | 0 | int r; |
78 | 0 | char addrstring[INET6_ADDRSTRLEN + 1]; |
79 | 0 |
|
80 | 0 | r = nr_transport_addr_get_addrstring(&addr, addrstring, sizeof(addrstring)); |
81 | 0 | if (r) |
82 | 0 | return false; |
83 | 0 | out->host = addrstring; |
84 | 0 |
|
85 | 0 | int port; |
86 | 0 | r = nr_transport_addr_get_port(&addr, &port); |
87 | 0 | if (r) |
88 | 0 | return false; |
89 | 0 | |
90 | 0 | out->port = port; |
91 | 0 |
|
92 | 0 | switch (addr.protocol) { |
93 | 0 | case IPPROTO_TCP: |
94 | 0 | if (addr.tls_host[0] != '\0') { |
95 | 0 | out->transport = kNrIceTransportTls; |
96 | 0 | } else { |
97 | 0 | out->transport = kNrIceTransportTcp; |
98 | 0 | } |
99 | 0 | break; |
100 | 0 | case IPPROTO_UDP: |
101 | 0 | out->transport = kNrIceTransportUdp; |
102 | 0 | break; |
103 | 0 | default: |
104 | 0 | MOZ_CRASH(); |
105 | 0 | return false; |
106 | 0 | } |
107 | 0 | |
108 | 0 | return true; |
109 | 0 | } |
110 | | |
111 | | static bool ToNrIceCandidate(const nr_ice_candidate& candc, |
112 | 0 | NrIceCandidate* out) { |
113 | 0 | MOZ_ASSERT(out); |
114 | 0 | int r; |
115 | 0 | // Const-cast because the internal nICEr code isn't const-correct. |
116 | 0 | nr_ice_candidate *cand = const_cast<nr_ice_candidate *>(&candc); |
117 | 0 |
|
118 | 0 | if (!ToNrIceAddr(cand->addr, &out->cand_addr)) |
119 | 0 | return false; |
120 | 0 | |
121 | 0 | if (cand->isock) { |
122 | 0 | nr_transport_addr addr; |
123 | 0 | r = nr_socket_getaddr(cand->isock->sock, &addr); |
124 | 0 | if (r) |
125 | 0 | return false; |
126 | 0 | |
127 | 0 | if (!ToNrIceAddr(addr, &out->local_addr)) |
128 | 0 | return false; |
129 | 0 | } |
130 | 0 | |
131 | 0 | NrIceCandidate::Type type; |
132 | 0 |
|
133 | 0 | switch (cand->type) { |
134 | 0 | case HOST: |
135 | 0 | type = NrIceCandidate::ICE_HOST; |
136 | 0 | break; |
137 | 0 | case SERVER_REFLEXIVE: |
138 | 0 | type = NrIceCandidate::ICE_SERVER_REFLEXIVE; |
139 | 0 | break; |
140 | 0 | case PEER_REFLEXIVE: |
141 | 0 | type = NrIceCandidate::ICE_PEER_REFLEXIVE; |
142 | 0 | break; |
143 | 0 | case RELAYED: |
144 | 0 | type = NrIceCandidate::ICE_RELAYED; |
145 | 0 | break; |
146 | 0 | default: |
147 | 0 | return false; |
148 | 0 | } |
149 | 0 | |
150 | 0 | NrIceCandidate::TcpType tcp_type; |
151 | 0 | switch (cand->tcp_type) { |
152 | 0 | case TCP_TYPE_ACTIVE: |
153 | 0 | tcp_type = NrIceCandidate::ICE_ACTIVE; |
154 | 0 | break; |
155 | 0 | case TCP_TYPE_PASSIVE: |
156 | 0 | tcp_type = NrIceCandidate::ICE_PASSIVE; |
157 | 0 | break; |
158 | 0 | case TCP_TYPE_SO: |
159 | 0 | tcp_type = NrIceCandidate::ICE_SO; |
160 | 0 | break; |
161 | 0 | default: |
162 | 0 | tcp_type = NrIceCandidate::ICE_NONE; |
163 | 0 | break; |
164 | 0 | } |
165 | 0 | |
166 | 0 | out->type = type; |
167 | 0 | out->tcp_type = tcp_type; |
168 | 0 | out->codeword = candc.codeword; |
169 | 0 | out->label = candc.label; |
170 | 0 | out->trickled = candc.trickled; |
171 | 0 | return true; |
172 | 0 | } |
173 | | |
174 | | // Make an NrIceCandidate from the candidate |cand|. |
175 | | // This is not a member fxn because we want to hide the |
176 | | // defn of nr_ice_candidate but we pass by reference. |
177 | 0 | static UniquePtr<NrIceCandidate> MakeNrIceCandidate(const nr_ice_candidate& candc) { |
178 | 0 | UniquePtr<NrIceCandidate> out(new NrIceCandidate()); |
179 | 0 |
|
180 | 0 | if (!ToNrIceCandidate(candc, out.get())) { |
181 | 0 | return nullptr; |
182 | 0 | } |
183 | 0 | return out; |
184 | 0 | } |
185 | | |
186 | | static bool Matches(const nr_ice_media_stream* stream, |
187 | | const std::string& ufrag, |
188 | 0 | const std::string& pwd) { |
189 | 0 | return stream && (stream->ufrag == ufrag) && (stream->pwd == pwd); |
190 | 0 | } |
191 | | |
192 | | NrIceMediaStream::NrIceMediaStream(NrIceCtx *ctx, |
193 | | const std::string& id, |
194 | | const std::string& name, |
195 | | size_t components) : |
196 | | state_(ICE_CONNECTING), |
197 | | ctx_(ctx->ctx()), |
198 | | ctx_peer_(ctx->peer()), |
199 | | name_(name), |
200 | | components_(components), |
201 | | stream_(nullptr), |
202 | | old_stream_(nullptr), |
203 | | id_(id) |
204 | 0 | { |
205 | 0 | } |
206 | | |
207 | 0 | NrIceMediaStream::~NrIceMediaStream() { |
208 | 0 | // We do not need to destroy anything. All major resources |
209 | 0 | // are attached to the ice ctx. |
210 | 0 | } |
211 | | |
212 | | nsresult NrIceMediaStream::ConnectToPeer( |
213 | | const std::string& ufrag, |
214 | | const std::string& pwd, |
215 | 0 | const std::vector<std::string>& attributes) { |
216 | 0 | MOZ_ASSERT(stream_); |
217 | 0 |
|
218 | 0 | if (Matches(old_stream_, ufrag, pwd)) { |
219 | 0 | // Roll back to old stream, since we apparently aren't using the new one |
220 | 0 | // (We swap before we close so we never have stream_ == nullptr) |
221 | 0 | std::swap(stream_, old_stream_); |
222 | 0 | CloseStream(&old_stream_); |
223 | 0 | } else if (old_stream_) { |
224 | 0 | // Right now we wait for ICE to complete before closing the old stream. |
225 | 0 | // It might be worth it to close it sooner, but we don't want to close it |
226 | 0 | // right away. |
227 | 0 | nr_ice_media_stream_set_obsolete(old_stream_); |
228 | 0 | } |
229 | 0 |
|
230 | 0 | nr_ice_media_stream* peer_stream; |
231 | 0 | if (nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream)) { |
232 | 0 | // No peer yet |
233 | 0 | std::vector<char *> attributes_in; |
234 | 0 | attributes_in.reserve(attributes.size()); |
235 | 0 | for (auto& attribute : attributes) { |
236 | 0 | MOZ_MTLOG(ML_DEBUG, "Setting " << attribute << " on stream " << name_); |
237 | 0 | attributes_in.push_back(const_cast<char *>(attribute.c_str())); |
238 | 0 | } |
239 | 0 |
|
240 | 0 | // Still need to call nr_ice_ctx_parse_stream_attributes. |
241 | 0 | int r = nr_ice_peer_ctx_parse_stream_attributes(ctx_peer_, |
242 | 0 | stream_, |
243 | 0 | attributes_in.empty() ? |
244 | 0 | nullptr : &attributes_in[0], |
245 | 0 | attributes_in.size()); |
246 | 0 | if (r) { |
247 | 0 | MOZ_MTLOG(ML_ERROR, "Couldn't parse attributes for stream " |
248 | 0 | << name_ << "'"); |
249 | 0 | return NS_ERROR_FAILURE; |
250 | 0 | } |
251 | 0 | } |
252 | 0 |
|
253 | 0 | return NS_OK; |
254 | 0 | } |
255 | | |
256 | | nsresult NrIceMediaStream::SetIceCredentials(const std::string& ufrag, |
257 | 0 | const std::string& pwd) { |
258 | 0 | if (Matches(stream_, ufrag, pwd)) { |
259 | 0 | return NS_OK; |
260 | 0 | } |
261 | 0 | |
262 | 0 | MOZ_MTLOG(ML_DEBUG, "Setting ICE credentials for " << name_ << " - " |
263 | 0 | << ufrag << ":" << pwd); |
264 | 0 | CloseStream(&old_stream_); |
265 | 0 | old_stream_ = stream_; |
266 | 0 |
|
267 | 0 | std::string name(name_ + " - " + ufrag + ":" + pwd); |
268 | 0 |
|
269 | 0 | int r = nr_ice_add_media_stream(ctx_, |
270 | 0 | name.c_str(), |
271 | 0 | ufrag.c_str(), |
272 | 0 | pwd.c_str(), |
273 | 0 | components_, &stream_); |
274 | 0 | if (r) { |
275 | 0 | MOZ_MTLOG(ML_ERROR, "Couldn't create ICE media stream for '" |
276 | 0 | << name_ << "': error=" << r); |
277 | 0 | stream_ = old_stream_; |
278 | 0 | old_stream_ = nullptr; |
279 | 0 | return NS_ERROR_FAILURE; |
280 | 0 | } |
281 | 0 |
|
282 | 0 | state_ = ICE_CONNECTING; |
283 | 0 | return NS_OK; |
284 | 0 | } |
285 | | |
286 | | // Parse trickle ICE candidate |
287 | 0 | nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate) { |
288 | 0 | // TODO(bug 1490658): This needs to take ufrag into account. For now, trickle |
289 | 0 | // candidates will land on the most recently-created ICE stream. |
290 | 0 | int r; |
291 | 0 |
|
292 | 0 | MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << ctx_->label << ")/STREAM(" << |
293 | 0 | name() << ") : parsing trickle candidate " << candidate); |
294 | 0 |
|
295 | 0 | r = nr_ice_peer_ctx_parse_trickle_candidate(ctx_peer_, |
296 | 0 | stream_, |
297 | 0 | const_cast<char *>( |
298 | 0 | candidate.c_str()) |
299 | 0 | ); |
300 | 0 | if (r) { |
301 | 0 | if (r == R_ALREADY) { |
302 | 0 | MOZ_MTLOG(ML_INFO, "Trickle candidate is redundant for stream '" |
303 | 0 | << name_ << "' because it is completed: " << candidate); |
304 | 0 | } else if (r == R_REJECTED) { |
305 | 0 | MOZ_MTLOG(ML_INFO, "Trickle candidate is ignored for stream '" |
306 | 0 | << name_ << "', probably because it is for an unused component" |
307 | 0 | << ": " << candidate); |
308 | 0 | } else { |
309 | 0 | MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '" |
310 | 0 | << name_ << "': " << candidate); |
311 | 0 | return NS_ERROR_FAILURE; |
312 | 0 | } |
313 | 0 | } |
314 | 0 |
|
315 | 0 | return NS_OK; |
316 | 0 | } |
317 | | |
318 | | // Returns NS_ERROR_NOT_AVAILABLE if component is unpaired or disabled. |
319 | | nsresult NrIceMediaStream::GetActivePair(int component, |
320 | | UniquePtr<NrIceCandidate>* localp, |
321 | 0 | UniquePtr<NrIceCandidate>* remotep) { |
322 | 0 | int r; |
323 | 0 | nr_ice_candidate *local_int; |
324 | 0 | nr_ice_candidate *remote_int; |
325 | 0 |
|
326 | 0 | if (!stream_) { |
327 | 0 | return NS_ERROR_NOT_AVAILABLE; |
328 | 0 | } |
329 | 0 | |
330 | 0 | r = nr_ice_media_stream_get_active(ctx_peer_, |
331 | 0 | stream_, |
332 | 0 | component, |
333 | 0 | &local_int, &remote_int); |
334 | 0 | // If result is R_REJECTED then component is unpaired or disabled. |
335 | 0 | if (r == R_REJECTED) |
336 | 0 | return NS_ERROR_NOT_AVAILABLE; |
337 | 0 | |
338 | 0 | if (r) |
339 | 0 | return NS_ERROR_FAILURE; |
340 | 0 | |
341 | 0 | UniquePtr<NrIceCandidate> local( |
342 | 0 | MakeNrIceCandidate(*local_int)); |
343 | 0 | if (!local) |
344 | 0 | return NS_ERROR_FAILURE; |
345 | 0 | |
346 | 0 | UniquePtr<NrIceCandidate> remote( |
347 | 0 | MakeNrIceCandidate(*remote_int)); |
348 | 0 | if (!remote) |
349 | 0 | return NS_ERROR_FAILURE; |
350 | 0 | |
351 | 0 | if (localp) |
352 | 0 | *localp = std::move(local); |
353 | 0 | if (remotep) |
354 | 0 | *remotep = std::move(remote); |
355 | 0 |
|
356 | 0 | return NS_OK; |
357 | 0 | } |
358 | | |
359 | | |
360 | | nsresult NrIceMediaStream::GetCandidatePairs(std::vector<NrIceCandidatePair>* |
361 | 0 | out_pairs) const { |
362 | 0 | MOZ_ASSERT(out_pairs); |
363 | 0 | if (!stream_) { |
364 | 0 | return NS_ERROR_NOT_AVAILABLE; |
365 | 0 | } |
366 | 0 | |
367 | 0 | // If we haven't at least started checking then there is nothing to report |
368 | 0 | if (ctx_peer_->state != NR_ICE_PEER_STATE_PAIRED) { |
369 | 0 | return NS_OK; |
370 | 0 | } |
371 | 0 | |
372 | 0 | // Get the check_list on the peer stream (this is where the check_list |
373 | 0 | // actually lives, not in stream_) |
374 | 0 | nr_ice_media_stream* peer_stream; |
375 | 0 | int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream); |
376 | 0 | if (r != 0) { |
377 | 0 | return NS_ERROR_FAILURE; |
378 | 0 | } |
379 | 0 | |
380 | 0 | nr_ice_cand_pair *p1, *p2; |
381 | 0 | out_pairs->clear(); |
382 | 0 |
|
383 | 0 | TAILQ_FOREACH(p1, &peer_stream->check_list, check_queue_entry) { |
384 | 0 | MOZ_ASSERT(p1); |
385 | 0 | MOZ_ASSERT(p1->local); |
386 | 0 | MOZ_ASSERT(p1->remote); |
387 | 0 | NrIceCandidatePair pair; |
388 | 0 |
|
389 | 0 | p2 = TAILQ_FIRST(&peer_stream->check_list); |
390 | 0 | while (p2) { |
391 | 0 | if (p1 == p2) { |
392 | 0 | /* Don't compare with our self. */ |
393 | 0 | p2=TAILQ_NEXT(p2, check_queue_entry); |
394 | 0 | continue; |
395 | 0 | } |
396 | 0 | if (strncmp(p1->codeword,p2->codeword,sizeof(p1->codeword))==0) { |
397 | 0 | /* In case of duplicate pairs we only report the one winning pair */ |
398 | 0 | if ( |
399 | 0 | ((p2->remote->component && (p2->remote->component->active == p2)) && |
400 | 0 | !(p1->remote->component && (p1->remote->component->active == p1))) || |
401 | 0 | ((p2->peer_nominated || p2->nominated) && |
402 | 0 | !(p1->peer_nominated || p1->nominated)) || |
403 | 0 | (p2->priority > p1->priority) || |
404 | 0 | ((p2->state == NR_ICE_PAIR_STATE_SUCCEEDED) && |
405 | 0 | (p1->state != NR_ICE_PAIR_STATE_SUCCEEDED)) || |
406 | 0 | ((p2->state != NR_ICE_PAIR_STATE_CANCELLED) && |
407 | 0 | (p1->state == NR_ICE_PAIR_STATE_CANCELLED)) |
408 | 0 | ) { |
409 | 0 | /* p2 is a better pair. */ |
410 | 0 | break; |
411 | 0 | } |
412 | 0 | } |
413 | 0 | p2=TAILQ_NEXT(p2, check_queue_entry); |
414 | 0 | } |
415 | 0 | if (p2) { |
416 | 0 | /* p2 points to a duplicate but better pair so skip this one */ |
417 | 0 | continue; |
418 | 0 | } |
419 | 0 | |
420 | 0 | switch (p1->state) { |
421 | 0 | case NR_ICE_PAIR_STATE_FROZEN: |
422 | 0 | pair.state = NrIceCandidatePair::State::STATE_FROZEN; |
423 | 0 | break; |
424 | 0 | case NR_ICE_PAIR_STATE_WAITING: |
425 | 0 | pair.state = NrIceCandidatePair::State::STATE_WAITING; |
426 | 0 | break; |
427 | 0 | case NR_ICE_PAIR_STATE_IN_PROGRESS: |
428 | 0 | pair.state = NrIceCandidatePair::State::STATE_IN_PROGRESS; |
429 | 0 | break; |
430 | 0 | case NR_ICE_PAIR_STATE_FAILED: |
431 | 0 | pair.state = NrIceCandidatePair::State::STATE_FAILED; |
432 | 0 | break; |
433 | 0 | case NR_ICE_PAIR_STATE_SUCCEEDED: |
434 | 0 | pair.state = NrIceCandidatePair::State::STATE_SUCCEEDED; |
435 | 0 | break; |
436 | 0 | case NR_ICE_PAIR_STATE_CANCELLED: |
437 | 0 | pair.state = NrIceCandidatePair::State::STATE_CANCELLED; |
438 | 0 | break; |
439 | 0 | default: |
440 | 0 | MOZ_ASSERT(0); |
441 | 0 | } |
442 | 0 |
|
443 | 0 | pair.priority = p1->priority; |
444 | 0 | pair.nominated = p1->peer_nominated || p1->nominated; |
445 | 0 | pair.component_id = p1->remote->component->component_id; |
446 | 0 |
|
447 | 0 | // As discussed with drno: a component's can_send field (set to true |
448 | 0 | // by ICE consent) is a very close approximation for writable and |
449 | 0 | // readable. Note: the component for the local candidate never has |
450 | 0 | // the can_send member set to true, remote for both readable and |
451 | 0 | // writable. (mjf) |
452 | 0 | pair.writable = p1->remote->component->can_send; |
453 | 0 | pair.readable = p1->remote->component->can_send; |
454 | 0 | pair.selected = p1->remote->component && |
455 | 0 | p1->remote->component->active == p1; |
456 | 0 | pair.codeword = p1->codeword; |
457 | 0 | pair.bytes_sent = p1->bytes_sent; |
458 | 0 | pair.bytes_recvd = p1->bytes_recvd; |
459 | 0 | pair.ms_since_last_send = p1->last_sent.tv_sec*1000 |
460 | 0 | + p1->last_sent.tv_usec/1000; |
461 | 0 | pair.ms_since_last_recv = p1->last_recvd.tv_sec*1000 |
462 | 0 | + p1->last_recvd.tv_usec/1000; |
463 | 0 |
|
464 | 0 | if (!ToNrIceCandidate(*(p1->local), &pair.local) || |
465 | 0 | !ToNrIceCandidate(*(p1->remote), &pair.remote)) { |
466 | 0 | return NS_ERROR_FAILURE; |
467 | 0 | } |
468 | 0 | |
469 | 0 | out_pairs->push_back(pair); |
470 | 0 | } |
471 | 0 |
|
472 | 0 | return NS_OK; |
473 | 0 | } |
474 | | |
475 | | nsresult NrIceMediaStream::GetDefaultCandidate( |
476 | | int component, |
477 | 0 | NrIceCandidate* candidate) const { |
478 | 0 |
|
479 | 0 | nr_ice_candidate *cand; |
480 | 0 |
|
481 | 0 | int r = nr_ice_media_stream_get_default_candidate(stream_, component, &cand); |
482 | 0 | if (r) { |
483 | 0 | if (r == R_NOT_FOUND) { |
484 | 0 | MOZ_MTLOG(ML_INFO, "Couldn't get default ICE candidate for '" |
485 | 0 | << name_ << "', no candidates."); |
486 | 0 | } else { |
487 | 0 | MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '" |
488 | 0 | << name_ << "', " << r); |
489 | 0 | } |
490 | 0 | return NS_ERROR_FAILURE; |
491 | 0 | } |
492 | 0 |
|
493 | 0 | if (!ToNrIceCandidate(*cand, candidate)) { |
494 | 0 | MOZ_MTLOG(ML_ERROR, "Failed to convert default ICE candidate for '" |
495 | 0 | << name_ << "'"); |
496 | 0 | return NS_ERROR_FAILURE; |
497 | 0 | } |
498 | 0 |
|
499 | 0 | return NS_OK; |
500 | 0 | } |
501 | | |
502 | 0 | std::vector<std::string> NrIceMediaStream::GetAttributes() const { |
503 | 0 | char **attrs = nullptr; |
504 | 0 | int attrct; |
505 | 0 | int r; |
506 | 0 | std::vector<std::string> ret; |
507 | 0 |
|
508 | 0 | if (!stream_) { |
509 | 0 | return ret; |
510 | 0 | } |
511 | 0 | |
512 | 0 | r = nr_ice_media_stream_get_attributes(stream_, |
513 | 0 | &attrs, &attrct); |
514 | 0 | if (r) { |
515 | 0 | MOZ_MTLOG(ML_ERROR, "Couldn't get ICE candidates for '" |
516 | 0 | << name_ << "'"); |
517 | 0 | return ret; |
518 | 0 | } |
519 | 0 |
|
520 | 0 | for (int i=0; i<attrct; i++) { |
521 | 0 | ret.push_back(attrs[i]); |
522 | 0 | RFREE(attrs[i]); |
523 | 0 | } |
524 | 0 |
|
525 | 0 | RFREE(attrs); |
526 | 0 |
|
527 | 0 | return ret; |
528 | 0 | } |
529 | | |
530 | | static nsresult GetCandidatesFromStream( |
531 | | nr_ice_media_stream *stream, |
532 | 0 | std::vector<NrIceCandidate> *candidates) { |
533 | 0 | MOZ_ASSERT(candidates); |
534 | 0 | nr_ice_component* comp=STAILQ_FIRST(&stream->components); |
535 | 0 | while(comp){ |
536 | 0 | if (comp->state != NR_ICE_COMPONENT_DISABLED) { |
537 | 0 | nr_ice_candidate *cand = TAILQ_FIRST(&comp->candidates); |
538 | 0 | while(cand){ |
539 | 0 | NrIceCandidate new_cand; |
540 | 0 | // This can fail if the candidate is server reflexive or relayed, and |
541 | 0 | // has not yet received a response (ie; it doesn't know its address |
542 | 0 | // yet). For the purposes of this code, this isn't a candidate we're |
543 | 0 | // interested in, since it is not fully baked yet. |
544 | 0 | if (ToNrIceCandidate(*cand, &new_cand)) { |
545 | 0 | candidates->push_back(new_cand); |
546 | 0 | } |
547 | 0 | cand=TAILQ_NEXT(cand,entry_comp); |
548 | 0 | } |
549 | 0 | } |
550 | 0 | comp=STAILQ_NEXT(comp,entry); |
551 | 0 | } |
552 | 0 |
|
553 | 0 | return NS_OK; |
554 | 0 | } |
555 | | |
556 | | nsresult NrIceMediaStream::GetLocalCandidates( |
557 | 0 | std::vector<NrIceCandidate>* candidates) const { |
558 | 0 | if (!stream_) { |
559 | 0 | return NS_ERROR_NOT_AVAILABLE; |
560 | 0 | } |
561 | 0 | |
562 | 0 | return GetCandidatesFromStream(stream_, candidates); |
563 | 0 | } |
564 | | |
565 | | nsresult NrIceMediaStream::GetRemoteCandidates( |
566 | 0 | std::vector<NrIceCandidate>* candidates) const { |
567 | 0 | if (!stream_) { |
568 | 0 | return NS_ERROR_NOT_AVAILABLE; |
569 | 0 | } |
570 | 0 | |
571 | 0 | // If we haven't at least started checking then there is nothing to report |
572 | 0 | if (ctx_peer_->state != NR_ICE_PEER_STATE_PAIRED) { |
573 | 0 | return NS_OK; |
574 | 0 | } |
575 | 0 | |
576 | 0 | nr_ice_media_stream* peer_stream; |
577 | 0 | int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream); |
578 | 0 | if (r != 0) { |
579 | 0 | return NS_ERROR_FAILURE; |
580 | 0 | } |
581 | 0 | |
582 | 0 | return GetCandidatesFromStream(peer_stream, candidates); |
583 | 0 | } |
584 | | |
585 | | |
586 | 0 | nsresult NrIceMediaStream::DisableComponent(int component_id) { |
587 | 0 | if (!stream_) |
588 | 0 | return NS_ERROR_FAILURE; |
589 | 0 | |
590 | 0 | int r = nr_ice_media_stream_disable_component(stream_, |
591 | 0 | component_id); |
592 | 0 | if (r) { |
593 | 0 | MOZ_MTLOG(ML_ERROR, "Couldn't disable '" << name_ << "':" << |
594 | 0 | component_id); |
595 | 0 | return NS_ERROR_FAILURE; |
596 | 0 | } |
597 | 0 |
|
598 | 0 | return NS_OK; |
599 | 0 | } |
600 | | |
601 | 0 | nsresult NrIceMediaStream::GetConsentStatus(int component_id, bool *can_send, struct timeval *ts) { |
602 | 0 | if (!stream_) |
603 | 0 | return NS_ERROR_FAILURE; |
604 | 0 | |
605 | 0 | nr_ice_media_stream* peer_stream; |
606 | 0 | int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream); |
607 | 0 | if (r) { |
608 | 0 | MOZ_MTLOG(ML_ERROR, "Failed to find peer stream for '" << name_ << "':" << |
609 | 0 | component_id); |
610 | 0 | return NS_ERROR_FAILURE; |
611 | 0 | } |
612 | 0 |
|
613 | 0 | int send = 0; |
614 | 0 | r = nr_ice_media_stream_get_consent_status(peer_stream, component_id, |
615 | 0 | &send, ts); |
616 | 0 | if (r) { |
617 | 0 | MOZ_MTLOG(ML_ERROR, "Failed to get consent status for '" << name_ << "':" << |
618 | 0 | component_id); |
619 | 0 | return NS_ERROR_FAILURE; |
620 | 0 | } |
621 | 0 | *can_send = !!send; |
622 | 0 |
|
623 | 0 | return NS_OK; |
624 | 0 | } |
625 | | |
626 | 0 | bool NrIceMediaStream::HasStream(nr_ice_media_stream *stream) const { |
627 | 0 | return (stream == stream_) || (stream == old_stream_); |
628 | 0 | } |
629 | | |
630 | | nsresult NrIceMediaStream::SendPacket(int component_id, |
631 | | const unsigned char *data, |
632 | 0 | size_t len) { |
633 | 0 | nr_ice_media_stream* stream = old_stream_ ? old_stream_ : stream_; |
634 | 0 | if (!stream) { |
635 | 0 | return NS_ERROR_FAILURE; |
636 | 0 | } |
637 | 0 | |
638 | 0 | int r = nr_ice_media_stream_send(ctx_peer_, stream, |
639 | 0 | component_id, |
640 | 0 | const_cast<unsigned char *>(data), len); |
641 | 0 | if (r) { |
642 | 0 | MOZ_MTLOG(ML_ERROR, "Couldn't send media on '" << name_ << "'"); |
643 | 0 | if (r == R_WOULDBLOCK) { |
644 | 0 | return NS_BASE_STREAM_WOULD_BLOCK; |
645 | 0 | } |
646 | 0 | |
647 | 0 | return NS_BASE_STREAM_OSERROR; |
648 | 0 | } |
649 | 0 | |
650 | 0 | return NS_OK; |
651 | 0 | } |
652 | | |
653 | | |
654 | 0 | void NrIceMediaStream::Ready() { |
655 | 0 | // This function is called whenever a stream becomes ready, but it |
656 | 0 | // gets fired multiple times when a stream gets nominated repeatedly. |
657 | 0 | if (state_ != ICE_OPEN) { |
658 | 0 | MOZ_MTLOG(ML_DEBUG, "Marking stream ready '" << name_ << "'"); |
659 | 0 | state_ = ICE_OPEN; |
660 | 0 | CloseStream(&old_stream_); |
661 | 0 | SignalReady(this); |
662 | 0 | } |
663 | 0 | else { |
664 | 0 | MOZ_MTLOG(ML_DEBUG, "Stream ready callback fired again for '" << name_ << "'"); |
665 | 0 | } |
666 | 0 | } |
667 | | |
668 | 0 | void NrIceMediaStream::Failed() { |
669 | 0 | if (state_ != ICE_CLOSED) { |
670 | 0 | MOZ_MTLOG(ML_DEBUG, "Marking stream failed '" << name_ << "'"); |
671 | 0 | state_ = ICE_CLOSED; |
672 | 0 | // We don't need the old stream anymore. |
673 | 0 | CloseStream(&old_stream_); |
674 | 0 | SignalFailed(this); |
675 | 0 | } |
676 | 0 | } |
677 | | |
678 | 0 | void NrIceMediaStream::Close() { |
679 | 0 | MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'"); |
680 | 0 | state_ = ICE_CLOSED; |
681 | 0 |
|
682 | 0 | CloseStream(&old_stream_); |
683 | 0 | CloseStream(&stream_); |
684 | 0 | } |
685 | | |
686 | | void |
687 | | NrIceMediaStream::CloseStream(nr_ice_media_stream **stream) |
688 | 0 | { |
689 | 0 | if (*stream) { |
690 | 0 | int r = nr_ice_remove_media_stream(ctx_, stream); |
691 | 0 | if (r) { |
692 | 0 | MOZ_ASSERT(false, "Failed to remove stream"); |
693 | 0 | MOZ_MTLOG(ML_ERROR, "Failed to remove stream, error=" << r); |
694 | 0 | } |
695 | 0 | *stream = nullptr; |
696 | 0 | } |
697 | 0 | } |
698 | | |
699 | | } // close namespace |