/// <summary> /// Callback invoked after the native transceiver has been destroyed, for clean-up. /// This is called by the peer connection when it closes, just before the C# transceiver /// object instance is destroyed. /// /// This replaces an hypothetical TransceiverRemoved callback, which doesn't exist to /// prevent confusion and underline the fact transceiver cannot be removed after being /// added to a peer connection, until that peer connection is closed and destroys them. /// </summary> internal void CleanUpAfterNativeDestroyed() { // The native peer connection was destroyed, therefore all its transceivers and remote // tracks too, since it was owning them. However local tracks are owned by the user, so // are possibly still alive. Debug.Assert(!_nativeHandle.IsClosed); Debug.Assert(_remoteTrack == null); if (_localTrack != null) { Debug.Assert(_localTrack.Transceiver == this); _localTrack.Transceiver = null; _localTrack = null; } _nativeHandle.Dispose(); // No need (and can't) unregister callbacks, the native transceiver is already destroyed Utils.ReleaseWrapperRef(_argsRef); _argsRef = IntPtr.Zero; }
/// <summary> /// Change the local audio track sending data to the remote peer. /// /// This detaches the previous local audio track if any, and attaches the new one instead. /// Note that the transceiver will only send some audio data to the remote peer if its /// negotiated direction includes sending some data and it has an attached local track to /// produce this data. /// /// This change is transparent to the session, and does not trigger any renegotiation. /// </summary> /// <param name="track">The new local audio track attached to the transceiver, and used to /// produce audio data to send to the remote peer if the transceiver is sending. /// Passing <c>null</c> is allowed, and will detach the current track if any.</param> private void SetLocalTrackImpl(LocalMediaTrack track) { if (track == _localTrack) { return; } var audioTrack = (track as LocalAudioTrack); var videoTrack = (track as LocalVideoTrack); if ((audioTrack != null) && (MediaKind != MediaKind.Audio)) { throw new ArgumentException("Cannot set local audio track as local track of video transceiver"); } if ((videoTrack != null) && (MediaKind != MediaKind.Video)) { throw new ArgumentException("Cannot set local video track as local track of audio transceiver"); } if (track != null) { if ((track.PeerConnection != null) && (track.PeerConnection != PeerConnection)) { throw new InvalidOperationException($"Cannot set track {track} of peer connection {track.PeerConnection} on transceiver {this} of different peer connection {PeerConnection}."); } uint res = Utils.MRS_E_UNKNOWN; if (audioTrack != null) { res = TransceiverInterop.Transceiver_SetLocalAudioTrack(_nativeHandle, audioTrack._nativeHandle); } else if (videoTrack != null) { res = TransceiverInterop.Transceiver_SetLocalVideoTrack(_nativeHandle, videoTrack._nativeHandle); } Utils.ThrowOnErrorCode(res); } else { // Note: Cannot pass null for SafeHandle parameter value (ArgumentNullException) uint res = Utils.MRS_E_UNKNOWN; if (MediaKind == MediaKind.Audio) { res = TransceiverInterop.Transceiver_SetLocalAudioTrack(_nativeHandle, new LocalAudioTrackHandle()); } else if (MediaKind == MediaKind.Video) { res = TransceiverInterop.Transceiver_SetLocalVideoTrack(_nativeHandle, new LocalVideoTrackHandle()); } Utils.ThrowOnErrorCode(res); } // Remove old track if (_localTrack != null) { Debug.Assert(_localTrack.Transceiver == this); Debug.Assert(_localTrack.PeerConnection == PeerConnection); _localTrack.Transceiver = null; _localTrack.PeerConnection = null; _localTrack = null; } // Add new track if (track != null) { Debug.Assert(track.Transceiver == null); Debug.Assert(track.PeerConnection == null); _localTrack = track; _localTrack.Transceiver = this; _localTrack.PeerConnection = PeerConnection; } }