/// <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;
            }
        }