/// <summary> /// Create a new connection offer, either for a first connection to the remote peer, or for /// renegotiating some new or removed transceivers. /// /// This method submits an internal task to create an SDP offer message. Once the message is /// created, the implementation raises the <see xref="Microsoft.MixedReality.WebRTC.PeerConnection.LocalSdpReadytoSend"/> /// event to allow the user to send the message via the chosen signaling solution to the remote /// peer. /// /// <div class="IMPORTANT alert alert-important"> /// <h5>IMPORTANT</h5> /// <p> /// This method is very similar to the <c>CreateOffer()</c> method available in the underlying C# library, /// and actually calls it. However it also performs additional work in order to pair the transceivers of /// the local and remote peer. Therefore Unity applications must call this method instead of the C# library /// one to ensure transceiver pairing works as intended. /// </p> /// </div> /// </summary> /// <returns> /// <c>true</c> if the offer creation task was submitted successfully, and <c>false</c> otherwise. /// The offer SDP message is always created asynchronously. /// </returns> /// <remarks> /// This method can only be called from the main Unity application thread, where Unity objects can /// be safely accessed. /// </remarks> public bool StartConnection() { // MediaLine manipulates some MonoBehaviour objects when managing senders and receivers EnsureIsMainAppThread(); if (Peer == null) { throw new InvalidOperationException("Cannot create an offer with an uninitialized peer."); } // Batch all changes into a single offer AutoCreateOfferOnRenegotiationNeeded = false; // Add all new transceivers for local tracks. Since transceivers are only paired by negotiated mid, // we need to know which peer sends the offer before adding the transceivers on the offering side only, // and then pair them on the receiving side. Otherwise they are duplicated, as the transceiver mid from // locally-created transceivers is not negotiated yet, so ApplyRemoteDescriptionAsync() won't be able // to find them and will re-create a new set of transceivers, leading to duplicates. // So we wait until we know this peer is the offering side, and add transceivers to it right before // creating an offer. The remote peer will then match the transceivers by index after it applied the offer, // then add any missing one. // Update all transceivers, whether previously existing or just created above var transceivers = Peer.Transceivers; int index = 0; foreach (var mediaLine in _mediaLines) { // Ensure each media line has a transceiver Transceiver tr = mediaLine.Transceiver; if (tr != null) { // Media line already had a transceiver from a previous session negotiation Debug.Assert(tr.MlineIndex >= 0); // associated } else { // Create new transceivers for a media line added since last session negotiation. // Compute the transceiver desired direction based on what the local peer expects, both in terms // of sending and in terms of receiving. Note that this means the remote peer will not be able to // send any data if the local peer did not add a remote source first. // Tracks are not tested explicitly since the local track can be swapped on-the-fly without renegotiation, // and the remote track is generally not added yet at the beginning of the negotiation, but only when // the remote description is applied (so for the offering side, at the end of the exchange when the // answer is received). bool wantsSend = (mediaLine.Source != null); bool wantsRecv = (mediaLine.Receiver != null); var wantsDir = Transceiver.DirectionFromSendRecv(wantsSend, wantsRecv); var settings = new TransceiverInitSettings { Name = $"mrsw#{index}", InitialDesiredDirection = wantsDir }; tr = Peer.AddTransceiver(mediaLine.MediaKind, settings); try { mediaLine.PairTransceiver(tr); } catch (Exception ex) { LogErrorOnMediaLineException(ex, mediaLine, tr); } } Debug.Assert(tr != null); Debug.Assert(transceivers[index] == tr); ++index; } // Create the offer AutoCreateOfferOnRenegotiationNeeded = true; return(Peer.CreateOffer()); }