Line data Source code
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 0 : MOZ_MTLOG_MODULE("mtransport")
74 :
75 0 : static bool ToNrIceAddr(nr_transport_addr &addr,
76 : NrIceAddr *out) {
77 : int r;
78 : char addrstring[INET6_ADDRSTRLEN + 1];
79 :
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 :
85 : int port;
86 0 : r = nr_transport_addr_get_port(&addr, &port);
87 0 : if (r)
88 0 : return false;
89 :
90 0 : out->port = port;
91 :
92 0 : switch (addr.protocol) {
93 : case IPPROTO_TCP:
94 0 : if (addr.tls_host[0] != '\0') {
95 0 : out->transport = kNrIceTransportTls;
96 : } else {
97 0 : out->transport = kNrIceTransportTcp;
98 : }
99 0 : break;
100 : case IPPROTO_UDP:
101 0 : out->transport = kNrIceTransportUdp;
102 0 : break;
103 : default:
104 0 : MOZ_CRASH();
105 : return false;
106 : }
107 :
108 0 : return true;
109 : }
110 :
111 0 : static bool ToNrIceCandidate(const nr_ice_candidate& candc,
112 : NrIceCandidate* out) {
113 0 : MOZ_ASSERT(out);
114 : int r;
115 : // Const-cast because the internal nICEr code isn't const-correct.
116 0 : nr_ice_candidate *cand = const_cast<nr_ice_candidate *>(&candc);
117 :
118 0 : if (!ToNrIceAddr(cand->addr, &out->cand_addr))
119 0 : return false;
120 :
121 0 : if (cand->isock) {
122 : nr_transport_addr addr;
123 0 : r = nr_socket_getaddr(cand->isock->sock, &addr);
124 0 : if (r)
125 0 : return false;
126 :
127 0 : if (!ToNrIceAddr(addr, &out->local_addr))
128 0 : return false;
129 : }
130 :
131 : NrIceCandidate::Type type;
132 :
133 0 : switch (cand->type) {
134 : case HOST:
135 0 : type = NrIceCandidate::ICE_HOST;
136 0 : break;
137 : case SERVER_REFLEXIVE:
138 0 : type = NrIceCandidate::ICE_SERVER_REFLEXIVE;
139 0 : break;
140 : case PEER_REFLEXIVE:
141 0 : type = NrIceCandidate::ICE_PEER_REFLEXIVE;
142 0 : break;
143 : case RELAYED:
144 0 : type = NrIceCandidate::ICE_RELAYED;
145 0 : break;
146 : default:
147 0 : return false;
148 : }
149 :
150 : NrIceCandidate::TcpType tcp_type;
151 0 : switch (cand->tcp_type) {
152 : case TCP_TYPE_ACTIVE:
153 0 : tcp_type = NrIceCandidate::ICE_ACTIVE;
154 0 : break;
155 : case TCP_TYPE_PASSIVE:
156 0 : tcp_type = NrIceCandidate::ICE_PASSIVE;
157 0 : break;
158 : case TCP_TYPE_SO:
159 0 : tcp_type = NrIceCandidate::ICE_SO;
160 0 : break;
161 : default:
162 0 : tcp_type = NrIceCandidate::ICE_NONE;
163 0 : break;
164 : }
165 :
166 0 : out->type = type;
167 0 : out->tcp_type = tcp_type;
168 0 : out->codeword = candc.codeword;
169 0 : return true;
170 : }
171 :
172 : // Make an NrIceCandidate from the candidate |cand|.
173 : // This is not a member fxn because we want to hide the
174 : // defn of nr_ice_candidate but we pass by reference.
175 0 : static UniquePtr<NrIceCandidate> MakeNrIceCandidate(const nr_ice_candidate& candc) {
176 0 : UniquePtr<NrIceCandidate> out(new NrIceCandidate());
177 :
178 0 : if (!ToNrIceCandidate(candc, out.get())) {
179 0 : return nullptr;
180 : }
181 0 : return out;
182 : }
183 :
184 : // NrIceMediaStream
185 : RefPtr<NrIceMediaStream>
186 0 : NrIceMediaStream::Create(NrIceCtx *ctx,
187 : const std::string& name,
188 : int components) {
189 : RefPtr<NrIceMediaStream> stream =
190 0 : new NrIceMediaStream(ctx, name, components);
191 0 : MOZ_ASSERT(stream->ctx_ == ctx->ctx());
192 :
193 0 : int r = nr_ice_add_media_stream(ctx->ctx(),
194 0 : const_cast<char *>(name.c_str()),
195 0 : components, &stream->stream_);
196 0 : if (r) {
197 0 : MOZ_MTLOG(ML_ERROR, "Couldn't create ICE media stream for '"
198 : << name << "'");
199 0 : return nullptr;
200 : }
201 :
202 0 : return stream;
203 : }
204 :
205 0 : NrIceMediaStream::NrIceMediaStream(NrIceCtx *ctx,
206 : const std::string& name,
207 0 : size_t components) :
208 : state_(ICE_CONNECTING),
209 0 : ctx_(ctx->ctx()),
210 0 : ctx_peer_(ctx->peer()),
211 : name_(name),
212 : components_(components),
213 : stream_(nullptr),
214 : level_(0),
215 0 : has_parsed_attrs_(false)
216 : {
217 0 : }
218 :
219 0 : NrIceMediaStream::~NrIceMediaStream() {
220 : // We do not need to destroy anything. All major resources
221 : // are attached to the ice ctx.
222 0 : }
223 :
224 0 : nsresult NrIceMediaStream::ParseAttributes(std::vector<std::string>&
225 : attributes) {
226 0 : if (!stream_)
227 0 : return NS_ERROR_FAILURE;
228 :
229 0 : std::vector<char *> attributes_in;
230 :
231 0 : for (auto& attribute : attributes) {
232 0 : attributes_in.push_back(const_cast<char *>(attribute.c_str()));
233 : }
234 :
235 : // Still need to call nr_ice_ctx_parse_stream_attributes.
236 0 : int r = nr_ice_peer_ctx_parse_stream_attributes(ctx_peer_,
237 : stream_,
238 0 : attributes_in.size() ?
239 : &attributes_in[0] : nullptr,
240 0 : attributes_in.size());
241 0 : if (r) {
242 0 : MOZ_MTLOG(ML_ERROR, "Couldn't parse attributes for stream "
243 : << name_ << "'");
244 0 : return NS_ERROR_FAILURE;
245 : }
246 :
247 0 : has_parsed_attrs_ = true;
248 0 : return NS_OK;
249 : }
250 :
251 : // Parse trickle ICE candidate
252 0 : nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate) {
253 : int r;
254 :
255 0 : MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << ctx_->label << ")/STREAM(" <<
256 : name() << ") : parsing trickle candidate " << candidate);
257 :
258 0 : r = nr_ice_peer_ctx_parse_trickle_candidate(ctx_peer_,
259 : stream_,
260 : const_cast<char *>(
261 0 : candidate.c_str())
262 0 : );
263 0 : if (r) {
264 0 : if (r == R_ALREADY) {
265 0 : MOZ_MTLOG(ML_ERROR, "Trickle candidates are redundant for stream '"
266 : << name_ << "' because it is completed");
267 :
268 : } else {
269 0 : MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '"
270 : << name_ << "'");
271 0 : return NS_ERROR_FAILURE;
272 : }
273 : }
274 :
275 0 : return NS_OK;
276 : }
277 :
278 : // Returns NS_ERROR_NOT_AVAILABLE if component is unpaired or disabled.
279 0 : nsresult NrIceMediaStream::GetActivePair(int component,
280 : UniquePtr<NrIceCandidate>* localp,
281 : UniquePtr<NrIceCandidate>* remotep) {
282 : int r;
283 : nr_ice_candidate *local_int;
284 : nr_ice_candidate *remote_int;
285 :
286 0 : if (!stream_) {
287 0 : return NS_ERROR_NOT_AVAILABLE;
288 : }
289 :
290 0 : r = nr_ice_media_stream_get_active(ctx_peer_,
291 : stream_,
292 : component,
293 0 : &local_int, &remote_int);
294 : // If result is R_REJECTED then component is unpaired or disabled.
295 0 : if (r == R_REJECTED)
296 0 : return NS_ERROR_NOT_AVAILABLE;
297 :
298 0 : if (r)
299 0 : return NS_ERROR_FAILURE;
300 :
301 : UniquePtr<NrIceCandidate> local(
302 0 : MakeNrIceCandidate(*local_int));
303 0 : if (!local)
304 0 : return NS_ERROR_FAILURE;
305 :
306 : UniquePtr<NrIceCandidate> remote(
307 0 : MakeNrIceCandidate(*remote_int));
308 0 : if (!remote)
309 0 : return NS_ERROR_FAILURE;
310 :
311 0 : if (localp)
312 0 : *localp = Move(local);
313 0 : if (remotep)
314 0 : *remotep = Move(remote);
315 :
316 0 : return NS_OK;
317 : }
318 :
319 :
320 0 : nsresult NrIceMediaStream::GetCandidatePairs(std::vector<NrIceCandidatePair>*
321 : out_pairs) const {
322 0 : MOZ_ASSERT(out_pairs);
323 0 : if (!stream_) {
324 0 : return NS_ERROR_NOT_AVAILABLE;
325 : }
326 :
327 : // If we haven't at least started checking then there is nothing to report
328 0 : if (ctx_peer_->state != NR_ICE_PEER_STATE_PAIRED) {
329 0 : return NS_OK;
330 : }
331 :
332 : // Get the check_list on the peer stream (this is where the check_list
333 : // actually lives, not in stream_)
334 : nr_ice_media_stream* peer_stream;
335 0 : int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
336 0 : if (r != 0) {
337 0 : return NS_ERROR_FAILURE;
338 : }
339 :
340 : nr_ice_cand_pair *p1, *p2;
341 0 : out_pairs->clear();
342 :
343 0 : TAILQ_FOREACH(p1, &peer_stream->check_list, check_queue_entry) {
344 0 : MOZ_ASSERT(p1);
345 0 : MOZ_ASSERT(p1->local);
346 0 : MOZ_ASSERT(p1->remote);
347 0 : NrIceCandidatePair pair;
348 :
349 0 : p2 = TAILQ_FIRST(&peer_stream->check_list);
350 0 : while (p2) {
351 0 : if (p1 == p2) {
352 : /* Don't compare with our self. */
353 0 : p2=TAILQ_NEXT(p2, check_queue_entry);
354 0 : continue;
355 : }
356 0 : if (strncmp(p1->codeword,p2->codeword,sizeof(p1->codeword))==0) {
357 : /* In case of duplicate pairs we only report the one winning pair */
358 0 : if (
359 0 : ((p2->remote->component && (p2->remote->component->active == p2)) &&
360 0 : !(p1->remote->component && (p1->remote->component->active == p1))) ||
361 0 : ((p2->peer_nominated || p2->nominated) &&
362 0 : !(p1->peer_nominated || p1->nominated)) ||
363 0 : (p2->priority > p1->priority) ||
364 0 : ((p2->state == NR_ICE_PAIR_STATE_SUCCEEDED) &&
365 0 : (p1->state != NR_ICE_PAIR_STATE_SUCCEEDED)) ||
366 0 : ((p2->state != NR_ICE_PAIR_STATE_CANCELLED) &&
367 0 : (p1->state == NR_ICE_PAIR_STATE_CANCELLED))
368 : ) {
369 : /* p2 is a better pair. */
370 : break;
371 : }
372 : }
373 0 : p2=TAILQ_NEXT(p2, check_queue_entry);
374 : }
375 0 : if (p2) {
376 : /* p2 points to a duplicate but better pair so skip this one */
377 0 : continue;
378 : }
379 :
380 0 : switch (p1->state) {
381 : case NR_ICE_PAIR_STATE_FROZEN:
382 0 : pair.state = NrIceCandidatePair::State::STATE_FROZEN;
383 0 : break;
384 : case NR_ICE_PAIR_STATE_WAITING:
385 0 : pair.state = NrIceCandidatePair::State::STATE_WAITING;
386 0 : break;
387 : case NR_ICE_PAIR_STATE_IN_PROGRESS:
388 0 : pair.state = NrIceCandidatePair::State::STATE_IN_PROGRESS;
389 0 : break;
390 : case NR_ICE_PAIR_STATE_FAILED:
391 0 : pair.state = NrIceCandidatePair::State::STATE_FAILED;
392 0 : break;
393 : case NR_ICE_PAIR_STATE_SUCCEEDED:
394 0 : pair.state = NrIceCandidatePair::State::STATE_SUCCEEDED;
395 0 : break;
396 : case NR_ICE_PAIR_STATE_CANCELLED:
397 0 : pair.state = NrIceCandidatePair::State::STATE_CANCELLED;
398 0 : break;
399 : default:
400 0 : MOZ_ASSERT(0);
401 : }
402 :
403 0 : pair.priority = p1->priority;
404 0 : pair.nominated = p1->peer_nominated || p1->nominated;
405 : // As discussed with drno: a component's can_send field (set to true
406 : // by ICE consent) is a very close approximation for writable and
407 : // readable. Note: the component for the local candidate never has
408 : // the can_send member set to true, remote for both readable and
409 : // writable. (mjf)
410 0 : pair.writable = p1->remote->component->can_send;
411 0 : pair.readable = p1->remote->component->can_send;
412 0 : pair.selected = p1->remote->component &&
413 0 : p1->remote->component->active == p1;
414 0 : pair.codeword = p1->codeword;
415 0 : pair.bytes_sent = p1->bytes_sent;
416 0 : pair.bytes_recvd = p1->bytes_recvd;
417 0 : pair.ms_since_last_send = p1->last_sent.tv_sec*1000
418 0 : + p1->last_sent.tv_usec/1000;
419 0 : pair.ms_since_last_recv = p1->last_recvd.tv_sec*1000
420 0 : + p1->last_recvd.tv_usec/1000;
421 :
422 0 : if (!ToNrIceCandidate(*(p1->local), &pair.local) ||
423 0 : !ToNrIceCandidate(*(p1->remote), &pair.remote)) {
424 0 : return NS_ERROR_FAILURE;
425 : }
426 :
427 0 : out_pairs->push_back(pair);
428 : }
429 :
430 0 : return NS_OK;
431 : }
432 :
433 0 : nsresult NrIceMediaStream::GetDefaultCandidate(
434 : int component,
435 : NrIceCandidate* candidate) const {
436 :
437 : nr_ice_candidate *cand;
438 :
439 0 : int r = nr_ice_media_stream_get_default_candidate(stream_, component, &cand);
440 0 : if (r) {
441 0 : MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '"
442 : << name_ << "'");
443 0 : return NS_ERROR_FAILURE;
444 : }
445 :
446 0 : if (!ToNrIceCandidate(*cand, candidate)) {
447 0 : MOZ_MTLOG(ML_ERROR, "Failed to convert default ICE candidate for '"
448 : << name_ << "'");
449 0 : return NS_ERROR_FAILURE;
450 : }
451 :
452 0 : return NS_OK;
453 : }
454 :
455 0 : std::vector<std::string> NrIceMediaStream::GetCandidates() const {
456 0 : char **attrs = nullptr;
457 : int attrct;
458 : int r;
459 0 : std::vector<std::string> ret;
460 :
461 0 : if (!stream_) {
462 0 : return ret;
463 : }
464 :
465 0 : r = nr_ice_media_stream_get_attributes(stream_,
466 0 : &attrs, &attrct);
467 0 : if (r) {
468 0 : MOZ_MTLOG(ML_ERROR, "Couldn't get ICE candidates for '"
469 : << name_ << "'");
470 0 : return ret;
471 : }
472 :
473 0 : for (int i=0; i<attrct; i++) {
474 0 : ret.push_back(attrs[i]);
475 0 : RFREE(attrs[i]);
476 : }
477 :
478 0 : RFREE(attrs);
479 :
480 0 : return ret;
481 : }
482 :
483 0 : static nsresult GetCandidatesFromStream(
484 : nr_ice_media_stream *stream,
485 : std::vector<NrIceCandidate> *candidates) {
486 0 : MOZ_ASSERT(candidates);
487 0 : nr_ice_component* comp=STAILQ_FIRST(&stream->components);
488 0 : while(comp){
489 0 : if (comp->state != NR_ICE_COMPONENT_DISABLED) {
490 0 : nr_ice_candidate *cand = TAILQ_FIRST(&comp->candidates);
491 0 : while(cand){
492 0 : NrIceCandidate new_cand;
493 : // This can fail if the candidate is server reflexive or relayed, and
494 : // has not yet received a response (ie; it doesn't know its address
495 : // yet). For the purposes of this code, this isn't a candidate we're
496 : // interested in, since it is not fully baked yet.
497 0 : if (ToNrIceCandidate(*cand, &new_cand)) {
498 0 : candidates->push_back(new_cand);
499 : }
500 0 : cand=TAILQ_NEXT(cand,entry_comp);
501 : }
502 : }
503 0 : comp=STAILQ_NEXT(comp,entry);
504 : }
505 :
506 0 : return NS_OK;
507 : }
508 :
509 0 : nsresult NrIceMediaStream::GetLocalCandidates(
510 : std::vector<NrIceCandidate>* candidates) const {
511 0 : if (!stream_) {
512 0 : return NS_ERROR_NOT_AVAILABLE;
513 : }
514 :
515 0 : return GetCandidatesFromStream(stream_, candidates);
516 : }
517 :
518 0 : nsresult NrIceMediaStream::GetRemoteCandidates(
519 : std::vector<NrIceCandidate>* candidates) const {
520 0 : if (!stream_) {
521 0 : return NS_ERROR_NOT_AVAILABLE;
522 : }
523 :
524 : // If we haven't at least started checking then there is nothing to report
525 0 : if (ctx_peer_->state != NR_ICE_PEER_STATE_PAIRED) {
526 0 : return NS_OK;
527 : }
528 :
529 : nr_ice_media_stream* peer_stream;
530 0 : int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
531 0 : if (r != 0) {
532 0 : return NS_ERROR_FAILURE;
533 : }
534 :
535 0 : return GetCandidatesFromStream(peer_stream, candidates);
536 : }
537 :
538 :
539 0 : nsresult NrIceMediaStream::DisableComponent(int component_id) {
540 0 : if (!stream_)
541 0 : return NS_ERROR_FAILURE;
542 :
543 0 : int r = nr_ice_media_stream_disable_component(stream_,
544 0 : component_id);
545 0 : if (r) {
546 0 : MOZ_MTLOG(ML_ERROR, "Couldn't disable '" << name_ << "':" <<
547 : component_id);
548 0 : return NS_ERROR_FAILURE;
549 : }
550 :
551 0 : return NS_OK;
552 : }
553 :
554 0 : nsresult NrIceMediaStream::GetConsentStatus(int component_id, bool *can_send, struct timeval *ts) {
555 0 : if (!stream_)
556 0 : return NS_ERROR_FAILURE;
557 :
558 : nr_ice_media_stream* peer_stream;
559 0 : int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
560 0 : if (r) {
561 0 : MOZ_MTLOG(ML_ERROR, "Failed to find peer stream for '" << name_ << "':" <<
562 : component_id);
563 0 : return NS_ERROR_FAILURE;
564 : }
565 :
566 0 : int send = 0;
567 0 : r = nr_ice_media_stream_get_consent_status(peer_stream, component_id,
568 0 : &send, ts);
569 0 : if (r) {
570 0 : MOZ_MTLOG(ML_ERROR, "Failed to get consent status for '" << name_ << "':" <<
571 : component_id);
572 0 : return NS_ERROR_FAILURE;
573 : }
574 0 : *can_send = !!send;
575 :
576 0 : return NS_OK;
577 : }
578 :
579 0 : nsresult NrIceMediaStream::SendPacket(int component_id,
580 : const unsigned char *data,
581 : size_t len) {
582 0 : if (!stream_)
583 0 : return NS_ERROR_FAILURE;
584 :
585 0 : int r = nr_ice_media_stream_send(ctx_peer_, stream_,
586 : component_id,
587 0 : const_cast<unsigned char *>(data), len);
588 0 : if (r) {
589 0 : MOZ_MTLOG(ML_ERROR, "Couldn't send media on '" << name_ << "'");
590 0 : if (r == R_WOULDBLOCK) {
591 0 : return NS_BASE_STREAM_WOULD_BLOCK;
592 : }
593 :
594 0 : return NS_BASE_STREAM_OSERROR;
595 : }
596 :
597 0 : return NS_OK;
598 : }
599 :
600 :
601 0 : void NrIceMediaStream::Ready() {
602 : // This function is called whenever a stream becomes ready, but it
603 : // gets fired multiple times when a stream gets nominated repeatedly.
604 0 : if (state_ != ICE_OPEN) {
605 0 : MOZ_MTLOG(ML_DEBUG, "Marking stream ready '" << name_ << "'");
606 0 : state_ = ICE_OPEN;
607 0 : SignalReady(this);
608 : }
609 : else {
610 0 : MOZ_MTLOG(ML_DEBUG, "Stream ready callback fired again for '" << name_ << "'");
611 : }
612 0 : }
613 :
614 0 : void NrIceMediaStream::Close() {
615 0 : MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'");
616 0 : state_ = ICE_CLOSED;
617 :
618 0 : if (stream_) {
619 0 : int r = nr_ice_remove_media_stream(ctx_, &stream_);
620 0 : if (r) {
621 0 : MOZ_ASSERT(false, "Failed to remove stream");
622 : MOZ_MTLOG(ML_ERROR, "Failed to remove stream, error=" << r);
623 : }
624 : }
625 0 : }
626 :
627 : } // close namespace
|