Bumping manifests a=b2g-bump
[gecko.git] / media / mtransport / nricemediastream.cpp
blob92b2e34f32d5e3be17c3cd6205d61014dca03bb4
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/. */
7 // Original author: ekr@rtfm.com
9 // Some of this code is cut-and-pasted from nICEr. Copyright is:
12 Copyright (c) 2007, Adobe Systems, Incorporated
13 All rights reserved.
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are
17 met:
19 * Redistributions of source code must retain the above copyright
20 notice, this list of conditions and the following disclaimer.
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.
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.
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.
44 #include <string>
45 #include <vector>
47 #include "logging.h"
48 #include "nsError.h"
49 #include "mozilla/Scoped.h"
51 // nICEr includes
52 extern "C" {
53 #include "nr_api.h"
54 #include "registry.h"
55 #include "async_timer.h"
56 #include "ice_util.h"
57 #include "transport_addr.h"
58 #include "nr_crypto.h"
59 #include "nr_socket.h"
60 #include "nr_socket_local.h"
61 #include "stun_client_ctx.h"
62 #include "stun_server_ctx.h"
63 #include "ice_ctx.h"
64 #include "ice_candidate.h"
65 #include "ice_handler.h"
68 // Local includes
69 #include "nricectx.h"
70 #include "nricemediastream.h"
72 namespace mozilla {
74 MOZ_MTLOG_MODULE("mtransport")
76 static bool ToNrIceAddr(nr_transport_addr &addr,
77 NrIceAddr *out) {
78 int r;
79 char addrstring[INET6_ADDRSTRLEN + 1];
81 r = nr_transport_addr_get_addrstring(&addr, addrstring, sizeof(addrstring));
82 if (r)
83 return false;
84 out->host = addrstring;
86 int port;
87 r = nr_transport_addr_get_port(&addr, &port);
88 if (r)
89 return false;
91 out->port = port;
93 switch (addr.protocol) {
94 case IPPROTO_TCP:
95 out->transport = kNrIceTransportTcp;
96 break;
97 case IPPROTO_UDP:
98 out->transport = kNrIceTransportUdp;
99 break;
100 default:
101 MOZ_CRASH();
102 return false;
105 return true;
108 static bool ToNrIceCandidate(const nr_ice_candidate& candc,
109 NrIceCandidate* out) {
110 MOZ_ASSERT(out);
111 int r;
112 // Const-cast because the internal nICEr code isn't const-correct.
113 nr_ice_candidate *cand = const_cast<nr_ice_candidate *>(&candc);
115 if (!ToNrIceAddr(cand->addr, &out->cand_addr))
116 return false;
118 if (cand->isock) {
119 nr_transport_addr addr;
120 r = nr_socket_getaddr(cand->isock->sock, &addr);
121 if (r)
122 return false;
124 if (!ToNrIceAddr(addr, &out->local_addr))
125 return false;
128 NrIceCandidate::Type type;
130 switch (cand->type) {
131 case HOST:
132 type = NrIceCandidate::ICE_HOST;
133 break;
134 case SERVER_REFLEXIVE:
135 type = NrIceCandidate::ICE_SERVER_REFLEXIVE;
136 break;
137 case PEER_REFLEXIVE:
138 type = NrIceCandidate::ICE_PEER_REFLEXIVE;
139 break;
140 case RELAYED:
141 type = NrIceCandidate::ICE_RELAYED;
142 break;
143 default:
144 return false;
147 out->type = type;
148 out->codeword = candc.codeword;
149 return true;
152 // Make an NrIceCandidate from the candidate |cand|.
153 // This is not a member fxn because we want to hide the
154 // defn of nr_ice_candidate but we pass by reference.
155 static NrIceCandidate* MakeNrIceCandidate(const nr_ice_candidate& candc) {
156 ScopedDeletePtr<NrIceCandidate> out(new NrIceCandidate());
158 if (!ToNrIceCandidate(candc, out)) {
159 return nullptr;
161 return out.forget();
164 // NrIceMediaStream
165 RefPtr<NrIceMediaStream>
166 NrIceMediaStream::Create(NrIceCtx *ctx,
167 const std::string& name,
168 int components) {
169 RefPtr<NrIceMediaStream> stream =
170 new NrIceMediaStream(ctx, name, components);
172 int r = nr_ice_add_media_stream(ctx->ctx(),
173 const_cast<char *>(name.c_str()),
174 components, &stream->stream_);
175 if (r) {
176 MOZ_MTLOG(ML_ERROR, "Couldn't create ICE media stream for '"
177 << name << "'");
178 return nullptr;
181 return stream;
184 NrIceMediaStream::~NrIceMediaStream() {
185 // We do not need to destroy anything. All major resources
186 // are attached to the ice ctx.
189 nsresult NrIceMediaStream::ParseAttributes(std::vector<std::string>&
190 attributes) {
191 if (!stream_)
192 return NS_ERROR_FAILURE;
194 std::vector<char *> attributes_in;
196 for (size_t i=0; i<attributes.size(); ++i) {
197 attributes_in.push_back(const_cast<char *>(attributes[i].c_str()));
200 // Still need to call nr_ice_ctx_parse_stream_attributes.
201 int r = nr_ice_peer_ctx_parse_stream_attributes(ctx_->peer(),
202 stream_,
203 attributes_in.size() ?
204 &attributes_in[0] : nullptr,
205 attributes_in.size());
206 if (r) {
207 MOZ_MTLOG(ML_ERROR, "Couldn't parse attributes for stream "
208 << name_ << "'");
209 return NS_ERROR_FAILURE;
212 has_parsed_attrs_ = true;
213 return NS_OK;
216 // Parse trickle ICE candidate
217 nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate) {
218 int r;
220 MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << ctx_->name() << ")/STREAM(" <<
221 name() << ") : parsing trickle candidate " << candidate);
223 r = nr_ice_peer_ctx_parse_trickle_candidate(ctx_->peer(),
224 stream_,
225 const_cast<char *>(
226 candidate.c_str())
228 if (r) {
229 if (r == R_ALREADY) {
230 MOZ_MTLOG(ML_ERROR, "Trickle candidates are redundant for stream '"
231 << name_ << "' because it is completed");
233 } else {
234 MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '"
235 << name_ << "'");
236 return NS_ERROR_FAILURE;
240 return NS_OK;
243 // Returns NS_ERROR_NOT_AVAILABLE if component is unpaired or disabled.
244 nsresult NrIceMediaStream::GetActivePair(int component,
245 NrIceCandidate **localp,
246 NrIceCandidate **remotep) {
247 int r;
248 nr_ice_candidate *local_int;
249 nr_ice_candidate *remote_int;
251 r = nr_ice_media_stream_get_active(ctx_->peer(),
252 stream_,
253 component,
254 &local_int, &remote_int);
255 // If result is R_REJECTED then component is unpaired or disabled.
256 if (r == R_REJECTED)
257 return NS_ERROR_NOT_AVAILABLE;
259 if (r)
260 return NS_ERROR_FAILURE;
262 ScopedDeletePtr<NrIceCandidate> local(
263 MakeNrIceCandidate(*local_int));
264 if (!local)
265 return NS_ERROR_FAILURE;
267 ScopedDeletePtr<NrIceCandidate> remote(
268 MakeNrIceCandidate(*remote_int));
269 if (!remote)
270 return NS_ERROR_FAILURE;
272 if (localp)
273 *localp = local.forget();
274 if (remotep)
275 *remotep = remote.forget();
277 return NS_OK;
281 nsresult NrIceMediaStream::GetCandidatePairs(std::vector<NrIceCandidatePair>*
282 out_pairs) const {
283 MOZ_ASSERT(out_pairs);
285 // Get the check_list on the peer stream (this is where the check_list
286 // actually lives, not in stream_)
287 nr_ice_media_stream* peer_stream;
288 int r = nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream);
289 if (r != 0) {
290 return NS_ERROR_FAILURE;
293 nr_ice_cand_pair *p1;
294 out_pairs->clear();
296 TAILQ_FOREACH(p1, &peer_stream->check_list, entry) {
297 MOZ_ASSERT(p1);
298 MOZ_ASSERT(p1->local);
299 MOZ_ASSERT(p1->remote);
300 NrIceCandidatePair pair;
302 switch (p1->state) {
303 case NR_ICE_PAIR_STATE_FROZEN:
304 pair.state = NrIceCandidatePair::State::STATE_FROZEN;
305 break;
306 case NR_ICE_PAIR_STATE_WAITING:
307 pair.state = NrIceCandidatePair::State::STATE_WAITING;
308 break;
309 case NR_ICE_PAIR_STATE_IN_PROGRESS:
310 pair.state = NrIceCandidatePair::State::STATE_IN_PROGRESS;
311 break;
312 case NR_ICE_PAIR_STATE_FAILED:
313 pair.state = NrIceCandidatePair::State::STATE_FAILED;
314 break;
315 case NR_ICE_PAIR_STATE_SUCCEEDED:
316 pair.state = NrIceCandidatePair::State::STATE_SUCCEEDED;
317 break;
318 case NR_ICE_PAIR_STATE_CANCELLED:
319 pair.state = NrIceCandidatePair::State::STATE_CANCELLED;
320 break;
321 default:
322 MOZ_ASSERT(0);
325 pair.priority = p1->priority;
326 pair.nominated = p1->peer_nominated || p1->nominated;
327 pair.selected = p1->remote->component &&
328 p1->remote->component->active == p1;
329 pair.codeword = p1->codeword;
331 if (!ToNrIceCandidate(*(p1->local), &pair.local) ||
332 !ToNrIceCandidate(*(p1->remote), &pair.remote)) {
333 return NS_ERROR_FAILURE;
336 out_pairs->push_back(pair);
339 return NS_OK;
342 nsresult NrIceMediaStream::GetDefaultCandidate(
343 NrIceCandidate* candidate) const {
345 nr_ice_candidate *cand;
347 int r = nr_ice_media_stream_get_default_candidate(stream_, 1, &cand);
348 if (r) {
349 MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '"
350 << name_ << "'");
351 return NS_ERROR_FAILURE;
354 if (!ToNrIceCandidate(*cand, candidate)) {
355 MOZ_MTLOG(ML_ERROR, "Failed to convert default ICE candidate for '"
356 << name_ << "'");
357 return NS_ERROR_FAILURE;
360 return NS_OK;
363 std::vector<std::string> NrIceMediaStream::GetCandidates() const {
364 char **attrs = 0;
365 int attrct;
366 int r;
367 std::vector<std::string> ret;
369 r = nr_ice_media_stream_get_attributes(stream_,
370 &attrs, &attrct);
371 if (r) {
372 MOZ_MTLOG(ML_ERROR, "Couldn't get ICE candidates for '"
373 << name_ << "'");
374 return ret;
377 for (int i=0; i<attrct; i++) {
378 ret.push_back(attrs[i]);
379 RFREE(attrs[i]);
382 RFREE(attrs);
384 return ret;
387 static nsresult GetCandidatesFromStream(
388 nr_ice_media_stream *stream,
389 std::vector<NrIceCandidate> *candidates) {
390 MOZ_ASSERT(candidates);
391 nr_ice_component* comp=STAILQ_FIRST(&stream->components);
392 while(comp){
393 if (comp->state != NR_ICE_COMPONENT_DISABLED) {
394 nr_ice_candidate *cand = TAILQ_FIRST(&comp->candidates);
395 while(cand){
396 NrIceCandidate new_cand;
397 // This can fail if the candidate is server reflexive or relayed, and
398 // has not yet received a response (ie; it doesn't know its address
399 // yet). For the purposes of this code, this isn't a candidate we're
400 // interested in, since it is not fully baked yet.
401 if (ToNrIceCandidate(*cand, &new_cand)) {
402 candidates->push_back(new_cand);
404 cand=TAILQ_NEXT(cand,entry_comp);
407 comp=STAILQ_NEXT(comp,entry);
410 return NS_OK;
413 nsresult NrIceMediaStream::GetLocalCandidates(
414 std::vector<NrIceCandidate>* candidates) const {
415 return GetCandidatesFromStream(stream_, candidates);
418 nsresult NrIceMediaStream::GetRemoteCandidates(
419 std::vector<NrIceCandidate>* candidates) const {
420 nr_ice_media_stream* peer_stream;
421 int r = nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream);
422 if (r != 0) {
423 return NS_ERROR_FAILURE;
426 return GetCandidatesFromStream(peer_stream, candidates);
430 nsresult NrIceMediaStream::DisableComponent(int component_id) {
431 if (!stream_)
432 return NS_ERROR_FAILURE;
434 int r = nr_ice_media_stream_disable_component(stream_,
435 component_id);
436 if (r) {
437 MOZ_MTLOG(ML_ERROR, "Couldn't disable '" << name_ << "':" <<
438 component_id);
439 return NS_ERROR_FAILURE;
442 return NS_OK;
445 nsresult NrIceMediaStream::SendPacket(int component_id,
446 const unsigned char *data,
447 size_t len) {
448 if (!stream_)
449 return NS_ERROR_FAILURE;
451 int r = nr_ice_media_stream_send(ctx_->peer(), stream_,
452 component_id,
453 const_cast<unsigned char *>(data), len);
454 if (r) {
455 MOZ_MTLOG(ML_ERROR, "Couldn't send media on '" << name_ << "'");
456 if (r == R_WOULDBLOCK) {
457 return NS_BASE_STREAM_WOULD_BLOCK;
460 return NS_BASE_STREAM_OSERROR;
463 return NS_OK;
467 void NrIceMediaStream::Ready() {
468 // This function is called whenever a stream becomes ready, but it
469 // gets fired multiple times when a stream gets nominated repeatedly.
470 if (state_ != ICE_OPEN) {
471 MOZ_MTLOG(ML_DEBUG, "Marking stream ready '" << name_ << "'");
472 state_ = ICE_OPEN;
473 SignalReady(this);
475 else {
476 MOZ_MTLOG(ML_DEBUG, "Stream ready callback fired again for '" << name_ << "'");
480 void NrIceMediaStream::Close() {
481 MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'");
482 state_ = ICE_CLOSED;
483 stream_ = nullptr;
485 } // close namespace